RM 6.1.1 (26/3) specifies that if the prefix of 'Old wihin a postcondition is
potentially unevaluated, as in the right-hand side of a short-circuit operation
then the prefix can only be an entity name.

Compiling main.adb must yield:

    p.ads:5:44: prefix that is potentially unevaluated must denote an entity

---
with P; use P;
procedure Main is
   A : My_Array := (1, 2, 3);
   V : Integer;
begin
   Extract (A, 6, V);
end Main;
---
package P is
   type My_Array is array (Natural range <>) of Integer;
   
   procedure Extract (A : in out My_Array; J : Integer; V : out Integer) with
     Post => (if J in A'Range then V = A(J)'Old and A(J) = 0);  --  INCORRECT

   procedure Check (A : in out My_Array; J : Integer; V : out Integer) with
     Post => (if A(A'First)'Old > A(A'Last)'Old then True
        else raise program_error);  -- OK
end P;

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

2014-01-23  Ed Schonberg  <schonb...@adacore.com>

        * sem_util.ads, sem_util.adb (Is_Potentially_Unevaluated): new
        predicate to implement rule given in 6.1.1 (20/3).
        * sem_attr.adb (Analyze_Attribute, case 'Old): Reject prefix of
        'Old in a postcondition, if it is potentially unevaluated and
        it is not an entity name.

Index: sem_util.adb
===================================================================
--- sem_util.adb        (revision 206980)
+++ sem_util.adb        (working copy)
@@ -10249,6 +10249,48 @@
       end if;
    end Is_Partially_Initialized_Type;
 
+   --------------------------------
+   -- Is_Potentially_Unevaluated --
+   --------------------------------
+
+   function Is_Potentially_Unevaluated (N : Node_Id) return Boolean is
+      Par  : Node_Id;
+      Expr : Node_Id;
+
+   begin
+      Expr := N;
+      Par  := Parent (N);
+      while not Nkind_In (Par, N_If_Expression,
+                                N_Case_Expression,
+                                N_And_Then,
+                                N_Or_Else,
+                                N_In,
+                                N_Not_In)
+      loop
+         Expr := Par;
+         Par  := Parent (Par);
+         if Nkind (Par) not in N_Subexpr then
+            return False;
+         end if;
+      end loop;
+
+      if Nkind (Par) = N_If_Expression then
+         return Is_Elsif (Par) or else Expr /= First (Expressions (Par));
+
+      elsif Nkind (Par) = N_Case_Expression then
+         return Expr /= Expression (Par);
+
+      elsif Nkind_In (Par, N_And_Then, N_Or_Else) then
+         return Expr = Right_Opnd (Par);
+
+      elsif Nkind_In (Par, N_In, N_Not_In) then
+         return Expr /= Left_Opnd (Par);
+
+      else
+         return False;
+      end if;
+   end Is_Potentially_Unevaluated;
+
    ------------------------------------
    -- Is_Potentially_Persistent_Type --
    ------------------------------------
Index: sem_util.ads
===================================================================
--- sem_util.ads        (revision 206918)
+++ sem_util.ads        (working copy)
@@ -1116,6 +1116,9 @@
    --  if Include_Implicit is False, these cases do not count as making the
    --  type be partially initialized.
 
+   function Is_Potentially_Unevaluated (N : Node_Id) return Boolean;
+   --  Predicate to implement definition given in RM 6.1.1 (20/3)
+
    function Is_Potentially_Persistent_Type (T : Entity_Id) return Boolean;
    --  Determines if type T is a potentially persistent type. A potentially
    --  persistent type is defined (recursively) as a scalar type, a non-tagged
Index: sem_attr.adb
===================================================================
--- sem_attr.adb        (revision 206918)
+++ sem_attr.adb        (working copy)
@@ -4337,6 +4337,8 @@
          --  During pre-analysis, Prag is the enclosing pragma node if any
 
       begin
+         Prag := Empty;
+
          --  Find enclosing scopes, excluding loops
 
          CS := Current_Scope;
@@ -4515,6 +4517,18 @@
               ("??attribute Old applied to constant has no effect", P);
          end if;
 
+         --  Check that the prefix of 'Old is an entity, when it appears in
+         --  a postcondition and may be potentially unevaluated (6.1.1 (27/3)).
+
+         if Present (Prag)
+           and then Get_Pragma_Id (Prag) = Pragma_Postcondition
+           and then Is_Potentially_Unevaluated (N)
+           and then not Is_Entity_Name (P)
+         then
+            Error_Msg_N ("prefix that is potentially unevaluated must "
+               & "denote an entity", N);
+         end if;
+
          --  The attribute appears within a pre/postcondition, but refers to
          --  an entity in the enclosing subprogram. If it is a component of
          --  a formal its expansion might generate actual subtypes that may

Reply via email to