From fd64f348a6b40621dc2bcc743f5fdfb31ed0894c Mon Sep 17 00:00:00 2001 From: Martin Sebor Date: Wed, 6 Jan 2021 13:36:18 -0700 Subject: [PATCH] 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. --- gcc/builtins.c | 210 ++++++++++-------- .../g++.dg/warn/Wmismatched-new-delete-3.C | 159 +++++++++++++ 2 files changed, 280 insertions(+), 89 deletions(-) create mode 100644 gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-3.C diff --git a/gcc/builtins.c b/gcc/builtins.c index e7709420b16..02e7815aee5 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -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,121 +13056,152 @@ call_dealloc_argno (tree exp) return UINT_MAX; } -/* Return true if DELETE_DECL is an operator delete that's not suitable - to call with a pointer returned fron NEW_DECL. */ +/* 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 (tree new_decl, tree delete_decl) +new_delete_mismatch_p (const demangle_component &newc, + const demangle_component &delc) { - 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. */ - if (valid_new_delete_pair_p (new_name, delete_name)) - return false; - - const char *new_str = IDENTIFIER_POINTER (new_name); - const char *del_str = IDENTIFIER_POINTER (delete_name); - - if (*new_str != '_') - return *new_str != *del_str; + if (newc.type != delc.type) + return true; - ++del_str; - if (*++new_str != 'Z') - return *new_str != *del_str; + 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; - ++del_str; - if (*++new_str == 'n') - return *del_str != 'd'; + 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; + } - if (*new_str != 'N') - return *del_str != 'N'; + case DEMANGLE_COMPONENT_OPERATOR: + /* Operator mismatches are handled above. */ + return false; - /* Handle user-defined member operators below. */ - ++new_str; - ++del_str; + 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); - 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) + 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); - /* Skip past the name length. */ - new_str = new_end; - del_str = del_end; + 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); - /* 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; + 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); - for (nlen = dlen; nlen--; ) - if (!*++del_end) + 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(&newc); + demangle_component *pdc = const_cast(&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; + } - /* The names have the expected length. Compare them. */ - if (memcmp (new_str, del_str, dlen)) + 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); - new_str = new_end; - del_str = del_end; - - if (*new_str == 'I') - { - /* Template instantiation. */ - do - { - ++new_str; - ++del_str; + case DEMANGLE_COMPONENT_FUNCTION_PARAM: + case DEMANGLE_COMPONENT_TEMPLATE_PARAM: + return newc.u.s_number.number != delc.u.s_number.number; - if (*new_str == 'n') - break; - if (*new_str != *del_str) - return true; - } - while (*new_str); - } + case DEMANGLE_COMPONENT_CHARACTER: + return newc.u.s_character.character != delc.u.s_character.character; - if (*new_str == 'n') - { - if (*del_str != 'd') - return true; + 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; + } - ++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 (!newc.u.s_binary.left != !delc.u.s_binary.left) + return true; - if (*new_str != 'E') - return *del_str != *new_str; + if (!newc.u.s_binary.left) + return false; - ++new_str; - ++del_str; - if (*new_str != 'j' && *new_str != 'm' && *new_str != 'y') - return true; - if (*del_str != 'P' || *++del_str != 'v') + 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; - /* 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. */ + 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. */ + +static bool +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 (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); + + 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 functions. Return true if the latter is suitable to deallocate objects allocated by calls to the former. */ diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-3.C b/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-3.C new file mode 100644 index 00000000000..25e13461fe7 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-3.C @@ -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 +struct A1 +{ + A1 (); + + void* operator new (size_t); + void operator delete (void*); + + void* operator new[] (size_t); + void operator delete[] (void*); + + template + 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 *p = new A1(); + sink (p); + delete p; + } + + { + A1 *p = new A1(); + sink (p); + delete p; + } + + { + void *p = new A1(); + A1 *q = (A1*)p; + sink (q); + delete q; // { dg-warning "-Wmismatched-new-delete" } + } +} + +void test_a2 () +{ + { + A1::A2 *p = new A1::A2(); + sink (p); + delete p; + } + { + A1::A2 *p = new A1::A2(); + sink (p); + delete p; + } + { + A1::A2 *p = new A1::A2(); + sink (p); + delete p; + } + + typedef A1::A2 A; + { + A *p = (A*)new A1::A2(); + sink (p); + delete p; // { dg-warning "-Wmismatched-new-delete" } + } + + { + A *p = (A*)new A1::A2(); + sink (p); + delete p; // { dg-warning "-Wmismatched-new-delete" } + } + + { + A *p = (A*)new A1::A2(); + sink (p); + delete p; // { dg-warning "-Wmismatched-new-delete" } + } +} + + +template +struct B1 +{ + B1 (); + + void* operator new (size_t); + void operator delete (void*); + + void* operator new[] (size_t); + void operator delete[] (void*); + + template + 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 B1c; + typedef B1c::B2 B1cB2B1c; + + { + B1cB2B1c *p = new B1cB2B1c; + sink (p); + delete p; + } + + { + B1cB2B1c *p = new B1cB2B1c[1]; + sink (p); + delete[] p; + } +} + + +void test_a_b () +{ + typedef B1::B2 B; + + { + B *p = (B*)new A1::A2[1]; + sink (p); + delete[] p; // { dg-warning "-Wmismatched-new-delete" } + } + + { + B *p = (B*)new A1::A2[2]; + sink (p); + delete[] p; // { dg-warning "-Wmismatched-new-delete" } + } + + { + B *p = (B*)new A1::A2[3]; + sink (p); + delete[] p; // { dg-warning "-Wmismatched-new-delete" } + } +} -- 2.30.2