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

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

--- ai05s/ai05-0142-2.txt	2009/04/04 06:48:18	1.1
+++ ai05s/ai05-0142-2.txt	2009/04/30 06:19:47	1.2
@@ -329,6 +329,1023 @@
 
 !appendix
 
+From: Steve Baird
+Sent: Wednesday, April  8, 2009  5:24 PM
 
+> In the interests of putting my effort where my mouth is, here is one
+> of my action items:
+>
+>> Randy: Create an AI-142 alternative using "limited access".
+[This is AI05-142-2/01 - ED.]
+
+To solve the problem that the AI is trying to address, we do not need limited
+access types other than as parameter and function result types. I think there
+may be complexity without corresponding benefit in allowing them in other cases.
+
+For example, a limited discriminant type sounds like a mess (membership testing
+implicitly involves equality testing).
+
+Wouldn't a limited access non-discriminant component type require much the same
+accessibility checks as an access discriminant?
+
+What prevents a dangling reference in this case?
+
+     type R is
+        limited record
+           F : limited access Some_Tagged_Type'Class;
+         end R;
+
+      type Ref is access R;
+      Ptr : Ref;
+
+      function Func (X : Some_Tagged_Type'Class) return
+        limited access Some_Tagged_Type'Class is
+      begin
+          return X'Access;
+      end Func;
+
+      procedure P
+          Y: Some_Extension;
+      begin
+          Ptr := new R'(F => Func (X => Y));
+      end P;
+
+Similar problems seem to exist if the result type of the function is R and the
+function returns an aggregate.
+
+What would be the benefit of allowing
+      X : limited access T := ... ;
+?
+Why would anyone want to do this?
+
+----
+
+Do we really need limited access types as parameters?
+What would be the consequences of taking a program which makes use of such
+parameters and deleting the word "limited" from all of the parameter
+declarations?
+
+You mention in the !proposal section that a function with a limited access
+result type wants to be able to return a limited access parameter, but this
+could be accomplished by allowing it to return any access parameter, whether
+limited or not. Thus, this does not provide a justification for limited access
+parameters.
+
+It seems that this new construct should be allowed only for function results.
+
+----
+
+Does the definition of "immutably limited" need updating?
+Even if it doesn't strictly need it, would it still be a good idea?
+
+Certainly "mode conformant" would need updating.
+I understand that the wording section included "** Rest TBD **".
+I'm just mentioning this because it crossed my mind.
+
+----
+
+Does the definition of a limited type need to be updated to include an anonymous
+access to subprogram type? If would be odd if such a type were a "limited access
+type" but not a limited type. In plain English, it would be obvious that a
+limited access type is (one kind of) a limited type. Does this need to be stated
+explicitly?
+
+----
+
+I agree with your concern about whether the word "anonymous" should somehow be
+included. It is already somewhat confusing that a discriminant of a named access
+type is not an access discriminant. I'd like to avoid adding another such
+construct.
+
+----
+
+We are really interested in the "no assignment" aspect of limitedness here, as
+opposed to the "no equality comparison" aspect. Should some mechanism be added
+to at least allow testing to see whether a given value is null?
+
+----
+
+Would there be problems with ".all" renames of limited access function results?
+
+      X : T renames F (<some aggregate>).all;
+
+It seems like the finalization of the aggregate would have to be deferred to
+match the lifetime of the rename. This could get messy, as in
+
+      Y : T renames
+            F (G (<some aggregate>).all.Aliased_Component'Access).all;
+
+The same applies to generic in-out mode parameters (of course).
+
+Would the possibility of an outstanding access value cause the finalization of
+an aggregate object or a function result object to be deferred?
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Wednesday, April  8, 2009  6:40 PM
+
+> To solve the problem that the AI is trying to address, we do not need
+> limited access types other than as parameter and function result
+> types.
+
+I don't buy this at all. Returning a bare limited access value is still too
+unsafe to use with Ada.Containers. The entire point of allowing them in other
+places is so that we could return records with component(s) of limited access
+types. I don't have any other need for them, but I don't see how to support that
+case without supporting other ways to define objects.
+
+> I think there may be complexity
+> without corresponding benefit in allowing them in other cases.
+
+That's true. I seriously considered *only* allowing them as record components
+(and the two other cases), but it just seemed way to arbitrary. And there
+doesn't seem to be any added (definitional) cost to the other cases; record
+components is the messy one.
+
+> For example, a limited discriminant type sounds like a mess
+> (membership testing implicitly involves equality testing).
+
+Not sure why. Semantically, I think they are identical to the non-limited case
+except for the accessibility when reading them.
+
+> Wouldn't a limited access non-discriminant component type require much
+> the same accessibility checks as an access discriminant?
+
+Yes. Indeed, I was thinking that the most appropriate set of rules was to use
+*exactly* the same checks, so that the added implementation cost is small. (And
+there are far fewer cases for a normal component.)
+
+> What prevents a dangling reference in this case?
+>
+>      type R is
+>         limited record
+>            F : limited access Some_Tagged_Type'Class;
+>          end R;
+>
+>       type Ref is access R;
+>       Ptr : Ref;
+>
+>       function Func (X : Some_Tagged_Type'Class) return
+>         limited access Some_Tagged_Type'Class is
+>       begin
+>           return X'Access;
+>       end Func;
+>
+>       procedure P
+>           Y: Some_Extension;
+>       begin
+>           Ptr := new R'(F => Func (X => Y));
+>       end P;
+
+The accessibility of the component F is that of the object Ptr, while Func
+returns a limited access which has infinite accessibility when reading. Thus,
+this clearly fails an accessibility check; the allocator is illegal.
+
+As I noted in the AI, it is weird but intended that the result of a function
+returning a limited access function cannot be converted to a different limited
+access type (via return or initialization) other than a limited access parameter
+(and that's because the parameter allows everything).
+
+> Similar problems seem to exist if the result type of the function is R
+> and the function returns an aggregate.
+
+Same reason, this would fail an accessibility check. The intent is that a
+limited access type cannot be converted to *any* other access type unless it
+allows *any* accessibility (only the various forms of access parameters
+qualify).
+
+> What would be the benefit of allowing
+>       X : limited access T := ... ;
+> ?
+> Why would anyone want to do this?
+
+No reason, just that it seems weird to allow them in most contexts but not one
+or two specific ones. If we decide to allow *only* access parameters, access
+results, and record components, then it would not be so weird.
+
+> ----
+>
+> Do we really need limited access types as parameters?
+> What would be the consequences of taking a program which makes use of
+> such parameters and deleting the word "limited"
+> from all of the parameter declarations?
+
+It eliminates the runtime checks that are necessary for regular anonymous access
+parameters. Since accessibility checks always fail for such types, no overhead
+for such checks is needed. They make sense if all you want to do with the access
+is dereference it, or pass it to another access parameter.
+
+It should be noted that I'm trying to get the benefits of my "specified
+accessibility" proposal here; it's not that critical to have them.
+
+> You mention in the !proposal section that a function with a limited
+> access result type wants to be able to return a limited access
+> parameter, but this could be accomplished by allowing it to return any
+> access parameter, whether limited or not. Thus, this does not provide
+> a justification for limited access parameters.
+
+Correct. See above.
+
+> It seems that this new construct should be allowed only for function
+> results.
+
+No point, because it would be just as unsafe as the original. I would never
+agree to allow them in the Ada.Containers packages, and thus there would be no
+reason for defining it. (Note that I consider this safety issue a dead-body,
+bang-the-shoe-on-the-table critical. The argument that deleting a component is
+unlikely doesn't hold any water [note that I wrote something different in
+AI-142-1, but I've since seen new examples that show that it is way too
+dangerous to allow], especially in that in cases like vectors, it is the
+deletion of *any* component [not just the component that you are holding onto
+with the access] that could cause problems.
+
+You have to have a mechanism for a call-back in order to make them safe -- the
+idea is that there is an associated controlled record that causes the call-back
+when it is finalized. The limited access ensures that the access value cannot
+exist longer than the controlled object, so using it is safe. (And, as the
+examples I put in the AI shows, they can be quite simple to write.)
+
+As a practical matter, we *only* need limited access components in types that
+are returned from functions, but that seems just too silly of a restriction to
+invent.
+
+If you have a better idea of how to get the needed call-back (to end the
+"anti-tampering" checks when the access value is destroyed), or a better way to
+make containers usage safe, please suggest it.
+
+If you want to see the details of what I'm talking about, please look at the
+!examples section of the proposal.
+
+> ----
+>
+> Does the definition of "immutably limited" need updating?
+> Even if it doesn't strictly need it, would it still be a good idea?
+
+Probably, but mainly because these "limited" access types are not limited in the
+traditional sense. They still can be assigned (such as in function results);
+they're definitely not built-in-place types. As you've noted, you could use them
+in aggregates; it is the accessibility check that makes them illegal, not the
+assignment.
+
+Indeed, I did think about using a word other than "limited" ("restricted" comes
+to mind) because it seems too overloaded here, but at this stage using a new
+keyword is not appealing so I didn't do it.
+
+> Certainly "mode conformant" would need updating.
+> I understand that the wording section included "** Rest TBD **".
+> I'm just mentioning this because it crossed my mind.
+
+Why? Surely *static matching* and *static compatibility* would need updating,
+but that seems like about it to me (everything else would follow from that).
+
+> ----
+>
+> Does the definition of a limited type need to be updated to include an
+> anonymous access to subprogram type?
+> If would be odd if such a type were a "limited access type"
+> but not a limited type. In plain English, it would be obvious that a
+> limited access type is (one kind of) a limited type.
+> Does this need to be stated explicitly?
+
+I don't think these are limited types. We specifically wanted to avoid defining
+limited elementary types, and I surely did not do so. They're passed by copy and
+not built-in-place. I don't see how we can define pass-by-copy on a limited
+type, and don't really want to think about it.
+
+These are really "restricted" access types, not limited in any traditional
+sense. We might want to disallow using them as components in non-limited
+composite types, but only because the assignment operation between the
+components would be defined to fail accessibility. (I seem to have forgotten
+this detail in my write-up.)
+
+> ----
+>
+> I agree with your concern about whether the word "anonymous"
+> should somehow be included. It is already somewhat confusing that a
+> discriminant of a named access type is not an access discriminant. I'd
+> like to avoid adding another such construct.
+
+Yes, I truly dislike calling parameters of an anonymous access type "access
+parameters", because a parameter of a named access type is an access parameter
+in causal talk. It's annoying when official language terminology is contrary to
+causal discussion.
+
+> ----
+>
+> We are really interested in the "no assignment" aspect of limitedness
+> here, as opposed to the "no equality comparison" aspect.
+> Should some mechanism be added to at least allow testing to see
+> whether a given value is null?
+
+Actually, we're not interested in either. We're interested it preventing
+conversions to other types via accessibility checks. As I said, a limited access
+is not (formally) a limited type in my view.
+
+In any case, these are anonymous access types, and as such, they match the "="
+for anonymous access types in Standard. So clearly there would be an "="
+operator (there isn't an inherited one in any case for anonymous access types).
+
+> ----
+>
+> Would there be problems with ".all" renames of limited access function
+> results?
+>
+>       X : T renames F (<some aggregate>).all;
+>
+> It seems like the finalization of the aggregate would have to be
+> deferred to match the lifetime of the rename.
+
+Yes, that is already the rule that I was proposing. The language already
+requires the function result to have the lifetime of the renames, and the
+proposal expands that to include all of the parameters.
+
+> This could get messy, as in
+>
+>       Y : T renames
+>             F (G (<some aggregate>).all.Aliased_Component'Access).all;
+>
+> The same applies to generic in-out mode parameters (of course).
+
+The parameter itself would *always* have same master as the result object. Parts
+of the component expression might have shorter masters (if G returned something
+other than a limited access, for instance).
+
+> Would the possibility of an outstanding access value cause the
+> finalization of an aggregate object or a function result object to be
+> deferred?
+
+As I noted, when you rename a function result, that already changes the master
+to effectively defer the finalization. I would extend that to the parameters of
+a function as well if the function result has a (visible) part that is of a
+limited access type. I think that the rule would be straightforward to define;
+whether the implementation would be straightforward is an open question, of
+course. Note when I say "parameters", I mean the actual parameter expressions of
+whatever types. I don't think there is any value to trying to decide between
+things that might be returned vs. ones that will never be returned.
+
+****************************************************************
+
+From: Steve Baird
+Sent: Wednesday, April  8, 2009  7:40 PM
+
+> Probably, but mainly because these "limited" access types are not limited in
+> the traditional sense. They still can be assigned (such as in function
+> results);
+
+It looks like I was confused.
+
+I thought you were very deliberately not modifying 7.5(3/2-4/2):
+
+  A type is limited if it is a descendant of one of the following:
+    - a type with the reserved word limited ... in its definition;
+
+and that when you said "limited" you meant "limited".
+
+I'll reread everything, substituting "funky" for "limited".
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Wednesday, April  8, 2009  8:24 PM
+
+> I thought you were very deliberately not modifying 7.5(3/2-4/2):
+>
+>   A type is limited if it is a descendant of one of the following:
+>     - a type with the reserved word limited ... in its definition;
+>
+> and that when you said "limited" you meant "limited".
+
+I thought that I had to explicitly say that something was a limited type in
+order for it to be so; I never actually verified that (obviously). Sorry about
+the confusion.
+
+I modeled these on anonymous access-to-subprogram parameters, which clearly are
+not formally limited, even though they act that way in practice. That's what I
+understood that we were talking about.
+
+My intent was that it was fine to assign *into* a limited access object (subject
+various accessibility checks), but assigning out of them is banned. That's not
+quite the classical definition of limited.
+
+> I'll reread everything, substituting "funky" for "limited".
+
+Keep in mind a couple of things that I noted from your previous e-mail:
+
+* There does need to be a ban on including "limited access" as components of
+  non-limited composite types. That's because an assignment of such an object
+  would necessarily include an assignment of a component that would necessarily
+  fail an accessibility check. So we have to ban such assignments, and I think
+  the easiest way is just to require limited types only. (Initialization is OK,
+  I think.) [The intended use of limited access components is only in limited
+  types anyway.]
+
+* Your concerns about the complexity of limited access components and the like
+  surely is separate from whether these are "limited" or "restricted" or
+  "funky". Do read my answers there to clarify what I had in mind.
+
+* You asked about "X : limited access T := ... ;" earlier. Since you can assign
+  this object multiple times, there might in fact be a use for it. With a
+  renames, you could only hold one object, and can't change it due to other
+  computation. I would suggest using "limited" anytime that you hold onto (in a
+  block, for example) a pointer for dereferencing purposes only (another example
+  of the principle of least privilege). Still, this is a minor use at best.
+
+
+I did create this proposal in record time (I was worried about having it done
+before the next call), so I probably missed some points -- it is really easy to
+get wrapped up in a particular vision without getting it all down on paper.
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Wednesday, April  8, 2009  8:34 PM
+
+All limited types can be "assigned" in Ada 2005, but they can't be used in an
+assignment *statement*. I presume that would still be true here, namely they
+could only be assigned as part of initialization, and then never "re"-assigned
+to some other value, making them very much like "in" parameters and
+discriminants.  But I haven't seen Randy's proposal, so I am only guessing.  Did
+you send it to everyone, Randy?  Maybe it hit the "spam" filter...
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Wednesday, April  8, 2009 10:15 PM
+
+I surely did send to everyone - perhaps you didn't notice it as it was in the
+thread about "action items". I'll send you another copy. I really (as I
+explained to Steve) was planning to allow assignment (and conversion) *into*
+such objects, but not in the other direction. Truly limited access types had
+made people uncomfortable in the past, and I didn't think we wanted to revisit
+that -- the discussion on the conference call the other day was solely about
+treating them like anonymous access-to-subprogram parameters and using
+accessibility to prevent trouble rather than actual limitedness. I don't think I
+made that clear enough in the write-up (at least Steve didn't understand that).
+
+It should be noted that I suspect that making them "really" limited would
+probably actually work to solve the critical problem; it's just that the idea
+has been proposed many times in the past and the idea of immutably limited
+elementary types scared everyone into not considering it. So I didn't even try
+to think about it, especially given the discussion on the conference call.
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Wednesday, April  8, 2009  11:10 PM
+
+I don't remember that "truly" limited access types made folks uncomfortable, and
+in any case, there has been a *whole* lot of water over the dam since we last
+considered that.  I am personally made much *more* uncomfortable by allowing
+assignment statements that assign to these guys.  I think that is really asking
+for trouble.  I think allowing them to be assigned exactly once, when created,
+is adequate, and anything more is going to be too complicated.  If we disallow
+assignment statements, then limited-access components can use the rules for
+access discriminants in most cases.
+
+Do you have some examples where assignment statements are actually needed?  I
+really worry about assigning to a limited-access component of an OUT parameter.
+
+I think we need more examples, in general, to really know the best way for these
+guys to work.  I'll admit I am wondering whether we could just use access
+discriminants whenever a component would be needed, and then we only need to
+allow "limited" on parameters and function results. A record type with a single
+access discriminant and no other components would seem to be something that
+could be stuck into the middle of anything and accomplish pretty much the same
+thing as a limited-access component.
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Wednesday, April  8, 2009  11:40 PM
+
+...
+> Do you have some examples where assignment statements are actually
+> needed?  I really worry about assigning to a limited-access component
+> of an OUT parameter.
+
+No, I don't have any such examples. But then I don't understand the worries
+about accessibility during the conference call. Why would we care if you can't
+assign them???
+
+> I think we need more examples, in general, to really know the best way
+> for these guys to work.  I'll admit I am wondering whether we could
+> just use access discriminants whenever a component would be needed,
+> and then we only need to allow "limited" on parameters and function
+> results.
+> A record type with a single access discriminant and no other
+> components would seem to be something that could be stuck into the
+> middle of anything and accomplish pretty much the same thing as a
+> limited-access component.
+
+If we can do that, then we don't need them at all (or any other solution for
+that matter), because the only workable solution for the existing containers
+involves returning a limited access component in a limited controlled object.
+(At least so far as I can work out.) I have no reason to actually use limited
+access parameters (other than the slightly cheaper implementation, but probably
+not worth it alone) or limited access returns (they're not safe enough for use
+with the predefined containers or any of my other designs). I could imagine some
+confused users wanting to use limited access returns directly, but it surely
+isn't worth the definitional overhead if we can't use them in the one case that
+matters (the predefined containers).
+
+But then I'm confused: what prevents assigning an access discriminant value into
+some other object (of some other access type) that lives longer than the object
+with the discriminant?? I suppose accessibility might do it in some cases (I
+really have little idea on what the accessibility rules for access discriminants
+are in detail), but I can't imagine that would *always* work. For instance, we
+would want to prevent taking 'Access of any part of the designated object.
+
+And even if it does, we no longer have a simple reason to change the master of
+the parameters to live as long as the result. I don't think we'd want to make
+that change on all function calls -- it would make some programs use quite a bit
+more memory. (A significant change would be rare, but that almost makes it
+worse.) That change is also required to make useful accessors for
+Ada.Containers, presuming we want the accessors to work for the new bounded
+containers as well as the existing unbounded containers.
+
+So I still think we need some syntax to specify this case, even if the
+*semantics* of an access discriminant works. (Indeed, I think it does for
+inbound usages, which is what matters.)
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Thursday, April  9, 2009 11:10 AM
+
+Can you explain why you can't return a "bare" limited access value, but instead
+need to wrap it in a record, to accomplish your goal of giving r/w access to a
+component of a container?
+
+By the way, we have plenty of examples in our home-grown container packages
+where we return an access value, and then have a stern comment saying "never
+store this access value."  That is where I think a limited access result type
+would be ideal. I don't understand why these wouldn't work for the
+language-defined containers.
+
+****************************************************************
+
+From: Steve Baird
+Sent: Thursday, April  9, 2009  11:44 AM
+
+I think Randy wants to return a record so that he can update the tampering state
+of the container when the record is finalized.
+
+Randy - is this right?
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Thursday, April  9, 2009  1:11 PM
+
+That's correct. I'm writing a longer explanation in a reply to another message.
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Thursday, April  9, 2009  11:59 AM
+
+Yes, I realized that after I wrote my answer.  I suppose the goal is to have a
+controlled record wrapping a *visible* [limited] access value that can't be
+changed or copied.  It certainly feels round-a-bout.
+
+I *think* an access discriminant of a [limited] controlled object would already
+do the trick.  The access discriminant can't be copied to a place that would
+outlive the enclosing controlled object, so when the enclosing controlled object
+gets finalized, it can safely know there are no remaining references, and
+reenable tampering (or more precisely, decrement the "no tampering" count).
+
+****************************************************************
+
+From: Steve Baird
+Sent: Thursday, April  9, 2009 12:17 PM
+
+> I *think* an access discriminant of a [limited] controlled object
+> would already do the trick.  The access discriminant can't be copied
+> to a place that would outlive the enclosing controlled object, so when
+> the enclosing controlled object gets finalized, it can safely know
+> there are no remaining references, and reenable tampering (or more
+> precisely, decrement the "no tampering" count).
+
+I was also thinking about that approach.
+
+I think (Randy - correct me if I am misstating your position) the problem is
+that you'd have to use Unchecked_Access to implement something like
+
+     type Element_Handle (D : access Element) is tagged limited private;
+     -- controlled
+
+     function Handle (C : Container; I : Index) return Element_Handle is
+     begin
+         return Element_Handle (D => C.Elements(I)'Access, ... );
+     end Handle;
+
+Randy is trying to come up with a mechanism which will somehow allow a function
+result to safely contain references to the actual parameters of the function.
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Thursday, April  9, 2009 12:55 PM
+
+My experience with containers is that they pretty much always involve a level of
+indirection, so the actual elements are always stored in the heap, and your
+'Access would succeed.  But if not, then I agree we need to add Randy's extra
+"level" where we can take 'Access of a part of a parameter, and use it to
+initialize an access discriminant of a build-in-place object.
+
+Note that in your example, if there is no level of indirection in getting to
+"C.Elements", either the parameter mode of the Container would need to be IN OUT
+(;-), or be an access parameter, or the access discriminant would need to be
+"access constant Element."
+
+I realize I may be revisiting a heavily trodden path here, but I just want to be
+sure that whatever we propose (or already have) really solves the problem.
+
+Thanks for your indulgence,
+
+P.S. By the way, I was contemplating whether the rules against multiple access
+we are contemplating for function IN-OUT parameters, might have a role to play
+here in preventing passing the container as an in-out parameter (allowing it to
+be tampered), in the same "context" where we have another "live" reference to
+some part of the container. (This is a *very* long shot, I suspect.) -TT
+
+****************************************************************
+
+From: Steve Baird
+Sent: Thursday, April  9, 2009  1:17 PM
+
+> My experience with containers is that they pretty much always involve
+> a level of indirection, so the actual elements are always stored in
+> the heap, and your 'Access would succeed.
+
+Agreed, except possibly for in the cases of the bounded containers.
+
+> But if not,
+> then I agree we need to add Randy's extra "level"
+> where we can take 'Access of a part of a parameter, and use it to
+> initialize an access discriminant of a build-in-place object.
+
+If the existing language works in the cases we care
+about (and bounded containers need to be discussed
+here), then perhaps we don't need to do anything.
+
+Randy - what say you?
+
+> Note that in your example, if there is no level of
+> indirection in getting to "C.Elements", either the
+> parameter mode of the Container would need to be
+> IN OUT (;-), or be an access parameter, or the
+> access discriminant would need to be "access constant Element."
+>
+
+True.
+
+> I realize I may be revisiting a heavily trodden path
+> here, but I just want to be sure that whatever we
+> propose (or already have) really solves the problem.
+>
+
+Agreed on all points.
+
+> Thanks for your indulgence,
+>
+> P.S. By the way, I was contemplating whether the rules against
+> multiple access we are contemplating for function IN-OUT
+> parameters, might have a role to play here in preventing
+> passing the container as an in-out parameter (allowing
+> it to be tampered), in the same "context" where we have
+> another "live" reference to some part of the container.
+> (This is a *very* long shot, I suspect.) -TT
+
+This issue will, I'm sure, be given the priority that it deserves.
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Thursday, April  9, 2009  1:50 PM
+
+> My experience with containers is that they pretty much always involve
+> a level of indirection, so the actual elements are always stored in
+> the heap, and your 'Access would succeed.
+
+That's definitely *not* the intended implementation model for the bounded
+containers. And surely anything that we define has to work for the bounded
+containers.
+
+> But if not, then I agree we need to add Randy's extra "level"
+> where we can take 'Access of a part of a parameter, and use it to
+> initialize an access discriminant of a build-in-place object.
+
+OK, good. But note that also implies changing the master of the parameters
+(which matters if the parameter is a function result, for instance). And I don't
+think that is something we want to do generally.
+
+> Note that in your example, if there is no level of indirection in
+> getting to "C.Elements", either the parameter mode of the Container
+> would need to be IN OUT (;-), or be an access parameter, or the access
+> discriminant would need to be "access constant Element."
+
+Correct, the container parameters would have to be "in out" or "access". That's
+already noted in the proposal.
+
+> I realize I may be revisiting a heavily trodden path here, but I just
+> want to be sure that whatever we propose (or already have) really
+> solves the problem.
+
+Correct.
+
+> Thanks for your indulgence,
+
+:-)
+
+> P.S. By the way, I was contemplating whether the rules against
+> multiple access we are contemplating for function IN-OUT parameters,
+> might have a role to play here in preventing passing the container as
+> an in-out parameter (allowing it to be tampered), in the same
+> "context" where we have another "live" reference to some part of the
+> container.
+> (This is a *very* long shot, I suspect.) -TT
+
+The answer is no. It wouldn't help with globally available containers.
+
+In another message, Tucker wrote:
+
+> By the way, we have plenty of examples in our home-grown container
+> packages where we return an access value, and then have a stern
+> comment saying "never store this access value."
+
+That's insufficient to avoid problems (although it clearly reduces them).
+
+Let me show you the worst possible case (at least that I've thought of to date)
+that shows the need for a tampering check that is tightly associated with the
+access value:
+
+Assume that Ada.Containers.Vectors has an accessor function defined as follows:
+
+function Accessor (C : in Vector; I : Index_Type) return limited access Element_Type;
+
+[Drop the "limited" if you like; it helps but doesn't solve everything and has
+nothing to do with this example.]
+
+Now consider the following program fragments:
+
+  package Data is
+    type Node (D : Boolean := True) is record
+          case D is
+              when True =>
+                  CT : Natural;
+              when False =>
+                  CF : Float;
+          end case;
+    end record;
+    subtype True_Node  is Node (D => True);
+    subtype False_Node is Node (D => False);
+
+    package Node_Vector is new Ada.Containers.Vectors (Element_Type => Node,
+           Index_Type => Positive);
+
+    Node_List : Node_Vector.Vector;
+  end Data;
+
+  with Data;
+  package Far_Away is
+    procedure Some_Complex_Operation (...);
+  end Far_Away;
+
+  package body Far_Away is
+    procedure Some_Complex_Operation (...) is
+        Some_Index : Positive;
+    begin
+        ...
+        Some_Index := <lengthy-computation-resulting-in-value-of-1>;
+        ...
+        Data.Node_List.Delete (Some_Index);
+        ...
+    end Some_Complex_Operation;
+  end Far_Away;
+
+  with Data;
+  package Process is
+    procedure Process_True (NT : in out Data.True_Node);
+  end Process;
+
+  with Far_Away;
+  package body Process is
+    procedure Process_True (NT : in out Data.True_Node) is
+       Component : renames NT.CT; -- OK, NT is constrained.
+    begin
+       ...
+       Far_Away.Some_Complex_Operation (...);
+       if Component > 10 then -- ** Oh-oh!! Component has moved.
+          ...
+       end if;
+    end Process_True;
+  end Process;
+
+  with Data, Process;
+  procedure Main is
+  begin
+      Data.Node_List.Append (New_Item => (D => False, CF => 1.0)); -- Element 1.
+      Data.Node_List.Append (New_Item => (D => True, CT => 4)); -- Element 2.
+      Data.Node_List.Append (New_Item => (D => False, CF => 26.5)); -- Element 3.
+      ...
+      Process.Process_True (Data.Node_List.Accessor (2).all);
+      ...
+  end Main;
+
+Now, in the canonical implementation for a vector, all of the data is stored in
+an array of some size, and when a component is deleted, the rest of them slide
+down. So, when the Far_Away operation (probably called through many levels of
+calls) deletes the first element of the vector, all of the rest of them move.
+That means that the second element passed to Process_True unexpectedly changes
+its value to that which was in the third element. But that element doesn't match
+the constraint and doesn't even have a CT component! So we get erroneous
+behavior.
+
+Keep in mind that no one has copied this access value at all. So a rule (or
+"stern warning") preventing copying does no good. You can also get a similar
+effect by renaming the access value itself and doing bad things while the
+renames exists.
+
+Note that the erroneousness can't happen if you use Update_Element instead,
+because the tampering check will cause the deletion to raise Program_Error.
+Similarly, cursors have rules allowing implementations to detect this sliding
+and raise Program_Error. But this raw access can have no such check. Thus, I
+conclude that a raw access is simply not enough here.
+
+Of course, there are many lesser problems caused by similar scenarios; simply
+having the element value change unexpectedly is unlikely to produce good
+results.
+
+This is surely a possible scenario. The record type is very similar to the one
+used in the Janus/Ada symboltable, for instance (other than it is a lot
+smaller!). I could easily imagine using a bounded multiway tree to hold such a
+record, and having the other conditions hold as well.
+
+Indeed, the bare access code that is actually used in Janus/Ada has run into
+this exact scenario on a couple of occasions. It is incredibly hard to debug,
+given the wide distances between the code that causes the trouble and the code
+that fails. Indeed, we never figured out some of those bugs; we "solved" the
+problem by eliminating the deletions of nodes completely once we moved to the
+"massive" 256K memory of the original IBM PC, just marking them as inactive
+instead. (We only had 48K for everything on the original CP/M compilers, so
+keeping "dead" nodes was not an option.)
+
+As I've said before, exactly how we solve this problem is not a big deal to me,
+but I do think we need to solve it and we can't sacrifice safety to do it.
+
 ****************************************************************
 
+From: Randy Brukardt
+Sent: Thursday, April  9, 2009  5:00 PM
+
+In order to possibly make a second cut at this proposal, let me go over some
+important areas that seem to have avoided discussion so far.
+
+First, Tucker seems to be oblivious to the fact that "call-level" accessibility
+as I originally defined it doesn't work. The problem is that the parameters of a
+call in general have a different (shorter-living) master than the result of a
+call. That means that returning part of a parameter can be a problem if the
+parameter has been finalized.
+
+For instance, consider this version of the example from the AI of a vector of
+vectors:
+
+     Obj : limited access Integer renames
+           MV.Modifiable_Element(0).Modifiable_Element(0);
+
+In this call, the master of the final return object (the limited access) is that
+of the renames. But the master of the prefix MV.Modifiable_Element(0) is much
+shorter; it will be finalized well before the renames is fully elaborated. That
+would cause any tampering lock to be released prematurely.
+
+The problem is worse if someone uses a read-only accessor (which could make
+sense for large objects that you don't want to copy). If we have:
+
+     function Constructor (...) return Big_Object;
+
+     function Selector (O : in Big_Object) return limited access constant Small_Part;
+
+where Selector returns part of Big_Object, then if we have:
+
+     Small : Small_Part renames Constructor.Selector.all;
+
+Constructor will be finalized before Small could even be referenced, meaning the
+object would simply not exist. That is surely bad. While I think examples like
+this latter one shouldn't be common in practice, the danger is too severe to
+ignore.
+
+This problem is easily solved by changing the master of the parameters for such
+function calls to be the same as the return object. The problem is that if we
+don't have a syntactic marker as to when to make such a change, we would have to
+do it for some much less obvious scenario, such as for all functions, or maybe
+for all functions returning an object with an access discriminant or an access
+result. And then we also have to use call-level accessibility only for functions
+that meet whatever the requirement is. That's clearly easier by allowing
+call-level accessibility rules to only apply to limited access types (or
+whatever we use in their place).
+
+---
+
+Upon further reflection, we surely do need special,
+anonymous-access-to-subprogram-parameter-like accessibility for limited access
+types if we in fact have them. Otherwise, I don't see how we could prevent
+conversions to other types at the point of the call, or taking of 'Access for
+aliased components. Limitedness would only prevent assignment, not conversions.
+
+---
+
+While I don't see any major problem with the semantics I originally defined, I
+don't see any major need either. So I would be happy to make limited access
+types limited. That would simplify the proposal somewhat.
+
+---
+
+There doesn't appear to be any semantic difference between a limited access
+discriminant and a "regular" one, other than the different accessibility upon
+reading. I'd expect to define it that way (if it is defined at all).
+
+---
+
+In view of these issues, I see three reasonable alternatives to write up.
+Which should I follow:
+
+(1) Allow limited access types anywhere that anonymous access types are
+    (presuming that a limited type is OK in that context). This has the
+    advantage of consistency. But many of the possible places aren't that
+    useful.
+
+(2) Allow limited access types only in access parameters, results, and in
+    components.
+
+(3) Allow limited access types only in access parameters, results, and in
+    discriminants. This seems nice for some reason, probably because it pretty
+    much matches what Ada 95 allowed for anonymous access types.
+
+Well, of course there is a fourth idea, don't do anything at all. I hope we're
+not to that point yet.
+
+Thoughts??
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Thursday, April  9, 2009  10:09 PM
+
+...
+> First, Tucker seems to be oblivious to the fact that "call-level"
+> accessibility as I originally defined it doesn't work. The problem is
+> that the parameters of a call in general have a different
+> (shorter-living) master than the result of a call. That means that
+> returning part of a parameter can be a problem if the parameter has been finalized.
+
+Clearly we would have to fix this for cases where the result might designate
+part of a parameter.
+
+...
+> This problem is easily solved by changing the master of the parameters
+> for such function calls to be the same as the return object. The
+> problem is that if we don't have a syntactic marker as to when to make
+> such a change, we would have to do it for some much less obvious
+> scenario, such as for all functions, or maybe for all functions
+> returning an object with an access discriminant or an access result.
+> And then we also have to use call-level accessibility only for functions that meet whatever the requirement is.
+> That's clearly easier by allowing call-level accessibility rules to
+> only apply to limited access types (or whatever we use in their place).
+
+I see your point.  I don't see a major issue with having the finalization of
+parameters with an aliased part or coextension deferred until the result object,
+if there is a chance that some part or coextension of the result might designate
+the aliased part/coextension.  My guess is that many compilers might do that
+anyway. ;-)
+
+...
+> In view of these issues, I see three reasonable alternatives to write up.
+> Which should I follow:
+>
+> (1) Allow limited access types anywhere that anonymous access types
+> are (presuming that a limited type is OK in that context). This has
+> the advantage of consistency. But many of the possible places aren't
+> that useful.
+>
+> (2) Allow limited access types only in access parameters, results, and
+> in components.
+>
+> (3) Allow limited access types only in access parameters, results, and
+> in discriminants. This seems nice for some reason, probably because it
+> pretty much matches what Ada 95 allowed for anonymous access types.
+>
+> Well, of course there is a fourth idea, don't do anything at all. I
+> hope we're not to that point yet.
+>
+> Thoughts??
+
+I think adding "call-level" accessibility is a smaller change, and is relatively
+intuitive, so I would rather do that than add a whole "limited access type"
+concept.
+
+[Editor's note: This thread continues in AI05-0142-3, which uses some of these
+ideas to come up with a smaller solution.]
+
+****************************************************************

Questions? Ask the ACAA Technical Agent