blob: 5570e123fe0acf26b4fc6ef084bb1185ff10420f [file] [log] [blame]
------------------------------------------------------------------------------
-- --
-- GNAT COMPILER COMPONENTS --
-- --
-- V A S T --
-- --
-- B o d y --
-- --
-- Copyright (C) 2020-2026, Free Software Foundation, Inc. --
-- --
-- GNAT is free software; you can redistribute it and/or modify it under --
-- terms of the GNU General Public License as published by the Free Soft- --
-- ware Foundation; either version 3, or (at your option) any later ver- --
-- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
-- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
-- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
-- for more details. You should have received a copy of the GNU General --
-- Public License distributed with GNAT; see file COPYING3. If not, go to --
-- http://www.gnu.org/licenses for a complete copy of the license. --
-- --
-- GNAT was originally developed by the GNAT team at New York University. --
-- Extensive contributions were provided by Ada Core Technologies Inc. --
-- --
------------------------------------------------------------------------------
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 Einfo.Utils; use Einfo.Utils;
with Errout;
with Exp_Ch6;
with Exp_Tss;
with Lib; use Lib;
with Namet; use Namet;
with Nlists; use Nlists;
with Opt; use Opt;
with Output;
with Sem_Aux;
with Sem_Util;
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_FE_Only,
Check_Sharing,
Check_Parent_Present,
Check_Parent_Correct,
Check_Scope_Present,
Check_Scope_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 is variable so we can modify it in gdb, for example
Status : array (Check_Enum) of Check_Status :=
(Check_Other => Enabled,
Check_Sloc => Disabled,
Check_Analyzed => Disabled,
Check_Error_Nodes => Enabled,
Check_FE_Only => Disabled,
Check_Sharing => Disabled,
Check_Parent_Present => Enabled,
Check_Parent_Correct => Disabled,
Check_Scope_Present => Print_And_Continue,
Check_Scope_Correct => Print_And_Continue);
-- 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");
type Pass_Number is range 1 .. 2;
Pass : Pass_Number;
procedure VAST;
-- Called by VAST_If_Enabled to do all the checking
procedure Fail
(Check : Check_Enum := Check_Other;
Detail : String := "");
-- Print failure information if Check is not disabled. Called by Assert
-- when Condition is False and for other failures.
procedure Fail_Breakpoint (N : Node_Id) with Export;
-- Does nothing. Called by Fail; useful to set a breakpoint in gdb on this.
procedure Assert
(Condition : Boolean;
Check : Check_Enum := Check_Other;
Detail : String := "");
-- Check that the Condition is True. Status determines action on failure.
-- Note: This procedure is used to detect errors in the tree, whereas
-- pragma Assert is used to detect errors in VAST itself.
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));
function Kind_Image (N : Node_Or_Entity_Id) return String is
(if Nkind (N) in N_Entity then Image (Ekind (N))
else Image (Nkind (N)));
function Node_Image (N : Node_Or_Entity_Id) return String is
(Kind_Image (N) & N'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 Is_FE_Only (Kind : Node_Kind) return Boolean;
-- True if nodes of this Kind can appear only in the front end. They should
-- be transformed into something else before calling the back end, or else
-- they can only appear in illegal code.
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_Node_Pass_2 (N : Node_Id);
-- Called by Do_Tree in the second pass
procedure Do_Unit (U : Unit_Number_Type);
-- Call Do_Tree on the root node of a compilation unit
function Is_On_Stack (Kind : Node_Kind) return Boolean;
-- True if there is at least one node on the stack with the specified Kind
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_Info is record
Count : Nat := 0;
Prev_Parent : Node_Id := Empty;
In_Aspect : Boolean := False;
end record;
type Node_Info_Array is array (Node_Id range <>) of Node_Info;
type Node_Info_Array_Ptr is access all Node_Info_Array;
procedure Free is new Ada.Unchecked_Deallocation
(Node_Info_Array, Node_Info_Array_Ptr);
Nodes_Info : Node_Info_Array_Ptr;
-- Nodes_Info (N).Prev_Parent is non-Empty if and only if the tree walk has
-- visited N. If non-Empty, it points to the most recent parent of N in the
-- tree walk; that is, the node that allowed us to get to N. Normally, each
-- reachable node is visited exactly once, and if the Parent pointers
-- aren't messed up, then Nodes_Info (N).Prev_Parent will be Parent (N).
-- (See below for the special case of the root compilation unit node.)
--
-- Used to detect incorrect sharing of subtrees or (worse) cycles. We don't
-- allocate this on the stack, for fear of Storage_Error.
--
-- Nodes_Info (N).Count is the number of ways N is reachable in the walk.
-- It should be 1 for all nodes except the root.
function Get_Node_Field_Union is new
Atree.Atree_Private_Part.Get_32_Bit_Field (Union_Id) with Inline;
function Has_Field (Kind : Node_Kind; F : Node_Field) return Boolean;
-- True if nodes of type Kind have field F
function Related_Chars (N : Node_Id) return Name_Id;
-- Return a Name_Id related to N that is worth printing when we print
-- information about N. Returns No_Name if there is no interesting Name_Id.
-- This is typically "Chars (N)" or "Chars (Defining_Identifier (N))" or
-- similar.
procedure Check_Scope (N : Node_Id);
-- Check that the Scope of N makes sense
procedure Validate_Subprogram_Calls (N : Node_Id);
-- Check that the number of actuals (including extra actuals) of all calls
-- within N match their corresponding formals; check also that the names
-- of BIP extra actuals and formals match.
--------------
-- 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;
---------------
-- Has_Field --
---------------
function Has_Field (Kind : Node_Kind; F : Node_Field) return Boolean is
Fields : Node_Field_Array renames Node_Field_Table (Kind).all;
begin
for Index in Fields'Range loop
if Fields (Index) = F then
return True;
end if;
end loop;
return False;
end Has_Field;
-------------------
-- Related_Chars --
-------------------
function Related_Chars (N : Node_Id) return Name_Id is
begin
return Result : Name_Id := No_Name do
if Has_Field (Nkind (N), F_Chars) then
Result := Chars (N);
elsif Has_Field (Nkind (N), F_Defining_Identifier) then
Result := Related_Chars (Defining_Identifier (N));
elsif Has_Field (Nkind (N), F_Defining_Unit_Name) then
Result := Related_Chars (Defining_Unit_Name (N));
elsif Has_Field (Nkind (N), F_Specification) then
Result := Related_Chars (Specification (N));
end if;
end return;
end Related_Chars;
--------------
-- Put_Node --
--------------
procedure Put_Node (N : Node_Id) is
begin
if Debug.Debug_Flag_Underscore_WW then
Put (Node_Image (N) & " ");
Sinput.Write_Location (Sloc (N));
if Comes_From_Source (N) then
Put (" (s)");
end if;
declare
Chars_To_Print : constant Name_Id := Related_Chars (N);
begin
if Present (Chars_To_Print) then
Put (" ");
Write_Name_For_Debug (Chars_To_Print, Quote => """");
end if;
end;
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;
----------------
-- Leave_Node --
----------------
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;
-----------------
-- Is_On_Stack --
-----------------
function Is_On_Stack (Kind : Node_Kind) return Boolean is
begin
for J in reverse Node_Stack.First .. Node_Stack.Last loop
if Nkind (Node_Stack.Table (J)) = Kind then
return True;
end if;
end loop;
return False;
end Is_On_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;
---------------------
-- Fail_Breakpoint --
---------------------
procedure Fail_Breakpoint (N : Node_Id) is
begin
null;
end Fail_Breakpoint;
----------
-- Fail --
----------
VAST_Failure : exception;
procedure Fail
(Check : Check_Enum := Check_Other;
Detail : String := "")
is
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 ("");
Put ("VAST file: ");
Sinput.Write_Location (Sloc (Top_Node));
Put_Line ("");
Put_Node_Stack;
if Status (Check) = Enabled then
Put_Node_Stack;
raise VAST_Failure with Message;
end if;
Debug.Debug_Flag_Underscore_WW := Save;
Fail_Breakpoint (Ancestor_Node (0));
end case;
end Fail;
------------
-- Assert --
------------
procedure Assert
(Condition : Boolean;
Check : Check_Enum := Check_Other;
Detail : String := "")
is
begin
if not Condition then
Fail (Check, Detail);
end if;
end Assert;
-----------------
-- Check_Scope --
-----------------
procedure Check_Scope (N : Node_Id) is
use Exp_Tss, Sem_Util;
begin
if Present (Scope (N)) then
if False then -- ????
Assert (Enclosing_Declaration (Scope (N)) =
Enclosing_Declaration (Enclosing_Declaration (N)),
Check_Scope_Correct);
end if;
else
if Ekind (N) = E_Void then
-- ????These seem to be SW, PI, &c, and their params.
null;
elsif Ekind (N) = E_Procedure and then Is_TSS (N, TSS_Put_Image)
then
null; -- also PI
elsif Ekind (N) = E_Protected_Body then
null;
else
Fail (Check_Scope_Present);
end if;
end if;
end Check_Scope;
--------------------
-- Do_Node_Pass_2 --
--------------------
procedure Do_Node_Pass_2 (N : Node_Id) is
begin
-- Check Sloc
case Nkind (N) is
-- ???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))), Check_Sloc);
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);
-- Misc checks based on node/entity kind
case Nkind (N) is
when N_Unused_At_Start | N_Unused_At_End =>
-- ????Can't get here, because Is_FE_Only. Also 'case' below.
Fail;
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.
Fail (Check_Error_Nodes);
when N_Entity =>
Check_Scope (N);
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 =>
Fail (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;
end Do_Node_Pass_2;
-------------
-- Do_Tree --
-------------
procedure Do_Tree (N : Node_Id) is
Visited : constant Boolean := Present (Nodes_Info (N).Prev_Parent);
begin
if False and Nkind (N) = N_Aspect_Specification then
-- ????This cuts failures 453490/235214 = 1.9.
return;
end if;
if Pass = 1 then
Nodes_Info (N).Count := Nodes_Info (N).Count + 1;
-- ????Get rid of asserts:
pragma Assert
(if Nkind (N) not in N_Empty | N_Compilation_Unit then
Visited = (Nodes_Info (N).Count > 1));
if Is_On_Stack (N_Aspect_Specification) then
Nodes_Info (N).In_Aspect := True;
end if;
elsif Pass = 2 then
pragma Assert (Nodes_Info (N).Count > 0);
end if;
Enter_Node (N);
Assert (not Is_FE_Only (Nkind (N)), Check_FE_Only);
-- ????Also check for particular pragmas, etc.
-- And Ekind.
if Nkind (N) = N_Empty then
Assert (N = Empty);
Assert (No (Sloc (N)));
goto Done; -- -------------->
-- Don't do any further checks on Empty
end if;
-- If we visit the same node more than once, then there are shared
-- nodes; the "tree" is not a tree:
-- We know that the "extra formals" involve shared subtrees,
-- and that's probably unavoidable. See Expand_Call_Helper.
-- A lot of shared subtrees come from aspect specifications,
-- probably because they get turned into pragmas, and the
-- subtrees get placed inside the pragmas without removing
-- them from the original aspect specifications.
if Pass = 2 and then Nodes_Info (N).Count > 1 and then
not Nodes_Info (N).In_Aspect -- ????cuts failures by 1.9
then
declare
Count : constant String :=
(if Nodes_Info (N).Count = 2 then ""
else Nodes_Info (N).Count'Img & "par");
Aspect : constant String :=
(if Nodes_Info (N).In_Aspect then "{asp}" else "");
begin
Fail (Check_Sharing,
"(prev-par=" &
Node_Image (Nodes_Info (N).Prev_Parent) & ")" &
Count & Aspect);
if Status (Check_Sharing) /= Disabled then
Output.Write_Line
(Kind_Image (Ancestor_Node (1)) & "```" & Kind_Image (N));
Output.Write_Line ("");
end if;
end;
end if;
if Node_Stack.Last = 1 then
Nodes_Info (N).Prev_Parent := Ancestor_Node (0);
Assert (Nkind (N) = N_Compilation_Unit);
-- This is the root node. Set the parent to itself,
-- for no particular reason except to make it not Empty.
else
Nodes_Info (N).Prev_Parent := Ancestor_Node (1);
end if;
if not Visited then -- Don't walk it more than once
if Pass = 2 then
Do_Node_Pass_2 (N);
end if;
Do_Subtrees (N);
end if;
<<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 the first Offset is not the sentinel
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.
-- These units have no name and no associated tree; we had better not
-- try to walk nonexistent 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
begin
Put_Line ("VAST");
-- Operating_Mode = Generate_Code implies there are no legality errors
pragma Assert (Serious_Errors_Detected = 0);
pragma Assert (not Errout.Compilation_Errors);
Put_Line ("VAST checking" & Last_Unit'Img & " units");
declare
use Atree_Private_Part;
Last_Node : constant Node_Id := Node_Offsets.Last;
begin
pragma Assert (Nodes_Info = null);
Nodes_Info := new Node_Info_Array (Node_Id'First .. Last_Node);
-- Walk all nodes in all units doing Pass 1, and so on
-- for each Pass.
for P in Pass_Number loop
Pass := P;
Put_Line ("VAST Pass" & Pass'Img);
if Pass = 2 then -- ????Is this needed?
for Index in Nodes_Info'Range loop
Nodes_Info (Index).Prev_Parent := Empty;
end loop;
end if;
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;
end loop;
-- Validate subprogram calls; check "extra formals". This works only
-- for the main unit.
Validate_Subprogram_Calls (Cunit (Main_Unit));
-- We shouldn't have allocated any new nodes during VAST
pragma Assert (Node_Offsets.Last = Last_Node);
Free (Nodes_Info);
end;
Put_Line ("VAST done.");
end VAST;
---------------------
-- VAST_If_Enabled --
---------------------
procedure VAST_If_Enabled is
-- This is the public entry point
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. VAST is disabled if
-- legality errors were detected; the tree is known to be malformed
-- in some error cases. The -gnatc switch also disables VAST.
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 or Force_Enable_VAST then
Debug_Flag_Underscore_VV := True;
end if;
-- Do nothing if VAST is disabled
if not Debug_Flag_Underscore_VV then
return;
end if;
VAST;
end VAST_If_Enabled;
-------------------------------
-- Validate_Subprogram_Calls --
-------------------------------
procedure Validate_Subprogram_Calls (N : Node_Id) is
use Sem_Aux, Sem_Util;
function Process_Node (Nod : Node_Id) return Traverse_Result;
-- Function to traverse the subtree of N using Traverse_Proc.
------------------
-- Process_Node --
------------------
function Process_Node (Nod : Node_Id) return Traverse_Result is
begin
case Nkind (Nod) is
when N_Entry_Call_Statement
| N_Procedure_Call_Statement
| N_Function_Call
=>
declare
Call_Node : Node_Id renames Nod;
Subp : constant Entity_Id := Get_Called_Entity (Nod);
begin
pragma Assert (Exp_Ch6.Check_BIP_Actuals (Call_Node, Subp));
-- Build-in-place function calls return their result by
-- reference.
pragma Assert (not Exp_Ch6.Is_Build_In_Place_Function (Subp)
or else Returns_By_Ref (Subp));
end;
-- Skip generic bodies
when N_Package_Body =>
if Ekind (Unique_Defining_Entity (Nod)) = E_Generic_Package then
return Skip;
end if;
when N_Subprogram_Body =>
if Ekind (Unique_Defining_Entity (Nod)) in E_Generic_Function
| E_Generic_Procedure
then
return Skip;
end if;
-- Nodes we want to ignore
-- Skip calls placed in the full declaration of record types since
-- the call will be performed by their Init Proc; for example,
-- calls initializing default values of discriminants or calls
-- providing the initial value of record type components. Other
-- full type declarations are processed because they may have
-- calls that must be checked. For example:
-- type T is array (1 .. Some_Function_Call (...)) of Some_Type;
-- ??? More work needed here to handle the following case:
-- type Rec is record
-- F : String (1 .. <some complicated expression>);
-- end record;
when N_Full_Type_Declaration =>
if Is_Record_Type (Defining_Entity (Nod)) then
return Skip;
end if;
-- Skip calls placed in unexpanded initialization expressions
when N_Object_Declaration =>
if No_Initialization (Nod) then
return Skip;
end if;
-- Skip calls placed in subprogram specifications since function
-- calls initializing default parameter values will be processed
-- when the call to the subprogram is found (if the default actual
-- parameter is required), and calls found in aspects will be
-- processed when their corresponding pragma is found, or in the
-- specific case of class-wide pre-/postconditions, when their
-- helpers are found.
when N_Procedure_Specification
| N_Function_Specification
=>
return Skip;
when N_Abstract_Subprogram_Declaration
| N_Aspect_Specification
| N_At_Clause
| N_Call_Marker
| N_Empty
| N_Enumeration_Representation_Clause
| N_Enumeration_Type_Definition
| N_Function_Instantiation
| N_Freeze_Generic_Entity
| N_Generic_Function_Renaming_Declaration
| N_Generic_Package_Renaming_Declaration
| N_Generic_Procedure_Renaming_Declaration
| N_Generic_Package_Declaration
| N_Generic_Subprogram_Declaration
| N_Itype_Reference
| N_Number_Declaration
| N_Package_Instantiation
| N_Package_Renaming_Declaration
| N_Pragma
| N_Procedure_Instantiation
| N_Protected_Type_Declaration
| N_Record_Representation_Clause
| N_Validate_Unchecked_Conversion
| N_Variable_Reference_Marker
| N_Use_Package_Clause
| N_Use_Type_Clause
| N_With_Clause
=>
return Skip;
when others =>
null;
end case;
return OK;
end Process_Node;
procedure Check_Calls is new Traverse_Proc (Process_Node);
-- Start of processing for Validate_Subprogram_Calls
begin
-- No action if we are not generating code (including if we have
-- errors).
if Operating_Mode /= Generate_Code then
return;
end if;
pragma Assert (Serious_Errors_Detected = 0);
-- Do not attempt to verify the return type in CodePeer_Mode
-- as CodePeer_Mode is missing some expansion code that
-- results in trees that would be considered malformed for
-- GCC but aren't for GNAT2SCIL.
if not CodePeer_Mode then
Check_Calls (N);
end if;
end Validate_Subprogram_Calls;
----------------
-- Is_FE_Only --
----------------
function Is_FE_Only (Kind : Node_Kind) return Boolean is
-- ????This is work in progress; see "?" marks below
begin
case Kind is
when N_Abortable_Part
| N_Abort_Statement
| N_Asynchronous_Select
| N_Compound_Statement
| N_Conditional_Entry_Call
| N_Continue_Statement
| N_Contract
| N_Delay_Alternative
| N_Delay_Until_Statement
| N_Delta_Constraint
| N_Entry_Call_Alternative
| N_Entry_Index_Specification
| N_Error
| N_Formal_Derived_Type_Definition
| N_Formal_Package_Declaration
| N_Goto_When_Statement
| N_Interpolated_String_Literal
| N_Iterated_Element_Association
| N_Mod_Clause
| N_Raise_When_Statement
| N_Return_When_Statement
| N_SCIL_Dispatching_Call
| N_SCIL_Dispatch_Table_Tag_Init
| N_SCIL_Membership_Test
| N_Timed_Entry_Call
| N_Triggering_Alternative
| N_Unused_At_End
| N_Unused_At_Start
=> return True;
when N_Empty
| N_Delay_Relative_Statement -- ????not turned into rt call?
| N_Expression_Function
| N_Iterated_Component_Association -- ????
| N_Single_Protected_Declaration
| N_Accept_Alternative -- ????not turned into rt call?
| N_Accept_Statement -- ????not turned into rt call?
| N_Decimal_Fixed_Point_Definition
| N_Digits_Constraint
| N_Entry_Call_Statement -- ????not turned into rt call?
| N_Requeue_Statement -- ????not turned into rt call?
| N_Selective_Accept -- ????not turned into rt call?
| N_Terminate_Alternative -- ????not turned into rt call?
| N_Defining_Character_Literal
| N_Access_Function_Definition
| N_Formal_Discrete_Type_Definition
| N_Formal_Modular_Type_Definition
| N_Iterator_Specification
| N_Op_Expon
| N_Variant
| N_Variant_Part
| N_Access_Definition
| N_Access_Procedure_Definition
| N_Access_To_Object_Definition
| N_Aspect_Specification
| N_Case_Statement_Alternative
| N_Compilation_Unit_Aux
| N_Component_Clause
| N_Component_Declaration
| N_Component_Definition
| N_Component_List
| N_Constrained_Array_Definition
| N_Derived_Type_Definition
| N_Designator
| N_Discriminant_Association
| N_Discriminant_Specification
| N_Elsif_Part
| N_Enumeration_Type_Definition
| N_Floating_Point_Definition
| N_Formal_Concrete_Subprogram_Declaration
| N_Formal_Floating_Point_Definition
| N_Formal_Object_Declaration
| N_Formal_Private_Type_Definition
| N_Formal_Signed_Integer_Type_Definition
| N_Formal_Type_Declaration
| N_Generic_Association
| N_Index_Or_Discriminant_Constraint
| N_Iteration_Scheme
| N_Loop_Parameter_Specification
| N_Modular_Type_Definition
| N_Others_Choice
| N_Parameter_Association
| N_Parameter_Specification
| N_Quantified_Expression -- ????
| N_Range
| N_Range_Constraint
| N_Record_Definition
| N_Signed_Integer_Type_Definition
| N_Subtype_Indication
| N_Unconstrained_Array_Definition
| N_Pragma_Argument_Association
| N_Case_Expression
| N_Case_Expression_Alternative
| N_Delta_Aggregate -- ????
| N_Entry_Body_Formal_Part
| N_Entry_Declaration
| N_Extended_Return_Statement -- ????
| N_Formal_Abstract_Subprogram_Declaration
| N_Formal_Decimal_Fixed_Point_Definition
| N_Formal_Incomplete_Type_Definition
| N_Formal_Ordinary_Fixed_Point_Definition
| N_Ordinary_Fixed_Point_Definition
| N_Protected_Definition
| N_Raise_Expression
| N_Real_Range_Specification
| N_Target_Name -- ????
| N_Task_Definition
=> return False;
-- ????
when N_Abstract_Subprogram_Declaration
| N_Aggregate
| N_Allocator
| N_And_Then
| N_Assignment_Statement
| N_At_Clause
| N_Attribute_Definition_Clause
| N_Attribute_Reference
| N_Block_Statement
| N_Call_Marker
| N_Case_Statement
| N_Character_Literal
| N_Code_Statement
| N_Compilation_Unit
| N_Component_Association
| N_Defining_Identifier
| N_Defining_Operator_Symbol
| N_Defining_Program_Unit_Name
| N_Entry_Body
| N_Enumeration_Representation_Clause
| N_Exception_Declaration
| N_Exception_Handler
| N_Exception_Renaming_Declaration
| N_Exit_Statement
| N_Expanded_Name
| N_Explicit_Dereference
| N_Expression_With_Actions
| N_Extension_Aggregate
| N_External_Initializer
| N_Free_Statement
| N_Freeze_Entity
| N_Freeze_Generic_Entity
| N_Full_Type_Declaration
| N_Function_Call
| N_Function_Instantiation
| N_Function_Specification
| N_Generic_Function_Renaming_Declaration
| N_Generic_Package_Declaration
| N_Generic_Package_Renaming_Declaration
| N_Generic_Procedure_Renaming_Declaration
| N_Generic_Subprogram_Declaration
| N_Goto_Statement
| N_Handled_Sequence_Of_Statements
| N_Identifier
| N_If_Expression
| N_If_Statement
| N_Implicit_Label_Declaration
| N_In
| N_Incomplete_Type_Declaration
| N_Indexed_Component
| N_Integer_Literal
| N_Itype_Reference
| N_Label
| N_Loop_Statement
| N_Not_In
| N_Null
| N_Null_Statement
| N_Number_Declaration
| N_Object_Declaration
| N_Object_Renaming_Declaration
| N_Op_Abs
| N_Op_Add
| N_Op_And
| N_Op_Concat
| N_Op_Divide
| N_Op_Eq
| N_Operator_Symbol
| N_Op_Ge
| N_Op_Gt
| N_Op_Le
| N_Op_Lt
| N_Op_Minus
| N_Op_Mod
| N_Op_Multiply
| N_Op_Ne
| N_Op_Not
| N_Op_Or
| N_Op_Plus
| N_Op_Rem
| N_Op_Rotate_Left
| N_Op_Rotate_Right
| N_Op_Shift_Left
| N_Op_Shift_Right
| N_Op_Shift_Right_Arithmetic
| N_Op_Subtract
| N_Op_Xor
| N_Or_Else
| N_Package_Body
| N_Package_Body_Stub
| N_Package_Declaration
| N_Package_Instantiation
| N_Package_Renaming_Declaration
| N_Package_Specification
| N_Pop_Constraint_Error_Label
| N_Pop_Program_Error_Label
| N_Pop_Storage_Error_Label
| N_Pragma
| N_Private_Extension_Declaration
| N_Private_Type_Declaration
| N_Procedure_Call_Statement
| N_Procedure_Instantiation
| N_Procedure_Specification
| N_Protected_Body
| N_Protected_Body_Stub
| N_Protected_Type_Declaration
| N_Push_Constraint_Error_Label
| N_Push_Program_Error_Label
| N_Push_Storage_Error_Label
| N_Qualified_Expression
| N_Raise_Constraint_Error
| N_Raise_Program_Error
| N_Raise_Statement
| N_Raise_Storage_Error
| N_Real_Literal
| N_Record_Representation_Clause
| N_Reference
| N_Selected_Component
| N_Simple_Return_Statement
| N_Single_Task_Declaration
| N_Slice
| N_String_Literal
| N_Subprogram_Body
| N_Subprogram_Body_Stub
| N_Subprogram_Declaration
| N_Subprogram_Renaming_Declaration
| N_Subtype_Declaration
| N_Subunit
| N_Task_Body
| N_Task_Body_Stub
| N_Task_Type_Declaration
| N_Type_Conversion
| N_Unchecked_Type_Conversion
| N_Use_Package_Clause
| N_Use_Type_Clause
| N_Validate_Unchecked_Conversion
| N_Variable_Reference_Marker
| N_With_Clause
=> return False;
end case;
end Is_FE_Only;
end VAST;