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>
Implement P0258R2 - helper for C++17
......
......@@ -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))
......
......@@ -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);
......
......@@ -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)))
......
......@@ -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;
......
......@@ -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
......
......@@ -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);
......
......@@ -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
......
......@@ -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);
......@@ -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 };
......
......@@ -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, "");
}
......@@ -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()
......
......@@ -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();
}
// { dg-do run }
// { dg-do run { target c++14_down } }
// { dg-options "-fno-elide-constructors" }
int copies;
......
......@@ -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 } }
}
......@@ -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 } }
......@@ -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 (...) { }
}
......@@ -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.
}
......@@ -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 } }
}
};
......@@ -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
}
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