Version 1.3 of ai12s/ai12-0175-1.txt

Unformatted version of ai12s/ai12-0175-1.txt version 1.3
Other versions for file ai12s/ai12-0175-1.txt

!standard 4.9(1)          15-10-08 AI12-0175-1/01
!standard 13.7.1(10/3)
!class Amendment 15-10-08
!status work item 15-10-08
!status received 15-07-14
!priority Low
!difficulty Medium
!subject Preelaborable packages with address clauses
!summary
Add aspects so that sufficiently simple composite types (including private types) and functions of those can be treated as static.
!problem
It's not possible to portably write a Pure or Preelaborated package that contains an object mapped to a static address. That's because the only way to create such an address is to call System.Storage_Elements.To_Address. That function is not a static function.
However, 10.2.1(5) & (7) say:
An elaborable construct is preelaborable unless its elaboration performs any of the following actions:
...
* A call to a subprogram other than a static function.
Elaborating an address clause calls that function, and thus a package containing such an address clause cannot be Pure or Preelaborated.
Some implementations have ways around this problem (an implementation that did not follow the Implementation Advice 13.7(37) probably allows directly using a literal or aggregate of literals to specify a value of System.Address), but a portable solution is needed.
This is important as it is common to access memory-mapped hardware registers in embedded programs. It is also common in such programs to use preelaborated packages to reduce start-up overhead. The inability to do both is a silly and annoying limitation.
!proposal
(See Summary.)
!wording
[Editor's note: I didn't try to update the existing AARM notes; that will need to be done.]
Modify 4.9(1):
Certain expressions of a {potentially static}[scalar or string] type are defined to be static. Similarly, certain discrete ranges are defined to be static, and certain {potentially static}[scalar or string] subtypes are defined to be static subtypes. [ Static means determinable at compile time, using the declared properties or values of the program entities.]
{A potentially static type is either a scalar type, a string type, or a composite type for which the Potentially_Static aspect is True.}
Static Semantics
For a composite type, the following language-defined aspect may be specified with an aspect_specification (see 13.1.1):
Potentially_Static The type of aspect Potentially_Static is Boolean. If directly specified, the aspect_definition shall be a static expression. If not specified (including by inheritance), the aspect is False.
[Editor's note: Legality Rules for this aspect are below, after paragraph 37. The meaning was given in the paragraph preceding this definition.]
Add after 4.9(5):
* an aggregate of a subtype subtype; for an array aggregate, all of the discrete_choices (if any) of the discrete_choice_list of each array_component_association are static expressions, static ranges, or static subtypes;
Modify 4.9(7):
* an attribute_reference that denotes a scalar value, and whose prefix denotes a static [scalar] subtype;
Modify 4.9(9):
* a type_conversion whose subtype_mark denotes a static [scalar] subtype, and whose operand is a static expression;
Modify 4.9(10 & 11/4) to drop the parenthesized "(scalar or string)".
Modify 4.9(20):
* a predefined concatenation operator whose result type is a {potentially
static}[string] type;
* {a predefined relational operator whose parameter types are all potentially static non-scalar types none of which are descendants of formal types;}
* a function for which the Static_Function aspect is True;
Add after 4.9(23):
For a subprogram_declaration or an expression_function_declaration, the following language-defined aspect may be specified with an aspect_specification (see 13.1.1):
Static_Function The type of aspect Static_Function is Boolean. If directly specified, the aspect_definition shall be a static expression. If not specified (including by inheritance), the aspect is False.
[Editor's note: Legality Rules for this aspect are below, after paragraph 37. The meaning was given in the paragraph 20.]
Modify 4.9(24):
A static constant is{
* a constant view declared by a full constant declaration
or an object_renaming_declaration with a static nominal subtype, having a value defined by a static scalar expression or by a static {composite}[string] expression whose value has a length not exceeding the maximum length of a string_literal in the implementation{; or
* a deferred constant for which the aspect Static_Constant is True.
For a deferred constant declaration, the following language-defined aspect may be specified with an aspect_specification (see 13.1.1):
Static_Constant The type of aspect Static_Function is Boolean. If directly specified, the aspect_definition shall be a static expression. If not specified (including by inheritance), the aspect is False.
[Editor's note: Legality Rules for this aspect are below, after paragraph 37. The meaning was given above.]
Modify 4.9(26/3):
A static subtype is either a static scalar subtype or a static {composite}[string] subtype. A static scalar subtype is an unconstrained scalar subtype whose type is not a descendant of a formal type, or a constrained scalar subtype formed by imposing a compatible static constraint on a static scalar subtype. A static {composite}[string] subtype is an unconstrained {array}[string] subtype whose index subtype and component subtype are static{ and whose type is potentially static, [or] a constrained {array}[string] subtype formed by imposing a compatible static constraint on a static {array}[string] subtype{, or a constrained composite subtype formed by imposing a compatible static constraint on a potentially static record or private type, or on a constrained composite subtype. In any case, the subtype of a generic formal object of mode in out, and the result subtype of a generic formal function, are not static. Also, a subtype is not static if any Dynamic_Predicate specifications apply to it.
[Editor's note: The only constraint allowed on record or private types is the null constraint, as we do not allow any discriminants. I think the case of a non-discriminated type is covered by "imposing" a null constraint on a potentially static record or private type. If not, we'll have to fix the wording.]
Modify 4.9(32):
A subtype is statically constrained if it is constrained, and its constraint is static. An object is statically constrained if its nominal subtype is statically constrained, or if it is a static {composite}[string] constant.
Add after 4.9(37/2):
If aspect Potentially_Static is True for a type, the type:
* shall not be an inherently limited type; and
[Sorry, no static tasks. ;-)]
* shall not have any discriminants; and
* shall not have any component whose nominal subtype is not a static subtype Redundant[of a potentially static type]; and
AARM Proof: All static subtypes are of potentially static types.
* if the type is not an unconstrained array, shall not have a size exceeding the size of a Wide_Wide_Character times maximum length of a string_literal in the implementation.
AARM Discussion: We're allowing simple "tuples" to be static expressions, not arbitrarily large and complex types. The size limit corresponds to the Ada 95 limit for strings, so we're not requiring carrying around massive values.
AARM Ramification: This size limit might appear to be a portability problem. However, the string_literal limit is tied to the line length, and 2.2 requires that to be at least 200 characters. Thus types less than 800 storage elements in size ought to be portably static. [We could have used that as an alternative way of saying this, if we wanted even more portability.]
If aspect Static_Function is True for a subprogram_declaration S, the subprogram shall have a completion that is a static function, and that completion shall be in the same declaration list as S, or in the private part of package P if S is in the visible part of P.
AARM Ramification: This is necessarily an expression function. It has to be available to the compiler to evaluate, thus the completion has to be in the same compilation unit as the declaration.
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.
[This wording was borrowed from AI12-0075-1. Send all brickbats to the author of that AI. :-)]
If aspect Static_Function is True for an expression_function_declaration F, then
* the return expression of F shall be a potentially static expression; and
* the return expression of F shall not contain a call to itself; and
* each of the parameters of F (if any) shall be of mode IN and have a static subtype; and
* the result subtype of F shall be is a static subtype; and
* no precondition or postcondition expression shall applies to F.
[The list of rules was borrowed from AI12-0075-1, but the wording was improved.]
If aspect Static_Constant is True for a deferred constant declaration, the full declaration shall be a static constant.
----------------------- Now, using the above, make Address potentially-static and make To_Address a static function:
Modify 13.7(12):
type Address is implementation-defined{ with Potentially_Static => True}; Null_Address : constant Address{ with Static_Constant => True};
Add "Static_Function" to each function of 13.7(14/3).
Modify 13.7.1(10/3):
type Integer_Address is implementation-defined; function To_Address(Value : Integer_Address) return Address with Static_Function, Convention => Intrinsic; function To_Integer(Value : Address) return Integer_Address with Static_Function, Convention => Intrinsic;
!discussion
We could just apply another hack to fix this problem directly, adding some rules that make type Address potentially static (which is weird as it is a private type). But we've done that before (see static strings) and it's time to at least explore a more general solution.
We've had a number of issues over the years that relate to the fact that composite types (other that string types) are not static. For instance, one cannot use Composite'Size in a representation aspect as it is not static.
This problem is just another example of that.
The special case for string types is somewhat of a wart, because users cannot create similar types and functions on their own. Why should strings be static, but Points (pairs of integer cooredinates) or complex values (pairs of float values) not be static? Allowing them to be static would expand the capabilities of Pure and Preelaborated packages, as well as allowing their representation attributes to be used in representation clauses.
Therefore, we define a pair of aspects that allow staticness to be extended to composite types and operations that are sufficiently simple. We're trying to allow simple "tuples" and private types thereof to be static expressions; we're not trying to allow everything.
---------------
In wording through the wording, it is obvious that a number of capabilities are not currently provided for static strings: relational operators, type conversions, and aggregates. All of these seem necessary for more generalized static types. We need aggregates to construct values for types like Size and Complex. It would be bizarre to not allow static equality tests on such types, as they are needed to make conditional expressions statically unevaluated (which is needed to work around compatibility issues, see below). Type conversions aren't necessary, but seem like a wart.
---------------
We banned discriminants as we don't need them for any comtemplated use. Handling discriminant constraints and the like complicates the model, even if we don't allow any discriminant-dependent components. Similarly, not having any discriminant-dependent components simplifies the model by ensuring that all components are present in every value.
These restrictions could be lifted with some additional wording, if desired.
---------------
An alternative model would be that the aspects are only needed on private types and subprogram declarations; array and record types, and expression functions with the appropriate properties would automatically be potentially static. We did not use that model for compatibility reasons: bad static expressions are illegal, while the same expression in existing Ada is legal but raises an exception at runtime.
Thus, automatically making types and functions static would introduce errors in cases where the code is currently legal. If that code happens to be in a dead branch that is never executed, there could be a significant impact.
For example:
type Size is Width, Height : Natural; end record;
procedure Bar is MY_Size : constant Size := (Width => 10, Height => 0); Needed_Area : constant Natural := 100; -- Need 100 pixels.
function Area (A_Size : in Size) return Natural is (A_Size.Width * A_Size.Height);
begin if Area (My_Size) > 0 then Put_Line ("Ratio =" & Float'Image(Float (Needed_Area) / Area(My_Size))); -- (!!) end if; end Bar;
If Size was automatically potentially static, then the divide is illegal (as it divides by statically by zero), even though it can never be executed.
Any of the Legality Rules 4.9(33-37) can cause this effect, as well as case statement or aggregate completeness checks.
!ASIS
No ASIS effect. (I believe that staticness is not an ASIS property.)
!ACATS test
An ACATS C-Test is needed to check that the new capabilities are supported.
!appendix

From: Simon Wright
Sent: Tuesday, July 14, 2015  9:33 AM

For bare-metal programming on microcontrollers, such as the Arduino Due or
the STM32F4 range, one has to directly address the hardware registers. 
For example,

package Registers.ATSAM3X8.PMC is

   type PMC_T is record
      SCER  : aliased Interfaces.Unsigned_32;
      —  etc
   end record with Convention => Ada, Volatile;

   for PMC_T use record
      SCER  at 16#0000# range 0 .. 31;
      —  etc
   end record;

   PMC : PMC_T
     with
       Import,
       Convention => Ada,
       Address => System.Storage_Elements.To_Address (16#400E0600#);

end Registers.ATSAM3X8.PMC;

It would be good to be able to mark this package as preelaborable, but this is
illegal because of the call to System.Storage_Elements.To_Address. 
To quote Mark Lorenzen on c.l.a, "the current restriction prevents many
low-level I/O packages from having preelaborable elaboration, which
transitively prevents a whole I/O library from having preelaborable elaboration.”

Philosophically, I don’t see why it shouldn’t be Pure. OK, it elaborates a
variable declaration, but not one that actually _does_ anything!

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

From: Matthias Richter
Sent: Tuesday, July 14, 2015  12:36 PM

GNAT has the implementation-defined attribute System'To_Address which is
identical to System.Storage_Elements.To_Address except that it is static.

See GNAT RM, chapter 4.59 and 9.15 for explanations.

A non-implementation-defined solution would be preferable...

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

From: Tucker Taft
Sent: Tuesday, July 14, 2015  1:26 PM

...
> It would be good to be able to mark this package as preelaborable, but 
> this is illegal because of the call to System.Storage_Elements.To_Address.
> To quote Mark Lorenzen on c.l.a, "the current restriction prevents 
> many low-level I/O packages from having preelaborable elaboration, 
> which transitively prevents a whole I/O library from having 
> preelaborable elaboration.”

As of Ada 2005, the System.Storage_Elements package is marked Pure, and all
Pure packages are also Preelaborate'd.

> Philosophically, I don’t see why it shouldn’t be Pure. OK, it 
> elaborates a variable declaration, but not one that actually _does_ anything!

The above could have a pragma Preelaborate (or "with Preelaborate" if you
prefer), so long as you are using an Ada 2005 compiler or beyond.  Note that
even in Ada 95, package System was *allowed* to be marked Pure, but not
required to be.

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

From: Randy Brukardt
Sent: Tuesday, July 14, 2015  2:16 PM

> The above could have a pragma Preelaborate (or "with Preelaborate" if 
> you prefer), so long as you are using an Ada
> 2005 compiler or beyond.  Note that even in Ada 95, package System was 
> *allowed* to be marked Pure, but not required to be.

Now, you made the same mistake as I did when this originally came up on
comp.lang.ada. I direct your attention to 10.2.1(5) & (7):

  An elaborable construct is preelaborable unless its elaboration performs
  any of the following actions: 
     ...
     * A call to a subprogram other than a static function.

System.Storage_Elements.To_Address is not a static function (currently, no
explicitly declared function can be static), and the elaboration of PMC (and its
address clause, no matter what the syntax) evaluates that function.
Ergo, the package cannot be preelaborated.

There is no workaround (within the language at least), as there is no way to
declare a static constant of type Address. (Janus/Ada doesn't have this
problem, as we never followed the advice to make System.Address private. But
telling everyone to ignore that advice doesn't seem like much of a solution,
and the result still wouldn't be portable [in Janus/Ada, System.Address is a
record]).

However, we do have an aspect (as yet unnamed) on the table to allow the
declaration of static functions in very limited circumstances. Perhaps we
ought to consider extending that idea to private types in order that we
could mark To_Address as potentially static. Or something -- this unintended
restriction surely is silly and aggrevating.

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

From: Randy Brukardt
Sent: Tuesday, July 14, 2015  2:22 PM

...
> Philosophically, I don't see why it shouldn't be Pure. OK, it 
> elaborates a variable declaration, but not one that actually _does_ 
> anything!

If it isn't really a variable, then it could be declared a constant and be 
pure (presuming the other bug is fixed). But pure packages do not allow any
variable state (that's mostly an issue for the Annex E uses of Pure - the idea
is that pure packages can be duplicated in each partition, which doesn't work
if any variables are involved). If you ever write to the variable, then it cannot
be in a pure package.

Seriously, Pure packages are a mistake, there is almost nothing that can be
Pure in Ada. (I've never successfully made any package of mine Pure in my years
of programming.) There are a lot of individual functions that are Pure, but
it's very rare that you have an entire ADT that can be marked Pure (and even if
you can do that, you can't debug the result). As such, I mainly care about
preelaborated packages (as that is a realistic goal). Arguably, everything
should be preelaborated unless declared otherwise, but this is another place
where the default is wrong because of history.

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

From: Simon Wright
Sent: Tuesday, July 14, 2015  2:14 PM

...
> The above could have a pragma Preelaborate (or "with Preelaborate" if you
> prefer), so long as you are using an Ada 2005 compiler or beyond.  Note that
> even in Ada 95, package System was *allowed* to be marked Pure, but not
> required to be.

In GNAT GPL 2015 (and for some time now) both System and
System.Storage_Elements are marked Pure, and in addition To_Address has a
pragma Pure_Function applied to it to avoid precisely this problem (according
to the comments). The pragma appears to be inoperative, because the compiler
says

    16.   PMC : PMC_T
    17.     with
    18.       Import,
    19.       Convention => Ada,
    20.       Address => System.Storage_Elements.To_Address (16#400E0600#);
                                                |
        >>> non-static call not allowed in preelaborated unit

(the pipe symbol is indicating To_Address)

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

From: Tucker Taft
Sent: Tuesday, July 14, 2015  2:39 PM

A static function and a pure function are two different things.  A static
function has compile-time-known semantics.  "Purity" relates to the absence
of side effects.

And thanks Randy for clarifying the question.  I took the question to be one
about the pre-elaborability of System.Storage_Units, but as you point out, the
problem is the fact that To_Address is not a static function.

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

Questions? Ask the ACAA Technical Agent