On 07/10/2012 02:04 AM, Jason Merrill wrote:
Apparently we need to implement DR 1402 in 4.7 in order to fix the std::pair ABI breakage properly. So here it is: if overload resolution chooses a non-trivial copy constructor, instead of causing the move constructor to be deleted, we just don't implicitly declare it.
This is an incomplete implementation of (the current proposed resolution of) DR 1402, which also changes the conditions when a virtual base interferes with an implicitly-declared move assignment operator. This patch implements that, as well as some code cleanup.
Tested x86_64-pc-linux-gnu, applying to trunk.
commit 4295ec42ce03f2a4283c0e82d5cbdce681a68efa Author: Jason Merrill <ja...@redhat.com> Date: Tue Jul 10 18:19:28 2012 +0200 DR 1402 * method.c (synthesized_method_walk): Replace uses of msg with diag. Correct handling of virtual bases with move operations. (process_subob_fn, walk_field_subobs): Replace uses of msg with diag. diff --git a/gcc/cp/method.c b/gcc/cp/method.c index 79edf81..f3fd7b8 100644 --- a/gcc/cp/method.c +++ b/gcc/cp/method.c @@ -923,7 +923,7 @@ get_copy_assign (tree type) static void process_subob_fn (tree fn, bool move_p, tree *spec_p, bool *trivial_p, bool *deleted_p, bool *constexpr_p, bool *no_implicit_p, - const char *msg, tree arg) + bool diag, tree arg) { if (!fn || fn == error_mark_node) goto bad; @@ -943,7 +943,7 @@ process_subob_fn (tree fn, bool move_p, tree *spec_p, bool *trivial_p, { if (deleted_p) *deleted_p = true; - if (msg) + if (diag) error ("union member %q+D with non-trivial %qD", arg, fn); } } @@ -956,7 +956,7 @@ process_subob_fn (tree fn, bool move_p, tree *spec_p, bool *trivial_p, if (constexpr_p && !DECL_DECLARED_CONSTEXPR_P (fn)) { *constexpr_p = false; - if (msg) + if (diag) { inform (0, "defaulted constructor calls non-constexpr " "%q+D", fn); @@ -979,7 +979,7 @@ walk_field_subobs (tree fields, tree fnname, special_function_kind sfk, int quals, bool copy_arg_p, bool move_p, bool assign_p, tree *spec_p, bool *trivial_p, bool *deleted_p, bool *constexpr_p, bool *no_implicit_p, - const char *msg, int flags, tsubst_flags_t complain) + bool diag, int flags, tsubst_flags_t complain) { tree field; for (field = fields; field; field = DECL_CHAIN (field)) @@ -996,13 +996,13 @@ walk_field_subobs (tree fields, tree fnname, special_function_kind sfk, bool bad = true; if (CP_TYPE_CONST_P (mem_type) && !CLASS_TYPE_P (mem_type)) { - if (msg) + if (diag) error ("non-static const member %q#D, can%'t use default " "assignment operator", field); } else if (TREE_CODE (mem_type) == REFERENCE_TYPE) { - if (msg) + if (diag) error ("non-static reference member %q#D, can%'t use " "default assignment operator", field); } @@ -1018,7 +1018,7 @@ walk_field_subobs (tree fields, tree fnname, special_function_kind sfk, if (DECL_INITIAL (field)) { - if (msg && DECL_INITIAL (field) == error_mark_node) + if (diag && DECL_INITIAL (field) == error_mark_node) inform (0, "initializer for %q+#D is invalid", field); if (trivial_p) *trivial_p = false; @@ -1041,14 +1041,14 @@ walk_field_subobs (tree fields, tree fnname, special_function_kind sfk, if (CP_TYPE_CONST_P (mem_type) && default_init_uninitialized_part (mem_type)) { - if (msg) + if (diag) error ("uninitialized non-static const member %q#D", field); bad = true; } else if (TREE_CODE (mem_type) == REFERENCE_TYPE) { - if (msg) + if (diag) error ("uninitialized non-static reference member %q#D", field); bad = true; @@ -1064,7 +1064,7 @@ walk_field_subobs (tree fields, tree fnname, special_function_kind sfk, && TREE_CODE (DECL_CONTEXT (field)) != UNION_TYPE) { *constexpr_p = false; - if (msg) + if (diag) inform (0, "defaulted default constructor does not " "initialize %q+#D", field); } @@ -1078,7 +1078,7 @@ walk_field_subobs (tree fields, tree fnname, special_function_kind sfk, walk_field_subobs (TYPE_FIELDS (mem_type), fnname, sfk, quals, copy_arg_p, move_p, assign_p, spec_p, trivial_p, deleted_p, constexpr_p, no_implicit_p, - msg, flags, complain); + diag, flags, complain); continue; } @@ -1095,7 +1095,7 @@ walk_field_subobs (tree fields, tree fnname, special_function_kind sfk, rval = locate_fn_flags (mem_type, fnname, argtype, flags, complain); process_subob_fn (rval, move_p, spec_p, trivial_p, deleted_p, - constexpr_p, no_implicit_p, msg, field); + constexpr_p, no_implicit_p, diag, field); } } @@ -1116,7 +1116,6 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p, VEC(tree,gc) *vbases; int i, quals, flags; tsubst_flags_t complain; - const char *msg; bool ctor_p; if (spec_p) @@ -1240,25 +1239,21 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p, quals = TYPE_UNQUALIFIED; argtype = NULL_TREE; - if (!diag) - msg = NULL; - else if (assign_p) - msg = ("base %qT does not have a move assignment operator or trivial " - "copy assignment operator"); - else - msg = ("base %qT does not have a move constructor or trivial " - "copy constructor"); - for (binfo = TYPE_BINFO (ctype), i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i) { tree basetype = BINFO_TYPE (base_binfo); + + if (!assign_p && BINFO_VIRTUAL_P (base_binfo)) + /* We'll handle virtual bases below. */ + continue; + if (copy_arg_p) argtype = build_stub_type (basetype, quals, move_p); rval = locate_fn_flags (base_binfo, fnname, argtype, flags, complain); process_subob_fn (rval, move_p, spec_p, trivial_p, deleted_p, - constexpr_p, no_implicit_p, msg, basetype); + constexpr_p, no_implicit_p, diag, basetype); if (ctor_p && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (basetype)) { /* In a constructor we also need to check the subobject @@ -1271,7 +1266,7 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p, throw) or exception-specification (a throw from one of the dtors would be a double-fault). */ process_subob_fn (rval, false, NULL, NULL, - deleted_p, NULL, NULL, NULL, + deleted_p, NULL, NULL, false, basetype); } @@ -1288,21 +1283,31 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p, } vbases = CLASSTYPE_VBASECLASSES (ctype); - if (vbases && assign_p && move_p) + if (vbases == NULL) + /* No virtual bases to worry about. */; + else if (assign_p && move_p && no_implicit_p) { + /* Don't implicitly declare a defaulted move assignment if a virtual + base has non-trivial move assignment, since moving the same base + more than once is dangerous. */ /* Should the spec be changed to allow vbases that only occur once? */ - if (diag) - error ("%qT has virtual bases, default move assignment operator " - "cannot be generated", ctype); - else if (deleted_p) - *deleted_p = true; + FOR_EACH_VEC_ELT (tree, vbases, i, base_binfo) + { + tree basetype = BINFO_TYPE (base_binfo); + if (copy_arg_p) + argtype = build_stub_type (basetype, quals, move_p); + rval = locate_fn_flags (base_binfo, fnname, argtype, flags, complain); + if (rval && rval != error_mark_node + && move_fn_p (rval) && !trivial_fn_p (rval)) + { + *no_implicit_p = true; + break; + } + } } else if (!assign_p) { - if (diag) - msg = ("virtual base %qT does not have a move constructor " - "or trivial copy constructor"); - if (vbases && constexpr_p) + if (constexpr_p) *constexpr_p = false; FOR_EACH_VEC_ELT (tree, vbases, i, base_binfo) { @@ -1312,35 +1317,29 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p, rval = locate_fn_flags (base_binfo, fnname, argtype, flags, complain); process_subob_fn (rval, move_p, spec_p, trivial_p, deleted_p, - constexpr_p, no_implicit_p, msg, basetype); + constexpr_p, no_implicit_p, diag, basetype); if (ctor_p && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (basetype)) { rval = locate_fn_flags (base_binfo, complete_dtor_identifier, NULL_TREE, flags, complain); process_subob_fn (rval, false, NULL, NULL, - deleted_p, NULL, NULL, NULL, + deleted_p, NULL, NULL, false, basetype); } } } - if (!diag) - /* Leave msg null. */; - else if (assign_p) - msg = ("non-static data member %qD does not have a move " - "assignment operator or trivial copy assignment operator"); - else - msg = ("non-static data member %qD does not have a move " - "constructor or trivial copy constructor"); + + /* Now handle the non-static data members. */ walk_field_subobs (TYPE_FIELDS (ctype), fnname, sfk, quals, copy_arg_p, move_p, assign_p, spec_p, trivial_p, deleted_p, constexpr_p, no_implicit_p, - msg, flags, complain); + diag, flags, complain); if (ctor_p) walk_field_subobs (TYPE_FIELDS (ctype), complete_dtor_identifier, sfk_destructor, TYPE_UNQUALIFIED, false, false, false, NULL, NULL, deleted_p, NULL, - NULL, NULL, flags, complain); + NULL, false, flags, complain); pop_scope (scope); diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted37.C b/gcc/testsuite/g++.dg/cpp0x/defaulted37.C new file mode 100644 index 0000000..69105cc --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/defaulted37.C @@ -0,0 +1,21 @@ +// DR 1402 +// { dg-do compile { target c++11 } } + +struct A +{ + int moved = 0; + A& operator=(A&&) { ++moved; } + ~A() { if (moved > 1) __builtin_abort(); } +}; + +struct B: virtual A { B& operator=(B&&) = default; }; +struct C: virtual A { }; // { dg-error "operator=.const A&" } + +int main() +{ + B b1, b2; + b2 = static_cast<B&&>(b1); + + C c1, c2; + c2 = static_cast<C&&>(c1); // { dg-error "operator=.const C&" } +}