+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
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,
*diagnostic_kind = DK_WARNING;
else if (errorcount > savee)
*diagnostic_kind = DK_ERROR;
- else
- *diagnostic_kind = DK_UNSPECIFIED;
return expr;
}
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)
{
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
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))
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);
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. */
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)))
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;
}
/* 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;
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
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
}
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);
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
};
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);
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) { }
};
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 };
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, "");
}
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()
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;
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 } }
}
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 } }
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
{
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 (...) { }
}
{
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;
};
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.
}
class A
{
public:
- A(A &); // { dg-message "note" }
+ A(A &); // { dg-message "note" "" { target c++14_down } }
};
class B
C()
{
B b;
- A a = b;// { dg-error "rvalue" }
+ A a = b;// { dg-error "rvalue" "" { target c++14_down } }
}
};
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()) {}
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
}