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

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

--- ai05s/ai05-0051-1.txt	2008/01/29 04:02:04	1.5
+++ ai05s/ai05-0051-1.txt	2008/02/05 05:27:02	1.6
@@ -1,4 +1,4 @@
-!standard 6.5(21/2)                              08-01-27    AI05-0051-1/04
+!standard 6.5(21/2)                              08-02-03    AI05-0051-1/05
 !class binding interpretation 07-05-04
 !status work item 07-05-04
 !status received 07-04-19
@@ -121,16 +121,18 @@
       as that of the level of the master that elaborated the function
       body.
 
-Modify 6.5(8/2) as follows:
+Modify 6.5(8/2) as follows (The original text includes the wording changes
+from AI05-0024-1 and AI05-0032-1):
 
     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.
+    is class-wide, the tag of the return object is that the type of the
+    subtype_indication if it is specific, or otherwise that of of the
+    value of the expression. A check is made that the master of the type
+    identified by the tag of the result includes the elaboration of the
+    master [that elaborated the function body] {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:
 
@@ -2003,5 +2005,509 @@
        Do_Something (new Lim_T'(Blurfo)); -- OK.
        Do_Something (Blah); -- Runtime error. Yuck.
     end Nested;
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Monday, January 28, 2008  10:27 PM
+
+...
+> 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.
+
+Actually, I meant what I wrote.  I'm using the phrase
+"be in place of" in its sense of "substitute."  I think
+it is grammatically correct to use the subjunctive
+"as though A were in place of B" in the sense of
+"as though A were substituted for B."  Perhaps
+I should have used the word "substituted."
+
+
+> ---
+> 
+> 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).
+
+The change in 6.5(8/2) deals with a special case
+having to do with accept statements.  I'm not
+too worried if code inside of accept statements
+that fiddles around with accessibility levels
+is a bit more expensive.  Do you think there is
+some distributed overhead as well?  I think the
+check will generally be a simple level comparison,
+similar to the one currently used with access
+parameters.
+
+...
+> But it depends on the type conversion being at the point of the call (which
+> is why I call it a hack).
+
+I think you are mistaken (see below).
+
+> ... 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.
+
+These both fail the accessibility check associated
+with the type conversion, because they are both
+allocated at a level associated with the innermost
+master of the call, which in this case is the
+procedure call statement.  That is more nested than
+Lim_T_Ptr.
+
+>     procedure Nested is
+>     begin
+>        Do_Something (new Lim_T'(Blurfo)); -- OK.
+>        Do_Something (Blah); -- Runtime error. Yuck.
+
+These also both fail.
+
+>     end Nested;
+
+*****************************************************************
+
+From: Randy Brukardt
+Sent: Monday, January 28, 2008  10:54 PM
+
+...
+> Actually, I meant what I wrote.  I'm using the phrase
+> "be in place of" in its sense of "substitute."  I think
+> it is grammatically correct to use the subjunctive
+> "as though A were in place of B" in the sense of
+> "as though A were substituted for B."  Perhaps
+> I should have used the word "substituted."
+
+I think "substituted" would be better. "in place of" is much too close
+to "at the place of". But I still think "given at the place of" is best,
+because I don't think "substitute" quite reflects what is going on
+(especially in the case of access discriminants, where you surely aren't
+substituting the whole call with just one of the discriminants).
+
+> > ---
+> >
+> > 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).
+>
+> The change in 6.5(8/2) deals with a special case
+> having to do with accept statements.  I'm not
+> too worried if code inside of accept statements
+> that fiddles around with accessibility levels
+> is a bit more expensive.  Do you think there is
+> some distributed overhead as well?  I think the
+> check will generally be a simple level comparison,
+> similar to the one currently used with access
+> parameters.
+
+I do think that there is some distributed overhead that comes from this
+rule. With the old rule, most of the accessibility checks for return
+statements can be done at compile-time, so there is no check code at all.
+With this rule, all functions need check code. And that check code cannot be
+a "simple level comparison", because that would allow bad stuff (the accept
+cases we've talked about). A function cannot generate code assuming how it
+will be called!
+
+...
+> > ... 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.
+>
+> These both fail the accessibility check associated
+> with the type conversion, because they are both
+> allocated at a level associated with the innermost
+> master of the call, which in this case is the
+> procedure call statement.  That is more nested than
+> Lim_T_Ptr.
+
+Yuck! Again, an anonymous access type does the wrong thing. Is there any
+case where accessibility and/or anonymous access types actually do something
+useful in Ada?? (No, don't answer that. ;-)
+
+<start gripe>
+Anonymous access types are one of the worst ideas ever put into Ada. All of
+bizarre rules for accessibility, coextensions, and finalization issues --
+and there is no sign that we're remotely close to getting these "right". And
+then they're not really intuitive. All of this crap because we were
+unwilling to have in out parameters on functions. Bleeh!
+<end gripe>
+
+*****************************************************************
+
+From: Tucker Taft
+Sent: Monday, January 28, 2008  11:34 PM
+
+> I do think that there is some distributed overhead that comes from this
+> rule. With the old rule, most of the accessibility checks for return
+> statements can be done at compile-time, so there is no check code at all.
+> With this rule, all functions need check code. And that check code cannot be
+> a "simple level comparison", because that would allow bad stuff (the accept
+> cases we've talked about). A function cannot generate code assuming how it
+> will be called!
+
+I'll have to restudy AI05-0024-1, but I don't think most code
+needs to worry about it at all, unless it is *lexically* nested
+inside of a task body.  Are you implying that any old function
+that gets an access parameter can't use a simple level
+check to see whether it can convert it to a named access
+type, presuming it is not lexically inside a task body?
+That surprises me, to say the least.
+
+*****************************************************************
+
+From: Randy Brukardt
+Sent: Monday, January 28, 2008  11:59 PM
+
+Umm, 6.5(8/2) is the *class-wide* accessibility check, not the access
+accessibility check. Although it looks like the access check gets
+involved too. And it's pretty obvious that you can trigger the problem
+in a function.
+
+Here's the original example, suitably changed:
+
+  declare
+    type T1 is tagged null record;
+
+    function Something (Default : in T1'Class) return access T1'Class is
+    begin
+        ...
+        return new T1'Class(Default);
+    end Something;
+
+    task T is
+      entry E (X : T1'Class);
+    end T;
+
+    task body T is
+      type Ref2 is access T1'Class;
+      R2 : Ref2;
+    begin
+      accept E (X : T1'Class) do
+        R2 := new T1'Class'(X); -- Original problem case.
+        R2 := Ref2(Something (X)); -- New problem case.
+      end;
+      -- Delay for a while and then do nasty things with R2.
+    end;
+
+    procedure Proc is
+      type T2 is new T1 with null record;
+      X2 : T2;
+    begin
+      T.E (X2);
+    end;
+
+  begin
+    Proc;
+  end;
+
+In the accept statement, X is passed in and the complex check is needed
+for the direct allocator (otherwise we get trouble because the type has
+gone before the object).
+
+By your proposed rules, the call to Something has the same accessibility
+as Ref2, and thus the allocator inside of Something needs the same sort
+of complex check as the allocator directly inside of the accept statement.
+
+Notice that the function and the task could declared far apart. Indeed,
+the function has to be capable of handling the case, even if there is
+no task in existence. Moreover, unless you are willing to peek into
+the body, the function has to have the complex check (and the complex
+data passed in to support it) even if it doesn't return the classwide object.
+
+A similar check is needed for a class-wide object return (in this case, if
+Something returned Obj directly).
+
+I'm not sure if there is any way to trigger this problem without a tagged
+parameter. If not, I suppose it would be possible to have two kinds of
+checks, and only use the complex one if the profile includes a tagged
+parameter and returns a class-wide type or tagged type.
+
+But this sure sounds distributed to me...
+
+*****************************************************************
+
+From: Randy Brukardt
+Sent: Tuesday, January 29, 2008  12:09 AM
+
+> I'll have to restudy AI05-0024-1, but I don't think most code
+> needs to worry about it at all, unless it is *lexically* nested
+> inside of a task body.
+
+Under your new rule, it only matters where the *call* is lexically
+nested.
+
+I suppose you could generate two bodies for every affected function,
+one with a simple check and one with the complex check. That would
+eliminate the overhead other than in the bad calls. But if you're
+seriously suggesting that, I'd have to suggest a check of your
+sanity...
+
+*****************************************************************
+
+From: Randy Brukardt
+Sent: Tuesday, January 29, 2008  12:14 AM
+
+(Sorry, I keep hitting "Send" and then thinking of something else I should have said...)
+
+...
+> Are you implying that any old function
+> that gets an access parameter can't use a simple level
+> check to see whether it can convert it to a named access
+> type, presuming it is not lexically inside a task body?
+
+Possibly, since you can call a function from inside aof an accept statement.
+If Something had been declared:
+    function Something (Ptr : access T1'Class) return access T1'Class is
+    begin
+        return Ptr;
+    end Something;
+
+and the call was
+
+        R2 := Ref2(Something (X'Access)); -- Still gotta problem.
+
+But I think the parameter has to be access-to-tagged to cause the problem,
+so it isn't *every* function.
+
+Note that the class-wide check has a similar issue:
+    function Something (Ptr : not null access T1'Class) return T1'Class is
+    begin
+        return Ptr.all;
+    end Something;
+
+*****************************************************************
+
+From: Tucker Taft
+Sent: Tuesday, January 29, 2008  12:33 AM
+
+One way to minimize the distributed overhead
+would be to only do the more expensive check
+if the simple level-based check *fails.*
+Presumably the passed-in level can be chosen
+so that it will be quite rare that the expensive
+check will pass while the cheap check will fail.
+And the more expensive check seems to be relevant
+only when checking a class-wide type.
+
+*****************************************************************
+
+From: Randy Brukardt
+Sent: Tuesday, January 29, 2008  12:49 AM
+
+I suppose, but I would expect that the primary extra cost would simply be
+the need to pass in the more complex data needed to do the complex check.
+(And to pass it along with access parameters.) Most of the time, even the
+expensive check will succeed very quickly (most things are library level,
+after all, or nested a single level). So I'm not sure it is worth it to
+duplicate that code.
+
+But surely there is a distributed space overhead, because of the more
+complex checks and the more complex data to support them.
+
+*****************************************************************
+
+From: Tucker Taft
+Sent: Tuesday, January 29, 2008  7:55 AM
+
+...
+> Possibly, since you can call a function from inside aof an accept statement.
+> If Something had been declared:
+>     function Something (Ptr : access T1'Class) return access T1'Class is
+>     begin
+>         return Ptr;
+>     end Something;
+> 
+> and the call was
+> 
+>         R2 := Ref2(Something (X'Access)); -- Still gotta problem.
+
+I don't think so, since access parameters can't be passed
+via an entry call, so you never get two different stacks
+involved in the accessibility of an access parameter.
+So I believe you never need more than a simple level for
+an access parameter.
+
+*****************************************************************
+
+From: Tucker Taft
+Sent: Tuesday, January 29, 2008  7:58 AM
+
+I think your access parameter example is
+incorrect.  I believe the only time you
+get two different stacks involved in
+accessibility is a check on the level associated
+with a tag, and then presumably the information
+you need is carried with the tag or with the
+allocation context you need to pass for these
+kinds of functions anyway.
+
+*****************************************************************
+
+From: Randy Brukardt
+Sent: Tuesday, January 29, 2008  1:18 PM
+
+> I don't think so, since access parameters can't be passed
+> via an entry call, so you never get two different stacks
+> involved in the accessibility of an access parameter.
+> So I believe you never need more than a simple level for
+> an access parameter.
+
+Humm, I think you're wrong. The two stacks get involved because I took the
+'Access of an item on a different stack. Perhaps it's easier to see in a
+complete example:
+
+  declare
+    type T1 is tagged null record;
+
+    function Something (Ptr : access T1'Class) return access T1'Class is
+    begin
+        return Ptr;
+    end Something;
+
+    task T is
+      entry E (X : T1'Class);
+    end T;
+
+    task body T is
+      type Ref2 is access T1'Class;
+      R2 : Ref2;
+    begin
+      accept E (X : T1'Class) do
+        R2 := new T1'Class'(X); -- Original problem case.
+        R2 := Ref2(Something (X'Access)); -- New problem case.
+      end;
+      -- Delay for a while and then do nasty things with R2.
+    end;
+
+    procedure Proc is
+      type T2 is new T1 with null record;
+      X2 : T2;
+    begin
+      T.E (X2);
+    end;
+
+  begin
+    Proc;
+  end;
+
+Since the accessibility of the anonymous return subtype comes from the call
+in your new rule, you have to be able to make the full check. And the
+accessibility of anonymous access parameters come from the actual object. So
+it looks like we have the full complexity here.
+
+*****************************************************************
+
+From: Tucker Taft
+Sent: Tuesday, January 29, 2008  12:10 PM
+
+When you take 'Access of a formal parameter of a
+tagged type, it is treated like a local variable.
+Hence, in your example the accessibility passed
+via the access parameter associated with "X'Access"
+is guaranteed to be on the same stack as the task
+itself, and can't come from the entry caller's
+stack.  See below.
+
+...
+> Humm, I think you're wrong. The two stacks get involved because I took the
+> 'Access of an item on a different stack. 
+
+You took 'Access of a formal parameter, and that is always
+treated like a local variable (since "normal" formals
+don't carry along their own accessibility level from
+the actual -- only access parameters do, and we don't
+allow those).
+
+> ... Perhaps it's easier to see in a
+> complete example:
+> 
+>   declare
+>     type T1 is tagged null record;
+> 
+>     function Something (Ptr : access T1'Class) return access T1'Class is
+>     begin
+>         return Ptr;
+>     end Something;
+> 
+>     task T is
+>       entry E (X : T1'Class);
+>     end T;
+> 
+>     task body T is
+>       type Ref2 is access T1'Class;
+>       R2 : Ref2;
+>     begin
+>       accept E (X : T1'Class) do
+>         R2 := new T1'Class'(X); -- Original problem case.
+>         R2 := Ref2(Something (X'Access)); -- New problem case.
+>       end;
+>       -- Delay for a while and then do nasty things with R2.
+>     end;
+> 
+>     procedure Proc is
+>       type T2 is new T1 with null record;
+>       X2 : T2;
+>     begin
+>       T.E (X2);
+>     end;
+> 
+>   begin
+>     Proc;
+>   end;
+> 
+> Since the accessibility of the anonymous return subtype comes from the call
+> in your new rule, you have to be able to make the full check. And the
+> accessibility of anonymous access parameters come from the actual object. 
+
+True, but the actual object (X) in this case is a formal parameter,
+which is treated like a local variable.  It is true that the
+accessibility associated with the tag of T2 has the "full complexity,"
+but there are never two stacks involved when passing the accessibility
+level of an access parameter.
+
+> ... So
+> it looks like we have the full complexity here.
+
+Access parameters never have the "full" complexity,
+thanks to the fact that we don't allow them in
+entry calls.
+
+*****************************************************************
+
+From: Tucker Taft
+Sent: Monday, February 4, 2008  5:06 PM
+
+Randy pointed out that we had already revised 6.5(8/2),
+which this AI also touches.  So this version [/05 - ED] is based
+on the latest 6.5(8/2), but is otherwise identical
+to version 4 sent earlier.
 
 *****************************************************************

Questions? Ask the ACAA Technical Agent