Version 1.11 of ai12s/ai12-0377-1.txt
!standard 6.4.1(5.1/4) 20-07-07 AI12-0377-1/04
!standard 6.4.1(5.2/4)
!standard 6.4.1(5.3/4)
!standard 6.4.1(13.1/4)
!standard 6.4.1(13.2/4)
!standard 6.4.1(13.3/4)
!standard 6.4.1(13.4/4)
!standard 6.4.1(15/3)
!class binding interpretation 20-06-13
!status Amendment 1-2012 20-06-15
!status ARG Approved 13-0-0 20-06-13
!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.
!question
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.
Should this be revisited? (Yes.)
!recommendation
(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 type nor the operand type has the Default_Value aspect
specified; or
* both the target type and the operand type shall have the Default_Value
aspect specified, and there shall exist a type (other than a root numeric
type) that is an ancestor of both the target type and the operand type.
[Editor's note: This removes the access part of this rule, and makes a
version apply to all view conversions. The access part of the rule is
addressed in AI12-0378-1.]
Revert 6.4.1(13.1/4) to its previous version, namely:
For a scalar type that has the Default_Value aspect specified, the
formal parameter is initialized from the value of the actual, without
checking that the value satisfies any constraint or any predicate.
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 Program_Error is raised if either the target or
the operand type has the Default_Value aspect specified, unless they
both have the Default_Value aspect specified, and there is a type
(other than a root numeric type) that is an ancestor of both the
target type and the operand type.
!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. AI12-0074-1 has examples
of "deinitialization" causing problems, particularly with "unused" out
parameters which only return values in some cases.
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 relationship still has value, though
as the set of values will still be related.
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.
!corrigendum 6.4.1(5.1/4)
Replace the paragraph:
If the mode is out, the actual parameter is a view conversion, and the
type of the formal parameter is an access type or a scalar type that has
the Default_Value aspect specified, then
by:
If the mode is out, the actual parameter is a view conversion, and the
type of the formal parameter is a scalar type, then
!corrigendum 6.4.1(5.2/4)
Replace the paragraph:
- 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
by:
- neither the target type nor the operand type has the Default_Value
aspect specified; or
!corrigendum 6.4.1(5.3/4)
Replace the paragraph:
- in the case of a scalar type, the type of the operand of the
conversion shall have the Default_Value aspect specified.
by:
- both the target type and the operand type shall have the Default_Value
aspect specified, and there shall exist a type (other than a root numeric
type) that is an ancestor of both the target type and the operand type.
!corrigendum 6.4.1(13.1/4)
Replace the paragraph:
- For a scalar type that has the Default_Value aspect specified,
the formal parameter is initialized from the value of the actual,
without checking that the value satisfies any constraint or any
predicate, except in the following case: if the actual parameter
is a view conversion and either
by:
- For a scalar type that has the Default_Value aspect specified, the
formal parameter is initialized from the value of the actual, without checking
that the value satisfies any constraint or any predicate.
!corrigendum 6.4.1(13.2/4)
Delete the paragraph:
- there exists no type (other than a root numeric type) that
is an ancestor of both the target type and the type of the
operand of the conversion; or
!corrigendum 6.4.1(13.3/4)
Delete the paragraph:
- the Default_Value aspect is unspecified for the type of the
operand of the conversion
!corrigendum 6.4.1(13.4/4)
Delete the paragraph:
then Program_Error is raised;
!corrigendum 6.4.1(15)
Insert after the paragraph:
- For any other type, the formal parameter is uninitialized. If
composite, a view conversion of the actual parameter to the nominal
subtype of the formal is evaluated (which might raise Constraint_Error),
and the actual subtype of the formal is that of the view conversion. If
elementary, the actual subtype of the formal is given by its nominal subtype.
the new paragraph:
- Furthermore, if the type is a scalar type, and the actual parameter is a
view conversion, then Program_Error is raised if either the target or
the operand type has the Default_Value aspect specified, unless they
both have the Default_Value aspect specified, and there is a type
(other than a root numeric type) that is an ancestor of both the
target type and the operand type.
!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.
****************************************************************
[Editor's note: Some of the e-mails in the following thread discussed topics
in both this AI and AI12-0378-1. The e-mails were split and filed in the
appropriate AI to avoid confusion.]
From: Tucker Taft
Sent: Wednesday, April 29, 2020 11:34 AM
Wording suggestion:
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:
* the target and operand type do not have the Default_Value aspect
specified; or
* the target and operand type both have the Default_Value aspect
specified, and there shall exist a type (other than a root numeric
type) that is an ancestor of both the target type and the operand type.
****************************************************************
From: Randy Brukardt
Sent: Wednesday, April 29, 2020 3:32 PM
BTW, Claire, if you could provide an example of the problem that you were
concerned about with the AI12-0377-1 proposal, it would be appreciated.
My incomplete understanding of your concern made it appear that the only
solution would be to ban all view conversions of types with Default_Value
unless the defaults were the same. That would prevent declaring (or at
least using) any derived types with different defaults (if there are
primitive operations) -- but that was specifically the case that Arno wanted
us to allow as he has customer code that depends upon it. It doesn't seem
possible to thread that needle!
****************************************************************
From: Erhard Ploedereder
Sent: Wednesday, April 29, 2020 4:57 PM
> 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:
>
> * the target and operand type do not have the Default_Value aspect
> specified; or
I doubt that a Float and a Boolean, both without a Default_Value, are o.k.
in a view conversion :-) (There is a convertability constraint missing).
****************************************************************
From: Tucker Taft
Sent: Wednesday, April 29, 2020 5:18 PM
...
>> 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:
>>
>> * the target and operand type do not have the Default_Value aspect
>> specified; or
>
> I doubt that a Float and a Boolean, both without a Default_Value, are
> o.k. in a view conversion :-) (There is a convertability constraint
> missing).
This is the dynamic semantics. The legality rules have already done their
thing, so we only need to explain what happens with legal view conversions.
****************************************************************
From: Erhard Ploedereder
Sent: Thursday, April 30, 2020 8:46 AM
>> I doubt that a Float and a Boolean, both without a Default_Value, are
>> o.k. in a view conversion :-) (There is a convertability constraint
>> missing).
>>
> This is the dynamic semantics. The legality rules have already done their
> thing, so we only need to explain what happens with legal view conversions.
>
o.k. Understood. Or maybe only half of it, because the next bullet
> * the target and operand type both have the Default_Value aspect
> specified, and there shall exist a type (other than a root numeric
> type) that is an ancestor of both the target type and the operand type.
includes a compatibility constraint as well, which ought to have been checked
by static semantics as well. (That part of the wording triggered my comment in
the first place, a "why here, but not there?"-question).
****************************************************************
From: Claire Dross
Sent: Thursday, April 30, 2020 3:28 AM
Honestly, I think I will need some help to understand the rationale of the
rules for passing of OUT parameters with default values. Yesterday, we said
that we did not want a null procedure to deinitialize a scalar variable whose
type has a Default_Value aspect. But I don't really understand why. Surely
null procedures will deinitialize variables which don't have this aspect, and
this is a bounded error, right? So to say it otherwise, I think
procedure P (X : out T) is
begin
null;
end P;
where T is an integer type without a Default_Value is wrong. Why should we do
anything to support correctly (reject correctly) the corner case of call to
such a function on a view conversion?
****************************************************************
From: Jeff Cousins
Sent: Thursday, April 30, 2020 3:57 AM
To be honest, I agree with Claire. To not assign anything to an out parameter
is plain wrong so I don't see why we should go to a lot of trouble to get it
to return something sensible in certain circumstances. (A former colleague
used somewhat stronger language).
****************************************************************
From: Tucker Taft
Sent: Thursday, April 30, 2020 8:08 AM
One of the fundamental "promises" associated with types with a default, is
they never become de-initialized.
****************************************************************
From: Erhard Ploedereder
Sent: Thursday, April 30, 2020 8:28 AM
To me, the foremost reason in favor of the AI is that I would like to rely on
the fact that any variable with specified default_value is guaranteed to
always be initialized. No buts, ifs, and whens, or special cases or corners
that I need to check for after all.
Just like non-null types .... no buts, ifs, except whens....
And as to risk: a deinitialized Integer is almost as bad as a deinitialized
access value. All I need to do is use it as index into an array, where
matching subtype properties are statically established and hence the index
check unnecessary. (I can even exploit that security hole by placing the
"right value" into place by a preceeding call.)
****************************************************************
From: Claire Dross
Sent: Thursday, April 30, 2020 8:51 AM
> To me, the foremost reason in favor of the AI is that I would like to
> rely on the fact that any variable with specified default_value is
> guaranteed to always be initialized. No buts, ifs, and whens, or special
> cases or corners that I need to check for after all.
But do we achieve that by the above?
Here is an example:
procedure Test_Out_Param with SPARK_Mode is
type My_Neg is new Integer range Integer'First .. -1 with
Default_Value => -1;
type My_Pos is new Integer range 1 .. Integer'Last with
Default_Value => 1;
procedure P (X : out My_Neg) is
begin
null;
end P;
Y : My_Pos;
begin
P (My_Neg (Y));
end Test_Out_Param;
With the change proposed in this AI, I get that after a call to P, Y
indeed is still properly initialized. But what about inside P? Certainly
X is not in the bounds of My_Neg, and the fact that its initial value is set
by the default of some other type or not does not change anything about
that...
****************************************************************
From: Tucker Taft
Sent: Thursday, April 30, 2020 9:04 AM
Being initialized, but out of range, and having a random uninitialized value,
are two different things. A compiler cannot assume an OUT parameter is
initialized to an in-range value. But if it has a default, it can assume it
is not a random bit pattern, so a constraint check, for example, will have a
well-defined result.
****************************************************************
From: Bob Duff
Sent: Thursday, April 30, 2020 9:20 AM
> To me, the foremost reason in favor of the AI is that I would like to
> rely on the fact that any variable with specified default_value is
> guaranteed to always be initialized. No buts, ifs, and whens, or
> special cases or corners that I need to check for after all.
>
> Just like non-null types .... no buts, ifs, except whens....
Right, that's the property this AI is trying to preserve.
> And as to risk: a deinitialized Integer is almost as bad as a
> deinitialized access value. All I need to do is use it as index into
> an array, where matching subtype properties are statically established
> and hence the index check unnecessary. (I can even exploit that
> security hole by placing the "right value" into place by a preceeding
> call.)
True in Ada 83, but now it's a bounded error, so "A(I) := ..."
must either modify some element of A, or raise an exception if I is invalid.
****************************************************************
From: Claire Dross
Sent: Thursday, April 30, 2020 9:22 AM
> Being initialized, but out of range, and having a random uninitialized
> value, are two different things. A compiler cannot assume an OUT parameter
> is initialized to an in-range value. But if it has a default, it can
> assume it is not a random bit pattern, so a constraint check, for example,
> will have a well-defined result.
I must say the difference on integer types does not seem obvious to me. And
the problem raised by Erhard seems to still occur?
****************************************************************
From: Bob Duff
Sent: Thursday, April 30, 2020 11:49 AM
> I must say the difference on integer types does not seem obvious to
> me. And the problem raised by Erhard seems to still occur?
No, adding to your earlier example:
procedure Test_Out_Param with SPARK_Mode is
type My_Neg is new Integer range Integer'First .. -1 with
Default_Value => -1;
type My_Pos is new Integer range 1 .. Integer'Last with
Default_Value => 1;
A : array (My_Neg range -1 .. -1) of Integer := (others => 0);
procedure P (X : out My_Neg) is
begin
A (X) := 123; -- Constraint_Error raised here.
end P;
Y : My_Pos;
begin
P (My_Neg (Y));
end Test_Out_Param;
The above is required to raise C_E rather than trash some memory location.
GNAT does that correctly (at least for this example).
The compiler is not allowed to eliminate checks based purely on the subtype's
range -- it must also prove that the value is valid. This was an Ada 95
change.
****************************************************************
From: Bob Duff
Sent: Thursday, April 30, 2020 11:53 AM
Here's another variation on that example:
procedure Test_Out_Param with SPARK_Mode is
type My_Neg is new Integer range Integer'First .. -1 with
Default_Value => -1;
type My_Pos is new Integer range 1 .. Integer'Last with
Default_Value => 1;
procedure P (X : out My_Neg) is
begin
X := -1;
end P;
Y : My_Pos;
begin
P (My_Neg (Y)); -- Constraint_Error raised on copy back.
end Test_Out_Param;
****************************************************************
From: Randy Brukardt
Sent: Thursday, April 30, 2020 2:02 PM
>Just like non-null types .... no buts, ifs, except whens....
Actually, there is one "but" with null exclusions, one that I noticed working
on this AI:
Ptr : Some_Ptr := null;
procedure P (A : out not null Some_Ptr) is
-- Can't assume that A is not null here.
end P;
P (Ptr);
A null exclusion on an out parameter cannot be assumed to be true, since there
is no way to initialize it with a non-null value if the actual is in fact null
(as in this example). We surely don't want to be conjuring a junk object out
of the ether just so we can pass it on the possible chance that someone might
read it. And we don't want to be raising exceptions for passing a value that
probably never will be used.
Which probably goes to show that there is no rule that has no exceptions. :-)
****************************************************************
From: Tucker Taft
Sent: Thursday, April 30, 2020 2:16 PM
For OUT parameters, you generally have to assume the constraints are *not*
satisfied on the way in. The point about having a default value, as do all
access objects, is that you can presume it has a *well defined* value. The
constraints on an "out" parameter are checked only on the way "out" so you
should interpret an out parameter as always being a completely unconstrained
object initially, that has some constraints you had better satisfy before you
do a return.
****************************************************************
From: Randy Brukardt
Sent: Thursday, April 30, 2020 2:45 PM
Right. But that means that whatever you can assume about every other object
in an Ada program does not apply to "out" parameters. (with the exception of
the tag and accessibility checks that would be impossible to properly check
on a usage, the focus of AI12-0378-1, the split from AI12-0377-1 as requested
yesterday). My point to Erhard's statement is that there is always a "but"
when it comes to "out" parameters.
****************************************************************
From: Richard Wai
Sent: Thursday, April 30, 2020 2:59 PM
I always saw such null excluded out parameters as expressing a contract on
the subprogram. So a null exclusion on an “out” parameter is a contract which
states that the subprogram promises to set the parameter to something not null.
Similarly if you had an out parameter of some constrained subtype, that’s a
contract on the subprogram to produce a value of that subtype.
I don’t see anything wrong with this:
Type Day is (Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday);
Subtype Workday is Day range Monday .. Friday;
Procedure Next_Meeting (On_Day: out Workday);
My_Busy_Day: Day := Saturday;
Next_Meeting (My_Busy_Day);
The contract is that Next_Meeting should only give me a Workday. But I
shouldn’t be stopped from using a parameter of Day, especially if it is not
already set to a value in the range of Workday..
****************************************************************
From: Erhard Ploedereder
Sent: Friday, May 1, 2020 8:10 AM
Interesting .... but maybe there was a bug in the example? The semantics
that you describe seems too be "in out" semantics.
See -- for inserts and remarks ...
>> I must say the difference on integer types does not seem obvious to
>> me. And the problem raised by Erhard seems to still occur?
>
> No, adding to your earlier example:
>
> procedure Test_Out_Param with SPARK_Mode is
> type My_Neg is new Integer range Integer'First .. -1 with
> Default_Value => -1;
> type My_Pos is new Integer range 1 .. Integer'Last with
> Default_Value => 1;
>
> A : array (My_Neg range -1 .. -1) of Integer := (others => 0);
-- added another array
B : array (My_Pos range 1 .. 1) of Integer := (others => 0);
>
> procedure P (X : out My_Neg) is
> begin
> A (X) := 123; -- Constraint_Error raised here.
-- I would have expected that the Default_Value -1 applies and A(-1)
-- is perfectly fine, but
B (X) := 123; -- has to raise Constraint_Error for B(-1)
> end P;
>
> Y : My_Pos;
> begin
> P (My_Neg (Y));
-- has to raise Constraint_Error on the way out, when X (=-1) gets
-- written to Y per copy semantics for scalars
> end Test_Out_Param;
****************************************************************
From: Bob Duff
Sent: Friday, May 1, 2020 10:28 AM
> Interesting .... but maybe there was a bug in the example? The
> semantics that you describe seems too be "in out" semantics.
Right, 'out' parameters sometimes pass information IN, which is strange.
> > A (X) := 123; -- Constraint_Error raised here.
>
> -- I would have expected that the Default_Value -1 applies and A(-1)
> -- is perfectly fine, but
That's not an unreasonable expectation, but I guess that's not what language
designers decided. The goal was "if it has a default, it can't get
deinitialized", and never mind if the default is outside the bounds of the
subtype, as we see in these weird 'out' param cases.
> B (X) := 123; -- has to raise Constraint_Error for B(-1)
>
> > end P;
> >
> > Y : My_Pos;
> > begin
> > P (My_Neg (Y));
>
> -- has to raise Constraint_Error on the way out, when X (=-1) gets
> -- written to Y per copy semantics for scalars
If it were assigned to -1 inside the procedure, then it would raise C_E on copy
back. But if it's not assigned at all (which is a weird thing to do), then it
remains +1, which copies back without C_E.
Not sure I would have designed it that way...
****************************************************************
From: Erhard Ploedereder
Sent: Friday, May 1, 2020 11:16 AM
> Right, 'out' parameters sometimes pass information IN, which is
> strange.
Well, yes, sometimes, but not the value in the scalar case with copy
semantics. My reading of the Manual says that the value is uninitialized
in the absence of Default_Value (8.6.1 (15)), or initialized to a specfified
Default_Value (8.6.1.(13)). There is no third case. In consequence, the final
value is just that if X is not assigned to in the body.
So, your expectation that "1" from Y arrives at X is not justified by
anything in the manual, or is it? And, if the actual value were always passed
in, the Default_Value on out parameters would never engage and hence be
utterly useless.
It may be strange that it always engages for scalar outs, but I much prefer
that to deinitialized variables despite an assertion to the contrary.
****************************************************************
From: Randy Brukardt
Sent: Friday, May 1, 2020 12:29 PM
>> Right, 'out' parameters sometimes pass information IN, which is
>> strange.
>Well, yes, sometimes, but not the value in the scalar case with copy
>semantics.
>My reading of the Manual says that the value is uninitialized in the
>absence of Default_Value (8.6.1 (15)),
You mean 6.4.1(15).
>or initialized to a specfified
>Default_Value (8.6.1.(13)).
6.4.1(13) is about access types (and is one the the rules we need to change
slightly).
6.4.1(13.1/4) is about types with Default_Value and it does not say that.
It says the value of the actual is passed it without any check. That's the
semantics that Ichbiah picked for out parameters like access and composites.
I suppose we *could* have done something different regardless (it was a
totally new feature), but it's getting rather late to change basic semantics
of an Ada 2012 feature. Especially to silently change the semantics of
existing, legal code -- that's the worst kind of incompatibility.
>There is no third case. In consequence, the
>final value is just that if X is not assigned to in the body.
That's true, but you have dreamed up semantics that doesn't exist in the RM.
****************************************************************
From: Erhard Ploedereder
Sent: Friday, May 1, 2020 6:05 PM
Sorry that I mistyped the references. I meant 6.4.1. in both cases.
Here they are again in corrected form:
My reading of the Manual says that the value is uninitialized in the absence
of Default_Value (6.4.1 (15)), or initialized to ... (6.4.1.(13.1/4)).
But you are right. I imagined semantics that never existed. I didn't even read
the sentence to its conclusion because it was so obvious what the initial
value would be. It is hard to believe that the semantics of Default_Value for
out parameters are so misleading -- the type says that the default initial
value is 1, and then means that the initial value is taken from the actual.
Ok. Case closed for me, since the RM is consistent albeit very surprising.
****************************************************************
From: Tucker Taft
Sent: Saturday, May 2, 2020 7:46 AM
>To be honest, I agree with Claire. To not assign anything to an out
>parameter is plain wrong so I don't see why we should go to a lot of trouble
>to get it to return something sensible in certain circumstances. (A former
>colleague used somewhat stronger language).
I remember Bob Morgan used to say something like: "Just because it is
erroneous doesn't mean we should generate code that deletes the disk."
The fact is that most programs contain code whose execution might result in
erroneous execution or produce a bounded error.
We provide certain guarantees so that even though the code may be buggy, you
know the consequences are limited. Ada's access types have always made the
guarantee that an access value is never complete junk. When we introduced the
Default_Value aspect, our goal was to make the same guarantee, namely that a
value of a type with a Default_Value is never complete junk. You may believe
that was a mistake, but we made it many years ago, and at this point, I think
we should not withdraw that guarantee.
We have discovered a corner case with view conversions. I don't think that
means we should say: the guarantee holds except in certain corner cases. We
should come up with a reasonable rule for this corner case.
****************************************************************
From: Tullio Vardanega
Sent: Saturday, May 2, 2020 7:51 AM
This is my view also. Not a view conversion, though.
****************************************************************
From: Claire Dross
Sent: Monday, May 4, 2020 1:50 AM
>I don’t see anything wrong with this:
>
>Type Day is (Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday);
>
>Subtype Workday is Day range Monday .. Friday;
>
>Procedure Next_Meeting (On_Day: out Workday);
>
>My_Busy_Day: Day := Saturday;
>
>Next_Meeting (My_Busy_Day);
>
>The contract is that Next_Meeting should only give me a Workday. But I
>shouldn’t be stopped from using a parameter of Day, especially if it is not
>already set to a value in the range of Workday..
What is more dubious is that nothing prevents me from not setting On_Day to a
Workday in Next_Meeting, and so, even if day has a default value.
****************************************************************
From the WG 9 review, issue #6 [Randy Brukardt]
6.4.1(5.2/5) reads:
the target and operand type both do not have the Default_Value aspect
specified; or
"Type" seems wrong, as there are two types here, so why the singular?
There is similar text in 6.4.1(5.3/5).
Suggested fix:
Tucker Taft suggested replacing the 6.4.1(5.2/5), as the combination of
"both" and "not" is confusing, with:
neither the target type nor the operand type has the Default_Value aspect
specified;
John Barnes suggested moving the "both", and I had noted that the end of
6.4.1(5.3/5) repeats "type", so that gives for 6.4.1(5.3/5):
both the target type and the operand type shall have the Default_Value
aspect specified, and there shall exist a type (other than a root numeric
type) that is an ancestor of both the target type and the operand type.
[Editor's note: This was processed as an Editorial Review on AI12-0377-1.]
****************************************************************
Questions? Ask the ACAA Technical Agent