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 <[email protected]>
* 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,