https://gcc.gnu.org/g:57ecb9897a096418ec3335bc0d18ec590e100396

commit r17-2482-g57ecb9897a096418ec3335bc0d18ec590e100396
Author: Jakub Jelinek <[email protected]>
Date:   Fri Jul 17 09:58:00 2026 +0200

    c++: Implement C++29 P3424R2 - Deallocation Functions with Throwing 
Exception Specification Are Ill-formed [PR125833]
    
    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 grokfndecl, maybe_instantiate_noexcept 
and
    cp_parser_class_specifier.
    
    2026-07-17  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 (maybe_instantiate_noexcept): Likewise.
            * parser.cc (cp_parser_class_specifier): Likewise.
    
            * g++.dg/cpp0x/dealloc2.C: Expect extra diagnostics in C++29.
            * g++.dg/cpp29/dealloc1.C: New test.
    
    Reviewed-by: Jason Merrill <[email protected]>

Diff:
---
 gcc/cp/cp-tree.h                      |  1 +
 gcc/cp/decl.cc                        | 23 ++++++++++++++++++
 gcc/cp/parser.cc                      |  4 ++++
 gcc/cp/pt.cc                          |  4 ++++
 gcc/testsuite/g++.dg/cpp0x/dealloc2.C |  3 ++-
 gcc/testsuite/g++.dg/cpp29/dealloc1.C | 45 +++++++++++++++++++++++++++++++++++
 6 files changed, 79 insertions(+), 1 deletion(-)

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 9271209ba2eb..c73b42ed44ff 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7651,6 +7651,7 @@ extern tree start_decl                            (const 
cp_declarator *, cp_decl_specifier_seq *, int,
 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);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index d10290752e7b..639eb85c0760 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -9430,6 +9430,25 @@ omp_declare_variant_finalize (tree decl, tree attr)
     }
 }
 
+/* [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)))
+    {
+      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 +12640,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)
     {
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index f455092b989e..3f042bec1c66 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -30135,6 +30135,10 @@ cp_parser_class_specifier (cp_parser* parser)
 
          /* Remove any template parameters from the symbol table.  */
          maybe_end_member_template_processing ();
+
+         /* [basic.stc.dynamic.deallocation]/3 - A deallocation function
+            shall not have a potentially throwing exception specification.  */
+         maybe_diagnose_deallocation_noexcept_false (decl);
        }
       vec_safe_truncate (unparsed_noexcepts, 0);
 
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 1fe94a863102..1290db45e182 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -28802,6 +28802,10 @@ maybe_instantiate_noexcept (tree fn, tsubst_flags_t 
complain)
   if (orig_fn)
     TREE_TYPE (orig_fn) = TREE_TYPE (fn);
 
+  /* [basic.stc.dynamic.deallocation]/3 - A deallocation function shall not
+     have a potentially throwing exception specification.  */
+  maybe_diagnose_deallocation_noexcept_false (fn);
+
   return true;
 }
 
diff --git a/gcc/testsuite/g++.dg/cpp0x/dealloc2.C 
b/gcc/testsuite/g++.dg/cpp0x/dealloc2.C
index 5a0a89257b7a..75b833382e98 100644
--- a/gcc/testsuite/g++.dg/cpp0x/dealloc2.C
+++ b/gcc/testsuite/g++.dg/cpp0x/dealloc2.C
@@ -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" }
diff --git a/gcc/testsuite/g++.dg/cpp29/dealloc1.C 
b/gcc/testsuite/g++.dg/cpp29/dealloc1.C
new file mode 100644
index 000000000000..cf39a68830d9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp29/dealloc1.C
@@ -0,0 +1,45 @@
+// 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, bool U>
+struct C {
+  static void operator delete (void *) noexcept (T);   // { dg-error 
"deallocation function 'static void C<T, U>::operator delete\\\(void\\\*\\\) 
\\\[with bool T = false; bool U = true\\\]' declared possibly throwing" "" { 
target c++29 } }
+  static void operator delete[] (void *) noexcept (T); // { dg-error 
"deallocation function 'static void C<T, U>::operator delete 
\\\[\\\]\\\(void\\\*\\\) \\\[with bool T = false; bool U = true\\\]' declared 
possibly throwing" "" { target c++29 } }
+};
+template <bool T, bool U>
+struct D {
+  static void operator delete (void *) noexcept (T) {} // { dg-error 
"deallocation function 'static void D<T, U>::operator delete\\\(void\\\*\\\) 
\\\[with bool T = false; bool U = true\\\]' declared possibly throwing" "" { 
target c++29 } }
+  static void operator delete[] (void *) noexcept (T) {}// { dg-error 
"deallocation function 'static void D<T, U>::operator delete 
\\\[\\\]\\\(void\\\*\\\) \\\[with bool T = false; bool U = true\\\]' declared 
possibly throwing" "" { target c++29 } }
+};
+C <false, false> a;
+C <true, false> b;
+D <false, false> c;
+D <true, false> d;
+auto e = &C <false, true>::operator delete;
+auto f = &C <true, true>::operator delete;
+auto g = &D <false, true>::operator delete;
+auto h = &D <true, true>::operator delete;
+auto i = &C <false, true>::operator delete[];
+auto j = &C <true, true>::operator delete[];
+auto k = &D <false, true>::operator delete[];
+auto l = &D <true, true>::operator delete[];
+struct E {
+  static void operator delete (void *) noexcept (C) {} // { dg-error 
"deallocation function 'static void E::operator delete\\\(void\\\*\\\)' 
declared possibly throwing" "" { target c++29 } }
+  static constexpr bool C = false;
+};
+template <int N>
+struct F {
+  static void operator delete (void *) noexcept (false);       // { dg-error 
"deallocation function 'static void F<N>::operator delete\\\(void\\\*\\\)' 
declared possibly throwing" "" { target c++29 } }
+  static void operator delete[] (void *) noexcept (false);     // { dg-error 
"deallocation function 'static void F<N>::operator delete 
\\\[\\\]\\\(void\\\*\\\)' declared possibly throwing" "" { target c++29 } }
+};
+template <int N>
+struct G {
+  static void operator delete (void *) noexcept (false) {}     // { dg-error 
"deallocation function 'static void G<N>::operator delete\\\(void\\\*\\\)' 
declared possibly throwing" "" { target c++29 } }
+  static void operator delete[] (void *) noexcept (false) {}   // { dg-error 
"deallocation function 'static void G<N>::operator delete 
\\\[\\\]\\\(void\\\*\\\)' declared possibly throwing" "" { target c++29 } }
+};

Reply via email to