From b7558a2c1f87e374c48fa2be8e3ab93e1b3c68b0 Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Thu, 6 Oct 2016 17:24:40 -0400 Subject: [PATCH] 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 --- gcc/cp/ChangeLog | 13 +++++++ gcc/cp/call.c | 38 +++++++++++++++---- gcc/cp/cp-tree.h | 1 + gcc/cp/cvt.c | 27 +++++++++++-- gcc/cp/except.c | 2 +- gcc/cp/init.c | 23 +++++++---- gcc/cp/typeck.c | 2 + gcc/doc/invoke.texi | 6 ++- .../g++.dg/cpp0x/constexpr-ctor14a.C | 2 +- gcc/testsuite/g++.dg/cpp0x/initlist9.C | 4 +- gcc/testsuite/g++.dg/cpp0x/noexcept23.C | 7 +++- gcc/testsuite/g++.dg/cpp0x/noexcept24.C | 7 +++- gcc/testsuite/g++.dg/cpp1z/elide1.C | 9 +++++ gcc/testsuite/g++.dg/init/copy3.C | 2 +- gcc/testsuite/g++.dg/overload/arg3.C | 6 +-- gcc/testsuite/g++.dg/template/copy1.C | 6 +-- gcc/testsuite/g++.old-deja/g++.eh/ctor1.C | 6 +-- .../g++.old-deja/g++.jason/temporary2.C | 4 +- gcc/testsuite/g++.old-deja/g++.mike/p2431.C | 4 +- gcc/testsuite/g++.old-deja/g++.pt/auto_ptr.C | 6 +-- 20 files changed, 133 insertions(+), 42 deletions(-) diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 008d6461acf..553658f42d1 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,16 @@ +2016-10-06 Jason Merrill + + * 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 Implement P0258R2 - helper for C++17 diff --git a/gcc/cp/call.c b/gcc/cp/call.c index dac1337cc49..6feaf7e22c9 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -6365,6 +6365,22 @@ build_temp (tree expr, tree type, int flags, int savew, savee; vec *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; args = make_tree_vector_single (expr); expr = build_special_member_call (NULL_TREE, complete_ctor_identifier, @@ -6374,8 +6390,6 @@ build_temp (tree expr, tree type, int flags, *diagnostic_kind = DK_WARNING; else if (errorcount > savee) *diagnostic_kind = DK_ERROR; - else - *diagnostic_kind = DK_UNSPECIFIED; return expr; } @@ -6778,10 +6792,6 @@ 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) { @@ -7068,7 +7078,12 @@ build_x_va_arg (source_location loc, tree expr, tree type) 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 @@ -7806,6 +7821,15 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) else 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 the implementation elided its use. */ if (!trivial || DECL_DELETED_FN (fn)) diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 49cbdf26fcf..9282bbe3883 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -5692,6 +5692,7 @@ 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); diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c index ecc8ef82b2a..063457f1c5e 100644 --- a/gcc/cp/cvt.c +++ b/gcc/cp/cvt.c @@ -658,6 +658,27 @@ 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. */ @@ -694,10 +715,8 @@ ocp_convert (tree type, tree expr, int convtype, int flags, return error_mark_node; if (MAYBE_CLASS_TYPE_P (type) && (convtype & CONV_FORCE_TEMP) - && !(cxx_dialect >= cxx1z - && TREE_CODE (e) == TARGET_EXPR)) - /* We need a new temporary; don't take this shortcut. But in C++17, don't - force a temporary if we already have one. */; + && !early_elide_copy (type, e)) + /* We need a new temporary; don't take this shortcut. */; else if (same_type_ignoring_top_level_qualifiers_p (type, TREE_TYPE (e))) { if (same_type_p (type, TREE_TYPE (e))) diff --git a/gcc/cp/except.c b/gcc/cp/except.c index 1c60b08bb10..2f88082bf5e 100644 --- a/gcc/cp/except.c +++ b/gcc/cp/except.c @@ -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)) + if (CLASS_TYPE_P (temp_type) && !early_elide_copy (temp_type, exp)) { int flags = LOOKUP_NORMAL | LOOKUP_ONLYCONVERTING; vec *exp_vec; diff --git a/gcc/cp/init.c b/gcc/cp/init.c index d1c8274003c..63c3dab9b64 100644 --- a/gcc/cp/init.c +++ b/gcc/cp/init.c @@ -584,9 +584,13 @@ get_nsdmi (tree member, bool in_ctor) } /* Strip redundant TARGET_EXPR so we don't need to remap it, and 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 = 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_ref = save_ccr; @@ -1638,6 +1642,13 @@ 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 @@ -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 cp_parser_late_parsing_nsdmi. - 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. */ + A TARGET_EXPR for which early_elide_copy is true represents the whole + initialization, so we shouldn't build up another ctor call. */ + if (init && (TREE_CODE (init) == CONSTRUCTOR - || (TREE_CODE (init) == TARGET_EXPR - && (TARGET_EXPR_DIRECT_INIT_P (init) - || TARGET_EXPR_LIST_INIT_P (init)))) + || early_elide_copy (type, init)) && same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (init), type)) { /* Early initialization via a TARGET_EXPR only works for diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index c70cfc80ff7..f1abb40a775 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -7616,6 +7616,8 @@ 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 *rhs_vec = make_tree_vector_single (rhs); diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index d9667e7f5d9..22af6e45673 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -2325,7 +2325,11 @@ release of G++. The C++ standard allows an implementation to omit creating a temporary that is only used to initialize another object of the same type. 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 @opindex fno-enforce-eh-specs diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ctor14a.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-ctor14a.C index 644ae63fe0d..8d1ebcd94f1 100644 --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-ctor14a.C +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ctor14a.C @@ -8,7 +8,7 @@ struct 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) SA(a.p == &a); diff --git a/gcc/testsuite/g++.dg/cpp0x/initlist9.C b/gcc/testsuite/g++.dg/cpp0x/initlist9.C index bb6414c6228..ef806d0c8b7 100644 --- a/gcc/testsuite/g++.dg/cpp0x/initlist9.C +++ b/gcc/testsuite/g++.dg/cpp0x/initlist9.C @@ -8,7 +8,7 @@ struct b b() = default; ~b() = default; 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) { } }; @@ -19,7 +19,7 @@ int main() b tst1 = { false }; // copy initialization. - b tst2 = false; // { dg-error "use" } + b tst2 = false; // { dg-error "use" "" { target c++14_down } } // direct list initialization b tst3 { false }; diff --git a/gcc/testsuite/g++.dg/cpp0x/noexcept23.C b/gcc/testsuite/g++.dg/cpp0x/noexcept23.C index 5a01df46c65..b630b23a3c4 100644 --- a/gcc/testsuite/g++.dg/cpp0x/noexcept23.C +++ b/gcc/testsuite/g++.dg/cpp0x/noexcept23.C @@ -10,5 +10,10 @@ void a(A) noexcept {} 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, ""); } diff --git a/gcc/testsuite/g++.dg/cpp0x/noexcept24.C b/gcc/testsuite/g++.dg/cpp0x/noexcept24.C index c17ddfa52a7..60ca44395b2 100644 --- a/gcc/testsuite/g++.dg/cpp0x/noexcept24.C +++ b/gcc/testsuite/g++.dg/cpp0x/noexcept24.C @@ -13,7 +13,12 @@ void a(A) noexcept {} template void f() { - static_assert(!noexcept(a(A{})), ""); +#if __cplusplus <= 201402L + const bool val = false; +#else + const bool val = true; +#endif + static_assert(val == noexcept(a(A{})), ""); } void g() diff --git a/gcc/testsuite/g++.dg/cpp1z/elide1.C b/gcc/testsuite/g++.dg/cpp1z/elide1.C index a0538bbbef8..71476e23970 100644 --- a/gcc/testsuite/g++.dg/cpp1z/elide1.C +++ b/gcc/testsuite/g++.dg/cpp1z/elide1.C @@ -14,3 +14,12 @@ A a2 = (42, A()); A f(); A a3 = f(); A a4 = b ? A() : f(); + +void g(A); +A f() { + g(A()); + if (b) + throw A(); + else + return A(); +} diff --git a/gcc/testsuite/g++.dg/init/copy3.C b/gcc/testsuite/g++.dg/init/copy3.C index fa6a6ea1884..cfc73943928 100644 --- a/gcc/testsuite/g++.dg/init/copy3.C +++ b/gcc/testsuite/g++.dg/init/copy3.C @@ -1,4 +1,4 @@ -// { dg-do run } +// { dg-do run { target c++14_down } } // { dg-options "-fno-elide-constructors" } int copies; diff --git a/gcc/testsuite/g++.dg/overload/arg3.C b/gcc/testsuite/g++.dg/overload/arg3.C index 1684fccf5c7..9507c027252 100644 --- a/gcc/testsuite/g++.dg/overload/arg3.C +++ b/gcc/testsuite/g++.dg/overload/arg3.C @@ -11,12 +11,12 @@ struct A {}; struct B : A { 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() { - foo(0); // { dg-error "" } + foo(0); // { dg-error "" "" { target c++14_down } } } diff --git a/gcc/testsuite/g++.dg/template/copy1.C b/gcc/testsuite/g++.dg/template/copy1.C index bf5a37ce0ab..a34221df38b 100644 --- a/gcc/testsuite/g++.dg/template/copy1.C +++ b/gcc/testsuite/g++.dg/template/copy1.C @@ -6,9 +6,9 @@ struct A { - A(A&); // { dg-message "A::A" } - template A(T); // { dg-message "A::A" } + A(A&); // { dg-message "A::A" "" { target c++14_down } } + template A(T); // { dg-message "A::A" "" { target c++14_down } } }; -A a = 0; // { dg-error "" } +A a = 0; // { dg-error "" "" { target c++14_down } } diff --git a/gcc/testsuite/g++.old-deja/g++.eh/ctor1.C b/gcc/testsuite/g++.old-deja/g++.eh/ctor1.C index 9b4adaf0e4a..21b27d707f2 100644 --- a/gcc/testsuite/g++.old-deja/g++.eh/ctor1.C +++ b/gcc/testsuite/g++.old-deja/g++.eh/ctor1.C @@ -2,7 +2,7 @@ struct 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 @@ -10,8 +10,8 @@ main () { try { - throw A(); // { dg-error "rvalue" "" } can't copy -// { dg-error "thrown expression" "expr" { target *-*-* } 13 } + throw A(); // { dg-error "rvalue" "" { target c++14_down } } can't copy + // { dg-error "thrown expression" "expr" { target c++14_down } 13 } } catch (...) { } } diff --git a/gcc/testsuite/g++.old-deja/g++.jason/temporary2.C b/gcc/testsuite/g++.old-deja/g++.jason/temporary2.C index e55738477f2..c855f8f4a07 100644 --- a/gcc/testsuite/g++.old-deja/g++.jason/temporary2.C +++ b/gcc/testsuite/g++.old-deja/g++.jason/temporary2.C @@ -3,7 +3,7 @@ class X // Indentation has been done so to see the similarities. { public: 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. int i; }; @@ -12,6 +12,6 @@ X foo() { X x; return x; } 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. } diff --git a/gcc/testsuite/g++.old-deja/g++.mike/p2431.C b/gcc/testsuite/g++.old-deja/g++.mike/p2431.C index 8a7ede660ba..d990a106e55 100644 --- a/gcc/testsuite/g++.old-deja/g++.mike/p2431.C +++ b/gcc/testsuite/g++.old-deja/g++.mike/p2431.C @@ -3,7 +3,7 @@ class A { public: - A(A &); // { dg-message "note" } + A(A &); // { dg-message "note" "" { target c++14_down } } }; class B @@ -18,6 +18,6 @@ class C C() { B b; - A a = b;// { dg-error "rvalue" } + A a = b;// { dg-error "rvalue" "" { target c++14_down } } } }; diff --git a/gcc/testsuite/g++.old-deja/g++.pt/auto_ptr.C b/gcc/testsuite/g++.old-deja/g++.pt/auto_ptr.C index 4a363a27f30..35e1cc264cc 100644 --- a/gcc/testsuite/g++.old-deja/g++.pt/auto_ptr.C +++ b/gcc/testsuite/g++.old-deja/g++.pt/auto_ptr.C @@ -9,7 +9,7 @@ template struct auto_ptr { typedef X element_type; 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 auto_ptr(auto_ptr& r) throw() : px(r.release()) {} @@ -44,12 +44,12 @@ struct Derived : Base { Derived() {} }; auto_ptr f() { auto_ptr null(0); return null; } void g(auto_ptr) { } -void h(auto_ptr) { } // { dg-message "initializing" } +void h(auto_ptr) { } // { dg-message "initializing" "" { target c++14_down } } int main() { auto_ptr x(f()); auto_ptr y(f()); x = y; g(f()); - h(f()); // { dg-error "rvalue" "" } no usable copy ctor + h(f()); // { dg-error "rvalue" "" { target c++14_down } } no usable copy ctor } -- 2.30.2