3

I have the following packages:

-------------------
-- File: father.ads
-------------------

package Father with SPARK_Mode => On is

   pragma Elaborate_Body;
   
   type Father_T is abstract tagged private;
   
   function Get_Field_1 (Self : Father_T) return Positive;
   
   procedure Proc_1 (Self : in out Father_T; Another : Father_T) is abstract;
   
   function Func_1 (Self : Father_T) return Boolean is abstract;
   
private

   pragma SPARK_Mode (Off);
   
   type Father_T is abstract tagged record
      
      Field_1 : Positive := 1;
      
   end record;
   
end Father;

--------------------------
-- File: father-middle.ads
--------------------------

package Father.Middle with SPARK_Mode => On is

   pragma Elaborate_Body;
   
   type Middle_T is abstract new Father_T with null record; -- [L1]

   overriding procedure Proc_1 (Self : in out Middle_T; Another : Middle_T);
   
private
   
   pragma SPARK_Mode (Off);
   
   function Func_2 (Self : Middle_T) return Boolean;
   
end Father.Middle; -- [L3]

--------------------------
-- File: father-middle.adb
--------------------------

package body Father.Middle is

   ------------
   -- Proc_1 --
   ------------

   overriding procedure Proc_1 (Self : in out Middle_T; Another : Middle_T) is -- [L2]
   begin
      Self.Field_1 := Self.Field_1 + Another.Field_1;
   end Proc_1;

   ------------
   -- Func_2 --
   ------------

   function Func_2 (Self : Middle_T) return Boolean is
   begin
      return False;
   end Func_2;

end Father.Middle;

-------------------------------
-- File: father-middle-lead.ads
-------------------------------

package Father.Middle.Leaf with SPARK_Mode => On is

   pragma Elaborate_Body;
   
   type Leaf_T is new Middle_T with null record;
   
   function Create return Leaf_T;
   
   overriding function Func_1 (Self : Leaf_T) return Boolean;

end Father.Middle.Leaf;

The compiler gives the following error:

father-middle.ads:7:04: first freezing point of type "Middle_T" must appear within early call region of primitive body "Proc_1" (SPARK RM 7.7(8)) [L1]

father-middle.ads:7:04: region starts at father-middle.adb:7 [L2]

father-middle.ads:7:04: region ends at father-middle.adb:7 [L2]

father-middle.ads:7:04: first freezing point at line 17 [L3]

If I override Proc_1 in Father.Middle.Leaf instead of Father.Middle the error is gone.

I have read https://docs.adacore.com/spark2014-docs/html/lrm/packages.html#elaboration-issues but I am new to SPARK.

Is there a way to override Proc_1 in Father.Middle?

I am using GNAT Studio Community 2020 in Windows 10.

F. Edwards
  • 31
  • 3

2 Answers2

2

Unfortunately, I do not have a conclusive answer, but I do have some doubt whether the early region analysis is actually correct here.

The reason for my doubt is that if you add a new concrete type Dummy_T to Father.Middle that inherits from Middle_T then suddenly all seems fine (as shown below). In my view (but I can be wrong here) the addition of such a type should not change the outcome of the early region analysis for Proc_1 of Middle_T; the only difference is that the freezing point of Middle_T is moved from the end of the package spec in the original code to just before the type definition of Dummy_T in the code below.

Furthermore, it's also unclear why the early region of Proc_1 in the body of Father.Middle does not extend into the package spec given that the pragma Elaborate_Body has been stated in Father.Middle. It would appear (if I understand SPARK RM 7.7(4) and 7.7(9) correctly) that the early region analyzer detects a non-preelaborable construct somewhere at the beginning of the body or end of the spec of Father.Middle. However, nothing of such seems to be there. So maybe something goes wrong in the analysis of the expanded code, but this would be pure speculation as the early region analyzer does not provide any debugging facilities to trace its behavior. Hence, it's quite hard to check the analyzer's reasoning from an end-user perspective.

Please note again that my analysis as a fellow end-user is hypothetical. The error might still be correct and I may be wrong. I just think that my findings were interesting enough to share.

(GNAT references: error, analyzer spec and main routine)

father-middle.ads

package Father.Middle with SPARK_Mode is
   pragma Elaborate_Body;
   
   type Middle_T is abstract new Father_T with null record;

   overriding procedure Proc_1 (Self : in out Middle_T; Another : Middle_T);
   
private
   pragma SPARK_Mode (Off);
   
   function Func_2 (Self : Middle_T) return Boolean;
   
   --
   --  ??? Just add a new concrete type and all is fine now?
   --
   
   type Dummy_T is new Middle_T with null record;
   
   overriding function Func_1 (Self : Dummy_T) return Boolean is (False);
   
end Father.Middle;
DeeDee
  • 5,654
  • 7
  • 14
  • Thank you for replying! You are right. The error is gone now. I do not have any clue on what is happening TBH. I will wait a few days before accepting this solution to see if a new solution is posted. Thanks again. – F. Edwards Jan 14 '21 at 06:41
0

Have you tried the following?

type Middle_T is tagged;
overriding procedure Proc_1 (Self : in out Middle_T; Another : Middle_T);
type Middle_T is abstract new Father_T with null record;

or

type Middle_T;
procedure Proc_1 (Self : in out Middle_T; Another : Middle_T);
type Middle_T is abstract new Father_T with null record;

Sometimes forward-declaring the type for the usage of being able to declare subprograms helps.

Shark8
  • 4,095
  • 1
  • 17
  • 31