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.
[lodash] Type 'T' does not satisfy the constraint 'object'. · Issue #23375 · DefinitelyTyped/DefinitelyTyped · GitHub
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:
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:
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With