[Ada] Double finalization of limited controlled result

Message ID 20180611092402.GA135131@adacore.com
State New
Headers show
Series
  • [Ada] Double finalization of limited controlled result
Related show

Commit Message

Pierre-Marie de Rodat June 11, 2018, 9:24 a.m.
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

Tested on x86_64-pc-linux-gnu, committed on trunk

2018-06-11  Hristian Kirtchev  <kirtchev@adacore.com>

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.

Patch

--- gcc/ada/exp_ch6.adb
+++ 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 := <Alloc_Expr>;
+      --       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 := <Alloc_Expr>;
-      --          Temp_Id := Temp_Typ (Local);
-      --       end;
-      --    end if;
+      --             Local := <Alloc_Expr>;
+      --             Temp_Id := Temp_Typ (Local);
+      --          end;
+      --       end if;
+      --
+      --    * Non-controlled case
+      --
+      --       Temp_Id := <Alloc_Expr>;
       --
       --  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 := <Orig_Expr>;
+               --    else
                --       declare
                --          <Decls>
                --       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;
 
    --------------------------------------

--- gcc/ada/exp_util.adb
+++ 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;