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

Reply via email to