Version 1.8 of ai05s/ai05-0148-1.txt

Unformatted version of ai05s/ai05-0148-1.txt version 1.8
Other versions for file ai05s/ai05-0148-1.txt

!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 : 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 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 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 parameter 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)
Insert after the paragraph:
the new paragraph:
!corrigendum 3.10.2(19/2)
Insert after the paragraph:
the new paragraph:
!corrigendum 4.6(24.17/2)
Replace the paragraph:
by:
!corrigendum 4.6(48)
Replace the paragraph:
by:
!ACATS test
ACATS C-Tests are needed to check that the accessibility is retained.
!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.

****************************************************************


Questions? Ask the ACAA Technical Agent