This patch complete the generation of invariant checks, for the case of
return values or in-out parameters that involve access types whose designated
type has invariants.

Executing:

      gnatmake -q -gnat12 -gnata main
      main

must yield:

 1
TEST 0
 1
TEST 1
 2
TEST 2
 2
TEST 3
TEST 4
 3
 4
TEST 5
 3
 4
END

---
with P; use P;
with Ada.Text_IO; use Ada.Text_IO;

procedure Main is
   O : T;                   --  value = 1
   V : T_Access := new T;   --  value = 2
   W : aliased X;
begin
   W.V1 := new T;           --  value = 3
   W.V2 := new T;           --  value = 4

   Put_Line ("TEST 0");
   Test_0 (O);

   Put_Line ("TEST 1");
   Test_1 (V);

   Put_Line ("TEST 2");
   Test_2 (V);

   Put_Line ("TEST 3");
   Test_3 (W);

   Put_Line ("TEST 4");
   Test_4 (W);

   Put_Line ("TEST 5");
   Test_5 (W'Access);

   Put_Line ("END");
end Main;
---
package P is

   type T is private
   with Type_Invariant => Check (T);

   type T_Access is access all T;

   type X is record
      V1 : access T;
      V2 : T_Access;
   end record;

   function Make (X : integer) return T;
   function Make (X : integer) return access T;

   procedure Test_0 (Obj : in out T);

   function Check (O : T) return Boolean;

   procedure Test_1 (V : access T);

   procedure Test_2 (V : T_Access);

   procedure Test_3 (V : X);

   procedure Test_4 (V : in out X);

   procedure Test_5 (V : access X);

private
   Counter : Integer := 0;
   function Incr return Integer;

   type T is record
      Value : Integer := Incr;
   end record;

end P;
---
with Ada.Text_IO; use Ada.Text_IO;
package body P is
   function Incr return Integer is
   begin
      Counter := Counter + 1;
      return Counter;
   end;

   Root : aliased T := (others => 15);

   function Check (O : T) return Boolean is
   begin
      Put_Line (Integer'Image (O.Value));
      return True;
   end Check;

   function Make (X : Integer) return T is
   begin
      return (Value => X);
   end;

   function Make (X : Integer) return access T is
   begin
      return Root'access;
   end;

   procedure Test_0 (Obj : in out T) is
   begin
      null;
   end;

   procedure Test_1 (V : access T) is
   begin
      null;
   end Test_1;

   procedure Test_2 (V : T_Access) is
   begin
      null;
   end Test_2;

   procedure Test_3 (V : X) is
   begin
      null;
   end Test_3;

   procedure Test_4 (V : in out X) is
   begin
      null;
   end Test_4;

   procedure Test_5 (V : access X) is
   begin
      null;
   end Test_5;

end P;

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

2012-10-02  Ed Schonberg  <schonb...@adacore.com>

        * sem_ch6.adb (Process_PPCs): Generate invariant checks for a
        return value whose type is an access type and whose designated
        type has invariants. Ditto for in-out parameters and in-parameters
        of an access type.
        * exp_ch3.adb (Build_Component_Invariant_Call): Add invariant check
        for an access component whose designated type has invariants.

Index: sem_ch6.adb
===================================================================
--- sem_ch6.adb (revision 191911)
+++ sem_ch6.adb (working copy)
@@ -11078,6 +11078,12 @@
       Plist : List_Id := No_List;
       --  List of generated postconditions
 
+      procedure Check_Access_Invariants (E : Entity_Id);
+      --  If the subprogram returns an access to a type with invariants, or
+      --  has access parameters whose designated type has an invariant, then
+      --  under the same visibility conditions as for other invariant checks,
+      --  the type invariant must be applied to the returned value.
+
       function Grab_CC return Node_Id;
       --  Prag contains an analyzed contract case pragma. This function copies
       --  relevant components of the pragma, creates the corresponding Check
@@ -11108,6 +11114,43 @@
       --  that an invariant check is required (for an IN OUT parameter, or
       --  the returned value of a function.
 
+      -----------------------------
+      -- Check_Access_Invariants --
+      -----------------------------
+
+      procedure Check_Access_Invariants (E : Entity_Id) is
+         Call : Node_Id;
+         Obj  : Node_Id;
+         Typ  : Entity_Id;
+
+      begin
+         if Is_Access_Type (Etype (E))
+           and then not Is_Access_Constant (Etype (E))
+         then
+            Typ := Designated_Type (Etype (E));
+
+            if Has_Invariants (Typ)
+              and then Present (Invariant_Procedure (Typ))
+              and then Is_Public_Subprogram_For (Typ)
+            then
+               Obj :=
+                 Make_Explicit_Dereference (Loc,
+                   Prefix => New_Occurrence_Of (E, Loc));
+               Set_Etype (Obj, Typ);
+
+               Call := Make_Invariant_Call (Obj);
+
+               Append_To (Plist,
+                 Make_If_Statement (Loc,
+                   Condition =>
+                     Make_Op_Ne (Loc,
+                       Left_Opnd   => Make_Null (Loc),
+                       Right_Opnd  => New_Occurrence_Of (E, Loc)),
+                   Then_Statements => New_List (Call)));
+            end if;
+         end if;
+      end Check_Access_Invariants;
+
       -------------
       -- Grab_CC --
       -------------
@@ -11308,12 +11351,19 @@
          Formal : Entity_Id;
 
       begin
-         --  Check function return result
+         --  Check function return result. If result is an access type there
+         --  may be invariants on the designated type.
 
          if Ekind (Designator) /= E_Procedure
            and then Has_Invariants (Etype (Designator))
          then
             return True;
+
+         elsif Ekind (Designator) /= E_Procedure
+           and then Is_Access_Type (Etype (Designator))
+           and then Has_Invariants (Designated_Type (Etype (Designator)))
+         then
+            return True;
          end if;
 
          --  Check parameters
@@ -11321,11 +11371,15 @@
          Formal := First_Formal (Designator);
          while Present (Formal) loop
             if Ekind (Formal) /= E_In_Parameter
-              and then
-                (Has_Invariants (Etype (Formal))
-                  or else Present (Predicate_Function (Etype (Formal))))
+              and then (Has_Invariants (Etype (Formal))
+                         or else Present (Predicate_Function (Etype (Formal))))
             then
                return True;
+
+            elsif Is_Access_Type (Etype (Formal))
+              and then Has_Invariants (Designated_Type (Etype (Formal)))
+            then
+               return True;
             end if;
 
             Next_Formal (Formal);
@@ -11731,6 +11785,10 @@
                   Append_To (Plist,
                     Make_Invariant_Call (New_Occurrence_Of (Rent, Loc)));
                end if;
+
+               --  Same if return value is an access to type with invariants.
+
+               Check_Access_Invariants (Rent);
             end;
 
          --  Procedure rather than a function
@@ -11750,7 +11808,9 @@
          begin
             Formal := First_Formal (Designator);
             while Present (Formal) loop
-               if Ekind (Formal) /= E_In_Parameter then
+               if Ekind (Formal) /= E_In_Parameter
+                 or else Is_Access_Type (Etype (Formal))
+               then
                   Ftype := Etype (Formal);
 
                   if Has_Invariants (Ftype)
@@ -11762,6 +11822,8 @@
                          (New_Occurrence_Of (Formal, Loc)));
                   end if;
 
+                  Check_Access_Invariants (Formal);
+
                   if Present (Predicate_Function (Ftype)) then
                      Append_To (Plist,
                        Make_Predicate_Check
Index: exp_ch3.adb
===================================================================
--- exp_ch3.adb (revision 191914)
+++ exp_ch3.adb (working copy)
@@ -3674,20 +3674,43 @@
       return Node_Id
       is
          Sel_Comp : Node_Id;
+         Typ      : Entity_Id;
+         Call     : Node_Id;
 
       begin
          Invariant_Found := True;
+         Typ := Etype (Comp);
+
          Sel_Comp :=
            Make_Selected_Component (Loc,
              Prefix      => New_Occurrence_Of (Object_Entity, Loc),
              Selector_Name => New_Occurrence_Of (Comp, Loc));
 
-         return
+         if Is_Access_Type (Typ) then
+            Sel_Comp := Make_Explicit_Dereference (Loc, Sel_Comp);
+            Typ := Designated_Type (Typ);
+         end if;
+
+         Call :=
            Make_Procedure_Call_Statement (Loc,
              Name                   =>
-               New_Occurrence_Of
-                 (Invariant_Procedure (Etype (Comp)), Loc),
+               New_Occurrence_Of (Invariant_Procedure (Typ), Loc),
              Parameter_Associations => New_List (Sel_Comp));
+
+         if Is_Access_Type (Etype (Comp)) then
+            Call :=
+              Make_If_Statement (Loc,
+                Condition =>
+                  Make_Op_Ne (Loc,
+                    Left_Opnd   => Make_Null (Loc),
+                    Right_Opnd  =>
+                       Make_Selected_Component (Loc,
+                         Prefix      => New_Occurrence_Of (Object_Entity, Loc),
+                         Selector_Name => New_Occurrence_Of (Comp, Loc))),
+                Then_Statements => New_List (Call));
+         end if;
+
+         return Call;
       end Build_Component_Invariant_Call;
 
       ----------------------------
@@ -3706,8 +3729,17 @@
             if Nkind (Decl) = N_Component_Declaration then
                Id  := Defining_Identifier (Decl);
 
-               if Has_Invariants (Etype (Id)) then
+               if Has_Invariants (Etype (Id))
+                 and then In_Open_Scopes (Scope (R_Type))
+               then
                   Append_To (Stmts, Build_Component_Invariant_Call (Id));
+
+               elsif Is_Access_Type (Etype (Id))
+                 and then not Is_Access_Constant (Etype (Id))
+                 and then Has_Invariants (Designated_Type (Etype (Id)))
+                 and then In_Open_Scopes (Scope (Designated_Type (Etype (Id))))
+               then
+                  Append_To (Stmts, Build_Component_Invariant_Call (Id));
                end if;
             end if;
 
@@ -5861,10 +5893,15 @@
          Build_Array_Init_Proc (Base, N);
       end if;
 
-      if Has_Invariants (Component_Type (Base)) then
+      if Has_Invariants (Component_Type (Base))
+        and then In_Open_Scopes (Scope (Component_Type (Base)))
+      then
+         --  Generate component invariant checking procedure. This is only
+         --  relevant if the array type is within the scope of the component
+         --  type. Otherwise an array object can only be built using the public
+         --  subprograms for the component type, and calls to those will have
+         --  invariant checks.
 
-         --  Generate component invariant checking procedure.
-
          Insert_Component_Invariant_Checks
            (N, Base, Build_Array_Invariant_Proc (Base, N));
       end if;

Reply via email to