Flyology GNAT Patchsets

I’d like to share Flyology GNAT Patchsets, a project that curates reproducible GCC/GNAT fixes while they are being reviewed or backported upstream.

Patchset 1.1.0 already includes fixes for:

  • Storage-model component actuals: GNAT 14–16 could bypass Copy_From and Copy_To for selected, indexed, or sliced values reached through an access type with a custom Designated_Storage_Model.

  • Lock-free protected Duration values: with validity checking enabled, GNAT 13–16 could reject a valid negative Duration or even abort during compilation on Linux x86-64, depending on the optimization level.

Each patch includes an executable regression test and is validated against pinned GCC sources. Prebuilt toolchains are available for Linux x86-64, Linux AArch64, and macOS AArch64.

These patches have not yet received extensive upstream review, so they should be considered provisional. However, reproducible source builds and executable regression tests indicate that they address the reported issues without reproducing the original failures.

To try one, first add the Flyology index to Alire:

alr index --add \
  git+https://github.com/flyology-ada/alire-index.git \
  --name flyology --before community

Then select a patched compiler locally in an Alire workspace:

alr -n toolchain --select --local \
  gnat_flyology_native=16.1.0-patchset.1.1.0

Versions based on GNAT 13.2, 14.2, 15.3, and 16.1 are available. The selection is workspace-local, so it does not replace your default Alire toolchain.

Feedback and reports from real-world projects are very welcome.

3 Likes

For a more concrete view, these are the failures from a programmer’s perspective.

1. Components in custom storage models

Assume Arena implements a Storage_Model_Type and stores designated objects outside normal native memory:

type Node;
type Node_Pointer is access Node
  with Designated_Storage_Model => Arena;

type Node is record
   Value : Integer;
   Next  : Node_Pointer;
end record;

P : Node_Pointer := new Node'(Value => 10, Next => null);

procedure Consume (Value : Integer) is
begin
   null;
end Consume;

Accessing the complete object works:

Consume_Node (P.all);  -- Copy_From is called

But on GNAT 14–16, passing one of its components can fail:

Consume (P.Value);

GNAT treats P.Value like an ordinary native-memory access instead of obtaining it through the storage model’s Copy_From. In the regression, this raises CONSTRAINT_ERROR due to an erroneous memory access.

The same problem affects indexed components and in out copy-back:

Consume   (A (2));
Increment (P.Value);

The patch makes these forms use Copy_From and, when needed, Copy_To. The complete executable regression covers selected, indexed, nested, and in out actuals.

2. Negative Duration in a protected object

This one has a much smaller reproducer:

procedure Protected_Duration_Validity is
   protected Control is
      procedure Set (Value : Duration);
      function Get return Duration;
   private
      Current : Duration := 0.0;
   end Control;

   protected body Control is
      procedure Set (Value : Duration) is
      begin
         Current := Value;
      end Set;

      function Get return Duration is
      begin
         return Current;
      end Get;
   end Control;
begin
   Control.Set (-1.0);  -- Valid Duration value
end Protected_Duration_Validity;

On Linux x86-64 with validity checking enabled:

gnatmake -O0 -gnatVa protected_duration_validity.adb

The program builds but raises CONSTRAINT_ERROR: invalid data when assigning the valid negative value.

With optimization:

gnatmake -O2 -gnatVa protected_duration_validity.adb

GNAT 13–16 can instead abort inside fold_convert_loc, without producing an executable.

The patched compilers build and run the complete regression successfully at both optimization levels.

2 Likes