I'm not 100% clear on what the semantics of the matching here are meant to be - AFAICT, an operator new/delete pair matches (after falling through the other cases) if all their components (besides the actual operator name, of course) match, and the pair of actual operator names matches if one is a singleton new and the other a singleton delete, or, similarly, if one is an array new and the other an array delete. We also appear to ignore their argument types (or so it seems by experimentation - I was not able to quite discern what path those take).
Stripping operator template arguments from either side of the pair should have no impact on this logic, I think. Tested on x86_64-pc-linux-gnu, no regressions. OK for trunk? TIA, have a lovely evening. ---------- >8 ---------- Template parameters on a member operator new cannot affect its member status nor whether it is a singleton or array operator new, hence, we can ignore it for purposes of matching. Similar logic applies to the placement operator delete. In the PR (and a lot of idiomatic coroutine code generally), operator new is templated in order to be able to inspect (some of) the arguments passed to the coroutine, to make allocation-related decisions. However, the coroutine implementation will not call a placement delete form, so it cannot get templated. As a result, when demangling, we have an extra template DEMANGLE_COMPONENT_TEMPLATE around the actual operator new, but not operator delete. This terminates new_delete_mismatch_p early. PR middle-end/109224 - Wmismatched-new-delete false positive with a templated operator new (common with coroutines) gcc/ChangeLog: PR middle-end/109224 * gimple-ssa-warn-access.cc (new_delete_mismatch_p): Strip DEMANGLE_COMPONENT_TEMPLATE from the operator new and operator after demangling. gcc/testsuite/ChangeLog: PR middle-end/109224 * g++.dg/warn/Wmismatched-new-delete-9.C: New test. --- gcc/gimple-ssa-warn-access.cc | 18 ++++++- .../g++.dg/warn/Wmismatched-new-delete-9.C | 47 +++++++++++++++++++ gcc/testsuite/g++.dg/warn/pr109224.C | 25 ++++++++++ 3 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-9.C create mode 100644 gcc/testsuite/g++.dg/warn/pr109224.C diff --git a/gcc/gimple-ssa-warn-access.cc b/gcc/gimple-ssa-warn-access.cc index 61f9f0f3d310..e3fec5fb8e77 100644 --- a/gcc/gimple-ssa-warn-access.cc +++ b/gcc/gimple-ssa-warn-access.cc @@ -1762,7 +1762,23 @@ new_delete_mismatch_p (tree new_decl, tree delete_decl) void *np = NULL, *dp = NULL; demangle_component *ndc = cplus_demangle_v3_components (new_str, 0, &np); demangle_component *ddc = cplus_demangle_v3_components (del_str, 0, &dp); - bool mismatch = ndc && ddc && new_delete_mismatch_p (*ndc, *ddc); + + /* Sometimes, notably quite often with coroutines, 'operator new' is + templated. However, template arguments can't change whether a given + new/delete is a singleton or array one, nor what it is a member of, so + the template arguments can be safely ignored for the purposes of checking + for mismatches. */ + + auto strip_dc_template = [] (demangle_component* dc) + { + if (dc->type == DEMANGLE_COMPONENT_TEMPLATE) + dc = dc->u.s_binary.left; + return dc; + }; + + bool mismatch = ndc && ddc + && new_delete_mismatch_p (*strip_dc_template (ndc), + *strip_dc_template (ddc)); free (np); free (dp); return mismatch; diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-9.C b/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-9.C new file mode 100644 index 000000000000..fa511bbfdb4b --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-9.C @@ -0,0 +1,47 @@ +/* { dg-do compile { target c++11 } } */ +/* { dg-additional-options "-Wmismatched-new-delete" } */ +/* PR middle-end/109224 */ +/* Verify that we consider templated operator new matching with its operator + delete. */ + +#include <new> + +struct foo +{ + template<typename... Args> + void* operator new (std::size_t sz, Args&&...); + template<typename... Args> + void* operator new[] (std::size_t sz, Args&&...); + + void operator delete (void* x); + void operator delete[] (void* x); + + template<typename... Args> + void operator delete (void* x, Args&&...); + template<typename... Args> + void operator delete[] (void* x, Args&&...); +}; + +void +f () +{ + delete (new (123, true) foo); + delete[] (new (123, true) foo[123]); + + delete (new (123, true) foo[123]); + // { dg-warning "Wmismatched-new-delete" "" { target *-*-* } {.-1} } + // { dg-note "returned from" "" { target *-*-* } {.-2} } + delete[] (new (123, true) foo); + // { dg-warning "Wmismatched-new-delete" "" { target *-*-* } {.-1} } + // { dg-note "returned from" "" { target *-*-* } {.-2} } + + foo::operator delete (new (123, true) foo, 123, true); + foo::operator delete[] (new (123, true) foo[123], 123, true); + + foo::operator delete (new (123, true) foo[123], 123, true); + // { dg-warning "Wmismatched-new-delete" "" { target *-*-* } {.-1} } + // { dg-note "returned from" "" { target *-*-* } {.-2} } + foo::operator delete[] (new (123, true) foo, 123, true); + // { dg-warning "Wmismatched-new-delete" "" { target *-*-* } {.-1} } + // { dg-note "returned from" "" { target *-*-* } {.-2} } +} diff --git a/gcc/testsuite/g++.dg/warn/pr109224.C b/gcc/testsuite/g++.dg/warn/pr109224.C new file mode 100644 index 000000000000..4b6102226ffc --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/pr109224.C @@ -0,0 +1,25 @@ +// { dg-do compile { target c++20 } } +#include <coroutine> + +struct Task { + struct promise_type { + std::suspend_never initial_suspend() { return {}; } + std::suspend_never final_suspend() noexcept { return {}; } + void unhandled_exception() { throw; } + Task get_return_object() { return {}; } + void return_void() {} + + template<class I> + void* operator new(std::size_t sz, I); + + void operator delete(void* ptr, std::size_t); + }; +}; + +Task f(int) { + co_return; +} + +int main() { + f(42); +} -- 2.45.2