Skip to content

Commit dd5dee1

Browse files
committed
[Clang] Clean up the fix for deferred access checking
200f3bd introduced a parsing scope to avoid deferring access checking for friend declarations. That turned out to be insufficient because it only sets up the scope until we see the friend keyword, which can be bypassed if the declaration occurs before the keyword. That said, based on the discussion in #12361, we still want to preserve the fix since a friend function declaration doesn't grant the access to other redeclarations. This patch implemented it by not considering the friend declaration functions as effective contexts. So we don't end up checking the canonical function declaration when we're supposed to check only non-function friend declarations.
1 parent 57fd785 commit dd5dee1

File tree

2 files changed

+68
-2
lines changed

2 files changed

+68
-2
lines changed

clang/lib/Sema/SemaAccess.cpp

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1497,9 +1497,37 @@ void Sema::HandleDelayedAccessCheck(DelayedDiagnostic &DD, Decl *D) {
14971497
DC = D->getLexicalDeclContext();
14981498
} else if (FunctionDecl *FN = dyn_cast<FunctionDecl>(D)) {
14991499
DC = FN;
1500+
// C++ [class.access.general]p4:
1501+
// Access control is applied uniformly to declarations and expressions.
1502+
// C++ [class.access.general]p6:
1503+
// All access controls in [class.access] affect the ability to name a
1504+
// class member from the declaration, including parts of the declaration
1505+
// preceding the name of the entity being declared [...]
1506+
//
1507+
// A friend function declaration might name an entity to which it has access
1508+
// in the particular context, but it doesn't implicitly grant access
1509+
// to that entity in other declaration contexts. For example:
1510+
//
1511+
// class A {
1512+
// using T = int;
1513+
// friend void foo(T); // #1
1514+
// };
1515+
// class B {
1516+
// friend void foo(A::T); // #2
1517+
// };
1518+
//
1519+
// The friend declaration at #1 does not give B access to A::T, nor does it
1520+
// befriend B. Therefore, the friend declaration should not serve
1521+
// as an effective context.
1522+
if (FN->getFriendObjectKind())
1523+
DC = FN->getLexicalDeclContext();
15001524
} else if (TemplateDecl *TD = dyn_cast<TemplateDecl>(D)) {
1501-
if (auto *D = dyn_cast_if_present<DeclContext>(TD->getTemplatedDecl()))
1525+
if (auto *D = dyn_cast_if_present<DeclContext>(TD->getTemplatedDecl())) {
15021526
DC = D;
1527+
if (FunctionDecl *FN = dyn_cast<FunctionDecl>(DC);
1528+
FN && FN->getFriendObjectKind())
1529+
DC = FN->getLexicalDeclContext();
1530+
}
15031531
} else if (auto *RD = dyn_cast<RequiresExprBodyDecl>(D)) {
15041532
DC = RD;
15051533
}

clang/test/SemaCXX/access-control-check.cpp

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
// RUN: %clang_cc1 -fsyntax-only -verify %s
1+
// RUN: %clang_cc1 -fsyntax-only -Wno-unused-variable -verify %s
2+
// RUN: %clang_cc1 -fsyntax-only -Wno-unused-variable -std=c++20 -verify=expected,since-cxx20 %s
23

34
class M {
45
int iM;
@@ -59,3 +60,40 @@ struct A {
5960
int i = f<B>();
6061

6162
}
63+
64+
namespace GH12361 {
65+
class D1 {
66+
class E1 {
67+
class F1 {}; // #F1
68+
69+
friend D1::E1::F1 foo1();
70+
friend void foo2(D1::E1::F1);
71+
friend void foo3() noexcept(sizeof(D1::E1::F1) == 1);
72+
friend void foo4();
73+
#if __cplusplus >= 202002L
74+
template <class T>
75+
friend void foo5(T) requires (sizeof(D1::E1::F1) == 1);
76+
#endif
77+
};
78+
79+
D1::E1::F1 friend foo1(); // expected-error{{'F1' is a private member of 'GH12361::D1::E1'}}
80+
// expected-note@#F1 {{implicitly declared private}}
81+
friend void foo2(D1::E1::F1); // expected-error{{'F1' is a private member of 'GH12361::D1::E1'}}
82+
// expected-note@#F1 {{implicitly declared private}}
83+
84+
// FIXME: This should be diagnosed. We entered the function DC too early.
85+
friend void foo3() noexcept(sizeof(D1::E1::F1) == 1);
86+
friend void foo4() {
87+
D1::E1::F1 V;
88+
}
89+
#if __cplusplus >= 202002L
90+
template <class T>
91+
friend void foo5(T)
92+
requires (sizeof(D1::E1::F1) == 1); // since-cxx20-error {{'F1' is a private member of 'GH12361::D1::E1'}}
93+
// since-cxx20-note@#F1 {{implicitly declared private}}
94+
#endif
95+
};
96+
97+
D1::E1::F1 foo1() { return D1::E1::F1(); }
98+
99+
}

0 commit comments

Comments
 (0)