From: Bob Duff <d...@adacore.com>

Prior to this fix, if pp(N) tried to print a "base type only" field, and
Base_Type(N) was not yet set, it would raise an exception, which was
confusing. This patch makes it simply ignore such fields. Similarly
for Impl_Base_Type_Only and Root_Type_Only fields.

We do this by having alternative versions of Base_Type,
Implementation_Base_Type, and Root_Type that return Empty
in error cases, and call these alteratives from Treepr.
We don't want to Base_Type and friends to return Empty;
we want them to blow up when called from anywhere but
Treepr.

gcc/ada/ChangeLog:

        * atree.ads (Node_To_Fetch_From_If_Set): Alternative to
        Node_To_Fetch_From that returns Empty in error cases.
        For use only in Treepr.
        * treepr.adb (Print_Entity_Field): Avoid printing field
        if Node_To_Fetch_From_If_Set returns Empty.
        * einfo-utils.ads (Base_Type_If_Set): Alternative to
        Base_Type that returns Empty in error cases.
        (Implementation_Base_Type_If_Set): Likewise.
        (Root_Type_If_Set): Likewise.
        (Underlying_Type): Use more accurate result subtype.
        * einfo-utils.adb (Base_Type): Add Asserts.
        (Implementation_Base_Type): Add Assert; minor cleanup.
        (Root_Type): Add Assert; minor cleanup. Remove Assert that
        is redundant with predicate.
        (Base_Type_If_Set): Body of new function.
        (Implementation_Base_Type_If_Set): Body of new function.
        (Root_Type_If_Set): Body of new function.

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

---
 gcc/ada/atree.ads       |  14 ++++
 gcc/ada/einfo-utils.adb | 167 +++++++++++++++++++++++++++-------------
 gcc/ada/einfo-utils.ads |  11 ++-
 gcc/ada/treepr.adb      |   6 +-
 4 files changed, 141 insertions(+), 57 deletions(-)

diff --git a/gcc/ada/atree.ads b/gcc/ada/atree.ads
index 802db8709338..f14491c2d75b 100644
--- a/gcc/ada/atree.ads
+++ b/gcc/ada/atree.ads
@@ -651,6 +651,20 @@ package Atree is
    --  similarly for the other two cases. This can return something other
    --  than N only if N is an Entity.
 
+   function Node_To_Fetch_From_If_Set
+     (N : Node_Or_Entity_Id; Field : Node_Or_Entity_Field)
+     return Node_Or_Entity_Id is
+      (case Field_Descriptors (Field).Type_Only is
+         when No_Type_Only => N,
+         when Base_Type_Only => Base_Type_If_Set (N),
+         when Impl_Base_Type_Only => Implementation_Base_Type_If_Set (N),
+         when Root_Type_Only => Root_Type_If_Set (N));
+   --  This is a more permissive version of Node_To_Fetch_From, which
+   --  returns the same value, except it returns Empty in cases where
+   --  Node_To_Fetch_From would crash because relevant fields are not yet
+   --  set. This is used in Treepr, to allow it to print half-baked nodes
+   --  without crashing.
+
    -----------------------------
    -- Private Part Subpackage --
    -----------------------------
diff --git a/gcc/ada/einfo-utils.adb b/gcc/ada/einfo-utils.adb
index 417da6e828bc..d84e562853cc 100644
--- a/gcc/ada/einfo-utils.adb
+++ b/gcc/ada/einfo-utils.adb
@@ -669,6 +669,8 @@ package body Einfo.Utils is
             Result := Id;
          else
             pragma Assert (Is_Type (Id));
+            --  ...because Is_Base_Type returns True for nontypes
+
             Result := Etype (Id);
             if False then
                pragma Assert (Is_Base_Type (Result));
@@ -679,9 +681,29 @@ package body Einfo.Utils is
                --  expect.
             end if;
          end if;
+
+         --  pragma Assert (Result = Base_Type_If_Set (Id));
+         --  Disabled; too slow
       end return;
    end Base_Type;
 
+   ----------------------
+   -- Base_Type_If_Set --
+   ----------------------
+
+   function Base_Type_If_Set (Id : E) return Opt_N_Entity_Id is
+   begin
+      return Result : Opt_N_Entity_Id do
+         if Is_Base_Type (Id) then
+            Result := Id;
+         elsif Field_Is_Initial_Zero (Id, F_Etype) then
+            Result := Empty;
+         else
+            Result := Etype (Id);
+         end if;
+      end return;
+   end Base_Type_If_Set;
+
    ----------------------
    -- Declaration_Node --
    ----------------------
@@ -1374,30 +1396,43 @@ package body Einfo.Utils is
    ------------------------------
 
    function Implementation_Base_Type (Id : E) return E is
-      Bastyp : Entity_Id;
       Imptyp : Entity_Id;
-
    begin
-      Bastyp := Base_Type (Id);
+      return Result : E := Base_Type (Id) do
+         if Is_Incomplete_Or_Private_Type (Result) then
+            Imptyp := Underlying_Type (Result);
 
-      if Is_Incomplete_Or_Private_Type (Bastyp) then
-         Imptyp := Underlying_Type (Bastyp);
+            --  If we have an implementation type, return its Base_Type.
 
-         --  If we have an implementation type, then just return it,
-         --  otherwise we return the Base_Type anyway. This can only
-         --  happen in error situations and should avoid some error bombs.
-
-         if Present (Imptyp) then
-            return Base_Type (Imptyp);
-         else
-            return Bastyp;
+            if Present (Imptyp) then
+               Result := Base_Type (Imptyp);
+            end if;
          end if;
 
-      else
-         return Bastyp;
-      end if;
+         --  pragma Assert (Result = Implementation_Base_Type_If_Set (Id));
+         --  Disabled; too slow
+      end return;
    end Implementation_Base_Type;
 
+   -------------------------------------
+   -- Implementation_Base_Type_If_Set --
+   -------------------------------------
+
+   function Implementation_Base_Type_If_Set (Id : E) return Opt_N_Entity_Id is
+      Imptyp : Entity_Id;
+   begin
+      return Result : Opt_N_Entity_Id := Base_Type_If_Set (Id) do
+         if Present (Result) and then Is_Incomplete_Or_Private_Type (Result)
+         then
+            Imptyp := Underlying_Type (Result);
+
+            if Present (Imptyp) then
+               Result := Base_Type_If_Set (Imptyp);
+            end if;
+         end if;
+      end return;
+   end Implementation_Base_Type_If_Set;
+
    -------------------------
    -- Invariant_Procedure --
    -------------------------
@@ -2540,52 +2575,76 @@ package body Einfo.Utils is
    ---------------
 
    function Root_Type (Id : E) return E is
-      T, Etyp : Entity_Id;
+      Etyp : Entity_Id;
 
    begin
-      pragma Assert (Nkind (Id) in N_Entity);
+      return T : E := Base_Type (Id) do
+         if Ekind (T) = E_Class_Wide_Type then
+            T := Etype (T);
+         else
+            loop
+               Etyp := Etype (T);
 
-      T := Base_Type (Id);
+               exit when T = Etyp
+                 or else
+                   (Is_Private_Type (T) and then Etyp = Full_View (T))
+                 or else
+                   (Is_Private_Type (Etyp) and then Full_View (Etyp) = T);
 
-      if Ekind (T) = E_Class_Wide_Type then
-         return Etype (T);
+               T := Etyp;
 
-      --  Other cases
+               --  Quit if there is a circularity in the inheritance chain.
+               --  This happens in some error situations and we do not want
+               --  to get stuck in this loop.
 
-      else
-         loop
-            Etyp := Etype (T);
+               if T = Base_Type (Id) then
+                  Check_Error_Detected;
+                  exit;
+               end if;
+            end loop;
+         end if;
 
-            if T = Etyp then
-               return T;
-
-            --  Following test catches some error cases resulting from
-            --  previous errors.
-
-            elsif No (Etyp) then
-               Check_Error_Detected;
-               return T;
-
-            elsif Is_Private_Type (T) and then Etyp = Full_View (T) then
-               return T;
-
-            elsif Is_Private_Type (Etyp) and then Full_View (Etyp) = T then
-               return T;
-            end if;
-
-            T := Etyp;
-
-            --  Return if there is a circularity in the inheritance chain. This
-            --  happens in some error situations and we do not want to get
-            --  stuck in this loop.
-
-            if T = Base_Type (Id) then
-               return T;
-            end if;
-         end loop;
-      end if;
+         --  pragma Assert (T = Root_Type_If_Set (Id));
+         --  Disabled; too slow
+      end return;
    end Root_Type;
 
+   function Root_Type_If_Set (Id : E) return Opt_N_Entity_Id is
+      Etyp : Entity_Id;
+
+   begin
+      return T : Opt_N_Entity_Id := Base_Type_If_Set (Id) do
+         if Ekind (T) = E_Class_Wide_Type then
+            T := Etype (T);
+         else
+            loop
+               Etyp := Etype (T);
+
+               if No (Etyp) then
+                  T := Empty;
+                  exit;
+               end if;
+
+               exit when T = Etyp
+                 or else
+                   (Is_Private_Type (T) and then Etyp = Full_View (T))
+                 or else
+                   (Is_Private_Type (Etyp) and then Full_View (Etyp) = T);
+
+               T := Etyp;
+
+               --  Quit if there is a circularity in the inheritance chain.
+               --  This happens in some error situations and we do not want
+               --  to get stuck in this loop.
+
+               if T = Base_Type_If_Set (Id) then
+                  exit;
+               end if;
+            end loop;
+         end if;
+      end return;
+   end Root_Type_If_Set;
+
    ---------------------
    -- Safe_Emax_Value --
    ---------------------
@@ -3010,7 +3069,7 @@ package body Einfo.Utils is
    -- Underlying_Type --
    ---------------------
 
-   function Underlying_Type (Id : E) return Entity_Id is
+   function Underlying_Type (Id : E) return Opt_N_Entity_Id is
    begin
       --  For record_with_private the underlying type is always the direct full
       --  view. Never try to take the full view of the parent it does not make
diff --git a/gcc/ada/einfo-utils.ads b/gcc/ada/einfo-utils.ads
index 78b49891f609..27cf9e670f0e 100644
--- a/gcc/ada/einfo-utils.ads
+++ b/gcc/ada/einfo-utils.ads
@@ -161,6 +161,15 @@ package Einfo.Utils is
    function First_Formal (Id : E) return Entity_Id;
    function First_Formal_With_Extras (Id : E) return Entity_Id;
 
+   function Base_Type_If_Set (Id : E) return Opt_N_Entity_Id;
+   function Implementation_Base_Type_If_Set (Id : E) return Opt_N_Entity_Id;
+   function Root_Type_If_Set (Id : E) return Opt_N_Entity_Id;
+   --  Base_Type_If_Set is a more permissive version of Base_Type, which
+   --  returns the same value, except it returns Empty in cases where Base_Type
+   --  would crash because relevant fields are not yet set. Likewise for the
+   --  other two. These are used in Treepr, to allow it to print half-baked
+   --  nodes without crashing.
+
    function Float_Rep
      (N : Entity_Id) return F with Inline, Pre =>
       N in E_Void_Id
@@ -238,7 +247,7 @@ package Einfo.Utils is
    function Stream_Size_Clause (Id : E) return N with Inline;
    function Type_High_Bound (Id : E) return N with Inline;
    function Type_Low_Bound (Id : E) return N with Inline;
-   function Underlying_Type (Id : E) return Entity_Id;
+   function Underlying_Type (Id : E) return Opt_N_Entity_Id;
 
    function Scope_Depth (Id : Scope_Kind_Id) return U with Inline;
    function Scope_Depth_Set (Id : Scope_Kind_Id) return B with Inline;
diff --git a/gcc/ada/treepr.adb b/gcc/ada/treepr.adb
index 857b9263f012..fbad71a3765a 100644
--- a/gcc/ada/treepr.adb
+++ b/gcc/ada/treepr.adb
@@ -1047,9 +1047,11 @@ package body Treepr is
       FD     : Field_Descriptor;
       Format : UI_Format := Auto)
    is
-      NN : constant Node_Id := Node_To_Fetch_From (N, Field);
+      NN : constant Node_Id := Node_To_Fetch_From_If_Set (N, Field);
+      --  If NN is Empty, it means that we cannot compute the
+      --  Node_To_Fetch_From, so we simply skip this field.
    begin
-      if not Field_Is_Initial_Zero (N, Field) then
+      if Present (NN) and then not Field_Is_Initial_Zero (N, Field) then
          Print_Field (Prefix, Image (Field), NN, FD, Format);
       end if;
    end Print_Entity_Field;
-- 
2.43.0

Reply via email to