Patchwork C++ PATCH for c++/51406, 51161 (wrong-code with static cast to rvalue ref)

login
register
mail settings
Submitter Jason Merrill
Date Dec. 14, 2011, 5:14 a.m.
Message ID <4EE830A2.5080606@redhat.com>
Download mbox | patch
Permalink /patch/131294/
State New
Headers show

Comments

Jason Merrill - Dec. 14, 2011, 5:14 a.m.
The code for casting to rvalue ref was assuming that no base adjustment 
would be necessary.  This patch delegates to the normal lvalue binding 
code, and then changes the result to be an rvalue reference.

Tested x86_64-pc-linux-gnu, applying to trunk.  Will apply to 4.5/4.6 as 
well after testing.

Patch

commit ed6825825765c8c02a53dc011e252ddfacc9f5e5
Author: Jason Merrill <jason@redhat.com>
Date:   Tue Dec 13 23:02:02 2011 -0500

    	PR c++/51406
    	PR c++/51161
    	* typeck.c (build_static_cast_1): Fix cast of lvalue to
    	base rvalue reference.

diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 4973d7d..b168963 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -5856,12 +5856,22 @@  build_static_cast_1 (tree type, tree expr, bool c_cast_p,
      cv2 T2 if cv2 T2 is reference-compatible with cv1 T1 (8.5.3)."  */
   if (TREE_CODE (type) == REFERENCE_TYPE
       && TYPE_REF_IS_RVALUE (type)
-      && lvalue_or_rvalue_with_address_p (expr)
+      && real_lvalue_p (expr)
       && reference_related_p (TREE_TYPE (type), intype)
       && (c_cast_p || at_least_as_qualified_p (TREE_TYPE (type), intype)))
     {
-      expr = build_typed_address (expr, type);
-      return convert_from_reference (expr);
+      /* Handle the lvalue case here by casting to lvalue reference and
+	 then changing it to an rvalue reference.  Casting an xvalue to
+	 rvalue reference will be handled by the main code path.  */
+      tree lref = cp_build_reference_type (TREE_TYPE (type), false);
+      result = (perform_direct_initialization_if_possible
+		(lref, expr, c_cast_p, complain));
+      result = cp_fold_convert (type, result);
+      /* Make sure we don't fold back down to a named rvalue reference,
+	 because that would be an lvalue.  */
+      if (DECL_P (result))
+	result = build1 (NON_LVALUE_EXPR, type, result);
+      return convert_from_reference (result);
     }
 
   /* Resolve overloaded address here rather than once in
diff --git a/gcc/testsuite/g++.dg/cpp0x/rv-cast3.C b/gcc/testsuite/g++.dg/cpp0x/rv-cast3.C
new file mode 100644
index 0000000..6c70324
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/rv-cast3.C
@@ -0,0 +1,17 @@ 
+// PR c++/51406
+// { dg-do run { target c++11 } }
+
+extern "C" int printf(const char *,...);
+extern "C" void abort();
+
+struct A { int a; A() : a(1) {} };
+struct B { int b; B() : b(2) {} };
+struct X : A, B {};
+
+int main() {
+    X x;
+    int a=static_cast<A&&>(x).a;
+    int b=static_cast<B&&>(x).b;
+    // printf ("%d %d\n", a, b);
+    if (a!=1 || b!=2) abort();
+}
diff --git a/gcc/testsuite/g++.dg/cpp0x/rv-cast4.C b/gcc/testsuite/g++.dg/cpp0x/rv-cast4.C
new file mode 100644
index 0000000..13f369d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/rv-cast4.C
@@ -0,0 +1,13 @@ 
+// PR c++/51161
+// { dg-do compile { target c++11 } }
+
+struct A{};
+struct B : A{};
+struct C : A{};
+struct D : B, C{};
+
+int main()
+{
+  D d;
+  static_cast<A &&>(d);		// { dg-error "ambiguous" }
+}