CVS difference for ai05s/ai05-0115-1.txt
--- ai05s/ai05-0115-1.txt 2008/11/25 02:05:25 1.3
+++ ai05s/ai05-0115-1.txt 2009/06/02 22:42:07 1.4
@@ -1,4 +1,4 @@
-!standard 4.3.1(13) 08-11-24 AI05-0115-1/03
+!standard 4.3.1(13) 09-06-02 AI05-0115-1/04
!standard 4.3.1(14)
!standard 4.3.2(5/2)
!class binding interpretation 08-10-15
@@ -13,10 +13,13 @@
An aggregate is illegal if it has components that are not visible.
+The ancestor type of an extension aggregate must not have unknown
+discriminants.
+
!question
Consider the following:
-
+
package Pak1 is
type T1 is tagged private;
private
@@ -47,7 +50,7 @@
N : Integer;
begin
N := R.C1; -- (A: Error.)
- R := (C1 => 1, C2 => 2, C3 => 3); -- (B: Legal? No.)
+ 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;
@@ -64,6 +67,32 @@
But what about (C) and (D), which define a value for the component without
mentioning its name?
+A related example is:
+
+ package T_Pack is
+ type T (<>) is abstract tagged limited private;
+ private
+ type T (C: Character) is abstract tagged limited null record;
+ end T_Pack;
+
+ with T_Pack;
+
+ package S_Pack is
+ use T_Pack;
+ type S is new T with null record;
+ function Create return S;
+ end S_Pack;
+
+ package body S_Pack is
+ function Create return S is
+ begin
+ return S'(T with null record); -- Legal? (No.)
+ end Create;
+ end S_Pack;
+
+Here the ancestor type has unknown discriminants, so that actual discriminant
+C is not visible.
+
!recommendation
(See Summary.)
@@ -81,12 +110,17 @@
components" if T is A, or if T is a record extension of A, or if
T is a record extension of type B, and B extends A with known components.
+[Editor's note: See the last mail from Randy. This wording doesn't capture
+the locality very well, in that it is the view WRT to the type derivation
+that matters. Should we work this hard to preserve an annoying Ada feature??
+See also AI05-0125-1.]
+
AARM Note:
Having known components and extending an ancestor type A with known
components are characteristics of the type T (see 7.3 and 7.3.1). That
means that these properties can be added as more information becomes
available about an ancestor type, and thus whether or not a type
- has known components depends on its view.
+ has known components depends on its view.
Consider the following example:
@@ -108,20 +142,23 @@
end Pkg2;
with Pkg1.Pkg2;
- package body Pkg1 is
+ package body Pkg1 is
type T3 is new Pkg2.T2 with null record;
-- T3 does not have known components here because T2
-- does not have known components (as seen from here)
-- even though T1 does (as seen from here).
- end Pkg1;
-
+ end Pkg1;
+
Replace 4.3.1(14):
The type of a record aggregate shall have known components.
-Replace the last sentence of 4.3.2(5/2):
+Replace 4.3.2(5/2):
+ If the ancestor_part is a subtype_mark, it shall denote a specific tagged
+ subtype, and it shall not denote a type with unknown discriminants.
+ If the ancestor_part is an expression, it shall not be dynamically tagged.
The type of the extension_aggregate shall be derived from the type
of the ancestor_part. The type of the extension aggregate shall
extend the type of the ancestor_part with known components.
@@ -136,7 +173,7 @@
what attributes and other predefined operations are allowed, [and ]whether
the first subtype is static{, whether the type has known components,
and (for any ancestor type A of the given type) whether the type
- extends A with known components}.
+ extends A with known components}.
!discussion
@@ -153,7 +190,7 @@
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 C1 => 1, C2 => 2, C3 => 3); -- (Illegal.)
R := (Root with C2 => 2, C3 => 3, others => 1); -- (Illegal.)
R := (Root with others => 4); -- (Illegal.)
@@ -165,55 +202,81 @@
it would likely be a fairly large change to compilers (even if it would be more
usable).
+---
+
+We added a separate rule about ancestors with unknown discriminants. It is
+really weird to have to logically look into the private part to justify these
+are illegal. We also want this to be rejected even if the full type does not
+have any discriminants. Finally, we also want to cover generic cases:
+
+ generic
+ type T (<>) is tagged private;
+ package Gen_Pack is
+ type New_T is new T with record
+ A : Integer;
+ end record;
+ function Create return New_T;
+ end Gen_Pack;
+
+ package body Gen_Pack is
+ function Create return New_T is
+ begin
+ return New_T' (T with A => 10);
+ end Create;
+ end Gen_Pack;
+
+where the components in question are completely unknown when the generic
+body is compiled.
+
!example
The following example includes the interesting cases identified by discussion
on this topic.
package Pkg1 is
- type T1 is tagged private; -- Does not have known discriminants (partial view).
+ type T1 is tagged private; -- Does not have known components (partial view).
private
type T1 is tagged record C1 : Integer; end record;
- -- Does have known discriminants
+ -- Does have known components
end Pkg1;
-
+
package Pkg1.Pkg2 is -- Private case
type T2 is new Pkg1.T1 with private;
- -- Does not have known discriminants (partial view and parent type).
+ -- Does not have known components (partial view and parent type).
X1 : T2 := (others => 1); -- Illegal.
private
type T2 is new Pkg1.T1 with record C2 : Integer; end record;
- -- Does have known discriminants (derived from type that has them).
+ -- Does have known components (derived from type that has them).
X2 : T2 := (C1 | C2 => 1); -- Legal.
end Pkg1.Pkg2;
package Pkg1.Pkg3 is -- Record case
type T3 is new Pkg1.T1 with record C2 : Integer; end record;
- -- Does not have known discriminants (parent type)
+ -- Does not have known components (parent type)
X1 : T3 := (others => 1); -- Illegal.
private
- -- T3 gets known discriminants here (an additional characteristic).
+ -- T3 gets known components here (an additional characteristic).
X2 : T3 := (C1 | C2 => 1); -- Legal.
end Pkg1.Pkg3;
-
+
with Pkg1.Pkg2, Pkg1.Pkg3;
package body Pkg1 is
type T4 is new Pkg2.T2 with record C1 : Integer; end record;
-- T2 does not have a (visible) C1 component.
- -- T4 does not have known discriminants (parent type).
+ -- T4 does not have known components (parent type).
X3 : T4 := (C1 => 1, C2 => 2); -- Illegal.
X4 : T4 := (others => 1); -- Illegal.
type T5 is new Pkg3.T3 with record C3 : Integer; end record;
- -- T3 does not have a (visible) C1 component. The fact that one is declared in
- -- in the private part is irrelevant.
- -- T5 does not have known discriminants (parent type).
+ -- T3 does not have a (visible) C1 component. The fact that one is
+ -- declared in in the private part is irrelevant.
+ -- T5 does not have known components (parent type).
X5 : T5 := (C3 => 1, C2 => 2, C1 => 1); -- Illegal.
X6 : T5 := (C2 => 2, others => 1); -- Illegal.
type T6 is new Pkg3.T3 with record C1 : Integer; end record;
- -- Legal with the current rules, as C1 never becomes visible for this type.
- -- T6 does not have known discriminants (parent type)
+ -- Legal with the current rules, as C1 never becomes visible for
+ -- this type. T6 does not have known components (parent type)
X7 : T6 := (C1 => 1, C2 => 2); -- Illegal.
X8 : T6 := (C2 => 2, others => 1); -- Illegal.
@@ -236,7 +299,8 @@
@drepl
If the @fa<ancestor_part> is a @fa<subtype_mark>, it shall denote a specific
-tagged subtype. If the @fa<ancestor_part> is an @fa<expression>, it shall
+tagged subtype, and it shall not denote a type with unknown discriminants.
+If the @fa<ancestor_part> is an @fa<expression>, it shall
not be dynamically tagged. The type of the @fa<extension_aggregate> shall
be derived from the type of the @fa<ancestor_part>, through one or more
record extensions (and no private extensions).
@@ -263,7 +327,7 @@
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
@@ -294,7 +358,7 @@
N : Integer;
begin
N := R.F1; -- (A)
- R := (F1 => 1, F2 => 2, F3 => 3); -- (B)
+ R := (F1 => 1, F2 => 2, F3 => 3); -- (B)
R := (F2 => 2, F3 => 3, others => 1); -- (C)
R := (others => 4); -- (D)
end Foo;
@@ -411,7 +475,7 @@
Steve:
-I think the following should be legal. Would your wording cause it to
+I think the following should be legal. Would your wording cause it to
be rejected?
package Parent is
@@ -429,7 +493,7 @@
X2 : T2 := (F1 => 37);
end Parent.Child;
-The point is that, at the point of derivation, T2 was derived from a
+The point is that, at the point of derivation, T2 was derived from a
private extension.
Randy:
@@ -442,12 +506,12 @@
Tucker:
One interesting way to illustrate the problem would be for
-Pak2.T2 to have its *own* "C1" component, which would be legal.
+Pak2.T2 to have its *own* "C1" component, which would be legal.
Clearly the C1 component from T1 should never be visible in T3, even in its own body.
Randy:
-True. That would be a bad example here, though, because it would make the named
+True. That would be a bad example here, though, because it would make the named
notation aggregate illegal.
I suppose one alternative would be to step back further and think about if we
@@ -541,9 +605,9 @@
Steve:
-Given that 7.3.1(3/3) only says that the characteristics "become
-visible" and determining the region in which this visibility applies
-is left as an exercise for the reader, I suppose Randy was only
+Given that 7.3.1(3/3) only says that the characteristics "become
+visible" and determining the region in which this visibility applies
+is left as an exercise for the reader, I suppose Randy was only
following precedent.
Randy:
@@ -774,12 +838,12 @@
One wonders if something similar should be done for operations (which
is the question Adam asked Oct 3, and will have its own AI).
-> If you modify my example as I described previously by giving T1 a
-> component named F2 (Randy: F is for "Field"), then 8.3(26/2)'s
-> statement that "a type extension is illegal if somewhere within its
-> immediate scope it has two visible components with the same name" does
-> not apply because T3 does not inherit a visible F2 component from T2.
-> Is this a problem? I'm trying to decide if there are any issues with
+> If you modify my example as I described previously by giving T1 a
+> component named F2 (Randy: F is for "Field"), then 8.3(26/2)'s
+> statement that "a type extension is illegal if somewhere within its
+> immediate scope it has two visible components with the same name" does
+> not apply because T3 does not inherit a visible F2 component from T2.
+> Is this a problem? I'm trying to decide if there are any issues with
> formal derived types, view conversions, etc. It seems odd that given
> X : T3,
> , X.F2 and T1 (X).F2 would denote different components.
@@ -796,7 +860,7 @@
private
type T1 is tagged record C1 : Integer; end record;
end Pkg1;
-
+
package Pkg1.Pkg2 is -- Private case
type T2 is new Pkg1.T1 with private;
X1 : T2 := (others => 1); -- Illegal by the existing rule.
@@ -811,7 +875,7 @@
private
X2 : T3 := (C1 | C2 => 1); -- Legal (one hopes)
end Pkg1.Pkg3;
-
+
with Pkg1.Pkg2, Pkg1.Pkg3;
package body Pkg1 is
type T4 is new Pkg2.T2 with record C1 : Integer; end record;
@@ -891,7 +955,7 @@
Given a specific tagged type T which is a descendant of a type A,
T is said to "extend A with hidden components" if any
ancestor of T which is not also an ancestor of A has
- hidden components.
+ hidden components.
AARM Note:
These particular characteristics of the type T follow
@@ -918,14 +982,14 @@
end Pkg2;
with Pkg1.Pkg2;
- package body Pkg1 is
+ package body Pkg1 is
type T3 is new Pkg2.T2 with null record;
-- T3 has hidden components here because T2
-- has hidden components (as seen from here)
-- even though T1 does not (as seen from here).
- end Pkg1;
-
+ end Pkg1;
+
In the Legality Rules section of 4.3.1, we replace 4.3.1(26)
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
@@ -967,7 +1031,7 @@
Yes, because it very view dependent. And it would help make clear that
it depends on the views and on 7.3.1.
-> 3) Are any wording tweaks needed in order to cope with interface
+> 3) Are any wording tweaks needed in order to cope with interface
> types?
Interfaces can't have components, so it is harmless to have an aggregate
@@ -986,7 +1050,7 @@
Randy:
-"limitedness" can't be a characteristic, as it is not inherited
+"limitedness" can't be a characteristic, as it is not inherited
(esp. in Ada 2005) -- it's declared in most cases. I don't think any
of the "characteristic" rules apply to it. "non-limitedness" could be
a characteristic (it gets added). I realize that the wording of 7.3.1
@@ -996,7 +1060,7 @@
what the rule really is? It's really been defined by the ACATS and the
various ramification AIs, not by wording in the standard.
-> Furthermore, I see no problem with reversing the polarity of the
+> Furthermore, I see no problem with reversing the polarity of the
> characteristic if we decide that we want that.
I think that would be a better approach.
@@ -1102,7 +1166,7 @@
T is said to "extend A with known components" if T is A or if
T is neither a tagged private type, a private extension, nor
an immediate descendant of a descendant of A which does not
- extend A with known components.
+ extend A with known components.
AARM Note:
Having known components and extending an ancestor type A with known
@@ -1128,14 +1192,14 @@
end Pkg2;
with Pkg1.Pkg2;
- package body Pkg1 is
+ package body Pkg1 is
type T3 is new Pkg2.T2 with null record;
-- T3 does not have known components here because T2
-- does not have known components (as seen from here)
-- even though T1 does (as seen from here).
- end Pkg1;
-
+ end Pkg1;
+
In the Legality Rules section of 4.3.1, replace 4.3.1(26)
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
@@ -1160,7 +1224,7 @@
within its scope, the full view determines the classes that include the
type, which components, entries, and protected subprograms are visible,
what attributes and other predefined operations are allowed, and whether
- the first subtype is static.
+ the first subtype is static.
replace the ending
"and whether the first subtype is static"
with
@@ -1215,7 +1279,7 @@
calculated for "ancestors" in general, because you can different answers
depending on the order that you look at the ancestors (with sufficiently
messy child packages).
-
+
> Given a specific tagged type T having an ancestor type A,
> T is defined to "extend A with known components" if T is a record
> extension of A, or if T extends a type B with known components
@@ -1245,5 +1309,80 @@
them against the batch of examples that we previously did.
****************************************************************
+
+!topic Extension aggregate ancestor with unknown discriminants
+!reference RM 4.3.2
+!from Adam Beneschan 09-03-09
+!discussion
+
+This arises from a case in GNAT bug 34507, which was also mentioned in a recent
+comp.lang.ada conversation involving constructor functions. However, I believe
+there may be a missing language rule; or perhaps just some clarifying language
+is needed.
+
+In this code (based on an example from the bug report):
+
+ package T_Pack is
+ type T (<>) is abstract tagged limited private;
+ private
+ type T (C: Character) is abstract tagged limited null record;
+ end T_Pack;
+
+ with T_Pack;
+
+ package S_Pack is
+ use T_Pack;
+ type S is new T with null record;
+ function Create return S;
+ end S_Pack;
+
+ package body S_Pack is
+ function Create return S is
+ begin
+ return S'(T with ???); -- CAN THIS POSSIBLY BE LEGAL?
+ end Create;
+ end S_Pack;
+
+I don't see how it's possible for any extension aggregate to be written with T
+as a subtype mark (where the full view of T is not visible). T is
+unconstrained, and therefore some constraint has to be provided with the value,
+but this is impossible since the discriminant C is not visible.
+
+Maybe the illegality follows from 4.3.2(6), since the inherited discriminant is
+"needed" but cannot be named (possibly because of AI05-115), thus making any
+possible extension aggregate of this sort illegal. Even so, trying to figure
+out whether it's legal gets into a murky area of whether a discriminant that is
+not visible, or "unknown", is considered to be "inherited" for the purposes of
+the Static Semantics section in 4.3.2(6). Then there's this case:
+
+ generic
+ type T (<>) is tagged private;
+ package Gen_Pack is
+ type New_T is new T with record
+ A : Integer;
+ end record;
+ function Create return New_T;
+ end Gen_Pack;
+
+ package body Gen_Pack is
+ function Create return New_T is
+ begin
+ return New_T' (T with A => 10);
+ end Create;
+ end Gen_Pack;
+
+I don't see how this could be allowed, since Gen_Pack could be instantiated with
+an unconstrained discriminated type. (GNAT allows the above generic to be
+declared, but gives an error on the aggregate if you instantiate with an
+unconstrained discriminated record type; looks like a contract model violation
+to me.)
+
+My suggestion: Change the first sentence of 4.3.2(5) to:
+
+If the ancestor_part is a subtype_mark, it shall denote a specific tagged
+subtype, and it shall not denote a type with unknown discriminants.
+
+Or something to that effect.
+
+****************************************************************
-
Questions? Ask the ACAA Technical Agent