!standard 07.03 (15) 00-07-13 AI95-00157/06 !class ramification 96-09-04 !status Response 2000 00-01-24 !status WG9 approved (8-0-0) 97-07-04 !status ARG approved 12-0-0 (subject to editorial review) 96-10-07 !status work item 96-09-08 !status received 96-09-04 !reference AI95-00033 !priority High !difficulty Hard !qualifier Clarification !subject Visibility of inherited private components !summary Consider types Parent, Child, and Grandchild, derived from one another in that order. The components of Child are determined where Child is declared. If, at the place where Grandchild is declared, additional components of Parent are visible (that were not visible to Child), then Grandchild does *not* inherit them as visible components. One can convert from Grandchild to Parent in order to manipulate these components. !question Consider the following example: package P is type Parent is tagged private; private type Parent is record C: Integer; end record; end P; with P; use P; package Q is type Child is new Parent with record C: Integer; end record; end Q; with Q; use Q; package P.Child is type Grandchild is new Q.Child with null record; X: Grandchild; ... X.C ... end P.Child; What is the meaning of X.C, given that Grandchild is declared in a place where the full view of Parent is visible? !response There is a general design principle in Ada that you can never have more visibility into the components or operations of a type than in the package where the type is declared. Effectively, the components and operations of a type are "frozen" to be those visible somewhere within the "immediate" scope of the type. Even if you go into a package that knows more about the ancestors of the type, that doesn't change the set of components or primitives that the type has. So in the above example, the type Child is declared in a place where there is no visibility on the C component of Parent; hence this component is not declared, and it is legal to declare another, unrelated, C component in Child. Thus, X.C refers to the C component declared in Child. This is despite the fact that at the point of "X.C", it *is* visible that Child is derived from Parent, and it *is* visible that Parent has a component called C. To refer to the C component from Parent, one would write Parent(X).C. The C component from Parent does exist, despite the fact that it is not visible. This paradox of knowing the ancestry of a type, but not being able to take advantage of it except through explicit conversion, also applied to derived types in Ada 83. It is based on the general notion that even inherited operations and components need to have a point of declaration, and that point of declaration is required to be in the immediate scope of the derived type. If there is no place where such implicit declarations could occur, then the corresponding operations or components are not inherited. (Of course, inherited operations get declared (if at all) in the declarative region containing the type, whereas components get declared (if at all) in the declarative region of the type itself.) See 7.3.1 and AARM-7.3.1(7.a-7.r). Consider the following modified example: package P is type Parent is tagged private; private type Parent is record C: Integer; end record; end P; package P.Q is type Child is new Parent with record C: Integer; -- Illegal! end record; end P.Q; The above example is illegal, because in this case Child *does* inherit C from Parent, so the second declaration of C is an illegal homograph. !ACATS test New ACATS test B730006 checks the example noted in this ruling, and a related example. !appendix !section 7.3(15) !subject Controversial visibility of inherited private components in a child unit !reference 96-5635.a Bernard Maudry 96-8-21>> !reference RM95 7.3 (15) 7.3.1 (3) 7.3.1 (10) !from Bernard Maudry 96-08-21 !keywords visibility inherited private components child !discussion I would like to know if the following code is legal or not as 2 of the 4 compilers I use think that it is illegal. I will not try to explain you the problem as you will better understand it when reading the code below. package Top is type Node is abstract tagged private ; private type Node is abstract tagged record Name : Integer := 0; end record; end Top ; package Top.Mid is type Node is abstract new Top.Node with private ; private type Node is abstract new Top.Node with record Kind : Integer := 0; end record; end Top.Mid ; with Top.Mid; generic type Parent is abstract new Top.Mid.Node with private; package Top.Extra is type Node is abstract new Parent with null record ; end Top.Extra ; with Top.Mid; with Top.Extra; package Top.Mid.Extra is new Top.Extra (Parent => Top.Mid.Node); with Top.Mid.Extra ; package Top.Mid.Bot is type Node is new Top.Mid.Extra.Node with null record ; The_Node : Node ; private -- The legality of the two following lines of code is controversial. -- Of 4 Ada95 compilers, 2 of them think they are legal, and the 2 others -- reject them as illegal. -- Where is the truth ? Why ? Buggy_1 : Integer := The_Node.Name; Buggy_2 : Integer := The_Node.Kind; end Top.Mid.Bot ; **************************************************************** !section 7.3(15) !subject Controversial visibility of inherited private components ... !reference RM95 7.3 (15) 7.3.1 (3) 7.3.1 (10) !reference 96-5635.a Bernard Maudry 96-08-21 !from Tucker Taft 96-08-22 !keywords visibility inherited private components child !reference 96-5636.a Tucker Taft 96-8-22>> !discussion > I would like to know if the following code is legal or not as 2 of the 4 > compilers I use think that it is illegal. I will not try to explain > you the problem as you will better understand it when reading the code below. [Well, reading the code that appeared below was a bit tricky, given that it ended up as one long line. I have reformatted it below.] package Top is type Node is abstract tagged private ; private type Node is abstract tagged record Name : Integer := 0; end record; end Top ; package Top.Mid is type Node is abstract new Top.Node with private ; private type Node is abstract new Top.Node with record Kind : Integer := 0; end record; end Top.Mid ; with Top.Mid; generic type Parent is abstract new Top.Mid.Node with private; package Top.Extra is type Node is abstract new Parent with null record ; end Top.Extra ; with Top.Mid; with Top.Extra; package Top.Mid.Extra is new Top.Extra (Parent => Top.Mid.Node); with Top.Mid.Extra ; package Top.Mid.Bot is type Node is new Top.Mid.Extra.Node with null record ; The_Node : Node ; private -- The legality of the two following lines of code is controversial. -- Of 4 Ada95 compilers, 2 of them think they are legal, and the 2 others -- reject them as illegal. -- Where is the truth ? Why ? Buggy_1 : Integer := The_Node.Name; Buggy_2 : Integer := The_Node.Kind; -- NOTE: I have added the following two lines: Not_Buggy_1 : Integer := Top.Mid.Node(The_Node).Name; Not_Buggy_2 : Integer := Top.Mid.Node(The_Node).Kind; end Top.Mid.Bot ; ======================== The compilers that declare the lines Buggy_1 and Buggy_2 illegal are operating correctly. If you want visibility on Name and Kind you will have to convert the type to Top.Mid.Node, as illustrated in Not_Buggy_1 and _2. There is a general rule that you can never have more visibility into the components or operations of a type that in the package where the type is declared. Effectively, the components and operations of a type are "frozen" to be those visible somewhere within the "immediate" scope of the type. Even if you go into a package that knows more about the ancestors of the type, that doesn't change the set of components or primitives that the type has. So in the above case, the type Top.Mid.Extra.Node is declared in an instance of Top.Extra, where there is no visibility on the Name or Kind components of the immediate parent type (Top.Mid.Node), and hence these components are not inherited. This is despite the fact that it *is* visible that Top.Mid.Node is derived from Top.Node, and it *is* visble (inside the implicit private part of Top.Extra) that Top.Node has a "Name" component. If you want these components to be inherited, then the generic needs to be a child of Top.Mid. Even then, only within the private part and body of the generic itself, or within the private part or body of a child of the generic, will these extra components be visible. Alternately, as illustrated above, you can take advantage of the fact that these types are visibly derived from Top.Mid.Node, to convert to Top.Mid.Node and gain visibility on the extra components in that manner. This paradox of knowing the ancestry of a type, but not being able to take advantage of it except through explicit conversion, also applied to derived types in Ada 83. It is based on the general notion that even inherited operations and components need to have a point of declaration, and that point of declaration is required to be in the immediate scope of the derived type. If there is no place where such implicit declarations could occur, then the corresponding operations or components are not inherited. This is discussed in some depth in RM95-7.3.1, and if you have it, in AARM95-7.3.1(7.a-7.r). -Tuck **************************************************************** !section 7.3(15) !subject Controversial visibility of inherited private components in a child unit !reference RM95 7.3(15), 7.3.1(3), 7.3.1(10), 8.2(4) !reference 96-5635.a Bernard Maudry 96-8-21 !from Dan Lehman 96-08-22 !keywords visibility inherited private components child !reference 96-5639.a Dan Lehman 96-8-22>> !discussion I conclude that the example given by Bernard Maudry in raising this issue is legal, that both Name & Kind are visible components at the point where he reports some compilers (names, please?) reporting an illegality (it would be nice to know what those compilers report!). Am I missing something? I'll re-cast his example, as the original msg. *disformatted* it (NB: I'VE CHANGED THE INSTANCE NAME FROM "EXTRA" TO "LOWER", which I think will avoid adding confusion re the generic unit "Extra"): package Top is type Node is abstract tagged private ; private type Node is abstract tagged record Name : Integer := 0; end record; end Top ; package Top.Mid is type Node is abstract new Top.Node with private ; private type Node is abstract new Top.Node with record Kind : Integer := 0; end record; end Top.Mid ; with Top.Mid; generic type Parent is abstract new Top.Mid.Node with private; package Top.Extra is type Node is abstract new Parent with null record ; end Top.Extra ; with Top.Mid; with Top.Extra; package Top.Mid.LOWER is new Top.Extra (Parent => Top.Mid.Node); with Top.Mid.LOWER ; package Top.Mid.Bot is type Node is new Top.Mid.LOWER.Node with null record ; The_Node : Node ; private -- The legality of the two following lines of code is controversial. -- Of 4 Ada95 compilers, 2 of them think they are legal, -- and the 2 others reject them as illegal. -- Where is the truth ? Why ? Buggy_1 : Integer := The_Node.Name; -- LEGAL ?? (yes) Buggy_2 : Integer := The_Node.Kind; -- LEGAL ?? (yes) end Top.Mid.Bot ; I conclude that the controversial lines are legal. Were "private" removed from Top.Mid.Bot, then they'd be illegal as per 8.2(4). ---Dan Lehman -------------- * ============================================================================= **************************************************************** !section 7.3(15) !subject Controversial visibility of inherited private components ... !reference RM95 7.3 (15) 7.3.1(1) 7.3.1 (3) 7.3.1 (6) 7.3.1 (10) !reference 96-5635.a Bernard Maudry 96-08-21 !reference 96-5636.a Tucker Taft 96-8-22 !from Robert I. Eachus !keywords visibility inherited private components child !reference 96-5644.a Robert I. Eachus 96-8-23>> !discussion Tucker Taft said: > There is a general rule that you can never have more visibility > into the components or operations of a type that in the package > where the type is declared. Effectively, the components and > operations of a type are "frozen" to be those visible somewhere > within the "immediate" scope of the type. Even if you go into a > package that knows more about the ancestors of the type, that > doesn't change the set of components or primitives that the type > has. This is a nice principle, but I've run into a troubling glitch in related to it. It involves pass-through derivation like the original topic of this comment, but it originally came up in the context of private operations so I'll frame it there: package A is type Parent is ...; private procedure Do_Something(P: in Parent); end A; package A.B is type Child is new A.Parent; end A.B; package A.B.C is type Grandchild is new A.B.Child; end A.B.C; package body A.B.C is Thing: Grandchild; begin Do_Something(Thing); -- legal? end A.B.C; The problem is that 7.3.1(6) says: "Inherited primitive subprograms follow a different rule. For a derived_type_definition, each inherited primitive subprogram is implicitly declared at the earliest place, if any, within the immediate scope of the the type_declaration, but after the type_declaration, where the corresponding declaration from the parent is visible. If there is no such place, then the inherited subprogram is not declared at all. An inherited subprogram that is not declared at all cannot be named in a call, and cannot be overridden, but for a tagged type it is possible to dispatch to it." If taken literally, there is no Do_Something for Child, and it cannot be inherited by Grandchild. (Of course, this example is somewhat pathological, since it is always legal to explictly call Do_Something(Parent(Thing)), but if there are additional dispatching operations on Parent or Child it does make a difference.) I'd like to assume that this is just a glitch, and that there is an implicit private part but I can't. 7.3.1(1) says: "For a type declared in the visible part of a package or generic package, certain operations on the type do not become visible until later in the package -- either in the private part, or the body." This seems to say that if A.B had a body (and other declarations which required one), then Do_Something for Child would be declared in the body of B, and certainly wouldn't be available for derivation. I'd like to thank Weston T. Pan (westonpa@chaph.usc.edu) for help in defining this issue. Robert I. Eachus with Standard_Disclaimer; use Standard_Disclaimer; function Message (Text: in Clever_Ideas) return Better_Ideas is... **************************************************************** !section 7.3(15) !subject Controversial visibility of inherited private components ... !reference RM95 7.3 (15) 7.3.1(1) 7.3.1 (3) 7.3.1 (6) 7.3.1 (10) !reference 96-5635.a Bernard Maudry 96-08-21 !reference 96-5636.a Tucker Taft 96-8-22 !reference 96-5644.a Robert I. Eachus 96-8-23 !from Gary Dismukes !keywords visibility inherited private components child !reference 96-5651.a Gary Dismukes 96-8-25>> !discussion Robert Eachus wrote: > > This is a nice principle, but I've run into a troubling glitch in > related to it. It involves pass-through derivation like the original > topic of this comment, but it originally came up in the context of > private operations so I'll frame it there: > > package A is > type Parent is ...; > private > procedure Do_Something(P: in Parent); > end A; > > package A.B is > type Child is new A.Parent; > end A.B; > > package A.B.C is > type Grandchild is new A.B.Child; > end A.B.C; > > package body A.B.C is > Thing: Grandchild; > begin > Do_Something(Thing); -- legal? Yes, this is legal (see below). > end A.B.C; > > > The problem is that 7.3.1(6) says: "Inherited primitive > subprograms follow a different rule. For a derived_type_definition, > each inherited primitive subprogram is implicitly declared at the > earliest place, if any, within the immediate scope of the the > type_declaration, but after the type_declaration, where the > corresponding declaration from the parent is visible. If there is no > such place, then the inherited subprogram is not declared at all. An > inherited subprogram that is not declared at all cannot be named in a > call, and cannot be overridden, but for a tagged type it is possible > to dispatch to it." > > If taken literally, there is no Do_Something for Child, and it > cannot be inherited by Grandchild. (Of course, this example is > somewhat pathological, since it is always legal to explictly call > Do_Something(Parent(Thing)), but if there are additional dispatching > operations on Parent or Child it does make a difference.) The procedure Do_Something will be inherited for both type Child and type Grandchild, and declared for each of these types in the (implicit) private part of the respective package, so the call to Do_Something in the body of A.B.C is legal. > I'd like to assume that this is just a glitch, and that there is an > implicit private part but I can't. 7.3.1(1) says: "For a type > declared in the visible part of a package or generic package, certain > operations on the type do not become visible until later in the > package -- either in the private part, or the body." This seems to > say that if A.B had a body (and other declarations which required > one), then Do_Something for Child would be declared in the body of B, > and certainly wouldn't be available for derivation. The mention of the possibility of operations being declared in a body doesn't apply to this case. There are cases where operations can't be declared until reaching a body, such as for nested packages followed by types that enable further operations to be declared, but in the case of your example the types will be declared in the implicit private part of the package specification since that's the earliest place where the parent operation is visible. The presence of the implicit private part is explicitly defined in the last sentence of 7.1(6). So there's no problem with the operations getting declared in such cases. Gary Dismukes **************************************************************** !section 7.3(15) !subject Controversial visibility of inherited private components ... !reference RM95 7.3 (15) 7.3.1(1) 7.3.1 (3) 7.3.1 (6) 7.3.1 (10) 7.1(6) !reference 96-5635.a Bernard Maudry 96-08-21 !reference 96-5636.a Tucker Taft 96-8-22 !reference 96-5644.a Robert I. Eachus 96-8-23 !reference 96-5651.a Gary Dismukes 96-8-25 !from Robert I. Eachus !keywords visibility inherited private components child !reference 96-5652.a Robert I. Eachus 96-8-26>> !discussion Gary Dismukes said: > ...The presence of the implicit private part is explicitly defined > in the last sentence of 7.1(6). So there's no problem with the > operations getting declared in such cases. This is probably the right answer, and the one I expected. But I still can't read it in 7.1(6) and without help. For reference: RM 7.1(6): "...If the reserved word private does not appear, the package has an implicit EMPTY private part." RM 7.3.1(3..5) talk about "...where additional characteristic become visible..." RM 7.3.1(6) starts out: "Inherited primitive subprograms follow a different rule. For a derived_type_definition, each inherited primitive subprogram is implicitly declared at the earliest place, if any, within the immediate scope of the type_declaration...An inherited subprogram that is not declared at all cannot be named in a call and cannot be overridden, but for a tagged type, it is possible to dispatch to it." So you see my problem. Is it possible to (implicitly) declare a derived user subprogram in an implicit private part? The preamble to 7.3.1(6) and the empty in 7.1(6) encourage me to believe otherwise. If people think this is a confirmation, fine. But we got here from a compiler bug in the non-child package case. Robert I. Eachus with Standard_Disclaimer; use Standard_Disclaimer; function Message (Text: in Clever_Ideas) return Better_Ideas is... **************************************************************** !section 7.3(15) !subject Controversial visibility of inherited private components ... !reference RM95 7.3 (15) 7.3.1(1) 7.3.1 (3) 7.3.1 (6) 7.3.1 (10) 7.1(6) !reference 96-5635.a Bernard Maudry 96-08-21 !reference 96-5636.a Tucker Taft 96-8-22 !reference 96-5644.a Robert I. Eachus 96-8-23 !reference 96-5651.a Gary Dismukes 96-8-25 !keywords visibility inherited private components child !reference 96-5652.a Robert I. Eachus 96-8-26 !from Bob Duff !reference 96-5655.a Robert A Duff 96-8-27>> !discussion > RM 7.1(6): "...If the reserved word private does not appear, the > package has an implicit EMPTY private part." ... > So you see my problem. Is it possible to (implicitly) declare a > derived user subprogram in an implicit private part? Yes. I think you're reading too much into the word "empty". It just means that "package P is ... end P;" is equivalent to "package P is ... private end P;". I guess it means, "empty of explicit lexical elements". It does contain at least one space character, and it does contain all kinds of implicit junk, but worrying about that is getting far too pedantic, I think. Shall we argue about whether implicit declarations contain lexical elements and characters and so forth? >... The preamble to > 7.3.1(6) and the empty in 7.1(6) encourage me to believe otherwise. > If people think this is a confirmation, fine. I can't think of a better way to say it in English. If we were writing a formal definition, we might try to be more precise, but we're not. Therefore, yes, I think it's a confirmation that an "empty" private part can contain implicit stuff. (I mean, think about this: Clearly, an explicit empty private part can contain implicit declarations. Unless you think there's no such thing as an empty private part.) I believe one ground rule should be: if we think the RM wording is the best possible, then we have a confirmation. Keeping in mind that the average programmer will understand "empty declarative part" better than some verbosity like "empty except for the necessary space character, and perhaps a new-line to obey the formatting rules, and a bunch of implicit stuff, blah blah blah". >... But we got here from a > compiler bug in the non-child package case. I would be *extremely* surprised to find that this bug was caused (twice) by someone reading "empty" in such a pedantic manner. I would also be surprised if the bug went away if you added an *explicit* empty private part. To me, it just looks like a plain old bug (of which there are too many in this world). - Bob **************************************************************** !section 7.3(15) !subject Controversial visibility of inherited private components ... !reference RM95 7.3 (15) 7.3.1(1) 7.3.1 (3) 7.3.1 (6) 7.3.1 (10) 7.1(6) !reference 96-5635.a Bernard Maudry 96-08-21 !reference 96-5636.a Tucker Taft 96-8-22 !reference 96-5644.a Robert I. Eachus 96-8-23 !reference 96-5651.a Gary Dismukes 96-8-25 !reference 96-5655.a Robert A Duff 96-8-27 !keywords visibility inherited private components child !reference 96-5652.a Robert I. Eachus 96-8-26 !from Dan Lehman !reference 96-5661.a Dan Lehman 96-8-28>> !discussion Bob D. writes: > > RM 7.1(6): "...If the reserved word private does not appear, the > > package has an implicit EMPTY private part." > > Yes. I think you're reading too much into the word "empty". ... > > I can't think of a better way to say it in English. How about "... an implicit private part"? One then might naturally expect an implicit priv. part to contain implicit declarations--each with as many lexical elements as another (which an implementation may constrain to be no greater than the number of angels that can dance on the head of a pin (pin sizes may vary)). And, more seriously, add "... immediately following the last lexical element of the visible part."--(so why the "one space"?). > It does contain at least one space character I suggest an ACVC implementation-dependent test where code is exactly the maximum file size and it checks that this guy breaks the limit! --the resulting FRN would have movie rights! (-; ---Dan ------- * **************************************************************** !section 7.3(15) !subject Controversial visibility of inherited private components ... !reference RM95 7.3 (15) 7.3.1(1) 7.3.1 (3) 7.3.1 (6) 7.3.1 (10) 7.1(6) !reference 96-5635.a Bernard Maudry 96-08-21 !reference 96-5636.a Tucker Taft 96-8-22 !reference 96-5644.a Robert I. Eachus 96-8-23 !reference 96-5651.a Gary Dismukes 96-8-25 !reference 96-5655.a Robert A Duff 96-8-27 !reference 96-5652.a Robert I. Eachus 96-8-26 !reference 96-5661.a Dan Lehman 96-8-28 !keywords visibility inherited private components child !from Robert I. Eachus 96-8-29 !reference 96-5663.a Robert I. Eachus 96-8-29>> !discussion Before everyone gets sick of this topic, I'd like to add a couple ramifications to the discussion, and make sure everyone agrees with them. (I don't think that there is any question in this case of ambiguity, but they are a somewhat surprising consequence of the rules...) Please don't respond with "Why should that be surprising?" or the like. Anyone reading this list should be able to reach exactly the same conclusions, but there are lesser mortals out there. ;-) This is one of those cases where I feel the role of the ARG in issuing ramifications and confirmations is just as important as the role of issuing binding interpretations. Ramification 1: package A is type Parent is tagged private; private procedure Do_Something(P: in Parent); end A; with A; use A; package B is type Child is new Parent with null record; end B; package body B is Thing: Child; begin Do_Something(Thing); -- illegal end B; with B; use B; package A.C is type Grandchild is new Child with null record; end A.C; package body A.C is Another_Thing: Grandchild; begin Do_Something(Another_Thing); -- illegal Do_Something(Parent'Class(Another_Thing)); -- legal end A.C; ---------------------------------------------------------------------------- Ramification 2: package A is type Parent is tagged private; package B is type Child is new Parent with null record; end B; private procedure Do_Something(P: in Parent); end A; package body A is package body B is -- Do_Something(Child) declared here. Thing: Child; begin Do_Something(Thing); -- legal end B; end A; package A.C is use A.B; type Grandchild is new Child with null record; end A.C; package body A.C is Another_Thing: Grandchild; begin Do_Something(Another_Thing); -- illegal Do_Something(Parent'Class(Another_Thing)); -- legal end A.C; This one is the one that bothered me. If B is a child package, type Child inherits the private operations on Parent and they can be further derived. If B is directly nested, the operations are only inherited in the body of B. But if B is out of the direct line, there are inherited operations that cannot be called explicitly. I do NOT suggest changing this, and I don't know how to change it without breaking things. It is just another case where the structure of package nesting and child units is important in Ada 95. **************************************************************** !section 7.3(15) !subject Controversial visibility of inherited private components ... !reference RM95 7.3 (15) 7.3.1(1) 7.3.1 (3) 7.3.1 (6) 7.3.1 (10) 7.1(6) !reference 96-5635.a Bernard Maudry 96-08-21 !reference 96-5636.a Tucker Taft 96-8-22 !reference 96-5644.a Robert I. Eachus 96-8-23 !reference 96-5651.a Gary Dismukes 96-8-25 !reference 96-5655.a Robert A Duff 96-8-27 !reference 96-5652.a Robert I. Eachus 96-8-26 !reference 96-5661.a Dan Lehman 96-8-28 !keywords visibility inherited private components child !reference 96-5663.a Robert I. Eachus 96-8-29 !from Bob Duff !reference 96-5667.a Robert A Duff 96-8-30>> !discussion > Before everyone gets sick of this topic, I'd like to add a couple > ramifications to the discussion, and make sure everyone agrees with > them. The ramifications illustrated in your examples are correct (i.e., the lines marked "-- legal" are legal and the lines marked "-- illegal" are illegal). > Please don't respond with "Why should that be surprising?" or the > like. OK. - Bob ****************************************************************