Required behavior for unchecked conversions AI-00590/06 1
93-06-24 BI WA
| !standard 13.10.02 (03) 93-06-24 AI-00590/06
!class binding interpretation 89-09-26
| !status approved by WG9 93-06-18
!status ARG-approved 93-01-18 (reviewed)
!status ARG-approved (11-0-2) 89-10-25 (pending editorial review)
!status received 88-09-02
!references AI-00536, AI-00825, 83-01000, 83-01364
!topic Required behavior for unchecked conversions
!SUMMARY 93-01-25
If UNCHECKED_CONVERSION is instantiated with types that have the same size,
the effect of calling the instantiated function is to return the
(uninterpreted) parameter value as a value of the target type, that is, the
bit pattern defining the source value has the length of the type and is
returned unchanged as the bit pattern defining a value of the target type.
However, if the source and target types do not have the same size, or if one
of these types is a task type, a type with discriminants, an unconstrained
array type, a composite type having one of these types as a component, or a
private type whose full type is one of these types, the instantiation may be
rejected; if not rejected, the effect of the instantiated function must be
documented in appendix F.
!QUESTION 92-12-14
13.10.2(2-3) say:
The effect of an unchecked conversion is to return the
(uninterpreted) parameter value as a value of the target type, that
is, the bit pattern defining the source value is returned unchanged
as the bit pattern defining a value of the target type. An
implementation may place restrictions on unchecked conversions, for
example, restrictions depending on the respective sizes of objects of
the source and target type. Such restrictions must be documented in
appendix F.
Whenever unchecked conversions are used, it is the programmer's
responsibility to ensure that these conversions maintain the
properties that are guaranteed by the language for objects of the
target type. Programs that violate these properties by means of
unchecked conversions are erroneous.
Although 13.10.2(2) seems to imply that the effect of an unchecked conversion
can always be implementation dependent, are there not some situations in
which all implementations should be expected to behave consistently? For
example, consider:
type BYTE is range -128..127;
for BYTE'SIZE use 8;
Required behavior for unchecked conversions AI-00590/06 2
93-06-24 BI WA
type BIT_ARRAY is array (1..8) of BOOLEAN;
pragma PACK (BIT_ARRAY); -- assume BIT_ARRAY'SIZE = 8
function TO_BITS is new UNCHECKED_CONVERSION (BYTE, BIT_ARRAY);
function TO_BYTE is new UNCHECKED_CONVERSION (BIT_ARRAY, BYTE);
X : BYTE := -6;
Y : BIT_ARRAY := TO_BITS(X);
Z : BYTE := TO_BYTE (Y); -- X = Z? (yes)
Does the value of X equal the value of Z? Is the answer unchanged regardless
of the sizes of objects X, Y, and Z? Is the answer unchanged even if X, Y,
and Z occupy 32 bits and the array value is stored in the leftmost part of a
word?
In short:
1. Does the standard place any implementation-independent
restrictions on the behavior of instantiations of UNCHECKED_
CONVERSION? In particular, if the source and target types have
the same size (whether or not the size is specified by a
clause), must an instantiation of UNCHECKED_CONVERSION treat the
values of the types as having the specified size?
There are also some other questions concerning the required treatment of
unchecked conversion:
2. Is an implementation allowed to prohibit instantiations of
unchecked conversion when one of the parameters is an
unconstrained array type or an unconstrained type with
discriminants?
3. Is an implementation required to specify in Appendix F how it
treats instantiations of unchecked conversions, so programmers
will know which effect in the above examples will be obtained?
!RECOMMENDATION 90-05-22
See the summary.
!DISCUSSION 93-01-25
The first sentence of 13.10.2(2) says:
The effect of an unchecked conversion is to return the
(uninterpreted) parameter value as a value of the target type, that
is, the bit pattern defining the source value is returned unchanged
as the bit pattern defining a value of the target type.
Since an instantiation of UNCHECKED_CONVERSION yields a function that
operates on the value of its parameter, the conversion function must
Required behavior for unchecked conversions AI-00590/06 3
93-06-24 BI WA
determine which bits represent the value of the parameter. These bits are
then to be returned as a representation of a value of the result type.
Values of different types are, in general, not all represented with the same
number of bits. Let's consider the simplest case first -- when the values of
the parameter and result type are represented using the same number of bits.
The only construct that Ada provides for determining the number of bits
occupied by a value of a type is the SIZE attribute applied to a type. Other
commentaries (AI-00536) specify how the value of the SIZE attribute is
determined for specific types. In particular, if a length clause has been
specified for a type, the SIZE attribute returns the specified value. If no
length clause has been specified, then the SIZE attribute returns, in
essence, the number of bits that an implementation will use when storing
values of the type in a packed record or packed array. (This need not be the
minimal number of bits; see AI-00536). From the viewpoint of unchecked
conversion, whatever this value is determined to be, the associated number of
bits is always sufficient to represent a value of the type.
Now let's consider the example given in the question. X'SIZE, the number of
bits occupied by X's value, could be any value greater than or equal to 8
(see AI-00536). In particular, suppose it is 32, i.e., suppose X occupies a
full word on a 32-bit architecture. Similarly, suppose that the bits
representing the value of Y occupy the leftmost 8 bits of a 32-bit word,
while the bits representing the value of X occupy the rightmost bits when the
values are stored in memory. The recommendation states that the 8 bits
comprising the value of X must become the 8 bits comprising the value of Y,
independent of where these bits are located within the word holding Y's
value. (The recommendation makes no statement about the ordering of the bits
within Y; that is implementation-dependent.)
The initialization of Z then stores the eight bits comprising the value of Y
as a value of type BYTE. Since no changes are being made to the eight bits
during these conversions, the value of Z must equal the value of X. Of
course, if X's value is represented as a 32-bit signed twos-complement
integer, this means the (eight-bit) value of Y must be sign-extended before
it is stored in Z. The need for sign-extension only exists in this case
because the implementation has chosen to store BYTE's eight-bit values as
32-bit signed twos-complement integers. If instead, the implementation had
chosen to store BYTE values in only eight bits with 24 bits of padding, then
sign-extension would not be necessary.
In one sense, this discussion of sign-extension and eight-bit values is
irrelevant because the language does not define how values are represented.
The most that can be said with certainty is that since unchecked conversions
do not change bit patterns, certain sequences of conversions will necessarily
leave bit patterns unchanged when all types have the same size. For example,
a conversion of a given value from type A to type B and back to type A must
necessarily yield an unchanged value of type A. And similarly, a conversion
of a value from type A to type B to type C must yield the same result as a
conversion directly from type A to type C.
Required behavior for unchecked conversions AI-00590/06 4
93-06-24 BI WA
The discussion of the example also shows that unchecked conversions must
sometimes generate code (e.g., code to do a sign extension, or, when lengths
are not static, code to extract the appropriate bits).
One problem that concerns some people is the ordering of bits in the
representation of values. For example, consider:
type INT is range -2**15 .. 2**15-1;
for INT'SIZE use 16;
type BYTE_ARRAY is array (1..2) of BYTE;
pragma PACK (BYTE_ARRAY); -- assume BYTE_ARRAY'SIZE = 16
function TO_BYTES is new UNCHECKED_CONVERSION (INT, BYTE_ARRAY);
function TO_INT is new UNCHECKED_CONVERSION (BYTE_ARRAY, INT);
X1 : INT := 2;
Y1 : BYTE_ARRAY := TO_BYTES (X1);
Z1 : INT := TO_INT (Y1);
Following the reasoning of the previous example, it is implementation-
dependent whether BYTE_ARRAY(1) = 0 or 2 (depending on how bytes are ordered
in arrays), but when the reverse conversion is performed, it must be the case
that X1 = Z1. This means implementations must take a consistent view of how
to treat values of a type when performing unchecked conversions but the
language does not define what that view is.
The effect of the recommendation holds even if the lengths of the types are
non-static, e.g., for the type STRING(1..N). Unchecked conversions may be
less efficient in such cases.
The second sentence of 13.10.2(2) says:
An implementation may place restrictions on unchecked conversions,
for example, restrictions depending on the respective sizes of
objects of the source and target type.
The wording here is misleading because no object is associated with the
TARGET type since a conversion function returns a value, not an object.
Similarly, the SOURCE value might not be the name of an object; it might be
an expression, in which case, it makes no sense to speak of the the size of a
source "object". The only sensible way to interpret "sizes of objects of the
source and target type" is with respect to the SIZE attribute of the
respective types, since the value of the SIZE attribute for a type is related
to the size of objects of the type in the sense that all values of the type
can be represented in at least SIZE bits. Moreover, the term "restrictions"
can only be understood in the sense of "conditions under which an
implementation may reject a program." Such restrictions can only be imposed
on instantiations of UNCHECKED_CONVERSION, e.g., an implementation need not
accept an instantiation that involves an unconstrained array type, but having
accepted the instantiation, it cannot reject a call of the conversion
function that has the correct source and target type.
Required behavior for unchecked conversions AI-00590/06 5
93-06-24 BI WA
When the source and target types have different sizes, an implementation must
decide how to truncate or pad bits. Such decisions are not dictated by the
language, but should be documented in appendix F together with other
implementation defined information.