Version 1.2 of acs/ac-00332.txt

Unformatted version of acs/ac-00332.txt version 1.2
Other versions for file acs/ac-00332.txt

!standard B.3.3(20/2)          20-09-03 AC95-00332/00
!standard B.3.3(22/2)
!standard B.3.3(23/2)
!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