diff mbox

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

Message ID 4EE830A2.5080606@redhat.com
State New
Headers show

Commit Message

Jason Merrill Dec. 14, 2011, 5:14 a.m. UTC
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.
diff mbox

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" }
+}