diff options
author | Javier Miranda <miranda@adacore.com> | 2021-12-17 16:43:57 +0000 |
---|---|---|
committer | Pierre-Marie de Rodat <derodat@adacore.com> | 2022-01-07 16:24:14 +0000 |
commit | 2eed8f16bfefbf50d991419cc11fe9a0e2122aa8 (patch) | |
tree | 0348cf267081ac448c0616d726ea41bb60d304dd /gcc/ada/atree.adb | |
parent | 7f4e820d3bb5b65eab4c5693bdbe0cbe5877cd85 (diff) | |
download | gcc-2eed8f16bfefbf50d991419cc11fe9a0e2122aa8.zip gcc-2eed8f16bfefbf50d991419cc11fe9a0e2122aa8.tar.gz gcc-2eed8f16bfefbf50d991419cc11fe9a0e2122aa8.tar.bz2 |
[Ada] Crash in class-wide pre/postconditions
gcc/ada/
* atree.ads (Traverse_Func_With_Parent): New generic subprogram.
(Traverse_Proc_With_Parent): Likewise.
* atree.adb (Parents_Stack): New table used to traverse trees
passing the parent field of each node.
(Internal_Traverse_With_Parent): New generic subprogram.
(Traverse_Func_With_Parent): Likewise.
(Traverse_Proc_With_Parent): Likewise.
* contracts.adb (Fix_Parents): New subprogram.
(Restore_Original_Selected_Component): Enhanced to fix the
parent field of restored nodes.
(Inherit_Condition): Adding assertions to check the parent field
of inherited conditions and to ensure that the built inherited
condition has no reference to the formals of the parent
subprogram.
* sem_util.ads, sem_util.adb (Check_Parents): New subprogram.
Diffstat (limited to 'gcc/ada/atree.adb')
-rw-r--r-- | gcc/ada/atree.adb | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/gcc/ada/atree.adb b/gcc/ada/atree.adb index b4a33cb..8bffc97 100644 --- a/gcc/ada/atree.adb +++ b/gcc/ada/atree.adb @@ -88,6 +88,23 @@ package body Atree is Table_Increment => Alloc.Node_Offsets_Increment, Table_Name => "Orig_Nodes"); + ------------------ + -- Parent Stack -- + ------------------ + + -- A separate table is used to traverse trees. It passes the parent field + -- of each node to the called process subprogram. It is defined global to + -- avoid adding performance overhead if allocated each time the traversal + -- functions are invoked. + + package Parents_Stack is new Table.Table + (Table_Component_Type => Node_Id, + Table_Index_Type => Nat, + Table_Low_Bound => 1, + Table_Initial => 256, + Table_Increment => 100, + Table_Name => "Parents_Stack"); + -------------------------- -- Paren_Count Handling -- -------------------------- @@ -135,6 +152,20 @@ package body Atree is -- Fix up parent pointers for the children of Fix_Node after a copy, -- setting them to Fix_Node when they pointed to Ref_Node. + generic + with function Process + (Parent_Node : Node_Id; + Node : Node_Id) return Traverse_Result is <>; + function Internal_Traverse_With_Parent + (Node : Node_Id) return Traverse_Final_Result; + pragma Inline (Internal_Traverse_With_Parent); + -- Internal function that provides a functionality similar to Traverse_Func + -- but extended to pass the Parent node to the called Process subprogram; + -- delegates to Traverse_Func_With_Parent the initialization of the stack + -- data structure which stores the parent nodes (cf. Parents_Stack). + -- ??? Could we factorize the common code of Internal_Traverse_Func and + -- Traverse_Func? + procedure Mark_New_Ghost_Node (N : Node_Or_Entity_Id); -- Mark arbitrary node or entity N as Ghost when it is created within a -- Ghost region. @@ -2322,6 +2353,167 @@ package body Atree is return Size_In_Slots (N) - N_Head; end Size_In_Slots_Dynamic; + ----------------------------------- + -- Internal_Traverse_With_Parent -- + ----------------------------------- + + function Internal_Traverse_With_Parent + (Node : Node_Id) return Traverse_Final_Result + is + Tail_Recursion_Counter : Natural := 0; + + procedure Pop_Parents; + -- Pop enclosing nodes of tail recursion plus the current parent. + + function Traverse_Field (Fld : Union_Id) return Traverse_Final_Result; + -- Fld is one of the Traversed fields of Nod, which is necessarily a + -- Node_Id or List_Id. It is traversed, and the result is the result of + -- this traversal. + + ----------------- + -- Pop_Parents -- + ----------------- + + procedure Pop_Parents is + begin + -- Pop the enclosing nodes of the tail recursion + + for J in 1 .. Tail_Recursion_Counter loop + Parents_Stack.Decrement_Last; + end loop; + + -- Pop the current node + + pragma Assert (Parents_Stack.Table (Parents_Stack.Last) = Node); + Parents_Stack.Decrement_Last; + end Pop_Parents; + + -------------------- + -- Traverse_Field -- + -------------------- + + function Traverse_Field (Fld : Union_Id) return Traverse_Final_Result is + begin + if Fld /= Union_Id (Empty) then + + -- Descendant is a node + + if Fld in Node_Range then + return Internal_Traverse_With_Parent (Node_Id (Fld)); + + -- Descendant is a list + + elsif Fld in List_Range then + declare + Elmt : Node_Id := First (List_Id (Fld)); + begin + while Present (Elmt) loop + if Internal_Traverse_With_Parent (Elmt) = Abandon then + return Abandon; + end if; + + Next (Elmt); + end loop; + end; + + else + raise Program_Error; + end if; + end if; + + return OK; + end Traverse_Field; + + -- Local variables + + Parent_Node : Node_Id := Parents_Stack.Table (Parents_Stack.Last); + Cur_Node : Node_Id := Node; + + -- Start of processing for Internal_Traverse_With_Parent + + begin + -- If the last field is a node, we eliminate the tail recursion by + -- jumping back to this label. This is because concatenations are + -- sometimes deeply nested, as in X1&X2&...&Xn. Gen_IL ensures that the + -- Left_Opnd field of N_Op_Concat comes last in Traversed_Fields, so the + -- tail recursion is eliminated in that case. This trick prevents us + -- from running out of stack memory in that case. We don't bother + -- eliminating the tail recursion if the last field is a list. + + <<Tail_Recurse>> + + Parents_Stack.Append (Cur_Node); + + case Process (Parent_Node, Cur_Node) is + when Abandon => + Pop_Parents; + return Abandon; + + when Skip => + Pop_Parents; + return OK; + + when OK => + null; + + when OK_Orig => + Cur_Node := Original_Node (Cur_Node); + end case; + + -- Check for empty Traversed_Fields before entering loop below, so the + -- tail recursive step won't go past the end. + + declare + Cur_Field : Offset_Array_Index := Traversed_Offset_Array'First; + Offsets : Traversed_Offset_Array renames + Traversed_Fields (Nkind (Cur_Node)); + + begin + if Offsets (Traversed_Offset_Array'First) /= No_Field_Offset then + while Offsets (Cur_Field + 1) /= No_Field_Offset loop + declare + F : constant Union_Id := + Get_Node_Field_Union (Cur_Node, Offsets (Cur_Field)); + + begin + if Traverse_Field (F) = Abandon then + Pop_Parents; + return Abandon; + end if; + end; + + Cur_Field := Cur_Field + 1; + end loop; + + declare + F : constant Union_Id := + Get_Node_Field_Union (Cur_Node, Offsets (Cur_Field)); + + begin + if F not in Node_Range then + if Traverse_Field (F) = Abandon then + Pop_Parents; + return Abandon; + end if; + + elsif F /= Empty_List_Or_Node then + -- Here is the tail recursion step, we reset Cur_Node and + -- jump back to the start of the procedure, which has the + -- same semantic effect as a call. + + Tail_Recursion_Counter := Tail_Recursion_Counter + 1; + Parent_Node := Cur_Node; + Cur_Node := Node_Id (F); + goto Tail_Recurse; + end if; + end; + end if; + end; + + Pop_Parents; + return OK; + end Internal_Traverse_With_Parent; + ------------------- -- Traverse_Func -- ------------------- @@ -2454,6 +2646,32 @@ package body Atree is return OK; end Traverse_Func; + ------------------------------- + -- Traverse_Func_With_Parent -- + ------------------------------- + + function Traverse_Func_With_Parent + (Node : Node_Id) return Traverse_Final_Result + is + function Traverse is new Internal_Traverse_With_Parent (Process); + Result : Traverse_Final_Result; + begin + -- Ensure that the Parents stack is not currently in use; required since + -- it is global and hence a tree traversal with parents must be finished + -- before the next tree traversal with parents starts. + + pragma Assert (Parents_Stack.Last = 0); + Parents_Stack.Set_Last (0); + + Parents_Stack.Append (Parent (Node)); + Result := Traverse (Node); + Parents_Stack.Decrement_Last; + + pragma Assert (Parents_Stack.Last = 0); + + return Result; + end Traverse_Func_With_Parent; + ------------------- -- Traverse_Proc -- ------------------- @@ -2466,6 +2684,18 @@ package body Atree is Discard := Traverse (Node); end Traverse_Proc; + ------------------------------- + -- Traverse_Proc_With_Parent -- + ------------------------------- + + procedure Traverse_Proc_With_Parent (Node : Node_Id) is + function Traverse is new Traverse_Func_With_Parent (Process); + Discard : Traverse_Final_Result; + pragma Warnings (Off, Discard); + begin + Discard := Traverse (Node); + end Traverse_Proc_With_Parent; + ------------ -- Unlock -- ------------ |