aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorJason Merrill <jason@redhat.com>2016-10-06 17:24:40 -0400
committerJason Merrill <jason@gcc.gnu.org>2016-10-06 17:24:40 -0400
commitb7558a2c1f87e374c48fa2be8e3ab93e1b3c68b0 (patch)
treec9167e38db4b5297835f67da69fdb6b691450d9f /gcc
parent937ec71aba5b3c633bd5979d6346254e83f46d1c (diff)
downloadgcc-b7558a2c1f87e374c48fa2be8e3ab93e1b3c68b0.zip
gcc-b7558a2c1f87e374c48fa2be8e3ab93e1b3c68b0.tar.gz
gcc-b7558a2c1f87e374c48fa2be8e3ab93e1b3c68b0.tar.bz2
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
Diffstat (limited to 'gcc')
-rw-r--r--gcc/cp/ChangeLog13
-rw-r--r--gcc/cp/call.c38
-rw-r--r--gcc/cp/cp-tree.h1
-rw-r--r--gcc/cp/cvt.c27
-rw-r--r--gcc/cp/except.c2
-rw-r--r--gcc/cp/init.c23
-rw-r--r--gcc/cp/typeck.c2
-rw-r--r--gcc/doc/invoke.texi6
-rw-r--r--gcc/testsuite/g++.dg/cpp0x/constexpr-ctor14a.C2
-rw-r--r--gcc/testsuite/g++.dg/cpp0x/initlist9.C4
-rw-r--r--gcc/testsuite/g++.dg/cpp0x/noexcept23.C7
-rw-r--r--gcc/testsuite/g++.dg/cpp0x/noexcept24.C7
-rw-r--r--gcc/testsuite/g++.dg/cpp1z/elide1.C9
-rw-r--r--gcc/testsuite/g++.dg/init/copy3.C2
-rw-r--r--gcc/testsuite/g++.dg/overload/arg3.C6
-rw-r--r--gcc/testsuite/g++.dg/template/copy1.C6
-rw-r--r--gcc/testsuite/g++.old-deja/g++.eh/ctor1.C6
-rw-r--r--gcc/testsuite/g++.old-deja/g++.jason/temporary2.C4
-rw-r--r--gcc/testsuite/g++.old-deja/g++.mike/p2431.C4
-rw-r--r--gcc/testsuite/g++.old-deja/g++.pt/auto_ptr.C6
20 files changed, 133 insertions, 42 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 008d646..553658f 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,16 @@
+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>
Implement P0258R2 - helper for C++17
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index dac1337..6feaf7e 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<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;
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 49cbdf2..9282bbe 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 ecc8ef8..063457f 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 1c60b08..2f88082 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<tree, va_gc> *exp_vec;
diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index d1c8274..63c3dab 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 c70cfc8..f1abb40 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<tree, va_gc> *rhs_vec = make_tree_vector_single (rhs);
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index d9667e7..22af6e4 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 644ae63..8d1ebcd 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 bb6414c..ef806d0 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 5a01df4..b630b23 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 c17ddfa..60ca443 100644
--- a/gcc/testsuite/g++.dg/cpp0x/noexcept24.C
+++ b/gcc/testsuite/g++.dg/cpp0x/noexcept24.C
@@ -13,7 +13,12 @@ void a(A<T>) noexcept {}
template<typename T>
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()
diff --git a/gcc/testsuite/g++.dg/cpp1z/elide1.C b/gcc/testsuite/g++.dg/cpp1z/elide1.C
index a0538bb..71476e2 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 fa6a6ea..cfc7394 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 1684fcc..9507c02 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 bf5a37c..a34221d 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 <class T> A(T); // { dg-message "A::A" }
+ A(A&); // { dg-message "A::A" "" { target c++14_down } }
+ 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 } }
diff --git a/gcc/testsuite/g++.old-deja/g++.eh/ctor1.C b/gcc/testsuite/g++.old-deja/g++.eh/ctor1.C
index 9b4adaf..21b27d7 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 e557384..c855f8f 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 8a7ede6..d990a10 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 4a363a2..35e1cc2 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<typename X> 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<typename Y>
auto_ptr(auto_ptr<Y>& r) throw() : px(r.release()) {}
@@ -44,12 +44,12 @@ struct Derived : Base { Derived() {} };
auto_ptr<Derived> f() { auto_ptr<Derived> null(0); return null; }
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() {
auto_ptr<Base> x(f());
auto_ptr<Derived> 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
}