I shortened this down to a small useless example and tested against 12.2 on godbolt. Unless I am missing something I think GNAT has a bug and wanted to check and make sure I wasn’t missing something obvious. There’s not any unchecked operations that I can tell. The example is obtuse, but I was whittling it down from someone’s code that I was reviewing and had to do a lot of reorg/renaming to avoid showing anything proprietary. At the end, I think the person ends up with a pointer to a temporary object that no longer exists.
EDIT: the potential bug is that it assigns an access to an out of scope object:
p : Instance_Access := Bug.Make.Bad;
Full code:
function Square(num : Integer) return Integer is
package Bug is
type Instance is tagged limited private;
function Make return Instance;
function Bad(Self : aliased Instance) return not null access constant Instance;
private
type Inner(Source : not null access Instance) is limited null record;
type Instance is tagged limited record
Core : Inner(Instance'Access);
end record;
end Bug;
package body Bug is
function Make return Instance is
begin
return Result : Instance;
end Make;
function Bad(Self : aliased Instance) return not null access constant Instance is
begin
return Self.Core.Source;
end Bad;
end Bug;
type Instance_Access is access constant Bug.Instance;
p : Instance_Access := Bug.Make.Bad;
begin
return 0;
end Square;
There’s certainly a bug, but I think it’s in this code, not the compiler? unless it’s illegal to create a limited object in the scope of an expression. Could just be erroneous.
Bug.Make returns an anonymous object, .Bad returns a pointer to that object, and after the assignment the anonymous object has been finalized but the pointer to it remains.
Extended demo:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Finalization;
procedure Jere is
function Square (num : Integer) return Integer is
package Bug is
type Instance is new Ada.Finalization.Limited_Controlled with private;
function Make return Instance;
function Bad
(Self : aliased Instance) return not null access constant Instance;
function Instance_Number (Self : Instance) return Natural;
private
type Inner (Source : not null access Instance) is limited null record;
type Instance is new Ada.Finalization.Limited_Controlled with record
Instance_Number : Natural := 0;
Finalized : Boolean := False;
Core : Inner (Instance'Access);
end record;
overriding procedure Initialize (Obj : in out Instance);
overriding procedure Finalize (Obj : in out Instance);
end Bug;
package body Bug is
function Make return Instance is
begin
return Result : Instance;
end Make;
function Bad
(Self : aliased Instance) return not null access constant Instance
is
begin
Put_Line ("in Bad");
return Self.Core.Source;
end Bad;
function Instance_Number (Self : Instance) return Natural
is (Self.Instance_Number);
Count : Natural := 0;
procedure Initialize (Obj : in out Instance) is
begin
Count := Count + 1;
Obj.Instance_Number := Count;
Put_Line ("initializing object" & Obj.Instance_Number'Image);
end Initialize;
procedure Finalize (Obj : in out Instance) is
begin
if Obj.Finalized then
Put ("re-");
end if;
Obj.Finalized := True;
Put_Line ("finalizing object" & Obj.Instance_Number'Image);
Obj.Instance_Number := Natural'Last;
end Finalize;
end Bug;
type Instance_Access is access constant Bug.Instance;
p : Instance_Access := Bug.Make.Bad;
begin
Put_Line ("in Square; instance number" & P.Instance_Number'Image);
return 0;
end Square;
begin
Put_Line ("about to call Square ...");
declare
Dummy : Integer;
begin
Dummy := Square (42);
end;
Put_Line ("... done.");
end Jere;
I thought that the compiler should prevent (either compile time or runtime) the assignment of that access object, since it points to temporary memory that is no longer in scope. I don’t think I am doing anything unchecked in that code?
If I am doing something unchecked,then I agree it’s a bug in the code. But if I am not, then I think the compiler is supposed to catch it and it would be a compiler bug? To be fair, I don’t know the heart of darkness section very well, so it could be a language bug, but that’s less likely than the first two scenarios.
Simon hit up on it, but I think the compiler should prevent that access object assignment, either at runtime or compile time (not sure which). I was just making sure the code I presented didn’t have some sort of unchecked programming that I am missing.
I’m reviewing code similar to this, so I wanted to make sure I address it in the review correctly.