I believe this is a clang bug related to a new placement expression whose constructors throw

The problem occurs when a new expression of the form new(std::nothrow) C; , where C is the name of the class whose constructor throws. See the code below and a live example using g++ :

 #include <iostream> void* operator new(std::size_t size, const std::nothrow_t&) noexcept { void* p; p = malloc(size); std::cout << "operator new(std::nothrow)" << '\n'; return p; } void operator delete(void* p, const std::nothrow_t&) noexcept { free(p); std::cout << "operator delete(std::nothrow)" << '\n'; std::cout << p << '\n'; } class T{}; class C { int i; public: C(int i) : i{i} { std::cout << "C()" << '\n'; throw T{}; } ~C() { std::cout << "~C()" << '\n'; } }; int main() { C* c; try { c = new(std::nothrow) C(3); } catch (T&) { std::cout << "exception thrown in C(int) was caught" << '\n'; std::cout << c << '\n'; } } 

g++ displays the following and looks correct:

 operator new(std::nothrow) C() operator delete(std::nothrow) 0x13f9c20 exception thrown in C(int) was caught 0 

If you use clang , you will get the following output:

 operator new(std::nothrow) C() exception thrown in C(int) was caught 0x7fffecdeed00 

That is, as if clang does not call operator delete(void*, std::nothrow_t&) defined in the program, and instead calls the operator in the standard library.

It is strange that simply removing the expression std::cout << p << '\n'; in operator delete(void*, std::nothrow_t&) defined in the code, clangs will appear correctly, print:

 operator new(std::nothrow) C() operator delete(std::nothrow) exception thrown in C(int) was caught 0x7fffc0ffc000 

Edit

In response to the comment from @TC below and others who say that the code above has undefined behavior, I present below another code that shows how the compiler should act in order to correctly compile the fragment above using the pseudocode provided by @TC here . See also this live example . It is important to note that this code does not use the new expression new(nothrow) .

 #include <iostream> void * operator new(std::size_t n) { void* p; try { p = malloc(n); } catch (std::bad_alloc&) { throw; } std::cout << "operator new" << '\n'; return p; } void operator delete(void *p) noexcept { free(p); std::cout << "operator delete" << '\n'; } void* operator new(std::size_t size, const std::nothrow_t&) noexcept { void* p = malloc(size); std::cout << "operator new(std::nothrow)" << '\n'; return p; } void operator delete(void* p, const std::nothrow_t&) noexcept { free(p); std::cout << "operator delete(std::nothrow)" << '\n'; std::cout << p << '\n'; } class T {}; class C { int i; public: C(int i) : i{ i } { std::cout << "C()" << '\n'; throw T{}; } ~C() { std::cout << "~C()" << '\n'; } }; int main() { C *c; try { c = (C*)operator new(sizeof(C), std::nothrow); struct cleanup { void* p; bool active; ~cleanup() { if (active) operator delete(p, std::nothrow); } void dismiss() { active = false; } } guard = { (void*)c, true }; new(c) C{1}; guard.dismiss(); } catch ( std::bad_alloc& ) { c = nullptr; } catch (T&) { std::cout << "exception thrown in C() was caught" << '\n'; std::cout << c << '\n'; } } 

g++ for this code displays the following:

 operator new(std::nothrow) C() operator delete(std::nothrow) 0x10c3c20 exception thrown in C() was caught 0x10c3c20 

Surprisingly, clang seems to act correctly with this code, which does not use the new expression new(nothrow) , which clearly shows that when creating this new expression, clang has an error.

+6
source share
1 answer

On my OS X 10.11.1 system, the std :: lib-supplied operator delete is in / usr / lib / libc ++ abi.dylib. On Unix-like systems, this signature becomes removable, giving it a “loose connection”. When the linker sees two identical signatures, and one of them has a weak connection, he will prefer the one that does not.

I can confirm that in my system, operator delete(void*, std::nothrow_t const&) has a weak connection with the following command:

 $ nm -gm /usr/lib/libc++abi.dylib |c++filt |grep nothrow_t 0000000000024406 (__TEXT,__text) weak external operator delete[](void*, std::nothrow_t const&) 00000000000243fc (__TEXT,__text) weak external operator delete(void*, std::nothrow_t const&) 00000000000243c0 (__TEXT,__text) weak external operator new[](unsigned long, std::nothrow_t const&) 000000000002437e (__TEXT,__text) weak external operator new(unsigned long, std::nothrow_t const&) 

Can you conduct a similar analysis in your system and report the results?

Update

Thanks to the TC instructions below on how to copy a symptom, now it seems to me that this is a clan compiler code generation error, introduced in 3.7, still present at the top of the trunk and only reproduced at -O2 (not -O1 or lower and not -O3) .

I think the error report is fine, and it should have good instructions on how to reproduce the error (unless you want them to give it a low priority).

PS

And set C *c = nullptr; so they don’t waste time chasing the inappropriate UB.

Second update

I still cannot play this locally with clang tip-of-trunk. But I can see it on websites like:

http://melpon.org/wandbox/permlink/5zIRyPJpq32LfU0t

I have no explanation for this discrepancy yet. Perhaps my tip of the trunk is later than theirs? Perhaps they do not use libC ++ abi?

+5
source

All Articles