From 7d1d3a546409ce9e4aedb3b8d537cc770beabd62 Mon Sep 17 00:00:00 2001 From: Hristian Kirtchev Date: Mon, 11 Jun 2018 09:19:30 +0000 Subject: [PATCH] [Ada] Double finalization of limited controlled result This patch disables a build-in-place optimization when a function returns a limited controlled result because the optimization may violate the semantics of finalizable types by performing illegal calls to Finalize. In general, the optimization causes the result object of a build-in-place function to be allocated at the caller site, with a pointer to the object passed to the function. The function then simply initializes the caller- allocated object. This mode of operation however violates semantics of finalizable types when the context of the call is allocation. The act of allocating the controlled object at the caller site will place it on the associated access type's finalization master. If the function fails the initialization of the object, the malformed object will still be finalized when the finalization master goes out of scope. This is dangerous, and must not happen. ------------ -- Source -- ------------ -- pack.ads with Ada.Finalization; use Ada.Finalization; package Pack is type Lim_Ctrl is new Limited_Controlled with null record; procedure Finalize (Obj : in out Lim_Ctrl); type Lim_Ctrl_Ptr is access all Lim_Ctrl; function Make_Lim_Ctrl_Bad_Init return Lim_Ctrl; function Make_Lim_Ctrl_OK_Init return Lim_Ctrl; end Pack; -- pack.adb with Ada.Text_IO; use Ada.Text_IO; package body Pack is procedure Finalize (Obj : in out Lim_Ctrl) is begin Put_Line (" Finalize"); end Finalize; function Make_Lim_Ctrl_Bad_Init return Lim_Ctrl is begin return Result : Lim_Ctrl := raise Program_Error do null; end return; end Make_Lim_Ctrl_Bad_Init; function Make_Lim_Ctrl_OK_Init return Lim_Ctrl is begin return Result : Lim_Ctrl do raise Program_Error; end return; end Make_Lim_Ctrl_OK_Init; end Pack; -- main.adb with Ada.Text_IO; use Ada.Text_IO; with Pack; use Pack; procedure Main is begin begin Put_Line ("1) Heap-allocated bad init"); declare Obj : Lim_Ctrl_Ptr := new Lim_Ctrl'(Make_Lim_Ctrl_Bad_Init); begin Put_Line ("1) ERROR: Heap-allocated bad init: exception not raised"); end; exception when Program_Error => Put_Line ("1) Heap-allocated bad init: Program_Error raised"); when others => Put_Line ("1) ERROR: Heap-allocatd bad init: unexpected exception"); end; begin Put_Line ("2) Stack-allocated bad init"); declare Obj : Lim_Ctrl := Make_Lim_Ctrl_Bad_Init; begin Put_Line ("2) ERROR: Stack-allocated bad init: exception not raised"); end; exception when Program_Error => Put_Line ("2) Stack-allocated bad init: Program_Error raised"); when others => Put_Line ("2) ERROR: Stack-allocated bad init: unexpected exception"); end; begin Put_Line ("3) Heap-allocated OK init"); declare Obj : Lim_Ctrl_Ptr := new Lim_Ctrl'(Make_Lim_Ctrl_OK_Init); begin Put_Line ("3) ERROR: Heap-allocated OK init: exception not raised"); end; exception when Program_Error => Put_Line ("3) Heap-allocated OK init: Program_Error raised"); when others => Put_Line ("3) ERROR: Heap-allocatd OK init: unexpected exception"); end; begin Put_Line ("4) Stack-allocated OK init"); declare Obj : Lim_Ctrl := Make_Lim_Ctrl_OK_Init; begin Put_Line ("4) ERROR: Stack-allocated OK init: exception not raised"); end; exception when Program_Error => Put_Line ("4) Stack-allocated OK init: Program_Error raised"); when others => Put_Line ("4) ERROR: Stack-allocated OK init: unexpected exception"); end; end Main; ---------------------------- -- Compilation and output -- ---------------------------- $ gnatmake -q main.adb $ ./main 1) Heap-allocated bad init 1) Heap-allocated bad init: Program_Error raised 2) Stack-allocated bad init 2) Stack-allocated bad init: Program_Error raised 3) Heap-allocated OK init Finalize 3) Heap-allocated OK init: Program_Error raised 4) Stack-allocated OK init Finalize 4) Stack-allocated OK init: Program_Error raised 2018-06-11 Hristian Kirtchev gcc/ada/ * exp_ch6.adb (Add_Unconstrained_Actuals_To_Build_In_Place_Call): Do not add any actuals when the size of the object is known, and the caller will allocate it. (Build_Heap_Allocator): Rename to Build_Heap_Or_Pool_Allocator to better illustrate its functionality. Update the comment on the generated code. Generate a branch for the heap and pool cases where the object is not necessarity controlled. (Expand_N_Extended_Return_Statement): Expand the extended return statement into four branches depending the requested mode if the caller will not allocate the object on its side. (Make_Build_In_Place_Call_In_Allocator): Do not allocate a controlled object on the caller side because this will violate the semantics of finalizable types. Instead notify the function to allocate the object on the heap or a user-defined storage pool. (Needs_BIP_Alloc_Form): A build-in-place function needs to be notified which of the four modes to employ when returning a limited controlled result. * exp_util.adb (Build_Allocate_Deallocate_Proc): Remove a redundant guard which is already covered in Needs_Finalization. From-SVN: r261427 --- gcc/ada/ChangeLog | 22 ++ gcc/ada/exp_ch6.adb | 578 +++++++++++++++++++++++-------------------- gcc/ada/exp_util.adb | 8 +- 3 files changed, 337 insertions(+), 271 deletions(-) diff --git a/gcc/ada/ChangeLog b/gcc/ada/ChangeLog index b713c4964f1..51749409898 100644 --- a/gcc/ada/ChangeLog +++ b/gcc/ada/ChangeLog @@ -1,3 +1,25 @@ +2018-06-11 Hristian Kirtchev + + * exp_ch6.adb (Add_Unconstrained_Actuals_To_Build_In_Place_Call): Do + not add any actuals when the size of the object is known, and the + caller will allocate it. + (Build_Heap_Allocator): Rename to Build_Heap_Or_Pool_Allocator to + better illustrate its functionality. Update the comment on the + generated code. Generate a branch for the heap and pool cases where + the object is not necessarity controlled. + (Expand_N_Extended_Return_Statement): Expand the extended return + statement into four branches depending the requested mode if the caller + will not allocate the object on its side. + (Make_Build_In_Place_Call_In_Allocator): Do not allocate a controlled + object on the caller side because this will violate the semantics of + finalizable types. Instead notify the function to allocate the object + on the heap or a user-defined storage pool. + (Needs_BIP_Alloc_Form): A build-in-place function needs to be notified + which of the four modes to employ when returning a limited controlled + result. + * exp_util.adb (Build_Allocate_Deallocate_Proc): Remove a redundant + guard which is already covered in Needs_Finalization. + 2018-06-11 Olivier Hainque * libgnat/s-excmac*.ads: Factorize Unwind_Action definitions ... diff --git a/gcc/ada/exp_ch6.adb b/gcc/ada/exp_ch6.adb index 2895ed973b2..9ddf0fa0381 100644 --- a/gcc/ada/exp_ch6.adb +++ b/gcc/ada/exp_ch6.adb @@ -336,22 +336,18 @@ package body Exp_Ch6 is Alloc_Form_Exp : Node_Id := Empty; Pool_Actual : Node_Id := Make_Null (No_Location)) is - Loc : constant Source_Ptr := Sloc (Function_Call); + 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 + -- Nothing to do when the size of the object is known, and the caller is + -- in charge of allocating it, and the callee doesn't unconditionally + -- require an allocation form (such as due to having a tagged result). + + if not Needs_BIP_Alloc_Form (Function_Id) then return; end if; @@ -382,8 +378,8 @@ package body Exp_Ch6 is Add_Extra_Actual_To_Call (Function_Call, Alloc_Form_Formal, Alloc_Form_Actual); - -- Pass the Storage_Pool parameter. This parameter is omitted on - -- ZFP as those targets do not support pools. + -- Pass the Storage_Pool parameter. This parameter is omitted on ZFP as + -- those targets do not support pools. if RTE_Available (RE_Root_Storage_Pool_Ptr) then Pool_Formal := Build_In_Place_Formal (Function_Id, BIP_Storage_Pool); @@ -4488,38 +4484,46 @@ package body Exp_Ch6 is -- 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). + -- (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); - function Build_Heap_Allocator + function Build_Heap_Or_Pool_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. + -- heap or user-defined storage pool. The object may need finalization + -- actions depending on the return type. -- - -- 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; + -- * Controlled case + -- + -- if BIPfinalizationmaster = null then + -- Temp_Id := ; + -- else + -- 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; + -- procedure Allocate (...) is + -- begin + -- System.Storage_Pools.Subpools.Allocate_Any (...); + -- end Allocate; -- - -- Local := ; - -- Temp_Id := Temp_Typ (Local); - -- end; - -- end if; + -- Local := ; + -- Temp_Id := Temp_Typ (Local); + -- end; + -- end if; + -- + -- * Non-controlled case + -- + -- Temp_Id := ; -- -- 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 @@ -4536,11 +4540,11 @@ package body Exp_Ch6 is -- Func_Id is the entity of the function where the extended return -- statement appears. - -------------------------- - -- Build_Heap_Allocator -- - -------------------------- + ---------------------------------- + -- Build_Heap_Or_Pool_Allocator -- + ---------------------------------- - function Build_Heap_Allocator + function Build_Heap_Or_Pool_Allocator (Temp_Id : Entity_Id; Temp_Typ : Entity_Id; Func_Id : Entity_Id; @@ -4550,7 +4554,7 @@ package body Exp_Ch6 is begin pragma Assert (Is_Build_In_Place_Function (Func_Id)); - -- Processing for build-in-place object allocation. + -- Processing for objects that require finalization actions if Needs_Finalization (Ret_Typ) then declare @@ -4558,6 +4562,7 @@ package body Exp_Ch6 is Fin_Mas_Id : constant Entity_Id := Build_In_Place_Formal (Func_Id, BIP_Finalization_Master); + Orig_Expr : constant Node_Id := New_Copy_Tree (Alloc_Expr); Stmts : constant List_Id := New_List; Desig_Typ : Entity_Id; Local_Id : Entity_Id; @@ -4619,7 +4624,7 @@ package body Exp_Ch6 is -- Perform minor decoration in order to set the master and the -- storage pool attributes. - Set_Ekind (Ptr_Typ, E_Access_Type); + Set_Ekind (Ptr_Typ, E_Access_Type); Set_Finalization_Master (Ptr_Typ, Fin_Mas_Id); Set_Associated_Storage_Pool (Ptr_Typ, Pool_Id); @@ -4658,7 +4663,9 @@ package body Exp_Ch6 is -- to a Finalize_Storage_Only allocation. -- Generate: - -- if BIPfinalizationmaster /= null then + -- if BIPfinalizationmaster = null then + -- Temp_Id := ; + -- else -- declare -- -- begin @@ -4669,11 +4676,16 @@ package body Exp_Ch6 is return Make_If_Statement (Loc, Condition => - Make_Op_Ne (Loc, + Make_Op_Eq (Loc, Left_Opnd => New_Occurrence_Of (Fin_Mas_Id, Loc), Right_Opnd => Make_Null (Loc)), Then_Statements => New_List ( + Make_Assignment_Statement (Loc, + Name => New_Occurrence_Of (Temp_Id, Loc), + Expression => Orig_Expr)), + + Else_Statements => New_List ( Make_Block_Statement (Loc, Declarations => Decls, Handled_Statement_Sequence => @@ -4690,7 +4702,7 @@ package body Exp_Ch6 is Name => New_Occurrence_Of (Temp_Id, Loc), Expression => Alloc_Expr); end if; - end Build_Heap_Allocator; + end Build_Heap_Or_Pool_Allocator; --------------------------- -- Move_Activation_Chain -- @@ -5037,11 +5049,9 @@ package body Exp_Ch6 is -- determine the form of allocation needed, initialization -- is done with each part of the if statement that handles -- the different forms of allocation (this is true for - -- unconstrained and tagged result subtypes). + -- unconstrained, tagged, and controlled result subtypes). - if Is_Constrained (Ret_Typ) - and then not Is_Tagged_Type (Underlying_Type (Ret_Typ)) - then + if not Needs_BIP_Alloc_Form (Func_Id) then Insert_After (Ret_Obj_Decl, Init_Assignment); end if; end if; @@ -5057,16 +5067,14 @@ package body Exp_Ch6 is -- a storage pool. We generate an if statement to test the -- implicit allocation formal and initialize a local access -- value appropriately, creating allocators in the secondary - -- stack and global heap cases. The special formal also exists + -- stack and global heap cases. The special formal also exists -- and must be tested when the function has a tagged result, -- even when the result subtype is constrained, because in -- general such functions can be called in dispatching contexts -- and must be handled similarly to functions with a class-wide -- result. - if not Is_Constrained (Ret_Typ) - or else Is_Tagged_Type (Underlying_Type (Ret_Typ)) - then + if Needs_BIP_Alloc_Form (Func_Id) then Obj_Alloc_Formal := Build_In_Place_Formal (Func_Id, BIP_Alloc_Form); @@ -5331,7 +5339,7 @@ package body Exp_Ch6 is (Global_Heap)))), Then_Statements => New_List ( - Build_Heap_Allocator + Build_Heap_Or_Pool_Allocator (Temp_Id => Alloc_Obj_Id, Temp_Typ => Ref_Type, Func_Id => Func_Id, @@ -5355,7 +5363,7 @@ package body Exp_Ch6 is Then_Statements => New_List ( Pool_Decl, - Build_Heap_Allocator + Build_Heap_Or_Pool_Allocator (Temp_Id => Alloc_Obj_Id, Temp_Typ => Ref_Type, Func_Id => Func_Id, @@ -7256,204 +7264,6 @@ package body Exp_Ch6 is end if; end Expand_Simple_Function_Return; - -------------------------------------------- - -- Has_Unconstrained_Access_Discriminants -- - -------------------------------------------- - - function Has_Unconstrained_Access_Discriminants - (Subtyp : Entity_Id) return Boolean - is - Discr : Entity_Id; - - begin - if Has_Discriminants (Subtyp) - and then not Is_Constrained (Subtyp) - then - Discr := First_Discriminant (Subtyp); - while Present (Discr) loop - if Ekind (Etype (Discr)) = E_Anonymous_Access_Type then - return True; - end if; - - Next_Discriminant (Discr); - end loop; - end if; - - return False; - end Has_Unconstrained_Access_Discriminants; - - ----------------------------------- - -- Is_Build_In_Place_Result_Type -- - ----------------------------------- - - function Is_Build_In_Place_Result_Type (Typ : Entity_Id) return Boolean is - begin - if not Expander_Active then - return False; - end if; - - -- In Ada 2005 all functions with an inherently limited return type - -- must be handled using a build-in-place profile, including the case - -- of a function with a limited interface result, where the function - -- may return objects of nonlimited descendants. - - if Is_Limited_View (Typ) then - return Ada_Version >= Ada_2005 and then not Debug_Flag_Dot_L; - - else - if Debug_Flag_Dot_9 then - return False; - end if; - - if Has_Interfaces (Typ) then - return False; - end if; - - declare - T : Entity_Id := Typ; - begin - -- For T'Class, return True if it's True for T. This is necessary - -- because a class-wide function might say "return F (...)", where - -- F returns the corresponding specific type. We need a loop in - -- case T is a subtype of a class-wide type. - - while Is_Class_Wide_Type (T) loop - T := Etype (T); - end loop; - - -- If this is a generic formal type in an instance, return True if - -- it's True for the generic actual type. - - if Nkind (Parent (T)) = N_Subtype_Declaration - and then Present (Generic_Parent_Type (Parent (T))) - then - T := Entity (Subtype_Indication (Parent (T))); - - if Present (Full_View (T)) then - T := Full_View (T); - end if; - end if; - - if Present (Underlying_Type (T)) then - T := Underlying_Type (T); - end if; - - declare - Result : Boolean; - -- So we can stop here in the debugger - begin - -- ???For now, enable build-in-place for a very narrow set of - -- controlled types. Change "if True" to "if False" to - -- experiment with more controlled types. Eventually, we might - -- like to enable build-in-place for all tagged types, all - -- types that need finalization, and all caller-unknown-size - -- types. - - if True then - Result := Is_Controlled (T) - and then Present (Enclosing_Subprogram (T)) - and then not Is_Compilation_Unit (Enclosing_Subprogram (T)) - and then Ekind (Enclosing_Subprogram (T)) = E_Procedure; - else - Result := Is_Controlled (T); - end if; - - return Result; - end; - end; - end if; - end Is_Build_In_Place_Result_Type; - - -------------------------------- - -- Is_Build_In_Place_Function -- - -------------------------------- - - function Is_Build_In_Place_Function (E : Entity_Id) return Boolean is - begin - -- This function is called from Expand_Subtype_From_Expr during - -- semantic analysis, even when expansion is off. In those cases - -- the build_in_place expansion will not take place. - - if not Expander_Active then - return False; - end if; - - -- For now we test whether E denotes a function or access-to-function - -- type whose result subtype is inherently limited. Later this test - -- may be revised to allow composite nonlimited types. Functions with - -- a foreign convention or whose result type has a foreign convention - -- never qualify. - - if Ekind_In (E, E_Function, E_Generic_Function) - or else (Ekind (E) = E_Subprogram_Type - and then Etype (E) /= Standard_Void_Type) - then - -- Note: If the function has a foreign convention, it cannot build - -- its result in place, so you're on your own. On the other hand, - -- if only the return type has a foreign convention, its layout is - -- intended to be compatible with the other language, but the build- - -- in place machinery can ensure that the object is not copied. - - return Is_Build_In_Place_Result_Type (Etype (E)) - and then not Has_Foreign_Convention (E) - and then not Debug_Flag_Dot_L; - - else - return False; - end if; - end Is_Build_In_Place_Function; - - ------------------------------------- - -- Is_Build_In_Place_Function_Call -- - ------------------------------------- - - function Is_Build_In_Place_Function_Call (N : Node_Id) return Boolean is - Exp_Node : constant Node_Id := Unqual_Conv (N); - Function_Id : Entity_Id; - - begin - -- Return False if the expander is currently inactive, since awareness - -- of build-in-place treatment is only relevant during expansion. Note - -- that Is_Build_In_Place_Function, which is called as part of this - -- function, is also conditioned this way, but we need to check here as - -- well to avoid blowing up on processing protected calls when expansion - -- is disabled (such as with -gnatc) since those would trip over the - -- raise of Program_Error below. - - -- In SPARK mode, build-in-place calls are not expanded, so that we - -- may end up with a call that is neither resolved to an entity, nor - -- an indirect call. - - if not Expander_Active or else Nkind (Exp_Node) /= N_Function_Call then - return False; - end if; - - if Is_Entity_Name (Name (Exp_Node)) then - Function_Id := Entity (Name (Exp_Node)); - - -- In the case of an explicitly dereferenced call, use the subprogram - -- type generated for the dereference. - - elsif Nkind (Name (Exp_Node)) = N_Explicit_Dereference then - Function_Id := Etype (Name (Exp_Node)); - - -- This may be a call to a protected function. - - elsif Nkind (Name (Exp_Node)) = N_Selected_Component then - Function_Id := Etype (Entity (Selector_Name (Name (Exp_Node)))); - - else - raise Program_Error; - end if; - - declare - Result : constant Boolean := Is_Build_In_Place_Function (Function_Id); - -- So we can stop here in the debugger - begin - return Result; - end; - end Is_Build_In_Place_Function_Call; - ----------------------- -- Freeze_Subprogram -- ----------------------- @@ -7646,6 +7456,32 @@ package body Exp_Ch6 is end if; end Freeze_Subprogram; + -------------------------------------------- + -- Has_Unconstrained_Access_Discriminants -- + -------------------------------------------- + + function Has_Unconstrained_Access_Discriminants + (Subtyp : Entity_Id) return Boolean + is + Discr : Entity_Id; + + begin + if Has_Discriminants (Subtyp) + and then not Is_Constrained (Subtyp) + then + Discr := First_Discriminant (Subtyp); + while Present (Discr) loop + if Ekind (Etype (Discr)) = E_Anonymous_Access_Type then + return True; + end if; + + Next_Discriminant (Discr); + end loop; + end if; + + return False; + end Has_Unconstrained_Access_Discriminants; + ------------------------------ -- Insert_Post_Call_Actions -- ------------------------------ @@ -7768,6 +7604,177 @@ package body Exp_Ch6 is end if; end Insert_Post_Call_Actions; + ----------------------------------- + -- Is_Build_In_Place_Result_Type -- + ----------------------------------- + + function Is_Build_In_Place_Result_Type (Typ : Entity_Id) return Boolean is + begin + if not Expander_Active then + return False; + end if; + + -- In Ada 2005 all functions with an inherently limited return type + -- must be handled using a build-in-place profile, including the case + -- of a function with a limited interface result, where the function + -- may return objects of nonlimited descendants. + + if Is_Limited_View (Typ) then + return Ada_Version >= Ada_2005 and then not Debug_Flag_Dot_L; + + else + if Debug_Flag_Dot_9 then + return False; + end if; + + if Has_Interfaces (Typ) then + return False; + end if; + + declare + T : Entity_Id := Typ; + begin + -- For T'Class, return True if it's True for T. This is necessary + -- because a class-wide function might say "return F (...)", where + -- F returns the corresponding specific type. We need a loop in + -- case T is a subtype of a class-wide type. + + while Is_Class_Wide_Type (T) loop + T := Etype (T); + end loop; + + -- If this is a generic formal type in an instance, return True if + -- it's True for the generic actual type. + + if Nkind (Parent (T)) = N_Subtype_Declaration + and then Present (Generic_Parent_Type (Parent (T))) + then + T := Entity (Subtype_Indication (Parent (T))); + + if Present (Full_View (T)) then + T := Full_View (T); + end if; + end if; + + if Present (Underlying_Type (T)) then + T := Underlying_Type (T); + end if; + + declare + Result : Boolean; + -- So we can stop here in the debugger + begin + -- ???For now, enable build-in-place for a very narrow set of + -- controlled types. Change "if True" to "if False" to + -- experiment with more controlled types. Eventually, we might + -- like to enable build-in-place for all tagged types, all + -- types that need finalization, and all caller-unknown-size + -- types. + + if True then + Result := Is_Controlled (T) + and then Present (Enclosing_Subprogram (T)) + and then not Is_Compilation_Unit (Enclosing_Subprogram (T)) + and then Ekind (Enclosing_Subprogram (T)) = E_Procedure; + else + Result := Is_Controlled (T); + end if; + + return Result; + end; + end; + end if; + end Is_Build_In_Place_Result_Type; + + -------------------------------- + -- Is_Build_In_Place_Function -- + -------------------------------- + + function Is_Build_In_Place_Function (E : Entity_Id) return Boolean is + begin + -- This function is called from Expand_Subtype_From_Expr during + -- semantic analysis, even when expansion is off. In those cases + -- the build_in_place expansion will not take place. + + if not Expander_Active then + return False; + end if; + + -- For now we test whether E denotes a function or access-to-function + -- type whose result subtype is inherently limited. Later this test + -- may be revised to allow composite nonlimited types. Functions with + -- a foreign convention or whose result type has a foreign convention + -- never qualify. + + if Ekind_In (E, E_Function, E_Generic_Function) + or else (Ekind (E) = E_Subprogram_Type + and then Etype (E) /= Standard_Void_Type) + then + -- Note: If the function has a foreign convention, it cannot build + -- its result in place, so you're on your own. On the other hand, + -- if only the return type has a foreign convention, its layout is + -- intended to be compatible with the other language, but the build- + -- in place machinery can ensure that the object is not copied. + + return Is_Build_In_Place_Result_Type (Etype (E)) + and then not Has_Foreign_Convention (E) + and then not Debug_Flag_Dot_L; + else + return False; + end if; + end Is_Build_In_Place_Function; + + ------------------------------------- + -- Is_Build_In_Place_Function_Call -- + ------------------------------------- + + function Is_Build_In_Place_Function_Call (N : Node_Id) return Boolean is + Exp_Node : constant Node_Id := Unqual_Conv (N); + Function_Id : Entity_Id; + + begin + -- Return False if the expander is currently inactive, since awareness + -- of build-in-place treatment is only relevant during expansion. Note + -- that Is_Build_In_Place_Function, which is called as part of this + -- function, is also conditioned this way, but we need to check here as + -- well to avoid blowing up on processing protected calls when expansion + -- is disabled (such as with -gnatc) since those would trip over the + -- raise of Program_Error below. + + -- In SPARK mode, build-in-place calls are not expanded, so that we + -- may end up with a call that is neither resolved to an entity, nor + -- an indirect call. + + if not Expander_Active or else Nkind (Exp_Node) /= N_Function_Call then + return False; + end if; + + if Is_Entity_Name (Name (Exp_Node)) then + Function_Id := Entity (Name (Exp_Node)); + + -- In the case of an explicitly dereferenced call, use the subprogram + -- type generated for the dereference. + + elsif Nkind (Name (Exp_Node)) = N_Explicit_Dereference then + Function_Id := Etype (Name (Exp_Node)); + + -- This may be a call to a protected function. + + elsif Nkind (Name (Exp_Node)) = N_Selected_Component then + Function_Id := Etype (Entity (Selector_Name (Name (Exp_Node)))); + + else + raise Program_Error; + end if; + + declare + Result : constant Boolean := Is_Build_In_Place_Function (Function_Id); + -- So we can stop here in the debugger + begin + return Result; + end; + end Is_Build_In_Place_Function_Call; + ----------------------- -- Is_Null_Procedure -- ----------------------- @@ -7853,10 +7860,9 @@ package body Exp_Ch6 is -- Step past qualification or unchecked conversion (the latter can occur -- in cases of calls to 'Input). - if Nkind_In (Func_Call, - N_Qualified_Expression, - N_Type_Conversion, - N_Unchecked_Type_Conversion) + if Nkind_In (Func_Call, N_Qualified_Expression, + N_Type_Conversion, + N_Unchecked_Type_Conversion) then Func_Call := Expression (Func_Call); end if; @@ -7889,16 +7895,37 @@ package body Exp_Ch6 is Set_Can_Never_Be_Null (Acc_Type, False); -- It gets initialized to null, so we can't have that - -- When the result subtype is constrained, the return object is - -- allocated on the caller side, and access to it is passed to the - -- function. + -- When the result subtype is constrained, the return object is created + -- on the caller side, and access to it is passed to the function. This + -- optimization is disabled when the result subtype needs finalization + -- actions because the caller side allocation may result in undesirable + -- finalization. Consider the following example: + -- + -- function Make_Lim_Ctrl return Lim_Ctrl is + -- begin + -- return Result : Lim_Ctrl := raise Program_Error do + -- null; + -- end return; + -- end Make_Lim_Ctrl; + -- + -- Obj : Lim_Ctrl_Ptr := new Lim_Ctrl'(Make_Lim_Ctrl); + -- + -- Even though the size of limited controlled type Lim_Ctrl is known, + -- allocating Obj at the caller side will chain Obj on Lim_Ctrl_Ptr's + -- finalization master. The subsequent call to Make_Lim_Ctrl will fail + -- during the initialization actions for Result, which implies that + -- Result (and Obj by extension) should not be finalized. However Obj + -- will be finalized when access type Lim_Ctrl_Ptr goes out of scope + -- since it is already attached on the related finalization master. -- Here and in related routines, we must examine the full view of the -- type, because the view at the point of call may differ from that -- that in the function body, and the expansion mechanism depends on -- the characteristics of the full view. - if Is_Constrained (Underlying_Type (Result_Subt)) then + if Is_Constrained (Underlying_Type (Result_Subt)) + and then not Needs_Finalization (Underlying_Type (Result_Subt)) + then -- Replace the initialized allocator of form "new T'(Func (...))" -- with an uninitialized allocator of form "new T", where T is the -- result subtype of the called function. The call to the function @@ -7926,8 +7953,8 @@ package body Exp_Ch6 is Temp_Init := Relocate_Node (Allocator); - if Nkind_In - (Function_Call, N_Type_Conversion, N_Unchecked_Type_Conversion) + if Nkind_In (Function_Call, N_Type_Conversion, + N_Unchecked_Type_Conversion) then Temp_Init := Unchecked_Convert_To (Acc_Type, Temp_Init); end if; @@ -8001,17 +8028,17 @@ package body Exp_Ch6 is -- that the full types will be compatible, but the types not visibly -- compatible. - elsif Nkind_In - (Function_Call, N_Type_Conversion, N_Unchecked_Type_Conversion) + elsif Nkind_In (Function_Call, N_Type_Conversion, + N_Unchecked_Type_Conversion) then Ref_Func_Call := Unchecked_Convert_To (Acc_Type, Ref_Func_Call); end if; declare Assign : constant Node_Id := - Make_Assignment_Statement (Loc, - Name => New_Occurrence_Of (Return_Obj_Access, Loc), - Expression => Ref_Func_Call); + Make_Assignment_Statement (Loc, + Name => New_Occurrence_Of (Return_Obj_Access, Loc), + Expression => Ref_Func_Call); -- Assign the result of the function call into the temp. In the -- caller-allocates case, this is overwriting the temp with its -- initial value, which has no effect. In the callee-allocates case, @@ -8025,6 +8052,7 @@ package body Exp_Ch6 is -- to wrap the assignment in a block that activates them. The -- activation chain of that block must be passed to the function, -- rather than some outer chain. + begin if Has_Task (Result_Subt) then Actions := New_List; @@ -9062,8 +9090,30 @@ package body Exp_Ch6 is function Needs_BIP_Alloc_Form (Func_Id : Entity_Id) return Boolean is pragma Assert (Is_Build_In_Place_Function (Func_Id)); Func_Typ : constant Entity_Id := Underlying_Type (Etype (Func_Id)); + begin - return not Is_Constrained (Func_Typ) or else Is_Tagged_Type (Func_Typ); + -- A build-in-place function needs to know which allocation form to + -- use when: + -- + -- 1) The result subtype is unconstrained. In this case, depending on + -- the context of the call, the object may need to be created in the + -- secondary stack, the heap, or a user-defined storage pool. + -- + -- 2) The result subtype is tagged. In this case the function call may + -- dispatch on result and thus needs to be treated in the same way as + -- calls to functions with class-wide results, because a callee that + -- can be dispatched to may have any of various result subtypes, so + -- if any of the possible callees would require an allocation form to + -- be passed then they all do. + -- + -- 3) The result subtype needs finalization actions. In this case, based + -- on the context of the call, the object may need to be created at + -- the caller site, in the heap, or in a user-defined storage pool. + + return + not Is_Constrained (Func_Typ) + or else Is_Tagged_Type (Func_Typ) + or else Needs_Finalization (Func_Typ); end Needs_BIP_Alloc_Form; -------------------------------------- diff --git a/gcc/ada/exp_util.adb b/gcc/ada/exp_util.adb index 7573121c154..7b49a7a29ba 100644 --- a/gcc/ada/exp_util.adb +++ b/gcc/ada/exp_util.adb @@ -682,16 +682,10 @@ package body Exp_Util is if Needs_Fin then - -- Certain run-time configurations and targets do not provide support - -- for controlled types. - - if Restriction_Active (No_Finalization) then - return; - -- Do nothing if the access type may never allocate / deallocate -- objects. - elsif No_Pool_Assigned (Ptr_Typ) then + if No_Pool_Assigned (Ptr_Typ) then return; end if; -- 2.30.2