diff options
Diffstat (limited to 'gcc/cp/call.c')
-rw-r--r-- | gcc/cp/call.c | 449 |
1 files changed, 358 insertions, 91 deletions
diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 390a4c5..0034c1c 100644 --- a/gcc/cp/call.c +++ b/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)) { |