[Ada] Double finalization of limited controlled result

Programming / Compilers / GCC - pmderodat [138bc75d-0d04-0410-961f-82ee72b054a4] - 11 June 2018 09:19 EDT

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.

36c80e26b07 [Ada] Double finalization of limited controlled result
gcc/ada/ChangeLog | 22 ++
gcc/ada/exp_ch6.adb | 578 ++++++++++++++++++++++++++++-----------------------
gcc/ada/exp_util.adb | 8 +-
3 files changed, 337 insertions(+), 271 deletions(-)

Upstream: gcc.gnu.org


  • Share