Version 1.7 of ais/ai-00184.txt
!standard 04.06 (54) 99-07-27 AI95-00184/05
!class binding interpretation 97-03-19
!status Corrigendum 2000 99-07-27
!status WG9 approved 99-06-12
!status ARG approved 98-10-08 (7-0-0)
!status work item 97-03-19
!status received 97-03-19
!priority High
!difficulty Hard
!subject Definiteness and type derivation
!summary
The legality rules about object renaming are checked in the private part of an
instance. In a generic body, they are checked in an assume-the-worst manner:
it is illegal to rename a component that depends on a discriminant of a
variable whose nominal subtype is an untagged indefinite generic formal
derived type (or a descendant of such a type) unless the variable is aliased.
A view conversion to an indefinite object is constrained.
For a conversion of an object name to a tagged type to be a view conversion,
the object's nominal subtype has to be tagged.
!question
The definiteness of a type is not preserved by type derivation. A type with
defaulted discriminants may be derived from a type without defaulted
discriminants and vice-versa.
This makes it possible to rename a component of a record that later disappears
due to an assignment to the enclosing object, as shown in the following
examples:
1 - An example where the parent type is indefinite and the derived type is
definite:
type T1 (D1 : Boolean) is
record
case D1 is
when False =>
C1 : Integer;
when True =>
C2 : Float;
end case;
end record;
generic
type F is new T1;
X : in out F;
package G is
C1_Ren : Integer renames X.C1;
end G;
type T2 (D2 : Boolean := True) is new T1 (D1 => D2);
Y : T2;
package I is new G (T2, Y);
Y := (D1 => True, C2 => 3.0); --
The declaration of C1_Ren in the generic G is legal as per RM95 8.5.1(5),
because the formal type F is indefinite. But when G is instantiated with type
T2, the actual type is definite, so now we have renamed a component that may
disappear by assignment to the variable Y. Note that the declaration of
C1_Ren might be in the body of G, so we cannot avoid this problem by
rechecking RM95 8.5.1(5) on the instantiation.
2 - An example where the parent type is definite and the derived type is
indefinite:
type Definite_Parent (D1 : Integer := 6) is
record
F : String (1 .. D1);
end record;
type Indefinite_Child (D2 : Integer) is new Definite_Parent (D1 => D2);
Y : Definite_Parent;
procedure P (X : in out Indefinite_Child) is
C : Character renames X.F (3);
begin
X := (0, "");
--
end;
begin
P (Indefinite_Child (Y));
Assume that the implementation chooses to pass X by reference. Then,
6.4.1(10) says that there is an implicit view conversion to Indefinite_Child,
and the formal parameter X then denotes the result of this view conversion.
The result of the explicit view conversion is unconstrained, and the result
of the implicit view conversion is also unconstrained, hence X is
unconstrained, which violates the language design principle of NOTE 3.7(28).
One of the unpleasant consequences of this violation is that the assignment to
X doesn't raise an exception, and after the execution of this assignment C
denotes a non-existent component.
Note that if the implementation chooses to pass by copy, then there is an
implicit value conversion -- see 6.4.1(11-11.a). So in that case, there's no
problem.
!recommendation
(See wording.)
!wording
Change the beginning of RM95 4.6(5) to read: "A type_conversion whose operand
is the name of an object is called a view conversion if both its target type
and its operand type are tagged, or if it appears ..."
Add after RM95 4.6(56): "The object denoted by a view conversion to an
indefinite subtype is constrained."
Add to RM95 8.5.1(5): "In addition to the places where Legality Rules
normally apply, this rule applies also in the private part of an instance of a
generic unit. Furthermore, for a renaming that appears in the body of a
generic unit, the renamed entity shall not be a subcomponent that depends on a
discriminant of a variable whose nominal subtype is (descended from) an
untagged indefinite generic formal derived type, unless the variable is
aliased."
!discussion
The fix for example 1 is to forbid the renaming: even though the formal type
look indefinite, it is possible for the actual type to be definite. Note that
the manual already covers the case where C1_Ren is declared in the visible
part of the generic unit, because legality rules are checked in the instance
(RM95 12.3(11)). We are extending the legality rules for object renaming to
apply in the private part of the instance, and we are assuming the worst in
the body.
To fix example 2, we could forbid a view conversion that obtains an indefinite
view of an object whose nominal subtype is definite. However, such a view
conversion was legal in Ada 83, so this would be an incompatibility. It seems
better to mandate a check on the assignment to X: it is very surprising that
this check is not there in the first place.
At the April 1998 meeting, the following example was discussed, in addition to
those mentioned in the original question:
package P is
pragma Elaborate_Body;
type T (D : Integer) is private;
private
type T (D : Integer) is tagged
record
C : String (1 .. D);
end record;
end P;
with P;
package Q is
type NT (ND : Integer := 3) is new T (ND);
X : NT;
Y : NT (0);
end Q
with Q;
package body P is
C_Ren : Character renames T (Q.X).C (2);
begin
Q.X := Q.Y; --
end P;
This is similar to example 2 above, except that here we don't use a view
conversion to change the discriminant of Q.X. In this case the trouble
comes from the view conversion T (Q.X) in the declaration of C_Ren. As
long as all the types involved are tagged, renaming a component of a
view conversion works fine, because tagged types don't have defaulted
discriminants. But here we go through an untagged type to change the
discriminants. It is clear that the conversion T (Q.X) should not be
considered a view conversion.
!corrigendum 4.06(5)
Replace the paragraph:
A type_conversion whose operand is the name of an object is called a
view conversion if its target type is tagged, or if it appears as an actual
parameter of mode out or in out; other type_conversions are
called value conversions.
by:
A type_conversion whose operand is the name of an object is called a
view conversion if both its target type and operand type are tagged, or
if it appears as an actual parameter of mode out or in out; other
type_conversions are called value conversions.
!corrigendum 4.06(56)
Insert after the paragraph:
- Reading the value of the view yields the result of converting the
value of the operand object to the target subtype (which might raise
Constraint_Error), except if the object is of an access type and the view
conversion is passed as an out parameter; in this latter case, the value
of the operand object is used to initialize the formal parameter without
checking against any constraint of the target subtype (see 6.4.1).
the new paragraph:
- The object denoted by a view conversion to an
indefinite subtype is constrained.
!corrigendum 8.5.1(5)
Replace the paragraph:
The renamed entity shall not be a subcomponent that depends on
discriminants of a variable whose nominal subtype is unconstrained, unless
this subtype is indefinite, or the variable is aliased. A slice of an array
shall not be renamed if this restriction disallows renaming of the array.
by:
The renamed entity shall not be a subcomponent that depends on
discriminants of a variable whose nominal subtype is unconstrained, unless
this subtype is indefinite, or the variable is aliased. A slice of an array
shall not be renamed if this restriction disallows renaming of the array.
In addition to the places where Legality Rules normally apply, this rule applies
also in the private part of an instance of a generic unit. Furthermore, for a
renaming that appears in the body of a generic unit, the renamed entity shall
not be a subcomponent that depends on a discriminant of a variable whose nominal
subtype is (descended from) an untagged indefinite generic formal derived type,
unless the variable is aliased.
!ACATS test
A B-Test should be created containing violations of each of the new rules.
!appendix
!section 4.6(54)
!subject View conversion to an indefinite subtype
!reference RM95 4.6(54)
!from Pascal Leroy 97-03-10
!reference 97-15727.e Pascal Leroy 97-3-10>>
!discussion
The referenced paragraph states that, for a view conversion: "the subtype of
the view is constrained if ... the operand type is a descendant of the target
type, and has discriminants that were not inherited from the target type."
This paragraph doesn't say anything in the case where the operand type is not
a descendant of the target type. This makes it possible to rename a record
component which later disappears, as shown in the following example:
type Definite_Parent (D1 : Integer := 6) is
record
F : String (1 .. D1);
end record;
type Indefinite_Child (D2 : Integer) is new Definite_Parent (D1 => D2);
Y : Definite_Parent;
procedure P (X : in out Indefinite_Child) is
C : Character renames X.F (3);
begin
X := (0, "");
-- X.F (3) has disappeared!
end;
begin
P (Indefinite_Child (Y));
Assume that the implementation chooses to pass X by reference. Then,
6.4.1(10) says that there is an implicit view conversion to Indefinite_Child,
and the formal parameter X then denotes the result of this view conversion.
The result of the explicit view conversion is unconstrained, and the result
of the implicit view conversion is also unconstrained, hence X is
unconstrained, which violates the language design principle of NOTE 3.7(28).
One of the unpleasant consequences of this violation is that the assignment to
X doesn't raise an exception, and after the execution of this assignment C
denotes a non-existent component.
Note that if the implementation chooses to pass by copy, then there is an
implicit value conversion -- see 6.4.1(11-11.a). So in that case, there's no
problem.
****************************************************************
!topic Definiteness and type derivation
!reference RM95 3.4
!reference AI95-00184
!from Pascal Leroy 97-10-14
<<reference as: 1998-15825.c Pascal Leroy 1998-3-17>>
!discussion
The definiteness of a type is not preserved by type derivation. A type with
defaulted discriminants may be derived from a type without defaulted
discriminants and vice-versa.
This has a number of pathological consequences, as shown by the examples
below. Note that although these examples all have generics, the problems do
not stem from generics: AI95-00184 has an example without generics.
1 - A renamed subcomponent may disappear:
type T1 (D1 : Boolean) is
record
case D1 is
when False =>
C1 : Integer;
when True =>
C2 : Float;
end case;
end record;
generic
type F is new T1;
X : in out F;
package G is
C1_Ren : Integer renames X.C1;
end G;
type T2 (D2 : Boolean := True) is new T1 (D1 => D2);
Y : T2;
package I is new G (T2, Y);
The declaration of C1_Ren in the generic G is legal as per RM95 8.5.1(5),
because the formal type F is indefinite. But when G is instantiated with
type
T2, the actual type is definite, so now we have renamed a component that may
disappear by assignment to the variable Y. Note that the declaration of
C1_Ren might be in the body of G, so we cannot avoid this problem by
rechecking RM95 8.5.1(5) on the instantiation.
2 - It is possible to create an unconstrained object of an indefinite
subtype:
type T1 (D1 : Boolean := True) is ...;
type T2 (D2 : Boolean) is new T1 (D1 => D2);
generic
type F is new T1;
package G is
X : F;
end G;
package I is new G (T2);
The declaration of X in the generic G is legal because F has defaulted
discriminants. But when G is instantiated with T2, discriminants don't have
defaults, so we have created an object I.X without specifying a value for
its
discriminants. Again, the offending construct could be in the body of the
generic, so it cannot be detected by rechecking the instantiation.
****************************************************************
Questions? Ask the ACAA Technical Agent