2
0
mirror of git://gcc.gnu.org/git/gcc.git synced 2025-04-10 08:30:31 +08:00

PR c++/98305 spurious -Wmismatched-new-delete on template instance

gcc/ChangeLog:

	PR c++/98305
	* builtins.c (new_delete_mismatch_p): New overload.
	(new_delete_mismatch_p (tree, tree)): Call it.

gcc/testsuite/ChangeLog:

	PR c++/98305
	* g++.dg/warn/Wmismatched-new-delete-3.C: New test.
This commit is contained in:
Martin Sebor 2021-01-06 13:36:18 -07:00
parent 334a295faf
commit fd64f348a6
2 changed files with 289 additions and 98 deletions
gcc

@ -78,6 +78,7 @@ along with GCC; see the file COPYING3. If not see
#include "tree-ssa-live.h"
#include "tree-outof-ssa.h"
#include "attr-fnspec.h"
#include "demangle.h"
struct target_builtins default_target_builtins;
#if SWITCHABLE_TARGET
@ -13055,6 +13056,122 @@ call_dealloc_argno (tree exp)
return UINT_MAX;
}
/* Return true if DELC doesn't refer to an operator delete that's
suitable to call with a pointer returned from the operator new
described by NEWC. */
static bool
new_delete_mismatch_p (const demangle_component &newc,
const demangle_component &delc)
{
if (newc.type != delc.type)
return true;
switch (newc.type)
{
case DEMANGLE_COMPONENT_NAME:
{
int len = newc.u.s_name.len;
const char *news = newc.u.s_name.s;
const char *dels = delc.u.s_name.s;
if (len != delc.u.s_name.len || memcmp (news, dels, len))
return true;
if (news[len] == 'n')
{
if (news[len + 1] == 'a')
return dels[len] != 'd' || dels[len + 1] != 'a';
if (news[len + 1] == 'w')
return dels[len] != 'd' || dels[len + 1] != 'l';
}
return false;
}
case DEMANGLE_COMPONENT_OPERATOR:
/* Operator mismatches are handled above. */
return false;
case DEMANGLE_COMPONENT_EXTENDED_OPERATOR:
if (newc.u.s_extended_operator.args != delc.u.s_extended_operator.args)
return true;
return new_delete_mismatch_p (*newc.u.s_extended_operator.name,
*delc.u.s_extended_operator.name);
case DEMANGLE_COMPONENT_FIXED_TYPE:
if (newc.u.s_fixed.accum != delc.u.s_fixed.accum
|| newc.u.s_fixed.sat != delc.u.s_fixed.sat)
return true;
return new_delete_mismatch_p (*newc.u.s_fixed.length,
*delc.u.s_fixed.length);
case DEMANGLE_COMPONENT_CTOR:
if (newc.u.s_ctor.kind != delc.u.s_ctor.kind)
return true;
return new_delete_mismatch_p (*newc.u.s_ctor.name,
*delc.u.s_ctor.name);
case DEMANGLE_COMPONENT_DTOR:
if (newc.u.s_dtor.kind != delc.u.s_dtor.kind)
return true;
return new_delete_mismatch_p (*newc.u.s_dtor.name,
*delc.u.s_dtor.name);
case DEMANGLE_COMPONENT_BUILTIN_TYPE:
{
/* The demangler API provides no better way to compare built-in
types except to by comparing their demangled names. */
size_t nsz, dsz;
demangle_component *pnc = const_cast<demangle_component *>(&newc);
demangle_component *pdc = const_cast<demangle_component *>(&delc);
char *nts = cplus_demangle_print (0, pnc, 16, &nsz);
char *dts = cplus_demangle_print (0, pdc, 16, &dsz);
if (!nts != !dts)
return true;
bool mismatch = strcmp (nts, dts);
free (nts);
free (dts);
return mismatch;
}
case DEMANGLE_COMPONENT_SUB_STD:
if (newc.u.s_string.len != delc.u.s_string.len)
return true;
return memcmp (newc.u.s_string.string, delc.u.s_string.string,
newc.u.s_string.len);
case DEMANGLE_COMPONENT_FUNCTION_PARAM:
case DEMANGLE_COMPONENT_TEMPLATE_PARAM:
return newc.u.s_number.number != delc.u.s_number.number;
case DEMANGLE_COMPONENT_CHARACTER:
return newc.u.s_character.character != delc.u.s_character.character;
case DEMANGLE_COMPONENT_DEFAULT_ARG:
case DEMANGLE_COMPONENT_LAMBDA:
if (newc.u.s_unary_num.num != delc.u.s_unary_num.num)
return true;
return new_delete_mismatch_p (*newc.u.s_unary_num.sub,
*delc.u.s_unary_num.sub);
default:
break;
}
if (!newc.u.s_binary.left != !delc.u.s_binary.left)
return true;
if (!newc.u.s_binary.left)
return false;
if (new_delete_mismatch_p (*newc.u.s_binary.left, *delc.u.s_binary.left)
|| !newc.u.s_binary.right != !delc.u.s_binary.right)
return true;
if (newc.u.s_binary.right)
return new_delete_mismatch_p (*newc.u.s_binary.right,
*delc.u.s_binary.right);
return false;
}
/* Return true if DELETE_DECL is an operator delete that's not suitable
to call with a pointer returned fron NEW_DECL. */
@ -13064,110 +13181,25 @@ new_delete_mismatch_p (tree new_decl, tree delete_decl)
tree new_name = DECL_ASSEMBLER_NAME (new_decl);
tree delete_name = DECL_ASSEMBLER_NAME (delete_decl);
/* valid_new_delete_pair_p() returns a conservative result. A true
result is reliable but a false result doesn't necessarily mean
the operators don't match. */
/* valid_new_delete_pair_p() returns a conservative result (currently
it only handles global operators). A true result is reliable but
a false result doesn't necessarily mean the operators don't match. */
if (valid_new_delete_pair_p (new_name, delete_name))
return false;
/* For anything not handled by valid_new_delete_pair_p() such as member
operators compare the individual demangled components of the mangled
name. */
const char *new_str = IDENTIFIER_POINTER (new_name);
const char *del_str = IDENTIFIER_POINTER (delete_name);
if (*new_str != '_')
return *new_str != *del_str;
++del_str;
if (*++new_str != 'Z')
return *new_str != *del_str;
++del_str;
if (*++new_str == 'n')
return *del_str != 'd';
if (*new_str != 'N')
return *del_str != 'N';
/* Handle user-defined member operators below. */
++new_str;
++del_str;
do
{
/* Determine if both operators are members of the same type.
If not, they don't match. */
char *new_end, *del_end;
unsigned long nlen = strtoul (new_str, &new_end, 10);
unsigned long dlen = strtoul (del_str, &del_end, 10);
if (nlen != dlen)
return true;
/* Skip past the name length. */
new_str = new_end;
del_str = del_end;
/* Skip past the names making sure each has the expected length
(it would suggest some sort of a corruption if they didn't). */
while (nlen--)
if (!*++new_end)
return true;
for (nlen = dlen; nlen--; )
if (!*++del_end)
return true;
/* The names have the expected length. Compare them. */
if (memcmp (new_str, del_str, dlen))
return true;
new_str = new_end;
del_str = del_end;
if (*new_str == 'I')
{
/* Template instantiation. */
do
{
++new_str;
++del_str;
if (*new_str == 'n')
break;
if (*new_str != *del_str)
return true;
}
while (*new_str);
}
if (*new_str == 'n')
{
if (*del_str != 'd')
return true;
++del_str;
if (*++new_str == 'w' && *del_str != 'l')
return true;
if (*new_str == 'a' && *del_str != 'a')
return true;
++new_str;
++del_str;
break;
}
} while (true);
if (*new_str != 'E')
return *del_str != *new_str;
++new_str;
++del_str;
if (*new_str != 'j' && *new_str != 'm' && *new_str != 'y')
return true;
if (*del_str != 'P' || *++del_str != 'v')
return true;
/* Ignore any remaining arguments. Since both operators are members
of the same class, mismatches in those should be detectable and
diagnosed by the front end. */
return false;
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 = new_delete_mismatch_p (*ndc, *ddc);
free (np);
free (dp);
return mismatch;
}
/* ALLOC_DECL and DEALLOC_DECL are pair of allocation and deallocation

@ -0,0 +1,159 @@
/* PR c++/98305 spurious -Wmismatched-new-delete on template instance
{ dg-do compile }
{ dg-options "-Wall" } */
typedef __SIZE_TYPE__ size_t;
void sink (void*, ...);
template <class>
struct A1
{
A1 ();
void* operator new (size_t);
void operator delete (void*);
void* operator new[] (size_t);
void operator delete[] (void*);
template <class, class>
struct A2
{
A2 ();
void* operator new (size_t);
void operator delete (void*);
void* operator new[] (size_t);
void operator delete[] (void*);
};
};
void test_a1 ()
{
{
A1<int> *p = new A1<int>();
sink (p);
delete p;
}
{
A1<long> *p = new A1<long>();
sink (p);
delete p;
}
{
void *p = new A1<int>();
A1<long> *q = (A1<long>*)p;
sink (q);
delete q; // { dg-warning "-Wmismatched-new-delete" }
}
}
void test_a2 ()
{
{
A1<int>::A2<int, int> *p = new A1<int>::A2<int, int>();
sink (p);
delete p;
}
{
A1<void>::A2<int, long> *p = new A1<void>::A2<int, long>();
sink (p);
delete p;
}
{
A1<char*>::A2<long, double> *p = new A1<char*>::A2<long, double>();
sink (p);
delete p;
}
typedef A1<char>::A2<char, char> A;
{
A *p = (A*)new A1<char>::A2<char, int>();
sink (p);
delete p; // { dg-warning "-Wmismatched-new-delete" }
}
{
A *p = (A*)new A1<char>::A2<int, char>();
sink (p);
delete p; // { dg-warning "-Wmismatched-new-delete" }
}
{
A *p = (A*)new A1<int>::A2<char, char>();
sink (p);
delete p; // { dg-warning "-Wmismatched-new-delete" }
}
}
template <class>
struct B1
{
B1 ();
void* operator new (size_t);
void operator delete (void*);
void* operator new[] (size_t);
void operator delete[] (void*);
template <class, class>
struct B2
{
B2 ();
void* operator new (size_t);
void operator delete (void*);
void* operator new[] (size_t);
void operator delete[] (void*);
};
};
void test_b_b ()
{
typedef B1<char> B1c;
typedef B1c::B2<B1c, B1c> B1cB2B1c;
{
B1cB2B1c *p = new B1cB2B1c;
sink (p);
delete p;
}
{
B1cB2B1c *p = new B1cB2B1c[1];
sink (p);
delete[] p;
}
}
void test_a_b ()
{
typedef B1<char>::B2<char, char> B;
{
B *p = (B*)new A1<char>::A2<char, int>[1];
sink (p);
delete[] p; // { dg-warning "-Wmismatched-new-delete" }
}
{
B *p = (B*)new A1<char>::A2<int, char>[2];
sink (p);
delete[] p; // { dg-warning "-Wmismatched-new-delete" }
}
{
B *p = (B*)new A1<int>::A2<char, char>[3];
sink (p);
delete[] p; // { dg-warning "-Wmismatched-new-delete" }
}
}