Commit b7558a2c by Jason Merrill Committed by Jason Merrill

C++17 copy elision improvements.

	* call.c (build_temp, convert_like_real): Don't re-copy
	TARGET_EXPR.  Handle packed fields.
	(build_x_va_arg): Wrap it in a TARGET_EXPR.
	(build_over_call): Add sanity check.
	* cvt.c (early_elide_copy): New.
	(ocp_convert): Use it.
	* except.c (build_throw): Use it.
	* init.c (get_nsdmi): Put back the TARGET_EXPR.
	(expand_default_init): Call early_elide_copy.
	* typeck.c (cp_build_modify_expr): Call early_elide_copy.

From-SVN: r240845
parent 937ec71a
2016-10-06 Jason Merrill <jason@redhat.com>
* call.c (build_temp, convert_like_real): Don't re-copy
TARGET_EXPR. Handle packed fields.
(build_x_va_arg): Wrap it in a TARGET_EXPR.
(build_over_call): Add sanity check.
* cvt.c (early_elide_copy): New.
(ocp_convert): Use it.
* except.c (build_throw): Use it.
* init.c (get_nsdmi): Put back the TARGET_EXPR.
(expand_default_init): Call early_elide_copy.
* typeck.c (cp_build_modify_expr): Call early_elide_copy.
2016-10-06 Jakub Jelinek <jakub@redhat.com> 2016-10-06 Jakub Jelinek <jakub@redhat.com>
Implement P0258R2 - helper for C++17 Implement P0258R2 - helper for C++17
......
...@@ -6365,6 +6365,22 @@ build_temp (tree expr, tree type, int flags, ...@@ -6365,6 +6365,22 @@ build_temp (tree expr, tree type, int flags,
int savew, savee; int savew, savee;
vec<tree, va_gc> *args; vec<tree, va_gc> *args;
*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
do that now. */
if ((lvalue_kind (expr) & clk_packed)
&& CLASS_TYPE_P (TREE_TYPE (expr))
&& !type_has_nontrivial_copy_init (TREE_TYPE (expr)))
return get_target_expr_sfinae (expr, complain);
savew = warningcount + werrorcount, savee = errorcount; savew = warningcount + werrorcount, savee = errorcount;
args = make_tree_vector_single (expr); args = make_tree_vector_single (expr);
expr = build_special_member_call (NULL_TREE, complete_ctor_identifier, expr = build_special_member_call (NULL_TREE, complete_ctor_identifier,
...@@ -6374,8 +6390,6 @@ build_temp (tree expr, tree type, int flags, ...@@ -6374,8 +6390,6 @@ build_temp (tree expr, tree type, int flags,
*diagnostic_kind = DK_WARNING; *diagnostic_kind = DK_WARNING;
else if (errorcount > savee) else if (errorcount > savee)
*diagnostic_kind = DK_ERROR; *diagnostic_kind = DK_ERROR;
else
*diagnostic_kind = DK_UNSPECIFIED;
return expr; return expr;
} }
...@@ -6778,10 +6792,6 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum, ...@@ -6778,10 +6792,6 @@ 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)
{ {
...@@ -7068,7 +7078,12 @@ build_x_va_arg (source_location loc, tree expr, tree type) ...@@ -7068,7 +7078,12 @@ build_x_va_arg (source_location loc, tree expr, tree type)
return convert_from_reference (expr); return convert_from_reference (expr);
} }
return build_va_arg (loc, expr, type); tree ret = build_va_arg (loc, expr, type);
if (CLASS_TYPE_P (type))
/* Wrap the VA_ARG_EXPR in a TARGET_EXPR now so other code doesn't need to
know how to handle it. */
ret = get_target_expr (ret);
return ret;
} }
/* TYPE has been given to va_arg. Apply the default conversions which /* TYPE has been given to va_arg. Apply the default conversions which
...@@ -7806,6 +7821,15 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) ...@@ -7806,6 +7821,15 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
else else
arg = cp_build_indirect_ref (arg, RO_NULL, complain); arg = cp_build_indirect_ref (arg, RO_NULL, complain);
/* In C++17 we shouldn't be copying a TARGET_EXPR except into a base
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)
/* See unsafe_copy_elision_p. */
|| DECL_BASE_CONSTRUCTOR_P (fn));
/* [class.copy]: the copy constructor is implicitly defined even if /* [class.copy]: the copy constructor is implicitly defined even if
the implementation elided its use. */ the implementation elided its use. */
if (!trivial || DECL_DELETED_FN (fn)) if (!trivial || DECL_DELETED_FN (fn))
......
...@@ -5692,6 +5692,7 @@ extern tree convert_to_reference (tree, tree, int, int, tree, ...@@ -5692,6 +5692,7 @@ 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);
......
...@@ -658,6 +658,27 @@ cp_convert_and_check (tree type, tree expr, tsubst_flags_t complain) ...@@ -658,6 +658,27 @@ 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. */
...@@ -694,10 +715,8 @@ ocp_convert (tree type, tree expr, int convtype, int flags, ...@@ -694,10 +715,8 @@ ocp_convert (tree type, tree expr, int convtype, int flags,
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)
&& !(cxx_dialect >= cxx1z && !early_elide_copy (type, e))
&& TREE_CODE (e) == TARGET_EXPR)) /* We need a new temporary; don't take this shortcut. */;
/* We need a new temporary; don't take this shortcut. But in C++17, don't
force a temporary if we already have one. */;
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)))
{ {
if (same_type_p (type, TREE_TYPE (e))) if (same_type_p (type, TREE_TYPE (e)))
......
...@@ -683,7 +683,7 @@ build_throw (tree exp) ...@@ -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)) if (CLASS_TYPE_P (temp_type) && !early_elide_copy (temp_type, 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;
......
...@@ -584,9 +584,13 @@ get_nsdmi (tree member, bool in_ctor) ...@@ -584,9 +584,13 @@ get_nsdmi (tree member, bool in_ctor)
} }
/* Strip redundant TARGET_EXPR so we don't need to remap it, and /* Strip redundant TARGET_EXPR so we don't need to remap it, and
so the aggregate init code below will see a CONSTRUCTOR. */ so the aggregate init code below will see a CONSTRUCTOR. */
if (init && SIMPLE_TARGET_EXPR_P (init)) bool simple_target = (init && SIMPLE_TARGET_EXPR_P (init));
if (simple_target)
init = TARGET_EXPR_INITIAL (init); init = TARGET_EXPR_INITIAL (init);
init = break_out_target_exprs (init); init = break_out_target_exprs (init);
if (simple_target && TREE_CODE (init) != CONSTRUCTOR)
/* Now put it back so C++17 copy elision works. */
init = get_target_expr (init);
} }
current_class_ptr = save_ccp; current_class_ptr = save_ccp;
current_class_ref = save_ccr; current_class_ref = save_ccr;
...@@ -1638,6 +1642,13 @@ expand_default_init (tree binfo, tree true_exp, tree exp, tree init, int flags, ...@@ -1638,6 +1642,13 @@ 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
...@@ -1648,14 +1659,12 @@ expand_default_init (tree binfo, tree true_exp, tree exp, tree init, int flags, ...@@ -1648,14 +1659,12 @@ 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 with TARGET_EXPR_DIRECT_INIT_P or TARGET_EXPR_LIST_INIT_P A TARGET_EXPR for which early_elide_copy is true represents the whole
set represents the whole initialization, so we shouldn't build up initialization, so we shouldn't build up another ctor call. */
another ctor call. */
if (init if (init
&& (TREE_CODE (init) == CONSTRUCTOR && (TREE_CODE (init) == CONSTRUCTOR
|| (TREE_CODE (init) == TARGET_EXPR || early_elide_copy (type, init))
&& (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
......
...@@ -7616,6 +7616,8 @@ cp_build_modify_expr (location_t loc, tree lhs, enum tree_code modifycode, ...@@ -7616,6 +7616,8 @@ 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);
......
...@@ -2325,7 +2325,11 @@ release of G++. ...@@ -2325,7 +2325,11 @@ release of G++.
The C++ standard allows an implementation to omit creating a temporary The C++ standard allows an implementation to omit creating a temporary
that is only used to initialize another object of the same type. that is only used to initialize another object of the same type.
Specifying this option disables that optimization, and forces G++ to Specifying this option disables that optimization, and forces G++ to
call the copy constructor in all cases. call the copy constructor in all cases. This option also causes G++
to call trivial member functions which otherwise would be expanded inline.
In C++17, the compiler is required to omit these temporaries, but this
option still affects trivial member functions.
@item -fno-enforce-eh-specs @item -fno-enforce-eh-specs
@opindex fno-enforce-eh-specs @opindex fno-enforce-eh-specs
......
...@@ -8,7 +8,7 @@ struct A ...@@ -8,7 +8,7 @@ struct A
}; };
constexpr A a; constexpr A a;
constexpr A b = A(); // { dg-error "" } constexpr A b = A(); // { dg-error "" "" { target c++14_down } }
#define SA(X) static_assert ((X), #X) #define SA(X) static_assert ((X), #X)
SA(a.p == &a); SA(a.p == &a);
...@@ -8,7 +8,7 @@ struct b ...@@ -8,7 +8,7 @@ struct b
b() = default; b() = default;
~b() = default; ~b() = default;
b& operator=(const b&) = delete; b& operator=(const b&) = delete;
b(const b&) = delete; // { dg-message "declared" } b(const b&) = delete; // { dg-message "declared" "" { target c++14_down } }
b(bool _t): t (_t) { } b(bool _t): t (_t) { }
}; };
...@@ -19,7 +19,7 @@ int main() ...@@ -19,7 +19,7 @@ int main()
b tst1 = { false }; b tst1 = { false };
// copy initialization. // copy initialization.
b tst2 = false; // { dg-error "use" } b tst2 = false; // { dg-error "use" "" { target c++14_down } }
// direct list initialization // direct list initialization
b tst3 { false }; b tst3 { false };
......
...@@ -10,5 +10,10 @@ void a(A) noexcept {} ...@@ -10,5 +10,10 @@ void a(A) noexcept {}
void f() void f()
{ {
static_assert(!noexcept(a(A{})), ""); #if __cplusplus <= 201402L
const bool val = false;
#else
const bool val = true;
#endif
static_assert(noexcept(a(A{})) == val, "");
} }
...@@ -13,7 +13,12 @@ void a(A<T>) noexcept {} ...@@ -13,7 +13,12 @@ void a(A<T>) noexcept {}
template<typename T> template<typename T>
void f() void f()
{ {
static_assert(!noexcept(a(A<T>{})), ""); #if __cplusplus <= 201402L
const bool val = false;
#else
const bool val = true;
#endif
static_assert(val == noexcept(a(A<T>{})), "");
} }
void g() void g()
......
...@@ -14,3 +14,12 @@ A a2 = (42, A()); ...@@ -14,3 +14,12 @@ A a2 = (42, A());
A f(); A f();
A a3 = f(); A a3 = f();
A a4 = b ? A() : f(); A a4 = b ? A() : f();
void g(A);
A f() {
g(A());
if (b)
throw A();
else
return A();
}
// { dg-do run } // { dg-do run { target c++14_down } }
// { dg-options "-fno-elide-constructors" } // { dg-options "-fno-elide-constructors" }
int copies; int copies;
......
...@@ -11,12 +11,12 @@ struct A {}; ...@@ -11,12 +11,12 @@ struct A {};
struct B : A struct B : A
{ {
B(int); B(int);
B(B&); // { dg-message "note" "" } B(B&); // { dg-message "note" "" { target c++14_down } }
}; };
void foo(B); // { dg-message "initializing" } void foo(B); // { dg-message "initializing" "" { target c++14_down } }
void bar() void bar()
{ {
foo(0); // { dg-error "" } foo(0); // { dg-error "" "" { target c++14_down } }
} }
...@@ -6,9 +6,9 @@ ...@@ -6,9 +6,9 @@
struct A struct A
{ {
A(A&); // { dg-message "A::A" } A(A&); // { dg-message "A::A" "" { target c++14_down } }
template <class T> A(T); // { dg-message "A::A" } template <class T> A(T); // { dg-message "A::A" "" { target c++14_down } }
}; };
A a = 0; // { dg-error "" } A a = 0; // { dg-error "" "" { target c++14_down } }
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
struct A struct A
{ {
A(); A();
A(A&); // { dg-message "A::A|no known conversion" } referenced below A(A&); // { dg-message "A::A|no known conversion" "" { target c++14_down } } referenced below
}; };
int int
...@@ -10,8 +10,8 @@ main () ...@@ -10,8 +10,8 @@ main ()
{ {
try try
{ {
throw A(); // { dg-error "rvalue" "" } can't copy throw A(); // { dg-error "rvalue" "" { target c++14_down } } can't copy
// { dg-error "thrown expression" "expr" { target *-*-* } 13 } // { dg-error "thrown expression" "expr" { target c++14_down } 13 }
} }
catch (...) { } catch (...) { }
} }
...@@ -3,7 +3,7 @@ class X // Indentation has been done so to see the similarities. ...@@ -3,7 +3,7 @@ class X // Indentation has been done so to see the similarities.
{ {
public: public:
X() {} X() {}
X(X& x) {x.i=7;} // { dg-message "note" } Both functions modify the X(X& x) {x.i=7;} // { dg-message "note" "" { target c++14_down } } Both functions modify the
void bar(X& x) {x.i=7;} // { dg-message "note" } reference parameter x. void bar(X& x) {x.i=7;} // { dg-message "note" } reference parameter x.
int i; int i;
}; };
...@@ -12,6 +12,6 @@ X foo() { X x; return x; } ...@@ -12,6 +12,6 @@ X foo() { X x; return x; }
int main() int main()
{ {
X x(foo()); // { dg-error "rvalue" } Compiler doesn't warn about temporary reference. X x(foo()); // { dg-error "rvalue" "" { target c++14_down } } Compiler doesn't warn about temporary reference.
x.bar(foo()); // { dg-error "rvalue" } The same mistake is warned about in this case. x.bar(foo()); // { dg-error "rvalue" } The same mistake is warned about in this case.
} }
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
class A class A
{ {
public: public:
A(A &); // { dg-message "note" } A(A &); // { dg-message "note" "" { target c++14_down } }
}; };
class B class B
...@@ -18,6 +18,6 @@ class C ...@@ -18,6 +18,6 @@ class C
C() C()
{ {
B b; B b;
A a = b;// { dg-error "rvalue" } A a = b;// { dg-error "rvalue" "" { target c++14_down } }
} }
}; };
...@@ -9,7 +9,7 @@ template<typename X> struct auto_ptr { ...@@ -9,7 +9,7 @@ template<typename X> struct auto_ptr {
typedef X element_type; typedef X element_type;
explicit auto_ptr(X* p =0) throw() : px(p) {} explicit auto_ptr(X* p =0) throw() : px(p) {}
auto_ptr(auto_ptr& r) throw() : px(r.release()) {} // { dg-message "note" } candidate auto_ptr(auto_ptr& r) throw() : px(r.release()) {} // { dg-message "note" "" { target c++14_down } } candidate
template<typename Y> template<typename Y>
auto_ptr(auto_ptr<Y>& r) throw() : px(r.release()) {} auto_ptr(auto_ptr<Y>& r) throw() : px(r.release()) {}
...@@ -44,12 +44,12 @@ struct Derived : Base { Derived() {} }; ...@@ -44,12 +44,12 @@ struct Derived : Base { Derived() {} };
auto_ptr<Derived> f() { auto_ptr<Derived> null(0); return null; } auto_ptr<Derived> f() { auto_ptr<Derived> null(0); return null; }
void g(auto_ptr<Derived>) { } void g(auto_ptr<Derived>) { }
void h(auto_ptr<Base>) { } // { dg-message "initializing" } void h(auto_ptr<Base>) { } // { dg-message "initializing" "" { target c++14_down } }
int main() { int main() {
auto_ptr<Base> x(f()); auto_ptr<Base> x(f());
auto_ptr<Derived> y(f()); auto_ptr<Derived> y(f());
x = y; x = y;
g(f()); g(f());
h(f()); // { dg-error "rvalue" "" } no usable copy ctor h(f()); // { dg-error "rvalue" "" { target c++14_down } } no usable copy ctor
} }
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