Version 1.8 of ai05s/ai05-0035-1.txt
!standard 10.2.1(15.1/2) 07-10-01 AI05-0035-1/04
!standard 10.2.1(15.4/2)
!standard 10.2.1(15.5/2)
!standard 10.2.1(17/2)
!class binding interpretation 07-01-12
!status Amendment 201Z 08-11-26
!status WG9 Approved 07-11-08
!status ARG Approved 8-0-2 07-06-03
!status work item 07-01-12
!status received 07-01-12
!priority Medium
!difficulty Easy
!qualifier Omission
!subject Inconsistencies with pure units
!summary
The restrictions on named access types in pure units only apply to nonderived types.
The rules for pure units are enforced inside of generic units (not just inside of
instance specifications).
Subunits follow the same rules as library units for purity.
Only subunits that are elaborated are required to be pure, just as in the case of
preelaborated units.
!question
1 - 10.2.1(15.4-15.5/2) apply to all named access-to-object types. However, derived
access types do not declare storage pools (they use the ones declared by the parent
type), and thus do not cause any added state. As such, there is no reason for them
to have rules applied. Should they be excluded from these rules? (Yes.)
2 - The elaboration of generic units (as opposed to instances) does exactly nothing.
The rules 10.2.1(15.1/2-15.5/2) therefore do not apply inside of a pure generic unit.
Rechecking will happen on the instance, but because the boilerplate text is not
given in 10.2.1(17/2), rechecking only happens in the visible part.
That means that the pure unit restrictions are not applied to the private part and body
of a generic unit. That provides a simple way to add a variable to a pure unit:
just instantiate an appropriate generic. That doesn't seem right...
Contrast this to the situation for preelaborated units: the rules apply in the
private part, and there are special rules to apply to generic bodies. All of this seems
to be missing for pure units.
Is this hole intended? (No.)
3 - The rules in 10.2.1(15.1/2) apply only to library items. However, 10.2.1(17/2)
requires that all compilation units of a pure unit be pure. A subunit is a
compilation unit, so when (if ever) is a subunit pure?
4 - AI95-0002 changes the rules in 10.2.1(11/1) to exclude some subunits of
preelaborated units to not require preelaboration.
However, no corresponding change was made to pure units. The same considerations
seem to apply: something legal in a nested unit shouldn't become illegal simply
because the unit was changed into a subunit. Moreover, "pure" never seems to have
been considered in AI95-0002, so it appear this difference is an oversight.
Moreover, since pure units are also preelaborated, we have the bizarre situation
where a unit needs pure restriction checks but not preelaboration restriction
checks. This combination seems to provide more confusion than value.
Should this rule be changed to match preelaboration? (Yes.)
!recommendation
(See Summary.)
!wording
1 - Change 10.2.1(15.4/2) as follows:
...of a {nonderived} named access-to-variable type...
Change 10.2.1(15.5/2) as follows:
...of a {nonderived} named access-to-constant type...
2 - Add after 10.2.1(15.5/2):
A generic body is pure only if elaboration of a corresponding instance body
would not perform any such actions presuming any composite formal types have
nonvisible components whose default initialization evaluates an allocator
of an access-to-variable type.
3 - Change 10.2.1(15.1/2) to:
A pure compilation unit is a preelaborable compilation unit whose elaboration...
2 & 4 - Change 10.2.1(17/2) as follows:
A pragma Pure is used to declare that a library unit is pure. {The declaration and
body of a declared pure library unit, and all subunits that are elaborated as part
of elaborating the library unit, shall be pure. In addition, all} [If a pragma
Pure applies to a library unit, then its] compilation units {of a declared pure
library unit} [shall be pure, and they] shall depend semantically only on
compilation units of other library units that are declared pure. {In addition
to the places where Legality Rules normally apply (see 12.3), this rule also
applies in the private part of an instance of a generic unit.} Furthermore,
the full view of any partial view declared in the visible part of the library
unit that has any available stream attributes shall support external streaming (see 13.13.2).
!discussion
1 - Derived access types always share their storage pool with their parent type.
Thus, the properties of the pool were checked for the parent type, and no further
checks are needed. This especially matters in generics, where we otherwise would
need an assume-the-worst rule for derivations from formal access types in
generic bodies. With this rule change, we don't need such a rule, and pure generic
packages then will have fewer restrictions.
2 - Given change (1), none of the rules in 10.2.1(15.1-15.5/2) depend on the
properties of the actual parameters to an instance.
Therefore, we can use a fairly simple assume-the-worst rule for generic bodies,
and require the (re)checking be done for the instance specifications. This latter
requires rechecking in the private part of an instance.
We could have gone simpler still, and simply always make the checks on generic units. However,
that would add restrictions to the generic specification of pure generic units that
may not matter if the generic is always instantiated in impure contexts. Also, rechecking
is consistent with a very similar rules for Preelaborate. It would be confusing to
use different rules for the two categorizations, especially as pure requires
preelaborate's restrictions to have been followed.
3 - A subunit is not a library item. Thus, there is no definition for what constitutes
a pure subunit. One option is to simply say that no subunits are pure. However, that
would effectively ban subunits of pure units, which seems unnecessarily restrictive.
It can be argued that the pure rules are intentionally designed to ignore subunits (that
is, 10.2.1(17/2) should exempt subunits). However, then a package subunit of a pure
package would not be required to be pure or even preelaborated, which would open up
all sorts of holes.
Thus, we do have to define what a pure subunit is. The easiest way is to ensure that
the definition of pure applies to all compilation units, not just library_items.
4 - The rules for Preelaborate and Pure are supposed to be parallel to each other,
and this is an unnecessary difference. We need to make this change after fixing (2);
this is not a real problem until (2) is fixed, as no subunits would be required
to be pure by the original wording.
!corrigendum 10.2.1(15.1/2)
Replace the paragraph:
A pure library_item is a preelaborable library_item whose
elaboration does not perform any of the following actions:
by:
A pure compilation unit is a preelaborable compilation unit whose
elaboration does not perform any of the following actions:
!corrigendum 10.2.1(15.4/2)
Replace the paragraph:
- the elaboration of the declaration of a named
access-to-variable type unless the Storage_Size of the type
has been specified by a static expression with value zero or
is defined by the language to be zero;
by:
- the elaboration of the declaration of a nonderived named
access-to-variable type unless the Storage_Size of the type
has been specified by a static expression with value zero or
is defined by the language to be zero;
!corrigendum 10.2.1(15.5/2)
Replace the paragraph:
- the elaboration of the declaration of a named
access-to-constant type for which the Storage_Size has been specified by an
expression other than a static expression with value zero.
by:
- the elaboration of the declaration of a nonderived named
access-to-constant type for which the Storage_Size has been specified by an
expression other than a static expression with value zero.
A generic body is pure only if elaboration of a corresponding instance body
would not perform any such actions presuming any composite formal types have
nonvisible components whose default initialization evaluates an allocator
of an access-to-variable type.
!corrigendum 10.2.1(17/2)
Replace the paragraph:
A pragma Pure is used to declare that a library unit is pure.
If a pragma Pure applies to a library unit,
then its compilation units shall be pure,
and they shall depend semantically only on
compilation units of other library units that are
declared pure. Furthermore, the full view of any
partial view declared in the visible part of the library unit that has
any available stream attributes shall support external streaming
(see 13.13.2).
by:
A pragma Pure is used to declare that a library unit is pure. The declaration and
body of a declared pure library unit, and all subunits that are elaborated as part
of elaborating the library unit, shall be pure. In addition, all
compilation units of a declared pure
library unit shall depend semantically only on
compilation units of other library units that are declared pure. In addition
to the places where Legality Rules normally apply (see 12.3), this rule also
applies in the private part of an instance of a generic unit. Furthermore,
the full view of any partial view declared in the visible part of the library
unit that has any available stream attributes shall support external streaming (see 13.13.2).
!ACATS test
1 - The only testable scenario would be access types derived from a formal access
type; this generic could be instantiated in an impure context with any sort of type.
This seems rather pathological.
2 - B-Tests should be created to ensure that the checks are made in the generic unit.
3 - A B-Test should ensure that pure restrictions are enforced in a package subunit of
a pure package.
4 - A C-Test should ensure that a package subunit of a pure subprogram does not need
to be pure.
!appendix
From: Randy Brukardt
Date: Friday, January 12, 2007 7:31 PM
Pascal had written:
> The elaboration of generic specifications does exactly nothing. So
> anything is permitted in a generic specification. Rechecking will happen
> on the instance, and then only if the instance itself is marked as
> preelaborable. Where we need special rules is for generic bodies, because
> while the elaboration of generic bodies also does nothing, we won't have a
> chance to recheck anything on the instance. All of this is nicely
> explained in AARM 10.2.1(10.a.1/2).
Well, while I agree with your explanation, now I have a new worry, having moved
on to Pure.
Following this explanation, none of the extra Pure restrictions of
10.2.1(15.1-15.4/2) would enforced inside of a generic template. Moreover,
10.2.1(17/2) does not have the wording to enforce them in a private part. And
there is no generic special cases for Pure like there are for Preelaborate.
Therefore, I must conclude that they're never enforced for the private part
and body of generic units.
Thus, if you want a variable or allocator or access type with non-empty
collection in a Pure package, just instantiate an appropriate generic. That
doesn't seem right...
Example:
generic
type Anything is private;
pragma Preelaborable_Initialization (Anything);
package Add_a_Var is
pragma Pure (Add_A_Var);
procedure Set (Val : in Anything);
function Get return Anything;
end Add_a_Var;
package body Add_a_Var is
My_Var : Anything; -- Legal, elab of Add_a_Var does not elaborate this.
-- Anything has PInit in order to pass 10.2.1(10.1/2).
procedure Set (Val : in Anything) is
begin
My_Var := Val;
end Set;
function Get return Anything is
begin
return My_var;
end Get;
end Add_a_Var;
with Add_a_Var;
package Oops is
pragma Pure (Oops);
package Int_Var is new Add_a_Var (Integer); -- Legal, nothing bad in visible part.
end Oops;
And now you've added a variable to your Pure package, upsetting everything that
assumes no state.
I suspect that we need to do two things to plug this hole:
(1) Add the boilerplate to 10.2.1(17/2) so that the additional Pure rules are
rechecked in the private part;
(2) Add after 10.2.1(15.6/2) something like:
A generic_body is pure only if elaboration of a corresponding instance body
would not perform any such actions.
[We don't need any "presuming that"s, because 15.2-5/2 don't depend on the
properties of the types involved, other than that it is access type.]
Humm, since the rules don't depend on the formal types, probably we should
simply enforce them in both halves of the generic unit (that is, in both the
spec and body). That doesn't require any rechecking in an instance. Of course,
that would be different than the Preelaboration rules, and might cause trouble
in (language) maintenance. That would look like:
(2) Add after 10.2.1(15.6/2) something like:
A generic unit is pure only if elaboration of the corresponding unit of an
instance would not perform any such actions.
Which one do you prefer? I'll make another AI for this one. Sigh.
****************************************************************
From: Pascal Leroy
Date: Monday, January 15, 2007 2:11 AM
> Well, while I agree with your explanation, now I have a new
> worry, having moved on to Pure.
>
> Following this explanation, none of the extra Pure restrictions of
> 10.2.1(15.1-15.4/2) would enforced inside of a generic
> template. Moreover,
> 10.2.1(17/2) does not have the wording to enforce them in a
> private part. And there is no generic special cases for Pure
> like there are for Preelaborate. Therefore, I must conclude
> that they're never enforced for the private part and body of
> generic units.
I agree. Note that it all goes back to the original Ada 95: while there
were rules for preelaboration, there has never been anything for purity.
Strange.
> (1) Add the boilerplate to 10.2.1(17/2) so that the
> additional Pure rules are rechecked in the private part;
Correct.
> (2) Add after 10.2.1(15.6/2) something like:
>
> A generic_body is pure only if elaboration of a corresponding
> instance body would not perform any such actions.
>
> [We don't need any "presuming that"s, because 15.2-5/2 don't
> depend on the properties of the types involved, other than
> that it is access type.]
I don't think that's true. It seems to me that 10.2.1(15.4/2-15.5/2)
effectively depend on whether the actual for a formal access type has
storage size 0. Consider:
generic
type AV is access Integer;
type AC is access constant Boolean;
package G is
pragma Pure (G);
end G;
package body G is
type DAV is new AV; -- Legal? Depends on the storage size of AV.
type DAC is new AC; -- Legal? Depends on the storage size of AC.
end G;
Obviously we want the declarations of DAV and DAC to be illegal, but we
need an explicit rule to say so. And you cannot say "a formal access type
is defined to have storage size 0", because that would make allocators for
such types illegal everywhere.
> I'll make another AI for this one. Sigh.
Yes, we have to.
****************************************************************
From: Randy Brukardt
Date: Monday, January 15, 2007 5:53 PM
> I don't think that's true. It seems to me that 10.2.1(15.4/2-15.5/2)
> effectively depend on whether the actual for a formal access type has
> storage size 0. Consider:
Do we have to?? ;-)
> generic
> type AV is access Integer;
> type AC is access constant Boolean;
> package G is
> pragma Pure (G);
> end G;
>
> package body G is
> type DAV is new AV; -- Legal? Depends on the storage size of AV.
> type DAC is new AC; -- Legal? Depends on the storage size of AC.
> end G;
No, I never thought of deriving from a formal access type, it's not exactly
something that you would normally want to do.
<grumble>
Why the heck do we even allow derived access types? What could they possibly
be useful for? Usually, we want less type checking with access types, not more.
Sigh.
</grumble>
> Obviously we want the declarations of DAV and DAC to be illegal, but we
> need an explicit rule to say so. And you cannot say "a formal access type
> is defined to have storage size 0", because that would make allocators for
> such types illegal everywhere.
Yes, "presuming that any formal access types have a non-zero storage size
specified."
> > I'll make another AI for this one. Sigh.
>
> Yes, we have to.
Long done. And now redone.
****************************************************************
From: Tucker Taft
Date: Monday, January 15, 2007 6:46 PM
Unless I am missing something (which is entirely possible)
I don't understand why we "obviously" want DAV and DAC to
be illegal. Declaring a derived access type has no
particular effect, from the point of view of storage
pools. It is little more than a renaming or a subtype
declaration. We should only disallow declaring
non-derived access types with non-zero storage pools.
There is never any harm in declaring derived access types.
****************************************************************
From: Randy Brukardt
Date: Monday, January 15, 2007 7:42 PM
Well, we're discussing how the existing rules should be implemented
in generic bodies. We weren't discussing whether those rules were
too broad. It surely doesn't make sense to have different rules in
generic bodies than in the rest of the Ada universe.
Now, I suppose you could argue that the existing rules are broader
than they need to be, and it is just as easy to change that rather
than fix the generic body bugs. But we're in bug fixing mode now, not
redesign the language mode. It would be easier still (and just as useful)
to replace 10.2.1(15.1/2-18/2) entirely with "pragma Pure is a synonym
for pragma Preelaborate"! ;-)
****************************************************************
From: Tucker Taft
Date: Monday, January 15, 2007 10:59 PM
I think both 10.2.1(15.4) and 10.2.1(15.5) should talk
only about non-derived access types. I think it was
just an oversight that derived access types got lumped into this
restriction, since derived access types never create
a new storage pool. I can believe you are frustrated,
but this seems like a pretty straightforward goof, and it
happens to solve Pascal's contract model problem.
****************************************************************
From: Pascal Leroy
Date: Wednesday, January 17, 2007 8:42 AM
I agree with Tuck.
The fix is probably to add "nonderived" to 10.2.1(15.4/2-15.5/2).
I agree that we are in bug fixing mode, but we clearly have a bug here (we
need to say something about generic bodies) and either we invent
convoluted rules for generic bodies, or we add "nonderived" in two places.
I certainly prefer the latter.
****************************************************************
From: Randy Brukardt
Date: Wednesday, January 17, 2007 12:32 PM
OK, but remember that we still need the rules for generic bodies, they're
just a bit less convoluted. Because we surely want to prohibit variables
in pure generic bodies, and that's not true currently. The net effect is
that one bug (generic bodies aren't checked) has turned into three. C'est la
vie.
***************************************************************
From: Randy Brukardt
Date: Friday, January 12, 2007 8:45 PM
You'd think I was getting paid by the number of holes I find...
AI95-0002 changes the rules to exclude some subunits of preelaborated units to be
not require preelaboration.
Shouldn't that change also have been made to 10.2.1(17/2) for pure units?? The
same considerations would appear to apply, even though "pure" is never mentioned
by the AI or any discussion that I could find.
In particular, should a package subunit of a pure subprogram be required to be
pure? I don't think so; it's not elaborated by the elaboration of the pure subprogram.
I'll add that to the other pure issue.
****************************************************************
From: Pascal Leroy
Date: Monday, January 15, 2007 3:57 AM
I'm not sure if there is actually a real problem here, although surely the
fact that the wording is different is puzzling.
First, note that 10.2.1(5) talks about "construct" while 10.2.1(15.1/2)
talks about "library_item".
Because of the wording of 10.2.1(5), a package subunit of a preelaborable
subprogram may be non-preelaborable. But since it is actually not
elaborated when the enclosing subprogram is elaborated, we need the second
sentence of 10.2.1(11/1) to say "well, a subunit may well be
non-preelaborable but it's OK if it's not elaborated when the library unit
gets elaborated".
On the other hand, 10.2.1(15.1/2) is worded in terms of what happens when
the library_item get elaborated (and that seems much more sensible than
worrying about the elaboration of arbitrarily nested constructs). Surely
when a library subprogram get elaborated any package subunit that it may
contain *doesn't* get elaborated, so none of the actions of
10.2.1(15.2/2-15.5/2) take place (at least, not because of the subunit).
From which I conclude that the RM is correct but unnecessarily obscure.
****************************************************************
From: Randy Brukardt
Date: Monday, January 15, 2007 5:31 PM
> First, note that 10.2.1(5) talks about "construct" while 10.2.1(15.1/2)
> talks about "library_item".
Good point. But that means none of this applies to a subunit (which is never
a library item).
> Because of the wording of 10.2.1(5), a package subunit of a preelaborable
> subprogram may be non-preelaborable. But since it is actually not
> elaborated when the enclosing subprogram is elaborated, we need the second
> sentence of 10.2.1(11/1) to say "well, a subunit may well be
> non-preelaborable but it's OK if it's not elaborated when the library unit
> gets elaborated".
Right.
> On the other hand, 10.2.1(15.1/2) is worded in terms of what happens when
> the library_item get elaborated (and that seems much more sensible than
> worrying about the elaboration of arbitrarily nested constructs). Surely
> when a library subprogram get elaborated any package subunit that it may
> contain *doesn't* get elaborated, so none of the actions of
> 10.2.1(15.2/2-15.5/2) take place (at least, not because of the subunit).
OK, but what happens for a package subunit of a pure package?
10.2.1(17/2) says "... then its compilation units shall be pure". Surely a
subunit is a compilation unit, but it is not a library_item. Thus, I could
conclude that a pure package cannot have subunits at all.
Surely, we want package subunits of a pure package to be pure and preelaborated.
(That is, the rules need to be enforced.) But if we just fix the language to make
that true, then we catch package subunits of a pure subprogram.
The easiest way to do that is to adopt the AI-2 language, and fix 10.2.1(15.1/2)
to say "compilation unit" rather than "library_item". I'm sure there is another
way to do it, but why try to work out something from scratch? It's necessarily
going to be wrong.
> From which I conclude that the RM is correct but unnecessarily obscure.
Sorry, we're not going to get off that easily. ;-)
****************************************************************
Questions? Ask the ACAA Technical Agent