Patchwork C++ PATCH for c++/48370 (extending lifetime of temps in aggregate initialization)

login
register
mail settings
Submitter Jason Merrill
Date Nov. 4, 2011, 3:50 a.m.
Message ID <4EB36120.3060603@redhat.com>
Download mbox | patch
Permalink /patch/123563/
State New
Headers show

Comments

Jason Merrill - Nov. 4, 2011, 3:50 a.m.
12.2 states that a temporary bound to a reference lives as long as the 
reference itself.  We have done that for reference variables, but not in 
other cases, such as aggregate initialization of a struct with reference 
members.  In C++11, elements of a std::initializer_list have the same 
semantics; they live as long as the std::initializer_list.  Again, we 
were implementing that for initializer_list variables but not for 
initializer_list subobjects.  This patch fixes that.

Furthermore, if a temporary's lifetime is extended, we need to also 
extend the lifetimes of any temporaries bound to references in its 
initializer, and so on.

The patch introduces a function extend_ref_init_temps called from 
store_init_value after the call to digest_init.  To expose elements of 
an initializer_list to this function, I needed to stop using 
build_aggr_init_full_exprs for aggregate initialization of arrays, and 
consequently needed to call build_vec_init from store_init_value to use 
one EH region for cleaning up the whole array rather than one per 
element.  To deal with multiple extended temporaries, we need to change 
the cleanup pointer from a single tree to a VEC, and add a discriminator 
to the mangled name of reference init temporaries; this has no ABI 
impact, since the temporaries have no linkage, but I also updated the 
demangler accordingly.

Since we now do lifetime extension in extend_ref_init_temps, we can 
drastically simplify initialize_reference and do away with 
build_init_list_var_init.

Tested x86_64-pc-linux-gnu, applying to trunk.
H.J. Lu - June 13, 2012, 5 a.m.
On Thu, Nov 3, 2011 at 8:50 PM, Jason Merrill <jason@redhat.com> wrote:
> 12.2 states that a temporary bound to a reference lives as long as the
> reference itself.  We have done that for reference variables, but not in
> other cases, such as aggregate initialization of a struct with reference
> members.  In C++11, elements of a std::initializer_list have the same
> semantics; they live as long as the std::initializer_list.  Again, we were
> implementing that for initializer_list variables but not for
> initializer_list subobjects.  This patch fixes that.
>
> Furthermore, if a temporary's lifetime is extended, we need to also extend
> the lifetimes of any temporaries bound to references in its initializer, and
> so on.
>
> The patch introduces a function extend_ref_init_temps called from
> store_init_value after the call to digest_init.  To expose elements of an
> initializer_list to this function, I needed to stop using
> build_aggr_init_full_exprs for aggregate initialization of arrays, and
> consequently needed to call build_vec_init from store_init_value to use one
> EH region for cleaning up the whole array rather than one per element.  To
> deal with multiple extended temporaries, we need to change the cleanup
> pointer from a single tree to a VEC, and add a discriminator to the mangled
> name of reference init temporaries; this has no ABI impact, since the
> temporaries have no linkage, but I also updated the demangler accordingly.
>
> Since we now do lifetime extension in extend_ref_init_temps, we can
> drastically simplify initialize_reference and do away with
> build_init_list_var_init.
>
> Tested x86_64-pc-linux-gnu, applying to trunk.

This caused:

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53650
H.J. Lu - Aug. 7, 2012, 7:56 p.m.
On Tue, Jun 12, 2012 at 10:00 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Thu, Nov 3, 2011 at 8:50 PM, Jason Merrill <jason@redhat.com> wrote:
>> 12.2 states that a temporary bound to a reference lives as long as the
>> reference itself.  We have done that for reference variables, but not in
>> other cases, such as aggregate initialization of a struct with reference
>> members.  In C++11, elements of a std::initializer_list have the same
>> semantics; they live as long as the std::initializer_list.  Again, we were
>> implementing that for initializer_list variables but not for
>> initializer_list subobjects.  This patch fixes that.
>>
>> Furthermore, if a temporary's lifetime is extended, we need to also extend
>> the lifetimes of any temporaries bound to references in its initializer, and
>> so on.
>>
>> The patch introduces a function extend_ref_init_temps called from
>> store_init_value after the call to digest_init.  To expose elements of an
>> initializer_list to this function, I needed to stop using
>> build_aggr_init_full_exprs for aggregate initialization of arrays, and
>> consequently needed to call build_vec_init from store_init_value to use one
>> EH region for cleaning up the whole array rather than one per element.  To
>> deal with multiple extended temporaries, we need to change the cleanup
>> pointer from a single tree to a VEC, and add a discriminator to the mangled
>> name of reference init temporaries; this has no ABI impact, since the
>> temporaries have no linkage, but I also updated the demangler accordingly.
>>
>> Since we now do lifetime extension in extend_ref_init_temps, we can
>> drastically simplify initialize_reference and do away with
>> build_init_list_var_init.
>>
>> Tested x86_64-pc-linux-gnu, applying to trunk.
>
> This caused:
>
> http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53650
>

This also caused:

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54197

Patch

commit 82e649dc7b7918d48b14a88dc2fda9836e421cd9
Author: Jason Merrill <jason@redhat.com>
Date:   Sat Oct 22 11:40:16 2011 -0400

    	PR c++/48370
    	* call.c (extend_ref_init_temps, extend_ref_init_temps_1): New.
    	(set_up_extended_ref_temp): Use it.  Change cleanup parm to VEC.
    	(initialize_reference): Just call convert_like.
    	* decl.c (grok_reference_init): Just call initialize_reference.
    	(build_init_list_var_init): Remove.
    	(check_initializer): Change cleanup parm to VEC.  Handle references
    	like other types.  Call perform_implicit_conversion instead
    	of build_init_list_var_init.  Don't use build_aggr_init for
    	aggregate initialization of arrays.
    	(cp_finish_decl): Change cleanup to VEC.
    	* typeck2.c (store_init_value): Call extend_ref_init_temps.
    	Use build_vec_init for non-constant arrays.
    	* init.c (expand_aggr_init_1): Adjust.
    	(build_vec_init): Avoid re-converting an initializer
    	that's already digested.
    	* mangle.c (mangle_ref_init_variable): Add a discriminator.
    	* cp-tree.h: Adjust.
    	* typeck.c (convert_for_initialization): Adjust.
    	* decl2.c (maybe_emit_vtables): Adjust.

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index ce8933a..4d7facc 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -8502,6 +8502,44 @@  perform_direct_initialization_if_possible (tree type,
   return expr;
 }
 
+/* When initializing a reference that lasts longer than a full-expression,
+   this special rule applies:
+
+     [class.temporary]
+
+     The temporary to which the reference is bound or the temporary
+     that is the complete object to which the reference is bound
+     persists for the lifetime of the reference.
+
+     The temporaries created during the evaluation of the expression
+     initializing the reference, except the temporary to which the
+     reference is bound, are destroyed at the end of the
+     full-expression in which they are created.
+
+   In that case, we store the converted expression into a new
+   VAR_DECL in a new scope.
+
+   However, we want to be careful not to create temporaries when
+   they are not required.  For example, given:
+
+     struct B {};
+     struct D : public B {};
+     D f();
+     const B& b = f();
+
+   there is no need to copy the return value from "f"; we can just
+   extend its lifetime.  Similarly, given:
+
+     struct S {};
+     struct T { operator S(); };
+     T t;
+     const S& s = t;
+
+  we can extend the lifetime of the return value of the conversion
+  operator.
+
+  The next several functions are involved in this lifetime extension.  */
+
 /* DECL is a VAR_DECL whose type is a REFERENCE_TYPE.  The reference
    is being bound to a temporary.  Create and return a new VAR_DECL
    with the indicated TYPE; this variable will store the value to
@@ -8519,6 +8557,7 @@  make_temporary_var_for_ref_to_temp (tree decl, tree type)
   if (TREE_STATIC (decl))
     {
       /* Namespace-scope or local static; give it a mangled name.  */
+      /* FIXME share comdat with decl?  */
       tree name;
 
       TREE_STATIC (var) = 1;
@@ -8540,8 +8579,9 @@  make_temporary_var_for_ref_to_temp (tree decl, tree type)
    cleanup for the new variable is returned through CLEANUP, and the
    code to initialize the new variable is returned through INITP.  */
 
-tree
-set_up_extended_ref_temp (tree decl, tree expr, tree *cleanup, tree *initp)
+static tree
+set_up_extended_ref_temp (tree decl, tree expr, VEC(tree,gc) **cleanups,
+			  tree *initp)
 {
   tree init;
   tree type;
@@ -8562,6 +8602,10 @@  set_up_extended_ref_temp (tree decl, tree expr, tree *cleanup, tree *initp)
   if (TREE_CODE (expr) != TARGET_EXPR)
     expr = get_target_expr (expr);
 
+  /* Recursively extend temps in this initializer.  */
+  TARGET_EXPR_INITIAL (expr)
+    = extend_ref_init_temps (decl, TARGET_EXPR_INITIAL (expr), cleanups);
+
   /* If the initializer is constant, put it in DECL_INITIAL so we get
      static initialization and use in constant expressions.  */
   init = maybe_constant_init (expr);
@@ -8595,7 +8639,11 @@  set_up_extended_ref_temp (tree decl, tree expr, tree *cleanup, tree *initp)
       if (TREE_STATIC (var))
 	init = add_stmt_to_compound (init, register_dtor_fn (var));
       else
-	*cleanup = cxx_maybe_build_cleanup (var, tf_warning_or_error);
+	{
+	  tree cleanup = cxx_maybe_build_cleanup (var, tf_warning_or_error);
+	  if (cleanup)
+	    VEC_safe_push (tree, gc, *cleanups, cleanup);
+	}
 
       /* We must be careful to destroy the temporary only
 	 after its initialization has taken place.  If the
@@ -8629,18 +8677,10 @@  set_up_extended_ref_temp (tree decl, tree expr, tree *cleanup, tree *initp)
 }
 
 /* Convert EXPR to the indicated reference TYPE, in a way suitable for
-   initializing a variable of that TYPE.  If DECL is non-NULL, it is
-   the VAR_DECL being initialized with the EXPR.  (In that case, the
-   type of DECL will be TYPE.)  If DECL is non-NULL, then CLEANUP must
-   also be non-NULL, and with *CLEANUP initialized to NULL.  Upon
-   return, if *CLEANUP is no longer NULL, it will be an expression
-   that should be pushed as a cleanup after the returned expression
-   is used to initialize DECL.
-
-   Return the converted expression.  */
+   initializing a variable of that TYPE.  */
 
 tree
-initialize_reference (tree type, tree expr, tree decl, tree *cleanup,
+initialize_reference (tree type, tree expr,
 		      int flags, tsubst_flags_t complain)
 {
   conversion *conv;
@@ -8674,98 +8714,10 @@  initialize_reference (tree type, tree expr, tree decl, tree *cleanup,
       return error_mark_node;
     }
 
-  /* If DECL is non-NULL, then this special rule applies:
-
-       [class.temporary]
-
-       The temporary to which the reference is bound or the temporary
-       that is the complete object to which the reference is bound
-       persists for the lifetime of the reference.
-
-       The temporaries created during the evaluation of the expression
-       initializing the reference, except the temporary to which the
-       reference is bound, are destroyed at the end of the
-       full-expression in which they are created.
-
-     In that case, we store the converted expression into a new
-     VAR_DECL in a new scope.
-
-     However, we want to be careful not to create temporaries when
-     they are not required.  For example, given:
-
-       struct B {};
-       struct D : public B {};
-       D f();
-       const B& b = f();
-
-     there is no need to copy the return value from "f"; we can just
-     extend its lifetime.  Similarly, given:
-
-       struct S {};
-       struct T { operator S(); };
-       T t;
-       const S& s = t;
-
-    we can extend the lifetime of the return value of the conversion
-    operator.  */
   gcc_assert (conv->kind == ck_ref_bind);
-  if (decl)
-    {
-      tree var;
-      tree base_conv_type;
 
-      gcc_assert (complain == tf_warning_or_error);
-
-      /* Skip over the REF_BIND.  */
-      conv = conv->u.next;
-      /* If the next conversion is a BASE_CONV, skip that too -- but
-	 remember that the conversion was required.  */
-      if (conv->kind == ck_base)
-	{
-	  base_conv_type = conv->type;
-	  conv = conv->u.next;
-	}
-      else
-	base_conv_type = NULL_TREE;
-      /* Perform the remainder of the conversion.  */
-      expr = convert_like_real (conv, expr,
-				/*fn=*/NULL_TREE, /*argnum=*/0,
-				/*inner=*/-1,
-				/*issue_conversion_warnings=*/true,
-				/*c_cast_p=*/false,
-				complain);
-      if (error_operand_p (expr))
-	expr = error_mark_node;
-      else
-	{
-	  if (!lvalue_or_rvalue_with_address_p (expr))
-	    {
-	      tree init;
-	      var = set_up_extended_ref_temp (decl, expr, cleanup, &init);
-	      /* Use its address to initialize the reference variable.  */
-	      expr = build_address (var);
-	      if (base_conv_type)
-		expr = convert_to_base (expr,
-					build_pointer_type (base_conv_type),
-					/*check_access=*/true,
-					/*nonnull=*/true, complain);
-	      if (init)
-		expr = build2 (COMPOUND_EXPR, TREE_TYPE (expr), init, expr);
-	    }
-	  else
-	    /* Take the address of EXPR.  */
-	    expr = cp_build_addr_expr (expr, complain);
-	  /* If a BASE_CONV was required, perform it now.  */
-	  if (base_conv_type)
-	    expr = (perform_implicit_conversion
-		    (build_pointer_type (base_conv_type), expr,
-		     complain));
-	  expr = build_nop (type, expr);
-	}
-    }
-  else
-    /* Perform the conversion.  */
-    expr = convert_like (conv, expr, complain);
+  /* Perform the conversion.  */
+  expr = convert_like (conv, expr, complain);
 
   /* Free all the conversions we allocated.  */
   obstack_free (&conversion_obstack, p);
@@ -8773,6 +8725,68 @@  initialize_reference (tree type, tree expr, tree decl, tree *cleanup,
   return expr;
 }
 
+/* Subroutine of extend_ref_init_temps.  Possibly extend one initializer,
+   which is bound either to a reference or a std::initializer_list.  */
+
+static tree
+extend_ref_init_temps_1 (tree decl, tree init, VEC(tree,gc) **cleanups)
+{
+  tree sub = init;
+  tree *p;
+  STRIP_NOPS (sub);
+  if (TREE_CODE (sub) != ADDR_EXPR)
+    return init;
+  /* Deal with binding to a subobject.  */
+  for (p = &TREE_OPERAND (sub, 0); TREE_CODE (*p) == COMPONENT_REF; )
+    p = &TREE_OPERAND (*p, 0);
+  if (TREE_CODE (*p) == TARGET_EXPR)
+    {
+      tree subinit = NULL_TREE;
+      *p = set_up_extended_ref_temp (decl, *p, cleanups, &subinit);
+      if (subinit)
+	init = build2 (COMPOUND_EXPR, TREE_TYPE (init), subinit, init);
+    }
+  return init;
+}
+
+/* INIT is part of the initializer for DECL.  If there are any
+   reference or initializer lists being initialized, extend their
+   lifetime to match that of DECL.  */
+
+tree
+extend_ref_init_temps (tree decl, tree init, VEC(tree,gc) **cleanups)
+{
+  tree type = TREE_TYPE (init);
+  if (processing_template_decl)
+    return init;
+  if (TREE_CODE (type) == REFERENCE_TYPE)
+    init = extend_ref_init_temps_1 (decl, init, cleanups);
+  else if (is_std_init_list (type))
+    {
+      /* The temporary array underlying a std::initializer_list
+	 is handled like a reference temporary.  */
+      tree ctor = init;
+      if (TREE_CODE (ctor) == TARGET_EXPR)
+	ctor = TARGET_EXPR_INITIAL (ctor);
+      if (TREE_CODE (ctor) == CONSTRUCTOR)
+	{
+	  tree array = CONSTRUCTOR_ELT (ctor, 0)->value;
+	  array = extend_ref_init_temps_1 (decl, array, cleanups);
+	  CONSTRUCTOR_ELT (ctor, 0)->value = array;
+	}
+    }
+  else if (TREE_CODE (init) == CONSTRUCTOR)
+    {
+      unsigned i;
+      constructor_elt *p;
+      VEC(constructor_elt,gc) *elts = CONSTRUCTOR_ELTS (init);
+      FOR_EACH_VEC_ELT (constructor_elt, elts, i, p)
+	p->value = extend_ref_init_temps (decl, p->value, cleanups);
+    }
+
+  return init;
+}
+
 /* Returns true iff TYPE is some variant of std::initializer_list.  */
 
 bool
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index ac42e0e..dc52d29 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -4810,9 +4810,9 @@  extern tree cxx_type_promotes_to		(tree);
 extern tree type_passed_as			(tree);
 extern tree convert_for_arg_passing		(tree, tree);
 extern bool is_properly_derived_from		(tree, tree);
-extern tree set_up_extended_ref_temp		(tree, tree, tree *, tree *);
-extern tree initialize_reference		(tree, tree, tree, tree *, int,
+extern tree initialize_reference		(tree, tree, int,
 						 tsubst_flags_t);
+extern tree extend_ref_init_temps		(tree, tree, VEC(tree,gc)**);
 extern tree make_temporary_var_for_ref_to_temp	(tree, tree);
 extern tree strip_top_quals			(tree);
 extern bool reference_related_p			(tree, tree);
@@ -5793,7 +5793,7 @@  extern void complete_type_check_abstract	(tree);
 extern int abstract_virtuals_error		(tree, tree);
 extern int abstract_virtuals_error_sfinae	(tree, tree, tsubst_flags_t);
 
-extern tree store_init_value			(tree, tree, int);
+extern tree store_init_value			(tree, tree, VEC(tree,gc)**, int);
 extern void check_narrowing			(tree, tree);
 extern tree digest_init				(tree, tree, tsubst_flags_t);
 extern tree digest_init_flags			(tree, tree, int);
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index edbc783..50c45de 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -71,7 +71,7 @@  static void require_complete_types_for_parms (tree);
 static int ambi_op_p (enum tree_code);
 static int unary_op_p (enum tree_code);
 static void push_local_name (tree);
-static tree grok_reference_init (tree, tree, tree, tree *, int);
+static tree grok_reference_init (tree, tree, tree, int);
 static tree grokvardecl (tree, tree, const cp_decl_specifier_seq *,
 			 int, int, tree);
 static int check_static_variable_definition (tree, tree);
@@ -91,7 +91,7 @@  static tree lookup_and_check_tag (enum tag_types, tree, tag_scope, bool);
 static int walk_namespaces_r (tree, walk_namespaces_fn, void *);
 static void maybe_deduce_size_from_array_init (tree, tree);
 static void layout_var_decl (tree);
-static tree check_initializer (tree, tree, int, tree *);
+static tree check_initializer (tree, tree, int, VEC(tree,gc) **);
 static void make_rtl_for_nonlocal_decl (tree, tree, const char *);
 static void save_function_data (tree);
 static void copy_type_enum (tree , tree);
@@ -4611,11 +4611,8 @@  start_decl_1 (tree decl, bool initialized)
    Quotes on semantics can be found in ARM 8.4.3.  */
 
 static tree
-grok_reference_init (tree decl, tree type, tree init, tree *cleanup,
-		     int flags)
+grok_reference_init (tree decl, tree type, tree init, int flags)
 {
-  tree tmp;
-
   if (init == NULL_TREE)
     {
       if ((DECL_LANG_SPECIFIC (decl) == 0
@@ -4641,62 +4638,8 @@  grok_reference_init (tree decl, tree type, tree init, tree *cleanup,
      DECL_INITIAL for local references (instead assigning to them
      explicitly); we need to allow the temporary to be initialized
      first.  */
-  tmp = initialize_reference (type, init, decl, cleanup, flags,
-			      tf_warning_or_error);
-  if (DECL_DECLARED_CONSTEXPR_P (decl))
-    {
-      tmp = cxx_constant_value (tmp);
-      DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl)
-	= reduced_constant_expression_p (tmp);
-    }
-
-  if (tmp == error_mark_node)
-    return NULL_TREE;
-  else if (tmp == NULL_TREE)
-    {
-      error ("cannot initialize %qT from %qT", type, TREE_TYPE (init));
-      return NULL_TREE;
-    }
-
-  if (TREE_STATIC (decl) && !TREE_CONSTANT (tmp))
-    return tmp;
-
-  DECL_INITIAL (decl) = tmp;
-
-  return NULL_TREE;
-}
-
-/* Subroutine of check_initializer.  We're initializing a DECL of
-   std::initializer_list<T> TYPE from a braced-init-list INIT, and need to
-   extend the lifetime of the underlying array to match that of the decl,
-   just like for reference initialization.  CLEANUP is as for
-   grok_reference_init.  */
-
-static tree
-build_init_list_var_init (tree decl, tree type, tree init, tree *array_init,
-			  tree *cleanup)
-{
-  tree aggr_init, array, arrtype;
-  init = perform_implicit_conversion (type, init, tf_warning_or_error);
-  if (error_operand_p (init))
-    return error_mark_node;
-
-  aggr_init = TARGET_EXPR_INITIAL (init);
-  array = CONSTRUCTOR_ELT (aggr_init, 0)->value;
-  arrtype = TREE_TYPE (array);
-  STRIP_NOPS (array);
-  gcc_assert (TREE_CODE (array) == ADDR_EXPR);
-  array = TREE_OPERAND (array, 0);
-  /* If the array is constant, finish_compound_literal already made it a
-     static variable and we don't need to do anything here.  */
-  if (decl && TREE_CODE (array) == TARGET_EXPR)
-    {
-      tree var = set_up_extended_ref_temp (decl, array, cleanup, array_init);
-      var = build_address (var);
-      var = convert (arrtype, var);
-      CONSTRUCTOR_ELT (aggr_init, 0)->value = var;
-    }
-  return init;
+  return initialize_reference (type, init, flags,
+			       tf_warning_or_error);
 }
 
 /* Designated initializers in arrays are not supported in GNU C++.
@@ -5440,7 +5383,7 @@  build_aggr_init_full_exprs (tree decl, tree init, int flags)
    evaluated dynamically to initialize DECL.  */
 
 static tree
-check_initializer (tree decl, tree init, int flags, tree *cleanup)
+check_initializer (tree decl, tree init, int flags, VEC(tree,gc) **cleanups)
 {
   tree type = TREE_TYPE (decl);
   tree init_code = NULL;
@@ -5509,19 +5452,26 @@  check_initializer (tree decl, tree init, int flags, tree *cleanup)
     }
   else if (!init && DECL_REALLY_EXTERN (decl))
     ;
-  else if (TREE_CODE (type) == REFERENCE_TYPE)
-    init = grok_reference_init (decl, type, init, cleanup, flags);
-  else if (init || type_build_ctor_call (type))
+  else if (init || type_build_ctor_call (type)
+	   || TREE_CODE (type) == REFERENCE_TYPE)
     {
-      if (!init)
+      if (TREE_CODE (type) == REFERENCE_TYPE)
+	{
+	  init = grok_reference_init (decl, type, init, flags);
+	  flags |= LOOKUP_ALREADY_DIGESTED;
+	}
+      else if (!init)
 	check_for_uninitialized_const_var (decl);
       /* Do not reshape constructors of vectors (they don't need to be
 	 reshaped.  */
       else if (BRACE_ENCLOSED_INITIALIZER_P (init))
 	{
 	  if (is_std_init_list (type))
-	    init = build_init_list_var_init (decl, type, init,
-					     &extra_init, cleanup);
+	    {
+	      init = perform_implicit_conversion (type, init,
+						  tf_warning_or_error);
+	      flags |= LOOKUP_ALREADY_DIGESTED;
+	    }
 	  else if (TYPE_NON_AGGREGATE_CLASS (type))
 	    {
 	      /* Don't reshape if the class has constructors.  */
@@ -5550,9 +5500,10 @@  check_initializer (tree decl, tree init, int flags, tree *cleanup)
       if (type == error_mark_node)
 	return NULL_TREE;
 
-      if (type_build_ctor_call (type)
-	  || (CLASS_TYPE_P (type)
-	      && !(init && BRACE_ENCLOSED_INITIALIZER_P (init))))
+      if ((type_build_ctor_call (type) || CLASS_TYPE_P (type))
+	  && !(flags & LOOKUP_ALREADY_DIGESTED)
+	  && !(init && BRACE_ENCLOSED_INITIALIZER_P (init)
+	       && CP_AGGREGATE_TYPE_P (type)))
 	{
 	  init_code = build_aggr_init_full_exprs (decl, init, flags);
 
@@ -5594,7 +5545,7 @@  check_initializer (tree decl, tree init, int flags, tree *cleanup)
 
       if (init && TREE_CODE (init) != TREE_VEC)
 	{
-	  init_code = store_init_value (decl, init, flags);
+	  init_code = store_init_value (decl, init, cleanups, flags);
 	  if (pedantic && TREE_CODE (type) == ARRAY_TYPE
 	      && DECL_INITIAL (decl)
 	      && TREE_CODE (DECL_INITIAL (decl)) == STRING_CST
@@ -5956,7 +5907,7 @@  cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
 		tree asmspec_tree, int flags)
 {
   tree type;
-  tree cleanup;
+  VEC(tree,gc) *cleanups = NULL;
   const char *asmspec = NULL;
   int was_readonly = 0;
   bool var_definition_p = false;
@@ -5979,9 +5930,6 @@  cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
   if (type == error_mark_node)
     return;
 
-  /* Assume no cleanup is required.  */
-  cleanup = NULL_TREE;
-
   /* If a name was specified, get the string.  */
   if (at_namespace_scope_p ())
     asmspec_tree = maybe_apply_renaming_pragma (decl, asmspec_tree);
@@ -6101,7 +6049,7 @@  cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
 	  /* This variable seems to be a non-dependent constant, so process
 	     its initializer.  If check_initializer returns non-null the
 	     initialization wasn't constant after all.  */
-	  tree init_code = check_initializer (decl, init, flags, &cleanup);
+	  tree init_code = check_initializer (decl, init, flags, &cleanups);
 	  if (init_code == NULL_TREE)
 	    init = NULL_TREE;
 	}
@@ -6202,7 +6150,7 @@  cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
 		error ("Java object %qD not allocated with %<new%>", decl);
 	      init = NULL_TREE;
 	    }
-	  init = check_initializer (decl, init, flags, &cleanup);
+	  init = check_initializer (decl, init, flags, &cleanups);
 	  /* Thread-local storage cannot be dynamically initialized.  */
 	  if (DECL_THREAD_LOCAL_P (decl) && init)
 	    {
@@ -6367,8 +6315,12 @@  cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
 
   /* If a CLEANUP_STMT was created to destroy a temporary bound to a
      reference, insert it in the statement-tree now.  */
-  if (cleanup)
-    push_cleanup (decl, cleanup, false);
+  if (cleanups)
+    {
+      unsigned i; tree t;
+      FOR_EACH_VEC_ELT_REVERSE (tree, cleanups, i, t)
+	push_cleanup (decl, t, false);
+    }
 
   if (was_readonly)
     TREE_READONLY (decl) = 1;
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index be9044b..32b5c7e 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -1877,10 +1877,12 @@  maybe_emit_vtables (tree ctype)
 
       if (TREE_TYPE (DECL_INITIAL (vtbl)) == 0)
 	{
-	  tree expr = store_init_value (vtbl, DECL_INITIAL (vtbl), LOOKUP_NORMAL);
+	  VEC(tree,gc)* cleanups = NULL;
+	  tree expr = store_init_value (vtbl, DECL_INITIAL (vtbl), &cleanups,
+					LOOKUP_NORMAL);
 
 	  /* It had better be all done at compile-time.  */
-	  gcc_assert (!expr);
+	  gcc_assert (!expr && !cleanups);
 	}
 
       /* Write it out.  */
diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index ec7ba0e..3881275 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -1597,12 +1597,14 @@  expand_aggr_init_1 (tree binfo, tree true_exp, tree exp, tree init, int flags,
   if (init && TREE_CODE (exp) == VAR_DECL
       && COMPOUND_LITERAL_P (init))
     {
+      VEC(tree,gc)* cleanups = NULL;
       /* If store_init_value returns NULL_TREE, the INIT has been
 	 recorded as the DECL_INITIAL for EXP.  That means there's
 	 nothing more we have to do.  */
-      init = store_init_value (exp, init, flags);
+      init = store_init_value (exp, init, &cleanups, flags);
       if (init)
 	finish_expr_stmt (init);
+      gcc_assert (!cleanups);
       return;
     }
 
@@ -3150,6 +3152,9 @@  build_vec_init (tree base, tree maxindex, tree init,
       bool try_const = (TREE_CODE (atype) == ARRAY_TYPE
 			&& (literal_type_p (inner_elt_type)
 			    || TYPE_HAS_CONSTEXPR_CTOR (inner_elt_type)));
+      /* If the constructor already has the array type, it's been through
+	 digest_init, so we shouldn't try to do anything more.  */
+      bool digested = same_type_p (atype, TREE_TYPE (init));
       bool saw_non_const = false;
       bool saw_const = false;
       /* If we're initializing a static array, we want to do static
@@ -3172,7 +3177,9 @@  build_vec_init (tree base, tree maxindex, tree init,
 	  num_initialized_elts++;
 
 	  current_stmt_tree ()->stmts_are_full_exprs_p = 1;
-	  if (MAYBE_CLASS_TYPE_P (type) || TREE_CODE (type) == ARRAY_TYPE)
+	  if (digested)
+	    one_init = build2 (INIT_EXPR, type, baseref, elt);
+	  else if (MAYBE_CLASS_TYPE_P (type) || TREE_CODE (type) == ARRAY_TYPE)
 	    one_init = build_aggr_init (baseref, elt, 0, complain);
 	  else
 	    one_init = cp_build_modify_expr (baseref, NOP_EXPR,
diff --git a/gcc/cp/mangle.c b/gcc/cp/mangle.c
index 69fe147..7c907b8 100644
--- a/gcc/cp/mangle.c
+++ b/gcc/cp/mangle.c
@@ -3503,12 +3503,17 @@  mangle_guard_variable (const tree variable)
    initialize a static reference.  This isn't part of the ABI, but we might
    as well call them something readable.  */
 
+static GTY(()) int temp_count;
+
 tree
 mangle_ref_init_variable (const tree variable)
 {
   start_mangling (variable);
   write_string ("_ZGR");
   write_name (variable, /*ignore_local_scope=*/0);
+  /* Avoid name clashes with aggregate initialization of multiple
+     references at once.  */
+  write_unsigned_number (temp_count++);
   return finish_mangling_get_identifier (/*warn=*/false);
 }
 
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 0b1f217..58bb14f 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -7561,8 +7561,7 @@  convert_for_initialization (tree exp, tree type, tree rhs, int flags,
 
       if (fndecl)
 	savew = warningcount, savee = errorcount;
-      rhs = initialize_reference (type, rhs, /*decl=*/NULL_TREE,
-				  /*cleanup=*/NULL, flags, complain);
+      rhs = initialize_reference (type, rhs, flags, complain);
       if (fndecl)
 	{
 	  if (warningcount > savew)
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index 1b43449..70edc2f 100644
--- a/gcc/cp/typeck2.c
+++ b/gcc/cp/typeck2.c
@@ -655,7 +655,7 @@  split_nonconstant_init (tree dest, tree init)
    for static variable.  In that case, caller must emit the code.  */
 
 tree
-store_init_value (tree decl, tree init, int flags)
+store_init_value (tree decl, tree init, VEC(tree,gc)** cleanups, int flags)
 {
   tree value, type;
 
@@ -699,6 +699,8 @@  store_init_value (tree decl, tree init, int flags)
     /* Digest the specified initializer into an expression.  */
     value = digest_init_flags (type, init, flags);
 
+  value = extend_ref_init_temps (decl, value, cleanups);
+
   /* In C++0x constant expression is a semantic, not syntactic, property.
      In C++98, make sure that what we thought was a constant expression at
      template definition time is still constant.  */
@@ -725,7 +727,16 @@  store_init_value (tree decl, tree init, int flags)
   if (value != error_mark_node
       && (TREE_SIDE_EFFECTS (value)
 	   || ! initializer_constant_valid_p (value, TREE_TYPE (value))))
-    return split_nonconstant_init (decl, value);
+    {
+      if (TREE_CODE (type) == ARRAY_TYPE
+	  && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (TREE_TYPE (type)))
+	/* For an array, we only need/want a single cleanup region rather
+	   than one per element.  */
+	return build_vec_init (decl, NULL_TREE, value, false, 1,
+			       tf_warning_or_error);
+      else
+	return split_nonconstant_init (decl, value);
+    }
   /* If the value is a constant, just put it in DECL_INITIAL.  If DECL
      is an automatic variable, the middle end will turn this into a
      dynamic initialization later.  */
diff --git a/gcc/testsuite/g++.dg/cpp0x/initlist-lifetime1.C b/gcc/testsuite/g++.dg/cpp0x/initlist-lifetime1.C
new file mode 100644
index 0000000..e43ce5d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/initlist-lifetime1.C
@@ -0,0 +1,34 @@ 
+// Test that we properly extend the lifetime of the initializer_list
+// array even if the initializer_list is a subobject.
+// { dg-options -std=c++0x }
+// { dg-do run }
+
+#include <initializer_list>
+
+extern "C" void abort();
+bool ok;
+
+bool do_throw;
+
+struct A {
+  A(int) { if (do_throw) throw 42; }
+  ~A() { if (!ok) abort(); }
+};
+
+typedef std::initializer_list<A> AL;
+typedef std::initializer_list<AL> AL2;
+typedef std::initializer_list<AL2> AL3;
+
+struct B {
+  AL al;
+  const AL& alr;
+};
+
+int main(int argc, const char** argv)
+{
+  do_throw = (argc > 1);	// always false, but optimizer can't tell
+  AL ar[] = {{1,2},{3,4}};
+  B b = {{5,6},{7,8}};
+  AL3 al3 = {{{1},{2},{3}}};
+  ok = true;
+}
diff --git a/gcc/testsuite/g++.dg/eh/array1.C b/gcc/testsuite/g++.dg/eh/array1.C
new file mode 100644
index 0000000..ceb9824
--- /dev/null
+++ b/gcc/testsuite/g++.dg/eh/array1.C
@@ -0,0 +1,16 @@ 
+// Test that we have one EH cleanup region for the whole array
+// rather than one for each element.
+// { dg-options -fdump-tree-gimple }
+// { dg-final { scan-tree-dump-times "catch" 1 "gimple" } }
+
+struct A
+{
+  A();
+  ~A();
+};
+
+void f()
+{
+  A a[10] = { };
+}
+
diff --git a/gcc/testsuite/g++.dg/init/lifetime1.C b/gcc/testsuite/g++.dg/init/lifetime1.C
new file mode 100644
index 0000000..38e25ec
--- /dev/null
+++ b/gcc/testsuite/g++.dg/init/lifetime1.C
@@ -0,0 +1,29 @@ 
+// PR c++/48370
+// { dg-do run }
+
+extern "C" void abort();
+bool ok;
+
+struct A {
+  int i;
+  A(int i): i(i) { }
+  ~A() { if (!ok) abort(); }
+};
+
+struct D { int i; };
+
+struct B: D, A { B(int i): A(i) { } };
+struct E: D, virtual A { E(int i): A(i) { } };
+
+struct C
+{
+  const A& ar1;
+  const A& ar2;
+  const A& ar3;
+};
+
+int main()
+{
+  C c = { 1, B(2), E(3) };
+  ok = true;
+}
diff --git a/gcc/testsuite/g++.dg/init/ref21.C b/gcc/testsuite/g++.dg/init/ref21.C
new file mode 100644
index 0000000..db4ac4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/init/ref21.C
@@ -0,0 +1,7 @@ 
+struct A
+{
+  const int &i1;
+  const int &i2;
+};
+
+A a = { 1, 2 };
diff --git a/libiberty/cp-demangle.c b/libiberty/cp-demangle.c
index c7afef0..d0f57b7 100644
--- a/libiberty/cp-demangle.c
+++ b/libiberty/cp-demangle.c
@@ -1846,8 +1846,11 @@  d_special_name (struct d_info *di)
 	  return d_make_comp (di, DEMANGLE_COMPONENT_GUARD, d_name (di), NULL);
 
 	case 'R':
-	  return d_make_comp (di, DEMANGLE_COMPONENT_REFTEMP, d_name (di),
-			      NULL);
+	  {
+	    struct demangle_component *name = d_name (di);
+	    return d_make_comp (di, DEMANGLE_COMPONENT_REFTEMP, name,
+				d_number_component (di));
+	  }
 
 	case 'A':
 	  return d_make_comp (di, DEMANGLE_COMPONENT_HIDDEN_ALIAS,
@@ -3921,7 +3924,9 @@  d_print_comp (struct d_print_info *dpi, int options,
       return;
 
     case DEMANGLE_COMPONENT_REFTEMP:
-      d_append_string (dpi, "reference temporary for ");
+      d_append_string (dpi, "reference temporary #");
+      d_print_comp (dpi, options, d_right (dc));
+      d_append_string (dpi, " for ");
       d_print_comp (dpi, options, d_left (dc));
       return;