------------------------------------------------------------------------------ -- -- -- GNAT COMPILER COMPONENTS -- -- -- -- S E M _ S C I L -- -- -- -- B o d y -- -- -- -- Copyright (C) 2009, 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. -- -- -- ------------------------------------------------------------------------------ with Einfo; use Einfo; with Namet; use Namet; with Nlists; use Nlists; with Opt; use Opt; with Rtsfind; use Rtsfind; with Sem; use Sem; with Sem_Aux; use Sem_Aux; with Sem_Util; use Sem_Util; with Sinfo; use Sinfo; with Snames; use Snames; with Stand; use Stand; package body Sem_SCIL is ---------------------- -- Adjust_SCIL_Node -- ---------------------- procedure Adjust_SCIL_Node (Old_Node : Node_Id; New_Node : Node_Id) is SCIL_Node : Node_Id; begin pragma Assert (Generate_SCIL); -- Check cases in which no action is required. Currently the only SCIL -- nodes that may require adjustment are those of dispatching calls -- internally generated by the frontend. if Comes_From_Source (Old_Node) or else not Nkind_In (New_Node, N_Function_Call, N_Procedure_Call_Statement) then return; -- Conditional expression associated with equality operator. Old_Node -- may be part of the expansion of the predefined equality operator of -- a tagged type and hence we need to check if it has a SCIL dispatching -- node that needs adjustment. elsif Nkind (Old_Node) = N_Conditional_Expression and then (Nkind (Original_Node (Old_Node)) = N_Op_Eq or else (Nkind (Original_Node (Old_Node)) = N_Function_Call and then Chars (Name (Original_Node (Old_Node))) = Name_Op_Eq)) then null; -- Type conversions may involve dispatching calls to functions whose -- associated SCIL dispatching node needs adjustment. elsif Nkind_In (Old_Node, N_Type_Conversion, N_Unchecked_Type_Conversion) then null; -- Relocated subprogram call elsif Nkind (Old_Node) = Nkind (New_Node) and then Original_Node (Old_Node) = Original_Node (New_Node) then null; else return; end if; -- Search for the SCIL node and update it (if found) SCIL_Node := Find_SCIL_Node (Old_Node); if Present (SCIL_Node) then Set_SCIL_Related_Node (SCIL_Node, New_Node); end if; end Adjust_SCIL_Node; --------------------- -- Check_SCIL_Node -- --------------------- function Check_SCIL_Node (N : Node_Id) return Traverse_Result is Ctrl_Tag : Node_Id; Ctrl_Typ : Entity_Id; begin if Nkind (N) = N_SCIL_Membership_Test then -- Check contents of the boolean expression associated with the -- membership test. pragma Assert (Nkind (SCIL_Related_Node (N)) = N_Identifier and then Etype (SCIL_Related_Node (N)) = Standard_Boolean); -- Check the entity identifier of the associated tagged type (that -- is, in testing for membership in T'Class, the entity id of the -- specific type T). -- Note: When the SCIL node is generated the private and full-view -- of the tagged types may have been swapped and hence the node -- referenced by attribute SCIL_Entity may be the private view. -- Therefore, in order to uniformily locate the full-view we use -- attribute Underlying_Type. pragma Assert (Is_Tagged_Type (Underlying_Type (SCIL_Entity (N)))); -- Interface types are unsupported pragma Assert (not Is_Interface (Underlying_Type (SCIL_Entity (N)))); -- Check the decoration of the expression that denotes the tag value -- being tested Ctrl_Tag := SCIL_Tag_Value (N); case Nkind (Ctrl_Tag) is -- For class-wide membership tests the SCIL tag value is the tag -- of the tested object (i.e. Obj.Tag). when N_Selected_Component => pragma Assert (Etype (Ctrl_Tag) = RTE (RE_Tag)); null; when others => pragma Assert (False); null; end case; return Skip; elsif Nkind (N) = N_SCIL_Dispatching_Call then Ctrl_Tag := SCIL_Controlling_Tag (N); -- SCIL_Related_Node of SCIL dispatching call nodes MUST reference -- subprogram calls. if not Nkind_In (SCIL_Related_Node (N), N_Function_Call, N_Procedure_Call_Statement) then pragma Assert (False); raise Program_Error; -- In simple cases the controlling tag is the tag of the controlling -- argument (i.e. Obj.Tag). elsif Nkind (Ctrl_Tag) = N_Selected_Component then Ctrl_Typ := Etype (Ctrl_Tag); -- Interface types are unsupported if Is_Interface (Ctrl_Typ) or else (RTE_Available (RE_Interface_Tag) and then Ctrl_Typ = RTE (RE_Interface_Tag)) then null; else pragma Assert (Ctrl_Typ = RTE (RE_Tag)); null; end if; -- When the controlling tag of a dispatching call is an identifier -- the SCIL_Controlling_Tag attribute references the corresponding -- object or parameter declaration. Interface types are still -- unsupported. elsif Nkind_In (Ctrl_Tag, N_Object_Declaration, N_Parameter_Specification) then Ctrl_Typ := Etype (Defining_Identifier (Ctrl_Tag)); -- Interface types are unsupported. if Is_Interface (Ctrl_Typ) or else (RTE_Available (RE_Interface_Tag) and then Ctrl_Typ = RTE (RE_Interface_Tag)) or else (Is_Access_Type (Ctrl_Typ) and then Is_Interface (Available_View (Base_Type (Designated_Type (Ctrl_Typ))))) then null; else pragma Assert (Ctrl_Typ = RTE (RE_Tag) or else (Is_Access_Type (Ctrl_Typ) and then Available_View (Base_Type (Designated_Type (Ctrl_Typ))) = RTE (RE_Tag))); null; end if; -- Interface types are unsupported elsif Is_Interface (Etype (Ctrl_Tag)) then null; else pragma Assert (False); raise Program_Error; end if; return Skip; -- Node is not N_SCIL_Dispatching_Call else return OK; end if; end Check_SCIL_Node; -------------------- -- Find_SCIL_Node -- -------------------- function Find_SCIL_Node (Node : Node_Id) return Node_Id is Found_Node : Node_Id; -- This variable stores the last node found by the nested subprogram -- Find_SCIL_Node. function Find_SCIL_Node (L : List_Id) return Boolean; -- Searches in list L for a SCIL node associated with a dispatching call -- whose SCIL_Related_Node is Node. If found returns true and stores the -- SCIL node in Found_Node; otherwise returns False and sets Found_Node -- to Empty. -------------------- -- Find_SCIL_Node -- -------------------- function Find_SCIL_Node (L : List_Id) return Boolean is N : Node_Id; begin N := First (L); while Present (N) loop if Nkind (N) in N_SCIL_Node and then SCIL_Related_Node (N) = Node then Found_Node := N; return True; end if; Next (N); end loop; Found_Node := Empty; return False; end Find_SCIL_Node; -- Local variables P : Node_Id; -- Start of processing for Find_SCIL_Node begin pragma Assert (Generate_SCIL); -- Search for the SCIL node in list associated with a transient scope if Scope_Is_Transient then declare SE : Scope_Stack_Entry renames Scope_Stack.Table (Scope_Stack.Last); begin if SE.Is_Transient and then Present (SE.Actions_To_Be_Wrapped_Before) and then Find_SCIL_Node (SE.Actions_To_Be_Wrapped_Before) then return Found_Node; end if; end; end if; -- Otherwise climb up the tree searching for the SCIL node analyzing -- all the lists in which Insert_Actions may have inserted it P := Node; while Present (P) loop case Nkind (P) is -- Actions associated with AND THEN or OR ELSE when N_Short_Circuit => if Present (Actions (P)) and then Find_SCIL_Node (Actions (P)) then return Found_Node; end if; -- Actions of conditional expressions when N_Conditional_Expression => if (Present (Then_Actions (P)) and then Find_SCIL_Node (Actions (P))) or else (Present (Else_Actions (P)) and then Find_SCIL_Node (Else_Actions (P))) then return Found_Node; end if; -- Actions in handled sequence of statements when N_Handled_Sequence_Of_Statements => if Find_SCIL_Node (Statements (P)) then return Found_Node; end if; -- Conditions of while expression or elsif. when N_Iteration_Scheme | N_Elsif_Part => if Present (Condition_Actions (P)) and then Find_SCIL_Node (Condition_Actions (P)) then return Found_Node; end if; -- Statements, declarations, pragmas, representation clauses when -- Statements N_Procedure_Call_Statement | N_Statement_Other_Than_Procedure_Call | -- Pragmas N_Pragma | -- Representation_Clause N_At_Clause | N_Attribute_Definition_Clause | N_Enumeration_Representation_Clause | N_Record_Representation_Clause | -- Declarations N_Abstract_Subprogram_Declaration | N_Entry_Body | N_Exception_Declaration | N_Exception_Renaming_Declaration | N_Formal_Abstract_Subprogram_Declaration | N_Formal_Concrete_Subprogram_Declaration | N_Formal_Object_Declaration | N_Formal_Type_Declaration | N_Full_Type_Declaration | N_Function_Instantiation | N_Generic_Function_Renaming_Declaration | N_Generic_Package_Declaration | N_Generic_Package_Renaming_Declaration | N_Generic_Procedure_Renaming_Declaration | N_Generic_Subprogram_Declaration | N_Implicit_Label_Declaration | N_Incomplete_Type_Declaration | N_Number_Declaration | N_Object_Declaration | N_Object_Renaming_Declaration | N_Package_Body | N_Package_Body_Stub | N_Package_Declaration | N_Package_Instantiation | N_Package_Renaming_Declaration | N_Private_Extension_Declaration | N_Private_Type_Declaration | N_Procedure_Instantiation | N_Protected_Body | N_Protected_Body_Stub | N_Protected_Type_Declaration | N_Single_Task_Declaration | N_Subprogram_Body | N_Subprogram_Body_Stub | N_Subprogram_Declaration | N_Subprogram_Renaming_Declaration | N_Subtype_Declaration | N_Task_Body | N_Task_Body_Stub | N_Task_Type_Declaration | -- Freeze entity behaves like a declaration or statement N_Freeze_Entity => -- Do not search here if the item is not a list member if not Is_List_Member (P) then null; -- Do not search if parent of P is an N_Component_Association -- node (i.e. we are in the context of an N_Aggregate or -- N_Extension_Aggregate node). In this case the node should -- have been added before the entire aggregate. elsif Nkind (Parent (P)) = N_Component_Association then null; -- Do not search if the parent of P is either an N_Variant -- node or an N_Record_Definition node. In this case the node -- should have been added before the entire record. elsif Nkind (Parent (P)) = N_Variant or else Nkind (Parent (P)) = N_Record_Definition then null; -- Otherwise search it in the list containing this node elsif Find_SCIL_Node (List_Containing (P)) then return Found_Node; end if; -- A special case, N_Raise_xxx_Error can act either as a statement -- or a subexpression. We diferentiate them by looking at the -- Etype. It is set to Standard_Void_Type in the statement case. when N_Raise_xxx_Error => if Etype (P) = Standard_Void_Type then if Is_List_Member (P) and then Find_SCIL_Node (List_Containing (P)) then return Found_Node; end if; -- In the subexpression case, keep climbing else null; end if; -- If a component association appears within a loop created for -- an array aggregate, check if the SCIL node was added to the -- the list of nodes attached to the association. when N_Component_Association => if Nkind (Parent (P)) = N_Aggregate and then Present (Loop_Actions (P)) and then Find_SCIL_Node (Loop_Actions (P)) then return Found_Node; end if; -- Another special case, an attribute denoting a procedure call when N_Attribute_Reference => if Is_Procedure_Attribute_Name (Attribute_Name (P)) and then Find_SCIL_Node (List_Containing (P)) then return Found_Node; -- In the subexpression case, keep climbing else null; end if; -- SCIL nodes do not have subtrees and hence they can never be -- found climbing tree when N_SCIL_Dispatch_Table_Object_Init | N_SCIL_Dispatch_Table_Tag_Init | N_SCIL_Dispatching_Call | N_SCIL_Membership_Test | N_SCIL_Tag_Init => pragma Assert (False); raise Program_Error; -- For all other node types, keep climbing tree when N_Abortable_Part | N_Accept_Alternative | N_Access_Definition | N_Access_Function_Definition | N_Access_Procedure_Definition | N_Access_To_Object_Definition | N_Aggregate | N_Allocator | N_Case_Statement_Alternative | N_Character_Literal | N_Compilation_Unit | N_Compilation_Unit_Aux | N_Component_Clause | N_Component_Declaration | N_Component_Definition | N_Component_List | N_Constrained_Array_Definition | N_Decimal_Fixed_Point_Definition | N_Defining_Character_Literal | N_Defining_Identifier | N_Defining_Operator_Symbol | N_Defining_Program_Unit_Name | N_Delay_Alternative | N_Delta_Constraint | N_Derived_Type_Definition | N_Designator | N_Digits_Constraint | N_Discriminant_Association | N_Discriminant_Specification | N_Empty | N_Entry_Body_Formal_Part | N_Entry_Call_Alternative | N_Entry_Declaration | N_Entry_Index_Specification | N_Enumeration_Type_Definition | N_Error | N_Exception_Handler | N_Expanded_Name | N_Explicit_Dereference | N_Extension_Aggregate | N_Floating_Point_Definition | N_Formal_Decimal_Fixed_Point_Definition | N_Formal_Derived_Type_Definition | N_Formal_Discrete_Type_Definition | N_Formal_Floating_Point_Definition | N_Formal_Modular_Type_Definition | N_Formal_Ordinary_Fixed_Point_Definition | N_Formal_Package_Declaration | N_Formal_Private_Type_Definition | N_Formal_Signed_Integer_Type_Definition | N_Function_Call | N_Function_Specification | N_Generic_Association | N_Identifier | N_In | N_Index_Or_Discriminant_Constraint | N_Indexed_Component | N_Integer_Literal | N_Itype_Reference | N_Label | N_Loop_Parameter_Specification | N_Mod_Clause | N_Modular_Type_Definition | N_Not_In | N_Null | N_Op_Abs | N_Op_Add | N_Op_And | N_Op_Concat | N_Op_Divide | N_Op_Eq | N_Op_Expon | 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_Operator_Symbol | N_Ordinary_Fixed_Point_Definition | N_Others_Choice | N_Package_Specification | N_Parameter_Association | N_Parameter_Specification | N_Pop_Constraint_Error_Label | N_Pop_Program_Error_Label | N_Pop_Storage_Error_Label | N_Pragma_Argument_Association | N_Procedure_Specification | N_Protected_Definition | N_Push_Constraint_Error_Label | N_Push_Program_Error_Label | N_Push_Storage_Error_Label | N_Qualified_Expression | N_Range | N_Range_Constraint | N_Real_Literal | N_Real_Range_Specification | N_Record_Definition | N_Reference | N_Selected_Component | N_Signed_Integer_Type_Definition | N_Single_Protected_Declaration | N_Slice | N_String_Literal | N_Subprogram_Info | N_Subtype_Indication | N_Subunit | N_Task_Definition | N_Terminate_Alternative | N_Triggering_Alternative | N_Type_Conversion | N_Unchecked_Expression | N_Unchecked_Type_Conversion | N_Unconstrained_Array_Definition | N_Unused_At_End | N_Unused_At_Start | N_Use_Package_Clause | N_Use_Type_Clause | N_Variant | N_Variant_Part | N_Validate_Unchecked_Conversion | N_With_Clause => null; end case; -- If we fall through above tests, keep climbing tree if Nkind (Parent (P)) = N_Subunit then -- This is the proper body corresponding to a stub. Insertion done -- at the point of the stub, which is in the declarative part of -- the parent unit. P := Corresponding_Stub (Parent (P)); else P := Parent (P); end if; end loop; -- SCIL node not found return Empty; end Find_SCIL_Node; ------------------------- -- First_Non_SCIL_Node -- ------------------------- function First_Non_SCIL_Node (L : List_Id) return Node_Id is N : Node_Id; begin N := First (L); while Nkind (N) in N_SCIL_Node loop Next (N); end loop; return N; end First_Non_SCIL_Node; ------------------------ -- Next_Non_SCIL_Node -- ------------------------ function Next_Non_SCIL_Node (N : Node_Id) return Node_Id is Aux_N : Node_Id; begin Aux_N := Next (N); while Nkind (Aux_N) in N_SCIL_Node loop Next (Aux_N); end loop; return Aux_N; end Next_Non_SCIL_Node; end Sem_SCIL;