We recently updated GCC versions (4.8.2 to 5.3.0) and started receiving unexpected constraint errors in some Ada applications. I've reduced it to the following:
-- moo.adb
with text_io;
procedure moo is
type thing_type is (something1,something2,something3,something4,something5,something6);
for thing_type use (something1 => 1,something2 => 2,something3 =>
3,something4 => 4,something5 => 5,something6 => 6);
for thing_type'size use 32;
type thing_array_t is array (0 .. 5) of thing_type;
thing_array : thing_array_t := (others => something1);
begin
text_io.put_line("item 0 = " & thing_type'image(thing_array(0)));
end moo;
This program will compile just fine on either GCC version (simply compiled with "gnatmake moo.adb".) When built with 4.8.2, the output is as expected:
item 0 = SOMETHING1
When built with 5.0.3, we instead receive
raised CONSTRAINT_ERROR : moo.adb:13 invalid data
Interestingly, the results are exactly the same when compiled as 32 and 64 bit. Many things can be changed to make the program work fine with 5.3.0: removing the thing_type'size clause, adding or removing values to the enumerator, changing the number of items in the array, using a different value for initializing the array, etc. Are there any obvious problems with this code that could account for this behavior?
This bug is still present in GCC 7.0.1. Running under the debugger, output slightly edited,
(gdb) catch exception
Catchpoint 2: all Ada exceptions
(gdb) run
Starting program: /Users/simon/tmp/so/moo
[New Thread 0x1703 of process 75511]
Catchpoint 2, CONSTRAINT_ERROR (moo.adb:10 invalid data) at 0x0000000100001abe in _ada_moo () at moo.adb:10
10 text_io.put_line("item 0 = " & thing_type'image(thing_array(0)));
(gdb) p thing_array
$5 = (0 => 16843009, 16843009, 16843009, 16843009, 16843009, 16843009)
(gdb) p/x thing_array
$6 = (0 => 0x1010101, 0x1010101, 0x1010101, 0x1010101, 0x1010101, 0x1010101)
so GNAT has mistakenly set each byte of thing_array
elements to 16#01#
, rather than the whole element.
The same happens if something1
is set to 2 (and later values incremented, likewise).
The only thing I can find that helps is to declare, for example,
type base_thing_type is (invalid, something1,something2,something3,something4,something5,something6);
for base_thing_type use (invalid => 0, something1 => 1,something2 => 2,something3 =>
3,something4 => 4,something5 => 5,something6 => 6);
for base_thing_type'size use 32;
type thing_type is new base_thing_type range something1 .. something6;