mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-02-25 03:45:23 +08:00
Core 1402
Core 1402 cp/ * call.c (joust): An implicitly deleted move function is worse than any non-deleted function. * method.c (process_subob_fn): No special rules for move. (synthesized_method_walk, implicitly_declare_fn): Likewise. Warn about virtual base with non-trivial move assignment. * cp-tree.h (struct lang_decl_fn): Remove suppress_implicit_decl. (FNDECL_SUPPRESS_IMPLICIT_DECL): Remove. c-family/ * c.opt (Wvirtual-move-assign): New. From-SVN: r192813
This commit is contained in:
parent
57c3feb40a
commit
f14edc1af5
@ -1,5 +1,7 @@
|
||||
2012-10-25 Jason Merrill <jason@redhat.com>
|
||||
|
||||
* c.opt (Wvirtual-move-assign): New.
|
||||
|
||||
* c.opt (Winherited-variadic-ctor): New.
|
||||
|
||||
2012-10-25 Marc Glisse <marc.glisse@inria.fr>
|
||||
|
@ -741,6 +741,10 @@ Wvolatile-register-var
|
||||
C ObjC C++ ObjC++ Var(warn_volatile_register_var) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall)
|
||||
Warn when a register variable is declared volatile
|
||||
|
||||
Wvirtual-move-assign
|
||||
C++ ObjC++ Var(warn_virtual_move_assign) Warning Init(1)
|
||||
Warn if a virtual base has a non-trivial move assignment operator
|
||||
|
||||
Wwrite-strings
|
||||
C ObjC C++ ObjC++ Var(warn_write_strings) Warning
|
||||
In C++, nonzero means warn about deprecated conversion from string literals to 'char *'. In C, similar warning, except that the conversion is of course not deprecated by the ISO C standard.
|
||||
|
@ -1,5 +1,14 @@
|
||||
2012-10-25 Jason Merrill <jason@redhat.com>
|
||||
|
||||
Core 1402
|
||||
* call.c (joust): An implicitly deleted move function is
|
||||
worse than any non-deleted function.
|
||||
* method.c (process_subob_fn): No special rules for move.
|
||||
(synthesized_method_walk, implicitly_declare_fn): Likewise.
|
||||
Warn about virtual base with non-trivial move assignment.
|
||||
* cp-tree.h (struct lang_decl_fn): Remove suppress_implicit_decl.
|
||||
(FNDECL_SUPPRESS_IMPLICIT_DECL): Remove.
|
||||
|
||||
* semantics.c (finish_omp_threadprivate): Call complete_type.
|
||||
|
||||
* class.c (one_inherited_ctor): Warn about variadic inherited ctor.
|
||||
|
@ -8246,6 +8246,22 @@ joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn,
|
||||
&& (IS_TYPE_OR_DECL_P (cand1->fn)))
|
||||
return 1;
|
||||
|
||||
/* Prefer a non-deleted function over an implicitly deleted move
|
||||
constructor or assignment operator. This differs slightly from the
|
||||
wording for issue 1402 (which says the move op is ignored by overload
|
||||
resolution), but this way produces better error messages. */
|
||||
if (TREE_CODE (cand1->fn) == FUNCTION_DECL
|
||||
&& TREE_CODE (cand2->fn) == FUNCTION_DECL
|
||||
&& DECL_DELETED_FN (cand1->fn) != DECL_DELETED_FN (cand2->fn))
|
||||
{
|
||||
if (DECL_DELETED_FN (cand1->fn) && DECL_DEFAULTED_FN (cand1->fn)
|
||||
&& move_fn_p (cand1->fn))
|
||||
return -1;
|
||||
if (DECL_DELETED_FN (cand2->fn) && DECL_DEFAULTED_FN (cand2->fn)
|
||||
&& move_fn_p (cand2->fn))
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* a viable function F1
|
||||
is defined to be a better function than another viable function F2 if
|
||||
for all arguments i, ICSi(F1) is not a worse conversion sequence than
|
||||
|
@ -1954,7 +1954,7 @@ struct GTY(()) lang_decl_fn {
|
||||
unsigned thunk_p : 1;
|
||||
unsigned this_thunk_p : 1;
|
||||
unsigned hidden_friend_p : 1;
|
||||
unsigned suppress_implicit_decl : 1;
|
||||
/* 1 spare bit. */
|
||||
|
||||
/* For a non-thunk function decl, this is a tree list of
|
||||
friendly classes. For a thunk function decl, it is the
|
||||
@ -3144,12 +3144,6 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
|
||||
#define DECL_HIDDEN_FRIEND_P(NODE) \
|
||||
(LANG_DECL_FN_CHECK (DECL_COMMON_CHECK (NODE))->hidden_friend_p)
|
||||
|
||||
/* Nonzero if NODE is a FUNCTION_DECL generated by implicitly_declare_fn
|
||||
that we shouldn't actually declare implicitly; it is only used for
|
||||
comparing to an =default declaration. */
|
||||
#define FNDECL_SUPPRESS_IMPLICIT_DECL(NODE) \
|
||||
(LANG_DECL_FN_CHECK (DECL_COMMON_CHECK (NODE))->suppress_implicit_decl)
|
||||
|
||||
/* Nonzero if DECL has been declared threadprivate by
|
||||
#pragma omp threadprivate. */
|
||||
#define CP_DECL_THREADPRIVATE_P(DECL) \
|
||||
|
108
gcc/cp/method.c
108
gcc/cp/method.c
@ -969,8 +969,8 @@ get_copy_assign (tree type)
|
||||
DELETED_P or give an error message MSG with argument ARG. */
|
||||
|
||||
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,
|
||||
process_subob_fn (tree fn, tree *spec_p, bool *trivial_p,
|
||||
bool *deleted_p, bool *constexpr_p,
|
||||
bool diag, tree arg)
|
||||
{
|
||||
if (!fn || fn == error_mark_node)
|
||||
@ -996,12 +996,6 @@ process_subob_fn (tree fn, bool move_p, tree *spec_p, bool *trivial_p,
|
||||
}
|
||||
}
|
||||
|
||||
/* Core 1402: A non-trivial non-move ctor suppresses the implicit
|
||||
declaration of the move ctor/op=. */
|
||||
if (no_implicit_p && move_p && !move_signature_fn_p (fn)
|
||||
&& !trivial_fn_p (fn))
|
||||
*no_implicit_p = true;
|
||||
|
||||
if (constexpr_p && !DECL_DECLARED_CONSTEXPR_P (fn))
|
||||
{
|
||||
*constexpr_p = false;
|
||||
@ -1027,7 +1021,7 @@ static void
|
||||
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,
|
||||
bool *deleted_p, bool *constexpr_p,
|
||||
bool diag, int flags, tsubst_flags_t complain)
|
||||
{
|
||||
tree field;
|
||||
@ -1126,7 +1120,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,
|
||||
deleted_p, constexpr_p,
|
||||
diag, flags, complain);
|
||||
continue;
|
||||
}
|
||||
@ -1143,8 +1137,8 @@ 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, diag, field);
|
||||
process_subob_fn (rval, spec_p, trivial_p, deleted_p,
|
||||
constexpr_p, diag, field);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1158,7 +1152,7 @@ walk_field_subobs (tree fields, tree fnname, special_function_kind sfk,
|
||||
static void
|
||||
synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p,
|
||||
tree *spec_p, bool *trivial_p, bool *deleted_p,
|
||||
bool *constexpr_p, bool *no_implicit_p, bool diag,
|
||||
bool *constexpr_p, bool diag,
|
||||
tree inherited_base, tree inherited_parms)
|
||||
{
|
||||
tree binfo, base_binfo, scope, fnname, rval, argtype;
|
||||
@ -1171,9 +1165,6 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p,
|
||||
if (spec_p)
|
||||
*spec_p = (cxx_dialect >= cxx0x ? noexcept_true_spec : empty_except_spec);
|
||||
|
||||
if (no_implicit_p)
|
||||
*no_implicit_p = false;
|
||||
|
||||
if (deleted_p)
|
||||
{
|
||||
/* "The closure type associated with a lambda-expression has a deleted
|
||||
@ -1314,8 +1305,8 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p,
|
||||
if (inherited_base)
|
||||
argtype = NULL_TREE;
|
||||
|
||||
process_subob_fn (rval, move_p, spec_p, trivial_p, deleted_p,
|
||||
constexpr_p, no_implicit_p, diag, basetype);
|
||||
process_subob_fn (rval, spec_p, trivial_p, deleted_p,
|
||||
constexpr_p, diag, basetype);
|
||||
if (ctor_p && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (basetype))
|
||||
{
|
||||
/* In a constructor we also need to check the subobject
|
||||
@ -1327,8 +1318,8 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p,
|
||||
do they affect constexpr-ness (a constant expression doesn't
|
||||
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, false,
|
||||
process_subob_fn (rval, NULL, NULL,
|
||||
deleted_p, NULL, false,
|
||||
basetype);
|
||||
}
|
||||
|
||||
@ -1342,31 +1333,20 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p,
|
||||
*deleted_p = true;
|
||||
check_vdtor = false;
|
||||
}
|
||||
|
||||
if (diag && assign_p && move_p
|
||||
&& BINFO_VIRTUAL_P (base_binfo)
|
||||
&& rval && TREE_CODE (rval) == FUNCTION_DECL
|
||||
&& move_fn_p (rval) && !trivial_fn_p (rval))
|
||||
warning (OPT_Wvirtual_move_assign,
|
||||
"defaulted move assignment for %qT calls a non-trivial "
|
||||
"move assignment operator for virtual base %qT",
|
||||
ctype, basetype);
|
||||
}
|
||||
|
||||
vbases = CLASSTYPE_VBASECLASSES (ctype);
|
||||
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? */
|
||||
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 (constexpr_p)
|
||||
@ -1378,14 +1358,14 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_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, diag, basetype);
|
||||
process_subob_fn (rval, spec_p, trivial_p, deleted_p,
|
||||
constexpr_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, false,
|
||||
process_subob_fn (rval, NULL, NULL,
|
||||
deleted_p, NULL, false,
|
||||
basetype);
|
||||
}
|
||||
}
|
||||
@ -1394,14 +1374,14 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p,
|
||||
/* 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,
|
||||
deleted_p, constexpr_p,
|
||||
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, false, flags, complain);
|
||||
false, flags, complain);
|
||||
|
||||
pop_scope (scope);
|
||||
|
||||
@ -1473,7 +1453,7 @@ maybe_explain_implicit_delete (tree decl)
|
||||
"definition would be ill-formed:", decl);
|
||||
pop_scope (scope);
|
||||
synthesized_method_walk (ctype, sfk, const_p,
|
||||
NULL, NULL, NULL, NULL, NULL, true,
|
||||
NULL, NULL, NULL, NULL, true,
|
||||
DECL_INHERITED_CTOR_BASE (decl), parms);
|
||||
}
|
||||
|
||||
@ -1494,7 +1474,7 @@ explain_implicit_non_constexpr (tree decl)
|
||||
bool dummy;
|
||||
synthesized_method_walk (DECL_CLASS_CONTEXT (decl),
|
||||
special_function_p (decl), const_p,
|
||||
NULL, NULL, NULL, &dummy, NULL, true,
|
||||
NULL, NULL, NULL, &dummy, true,
|
||||
NULL_TREE, NULL_TREE);
|
||||
}
|
||||
|
||||
@ -1507,10 +1487,10 @@ deduce_inheriting_ctor (tree decl)
|
||||
{
|
||||
gcc_assert (DECL_INHERITED_CTOR_BASE (decl));
|
||||
tree spec;
|
||||
bool trivial, constexpr_, deleted, no_implicit;
|
||||
bool trivial, constexpr_, deleted;
|
||||
synthesized_method_walk (DECL_CONTEXT (decl), sfk_inheriting_constructor,
|
||||
false, &spec, &trivial, &deleted, &constexpr_,
|
||||
&no_implicit, /*diag*/false,
|
||||
/*diag*/false,
|
||||
DECL_INHERITED_CTOR_BASE (decl),
|
||||
FUNCTION_FIRST_USER_PARMTYPE (decl));
|
||||
DECL_DELETED_FN (decl) = deleted;
|
||||
@ -1540,7 +1520,6 @@ implicitly_declare_fn (special_function_kind kind, tree type,
|
||||
bool deleted_p;
|
||||
bool trivial_p;
|
||||
bool constexpr_p;
|
||||
bool no_implicit_p;
|
||||
|
||||
/* Because we create declarations for implicitly declared functions
|
||||
lazily, we may be creating the declaration for a member of TYPE
|
||||
@ -1627,11 +1606,10 @@ implicitly_declare_fn (special_function_kind kind, tree type,
|
||||
deleted_p = DECL_DELETED_FN (DECL_TEMPLATE_RESULT (inherited_ctor));
|
||||
constexpr_p
|
||||
= DECL_DECLARED_CONSTEXPR_P (DECL_TEMPLATE_RESULT (inherited_ctor));
|
||||
no_implicit_p = false;
|
||||
}
|
||||
else
|
||||
synthesized_method_walk (type, kind, const_p, &raises, &trivial_p,
|
||||
&deleted_p, &constexpr_p, &no_implicit_p, false,
|
||||
&deleted_p, &constexpr_p, false,
|
||||
inherited_base, inherited_parms);
|
||||
/* Don't bother marking a deleted constructor as constexpr. */
|
||||
if (deleted_p)
|
||||
@ -1719,7 +1697,6 @@ implicitly_declare_fn (special_function_kind kind, tree type,
|
||||
DECL_DELETED_FN (fn) = deleted_p;
|
||||
DECL_DECLARED_CONSTEXPR_P (fn) = constexpr_p;
|
||||
}
|
||||
FNDECL_SUPPRESS_IMPLICIT_DECL (fn) = no_implicit_p;
|
||||
DECL_EXTERNAL (fn) = true;
|
||||
DECL_NOT_REALLY_EXTERN (fn) = 1;
|
||||
DECL_DECLARED_INLINE_P (fn) = 1;
|
||||
@ -1731,6 +1708,18 @@ implicitly_declare_fn (special_function_kind kind, tree type,
|
||||
if (inherited_ctor && TREE_CODE (inherited_ctor) == TEMPLATE_DECL)
|
||||
fn = add_inherited_template_parms (fn, inherited_ctor);
|
||||
|
||||
/* Warn about calling a non-trivial move assignment in a virtual base. */
|
||||
if (kind == sfk_move_assignment && !deleted_p && !trivial_p
|
||||
&& CLASSTYPE_VBASECLASSES (type))
|
||||
{
|
||||
location_t loc = input_location;
|
||||
input_location = DECL_SOURCE_LOCATION (fn);
|
||||
synthesized_method_walk (type, kind, const_p,
|
||||
NULL, NULL, NULL, NULL, true,
|
||||
NULL_TREE, NULL_TREE);
|
||||
input_location = loc;
|
||||
}
|
||||
|
||||
return fn;
|
||||
}
|
||||
|
||||
@ -1909,17 +1898,6 @@ lazily_declare_fn (special_function_kind sfk, tree type)
|
||||
|| type_has_user_declared_move_assign (type)))
|
||||
DECL_DELETED_FN (fn) = true;
|
||||
|
||||
/* For move variants, rather than declare them as deleted we just
|
||||
don't declare them at all. */
|
||||
if (DECL_DELETED_FN (fn)
|
||||
&& (sfk == sfk_move_constructor
|
||||
|| sfk == sfk_move_assignment))
|
||||
return NULL_TREE;
|
||||
|
||||
/* We also suppress implicit move if it would call a non-trivial copy. */
|
||||
if (FNDECL_SUPPRESS_IMPLICIT_DECL (fn))
|
||||
return NULL_TREE;
|
||||
|
||||
/* A destructor may be virtual. */
|
||||
if (sfk == sfk_destructor
|
||||
|| sfk == sfk_move_assignment
|
||||
|
@ -4722,6 +4722,16 @@ using scalars of wider type, which normally is more performance efficient;
|
||||
and @code{as a single scalar}, which means that vector fits into a
|
||||
scalar type.
|
||||
|
||||
@item -Wno-virtual-move-assign
|
||||
@opindex Wvirtual-move-assign
|
||||
@opindex Wno-virtual-move-assign
|
||||
Suppress warnings about inheriting from a virtual base with a
|
||||
non-trivial C++11 move assignment operator. This is dangerous because
|
||||
if the virtual base is reachable along more than one path, it will be
|
||||
moved multiple times, which can mean both objects end up in the
|
||||
moved-from state. If the move assignment operator is written to avoid
|
||||
moving from a moved-from object, this warning can be disabled.
|
||||
|
||||
@item -Wvla
|
||||
@opindex Wvla
|
||||
@opindex Wno-vla
|
||||
|
@ -3,13 +3,11 @@
|
||||
|
||||
struct A
|
||||
{
|
||||
int moved = 0;
|
||||
A& operator=(A&&) { ++moved; }
|
||||
~A() { if (moved > 1) __builtin_abort(); }
|
||||
A& operator=(A&&);
|
||||
};
|
||||
|
||||
struct B: virtual A { B& operator=(B&&) = default; };
|
||||
struct C: virtual A { }; // { dg-error "operator=.const A&" }
|
||||
struct B: virtual A { B& operator=(B&&) = default; }; // { dg-warning "virtual base" }
|
||||
struct C: virtual A { }; // { dg-warning "virtual base" }
|
||||
|
||||
int main()
|
||||
{
|
||||
@ -17,5 +15,5 @@ int main()
|
||||
b2 = static_cast<B&&>(b1);
|
||||
|
||||
C c1, c2;
|
||||
c2 = static_cast<C&&>(c1); // { dg-error "operator=.const C&" }
|
||||
c2 = static_cast<C&&>(c1);
|
||||
}
|
||||
|
23
gcc/testsuite/g++.dg/cpp0x/defaulted39.C
Normal file
23
gcc/testsuite/g++.dg/cpp0x/defaulted39.C
Normal file
@ -0,0 +1,23 @@
|
||||
// DR 1402
|
||||
// { dg-options -std=c++11 }
|
||||
|
||||
template <class T> T&& move(T& t);
|
||||
|
||||
struct A
|
||||
{
|
||||
A(const A&);
|
||||
};
|
||||
|
||||
struct B
|
||||
{
|
||||
B(B&&);
|
||||
};
|
||||
|
||||
struct C
|
||||
{
|
||||
A a;
|
||||
B b;
|
||||
};
|
||||
|
||||
extern C c1;
|
||||
C c2(move(c1));
|
23
gcc/testsuite/g++.dg/cpp0x/defaulted40.C
Normal file
23
gcc/testsuite/g++.dg/cpp0x/defaulted40.C
Normal file
@ -0,0 +1,23 @@
|
||||
// DR 1402
|
||||
// { dg-options -std=c++11 }
|
||||
|
||||
template <class T> T&& move(T& t);
|
||||
|
||||
struct A
|
||||
{
|
||||
A(const A&);
|
||||
};
|
||||
|
||||
struct B
|
||||
{
|
||||
B(B&&) = delete; // { dg-prune-output "declared" }
|
||||
};
|
||||
|
||||
struct C // { dg-error "deleted" }
|
||||
{
|
||||
A a;
|
||||
B b;
|
||||
};
|
||||
|
||||
extern C c1;
|
||||
C c2(move(c1)); // { dg-error "deleted" }
|
Loading…
Reference in New Issue
Block a user