Bug 52320

Summary: missing destructor call after thrown exception in initializer
Product: gcc Reporter: Michael Mehlich <mmehlich>
Component: c++Assignee: Jason Merrill <jason>
Status: RESOLVED FIXED    
Severity: normal CC: daniel.kruegler, egallager, greenc, jakub, jason, redi, webrown.cpp
Priority: P3 Keywords: wrong-code
Version: 4.5.3   
Target Milestone: 12.0   
See Also: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=41449
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57510
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93922
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94041
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66451
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53868
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103984
Host: Target:
Build: Known to work:
Known to fail: Last reconfirmed: 2017-08-18 00:00:00

Description Michael Mehlich 2012-02-21 00:56:52 UTC
The following example has constructors of class A called 4 times but destructors only 3 times. 

Apparently, if an exception is thrown during the construction of an aggregate class, the destructors of already constructed data members are not called.
 
#include <iostream>

#define FUNCTION_NAME __PRETTY_FUNCTION__

#define TRACE_FUNCTION { std::cout << this << "->" << FUNCTION_NAME << std::endl; }

struct A {
	A() { TRACE_FUNCTION; }
	A(int) { TRACE_FUNCTION; }
	A(const A&) { TRACE_FUNCTION; }
	A &operator=(const A&) TRACE_FUNCTION;
	~A() TRACE_FUNCTION;
};

int main() {
	try {
		struct X {
			A e1[2], e2;
		} 
		x2[3] = { 1, 2, 3, 4, (0, throw 9, 5), 6 };
	} catch (...) {
	}
}

Detailed version information:
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/i686-pc-cygwin/4.5.3/lto-wrapper.exe
Target: i686-pc-cygwin
Configured with: /gnu/gcc/releases/respins/4.5.3-3/gcc4-4.5.3-3/src/gcc-4.5.3/configure --srcdir=/gnu/gcc/releases/respins/4.5.3-3/gcc4-4.5.3-3/src/gcc-4.5.3 --prefix=/usr --exec-prefix=/usr --bindir=/usr/bin --sbindir=/usr/sbin --libexecdir=/usr/lib --datadir=/usr/share --localstatedir=/var --sysconfdir=/etc --datarootdir=/usr/share --docdir=/usr/share/doc/gcc4 -C --datadir=/usr/share --infodir=/usr/share/info --mandir=/usr/share/man -v --with-gmp=/usr --with-mpfr=/usr --enable-bootstrap --enable-version-specific-runtime-libs --libexecdir=/usr/lib --enable-static --enable-shared --enable-shared-libgcc --disable-__cxa_atexit --with-gnu-ld --with-gnu-as --with-dwarf2 --disable-sjlj-exceptions --enable-languages=ada,c,c++,fortran,java,lto,objc,obj-c++ --enable-graphite --enable-lto --enable-java-awt=gtk --disable-symvers --enable-libjava --program-suffix=-4 --enable-libgomp --enable-libssp --enable-libada --enable-threads=posix --with-arch=i686 --with-tune=generic --enable-libgcj-sublibs CC=gcc-4 CXX=g++-4 CC_FOR_TARGET=gcc-4 CXX_FOR_TARGET=g++-4 GNATMAKE_FOR_TARGET=gnatmake GNATBIND_FOR_TARGET=gnatbind --with-ecj-jar=/usr/share/java/ecj.jar
Thread model: posix
gcc version 4.5.3 (GCC)
Comment 1 Daniel Krügler 2012-02-21 08:26:30 UTC
Confirmed for 4.7.0 20120218 (experimental).

Reduced example with added information:

//----
#include <iostream>

#define FUNCTION_NAME __PRETTY_FUNCTION__

#define TRACE_FUNCTION(I) { std::cout << this << "->" << FUNCTION_NAME << "; i = " << I << std::endl; }

struct A {
  int i;
  A(int i) : i(i) { TRACE_FUNCTION(i); }
  A(const A& rhs) : i(rhs.i) { TRACE_FUNCTION(i); }
  A &operator=(const A& rhs) { i = rhs.i; TRACE_FUNCTION(i); return *this; }
  ~A() { TRACE_FUNCTION(i); }
};

struct Y {
  A e1[2];
};

int main() {
  try {
    Y y1[1] = { {{1, (throw 0, 2)} } };
  } catch (int) {
  }
}
//----

Observed output is:

0x22fd60->A::A(int); i = 1

It does not happen when the local array y1 is replaced by a non-array Y.
Comment 2 Jonathan Wakely 2012-02-21 10:37:29 UTC
This looks similar to PR 41449 which Jason fixed
Comment 3 Daniel Krügler 2012-02-21 10:53:59 UTC
(In reply to comment #2)

Agreed. It seems that the fix did not solve some array-related corner cases like this one.
Comment 4 Eric Gallager 2017-08-18 16:09:49 UTC
(In reply to Daniel Krügler from comment #1)
> Confirmed for 4.7.0 20120218 (experimental).
> 

(In reply to Daniel Krügler from comment #3)
> (In reply to comment #2)
> 
> Agreed. It seems that the fix did not solve some array-related corner cases
> like this one.

Changing status to NEW then.
Comment 5 Jonathan Wakely 2019-02-21 01:40:51 UTC
Related to PR 57510
Comment 6 Jason Merrill 2019-12-19 14:07:54 UTC
Author: jason
Date: Thu Dec 19 14:07:22 2019
New Revision: 279577

URL: https://gcc.gnu.org/viewcvs?rev=279577&root=gcc&view=rev
Log:
	PR c++/52320 - EH cleanups for partially constructed arrays.

This testcase wasn't fixed by the 66139 patch; split_nonconstant_init_1 was
failing to add a cleanup for an array member of a class (e.g. e1) that will
run if initializing a later member (e.g. e2) throws.

	* typeck2.c (split_nonconstant_init_1): Add nested parm.
	Add cleanup for whole array if true.

Added:
    trunk/gcc/testsuite/g++.dg/eh/aggregate1.C
Modified:
    trunk/gcc/cp/ChangeLog
    trunk/gcc/cp/typeck2.c
Comment 7 Jason Merrill 2019-12-19 14:14:29 UTC
Fixed for GCC 10 so far.
Comment 8 Jakub Jelinek 2020-03-11 07:00:36 UTC
commit r10-7110-g14af5d9b19b0f4ee1d929e505e245ae5c2f6bdc6
Author: Jason Merrill <jason@redhat.com>
Date:   Tue Mar 10 16:05:18 2020 -0400

    c++: Partially revert patch for PR66139.
    
    The patch for 66139 exposed a long-standing bug with
    split_nonconstant_init (since 4.7, apparently): initializion of individual
    elements of an aggregate are not a full-expressions, but
    split_nonconstant_init was making full-expressions out of them.  My fix for
    66139 extended the use of split_nonconstant_init, and thus the bug, to
    aggregate initialization of temporaries within an expression, in which
    context (PR94041) the bug is more noticeable.  PR93922 is a problem with my
    implementation strategy of splitting out at gimplification time, introducing
    function calls that weren't in the GENERIC.  So I'm going to revert the
    patch now and try again for GCC 11.
    
    gcc/cp/ChangeLog
    2020-03-10  Jason Merrill  <jason@redhat.com>
    
            PR c++/93922
            PR c++/94041
            PR c++/52320
            PR c++/66139
            * cp-gimplify.c (cp_gimplify_init_expr): Partially revert patch for
            66139: Don't split_nonconstant_init.  Remove pre_p parameter.
Comment 9 Jakub Jelinek 2020-05-07 11:56:12 UTC
GCC 10.1 has been released.
Comment 10 Richard Biener 2020-07-23 06:51:52 UTC
GCC 10.2 is released, adjusting target milestone.
Comment 11 Richard Biener 2021-04-08 12:02:21 UTC
GCC 10.3 is being released, retargeting bugs to GCC 10.4.
Comment 12 GCC Commits 2022-01-07 00:24:41 UTC
The master branch has been updated by Jason Merrill <jason@gcc.gnu.org>:

https://gcc.gnu.org/g:4f6bc28fc7dd86bd9e7408cbf28de1e973dd1eda

commit r12-6329-g4f6bc28fc7dd86bd9e7408cbf28de1e973dd1eda
Author: Jason Merrill <jason@redhat.com>
Date:   Thu Mar 5 15:50:45 2020 -0500

    c++: EH and partially constructed aggr temp [PR66139]
    
    Now that PR94041 is fixed, I can return to addressing PR66139, missing
    cleanups for partially constructed aggregate temporaries.  My previous
    approach of calling split_nonconstant_init in cp_gimplify_init_expr broke
    because gimplification is too late to be introducing destructor calls.  So
    instead I now call it under cp_fold_function, just before cp_genericize;
    doing it from cp_genericize itself ran into trouble with the rewriting of
    invisible references.
    
    So now the prediction in cp_gimplify_expr that cp_gimplify_init_expr
    might need to replace references to TARGET_EXPR_SLOT within
    TARGET_EXPR_INITIAL has come to pass.  constexpr.c already had the simple
    search-and-replace tree walk I needed, but it needed to be fixed to actually
    replace all occurrences instead of just the first one.
    
    Handling of VEC_INIT_EXPR at gimplify time had similar issues that we worked
    around with build_vec_init_elt, so I'm moving that to cp_fold_function as
    well.  But it seems that build_vec_init_elt is still useful for setting the
    VEC_INIT_EXPR_IS_CONSTEXPR flag, so I'm leaving it alone.
    
    This also fixes 52320, because build_aggr_init of each X from build_vec_init
    builds an INIT_EXPR rather than call split_nonconstant_init at that point,
    and now that INIT_EXPR gets split later.
    
            PR c++/66139
            PR c++/52320
    
    gcc/cp/ChangeLog:
    
            * constexpr.c (replace_decl): Rename from replace_result_decl.
            * cp-tree.h (replace_decl): Declare it.
            * cp-gimplify.c (cp_gimplify_init_expr): Call it.
            (cp_gimplify_expr): Don't handle VEC_INIT_EXPR.
            (cp_genericize_init, cp_genericize_init_expr)
            (cp_genericize_target_expr): New.
            (cp_fold_r): Call them.
            * tree.c (build_array_copy): Add a TARGET_EXPR.
            * typeck2.c (digest_init_r): Look through a TARGET_EXPR.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/cpp0x/initlist116.C: New test.
            * g++.dg/cpp0x/initlist117.C: New test.
            * g++.dg/cpp0x/lambda/lambda-eh.C: New test.
            * g++.dg/eh/aggregate1.C: New test.
Comment 13 Jason Merrill 2022-01-07 00:30:39 UTC
Fixed for GCC 12.