diff mbox

C++ PATCHes for temporary lifetime extension bugs

Message ID CADzB+2mbKB829QN-Azzr984zftVg45Rz6UhBPXA031SYTmrqLg@mail.gmail.com
State New
Headers show

Commit Message

Jason Merrill Oct. 5, 2016, 11 p.m. UTC
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 <jason@redhat.com>
Date:   Wed Oct 5 14:30:18 2016 -0400

            * call.c (extend_ref_init_temps): Fix TARGET_EXPR handling.
commit c9d1d33ded916cb7f0dbbf6b7b4673acc2af0244
Author: Jason Merrill <jason@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;
 }
diff mbox

Patch

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);
+}