From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 1930) id B2B41385E019; Sat, 14 Aug 2021 19:27:56 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org B2B41385E019 MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset="utf-8" From: Martin Sebor To: gcc-cvs@gcc.gnu.org Subject: [gcc r12-2908] Diagnose mismatches between array and scalar new and delete [PR101791]. X-Act-Checkin: gcc X-Git-Author: Martin Sebor X-Git-Refname: refs/heads/master X-Git-Oldrev: 240f07805db27cfc746276039c5edccb4c031070 X-Git-Newrev: 96194a07bdbc57dd9733892a791d87dbe25f0802 Message-Id: <20210814192756.B2B41385E019@sourceware.org> Date: Sat, 14 Aug 2021 19:27:56 +0000 (GMT) X-BeenThere: gcc-cvs@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-cvs mailing list List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 14 Aug 2021 19:27:56 -0000 https://gcc.gnu.org/g:96194a07bdbc57dd9733892a791d87dbe25f0802 commit r12-2908-g96194a07bdbc57dd9733892a791d87dbe25f0802 Author: Martin Sebor Date: Sat Aug 14 13:25:41 2021 -0600 Diagnose mismatches between array and scalar new and delete [PR101791]. Resolves: PR middle-end/101791 - missing warning on a mismatch between scalar and array forms of new and delete gcc/ChangeLog: PR middle-end/101791 * gimple-ssa-warn-access.cc (new_delete_mismatch_p): Use new argument to valid_new_delete_pair_p. * tree.c (valid_new_delete_pair_p): Add argument. * tree.h (valid_new_delete_pair_p): Same. gcc/testsuite/ChangeLog: PR middle-end/101791 * g++.dg/warn/Wmismatched-new-delete-6.C: New test. * g++.dg/warn/Wmismatched-new-delete-7.C: New test. Diff: --- gcc/gimple-ssa-warn-access.cc | 9 +- .../g++.dg/warn/Wmismatched-new-delete-6.C | 158 +++++++++++++++++++++ .../g++.dg/warn/Wmismatched-new-delete-7.C | 91 ++++++++++++ gcc/tree.c | 28 +++- 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 a4559ddb5b3..93f43b711e2 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 00000000000..e19d00058b3 --- /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 00000000000..67a5354b91d --- /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; +} + diff --git a/gcc/tree.c b/gcc/tree.c index a0ff79468eb..6ec8a9738c8 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -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; } diff --git a/gcc/tree.h b/gcc/tree.h index c3f302a406a..a26500dca61 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -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. */