CVS difference for ai05s/ai05-0051-1.txt

Differences between 1.4 and version 1.5
Log of other versions for file ai05s/ai05-0051-1.txt

--- ai05s/ai05-0051-1.txt	2007/12/13 04:39:36	1.4
+++ ai05s/ai05-0051-1.txt	2008/01/29 04:02:04	1.5
@@ -1,4 +1,4 @@
-!standard 6.5(21/2)                              07-10-31    AI05-0051-1/03
+!standard 6.5(21/2)                              08-01-27    AI05-0051-1/04
 !class binding interpretation 07-05-04
 !status work item 07-05-04
 !status received 07-04-19
@@ -20,34 +20,72 @@
 
 !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.
+We propose that in all cases where accessibility checks are currently
+required in return statements, they be based on an accessibility level
+passed in from the caller, rather than on the accessibility level of the
+master that elaborated the function body.  This eliminates the problem
+with dispatching calls, since the passed-in level is unaffected by
+whether the call is a dispatching call to a primitive of a type declared
+at a more nested level than the function named in the call. 
+
+Currently, almost all cases where an accessibility check is performed as
+part of a return statement, the caller already needs to pass in
+information because the returned object might need to be created in a
+place or storage pool determined by the context of the call, or might
+need finalization at a point determined by the caller.
+
+The one place where the current rules do not require some caller context
+is where a function has an access result. However, the current rule for
+handling access results causes an anonymous allocator to almost always
+lead to a storage leak. We propose to change the rules for what storage
+pool should be used in such an anonymous allocator, to minimize the
+likelihood of creating a storage leak.  As a side-effect, we also solve
+the problem with dispatching calls to functions with access results,
+and make all of these cases more consistent with one another.
+Essentially 3.10.2(14-14.3) will be adjusted so that anonymous
+allocators in return expressions are treated as they would be if they
+were substituted into the call site.
+
+Note that we define the static accessibility level to be associated with a
+passed-in accessibility level to be the same as that of the master that
+elaborated the function body, which preserves the same legality rules
+already present in Ada 2005.  Only the dynamic checks are changed to
+overcome the problem with dispatching calls to overridings of nested 
+extensions.
 
 !wording
 
-Add after 3.10.2(10.1):
+Modify 3.10.2(14/2) as follows:
 
-    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.]
-    
+    The accessibility level of an object created by an allocator is the
+    same as that of the access type, except for an allocator of an
+    anonymous access type {(an "anonymous allocator") in certain
+    contexts} [that defines the value of an access parameter or an
+    access discriminant]. {In particular, for an anonymous allocator
+    that defines the result of a function with an access result, the
+    accessibility level is determined as though the allocator were in
+    place of the call of the function.  (If the call is the operand of a
+    type conversion, the level is that of the target access type of the
+    conversion.)}   For an {anonymous} allocator defining the value of
+    an access parameter, the accessibility level is that of the
+    innermost master of the call. For one defining an access
+    discriminant, the accessibility level is determined as follows:
+
+Add after 3.10.2(14.4/2):
+
+    * Within a return statement, the accessibility level of the 
+      anonymous access type of an access result is determined by the
+      point of call.  If the call is the operand of an explicit type
+      conversion, the accessibility level is that of the target access
+      type of the conversion.  If the call is an actual parameter of
+      another call, the accessibility level is that of the innermost
+      master of the call.  If the call defines an access discriminant,
+      the level is the same as that given above for an object created by
+      an anonymous allocator that defines an access discriminant (even
+      if the access result is of an access-to-subprogram type). If the
+      call itself defines the result of a function with an access
+      result, this rule is applied recursively.
+
 Modify 3.10.2(15) as follows:
 
     The accessibility level of a view of an object or subprogram
@@ -58,7 +96,7 @@
     level of the view of the function denoted by the dereference is
     that of the master containing the access parameter}.
     
-  AARM NOTE: 
+  {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
@@ -72,35 +110,38 @@
       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.
+      of that master.}
+      
+Add after 3.10.2(19/2):
 
-Modify 6.5(8/2) as follows:
+    * For determining within a return statement applying to a function
+      with an access result, whether a level is statically deeper than
+      the level of the anonymous access type of the access result, the
+      level determined by the point of call is presumed to be the same
+      as that of the level of the master that elaborated the function
+      body.
 
-    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(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] {the level of the
+    return object as determined by the point of call (see 3.10.2)}. 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.
-    
+    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] {the level of the return object as determined by the
+    point of call (see 3.10.2)}. If this check fails, Program_Error is
+    raised. 
     
 Modify 7.6.1(11/2) as follows:
 
@@ -120,34 +161,53 @@
     objects finalized as part of its finalization cease to exist, as
     do any types and subtypes defined and created within the master.
     
+Add the following after 13.11(25.2/2):
+
+   * If the allocator defines the result of a function with an
+     access result, the storage pool is determined as though the
+     allocator were in place of the call of the function.  If the call
+     is the operand of a type conversion, the storage pool is that of
+     the target access type of the conversion. If the call is itself
+     defining the result of a function with an access result, this rule
+     is applied recursively.
+        
 !discussion
 
-In most cases, the dynamic semantics of access result types follow easily
-from an informal equivalence rule. The declaration
+In many cases, the original formulation the semantics rules
+for access result types followed from an informal equivalence 
+rule. The declaration
 
     function Foo ( ...) return access T;
 
-is pretty much equivalent to
+was 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?
+scope than its parent type, then this equivalence breaks down because the
+caller and the callee see different access types, at different accessibility
+levels.
+
+Also, if such a function returns an allocator, then what is the lifetime
+and accessibility level of the allocated object, both in the normal
+case, and in the case where the function has been invoked via a
+dispatching call to the operation of the parent type?  It is important
+that the object live at least as long as that implied by the
+accessibility level associated with the function result.  We would also
+prefer that the storage be allocated from a pool that will minimize
+storage leaks. This AI proposes that we use a simple substitution
+rule to determine both the accessibility level and the best storage 
+pool to use.
+
+The accessibility issue for a function with an access result
+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.
 
-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:
 
@@ -178,7 +238,7 @@
        return (Disc => X'Access);
     end F3;
 
-Each of these three scenarios becomes problematic if the function
+Each of these three scenarios becomes more interesting 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.
@@ -204,11 +264,33 @@
       begin
         <Call X.F1, X.F2, and X.F3>
         
+        -- Each call returns a reference to an object or a type that
+        -- is more nested than Pkg_1.  Hence, it is important that
+        -- the caller and callee agree about what accessibility level
+        -- to presume for the returned object or designated object,
+        -- so we don't end up with a dangling reference.
         --
-        -- Each call should fail, right?
-        -- Otherwise we've got the potential for dangling references.
+        -- For X.F4, we also need to determine the storage pool for the
+        -- anonymous allocator in the return statement.  For F2 and
+        -- F3, if the calling context is an initialized allocator, we
+        -- can avoid copying if we have the return object allocated
+        -- directly in the desired storage pool.  This is required for
+        -- F3 because it is limited, and is allowed for F2 for efficiency.
         --
-        -- The situation with X.F4 is even less clear.
+        -- If such an initialized allocator is for an access type
+        -- declared outside Call_Functions, we need to be sure that 
+        -- some accessibility check prevents the allocator from succeeding.
+        -- 
+        -- The result of calls on F1 or F4 might be converted to an access
+        -- type declared outside of Call_Functions.  Again, some accessiblity
+        -- check must prevent this.
+        --
+        -- By passing in an accessibility level in all these cases,
+        -- as well as a storage pool or equivalent, the called function
+        -- can do the appropriate accessibility check, and allocate 
+        -- from the appropriate storage pool to minimize the chance
+        -- of a storage leak.
+ 
        end Call_Functions;
       
       procedure Nested is
@@ -246,60 +328,9 @@
     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;
+This AI also addresses two somewhat independent issues relating to
+accessibility and anonymous allocators:
          
 For access-to-subprogram parameters, which have by definition an
 infinitely deep accessibility level, we specify (in 3.10.2(15)) that
@@ -311,12 +342,6 @@
 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
@@ -327,18 +352,103 @@
 storage pool object gets finalized, but there is no similar
 consideration for non-coextension anonymous allocations, since they
 always use the "default" storage pool.
+
+BACKGROUND ON THIS APPROACH
+
+We had earlier considered a rule where the accessibility
+level of the controlling tag used in the call would
+be used at the call site to determine the actual
+level of the returned object.  However, after
+trying to work out the details for that, we gave
+up.  Furthermore, it implied a check both in the
+return statement and then another check after return
+on use.  That seemed inefficient.  Finally, we realized
+that in essentially all of these cases, we already
+need to pass in some kind of caller context because
+the return object might not allow copying or might
+require finalization, and the caller is determining
+where the return object is to be located and/or
+what master it is to be associated with for
+finalization purposes.
+
+Hence the proposed solution is now to change the dynamic
+accessibility-level check at the return statement 
+to be based on an accessibility level passed in from
+the caller, rather than basing it on the level of
+the master that elaborated the function body.
+Using the level of the master is too optimistic
+for nested overrides, and is too pessimistic in 
+many other cases.  We chose to leave the static legality
+checks the same.
+
+After solving this problem for returning class-wide
+objects and objects with access discriminants, we went
+on to propose essentially a similar solution for
+functions with access results.  Namely, the "dynamic"
+accessibility level for access results would be
+determined by the caller rather than being determined
+by the master that elaborated the function body.
+This solves the problem with nested overrides,
+makes access results work more consistently with
+access discriminants and functions that return limited
+types, and it minimizes storage leaks.  It effectively 
+means that whether an initialized allocator is at the 
+call site using the result of the function as the initial
+value, or the initialized allocator is at the return
+statement of a function with an access result,
+and the caller merely converts it to the desired
+type, the net effect is the same.  
+
+EXAMPLES
+
+Here is an example.  In the following, the 
+initialization of X and the initialization of Y 
+produce essentially the same thing, namely a pointer
+to a heap object allocated from the storage pool 
+associated with Lim_T_Ptr:
+
+     function Blurfo return Lim_T is ...
+
+     function Blah return access Lim_T is
+     begin
+         return new Lim_T'(Blurfo);
+     end Blah;
+
+     type Lim_T_Ptr is access Lim_T;
+
+     X : Lim_T_Ptr := new Lim_T'(Blurfo);
+     Y : Lim_T_Ptr := Lim_T_Ptr(Blah);
 
-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. 
+Another useful feature of having the caller specify
+the dynamic accessibility level to check occurs when
+the function returns a reference to a component of 
+an object denoted by an access parameter.  E.g.:
 
+    type Rec is record
+       Comp : aliased Comp_Type;
+    end record;
+
+    function Get_Comp(R : access Rec) return access Comp_Type is
+    begin
+        return R.Comp'Access;  -- (1)
+          -- Run-time check based on level passed in from caller
+    end Get_Comp;
+
+    procedure Process(G : access Comp_Type) is ...
+
+    procedure Test is
+        Local : aliased Rec;
+    begin
+        Process(Get_Comp(Local'Access));
+    end Test;
+
+The above is perfectly safe and reasonable, but would
+fail if the dynamic check at (1) were against the level of
+the master that elaborated Get_Comp.
+
 !ACATS test
 
+C-Tests need to be constructed to check this semantics.
 
 !appendix
 
@@ -1621,7 +1731,7 @@
 
 ****************************************************************
 
-From: Tucker Taft
+From: Randy Brukardt
 Sent: Wednesday, November 28, 2007  9:40 PM
 
 > > In general, I feel that this proposal adds
@@ -1811,5 +1921,87 @@
 
 I'll wait for your full rebuttal before writing more. (Well, at least I'll
 try... ;-)
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Monday, January 28, 2008  9:37 PM
+
+Tucker wrote:
+> Here is another attempt at AI-51 [this is version /04 - ED],
+> addressing Steve's issue with what happens when a function
+> that has an accessibility check upon return
+> based on its level, is reached via a dispatching
+> call through a name that denotes a primitive
+> declared at an outer level.  The problem is that
+> the caller and the callee don't agree about
+> the accessibility level.
+
+Your wording has (in several places):
+
+"...is determined as though the allocator were in place of the call of the function..."
+
+"were in place of" doesn't make much sense to me. I know that you mean "...as though
+the allocator were given at the place of the call..." -- so why didn't you say
+that? I think it's a lot clearer.
+
+---
+
+The wording change of 6.5(8/2) doesn't take AI05-0024-1 into account. That's OK,
+other than that it means that a simple level comparision will not do for this
+check. That means that the check will be quite expensive - and that means that
+something that looks cheap (anonymous return types) will be anything but cheap.
+Since the level will be passed in, there won't be any practical way (short of
+inlining) to eliminate this check (unlike the current semantics).
+
+---
+
+You said in your discussion:
+
+> Here is an example.  In the following, the
+> initialization of X and the initialization of Y
+> produce essentially the same thing, namely a pointer
+> to a heap object allocated from the storage pool
+> associated with Lim_T_Ptr:
+> 
+>       function Blurfo return Lim_T is ...
+> 
+>       function Blah return access Lim_T is
+>       begin
+>           return new Lim_T'(Blurfo);
+>       end Blah;
+> 
+>       type Lim_T_Ptr is access Lim_T;
+> 
+>       X : Lim_T_Ptr := new Lim_T'(Blurfo);
+>       Y : Lim_T_Ptr := Lim_T_Ptr(Blah);
+
+You didn't mention it, but you have a hack so this works when nested. For instance:
+
+    procedure Something is
+       X : Lim_T_Ptr := new Lim_T'(Blurfo); -- OK
+       Y : Lim_T_Ptr := Lim_T_Ptr(Blah); -- OK, because the level is taken from the
+                                         -- type conversion, not the call.
+    begin
+       null;
+    end Something;
+
+But it depends on the type conversion being at the point of the call (which is
+why I call it a hack). That isn't necessarily true. Consider:
+
+    procedure Do_Something (A : access Lim_T) is
+        X : Lim_T_Ptr := Lim_T_Ptr(A);
+    begin
+        ...
+    end Do_Something;
+
+    Do_Something (new Lim_T'(Blurfo)); -- OK.
+    Do_Something (Blah); -- OK.
+
+    procedure Nested is
+    begin
+       Do_Something (new Lim_T'(Blurfo)); -- OK.
+       Do_Something (Blah); -- Runtime error. Yuck.
+    end Nested;
 
 *****************************************************************

Questions? Ask the ACAA Technical Agent