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.
This commit is contained in:
Jason Merrill 2025-10-08 16:09:49 +01:00
parent d52a81fdb3
commit d77b548fb6
8 changed files with 77 additions and 28 deletions

View File

@ -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);

View File

@ -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);
}

View File

@ -3557,15 +3557,17 @@ build_new_1 (vec<tree, va_gc> **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<tree, va_gc> **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
{

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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);
}

View File

@ -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);
}