Bug 15764 - no cleanup if temporary's dtor terminates with an exception
Summary: no cleanup if temporary's dtor terminates with an exception
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 3.4.0
: P2 normal
Target Milestone: ---
Assignee: Jason Merrill
URL:
Keywords: wrong-code
Depends on:
Blocks:
 
Reported: 2004-06-01 17:03 UTC by Boris Kolpackov
Modified: 2007-10-04 05:26 UTC (History)
2 users (show)

See Also:
Host: x86_64-unknown-linux-gnu
Target: x86_64-unknown-linux-gnu
Build: x86_64-unknown-linux-gnu
Known to work: 4.3.0
Known to fail:
Last reconfirmed: 2007-09-06 05:36:24


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Boris Kolpackov 2004-06-01 17:03:21 UTC
$ cat >test.cxx

#include <cstdlib>

struct a
{
  ~a ()
  {
    throw 1;
  }
};

int 
f (a const& acr = a ())
{
  return 1;
}

struct b
{
  b (int)
  {
  }

  ~b ()
  {
    std::abort ();
  }
};

int
main ()
{
  try
  {
    b b_ (f ());    
  } 
  catch (...) {}
}

$ g++ -v
Reading specs from /home/boris/local/lib/gcc/x86_64-unknown-linux-gnu/3.4.0/specs
Configured with: /home/boris/gcc-3.4.0/configure --prefix=/home/boris/local
--enable-__cxa_atexit --enable-languages=c,c++ --enable-long-long
--with-system-zlib --enable-shared --disable-multilib
Thread model: posix
gcc version 3.4.0

$ g++ test.cxx
$ ./a.out

I tried it on Intel C++ 8.0 and Compaq C++ 6.5-040 and they both call b::~b.

-boris
Comment 1 Drea Pinski 2004-06-01 17:13:32 UTC
    b b_ (f ());    

b is not initialized when ~a throws as it is equivant to
int i;
{
  const a &c= a();
  i = f(c);
}
b b_ (i);
Comment 2 Wolfgang Bangerth 2004-06-01 17:45:01 UTC
Andrew is right: b::b() isn't called (as you can easily verify by 
placing another call to std::abort into the constructor). Thus, 
there shouldn't be any call to the destructor either. 
 
W. 
Comment 3 Boris Kolpackov 2004-06-01 17:52:55 UTC
$ cat >test.cxx

#include <iostream>

using std::cerr;
using std::endl;

struct a
{
  ~a ()
  {
    throw 1;
  }
};

int 
f (a const& acr = a ())
{
  return 1;
}

struct b
{
  b (int)
  {
    cerr << "b" << endl;
  }

  ~b ()
  {
    cerr << "~b" << endl;
    // std::abort ();
  }
};

int
main ()
{
  try
  {
    b b_ (f ());    
  } 
  catch (...) {}
}

$ g++ test.cxx
$ ./a.out
b
$ #?
Comment 4 Wolfgang Bangerth 2004-06-01 18:15:44 UTC
Alright, I shouldn't first reduce and then draw conclusions. The 
call to f() is the necessary step. Here is a slightly smaller testcase: 
 
------------------ 
extern "C" void abort (); 
 
int counter = 0; 
 
struct a { 
  ~a () { throw 1; } 
}; 
 
int f (a const&) { return 1; } 
 
struct b { 
  b (...) { ++counter; } 
  ~b ()   { --counter; } 
}; 
 
int main () { 
  try { b tmp(f (a())); }  
  catch (...) {} 
 
  if (counter != 0) 
    abort (); 
} 
-------------- 
 
In 
  b tmp(f(a())); 
the call to a::a() throws, so f return prematurely, and we shouldn't 
even start to construct tmp. However, we do, thus incrementing the 
counter. Then we fail to run the destructor, though. 
 
W. 
Comment 5 Wolfgang Bangerth 2004-06-01 18:16:25 UTC
Confirmed, indeed, with 2.95 through mainline. 
Comment 6 Boris Kolpackov 2004-06-01 18:18:55 UTC
> the call to a::a() throws
              ^^^^^^
              a::~a()

Comment 7 Wolfgang Bangerth 2004-06-01 18:29:03 UTC
Gee, yes, I can't seem to read code today. Since the lifetime 
of the temporary a() is until the end of the expression, the 
variable tmp is fully constructed. It needs to be destroyed  
again as part of the cleanup to be done when a::~a throws, 
but isn't. 
 
W. 
Comment 8 Drea Pinski 2004-10-15 02:20:25 UTC
The try/finally block for the tmp is not around the initializer, why?
Comment 9 Jason Merrill 2007-09-06 05:36:24 UTC
This version of the test breaks under ICC:

----------------------
extern "C" void abort ();

int counter = 0;
int thrown = 0;
struct a {
  ~a () { if (!thrown++) throw 1; }
};

int f (a const&, const a&) { return 1; }

struct b {
  b (...) { ++counter; }
  ~b ()   { --counter; }
};

void g();

int main () {
  try {
    b tmp(f (a(), a()));
  }
  catch (...) {}

  if (counter != 0)
    abort ();
}
----------------------

For whatever reason, if the first ~a throws, ICC doesn't try to destroy tmp, but if the second ~a throws, ICC cleans up tmp properly.  Odd.
Comment 10 Jason Merrill 2007-10-03 10:43:52 UTC
Subject: Bug 15764

Author: jason
Date: Wed Oct  3 10:43:42 2007
New Revision: 128979

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=128979
Log:
        PR c++/15764
        * cp/decl.c (wrap_cleanups_r): New fn.
        (wrap_temporary_cleanups): New fn.
        (initialize_local_var): Call it.
        * tree-eh.c (same_handler_p): New fn.
        (optimize_double_finally): New fn.
        (refactor_eh_r): New fn.
        (refactor_eh): New fn.
        (pass_refactor_eh): New pass.
        * tree-pass.h: Declare it.
        * passes.c (init_optimization_passes): Add it.

Added:
    trunk/gcc/testsuite/g++.dg/eh/init-temp1.C
Modified:
    trunk/gcc/ChangeLog
    trunk/gcc/cp/ChangeLog
    trunk/gcc/cp/decl.c
    trunk/gcc/passes.c
    trunk/gcc/tree-eh.c
    trunk/gcc/tree-pass.h

Comment 11 Jason Merrill 2007-10-03 18:26:40 UTC
Fixed for 4.3.