!standard 4.3.1(14) 08-10-15 AI05-0115-1/01 !standard 4.3.2(5/2) !class binding interpretation 08-10-15 !status work item 08-10-15 !status received 08-07-25 !priority Medium !difficulty Medium !qualifier Omission !subject Aggregates with components that are not visible !summary An aggregate is illegal if it has components that are not visible. !question Consider the following: package Pak1 is type T1 is tagged private; private type T1 is tagged record C1 : Integer; end record; end Pak1; with Pak1; package Pak2 is type T2 is new Pak1.T1 with record C2 : Integer; end record; end Pak2; with Pak2; package Pak1.Pak3 is type T3 is new Pak2.T2 with record C3 : Integer; end record; procedure Foo; end Pak1.Pak3; package body Pak1.Pak3 is procedure Foo is R : T3; N : Integer; begin N := R.C1; -- (A: Error.) R := (C1 => 1, C2 => 2, C3 => 3); -- (B: Legal? No.) R := (C2 => 2, C3 => 3, others => 1); -- (C: Legal? No.) R := (others => 4); -- (D: Legal? No.) end Foo; end Pak1.Pak3; (A) is illegal by 7.3.1(4). Although the component F1 of the grandparent type has become visible in the body of Pak1.Pak3, 7.3.1(4) speaks about "additional characteristics of the parent type" becoming visible, and there isn't any place where the hidden component F1 of the type T2 becomes visible. AI95-0157 confirms this interpretation. That means that (B) is illegal, because one of the component names is not visible. But what about (C) and (D), which define a value for the component without mentioning its name? !recommendation (See Summary.) !wording Modify 4.3.1(14): If the type of a record_aggregate is a record extension, then it shall be a descendant of a record type, through one or more record extensions (and no private extensions). {Moreover, the parent type of each of those record extensions shall have been a record type or record extension at the point of the record extension declaration.} [Editor's Note: An alternative way of wording this would simply require that all components are visible. However, care would be needed to avoid making private types which are null records always legal, as that would break privacy. One way of wording that would be something like: A record_aggregate is illegal if any possible components of the composite value defined by the type of the aggregate are not visible or not declared. It's not clear that this is any actual improvement; it's pretty vague about "possible components". It also doesn't work as well for the extension aggregate case. Thus I didn't use it. End Editor's Note.] AARM Note: The last sentence prevents aggregates where not all of the components are visible, even though all of the types are full views at the point of the aggregate. For example: package Pak1 is type T1 is tagged private; private type T1 is tagged record C1 : Integer; end record; end Pak1; with Pak1; package Pak2 is type T2 is new Pak1.T1 with record C2 : Integer; end record; end Pak2; with Pak2; package Pak1.Pak3 is type T3 is new Pak2.T2 with record C3 : Integer; end record; procedure Foo; end Pak1.Pak3; package body Pak1.Pak3 is procedure Foo is R : T3; begin R := (others => 4); -- (Illegal.) end Foo; end Pak1.Pak3; By requiring that all of the parent types are record types or record extensions at the point of the derivation, we avoid this problem. Modify 4.3.2(5/2): ... The type of the extension_aggregate shall be derived from the type of the ancestor_part, through one or more record extensions (and no private extensions). {Moreover, the parent type of each of those record extensions shall have been a record extension at the point of the record extension declaration.} !discussion Logically, an aggregate is a shorthand for setting each component individually. If setting a component explicitly is illegal because of a visibility issue, then the same rule ought to apply to the equivalent aggregate. Thus all of the example aggregates are illegal. The best way to fix this is to fix 4.3.1(14) to make it clear that what matters whether the parent type was a partial view at the point of the derivation (whether the parent type is a partial view at the point of the aggregate is irrelevant). Note that a similar case can be constructed for extension aggregates: Make type T1 in the example derived from a root type, then aggregates like: R := (Root with C1 => 1, C2 => 2, C3 => 3); -- (Illegal.) R := (Root with C2 => 2, C3 => 3, others => 1); -- (Illegal.) R := (Root with others => 4); -- (Illegal.) [Editor's Second Note: I'm not completely convinced that there isn't some weird case where the components get declared in the body. There are such cases for operations that are declared in nested packages, but I *think* that components don't have any such cases because a nested package can't add any components to an outer type. Steve Baird may prove me wrong...] !corrigendum 4.3.1(14) @drepl If the type of a @fa is a record extension, then it shall be a descendant of a record type, through one or more record extensions (and no private extensions). @dby If the type of a @fa is a record extension, then it shall be a descendant of a record type, through one or more record extensions (and no private extensions). Moreover, the parent type of each of those record extensions shall have been a record type or record extension at the point of the record extension declaration. !corrigendum 4.3.2(5/2) @drepl If the @fa is a @fa, it shall denote a specific tagged subtype. If the @fa is an @fa, it shall not be dynamically tagged. The type of the @fa shall be derived from the type of the @fa, through one or more record extensions (and no private extensions). @dby If the @fa is a @fa, it shall denote a specific tagged subtype. If the @fa is an @fa, it shall not be dynamically tagged. The type of the @fa shall be derived from the type of the @fa, through one or more record extensions (and no private extensions). Moreover, the parent type of each of those record extensions shall have been a record extension at the point of the record extension declaration. !ACATS Test B-Tests for examples like these should be constructed. !appendix !topic Component visibility question !reference 4.3.1(14), 7.3.1(4), AI95-157 !from Adam Beneschan 08-07-25 !discussion I'm having trouble figuring out how the visibility and private-operation rules apply in this case: package Pak1 is type T1 is tagged private; private type T1 is tagged record F1 : Integer; end record; end Pak1; with Pak1; package Pak2 is type T2 is new Pak1.T1 with record F2 : Integer; end record; end Pak2; with Pak2; package Pak1.Pak3 is type T3 is new Pak2.T2 with record F3 : Integer; end record; procedure Foo; end Pak1.Pak3; package body Pak1.Pak3 is procedure Foo is R : T3; N : Integer; begin N := R.F1; -- (A) R := (F1 => 1, F2 => 2, F3 => 3); -- (B) R := (F2 => 2, F3 => 3, others => 1); -- (C) R := (others => 4); -- (D) end Foo; end Pak1.Pak3; I'm pretty sure that the component selection in (A) is illegal, based on the wording in 7.3.1(4). Although the component F1 of the grandparent type has become visible in the body of Pak1.Pak3, 7.3.1(4) speaks about "additional characteristics of the parent type" becoming visible, and there isn't any place where the hidden component F1 of the type T2 becomes visible. I think this case is covered by AI95-157 also. (B) is a rather different case. Normally, an aggregate would be illegal for a derived type if the ultimate ancestor were a private type (rather than a record type), or if any private extensions were involved (4.3.1(14)). In this case, though, there are no private extensions; and at the point where statement (B) occurs, the type is derived from a record type, not a private type, since the full view of T1 is available and T1 is a record type. Should 4.3.1(14) somehow be interpreted so that, in this case, T3 is still considered a descendant of a "private type" rather than a "record type" since there's an intermediate parent type for which "additional characteristics" have not become visible? Even if the aggregate in (B) is not illegal by 4.3.1(14), it would still seem to be illegal since one of the component names, F1, is not actually a visible component of the type (for the same reason as in (A)). Assuming that's the case, then what about (C) and (D)? Are they illegal or not? I'm guessing that AI95-157 applies to (B), (C), and (D) also, and that any record aggregate of type T3 is illegal regardless of what components are or are not named using named notation. [Extension aggregates are still OK.] But it's not 100% clear to me that the principle applies, given that the wording of 4.3.1(14) doesn't seem to use any of the terms that 7.3.1(4) or AI95-157 discuss. (Is the ability to specify an aggregate an "operation" of a record type? I don't think the RM answers that definitively.) **************************************************************** From: Adam Beneschan Sent: Friday, August 8, 2008 5:25 PM Since I posted this, I found that this question affects a publicly available open-source Ada suite, PolyORB. If I'm right, then the Ada source for that suite is illegal (but GNAT apparently does not catch it). If possible, I'd appreciate some opinion on whether this code is actually illegal, before I make a bug report to the maintainers. Here are the relevant constructs from the source: package PolyORB.SOAP_P.Message is type Object is tagged private; private type Object is tagged record Name_Space : Unbounded_String; Wrapper_Name : Unbounded_String; P : SOAP_P.Parameters.List; end record; end PolyORB.SOAP_P.Message; package PolyORB.SOAP_P.Message.Response is type Object is new Message.Object with null record; end PolyORB.SOAP_P.Message.Response; with PolyORB.SOAP_P.Message.Response.Error; package body PolyORB.SOAP_P.Message.XML is (... in a Load_Response function:) return new Message.Response.Object' -- LEGAL??? (Null_Unbounded_String, S.Wrapper_Name, S.Parameters); end PolyORB.SOAP_P.Message.XML; If my interpretation is correct, then the components of the derived type Message.Response.Object (and, indeed, the fact that it's a record) do not become visible until a point in PolyORB.SOAP_P.Message.Response where the private part of PolyORB.SOAP_P.Message is visible, i.e. the private part of PolyORB.SOAP_P.Message.Response. And, since the private part of PolyORB.SOAP_P.Message.Response is not at all visible inside PolyORB.SOAP_P.Message.XML, the inherited components of Message.Response.Object are not visible either, and therefore the aggregate should not be legal. Thoughts? **************************************************************** From: Randy Brukardt Sent: Friday, August 8, 2008 7:34 PM Without considering the case very fully, it seems clear that the intent of 4.3.1(14) is that a record aggregate is not allowed for a record extension if any of the ancestors is a partial view rather than a record of some sort. And clearly this needs to be "frozen" once an extension is done which is out of scope of the original type, just as for components. So I think that an aggregate should be illegal (given the current rules; I would have preferred that we find a way to allow aggregates for private types, but since that failed we shouldn't be allowing funny holes). But there clearly is no current reason to think that the Standard answers this question at all. The only reason to think this way is that the model of AI95-0157 should apply here, too, and that will require a new rule of some sort. So it's probably impossible to say definitely that a compiler that works some other way is wrong. Whether to send in a bug report is therefore impossible to answer. ****************************************************************