While looking at guaranteed copy elision, I noticed some outstanding issues with temporary lifetime extension. In one case, we were failing to extend temporaries bound to references within another temporary. In another, we were failing to extend a temporary when we bind a reference to a scalar subobject.
Tested x86_64-pc-linux-gnu, applying to trunk.
commit 72052afef06d077e8061b974aff3c2910037b7a0 Author: Jason Merrill <ja...@redhat.com> Date: Wed Oct 5 14:30:18 2016 -0400 * call.c (extend_ref_init_temps): Fix TARGET_EXPR handling. diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 0914ae2..c333418 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -10172,28 +10172,31 @@ extend_ref_init_temps (tree decl, tree init, vec<tree, va_gc> **cleanups) return init; if (TREE_CODE (type) == REFERENCE_TYPE) init = extend_ref_init_temps_1 (decl, init, cleanups); - else if (is_std_init_list (type)) + else { - /* 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; + if (is_std_init_list (type)) + { + /* The temporary array underlying a std::initializer_list + is handled like a reference temporary. */ + tree array = CONSTRUCTOR_ELT (ctor, 0)->value; + array = extend_ref_init_temps_1 (decl, array, cleanups); + CONSTRUCTOR_ELT (ctor, 0)->value = array; + } + else + { + unsigned i; + constructor_elt *p; + vec<constructor_elt, va_gc> *elts = CONSTRUCTOR_ELTS (ctor); + FOR_EACH_VEC_SAFE_ELT (elts, i, p) + p->value = extend_ref_init_temps (decl, p->value, cleanups); + } } } - else if (TREE_CODE (init) == CONSTRUCTOR) - { - unsigned i; - constructor_elt *p; - vec<constructor_elt, va_gc> *elts = CONSTRUCTOR_ELTS (init); - FOR_EACH_VEC_SAFE_ELT (elts, i, p) - p->value = extend_ref_init_temps (decl, p->value, cleanups); - } return init; } diff --git a/gcc/testsuite/g++.dg/cpp1z/elide2.C b/gcc/testsuite/g++.dg/cpp1z/elide2.C new file mode 100644 index 0000000..277decf --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/elide2.C @@ -0,0 +1,25 @@ +// DR 1697 +// { dg-do run { target c++11 } } + +#define assert(X) do { if (!(X)) __builtin_abort(); } while(0) + +int i; +struct S { + ~S() { assert (i++ == 2); } +}; +struct X { + X() { assert (i++ == 0); } + X(const X&); +}; +struct T { + S &&s; + X x; +}; +void f() { assert (i++ == 1); } +int main() { + { + T t = T{ {}, {} }; + f(); + } + assert (i == 3); +}
commit c9d1d33ded916cb7f0dbbf6b7b4673acc2af0244 Author: Jason Merrill <ja...@redhat.com> Date: Wed Oct 5 16:24:33 2016 -0400 PR c++/54293 - binding reference to member of temporary * call.c (reference_binding): Fix binding to member of temporary. diff --git a/gcc/cp/call.c b/gcc/cp/call.c index c333418..dac1337 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -1539,15 +1539,20 @@ reference_binding (tree rto, tree rfrom, tree expr, bool c_cast_p, int flags, gl_kind = clk_rvalueref; } else if (expr) - { - gl_kind = lvalue_kind (expr); - if (gl_kind & clk_class) - /* A class prvalue is not a glvalue. */ - gl_kind = clk_none; - } + gl_kind = lvalue_kind (expr); + else if (CLASS_TYPE_P (from) + || TREE_CODE (from) == ARRAY_TYPE) + gl_kind = clk_class; else gl_kind = clk_none; - is_lvalue = gl_kind && !(gl_kind & clk_rvalueref); + + /* Don't allow a class prvalue when LOOKUP_NO_TEMP_BIND. */ + if ((flags & LOOKUP_NO_TEMP_BIND) + && (gl_kind & clk_class)) + gl_kind = clk_none; + + /* Same mask as real_lvalue_p. */ + is_lvalue = gl_kind && !(gl_kind & (clk_rvalueref|clk_class)); tfrom = from; if ((gl_kind & clk_bitfield) != 0) @@ -1569,11 +1574,7 @@ reference_binding (tree rto, tree rfrom, tree expr, bool c_cast_p, int flags, [8.5.3/5 dcl.init.ref] is changed to also require direct bindings for const and rvalue references to rvalues of compatible class type. We should also do direct bindings for non-class xvalues. */ - if (related_p - && (gl_kind - || (!(flags & LOOKUP_NO_TEMP_BIND) - && (CLASS_TYPE_P (from) - || TREE_CODE (from) == ARRAY_TYPE)))) + if (related_p && gl_kind) { /* [dcl.init.ref] diff --git a/gcc/testsuite/g++.dg/init/ref19.C b/gcc/testsuite/g++.dg/init/ref19.C index ed78c93..d583e93 100644 --- a/gcc/testsuite/g++.dg/init/ref19.C +++ b/gcc/testsuite/g++.dg/init/ref19.C @@ -11,7 +11,11 @@ struct A int main() { - const int &r = A().i; + { + const int &r = A().i; + if (d != 0) + return 1; + } if (d != 1) return 1; }