From d77b548fb647d52817d0c44d45bb817d166b7a19 Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Wed, 8 Oct 2025 16:09:49 +0100 Subject: [PATCH] c++: clobber non-placement new And also add the clobber for non-placement new. For now let's limit the clobber of an array with non-constant bound to placement new in constant evaluation, where we need it to set the active member of a union. And catch some additional cases of there being no actual data to clobber. This changes the diagnostics in a couple of analyzer tests, but the new diagnostics are also valid. It also adds some -Wuninitialized warnings which seem like an improvement; the lines that now warn about an uninitialized vptr are correct, since trying to assign to a member of a virtual base reads the vptr of an object that was never created. gcc/cp/ChangeLog: * init.cc (build_new_1): Also clobber for non-placement new. Only loop clobber in constexpr. * expr.cc (wrap_with_if_consteval): New. * cp-tree.h (wrap_with_if_consteval): Declare. gcc/testsuite/ChangeLog: * g++.dg/analyzer/new-2.C: Adjust diags. * g++.dg/analyzer/noexcept-new.C: Adjust diags. * g++.dg/warn/Warray-bounds-23.C: Add warnings. * g++.dg/warn/Warray-bounds-24.C: Add warnings. * g++.dg/cpp26/constexpr-new4a.C: New test. --- gcc/cp/cp-tree.h | 1 + gcc/cp/expr.cc | 16 ++++++++ gcc/cp/init.cc | 39 +++++++++++++------- gcc/testsuite/g++.dg/analyzer/new-2.C | 4 +- gcc/testsuite/g++.dg/analyzer/noexcept-new.C | 12 +++--- gcc/testsuite/g++.dg/cpp26/constexpr-new4a.C | 21 +++++++++++ gcc/testsuite/g++.dg/warn/Warray-bounds-23.C | 6 +-- gcc/testsuite/g++.dg/warn/Warray-bounds-24.C | 6 +-- 8 files changed, 77 insertions(+), 28 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp26/constexpr-new4a.C diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 5f8dfad7bf42..7298d3b81bd7 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -7561,6 +7561,7 @@ extern tree mark_lvalue_use_nonread (tree); extern tree mark_type_use (tree); extern tree mark_discarded_use (tree); extern void mark_exp_read (tree); +extern tree wrap_with_if_consteval (tree); /* friend.cc */ extern int is_friend (tree, tree); diff --git a/gcc/cp/expr.cc b/gcc/cp/expr.cc index 32dc3eee78f9..f2e99397acac 100644 --- a/gcc/cp/expr.cc +++ b/gcc/cp/expr.cc @@ -430,3 +430,19 @@ fold_for_warn (tree x) return c_fully_fold (x, /*for_init*/false, /*maybe_constp*/NULL); } + +/* Make EXPR only execute during constant evaluation by wrapping it in a + statement-expression containing 'if consteval'. */ + +tree +wrap_with_if_consteval (tree expr) +{ + tree stmtex = begin_stmt_expr (); + tree ifcev = begin_if_stmt (); + IF_STMT_CONSTEVAL_P (ifcev) = true; + finish_if_stmt_cond (boolean_false_node, ifcev); + finish_expr_stmt (expr); + finish_then_clause (ifcev); + finish_if_stmt (ifcev); + return finish_stmt_expr (stmtex, /*no scope*/true); +} diff --git a/gcc/cp/init.cc b/gcc/cp/init.cc index 912298728acd..1b7f3e6b41c9 100644 --- a/gcc/cp/init.cc +++ b/gcc/cp/init.cc @@ -3557,15 +3557,17 @@ build_new_1 (vec **placement, tree type, tree nelts, alloc_expr = maybe_wrap_new_for_constexpr (alloc_expr, type, cookie_size); - bool std_placement = std_placement_new_fn_p (alloc_fn); + const bool std_placement = std_placement_new_fn_p (alloc_fn); - /* For std placement new, clobber the object if the constructor won't do it - in start_preparsed_function. This is most important for activating an - array in a union (c++/121068), but should also help the optimizers. */ + /* Clobber the object now that the constructor won't do it in + start_preparsed_function. This is most important for activating an array + in a union (c++/121068), but should also help the optimizers. */ const bool do_clobber - = (std_placement && flag_lifetime_dse > 1 + = (flag_lifetime_dse > 1 && !processing_template_decl && !is_empty_type (elt_type) + && !integer_zerop (TYPE_SIZE (type)) + && (!outer_nelts || !integer_zerop (cst_outer_nelts)) && (!*init || CLASS_TYPE_P (elt_type))); /* In the simple case, we can stop now. */ @@ -3665,15 +3667,24 @@ build_new_1 (vec **placement, tree type, tree nelts, if (array_p && TREE_CODE (cst_outer_nelts) != INTEGER_CST) { /* Clobber each element rather than the array at once. */ - tree clobber = build_clobber (elt_type, CLOBBER_OBJECT_BEGIN); - CONSTRUCTOR_IS_DIRECT_INIT (clobber) = true; - tree maxindex = cp_build_binary_op (input_location, - MINUS_EXPR, outer_nelts, - integer_one_node, - complain); - clobber_expr = build_vec_init (data_addr, maxindex, clobber, - /*valinit*/false, /*from_arr*/0, - complain, nullptr); + /* But for now, limit a clobber loop to placement new during + constant-evaluation, as cddce1 thinks it might be infinite, leading + to bogus warnings on Wstringop-overflow-4.C (2025-09-30). We + need it in constexpr for constexpr-new4a.C. */ + if (std_placement && current_function_decl + && maybe_constexpr_fn (current_function_decl)) + { + tree clobber = build_clobber (elt_type, CLOBBER_OBJECT_BEGIN); + CONSTRUCTOR_IS_DIRECT_INIT (clobber) = true; + tree maxindex = cp_build_binary_op (input_location, + MINUS_EXPR, outer_nelts, + integer_one_node, + complain); + clobber_expr = build_vec_init (data_addr, maxindex, clobber, + /*valinit*/false, /*from_arr*/0, + complain, nullptr); + clobber_expr = wrap_with_if_consteval (clobber_expr); + } } else { diff --git a/gcc/testsuite/g++.dg/analyzer/new-2.C b/gcc/testsuite/g++.dg/analyzer/new-2.C index 391d159a53ae..9337569e60bc 100644 --- a/gcc/testsuite/g++.dg/analyzer/new-2.C +++ b/gcc/testsuite/g++.dg/analyzer/new-2.C @@ -59,9 +59,9 @@ void test_nonthrowing () int z = *y + 2; /* { dg-warning "dereference of NULL 'y'" } */ /* { dg-bogus "use of uninitialized value '\\*y'" "" { target *-*-* } .-1 } */ - z = *x + 4; /* { dg-warning "dereference of possibly-NULL 'x'" } */ + z = *x + 4; /* { dg-warning "dereference of NULL 'x'" } */ /* { dg-warning "use of uninitialized value '\\*x'" "" { target *-*-* } .-1 } */ - z = arr[0] + 4; /* { dg-warning "dereference of possibly-NULL 'arr'" } */ + z = arr[0] + 4; /* { dg-warning "dereference of NULL 'arr'" } */ /* { dg-warning "use of uninitialized value '\\*arr'" "" { target *-*-* } .-1 } */ delete y; diff --git a/gcc/testsuite/g++.dg/analyzer/noexcept-new.C b/gcc/testsuite/g++.dg/analyzer/noexcept-new.C index f4bb4956d26d..11225431b57d 100644 --- a/gcc/testsuite/g++.dg/analyzer/noexcept-new.C +++ b/gcc/testsuite/g++.dg/analyzer/noexcept-new.C @@ -11,15 +11,15 @@ struct A void test_throwing () { - int* x = new int; + int* x = new int; /* { dg-warning "dereference of possibly-NULL" } */ int* y = new int(); /* { dg-warning "dereference of possibly-NULL" } */ - int* arr = new int[10]; + int* arr = new int[10]; /* { dg-warning "dereference of possibly-NULL" } */ A *a = new A(); /* { dg-warning "dereference of possibly-NULL" } */ int z = *y + 2; - z = *x + 4; /* { dg-warning "dereference of possibly-NULL 'x'" } */ + z = *x + 4; /* { dg-warning "use of uninitialized value '\\*x'" "" { target *-*-* } .-1 } */ - z = arr[0] + 4; /* { dg-warning "dereference of possibly-NULL 'arr'" } */ + z = arr[0] + 4; /* { dg-warning "use of uninitialized value '\\*arr'" "" { target *-*-* } .-1 } */ a->y = a->x + 3; @@ -37,9 +37,9 @@ void test_nonthrowing () int z = *y + 2; /* { dg-warning "dereference of NULL 'y'" } */ /* { dg-bogus "use of uninitialized value '\\*y'" "" { target *-*-* } .-1 } */ - z = *x + 4; /* { dg-warning "dereference of possibly-NULL 'x'" } */ + z = *x + 4; /* { dg-warning "dereference of NULL 'x'" } */ /* { dg-warning "use of uninitialized value '\\*x'" "" { target *-*-* } .-1 } */ - z = arr[0] + 4; /* { dg-warning "dereference of possibly-NULL 'arr'" } */ + z = arr[0] + 4; /* { dg-warning "dereference of NULL 'arr'" } */ /* { dg-warning "use of uninitialized value '\\*arr'" "" { target *-*-* } .-1 } */ delete y; diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-new4a.C b/gcc/testsuite/g++.dg/cpp26/constexpr-new4a.C new file mode 100644 index 000000000000..d621293f3a69 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/constexpr-new4a.C @@ -0,0 +1,21 @@ +// PR c++/121068 +// { dg-do compile { target c++26 } } + +constexpr void *operator new (__SIZE_TYPE__, void *p) { return p; } +constexpr void *operator new[] (__SIZE_TYPE__, void *p) { return p; } + +consteval int +foo(int n) +{ + using T = int; + union { T arr[3]; }; + new(arr) T[n]; // makes arr active + for (int i = 0; i < 3; ++i) + arr[i].~T(); + + new (arr + 2) T{10}; // A + + return 1; +}; + +constexpr int g = foo(3); diff --git a/gcc/testsuite/g++.dg/warn/Warray-bounds-23.C b/gcc/testsuite/g++.dg/warn/Warray-bounds-23.C index c43a7dea3efc..7f6a6395ac74 100644 --- a/gcc/testsuite/g++.dg/warn/Warray-bounds-23.C +++ b/gcc/testsuite/g++.dg/warn/Warray-bounds-23.C @@ -34,7 +34,7 @@ void test_D1_b0i () { { D1 *p = (D1*)new char[sizeof (D1)]; - p->b0i = __LINE__; + p->b0i = __LINE__; // { dg-warning -Wuninitialized } sink (p); } @@ -110,7 +110,7 @@ void test_D2_b0i () { { D2 *p = (D2*)new char[sizeof (D2)]; - p->b0i = __LINE__; + p->b0i = __LINE__; // { dg-warning -Wuninitialized } sink (p); } @@ -201,7 +201,7 @@ void test_D3_b0i () { { D3 *p = (D3*)new char[sizeof (D3)]; - p->b0i = __LINE__; + p->b0i = __LINE__; // { dg-warning -Wuninitialized } sink (p); } diff --git a/gcc/testsuite/g++.dg/warn/Warray-bounds-24.C b/gcc/testsuite/g++.dg/warn/Warray-bounds-24.C index 071453a485d7..068195ab1b06 100644 --- a/gcc/testsuite/g++.dg/warn/Warray-bounds-24.C +++ b/gcc/testsuite/g++.dg/warn/Warray-bounds-24.C @@ -34,7 +34,7 @@ void test_D1_b0a () { { D1 *p = (D1*)new char[sizeof (D1)]; - *p->b0a = __LINE__; // { dg-warning "-Warray-bounds" "pr99630" { xfail *-*-* } } + *p->b0a = __LINE__; // { dg-warning "-Wuninitialized" } sink (p); } @@ -110,7 +110,7 @@ void test_D2_b0a () { { D2 *p = (D2*)new char[sizeof (D2)]; - *p->b0a = __LINE__; // { dg-warning "-Warray-bounds" "pr99630" { xfail *-*-* } } + *p->b0a = __LINE__; // { dg-warning "-Wuninitialized" } sink (p); } @@ -201,7 +201,7 @@ void test_D3_b0a () { { D3 *p = (D3*)new char[sizeof (D3)]; - *p->b0a = __LINE__; // { dg-warning "-Warray-bounds" "pr99630" { xfail *-*-* } } + *p->b0a = __LINE__; // { dg-warning "-Wuninitialized" } sink (p); }