Version 1.4 of ai05s/ai05-0051-1.txt

Unformatted version of ai05s/ai05-0051-1.txt version 1.4
Other versions for file ai05s/ai05-0051-1.txt

!standard 6.5(21/2)          07-10-31 AI05-0051-1/03
!class binding interpretation 07-05-04
!status work item 07-05-04
!status received 07-04-19
!priority Medium
!difficulty Easy
!qualifier Clarification
!subject Accessibility of dispatching function calls (aka another Baird question)
!summary
(see recommendation)
!question
How do the various accessibility rules pertaining to function results apply in the case of a dispatching call to a function where the accessibility level of the function named in the call differs from the accessibility level of the function whose body is executed?
!recommendation
The accessibility level against which to perform run-time accessibility checks at the return statement of a dispatching function is determined by the level of the function named at the call-site, even if that is different from the accessibility level of the function body reached by the dispatching call. If the function has an access result, and the expression of the return statement is an (anonymous) allocator, then this accessibility level in turn determines the lifetime of the allocated object.
Similar rules would apply for calls through an access-to-subprogram value. The caller's view would determine the accessibility level to use for run-time accessibility checks.
!wording
Add after 3.10.2(10.1):
Within a return statement that applies to a function with an access result, the accessibility level of the anonymous result access type is determined by the accessibility level of the view of the function named at the point of call. For the purposes of determining whether this level is statically deeper than another, it is presumed to be the same as the level of the function containing the return statement. [Redundant: Note that for a dispatching call or a call through an access-to-subprogram value, the level of the function named at the point of call might be different from that of the function containing the return statement.]
Modify 3.10.2(15) as follows:
The accessibility level of a view of an object or subprogram denoted by a dereference of an access value is the same as that of the access type{, except in the case where the dereference is the name or prefix of a function_call, and the access-to-subprogram value is an access parameter. In this case, the accessibility level of the view of the function denoted by the dereference is that of the master containing the access parameter}.
AARM NOTE:
This is important so that a call through an access-to-subprogram parameter on a function with an access result, a classwide result, or a result with access discriminants, has some reasonable, non-infinitely-deep, accessibility level. We gave such access-to-subprogram parameters infinitely deep accessibility levels to prevent the access values from being converted to named access-to-subprogram types, but once they have been called and return a value, we need to have something useful in the way of an accessibility level to use when their results have an associated level. Since the access-to-subprogram parameter is clearly denoting a function from some scope dynamically enclosing the master containing the access parameter, it is safe to give it an accessibility level of that master.
Modify 6.5(8/2) as follows:
If the result type of a function is a specific tagged type, the tag of the return object is that of the result type. If the result type is class-wide, the tag of the return object is that of the value of the expression. A check is made that the accessibility level of the type identified by the tag of the result is not deeper than that of the [master that elaborated the function body.] {view of the function named at the point of call. [Redundant: Note that for a dispatching call or a call through an access-to-subprogram value, this level might be different from that of the function containing the return statement.]} If this check fails, Program_Error is raised.
Modify 6.5(21/2) as follows:
If the result subtype of a function has one or more unconstrained access discriminants, a check is made that the accessibility level of the anonymous access type of each access discriminant, as determined by the expression or the return_subtype_indication of the function, is not deeper than that of the [master that elaborated the function body.] {view of the function named at the point of call. [Redundant: Note that for a dispatching call or a call through an access-to-subprogram value, this level might be different from that of the function containing the return statement.]} If this check fails, Program_Error is raised.
Modify 7.6.1(11/2) as follows:
The order in which the finalization of a master performs finalization of objects is as follows: Objects created by declarations in the master are finalized in the reverse order of their creation. For objects that were created by allocators for [an] {a named} access type whose ultimate ancestor is declared in the master, this rule is applied as though each such object that still exists had been created in an arbitrary order at the first freezing point (see 13.14) of the ultimate ancestor type; the finalization of these objects is called the finalization of the collection. {Objects created by allocators for an anonymous access type that are not coextensions of some other object, are finalized in an arbitrary order during the finalization of their associated master.} After the finalization of a master is complete, the objects finalized as part of its finalization cease to exist, as do any types and subtypes defined and created within the master.
!discussion
In most cases, the dynamic semantics of access result types follow easily from an informal equivalence rule. The declaration
function Foo ( ...) return access T;
is pretty much equivalent to
type T_Ref is access all T; function Foo (...) return T_Ref;
, and that's that.
If the function in question overrides an inherited dispatching operation and if the controlling type of the function is declared in a more nested scope than its parent type, then this equivalence breaks down.
If the function returns an allocator, then what is the lifetime and accessibility level of the allocated object in the case where the function has been invoked via a dispatching call to the operation of the parent type?
This is an instance of a more general problem associated with uses of the accessibility level of a function body in the case where this level does not match the accessibility level of the function named in the (dispatching) call.
A function with an access result type may return a reference to an object whose accessibility level matches that of the function body:
X : aliased Integer; function F1 return access Integer is begin return X'access; end F1;
A function with a class-wide result type may return a result whose type's accessibility level matches that of the function body:
type Derived is new Some_Tagged_Type with null record; function F2 return Some_Tagged_Type'Class is begin return Derived'(...); end F2;
A function whose result type has anonymous access discriminants may return a result with a discriminant that references an object whose accessibility level matches that of the function body:
type Discriminated (Disc : access Integer) is limited null record; X : aliased Integer; function F3 return Discriminated is begin return (Disc => X'access); end F3;
Each of these three scenarios becomes problematic if the function overrides an inherited dispatching operation and the controlling type of the function is declared in a more nested scope than its parent type.
Consider:
declare type Tagged_Type is tagged null record; type Discriminated (D : access Integer) is tagged limited null record;
package Pkg_1 is type Parent_Type is abstract tagged null record; function F1 (X : Parent_Type) return access Integer is abstract; function F2 (X : Parent_Type) return Tagged_Type'Class is abstract; function F3 (X : Parent_Type) return Discriminated is abstract; function F4 (X : Parent_Type) return access Some_Designated_Type is abstract; end Pkg_1;
procedure Call_Functions (X : Pkg_1.Parent_Type'Class) is begin <Call X.F1, X.F2, and X.F3>
-- -- Each call should fail, right? -- Otherwise we've got the potential for dangling references. -- -- The situation with X.F4 is even less clear. end Call_Functions;
procedure Nested is package Pkg_2 is type Extension is new Pkg_1.Parent_Type with null record; function F1 (X : Extension) return access Integer; function F2 (X : Extension) return Tagged_Type'Class; function F3 (X : Extension) return Discriminated; function F4 (X : Extension) return access Some_Designated_Type; end Pkg_2;
package Other_Locals is I : aliased Integer; type Extension is new Tagged_Type with null record; end Locals;
package body Pkg_2 is function F1 (X : Extension) return access Integer is begin return Other_Locals.I'access; end F1;
function F2 (X : Extension) return Tagged_Type'Class is begin return Other_Locals.Extension'(null record); end F2;
function F3 (X : Extension) return Discriminated is begin return Discriminated'(D => Other_Locals.I'access); end F3;
function F4 (X : Extension) return access Some_Designated_Type is begin return new Some_Designated_Type'(...); end F4; end Pkg_2;
X : Pkg_2.Extension; begin Call_Functions (X); end Nested; begin Nested; end;
This AI presumes an implementation model where these problematic dispatching operations of nested extensions expect an accessibility level to be passed in to indicate the accessibility level of the function declaration named in the call; that level is then used for anonymous-typed allocators at the point of return, and for accessibility checks.
Once we accept that we need to pass in an accessibility level to any function that has a class-wide, access-discriminated, or access result return type, and is, or might become through renaming, a dispatching operation, we conclude that this model might as well be adopted universally for functions with such a result type. In particular, because with a call through an access-to-subprogram, it is hard to know whether you might reach a dispatching function, some sort of level would need to passed whenever making a call through an access-to-subprogram. And since essentially any function might be called through an access-to-subprogram value, all functions with such a result type will need to expect a passed-in accessibility level.
Once we accept we are always passing in a level for these kinds of functions, we need to decide what are the implications for calls through access-to-subprogram values. Since the level of the function named by the dereference of an access-to-subprogram value is always at least as deep as that of the actual designated function, passing in a deeper level will never cause a failure of a run-time accessibility check when a corresponding static accessibility check passed. For the cases where there is no static accessibility check, such as with an access parameter, this may eliminate run-time check failures exactly in cases where they are safe, since code calling through a more deeply nested access-to-subprogram type is set up to handle more deeply nested results.
Here is an example of where these dynamic levels would make a difference:
procedure Getting_Fancy( Y : access T; ATF : access function(X : access T) return access T) is
G : access T; begin G := ATF.all(Y); -- passes in "result level" of master Getting_Fancy end Getting_Fancy;
...
function Identity(X : access T) return access T is begin return X; -- run-time accessibility check -- of level of X against "result level" -- passed in to be sure X is no deeper end Identity;
For access-to-subprogram parameters, which have by definition an infinitely deep accessibility level, we specify (in 3.10.2(15)) that the level of a dereference used to name a function in a call is that of the level of the master containing the access parameter. This is essentially the same rule when you take 'Access of a tagged parameter. It is treated like a local variable of the subprogram. So in a corresponding way, we treat the call as though it were via an access-to-subprogram type declared local to the subprogram containing the access parameter.
We need to make the dynamic accessibility level of an access result type reflect this same set of rules, so we add a paragraph in 3.10.2 to specify that the accessibility level comes from the caller's view of things. This is important because if the return expression is an (anonymous) allocator, we need to know its accessibility level.
We relax the rules for order of finalization of objects (other than coextensions) created by anonymous allocators, since tying them to a particular freezing point is tricky, and doesn't seem to be of great value to the user. All that really matters is that they get finalized in the master determined by their accessibility level, and that is already specified by 7.6.1(4). For named access types, there is the concern that their collections be finalized before any corresponding storage pool object gets finalized, but there is no similar consideration for non-coextension anonymous allocations, since they always use the "default" storage pool.
Note that in some of this wording, we have stopped using the phrase "the master that elaborated the function body" and simply talked about the level of the (view of the) function. By definition, the level of a declared entity is that of the master that elaborates it, so talking about the master that elaborates the function body is a long-winded way of saying the level of the function. By simplifying the wording, it is easier to talk about the function "named" at the point of the call.
!ACATS test
!appendix

From: Stephen W. Baird
Sent: Thursday, April 19, 2007  12:04 PM

!question

How do the various accessibility rules pertaining to function results
apply in the case of a dispatching call to a function where the accessibility
level of the function named in the call differs from the accessibility
level of the function whose body is executed?

!discussion

In most cases, the dynamic semantics of access result types follow easily
from an informal equivalence rule. The declaration

    function Foo ( ...) return access T;

is pretty much equivalent to

    type T_Ref is access all T;
    function Foo (...) return T_Ref;

, and that's that.

If the function in question overrides an inherited dispatching operation
and if the controlling type of the function is declared in a more nested
scope than its parent type, then this equivalence breaks down.

If the function returns an allocator, then what is the lifetime and
accessibility level of the allocated object in the case where the
function has been invoked via a dispatching call to the operation of
the parent type?

Tuck has pointed out that this is an instance of a more general problem
associated with uses of the accessibility level of a function body
in the case where this level does not match the accessibility level
of the function named in the (dispatching) call.

A function with an access result type may return a reference to an object
whose accessibility level matches that of the function body:

    X : aliased Integer;
    function F1 return access Integer is
    begin
      return X'Access;
    end F1;
    
A function with a class-wide result type may return a
result whose type's accessibility level matches that of
the function body:

    type Derived is new Some_Tagged_Type with null record;
    function F2 return Some_Tagged_Type'Class is
    begin
      return Derived'(...);
    end F2;

A function whose result type has anonymous access discriminants
may return a result with a discriminant that references an
object whose accessibility level matches that of the function body:

    type Discriminated (Disc : access Integer) is limited null record;
    X : aliased Integer;
    function F3 return Discriminated is
    begin
       return (Disc => X'Access);
    end F3;

Each of these three scenarios becomes problematic if the function
overrides an inherited dispatching operation and the controlling
type of the function is declared in a more nested scope than its
parent type.

Consider:

    declare
      type Tagged_Type is tagged null record;
      type Discriminated (D : access Integer)
        is tagged limited null record;
          
      package Pkg_1 is
        type Parent_Type is abstract tagged null record;
        function F1 (X : Parent_Type) return access Integer is abstract;
        function F2 (X : Parent_Type) return Tagged_Type'Class is abstract;
        function F3 (X : Parent_Type) return Discriminated is abstract;
        function F4 (X : Parent_Type)
                 return access Some_Designated_Type is abstract;
      end Pkg_1;
      
      procedure Call_Functions
                 (X : Pkg_1.Parent_Type'Class) is
      begin
        <Call X.F1, X.F2, and X.F3>
        
        --
        -- Each call should fail, right?
        -- Otherwise we've got the potential for dangling references.
        --
        -- The situation with X.F4 is even less clear.
       end Call_Functions;
      
      procedure Nested is
        package Pkg_2 is
          type Extension is new Pkg_1.Parent_Type with null record;
          function F1 (X : Extension) return access Integer;
          function F2 (X : Extension) return Tagged_Type'Class;
          function F3 (X : Extension) return Discriminated;
          function F4 (X : Extension) return access Some_Designated_Type;
        end Pkg_2;
     
        package Other_Locals is
          I : aliased Integer;
          type Extension is new Tagged_Type with null record;
        end Locals;
          
        package body Pkg_2 is
          function F1 (X : Extension) return access Integer
            is begin return Other_Locals.I'access; end F1;
            
          function F2 (X : Extension) return Tagged_Type'Class
            is begin return Other_Locals.Extension'(null record); end F2;
 
          function F3 (X : Extension) return Discriminated
            is begin return Discriminated'(D => Other_Locals.I'Access); end F3;

          function F4 (X : Extension) return access Some_Designated_Type
            is begin return new Some_Designated_Type'(...); end F4;
         end Pkg_2;
                    
        X : Pkg_2.Extension;       
      begin
        Call_Functions (X);
      end Nested;    
    begin
      Nested;
    end;
    
Tuck suggests an implementation model where these problematic dispatching
operations of nested extensions expect an accessibility level to be
passed in to indicate the accessibility level of the function declaration
named in the call; that level is then used for anonymous-typed allocators
at the point of return, and for accessibility checks.

This seems like a good approach, but it is not implicit in the current RM.
Wording changes would be needed - a "clarification" would not suffice.

Furthermore, there are some aspects of this problem that might still
need resolution even if this proposal were adopted:

1) In the case of a dispatching call to a function with an
   access result type which returns an allocator, exactly when is the
   allocated object finalized? This remains a valid (albeit obscure)
   question even in the case where the parent type and the extension
   type have the same accessibility level. 7.6.1(11.2) refers to
   "the first freezing point of the ultimate ancestor type", but
   is this the freezing point of the access type associated with the
   function named in the call or with the function whose body is executed?

2) A wording change in 6.5(21/2) seems to be needed; in the case of
   a class-wide result type, the specified check may need to be performed
   even if the result type of the function has no access discriminants.

   Consider the following example:

     declare
       type Root is tagged limited null record;
       type Ext (D : access Integer) is new Root with null record;

       function F return Root'Class is
         Local_Var : aliased Integer;

         function Local_Func return Ext is
         begin
           return (D => Local_Var'Access);
         end Local_Func;
       begin
         return Local_Func;
       end F;
    
       procedure Do_Something (X : access Integer) is ... end;
     begin
       Do_Something (Ext (F).D);
     end;

   If this test executes without raising any exception, then a
   dangling reference will be passed to Do_Something. It seems
   that the check described in 6.5(21/2) needs to be performed
   as part of returning from F, but the result subtype of F
   lacks unconstrained access discriminants.
   
   This will introduce distributed overhead in the sense that
   a function which returns a limited classwide result may have
   to check for the possibility that its result has a "bad"
   access discriminant values even if there are no access
   discriminants anywhere in the program.
   
   We probably don't want two active AIs which both modify
   6.5(21/2), but this issue might be split off into a separate
   AI if that doesn't appear to be a problem.


From: Tucker Taft
Sent: March 27, 2007
      Sent to Steve Baird, in response to (roughly) the question
      about allocators that begins the !discussion section above.

Very nasty.  It seems like we might have the same
problem with functions returning class-wide types,
and perhaps with functions returning objects with
access discriminants.  In all three cases, there
is an accessibility check at the point of the return
statement, and in each case the check is against
the accessibility level of the function body itself.
But if the caller has reached the function body via
a dispatching call, the caller may be presuming
an accessibility level associated with the dispatching
operation identified at compile-time, rather than the body
determined at runtime.

There seem various possible solutions, none particularly
pleasant.  I think my favorite would be essentially
a "wrapper" model, where dispatching operations of
nested extensions expect an accessibility level passed
*in* to indicate the accessibility level of the function
declaration named in the call, and that level is used
for anonymous allocators at the point of return, and
for accessibility checks.  The "wrapper" aspect is that
to avoid distributed overhead, the extra implicit parameter
would be provided by a "wrapper" rather than by the
caller, so if calling operations of non-nested extensions,
there is no additional overhead.  Note that any function
that might *become* a dispatching operation via renaming
would have to assume the worst, and allow for this
implicit parameter.  Some kind of a wrapper may be needed
for these kinds of dispatching calls to deal with the
static link, so adding another implicit parameter might
be relatively straightforward.

This model also seems consistent with the wording in 3.10.2(10.1/2)
where we indicate that the accessibility of a return object
goes from being that of the return statement, to being that
determined by the point of call, with no interim "stop"
at the level of the function body.

I think passing *in* an accessibility level is preferable to
passing one *out* because we already have situations where
we pass one in (i.e. with access parameters, and probably also
with the new limited return types), and so we have
already figured out what that entails as far as passing
static vs. dynamic levels.  On the other hand, the level we
pass in for access parameters is never used for the *target*
of a conversion, only for the *operand*, so the same logic
might not work...  Hmmm...

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

From: Tucker Taft
Sent: Wednesday, October 31, 2007  12:34 AM

Here is an AI [version /03 - ED.] that Steve started, and
I finished, which attempts to deal with the case where, from
an accessibility level point of view, a caller
has one view of a function, and the return statement
has a different view.  This can happen in four ways:


    1) In a dispatching call, where the caller
       is calling an operation of a parent
       type T1, and is actually reaching an
       overriding defined for a nested extension T2.
       (caller view is not as deep as called function)

    2) In a dispatching call, where the caller
       is calling an operation of a nested
       extension T2, and is actually reaching
       an inherited operation from a parent T1.
       (caller view is deeper than called function)

    3) In a call through an access-to-subprogram
       value, where the access type is declared at
       a deeper level than the designated subprogram.
       (caller view is deeper than called function)

    4) In a call through an access-to-subprogram
       value that is an access parameter, which
       has an "infinite" accessibility level, clearly
       deeper than the designated subprogram.
       (caller view is *much* deeper than called function)

After much head scratching and trying various solutions,
we settled on uniformly using the caller's view for
the run-time accessibility checks performed at a return
statement.  This also means that the caller has an
accurate view of the accessibility characteristics
of the returned object.  In addition it means that
if an access parameter is passed by the caller, the
level of the access parameter can be meaningfully
compared with the "result" level passed in, in case
the access parameter is used as part of the result
in one way or another (e.g. as an access discriminant,
or as the access result).

Case (4) represented an existing hole in 3.10.2, where
we hadn't properly figured out the accessibility
characteristics of results returned from calls through
an access-to-subprogram parameter.  In this AI, we
suggest that rather than the result of such a call
also having an "infinite" accessibility level, it
should have a level corresponding to the master containing
the access parameter, as though the access value were
of an access-to-subprogram type declared local to the
master with the access parameter.

Anyway, I am giving away too much in the cover letter.
Time to read the AI!

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

From: Stephen W. Baird
Sent: Wednesday, October 31, 2007  6:12 PM

> The accessibility level against which to perform
> run-time accessibility checks at the return statement
> of a dispatching function is determined by the
> level of the function named at the call-site, even if
> that is different from the accessibility level of the
> function body reached by the dispatching call.  If the
> function has an access result, and the expression of the
> return statement is an (anonymous) allocator, then this
> accessibility level in turn determines the lifetime of
> the allocated object.

I think this means that if a function with an anonymous
access result type returns an allocator, then the allocated
object may have a different master than if the function
had returned a recursive call which returned an allocator.
This makes me nervous. Is this what we want?

If a caller makes a dispatching call on a subprogram
declared in a less nested scope than the scope of the
executed function body and the callee then attempts to
return the result of a nondispatching recursive call,
is Program_Error raised because the recursive
call might have successfully returned a "bad" reference?

It seems odd to have a simple recursive call like
    function F return access Integer is
    begin
      if ... then
        return F;
      end if;
      ...
    end F;
failing the accessibility check associated with an
access type conversion. It seems like the source type
and the target type are the same, but this AI
suggests that they aren't (at least dynamically).

I suspect that I'm just confused; this stuff is tricky.

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

From: Tucker Taft
Sent: Wednesday, October 31, 2007  7:19 PM

> I think this means that if a function with an anonymous
> access result type returns an allocator, then the allocated
> object may have a different master than if the function
> had returned a recursive call which returned an allocator.
> This makes me nervous. Is this what we want?

Probably not.

I think we need to presume that when calling such
a function in a return statement, the level against
which the returned value is being checked must be
passed on to the called function.  That is, in:

    return Func(...);

if Func has a result type that is classwide,
access-discriminated, or an anonymous access
type, then if a level is passed into the
enclosing function, the same level should be
passed to Func.  I suspect AI05-51 will need
some extra wording to that effect, though
conceivably it may be implied by something
already in the AI.

> 
> If a caller makes a dispatching call on a subprogram
> declared in a less nested scope than the scope of the
> executed function body and the callee then attempts to
> return the result of a nondispatching recursive call,
> is Program_Error raised because the recursive
> call might have successfully returned a "bad" reference?
> 
> It seems odd to have a simple recursive call like
>     function F return access Integer is
>     begin
>       if ... then
>         return F;
>       end if;
>       ...
>     end F;
> failing the accessibility check associated with an
> access type conversion. It seems like the source type
> and the target type are the same, but this AI
> suggests that they aren't (at least dynamically).

I think your example would work as desired if the
return statement passes along the dynamic accessibility
level it received implicitly.

> I suspect that I'm just confused; this stuff is tricky.

I think you have identified an important additional
requirement, namely that the return statement passes
along the dynamic accessibility level.

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

From: Tucker Taft
Sent: Wednesday, October 31, 2007  8:38 PM

> ...
> I think we need to presume that when calling such
> a function in a return statement, the level against
> which the returned value is being checked must be
> passed on to the called function.  That is, in:
> 
>    return Func(...);
> 
> if Func has a result type that is classwide,
> access-discriminated, or an anonymous access
> type, then if a level is passed into the
> enclosing function, the same level should be
> passed to Func.  I suspect AI05-51 will need
> some extra wording to that effect, though
> conceivably it may be implied by something
> already in the AI.

I think the paragraph proposed in the AI to follow
3.10.2(10.1) could be rewritten roughly as follows:

     Within a return statement that applies to a
     function with a result subtype that is an access
     result, that is class-wide, or that has one or more
     unconstrained access discriminants, the (dynamic)
     accessibility level of this function, and other
     functions with such a result subtype elaborated by
     the same master, are considered to have the same
     accessibility level as that of the view of the
     function named at the point of call. [Redundant:
     Note that for a dispatching call or a call through
     an access-to-subprogram value, the level of the
     function named at the point of call might be
     different from that of the function containing the
     return statement.]

The basic idea is that within the return statement,
the function and all other similar functions act
as though there were of the same accessibility
level as the function named at the call site.
That means that any function that has a level
passed in, and that is of the same level as
the function with the return statement, will
be passed in the same dynamic level.

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

From: Stephen W. Baird
Sent: Thursday, November 1, 2007  4:07 PM

This seems like it is getting way too complicated.  I don't like this
call-site sensitivity (if you call this function from here, it means
something different than if you call it from there).

Before looking at any of the details of this proposal,
let's back up and look at why access result types were introduced.
They were only intended to be a notational convenience, to spare the
user the inconvenience of declaring a named access type.
We were introducing anonymous access types in other places and
access result types for functions fit in nicely with these other changes.
They were not intended to have any interesting dynamic semantics,
nor was it realized at the time that they would have to.

Now we've realized that there are some fairly complicated problems in
this area, I'd like to at least consider the possibility of solving them 
all
by statically disallowing the problematic cases.  We could introduce a 
rule that the profile of a dispatching function or of an 
access-to-function type
cannot have an (anonymous) access result type. Or,  if that is too
restrictive, we could impose more stringent restrictions on the use of the
'Access attribute for subprograms if the profile has an access result 
type,
along with similar restrictions for nested extensions if there is a 
primitive
operation with an access result type. The point is that we could devise
rules of some sort for statically disallowing the problematic cases.

What do folks think about this general approach?

Ok, now for a more specific point about this proposal.

In implementation terms, the general idea is that within a return 
statement
that is returning from a function which was passed an
accessibility_level/storage_pool/whatever descriptor,  you pass the
descriptor reference along  whenever you call any  function that also
takes such a descriptor parameter.

Consider the following example:

 function F (Controlling_Operand : Some_Tagged_Type;
                      Flag                : Boolean := False)  return 
access Designated is 
    begin
        return Result : access Designated do
            declare
                task T;
                task body T is
                    procedure P is
                    begin
                        if Flag then
                            Result := new Designated;
                        else
                            Result := F (not Flag);
                        end if;
                    end P;
                begin
                    P;
                end T;
            begin
                null;
            end;
        end return;
    end F;
 
This example demonstrates (I think) that when you say "within a
return statement", you really mean it. The rule even applies inside
of nested procedures, tasks, etc. But that would mean that it would
even apply inside of a nested return statement (returning from a nested
function), and so now you have contradictory requrements (I think)
imposed by the two enclosing return statements. The simple
solution (let the innermost return statement win) doesn't work.
Consider modifying the example so that a recursive call to F
    Result := new F (not Flag);
occurs within a return statement within a function declared within
the declare block that is declared within F's (one and only)
return statement. 

I expect that we can work this particular corner case out, but  it
seems likely that there are other dragons lurking in the vicinity.

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

From: Stephen W. Baird
Sent: Thursday, November 1, 2007  5:05 PM

A correction to the example of my previous message on this thread:
   The recursive call to F lacks a value for
   the Controlling_Operand parameter. I intended
   to pass along the existing parameter in
   a nondispatching call:
      Result := F (Controlling_Operand, not Flag);

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

From: John Barnes
Sent: Thursday, November 1, 2007  4:35 PM

As time has gone on I am becoming to feel that anonymous access types in
general (that is universally) were perhaps a mistake. I know I had great
trouble weaving them into my book. Personnally I would now never use them.
Ada prides itself in being precise and anon types seem to have slid us down
a slippery slope of confusion leading to accessibility problems.

Of course anonymous access to subprogram parameters are important but they
are just essentially subprogram parameters as in Algol 60 so that is no
problem.


Anyway. So I am very sympathetic to Steve's view. Too late too late the
virgin cried no doubt!

See y'all next week. Or a subset thereof.

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

From: Tucker Taft
Sent: Thursday, November 1, 2007  9:41 PM

> ...  We could introduce a 
> rule that the profile of a dispatching function or of an access-to-function type
> cannot have an (anonymous) access result type. Or,  if that is too
> restrictive, we could impose more stringent restrictions on the use of the
> 'Access attribute for subprograms if the profile has an access result type,
> along with similar restrictions for nested extensions if there is a primitive
> operation with an access result type. The point is that we could devise
> rules of some sort for statically disallowing the problematic cases.
> 
> What do folks think about this general approach?

I think it depends how draconian is the restriction.
Disallowing them altogether for dispatching functions
would seems pretty draconian.  Disallowing nested
extensions of types with such functions is less
draconian.  I think once you do that, then you
can eliminate the issue with calling through
access-to-subprogram values, because such functions
no longer need to be passed a level.

But remember, we also have the same issue for functions
returning class-wide types and types with unconstrained
access discriminants, though perhaps those aren't as bad.

> Ok, now for a more specific point about this proposal.
> 
> In implementation terms, the general idea is that within a return 
> statement that is returning from a function which was passed an
> accessibility_level/storage_pool/whatever descriptor,  you pass the
> descriptor reference along  whenever you call any  function that also
> takes such a descriptor parameter.

Only if the function called in the return statement
is at the *same* level as the function to which
the return statement applies.

> Consider the following example:
> 
>  function F (Controlling_Operand : Some_Tagged_Type;
>                       Flag                : Boolean := False)  return 
> access Designated is 
>     begin
>         return Result : access Designated do
>             declare
>                 task T;
>                 task body T is
>                     procedure P is
>                     begin
>                         if Flag then
>                             Result := new Designated;
>                         else
>                             Result := F (not Flag);
>                         end if;
>                     end P;
>                 begin
>                     P;
>                 end T;
>             begin
>                 null;
>             end;
>         end return;
>     end F;
>  
> This example demonstrates (I think) that when you say "within a
> return statement", you really mean it. The rule even applies inside
> of nested procedures, tasks, etc. But that would mean that it would
> even apply inside of a nested return statement (returning from a nested
> function), and so now you have contradictory requrements (I think)
> imposed by the two enclosing return statements.

Not true, because the rule only applies if the called
function is at the same level as the function to which the
return statement applies.  If you have a nested return
statement, clearly the function called could only match
at most one of them, and it is that one whose dynamic level
is passed along.

> ... The simple
> solution (let the innermost return statement win) doesn't work.
> Consider modifying the example so that a recursive call to F
>     Result := new F (not Flag);
> occurs within a return statement within a function declared within
> the declare block that is declared within F's (one and only)
> return statement. 
> 
> I expect that we can work this particular corner case out, but  it
> seems likely that there are other dragons lurking in the vicinity.

I don't think this particular one is a problem.
But I will never underestimate your amazing ability
to find bizarro cases.

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

From: Pascal Leroy
Sent: Monday, November 5, 2007  3:10 PM

> > What do folks think about this general approach?
>
> I think it depends how draconian is the restriction.
> Disallowing them altogether for dispatching functions
> would seems pretty draconian.  Disallowing nested
> extensions of types with such functions is less
> draconian.  I think once you do that, then you
> can eliminate the issue with calling through
> access-to-subprogram values, because such functions
> no longer need to be passed a level.

I am sympathetic to the notion of finding a way to disallow the
problematic cases statically, if only because Tuck's proposed rule
caused my brain to explode.  However, I foresee difficulties with
privacy and perhaps the contract model.  If the function with an
anonymous access result is declared in a private part, how do you
disallow nested extensions outside of the package?  Perhaps other
rules of the language prevent problems in this case, but I wouldn't
bet on it.

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

From: Stephen W. Baird
Sent: Monday, November 5, 2007  3:51 PM

Another option would be to introduce a rule that the Storage_Size of an
access result type is "defined by the language to be zero" (4.8(5.3/2)).

This would only help with the allocator-related problems mentioned
in the AI, but it still might turn out to be a useful part of a more 
complete solution if it isn't rejected as being too restrictive.

In any case, this rule  doesn't seem to suffer from any of the contract
model difficulties that you describe.

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

From: Tucker Taft
Sent: Monday, November 5, 2007  9:40 PM

> I am sympathetic to the notion of finding a way to disallow the
> problematic cases statically, if only because Tuck's proposed rule
> caused my brain to explode.  ...

My brain was exploding when I proposed it as well.

How about this alternative:

A) Don't do anything special (at the call site)
for dispatching operations that return classwide
or access-discriminated result types.  Instead,
just use the tag of the controlling operand to
determine the run-time accessibility level of the result
of calling such a dispatching function.  For example:

     type T is tagged null record;
     type Has_Acc_Discrim(D : access Q) is null record;

     function Gizmo(X : T) return Has_Acc_Discrim;

If Gizmo is a dispatching operation of T, then when
we make a dispatching call on it using an actual
parameter of type T'Class, we dispatch to a routine
that is associated with the type identified by the
tag of the actual.  E.g:

    Y : T2;

    HAD_Obj : Has_Acc_Discrim renames Gizmo(T'Class(Y));

So what is the run-time accessibility level associated
with HAD_Obj?  With this model, it is determined by the
run-time tag of the actual controlling operand, in this
case "T'Class(Y)", i.e. the level implied by T2'Tag.
We know that all controlling operands have the same tag,
so there is no ambiguity.  If the corresponding operation
is actually inherited for the given descendant, that
is OK, because the actual accessibility level of the
return object is no deeper than that implied by the
controlling tag.

B) For dispatching operations with access result,
we need to worry about two things, one, what sort of
run-time accessibility check is performed, and two, what
storage pool is used if the return statement consists
of an allocator.  I would propose we model an access result
like "return access Q" roughly like "return Has_Acc_Discrim".
That is, we treat the access result as though it
were an access discriminant of an imaginary "wrapper"
type.  The context of the call-site would determine the
storage pool to be used for an anonymous allocator
in the return statement.  The accessibility level
check would be the same as that given above for A,
namely we check against the level of the master
of the function body, and the caller presumes the
level is that of the actual controlling tag.

To be more precise what I mean by using the context
of the call-site to determine the storage pool,
if the result of the call is converted explicitly
or implicitly to another access type, the storage
pool of that target access type is passed in as the
storage pool for the access result.  If the result of
the call is dereferenced, then it is treated the same
way a function returning an object with access
discrims, and the context of the usage of the
dereference determines the storage pool.

The reason I like this rule is that it means that there
is some chance you can avoid storage leaks associated
with access results, while also solving the problem
associated with calls on dispatching operations
on nested type extensions.  The current rule pretty much
guarantees storage leaks, since the default storage pool
from which the anonymous allocator allocates does
not support reclamation, and it is at a level that
means the result will often live forever.  By instead
determining the storage pool by the usage of the
result, we can avoid the storage leak, while still
remaining safe with respect to accessibility.

For example:

    function Gizmo2(X : T) return access Q;

    ...

    type Z is access Q;

    R : Z := Gizmo2(blah);
      -- pass in storage pool associated with Z
      -- to be used by anonymous allocator

    S : Z := new Q'(Gizmo2(blurfo).all);
      -- again, here we pass in storage pool
      -- associated with Z, as we would have
      -- if Gizmo2 returned an object with
      -- access discrims

    V : access Q := Gizmo2(blech);
      -- pass in a storage pool associated with
      -- the implicit access type of V, essentially
      -- the local frame

      ...

    return Gizmo2(belch);
      -- pass in the storage pool associated with
      -- the result type, which if it is also "access Q"
      -- means the same storage pool passed into the
      -- enclosing function.

------------

I think the above approach gives an answer to the
interesting questions, such as:

   1) what run-time accessibility check is performed
      at a return statement when the result type is
      classwide, access-discriminated, or an access result?
     Answer: exactly what we currently say in Ada 2005 --
      check against the level of the master that elab'ed
      the function body.

   2) what run-time accessibility level does the result of
      a call on a dispatching function have?
     Answer: the same as that of the controlling tag
      of the call.

   3) What storage pool should be used for access results?
     Answer: a storage pool passed in from the caller,
      determined by the context of the call, using a model
      where we treat the result as though it were an
      access discriminant of a "wrapper" result type.

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

From: Stephen W. Baird
Sent: Tuesday, November 27, 2007  5:23 PM

I don't like the general approach of the AI05-51 proposal we
discussed at the Fairfax meeting (see Tuck's message
of 11/06/07).

In general, I feel that this proposal adds
unnecessary complexity to the dynamic semantics of
the language. By making the runtime accessibility
checking rules of the language more complex, we
would be making it harder to write reliable software.

Consider the following example:

  procedure Foo is
    package Pkg is
      type T is tagged
        record
          Int : Integer;
        end record;
      function F (X : T) return access String;
    end Pkg;

    package body Pkg is
      type String_List_Node is
          record
            Item : access String;
            Link : access String_List_Node;
          end record;

      Allocated_Strings : access String_List_Node;

      function F (X : T) return access String is
      begin
        return Result : access String :=
              new String'(Integer'Image (X.Int)) do
          Allocated_Strings :=
            new String_List_Node'
              (Item => Result,
               Link => Allocated_Strings);
        end return;
      end F;
    end Pkg;
  begin
    declare
      type D is new Pkg.T with null record;
      X : D := (Int => 123);

      procedure Check (F_Result : access String) is
        Check_Failed : exception;
      begin
        if F_Result.all /= Integer'Image (X.Int) then
          raise Check_Failed;
        end if;
      end Check;
    begin
      Check (X.F);
      Check (Pkg.T (X).F);
    end;
  end Foo;

As I understand the proposed changes, this example
would raise Program_Error during the evaluation of
the aggregate (specifically, during the type
conversion associated with the "Item => Result"
portion of the aggregate).

I think that users would find this surprising.
I believe that this example should execute without
raising an exception.

It's clear that we have make changes of some kind
to deal with the problems identified in the AI.
Let's consider another alternative.

The root cause of these problems is a disagreement
between the accessibility level of the function
named in a call and the accessibility level of the
function whose body is executed. I would like to
eliminate this case without imposing draconian
restrictions. Very roughly speaking, the idea is to
get a function to behave as though it were declared
at the least deeply nested level from which it
could be called. If a function is callable from a
less nested level than where it is declared, then
that is part of the contract which the function's
body is responsible for honoring.

We define a new accessibility level, the "inherited
accessibility level" (for lack of a better name),
of a subprogram as the most deeply nested
accessibility level that satisfies the following
conditions:

   The inherited accessibility level of a subprogram
   is not deeper than the accessibility level of the
   subprogram.
 
   The inherited accessibility level of an inherited
   subprogram is not deeper than the inherited
   accessibility level of the subprogram's parent
   subprogram.
 
   The inherited accessibility level of a
   dispatching subprogram is not deeper than the
   inherited accessibility level of any subprogram
   which the given subprogram overrides.
 
The function result accessibility checks discussed
in the AI are then performed relative to the
inherited accessibility level of the function, as
opposed to the "normal" accessibility level of
the function. Similarly, the properties of an
(anonymous) access result type (including its
accessibility level) are determined by the
inherited accessibility level of the function.
In determining this new accessibility level, there
are no dependencies on dynamic values such as
the tag of the controlling operand or the
subprogram view referenced by the caller.

A few legality rules would be needed in order to
deal with some corner cases. 

In the case of a dispatching operation which is
declared as a rename of some other subprogram, the
inherited accessibility level of the dispatching
operation and of the renamed subprogram must match.
We don't want to allow

  declare
    package Outer is
      type T1 is tagged null record;
      function F1 (X : T1) return access String;
    end Outer;

    ... ;
  begin
    declare
      package Inner is
        type T2 is new Outer.T1 with null record;

        not overriding function F2 (X : T2)
          return access String;

        overriding function F1 (X : T2)
          return access String renames F2;
      end Inner;

      ... ;
    begin
      ...;
    end;
  end;

, because Inner.F2's inherited accessibility level
is deeper than Inner.F1's.

In the case of a dispatching inherited
subprogram which is not overridden, the inherited
accessibility level of the subprogram and of its
parent subprogram must match. We don't want to allow 

  declare
    package Outer is
      type Ifc is interface;
      function F (X : Ifc) return access String
        is abstract;
    end Outer; 
  begin
    declare
      package Inner1 is
        type T1 is tagged null record;
        function F (X : T1) return access String;
      end Inner1;

      package Inner2 is
        type T2 is new Inner1.T1 and Outer.Ifc
          with null record;
      end Inner;
 
      ...;
    begin
      ...;
    end;
  end; 

If this were allowed, a call to Outer.F might end up
executing the body of Inner1.F, thereby violating the
rule that a function's inherited accessibility level
corresponds to the least deeply nested level from
which the function might be called.

These restrictions are only needed if
the subprogram is a function with an "interesting"
result type (anonymous access, class-wide, or has
access discriminants). I believe they are not
needed for an abstract subprogram.

Does this approach look like it would work?
Does it look preferable to the Fairfax proposal?

I don't like the name "inherited accessibility
level" very much. Any suggestions? Would
"caller's accessibility level" or "exported
accessibility level" be better?

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

From: Tucker Taft
Sent: Wednesday, November 28, 2007  11:15 AM

> I don't like the general approach of the AI05-51 proposal we
> discussed at the Fairfax meeting (see Tuck's message
> of 11/06/07).
> 
> In general, I feel that this proposal adds
> unnecessary complexity to the dynamic semantics of
> the language. By making the runtime accessibility
> checking rules of the language more complex, we
> would be making it harder to write reliable software.

On the other hand, it seems valuable to me to have
the same rules for anonymous allocators as access
discriminants and as "unwrapped" values in a return
statement.  That is, the rules proposed on 11/6 have the
advantage of uniformity, where:

     return new T;
  and
     return (Discrim => new T, ...);

both use a storage pool determined by the caller (presuming
the access types are anonymous in both cases).

> Consider the following example:
> 
>   procedure Foo is
>     package Pkg is
>       type T is tagged
>         record
>           Int : Integer;
>         end record;
>       function F (X : T) return access String;
>     end Pkg;
> 
>     package body Pkg is
>       type String_List_Node is
>           record
>             Item : access String;
>             Link : access String_List_Node;
>           end record;
> 
>       Allocated_Strings : access String_List_Node;
> 
>       function F (X : T) return access String is
>       begin
>         return Result : access String :=
>               new String'(Integer'Image (X.Int)) do
>           Allocated_Strings :=
>             new String_List_Node'
>               (Item => Result,
>                Link => Allocated_Strings);
 >         end return;

It seems easy enough to avoid using the storage pool
provided by the caller if that is desirable, by allocating
the result as part of the creation of the String_List_Node,
and then returning that:

         Allocated_Strings :=
             new String_List_Node'
               (Item => new String'(Integer'Image (X.Int)),
                Link => Allocated_Strings);
         return Allocated_Strings.Item;

It seems that programmers will need to get used to the idea that
the storage pool for anonymous allocators in a return
statement are determined by the caller.  To have different
rules for whether the anonymous allocators are inside or
outside an aggregate seems potentially more error prone,
and has the added advantage of avoiding a certain storage
leak.

>       end F;
>     end Pkg;
>   begin
>     declare
>       type D is new Pkg.T with null record;
>       X : D := (Int => 123);
> 
>       procedure Check (F_Result : access String) is
>         Check_Failed : exception;
>       begin
>         if F_Result.all /= Integer'Image (X.Int) then
>           raise Check_Failed;
>         end if;
>       end Check;
>     begin
>       Check (X.F);
>       Check (Pkg.T (X).F);
>     end;
>   end Foo;
> 
> As I understand the proposed changes, this example
> would raise Program_Error during the evaluation of
> the aggregate (specifically, during the type
> conversion associated with the "Item => Result"
> portion of the aggregate).

A similar result would occur if Result were an object
with an access discriminant, and the programmer attempted
to string the value of the access discriminant on
a local list before returning it.
 
> I think that users would find this surprising.
> I believe that this example should execute without
> raising an exception.

Clearly this is a matter of expectation.  Programmers
will not be surprised if they understand the general
rule that anonymous allocators in return statements
use the caller's storage pool.  That makes sense since
the primary user of the result is the caller.  If
the programmer wants to squirrel away a copy of something
before it is returned, they clearly have a special
case and will have to use special care to avoid
dangling references.

> It's clear that we have make changes of some kind
> to deal with the problems identified in the AI.
> Let's consider another alternative.
> 
> The root cause of these problems is a disagreement
> between the accessibility level of the function
> named in a call and the accessibility level of the
> function whose body is executed. 

I would claim that isn't really the heart of the
problem for anonymous allocators.  There it is that the
caller uses the result of the anonymous allocator
in a particular context, and it is not helpful
to allocate in a storage pool over which the
caller has no control.

Having anonymous allocators defining all or part
of a return value is new with Ada 2005.  It seems
quite helpful if all such anonymous allocators are
treated similarly, whether they are used to define
the whole return value, or an access discriminant
of one.

> ... I would like to
> eliminate this case without imposing draconian
> restrictions. Very roughly speaking, the idea is to
> get a function to behave as though it were declared
> at the least deeply nested level from which it
> could be called. If a function is callable from a
> less nested level than where it is declared, then
> that is part of the contract which the function's
> body is responsible for honoring.
> 
> We define a new accessibility level, the "inherited
> accessibility level" (for lack of a better name),
> of a subprogram as the most deeply nested
> accessibility level that satisfies the following
> conditions:
> 
>    The inherited accessibility level of a subprogram
>    is not deeper than the accessibility level of the
>    subprogram.
>  
>    The inherited accessibility level of an inherited
>    subprogram is not deeper than the inherited
>    accessibility level of the subprogram's parent
>    subprogram.
>  
>    The inherited accessibility level of a
>    dispatching subprogram is not deeper than the
>    inherited accessibility level of any subprogram
>    which the given subprogram overrides.
>  
> The function result accessibility checks discussed
> in the AI are then performed relative to the
> inherited accessibility level of the function, as
> opposed to the "normal" accessibility level of
> the function. Similarly, the properties of an
> (anonymous) access result type (including its
> accessibility level) are determined by the
> inherited accessibility level of the function.
> In determining this new accessibility level, there
> are no dependencies on dynamic values such as
> the tag of the controlling operand or the
> subprogram view referenced by the caller.
> ...
> Does this approach look like it would work?
> Does it look preferable to the Fairfax proposal?

I'm not very fond of effectively pushing the accessibility
level up to the global level in most cases,
and making a storage leak even more likely.

Once we accept that anonymous allocators in return
statements generally depend on the context provided
by the caller, it seems natural for this to apply
to access results as well as access discriminants
of results.

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

From: Tucker Taft
Sent: Wednesday, November 28, 2007  9:40 PM

> > In general, I feel that this proposal adds
> > unnecessary complexity to the dynamic semantics of
> > the language. By making the runtime accessibility
> > checking rules of the language more complex, we
> > would be making it harder to write reliable software.
>
> On the other hand, it seems valuable to me to have
> the same rules for anonymous allocators as access
> discriminants and as "unwrapped" values in a return
> statement.  That is, the rules proposed on 11/6 have the
> advantage of uniformity, where:
>
>      return new T;
>   and
>      return (Discrim => new T, ...);
>
> both use a storage pool determined by the caller (presuming
> the access types are anonymous in both cases).

Surely having them illegal would have that effect. ;-)

But I'm not sure I agree with your basic point.

In the case of an access discriminant, the discriminant cannot be changed on
the target object; either that object is already constrained (and this one
had better match) or this is an initial value, in which case your point
holds.

But in the case of an access return, the returned access can be used in many
ways, and there is no reason to assume that the level of the call is any
more correct than the level of the function. For instance, consider:

     function New_Node return access T is
     begin
         return new T;
     end New_Node;

     type List is access all T;
     My_List : List;
     Another_List : access T;

     procedure Yuck is
     begin
         if ...
            My_List := List(New_Node);
         else
            Another_List := New_Node;
         end if;
     end Yuck;

With the rules as they are, this will work as expected. With the "level of
the call" rule, this will have fail an accessibility check, because List is
library-level, and surely the level of the call is nested in Yuck. (And the
same is true for the accessibility level of the anonymous type of
Another_List.)

The net effect is that the correct pool for an access return depends on how
it is going to be used: it's not the call that matters, it is how the result
of the call is "consumed". I'm not sure that there is much point to a rule
with complex dynamic semantics unless it can properly handle simple cases
like these.

I could imagine trying to factor the accessibility of the target type into
this, but that just seems too complex to me.

The net is that I don't think we should even be allowing allocators for
access results, because we have no idea what the appropriate pool is (it
should probably be that of List for the first call). In the access
discriminant case, the (ultimate) caller's level seems to be the right
answer (in that the object has to be declared there), but that doesn't make
sense for access results.

It makes sense to be consistent, but only as long as the result makes sense.
Allocators for access results don't seem to make sense. (One could argue
that the entire access result thing is an attempt to be overly consistent,
but it seems useful to replace the misguided return-by-reference -- but we
don't need allocators to do that).

...
> > I think that users would find this surprising.
> > I believe that this example should execute without
> > raising an exception.
>
> Clearly this is a matter of expectation.  Programmers
> will not be surprised if they understand the general
> rule that anonymous allocators in return statements
> use the caller's storage pool.  That makes sense since
> the primary user of the result is the caller.  If
> the programmer wants to squirrel away a copy of something
> before it is returned, they clearly have a special
> case and will have to use special care to avoid
> dangling references.

As I showed above, the caller can't squirrel it away, either (with your
proposed model), so when/how can they do it????

The fact is that your proposed rules are next to useless, because they
almost never do what the user really wants. Indeed, *any* rules that try to
guess the user intent for access results are going to fail, because there
are so many things that could be usefully done. It makes more sense to
*make* the user say what pool they want (by using a named access type) for
an allocator in a return statement, and that is easily accomplished by
defining the storage pool to be zero for access result types.

Why cause our heads to explode for this one???

...
> I would claim that isn't really the heart of the
> problem for anonymous allocators.  There it is that the
> caller uses the result of the anonymous allocator
> in a particular context, and it is not helpful
> to allocate in a storage pool over which the
> caller has no control.

Absolutely right. But even more than that, it doesn't make sense to allocate
in the *wrong* storage pool, either: only the programmer knows what they
want, and the language should not be in the business of guessing what it is.
Make them write it out.

> Having anonymous allocators defining all or part
> of a return value is new with Ada 2005.  It seems
> quite helpful if all such anonymous allocators are
> treated similarly, whether they are used to define
> the whole return value, or an access discriminant
> of one.

And a horrible mistake, at that. Instead of getting rid of the mistake,
we've made it 10 times worse. Grrrr.

P.S. I'd suggest making all allocators for anonymous access types illegal,
too, but I think people are getting tired of hearing that. ;-) [They
logically leak, and fooling with the rules to "define" that away does
nothing to make the language more consistent or usable. Our implementation
leaks for all such allocators, and no one has ever complained about that...]

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

From: Tucker Taft
Sent: Wednesday, November 28, 2007 10:36 PM

I don't have time to give a full response at
this moment, but when I say it uses
caller "context" to determine the storage pool,
I mean it does something analogous to what
we have defined to happen for functions that
return limited types.  That is, the level is
not that of the function call, but rather
a level determined by how the value is used.
Effectively for an anonymous access type,
the level/storage pool used for the anonymous
allocator in the return statement is determined by the
level/storage pool of the type to which the
result is converted at the call site.

I gave more details in my original proposal of 11/6,
I believe.

More later...

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

From: Randy Brukardt
Sent: Wednesday, November 28, 2007 11:33 PM

...
> I don't have time to give a full response at
> this moment, but when I say it uses
> caller "context" to determine the storage pool,
> I mean it does something analogous to what
> we have defined to happen for functions that
> return limited types.

Riiiggghhhttt...

But that sort of definition doesn't take assignment into account (it's not
necessary for limited return types), and adding that complicates things a
lot. (And they're already too complicated.)

Your note of 11/6 says that access return types would be treated like they
were the discriminants of an anonymous and invisible wrapper object. But
that is precisely the model that I was objecting to, because while there is
no obvious problem with access discriminants (since they can't be assigned
into an existing object), that model doesn't work with bare access types
(since they can be assigned, and usually would be).

I'll wait for your full rebuttal before writing more. (Well, at least I'll
try... ;-)

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

Questions? Ask the ACAA Technical Agent