Hello! GCC-16 does not compile the following C++26 code, which AFAIK is valid: #include <new> #include <memory> consteval auto func () { struct error { }; struct object { constexpr explicit object (int value) { if (value < 0) { throw error { }; } } }; try { struct storage { object* ptr; constexpr storage () : ptr { std::allocator<object> { }.allocate(1) } { } constexpr ~storage () { std::allocator<object> { }.deallocate(ptr, 1); } }; auto object_storage = storage { }; ::new(object_storage.ptr) object { -1 }; } catch (error&) { } return true; } static_assert(func()); Compiler Explorer link: https://godbolt.org/z/K5K86qWvM To me this looks like a bug in GCC, which is occurring when an object is being created at compile-time via placement new, inside a try & catch block, and its constructor throws an exception. Thank you, Mateusz Zych
To me this testcase looks to be invalid, but I think most likely undesirably so. The wg21.link/P2747R2 paper made placement operator new constexpr. At that time constexpr exceptions weren't in C++26, so that was all that was needed. But later on when wg21.link/P3068R5 was voted in, the P2747R2 changes look insufficient. The problem is that when you throw from constructor during operator new, it invokes placement operator delete. And P2747R2 didn't touch that, see http://eel.is/c++draft/new.delete.placement and http://eel.is/c++draft/new.syn If I change in preprocessed source the --- pr122671.ii 2025-11-18 12:05:55.474736993 +0100 +++ pr122671.ii 2025-11-18 12:17:23.568904481 +0100 @@ -282,10 +282,10 @@ void* operator new[](std::size_t, void* noexcept { return __p; } -inline void operator delete (void*, void*) +constexpr void operator delete (void*, void*) noexcept { } -inline void operator delete[](void*, void*) +constexpr void operator delete[](void*, void*) noexcept { } then it compiles fine. So, I think a LWG issue for this should be filed and make those placement operator deletes constexpr in C++26 too. Testcase with some formatting cleanups for the testsuite: #include <new> #include <memory> consteval auto foo () { struct E {}; struct O { constexpr explicit O (int x) { if (x < 0) { throw E {}; } } }; try { struct S { O *s; constexpr S () : s { std::allocator <O> {}.allocate (1) } {} constexpr ~S () { std::allocator <O> {}.deallocate (s, 1); } }; auto s = S {}; ::new (s.s) O { -1 }; } catch (E &) { } return true; } static_assert (foo ());
https://cplusplus.github.io/LWG/issue4477
The master branch has been updated by Jakub Jelinek <jakub@gcc.gnu.org>: https://gcc.gnu.org/g:5294e0a0b40674365d8f9190fc1d9c456ea8c150 commit r16-5411-g5294e0a0b40674365d8f9190fc1d9c456ea8c150 Author: Jakub Jelinek <jakub@redhat.com> Date: Wed Nov 19 09:38:17 2025 +0100 libstdc++: Implement proposed resolution of LWG4477 [PR122671] This patch implements the proposed resolution of https://cplusplus.github.io/LWG/issue4477 The PR complains that one can't successfully throw from constructor in placement new in a constant expression and catch that exception later on. The problem is while P2747R2 made placement ::operator new and ::operator new[] constexpr, when the ctor throws it invokes also these weird placement ::operator delete and ::operator delete[] which intentionally perform no action, and those weren't constexpr, so constant expression evaluation failed. 2025-11-19 Jakub Jelinek <jakub@redhat.com> PR libstdc++/122671 * libsupc++/new (::operator delete, ::operator delete[]): Implement proposed LWG4477 resolution. Use _GLIBCXX_PLACEMENT_CONSTEXPR for placement operator deletes. * g++.dg/cpp26/constexpr-eh17.C: New test.
Implemented for GCC 16.
Hello Jakub! You're right! I haven't realized that the issue was with the P2747R2. Agreed - since the operator placement delete "intentionally performs no action", marking it constexpr should be non-controversial. Thank you for submitting the LWG4477 issue and committing the required change into GCC's libstdc++. Hopefully your proposed resolution will be accepted into C++26. Lastly, I confirm that your change works. I tested the GCC trunk by compiling a code which throws an exception during invocation of the std::vector<T, Alloc>::emplace_back(). Thank you, Mateusz Zych