!standard 3.10.2(13.1/2) 09-10-12 AI05-0148-1/06 !standard 3.10.2(19/2) !standard 4.6(24.17/2) !standard 4.6(48) !class Amendment 09-04-12 !status Amendment 201Z 09-06-25 !status WG9 Approved 09-11-05 !status ARG Approved 7-0-0 09-06-14 !status work item 09-04-12 !status received 09-04-12 !priority Medium !difficulty Medium !subject Accessibility of anonymous access stand-alone objects !summary The accessibility level of a stand-alone object of an anonymous access type may vary during its lifetime. This increases the usefulness of such objects. !problem The accessibility level of a stand-alone object of an anonymous access type is defined, in Ada 2005, to be the same as the master in which it is declared. Unfortunately, this is relatively useless. It can safely point at anything, but it immediately loses track of the accessibility level of its target, so the value can't be copied back into any pointer that is longer-lived. That means you can't use one of these for the most basic purpose of swapping two values that have longer lives. An example of this comes up when trying to reverse the nodes of a linked list, presuming the "Next" component of each node is of an anonymous access type: type Root_Node is tagged record ... Next : access Root_Node'Class; end record; function Reverse_List(List : access Root_Node'Class) return access Root_Node'Class is -- Reverse the order of the nodes of the list Result : access Root_Node'Class := null; This_Node : access Root_Node'Class := List; begin -- Iterate through the nodes moving them to the beginning of -- the new list. while This_Node /= null loop -- Put this node on front of new list declare Next_Node : constant access Root_Node'Class := This_Node.Next; begin This_Node.Next := Result; -- Fails accessibility check Result := This_Node; -- advance to next node on list This_Node := Next_Node; end; end loop; return Result; -- Fails accessibility check end Reverse_List; As indicated in comments, assigning into a local variable is no problem, but assigning *from* it is very likely to fail an accessibility check in any useful algorithm. !proposal We propose to allow the accessibility level of a stand-alone object of an anonymous access type to vary during its lifetime, taking on the level of the value from which it is assigned, provided that level is no deeper than the level of the master in which the stand-alone object is declared. This restriction is enforced with the usual pair of a legality rule and a dynamic semantics rule, as follows: In an assignment to a stand-alone object of an anonymous access type, the accessibility level of the RHS shall not be statically deeper than that of the master in which the object is declared. A (run-time) check is performed on such an assignment that the (dynamic) accessibility level of the RHS is no deeper than that of the master in which the object is declared. If the check fails, Program_Error is raised. Note that a library-level object of an anonymous access type can only designate library-level objects. The null value is considered library-level for the purposes of these checks. !wording Add the following after 3.10.2(13.1/2): The accessibility level of the type of a stand-alone object of an anonymous access-to-object type is the same as the accessibility level of the type of the access value most recently assigned to the object, but is never deeper than that of the declaration of the stand-alone object. Add the following after 3.10.2(19/2): The statically deeper relationship does not apply to the accessibility level of the type of a stand-alone object of an anonymous access-to-object type; that is, such an accessibility level is not considered to be statically deeper, nor statically shallower, than any other. Add more text to the AARM Implementation Note (after 3.10.2(22.ff)): The implementation of accessibility checks for stand-alone objects of anonyomous access-to-object types can be similar to that for anonymous access-to-object parameters. A static level suffices; it can be calculated using rules similar to those previously described for access parameters. One important difference between the stand-alone access variables and access parameters is that one can assign a local access parameter to a more global stand-alone access variable. Similarly, one can assign a more global access parameter to a more local stand-alone access variable. For these cases, it is important to note that the "correct" static accessibility level for an access parameter assigned to a stand-alone access object is the minimum of the passed in level and the static accessibility level of the stand-alone object itself. This is true since the static accessibility level passed in might be deeper than that of the stand-alone object, but the dynamic accessibility of the passed in object clearly must be shallower than the stand-alone object (whatever is passed in must live at least as long as the subprogram call). We do not need to keep a more local static level as accesses to objects statically deeper than the stand-alone object cannot be stored into the stand-alone object. Change 4.6(24.17/2): * The accessibility level of the operand type shall not be statically deeper than that of the target type{, unless the target is a stand-alone object of an anonymous access type. If the target is such a stand-alone object, the accessibility level of the operand type shall not be statically deeper than that of the declaration of the stand-alone object}. In addition to the places where Legality Rules normally apply (see 12.3), this rule applies also in the private part of an instance of a generic unit. AARM Reason: We prohibit storing accesses to objects deeper than a stand-alone object of a anonymous access-to-object (even while we allow storing all other accesses) in order to prevent dangling accesses. Change 4.6(48): * For an access-to-object type, a check is made that the accessibility level of the operand type is not deeper than that of the target type{, unless the target is a stand-alone object of an anonymous access type}. {If the target is such a stand-alone object, a check is made that the accessibility level of the operand type is not deeper than that of the declaration of the stand-alone object; [Redundant: if the check succeeds, the accessibility level of the target type becomes that of the operand type].} !discussion The current rule in Ada 2005 makes stand-alone objects of an anonymous access type useful only to point at local objects, which is not of any significant value. We considered various ways of fixing this. One was to have them take on the accessibility level of their initial value, analogous to the way the access parameters and access object renames work. This certainly seems more useful, but it is not clear what to do when an object is initialized to null. Normally one would think of null as being library-level, but perhaps it is better thought of as having no particular level. An alternative idea was to require that all non-null values assigned to a given access object be of the same accessibility level. This works fine for static accessibility levels, but is not very practical for dynamic accessibility levels, such as those of access parameters, as it would essentially require a separate value against which each newly assigned value would be compared, and which would be initialized from each non-null assignment. The simplest idea seems to have stand-alone objects take on the accessibility level of their assigned value. This makes complete sense for constants, it is already what happens for object renames and access parameters, and it is only for variables that we have a somewhat new notion. It clearly is more flexible than the current rule or the single-non-null-level rule, and is probably less likely to cause surprises or problems. Note that the accessibility level of the type of a stand-alone object of an access-to-object type is itself dynamic, which is admittedly a bit odd. An alternative approach might say that the accessibility level of the view designated by a stand-alone object is dynamic while the level of the anonymous access type is static, but that would involve larger changes in the model. Note that we have not chosen to include access-to-subprogram types in this change. They remain as in Ada 2005, with stand-alone objects being of local accessibility. There seems little need for the dynamic flexibility we are proposing here for access-to-object types, because access-to-subprogram parameters are not dynamic. More local objects Despite this being a dynamic accessibility model, it is not allowed to store a more local access into a stand-alone object of an anonymous access-to-object type. This is necessary to prevent dangling references which would be very hard to detect. Consider: declare Obj : access Integer; procedure Do_It (Value : in Integer) is Local : aliased Integer := Value; begin if Value mod 3 = 0 then Obj := Local'Access; -- (1) else Obj.all := Value + Obj.all; -- (2) end if; end Do_It; begin Do_It (3); Do_It (4); end; The assignment at (1) is statically illegal. If it was not, we would store an access to a more local object into Obj. When the first call to Do_It returns, that object ceases to exist, but Obj still has an access to it. The second call to Do_It then uses Obj at (2). This references an object that no longer exists (the Local for Do_It (3), the Local for Do_It (4) is a different object, at least logically). Detecting this would require accessibility checks at dereferences and a complex scheme of nesting detection. (A simple stack check doesn't work, that can be seen in this case where the level of the dangling, destroyed object is identical to one that exists, but the object that exists is not the correct one.) The needed check would require far too much overhead (and it would be distributed overhead). Implementation Issues Because access-to-object parameters already have dynamic accessibility levels, the intent is that these stand-alone variables may be handled in a very similar way. Furthermore, if there are no access-to-object parameters in scope, then all of the accessibility levels of interest will be static, and hence essentially all of the checking can be performed at compile-time given some level of flow analysis. One important difference between the stand-alone access variables and access parameters is that one can assign a local access parameter to a more global stand-alone access variable. This is similar to the problem of calling a subprogram at a higher level and passing along an access parameter. Similarly, one can assign a more global access parameter to a more local stand-alone access variable. AARM 3.10.2(22.dd/2) addresses the issue of passing an access parameter along to another subprogram: * If the actual is an access parameter of an access-to-object type, usually just pass along the level passed in. However, if the static nesting level of the formal (access) parameter is greater than the static nesting level of the actual (access) parameter, the level to be passed is the minimum of the static nesting level of the access parameter and the actual level passed in. Note also the paragraph 22.ee indicates that the accessibility level passed in for an access parameter is to be ignored when converting to a local access type. This is because the accessibility level passed in might be deeper than that of the called subprogram. So the "true" static accessibility level for an access parameter is generally the minimum of the passed in value and the static accessibility level of the subprogram itself. This must be remembered when assigning the accessibility level of an access parameter to that of a stand-alone object. This "min" operation should probably be performed explicitly at that point. So what can we say about an assignment from an access parameter (or access variable) at one level to a stand-alone access variable at some other level? First, we should assume the accessibility level is normalized as suggested above, namely the value passed in with an access parameter is compared against the static level of the called subprogram, and reduced to that level if the passed-in value is greater. Then the normalized value can safely be assigned to an access variable at some other level, subject to first checking that it is not deeper than that of the access variable being assigned. The normalized value corresponds to the static nesting level, and hence is meaningfully compared and assigned to all visible nesting levels. For example: procedure P(X : access T) is Y : access T; procedure Q is Z : access T := X; -- normalize level here begin Y := Z; -- this is always safe but might -- fail if level of X not normalized -- before being assigned to Z. end Q; begin Q; end P; Interaction with Tasking Tasking imposes certain additional concerns with respect to accessibility. In particular, inside an accept body, there are two independent stacks that are effectively coming together, potentially producing accessibility levels that cannot be meaningfully compared. However, access parameters are not permitted on entry calls, and there are no OUT parameters of an anonymous access type, so in fact accessibility levels from the task entry caller never enter into the picture, and we can follow the "normal" rules. Up-level references across a task body boundary also do not create any special problems (other than the usual danger of unsynchronized access to shared variables), because the task body is just like a subprogram body from the point of view of static nesting and accessibility levels. !example type Node is tagged record Prev, Next : access Node'Class; end Node; procedure Swap(A, B : access Node'Class) is -- Swap nodes between circularly-linked lists -- Requires: neither list is part of an empty list pragma Assert(A.Next /= A); pragma Assert(B.Next /= B); After_A : constant access Node'Class := A.Next; Before_A : constant access Node'Class := A.Prev; begin -- Replace A with B Before_A.Next := B; After_A.Prev := B; A.Next := B.Next; B.Next := After_A; -- Would fail with old rules A.Prev := B.Prev; B.Prev := Before_A; -- Would fail with old rules -- Replace B with A A.Next.Prev := A; A.Prev.Next := A; end Swap; !corrigendum 3.10.2(13.1/2) @dinsa @xbullet @dinst @xbullet !corrigendum 3.10.2(19/2) @dinsa @xbullet @dinst @xbullet !corrigendum 4.6(24.17/2) @drepl @xinbull @dby @xinbull !corrigendum 4.6(48) @drepl @xinbull @dby @xinbull !ACATS test ACATS C-Tests are needed to check that the accessibility is retained. !ASIS No change needed. !appendix [This idea grew out of the voluminous e-mail threads found in AI05-138-1. - Editor.] **************************************************************** From: Tucker Taft Sent: Wednesday, March 18, 2009 9:08 PM Randy asked me to write up something more formal on my proposal for standalone objects of an anonymous access type. Franco gave a quite good argument in my view that the current Ada 2005 accessibility rules for these objects makes them essentially useless. So here is a proposal to make their accessibility level come from their assigned value, modulo the requirement that they can never designate an object shorter-lived than themselves. --- !problem The accessibility level of a stand-alone object of an anonymous access type is defined, in Ada 2005, to be the same as the master in which it is declared. Unfortunately, this is relatively useless. It can safely point at anything, but it immediately loses track of the accessibility level of its target, so the value can't be copied back into any pointer that is longer-lived. That means you can't use one of these for the most basic purpose of swapping two values that have longer lives. An example of this comes up when trying to reverse the nodes of a linked list, presuming the "Next" component of each node is of an anonymous access type: type Node is record ... Next : access Node; end record function Reverse(List : access Node) return access Node is -- Reverse the order of the nodes of the list Result : access Node := null; This_Node : access Node := List; begin -- Iterate through the nodes moving them to the beginning of -- the new list. while This_Node /= null loop -- Put this node on front of new list declare Next_Node : constant access Node := This_Node.Next; begin Next_Node := This_Node.Next; This_Node.Next := Result; -- Fails accessibility check Result := This_Node; -- advance to next node on list This_Node := Next_Node; end; end loop; return Result; -- Fails accessibility check end Reverse; As indicated in comments, assigning into a local variable is no problem, but assigning *from* it is very likely to fail an accessibility check in any useful algorithm. !proposal We propose to allow the accessibility level of a stand-alone object of an anonymous access type to vary during its lifetime, taking on the level of the value from which it is assigned, provided that level is no deeper than the level of the master in which the stand-alone object is declared. This restriction is enforced with the usual pair of a legality rule and a dynamic semantics rule, as follows: In an assignment to a stand-alone object of an anonymous access type, the accessibility level of the RHS shall not be statically deeper than that of the master in which the object is declared. A (run-time) check is performed on such an assignment that the (dynamic) accessibility level of the RHS is no deeper than that of the master in which the object is declared. If the check fails, Program_Error is raised. Note that a library-level object of an anonymous access type can only designate library-level objects. The null value is considered libary-level for the purposes of these checks. !discussion The current rule in Ada 2005 makes standalone objects of an anonymous access type useful only to point at local objects, which is not of any significant value. We considered various ways of fixing this. One was to have them take on the accessibility level of their initial value, analogous to the way the access parameters and access object renames work. This certainly seems more useful, but it is not clear what to do when an object is initialized to null. Normally one would think of null as being library-level, but perhaps it is better thought of being no particular level. An alternative idea was to require that all non-null values assigned to a given access object be of the same accessibility level. This works fine for static accessibility levels, but is not very practical for dynamic accessibility levels, such as those of access parameters, as it would essentially require a separate value against which each newly assigned value would be compared, and which would be initialized from each non-null assignment. The simplest idea seems to have stand-alone objects take on the accessibility level of their assigned value. This makes complete sense for constants, it is already what happens for object renames and access parameters, and it is only for variables that we have a somewhat new notion. It clearly is more flexible than the current rule or the single-non-null-level rule, and is probably less likely to cause surprises or problems. **************************************************************** From: Randy Brukardt Sent: Wednesday, March 18, 2009 10:51 PM Thanks for doing this. I see the rules for assigning *to* an anonymous access object. But I don't see any definition here of what the accessibility level an anonymous access object is (that is, assigning *from* such an object). I presume you will need to change some rules in 3.10.2 to make this partially dynamic accessibility happen. You surely don't want 3.10.2(7) to apply; I suppose you want something like 3.10.2(11.1/2) or 3.10.2(13/2) to apply. But I don't want to guess quite what is meant here - I've been wrong before. (BTW: Might fix "libary level" for null, while you are at it.) **************************************************************** From: Tucker Taft Sent: Sunday, April 12, 2009 8:46 PM Here is wording for stand-alone objects of an anonymous access type. [This is version /01 of the AI.] I ultimately decided to make the accessibility level of the anonymous access type dynamic. Other alternatives seemed to require bigger changes. **************************************************************** From: Bob Duff Sent: Monday, April 13, 2009 9:01 AM > The simplest idea seems to have stand-alone objects take on the > accessibility level of their assigned value. I still think it would be simpler to say that the level is that of the designated subtype, so it's static. Or do this: > An alternative idea was to require that all non-null values assigned > to a given access object be of the same accessibility level. Either way, I would like to have a way to statically define the accessibility level of access parameters. As far as I can see, dynamic accessibility is never what you want, and it breaks abstraction. Therefore, I don't buy this objection: > ...but is > not very practical for dynamic accessibility levels, such as those of > access parameters, ... And except for the access param issue, I can't see any use the added flexibility of doing it dynamically -- the checks are just a tripping hazard. Can you think of an example? I also think that access parameters will be very rare in Ada 201X, given that we will have 'in out' on function params. (In new code, I mean, which is what matters here.) **************************************************************** From: Tucker Taft Sent: Monday, April 13, 2009 9:21 AM > ... >> The simplest idea seems to have stand-alone objects take on the >> accessibility level of their assigned value. > > I still think it would be simpler to say that the level is that of the > designated subtype, so it's static. Saying something is simpler is only half of the argument. You have to justify it as being at least somewhat useful. You are effectively saying that there are no local aliased objects and no access types more local than their designated type. I really don't buy that at all. Personally, I am almost exclusively interested in objects whose lifetimes is shorter than their designated type, since most of the designated types are library-level, while most of the objects are local. > And except for the access param issue, I can't see any use the added > flexibility of doing it dynamically -- the checks are just a tripping hazard. > Can you think of an example? Access parameters are where dynamic accessibility comes from, except in generic bodies, where it is semi-dynamic. Presuming we don't outlaw access parameters, I think we have to deal with them. I don't buy the argument that all access parameters are going to disappear. Even if some of us don't like them, there are plenty of Ada users who will continue to have legacy Ada 95 code for many years, even if there are new portions of their code that take advantage of newer features. Starting over from scratch is a rare occurrence in big systems. I also don't understand the allergy to dynamic accessibility checks, since we seem quite content doing dynamic range checks, relying on the compiler to remove them statically wherever possible. Doing the same for accessibility checks seems quite straightforward, particularly given how many of the accessibility levels will be known at compile-time. > I also think that access parameters will be very rare in Ada 201X, > given that we will have 'in out' on function params. (In new code, I > mean, which is what matters here.) As indicated above, I don't buy the notion that access parameters will suddenly disappear. Large software systems evolve pretty slowly. Inventing new features that don't work with old ones will only be frustrating, not liberating. **************************************************************** From: Bob Duff Sent: Monday, April 13, 2009 10:17 AM > Saying something is simpler is only half of the argument. > You have to justify it as being at least somewhat useful. No, I disagree. I think when someone proposes to add complexity, the burden of proof is on them, to show that it's necessary, or at least useful. In your Reverse example, there is an access parameter. Approximately 100% of calls to Reverse will pass a library-level pointer to heap-allocated lists. Therefore, my idea of allowing the programmer to state that as part of the contract makes sense in this case. (I am imagining allowing that on a global basis as well, so you can do it conveniently in existing code.) Furthermore, there is exactly zero benefit to using anonymous access types in this example. Named types work fine. There is zero benefit to spelling it "access Node" instead of "Access_Node" (or "Node_Ref" or whatever). You don't even save the cost of declaring the named type (one line of code), because you have to have a named type to do the heap allocation and deallocation. Heap allocation is broken for anon access, and deallocation is disallowed. You need to provide a complete example showing that anon access types are BETTER than named access types, under your proposed rules. I have yet to see that, which is why I've found this conversation so frustrating over the past few months (years?). I imagine such an example would contain access-to-class-wide types, and would benefit by avoiding bogus explicit conversions. Or maybe the issue is "limited with"? Can you construct such an example? I've tried quite hard, and failed. I think we will need some language changes in addition to the one proposed here, in order to create such an example. Without such an example, I am opposed to adding this complexity. > You are effectively saying that there are no local aliased objects and > no access types more local than their designated type. > I really don't buy that at all. I'm not saying that. I'm saying the vast majority of access types are used to build heap-allocated data structures (using a global/default heap). Local aliased objects generally need 'Unchecked_Access, so a global named access type works fine. If they don't need 'Unchecked_Access, then a local named access type works fine. >...Personally, I am almost exclusively > interested in objects whose lifetimes is shorter than their >designated type, since most of the designated types are library-level, >while most of the objects are local. I'm just astonished that you can say that! Look at the code you've been writing for the past few years. You will see LOTS of heap-allocated data structures, plus a few local aliased objects. Admittedly, your data structures live in storage pools, but that's highly unusual -- the vast majority of AdaCore customers (for example) do not use storage pools, based on the number of customers asking about them, or reporting bugs. > > And except for the access param issue, I can't see any use the added > > flexibility of doing it dynamically -- the checks are just a tripping hazard. > > Can you think of an example? > > Access parameters are where dynamic accessibility comes from, except > in generic bodies, where it is semi-dynamic. > > Presuming we don't outlaw access parameters, I think we have to deal > with them. I don't buy the argument that all access parameters are > going to disappear. That's not what I said. I said they will be rare in new code. To argue against that, you have to say what they're good for. I also said we should try to fix them, so they can (optionally) have static accessibility level. >...Even > if some of us don't like them, there are plenty of Ada users who will >continue to have legacy Ada 95 code for many years, even if there are >new portions of their code that take advantage of newer features. >Starting over from scratch is a rare occurrence in big systems. Existing code will continue to use access parameters and named access types. If someone wants to start using anon access, they will have to rewrite some portions of code. Many folks won't do that (just like many folks continue to use rendezvous when protected entries might be better). > I also don't understand the allergy to dynamic accessibility checks, > since we seem quite content doing dynamic range checks, relying on the > compiler to remove them statically wherever possible. There's a difference: it is common to have dynamic integer values that are not statically known to be in range (or in the bounds of an array). But I have NEVER seen an example where I wanted dynamic accessibility in Ada 95. Every access parameter falls into one of two cases: - It is saved in a global data structure, so the caller must pass in a global pointer (or use 'Unchecked_Access, and take care). - It is not saved in a global data structure, so the caller can pass in 'Access of locals. (Note that this case is not useful in Ada 201X.) You always know statically which it is, but unfortunately you can't document it in the code, only in the comments. You can try to prove me wrong by providing an example. >...Doing the same for accessibility checks seems quite >straightforward, particularly given how many of the accessibility >levels will be known at compile-time. > > > I also think that access parameters will be very rare in Ada 201X, > > given that we will have 'in out' on function params. (In new code, > > I mean, which is what matters here.) > > As indicated above, I don't buy the notion that access parameters will > suddenly disappear. Large software systems evolve pretty slowly. > Inventing new features that don't work with old ones will only be > frustrating, not liberating. As a general principle, I agree. But sometimes, we have to admit that we've made a mistake, and fix it for the future. For example, we defined interrupt handlers as protected procedures, and banished interrupt entries to the Obsolescent annex. **************************************************************** From: Bob Duff Sent: Monday, April 13, 2009 10:32 AM > You need to provide a complete example showing that anon access types > are BETTER than named access types, under your proposed rules. I have > yet to see that, which is why I've found this conversation so > frustrating over the past few months (years?). A good example might be a double-linked list generic package. If it's better in some way(s) with anon access, then I'd be convinced that adding complexity is worthwhile. **************************************************************** From: Tucker Taft Sent: Monday, April 13, 2009 11:42 AM > No, I disagree. I think when someone proposes to add complexity, the > burden of proof is on them, to show that it's necessary, or at least > useful. I'll admit I was assuming that components of an anonymous access type were useful, and used that to justify the need for this example. AI-230 provides some justification for the need for such anon-access components, and ultimately we decided to accept AI-230, so I considered that adequate, and did not see the need to rejustify their value. My main claim is that given the presence of anon-access components, anon-access stand-alone objects are not useful as proposed by AI-385. Note that AI-230 is consistent with this view, in that it only proposes stand-alone object renaming, rather than declaration, and actually mentions the possibility we are now discussing as one solution for stand-alone object declaration, if they are desired. Unfortunately, it looks like we forgot to re-read AI-230 when AI-385 was formulated. > In your Reverse example, there is an access parameter. Approximately > 100% of calls to Reverse will pass a library-level pointer to heap-allocated lists. > Therefore, my idea of allowing the programmer to state that as part of > the contract makes sense in this case. (I am imagining allowing that > on a global basis as well, so you can do it conveniently in existing > code.) > > Furthermore, there is exactly zero benefit to using anonymous access > types in this example. Named types work fine. There is zero benefit > to spelling it "access Node" instead of "Access_Node" (or "Node_Ref" > or whatever). You don't even save the cost of declaring the named > type (one line of code), because you have to have a named type to do > the heap allocation and deallocation. Heap allocation is broken for anon > access, and deallocation is disallowed. Clearly to make the example more interesting, the "Next" component, and the parameter and function results, should be of type "access Node'Class." > You need to provide a complete example showing that anon access types > are BETTER than named access types, under your proposed rules. I have > yet to see that, which is why I've found this conversation so > frustrating over the past few months (years?). If you hated AI-230 to begin with, then I can imagine this discussion is frustrating. But if you accept that we decided to implement AI-230 in Ada 2005, then I believe we should try to make the features of Ada 2005 work well together, and I don't think we realized how poorly AI-385 and AI-230 worked together. > I imagine such an example would contain access-to-class-wide types, > and would benefit by avoiding bogus explicit conversions. > Or maybe the issue is "limited with"? > Can you construct such an example? I've tried quite hard, and failed. The main justification for AI-230 is to allow components to be of an anon-access type, so they can be assigned from objects of some other access type (anon or named) without the "noise" of explicit conversions. Once you have components of an anon-access type, then you need some way to copy between them, or exchange them, which will generally involve temps. The temps need to be flexible enough to hold the various kind of anon-access values that arise. The AI-385 proposal for stand-alone access objects with strictly "local" accessibility failed in that job pretty dramatically. > I think we will need some language changes in addition to the one > proposed here, in order to create such an example. Without such an > example, I am opposed to adding this complexity. I don't see the need for more changes. I see the anon-access standalone objects defined by AI-385 as being broken, and I believe they should be fixed. **************************************************************** From: Randy Brukardt Sent: Monday, April 13, 2009 5:58 PM ... > In your Reverse example, there is an access parameter. > Approximately 100% of calls to Reverse will pass a library-level > pointer to heap-allocated lists. > Therefore, my idea of allowing the programmer to state that as part of > the contract makes sense in this case. (I am imagining allowing that > on a global basis as well, so you can do it conveniently in existing > code.) Didn't we "decide" that we were *not* going to allow such specification directly, but rather allow/expect programmers to use the accessibility membership in preconditions to provide this sort of constraint? One would hope that the compiler could then propagate that information to other accessibility checks in order to eliminate them as well. (This would probably be special-case work, but in that sense no different than the lengths that everyone goes to to eliminate range checks.) ... > You need to provide a complete example showing that anon access types > are BETTER than named access types, under your proposed rules. I have > yet to see that, which is why I've found this conversation so > frustrating over the past few months (years?). Decades?? :-) > I imagine such an example would contain access-to-class-wide types, > and would benefit by avoiding bogus explicit conversions. > Or maybe the issue is "limited with"? > Can you construct such an example? I've tried quite hard, and failed. I think you can construct such examples, but it will depend on how we deal with limited with "bridging". Isn't is great that all of these issues are interrelated?? :-) ... > I'm not saying that. I'm saying the vast majority of access types are > used to build heap-allocated data structures (using a global/default > heap). Local aliased objects generally need 'Unchecked_Access, so a > global named access type works fine. > If they don't need 'Unchecked_Access, then a local named access type > works fine. I find it very annoying that you have to use 'Unchecked_Access all over the place, and hardly ever can actually use 'Access. But since I don't have an idea for fixing that, I'll go shut up now... :-) ... > > I also don't understand the allergy to dynamic accessibility checks, > > since we seem quite content doing dynamic range checks, relying on > > the compiler to remove them statically wherever possible. > > There's a difference: it is common to have dynamic integer values that > are not statically known to be in range (or in the bounds of an > array). But I have NEVER seen an example where I wanted dynamic > accessibility in Ada 95. > Every access parameter falls into one of two cases: > > - It is saved in a global data structure, so the caller must pass in a > global pointer (or use 'Unchecked_Access, and take care). The handful of cases where I tried to use anonymous access parameters fall into this case. And all I got was Program_Errors, because I forgot about this issue when I wrote the code. It really wasn't helpful; I ended up reverting to a named access type and more 'Unchecked_Access uses. > - It is not saved in a global data structure, so the caller can > pass in 'Access of locals. (Note that this case is not useful > in Ada 201X.) > > You always know statically which it is, but unfortunately you can't > document it in the code, only in the comments. > > You can try to prove me wrong by providing an example. Well, I'm sure there exists some example that is different. One obvious possibility is a generic managed pool, where you want the objects to be of the level of the instantiation. If you instantiate in the main subprogram (I do that sometimes), you don't want either of the above. But there, too, the accessibility level ought to be static (just not library-level). **************************************************************** From: Randy Brukardt Sent: Monday, April 13, 2009 6:22 PM ... > I'll admit I was assuming that components of an anonymous access type > were useful, and used that to justify the need for this example. > AI-230 provides some justification for the need for such anon-access > components, and ultimately we decided to accept AI-230, so I > considered that adequate, and did not see the need to rejustify their > value. The problem with AI-230 is that what it proposed really did not solve any of the problems in the !problem section. (From AI-230): --In Java and other OO languages, types that are references to a subclass are --freely convertible to types that are references to a superclass. This implicit --conversion, which is always safe, significantly reduces the need for explicit --conversions when passing references as parameters. We didn't do this, at least in class-wide cases (I'm presuming that Franco's concerns here are legit.) --With the current access type conversion rules in Ada 95, large numbers of --explicit conversions are required. This obscures the explicit conversions that --really do need attention, and makes the whole OO coding style seem more --cumbersome in Ada than in other OO languages. We reduced these somewhat, but many of those conversions remain. --In addition to addressing the problem of minimizing unnecessary --explicit-yet-safe conversions, there is the related problem of --minimizing (named) "access type proliferation". This generally occurs --when, for one reason or another, an access type is not defined at the --point of the type declaration (e.g., with the existing "purity" rules, a --pure package would never declare any access types). Ultimately, if they --need an access type, users of the type end up declaring their own --"personal" access type, creating yet more need for unnecessary --conversions. *Maybe* we addressed this, but I doubt it. And "purity" is completely useless; nothing real can use it. (Individual functions are a different deal.) And the reason that some of us (for instance, me) reluctantly voted for this AI doesn't appear here at all: the bridging issue for limited withs. It was bridging (which can't be done with named types in Ada 2005) that got those of us that saw no point and no value to these things to go along. And that use doesn't work very well, as many items need explicit conversions, even though we were told otherwise. So I'd be careful relying too much on AI-230. It seems to me to have been a camel's nose to get these awful things further in the tent, and it doesn't appear that the authors tried too hard to actually solve the problems that they set out to solve. **************************************************************** From: Randy Brukardt Sent: Monday, April 13, 2009 6:06 PM In Tucker's proposal, I found the following very confusing: The accessibility level of the operand type shall not be statically deeper than that of the target type{, unless the target is a stand-alone object. If the target is a stand-alone object, the accessibility level of the operand type shall not be statically deeper than that of the declaration of the stand-alone object}. In addition to the places where Legality Rules normally apply (see 12.3), this rule applies also in the private part of an instance of a generic unit. * For an access-to-object type, a check is made that the accessibility level of the operand type is not deeper than that of the target type{, unless the target is a stand-alone object}. {If the target is a stand-alone object, a check is made that the accessibility level of the operand type is not deeper than that of the declaration of the stand-alone object. [Redundant: The accessibility level of the target type becomes that of the operand type.]} This wording doesn't make it clear that it is only talking about *anonymous* access stand-alone objects. Both of these rules apply to any general access-to-object type (not just anonymous). I think the intent is that the only way that you can be converting directly into a stand-alone object is if the type is anonymous. But I'm not really sure that is true, a subtype conversion is needed for a constrained access subtype: type Disc (B : Boolean) is record... type Acc_Disc is access all Disc; subtype True_Disc is access Disc(True); procedure Foo (Obj : Acc_Disc) is Bar : True_Disc := Obj; begin ... Surely Bar is a stand-alone object, and surely we're converting directly to it. So how come this new rules don't apply? (I surely hope they don't!!) Even if some hair-splitting would get the right answer, I think we need to be more explicit. **************************************************************** From: Tucker Taft Sent: Monday, April 13, 2009 6:32 PM My bad. I meant to say "a stand-alone object of an anonymous access type" in both cases. **************************************************************** From: Randy Brukardt Sent: Monday, April 13, 2009 6:50 PM OK, I fixed the AI. That would have been a bit redundant, so I changed the wording to: * The accessibility level of the operand type shall not be statically deeper than that of the target type{, unless the target is a stand-alone object of an anonymous access type. If the target is such a stand-alone object, the accessibility level of the operand type shall not be statically deeper than that of the declaration of the stand-alone object}. In addition to the places where Legality Rules normally apply (see 12.3), this rule applies also in the private part of an instance of a generic unit. That is, I added "such" to the second added sentence so we don't have to repeat all of the anonymous stuff. I did that for both paragraphs. **************************************************************** From: Steve Baird Sent: Tuesday, July 5, 2011 3:36 PM I think AI05-0148 should have included an update of 3.10.2(29.b/2): This check requires that some indication of lifetime is passed as an implicit parameter along with access parameters of an access-to-object type. No such requirement applies to other anonymous access types, since the checks associated with them are all compile-time checks. This is no longer true for a saooaaat, right? [Note: it was pointed out to me (by you?) that "saooaaat" really should be "saooaaatot" - stand-alone object of an anonymous access-to-object type - but you know what I mean] **************************************************************** From: Randy Brukardt Sent: Tuesday, July 5, 2011 6:02 PM It wasn't me that pointed out that "saooaaat" isn't right. Also, most acronyms leave out words like "of" "an" and "to", so I think it should be "saoaaot", not that that helps much. :-) Anyway, I added an additional sentence into this note. "A similar indication is required for stand-alone objects of anonymous access-to-object types." ****************************************************************