Skip to content

[Clang] Clean up the fix for deferred access checking #141340

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions clang/include/clang/Sema/Scope.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,6 @@ class Scope {

/// This is a scope of type alias declaration.
TypeAliasScope = 0x20000000,

/// This is a scope of friend declaration.
FriendScope = 0x40000000,
};

private:
Expand Down Expand Up @@ -590,9 +587,6 @@ class Scope {
/// Determine whether this scope is a type alias scope.
bool isTypeAliasScope() const { return getFlags() & Scope::TypeAliasScope; }

/// Determine whether this scope is a friend scope.
bool isFriendScope() const { return getFlags() & Scope::FriendScope; }

/// Returns if rhs has a higher scope depth than this.
///
/// The caller is responsible for calling this only if one of the two scopes
Expand Down
7 changes: 2 additions & 5 deletions clang/lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4219,12 +4219,9 @@ void Parser::ParseDeclarationSpecifiers(

// friend
case tok::kw_friend:
if (DSContext == DeclSpecContext::DSC_class) {
if (DSContext == DeclSpecContext::DSC_class)
isInvalid = DS.SetFriendSpec(Loc, PrevSpec, DiagID);
Scope *CurS = getCurScope();
if (!isInvalid && CurS)
CurS->setFlags(CurS->getFlags() | Scope::FriendScope);
} else {
else {
PrevSpec = ""; // not actually used by the diagnostic
DiagID = diag::err_friend_invalid_in_context;
isInvalid = true;
Expand Down
1 change: 0 additions & 1 deletion clang/lib/Sema/Scope.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,6 @@ void Scope::dumpImpl(raw_ostream &OS) const {
{LambdaScope, "LambdaScope"},
{OpenACCComputeConstructScope, "OpenACCComputeConstructScope"},
{TypeAliasScope, "TypeAliasScope"},
{FriendScope, "FriendScope"},
};

for (auto Info : FlagInfo) {
Expand Down
60 changes: 34 additions & 26 deletions clang/lib/Sema/SemaAccess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1469,32 +1469,12 @@ static Sema::AccessResult CheckAccess(Sema &S, SourceLocation Loc,
// specifier, like this:
// A::private_type A::foo() { ... }
//
// friend declaration should not be delayed because it may lead to incorrect
// redeclaration chain, such as:
// class D {
// class E{
// class F{};
// friend void foo(D::E::F& q);
// };
// friend void foo(D::E::F& q);
// };
// Or we might be parsing something that will turn out to be a friend:
// void foo(A::private_type);
// void B::foo(A::private_type);
if (S.DelayedDiagnostics.shouldDelayDiagnostics()) {
// [class.friend]p9:
// A member nominated by a friend declaration shall be accessible in the
// class containing the friend declaration. The meaning of the friend
// declaration is the same whether the friend declaration appears in the
// private, protected, or public ([class.mem]) portion of the class
// member-specification.
Scope *TS = S.getCurScope();
bool IsFriendDeclaration = false;
while (TS && !IsFriendDeclaration) {
IsFriendDeclaration = TS->isFriendScope();
TS = TS->getParent();
}
if (!IsFriendDeclaration) {
S.DelayedDiagnostics.add(DelayedDiagnostic::makeAccess(Loc, Entity));
return Sema::AR_delayed;
}
S.DelayedDiagnostics.add(DelayedDiagnostic::makeAccess(Loc, Entity));
return Sema::AR_delayed;
}

EffectiveContext EC(S.CurContext);
Expand All @@ -1517,9 +1497,37 @@ void Sema::HandleDelayedAccessCheck(DelayedDiagnostic &DD, Decl *D) {
DC = D->getLexicalDeclContext();
} else if (FunctionDecl *FN = dyn_cast<FunctionDecl>(D)) {
DC = FN;
// C++ [class.access.general]p4:
// Access control is applied uniformly to declarations and expressions.
// C++ [class.access.general]p6:
// All access controls in [class.access] affect the ability to name a
// class member from the declaration, including parts of the declaration
// preceding the name of the entity being declared [...]
//
// A friend function declaration might name an entity to which it has access
// in the particular context, but it doesn't implicitly grant access
// to that entity in other declaration contexts. For example:
//
// class A {
// using T = int;
// friend void foo(T); // #1
// };
// class B {
// friend void foo(A::T); // #2
// };
//
// The friend declaration at #1 does not give B access to A::T, nor does it
// befriend B. Therefore, the friend declaration should not serve
// as an effective context.
if (FN->getFriendObjectKind())
DC = FN->getLexicalDeclContext();
} else if (TemplateDecl *TD = dyn_cast<TemplateDecl>(D)) {
if (auto *D = dyn_cast_if_present<DeclContext>(TD->getTemplatedDecl()))
if (auto *D = dyn_cast_if_present<DeclContext>(TD->getTemplatedDecl())) {
DC = D;
if (FunctionDecl *FN = dyn_cast<FunctionDecl>(DC);
FN && FN->getFriendObjectKind())
DC = FN->getLexicalDeclContext();
}
} else if (auto *RD = dyn_cast<RequiresExprBodyDecl>(D)) {
DC = RD;
}
Expand Down
30 changes: 0 additions & 30 deletions clang/test/SemaCXX/PR12361.cpp

This file was deleted.

40 changes: 39 additions & 1 deletion clang/test/SemaCXX/access-control-check.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
// RUN: %clang_cc1 -fsyntax-only -Wno-unused-variable -verify %s
// RUN: %clang_cc1 -fsyntax-only -Wno-unused-variable -std=c++20 -verify=expected,since-cxx20 %s

class M {
int iM;
Expand Down Expand Up @@ -59,3 +60,40 @@ struct A {
int i = f<B>();

}

namespace GH12361 {
class D1 {
class E1 {
class F1 {}; // #F1

friend D1::E1::F1 foo1();
friend void foo2(D1::E1::F1);
friend void foo3() noexcept(sizeof(D1::E1::F1) == 1);
friend void foo4();
#if __cplusplus >= 202002L
template <class T>
friend void foo5(T) requires (sizeof(D1::E1::F1) == 1);
#endif
};

D1::E1::F1 friend foo1(); // expected-error{{'F1' is a private member of 'GH12361::D1::E1'}}
// expected-note@#F1 {{implicitly declared private}}
friend void foo2(D1::E1::F1); // expected-error{{'F1' is a private member of 'GH12361::D1::E1'}}
// expected-note@#F1 {{implicitly declared private}}

// FIXME: This should be diagnosed. We entered the function DC too early.
friend void foo3() noexcept(sizeof(D1::E1::F1) == 1);
friend void foo4() {
D1::E1::F1 V;
}
#if __cplusplus >= 202002L
template <class T>
friend void foo5(T)
requires (sizeof(D1::E1::F1) == 1); // since-cxx20-error {{'F1' is a private member of 'GH12361::D1::E1'}}
// since-cxx20-note@#F1 {{implicitly declared private}}
#endif
};

D1::E1::F1 foo1() { return D1::E1::F1(); }

}
Loading