mirror of git://gcc.gnu.org/git/gcc.git
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>
|
2016-10-07 Nathan Sidwell <nathan@acm.org>
|
||||||
|
|
||||||
PR c++/64433
|
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. */
|
creating a garbage BASELINK; constructors can't be inherited. */
|
||||||
ctors = lookup_fnfields_slot (totype, complete_ctor_identifier);
|
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))
|
if (MAYBE_CLASS_TYPE_P (fromtype))
|
||||||
{
|
{
|
||||||
tree to_nonref = non_reference (totype);
|
tree to_nonref = non_reference (totype);
|
||||||
|
|
@ -3745,7 +3753,13 @@ build_user_type_conversion_1 (tree totype, tree expr, int flags,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conv_fns)
|
if (conv_fns)
|
||||||
|
{
|
||||||
|
if (BRACE_ENCLOSED_INITIALIZER_P (expr))
|
||||||
|
/* FIXME see above about C++17. */
|
||||||
|
first_arg = CONSTRUCTOR_ELT (expr, 0)->value;
|
||||||
|
else
|
||||||
first_arg = expr;
|
first_arg = expr;
|
||||||
|
}
|
||||||
|
|
||||||
for (; conv_fns; conv_fns = TREE_CHAIN (conv_fns))
|
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;
|
*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
|
/* 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
|
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
|
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;
|
struct z_candidate *cand = convs->cand;
|
||||||
tree convfn = cand->fn;
|
tree convfn = cand->fn;
|
||||||
unsigned i;
|
|
||||||
|
|
||||||
/* When converting from an init list we consider explicit
|
/* When converting from an init list we consider explicit
|
||||||
constructors, but actually trying to call one is an error. */
|
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);
|
expr = mark_rvalue_use (expr);
|
||||||
|
|
||||||
/* Set user_conv_p on the argument conversions, so rvalue/base
|
/* Pass LOOKUP_NO_CONVERSION so rvalue/base handling knows not to allow
|
||||||
handling knows not to allow any more UDCs. */
|
any more UDCs. */
|
||||||
for (i = 0; i < cand->num_convs; ++i)
|
expr = build_over_call (cand, LOOKUP_NORMAL|LOOKUP_NO_CONVERSION,
|
||||||
cand->convs[i]->user_conv_p = true;
|
complain);
|
||||||
|
|
||||||
expr = build_over_call (cand, LOOKUP_NORMAL, complain);
|
|
||||||
|
|
||||||
/* If this is a constructor or a function returning an aggr type,
|
/* If this is a constructor or a function returning an aggr type,
|
||||||
we need to build up a TARGET_EXPR. */
|
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;
|
flags |= LOOKUP_ONLYCONVERTING;
|
||||||
if (convs->rvaluedness_matches_p)
|
if (convs->rvaluedness_matches_p)
|
||||||
flags |= LOOKUP_PREFER_RVALUE;
|
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);
|
expr = build_temp (expr, totype, flags, &diag_kind, complain);
|
||||||
if (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)");
|
" (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,
|
val = convert_like_with_context (conv, arg, fn, i - is_method,
|
||||||
conversion_warning
|
conversion_warning
|
||||||
? complain
|
? complain
|
||||||
|
|
@ -7825,8 +7842,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
|
||||||
subobject. */
|
subobject. */
|
||||||
if (CHECKING_P && cxx_dialect >= cxx1z)
|
if (CHECKING_P && cxx_dialect >= cxx1z)
|
||||||
gcc_assert (TREE_CODE (arg) != TARGET_EXPR
|
gcc_assert (TREE_CODE (arg) != TARGET_EXPR
|
||||||
// FIXME we shouldn't copy for direct-init either
|
|| seen_error ()
|
||||||
|| !(flags & LOOKUP_ONLYCONVERTING)
|
|
||||||
/* See unsafe_copy_elision_p. */
|
/* See unsafe_copy_elision_p. */
|
||||||
|| DECL_BASE_CONSTRUCTOR_P (fn));
|
|| DECL_BASE_CONSTRUCTOR_P (fn));
|
||||||
|
|
||||||
|
|
@ -8089,6 +8105,19 @@ in_charge_arg_for_name (tree name)
|
||||||
return NULL_TREE;
|
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
|
/* Build a call to a constructor, destructor, or an assignment
|
||||||
operator for INSTANCE, an expression with class type. NAME
|
operator for INSTANCE, an expression with class type. NAME
|
||||||
indicates the special member function to call; *ARGS are the
|
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);
|
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);
|
fns = lookup_fnfields (binfo, name, 1);
|
||||||
|
|
||||||
/* When making a call to a constructor or destructor for a subobject
|
/* 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)
|
if ((complain & tf_error)
|
||||||
&& (flags & LOOKUP_DELEGATING_CONS)
|
&& (flags & LOOKUP_DELEGATING_CONS)
|
||||||
&& name == complete_ctor_identifier
|
&& name == complete_ctor_identifier)
|
||||||
&& TREE_CODE (ret) == CALL_EXPR
|
check_self_delegation (ret);
|
||||||
&& (DECL_ABSTRACT_ORIGIN (TREE_OPERAND (CALL_EXPR_FN (ret), 0))
|
|
||||||
== current_function_decl))
|
|
||||||
error ("constructor delegates to itself");
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5692,7 +5692,6 @@ extern tree convert_to_reference (tree, tree, int, int, tree,
|
||||||
tsubst_flags_t);
|
tsubst_flags_t);
|
||||||
extern tree convert_from_reference (tree);
|
extern tree convert_from_reference (tree);
|
||||||
extern tree force_rvalue (tree, tsubst_flags_t);
|
extern tree force_rvalue (tree, tsubst_flags_t);
|
||||||
extern bool early_elide_copy (tree, tree);
|
|
||||||
extern tree ocp_convert (tree, tree, int, int,
|
extern tree ocp_convert (tree, tree, int, int,
|
||||||
tsubst_flags_t);
|
tsubst_flags_t);
|
||||||
extern tree cp_convert (tree, tree, 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;
|
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...
|
/* Conversion...
|
||||||
|
|
||||||
FLAGS indicates how we should behave. */
|
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))
|
if (error_operand_p (e))
|
||||||
return error_mark_node;
|
return error_mark_node;
|
||||||
|
|
||||||
if (MAYBE_CLASS_TYPE_P (type) && (convtype & CONV_FORCE_TEMP)
|
if (MAYBE_CLASS_TYPE_P (type) && (convtype & CONV_FORCE_TEMP))
|
||||||
&& !early_elide_copy (type, e))
|
|
||||||
/* We need a new temporary; don't take this shortcut. */;
|
/* We need a new temporary; don't take this shortcut. */;
|
||||||
else if (same_type_ignoring_top_level_qualifiers_p (type, TREE_TYPE (e)))
|
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);
|
object = cp_build_indirect_ref (object, RO_NULL, tf_warning_or_error);
|
||||||
|
|
||||||
/* And initialize the exception object. */
|
/* 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;
|
int flags = LOOKUP_NORMAL | LOOKUP_ONLYCONVERTING;
|
||||||
vec<tree, va_gc> *exp_vec;
|
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);
|
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)
|
if (init && BRACE_ENCLOSED_INITIALIZER_P (init)
|
||||||
&& CP_AGGREGATE_TYPE_P (type))
|
&& CP_AGGREGATE_TYPE_P (type))
|
||||||
/* A brace-enclosed initializer for an aggregate. In C++0x this can
|
/* 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
|
initializer, whether that happened just above or in
|
||||||
cp_parser_late_parsing_nsdmi.
|
cp_parser_late_parsing_nsdmi.
|
||||||
|
|
||||||
A TARGET_EXPR for which early_elide_copy is true represents the whole
|
A TARGET_EXPR with TARGET_EXPR_DIRECT_INIT_P or TARGET_EXPR_LIST_INIT_P
|
||||||
initialization, so we shouldn't build up another ctor call. */
|
set represents the whole initialization, so we shouldn't build up
|
||||||
|
another ctor call. */
|
||||||
if (init
|
if (init
|
||||||
&& (TREE_CODE (init) == CONSTRUCTOR
|
&& (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))
|
&& same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (init), type))
|
||||||
{
|
{
|
||||||
/* Early initialization via a TARGET_EXPR only works for
|
/* 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))
|
else if (! MAYBE_CLASS_TYPE_P (lhstype))
|
||||||
/* Do the default thing. */;
|
/* Do the default thing. */;
|
||||||
else if (early_elide_copy (lhstype, rhs))
|
|
||||||
/* Do the default thing. */;
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
vec<tree, va_gc> *rhs_vec = make_tree_vector_single (rhs);
|
vec<tree, va_gc> *rhs_vec = make_tree_vector_single (rhs);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
// PR c++/38698
|
// PR c++/38698
|
||||||
// { dg-do compile { target c++11 } }
|
// { dg-do compile { target c++11 } }
|
||||||
// { dg-prune-output "note" }
|
|
||||||
|
|
||||||
struct A
|
struct A
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -23,3 +23,10 @@ A f() {
|
||||||
else
|
else
|
||||||
return A();
|
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<int, string>>, "");
|
||||||
static_assert(!is_move_constructible_v<variant<AllDeleted, string>>, "");
|
static_assert(!is_move_constructible_v<variant<AllDeleted, string>>, "");
|
||||||
static_assert(!noexcept(variant<int, Empty>(variant<int, Empty>())), "");
|
static_assert(!noexcept(variant<int, Empty>(declval<variant<int, Empty>>())), "");
|
||||||
static_assert(noexcept(variant<int, DefaultNoexcept>(variant<int, DefaultNoexcept>())), "");
|
static_assert(noexcept(variant<int, DefaultNoexcept>(declval<variant<int, DefaultNoexcept>>())), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
void arbitrary_ctor()
|
void arbitrary_ctor()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue