Patchwork C++ PATCH for c++/40975 (ICE with new in default arg)

login
register
mail settings
Submitter Jason Merrill
Date April 28, 2011, 1:46 a.m.
Message ID <4DB8C6E7.5000900@redhat.com>
Download mbox | patch
Permalink /patch/93157/
State New
Headers show

Comments

Jason Merrill - April 28, 2011, 1:46 a.m.
When we expand a default argument, we need to rebuild any TARGET_EXPRs 
so that we don't run into issues with invalid tree sharing.  But 
copy_tree_r isn't prepared to deal with the STATEMENT_LIST created by 
array initialization.

Since I already introduced VEC_INIT_EXPR in the constexpr work, it seems 
to make sense to extend it to be usable for array new as well.  So this 
patch does that.

Tested x86_64-pc-linux-gnu, applying to trunk.  I'm also applying a tiny 
tweak I made while looking at this; it was bizarre that we were deciding 
whether or not to delete the array pointer based on whether the value 
was sfk_base_destructor.
commit 9262255ba31a1623bcb63df6104d56d0558bdc8c
Author: Jason Merrill <jason@redhat.com>
Date:   Wed Apr 27 12:56:16 2011 -0400

    	PR c++/40975
    	* cp-tree.def (VEC_INIT_EXPR): Add third operand.
    	* cp-tree.h (VEC_INIT_EXPR_NELTS): New.
    	* cp-gimplify.c (cp_gimplify_expr) [VEC_INIT_EXPR]: Handle it.
    	* tree.c (build_vec_init_expr): Handle getting pointer/nelts.
    	(build_vec_init_elt): Don't expect an array type.
    	(build_array_copy): Adjust.
    	* init.c (perform_member_init): Adjust.
    	(build_new_1): Use build_vec_init_expr.
commit 43347ec1f0e6913d0478c13509881563650c1697
Author: Jason Merrill <jason@redhat.com>
Date:   Wed Apr 27 15:10:11 2011 -0400

    	* init.c (build_vec_delete_1): Look for sfk_deleting_destructor to
    	decide whether to delete.
    	(build_vec_init): Pass sfk_complete_destructor.

diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index 883734f..3f88857 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -2793,9 +2793,8 @@ build_vec_delete_1 (tree base, tree maxindex, tree type,
   loop = build_compound_expr (input_location, tbase_init, loop);
 
  no_destructor:
-  /* If the delete flag is one, or anything else with the low bit set,
-     delete the storage.  */
-  if (auto_delete_vec != sfk_base_destructor)
+  /* Delete the storage if appropriate.  */
+  if (auto_delete_vec == sfk_deleting_destructor)
     {
       tree base_tbd;
 
@@ -2824,12 +2823,11 @@ build_vec_delete_1 (tree base, tree maxindex, tree type,
 	  virtual_size = size_binop (PLUS_EXPR, virtual_size, cookie_size);
 	}
 
-      if (auto_delete_vec == sfk_deleting_destructor)
-	deallocate_expr = build_op_delete_call (VEC_DELETE_EXPR,
-						base_tbd, virtual_size,
-						use_global_delete & 1,
-						/*placement=*/NULL_TREE,
-						/*alloc_fn=*/NULL_TREE);
+      deallocate_expr = build_op_delete_call (VEC_DELETE_EXPR,
+					      base_tbd, virtual_size,
+					      use_global_delete & 1,
+					      /*placement=*/NULL_TREE,
+					      /*alloc_fn=*/NULL_TREE);
     }
 
   body = loop;
@@ -3284,7 +3282,7 @@ build_vec_init (tree base, tree maxindex, tree init,
 
       finish_cleanup_try_block (try_block);
       e = build_vec_delete_1 (rval, m,
-			      inner_elt_type, sfk_base_destructor,
+			      inner_elt_type, sfk_complete_destructor,
 			      /*use_global_delete=*/0, complain);
       if (e == error_mark_node)
 	errors = true;

Patch

diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 63277ca..802040d 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -9580,6 +9580,17 @@  make_tree_vector_single (tree t)
   return ret;
 }
 
+/* Get a new tree vector of the TREE_VALUEs of a TREE_LIST chain.  */
+
+VEC(tree,gc) *
+make_tree_vector_from_list (tree list)
+{
+  VEC(tree,gc) *ret = make_tree_vector ();
+  for (; list; list = TREE_CHAIN (list))
+    VEC_safe_push (tree, gc, ret, TREE_VALUE (list));
+  return ret;
+}
+
 /* Get a new tree vector which is a copy of an existing one.  */
 
 VEC(tree,gc) *
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 09aa600..ad6da6b 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -959,6 +959,7 @@  extern void set_underlying_type (tree x);
 extern VEC(tree,gc) *make_tree_vector (void);
 extern void release_tree_vector (VEC(tree,gc) *);
 extern VEC(tree,gc) *make_tree_vector_single (tree);
+extern VEC(tree,gc) *make_tree_vector_from_list (tree);
 extern VEC(tree,gc) *make_tree_vector_copy (const VEC(tree,gc) *);
 
 /* In c-gimplify.c  */
diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c
index ca62df3..dc2e0fb 100644
--- a/gcc/cp/cp-gimplify.c
+++ b/gcc/cp/cp-gimplify.c
@@ -530,10 +530,12 @@  cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
       {
 	location_t loc = input_location;
 	tree init = VEC_INIT_EXPR_INIT (*expr_p);
-	int from_array = (init && TREE_CODE (TREE_TYPE (init)) == ARRAY_TYPE);
+	int from_array = (init && TREE_TYPE (init)
+			  && TREE_CODE (TREE_TYPE (init)) == ARRAY_TYPE);
 	gcc_assert (EXPR_HAS_LOCATION (*expr_p));
 	input_location = EXPR_LOCATION (*expr_p);
-	*expr_p = build_vec_init (VEC_INIT_EXPR_SLOT (*expr_p), NULL_TREE,
+	*expr_p = build_vec_init (VEC_INIT_EXPR_SLOT (*expr_p),
+				  VEC_INIT_EXPR_NELTS (*expr_p),
 				  init, VEC_INIT_EXPR_VALUE_INIT (*expr_p),
 				  from_array,
 				  tf_warning_or_error);
diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def
index 7bd35e0..c9fc970 100644
--- a/gcc/cp/cp-tree.def
+++ b/gcc/cp/cp-tree.def
@@ -83,8 +83,8 @@  DEFTREECODE (AGGR_INIT_EXPR, "aggr_init_expr", tcc_vl_exp, 3)
 
 /* Initialization of an array from another array, expressed at a high level
    so that it works with TARGET_EXPR.  Operand 0 is the target, operand 1
-   is the initializer.  */
-DEFTREECODE (VEC_INIT_EXPR, "vec_init_expr", tcc_expression, 2)
+   is the initializer, operand 2 is the number of elements or NULL_TREE.  */
+DEFTREECODE (VEC_INIT_EXPR, "vec_init_expr", tcc_expression, 3)
 
 /* A throw expression.  operand 0 is the expression, if there was one,
    else it is NULL_TREE.  */
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 26da4b3..a65998d 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -2896,8 +2896,9 @@  more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
        (arg) = next_aggr_init_expr_arg (&(iter)))
 
 /* VEC_INIT_EXPR accessors.  */
-#define VEC_INIT_EXPR_SLOT(NODE) TREE_OPERAND (NODE, 0)
-#define VEC_INIT_EXPR_INIT(NODE) TREE_OPERAND (NODE, 1)
+#define VEC_INIT_EXPR_SLOT(NODE) TREE_OPERAND (VEC_INIT_EXPR_CHECK (NODE), 0)
+#define VEC_INIT_EXPR_INIT(NODE) TREE_OPERAND (VEC_INIT_EXPR_CHECK (NODE), 1)
+#define VEC_INIT_EXPR_NELTS(NODE) TREE_OPERAND (VEC_INIT_EXPR_CHECK (NODE), 2)
 
 /* Indicates that a VEC_INIT_EXPR is a potential constant expression.
    Only set when the current function is constexpr.  */
@@ -5022,6 +5023,7 @@  extern tree get_copy_ctor			(tree);
 extern tree get_copy_assign			(tree);
 extern tree get_default_ctor			(tree);
 extern tree get_dtor				(tree);
+extern tree get_dtor_sfinae			(tree, tsubst_flags_t);
 extern tree locate_ctor				(tree);
 
 /* In optimize.c */
@@ -5418,7 +5420,7 @@  extern tree get_target_expr_sfinae		(tree, tsubst_flags_t);
 extern tree build_cplus_array_type		(tree, tree);
 extern tree build_array_of_n_type		(tree, int);
 extern tree build_array_copy			(tree);
-extern tree build_vec_init_expr			(tree, tree);
+extern tree build_vec_init_expr			(tree, tree, tree, tsubst_flags_t);
 extern void diagnose_non_constexpr_vec_init	(tree);
 extern tree hash_tree_cons			(tree, tree, tree);
 extern tree hash_tree_chain			(tree, tree);
diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index 25beba8..883734f 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -506,7 +506,8 @@  perform_member_init (tree member, tree init)
       /* mem() means value-initialization.  */
       if (TREE_CODE (type) == ARRAY_TYPE)
 	{
-	  init = build_vec_init_expr (type, init);
+	  init = build_vec_init_expr (type, init, NULL_TREE,
+				      tf_warning_or_error);
 	  init = build2 (INIT_EXPR, type, decl, init);
 	  finish_expr_stmt (init);
 	}
@@ -541,7 +542,8 @@  perform_member_init (tree member, tree init)
 	      || same_type_ignoring_top_level_qualifiers_p (type,
 							    TREE_TYPE (init)))
 	    {
-	      init = build_vec_init_expr (type, init);
+	      init = build_vec_init_expr (type, init, NULL_TREE,
+					  tf_warning_or_error);
 	      init = build2 (INIT_EXPR, type, decl, init);
 	      finish_expr_stmt (init);
 	    }
@@ -2384,15 +2386,14 @@  build_new_1 (VEC(tree,gc) **placement, tree type, tree nelts,
 	      vecinit = build_tree_list_vec (*init);
             }
 	  init_expr
-	    = build_vec_init (data_addr,
-			      cp_build_binary_op (input_location,
-						  MINUS_EXPR, outer_nelts,
-						  integer_one_node,
-						  complain),
-			      vecinit,
-			      explicit_value_init_p,
-			      /*from_array=*/0,
-                              complain);
+	    = build_vec_init_expr (data_addr,
+				   (explicit_value_init_p
+				    ? void_type_node: vecinit),
+				   cp_build_binary_op (input_location,
+						       MINUS_EXPR, outer_nelts,
+						       integer_one_node,
+						       complain),
+				   complain);
 
 	  /* An array initialization is stable because the initialization
 	     of each element is a full-expression, so the temporaries don't
diff --git a/gcc/cp/method.c b/gcc/cp/method.c
index 6b26806..8b1b4dc 100644
--- a/gcc/cp/method.c
+++ b/gcc/cp/method.c
@@ -843,10 +843,16 @@  locate_fn_flags (tree type, tree name, tree argtype, int flags,
 /* Locate the dtor of TYPE.  */
 
 tree
+get_dtor_sfinae (tree type, tsubst_flags_t complain)
+{
+  return locate_fn_flags (type, complete_dtor_identifier, NULL_TREE,
+			  LOOKUP_NORMAL, complain);
+}
+
+tree
 get_dtor (tree type)
 {
-  tree fn = locate_fn_flags (type, complete_dtor_identifier, NULL_TREE,
-			     LOOKUP_NORMAL, tf_warning_or_error);
+  tree fn = get_dtor_sfinae (type, tf_warning_or_error);
   if (fn == error_mark_node)
     return NULL_TREE;
   return fn;
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 8fe8832..2f082a6 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -473,45 +473,80 @@  build_cplus_new (tree type, tree init, tsubst_flags_t complain)
    another array to copy.  */
 
 static tree
-build_vec_init_elt (tree type, tree init)
+build_vec_init_elt (tree type, tree init, tsubst_flags_t complain)
 {
-  tree inner_type = strip_array_types (type);
+  tree inner_type = strip_array_types (TREE_TYPE (type));
   VEC(tree,gc) *argvec;
 
-  if (integer_zerop (array_type_nelts_total (type))
-      || !CLASS_TYPE_P (inner_type))
+  if (!CLASS_TYPE_P (inner_type))
     /* No interesting initialization to do.  */
     return integer_zero_node;
   else if (init == void_type_node)
     return build_value_init (inner_type, tf_warning_or_error);
 
-  gcc_assert (init == NULL_TREE
-	      || (same_type_ignoring_top_level_qualifiers_p
-		  (type, TREE_TYPE (init))));
-
-  argvec = make_tree_vector ();
-  if (init)
+  if (init == NULL_TREE)
+    argvec = make_tree_vector ();
+  else if (TREE_CODE (init) == TREE_LIST)
+    /* Array init extension, i.e. g++.robertl/eb58.C. */
+    argvec = make_tree_vector_from_list (init);
+  else if (same_type_ignoring_top_level_qualifiers_p
+	   (inner_type, strip_array_types (TREE_TYPE (init))))
     {
+      /* Array copy or list-initialization.  */
       tree dummy = build_dummy_object (inner_type);
       if (!real_lvalue_p (init))
 	dummy = move (dummy);
-      VEC_quick_push (tree, argvec, dummy);
+      argvec = make_tree_vector_single (dummy);
     }
-  return build_special_member_call (NULL_TREE, complete_ctor_identifier,
+  else
+    gcc_unreachable ();
+  init = build_special_member_call (NULL_TREE, complete_ctor_identifier,
 				    &argvec, inner_type, LOOKUP_NORMAL,
-				    tf_warning_or_error);
+				    complain);
+  release_tree_vector (argvec);
+
+  /* For array new, also mark the destructor as used.  */
+  if (TREE_CODE (type) == POINTER_TYPE
+      && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (inner_type))
+    {
+      tree dtor = get_dtor_sfinae (inner_type, complain);
+      if (dtor == error_mark_node)
+	return error_mark_node;
+      else if (dtor)
+	mark_used (dtor);
+    }
+  return init;
 }
 
-/* Return a TARGET_EXPR which expresses the initialization of an array to
-   be named later, either default-initialization or copy-initialization
-   from another array of the same type.  */
+/* Return a TARGET_EXPR which expresses the initialization of an array.  If
+   TARGET is an array type, the initialization is of an array to be named
+   later, and the initialization will be wrapped in a TARGET_EXPR.  If
+   TARGET is an expression, it is the array to be initialized.  INIT is the
+   initializer, or void_type_node for value-initialization.  If TARGET is
+   an expression, NELTS is the number of elements to initialize. */
 
 tree
-build_vec_init_expr (tree type, tree init)
+build_vec_init_expr (tree target, tree init, tree nelts,
+		     tsubst_flags_t complain)
 {
-  tree slot;
+  tree slot, type;
   bool value_init = false;
-  tree elt_init = build_vec_init_elt (type, init);
+  tree elt_init;
+  tree real_nelts;
+
+  if (TYPE_P (target))
+    {
+      gcc_assert (TREE_CODE (target) == ARRAY_TYPE && nelts == NULL_TREE);
+      type = target;
+      slot = build_local_temp (type);
+    }
+  else
+    {
+      gcc_assert (EXPR_P (target));
+      slot = target;
+      type = TREE_TYPE (slot);
+      gcc_assert (TREE_CODE (type) == POINTER_TYPE && nelts != NULL_TREE);
+    }
 
   if (init == void_type_node)
     {
@@ -519,8 +554,14 @@  build_vec_init_expr (tree type, tree init)
       init = NULL_TREE;
     }
 
-  slot = build_local_temp (type);
-  init = build2 (VEC_INIT_EXPR, type, slot, init);
+  real_nelts = nelts ? nelts : array_type_nelts_total (type);
+  if (integer_zerop (real_nelts))
+    /* No elements to initialize.  */
+    elt_init = integer_zero_node;
+  else
+    elt_init = build_vec_init_elt (type, init, complain);
+
+  init = build3 (VEC_INIT_EXPR, type, slot, init, nelts);
   SET_EXPR_LOCATION (init, input_location);
 
   if (cxx_dialect >= cxx0x
@@ -528,8 +569,11 @@  build_vec_init_expr (tree type, tree init)
     VEC_INIT_EXPR_IS_CONSTEXPR (init) = true;
   VEC_INIT_EXPR_VALUE_INIT (init) = value_init;
 
-  init = build_target_expr (slot, init, tf_warning_or_error);
-  TARGET_EXPR_IMPLICIT_P (init) = 1;
+  if (slot != target)
+    {
+      init = build_target_expr (slot, init, complain);
+      TARGET_EXPR_IMPLICIT_P (init) = 1;
+    }
 
   return init;
 }
@@ -547,14 +591,15 @@  diagnose_non_constexpr_vec_init (tree expr)
   else
     init = VEC_INIT_EXPR_INIT (expr);
 
-  elt_init = build_vec_init_elt (type, init);
+  elt_init = build_vec_init_elt (type, init, tf_warning_or_error);
   require_potential_constant_expression (elt_init);
 }
 
 tree
 build_array_copy (tree init)
 {
-  return build_vec_init_expr (TREE_TYPE (init), init);
+  return build_vec_init_expr (TREE_TYPE (init), init, NULL_TREE,
+			      tf_warning_or_error);
 }
 
 /* Build a TARGET_EXPR using INIT to initialize a new temporary of the
diff --git a/gcc/testsuite/g++.dg/cpp0x/initlist49.C b/gcc/testsuite/g++.dg/cpp0x/initlist49.C
new file mode 100644
index 0000000..752c433
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/initlist49.C
@@ -0,0 +1,18 @@ 
+// Test for non-trivial list-initialization with array new.
+// { dg-options -std=c++0x }
+// { dg-do run }
+
+struct A
+{
+  enum E { c_string, number } e;
+  A(const char *): e(c_string) {}
+  A(int): e(number) {}
+};
+
+int main()
+{
+  A* ap = new A[2]{1, ""};
+  if (ap[0].e != A::number || ap[1].e != A::c_string)
+    return 1;
+  delete[] ap;
+}
diff --git a/gcc/testsuite/g++.dg/init/new30.C b/gcc/testsuite/g++.dg/init/new30.C
new file mode 100644
index 0000000..24582d8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/init/new30.C
@@ -0,0 +1,15 @@ 
+// PR c++/40975
+
+struct data_type
+{
+    // constructor required to reproduce compiler bug
+    data_type() {}
+};
+
+struct ptr_type
+{
+    // array new as default argument required to reproduce compiler bug
+    ptr_type (data_type* ptr = new data_type[1]) { delete[] ptr; }
+};
+
+ptr_type obj;