dinkelk
dinkelk

Reputation: 2786

Ada Access to Unconstrained Type at Static Address

The following is illegal Ada:

type Byte_Array is array (Natural range <>) of Interfaces.Unsigned_8;
type Byte_Array_Access is access all Byte_Array;
bytes : aliased Byte_Array(0 .. 9999);
for bytes'Address use To_Address(16#0040_0000#);
bytes_Access : Byte_Array_Access := bytes'Access;

and the compiler produces the error:

object subtype must statically match designated subtype

on the last line. The workaround I have seen is to explicitly initialize the array like this:

bytes : aliased Byte_Array := (0 .. 9999 => 0);

However, this does not work for me as I have nonvolatile data stored at address 16#0040_0000# that cannot be overwritten. What are my options?


More info:

I would like to add that you can avoid explicitly initializing the array via:

bytes : aliased Byte_Array := (0 .. 9999 => <>);

However, this still fails to compile, producing the different error:

aliased object "bytes" with unconstrained array nominal subtype
can overlay only aliased object with compatible subtype

on the line for bytes'Address use To_Address(16#0040_0000#); .

Upvotes: 2

Views: 566

Answers (1)

Simon Wright
Simon Wright

Reputation: 25491

Would it help to make your own sort of 'accessor'? something like

with Ada.Text_IO; use Ada.Text_IO;
with Interfaces;
with System;
with System.Address_Image;
procedure Byte_Arrays is
   type Byte_Array is array (Natural range <>) of Interfaces.Unsigned_8;
   package Access_Byte_Array is
      type Byte_Array_Access is private;
      function Form_Access (Of_Array : Byte_Array) return Byte_Array_Access;
      function Address (Of_Array : Byte_Array_Access) return System.Address;
      function Length (Of_Array : Byte_Array_Access) return Integer;
   private
      type Byte_Array_Access is record
         The_Array : System.Address;
         First : Natural;
         Last : Integer;  -- could have zero length
      end record;
      function Address (Of_Array : Byte_Array_Access) return System.Address
      is (Of_Array.The_Array);
      function Length (Of_Array : Byte_Array_Access) return Integer is
        (if Of_Array.Last < Of_Array.First
         then 0
         else Of_Array.Last - Of_Array.First + 1);
   end Access_Byte_Array;
   package body Access_Byte_Array is
      function Form_Access (Of_Array : Byte_Array) return Byte_Array_Access
      is
      begin
         return (The_Array => Of_Array'Address,
                 First => Of_Array'First,
                 Last => Of_Array'Last);
      end Form_Access;
   end Access_Byte_Array;
   use Access_Byte_Array;
   Bytes : aliased Byte_Array(0 .. -9999);
   for Bytes'Address use System'To_Address(16#0040_0000#);
   Bytes_Access : constant Byte_Array_Access := Form_Access (Bytes);
begin
   Put_Line (Length (Bytes_Access)'Image);
   Put_Line (System.Address_Image (Address (Bytes_Access)));
end Byte_Arrays;

with output

$ ./byte_arrays 
 10000
0000000000400000

[later] For FSF GNAT 10.1.0 on macOS, these changes work:

   type Standard_Access is access all Byte_Array;
...
      function Standard_Accessor
        (Of_Array : aliased Byte_Array_Access) return Standard_Access;
...

      type Bounds is record
         Lower : Integer;
         Upper : Integer;
      end record with Pack;
      type Byte_Array_Access is record
         The_Array  : System.Address;
         The_Bounds : Bounds;
      end record;
...
      function Standard_Accessor
        (Of_Array : aliased Byte_Array_Access) return Standard_Access
      is
         type Raw_Accessor is record
            Contents : System.Address;
            Bounds_P : System.Address;
         end record with Pack;
         function Convert
         is new Ada.Unchecked_Conversion (Raw_Accessor, Standard_Access);
         Raw_Result : constant Raw_Accessor
           := (Contents => Of_Array.The_Array,
               Bounds_P => Of_Array.The_Bounds'Address);
      begin
         return Convert (Raw_Result);
      end Standard_Accessor;

Standard_Accessor’s parameter has to be aliased so that it’s passed by reference rather than on the stack. Of course, you have to be careful not to delete it while the Standard_Access is extant, so perhaps best always declared at package level.

Upvotes: 4

Related Questions