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
