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

Reply via email to