[Ada] Spurious crash on expression function as completion with contracts
authorEd Schonberg <schonberg@adacore.com>
Tue, 21 Aug 2018 14:49:34 +0000 (14:49 +0000)
committerPierre-Marie de Rodat <pmderodat@gcc.gnu.org>
Tue, 21 Aug 2018 14:49:34 +0000 (14:49 +0000)
This patch fixes a compiler abort on an expression function that is a
completion of a subprogram with preconditions. The problem is caused by
the presence of types in the precondition that are not frozen when the
subprogram body constructed for the expression function receives the
code that enforces the precondition. These types must be frozen before
the contract is expanded, so the freeze nodes for these types appear in
the proper scope. This is analogous to what is done with type references
that appear in the original expression of the expression function.

2018-08-21  Ed Schonberg  <schonberg@adacore.com>

gcc/ada/

* sem_ch6.adb: Remove Freeze_Expr_Types.
* freeze.ads, freeze.adb (Freeze_Expr_Types): Moved from
sem_ch6.adb, and extended to handle other expressions that may
contain unfrozen types that must be frozen in their proper
scopes.
* contracts.adb (Analyze_Entry_Or_Subprogram_Contract): If the
contract is for the generated body of an expression function
that is a completion, traverse the expressions for pre- and
postconditions to freeze all types before adding the contract
code within the subprogram body.

gcc/testsuite/

* gnat.dg/expr_func6.adb, gnat.dg/expr_func6.ads: New testcase.

From-SVN: r263734

gcc/ada/ChangeLog
gcc/ada/contracts.adb
gcc/ada/freeze.adb
gcc/ada/freeze.ads
gcc/ada/sem_ch6.adb
gcc/testsuite/ChangeLog
gcc/testsuite/gnat.dg/expr_func6.adb [new file with mode: 0644]
gcc/testsuite/gnat.dg/expr_func6.ads [new file with mode: 0644]

index 373a648b98634cab97ccc41c2f5b473c85b5a801..ca38083e42c774f7313522803e60ddd07f731723 100644 (file)
@@ -1,3 +1,16 @@
+2018-08-21  Ed Schonberg  <schonberg@adacore.com>
+
+       * sem_ch6.adb: Remove Freeze_Expr_Types.
+       * freeze.ads, freeze.adb (Freeze_Expr_Types): Moved from
+       sem_ch6.adb, and extended to handle other expressions that may
+       contain unfrozen types that must be frozen in their proper
+       scopes.
+       * contracts.adb (Analyze_Entry_Or_Subprogram_Contract): If the
+       contract is for the generated body of an expression function
+       that is a completion, traverse the expressions for pre- and
+       postconditions to freeze all types before adding the contract
+       code within the subprogram body.
+
 2018-08-21  Hristian Kirtchev  <kirtchev@adacore.com>
 
        * sem_ch10.adb: Remove the with and use clause for unit Ghost.
index 5577604a6bf4cbf87916513a938689bb2e44aaf5..e70765a06e8136df7e8e66829fad78effd83bfd3 100644 (file)
@@ -32,6 +32,7 @@ with Errout;   use Errout;
 with Exp_Prag; use Exp_Prag;
 with Exp_Tss;  use Exp_Tss;
 with Exp_Util; use Exp_Util;
+with Freeze;   use Freeze;
 with Namet;    use Namet;
 with Nlists;   use Nlists;
 with Nmake;    use Nmake;
@@ -47,6 +48,7 @@ with Sem_Prag; use Sem_Prag;
 with Sem_Util; use Sem_Util;
 with Sinfo;    use Sinfo;
 with Snames;   use Snames;
+with Stand;    use Stand;
 with Stringt;  use Stringt;
 with SCIL_LL;  use SCIL_LL;
 with Tbuild;   use Tbuild;
@@ -589,14 +591,40 @@ package body Contracts is
          if Skip_Assert_Exprs then
             null;
 
-         --  Otherwise analyze the pre/postconditions
+         --  Otherwise analyze the pre/postconditions. Their expressions
+         --  might include references to types that are not frozen yet,
+         --  in the case where the body is a rewritten expression function
+         --  that is a completion, so freeze all types within before
+         --  constructing the contract code.
 
          else
-            Prag := Pre_Post_Conditions (Items);
-            while Present (Prag) loop
-               Analyze_Pre_Post_Condition_In_Decl_Part (Prag, Freeze_Id);
-               Prag := Next_Pragma (Prag);
-            end loop;
+            declare
+               Bod : Node_Id;
+               Freeze_Types : Boolean := False;
+            begin
+               if Present (Freeze_Id) then
+                  Bod := Unit_Declaration_Node (Freeze_Id);
+                  if Nkind (Bod) = N_Subprogram_Body
+                    and then Was_Expression_Function (Bod)
+                    and then Ekind (Subp_Id) = E_Function
+                    and then Chars (Subp_Id) = Chars (Freeze_Id)
+                    and then Subp_Id /= Freeze_Id
+                  then
+                     Freeze_Types := True;
+                  end if;
+               end if;
+
+               Prag := Pre_Post_Conditions (Items);
+               while Present (Prag) loop
+                  if Freeze_Types then
+                     Freeze_Expr_Types (Subp_Id, Standard_Boolean,
+                       Expression (Corresponding_Aspect (Prag)), Bod);
+                  end if;
+
+                  Analyze_Pre_Post_Condition_In_Decl_Part (Prag, Freeze_Id);
+                  Prag := Next_Pragma (Prag);
+               end loop;
+            end;
          end if;
 
          --  Analyze contract-cases and test-cases
index ea9454a98151bd5980dbd032abcbad3530f0934c..d7f3f58e33cfcbd0d3d25a9f9f7697fe98b2e58a 100644 (file)
@@ -49,6 +49,7 @@ with Rtsfind;   use Rtsfind;
 with Sem;       use Sem;
 with Sem_Aux;   use Sem_Aux;
 with Sem_Cat;   use Sem_Cat;
+with Sem_Ch3;   use Sem_Ch3;
 with Sem_Ch6;   use Sem_Ch6;
 with Sem_Ch7;   use Sem_Ch7;
 with Sem_Ch8;   use Sem_Ch8;
@@ -7643,6 +7644,209 @@ package body Freeze is
       In_Spec_Expression := In_Spec_Exp;
    end Freeze_Expression;
 
+   -----------------------
+   -- Freeze_Expr_Types --
+   -----------------------
+
+   procedure Freeze_Expr_Types
+     (Def_Id : Entity_Id;
+      Typ    : Entity_Id;
+      Expr   : Node_Id;
+      N      : Node_Id)
+   is
+
+      function Cloned_Expression return Node_Id;
+      --  Build a duplicate of the expression of the return statement that
+      --  has no defining entities shared with the original expression.
+
+      function Freeze_Type_Refs (Node : Node_Id) return Traverse_Result;
+      --  Freeze all types referenced in the subtree rooted at Node
+
+      -----------------------
+      -- Cloned_Expression --
+      -----------------------
+
+      function Cloned_Expression return Node_Id is
+         function Clone_Id (Node : Node_Id) return Traverse_Result;
+         --  Tree traversal routine that clones the defining identifier of
+         --  iterator and loop parameter specification nodes.
+
+         --------------
+         -- Clone_Id --
+         --------------
+
+         function Clone_Id (Node : Node_Id) return Traverse_Result is
+         begin
+            if Nkind_In (Node, N_Iterator_Specification,
+                               N_Loop_Parameter_Specification)
+            then
+               Set_Defining_Identifier (Node,
+                 New_Copy (Defining_Identifier (Node)));
+            end if;
+
+            return OK;
+         end Clone_Id;
+
+         procedure Clone_Def_Ids is new Traverse_Proc (Clone_Id);
+
+         --  Local variable
+
+         Dup_Expr : constant Node_Id := New_Copy_Tree (Expr);
+
+      --  Start of processing for Cloned_Expression
+
+      begin
+         --  We must duplicate the expression with semantic information to
+         --  inherit the decoration of global entities in generic instances.
+         --  Set the parent of the new node to be the parent of the original
+         --  to get the proper context, which is needed for complete error
+         --  reporting and for semantic analysis.
+
+         Set_Parent (Dup_Expr, Parent (Expr));
+
+         --  Replace the defining identifier of iterators and loop param
+         --  specifications by a clone to ensure that the cloned expression
+         --  and the original expression don't have shared identifiers;
+         --  otherwise, as part of the preanalysis of the expression, these
+         --  shared identifiers may be left decorated with itypes which
+         --  will not be available in the tree passed to the backend.
+
+         Clone_Def_Ids (Dup_Expr);
+
+         return Dup_Expr;
+      end Cloned_Expression;
+
+      ----------------------
+      -- Freeze_Type_Refs --
+      ----------------------
+
+      function Freeze_Type_Refs (Node : Node_Id) return Traverse_Result is
+         procedure Check_And_Freeze_Type (Typ : Entity_Id);
+         --  Check that Typ is fully declared and freeze it if so
+
+         ---------------------------
+         -- Check_And_Freeze_Type --
+         ---------------------------
+
+         procedure Check_And_Freeze_Type (Typ : Entity_Id) is
+         begin
+            --  Skip Itypes created by the preanalysis, and itypes whose
+            --  scope is another type (i.e. component subtypes that depend
+            --  on a discriminant),
+
+            if Is_Itype (Typ)
+              and then (Scope_Within_Or_Same (Scope (Typ), Def_Id)
+                         or else Is_Type (Scope (Typ)))
+            then
+               return;
+            end if;
+
+            --  This provides a better error message than generating
+            --  primitives whose compilation fails much later. Refine
+            --  the error message if possible.
+
+            Check_Fully_Declared (Typ, Node);
+
+            if Error_Posted (Node) then
+               if Has_Private_Component (Typ)
+                 and then not Is_Private_Type (Typ)
+               then
+                  Error_Msg_NE ("\type& has private component", Node, Typ);
+               end if;
+
+            else
+               Freeze_Before (N, Typ);
+            end if;
+         end Check_And_Freeze_Type;
+
+      --  Start of processing for Freeze_Type_Refs
+
+      begin
+         --  Check that a type referenced by an entity can be frozen
+
+         if Is_Entity_Name (Node) and then Present (Entity (Node)) then
+            Check_And_Freeze_Type (Etype (Entity (Node)));
+
+            --  Check that the enclosing record type can be frozen
+
+            if Ekind_In (Entity (Node), E_Component, E_Discriminant) then
+               Check_And_Freeze_Type (Scope (Entity (Node)));
+            end if;
+
+         --  Freezing an access type does not freeze the designated type,
+         --  but freezing conversions between access to interfaces requires
+         --  that the interface types themselves be frozen, so that dispatch
+         --  table entities are properly created.
+
+         --  Unclear whether a more general rule is needed ???
+
+         elsif Nkind (Node) = N_Type_Conversion
+           and then Is_Access_Type (Etype (Node))
+           and then Is_Interface (Designated_Type (Etype (Node)))
+         then
+            Check_And_Freeze_Type (Designated_Type (Etype (Node)));
+         end if;
+
+         --  An implicit dereference freezes the designated type. In the
+         --  case of a dispatching call whose controlling argument is an
+         --  access type, the dereference is not made explicit, so we must
+         --  check for such a call and freeze the designated type.
+
+         if Nkind (Node) in N_Has_Etype
+           and then Present (Etype (Node))
+           and then Is_Access_Type (Etype (Node))
+           and then Nkind (Parent (Node)) = N_Function_Call
+           and then Node = Controlling_Argument (Parent (Node))
+         then
+            Check_And_Freeze_Type (Designated_Type (Etype (Node)));
+         end if;
+
+         --  No point in posting several errors on the same expression
+
+         if Serious_Errors_Detected > 0 then
+            return Abandon;
+         else
+            return OK;
+         end if;
+      end Freeze_Type_Refs;
+
+      procedure Freeze_References is new Traverse_Proc (Freeze_Type_Refs);
+
+      --  Local variables
+
+      Saved_First_Entity : constant Entity_Id := First_Entity (Def_Id);
+      Saved_Last_Entity  : constant Entity_Id := Last_Entity  (Def_Id);
+      Dup_Expr           : constant Node_Id   := Cloned_Expression;
+
+   --  Start of processing for Freeze_Expr_Types
+
+   begin
+      --  Preanalyze a duplicate of the expression to have available the
+      --  minimum decoration needed to locate referenced unfrozen types
+      --  without adding any decoration to the function expression.
+
+      Push_Scope (Def_Id);
+      Install_Formals (Def_Id);
+
+      Preanalyze_Spec_Expression (Dup_Expr, Typ);
+      End_Scope;
+
+      --  Restore certain attributes of Def_Id since the preanalysis may
+      --  have introduced itypes to this scope, thus modifying attributes
+      --  First_Entity and Last_Entity.
+
+      Set_First_Entity (Def_Id, Saved_First_Entity);
+      Set_Last_Entity  (Def_Id, Saved_Last_Entity);
+
+      if Present (Last_Entity (Def_Id)) then
+         Set_Next_Entity (Last_Entity (Def_Id), Empty);
+      end if;
+
+      --  Freeze all types referenced in the expression
+
+      Freeze_References (Dup_Expr);
+   end Freeze_Expr_Types;
+
    -----------------------------
    -- Freeze_Fixed_Point_Type --
    -----------------------------
index 20badd00fbb1e4e12b6b4ef9b354b4fc8256de76..96b3c90b59a425004d0cb4ac17789ba8a3142698 100644 (file)
@@ -230,6 +230,17 @@ package Freeze is
    --  so need to be similarly treated. Freeze_Expression takes care of
    --  determining the proper insertion point for generated freeze actions.
 
+   procedure Freeze_Expr_Types
+     (Def_Id : Entity_Id;
+      Typ    : Entity_Id;
+      Expr   : Node_Id;
+      N      : Node_Id);
+   --  N is the body constructed for an expression function that is a
+   --  completion, and Def_Id is the function being completed.
+   --  This procedure freezes before N all the types referenced in Expr,
+   --  which is either the expression of the expression function, or
+   --  the expression in a pre/post aspect that applies to Def_Id;
+
    procedure Freeze_Fixed_Point_Type (Typ : Entity_Id);
    --  Freeze fixed point type. For fixed-point types, we have to defer
    --  setting the size and bounds till the freeze point, since they are
index 2ddd3d35767f69470dc2e4050ec428613d4e352b..5548c81c5574cf08613541903eb65522f3ed2700 100644 (file)
@@ -285,208 +285,6 @@ package body Sem_Ch6 is
       LocX : constant Source_Ptr := Sloc (Expr);
       Spec : constant Node_Id    := Specification (N);
 
-      procedure Freeze_Expr_Types (Def_Id : Entity_Id);
-      --  N is an expression function that is a completion and Def_Id its
-      --  defining entity. Freeze before N all the types referenced by the
-      --  expression of the function.
-
-      -----------------------
-      -- Freeze_Expr_Types --
-      -----------------------
-
-      procedure Freeze_Expr_Types (Def_Id : Entity_Id) is
-         function Cloned_Expression return Node_Id;
-         --  Build a duplicate of the expression of the return statement that
-         --  has no defining entities shared with the original expression.
-
-         function Freeze_Type_Refs (Node : Node_Id) return Traverse_Result;
-         --  Freeze all types referenced in the subtree rooted at Node
-
-         -----------------------
-         -- Cloned_Expression --
-         -----------------------
-
-         function Cloned_Expression return Node_Id is
-            function Clone_Id (Node : Node_Id) return Traverse_Result;
-            --  Tree traversal routine that clones the defining identifier of
-            --  iterator and loop parameter specification nodes.
-
-            --------------
-            -- Clone_Id --
-            --------------
-
-            function Clone_Id (Node : Node_Id) return Traverse_Result is
-            begin
-               if Nkind_In (Node, N_Iterator_Specification,
-                                  N_Loop_Parameter_Specification)
-               then
-                  Set_Defining_Identifier (Node,
-                    New_Copy (Defining_Identifier (Node)));
-               end if;
-
-               return OK;
-            end Clone_Id;
-
-            procedure Clone_Def_Ids is new Traverse_Proc (Clone_Id);
-
-            --  Local variable
-
-            Dup_Expr : constant Node_Id := New_Copy_Tree (Expr);
-
-         --  Start of processing for Cloned_Expression
-
-         begin
-            --  We must duplicate the expression with semantic information to
-            --  inherit the decoration of global entities in generic instances.
-            --  Set the parent of the new node to be the parent of the original
-            --  to get the proper context, which is needed for complete error
-            --  reporting and for semantic analysis.
-
-            Set_Parent (Dup_Expr, Parent (Expr));
-
-            --  Replace the defining identifier of iterators and loop param
-            --  specifications by a clone to ensure that the cloned expression
-            --  and the original expression don't have shared identifiers;
-            --  otherwise, as part of the preanalysis of the expression, these
-            --  shared identifiers may be left decorated with itypes which
-            --  will not be available in the tree passed to the backend.
-
-            Clone_Def_Ids (Dup_Expr);
-
-            return Dup_Expr;
-         end Cloned_Expression;
-
-         ----------------------
-         -- Freeze_Type_Refs --
-         ----------------------
-
-         function Freeze_Type_Refs (Node : Node_Id) return Traverse_Result is
-            procedure Check_And_Freeze_Type (Typ : Entity_Id);
-            --  Check that Typ is fully declared and freeze it if so
-
-            ---------------------------
-            -- Check_And_Freeze_Type --
-            ---------------------------
-
-            procedure Check_And_Freeze_Type (Typ : Entity_Id) is
-            begin
-               --  Skip Itypes created by the preanalysis, and itypes whose
-               --  scope is another type (i.e. component subtypes that depend
-               --  on a discriminant),
-
-               if Is_Itype (Typ)
-                 and then (Scope_Within_Or_Same (Scope (Typ), Def_Id)
-                            or else Is_Type (Scope (Typ)))
-               then
-                  return;
-               end if;
-
-               --  This provides a better error message than generating
-               --  primitives whose compilation fails much later. Refine
-               --  the error message if possible.
-
-               Check_Fully_Declared (Typ, Node);
-
-               if Error_Posted (Node) then
-                  if Has_Private_Component (Typ)
-                    and then not Is_Private_Type (Typ)
-                  then
-                     Error_Msg_NE ("\type& has private component", Node, Typ);
-                  end if;
-
-               else
-                  Freeze_Before (N, Typ);
-               end if;
-            end Check_And_Freeze_Type;
-
-         --  Start of processing for Freeze_Type_Refs
-
-         begin
-            --  Check that a type referenced by an entity can be frozen
-
-            if Is_Entity_Name (Node) and then Present (Entity (Node)) then
-               Check_And_Freeze_Type (Etype (Entity (Node)));
-
-               --  Check that the enclosing record type can be frozen
-
-               if Ekind_In (Entity (Node), E_Component, E_Discriminant) then
-                  Check_And_Freeze_Type (Scope (Entity (Node)));
-               end if;
-
-            --  Freezing an access type does not freeze the designated type,
-            --  but freezing conversions between access to interfaces requires
-            --  that the interface types themselves be frozen, so that dispatch
-            --  table entities are properly created.
-
-            --  Unclear whether a more general rule is needed ???
-
-            elsif Nkind (Node) = N_Type_Conversion
-              and then Is_Access_Type (Etype (Node))
-              and then Is_Interface (Designated_Type (Etype (Node)))
-            then
-               Check_And_Freeze_Type (Designated_Type (Etype (Node)));
-            end if;
-
-            --  An implicit dereference freezes the designated type. In the
-            --  case of a dispatching call whose controlling argument is an
-            --  access type, the dereference is not made explicit, so we must
-            --  check for such a call and freeze the designated type.
-
-            if Nkind (Node) in N_Has_Etype
-              and then Present (Etype (Node))
-              and then Is_Access_Type (Etype (Node))
-              and then Nkind (Parent (Node)) = N_Function_Call
-              and then Node = Controlling_Argument (Parent (Node))
-            then
-               Check_And_Freeze_Type (Designated_Type (Etype (Node)));
-            end if;
-
-            --  No point in posting several errors on the same expression
-
-            if Serious_Errors_Detected > 0 then
-               return Abandon;
-            else
-               return OK;
-            end if;
-         end Freeze_Type_Refs;
-
-         procedure Freeze_References is new Traverse_Proc (Freeze_Type_Refs);
-
-         --  Local variables
-
-         Saved_First_Entity : constant Entity_Id := First_Entity (Def_Id);
-         Saved_Last_Entity  : constant Entity_Id := Last_Entity  (Def_Id);
-         Dup_Expr           : constant Node_Id   := Cloned_Expression;
-
-      --  Start of processing for Freeze_Expr_Types
-
-      begin
-         --  Preanalyze a duplicate of the expression to have available the
-         --  minimum decoration needed to locate referenced unfrozen types
-         --  without adding any decoration to the function expression.
-
-         Push_Scope (Def_Id);
-         Install_Formals (Def_Id);
-
-         Preanalyze_Spec_Expression (Dup_Expr, Etype (Def_Id));
-         End_Scope;
-
-         --  Restore certain attributes of Def_Id since the preanalysis may
-         --  have introduced itypes to this scope, thus modifying attributes
-         --  First_Entity and Last_Entity.
-
-         Set_First_Entity (Def_Id, Saved_First_Entity);
-         Set_Last_Entity  (Def_Id, Saved_Last_Entity);
-
-         if Present (Last_Entity (Def_Id)) then
-            Set_Next_Entity (Last_Entity (Def_Id), Empty);
-         end if;
-
-         --  Freeze all types referenced in the expression
-
-         Freeze_References (Dup_Expr);
-      end Freeze_Expr_Types;
-
       --  Local variables
 
       Asp      : Node_Id;
@@ -600,7 +398,7 @@ package body Sem_Ch6 is
          --  As elsewhere, we do not emit freeze nodes within a generic unit.
 
          if not Inside_A_Generic then
-            Freeze_Expr_Types (Def_Id);
+            Freeze_Expr_Types (Def_Id, Etype (Def_Id), Expr, N);
          end if;
 
          --  For navigation purposes, indicate that the function is a body
index b4f0c41116db501d5fa16e24c8a8debb504d5aa6..444f3e852ca8a11978887e7f0148f60b7a560e04 100644 (file)
@@ -1,3 +1,7 @@
+2018-08-21  Ed Schonberg  <schonberg@adacore.com>
+
+       * gnat.dg/expr_func6.adb, gnat.dg/expr_func6.ads: New testcase.
+
 2018-08-21  Javier Miranda  <miranda@adacore.com>
 
        * gnat.dg/spark2.adb, gnat.dg/spark2.ads: New testcase.
diff --git a/gcc/testsuite/gnat.dg/expr_func6.adb b/gcc/testsuite/gnat.dg/expr_func6.adb
new file mode 100644 (file)
index 0000000..7223fa0
--- /dev/null
@@ -0,0 +1,7 @@
+--  { dg-do compile }
+
+package body Expr_Func6 is
+
+   procedure Dummy is null;
+
+end Expr_Func6;
diff --git a/gcc/testsuite/gnat.dg/expr_func6.ads b/gcc/testsuite/gnat.dg/expr_func6.ads
new file mode 100644 (file)
index 0000000..462bde1
--- /dev/null
@@ -0,0 +1,17 @@
+pragma Assertion_Policy (Check);
+
+package Expr_Func6 is
+
+   type Monolean is (Nottrue);
+
+   function Basic_Function return Monolean;
+   function Fancy_Function_With_Contract return Boolean
+     with Pre => Basic_Function = Nottrue;
+
+   function Fancy_Function_With_Contract return Boolean is (False);
+
+   function Basic_Function return Monolean is (Nottrue);
+
+   procedure Dummy;
+
+end Expr_Func6;