pragma Extensions_Allowed (On);
procedure Test is
type Test_T is
abstract tagged null record
with Finalizable =>
(Finalize => Finalize,
Relaxed_Finalization => True);
procedure Finalize (This : in out Test_T) is abstract;
begin
null;
end Test;
> gnatmake test.adb
gcc -c test.adb
+===========================GNAT BUG DETECTED==============================+
| 15.0.1 20250329 (experimental) (aarch64-apple-darwin24.3.0) |
| Assert_Failure failed precondition from einfo-entities.ads:3296 |
| Error detected at test.adb:11:49 |
| Compiling test.adb |
| Please submit a bug report; see https://gcc.gnu.org/bugs/ . |
| Use a subject line meaningful to you and us to track the bug. |
| Include the entire contents of this bug box in the report. |
| Include the exact command that you entered. |
| Also include sources listed below. |
+==========================================================================+
Please include these source files with error report
Note that list may not be accurate in some cases,
so please double check that the problem can still
be reproduced with the set of files listed.
Consider also -gnatd.n switch (see debug.adb).
test.adb
test.adb:11:14: warning: not dispatching (must be defined in a package spec) [enabled by default]
compilation abandoned
gnatmake: "test.adb" compilation error
Finalize itself cannot be abstract (though your record type can be abstract)
Finalize needs the aspect No_Raise
As far as the bug, I would report it. The feature is new so they probably only tested their test cases. What I got to work on Godbolt was:
package Test is
type Test_T is
abstract tagged null record
with Finalizable =>
(Initialize => Initialize,
Adjust => Adjust,
Finalize => Finalize,
Relaxed_Finalization => True);
procedure Adjust (This : in out Test_T) is null;
procedure Initialize (This : in out Test_T) is null;
procedure Finalize (This : in out Test_T) is null
with No_Raise;
end Test;
I needed to specify all the procedures or the compiler bug occurred. Their test case probably used all 3. I don’t know if all 3 are required or not (didn’t dig deep into the docs to see).
Note that the procedures don’t have to be null, you can give them actual implementations if you wish, they just cannot be abstract.
I did have to nest them in a package to avoid the bug as well, so you’ll want them to be in a package (not sure if a requirement or just part of the bug). But to properly establish primitive operations for a record type you want them in a package anyways.
Interesting, so the subprograms implementing the aspect for a tagged type would be primitive, overridable? What happens with controlled types? Would Finalize be called twice and once if not on the heap?
It looks like they can’t be derived off of controlled types as they would not longer be root types.
This feature also doesn’t handle finalization from heap allocated objects. It’s purely for simple cases. I assume since this is a really new feature, they wanted to start with a restricted rule set.
So, this is another point where my proposal (essentially abstractions for type-structures and type-usages) would help: if the conceptual type-hierarchy were reified with the “abstract type” and “abstract interface”, finalization would be easily addable at the base of the hierarchy.
This should be a non-issue TBH.
ALL types have finalization; true, this might be a null-procedure, and clean-up is popping the item from the stack (encapsulated with all other local entities of the frame). And some types might be a null-procedure, allocated into a subpool that is discarded altogether, which is exactly how you can view the frame. The co-derivation non-cleanup issue in GNAT exists because they failed to do such a thing.
Not an issue per se, but something to keep in mind otherwise, someone unsuspecting might find themselves with a memory leak or finalization operations that don’t run as expected (if they are dynamically allocating tagged objects using this feature and are used to how Controlled objects work).