The problem in this bug was that the constexpr code builds a lot of
CONSTRUCTORs and then fills in the elements later without ever going
back and updating TREE_CONSTANT and TREE_SIDE_EFFECTS.
This patch adds middle end functions recompute_constructor_flags and
verify_constructor_flags, and fixes the constexpr code to be more
careful about updating the flags.
Tested x86_64-pc-linux-gnu. Are the tree.c changes OK for trunk?
commit 2ffc171465931c8de27a8f5afd2963df63d8d6e5
Author: Jason Merrill <ja...@redhat.com>
Date: Tue Jan 26 15:12:27 2016 -0500
PR c++/68782
gcc/
* tree.c (recompute_constructor_flags): Split out from
build_constructor.
(verify_constructor_flags): New.
* tree.h: Declare them.
gcc/cp/
* constexpr.c (cxx_eval_bare_aggregate): Update TREE_CONSTANT
and TREE_SIDE_EFFECTS.
(cxx_eval_constant_expression) [CONSTRUCTOR]: Call
verify_constructor_flags.
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 6b0e5a8..263ef38 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -2214,6 +2214,9 @@ cxx_eval_bare_aggregate (const constexpr_ctx *ctx, tree t,
vec<constructor_elt, va_gc> **p = &CONSTRUCTOR_ELTS (ctx->ctor);
vec_alloc (*p, vec_safe_length (v));
+ bool constant_p = true;
+ bool side_effects_p = false;
+
unsigned i; tree index, value;
FOR_EACH_CONSTRUCTOR_ELT (v, i, index, value)
{
@@ -2231,6 +2234,11 @@ cxx_eval_bare_aggregate (const constexpr_ctx *ctx, tree t,
break;
if (elt != value)
changed = true;
+
+ if (!TREE_CONSTANT (elt))
+ constant_p = false;
+ if (TREE_SIDE_EFFECTS (elt))
+ side_effects_p = true;
if (index && TREE_CODE (index) == COMPONENT_REF)
{
/* This is an initialization of a vfield inside a base
@@ -2264,6 +2272,8 @@ cxx_eval_bare_aggregate (const constexpr_ctx *ctx, tree t,
/* We're done building this CONSTRUCTOR, so now we can interpret an
element without an explicit initializer as value-initialized. */
CONSTRUCTOR_NO_IMPLICIT_ZERO (t) = false;
+ TREE_CONSTANT (t) = constant_p;
+ TREE_SIDE_EFFECTS (t) = side_effects_p;
if (VECTOR_TYPE_P (TREE_TYPE (t)))
t = fold (t);
return t;
@@ -2826,6 +2836,8 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
}
type = TREE_TYPE (object);
bool no_zero_init = true;
+
+ vec<tree,va_gc> *ctors = make_tree_vector();
while (!refs->is_empty())
{
if (*valp == NULL_TREE)
@@ -2837,6 +2849,8 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
subobjects will also be zero-initialized. */
no_zero_init = CONSTRUCTOR_NO_IMPLICIT_ZERO (*valp);
+ vec_safe_push (ctors, *valp);
+
enum tree_code code = TREE_CODE (type);
type = refs->pop();
tree index = refs->pop();
@@ -2889,14 +2903,35 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
/* The hash table might have moved since the get earlier. */
valp = ctx->values->get (object);
if (TREE_CODE (init) == CONSTRUCTOR)
- /* An outer ctx->ctor might be pointing to *valp, so just replace
- its contents. */
- CONSTRUCTOR_ELTS (*valp) = CONSTRUCTOR_ELTS (init);
+ {
+ /* An outer ctx->ctor might be pointing to *valp, so replace
+ its contents. */
+ CONSTRUCTOR_ELTS (*valp) = CONSTRUCTOR_ELTS (init);
+ TREE_CONSTANT (*valp) = TREE_CONSTANT (init);
+ TREE_SIDE_EFFECTS (*valp) = TREE_SIDE_EFFECTS (init);
+ }
else
*valp = init;
}
else
- *valp = init;
+ {
+ *valp = init;
+
+ /* Update TREE_CONSTANT and TREE_SIDE_EFFECTS on enclosing
+ CONSTRUCTORs. */
+ unsigned i; tree elt;
+ bool c = TREE_CONSTANT (init);
+ bool s = TREE_SIDE_EFFECTS (init);
+ if (!c || s)
+ FOR_EACH_VEC_SAFE_ELT (ctors, i, elt)
+ {
+ if (!c)
+ TREE_CONSTANT (elt) = false;
+ if (s)
+ TREE_SIDE_EFFECTS (elt) = true;
+ }
+ }
+ release_tree_vector (ctors);
if (*non_constant_p)
return t;
@@ -3579,9 +3614,16 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
case CONSTRUCTOR:
if (TREE_CONSTANT (t))
- /* Don't re-process a constant CONSTRUCTOR, but do fold it to
- VECTOR_CST if applicable. */
- return fold (t);
+ {
+ /* Don't re-process a constant CONSTRUCTOR, but do fold it to
+ VECTOR_CST if applicable. */
+ if (CHECKING_P)
+ verify_constructor_flags (t);
+ else
+ recompute_constructor_flags (t);
+ if (TREE_CONSTANT (t))
+ return fold (t);
+ }
r = cxx_eval_bare_aggregate (ctx, t, lval,
non_constant_p, overflow_p);
break;
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-aggr2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-aggr2.C
new file mode 100644
index 0000000..805d026
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-aggr2.C
@@ -0,0 +1,27 @@
+// PR c++/68782
+// { dg-do compile { target c++11 } }
+
+#define assert(X) do { if (!(X)) __builtin_abort(); } while (0)
+
+struct holder { int& value; };
+
+constexpr holder from_value(int& value)
+{ return { value }; }
+
+struct aggr { int i; };
+
+constexpr holder from_aggr(aggr& a)
+{ return from_value(a.i); }
+
+int main()
+{
+ aggr a { 42 };
+
+ // these don't fire
+ assert( &from_value(a.i).value != nullptr );
+ assert( &a.i == &from_value(a.i).value );
+
+ // those do
+ assert( &from_aggr(a).value != nullptr );
+ assert( &a.i == &from_aggr(a).value );
+}
diff --git a/gcc/tree.c b/gcc/tree.c
index 9c67bea..fa7646b 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -1790,34 +1790,66 @@ build_vector_from_val (tree vectype, tree sc)
}
}
-/* Return a new CONSTRUCTOR node whose type is TYPE and whose values
- are in the vec pointed to by VALS. */
-tree
-build_constructor (tree type, vec<constructor_elt, va_gc> *vals)
+/* Something has messed with the elements of CONSTRUCTOR C after it was built;
+ calculate TREE_CONSTANT and TREE_SIDE_EFFECTS. */
+
+void
+recompute_constructor_flags (tree c)
{
- tree c = make_node (CONSTRUCTOR);
unsigned int i;
- constructor_elt *elt;
+ tree val;
bool constant_p = true;
bool side_effects_p = false;
+ vec<constructor_elt, va_gc> *vals = CONSTRUCTOR_ELTS (c);
- TREE_TYPE (c) = type;
- CONSTRUCTOR_ELTS (c) = vals;
-
- FOR_EACH_VEC_SAFE_ELT (vals, i, elt)
+ FOR_EACH_CONSTRUCTOR_VALUE (vals, i, val)
{
/* Mostly ctors will have elts that don't have side-effects, so
the usual case is to scan all the elements. Hence a single
loop for both const and side effects, rather than one loop
each (with early outs). */
- if (!TREE_CONSTANT (elt->value))
+ if (!TREE_CONSTANT (val))
constant_p = false;
- if (TREE_SIDE_EFFECTS (elt->value))
+ if (TREE_SIDE_EFFECTS (val))
side_effects_p = true;
}
TREE_SIDE_EFFECTS (c) = side_effects_p;
TREE_CONSTANT (c) = constant_p;
+}
+
+/* Make sure that TREE_CONSTANT and TREE_SIDE_EFFECTS are correct for
+ CONSTRUCTOR C. */
+
+void
+verify_constructor_flags (tree c)
+{
+ unsigned int i;
+ tree val;
+ bool constant_p = TREE_CONSTANT (c);
+ bool side_effects_p = TREE_SIDE_EFFECTS (c);
+ vec<constructor_elt, va_gc> *vals = CONSTRUCTOR_ELTS (c);
+
+ FOR_EACH_CONSTRUCTOR_VALUE (vals, i, val)
+ {
+ if (constant_p && !TREE_CONSTANT (val))
+ internal_error ("non-constant element in constant CONSTRUCTOR");
+ if (!side_effects_p && TREE_SIDE_EFFECTS (val))
+ internal_error ("side-effects element in no-side-effects CONSTRUCTOR");
+ }
+}
+
+/* Return a new CONSTRUCTOR node whose type is TYPE and whose values
+ are in the vec pointed to by VALS. */
+tree
+build_constructor (tree type, vec<constructor_elt, va_gc> *vals)
+{
+ tree c = make_node (CONSTRUCTOR);
+
+ TREE_TYPE (c) = type;
+ CONSTRUCTOR_ELTS (c) = vals;
+
+ recompute_constructor_flags (c);
return c;
}
diff --git a/gcc/tree.h b/gcc/tree.h
index 9b987bb..f789785 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -3918,6 +3918,8 @@ extern tree build_vector_stat (tree, tree * MEM_STAT_DECL);
#define build_vector(t,v) build_vector_stat (t, v MEM_STAT_INFO)
extern tree build_vector_from_ctor (tree, vec<constructor_elt, va_gc> *);
extern tree build_vector_from_val (tree, tree);
+extern void recompute_constructor_flags (tree);
+extern void verify_constructor_flags (tree);
extern tree build_constructor (tree, vec<constructor_elt, va_gc> *);
extern tree build_constructor_single (tree, tree, tree);
extern tree build_constructor_from_list (tree, tree);