Commit 4ce8c5de by Jason Merrill Committed by Jason Merrill

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.

From-SVN: r251035
parent a0a10c6d
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
......
......@@ -101,7 +101,7 @@ struct conversion {
/* 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;
......@@ -1161,6 +1161,7 @@ standard_conversion (tree to, tree from, tree expr, bool c_cast_p,
}
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;
}
......@@ -1629,11 +1630,7 @@ reference_binding (tree rto, tree rfrom, tree expr, bool c_cast_p, int flags,
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));
......@@ -1659,8 +1656,7 @@ reference_binding (tree rto, tree rfrom, tree expr, bool c_cast_p, int flags,
/* 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. */
......@@ -6917,6 +6913,7 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
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))
......@@ -7716,6 +7713,19 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
++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)
......
......@@ -5296,7 +5296,7 @@ enum overload_flags { NO_SPECIAL = 0, DTOR_FLAG, TYPENAME_FLAG };
(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)
......
......@@ -665,6 +665,7 @@ build_throw (tree exp)
{
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
......@@ -675,14 +676,31 @@ build_throw (tree exp)
&& ! 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");
......
......@@ -9156,6 +9156,7 @@ check_return_expr (tree retval, bool *no_warning)
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)
......@@ -9163,14 +9164,25 @@ check_return_expr (tree retval, bool *no_warning)
&& !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;'. */
......
// 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;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment