Version 1.4 of ai12s/ai12-0377-1.txt
!standard 6.4.1(5.1/4) 20-04-29 AI12-0377-1/01
!standard 6.4.1(5.2/4)
!standard 6.4.1(5.3/4)
!standard 6.4.1(13.2/4)
!standard 6.4.1(13.3/4)
!class Amendment 20-04-20
!status work item 20-04-20
!status received 20-04-14
!priority Low
!difficulty Medium
!subject View conversions and out parameters of types with Default_Value revisited
!summary
An actual of an out parameter that is a view conversion is illegal if either
the target or operand type has Default_Value specified while the other does
not. Program_Error is raised if this occurs where one of the types is a
generic formal type.
!problem
Consider this example:
procedure View_Conversion_With_Default_Value is
type Defaulted_Integer is new Integer with Default_Value => 123;
procedure P (X : out Integer) is
begin
null; --
end P;
X : Defaulted_Integer;
Y : Defaulted_Integer := 456;
begin
P (Integer (X)); --
P (Integer (Y)); --
end View_Conversion_With_Default_Value;
Even though the type of X and Y has specified Default_Value, the value of the
actual isn't copied into the formal in this case (it falls under the rule of
6.4.1(15)), leaving the formal parameter uninitialized. Upon return, the value
of the formal will be converted to Defaulted_Integer and assigned to the actual
object.
If the formal wasn't assigned to within the procedure, as in the above example,
then the (uninitialized) value that comes back will deinitialize the actual
object, which is the sort of thing that shouldn't normally be possible for an
object whose type has a specified Default_Value.
!proposal
(See Summary.)
!wording
Replace 6.4.1(5.1/3) with:
If the mode is out, the actual parameter is a view conversion, and the type of
the formal parameter is a scalar type, then either:
* neither the target nor operand type have the Default_Value aspect specified; or
* there shall exist a type (other than a root numeric type) that is an ancestor of
both the target type and the operand type, and both the target and operand types
have the Default_Value aspect specified.
[Author's note: This removes the access part of this rule, and makes a
version apply to all view conversions.]
Revert 6.4.1(13.1/4) to its previous version.
Delete 6.4.1(13.2-13.4/4), and AARM 6.4.1(13.d/4).
Add after 6.4.1(15):
* Furthermore, if the type is a scalar type, and the actual parameter is a
view conversion, then if neither:
* neither the target nor operand type have the Default_Value aspect specified; or
* there exists a type (other than a root numeric type) that is an ancestor of
both the target type and the operand type, and both the target and operand types
have the Default_Value aspect specified
then Program_Error is raised;
!discussion
"Deinitialization" here cannot introduce an invalid value into an object
that the compiler assumed to be valid (the conversion back to
the actual would still do a subtype check, which would cause Constraint_Error
if the value is invalid. Note that the check would be required since one
cannot assume anything about out parameters). Even so, it seems dangerous
enough that we should take steps to prevent it.
The best solution is to make any view conversion used in an out parameter
illegal if one type has Default_Value and the other does not. Note
that the repeal of 13.1(10/3) by AI12-0376-1 means that having the types be
related no longer prevents problems (as a derived type can define a
Default_Value when the parent type does not have one), so we would have to do
something with this rule in any case.
The alternative of passing the default value in problematic cases would make
the deinitialization noted in the problem worse (since it would clobber the
current value of the object unconditionally, even if it would be in range),
so that is not a viable solution to this problem.
!ASIS
No ASIS effect.
!ACATS test
An ACATS B-Test is needed to check that the new rules are enforced,
rather than the previous rules.
!appendix
From: Gary Dismukes
Sent: Tuesday, April 14, 2020 5:43 PM
Recently, while fixing a GNAT problem related to passing view conversions to
out-mode parameters when Default_Value is involved (related to the rules added
in AI12-0074), it occurred to me that there's a similar situation that can
arise when the formal type doesn't have aspect Default_Value, but the type of
the operand of the view conversion does have the aspect. (AI12-0074 addressed
the case of formal types that specific Default_Value, requiring the type of an
operand of a view conversion actual to also specify Default_Value, and this is
sort of the reverse of that.)
Consider this example, which is a minor variation of the one in the AI:
procedure View_Conversion_With_Default_Value is
type Defaulted_Integer is new Integer with Default_Value => 123;
procedure P (X : out Integer) is
begin
null; -- X happens to be unassigned
end P;
X : Defaulted_Integer;
Y : Defaulted_Integer := 456;
begin
P (Integer (X)); -- Deinitialize X?
P (Integer (Y)); -- Deinitialize Y?
end View_Conversion_With_Default_Value;
Even though the type of X and Y has specified Default_Value, the value of the
actual isn't copied into the formal in this case (it falls under the rule of
6.4.1(15)), leaving the formal parameter uninitialized. Upon return, the value
of the formal will be converted to Defaulted_Integer and assigned to the actual
object.
If the formal wasn't assigned to within the procedure, as in the above example,
then the (uninitialized) value that comes back will deinitialize the actual
object, which is the sort of thing that shouldn't normally be possible for an
object whose type has a specified Default_Value.
You could argue that you shouldn't have a formal of mode out that isn't
assigned to, but that's kind of beside the point, because the normal guarantee
of Default_Value, that variables of the type always have a good value, is
violated in this case, so we clearly have a safety hazard. To avoid this, the
rules either need to require the value of the actual to be assigned to the
formal going in, or such a conversion needs to be rejected. I was initially
leaning towards the former, but Tuck has convinced me that this should be
illegal. Basically we don't want to impose the penalty of preventing an
optimization like transforming a procedure with a single elementary out formal
into a function returning the parameter, just to protect against what is going
to be the rare case of passing a view conversion of an operand with
Default_Value.
So it seems that we need to augment the rules of 6.4.1(5.1-5.3), which require
the type of the operand of a view conversion to have Default_Value when the
formal's type has Default_Value. We need to also disallow the case where a
view conversion operand's type has Default_Value but the formal's type
doesn't.
I believe we'll also need to add something along the lines of the rules in
6.4.1(13.1-13.4) that require Program_Error be raised, to address call cases
within generic bodies when formal types are involved (those rules were also
added by AI12-0074).
****************************************************************
From: Tucker Taft
Sent: Tuesday, April 14, 2020 7:49 PM
We have discussed this internally at AdaCore, and concluded that we should
make it illegal to pass a scalar type with Default_Value in a view conversion
to an OUT formal parameter without a Default_Value. Requiring "copy in" is
not practical given the way that OUT parameters of a scalar type are sometimes
implemented (such as transforming the procedure to a function and returning
the OUT parameter as a function result).
****************************************************************
From: Randy Brukardt
Sent: Tuesday, April 21, 2020 7:57 PM
> Recently, while fixing a GNAT problem related to passing view
> conversions to out-mode parameters when Default_Value is
> involved (related to the rules added in AI12-0074), it
> occurred to me that there's a similar situation that can
> arise when the formal type doesn't have aspect Default_Value,
> but the type of the operand of the view conversion does have
> the aspect. (AI12-0074 addressed the case of formal types
> that specific Default_Value, requiring the type of an operand
> of a view conversion actual to also specify Default_Value,
> and this is sort of the reverse of that.)
I'm not throughly convinced that this problem (in any form) is worth worrying
about, since it can't cause an invalid value to be introduced to the program
(at least in a correct implementation). If the implementation is assuming that
the actual object is valid, then a constraint check is necessary at the back
assignment and that would prevent any invalid values from being stored. The
value would indeed be junk, but that's what was written (and it happens for
all scalar out parameters).
Anyway, assuming that we do want to keep this illegal in any case, we need to
cover *this* case as well. Attached is an attempt at doing that, and also
handling the access problem (with a solution close to what GNAT is currently
doing).
I suspect that the AI and especially the wording will need some wordsmithing.
****************************************************************
Questions? Ask the ACAA Technical Agent