Version 1.3 of ais/ai-00167.txt
!standard 13.09.01 (12) 01-01-29 AI95-00167/01
!class binding interpretation 98-03-18
!status work item 98-03-18
!status received 96-11-16
!priority Low
!difficulty Hard
!subject Scalar unchecked conversion is never erroneous
!summary 98-03-18
Scalar results produced by unchecked conversions or calls on imported
subprograms may be invalid, but not abnormal. The execution of such a call is
not inherently erroneous.
!question 98-03-18
13.9.2(1) says, "The Valid attribute can be used to check the validity of data
produced by unchecked conversion, input, interface to foreign languages, and
the like," but 13.9.1(12) says, "A call to an imported function or an instance
of Unchecked_Conversion is erroneous if the result is scalar, and the result
object has an invalid representation." How can the Valid attribute be used to
check the validity of an unchecked-conversion result without rendering
execution erroneous in the case that the result is invalid?
!response 98-03-18
If a call to an imported function or an instance of Unchecked_Conversion
returns a scalar object whose representation is not the representation of any
value in the return subtype, or if a call to an imported procedure causes a
scalar actual parameter to hold a representation that is not the representation
of any value in the parameter subtype, the function result or actual parameter
holds an invalid representation, but is not abnormal. Notwithstanding
13.9.1(12), the fact that the subprogram call produced an invalid
representation does not make execution of the call erroneous.
!discussion 98-03-18
Implementations that already provide the desired behavior (i.e., disregarding
the fact that execution is formally erroneous in the case of an invalid
unchecked-conversion result, and performing a meaningful validity test for the
Valid attribute) need not change to conform to this interpretation.
Consider the following declarations:
type Setting is (Off, Low, Medium, High);
for Setting use (2#000#, 2#001#, 2#010#, 2#100#);
for Setting'Size use 3;
type Three_Bits is array (Natural range <>) of Boolean;
for Three_Bits'Component_Size use 1;
for Three_Bits'Size use 3;
function Bits_To_Setting is
new Ada.Unchecked_Conversion(Three_Bits, Setting);
Raw_Input : Three_Bits;
Unvalidated_Setting : Setting;
Validated_Setting : Setting;
Input_Error: exception;
According to 13.9.1(12), execution of
Unvalidated_Setting := Bits_To_Setting(Raw_Input);
if Unvalidated_Setting'Valid then
Validated_Setting := Unvalidated_Setting;
else
raise Input_Error;
end if;
is erroneous if Raw_Input does not contain one of the four bit patterns that
are valid representations of Setting values. Execution is rendered erroneous
by the function call in the first assignment statement. Even though an
implementation is likely in practice to behave as expected, raising
Input_Error, execution is, formally, unpredictable from this point on. In
theory, it is permissible to generate code for an attribute X'Valid, where X is
known to be the result of an unchecked conversion, that always yields True
(since the only case in which the attribute would yield False is the case in
which execution is erroneous, and any behavior is permissible in that case)!
13.9(10) and 13.9(11) stipulate that if the representation of the actual
parameter of an unchecked conversion is not "the representation of an object of
the target subtype," then "the effect is implementation-defined; in particular
the result can be abnormal." In the case of a scalar target type, assuming
that the unchecked conversion produces an object with the same bit pattern as
the actual parameter, the result will be invalid, as defined in 13.9.1(2) ("the
object's representation does represent any value of the object's subtype").
It is the intent of the Standard that a scalar unchecked-conversion result
holding an invalid representation is not abnormal. Abnormality is a graver
condition than invalidity. By 13.9.1(1), it is a bounded error to "evaluate
the value" of an object with an invalid representation. This bounded error may
result in an exception or in the use of the invalid representation value, but
not in arbitrary behavior. In contrast, an abnormal object is considered so
seriously corrupted that it is erroneous even to evaluate its name, even as the
prefix of some enclosing name. Such serious corruption can occur in some
composite objects (for example, dope vectors, discriminants, or internal
offsets may be corrupted, causing run-time checks themselves to misbehave). H
owever, the only forms of corrupt scalar data are:
o a representation for an integer-type, enumeration-type, or
floating-point-type object that is outside the range of the object's
subtype
o a representation for an enumeration-type object that is not the
representation of any value in the type
o a representation for a floating-point-type object that is not the
representation of any floating-point value
It is feasible to check for each of these forms of corruption, and the
evaluation of the Valid attribute is expected to do so. (The check for an
invalid representation in an enumeration type with gaps may entail a binary
search of a table of valid representations. The check for an invalid
floating-point representation may entail loading a value into a floating-point
register or adding 0.0 to it, and responding to a resulting hardware trap.)
As explained by the note in 13.9.2(12), evaluation of the attribute X'Valid
does not entail "evaluating the value" of X. Therefore, the Valid attribute of
a scalar unchecked-conversion result can always be evaluated without generating
a bounded error. More importantly, an unchecked conversion that returns an
invalid result does not render execution erroneous.
The use of an invalid unchecked-conversion result in other contexts may result
in a bounded error, but not in erroneous execution. The following example
illustrates the importance of this distinction:
case Bits_To_Setting(Raw_Input) is
when Off => ...
when Low => ...
when Medium => ...
when High => ...
end case;
Suppose Raw_Input does not contain the representation of any Setting value. If
the execution of the unchecked conversion were considered erroneous, it would
be permissible for the implementation to ignore the possibility of an invalid
result. That is, the implementation could optimize away the check called for
by 5.4(13), which verifies that the value of a case-statement expression is
covered by one of the case statement's discrete choice lists. The
justification for eliminating the check is that the check can only fail during
erroneous executions, and any behavior is permissible during erroneous
execution. If the check is eliminated, an invalid result could cause a branch
to an arbitrary address, with catastrophic results.
In fact, the unchecked conversion is not erroneous, but the object it returns
contains an invalid representation. The execution of the case statement
entails the evaluation of this object to obtain its value, and this evaluation
is a bounded error. The possible consequences of this bounded error are
enumerated in 13.9.1(9): Constraint_Error or Program_Error can be raised, or
execution can continue using the invalid representation. If execution
continues using the invalid representation, the check stipulated by 5.4(13) is
performed, raising Constraint_Error.
(It has been suggested that, since 13.9.1(12) applies only to scalars, a
programmer can avoid erroneous execution by having the unchecked conversion
return a one-element record containing the scalar:
type Setting_Container is
record
Only_Component: Setting;
end record;
for Setting_Container use
record
Only_Component at 0 range 0 .. 2;
end record;
for Setting_Container'Size use 3;
function Bits_To_Setting_Container is
new Ada.Unchecked_Conversion(Three_Bits, Setting_Container);
Unvalidated_Setting_Container: Setting_Container;
...
Unvalidated_Setting_Container := Bits_To_Setting_Container(Raw_Input);
if Unvalidated_Setting_Container.Only_Component'Valid then
Validated_Setting := Unvalidated_Setting_Container.Only_Component;
else
raise Input_Error;
end if;
However, by 13.9(11), Bits_To_Setting_Container is permitted to return an
abnormal object if Raw_Input does not contain the representation of a
Setting_Container value. Then evaluation of the call on
Bits_To_Setting_Container is erroneous by 13.9.1(8).)
!appendix
!section 13.9.1(12)
!subject Erroneous scalar Unchecked_Conversion?
!reference RM95-13.9.1(12)
!from Keith Thompson 96-10-07
!reference 96-5719.a Keith Thompson 96-10-7>>
!discussion
RM95-13.9.1(12) says:
A call to an imported function or an instance of
Unchecked_Conversion is erroneous if the result
is scalar, and the result object has an invalid
representation.
This is followed in the AARM by several paragraphs recommending that
implementations should behave sensibly. The last sentence of 12.a says:
We considered requiring such sensible behavior, but
it resulted in too much arcane verbiage, and since
implementations have little incentive to behave
irrationally, such verbiage is not important to have.
Unfortunately, recommending that implementations behave sensibly is not
sufficient. The best policy for a user trying to write good portable
Ada is to avoid erroneous execution altogether, even if some specific
implementations happen to document the particular form of undefined
behavior that they implement.
It should be possible, for example, to use Unchecked_Conversion to
convert an integer value to a sparse enumeration type and apply the
'Valid attribute to the result without invoking erroneous execution.
In any case, this paragraph should have been in 13.9, not 13.9.1.
****************************************************************
>From the minutes of the April 1997 ARG meeting in Henley:
AI-167 Erroneous scalar Unchecked_Conversion?
There is a dilemma here -- the user can't get to check the validity of the
resulting value before the program is *defined" to be erroneous. Sparse
enumerations are a particular source of problems for this AI. One
obvious portable solution is to somehow *promptly" test the validity of
the resulting value before the user does anything else to the value.
Making this work seems too messy to define. A non-portable solution is
to make the situation implementation-defined rather than erroneous.
Another non-portable solution is to raise an exception when an invalid
value is detected during the conversion. This was rejected during the
language design process.
The user can directly handle this problem by doing the unchecked
conversion by either:
_ Converting to an integer, then using a case statement to check
for valid values
_ Wrapping the designated result in a record as the sole
component. The user can then perform a validity check of the
component value. This is due to the fact that there is no
component type checking performed when the assignment is
to a record type.
The group reached consensus that the only option is to confirm the
language on this issue and to expect the user to do the sensible thing to
avoid this problem.
****************************************************************
>From the minutes of the November 1997 ARG meeting in St. Louis:
The group revisits the major points already discussed at the Henley
meeting. The discussion centered on a model that makes the conversion
in question a bounded error, possibly rendering (for conversion to
sparse enumeration) the result to be a value with invalid
representation, eligible to testing by 'Valid. Staying with
erroneousness, there is no opportunity to do anything reasonable,
formally speaking. The sentiment is voiced that that's fine because we
know that, in reality, this conversion and follow-on 'Valid query
would work. The counter-argument is that users want more assurance.
This AI applies to Unchecked_Conversion and imports.
Bob recalls that the erroneous execution for this case was originally
selected for optimization reasons. It is certainly strange that
unchecked conversion of a wrapper record is the way around this
erroneous behavior or any related optimization.
John recommends that the most straightforward way out of this dilemma
is to simply state by fiat than 'Valid when applied [immediately]
after an Unchecked_Conversion whose target is a sparse enumerated type
produces a useable result and not an erroneous execution.
Alternatively, Bob recommends that 13.9.1(12) be changed to returning
a value with invalid representation (which can be subsequently checked
with 'Valid applied to the object with this value).
Norm will produce the write-up of this AI.
****************************************************************
Editor's note:
The priority of this AI was changed based on ARG discussion in November 2000.
****************************************************************
Questions? Ask the ACAA Technical Agent