Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -9631,11 +9631,15 @@ } if (isFriend) { + // In MSVC mode for older versions of the standard, friend function + // declarations behave as declarations + bool PerformFriendInjection = + getLangOpts().MSVCCompat && !getLangOpts().CPlusPlus20; if (FunctionTemplate) { - FunctionTemplate->setObjectOfFriendDecl(); + FunctionTemplate->setObjectOfFriendDecl(PerformFriendInjection); FunctionTemplate->setAccess(AS_public); } - NewFD->setObjectOfFriendDecl(); + NewFD->setObjectOfFriendDecl(PerformFriendInjection); NewFD->setAccess(AS_public); } Index: clang/test/SemaCXX/ms-friend-function-decl.cpp =================================================================== --- /dev/null +++ clang/test/SemaCXX/ms-friend-function-decl.cpp @@ -0,0 +1,45 @@ +// 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 + +namespace ns { + +class C { +public: + template + friend void funtemp(); + + friend void fun(); + + void test() { + ::ns::fun(); // modern-error {{no member named 'fun' in namespace 'ns'}} + + // modern-error@+3 {{no member named 'funtemp' in namespace 'ns'}} + // modern-error@+2 {{expected '(' for function-style cast or type construction}} + // modern-error@+1 {{expected expression}} + ::ns::funtemp(); + } +}; + +void fun() { +} + +template +void funtemp() {} + +} // namespace ns + +class Glob { +public: + friend void funGlob(); + + void test() { + funGlob(); // modern-error {{use of undeclared identifier 'funGlob'}} + } +}; + +void funGlob() { +} Index: clang/unittests/AST/ASTImporterTest.cpp =================================================================== --- clang/unittests/AST/ASTImporterTest.cpp +++ clang/unittests/AST/ASTImporterTest.cpp @@ -2658,7 +2658,10 @@ getTuDecl("struct X { friend void f(); };", Lang_CXX03, "input0.cc"); auto *FromD = FirstDeclMatcher().match(FromTU, FunctionPattern); ASSERT_TRUE(FromD->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); - ASSERT_FALSE(FromD->isInIdentifierNamespace(Decl::IDNS_Ordinary)); + // Before CXX20, MSVC treats friend function declarations as function + // declarations + ASSERT_EQ(FromTU->getLangOpts().MSVCCompat, + FromD->isInIdentifierNamespace(Decl::IDNS_Ordinary)); { auto FromName = FromD->getDeclName(); auto *Class = FirstDeclMatcher().match(FromTU, ClassPattern); @@ -2702,7 +2705,10 @@ auto *FromNormal = LastDeclMatcher().match(FromTU, FunctionPattern); ASSERT_TRUE(FromFriend->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); - ASSERT_FALSE(FromFriend->isInIdentifierNamespace(Decl::IDNS_Ordinary)); + // Before CXX20, MSVC treats friend function declarations as function + // declarations + ASSERT_EQ(FromTU->getLangOpts().MSVCCompat, + FromFriend->isInIdentifierNamespace(Decl::IDNS_Ordinary)); ASSERT_FALSE(FromNormal->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); ASSERT_TRUE(FromNormal->isInIdentifierNamespace(Decl::IDNS_Ordinary)); @@ -2793,7 +2799,10 @@ ASSERT_TRUE(FromNormalF->isInIdentifierNamespace(Decl::IDNS_Ordinary)); ASSERT_FALSE(FromNormalF->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); - ASSERT_FALSE(FromFriendF->isInIdentifierNamespace(Decl::IDNS_Ordinary)); + // Before CXX20, MSVC treats friend function declarations as function + // declarations + ASSERT_EQ(FromFriendTU->getLangOpts().MSVCCompat, + FromFriendF->isInIdentifierNamespace(Decl::IDNS_Ordinary)); ASSERT_TRUE(FromFriendF->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); auto LookupRes = FromNormalTU->noload_lookup(FromNormalName); ASSERT_TRUE(LookupRes.isSingleResult());