| ------------------------------------------------------------------------------ |
| -- -- |
| -- GNAT COMPILER COMPONENTS -- |
| -- -- |
| -- E X P _ C H 6 -- |
| -- -- |
| -- B o d y -- |
| -- -- |
| -- Copyright (C) 1992-2015, 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 Atree; use Atree; |
| with Checks; use Checks; |
| with Debug; use Debug; |
| with Einfo; use Einfo; |
| with Errout; use Errout; |
| with Elists; use Elists; |
| with Exp_Aggr; use Exp_Aggr; |
| with Exp_Atag; use Exp_Atag; |
| with Exp_Ch2; use Exp_Ch2; |
| with Exp_Ch3; use Exp_Ch3; |
| with Exp_Ch7; use Exp_Ch7; |
| with Exp_Ch9; use Exp_Ch9; |
| with Exp_Dbug; use Exp_Dbug; |
| with Exp_Disp; use Exp_Disp; |
| with Exp_Dist; use Exp_Dist; |
| with Exp_Intr; use Exp_Intr; |
| with Exp_Pakd; use Exp_Pakd; |
| with Exp_Prag; use Exp_Prag; |
| with Exp_Tss; use Exp_Tss; |
| with Exp_Unst; use Exp_Unst; |
| with Exp_Util; use Exp_Util; |
| with Freeze; use Freeze; |
| with Inline; use Inline; |
| with Lib; use Lib; |
| with Namet; use Namet; |
| with Nlists; use Nlists; |
| with Nmake; use Nmake; |
| with Opt; use Opt; |
| with Restrict; use Restrict; |
| with Rident; use Rident; |
| with Rtsfind; use Rtsfind; |
| with Sem; use Sem; |
| with Sem_Aux; use Sem_Aux; |
| with Sem_Ch6; use Sem_Ch6; |
| with Sem_Ch8; use Sem_Ch8; |
| with Sem_Ch13; use Sem_Ch13; |
| with Sem_Dim; use Sem_Dim; |
| with Sem_Disp; use Sem_Disp; |
| with Sem_Dist; use Sem_Dist; |
| with Sem_Eval; use Sem_Eval; |
| with Sem_Mech; use Sem_Mech; |
| with Sem_Res; use Sem_Res; |
| with Sem_SCIL; use Sem_SCIL; |
| with Sem_Util; use Sem_Util; |
| with Sinfo; use Sinfo; |
| with Snames; use Snames; |
| with Stand; use Stand; |
| with Stringt; use Stringt; |
| with Targparm; use Targparm; |
| with Tbuild; use Tbuild; |
| with Uintp; use Uintp; |
| with Validsw; use Validsw; |
| |
| package body Exp_Ch6 is |
| |
| ----------------------- |
| -- Local Subprograms -- |
| ----------------------- |
| |
| procedure Add_Access_Actual_To_Build_In_Place_Call |
| (Function_Call : Node_Id; |
| Function_Id : Entity_Id; |
| Return_Object : Node_Id; |
| Is_Access : Boolean := False); |
| -- Ada 2005 (AI-318-02): Apply the Unrestricted_Access attribute to the |
| -- object name given by Return_Object and add the attribute to the end of |
| -- the actual parameter list associated with the build-in-place function |
| -- call denoted by Function_Call. However, if Is_Access is True, then |
| -- Return_Object is already an access expression, in which case it's passed |
| -- along directly to the build-in-place function. Finally, if Return_Object |
| -- is empty, then pass a null literal as the actual. |
| |
| procedure Add_Unconstrained_Actuals_To_Build_In_Place_Call |
| (Function_Call : Node_Id; |
| Function_Id : Entity_Id; |
| Alloc_Form : BIP_Allocation_Form := Unspecified; |
| Alloc_Form_Exp : Node_Id := Empty; |
| Pool_Actual : Node_Id := Make_Null (No_Location)); |
| -- Ada 2005 (AI-318-02): Add the actuals needed for a build-in-place |
| -- function call that returns a caller-unknown-size result (BIP_Alloc_Form |
| -- and BIP_Storage_Pool). If Alloc_Form_Exp is present, then use it, |
| -- otherwise pass a literal corresponding to the Alloc_Form parameter |
| -- (which must not be Unspecified in that case). Pool_Actual is the |
| -- parameter to pass to BIP_Storage_Pool. |
| |
| procedure Add_Finalization_Master_Actual_To_Build_In_Place_Call |
| (Func_Call : Node_Id; |
| Func_Id : Entity_Id; |
| Ptr_Typ : Entity_Id := Empty; |
| Master_Exp : Node_Id := Empty); |
| -- Ada 2005 (AI-318-02): If the result type of a build-in-place call needs |
| -- finalization actions, add an actual parameter which is a pointer to the |
| -- finalization master of the caller. If Master_Exp is not Empty, then that |
| -- will be passed as the actual. Otherwise, if Ptr_Typ is left Empty, this |
| -- will result in an automatic "null" value for the actual. |
| |
| procedure Add_Task_Actuals_To_Build_In_Place_Call |
| (Function_Call : Node_Id; |
| Function_Id : Entity_Id; |
| Master_Actual : Node_Id; |
| Chain : Node_Id := Empty); |
| -- Ada 2005 (AI-318-02): For a build-in-place call, if the result type |
| -- contains tasks, add two actual parameters: the master, and a pointer to |
| -- the caller's activation chain. Master_Actual is the actual parameter |
| -- expression to pass for the master. In most cases, this is the current |
| -- master (_master). The two exceptions are: If the function call is the |
| -- initialization expression for an allocator, we pass the master of the |
| -- access type. If the function call is the initialization expression for a |
| -- return object, we pass along the master passed in by the caller. In most |
| -- contexts, the activation chain to pass is the local one, which is |
| -- indicated by No (Chain). However, in an allocator, the caller passes in |
| -- the activation Chain. Note: Master_Actual can be Empty, but only if |
| -- there are no tasks. |
| |
| procedure Check_Overriding_Operation (Subp : Entity_Id); |
| -- Subp is a dispatching operation. Check whether it may override an |
| -- inherited private operation, in which case its DT entry is that of |
| -- the hidden operation, not the one it may have received earlier. |
| -- This must be done before emitting the code to set the corresponding |
| -- DT to the address of the subprogram. The actual placement of Subp in |
| -- the proper place in the list of primitive operations is done in |
| -- Declare_Inherited_Private_Subprograms, which also has to deal with |
| -- implicit operations. This duplication is unavoidable for now??? |
| |
| procedure Detect_Infinite_Recursion (N : Node_Id; Spec : Entity_Id); |
| -- This procedure is called only if the subprogram body N, whose spec |
| -- has the given entity Spec, contains a parameterless recursive call. |
| -- It attempts to generate runtime code to detect if this a case of |
| -- infinite recursion. |
| -- |
| -- The body is scanned to determine dependencies. If the only external |
| -- dependencies are on a small set of scalar variables, then the values |
| -- of these variables are captured on entry to the subprogram, and if |
| -- the values are not changed for the call, we know immediately that |
| -- we have an infinite recursion. |
| |
| procedure Expand_Actuals (N : in out Node_Id; Subp : Entity_Id); |
| -- For each actual of an in-out or out parameter which is a numeric |
| -- (view) conversion of the form T (A), where A denotes a variable, |
| -- we insert the declaration: |
| -- |
| -- Temp : T[ := T (A)]; |
| -- |
| -- prior to the call. Then we replace the actual with a reference to Temp, |
| -- and append the assignment: |
| -- |
| -- A := TypeA (Temp); |
| -- |
| -- after the call. Here TypeA is the actual type of variable A. For out |
| -- parameters, the initial declaration has no expression. If A is not an |
| -- entity name, we generate instead: |
| -- |
| -- Var : TypeA renames A; |
| -- Temp : T := Var; -- omitting expression for out parameter. |
| -- ... |
| -- Var := TypeA (Temp); |
| -- |
| -- For other in-out parameters, we emit the required constraint checks |
| -- before and/or after the call. |
| -- |
| -- For all parameter modes, actuals that denote components and slices of |
| -- packed arrays are expanded into suitable temporaries. |
| -- |
| -- For non-scalar objects that are possibly unaligned, add call by copy |
| -- code (copy in for IN and IN OUT, copy out for OUT and IN OUT). |
| -- |
| -- For OUT and IN OUT parameters, add predicate checks after the call |
| -- based on the predicates of the actual type. |
| -- |
| -- The parameter N is IN OUT because in some cases, the expansion code |
| -- rewrites the call as an expression actions with the call inside. In |
| -- this case N is reset to point to the inside call so that the caller |
| -- can continue processing of this call. |
| |
| procedure Expand_Ctrl_Function_Call (N : Node_Id); |
| -- N is a function call which returns a controlled object. Transform the |
| -- call into a temporary which retrieves the returned object from the |
| -- secondary stack using 'reference. |
| |
| procedure Expand_Non_Function_Return (N : Node_Id); |
| -- Expand a simple return statement found in a procedure body, entry body, |
| -- accept statement, or an extended return statement. Note that all non- |
| -- function returns are simple return statements. |
| |
| function Expand_Protected_Object_Reference |
| (N : Node_Id; |
| Scop : Entity_Id) return Node_Id; |
| |
| procedure Expand_Protected_Subprogram_Call |
| (N : Node_Id; |
| Subp : Entity_Id; |
| Scop : Entity_Id); |
| -- A call to a protected subprogram within the protected object may appear |
| -- as a regular call. The list of actuals must be expanded to contain a |
| -- reference to the object itself, and the call becomes a call to the |
| -- corresponding protected subprogram. |
| |
| function Has_Unconstrained_Access_Discriminants |
| (Subtyp : Entity_Id) return Boolean; |
| -- Returns True if the given subtype is unconstrained and has one |
| -- or more access discriminants. |
| |
| procedure Expand_Simple_Function_Return (N : Node_Id); |
| -- Expand simple return from function. In the case where we are returning |
| -- from a function body this is called by Expand_N_Simple_Return_Statement. |
| |
| ---------------------------------------------- |
| -- Add_Access_Actual_To_Build_In_Place_Call -- |
| ---------------------------------------------- |
| |
| procedure Add_Access_Actual_To_Build_In_Place_Call |
| (Function_Call : Node_Id; |
| Function_Id : Entity_Id; |
| Return_Object : Node_Id; |
| Is_Access : Boolean := False) |
| is |
| Loc : constant Source_Ptr := Sloc (Function_Call); |
| Obj_Address : Node_Id; |
| Obj_Acc_Formal : Entity_Id; |
| |
| begin |
| -- Locate the implicit access parameter in the called function |
| |
| Obj_Acc_Formal := Build_In_Place_Formal (Function_Id, BIP_Object_Access); |
| |
| -- If no return object is provided, then pass null |
| |
| if not Present (Return_Object) then |
| Obj_Address := Make_Null (Loc); |
| Set_Parent (Obj_Address, Function_Call); |
| |
| -- If Return_Object is already an expression of an access type, then use |
| -- it directly, since it must be an access value denoting the return |
| -- object, and couldn't possibly be the return object itself. |
| |
| elsif Is_Access then |
| Obj_Address := Return_Object; |
| Set_Parent (Obj_Address, Function_Call); |
| |
| -- Apply Unrestricted_Access to caller's return object |
| |
| else |
| Obj_Address := |
| Make_Attribute_Reference (Loc, |
| Prefix => Return_Object, |
| Attribute_Name => Name_Unrestricted_Access); |
| |
| Set_Parent (Return_Object, Obj_Address); |
| Set_Parent (Obj_Address, Function_Call); |
| end if; |
| |
| Analyze_And_Resolve (Obj_Address, Etype (Obj_Acc_Formal)); |
| |
| -- Build the parameter association for the new actual and add it to the |
| -- end of the function's actuals. |
| |
| Add_Extra_Actual_To_Call (Function_Call, Obj_Acc_Formal, Obj_Address); |
| end Add_Access_Actual_To_Build_In_Place_Call; |
| |
| ------------------------------------------------------ |
| -- Add_Unconstrained_Actuals_To_Build_In_Place_Call -- |
| ------------------------------------------------------ |
| |
| procedure Add_Unconstrained_Actuals_To_Build_In_Place_Call |
| (Function_Call : Node_Id; |
| Function_Id : Entity_Id; |
| Alloc_Form : BIP_Allocation_Form := Unspecified; |
| Alloc_Form_Exp : Node_Id := Empty; |
| Pool_Actual : Node_Id := Make_Null (No_Location)) |
| is |
| Loc : constant Source_Ptr := Sloc (Function_Call); |
| Alloc_Form_Actual : Node_Id; |
| Alloc_Form_Formal : Node_Id; |
| Pool_Formal : Node_Id; |
| |
| begin |
| -- The allocation form generally doesn't need to be passed in the case |
| -- of a constrained result subtype, since normally the caller performs |
| -- the allocation in that case. However this formal is still needed in |
| -- the case where the function has a tagged result, because generally |
| -- such functions can be called in a dispatching context and such calls |
| -- must be handled like calls to class-wide functions. |
| |
| if Is_Constrained (Underlying_Type (Etype (Function_Id))) |
| and then not Is_Tagged_Type (Underlying_Type (Etype (Function_Id))) |
| then |
| return; |
| end if; |
| |
| -- Locate the implicit allocation form parameter in the called function. |
| -- Maybe it would be better for each implicit formal of a build-in-place |
| -- function to have a flag or a Uint attribute to identify it. ??? |
| |
| Alloc_Form_Formal := Build_In_Place_Formal (Function_Id, BIP_Alloc_Form); |
| |
| if Present (Alloc_Form_Exp) then |
| pragma Assert (Alloc_Form = Unspecified); |
| |
| Alloc_Form_Actual := Alloc_Form_Exp; |
| |
| else |
| pragma Assert (Alloc_Form /= Unspecified); |
| |
| Alloc_Form_Actual := |
| Make_Integer_Literal (Loc, |
| Intval => UI_From_Int (BIP_Allocation_Form'Pos (Alloc_Form))); |
| end if; |
| |
| Analyze_And_Resolve (Alloc_Form_Actual, Etype (Alloc_Form_Formal)); |
| |
| -- Build the parameter association for the new actual and add it to the |
| -- end of the function's actuals. |
| |
| Add_Extra_Actual_To_Call |
| (Function_Call, Alloc_Form_Formal, Alloc_Form_Actual); |
| |
| -- Pass the Storage_Pool parameter. This parameter is omitted on |
| -- .NET/JVM/ZFP as those targets do not support pools. |
| |
| if VM_Target = No_VM |
| and then RTE_Available (RE_Root_Storage_Pool_Ptr) |
| then |
| Pool_Formal := Build_In_Place_Formal (Function_Id, BIP_Storage_Pool); |
| Analyze_And_Resolve (Pool_Actual, Etype (Pool_Formal)); |
| Add_Extra_Actual_To_Call |
| (Function_Call, Pool_Formal, Pool_Actual); |
| end if; |
| end Add_Unconstrained_Actuals_To_Build_In_Place_Call; |
| |
| ----------------------------------------------------------- |
| -- Add_Finalization_Master_Actual_To_Build_In_Place_Call -- |
| ----------------------------------------------------------- |
| |
| procedure Add_Finalization_Master_Actual_To_Build_In_Place_Call |
| (Func_Call : Node_Id; |
| Func_Id : Entity_Id; |
| Ptr_Typ : Entity_Id := Empty; |
| Master_Exp : Node_Id := Empty) |
| is |
| begin |
| if not Needs_BIP_Finalization_Master (Func_Id) then |
| return; |
| end if; |
| |
| declare |
| Formal : constant Entity_Id := |
| Build_In_Place_Formal (Func_Id, BIP_Finalization_Master); |
| Loc : constant Source_Ptr := Sloc (Func_Call); |
| |
| Actual : Node_Id; |
| Desig_Typ : Entity_Id; |
| |
| begin |
| -- If there is a finalization master actual, such as the implicit |
| -- finalization master of an enclosing build-in-place function, |
| -- then this must be added as an extra actual of the call. |
| |
| if Present (Master_Exp) then |
| Actual := Master_Exp; |
| |
| -- Case where the context does not require an actual master |
| |
| elsif No (Ptr_Typ) then |
| Actual := Make_Null (Loc); |
| |
| else |
| Desig_Typ := Directly_Designated_Type (Ptr_Typ); |
| |
| -- Check for a library-level access type whose designated type has |
| -- supressed finalization. Such an access types lack a master. |
| -- Pass a null actual to the callee in order to signal a missing |
| -- master. |
| |
| if Is_Library_Level_Entity (Ptr_Typ) |
| and then Finalize_Storage_Only (Desig_Typ) |
| then |
| Actual := Make_Null (Loc); |
| |
| -- Types in need of finalization actions |
| |
| elsif Needs_Finalization (Desig_Typ) then |
| |
| -- The general mechanism of creating finalization masters for |
| -- anonymous access types is disabled by default, otherwise |
| -- finalization masters will pop all over the place. Such types |
| -- use context-specific masters. |
| |
| if Ekind (Ptr_Typ) = E_Anonymous_Access_Type |
| and then No (Finalization_Master (Ptr_Typ)) |
| then |
| Build_Finalization_Master |
| (Typ => Ptr_Typ, |
| For_Anonymous => True, |
| Context_Scope => Scope (Ptr_Typ), |
| Insertion_Node => Associated_Node_For_Itype (Ptr_Typ)); |
| end if; |
| |
| -- Access-to-controlled types should always have a master |
| |
| pragma Assert (Present (Finalization_Master (Ptr_Typ))); |
| |
| Actual := |
| Make_Attribute_Reference (Loc, |
| Prefix => |
| New_Occurrence_Of (Finalization_Master (Ptr_Typ), Loc), |
| Attribute_Name => Name_Unrestricted_Access); |
| |
| -- Tagged types |
| |
| else |
| Actual := Make_Null (Loc); |
| end if; |
| end if; |
| |
| Analyze_And_Resolve (Actual, Etype (Formal)); |
| |
| -- Build the parameter association for the new actual and add it to |
| -- the end of the function's actuals. |
| |
| Add_Extra_Actual_To_Call (Func_Call, Formal, Actual); |
| end; |
| end Add_Finalization_Master_Actual_To_Build_In_Place_Call; |
| |
| ------------------------------ |
| -- Add_Extra_Actual_To_Call -- |
| ------------------------------ |
| |
| procedure Add_Extra_Actual_To_Call |
| (Subprogram_Call : Node_Id; |
| Extra_Formal : Entity_Id; |
| Extra_Actual : Node_Id) |
| is |
| Loc : constant Source_Ptr := Sloc (Subprogram_Call); |
| Param_Assoc : Node_Id; |
| |
| begin |
| Param_Assoc := |
| Make_Parameter_Association (Loc, |
| Selector_Name => New_Occurrence_Of (Extra_Formal, Loc), |
| Explicit_Actual_Parameter => Extra_Actual); |
| |
| Set_Parent (Param_Assoc, Subprogram_Call); |
| Set_Parent (Extra_Actual, Param_Assoc); |
| |
| if Present (Parameter_Associations (Subprogram_Call)) then |
| if Nkind (Last (Parameter_Associations (Subprogram_Call))) = |
| N_Parameter_Association |
| then |
| |
| -- Find last named actual, and append |
| |
| declare |
| L : Node_Id; |
| begin |
| L := First_Actual (Subprogram_Call); |
| while Present (L) loop |
| if No (Next_Actual (L)) then |
| Set_Next_Named_Actual (Parent (L), Extra_Actual); |
| exit; |
| end if; |
| Next_Actual (L); |
| end loop; |
| end; |
| |
| else |
| Set_First_Named_Actual (Subprogram_Call, Extra_Actual); |
| end if; |
| |
| Append (Param_Assoc, To => Parameter_Associations (Subprogram_Call)); |
| |
| else |
| Set_Parameter_Associations (Subprogram_Call, New_List (Param_Assoc)); |
| Set_First_Named_Actual (Subprogram_Call, Extra_Actual); |
| end if; |
| end Add_Extra_Actual_To_Call; |
| |
| --------------------------------------------- |
| -- Add_Task_Actuals_To_Build_In_Place_Call -- |
| --------------------------------------------- |
| |
| procedure Add_Task_Actuals_To_Build_In_Place_Call |
| (Function_Call : Node_Id; |
| Function_Id : Entity_Id; |
| Master_Actual : Node_Id; |
| Chain : Node_Id := Empty) |
| is |
| Loc : constant Source_Ptr := Sloc (Function_Call); |
| Result_Subt : constant Entity_Id := |
| Available_View (Etype (Function_Id)); |
| Actual : Node_Id; |
| Chain_Actual : Node_Id; |
| Chain_Formal : Node_Id; |
| Master_Formal : Node_Id; |
| |
| begin |
| -- No such extra parameters are needed if there are no tasks |
| |
| if not Has_Task (Result_Subt) then |
| return; |
| end if; |
| |
| Actual := Master_Actual; |
| |
| -- Use a dummy _master actual in case of No_Task_Hierarchy |
| |
| if Restriction_Active (No_Task_Hierarchy) then |
| Actual := New_Occurrence_Of (RTE (RE_Library_Task_Level), Loc); |
| |
| -- In the case where we use the master associated with an access type, |
| -- the actual is an entity and requires an explicit reference. |
| |
| elsif Nkind (Actual) = N_Defining_Identifier then |
| Actual := New_Occurrence_Of (Actual, Loc); |
| end if; |
| |
| -- Locate the implicit master parameter in the called function |
| |
| Master_Formal := Build_In_Place_Formal (Function_Id, BIP_Task_Master); |
| Analyze_And_Resolve (Actual, Etype (Master_Formal)); |
| |
| -- Build the parameter association for the new actual and add it to the |
| -- end of the function's actuals. |
| |
| Add_Extra_Actual_To_Call (Function_Call, Master_Formal, Actual); |
| |
| -- Locate the implicit activation chain parameter in the called function |
| |
| Chain_Formal := |
| Build_In_Place_Formal (Function_Id, BIP_Activation_Chain); |
| |
| -- Create the actual which is a pointer to the current activation chain |
| |
| if No (Chain) then |
| Chain_Actual := |
| Make_Attribute_Reference (Loc, |
| Prefix => Make_Identifier (Loc, Name_uChain), |
| Attribute_Name => Name_Unrestricted_Access); |
| |
| -- Allocator case; make a reference to the Chain passed in by the caller |
| |
| else |
| Chain_Actual := |
| Make_Attribute_Reference (Loc, |
| Prefix => New_Occurrence_Of (Chain, Loc), |
| Attribute_Name => Name_Unrestricted_Access); |
| end if; |
| |
| Analyze_And_Resolve (Chain_Actual, Etype (Chain_Formal)); |
| |
| -- Build the parameter association for the new actual and add it to the |
| -- end of the function's actuals. |
| |
| Add_Extra_Actual_To_Call (Function_Call, Chain_Formal, Chain_Actual); |
| end Add_Task_Actuals_To_Build_In_Place_Call; |
| |
| ----------------------- |
| -- BIP_Formal_Suffix -- |
| ----------------------- |
| |
| function BIP_Formal_Suffix (Kind : BIP_Formal_Kind) return String is |
| begin |
| case Kind is |
| when BIP_Alloc_Form => |
| return "BIPalloc"; |
| when BIP_Storage_Pool => |
| return "BIPstoragepool"; |
| when BIP_Finalization_Master => |
| return "BIPfinalizationmaster"; |
| when BIP_Task_Master => |
| return "BIPtaskmaster"; |
| when BIP_Activation_Chain => |
| return "BIPactivationchain"; |
| when BIP_Object_Access => |
| return "BIPaccess"; |
| end case; |
| end BIP_Formal_Suffix; |
| |
| --------------------------- |
| -- Build_In_Place_Formal -- |
| --------------------------- |
| |
| function Build_In_Place_Formal |
| (Func : Entity_Id; |
| Kind : BIP_Formal_Kind) return Entity_Id |
| is |
| Formal_Name : constant Name_Id := |
| New_External_Name |
| (Chars (Func), BIP_Formal_Suffix (Kind)); |
| Extra_Formal : Entity_Id := Extra_Formals (Func); |
| |
| begin |
| -- Maybe it would be better for each implicit formal of a build-in-place |
| -- function to have a flag or a Uint attribute to identify it. ??? |
| |
| -- The return type in the function declaration may have been a limited |
| -- view, and the extra formals for the function were not generated at |
| -- that point. At the point of call the full view must be available and |
| -- the extra formals can be created. |
| |
| if No (Extra_Formal) then |
| Create_Extra_Formals (Func); |
| Extra_Formal := Extra_Formals (Func); |
| end if; |
| |
| loop |
| pragma Assert (Present (Extra_Formal)); |
| exit when Chars (Extra_Formal) = Formal_Name; |
| |
| Next_Formal_With_Extras (Extra_Formal); |
| end loop; |
| |
| return Extra_Formal; |
| end Build_In_Place_Formal; |
| |
| -------------------------------- |
| -- Check_Overriding_Operation -- |
| -------------------------------- |
| |
| procedure Check_Overriding_Operation (Subp : Entity_Id) is |
| Typ : constant Entity_Id := Find_Dispatching_Type (Subp); |
| Op_List : constant Elist_Id := Primitive_Operations (Typ); |
| Op_Elmt : Elmt_Id; |
| Prim_Op : Entity_Id; |
| Par_Op : Entity_Id; |
| |
| begin |
| if Is_Derived_Type (Typ) |
| and then not Is_Private_Type (Typ) |
| and then In_Open_Scopes (Scope (Etype (Typ))) |
| and then Is_Base_Type (Typ) |
| then |
| -- Subp overrides an inherited private operation if there is an |
| -- inherited operation with a different name than Subp (see |
| -- Derive_Subprogram) whose Alias is a hidden subprogram with the |
| -- same name as Subp. |
| |
| Op_Elmt := First_Elmt (Op_List); |
| while Present (Op_Elmt) loop |
| Prim_Op := Node (Op_Elmt); |
| Par_Op := Alias (Prim_Op); |
| |
| if Present (Par_Op) |
| and then not Comes_From_Source (Prim_Op) |
| and then Chars (Prim_Op) /= Chars (Par_Op) |
| and then Chars (Par_Op) = Chars (Subp) |
| and then Is_Hidden (Par_Op) |
| and then Type_Conformant (Prim_Op, Subp) |
| then |
| Set_DT_Position_Value (Subp, DT_Position (Prim_Op)); |
| end if; |
| |
| Next_Elmt (Op_Elmt); |
| end loop; |
| end if; |
| end Check_Overriding_Operation; |
| |
| ------------------------------- |
| -- Detect_Infinite_Recursion -- |
| ------------------------------- |
| |
| procedure Detect_Infinite_Recursion (N : Node_Id; Spec : Entity_Id) is |
| Loc : constant Source_Ptr := Sloc (N); |
| |
| Var_List : constant Elist_Id := New_Elmt_List; |
| -- List of globals referenced by body of procedure |
| |
| Call_List : constant Elist_Id := New_Elmt_List; |
| -- List of recursive calls in body of procedure |
| |
| Shad_List : constant Elist_Id := New_Elmt_List; |
| -- List of entity id's for entities created to capture the value of |
| -- referenced globals on entry to the procedure. |
| |
| Scop : constant Uint := Scope_Depth (Spec); |
| -- This is used to record the scope depth of the current procedure, so |
| -- that we can identify global references. |
| |
| Max_Vars : constant := 4; |
| -- Do not test more than four global variables |
| |
| Count_Vars : Natural := 0; |
| -- Count variables found so far |
| |
| Var : Entity_Id; |
| Elm : Elmt_Id; |
| Ent : Entity_Id; |
| Call : Elmt_Id; |
| Decl : Node_Id; |
| Test : Node_Id; |
| Elm1 : Elmt_Id; |
| Elm2 : Elmt_Id; |
| Last : Node_Id; |
| |
| function Process (Nod : Node_Id) return Traverse_Result; |
| -- Function to traverse the subprogram body (using Traverse_Func) |
| |
| ------------- |
| -- Process -- |
| ------------- |
| |
| function Process (Nod : Node_Id) return Traverse_Result is |
| begin |
| -- Procedure call |
| |
| if Nkind (Nod) = N_Procedure_Call_Statement then |
| |
| -- Case of one of the detected recursive calls |
| |
| if Is_Entity_Name (Name (Nod)) |
| and then Has_Recursive_Call (Entity (Name (Nod))) |
| and then Entity (Name (Nod)) = Spec |
| then |
| Append_Elmt (Nod, Call_List); |
| return Skip; |
| |
| -- Any other procedure call may have side effects |
| |
| else |
| return Abandon; |
| end if; |
| |
| -- A call to a pure function can always be ignored |
| |
| elsif Nkind (Nod) = N_Function_Call |
| and then Is_Entity_Name (Name (Nod)) |
| and then Is_Pure (Entity (Name (Nod))) |
| then |
| return Skip; |
| |
| -- Case of an identifier reference |
| |
| elsif Nkind (Nod) = N_Identifier then |
| Ent := Entity (Nod); |
| |
| -- If no entity, then ignore the reference |
| |
| -- Not clear why this can happen. To investigate, remove this |
| -- test and look at the crash that occurs here in 3401-004 ??? |
| |
| if No (Ent) then |
| return Skip; |
| |
| -- Ignore entities with no Scope, again not clear how this |
| -- can happen, to investigate, look at 4108-008 ??? |
| |
| elsif No (Scope (Ent)) then |
| return Skip; |
| |
| -- Ignore the reference if not to a more global object |
| |
| elsif Scope_Depth (Scope (Ent)) >= Scop then |
| return Skip; |
| |
| -- References to types, exceptions and constants are always OK |
| |
| elsif Is_Type (Ent) |
| or else Ekind (Ent) = E_Exception |
| or else Ekind (Ent) = E_Constant |
| then |
| return Skip; |
| |
| -- If other than a non-volatile scalar variable, we have some |
| -- kind of global reference (e.g. to a function) that we cannot |
| -- deal with so we forget the attempt. |
| |
| elsif Ekind (Ent) /= E_Variable |
| or else not Is_Scalar_Type (Etype (Ent)) |
| or else Treat_As_Volatile (Ent) |
| then |
| return Abandon; |
| |
| -- Otherwise we have a reference to a global scalar |
| |
| else |
| -- Loop through global entities already detected |
| |
| Elm := First_Elmt (Var_List); |
| loop |
| -- If not detected before, record this new global reference |
| |
| if No (Elm) then |
| Count_Vars := Count_Vars + 1; |
| |
| if Count_Vars <= Max_Vars then |
| Append_Elmt (Entity (Nod), Var_List); |
| else |
| return Abandon; |
| end if; |
| |
| exit; |
| |
| -- If recorded before, ignore |
| |
| elsif Node (Elm) = Entity (Nod) then |
| return Skip; |
| |
| -- Otherwise keep looking |
| |
| else |
| Next_Elmt (Elm); |
| end if; |
| end loop; |
| |
| return Skip; |
| end if; |
| |
| -- For all other node kinds, recursively visit syntactic children |
| |
| else |
| return OK; |
| end if; |
| end Process; |
| |
| function Traverse_Body is new Traverse_Func (Process); |
| |
| -- Start of processing for Detect_Infinite_Recursion |
| |
| begin |
| -- Do not attempt detection in No_Implicit_Conditional mode, since we |
| -- won't be able to generate the code to handle the recursion in any |
| -- case. |
| |
| if Restriction_Active (No_Implicit_Conditionals) then |
| return; |
| end if; |
| |
| -- Otherwise do traversal and quit if we get abandon signal |
| |
| if Traverse_Body (N) = Abandon then |
| return; |
| |
| -- We must have a call, since Has_Recursive_Call was set. If not just |
| -- ignore (this is only an error check, so if we have a funny situation, |
| -- due to bugs or errors, we do not want to bomb). |
| |
| elsif Is_Empty_Elmt_List (Call_List) then |
| return; |
| end if; |
| |
| -- Here is the case where we detect recursion at compile time |
| |
| -- Push our current scope for analyzing the declarations and code that |
| -- we will insert for the checking. |
| |
| Push_Scope (Spec); |
| |
| -- This loop builds temporary variables for each of the referenced |
| -- globals, so that at the end of the loop the list Shad_List contains |
| -- these temporaries in one-to-one correspondence with the elements in |
| -- Var_List. |
| |
| Last := Empty; |
| Elm := First_Elmt (Var_List); |
| while Present (Elm) loop |
| Var := Node (Elm); |
| Ent := Make_Temporary (Loc, 'S'); |
| Append_Elmt (Ent, Shad_List); |
| |
| -- Insert a declaration for this temporary at the start of the |
| -- declarations for the procedure. The temporaries are declared as |
| -- constant objects initialized to the current values of the |
| -- corresponding temporaries. |
| |
| Decl := |
| Make_Object_Declaration (Loc, |
| Defining_Identifier => Ent, |
| Object_Definition => New_Occurrence_Of (Etype (Var), Loc), |
| Constant_Present => True, |
| Expression => New_Occurrence_Of (Var, Loc)); |
| |
| if No (Last) then |
| Prepend (Decl, Declarations (N)); |
| else |
| Insert_After (Last, Decl); |
| end if; |
| |
| Last := Decl; |
| Analyze (Decl); |
| Next_Elmt (Elm); |
| end loop; |
| |
| -- Loop through calls |
| |
| Call := First_Elmt (Call_List); |
| while Present (Call) loop |
| |
| -- Build a predicate expression of the form |
| |
| -- True |
| -- and then global1 = temp1 |
| -- and then global2 = temp2 |
| -- ... |
| |
| -- This predicate determines if any of the global values |
| -- referenced by the procedure have changed since the |
| -- current call, if not an infinite recursion is assured. |
| |
| Test := New_Occurrence_Of (Standard_True, Loc); |
| |
| Elm1 := First_Elmt (Var_List); |
| Elm2 := First_Elmt (Shad_List); |
| while Present (Elm1) loop |
| Test := |
| Make_And_Then (Loc, |
| Left_Opnd => Test, |
| Right_Opnd => |
| Make_Op_Eq (Loc, |
| Left_Opnd => New_Occurrence_Of (Node (Elm1), Loc), |
| Right_Opnd => New_Occurrence_Of (Node (Elm2), Loc))); |
| |
| Next_Elmt (Elm1); |
| Next_Elmt (Elm2); |
| end loop; |
| |
| -- Now we replace the call with the sequence |
| |
| -- if no-changes (see above) then |
| -- raise Storage_Error; |
| -- else |
| -- original-call |
| -- end if; |
| |
| Rewrite (Node (Call), |
| Make_If_Statement (Loc, |
| Condition => Test, |
| Then_Statements => New_List ( |
| Make_Raise_Storage_Error (Loc, |
| Reason => SE_Infinite_Recursion)), |
| |
| Else_Statements => New_List ( |
| Relocate_Node (Node (Call))))); |
| |
| Analyze (Node (Call)); |
| |
| Next_Elmt (Call); |
| end loop; |
| |
| -- Remove temporary scope stack entry used for analysis |
| |
| Pop_Scope; |
| end Detect_Infinite_Recursion; |
| |
| -------------------- |
| -- Expand_Actuals -- |
| -------------------- |
| |
| -------------------- |
| -- Expand_Actuals -- |
| -------------------- |
| |
| procedure Expand_Actuals (N : in out Node_Id; Subp : Entity_Id) is |
| Loc : constant Source_Ptr := Sloc (N); |
| Actual : Node_Id; |
| Formal : Entity_Id; |
| N_Node : Node_Id; |
| Post_Call : List_Id; |
| E_Actual : Entity_Id; |
| E_Formal : Entity_Id; |
| |
| procedure Add_Call_By_Copy_Code; |
| -- For cases where the parameter must be passed by copy, this routine |
| -- generates a temporary variable into which the actual is copied and |
| -- then passes this as the parameter. For an OUT or IN OUT parameter, |
| -- an assignment is also generated to copy the result back. The call |
| -- also takes care of any constraint checks required for the type |
| -- conversion case (on both the way in and the way out). |
| |
| procedure Add_Simple_Call_By_Copy_Code; |
| -- This is similar to the above, but is used in cases where we know |
| -- that all that is needed is to simply create a temporary and copy |
| -- the value in and out of the temporary. |
| |
| procedure Check_Fortran_Logical; |
| -- A value of type Logical that is passed through a formal parameter |
| -- must be normalized because .TRUE. usually does not have the same |
| -- representation as True. We assume that .FALSE. = False = 0. |
| -- What about functions that return a logical type ??? |
| |
| function Is_Legal_Copy return Boolean; |
| -- Check that an actual can be copied before generating the temporary |
| -- to be used in the call. If the actual is of a by_reference type then |
| -- the program is illegal (this can only happen in the presence of |
| -- rep. clauses that force an incorrect alignment). If the formal is |
| -- a by_reference parameter imposed by a DEC pragma, emit a warning to |
| -- the effect that this might lead to unaligned arguments. |
| |
| function Make_Var (Actual : Node_Id) return Entity_Id; |
| -- Returns an entity that refers to the given actual parameter, Actual |
| -- (not including any type conversion). If Actual is an entity name, |
| -- then this entity is returned unchanged, otherwise a renaming is |
| -- created to provide an entity for the actual. |
| |
| procedure Reset_Packed_Prefix; |
| -- The expansion of a packed array component reference is delayed in |
| -- the context of a call. Now we need to complete the expansion, so we |
| -- unmark the analyzed bits in all prefixes. |
| |
| --------------------------- |
| -- Add_Call_By_Copy_Code -- |
| --------------------------- |
| |
| procedure Add_Call_By_Copy_Code is |
| Expr : Node_Id; |
| Init : Node_Id; |
| Temp : Entity_Id; |
| Indic : Node_Id; |
| Var : Entity_Id; |
| F_Typ : constant Entity_Id := Etype (Formal); |
| V_Typ : Entity_Id; |
| Crep : Boolean; |
| |
| begin |
| if not Is_Legal_Copy then |
| return; |
| end if; |
| |
| Temp := Make_Temporary (Loc, 'T', Actual); |
| |
| -- Use formal type for temp, unless formal type is an unconstrained |
| -- array, in which case we don't have to worry about bounds checks, |
| -- and we use the actual type, since that has appropriate bounds. |
| |
| if Is_Array_Type (F_Typ) and then not Is_Constrained (F_Typ) then |
| Indic := New_Occurrence_Of (Etype (Actual), Loc); |
| else |
| Indic := New_Occurrence_Of (Etype (Formal), Loc); |
| end if; |
| |
| if Nkind (Actual) = N_Type_Conversion then |
| V_Typ := Etype (Expression (Actual)); |
| |
| -- If the formal is an (in-)out parameter, capture the name |
| -- of the variable in order to build the post-call assignment. |
| |
| Var := Make_Var (Expression (Actual)); |
| |
| Crep := not Same_Representation |
| (F_Typ, Etype (Expression (Actual))); |
| |
| else |
| V_Typ := Etype (Actual); |
| Var := Make_Var (Actual); |
| Crep := False; |
| end if; |
| |
| -- Setup initialization for case of in out parameter, or an out |
| -- parameter where the formal is an unconstrained array (in the |
| -- latter case, we have to pass in an object with bounds). |
| |
| -- If this is an out parameter, the initial copy is wasteful, so as |
| -- an optimization for the one-dimensional case we extract the |
| -- bounds of the actual and build an uninitialized temporary of the |
| -- right size. |
| |
| if Ekind (Formal) = E_In_Out_Parameter |
| or else (Is_Array_Type (F_Typ) and then not Is_Constrained (F_Typ)) |
| then |
| if Nkind (Actual) = N_Type_Conversion then |
| if Conversion_OK (Actual) then |
| Init := OK_Convert_To (F_Typ, New_Occurrence_Of (Var, Loc)); |
| else |
| Init := Convert_To (F_Typ, New_Occurrence_Of (Var, Loc)); |
| end if; |
| |
| elsif Ekind (Formal) = E_Out_Parameter |
| and then Is_Array_Type (F_Typ) |
| and then Number_Dimensions (F_Typ) = 1 |
| and then not Has_Non_Null_Base_Init_Proc (F_Typ) |
| then |
| -- Actual is a one-dimensional array or slice, and the type |
| -- requires no initialization. Create a temporary of the |
| -- right size, but do not copy actual into it (optimization). |
| |
| Init := Empty; |
| Indic := |
| Make_Subtype_Indication (Loc, |
| Subtype_Mark => New_Occurrence_Of (F_Typ, Loc), |
| Constraint => |
| Make_Index_Or_Discriminant_Constraint (Loc, |
| Constraints => New_List ( |
| Make_Range (Loc, |
| Low_Bound => |
| Make_Attribute_Reference (Loc, |
| Prefix => New_Occurrence_Of (Var, Loc), |
| Attribute_Name => Name_First), |
| High_Bound => |
| Make_Attribute_Reference (Loc, |
| Prefix => New_Occurrence_Of (Var, Loc), |
| Attribute_Name => Name_Last))))); |
| |
| else |
| Init := New_Occurrence_Of (Var, Loc); |
| end if; |
| |
| -- An initialization is created for packed conversions as |
| -- actuals for out parameters to enable Make_Object_Declaration |
| -- to determine the proper subtype for N_Node. Note that this |
| -- is wasteful because the extra copying on the call side is |
| -- not required for such out parameters. ??? |
| |
| elsif Ekind (Formal) = E_Out_Parameter |
| and then Nkind (Actual) = N_Type_Conversion |
| and then (Is_Bit_Packed_Array (F_Typ) |
| or else |
| Is_Bit_Packed_Array (Etype (Expression (Actual)))) |
| then |
| if Conversion_OK (Actual) then |
| Init := OK_Convert_To (F_Typ, New_Occurrence_Of (Var, Loc)); |
| else |
| Init := Convert_To (F_Typ, New_Occurrence_Of (Var, Loc)); |
| end if; |
| |
| elsif Ekind (Formal) = E_In_Parameter then |
| |
| -- Handle the case in which the actual is a type conversion |
| |
| if Nkind (Actual) = N_Type_Conversion then |
| if Conversion_OK (Actual) then |
| Init := OK_Convert_To (F_Typ, New_Occurrence_Of (Var, Loc)); |
| else |
| Init := Convert_To (F_Typ, New_Occurrence_Of (Var, Loc)); |
| end if; |
| else |
| Init := New_Occurrence_Of (Var, Loc); |
| end if; |
| |
| else |
| Init := Empty; |
| end if; |
| |
| N_Node := |
| Make_Object_Declaration (Loc, |
| Defining_Identifier => Temp, |
| Object_Definition => Indic, |
| Expression => Init); |
| Set_Assignment_OK (N_Node); |
| Insert_Action (N, N_Node); |
| |
| -- Now, normally the deal here is that we use the defining |
| -- identifier created by that object declaration. There is |
| -- one exception to this. In the change of representation case |
| -- the above declaration will end up looking like: |
| |
| -- temp : type := identifier; |
| |
| -- And in this case we might as well use the identifier directly |
| -- and eliminate the temporary. Note that the analysis of the |
| -- declaration was not a waste of time in that case, since it is |
| -- what generated the necessary change of representation code. If |
| -- the change of representation introduced additional code, as in |
| -- a fixed-integer conversion, the expression is not an identifier |
| -- and must be kept. |
| |
| if Crep |
| and then Present (Expression (N_Node)) |
| and then Is_Entity_Name (Expression (N_Node)) |
| then |
| Temp := Entity (Expression (N_Node)); |
| Rewrite (N_Node, Make_Null_Statement (Loc)); |
| end if; |
| |
| -- For IN parameter, all we do is to replace the actual |
| |
| if Ekind (Formal) = E_In_Parameter then |
| Rewrite (Actual, New_Occurrence_Of (Temp, Loc)); |
| Analyze (Actual); |
| |
| -- Processing for OUT or IN OUT parameter |
| |
| else |
| -- Kill current value indications for the temporary variable we |
| -- created, since we just passed it as an OUT parameter. |
| |
| Kill_Current_Values (Temp); |
| Set_Is_Known_Valid (Temp, False); |
| |
| -- If type conversion, use reverse conversion on exit |
| |
| if Nkind (Actual) = N_Type_Conversion then |
| if Conversion_OK (Actual) then |
| Expr := OK_Convert_To (V_Typ, New_Occurrence_Of (Temp, Loc)); |
| else |
| Expr := Convert_To (V_Typ, New_Occurrence_Of (Temp, Loc)); |
| end if; |
| else |
| Expr := New_Occurrence_Of (Temp, Loc); |
| end if; |
| |
| Rewrite (Actual, New_Occurrence_Of (Temp, Loc)); |
| Analyze (Actual); |
| |
| -- If the actual is a conversion of a packed reference, it may |
| -- already have been expanded by Remove_Side_Effects, and the |
| -- resulting variable is a temporary which does not designate |
| -- the proper out-parameter, which may not be addressable. In |
| -- that case, generate an assignment to the original expression |
| -- (before expansion of the packed reference) so that the proper |
| -- expansion of assignment to a packed component can take place. |
| |
| declare |
| Obj : Node_Id; |
| Lhs : Node_Id; |
| |
| begin |
| if Is_Renaming_Of_Object (Var) |
| and then Nkind (Renamed_Object (Var)) = N_Selected_Component |
| and then Is_Entity_Name (Prefix (Renamed_Object (Var))) |
| and then Nkind (Original_Node (Prefix (Renamed_Object (Var)))) |
| = N_Indexed_Component |
| and then |
| Has_Non_Standard_Rep (Etype (Prefix (Renamed_Object (Var)))) |
| then |
| Obj := Renamed_Object (Var); |
| Lhs := |
| Make_Selected_Component (Loc, |
| Prefix => |
| New_Copy_Tree (Original_Node (Prefix (Obj))), |
| Selector_Name => New_Copy (Selector_Name (Obj))); |
| Reset_Analyzed_Flags (Lhs); |
| |
| else |
| Lhs := New_Occurrence_Of (Var, Loc); |
| end if; |
| |
| Set_Assignment_OK (Lhs); |
| |
| if Is_Access_Type (E_Formal) |
| and then Is_Entity_Name (Lhs) |
| and then |
| Present (Effective_Extra_Accessibility (Entity (Lhs))) |
| then |
| -- Copyback target is an Ada 2012 stand-alone object of an |
| -- anonymous access type. |
| |
| pragma Assert (Ada_Version >= Ada_2012); |
| |
| if Type_Access_Level (E_Formal) > |
| Object_Access_Level (Lhs) |
| then |
| Append_To (Post_Call, |
| Make_Raise_Program_Error (Loc, |
| Reason => PE_Accessibility_Check_Failed)); |
| end if; |
| |
| Append_To (Post_Call, |
| Make_Assignment_Statement (Loc, |
| Name => Lhs, |
| Expression => Expr)); |
| |
| -- We would like to somehow suppress generation of the |
| -- extra_accessibility assignment generated by the expansion |
| -- of the above assignment statement. It's not a correctness |
| -- issue because the following assignment renders it dead, |
| -- but generating back-to-back assignments to the same |
| -- target is undesirable. ??? |
| |
| Append_To (Post_Call, |
| Make_Assignment_Statement (Loc, |
| Name => New_Occurrence_Of ( |
| Effective_Extra_Accessibility (Entity (Lhs)), Loc), |
| Expression => Make_Integer_Literal (Loc, |
| Type_Access_Level (E_Formal)))); |
| |
| else |
| Append_To (Post_Call, |
| Make_Assignment_Statement (Loc, |
| Name => Lhs, |
| Expression => Expr)); |
| end if; |
| end; |
| end if; |
| end Add_Call_By_Copy_Code; |
| |
| ---------------------------------- |
| -- Add_Simple_Call_By_Copy_Code -- |
| ---------------------------------- |
| |
| procedure Add_Simple_Call_By_Copy_Code is |
| Temp : Entity_Id; |
| Decl : Node_Id; |
| Incod : Node_Id; |
| Outcod : Node_Id; |
| Lhs : Node_Id; |
| Rhs : Node_Id; |
| Indic : Node_Id; |
| F_Typ : constant Entity_Id := Etype (Formal); |
| |
| begin |
| if not Is_Legal_Copy then |
| return; |
| end if; |
| |
| -- Use formal type for temp, unless formal type is an unconstrained |
| -- array, in which case we don't have to worry about bounds checks, |
| -- and we use the actual type, since that has appropriate bounds. |
| |
| if Is_Array_Type (F_Typ) and then not Is_Constrained (F_Typ) then |
| Indic := New_Occurrence_Of (Etype (Actual), Loc); |
| else |
| Indic := New_Occurrence_Of (Etype (Formal), Loc); |
| end if; |
| |
| -- Prepare to generate code |
| |
| Reset_Packed_Prefix; |
| |
| Temp := Make_Temporary (Loc, 'T', Actual); |
| Incod := Relocate_Node (Actual); |
| Outcod := New_Copy_Tree (Incod); |
| |
| -- Generate declaration of temporary variable, initializing it |
| -- with the input parameter unless we have an OUT formal or |
| -- this is an initialization call. |
| |
| -- If the formal is an out parameter with discriminants, the |
| -- discriminants must be captured even if the rest of the object |
| -- is in principle uninitialized, because the discriminants may |
| -- be read by the called subprogram. |
| |
| if Ekind (Formal) = E_Out_Parameter then |
| Incod := Empty; |
| |
| if Has_Discriminants (Etype (Formal)) then |
| Indic := New_Occurrence_Of (Etype (Actual), Loc); |
| end if; |
| |
| elsif Inside_Init_Proc then |
| |
| -- Could use a comment here to match comment below ??? |
| |
| if Nkind (Actual) /= N_Selected_Component |
| or else |
| not Has_Discriminant_Dependent_Constraint |
| (Entity (Selector_Name (Actual))) |
| then |
| Incod := Empty; |
| |
| -- Otherwise, keep the component in order to generate the proper |
| -- actual subtype, that depends on enclosing discriminants. |
| |
| else |
| null; |
| end if; |
| end if; |
| |
| Decl := |
| Make_Object_Declaration (Loc, |
| Defining_Identifier => Temp, |
| Object_Definition => Indic, |
| Expression => Incod); |
| |
| if Inside_Init_Proc |
| and then No (Incod) |
| then |
| -- If the call is to initialize a component of a composite type, |
| -- and the component does not depend on discriminants, use the |
| -- actual type of the component. This is required in case the |
| -- component is constrained, because in general the formal of the |
| -- initialization procedure will be unconstrained. Note that if |
| -- the component being initialized is constrained by an enclosing |
| -- discriminant, the presence of the initialization in the |
| -- declaration will generate an expression for the actual subtype. |
| |
| Set_No_Initialization (Decl); |
| Set_Object_Definition (Decl, |
| New_Occurrence_Of (Etype (Actual), Loc)); |
| end if; |
| |
| Insert_Action (N, Decl); |
| |
| -- The actual is simply a reference to the temporary |
| |
| Rewrite (Actual, New_Occurrence_Of (Temp, Loc)); |
| |
| -- Generate copy out if OUT or IN OUT parameter |
| |
| if Ekind (Formal) /= E_In_Parameter then |
| Lhs := Outcod; |
| Rhs := New_Occurrence_Of (Temp, Loc); |
| |
| -- Deal with conversion |
| |
| if Nkind (Lhs) = N_Type_Conversion then |
| Lhs := Expression (Lhs); |
| Rhs := Convert_To (Etype (Actual), Rhs); |
| end if; |
| |
| Append_To (Post_Call, |
| Make_Assignment_Statement (Loc, |
| Name => Lhs, |
| Expression => Rhs)); |
| Set_Assignment_OK (Name (Last (Post_Call))); |
| end if; |
| end Add_Simple_Call_By_Copy_Code; |
| |
| --------------------------- |
| -- Check_Fortran_Logical -- |
| --------------------------- |
| |
| procedure Check_Fortran_Logical is |
| Logical : constant Entity_Id := Etype (Formal); |
| Var : Entity_Id; |
| |
| -- Note: this is very incomplete, e.g. it does not handle arrays |
| -- of logical values. This is really not the right approach at all???) |
| |
| begin |
| if Convention (Subp) = Convention_Fortran |
| and then Root_Type (Etype (Formal)) = Standard_Boolean |
| and then Ekind (Formal) /= E_In_Parameter |
| then |
| Var := Make_Var (Actual); |
| Append_To (Post_Call, |
| Make_Assignment_Statement (Loc, |
| Name => New_Occurrence_Of (Var, Loc), |
| Expression => |
| Unchecked_Convert_To ( |
| Logical, |
| Make_Op_Ne (Loc, |
| Left_Opnd => New_Occurrence_Of (Var, Loc), |
| Right_Opnd => |
| Unchecked_Convert_To ( |
| Logical, |
| New_Occurrence_Of (Standard_False, Loc)))))); |
| end if; |
| end Check_Fortran_Logical; |
| |
| ------------------- |
| -- Is_Legal_Copy -- |
| ------------------- |
| |
| function Is_Legal_Copy return Boolean is |
| begin |
| -- An attempt to copy a value of such a type can only occur if |
| -- representation clauses give the actual a misaligned address. |
| |
| if Is_By_Reference_Type (Etype (Formal)) then |
| |
| -- If the front-end does not perform full type layout, the actual |
| -- may in fact be properly aligned but there is not enough front- |
| -- end information to determine this. In that case gigi will emit |
| -- an error if a copy is not legal, or generate the proper code. |
| -- For other backends we report the error now. |
| |
| -- Seems wrong to be issuing an error in the expander, since it |
| -- will be missed in -gnatc mode ??? |
| |
| if Frontend_Layout_On_Target then |
| Error_Msg_N |
| ("misaligned actual cannot be passed by reference", Actual); |
| end if; |
| |
| return False; |
| |
| -- For users of Starlet, we assume that the specification of by- |
| -- reference mechanism is mandatory. This may lead to unaligned |
| -- objects but at least for DEC legacy code it is known to work. |
| -- The warning will alert users of this code that a problem may |
| -- be lurking. |
| |
| elsif Mechanism (Formal) = By_Reference |
| and then Is_Valued_Procedure (Scope (Formal)) |
| then |
| Error_Msg_N |
| ("by_reference actual may be misaligned??", Actual); |
| return False; |
| |
| else |
| return True; |
| end if; |
| end Is_Legal_Copy; |
| |
| -------------- |
| -- Make_Var -- |
| -------------- |
| |
| function Make_Var (Actual : Node_Id) return Entity_Id is |
| Var : Entity_Id; |
| |
| begin |
| if Is_Entity_Name (Actual) then |
| return Entity (Actual); |
| |
| else |
| Var := Make_Temporary (Loc, 'T', Actual); |
| |
| N_Node := |
| Make_Object_Renaming_Declaration (Loc, |
| Defining_Identifier => Var, |
| Subtype_Mark => |
| New_Occurrence_Of (Etype (Actual), Loc), |
| Name => Relocate_Node (Actual)); |
| |
| Insert_Action (N, N_Node); |
| return Var; |
| end if; |
| end Make_Var; |
| |
| ------------------------- |
| -- Reset_Packed_Prefix -- |
| ------------------------- |
| |
| procedure Reset_Packed_Prefix is |
| Pfx : Node_Id := Actual; |
| begin |
| loop |
| Set_Analyzed (Pfx, False); |
| exit when |
| not Nkind_In (Pfx, N_Selected_Component, N_Indexed_Component); |
| Pfx := Prefix (Pfx); |
| end loop; |
| end Reset_Packed_Prefix; |
| |
| -- Start of processing for Expand_Actuals |
| |
| begin |
| Post_Call := New_List; |
| |
| Formal := First_Formal (Subp); |
| Actual := First_Actual (N); |
| while Present (Formal) loop |
| E_Formal := Etype (Formal); |
| E_Actual := Etype (Actual); |
| |
| if Is_Scalar_Type (E_Formal) |
| or else Nkind (Actual) = N_Slice |
| then |
| Check_Fortran_Logical; |
| |
| -- RM 6.4.1 (11) |
| |
| elsif Ekind (Formal) /= E_Out_Parameter then |
| |
| -- The unusual case of the current instance of a protected type |
| -- requires special handling. This can only occur in the context |
| -- of a call within the body of a protected operation. |
| |
| if Is_Entity_Name (Actual) |
| and then Ekind (Entity (Actual)) = E_Protected_Type |
| and then In_Open_Scopes (Entity (Actual)) |
| then |
| if Scope (Subp) /= Entity (Actual) then |
| Error_Msg_N |
| ("operation outside protected type may not " |
| & "call back its protected operations??", Actual); |
| end if; |
| |
| Rewrite (Actual, |
| Expand_Protected_Object_Reference (N, Entity (Actual))); |
| end if; |
| |
| -- Ada 2005 (AI-318-02): If the actual parameter is a call to a |
| -- build-in-place function, then a temporary return object needs |
| -- to be created and access to it must be passed to the function. |
| -- Currently we limit such functions to those with inherently |
| -- limited result subtypes, but eventually we plan to expand the |
| -- functions that are treated as build-in-place to include other |
| -- composite result types. |
| |
| if Is_Build_In_Place_Function_Call (Actual) then |
| Make_Build_In_Place_Call_In_Anonymous_Context (Actual); |
| end if; |
| |
| Apply_Constraint_Check (Actual, E_Formal); |
| |
| -- Out parameter case. No constraint checks on access type |
| -- RM 6.4.1 (13) |
| |
| elsif Is_Access_Type (E_Formal) then |
| null; |
| |
| -- RM 6.4.1 (14) |
| |
| elsif Has_Discriminants (Base_Type (E_Formal)) |
| or else Has_Non_Null_Base_Init_Proc (E_Formal) |
| then |
| Apply_Constraint_Check (Actual, E_Formal); |
| |
| -- RM 6.4.1 (15) |
| |
| else |
| Apply_Constraint_Check (Actual, Base_Type (E_Formal)); |
| end if; |
| |
| -- Processing for IN-OUT and OUT parameters |
| |
| if Ekind (Formal) /= E_In_Parameter then |
| |
| -- For type conversions of arrays, apply length/range checks |
| |
| if Is_Array_Type (E_Formal) |
| and then Nkind (Actual) = N_Type_Conversion |
| then |
| if Is_Constrained (E_Formal) then |
| Apply_Length_Check (Expression (Actual), E_Formal); |
| else |
| Apply_Range_Check (Expression (Actual), E_Formal); |
| end if; |
| end if; |
| |
| -- If argument is a type conversion for a type that is passed |
| -- by copy, then we must pass the parameter by copy. |
| |
| if Nkind (Actual) = N_Type_Conversion |
| and then |
| (Is_Numeric_Type (E_Formal) |
| or else Is_Access_Type (E_Formal) |
| or else Is_Enumeration_Type (E_Formal) |
| or else Is_Bit_Packed_Array (Etype (Formal)) |
| or else Is_Bit_Packed_Array (Etype (Expression (Actual))) |
| |
| -- Also pass by copy if change of representation |
| |
| or else not Same_Representation |
| (Etype (Formal), |
| Etype (Expression (Actual)))) |
| then |
| Add_Call_By_Copy_Code; |
| |
| -- References to components of bit packed arrays are expanded |
| -- at this point, rather than at the point of analysis of the |
| -- actuals, to handle the expansion of the assignment to |
| -- [in] out parameters. |
| |
| elsif Is_Ref_To_Bit_Packed_Array (Actual) then |
| Add_Simple_Call_By_Copy_Code; |
| |
| -- If a non-scalar actual is possibly bit-aligned, we need a copy |
| -- because the back-end cannot cope with such objects. In other |
| -- cases where alignment forces a copy, the back-end generates |
| -- it properly. It should not be generated unconditionally in the |
| -- front-end because it does not know precisely the alignment |
| -- requirements of the target, and makes too conservative an |
| -- estimate, leading to superfluous copies or spurious errors |
| -- on by-reference parameters. |
| |
| elsif Nkind (Actual) = N_Selected_Component |
| and then |
| Component_May_Be_Bit_Aligned (Entity (Selector_Name (Actual))) |
| and then not Represented_As_Scalar (Etype (Formal)) |
| then |
| Add_Simple_Call_By_Copy_Code; |
| |
| -- References to slices of bit packed arrays are expanded |
| |
| elsif Is_Ref_To_Bit_Packed_Slice (Actual) then |
| Add_Call_By_Copy_Code; |
| |
| -- References to possibly unaligned slices of arrays are expanded |
| |
| elsif Is_Possibly_Unaligned_Slice (Actual) then |
| Add_Call_By_Copy_Code; |
| |
| -- Deal with access types where the actual subtype and the |
| -- formal subtype are not the same, requiring a check. |
| |
| -- It is necessary to exclude tagged types because of "downward |
| -- conversion" errors. |
| |
| elsif Is_Access_Type (E_Formal) |
| and then not Same_Type (E_Formal, E_Actual) |
| and then not Is_Tagged_Type (Designated_Type (E_Formal)) |
| then |
| Add_Call_By_Copy_Code; |
| |
| -- If the actual is not a scalar and is marked for volatile |
| -- treatment, whereas the formal is not volatile, then pass |
| -- by copy unless it is a by-reference type. |
| |
| -- Note: we use Is_Volatile here rather than Treat_As_Volatile, |
| -- because this is the enforcement of a language rule that applies |
| -- only to "real" volatile variables, not e.g. to the address |
| -- clause overlay case. |
| |
| elsif Is_Entity_Name (Actual) |
| and then Is_Volatile (Entity (Actual)) |
| and then not Is_By_Reference_Type (E_Actual) |
| and then not Is_Scalar_Type (Etype (Entity (Actual))) |
| and then not Is_Volatile (E_Formal) |
| then |
| Add_Call_By_Copy_Code; |
| |
| elsif Nkind (Actual) = N_Indexed_Component |
| and then Is_Entity_Name (Prefix (Actual)) |
| and then Has_Volatile_Components (Entity (Prefix (Actual))) |
| then |
| Add_Call_By_Copy_Code; |
| |
| -- Add call-by-copy code for the case of scalar out parameters |
| -- when it is not known at compile time that the subtype of the |
| -- formal is a subrange of the subtype of the actual (or vice |
| -- versa for in out parameters), in order to get range checks |
| -- on such actuals. (Maybe this case should be handled earlier |
| -- in the if statement???) |
| |
| elsif Is_Scalar_Type (E_Formal) |
| and then |
| (not In_Subrange_Of (E_Formal, E_Actual) |
| or else |
| (Ekind (Formal) = E_In_Out_Parameter |
| and then not In_Subrange_Of (E_Actual, E_Formal))) |
| then |
| -- Perhaps the setting back to False should be done within |
| -- Add_Call_By_Copy_Code, since it could get set on other |
| -- cases occurring above??? |
| |
| if Do_Range_Check (Actual) then |
| Set_Do_Range_Check (Actual, False); |
| end if; |
| |
| Add_Call_By_Copy_Code; |
| end if; |
| |
| -- RM 3.2.4 (23/3): A predicate is checked on in-out and out |
| -- by-reference parameters on exit from the call. If the actual |
| -- is a derived type and the operation is inherited, the body |
| -- of the operation will not contain a call to the predicate |
| -- function, so it must be done explicitly after the call. Ditto |
| -- if the actual is an entity of a predicated subtype. |
| |
| -- The rule refers to by-reference types, but a check is needed |
| -- for by-copy types as well. That check is subsumed by the rule |
| -- for subtype conversion on assignment, but we can generate the |
| -- required check now. |
| |
| -- Note also that Subp may be either a subprogram entity for |
| -- direct calls, or a type entity for indirect calls, which must |
| -- be handled separately because the name does not denote an |
| -- overloadable entity. |
| |
| By_Ref_Predicate_Check : declare |
| Aund : constant Entity_Id := Underlying_Type (E_Actual); |
| Atyp : Entity_Id; |
| |
| function Is_Public_Subp return Boolean; |
| -- Check whether the subprogram being called is a visible |
| -- operation of the type of the actual. Used to determine |
| -- whether an invariant check must be generated on the |
| -- caller side. |
| |
| --------------------- |
| -- Is_Public_Subp -- |
| --------------------- |
| |
| function Is_Public_Subp return Boolean is |
| Pack : constant Entity_Id := Scope (Subp); |
| Subp_Decl : Node_Id; |
| |
| begin |
| if not Is_Subprogram (Subp) then |
| return False; |
| |
| -- The operation may be inherited, or a primitive of the |
| -- root type. |
| |
| elsif |
| Nkind_In (Parent (Subp), N_Private_Extension_Declaration, |
| N_Full_Type_Declaration) |
| then |
| Subp_Decl := Parent (Subp); |
| |
| else |
| Subp_Decl := Unit_Declaration_Node (Subp); |
| end if; |
| |
| return Ekind (Pack) = E_Package |
| and then |
| List_Containing (Subp_Decl) = |
| Visible_Declarations |
| (Specification (Unit_Declaration_Node (Pack))); |
| end Is_Public_Subp; |
| |
| -- Start of processing for By_Ref_Predicate_Check |
| |
| begin |
| if No (Aund) then |
| Atyp := E_Actual; |
| else |
| Atyp := Aund; |
| end if; |
| |
| if Has_Predicates (Atyp) |
| and then Present (Predicate_Function (Atyp)) |
| |
| -- Skip predicate checks for special cases |
| |
| and then Predicate_Tests_On_Arguments (Subp) |
| then |
| Append_To (Post_Call, |
| Make_Predicate_Check (Atyp, Actual)); |
| end if; |
| |
| -- We generated caller-side invariant checks in two cases: |
| |
| -- a) when calling an inherited operation, where there is an |
| -- implicit view conversion of the actual to the parent type. |
| |
| -- b) When the conversion is explicit |
| |
| -- We treat these cases separately because the required |
| -- conversion for a) is added later when expanding the call. |
| |
| if Has_Invariants (Etype (Actual)) |
| and then |
| Nkind (Parent (Subp)) = N_Private_Extension_Declaration |
| then |
| if Comes_From_Source (N) and then Is_Public_Subp then |
| Append_To (Post_Call, Make_Invariant_Call (Actual)); |
| end if; |
| |
| elsif Nkind (Actual) = N_Type_Conversion |
| and then Has_Invariants (Etype (Expression (Actual))) |
| then |
| if Comes_From_Source (N) and then Is_Public_Subp then |
| Append_To (Post_Call, |
| Make_Invariant_Call (Expression (Actual))); |
| end if; |
| end if; |
| end By_Ref_Predicate_Check; |
| |
| -- Processing for IN parameters |
| |
| else |
| -- For IN parameters is in the packed array case, we expand an |
| -- indexed component (the circuit in Exp_Ch4 deliberately left |
| -- indexed components appearing as actuals untouched, so that |
| -- the special processing above for the OUT and IN OUT cases |
| -- could be performed. We could make the test in Exp_Ch4 more |
| -- complex and have it detect the parameter mode, but it is |
| -- easier simply to handle all cases here.) |
| |
| if Nkind (Actual) = N_Indexed_Component |
| and then Is_Packed (Etype (Prefix (Actual))) |
| then |
| Reset_Packed_Prefix; |
| Expand_Packed_Element_Reference (Actual); |
| |
| -- If we have a reference to a bit packed array, we copy it, since |
| -- the actual must be byte aligned. |
| |
| -- Is this really necessary in all cases??? |
| |
| elsif Is_Ref_To_Bit_Packed_Array (Actual) then |
| Add_Simple_Call_By_Copy_Code; |
| |
| -- If a non-scalar actual is possibly unaligned, we need a copy |
| |
| elsif Is_Possibly_Unaligned_Object (Actual) |
| and then not Represented_As_Scalar (Etype (Formal)) |
| then |
| Add_Simple_Call_By_Copy_Code; |
| |
| -- Similarly, we have to expand slices of packed arrays here |
| -- because the result must be byte aligned. |
| |
| elsif Is_Ref_To_Bit_Packed_Slice (Actual) then |
| Add_Call_By_Copy_Code; |
| |
| -- Only processing remaining is to pass by copy if this is a |
| -- reference to a possibly unaligned slice, since the caller |
| -- expects an appropriately aligned argument. |
| |
| elsif Is_Possibly_Unaligned_Slice (Actual) then |
| Add_Call_By_Copy_Code; |
| |
| -- An unusual case: a current instance of an enclosing task can be |
| -- an actual, and must be replaced by a reference to self. |
| |
| elsif Is_Entity_Name (Actual) |
| and then Is_Task_Type (Entity (Actual)) |
| then |
| if In_Open_Scopes (Entity (Actual)) then |
| Rewrite (Actual, |
| (Make_Function_Call (Loc, |
| Name => New_Occurrence_Of (RTE (RE_Self), Loc)))); |
| Analyze (Actual); |
| |
| -- A task type cannot otherwise appear as an actual |
| |
| else |
| raise Program_Error; |
| end if; |
| end if; |
| end if; |
| |
| Next_Formal (Formal); |
| Next_Actual (Actual); |
| end loop; |
| |
| -- Find right place to put post call stuff if it is present |
| |
| if not Is_Empty_List (Post_Call) then |
| |
| -- Cases where the call is not a member of a statement list |
| |
| if not Is_List_Member (N) then |
| |
| -- In Ada 2012 the call may be a function call in an expression |
| -- (since OUT and IN OUT parameters are now allowed for such |
| -- calls). The write-back of (in)-out parameters is handled |
| -- by the back-end, but the constraint checks generated when |
| -- subtypes of formal and actual don't match must be inserted |
| -- in the form of assignments. |
| |
| if Ada_Version >= Ada_2012 |
| and then Nkind (N) = N_Function_Call |
| then |
| -- We used to just do handle this by climbing up parents to |
| -- a non-statement/declaration and then simply making a call |
| -- to Insert_Actions_After (P, Post_Call), but that doesn't |
| -- work. If we are in the middle of an expression, e.g. the |
| -- condition of an IF, this call would insert after the IF |
| -- statement, which is much too late to be doing the write |
| -- back. For example: |
| |
| -- if Clobber (X) then |
| -- Put_Line (X'Img); |
| -- else |
| -- goto Junk |
| -- end if; |
| |
| -- Now assume Clobber changes X, if we put the write back |
| -- after the IF, the Put_Line gets the wrong value and the |
| -- goto causes the write back to be skipped completely. |
| |
| -- To deal with this, we replace the call by |
| |
| -- do |
| -- Tnnn : function-result-type renames function-call; |
| -- Post_Call actions |
| -- in |
| -- Tnnn; |
| -- end; |
| |
| -- Note: this won't do in Modify_Tree_For_C mode, but we |
| -- will deal with that later (it will require creating a |
| -- declaration for Temp, using Insert_Declaration) ??? |
| |
| declare |
| Tnnn : constant Entity_Id := Make_Temporary (Loc, 'T'); |
| FRTyp : constant Entity_Id := Etype (N); |
| Name : constant Node_Id := Relocate_Node (N); |
| |
| begin |
| Prepend_To (Post_Call, |
| Make_Object_Renaming_Declaration (Loc, |
| Defining_Identifier => Tnnn, |
| Subtype_Mark => New_Occurrence_Of (FRTyp, Loc), |
| Name => Name)); |
| |
| Rewrite (N, |
| Make_Expression_With_Actions (Loc, |
| Actions => Post_Call, |
| Expression => New_Occurrence_Of (Tnnn, Loc))); |
| |
| -- We don't want to just blindly call Analyze_And_Resolve |
| -- because that would cause unwanted recursion on the call. |
| -- So for a moment set the call as analyzed to prevent that |
| -- recursion, and get the rest analyzed properly, then reset |
| -- the analyzed flag, so our caller can continue. |
| |
| Set_Analyzed (Name, True); |
| Analyze_And_Resolve (N, FRTyp); |
| Set_Analyzed (Name, False); |
| |
| -- Reset calling argument to point to function call inside |
| -- the expression with actions so the caller can continue |
| -- to process the call. |
| |
| N := Name; |
| end; |
| |
| -- If not the special Ada 2012 case of a function call, then |
| -- we must have the triggering statement of a triggering |
| -- alternative or an entry call alternative, and we can add |
| -- the post call stuff to the corresponding statement list. |
| |
| else |
| declare |
| P : Node_Id; |
| |
| begin |
| P := Parent (N); |
| pragma Assert (Nkind_In (P, N_Triggering_Alternative, |
| N_Entry_Call_Alternative)); |
| |
| if Is_Non_Empty_List (Statements (P)) then |
| Insert_List_Before_And_Analyze |
| (First (Statements (P)), Post_Call); |
| else |
| Set_Statements (P, Post_Call); |
| end if; |
| |
| return; |
| end; |
| end if; |
| |
| -- Otherwise, normal case where N is in a statement sequence, |
| -- just put the post-call stuff after the call statement. |
| |
| else |
| Insert_Actions_After (N, Post_Call); |
| return; |
| end if; |
| end if; |
| |
| -- The call node itself is re-analyzed in Expand_Call |
| |
| end Expand_Actuals; |
| |
| ----------------- |
| -- Expand_Call -- |
| ----------------- |
| |
| -- This procedure handles expansion of function calls and procedure call |
| -- statements (i.e. it serves as the body for Expand_N_Function_Call and |
| -- Expand_N_Procedure_Call_Statement). Processing for calls includes: |
| |
| -- Replace call to Raise_Exception by Raise_Exception_Always if possible |
| -- Provide values of actuals for all formals in Extra_Formals list |
| -- Replace "call" to enumeration literal function by literal itself |
| -- Rewrite call to predefined operator as operator |
| -- Replace actuals to in-out parameters that are numeric conversions, |
| -- with explicit assignment to temporaries before and after the call. |
| |
| -- Note that the list of actuals has been filled with default expressions |
| -- during semantic analysis of the call. Only the extra actuals required |
| -- for the 'Constrained attribute and for accessibility checks are added |
| -- at this point. |
| |
| procedure Expand_Call (N : Node_Id) is |
| Loc : constant Source_Ptr := Sloc (N); |
| Call_Node : Node_Id := N; |
| Extra_Actuals : List_Id := No_List; |
| Prev : Node_Id := Empty; |
| |
| procedure Add_Actual_Parameter (Insert_Param : Node_Id); |
| -- Adds one entry to the end of the actual parameter list. Used for |
| -- default parameters and for extra actuals (for Extra_Formals). The |
| -- argument is an N_Parameter_Association node. |
| |
| procedure Add_Extra_Actual (Expr : Node_Id; EF : Entity_Id); |
| -- Adds an extra actual to the list of extra actuals. Expr is the |
| -- expression for the value of the actual, EF is the entity for the |
| -- extra formal. |
| |
| function Inherited_From_Formal (S : Entity_Id) return Entity_Id; |
| -- Within an instance, a type derived from an untagged formal derived |
| -- type inherits from the original parent, not from the actual. The |
| -- current derivation mechanism has the derived type inherit from the |
| -- actual, which is only correct outside of the instance. If the |
| -- subprogram is inherited, we test for this particular case through a |
| -- convoluted tree traversal before setting the proper subprogram to be |
| -- called. |
| |
| function In_Unfrozen_Instance (E : Entity_Id) return Boolean; |
| -- Return true if E comes from an instance that is not yet frozen |
| |
| function Is_Direct_Deep_Call (Subp : Entity_Id) return Boolean; |
| -- Determine if Subp denotes a non-dispatching call to a Deep routine |
| |
| function New_Value (From : Node_Id) return Node_Id; |
| -- From is the original Expression. New_Value is equivalent to a call |
| -- to Duplicate_Subexpr with an explicit dereference when From is an |
| -- access parameter. |
| |
| -------------------------- |
| -- Add_Actual_Parameter -- |
| -------------------------- |
| |
| procedure Add_Actual_Parameter (Insert_Param : Node_Id) is |
| Actual_Expr : constant Node_Id := |
| Explicit_Actual_Parameter (Insert_Param); |
| |
| begin |
| -- Case of insertion is first named actual |
| |
| if No (Prev) or else |
| Nkind (Parent (Prev)) /= N_Parameter_Association |
| then |
| Set_Next_Named_Actual |
| (Insert_Param, First_Named_Actual (Call_Node)); |
| Set_First_Named_Actual (Call_Node, Actual_Expr); |
| |
| if No (Prev) then |
| if No (Parameter_Associations (Call_Node)) then |
| Set_Parameter_Associations (Call_Node, New_List); |
| end if; |
| |
| Append (Insert_Param, Parameter_Associations (Call_Node)); |
| |
| else |
| Insert_After (Prev, Insert_Param); |
| end if; |
| |
| -- Case of insertion is not first named actual |
| |
| else |
| Set_Next_Named_Actual |
| (Insert_Param, Next_Named_Actual (Parent (Prev))); |
| Set_Next_Named_Actual (Parent (Prev), Actual_Expr); |
| Append (Insert_Param, Parameter_Associations (Call_Node)); |
| end if; |
| |
| Prev := Actual_Expr; |
| end Add_Actual_Parameter; |
| |
| ---------------------- |
| -- Add_Extra_Actual -- |
| ---------------------- |
| |
| procedure Add_Extra_Actual (Expr : Node_Id; EF : Entity_Id) is |
| Loc : constant Source_Ptr := Sloc (Expr); |
| |
| begin |
| if Extra_Actuals = No_List then |
| Extra_Actuals := New_List; |
| Set_Parent (Extra_Actuals, Call_Node); |
| end if; |
| |
| Append_To (Extra_Actuals, |
| Make_Parameter_Association (Loc, |
| Selector_Name => New_Occurrence_Of (EF, Loc), |
| Explicit_Actual_Parameter => Expr)); |
| |
| Analyze_And_Resolve (Expr, Etype (EF)); |
| |
| if Nkind (Call_Node) = N_Function_Call then |
| Set_Is_Accessibility_Actual (Parent (Expr)); |
| end if; |
| end Add_Extra_Actual; |
| |
| --------------------------- |
| -- Inherited_From_Formal -- |
| --------------------------- |
| |
| function Inherited_From_Formal (S : Entity_Id) return Entity_Id is |
| Par : Entity_Id; |
| Gen_Par : Entity_Id; |
| Gen_Prim : Elist_Id; |
| Elmt : Elmt_Id; |
| Indic : Node_Id; |
| |
| begin |
| -- If the operation is inherited, it is attached to the corresponding |
| -- type derivation. If the parent in the derivation is a generic |
| -- actual, it is a subtype of the actual, and we have to recover the |
| -- original derived type declaration to find the proper parent. |
| |
| if Nkind (Parent (S)) /= N_Full_Type_Declaration |
| or else not Is_Derived_Type (Defining_Identifier (Parent (S))) |
| or else Nkind (Type_Definition (Original_Node (Parent (S)))) /= |
| N_Derived_Type_Definition |
| or else not In_Instance |
| then |
| return Empty; |
| |
| else |
| Indic := |
| Subtype_Indication |
| (Type_Definition (Original_Node (Parent (S)))); |
| |
| if Nkind (Indic) = N_Subtype_Indication then |
| Par := Entity (Subtype_Mark (Indic)); |
| else |
| Par := Entity (Indic); |
| end if; |
| end if; |
| |
| if not Is_Generic_Actual_Type (Par) |
| or else Is_Tagged_Type (Par) |
| or else Nkind (Parent (Par)) /= N_Subtype_Declaration |
| or else not In_Open_Scopes (Scope (Par)) |
| then |
| return Empty; |
| else |
| Gen_Par := Generic_Parent_Type (Parent (Par)); |
| end if; |
| |
| -- If the actual has no generic parent type, the formal is not |
| -- a formal derived type, so nothing to inherit. |
| |
| if No (Gen_Par) then |
| return Empty; |
| end if; |
| |
| -- If the generic parent type is still the generic type, this is a |
| -- private formal, not a derived formal, and there are no operations |
| -- inherited from the formal. |
| |
| if Nkind (Parent (Gen_Par)) = N_Formal_Type_Declaration then |
| return Empty; |
| end if; |
| |
| Gen_Prim := Collect_Primitive_Operations (Gen_Par); |
| |
| Elmt := First_Elmt (Gen_Prim); |
| while Present (Elmt) loop |
| if Chars (Node (Elmt)) = Chars (S) then |
| declare |
| F1 : Entity_Id; |
| F2 : Entity_Id; |
| |
| begin |
| F1 := First_Formal (S); |
| F2 := First_Formal (Node (Elmt)); |
| while Present (F1) |
| and then Present (F2) |
| loop |
| if Etype (F1) = Etype (F2) |
| or else Etype (F2) = Gen_Par |
| then |
| Next_Formal (F1); |
| Next_Formal (F2); |
| else |
| Next_Elmt (Elmt); |
| exit; -- not the right subprogram |
| end if; |
| |
| return Node (Elmt); |
| end loop; |
| end; |
| |
| else |
| Next_Elmt (Elmt); |
| end if; |
| end loop; |
| |
| raise Program_Error; |
| end Inherited_From_Formal; |
| |
| -------------------------- |
| -- In_Unfrozen_Instance -- |
| -------------------------- |
| |
| function In_Unfrozen_Instance (E : Entity_Id) return Boolean is |
| S : Entity_Id; |
| |
| begin |
| S := E; |
| while Present (S) and then S /= Standard_Standard loop |
| if Is_Generic_Instance (S) |
| and then Present (Freeze_Node (S)) |
| and then not Analyzed (Freeze_Node (S)) |
| then |
| return True; |
| end if; |
| |
| S := Scope (S); |
| end loop; |
| |
| return False; |
| end In_Unfrozen_Instance; |
| |
| ------------------------- |
| -- Is_Direct_Deep_Call -- |
| ------------------------- |
| |
| function Is_Direct_Deep_Call (Subp : Entity_Id) return Boolean is |
| begin |
| if Is_TSS (Subp, TSS_Deep_Adjust) |
| or else Is_TSS (Subp, TSS_Deep_Finalize) |
| or else Is_TSS (Subp, TSS_Deep_Initialize) |
| then |
| declare |
| Actual : Node_Id; |
| Formal : Node_Id; |
| |
| begin |
| Actual := First (Parameter_Associations (N)); |
| Formal := First_Formal (Subp); |
| while Present (Actual) |
| and then Present (Formal) |
| loop |
| if Nkind (Actual) = N_Identifier |
| and then Is_Controlling_Actual (Actual) |
| and then Etype (Actual) = Etype (Formal) |
| then |
| return True; |
| end if; |
| |
| Next (Actual); |
| Next_Formal (Formal); |
| end loop; |
| end; |
| end if; |
| |
| return False; |
| end Is_Direct_Deep_Call; |
| |
| --------------- |
| -- New_Value -- |
| --------------- |
| |
| function New_Value (From : Node_Id) return Node_Id is |
| Res : constant Node_Id := Duplicate_Subexpr (From); |
| begin |
| if Is_Access_Type (Etype (From)) then |
| return Make_Explicit_Dereference (Sloc (From), Prefix => Res); |
| else |
| return Res; |
| end if; |
| end New_Value; |
| |
| -- Local variables |
| |
| Curr_S : constant Entity_Id := Current_Scope; |
| Remote : constant Boolean := Is_Remote_Call (Call_Node); |
| Actual : Node_Id; |
| Formal : Entity_Id; |
| Orig_Subp : Entity_Id := Empty; |
| Param_Count : Natural := 0; |
| Parent_Formal : Entity_Id; |
| Parent_Subp : Entity_Id; |
| Scop : Entity_Id; |
| Subp : Entity_Id; |
| |
| Prev_Orig : Node_Id; |
| -- Original node for an actual, which may have been rewritten. If the |
| -- actual is a function call that has been transformed from a selected |
| -- component, the original node is unanalyzed. Otherwise, it carries |
| -- semantic information used to generate additional actuals. |
| |
| CW_Interface_Formals_Present : Boolean := False; |
| |
| -- Start of processing for Expand_Call |
| |
| begin |
| -- Expand the procedure call if the first actual has a dimension and if |
| -- the procedure is Put (Ada 2012). |
| |
| if Ada_Version >= Ada_2012 |
| and then Nkind (Call_Node) = N_Procedure_Call_Statement |
| and then Present (Parameter_Associations (Call_Node)) |
| then |
| Expand_Put_Call_With_Symbol (Call_Node); |
| end if; |
| |
| -- Ignore if previous error |
| |
| if Nkind (Call_Node) in N_Has_Etype |
| and then Etype (Call_Node) = Any_Type |
| then |
| return; |
| end if; |
| |
| -- Call using access to subprogram with explicit dereference |
| |
| if Nkind (Name (Call_Node)) = N_Explicit_Dereference then |
| Subp := Etype (Name (Call_Node)); |
| Parent_Subp := Empty; |
| |
| -- Case of call to simple entry, where the Name is a selected component |
| -- whose prefix is the task, and whose selector name is the entry name |
| |
| elsif Nkind (Name (Call_Node)) = N_Selected_Component then |
| Subp := Entity (Selector_Name (Name (Call_Node))); |
| Parent_Subp := Empty; |
| |
| -- Case of call to member of entry family, where Name is an indexed |
| -- component, with the prefix being a selected component giving the |
| -- task and entry family name, and the index being the entry index. |
| |
| elsif Nkind (Name (Call_Node)) = N_Indexed_Component then |
| Subp := Entity (Selector_Name (Prefix (Name (Call_Node)))); |
| Parent_Subp := Empty; |
| |
| -- Normal case |
| |
| else |
| Subp := Entity (Name (Call_Node)); |
| Parent_Subp := Alias (Subp); |
| |
| -- Replace call to Raise_Exception by call to Raise_Exception_Always |
| -- if we can tell that the first parameter cannot possibly be null. |
| -- This improves efficiency by avoiding a run-time test. |
| |
| -- We do not do this if Raise_Exception_Always does not exist, which |
| -- can happen in configurable run time profiles which provide only a |
| -- Raise_Exception. |
| |
| if Is_RTE (Subp, RE_Raise_Exception) |
| and then RTE_Available (RE_Raise_Exception_Always) |
| then |
| declare |
| FA : constant Node_Id := |
| Original_Node (First_Actual (Call_Node)); |
| |
| begin |
| -- The case we catch is where the first argument is obtained |
| -- using the Identity attribute (which must always be |
| -- non-null). |
| |
| if Nkind (FA) = N_Attribute_Reference |
| and then Attribute_Name (FA) = Name_Identity |
| then |
| Subp := RTE (RE_Raise_Exception_Always); |
| Set_Name (Call_Node, New_Occurrence_Of (Subp, Loc)); |
| end if; |
| end; |
| end if; |
| |
| if Ekind (Subp) = E_Entry then |
| Parent_Subp := Empty; |
| end if; |
| end if; |
| |
| -- Detect the following code in System.Finalization_Masters only on |
| -- .NET/JVM targets: |
| |
| -- procedure Finalize (Master : in out Finalization_Master) is |
| -- begin |
| -- . . . |
| -- begin |
| -- Finalize (Curr_Ptr.all); |
| |
| -- Since .NET/JVM compilers lack address arithmetic and Deep_Finalize |
| -- cannot be named in library or user code, the compiler has to deal |
| -- with this by transforming the call to Finalize into Deep_Finalize. |
| |
| if VM_Target /= No_VM |
| and then Chars (Subp) = Name_Finalize |
| and then Ekind (Curr_S) = E_Block |
| and then Ekind (Scope (Curr_S)) = E_Procedure |
| and then Chars (Scope (Curr_S)) = Name_Finalize |
| and then Etype (First_Formal (Scope (Curr_S))) = |
| RTE (RE_Finalization_Master) |
| then |
| declare |
| Deep_Fin : constant Entity_Id := |
| Find_Prim_Op (RTE (RE_Root_Controlled), |
| TSS_Deep_Finalize); |
| begin |
| -- Since Root_Controlled is a tagged type, the compiler should |
| -- always generate Deep_Finalize for it. |
| |
| pragma Assert (Present (Deep_Fin)); |
| |
| -- Generate: |
| -- Deep_Finalize (Curr_Ptr.all); |
| |
| Rewrite (N, |
| Make_Procedure_Call_Statement (Loc, |
| Name => |
| New_Occurrence_Of (Deep_Fin, Loc), |
| Parameter_Associations => |
| New_Copy_List_Tree (Parameter_Associations (N)))); |
| |
| Analyze (N); |
| return; |
| end; |
| end if; |
| |
| -- Ada 2005 (AI-345): We have a procedure call as a triggering |
| -- alternative in an asynchronous select or as an entry call in |
| -- a conditional or timed select. Check whether the procedure call |
| -- is a renaming of an entry and rewrite it as an entry call. |
| |
| if Ada_Version >= Ada_2005 |
| and then Nkind (Call_Node) = N_Procedure_Call_Statement |
| and then |
| ((Nkind (Parent (Call_Node)) = N_Triggering_Alternative |
| and then Triggering_Statement (Parent (Call_Node)) = Call_Node) |
| or else |
| (Nkind (Parent (Call_Node)) = N_Entry_Call_Alternative |
| and then Entry_Call_Statement (Parent (Call_Node)) = Call_Node)) |
| then |
| declare |
| Ren_Decl : Node_Id; |
| Ren_Root : Entity_Id := Subp; |
| |
| begin |
| -- This may be a chain of renamings, find the root |
| |
| if Present (Alias (Ren_Root)) then |
| Ren_Root := Alias (Ren_Root); |
| end if; |
| |
| if Present (Original_Node (Parent (Parent (Ren_Root)))) then |
| Ren_Decl := Original_Node (Parent (Parent (Ren_Root))); |
| |
| if Nkind (Ren_Decl) = N_Subprogram_Renaming_Declaration then |
| Rewrite (Call_Node, |
| Make_Entry_Call_Statement (Loc, |
| Name => |
| New_Copy_Tree (Name (Ren_Decl)), |
| Parameter_Associations => |
| New_Copy_List_Tree |
| (Parameter_Associations (Call_Node)))); |
| |
| return; |
| end if; |
| end if; |
| end; |
| end if; |
| |
| -- First step, compute extra actuals, corresponding to any Extra_Formals |
| -- present. Note that we do not access Extra_Formals directly, instead |
| -- we simply note the presence of the extra formals as we process the |
| -- regular formals collecting corresponding actuals in Extra_Actuals. |
| |
| -- We also generate any required range checks for actuals for in formals |
| -- as we go through the loop, since this is a convenient place to do it. |
| -- (Though it seems that this would be better done in Expand_Actuals???) |
| |
| -- Special case: Thunks must not compute the extra actuals; they must |
| -- just propagate to the target primitive their extra actuals. |
| |
| if Is_Thunk (Current_Scope) |
| and then Thunk_Entity (Current_Scope) = Subp |
| and then Present (Extra_Formals (Subp)) |
| then |
| pragma Assert (Present (Extra_Formals (Current_Scope))); |
| |
| declare |
| Target_Formal : Entity_Id; |
| Thunk_Formal : Entity_Id; |
| |
| begin |
| Target_Formal := Extra_Formals (Subp); |
| Thunk_Formal := Extra_Formals (Current_Scope); |
| while Present (Target_Formal) loop |
| Add_Extra_Actual |
| (New_Occurrence_Of (Thunk_Formal, Loc), Thunk_Formal); |
| |
| Target_Formal := Extra_Formal (Target_Formal); |
| Thunk_Formal := Extra_Formal (Thunk_Formal); |
| end loop; |
| |
| while Is_Non_Empty_List (Extra_Actuals) loop |
| Add_Actual_Parameter (Remove_Head (Extra_Actuals)); |
| end loop; |
| |
| Expand_Actuals (Call_Node, Subp); |
| return; |
| end; |
| end if; |
| |
| Formal := First_Formal (Subp); |
| Actual := First_Actual (Call_Node); |
| Param_Count := 1; |
| while Present (Formal) loop |
| |
| -- Generate range check if required |
| |
| if Do_Range_Check (Actual) |
| and then Ekind (Formal) = E_In_Parameter |
| then |
| Generate_Range_Check |
| (Actual, Etype (Formal), CE_Range_Check_Failed); |
| end if; |
| |
| -- Prepare to examine current entry |
| |
| Prev := Actual; |
| Prev_Orig := Original_Node (Prev); |
| |
| -- Ada 2005 (AI-251): Check if any formal is a class-wide interface |
| -- to expand it in a further round. |
| |
| CW_Interface_Formals_Present := |
| CW_Interface_Formals_Present |
| or else |
| (Ekind (Etype (Formal)) = E_Class_Wide_Type |
| and then Is_Interface (Etype (Etype (Formal)))) |
| or else |
| (Ekind (Etype (Formal)) = E_Anonymous_Access_Type |
| and then Is_Interface (Directly_Designated_Type |
| (Etype (Etype (Formal))))); |
| |
| -- Create possible extra actual for constrained case. Usually, the |
| -- extra actual is of the form actual'constrained, but since this |
| -- attribute is only available for unconstrained records, TRUE is |
| -- expanded if the type of the formal happens to be constrained (for |
| -- instance when this procedure is inherited from an unconstrained |
| -- record to a constrained one) or if the actual has no discriminant |
| -- (its type is constrained). An exception to this is the case of a |
| -- private type without discriminants. In this case we pass FALSE |
| -- because the object has underlying discriminants with defaults. |
| |
| if Present (Extra_Constrained (Formal)) then |
| if Ekind (Etype (Prev)) in Private_Kind |
| and then not Has_Discriminants (Base_Type (Etype (Prev))) |
| then |
| Add_Extra_Actual |
| (New_Occurrence_Of (Standard_False, Loc), |
| Extra_Constrained (Formal)); |
| |
| elsif Is_Constrained (Etype (Formal)) |
| or else not Has_Discriminants (Etype (Prev)) |
| then |
| Add_Extra_Actual |
| (New_Occurrence_Of (Standard_True, Loc), |
| Extra_Constrained (Formal)); |
| |
| -- Do not produce extra actuals for Unchecked_Union parameters. |
| -- Jump directly to the end of the loop. |
| |
| elsif Is_Unchecked_Union (Base_Type (Etype (Actual))) then |
| goto Skip_Extra_Actual_Generation; |
| |
| else |
| -- If the actual is a type conversion, then the constrained |
| -- test applies to the actual, not the target type. |
| |
| declare |
| Act_Prev : Node_Id; |
| |
| begin |
| -- Test for unchecked conversions as well, which can occur |
| -- as out parameter actuals on calls to stream procedures. |
| |
| Act_Prev := Prev; |
| while Nkind_In (Act_Prev, N_Type_Conversion, |
| N_Unchecked_Type_Conversion) |
| loop |
| Act_Prev := Expression (Act_Prev); |
| end loop; |
| |
| -- If the expression is a conversion of a dereference, this |
| -- is internally generated code that manipulates addresses, |
| -- e.g. when building interface tables. No check should |
| -- occur in this case, and the discriminated object is not |
| -- directly a hand. |
| |
| if not Comes_From_Source (Actual) |
| and then Nkind (Actual) = N_Unchecked_Type_Conversion |
| and then Nkind (Act_Prev) = N_Explicit_Dereference |
| then |
| Add_Extra_Actual |
| (New_Occurrence_Of (Standard_False, Loc), |
| Extra_Constrained (Formal)); |
| |
| else |
| Add_Extra_Actual |
| (Make_Attribute_Reference (Sloc (Prev), |
| Prefix => |
| Duplicate_Subexpr_No_Checks |
| (Act_Prev, Name_Req => True), |
| Attribute_Name => Name_Constrained), |
| Extra_Constrained (Formal)); |
| end if; |
| end; |
| end if; |
| end if; |
| |
| -- Create possible extra actual for accessibility level |
| |
| if Present (Extra_Accessibility (Formal)) then |
| |
| -- Ada 2005 (AI-252): If the actual was rewritten as an Access |
| -- attribute, then the original actual may be an aliased object |
| -- occurring as the prefix in a call using "Object.Operation" |
| -- notation. In that case we must pass the level of the object, |
| -- so Prev_Orig is reset to Prev and the attribute will be |
| -- processed by the code for Access attributes further below. |
| |
| if Prev_Orig /= Prev |
| and then Nkind (Prev) = N_Attribute_Reference |
| and then |
| Get_Attribute_Id (Attribute_Name (Prev)) = Attribute_Access |
| and then Is_Aliased_View (Prev_Orig) |
| then |
| Prev_Orig := Prev; |
| end if; |
| |
| -- Ada 2005 (AI-251): Thunks must propagate the extra actuals of |
| -- accessibility levels. |
| |
| if Is_Thunk (Current_Scope) then |
| declare |
| Parm_Ent : Entity_Id; |
| |
| begin |
| if Is_Controlling_Actual (Actual) then |
| |
| -- Find the corresponding actual of the thunk |
| |
| Parm_Ent := First_Entity (Current_Scope); |
| for J in 2 .. Param_Count loop |
| Next_Entity (Parm_Ent); |
| end loop; |
| |
| -- Handle unchecked conversion of access types generated |
| -- in thunks (cf. Expand_Interface_Thunk). |
| |
| elsif Is_Access_Type (Etype (Actual)) |
| and then Nkind (Actual) = N_Unchecked_Type_Conversion |
| then |
| Parm_Ent := Entity (Expression (Actual)); |
| |
| else pragma Assert (Is_Entity_Name (Actual)); |
| Parm_Ent := Entity (Actual); |
| end if; |
| |
| Add_Extra_Actual |
| (New_Occurrence_Of (Extra_Accessibility (Parm_Ent), Loc), |
| Extra_Accessibility (Formal)); |
| end; |
| |
| elsif Is_Entity_Name (Prev_Orig) then |
| |
| -- When passing an access parameter, or a renaming of an access |
| -- parameter, as the actual to another access parameter we need |
| -- to pass along the actual's own access level parameter. This |
| -- is done if we are within the scope of the formal access |
| -- parameter (if this is an inlined body the extra formal is |
| -- irrelevant). |
| |
| if (Is_Formal (Entity (Prev_Orig)) |
| or else |
| (Present (Renamed_Object (Entity (Prev_Orig))) |
| and then |
| Is_Entity_Name (Renamed_Object (Entity (Prev_Orig))) |
| and then |
| Is_Formal |
| (Entity (Renamed_Object (Entity (Prev_Orig)))))) |
| and then Ekind (Etype (Prev_Orig)) = E_Anonymous_Access_Type |
| and then In_Open_Scopes (Scope (Entity (Prev_Orig))) |
| then |
| declare |
| Parm_Ent : constant Entity_Id := Param_Entity (Prev_Orig); |
| |
| begin |
| pragma Assert (Present (Parm_Ent)); |
| |
| if Present (Extra_Accessibility (Parm_Ent)) then |
| Add_Extra_Actual |
| (New_Occurrence_Of |
| (Extra_Accessibility (Parm_Ent), Loc), |
| Extra_Accessibility (Formal)); |
| |
| -- If the actual access parameter does not have an |
| -- associated extra formal providing its scope level, |
| -- then treat the actual as having library-level |
| -- accessibility. |
| |
| else |
| Add_Extra_Actual |
| (Make_Integer_Literal (Loc, |
| Intval => Scope_Depth (Standard_Standard)), |
| Extra_Accessibility (Formal)); |
| end if; |
| end; |
| |
| -- The actual is a normal access value, so just pass the level |
| -- of the actual's access type. |
| |
| else |
| Add_Extra_Actual |
| (Dynamic_Accessibility_Level (Prev_Orig), |
| Extra_Accessibility (Formal)); |
| end if; |
| |
| -- If the actual is an access discriminant, then pass the level |
| -- of the enclosing object (RM05-3.10.2(12.4/2)). |
| |
| elsif Nkind (Prev_Orig) = N_Selected_Component |
| and then Ekind (Entity (Selector_Name (Prev_Orig))) = |
| E_Discriminant |
| and then Ekind (Etype (Entity (Selector_Name (Prev_Orig)))) = |
| E_Anonymous_Access_Type |
| then |
| Add_Extra_Actual |
| (Make_Integer_Literal (Loc, |
| Intval => Object_Access_Level (Prefix (Prev_Orig))), |
| Extra_Accessibility (Formal)); |
| |
| -- All other cases |
| |
| else |
| case Nkind (Prev_Orig) is |
| |
| when N_Attribute_Reference => |
| case Get_Attribute_Id (Attribute_Name (Prev_Orig)) is |
| |
| -- For X'Access, pass on the level of the prefix X |
| |
| when Attribute_Access => |
| |
| -- If this is an Access attribute applied to the |
| -- the current instance object passed to a type |
| -- initialization procedure, then use the level |
| -- of the type itself. This is not really correct, |
| -- as there should be an extra level parameter |
| -- passed in with _init formals (only in the case |
| -- where the type is immutably limited), but we |
| -- don't have an easy way currently to create such |
| -- an extra formal (init procs aren't ever frozen). |
| -- For now we just use the level of the type, |
| -- which may be too shallow, but that works better |
| -- than passing Object_Access_Level of the type, |
| -- which can be one level too deep in some cases. |
| -- ??? |
| |
| if Is_Entity_Name (Prefix (Prev_Orig)) |
| and then Is_Type (Entity (Prefix (Prev_Orig))) |
| then |
| Add_Extra_Actual |
| (Make_Integer_Literal (Loc, |
| Intval => |
| Type_Access_Level |
| (Entity (Prefix (Prev_Orig)))), |
| Extra_Accessibility (Formal)); |
| |
| else |
| Add_Extra_Actual |
| (Make_Integer_Literal (Loc, |
| Intval => |
| Object_Access_Level |
| (Prefix (Prev_Orig))), |
| Extra_Accessibility (Formal)); |
| end if; |
| |
| -- Treat the unchecked attributes as library-level |
| |
| when Attribute_Unchecked_Access | |
| Attribute_Unrestricted_Access => |
| Add_Extra_Actual |
| (Make_Integer_Literal (Loc, |
| Intval => Scope_Depth (Standard_Standard)), |
| Extra_Accessibility (Formal)); |
| |
| -- No other cases of attributes returning access |
| -- values that can be passed to access parameters. |
| |
| when others => |
| raise Program_Error; |
| |
| end case; |
| |
| -- For allocators we pass the level of the execution of the |
| -- called subprogram, which is one greater than the current |
| -- scope level. |
| |
| when N_Allocator => |
| Add_Extra_Actual |
| (Make_Integer_Literal (Loc, |
| Intval => Scope_Depth (Current_Scope) + 1), |
| Extra_Accessibility (Formal)); |
| |
| -- For most other cases we simply pass the level of the |
| -- actual's access type. The type is retrieved from |
| -- Prev rather than Prev_Orig, because in some cases |
| -- Prev_Orig denotes an original expression that has |
| -- not been analyzed. |
| |
| when others => |
| Add_Extra_Actual |
| (Dynamic_Accessibility_Level (Prev), |
| Extra_Accessibility (Formal)); |
| end case; |
| end if; |
| end if; |
| |
| -- Perform the check of 4.6(49) that prevents a null value from being |
| -- passed as an actual to an access parameter. Note that the check |
| -- is elided in the common cases of passing an access attribute or |
| -- access parameter as an actual. Also, we currently don't enforce |
| -- this check for expander-generated actuals and when -gnatdj is set. |
| |
| if Ada_Version >= Ada_2005 then |
| |
| -- Ada 2005 (AI-231): Check null-excluding access types. Note that |
| -- the intent of 6.4.1(13) is that null-exclusion checks should |
| -- not be done for 'out' parameters, even though it refers only |
| -- to constraint checks, and a null_exclusion is not a constraint. |
| -- Note that AI05-0196-1 corrects this mistake in the RM. |
| |
| if Is_Access_Type (Etype (Formal)) |
| and then Can_Never_Be_Null (Etype (Formal)) |
| and then Ekind (Formal) /= E_Out_Parameter |
| and then Nkind (Prev) /= N_Raise_Constraint_Error |
| and then (Known_Null (Prev) |
| or else not Can_Never_Be_Null (Etype (Prev))) |
| then |
| Install_Null_Excluding_Check (Prev); |
| end if; |
| |
| -- Ada_Version < Ada_2005 |
| |
| else |
| if Ekind (Etype (Formal)) /= E_Anonymous_Access_Type |
| or else Access_Checks_Suppressed (Subp) |
| then |
| null; |
| |
| elsif Debug_Flag_J then |
| null; |
| |
| elsif not Comes_From_Source (Prev) then |
| null; |
| |
| elsif Is_Entity_Name (Prev) |
| and then Ekind (Etype (Prev)) = E_Anonymous_Access_Type |
| then |
| null; |
| |
| elsif Nkind_In (Prev, N_Allocator, N_Attribute_Reference) then |
| null; |
| |
| -- Suppress null checks when passing to access parameters of Java |
| -- and CIL subprograms. (Should this be done for other foreign |
| -- conventions as well ???) |
| |
| elsif Convention (Subp) = Convention_Java |
| or else Convention (Subp) = Convention_CIL |
| then |
| null; |
| |
| else |
| Install_Null_Excluding_Check (Prev); |
| end if; |
| end if; |
| |
| -- Perform appropriate validity checks on parameters that |
| -- are entities. |
| |
| if Validity_Checks_On then |
| if (Ekind (Formal) = E_In_Parameter |
| and then Validity_Check_In_Params) |
| or else |
| (Ekind (Formal) = E_In_Out_Parameter |
| and then Validity_Check_In_Out_Params) |
| then |
| -- If the actual is an indexed component of a packed type (or |
| -- is an indexed or selected component whose prefix recursively |
| -- meets this condition), it has not been expanded yet. It will |
| -- be copied in the validity code that follows, and has to be |
| -- expanded appropriately, so reanalyze it. |
| |
| -- What we do is just to unset analyzed bits on prefixes till |
| -- we reach something that does not have a prefix. |
| |
| declare |
| Nod : Node_Id; |
| |
| begin |
| Nod := Actual; |
| while Nkind_In (Nod, N_Indexed_Component, |
| N_Selected_Component) |
| loop |
| Set_Analyzed (Nod, False); |
| Nod := Prefix (Nod); |
| end loop; |
| end; |
| |
| Ensure_Valid (Actual); |
| end if; |
| end if; |
| |
| -- For IN OUT and OUT parameters, ensure that subscripts are valid |
| -- since this is a left side reference. We only do this for calls |
| -- from the source program since we assume that compiler generated |
| -- calls explicitly generate any required checks. We also need it |
| -- only if we are doing standard validity checks, since clearly it is |
| -- not needed if validity checks are off, and in subscript validity |
| -- checking mode, all indexed components are checked with a call |
| -- directly from Expand_N_Indexed_Component. |
| |
| if Comes_From_Source (Call_Node) |
| and then Ekind (Formal) /= E_In_Parameter |
| and then Validity_Checks_On |
| and then Validity_Check_Default |
| and then not Validity_Check_Subscripts |
| then |
| Check_Valid_Lvalue_Subscripts (Actual); |
| end if; |
| |
| -- Mark any scalar OUT parameter that is a simple variable as no |
| -- longer known to be valid (unless the type is always valid). This |
| -- reflects the fact that if an OUT parameter is never set in a |
| -- procedure, then it can become invalid on the procedure return. |
| |
| if Ekind (Formal) = E_Out_Parameter |
| and then Is_Entity_Name (Actual) |
| and then Ekind (Entity (Actual)) = E_Variable |
| and then not Is_Known_Valid (Etype (Actual)) |
| then |
| Set_Is_Known_Valid (Entity (Actual), False); |
| end if; |
| |
| -- For an OUT or IN OUT parameter, if the actual is an entity, then |
| -- clear current values, since they can be clobbered. We are probably |
| -- doing this in more places than we need to, but better safe than |
| -- sorry when it comes to retaining bad current values. |
| |
| if Ekind (Formal) /= E_In_Parameter |
| and then Is_Entity_Name (Actual) |
| and then Present (Entity (Actual)) |
| then |
| declare |
| Ent : constant Entity_Id := Entity (Actual); |
| Sav : Node_Id; |
| |
| begin |
| -- For an OUT or IN OUT parameter that is an assignable entity, |
| -- we do not want to clobber the Last_Assignment field, since |
| -- if it is set, it was precisely because it is indeed an OUT |
| -- or IN OUT parameter. We do reset the Is_Known_Valid flag |
| -- since the subprogram could have returned in invalid value. |
| |
| if Ekind_In (Formal, E_Out_Parameter, E_In_Out_Parameter) |
| and then Is_Assignable (Ent) |
| then |
| Sav := Last_Assignment (Ent); |
| Kill_Current_Values (Ent); |
| Set_Last_Assignment (Ent, Sav); |
| Set_Is_Known_Valid (Ent, False); |
| |
| -- For all other cases, just kill the current values |
| |
| else |
| Kill_Current_Values (Ent); |
| end if; |
| end; |
| end if; |
| |
| -- If the formal is class wide and the actual is an aggregate, force |
| -- evaluation so that the back end who does not know about class-wide |
| -- type, does not generate a temporary of the wrong size. |
| |
| if not Is_Class_Wide_Type (Etype (Formal)) then |
| null; |
| |
| elsif Nkind (Actual) = N_Aggregate |
| or else (Nkind (Actual) = N_Qualified_Expression |
| and then Nkind (Expression (Actual)) = N_Aggregate) |
| then |
| Force_Evaluation (Actual); |
| end if; |
| |
| -- In a remote call, if the formal is of a class-wide type, check |
| -- that the actual meets the requirements described in E.4(18). |
| |
| if Remote and then Is_Class_Wide_Type (Etype (Formal)) then |
| Insert_Action (Actual, |
| Make_Transportable_Check (Loc, |
| Duplicate_Subexpr_Move_Checks (Actual))); |
| end if; |
| |
| -- This label is required when skipping extra actual generation for |
| -- Unchecked_Union parameters. |
| |
| <<Skip_Extra_Actual_Generation>> |
| |
| Param_Count := Param_Count + 1; |
| Next_Actual (Actual); |
| Next_Formal (Formal); |
| end loop; |
| |
| -- If we are calling an Ada 2012 function which needs to have the |
| -- "accessibility level determined by the point of call" (AI05-0234) |
| -- passed in to it, then pass it in. |
| |
| if Ekind_In (Subp, E_Function, E_Operator, E_Subprogram_Type) |
| and then |
| Present (Extra_Accessibility_Of_Result (Ultimate_Alias (Subp))) |
| then |
| declare |
| Ancestor : Node_Id := Parent (Call_Node); |
| Level : Node_Id := Empty; |
| Defer : Boolean := False; |
| |
| begin |
| -- Unimplemented: if Subp returns an anonymous access type, then |
| |
| -- a) if the call is the operand of an explict conversion, then |
| -- the target type of the conversion (a named access type) |
| -- determines the accessibility level pass in; |
| |
| -- b) if the call defines an access discriminant of an object |
| -- (e.g., the discriminant of an object being created by an |
| -- allocator, or the discriminant of a function result), |
| -- then the accessibility level to pass in is that of the |
| -- discriminated object being initialized). |
| |
| -- ??? |
| |
| while Nkind (Ancestor) = N_Qualified_Expression |
| loop |
| Ancestor := Parent (Ancestor); |
| end loop; |
| |
| case Nkind (Ancestor) is |
| when N_Allocator => |
| |
| -- At this point, we'd like to assign |
| |
| -- Level := Dynamic_Accessibility_Level (Ancestor); |
| |
| -- but Etype of Ancestor may not have been set yet, |
| -- so that doesn't work. |
| |
| -- Handle this later in Expand_Allocator_Expression. |
| |
| Defer := True; |
| |
| when N_Object_Declaration | N_Object_Renaming_Declaration => |
| declare |
| Def_Id : constant Entity_Id := |
| Defining_Identifier (Ancestor); |
| |
| begin |
| if Is_Return_Object (Def_Id) then |
| if Present (Extra_Accessibility_Of_Result |
| (Return_Applies_To (Scope (Def_Id)))) |
| then |
| -- Pass along value that was passed in if the |
| -- routine we are returning from also has an |
| -- Accessibility_Of_Result formal. |
| |
| Level := |
| New_Occurrence_Of |
| (Extra_Accessibility_Of_Result |
| (Return_Applies_To (Scope (Def_Id))), Loc); |
| end if; |
| else |
| Level := |
| Make_Integer_Literal (Loc, |
| Intval => Object_Access_Level (Def_Id)); |
| end if; |
| end; |
| |
| when N_Simple_Return_Statement => |
| if Present (Extra_Accessibility_Of_Result |
| (Return_Applies_To |
| (Return_Statement_Entity (Ancestor)))) |
| then |
| -- Pass along value that was passed in if the returned |
| -- routine also has an Accessibility_Of_Result formal. |
| |
| Level := |
| New_Occurrence_Of |
| (Extra_Accessibility_Of_Result |
| (Return_Applies_To |
| (Return_Statement_Entity (Ancestor))), Loc); |
| end if; |
| |
| when others => |
| null; |
| end case; |
| |
| if not Defer then |
| if not Present (Level) then |
| |
| -- The "innermost master that evaluates the function call". |
| |
| -- ??? - Should we use Integer'Last here instead in order |
| -- to deal with (some of) the problems associated with |
| -- calls to subps whose enclosing scope is unknown (e.g., |
| -- Anon_Access_To_Subp_Param.all)? |
| |
| Level := Make_Integer_Literal (Loc, |
| Scope_Depth (Current_Scope) + 1); |
| end if; |
| |
| Add_Extra_Actual |
| (Level, |
| Extra_Accessibility_Of_Result (Ultimate_Alias (Subp))); |
| end if; |
| end; |
| end if; |
| |
| -- If we are expanding the RHS of an assignment we need to check if tag |
| -- propagation is needed. You might expect this processing to be in |
| -- Analyze_Assignment but has to be done earlier (bottom-up) because the |
| -- assignment might be transformed to a declaration for an unconstrained |
| -- value if the expression is classwide. |
| |
| if Nkind (Call_Node) = N_Function_Call |
| and then Is_Tag_Indeterminate (Call_Node) |
| and then Is_Entity_Name (Name (Call_Node)) |
| then |
| declare |
| Ass : Node_Id := Empty; |
| |
| begin |
| if Nkind (Parent (Call_Node)) = N_Assignment_Statement then |
| Ass := Parent (Call_Node); |
| |
| elsif Nkind (Parent (Call_Node)) = N_Qualified_Expression |
| and then Nkind (Parent (Parent (Call_Node))) = |
| N_Assignment_Statement |
| then |
| Ass := Parent (Parent (Call_Node)); |
| |
| elsif Nkind (Parent (Call_Node)) = N_Explicit_Dereference |
| and then Nkind (Parent (Parent (Call_Node))) = |
| N_Assignment_Statement |
| then |
| Ass := Parent (Parent (Call_Node)); |
| end if; |
| |
| if Present (Ass) |
| and then Is_Class_Wide_Type (Etype (Name (Ass))) |
| then |
| if Is_Access_Type (Etype (Call_Node)) then |
| if Designated_Type (Etype (Call_Node)) /= |
| Root_Type (Etype (Name (Ass))) |
| then |
| Error_Msg_NE |
| ("tag-indeterminate expression " |
| & " must have designated type& (RM 5.2 (6))", |
| Call_Node, Root_Type (Etype (Name (Ass)))); |
| else |
| Propagate_Tag (Name (Ass), Call_Node); |
| end if; |
| |
| elsif Etype (Call_Node) /= Root_Type (Etype (Name (Ass))) then |
| Error_Msg_NE |
| ("tag-indeterminate expression must have type&" |
| & "(RM 5.2 (6))", |
| Call_Node, Root_Type (Etype (Name (Ass)))); |
| |
| else |
| Propagate_Tag (Name (Ass), Call_Node); |
| end if; |
| |
| -- The call will be rewritten as a dispatching call, and |
| -- expanded as such. |
| |
| return; |
| end if; |
| end; |
| end if; |
| |
| -- Ada 2005 (AI-251): If some formal is a class-wide interface, expand |
| -- it to point to the correct secondary virtual table |
| |
| if Nkind (Call_Node) in N_Subprogram_Call |
| and then CW_Interface_Formals_Present |
| then |
| Expand_Interface_Actuals (Call_Node); |
| end if; |
| |
| -- Deals with Dispatch_Call if we still have a call, before expanding |
| -- extra actuals since this will be done on the re-analysis of the |
| -- dispatching call. Note that we do not try to shorten the actual list |
| -- for a dispatching call, it would not make sense to do so. Expansion |
| -- of dispatching calls is suppressed when VM_Target, because the VM |
| -- back-ends directly handle the generation of dispatching calls and |
| -- would have to undo any expansion to an indirect call. |
| |
| if Nkind (Call_Node) in N_Subprogram_Call |
| and then Present (Controlling_Argument (Call_Node)) |
| then |
| declare |
| Call_Typ : constant Entity_Id := Etype (Call_Node); |
| Typ : constant Entity_Id := Find_Dispatching_Type (Subp); |
| Eq_Prim_Op : Entity_Id := Empty; |
| New_Call : Node_Id; |
| Param : Node_Id; |
| Prev_Call : Node_Id; |
| |
| begin |
| if not Is_Limited_Type (Typ) then |
| Eq_Prim_Op := Find_Prim_Op (Typ, Name_Op_Eq); |
| end if; |
| |
| if Tagged_Type_Expansion then |
| Expand_Dispatching_Call (Call_Node); |
| |
| -- The following return is worrisome. Is it really OK to skip |
| -- all remaining processing in this procedure ??? |
| |
| return; |
| |
| -- VM targets |
| |
| else |
| Apply_Tag_Checks (Call_Node); |
| |
| -- If this is a dispatching "=", we must first compare the |
| -- tags so we generate: x.tag = y.tag and then x = y |
| |
| if Subp = Eq_Prim_Op then |
| |
| -- Mark the node as analyzed to avoid reanalizing this |
| -- dispatching call (which would cause a never-ending loop) |
| |
| Prev_Call := Relocate_Node (Call_Node); |
| Set_Analyzed (Prev_Call); |
| |
| Param := First_Actual (Call_Node); |
| New_Call := |
| Make_And_Then (Loc, |
| Left_Opnd => |
| Make_Op_Eq (Loc, |
| Left_Opnd => |
| Make_Selected_Component (Loc, |
| Prefix => New_Value (Param), |
| Selector_Name => |
| New_Occurrence_Of |
| (First_Tag_Component (Typ), Loc)), |
| |
| Right_Opnd => |
| Make_Selected_Component (Loc, |
| Prefix => |
| Unchecked_Convert_To (Typ, |
| New_Value (Next_Actual (Param))), |
| Selector_Name => |
| New_Occurrence_Of |
| (First_Tag_Component (Typ), Loc))), |
| Right_Opnd => Prev_Call); |
| |
| Rewrite (Call_Node, New_Call); |
| |
| Analyze_And_Resolve |
| (Call_Node, Call_Typ, Suppress => All_Checks); |
| end if; |
| |
| -- Expansion of a dispatching call results in an indirect call, |
| -- which in turn causes current values to be killed (see |
| -- Resolve_Call), so on VM targets we do the call here to |
| -- ensure consistent warnings between VM and non-VM targets. |
| |
| Kill_Current_Values; |
| end if; |
| |
| -- If this is a dispatching "=" then we must update the reference |
| -- to the call node because we generated: |
| -- x.tag = y.tag and then x = y |
| |
| if Subp = Eq_Prim_Op then |
| Call_Node := Right_Opnd (Call_Node); |
| end if; |
| end; |
| end if; |
| |
| -- Similarly, expand calls to RCI subprograms on which pragma |
| -- All_Calls_Remote applies. The rewriting will be reanalyzed |
| -- later. Do this only when the call comes from source since we |
| -- do not want such a rewriting to occur in expanded code. |
| |
| if Is_All_Remote_Call (Call_Node) then |
| Expand_All_Calls_Remote_Subprogram_Call (Call_Node); |
| |
| -- Similarly, do not add extra actuals for an entry call whose entity |
| -- is a protected procedure, or for an internal protected subprogram |
| -- call, because it will be rewritten as a protected subprogram call |
| -- and reanalyzed (see Expand_Protected_Subprogram_Call). |
| |
| elsif Is_Protected_Type (Scope (Subp)) |
| and then (Ekind (Subp) = E_Procedure |
| or else Ekind (Subp) = E_Function) |
| then |
| null; |
| |
| -- During that loop we gathered the extra actuals (the ones that |
| -- correspond to Extra_Formals), so now they can be appended. |
| |
| else |
| while Is_Non_Empty_List (Extra_Actuals) loop |
| Add_Actual_Parameter (Remove_Head (Extra_Actuals)); |
| end loop; |
| end if; |
| |
| -- At this point we have all the actuals, so this is the point at which |
| -- the various expansion activities for actuals is carried out. |
| |
| Expand_Actuals (Call_Node, Subp); |
| |
| -- Verify that the actuals do not share storage. This check must be done |
| -- on the caller side rather that inside the subprogram to avoid issues |
| -- of parameter passing. |
| |
| if Check_Aliasing_Of_Parameters then |
| Apply_Parameter_Aliasing_Checks (Call_Node, Subp); |
| end if; |
| |
| -- If the subprogram is a renaming, or if it is inherited, replace it in |
| -- the call with the name of the actual subprogram being called. If this |
| -- is a dispatching call, the run-time decides what to call. The Alias |
| -- attribute does not apply to entries. |
| |
| if Nkind (Call_Node) /= N_Entry_Call_Statement |
| and then No (Controlling_Argument (Call_Node)) |
| and then Present (Parent_Subp) |
| and then not Is_Direct_Deep_Call (Subp) |
| then |
| if Present (Inherited_From_Formal (Subp)) then |
| Parent_Subp := Inherited_From_Formal (Subp); |
| else |
| Parent_Subp := Ultimate_Alias (Parent_Subp); |
| end if; |
| |
| -- The below setting of Entity is suspect, see F109-018 discussion??? |
| |
| Set_Entity (Name (Call_Node), Parent_Subp); |
| |
| if Is_Abstract_Subprogram (Parent_Subp) |
| and then not In_Instance |
| then |
| Error_Msg_NE |
| ("cannot call abstract subprogram &!", |
| Name (Call_Node), Parent_Subp); |
| end if; |
| |
| -- Inspect all formals of derived subprogram Subp. Compare parameter |
| -- types with the parent subprogram and check whether an actual may |
| -- need a type conversion to the corresponding formal of the parent |
| -- subprogram. |
| |
| -- Not clear whether intrinsic subprograms need such conversions. ??? |
| |
| if not Is_Intrinsic_Subprogram (Parent_Subp) |
| or else Is_Generic_Instance (Parent_Subp) |
| then |
| declare |
| procedure Convert (Act : Node_Id; Typ : Entity_Id); |
| -- Rewrite node Act as a type conversion of Act to Typ. Analyze |
| -- and resolve the newly generated construct. |
| |
| ------------- |
| -- Convert -- |
| ------------- |
| |
| procedure Convert (Act : Node_Id; Typ : Entity_Id) is |
| begin |
| Rewrite (Act, OK_Convert_To (Typ, Relocate_Node (Act))); |
| Analyze (Act); |
| Resolve (Act, Typ); |
| end Convert; |
| |
| -- Local variables |
| |
| Actual_Typ : Entity_Id; |
| Formal_Typ : Entity_Id; |
| Parent_Typ : Entity_Id; |
| |
| begin |
| Actual := First_Actual (Call_Node); |
| Formal := First_Formal (Subp); |
| Parent_Formal := First_Formal (Parent_Subp); |
| while Present (Formal) loop |
| Actual_Typ := Etype (Actual); |
| Formal_Typ := Etype (Formal); |
| Parent_Typ := Etype (Parent_Formal); |
| |
| -- For an IN parameter of a scalar type, the parent formal |
| -- type and derived formal type differ or the parent formal |
| -- type and actual type do not match statically. |
| |
| if Is_Scalar_Type (Formal_Typ) |
| and then Ekind (Formal) = E_In_Parameter |
| and then Formal_Typ /= Parent_Typ |
| and then |
| not Subtypes_Statically_Match (Parent_Typ, Actual_Typ) |
| and then not Raises_Constraint_Error (Actual) |
| then |
| Convert (Actual, Parent_Typ); |
| Enable_Range_Check (Actual); |
| |
| -- If the actual has been marked as requiring a range |
| -- check, then generate it here. |
| |
| if Do_Range_Check (Actual) then |
| Generate_Range_Check |
| (Actual, Etype (Formal), CE_Range_Check_Failed); |
| end if; |
| |
| -- For access types, the parent formal type and actual type |
| -- differ. |
| |
| elsif Is_Access_Type (Formal_Typ) |
| and then Base_Type (Parent_Typ) /= Base_Type (Actual_Typ) |
| then |
| if Ekind (Formal) /= E_In_Parameter then |
| Convert (Actual, Parent_Typ); |
| |
| elsif Ekind (Parent_Typ) = E_Anonymous_Access_Type |
| and then Designated_Type (Parent_Typ) /= |
| Designated_Type (Actual_Typ) |
| and then not Is_Controlling_Formal (Formal) |
| then |
| -- This unchecked conversion is not necessary unless |
| -- inlining is enabled, because in that case the type |
| -- mismatch may become visible in the body about to be |
| -- inlined. |
| |
| Rewrite (Actual, |
| Unchecked_Convert_To (Parent_Typ, |
| Relocate_Node (Actual))); |
| Analyze (Actual); |
| Resolve (Actual, Parent_Typ); |
| end if; |
| |
| -- If there is a change of representation, then generate a |
| -- warning, and do the change of representation. |
| |
| elsif not Same_Representation (Formal_Typ, Parent_Typ) then |
| Error_Msg_N |
| ("??change of representation required", Actual); |
| Convert (Actual, Parent_Typ); |
| |
| -- For array and record types, the parent formal type and |
| -- derived formal type have different sizes or pragma Pack |
| -- status. |
| |
| elsif ((Is_Array_Type (Formal_Typ) |
| and then Is_Array_Type (Parent_Typ)) |
| or else |
| (Is_Record_Type (Formal_Typ) |
| and then Is_Record_Type (Parent_Typ))) |
| and then |
| (Esize (Formal_Typ) /= Esize (Parent_Typ) |
| or else Has_Pragma_Pack (Formal_Typ) /= |
| Has_Pragma_Pack (Parent_Typ)) |
| then |
| Convert (Actual, Parent_Typ); |
| end if; |
| |
| Next_Actual (Actual); |
| Next_Formal (Formal); |
| Next_Formal (Parent_Formal); |
| end loop; |
| end; |
| end if; |
| |
| Orig_Subp := Subp; |
| Subp := Parent_Subp; |
| end if; |
| |
| -- Deal with case where call is an explicit dereference |
| |
| if Nkind (Name (Call_Node)) = N_Explicit_Dereference then |
| |
| -- Handle case of access to protected subprogram type |
| |
| if Is_Access_Protected_Subprogram_Type |
| (Base_Type (Etype (Prefix (Name (Call_Node))))) |
| then |
| -- If this is a call through an access to protected operation, the |
| -- prefix has the form (object'address, operation'access). Rewrite |
| -- as a for other protected calls: the object is the 1st parameter |
| -- of the list of actuals. |
| |
| declare |
| Call : Node_Id; |
| Parm : List_Id; |
| Nam : Node_Id; |
| Obj : Node_Id; |
| Ptr : constant Node_Id := Prefix (Name (Call_Node)); |
| |
| T : constant Entity_Id := |
| Equivalent_Type (Base_Type (Etype (Ptr))); |
| |
| D_T : constant Entity_Id := |
| Designated_Type (Base_Type (Etype (Ptr))); |
| |
| begin |
| Obj := |
| Make_Selected_Component (Loc, |
| Prefix => Unchecked_Convert_To (T, Ptr), |
| Selector_Name => |
| New_Occurrence_Of (First_Entity (T), Loc)); |
| |
| Nam := |
| Make_Selected_Component (Loc, |
| Prefix => Unchecked_Convert_To (T, Ptr), |
| Selector_Name => |
| New_Occurrence_Of (Next_Entity (First_Entity (T)), Loc)); |
| |
| Nam := |
| Make_Explicit_Dereference (Loc, |
| Prefix => Nam); |
| |
| if Present (Parameter_Associations (Call_Node)) then |
| Parm := Parameter_Associations (Call_Node); |
| else |
| Parm := New_List; |
| end if; |
| |
| Prepend (Obj, Parm); |
| |
| if Etype (D_T) = Standard_Void_Type then |
| Call := |
| Make_Procedure_Call_Statement (Loc, |
| Name => Nam, |
| Parameter_Associations => Parm); |
| else |
| Call := |
| Make_Function_Call (Loc, |
| Name => Nam, |
| Parameter_Associations => Parm); |
| end if; |
| |
| Set_First_Named_Actual (Call, First_Named_Actual (Call_Node)); |
| Set_Etype (Call, Etype (D_T)); |
| |
| -- We do not re-analyze the call to avoid infinite recursion. |
| -- We analyze separately the prefix and the object, and set |
| -- the checks on the prefix that would otherwise be emitted |
| -- when resolving a call. |
| |
| Rewrite (Call_Node, Call); |
| Analyze (Nam); |
| Apply_Access_Check (Nam); |
| Analyze (Obj); |
| return; |
| end; |
| end if; |
| end if; |
| |
| -- If this is a call to an intrinsic subprogram, then perform the |
| -- appropriate expansion to the corresponding tree node and we |
| -- are all done (since after that the call is gone). |
| |
| -- In the case where the intrinsic is to be processed by the back end, |
| -- the call to Expand_Intrinsic_Call will do nothing, which is fine, |
| -- since the idea in this case is to pass the call unchanged. If the |
| -- intrinsic is an inherited unchecked conversion, and the derived type |
| -- is the target type of the conversion, we must retain it as the return |
| -- type of the expression. Otherwise the expansion below, which uses the |
| -- parent operation, will yield the wrong type. |
| |
| if Is_Intrinsic_Subprogram (Subp) then |
| Expand_Intrinsic_Call (Call_Node, Subp); |
| |
| if Nkind (Call_Node) = N_Unchecked_Type_Conversion |
| and then Parent_Subp /= Orig_Subp |
| and then Etype (Parent_Subp) /= Etype (Orig_Subp) |
| then |
| Set_Etype (Call_Node, Etype (Orig_Subp)); |
| end if; |
| |
| return; |
| end if; |
| |
| if Ekind_In (Subp, E_Function, E_Procedure) then |
| |
| -- We perform two simple optimization on calls: |
| |
| -- a) replace calls to null procedures unconditionally; |
| |
| -- b) for To_Address, just do an unchecked conversion. Not only is |
| -- this efficient, but it also avoids order of elaboration problems |
| -- when address clauses are inlined (address expression elaborated |
| -- at the wrong point). |
| |
| -- We perform these optimization regardless of whether we are in the |
| -- main unit or in a unit in the context of the main unit, to ensure |
| -- that tree generated is the same in both cases, for CodePeer use. |
| |
| if Is_RTE (Subp, RE_To_Address) then |
| Rewrite (Call_Node, |
| Unchecked_Convert_To |
| (RTE (RE_Address), Relocate_Node (First_Actual (Call_Node)))); |
| return; |
| |
| elsif Is_Null_Procedure (Subp) then |
| Rewrite (Call_Node, Make_Null_Statement (Loc)); |
| return; |
| end if; |
| |
| -- Handle inlining. No action needed if the subprogram is not inlined |
| |
| if not Is_Inlined (Subp) then |
| null; |
| |
| -- Handle frontend inlining |
| |
| elsif not Back_End_Inlining then |
| Inlined_Subprogram : declare |
| Bod : Node_Id; |
| Must_Inline : Boolean := False; |
| Spec : constant Node_Id := Unit_Declaration_Node (Subp); |
| |
| begin |
| -- Verify that the body to inline has already been seen, and |
| -- that if the body is in the current unit the inlining does |
| -- not occur earlier. This avoids order-of-elaboration problems |
| -- in the back end. |
| |
| -- This should be documented in sinfo/einfo ??? |
| |
| if No (Spec) |
| or else Nkind (Spec) /= N_Subprogram_Declaration |
| or else No (Body_To_Inline (Spec)) |
| then |
| Must_Inline := False; |
| |
| -- If this an inherited function that returns a private type, |
| -- do not inline if the full view is an unconstrained array, |
| -- because such calls cannot be inlined. |
| |
| elsif Present (Orig_Subp) |
| and then Is_Array_Type (Etype (Orig_Subp)) |
| and then not Is_Constrained (Etype (Orig_Subp)) |
| then |
| Must_Inline := False; |
| |
| elsif In_Unfrozen_Instance (Scope (Subp)) then |
| Must_Inline := False; |
| |
| else |
| Bod := Body_To_Inline (Spec); |
| |
| if (In_Extended_Main_Code_Unit (Call_Node) |
| or else In_Extended_Main_Code_Unit (Parent (Call_Node)) |
| or else Has_Pragma_Inline_Always (Subp)) |
| and then (not In_Same_Extended_Unit (Sloc (Bod), Loc) |
| or else |
| Earlier_In_Extended_Unit (Sloc (Bod), Loc)) |
| then |
| Must_Inline := True; |
| |
| -- If we are compiling a package body that is not the main |
| -- unit, it must be for inlining/instantiation purposes, |
| -- in which case we inline the call to insure that the same |
| -- temporaries are generated when compiling the body by |
| -- itself. Otherwise link errors can occur. |
| |
| -- If the function being called is itself in the main unit, |
| -- we cannot inline, because there is a risk of double |
| -- elaboration and/or circularity: the inlining can make |
| -- visible a private entity in the body of the main unit, |
| -- that gigi will see before its sees its proper definition. |
| |
| elsif not (In_Extended_Main_Code_Unit (Call_Node)) |
| and then In_Package_Body |
| then |
| Must_Inline := not In_Extended_Main_Source_Unit (Subp); |
| end if; |
| end if; |
| |
| if Must_Inline then |
| Expand_Inlined_Call (Call_Node, Subp, Orig_Subp); |
| |
| else |
| -- Let the back end handle it |
| |
| Add_Inlined_Body (Subp, Call_Node); |
| |
| if Front_End_Inlining |
| and then Nkind (Spec) = N_Subprogram_Declaration |
| and then (In_Extended_Main_Code_Unit (Call_Node)) |
| and then No (Body_To_Inline (Spec)) |
| and then not Has_Completion (Subp) |
| and then In_Same_Extended_Unit (Sloc (Spec), Loc) |
| then |
| Cannot_Inline |
| ("cannot inline& (body not seen yet)?", |
| Call_Node, Subp); |
| end if; |
| end if; |
| end Inlined_Subprogram; |
| |
| -- Back end inlining: let the back end handle it |
| |
| elsif No (Unit_Declaration_Node (Subp)) |
| or else Nkind (Unit_Declaration_Node (Subp)) /= |
| N_Subprogram_Declaration |
| or else No (Body_To_Inline (Unit_Declaration_Node (Subp))) |
| or else Nkind (Body_To_Inline (Unit_Declaration_Node (Subp))) in |
| N_Entity |
| then |
| Add_Inlined_Body (Subp, Call_Node); |
| |
| -- Front end expansion of simple functions returning unconstrained |
| -- types (see Check_And_Split_Unconstrained_Function). Note that the |
| -- case of a simple renaming (Body_To_Inline in N_Entity above, see |
| -- also Build_Renamed_Body) cannot be expanded here because this may |
| -- give rise to order-of-elaboration issues for the types of the |
| -- parameters of the subprogram, if any. |
| |
| else |
| Expand_Inlined_Call (Call_Node, Subp, Orig_Subp); |
| end if; |
| end if; |
| |
| -- Check for protected subprogram. This is either an intra-object call, |
| -- or a protected function call. Protected procedure calls are rewritten |
| -- as entry calls and handled accordingly. |
| |
| -- In Ada 2005, this may be an indirect call to an access parameter that |
| -- is an access_to_subprogram. In that case the anonymous type has a |
| -- scope that is a protected operation, but the call is a regular one. |
| -- In either case do not expand call if subprogram is eliminated. |
| |
| Scop := Scope (Subp); |
| |
| if Nkind (Call_Node) /= N_Entry_Call_Statement |
| and then Is_Protected_Type (Scop) |
| and then Ekind (Subp) /= E_Subprogram_Type |
| and then not Is_Eliminated (Subp) |
| then |
| -- If the call is an internal one, it is rewritten as a call to the |
| -- corresponding unprotected subprogram. |
| |
| Expand_Protected_Subprogram_Call (Call_Node, Subp, Scop); |
| end if; |
| |
| -- Functions returning controlled objects need special attention. If |
| -- the return type is limited, then the context is initialization and |
| -- different processing applies. If the call is to a protected function, |
| -- the expansion above will call Expand_Call recursively. Otherwise the |
| -- function call is transformed into a temporary which obtains the |
| -- result from the secondary stack. |
| |
| if Needs_Finalization (Etype (Subp)) then |
| if not Is_Limited_View (Etype (Subp)) |
| and then |
| (No (First_Formal (Subp)) |
| or else |
| not Is_Concurrent_Record_Type (Etype (First_Formal (Subp)))) |
| then |
| Expand_Ctrl_Function_Call (Call_Node); |
| |
| -- Build-in-place function calls which appear in anonymous contexts |
| -- need a transient scope to ensure the proper finalization of the |
| -- intermediate result after its use. |
| |
| elsif Is_Build_In_Place_Function_Call (Call_Node) |
| and then |
| Nkind_In (Parent (Call_Node), N_Attribute_Reference, |
| N_Function_Call, |
| N_Indexed_Component, |
| N_Object_Renaming_Declaration, |
| N_Procedure_Call_Statement, |
| N_Selected_Component, |
| N_Slice) |
| then |
| Establish_Transient_Scope (Call_Node, Sec_Stack => True); |
| end if; |
| end if; |
| end Expand_Call; |
| |
| ------------------------------- |
| -- Expand_Ctrl_Function_Call -- |
| ------------------------------- |
| |
| procedure Expand_Ctrl_Function_Call (N : Node_Id) is |
| function Is_Element_Reference (N : Node_Id) return Boolean; |
| -- Determine whether node N denotes a reference to an Ada 2012 container |
| -- element. |
| |
| -------------------------- |
| -- Is_Element_Reference -- |
| -------------------------- |
| |
| function Is_Element_Reference (N : Node_Id) return Boolean is |
| Ref : constant Node_Id := Original_Node (N); |
| |
| begin |
| -- Analysis marks an element reference by setting the generalized |
| -- indexing attribute of an indexed component before the component |
| -- is rewritten into a function call. |
| |
| return |
| Nkind (Ref) = N_Indexed_Component |
| and then Present (Generalized_Indexing (Ref)); |
| end Is_Element_Reference; |
| |
| -- Local variables |
| |
| Is_Elem_Ref : constant Boolean := Is_Element_Reference (N); |
| |
| -- Start of processing for Expand_Ctrl_Function_Call |
| |
| begin |
| -- Optimization, if the returned value (which is on the sec-stack) is |
| -- returned again, no need to copy/readjust/finalize, we can just pass |
| -- the value thru (see Expand_N_Simple_Return_Statement), and thus no |
| -- attachment is needed |
| |
| if Nkind (Parent (N)) = N_Simple_Return_Statement then |
| return; |
| end if; |
| |
| -- Resolution is now finished, make sure we don't start analysis again |
| -- because of the duplication. |
| |
| Set_Analyzed (N); |
| |
| -- A function which returns a controlled object uses the secondary |
| -- stack. Rewrite the call into a temporary which obtains the result of |
| -- the function using 'reference. |
| |
| Remove_Side_Effects (N); |
| |
| -- When the temporary function result appears inside a case expression |
| -- or an if expression, its lifetime must be extended to match that of |
| -- the context. If not, the function result will be finalized too early |
| -- and the evaluation of the expression could yield incorrect result. An |
| -- exception to this rule are references to Ada 2012 container elements. |
| -- Such references must be finalized at the end of each iteration of the |
| -- related quantified expression, otherwise the container will remain |
| -- busy. |
| |
| if not Is_Elem_Ref |
| and then Within_Case_Or_If_Expression (N) |
| and then Nkind (N) = N_Explicit_Dereference |
| then |
| Set_Is_Processed_Transient (Entity (Prefix (N))); |
| end if; |
| end Expand_Ctrl_Function_Call; |
| |
| ---------------------------------------- |
| -- Expand_N_Extended_Return_Statement -- |
| ---------------------------------------- |
| |
| -- If there is a Handled_Statement_Sequence, we rewrite this: |
| |
| -- return Result : T := <expression> do |
| -- <handled_seq_of_stms> |
| -- end return; |
| |
| -- to be: |
| |
| -- declare |
| -- Result : T := <expression>; |
| -- begin |
| -- <handled_seq_of_stms> |
| -- return Result; |
| -- end; |
| |
| -- Otherwise (no Handled_Statement_Sequence), we rewrite this: |
| |
| -- return Result : T := <expression>; |
| |
| -- to be: |
| |
| -- return <expression>; |
| |
| -- unless it's build-in-place or there's no <expression>, in which case |
| -- we generate: |
| |
| -- declare |
| -- Result : T := <expression>; |
| -- begin |
| -- return Result; |
| -- end; |
| |
| -- Note that this case could have been written by the user as an extended |
| -- return statement, or could have been transformed to this from a simple |
| -- return statement. |
| |
| -- That is, we need to have a reified return object if there are statements |
| -- (which might refer to it) or if we're doing build-in-place (so we can |
| -- set its address to the final resting place or if there is no expression |
| -- (in which case default initial values might need to be set). |
| |
| procedure Expand_N_Extended_Return_Statement (N : Node_Id) is |
| Loc : constant Source_Ptr := Sloc (N); |
| |
| Par_Func : constant Entity_Id := |
| Return_Applies_To (Return_Statement_Entity (N)); |
| Result_Subt : constant Entity_Id := Etype (Par_Func); |
| Ret_Obj_Id : constant Entity_Id := |
| First_Entity (Return_Statement_Entity (N)); |
| Ret_Obj_Decl : constant Node_Id := Parent (Ret_Obj_Id); |
| |
| Is_Build_In_Place : constant Boolean := |
| Is_Build_In_Place_Function (Par_Func); |
| |
| Exp : Node_Id; |
| HSS : Node_Id; |
| Result : Node_Id; |
| Return_Stmt : Node_Id; |
| Stmts : List_Id; |
| |
| function Build_Heap_Allocator |
| (Temp_Id : Entity_Id; |
| Temp_Typ : Entity_Id; |
| Func_Id : Entity_Id; |
| Ret_Typ : Entity_Id; |
| Alloc_Expr : Node_Id) return Node_Id; |
| -- Create the statements necessary to allocate a return object on the |
| -- caller's master. The master is available through implicit parameter |
| -- BIPfinalizationmaster. |
| -- |
| -- if BIPfinalizationmaster /= null then |
| -- declare |
| -- type Ptr_Typ is access Ret_Typ; |
| -- for Ptr_Typ'Storage_Pool use |
| -- Base_Pool (BIPfinalizationmaster.all).all; |
| -- Local : Ptr_Typ; |
| -- |
| -- begin |
| -- procedure Allocate (...) is |
| -- begin |
| -- System.Storage_Pools.Subpools.Allocate_Any (...); |
| -- end Allocate; |
| -- |
| -- Local := <Alloc_Expr>; |
| -- Temp_Id := Temp_Typ (Local); |
| -- end; |
| -- end if; |
| -- |
| -- Temp_Id is the temporary which is used to reference the internally |
| -- created object in all allocation forms. Temp_Typ is the type of the |
| -- temporary. Func_Id is the enclosing function. Ret_Typ is the return |
| -- type of Func_Id. Alloc_Expr is the actual allocator. |
| |
| function Move_Activation_Chain return Node_Id; |
| -- Construct a call to System.Tasking.Stages.Move_Activation_Chain |
| -- with parameters: |
| -- From current activation chain |
| -- To activation chain passed in by the caller |
| -- New_Master master passed in by the caller |
| |
| -------------------------- |
| -- Build_Heap_Allocator -- |
| -------------------------- |
| |
| function Build_Heap_Allocator |
| (Temp_Id : Entity_Id; |
| Temp_Typ : Entity_Id; |
| Func_Id : Entity_Id; |
| Ret_Typ : Entity_Id; |
| Alloc_Expr : Node_Id) return Node_Id |
| is |
| begin |
| pragma Assert (Is_Build_In_Place_Function (Func_Id)); |
| |
| -- Processing for build-in-place object allocation. This is disabled |
| -- on .NET/JVM because the targets do not support pools. |
| |
| if VM_Target = No_VM |
| and then Needs_Finalization (Ret_Typ) |
| then |
| declare |
| Decls : constant List_Id := New_List; |
| Fin_Mas_Id : constant Entity_Id := |
| Build_In_Place_Formal |
| (Func_Id, BIP_Finalization_Master); |
| Stmts : constant List_Id := New_List; |
| Desig_Typ : Entity_Id; |
| Local_Id : Entity_Id; |
| Pool_Id : Entity_Id; |
| Ptr_Typ : Entity_Id; |
| |
| begin |
| -- Generate: |
| -- Pool_Id renames Base_Pool (BIPfinalizationmaster.all).all; |
| |
| Pool_Id := Make_Temporary (Loc, 'P'); |
| |
| Append_To (Decls, |
| Make_Object_Renaming_Declaration (Loc, |
| Defining_Identifier => Pool_Id, |
| Subtype_Mark => |
| New_Occurrence_Of (RTE (RE_Root_Storage_Pool), Loc), |
| Name => |
| Make_Explicit_Dereference (Loc, |
| Prefix => |
| Make_Function_Call (Loc, |
| Name => |
| New_Occurrence_Of (RTE (RE_Base_Pool), Loc), |
| Parameter_Associations => New_List ( |
| Make_Explicit_Dereference (Loc, |
| Prefix => |
| New_Occurrence_Of (Fin_Mas_Id, Loc))))))); |
| |
| -- Create an access type which uses the storage pool of the |
| -- caller's master. This additional type is necessary because |
| -- the finalization master cannot be associated with the type |
| -- of the temporary. Otherwise the secondary stack allocation |
| -- will fail. |
| |
| Desig_Typ := Ret_Typ; |
| |
| -- Ensure that the build-in-place machinery uses a fat pointer |
| -- when allocating an unconstrained array on the heap. In this |
| -- case the result object type is a constrained array type even |
| -- though the function type is unconstrained. |
| |
| if Ekind (Desig_Typ) = E_Array_Subtype then |
| Desig_Typ := Base_Type (Desig_Typ); |
| end if; |
| |
| -- Generate: |
| -- type Ptr_Typ is access Desig_Typ; |
| |
| Ptr_Typ := Make_Temporary (Loc, 'P'); |
| |
| Append_To (Decls, |
| Make_Full_Type_Declaration (Loc, |
| Defining_Identifier => Ptr_Typ, |
| Type_Definition => |
| Make_Access_To_Object_Definition (Loc, |
| Subtype_Indication => |
| New_Occurrence_Of (Desig_Typ, Loc)))); |
| |
| -- Perform minor decoration in order to set the master and the |
| -- storage pool attributes. |
| |
| Set_Ekind (Ptr_Typ, E_Access_Type); |
| Set_Finalization_Master (Ptr_Typ, Fin_Mas_Id); |
| Set_Associated_Storage_Pool (Ptr_Typ, Pool_Id); |
| |
| -- Create the temporary, generate: |
| -- Local_Id : Ptr_Typ; |
| |
| Local_Id := Make_Temporary (Loc, 'T'); |
| |
| Append_To (Decls, |
| Make_Object_Declaration (Loc, |
| Defining_Identifier => Local_Id, |
| Object_Definition => |
| New_Occurrence_Of (Ptr_Typ, Loc))); |
| |
| -- Allocate the object, generate: |
| -- Local_Id := <Alloc_Expr>; |
| |
| Append_To (Stmts, |
| Make_Assignment_Statement (Loc, |
| Name => New_Occurrence_Of (Local_Id, Loc), |
| Expression => Alloc_Expr)); |
| |
| -- Generate: |
| -- Temp_Id := Temp_Typ (Local_Id); |
| |
| Append_To (Stmts, |
| Make_Assignment_Statement (Loc, |
| Name => New_Occurrence_Of (Temp_Id, Loc), |
| Expression => |
| Unchecked_Convert_To (Temp_Typ, |
| New_Occurrence_Of (Local_Id, Loc)))); |
| |
| -- Wrap the allocation in a block. This is further conditioned |
| -- by checking the caller finalization master at runtime. A |
| -- null value indicates a non-existent master, most likely due |
| -- to a Finalize_Storage_Only allocation. |
| |
| -- Generate: |
| -- if BIPfinalizationmaster /= null then |
| -- declare |
| -- <Decls> |
| -- begin |
| -- <Stmts> |
| -- end; |
| -- end if; |
| |
| return |
| Make_If_Statement (Loc, |
| Condition => |
| Make_Op_Ne (Loc, |
| Left_Opnd => New_Occurrence_Of (Fin_Mas_Id, Loc), |
| Right_Opnd => Make_Null (Loc)), |
| |
| Then_Statements => New_List ( |
| Make_Block_Statement (Loc, |
| Declarations => Decls, |
| Handled_Statement_Sequence => |
| Make_Handled_Sequence_Of_Statements (Loc, |
| Statements => Stmts)))); |
| end; |
| |
| -- For all other cases, generate: |
| -- Temp_Id := <Alloc_Expr>; |
| |
| else |
| return |
| Make_Assignment_Statement (Loc, |
| Name => New_Occurrence_Of (Temp_Id, Loc), |
| Expression => Alloc_Expr); |
| end if; |
| end Build_Heap_Allocator; |
| |
| --------------------------- |
| -- Move_Activation_Chain -- |
| --------------------------- |
| |
| function Move_Activation_Chain return Node_Id is |
| begin |
| return |
| Make_Procedure_Call_Statement (Loc, |
| Name => |
| New_Occurrence_Of (RTE (RE_Move_Activation_Chain), Loc), |
| |
| Parameter_Associations => New_List ( |
| |
| -- Source chain |
| |
| Make_Attribute_Reference (Loc, |
| Prefix => Make_Identifier (Loc, Name_uChain), |
| Attribute_Name => Name_Unrestricted_Access), |
| |
| -- Destination chain |
| |
| New_Occurrence_Of |
| (Build_In_Place_Formal (Par_Func, BIP_Activation_Chain), Loc), |
| |
| -- New master |
| |
| New_Occurrence_Of |
| (Build_In_Place_Formal (Par_Func, BIP_Task_Master), Loc))); |
| end Move_Activation_Chain; |
| |
| -- Start of processing for Expand_N_Extended_Return_Statement |
| |
| begin |
| -- Given that functionality of interface thunks is simple (just displace |
| -- the pointer to the object) they are always handled by means of |
| -- simple return statements. |
| |
| pragma Assert (not Is_Thunk (Current_Scope)); |
| |
| if Nkind (Ret_Obj_Decl) = N_Object_Declaration then |
| Exp := Expression (Ret_Obj_Decl); |
| else |
| Exp := Empty; |
| end if; |
| |
| HSS := Handled_Statement_Sequence (N); |
| |
| -- If the returned object needs finalization actions, the function must |
| -- perform the appropriate cleanup should it fail to return. The state |
| -- of the function itself is tracked through a flag which is coupled |
| -- with the scope finalizer. There is one flag per each return object |
| -- in case of multiple returns. |
| |
| if Is_Build_In_Place |
| and then Needs_Finalization (Etype (Ret_Obj_Id)) |
| then |
| declare |
| Flag_Decl : Node_Id; |
| |