Skip to content

Commit

Permalink
In MSVC compatibility mode, friend function declarations behave as fu…
Browse files Browse the repository at this point in the history
…nction 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
  • Loading branch information
1 parent bc8e601 commit ad47114
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 5 deletions.
8 changes: 6 additions & 2 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
45 changes: 45 additions & 0 deletions clang/test/SemaCXX/ms-friend-function-decl.cpp
Original file line number Diff line number Diff line change
@@ -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() {
}
15 changes: 12 additions & 3 deletions clang/unittests/AST/ASTImporterTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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));

Expand Down Expand Up @@ -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());
Expand Down

0 comments on commit ad47114

Please sign in to comment.