Tested on x86_64-pc-linux-gnu, but a full bootstrap is in progress. Does this
look like the right approach?
gcc/cp/ChangeLog:
PR c++/94066
* constexpr.c (get_or_insert_ctor_field): Split out (while adding
handling for VECTOR_TYPEs) from ...
(cxx_eval_store_expression): ... here. Record the sequence of indexes
into INDEXES that yields the suboject we're assigning to. After
evaluating the initializer of the store expression, recompute valp using
INDEXES.
(cxx_eval_bare_aggregate): Use get_or_insert_ctor_field to recompute the
pointer to the constructor_elt we're assigning through after evaluating
each initializer.
gcc/testsuite/ChangeLog:
PR c++/94066
* g++.dg/cpp1y/pr94066.C: New test.
* g++.dg/cpp1y/pr94066-2.C: New test.
* g++.dg/cpp1y/pr94066-3.C: New test.
* g++.dg/cpp1y/pr94066-4.C: New test.
* g++.dg/cpp1y/pr94066-5.C: New test.
---
gcc/cp/constexpr.c | 184 +++++++++++++++----------
gcc/testsuite/g++.dg/cpp1y/pr94066-2.C | 21 +++
gcc/testsuite/g++.dg/cpp1y/pr94066-3.C | 21 +++
gcc/testsuite/g++.dg/cpp1y/pr94066-4.C | 22 +++
gcc/testsuite/g++.dg/cpp1y/pr94066-5.C | 18 +++
gcc/testsuite/g++.dg/cpp1y/pr94066.C | 20 +++
6 files changed, 210 insertions(+), 76 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/cpp1y/pr94066-2.C
create mode 100644 gcc/testsuite/g++.dg/cpp1y/pr94066-3.C
create mode 100644 gcc/testsuite/g++.dg/cpp1y/pr94066-4.C
create mode 100644 gcc/testsuite/g++.dg/cpp1y/pr94066-5.C
create mode 100644 gcc/testsuite/g++.dg/cpp1y/pr94066.C
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 192face9a3a..9fcf9f33457 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -3144,6 +3144,88 @@ find_array_ctor_elt (tree ary, tree dindex, bool insert)
return -1;
}
+/* Return a pointer to the constructor_elt of CTOR which matches INDEX. If no
+ matching constructor_elt exists, then add it to CTOR. Emit an appropriate
+ diagnostic if CTOR constructs a UNION_TYPE and adding INDEX would change the
+ active member of the union, unless QUIET is true. */
+
+static constructor_elt *
+get_or_insert_ctor_field (tree ctor, tree index,
+ bool quiet, bool *non_constant_p)
+{
+ tree type = TREE_TYPE (ctor);
+ if (TREE_CODE (type) == VECTOR_TYPE && index == NULL_TREE)
+ {
+ CONSTRUCTOR_APPEND_ELT (CONSTRUCTOR_ELTS (ctor), index, NULL_TREE);
+ return &CONSTRUCTOR_ELTS (ctor)->last();
+ }
+ else if (TREE_CODE (type) == ARRAY_TYPE || TREE_CODE (type) == VECTOR_TYPE)
+ {
+ HOST_WIDE_INT i = find_array_ctor_elt (ctor, index, /*insert*/true);
+ gcc_assert (i >= 0);
+ constructor_elt *cep = CONSTRUCTOR_ELT (ctor, i);
+ gcc_assert (cep->index == NULL_TREE
+ || TREE_CODE (cep->index) != RANGE_EXPR);
+ return cep;
+ }
+ else
+ {
+ gcc_assert (TREE_CODE (index) == FIELD_DECL);
+
+ /* We must keep the CONSTRUCTOR's ELTS in FIELD order.
+ Usually we meet initializers in that order, but it is
+ possible for base types to be placed not in program
+ order. */
+ tree fields = TYPE_FIELDS (DECL_CONTEXT (index));
+ unsigned HOST_WIDE_INT idx;
+
+ if (TREE_CODE (type) == UNION_TYPE && CONSTRUCTOR_NELTS (ctor)
+ && CONSTRUCTOR_ELT (ctor, 0)->index != index)
+ {
+ if (cxx_dialect < cxx2a)
+ {
+ if (!quiet)
+ error ("change of the active member of a union from %qD to %qD",
+ CONSTRUCTOR_ELT (ctor, 0)->index,
+ index);
+ *non_constant_p = true;
+ }
+ /* Changing active member. */
+ vec_safe_truncate (CONSTRUCTOR_ELTS (ctor), 0);
+ }
+
+ constructor_elt *cep = NULL;
+ for (idx = 0;
+ vec_safe_iterate (CONSTRUCTOR_ELTS (ctor), idx, &cep);
+ idx++, fields = DECL_CHAIN (fields))
+ {
+ if (index == cep->index)
+ goto found;
+
+ /* The field we're initializing must be on the field
+ list. Look to see if it is present before the
+ field the current ELT initializes. */
+ for (; fields != cep->index; fields = DECL_CHAIN (fields))
+ if (index == fields)
+ goto insert;
+ }
+
+ /* We fell off the end of the CONSTRUCTOR, so insert a new
+ entry at the end. */
+ insert:
+ {
+ constructor_elt ce = { index, NULL_TREE };
+
+ vec_safe_insert (CONSTRUCTOR_ELTS (ctor), idx, ce);
+ cep = CONSTRUCTOR_ELT (ctor, idx);
+ }
+ found:;
+
+ return cep;
+ }
+}
+
+
/* Under the control of CTX, issue a detailed diagnostic for
an out-of-bounds subscript INDEX into the expression ARRAY. */
@@ -3784,14 +3866,12 @@ cxx_eval_bare_aggregate (const constexpr_ctx *ctx, tree t,
}
else
{
- if (new_ctx.ctor != ctx->ctor)
- {
- /* We appended this element above; update the value. */
- gcc_assert ((*p)->last().index == index);
- (*p)->last().value = elt;
- }
- else
- CONSTRUCTOR_APPEND_ELT (*p, index, elt);
+ iloc_sentinel ils (cp_expr_location (t));
+ constructor_elt *cep
+ = get_or_insert_ctor_field (ctx->ctor, index,
+ ctx->quiet, non_constant_p);
+ cep->value = elt;
+
/* Adding or replacing an element might change the ctor's flags. */
TREE_CONSTANT (ctx->ctor) = constant_p;
TREE_SIDE_EFFECTS (ctx->ctor) = side_effects_p;
@@ -4566,7 +4646,7 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree
t,
type = TREE_TYPE (object);
bool no_zero_init = true;
- releasing_vec ctors;
+ releasing_vec ctors, indexes;
while (!refs->is_empty ())
{
if (*valp == NULL_TREE)
@@ -4607,77 +4687,21 @@ cxx_eval_store_expression (const constexpr_ctx *ctx,
tree t,
subobjects will also be zero-initialized. */
no_zero_init = CONSTRUCTOR_NO_CLEARING (*valp);
- vec_safe_push (ctors, *valp);
-
enum tree_code code = TREE_CODE (type);
type = refs->pop();
tree index = refs->pop();
- constructor_elt *cep = NULL;
- if (code == ARRAY_TYPE)
- {
- HOST_WIDE_INT i
- = find_array_ctor_elt (*valp, index, /*insert*/true);
- gcc_assert (i >= 0);
- cep = CONSTRUCTOR_ELT (*valp, i);
- gcc_assert (cep->index == NULL_TREE
- || TREE_CODE (cep->index) != RANGE_EXPR);
- }
- else
- {
- gcc_assert (TREE_CODE (index) == FIELD_DECL);
-
- /* We must keep the CONSTRUCTOR's ELTS in FIELD order.
- Usually we meet initializers in that order, but it is
- possible for base types to be placed not in program
- order. */
- tree fields = TYPE_FIELDS (DECL_CONTEXT (index));
- unsigned HOST_WIDE_INT idx;
-
- if (code == UNION_TYPE && CONSTRUCTOR_NELTS (*valp)
- && CONSTRUCTOR_ELT (*valp, 0)->index != index)
- {
- if (cxx_dialect < cxx2a)
- {
- if (!ctx->quiet)
- error_at (cp_expr_loc_or_input_loc (t),
- "change of the active member of a union "
- "from %qD to %qD",
- CONSTRUCTOR_ELT (*valp, 0)->index,
- index);
- *non_constant_p = true;
- }
- /* Changing active member. */
- vec_safe_truncate (CONSTRUCTOR_ELTS (*valp), 0);
- no_zero_init = true;
- }
+ vec_safe_push (ctors, *valp);
+ vec_safe_push (indexes, index);
- for (idx = 0;
- vec_safe_iterate (CONSTRUCTOR_ELTS (*valp), idx, &cep);
- idx++, fields = DECL_CHAIN (fields))
- {
- if (index == cep->index)
- goto found;
-
- /* The field we're initializing must be on the field
- list. Look to see if it is present before the
- field the current ELT initializes. */
- for (; fields != cep->index; fields = DECL_CHAIN (fields))
- if (index == fields)
- goto insert;
- }
+ if (code == UNION_TYPE && CONSTRUCTOR_NELTS (*valp)
+ && CONSTRUCTOR_ELT (*valp, 0)->index != index)
+ no_zero_init = true;
- /* We fell off the end of the CONSTRUCTOR, so insert a new
- entry at the end. */
- insert:
- {
- constructor_elt ce = { index, NULL_TREE };
+ iloc_sentinel ils (cp_expr_location (t));
+ constructor_elt *cep
+ = get_or_insert_ctor_field (*valp, index, ctx->quiet, non_constant_p);
- vec_safe_insert (CONSTRUCTOR_ELTS (*valp), idx, ce);
- cep = CONSTRUCTOR_ELT (*valp, idx);
- }
- found:;
- }
valp = &cep->value;
}
@@ -4758,9 +4782,17 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
init = tinit;
init = cxx_eval_constant_expression (&new_ctx, init, false,
non_constant_p, overflow_p);
- if (ctors->is_empty())
- /* The hash table might have moved since the get earlier. */
- valp = ctx->global->values.get (object);
+ /* The hash table might have moved since the get earlier. */
+ valp = ctx->global->values.get (object);
+ /* And the underlying CONSTRUCTORs may have been modified by the
+ initializer. */
+ for (unsigned i = 0; i < vec_safe_length (indexes); i++)
+ {
+ constructor_elt *cep
+ = get_or_insert_ctor_field (*valp, indexes[i],
+ /*quiet=*/true, non_constant_p);
+ valp = &cep->value;
+ }
}
/* Don't share a CONSTRUCTOR that might be changed later. */
diff --git a/gcc/testsuite/g++.dg/cpp1y/pr94066-2.C
b/gcc/testsuite/g++.dg/cpp1y/pr94066-2.C
new file mode 100644
index 00000000000..ec8210a9d73
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/pr94066-2.C
@@ -0,0 +1,21 @@
+// PR c++/94066
+// { dg-do compile { target c++14 } }
+
+struct A { long x; };
+
+union U;
+constexpr A foo(U *up);
+
+union U {
+ U() = default;
+ A a = foo(this); int y;
+};
+
+constexpr A foo(U *up) {
+ up->y = 11; // { dg-error ".U::a. to .U::y." "" { target c++17_down } }
+ return {42};
+}
+
+extern constexpr U u = {}; // { dg-error ".U::y. to .U::a." "" { target
c++17_down } }
+
+static_assert(u.a.x == 42, ""); // { dg-error "non-constant" "" { target
c++17_down } }
diff --git a/gcc/testsuite/g++.dg/cpp1y/pr94066-3.C
b/gcc/testsuite/g++.dg/cpp1y/pr94066-3.C
new file mode 100644
index 00000000000..b68c975d569
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/pr94066-3.C
@@ -0,0 +1,21 @@
+// PR c++/94066
+// { dg-do compile { target c++14 } }
+
+struct A { long x; };
+
+struct U;
+constexpr A foo(U *up);
+
+struct U {
+ A a = foo(this); int y;
+};
+
+constexpr A foo(U *up) {
+ up->y = 11;
+ return {42};
+}
+
+extern constexpr U u = {};
+
+static_assert(u.y == 11, "");
+static_assert(u.a.x == 42, "");
diff --git a/gcc/testsuite/g++.dg/cpp1y/pr94066-4.C
b/gcc/testsuite/g++.dg/cpp1y/pr94066-4.C
new file mode 100644
index 00000000000..0362c939faf
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/pr94066-4.C
@@ -0,0 +1,22 @@
+// PR c++/94066
+// { dg-do compile { target c++14 } }
+
+struct A { long x; };
+
+struct U;
+constexpr A foo(U *up);
+
+struct U {
+ U() = default;
+ int y; A a = foo(this);
+};
+
+constexpr A foo(U *up) {
+ up->y = 11;
+ return {42};
+}
+
+extern constexpr U u = {};
+
+static_assert(u.y == 11, "");
+static_assert(u.a.x == 42, "");
diff --git a/gcc/testsuite/g++.dg/cpp1y/pr94066-5.C
b/gcc/testsuite/g++.dg/cpp1y/pr94066-5.C
new file mode 100644
index 00000000000..63069f3ea5f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/pr94066-5.C
@@ -0,0 +1,18 @@
+// PR c++/94066
+// { dg-do compile { target c++14 } }
+
+union U;
+constexpr int foo(U *up);
+
+union U {
+ int a = foo(this); int y;
+};
+
+constexpr int foo(U *up) {
+ up->y = 11;
+ return 42;
+}
+
+extern constexpr U u = {}; // { dg-error ".U::y. to .U::a." "" { target
c++17_down } }
+
+static_assert(u.a == 42, ""); // { dg-error "non-constant" "" { target
c++17_down } }
diff --git a/gcc/testsuite/g++.dg/cpp1y/pr94066.C
b/gcc/testsuite/g++.dg/cpp1y/pr94066.C
new file mode 100644
index 00000000000..11912199406
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/pr94066.C
@@ -0,0 +1,20 @@
+// PR c++/94066
+// { dg-do compile { target c++14 } }
+
+struct A { long x; };
+
+union U;
+constexpr A foo(U *up);
+
+union U {
+ A a = foo(this); int y;
+};
+
+constexpr A foo(U *up) {
+ up->y = 11; // { dg-error ".U::a. to .U::y." "" { target c++17_down } }
+ return {42};
+}
+
+extern constexpr U u = {}; // { dg-error ".U::y. to .U::a." "" { target
c++17_down } }
+
+static_assert(u.a.x == 42, ""); // { dg-error "non-constant" "" { target
c++17_down } }