On Tue, Jul 14, 2026 at 10:19:20AM -0400, Jason Merrill wrote: > > @@ -9719,6 +9738,11 @@ cp_finish_decl (tree decl, tree init, bo > > DECL_ATTRIBUTES (decl))) > > omp_declare_variant_finalize (decl, attr); > > + /* [basic.stc.dynamic.deallocation]/3 - A deallocation function shall not > > + have a potentially throwing exception specification. */ > > + if (cxx_dialect >= cxx29 && TREE_CODE (decl) == FUNCTION_DECL) > > Let's move the cxx_dialect check into the new function.
Done. > > @@ -20538,6 +20562,11 @@ finish_function (bool inline_p) > > if (fndecl == NULL_TREE || fndecl == error_mark_node) > > return error_mark_node; > > + /* [basic.stc.dynamic.deallocation]/3 - A deallocation function shall not > > + have a potentially throwing exception specification. */ > > + if (cxx_dialect >= cxx29) > > + maybe_diagnose_deallocation_noexcept_false (fndecl); > > It's surprising to check this here, where we're dealing with the end of a > function definition, as well as in the other two functions that deal with > forming the declaration. > > Would it work to check in grokfndecl and not here nor in cp_finish_decl? It works too (and doesn't emit the undesirable diagnostics in cpp0x/dealloc2.C line 9 for C++29). 2026-07-14 Jakub Jelinek <[email protected]> PR c++/125833 * cp-tree.h: Implement C++29 P3424R2 - Deallocation Functions with Throwing Exception Specification Are Ill-formed. (maybe_diagnose_deallocation_noexcept_false): Declare. * decl.cc (maybe_diagnose_deallocation_noexcept_false): New function. (grokfndecl): Call it. * pt.cc (tsubst_function_decl): Likewise. * g++.dg/cpp0x/dealloc2.C: Expect extra diagnostics in C++29. * g++.dg/cpp29/dealloc1.C: New test. --- gcc/cp/cp-tree.h.jj 2026-07-14 16:39:33.562964575 +0200 +++ gcc/cp/cp-tree.h 2026-07-14 16:40:07.619539955 +0200 @@ -7651,6 +7651,7 @@ extern tree start_decl (const cp_decl extern void start_decl_1 (tree, bool); extern bool check_array_initializer (tree, tree, tree); extern void omp_declare_variant_finalize (tree, tree); +extern void maybe_diagnose_deallocation_noexcept_false (tree); struct cp_decomp { tree decl; unsigned int count; }; extern void cp_finish_decl (tree, tree, bool, tree, int, cp_decomp * = nullptr); extern tree lookup_decomp_type (tree); --- gcc/cp/decl.cc.jj 2026-07-14 16:39:33.563964563 +0200 +++ gcc/cp/decl.cc 2026-07-14 16:43:58.359663079 +0200 @@ -9430,6 +9430,26 @@ omp_declare_variant_finalize (tree decl, } } +/* [basic.stc.dynamic.deallocation]/3 - A deallocation function shall not + have a potentially throwing exception specification. */ + +void +maybe_diagnose_deallocation_noexcept_false (tree decl) +{ + if (cxx_dialect >= cxx29 + && DECL_NAME (decl) + && IDENTIFIER_NEWDEL_OP_P (DECL_NAME (decl)) + && !IDENTIFIER_NEW_OP_P (DECL_NAME (decl))) + { + maybe_instantiate_noexcept (decl, tf_none); + tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl)); + if (spec && spec == noexcept_false_spec) + error_at (DECL_SOURCE_LOCATION (decl), + "deallocation function %qD declared possibly throwing", + decl); + } +} + static void cp_maybe_mangle_decomp (tree, cp_decomp *); /* Finish processing of a declaration; @@ -12621,6 +12641,10 @@ grokfndecl (tree ctype, } } + /* [basic.stc.dynamic.deallocation]/3 - A deallocation function shall not + have a potentially throwing exception specification. */ + maybe_diagnose_deallocation_noexcept_false (decl); + /* Caller will do the rest of this. */ if (check < 0) { --- gcc/cp/pt.cc.jj 2026-07-14 16:39:33.566964525 +0200 +++ gcc/cp/pt.cc 2026-07-14 16:44:15.358451136 +0200 @@ -15677,6 +15677,10 @@ tsubst_function_decl (tree t, tree args, DECL_ATTRIBUTES (r))) omp_declare_variant_finalize (r, attr); + /* [basic.stc.dynamic.deallocation]/3 - A deallocation function shall not + have a potentially throwing exception specification. */ + maybe_diagnose_deallocation_noexcept_false (r); + return r; } --- gcc/testsuite/g++.dg/cpp0x/dealloc2.C.jj 2026-07-14 16:39:33.567964513 +0200 +++ gcc/testsuite/g++.dg/cpp0x/dealloc2.C 2026-07-14 16:54:36.950706126 +0200 @@ -3,6 +3,7 @@ struct A {}; void operator delete (void *, A); void operator delete (void *, A) noexcept (false); // { dg-error "declaration of 'void operator delete\\\(void\\\*, A\\\) noexcept \\\(false\\\)' has a different exception specifier" } +// { dg-error "deallocation function 'void operator delete\\\(void\\\*, A\\\)' declared possibly throwing" "" { target c++29 } .-1 } struct B {}; -void operator delete (void *, B) noexcept (false); +void operator delete (void *, B) noexcept (false); // { dg-error "deallocation function 'void operator delete\\\(void\\\*, B\\\)' declared possibly throwing" "" { target c++29 } } void operator delete (void *, B); // { dg-error "declaration of 'void operator delete\\\(void\\\*, B\\\) noexcept' has a different exception specifier" } --- gcc/testsuite/g++.dg/cpp29/dealloc1.C.jj 2026-07-14 16:40:07.659788894 +0200 +++ gcc/testsuite/g++.dg/cpp29/dealloc1.C 2026-07-14 16:40:07.659788894 +0200 @@ -0,0 +1,23 @@ +// P3424R2 - Deallocation Functions with Throwing Exception Specification Are Ill-formed +// { dg-do compile { target c++11 } } + +struct A {}; +struct B {}; +void operator delete (void *, A) noexcept (false); // { dg-error "deallocation function 'void operator delete\\\(void\\\*, A\\\)' declared possibly throwing" "" { target c++29 } } +void operator delete (void *, B) noexcept (false) {} // { dg-error "deallocation function 'void operator delete\\\(void\\\*, B\\\)' declared possibly throwing" "" { target c++29 } } +void operator delete[] (void *, A) noexcept (false); // { dg-error "deallocation function 'void operator delete \\\[\\\]\\\(void\\\*, A\\\)' declared possibly throwing" "" { target c++29 } } +void operator delete[] (void *, B) noexcept (false) {} // { dg-error "deallocation function 'void operator delete \\\[\\\]\\\(void\\\*, B\\\)' declared possibly throwing" "" { target c++29 } } +template <bool T> +struct C { + static void operator delete (void *) noexcept (T); // { dg-error "deallocation function 'static void C<T>::operator delete\\\(void\\\*\\\) \\\[with bool T = false\\\]' declared possibly throwing" "" { target c++29 } } + static void operator delete[] (void *) noexcept (T); // { dg-error "deallocation function 'static void C<T>::operator delete \\\[\\\]\\\(void\\\*\\\) \\\[with bool T = false\\\]' declared possibly throwing" "" { target c++29 } } +}; +template <bool T> +struct D { + static void operator delete (void *) noexcept (T) {} // { dg-error "deallocation function 'static void D<T>::operator delete\\\(void\\\*\\\) \\\[with bool T = false\\\]' declared possibly throwing" "" { target c++29 } } + static void operator delete[] (void *) noexcept (T) {}// { dg-error "deallocation function 'static void D<T>::operator delete \\\[\\\]\\\(void\\\*\\\) \\\[with bool T = false\\\]' declared possibly throwing" "" { target c++29 } } +}; +C <false> a; +C <true> b; +D <false> c; +D <true> d; Jakub
