Question

I declare a union type like this:

type Access_Kind is (Named, Indexed);
type Array_Type is array (0 .. 1) of Integer;

type Record_Type (Kind : Access_Kind := Access_Kind'First) is record
   case Kind is
      when Named =>
         X, Y : Integer;
      when Indexed =>
         S : Array_Type;
   end case;
end record;
pragma Unchecked_Union (Record_Type);
pragma Convention (C_Pass_By_Copy, Record_Type);

function Create (X, Y : Integer) return Record_Type;

Now when I try to create a derived type:

type Integer2 is new Record_Type;

GNAT gives me the following warning:

warning: in instantiation at line [pragma Convention...]
warning: variant record has no direct equivalent in C
warning: use of convention for type "Integer2" is dubious

So it looks like the pragma Convention is applied to the derived type, but Unchecked_Union is not. I cannot apply it to the derived type again because Record_Type already has primitive operations defined (Integer2 is defined in another package).

Is this correct behavior or a GNAT bug? How can I correctly derive from Unchecked_Union types so that the new type inherits the Unchecked_Union pragma?

GNAT Version: GNAT GPL 2012 (20120509).

Was it helpful?

Solution

One possibility would be to declare the operations of Record_Type in a nested package, say Ops, so that they aren't primitive:

package Union is

   type Access_Kind is (Named, Indexed);
   type Array_Type is array (0 .. 1) of Integer;

   type Record_Type (Kind : Access_Kind := Access_Kind'First) is record
      case Kind is
         when Named =>
            X, Y : Integer;
         when Indexed =>
            S : Array_Type;
      end case;
   end record;
   pragma Unchecked_Union (Record_Type);
   pragma Convention (C_Pass_By_Copy, Record_Type);

   --  If P was declared immediately within Union, it would be
   --  primitive, and it wouldn't be possible to declare
   --  representation aspects for Integer2.
   package Ops is
      procedure P (R : Record_Type) is null;
      --  "is null" only so that I can use -gnatR without needing a
      --  body.
   end Ops;

   type Integer2 is new Record_Type;
   pragma Unchecked_Union (Integer2);
   pragma Convention (C_Pass_By_Copy, Integer2);

end Union;

Using -gnatR to display the chosen representation, I get

$ gnatmake -c -u -f -gnatwa union.ads -gnatR
gcc -c -gnatwa -gnatR -gnat05 union.ads

Representation information for unit Union (spec)
------------------------------------------------

for Array_Type'Size use 64;
for Array_Type'Alignment use 4;
for Array_Type'Component_Size use 32;

for Record_Type'Size use 64;
for Record_Type'Alignment use 4;
for Record_Type use record
   Kind at ?? range  0 ..  -1;
   X    at  0 range  0 .. 31;
   Y    at  4 range  0 .. 31;
   S    at  0 range  0 .. 63;
end record;

for Integer2'Size use 64;
for Integer2'Alignment use 4;
for Integer2 use record
   Kind at ?? range  0 ..  -1;
   X    at  0 range  0 .. 31;
   Y    at  4 range  0 .. 31;
   S    at  0 range  0 .. 63;
end record;

That said, I think GNAT's behaviour is wrong.

ARM 13.1(0.1) says that there are representational and operational aspects, and (1) defines the representational aspects. (10) is why we need to avoid primitive operations. (15) says that representational aspects are inherited by derived types; but (15.1) says that operational aspects are not, "unless specified" for the specific aspect. I guess that "specified" means "in the ARM for language-defined aspects, or by the vendor for vendor-defined aspects".

B3.3(3.1) states that Unchecked_Union is a representation aspect. It should therefore be inherited.

B3.1(0.1) states that interfacing aspects, including Convention, are representation aspects. C_Pass_By_Copy should therefore be inherited.

I'll work up a bug report.

OTHER TIPS

In Ada 2012, pragma Unchecked_Union is obsolescent, and you can now specify aspect Unchecked_Union. In either case, as discussed in Rationale for Ada 2005: 6.4 Pragmas and Restrictions, the warning is a reminder that unchecked union types were "introduced in Ada 2005 for the sole purpose of interfacing to C programs and not for living dangerously." A type derived from Record_Type is not forbidden; it's just a bad idea, as it propagates the opportunity for erroneous execution, as shown in the Notes section. Instead, encapsulate the union in the body of your binding and derive from your higher level type.

Addendum: Checking an older versions for reference,

GNAT 4.6
Copyright 1996-2010, Free Software Foundation, Inc.
...
Representation information for unit Unchecked (body)
----------------------------------------------------

for Array_Type'Size use 64;
for Array_Type'Alignment use 4;
for Array_Type'Component_Size use 32;

for Record_Type'Size use 64;
for Record_Type'Alignment use 4;
for Record_Type use record
   Kind at ?? range  0 ..  -1;
   X    at  0 range  0 .. 31;
   Y    at  4 range  0 .. 31;
   S    at  0 range  0 .. 63;
end record;

for Integer2'Size use 96;
for Integer2'Alignment use 4;
for Integer2 use record
   Kind at 0 range  0 ..  7;
   X    at 4 range  0 .. 31;
   Y    at 8 range  0 .. 31;
   S    at 4 range  0 .. 63;
end record;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top