On 7/14/26 9:28 AM, Jakub Jelinek wrote:
Hi!
The following patch attempts to implement the C++29 P3424R2
Deallocation Functions with Throwing Exception Specification Are Ill-formed
paper by diagnosing those cases in cp_finish_decl, finish_function and
tsubst_function_decl.
So far tested with
GXX_TESTSUITE_STDS=98,11,14,17,20,23,26,29 make -j32 -k check-g++
make -j32 check-target-libstdc++-v3
Ok for trunk if it passes full bootstrap/regtest?
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.
(cp_finish_decl): Call it for C++29 on FUNCTION_DECLs.
(finish_function): Likewise.
* 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 07:50:44.223066308 +0200
+++ gcc/cp/cp-tree.h 2026-07-14 14:00:47.165119324 +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 08:00:17.466447608 +0200
+++ gcc/cp/decl.cc 2026-07-14 14:00:24.039412681 +0200
@@ -9430,6 +9430,25 @@ 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 (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;
@@ -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.
+ maybe_diagnose_deallocation_noexcept_false (decl);
+
if (processing_template_decl)
{
bool type_dependent_p;
@@ -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?
if (!DECL_OMP_DECLARE_REDUCTION_P (fndecl))
finish_lambda_scope ();
--- gcc/cp/pt.cc.jj 2026-07-13 18:37:33.601239787 +0200
+++ gcc/cp/pt.cc 2026-07-14 14:01:31.442562291 +0200
@@ -15677,6 +15677,11 @@ 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. */
+ if (cxx_dialect >= cxx29)
+ maybe_diagnose_deallocation_noexcept_false (r);
+
return r;
}
--- gcc/testsuite/g++.dg/cpp0x/dealloc2.C.jj 2026-07-14 08:00:17.469036737 +0200
+++ gcc/testsuite/g++.dg/cpp0x/dealloc2.C 2026-07-14 14:18:19.626012660
+0200
@@ -3,6 +3,8 @@
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" }
+// { dg-error "deallocation function 'void operator delete\\\(void\\\*, B\\\)' declared
possibly throwing" "" { target c++29 } .-1 }
--- gcc/testsuite/g++.dg/cpp29/dealloc1.C.jj 2026-07-14 14:11:38.779002313
+0200
+++ gcc/testsuite/g++.dg/cpp29/dealloc1.C 2026-07-14 14:16:40.831242443
+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