Skip to content

Commit 200f3bd

Browse files
authored
[Clang][Sema] access checking of friend declaration should not be delayed (llvm#91430)
attempt to fix llvm#12361 Consider this example: ```cpp class D { class E{ class F{}; friend void foo(D::E::F& q); }; friend void foo(D::E::F& q); }; void foo(D::E::F& q) {} ``` The first friend declaration of foo is correct. After that, the second friend declaration delayed access checking and set its previous declaration to be the first one. When doing access checking of `F`(which is private filed of `E`), we put its canonical declaration(the first friend declaration) into `EffectiveContext.Functions`. Actually, we are still checking the first one. This is incorrect due to the delayed checking. Creating a new scope to indicate we are parsing a friend declaration and doing access checking in time.
1 parent f52ca63 commit 200f3bd

File tree

6 files changed

+68
-7
lines changed

6 files changed

+68
-7
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,7 @@ Bug Fixes to C++ Support
706706
within initializers for variables that are usable in constant expressions or are constant
707707
initialized, rather than evaluating them as a part of the larger manifestly constant evaluated
708708
expression.
709+
- Fix a bug in access control checking due to dealyed checking of friend declaration. Fixes (#GH12361).
709710

710711
Bug Fixes to AST Handling
711712
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/Sema/Scope.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,9 @@ class Scope {
159159

160160
/// This is a scope of type alias declaration.
161161
TypeAliasScope = 0x20000000,
162+
163+
/// This is a scope of friend declaration.
164+
FriendScope = 0x40000000,
162165
};
163166

164167
private:
@@ -586,6 +589,9 @@ class Scope {
586589
/// Determine whether this scope is a type alias scope.
587590
bool isTypeAliasScope() const { return getFlags() & Scope::TypeAliasScope; }
588591

592+
/// Determine whether this scope is a friend scope.
593+
bool isFriendScope() const { return getFlags() & Scope::FriendScope; }
594+
589595
/// Returns if rhs has a higher scope depth than this.
590596
///
591597
/// The caller is responsible for calling this only if one of the two scopes

clang/lib/Parse/ParseDecl.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4331,9 +4331,12 @@ void Parser::ParseDeclarationSpecifiers(
43314331

43324332
// friend
43334333
case tok::kw_friend:
4334-
if (DSContext == DeclSpecContext::DSC_class)
4334+
if (DSContext == DeclSpecContext::DSC_class) {
43354335
isInvalid = DS.SetFriendSpec(Loc, PrevSpec, DiagID);
4336-
else {
4336+
Scope *CurS = getCurScope();
4337+
if (!isInvalid && CurS)
4338+
CurS->setFlags(CurS->getFlags() | Scope::FriendScope);
4339+
} else {
43374340
PrevSpec = ""; // not actually used by the diagnostic
43384341
DiagID = diag::err_friend_invalid_in_context;
43394342
isInvalid = true;

clang/lib/Sema/Scope.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ void Scope::dumpImpl(raw_ostream &OS) const {
229229
{ClassInheritanceScope, "ClassInheritanceScope"},
230230
{CatchScope, "CatchScope"},
231231
{OpenACCComputeConstructScope, "OpenACCComputeConstructScope"},
232+
{FriendScope, "FriendScope"},
232233
};
233234

234235
for (auto Info : FlagInfo) {

clang/lib/Sema/SemaAccess.cpp

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1473,12 +1473,32 @@ static Sema::AccessResult CheckAccess(Sema &S, SourceLocation Loc,
14731473
// specifier, like this:
14741474
// A::private_type A::foo() { ... }
14751475
//
1476-
// Or we might be parsing something that will turn out to be a friend:
1477-
// void foo(A::private_type);
1478-
// void B::foo(A::private_type);
1476+
// friend declaration should not be delayed because it may lead to incorrect
1477+
// redeclaration chain, such as:
1478+
// class D {
1479+
// class E{
1480+
// class F{};
1481+
// friend void foo(D::E::F& q);
1482+
// };
1483+
// friend void foo(D::E::F& q);
1484+
// };
14791485
if (S.DelayedDiagnostics.shouldDelayDiagnostics()) {
1480-
S.DelayedDiagnostics.add(DelayedDiagnostic::makeAccess(Loc, Entity));
1481-
return Sema::AR_delayed;
1486+
// [class.friend]p9:
1487+
// A member nominated by a friend declaration shall be accessible in the
1488+
// class containing the friend declaration. The meaning of the friend
1489+
// declaration is the same whether the friend declaration appears in the
1490+
// private, protected, or public ([class.mem]) portion of the class
1491+
// member-specification.
1492+
Scope *TS = S.getCurScope();
1493+
bool IsFriendDeclaration = false;
1494+
while (TS && !IsFriendDeclaration) {
1495+
IsFriendDeclaration = TS->isFriendScope();
1496+
TS = TS->getParent();
1497+
}
1498+
if (!IsFriendDeclaration) {
1499+
S.DelayedDiagnostics.add(DelayedDiagnostic::makeAccess(Loc, Entity));
1500+
return Sema::AR_delayed;
1501+
}
14821502
}
14831503

14841504
EffectiveContext EC(S.CurContext);

clang/test/SemaCXX/PR12361.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++98 %s
2+
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++17 %s
3+
4+
class D {
5+
class E{
6+
class F{}; // expected-note{{implicitly declared private here}}
7+
friend void foo(D::E::F& q);
8+
};
9+
friend void foo(D::E::F& q); // expected-error{{'F' is a private member of 'D::E'}}
10+
};
11+
12+
void foo(D::E::F& q) {}
13+
14+
class D1 {
15+
class E1{
16+
class F1{}; // expected-note{{implicitly declared private here}}
17+
friend D1::E1::F1 foo1();
18+
};
19+
friend D1::E1::F1 foo1(); // expected-error{{'F1' is a private member of 'D1::E1'}}
20+
};
21+
22+
D1::E1::F1 foo1() { return D1::E1::F1(); }
23+
24+
class D2 {
25+
class E2{
26+
class F2{};
27+
friend void foo2();
28+
};
29+
friend void foo2(){ D2::E2::F2 c;}
30+
};

0 commit comments

Comments
 (0)