https://gcc.gnu.org/g:1c2e59eccb78aadf3f4e88edc0f6a1dd84c01d07

commit r17-2384-g1c2e59eccb78aadf3f4e88edc0f6a1dd84c01d07
Author: Jakub Jelinek <[email protected]>
Date:   Tue Jul 14 08:00:30 2026 +0200

    c++: Default deallocating functions to noexcept for C++11
    
    https://eel.is/c++draft/except.spec#9 says
    A deallocation function with no explicit noexcept-specifier has a 
non-throwing
    exception specification.
    and something like that is there back to C++11.
    We only imply noexcept for destructors though (in
    deduce_noexcept_on_destructor).
    The following patch does that for operator delete/operator delete[]
    both for :: namespace ones and for class member functions.
    
    On Mon, Jul 13, 2026 at 04:34:12PM -0400, Jason Merrill wrote:
    > Does this affect mangling?
    
    I've tried to compile
    
    struct A {};
    void operator delete (void *, A) {}
    struct B {};
    void operator delete (void *, B) {}
    struct C {
      static void operator delete (void *);
    };
    void C::operator delete (void *) {}
    struct D {};
    void operator delete (void *, D) noexcept (true) {}
    template <bool B>
    struct E {
      template <typename T>
      static void operator delete (void *, T) noexcept (B) {}
    };
    auto f1 () { return &E <true>::operator delete<int>; }
    auto f2 () { return &E <false>::operator delete<int>; }
    
    with g++ 15, vanilla trunk and patched trunk and everything is identical
    except .ident, mangled names
    
    _ZdlPv1A
    _ZdlPv1B
    _ZN1CdlEPv
    _ZdlPv1D
    _Z2f1v
    _Z2f2v
    _ZN1EILb1EEdlIiEEvPvT_
    _ZN1EILb0EEdlIiEEvPvT_
    
    If I add
    template <typename T>
    void bar () {}
    void baz () { bar <decltype (C::operator delete)> (); }
    to that testcase, then there is a difference, _Z3barIFvPvEEvv
    vs. _Z3barIDoFvPvEEvv.  Isn't that desirable though?
    Note, clang++ mangles it the same as the patched g++, it is unlikely
    anybody does this in real-world code and they do actually want to
    test in that case whether it is explicitly or implicitly noexcept or not.
    Like when one uses reflection and tests is_noexcept, or uses noexcept
    on the destroying delete.
    
    2026-07-14  Jakub Jelinek  <[email protected]>
    
            * decl.cc (grokfndecl): If raises is NULL_TREE for C++11
            deallocation function, use noexcept_true_spec instead.
    
            * g++.dg/cpp0x/dealloc1.C: New test.
            * g++.dg/cpp0x/dealloc2.C: New test.
            * g++.dg/cpp2a/destroying-delete7.C: New test.
            * g++.dg/reflect/is_noexcept5.C: New test.
            * g++.dg/cpp1z/aligned-new3.C (operator delete): Don't expect
            a warning.
    
    Reviewed-by: Jason Merrill <[email protected]>

Diff:
---
 gcc/cp/decl.cc                                  |  8 ++++++++
 gcc/testsuite/g++.dg/cpp0x/dealloc1.C           |  8 ++++++++
 gcc/testsuite/g++.dg/cpp0x/dealloc2.C           |  8 ++++++++
 gcc/testsuite/g++.dg/cpp1z/aligned-new3.C       |  2 +-
 gcc/testsuite/g++.dg/cpp2a/destroying-delete7.C | 10 ++++++++++
 gcc/testsuite/g++.dg/reflect/is_noexcept5.C     | 25 +++++++++++++++++++++++++
 6 files changed, 60 insertions(+), 1 deletion(-)

diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 340bbe71b9df..d10290752e7b 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -12166,6 +12166,14 @@ grokfndecl (tree ctype,
       return NULL_TREE;
     }
 
+  /* [except.spec]/9 - A deallocation function with no explicit 
noexcept-specifier
+     has a non-throwing exception specification.  */
+  if (raises == NULL_TREE
+      && cxx_dialect >= cxx11
+      && IDENTIFIER_NEWDEL_OP_P (declarator)
+      && !IDENTIFIER_NEW_OP_P (declarator))
+    raises = noexcept_true_spec;
+
   type = build_cp_fntype_variant (type, rqual, raises, late_return_type_p);
 
   decl = build_lang_decl_loc (location, FUNCTION_DECL, declarator, type);
diff --git a/gcc/testsuite/g++.dg/cpp0x/dealloc1.C 
b/gcc/testsuite/g++.dg/cpp0x/dealloc1.C
new file mode 100644
index 000000000000..8c861d045803
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/dealloc1.C
@@ -0,0 +1,8 @@
+// { dg-do compile { target c++11 } }
+
+struct A {};
+void operator delete (void *, A);
+void operator delete (void *, A) noexcept;
+struct B {};
+void operator delete (void *, B) noexcept (true);
+void operator delete (void *, B);
diff --git a/gcc/testsuite/g++.dg/cpp0x/dealloc2.C 
b/gcc/testsuite/g++.dg/cpp0x/dealloc2.C
new file mode 100644
index 000000000000..5a0a89257b7a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/dealloc2.C
@@ -0,0 +1,8 @@
+// { dg-do compile { target c++11 } }
+
+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" }
+struct B {};
+void operator delete (void *, B) noexcept (false);
+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/cpp1z/aligned-new3.C 
b/gcc/testsuite/g++.dg/cpp1z/aligned-new3.C
index 165c3771ab9d..d6fba98fd133 100644
--- a/gcc/testsuite/g++.dg/cpp1z/aligned-new3.C
+++ b/gcc/testsuite/g++.dg/cpp1z/aligned-new3.C
@@ -13,7 +13,7 @@ void* operator new (std::size_t n, std::align_val_t)
 }
 
 bool deleted = false;
-void operator delete (void *p, std::size_t, std::align_val_t) // { dg-warning 
"exception specifier" }
+void operator delete (void *p, std::size_t, std::align_val_t)
 {
   deleted = true;
   operator delete (p);
diff --git a/gcc/testsuite/g++.dg/cpp2a/destroying-delete7.C 
b/gcc/testsuite/g++.dg/cpp2a/destroying-delete7.C
new file mode 100644
index 000000000000..468cc315d11a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/destroying-delete7.C
@@ -0,0 +1,10 @@
+// { dg-do compile { target c++20 } }
+
+#include <new>
+
+struct T {
+  ~T () noexcept (false);
+  static void operator delete (T *, std::destroying_delete_t);
+};
+T *p = nullptr;
+static_assert (noexcept (delete (p)));
diff --git a/gcc/testsuite/g++.dg/reflect/is_noexcept5.C 
b/gcc/testsuite/g++.dg/reflect/is_noexcept5.C
new file mode 100644
index 000000000000..854c3291fbe5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/is_noexcept5.C
@@ -0,0 +1,25 @@
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+// Test std::meta::is_noexcept.
+
+#include <meta>
+#include <new>
+
+struct A {
+  ~A ();
+};
+static_assert (is_noexcept (^^A::~A));
+struct B {
+  ~B () noexcept (false);
+};
+static_assert (!is_noexcept (^^B::~B));
+struct C {
+  ~C () noexcept (false);
+  static void operator delete (void *);
+};
+static_assert (is_noexcept (^^C::operator delete));
+struct D {
+  ~D () noexcept (false);
+  static void operator delete (D *, std::destroying_delete_t);
+};
+static_assert (is_noexcept (^^D::operator delete));

Reply via email to