Version 1.4 of ai12s/ai12-0075-1.txt

Unformatted version of ai12s/ai12-0075-1.txt version 1.4
Other versions for file ai12s/ai12-0075-1.txt

!standard 4.9(6)          15-08-25 AI12-0075-1/03
!standard 4.9(18)
!class Amendment 13-06-09
!status work item 13-06-09
!status received 13-05-07
!priority Medium
!difficulty Medium
!subject Static expression functions
!summary
An expression function can be static in appropriate circumstances.
!problem
It should be possible to use an expression function in a static expression. This would allow greater abstraction for static expressions, which sometimes can get quite large.
!proposal
(See summary.)
!wording
Add just before the definition of "static function" (i.e., just before 4.9(18)):
A "potentially static expression" is defined in the same way as a static expression except that
- a name denoting a formal parameter of an expression function
is a potentially static expression; and
- each use of "static expression" in
the definition of "static expression" is replaced with a corresponding use of "potentially static expression" in the definition of "potentially static expression".
AARM Discussion: These uses occur in the definition of "static expression" in the cases of function calls, type conversions, qualified expressions, membership tests, short circuit control forms, conditional expressions, and parenthesized expressions.
A "static expression function" is defined to be an expression function
- whose expression is a potentially static expression; and - which contains no calls to itself; and - each of whose parameters (if any) is of mode IN and is of a static subtype; and - whose result subtype is a static subtype; and - to which no precondition or postcondition expression applies.
AARM Discussion: In the case of an expression_function_declaration which is the completion of an earlier subprogram_declaration, the function is an expression function if and only if the completion is visible. This implies that it is possible for a (view of a) function to be a static expression function if and only if its completion is visible. This is similar to the way that the completion of a deferred constant may be a static constant.
In addition, an inherited function is defined to be a static expression function if the corresponding primitive function of the parent or progenitor type is a static expression function (as viewed at the point of the declaration of the inherited function).
AARM Discussion: Unlike non-inherited subprograms, all views of a given inherited subprogram always agree with respect to whether the given subprogram is a static expression function.
Add to the definition of static function (i.e. as another list item at the end of the 4.9 (19-22) list):
- a static expression function
Add at the and of the 4.9 "Legality Rules" section:
AARM Discussion: The preceding "statically unevaluated" rule allows
X : constant := (if True then 37 else (1 / 0))
but does not allow
function If_Then_Else (Flag : Boolean; X, Y : Integer) is (if Flag then X else Y); X : constant := If_Then_Else (True, 37, 1 / 0);
because evaluation of a function call includes evaluation of all of its actual parameters.
!discussion
1) It has been suggested that a new aspect, perhaps named Static_Function (or perhaps just Static), should be introduced and that a function is not a static function unless its Static_Function aspect is specified to be True (even if it meets all of the other requirements).
This proposal has its advantages and disadvantages, but one strong argument seems to be the existing treatment of static constants. This question was already settled (for static constants) back in Ada83 and static expression functions should be treated consistently with static constants.
This leads to the conclusion that the proposed new aspect is a bad idea.
On the other hand, there is the question of compatibility. The following example is currently legal:
procedure Foo is function Inc (X : integer) return Integer is (X + 1); subtype S is Integer range 0 .. Inc (3); X : S := ... ; begin case X is when 0 .. 33 => ...; when 44 .. 55 => ...; when others => ... ; end case; end;
The proposal given above would cause this example to become illegal (because the subtype S would become a static subtype). The Static_Function (or just Static) aspect would eliminate this incompatibility.
This incompatibility would only affect code which uses expression functions. Expression functions were first introduced in Ada 2012, so the importance of this compatibility issue is less clear than if pre-Ada-2012 code could be affected.
[Editor's note: There are many other ways to cause this incompatibility. For instance, consider the following
procedure Bar is NUM_ITEMS : constant := 0; Count : constant Natural := 12; function Average (Count : in Natural) return Natural is (Count / NUM_ITEMS);
begin if NUM_ITEMS > 0 then Put_Line ("Average =" & Natural'Image(Average(Count))); -- (!!) end if; end Bar;
The proposal given above would make the call to Average illegal (because it divides by zero), even though it could never be executed. Obviously, the non-static version is not a problem (I just said it won't be executed :-).
And of course one can construct similar examples using any of the 4.9(33-36) rules - out of base type values (also plausible in conditional code, or even precision for decimal types.
While expression functions were only introduced in Ada 2012, they are widely used because of their convinience, and it will be six years after their introduction before this change is adopted. We need to be very careful not to introduce incompatibilties for a nice-to-have feature. End Editor's Note.]
If it is decided that a new aspect is needed, then the following changes to the preceding proposal would be needed:
- The new aspect is a Boolean aspect which can only be specified
as part of an Expression_Function declaration with the usual "must be static Boolean value" rule. Inheritance rules for the aspect would be needed.
- A new legality rule stating that if an expression
function's Static_Function aspect is True, then the function's expression shall be a potentially static expression.
- The following clause in the definition of static expression function:
"whose expression is a potentially static expression; and"
would be replaced with
"whose Static_Function aspect is True; and" .
2) It would be simpler to say that an inherited subprogram is never a static expression function.
However, it seems nice to allow things like
declare package Pkg1 is type T1 is range 1 .. 10; function Wrapping_Inc (X : T1) is (if X = T1'Last then T1'First else T1'Succ (X)); end Pkg1;
package Pkg2 is type T2 is new Pkg1.T1; end Pkg2;
Named_Number : constant := Pkg2.Wrapping_Inc (3); begin ... end;
by defining the inherited function Pkg2.Wrapping_Inc to be a static expression function.
It seems clear that we want to avoid a "characteristics"-like situation where a change in the status of the parent type's operator causes a change in the status of the inherited operator. That's the reason for the "as viewed at the point of ..." wording.
It's not clear whether an inherited function is ever an expression function; we might want to consider clarifying that point if we want to avoid the situation of a "static expression function" which is not an "expression function". [This question might also need to be resolved for reasons having nothing to do with this AI because of the special treatment of expression functions in the freezing rules.]
3) Do we need to say anything about type invariants? No, because if a function has a parameter (or result) of a private type, then it's not a static function. But what if it is completed as a static expression function after the private type is completed? Maybe we need a rule saying that if a type invariant applies to a function than the function is not a static expression function (like the rule already given for predicates).
4) We could allow a static expression function to have an applicable pre/post-condition as long as the condition is a potentially static expression. Pre/post conditions for one-part (i.e., no forward declaration) expression functions are of very limited value, but supporting PPCs might make more sense for two-part functions.
5) We've got the 3.8 rule
If the discriminant is used to define the constraint of a component, the bounds of an entry family, or the constraint of the parent subtype in a derived_type_definition, then its name shall appear alone as a direct_name (not as part of a larger expression or expanded name).
It might seem tempting to allow (in a component constraint) the case of a call to a static function such that all parameters (including, of course, defaulted parameters) are either static expressions or names denoting discriminants. This would allow, for example, an index constraint value of D + 1, where D is a discriminant.
This would introduce some implementation problems.
One property we need for discriminant-dependent component constraint expressions is reproducibility - given the same discriminant values we must always get the same result. This relaxation of the current rules would meet that requirement, but that's not enough.
We also need, given a set of discriminant subtypes, to be able to easily calculate the minumum and maximum values that an expression might yield (e.g., we need to know how much storage is needed when we declare an unconstrained object of a type which has an array component with a discriminant-dependent index constraint).
Given something like
subtype Index is Integer range 0 .. <some non-static expression>; type Rec (Length : Index := 0) is record F : String (1 .. Length); end record; X : Rec;
it is easy to compute (at runtime) the amount of storage required for X. If we replace "1 .. Length" in the above example with "1 .. Some_Static_Expr_Func (Length)", then this becomes more complicated because a static expression function need not be monotonic.
Thus, this proposal would lead to implementation problems.
6) Because an expression function can be a completion, one might think that a rule is needed to prevent indirect recursion in a static expression function. However, no special rule is needed. Given
function F1 (X : Natural); function F2 (X : Natural) is (if X = 0 then 0 else 1 + F1 (X / 2)); function F1 (X : Natural) is (F2 (X));
neither of the two functions are static expression functions. This is because, as viewed from the point where F2 calls F1, F1 is not an expression function.
7) There has been some discussion on ada-comment about somehow allowing comes calls to System.Storage_Elements.To_Address to be considered "static" in some sense in order to allow an address aspect specification in a preelaborated package.
That seems like a separate issue that should be dealt with in a separate AI.
Ditto for things like allowing Some_Private_Type'Size to be static in some cases so that it can be used in a representation clause for an enclosing composite type.
This AI is only about allowing expression functions to be called in static expressions.
[Editor's note: I feel this is a mistake, as we're piling up special gizmos without integrating them. See AI12-0175-1 for a possible way to solve all of these problems with one integrated solution.]
!example
!ASIS
** ASIS queries needed **
!ACATS test
ACATS B-Tests and C-Tests.
!appendix

From: Jean-Pierre Rosen
Sent: Tuesday, May  7, 2013  7:28 AM

First asking, in case it has been discussed before...

Wouldn't it be possible to make some expression functions static?

Apparently, all that is needed is to add "formal parameters of an expression
function" to the list of static things in 4.9. (Of course, the actual
parameters of the call would have to be static, but that's already covered by
4.9(6))

****************************************************************

From: Randy Brukardt
Sent: Tuesday, May  7, 2013  6:24 PM

> First asking, in case it has been discussed before...
>
> Wouldn't it be possible to make some expression functions static?

This was mentioned as a possible extension in the original AI. The !discussion
section of AI05-0177-1 says:

One could imagine allowing these functions (when visible) to be used in static
expressions if the inlined expression would be static. This would definitely
allow abstracting expressions that currently are written as a giant single
expression because they need to be static. That's something you cannot do now.
We didn't add this to proposal in order to keep the proposal simple.

There's a bit of discussion of this in the e-mail, but nothing very interesting.
I thought we also discussed it briefly at a meeting, but I didn't find anything
in the October 2010 minutes when this was mainly discussed.

> Apparently, all that is needed is to add "formal parameters of an
> expression function" to the list of static things in 4.9. (Of course,
> the actual parameters of the call would have to be static, but that's
> already covered by  4.9(6))

I think it would be necessary for the fact that it is an expression function to
be visible at the point of use. That would limit the value of this as it would
prevent expression functions used as completions from participating. If we were
willing to break privacy, we could allow expression functions used as completion
in the private part, but that's probably a bad idea.

Perhaps a better idea would be to have an aspect Static_Possible that could be
applied to a subprogram declaration. The completion then would have to be an
expression function that can be static.

Also, I think there would have to be rule that for such a function the
expression of it is static if one considers the parameters static. (And since
not all types can be static, if there are any problems that aren't of either
scalar or string types, then those parameters would be non-static.) And we'd
need some sort of rule to deal with recursion (that's something we don't
currently have to deal with in static expressions), unless we're willing to have
compilers crash in such circumstances. (Running out of memory is hard to check
for accurately, and it's often impossible to recover.)

Anyway, this is a non-trivial idea. No problem with adding it as a possible
Amendment, but it isn't going to magically appear as a Binding Interpretation in
Ada 2012 (we already rejected that idea initially).

****************************************************************

From: Robert Dewar
Sent: Tuesday, May  7, 2013  6:38 PM

> Anyway, this is a non-trivial idea. No problem with adding it as a
> possible Amendment, but it isn't going to magically appear as a
> Binding Interpretation in Ada 2012 (we already rejected that idea initially).

Maybe we should try implementing Randy's pragma first, and see if there are
unforseen difficulties. With that pragma, we don't even have to limit it to
expression functions!

****************************************************************

From: Steve Baird
Sent: Wednesday, May  8, 2013  4:18 PM

> Anyway, this is a non-trivial idea. No problem with adding it as a
> possible Amendment, but it isn't going to magically appear as a
> Binding Interpretation in Ada 2012 (we already rejected that idea initially).

You got that right.

We'd have to be careful not to ignore the subtypes of the formal parameters.

When we "plug in" the arguments to see if the resulting expression is static, I
think we'd need to treat the plugged-in value like a qualified expression,
qualified by the subtype of the formal.

There is also the question of ignored formal parameters.

A particularly pathological example:

    package Pkg is
        type T is private with Invariant => Is_Even (T);

        function Is_Even (X : T) return Boolean;

        function Zoofle (X : in out T; Y, Z : Integer) return Integer
         is (Y + Z) with Static_Possible;

    private
        type T is new Integer;

        function Is_Even (X : T) return Boolean is (X mod 2 = 0);

        Invalid : T := 3;

        Three : constant Integer := Zoofle (Invalid, 1, 2); -- static?
    end Pkg;

We'd probably deal with this particular case by just requiring formal parameters
to be of mode In, but the point is that such a rule would need to be stated.

I agree with Randy; this is not a one-liner.

****************************************************************

From: Robert Dewar
Sent: Wednesday, May  8, 2013  4:31 PM

> We'd probably deal with this particular case by just requiring formal
> parameters to be of mode In, but the point is that such a rule would
> need to be stated.

To me that's an OBVIOUS restriction for a static function

****************************************************************

From: Steve Baird
Sent: Wednesday, May  8, 2013  5:33 PM

Interactions with short circuit evaluation, for example, might be less obvious.

     Flag_V1 : constant Boolean := True or else (1 / 0) = 123;

is static, but presumably this version

     function Or_Else (C1, C2 : Boolean) return Boolean is
         (C1 or else C2) with Static_Possible;

     Flag_V2 : constant Boolean := Or_Else (True, (1 / 0) = 123);

would not be static.

My only real point is to agree with Randy: this is not a one-liner, if only
because these "OBVIOUS" restrictions would need to be stated.

****************************************************************

From: Robert Dewar
Sent: Wednesday, May  8, 2013  5:56 PM

>       Flag_V2 : constant Boolean := Or_Else (True, (1 / 0) = 123);
>
> would not be static.

I would not allow anything like this most certainly. All arguments to a function
have to be static. Again that seems like an obvious restriction. (1 / 0) = 123
is not a static expression!

> My only real point is to agree with Randy: this is not a one-liner, if
> only because these "OBVIOUS" restrictions would need to be stated.

The proper approach would be to make the restrictions as tight as possible,
rather than getting into the tempting mode of making them as universal as
possible.

****************************************************************

From: Steve Baird
Sent: Tuesday, August 25, 2015  4:06 PM

Here is a first cut at an AI allowing calls to expression functions to occur
in static expressions. This is part of my Madrid homework. [This was version
/04 of the AI - Editor.]

Many thanks to Randy for his helpful comments (but please do not take this to
mean that Randy agrees with anything in this proposal).

****************************************************************

From: Tucker Taft
Sent: Monday, September 14, 2015  3:52 PM

Looks good.  I agree we probably don't want an attribute.

****************************************************************

Questions? Ask the ACAA Technical Agent