diff options
-rw-r--r-- | gcc/gimple-ssa-warn-access.cc | 9 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-6.C | 158 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-7.C | 91 | ||||
-rw-r--r-- | gcc/tree.c | 28 | ||||
-rw-r--r-- | gcc/tree.h | 4 |
5 files changed, 284 insertions, 6 deletions
diff --git a/gcc/gimple-ssa-warn-access.cc b/gcc/gimple-ssa-warn-access.cc index a4559dd..93f43b7 100644 --- a/gcc/gimple-ssa-warn-access.cc +++ b/gcc/gimple-ssa-warn-access.cc @@ -1782,9 +1782,14 @@ new_delete_mismatch_p (tree new_decl, tree 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)) + a false result doesn't necessarily mean the operators don't match + unless CERTAIN is set. */ + bool certain; + if (valid_new_delete_pair_p (new_name, delete_name, &certain)) return false; + /* CERTAIN is set when the negative result is certain. */ + if (certain) + return true; /* For anything not handled by valid_new_delete_pair_p() such as member operators compare the individual demangled components of the mangled diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-6.C b/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-6.C new file mode 100644 index 0000000..e19d000 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-6.C @@ -0,0 +1,158 @@ +/* PR middle-end/101791 - missing warning on a mismatch between scalar + and array forms of new and delete + { dg-do compile } + { dg-options "-Wall" } */ + +typedef __SIZE_TYPE__ size_t; + +namespace std +{ +#if __cplusplus >= 201703L +enum class align_val_t: size_t { }; +#else +enum align_val_t { }; +#endif + +struct nothrow_t { }; +const nothrow_t nothrow = { }; + +} + +void* operator new (size_t); +void* operator new (size_t, std::align_val_t); +void* operator new (size_t, std::nothrow_t) throw (); +void* operator new (size_t, std::align_val_t, std::nothrow_t) throw (); + +void* operator new[] (size_t); +void* operator new[] (size_t, std::align_val_t); +void* operator new[] (size_t, std::nothrow_t) throw (); +void* operator new[] (size_t, std::align_val_t, std::nothrow_t) throw (); + +void operator delete (void*); +void operator delete (void*, size_t); +void operator delete (void*, std::align_val_t); +void operator delete (void*, size_t, std::align_val_t); +void operator delete (void*, std::nothrow_t) throw (); +void operator delete (void*, std::align_val_t, std::nothrow_t) throw (); + +void operator delete[] (void*); +void operator delete[] (void*, size_t); +void operator delete[] (void*, std::align_val_t); +void operator delete[] (void*, size_t, std::align_val_t); +void operator delete[] (void*, std::nothrow_t) throw (); +void operator delete[] (void*, std::align_val_t, std::nothrow_t) throw (); + + +void sink (void*, ...); + + +void nowarn_scalar_scalar () +{ + { + int *p = new int; + sink (p); + delete p; + } + + { + int *p = new (std::align_val_t (8)) int; + sink (p); + delete p; + } + + { + int *p = new (std::nothrow) int; + sink (p); + delete p; + } + + { + int *p = new (std::align_val_t (8), std::nothrow) int; + sink (p); + delete p; + } +} + +void nowarn_array_array () +{ + { + int *p = new int[__LINE__]; + sink (p); + delete[] p; + } + + { + int *p = new (std::align_val_t (8)) int[__LINE__]; + sink (p); + delete[] p; + } + + { + int *p = new (std::nothrow) int[__LINE__]; + sink (p); + delete[] p; + } + + { + int *p = new (std::align_val_t (8), std::nothrow) int[__LINE__]; + sink (p); + delete[] p; + } +} + + + +void nowarn_scalar_array () +{ + { + int *p = new int; // { dg-message "returned from" } + sink (p); + delete[] p; // { dg-warning "\\\[-Wmismatched-new-delete" } + } + + { + int *p = new (std::align_val_t (8)) int; + sink (p); + delete[] p; // { dg-warning "\\\[-Wmismatched-new-delete" } + } + + { + int *p = new (std::nothrow) int; + sink (p); + delete[] p; // { dg-warning "\\\[-Wmismatched-new-delete" } + } + + { + int *p = new (std::align_val_t (8), std::nothrow) int; + sink (p); + delete[] p; // { dg-warning "\\\[-Wmismatched-new-delete" } + } +} + + +void nowarn_array_scalar () +{ + { + int *p = new int[__LINE__]; + sink (p); + delete p; // { dg-warning "\\\[-Wmismatched-new-delete" } + } + + { + int *p = new (std::align_val_t (8)) int[__LINE__]; + sink (p); + delete p; // { dg-warning "\\\[-Wmismatched-new-delete" } + } + + { + int *p = new (std::nothrow) int[__LINE__]; + sink (p); + delete p; // { dg-warning "\\\[-Wmismatched-new-delete" } + } + + { + int *p = new (std::align_val_t (8), std::nothrow) int[__LINE__]; + sink (p); + delete p; // { dg-warning "\\\[-Wmismatched-new-delete" } + } +} diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-7.C b/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-7.C new file mode 100644 index 0000000..67a5354 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-7.C @@ -0,0 +1,91 @@ +/* PR middle-end/101791 - missing warning on a mismatch between scalar + and array forms of new and delete + Verify that likely safe calls to technically mismatched member operator + new and delete are not diagnosed. This test might need to be adjusted + if it turns out the assumptions GCC makes are overly conservative. + { dg-do compile } + { dg-options "-Wall" } */ + +typedef __SIZE_TYPE__ size_t; + +namespace std +{ +#if __cplusplus >= 201703L +enum class align_val_t: size_t { }; +#else +enum align_val_t { }; +#endif + +struct nothrow_t { }; +const nothrow_t nothrow = { }; + +} + +void sink (void*, ...); + +struct X { } x; +struct Y { } y; + +struct A +{ + void* operator new (size_t); + void* operator new (size_t, std::align_val_t); + + void* operator new (size_t, X); + void* operator new (size_t, Y); + + void* operator new (size_t, std::align_val_t, X); + void* operator new (size_t, std::nothrow_t, Y); + + /* A single operator delete callable on the result of calls to any + of the operator new overloads above (this may be too optimistic). */ + void operator delete (void*); +}; + +A* nowarn_align () +{ + /* The following are likely okay given A's definition above but would + not be if A also defined an align_val_t overload of operator delete. */ + A *p = new (std::align_val_t (8)) A; + delete p; + + return new (std::align_val_t (16)) A; +} + +A* nowarn_X () +{ + /* The following are also likely okay given A's definition above but + also would not be if A also defined an overload of operator delete + for X. */ + A *p = new (x) A; + delete p; + return new (x) A; +} + +A* nowarn_Y () +{ + // Same as above. + A *p = new (y) A; + delete p; + return new (y) A; +} + + +A* nowarn_align_X () +{ + // Same as above. + A *p = new (std::align_val_t (32), x) A; + delete p; + + return new (std::align_val_t (64), x) A; +} + + +A* nowarn_nothrow_Y () +{ + // Same as above. + A *p = new (std::nothrow, y) A; + delete p; + return new (std::nothrow, y) A; +} + @@ -14314,17 +14314,28 @@ verify_type_context (location_t loc, type_context_kind context, || targetm.verify_type_context (loc, context, type, silent_p)); } -/* Return that NEW_ASM and DELETE_ASM name a valid pair of new and - delete operators. */ +/* 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 + do not. */ bool -valid_new_delete_pair_p (tree new_asm, tree delete_asm) +valid_new_delete_pair_p (tree new_asm, tree delete_asm, + bool *pcertain /* = NULL */) { + bool certain; + if (!pcertain) + pcertain = &certain; + const char *new_name = IDENTIFIER_POINTER (new_asm); const char *delete_name = IDENTIFIER_POINTER (delete_asm); unsigned int new_len = IDENTIFIER_LENGTH (new_asm); unsigned int delete_len = IDENTIFIER_LENGTH (delete_asm); + /* The following failures are due to invalid names so they're not + considered certain mismatches. */ + *pcertain = false; + if (new_len < 5 || delete_len < 6) return false; if (new_name[0] == '_') @@ -14337,11 +14348,19 @@ valid_new_delete_pair_p (tree new_asm, tree delete_asm) ++delete_name, --delete_len; if (new_len < 4 || delete_len < 5) return false; + + /* The following failures are due to names of user-defined operators + so they're also not considered certain mismatches. */ + /* *_len is now just the length after initial underscores. */ if (new_name[0] != 'Z' || new_name[1] != 'n') return false; if (delete_name[0] != 'Z' || delete_name[1] != 'd') return false; + + /* The following failures are certain mismatches. */ + *pcertain = true; + /* _Znw must match _Zdl, _Zna must match _Zda. */ if ((new_name[2] != 'w' || delete_name[2] != 'l') && (new_name[2] != 'a' || delete_name[2] != 'a')) @@ -14380,6 +14399,9 @@ valid_new_delete_pair_p (tree new_asm, tree delete_asm) && !memcmp (delete_name + 5, "St11align_val_tRKSt9nothrow_t", 29)) return true; } + + /* The negative result is conservative. */ + *pcertain = false; return false; } @@ -5406,7 +5406,9 @@ extern bool gimple_canonical_types_compatible_p (const_tree, const_tree, extern bool type_with_interoperable_signedness (const_tree); extern bitmap get_nonnull_args (const_tree); extern int get_range_pos_neg (tree); -extern bool valid_new_delete_pair_p (tree, tree); + +/* Return true for a valid pair of new and delete operators. */ +extern bool valid_new_delete_pair_p (tree, tree, bool * = NULL); /* Return simplified tree code of type that is used for canonical type merging. */ |