Further P0135 refinement.

* call.c (build_user_type_conversion_1): Consider conversions from
	a single element in an initializer-list.
	(build_temp): Undo early_elide_copy change.
	(build_over_call): Check that we don't try to copy a TARGET_EXPR
	in C++17 mode.  Set user_conv_p here.
	(convert_like_real): Not here.
	(check_self_delegation): Split out from...
	(build_special_member_call): ...here.  Handle C++17 copy elision.
	* cvt.c (early_elide_copy): Remove.
	(ocp_convert): Undo early_elide_copy change.
	* except.c (build_throw): Likewise.
	* init.c (expand_default_init): Likewise.
	* typeck.c (cp_build_modify_expr): Likewise.

From-SVN: r240889
This commit is contained in:
Jason Merrill 2016-10-08 12:23:26 -04:00 committed by Jason Merrill
parent 80994c4654
commit 36cbfdb066
10 changed files with 112 additions and 61 deletions

View File

@ -1,3 +1,20 @@
2016-10-07 Jason Merrill <jason@redhat.com>
Further P0135 refinement.
* call.c (build_user_type_conversion_1): Consider conversions from
a single element in an initializer-list.
(build_temp): Undo early_elide_copy change.
(build_over_call): Check that we don't try to copy a TARGET_EXPR
in C++17 mode. Set user_conv_p here.
(convert_like_real): Not here.
(check_self_delegation): Split out from...
(build_special_member_call): ...here. Handle C++17 copy elision.
* cvt.c (early_elide_copy): Remove.
(ocp_convert): Undo early_elide_copy change.
* except.c (build_throw): Likewise.
* init.c (expand_default_init): Likewise.
* typeck.c (cp_build_modify_expr): Likewise.
2016-10-07 Nathan Sidwell <nathan@acm.org>
PR c++/64433

View File

@ -3671,6 +3671,14 @@ build_user_type_conversion_1 (tree totype, tree expr, int flags,
creating a garbage BASELINK; constructors can't be inherited. */
ctors = lookup_fnfields_slot (totype, complete_ctor_identifier);
/* FIXME P0135 doesn't say what to do in C++17 about list-initialization from
a single element. For now, let's handle constructors as before and also
consider conversion operators from the element. */
if (cxx_dialect >= cxx1z
&& BRACE_ENCLOSED_INITIALIZER_P (expr)
&& CONSTRUCTOR_NELTS (expr) == 1)
fromtype = TREE_TYPE (CONSTRUCTOR_ELT (expr, 0)->value);
if (MAYBE_CLASS_TYPE_P (fromtype))
{
tree to_nonref = non_reference (totype);
@ -3745,7 +3753,13 @@ build_user_type_conversion_1 (tree totype, tree expr, int flags,
}
if (conv_fns)
first_arg = expr;
{
if (BRACE_ENCLOSED_INITIALIZER_P (expr))
/* FIXME see above about C++17. */
first_arg = CONSTRUCTOR_ELT (expr, 0)->value;
else
first_arg = expr;
}
for (; conv_fns; conv_fns = TREE_CHAIN (conv_fns))
{
@ -6367,11 +6381,6 @@ build_temp (tree expr, tree type, int flags,
*diagnostic_kind = DK_UNSPECIFIED;
if (TREE_CODE (expr) == CONSTRUCTOR)
expr = get_target_expr_sfinae (expr, complain);
if (early_elide_copy (type, expr))
return expr;
/* If the source is a packed field, calling the copy constructor will require
binding the field to the reference parameter to the copy constructor, and
we'll end up with an infinite loop. If we can use a bitwise copy, then
@ -6563,7 +6572,6 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
{
struct z_candidate *cand = convs->cand;
tree convfn = cand->fn;
unsigned i;
/* When converting from an init list we consider explicit
constructors, but actually trying to call one is an error. */
@ -6609,12 +6617,10 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
expr = mark_rvalue_use (expr);
/* Set user_conv_p on the argument conversions, so rvalue/base
handling knows not to allow any more UDCs. */
for (i = 0; i < cand->num_convs; ++i)
cand->convs[i]->user_conv_p = true;
expr = build_over_call (cand, LOOKUP_NORMAL, complain);
/* Pass LOOKUP_NO_CONVERSION so rvalue/base handling knows not to allow
any more UDCs. */
expr = build_over_call (cand, LOOKUP_NORMAL|LOOKUP_NO_CONVERSION,
complain);
/* If this is a constructor or a function returning an aggr type,
we need to build up a TARGET_EXPR. */
@ -6792,6 +6798,10 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
flags |= LOOKUP_ONLYCONVERTING;
if (convs->rvaluedness_matches_p)
flags |= LOOKUP_PREFER_RVALUE;
if (TREE_CODE (expr) == TARGET_EXPR
&& TARGET_EXPR_LIST_INIT_P (expr))
/* Copy-list-initialization doesn't actually involve a copy. */
return expr;
expr = build_temp (expr, totype, flags, &diag_kind, complain);
if (diag_kind && complain)
{
@ -7710,6 +7720,13 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
" (you can disable this with -fno-deduce-init-list)");
}
}
/* Set user_conv_p on the argument conversions, so rvalue/base handling
knows not to allow any more UDCs. This needs to happen after we
process cand->warnings. */
if (flags & LOOKUP_NO_CONVERSION)
conv->user_conv_p = true;
val = convert_like_with_context (conv, arg, fn, i - is_method,
conversion_warning
? complain
@ -7825,8 +7842,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
subobject. */
if (CHECKING_P && cxx_dialect >= cxx1z)
gcc_assert (TREE_CODE (arg) != TARGET_EXPR
// FIXME we shouldn't copy for direct-init either
|| !(flags & LOOKUP_ONLYCONVERTING)
|| seen_error ()
/* See unsafe_copy_elision_p. */
|| DECL_BASE_CONSTRUCTOR_P (fn));
@ -8089,6 +8105,19 @@ in_charge_arg_for_name (tree name)
return NULL_TREE;
}
/* We've built up a constructor call RET. Complain if it delegates to the
constructor we're currently compiling. */
static void
check_self_delegation (tree ret)
{
if (TREE_CODE (ret) == TARGET_EXPR)
ret = TARGET_EXPR_INITIAL (ret);
tree fn = cp_get_callee_fndecl (ret);
if (fn && DECL_ABSTRACT_ORIGIN (fn) == current_function_decl)
error ("constructor delegates to itself");
}
/* Build a call to a constructor, destructor, or an assignment
operator for INSTANCE, an expression with class type. NAME
indicates the special member function to call; *ARGS are the
@ -8162,6 +8191,38 @@ build_special_member_call (tree instance, tree name, vec<tree, va_gc> **args,
gcc_assert (instance != NULL_TREE);
/* In C++17, "If the initializer expression is a prvalue and the
cv-unqualified version of the source type is the same class as the class
of the destination, the initializer expression is used to initialize the
destination object." Handle that here to avoid doing overload
resolution. */
if (cxx_dialect >= cxx1z
&& args && vec_safe_length (*args) == 1
&& name == complete_ctor_identifier)
{
tree arg = (**args)[0];
/* FIXME P0135 doesn't say how to handle direct initialization from a
type with a suitable conversion operator. Let's handle it like
copy-initialization, but allowing explict conversions. */
if (!reference_related_p (class_type, TREE_TYPE (arg)))
arg = perform_implicit_conversion_flags (class_type, arg,
tf_warning, flags);
if (TREE_CODE (arg) == TARGET_EXPR
&& (same_type_ignoring_top_level_qualifiers_p
(class_type, TREE_TYPE (arg))))
{
if (is_dummy_object (instance))
return arg;
if ((complain & tf_error)
&& (flags & LOOKUP_DELEGATING_CONS))
check_self_delegation (arg);
/* Avoid change of behavior on Wunused-var-2.C. */
mark_lvalue_use (instance);
return build2 (INIT_EXPR, class_type, instance, arg);
}
}
fns = lookup_fnfields (binfo, name, 1);
/* When making a call to a constructor or destructor for a subobject
@ -8206,11 +8267,8 @@ build_special_member_call (tree instance, tree name, vec<tree, va_gc> **args,
if ((complain & tf_error)
&& (flags & LOOKUP_DELEGATING_CONS)
&& name == complete_ctor_identifier
&& TREE_CODE (ret) == CALL_EXPR
&& (DECL_ABSTRACT_ORIGIN (TREE_OPERAND (CALL_EXPR_FN (ret), 0))
== current_function_decl))
error ("constructor delegates to itself");
&& name == complete_ctor_identifier)
check_self_delegation (ret);
return ret;
}

View File

@ -5692,7 +5692,6 @@ extern tree convert_to_reference (tree, tree, int, int, tree,
tsubst_flags_t);
extern tree convert_from_reference (tree);
extern tree force_rvalue (tree, tsubst_flags_t);
extern bool early_elide_copy (tree, tree);
extern tree ocp_convert (tree, tree, int, int,
tsubst_flags_t);
extern tree cp_convert (tree, tree, tsubst_flags_t);

View File

@ -658,27 +658,6 @@ cp_convert_and_check (tree type, tree expr, tsubst_flags_t complain)
return result;
}
/* Returns true if we should avoid even doing overload resolution for copying
EXPR to initialize a TYPE. */
bool
early_elide_copy (tree type, tree expr)
{
if (TREE_CODE (expr) != TARGET_EXPR)
return false;
/* List-initialization and direct-initialization don't involve a copy. */
if (TARGET_EXPR_LIST_INIT_P (expr)
|| TARGET_EXPR_DIRECT_INIT_P (expr))
return true;
/* In C++17, "If the initializer expression is a prvalue and the
cv-unqualified version of the source type is the same class as the class
of the destination, the initializer expression is used to initialize the
destination object." */
return (cxx_dialect >= cxx1z
&& (same_type_ignoring_top_level_qualifiers_p
(type, TREE_TYPE (expr))));
}
/* Conversion...
FLAGS indicates how we should behave. */
@ -714,8 +693,7 @@ ocp_convert (tree type, tree expr, int convtype, int flags,
if (error_operand_p (e))
return error_mark_node;
if (MAYBE_CLASS_TYPE_P (type) && (convtype & CONV_FORCE_TEMP)
&& !early_elide_copy (type, e))
if (MAYBE_CLASS_TYPE_P (type) && (convtype & CONV_FORCE_TEMP))
/* We need a new temporary; don't take this shortcut. */;
else if (same_type_ignoring_top_level_qualifiers_p (type, TREE_TYPE (e)))
{

View File

@ -683,7 +683,7 @@ build_throw (tree exp)
object = cp_build_indirect_ref (object, RO_NULL, tf_warning_or_error);
/* And initialize the exception object. */
if (CLASS_TYPE_P (temp_type) && !early_elide_copy (temp_type, exp))
if (CLASS_TYPE_P (temp_type))
{
int flags = LOOKUP_NORMAL | LOOKUP_ONLYCONVERTING;
vec<tree, va_gc> *exp_vec;

View File

@ -1644,13 +1644,6 @@ expand_default_init (tree binfo, tree true_exp, tree exp, tree init, int flags,
init = reshape_init (type, init, complain);
}
/* Also pull out a TARGET_EXPR that we want to avoid copying. */
if (init && true_exp == exp
&& TREE_CODE (init) == TREE_LIST
&& list_length (init) == 1
&& early_elide_copy (type, TREE_VALUE (init)))
init = TREE_VALUE (init);
if (init && BRACE_ENCLOSED_INITIALIZER_P (init)
&& CP_AGGREGATE_TYPE_P (type))
/* A brace-enclosed initializer for an aggregate. In C++0x this can
@ -1661,12 +1654,14 @@ expand_default_init (tree binfo, tree true_exp, tree exp, tree init, int flags,
initializer, whether that happened just above or in
cp_parser_late_parsing_nsdmi.
A TARGET_EXPR for which early_elide_copy is true represents the whole
initialization, so we shouldn't build up another ctor call. */
A TARGET_EXPR with TARGET_EXPR_DIRECT_INIT_P or TARGET_EXPR_LIST_INIT_P
set represents the whole initialization, so we shouldn't build up
another ctor call. */
if (init
&& (TREE_CODE (init) == CONSTRUCTOR
|| early_elide_copy (type, init))
|| (TREE_CODE (init) == TARGET_EXPR
&& (TARGET_EXPR_DIRECT_INIT_P (init)
|| TARGET_EXPR_LIST_INIT_P (init))))
&& same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (init), type))
{
/* Early initialization via a TARGET_EXPR only works for

View File

@ -7639,8 +7639,6 @@ cp_build_modify_expr (location_t loc, tree lhs, enum tree_code modifycode,
}
else if (! MAYBE_CLASS_TYPE_P (lhstype))
/* Do the default thing. */;
else if (early_elide_copy (lhstype, rhs))
/* Do the default thing. */;
else
{
vec<tree, va_gc> *rhs_vec = make_tree_vector_single (rhs);

View File

@ -1,6 +1,5 @@
// PR c++/38698
// { dg-do compile { target c++11 } }
// { dg-prune-output "note" }
struct A
{

View File

@ -23,3 +23,10 @@ A f() {
else
return A();
}
A* ap = new A(f());
struct B {
A a;
B(): a(A()) {}
};

View File

@ -91,8 +91,8 @@ void move_ctor()
{
static_assert(is_move_constructible_v<variant<int, string>>, "");
static_assert(!is_move_constructible_v<variant<AllDeleted, string>>, "");
static_assert(!noexcept(variant<int, Empty>(variant<int, Empty>())), "");
static_assert(noexcept(variant<int, DefaultNoexcept>(variant<int, DefaultNoexcept>())), "");
static_assert(!noexcept(variant<int, Empty>(declval<variant<int, Empty>>())), "");
static_assert(noexcept(variant<int, DefaultNoexcept>(declval<variant<int, DefaultNoexcept>>())), "");
}
void arbitrary_ctor()