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:
parent
80994c4654
commit
36cbfdb066
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
24
gcc/cp/cvt.c
24
gcc/cp/cvt.c
|
@ -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)))
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
// PR c++/38698
|
||||
// { dg-do compile { target c++11 } }
|
||||
// { dg-prune-output "note" }
|
||||
|
||||
struct A
|
||||
{
|
||||
|
|
|
@ -23,3 +23,10 @@ A f() {
|
|||
else
|
||||
return A();
|
||||
}
|
||||
|
||||
A* ap = new A(f());
|
||||
|
||||
struct B {
|
||||
A a;
|
||||
B(): a(A()) {}
|
||||
};
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue