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

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

--- ai05s/ai05-0051-1.txt	2007/11/07 06:32:42	1.3
+++ ai05s/ai05-0051-1.txt	2007/12/13 04:39:36	1.4
@@ -1209,3 +1209,607 @@
       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