Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Constraint_Error raised when using modular types not divisble by 8

Tags:

ada

gnat

I've encountered an issue where using modular types in Ada that are not divisible by the system's Storage_Unit ( as defined in the runtime's system.ads ) will raise a Constraint_Error at runtime when accessed. I originally encountered this issue working on an bare-metal system using a minimal runtime while trying to read 12bit values from a buffer by overlaying the 12bit array over the buffer in memory. Does anyone know why this is occurring?

The following minimal example illustrates the issue I'm encountering. I tested this using AdaCore's GNAT 2019, compiled with the included zfp runtime. Using the standard runtime does not reproduce the issue.

with Ada.Text_IO; use Ada.Text_IO;

procedure Main is
   ----------------------------------------------------------------------------
   --  Modular type with standard size divisible by 8.
   ----------------------------------------------------------------------------
   type Type_One is mod 2 ** 16;
   type Buffer_Type_One is array (1 .. 128) of Type_One;

   ----------------------------------------------------------------------------
   --  Modular type with non-base 8 size.
   ----------------------------------------------------------------------------
   type Type_Two is mod 2 ** 12;
   type Buffer_Type_Two is array (1 .. 128) of Type_Two;

   type Buffer is array (1 .. 256) of Character;

   ----------------------------------------------------------------------------
   --  Example buffer.
   ----------------------------------------------------------------------------
   Test_Buffer : Buffer := (others => ' ');
begin
   ----------------------------------------------------------------------------
   --  Will not raise an exception.
   ----------------------------------------------------------------------------
   Test_One :
      declare
         Buffer_One : Buffer_Type_One
         with Import,
         Convention => Ada,
         Address    => Test_Buffer'Address;
      begin
         Put_Line ("Testing type one");
         for I in Natural range 1 .. 16 loop
            Put_Line ("Test: " & Buffer_One (I)'Image);
         end loop;
      end Test_One;

   ----------------------------------------------------------------------------
   --  Will raise a constraint error at runtime.
   ----------------------------------------------------------------------------
   Test_Two :
      declare
         Buffer_Two : Buffer_Type_Two
         with Import,
         Convention => Ada,
         Address    => Test_Buffer'Address;
      begin
         Put_Line ("Testing type two");
         for I in Natural range 1 .. 16 loop
            Put_Line ("Test: " & Buffer_Two (I)'Image);
         end loop;
      exception
         when Constraint_Error =>
            Put_Line ("Constraint error encountered.");
      end Test_Two;

end Main;

Here is the project file I used to compile this example:

project Test is

   for Object_Dir use "obj";
   for Exec_Dir use "build";
   for Create_Missing_Dirs use "True";

   for Languages use ("Ada");

   package Builder is
      for Executable ("main.adb") use "test";
   end Builder;

   for Main use ("main.adb");

   package Compiler is
      for Default_Switches ("Ada") use (
        "-gnat2012",
        "-gnatwadehl",
        "-gnatVa",
        "-gnaty3abcdefhiklmnoprstux"
      );
   end Compiler;

   for Runtime ("Ada") use "zfp";
end Test;

I can't seem to find anything in the RM that would indicate why this would happen.

EDIT: Simon Wright below has figured out why this is happening. My naive understanding was that an instance of Buffer_Type_Two overlaid at the specified memory address would interpret the memory at this location as a sequence of 12bit values. It appears that this is not the case. It appears as though the compiler is rounding the size of the type up to 16bits, then raising a Constraint_Error when the 16bit value read from the array does not conform to the 12bit type.

If anyone can think of a better way to read a sequence of 12bit values from a location in memory in a sequential way I would greatly appreciate it, thank you.

like image 404
ajxs Avatar asked May 21 '20 11:05

ajxs


People also ask

Which type does not satisfy the constraint “object”?

[lodash] Type 'T' does not satisfy the constraint 'object'. · Issue #23375 · DefinitelyTyped/DefinitelyTyped · GitHub

What is the modulus of B in DBMS?

b is the divisor which is also a number or expression by which to divide the dividend. If b is 0, most database systems will issue a division by zero error except for Oracle database. Oracle database will return the dividend (a) instead. Most database systems use the following formula to calculate the modulus except for Oracle and MySQL:

What happens if the modulus of B is 0?

If b is 0, most database systems will issue a division by zero error except for Oracle database. Oracle database will return the dividend (a) instead. Most database systems use the following formula to calculate the modulus except for Oracle and MySQL:

Is there a modulo operator in PostgreSQL?

Besides the MOD function, some database systems provide a built-in modulo operator % such as Microsoft SQL, MySQL, and PostgresQL that is equivalent to the MOD function. The following statement divides the number 33 by 5 that results in 6 as the integer portion of the result and 4 as the remainder.


1 Answers

With recent GNATs, you can achieve the behaviour you want by defining Buffer_Type_Two as

type Buffer_Type_Two is array (1 .. 128) of Type_Two
  with Pack;

ARM 13.2(9) warns that this may not do what you want for 13-bit values (recent GNATs do, though).

An alternative would be

type Buffer_Type_Two is array (1 .. 128) of Type_Two
  with Component_Size => 12;

The results are

...
Testing type two
Test:  32
Test:  514
Test:  32
Test:  514
...

For 13 bits, with either approach,

...
Testing type two
Test:  32
Test:  257
Test:  2056
Test:  64
Test:  514
Test:  4112
Test:  128
Test:  1028
Test:  32
...

HOWEVER, for an embedded target, you’ll need to use the -full- runtime system; for others, as pointed out by @egilhh above,

ajxs.adb:14:09: packing of 12-bit components not supported by configuration
like image 188
Simon Wright Avatar answered Oct 05 '22 07:10

Simon Wright