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> 2017-08-10 David Malcolm <dmalcolm@redhat.com>
* parser.c (cp_parser_error): Update for new param to * parser.c (cp_parser_error): Update for new param to
......
...@@ -101,7 +101,7 @@ struct conversion { ...@@ -101,7 +101,7 @@ struct conversion {
/* If KIND is ck_ref_bind, true when either an lvalue reference is /* 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 lvalue expression or an rvalue reference is
being bound to an rvalue expression. If KIND is ck_rvalue, 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. */ KIND is ck_base, always false. */
BOOL_BITFIELD rvaluedness_matches_p: 1; BOOL_BITFIELD rvaluedness_matches_p: 1;
BOOL_BITFIELD check_narrowing: 1; BOOL_BITFIELD check_narrowing: 1;
...@@ -1161,6 +1161,7 @@ standard_conversion (tree to, tree from, tree expr, bool c_cast_p, ...@@ -1161,6 +1161,7 @@ standard_conversion (tree to, tree from, tree expr, bool c_cast_p,
} }
conv = build_conv (ck_rvalue, from, conv); conv = build_conv (ck_rvalue, from, conv);
if (flags & LOOKUP_PREFER_RVALUE) if (flags & LOOKUP_PREFER_RVALUE)
/* Tell convert_like_real to set LOOKUP_PREFER_RVALUE. */
conv->rvaluedness_matches_p = true; conv->rvaluedness_matches_p = true;
} }
...@@ -1629,11 +1630,7 @@ reference_binding (tree rto, tree rfrom, tree expr, bool c_cast_p, int flags, ...@@ -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 = build_identity_conv (tfrom, expr);
conv = direct_reference_binding (rto, conv); conv = direct_reference_binding (rto, conv);
if (flags & LOOKUP_PREFER_RVALUE) if (TREE_CODE (rfrom) == REFERENCE_TYPE)
/* 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)
/* Handle rvalue reference to function properly. */ /* Handle rvalue reference to function properly. */
conv->rvaluedness_matches_p conv->rvaluedness_matches_p
= (TYPE_REF_IS_RVALUE (rto) == TYPE_REF_IS_RVALUE (rfrom)); = (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, ...@@ -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 /* Don't allow binding of lvalues (other than function lvalues) to
rvalue references. */ rvalue references. */
if (is_lvalue && TYPE_REF_IS_RVALUE (rto) if (is_lvalue && TYPE_REF_IS_RVALUE (rto)
&& TREE_CODE (to) != FUNCTION_TYPE && TREE_CODE (to) != FUNCTION_TYPE)
&& !(flags & LOOKUP_PREFER_RVALUE))
conv->bad_p = true; conv->bad_p = true;
/* Nor the reverse. */ /* Nor the reverse. */
...@@ -6917,6 +6913,7 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum, ...@@ -6917,6 +6913,7 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
else else
flags |= LOOKUP_ONLYCONVERTING; flags |= LOOKUP_ONLYCONVERTING;
if (convs->rvaluedness_matches_p) if (convs->rvaluedness_matches_p)
/* standard_conversion got LOOKUP_PREFER_RVALUE. */
flags |= LOOKUP_PREFER_RVALUE; flags |= LOOKUP_PREFER_RVALUE;
if (TREE_CODE (expr) == TARGET_EXPR if (TREE_CODE (expr) == TARGET_EXPR
&& TARGET_EXPR_LIST_INIT_P (expr)) && TARGET_EXPR_LIST_INIT_P (expr))
...@@ -7716,6 +7713,19 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) ...@@ -7716,6 +7713,19 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
++arg_index; ++arg_index;
parm = TREE_CHAIN (parm); 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. */ /* Bypass access control for 'this' parameter. */
else if (TREE_CODE (TREE_TYPE (fn)) == METHOD_TYPE) else if (TREE_CODE (TREE_TYPE (fn)) == METHOD_TYPE)
......
...@@ -5296,7 +5296,7 @@ enum overload_flags { NO_SPECIAL = 0, DTOR_FLAG, TYPENAME_FLAG }; ...@@ -5296,7 +5296,7 @@ enum overload_flags { NO_SPECIAL = 0, DTOR_FLAG, TYPENAME_FLAG };
(Normally, these entities are registered in the symbol table, but (Normally, these entities are registered in the symbol table, but
not found by lookup.) */ not found by lookup.) */
#define LOOKUP_HIDDEN (LOOKUP_PREFER_NAMESPACES << 1) #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) #define LOOKUP_PREFER_RVALUE (LOOKUP_HIDDEN << 1)
/* We're inside an init-list, so narrowing conversions are ill-formed. */ /* We're inside an init-list, so narrowing conversions are ill-formed. */
#define LOOKUP_NO_NARROWING (LOOKUP_PREFER_RVALUE << 1) #define LOOKUP_NO_NARROWING (LOOKUP_PREFER_RVALUE << 1)
......
...@@ -665,6 +665,7 @@ build_throw (tree exp) ...@@ -665,6 +665,7 @@ build_throw (tree exp)
{ {
int flags = LOOKUP_NORMAL | LOOKUP_ONLYCONVERTING; int flags = LOOKUP_NORMAL | LOOKUP_ONLYCONVERTING;
vec<tree, va_gc> *exp_vec; vec<tree, va_gc> *exp_vec;
bool converted = false;
/* Under C++0x [12.8/16 class.copy], a thrown lvalue is sometimes /* Under C++0x [12.8/16 class.copy], a thrown lvalue is sometimes
treated as an rvalue for the purposes of overload resolution treated as an rvalue for the purposes of overload resolution
...@@ -675,14 +676,31 @@ build_throw (tree exp) ...@@ -675,14 +676,31 @@ build_throw (tree exp)
&& ! TREE_STATIC (exp) && ! TREE_STATIC (exp)
/* The variable must not have the `volatile' qualifier. */ /* The variable must not have the `volatile' qualifier. */
&& !(cp_type_quals (TREE_TYPE (exp)) & TYPE_QUAL_VOLATILE)) && !(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. */ /* Call the copy constructor. */
exp_vec = make_tree_vector_single (exp); if (!converted)
exp = (build_special_member_call {
(object, complete_ctor_identifier, &exp_vec, exp_vec = make_tree_vector_single (exp);
TREE_TYPE (object), flags, tf_warning_or_error)); exp = (build_special_member_call
release_tree_vector (exp_vec); (object, complete_ctor_identifier, &exp_vec,
TREE_TYPE (object), flags, tf_warning_or_error));
release_tree_vector (exp_vec);
}
if (exp == error_mark_node) if (exp == error_mark_node)
{ {
error (" in thrown expression"); error (" in thrown expression");
......
...@@ -9156,6 +9156,7 @@ check_return_expr (tree retval, bool *no_warning) ...@@ -9156,6 +9156,7 @@ check_return_expr (tree retval, bool *no_warning)
Note that these conditions are similar to, but not as strict as, Note that these conditions are similar to, but not as strict as,
the conditions for the named return value optimization. */ the conditions for the named return value optimization. */
bool converted = false;
if ((cxx_dialect != cxx98) if ((cxx_dialect != cxx98)
&& ((VAR_P (retval) && !DECL_HAS_VALUE_EXPR_P (retval)) && ((VAR_P (retval) && !DECL_HAS_VALUE_EXPR_P (retval))
|| TREE_CODE (retval) == PARM_DECL) || TREE_CODE (retval) == PARM_DECL)
...@@ -9163,14 +9164,25 @@ check_return_expr (tree retval, bool *no_warning) ...@@ -9163,14 +9164,25 @@ check_return_expr (tree retval, bool *no_warning)
&& !TREE_STATIC (retval) && !TREE_STATIC (retval)
/* This is only interesting for class type. */ /* This is only interesting for class type. */
&& CLASS_TYPE_P (functype)) && 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 /* First convert the value to the function's return type, then
to the type of return value's location to handle the to the type of return value's location to handle the
case that functype is smaller than the valtype. */ case that functype is smaller than the valtype. */
retval = convert_for_initialization if (!converted)
(NULL_TREE, functype, retval, flags, ICR_RETURN, NULL_TREE, 0, retval = convert_for_initialization
tf_warning_or_error); (NULL_TREE, functype, retval, flags, ICR_RETURN, NULL_TREE, 0,
tf_warning_or_error);
retval = convert (valtype, retval); retval = convert (valtype, retval);
/* If the conversion failed, treat this just like `return;'. */ /* 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