diff options
Diffstat (limited to 'gcc/cp')
-rw-r--r-- | gcc/cp/call.cc | 109 | ||||
-rw-r--r-- | gcc/cp/cp-tree.h | 1 | ||||
-rw-r--r-- | gcc/cp/decl.cc | 66 | ||||
-rw-r--r-- | gcc/cp/pt.cc | 5 |
4 files changed, 171 insertions, 10 deletions
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index 2c0fa37..492db9b 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -6232,6 +6232,7 @@ add_candidates (tree fns, tree first_arg, const vec<tree, va_gc> *args, bool check_list_ctor = false; bool check_converting = false; unification_kind_t strict; + tree ne_fns = NULL_TREE; if (!fns) return; @@ -6269,6 +6270,32 @@ add_candidates (tree fns, tree first_arg, const vec<tree, va_gc> *args, ctype = conversion_path ? BINFO_TYPE (conversion_path) : NULL_TREE; } + /* P2468: Check if operator== is a rewrite target with first operand + (*args)[0]; for now just do the lookups. */ + if ((flags & (LOOKUP_REWRITTEN | LOOKUP_REVERSED)) + && DECL_OVERLOADED_OPERATOR_IS (fn, EQ_EXPR)) + { + tree ne_name = ovl_op_identifier (false, NE_EXPR); + if (DECL_CLASS_SCOPE_P (fn)) + { + ne_fns = lookup_fnfields (TREE_TYPE ((*args)[0]), ne_name, + 1, tf_none); + if (ne_fns == error_mark_node || ne_fns == NULL_TREE) + ne_fns = NULL_TREE; + else + ne_fns = BASELINK_FUNCTIONS (ne_fns); + } + else + { + tree context = decl_namespace_context (fn); + ne_fns = lookup_qualified_name (context, ne_name, LOOK_want::NORMAL, + /*complain*/false); + if (ne_fns == error_mark_node + || !is_overloaded_fn (ne_fns)) + ne_fns = NULL_TREE; + } + } + if (first_arg) non_static_args = args; else @@ -6345,6 +6372,27 @@ add_candidates (tree fns, tree first_arg, const vec<tree, va_gc> *args, continue; } + /* When considering reversed operator==, if there's a corresponding + operator!= in the same scope, it's not a rewrite target. */ + if (ne_fns) + { + bool found = false; + for (lkp_iterator ne (ne_fns); !found && ne; ++ne) + if (0 && !ne.using_p () + && DECL_NAMESPACE_SCOPE_P (fn) + && DECL_CONTEXT (*ne) != DECL_CONTEXT (fn)) + /* ??? This kludge excludes inline namespace members for the H + test in spaceship-eq15.C, but I don't see why we would want + that behavior. Asked Core 2022-11-04. Disabling for now. */; + else if (fns_correspond (fn, *ne)) + { + found = true; + break; + } + if (found) + continue; + } + if (TREE_CODE (fn) == TEMPLATE_DECL) { if (!add_template_candidate (candidates, @@ -6917,10 +6965,12 @@ build_new_op (const op_location_t &loc, enum tree_code code, int flags, gcc_checking_assert (cand->reversed ()); gcc_fallthrough (); case NE_EXPR: + if (result == error_mark_node) + ; /* 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) + else if (TREE_CODE (TREE_TYPE (result)) != BOOLEAN_TYPE) { if (complain & tf_error) { @@ -12488,10 +12538,53 @@ joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn, if (winner && comp != winner) { /* Ambiguity between normal and reversed comparison operators - with the same parameter types; prefer the normal one. */ - if ((cand1->reversed () != cand2->reversed ()) + with the same parameter types. P2468 decided not to go with + this approach to resolving the ambiguity, so pedwarn. */ + if ((complain & tf_warning_or_error) + && (cand1->reversed () != cand2->reversed ()) && cand_parms_match (cand1, cand2)) - return cand1->reversed () ? -1 : 1; + { + struct z_candidate *w, *l; + if (cand2->reversed ()) + winner = 1, w = cand1, l = cand2; + else + winner = -1, w = cand2, l = cand1; + if (warn) + { + auto_diagnostic_group d; + if (pedwarn (input_location, 0, + "C++20 says that these are ambiguous, " + "even though the second is reversed:")) + { + print_z_candidate (input_location, + N_("candidate 1:"), w); + print_z_candidate (input_location, + N_("candidate 2:"), l); + if (w->fn == l->fn + && DECL_NONSTATIC_MEMBER_FUNCTION_P (w->fn) + && (type_memfn_quals (TREE_TYPE (w->fn)) + & TYPE_QUAL_CONST) == 0) + { + /* Suggest adding const to + struct A { bool operator==(const A&); }; */ + tree parmtype + = FUNCTION_FIRST_USER_PARMTYPE (w->fn); + parmtype = TREE_VALUE (parmtype); + if (TYPE_REF_P (parmtype) + && TYPE_READONLY (TREE_TYPE (parmtype)) + && (same_type_ignoring_top_level_qualifiers_p + (TREE_TYPE (parmtype), + DECL_CONTEXT (w->fn)))) + inform (DECL_SOURCE_LOCATION (w->fn), + "try making the operator a %<const%> " + "member function"); + } + } + } + else + add_warning (w, l); + return winner; + } winner = 0; goto tweak; @@ -12880,7 +12973,7 @@ tourney (struct z_candidate *candidates, tsubst_flags_t complain) { struct z_candidate *champ = candidates, *challenger; int fate; - int champ_compared_to_predecessor = 0; + struct z_candidate *champ_compared_to_predecessor = nullptr; /* Walk through the list once, comparing each current champ to the next candidate, knocking out a candidate or two with each comparison. */ @@ -12897,12 +12990,12 @@ tourney (struct z_candidate *candidates, tsubst_flags_t complain) champ = challenger->next; if (champ == 0) return NULL; - champ_compared_to_predecessor = 0; + champ_compared_to_predecessor = nullptr; } else { + champ_compared_to_predecessor = champ; champ = challenger; - champ_compared_to_predecessor = 1; } challenger = champ->next; @@ -12914,7 +13007,7 @@ tourney (struct z_candidate *candidates, tsubst_flags_t complain) for (challenger = candidates; challenger != champ - && !(champ_compared_to_predecessor && challenger->next == champ); + && challenger != champ_compared_to_predecessor; challenger = challenger->next) { fate = joust (champ, challenger, 0, complain); diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index d13bb3d..bbc8be2 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -6820,6 +6820,7 @@ extern void note_break_stmt (void); extern bool note_iteration_stmt_body_start (void); extern void note_iteration_stmt_body_end (bool); extern void determine_local_discriminator (tree); +extern bool fns_correspond (tree, tree); extern int decls_match (tree, tree, bool = true); extern bool maybe_version_functions (tree, tree, bool); extern bool merge_default_template_args (tree, tree, bool); diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index 6e98ea3..890cfca 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -980,6 +980,72 @@ function_requirements_equivalent_p (tree newfn, tree oldfn) return cp_tree_equal (reqs1, reqs2); } +/* Two functions of the same name correspond [basic.scope.scope] if + + + both declare functions with the same non-object-parameter-type-list, + equivalent ([temp.over.link]) trailing requires-clauses (if any, except as + specified in [temp.friend]), and, if both are non-static members, they have + corresponding object parameters, or + + + both declare function templates with equivalent + non-object-parameter-type-lists, return types (if any), template-heads, and + trailing requires-clauses (if any), and, if both are non-static members, + they have corresponding object parameters. + + This is a subset of decls_match: it identifies declarations that cannot be + overloaded with one another. This function does not consider DECL_NAME. */ + +bool +fns_correspond (tree newdecl, tree olddecl) +{ + if (TREE_CODE (newdecl) != TREE_CODE (olddecl)) + return false; + + if (TREE_CODE (newdecl) == TEMPLATE_DECL) + { + if (!template_heads_equivalent_p (newdecl, olddecl)) + return 0; + newdecl = DECL_TEMPLATE_RESULT (newdecl); + olddecl = DECL_TEMPLATE_RESULT (olddecl); + } + + tree f1 = TREE_TYPE (newdecl); + tree f2 = TREE_TYPE (olddecl); + + int rq1 = type_memfn_rqual (f1); + int rq2 = type_memfn_rqual (f2); + + /* If only one is a non-static member function, ignore ref-quals. */ + if (TREE_CODE (f1) != TREE_CODE (f2)) + rq1 = rq2; + /* Two non-static member functions have corresponding object parameters if: + + exactly one is an implicit object member function with no ref-qualifier + and the types of their object parameters ([dcl.fct]), after removing + top-level references, are the same, or + + their object parameters have the same type. */ + /* ??? We treat member functions of different classes as corresponding even + though that means the object parameters have different types. */ + else if ((rq1 == REF_QUAL_NONE) != (rq2 == REF_QUAL_NONE)) + rq1 = rq2; + + bool types_match = rq1 == rq2; + + if (types_match) + { + tree p1 = FUNCTION_FIRST_USER_PARMTYPE (newdecl); + tree p2 = FUNCTION_FIRST_USER_PARMTYPE (olddecl); + types_match = compparms (p1, p2); + } + + /* Two function declarations match if either has a requires-clause + then both have a requires-clause and their constraints-expressions + are equivalent. */ + if (types_match && flag_concepts) + types_match = function_requirements_equivalent_p (newdecl, olddecl); + + return types_match; +} + /* Subroutine of duplicate_decls: return truthvalue of whether or not types of these decls match. diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index c3fc56a..57917de 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -20937,8 +20937,9 @@ tsubst_copy_and_build (tree t, /* In a lambda fn, we have to be careful to not introduce new this captures. Legacy code can't be using lambdas anyway, so it's ok to be - stricter. Be strict with C++20 template-id ADL too. */ - bool strict = in_lambda || template_id_p; + stricter. Be strict with C++20 template-id ADL too. + And be strict if we're already failing anyway. */ + bool strict = in_lambda || template_id_p || seen_error(); bool diag = true; if (strict) error_at (cp_expr_loc_or_input_loc (t), |