convert_like_real was getting confused because it was seeing a reference binding that we had marked as bad, but it couldn't tell what was bad about it. This happened because when we did the ck_base conversion the rvalue expression became an lvalue. Fixed by preserving rvalueness through convert_to_base. As a consequence of these changes I also needed to tweak build_dynamic_cast_1 so that we don't try to pass off a tree of REFERENCE_TYPE to build_static_cast.

Tested x86_64-pc-linux-gnu, applying to trunk.
commit 339616961e9ebd216bf73abd6e36e0a5f049ed71
Author: Jason Merrill <ja...@redhat.com>
Date:   Fri Oct 10 18:21:02 2014 -0400

    	PR c++/62115
    	* class.c (build_base_path): Preserve rvalueness.
    	* call.c (convert_like_real) [ck_base]: Let convert_to_base handle &/*.
    	* rtti.c (build_dynamic_cast_1): Call convert_to_reference later.

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 76d8eab..8a89aad 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -6341,10 +6341,8 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
 	  /* We are going to bind a reference directly to a base-class
 	     subobject of EXPR.  */
 	  /* Build an expression for `*((base*) &expr)'.  */
-	  expr = cp_build_addr_expr (expr, complain);
-	  expr = convert_to_base (expr, build_pointer_type (totype),
+	  expr = convert_to_base (expr, totype,
 				  !c_cast_p, /*nonnull=*/true, complain);
-	  expr = cp_build_indirect_ref (expr, RO_IMPLICIT_CONVERSION, complain);
 	  return expr;
 	}
 
diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index b661187..99bfa95 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -251,6 +251,7 @@ build_base_path (enum tree_code code,
   int want_pointer = TYPE_PTR_P (TREE_TYPE (expr));
   bool has_empty = false;
   bool virtual_access;
+  bool rvalue = false;
 
   if (expr == error_mark_node || binfo == error_mark_node || !binfo)
     return error_mark_node;
@@ -324,8 +325,11 @@ build_base_path (enum tree_code code,
     }
 
   if (!want_pointer)
-    /* This must happen before the call to save_expr.  */
-    expr = cp_build_addr_expr (expr, complain);
+    {
+      rvalue = !real_lvalue_p (expr);
+      /* This must happen before the call to save_expr.  */
+      expr = cp_build_addr_expr (expr, complain);
+    }
   else
     expr = mark_rvalue_use (expr);
 
@@ -351,9 +355,7 @@ build_base_path (enum tree_code code,
       || in_template_function ())
     {
       expr = build_nop (ptr_target_type, expr);
-      if (!want_pointer)
-	expr = build_indirect_ref (EXPR_LOCATION (expr), expr, RO_NULL);
-      return expr;
+      goto indout;
     }
 
   /* If we're in an NSDMI, we don't have the full constructor context yet
@@ -364,9 +366,7 @@ build_base_path (enum tree_code code,
     {
       expr = build1 (CONVERT_EXPR, ptr_target_type, expr);
       CONVERT_EXPR_VBASE_PATH (expr) = true;
-      if (!want_pointer)
-	expr = build_indirect_ref (EXPR_LOCATION (expr), expr, RO_NULL);
-      return expr;
+      goto indout;
     }
 
   /* Do we need to check for a null pointer?  */
@@ -402,6 +402,8 @@ build_base_path (enum tree_code code,
     {
       expr = cp_build_indirect_ref (expr, RO_NULL, complain);
       expr = build_simple_base_path (expr, binfo);
+      if (rvalue)
+	expr = move (expr);
       if (want_pointer)
 	expr = build_address (expr);
       target_type = TREE_TYPE (expr);
@@ -478,8 +480,13 @@ build_base_path (enum tree_code code,
   else
     null_test = NULL;
 
+ indout:
   if (!want_pointer)
-    expr = cp_build_indirect_ref (expr, RO_NULL, complain);
+    {
+      expr = cp_build_indirect_ref (expr, RO_NULL, complain);
+      if (rvalue)
+	expr = move (expr);
+    }
 
  out:
   if (null_test)
diff --git a/gcc/cp/rtti.c b/gcc/cp/rtti.c
index 10cc168..762953b 100644
--- a/gcc/cp/rtti.c
+++ b/gcc/cp/rtti.c
@@ -608,10 +608,6 @@ build_dynamic_cast_1 (tree type, tree expr, tsubst_flags_t complain)
 	  errstr = _("source is of incomplete class type");
 	  goto fail;
 	}
-
-      /* Apply trivial conversion T -> T& for dereferenced ptrs.  */
-      expr = convert_to_reference (exprtype, expr, CONV_IMPLICIT,
-				   LOOKUP_NORMAL, NULL_TREE, complain);
     }
 
   /* The dynamic_cast operator shall not cast away constness.  */
@@ -631,6 +627,11 @@ build_dynamic_cast_1 (tree type, tree expr, tsubst_flags_t complain)
       return build_static_cast (type, expr, complain);
   }
 
+  /* Apply trivial conversion T -> T& for dereferenced ptrs.  */
+  if (tc == REFERENCE_TYPE)
+    expr = convert_to_reference (exprtype, expr, CONV_IMPLICIT,
+				 LOOKUP_NORMAL, NULL_TREE, complain);
+
   /* Otherwise *exprtype must be a polymorphic class (have a vtbl).  */
   if (TYPE_POLYMORPHIC_P (TREE_TYPE (exprtype)))
     {
diff --git a/gcc/testsuite/g++.dg/expr/cond6.C b/gcc/testsuite/g++.dg/expr/cond6.C
index 943aa85..8f7f084 100644
--- a/gcc/testsuite/g++.dg/expr/cond6.C
+++ b/gcc/testsuite/g++.dg/expr/cond6.C
@@ -1,10 +1,11 @@
 // { dg-do run }
 
 extern "C" void abort ();
+bool ok = false;
 
 struct B {
   B() {}
-  B(const B& b) { abort (); }
+  B(const B& b) { ok = true; }
 };
 
 struct D : public B {
@@ -21,4 +22,5 @@ D f() {
 
 int main () {
   b = (true ? f() : b);
+  return !ok;
 }
diff --git a/gcc/testsuite/g++.dg/overload/defarg9.C b/gcc/testsuite/g++.dg/overload/defarg9.C
new file mode 100644
index 0000000..401fe0e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/overload/defarg9.C
@@ -0,0 +1,11 @@
+// PR c++/62115
+
+struct A {};
+struct B : A {};
+
+struct C
+{
+  C(A& a = B()) {}		// { dg-error "rvalue" }
+};
+
+C c;				// { dg-error "" }

Reply via email to