Hi!

I am trying to submit my first patch to gcc. I hope I made everything right.

This patch fixes two false positives related to C++20 destroying operator
delete in -Wmismatched-new-delete and -Wfree-nonheap-object.

Tested on x86_64-pc-linux-gnu:
- make -k check
- bootstrapped

2026-03-04 Yifan Dong <[email protected]>

ChangeLog:
* gimple-ssa-warn-access.cc: Handle destroying operator delete in matching
and offset-warning paths.
* testsuite/g++.dg/warn/pr100861.C: New test.
* testsuite/g++.dg/warn/pr121717.C: New test.


diff --git a/gcc/gimple-ssa-warn-access.cc b/gcc/gimple-ssa-warn-access.cc
index c8f10cae32f..fce84f4b025 100644
--- a/gcc/gimple-ssa-warn-access.cc
+++ b/gcc/gimple-ssa-warn-access.cc
@@ -1810,6 +1810,40 @@ new_delete_mismatch_p (tree new_decl, tree
delete_decl)
   return mismatch;
 }

+/* Return true if TYPE is std::destroying_delete_t.  */
+
+static bool
+std_destroying_delete_t_p (tree type)
+{
+  if (!type)
+    return false;
+
+  type = TYPE_MAIN_VARIANT (type);
+  return (TYPE_CONTEXT (type)
+  && TREE_CODE (TYPE_CONTEXT (type)) == NAMESPACE_DECL
+  && DECL_NAME (TYPE_CONTEXT (type))
+  && id_equal (DECL_NAME (TYPE_CONTEXT (type)), "std")
+  && TYPE_IDENTIFIER (type)
+  && id_equal (TYPE_IDENTIFIER (type), "destroying_delete_t"));
+}
+
+/* Return true if FNDECL is a destroying operator delete.
+   Such a function has at least two parameters and its second one has
+   type std::destroying_delete_t.  */
+
+static bool
+destroying_operator_delete_p (tree fndecl)
+{
+  if (!DECL_IS_OPERATOR_DELETE_P (fndecl))
+    return false;
+
+  tree args = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
+  if (!args || !TREE_CHAIN (args))
+    return false;
+
+  return std_destroying_delete_t_p (TREE_VALUE (TREE_CHAIN (args)));
+}
+
 /* ALLOC_DECL and DEALLOC_DECL are pair of allocation and deallocation
    functions.  Return true if the latter is suitable to deallocate objects
    allocated by calls to the former.  */
@@ -1825,9 +1859,16 @@ matching_alloc_calls_p (tree alloc_decl, tree
dealloc_decl)
   if (DECL_IS_OPERATOR_NEW_P (alloc_decl))
     {
       if (DECL_IS_OPERATOR_DELETE_P (dealloc_decl))
- /* Return true iff both functions are of the same array or
-   singleton form and false otherwise.  */
- return !new_delete_mismatch_p (alloc_decl, dealloc_decl);
+ {
+  /* Return true iff both functions are of the same array or
+     singleton form and false otherwise.  Destroying delete has no
+     matching operator new counterpart and so should never mismatch
+     with one.  */
+  if (destroying_operator_delete_p (dealloc_decl))
+    return true;
+
+  return !new_delete_mismatch_p (alloc_decl, dealloc_decl);
+ }

       /* Return false for deallocation functions that are known not
  to match.  */
@@ -2052,6 +2093,11 @@ warn_dealloc_offset (location_t loc, gimple *call,
const access_ref &aref)
   if (!dealloc_decl)
     return false;

+  /* A destroying operator delete can legitimately be invoked with an
+     adjusted base-subobject pointer.  */
+  if (destroying_operator_delete_p (dealloc_decl))
+    return false;
+
   if (DECL_IS_OPERATOR_DELETE_P (dealloc_decl)
       && !DECL_IS_REPLACEABLE_OPERATOR (dealloc_decl))
     {
diff --git a/gcc/testsuite/g++.dg/warn/pr100861.C
b/gcc/testsuite/g++.dg/warn/pr100861.C
new file mode 100644
index 00000000000..37ea5129c56
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/pr100861.C
@@ -0,0 +1,31 @@
+/* PR c++/100861 */
+/* { dg-do compile { target c++20 } } */
+/* { dg-additional-options "-Wmismatched-new-delete" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+namespace std {
+struct destroying_delete_t
+{
+  explicit destroying_delete_t () = default;
+};
+} // namespace std
+
+void *operator new (size_t);
+void operator delete (void *) noexcept;
+
+struct Widget
+{
+  void operator delete (Widget *p, std::destroying_delete_t)
+  {
+    p->~Widget ();
+    ::operator delete (p);
+  }
+};
+
+void
+f ()
+{
+  Widget *p = new Widget;
+  delete p; // { dg-bogus "Wmismatched-new-delete" }
+}
diff --git a/gcc/testsuite/g++.dg/warn/pr121717.C
b/gcc/testsuite/g++.dg/warn/pr121717.C
new file mode 100644
index 00000000000..86a0362b8c2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/pr121717.C
@@ -0,0 +1,30 @@
+/* PR middle-end/121717 */
+/* { dg-do compile { target c++20 } } */
+/* { dg-options "-O2 -Wfree-nonheap-object" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+namespace std {
+struct destroying_delete_t
+{
+  explicit destroying_delete_t () = default;
+};
+} // namespace std
+
+struct A
+{
+  virtual ~A ();
+  void operator delete (A *, std::destroying_delete_t, size_t) noexcept;
+};
+
+struct B
+{
+  virtual void b () = 0;
+};
+
+class X : private B, public A
+{
+  ~X () override;
+};
+
+X::~X () {} // { dg-bogus "-Wfree-nonheap-object" }


Best,
Yifan

Reply via email to