What follows is a summary of discussions between Martin and myself which have uncovered a cleaner way to handle call's to new. I'm also willing to try my hand at implementing the change but I could definitely use a few pointers. >>>>> "ML" == Martin v Loewis writes: >> I don't think some of the extra code is caused by a bug, but rather >> the addition of a feature. LL5 non-removal however is a bug, that >> I suspect someone introduced. A binary search of cvs could tell >> how broke it and how they broke it. That would then prompt them >> into fixing it (maybe). ML> Indeed. As we've found later on, the mainline *does* remove the ML> additional __builtin_delete call. ML> I'm not sure whether this really is a regression over egcs 1.1 - ML> in egcs 1.1, the processing of the implicit call to the ML> deallocator was quite different. So it may not be the case that ML> egcs 2.9x (with x>3, x<6) ever did the right thing. FWIW, the ML> change activating the current front-end behavior was ML> 1998-10-22 Martin von Löwis ML> * init.c (build_new_1): Delay cleanup until end of full ML> expression. Martin and I have discussed the reasons behind the change introduced above. It concerns the life span of temporaries in the call to a constructor called via new who's results is a parameter to a function call. Here is the example we used: f(new Foo(A(), B(), ...), ...); In particular the lifespan of A, B must extend until 'f' returns (which wasn't true in egcs 1.1). This turns out to be very tricky to accomplish given the requirement that any memory allocated by new must be deallocated if, Foo::Foo, A::A, or B::B throw an error. This is because in the simplest expansion of this code: { Foo *tmp = ::new(sizeof(Foo), ...); +- +- | | A a(); | 1 B b(); 2 | Foo::Foo(tmp, a, b, ...); | +- | f(tmp, ...) +-----} You need to catch and delete 'tmp' in region 1 but the lifespan of a & b needs to be '2'. This is not trivial (possible?) to communicate to the back end, and it is impossible for normal C++ code to generate such a situation. The patch referenced above introduced a sentry variable which prevented the error handler from deleting the allocated memory after region 1 completed, although the error handler was in effect for all of region 2. Fortunately, the C++ standard allows you some freedom in rewriting this simple version of the code, in particular you are free to rearrange the call to new with the construction of parameters for the constructor [see 5.3.4 sec 21 in final C++ Spec (sec 22 in CD2)]: Whether the allocation function is called before evaluating the constructor arguments or after evaluating the constructor arguments but before entering the constructor is unspecified. It is also unspecified whether the arguments to a constructor are evaluated if the allocation function returns the null pointer or exits using an exception. Given this we can safely rewrite the above code as: { A a(); B b(); Foo *tmp = ::new(sizeof(Foo), ...); try { Foo::Foo(tmp, a, b); } catch(...) { ::delete(tmp, ...); } f(tmp, ...); } In this case if the construction of 'a', or 'b' throws an error you don't need to catch the error and free the memory since you haven't allocated it yet!!! This should (hopefully) lead to much less convoluted code in all cases. However, now that I have a possible solution I would appreciate pointers to any places where similar sorts of rewriting are done, as well as where in the c++ font end such rewriting should be done. Any help and or comments are appreciated. -- Thomas DeWeese deweese@kodak.com "The only difference between theory and practice is that in theory there isn't any." -- unknown