In MSVC compatibility mode, friend function declarations behave as function declarations

Before C++20, MSVC treated any friend function declaration as a function declaration, so the following code would compile despite funGlob being declared after its first call:

```
class Glob {
public:
  friend void funGlob();

  void test() {
    funGlob();
  }
};

void funGlob() {}
```
This proposed patch mimics the MSVC behavior when in MSVC compatibility mode

Reviewed By: rnk

Differential Revision: https://reviews.llvm.org/D124613
This commit is contained in:
Fred Tingaud 2022-05-03 09:29:16 +02:00 committed by Marco Antognini
parent bc8e601257
commit ad47114ad8
3 changed files with 63 additions and 5 deletions

View File

@ -9632,11 +9632,15 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
}
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);
}

View File

@ -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 <typename T>
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<int>();
}
};
void fun() {
}
template <typename T>
void funtemp() {}
} // namespace ns
class Glob {
public:
friend void funGlob();
void test() {
funGlob(); // modern-error {{use of undeclared identifier 'funGlob'}}
}
};
void funGlob() {
}

View File

@ -2658,7 +2658,10 @@ TEST_P(ImportFriendFunctions, Lookup) {
getTuDecl("struct X { friend void f(); };", Lang_CXX03, "input0.cc");
auto *FromD = FirstDeclMatcher<FunctionDecl>().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<CXXRecordDecl>().match(FromTU, ClassPattern);
@ -2702,7 +2705,10 @@ TEST_P(ImportFriendFunctions, LookupWithProtoAfter) {
auto *FromNormal =
LastDeclMatcher<FunctionDecl>().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 @@ TEST_P(ImportFriendFunctions, ImportFriendChangesLookup) {
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());