Patchwork [Ada] Extended syntax for Check_Policy

login
register
mail settings
Submitter Arnaud Charlet
Date April 12, 2013, 1:46 p.m.
Message ID <20130412134606.GA2282@adacore.com>
Download mbox | patch
Permalink /patch/236097/
State New
Headers show

Comments

Arnaud Charlet - April 12, 2013, 1:46 p.m.
With this patch, Check_Policy can optionally use the same syntax as
Assertion_Policy (the old syntax is still allowed).

This test shows error detection

     1. package BadCpol is
     2.    pragma Check_Policy
     3.      (Junk, Ignore);                        -- OK
     4.    pragma Check_Policy
     5.      (Junk => Ignore);                      -- OK
     6.    pragma Check_Policy
     7.      (Name => Junk, Policy => Ignore);      -- OK
     8.    pragma Check_Policy
     9.      (Name => Name, Policy => Policy);      -- Error
                      |
        >>> pragma "Check_Policy" does not allow "Name" as check name

    10.    pragma Check_Policy
    11.      (Name => Name1, Policy => Ignore);     -- OK
    12.    pragma Check_Policy
    13.      (Policy, Ignore);                      -- Error
              |
        >>> pragma "Check_Policy" does not allow "Policy" as check name

    14.    pragma Check_Policy
    15.      (Policy => Ignore);                    -- Error
              |
        >>> pragma "Check_Policy" does not allow "Policy" as check name

    16.    pragma Check_Policy
    17.      (Pre'Class, Ignore);                   -- OK
    18.    pragma Check_Policy
    19.      (Pre'Class => Check);                  -- OK
    20.    pragma Check_Policy
    21.      (Pre => Check, Post);                  -- Error
                            |
        >>> pragma argument identifier required here
        >>> since previous argument had identifier (RM 2.8(4))
        >>> missing assertion kind for pragma "Check_Policy"

    22. end;

The following test compiled and run without -gnata generates
the output:

OK 1
OK 2
OK 3

     1. with Text_IO; use Text_IO;
     2. with System.Assertions;
     3. use System.Assertions;
     4.
     5. procedure GoodCpol is
     6.    X, Y : Integer;
     7.
     8. begin
     9.    X := 23;
    10.    Y := 24;
    11.
    12.    declare
    13.       pragma Check_Policy (Grumble, Check);
    14.    begin
    15.       pragma Check (Grumble, Y < X);
              |
        >>> warning: check will fail at run time

    16.       Put_Line ("Not OK 1");
    17.    exception
    18.       when Assert_Failure =>
    19.          Put_Line ("OK 1");
    20.    end;
    21.
    22.    declare
    23.       pragma Check_Policy (Mumble => Ignore, Grumble => Check);
    24.    begin
    25.       pragma Check (Mumble, Y = 100, "Not OK 2");
    26.       pragma Check (Grumble, Y < X);
    27.       Put_Line ("Not OK 3");
    28.    exception
    29.       when Assert_Failure =>
    30.          Put_Line ("OK 2");
    31.
    32.    end;
    33.    declare
    34.       pragma Check_Policy (Mumble => Disable);
    35.    begin
    36.       pragma Check (Mumble, "Not OK 4");
    37.       Put_Line ("OK 3");
    38.    exception
    39.       when Assert_Failure =>
    40.          Put_Line ("Not OK 5");
    41.    end;
    42. end GoodCpol;

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

2013-04-12  Robert Dewar  <dewar@adacore.com>

	* exp_util.adb (Make_Invariant_Call): Use Check_Kind instead
	of Check_Enabled.
	* gnat_rm.texi (Check_Policy): Update documentation for new
	Check_Policy syntax.
	* sem_prag.adb (Check_Kind): Replaces Check_Enabled
	(Analyze_Pragma, case Check_Policy): Rework to accomodate new
	syntax (like Assertion_Policy).
	* sem_prag.ads (Check_Kind): Replaces Check_Enabled.

Patch

Index: gnat_rm.texi
===================================================================
--- gnat_rm.texi	(revision 197915)
+++ gnat_rm.texi	(working copy)
@@ -1557,15 +1557,27 @@ 
  ([Name   =>] CHECK_KIND,
   [Policy =>] POLICY_IDENTIFIER);
 
-CHECK_KIND ::= IDENTIFIER |
-                   Pre'Class | Post'Class | Type_Invariant'Class
+Pragma Check_Policy (
+    CHECK_KIND => POLICY_IDENTIFIER
+ @{, CHECK_KIND => POLICY_IDENTIFIER@});
 
+ASSERTION_KIND ::= RM_ASSERTION_KIND | ID_ASSERTION_KIND
+
+CHECK_KIND ::= IDENTIFIER           |
+               Pre'Class            |
+               Post'Class           |
+               Type_Invariant'Class |
+               Invariant'Class
+
+The identifiers Name and Policy are not allowed as CHECK_KIND values. This
+avoids confusion between the two possible syntax forms for this pragma.
+
 POLICY_IDENTIFIER ::= ON | OFF | CHECK | DISABLE | IGNORE
 @end smallexample
 
 @noindent
 This pragma is used to set the checking policy for assertions (specified
-by aspects of pragmas), the @code{Debug} pragma, or additional checks
+by aspects or pragmas), the @code{Debug} pragma, or additional checks
 to be checked using the @code{Check} pragma. It may appear either as
 a configuration pragma, or within a declarative part of package. In the
 latter case, it applies from the point where it appears to the end of
@@ -1573,10 +1585,8 @@ 
 
 The @code{Check_Policy} pragma is similar to the
 predefined @code{Assertion_Policy} pragma,
-and if the first argument corresponds to one of the assertion kinds that
+and if the check kind corresponds to one of the assertion kinds that
 are allowed by @code{Assertion_Policy}, then the effect is identical.
-The identifiers @code{Precondition} and @code{Postcondition} are allowed
-synonyms for @code{Pre} and @code{Post}.
 
 If the first argument is Debug, then the policy applies to Debug pragmas,
 disabling their effect if the policy is @code{Off}, @code{Disable}, or
@@ -1605,9 +1615,8 @@ 
 The check policy settings @code{CHECK} and @code{IGNORE} are recognized
 as synonyms for @code{ON} and @code{OFF}. These synonyms are provided for
 compatibility with the standard @code{Assertion_Policy} pragma. The check
-policy setting @code{DISABLE} is also synonymous with @code{OFF} in this
-context, but does not have any other significance for check
-names other than assertion kinds.
+policy setting @code{DISABLE} causes the second argument of a corresponding
+@code{Check} pragma to be completely ignored and not analyzed.
 
 @node Pragma Comment
 @unnumberedsec Pragma Comment
Index: exp_util.adb
===================================================================
--- exp_util.adb	(revision 197917)
+++ exp_util.adb	(working copy)
@@ -5456,7 +5456,7 @@ 
       pragma Assert
         (Has_Invariants (Typ) and then Present (Invariant_Procedure (Typ)));
 
-      if Check_Enabled (Name_Invariant) then
+      if Check_Kind (Name_Invariant) = Name_Check then
          return
            Make_Procedure_Call_Statement (Loc,
              Name                   =>
Index: sem_prag.adb
===================================================================
--- sem_prag.adb	(revision 197917)
+++ sem_prag.adb	(working copy)
@@ -2320,12 +2320,12 @@ 
          --  For a pragma PPC in the extended main source unit, record enabled
          --  status in SCO.
 
-         --  This may seem redundant with the call to Check_Enabled occurring
-         --  later on when the pragma is rewritten into a pragma Check but
-         --  is actually required in the case of a postcondition within a
+         --  This may seem redundant with the call to Check_Kind test that
+         --  occurs later on when the pragma is rewritten into a pragma Check
+         --  but is actually required in the case of a postcondition within a
          --  generic.
 
-         if Check_Enabled (Pname) and then not Split_PPC (N) then
+         if Check_Kind (Pname) = Name_Check and then not Split_PPC (N) then
             Set_SCO_Pragma_Enabled (Loc);
          end if;
 
@@ -6763,7 +6763,11 @@ 
 
       Check_Applicable_Policy (N);
 
+      --  If pragma is disable, rewrite as Null statement and skip analysis
+
       if Is_Disabled (N) then
+         Rewrite (N, Make_Null_Statement (Loc));
+         Analyze (N);
          raise Pragma_Exit;
       end if;
 
@@ -7612,6 +7616,7 @@ 
                --  now inserted all the equivalent Check pragmas.
 
                Rewrite (N, Make_Null_Statement (Loc));
+               Analyze (N);
             end if;
          end Assertion_Policy;
 
@@ -8096,7 +8101,32 @@ 
             Rewrite_Assertion_Kind (Get_Pragma_Arg (Arg1));
             Check_Arg_Is_Identifier (Arg1);
             Cname := Chars (Get_Pragma_Arg (Arg1));
-            Check_On := Check_Enabled (Cname);
+
+            --  Set Check_On to indicate check status
+
+            case Check_Kind (Cname) is
+               when Name_Ignore =>
+                  Check_On := False;
+
+               when Name_Check =>
+                  Check_On := True;
+
+               --  For disable, rewrite pragma as null statement and skip
+               --  rest of the analysis of the pragma.
+
+               when Name_Disable =>
+                  Rewrite (N, Make_Null_Statement (Loc));
+                  Analyze (N);
+                  raise Pragma_Exit;
+
+               --  No other possibilities
+
+               when others =>
+                  raise Program_Error;
+            end case;
+
+            --  If check kind was not Disable, then continue pragma analysis
+
             Expr := Get_Pragma_Arg (Arg2);
 
             --  Deal with SCO generation
@@ -8233,24 +8263,36 @@ 
          -- Check_Policy --
          ------------------
 
+         --  This is the old style syntax, which is still allowed in all modes:
+
          --  pragma Check_Policy ([Name   =>] CHECK_KIND
          --                       [Policy =>] POLICY_IDENTIFIER);
 
          --  POLICY_IDENTIFIER ::= On | Off | Check | Disable | Ignore
 
-         --  CHECK_KIND ::= IDENTIFIER |
-         --                 Pre'Class | Post'Class | Identifier'Class
+         --  CHECK_KIND ::= IDENTIFIER           |
+         --                 Pre'Class            |
+         --                 Post'Class           |
+         --                 Type_Invariant'Class |
+         --                 Invariant'Class
 
-         when Pragma_Check_Policy => Check_Policy :
+         --  This is the new style syntax, compatible with Assertion_Policy
+         --  and also allowed in all modes.
+
+         --  Pragma Check_Policy (
+         --      CHECK_KIND => POLICY_IDENTIFIER
+         --   {, CHECK_KIND => POLICY_IDENTIFIER});
+
+         --  Note: the identifiers Name and Policy are not allowed as
+         --  Check_Kind values. This avoids ambiguities between the old and
+         --  new form syntax.
+
+         when Pragma_Check_Policy => Check_Policy : declare
+            Kind : Node_Id;
+
          begin
             GNAT_Pragma;
-            Check_Arg_Count (2);
-            Check_Optional_Identifier (Arg1, Name_Name);
-            Rewrite_Assertion_Kind (Get_Pragma_Arg (Arg1));
-            Check_Arg_Is_Identifier (Arg1);
-            Check_Optional_Identifier (Arg2, Name_Policy);
-            Check_Arg_Is_One_Of
-              (Arg2, Name_On, Name_Off, Name_Check, Name_Disable, Name_Ignore);
+            Check_At_Least_N_Arguments (1);
 
             --  A Check_Policy pragma can appear either as a configuration
             --  pragma, or in a declarative part or a package spec (see RM
@@ -8261,8 +8303,90 @@ 
                Check_Is_In_Decl_Part_Or_Package_Spec;
             end if;
 
-            Set_Next_Pragma (N, Opt.Check_Policy_List);
-            Opt.Check_Policy_List := N;
+            --  Figure out if we have the old or new syntax. We have the
+            --  old syntax if the first argument has no identifier, or the
+            --  identifier is Name.
+
+            if Nkind (Arg1) /= N_Pragma_Argument_Association
+               or else Nam_In (Chars (Arg1), No_Name, Name_Name)
+            then
+               --  Old syntax
+
+               Check_Arg_Count (2);
+               Check_Optional_Identifier (Arg1, Name_Name);
+               Kind := Get_Pragma_Arg (Arg1);
+               Rewrite_Assertion_Kind (Kind);
+               Check_Arg_Is_Identifier (Arg1);
+
+               --  Check forbidden check kind
+
+               if Nam_In (Chars (Kind), Name_Name, Name_Policy) then
+                  Error_Msg_Name_2 := Chars (Kind);
+                     Error_Pragma_Arg
+                       ("pragma% does not allow% as check name", Arg1);
+               end if;
+
+               --  Check policy
+
+               Check_Optional_Identifier (Arg2, Name_Policy);
+               Check_Arg_Is_One_Of
+                 (Arg2,
+                  Name_On, Name_Off, Name_Check, Name_Disable, Name_Ignore);
+
+               --  And chain pragma on the Check_Policy_List for search
+
+               Set_Next_Pragma (N, Opt.Check_Policy_List);
+               Opt.Check_Policy_List := N;
+
+            --  For the new syntax, what we do is to convert each argument to
+            --  an old syntax equivalent. We do that because we want to chain
+            --  old style Check_Pragmas for the search (we don't wnat to have
+            --  to deal with multiple arguments in the search)
+
+            else
+               declare
+                  Arg  : Node_Id;
+                  Argx : Node_Id;
+                  LocP : Source_Ptr;
+
+               begin
+                  Arg := Arg1;
+                  while Present (Arg) loop
+                     LocP := Sloc (Arg);
+                     Argx := Get_Pragma_Arg (Arg);
+
+                     --  Kind must be specified
+
+                     if Nkind (Arg) /= N_Pragma_Argument_Association
+                       or else Chars (Arg) = No_Name
+                     then
+                        Error_Pragma_Arg
+                          ("missing assertion kind for pragma%", Arg);
+                     end if;
+
+                     --  Construct equivalent old form syntax Check_Policy
+                     --  pragma and insert it to get remaining checks.
+
+                     Insert_Action (N,
+                       Make_Pragma (LocP,
+                         Chars                        => Name_Check_Policy,
+                         Pragma_Argument_Associations => New_List (
+                           Make_Pragma_Argument_Association (LocP,
+                             Expression =>
+                               Make_Identifier (LocP, Chars (Arg))),
+                           Make_Pragma_Argument_Association (Sloc (Argx),
+                             Expression => Argx))));
+
+                     Arg := Next (Arg);
+                  end loop;
+
+                  --  Rewrite original Check_Policy pragma to null, since we
+                  --  have converted it into a series of old syntax pragmas.
+
+                  Rewrite (N, Make_Null_Statement (Loc));
+                  Analyze (N);
+               end;
+            end if;
          end Check_Policy;
 
          ---------------------
@@ -17734,11 +17858,11 @@ 
       when Pragma_Exit => null;
    end Analyze_Pragma;
 
-   -------------------
-   -- Check_Enabled --
-   -------------------
+   ----------------
+   -- Check_Kind --
+   ----------------
 
-   function Check_Enabled (Nam : Name_Id) return Boolean is
+   function Check_Kind (Nam : Name_Id) return Name_Id is
       PP : Node_Id;
 
    begin
@@ -17757,9 +17881,11 @@ 
             then
                case (Chars (Get_Pragma_Arg (Last (PPA)))) is
                   when Name_On | Name_Check =>
-                     return True;
-                  when Name_Off | Name_Disable | Name_Ignore =>
-                     return False;
+                     return Name_Check;
+                  when Name_Off | Name_Ignore =>
+                     return Name_Ignore;
+                  when Name_Disable =>
+                     return Name_Disable;
                   when others =>
                      raise Program_Error;
                end case;
@@ -17775,8 +17901,12 @@ 
       --  compatibility with the RM for the cases of assertion, invariant,
       --  precondition, predicate, and postcondition.
 
-      return Assertions_Enabled;
-   end Check_Enabled;
+      if Assertions_Enabled then
+         return Name_Check;
+      else
+         return Name_Ignore;
+      end if;
+   end Check_Kind;
 
    -----------------------------
    -- Check_Applicable_Policy --
Index: sem_prag.ads
===================================================================
--- sem_prag.ads	(revision 197915)
+++ sem_prag.ads	(working copy)
@@ -54,7 +54,7 @@ 
    --  of the expressions in the pragma as "spec expressions" (see section
    --  in Sem "Handling of Default and Per-Object Expressions...").
 
-   function Check_Enabled (Nam : Name_Id) return Boolean;
+   function Check_Kind (Nam : Name_Id) return Name_Id;
    --  This function is used in connection with pragmas Assertion, Check,
    --  and assertion aspects and pragmas, to determine if Check pragmas
    --  (or corresponding assertion aspects or pragmas) are currently active
@@ -63,17 +63,15 @@ 
    --  Assertion_Policy as configuration pragmas either in a configuration
    --  pragma file, or at the start of the current unit, or locally given
    --  Check_Policy and Assertion_Policy pragmas that are currently active.
-   --  True is returned if the specified check is enabled.
    --
-   --  This function knows about all relevant synonyms (e.g. Precondition or
-   --  Pre can be used to refer to the Pre aspect or Precondition pragma, and
-   --  Predicate refers to both static and dynamic predicates, and Assertion
-   --  applies to all assertion aspects and pragmas).
+   --  The value returned is one of the names Check, Ignore, Disable (On
+   --  returns Check, and Off returns Ignore).
    --
-   --  Note: for assertion kinds Pre'Class, Post'Class, Type_Invariant'Class,
-   --  the name passed is Name_uPre, Name_uPost, Name_uType_Invariant, which
-   --  corresponds to _Pre, _Post, _Type_Invariant, which are special names
-   --  used in identifiers to represent these attribute references.
+   --  Note: for assertion kinds Pre'Class, Post'Class, Invariant'Class,
+   --  and Type_Invariant'Class, the name passed is Name_uPre, Name_uPost,
+   --  Name_uInvariant, or Name_uType_Invariant, which corresponds to _Pre,
+   --  _Post, _Invariant, or _Type_Invariant, which are special names used
+   --  in identifiers to represent these attribute references.
 
    procedure Check_Applicable_Policy (N : Node_Id);
    --  N is either an N_Aspect or an N_Pragma node. There are two cases. If
@@ -83,9 +81,9 @@ 
    --  we use for the purpose of this procedure is the aspect name, which may
    --  be different from the pragma name (e.g. Precondition for Pre aspect).
    --  In addition, 'Class aspects are recognized (and the corresponding
-   --  special names used in the processing.
+   --  special names used in the processing).
    --
-   --  If the name is valid assertion_Kind name, then the Check_Policy pragma
+   --  If the name is valid ASSERTION_KIND name, then the Check_Policy pragma
    --  chain is checked for a matching entry (or for an Assertion entry which
    --  matches all possibilities). If a matching entry is found then the policy
    --  is checked. If it is Off, Ignore, or Disable, then the Is_Ignored flag