!standard 6.01 (18) 09-04-12 AI05-0148-1/01 !standard 6.06 (03) !standard 9.5.1 (02) !class Amendment 09-04-12 !status work item 09-04-12 !status received 09-04-12 !priority Medium !difficulty Medium !subject !summary (See proposal.) !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. !wording Add the folowing after 3.10.2(13/2): The accessibility level of the type of a stand-alone object of an anonymous access-to-object type is determined by 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. 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. 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. 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}. {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.]} !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. 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. !example !ACATS test ACATS C-Tests are needed to check that the accessibility is retained. !appendix 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: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: 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: Bob Duff Sent: Monday, April 13, 2009 x:xx PM **************************************************************** From: Bob Duff Sent: Monday, April 13, 2009 x:xx PM **************************************************************** From: Bob Duff Sent: Monday, April 13, 2009 x:xx PM **************************************************************** From: Bob Duff Sent: Monday, April 13, 2009 x:xx PM ****************************************************************