CVS difference for ais/ai-00228.txt

Differences between 1.9 and version 1.10
Log of other versions for file ais/ai-00228.txt

--- ais/ai-00228.txt	2003/06/28 00:33:28	1.9
+++ ais/ai-00228.txt	2004/04/30 02:35:38	1.10
@@ -701,3 +701,293 @@
 
 *************************************************************
 
+!topic Derived types, private parts, abstract subprograms
+!reference RM95 3.9.3(6-7), 7.3.1(6), 8.3(12)
+!from Adam Beneschan 04-03-09
+!discussion
+
+
+This is from comp.lang.ada.  Robert Eachus recommended copying it to
+Ada-Comment.
+
+Following is my original message:
+
+===============================================================================
+We believe we have found an error in some Ada source code that is
+publicly available on the Internet, that isn't supposed to compile but
+that some compilers apparently let slip through.  However, I wanted to
+ask to make sure my interpretation of the RM is correct.
+
+package pak1 is
+   type T1 is abstract tagged null record;
+   procedure Operation (X : T1) is abstract;
+end pak1;
+
+with pak1;
+package pak2 is
+   type T2 is new pak1.T1 with null record;
+private
+   procedure Operation (X : T2);
+end pak2;
+
+package pak3 is
+   procedure Foo;
+end pak3;
+
+with pak2;
+package body pak3 is
+   type T3 is new pak2.T2 with null record;
+
+   procedure Foo is
+       X : T3;
+   begin
+      Operation (X);      --legal?
+   end Foo;
+
+end pak3;
+
+
+As I interpret the RM: When T2 is declared, Operaton is inherited, but
+the inherited subprogram is still abstract.  3.9.3(6) says that
+Operation must be overridden with a nonabstract procedure, but that
+the overriding may be done in the private part, which is what's done
+here.
+
+When T3 is declared, T2's primitive subprograms are inherited.  As I
+understand it, this includes primitive subprograms of T2 that are
+overridden, since those would reemerge in places where the overriding
+subprogram isn't visible.  Is this correct in general for derived
+types?  Thus, it would seem that T3 would inherit both the abstract
+and the nonabstract versions of Operation from T2; but by 7.3.1(6),
+the nonabstract version is not visible in places where pak2's private
+part is not visible.  In this instance, the nonabstract inherited
+version of Operation that operates on T3 is, in the language of
+7.3.1(6), "not declared at all" and thus "cannot be named in a call"
+(but still could be called indirectly with a dispatching call).  This
+would seem to mean that the call with the "--legal?" comment cannot
+name the nonabstract inherited Operation; however, the *abstract*
+inherited Operation is still visible (reemerges?), and thus Operation
+in this statement names this subprogram, but this makes the call
+illegal because it's a nondispatching call on an abstract subprogram
+(3.9.3(7)).
+
+Is this all correct?
+
+Is this the intent of the language?  It seems like it should be,
+because declaring pak2.Operation in the private part of pak2 should
+mean that you can't call it directly when the private part of pak2
+isn't visible, even via a derived type.  But I want to make sure there
+isn't any subtlety I've missed before I make a bug report.
+
+Thanks in advance for any help,
+
+                                      -- Adam
+===============================================================================
+
+Additional comments: It seems to me that there are two possible ways
+to interpret the RM that would make the call to Operation illegal.
+One is to say that T3 inherits both the abstract and nonabstract
+overriding Operations, and that at the point with the "--legal?"
+comment only the abstract one is visible and thus cannot be called
+with a non-dispatching call.  Another is to say that only the
+nonabstract Operation exists for T3, but it is not visible at the
+point with the comment, so the statement is illegal because Operation
+is undefined.  It doesn't matter in the above example, but it would
+matter if you declare an object of type T3'Class and call Operation on
+it.  But in either case, it would be a little surprising to me if the
+call in my original example were legal, because it would mean that you
+can directly call a subprogram that's supposed to be invisible.
+
+Another comment: If indeed T3 inherits both the abstract and
+nonabstract Operations, this would violate 3.9.3(6) unless it were the
+case that the inherited nonabstract Operation overrides the inherited
+abstract Operation.  I don't have a problem with this, since the whole
+point of 3.9.3(6) is to make sure that a subprogram exists that can be
+dispatched to, and 7.3.1(6) makes it clear that even inherited
+subprograms that are not declared anywhere can be dispatched to.
+8.3(12), which makes it clear that one implicit declaration can
+override an implicit declaration, still seems to require that the
+subprogram actually be declared, while 7.3.1(6) indicates that in some
+cases (including the above example) an inherited subprogram is not
+declared at all.  So can a nonabstract subprogram that is not declared
+at all override an abstract subprogram and thus satisfy 3.9.3(6)?  I
+think it's clear that it should, but a minor wording change might be
+required.
+
+
+Robert Eachus' response:
+
+===============================================================================
+I would suggest you send a bug report to the authors of the code, plus
+report this to Ada Comment.
+
+Incidently, I think the code as written is correct.  Type T2 is not
+abstract, so types derived from it are not abstract types.  But this is
+one of those "interesting corner cases" which should at least be
+documented...
+===============================================================================
+
+My comment: I agree that types derived from T2 are not abstract
+(actually, abstractness is not an inherited property anyway), but the
+question is whether the way things are declared makes "Operation" on
+T3 invisible and thus unusable by pak3.
+
+
+Christoph Grein's response:
+
+===============================================================================
+I amended your example a bit.
+
+> package pak1 is
+>    type T1 is abstract tagged null record;
+>    procedure Operation (X : T1) is abstract;
+> end pak1;
+>
+> with pak1;
+> package pak2 is
+>    type T2 is new pak1.T1 with null record;
+> private
+>    procedure Operation (X : T2);
+> end pak2;
+
+with pak1;
+package pak2P is
+  type T2 is private;
+private
+  type T2 is new pak1.T1 with null record;
+  procedure Operation (X : T2);
+end pak2P;
+
+> package pak3 is
+>    procedure Foo;
+> end pak3;
+>
+> with pak2, pak2P;
+> package body pak3 is
+>    type T3 is new pak2.T2 with null record;
+>
+>    procedure Foo is
+>        X : T3;      Y : pak2P.T;
+>    begin
+>       Operation (X);      --legal?  (yes, because T2 is visibly derived
+                            -- from T1
+        Operation (Y);      -- illegal (not visible, because T2 is not visibly
+                            -- derived from T1
+>    end Foo;
+>
+> end pak3;
+
+My interpretation is that it is irrelevant whether inherited primitives are
+overridden visibly or not if only the type is visibly derived.
+
+It matters of course if the type is invisibly derived.
+
+package P is
+  type T is tagged ...
+  procedure Prim (X: T; I: Integer := 5);
+end P;
+with P;
+package P1 is
+  type T1 is new P.T with ...
+private
+  procedure Prim (Y: T1; I: Integer := -5);
+end P1;
+
+In this example, Prim applied to a value of type T1 uses different default
+values for I depending on whether the overriding is visible or not. Also named
+association for the first parameter has to use different names.
+And IMHO it's irrelevant whether the ancestor of Prim is abstract or not.
+
+Thus:
+
+with P1;
+procedure M is  -- cannot see the private part
+  Ob: P1.T1;
+begin
+  P1.Prim (X => Ob);  -- uses I => 5
+end M;
+
+procedure P1.M is  -- bodies of children see the private parts of parents
+  Ob: P1.T1;
+begin
+  P1.Prim (Y => Ob);  -- uses I => -5
+end P1.M;
+
+Gnat 3.16a compiles this OK and uses the correct default, alas Apex 3.2.0b
+reports an error on parameter name Y and uses the wrong default.
+===============================================================================
+
+*************************************************************
+
+From: Pascal Leroy
+Sent: Monday, March 15, 2004  7:24 AM
+
+> As I interpret the RM: When T2 is declared, Operaton is
+> inherited, but the inherited subprogram is still abstract.
+
+Nope, it "shall be overridden", which is a completely different thing.
+See the !discussion of AI 228 for (excruciating) details.  From the AI
+you can deduce that the call is legal, because none of the restrictions
+applicable to an abstract subprogram pertain to a "shall be overridden"
+one.  (Don't read the !appendix, because the ARG went back and forth on
+this one.)
+
+*************************************************************
+
+From: Adam Beneschan
+Sent: Monday, March 15, 2004  4:38 PM
+
+> Nope, it "shall be overridden", which is a completely different thing.
+> See the !discussion of AI 228 for (excruciating) details.  From the AI
+> you can deduce that the call is legal, because none of the restrictions
+> applicable to an abstract subprogram pertain to a "shall be overridden"
+> one.  (Don't read the !appendix, because the ARG went back and forth on
+> this one.)
+
+OK, thanks for pointing me to this.  This explains what I need to
+know.
+
+It turns out the biggest mistake I made was this part of my original
+message:
+
+>> When T3 is declared, T2's primitive subprograms are inherited.  As I
+>> understand it, this includes primitive subprograms of T2 that are
+>> overridden, since those would reemerge in places where the overriding
+>> subprogram isn't visible.  Is this correct in general for derived
+>> types?
+
+I had forgotten that the situation is different for tagged and
+untagged types.  For untagged types, if an inherited subprogram is
+overridden in a private part, and you call the inherited subprogram
+from a place where the private part isn't visible, you get the
+parent's subprogram because the overriding one isn't visible.  For
+tagged types, though, it still calls the body of the overriding one,
+even though it isn't visible, because of 3.9.3(20).  What I had missed
+is that 3.9.3(20) applies to a "call on a dispatching operation", and
+I mistakenly read it as applying to a "dispatching call", which is not
+the same thing.  :(
+
+*************************************************************
+
+From: Adam Beneschan
+Sent: Monday, March 15, 2004  7:38 PM
+
+I wrote:
+
+> I had forgotten that the situation is different for tagged and
+> untagged types.  For untagged types, if an inherited subprogram is
+> overridden in a private part, and you call the inherited subprogram
+> from a place where the private part isn't visible, you get the
+> parent's subprogram because the overriding one isn't visible.  For
+> tagged types, though, it still calls the body of the overriding one,
+> even though it isn't visible, because of 3.9.3(20).  What I had missed
+                                           ^^^^^^^^^
+> is that 3.9.3(20) applies to a "call on a dispatching operation", and
+          ^^^^^^^^^
+> I mistakenly read it as applying to a "dispatching call", which is not
+> the same thing.  :(
+
+I not only can't read, I can't type.  That should be 3.9.2(20).
+
+*************************************************************
+

Questions? Ask the ACAA Technical Agent