https://gcc.gnu.org/g:7d83a32aacd6005c0c038c74562e35d70f6a77a8
commit r15-6409-g7d83a32aacd6005c0c038c74562e35d70f6a77a8 Author: Arsen Arsenović <ar...@aarsen.me> Date: Thu Aug 1 17:38:15 2024 +0200 warn-access: ignore template parameters when matching operator new/delete [PR109224] 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. Diff: --- 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(-) diff --git a/gcc/gimple-ssa-warn-access.cc b/gcc/gimple-ssa-warn-access.cc index 820f23825254..1eb9d2ee4bac 100644 --- a/gcc/gimple-ssa-warn-access.cc +++ b/gcc/gimple-ssa-warn-access.cc @@ -1765,7 +1765,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..d431f4049e87 --- /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 (foo::operator new (1, 123, true), 123, true); + foo::operator delete[] (foo::operator new[] (123, 123, true), 123, true); + + foo::operator delete (foo::operator new[] (123, 123, true), 123, true); + // { dg-warning "Wmismatched-new-delete" "" { target *-*-* } {.-1} } + // { dg-note "returned from" "" { target *-*-* } {.-2} } + foo::operator delete[] (foo::operator new (1, 123, true), 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); +}