Version 1.22 of ais/ai-00216.txt

Unformatted version of ais/ai-00216.txt version 1.22
Other versions for file ais/ai-00216.txt

!standard B.03.03 (00)          05-09-28 AI95-00216/17
!standard B.03 (60.2)
!standard B.03 (74)
!class amendment 99-03-23
!status Amendment 200Y 03-02-17
!status WG9 approved 03-06-20
!status ARG Approved 7-0-1 03-02-07
!status work item 02-11-06
!status ARG Approved 6-0-3 02-06-22
!status work item 99-03-23
!status received 99-03-23
!priority Medium
!difficulty Hard
!subject Unchecked unions -- variant records with no run-time discriminant
!summary
We propose to formalize the rules for the pragma Unchecked_Union, which is now supported by several Ada 95 compilers. The proposed rules are intended to be flexible enough to accommodate all reasonable uses, while preserving a modicum of safety.
!problem
The Ada 95 standard does not include a mechanism for mapping C unions to Ada data structures. This capability is important enough that several Ada 95 compilers have defined a method to support unions.
!proposal
pragma Unchecked_Union (first_subtype_local_name);
This representation pragma is intended for use with a type with a variant part (potentially with further nested variant parts), that is to be manipulated in C using unions. The pragma specifies that the associated type should be given a representation that leaves no space for the discriminants of the type. Furthermore, the effect of this pragma includes an implicit suppress of Discriminant_Check on the specified type, and an implicit convention of C (which may be overridden).
The specified type may have a non-variant part preceding the variant part, which would correspond to a C struct containing a union as its last component. A variant may have multiple components, which would correspond to a C union with a struct as one of its elements. A variant may have a nested variant part, which would correspond to a nested C union. The type may have multiple discriminants, to support the possibly nested unions being selected along different dimensions.
The Ada type may, but need not, have defaults for all discriminants. All objects of the type, even if limited or allocated in the heap (and hence effectively constrained by the initial discriminant values), must be allocated the same size, which will be at least the maximum size required for any discriminant value. As with any type with a specified Convention, this should be the same as the corresponding construct in the specified language implementation. For C, this should be the size C would allocate for the corresponding struct/union. This is needed for the intended use of interfacing to C, because any whole-object assignments performed to or from such an object by the C code will generally involve this maximum size, even if they preserve the (conceptual) discriminant value.
Each discriminant of an object of an unchecked union type must be specified (explicitly or implicitly) when the object is created, even though its value is not stored, to enable appropriate default initialization of the appropriate variants, or to determine which fields should appear in a record aggregate.
Within the definition of an unchecked union type, the discriminants may not be used in a constraint in a component_definition, unless the type of the component is itself an unchecked union type. This is to ensure that the size of the component does not depend on the value of a discriminant. Note that a discriminant may be used to govern a discriminant part or as a default initial value for a component.
Outside the definition of the object's type, a discriminant of an object of an unchecked union type must not be read.
Constrained subtypes of an unchecked union type are permitted, as this may be necessary to specify the (initial) discriminant value for a variable or subcomponent having the type. It is erroneous to perform any operation (in C or Ada) that would have failed a discriminant check had the discriminant been present at run-time.
The pragma Unchecked_Union may be applied to a derived type, presuming it meets the requirements for the pragma. Converting the derived type to an unconstrained subtype of an ancestor (checked) type raises Program_Error, because there is no way to determine the values of the discriminants. Converting to a constrained subtype is permitted, as the discriminant values are implied by the constraint (as above, the conversion is erroneous if it would have failed a discriminant check). Converting from an ancestor (checked) type to the derived type is permitted, and simply drops the discriminants (and performs whatever other representation adjustments are necessary). If the target (unchecked) subtype is constrained, a constraint check is performed on the value of the checked type prior to dropping the discriminants. (These conversion rules are intended to allow an Ada program to primarily manipulate a checked type, and then convert to/from an unchecked type just before and after communicating with C code.)
A private type is never an unchecked union (even if its full type is). As usual, a derived type inherits the unchecked union representation aspect from its parent type. A formal derived type is an unchecked union if its specified ancestor is an unchecked union.
If an unchecked union completes a private type or private extension, the partial view must not have known discriminants or it must be an unchecked union. For contract model reasons, in an instantiation of a generic, if the actual type is an unchecked union, the formal type must not have with known discriminants, or it must be an unchecked union.
To avoid other kinds of generic contract violations, if an unchecked union is non-limited, all of the normal operations of a non-limited type exist, including assignment, equality, membership, 'Read, 'Write, 'Input, 'Output, etc. Assignment is defined in terms of a block copy on all bits of the representation. The other operations all raise Program_Error, because they generally require reading the value of the discriminant to give a meaningful result.
Record representation clauses are permitted for unchecked unions. No space is given for a discriminant; it is illegal to mention a discriminant explicitly in a component clause.
An implementation may support this pragma for tagged record types or record extensions, with implementation-defined semantics.
!wording
In paragraph B.3(60.2/1), replace
"a type T is eligible for convention C_Pass_By_Copy if T is a record type"
with
"a type T is eligible for convention C_Pass_By_Copy if T is an
unchecked union type or if T is a record type".
Delete B.3(74) (which says that we don't support unions).
B.3.3 Pragma Unchecked_Union
[A pragma Unchecked_Union specifies an interface correspondence between a given discriminated type and some C union. The pragma specifies that the associated type shall be given a representation that leaves no space for its discriminant(s).]
Syntax
The form of a pragma Unchecked_Union is as follows:
pragma Unchecked_Union (first_subtype_local_name);
Legality Rules
Unchecked_Union is a representation pragma, specifying the unchecked union aspect of representation.
The first_subtype_local_name of a pragma Unchecked_Union shall denote an unconstrained discriminated record subtype having a variant_part.
A type to which a pragma Unchecked_Union applies is called an unchecked union type. A subtype of an unchecked union type is defined to be an unchecked union subtype. An object of an unchecked union type is defined to be an unchecked union object.
All component subtypes of the type shall be C-compatible.
If a component subtype of the type is subject to a per-object constraint, then the component subtype shall be an unchecked union subtype.
Any name that denotes a discriminant of an object of an unchecked union type shall occur within the declarative region of the type.
A component declared in a variant_part of an unchecked union type shall not have a controlled, protected, or task part.
The completion of an incomplete or private type declaration having a known_discriminant_part shall not be an unchecked union type.
An unchecked union subtype shall only be passed as a generic actual parameter if the corresponding formal type has no known discriminants or is an unchecked union type.
AARM Note: This includes formal private types without a known_discriminant_part, formal derived types that do not inherit any discriminants (Formal derived types do not have known_discriminant_parts), and formal derived types that are unchecked union types.
Static Semantics
An unchecked union type is eligible for convention C.
All objects of the type have the same size.
Discriminants of objects of the type are of size zero.
Any check which would require reading a discriminant of an unchecked union object is suppressed (see 11.5). These checks include:
* The check performed when addressing a variant component (i.e., a component that was declared in a variant part) of an unchecked union object that the object has this component (see 4.1.3).
* Any checks associated with a type or subtype conversion of a value of an unchecked union type (see 4.6). This includes, for example, the check associated with the implicit subtype conversion of an assignment statement.
* The subtype membership check associated with the evaluation of a qualified expression (see 4.7) or an uninitialized allocator (see 4.8).
AARM Note: If a suppressed check would have failed, execution is erroneous (see 11.5). An implementation is always allowed to make a suppressed check if it can somehow determine the discriminant value.
Dynamic Semantics
A view of an unchecked union object [(including a type conversion or function call)] has "inferable discriminants" if it has a constrained nominal subtype, unless the object is a component of an enclosing unchecked union object that is subject to a per-object constraint and the enclosing object lacks inferable discriminants.
An expression of an unchecked union type has "inferable discriminants" if it is either a name of an object with inferable discriminants or a qualified expression whose subtype_mark denotes a constrained subtype.
Program_Error is raised in the following cases:
Evaluation of the predefined equality operator for an unchecked union type if either of the operands lacks inferable discriminants.
Evaluation of the predefined equality operator for a type which has a subcomponent of an unchecked union type whose nominal subtype is unconstrained.
Evaluation of a membership test if the subtype_mark denotes a constrained unchecked union subtype and the expression lacks inferable discriminants.
Conversion from a derived unchecked union type to an unconstrained non-unchecked-union type if the operand of the conversion lacks inferable discriminants.
Execution of the default implementation of the Write or Read attribute of an unchecked union type.
Execution of the default implementation of the Output or Input attribute of an unchecked union type if the type lacks default discriminant values.
Implementation Permissions
An implementation may require that pragma Controlled be specified for the type of an access subcomponent of an unchecked union type.
Notes
The use of an unchecked union to obtain the effect of an unchecked conversion results in erroneous execution (see 11.5). Execution of the following example is erroneous even if Float'Size = Integer'Size:
type T (Flag : Boolean := False) is record case Flag is when False => F1 : Float := 0.0; when True => F2 : Integer := 0; end case; end record; pragma Unchecked_Union (T);
X : T; Y : Integer := X.F2; -- erroneous
!example
Given the C type:
struct sym {
int id; char *name; union {
struct { struct sym *obj_type; int obj_val_if_known; } obj; struct { struct sym *pkg_first_component; int pkg_num_components; } pkg;
} u;
};
This would map to the following unchecked union type in Ada:
type Sym; type Sym_Ptr is access Sym;
type Sym_Kind_Enum is (Obj_Kind, Pkg_Kind);
type Sym(Kind : Sym_Kind_Enum := Sym_Kind_Enum'First) is record
Id : Integer := Assign_Id(Kind); Name : Interfaces.C,Strings.Chars_Ptr; case Kind is when Obj_Kind => Obj_Type : Sym_Ptr; Obj_Val_If_Known : Integer := -1; when Pkg_Kind => Pkg_First_Component : Sym_Ptr; Pkg_Num_Components : Integer := 0; end case;
end record; pragma Unchecked_Union(Sym);
!discussion
Several Ada 95 compilers now support a pragma Unchecked_Union for specifying that the discriminant of a variant record should not be present at run-time, thereby matching a C union. However, these compilers differ in the legality rules they enforce relative to the type.
This proposal is designed to allow what in C is essentially one (named) type definition, to similarly become one type definition in Ada. It is quite common to use anonymous unions and structs nested within one another in C. In Ada, the natural mapping for this is non-variant and variant parts, and nested variants. The need to eliminate the storage for the discriminant exists for these more complex uses of "union" in C just as much as it exists for a very simple use of "union". Hence, we propose to allow unchecked union types to include non-variant parts along with variant parts, to include multiple components per variant, and to support nested variants, to maximize the likelihood that there is a natural mapping possible between the C type structure and the Ada type structure.
We have also defined rules that should eliminate generic contract model violations related to unchecked unions, as this seems important if such types are going to be used in relatively portable C interfacing code with compilers that may share generics.
!corrigendum B.3(60.2/1)
Replace the paragraph:
The eligibility rules in B.1 do not apply to convention C_Pass_By_Copy. Instead, a type T is eligible for convention C_Pass_By_Copy if T is a record type that has no discriminants and that only has components with statically constrained subtypes, and each component is C-compatible.
by:
The eligibility rules in B.1 do not apply to convention C_Pass_By_Copy. Instead, a type T is eligible for convention C_Pass_By_Copy if T is an unchecked union type or if T is a record type that has no discriminants and that only has components with statically constrained subtypes, and each component is C-compatible.
!corrigendum B.3(74)
Delete the paragraph:
11 There is no explicit support for C's union types. Unchecked conversions can be used to obtain the effect of C unions.
!corrigendum B.3.3(1)
Insert new clause:
A pragma Unchecked_Union specifies an interface correspondence between a given discriminated type and some C union. The pragma specifies that the associated type shall be given a representation that leaves no space for its discriminant(s).
Syntax
The form of a pragma Unchecked_Union is as follows:
pragma Unchecked_Union (first_subtype_local_name);
Legality Rules
Unchecked_Union is a representation pragma, specifying the unchecked union aspect of representation.
The first_subtype_local_name of a pragma Unchecked_Union shall denote an unconstrained discriminated record subtype having a variant_part.
A type to which a pragma Unchecked_Union applies is called an unchecked union type. A subtype of an unchecked union type is defined to be an unchecked union subtype. An object of an unchecked union type is defined to be an unchecked union object.
All component subtypes of an unchecked union type shall be C-compatible.
If a component subtype of an unchecked union type is subject to a per-object constraint, then the component subtype shall be an unchecked union subtype.
Any name that denotes a discriminant of an object of an unchecked union type shall occur within the declarative region of the type.
A component declared in a variant_part of an unchecked union type shall not have a controlled, protected, or task part.
The completion of an incomplete or private type declaration having a known_discriminant_part shall not be an unchecked union type.
An unchecked union subtype shall only be passed as a generic actual parameter if the corresponding formal type has no known discriminants or is an unchecked union type.
Static Semantics
An unchecked union type is eligible for convention C.
All objects of an unchecked union type have the same size.
Discriminants of objects of an unchecked union type are of size zero.
Any check which would require reading a discriminant of an unchecked union object is suppressed (see 11.5). These checks include:
Dynamic Semantics
A view of an unchecked union object (including a type conversion or function call) has inferable discriminants if it has a constrained nominal subtype, unless the object is a component of an enclosing unchecked union object that is subject to a per-object constraint and the enclosing object lacks inferable discriminants.
An expression of an unchecked union type has inferable discriminants if it is either a name of an object with inferable discriminants or a qualified expression whose subtype_mark denotes a constrained subtype.
Program_Error is raised in the following cases:
Implementation Permissions
An implementation may require that pragma Controlled be specified for the type of an access subcomponent of an unchecked union type.
NOTES
15 The use of an unchecked union to obtain the effect of an unchecked conversion results in erroneous execution (see 11.5). Execution of the following example is erroneous even if Float'Size = Integer'Size:

type T (Flag : Boolean := False) is record case Flag is when False => F1 : Float := 0.0; when True => F2 : Integer := 0; end case; end record; pragma Unchecked_Union (T);
X : T; Y : Integer := X.F2; -- erroneous
!ACATS test
Tests should be created to check on the implementation of this feature.
!appendix

Randy Brukardt  99-03-29

I've reformatted Tucker's submission into the format discussed at
the recent ARG meeting. (This is version /01). Note that I haven't made any
of the changes considered at that meeting.

*************************************************************

From: Jean-Pierre Rosen
Sent: Friday, October 1, 1999 at 2:09 AM

Agreed, just a suggestion: [Editor's note, this a comment on version 2]

> To avoid other kinds of generic contract violations, if the type is
> non-limited, all of the normal operations of a non-limited type exist,
> including assignment, equality, membership, 'Read, 'Write, 'Input,
> 'Output, etc.  Assignment is defined in terms of a block copy on all bits
> of the representation.  The other operations all raise Program_Error,
> because they generally require reading the value of the discriminant
> to give a meaningful result.
>
It seems to me that completely forbidding the use of  'Read and 'Write is
too strict; there are certainly cases where you want to send such types over
a network!
'Read and 'Write could be defined like the case where there are no default
values (i.e. no discriminants written).
'input and 'output could raise Program_Error, or do the same.

OTOH, it is always possible for the user to redefine 'read and 'write...
Well, in that case, it is important to NOT define 'input and 'output as
raising P_E, since their normal behaviour (for discriminated defaulted
types) is simply to call 'Read and 'Write.

*************************************************************

From: Robert A Duff
Sent: Friday, October 1, 1999 at 8:57 AM

> It seems to me that completely forbidding the use of  'Read and 'Write is
> too strict; there are certainly cases where you want to send such types over
> a network!
> 'Read and 'Write could be defined like the case where there are no default
> values (i.e. no discriminants written).
> 'input and 'output could raise Program_Error, or do the same.
>
> OTOH, it is allways possible for the user to redefine 'read and 'write...

As Tucker mentioned in the meeting, another way to get the operations
that raise Program_Error is to first define the variant record as a
normal type, then derive from it, and declare the derived type to be an
unchecked_union.  Then you can convert values to the parent type, and do
all the normal stuff without getting Program_Error.

Oh, and by the way, we decided that you could convert to constrained
subtypes of the parent type (contrary to what Tuck's write-up says).
Of course you can't convert to an unconstrained subtype, because the
compiler can't know what the discriminant value should be.

> Well, in that case, it is important to NOT define 'input and 'output as
> raising P_E, since their normal behaviour (for discriminated defaulted
> types) is simply to call 'Read and 'Write.

Good point.

- Bob

*************************************************************

From: Steve Baird
Sent: Monday, January 28, 2002 at  2:40 PM

B.3.3 Pragma Unchecked_Union

[A pragma Unchecked_Union specifies an interface correspondence
between a given discriminated type and some C union. The pragma
specifies that the associated type shall be given a representation
that leaves no space for its discriminant(s).]

                Syntax

The form of pragma Unchecked_Union is as follows:

    pragma Unchecked_Union (first_subtype_local_name);

                Legality Rules

Unchecked_Union is a representation pragma, specifying the unchecked
union aspect of representation.

The first_subtype_local_name of a pragma Unchecked_Union shall denote
an unconstrained discriminated record subtype having a variant_part.

The type is defined to have an unchecked union type. A subtype of an
unchecked union type is defined to have an unchecked union subtype.
An object of an unchecked union type is defined to have an unchecked union
object.

All component subtypes of the type shall be C-compatible.

If a component subtype of the type is subject to a per-object constraint,
then the component subtype shall be an unchecked union subtype.

The type shall not be a by-reference type.

                Static Semantics

An unchecked union type is eligible for conventions C and C_Pass_By_Copy,
as is any subtype of the type.

Discriminant_Check is suppressed for the type.

All objects of the type shall have the same size.

Discriminants of objects of the type shall be of size zero.

Any name which denotes a discriminant of an object of an
Unchecked_Union type shall occur within the declarative region of the type.

An Unchecked_Union subtype shall not be passed as a generic actual parameter
if the corresponding formal type has a known_discriminant_part or
is a formal derived type which is not an unchecked union type.

                Dynamic Semantics

A view of an unchecked union object [(including a type conversion or
function call)] is said to have "inferable discriminants" if it has a
constrained nominal subtype, unless the object is a component of an
enclosing unchecked union object which is subject to a per-object constraint
and the enclosing object lacks inferable discriminants.

An expression of an Unchecked_Union type is said to have "inferable
discriminants" if it is either a name of an object with inferable
discriminants
or a qualified expression whose subtype_mark denotes a constrained subtype.

The predefined equality operator for an Unchecked_Union type
raises Program_Error if either of the operands lacks inferable
discriminants. [This includes the case where the equality operator
is invoked implicitly by the equality operator for an enclosing composite
type - if the Unchecked_Union component is unconstrained, Program_Error
is raised].

Evaluation of a membership test raises Program_Error if the
subtype_mark denotes a constrained Unchecked_Union subtype and the
expression lacks inferable discriminants.

Conversion from a derived Unchecked_Union type to an unconstrained
non-Unchecked_Union ancestor type raises Program_Error if the operand
of the conversion lacks inferable discriminants.

Unless overridden by an attribute_definition_clause, execution of
the Write or Read attribute of an Unchecked_Union type
raises Program_Error. The same holds for the Output and Input attributes
if the type lacks default discriminant values.


-------- Discussion -------

1) Consider the following example:

     with Interfaces;
     package Uu is
       type Uu_32 (Is_Signed : Boolean) is
          record
            case Is_Signed is
                when False =>
                  Unsigned : Interfaces.Unsigned_32;
                when True =>
                  Signed : Interfaces.Integer_32;
            end case;
          end record;
      pragma Unchecked_Union (Uu_32);

       X : Uu_32 := (False, Interfaces.Unsigned_32'Last);
       Y : Interfaces.Integer_32 := X.Signed;
     end Uu;

   Elaboration of this package results in erroneous execution. If it is
   intended that this should behave as some sort of non-erroneous alternative
   form of Unchecked_Conversion, then the definition needs to be changed.

2) The various operations listed as raising Program_Error (i.e. certain
   equality tests, membership tests, conversions, and streaming operations)
   could be defined to be bounded errors, where the implementation would have
   the option of either raising Program_Error or returning the correct
   answer (typically, the latter alternative would only be selected
   if the implementation was somehow able to infer discriminant values
   for the object).

   Consider the following example:

     procedure Uu is
       type Uu_32 is <as above> ;

       subtype Signed_Uu is Uu_32 (Is_Signed => True);

       generic
         Unknown_Discrim : Uu_32;
       package G is
         Flag1 : constant Boolean := Unknown_Discrim in Signed_Uu;
       end G;

       package body G is
         Flag2 : constant Boolean := Unknown_Discrim in Signed_Uu;
       end G;

       Known_Discrim : constant Signed_Uu := (True, 33);

       package I is new G (Unknown_Discrim => Known_Discrim);
     begin
       null;
     end;

   An implementation (particularly one which does not share code for generics)
   might have to go out of its way to ignore known information in order to
   raise Program_Error instead of returning the correct answer for these
   membership tests.

3) It has been suggested that defining the dynamic semantics of Unchecked_Union
   in terms of check suppression leads to definitional problems in the
   cases of imported objects declared in C code, values created via unchecked
   conversion, values created via streaming, etc. What is the "correct"
   (albeit missing) discriminant value in these cases? What matters is that
   some appropriate discriminant value exists (even if an oracular consultation
   would be needed to ascertain its value) and, in particular, that
   the assumption that it exists does not lead to a contradiction.

4) Would the term "implied discriminants" be preferable to "inferable
   discriminants"?

5) Propagated discriminant constraints where both the enclosing type and
   the component type are unchecked union types introduce definitional
   complications. Some of the rules could be simpler if constructs such as
       type Uu_32 is <as above> ;

       type Another_Uu (Is_Signed : Boolean) is
         record
           F : Uu_32 (Is_Signed => Is_Signed);
           ...
         end record;
       pragma Unchecked_Union (Another_Uu);
   were illegal.

6) Requiring that the type not be By_Reference is just a convenient
   way of avoiding interactions with finalization, tags, view
   conversions, and limited types. For most implementations, this is
   already a consequence of the C-compatibility requirement.

7) The requirement that constrained objects must have the same
   size as unconstrained objects stems from C.

8) Checking is suppressed when converting from a non-Unchecked_Union
   ancestor type to a constrained Unchecked_Union derived subtype.
   This is a logical consequence of suppressing Discriminant_Check
   for the Unchecked_Union type, but contradicts the earlier AI.
   Adding a rule of the form "discriminant checks are suppressed,
   except that ... " does not seem like a good idea. Opening that can
   of worms would lead to questions like "what about converting an
   operand which has inferable discriminants? what about a variant
   field access where the prefix has inferable discriminants?".

9) If a mechanism is introduced for reinstating suppressed checks
   (see AI-224), it must be made clear that the check suppression
   implied by an Unchecked_Union pragma is irrevocable.

*************************************************************

From: Tucker Taft
Sent: Monday, January 28, 2002 at  4:22 PM

"Baird, Steve" wrote:

> B.3.3 Pragma Unchecked_Union
>
> [A pragma Unchecked_Union specifies an interface correspondence
> between a given discriminated type and some C union. The pragma
> specifies that the associated type shall be given a representation
> that leaves no space for its discriminant(s).]
>
>                 Syntax
>
> The form of pragma Unchecked_Union is as follows:
>
>     pragma Unchecked_Union (first_subtype_local_name);
>
>                 Legality Rules
>
> Unchecked_Union is a representation pragma, specifying the unchecked
> union aspect of representation.
>
> The first_subtype_local_name of a pragma Unchecked_Union shall denote
> an unconstrained discriminated record subtype having a variant_part.
>
> The type is said to be an unchecked union type. A subtype of an
> unchecked union type is said to be an unchecked union subtype.
> An object of an unchecked union type is said to be an unchecked union
> object.
>
> All component subtypes of the type shall be C-compatible.
>
> If a component subtype of the type is subject to a per-object constraint,
> then the component subtype shall be an unchecked union subtype.
>
> The type shall not be a by-reference type.

This seems heavy handed.  Is there a clear reason why
one shouldn't have an atomic or volatile component in an
unchecked-union?

>                 Static Semantics
>
> An unchecked union type is eligible for conventions C and C_Pass_By_Copy,
> as is any subtype of the type.

I thought the convention was by default "C" but that could be overridden.

>
> Discriminant_Check is suppressed for the type.
>
> All objects of the type shall have the same size.
>
> Discriminants of objects of the type shall be of size zero.
>
> Any name which denotes a discriminant of an object of an
> Unchecked_Union type shall occur within the declarative region of the type.

Discriminant names can also appear in named aggregates -- in fact they must.

Given your concept of "inferrable" discriminants, why not allow
referring to the discriminants of an object with inferrable discriminants?


>
> An Unchecked_Union subtype shall not be passed as a generic actual parameter
> if the corresponding formal type has a known_discriminant_part or
> is a formal derived type which is not an unchecked union type.
>
>                 Dynamic Semantics
>
> A view of an unchecked union object [(including a type conversion or
> function call)] is said to have "inferable discriminants" if it has a
> constrained nominal subtype, unless the object is a component of an
> enclosing unchecked union object which is subject to a per-object constraint
> and the enclosing object lacks inferable discriminants.
>
> An expression of an Unchecked_Union type is said to have "inferable
> discriminants" if it is either a name of an object with inferable
> discriminants or a qualified expression whose subtype_mark denotes a
> constrained subtype.
>
> The predefined equality operator for an Unchecked_Union type
> raises Program_Error if either of the operands lacks inferable
> discriminants. [This includes the case where the equality operator
> is invoked implicitly by the equality operator for an enclosing composite
> type - if the Unchecked_Union component is unconstrained, Program_Error
> is raised].
>
> Evaluation of a membership test raises Program_Error if the
> subtype_mark denotes a constrained Unchecked_Union subtype and the
> expression lacks inferable discriminants.
>
> Conversion from a derived Unchecked_Union type to an unconstrained
> non-Unchecked_Union ancestor type raises Program_Error if the operand
> of the conversion lacks inferable discriminants.

Why does it matter whether the other type is an ancestor?  Presuming
we are talking untagged types, all that is required is that they
have a common ancestor for them to be convertible.

I would drop the whole mention of "derived" and "ancestor" since that
is better handled by the normal legality rules.

*************************************************************

From: Steve Baird
Sent: Monday, January 28, 2002 at  6:33 PM

> From: Tucker Taft
> ...
> This seems heavy handed.  Is there a clear reason why
> one shouldn't have an atomic or volatile component in an
> unchecked-union?

Fair enough. I was just trying to avoid enumerating all the
constructs mentioned in discussion point #6.

> I thought the convention was by default "C" but that
> could be overridden.

This is a way of bypassing the C vs. C_Pass_By_Copy
question.

> Discriminant names can also appear in named aggregates

Good point; I agree.

> Given your concept of "inferrable" discriminants, why not
> allow referring to the discriminants of an object with
> inferrable discriminants?

That's certainly an option. I had thought of this concept
as only being part of the dynamic semantics of the language,
not the static semantics, but your idea seems reasonable.

> Why does it matter whether the other type is an ancestor?
Good point. My mistake.

*************************************************************

From: Randy Brukardt
Sent: Monday, February  4, 2002 at  8:22 PM

> 9) If a mechanism is introduced for reinstating suppressed checks
>    (see AI-224), it must be made clear that the check suppression
>    implied by an Unchecked_Union pragma is irrevocable.

I'm not suppressing of checks is the right model here. An implementation is
allowed to ignore check suppression anytime it wants. I'm not sure that an
implementation could do any useful checking, but it seems like the model ought
to be that there are no checks. That might be harder to define, though.

*************************************************************

From: Steve Baird
Sent: Tuesday, February  5, 2002 at  6:01 PM

I believe that suppression is the right model. An implementation
is allowed to ignore check suppression in the case of an unchecked_union
type. If an implementation is able to gather enough information from
somewhere (certainly not from the zero-sized discriminant fields) to
perform a check, it may do so.

What is new about this form of suppression is that ignoring it
is, in general, much harder (i.e. effectively impossible) than ignoring
the suppression of other checks.

I readily concede that the notion of a discriminant which exists in some
theoretical sense (e.g. its value may be inferred in some cases)
but occupies no storage is a bit odd.

To me, the compelling advantage of this approach is its simplicity; only
one line of RM text is needed because it uses an existing
language mechanism.

Inventing a similar-but-different set of rules expressly for
Unchecked_Union would be much more complicated.

*************************************************************

From: Randy Brukardt
Sent: Wednesday, February  6, 2002 at  6:05 PM

> To me, the compelling advantage of this approach is its simplicity; only
> one line of RM text is needed because it uses an existing language mechanism.

Except that it messes up Unsuppress further; as the one with that short straw,
I'm not amused :-)

*************************************************************

From: Robert Dewar
Sent: Wednesday, February  6, 2002 at  7:30 PM

You just say that Unsuppress unsupresses checks previously suppressed by
Suppress, then that does not include the unchecked union stuff.

*************************************************************

From: Steve Baird
Sent: Tuesday, March 26, 2002 at  6:33 PM

The following revised wording for AI-216 incorporates the
suggestions that were made at the Cupertino ARG meeting.

   -- Steve

----

B.3(60.2/1) - replace
    "a type T is eligible for convention C_Pass_By_Copy if T is a record type"
with
    "a type T is eligible for convention C_Pass_By_Copy if T is an
     unchecked union type or if T is a record type".

The resulting paragraph reads:
    The eligibility rules in B.1 do not apply to convention C_Pass_By_Copy.
    Instead, a type T is eligible for convention C_Pass_By_Copy if
    T is an unchecked union type or if T is a record type that has no
    discriminants and that only has components with statically constrained
    subtypes, and each component is C-compatible.

----


B.3.3 Pragma Unchecked_Union

[A pragma Unchecked_Union specifies an interface correspondence
between a given discriminated type and some C union. The pragma
specifies that the associated type shall be given a representation
that leaves no space for its discriminant(s).]

                Syntax

The form of pragma Unchecked_Union is as follows:

    pragma Unchecked_Union (first_subtype_local_name);

                Legality Rules

Unchecked_Union is a representation pragma, specifying the unchecked
union aspect of representation.

The first_subtype_local_name of a pragma Unchecked_Union shall denote
an unconstrained discriminated record subtype having a variant_part.

The type is said to be an unchecked union type. A subtype of an
unchecked union type is said to be an unchecked union subtype.
An object of an unchecked union type is said to be an unchecked union
object.

All component subtypes of the type shall be C-compatible.

If a component subtype of the type is subject to a per-object constraint,
then the component subtype shall be an unchecked union subtype.

Any name which denotes a discriminant of an object of an
unchecked union type shall occur within the declarative region of the type.

The completion of an incomplete or private type declaration having a
known_discriminant_part shall not be an unchecked union type.

An unchecked union subtype shall not be passed as a generic actual parameter
if the corresponding formal type has a known_discriminant_part or
is a formal derived type which is not an unchecked union type.

                Static Semantics

An unchecked union type is eligible for convention C.

Discriminant_Check is suppressed for the type.

All objects of the type have the same size.

Discriminants of objects of the type are of size zero.

                Dynamic Semantics

A view of an unchecked union object [(including a type conversion or
function call)] is said to have "inferable discriminants" if it has a
constrained nominal subtype, unless the object is a component of an
enclosing unchecked union object which is subject to a per-object constraint
and the enclosing object lacks inferable discriminants.

An expression of an unchecked union type is said to have "inferable
discriminants" if it is either a name of an object with inferable discriminants
or a qualified expression whose subtype_mark denotes a constrained subtype.

Program_Error is raised in the following cases:

    Evaluation of the predefined equality operator for an unchecked union type
    if either of the operands lacks inferable discriminants. [This includes the
    case where the equality operator is invoked implicitly by the equality
    operator for an enclosing composite type - if the unchecked union
    component subtype is unconstrained, Program_Error is raised].

    Evaluation of a membership test if the subtype_mark denotes a constrained
    unchecked union subtype and the expression lacks inferable discriminants.

    Conversion from a derived unchecked union type to an unconstrained
    non-unchecked-union type if the operand of the conversion lacks inferable
    discriminants.

    Execution of the default implementation of the Write or Read attribute
    of an unchecked union type.

    Execution of the default implementation of the Output or Input attribute
    of an unchecked union type if the type lacks default discriminant values.

*************************************************************

From: Randy Brukardt
Sent: Wednesday, April 17, 2002 at  7:18 PM

> The following revised wording for AI-216 incorporates the
> suggestions that were made at the Cupertino ARG meeting.

This revised wording is missing a rule mentioned in the minutes:

  Steve (Baird) points out that we can't handle finalization here (because we
  can't know whether a controlled component exists or not, so we can't
  implement the as-if semantics), so that has to be disallowed. Therefore,
  replace the rule with "The type shall not have a controlled part."

I've added the following to the Legality Rules section of the AI (along with Steve's other changes):

   An unchecked union type shall not have a controlled part.

(The slight wording change is simply to make the rule make sense in context.)

*************************************************************

From: Randy Brukardt
Sent: Thursday, April 18, 2002 at 11:05 PM

> This revised wording is missing a rule mentioned in the minutes:

An out of band discussion on this rule has covered the following points:

C-compatibility doesn't quite do the job here. We shouldn't make it
impossible for an implementation to support controlled types as C-compatible
(some C++ types make most naturally map to controlled types), so a separate
rule has value.

If we have a separate rule, we should include everything that has
finalization semantics (i.e. protected types and tasks), because there
doesn't seem to be any reason to play favorites here. Omitting one or both
of them will just look weird.

The rule really only needs to cover components declared in a variant_part
(not the object as a whole, not any non-variant components). However, that
is a somewhat harder wording problem, and it is unclear that it is worth it.

The wording to limit the rule's coverage to just variants would be something
like:

A component declared in a variant_part of an unchecked union type shall not
have a controlled, protected, or task part.

('Part' here is the technical term defined in 3.2(6), which covers the
component itself and any subcomponents.)

Ignoring the variant_part issue gives us:

An unchecked union type shall not have a controlled, protected, or task
subcomponent.

which may be easier to implement. But the first rule is what's required.

*************************************************************

From: Michael Yoder
Sent: Thursday, October 31, 2002 at  3:39 PM

This is a comment on AI-00216, Unchecked Unions.

There's an issue that deserves at least some mention, namely, this would
be the first Ada feature that directly breaks the ability to do precise
garbage collection[1]. The ability could be preserved by adding this
sentence:

A component declared in a variant_part of an unchecked union type shall
not be of an access type unless that type is one to which pragma
Controlled applies explicitly or implicitly.

Alternatively, it could be noted (as implementation advice?) that when
such a component is present, and garbage collection is in use, it is
generally necessary to use so-called "conservative" garbage collection
techniques for areas of storage that might or might not represent access
values.

[1] I avoid the issue of whether using pragma Interface indirectly
breaks this ability; I don't think it should automatically do so.

*************************************************************

From: Robert Dewar
Sent: Thursday, October 31, 2002 at  5:00 PM

<<There's an issue that deserves at least some mention, namely, this would
be the first Ada feature that directly breaks the ability to do precise
garbage collection[1]. The ability could be preserved by adding this
sentence:>>

First, of all, I wish we would not regard Unchecked_Union as a first class
citizen when it comes to features. It is a low level kludge, designed to be
used only to model C types (that is why incidentally I strongly oppose the
efforts to generalize it unnecessarily, they will encourage people to think
of it the way that is represented in the paragraph above, as a "feature"
in Ada that breaks things).

In fact the odd thing is that the claim above is completely wrong. It is
absolutely clear that Unchecked_Conversion, and address overlays from the
use of address clauses have exactly the same effect and of course I know
that Michael knows this.

So how could be make this statement? My guess is that the idea is that
unchecked_conversion and address overlay are obviously low level junk
stuff that would e.g. break garbage collection but Unchecked_Union is,
for reasons that I cannot fathom, regarded as being a respectable
feature in the language.

<<A component declared in a variant_part of an unchecked union type shall
not be of an access type unless that type is one to which pragma
Controlled applies explicitly or implicitly.>>

I object to this proposal, it has the effect of trying to elevate
unchecked_union to a level of respectability that is unsuited to it.
Unchecked_Union should be regarded as equivalent to the use of
Unchecked_Conversion. Indeed it is basically syntactic sugar to make
the use of unchecked conversion a little easier to use in this case.

<<Alternatively, it could be noted (as implementation advice?) that when
such a component is present, and garbage collection is in use, it is
generally necessary to use so-called "conservative" garbage collection
techniques for areas of storage that might or might not represent access>>

But general address arithmetic (permitted in Ada, but not in C), can break
even conservative garbage collection. For example, have a look at the
implementation of the Tables unit in the GNAT sources. It provides a
dynamic table facility using virtual origins for addressing using
address arithmetic. Of course virtual origins break conservative
garbage collection (because the virtual pointer is outside the
allocated array block). Now of course the coding of Table is strictly
speaking well into the implementation dependent/defined area, but in
practice it is likely to work on most Ada implementations (and of course
works fine in all GNAT implementations).

Don't try to make Unchecked_Union into something it is not!

*************************************************************

From: Michael Yoder
Sent: Thursday, October 31, 2002 at  6:19 PM

>So how could be make this statement? My guess is that the idea is that
>unchecked_conversion and address overlay are obviously low level junk
>stuff that would e.g. break garbage collection but Unchecked_Union is,
>for reasons that I cannot fathom, regarded as being a respectable
>feature in the language.

Wrong. Unchecked_Conversion, address clauses, and address arithmetic are
just as respectable, as far as I'm concerned, as any non-deprecated
feature of the language. They are different because, from the point of
view of a garbage collector, it's perfectly feasible and reasonable to
ignore their existence when collecting[1]: uses that break garbage
collection are erroneous, or at least nonportable. With unchecked
unions, the null treatment is a wrong choice, absent some guidance. The
goal of the suggestion was to provide the guidance.

Think before you condescend!

[1] An implementation might need to mark variables with address clauses
as immovable.

*************************************************************

From: Robert Dewar
Sent: Thursday, October 31, 2002 at  7:30 PM

But any program you write with unchecked unions can be easily transcribed
to use unchecked conversion, the transformation is quite straightforward.
All you do is to define an array of storage elements, and then whenever
you access it, you unchecked convert to the record form you want before
extracting the field you want.

The only point of unchecked union is to make this procedure (a very familiar
one for modeling C unions in the absence of this feature) a little more
convenient, and also to ensure that things are layed out to match the
C layout.

So I am not "condescending" here, simply pointing out this obvious equivalence
and suggesting that anything that breaks this equivalence is unwise and
unwelcome. If it is OK to ignore unchecked conversion that is exactly
equivalent to unchecked union then it is OK to ignore unchecked union.

Mind you I think worrying about garbage collection is (to use the French
phrase) a "storm in a glass". I think the likelihood of anyone trying to
do garbage collection in a full blown Ada compiler is nil.

Furthermore, speaking as someone with a lot of experience in garbage
collected languages (from my SPITBOL and SETL days), I find the idea of
garbage collection in the presence of low level features that can mess
up memory to be pretty frightening. What can happen in such a situation
is that memory gets slightly messed up by the first GC, then the cancer
spreads until finally things totally collapse N GC's later, by which
time it is really hard to figure out what is going on :-)

*************************************************************

From: Michael Yoder
Sent: Thursday, October 31, 2002 at 10:35 PM

>  I think the likelihood of anyone trying to
>do garbage collection in a full blown Ada compiler is nil.

Well, the IAPC compiler (for Ada83) had run-time garbage collection, and
I was personally involved in implementing it (including the garbage
collection part). Mind you I'm not claiming this should really count: it
was only a prototype, though it did implement the full language.

I'm surprised at the seeming implication that your mixed Ada/Java
product doesn't garbage collect. Would you be willing to expand on that?

>Furthermore, speaking as someone with a lot of experience in garbage
>collected languages (from my SPITBOL and SETL days), I find the idea of
>garbage collection in the presence of low level features that can mess
>up memory to be pretty frightening.

Well... I did simple low-level stuff involving address operations in UCI
Lisp, which had a relocating collector. Maybe if you've gotten away with
it it's less frightening. :-) I won't pretend that getting away with it
once or twice is significant empirical evidence against your viewpoint.
But my take on mixing the two would be to use a different approach in
any case: you need a heap that is protected from garbage collection, for
pragma Controlled. I'd restrict address calculations, barring odd
emergencies, to uses involving entities in that heap or, perhaps, in
task stacks.

*************************************************************

From: Robert Dewar
Sent: Thursday, October 31, 2002 at 10:41 PM

Well the tense is wrong, since this product has been discontinued. But
yes it did GC< but of course did not allow unsafe unchecked conversions
etc.

*************************************************************

From: Robert A. Duff
Sent: Friday, November  1, 2002 at 10:31 AM

> But any program you write with unchecked unions can be easily transcribed
> to use unchecked conversion, the transformation is quite straightforward.

True, but the AI itself does not define U_U in terms of U_C.
If that's the semantics you want (which is fine with me),
it seems like some wording is needed, similar to 13.9(11),
which allows U_C of access values to cause erroneous execution.
I don't see anything in the AI as written that would allow
a copying garbage collector to ignore pointers hidden by unchecked
unions.

Actually, I think the analogy should not be with U_C, but with address
clauses that make overlays.  The relevant permission for garbage
collectors is 13.3(13).

On the other hand, Robert is (sadly) correct when he says:

> Mind you I think worrying about garbage collection is (to use the French
> phrase) a "storm in a glass". I think the likelihood of anyone trying to
> do garbage collection in a full blown Ada compiler is nil.

which makes me think none of this matters in practise.  Sigh.

> Furthermore, speaking as someone with a lot of experience in garbage
> collected languages (from my SPITBOL and SETL days), I find the idea of
> garbage collection in the presence of low level features that can mess
> up memory to be pretty frightening. What can happen in such a situation
> is that memory gets slightly messed up by the first GC, then the cancer
> spreads until finally things totally collapse N GC's later, by which
> time it is really hard to figure out what is going on :-)

True.  However, I think this sort of decision should be left to
programmers.  There's nothing to stop a project that uses a GC'ed
implementation of Ada from having coding conventions that outlaw
various low-level features.  Other than the fact that it will
be hard to find such an Ada implementation!  :-(

Also, an Ada implementation with GC ought to document how it deals with
these cases, so programmers know what to avoid.

----------------

Since we're supposed to be doing editorial review, here are a couple of
things I noticed:

Typo at line 111:

> Record representation clauses are permitted for unchecked unions. No space is
> given for a discriminant; it is illgal to mention a discriminant explicitly in
                                  ^^^^^^
> a component clause.

> An expression of an unchecked union type has "inferable discriminants"
> if it is either a name of an object with inferable discriminants or a
> qualified expression whose subtype_mark denotes a constrained subtype.

It should allow parentheses: "if it is the name of an object with
inferable discriminants, possibly parenthesized and/or qualified."
(I believe there's another AI that uses this wording -- it's not worth
expanding into the full-blown recursive definition.  I think "possibly
parenthesized and/or qualified" makes it clear enough that
(((T'((X))))) is OK.

- Bob

P.S. Let's put AI numbers in our Subject lines, OK?

*************************************************************

From: Robert Dewar
Sent: Friday, November 1, 2002 at 6:15 PM

<<True, but the AI itself does not define U_U in terms of U_C.
If that's the semantics you want (which is fine with me),
it seems like some wording is needed, similar to 13.9(11),
which allows U_C of access values to cause erroneous execution.
I don't see anything in the AI as written that would allow
a copying garbage collector to ignore pointers hidden by unchecked
unions.>>

Obviously such wording is required, since if you do overlay access
values with say float's, there is an obvious path to what MUST
in fact be erroneous, whether or not the AI bothers to say so :-)

*************************************************************

From: Robert I. Eachus
Sent: Friday, November  1, 2002 at 12:48 PM

>Actually, I think the analogy should not be with U_C, but with address
>clauses that make overlays.  The relevant permission for garbage
>collectors is 13.3(13).

A voice of  reason!  I don't think the discussion should bee phrased in
terms of what should or should not be legal.  This is editorial review!
 But I do think an explicit sentence like 13.3.(13), is appropriate.
 Let me put up a strawman:

"For an Unchecked_Union, it is the programmer's responsibility to ensure
that any read or write is the object references a valid value for the
type of the value, otherwise program execution is erroneous."

>On the other hand, Robert is (sadly) correct when he says:
>
>
>>Mind you I think worrying about garbage collection is (to use the French
>>phrase) a "storm in a glass". I think the likelihood of anyone trying to
>>do garbage collection in a full blown Ada compiler is nil.
>>
>>
>
>which makes me think none of this matters in practice.  Sigh.

No, it does matter in practice.  But not for full blown garbage
collectors.  The real issue as I see it is when a storage managed type,
either a magic type like Unbounded_String or a type with a managed
storage pool, gets mixed with Unchecked_Unions.  We should allow it.  It
is the programmer's responsibility to do it right. And there is no way
the ARG can get into defining what doing it right means. (I would like
to allow controlled components, under exactly the same rules, but that
is not exactly an editorial comment. Hmmm...  Delete "A component
declared in a variant_part of an unchecked union type shall not have a
controlled, protected, or task part." from the legality rules, as the
rule "  As the preceding rule: "All component subtypes of the type shall
be C-compatible." seems sufficient. ;-)

I can imagine writing a C function that returns a record or a reference
to one of several predefined Unbounded_Strings.  The code could be
portable between any Ada implementation that supports the needed pragma
Interface and Unchecked_Union.  But I can't imagine any legality check
we could define that could confirm that.  If the implementation wants to
check that the returned value is a valid Unbounded_String before
assigning it to another Unbounded_String, fine.  If it isn't checked and
erroneous execution will result if the programmer gets it wrong?  Fine
also.  But that is what this sentence would say, and also what the AI
tries to say here:  "It is erroneous to perform any operation (in C or
Ada) that would have failed a discriminant check had the discriminant
been present at run-time."  But that sentence isn't in the proposed RM
language.

*************************************************************

From: Robert Dewar
Sent: Friday, November 1, 2002 at 6:28 PM

Thankyou Robert for your suggestion:

<<"For an Unchecked_Union, it is the programmer's responsibility to ensure
that any read or write is the object references a valid value for the
type of the value, otherwise program execution is erroneous.">>

Right, that's exactly what was needed. And i it was omitted, it would
still be implicitly there, because nothing else would make anyt sense
at all :-)

*************************************************************

From: Tucker Taft
Sent: Saturday, November 2, 2002 at 11:26 AM

For what it's worth, there are at least two
Ada implementations that support garbage
collection, namely AppletMagic and JGNAT.

Supporting unchecked union is pretty easy
for AppletMagic, given the way we support
variant records.  We leave space for
all of the components, but we only use
those that are associated with the "current"
variant. This isn't as space-wasting as you
would expect, because nested records and
arrays are always separate objects, and we
"null out" the nested object references
as part of a whole-object assignment.

In any case, omitting the discriminant wouldn't
be any big deal.  Of course, the thing we
can't do is support rep-clauses!

I also see no great harm in adding an implementation
permission, rather than a hard-and-fast rule, to
require that access components in variant parts
of an unchecked union have pragma Controlled
applied to their type.   At least it shows we
have thought about the problem.

*************************************************************

From: Robert Dewar
Sent: Saturday, November  2, 2002 at 11:44 AM

<<For what it's worth, there are at least two
Ada implementations that support garbage
collection, namely AppletMagic and JGNAT.>>

But they are nowhere near full language implementations wrt chapter
12 and low level stuff.

<<Supporting unchecked union is pretty easy
for AppletMagic, given the way we support
variant records.  We leave space for
all of the components, but we only use
those that are associated with the "current"
variant. This isn't as space-wasting as you
would expect, because nested records and
arrays are always separate objects, and we
"null out" the nested object references
as part of a whole-object assignment.>>

OK, but what's the point. The idea of unchecked union is to map into
C unions. I find it useless (actually worse than useless - actively
dangerous and undesirable) outside this narrow usage.

*************************************************************

From: Robert Dewar
Sent: Saturday, November  2, 2002 at 11:49 AM

<<But they are nowhere near full language implementations wrt chapter
12 and low level stuff.>>

oops, I mean chapter 13.

*************************************************************

From: Robert I. Eachus
Sent: Friday, November  1, 2002 at  1:50 PM

There are two quasi-non editorial matters I'd like to bring up, and keep
separate from the main thread.  I call these "quasi" because to me they
look editorial, but could easily cause a discussion of non-editorial issues.

First, what about 'Valid?  As I see it, no problem.  It applies to an
object, which must have inferable discriminants, and it will check
whether the object is a valid object of that subtype.  If everyone
agrees, there should be another bullet added to Note 19 in 13.9.2, and
probably a sentence in the Dynamic Semantics that indicates 'Valid
checks the object for conformance to the inferable discriminants.

Second, the paragraph on equality  really leaves me stunned:

    Evaluation of the predefined equality operator for an unchecked union type
    if either of the operands lacks inferable discriminants. [This includes the
    case where the equality operator is invoked implicitly by the equality
    operator for an enclosing composite type - if the unchecked union
    component subtype is unconstrained, Program_Error is raised].

Is this really necessary, or is it a contradiction? First  look at notes
13 and 14 in 4.5.2.  Yes, I know these are notes, but we should  move
very carefully when making changes that render notes incorrect.   Note
13 says that predefined equality can only raise an exception  "...if the
type has a tagged part whose primitive equals operator propagates an
exception."  Oops!

But the second note is much more telling.  "If a composite type has
components that depend on discriminants, two values of this type have
matching components if and only if their discriminants are equal.  Two
nonnull arrays have matching components if and only if the length of
each dimension is the same for both."

Why is this a problem?  The equality paragraph is okay for the case
where the values have non-inferable unchecked union subtypes.  But what
about comparing two arrays with unchecked union components, or worse,
discriminated record values whose values might contain unchecked union
components.  We now have a required run-time check to decide whether to
return false or raise Program_Error in cases where compilers currently
may eliminate the equality test at compile time, or return false at
run-time after comparing one or more discriminants.  Requiring complete
evaluation like this may cause erroneous execution, which it seems to me
is what the paragraph intends to prevent.

The minimum fix it seems to me is to require Program_Error for
subcomponents only in the case where they are "matching components" or
subcomponents of matching components.

*************************************************************

From: Robert Dewar
Sent: Friday, November 1, 2002 at 6:33 PM

<<The minimum fix it seems to me is to require Program_Error for
subcomponents only in the case where they are "matching components" or
subcomponents of matching components.>>

I would completely remove the requirement for raising Program_Error.
This is supposed to be a low level feature for interfacing to C and
which should not result in any generation of junk code.

*************************************************************

From: Robert A. Duff
Sent: Saturday, November 2, 2002 at 11:29 AM

> First, what about 'Valid?  As I see it, no problem.  It applies to an
> object, which must have inferable discriminants, and it will check
> whether the object is a valid object of that subtype.  If everyone
> agrees, there should be another bullet added to Note 19 in 13.9.2, and
> probably a sentence in the Dynamic Semantics that indicates 'Valid
> checks the object for conformance to the inferable discriminants.

'Valid is only allowed for scalars, so I don't think this is an issue.
If I'm missing the point, please explain with an example.

> Second, the paragraph on equality  really leaves me stunned:
>
>     Evaluation of the predefined equality operator for an unchecked union type
>     if either of the operands lacks inferable discriminants. [This includes the
>     case where the equality operator is invoked implicitly by the equality
>     operator for an enclosing composite type - if the unchecked union
>     component subtype is unconstrained, Program_Error is raised].
>
> Is this really necessary, or is it a contradiction?

Yes, it is really necessary.  You can't do "=" without knowing what the
discriminants are.

Yes, it contradicts the NOTE in 4.5.2(33), as you note:

>... First  look at notes
> 13 and 14 in 4.5.2.

(Note that it's para 33, not 13.)

The easiest fix is to delete the NOTE.

>...  Yes, I know these are notes, but we should  move
> very carefully when making changes that render notes incorrect.   Note
> 13 says that predefined equality can only raise an exception  "...if the
> type has a tagged part whose primitive equals operator propagates an
> exception."  Oops!
>
> But the second note is much more telling.  "If a composite type has
> components that depend on discriminants, two values of this type have
> matching components if and only if their discriminants are equal.  Two
> nonnull arrays have matching components if and only if the length of
> each dimension is the same for both."

I agree that there is a problem here.  This is a subtle semantic point,
so Pascal might want to rule this "non-editorial".

The intent is that all of these Program_Errors can be detected at
compile time, except in a shared-generic-bodies situation.
The only reason these are run-time errors is to avoid generic
contract problems.  In practice, in a non-shared implementation,
you would get a warning at compile time (as well as a run-time error),
so we can think of these as essentially being compile-time errors.

But the proposed wording does not accomplish that.  For example:

    type T(Discrim: Integer := 1) is ...
    pragma Unchecked_Union(T);

    subtype S is T(Discrim => 1);

    type A is array(Positive range <>) of T;
    type B is array(Positive range <>) of S;

    A1: A := ...;
    A2: A := ...;
    B1: B := ...;
    B2: B := ...;

I believe that the intent is that "A1 = A2" should raise Program_Error
(probably with a compile-time warning), and "B1 = B2" should not.

However, as worded, if A1 and A2 happen to be zero-length arrays,
"A1 = A2" returns True.  To fix the problem, change the AI to say:

    Program_Error is raised....

        - Evaluation of the predefined equality operator for an
          unchecked union type if either of the operands
          lacks inferable discriminants.

        - Evaluation of the predefined equality operator for a
          composite type if the subtype of some subcomponent
          lacks inferable discriminants.

          AARM Annotatation: Program_Error is raised even if the
          subcomponent(s) don't actually exist at run time.
          For example, "A1 = A2", where A1 and A2 are zero-length arrays
          will raise Program_Error if the component subtype
          lacks inferable discriminants.

> Why is this a problem?  The equality paragraph is okay for the case
> where the values have non-inferable unchecked union subtypes.  But what
> about comparing two arrays with unchecked union components, or worse,
> discriminated record values whose values might contain unchecked union
> components.  We now have a required run-time check to decide whether to
> return false or raise Program_Error in cases where compilers currently
> may eliminate the equality test at compile time, or return false at
> run-time after comparing one or more discriminants.  Requiring complete
> evaluation like this may cause erroneous execution, which it seems to me
> is what the paragraph intends to prevent.
>
> The minimum fix it seems to me is to require Program_Error for
> subcomponents only in the case where they are "matching components" or
> subcomponents of matching components.

I don't think that's the best fix.  The compiler should be allowed to
detect all of these cases entirely at compile time, and not generate any
code for doing the "=" test (other than just inserting "raise P_E;" in
the object code).

*************************************************************

From: Robert A. Duff
Sent: Saturday, November 2, 2002 at 11:37 AM

Robert Dewar says:

> I would completely remove the requirement for raising Program_Error.
> This is supposed to be a low level feature for interfacing to C and
> which should not result in any generation of junk code.

I agree with the principle, which I would state as "Efficiency is more
important than safety for low-level features."  However, that principle
does not apply here.  As I said in my reply to Robert Eachus, the checks
we're talking about are really compile-time checks, even though they are
Program_Errors for generic contract reasons.

In other words, a program that uses Unchecked_Unions properly will never
attempt to do an "=" in the bad cases, so there is exactly zero overhead
for these run-time checks.  There is certainly no point in worrying
about the efficiency of incorrect code.

Low-level features should not be made less safe than they need to be,
unless there is a real run-time cost.  If somebody uses "=" in a bad
way, they should get an error -- that has no effect on efficiency,
because they will remove that code from their program.

*************************************************************

From: Robert Dewar
Sent: Saturday, November 2, 2002 at 11:47 AM

Well what you want of course is a proper error message, or at least a
warning. This business of raising PE for obvious compile time errors
is a bit silly. I suppose one can argue that a warning is mandated
by the Annex H requirements, but it's a bit round about.

The generic contract model is a bit silly here. So we have a rule that
says, yes, you can sign the contract on the car, and the contract says
the car will run, but as soon as you drive it out of the showroom
it will blow up :-)

*************************************************************

From: Robert Dewar
Sent: Saturday, November 2, 2002 at 11:48 AM

Perhaps we could add implementation advice (perhaqps in chapter 1) that
says that whenever there is a static rule requiring program_error
then a warning must be given at compile time. It is OK to talk about
undefined rubbish like warning msgs in IA.

*************************************************************

From: Robert A. Duff
Sent: Saturday, November 2, 2002 at 12:19 PM

> Well what you want of course is a proper error message, or at least a
> warning. This business of raising PE for obvious compile time errors
> is a bit silly. I suppose one can argue that a warning is mandated
> by the Annex H requirements, but it's a bit round about.
>
> The generic contract model is a bit silly here. So we have a rule that
> says, yes, you can sign the contract on the car, and the contract says
> the car will run, but as soon as you drive it out of the showroom
> it will blow up :-)

Yes, it's silly.  But we have precedent.  ;-)  For example, the fact
that you can't return a task to outside it's master has exactly the
same properties: you get a warning *and* a run-time error, unless the
compiler does shared generics.

> Perhaps we could add implementation advice (perhaqps in chapter 1) that
> says that whenever there is a static rule requiring program_error
> then a warning must be given at compile time. It is OK to talk about
> undefined rubbish like warning msgs in IA.

I would not *object* to that, but it seems unnecessary: compiler
writers are smart enough to figure it out on their own.

*************************************************************

From: Robert Dewar
Sent: Saturday, November 2, 2002 at  6:19 PM

<<I would not *object* to that, but it seems unnecessary: compiler
writers are smart enough to figure it out on their own.>>

I don't think so, it is certainly not the case that all implementations
warn on unblocked return from functions, which seems a truly simple case
(by the way, it is an idiotic feature of Ada that this is not statically
illegal. Ada is really marvelous here, it insists syntactically on stupid
returns that are not needed (e.g. in a function that raises an exception
and does nothing else), and it does not insist on returns where they
are obviously needed :-)

*************************************************************

From: Robert A. Duff
Sent: Sunday, November 3, 2002 at 10:18 AM

I basically agree, although I think "idiotic" and "obviously needed" are
overstating it.  I have seen quite a few cases in my own code where I
had to put in a bogus "raise Program_Error;", because although it was
obvious to *me* that the code can't get there, it wasn't obvious to the
compiler.  Furthermore, why don't compilers understand that "pragma
Assert(False);" prevents a path from falling off the end of a function,
just as much as "raise"?  In other words, the basic idea is good,
but the details are not "obvious".

*************************************************************

From: Robert A. Duff
Sent: Saturday, November 2, 2002 at 12:10 PM

Well, I can't understand the wording proposed by Robert Eachus.
There must be a typo in there.  Can you clarify?

I don't agree that making U_U potentially erroneous in the presence of
GC is the only solution.  Mike Yoder proposed two other possible
solutions that are also quite feasible.  So I'm afraid Robert's Rule (of
inventing RM wording that doesn't exist whenever the existing wording is
nonsensical) works here.  ;-)

The more I think about it, the less I like Robert Dewar's analogy with
Unchecked_Conversion.  Consider a U_C:

    function Cast is new Unchecked_Conversion(Integer, Integer);

    X: Integer := 0;

    Put_Line(Integer'Image(Cast(X));

Here, the unchecked conversion is to same type, so surely the
representation of X is a correct rep for type Integer, so surely this
very-simple U_C must work properly in all implementations.  It is *not*
impl-def, nor is it erroneous.

Agreed?

Now consider the analogous thing with Unchecked_Unions:

    type T(Discrim: Boolean) is
        record
            case Discrim is
                when True => X: Integer;
                when False => Y: Integer;
        end record;

    pragma Unchecked_Union(T);

    Var: T := (Discrim => True, X => 0);
    ...

    Put_Line(Integer'Image(Var.Y)); -- Erroneous!

The above Put_Line call is erroneous, according to the AI.
It is not implementation-defined *whether* it's erroneous.
It is simply erroneous.  In particular, it is not just like
an Integer-to-Integer U_C.

This is because the code violates a Discriminant_Check,
and pragma U_U causes an implicit pragma Suppress of Discriminant_Check,
and if you violate suppressed checks, you're erroneous.

I must admit that the programmer will likely get away with the
erroneousness, and simply print out the integer equivalent of the
address.  Whether that's "evil" or not is between that programmer and
his implementer -- it's not our business.

Likewise, an analogy between U_U and overlays-with-address-clauses is
also inapt.  Overlays can work non-erroneously (if you read your
compiler-specific docs carefully).

Now suppose we add GC into the picture.  Suppose we unchecked-convert
from an access value to a same-sized integer, and print out the
integer.  That will work fine, even in GC'ed implementations.
(It's the other way around that causes trouble.)

But the U_U case is unrelated to U_C (in either direction).
In the U_U case, we have either an integer or an access value,
but we can't tell which at run-time.  We don't have both at the
same time; only one component actually "exists" at any given time.

Therefore, I lean more and more toward Mike Yoder's idea: if you have
moving GC, then you better suppress GC on the access types inside U_U's
(presumably via pragma Controlled).  Or Tuck's suggestion of at least
allowing the GC'ed implementation to require that.

*************************************************************

From: Michael F. Yoder
Sent: Saturday, November 2, 2002 at  1:21 PM

Robert A Duff wrote:

>Therefore, I lean more and more toward Mike Yoder's idea: if you have
>moving GC, then you better suppress GC on the access types inside U_U's
>(presumably via pragma Controlled).  Or Tuck's suggestion of at least
>allowing the GC'ed implementation to require that.

It isn't only for moving GC: consider deciding whether to collect an
object when the only remaining reference to it is a "maybe" pointer.

I'm coming to think Tucker's idea is better. In a distributed
environment, I'd expect GC policies to vary on a per-partition basis.[1]
So, if the partition policy is no GC at all, that's like putting pragma
Controlled on all access types; U_U users can go wild. If it's
compacting GC preserving address order, it's best to get the bad news
about the U_U type at compile time. If it's a "conservative" collector,
U_U users can still go wild. And so forth.

I'd expect a serious GC implementation to assume the existence of
policies that operate somewhat like scheduling policies.

I think Tucker's suggestion is to say access types with pragma
Controlled are always allowed, and other access types may be forbidden
in circumstances specified by the implementation. That seems just right.

[1] If you know a reason this shouldn't be so, please speak up.

*************************************************************

From: Robert A. Duff
Sent: Saturday, November 2, 2002 at  3:56 PM

I agree with what Mike Yoder says above.

I've read lots of papers about distributed GC, but it still seems like
science fiction, to me.  I don't know what the implications are for
Annex E's "remote access types".  But I agree that GC policies could
vary across partitions.

*************************************************************

From: Robert A. Duff
Sent: Saturday, November 2, 2002 at 12:43 PM

Here's more editorial review of AI-216.  (I'm not sure the last item is
"editorial".)

The !proposal says:

> it is illgal to mention a discriminant explicitly in
> a component clause.

but I don't see that reflected in the !wording.

> The first_subtype_local_name of a pragma Unchecked_Union shall denote
> an unconstrained discriminated record subtype having a variant_part.

Should say "discriminated record first subtype"?

> An object of an unchecked union type is defined to have an unchecked union
                                                     ^^^^
                                                     be
> object.

> Any name which denotes a discriminant of an object of an
           ^^^^^
           that
> Unchecked_Union type shall occur within the declarative region of the type.

> An unchecked union subtype shall not be passed as a generic actual parameter
> if the corresponding formal type has a known_discriminant_part or
> is a formal derived type which is not an unchecked union type.
                           ^^^^^
                           that

> A view of an unchecked union object [(including a type conversion or
> function call)] has "inferable discriminants" if it has a
> constrained nominal subtype, unless the object is a component of an
> enclosing unchecked union object which is subject to a per-object constraint
                                   ^^^^^
                                   that
> and the enclosing object lacks inferable discriminants.

> Program_Error is raised in the following cases:
...
>     Conversion from a derived unchecked union type to an unconstrained
>     non-unchecked-union type if the operand of the conversion lacks inferable
>     discriminants.

Why "derived"?

*************************************************************

From: Robert Dewar
Sent: Saturday, November 2, 2002 at  6:17 PM

<<Here, the unchecked conversion is to same type, so surely the
representation of X is a correct rep for type Integer, so surely this
very-simple U_C must work properly in all implementations.  It is *not*
impl-def, nor is it erroneous.>>

It is most certainly impl-def in the general case (i.e. where paras 6-10
do not apply) see para 11:

11   Otherwise, the effect is implementation defined; in particular, the
result can be abnormal (see 13.9.1).

Note that this means that accessing the result can be erroneous. I am not
making this up, it's what the RM says.

<<The more I think about it, the less I like Robert Dewar's analogy with
Unchecked_Conversion.  Consider a U_C:>>

It is a fairly reasonable comparison given that

a) anythying that can be done with U_C can be done with U_U
b) anything that can be done with U_U can be done with U_C
c) a significant use in practice for unions in C is to get the effect of
   U_C (see for example the recent gcc thread on this issue), so it is not
   at all surprising that the Ada equivalent is similar to U_C.

I must say that I consider that this feature has lost its way. The original
motivation behind introducing this unsafe feature is to model a corresponding
unsafe feature in C, namely unions.

But now the language experts get to work and work hard to make this as
respectable as possible, and furthermore, pile it up with all kinds of
irrelevant semantics that have nothing to do with the original purpose.

*************************************************************

From: Robert I. Eachus
Sent: Sunday, November 3, 2002 at  8:12 PM

Robert A Duff wrote:

>>First, what about 'Valid?  As I see it, no problem.  It applies to an
>>object, which must have inferable discriminants, and it will check
>>whether the object is a valid object of that subtype.  If everyone
>>agrees, there should be another bullet added to Note 19 in 13.9.2, and
>>probably a sentence in the Dynamic Semantics that indicates 'Valid
>>checks the object for conformance to the inferable discriminants.
>>
>'Valid is only allowed for scalars, so I don't think this is an issue.
>If I'm missing the point, please explain with an example.

Sorry the second sentence got twisted around when trying to say what I
meant.  An object of an Unconstrained_Union subtype of course has
(sub)components which are scalar, as are the discriminants.  Checking
'Valid for such a component should (where necessary) check the inferable
discriminants as well as the bit patterns, if any.

let me give an example:

  type U_U(Error: Boolean)  is record
     case Error is
         when True => EC: Error_Code;
         when False => SA: System.Address;
     end case;
end record;

Return_Value: U_U(False) := Some_C_Function;

...

if Return_Value.SA'Valid then....

Now I may know that if Some_C_Function returns an Error_Code, that in
this implementation it is not a valid System.Address.  In that case I
should be able to use Return_Value.SA as an address without getting bus
error.  As far as I can see, this should require no extra work on the
part of implementors, but it is certainly a special case use of ' Valid.
 (And yes I know that System.Address is not necessarily a scalar type,
but we are definitely in implementation dependent code here.)

*************************************************************

From: Robert I. Eachus
Sent: Sunday, November 3, 2002 at  8:21 PM

Robert A Duff wrote:

>Yes, it is really necessary.  You can't do "=" without knowing what the
>discriminants are.

If one value has inferable discriminants and the other does not, can
they be equal?  The reason I ask really has to do with the empty array
problem. If I compare two objects of a type with an array of
unchecked_union components whose bounds depend on discriminants, and one
array is empty, what happens?  I certainly don't want Program_Error!

>(Note that it's para 33, not 13.)

NOTE 13, and yes it is unofficially para 33. ;-)

>The easiest fix is to delete the NOTE.

And better is to fix it.

>...
>I don't think that's the best fix.  The compiler should be allowed to
>detect all of these cases entirely at compile time, and not generate any
>code for doing the "=" test (other than just inserting "raise P_E;" in
>the object code).

Let me explain my problem with your approach, and it definitely is a generic
contract model problem.  If I have a list package with a declaration like:

  generic
    type Element is private;
    Max_Size: in Integer;
  package Bounded_Lists is
    type List is limited private;
    ...
    function Is_Empty(L: List) return Boolean;
    ...
  end Lists;

I don't want to have to deal with the case that a List containing no values
MUST raise Program_Error in some cases.  This is a distributed cost.  Yes, I
can implement the list in a way that doesn't run into the problem, but that is
exactly the distributed cost.  Right now the rules of the language state that
empty arrays compare equal.  You want to change that, I don't.  Note that the
actual List type would have a discriminated component something like:

    subtype Size is Integer range 0..Max_Size;
    Element_Array is array(Size range <>) of Element;
    type Content(Actual_Size: Size := 0) is
      record
        EA:Element_Array(1..Actual_Size);
      end record;
    type List is record
        C: Content;
      end record;

Currently most compilers when generating code to compare two elements of type
List will generate something like:  ...
      if A.C.Actual_Size /= B.C.Actual_Size
      then return False;
      else
        for I in A.C.EA'range loop
          if A.C.EA(I) /= B.C.EA(I) then return False; end if;
        end loop;
        return True;
      end if;

It is perfectly legal now to eliminate some of the comparisons on Element.  But
your rule requires checking to see that no subcomponent of any Element contains
an Unchecked_Union without inferable discriminants. If two comparable elements
both lack inferable discriminants, I am happy to leave it to the programmer to
decide whether he is satisfied with that.  One, does and one does not?  I see
no problem with that case for SUBcomponents.  It can only happen when there are
higher level discriminants (whether inferred for some containing type or
actually present) that do not compare equal, so always return false.

I would prefer to leave the top level case as a bounded error, because I don't
see any realistic chance that anyone would care.  (Some people might even want
it to compare the bits, but I don't think we should try to guarentee that.)  It
is the composition case that I worry about.  There are real cases where a
careful compiler vendor would have to generate worse code for existing programs
that do not use Unchecked_Unions.

*************************************************************

From: Robert A. Duff
Sent: Monday, November 4, 2002 at  8:08 PM

> If one value has inferable discriminants and the other does not, can
> they be equal?

In that case, the AI says raise P_E.  If X has inferrable discriminants,
and Y does not, you have to qualify Y:

    if X = Constrained_Subtype'(Y) then...

which will then *not* raise P_E, but return either True or False.

That seems like the right choice.  You don't want to infer the
discriminants of Y from those of X -- that would be more complicated,
and more error-prone.  (Of course the whole feature is error prone, but
at least here the programmer can explicitly say what he *thinks* the
discriminants are -- presumably a function of some data stored
"elsewhere".)

>...  The reason I ask really has to do with the empty array
> problem. If I compare two objects of a type with an array of
> unchecked_union components whose bounds depend on discriminants, and one
> array is empty, what happens?  I certainly don't want Program_Error!

I think you certainly *do* want P_E.  Well, what you really want is a
compile-time error, but because of shared generics, the best we can do
is P_E (plus a compile-time warning).

> Let me explain my problem with your approach, and it definitely is a
> generic contract model problem.

But I don't understand whether you're talking about shared-code
generics.

I don't care about the efficiency properties of shared-code generics.
I do care that they are feasible to implement (although some folks have
given up even on *that* principle).

And in the non-shared-code implementation, the compiler can tell *at
compile time* whether P_E is to be raised, so there is *zero* efficieny
cost in the non-P_E case.

The key point here is that we expect precise compile-time warnings for
the error case.  In order for that to be possible, we have to define the
semantics so that empty arrays raise P_E in the same cases as non-empty
arrays, because the compiler cannot tell whether the array is empty.
The compiler *can* tell whether there are any non-inferrable
subcomponent subtypes anywhere in there.

If I'm missing something, and there really is distributed overhead (in
the non-shared-code implementation), please show a more complete
example, because I can't think of any.  As far as I can tell, the
compiler can always detect this "has subcomponent subtypes with
non-inferrable discrims" property at compile time.

*************************************************************

From: Robert I. Eachus
Sent: Monday, November 4, 2002 at  3:10 PM

>And in the non-shared-code implementation, the compiler can tell *at
>compile time* whether P_E is to be raised, so there is *zero* efficieny
>cost in the non-P_E case.

This is where we disagree.  Either you are saying that Program_Error
should be raised if the compiler can't prove that there will be no
Unchecked_Union components without inferable discriminants at compile
time, or you are asking compilers to solve the halting problem.
Records--even records without discriminants--can have components with
discriminants.  It is easily possible to create abstract types that have
such hidden discriminants, and where those discriminants CHANGE during
the execution of a program.

The example I gave should have been sufficient.  If you have a List of
the type given, and the Element type is an Unchecked_Union, what
happens?  You seem to say, no problem, always raise Program_Error at
compile time.  But what if I compare two List objects with no Contents?
Are you saying Program_Error should be raised in this case?  That is
the case I am really trying to pin you down on.  I don't see how you can
have inferable discriminants for a component that is not there.

>The key point here is that we expect precise compile-time warnings for
>the error case.  In order for that to be possible, we have to define the
>semantics so that empty arrays raise P_E in the same cases as non-empty
>arrays, because the compiler cannot tell whether the array is empty.
>The compiler *can* tell whether there are any non-inferrable
>subcomponent subtypes anywhere in there.
>
>If I'm missing something, and there really is distributed overhead (in
>the non-shared-code implementation), please show a more complete
>example, because I can't think of any.  As far as I can tell, the
>compiler can always detect this "has subcomponent subtypes with
>non-inferrable discrims" property at compile time.

Ah, that is exactly what you are saying.  Now it comes down to a wording
issue, and I think a pretty major wording issue.  When are the
discriminant(s) of a missing component inferable?  You seem to think
that only when they are statically inferable is derivable from the AI.
I think that the AI makes it clear that a (sub)component must exist
before you can even ask the question.  Saying that the program must
raise Program_Error and then hoping the compiler can figure out how to
do it at compile time is just not acceptable.  You either have to go the
extra mile of  defining when a component (statically) inherits (ouch!)
the non-inferable discriminant property from its children, or allow the
compiler to give the correct answer at run-time instead of
Program_Error.  I don't understand why you are so steamed-up about this.
We are discussing a case where any sensible programmer will expect
either a junk answer or the correct answer.  Let me give an example:

type Test(X: Integer := 0)  is record
   Okay: Boolean := False;
   case X is
    when Integer'First..-1 =>
         Y: Integer := 6;
    when 0..Integer'Last  =>
          Z: U_U_Array(1..X);
   end case;
  end record;

  A, B: Test;
begin
   A := (-1, True, 3);
   B := (0, False);
   if A = B then -- must raise Program_Error!

To me this is a way too heavy distributed burden on implementations to
even be considered.  I might find a bounded error of False or
Program_Error acceptable,  but I really prefer bounded error only when
both values have comparable components and at least one does not have
inferable discrimanants.

*************************************************************

From: Robert Dewar
Sent: Monday, November 4, 2002 at  3:17 PM

I strongly agree with this

And I have trouble believinhg the horrible complex mess that has been made
out of what should be a simple feature with very limited goals and no
decorations :-(

*************************************************************

From: Robert A. Duff
Sent: Monday, November 4, 2002 at  4:18 PM

I really can't answer your e-mail until you answer my question:

I wrote:

> >But I don't understand whether you're talking about shared-code
> >generics.

*************************************************************

From: Robert I Eachus
Sent: Monday, November 4, 2002 at  7:57 PM

>>>But I don't understand whether you're talking about shared-code
>>>generics.
>>>

The issue has NOTHING to do with generics, shared or otherwise.  It has
to do with your belief that whether or not a value CONTAINS a component
of an unchecked union type (with or without inferable discriminants) can
be determined at compile time.  To boil my argument down to its essentials:

     1)  A record type with or without discriminants, can contain
another record that has discriminants (with defaults) that do not depend
on the discriminants of the enclosing record.

     2) Objects of such a record type can be assigned new component
values, at run-time, with different discriminant values.

     3) One of those new values might have a variant part that contains
a component of an unchecked union type, with or without inferable
discriminants, or maybe some of each.

     4) Therefore it takes a run-time check to determine whether or not
such a component is present.

The only reason that this affects generics is that there are a lot of
generic packages out there which contain code that this paragraph will
require be made significantly worse.  But there are non-generic
abstractions with the same property.

Now what I had thought was the original intent was much less disruptive.
If two objects of a record type have different discriminants, they are
not equal.  End of story.  If, and only if, you have to compare two
unchecked union components in the process of determining equality, and
one or more needed descriminants cannot be inferred, raise
Program_Error.  Otherwise return the correct result.

Now I happen to think that is a bit of overkill, and I also think that
the implementation freedom to do the equality checks in some order as
long as discriminants are checked before components that depend on them
would result in implementation differences, but that is all okay.  Not
perfect, but definitely tolerable.

But let me take this program destroying new equality feature to the
highest heights.  No, I wouldn't want to do this, but as I see the AI,
it can happen.  Say I have a large system with a tagged type.  I add a
new derived type to the tagged type that contains an component with an
unconstrained unchecked union type.  Does existing code morph to raise
Program_Error?  I hope not!  The possibility of Program_Error should
only exist if the equality operation for the new subtype is called.
 Note that in general it will only be called if two values with the new
tag are compared.  Legislating any other result is going to require work
by compiler writers, and I guarantee that the resulting code won't be as
efficient as what exists now.

*************************************************************

From: Robert A. Duff
Sent: Tuesday, November 5, 2002 at  3:54 PM

> >>>But I don't understand whether you're talking about shared-code
> >>>generics.
> >>>
>
> The issue has NOTHING to do with generics, shared or otherwise.

OK, thank you.  So you're *not* talking about shared generics.  Good.

I apparently have not explained myself very well, so let me try again.
I will:

    Part 1.  Define my proposal in greater detail.

    Part 2.  Show that this proposal *is* implementable (without solving
    the halting problem), and involves *zero* distributed overhead.

    Part 3.  Argue that this proposal is simpler than the AI as written.

    Part 4.  Argue that this proposal is safer than the AI as written.

If you disagree, please explain which part you're disagreeing with!


Part 1.

Consider RM-3.2.1(5):

    A given type shall not have a subcomponent whose type is the given
    type itself.

This is entirely a compile-time rule.  Even empty arrays are not allowed
to contain themselves.  This is the meaning of "subcomponent" that I was
talking about -- not the concept of subcomponents that might or might
not exist at run time.

I was hoping I could get away with a simple statement in the same style
as the above rule, but if you insist on the full-blown recursive
definition, here goes:

The AI already defines the notion of "inferable discriminants".  The
cases where equality won't work are a U_U type without inferable
discriminants, or a composite type *containing* such a U_U type, visibly
or invisibly.  The AI as written defines this dynamically; I propose to
define it statically.  (If anyone can think of a better term than
"missing internal discriminants", please suggest one.)

    The following specific types are defined to be "missing
    internal discriminants":

        - A composite type, if some subcomponent is an U_U without
          inferable discriminants.

        - A derived type, if the parent type is one of these.

        - A package-private type or private extension, if the full type
          is one of these.

        - A generic formal type, if the actual type is one of these.

Then we can say that predefined "=" raises P_E if the subtype of either
operand lacks inferable discriminants, or if the type of the operands
is missing internal discriminants.

Note that the above talks about specific types.  Class-wide equality is
defined in terms of the specific type(s) already.

Part 2.

I hope it is now clear that the "missing internal discriminants"
property can be detected at compile time.  (I'm ignoring shared
generics.)
Do you agree with that?

Even if a component is inside a variant part, or inside an array that
could be empty in some cases, it still causes the outer type to be
missing internal discrims.  So my proposal is to raise P_E when
comparing variant records, where one variant has the evil property, even
if that variant does not exist at run time.  Similarly, my proposal is
to raise P_E on "=" of arrays-of-evil-things, even if the array is zero
length.

Note also that the "has inferable discrims" property is compile-time
known, since it depends only on the constrainedness of the component.
It does not depend on the values of discriminants or anything like
that.

Because the relevant properties are known at compile time, the
determination of whether to raise P_E for a particular "=" can happen at
compile time.

Therefore, the implementation is trivial:  Compute the above property.
On each predefined "=", either generate the usual code (which Robert
Eachus outlined earlier), or else generate code to do "raise P_E;".

It should also be clear that there is *zero* distributed overhead.
Each non-U_U type either is "missing internal discriminants" or not.
If so, we raise P_E, so we don't care about efficiency.
If not, the generated code is identical to what it would be if
U_U had never been invented.

Note that class-wide equality is typically implemented by dispatching to
the specific-type equality.  The compiler can tell (at compile time of
each specific type) whether to generate "raise P_E" or to generate the
normal equality code.  There is still zero overhead for class-wide
equality here, because we're dispatching anyway.

Part 3.

It seems to me that replacing the whole "=" operation with a "raise P_E"
is simpler than replacing pieces of it.  If you don't agree with that,
surely you will at least agree that it's at least as simple.

Part 4.

My claim is that this proposal is safer than the AI as written.

This is because if you have a type that might cause P_E intermittently,
you will get a compile-time warning, and the warning will be precise
(you *will* get P_E, so you better fix your code).  Furthermore, you
will get P_E every time you call "=" on that type, so you will be less
likely to miss the problem.

Note that the RM allows predef "=" to compare the subcomponents in any
order.  Therefore, in the AI as written, some implementations might
return False, where other implementations raise P_E, simply because the
former compared some non-U_U component first, and found them unequal.
That seems like an undesirable nonportability; my proposal makes the
raising of P_E deterministic -- for some types it will, for others it
won't.

I admit that this claim does not apply to class-wide equality.
Class-wide equality does:

    Compare tags; if unequal, return False.

    Dispatch to specific equality.

This code could raise P_E intermittently, depending on the tag values
each time we get here.

SUMMARY: My proposal is implementable with zero overhead, and safer
(except in the obscure case of an unconstrained unchecked union inside a
class-wide type).

Part 5.  An example:

    type T(D: Boolean) is
        record
            case D is
                when True =>
                    X: Some_Unconstrained_Unchecked_Union;
                when False =>
                    Y: Integer;
            end case;
        end record;

    procedure P(X, Y: T) is
    begin
        if X = Y then ...
    end P;

In the AI as written, the "X = Y" will raise P_E only if X.D and Y.D are
both True.  In my proposal, the "X = Y" will raise P_E every time.

It seems to me that the "X = Y" call is simply wrong -- you shouldn't be
using predefined "=" on types where some components *might* not be able
to do predefined "=".  It seems better to catch this every time through
the code, and at compile time via a warning.  The correct way to write
the above code is to avoid "=" altogether, or else write a user-defined
"=" on T that (or on Some_Unconstrained_Unchecked_Union) that knows
where to gin-up the discriminants of X from.

Does anybody have a convincing example of how the above sort of thing
would be useful in practice?  I.e., where you can sneakily call
predefined "=", but write the code carefully so that X.D and Y.D are
never both True at the same time?  If so, then I will withdraw my
proposal, and I will live happily with the AI as written.

(Obviously, I can *construct* such an example -- just add "if not X.D"
to the above.  But the question is, is it useful in practice?)

Part 6.  Comments on Robert Eachus' objections:

> The issue has NOTHING to do with generics, shared or otherwise.  It has
> to do with your belief that whether or not a value CONTAINS a component
> of an unchecked union type (with or without inferable discriminants) can
> be determined at compile time.

It can (for specific types) as I showed above.  Of course, like all
compile-time checks, it's conservative.

>...  To boil my argument down to its essentials:
>
>      1)  A record type with or without discriminants, can contain
> another record that has discriminants (with defaults) that do not depend
> on the discriminants of the enclosing record.
>
>      2) Objects of such a record type can be assigned new component
> values, at run-time, with different discriminant values.
>
>      3) One of those new values might have a variant part that contains
> a component of an unchecked union type, with or without inferable
> discriminants, or maybe some of each.

My proposal is to raise P_E regardless of which variant actually exists
at run time, if *any* of those variants have a trouble-making
component.

>      4) Therefore it takes a run-time check to determine whether or not
> such a component is present.
>
> The only reason that this affects generics is that there are a lot of
> generic packages out there which contain code that this paragraph will
> require be made significantly worse.  But there are non-generic
> abstractions with the same property.
>
> Now what I had thought was the original intent was much less disruptive.
>  If two objects of a record type have different discriminants, they are
> not equal.  End of story.  If, and only if, you have to compare two
> unchecked union components in the process of determining equality, and
> one or more needed descriminants cannot be inferred, raise
> Program_Error.  Otherwise return the correct result.

I agree that this is the proposal in the AI as written, and I agree that
it has zero overhead.  But my proposal also has zero overhead.

> Now I happen to think that is a bit of overkill, and I also think that
> the implementation freedom to do the equality checks in some order as
> long as discriminants are checked before components that depend on them
> would result in implementation differences, but that is all okay.  Not
> perfect, but definitely tolerable.
>
> But let me take this program destroying new equality feature to the
> highest heights.  No, I wouldn't want to do this, but as I see the AI,
> it can happen.  Say I have a large system with a tagged type.

Ah.  I had missed the class-wide "=" issue; thanks for pointing it out.
I believe I have taken it into account correctly in the latest version
of my proposal, as written above.

>...  I add a
> new derived type to the tagged type that contains an component with an
> unconstrained unchecked union type.  Does existing code morph to raise
> Program_Error?  I hope not!

I hope so!  At least if the existing code dispatches to that new type.

In the AI as written, the existing code will raise P_E when passed the
new Tag, *sometimes*.  In my proposal, the existing code will raise P_E
every time it gets the bad Tag.  Both are, unfortunately, intermittent,
but my proposal is less so.

>...  The possibility of Program_Error should
> only exist if the equality operation for the new subtype is called.

Agreed.

>  Note that in general it will only be called if two values with the new
> tag are compared.  Legislating any other result is going to require work
> by compiler writers, and I guarantee that the resulting code won't be as
> efficient as what exists now.

Yes, I agree with that.  I have modified my proposal above so that it
works in this case, and is still zero overhead.

From an earlier message:

> type Test(X: Integer := 0)  is record
>    Okay: Boolean := False;
>    case X is
>     when Integer'First..-1 =>
>          Y: Integer := 6;
>     when 0..Integer'Last  =>
>           Z: U_U_Array(1..X);
>    end case;
>   end record;
>
>   A, B: Test;
> begin
>    A := (-1, True, 3);
>    B := (0, False);

You're missing "Z => ...".

>    if A = B then -- must raise Program_Error!

Yes, my proposal is to raise P_E here.

> To me this is a way too heavy distributed burden on implementations to
> even be considered.

I can't imagine why this would cause *any* overhead.  We know at compile
time that this will raise P_E, and we generate "raise P_E" code, and we
don't care about efficiency.  In cases that don't raise P_E, we know
*that* at compile time, and generate the same code we've always
generated for "=".  If we're generating the same code we always did for
non-U_U cases, then there is no distributed overhead; QED.

>...I might find a bounded error of False or
> Program_Error acceptable,  but I really prefer bounded error only when
> both values have comparable components and at least one does not have
> inferable discrimanants.

I don't like bounded errors.  I don't see any need for them in this
case.

*************************************************************

From: Robert I Eachus
Sent: Tuesday, November 5, 2002 at  5:00 PM

Ah, now Bob Duff and I are playing from the same sheet of music.  We
may not agree on the right (musical) interpretation, but at least we now
agree on what the (musical) notes say.

I don't think this qualifies as editorial review at this point.  I think
that the position that Bob Duff and I are taking on components of tagged
types is a ramifiication of the current wording.  But I get the feeling
that no one else had considered it.

Second, I now understand that Bob Duff is arguing for a very
conservative rule on raising Program_Error for equality.  My position is
much more liberal.  Either requires wording changes. As long as the rule
adopted doesn't cause contortions for implementors, I don't really think
it matters. (The current AI wording does seem to require contortions.)
Anyone who deliberately creates any of my examples is asking for
trouble if they don't know the details of the actual implementation.
This is not the normal and expected use of the pragma.  It was just my
feeling that a literal reading of the AI could result in harmful tests
being written with distributed costs.

I don't think that there is such a thing here as a "right" resolution of
these edge cases--other than, as I said above, don't cause implementors
to go to a lot of effort.  I can't imagine anyone intentionally
programming these sorts of structures except to test against the AI.
The case will occasionally happen by error though, so we shouldn't
completely ignore them.

*************************************************************

From: Robert Dewar
Sent: Tuesday, November 5, 2002 at  5:23 PM

<<I don't think that there is such a thing here as a "right" resolution of
these edge cases--other than, as I said above, don't cause implementors
to go to a lot of effort.  I can't imagine anyone intentionally
programming these sorts of structures except to test against the AI.>>

BUt once these difficulties have been pointed out, then even without
a test, the damage is done, because to ignore these cases would
be deliberately violating the stadnard and would mean that the
DoC could not be signed.

It is essential that all such difficulties be removed.

I really see absolutely zero demand for this complexification of
pragma Unchecked_Union. It seems a classic case of language designers
running away with a feature to me and losing sight of its original purpose.

*************************************************************

From: Pascal Leroy
Sent: Wednesday, November 6, 2002 at  4:20 AM

I am withdrawing the following two AIs from the editorial review: no need to
spend time on them, they won't go to the next WG9 meeting, and the ARG will
discuss them again:

AI 216: the recent discussion demonstrated that there are a number of
significant problems with the AI as written; it goes back to the original
author (Steve B., as I recall) for update.  I think we should follow Tuck's
suggestion regarding interaction with garbage collection; Bob D.'s suggestion
regarding equality; and Bob E.'s suggestion regarding validity and
erroneousness.

...

*************************************************************

From: Steve Baird
Sent: Tuesday, January 28, 2003 at 12:04 PM

Pascal Leroy said:
>  AI 216: the recent discussion demonstrated that there are a number of
>  significant problems with the AI as written; it goes back to the original
>  author (Steve B., as I recall) for update.

This is my attempt to update the wording of AI-216 to reflect these issues.

  -- Steve

--------

1) In the list of constructs which raise Program_Error in B.3.3's
  "Dynamic Semantics" section, replace

     Evaluation of the predefined equality operator for an unchecked union type
     if either of the operands lacks inferable discriminants. [This includes the
     case where the equality operator is invoked implicitly by the equality
     operator for an enclosing composite type - if the unchecked union
     component subtype is unconstrained, Program_Error is raised].

   with

     Evaluation of the predefined equality operator for an unchecked union type
     if either of the operands lacks inferable discriminants.

     Evaluation of the predefined equality operator for a type which has
     a subcomponent of an unchecked union type whose nominal subtype is
     unconstrained.

2) At the end of the "Static Semantics" section of B.3.3 add:

   Notes

   The use of an unchecked union to obtain the effect of an
   unchecked conversion results in erroneous execution (see 11.5).
   Execution of the following example is erroneous:

       type T (Flag : Boolean := False) is
           record
               case Flag is
                   when False =>
                       F1 : Integer := 0;
                   when True =>
                       F2 : Integer := 1;
               end case;
           end record;
       pragma Unchecked_Union (T);

       X : T;
       Y : Integer := X.F2; -- erroneous



3) At the end of B.3.3, add:

                Implementation Permissions

     An implementation may require that the type of an access subcomponent
     of an Unchecked_Union type must be subject to a Controlled pragma.

--------

Discussion:

1) I agree with Bob Duff's proposal regarding equality comparisons
   and the circumstances under which Program_Error is raised.
   Bob - speak up if this doesn't implement your proposal.

2) Robert Eachus suggested adding rules along the lines of:
       For an Unchecked_Union, it is the programmmer's
       responsibility to ensure that any read or write
       of the object references a valid value for the
       type of the value, otherwise program execution is erroneous.
   and also
       It is erroneous to perform any operation (in C
       or Ada) that would have failed a discriminant
       check had the discriminant been present at runtime.

   I generally agree with both of these statements, but they are already
   implied by 11.5(26) and the rule that Discriminant_Check is suppressed
   for an Unchecked_Union type. Therefore, I only added a note.

   The existing rules do not cover the case of an Unchecked_Union object which
   is initialized in C and then (mis)read in Ada, but this
   is just one instance of the much larger problem of non-Ada code
   breaking Ada's rules. Addressing just this one particular instance
   of the problem does not seem like a good idea. What is really needed
   is a very general clause in Annex B something like

       If a use of an entity defined in a foreign language
       results in violation of any property that is guaranteed
       by the language or of any invariant that is maintained
       by the implementation, then program execution is erroneous.

   , but that is outside of the scope of this AI.

3) Is there any need to allow the optional "no garbage-collected
   access subcomponents of unchecked union types" restriction to be
   implemented as a post-compilation rule?

*************************************************************

From: Randy Brukardt
Sent: Thursday, January 30, 2003 at 6:44 PM

Minor nits:
> 2) At the end of the "Static Semantics" section of B.3.3 add:
>   Notes
>   ...

Notes in the RM can only go at the end of a section, not in the middle. I
presume this is RM note (it seems it should be to me), so I've moved it to
the appropriate location.

>3) At the end of B.3.3, add:
>                Implementation Permissions
>
>     An implementation may require that the type of an access subcomponent
>     of an Unchecked_Union type must be subject to a Controlled pragma.

RM-speak seems to be "pragma Controlled" rather than "Controlled pragma".
And "subject" is a word which is hardly ever used in the RM (I only found 12
occurrences in the AARM).

So I think this would be better if it said something like:

An implementation may require that pragma Controlled is specified for the
type of an access subcomponent of an Unchecked_Union type.

>The existing rules do not cover the case of an Unchecked_Union object which
>is initialized in C and then (mis)read in Ada, but this
>is just one instance of the much larger problem of non-Ada code
>breaking Ada's rules. Addressing just this one particular instance
>of the problem does not seem like a good idea. What is really needed
>is a very general clause in Annex B something like ...

We discussed this in October, and I opened AI-320 to cover that, based on
the solution discussed at the meeting and the existing wording in the
Standard. So, you are completely correct when you say:

>  but that is outside of the scope of this AI.

*************************************************************

!topic Nitpick regarding Unchecked_Union proposal
!reference AI95-00216
!from Adam Beneschan 04-10-25
!discussion


I think there's a mistake in the text of this AI.  The !wording
section says:

    An unchecked union subtype shall only be passed as a generic
    actual parameter if the corresponding formal type does not have a
    known_discriminant_part, or is a formal derived type that is an
    unchecked union type.


The !corrigendum contains both these paragraphs:

    An unchecked union subtype shall not be passed as a generic actual
    parameter if the corresponding formal type has a
    @fa<known_discriminant_part> or is a formal derived type that is
    not an unchecked union type.

    An unchecked union subtype shall only be passed as a generic
    actual parameter if the corresponding formal type does not have a
    @fa<known_discriminant_part>, or is a formal derived type that is
    an unchecked union type.


It appears that the intent was to include only one of the paragraphs
shown in the !corrigendum section, but the other one got accidentally
left in.  My guess is that the first paragraph of the !corrigendum is
correct, and both the second paragraph there and the paragraph in the
!wording section are wrong.  The last clause of

    An unchecked union subtype shall only be passed as a generic
    actual parameter if the corresponding formal type does not have a
    known_discriminant_part, or is a formal derived type that is an
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    unchecked union type.
    ^^^^^^^^^^^^^^^^^^^^

doesn't really make sense as written, since a formal derived type
can't have a known_discriminant_part (RM95 12.5.1(11)).  But that's
just my guess---I really don't know what was intended.

*************************************************************

From: Randy Brukardt
Sent: Monday, October 25, 2004 at  9:27 PM

> It appears that the intent was to include only one of the paragraphs
> shown in the !corrigendum section, but the other one got accidentally
> left in.  My guess is that the first paragraph of the !corrigendum is
> correct, and both the second paragraph there and the paragraph in the
> !wording section are wrong.

That's unlikely. It's much more likely that the first paragraph of the
!corrigendum is left over. The only difference is that it is a double
negative; the rewritten paragraph doesn't have the double negative and thus
is easier to read.

>  The last clause of
>
>     An unchecked union subtype shall only be passed as a generic
>     actual parameter if the corresponding formal type does not have a
>     known_discriminant_part, or is a formal derived type that is an
>                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>     unchecked union type.
>     ^^^^^^^^^^^^^^^^^^^^
>
> doesn't really make sense as written, since a formal derived type
> can't have a known_discriminant_part (RM95 12.5.1(11)).  But that's
> just my guess---I really don't know what was intended.

Formal private types can have known_discriminant_parts. The phrase doesn't
say anything about derived types - it applies to all kinds of formals that
might be matched by an unchecked union.

*************************************************************

From: Adam Beneschan
Sent: Monday, October 25, 2004 at  9:39 PM

My point is that since formal *derived* types cannot have known
discriminant parts, the clause that I've marked (starting with "or")
is logically redundant, since it cannot possibly apply to anything,
the way this paragraph is written.  That's why I suspected this was
not the intended wording.

*************************************************************

From: Randy Brukardt
Sent: Monday, October 25, 2004 at  9:27 PM

I'd started drafting a correction when this came in.

I've researched the history of this wording, and as I had suspected, it
happened at the last minute in editorial review. The wording was changed to
accurately reflect the intent was specified in in the !proposal. The problem,
as you note, is that all derived types don't have known_discriminant_parts --
the (written) intent is and always has been wrong.

My guess is that we have to discuss private types and derived types separately,
because derived types inherit the discriminants without explicitly mentioning
them. And we're trying to disallow anything with discriminants that can be
manipulated without the type being an unchecked union type.

I think that the correct wording is:

  An unchecked union subtype shall only be passed as a generic actual
  parameter if the corresponding formal type is a formal private type that
  does not have a known_discriminant_part, or is a formal derived type that
  is an unchecked union type.

(I don't think that an unchecked_union type could match any other kind of
formal type, so this is a complete list. It would be OK for a formal derived
type with no discriminants to match an unchecked union type, but I think that
could only happen for a tagged type, and that hardly seems worth allowing,
especially with the complicated wording needed.)

Humm, the original replacement wording that Pascal had proposed was:

  A unchecked union subtype shall only be passed as a generic actual
  parameter if the corresponding formal type has no known discriminants, or is
  a formal derived type that is an unchecked union type.

which is actually correct (as a derived type inherits known discriminants from
its ancestor). He didn't catch it when I completely misunderstood what was
going on.

(From rereading the mail many times, I think I've proved that I'm the only one
who is confused here; but I managed to screw it up big time!!)

I'm not sure why the rule mentions "formal derived type" in the second part;
the only kind of generic formal that is defined to be an unchecked union type
is a formal derived type; a private type is never an unchecked union (that's in
the preceding paragraph of the wording).

So I think we can just use:

  A unchecked union subtype shall only be passed as a generic actual
  parameter if the corresponding formal type has no known discriminants or is
  an unchecked union type.

Along with an AARM Note.

I'll update the AI with this, and we'll consider it as a AI correction at the
next ARG meeting.

*************************************************************

!topic Unchecked_Union (AI-216) and discriminant checks
!reference AI95-00216, RM95 4.6(43,45), RM95 11.5
!from Adam Beneschan 04-10-25
!discussion

The proposed text for AI-216 says, "Discriminant_Check is suppressed
for an unchecked union type".  However, there are a couple cases where
it's not clear what this means.  The Discrminant_Check entry in the
index refers to 4.6(43) and 4.6(45), in the section on type
conversions; both of these paragraphs refer to two types, the target
type and the operand type.  So it isn't clear in those cases which of
those two types the Discriminant_Check is being performed on, and thus
which one would be suppressed.

Specifically:  Given these declarations:

procedure Proc (N1, N2 : Integer) is

   type T1 (X : Integer) is record ... end record; -- with variant part
   subtype S1 is T1 (N1);

   type T2 is new T1;
   pragma Unchecked_Union (T2);
   subtype S2 is T2 (N2);

   V1 : S1;
   V2 : S2;

Assuming N1 /= N2, which, if either, of these assignments will have
Discriminant_Check suppressed; and which, if either, will cause
Constraint_Error to be raised?

   V1 := S1 (V2);

   V2 := S2 (V1);

I believe the AI is ambiguous here, and it might be nice if it were
clearer on this.  (The RM section on pragma Suppress doesn't help; it
might be nice if that section were clearer too, in the case where just
one of the involved types is specified in the On parameter of a
Suppress pragma.)

*************************************************************

From: Randy Brukardt
Sent: Monday, October 25, 2004 at  11:06 PM

Adam Beneschan wrote:

> The proposed text for AI-216 says, "Discriminant_Check is suppressed
> for an unchecked union type".  However, there are a couple cases where
> it's not clear what this means.  The Discrminant_Check entry in the
> index refers to 4.6(43) and 4.6(45), in the section on type
> conversions; both of these paragraphs refer to two types, the target
> type and the operand type.  So it isn't clear in those cases which of
> those two types the Discriminant_Check is being performed on, and thus
> which one would be suppressed.

That's certainly true, but it is worse than you think.

I don't think anyone considered conversions between different *types*; all
of the talk was between *subtypes*. So I don't think that there is any idea
at all what should happen in such a case. (Note that such a case can't
really happen in practice, because you can only give the pragma if there is
no primitive operations on the type, its not by-reference, and its
untagged.)

...
> I believe the AI is ambiguous here, and it might be nice if it were
> clearer on this.  (The RM section on pragma Suppress doesn't help; it
> might be nice if that section were clearer too, in the case where just
> one of the involved types is specified in the On parameter of a
> Suppress pragma.)

We discovered during the definition of pragma Unsuppress that there is no
agreement between vendors what the On parameter means. Any attempt to define
it would cause major incompatibilities for someone (probably everyone), and
we decided to just put the entire On parameter in Obsolescent features.

The intent is that we would not say what happened in cases where more than
one entity was involved and the checking was different. Now, you point out
that the definition of Unchecked_Union is depending on precisely that.

I don't have a clue as to how to fix this. Trying to define precisely what
checks are suppressed by a particular type is a can of worms the size of a
watertower - we'll never finish the amendment if we try. And it makes no
sense at all to define it in this only specific case.

I'd prefer to get "suppress" out of this AI, as it has nothing to do with
suppressing checks; there shouldn't be any checks in the first place (the
discriminant values aren't known). But that's a lot of work.

Or we could simply disallow having the pragma on derived types (that isn't
useful, I don't think).

Another choice is to decide to not answer your question, which also seems
bad.

A final choice is to just drop the stupid AI, it's not worth the hassle.

In any event, we'll have to reopen the AI to decide.

           Randy.

P.S. I certainly wish you would have sent this one first; I wouldn't have
wasted 90 minutes fixing the first (easy) problem had I known about this
one.

*************************************************************

From: Adam Beneschan
Sent: Tuesday, October 26, 2004 at  10:34 AM

> P.S. I certainly wish you would have sent this one first; I wouldn't have
> wasted 90 minutes fixing the first (easy) problem had I known about this
> one.

Sorry about that.  As you may have guessed, I'm now working on
implementing this AI, and I sent the problems I noticed as soon as I
noticed them.  FYI, I'm still working on the implementation and
there's a chance I may notice other issues.  If it would help, if I do
any work on any other AI's before the 0Y standard is finalized, I'll
try to send things all at once instead of piecemeal.

*************************************************************

From: Adam Beneschan
Sent: Tuesday, October 26, 2004 at  10:55 AM

I wrote:

> procedure Proc (N1, N2 : Integer) is
>
>    type T1 (X : Integer) is record ... end record; -- with variant part
>    subtype S1 is T1 (N1);
>
>    type T2 is new T1;
>    pragma Unchecked_Union (T2);
>    subtype S2 is T2 (N2);
>
>    V1 : S1;
>    V2 : S2;
>
> Assuming N1 /= N2, which, if either, of these assignments will have
> Discriminant_Check suppressed; and which, if either, will cause
> Constraint_Error to be raised?
>
>    V1 := S1 (V2);
>
>    V2 := S2 (V1);

On second reading, it appears that these questions are at least
partially answered by the !proposal section (the paragraph beginning
"The pragma Unchecked_Union may be applied to a derived type...).  It
would appear that the first conversion is erroneous but does not raise
Constraint_Error (although the !proposal doesn't say specifically what
happens when the operand subtype is constrained; that case probably
wouldn't happen in real code, though).  The second would raise
Constraint_Error.  This paragraph also explains why it was considered
useful to apply the pragma to derived types.

My apologies for missing this paragraph the first time.

However, the language in the !wording and !corrigendum, which simply
says "Discriminant_Check is suppressed for the type", doesn't really
reflect the intent expressed by the !proposal paragraph.

*************************************************************

From: Tucker Taft
Sent: Tuesday, November 29, 2004 at  3:41 PM

I happened to be working on our implementation of unchecked-union,
and I couldn't understand the rationale behind the rules relating
to the stream attributes (B.3.3(27/2,28/2)).

They say:

   Program Error is raised in the following cases:

      * Execution of the default implementation of the Write or Read
        attribute of an unchecked union type

      * Execution of the default implementation of the Output or Input
        attribute of an unchecked union type if the type lacks
        default discriminant values.

The rule for Output and Input seemed exactly backwards to me.

I finally figured it out when I read the AI appendix, but it seems like
an AARM note (or even a user NOTE) could really help here.  The trick
is that the default implementation of Output and Input just turn around
and call 'Write/'Read when the type has default discriminant values,
so there is no need for them to directly raise Program_Error.  That
will happen as soon as 'Write/'Read are called, unless the user has
overridden 'Write or 'Read.  On the other hand, if the type lacks
default discriminant values, then it is the job of Output/Input to
write/read them, and we want *that* to raise Program_Error.

Or we could augment the second bullet to:

      * Execution of the default implementation of the Output or Input
        attribute of an unchecked union type if the type lacks
        default discriminant values Redundant:[ (as this is when
        these attributes do more than simply invoke the corresponding
        Write or Read attribute)].

*************************************************************


Questions? Ask the ACAA Technical Agent