> Ah, good point. But then we don't need any of the if (check_new) block > in this case, so we could clear check_new if we didn't add any > COMPOUND_EXPR to rval, or check rval != data_addr along with check_new?
Chose `rval != data_addr' alongside `check_new' rather than clearing check_new, because the two blocks serve independent purposes and each has its own condition. The null-check COND_EXPR (first block) must only be built when `check_new' and `rval != data_addr': init/cookie/clobber code has been wrapped into rval, and that code must not execute if alloc_node is null. The alloc_node anchor for -Wmismatched-new-delete (second block) must run when `check_new' and `!cookie_size': without a cookie, data_addr and alloc_node may be distinct tree nodes after fold_convert, and alloc_node must be appended explicitly to remain visible to the diagnostic -- independently of whether rval equals data_addr. Clearing check_new when rval == data_addr would suppress both blocks, but only suppressing the first is correct. Also took the opportunity to move nothrow and check_new into a local scope as const bool, introduced may_return_null to make the two contributing conditions explicit, and removed an old orphaned comment (a fragment of a C++ standard quote that lost its first sentence at some point). The patch is functionally equivalent to the previous version. The refactoring makes the logic clearer in my opinion, but happy to adjust if you prefer otherwise. > Please also add a "PR c++/125422" line at the top of the ChangeLog > entries. 'git gcc-verify' would complain about that. Fixed. Here is the updated patch: >From aaa5c3401af8a35c4f2d666e1046a1f49167c013 Mon Sep 17 00:00:00 2001 From: Joe Natter <[email protected]> Date: Thu, 21 May 2026 20:20:16 +0200 Subject: [PATCH] c++: Fix spurious -Wduplicated-branches for new (nothrow) T[n] [PR125422] When allocating an array of a trivial type with new (nothrow), build_new_1() may emit a compiler-generated COND_EXPR of the form (alloc_node != nullptr) ? rval : alloc_node For trivial arrays without constructors, destructors, or array cookies, rval remains equal to data_addr and is therefore equivalent to alloc_node. This causes both branches of the generated COND_EXPR to be identical, triggering -Wduplicated-branches on user code. Avoid building the null-check COND_EXPR when rval == data_addr. gcc/cp/ChangeLog: PR c++/125422 * init.cc (build_new_1): Avoid building a null-check COND_EXPR when rval == data_addr. gcc/testsuite/ChangeLog: PR c++/125422 * g++.dg/warn/Wduplicated-branches10.C: New test. Signed-off-by: Joe Natter <[email protected]> --- gcc/cp/init.cc | 34 ++++++++----------- .../g++.dg/warn/Wduplicated-branches10.C | 15 ++++++++ 2 files changed, 29 insertions(+), 20 deletions(-) create mode 100644 gcc/testsuite/g++.dg/warn/Wduplicated-branches10.C diff --git a/gcc/cp/init.cc b/gcc/cp/init.cc index d0929c35bfc..8bbc4a5ed5f 100644 --- a/gcc/cp/init.cc +++ b/gcc/cp/init.cc @@ -3111,7 +3111,6 @@ build_new_1 (vec<tree, va_gc> **placement, tree type, tree nelts, tree alloc_node; tree alloc_fn; tree cookie_expr, init_expr; - int nothrow, check_new; /* If non-NULL, the number of extra bytes to allocate at the beginning of the storage allocated for an array-new expression in order to store the number of elements. */ @@ -3602,19 +3601,6 @@ build_new_1 (vec<tree, va_gc> **placement, tree type, tree nelts, alloc_expr); } - /* unless an allocation function is declared with an empty excep- - tion-specification (_except.spec_), throw(), it indicates failure to - allocate storage by throwing a bad_alloc exception (clause _except_, - _lib.bad.alloc_); it returns a non-null pointer otherwise If the allo- - cation function is declared with an empty exception-specification, - throw(), it returns null to indicate failure to allocate storage and a - non-null pointer otherwise. - - So check for a null exception spec on the op new we just called. */ - - nothrow = TYPE_NOTHROW_P (TREE_TYPE (alloc_fn)); - check_new = flag_check_new || (nothrow && !std_placement); - if (cookie_size) { tree cookie; @@ -3959,7 +3945,15 @@ build_new_1 (vec<tree, va_gc> **placement, tree type, tree nelts, rval = TARGET_EXPR_INITIAL (alloc_expr); else { - if (check_new) + const bool nothrow = TYPE_NOTHROW_P (TREE_TYPE (alloc_fn)); + const bool may_return_null = nothrow && !std_placement; + const bool check_new = flag_check_new || may_return_null; + + /* Even if check_new (including flag_check_new) is set, skip the + null-check when rval == data_addr: the resulting conditional + would be "alloc_node != nullptr ? alloc_node : alloc_node", + triggering -Wduplicated-branches. */ + if (check_new && (rval != data_addr)) { tree ifexp = cp_build_binary_op (input_location, NE_EXPR, alloc_node, @@ -3967,12 +3961,12 @@ build_new_1 (vec<tree, va_gc> **placement, tree type, tree nelts, complain); rval = build_conditional_expr (input_location, ifexp, rval, alloc_node, complain); - /* If there's no offset between data_addr and alloc_node, append it - to help -Wmismatched-new-delete at -O0. */ - if (!cookie_size) - rval = build2 (COMPOUND_EXPR, TREE_TYPE (alloc_node), - rval, alloc_node); } + /* If there's no offset between data_addr and alloc_node, append it + to help -Wmismatched-new-delete at -O0. */ + if (check_new && !cookie_size) + rval = build2 (COMPOUND_EXPR, TREE_TYPE (alloc_node), + rval, alloc_node); /* Perform the allocation before anything else, so that ALLOC_NODE has been initialized before we start using it. */ diff --git a/gcc/testsuite/g++.dg/warn/Wduplicated-branches10.C b/gcc/testsuite/g++.dg/warn/Wduplicated-branches10.C new file mode 100644 index 00000000000..0ebccd7d6fe --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Wduplicated-branches10.C @@ -0,0 +1,15 @@ +// PR c++/125422 +// { dg-do compile } +// { dg-options "-Wduplicated-branches" } + +// new (nothrow) T[n] with a runtime variable must not trigger +// -Wduplicated-branches (false positive from compiler-generated COND_EXPR). + +#include <new> + +void +test_nothrow (int sz) +{ + char *p = new (std::nothrow) char[sz]; + delete[] p; +} -- 2.34.1
