Version 1.7 of ais/ai-00184.txt

Unformatted version of ais/ai-00184.txt version 1.7
Other versions for file 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); -- Oops! What happened to I.C1_Ren
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, ""); -- 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.
!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; -- Houston, we have a problem! 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:
the new paragraph:
!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