On 1/13/26 4:54 PM, Jakub Jelinek wrote:
Hi!
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.
Preexisting issue, but I'm not sure why this function is trying so hard;
why do we need to do more than check that the _Zn? and _Zd? match?
There doesn't seem to be a definition of what a "valid" pair is.
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.
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
2026-01-13 Jakub Jelinek <[email protected]>
PR tree-optimization/123513
* tree.cc (valid_new_delete_pair_p): Change nonnull to false in
function comment. 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.
--- gcc/tree.cc.jj 2026-01-12 09:58:18.748429069 +0100
+++ gcc/tree.cc 2026-01-12 19:21:33.465016103 +0100
@@ -15269,7 +15269,7 @@ verify_type_context (location_t loc, typ
/* Return true if NEW_ASM and DELETE_ASM name a valid pair of new and
delete operators. Return false if they may or may not name such
- a pair and, when nonnull, set *PCERTAIN to true if they certainly
+ a pair and, when false, set *PCERTAIN to true if they certainly
This change seems wrong; if pcertain is null we change a local variable
instead of anything from the function argument.
do not. */
bool
@@ -15318,6 +15318,13 @@ valid_new_delete_pair_p (tree new_asm, t
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;
--- gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-10.C.jj 2026-01-12
19:29:55.725562439 +0100
+++ gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-10.C 2026-01-12
19:29:17.085212804 +0100
@@ -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" }
+}
Jakub