CVS difference for ai05s/ai05-0142-4.txt

Differences between 1.2 and version 1.3
Log of other versions for file ai05s/ai05-0142-4.txt

--- ai05s/ai05-0142-4.txt	2009/05/19 22:33:25	1.2
+++ ai05s/ai05-0142-4.txt	2009/05/23 06:34:03	1.3
@@ -1,10 +1,10 @@
-!standard 3.10(9/2)                                  09-05-19  AI05-0142-4/02
+!standard 3.10(9/2)                                  09-05-22  AI05-0142-4/03
 !class Amendment 09-05-17
 !status work item 09-05-17
 !status received 09-05-17
 !priority Medium
 !difficulty Medium
-!subject Aliased parameters and accessors for Ada.Containers
+!subject Explicitly aliased parameters and accessors for Ada.Containers
 
 !summary
 
@@ -25,7 +25,7 @@
 program.
 
 An option that was rejected for the Ada.Containers packages was to return an
-access to the element type. That is problematic as it is difficult to
+access that designates the element type. That is problematic as it is difficult to
 control the accessibility and lifetime of the returned access. If the element is
 removed from the container, the returned access could become dangling; continued
 use of the access would make the program erroneous. Moreover, the accessibility
@@ -38,8 +38,8 @@
 
 !proposal
 
-Add aliased parameters, which allow 'Access of parts to be returned as access
-discriminants and anonymous access returns.
+Add explicitly aliased parameters, which allow 'Access of parts to be returned
+as access discriminants and anonymous access returns.
 
 !wording
 
@@ -104,13 +104,24 @@
 Add after 6.4.1(6): [Legality Rules]
 
 If the formal parameter is an explicitly aliased parameter, the type of the actual parameter
-shall be tagged or the actual parameter shall be an aliased view of an object.
+shall be tagged or the actual parameter shall be an aliased view of an object. Further,
+if the formal parameter subtype F is untagged:
+  * the subtype F shall statically match the nominal subtype of the actual object; or
+  * the subtype F shall be unconstrained, discriminated in its full view, and
+    unconstrained in any partial view.
 
 AARM Ramification: Tagged objects (and tagged aggregates for *in* parameters) do not need
 to be aliased. This matches the behavior of unaliased formal parameters of tagged types,
 which allow 'Access to be taken of the formal parameter regardless of the form of the
 actual parameter.
 
+AARM Reason: We need the subtype check on untagged actual parameters so that the requirements
+of 'Access are not lost. 'Access makes its checks against the nominal subtype of its prefix,
+and parameter passing can change that subtype. But we don't want this parameter passing to
+change the objects that would be allowed as the prefix of 'Access. This is particularly
+important for arrays, where we don't want to require any additional implementation burden.
+
+
 In a function call, the accessibility level of the actual object
 for each explicitly aliased parameter shall not be statically deeper than
 accessibility level of the master of the function result.
@@ -331,8 +342,8 @@
 The call site checks can fail only in obscure cases, as the vast majority of objects that could be
 passed to a function call will outlive the function result. However, if the function result is
 built-in-place, the result could be made library-level simply by including a call in an allocator
-for a library level access type. In that case, we do not want to be returning an access to a local
-object. The check is normally a static one, and imposes overhead only in rare cases (such as
+for a library level access type. In that case, we do not want to be returning an access designating
+a local object. The check is normally a static one, and imposes overhead only in rare cases (such as
 passing a dereference of an anonymous access parameter).
 
 For example:
@@ -370,6 +381,153 @@
 
 ---
 
+We briefly considered and rejected allowing explicitly aliased function results.
+Such returns would need to work as-if they are returned by reference.
+
+We could allow aliased results when a function call is used directly as the actual
+for an explicitly aliased parameter. We already do allow that a function that returns
+a tagged type (since these are always considered aliased), and most likely we could allow
+something similar for untagged types.
+
+But other uses would require the result to be built in place to get the effect of
+a reference return.  We don't want to require build-in-place semantics for non-limited
+types, as that is not safe when assigning into existing objects, and we don't want to
+add to the implementation burden.
+
+---
+
+The legality rule that requires static matching for untagged subtypes is required in order
+to prevent problems with the parameter passing changing the nominal constraint of the
+object. If 'Access is taken later, it might be legal when otherwise taking 'Access of that
+object directly would have been illegal.
+
+For instance:
+
+     type vector is array (positive range <>) of Natural;
+
+     Actual : aliased vector (1 .. 4); -- Nominal subtype is constrained
+
+     function Has_Four_Elements (x : aliased in vector) return Boolean is
+         type Ref is access constant vector;
+         Ptr : Ref;
+     begin
+         if X'First > 1 then
+            Ptr := Actual'Access; -- (1)
+            return Ptr.all'Length = 4;
+         else
+            Ptr := X'Access; -- (2)
+            return Ptr.all'Length = 4;
+         end if;
+     end Has_Four_Elements;
+
+     Has_Four : constant Boolean := Has_Four_Elements (Actual);
+
+(1) is illegal because the subtypes don't statically match. However, (2) is legal because
+the nominal subtype of X does statically match the designated type of Ref, Vector. We don't
+want incentives to pass something as a parameter so it can be used where it would not be
+able to be used directly.
+
+Note that this check in the array case is primarily about implementations being able to
+continue to require a contiguous representation of array dope. If the compiler does
+not assume that contiguous representation, it would be simple to implement this case
+by creating the needed dope at the point of the function call. Unlike the normal case
+of 'Access (where the needed lifetime of the dope is unclear, making a likely storage
+leak, even if non-continguous representations are supported), here the dope only needs
+to exist as long as the function result does - the same lifetime as any other temporary
+created as part of the evaluation of the actual of an explicitly aliased parameter.
+
+We also need this check in order to prevent mismatches in scalar subtypes; for instance,
+we surely do not want to create an access-to-Integer that designates an aliased Natural
+object.
+
+This rule is annoying as it is reasonable (for a container, for instance) to never actually
+use 'Access on the entire passed object in the body. An access to some component may be
+referenced instead. But we think that for typical containers (implemented as private types,
+either without discriminants or with unconstrained discriminants), a client would never
+run into this rule. That's because of the exception for unconstrained discriminated types
+and because a private type without discriminants could not have a constraint that didn't
+match. The check is most likely to fail for arrays and scalar types, but in these cases it
+it much more likely that the entire object will be returned.
+
+It has been noted that we could leverage the existing 'Constrained flag to make part of this
+check at runtime. However, this really does not help very much. We would still have to use
+static matching if the subtype of the explicitly aliased parameter is constrained. And most
+other cases are already legal. We could change the 6.4.1(6) wording to:
+
+Further, if the formal parameter subtype F is untagged:
+  * the subtype F shall statically match the nominal subtype of the actual object; or
+  * the subtype F shall be unconstrained and discriminated.
+
+But then we would need to add after to 3.10.2(29):
+   If the designated type D is untagged, discriminated in its full view, and constrained
+   in any partial view, a check is made X'Constrained is False; Program_Error is raised
+   if this check fails.
+
+AARM Notes: This check can only fail if the view is a formal parameter, in all other cases
+static matching will ensure that it is successful. 
+
+This doesn't seem to save much wording nor allow many additional cases.
+
+We could get more bang for our buck by defining another attribute that would work on arrays:
+   A'Nominally_Constrained
+     For an explicitly aliased formal parameter F of an array type,
+     returns True if the nominal subtype of the actual object is constrained, and returns
+     False otherwise. For other objects of an array type, returns True if the nominal subtype
+     of the object is constrained, and returns False otherwise.
+
+Then the 6.4.1(6) wording could be changed to:
+Further, if the formal parameter subtype F is untagged:
+  * the subtype F shall statically match the nominal subtype of the actual object; or
+  * the subtype F shall be unconstrained and either discriminated or an array subtype.
+
+We would need to add after to 3.10.2(29) (in addition to the wording above):
+   If the designated type D is an array type and unconstrained, a check is made
+   X'Nominally_Constrained is False; Program_Error is raised if this check fails.
+
+This would have some additional runtime overhead, but would allow more flexibility for
+array parameter passing (any sort of array object could be passed to an explicitly aliased
+unconstrained array parameter).
+
+Note that we could make changes like these in the future; they would be compatible with the
+current rules (as they would only allow more). So we adopt the more restrictive rule now
+and we can liberalize it in the future if needed.
+
+---
+
+Given that we had to make a check of 3.10.2(27-27.2/2) to avoid problems, one might wonder
+why we don't need to make a matching check of 3.10.2(26). Here, however, the existing rules
+of erroneousness prevent trouble. To see that, consider:
+
+     type Rec (B : Boolean := False) is record
+        case B is
+           when True =>
+               C1 : aliased Natural;
+           when False => null;
+        end case;
+     end record;
+
+     Obj : Rec := (True, C1 => 10);
+
+     function A_Func (P : aliased in Natural) return access Natural is
+     begin
+         Obj := (False); -- (1)
+         return P'Access; -- (2)
+     end A_Func;
+
+     if A_Func.all(Obj.C1) = 10 then
+         null;
+     end if;
+
+The assignment to Obj at (1) changes the discriminant and causes the component Rec.C1 to cease
+to exist. That's bad because that component has been passed (by reference) as an aliased
+parameter, and we then use that parameter after the component has gone away.
+
+Luckily, 3.7.2(4) makes execution of A_Func erroneous. Thus we don't need additional checks
+for this case. (And it would be weird to have them, given that regular parameter passing
+does not try to prevent this problem.)
+
+---
+
 We need to be able to define the period of existence of the return object of the accessor
 as a time when tampering with elements is prohibited. If we didn't do this, we would
 be opening the door to all manner of problems (up to and including erroneousness) if
@@ -1564,4 +1722,338 @@
 
 You're right.
 
+****************************************************************
+
+From: Bob Duff
+Sent: Tuesday, May 19, 2009  5:35 PM
+
+> > > Otherwise, Accessor returns an object whose discriminant is an access
+> > > to =
+> > 
+> > "discriminant designates"
+> 
+> That can't be right, 3.10(1) says that "designates" is something that 
+> an access value does to an object. I don't mind using a better verb 
+> here, but one that has a defined meaning (and surely could be confused 
+> here, given that we're talking about access types) won't do.
+> 
+> We could say "an access that designates the element" (that's better 
+> than "to").
+
+I don't understand your objection.  It seems like "designates" is exactly
+what you want, here.  The discriminant is an access value, and it designates
+some object (a component of a vector or whatever).
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Tuesday, May 19, 2009  6:03 PM
+
+Your suggestion "discriminant designates an access to ..." does not make sense.
+
+Best I can do is: "discriminant is an access that designates ..." does make sense.
+
+****************************************************************
+
+From: Bob Duff
+Sent: Tuesday, May 19, 2009  6:03 PM
+
+> Your suggestion "discriminant designates an access to ..." does not 
+> make sense.
+
+I agree it makes no sense, but that's not my suggestion.  ;-) Sorry for being unclear.
+My suggestion is to replace this:
+
+    Otherwise, Accessor returns an object whose discriminant is an access to
+    the element designated by Position.
+
+with this:
+
+    Otherwise, Accessor returns an object whose discriminant designates
+    the element designated by Position.
+
+The following also works:
+
+    Otherwise, Accessor returns an object whose discriminant is an access value
+    that designates the element designated by Position.
+
+but I think it's unnecessarily verbose -- we know it's an access value, because it's
+declared to be of an access type, just a few paragraphs earlier.
+
+I don't think we ever use the phrase "an access to" in the RM.
+(If we called the silly things "pointers", like the rest of the world does, wording
+would be easier!)
+
+Note: similar wording appears elsewhere in the AI.
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Wednesday, May 20, 2009  12:24 PM
+
+> but I think it's unnecessarily verbose -- we know it's an access 
+> value, because it's declared to be of an access type, just a few 
+> paragraphs earlier.
+
+OK, but I disagree. I really want to emphasize that an access (not some sort of
+value) is being returned.
+ 
+> I don't think we ever use the phrase "an access to" in the RM.
+> (If we called the silly things "pointers", like the rest of the world 
+> does, wording would be easier!)
+
+I do realize that, which is why I did replace the "to" with "that designates".
+
+> Note: similar wording appears elsewhere in the AI.
+
+I only found 3 instances of "access to". The first one is in the !problem section,
+which is informal but probably would be better off written properly anyway. The
+second one is talking about taking 'Access, not the same thing at all. The third
+one is in the !discussion, again informal text, but I changed it anyway.
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Thursday, May 21, 2009  5:27 AM
+
+I had a couple of "waking up" thoughts, as Jimmy Carter used to call them:
+
+   1) Since formal parameters of a discriminated type
+      have a 'Constrained attribute, we can accommodate
+      Randy's desire to relax the matching rules somewhat
+      for explicitly aliased formal parameters of a
+      discriminated type.
+
+      For 'Access, since there is no 'Constrained attribute
+      for heap objects, we have to have tighter matching
+      rules since you have to be able to decide whether
+      a heap object is constrained from the declaration
+      of the access type (now that we allow unconstrained
+      objects in the heap).
+
+      In fact, I don't think this will make much difference,
+      but at least the wording will be simpler, since we
+      don't need to talk about full views and partial views
+      and all of that.  Essentially the idea is that the
+      tighter matching requirements for 'Access will be
+      enforced if and when someone takes 'Access of an
+      explicitly aliased formal, by making use of the
+      'Constrained attribute.
+
+      Note that we could add a similar attribute for aliased
+      formals of an array type, though it would indicate whether
+      the actual's nominal *subtype* was constrained, because
+      of course all array *objects* are constrained.
+
+[Editor's note: The other thought pertains to AI05-0135-1 and is filed in that AI.]
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Friday, May 22, 2009  12:45 PM
+
+>    1) Since formal parameters of a discriminated type
+>       have a 'Constrained attribute, we can accommodate
+>       Randy's desire to relax the matching rules somewhat
+>       for explicitly aliased formal parameters of a
+>       discriminated type.
+> 
+>       For 'Access, since there is no 'Constrained attribute
+>       for heap objects, we have to have tighter matching
+>       rules since you have to be able to decide whether
+>       a heap object is constrained from the declaration
+>       of the access type (now that we allow unconstrained
+>       objects in the heap).
+
+That's exactly what I was talking about when I said that I thought a runtime check might
+be better. I had forgotten that we already require the necessary mechanism for discriminated
+types.
+
+>       In fact, I don't think this will make much difference,
+>       but at least the wording will be simpler, since we
+>       don't need to talk about full views and partial views
+>       and all of that.  Essentially the idea is that the
+>       tighter matching requirements for 'Access will be
+>       enforced if and when someone takes 'Access of an
+>       explicitly aliased formal, by making use of the
+>       'Constrained attribute.
+
+Well, it would allow something like:
+
+    type Rec (D : Natural := 100) is ...
+
+    Cobj : aliased Rec(10);
+
+    function Accessor (Param : aliased in out Rec; ...) return ...
+
+    := Accessor (Cobj);
+
+as long as the body of Accessor does not use 'Access of the entire parameter into an
+access-to-Rec.
+
+Actually, this seems pretty important, because the above is very similar to the form
+of the bounded containers. The actual objects have bounds, but of course the subprograms
+take unconstrained objects. (The difference is that the bounded containers, being tagged
+objects, don't allow defaulted discriminants. But surely I can imagine someone building
+an untagged container.)
+
+    Acc : access Rec := Cobj'Access; -- Illegal.
+
+(At least I hope the above is illegal, 'cause it would allow changing the discriminant
+of a constrained object!! Acc.all := (D => 20, ...) surely is legal.)
+
+>       Note that we could add a similar attribute for aliased
+>       formals of an array type, though it would indicate whether
+>       the actual's nominal *subtype* was constrained, because
+>       of course all array *objects* are constrained.
+
+Well, you've almost convinced me that that isn't important. I'd rather not add any runtime
+overhead to the parameter passing if we can avoid it.
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Friday, May 22, 2009  2:12 PM
+
+> Well, it would allow something like:
+> 
+>     type Rec (D : Natural := 100) is ...
+> 
+>     Cobj : aliased Rec(10);
+> 
+>     function Accessor (Param : aliased in out Rec; ...) return ...
+> 
+>     := Accessor (Cobj);
+> 
+> as long as the body of Accessor does not use 'Access of the entire 
+> parameter into an access-to-Rec.
+
+This would be legal even if we used the rules for 'Access, though I realize you would
+need convincing.  The relevant paragraph is 3.10.2(27.2).  But I agree it is simpler
+to explain and understand if we simply fall back on the availability of the 'Constrained
+attribute.
+
+> Actually, this seems pretty important, because the above is very 
+> similar to the form of the bounded containers. The actual objects have 
+> bounds, but of course the subprograms take unconstrained objects. (The 
+> difference is that the bounded containers, being tagged objects, don't 
+> allow defaulted discriminants. But surely I can imagine someone 
+> building an untagged
+> container.)
+
+The only time "access Rec" doesn't allow "Cobj'Access"
+is if there is a partial view of Rec that is constrained (that is, the partial view is
+simply "type Rec is private;", and the discriminants are only visible in the private part).
+We don't allow Cobj'Access in that case because the assumption is that all designated
+objects are unconstrained (to avoid privacy breaking).  For a container either the
+discriminants would be visible, or clients could never declare constrained objects,
+so you would never bump into the problem.
+
+But as I said, if by falling back on 'Constrained we can simplify the description and
+make one R.R. happier, that's fine with me!
+
+>     Acc : access Rec := Cobj'Access; -- Illegal.
+> 
+> (At least I hope the above is illegal, 'cause it would allow changing 
+> the discriminant of a constrained object!! Acc.all := (D => 20, ...) 
+> surely is
+> legal.)
+
+No it's not legal to change the discriminants through an access value, since
+in most cases, heap objects are constrained. In 2005 we added an exception for
+types whose partial view has no visible discriminant part -- those are presumed
+*always* unconstrained in the heap, but of course only code that has visibility
+on the full type would even notice.  At least that is what 3.10.2(27.2) is
+*supposed* to be accomplishing. Apparently at least in your case, it accomplished
+total obfuscation.  Perhaps a note (if only in the AARM) might be useful on this paragraph.
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Friday, May 22, 2009  11:45 PM
+
+...
+> > Well, it would allow something like:
+> > 
+> >     type Rec (D : Natural := 100) is ...
+> > 
+> >     Cobj : aliased Rec(10);
+> > 
+> >     function Accessor (Param : aliased in out Rec; ...) return ...
+> > 
+> >     := Accessor (Cobj);
+> > 
+> > as long as the body of Accessor does not use 'Access of the entire 
+> > parameter into an access-to-Rec.
+> 
+> This would be legal even if we used the rules for 'Access, though I 
+> realize you would need convincing.  The relevant paragraph is 
+> 3.10.2(27.2).  But I agree it is simpler to explain and understand if 
+> we simply fall back on the availability of the 'Constrained attribute.
+
+I obviously have had a brain meltdown, must be the sudden onset of summer (we seem
+to have skipped spring altogether). But now I wonder if 'Constrained would be helpful.
+
+> > Actually, this seems pretty important, because the above is very 
+> > similar to the form of the bounded containers. The actual objects 
+> > have bounds, but of course the subprograms take unconstrained 
+> > objects. (The difference is that the bounded containers, being 
+> > tagged objects, don't allow defaulted discriminants. But surely I 
+> > can imagine someone building an untagged container.)
+> 
+> The only time "access Rec" doesn't allow "Cobj'Access"
+> is if there is a partial view of Rec that is constrained (that is, the 
+> partial view is simply "type Rec is private;", and the discriminants 
+> are only visible in the private part).
+> We don't allow Cobj'Access in that case because the assumption is that 
+> all designated objects are unconstrained (to avoid privacy breaking).  
+> For a container either the discriminants would be visible, or clients 
+> could never declare constrained objects, so you would never bump into 
+> the problem.
+
+Humm. In that case, 'Constrained is definitely *not* what we want. We want static matching
+in all cases unless the parameter subtype is discriminated and unconstrained (unconstrained
+is the usual case, of course). And we don't want any checks at all unless the partial view
+exception exists. While 'Constrained will be true for a passed-in constrained object. So we
+would still need all of the "partial view" wording on the runtime check at the point of 'Access,
+because that is the only case when we would want to check that. (We surely don't want to
+check it for access-to-"ordinary"-unconstrained, because there is no reason to allow *less* here).
+
+I don't quite buy the last sentence, although I agree that *clients* couldn't bump into the
+problem. But if that is the case, we might as well just stick with the original legality rules
+on the call, since it would be fairly hard for them to fail in normal usage, so long as people
+avoid messing with aliased array parameters or aliased scalar parameters. And you'd convinced
+me that we really don't care about those latter cases.
+
+(That will come back to bite us, I suspect. BTW, you said that you couldn't remember the last
+time someone complained about these static matching rules. As it turns out, it was Adam in
+Ada-Comment waaaay back in March. Of 2009. Like I said, it is a *very* common complaint.)
+
+I'll add some "alternative" discussion about potential runtime checks to avoid the static checks.
+
+> >     Acc : access Rec := Cobj'Access; -- Illegal.
+> > 
+> > (At least I hope the above is illegal, 'cause it would allow 
+> > changing the discriminant of a constrained object!! Acc.all := (D => 
+> > 20, ...) surely is legal.)
+> 
+> No it's not legal to change the discriminants through an access value, 
+> since in most cases, heap objects are constrained.
+
+Well, you have convinced me that the above is legal. <PEDANT> And it is *legal* to try to
+change the discriminants of a designated object: but it must raise Constraint_Error.</PENDANT>
+
+> In 2005 we added an exception for types whose partial view has no 
+> visible discriminant part -- those are presumed
+> *always* unconstrained in the heap, but of course only code that has 
+> visibility on the full type would even notice.  At least that is what 
+> 3.10.2(27.2) is *supposed* to be accomplishing.
+> Apparently at least in your case, it accomplished total obfuscation.  
+> Perhaps a note (if only in the AARM) might be useful on this 
+> paragraph.
+
+I think there is such a note in the vicinity. But of course I thought I remembered how this
+all works, rather than actually looking it up. Which doesn't always work out. :-) And what I
+did look up I read in the paper RM, so I didn't see the AARM notes.
+ 
 ****************************************************************

Questions? Ask the ACAA Technical Agent