From patchwork Fri Nov 4 03:50:56 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jason Merrill X-Patchwork-Id: 123563 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) by ozlabs.org (Postfix) with SMTP id 29411B6F70 for ; Fri, 4 Nov 2011 14:51:23 +1100 (EST) Received: (qmail 14600 invoked by alias); 4 Nov 2011 03:51:20 -0000 Received: (qmail 14583 invoked by uid 22791); 4 Nov 2011 03:51:16 -0000 X-SWARE-Spam-Status: No, hits=-7.1 required=5.0 tests=AWL, BAYES_00, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, SPF_HELO_PASS, TW_TM X-Spam-Check-By: sourceware.org Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Fri, 04 Nov 2011 03:50:59 +0000 Received: from int-mx09.intmail.prod.int.phx2.redhat.com (int-mx09.intmail.prod.int.phx2.redhat.com [10.5.11.22]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id pA43owb2009028 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Thu, 3 Nov 2011 23:50:58 -0400 Received: from ns3.rdu.redhat.com (ns3.rdu.redhat.com [10.11.255.199]) by int-mx09.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id pA43owJT006515 for ; Thu, 3 Nov 2011 23:50:58 -0400 Received: from [0.0.0.0] (ovpn-113-72.phx2.redhat.com [10.3.113.72]) by ns3.rdu.redhat.com (8.13.8/8.13.8) with ESMTP id pA43ouH2026935 for ; Thu, 3 Nov 2011 23:50:57 -0400 Message-ID: <4EB36120.3060603@redhat.com> Date: Thu, 03 Nov 2011 23:50:56 -0400 From: Jason Merrill User-Agent: Mozilla/5.0 (X11; Linux i686; rv:7.0.1) Gecko/20111001 Thunderbird/7.0.1 MIME-Version: 1.0 To: gcc-patches List Subject: C++ PATCH for c++/48370 (extending lifetime of temps in aggregate initialization) Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org 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. commit 82e649dc7b7918d48b14a88dc2fda9836e421cd9 Author: Jason Merrill 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 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 %", 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 + +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 AL; +typedef std::initializer_list AL2; +typedef std::initializer_list 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;