diff mbox series

[Ada] Use of renamings of function results in SPARK annotations

Message ID 20170908085939.GA68761@adacore.com
State New
Headers show
Series [Ada] Use of renamings of function results in SPARK annotations | expand

Commit Message

Arnaud Charlet Sept. 8, 2017, 8:59 a.m. UTC
This patch changes the expansion of object renamings in SPARK to reuse the
entity associated with the renaming when the name denotes a function call.
The patch also modifies a routine used to extract the entity of references
to abstract states and whole objects to handle renamings of function results.
Together, both these changes allow for function result renamings in SPARK
annotations.

------------
-- Source --
------------

--  pack.ads

package Pack with SPARK_Mode is
   type Lim_Rec is limited record
      Data : Integer;
   end record;

   function Get_Integer return Integer;
   function Get_Lim_Rec return Lim_Rec;
   function Get_String  return String;
end Pack;

--  pack.adb

package body Pack with SPARK_Mode is
   function Get_Integer return Integer is
   begin
      return 123;
   end Get_Integer;

   function Get_Lim_Rec return Lim_Rec is
   begin
      return Result : Lim_Rec;
   end Get_Lim_Rec;

   function Get_String return String is
   begin
      return "456";
   end Get_String;

   package Nested
     with Initializes => (Int_Ren, Lim_Ren, Str_Ren)
   is
      Int_Ren : Integer renames Get_Integer;
      Lim_Ren : Lim_Rec renames Get_Lim_Rec;
      Str_Ren : String  renames Get_String;

      procedure Proc
        with Global  => (Int_Ren, Lim_Ren, Str_Ren),
             Depends => (null => (Int_Ren, Lim_Ren, Str_Ren));

   end Nested;

   package body Nested is
      procedure Proc is begin null; end Proc;
   end Nested;
end Pack;

----------------------------
-- Compilation and output --
----------------------------

$ gcc -c pack.adb
$ gcc -c pack.adb -gnatd.F
pack.adb:9:14: warning: variable "Result" is read but never assigned
pack.adb:9:14: warning: variable "Result" is read but never assigned

Tested on x86_64-pc-linux-gnu, committed on trunk

2017-09-08  Hristian Kirtchev  <kirtchev@adacore.com>

	* exp_spark.adb (Expand_SPARK_N_Object_Renaming_Declaration):
	Reimplemented.
	(Expand_SPARK_Potential_Renaming): Code clean up.
	* sem_prag.adb (Analyze_Initialization_Item): Add a guard in case
	the item does not have a proper entity.
	(Analyze_Input_Item): Add a guard in case the item does not have a
	proper entity.
	(Collect_States_And_Objects): Include object renamings in the
	items being collected.
	(Resolve_State): Update the documentation of this routine.
	* sem_util.adb (Entity_Of): Add circuitry to handle
	renamings of function results.
	(Remove_Entity): New routine.
	(Remove_Overloaded_Entity): Take advantage of factorization.
	* sem_util.ads (Entity_Of): Update the documentation
	of this routine.
	(Remove_Entity): New routine.
	(Remove_Overloaded_Entity): Update the documentation of this
	routine.
diff mbox series

Patch

Index: exp_spark.adb
===================================================================
--- exp_spark.adb	(revision 251863)
+++ exp_spark.adb	(working copy)
@@ -292,10 +292,55 @@ 
    ------------------------------------------------
 
    procedure Expand_SPARK_N_Object_Renaming_Declaration (N : Node_Id) is
+      CFS    : constant Boolean    := Comes_From_Source (N);
+      Loc    : constant Source_Ptr := Sloc (N);
+      Obj_Id : constant Entity_Id  := Defining_Entity (N);
+      Nam    : constant Node_Id    := Name (N);
+      Typ    : constant Entity_Id  := Etype (Subtype_Mark (N));
+
    begin
-      --  Unconditionally remove all side effects from the name
+      --  Transform a renaming of the form
 
-      Evaluate_Name (Name (N));
+      --    Obj_Id : <subtype mark> renames <function call>;
+
+      --  into
+
+      --    Obj_Id : constant <subtype mark> := <function call>;
+
+      --  Invoking Evaluate_Name and ultimately Remove_Side_Effects introduces
+      --  a temporary to capture the function result. Once potential renamings
+      --  are rewritten for SPARK, the temporary may be leaked out into source
+      --  constructs and lead to confusing error diagnostics. Using an object
+      --  declaration prevents this unwanted side effect.
+
+      if Nkind (Nam) = N_Function_Call then
+         Rewrite (N,
+           Make_Object_Declaration (Loc,
+             Defining_Identifier => Obj_Id,
+             Constant_Present    => True,
+             Object_Definition   => New_Occurrence_Of (Typ, Loc),
+             Expression          => Nam));
+
+         --  Inherit the original Comes_From_Source status of the renaming
+
+         Set_Comes_From_Source (N, CFS);
+
+         --  Sever the link to the renamed function result because the entity
+         --  will no longer alias anything.
+
+         Set_Renamed_Object (Obj_Id, Empty);
+
+         --  Remove the entity of the renaming declaration from visibility as
+         --  the analysis of the object declaration will reintroduce it again.
+
+         Remove_Entity (Obj_Id);
+         Analyze (N);
+
+      --  Otherwise unconditionally remove all side effects from the name
+
+      else
+         Evaluate_Name (Nam);
+      end if;
    end Expand_SPARK_N_Object_Renaming_Declaration;
 
    ------------------------
@@ -324,29 +369,30 @@ 
 
    procedure Expand_SPARK_Potential_Renaming (N : Node_Id) is
       Loc    : constant Source_Ptr := Sloc (N);
-      Ren_Id : constant Entity_Id  := Entity (N);
+      Obj_Id : constant Entity_Id  := Entity (N);
       Typ    : constant Entity_Id  := Etype (N);
-      Obj_Id : Node_Id;
+      Ren    : Node_Id;
 
    begin
       --  Replace a reference to a renaming with the actual renamed object
 
-      if Ekind (Ren_Id) in Object_Kind then
-         Obj_Id := Renamed_Object (Ren_Id);
+      if Ekind (Obj_Id) in Object_Kind then
+         Ren := Renamed_Object (Obj_Id);
 
-         if Present (Obj_Id) then
+         if Present (Ren) then
 
-            --  The renamed object is an entity when instantiating generics
-            --  or inlining bodies. In this case the renaming is part of the
-            --  mapping "prologue" which links actuals to formals.
+            --  Instantiations and inlining of subprograms employ "prologues"
+            --  which map actual to formal parameters by means of renamings.
+            --  Replace a reference to a formal by the corresponding actual
+            --  parameter.
 
-            if Nkind (Obj_Id) in N_Entity then
-               Rewrite (N, New_Occurrence_Of (Obj_Id, Loc));
+            if Nkind (Ren) in N_Entity then
+               Rewrite (N, New_Occurrence_Of (Ren, Loc));
 
             --  Otherwise the renamed object denotes a name
 
             else
-               Rewrite (N, New_Copy_Tree (Obj_Id, New_Sloc => Loc));
+               Rewrite (N, New_Copy_Tree (Ren, New_Sloc => Loc));
                Reset_Analyzed_Flags (N);
             end if;
 
Index: sem_prag.adb
===================================================================
--- sem_prag.adb	(revision 251866)
+++ sem_prag.adb	(working copy)
@@ -283,9 +283,9 @@ 
    --  reference for future checks (see Analyze_Refined_State_In_Decls).
 
    procedure Resolve_State (N : Node_Id);
-   --  Handle the overloading of state names by parameterless functions. When N
-   --  denotes a function, this routine finds the corresponding state and sets
-   --  the entity of N to that of the state.
+   --  Handle the overloading of state names by functions. When N denotes a
+   --  function, this routine finds the corresponding state and sets the entity
+   --  of N to that of the state.
 
    procedure Rewrite_Assertion_Kind
      (N           : Node_Id;
@@ -2811,9 +2811,10 @@ 
             if Is_Entity_Name (Item) then
                Item_Id := Entity_Of (Item);
 
-               if Ekind_In (Item_Id, E_Abstract_State,
-                                     E_Constant,
-                                     E_Variable)
+               if Present (Item_Id)
+                 and then Ekind_In (Item_Id, E_Abstract_State,
+                                             E_Constant,
+                                             E_Variable)
                then
                   --  The state or variable must be declared in the visible
                   --  declarations of the package (SPARK RM 7.1.5(7)).
@@ -2918,14 +2919,15 @@ 
                if Is_Entity_Name (Input) then
                   Input_Id := Entity_Of (Input);
 
-                  if Ekind_In (Input_Id, E_Abstract_State,
-                                         E_Constant,
-                                         E_Generic_In_Out_Parameter,
-                                         E_Generic_In_Parameter,
-                                         E_In_Parameter,
-                                         E_In_Out_Parameter,
-                                         E_Out_Parameter,
-                                         E_Variable)
+                  if Present (Input_Id)
+                    and then Ekind_In (Input_Id, E_Abstract_State,
+                                                 E_Constant,
+                                                 E_Generic_In_Out_Parameter,
+                                                 E_Generic_In_Parameter,
+                                                 E_In_Parameter,
+                                                 E_In_Out_Parameter,
+                                                 E_Out_Parameter,
+                                                 E_Variable)
                   then
                      --  The input cannot denote states or objects declared
                      --  within the related package (SPARK RM 7.1.5(4)).
@@ -3073,7 +3075,8 @@ 
             Decl := First (Visible_Declarations (Pack_Spec));
             while Present (Decl) loop
                if Comes_From_Source (Decl)
-                 and then Nkind (Decl) = N_Object_Declaration
+                 and then Nkind_In (Decl, N_Object_Declaration,
+                                          N_Object_Renaming_Declaration)
                then
                   Append_New_Elmt (Defining_Entity (Decl), States_And_Objs);
 
Index: sem_util.adb
===================================================================
--- sem_util.adb	(revision 251863)
+++ sem_util.adb	(working copy)
@@ -7117,23 +7117,46 @@ 
    ---------------
 
    function Entity_Of (N : Node_Id) return Entity_Id is
-      Id : Entity_Id;
+      Id  : Entity_Id;
+      Ren : Node_Id;
 
    begin
+      --  Assume that the arbitrary node does not have an entity
+
       Id := Empty;
 
       if Is_Entity_Name (N) then
          Id := Entity (N);
 
-         --  Follow a possible chain of renamings to reach the root renamed
-         --  object.
+         --  Follow a possible chain of renamings to reach the earliest renamed
+         --  source object.
 
          while Present (Id)
            and then Is_Object (Id)
            and then Present (Renamed_Object (Id))
          loop
-            if Is_Entity_Name (Renamed_Object (Id)) then
-               Id := Entity (Renamed_Object (Id));
+            Ren := Renamed_Object (Id);
+
+            --  The reference renames an abstract state or a whole object
+
+            --    Obj : ...;
+            --    Ren : ... renames Obj;
+
+            if Is_Entity_Name (Ren) then
+               Id := Entity (Ren);
+
+            --  The reference renames a function result. Check the original
+            --  node in case expansion relocates the function call.
+
+            --    Ren : ... renames Func_Call;
+
+            elsif Nkind (Original_Node (Ren)) = N_Function_Call then
+               exit;
+
+            --  Otherwise the reference renames something which does not yield
+            --  an abstract state or a whole object. Treat the reference as not
+            --  having a proper entity for SPARK legality purposes.
+
             else
                Id := Empty;
                exit;
@@ -20369,6 +20392,61 @@ 
       end if;
    end References_Generic_Formal_Type;
 
+   -------------------
+   -- Remove_Entity --
+   -------------------
+
+   procedure Remove_Entity (Id : Entity_Id) is
+      Scop    : constant Entity_Id := Scope (Id);
+      Prev_Id : Entity_Id;
+
+   begin
+      --  Remove the entity from the homonym chain. When the entity is the
+      --  head of the chain, associate the entry in the name table with its
+      --  homonym effectively making it the new head of the chain.
+
+      if Current_Entity (Id) = Id then
+         Set_Name_Entity_Id (Chars (Id), Homonym (Id));
+
+      --  Otherwise link the previous and next homonyms
+
+      else
+         Prev_Id := Current_Entity (Id);
+         while Present (Prev_Id) and then Homonym (Prev_Id) /= Id loop
+            Prev_Id := Homonym (Prev_Id);
+         end loop;
+
+         Set_Homonym (Prev_Id, Homonym (Id));
+      end if;
+
+      --  Remove the entity from the scope entity chain. When the entity is
+      --  the head of the chain, set the next entity as the new head of the
+      --  chain.
+
+      if First_Entity (Scop) = Id then
+         Prev_Id := Empty;
+         Set_First_Entity (Scop, Next_Entity (Id));
+
+      --  Otherwise the entity is either in the middle of the chain or it acts
+      --  as its tail. Traverse and link the previous and next entities.
+
+      else
+         Prev_Id := First_Entity (Scop);
+         while Present (Prev_Id) and then Next_Entity (Prev_Id) /= Id loop
+            Next_Entity (Prev_Id);
+         end loop;
+
+         Set_Next_Entity (Prev_Id, Next_Entity (Id));
+      end if;
+
+      --  Handle the case where the entity acts as the tail of the scope entity
+      --  chain.
+
+      if Last_Entity (Scop) = Id then
+         Set_Last_Entity (Scop, Prev_Id);
+      end if;
+   end Remove_Entity;
+
    --------------------
    -- Remove_Homonym --
    --------------------
@@ -20428,58 +20506,15 @@ 
 
       --  Local variables
 
-      Scop    : constant Entity_Id := Scope (Id);
-      Formal  : Entity_Id;
-      Prev_Id : Entity_Id;
+      Formal : Entity_Id;
 
    --  Start of processing for Remove_Overloaded_Entity
 
    begin
-      --  Remove the entity from the homonym chain. When the entity is the
-      --  head of the chain, associate the entry in the name table with its
-      --  homonym effectively making it the new head of the chain.
+      --  Remove the entity from both the homonym and scope chains
 
-      if Current_Entity (Id) = Id then
-         Set_Name_Entity_Id (Chars (Id), Homonym (Id));
+      Remove_Entity (Id);
 
-      --  Otherwise link the previous and next homonyms
-
-      else
-         Prev_Id := Current_Entity (Id);
-         while Present (Prev_Id) and then Homonym (Prev_Id) /= Id loop
-            Prev_Id := Homonym (Prev_Id);
-         end loop;
-
-         Set_Homonym (Prev_Id, Homonym (Id));
-      end if;
-
-      --  Remove the entity from the scope entity chain. When the entity is
-      --  the head of the chain, set the next entity as the new head of the
-      --  chain.
-
-      if First_Entity (Scop) = Id then
-         Prev_Id := Empty;
-         Set_First_Entity (Scop, Next_Entity (Id));
-
-      --  Otherwise the entity is either in the middle of the chain or it acts
-      --  as its tail. Traverse and link the previous and next entities.
-
-      else
-         Prev_Id := First_Entity (Scop);
-         while Present (Prev_Id) and then Next_Entity (Prev_Id) /= Id loop
-            Next_Entity (Prev_Id);
-         end loop;
-
-         Set_Next_Entity (Prev_Id, Next_Entity (Id));
-      end if;
-
-      --  Handle the case where the entity acts as the tail of the scope entity
-      --  chain.
-
-      if Last_Entity (Scop) = Id then
-         Set_Last_Entity (Scop, Prev_Id);
-      end if;
-
       --  The entity denotes a primitive subprogram. Remove it from the list of
       --  primitives of the associated controlling type.
 
Index: sem_util.ads
===================================================================
--- sem_util.ads	(revision 251863)
+++ sem_util.ads	(working copy)
@@ -689,8 +689,9 @@ 
    --  are entered using Sem_Ch6.Enter_Overloadable_Entity.
 
    function Entity_Of (N : Node_Id) return Entity_Id;
-   --  Return the entity of N or Empty. If N is a renaming, return the entity
-   --  of the root renamed object.
+   --  Obtain the entity of arbitrary node N. If N is a renaming, return the
+   --  entity of the earliest renamed source abstract state or whole object.
+   --  If no suitable entity is available, return Empty.
 
    procedure Explain_Limited_Type (T : Entity_Id; N : Node_Id);
    --  This procedure is called after issuing a message complaining about an
@@ -2265,14 +2266,20 @@ 
    --  Returns True if the expression Expr contains any references to a generic
    --  type. This can only happen within a generic template.
 
+   procedure Remove_Entity (Id : Entity_Id);
+   --  Remove arbitrary entity Id from both the homonym and scope chains. Use
+   --  Remove_Overloaded_Entity for overloadable entities. Note: the removal
+   --  performed by this routine does not affect the visibility of existing
+   --  homonyms.
+
    procedure Remove_Homonym (E : Entity_Id);
    --  Removes E from the homonym chain
 
    procedure Remove_Overloaded_Entity (Id : Entity_Id);
    --  Remove arbitrary entity Id from the homonym chain, the scope chain and
-   --  the primitive operations list of the associated controlling type. NOTE:
-   --  the removal performed by this routine does not affect the visibility of
-   --  existing homonyms.
+   --  the primitive operations list of the associated controlling type. Use
+   --  Remove_Entity for non-overloadable entities. Note: the removal performed
+   --  by this routine does not affect the visibility of existing homonyms.
 
    function Remove_Suffix (E : Entity_Id; Suffix : Character) return Name_Id;
    --  Returns the name of E without Suffix