The rules for pragma SPARK_Mode have changed. In particular, the mode
is not inherited anymore from a the spec to the body, and the body
should not have a mode if the spec did not have one. These new rules
are checked now. A minor change is that SPARK_Mode is set even on
subprograms for which Comes_From_Source returns False (including
internal subprograms), as we need this information on subprograms
from instances of generics.

The following test now generates an error:

     1. package SPARKcode is
     2.    pragma SPARK_Mode (On);
     3.    procedure P1;
     4.    procedure P2 with SPARK_Mode => On;
     5.    procedure P3 with SPARK_Mode => Off;
     6. end SPARKcode;

     1. package body SPARKcode is
     2.    pragma SPARK_Mode (On);
     3.    procedure P1 is begin null; end;
     4.    procedure P2 is begin null; end;
     5.    procedure P3 is begin null; end;
           |
        >>> incorrect application of SPARK_Mode at line 2
        >>> value Off was set for SPARK_Mode on "P3" at sparkcode.ads:5

     6. end SPARKcode;

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

2014-01-29  Yannick Moy  <m...@adacore.com>

        * sem_ch6.adb (Analyze_Subprogram_Body_Helper): SPARK_Mode
        not inherited from spec anymore. Check consistency
        rules after processing of declarations.
        * sem_ch7.adb (Analyze_Package_Body_Helper): SPARK_Mode not inherited
        from spec anymore. Check consistency rules after processing of
        declarations.
        (Analyze_Package_Declaration): Set SPARK_Mode only for non-generic
        packages.
        * sem_prag.adb (Analyze_Pragma/Pragma_SPARK_Mode): Implement new
        consistency rules.

Index: sem_ch7.adb
===================================================================
--- sem_ch7.adb (revision 207241)
+++ sem_ch7.adb (working copy)
@@ -346,29 +346,20 @@
 
       Push_Scope (Spec_Id);
 
-      --  Set SPARK_Mode from private part of spec if it has a SPARK pragma.
-      --  Note that in the default case, SPARK_Aux_Pragma will be a copy of
-      --  SPARK_Pragma in the spec, so this properly handles the case where
-      --  there is no explicit SPARK_Pragma mode in the private part.
+      --  Set SPARK_Mode only for non-generic package
 
-      if Present (SPARK_Pragma (Spec_Id)) then
-         SPARK_Mode_Pragma := SPARK_Aux_Pragma (Spec_Id);
-         SPARK_Mode := Get_SPARK_Mode_From_Pragma (SPARK_Mode_Pragma);
+      if Ekind (Spec_Id) = E_Package then
+         --  Set SPARK_Mode from context
+
          Set_SPARK_Pragma (Body_Id, SPARK_Mode_Pragma);
          Set_SPARK_Pragma_Inherited (Body_Id, True);
 
-      --  Otherwise set from context
+         --  Set elaboration code SPARK mode the same for now
 
-      else
-         Set_SPARK_Pragma (Body_Id, SPARK_Mode_Pragma);
-         Set_SPARK_Pragma_Inherited (Body_Id, True);
+         Set_SPARK_Aux_Pragma (Body_Id, SPARK_Pragma (Body_Id));
+         Set_SPARK_Aux_Pragma_Inherited (Body_Id, True);
       end if;
 
-      --  Set elaboration code SPARK mode the same for now
-
-      Set_SPARK_Aux_Pragma (Body_Id, SPARK_Pragma (Body_Id));
-      Set_SPARK_Aux_Pragma_Inherited (Body_Id, True);
-
       Set_Categorization_From_Pragmas (N);
 
       Install_Visible_Declarations (Spec_Id);
@@ -400,6 +391,32 @@
          Inspect_Deferred_Constant_Completion (Declarations (N));
       end if;
 
+      --  After declarations have been analyzed, the body has been set
+      --  its final value of SPARK_Mode. Check that SPARK_Mode for body
+      --  is consistent with SPARK_Mode for spec.
+
+      if Present (SPARK_Pragma (Body_Id)) then
+         if Present (SPARK_Aux_Pragma (Spec_Id)) then
+            if Get_SPARK_Mode_From_Pragma (SPARK_Aux_Pragma (Spec_Id)) = Off
+                 and then
+               Get_SPARK_Mode_From_Pragma (SPARK_Pragma (Body_Id)) = On
+            then
+               Error_Msg_Sloc := Sloc (SPARK_Pragma (Body_Id));
+               Error_Msg_N ("incorrect application of SPARK_Mode#", N);
+               Error_Msg_Sloc := Sloc (SPARK_Aux_Pragma (Spec_Id));
+               Error_Msg_NE ("\value Off was set for SPARK_Mode on & #",
+                             N, Spec_Id);
+            end if;
+
+         else
+            Error_Msg_Sloc := Sloc (SPARK_Pragma (Body_Id));
+            Error_Msg_N ("incorrect application of SPARK_Mode#", N);
+            Error_Msg_Sloc := Sloc (Spec_Id);
+            Error_Msg_NE ("\no value was set for SPARK_Mode on & #",
+                          N, Spec_Id);
+         end if;
+      end if;
+
       --  Analyze_Declarations has caused freezing of all types. Now generate
       --  bodies for RACW primitives and stream attributes, if any.
 
@@ -814,12 +831,14 @@
       Set_Etype    (Id, Standard_Void_Type);
       Set_Contract (Id, Make_Contract (Sloc (Id)));
 
-      --  Inherit spark mode from context for now
+      --  Set SPARK_Mode from context only for non-generic package
 
-      Set_SPARK_Pragma               (Id, SPARK_Mode_Pragma);
-      Set_SPARK_Aux_Pragma           (Id, SPARK_Mode_Pragma);
-      Set_SPARK_Pragma_Inherited     (Id, True);
-      Set_SPARK_Aux_Pragma_Inherited (Id, True);
+      if Ekind (Id) = E_Package then
+         Set_SPARK_Pragma               (Id, SPARK_Mode_Pragma);
+         Set_SPARK_Aux_Pragma           (Id, SPARK_Mode_Pragma);
+         Set_SPARK_Pragma_Inherited     (Id, True);
+         Set_SPARK_Aux_Pragma_Inherited (Id, True);
+      end if;
 
       --  Analyze aspect specifications immediately, since we need to recognize
       --  things like Pure early enough to diagnose violations during analysis.
Index: sem_prag.adb
===================================================================
--- sem_prag.adb        (revision 207241)
+++ sem_prag.adb        (working copy)
@@ -18597,14 +18597,26 @@
             Spec_Id : Entity_Id;
             Stmt    : Node_Id;
 
-            procedure Check_Pragma_Conformance (Old_Pragma : Node_Id);
-            --  Verify the monotonicity of SPARK modes between the new pragma
-            --  N, and the old pragma, Old_Pragma, that was inherited. If
-            --  Old_Pragma is Empty, the call has no effect, otherwise we
-            --  verify that the new mode is less restrictive than the old mode.
-            --  For example, if the old mode is ON, then the new mode can be
-            --  anything. But if the old mode is OFF, then the only allowed
-            --  new mode is also OFF.
+            procedure Check_Pragma_Conformance
+              (Context_Pragma : Node_Id;
+               Entity_Pragma  : Node_Id;
+               Entity         : Entity_Id);
+            --  If Context_Pragma is not Empty, verify that the new pragma N
+            --  is compatible with the pragma Context_Pragma that was inherited
+            --  from the context:
+            --  . if Context_Pragma is ON, then the new mode can be anything
+            --  . if Context_Pragma is OFF, then the only allowed new mode is
+            --    also OFF.
+            --
+            --  If Entity is not Empty, verify that the new pragma N is
+            --  compatible with Entity_Pragma, the SPARK_Mode previously set
+            --  for Entity (which may be Empty):
+            --  . if Entity_Pragma is ON, then the new mode can be anything
+            --  . if Entity_Pragma is OFF, then the only allowed new mode is
+            --    also OFF.
+            --  . if Entity_Pragma is Empty, we always issue an error, as this
+            --    corresponds to a case where a previous section of Entity
+            --    had no SPARK_Mode set.
 
             procedure Check_Library_Level_Entity (E : Entity_Id);
             --  Verify that pragma is applied to library-level entity E
@@ -18613,23 +18625,47 @@
             -- Check_Pragma_Conformance --
             ------------------------------
 
-            procedure Check_Pragma_Conformance (Old_Pragma : Node_Id) is
+            procedure Check_Pragma_Conformance
+              (Context_Pragma : Node_Id;
+               Entity_Pragma  : Node_Id;
+               Entity         : Entity_Id) is
             begin
-               if Present (Old_Pragma) then
-                  pragma Assert (Nkind (Old_Pragma) = N_Pragma);
+               if Present (Context_Pragma) then
+                  pragma Assert (Nkind (Context_Pragma) = N_Pragma);
 
                   --  New mode less restrictive than the established mode
 
-                  if Get_SPARK_Mode_From_Pragma (Old_Pragma) = Off
+                  if Get_SPARK_Mode_From_Pragma (Context_Pragma) = Off
                     and then Mode_Id = On
                   then
                      Error_Msg_N
-                       ("cannot change 'S'P'A'R'K_Mode from Off to On", Arg1);
+                       ("cannot change SPARK_Mode from Off to On", Arg1);
                      Error_Msg_Sloc := Sloc (SPARK_Mode_Pragma);
-                     Error_Msg_N ("\'S'P'A'R'K_Mode was set to Off#", Arg1);
+                     Error_Msg_N ("\SPARK_Mode was set to Off#", Arg1);
                      raise Pragma_Exit;
                   end if;
                end if;
+
+               if Present (Entity) then
+                  if Present (Entity_Pragma) then
+                     if Get_SPARK_Mode_From_Pragma (Entity_Pragma) = Off
+                       and then Mode_Id = On
+                     then
+                        Error_Msg_N ("incorrect use of SPARK_Mode", Arg1);
+                        Error_Msg_Sloc := Sloc (Entity_Pragma);
+                        Error_Msg_NE
+                          ("\value Off was set for SPARK_Mode on & #",
+                           Arg1, Entity);
+                        raise Pragma_Exit;
+                     end if;
+                  else
+                     Error_Msg_N ("incorrect use of SPARK_Mode", Arg1);
+                     Error_Msg_Sloc := Sloc (Entity);
+                     Error_Msg_NE ("\no value was set for SPARK_Mode on & #",
+                                   Arg1, Entity);
+                     raise Pragma_Exit;
+                  end if;
+               end if;
             end Check_Pragma_Conformance;
 
             --------------------------------
@@ -18733,7 +18769,10 @@
                   then
                      Spec_Id := Defining_Entity (Stmt);
                      Check_Library_Level_Entity (Spec_Id);
-                     Check_Pragma_Conformance (SPARK_Pragma (Spec_Id));
+                     Check_Pragma_Conformance
+                       (Context_Pragma => SPARK_Pragma (Spec_Id),
+                        Entity_Pragma  => Empty,
+                        Entity         => Empty);
 
                      Set_SPARK_Pragma               (Spec_Id, N);
                      Set_SPARK_Pragma_Inherited     (Spec_Id, False);
@@ -18748,7 +18787,10 @@
                   then
                      Spec_Id := Defining_Entity (Stmt);
                      Check_Library_Level_Entity (Spec_Id);
-                     Check_Pragma_Conformance (SPARK_Pragma (Spec_Id));
+                     Check_Pragma_Conformance
+                       (Context_Pragma => SPARK_Pragma (Spec_Id),
+                        Entity_Pragma  => Empty,
+                        Entity         => Empty);
 
                      Set_SPARK_Pragma               (Spec_Id, N);
                      Set_SPARK_Pragma_Inherited     (Spec_Id, False);
@@ -18804,7 +18846,10 @@
 
                   if List_Containing (N) = Private_Declarations (Context) then
                      Check_Library_Level_Entity (Spec_Id);
-                     Check_Pragma_Conformance (SPARK_Aux_Pragma (Spec_Id));
+                     Check_Pragma_Conformance
+                       (Context_Pragma => Empty,
+                        Entity_Pragma  => SPARK_Pragma (Spec_Id),
+                        Entity         => Spec_Id);
                      SPARK_Mode_Pragma := N;
                      SPARK_Mode := Mode_Id;
 
@@ -18815,7 +18860,10 @@
 
                   else
                      Check_Library_Level_Entity (Spec_Id);
-                     Check_Pragma_Conformance (SPARK_Pragma (Spec_Id));
+                     Check_Pragma_Conformance
+                       (Context_Pragma => SPARK_Pragma (Spec_Id),
+                        Entity_Pragma  => Empty,
+                        Entity         => Empty);
                      SPARK_Mode_Pragma := N;
                      SPARK_Mode := Mode_Id;
 
@@ -18834,8 +18882,10 @@
                then
                   Spec_Id := Defining_Entity (Context);
                   Check_Library_Level_Entity (Spec_Id);
-                  Check_Pragma_Conformance (SPARK_Pragma (Spec_Id));
-
+                  Check_Pragma_Conformance
+                    (Context_Pragma => SPARK_Pragma (Spec_Id),
+                     Entity_Pragma  => Empty,
+                     Entity         => Empty);
                   Set_SPARK_Pragma           (Spec_Id, N);
                   Set_SPARK_Pragma_Inherited (Spec_Id, False);
 
@@ -18848,7 +18898,10 @@
                   Spec_Id := Corresponding_Spec (Context);
                   Body_Id := Defining_Entity (Context);
                   Check_Library_Level_Entity (Body_Id);
-                  Check_Pragma_Conformance (SPARK_Pragma (Body_Id));
+                  Check_Pragma_Conformance
+                    (Context_Pragma => SPARK_Pragma (Body_Id),
+                     Entity_Pragma  => SPARK_Aux_Pragma (Spec_Id),
+                     Entity         => Spec_Id);
                   SPARK_Mode_Pragma := N;
                   SPARK_Mode := Mode_Id;
 
@@ -18867,7 +18920,19 @@
                   Context := Specification (Context);
                   Body_Id := Defining_Entity (Context);
                   Check_Library_Level_Entity (Body_Id);
-                  Check_Pragma_Conformance (SPARK_Pragma (Body_Id));
+
+                  if Present (Spec_Id) then
+                     Check_Pragma_Conformance
+                       (Context_Pragma => SPARK_Pragma (Body_Id),
+                        Entity_Pragma  => SPARK_Pragma (Spec_Id),
+                        Entity         => Spec_Id);
+                  else
+                     Check_Pragma_Conformance
+                       (Context_Pragma => SPARK_Pragma (Body_Id),
+                        Entity_Pragma  => Empty,
+                        Entity         => Empty);
+                  end if;
+
                   SPARK_Mode_Pragma := N;
                   SPARK_Mode := Mode_Id;
 
@@ -18887,7 +18952,10 @@
                   Spec_Id := Corresponding_Spec (Context);
                   Body_Id := Defining_Entity (Context);
                   Check_Library_Level_Entity (Body_Id);
-                  Check_Pragma_Conformance (SPARK_Aux_Pragma (Body_Id));
+                  Check_Pragma_Conformance
+                    (Context_Pragma => Empty,
+                     Entity_Pragma  => SPARK_Pragma (Body_Id),
+                     Entity         => Body_Id);
                   SPARK_Mode_Pragma := N;
                   SPARK_Mode := Mode_Id;
 
Index: sem_ch6.adb
===================================================================
--- sem_ch6.adb (revision 207241)
+++ sem_ch6.adb (working copy)
@@ -2999,35 +2999,11 @@
 
             Push_Scope (Spec_Id);
 
-            --  Set SPARK_Mode
+            --  Set SPARK_Mode from context
 
-            --  For internally generated subprogram, always off. But generic
-            --  instances are not generated implicitly, so are never considered
-            --  as internal, even though Comes_From_Source is false.
+            Set_SPARK_Pragma (Body_Id, SPARK_Mode_Pragma);
+            Set_SPARK_Pragma_Inherited (Body_Id, True);
 
-            if not Comes_From_Source (Spec_Id)
-              and then not Is_Generic_Instance (Spec_Id)
-            then
-               SPARK_Mode := Off;
-               SPARK_Mode_Pragma := Empty;
-
-            --  Inherited from spec
-
-            elsif Present (SPARK_Pragma (Spec_Id))
-              and then not SPARK_Pragma_Inherited (Spec_Id)
-            then
-               SPARK_Mode_Pragma := SPARK_Pragma (Spec_Id);
-               SPARK_Mode := Get_SPARK_Mode_From_Pragma (SPARK_Mode_Pragma);
-               Set_SPARK_Pragma (Body_Id, SPARK_Pragma (Spec_Id));
-               Set_SPARK_Pragma_Inherited (Body_Id, True);
-
-            --  Otherwise set from context
-
-            else
-               Set_SPARK_Pragma (Body_Id, SPARK_Mode_Pragma);
-               Set_SPARK_Pragma_Inherited (Body_Id, True);
-            end if;
-
             --  Make sure that the subprogram is immediately visible. For
             --  child units that have no separate spec this is indispensable.
             --  Otherwise it is safe albeit redundant.
@@ -3076,17 +3052,10 @@
 
             Push_Scope (Body_Id);
 
-            --  Set SPARK_Mode from context or OFF for internal routine
+            --  Set SPARK_Mode from context
 
-            if Comes_From_Source (Body_Id) then
-               Set_SPARK_Pragma (Body_Id, SPARK_Mode_Pragma);
-               Set_SPARK_Pragma_Inherited (Body_Id, True);
-            else
-               Set_SPARK_Pragma (Body_Id, Empty);
-               Set_SPARK_Pragma_Inherited (Body_Id, False);
-               SPARK_Mode := Off;
-               SPARK_Mode_Pragma := Empty;
-            end if;
+            Set_SPARK_Pragma (Body_Id, SPARK_Mode_Pragma);
+            Set_SPARK_Pragma_Inherited (Body_Id, True);
          end if;
 
          --  For stubs and bodies with no previous spec, generate references to
@@ -3277,6 +3246,35 @@
 
       Analyze_Declarations (Declarations (N));
 
+      --  After declarations have been analyzed, the body has been set
+      --  its final value of SPARK_Mode. Check that SPARK_Mode for body
+      --  is consistent with SPARK_Mode for spec.
+
+      if Present (Spec_Id) and then Present (SPARK_Pragma (Body_Id)) then
+         if Present (SPARK_Pragma (Spec_Id)) then
+            if Get_SPARK_Mode_From_Pragma (SPARK_Pragma (Spec_Id)) = Off
+                 and then
+               Get_SPARK_Mode_From_Pragma (SPARK_Pragma (Body_Id)) = On
+            then
+               Error_Msg_Sloc := Sloc (SPARK_Pragma (Body_Id));
+               Error_Msg_N ("incorrect application of SPARK_Mode#", N);
+               Error_Msg_Sloc := Sloc (SPARK_Pragma (Spec_Id));
+               Error_Msg_NE
+                 ("\value Off was set for SPARK_Mode on & #", N, Spec_Id);
+            end if;
+
+         elsif Nkind (Parent (Parent (Spec_Id))) = N_Subprogram_Body_Stub then
+            null;
+
+         else
+            Error_Msg_Sloc := Sloc (SPARK_Pragma (Body_Id));
+            Error_Msg_N ("incorrect application of SPARK_Mode#", N);
+            Error_Msg_Sloc := Sloc (Spec_Id);
+            Error_Msg_NE
+              ("\no value was set for SPARK_Mode on & #", N, Spec_Id);
+         end if;
+      end if;
+
       --  Check completion, and analyze the statements
 
       Check_Completion;
@@ -3632,16 +3630,11 @@
 
       Generate_Definition (Designator);
 
-      --  Set SPARK mode, always off for internal routines, otherwise set
-      --  from current context (may be overwritten later with explicit pragma)
+      --  Set SPARK mode from current context (may be overwritten later with
+      --  explicit pragma).
 
-      if Comes_From_Source (Designator) then
-         Set_SPARK_Pragma (Designator, SPARK_Mode_Pragma);
-         Set_SPARK_Pragma_Inherited (Designator, True);
-      else
-         Set_SPARK_Pragma (Designator, Empty);
-         Set_SPARK_Pragma_Inherited (Designator, False);
-      end if;
+      Set_SPARK_Pragma (Designator, SPARK_Mode_Pragma);
+      Set_SPARK_Pragma_Inherited (Designator, True);
 
       if Debug_Flag_C then
          Write_Str ("==> subprogram spec ");

Reply via email to