From: Eric Botcazou <[email protected]>

This streamlines the resolution of declare expressions in Ada 2022, which
started as a manual name resolution to avoid scope management issues, then
added local visibility support, and eventually added scope management with
a kludge for transient scopes.

Transient scopes are not really scopes but placeholders and, therefore, may
traverse regular scopes when the node they service is higher up.  This also
has uncovered a loophole in Find_Type_Of_Object in the preanalysis case.

gcc/ada/ChangeLog:

        * sem_ch3.adb (Find_Type_Of_Object): In the subtype indication case
        for a stand-alone object, invoke Process_Subtype during preanalysis
        instead of partially reimplementing it.
        * sem_ch5.ads (Has_Sec_Stack_Call): Move back declaration to...
        * sem_ch5.adb (Has_Sec_Stack_Call): ...here.
        * sem_ch4.adb (Analyze_Expression_With_Actions): Deal with transient
        scopes created during analysis.
        * sem_res.adb: Add clauses for Sem_Ch9 package.
        (Resolve_Declare_Expression): Streamline and deal with transient
        scopes created during resolution.

Tested on x86_64-pc-linux-gnu, committed on master.

---
 gcc/ada/sem_ch3.adb |  58 +++++-----------
 gcc/ada/sem_ch4.adb |  21 ++++++
 gcc/ada/sem_ch5.adb |  11 +++
 gcc/ada/sem_ch5.ads |  12 ----
 gcc/ada/sem_res.adb | 159 +++++++++-----------------------------------
 5 files changed, 79 insertions(+), 182 deletions(-)

diff --git a/gcc/ada/sem_ch3.adb b/gcc/ada/sem_ch3.adb
index f434ae3e92a..1b4873f52b0 100644
--- a/gcc/ada/sem_ch3.adb
+++ b/gcc/ada/sem_ch3.adb
@@ -19156,9 +19156,6 @@ package body Sem_Ch3 is
       elsif Nkind (P) /= N_Component_Declaration
         and then Def_Kind = N_Subtype_Indication
       then
-         --  Base name of subtype on object name, which will be unique in
-         --  the current scope.
-
          --  If this is a duplicate declaration, return base type, to avoid
          --  generating duplicate anonymous types.
 
@@ -19167,53 +19164,28 @@ package body Sem_Ch3 is
             return Entity (Subtype_Mark (Obj_Def));
          end if;
 
+         --  During preanalysis, for example within a pre/postcondition,
+         --  provide just enough information to use the subtype.
+
+         if Preanalysis_Active then
+            return
+              Process_Subtype
+                (Obj_Def,
+                 Related_Nod,
+                 Excludes_Null => Null_Exclusion_Present (P));
+         end if;
+
+         --  Base name of subtype on object name, which will be unique in
+         --  the current scope.
+
          Nam :=
             New_External_Name
              (Chars (Defining_Identifier (Related_Nod)), 'S', 0, 'T');
 
          T := Make_Defining_Identifier (Sloc (P), Nam);
 
-         --  If In_Spec_Expression, for example within a pre/postcondition,
-         --  provide enough information for use of the subtype without
-         --  depending on full analysis and freezing, which will happen when
-         --  building the corresponding subprogram.
-
-         if In_Spec_Expression then
-            Analyze (Subtype_Mark (Obj_Def));
-
-            declare
-               Base_T  : constant Entity_Id := Entity (Subtype_Mark (Obj_Def));
-               New_Def : constant Node_Id   := New_Copy_Tree (Obj_Def);
-               Decl    : constant Node_Id   :=
-                 Make_Subtype_Declaration (Sloc (P),
-                   Defining_Identifier => T,
-                   Subtype_Indication  => New_Def);
-
-            begin
-               Set_Etype  (T, Base_T);
-               Mutate_Ekind  (T, Subtype_Kind (Ekind (Base_T)));
-               Set_Parent (T, Decl);
-               Set_Scope (T, Current_Scope);
-
-               if Ekind (T) = E_Array_Subtype then
-                  Constrain_Array (T, New_Def, Related_Nod, T, 'P');
-
-               elsif Ekind (T) = E_Record_Subtype then
-                  Set_First_Entity (T, First_Entity (Base_T));
-                  Set_Has_Discriminants (T, Has_Discriminants (Base_T));
-                  Set_Is_Constrained (T);
-               end if;
-
-               Insert_Before (Related_Nod, Decl);
-            end;
-
-            return T;
-         end if;
-
          --  When generating code, insert subtype declaration ahead of
-         --  declaration that generated it. Similar behavior required under
-         --  preanalysis (including strict preanalysis) to perform the
-         --  minimum decoration, and avoid reporting spurious errors.
+         --  declaration that generated it.
 
          Insert_Action (Obj_Def,
            Make_Subtype_Declaration (Sloc (P),
diff --git a/gcc/ada/sem_ch4.adb b/gcc/ada/sem_ch4.adb
index 0bb74729aae..400fa4f8869 100644
--- a/gcc/ada/sem_ch4.adb
+++ b/gcc/ada/sem_ch4.adb
@@ -2486,6 +2486,7 @@ package body Sem_Ch4 is
 
       A        : Node_Id;
       EWA_Scop : Entity_Id;
+      SE       : Scope_Stack_Entry;
 
    --  Start of processing for Analyze_Expression_With_Actions
 
@@ -2523,8 +2524,28 @@ package body Sem_Ch4 is
          In_Declare_Expr := In_Declare_Expr - 1;
       end if;
 
+      --  If the analysis of the expression has created a transient
+      --  scope, we first need to save the transient scope and pop it.
+
+      if Scope_Is_Transient then
+         SE := Scope_Stack.Table (Scope_Stack.Last);
+         Scope_Stack.Decrement_Last;
+      else
+         SE.Is_Transient := False;
+      end if;
+
+      --  Remove the scope and its declarations from visibility. Note that
+      --  the scope is purely static and the EWA is not a master construct
+      --  (see AI22-0017) so the lifetime of the objects does not end here.
+
       pragma Assert (Current_Scope = Scope_Link (N));
       End_Scope;
+
+      --  Push again the transient scope, if any
+
+      if SE.Is_Transient then
+         Scope_Stack.Append (SE);
+      end if;
    end Analyze_Expression_With_Actions;
 
    ---------------------------
diff --git a/gcc/ada/sem_ch5.adb b/gcc/ada/sem_ch5.adb
index 6f53a8b01c3..c54ad85cf44 100644
--- a/gcc/ada/sem_ch5.adb
+++ b/gcc/ada/sem_ch5.adb
@@ -94,6 +94,17 @@ package body Sem_Ch5 is
    --  statements. On success, the return value is the entity of the loop
    --  referenced by the statement.
 
+   function Has_Sec_Stack_Call (N : Node_Id) return Boolean;
+   --  N is the node for an arbitrary construct. This function searches the
+   --  construct N to see if it contains a function call that returns on the
+   --  secondary stack, returning True if any such call is found, and False
+   --  otherwise.
+
+   --  ??? The implementation invokes Sem_Util.Requires_Transient_Scope so it
+   --  will return True if N contains a function call that needs finalization,
+   --  in addition to the above specification. See Analyze_Loop_Statement for
+   --  a similar comment about this entanglement.
+
    procedure Preanalyze_Range (R_Copy : Node_Id);
    --  Determine expected type of range or domain of iteration of Ada 2012
    --  loop by analyzing separate copy. Do the analysis and resolution of the
diff --git a/gcc/ada/sem_ch5.ads b/gcc/ada/sem_ch5.ads
index c01f2dcb029..03b210fe89d 100644
--- a/gcc/ada/sem_ch5.ads
+++ b/gcc/ada/sem_ch5.ads
@@ -51,16 +51,4 @@ package Sem_Ch5 is
    --  an unconditional transfer of control or an apparent infinite loop. It
    --  checks to see if the statement is followed by some other statement, and
    --  if so generates an appropriate warning for unreachable code.
-
-   function Has_Sec_Stack_Call (N : Node_Id) return Boolean;
-   --  N is the node for an arbitrary construct. This function searches the
-   --  construct N to see if it contains a function call that returns on the
-   --  secondary stack, returning True if any such call is found, and False
-   --  otherwise.
-
-   --  ??? The implementation invokes Sem_Util.Requires_Transient_Scope so it
-   --  will return True if N contains a function call that needs finalization,
-   --  in addition to the above specification. See Analyze_Loop_Statement for
-   --  a similar comment about this entanglement.
-
 end Sem_Ch5;
diff --git a/gcc/ada/sem_res.adb b/gcc/ada/sem_res.adb
index 54915607d68..61bc36e677c 100644
--- a/gcc/ada/sem_res.adb
+++ b/gcc/ada/sem_res.adb
@@ -67,6 +67,7 @@ with Sem_Ch4;        use Sem_Ch4;
 with Sem_Ch5;        use Sem_Ch5;
 with Sem_Ch6;        use Sem_Ch6;
 with Sem_Ch8;        use Sem_Ch8;
+with Sem_Ch9;        use Sem_Ch9;
 with Sem_Ch13;       use Sem_Ch13;
 with Sem_Dim;        use Sem_Dim;
 with Sem_Disp;       use Sem_Disp;
@@ -7752,145 +7753,49 @@ package body Sem_Res is
    -- Resolve_Declare_Expression --
    --------------------------------
 
-   procedure Resolve_Declare_Expression
-     (N   : Node_Id;
-      Typ : Entity_Id)
-   is
-      Expr : constant Node_Id := Expression (N);
-
-      Decl  : Node_Id;
-      Local : Entity_Id := Empty;
-
-      Save_Hidden_Map : constant Elist_Id := New_Elmt_List;
-      --  Stores the map of identifiers, and corresponding entities, that
-      --  temporarily loose visibility due to homonym declarations in the
-      --  current declare expression.
-
-      function Replace_Local (N  : Node_Id) return Traverse_Result;
-      --  Use a tree traversal to replace each occurrence of the name of
-      --  a local object declared in the construct, with the corresponding
-      --  entity. This replaces the usual way to perform name capture by
-      --  visibility, because it is not possible to place on the scope
-      --  stack the fake scope created for the analysis of the local
-      --  declarations; such a scope conflicts with the transient scopes
-      --  that may be generated if the expression includes function calls
-      --  requiring finalization.
-
-      -------------------
-      -- Replace_Local --
-      -------------------
-
-      function Replace_Local (N  : Node_Id) return Traverse_Result is
-      begin
-         --  The identifier may be the prefix of a selected component,
-         --  but not a selector name, because the local entities do not
-         --  have a scope that can be named: a selected component whose
-         --  selector is a homonym of a local entity must denote some
-         --  global entity.
-
-         if Nkind (N) = N_Identifier
-           and then Chars (N) = Chars (Local)
-           and then No (Entity (N))
-           and then
-             (Nkind (Parent (N)) /= N_Selected_Component
-               or else N = Prefix (Parent (N)))
-         then
-            Set_Entity (N, Local);
-            Set_Etype (N, Etype (Local));
-            Generate_Reference (Local, N);
-         end if;
-
-         return OK;
-      end Replace_Local;
-
-      procedure Replace_Local_Ref is new Traverse_Proc (Replace_Local);
-
-   --  Start of processing for Resolve_Declare_Expression
+   procedure Resolve_Declare_Expression (N : Node_Id; Typ : Entity_Id) is
+      After : Node_Id;
+      SE    : Scope_Stack_Entry;
 
    begin
-      --  Create a transient scope if the type of this declare-expression
-      --  or its expression requires it; this must be done before we push
-      --  in the scope stack the scope of this declare expression (in order
-      --  to properly remove it from the stack on exit from this routine).
-      --  Given that we don't know yet if secondary stack management will
-      --  be needed, we assume the worst case.
-
-      if Expander_Active
-        and then (Requires_Transient_Scope (Typ)
-                    or else Has_Sec_Stack_Call (Expr))
-      then
-         Establish_Transient_Scope (N, Manage_Sec_Stack => True);
-      end if;
+      --  First push the scope of the EWA and install its declarations
 
       Push_Scope (Scope_Link (N));
+      Install_Declarations (Scope_Link (N));
 
-      Decl := First (Actions (N));
-
-      while Present (Decl) loop
-         if Nkind (Decl) in
-            N_Object_Declaration | N_Object_Renaming_Declaration
-              and then Comes_From_Source (Defining_Identifier (Decl))
-         then
-            Local := Defining_Identifier (Decl);
-            Replace_Local_Ref (Expr);
-
-            --  Traverse the expression to replace references to local
-            --  variables that occur within declarations of the
-            --  declare_expression.
-
-            declare
-               D : Node_Id := Next (Decl);
-            begin
-               while Present (D) loop
-                  Replace_Local_Ref (D);
-                  Next (D);
-               end loop;
-            end;
-
-            --  Homonyms of the new local declaration are saved to be restored
-            --  after the resolution of the declare block's expression.
-
-            Append_Elmt (Local, Save_Hidden_Map);
-            Append_Elmt (Get_Name_Entity_Id (Chars (Local)), Save_Hidden_Map);
-
-            --  Update the references to local in the name table and make them
-            --  immediately visible to be available within the expression.
-
-            Set_Current_Entity (Local);
-            Set_Is_Immediately_Visible (Local);
-            Set_Is_Not_Self_Hidden (Local);
-         end if;
-
-         Next (Decl);
-      end loop;
-
-      --  The end of the declarative list is a freeze point for the
+      --  The end of the declarative list is a freezing point for the
       --  local declarations.
 
-      if Present (Local) then
-         Decl := Parent (Local);
-         Freeze_All (First_Entity (Scope (Local)), Decl);
+      After := Last (Actions (N));
+      Freeze_All (First_Entity (Scope_Link (N)), After);
+
+      --  Now resolve the expression
+
+      Resolve (Expression (N), Typ);
+      Check_Unset_Reference (Expression (N));
+
+      --  If the resolution of the expression has created a transient
+      --  scope, we first need to save the transient scope and pop it.
+
+      if Scope_Is_Transient then
+         SE := Scope_Stack.Table (Scope_Stack.Last);
+         Scope_Stack.Decrement_Last;
+      else
+         SE.Is_Transient := False;
       end if;
 
-      Resolve (Expr, Typ);
-      Check_Unset_Reference (Expr);
-
-      --  Restore any hidden entity homonyms to a local one
-
-      declare
-         Cursor : Elmt_Id := First_Elmt (Save_Hidden_Map);
-         Name : Name_Id;
-      begin
-         while Present (Cursor) loop
-            Name := Chars (Node (Cursor));
-            Next_Elmt (Cursor);
-            Set_Name_Entity_Id (Name, Node (Cursor));
-            Next_Elmt (Cursor);
-         end loop;
-      end;
+      --  Remove the scope and its declarations from visibility. Note that
+      --  the scope is purely static and the EWA is not a master construct
+      --  (see AI22-0017) so the lifetime of the objects does not end here.
 
       pragma Assert (Current_Scope = Scope_Link (N));
       End_Scope;
+
+      --  Push again the transient scope, if any
+
+      if SE.Is_Transient then
+         Scope_Stack.Append (SE);
+      end if;
    end Resolve_Declare_Expression;
 
    -----------------------------------
-- 
2.53.0

Reply via email to