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

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

!standard 4.9(6)          15-10-17 AI12-0075-1/04
!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
Replace 4.9(6)
- a function_call whose function_name or function_prefix statically
denotes a static function, and whose actual parameters, if any (whether given explicitly or by default), are all static expressions;
with
- a function_call whose function_name or function_prefix statically
denotes a static function, whose actual parameters, if any (whether given explicitly or by default), are all static expressions, and which satisfies an additional condition: either the function_name or function_prefix does not statically denote an expression function or the compile-time evaluation of the call does not fail any language-defined check.
Note: This means that a call to a static expression function whose (compile-time) evaluation fails a language-defined check is not a static expression (as opposed to being illegal). The motivation for this is to avoid incompatibly introducing legality violations for calls to expression functions occurring in dead code, as in
declare N : constant := Pkg1.This - Pkg2.That; -- it happens that N = 0 function Divide_By_N (X : Integer) return Integer is (X / N); Y : Integer; begin if N /= 0 then Y := Divide_By_N (1234); end if; ...
end;
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) This change is incompatible in some cases. 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); Y : S := ... ; begin case Y is when 0 .. 33 => ...; when 44 .. 55 => ...; when others => ... ; end case; end;
but will become illegal because with this change Y will have a static nominal subtype.
The most significant compatibility issues are addressed via the rule that a call to an expression function whose compile-time evaluation fails a check is non-static but still legal (as opposed to being illegal).
The alternative of defining a new Boolean aspect (perhaps named Static_Function) was discussed and not selected.
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.
!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.

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

From: Steve Baird
Sent: Saturday, September 17, 2015 11:20 PM

!wording differences from previous version are highlighted. [This is
version /04 of the AI - Editor.]

The idea with this change is that a call to an expression function with static
arguments which fails a check (during its compile time evaluation) is simply
not a static expression, as opposed to being illegal.

This addresses the compatibility issue that Randy raised. [But it doesn't
address the original compatibility issue that Steve himself raised - Editor.]

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

Questions? Ask the ACAA Technical Agent