llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Utkarsh Saxena (usx95) <details> <summary>Changes</summary> The namespace lookup should give a matching friend operator!= as friend functions belong to the namespace scope (as opposed to the lexical class scope). Thus to resolve ambiguity of operator== with itself, defining a corresponding friend operator!= should suffice. Fixes: https://github.com/llvm/llvm-project/issues/70210 --- Full diff: https://github.com/llvm/llvm-project/pull/70216.diff 2 Files Affected: - (modified) clang/lib/Sema/SemaOverload.cpp (+7-4) - (modified) clang/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3-2a.cpp (+163) ``````````diff diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index db386fef0661c05..92a58b3c3cf5ed7 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -14,6 +14,7 @@ #include "clang/AST/ASTLambda.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclFriend.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DependenceFlags.h" #include "clang/AST/Expr.h" @@ -962,13 +963,16 @@ static bool shouldAddReversedEqEq(Sema &S, SourceLocation OpLoc, return true; } // Otherwise the search scope is the namespace scope of which F is a member. - for (NamedDecl *Op : EqFD->getEnclosingNamespaceContext()->lookup(NotEqOp)) { + DeclContext *EqDC = EqFD->getEnclosingNamespaceContext(); + for (NamedDecl *Op : EqDC->lookup(NotEqOp)) { auto *NotEqFD = Op->getAsFunction(); + DeclContext *NotEqDC = Op->getFriendObjectKind() + ? NotEqFD->getEnclosingNamespaceContext() + : Op->getLexicalDeclContext(); if (auto *UD = dyn_cast<UsingShadowDecl>(Op)) NotEqFD = UD->getUnderlyingDecl()->getAsFunction(); if (FunctionsCorrespond(S.Context, EqFD, NotEqFD) && S.isVisible(NotEqFD) && - declaresSameEntity(cast<Decl>(EqFD->getEnclosingNamespaceContext()), - cast<Decl>(Op->getLexicalDeclContext()))) + declaresSameEntity(cast<Decl>(EqDC), cast<Decl>(NotEqDC))) return false; } return true; @@ -10086,7 +10090,6 @@ static bool haveSameParameterTypes(ASTContext &Context, const FunctionDecl *F1, const FunctionDecl *F2) { if (declaresSameEntity(F1, F2)) return true; - auto NextParam = [&](const FunctionDecl *F, unsigned &I, bool First) { if (First) { if (std::optional<QualType> T = getImplicitObjectParamType(Context, F)) diff --git a/clang/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3-2a.cpp b/clang/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3-2a.cpp index d83a176ec07eec9..ec3cf18b377dfce 100644 --- a/clang/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3-2a.cpp +++ b/clang/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3-2a.cpp @@ -403,6 +403,169 @@ bool h(X x) { return x == x; // expected-warning {{ambiguous}} } +namespace friend_opNE_GH{ +namespace test1 { +struct S { + operator int(); + friend bool operator==(const S &, int); // expected-note {{reversed}} +}; +struct A : S {}; +struct B : S {}; +bool x = A{} == B{}; // expected-warning {{ambiguous}} +} + +namespace test2 { +struct S { + operator int(); + friend bool operator==(const S &, int); + friend bool operator!=(const S &, int); +}; +struct A : public P {}; +struct B : public P {}; +bool check(A a, B b) { return a == b; } // expected-warning {{use of overloaded operator '==' (with operand types 'A' and 'B') to be ambiguous}} +} + +namespace non_member_template_1 { +struct P {}; +template<class S> +bool operator==(const P&, const S &); // expected-note {{candidate}} \ + // expected-note {{ambiguous candidate function with reversed arguments}} + +struct A : public P {}; +struct B : public P {}; +bool check(A a, B b) { return a == b; } // expected-warning {{use of overloaded operator '==' (with operand types 'A' and 'B') to be ambiguous}} + +template<class S> +bool operator!=(const P&, const S &); +bool fine(A a, B b) { return a == b; } // Ok. Found a matching operator!=. +} +} + +namespace friend_opNE_GH{ +namespace test1 { +struct S { + operator int(); + friend bool operator==(const S &, int); // expected-note {{reversed}} +}; +struct A : S {}; +struct B : S {}; +bool x = A{} == B{}; // expected-warning {{ambiguous}} +} + +namespace test2 { +struct S { + operator int(); + friend bool operator==(const S &, int); + friend bool operator!=(const S &, int); +}; +struct A : S {}; +struct B : S {}; +bool x = A{} == B{}; +} +} + +namespace ADL_GH68901{ +namespace test1 { +namespace A { +struct S {}; +bool operator==(S, int); // expected-note {{no known conversion from 'int' to 'S' for 1st argument}} +bool a = 0 == A::S(); // Ok. Operator!= not visible. +bool operator!=(S, int); +} // namespace A +bool a = 0 == A::S(); // expected-error {{invalid operands to binary expression ('int' and 'A::S')}} +} // namespace test1 + +namespace test2 { +namespace B { +struct Derived {}; +struct Base : Derived {}; + +bool operator==(Derived& a, Base& b); +bool operator!=(Derived& a, Base& b); +} // namespace B + +bool foo() { + B::Base a,b; + return a == b; +} +} // namespace test2 + + +namespace template_ { +namespace ns { +template <class T> struct A {}; +template <class T> struct B : A<T> {}; + +template <class T> bool operator==(B<T>, A<T>); // expected-note {{candidate template ignored: could not match 'B' against 'A'}} +template <class T> bool operator!=(B<T>, A<T>); +} + +void test() { + ns::A<int> a; + ns::B<int> b; + a == b; // expected-error {{invalid operands to binary expression}} +} +} // namespace test3 + +namespace using_not_eq { +namespace A { +struct S {}; +namespace B { +bool operator!=(S, int); +} +bool operator==(S, int); // expected-note {{candidate}} +using B::operator!=; +} // namespace A +bool a = 0 == A::S(); // expected-error {{invalid operands to binary expression}} +} // namespace reversed_lookup_not_like_ADL + +namespace using_eqeq { +namespace A { +struct S {}; +namespace B { +bool operator==(S, int); // expected-note {{candidate}} +bool operator!=(S, int); +} +using B::operator==; +} // namespace A +bool a = 0 == A::S(); // expected-error {{invalid operands to binary expression}} +} + +} //namespace ADL_GH68901 + +namespace function_scope_operator_eqeq { +// For non-members, we always lookup for matching operator!= in the namespace scope of +// operator== (and not in the scope of operator==). +struct X { operator int(); }; +namespace test1{ +bool h(X x) { + bool operator==(X, int); // expected-note {{reversed}} + return x == x; // expected-warning {{ambiguous}} +} + +bool g(X x) { + bool operator==(X, int); // expected-note {{reversed}} + bool operator!=(X, int); + return x == x; // expected-warning {{ambiguous}} +} +} // namespace test1 + +namespace test2 { +bool operator!=(X, int); + +bool h(X x) { + bool operator==(X, int); + return x == x; +} + +bool i(X x) { + bool operator==(X, int); + bool operator!=(X, int); + return x == x; +} +} // namespace test2 +} // namespace function_scope_operator_eqeq + bool g(X x) { bool operator==(X, int); // expected-note {{reversed}} bool operator!=(X, int); `````````` </details> https://github.com/llvm/llvm-project/pull/70216 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits