Search code examples
arraysadamemory-address

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#); .


Solution

  • 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.