+2017-08-10 Jason Merrill <jason@redhat.com>
+
+ PR c++/80452 - Core 1579, implicit move semantics on return/throw
+ * cp-tree.h (LOOKUP_PREFER_RVALUE): Now means that we've already
+ tentatively changed the lvalue to an rvalue.
+ * call.c (reference_binding): Remove LOOKUP_PREFER_RVALUE handling.
+ (build_over_call): If LOOKUP_PREFER_RVALUE, check that the first
+ parameter is an rvalue reference.
+ * except.c (build_throw): Do maybe-rvalue overload resolution twice.
+ * typeck.c (check_return_expr): Likewise.
+
2017-08-10 David Malcolm <dmalcolm@redhat.com>
* parser.c (cp_parser_error): Update for new param to
/* If KIND is ck_ref_bind, true when either an lvalue reference is
being bound to an lvalue expression or an rvalue reference is
being bound to an rvalue expression. If KIND is ck_rvalue,
- true when we should treat an lvalue as an rvalue (12.8p33). If
+ true when we are treating an lvalue as an rvalue (12.8p33). If
KIND is ck_base, always false. */
BOOL_BITFIELD rvaluedness_matches_p: 1;
BOOL_BITFIELD check_narrowing: 1;
}
conv = build_conv (ck_rvalue, from, conv);
if (flags & LOOKUP_PREFER_RVALUE)
+ /* Tell convert_like_real to set LOOKUP_PREFER_RVALUE. */
conv->rvaluedness_matches_p = true;
}
conv = build_identity_conv (tfrom, expr);
conv = direct_reference_binding (rto, conv);
- if (flags & LOOKUP_PREFER_RVALUE)
- /* The top-level caller requested that we pretend that the lvalue
- be treated as an rvalue. */
- conv->rvaluedness_matches_p = TYPE_REF_IS_RVALUE (rto);
- else if (TREE_CODE (rfrom) == REFERENCE_TYPE)
+ if (TREE_CODE (rfrom) == REFERENCE_TYPE)
/* Handle rvalue reference to function properly. */
conv->rvaluedness_matches_p
= (TYPE_REF_IS_RVALUE (rto) == TYPE_REF_IS_RVALUE (rfrom));
/* Don't allow binding of lvalues (other than function lvalues) to
rvalue references. */
if (is_lvalue && TYPE_REF_IS_RVALUE (rto)
- && TREE_CODE (to) != FUNCTION_TYPE
- && !(flags & LOOKUP_PREFER_RVALUE))
+ && TREE_CODE (to) != FUNCTION_TYPE)
conv->bad_p = true;
/* Nor the reverse. */
else
flags |= LOOKUP_ONLYCONVERTING;
if (convs->rvaluedness_matches_p)
+ /* standard_conversion got LOOKUP_PREFER_RVALUE. */
flags |= LOOKUP_PREFER_RVALUE;
if (TREE_CODE (expr) == TARGET_EXPR
&& TARGET_EXPR_LIST_INIT_P (expr))
++arg_index;
parm = TREE_CHAIN (parm);
}
+
+ if (flags & LOOKUP_PREFER_RVALUE)
+ {
+ /* The implicit move specified in 15.8.3/3 fails "...if the type of
+ the first parameter of the selected constructor is not an rvalue
+ reference to the object’s type (possibly cv-qualified)...." */
+ gcc_assert (!(complain & tf_error));
+ tree ptype = convs[0]->type;
+ if (TREE_CODE (ptype) != REFERENCE_TYPE
+ || !TYPE_REF_IS_RVALUE (ptype)
+ || CONVERSION_RANK (convs[0]) > cr_exact)
+ return error_mark_node;
+ }
}
/* Bypass access control for 'this' parameter. */
else if (TREE_CODE (TREE_TYPE (fn)) == METHOD_TYPE)
(Normally, these entities are registered in the symbol table, but
not found by lookup.) */
#define LOOKUP_HIDDEN (LOOKUP_PREFER_NAMESPACES << 1)
-/* Prefer that the lvalue be treated as an rvalue. */
+/* We're trying to treat an lvalue as an rvalue. */
#define LOOKUP_PREFER_RVALUE (LOOKUP_HIDDEN << 1)
/* We're inside an init-list, so narrowing conversions are ill-formed. */
#define LOOKUP_NO_NARROWING (LOOKUP_PREFER_RVALUE << 1)
{
int flags = LOOKUP_NORMAL | LOOKUP_ONLYCONVERTING;
vec<tree, va_gc> *exp_vec;
+ bool converted = false;
/* Under C++0x [12.8/16 class.copy], a thrown lvalue is sometimes
treated as an rvalue for the purposes of overload resolution
&& ! TREE_STATIC (exp)
/* The variable must not have the `volatile' qualifier. */
&& !(cp_type_quals (TREE_TYPE (exp)) & TYPE_QUAL_VOLATILE))
- flags = flags | LOOKUP_PREFER_RVALUE;
+ {
+ tree moved = move (exp);
+ exp_vec = make_tree_vector_single (moved);
+ moved = (build_special_member_call
+ (object, complete_ctor_identifier, &exp_vec,
+ TREE_TYPE (object), flags|LOOKUP_PREFER_RVALUE,
+ tf_none));
+ release_tree_vector (exp_vec);
+ if (moved != error_mark_node)
+ {
+ exp = moved;
+ converted = true;
+ }
+ }
/* Call the copy constructor. */
- exp_vec = make_tree_vector_single (exp);
- exp = (build_special_member_call
- (object, complete_ctor_identifier, &exp_vec,
- TREE_TYPE (object), flags, tf_warning_or_error));
- release_tree_vector (exp_vec);
+ if (!converted)
+ {
+ exp_vec = make_tree_vector_single (exp);
+ exp = (build_special_member_call
+ (object, complete_ctor_identifier, &exp_vec,
+ TREE_TYPE (object), flags, tf_warning_or_error));
+ release_tree_vector (exp_vec);
+ }
+
if (exp == error_mark_node)
{
error (" in thrown expression");
Note that these conditions are similar to, but not as strict as,
the conditions for the named return value optimization. */
+ bool converted = false;
if ((cxx_dialect != cxx98)
&& ((VAR_P (retval) && !DECL_HAS_VALUE_EXPR_P (retval))
|| TREE_CODE (retval) == PARM_DECL)
&& !TREE_STATIC (retval)
/* This is only interesting for class type. */
&& CLASS_TYPE_P (functype))
- flags = flags | LOOKUP_PREFER_RVALUE;
+ {
+ tree moved = move (retval);
+ moved = convert_for_initialization
+ (NULL_TREE, functype, moved, flags|LOOKUP_PREFER_RVALUE,
+ ICR_RETURN, NULL_TREE, 0, tf_none);
+ if (moved != error_mark_node)
+ {
+ retval = moved;
+ converted = true;
+ }
+ }
/* First convert the value to the function's return type, then
to the type of return value's location to handle the
case that functype is smaller than the valtype. */
- retval = convert_for_initialization
- (NULL_TREE, functype, retval, flags, ICR_RETURN, NULL_TREE, 0,
- tf_warning_or_error);
+ if (!converted)
+ retval = convert_for_initialization
+ (NULL_TREE, functype, retval, flags, ICR_RETURN, NULL_TREE, 0,
+ tf_warning_or_error);
retval = convert (valtype, retval);
/* If the conversion failed, treat this just like `return;'. */
--- /dev/null
+// PR c++/80452
+// { dg-do compile { target c++11 } }
+
+template<typename> struct check { };
+template<typename T> struct check<T&>;
+
+struct A {
+ A() = default;
+ A(A&&) = default;
+ A(const A&) = delete;
+};
+
+template <class T>
+struct B {
+ template <class U> B(U&&) { check<U> u; }
+};
+
+B<A> f()
+{
+ A a;
+ return a;
+}