Tested x86_64-pc-linux-gnu, applying to trunk.
-- 8< --
r16-3022 changed placement new to clobber the object, and improved constexpr
handling to do more with clobbers. But it occurred to me that in a lot of
cases we don't need to introduce a constructor_elt to represent an
uninitialized member of an uninitialized struct/array.
gcc/cp/ChangeLog:
* constexpr.cc (get_or_insert_ctor_field): -2 means don't insert.
(cxx_eval_component_reference): Handle finding void_node.
(cxx_eval_store_expression): Don't represent initial clobber
unless we need to activate a union member.
(cxx_eval_statement_list): Don't ask for a void prvalue.
(cxx_eval_loop_expr): The expr is discarded-value.
(cxx_eval_constant_expression): A loose clobber is non-constant.
Handle getting void_node instead of a real result.
(potential_constant_expression_1): A local temp is
potentially-constant.
* init.cc (build_new_1): Don't clobber empty types or
in a template.
(build_vec_init): Fix clobber handling.
gcc/testsuite/ChangeLog:
* g++.dg/init/pr25811.C: Tweak diagnostic.
* g++.dg/warn/Warray-bounds-12.C: Likewise.
* g++.dg/warn/Warray-bounds-13.C: Likewise.
* g++.dg/cpp26/constexpr-new6.C: New test.
---
gcc/cp/constexpr.cc | 84 +++++++++++++++++---
gcc/cp/init.cc | 11 ++-
gcc/testsuite/g++.dg/cpp26/constexpr-new6.C | 17 ++++
gcc/testsuite/g++.dg/init/pr25811.C | 2 +-
gcc/testsuite/g++.dg/warn/Warray-bounds-12.C | 6 +-
gcc/testsuite/g++.dg/warn/Warray-bounds-13.C | 4 +-
6 files changed, 105 insertions(+), 19 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/cpp26/constexpr-new6.C
diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index db363dc2760..1621e28da5c 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -5293,7 +5293,9 @@ find_array_ctor_elt (tree ary, tree dindex, bool insert)
matching constructor_elt exists, then add one to CTOR.
As an optimization, if POS_HINT is non-negative then it is used as a guess
- for the (integer) index of the matching constructor_elt within CTOR. */
+ for the (integer) index of the matching constructor_elt within CTOR.
+
+ If POS_HINT is -2, it means do not insert. */
static constructor_elt *
get_or_insert_ctor_field (tree ctor, tree index, int pos_hint = -1)
@@ -5303,9 +5305,11 @@ get_or_insert_ctor_field (tree ctor, tree index, int
pos_hint = -1)
&& CONSTRUCTOR_ELT (ctor, pos_hint)->index == index)
return CONSTRUCTOR_ELT (ctor, pos_hint);
+ bool insertp = (pos_hint != -2);
tree type = TREE_TYPE (ctor);
if (TREE_CODE (type) == VECTOR_TYPE && index == NULL_TREE)
{
+ gcc_assert (insertp);
CONSTRUCTOR_APPEND_ELT (CONSTRUCTOR_ELTS (ctor), index, NULL_TREE);
return &CONSTRUCTOR_ELTS (ctor)->last();
}
@@ -5323,13 +5327,26 @@ get_or_insert_ctor_field (tree ctor, tree index, int
pos_hint = -1)
tree lo = TREE_OPERAND (index, 0);
gcc_assert (array_index_cmp (elts->last().index, lo) < 0);
}
+ gcc_assert (insertp);
CONSTRUCTOR_APPEND_ELT (elts, index, NULL_TREE);
return &elts->last();
}
- HOST_WIDE_INT i = find_array_ctor_elt (ctor, index, /*insert*/true);
- gcc_assert (i >= 0);
+ HOST_WIDE_INT i = find_array_ctor_elt (ctor, index, insertp);
+ if (i < 0)
+ {
+ gcc_assert (!insertp);
+ return nullptr;
+ }
constructor_elt *cep = CONSTRUCTOR_ELT (ctor, i);
+ if (!insertp && cep->index && TREE_CODE (cep->index) == RANGE_EXPR)
+ {
+ /* Split a range even if we aren't inserting new entries. */
+ gcc_assert (!insertp);
+ i = find_array_ctor_elt (ctor, index, /*insert*/true);
+ gcc_assert (i >= 0);
+ cep = CONSTRUCTOR_ELT (ctor, i);
+ }
gcc_assert (cep->index == NULL_TREE
|| TREE_CODE (cep->index) != RANGE_EXPR);
return cep;
@@ -5384,6 +5401,9 @@ get_or_insert_ctor_field (tree ctor, tree index, int
pos_hint = -1)
entry at the end. */
insert:
+ if (!insertp)
+ return nullptr;
+
{
constructor_elt ce = { index, NULL_TREE };
@@ -5745,6 +5765,8 @@ cxx_eval_component_reference (const constexpr_ctx *ctx,
tree t,
if (pmf ? DECL_NAME (field) == DECL_NAME (part)
: field == part)
{
+ if (value == void_node)
+ goto uninit;
if (value)
{
STRIP_ANY_LOCATION_WRAPPER (value);
@@ -5796,6 +5818,7 @@ cxx_eval_component_reference (const constexpr_ctx *ctx,
tree t,
if (CONSTRUCTOR_NO_CLEARING (whole))
{
+ uninit:
/* 'whole' is part of the aggregate initializer we're currently
building; if there's no initializer for this member yet, that's an
error. */
@@ -7510,6 +7533,8 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree
t,
/* If we're modifying a const object, save it. */
tree const_object_being_modified = NULL_TREE;
bool mutable_p = false;
+ /* If we see a union, we can't ignore clobbers. */
+ int seen_union = 0;
for (tree probe = target; object == NULL_TREE; )
{
switch (TREE_CODE (probe))
@@ -7552,6 +7577,8 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree
t,
vec_safe_push (refs, elt);
vec_safe_push (refs, TREE_TYPE (probe));
probe = ob;
+ if (TREE_CODE (TREE_TYPE (ob)) == UNION_TYPE)
+ ++seen_union;
}
break;
@@ -7652,15 +7679,19 @@ cxx_eval_store_expression (const constexpr_ctx *ctx,
tree t,
}
/* Handle explicit end-of-lifetime. */
- if (TREE_CLOBBER_P (init)
- && CLOBBER_KIND (init) >= CLOBBER_OBJECT_END)
+ if (TREE_CLOBBER_P (init))
{
- if (refs->is_empty ())
+ if (CLOBBER_KIND (init) >= CLOBBER_OBJECT_END
+ && refs->is_empty ())
{
ctx->global->destroy_value (object);
return void_node;
}
+ if (!seen_union && !*valp
+ && CLOBBER_KIND (init) < CLOBBER_OBJECT_END)
+ return void_node;
+
/* Ending the lifetime of a const object is OK. */
const_object_being_modified = NULL_TREE;
}
@@ -7853,12 +7884,22 @@ cxx_eval_store_expression (const constexpr_ctx *ctx,
tree t,
ctors.safe_push (valp);
vec_safe_push (indexes, index);
+ /* Avoid adding an _elt for a clobber when the whole CONSTRUCTOR is
+ uninitialized. */
+ int pos = (!seen_union && TREE_CLOBBER_P (init)
+ && CONSTRUCTOR_NO_CLEARING (*valp)
+ && CLOBBER_KIND (init) < CLOBBER_OBJECT_END) ? -2 : -1;
constructor_elt *cep
- = get_or_insert_ctor_field (*valp, index);
+ = get_or_insert_ctor_field (*valp, index, pos);
+ if (cep == nullptr)
+ return void_node;
index_pos_hints.safe_push (cep - CONSTRUCTOR_ELTS (*valp)->begin());
if (code == UNION_TYPE)
- activated_union_member_p = true;
+ {
+ activated_union_member_p = true;
+ --seen_union;
+ }
valp = &cep->value;
type = reftype;
@@ -8279,7 +8320,8 @@ cxx_eval_statement_list (const constexpr_ctx *ctx, tree t,
value_cat lval = vc_discard;
/* The result of a statement-expression is not wrapped in EXPR_STMT. */
- if (tsi_one_before_end_p (i) && TREE_CODE (stmt) != EXPR_STMT)
+ if (tsi_one_before_end_p (i)
+ && !VOID_TYPE_P (TREE_TYPE (stmt)))
lval = vc_prvalue;
r = cxx_eval_constant_expression (ctx, stmt, lval,
@@ -8383,7 +8425,7 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t,
*jump_target = NULL_TREE;
if (expr)
- cxx_eval_constant_expression (ctx, expr, vc_prvalue,
+ cxx_eval_constant_expression (ctx, expr, vc_discard,
non_constant_p, overflow_p,
jump_target);
cleanup_cond ();
@@ -9664,6 +9706,14 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx,
tree t,
if (TREE_CONSTANT (t))
return t;
}
+ if (TREE_CLOBBER_P (t))
+ {
+ /* Assignment from a clobber is handled in cxx_eval_store_expression;
+ a clobber by itself isn't a constant-expression. */
+ gcc_assert (ctx->quiet);
+ *non_constant_p = true;
+ break;
+ }
r = cxx_eval_bare_aggregate (ctx, t, lval,
non_constant_p, overflow_p, jump_target);
break;
@@ -10226,6 +10276,19 @@ cxx_eval_constant_expression (const constexpr_ctx
*ctx, tree t,
if (r == error_mark_node)
*non_constant_p = true;
+ if (r == void_node && lval != vc_discard && !*jump_target
+ && !VOID_TYPE_P (TREE_TYPE (t)))
+ {
+ /* For diagnostic quality we should have handled this sooner, where we
+ can be more specific about the out-of-lifetime object. But here we
+ can still be correct. */
+ gcc_checking_assert (false);
+ if (!ctx->quiet)
+ error_at (EXPR_LOCATION (t),
+ "%qE accesses an object outside its lifetime", t);
+ *non_constant_p = true;
+ }
+
if (*non_constant_p)
return t;
else
@@ -11632,6 +11695,7 @@ potential_constant_expression_1 (tree t, bool
want_rval, bool strict, bool now,
&& (now || !var_in_maybe_constexpr_fn (t))
&& !type_dependent_expression_p (t)
&& !decl_maybe_constant_var_p (t)
+ && !is_local_temp (t)
&& (strict
|| !CP_TYPE_CONST_NON_VOLATILE_P (TREE_TYPE (t))
|| (DECL_INITIAL (t)
diff --git a/gcc/cp/init.cc b/gcc/cp/init.cc
index 8db84eb5e38..c950c363f59 100644
--- a/gcc/cp/init.cc
+++ b/gcc/cp/init.cc
@@ -3563,7 +3563,10 @@ build_new_1 (vec<tree, va_gc> **placement, tree type,
tree nelts,
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 && !*init && flag_lifetime_dse > 1
+ = (std_placement && flag_lifetime_dse > 1
+ && !processing_template_decl
+ && !is_empty_type (elt_type)
+ && !*init
&& (!CLASS_TYPE_P (elt_type)
|| type_has_non_user_provided_default_constructor (elt_type)));
@@ -4923,7 +4926,8 @@ build_vec_init (tree base, tree maxindex, tree init,
}
/* Any elements without explicit initializers get T{}. */
- empty_list = true;
+ if (!TREE_CLOBBER_P (init))
+ empty_list = true;
}
else if (init && TREE_CODE (init) == STRING_CST)
{
@@ -5062,7 +5066,8 @@ build_vec_init (tree base, tree maxindex, tree init,
}
else if (TREE_CODE (type) == ARRAY_TYPE)
{
- if (init && !BRACE_ENCLOSED_INITIALIZER_P (init))
+ if (init && !BRACE_ENCLOSED_INITIALIZER_P (init)
+ && !TREE_CLOBBER_P (init))
{
if ((complain & tf_error))
error_at (loc, "array must be initialized "
diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-new6.C
b/gcc/testsuite/g++.dg/cpp26/constexpr-new6.C
new file mode 100644
index 00000000000..b27c80d71c0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/constexpr-new6.C
@@ -0,0 +1,17 @@
+// { dg-do compile { target c++26 } }
+
+#include <new>
+
+union U { double d; int i; };
+
+constexpr int f()
+{
+ U u;
+ new (&u.i) int;
+ return u.i; // { dg-error "uninitialized" }
+}
+
+int main ()
+{
+ constexpr int i = f(); // { dg-message "" }
+}
diff --git a/gcc/testsuite/g++.dg/init/pr25811.C
b/gcc/testsuite/g++.dg/init/pr25811.C
index 4cda484e5af..853eeae3feb 100644
--- a/gcc/testsuite/g++.dg/init/pr25811.C
+++ b/gcc/testsuite/g++.dg/init/pr25811.C
@@ -187,7 +187,7 @@ void f11 ()
void f12 ()
{
- new A3[1]; // { dg-error "deleted|uninitialized reference member" }
+ new A3[1]; // { dg-error "deleted|uninitialized reference" }
}
void f13 ()
diff --git a/gcc/testsuite/g++.dg/warn/Warray-bounds-12.C
b/gcc/testsuite/g++.dg/warn/Warray-bounds-12.C
index 07fa351a86c..3a0b6093c82 100644
--- a/gcc/testsuite/g++.dg/warn/Warray-bounds-12.C
+++ b/gcc/testsuite/g++.dg/warn/Warray-bounds-12.C
@@ -19,7 +19,7 @@ void sink (void*);
void warn_new ()
{
- T (int32_t, 0, 0); // { dg-warning "array subscript 0 is outside
array bounds of 'int32_t \\\[0]'" }
+ T (int32_t, 0, 0); // { dg-warning "array subscript 0 is outside
array bounds" }
// { dg-message "object of size \\d allocated by
'\[^\n\r]*operator new\[^\n\r]*'" "note" { target *-*-* } .-1 }
T (int32_t, 1, 0); // { dg-warning "array subscript 'int32_t {aka
(long )?int}\\\[0]' is partly outside array bounds of 'unsigned char \\\[1]'" }
T (int32_t, 2, 0); // { dg-warning "array subscript 'int32_t {aka
(long )?int}\\\[0]' is partly outside array bounds of 'unsigned char \\\[2]'" }
@@ -45,7 +45,7 @@ void warn_array_new ()
#undef NEW
#define NEW(n) new char [n]
- T (int32_t, 0, 0); // { dg-warning "array subscript 0 is outside
array bounds of 'int32_t \\\[0]'" }
+ T (int32_t, 0, 0); // { dg-warning "array subscript 0 is outside
array bounds" }
// { dg-message "object of size \\d allocated by
'\[^\n\r]*operator new\[^\n\r]*'" "note" { target *-*-* } .-1 }
T (int32_t, 1, 0); // { dg-warning "array subscript 'int32_t {aka
(long )?int}\\\[0]' is partly outside array bounds of 'unsigned char \\\[1]'" }
T (int32_t, 2, 0); // { dg-warning "array subscript 'int32_t {aka
(long )?int}\\\[0]' is partly outside array bounds of 'unsigned char \\\[2]'" }
@@ -53,7 +53,7 @@ void warn_array_new ()
T (int32_t, 4, 0);
- T (int32_t, 0, 1); // { dg-warning "array subscript 1 is outside
array bounds of 'int32_t \\\[0]'" }
+ T (int32_t, 0, 1); // { dg-warning "array subscript 1 is outside
array bounds" }
T (int32_t, 1, 1); // { dg-warning "array subscript 1 is outside
array bounds " }
T (int32_t, 2, 1); // { dg-warning "array subscript 1 is outside
array bounds " }
T (int32_t, 3, 1); // { dg-warning "array subscript 1 is outside
array bounds " }
diff --git a/gcc/testsuite/g++.dg/warn/Warray-bounds-13.C
b/gcc/testsuite/g++.dg/warn/Warray-bounds-13.C
index 449324a315d..68b78e3fd9e 100644
--- a/gcc/testsuite/g++.dg/warn/Warray-bounds-13.C
+++ b/gcc/testsuite/g++.dg/warn/Warray-bounds-13.C
@@ -66,7 +66,7 @@ void warn_nothrow_array_new ()
#undef NEW
#define NEW(n) new (std::nothrow) char [n]
- T (int32_t, 0, 0); // { dg-warning "array subscript 0 is outside
array bounds of 'int32_t \\\[0]'" }
+ T (int32_t, 0, 0); // { dg-warning "array subscript 0 is outside
array bounds" }
// { dg-message "object of size \\d allocated by
'\[^\n\r]*operator new\[^\n\r]*'" "note" { target *-*-* } .-1 }
T (int32_t, 1, 0); // { dg-warning "array subscript 'int32_t {aka
(long )?int}\\\[0]' is partly outside array bounds of 'unsigned char \\\[1]'" }
T (int32_t, 2, 0); // { dg-warning "array subscript 'int32_t {aka
(long )?int}\\\[0]' is partly outside array bounds of 'unsigned char \\\[2]'" }
@@ -74,7 +74,7 @@ void warn_nothrow_array_new ()
T (int32_t, 4, 0);
- T (int32_t, 0, 1); // { dg-warning "array subscript 1 is outside
array bounds of 'int32_t \\\[0]'" }
+ T (int32_t, 0, 1); // { dg-warning "array subscript 1 is outside
array bounds " }
T (int32_t, 1, 1); // { dg-warning "array subscript 1 is outside
array bounds " }
T (int32_t, 2, 1); // { dg-warning "array subscript 1 is outside
array bounds " }
T (int32_t, 3, 1); // { dg-warning "array subscript 1 is outside
array bounds " }
base-commit: c91e508a52d4441b6c081ea50b0a08d0f4775675
--
2.51.0