Index: clang/lib/Sema/SemaLookup.cpp =================================================================== --- clang/lib/Sema/SemaLookup.cpp +++ clang/lib/Sema/SemaLookup.cpp @@ -3634,14 +3634,17 @@ bool Visible = false; for (D = D->getMostRecentDecl(); D; D = cast_or_null(D->getPreviousDecl())) { - if (D->getIdentifierNamespace() & Decl::IDNS_Ordinary) { - if (isVisible(D)) { + // We check the friend case first because in case of MSVC compatibility + // a friend declaration is also flagged as Ordinary but we don't want + // it to be visible outside its associated class. + if (D->getFriendObjectKind()) { + auto *RD = cast(D->getLexicalDeclContext()); + if (AssociatedClasses.count(RD) && isVisible(D)) { Visible = true; break; } - } else if (D->getFriendObjectKind()) { - auto *RD = cast(D->getLexicalDeclContext()); - if (AssociatedClasses.count(RD) && isVisible(D)) { + } else if (D->getIdentifierNamespace() & Decl::IDNS_Ordinary) { + if (isVisible(D)) { Visible = true; break; } Index: clang/test/SemaCXX/ms-friend-function-decl.cpp =================================================================== --- clang/test/SemaCXX/ms-friend-function-decl.cpp +++ clang/test/SemaCXX/ms-friend-function-decl.cpp @@ -1,9 +1,5 @@ -// RUN: %clang_cc1 -std=c++03 -fms-compatibility -fsyntax-only -verify %s -// RUN: %clang_cc1 -std=c++17 -fms-compatibility -fsyntax-only -verify %s -// RUN: %clang_cc1 -std=c++20 -fms-compatibility -fsyntax-only -verify=modern %s -#if __cplusplus < 202002L -// expected-no-diagnostics -#endif +// RUN: %clang_cc1 -std=c++17 -fms-compatibility -fsyntax-only -verify=expected %s +// RUN: %clang_cc1 -std=c++20 -fms-compatibility -fsyntax-only -verify=modern,expected %s namespace ns { @@ -43,3 +39,43 @@ void funGlob() { } + +// At some point, considering friend declaration as full declarations broke ADL detection. +// The following test checks that the friend declaration is only visible inside the class it's declared in. +namespace check_adl { +template +struct A {}; + +struct NotADLVisible { + template + friend constexpr bool operator==(A, NotADLVisible) noexcept { + static_assert(sizeof(T) < 0, "This should never be instantiated"); + return false; + } + + constexpr NotADLVisible(int) {} +}; +} // namespace check_adl + +void shouldNotSeeAnyOperator() { + bool b = check_adl::A() == 2; // expected-error {{invalid operands to binary expression ('check_adl::A' and 'int')}} +} + +// Check that we don't break the case of a function that is both visible to ADL and a friend +namespace check_adl_compliant { +struct A {}; + +struct B { + friend constexpr bool operator==(A, B) noexcept; + + constexpr B(int) {} +}; + +constexpr bool operator==(check_adl_compliant::A, check_adl_compliant::B) noexcept { + return false; +} +} // namespace check_adl_compliant + +void shouldSeeTheOperator() { + bool b = check_adl_compliant::A() == 2; +}