aboutsummaryrefslogtreecommitdiff
path: root/gcc/ada/vast.adb
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/ada/vast.adb')
-rw-r--r--gcc/ada/vast.adb592
1 files changed, 586 insertions, 6 deletions
diff --git a/gcc/ada/vast.adb b/gcc/ada/vast.adb
index 302a89b..59470fd 100644
--- a/gcc/ada/vast.adb
+++ b/gcc/ada/vast.adb
@@ -23,18 +23,598 @@
-- --
------------------------------------------------------------------------------
--- Dummy implementation
+pragma Unsuppress (All_Checks);
+pragma Assertion_Policy (Check);
+-- Enable checking. This isn't really necessary, but it might come in handy if
+-- we want to run VAST with a compiler built without checks. Anyway, it's
+-- harmless, because VAST is not run by default.
+
+with Ada.Unchecked_Deallocation;
+
+with System.Case_Util;
+
+with Atree; use Atree;
+with Debug;
+with Einfo.Entities; use Einfo.Entities;
+with Lib; use Lib;
+with Namet; use Namet;
+with Nlists; use Nlists;
+with Opt; use Opt;
+with Output;
+with Sinfo.Nodes; use Sinfo.Nodes;
+with Sinput;
+with Table;
+with Types; use Types;
package body VAST is
+ -- ???Basic tree properties not yet checked:
+ -- - No dangling trees. Every node that is reachable at all is reachable
+ -- by some syntactic path.
+ -- - Basic properties of Nlists/Elists (next/prev pointers make sense,
+ -- for example).
+
+ Force_Enable_VAST : constant Boolean := False;
+ -- Normally, VAST is enabled by the the -gnatd_V switch.
+ -- To force it to be enabled independent of any switches,
+ -- set this to True.
+
+ type Check_Enum is
+ (Check_Other,
+ Check_Sloc,
+ Check_Analyzed,
+ Check_Error_Nodes,
+ Check_Sharing,
+ Check_Parent_Present,
+ Check_Parent_Correct);
+
+ type Check_Status is
+ -- Action in case of check failure:
+ (Disabled, -- Do nothing
+ Enabled, -- Print messages, and raise an exception
+ Print_And_Continue); -- Print a message
+
+ pragma Warnings (Off, "Status*could be declared constant");
+ Status : array (Check_Enum) of Check_Status :=
+ (Check_Other => Enabled,
+ Check_Sloc => Disabled,
+ Check_Analyzed => Disabled,
+ Check_Error_Nodes => Print_And_Continue,
+ Check_Sharing => Disabled,
+ Check_Parent_Present => Print_And_Continue,
+ Check_Parent_Correct => Disabled);
+-- others => Print_And_Continue);
+-- others => Enabled);
+-- others => Disabled);
+ -- Passing checks are Check_Other, which should always be Enabled.
+ -- Currently-failing checks are different enumerals in Check_Enum,
+ -- which can be disabled individually until we fix the bugs, or enabled
+ -- when debugging particular bugs. Pass a nondefault Check_Enum to
+ -- Assert in order to deal with bugs we have not yet fixed,
+ -- and play around with the value of Status above for
+ -- testing and debugging.
+ --
+ -- Note: Once a bug is fixed, and the check passes reliably, we may choose
+ -- to remove that check from Check_Enum and use Check_Other instead.
+
+ type Node_Stack_Index is new Pos;
+ subtype Node_Stack_Count is
+ Node_Stack_Index'Base range 0 .. Node_Stack_Index'Last;
+
+ package Node_Stack is new Table.Table
+ (Table_Component_Type => Node_Id,
+ Table_Index_Type => Node_Stack_Index'Base,
+ Table_Low_Bound => 1,
+ Table_Initial => 1,
+ Table_Increment => 100,
+ Table_Name => "Node_Stack");
+
+ procedure Assert
+ (Condition : Boolean;
+ Check : Check_Enum := Check_Other;
+ Detail : String := "");
+ -- Check that the Condition is True. Status determines action on failure.
+
+ function To_Mixed (A : String) return String;
+ -- Copied from System.Case_Util; old versions of that package do not have
+ -- this function, so this is needed for bootstrapping.
+
+ function Image (Kind : Node_Kind) return String is (To_Mixed (Kind'Img));
+ function Image (Kind : Entity_Kind) return String is (To_Mixed (Kind'Img));
+
+ procedure Put (S : String);
+ procedure Put_Line (S : String);
+ procedure Put_Node (N : Node_Id);
+ procedure Put_Node_Stack;
+ -- Output routines; print only if -gnatd_W (VAST in verbose mode) is
+ -- enabled.
+
+ procedure Put_Indentation;
+ -- Print spaces to indicate nesting depth of Node_Stack
+
+ procedure Enter_Node (N : Node_Id);
+ procedure Leave_Node (N : Node_Id);
+ -- Called for each node while walking the tree.
+ -- Push/pop N to/from Node_Stack.
+ -- Print enter/leave debugging messages.
+ -- ???Possible improvements to messages:
+ -- Walk subtrees in a better order.
+ -- Print field names.
+ -- Don't print boring fields (such as N_Empty nodes).
+ -- Print more info (value of literals, "A.B.C" for expanded names, etc.).
+ -- Share some code with Treepr.
+
+ procedure Do_Tree (N : Node_Id);
+ -- Do VAST checking on a tree of nodes
+
+ function Has_Subtrees (N : Node_Id) return Boolean;
+ -- True if N has one or more syntactic fields
+
+ procedure Do_Subtrees (N : Node_Id);
+ -- Call Do_Tree on all the subtrees (i.e. syntactic fields) of N
+
+ procedure Do_List (L : List_Id);
+ -- Call Do_Tree on the list elements
+
+ procedure Do_Unit (U : Unit_Number_Type);
+ -- Call Do_Tree on the root node of a compilation unit
+
+ function Ancestor_Node (Count : Node_Stack_Count) return Node_Id;
+ -- Nth ancestor on the Node_Stack. Ancestor_Node(0) is the current node,
+ -- Ancestor_Node(1) is its parent, Ancestor_Node(2) is its grandparent,
+ -- and so on.
+
+ function Top_Node return Node_Id is (Ancestor_Node (0));
+
+ type Node_Set is array (Node_Id range <>) of Boolean;
+ pragma Pack (Node_Set);
+ type Node_Set_Ptr is access all Node_Set;
+ procedure Free is new Ada.Unchecked_Deallocation (Node_Set, Node_Set_Ptr);
+
+ Visited : Node_Set_Ptr;
+ -- Giant array of Booleans; Visited (N) is True if and only if we have
+ -- visited N in the tree walk. Used to detect incorrect sharing of subtrees
+ -- or (worse) cycles. We don't allocate the set on the stack, for fear of
+ -- Storage_Error.
+
+ function Get_Node_Field_Union is new
+ Atree.Atree_Private_Part.Get_32_Bit_Field (Union_Id) with Inline;
+
+ --------------
+ -- To_Mixed --
+ --------------
+
+ function To_Mixed (A : String) return String is
+ Result : String := A;
+ begin
+ System.Case_Util.To_Mixed (Result);
+ return Result;
+ end To_Mixed;
+
+ ---------
+ -- Put --
+ ---------
+
+ procedure Put (S : String) is
+ begin
+ if Debug.Debug_Flag_Underscore_WW then
+ Output.Write_Str (S);
+ end if;
+ end Put;
+
+ --------------
+ -- Put_Line --
+ --------------
+
+ procedure Put_Line (S : String) is
+ begin
+ if Debug.Debug_Flag_Underscore_WW then
+ Output.Write_Line (S);
+ end if;
+ end Put_Line;
+
+ --------------
+ -- Put_Node --
+ --------------
+
+ procedure Put_Node (N : Node_Id) is
+ begin
+ if Debug.Debug_Flag_Underscore_WW then
+ if Nkind (N) in N_Entity then
+ Put (Image (Ekind (N)));
+ else
+ Put (Image (Nkind (N)));
+ end if;
+
+ Put (N'Img & " ");
+ Sinput.Write_Location (Sloc (N));
+
+ if Comes_From_Source (N) then
+ Put (" (s)");
+ end if;
+
+ case Nkind (N) is
+ when N_Has_Chars =>
+ Put (" ");
+ Write_Name_For_Debug (Chars (N), Quote => """");
+ when others => null;
+ end case;
+
+ end if;
+ end Put_Node;
+
+ ---------------------
+ -- Put_Indentation --
+ ---------------------
+
+ procedure Put_Indentation is
+ begin
+ Put (String'(Natural (Node_Stack.First) ..
+ Natural (Node_Stack.Last) * 2 => ' '));
+ end Put_Indentation;
+
+ ----------------
+ -- Enter_Node --
+ ----------------
+
+ procedure Enter_Node (N : Node_Id) is
+ begin
+ Node_Stack.Append (N); -- push
+
+ if Has_Subtrees (N) then
+ Put ("-->");
+ else
+ -- If no subtrees, just print one line for enter/leave
+ Put (" ");
+ end if;
+ Put_Indentation;
+ Put_Node (N);
+ Put_Line ("");
+ end Enter_Node;
+
----------------
- -- Check_Tree --
+ -- Leave_Node --
----------------
- procedure Check_Tree (GNAT_Root : Node_Id) is
- pragma Unreferenced (GNAT_Root);
+ procedure Leave_Node (N : Node_Id) is
+ begin
+ if Has_Subtrees (N) then
+ Put ("<--");
+ Put_Indentation;
+ Put_Node (N);
+ Put_Line ("");
+ end if;
+
+ Node_Stack.Decrement_Last; -- pop
+ end Leave_Node;
+
+ --------------------
+ -- Put_Node_Stack --
+ --------------------
+
+ procedure Put_Node_Stack is
+ begin
+ for J in reverse Node_Stack.First .. Node_Stack.Last loop
+ Put_Node (Node_Stack.Table (J));
+ Put_Line ("");
+ end loop;
+ end Put_Node_Stack;
+
+ -------------------
+ -- Ancestor_Node --
+ -------------------
+
+ function Ancestor_Node (Count : Node_Stack_Count) return Node_Id is
+ begin
+ return Node_Stack.Table (Node_Stack.Last - Count);
+ end Ancestor_Node;
+
+ ------------
+ -- Assert --
+ ------------
+
+ VAST_Failure : exception;
+
+ procedure Assert
+ (Condition : Boolean;
+ Check : Check_Enum := Check_Other;
+ Detail : String := "")
+ is
begin
- null;
- end Check_Tree;
+ if not Condition then
+ declare
+ Part1 : constant String := "VAST fail";
+ Part2 : constant String :=
+ (if Check = Check_Other then ""
+ else ": " & To_Mixed (Check'Img));
+ Part3 : constant String :=
+ (if Detail = "" then "" else " -- " & Detail);
+ Message : constant String := Part1 & Part2 & Part3;
+ Save : constant Boolean := Debug.Debug_Flag_Underscore_WW;
+ begin
+ case Status (Check) is
+ when Disabled => null;
+ when Enabled | Print_And_Continue =>
+ Debug.Debug_Flag_Underscore_WW := True;
+ -- ???We should probably avoid changing the debug flag here
+ Put (Message & ": ");
+ Put_Node (Top_Node);
+ Put_Line ("");
+
+ if Status (Check) = Enabled then
+ Put_Node_Stack;
+ raise VAST_Failure with Message;
+ end if;
+
+ Debug.Debug_Flag_Underscore_WW := Save;
+ end case;
+ end;
+ end if;
+ end Assert;
+
+ -------------
+ -- Do_Tree --
+ -------------
+
+ procedure Do_Tree (N : Node_Id) is
+ begin
+ Enter_Node (N);
+
+ -- Skip the rest if empty. Check Sloc:
+
+ case Nkind (N) is
+ when N_Empty =>
+ Assert (No (Sloc (N)));
+ goto Done; -- -------------->
+ -- Don't do any further checks on Empty
+
+ -- ???Some nodes, including exception handlers, have no Sloc;
+ -- it's unclear why.
+
+ when N_Exception_Handler =>
+ Assert (if Comes_From_Source (N) then Present (Sloc (N)));
+ when others =>
+ Assert (Present (Sloc (N)), Check_Sloc);
+ end case;
+
+ -- All reachable nodes should have been analyzed by the time we get
+ -- here:
+
+ Assert (Analyzed (N), Check_Analyzed);
+
+ -- If we visit the same node more than once, then there are shared
+ -- nodes; the "tree" is not a tree:
+
+ Assert (not Visited (N), Check_Sharing);
+ Visited (N) := True;
+
+ -- Misc checks based on node/entity kind:
+
+ case Nkind (N) is
+ when N_Unused_At_Start | N_Unused_At_End =>
+ Assert (False);
+
+ when N_Error =>
+ -- VAST doesn't do anything when Serious_Errors_Detected > 0 (at
+ -- least for now), so we shouldn't encounter any N_Error nodes.
+ Assert (False, Check_Error_Nodes);
+
+ when N_Entity =>
+ case Ekind (N) is
+ when others =>
+ null; -- more to be done here
+ end case;
+
+ when others =>
+ null; -- more to be done here
+ end case;
+
+ -- Check that N has a Parent, except in certain cases:
+
+ case Nkind (N) is
+ when N_Empty =>
+ raise Program_Error; -- can't get here
+
+ when N_Error =>
+ Assert (False, Check_Error_Nodes);
+ -- The error node has no parent, but we shouldn't even be seeing
+ -- error nodes in VAST at all. See earlier "when N_Error".
+
+ when N_Compilation_Unit =>
+ Assert (No (Parent (N)));
+ -- The parent of the root of each unit is empty.
+
+ when N_Entity =>
+ if not Is_Itype (N) then
+ -- An Itype might or might not have a parent
+
+ Assert
+ (Present (Parent (N)), Detail => "missing parent of entity");
+ Assert (Parent (N) = Ancestor_Node (1), Check_Parent_Correct);
+ end if;
+
+ when others =>
+ Assert (Present (Parent (N)), Check_Parent_Present);
+ -- All other nodes should have a parent
+ if Status (Check_Parent_Present) = Enabled then
+ Assert (Parent (N) = Ancestor_Node (1), Check_Parent_Correct);
+ end if;
+ end case;
+
+ Do_Subtrees (N);
+
+ <<Done>>
+ Leave_Node (N);
+ end Do_Tree;
+
+ -----------------
+ -- Has_Subtrees --
+ -----------------
+
+ function Has_Subtrees (N : Node_Id) return Boolean is
+ Offsets : Traversed_Offset_Array renames
+ Traversed_Fields (Nkind (N));
+ begin
+ -- True if sentinel comes first
+ return Offsets (Offsets'First) /= No_Field_Offset;
+ end Has_Subtrees;
+
+ -----------------
+ -- Do_Subtrees --
+ -----------------
+
+ procedure Do_Subtrees (N : Node_Id) is
+ -- ???Do we need tail recursion elimination here,
+ -- as in Atree.Traverse_Func?
+ Offsets : Traversed_Offset_Array renames
+ Traversed_Fields (Nkind (N));
+ begin
+ for Cur_Field in Offset_Array_Index loop
+ exit when Offsets (Cur_Field) = No_Field_Offset;
+
+ declare
+ F : constant Union_Id :=
+ Get_Node_Field_Union (N, Offsets (Cur_Field));
+ begin
+ if F in Node_Range then
+ Do_Tree (Node_Id (F));
+ elsif F in List_Range then
+ Do_List (List_Id (F));
+ else
+ raise Program_Error;
+ end if;
+ end;
+ end loop;
+ end Do_Subtrees;
+
+ -------------
+ -- Do_List --
+ -------------
+
+ procedure Do_List (L : List_Id) is
+ Elmt : Node_Id := First (L);
+ Len : constant String := List_Length (L)'Img;
+ begin
+ if Is_Non_Empty_List (L) then
+ Put ("-->");
+ Put_Indentation;
+ Put_Line ("list len=" & Len);
+
+ while Present (Elmt) loop
+ Do_Tree (Elmt);
+ Next (Elmt);
+ end loop;
+
+ Put ("<--");
+ Put_Indentation;
+ Put_Line ("list len=" & Len);
+ end if;
+ end Do_List;
+
+ -------------
+ -- Do_Unit --
+ -------------
+
+ procedure Do_Unit (U : Unit_Number_Type) is
+ U_Name : constant Unit_Name_Type := Unit_Name (U);
+ U_Name_S : constant String :=
+ (if U_Name = No_Unit_Name then "<No_Unit_Name>"
+ else Get_Name_String (U_Name));
+ Predef : constant String :=
+ (if Is_Predefined_Unit (U) then " (predef)"
+ elsif Is_Internal_Unit (U) then " (gnat)"
+ else "");
+ Is_Main : constant String :=
+ (if U = Main_Unit then " (main unit)" else "");
+ Msg : constant String :=
+ "VAST for unit" & U'Img & " " & U_Name_S & Predef & Is_Main;
+
+ Is_Preprocessing_Dependency : constant Boolean :=
+ U_Name = No_Unit_Name;
+ -- True if this is a bogus unit added by Add_Preprocessing_Dependency.
+ -- ???Not sure what that's about, but these units have no name and
+ -- no associated tree, so we had better not try to walk those trees.
+
+ Root : constant Node_Id := Cunit (U);
+ begin
+ pragma Assert (Node_Stack.Last = 0);
+ Assert (No (Root) = Is_Preprocessing_Dependency);
+ -- All compilation units except these bogus ones should have a Cunit.
+
+ Put_Line (Msg);
+
+ if Is_Preprocessing_Dependency then
+ Put_Line ("Skipping preprocessing dependency");
+ return;
+ end if;
+
+ Assert (Present (Root));
+ Do_Tree (Root);
+ Put_Line (Msg & " (done)");
+ pragma Assert (Node_Stack.Last = 0);
+ end Do_Unit;
+
+ ----------
+ -- VAST --
+ ----------
+
+ procedure VAST is
+ pragma Assert (Expander_Active = (Operating_Mode = Generate_Code));
+ -- ???So why do we need both Operating_Mode and Expander_Active?
+ use Debug;
+ begin
+ -- Do nothing if we're not calling the back end; the main point of VAST
+ -- is to protect against code-generation bugs. This includes the
+ -- case where legality errors were detected; the tree is known to be
+ -- malformed in some error cases.
+
+ if Operating_Mode /= Generate_Code then
+ return;
+ end if;
+
+ -- If -gnatd_W (VAST in verbose mode) is enabled, then that should imply
+ -- -gnatd_V (enable VAST).
+
+ if Debug_Flag_Underscore_WW then
+ Debug_Flag_Underscore_VV := True;
+ end if;
+
+ -- Do nothing if VAST is disabled
+
+ if not (Debug_Flag_Underscore_VV or Force_Enable_VAST) then
+ return;
+ end if;
+
+ -- Turn off output unless verbose mode is enabled
+
+ Put_Line ("VAST");
+
+ -- Operating_Mode = Generate_Code implies there are no legality errors:
+
+ Assert (Serious_Errors_Detected = 0);
+
+ Put_Line ("VAST checking" & Last_Unit'Img & " units");
+
+ declare
+ use Atree_Private_Part;
+ Last_Node : constant Node_Id := Node_Offsets.Last;
+ begin
+ pragma Assert (Visited = null);
+ Visited := new Node_Set'(Node_Id'First .. Last_Node => False);
+
+ for U in Main_Unit .. Last_Unit loop
+ -- Main_Unit is the one passed to the back end, but here we are
+ -- walking all the units.
+ Do_Unit (U);
+ end loop;
+
+ -- We shouldn't have allocated any new nodes during VAST:
+
+ pragma Assert (Node_Offsets.Last = Last_Node);
+ Free (Visited);
+ end;
+
+ Put_Line ("VAST done.");
+ end VAST;
end VAST;