Skip to content

Commit ad47114

Browse files
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
1 parent bc8e601 commit ad47114

File tree

3 files changed

+63
-5
lines changed

3 files changed

+63
-5
lines changed

clang/lib/Sema/SemaDecl.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9632,11 +9632,15 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
96329632
}
96339633

96349634
if (isFriend) {
9635+
// In MSVC mode for older versions of the standard, friend function
9636+
// declarations behave as declarations
9637+
bool PerformFriendInjection =
9638+
getLangOpts().MSVCCompat && !getLangOpts().CPlusPlus20;
96359639
if (FunctionTemplate) {
9636-
FunctionTemplate->setObjectOfFriendDecl();
9640+
FunctionTemplate->setObjectOfFriendDecl(PerformFriendInjection);
96379641
FunctionTemplate->setAccess(AS_public);
96389642
}
9639-
NewFD->setObjectOfFriendDecl();
9643+
NewFD->setObjectOfFriendDecl(PerformFriendInjection);
96409644
NewFD->setAccess(AS_public);
96419645
}
96429646

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// RUN: %clang_cc1 -std=c++03 -fms-compatibility -fsyntax-only -verify %s
2+
// RUN: %clang_cc1 -std=c++17 -fms-compatibility -fsyntax-only -verify %s
3+
// RUN: %clang_cc1 -std=c++20 -fms-compatibility -fsyntax-only -verify=modern %s
4+
#if __cplusplus < 202002L
5+
// expected-no-diagnostics
6+
#endif
7+
8+
namespace ns {
9+
10+
class C {
11+
public:
12+
template <typename T>
13+
friend void funtemp();
14+
15+
friend void fun();
16+
17+
void test() {
18+
::ns::fun(); // modern-error {{no member named 'fun' in namespace 'ns'}}
19+
20+
// modern-error@+3 {{no member named 'funtemp' in namespace 'ns'}}
21+
// modern-error@+2 {{expected '(' for function-style cast or type construction}}
22+
// modern-error@+1 {{expected expression}}
23+
::ns::funtemp<int>();
24+
}
25+
};
26+
27+
void fun() {
28+
}
29+
30+
template <typename T>
31+
void funtemp() {}
32+
33+
} // namespace ns
34+
35+
class Glob {
36+
public:
37+
friend void funGlob();
38+
39+
void test() {
40+
funGlob(); // modern-error {{use of undeclared identifier 'funGlob'}}
41+
}
42+
};
43+
44+
void funGlob() {
45+
}

clang/unittests/AST/ASTImporterTest.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2658,7 +2658,10 @@ TEST_P(ImportFriendFunctions, Lookup) {
26582658
getTuDecl("struct X { friend void f(); };", Lang_CXX03, "input0.cc");
26592659
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, FunctionPattern);
26602660
ASSERT_TRUE(FromD->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
2661-
ASSERT_FALSE(FromD->isInIdentifierNamespace(Decl::IDNS_Ordinary));
2661+
// Before CXX20, MSVC treats friend function declarations as function
2662+
// declarations
2663+
ASSERT_EQ(FromTU->getLangOpts().MSVCCompat,
2664+
FromD->isInIdentifierNamespace(Decl::IDNS_Ordinary));
26622665
{
26632666
auto FromName = FromD->getDeclName();
26642667
auto *Class = FirstDeclMatcher<CXXRecordDecl>().match(FromTU, ClassPattern);
@@ -2702,7 +2705,10 @@ TEST_P(ImportFriendFunctions, LookupWithProtoAfter) {
27022705
auto *FromNormal =
27032706
LastDeclMatcher<FunctionDecl>().match(FromTU, FunctionPattern);
27042707
ASSERT_TRUE(FromFriend->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
2705-
ASSERT_FALSE(FromFriend->isInIdentifierNamespace(Decl::IDNS_Ordinary));
2708+
// Before CXX20, MSVC treats friend function declarations as function
2709+
// declarations
2710+
ASSERT_EQ(FromTU->getLangOpts().MSVCCompat,
2711+
FromFriend->isInIdentifierNamespace(Decl::IDNS_Ordinary));
27062712
ASSERT_FALSE(FromNormal->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
27072713
ASSERT_TRUE(FromNormal->isInIdentifierNamespace(Decl::IDNS_Ordinary));
27082714

@@ -2793,7 +2799,10 @@ TEST_P(ImportFriendFunctions, ImportFriendChangesLookup) {
27932799

27942800
ASSERT_TRUE(FromNormalF->isInIdentifierNamespace(Decl::IDNS_Ordinary));
27952801
ASSERT_FALSE(FromNormalF->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
2796-
ASSERT_FALSE(FromFriendF->isInIdentifierNamespace(Decl::IDNS_Ordinary));
2802+
// Before CXX20, MSVC treats friend function declarations as function
2803+
// declarations
2804+
ASSERT_EQ(FromFriendTU->getLangOpts().MSVCCompat,
2805+
FromFriendF->isInIdentifierNamespace(Decl::IDNS_Ordinary));
27972806
ASSERT_TRUE(FromFriendF->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
27982807
auto LookupRes = FromNormalTU->noload_lookup(FromNormalName);
27992808
ASSERT_TRUE(LookupRes.isSingleResult());

0 commit comments

Comments
 (0)