From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id B8FF73858C83 for ; Mon, 7 Nov 2022 20:57:59 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org B8FF73858C83 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1667854679; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=AXmMV1lAooeZh5DW/I87hk96ChWtvdjmiOnZpSdPrhg=; b=FSHirfW4WitXDz3N2DiMUKC+p9q2m07GD3EmYO9w0YYpVLSUVtpBjEgjhrfh2u17bjM2u2 3kn69HgLrVALRFJvb2+5iIxVpdvMGgWCuwT+KtZQbcWqnNv6Pxy9fWUAlOJNigbfjL+8hb UE03kxctzlHzawy+H5ZA47rz4S4Qdpo= Received: from mail-qv1-f70.google.com (mail-qv1-f70.google.com [209.85.219.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_128_GCM_SHA256) id us-mta-576-Puik4EVuM0qFTnuN8WU-Gg-1; Mon, 07 Nov 2022 15:57:58 -0500 X-MC-Unique: Puik4EVuM0qFTnuN8WU-Gg-1 Received: by mail-qv1-f70.google.com with SMTP id e13-20020ad450cd000000b004bb49d98da4so8327221qvq.9 for ; Mon, 07 Nov 2022 12:57:58 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=AXmMV1lAooeZh5DW/I87hk96ChWtvdjmiOnZpSdPrhg=; b=YEnM/HImzW3CUbuJzgc7XTg0o9/F4mr8NCZDY8MK3yxtw3IAq9m4wVKxLvmGGiC0WX 5U6nWAIqCPhfqgh1kS+CTTq7J2kfl8jJVGLkkcvnGvSZec+gyFuGwFDcee9HnZxbwHP2 HXS/OMIEeQy3tbrVgNig5vdlXdIs1Y2+XJWw0xRwpoa9g1AFERF+Jits/AV4qcYwylCq P3uosZLwaBQqe54qqKUw5ioYTNwPi46U4OQOXzWSRKAyG7atOBsJlIGU0auQym0A07hv 9lUA+WyEjJTBQSQZJIYGMaZI+/FugwvDPBrCTJ/omUgSU7dwjrtGBbLB8ZwjYPjXEMZ7 mDqg== X-Gm-Message-State: ACrzQf1F6S1Hdj+4XaTuKufj0IqGQx8CKYWtp8OUuPx2RS31XUBZIbtl CjDXC9BD2+2+7yO+HQxqNIfg8IuWV2sOb1faKtDuSH0jM34I38TDWqtUliaaxxYRL/UwJ7LgLFu uO9gjlN+2zaKnS5LzOlJ44X1zkgA7Xus9IiMpGQv91NMDTpnCBqnm8JyZ9WRjcLsEgw== X-Received: by 2002:a37:2d02:0:b0:6f1:15cd:1493 with SMTP id t2-20020a372d02000000b006f115cd1493mr36834323qkh.131.1667854677055; Mon, 07 Nov 2022 12:57:57 -0800 (PST) X-Google-Smtp-Source: AMsMyM7NH2sKRRcx1nkQ5g3cXTAsJxyxG7e38OgTzbLCBm9WKcXqA4tMUscf24w8/EEmUzHXbpWykA== X-Received: by 2002:a37:2d02:0:b0:6f1:15cd:1493 with SMTP id t2-20020a372d02000000b006f115cd1493mr36834281qkh.131.1667854676277; Mon, 07 Nov 2022 12:57:56 -0800 (PST) Received: from barrymore.redhat.com (rrcs-24-43-233-8.west.biz.rr.com. [24.43.233.8]) by smtp.gmail.com with ESMTPSA id y22-20020a05620a44d600b006ec62032d3dsm7990590qkp.30.2022.11.07.12.57.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 07 Nov 2022 12:57:55 -0800 (PST) From: Jason Merrill To: gcc-patches@gcc.gnu.org Cc: jwakely@redhat.com, Jakub Jelinek Subject: [PATCH RFC(libstdc++)] c++: implement P2468R2, the equality operator you are looking for Date: Mon, 7 Nov 2022 10:57:52 -1000 Message-Id: <20221107205752.2735464-1-jason@redhat.com> X-Mailer: git-send-email 2.31.1 MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset="US-ASCII"; x-default=true X-Spam-Status: No, score=-11.9 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,GIT_PATCH_0,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_NONE,TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: Tested x86_64-pc-linux-gnu. Jonathan, what do you want to do about the library test failure? -- >8 -- This paper is resolving the problem of well-formed C++17 code becoming ambiguous in C++20 due to asymmetrical operator== being compared with itself in reverse. I had previously implemented a tiebreaker such that if the two candidates were functions with the same parameter types, we would prefer the non-reversed candidate. But the committee went with a different approach: if there's an operator!= with the same parameter types as the operator==, don't consider the reversed form of the ==. So this patch implements that, and changes my old tiebreaker to give a pedwarn if it is used. I also noticed that we were giving duplicate errors for some testcases, and fixed the tourney logic to avoid that. As a result, a lot of tests of the form struct A { bool operator==(const A&); }; need to be fixed to add a const function-cv-qualifier, e.g. struct A { bool operator==(const A&) const; }; The committee thought such code ought to be fixed, so breaking it was fine. 18_support/comparisons/algorithms/fallback.cc also breaks with this patch, because of the similarly asymmetrical bool operator==(const S&, S&) { return true; } I assume this was written this way deliberately, so I'm not sure what to do about it. The build_new_op and tsubst_copy_and_build hunks fix issues with the concept failure diagnostics on that testcase. The H test in spaceship-eq15.C is specified in the standard to be well-formed because the op!= in the inline namespace is not found by the search, but that seems wrong to me. I've implemented that behavior, but disabled it for now; if we decide that is the way we want to go, we can just remove the "0 &&" in add_candidates to enable it. Co-authored-by: Jakub Jelinek gcc/cp/ChangeLog: * cp-tree.h (fns_correspond): Declare. * decl.cc (fns_correspond): New. * call.cc (add_candidates): Look for op!= matching op==. (joust): Complain about non-standard reversed tiebreaker. (tourney): Fix champ_compared_to_predecessor logic. (build_new_op): Don't complain about error_mark_node not having 'bool' type. * pt.cc (tsubst_copy_and_build): Don't try to be permissive when seen_error(). gcc/testsuite/ChangeLog: * g++.dg/cpp2a/spaceship-eq15.C: New test. * g++.dg/cpp0x/defaulted3.C: Add const. * g++.dg/cpp2a/bit-cast7.C: Add const. * g++.dg/cpp2a/spaceship-rewrite1.C: Expect error. * g++.dg/cpp2a/spaceship-rewrite5.C: Expect error. * g++.old-deja/g++.jason/byval2.C: Expect error. * g++.old-deja/g++.other/overload13.C: Add const. --- gcc/cp/cp-tree.h | 1 + gcc/cp/call.cc | 109 ++++++++- gcc/cp/decl.cc | 66 ++++++ gcc/cp/pt.cc | 5 +- gcc/testsuite/g++.dg/cpp0x/defaulted3.C | 2 +- gcc/testsuite/g++.dg/cpp2a/bit-cast7.C | 4 +- gcc/testsuite/g++.dg/cpp2a/spaceship-eq15.C | 208 ++++++++++++++++++ .../g++.dg/cpp2a/spaceship-rewrite1.C | 2 +- .../g++.dg/cpp2a/spaceship-rewrite5.C | 2 +- gcc/testsuite/g++.old-deja/g++.jason/byval2.C | 2 +- .../g++.old-deja/g++.other/overload13.C | 2 +- 11 files changed, 386 insertions(+), 17 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/spaceship-eq15.C diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index d13bb3d4c0e..bbc8be21900 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/call.cc b/gcc/cp/call.cc index 2c0fa37f53a..492db9b59ad 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -6232,6 +6232,7 @@ add_candidates (tree fns, tree first_arg, const vec *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 *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 *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 % " + "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/decl.cc b/gcc/cp/decl.cc index 6e98ea35a39..890cfcabd35 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 c3fc56a13ff..57917de321f 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), diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted3.C b/gcc/testsuite/g++.dg/cpp0x/defaulted3.C index 75e89c8ff0c..33de973a1fa 100644 --- a/gcc/testsuite/g++.dg/cpp0x/defaulted3.C +++ b/gcc/testsuite/g++.dg/cpp0x/defaulted3.C @@ -4,7 +4,7 @@ template struct A { template - bool operator==(const A&) = delete; // { dg-message "declared" } + bool operator==(const A&) const = delete; // { dg-message "declared" } operator bool () { return true; } }; diff --git a/gcc/testsuite/g++.dg/cpp2a/bit-cast7.C b/gcc/testsuite/g++.dg/cpp2a/bit-cast7.C index 4a3c6820070..6927db3c961 100644 --- a/gcc/testsuite/g++.dg/cpp2a/bit-cast7.C +++ b/gcc/testsuite/g++.dg/cpp2a/bit-cast7.C @@ -16,7 +16,7 @@ struct J struct K { long int a, b : 11, c; - constexpr bool operator == (const K &x) + constexpr bool operator == (const K &x) const { return a == x.a && b == x.b && c == x.c; } @@ -29,7 +29,7 @@ struct L struct M { long long int a, b : 11, c; - constexpr bool operator == (const M &x) + constexpr bool operator == (const M &x) const { return a == x.a && b == x.b && c == x.c; } diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-eq15.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-eq15.C new file mode 100644 index 00000000000..dc509563140 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-eq15.C @@ -0,0 +1,208 @@ +// P2468R2 - The Equality Operator You Are Looking For +// { dg-do compile { target c++20 } } + +struct A { + bool operator== (const A &) { return true; } + bool operator!= (const A &) { return false; } +}; +bool a = A{} != A{}; + +template +struct B { + bool operator== (const T &) const; + bool operator!= (const T &) const; +}; +struct B1 : B { }; +bool b1 = B1{} == B1{}; +bool b2 = B1{} != B1{}; + +template +struct C { + using C1 = C; + using C2 = C; + C () = default; + C (const C2 &); + bool operator== (C1) const; + bool operator!= (C1) const; +}; +using C3 = C; +bool c = C3{} == C3{}; + +struct D { + D (); + D (int *); + bool operator== (const D &) const; // { dg-message "candidate: 'bool D::operator==\\\(const D&\\\) const' \\\(reversed\\\)" } + operator int * () const; +}; +bool d = nullptr != D{}; // { dg-error "ambiguous overload for 'operator!=' in 'nullptr != D\\\(\\\)' \\\(operand types are 'std::nullptr_t' and 'D'\\\)" } + // { dg-message "candidate: 'operator!=\\\(int\\\*, int\\\*\\\)' \\\(built-in\\\)" "" { target *-*-* } .-1 } + +using ubool = unsigned char; + +struct E { + operator bool () const; +}; +unsigned char operator== (E, E);// { dg-message "candidate: 'unsigned char operator==\\\(E, E\\\)'" } + // { dg-message "no known conversion for argument 1 from 'int' to 'E'" "" { target *-*-* } .-1 } +unsigned char e = E{} != E{}; // { dg-error "return type of 'unsigned char operator==\\\(E, E\\\)' is not 'bool'" } + // { dg-message "used as rewritten candidate for comparison of 'E' and 'E'" "" { target *-*-* } .-1 } + +// F-H are the testcase from [over.match.oper] +struct F {}; +template +bool operator== (F, T); // { dg-message "candidate: 'template bool operator==\\\(F, T\\\)'" } + // { dg-message "template argument deduction/substitution failed:" "" { target *-*-* } .-1 } +bool f1 = 0 == F (); // OK, calls reversed == +template +bool operator!= (F, T); +bool f2 = 0 == F (); // { dg-error "no match for 'operator==' in '0 == F\\\(\\\)' \\\(operand types are 'int' and 'F'\\\)" } + // { dg-message "cannot convert '0' \\\(type 'int'\\\) to type 'F'" "" { target *-*-* } .-1 } + +struct G { + bool operator== (const G &); +}; +struct G1 : G { + G1 (); + G1 (G); + bool operator!= (const G &); +}; +bool g1 = G () == G1 (); // OK, != prevents rewrite +bool g2 = G1 () == G (); // { dg-error "ambiguous, even though the second is reversed" } + +struct H {}; +template +bool operator== (H, T); +inline namespace H1 { + template + bool operator!= (H, T); +} +// [over.match.oper] currently says that this is OK because the inline +// namespace isn't searched, but that seems wrong to me, so I'm going to go +// ahead and search it for now. Remove the "0 &&" in add_candidates to +// change this to the currently specified behavior. +// { dg-error "no match" "" { target *-*-* } .+1 } +bool h = 0 == H (); // OK, calls reversed == + +template +struct I { + int operator== (const double &) const; + friend inline int operator== (const double &, const T &) { return 1; } +}; +struct I1 : I { }; +bool i = I1{} == 0.; // { dg-error "return type of 'int operator==\\\(const double&, const I1&\\\)' is not 'bool'" } + // { dg-message "used as rewritten candidate for comparison of 'I1' and 'double'" "" { target *-*-* } .-1 } + +struct J { + bool operator== (const J &) const; + bool operator!= (const J &) const; +}; +struct J1 : J { + J1 (const J &); + bool operator== (const J1 &x) const { + return static_cast (*this) == x; // { dg-error "ambiguous overload for 'operator==' in '\\\*\\\(const J\\\*\\\)\\\(\\\(const J1\\\*\\\)this\\\) == x' \\\(operand types are 'const J' and 'const J1'\\\)" } + } +}; + +struct K { + bool operator== (const K &); +}; +bool k = K{} == K{}; // { dg-error "ambiguous, even though the second is reversed" } + +struct L { + bool operator== (const L &) const; +}; +bool l = L{} == L{}; + +struct M { + bool operator== (M); +}; +bool m = M () == M (); + +struct N { + virtual bool operator== (const N &) const; +}; +struct N1 : N { + bool operator== (const N &) const override; +}; +bool n = N1 () == N1 (); // { dg-error "ambiguous, even though the second is reversed" } + +struct O { + virtual signed char operator== (const O &) const; + signed char operator!= (const O &x) const { return !operator== (x); } +}; +struct O1 : O { + signed char operator== (const O &) const override; +}; +bool o = O1 () != O1 (); + +template +bool +foo (T x, T y) +requires requires { x == y; } +{ + return x == y; +} +bool b3 = foo (B1 (), B1 ()); + +struct P {}; +template +bool operator== (P, T); +template +bool operator!= (P, T); +bool p = 0 == P (); + +struct Q {}; +template +bool operator== (Q, T); +template +bool operator!= (Q, U); +bool q = 0 == Q (); // { dg-error "" } + +struct R { + template + bool operator== (const T &); +}; +bool r = R () == R (); // { dg-error "ambiguous, even though the second is reversed" } + +struct S { + template + bool operator== (const T &) const; + bool operator!= (const S &); +}; +bool s = S () == S (); + +struct T {}; +template +bool operator== (T, int); +bool operator!= (T, int); +bool t = 0 == T (); + +struct U {}; +bool operator== (U, int); +bool u1 = 0 == U (); +namespace U1 { bool operator!= (U, int); } +bool u2 = 0 == U (); +using U1::operator!=; +bool u3 = 0 == U (); // { dg-error "" } + +struct V {}; +template +bool operator== (V, T); +bool v1 = 0 == V (); +namespace V1 { template bool operator!= (V, T); } +bool v2 = 0 == V (); +using V1::operator!=; +bool v3 = 0 == V (); // { dg-error "" } + +template +struct W { +bool operator== (int) requires (N == 1); +bool operator!= (int) requires (N == 2); +}; +int w = 0 == W<1> (); + +struct X { + bool operator== (X const &); + static bool operator!= (X const &, X const &); // { dg-error "'static bool X::operator!=\\\(const X&, const X&\\\)' must be either a non-static member function or a non-member function" } +}; +bool x = X () == X (); // { dg-error "ambiguous, even though the second is reversed" } diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-rewrite1.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-rewrite1.C index c4030cd2f4d..ebe81e4b5e9 100644 --- a/gcc/testsuite/g++.dg/cpp2a/spaceship-rewrite1.C +++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-rewrite1.C @@ -11,5 +11,5 @@ int main() { A a1; A a2; - return a1 == a2; + return a1 == a2; // { dg-error "ambiguous, even though the second is reversed" } } diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-rewrite5.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-rewrite5.C index d0424377e8e..460f6332938 100644 --- a/gcc/testsuite/g++.dg/cpp2a/spaceship-rewrite5.C +++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-rewrite5.C @@ -12,4 +12,4 @@ struct A { A a; A b; -auto c = (a == b); +auto c = (a == b); // { dg-error "ambiguous, even though the second is reversed" "" { target c++20 } } diff --git a/gcc/testsuite/g++.old-deja/g++.jason/byval2.C b/gcc/testsuite/g++.old-deja/g++.jason/byval2.C index 40bf2a36528..0575109ed1a 100644 --- a/gcc/testsuite/g++.old-deja/g++.jason/byval2.C +++ b/gcc/testsuite/g++.old-deja/g++.jason/byval2.C @@ -18,6 +18,6 @@ inline char operator == (const Char a, const char b) { return 0; } char mystrcmp(Char s[31], Char t[31]) { - for (; *s == *t; ++s, ++t) if (*s == '\0') return 0; + for (; *s == *t; ++s, ++t) if (*s == '\0') return 0; // { dg-error "reversed" "" { target c++20 } } return char(*s - *t); } diff --git a/gcc/testsuite/g++.old-deja/g++.other/overload13.C b/gcc/testsuite/g++.old-deja/g++.other/overload13.C index 54ab404af11..f59bd4a49c3 100644 --- a/gcc/testsuite/g++.old-deja/g++.other/overload13.C +++ b/gcc/testsuite/g++.old-deja/g++.other/overload13.C @@ -2,7 +2,7 @@ // Origin: Nathan Sidwell struct A { - bool operator== (A const &); + bool operator== (A const &) const; operator bool () const; operator int * () const; }; base-commit: 071d00e0faabbd45449d2e83f207fca0f8e8ef68 -- 2.31.1