This implements two new pragmas, whose names, syntax, and semantics
match the corresponding aspects Type_Invariant and Type_Invariant'Class
as closely as possible. For Type_Invariant'Class the pragma is named
Type_Invariant_Class, since the attribute form of the name is not legal
for a pragma name.

The following test

     1. with Ada.Text_IO;    use Ada.Text_IO;
     2. with Ada.Assertions; use Ada.Assertions;
     3. with Ada.Exceptions; use Ada.Exceptions;
     4. procedure PSimpleinv is
     5.    package X is
     6.       type R is private;
     7.       pragma Type_Invariant (R, Testr (R));
     8.       function Testr (X : R) return Boolean;
     9.       function Gen (X : Integer) return R;
    10.       procedure Make (X : out R);
    11.
    12.       type T1 is tagged private;
    13.       pragma Type_Invariant (T1, Testt1 (T1));
    14.       type T2 is new T1 with private;
    15.       function Testt1 (X : T1) return Boolean;
    16.       function Maket2 return T2;
    17.
    18.    private
    19.       type R is record
    20.          Field : Integer := 4;
    21.       end record;
    22.
    23.       type T1 is tagged record
    24.          Field : Integer := 4;
    25.       end record;
    26.
    27.       type T2 is new T1 with record
    28.          Field2 : Integer := 4;
    29.       end record;
    30.    end X;
    31.
    32.    package body X is
    33.       function Testr (X : R) return Boolean is
    34.       begin
    35.          return X.Field mod 2 = 1;
    36.       end Testr;
    37.
    38.       function Gen (X : Integer) return R is
    39.       begin
    40.          return (Field => X);
    41.       end Gen;
    42.
    43.       procedure Make (X : out R) is
    44.       begin
    45.          X := (Field => 4);
    46.       end Make;
    47.
    48.       function Testt1 (X : T1) return Boolean is
    49.       begin
    50.          return X.Field mod 2 = 1;
    51.       end Testt1;
    52.
    53.       function Maket2 return T2 is
    54.       begin
    55.          return (Field => 4, Field2 => 3);
    56.       end Maket2;
    57.
    58.       --  No exception, private initialization
    59.
    60.       VPrivate : R := (Field => 6);
    61.    end X;
    62.
    63. begin
    64.    --  No exception, OK initialization
    65.
    66.    begin
    67.       declare
    68.          V1 : X.R := X.Gen (5);
    69.       begin
    70.          null;
    71.       end;
    72.       Put_Line ("V1: no exception");
    73.    end;
    74.
    75.    --  Bad result from public function
    76.
    77.    begin
    78.       declare
    79.          V2 : X.R := X.Gen (4);
    80.       begin
    81.          null;
    82.       end;
    83.       Put_Line ("V2: no exception");
    84.    exception
    85.       when E : Assertion_Error =>
    86.          Put_Line ("V2: " & Exception_Message (E));
    87.    end;
    88.
    89.    --  Bad default initialization
    90.
    91.    begin
    92.       declare
    93.          V3 : X.R;
    94.       begin
    95.          null;
    96.       end;
    97.       Put_Line ("V3: no exception");
    98.    exception
    99.       when E : Assertion_Error =>
   100.          Put_Line ("V3: " & Exception_Message (E));
   101.    end;
   102.
   103.    --  Bad OUT parameter
   104.
   105.    begin
   106.       declare
   107.          V4 : X.R := X.Gen (5);
   108.       begin
   109.          X.Make (V4);
   110.       end;
   111.       Put_Line ("V4: no exception");
   112.    exception
   113.       when E : Assertion_Error =>
   114.          Put_Line ("V4: " & Exception_Message (E));
   115.    end;
   116.
   117.    --  Bad conversion
   118.
   119.    begin
   120.       declare
   121.          TT : X.T2 := X.Maket2;
   122.          V5 : X.T1 := X.T1 (TT);
   123.       begin
   124.          null;
   125.       end;
   126.       Put_Line ("V5: no exception");
   127.    exception
   128.       when E : Assertion_Error =>
   129.          Put_Line ("V5: " & Exception_Message (E));
   130.    end;
   131. end PSimpleinv;

compiled with -gnata12 -gnatw.l, outputs:

V1: no exception
V2: failed invariant from psimpleinv.adb:7
V3: failed invariant from psimpleinv.adb:7
V4: failed invariant from psimpleinv.adb:7
V5: failed invariant from psimpleinv.adb:13

The following test:

     1. with Ada.Text_IO;    use Ada.Text_IO;
     2. with Ada.Assertions; use Ada.Assertions;
     3. with Ada.Exceptions; use Ada.Exceptions;
     4. procedure PInheritinv is
     5.    package X is
     6.
     7.       type T1 is tagged private;
     8.       pragma Type_Invariant_Class (T1, Testt1 (T1));
     9.
    10.       function Testt1 (X : T1) return Boolean;
    11.       function Maket1 return T1;
    12.
    13.       type T2 is new T1 with private;
                   |
        >>> info: "T2" inherits "Invariant'Class" aspect from line 8

    14.
    15.       function Maket2 return T2;
    16.       function Testt1 (X : T2) return Boolean;
    17.       function Maket1 return T2;
    18.
    19.    private
    20.       type T1 is tagged record
    21.          Field1 : Integer := 4;
    22.       end record;
    23.
    24.       type T2 is new T1 with record
    25.          Field2 : Integer := 4;
    26.       end record;
    27.    end X;
    28.
    29.    package body X is
    30.
    31.       function Testt1 (X : T1) return Boolean is
    32.       begin
    33.          return X.Field1 mod 2 = 1;
    34.       end Testt1;
    35.
    36.       function Testt1 (X : T2) return Boolean is
    37.       begin
    38.          return X.Field1 mod 2 = 0;
    39.       end Testt1;
    40.
    41.       function Maket1 return T1 is
    42.       begin
    43.          return (Field1 => 4);
    44.       end Maket1;
    45.
    46.       function Maket1 return T2 is
    47.       begin
    48.          return (Field1 => 4, Field2 => 3);
    49.       end Maket1;
    50.
    51.       function Maket2 return T2 is
    52.       begin
    53.          return (Field1 => 5, Field2 => 3);
    54.       end Maket2;
    55.    end X;
    56.
    57. begin
    58.    --  Bad value from Maket1
    59.
    60.    begin
    61.       declare
    62.          V1 : X.T1 := X.Maket1;
    63.       begin
    64.          null;
    65.       end;
    66.       Put_Line ("V1: no exception");
    67.    exception
    68.       when E : Assertion_Error =>
    69.          Put_Line ("V1: " & Exception_Message (E));
    70.    end;
    71.
    72.    --  Bad value from Maket2
    73.    --  (tested with overridden testt1)
    74.
    75.    begin
    76.       declare
    77.          V2 : X.T2 := X.Maket2;
    78.       begin
    79.          null;
    80.       end;
    81.       Put_Line ("V2: no exception");
    82.    exception
    83.       when E : Assertion_Error =>
    84.          Put_Line ("V2: " & Exception_Message (E));
    85.    end;
    86.
    87.    --  OK value from overridden Maket1
    88.    --  (tested with overridden testt1)
    89.
    90.    begin
    91.       declare
    92.          V3 : X.T2 := X.Maket1;
    93.       begin
    94.          null;
    95.       end;
    96.       Put_Line ("V3: no exception");
    97.    exception
    98.       when E : Assertion_Error =>
    99.          Put_Line ("V3: " & Exception_Message (E));
   100.    end;
   101.
   102. end PInheritinv;

compiled with -gnata12 -gnatw.l, outputs:

V1: failed invariant from pinheritinv.adb:8
V2: failed invariant from pinheritinv.adb:8
V3: no exception

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

2013-10-14  Robert Dewar  <de...@adacore.com>

        * exp_prag.adb (Expand_Pragma_Check): Generate proper string
        for invariant
        * gnat_rm.texi: Add documentation for pragmas
        Type_Invariant[_Class]
        * par-prag.adb: Add entries for pragmas Type_Invariant[_Class]
        * sem_ch13.adb: Minor reformatting
        * sem_prag.adb: Implement pragmas Type_Invariant[_Class]
        * snames.ads-tmpl: Add entries for pragmas Type_Invariant[_Class]

Index: exp_prag.adb
===================================================================
--- exp_prag.adb        (revision 203521)
+++ exp_prag.adb        (working copy)
@@ -311,6 +311,10 @@
       --  at" is omitted for name = Assertion, since it is redundant, given
       --  that the name of the exception is Assert_Failure.)
 
+      --  Also, instead of "XXX failed at", we generate slightly
+      --  different messages for some of the contract assertions (see
+      --  code below for details).
+
       --  An alternative expansion is used when the No_Exception_Propagation
       --  restriction is active and there is a local Assert_Failure handler.
       --  This is not a common combination of circumstances, but it occurs in
@@ -400,6 +404,15 @@
                   Insert_Str_In_Name_Buffer ("failed ", 1);
                   Add_Str_To_Name_Buffer (" from ");
 
+               --  For special case of Invariant, the string is "failed
+               --  invariant from yy", to be consistent with the string that is
+               --  generated for the aspect case (the code later on checks for
+               --  this specific string to modify it in some cases, so this is
+               --  functionally important).
+
+               elsif Nam = Name_Invariant then
+                  Add_Str_To_Name_Buffer ("failed invariant from ");
+
                --  For all other checks, the string is "xxx failed at yyy"
                --  where xxx is the check name with current source file casing.
 
Index: gnat_rm.texi
===================================================================
--- gnat_rm.texi        (revision 203521)
+++ gnat_rm.texi        (working copy)
@@ -253,6 +253,8 @@
 * Pragma Thread_Local_Storage::
 * Pragma Time_Slice::
 * Pragma Title::
+* Pragma Type_Invariant::
+* Pragma Type_Invariant_Class::
 * Pragma Unchecked_Union::
 * Pragma Unimplemented_Unit::
 * Pragma Universal_Aliasing ::
@@ -1073,6 +1075,8 @@
 * Pragma Thread_Local_Storage::
 * Pragma Time_Slice::
 * Pragma Title::
+* Pragma Type_Invariant::
+* Pragma Type_Invariant_Class::
 * Pragma Unchecked_Union::
 * Pragma Unimplemented_Unit::
 * Pragma Universal_Aliasing ::
@@ -5367,6 +5371,21 @@
   Dynamic_Predicate => F(Q) or G(Q);
 @end smallexample
 
+Note that there is are no pragmas @code{Dynamic_Predicate}
+or @code{Static_Predicate}. That is
+because these pragmas would affect legality and semantics of
+the program and thus do not have a neutral effect if ignored.
+The motivation behind providing pragmas equivalent to
+corresponding aspects is to allow a program to be written
+using the pragmas, and then compiled with a compiler that
+will ignore the pragmas. That doesn't work in the case of
+static and dynamic predicates, since if the corresponding
+pragmas are ignored, then the behavior of the program is
+fundamentally changed (for example a membership test
+@code{A in B} would not take into account a predicate
+defined for subtype B). When following this approach, the
+use of predicates should be avoided.
+
 @node Pragma Preelaborable_Initialization
 @unnumberedsec Pragma Preelaborable_Initialization
 @findex Preelaborable_Initialization
@@ -6786,6 +6805,56 @@
 notation is used, and named and positional notation can be mixed
 following the normal rules for procedure calls in Ada.
 
+@node Pragma Type_Invariant
+@unnumberedsec Pragma Type_Invariant
+@findex Invariant
+@findex Type_Invariant pragma
+@noindent
+Syntax:
+
+@smallexample @c ada
+pragma Type_Invariant
+  ([Entity =>] type_LOCAL_NAME,
+   [Check  =>] EXPRESSION);
+@end smallexample
+
+@noindent
+The @code{Type_Invariant} pragma is intended to be an exact
+replacement for the language-defined @code{Type_Invariant}
+aspect, and shares its restrictions and semantics. It differs
+from the language defined @code{Invariant} pragma in that it
+does not permit a string parameter, and it is
+controlled by the assertion identifier @code{Type_Invariant}
+rather than @code{Invariant}.
+
+@node Pragma Type_Invariant_Class
+@unnumberedsec Pragma Type_Invariant_Class
+@findex Invariant
+@findex Type_Invariant_Class pragma
+@noindent
+Syntax:
+
+@smallexample @c ada
+pragma Type_Invariant_Class
+  ([Entity =>] type_LOCAL_NAME,
+   [Check  =>] EXPRESSION);
+@end smallexample
+
+@noindent
+The @code{Type_Invariant_Class} pragma is intended to be an exact
+replacement for the language-defined @code{Type_Invariant'Class}
+aspect, and shares its restrictions and semantics.
+
+Note: This pragma is called @code{Type_Invariant_Class} rather than
+@code{Type_Invariant'Class} because the latter would not be strictly
+conforming to the allowed syntax for pragmas. The motivation
+for providing pragmas equivalent to the aspects is to allow a program
+to be written using the pragmas, and then compiled if necessary
+using an Ada compiler that does not recognize the pragmas or
+aspects, but is prepared to ignore the pragmas. The assertion
+policy that controls this pragma is @code{Type_Invariant'Class},
+not @code{Type_Invariant_Class}.
+
 @node Pragma Unchecked_Union
 @unnumberedsec Pragma Unchecked_Union
 @cindex Unions in C
Index: sem_prag.adb
===================================================================
--- sem_prag.adb        (revision 203523)
+++ sem_prag.adb        (working copy)
@@ -846,7 +846,7 @@
 
          if Is_Input then
             if (Ekind (Item_Id) = E_Out_Parameter
-                  and then not Is_Unconstrained_Or_Tagged_Item (Item_Id))
+                 and then not Is_Unconstrained_Or_Tagged_Item (Item_Id))
               or else
                (Global_Seen and then not Appears_In (Subp_Inputs, Item_Id))
             then
@@ -11772,7 +11772,6 @@
                 Name_Link_Name));
 
             Check_At_Least_N_Arguments (2);
-
             Check_At_Most_N_Arguments  (4);
             Process_Convention (C, Def_Id);
 
@@ -13716,7 +13715,7 @@
          begin
             GNAT_Pragma;
             Check_At_Least_N_Arguments (2);
-            Check_At_Most_N_Arguments (3);
+            Check_At_Most_N_Arguments  (3);
             Check_Optional_Identifier (Arg1, Name_Entity);
             Check_Optional_Identifier (Arg2, Name_Check);
 
@@ -15316,7 +15315,7 @@
          begin
             GNAT_Pragma;
             Check_At_Least_N_Arguments (1);
-            Check_At_Most_N_Arguments (2);
+            Check_At_Most_N_Arguments  (2);
 
             --  Process first argument
 
@@ -15700,11 +15699,13 @@
 
          begin
             GNAT_Pragma;
-            Check_At_Least_N_Arguments (1);
-            Check_At_Most_N_Arguments (1);
+            Check_Arg_Count (1);
             Check_No_Identifiers;
             Check_Pre_Post;
 
+            --  Rewrite Post[_Class] pragma as Precondition pragma setting the
+            --  flag Class_Present to True for the Post_Class case.
+
             Set_Class_Present (N, Prag_Id = Pragma_Pre_Class);
             PC_Pragma := New_Copy (N);
             Set_Pragma_Identifier
@@ -15760,11 +15761,13 @@
 
          begin
             GNAT_Pragma;
-            Check_At_Least_N_Arguments (1);
-            Check_At_Most_N_Arguments (1);
+            Check_Arg_Count (1);
             Check_No_Identifiers;
             Check_Pre_Post;
 
+            --  Rewrite Pre[_Class] pragma as Precondition pragma setting the
+            --  flag Class_Present to True for the Pre_Class case.
+
             Set_Class_Present (N, Prag_Id = Pragma_Pre_Class);
             PC_Pragma := New_Copy (N);
             Set_Pragma_Identifier
@@ -15787,7 +15790,7 @@
          begin
             GNAT_Pragma;
             Check_At_Least_N_Arguments (1);
-            Check_At_Most_N_Arguments (2);
+            Check_At_Most_N_Arguments  (2);
             Check_Optional_Identifier (Arg1, Name_Check);
             Check_Precondition_Postcondition (In_Body);
 
@@ -18317,6 +18320,34 @@
             end loop;
          end Title;
 
+         ----------------------------
+         -- Type_Invariant[_Class] --
+         ----------------------------
+
+         --  pragma Type_Invariant[_Class]
+         --    ([Entity =>] type_LOCAL_NAME,
+         --     [Check  =>] EXPRESSION);
+
+         when Pragma_Type_Invariant       |
+              Pragma_Type_Invariant_Class =>
+         Type_Invariant : declare
+            I_Pragma : Node_Id;
+
+         begin
+            Check_Arg_Count (2);
+
+            --  Rewrite Type_Invariant[_Class] pragma as an Invariant pragma,
+            --  setting Class_Present for the Type_Invariant_Class case.
+
+            Set_Class_Present (N, Prag_Id = Pragma_Type_Invariant_Class);
+            I_Pragma := New_Copy (N);
+            Set_Pragma_Identifier
+              (I_Pragma, Make_Identifier (Loc, Name_Invariant));
+            Rewrite (N, I_Pragma);
+            Set_Analyzed (N, False);
+            Analyze (N);
+         end Type_Invariant;
+
          ---------------------
          -- Unchecked_Union --
          ---------------------
@@ -21493,6 +21524,8 @@
       Pragma_Thread_Local_Storage           =>  0,
       Pragma_Time_Slice                     => -1,
       Pragma_Title                          => -1,
+      Pragma_Type_Invariant                 => -1,
+      Pragma_Type_Invariant_Class           => -1,
       Pragma_Unchecked_Union                =>  0,
       Pragma_Unimplemented_Unit             => -1,
       Pragma_Universal_Aliasing             => -1,
Index: par-prag.adb
===================================================================
--- par-prag.adb        (revision 203522)
+++ par-prag.adb        (working copy)
@@ -1293,6 +1293,8 @@
            Pragma_Thread_Local_Storage           |
            Pragma_Time_Slice                     |
            Pragma_Title                          |
+           Pragma_Type_Invariant                 |
+           Pragma_Type_Invariant_Class           |
            Pragma_Unchecked_Union                |
            Pragma_Unimplemented_Unit             |
            Pragma_Universal_Aliasing             |
Index: sem_ch13.adb
===================================================================
--- sem_ch13.adb        (revision 203522)
+++ sem_ch13.adb        (working copy)
@@ -5961,7 +5961,6 @@
 
       if Present (SId) then
          PDecl := Unit_Declaration_Node (SId);
-
       else
          PDecl := Build_Invariant_Procedure_Declaration (Typ);
       end if;
Index: snames.ads-tmpl
===================================================================
--- snames.ads-tmpl     (revision 203522)
+++ snames.ads-tmpl     (working copy)
@@ -144,8 +144,6 @@
    Name_Dynamic_Predicate              : constant Name_Id := N + $;
    Name_Static_Predicate               : constant Name_Id := N + $;
    Name_Synchronization                : constant Name_Id := N + $;
-   Name_Type_Invariant                 : constant Name_Id := N + $;
-   Name_Type_Invariant_Class           : constant Name_Id := N + $;
 
    --  Some special names used by the expander. Note that the lower case u's
    --  at the start of these names get translated to extra underscores. These
@@ -448,7 +446,7 @@
    Name_Wide_Character_Encoding        : constant Name_Id := N + $; -- GNAT
    Last_Configuration_Pragma_Name      : constant Name_Id := N + $;
 
-   --  Remaining pragma names
+   --  Remaining pragma names (non-configuration pragmas)
 
    Name_Abort_Defer                    : constant Name_Id := N + $; -- GNAT
    Name_Abstract_State                 : constant Name_Id := N + $; -- GNAT
@@ -621,6 +619,8 @@
    Name_Thread_Local_Storage           : constant Name_Id := N + $; -- GNAT
    Name_Time_Slice                     : constant Name_Id := N + $; -- GNAT
    Name_Title                          : constant Name_Id := N + $; -- GNAT
+   Name_Type_Invariant                 : constant Name_Id := N + $; -- GNAT
+   Name_Type_Invariant_Class           : constant Name_Id := N + $; -- GNAT
    Name_Unchecked_Union                : constant Name_Id := N + $; -- Ada 05
    Name_Unimplemented_Unit             : constant Name_Id := N + $; -- GNAT
    Name_Universal_Aliasing             : constant Name_Id := N + $; -- GNAT
@@ -1905,6 +1905,8 @@
       Pragma_Thread_Local_Storage,
       Pragma_Time_Slice,
       Pragma_Title,
+      Pragma_Type_Invariant,
+      Pragma_Type_Invariant_Class,
       Pragma_Unchecked_Union,
       Pragma_Unimplemented_Unit,
       Pragma_Universal_Aliasing,

Reply via email to