Patchwork C++ PATCH for c++/48281 (ICE with nested initializer_list)

login
register
mail settings
Submitter Jason Merrill
Date March 30, 2011, 6:05 p.m.
Message ID <4D9370E3.9010602@redhat.com>
Download mbox | patch
Permalink /patch/88943/
State New
Headers show

Comments

Jason Merrill - March 30, 2011, 6:05 p.m.
The testcase in 48281 started breaking in 4.6.0 because of my change to 
finish_compound_literal to stop putting constant compound literals into 
static variables, because it was interfering with constexpr.

First I noticed that the crash was due to non-constant CONSTRUCTORs with 
TREE_CONSTANT set.  So I fixed that, in constructor-const.patch.

That change fixed the crash, but then we were just optimizing away the 
entire contents of the lists due to a bug I just filed as PR 48370.

Fixing the bug in general will mean fixing 48370, but we can fix the 
regression by just reverting the change above; I dealt with a similar 
issue elsewhere by setting DECL_DECLARED_CONSTEXPR_P on the temporary 
variable so that constexpr evaluation can look at its initializer, so we 
can use the same approach here.  I'm only doing this for arrays now so 
that we can continue to elide copies of class temporaries.

While I was looking at this stuff, I decided to go ahead and initialize 
the initializer_list objects directly rather than mess with calling the 
constructor.

Tested x86_64-pc-linux-gnu, applying to trunk (all three patches) and 
4.6 (only the first patch).
commit 15ce1df0c9f6259babc885e8947719a586a47103
Author: Jason Merrill <jason@redhat.com>
Date:   Wed Mar 30 11:57:50 2011 -0400

    	PR c++/48281
    	* semantics.c (finish_compound_literal): Do put static/constant
    	arrays in static variables.
commit 4069e2ded787088dcf1d4caf1aeccd26e00524c0
Author: Jason Merrill <jason@redhat.com>
Date:   Wed Mar 30 10:34:09 2011 -0400

    	* call.c (convert_like_real) [ck_list]: Build up the
    	initializer_list object directly.
    	* decl.c (build_init_list_var_init): Adjust.

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index f7d108f..ad2de43 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -5467,8 +5467,8 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
 	tree elttype = TREE_VEC_ELT (CLASSTYPE_TI_ARGS (totype), 0);
 	tree new_ctor = build_constructor (init_list_type_node, NULL);
 	unsigned len = CONSTRUCTOR_NELTS (expr);
-	tree array, val;
-	VEC(tree,gc) *parms;
+	tree array, val, field;
+	VEC(constructor_elt,gc) *vec = NULL;
 	unsigned ix;
 
 	/* Convert all the elements.  */
@@ -5490,16 +5490,14 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
 	array = build_array_of_n_type (elttype, len);
 	array = finish_compound_literal (array, new_ctor);
 
-	parms = make_tree_vector ();
-	VEC_safe_push (tree, gc, parms, decay_conversion (array));
-	VEC_safe_push (tree, gc, parms, size_int (len));
-	/* Call the private constructor.  */
-	push_deferring_access_checks (dk_no_check);
-	new_ctor = build_special_member_call
-	  (NULL_TREE, complete_ctor_identifier, &parms, totype, 0, complain);
-	release_tree_vector (parms);
-	pop_deferring_access_checks ();
-	return build_cplus_new (totype, new_ctor);
+	/* Build up the initializer_list object.  */
+	totype = complete_type (totype);
+	field = next_initializable_field (TYPE_FIELDS (totype));
+	CONSTRUCTOR_APPEND_ELT (vec, field, decay_conversion (array));
+	field = next_initializable_field (DECL_CHAIN (field));
+	CONSTRUCTOR_APPEND_ELT (vec, field, size_int (len));
+	new_ctor = build_constructor (totype, vec);
+	return get_target_expr (new_ctor);
       }
 
     case ck_aggr:
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index d9c9ad8..bbccb67 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -4562,7 +4562,7 @@ build_init_list_var_init (tree decl, tree type, tree init, tree *array_init,
     return error_mark_node;
 
   aggr_init = TARGET_EXPR_INITIAL (init);
-  array = AGGR_INIT_EXPR_ARG (aggr_init, 1);
+  array = CONSTRUCTOR_ELT (aggr_init, 0)->value;
   arrtype = TREE_TYPE (array);
   STRIP_NOPS (array);
   gcc_assert (TREE_CODE (array) == ADDR_EXPR);
@@ -4574,7 +4574,7 @@ build_init_list_var_init (tree decl, tree type, tree init, tree *array_init,
       tree var = set_up_extended_ref_temp (decl, array, cleanup, array_init);
       var = build_address (var);
       var = convert (arrtype, var);
-      AGGR_INIT_EXPR_ARG (aggr_init, 1) = var;
+      CONSTRUCTOR_ELT (aggr_init, 0)->value = var;
     }
   return init;
 }
commit 0d659efed9f80f72baf3f1a57549d8f9d56dbbe6
Author: Jason Merrill <jason@redhat.com>
Date:   Wed Mar 30 10:19:12 2011 -0400

    	* call.c (convert_like_real): Correct TREE_CONSTANT on CONSTRUCTOR.
    	* decl.c (reshape_init_array_1): Likewise.

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index a1cfa96..f7d108f 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -5481,6 +5481,8 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
 	    if (!BRACE_ENCLOSED_INITIALIZER_P (val))
 	      check_narrowing (TREE_TYPE (sub), val);
 	    CONSTRUCTOR_APPEND_ELT (CONSTRUCTOR_ELTS (new_ctor), NULL_TREE, sub);
+	    if (!TREE_CONSTANT (sub))
+	      TREE_CONSTANT (new_ctor) = false;
 	  }
 	/* Build up the array.  */
 	elttype = cp_build_qualified_type
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 16ccfaf..d9c9ad8 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -4905,6 +4905,8 @@ reshape_init_array_1 (tree elt_type, tree max_index, reshape_iter *d)
 	return error_mark_node;
       CONSTRUCTOR_APPEND_ELT (CONSTRUCTOR_ELTS (new_init),
 			      size_int (index), elt_init);
+      if (!TREE_CONSTANT (elt_init))
+	TREE_CONSTANT (new_init) = false;
     }
 
   return new_init;
Jason Merrill - April 26, 2011, 11:42 p.m.
On 03/30/2011 02:05 PM, Jason Merrill wrote:
> First I noticed that the crash was due to non-constant CONSTRUCTORs with
> TREE_CONSTANT set. So I fixed that, in constructor-const.patch.

It turns out that we need to apply this patch to 4.6 as well, for bug 
48726.  So I'm doing that.

Jason

Patch

diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 9926d26..b88e190 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -2329,7 +2329,34 @@  finish_compound_literal (tree type, tree compound_literal)
   if (TREE_CODE (type) == ARRAY_TYPE)
     cp_complete_array_type (&type, compound_literal, false);
   compound_literal = digest_init (type, compound_literal);
-  return get_target_expr (compound_literal);
+  /* Put static/constant array temporaries in static variables, but always
+     represent class temporaries with TARGET_EXPR so we elide copies.  */
+  if ((!at_function_scope_p () || CP_TYPE_CONST_P (type))
+      && TREE_CODE (type) == ARRAY_TYPE
+      && initializer_constant_valid_p (compound_literal, type))
+    {
+      tree decl = create_temporary_var (type);
+      DECL_INITIAL (decl) = compound_literal;
+      TREE_STATIC (decl) = 1;
+      if (literal_type_p (type) && CP_TYPE_CONST_NON_VOLATILE_P (type))
+	{
+	  /* 5.19 says that a constant expression can include an
+	     lvalue-rvalue conversion applied to "a glvalue of literal type
+	     that refers to a non-volatile temporary object initialized
+	     with a constant expression".  Rather than try to communicate
+	     that this VAR_DECL is a temporary, just mark it constexpr.  */
+	  DECL_DECLARED_CONSTEXPR_P (decl) = true;
+	  DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl) = true;
+	  TREE_CONSTANT (decl) = true;
+	}
+      cp_apply_type_quals_to_decl (cp_type_quals (type), decl);
+      decl = pushdecl_top_level (decl);
+      DECL_NAME (decl) = make_anon_name ();
+      SET_DECL_ASSEMBLER_NAME (decl, DECL_NAME (decl));
+      return decl;
+    }
+  else
+    return get_target_expr (compound_literal);
 }
 
 /* Return the declaration for the function-name variable indicated by
diff --git a/gcc/testsuite/g++.dg/cpp0x/initlist46.C b/gcc/testsuite/g++.dg/cpp0x/initlist46.C
new file mode 100644
index 0000000..2b9f07d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/initlist46.C
@@ -0,0 +1,14 @@ 
+// PR c++/48281
+// { dg-options "-std=c++0x -O2" }
+// { dg-do run }
+
+#include <initializer_list>
+
+typedef std::initializer_list<int>  int1;
+typedef std::initializer_list<int1> int2;
+static int2 ib = {{42,2,3,4,5},{2,3,4,5,1},{3,4,5,2,1}};
+
+int main()
+{
+  return *(ib.begin()->begin()) != 42;
+}