!standard 4.1.6(2/3) 21-06-07 AI12-0428-1/03 !standard 4.1.6(3/3) !standard 5.5.1(8/5) !class binding interpretation 21-05-21 !status Amendment 1-2012 21-06-07 !status WG9 Approved 22-06-22 !status ARG Approved 13-0-2 21-06-03 !status work item 21-05-21 !status received 21-05-21 !priority Low !difficulty Easy !qualifier Error !subject "Same declaration list" requirement too strong for private types !summary The requirement that the function(s) specified for a Constant_Indexing or Variable_Indexing shall be in the same declaration list is too strong for the completion of a private type. !question Two Ada 2012 implementations get different results on the following example (distilled from ACATS test B416001): procedure B416001 is type Ref (Int_Ref : access Integer) is null record with Implicit_Dereference => Int_Ref; Null_Ref : constant Ref := (Int_Ref => null); package Invalid_Indexing_Types is type Priv_2 is private; function Priv_2_Func (P : Priv_2; Index : Positive) return Ref is (Null_Ref); private type Priv_2 is tagged null record with Variable_Indexing => Priv_2_Func; -- OK. end Invalid_Indexing_Types; ... One of the compilers matches the result suggested by the test author. The other compiler rejects the line marked "OK." referring to 4.1.6(3/3) and saying that Priv_2_Func is declared in the wrong declaration list. Is the test correct? (Yes.) !recommendation (See Summary.) !wording Modify 4.1.6(2/3): This aspect shall be specified by a name that denotes one or more functions declared immediately within the same declaration list in which T{, or the declaration completed by T,} is declared. All such functions shall have at least two parameters, the first of which is of type T or T'Class, or is an access-to-constant parameter with designated type T or T'Class. Modify 4.1.6(3/3): This aspect shall be specified by a name that denotes one or more functions declared immediately within the same declaration list in which T{, or the declaration completed by T,} is declared. All such functions shall have at least two parameters, the first of which is of type T or T'Class, or is an access parameter with designated type T or T'Class. All such functions shall have a return type that is a reference type (see 4.1.5), whose reference discriminant is of an access-to-variable type. Modify 5.5.1(8/5): This aspect is specified by a name that denotes exactly one function declared immediately within the same declaration list in which T{, or the declaration completed by T,} is declared, whose first parameter is of type T or T'Class or an access parameter whose designated type is type T or T'Class, whose other parameters, if any, have default expressions, and whose result type is an iterator type. This function is the default iterator function for T. Its result subtype is the default iterator subtype for T. The iteration cursor subtype for the default iterator subtype is the default cursor subtype for T. This aspect is inherited by descendants of type T (including T'Class). !discussion 4.1.6(3/3) says: Variable_Indexing This aspect shall be specified by a name that denotes one or more functions declared immediately within the same declaration list in which T is declared. All such functions shall have at least two parameters, the first of which is of type T or T'Class, or is an access parameter with designated type T or T'Class. All such functions shall have a return type that is a reference type (see 4.1.5), whose reference discriminant is of an access-to-variable type. The important part here is the requirement that the function is defined in the "same declaration list as T". That of course raises the question of where Priv_2 is defined. Careful study of the rules suggests that the wording says that the type is declared in the declaration list of the visible part of the package. For purposes of this sort of "what declaration list is it declared in" question, a completion only declares a new view of an already existing type (as opposed to declaring the type). The 4.1.6(3/3) wording seems to have an implicit assumption that a type is declared in a single declaration list and that indicates that we really are talking about a declaring a type (as opposed to declaring a view of a type). Note that AARM 3.9.2(1.a.1/2), confirms this interpretation in the context of stream-oriented attributes. With this understanding of the wording, then trying to hide indexing as follows: package Invalid_Indexing_Types is type Priv_X is private; private type Priv_X is tagged null record with Variable_Indexing => Priv_X_Func; -- !!! function Priv_X_Func (P : Priv_X; Index : Positive) return Ref is (Null_Ref); end Invalid_Indexing_Types; ...is illegal, as the operation is not in the same declaration list as the type. This seems to be a perfectly reasonable thing to do, so it seems pretty clear that the language is broken. Note that the requirement is simply intended to ensure that the specified function(s) have the same accessibility (lifetime) as the type, so that they always exist when the type does. (And, for functions with specific type parameters, that they are primitive.) This means that anything in the same package specification should be allowed (subject to visibility; one cannot use private operations in the visible part; that's ensured by basic rules of aspect specifications and does not need to be mentioned here). --- Default_Iterator uses similar wording, and was fixed in a corresponding manner. !corrigendum 4.1.6(2/3) @drepl @xhang<@xtermThis aspect shall be specified by a @fa that denotes one or more functions declared immediately within the same declaration list in which @i is declared. All such functions shall have at least two parameters, the first of which is of type @i or @i'Class, or is an access-to-constant parameter with designated type @i or @i'Class.> @dby @xhang<@xtermThis aspect shall be specified by a @fa that denotes one or more functions declared immediately within the same declaration list in which @i, or the declaration completed by @i, is declared. All such functions shall have at least two parameters, the first of which is of type @i or @i'Class, or is an access-to-constant parameter with designated type @i or @i'Class.> !corrigendum 4.1.6(3/3) @drepl @xhang<@xtermThis aspect shall be specified by a @fa that denotes one or more functions declared immediately within the same declaration list in which @i is declared. All such functions shall have at least two parameters, the first of which is of type @i or @i'Class, or is an access parameter with designated type @i or @i'Class. All such functions shall have a return type that is a reference type (see 4.1.5), whose reference discriminant is of an access-to-variable type.> @dby @xhang<@xtermThis aspect shall be specified by a @fa that denotes one or more functions declared immediately within the same declaration list in which @i, or the declaration completed by @i, is declared. All such functions shall have at least two parameters, the first of which is of type @i or @i'Class, or is an access parameter with designated type @i or @i'Class. All such functions shall have a return type that is a reference type (see 4.1.5), whose reference discriminant is of an access-to-variable type.> !corrigendum 5.5.1(8/5) @drepl @xhang<@xtermThis aspect is specified by a @fa that denotes exactly one function declared immediately within the same declaration list in which @i is declared, whose first parameter is of type @i or @i'Class or an access parameter whose designated type is type @i or @i'Class, whose other parameters, if any, have default expressions, and whose result type is an iterator type. This function is the @i for @i. Its result subtype is the @i for @i. The iteration cursor subtype for the default iterator subtype is the @i for @i. This aspect is inherited by descendants of type @i (including @i'Class).> @dby @xhang<@xtermThis aspect is specified by a @fa that denotes exactly one function declared immediately within the same declaration list in which @i, or the declaration completed by @i, is declared, whose first parameter is of type @i or @i'Class or an access parameter whose designated type is type @i or @i'Class, whose other parameters, if any, have default expressions, and whose result type is an iterator type. This function is the @i for @i. Its result subtype is the @i for @i. The iteration cursor subtype for the default iterator subtype is the @i for @i. This aspect is inherited by descendants of type @i (including @i'Class).> !ASIS No ASIS effect. !ACATS test ACATS test B416001 already has examples of this (commented out for now); those can be replaced for Ada 202x. !appendix [From WG 9 issue #174] I was testing a new (to me) compiler with the ACATS and I ran across one test (B416001) that gets different results on different compilers and now believe that it is the language that is wrong. Just showing the declarations for the case in question: procedure B416001 is type Ref (Int_Ref : access Integer) is null record with Implicit_Dereference => Int_Ref; Null_Ref : constant Ref := (Int_Ref => null); package Invalid_Indexing_Types is type Priv_2 is private; function Priv_2_Func (P : Priv_2; Index : Positive) return Ref is (Null_Ref); private type Priv_2 is tagged null record with Variable_Indexing => Priv_2_Func; -- OK. end Invalid_Indexing_Types; ... One of the compilers matches the result suggested by the test author. The other compiler rejects the line marked "OK." referring to 4.1.6(3/3) and saying that Priv_2_Func is declared in the wrong declaration list. 4.1.6(3/3) says: Variable_Indexing This aspect shall be specified by a name that denotes one or more functions declared immediately within the same declaration list in which T is declared. All such functions shall have at least two parameters, the first of which is of type T or T'Class, or is an access parameter with designated type T or T'Class. All such functions shall have a return type that is a reference type (see 4.1.5), whose reference discriminant is of an access-to-variable type. The important part here is the requirement that the function is defined in the "same declaration list as T". That of course raises the question of where Priv_2 is defined. Steve Baird provided the answer to that question by noting: I agree that the RM is unfortunately vague on that point, but given the current wording I would say that the type is declared in the declaration list of the visible part of the package. For purposes of this sort of "what declaration list is it declared in" question, a completion only declares a new view of an already existing type (as opposed to declaring the type). The current wording seems to have an implicit assumption that a type is declared in a single declaration list and, to me, that indicates that we really are talking about a declaring a type (as opposed to declaring a view of a type). FWIW, see AARM 3.9.2(1.a.1/2), which confirms this interpretation in the context of stream-oriented attributes. Of course, if we assume Steve's interpretation is correct, then trying to hide indexing via: package Invalid_Indexing_Types is type Priv_X is private; private type Priv_X is tagged null record with Variable_Indexing => Priv_X_Func; -- !!! function Priv_X_Func (P : Priv_X; Index : Positive) return Ref is (Null_Ref); end Invalid_Indexing_Types; ...is illegal, as the operation is not in the same declaration list as the type. This seems to be a perfectly reasonable thing to do and it poses no semantic problems, so it seems pretty clear that the language is broken. After some additional discussion, Tucker Taft proposed the following rewording: This aspect shall be specified by a name that denotes one or more functions declared immediately within the same declaration list in which T{, or the declaration completed by T,} is declared. Constant_Indexing has the same wording and needs the same fix in 4.1.6(2/3). ****************************************************************