A traversal function, especially when implemented as an expression
function, may need to return an if-expression or case-expression, while
still respecting Legality Rule SPARK RM 3.10(5). This case is now
allowed in GNATprove.

There is no impact on compilation.

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

2019-07-22  Yannick Moy  <m...@adacore.com>

gcc/ada/

        * sem_spark.adb (Get_Root_Object, Is_Path_Expression,
        Is_Subpath_Expression): Add parameter Is_Traversal to adapt
        these functions to the case of paths returned from a traversal
        function.
        (Read_Indexes): Handle the case of an if-expression or
        case-expression.
        (Check_Statement): Check Emit_Messages only when issuing an
        error message. This is important as Emit_Messages may store the
        information that an error was detected.
--- gcc/ada/sem_spark.adb
+++ gcc/ada/sem_spark.adb
@@ -715,10 +715,14 @@ package body Sem_SPARK is
 
    function Get_Root_Object
      (Expr              : Node_Id;
-      Through_Traversal : Boolean := True) return Entity_Id;
+      Through_Traversal : Boolean := True;
+      Is_Traversal      : Boolean := False) return Entity_Id;
    --  Return the root of the path expression Expr, or Empty for an allocator,
    --  NULL, or a function call. Through_Traversal is True if it should follow
-   --  through calls to traversal functions.
+   --  through calls to traversal functions. Is_Traversal is True if this
+   --  corresponds to a value returned from a traversal function, which should
+   --  allow if-expressions and case-expressions that refer to the same root,
+   --  even if the paths are not the same in all branches.
 
    generic
       with procedure Handle_Parameter_Or_Global
@@ -745,11 +749,19 @@ package body Sem_SPARK is
    --  A procedure that is called when deep globals or aliased globals are used
    --  without any global aspect.
 
-   function Is_Path_Expression (Expr : Node_Id) return Boolean;
-   --  Return whether Expr corresponds to a path
+   function Is_Path_Expression
+     (Expr         : Node_Id;
+      Is_Traversal : Boolean := False) return Boolean;
+   --  Return whether Expr corresponds to a path. Is_Traversal is True if this
+   --  corresponds to a value returned from a traversal function, which should
+   --  allow if-expressions and case-expressions.
 
-   function Is_Subpath_Expression (Expr : Node_Id) return Boolean;
-   --  Return True if Expr can be part of a path expression
+   function Is_Subpath_Expression
+     (Expr         : Node_Id;
+      Is_Traversal : Boolean := False) return Boolean;
+   --  Return True if Expr can be part of a path expression. Is_Traversal is
+   --  True if this corresponds to a value returned from a traversal function,
+   --  which should allow if-expressions and case-expressions.
 
    function Is_Prefix_Or_Almost (Pref, Expr : Node_Id) return Boolean;
    --  Determine if the candidate Prefix is indeed a prefix of Expr, or almost
@@ -1749,6 +1761,31 @@ package body Sem_SPARK is
                   end loop;
                end;
 
+            when N_If_Expression =>
+               declare
+                  Cond      : constant Node_Id := First (Expressions (Expr));
+                  Then_Part : constant Node_Id := Next (Cond);
+                  Else_Part : constant Node_Id := Next (Then_Part);
+               begin
+                  Read_Expression (Cond);
+                  Read_Indexes (Then_Part);
+                  Read_Indexes (Else_Part);
+               end;
+
+            when N_Case_Expression =>
+               declare
+                  Cases    : constant List_Id := Alternatives (Expr);
+                  Cur_Case : Node_Id := First (Cases);
+
+               begin
+                  Read_Expression (Expression (Expr));
+
+                  while Present (Cur_Case) loop
+                     Read_Indexes (Expression (Cur_Case));
+                     Next (Cur_Case);
+                  end loop;
+               end;
+
             when N_Attribute_Reference =>
                pragma Assert
                  (Get_Attribute_Id (Attribute_Name (Expr)) =
@@ -3115,14 +3152,14 @@ package body Sem_SPARK is
                      if Is_Anonymous_Access_Type (Return_Typ) then
                         pragma Assert (Is_Traversal_Function (Subp));
 
-                        if Nkind (Expr) /= N_Null and then Emit_Messages then
+                        if Nkind (Expr) /= N_Null then
                            declare
                               Expr_Root : constant Entity_Id :=
-                                Get_Root_Object (Expr);
+                                Get_Root_Object (Expr, Is_Traversal => True);
                               Param     : constant Entity_Id :=
                                 First_Formal (Subp);
                            begin
-                              if Param /= Expr_Root then
+                              if Param /= Expr_Root and then Emit_Messages then
                                  Error_Msg_NE
                                    ("returned value must be rooted in "
                                     & "traversed parameter & "
@@ -3642,10 +3679,31 @@ package body Sem_SPARK is
 
    function Get_Root_Object
      (Expr              : Node_Id;
-      Through_Traversal : Boolean := True) return Entity_Id
+      Through_Traversal : Boolean := True;
+      Is_Traversal      : Boolean := False) return Entity_Id
    is
+      function GRO (Expr : Node_Id) return Entity_Id;
+      --  Local wrapper on the actual function, to propagate the values of
+      --  optional parameters.
+
+      ---------
+      -- GRO --
+      ---------
+
+      function GRO (Expr : Node_Id) return Entity_Id is
+      begin
+         return Get_Root_Object (Expr, Through_Traversal, Is_Traversal);
+      end GRO;
+
+      Get_Root_Object : Boolean;
+      pragma Unmodified (Get_Root_Object);
+      --  Local variable to mask the name of function Get_Root_Object, to
+      --  prevent direct call. Instead GRO wrapper should be called.
+
+   --  Start of processing for Get_Root_Object
+
    begin
-      if not Is_Subpath_Expression (Expr) then
+      if not Is_Subpath_Expression (Expr, Is_Traversal) then
          if Emit_Messages then
             Error_Msg_N ("name expected here for path", Expr);
          end if;
@@ -3663,7 +3721,7 @@ package body Sem_SPARK is
             | N_Selected_Component
             | N_Slice
          =>
-            return Get_Root_Object (Prefix (Expr), Through_Traversal);
+            return GRO (Prefix (Expr));
 
          --  There is no root object for an (extension) aggregate, allocator,
          --  concat, or NULL.
@@ -3684,7 +3742,7 @@ package body Sem_SPARK is
             if Through_Traversal
               and then Is_Traversal_Function_Call (Expr)
             then
-               return Get_Root_Object (First_Actual (Expr), Through_Traversal);
+               return GRO (First_Actual (Expr));
             else
                return Empty;
             end if;
@@ -3693,7 +3751,7 @@ package body Sem_SPARK is
             | N_Type_Conversion
             | N_Unchecked_Type_Conversion
          =>
-            return Get_Root_Object (Expression (Expr), Through_Traversal);
+            return GRO (Expression (Expr));
 
          when N_Attribute_Reference =>
             pragma Assert
@@ -3706,6 +3764,69 @@ package body Sem_SPARK is
                  Attribute_Image);
             return Empty;
 
+         when N_If_Expression =>
+            if Is_Traversal then
+               declare
+                  Cond      : constant Node_Id := First (Expressions (Expr));
+                  Then_Part : constant Node_Id := Next (Cond);
+                  Else_Part : constant Node_Id := Next (Then_Part);
+                  Then_Root : constant Entity_Id := GRO (Then_Part);
+                  Else_Root : constant Entity_Id := GRO (Else_Part);
+               begin
+                  if Nkind (Then_Part) = N_Null then
+                     return Else_Root;
+                  elsif Nkind (Else_Part) = N_Null then
+                     return Then_Part;
+                  elsif Then_Root = Else_Root then
+                     return Then_Root;
+                  else
+                     if Emit_Messages then
+                        Error_Msg_N
+                          ("same name expected here in each branch", Expr);
+                     end if;
+                     return Empty;
+                  end if;
+               end;
+            else
+               if Emit_Messages then
+                  Error_Msg_N ("name expected here for path", Expr);
+               end if;
+               return Empty;
+            end if;
+
+         when N_Case_Expression =>
+            if Is_Traversal then
+               declare
+                  Cases       : constant List_Id := Alternatives (Expr);
+                  Cur_Case    : Node_Id := First (Cases);
+                  Cur_Root    : Entity_Id;
+                  Common_Root : Entity_Id := Empty;
+
+               begin
+                  while Present (Cur_Case) loop
+                     Cur_Root := GRO (Expression (Cur_Case));
+
+                     if Common_Root = Empty then
+                        Common_Root := Cur_Root;
+                     elsif Common_Root /= Cur_Root then
+                        if Emit_Messages then
+                           Error_Msg_N
+                             ("same name expected here in each branch", Expr);
+                        end if;
+                        return Empty;
+                     end if;
+                     Next (Cur_Case);
+                  end loop;
+
+                  return Common_Root;
+               end;
+            else
+               if Emit_Messages then
+                  Error_Msg_N ("name expected here for path", Expr);
+               end if;
+               return Empty;
+            end if;
+
          when others =>
             raise Program_Error;
       end case;
@@ -3876,7 +3997,30 @@ package body Sem_SPARK is
    -- Is_Path_Expression --
    ------------------------
 
-   function Is_Path_Expression (Expr : Node_Id) return Boolean is
+   function Is_Path_Expression
+     (Expr         : Node_Id;
+      Is_Traversal : Boolean := False) return Boolean
+   is
+      function IPE (Expr : Node_Id) return Boolean;
+      --  Local wrapper on the actual function, to propagate the values of
+      --  optional parameter Is_Traversal.
+
+      ---------
+      -- IPE --
+      ---------
+
+      function IPE (Expr : Node_Id) return Boolean is
+      begin
+         return Is_Path_Expression (Expr, Is_Traversal);
+      end IPE;
+
+      Is_Path_Expression : Boolean;
+      pragma Unmodified (Is_Path_Expression);
+      --  Local variable to mask the name of function Is_Path_Expression, to
+      --  prevent direct call. Instead IPE wrapper should be called.
+
+   --  Start of processing for Is_Path_Expression
+
    begin
       case Nkind (Expr) is
          when N_Expanded_Name
@@ -3907,7 +4051,47 @@ package body Sem_SPARK is
             | N_Type_Conversion
             | N_Unchecked_Type_Conversion
          =>
-            return Is_Path_Expression (Expression (Expr));
+            return IPE (Expression (Expr));
+
+         --  When returning from a traversal function, consider an
+         --  if-expression as a possible path expression.
+
+         when N_If_Expression =>
+            if Is_Traversal then
+               declare
+                  Cond      : constant Node_Id := First (Expressions (Expr));
+                  Then_Part : constant Node_Id := Next (Cond);
+                  Else_Part : constant Node_Id := Next (Then_Part);
+               begin
+                  return IPE (Then_Part)
+                    and then IPE (Else_Part);
+               end;
+            else
+               return False;
+            end if;
+
+         --  When returning from a traversal function, consider
+         --  a case-expression as a possible path expression.
+
+         when N_Case_Expression =>
+            if Is_Traversal then
+               declare
+                  Cases    : constant List_Id := Alternatives (Expr);
+                  Cur_Case : Node_Id := First (Cases);
+
+               begin
+                  while Present (Cur_Case) loop
+                     if not IPE (Expression (Cur_Case)) then
+                        return False;
+                     end if;
+                     Next (Cur_Case);
+                  end loop;
+
+                  return True;
+               end;
+            else
+               return False;
+            end if;
 
          when others =>
             return False;
@@ -4033,9 +4217,12 @@ package body Sem_SPARK is
    -- Is_Subpath_Expression --
    ---------------------------
 
-   function Is_Subpath_Expression (Expr : Node_Id) return Boolean is
+   function Is_Subpath_Expression
+     (Expr         : Node_Id;
+      Is_Traversal : Boolean := False) return Boolean
+   is
    begin
-      return Is_Path_Expression (Expr)
+      return Is_Path_Expression (Expr, Is_Traversal)
         or else (Nkind (Expr) = N_Attribute_Reference
                   and then
                     (Get_Attribute_Id (Attribute_Name (Expr)) =

Reply via email to