]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/ada/sem_spark.adb
[Ada] Minor reformattings
[thirdparty/gcc.git] / gcc / ada / sem_spark.adb
index a60a6cb4bacc51c5db3b48626fc531dc05cd52c2..f99dced0da39ec353fcc9b3fe2e3abf8480264ca 100644 (file)
@@ -637,6 +637,14 @@ package body Sem_SPARK is
 
    procedure Check_Declaration (Decl : Node_Id);
 
+   procedure Check_Declaration_Legality
+     (Decl  : Node_Id;
+      Force : Boolean;
+      Legal : in out Boolean);
+   --  Check the legality of declaration Decl regarding rules not related to
+   --  permissions. Update Legal to False if a rule is violated. Issue an
+   --  error message if Force is True and Emit_Messages returns True.
+
    procedure Check_Expression (Expr : Node_Id; Mode : Extended_Checking_Mode);
    pragma Precondition (Nkind_In (Expr, N_Index_Or_Discriminant_Constraint,
                                         N_Range_Constraint,
@@ -663,6 +671,9 @@ package body Sem_SPARK is
    procedure Check_Node (N : Node_Id);
    --  Main traversal procedure to check safe pointer usage
 
+   procedure Check_Old_Loop_Entry (N : Node_Id);
+   --  Check SPARK RM 3.10(14) regarding 'Old and 'Loop_Entry
+
    procedure Check_Package_Body (Pack : Node_Id);
 
    procedure Check_Package_Spec (Pack : Node_Id);
@@ -683,7 +694,10 @@ package body Sem_SPARK is
 
    procedure Check_Statement (Stmt : Node_Id);
 
-   procedure Check_Type (Typ : Entity_Id);
+   procedure Check_Type_Legality
+     (Typ   : Entity_Id;
+      Force : Boolean;
+      Legal : in out Boolean);
    --  Check that type Typ is either not deep, or that it is an observing or
    --  owning type according to SPARK RM 3.10
 
@@ -1135,11 +1149,12 @@ package body Sem_SPARK is
       Expr_Root   : Entity_Id;
       Perm        : Perm_Kind;
       Status      : Error_Status;
+      Dummy       : Boolean := True;
 
    --  Start of processing for Check_Assignment
 
    begin
-      Check_Type (Target_Typ);
+      Check_Type_Legality (Target_Typ, Force => True, Legal => Dummy);
 
       if Is_Anonymous_Access_Type (Target_Typ) then
          Check_Source_Of_Borrow_Or_Observe (Expr, Status);
@@ -1156,19 +1171,17 @@ package body Sem_SPARK is
 
          Expr_Root := Get_Root_Object (Expr);
 
-         --  SPARK RM 3.10(8): For an assignment statement where
-         --  the target is a stand-alone object of an anonymous
-         --  access-to-object type
+         --  SPARK RM 3.10(7): For an assignment statement where the target is
+         --  a stand-alone object of an anonymous access-to-object type.
 
          pragma Assert (Present (Target_Root));
 
-         --  If the type of the target is an anonymous
-         --  access-to-constant type (an observing access type), the
-         --  source shall be an owning access object denoted by a name
-         --  that is not in the Moved state, and whose root object
-         --  is not in the Moved state and is not declared at a
-         --  statically deeper accessibility level than that of
-         --  the target object.
+         --  If the type of the target is an anonymous access-to-constant type
+         --  (an observing access type), the source shall be an owning access
+         --  object denoted by a name that is not in the Moved state, and whose
+         --  root object is not in the Moved state and is not declared at a
+         --  statically deeper accessibility level than that of the target
+         --  object.
 
          if Is_Access_Constant (Target_Typ) then
             Perm := Get_Perm (Expr);
@@ -1191,11 +1204,10 @@ package body Sem_SPARK is
 
             --  ??? check accessibility level
 
-            --  If the type of the target is an anonymous
-            --  access-to-variable type (an owning access type), the
-            --  source shall be an owning access object denoted by a
-            --  name that is in the Unrestricted state, and whose root
-            --  object is the target object itself.
+            --  If the type of the target is an anonymous access-to-variable
+            --  type (an owning access type), the source shall be an owning
+            --  access object denoted by a name that is in the Unrestricted
+            --  state, and whose root object is the target object itself.
 
             Check_Expression (Expr, Observe);
             Handle_Observe (Target_Root, Expr, Is_Decl);
@@ -1407,11 +1419,18 @@ package body Sem_SPARK is
       Target     : constant Entity_Id := Defining_Identifier (Decl);
       Target_Typ : constant Node_Id := Etype (Target);
       Expr       : Node_Id;
+      Dummy      : Boolean := True;
 
    begin
+      --  Start with legality rules not related to permissions
+
+      Check_Declaration_Legality (Decl, Force => True, Legal => Dummy);
+
+      --  Now check permission-related legality rules
+
       case N_Declaration'(Nkind (Decl)) is
          when N_Full_Type_Declaration =>
-            Check_Type (Target);
+            null;
 
             --  ??? What about component declarations with defaults.
 
@@ -1421,7 +1440,105 @@ package body Sem_SPARK is
          when N_Object_Declaration =>
             Expr := Expression (Decl);
 
-            Check_Type (Target_Typ);
+            if Present (Expr) then
+               Check_Assignment (Target => Target,
+                                 Expr   => Expr);
+            end if;
+
+            if Is_Deep (Target_Typ) then
+               declare
+                  Tree : constant Perm_Tree_Access :=
+                    new Perm_Tree_Wrapper'
+                      (Tree =>
+                         (Kind                => Entire_Object,
+                          Is_Node_Deep        => True,
+                          Explanation         => Decl,
+                          Permission          => Read_Write,
+                          Children_Permission => Read_Write));
+               begin
+                  Set (Current_Perm_Env, Target, Tree);
+               end;
+            end if;
+
+         when N_Iterator_Specification =>
+            null;
+
+         when N_Loop_Parameter_Specification =>
+            null;
+
+         --  Checking should not be called directly on these nodes
+
+         when N_Function_Specification
+            | N_Entry_Declaration
+            | N_Procedure_Specification
+            | N_Component_Declaration
+         =>
+            raise Program_Error;
+
+         --  Ignored constructs for pointer checking
+
+         when N_Formal_Object_Declaration
+            | N_Formal_Type_Declaration
+            | N_Incomplete_Type_Declaration
+            | N_Private_Extension_Declaration
+            | N_Private_Type_Declaration
+            | N_Protected_Type_Declaration
+         =>
+            null;
+
+         --  The following nodes are rewritten by semantic analysis
+
+         when N_Expression_Function =>
+            raise Program_Error;
+      end case;
+   end Check_Declaration;
+
+   --------------------------------
+   -- Check_Declaration_Legality --
+   --------------------------------
+
+   procedure Check_Declaration_Legality
+     (Decl  : Node_Id;
+      Force : Boolean;
+      Legal : in out Boolean)
+   is
+      function Original_Emit_Messages return Boolean renames Emit_Messages;
+
+      function Emit_Messages return Boolean;
+      --  Local wrapper around generic formal parameter Emit_Messages, to
+      --  check the value of parameter Force before calling the original
+      --  Emit_Messages, and setting Legal to False.
+
+      -------------------
+      -- Emit_Messages --
+      -------------------
+
+      function Emit_Messages return Boolean is
+      begin
+         Legal := False;
+         return Force and then Original_Emit_Messages;
+      end Emit_Messages;
+
+      --  Local variables
+
+      Target     : constant Entity_Id := Defining_Identifier (Decl);
+      Target_Typ : constant Node_Id := Etype (Target);
+      Expr       : Node_Id;
+
+   --  Start of processing for Check_Declaration_Legality
+
+   begin
+      case N_Declaration'(Nkind (Decl)) is
+         when N_Full_Type_Declaration =>
+            Check_Type_Legality (Target, Force, Legal);
+
+         when N_Subtype_Declaration =>
+            null;
+
+         when N_Object_Declaration =>
+            Expr := Expression (Decl);
+
+            Check_Type_Legality (Target_Typ, Force, Legal);
 
             --  A declaration of a stand-alone object of an anonymous access
             --  type shall have an explicit initial value and shall occur
@@ -1450,26 +1567,6 @@ package body Sem_SPARK is
                end if;
             end if;
 
-            if Present (Expr) then
-               Check_Assignment (Target => Target,
-                                 Expr   => Expr);
-            end if;
-
-            if Is_Deep (Target_Typ) then
-               declare
-                  Tree : constant Perm_Tree_Access :=
-                    new Perm_Tree_Wrapper'
-                      (Tree =>
-                         (Kind                => Entire_Object,
-                          Is_Node_Deep        => True,
-                          Explanation         => Decl,
-                          Permission          => Read_Write,
-                          Children_Permission => Read_Write));
-               begin
-                  Set (Current_Perm_Env, Target, Tree);
-               end;
-            end if;
-
          when N_Iterator_Specification =>
             null;
 
@@ -1501,7 +1598,7 @@ package body Sem_SPARK is
          when N_Expression_Function =>
             raise Program_Error;
       end case;
-   end Check_Declaration;
+   end Check_Declaration_Legality;
 
    ----------------------
    -- Check_Expression --
@@ -2583,6 +2680,43 @@ package body Sem_SPARK is
    ----------------
 
    procedure Check_Node (N : Node_Id) is
+
+      procedure Check_Subprogram_Contract (N : Node_Id);
+      --  Check the postcondition-like contracts for use of 'Old
+
+      -------------------------------
+      -- Check_Subprogram_Contract --
+      -------------------------------
+
+      procedure Check_Subprogram_Contract (N : Node_Id) is
+      begin
+         if Nkind (N) = N_Subprogram_Declaration
+           or else Acts_As_Spec (N)
+         then
+            declare
+               E        : constant Entity_Id := Unique_Defining_Entity (N);
+               Post     : constant Node_Id :=
+                 Get_Pragma (E, Pragma_Postcondition);
+               Cases    : constant Node_Id :=
+                 Get_Pragma (E, Pragma_Contract_Cases);
+            begin
+               Check_Old_Loop_Entry (Post);
+               Check_Old_Loop_Entry (Cases);
+            end;
+
+         elsif Nkind (N) = N_Subprogram_Body then
+            declare
+               E        : constant Entity_Id := Defining_Entity (N);
+               Ref_Post : constant Node_Id :=
+                 Get_Pragma (E, Pragma_Refined_Post);
+            begin
+               Check_Old_Loop_Entry (Ref_Post);
+            end;
+         end if;
+      end Check_Subprogram_Contract;
+
+   --  Start of processing for Check_Node
+
    begin
       case Nkind (N) is
          when N_Declaration =>
@@ -2602,14 +2736,17 @@ package body Sem_SPARK is
                Check_Package_Body (N);
             end if;
 
-         when N_Subprogram_Body
-            | N_Entry_Body
-            | N_Task_Body
-         =>
+         when N_Subprogram_Body =>
             if not Is_Generic_Unit (Unique_Defining_Entity (N)) then
+               Check_Subprogram_Contract (N);
                Check_Callable_Body (N);
             end if;
 
+         when N_Entry_Body
+            | N_Task_Body
+         =>
+            Check_Callable_Body (N);
+
          when N_Protected_Body =>
             Check_List (Declarations (N));
 
@@ -2622,6 +2759,15 @@ package body Sem_SPARK is
          when N_Pragma =>
             Check_Pragma (N);
 
+         when N_Subprogram_Declaration =>
+            Check_Subprogram_Contract (N);
+
+         --  Attribute references in statement position are not supported in
+         --  SPARK and will be rejected by GNATprove.
+
+         when N_Attribute_Reference =>
+            null;
+
          --  Ignored constructs for pointer checking
 
          when N_Abstract_Subprogram_Declaration
@@ -2655,7 +2801,6 @@ package body Sem_SPARK is
             | N_Procedure_Instantiation
             | N_Raise_xxx_Error
             | N_Record_Representation_Clause
-            | N_Subprogram_Declaration
             | N_Subprogram_Renaming_Declaration
             | N_Task_Type_Declaration
             | N_Use_Package_Clause
@@ -2677,6 +2822,65 @@ package body Sem_SPARK is
       end case;
    end Check_Node;
 
+   --------------------------
+   -- Check_Old_Loop_Entry --
+   --------------------------
+
+   procedure Check_Old_Loop_Entry (N : Node_Id) is
+
+      function Check_Attribute (N : Node_Id) return Traverse_Result;
+
+      ---------------------
+      -- Check_Attribute --
+      ---------------------
+
+      function Check_Attribute (N : Node_Id) return Traverse_Result is
+         Attr_Id : Attribute_Id;
+         Aname   : Name_Id;
+         Pref    : Node_Id;
+
+      begin
+         if Nkind (N) = N_Attribute_Reference then
+            Attr_Id := Get_Attribute_Id (Attribute_Name (N));
+            Aname   := Attribute_Name (N);
+
+            if Attr_Id = Attribute_Old
+              or else Attr_Id = Attribute_Loop_Entry
+            then
+               Pref := Prefix (N);
+
+               if Is_Deep (Etype (Pref)) then
+                  if Nkind (Pref) /= N_Function_Call then
+                     if Emit_Messages then
+                        Error_Msg_Name_1 := Aname;
+                        Error_Msg_N
+                          ("prefix of % attribute must be a function call "
+                           & "(SPARK RM 3.10(14))", Pref);
+                     end if;
+
+                  elsif Is_Traversal_Function_Call (Pref) then
+                     if Emit_Messages then
+                        Error_Msg_Name_1 := Aname;
+                        Error_Msg_N
+                          ("prefix of % attribute should not call a traversal "
+                           & "function (SPARK RM 3.10(14))", Pref);
+                     end if;
+                  end if;
+               end if;
+            end if;
+         end if;
+
+         return OK;
+      end Check_Attribute;
+
+      procedure Check_All is new Traverse_Proc (Check_Attribute);
+
+   --  Start of processing for Check_Old_Loop_Entry
+
+   begin
+      Check_All (N);
+   end Check_Old_Loop_Entry;
+
    ------------------------
    -- Check_Package_Body --
    ------------------------
@@ -2869,8 +3073,18 @@ package body Sem_SPARK is
                Expr : constant Node_Id := Expression (Arg2);
             begin
                Check_Expression (Expr, Read);
+
+               --  Prefix of Loop_Entry should be checked inside any assertion
+               --  where it may appear.
+
+               Check_Old_Loop_Entry (Expr);
             end;
 
+         --  Prefix of Loop_Entry should be checked inside a Loop_Variant
+
+         when Pragma_Loop_Variant =>
+            Check_Old_Loop_Entry (Prag);
+
          --  There is no need to check contracts, as these can only access
          --  inputs and outputs of the subprogram. Inputs are checked
          --  independently for R permission. Outputs are checked
@@ -2963,6 +3177,7 @@ package body Sem_SPARK is
 
          when N_Package_Body
             | N_Package_Declaration
+            | N_Subprogram_Declaration
             | N_Subprogram_Body
          =>
             declare
@@ -3388,13 +3603,38 @@ package body Sem_SPARK is
       end case;
    end Check_Statement;
 
-   ----------------
-   -- Check_Type --
-   ----------------
+   -------------------------
+   -- Check_Type_Legality --
+   -------------------------
+
+   procedure Check_Type_Legality
+     (Typ   : Entity_Id;
+      Force : Boolean;
+      Legal : in out Boolean)
+   is
+      function Original_Emit_Messages return Boolean renames Emit_Messages;
+
+      function Emit_Messages return Boolean;
+      --  Local wrapper around generic formal parameter Emit_Messages, to
+      --  check the value of parameter Force before calling the original
+      --  Emit_Messages, and setting Legal to False.
+
+      -------------------
+      -- Emit_Messages --
+      -------------------
+
+      function Emit_Messages return Boolean is
+      begin
+         Legal := False;
+         return Force and then Original_Emit_Messages;
+      end Emit_Messages;
+
+      --  Local variables
 
-   procedure Check_Type (Typ : Entity_Id) is
       Check_Typ : constant Entity_Id := Retysp (Typ);
 
+   --  Start of processing for Check_Type_Legality
+
    begin
       case Type_Kind'(Ekind (Check_Typ)) is
          when Access_Kind =>
@@ -3404,7 +3644,7 @@ package body Sem_SPARK is
                =>
                   null;
                when E_Access_Subtype =>
-                  Check_Type (Base_Type (Check_Typ));
+                  Check_Type_Legality (Base_Type (Check_Typ), Force, Legal);
                when E_Access_Attribute_Type =>
                   if Emit_Messages then
                      Error_Msg_N ("access attribute not allowed in SPARK",
@@ -3431,7 +3671,7 @@ package body Sem_SPARK is
          when E_Array_Type
             | E_Array_Subtype
          =>
-            Check_Type (Component_Type (Check_Typ));
+            Check_Type_Legality (Component_Type (Check_Typ), Force, Legal);
 
          when Record_Kind =>
             if Is_Deep (Check_Typ)
@@ -3454,7 +3694,7 @@ package body Sem_SPARK is
                      --  Ignore components which are not visible in SPARK
 
                      if Component_Is_Visible_In_SPARK (Comp) then
-                        Check_Type (Etype (Comp));
+                        Check_Type_Legality (Etype (Comp), Force, Legal);
                      end if;
                      Next_Component_Or_Discriminant (Comp);
                   end loop;
@@ -3480,7 +3720,7 @@ package body Sem_SPARK is
          =>
             null;
       end case;
-   end Check_Type;
+   end Check_Type_Legality;
 
    --------------
    -- Get_Expl --
@@ -4026,6 +4266,24 @@ package body Sem_SPARK is
       end case;
    end Is_Deep;
 
+   --------------
+   -- Is_Legal --
+   --------------
+
+   function Is_Legal (N : Node_Id) return Boolean is
+      Legal : Boolean := True;
+
+   begin
+      case Nkind (N) is
+         when N_Declaration =>
+            Check_Declaration_Legality (N, Force => False, Legal => Legal);
+         when others =>
+            null;
+      end case;
+
+      return Legal;
+   end Is_Legal;
+
    ----------------------
    -- Is_Local_Context --
    ----------------------
@@ -4266,6 +4524,12 @@ package body Sem_SPARK is
    is
    begin
       return Is_Path_Expression (Expr, Is_Traversal)
+
+        or else (Nkind_In (Expr, N_Qualified_Expression,
+                                 N_Type_Conversion,
+                                 N_Unchecked_Type_Conversion)
+                  and then Is_Subpath_Expression (Expression (Expr)))
+
         or else (Nkind (Expr) = N_Attribute_Reference
                   and then
                     (Get_Attribute_Id (Attribute_Name (Expr)) =
@@ -4276,7 +4540,8 @@ package body Sem_SPARK is
                      or else
                      Get_Attribute_Id (Attribute_Name (Expr)) =
                        Attribute_Image))
-       or else Nkind (Expr) = N_Op_Concat;
+
+        or else Nkind (Expr) = N_Op_Concat;
    end Is_Subpath_Expression;
 
    ---------------------------
@@ -4740,13 +5005,49 @@ package body Sem_SPARK is
               Get_First_Key (Current_Borrowers);
             Var      : Entity_Id;
             Borrowed : Node_Id;
+            B_Pledge : Entity_Id := Empty;
 
          begin
+            --  Search for a call to a pledge function or a global pragma in
+            --  the parents of Expr.
+
+            declare
+               Call : Node_Id := Expr;
+            begin
+               while Present (Call)
+                 and then
+                   (Nkind (Call) /= N_Function_Call
+                     or else not Is_Pledge_Function (Get_Called_Entity (Call)))
+               loop
+                  --  Do not check for borrowed objects in global contracts
+                  --  ??? However we should keep these objects in the borrowed
+                  --  state when verifying the subprogram so that we can make
+                  --  sure that they are only read inside pledges.
+                  --  ??? There is probably a better way to disable checking of
+                  --  borrows inside global contracts.
+
+                  if Nkind (Call) = N_Pragma
+                    and then Get_Pragma_Id (Pragma_Name (Call)) = Pragma_Global
+                  then
+                     return;
+                  end if;
+
+                  Call := Parent (Call);
+               end loop;
+
+               if Present (Call)
+                 and then Nkind (First_Actual (Call)) in N_Has_Entity
+               then
+                  B_Pledge := Entity (First_Actual (Call));
+               end if;
+            end;
+
             while Key.Present loop
                Var := Key.K;
                Borrowed := Get (Current_Borrowers, Var);
 
                if Is_Prefix_Or_Almost (Pref => Borrowed, Expr => Expr)
+                 and then Var /= B_Pledge
                  and then Emit_Messages
                then
                   Error_Msg_Sloc := Sloc (Borrowed);