Version 1.8 of ai05s/ai05-0029-1.txt
!standard 7.3.1(3/1) 08-04-21 AI05-0029-1/04
!standard 7.3.1(6/1)
!standard 12.5(8/2)
!standard 12.5.1(21/2)
!class binding interpretation 06-11-13
!status Amendment 201Z 08-11-26
!status WG9 Approved 08-06-20
!status ARG Approved 8-0-1 08-02-08
!status work item 06-11-13
!status received 06-06-02
!priority Medium
!difficulty Easy
!qualifier Omission
!subject Operations that are not declared but still exist
!summary
Some operators and primitive subprograms might not be declared at all but still
exist, and they can be named and called from generic instantiations in some
cases.
!question
Consider the following code:
package Q is
type T is limited private;
private
type T is range 1 .. 10;
end Q;
generic
type A is array (Positive range <>) of T;
package Q.G is
A1, A2 : A (1 .. 1);
private
B : Boolean := A1 = A2;
end Q.G;
with Q.G;
package R is
type C is array (Positive range <>) of Q.T;
package I is new Q.G (C); --
end R;
Observe that type T is limited, but not immutably limited, so its partial
view doesn't have an "=" operator but of course its full view does.
Observe also that Q.G is a public child of Q, so the formal type A is
limited in the formal part and visible part of the generic, but at the
beginning of the private part of Q.G we discover that T is actually
nonlimited, and an "=" for A is declared (7.3.1(3)). Therefore, the call
to this "=" in the private part of Q.G is legal.
Now the nasty part is the instantiation I in R. The actual type C has no
"=" operator, because there is no place where its component type is
nonlimited. So it is very much unclear what is the interpretation of the
"=" operator in the private part of I. The relevant text in the RM
appears to be the penultimate sentence of 12.5(8), which says "In an
instance, the copy of such an implicit declaration declares a view of the
predefined operator of the actual type, even if this operator has been
overridden for the actual type". But since the actual type has no
predefined "=", that rule doesn't seem to help.
What happens here?
!recommendation
(See summary.)
!wording
Change 7.3.1(3/1) as follows:
For a composite type, the characteristics (see 7.3) of the type are determined
in part by the characteristics of its component types. At the place where the
composite type is declared, the only characteristics of component types used
are those characteristics visible at that place. If later immediately within
the declarative region in which the composite type is declared additional
characteristics become visible for a component type, then any corresponding
characteristics become visible for the composite type. Any additional
predefined operators are implicitly declared at that place. {If there is no
such place, then additional predefined operators are not declared at all, but
they still exist.}
AARM Note: We say that they exist because they can emerge in some unusual
generic instantiations. See 12.5(8/2).
Change 7.3.1(6/1) as follows:
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, immediately within the declarative
region in which the type_declaration occurs, 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{, but it still
exists. For a tagged type, it is possible to dispatch to an} [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].
Editor's Note: We remove the part about not being able to name it or call it,
because you can do just that in some cases involving generic instantiations and
emergence.
Change 12.5(8/2) as follows:
The formal type also belongs to each category that contains the determined
category. The primitive subprograms of the type are as for any type in the
determined category. For a formal type other than a formal derived type, these
are the predefined operators of the type. For an elementary formal type, the
predefined operators are implicitly declared immediately after the declaration
of the formal type. For a composite formal type, the predefined operators are
implicitly declared either immediately after the declaration of the formal
type, or later immediately within the declarative region in which the type is
declared according to the rules of 7.3.1. In an instance, the copy of such an
implicit declaration declares a view of the predefined operator of the actual
type, even if this operator has been overridden for the actual type {and even if
it is never declared for the actual type}. The rules specific to formal derived
types are given in 12.5.1.
AARM Note: The somewhat cryptic phrase "even if it is never declared" is
intended to deal with the following oddity:
package Q is
type T is limited private;
private
type T is range 1 .. 10;
end Q;
generic
type A is array (Positive range <>) of T;
package Q.G is
A1, A2 : A (1 .. 1);
private
B : Boolean := A1 = A2;
end Q.G;
with Q.G;
package R is
type C is array (Positive range <>) of Q.T;
package I is new Q.G (C); --
end R;
An "=" is available for the formal type A in the private part of Q.G. However,
no "=" operator is ever declared for type C, because its component type Q.T is
limited. Still, in the instance I the name "=" declares a view of the "=" for C
which exists-but-is-never-declared.
end of AARM Note.
Change 12.5.1(21/2) as follows:
For a formal derived type, the predefined operators and inherited user-defined
subprograms are determined by the ancestor type and any progenitor types, and
are implicitly declared at the earliest place, if any, immediately within the
declarative region in which the formal type is declared, where the
corresponding primitive subprogram of the ancestor or progenitor is visible
(see 7.3.1). In an instance, the copy of such an implicit declaration declares
a view of the corresponding primitive subprogram of the ancestor or progenitor
of the formal derived type, even if this primitive has been overridden for the
actual type {and even if it is never declared for the actual type}. When the
ancestor or progenitor of the formal derived type is itself a formal type, the
copy of the implicit declaration declares a view of the corresponding copied
operation of the ancestor or progenitor. In the case of a formal private
extension, however, the tag of the formal type is that of the actual type, so
if the tag in a call is statically determined to be that of the formal type,
the body executed will be that corresponding to the actual type.
!discussion
We cannot make the call to "=" illegal because it could be in the generic body,
and that would be a contract model violation.
We cannot say that the rules of 7.3.1 do not apply to 12.5(8) because this very
issue was discussed long ago as part of DR 8652/0037, and it clear that we
intended the language to work that way (we want package private types and
generic formal private types to be as similar as possible).
So we have to come up with a model that explains where the missing "=" operator
comes from. In 7.3.1 we already have the notion of subprograms that exist but
are never declared. This shows up in the context of dispatching, because we
need to specify what body gets executed when dispatching would land in a
subprogram that was not declared. This seems to be a firm foundation to build
upon, so we say that in the example at hand the generic makes it possible to
name a subprogram that exists but was never declared. This requires two changes
in chapter 12 (for operators of composite formal types and for primitive
subprograms of formal derived types). And it requires changes in 7.3.1 to
explain that the operators exist but are not declared. No normative change is
required for derived types in 7.3.1, but there is bracketed text that is
telling lies, so it seems good to fix it.
!corrigendum 7.3.1(3/1)
Replace the paragraph:
For a composite type, the characteristics (see 7.3) of the type are determined
in part by the characteristics of its component types. At the place where the
composite type is declared, the only characteristics of component types used
are those characteristics visible at that place. If later immediately within
the declarative region in which the composite type is declared additional
characteristics become visible for a component type, then any corresponding
characteristics become visible for the composite type. Any additional
predefined operators are implicitly declared at that place.
by:
For a composite type, the characteristics (see 7.3) of the type are determined
in part by the characteristics of its component types. At the place where the
composite type is declared, the only characteristics of component types used
are those characteristics visible at that place. If later immediately within
the declarative region in which the composite type is declared additional
characteristics become visible for a component type, then any corresponding
characteristics become visible for the composite type. Any additional
predefined operators are implicitly declared at that place. If there is no such
place, then additional predefined operators are not declared at all, but they
still exist.
!corrigendum 7.3.1(6/1)
Replace the paragraph:
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, immediately within the declarative
region in which the type_declaration occurs, 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.
by:
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, immediately within the declarative
region in which the type_declaration occurs, 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, but it still exists. For a tagged type, it is possible to
dispatch to an inherited subprogram that is not declared at all.
!corrigendum 12.5(8/2)
Replace the paragraph:
The formal type also belongs to each category that contains the determined
category. The primitive subprograms of the type are as for any type in the
determined category. For a formal type other than a formal derived type, these
are the predefined operators of the type. For an elementary formal type, the
predefined operators are implicitly declared immediately after the declaration
of the formal type. For a composite formal type, the predefined operators are
implicitly declared either immediately after the declaration of the formal
type, or later immediately within the declarative region in which the type is
declared according to the rules of 7.3.1. In an instance, the copy of such an
implicit declaration declares a view of the predefined operator of the actual
type, even if this operator has been overridden for the actual type. The rules
specific to formal derived types are given in 12.5.1.
by:
The formal type also belongs to each category that contains the determined
category. The primitive subprograms of the type are as for any type in the
determined category. For a formal type other than a formal derived type, these
are the predefined operators of the type. For an elementary formal type, the
predefined operators are implicitly declared immediately after the declaration
of the formal type. For a composite formal type, the predefined operators are
implicitly declared either immediately after the declaration of the formal
type, or later immediately within the declarative region in which the type is
declared according to the rules of 7.3.1. In an instance, the copy of such an
implicit declaration declares a view of the predefined operator of the actual
type, even if this operator has been overridden for the actual type and even if
it is never declared for the actual type. The rules specific to formal derived
types are given in 12.5.1.
!corrigendum 12.5.1(21/2)
Replace the paragraph:
For a formal derived type, the predefined operators and inherited user-defined
subprograms are determined by the ancestor type and any progenitor types, and
are implicitly declared at the earliest place, if any, immediately within the
declarative region in which the formal type is declared, where the
corresponding primitive subprogram of the ancestor or progenitor is visible
(see 7.3.1). In an instance, the copy of such an implicit declaration declares
a view of the corresponding primitive subprogram of the ancestor or progenitor
of the formal derived type, even if this primitive has been overridden for the
actual type. When the ancestor or progenitor of the formal derived type is
itself a formal type, the copy of the implicit declaration declares a view of
the corresponding copied operation of the ancestor or progenitor. In the case
of a formal private extension, however, the tag of the formal type is that of
the actual type, so if the tag in a call is statically determined to be that of
the formal type, the body executed will be that corresponding to the actual
type.
by:
For a formal derived type, the predefined operators and inherited user-defined
subprograms are determined by the ancestor type and any progenitor types, and
are implicitly declared at the earliest place, if any, immediately within the
declarative region in which the formal type is declared, where the
corresponding primitive subprogram of the ancestor or progenitor is visible
(see 7.3.1). In an instance, the copy of such an implicit declaration declares
a view of the corresponding primitive subprogram of the ancestor or progenitor
of the formal derived type, even if this primitive has been overridden for the
actual type and even if it is never declared for the actual type. When the
ancestor or progenitor of the formal derived type is itself a formal type, the
copy of the implicit declaration declares a view of the corresponding copied
operation of the ancestor or progenitor. In the case of a formal private
extension, however, the tag of the formal type is that of the actual type, so
if the tag in a call is statically determined to be that of the formal type,
the body executed will be that corresponding to the actual type.
!ACATS test
Create an ACATS C-Test like the example in the question.
!appendix
From: Pascal Leroy
Date: Friday, June 2, 2006 1:50 AM
I am in the process of revamping our implementation of inherited
subprograms, and I am running into an oddity on which I would like to have
the opinion of the assembled experts. Consider the following code:
package Q is
type T is limited private;
private
type T is range 1 .. 10;
end Q;
generic
type A is array (Positive range <>) of T;
package Q.G is
A1, A2 : A (1 .. 1);
private
B : Boolean := A1 = A2;
end Q.G;
with Q.G;
package R is
type C is array (Positive range <>) of Q.T;
package I is new Q.G (C); -- Where is the predefined "=" for C?
end R;
Observe that type T is limited, but not "really" limited, so its partial
view doesn't have an "=" operator but of course its full view does.
Observe also that Q.G is a public child of Q, so the formal type A is
limited in the formal part and visible part of the generic, but at the
beginning of the private part of Q.G we discover that T is actually
nonlimited, and an "=" for A is declared (7.3.1(3)). Therefore, the call
to this "=" in the private part of Q.G is legal.
Now the nasty part is the instantiation I in R. The actual type C has no
"=" operator, because there is no place where its component type is
nonlimited. So it is very much unclear what is the interpretation of the
"=" operator in the private part of I. The relevant text in the RM
appears to be the penultimate sentence of 12.5(8), which says "In an
instance, the copy of such an implicit declaration declares a view of the
predefined operator of the actual type, even if this operator has been
overridden for the actual type". But since the actual type has no
predefined "=", that rule doesn't seem to help.
Is an implementation expected to conjure up an "=" operator for C (and
hide it under the rug) just in case it were used in such an instantiation?
That would be disgggusting.
****************************************************************
From: Tucker Taft
Date: Friday, June 2, 2006 1:54 PM
The part that says "even if this operator has been
overridden for the actual type" is pretty much just
a non-normative clause. The key thing is that a
predefined operator is provided, independent of what
the actual type has (or doesn't have).
Operator "reemergence" in a generic is pretty well known,
but perhaps you are right that operator "emergence" is
less well known.
****************************************************************
From: Robert A. Duff
Date: Friday, June 2, 2006 3:58 PM
> Is an implementation expected to conjure up an "=" operator for C (and
> hide it under the rug) just in case it were used in such an instantiation?
> That would be disgggusting.
I would spell it deeesgusting. ;-)
Sounds like a nasty (but obscure) hole in the language (predefined "=" is
declared in certain places, and referred to in other places, but it's not
declared-where-used in this case).
For what it's worth, GNAT doesn't get that far. It gives an error on the
generic Q.G:
q-g.ads:7:23: there is no applicable operator "=" for type "A" defined at line 3
on the line:
B : Boolean := A1 = A2;
****************************************************************
Questions? Ask the ACAA Technical Agent