!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 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 Inconsistences with pure units !summary The restrictions on named access types in pure units only apply to non-derived 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 {non-derived} named access-to-variable type... Change 10.2.1(15.5/2) as follows: ...of a {non-derived} 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 non-visible 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) @drepl A @i @fa is a preelaborable @fa whose elaboration does not perform any of the following actions: @dby A @i compilation unit is a preelaborable compilation unit whose elaboration does not perform any of the following actions: !corrigendum 10.2.1(15.4/2) @drepl @xbullet @dby @xbullet !corrigendum 10.2.1(15.5/2) @drepl @xbullet @dby @xbullet 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 non-visible components whose default initialization evaluates an @fa of an access-to-variable type. !corrigendum 10.2.1(17/2) @drepl A @fa Pure is used to declare that a library unit is pure. If a @fa 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). @dby A @fa 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. 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. > 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. ;-) ****************************************************************