controlled-subpool-allocator

Controlled allocator named subpools

Controlled aggregate allocators drop an explicitly named subpool while synthesizing their custom allocation procedure.

AcceptedApplies standaloneSince 1.2.0

Where it applies.

How each patchset treats this bundle on each exact GCC target
PatchsetGCC 1313.2.0GCC 1414.2.0GCC 1515.3.0GCC 1616.1.0GCC 1616.2.0
1.2.0 (latest)Patchedgcc-13-1413.2.0Patchedgcc-13-1414.2.0Known-good control15.3.0Not coveredKnown-good control16.2.0
1.1.1Not applicable13.2.0Not applicable14.2.0Not applicable15.3.0Not applicable16.1.0Not applicable16.2.0
1.1.0Not applicable13.2.0Not applicable14.2.0Not applicable15.3.0Not coveredNot applicable16.2.0
1.0.1Not applicable13.2.0Not applicable14.2.0Not applicable15.3.0Not applicable16.1.0Not covered
1.0.0Not applicable13.2.0Not applicable14.2.0Not applicable15.3.0Not applicable16.1.0Not covered

Explanation.

Exact GNAT 13.2, 14.2, and 15.1 sources lose an explicitly named subpool while expanding an allocator for a controlled aggregate. Their expansion paths differ after the controlled-allocation rewrite, but each creates a replacement allocator and omits Subpool_Handle_Name. The generated procedure consequently passes a null subpool and calls the pool's Default_Subpool_For_Pool instead of the allocator's named subpool.

The failure is independent of optimization. At both -O0 and -O2, the executable regression raises:

PROGRAM_ERROR : System.Storage_Pools.Subpools.Default_Subpool_For_Pool:
default Default_Subpool_For_Pool called; must be overridden

An adjacent plain allocator succeeds with the same pool and named subpool, isolating the defect to the controlled-aggregate expansion path.

Correction

The GCC 13/14 variant copies Subpool_Handle_Name from the source allocator onto each synthesized no-initialization allocator before Build_Allocate_Deallocate_Proc examines it. The GCC 15.1 variant propagates the name while constructing each replacement allocator. Both variants cover the ordinary and interface controlled-aggregate branches and make the custom allocator pass the selected subpool just as the source allocator requested.

This is a focused backport of upstream GCC commit 5dd4b922bb341aa3b821e600ff2e46aa6ed6e850 ("Ada: Fix subpool dropped from allocator initialized by aggregate"). The GCC 15.1 exp_ch4.adb hunks are the upstream correction; the GCC 13/14 variant expresses the same invariant in the older expansion. The bundle does not backport the upstream commit's separate build-in-place function and C++ constructor paths in exp_ch6.adb, which are outside this controlled-qualified-expression regression. GCC 15.3.0, 16.1.0, and 16.2.0 contain the correction and are known-good controls. GCC 15.2 has not been tested and is deliberately unclassified.

Executable evidence

tests/controlled_subpool_allocator.adb provides an instrumented Root_Storage_Pool_With_Subpools. It allocates adjacent plain and controlled objects, including one through an interface access type, into one explicit subpool and checks that:

  • all three allocations reach the named subpool;
  • each subpool-selection expression is evaluated exactly once;
  • all allocated values survive;
  • releasing the subpool releases it once and finalizes both controlled objects exactly once.

The runner builds and executes the fixture with assertions enabled at -O0 and -O2. Before the patch it requires the specific default-subpool PROGRAM_ERROR on GCC 13.2.0, 14.2.0, and 15.1.0. After the patch, and on known-good controls, it requires the exact success marker:

PASS controlled named subpool allocator

Local Darwin AArch64 characterization with the pinned bootstrap compilers produced the following matrix:

GCC release-O0-O2Classification
13.2.0expected PROGRAM_ERRORexpected PROGRAM_ERRORaffected
14.2.0expected PROGRAM_ERRORexpected PROGRAM_ERRORaffected
15.1.0expected PROGRAM_ERRORexpected PROGRAM_ERRORaffected
15.3.0passpassknown-good control
16.1.0passpassknown-good control

The 15.1 executable is the official Alire gnat_native 15.1.2 Darwin AArch64 asset gnat-aarch64-darwin-15.1.0-2.tar.gz, whose SHA-256 is 60748c5436aba29243333ca1f26c1ef694749644d2f194ab3cbe9f58c53ca829. The packaged compiler reports 15.0.1 20250418 (prerelease); the GNAT-FSF release and source identity are therefore recorded alongside, rather than silently replacing, the compiler's own version string. The release build specification selects Darwin source ref gcc-15-1-darwin-rc1, now pinned here at commit 845fee6ec56db98b84888f782fe7daea99b4b358, whose gcc/DATESTAMP is 20250418.

From that exact patched Darwin source, a local source build completed gnat1, the GNAT tools, libgcc, and the Ada runtime. The installed compiler passed this runner at both -O0 and -O2. A full local package build under Xcode 26.6 was not claimed: the unmodified GCC 15.1 Darwin libatomic and libstdc++ builds stopped on unrelated unresolved symbols. The GNAT tools used the pinned 15.3 bootstrap's static C++ runtime for their host-tool links; the frontend and Ada runtime exercised by the regression were built from the patched 15.1 source.

The patchset matrix builds GCC 16.2.0 from its exact pinned source and runs it as the control; the older 16.1.0 bootstrap used for that build is not treated as the 16.2.0 executable result.

Source identities and application

The appropriate patch variant applies with patch --fuzz=0 to all six pinned affected source identities:

Release and flavorCommit/tree identityexp_ch4.adb SHA-256
GCC 13.2.0 FSFc891d8dc23e1a46ad9f3e757d09e57b500d40044 / e0036ea6a50a6a9e2b4ffe6e1a7c5276452f1023c2e54ceb420e3146c10d6535f7f4d7d12cf9e0833fcf080fdb92e23aa5f3175a
GCC 13.2.0 Darwin46003d70a6411362e9de99ced8242f52129d5f9a / 608a198adec22ab00cff48d887e30de4dadf2434c2e54ceb420e3146c10d6535f7f4d7d12cf9e0833fcf080fdb92e23aa5f3175a
GCC 14.2.0 FSF04696df09633baf97cdbbdd6e9929b9d472161d3 / 1357078fcfcdd78685d385d705c2fa137251cb010237809863b40b8a19d9a438836aea6781b2ad7493b66cd548af630b2c5a0a6c
GCC 14.2.0 Darwinaac1e84d685e43400873ca882b9c81154f9baa26 / a3f48716d9accaff42c4a64d0fc42aada527708d0237809863b40b8a19d9a438836aea6781b2ad7493b66cd548af630b2c5a0a6c
GCC 15.1.0 FSF1b306039ac49f8ad91ca71d3de3150a3c9fa792a / 9c666d84a329c64b13758acc00820cc34249d22dba49eb279cac6111e91839e7de79527895da51c7f5c064e8fbce2f02cbdf31c8
GCC 15.1.0 Darwin845fee6ec56db98b84888f782fe7daea99b4b358 / 30376aff8a0f6f85da80ea164bd5941375bf0166ba49eb279cac6111e91839e7de79527895da51c7f5c064e8fbce2f02cbdf31c8

The FSF and Darwin exp_ch4.adb blobs are identical within each affected release. GCC 14 moves surrounding code by one line, but both older-version hunks still apply with zero fuzz. GCC 15.1 uses its own variant because the controlled-allocation expansion was restructured. No nearby source, 15.2 inference, or branch-tip substitution is accepted.

Patch.

Variant gcc-13-14

Applies to 13.2.0, 14.2.0. Source flavors: linux, darwin_arm64.

+164 −0 2 files

Download the patch

gcc/ada/exp_ch4.adb +21−0modified
Unified diff for gcc/ada/exp_ch4.adb: original line, patched line, change, source
@@ -770 +770 @@package body Exp_Ch4 is
770770 (Expression (Temp_Decl), N);
771771
772772 Set_No_Initialization (Expression (Temp_Decl));
773Added line.
774Added line. -- Preserve a named subpool from the original allocator. The
775Added line. -- synthesized allocator is the one used to build the custom
776Added line. -- Allocate procedure, so dropping the name here would make a
777Added line. -- controlled allocation use the pool's default subpool.
778Added line.
779Added line. if Present (Subpool_Handle_Name (N)) then
780Added line. Set_Subpool_Handle_Name
781Added line. (Expression (Temp_Decl),
782Added line. New_Copy_Tree (Subpool_Handle_Name (N)));
783Added line. end if;
784Added line.
773785 Insert_Action (N, Temp_Decl);
774786
775787 Build_Allocate_Deallocate_Proc (Temp_Decl, True);
@@ -843 +855 @@package body Exp_Ch4 is
843855 (Expression (Temp_Decl), Comes_From_Source (N));
844856
845857 Set_No_Initialization (Expression (Temp_Decl));
858Added line.
859Added line. -- Preserve a named subpool from the original allocator
860Added line.
861Added line. if Present (Subpool_Handle_Name (N)) then
862Added line. Set_Subpool_Handle_Name
863Added line. (Expression (Temp_Decl),
864Added line. New_Copy_Tree (Subpool_Handle_Name (N)));
865Added line. end if;
866Added line.
846867 Insert_Action (N, Temp_Decl);
847868
848869 Build_Allocate_Deallocate_Proc (Temp_Decl, True);
gcc/testsuite/gnat.dg/controlled_subpool_allocator.adb +143−0new file
Unified diff for gcc/testsuite/gnat.dg/controlled_subpool_allocator.adb: original line, patched line, change, source
@@ -0 +1 @@
1Added line. -- { dg-do run }
2Added line. -- { dg-options "-gnata" }
3Added line.
4Added line. with Ada.Finalization;
5Added line. with Ada.Text_IO; use Ada.Text_IO;
6Added line. with Ada.Unchecked_Deallocate_Subpool;
7Added line. with System;
8Added line. with System.Storage_Elements; use System.Storage_Elements;
9Added line. with System.Storage_Pools.Subpools; use System.Storage_Pools.Subpools;
10Added line.
11Added line. procedure Controlled_Subpool_Allocator is
12Added line. Finalizations : Natural := 0;
13Added line.
14Added line. type Test_Subpool is new Root_Subpool with null record;
15Added line. type Test_Interface is limited interface;
16Added line.
17Added line. type Test_Pool is new Root_Storage_Pool_With_Subpools with record
18Added line. Region : aliased Test_Subpool;
19Added line. Storage : aliased Storage_Array (1 .. 4_096);
20Added line. Next : Storage_Offset := 1;
21Added line. Allocations : Natural := 0;
22Added line. Releases : Natural := 0;
23Added line. end record;
24Added line.
25Added line. overriding
26Added line. function Create_Subpool
27Added line. (Pool : in out Test_Pool) return not null Subpool_Handle;
28Added line.
29Added line. overriding
30Added line. procedure Allocate_From_Subpool
31Added line. (Pool : in out Test_Pool;
32Added line. Storage_Address : out System.Address;
33Added line. Size_In_Storage_Elements : Storage_Count;
34Added line. Alignment : Storage_Count;
35Added line. Subpool : not null Subpool_Handle);
36Added line.
37Added line. overriding
38Added line. procedure Deallocate_Subpool
39Added line. (Pool : in out Test_Pool; Subpool : in out Subpool_Handle);
40Added line.
41Added line. type Tracked is new Ada.Finalization.Limited_Controlled
42Added line. and Test_Interface with record
43Added line. Value : Natural := 0;
44Added line. end record;
45Added line.
46Added line. overriding procedure Finalize (Object : in out Tracked);
47Added line.
48Added line. overriding
49Added line. function Create_Subpool
50Added line. (Pool : in out Test_Pool) return not null Subpool_Handle
51Added line. is
52Added line. Result : constant not null Subpool_Handle :=
53Added line. Pool.Region'Unchecked_Access;
54Added line. begin
55Added line. Set_Pool_Of_Subpool (Result, Pool);
56Added line. return Result;
57Added line. end Create_Subpool;
58Added line.
59Added line. overriding
60Added line. procedure Allocate_From_Subpool
61Added line. (Pool : in out Test_Pool;
62Added line. Storage_Address : out System.Address;
63Added line. Size_In_Storage_Elements : Storage_Count;
64Added line. Alignment : Storage_Count;
65Added line. Subpool : not null Subpool_Handle)
66Added line. is
67Added line. Current : System.Address := Pool.Storage (Pool.Next)'Address;
68Added line. begin
69Added line. if Subpool /= Pool.Region'Unchecked_Access then
70Added line. raise Program_Error with "allocation used an unexpected subpool";
71Added line. end if;
72Added line.
73Added line. while Current mod Alignment /= 0 loop
74Added line. Pool.Next := Pool.Next + 1;
75Added line. Current := Pool.Storage (Pool.Next)'Address;
76Added line. end loop;
77Added line.
78Added line. Storage_Address := Current;
79Added line. Pool.Next := Pool.Next + Storage_Offset (Size_In_Storage_Elements);
80Added line. Pool.Allocations := Pool.Allocations + 1;
81Added line. end Allocate_From_Subpool;
82Added line.
83Added line. overriding
84Added line. procedure Deallocate_Subpool
85Added line. (Pool : in out Test_Pool; Subpool : in out Subpool_Handle)
86Added line. is
87Added line. begin
88Added line. Pool.Releases := Pool.Releases + 1;
89Added line. Subpool := null;
90Added line. end Deallocate_Subpool;
91Added line.
92Added line. overriding procedure Finalize (Object : in out Tracked) is
93Added line. begin
94Added line. if Object.Value /= 7 then
95Added line. raise Program_Error with "controlled object value was not preserved";
96Added line. end if;
97Added line. Finalizations := Finalizations + 1;
98Added line. end Finalize;
99Added line.
100Added line. Pool : Test_Pool;
101Added line. Region : Subpool_Handle := Create_Subpool (Pool);
102Added line. Selections : Natural := 0;
103Added line.
104Added line. function Select_Region return not null Subpool_Handle is
105Added line. begin
106Added line. Selections := Selections + 1;
107Added line. return Region;
108Added line. end Select_Region;
109Added line.
110Added line. type Plain_Access is access Integer with Storage_Pool => Pool;
111Added line. type Tracked_Access is access Tracked with Storage_Pool => Pool;
112Added line. type Interface_Access is access Test_Interface'Class
113Added line. with Storage_Pool => Pool;
114Added line.
115Added line. Plain_Object : Plain_Access := new (Region) Integer'(3);
116Added line. Tracked_Object : Tracked_Access :=
117Added line. new (Select_Region) Tracked'
118Added line. (Ada.Finalization.Limited_Controlled with Value => 7);
119Added line. Interface_Object : Interface_Access :=
120Added line. new (Select_Region) Tracked'
121Added line. (Ada.Finalization.Limited_Controlled with Value => 7);
122Added line.
123Added line. begin
124Added line. if Plain_Object.all /= 3 or else Tracked_Object.Value /= 7 then
125Added line. raise Program_Error with "allocated values were not preserved";
126Added line. end if;
127Added line. if Interface_Object = null or else Pool.Allocations /= 3 then
128Added line. raise Program_Error with "named subpool did not receive all allocations";
129Added line. end if;
130Added line. if Selections /= 2 then
131Added line. raise Program_Error with "subpool expressions were not evaluated once";
132Added line. end if;
133Added line.
134Added line. Ada.Unchecked_Deallocate_Subpool (Region);
135Added line. if Region /= null or else Pool.Releases /= 1 or else Finalizations /= 2 then
136Added line. raise Program_Error with "subpool release did not finalize both objects";
137Added line. end if;
138Added line.
139Added line. Plain_Object := null;
140Added line. Tracked_Object := null;
141Added line. Interface_Object := null;
142Added line. Put_Line ("PASS controlled named subpool allocator");
143Added line. end Controlled_Subpool_Allocator;
144

Variant gcc-15.1

Applies to 15.1.0. Source flavors: linux, darwin_arm64.

+149 −2 2 files

Download the patch

gcc/ada/exp_ch4.adb +6−2modified
Unified diff for gcc/ada/exp_ch4.adb: original line, patched line, change, source
@@ -659 +659 @@package body Exp_Ch4 is
659659 Object_Definition => New_Occurrence_Of (Typ, Loc),
660660 Expression =>
661661 Make_Allocator (Loc,
662Removed line. Expression => New_Occurrence_Of (Etype (Exp), Loc)));
662Added line. Subpool_Handle_Name =>
663Added line. Relocate_Node (Subpool_Handle_Name (N)),
664Added line. Expression => New_Occurrence_Of (Etype (Exp), Loc)));
663665
664666 begin
665667 -- Prevent default initialization of the allocator
@@ -698 +700 @@package body Exp_Ch4 is
698700 Object_Definition => New_Occurrence_Of (Typ, Loc),
699701 Expression =>
700702 Make_Allocator (Loc,
701Removed line. Expression => New_Occurrence_Of (Etype (Exp), Loc)));
703Added line. Subpool_Handle_Name =>
704Added line. Relocate_Node (Subpool_Handle_Name (N)),
705Added line. Expression => New_Occurrence_Of (Etype (Exp), Loc)));
702706
703707 begin
704708 -- Prevent default initialization of the allocator
gcc/testsuite/gnat.dg/controlled_subpool_allocator.adb +143−0new file
Unified diff for gcc/testsuite/gnat.dg/controlled_subpool_allocator.adb: original line, patched line, change, source
@@ -0 +1 @@
1Added line. -- { dg-do run }
2Added line. -- { dg-options "-gnata" }
3Added line.
4Added line. with Ada.Finalization;
5Added line. with Ada.Text_IO; use Ada.Text_IO;
6Added line. with Ada.Unchecked_Deallocate_Subpool;
7Added line. with System;
8Added line. with System.Storage_Elements; use System.Storage_Elements;
9Added line. with System.Storage_Pools.Subpools; use System.Storage_Pools.Subpools;
10Added line.
11Added line. procedure Controlled_Subpool_Allocator is
12Added line. Finalizations : Natural := 0;
13Added line.
14Added line. type Test_Subpool is new Root_Subpool with null record;
15Added line. type Test_Interface is limited interface;
16Added line.
17Added line. type Test_Pool is new Root_Storage_Pool_With_Subpools with record
18Added line. Region : aliased Test_Subpool;
19Added line. Storage : aliased Storage_Array (1 .. 4_096);
20Added line. Next : Storage_Offset := 1;
21Added line. Allocations : Natural := 0;
22Added line. Releases : Natural := 0;
23Added line. end record;
24Added line.
25Added line. overriding
26Added line. function Create_Subpool
27Added line. (Pool : in out Test_Pool) return not null Subpool_Handle;
28Added line.
29Added line. overriding
30Added line. procedure Allocate_From_Subpool
31Added line. (Pool : in out Test_Pool;
32Added line. Storage_Address : out System.Address;
33Added line. Size_In_Storage_Elements : Storage_Count;
34Added line. Alignment : Storage_Count;
35Added line. Subpool : not null Subpool_Handle);
36Added line.
37Added line. overriding
38Added line. procedure Deallocate_Subpool
39Added line. (Pool : in out Test_Pool; Subpool : in out Subpool_Handle);
40Added line.
41Added line. type Tracked is new Ada.Finalization.Limited_Controlled
42Added line. and Test_Interface with record
43Added line. Value : Natural := 0;
44Added line. end record;
45Added line.
46Added line. overriding procedure Finalize (Object : in out Tracked);
47Added line.
48Added line. overriding
49Added line. function Create_Subpool
50Added line. (Pool : in out Test_Pool) return not null Subpool_Handle
51Added line. is
52Added line. Result : constant not null Subpool_Handle :=
53Added line. Pool.Region'Unchecked_Access;
54Added line. begin
55Added line. Set_Pool_Of_Subpool (Result, Pool);
56Added line. return Result;
57Added line. end Create_Subpool;
58Added line.
59Added line. overriding
60Added line. procedure Allocate_From_Subpool
61Added line. (Pool : in out Test_Pool;
62Added line. Storage_Address : out System.Address;
63Added line. Size_In_Storage_Elements : Storage_Count;
64Added line. Alignment : Storage_Count;
65Added line. Subpool : not null Subpool_Handle)
66Added line. is
67Added line. Current : System.Address := Pool.Storage (Pool.Next)'Address;
68Added line. begin
69Added line. if Subpool /= Pool.Region'Unchecked_Access then
70Added line. raise Program_Error with "allocation used an unexpected subpool";
71Added line. end if;
72Added line.
73Added line. while Current mod Alignment /= 0 loop
74Added line. Pool.Next := Pool.Next + 1;
75Added line. Current := Pool.Storage (Pool.Next)'Address;
76Added line. end loop;
77Added line.
78Added line. Storage_Address := Current;
79Added line. Pool.Next := Pool.Next + Storage_Offset (Size_In_Storage_Elements);
80Added line. Pool.Allocations := Pool.Allocations + 1;
81Added line. end Allocate_From_Subpool;
82Added line.
83Added line. overriding
84Added line. procedure Deallocate_Subpool
85Added line. (Pool : in out Test_Pool; Subpool : in out Subpool_Handle)
86Added line. is
87Added line. begin
88Added line. Pool.Releases := Pool.Releases + 1;
89Added line. Subpool := null;
90Added line. end Deallocate_Subpool;
91Added line.
92Added line. overriding procedure Finalize (Object : in out Tracked) is
93Added line. begin
94Added line. if Object.Value /= 7 then
95Added line. raise Program_Error with "controlled object value was not preserved";
96Added line. end if;
97Added line. Finalizations := Finalizations + 1;
98Added line. end Finalize;
99Added line.
100Added line. Pool : Test_Pool;
101Added line. Region : Subpool_Handle := Create_Subpool (Pool);
102Added line. Selections : Natural := 0;
103Added line.
104Added line. function Select_Region return not null Subpool_Handle is
105Added line. begin
106Added line. Selections := Selections + 1;
107Added line. return Region;
108Added line. end Select_Region;
109Added line.
110Added line. type Plain_Access is access Integer with Storage_Pool => Pool;
111Added line. type Tracked_Access is access Tracked with Storage_Pool => Pool;
112Added line. type Interface_Access is access Test_Interface'Class
113Added line. with Storage_Pool => Pool;
114Added line.
115Added line. Plain_Object : Plain_Access := new (Region) Integer'(3);
116Added line. Tracked_Object : Tracked_Access :=
117Added line. new (Select_Region) Tracked'
118Added line. (Ada.Finalization.Limited_Controlled with Value => 7);
119Added line. Interface_Object : Interface_Access :=
120Added line. new (Select_Region) Tracked'
121Added line. (Ada.Finalization.Limited_Controlled with Value => 7);
122Added line.
123Added line. begin
124Added line. if Plain_Object.all /= 3 or else Tracked_Object.Value /= 7 then
125Added line. raise Program_Error with "allocated values were not preserved";
126Added line. end if;
127Added line. if Interface_Object = null or else Pool.Allocations /= 3 then
128Added line. raise Program_Error with "named subpool did not receive all allocations";
129Added line. end if;
130Added line. if Selections /= 2 then
131Added line. raise Program_Error with "subpool expressions were not evaluated once";
132Added line. end if;
133Added line.
134Added line. Ada.Unchecked_Deallocate_Subpool (Region);
135Added line. if Region /= null or else Pool.Releases /= 1 or else Finalizations /= 2 then
136Added line. raise Program_Error with "subpool release did not finalize both objects";
137Added line. end if;
138Added line.
139Added line. Plain_Object := null;
140Added line. Tracked_Object := null;
141Added line. Interface_Object := null;
142Added line. Put_Line ("PASS controlled named subpool allocator");
143Added line. end Controlled_Subpool_Allocator;
144

Tests.

controlled_subpool_allocator.adb Ada · 143 lines
--  { dg-do run }
--  { dg-options "-gnata" }

with Ada.Finalization;
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Unchecked_Deallocate_Subpool;
with System;
with System.Storage_Elements; use System.Storage_Elements;
with System.Storage_Pools.Subpools; use System.Storage_Pools.Subpools;

procedure Controlled_Subpool_Allocator is
   Finalizations : Natural := 0;

   type Test_Subpool is new Root_Subpool with null record;
   type Test_Interface is limited interface;

   type Test_Pool is new Root_Storage_Pool_With_Subpools with record
      Region      : aliased Test_Subpool;
      Storage     : aliased Storage_Array (1 .. 4_096);
      Next        : Storage_Offset := 1;
      Allocations : Natural := 0;
      Releases    : Natural := 0;
   end record;

   overriding
   function Create_Subpool
     (Pool : in out Test_Pool) return not null Subpool_Handle;

   overriding
   procedure Allocate_From_Subpool
     (Pool                     : in out Test_Pool;
      Storage_Address          : out System.Address;
      Size_In_Storage_Elements : Storage_Count;
      Alignment                : Storage_Count;
      Subpool                  : not null Subpool_Handle);

   overriding
   procedure Deallocate_Subpool
     (Pool : in out Test_Pool; Subpool : in out Subpool_Handle);

   type Tracked is new Ada.Finalization.Limited_Controlled
     and Test_Interface with record
      Value : Natural := 0;
   end record;

   overriding procedure Finalize (Object : in out Tracked);

   overriding
   function Create_Subpool
     (Pool : in out Test_Pool) return not null Subpool_Handle
   is
      Result : constant not null Subpool_Handle :=
        Pool.Region'Unchecked_Access;
   begin
      Set_Pool_Of_Subpool (Result, Pool);
      return Result;
   end Create_Subpool;

   overriding
   procedure Allocate_From_Subpool
     (Pool                     : in out Test_Pool;
      Storage_Address          : out System.Address;
      Size_In_Storage_Elements : Storage_Count;
      Alignment                : Storage_Count;
      Subpool                  : not null Subpool_Handle)
   is
      Current : System.Address := Pool.Storage (Pool.Next)'Address;
   begin
      if Subpool /= Pool.Region'Unchecked_Access then
         raise Program_Error with "allocation used an unexpected subpool";
      end if;

      while Current mod Alignment /= 0 loop
         Pool.Next := Pool.Next + 1;
         Current := Pool.Storage (Pool.Next)'Address;
      end loop;

      Storage_Address := Current;
      Pool.Next := Pool.Next + Storage_Offset (Size_In_Storage_Elements);
      Pool.Allocations := Pool.Allocations + 1;
   end Allocate_From_Subpool;

   overriding
   procedure Deallocate_Subpool
     (Pool : in out Test_Pool; Subpool : in out Subpool_Handle)
   is
   begin
      Pool.Releases := Pool.Releases + 1;
      Subpool := null;
   end Deallocate_Subpool;

   overriding procedure Finalize (Object : in out Tracked) is
   begin
      if Object.Value /= 7 then
         raise Program_Error with "controlled object value was not preserved";
      end if;
      Finalizations := Finalizations + 1;
   end Finalize;

   Pool       : Test_Pool;
   Region     : Subpool_Handle := Create_Subpool (Pool);
   Selections : Natural := 0;

   function Select_Region return not null Subpool_Handle is
   begin
      Selections := Selections + 1;
      return Region;
   end Select_Region;

   type Plain_Access is access Integer with Storage_Pool => Pool;
   type Tracked_Access is access Tracked with Storage_Pool => Pool;
   type Interface_Access is access Test_Interface'Class
     with Storage_Pool => Pool;

   Plain_Object     : Plain_Access := new (Region) Integer'(3);
   Tracked_Object   : Tracked_Access :=
     new (Select_Region) Tracked'
       (Ada.Finalization.Limited_Controlled with Value => 7);
   Interface_Object : Interface_Access :=
     new (Select_Region) Tracked'
       (Ada.Finalization.Limited_Controlled with Value => 7);

begin
   if Plain_Object.all /= 3 or else Tracked_Object.Value /= 7 then
      raise Program_Error with "allocated values were not preserved";
   end if;
   if Interface_Object = null or else Pool.Allocations /= 3 then
      raise Program_Error with "named subpool did not receive all allocations";
   end if;
   if Selections /= 2 then
      raise Program_Error with "subpool expressions were not evaluated once";
   end if;

   Ada.Unchecked_Deallocate_Subpool (Region);
   if Region /= null or else Pool.Releases /= 1 or else Finalizations /= 2 then
      raise Program_Error with "subpool release did not finalize both objects";
   end if;

   Plain_Object := null;
   Tracked_Object := null;
   Interface_Object := null;
   Put_Line ("PASS controlled named subpool allocator");
end Controlled_Subpool_Allocator;

Download · View in repository

run-test.sh shell · 66 lines
#!/usr/bin/env bash
set -euo pipefail

if [[ $# -ne 3 ]]; then
  echo "usage: $0 TOOLCHAIN_ROOT GCC_VERSION unpatched|patched" >&2
  exit 2
fi

root=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)
source "$root/scripts/regression-common.sh"
resolve_regression_toolchain "$1"
version=$2
state=$3
[[ "$state" == unpatched || "$state" == patched ]] || {
  echo "error: state must be unpatched or patched" >&2
  exit 2
}

case "$version" in
  13.2.0|14.2.0|15.1.0) affected=yes ;;
  15.3.0|16.1.0|16.2.0) affected=no ;;
  *) echo "error: unsupported GCC version: $version" >&2; exit 2 ;;
esac

fixture="$root/bundles/controlled-subpool-allocator/tests/controlled_subpool_allocator.adb"
work=$(mktemp -d "${TMPDIR:-/tmp}/gnat-controlled-subpool-test.XXXXXX")
trap 'rm -rf "$work"' EXIT

for optimization in 0 2; do
  case_dir="$work/O$optimization"
  mkdir -p "$case_dir"
  cp "$fixture" "$case_dir/controlled_subpool_allocator.adb"
  (
    cd "$case_dir"
    "${REGRESSION_ENV[@]}" "$REGRESSION_GNATMAKE" -q -f \
      "-O$optimization" -gnata controlled_subpool_allocator.adb
  )

  set +e
  "${REGRESSION_ENV[@]}" "$case_dir/controlled_subpool_allocator" \
    >"$case_dir/output.log" 2>&1
  run_status=$?
  set -e

  if [[ "$state" == unpatched && "$affected" == yes ]]; then
    [[ $run_status -ne 0 ]] || {
      echo "error: unpatched controlled-subpool regression unexpectedly passed at -O$optimization" >&2
      exit 1
    }
    grep -Eiq 'PROGRAM_ERROR.*Default_Subpool_For_Pool' "$case_dir/output.log" || {
      cat "$case_dir/output.log"
      exit 1
    }
    expected=expected-null-subpool-failure
  else
    [[ $run_status -eq 0 ]] || { cat "$case_dir/output.log"; exit 1; }
    grep -Fx "PASS controlled named subpool allocator" "$case_dir/output.log"
    if [[ "$affected" == no ]]; then
      expected=known-good-control
    else
      expected=patched
    fi
  fi

  echo "controlled-subpool-allocator -O$optimization: $expected (GCC $version)"
done

Download · View in repository

Commands.

Apply the patch
./scripts/apply-bundle.sh controlled-subpool-allocator GCC_VERSION SOURCE
Build the compiler
PATH=/path/to/bootstrap/bin:$PATH ./scripts/build-gnat.sh SOURCE BUILD INSTALL
Run the regression
./scripts/run-regressions.sh INSTALL 1.2.0 GCC_MAJOR patched

Metadata.