Version 1.5 of ai12s/ai12-0428-1.txt
!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; --
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)
Replace the paragraph:
- Constant_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-to-constant parameter with designated type
T or T'Class.
by:
- Constant_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, 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.
!corrigendum 4.1.6(3/3)
Replace the paragraph:
- 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.
by:
- 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, 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.
!corrigendum 5.5.1(8/5)
Replace the paragraph:
- Default_Iterator
- This aspect is specified by a name that
denotes exactly one function declared immediately within the same
declaration list in which 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).
by:
- Default_Iterator
- 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).
!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).
****************************************************************
Questions? Ask the ACAA Technical Agent