mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-03-24 19:01:17 +08:00
Implement C++20 operator<=>.
There are three major pieces to this support: scalar operator<=>, synthesis of comparison operators, and rewritten/reversed overload resolution (e.g. a < b becomes 0 > b <=> a). Unlike other defaulted functions, where we use synthesized_method_walk to semi-simulate what the definition of the function will be like, this patch determines the characteristics of a comparison operator by trying to define it. My handling of non-dependent rewritten operators in templates can still use some work: build_min_non_dep_op_overload can't understand the rewrites and crashes, so I'm avoiding it for now by clearing *overload. This means we'll do name lookup again at instantiation time, which can incorrectly mean a different result. I'll poke at this more in stage 3. I'm leaving out a fourth section ("strong structural equality") even though I've implemented it, because it seems likely to change radically tomorrow. Thanks to Tim van Deurzen and Jakub for implementing lexing of the <=> operator, and Jonathan for the initial <compare> header. gcc/cp/ * cp-tree.h (struct lang_decl_fn): Add maybe_deleted bitfield. (DECL_MAYBE_DELETED): New. (enum special_function_kind): Add sfk_comparison. (LOOKUP_REWRITTEN, LOOKUP_REVERSED): New. * call.c (struct z_candidate): Add rewritten and reversed methods. (add_builtin_candidate): Handle SPACESHIP_EXPR. (add_builtin_candidates): Likewise. (add_candidates): Don't add a reversed candidate if the parms are the same. (add_operator_candidates): Split out from build_new_op_1. Handle rewritten and reversed candidates. (add_candidate): Swap conversions of reversed candidate. (build_new_op_1): Swap them back. Build a second operation for rewritten candidates. (extract_call_expr): Handle rewritten calls. (same_fn_or_template): New. (joust): Handle rewritten and reversed candidates. * class.c (add_implicitly_declared_members): Add implicit op==. (classtype_has_op, classtype_has_defaulted_op): New. * constexpr.c (cxx_eval_binary_expression): Handle SPACESHIP_EXPR. (cxx_eval_constant_expression, potential_constant_expression_1): Likewise. * cp-gimplify.c (genericize_spaceship): New. (cp_genericize_r): Use it. * cp-objcp-common.c (cp_common_init_ts): Handle SPACESHIP_EXPR. * decl.c (finish_function): Handle deleted function. * decl2.c (grokfield): SET_DECL_FRIEND_CONTEXT on defaulted friend. (mark_used): Check DECL_MAYBE_DELETED. Remove assumption that defaulted functions are non-static members. * error.c (dump_expr): Handle SPACESHIP_EXPR. * method.c (type_has_trivial_fn): False for sfk_comparison. (enum comp_cat_tag, struct comp_cat_info_t): New types. (comp_cat_cache): New array variable. (lookup_comparison_result, lookup_comparison_category) (is_cat, cat_tag_for, spaceship_comp_cat) (spaceship_type, genericize_spaceship) (common_comparison_type, early_check_defaulted_comparison) (comp_info, build_comparison_op): New. (synthesize_method): Handle sfk_comparison. Handle deleted. (get_defaulted_eh_spec, maybe_explain_implicit_delete) (explain_implicit_non_constexpr, implicitly_declare_fn) (defaulted_late_check, defaultable_fn_check): Handle sfk_comparison. * name-lookup.c (get_std_name_hint): Add comparison categories. * tree.c (special_function_p): Add sfk_comparison. * typeck.c (cp_build_binary_op): Handle SPACESHIP_EXPR. 2019-11-05 Tim van Deurzen <tim@kompiler.org> Add new tree code for the spaceship operator. gcc/cp/ * cp-tree.def: Add new tree code. * operators.def: New binary operator. * parser.c: Add new token and tree code. libcpp/ * cpplib.h: Add spaceship operator for C++. * lex.c: Implement conditional lexing of spaceship operator for C++20. 2019-11-05 Jonathan Wakely <jwakely@redhat.com> libstdc++-v3/ * libsupc++/compare: New header. * libsupc++/Makefile.am (std_HEADERS): Add compare. * include/std/version: Define __cpp_lib_three_way_comparison. * include/std/functional: #include <compare>. From-SVN: r277865
This commit is contained in:
parent
b63566a404
commit
b7689b962d
@ -990,6 +990,7 @@ c_cpp_builtins (cpp_reader *pfile)
|
||||
cpp_define (pfile, "__cpp_nontype_template_parameter_class=201806L");
|
||||
cpp_define (pfile, "__cpp_impl_destroying_delete=201806L");
|
||||
cpp_define (pfile, "__cpp_constexpr_dynamic_alloc=201907L");
|
||||
cpp_define (pfile, "__cpp_impl_three_way_comparison=201907L");
|
||||
}
|
||||
if (flag_concepts)
|
||||
{
|
||||
|
@ -1,3 +1,59 @@
|
||||
2019-11-05 Jason Merrill <jason@redhat.com>
|
||||
|
||||
Implement C++20 operator<=>.
|
||||
* cp-tree.h (struct lang_decl_fn): Add maybe_deleted bitfield.
|
||||
(DECL_MAYBE_DELETED): New.
|
||||
(enum special_function_kind): Add sfk_comparison.
|
||||
(LOOKUP_REWRITTEN, LOOKUP_REVERSED): New.
|
||||
* call.c (struct z_candidate): Add rewritten and reversed methods.
|
||||
(add_builtin_candidate): Handle SPACESHIP_EXPR.
|
||||
(add_builtin_candidates): Likewise.
|
||||
(add_candidates): Don't add a reversed candidate if the parms are
|
||||
the same.
|
||||
(add_operator_candidates): Split out from build_new_op_1. Handle
|
||||
rewritten and reversed candidates.
|
||||
(add_candidate): Swap conversions of reversed candidate.
|
||||
(build_new_op_1): Swap them back. Build a second operation for
|
||||
rewritten candidates.
|
||||
(extract_call_expr): Handle rewritten calls.
|
||||
(same_fn_or_template): New.
|
||||
(joust): Handle rewritten and reversed candidates.
|
||||
* class.c (add_implicitly_declared_members): Add implicit op==.
|
||||
(classtype_has_op, classtype_has_defaulted_op): New.
|
||||
* constexpr.c (cxx_eval_binary_expression): Handle SPACESHIP_EXPR.
|
||||
(cxx_eval_constant_expression, potential_constant_expression_1):
|
||||
Likewise.
|
||||
* cp-gimplify.c (genericize_spaceship): New.
|
||||
(cp_genericize_r): Use it.
|
||||
* cp-objcp-common.c (cp_common_init_ts): Handle SPACESHIP_EXPR.
|
||||
* decl.c (finish_function): Handle deleted function.
|
||||
* decl2.c (grokfield): SET_DECL_FRIEND_CONTEXT on defaulted friend.
|
||||
(mark_used): Check DECL_MAYBE_DELETED. Remove assumption that
|
||||
defaulted functions are non-static members.
|
||||
* error.c (dump_expr): Handle SPACESHIP_EXPR.
|
||||
* method.c (type_has_trivial_fn): False for sfk_comparison.
|
||||
(enum comp_cat_tag, struct comp_cat_info_t): New types.
|
||||
(comp_cat_cache): New array variable.
|
||||
(lookup_comparison_result, lookup_comparison_category)
|
||||
(is_cat, cat_tag_for, spaceship_comp_cat)
|
||||
(spaceship_type, genericize_spaceship)
|
||||
(common_comparison_type, early_check_defaulted_comparison)
|
||||
(comp_info, build_comparison_op): New.
|
||||
(synthesize_method): Handle sfk_comparison. Handle deleted.
|
||||
(get_defaulted_eh_spec, maybe_explain_implicit_delete)
|
||||
(explain_implicit_non_constexpr, implicitly_declare_fn)
|
||||
(defaulted_late_check, defaultable_fn_check): Handle sfk_comparison.
|
||||
* name-lookup.c (get_std_name_hint): Add comparison categories.
|
||||
* tree.c (special_function_p): Add sfk_comparison.
|
||||
* typeck.c (cp_build_binary_op): Handle SPACESHIP_EXPR.
|
||||
|
||||
2019-11-05 Tim van Deurzen <tim@kompiler.org>
|
||||
|
||||
Add new tree code for the spaceship operator.
|
||||
* cp-tree.def: Add new tree code.
|
||||
* operators.def: New binary operator.
|
||||
* parser.c: Add new token and tree code.
|
||||
|
||||
2019-09-15 Jason Merrill <jason@redhat.com>
|
||||
|
||||
* call.c (build_new_op_1): Don't apply any standard conversions to
|
||||
|
449
gcc/cp/call.c
449
gcc/cp/call.c
@ -514,6 +514,9 @@ struct z_candidate {
|
||||
|
||||
/* The flags active in add_candidate. */
|
||||
int flags;
|
||||
|
||||
bool rewritten () { return (flags & LOOKUP_REWRITTEN); }
|
||||
bool reversed () { return (flags & LOOKUP_REVERSED); }
|
||||
};
|
||||
|
||||
/* Returns true iff T is a null pointer constant in the sense of
|
||||
@ -2106,6 +2109,11 @@ add_candidate (struct z_candidate **candidates,
|
||||
cand->flags = flags;
|
||||
*candidates = cand;
|
||||
|
||||
if (convs && cand->reversed ())
|
||||
/* Swap the conversions for comparison in joust; we'll swap them back
|
||||
before build_over_call. */
|
||||
std::swap (convs[0], convs[1]);
|
||||
|
||||
return cand;
|
||||
}
|
||||
|
||||
@ -2737,6 +2745,16 @@ add_builtin_candidate (struct z_candidate **candidates, enum tree_code code,
|
||||
where LR is the result of the usual arithmetic conversions between
|
||||
types L and R.
|
||||
|
||||
For every integral type T there exists a candidate operator function of
|
||||
the form
|
||||
|
||||
std::strong_ordering operator<=>(T, T);
|
||||
|
||||
For every pair of floating-point types L and R, there exists a candidate
|
||||
operator function of the form
|
||||
|
||||
std::partial_ordering operator<=>(L, R);
|
||||
|
||||
14For every pair of types T and I, where T is a cv-qualified or cv-
|
||||
unqualified complete object type and I is a promoted integral type,
|
||||
there exist candidate operator functions of the form
|
||||
@ -2758,11 +2776,15 @@ add_builtin_candidate (struct z_candidate **candidates, enum tree_code code,
|
||||
bool operator>=(T, T);
|
||||
bool operator==(T, T);
|
||||
bool operator!=(T, T);
|
||||
R operator<=>(T, T);
|
||||
|
||||
where R is the result type specified in [expr.spaceship].
|
||||
|
||||
17For every pointer to member type T, there exist candidate operator
|
||||
functions of the form
|
||||
bool operator==(T, T);
|
||||
bool operator!=(T, T); */
|
||||
bool operator!=(T, T);
|
||||
std::strong_equality operator<=>(T, T); */
|
||||
|
||||
case MINUS_EXPR:
|
||||
if (TYPE_PTROB_P (type1) && TYPE_PTROB_P (type2))
|
||||
@ -2780,6 +2802,11 @@ add_builtin_candidate (struct z_candidate **candidates, enum tree_code code,
|
||||
break;
|
||||
return;
|
||||
|
||||
/* This isn't exactly what's specified above for operator<=>, but it's
|
||||
close enough. In particular, we don't care about the return type
|
||||
specified above; it doesn't participate in overload resolution and it
|
||||
doesn't affect the semantics of the built-in operator. */
|
||||
case SPACESHIP_EXPR:
|
||||
case EQ_EXPR:
|
||||
case NE_EXPR:
|
||||
if ((TYPE_PTRMEMFUNC_P (type1) && TYPE_PTRMEMFUNC_P (type2))
|
||||
@ -3138,6 +3165,7 @@ add_builtin_candidates (struct z_candidate **candidates, enum tree_code code,
|
||||
case LE_EXPR:
|
||||
case GT_EXPR:
|
||||
case GE_EXPR:
|
||||
case SPACESHIP_EXPR:
|
||||
enum_p = 1;
|
||||
/* Fall through. */
|
||||
|
||||
@ -5740,6 +5768,15 @@ add_candidates (tree fns, tree first_arg, const vec<tree, va_gc> *args,
|
||||
fn_args = non_static_args;
|
||||
}
|
||||
|
||||
/* Don't bother reversing an operator with two identical parameters. */
|
||||
else if (args->length () == 2 && (flags & LOOKUP_REVERSED))
|
||||
{
|
||||
tree parmlist = TYPE_ARG_TYPES (TREE_TYPE (fn));
|
||||
if (same_type_p (TREE_VALUE (parmlist),
|
||||
TREE_VALUE (TREE_CHAIN (parmlist))))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (TREE_CODE (fn) == TEMPLATE_DECL)
|
||||
add_template_candidate (candidates,
|
||||
fn,
|
||||
@ -5800,6 +5837,178 @@ op_is_ordered (tree_code code)
|
||||
}
|
||||
}
|
||||
|
||||
/* Subroutine of build_new_op_1: Add to CANDIDATES all candidates for the
|
||||
operator indicated by CODE/CODE2. This function calls itself recursively to
|
||||
handle C++20 rewritten comparison operator candidates. */
|
||||
|
||||
static tree
|
||||
add_operator_candidates (z_candidate **candidates,
|
||||
tree_code code, tree_code code2,
|
||||
vec<tree, va_gc> *arglist,
|
||||
int flags, tsubst_flags_t complain)
|
||||
{
|
||||
z_candidate *start_candidates = *candidates;
|
||||
bool ismodop = code2 != ERROR_MARK;
|
||||
tree fnname = ovl_op_identifier (ismodop, ismodop ? code2 : code);
|
||||
|
||||
/* LOOKUP_REWRITTEN is set when we're looking for the == or <=> operator to
|
||||
rewrite from, and also when we're looking for the e.g. < operator to use
|
||||
on the result of <=>. In the latter case, we don't want the flag set in
|
||||
the candidate, we just want to suppress looking for rewrites. */
|
||||
bool rewritten = (flags & LOOKUP_REWRITTEN);
|
||||
if (rewritten && code != EQ_EXPR && code != SPACESHIP_EXPR)
|
||||
flags &= ~LOOKUP_REWRITTEN;
|
||||
|
||||
bool memonly = false;
|
||||
switch (code)
|
||||
{
|
||||
/* =, ->, [], () must be non-static member functions. */
|
||||
case MODIFY_EXPR:
|
||||
if (code2 != NOP_EXPR)
|
||||
break;
|
||||
/* FALLTHRU */
|
||||
case COMPONENT_REF:
|
||||
case ARRAY_REF:
|
||||
memonly = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Add namespace-scope operators to the list of functions to
|
||||
consider. */
|
||||
if (!memonly)
|
||||
{
|
||||
tree fns = lookup_name_real (fnname, 0, 1, /*block_p=*/true, 0, 0);
|
||||
fns = lookup_arg_dependent (fnname, fns, arglist);
|
||||
add_candidates (fns, NULL_TREE, arglist, NULL_TREE,
|
||||
NULL_TREE, false, NULL_TREE, NULL_TREE,
|
||||
flags, candidates, complain);
|
||||
}
|
||||
|
||||
/* Add class-member operators to the candidate set. */
|
||||
tree arg1_type = TREE_TYPE ((*arglist)[0]);
|
||||
unsigned nargs = arglist->length () > 1 ? 2 : 1;
|
||||
tree arg2_type = nargs > 1 ? TREE_TYPE ((*arglist)[1]) : NULL_TREE;
|
||||
if (CLASS_TYPE_P (arg1_type))
|
||||
{
|
||||
tree fns = lookup_fnfields (arg1_type, fnname, 1);
|
||||
if (fns == error_mark_node)
|
||||
return error_mark_node;
|
||||
if (fns)
|
||||
add_candidates (BASELINK_FUNCTIONS (fns),
|
||||
NULL_TREE, arglist, NULL_TREE,
|
||||
NULL_TREE, false,
|
||||
BASELINK_BINFO (fns),
|
||||
BASELINK_ACCESS_BINFO (fns),
|
||||
flags, candidates, complain);
|
||||
}
|
||||
/* Per [over.match.oper]3.2, if no operand has a class type, then
|
||||
only non-member functions that have type T1 or reference to
|
||||
cv-qualified-opt T1 for the first argument, if the first argument
|
||||
has an enumeration type, or T2 or reference to cv-qualified-opt
|
||||
T2 for the second argument, if the second argument has an
|
||||
enumeration type. Filter out those that don't match. */
|
||||
else if (! arg2_type || ! CLASS_TYPE_P (arg2_type))
|
||||
{
|
||||
struct z_candidate **candp, **next;
|
||||
|
||||
for (candp = candidates; *candp != start_candidates; candp = next)
|
||||
{
|
||||
unsigned i;
|
||||
z_candidate *cand = *candp;
|
||||
next = &cand->next;
|
||||
|
||||
tree parmlist = TYPE_ARG_TYPES (TREE_TYPE (cand->fn));
|
||||
|
||||
for (i = 0; i < nargs; ++i)
|
||||
{
|
||||
tree parmtype = TREE_VALUE (parmlist);
|
||||
tree argtype = unlowered_expr_type ((*arglist)[i]);
|
||||
|
||||
if (TYPE_REF_P (parmtype))
|
||||
parmtype = TREE_TYPE (parmtype);
|
||||
if (TREE_CODE (argtype) == ENUMERAL_TYPE
|
||||
&& (same_type_ignoring_top_level_qualifiers_p
|
||||
(argtype, parmtype)))
|
||||
break;
|
||||
|
||||
parmlist = TREE_CHAIN (parmlist);
|
||||
}
|
||||
|
||||
/* No argument has an appropriate type, so remove this
|
||||
candidate function from the list. */
|
||||
if (i == nargs)
|
||||
{
|
||||
*candp = cand->next;
|
||||
next = candp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!rewritten)
|
||||
{
|
||||
/* The standard says to rewrite built-in candidates, too,
|
||||
but there's no point. */
|
||||
add_builtin_candidates (candidates, code, code2, fnname, arglist,
|
||||
flags, complain);
|
||||
|
||||
/* Maybe add C++20 rewritten comparison candidates. */
|
||||
tree_code rewrite_code = ERROR_MARK;
|
||||
if (cxx_dialect >= cxx2a
|
||||
&& nargs == 2
|
||||
&& (OVERLOAD_TYPE_P (arg1_type) || OVERLOAD_TYPE_P (arg2_type)))
|
||||
switch (code)
|
||||
{
|
||||
case LT_EXPR:
|
||||
case LE_EXPR:
|
||||
case GT_EXPR:
|
||||
case GE_EXPR:
|
||||
case SPACESHIP_EXPR:
|
||||
rewrite_code = SPACESHIP_EXPR;
|
||||
break;
|
||||
|
||||
case NE_EXPR:
|
||||
case EQ_EXPR:
|
||||
rewrite_code = EQ_EXPR;
|
||||
break;
|
||||
|
||||
default:;
|
||||
}
|
||||
|
||||
if (rewrite_code)
|
||||
{
|
||||
flags |= LOOKUP_REWRITTEN;
|
||||
if (rewrite_code != code)
|
||||
/* Add rewritten candidates in same order. */
|
||||
add_operator_candidates (candidates, rewrite_code, ERROR_MARK,
|
||||
arglist, flags, complain);
|
||||
|
||||
z_candidate *save_cand = *candidates;
|
||||
|
||||
/* Add rewritten candidates in reverse order. */
|
||||
flags |= LOOKUP_REVERSED;
|
||||
vec<tree,va_gc> *revlist = make_tree_vector ();
|
||||
revlist->quick_push ((*arglist)[1]);
|
||||
revlist->quick_push ((*arglist)[0]);
|
||||
add_operator_candidates (candidates, rewrite_code, ERROR_MARK,
|
||||
revlist, flags, complain);
|
||||
|
||||
/* Release the vec if we didn't add a candidate that uses it. */
|
||||
for (z_candidate *c = *candidates; c != save_cand; c = c->next)
|
||||
if (c->args == revlist)
|
||||
{
|
||||
revlist = NULL;
|
||||
break;
|
||||
}
|
||||
release_tree_vector (revlist);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
static tree
|
||||
build_new_op_1 (const op_location_t &loc, enum tree_code code, int flags,
|
||||
tree arg1, tree arg2, tree arg3, tree *overload,
|
||||
@ -5809,7 +6018,7 @@ build_new_op_1 (const op_location_t &loc, enum tree_code code, int flags,
|
||||
vec<tree, va_gc> *arglist;
|
||||
tree result = NULL_TREE;
|
||||
bool result_valid_p = false;
|
||||
enum tree_code code2 = NOP_EXPR;
|
||||
enum tree_code code2 = ERROR_MARK;
|
||||
enum tree_code code_orig_arg1 = ERROR_MARK;
|
||||
enum tree_code code_orig_arg2 = ERROR_MARK;
|
||||
conversion *conv;
|
||||
@ -5828,14 +6037,12 @@ build_new_op_1 (const op_location_t &loc, enum tree_code code, int flags,
|
||||
code2 = TREE_CODE (arg3);
|
||||
arg3 = NULL_TREE;
|
||||
}
|
||||
tree fnname = ovl_op_identifier (ismodop, ismodop ? code2 : code);
|
||||
|
||||
tree arg1_type = unlowered_expr_type (arg1);
|
||||
tree arg2_type = arg2 ? unlowered_expr_type (arg2) : NULL_TREE;
|
||||
|
||||
arg1 = prep_operand (arg1);
|
||||
|
||||
bool memonly = false;
|
||||
switch (code)
|
||||
{
|
||||
case NEW_EXPR:
|
||||
@ -5868,16 +6075,6 @@ build_new_op_1 (const op_location_t &loc, enum tree_code code, int flags,
|
||||
code_orig_arg2 = TREE_CODE (arg2_type);
|
||||
break;
|
||||
|
||||
/* =, ->, [], () must be non-static member functions. */
|
||||
case MODIFY_EXPR:
|
||||
if (code2 != NOP_EXPR)
|
||||
break;
|
||||
/* FALLTHRU */
|
||||
case COMPONENT_REF:
|
||||
case ARRAY_REF:
|
||||
memonly = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -5908,82 +6105,10 @@ build_new_op_1 (const op_location_t &loc, enum tree_code code, int flags,
|
||||
/* Get the high-water mark for the CONVERSION_OBSTACK. */
|
||||
p = conversion_obstack_alloc (0);
|
||||
|
||||
/* Add namespace-scope operators to the list of functions to
|
||||
consider. */
|
||||
if (!memonly)
|
||||
{
|
||||
tree fns = lookup_name_real (fnname, 0, 1, /*block_p=*/true, 0, 0);
|
||||
fns = lookup_arg_dependent (fnname, fns, arglist);
|
||||
add_candidates (fns, NULL_TREE, arglist, NULL_TREE,
|
||||
NULL_TREE, false, NULL_TREE, NULL_TREE,
|
||||
flags, &candidates, complain);
|
||||
}
|
||||
|
||||
/* Add class-member operators to the candidate set. */
|
||||
if (CLASS_TYPE_P (arg1_type))
|
||||
{
|
||||
tree fns;
|
||||
|
||||
fns = lookup_fnfields (arg1_type, fnname, 1);
|
||||
if (fns == error_mark_node)
|
||||
{
|
||||
result = error_mark_node;
|
||||
goto user_defined_result_ready;
|
||||
}
|
||||
if (fns)
|
||||
add_candidates (BASELINK_FUNCTIONS (fns),
|
||||
NULL_TREE, arglist, NULL_TREE,
|
||||
NULL_TREE, false,
|
||||
BASELINK_BINFO (fns),
|
||||
BASELINK_ACCESS_BINFO (fns),
|
||||
flags, &candidates, complain);
|
||||
}
|
||||
/* Per [over.match.oper]3.2, if no operand has a class type, then
|
||||
only non-member functions that have type T1 or reference to
|
||||
cv-qualified-opt T1 for the first argument, if the first argument
|
||||
has an enumeration type, or T2 or reference to cv-qualified-opt
|
||||
T2 for the second argument, if the second argument has an
|
||||
enumeration type. Filter out those that don't match. */
|
||||
else if (! arg2 || ! CLASS_TYPE_P (arg2_type))
|
||||
{
|
||||
struct z_candidate **candp, **next;
|
||||
|
||||
for (candp = &candidates; *candp; candp = next)
|
||||
{
|
||||
tree parmlist, parmtype;
|
||||
int i, nargs = (arg2 ? 2 : 1);
|
||||
|
||||
cand = *candp;
|
||||
next = &cand->next;
|
||||
|
||||
parmlist = TYPE_ARG_TYPES (TREE_TYPE (cand->fn));
|
||||
|
||||
for (i = 0; i < nargs; ++i)
|
||||
{
|
||||
parmtype = TREE_VALUE (parmlist);
|
||||
|
||||
if (TYPE_REF_P (parmtype))
|
||||
parmtype = TREE_TYPE (parmtype);
|
||||
if (TREE_CODE (unlowered_expr_type ((*arglist)[i])) == ENUMERAL_TYPE
|
||||
&& (same_type_ignoring_top_level_qualifiers_p
|
||||
(unlowered_expr_type ((*arglist)[i]), parmtype)))
|
||||
break;
|
||||
|
||||
parmlist = TREE_CHAIN (parmlist);
|
||||
}
|
||||
|
||||
/* No argument has an appropriate type, so remove this
|
||||
candidate function from the list. */
|
||||
if (i == nargs)
|
||||
{
|
||||
*candp = cand->next;
|
||||
next = candp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add_builtin_candidates (&candidates, code, code2, fnname, arglist,
|
||||
flags, complain);
|
||||
result = add_operator_candidates (&candidates, code, code2, arglist,
|
||||
flags, complain);
|
||||
if (result == error_mark_node)
|
||||
goto user_defined_result_ready;
|
||||
|
||||
switch (code)
|
||||
{
|
||||
@ -6021,6 +6146,7 @@ build_new_op_1 (const op_location_t &loc, enum tree_code code, int flags,
|
||||
-fpermissive. */
|
||||
else
|
||||
{
|
||||
tree fnname = ovl_op_identifier (ismodop, ismodop ? code2 : code);
|
||||
const char *msg = (flag_permissive)
|
||||
? G_("no %<%D(int)%> declared for postfix %qs,"
|
||||
" trying prefix operator instead")
|
||||
@ -6091,7 +6217,12 @@ build_new_op_1 (const op_location_t &loc, enum tree_code code, int flags,
|
||||
if (resolve_args (arglist, complain) == NULL)
|
||||
result = error_mark_node;
|
||||
else
|
||||
result = build_over_call (cand, LOOKUP_NORMAL, complain);
|
||||
{
|
||||
if (cand->reversed ())
|
||||
/* We swapped these in add_candidate, swap them back now. */
|
||||
std::swap (cand->convs[0], cand->convs[1]);
|
||||
result = build_over_call (cand, LOOKUP_NORMAL, complain);
|
||||
}
|
||||
|
||||
if (trivial_fn_p (cand->fn))
|
||||
/* There won't be a CALL_EXPR. */;
|
||||
@ -6121,6 +6252,73 @@ build_new_op_1 (const op_location_t &loc, enum tree_code code, int flags,
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If this was a C++20 rewritten comparison, adjust the result. */
|
||||
if (cand->rewritten ())
|
||||
{
|
||||
/* FIXME build_min_non_dep_op_overload can't handle rewrites. */
|
||||
if (overload)
|
||||
*overload = NULL_TREE;
|
||||
switch (code)
|
||||
{
|
||||
case EQ_EXPR:
|
||||
gcc_checking_assert (cand->reversed ());
|
||||
gcc_fallthrough ();
|
||||
case NE_EXPR:
|
||||
/* If a rewritten operator== candidate is selected by
|
||||
overload resolution for an operator @, its return type
|
||||
shall be cv bool.... */
|
||||
if (TREE_CODE (TREE_TYPE (result)) != BOOLEAN_TYPE)
|
||||
{
|
||||
if (complain & tf_error)
|
||||
{
|
||||
auto_diagnostic_group d;
|
||||
error_at (loc, "return type of %qD is not %qs",
|
||||
cand->fn, "bool");
|
||||
inform (loc, "used as rewritten candidate for "
|
||||
"comparison of %qT and %qT",
|
||||
arg1_type, arg2_type);
|
||||
}
|
||||
result = error_mark_node;
|
||||
}
|
||||
else if (code == NE_EXPR)
|
||||
/* !(y == x) or !(x == y) */
|
||||
result = build1_loc (loc, TRUTH_NOT_EXPR,
|
||||
boolean_type_node, result);
|
||||
break;
|
||||
|
||||
/* If a rewritten operator<=> candidate is selected by
|
||||
overload resolution for an operator @, x @ y is
|
||||
interpreted as 0 @ (y <=> x) if the selected candidate is
|
||||
a synthesized candidate with reversed order of parameters,
|
||||
or (x <=> y) @ 0 otherwise, using the selected rewritten
|
||||
operator<=> candidate. */
|
||||
case SPACESHIP_EXPR:
|
||||
if (!cand->reversed ())
|
||||
/* We're in the build_new_op call below for an outer
|
||||
reversed call; we don't need to do anything more. */
|
||||
break;
|
||||
gcc_fallthrough ();
|
||||
case LT_EXPR:
|
||||
case LE_EXPR:
|
||||
case GT_EXPR:
|
||||
case GE_EXPR:
|
||||
{
|
||||
tree lhs = result;
|
||||
tree rhs = integer_zero_node;
|
||||
if (cand->reversed ())
|
||||
std::swap (lhs, rhs);
|
||||
result = build_new_op (loc, code,
|
||||
LOOKUP_NORMAL|LOOKUP_REWRITTEN,
|
||||
lhs, rhs, NULL_TREE,
|
||||
NULL, complain);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -6232,6 +6430,7 @@ build_new_op_1 (const op_location_t &loc, enum tree_code code, int flags,
|
||||
if (complain & tf_warning && warn_tautological_compare)
|
||||
warn_tautological_cmp (loc, code, arg1, arg2);
|
||||
/* Fall through. */
|
||||
case SPACESHIP_EXPR:
|
||||
case PLUS_EXPR:
|
||||
case MINUS_EXPR:
|
||||
case MULT_EXPR:
|
||||
@ -6307,6 +6506,29 @@ extract_call_expr (tree call)
|
||||
call = TREE_OPERAND (call, 0);
|
||||
if (TREE_CODE (call) == TARGET_EXPR)
|
||||
call = TARGET_EXPR_INITIAL (call);
|
||||
if (cxx_dialect >= cxx2a)
|
||||
switch (TREE_CODE (call))
|
||||
{
|
||||
/* C++20 rewritten comparison operators. */
|
||||
case TRUTH_NOT_EXPR:
|
||||
call = TREE_OPERAND (call, 0);
|
||||
break;
|
||||
case LT_EXPR:
|
||||
case LE_EXPR:
|
||||
case GT_EXPR:
|
||||
case GE_EXPR:
|
||||
case SPACESHIP_EXPR:
|
||||
{
|
||||
tree op0 = TREE_OPERAND (call, 0);
|
||||
if (integer_zerop (op0))
|
||||
call = TREE_OPERAND (call, 1);
|
||||
else
|
||||
call = op0;
|
||||
}
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
|
||||
gcc_assert (TREE_CODE (call) == CALL_EXPR
|
||||
|| TREE_CODE (call) == AGGR_INIT_EXPR
|
||||
|| call == error_mark_node);
|
||||
@ -10772,6 +10994,20 @@ joust_maybe_elide_copy (z_candidate *&cand)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* True if cand1 and cand2 represent the same function or function
|
||||
template. */
|
||||
|
||||
static bool
|
||||
same_fn_or_template (z_candidate *cand1, z_candidate *cand2)
|
||||
{
|
||||
if (cand1->fn == cand2->fn)
|
||||
return true;
|
||||
if (!cand1->template_decl || !cand2->template_decl)
|
||||
return false;
|
||||
return (most_general_template (TI_TEMPLATE (cand1->template_decl))
|
||||
== most_general_template (TI_TEMPLATE (cand2->template_decl)));
|
||||
}
|
||||
|
||||
/* Compare two candidates for overloading as described in
|
||||
[over.match.best]. Return values:
|
||||
|
||||
@ -10798,6 +11034,7 @@ joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn,
|
||||
/* If we have two pseudo-candidates for conversions to the same type,
|
||||
or two candidates for the same function, arbitrarily pick one. */
|
||||
if (cand1->fn == cand2->fn
|
||||
&& cand1->reversed () == cand2->reversed ()
|
||||
&& (IS_TYPE_OR_DECL_P (cand1->fn)))
|
||||
return 1;
|
||||
|
||||
@ -10917,6 +11154,21 @@ joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn,
|
||||
|
||||
if (winner && comp != winner)
|
||||
{
|
||||
if (same_fn_or_template (cand1, cand2))
|
||||
{
|
||||
/* Ambiguity between normal and reversed versions of the
|
||||
same comparison operator; prefer the normal one.
|
||||
https://lists.isocpp.org/core/2019/10/7438.php */
|
||||
if (cand1->reversed ())
|
||||
winner = -1;
|
||||
else
|
||||
{
|
||||
gcc_checking_assert (cand2->reversed ());
|
||||
winner = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
winner = 0;
|
||||
goto tweak;
|
||||
}
|
||||
@ -11046,6 +11298,21 @@ joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn,
|
||||
return winner;
|
||||
}
|
||||
|
||||
/* F2 is a rewritten candidate (12.4.1.2) and F1 is not, or F1 and F2 are
|
||||
rewritten candidates, and F2 is a synthesized candidate with reversed
|
||||
order of parameters and F1 is not. */
|
||||
if (cand1->rewritten ())
|
||||
{
|
||||
if (!cand2->rewritten ())
|
||||
return -1;
|
||||
if (!cand1->reversed () && cand2->reversed ())
|
||||
return 1;
|
||||
if (cand1->reversed () && !cand2->reversed ())
|
||||
return -1;
|
||||
}
|
||||
else if (cand2->rewritten ())
|
||||
return 1;
|
||||
|
||||
/* F1 is generated from a deduction-guide (13.3.1.8) and F2 is not */
|
||||
if (deduction_guide_p (cand1->fn))
|
||||
{
|
||||
|
@ -3234,6 +3234,17 @@ add_implicitly_declared_members (tree t, tree* access_decls,
|
||||
a virtual function from a base class. */
|
||||
declare_virt_assop_and_dtor (t);
|
||||
|
||||
/* If the class definition does not explicitly declare an == operator
|
||||
function, but declares a defaulted three-way comparison operator function,
|
||||
an == operator function is declared implicitly. */
|
||||
if (!classtype_has_op (t, EQ_EXPR))
|
||||
if (tree space = classtype_has_defaulted_op (t, SPACESHIP_EXPR))
|
||||
{
|
||||
tree eq = implicitly_declare_fn (sfk_comparison, t, false, space,
|
||||
NULL_TREE);
|
||||
add_method (t, eq, false);
|
||||
}
|
||||
|
||||
while (*access_decls)
|
||||
{
|
||||
tree using_decl = TREE_VALUE (*access_decls);
|
||||
@ -5386,6 +5397,44 @@ classtype_has_depr_implicit_copy (tree t)
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* True iff T has a member or friend declaration of operator OP. */
|
||||
|
||||
bool
|
||||
classtype_has_op (tree t, tree_code op)
|
||||
{
|
||||
tree name = ovl_op_identifier (op);
|
||||
if (get_class_binding (t, name))
|
||||
return true;
|
||||
for (tree f = DECL_FRIENDLIST (TYPE_MAIN_DECL (t)); f; f = TREE_CHAIN (f))
|
||||
if (FRIEND_NAME (f) == name)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* If T has a defaulted member or friend declaration of OP, return it. */
|
||||
|
||||
tree
|
||||
classtype_has_defaulted_op (tree t, tree_code op)
|
||||
{
|
||||
tree name = ovl_op_identifier (op);
|
||||
for (ovl_iterator oi (get_class_binding (t, name)); oi; ++oi)
|
||||
{
|
||||
tree fn = *oi;
|
||||
if (DECL_DEFAULTED_FN (fn))
|
||||
return fn;
|
||||
}
|
||||
for (tree f = DECL_FRIENDLIST (TYPE_MAIN_DECL (t)); f; f = TREE_CHAIN (f))
|
||||
if (FRIEND_NAME (f) == name)
|
||||
for (tree l = FRIEND_DECLS (f); l; l = TREE_CHAIN (l))
|
||||
{
|
||||
tree fn = TREE_VALUE (l);
|
||||
if (DECL_DEFAULTED_FN (fn))
|
||||
return fn;
|
||||
}
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* Nonzero if we need to build up a constructor call when initializing an
|
||||
object of this class, either because it has a user-declared constructor
|
||||
or because it doesn't have a default constructor (so we need to give an
|
||||
|
@ -2480,6 +2480,12 @@ cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t,
|
||||
else if (code == POINTER_PLUS_EXPR)
|
||||
r = cxx_fold_pointer_plus_expression (ctx, t, lhs, rhs, non_constant_p,
|
||||
overflow_p);
|
||||
else if (code == SPACESHIP_EXPR)
|
||||
{
|
||||
r = genericize_spaceship (type, lhs, rhs);
|
||||
r = cxx_eval_constant_expression (ctx, r, false, non_constant_p,
|
||||
overflow_p);
|
||||
}
|
||||
|
||||
if (r == NULL_TREE)
|
||||
r = fold_binary_loc (loc, code, type, lhs, rhs);
|
||||
@ -5226,6 +5232,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
|
||||
case GE_EXPR:
|
||||
case EQ_EXPR:
|
||||
case NE_EXPR:
|
||||
case SPACESHIP_EXPR:
|
||||
case UNORDERED_EXPR:
|
||||
case ORDERED_EXPR:
|
||||
case UNLT_EXPR:
|
||||
@ -7037,6 +7044,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
|
||||
case GE_EXPR:
|
||||
case EQ_EXPR:
|
||||
case NE_EXPR:
|
||||
case SPACESHIP_EXPR:
|
||||
want_rval = true;
|
||||
goto binary;
|
||||
|
||||
|
@ -1144,6 +1144,17 @@ cp_fold_function (tree fndecl)
|
||||
cp_walk_tree (&DECL_SAVED_TREE (fndecl), cp_fold_r, &pset, NULL);
|
||||
}
|
||||
|
||||
/* Turn SPACESHIP_EXPR EXPR into GENERIC. */
|
||||
|
||||
static tree genericize_spaceship (tree expr)
|
||||
{
|
||||
iloc_sentinel s (cp_expr_location (expr));
|
||||
tree type = TREE_TYPE (expr);
|
||||
tree op0 = TREE_OPERAND (expr, 0);
|
||||
tree op1 = TREE_OPERAND (expr, 1);
|
||||
return genericize_spaceship (type, op0, op1);
|
||||
}
|
||||
|
||||
/* Perform any pre-gimplification lowering of C++ front end trees to
|
||||
GENERIC. */
|
||||
|
||||
@ -1574,6 +1585,10 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
|
||||
genericize_break_stmt (stmt_p);
|
||||
break;
|
||||
|
||||
case SPACESHIP_EXPR:
|
||||
*stmt_p = genericize_spaceship (*stmt_p);
|
||||
break;
|
||||
|
||||
case OMP_FOR:
|
||||
case OMP_SIMD:
|
||||
case OMP_DISTRIBUTE:
|
||||
|
@ -518,6 +518,7 @@ cp_common_init_ts (void)
|
||||
MARK_TS_EXP (VEC_DELETE_EXPR);
|
||||
MARK_TS_EXP (VEC_INIT_EXPR);
|
||||
MARK_TS_EXP (VEC_NEW_EXPR);
|
||||
MARK_TS_EXP (SPACESHIP_EXPR);
|
||||
|
||||
/* Fold expressions. */
|
||||
MARK_TS_EXP (BINARY_LEFT_FOLD_EXPR);
|
||||
|
@ -255,6 +255,7 @@ DEFTREECODE (IMPLICIT_CONV_EXPR, "implicit_conv_expr", tcc_unary, 1)
|
||||
DEFTREECODE (DOTSTAR_EXPR, "dotstar_expr", tcc_expression, 2)
|
||||
DEFTREECODE (TYPEID_EXPR, "typeid_expr", tcc_expression, 1)
|
||||
DEFTREECODE (NOEXCEPT_EXPR, "noexcept_expr", tcc_unary, 1)
|
||||
DEFTREECODE (SPACESHIP_EXPR, "spaceship_expr", tcc_expression, 2)
|
||||
|
||||
/* A placeholder for an expression that is not type-dependent, but
|
||||
does occur in a template. When an expression that is not
|
||||
|
@ -2695,7 +2695,8 @@ struct GTY(()) lang_decl_fn {
|
||||
unsigned omp_declare_reduction_p : 1;
|
||||
unsigned has_dependent_explicit_spec_p : 1;
|
||||
unsigned immediate_fn_p : 1;
|
||||
unsigned spare : 11;
|
||||
unsigned maybe_deleted : 1;
|
||||
unsigned spare : 10;
|
||||
|
||||
/* 32-bits padding on 64-bit host. */
|
||||
|
||||
@ -3137,6 +3138,11 @@ struct GTY(()) lang_decl {
|
||||
#define DECL_HAS_DEPENDENT_EXPLICIT_SPEC_P(NODE) \
|
||||
(LANG_DECL_FN_CHECK (NODE)->has_dependent_explicit_spec_p)
|
||||
|
||||
/* Nonzero for a defaulted FUNCTION_DECL for which we haven't decided yet if
|
||||
it's deleted. */
|
||||
#define DECL_MAYBE_DELETED(NODE) \
|
||||
(LANG_DECL_FN_CHECK (NODE)->maybe_deleted)
|
||||
|
||||
/* True (in a FUNCTION_DECL) if NODE is a virtual function that is an
|
||||
invalid overrider for a function from a base class. Once we have
|
||||
complained about an invalid overrider we avoid complaining about it
|
||||
@ -5203,6 +5209,7 @@ enum special_function_kind {
|
||||
destroyed. */
|
||||
sfk_conversion, /* A conversion operator. */
|
||||
sfk_deduction_guide, /* A class template deduction guide. */
|
||||
sfk_comparison, /* A comparison operator (e.g. ==, <, <=>). */
|
||||
sfk_virtual_destructor /* Used by member synthesis fns. */
|
||||
};
|
||||
|
||||
@ -5565,6 +5572,17 @@ enum overload_flags { NO_SPECIAL = 0, DTOR_FLAG, TYPENAME_FLAG };
|
||||
#define LOOKUP_ALLOW_FLEXARRAY_INIT (LOOKUP_DELEGATING_CONS << 1)
|
||||
/* Require constant initialization of a non-constant variable. */
|
||||
#define LOOKUP_CONSTINIT (LOOKUP_ALLOW_FLEXARRAY_INIT << 1)
|
||||
/* We're looking for either a rewritten comparison operator candidate or the
|
||||
operator to use on the former's result. We distinguish between the two by
|
||||
knowing that comparisons other than == and <=> must be the latter, as must
|
||||
a <=> expression trying to rewrite to <=> without reversing. */
|
||||
#define LOOKUP_REWRITTEN (LOOKUP_CONSTINIT << 1)
|
||||
/* Reverse the order of the two arguments for comparison rewriting. First we
|
||||
swap the arguments in add_operator_candidates, then we swap the conversions
|
||||
in add_candidate (so that they correspond to the original order of the
|
||||
args), then we swap the conversions back in build_new_op_1 (so they
|
||||
correspond to the order of the args in the candidate). */
|
||||
#define LOOKUP_REVERSED (LOOKUP_REWRITTEN << 1)
|
||||
|
||||
#define LOOKUP_NAMESPACES_ONLY(F) \
|
||||
(((F) & LOOKUP_PREFER_NAMESPACES) && !((F) & LOOKUP_PREFER_TYPES))
|
||||
@ -6371,6 +6389,8 @@ extern bool type_has_virtual_destructor (tree);
|
||||
extern bool classtype_has_move_assign_or_move_ctor_p (tree, bool user_declared);
|
||||
extern bool classtype_has_non_deleted_move_ctor (tree);
|
||||
extern tree classtype_has_depr_implicit_copy (tree);
|
||||
extern bool classtype_has_op (tree, tree_code);
|
||||
extern tree classtype_has_defaulted_op (tree, tree_code);
|
||||
extern bool type_build_ctor_call (tree);
|
||||
extern bool type_build_dtor_call (tree);
|
||||
extern void explain_non_literal_class (tree);
|
||||
@ -7544,6 +7564,8 @@ extern tree composite_pointer_type (const op_location_t &,
|
||||
extern tree merge_types (tree, tree);
|
||||
extern tree strip_array_domain (tree);
|
||||
extern tree check_return_expr (tree, bool *);
|
||||
extern tree spaceship_type (tree, tsubst_flags_t = tf_warning_or_error);
|
||||
extern tree genericize_spaceship (tree, tree, tree);
|
||||
extern tree cp_build_binary_op (const op_location_t &,
|
||||
enum tree_code, tree, tree,
|
||||
tsubst_flags_t);
|
||||
|
@ -16793,6 +16793,13 @@ finish_function (bool inline_p)
|
||||
}
|
||||
}
|
||||
|
||||
if (DECL_DELETED_FN (fndecl))
|
||||
{
|
||||
DECL_INITIAL (fndecl) = error_mark_node;
|
||||
DECL_SAVED_TREE (fndecl) = NULL_TREE;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// If this is a concept, check that the definition is reasonable.
|
||||
if (DECL_DECLARED_CONCEPT_P (fndecl))
|
||||
check_function_concept (fndecl);
|
||||
@ -16939,6 +16946,7 @@ finish_function (bool inline_p)
|
||||
if (!processing_template_decl && !DECL_IMMEDIATE_FUNCTION_P (fndecl))
|
||||
cp_genericize (fndecl);
|
||||
|
||||
cleanup:
|
||||
/* We're leaving the context of this function, so zap cfun. It's still in
|
||||
DECL_STRUCT_FUNCTION, and we'll restore it in tree_rest_of_compilation. */
|
||||
set_cfun (NULL);
|
||||
|
@ -927,6 +927,10 @@ grokfield (const cp_declarator *declarator,
|
||||
}
|
||||
else if (init == ridpointers[(int)RID_DEFAULT])
|
||||
{
|
||||
if (friendp)
|
||||
/* ??? do_friend doesn't set this because funcdef_flag is false
|
||||
for in-class defaulted functions. So set it here. */
|
||||
SET_DECL_FRIEND_CONTEXT (value, current_class_type);
|
||||
if (defaultable_fn_check (value))
|
||||
{
|
||||
DECL_DEFAULTED_FN (value) = 1;
|
||||
@ -5471,6 +5475,17 @@ mark_used (tree decl, tsubst_flags_t complain)
|
||||
if (TREE_CODE (decl) == CONST_DECL)
|
||||
used_types_insert (DECL_CONTEXT (decl));
|
||||
|
||||
if (TREE_CODE (decl) == FUNCTION_DECL
|
||||
&& DECL_MAYBE_DELETED (decl))
|
||||
{
|
||||
/* ??? Switch other defaulted functions to use DECL_MAYBE_DELETED? */
|
||||
gcc_assert (special_function_p (decl) == sfk_comparison);
|
||||
|
||||
++function_depth;
|
||||
synthesize_method (decl);
|
||||
--function_depth;
|
||||
}
|
||||
|
||||
if (TREE_CODE (decl) == FUNCTION_DECL
|
||||
&& !maybe_instantiate_noexcept (decl, complain))
|
||||
return false;
|
||||
@ -5592,7 +5607,6 @@ mark_used (tree decl, tsubst_flags_t complain)
|
||||
|
||||
/* Is it a synthesized method that needs to be synthesized? */
|
||||
if (TREE_CODE (decl) == FUNCTION_DECL
|
||||
&& DECL_NONSTATIC_MEMBER_FUNCTION_P (decl)
|
||||
&& DECL_DEFAULTED_FN (decl)
|
||||
/* A function defaulted outside the class is synthesized either by
|
||||
cp_finish_decl or instantiate_decl. */
|
||||
|
@ -2286,6 +2286,7 @@ dump_expr (cxx_pretty_printer *pp, tree t, int flags)
|
||||
case GE_EXPR:
|
||||
case EQ_EXPR:
|
||||
case NE_EXPR:
|
||||
case SPACESHIP_EXPR:
|
||||
case EXACT_DIV_EXPR:
|
||||
dump_binary_op (pp, OVL_OP_INFO (false, TREE_CODE (t))->name, t, flags);
|
||||
break;
|
||||
|
735
gcc/cp/method.c
735
gcc/cp/method.c
@ -406,6 +406,7 @@ type_has_trivial_fn (tree ctype, special_function_kind sfk)
|
||||
case sfk_virtual_destructor:
|
||||
return !TYPE_HAS_NONTRIVIAL_DESTRUCTOR (ctype);
|
||||
case sfk_inheriting_constructor:
|
||||
case sfk_comparison:
|
||||
return false;
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
@ -877,6 +878,588 @@ do_build_copy_assign (tree fndecl)
|
||||
finish_compound_stmt (compound_stmt);
|
||||
}
|
||||
|
||||
/* C++20 <compare> comparison category types. */
|
||||
|
||||
enum comp_cat_tag
|
||||
{
|
||||
cc_weak_equality,
|
||||
cc_strong_equality,
|
||||
cc_partial_ordering,
|
||||
cc_weak_ordering,
|
||||
cc_strong_ordering,
|
||||
cc_last
|
||||
};
|
||||
|
||||
/* Names of the comparison categories and their value members, to be indexed by
|
||||
comp_cat_tag enumerators. genericize_spaceship below relies on the ordering
|
||||
of the members. */
|
||||
|
||||
struct comp_cat_info_t
|
||||
{
|
||||
const char *name;
|
||||
const char *members[4];
|
||||
};
|
||||
static const comp_cat_info_t comp_cat_info[cc_last]
|
||||
= {
|
||||
{ "weak_equality", "equivalent", "nonequivalent" },
|
||||
{ "strong_equality", "equal", "nonequal" },
|
||||
{ "partial_ordering", "equivalent", "greater", "less", "unordered" },
|
||||
{ "weak_ordering", "equivalent", "greater", "less" },
|
||||
{ "strong_ordering", "equal", "greater", "less" }
|
||||
};
|
||||
|
||||
/* A cache of the category types to speed repeated lookups. */
|
||||
|
||||
static GTY((deletable)) tree comp_cat_cache[cc_last];
|
||||
|
||||
/* Look up one of the result variables in the comparison category type. */
|
||||
|
||||
static tree
|
||||
lookup_comparison_result (tree type, const char *name_str,
|
||||
tsubst_flags_t complain = tf_warning_or_error)
|
||||
{
|
||||
tree name = get_identifier (name_str);
|
||||
tree decl = lookup_qualified_name (type, name);
|
||||
if (TREE_CODE (decl) != VAR_DECL)
|
||||
{
|
||||
if (complain & tf_error)
|
||||
{
|
||||
auto_diagnostic_group d;
|
||||
if (decl == error_mark_node || TREE_CODE (decl) == TREE_LIST)
|
||||
qualified_name_lookup_error (type, name, decl, input_location);
|
||||
else
|
||||
error ("%<%T::%D%> is not a static data member", type, decl);
|
||||
inform (input_location, "determining value of %qs", "operator<=>");
|
||||
}
|
||||
return error_mark_node;
|
||||
}
|
||||
return decl;
|
||||
}
|
||||
|
||||
/* Look up a <compare> comparison category type in std. */
|
||||
|
||||
static tree
|
||||
lookup_comparison_category (comp_cat_tag tag,
|
||||
tsubst_flags_t complain = tf_warning_or_error)
|
||||
{
|
||||
if (tree cached = comp_cat_cache[tag])
|
||||
return cached;
|
||||
|
||||
tree name = get_identifier (comp_cat_info[tag].name);
|
||||
tree decl = lookup_qualified_name (std_node, name);
|
||||
if (TREE_CODE (decl) != TYPE_DECL)
|
||||
{
|
||||
if (complain & tf_error)
|
||||
{
|
||||
auto_diagnostic_group d;
|
||||
if (decl == error_mark_node || TREE_CODE (decl) == TREE_LIST)
|
||||
qualified_name_lookup_error (std_node, name, decl, input_location);
|
||||
else
|
||||
error ("%<std::%D%> is not a type", decl);
|
||||
inform (input_location, "forming type of %qs", "operator<=>");
|
||||
}
|
||||
return error_mark_node;
|
||||
}
|
||||
/* Also make sure we can look up the value members now, since we won't
|
||||
really use them until genericize time. */
|
||||
tree type = TREE_TYPE (decl);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
const char *p = comp_cat_info[tag].members[i];
|
||||
if (!p) break;
|
||||
if (lookup_comparison_result (type, p, complain)
|
||||
== error_mark_node)
|
||||
return error_mark_node;
|
||||
}
|
||||
return comp_cat_cache[tag] = type;
|
||||
}
|
||||
|
||||
/* Wrapper that takes the tag rather than the type. */
|
||||
|
||||
static tree
|
||||
lookup_comparison_result (comp_cat_tag tag, const char *name_str,
|
||||
tsubst_flags_t complain = tf_warning_or_error)
|
||||
{
|
||||
tree type = lookup_comparison_category (tag, complain);
|
||||
return lookup_comparison_result (type, name_str, complain);
|
||||
}
|
||||
|
||||
/* Wrapper that takes the index into the members array instead of the name. */
|
||||
|
||||
static tree
|
||||
lookup_comparison_result (comp_cat_tag tag, tree type, int idx)
|
||||
{
|
||||
const char *name_str = comp_cat_info[tag].members[idx];
|
||||
if (!name_str)
|
||||
return NULL_TREE;
|
||||
return lookup_comparison_result (type, name_str);
|
||||
}
|
||||
|
||||
/* Does TYPE correspond to TAG? */
|
||||
|
||||
static bool
|
||||
is_cat (tree type, comp_cat_tag tag)
|
||||
{
|
||||
tree name = TYPE_LINKAGE_IDENTIFIER (type);
|
||||
return id_equal (name, comp_cat_info[tag].name);
|
||||
}
|
||||
|
||||
/* Return the comp_cat_tag for TYPE. */
|
||||
|
||||
static comp_cat_tag
|
||||
cat_tag_for (tree type)
|
||||
{
|
||||
for (int i = 0; i < cc_last; ++i)
|
||||
{
|
||||
comp_cat_tag tag = (comp_cat_tag)i;
|
||||
if (is_cat (type, tag))
|
||||
return tag;
|
||||
}
|
||||
return cc_last;
|
||||
}
|
||||
|
||||
/* Return the comparison category tag of a <=> expression with non-class type
|
||||
OPTYPE. */
|
||||
|
||||
static comp_cat_tag
|
||||
spaceship_comp_cat (tree optype)
|
||||
{
|
||||
if (INTEGRAL_OR_ENUMERATION_TYPE_P (optype) || TYPE_PTROBV_P (optype))
|
||||
return cc_strong_ordering;
|
||||
else if (TREE_CODE (optype) == REAL_TYPE)
|
||||
return cc_partial_ordering;
|
||||
else if (TYPE_PTRFN_P (optype) || TYPE_PTRMEM_P (optype)
|
||||
|| NULLPTR_TYPE_P (optype))
|
||||
return cc_strong_equality;
|
||||
else if (TREE_CODE (optype) == COMPLEX_TYPE)
|
||||
{
|
||||
tree intype = optype;
|
||||
while (TREE_CODE (intype) == COMPLEX_TYPE)
|
||||
intype = TREE_TYPE (intype);
|
||||
if (TREE_CODE (intype) == REAL_TYPE)
|
||||
return cc_weak_equality;
|
||||
else
|
||||
return cc_strong_equality;
|
||||
}
|
||||
|
||||
/* FIXME should vector <=> produce a vector of one of the above? */
|
||||
gcc_unreachable ();
|
||||
}
|
||||
|
||||
/* Return the comparison category type of a <=> expression with non-class type
|
||||
OPTYPE. */
|
||||
|
||||
tree
|
||||
spaceship_type (tree optype, tsubst_flags_t complain)
|
||||
{
|
||||
comp_cat_tag tag = spaceship_comp_cat (optype);
|
||||
return lookup_comparison_category (tag, complain);
|
||||
}
|
||||
|
||||
/* Turn <=> with type TYPE and operands OP0 and OP1 into GENERIC. */
|
||||
|
||||
tree
|
||||
genericize_spaceship (tree type, tree op0, tree op1)
|
||||
{
|
||||
/* ??? maybe optimize based on knowledge of representation? */
|
||||
comp_cat_tag tag = cat_tag_for (type);
|
||||
gcc_checking_assert (tag < cc_last);
|
||||
|
||||
tree eq = lookup_comparison_result (tag, type, 0);
|
||||
tree negt = lookup_comparison_result (tag, type, 1);
|
||||
|
||||
if (tag == cc_strong_equality || tag == cc_weak_equality)
|
||||
{
|
||||
tree comp = fold_build2 (EQ_EXPR, boolean_type_node, op0, op1);
|
||||
return fold_build3 (COND_EXPR, type, comp, eq, negt);
|
||||
}
|
||||
|
||||
tree r;
|
||||
op0 = save_expr (op0);
|
||||
op1 = save_expr (op1);
|
||||
|
||||
if (tag == cc_partial_ordering)
|
||||
{
|
||||
/* op0 == op1 ? equivalent : op0 < op1 ? less :
|
||||
op0 > op1 ? greater : unordered */
|
||||
tree uo = lookup_comparison_result (tag, type, 3);
|
||||
tree comp = fold_build2 (GT_EXPR, boolean_type_node, op0, op1);
|
||||
r = fold_build3 (COND_EXPR, type, comp, negt, uo);
|
||||
}
|
||||
else
|
||||
/* op0 == op1 ? equal : op0 < op1 ? less : greater */
|
||||
r = negt;
|
||||
|
||||
tree lt = lookup_comparison_result (tag, type, 2);
|
||||
tree comp = fold_build2 (LT_EXPR, boolean_type_node, op0, op1);
|
||||
r = fold_build3 (COND_EXPR, type, comp, lt, r);
|
||||
|
||||
comp = fold_build2 (EQ_EXPR, boolean_type_node, op0, op1);
|
||||
r = fold_build3 (COND_EXPR, type, comp, eq, r);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Check that the signature of a defaulted comparison operator is
|
||||
well-formed. */
|
||||
|
||||
static bool
|
||||
early_check_defaulted_comparison (tree fn)
|
||||
{
|
||||
location_t loc = DECL_SOURCE_LOCATION (fn);
|
||||
tree ctx;
|
||||
if (DECL_CLASS_SCOPE_P (fn))
|
||||
ctx = DECL_CONTEXT (fn);
|
||||
else
|
||||
ctx = DECL_FRIEND_CONTEXT (fn);
|
||||
bool ok = true;
|
||||
|
||||
if (!DECL_OVERLOADED_OPERATOR_IS (fn, SPACESHIP_EXPR)
|
||||
&& !same_type_p (TREE_TYPE (TREE_TYPE (fn)), boolean_type_node))
|
||||
{
|
||||
error_at (loc, "defaulted %qD must return %<bool%>", fn);
|
||||
ok = false;
|
||||
}
|
||||
|
||||
int i = DECL_NONSTATIC_MEMBER_FUNCTION_P (fn);
|
||||
if (i && type_memfn_quals (TREE_TYPE (fn)) != TYPE_QUAL_CONST)
|
||||
{
|
||||
error_at (loc, "defaulted %qD must be %<const%>", fn);
|
||||
ok = false;
|
||||
}
|
||||
tree parmnode = FUNCTION_FIRST_USER_PARMTYPE (fn);
|
||||
for (; parmnode != void_list_node; parmnode = TREE_CHAIN (parmnode))
|
||||
{
|
||||
++i;
|
||||
tree parmtype = TREE_VALUE (parmnode);
|
||||
diagnostic_t kind = DK_UNSPECIFIED;
|
||||
int opt = 0;
|
||||
if (same_type_p (parmtype, ctx))
|
||||
/* The draft specifies const reference, but let's also allow by-value
|
||||
unless -Wpedantic, hopefully it will be added soon. */
|
||||
kind = DK_PEDWARN,
|
||||
opt = OPT_Wpedantic;
|
||||
else if (TREE_CODE (parmtype) != REFERENCE_TYPE
|
||||
|| TYPE_QUALS (TREE_TYPE (parmtype)) != TYPE_QUAL_CONST
|
||||
|| !(same_type_ignoring_top_level_qualifiers_p
|
||||
(TREE_TYPE (parmtype), ctx)))
|
||||
kind = DK_ERROR;
|
||||
if (kind)
|
||||
emit_diagnostic (kind, loc, opt, "defaulted %qD must have "
|
||||
"parameter type %<const %T&%>", fn, ctx);
|
||||
if (kind == DK_ERROR)
|
||||
ok = false;
|
||||
}
|
||||
|
||||
/* We still need to deduce deleted/constexpr/noexcept and maybe return. */
|
||||
DECL_MAYBE_DELETED (fn) = true;
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
/* Subroutine of build_comparison_op. Given the vec of memberwise
|
||||
comparisons COMPS, calculate the overall comparison category for
|
||||
operator<=>. */
|
||||
|
||||
static tree
|
||||
common_comparison_type (vec<tree> &comps)
|
||||
{
|
||||
tree seen[cc_last] = {};
|
||||
|
||||
for (unsigned i = 0; i < comps.length(); ++i)
|
||||
{
|
||||
tree comp = comps[i];
|
||||
tree ctype = TREE_TYPE (comp);
|
||||
comp_cat_tag tag = cat_tag_for (ctype);
|
||||
if (tag < cc_last)
|
||||
seen[tag] = ctype;
|
||||
else
|
||||
/* If any Ti is not a comparison category type, U is void. */
|
||||
return void_type_node;
|
||||
}
|
||||
|
||||
/* Otherwise, if at least one T i is std::weak_equality, or at least one T i
|
||||
is std::strong_equality and at least one T j is std::partial_ordering or
|
||||
std::weak_ordering, U is std::weak_equality. */
|
||||
if (tree t = seen[cc_weak_equality]) return t;
|
||||
if (seen[cc_strong_equality]
|
||||
&& (seen[cc_partial_ordering] || seen[cc_weak_ordering]))
|
||||
return lookup_comparison_category (cc_weak_equality);
|
||||
|
||||
/* Otherwise, if at least one T i is std::strong_equality, U is
|
||||
std::strong_equality. */
|
||||
if (tree t = seen[cc_strong_equality]) return t;
|
||||
|
||||
/* Otherwise, if at least one T i is std::partial_ordering, U is
|
||||
std::partial_ordering. */
|
||||
if (tree t = seen[cc_partial_ordering]) return t;
|
||||
|
||||
/* Otherwise, if at least one T i is std::weak_ordering, U is
|
||||
std::weak_ordering. */
|
||||
if (tree t = seen[cc_weak_ordering]) return t;
|
||||
|
||||
/* Otherwise, U is std::strong_ordering. */
|
||||
if (tree t = seen[cc_strong_ordering]) return t;
|
||||
return lookup_comparison_category (cc_strong_ordering);
|
||||
}
|
||||
|
||||
/* Data structure for build_comparison_op. */
|
||||
|
||||
struct comp_info
|
||||
{
|
||||
tree fndecl;
|
||||
location_t loc;
|
||||
bool defining;
|
||||
bool first_time;
|
||||
bool constexp;
|
||||
bool was_constexp;
|
||||
bool noex;
|
||||
|
||||
comp_info (tree fndecl, tsubst_flags_t &complain)
|
||||
: fndecl (fndecl)
|
||||
{
|
||||
loc = DECL_SOURCE_LOCATION (fndecl);
|
||||
|
||||
/* We only have tf_error set when we're called from
|
||||
explain_invalid_constexpr_fn or maybe_explain_implicit_delete. */
|
||||
defining = !(complain & tf_error);
|
||||
|
||||
first_time = DECL_MAYBE_DELETED (fndecl);
|
||||
DECL_MAYBE_DELETED (fndecl) = false;
|
||||
|
||||
/* Do we want to try to set constexpr? */
|
||||
was_constexp = DECL_DECLARED_CONSTEXPR_P (fndecl);
|
||||
constexp = first_time;
|
||||
if (constexp)
|
||||
/* Set this for var_in_constexpr_fn. */
|
||||
DECL_DECLARED_CONSTEXPR_P (fndecl) = true;
|
||||
|
||||
/* Do we want to try to set noexcept? */
|
||||
noex = first_time;
|
||||
if (noex)
|
||||
{
|
||||
tree raises = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fndecl));
|
||||
if (raises && !UNEVALUATED_NOEXCEPT_SPEC_P (raises))
|
||||
/* There was an explicit exception-specification. */
|
||||
noex = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* EXPR is an expression built as part of the function body.
|
||||
Adjust the properties appropriately. */
|
||||
void check (tree expr)
|
||||
{
|
||||
if (expr == error_mark_node)
|
||||
DECL_DELETED_FN (fndecl) = true;
|
||||
if ((constexp || was_constexp)
|
||||
&& !potential_rvalue_constant_expression (expr))
|
||||
{
|
||||
if (was_constexp)
|
||||
require_potential_rvalue_constant_expression (expr);
|
||||
else
|
||||
constexp = false;
|
||||
}
|
||||
if (noex && !expr_noexcept_p (expr, tf_none))
|
||||
noex = false;
|
||||
}
|
||||
};
|
||||
|
||||
/* Build up the definition of a defaulted comparison operator. Unlike other
|
||||
defaulted functions that use synthesized_method_walk to determine whether
|
||||
the function is e.g. deleted, for comparisons we use the same code. We try
|
||||
to use synthesize_method at the earliest opportunity and bail out if the
|
||||
function ends up being deleted. */
|
||||
|
||||
static void
|
||||
build_comparison_op (tree fndecl, tsubst_flags_t complain)
|
||||
{
|
||||
comp_info info (fndecl, complain);
|
||||
|
||||
if (!info.defining && !(complain & tf_error) && !DECL_MAYBE_DELETED (fndecl))
|
||||
return;
|
||||
|
||||
int flags = LOOKUP_NORMAL | LOOKUP_NONVIRTUAL | LOOKUP_DEFAULTED;
|
||||
const ovl_op_info_t *op = IDENTIFIER_OVL_OP_INFO (DECL_NAME (fndecl));
|
||||
tree_code code = op->tree_code;
|
||||
|
||||
tree lhs = DECL_ARGUMENTS (fndecl);
|
||||
tree rhs = DECL_CHAIN (lhs);
|
||||
if (is_this_parameter (lhs))
|
||||
lhs = cp_build_fold_indirect_ref (lhs);
|
||||
else
|
||||
lhs = convert_from_reference (lhs);
|
||||
rhs = convert_from_reference (rhs);
|
||||
tree ctype = TYPE_MAIN_VARIANT (TREE_TYPE (lhs));
|
||||
|
||||
iloc_sentinel ils (info.loc);
|
||||
|
||||
/* A defaulted comparison operator function for class C is defined as
|
||||
deleted if ... C is a union-like class. */
|
||||
if (TREE_CODE (ctype) == UNION_TYPE)
|
||||
{
|
||||
if (complain & tf_error)
|
||||
inform (info.loc, "cannot default compare union %qT", ctype);
|
||||
DECL_DELETED_FN (fndecl) = true;
|
||||
}
|
||||
|
||||
tree compound_stmt = NULL_TREE;
|
||||
if (info.defining)
|
||||
compound_stmt = begin_compound_stmt (0);
|
||||
else
|
||||
++cp_unevaluated_operand;
|
||||
|
||||
tree rettype = TREE_TYPE (TREE_TYPE (fndecl));
|
||||
if (code != SPACESHIP_EXPR && is_auto (rettype))
|
||||
{
|
||||
rettype = boolean_type_node;
|
||||
apply_deduced_return_type (fndecl, rettype);
|
||||
}
|
||||
|
||||
if (code == EQ_EXPR || code == SPACESHIP_EXPR)
|
||||
{
|
||||
auto_vec<tree> comps;
|
||||
|
||||
/* Compare each of the subobjects. Note that we get bases from
|
||||
next_initializable_field because we're past C++17. */
|
||||
for (tree field = next_initializable_field (TYPE_FIELDS (ctype));
|
||||
field;
|
||||
field = next_initializable_field (DECL_CHAIN (field)))
|
||||
{
|
||||
tree expr_type = TREE_TYPE (field);
|
||||
|
||||
/* A defaulted comparison operator function for class C is defined as
|
||||
deleted if any non-static data member of C is of reference type or
|
||||
C is a union-like class. */
|
||||
if (TREE_CODE (expr_type) == REFERENCE_TYPE)
|
||||
{
|
||||
if (complain & tf_error)
|
||||
inform (DECL_SOURCE_LOCATION (field), "cannot default compare "
|
||||
"reference member %qD", field);
|
||||
DECL_DELETED_FN (fndecl) = true;
|
||||
continue;
|
||||
}
|
||||
else if (ANON_UNION_TYPE_P (expr_type))
|
||||
{
|
||||
if (complain & tf_error)
|
||||
inform (DECL_SOURCE_LOCATION (field), "cannot default compare "
|
||||
"anonymous union member");
|
||||
DECL_DELETED_FN (fndecl) = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
tree lhs_mem = build3 (COMPONENT_REF, expr_type, lhs, field,
|
||||
NULL_TREE);
|
||||
tree rhs_mem = build3 (COMPONENT_REF, expr_type, rhs, field,
|
||||
NULL_TREE);
|
||||
tree comp = build_new_op (info.loc, code, flags, lhs_mem, rhs_mem,
|
||||
NULL_TREE, NULL, complain);
|
||||
comps.safe_push (comp);
|
||||
}
|
||||
if (code == SPACESHIP_EXPR && is_auto (rettype))
|
||||
{
|
||||
rettype = common_comparison_type (comps);
|
||||
apply_deduced_return_type (fndecl, rettype);
|
||||
}
|
||||
for (unsigned i = 0; i < comps.length(); ++i)
|
||||
{
|
||||
tree comp = comps[i];
|
||||
tree eq, retval = NULL_TREE, if_ = NULL_TREE;
|
||||
if (info.defining)
|
||||
if_ = begin_if_stmt ();
|
||||
/* Spaceship is specified to use !=, but for the comparison category
|
||||
types, != is equivalent to !(==), so let's use == directly. */
|
||||
if (code == EQ_EXPR)
|
||||
{
|
||||
/* if (x==y); else return false; */
|
||||
eq = comp;
|
||||
retval = boolean_false_node;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* if (auto v = x<=>y, v == 0); else return v; */
|
||||
if (TREE_CODE (comp) == SPACESHIP_EXPR)
|
||||
TREE_TYPE (comp) = rettype;
|
||||
else
|
||||
comp = build_static_cast (rettype, comp, complain);
|
||||
info.check (comp);
|
||||
if (info.defining)
|
||||
{
|
||||
tree var = create_temporary_var (rettype);
|
||||
pushdecl (var);
|
||||
cp_finish_decl (var, comp, false, NULL_TREE, flags);
|
||||
comp = retval = var;
|
||||
}
|
||||
eq = build_new_op (info.loc, EQ_EXPR, flags, comp,
|
||||
integer_zero_node, NULL_TREE, NULL,
|
||||
complain);
|
||||
}
|
||||
tree ceq = contextual_conv_bool (eq, complain);
|
||||
info.check (ceq);
|
||||
if (info.defining)
|
||||
{
|
||||
finish_if_stmt_cond (ceq, if_);
|
||||
finish_then_clause (if_);
|
||||
begin_else_clause (if_);
|
||||
finish_return_stmt (retval);
|
||||
finish_else_clause (if_);
|
||||
finish_if_stmt (if_);
|
||||
}
|
||||
}
|
||||
if (info.defining)
|
||||
{
|
||||
tree val;
|
||||
if (code == EQ_EXPR)
|
||||
val = boolean_true_node;
|
||||
else
|
||||
{
|
||||
tree seql = lookup_comparison_result (cc_strong_ordering,
|
||||
"equal", complain);
|
||||
val = build_static_cast (rettype, seql, complain);
|
||||
}
|
||||
finish_return_stmt (val);
|
||||
}
|
||||
}
|
||||
else if (code == NE_EXPR)
|
||||
{
|
||||
tree comp = build_new_op (info.loc, EQ_EXPR, flags, lhs, rhs,
|
||||
NULL_TREE, NULL, complain);
|
||||
comp = contextual_conv_bool (comp, complain);
|
||||
info.check (comp);
|
||||
if (info.defining)
|
||||
{
|
||||
tree neg = build1 (TRUTH_NOT_EXPR, boolean_type_node, comp);
|
||||
finish_return_stmt (neg);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tree comp = build_new_op (info.loc, SPACESHIP_EXPR, flags, lhs, rhs,
|
||||
NULL_TREE, NULL, complain);
|
||||
tree comp2 = build_new_op (info.loc, code, flags, comp, integer_zero_node,
|
||||
NULL_TREE, NULL, complain);
|
||||
info.check (comp2);
|
||||
if (info.defining)
|
||||
finish_return_stmt (comp2);
|
||||
}
|
||||
|
||||
if (info.defining)
|
||||
finish_compound_stmt (compound_stmt);
|
||||
else
|
||||
--cp_unevaluated_operand;
|
||||
|
||||
if (info.first_time)
|
||||
{
|
||||
DECL_DECLARED_CONSTEXPR_P (fndecl) = info.constexp || info.was_constexp;
|
||||
tree raises = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fndecl));
|
||||
if (!raises || UNEVALUATED_NOEXCEPT_SPEC_P (raises))
|
||||
{
|
||||
raises = info.noex ? noexcept_true_spec : noexcept_false_spec;
|
||||
TREE_TYPE (fndecl) = build_exception_variant (TREE_TYPE (fndecl),
|
||||
raises);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Synthesize FNDECL, a non-static member function. */
|
||||
|
||||
void
|
||||
@ -889,6 +1472,7 @@ synthesize_method (tree fndecl)
|
||||
location_t save_input_location = input_location;
|
||||
int error_count = errorcount;
|
||||
int warning_count = warningcount + werrorcount;
|
||||
special_function_kind sfk = special_function_p (fndecl);
|
||||
|
||||
/* Reset the source location, we might have been previously
|
||||
deferred, and thus have saved where we were first needed. */
|
||||
@ -930,6 +1514,12 @@ synthesize_method (tree fndecl)
|
||||
else
|
||||
finish_mem_initializers (NULL_TREE);
|
||||
}
|
||||
else if (sfk == sfk_comparison)
|
||||
{
|
||||
/* Pass tf_none so the function is just deleted if there's a problem. */
|
||||
build_comparison_op (fndecl, tf_none);
|
||||
need_body = false;
|
||||
}
|
||||
|
||||
/* If we haven't yet generated the body of the function, just
|
||||
generate an empty compound statement. */
|
||||
@ -941,7 +1531,10 @@ synthesize_method (tree fndecl)
|
||||
}
|
||||
|
||||
finish_function_body (stmt);
|
||||
expand_or_defer_fn (finish_function (/*inline_p=*/false));
|
||||
finish_function (/*inline_p=*/false);
|
||||
|
||||
if (!DECL_DELETED_FN (fndecl))
|
||||
expand_or_defer_fn (fndecl);
|
||||
|
||||
input_location = save_input_location;
|
||||
|
||||
@ -1753,6 +2346,13 @@ get_defaulted_eh_spec (tree decl, tsubst_flags_t complain)
|
||||
if (DECL_CLONED_FUNCTION_P (decl))
|
||||
decl = DECL_CLONED_FUNCTION (decl);
|
||||
special_function_kind sfk = special_function_p (decl);
|
||||
if (sfk == sfk_comparison)
|
||||
{
|
||||
/* We're in synthesize_method. Start with NULL_TREE, build_comparison_op
|
||||
will adjust as needed. */
|
||||
gcc_assert (decl == current_function_decl);
|
||||
return NULL_TREE;
|
||||
}
|
||||
tree ctype = DECL_CONTEXT (decl);
|
||||
tree parms = FUNCTION_FIRST_USER_PARMTYPE (decl);
|
||||
tree parm_type = TREE_VALUE (parms);
|
||||
@ -1836,7 +2436,14 @@ maybe_explain_implicit_delete (tree decl)
|
||||
informed = true;
|
||||
}
|
||||
}
|
||||
if (!informed)
|
||||
if (!informed && sfk == sfk_comparison)
|
||||
{
|
||||
inform (DECL_SOURCE_LOCATION (decl),
|
||||
"%q#D is implicitly deleted because the default "
|
||||
"definition would be ill-formed:", decl);
|
||||
build_comparison_op (decl, tf_warning_or_error);
|
||||
}
|
||||
else if (!informed)
|
||||
{
|
||||
tree parms = FUNCTION_FIRST_USER_PARMTYPE (decl);
|
||||
bool const_p = false;
|
||||
@ -1891,10 +2498,18 @@ explain_implicit_non_constexpr (tree decl)
|
||||
bool const_p = CP_TYPE_CONST_P (non_reference (TREE_VALUE (parms)));
|
||||
tree inh = DECL_INHERITED_CTOR (decl);
|
||||
bool dummy;
|
||||
synthesized_method_walk (DECL_CLASS_CONTEXT (decl),
|
||||
special_function_p (decl), const_p,
|
||||
NULL, NULL, NULL, &dummy, true,
|
||||
&inh, parms);
|
||||
special_function_kind sfk = special_function_p (decl);
|
||||
if (sfk == sfk_comparison)
|
||||
{
|
||||
DECL_DECLARED_CONSTEXPR_P (decl) = true;
|
||||
build_comparison_op (decl, tf_warning_or_error);
|
||||
DECL_DECLARED_CONSTEXPR_P (decl) = false;
|
||||
}
|
||||
else
|
||||
synthesized_method_walk (DECL_CLASS_CONTEXT (decl),
|
||||
sfk, const_p,
|
||||
NULL, NULL, NULL, &dummy, true,
|
||||
&inh, parms);
|
||||
}
|
||||
|
||||
/* DECL is an instantiation of an inheriting constructor template. Deduce
|
||||
@ -1933,12 +2548,12 @@ deduce_inheriting_ctor (tree decl)
|
||||
/* Implicitly declare the special function indicated by KIND, as a
|
||||
member of TYPE. For copy constructors and assignment operators,
|
||||
CONST_P indicates whether these functions should take a const
|
||||
reference argument or a non-const reference. Returns the
|
||||
FUNCTION_DECL for the implicitly declared function. */
|
||||
reference argument or a non-const reference.
|
||||
Returns the FUNCTION_DECL for the implicitly declared function. */
|
||||
|
||||
tree
|
||||
implicitly_declare_fn (special_function_kind kind, tree type,
|
||||
bool const_p, tree inherited_ctor,
|
||||
bool const_p, tree pattern_fn,
|
||||
tree inherited_parms)
|
||||
{
|
||||
tree fn;
|
||||
@ -1950,8 +2565,11 @@ implicitly_declare_fn (special_function_kind kind, tree type,
|
||||
tree this_parm;
|
||||
tree name;
|
||||
HOST_WIDE_INT saved_processing_template_decl;
|
||||
bool deleted_p;
|
||||
bool constexpr_p;
|
||||
bool deleted_p = false;
|
||||
bool constexpr_p = false;
|
||||
bool friend_p = (kind == sfk_comparison && DECL_FRIEND_P (pattern_fn));
|
||||
tree inherited_ctor = (kind == sfk_inheriting_constructor
|
||||
? pattern_fn : NULL_TREE);
|
||||
|
||||
/* Because we create declarations for implicitly declared functions
|
||||
lazily, we may be creating the declaration for a member of TYPE
|
||||
@ -1978,6 +2596,7 @@ implicitly_declare_fn (special_function_kind kind, tree type,
|
||||
else
|
||||
return_type = void_type_node;
|
||||
|
||||
int this_quals = TYPE_UNQUALIFIED;
|
||||
switch (kind)
|
||||
{
|
||||
case sfk_destructor:
|
||||
@ -2021,6 +2640,36 @@ implicitly_declare_fn (special_function_kind kind, tree type,
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case sfk_comparison:
|
||||
/* If the class definition does not explicitly declare an == operator
|
||||
function, but declares a defaulted three-way comparison operator
|
||||
function, an == operator function is declared implicitly with the same
|
||||
access as the three-way comparison operator function.
|
||||
|
||||
The implicitly-declared == operator for a class X is an inline member
|
||||
and is defined as defaulted in the definition of X.
|
||||
|
||||
If the three-way comparison operator function is declared as a
|
||||
non-static const member, the implicitly-declared == operator function
|
||||
is a member of the form
|
||||
|
||||
bool X::operator==(const X&) const;
|
||||
|
||||
Otherwise, the implicitly-declared == operator function is of the form
|
||||
|
||||
friend bool operator==(const X&, const X&); */
|
||||
/* No other comparison operator is implicitly declared. */
|
||||
name = ovl_op_identifier (false, EQ_EXPR);
|
||||
return_type = boolean_type_node;
|
||||
rhs_parm_type = cp_build_qualified_type (type, TYPE_QUAL_CONST);
|
||||
rhs_parm_type = cp_build_reference_type (rhs_parm_type, false);
|
||||
parameter_types = tree_cons (NULL_TREE, rhs_parm_type, parameter_types);
|
||||
if (friend_p)
|
||||
parameter_types = tree_cons (NULL_TREE, rhs_parm_type, parameter_types);
|
||||
this_quals = TYPE_QUAL_CONST;
|
||||
break;
|
||||
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
}
|
||||
@ -2038,9 +2687,10 @@ implicitly_declare_fn (special_function_kind kind, tree type,
|
||||
else if (cxx_dialect >= cxx11)
|
||||
{
|
||||
raises = noexcept_deferred_spec;
|
||||
synthesized_method_walk (type, kind, const_p, NULL, &trivial_p,
|
||||
&deleted_p, &constexpr_p, false,
|
||||
&inherited_ctor, inherited_parms);
|
||||
if (kind != sfk_comparison)
|
||||
synthesized_method_walk (type, kind, const_p, NULL, &trivial_p,
|
||||
&deleted_p, &constexpr_p, false,
|
||||
&inherited_ctor, inherited_parms);
|
||||
}
|
||||
else
|
||||
synthesized_method_walk (type, kind, const_p, &raises, &trivial_p,
|
||||
@ -2062,7 +2712,9 @@ implicitly_declare_fn (special_function_kind kind, tree type,
|
||||
type_set_nontrivial_flag (type, kind);
|
||||
|
||||
/* Create the function. */
|
||||
fn_type = build_method_type_directly (type, return_type, parameter_types);
|
||||
tree this_type = cp_build_qualified_type (type, this_quals);
|
||||
fn_type = build_method_type_directly (this_type, return_type,
|
||||
parameter_types);
|
||||
if (raises)
|
||||
{
|
||||
if (raises != error_mark_node)
|
||||
@ -2073,16 +2725,25 @@ implicitly_declare_fn (special_function_kind kind, tree type,
|
||||
gcc_assert (seen_error ());
|
||||
}
|
||||
fn = build_lang_decl (FUNCTION_DECL, name, fn_type);
|
||||
if (kind != sfk_inheriting_constructor)
|
||||
if (kind == sfk_comparison)
|
||||
{
|
||||
DECL_SOURCE_LOCATION (fn) = DECL_SOURCE_LOCATION (pattern_fn);
|
||||
DECL_MAYBE_DELETED (fn) = true;
|
||||
}
|
||||
else if (kind != sfk_inheriting_constructor)
|
||||
DECL_SOURCE_LOCATION (fn) = DECL_SOURCE_LOCATION (TYPE_NAME (type));
|
||||
|
||||
if (!IDENTIFIER_CDTOR_P (name))
|
||||
/* Assignment operator. */
|
||||
DECL_OVERLOADED_OPERATOR_CODE_RAW (fn) = OVL_OP_NOP_EXPR;
|
||||
if (IDENTIFIER_OVL_OP_P (name))
|
||||
{
|
||||
const ovl_op_info_t *op = IDENTIFIER_OVL_OP_INFO (name);
|
||||
DECL_OVERLOADED_OPERATOR_CODE_RAW (fn) = op->ovl_op_code;
|
||||
}
|
||||
else if (IDENTIFIER_CTOR_P (name))
|
||||
DECL_CXX_CONSTRUCTOR_P (fn) = true;
|
||||
else
|
||||
else if (IDENTIFIER_DTOR_P (name))
|
||||
DECL_CXX_DESTRUCTOR_P (fn) = true;
|
||||
else
|
||||
gcc_unreachable ();
|
||||
|
||||
SET_DECL_ALIGN (fn, MINIMUM_METHOD_BOUNDARY);
|
||||
|
||||
@ -2097,6 +2758,13 @@ implicitly_declare_fn (special_function_kind kind, tree type,
|
||||
retrofit_lang_decl (decl);
|
||||
DECL_PARM_INDEX (decl) = DECL_PARM_LEVEL (decl) = 1;
|
||||
DECL_ARGUMENTS (fn) = decl;
|
||||
if (friend_p)
|
||||
{
|
||||
/* The second parm of friend op==. */
|
||||
tree decl2 = copy_decl (decl);
|
||||
DECL_CHAIN (decl) = decl2;
|
||||
DECL_PARM_INDEX (decl2) = 2;
|
||||
}
|
||||
}
|
||||
else if (kind == sfk_inheriting_constructor)
|
||||
{
|
||||
@ -2122,7 +2790,7 @@ implicitly_declare_fn (special_function_kind kind, tree type,
|
||||
constexpr_p = DECL_DECLARED_CONSTEXPR_P (inherited_ctor);
|
||||
}
|
||||
/* Add the "this" parameter. */
|
||||
this_parm = build_this_parm (fn, fn_type, TYPE_UNQUALIFIED);
|
||||
this_parm = build_this_parm (fn, fn_type, this_quals);
|
||||
DECL_CHAIN (this_parm) = DECL_ARGUMENTS (fn);
|
||||
DECL_ARGUMENTS (fn) = this_parm;
|
||||
|
||||
@ -2141,6 +2809,12 @@ implicitly_declare_fn (special_function_kind kind, tree type,
|
||||
set_linkage_according_to_type (type, fn);
|
||||
if (TREE_PUBLIC (fn))
|
||||
DECL_COMDAT (fn) = 1;
|
||||
if (kind == sfk_comparison && !friend_p)
|
||||
{
|
||||
/* The implicit op== has the same access as the op<=>. */
|
||||
TREE_PRIVATE (fn) = TREE_PRIVATE (pattern_fn);
|
||||
TREE_PROTECTED (fn) = TREE_PROTECTED (pattern_fn);
|
||||
}
|
||||
rest_of_decl_compilation (fn, namespace_bindings_p (), at_eof);
|
||||
gcc_assert (!TREE_USED (fn));
|
||||
|
||||
@ -2182,6 +2856,16 @@ defaulted_late_check (tree fn)
|
||||
/* Complain about invalid signature for defaulted fn. */
|
||||
tree ctx = DECL_CONTEXT (fn);
|
||||
special_function_kind kind = special_function_p (fn);
|
||||
|
||||
if (kind == sfk_comparison)
|
||||
{
|
||||
/* If the function was declared constexpr, check that the definition
|
||||
qualifies. Otherwise we can define the function lazily. */
|
||||
if (DECL_DECLARED_CONSTEXPR_P (fn))
|
||||
synthesize_method (fn);
|
||||
return;
|
||||
}
|
||||
|
||||
bool fn_const_p = (copy_fn_p (fn) == 2);
|
||||
tree implicit_fn = implicitly_declare_fn (kind, ctx, fn_const_p,
|
||||
NULL, NULL);
|
||||
@ -2272,6 +2956,13 @@ defaultable_fn_check (tree fn)
|
||||
else if (move_fn_p (fn))
|
||||
kind = sfk_move_assignment;
|
||||
}
|
||||
else if (DECL_OVERLOADED_OPERATOR_CODE_RAW (fn) >= OVL_OP_EQ_EXPR
|
||||
&& DECL_OVERLOADED_OPERATOR_CODE_RAW (fn) <= OVL_OP_SPACESHIP_EXPR)
|
||||
{
|
||||
kind = sfk_comparison;
|
||||
if (!early_check_defaulted_comparison (fn))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (kind == sfk_none)
|
||||
{
|
||||
@ -2293,7 +2984,7 @@ defaultable_fn_check (tree fn)
|
||||
if (DECL_NAME (p))
|
||||
TREE_NO_WARNING (p) = 1;
|
||||
|
||||
if (TYPE_BEING_DEFINED (DECL_CONTEXT (fn)))
|
||||
if (current_class_type && TYPE_BEING_DEFINED (current_class_type))
|
||||
/* Defer checking. */;
|
||||
else if (!processing_template_decl)
|
||||
defaulted_late_check (fn);
|
||||
|
@ -5592,6 +5592,12 @@ get_std_name_hint (const char *name)
|
||||
{"atomic_ref", "<atomic>", cxx2a},
|
||||
/* <bitset>. */
|
||||
{"bitset", "<bitset>", cxx11},
|
||||
/* <compare> */
|
||||
{"weak_equality", "<compare>", cxx2a},
|
||||
{"strong_equality", "<compare>", cxx2a},
|
||||
{"partial_ordering", "<compare>", cxx2a},
|
||||
{"weak_ordering", "<compare>", cxx2a},
|
||||
{"strong_ordering", "<compare>", cxx2a},
|
||||
/* <complex>. */
|
||||
{"complex", "<complex>", cxx98},
|
||||
{"complex_literals", "<complex>", cxx14},
|
||||
|
@ -104,12 +104,16 @@ DEF_OPERATOR ("|", BIT_IOR_EXPR, "or", OVL_OP_FLAG_BINARY)
|
||||
DEF_OPERATOR ("^", BIT_XOR_EXPR, "eo", OVL_OP_FLAG_BINARY)
|
||||
DEF_OPERATOR ("<<", LSHIFT_EXPR, "ls", OVL_OP_FLAG_BINARY)
|
||||
DEF_OPERATOR (">>", RSHIFT_EXPR, "rs", OVL_OP_FLAG_BINARY)
|
||||
|
||||
/* defaultable_fn_check relies on the ordering of the comparison operators. */
|
||||
DEF_OPERATOR ("==", EQ_EXPR, "eq", OVL_OP_FLAG_BINARY)
|
||||
DEF_OPERATOR ("!=", NE_EXPR, "ne", OVL_OP_FLAG_BINARY)
|
||||
DEF_OPERATOR ("<", LT_EXPR, "lt", OVL_OP_FLAG_BINARY)
|
||||
DEF_OPERATOR (">", GT_EXPR, "gt", OVL_OP_FLAG_BINARY)
|
||||
DEF_OPERATOR ("<=", LE_EXPR, "le", OVL_OP_FLAG_BINARY)
|
||||
DEF_OPERATOR (">=", GE_EXPR, "ge", OVL_OP_FLAG_BINARY)
|
||||
DEF_OPERATOR ("<=>", SPACESHIP_EXPR, "ss", OVL_OP_FLAG_BINARY)
|
||||
|
||||
DEF_OPERATOR ("&&", TRUTH_ANDIF_EXPR, "aa", OVL_OP_FLAG_BINARY)
|
||||
DEF_OPERATOR ("||", TRUTH_ORIF_EXPR, "oo", OVL_OP_FLAG_BINARY)
|
||||
DEF_OPERATOR (",", COMPOUND_EXPR, "cm", OVL_OP_FLAG_BINARY)
|
||||
|
@ -1850,6 +1850,7 @@ enum cp_parser_prec
|
||||
PREC_AND_EXPRESSION,
|
||||
PREC_EQUALITY_EXPRESSION,
|
||||
PREC_RELATIONAL_EXPRESSION,
|
||||
PREC_SPACESHIP_EXPRESSION,
|
||||
PREC_SHIFT_EXPRESSION,
|
||||
PREC_ADDITIVE_EXPRESSION,
|
||||
PREC_MULTIPLICATIVE_EXPRESSION,
|
||||
@ -1921,6 +1922,8 @@ static const cp_parser_binary_operations_map_node binops[] = {
|
||||
{ CPP_LSHIFT, LSHIFT_EXPR, PREC_SHIFT_EXPRESSION },
|
||||
{ CPP_RSHIFT, RSHIFT_EXPR, PREC_SHIFT_EXPRESSION },
|
||||
|
||||
{ CPP_SPACESHIP, SPACESHIP_EXPR, PREC_SPACESHIP_EXPRESSION },
|
||||
|
||||
{ CPP_LESS, LT_EXPR, PREC_RELATIONAL_EXPRESSION },
|
||||
{ CPP_GREATER, GT_EXPR, PREC_RELATIONAL_EXPRESSION },
|
||||
{ CPP_LESS_EQ, LE_EXPR, PREC_RELATIONAL_EXPRESSION },
|
||||
@ -15507,6 +15510,10 @@ cp_parser_operator (cp_parser* parser, location_t start_loc)
|
||||
op = GE_EXPR;
|
||||
break;
|
||||
|
||||
case CPP_SPACESHIP:
|
||||
op = SPACESHIP_EXPR;
|
||||
break;
|
||||
|
||||
case CPP_AND_AND:
|
||||
op = TRUTH_ANDIF_EXPR;
|
||||
break;
|
||||
|
@ -19003,6 +19003,7 @@ tsubst_copy_and_build (tree t,
|
||||
case GE_EXPR:
|
||||
case LT_EXPR:
|
||||
case GT_EXPR:
|
||||
case SPACESHIP_EXPR:
|
||||
case MEMBER_REF:
|
||||
case DOTSTAR_EXPR:
|
||||
{
|
||||
|
@ -5042,6 +5042,9 @@ special_function_p (const_tree decl)
|
||||
return sfk_conversion;
|
||||
if (deduction_guide_p (decl))
|
||||
return sfk_deduction_guide;
|
||||
if (DECL_OVERLOADED_OPERATOR_CODE_RAW (decl) >= OVL_OP_EQ_EXPR
|
||||
&& DECL_OVERLOADED_OPERATOR_CODE_RAW (decl) <= OVL_OP_SPACESHIP_EXPR)
|
||||
return sfk_comparison;
|
||||
|
||||
return sfk_none;
|
||||
}
|
||||
|
@ -4889,6 +4889,7 @@ cp_build_binary_op (const op_location_t &location,
|
||||
|
||||
case EQ_EXPR:
|
||||
case NE_EXPR:
|
||||
case SPACESHIP_EXPR:
|
||||
if (code0 == VECTOR_TYPE && code1 == VECTOR_TYPE)
|
||||
goto vector_compare;
|
||||
if ((complain & tf_warning)
|
||||
@ -4965,7 +4966,9 @@ cp_build_binary_op (const op_location_t &location,
|
||||
warn_for_null_address (location, op1, complain);
|
||||
}
|
||||
else if ((code0 == POINTER_TYPE && code1 == POINTER_TYPE)
|
||||
|| (TYPE_PTRDATAMEM_P (type0) && TYPE_PTRDATAMEM_P (type1)))
|
||||
|| (code == SPACESHIP_EXPR
|
||||
? TYPE_PTRMEM_P (type0) && TYPE_PTRMEM_P (type1)
|
||||
: TYPE_PTRDATAMEM_P (type0) && TYPE_PTRDATAMEM_P (type1)))
|
||||
result_type = composite_pointer_type (location,
|
||||
type0, type1, op0, op1,
|
||||
CPO_COMPARISON, complain);
|
||||
@ -5358,6 +5361,55 @@ cp_build_binary_op (const op_location_t &location,
|
||||
location);
|
||||
}
|
||||
|
||||
if (code == SPACESHIP_EXPR)
|
||||
{
|
||||
iloc_sentinel s (location);
|
||||
|
||||
tree orig_type0 = TREE_TYPE (orig_op0);
|
||||
tree_code orig_code0 = TREE_CODE (orig_type0);
|
||||
tree orig_type1 = TREE_TYPE (orig_op1);
|
||||
tree_code orig_code1 = TREE_CODE (orig_type1);
|
||||
if ((orig_code0 == BOOLEAN_TYPE) != (orig_code1 == BOOLEAN_TYPE))
|
||||
/* "If one of the operands is of type bool and the other is not, the
|
||||
program is ill-formed." */
|
||||
result_type = NULL_TREE;
|
||||
else if (code0 == POINTER_TYPE && orig_code0 != POINTER_TYPE
|
||||
&& code1 == POINTER_TYPE && orig_code1 != POINTER_TYPE)
|
||||
/* We only do array/function-to-pointer conversion if "at least one of
|
||||
the operands is of pointer type". */
|
||||
result_type = NULL_TREE;
|
||||
else if (orig_code0 == ENUMERAL_TYPE && orig_code1 == ENUMERAL_TYPE
|
||||
&& !(same_type_ignoring_top_level_qualifiers_p
|
||||
(orig_type0, orig_type1)))
|
||||
/* "If both operands have arithmetic types, or one operand has integral
|
||||
type and the other operand has unscoped enumeration type, the usual
|
||||
arithmetic conversions are applied to the operands." So we don't do
|
||||
arithmetic conversions if the operands both have enumeral type. */
|
||||
result_type = NULL_TREE;
|
||||
|
||||
if (result_type)
|
||||
build_type = spaceship_type (result_type, complain);
|
||||
|
||||
if (result_type && arithmetic_types_p)
|
||||
{
|
||||
/* If a narrowing conversion is required, other than from an integral
|
||||
type to a floating point type, the program is ill-formed. */
|
||||
bool ok = true;
|
||||
if (TREE_CODE (result_type) == REAL_TYPE
|
||||
&& INTEGRAL_OR_ENUMERATION_TYPE_P (TREE_TYPE (orig_op0)))
|
||||
/* OK */;
|
||||
else if (!check_narrowing (result_type, orig_op0, complain))
|
||||
ok = false;
|
||||
if (TREE_CODE (result_type) == REAL_TYPE
|
||||
&& INTEGRAL_OR_ENUMERATION_TYPE_P (TREE_TYPE (orig_op1)))
|
||||
/* OK */;
|
||||
else if (!check_narrowing (result_type, orig_op1, complain))
|
||||
ok = false;
|
||||
if (!ok && !(complain & tf_error))
|
||||
return error_mark_node;
|
||||
}
|
||||
}
|
||||
|
||||
if (!result_type)
|
||||
{
|
||||
if (complain & tf_error)
|
||||
|
6
gcc/testsuite/c-c++-common/cpp/spaceship-1.c
Normal file
6
gcc/testsuite/c-c++-common/cpp/spaceship-1.c
Normal file
@ -0,0 +1,6 @@
|
||||
/* { dg-do preprocess } */
|
||||
/* { dg-options "-std=c11" { target c } } */
|
||||
|
||||
#define A(x, y) x##y
|
||||
A(<=, >) /* { dg-error "does not give a valid preprocessing token" "" { target { ! c++2a } } } */
|
||||
A(<=>, >) /* { dg-error "does not give a valid preprocessing token" "" { target c++2a } } */
|
8
gcc/testsuite/g++.dg/cpp/spaceship-1.C
Normal file
8
gcc/testsuite/g++.dg/cpp/spaceship-1.C
Normal file
@ -0,0 +1,8 @@
|
||||
// { dg-do compile { target c++17_down } }
|
||||
// { dg-options "-Wno-pointer-arith" }
|
||||
|
||||
struct X {};
|
||||
bool operator<= (X, X);
|
||||
template<bool (X, X)> struct Y {};
|
||||
Y<&operator<=> y;
|
||||
bool foo (bool (*fn) (X, X), int n) { return n+&operator<=> fn; }
|
15
gcc/testsuite/g++.dg/cpp2a/spaceship-constexpr1.C
Normal file
15
gcc/testsuite/g++.dg/cpp2a/spaceship-constexpr1.C
Normal file
@ -0,0 +1,15 @@
|
||||
// { dg-do compile { target c++2a } }
|
||||
|
||||
struct A
|
||||
{
|
||||
int i;
|
||||
bool operator==(A a) const { return i == a.i; }
|
||||
};
|
||||
|
||||
struct B
|
||||
{
|
||||
A a;
|
||||
bool operator==(const B&) const = default; // { dg-error "A::operator==" }
|
||||
};
|
||||
|
||||
constexpr bool x = B() == B(); // { dg-error "non-.constexpr" }
|
17
gcc/testsuite/g++.dg/cpp2a/spaceship-eq1.C
Normal file
17
gcc/testsuite/g++.dg/cpp2a/spaceship-eq1.C
Normal file
@ -0,0 +1,17 @@
|
||||
// { dg-do run { target c++2a } }
|
||||
|
||||
struct D
|
||||
{
|
||||
int i;
|
||||
bool operator==(const D& x) const = default; // OK, returns x.i == y.i
|
||||
bool operator!=(const D& z) const = default; // OK, returns !(*this == z)
|
||||
};
|
||||
|
||||
#define assert(X) do { if (!(X)) __builtin_abort(); } while (0)
|
||||
|
||||
int main()
|
||||
{
|
||||
D d{42};
|
||||
assert (d == d);
|
||||
assert (!(d != d));
|
||||
}
|
24
gcc/testsuite/g++.dg/cpp2a/spaceship-eq1a.C
Normal file
24
gcc/testsuite/g++.dg/cpp2a/spaceship-eq1a.C
Normal file
@ -0,0 +1,24 @@
|
||||
// { dg-do run { target c++2a } }
|
||||
|
||||
template <class T>
|
||||
struct D
|
||||
{
|
||||
T i;
|
||||
bool operator==(const D& x) const = default; // OK, returns x.i == y.i
|
||||
bool operator!=(const D& z) const = default; // OK, returns !(*this == z)
|
||||
};
|
||||
|
||||
#define assert(X) do { if (!(X)) __builtin_abort(); } while (0)
|
||||
|
||||
template <class T>
|
||||
void f()
|
||||
{
|
||||
D<T> d{42};
|
||||
assert (d == d);
|
||||
assert (!(d != d));
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
f<int>();
|
||||
}
|
12
gcc/testsuite/g++.dg/cpp2a/spaceship-eq2.C
Normal file
12
gcc/testsuite/g++.dg/cpp2a/spaceship-eq2.C
Normal file
@ -0,0 +1,12 @@
|
||||
// { dg-do compile { target c++2a } }
|
||||
|
||||
struct D
|
||||
{
|
||||
int i;
|
||||
bool operator==(const D& x) const = default; // OK, returns x.i == y.i
|
||||
bool operator!=(const D& z) const = default; // OK, returns !(*this == z)
|
||||
};
|
||||
|
||||
constexpr D d{42};
|
||||
static_assert (d == d);
|
||||
static_assert (!(d != d));
|
16
gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C
Normal file
16
gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C
Normal file
@ -0,0 +1,16 @@
|
||||
// { dg-do compile { target c++2a } }
|
||||
|
||||
struct A {
|
||||
bool operator==(const A&) const;
|
||||
};
|
||||
|
||||
struct D
|
||||
{
|
||||
A i;
|
||||
bool operator==(const D& x) const = default; // { dg-error "A::operator==" }
|
||||
bool operator!=(const D& z) const = default; // { dg-error "D::operator==" }
|
||||
};
|
||||
|
||||
constexpr D d{A()};
|
||||
static_assert (d == d); // { dg-error "non-constant|constexpr" }
|
||||
static_assert (!(d != d)); // { dg-error "non-constant|constexpr" }
|
8
gcc/testsuite/g++.dg/cpp2a/spaceship-eq4.C
Normal file
8
gcc/testsuite/g++.dg/cpp2a/spaceship-eq4.C
Normal file
@ -0,0 +1,8 @@
|
||||
// { dg-do compile { target c++2a } }
|
||||
|
||||
struct A {
|
||||
int operator==(const A&) const = default; // { dg-error "return .bool" }
|
||||
bool operator==(const A&, const A&) const = default; // { dg-error "exactly one" }
|
||||
bool operator==(int) const = default; // { dg-error "parameter type" }
|
||||
bool operator==(const A&) = default; // { dg-error "const" }
|
||||
};
|
10
gcc/testsuite/g++.dg/cpp2a/spaceship-eq5.C
Normal file
10
gcc/testsuite/g++.dg/cpp2a/spaceship-eq5.C
Normal file
@ -0,0 +1,10 @@
|
||||
// { dg-do compile { target c++2a } }
|
||||
|
||||
struct A {
|
||||
int &r; // { dg-message "reference" }
|
||||
bool operator==(const A&) const = default; // { dg-message "deleted" }
|
||||
};
|
||||
|
||||
int i;
|
||||
A a { i };
|
||||
bool b = a == a; // { dg-error "deleted" }
|
10
gcc/testsuite/g++.dg/cpp2a/spaceship-eq6.C
Normal file
10
gcc/testsuite/g++.dg/cpp2a/spaceship-eq6.C
Normal file
@ -0,0 +1,10 @@
|
||||
// { dg-do compile { target c++2a } }
|
||||
|
||||
struct A
|
||||
{
|
||||
union { int i; } // { dg-message "union" }
|
||||
bool operator==(const A&) const = default; // { dg-message "deleted" }
|
||||
};
|
||||
|
||||
A a { 42 };
|
||||
bool b = a == a; // { dg-error "deleted" }
|
10
gcc/testsuite/g++.dg/cpp2a/spaceship-eq7.C
Normal file
10
gcc/testsuite/g++.dg/cpp2a/spaceship-eq7.C
Normal file
@ -0,0 +1,10 @@
|
||||
// { dg-do compile { target c++2a } }
|
||||
|
||||
union A
|
||||
{
|
||||
int i;
|
||||
bool operator==(const A&) const = default; // { dg-message "union" }
|
||||
};
|
||||
|
||||
A a { 42 };
|
||||
bool b = a == a; // { dg-error "deleted" }
|
5
gcc/testsuite/g++.dg/cpp2a/spaceship-err1.C
Normal file
5
gcc/testsuite/g++.dg/cpp2a/spaceship-err1.C
Normal file
@ -0,0 +1,5 @@
|
||||
// Test that we suggest adding #include <compare>.
|
||||
// { dg-do compile { target c++2a } }
|
||||
|
||||
auto x = 1<=>2; // { dg-error "" }
|
||||
// { dg-message "<compare>" "" { target *-*-* } .-1 }
|
7
gcc/testsuite/g++.dg/cpp2a/spaceship-err2.C
Normal file
7
gcc/testsuite/g++.dg/cpp2a/spaceship-err2.C
Normal file
@ -0,0 +1,7 @@
|
||||
// { dg-do compile { target c++2a } }
|
||||
|
||||
#include <compare>
|
||||
template <class T, T x = (T() <=> T())> // { dg-error "31:0 <=> 0" }
|
||||
void f(T);
|
||||
//constexpr int f(...) { return 42; }
|
||||
constexpr int i = f(24); // { dg-error "no match" }
|
15
gcc/testsuite/g++.dg/cpp2a/spaceship-rewrite1.C
Normal file
15
gcc/testsuite/g++.dg/cpp2a/spaceship-rewrite1.C
Normal file
@ -0,0 +1,15 @@
|
||||
// This should continue to work.
|
||||
// { dg-do compile { target c++2a } }
|
||||
|
||||
template<class T>
|
||||
struct A {
|
||||
template<class U>
|
||||
bool operator==(const A<U>&);
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
A<int> a1;
|
||||
A<void> a2;
|
||||
return a1 == a2;
|
||||
}
|
93
gcc/testsuite/g++.dg/cpp2a/spaceship-scalar1.C
Normal file
93
gcc/testsuite/g++.dg/cpp2a/spaceship-scalar1.C
Normal file
@ -0,0 +1,93 @@
|
||||
// { dg-do run { target c++2a } }
|
||||
|
||||
#include <compare>
|
||||
|
||||
#define assert(X) do { if (!(X)) __builtin_abort(); } while(0)
|
||||
|
||||
void f(){}
|
||||
void g(){}
|
||||
|
||||
int main()
|
||||
{
|
||||
{
|
||||
constexpr auto v = 1 <=> 2;
|
||||
static_assert (__is_same_as (decltype (v), const std::strong_ordering));
|
||||
static_assert (!is_eq (v));
|
||||
static_assert (is_neq (v));
|
||||
static_assert (is_lt (v));
|
||||
static_assert (is_lteq (v));
|
||||
static_assert (!is_gt (v));
|
||||
static_assert (!is_gteq (v));
|
||||
}
|
||||
|
||||
{
|
||||
enum E { a = 0 };
|
||||
constexpr auto v = E::a <=> 1;
|
||||
static_assert (__is_same_as (decltype (v), const std::strong_ordering));
|
||||
static_assert (!is_eq (v));
|
||||
static_assert (is_neq (v));
|
||||
static_assert (is_lt (v));
|
||||
static_assert (is_lteq (v));
|
||||
static_assert (!is_gt (v));
|
||||
static_assert (!is_gteq (v));
|
||||
}
|
||||
|
||||
{
|
||||
enum class E { a, b };
|
||||
constexpr auto v = E::a <=> E::b;
|
||||
static_assert (__is_same_as (decltype (v), const std::strong_ordering));
|
||||
static_assert (!is_eq (v));
|
||||
static_assert (is_neq (v));
|
||||
static_assert (is_lt (v));
|
||||
static_assert (is_lteq (v));
|
||||
static_assert (!is_gt (v));
|
||||
static_assert (!is_gteq (v));
|
||||
}
|
||||
|
||||
{
|
||||
int ar[2];
|
||||
constexpr auto v = &ar[1] <=> &ar[0];
|
||||
static_assert (__is_same_as (decltype (v), const std::strong_ordering));
|
||||
static_assert (!is_eq (v));
|
||||
static_assert (is_neq (v));
|
||||
static_assert (!is_lt (v));
|
||||
static_assert (!is_lteq (v));
|
||||
static_assert (is_gt (v));
|
||||
static_assert (is_gteq (v));
|
||||
}
|
||||
|
||||
{
|
||||
constexpr auto v = 3.14 <=> 3.14;
|
||||
static_assert (__is_same_as (decltype (v), const std::partial_ordering));
|
||||
static_assert (is_eq (v));
|
||||
static_assert (!is_neq (v));
|
||||
static_assert (!is_lt (v));
|
||||
static_assert (is_lteq (v));
|
||||
static_assert (!is_gt (v));
|
||||
static_assert (is_gteq (v));
|
||||
}
|
||||
|
||||
{
|
||||
// GCC doesn't consider &f == &g to be a constant expression (PR 69681)
|
||||
const auto v = &f <=> &g;
|
||||
static_assert (__is_same_as (decltype (v), const std::strong_equality));
|
||||
assert (!is_eq (v));
|
||||
assert (is_neq (v));
|
||||
}
|
||||
|
||||
{
|
||||
struct A { int i; int j; };
|
||||
constexpr auto v = &A::i <=> &A::j;
|
||||
static_assert (__is_same_as (decltype (v), const std::strong_equality));
|
||||
static_assert (!is_eq (v));
|
||||
static_assert (is_neq (v));
|
||||
}
|
||||
|
||||
{
|
||||
struct A { void f(); };
|
||||
constexpr auto v = &A::f <=> &A::f;
|
||||
static_assert (__is_same_as (decltype (v), const std::strong_equality));
|
||||
static_assert (is_eq (v));
|
||||
static_assert (!is_neq (v));
|
||||
}
|
||||
}
|
41
gcc/testsuite/g++.dg/cpp2a/spaceship-scalar1a.C
Normal file
41
gcc/testsuite/g++.dg/cpp2a/spaceship-scalar1a.C
Normal file
@ -0,0 +1,41 @@
|
||||
// { dg-do run { target c++2a } }
|
||||
|
||||
#include <compare>
|
||||
|
||||
#define assert(X) do { if (!(X)) __builtin_abort(); } while(0)
|
||||
|
||||
void f(){}
|
||||
void g(){}
|
||||
|
||||
template <class T, class U, class R>
|
||||
constexpr bool check(T a, U b, R expected)
|
||||
{
|
||||
auto r = a <=> b;
|
||||
static_assert (__is_same_as (decltype (r), R));
|
||||
return r == expected;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
static_assert (check (1, 2, std::strong_ordering::less));
|
||||
|
||||
enum E1 { a = 0 };
|
||||
static_assert (check (a, 1, std::strong_ordering::less));
|
||||
|
||||
enum class E2 { a, b };
|
||||
static_assert (check (E2::a, E2::b, std::strong_ordering::less));
|
||||
|
||||
int ar[2];
|
||||
static_assert (check (&ar[1], &ar[0], std::strong_ordering::greater));
|
||||
|
||||
static_assert (check (3.14, 3.14, std::partial_ordering::equivalent));
|
||||
|
||||
// GCC doesn't consider &f == &g to be a constant expression (PR 69681)
|
||||
assert (check (&f, &g, std::strong_equality::nonequal));
|
||||
|
||||
struct A { int i; int j; };
|
||||
static_assert (check (&A::i, &A::j, std::strong_equality::nonequal));
|
||||
|
||||
struct A2 { void f(); };
|
||||
static_assert (check (&A2::f, &A2::f, std::strong_equality::equal));
|
||||
}
|
11
gcc/testsuite/g++.dg/cpp2a/spaceship-scalar2.C
Normal file
11
gcc/testsuite/g++.dg/cpp2a/spaceship-scalar2.C
Normal file
@ -0,0 +1,11 @@
|
||||
// { dg-do compile { target c++2a } }
|
||||
|
||||
#include <compare>
|
||||
|
||||
int main()
|
||||
{
|
||||
{ true <=> 1; } // { dg-error "bool" }
|
||||
{ int a[2]; a <=> a; } // { dg-error "2" }
|
||||
{ -1 <=> 1U; } // { dg-error "narrowing" }
|
||||
{ enum A { a }; enum B { b }; a <=> b; } // { dg-error "A" }
|
||||
}
|
21
gcc/testsuite/g++.dg/cpp2a/spaceship-scalar3.C
Normal file
21
gcc/testsuite/g++.dg/cpp2a/spaceship-scalar3.C
Normal file
@ -0,0 +1,21 @@
|
||||
// { dg-do run { target c++2a } }
|
||||
// { dg-options "-fext-numeric-literals" }
|
||||
|
||||
#include <compare>
|
||||
|
||||
int main()
|
||||
{
|
||||
// GCC complex literal extension
|
||||
{
|
||||
constexpr auto v = 1 <=> 1i;
|
||||
static_assert (__is_same_as (decltype (v), const std::strong_equality));
|
||||
static_assert (!is_eq (v));
|
||||
static_assert (is_neq (v));
|
||||
}
|
||||
{
|
||||
constexpr auto v = 1i <=> 1.0i;
|
||||
static_assert (__is_same_as (decltype (v), const std::weak_equality));
|
||||
static_assert (is_eq (v));
|
||||
static_assert (!is_neq (v));
|
||||
}
|
||||
}
|
7
gcc/testsuite/g++.dg/cpp2a/spaceship-sfinae1.C
Normal file
7
gcc/testsuite/g++.dg/cpp2a/spaceship-sfinae1.C
Normal file
@ -0,0 +1,7 @@
|
||||
// { dg-do compile { target c++2a } }
|
||||
|
||||
// missing #include <compare>
|
||||
template <class T, T x = (T() <=> T()) == 0>
|
||||
void f(T);
|
||||
constexpr int f(...) { return 42; }
|
||||
constexpr int i = f(24);
|
43
gcc/testsuite/g++.dg/cpp2a/spaceship-synth1.C
Normal file
43
gcc/testsuite/g++.dg/cpp2a/spaceship-synth1.C
Normal file
@ -0,0 +1,43 @@
|
||||
// Test with all operators explicitly defaulted.
|
||||
// { dg-do run { target c++2a } }
|
||||
|
||||
#include <compare>
|
||||
|
||||
struct D
|
||||
{
|
||||
int i;
|
||||
auto operator<=>(const D& x) const = default;
|
||||
bool operator==(const D& x) const = default;
|
||||
bool operator!=(const D& x) const = default;
|
||||
bool operator<(const D& x) const = default;
|
||||
bool operator<=(const D& x) const = default;
|
||||
bool operator>(const D& x) const = default;
|
||||
bool operator>=(const D& x) const = default;
|
||||
};
|
||||
|
||||
#define assert(X) do { if (!(X)) __builtin_abort(); } while (0)
|
||||
|
||||
int main()
|
||||
{
|
||||
D d{42};
|
||||
D d2{24};
|
||||
|
||||
assert (is_eq (d <=> d));
|
||||
assert (is_lteq (d <=> d));
|
||||
assert (is_gteq (d <=> d));
|
||||
assert (is_lt (d2 <=> d));
|
||||
assert (is_lteq (d2 <=> d));
|
||||
assert (is_gt (d <=> d2));
|
||||
assert (is_gteq (d <=> d2));
|
||||
|
||||
assert (d == d);
|
||||
assert (!(d2 == d));
|
||||
assert (!(d == d2));
|
||||
assert (d != d2);
|
||||
assert (!(d2 != d2));
|
||||
|
||||
assert (d2 < d);
|
||||
assert (d2 <= d);
|
||||
assert (d > d2);
|
||||
assert (d >= d2);
|
||||
}
|
113
gcc/testsuite/g++.dg/cpp2a/spaceship-synth1a.C
Normal file
113
gcc/testsuite/g++.dg/cpp2a/spaceship-synth1a.C
Normal file
@ -0,0 +1,113 @@
|
||||
// Test with all operators explicitly defaulted.
|
||||
// { dg-do run { target c++2a } }
|
||||
|
||||
#include <compare>
|
||||
|
||||
template <class T>
|
||||
struct D
|
||||
{
|
||||
T i;
|
||||
auto operator<=>(const D& x) const = default;
|
||||
bool operator==(const D& x) const = default;
|
||||
bool operator!=(const D& x) const = default;
|
||||
bool operator<(const D& x) const = default;
|
||||
bool operator<=(const D& x) const = default;
|
||||
bool operator>(const D& x) const = default;
|
||||
bool operator>=(const D& x) const = default;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct E
|
||||
{
|
||||
T i;
|
||||
auto operator<=>(const E& x) const = default;
|
||||
// auto operator==(const E& x) const = default;
|
||||
// auto operator!=(const E& x) const = default;
|
||||
// auto operator<(const E& x) const = default;
|
||||
// auto operator<=(const E& x) const = default;
|
||||
// auto operator>(const E& x) const = default;
|
||||
// auto operator>=(const E& x) const = default;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct F
|
||||
{
|
||||
T i;
|
||||
constexpr auto operator<=>(T x) const { return i<=>x; }
|
||||
constexpr bool operator== (T x) const { return i==x; }
|
||||
};
|
||||
|
||||
#define assert(X) do { if (!(X)) __builtin_abort(); } while (0)
|
||||
|
||||
template <class T, class U>
|
||||
constexpr bool check_eq (T d, U d2)
|
||||
{
|
||||
return is_eq (d <=> d2)
|
||||
&& is_eq (d2 <=> d)
|
||||
&& is_lteq (d <=> d2)
|
||||
&& is_lteq (d2 <=> d)
|
||||
&& !is_lt (d <=> d2)
|
||||
&& !is_lt (d2 <=> d)
|
||||
&& is_gteq (d <=> d2)
|
||||
&& is_gteq (d2 <=> d)
|
||||
&& !is_gt (d <=> d2)
|
||||
&& !is_gt (d2 <=> d)
|
||||
&& d == d2
|
||||
&& d2 == d
|
||||
&& !(d != d2)
|
||||
&& !(d2 != d)
|
||||
&& d >= d2
|
||||
&& d <= d2
|
||||
&& d2 >= d
|
||||
&& d2 <= d
|
||||
&& !(d < d2)
|
||||
&& !(d2 < d)
|
||||
&& !(d > d2)
|
||||
&& !(d2 > d);
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
constexpr bool check_less (T d, U d2)
|
||||
{
|
||||
return !is_eq (d <=> d2)
|
||||
&& !is_eq (d2 <=> d)
|
||||
&& is_lteq (d <=> d2)
|
||||
&& !is_lteq (d2 <=> d)
|
||||
&& is_lt (d <=> d2)
|
||||
&& !is_lt (d2 <=> d)
|
||||
&& !is_gteq (d <=> d2)
|
||||
&& is_gteq (d2 <=> d)
|
||||
&& !is_gt (d <=> d2)
|
||||
&& is_gt (d2 <=> d)
|
||||
&& !(d == d2)
|
||||
&& !(d2 == d)
|
||||
&& (d != d2)
|
||||
&& (d2 != d)
|
||||
&& !(d >= d2)
|
||||
&& (d <= d2)
|
||||
&& (d2 >= d)
|
||||
&& !(d2 <= d)
|
||||
&& (d < d2)
|
||||
&& !(d2 < d)
|
||||
&& !(d > d2)
|
||||
&& (d2 > d);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
constexpr D<int> d{42};
|
||||
constexpr D<int> d2{24};
|
||||
|
||||
static_assert (check_eq (d, d));
|
||||
static_assert (check_less (d2, d));
|
||||
|
||||
constexpr E<float> e { 3.14 };
|
||||
constexpr E<float> ee { 2.72 };
|
||||
static_assert (check_eq (e, e));
|
||||
static_assert (check_less (ee, e));
|
||||
|
||||
constexpr F<char> f { 'b' };
|
||||
static_assert (check_eq (f, 'b'));
|
||||
static_assert (check_less (f, 'c'));
|
||||
static_assert (check_less ('a', f));
|
||||
}
|
43
gcc/testsuite/g++.dg/cpp2a/spaceship-synth2.C
Normal file
43
gcc/testsuite/g++.dg/cpp2a/spaceship-synth2.C
Normal file
@ -0,0 +1,43 @@
|
||||
// Test with only spaceship defaulted.
|
||||
// { dg-do run { target c++2a } }
|
||||
|
||||
#include <compare>
|
||||
|
||||
struct D
|
||||
{
|
||||
int i;
|
||||
auto operator<=>(const D& x) const = default;
|
||||
// auto operator==(const D& x) const = default;
|
||||
// auto operator!=(const D& x) const = default;
|
||||
// auto operator<(const D& x) const = default;
|
||||
// auto operator<=(const D& x) const = default;
|
||||
// auto operator>(const D& x) const = default;
|
||||
// auto operator>=(const D& x) const = default;
|
||||
};
|
||||
|
||||
#define assert(X) do { if (!(X)) __builtin_abort(); } while (0)
|
||||
|
||||
int main()
|
||||
{
|
||||
D d{42};
|
||||
D d2{24};
|
||||
|
||||
assert (is_eq (d <=> d));
|
||||
assert (is_lteq (d <=> d));
|
||||
assert (is_gteq (d <=> d));
|
||||
assert (is_lt (d2 <=> d));
|
||||
assert (is_lteq (d2 <=> d));
|
||||
assert (is_gt (d <=> d2));
|
||||
assert (is_gteq (d <=> d2));
|
||||
|
||||
assert (d == d);
|
||||
assert (!(d2 == d));
|
||||
assert (!(d == d2));
|
||||
assert (d != d2);
|
||||
assert (!(d2 != d2));
|
||||
|
||||
assert (d2 < d);
|
||||
assert (d2 <= d);
|
||||
assert (d > d2);
|
||||
assert (d >= d2);
|
||||
}
|
48
gcc/testsuite/g++.dg/cpp2a/spaceship-synth3.C
Normal file
48
gcc/testsuite/g++.dg/cpp2a/spaceship-synth3.C
Normal file
@ -0,0 +1,48 @@
|
||||
// Test for reversed candidates.
|
||||
// { dg-do run { target c++2a } }
|
||||
|
||||
#include <compare>
|
||||
|
||||
struct D
|
||||
{
|
||||
int i;
|
||||
auto operator<=>(int x) const { return i<=>x; }
|
||||
bool operator== (int x) const { return i==x; }
|
||||
};
|
||||
|
||||
#define assert(X) do { if (!(X)) __builtin_abort(); } while (0)
|
||||
|
||||
int main()
|
||||
{
|
||||
D d{42};
|
||||
int d1 = 42;
|
||||
int d2 = 24;
|
||||
|
||||
assert (is_eq (d <=> d1));
|
||||
assert (is_eq (d1 <=> d));
|
||||
assert (is_lteq (d <=> d1));
|
||||
assert (is_lteq (d1 <=> d));
|
||||
assert (is_gteq (d <=> d1));
|
||||
assert (is_gteq (d1 <=> d));
|
||||
assert (is_lt (d2 <=> d));
|
||||
assert (is_lteq (d2 <=> d));
|
||||
assert (is_gt (d <=> d2));
|
||||
assert (is_gteq (d <=> d2));
|
||||
|
||||
assert (d == d1);
|
||||
assert (d1 == d);
|
||||
assert (!(d2 == d));
|
||||
assert (!(d == d2));
|
||||
assert (d != d2);
|
||||
assert (d2 != d);
|
||||
assert (!(d != d1));
|
||||
assert (!(d1 != d));
|
||||
|
||||
assert (d2 < d);
|
||||
assert (d2 <= d);
|
||||
assert (d1 <= d);
|
||||
assert (d > d2);
|
||||
assert (d >= d2);
|
||||
assert (d >= d1);
|
||||
assert (d <= d1);
|
||||
}
|
54
gcc/testsuite/g++.dg/cpp2a/spaceship-synth3a.C
Normal file
54
gcc/testsuite/g++.dg/cpp2a/spaceship-synth3a.C
Normal file
@ -0,0 +1,54 @@
|
||||
// Test for reversed candidates.
|
||||
// { dg-do run { target c++2a } }
|
||||
|
||||
#include <compare>
|
||||
|
||||
struct D
|
||||
{
|
||||
int i;
|
||||
auto operator<=>(int x) const { return i<=>x; }
|
||||
bool operator== (int x) const { return i==x; }
|
||||
};
|
||||
|
||||
#define assert(X) do { if (!(X)) __builtin_abort(); } while (0)
|
||||
|
||||
template <class T>
|
||||
void f()
|
||||
{
|
||||
D d{42};
|
||||
int d1 = 42;
|
||||
int d2 = 24;
|
||||
|
||||
assert (is_eq (d <=> d1));
|
||||
assert (is_eq (d1 <=> d));
|
||||
assert (is_lteq (d <=> d1));
|
||||
assert (is_lteq (d1 <=> d));
|
||||
assert (is_gteq (d <=> d1));
|
||||
assert (is_gteq (d1 <=> d));
|
||||
assert (is_lt (d2 <=> d));
|
||||
assert (is_lteq (d2 <=> d));
|
||||
assert (is_gt (d <=> d2));
|
||||
assert (is_gteq (d <=> d2));
|
||||
|
||||
assert (d == d1);
|
||||
assert (d1 == d);
|
||||
assert (!(d2 == d));
|
||||
assert (!(d == d2));
|
||||
assert (d != d2);
|
||||
assert (d2 != d);
|
||||
assert (!(d != d1));
|
||||
assert (!(d1 != d));
|
||||
|
||||
assert (d2 < d);
|
||||
assert (d2 <= d);
|
||||
assert (d1 <= d);
|
||||
assert (d > d2);
|
||||
assert (d >= d2);
|
||||
assert (d >= d1);
|
||||
assert (d <= d1);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
f<int>();
|
||||
}
|
15
gcc/testsuite/g++.dg/cpp2a/spaceship-weak1.C
Normal file
15
gcc/testsuite/g++.dg/cpp2a/spaceship-weak1.C
Normal file
@ -0,0 +1,15 @@
|
||||
// Test explicit weak_ordering.
|
||||
// { dg-do compile { target c++2a } }
|
||||
|
||||
#include <compare>
|
||||
struct A
|
||||
{
|
||||
int i;
|
||||
std::weak_ordering operator<=> (const A&) const = default;
|
||||
};
|
||||
|
||||
constexpr A a = { 42 };
|
||||
constexpr auto c = a <=> a;
|
||||
static_assert (std::same_as <decltype (c), const std::weak_ordering>);
|
||||
static_assert (std::is_eq (c));
|
||||
|
@ -60,6 +60,7 @@ struct Y : virtual X
|
||||
template <typename T>
|
||||
int operator&(T x) { return m + x + 1; }
|
||||
friend int operator==(Y o, int x) { return o.m + x + 1; }
|
||||
int operator!=(int x) { return m + x + 1; }
|
||||
};
|
||||
|
||||
/* The folloiwng "FooN" functions each contain a different way to call and to
|
||||
@ -81,7 +82,6 @@ Foo1 (T)
|
||||
{ int t = x | I; assert (t == 7); }
|
||||
{ int t = x && I; assert (t == 7); }
|
||||
{ int t = x || I; assert (t == 7); }
|
||||
{ int t = x != I; assert (t == 7); }
|
||||
{ int t = x < I; assert (t == 7); }
|
||||
{ int t = x <= I; assert (t == 7); }
|
||||
{ int t = x > I; assert (t == 7); }
|
||||
@ -104,6 +104,7 @@ Foo1 (T)
|
||||
{ int t = x & I; assert (t == 8); }
|
||||
{ int t = &x; assert (t == 8); }
|
||||
{ int t = x == I; assert (t == 8); }
|
||||
{ int t = x != I; assert (t == 8); }
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@ -204,7 +205,6 @@ Foo4 (T)
|
||||
{ int t = x.operator| (I); assert (t == 7); }
|
||||
{ int t = x.operator&& (I); assert (t == 7); }
|
||||
{ int t = x.operator|| (I); assert (t == 7); }
|
||||
{ int t = x.operator!= (I); assert (t == 7); }
|
||||
{ int t = x.operator< (I); assert (t == 7); }
|
||||
{ int t = x.operator<= (I); assert (t == 7); }
|
||||
{ int t = x.operator> (I); assert (t == 7); }
|
||||
@ -227,6 +227,7 @@ Foo4 (T)
|
||||
{ int t = x.operator& (); assert (t == 8); }
|
||||
{ int t = x.operator& (I); assert (t == 8); }
|
||||
{ int t = operator== (x, I); assert (t == 8); }
|
||||
{ int t = x.operator!= (I); assert (t == 8); }
|
||||
}
|
||||
|
||||
|
||||
|
@ -11,18 +11,17 @@ public:
|
||||
operator int() const {return 2;}
|
||||
};
|
||||
|
||||
bool operator==(const MyInt& a, const int& b) // { dg-message "operator==" } candidate
|
||||
bool operator==(const MyInt& a, const int& b) // { dg-message "operator==" "" { target c++17_down } }
|
||||
{
|
||||
return (int)a == b;
|
||||
}
|
||||
|
||||
bool operator==(const MyInt& a, const MyInt& b) // { dg-message "operator==" } candidate
|
||||
bool operator==(const MyInt& a, const MyInt& b) // { dg-message "operator==" "" { target c++17_down } }
|
||||
{
|
||||
return (int)a == (int)b;
|
||||
}
|
||||
|
||||
bool f()
|
||||
{
|
||||
return 3 == MyInt(); // { dg-error "ambiguous" "err" }
|
||||
// { dg-message "operator==" "match candidate text" { target *-*-* } .-1 }
|
||||
return 3 == MyInt(); // { dg-error "ambiguous" "err" { target c++17_down } }
|
||||
}
|
||||
|
@ -1,3 +1,8 @@
|
||||
2019-11-05 Tim van Deurzen <tim@kompiler.org>
|
||||
|
||||
* cpplib.h: Add spaceship operator for C++.
|
||||
* lex.c: Implement conditional lexing of spaceship operator for C++20.
|
||||
|
||||
2019-10-31 Jakub Jelinek <jakub@redhat.com>
|
||||
|
||||
PR preprocessor/92296
|
||||
|
@ -78,6 +78,7 @@ struct _cpp_file;
|
||||
OP(NOT_EQ, "!=") \
|
||||
OP(GREATER_EQ, ">=") \
|
||||
OP(LESS_EQ, "<=") \
|
||||
OP(SPACESHIP, "<=>") \
|
||||
\
|
||||
/* These two are unary + / - in preprocessor expressions. */ \
|
||||
OP(PLUS_EQ, "+=") /* math */ \
|
||||
|
@ -2980,7 +2980,13 @@ _cpp_lex_direct (cpp_reader *pfile)
|
||||
|
||||
result->type = CPP_LESS;
|
||||
if (*buffer->cur == '=')
|
||||
buffer->cur++, result->type = CPP_LESS_EQ;
|
||||
{
|
||||
buffer->cur++, result->type = CPP_LESS_EQ;
|
||||
if (*buffer->cur == '>'
|
||||
&& CPP_OPTION (pfile, cplusplus)
|
||||
&& CPP_OPTION (pfile, lang) >= CLK_GNUCXX2A)
|
||||
buffer->cur++, result->type = CPP_SPACESHIP;
|
||||
}
|
||||
else if (*buffer->cur == '<')
|
||||
{
|
||||
buffer->cur++;
|
||||
@ -3491,6 +3497,7 @@ cpp_avoid_paste (cpp_reader *pfile, const cpp_token *token1,
|
||||
|| (CPP_OPTION (pfile, objc)
|
||||
&& token1->val.str.text[0] == '@'
|
||||
&& (b == CPP_NAME || b == CPP_STRING)));
|
||||
case CPP_LESS_EQ: return c == '>';
|
||||
case CPP_STRING:
|
||||
case CPP_WSTRING:
|
||||
case CPP_UTF8STRING:
|
||||
|
@ -1,5 +1,10 @@
|
||||
2019-11-05 Jonathan Wakely <jwakely@redhat.com>
|
||||
|
||||
* libsupc++/compare: New header.
|
||||
* libsupc++/Makefile.am (std_HEADERS): Add compare.
|
||||
* include/std/version: Define __cpp_lib_three_way_comparison.
|
||||
* include/std/functional: #include <compare>.
|
||||
|
||||
* include/std/version [!_GLIBCXX_HOSTED]: Do not define feature test
|
||||
macros for features that are only present in hosted builds.
|
||||
|
||||
|
@ -1385,8 +1385,9 @@ endif
|
||||
# <new>, <typeinfo>, <exception>, <initializer_list>, <cstdalign>, <cstdarg>,
|
||||
# <concepts>, <cstdbool>, <type_traits>, <bit>, <atomic>,
|
||||
# and any files which they include (and which we provide).
|
||||
# <new>, <typeinfo>, <exception>, and <initializer_list> are installed by
|
||||
# libsupc++, so only the others and the sub-includes are copied here.
|
||||
# <new>, <typeinfo>, <exception>, <initializer_list> and <compare>
|
||||
# are installed by libsupc++, so only the others and the sub-includes
|
||||
# are copied here.
|
||||
install-freestanding-headers:
|
||||
$(mkinstalldirs) $(DESTDIR)${gxx_include_dir}/bits
|
||||
for file in c++0x_warning.h atomic_base.h concept_check.h move.h; do \
|
||||
|
@ -66,6 +66,7 @@
|
||||
#endif
|
||||
#if __cplusplus > 201703L
|
||||
# include <bits/range_cmp.h>
|
||||
# include <compare>
|
||||
#endif
|
||||
|
||||
namespace std _GLIBCXX_VISIBILITY(default)
|
||||
|
@ -187,6 +187,9 @@
|
||||
#define __cpp_lib_list_remove_return_type 201806L
|
||||
#define __cpp_lib_math_constants 201907L
|
||||
#define __cpp_lib_span 201902L
|
||||
#if __cpp_impl_three_way_comparison >= 201907L
|
||||
# define __cpp_lib_three_way_comparison 201711L
|
||||
#endif
|
||||
#define __cpp_lib_to_array 201907L
|
||||
#endif
|
||||
#endif // C++2a
|
||||
|
@ -31,7 +31,7 @@ toolexeclib_LTLIBRARIES = libsupc++.la
|
||||
noinst_LTLIBRARIES = libsupc++convenience.la
|
||||
|
||||
std_HEADERS = \
|
||||
cxxabi.h exception initializer_list new typeinfo
|
||||
compare cxxabi.h exception initializer_list new typeinfo
|
||||
|
||||
bits_HEADERS = \
|
||||
atomic_lockfree_defines.h cxxabi_forced.h \
|
||||
|
644
libstdc++-v3/libsupc++/compare
Normal file
644
libstdc++-v3/libsupc++/compare
Normal file
@ -0,0 +1,644 @@
|
||||
// -*- C++ -*- operator<=> three-way comparison support.
|
||||
|
||||
// Copyright (C) 2019 Free Software Foundation, Inc.
|
||||
//
|
||||
// This file is part of GCC.
|
||||
//
|
||||
// GCC is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3, or (at your option)
|
||||
// any later version.
|
||||
//
|
||||
// GCC is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// Under Section 7 of GPL version 3, you are granted additional
|
||||
// permissions described in the GCC Runtime Library Exception, version
|
||||
// 3.1, as published by the Free Software Foundation.
|
||||
|
||||
// You should have received a copy of the GNU General Public License and
|
||||
// a copy of the GCC Runtime Library Exception along with this program;
|
||||
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
|
||||
// <http://www.gnu.org/licenses/>.
|
||||
|
||||
/** @file compare
|
||||
* This is a Standard C++ Library header.
|
||||
*/
|
||||
|
||||
#ifndef _COMPARE
|
||||
#define _COMPARE
|
||||
|
||||
#pragma GCC system_header
|
||||
|
||||
#if __cplusplus > 201703L && __cpp_impl_three_way_comparison >= 201907L
|
||||
|
||||
#pragma GCC visibility push(default)
|
||||
|
||||
#include <concepts>
|
||||
|
||||
namespace std
|
||||
{
|
||||
#define __cpp_lib_three_way_comparison 201711L
|
||||
|
||||
// [cmp.categories], comparison category types
|
||||
|
||||
namespace __cmp_cat
|
||||
{
|
||||
enum class _Eq
|
||||
{ equal = 0, equivalent = equal, nonequal = 1, nonequivalent = nonequal };
|
||||
|
||||
enum class _Ord { _Less = -1, _Greater = 1 };
|
||||
|
||||
enum class _Ncmp { _Unordered = -127 };
|
||||
|
||||
struct __unspec
|
||||
{
|
||||
constexpr __unspec(__unspec*) { }
|
||||
};
|
||||
}
|
||||
|
||||
class weak_equality
|
||||
{
|
||||
int _M_value;
|
||||
|
||||
constexpr explicit
|
||||
weak_equality(__cmp_cat::_Eq __val) noexcept
|
||||
: _M_value(int(__val))
|
||||
{ }
|
||||
|
||||
public:
|
||||
// valid values
|
||||
|
||||
static const weak_equality equivalent;
|
||||
static const weak_equality nonequivalent;
|
||||
|
||||
// comparisons
|
||||
|
||||
friend constexpr bool
|
||||
operator==(weak_equality __v, __cmp_cat::__unspec) noexcept
|
||||
{ return __v._M_value == 0; }
|
||||
|
||||
friend constexpr bool
|
||||
operator==(weak_equality, weak_equality) noexcept = default;
|
||||
|
||||
friend constexpr weak_equality
|
||||
operator<=>(weak_equality __v, __cmp_cat::__unspec) noexcept
|
||||
{ return __v; }
|
||||
|
||||
friend constexpr weak_equality
|
||||
operator<=>(__cmp_cat::__unspec, weak_equality __v) noexcept
|
||||
{ return __v; }
|
||||
};
|
||||
|
||||
// valid values' definitions
|
||||
inline constexpr weak_equality
|
||||
weak_equality::equivalent(__cmp_cat::_Eq::equivalent);
|
||||
|
||||
inline constexpr weak_equality
|
||||
weak_equality::nonequivalent(__cmp_cat::_Eq::nonequivalent);
|
||||
|
||||
class strong_equality
|
||||
{
|
||||
int _M_value;
|
||||
|
||||
constexpr explicit
|
||||
strong_equality(__cmp_cat::_Eq __val) noexcept
|
||||
: _M_value(int(__val))
|
||||
{ }
|
||||
|
||||
public:
|
||||
// valid values
|
||||
|
||||
static const strong_equality equal;
|
||||
static const strong_equality nonequal;
|
||||
static const strong_equality equivalent;
|
||||
static const strong_equality nonequivalent;
|
||||
|
||||
// conversion
|
||||
constexpr operator weak_equality() const noexcept
|
||||
{
|
||||
if (_M_value == 0)
|
||||
return weak_equality::equivalent;
|
||||
else
|
||||
return weak_equality::nonequivalent;
|
||||
}
|
||||
|
||||
// comparisons
|
||||
|
||||
friend constexpr bool
|
||||
operator==(strong_equality __v, __cmp_cat::__unspec) noexcept
|
||||
{ return __v._M_value == 0; }
|
||||
|
||||
friend constexpr bool
|
||||
operator==(strong_equality, strong_equality) noexcept = default;
|
||||
|
||||
friend constexpr strong_equality
|
||||
operator<=>(strong_equality __v, __cmp_cat::__unspec) noexcept
|
||||
{ return __v; }
|
||||
|
||||
friend constexpr strong_equality
|
||||
operator<=>(__cmp_cat::__unspec, strong_equality __v) noexcept
|
||||
{ return __v; }
|
||||
};
|
||||
|
||||
// valid values' definitions
|
||||
inline constexpr strong_equality
|
||||
strong_equality::equal(__cmp_cat::_Eq::equal);
|
||||
|
||||
inline constexpr strong_equality
|
||||
strong_equality::nonequal(__cmp_cat::_Eq::nonequal);
|
||||
|
||||
inline constexpr strong_equality
|
||||
strong_equality::equivalent(__cmp_cat::_Eq::equivalent);
|
||||
|
||||
inline constexpr strong_equality
|
||||
strong_equality::nonequivalent(__cmp_cat::_Eq::nonequivalent);
|
||||
|
||||
class partial_ordering
|
||||
{
|
||||
int _M_value;
|
||||
bool _M_is_ordered;
|
||||
|
||||
constexpr explicit
|
||||
partial_ordering(__cmp_cat::_Eq __v) noexcept
|
||||
: _M_value(int(__v)), _M_is_ordered(true)
|
||||
{ }
|
||||
|
||||
constexpr explicit
|
||||
partial_ordering(__cmp_cat::_Ord __v) noexcept
|
||||
: _M_value(int(__v)), _M_is_ordered(true)
|
||||
{ }
|
||||
|
||||
constexpr explicit
|
||||
partial_ordering(__cmp_cat::_Ncmp __v) noexcept
|
||||
: _M_value(int(__v)), _M_is_ordered(false)
|
||||
{ }
|
||||
|
||||
public:
|
||||
// valid values
|
||||
static const partial_ordering less;
|
||||
static const partial_ordering equivalent;
|
||||
static const partial_ordering greater;
|
||||
static const partial_ordering unordered;
|
||||
|
||||
// conversion
|
||||
constexpr operator weak_equality() const noexcept
|
||||
{
|
||||
if (_M_value == 0)
|
||||
return weak_equality::equivalent;
|
||||
else
|
||||
return weak_equality::nonequivalent;
|
||||
}
|
||||
|
||||
// comparisons
|
||||
friend constexpr bool
|
||||
operator==(partial_ordering __v, __cmp_cat::__unspec) noexcept
|
||||
{ return __v._M_is_ordered && __v._M_value == 0; }
|
||||
|
||||
friend constexpr bool
|
||||
operator==(partial_ordering, partial_ordering) noexcept = default;
|
||||
|
||||
friend constexpr bool
|
||||
operator< (partial_ordering __v, __cmp_cat::__unspec) noexcept
|
||||
{ return __v._M_is_ordered && __v._M_value < 0; }
|
||||
|
||||
friend constexpr bool
|
||||
operator> (partial_ordering __v, __cmp_cat::__unspec) noexcept
|
||||
{ return __v._M_is_ordered && __v._M_value > 0; }
|
||||
|
||||
friend constexpr bool
|
||||
operator<=(partial_ordering __v, __cmp_cat::__unspec) noexcept
|
||||
{ return __v._M_is_ordered && __v._M_value <= 0; }
|
||||
|
||||
friend constexpr bool
|
||||
operator>=(partial_ordering __v, __cmp_cat::__unspec) noexcept
|
||||
{ return __v._M_is_ordered && __v._M_value >= 0; }
|
||||
|
||||
friend constexpr bool
|
||||
operator< (__cmp_cat::__unspec, partial_ordering __v) noexcept
|
||||
{ return __v._M_is_ordered && 0 < __v._M_value; }
|
||||
|
||||
friend constexpr bool
|
||||
operator> (__cmp_cat::__unspec, partial_ordering __v) noexcept
|
||||
{ return __v._M_is_ordered && 0 > __v._M_value; }
|
||||
|
||||
friend constexpr bool
|
||||
operator<=(__cmp_cat::__unspec, partial_ordering __v) noexcept
|
||||
{ return __v._M_is_ordered && 0 <= __v._M_value; }
|
||||
|
||||
friend constexpr bool
|
||||
operator>=(__cmp_cat::__unspec, partial_ordering __v) noexcept
|
||||
{ return __v._M_is_ordered && 0 >= __v._M_value; }
|
||||
|
||||
friend constexpr partial_ordering
|
||||
operator<=>(partial_ordering __v, __cmp_cat::__unspec) noexcept
|
||||
{ return __v; }
|
||||
|
||||
friend constexpr partial_ordering
|
||||
operator<=>(__cmp_cat::__unspec, partial_ordering __v) noexcept
|
||||
{
|
||||
if (__v < 0)
|
||||
return partial_ordering::greater;
|
||||
else if (__v > 0)
|
||||
return partial_ordering::less;
|
||||
else
|
||||
return __v;
|
||||
}
|
||||
};
|
||||
|
||||
// valid values' definitions
|
||||
inline constexpr partial_ordering
|
||||
partial_ordering::less(__cmp_cat::_Ord::_Less);
|
||||
|
||||
inline constexpr partial_ordering
|
||||
partial_ordering::equivalent(__cmp_cat::_Eq::equivalent);
|
||||
|
||||
inline constexpr partial_ordering
|
||||
partial_ordering::greater(__cmp_cat::_Ord::_Greater);
|
||||
|
||||
inline constexpr partial_ordering
|
||||
partial_ordering::unordered(__cmp_cat::_Ncmp::_Unordered);
|
||||
|
||||
class weak_ordering
|
||||
{
|
||||
int _M_value;
|
||||
|
||||
constexpr explicit
|
||||
weak_ordering(__cmp_cat::_Eq __v) noexcept : _M_value(int(__v))
|
||||
{ }
|
||||
|
||||
constexpr explicit
|
||||
weak_ordering(__cmp_cat::_Ord __v) noexcept : _M_value(int(__v))
|
||||
{ }
|
||||
|
||||
public:
|
||||
// valid values
|
||||
static const weak_ordering less;
|
||||
static const weak_ordering equivalent;
|
||||
static const weak_ordering greater;
|
||||
|
||||
// conversions
|
||||
constexpr operator weak_equality() const noexcept
|
||||
{
|
||||
if (_M_value == 0)
|
||||
return weak_equality::equivalent;
|
||||
else
|
||||
return weak_equality::nonequivalent;
|
||||
}
|
||||
|
||||
constexpr operator partial_ordering() const noexcept
|
||||
{
|
||||
if (_M_value == 0)
|
||||
return partial_ordering::equivalent;
|
||||
else if (_M_value < 0)
|
||||
return partial_ordering::less;
|
||||
else
|
||||
return partial_ordering::greater;
|
||||
}
|
||||
|
||||
// comparisons
|
||||
friend constexpr bool
|
||||
operator==(weak_ordering __v, __cmp_cat::__unspec) noexcept
|
||||
{ return __v._M_value == 0; }
|
||||
|
||||
friend constexpr bool
|
||||
operator==(weak_ordering, weak_ordering) noexcept = default;
|
||||
|
||||
friend constexpr bool
|
||||
operator< (weak_ordering __v, __cmp_cat::__unspec) noexcept
|
||||
{ return __v._M_value < 0; }
|
||||
|
||||
friend constexpr bool
|
||||
operator> (weak_ordering __v, __cmp_cat::__unspec) noexcept
|
||||
{ return __v._M_value > 0; }
|
||||
|
||||
friend constexpr bool
|
||||
operator<=(weak_ordering __v, __cmp_cat::__unspec) noexcept
|
||||
{ return __v._M_value <= 0; }
|
||||
|
||||
friend constexpr bool
|
||||
operator>=(weak_ordering __v, __cmp_cat::__unspec) noexcept
|
||||
{ return __v._M_value >= 0; }
|
||||
|
||||
friend constexpr bool
|
||||
operator< (__cmp_cat::__unspec, weak_ordering __v) noexcept
|
||||
{ return 0 < __v._M_value; }
|
||||
|
||||
friend constexpr bool
|
||||
operator> (__cmp_cat::__unspec, weak_ordering __v) noexcept
|
||||
{ return 0 > __v._M_value; }
|
||||
|
||||
friend constexpr bool
|
||||
operator<=(__cmp_cat::__unspec, weak_ordering __v) noexcept
|
||||
{ return 0 <= __v._M_value; }
|
||||
|
||||
friend constexpr bool
|
||||
operator>=(__cmp_cat::__unspec, weak_ordering __v) noexcept
|
||||
{ return 0 >= __v._M_value; }
|
||||
|
||||
friend constexpr weak_ordering
|
||||
operator<=>(weak_ordering __v, __cmp_cat::__unspec) noexcept
|
||||
{ return __v; }
|
||||
|
||||
friend constexpr weak_ordering
|
||||
operator<=>(__cmp_cat::__unspec, weak_ordering __v) noexcept
|
||||
{
|
||||
if (__v < 0)
|
||||
return weak_ordering::greater;
|
||||
else if (__v > 0)
|
||||
return weak_ordering::less;
|
||||
else
|
||||
return __v;
|
||||
}
|
||||
};
|
||||
|
||||
// valid values' definitions
|
||||
inline constexpr weak_ordering
|
||||
weak_ordering::less(__cmp_cat::_Ord::_Less);
|
||||
|
||||
inline constexpr weak_ordering
|
||||
weak_ordering::equivalent(__cmp_cat::_Eq::equivalent);
|
||||
|
||||
inline constexpr weak_ordering
|
||||
weak_ordering::greater(__cmp_cat::_Ord::_Greater);
|
||||
|
||||
class strong_ordering
|
||||
{
|
||||
int _M_value;
|
||||
|
||||
constexpr explicit
|
||||
strong_ordering(__cmp_cat::_Eq __v) noexcept
|
||||
: _M_value(int(__v))
|
||||
{ }
|
||||
|
||||
constexpr explicit
|
||||
strong_ordering(__cmp_cat::_Ord __v) noexcept
|
||||
: _M_value(int(__v))
|
||||
{ }
|
||||
|
||||
public:
|
||||
// valid values
|
||||
static const strong_ordering less;
|
||||
static const strong_ordering equal;
|
||||
static const strong_ordering equivalent;
|
||||
static const strong_ordering greater;
|
||||
|
||||
// conversions
|
||||
constexpr operator weak_equality() const noexcept
|
||||
{
|
||||
if (_M_value == 0)
|
||||
return weak_equality::equivalent;
|
||||
else
|
||||
return weak_equality::nonequivalent;
|
||||
}
|
||||
|
||||
constexpr operator strong_equality() const noexcept
|
||||
{
|
||||
if (_M_value == 0)
|
||||
return strong_equality::equal;
|
||||
else
|
||||
return strong_equality::nonequal;
|
||||
}
|
||||
|
||||
constexpr operator partial_ordering() const noexcept
|
||||
{
|
||||
if (_M_value == 0)
|
||||
return partial_ordering::equivalent;
|
||||
else if (_M_value < 0)
|
||||
return partial_ordering::less;
|
||||
else
|
||||
return partial_ordering::greater;
|
||||
}
|
||||
|
||||
constexpr operator weak_ordering() const noexcept
|
||||
{
|
||||
if (_M_value == 0)
|
||||
return weak_ordering::equivalent;
|
||||
else if (_M_value < 0)
|
||||
return weak_ordering::less;
|
||||
else
|
||||
return weak_ordering::greater;
|
||||
}
|
||||
|
||||
// comparisons
|
||||
friend constexpr bool
|
||||
operator==(strong_ordering __v, __cmp_cat::__unspec) noexcept
|
||||
{ return __v._M_value == 0; }
|
||||
|
||||
friend constexpr bool
|
||||
operator==(strong_ordering, strong_ordering) noexcept = default;
|
||||
|
||||
friend constexpr bool
|
||||
operator< (strong_ordering __v, __cmp_cat::__unspec) noexcept
|
||||
{ return __v._M_value < 0; }
|
||||
|
||||
friend constexpr bool
|
||||
operator> (strong_ordering __v, __cmp_cat::__unspec) noexcept
|
||||
{ return __v._M_value > 0; }
|
||||
|
||||
friend constexpr bool
|
||||
operator<=(strong_ordering __v, __cmp_cat::__unspec) noexcept
|
||||
{ return __v._M_value <= 0; }
|
||||
|
||||
friend constexpr bool
|
||||
operator>=(strong_ordering __v, __cmp_cat::__unspec) noexcept
|
||||
{ return __v._M_value >= 0; }
|
||||
|
||||
friend constexpr bool
|
||||
operator< (__cmp_cat::__unspec, strong_ordering __v) noexcept
|
||||
{ return 0 < __v._M_value; }
|
||||
|
||||
friend constexpr bool
|
||||
operator> (__cmp_cat::__unspec, strong_ordering __v) noexcept
|
||||
{ return 0 > __v._M_value; }
|
||||
|
||||
friend constexpr bool
|
||||
operator<=(__cmp_cat::__unspec, strong_ordering __v) noexcept
|
||||
{ return 0 <= __v._M_value; }
|
||||
|
||||
friend constexpr bool
|
||||
operator>=(__cmp_cat::__unspec, strong_ordering __v) noexcept
|
||||
{ return 0 >= __v._M_value; }
|
||||
|
||||
friend constexpr strong_ordering
|
||||
operator<=>(strong_ordering __v, __cmp_cat::__unspec) noexcept
|
||||
{ return __v; }
|
||||
|
||||
friend constexpr strong_ordering
|
||||
operator<=>(__cmp_cat::__unspec, strong_ordering __v) noexcept
|
||||
{
|
||||
if (__v < 0)
|
||||
return strong_ordering::greater;
|
||||
else if (__v > 0)
|
||||
return strong_ordering::less;
|
||||
else
|
||||
return __v;
|
||||
}
|
||||
};
|
||||
|
||||
// valid values' definitions
|
||||
inline constexpr strong_ordering
|
||||
strong_ordering::less(__cmp_cat::_Ord::_Less);
|
||||
|
||||
inline constexpr strong_ordering
|
||||
strong_ordering::equal(__cmp_cat::_Eq::equal);
|
||||
|
||||
inline constexpr strong_ordering
|
||||
strong_ordering::equivalent(__cmp_cat::_Eq::equivalent);
|
||||
|
||||
inline constexpr strong_ordering
|
||||
strong_ordering::greater(__cmp_cat::_Ord::_Greater);
|
||||
|
||||
|
||||
// named comparison functions
|
||||
constexpr bool
|
||||
is_eq(weak_equality __cmp) noexcept
|
||||
{ return __cmp == 0; }
|
||||
|
||||
constexpr bool
|
||||
is_neq(weak_equality __cmp) noexcept
|
||||
{ return __cmp != 0; }
|
||||
|
||||
constexpr bool
|
||||
is_lt (partial_ordering __cmp) noexcept
|
||||
{ return __cmp < 0; }
|
||||
|
||||
constexpr bool
|
||||
is_lteq(partial_ordering __cmp) noexcept
|
||||
{ return __cmp <= 0; }
|
||||
|
||||
constexpr bool
|
||||
is_gt (partial_ordering __cmp) noexcept
|
||||
{ return __cmp > 0; }
|
||||
|
||||
constexpr bool
|
||||
is_gteq(partial_ordering __cmp) noexcept
|
||||
{ return __cmp >= 0; }
|
||||
|
||||
// [cmp.common], common comparison category type
|
||||
template<typename... _Ts>
|
||||
struct common_comparison_category {
|
||||
// using type = TODO
|
||||
};
|
||||
|
||||
template<typename... _Ts>
|
||||
using common_comparison_category_t
|
||||
= typename common_comparison_category<_Ts...>::type;
|
||||
|
||||
#if __cpp_concepts
|
||||
namespace __detail
|
||||
{
|
||||
template<typename _Tp, typename _Cat>
|
||||
concept __compares_as
|
||||
= same_as<common_comparison_category_t<_Tp, _Cat>, _Cat>;
|
||||
|
||||
template<typename _Tp, typename _Up>
|
||||
concept __partially_ordered_with
|
||||
= requires(const remove_reference_t<_Tp>& __t,
|
||||
const remove_reference_t<_Up>& __u) {
|
||||
{ __t < __u } -> boolean;
|
||||
{ __t > __u } -> boolean;
|
||||
{ __t <= __u } -> boolean;
|
||||
{ __t >= __u } -> boolean;
|
||||
{ __u < __t } -> boolean;
|
||||
{ __u > __t } -> boolean;
|
||||
{ __u <= __t } -> boolean;
|
||||
{ __u >= __t } -> boolean;
|
||||
};
|
||||
} // namespace __detail
|
||||
|
||||
// [cmp.concept], concept three_way_comparable
|
||||
template<typename _Tp, typename _Cat = partial_ordering>
|
||||
concept three_way_comparable
|
||||
= __detail::__weakly_eq_cmp_with<_Tp, _Tp>
|
||||
&& (!convertible_to<_Cat, partial_ordering>
|
||||
|| __detail::__partially_ordered_with<_Tp, _Tp>)
|
||||
&& requires(const remove_reference_t<_Tp>& __a,
|
||||
const remove_reference_t<_Tp>& __b) {
|
||||
{ __a <=> __b } -> __detail::__compares_as<_Cat>;
|
||||
};
|
||||
|
||||
template<typename _Tp, typename _Up, typename _Cat = partial_ordering>
|
||||
concept three_way_comparable_with
|
||||
= __detail::__weakly_eq_cmp_with<_Tp, _Up>
|
||||
&& (!convertible_to<_Cat, partial_ordering>
|
||||
|| __detail::__partially_ordered_with<_Tp, _Up>)
|
||||
&& three_way_comparable<_Tp, _Cat>
|
||||
&& three_way_comparable<_Up, _Cat>
|
||||
&& common_reference_with<const remove_reference_t<_Tp>&,
|
||||
const remove_reference_t<_Up>&>
|
||||
&& three_way_comparable<
|
||||
common_reference_t<const remove_reference_t<_Tp>&,
|
||||
const remove_reference_t<_Up>&>, _Cat>
|
||||
&& requires(const remove_reference_t<_Tp>& __t,
|
||||
const remove_reference_t<_Up>& __u) {
|
||||
{ __t <=> __u } -> __detail::__compares_as<_Cat>;
|
||||
{ __u <=> __t } -> __detail::__compares_as<_Cat>;
|
||||
};
|
||||
#endif
|
||||
|
||||
template<typename _Tp, typename _Up>
|
||||
using __cmp2way_res_t
|
||||
= decltype(std::declval<_Tp&>() <=> std::declval<_Up&>());
|
||||
|
||||
template<typename _Tp, typename _Up = _Tp, typename = void>
|
||||
struct __cmp3way_helper
|
||||
{ };
|
||||
|
||||
template<typename _Tp, typename _Up>
|
||||
struct __cmp3way_helper<_Tp, _Up, void_t<__cmp2way_res_t<_Tp, _Up>>>
|
||||
{
|
||||
using type = __cmp2way_res_t<_Tp, _Up>;
|
||||
using __type = type;
|
||||
};
|
||||
|
||||
/// [cmp.result], result of three-way comparison
|
||||
template<typename _Tp, typename _Up = _Tp>
|
||||
struct compare_three_way_result
|
||||
: __cmp3way_helper<_Tp, _Up>
|
||||
{ };
|
||||
|
||||
template<typename _Tp, typename _Up = _Tp>
|
||||
using compare_three_way_result_t
|
||||
= typename compare_three_way_result<_Tp, _Up>::__type;
|
||||
|
||||
// [cmp.object], typename compare_three_way
|
||||
struct compare_three_way
|
||||
{
|
||||
// TODO
|
||||
#if 0
|
||||
template<typename _Tp, typename _Up>
|
||||
requires (three_way_comparable_with<_Tp, _Up>
|
||||
|| BUILTIN-PTR-THREE-WAY(_Tp, _Up))
|
||||
constexpr auto
|
||||
operator()(_Tp&& __t, _Up&& __u) const noexcept
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
#endif
|
||||
|
||||
using is_transparent = void;
|
||||
};
|
||||
|
||||
// [cmp.alg], comparison algorithms
|
||||
inline namespace __cmp_alg
|
||||
{
|
||||
// TODO
|
||||
#if 0
|
||||
inline constexpr unspecified strong_order = unspecified;
|
||||
inline constexpr unspecified weak_order = unspecified;
|
||||
inline constexpr unspecified partial_order = unspecified;
|
||||
inline constexpr unspecified compare_strong_order_fallback = unspecified;
|
||||
inline constexpr unspecified compare_weak_order_fallback = unspecified;
|
||||
inline constexpr unspecified compare_partial_order_fallback = unspecified;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#pragma GCC visibility pop
|
||||
|
||||
#endif // C++20
|
||||
|
||||
#endif // _COMPARE
|
@ -52,7 +52,7 @@ static_assert( ! std::regular<HasReference> );
|
||||
|
||||
struct HasEq { };
|
||||
bool operator==(HasEq, HasEq) { return true; }
|
||||
#ifdef __cpp_lib_three_way_comparison
|
||||
#ifdef __cpp_impl_three_way_comparison
|
||||
static_assert( std::regular<HasEq> );
|
||||
#else
|
||||
static_assert( ! std::regular<HasEq> );
|
||||
|
Loading…
x
Reference in New Issue
Block a user