The first patch implements the adjustments from core issues 616 and
1213 to the value category of subobjects of class prvalues: they were
considered prvalues themselves, but that was kind of nonsensical.  Now
they are considered xvalues.  Along with this, I've removed the
diagnostic distinction between xvalues and prvalues when trying to use
one or the other as an lvalue; the important thing is that they are
rvalues.

The second patch corrects various issues with casts and xvalues/rvalue
references: we were treating an xvalue operand to dynamic_cast as an
lvalue, and we were objecting to casts from prvalue to rvalue
reference type.

Tested x86_64-pc-linux-gnu, applying to trunk.
commit e7b8c6f89b5e4c69bc2e74ade15a5364b9fac45e
Author: Jason Merrill <ja...@redhat.com>
Date:   Tue May 22 15:40:24 2018 -0400

            CWG 616, 1213 - value category of subobject references.
    
            * tree.c (lvalue_kind): A reference to a subobject of a prvalue is
            an xvalue.
            * typeck2.c (build_m_component_ref): Likewise.
            * typeck.c (cp_build_addr_expr_1, lvalue_or_else): Remove diagnostic
            distinction between temporary and xvalue.

diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 15b9697a63b..efb8c2bf926 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -87,6 +87,7 @@ lvalue_kind (const_tree ref)
     {
     case SAVE_EXPR:
       return clk_none;
+
       /* preincrements and predecrements are valid lvals, provided
 	 what they refer to are valid lvals.  */
     case PREINCREMENT_EXPR:
@@ -94,7 +95,14 @@ lvalue_kind (const_tree ref)
     case TRY_CATCH_EXPR:
     case REALPART_EXPR:
     case IMAGPART_EXPR:
-      return lvalue_kind (TREE_OPERAND (ref, 0));
+    case ARRAY_REF:
+    case VIEW_CONVERT_EXPR:
+      op1_lvalue_kind = lvalue_kind (TREE_OPERAND (ref, 0));
+      if (op1_lvalue_kind == clk_class)
+	/* in the case of an array operand, the result is an lvalue if that
+	   operand is an lvalue and an xvalue otherwise */
+	op1_lvalue_kind = clk_rvalueref;
+      return op1_lvalue_kind;
 
     case MEMBER_REF:
     case DOTSTAR_EXPR:
@@ -104,6 +112,11 @@ lvalue_kind (const_tree ref)
 	op1_lvalue_kind = lvalue_kind (TREE_OPERAND (ref, 0));
       if (TYPE_PTRMEMFUNC_P (TREE_TYPE (TREE_OPERAND (ref, 1))))
 	op1_lvalue_kind = clk_none;
+      else if (op1_lvalue_kind == clk_class)
+	/* The result of a .* expression whose second operand is a pointer to a
+	   data member is an lvalue if the first operand is an lvalue and an
+	   xvalue otherwise.  */
+	op1_lvalue_kind = clk_rvalueref;
       return op1_lvalue_kind;
 
     case COMPONENT_REF:
@@ -119,6 +132,11 @@ lvalue_kind (const_tree ref)
 	    return lvalue_kind (TREE_OPERAND (ref, 1));
 	}
       op1_lvalue_kind = lvalue_kind (TREE_OPERAND (ref, 0));
+      if (op1_lvalue_kind == clk_class)
+	/* If E1 is an lvalue, then E1.E2 is an lvalue;
+	   otherwise E1.E2 is an xvalue.  */
+	op1_lvalue_kind = clk_rvalueref;
+
       /* Look at the member designator.  */
       if (!op1_lvalue_kind)
 	;
@@ -165,7 +183,6 @@ lvalue_kind (const_tree ref)
       /* FALLTHRU */
     case INDIRECT_REF:
     case ARROW_EXPR:
-    case ARRAY_REF:
     case PARM_DECL:
     case RESULT_DECL:
     case PLACEHOLDER_EXPR:
@@ -203,11 +220,7 @@ lvalue_kind (const_tree ref)
 	     type-dependent expr, that is, but we shouldn't be testing
 	     lvalueness if we can't even tell the types yet!  */
 	  gcc_assert (!type_dependent_expression_p (CONST_CAST_TREE (ref)));
-	  if (CLASS_TYPE_P (TREE_TYPE (ref))
-	      || TREE_CODE (TREE_TYPE (ref)) == ARRAY_TYPE)
-	    return clk_class;
-	  else
-	    return clk_none;
+	  goto default_;
 	}
       op1_lvalue_kind = lvalue_kind (TREE_OPERAND (ref, 1)
 				    ? TREE_OPERAND (ref, 1)
@@ -257,18 +270,14 @@ lvalue_kind (const_tree ref)
     case PAREN_EXPR:
       return lvalue_kind (TREE_OPERAND (ref, 0));
 
-    case VIEW_CONVERT_EXPR:
-      if (location_wrapper_p (ref))
-	return lvalue_kind (TREE_OPERAND (ref, 0));
-      /* Fallthrough.  */
-
     default:
+    default_:
       if (!TREE_TYPE (ref))
 	return clk_none;
       if (CLASS_TYPE_P (TREE_TYPE (ref))
 	  || TREE_CODE (TREE_TYPE (ref)) == ARRAY_TYPE)
 	return clk_class;
-      break;
+      return clk_none;
     }
 
   /* If one operand is not an lvalue at all, then this expression is
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index ecb334d19d2..82089c45105 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -5860,11 +5860,8 @@ cp_build_addr_expr_1 (tree arg, bool strict_lvalue, tsubst_flags_t complain)
 	{
 	  if (!(complain & tf_error))
 	    return error_mark_node;
-	  if (kind & clk_class)
-	    /* Make this a permerror because we used to accept it.  */
-	    permerror (input_location, "taking address of temporary");
-	  else
-	    error ("taking address of xvalue (rvalue reference)");
+	  /* Make this a permerror because we used to accept it.  */
+	  permerror (input_location, "taking address of rvalue");
 	}
     }
 
@@ -9866,11 +9863,8 @@ lvalue_or_else (tree ref, enum lvalue_use use, tsubst_flags_t complain)
     {
       if (!(complain & tf_error))
 	return 0;
-      if (kind & clk_class)
-	/* Make this a permerror because we used to accept it.  */
-	permerror (input_location, "using temporary as lvalue");
-      else
-	error ("using xvalue (rvalue reference) as lvalue");
+      /* Make this a permerror because we used to accept it.  */
+      permerror (input_location, "using rvalue as lvalue");
     }
   return 1;
 }
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index ad0774c6731..40233e68716 100644
--- a/gcc/cp/typeck2.c
+++ b/gcc/cp/typeck2.c
@@ -1965,7 +1965,7 @@ build_m_component_ref (tree datum, tree component, tsubst_flags_t complain)
 
   if (TYPE_PTRDATAMEM_P (ptrmem_type))
     {
-      cp_lvalue_kind kind = lvalue_kind (datum);
+      bool is_lval = real_lvalue_p (datum);
       tree ptype;
 
       /* Compute the type of the field, as described in [expr.ref].
@@ -1995,9 +1995,7 @@ build_m_component_ref (tree datum, tree component, tsubst_flags_t complain)
 	return error_mark_node;
 
       /* If the object expression was an rvalue, return an rvalue.  */
-      if (kind & clk_class)
-	datum = rvalue (datum);
-      else if (kind & clk_rvalueref)
+      if (!is_lval)
 	datum = move (datum);
       return datum;
     }
diff --git a/gcc/testsuite/g++.dg/cpp0x/addressof2.C b/gcc/testsuite/g++.dg/cpp0x/addressof2.C
index bf218cca481..a38dce003a7 100644
--- a/gcc/testsuite/g++.dg/cpp0x/addressof2.C
+++ b/gcc/testsuite/g++.dg/cpp0x/addressof2.C
@@ -17,7 +17,7 @@ auto c = __builtin_addressof (s);
 auto d = addressof (s);
 auto e = __builtin_addressof (s.s);		// { dg-error "attempt to take address of bit-field" }
 auto f = addressof (s.s);			// { dg-error "cannot bind bitfield" }
-auto g = __builtin_addressof (S{});		// { dg-error "taking address of temporary" }
+auto g = __builtin_addressof (S{});		// { dg-error "taking address of rvalue" }
 auto h = addressof (S{});			// { dg-error "cannot bind non-const lvalue reference of type" }
 auto i = __builtin_addressof (S::t);		// { dg-error "invalid use of non-static data member" }
 auto j = __builtin_addressof (S::foo);		// { dg-error "invalid use of non-static member function" }
diff --git a/gcc/testsuite/g++.dg/cpp0x/decltype49.C b/gcc/testsuite/g++.dg/cpp0x/decltype49.C
index c3174982f82..d0a37823d4b 100644
--- a/gcc/testsuite/g++.dg/cpp0x/decltype49.C
+++ b/gcc/testsuite/g++.dg/cpp0x/decltype49.C
@@ -7,4 +7,4 @@ int A::*ipm = &A::i;
 template <class T, class U> class assert_same_type;
 template <class T> class assert_same_type<T,T> { };
 
-assert_same_type<decltype(A().*ipm),int> x2;
+assert_same_type<decltype(A().*ipm),int&&> x2;
diff --git a/gcc/testsuite/g++.dg/cpp0x/rv-lvalue-req.C b/gcc/testsuite/g++.dg/cpp0x/rv-lvalue-req.C
index 1ca3c415475..10a655dc1e5 100644
--- a/gcc/testsuite/g++.dg/cpp0x/rv-lvalue-req.C
+++ b/gcc/testsuite/g++.dg/cpp0x/rv-lvalue-req.C
@@ -4,9 +4,9 @@ template <class T> T&& declval();
 
 int main()
 {
-  &declval<int>();		        // { dg-error "xvalue" }
-  declval<int>() = declval<int>();	// { dg-error "xvalue" }
-  declval<int>()++;			// { dg-error "xvalue" }
-  --declval<int>();			// { dg-error "xvalue" }
-  declval<int>() += 1;			// { dg-error "xvalue" }
+  &declval<int>();		        // { dg-error "rvalue" }
+  declval<int>() = declval<int>();	// { dg-error "rvalue" }
+  declval<int>()++;			// { dg-error "rvalue" }
+  --declval<int>();			// { dg-error "rvalue" }
+  declval<int>() += 1;			// { dg-error "rvalue" }
 }
diff --git a/gcc/testsuite/g++.dg/ext/c99struct1.C b/gcc/testsuite/g++.dg/ext/c99struct1.C
index 93e84b46039..bd5be2dbbf5 100644
--- a/gcc/testsuite/g++.dg/ext/c99struct1.C
+++ b/gcc/testsuite/g++.dg/ext/c99struct1.C
@@ -6,7 +6,7 @@ struct s { int a[1]; };
 void
 foo5 (void)
 {
-	  ((struct s) { { 0 } }).a[0] = 1;
+  int i = ((struct s) { { 0 } }).a[0];
 }
 
 
diff --git a/gcc/testsuite/g++.dg/ext/complit11.C b/gcc/testsuite/g++.dg/ext/complit11.C
index 0662543d941..07418aba985 100644
--- a/gcc/testsuite/g++.dg/ext/complit11.C
+++ b/gcc/testsuite/g++.dg/ext/complit11.C
@@ -6,7 +6,7 @@ struct A { int i; };
 template<int t>
 void foo()
 {
-    ((struct A) { 0 }).i += 1;	// { dg-error "temporary" }
+    ((struct A) { 0 }).i += 1;	// { dg-error "lvalue" }
 }
 
 void g(void)
diff --git a/gcc/testsuite/g++.old-deja/g++.law/temps1.C b/gcc/testsuite/g++.old-deja/g++.law/temps1.C
index ad42f425751..d1ac15d6e90 100644
--- a/gcc/testsuite/g++.old-deja/g++.law/temps1.C
+++ b/gcc/testsuite/g++.old-deja/g++.law/temps1.C
@@ -17,4 +17,4 @@ struct cookie
 };
 
 cookie cat(&foo("apabepa"));// { dg-warning "deprecated conversion|forbids converting a string constant" "dep" }
-// { dg-warning "taking address of temporary" "add" { target *-*-* } .-1 }
+// { dg-warning "taking address of rvalue" "add" { target *-*-* } .-1 }
commit 23a88cb6641f097dd0a9faebfafbe2f1e876714b
Author: Jason Merrill <ja...@redhat.com>
Date:   Thu May 3 08:39:26 2018 -0400

            Fix cast to rvalue reference from prvalue.
    
            * cvt.c (diagnose_ref_binding): Handle rvalue reference.
            * rtti.c (build_dynamic_cast_1): Don't try to build a reference to
            non-class type.  Handle xvalue argument.
            * typeck.c (build_reinterpret_cast_1): Allow cast from prvalue to
            rvalue reference.
            * semantics.c (finish_compound_literal): Do direct-initialization,
            not cast, to initialize a reference.

diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
index 30b44b7d7ea..3f87317a47d 100644
--- a/gcc/cp/cvt.c
+++ b/gcc/cp/cvt.c
@@ -381,7 +381,8 @@ diagnose_ref_binding (location_t loc, tree reftype, tree intype, tree decl)
 {
   tree ttl = TREE_TYPE (reftype);
 
-  if (!CP_TYPE_CONST_NON_VOLATILE_P (ttl))
+  if (!TYPE_REF_IS_RVALUE (reftype)
+      && !CP_TYPE_CONST_NON_VOLATILE_P (ttl))
     {
       const char *msg;
 
diff --git a/gcc/cp/rtti.c b/gcc/cp/rtti.c
index 426a23276e0..6692fb7ff86 100644
--- a/gcc/cp/rtti.c
+++ b/gcc/cp/rtti.c
@@ -616,22 +616,22 @@ build_dynamic_cast_1 (tree type, tree expr, tsubst_flags_t complain)
   else
     {
       expr = mark_lvalue_use (expr);
-
-      exprtype = build_reference_type (TREE_TYPE (expr));
+      exprtype = TREE_TYPE (expr);
 
       /* T is a reference type, v shall be an lvalue of a complete class
 	 type, and the result is an lvalue of the type referred to by T.  */
-
-      if (! MAYBE_CLASS_TYPE_P (TREE_TYPE (exprtype)))
+      if (! MAYBE_CLASS_TYPE_P (exprtype))
 	{
 	  errstr = _("source is not of class type");
 	  goto fail;
 	}
-      if (!COMPLETE_TYPE_P (complete_type (TREE_TYPE (exprtype))))
+      if (!COMPLETE_TYPE_P (complete_type (exprtype)))
 	{
 	  errstr = _("source is of incomplete class type");
 	  goto fail;
 	}
+
+      exprtype = cp_build_reference_type (exprtype, !lvalue_p (expr));
     }
 
   /* The dynamic_cast operator shall not cast away constness.  */
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 94e8f54254d..46251deaa6c 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -2734,7 +2734,10 @@ finish_compound_literal (tree type, tree compound_literal,
       compound_literal
 	= finish_compound_literal (TREE_TYPE (type), compound_literal,
 				   complain, fcl_context);
-      return cp_build_c_cast (type, compound_literal, complain);
+      /* The prvalue is then used to direct-initialize the reference.  */
+      tree r = (perform_implicit_conversion_flags
+		(type, compound_literal, complain, LOOKUP_NORMAL));
+      return convert_from_reference (r);
     }
 
   if (!TYPE_OBJ_P (type))
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 82089c45105..a694499190f 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -7315,13 +7315,19 @@ build_reinterpret_cast_1 (tree type, tree expr, bool c_cast_p,
     type = cv_unqualified (type);
 
   /* [expr.reinterpret.cast]
-     An lvalue expression of type T1 can be cast to the type
+     A glvalue expression of type T1 can be cast to the type
      "reference to T2" if an expression of type "pointer to T1" can be
      explicitly converted to the type "pointer to T2" using a
      reinterpret_cast.  */
   if (TYPE_REF_P (type))
     {
-      if (! lvalue_p (expr))
+      if (TYPE_REF_IS_RVALUE (type))
+	{
+	  if (!obvalue_p (expr))
+	    /* Perform the temporary materialization conversion.  */
+	    expr = get_target_expr_sfinae (expr, complain);
+	}
+      else if (!lvalue_p (expr))
 	{
           if (complain & tf_error)
             error ("invalid cast of an rvalue expression of type "
diff --git a/gcc/testsuite/g++.dg/cpp0x/rv-cast6.C b/gcc/testsuite/g++.dg/cpp0x/rv-cast6.C
new file mode 100644
index 00000000000..3ae5691c5fd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/rv-cast6.C
@@ -0,0 +1,11 @@
+// Test that a prvalue can be used where a glvalue is expected.
+// { dg-do compile { target c++11 } }
+
+struct A { virtual void f(); };
+struct B : A {};
+
+auto && a = static_cast<A&&>(B());
+auto && b = reinterpret_cast<A&&>(B());
+auto && c = dynamic_cast<A&&>(B());
+auto && d = dynamic_cast<B&&>(static_cast<A&&>(B()));
+auto && e = const_cast<B&&>(B());

Reply via email to