Version 1.1 of acs/ac-00332.txt
!standard A.5.5(0) 20-09-03 AC95-00332/00
!standard A.5.6(0)
!standard A.5.7(0)
!class Amendment 20-09-03
!status received no action 20-09-03
!status received 20-07-10
!subject Unchecked_Union query
!summary
!appendix
From: Jeff Cousins
Sent: Friday, July 10, 2020 1:55 PM
I’ve been assisting John Barnes in updating his book to get the Ada 2012
baseline correct before adding anything to do with Ada 202X. A question
that has arisen goes back to Ada 2005’s Unchecked_Union.
In the following we think it’s relatively clear that a Program_Error should
be raised comparing X and Y when a default discriminant is used as Number is
mutable, and indeed both GNAT and ObjectAda raise a Program_Error.
with Ada.Text_IO;
procedure Unchecked_Union_With_Default_Test is
type Precision is (Single_Precision, Multiple_Precision);
type Number (Kind : Precision := Single_Precision) is record
case Kind is
when Single_Precision =>
SP_Value : Long_Float;
when Multiple_Precision =>
MP_Value_Length : Integer;
MP_Value_First : access Long_Float;
end case;
end record;
pragma Unchecked_Union (Number);
X : Number := (Single_Precision, 45.6);
Y : Number (Single_Precision); -- Inferable discriminant
begin
Y.SP_Value := 55.7;
if X = Y then -- Raises Program_Error with GNAT and ObjectAda
Ada.Text_IO.Put_Line ("X is Y");
else
Ada.Text_IO.Put_Line ("X isn't Y");
end if;
end Unchecked_Union_With_Default_Test;
But what about when the discriminant doesn’t have a default? GNAT raises a
Program_Error but ObjectAda doesn’t. I think ObjectAda is correct as X is
explicitly set to Single_Precision and this cannot change.
with Ada.Text_IO;
procedure Unchecked_Union_No_Default_Test is
type Precision is (Single_Precision, Multiple_Precision);
type Number (Kind : Precision) is record
case Kind is
when Single_Precision =>
SP_Value : Long_Float;
when Multiple_Precision =>
MP_Value_Length : Integer;
MP_Value_First : access Long_Float;
end case;
end record;
pragma Unchecked_Union (Number);
X : Number := (Single_Precision, 45.6);
Y : Number (Single_Precision); -- Inferable discriminant
begin
Y.SP_Value := 55.7;
if X = Y then -- Raises Program_Error with GNAT but not ObjectAda
Ada.Text_IO.Put_Line ("X is Y");
else
Ada.Text_IO.Put_Line ("X isn't Y");
end if;
end Unchecked_Union_No_Default_Test;
The ACATS 4.1 tests seem to test to death the case of a default discriminant
but not test at all the case of no default.
Thoughts?
***************************************************************
From: Steve Baird
Sent: Friday, July 10, 2020 6:02 PM
> But what about when the discriminant doesn’t have a default? GNAT
> raises a Program_Error but ObjectAda doesn’t. I think ObjectAda is
> correct as X is explicitly set to Single_Precision and this cannot change.
I think GNAT is doing the right thing here.
The RM says Program_Error is raised if
Evaluation of the predefined equality operator for an unchecked union
type if either of the operands lacks inferable discriminants.
So the relevant question here is not "Can a human looking at this example
use a minimal amount of common sense to identify the discriminant value at
compile time?".
The relevant question has to do with the definition of "inferable
discriminants", which in turn becomes a question about the nominal subtype
of the equality operand.
In the example, the nominal subtype of the object Y is unconstrained.
***************************************************************
From: Tucker Taft
Sent: Friday, July 10, 2020 8:33 PM
> In the example, the nominal subtype of the object Y is unconstrained.
The "actual" subtype is "constrained by its initial value" but the "nominal"
subtype is unconstrained, which is what matters for "inferable discriminants."
See RM 3.3.1(9/2) and B.3.3(20/2).
***************************************************************
From: Jeff Cousins
Sent: Saturday, July 11, 2020 9:54 AM
Thanks Steve and Tuck. Maybe time for more ACATS tests, and to do for 3.1 too
as that doesn't have any Unchecked_Union tests.
***************************************************************
From: Randy Brukardt
Sent: Sunday, July 19, 2020 3:44 AM
...
>But what about when the discriminant doesn't have a default? GNAT
>raises a Program_Error but ObjectAda doesn't. I think ObjectAda is
>correct as X is explicitly set to Single_Precision and this cannot change.
Steve gave the correct answer. I want to note that the same sort of effect
happens with aggregates. A modification of the example:
with Ada.Text_IO;
procedure Unchecked_Union_No_Default_Test is
type Precision is (Single_Precision, Multiple_Precision);
type Number (Kind : Precision) is record
case Kind is
when Single_Precision =>
SP_Value : Long_Float;
when Multiple_Precision =>
MP_Value_Length : Integer;
MP_Value_First : access Long_Float;
end case;
end record;
pragma Unchecked_Union (Number);
BTW, this is assuming that Long_Float and Integer and "access Long_Float"
are C-compatible. None of those things are required by Ada. If one wants this
to be portable, one must use types that have convention C (and for an anonymous
type, one would need to declare the entire Number record convention C).
Anyway, back to the example already in progress.
Y : Number (Single_Precision); -- Inferable discriminant
begin
Y.SP_Value := 55.7;
if Y = (Single_Precision, 45.6) then -- Should raise Program_Error
Ada.Text_IO.Put_Line ("Y is single 45.6");
else
Ada.Text_IO.Put_Line ("Y isn't single 45.6");
end if;
end Unchecked_Union_No_Default_Test;
One would have to qualify the aggregate with a constrained subtype (meaning
*that* would have to be declared and given a name) to eliminate the
Program_Error.
Note that an aggregate doesn't even have a nominal subtype, so there's no
obvious way to eliminate this case. OTOH, this is far from an intended use for
an Unchecked_Union, so the oddity shouldn't matter often. Probably not worth
fixing.
***************************************************************
From: John Barnes
Sent: Sunday, July 19, 2020 4:17 AM
Thanks for helping with that. Hopefully that was the last known error in
Programming in Ada 2012. So I should now have a cleanish baseline for moving
forward.
***************************************************************
From: Richard Wai
Sent: Sunday, July 19, 2020 12:02 PM
I'm super looking forward to the new edition!
***************************************************************
From: Jeff Cousins
Sent: Sunday, July 19, 2020 4:21 AM
Thanks Randy.
The ACATS tests turned out not to be terribly useful as they allow a
Program_Error to be raised, by having an exception handler to consume
it, but don't actually check that a Program_Error IS raised.
Hopefully you've received my suggested changes.
***************************************************************
Questions? Ask the ACAA Technical Agent