https://gcc.gnu.org/g:9bf2a58cb76531db78391fdc04dc5280dd3a4def
commit r16-6854-g9bf2a58cb76531db78391fdc04dc5280dd3a4def Author: Jakub Jelinek <[email protected]> Date: Sat Jan 17 14:37:30 2026 +0100 tree: Handle ::operator {new,delete} function templates as uncertain matches [PR123513] We have for some reason two different ways to check for matching ::operator new vs. ::operator delete kind. One is a dumb one in tree.cc (valid_new_delete_pair_p) and another one is in gimple-ssa-warn-access.cc (new_delete_mismatch_p). The former is used both in the latter and in optimizations, the latter only for warnings. The former just handles the easy cases, global operator new and the latter can handle everything as it uses the demangler. The former has essentially a tri-state return even when it has just bool return type, it has another bool * optional argument, so can return true for this is definitely ok, false with false with this might be not matching and false with true for this definitely doesn't match. false with false is returned e.g. for the class scope operator new/delete, where we definitely need the demangler to figure stuff out. false with true is returned for mismatches which are guaranteed, e.g. when one mangled name starts with _Znw and the other with _Zda, one is ::operator new and the other is ::operator delete[]. valid_new_delete_pair_p expects that after the _Znw/_Zna/_Zdl/_Zda prefix (or two _ at the start instead of one) it sees [jmy] for the size_t argument resp. Pv for void* for delete, for delete then optionally the same [jmy] for sized deallocation and optionally RKSt9nothrow_t after it for nothrow versions or also something with St11align_val_t. If it has some extra arguments after it, it also returns false/false. The following testcase shows another case where I'm afraid we need to return the maybe mismatch - when the global operators are function templates. _ZnwILm1024EEPvmR13BumpAllocatorIXT_EE _ZdlILm1024EEvPvR13BumpAllocatorIXT_EE where the Ilm1024EE here mean <1024ul> and Pv after it means function return type void *. As valid_new_delete_pair_p needs to find the m after it, it would need to know everything about what can appear in between I and E for the template arguments (which is a lot) and also be able to skip over mangling of arbitrary function return types (though perhaps it could hardcode those Pv vs. v cases for those). So, the following patch just returns false/false instead of false/true if known _Z{nw,na,dl,da} is followed by I, i.e. if it is a function template. For optimizations it makes no difference, those care just about the return value and not on *pcertain, and for the warning it means it will use the demangler which will figure stuff hopefully right. 2026-01-17 Jakub Jelinek <[email protected]> PR tree-optimization/123513 * tree.cc (valid_new_delete_pair_p): If new_name[3] or delete_name[3] is 'I', return false with *pcertain set to false rather than true. * g++.dg/warn/Wmismatched-new-delete-10.C: New test. Diff: --- .../g++.dg/warn/Wmismatched-new-delete-10.C | 25 ++++++++++++++++++++++ gcc/tree.cc | 7 ++++++ 2 files changed, 32 insertions(+) diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-10.C b/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-10.C new file mode 100644 index 000000000000..0d8544f19448 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-10.C @@ -0,0 +1,25 @@ +// PR tree-optimization/123513 +// { dg-do compile } +// { dg-options "-Wmismatched-new-delete" } + +typedef __SIZE_TYPE__ size_t; + +template <size_t N> +class A {}; +struct B { B (); }; + +template <size_t N> +void *operator new (size_t, A <N> &); + +template <size_t N> +void operator delete (void *, A <N> &); + +void +foo (B *, A <1024> &); + +void +bar () +{ + A <1024> a; + foo (new (a) B (), a); // { dg-bogus "called on pointer returned from a mismatched allocation function" } +} diff --git a/gcc/tree.cc b/gcc/tree.cc index faa0aa9f1af8..ca713cec4d6a 100644 --- a/gcc/tree.cc +++ b/gcc/tree.cc @@ -15318,6 +15318,13 @@ valid_new_delete_pair_p (tree new_asm, tree delete_asm, if ((new_name[2] != 'w' || delete_name[2] != 'l') && (new_name[2] != 'a' || delete_name[2] != 'a')) return false; + if (new_name[3] == 'I' || delete_name[3] == 'I') + { + /* When ::operator new or ::operator delete are function templates, + return uncertain mismatch, we need demangler in that case. */ + *pcertain = false; + return false; + } /* 'j', 'm' and 'y' correspond to size_t. */ if (new_name[3] != 'j' && new_name[3] != 'm' && new_name[3] != 'y') return false;
