Skip to content

Commit e8bcc52

Browse files
authored
[cxx-interop] Fix access check for nested private C++ enums (swiftlang#80366)
This patch fixes the access check for nested private C++ enums to look for the SWIFT_PRIVATE_FILEID of the enclosing C++ class, if any. Previously, the check was looking at for SWIFT_PRIVATE_FILEID on the enum decl itself (which is meaningless); that prevented nested private enum members from being accessible in Swift. This patch also specializes the type signature of getPrivateFileIDAttrs to clarify the fact that SWIFT_PRIVATE_FILEID is not a meaningful annotation on anything other than CXXRecordDecl, because that is the only kind of decl that can assign access specifiers to its members. rdar://148081340
1 parent 07bb353 commit e8bcc52

File tree

6 files changed

+88
-72
lines changed

6 files changed

+88
-72
lines changed

include/swift/ClangImporter/ClangImporter.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -763,7 +763,7 @@ AccessLevel convertClangAccess(clang::AccessSpecifier access);
763763
/// The returned fileIDs may not be of a valid format (e.g., missing a '/'),
764764
/// and should be parsed using swift::SourceFile::FileIDStr::parse().
765765
SmallVector<std::pair<StringRef, clang::SourceLocation>, 1>
766-
getPrivateFileIDAttrs(const clang::Decl *decl);
766+
getPrivateFileIDAttrs(const clang::CXXRecordDecl *decl);
767767

768768
/// Use some heuristics to determine whether the clang::Decl associated with
769769
/// \a decl would not exist without C++ interop.

lib/AST/DeclContext.cpp

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1490,21 +1490,34 @@ bool AccessScope::allowsPrivateAccess(const DeclContext *useDC, const DeclContex
14901490
// Do not allow access if the sourceDC is in a different file
14911491
auto *useSF = useDC->getOutermostParentSourceFile();
14921492
if (useSF != sourceDC->getOutermostParentSourceFile()) {
1493-
// This might be a C++ declaration with a SWIFT_PRIVATE_FILEID
1494-
// attribute, which asks us to treat it as if it were defined in the file
1493+
// sourceDC might be a C++ record with a SWIFT_PRIVATE_FILEID attribute,
1494+
// which asks us to treat it as if it were defined in the file
14951495
// with the specified FileID.
1496-
14971496
auto clangDecl = sourceNTD->getDecl()->getClangDecl();
1498-
if (!clangDecl)
1497+
1498+
if (isa_and_nonnull<clang::EnumDecl>(clangDecl)) {
1499+
// Consider: class C { private: enum class E { M }; };
1500+
// If sourceDC is a C++ enum (e.g, E), then we are looking at one of its
1501+
// members (e.g., E.M). If this is the case, then we should consider
1502+
// the SWIFT_PRIVATE_FILEID of its enclosing class decl (e.g., C), if any.
1503+
// Doing so allows the nested private enum's members to inherit the access
1504+
// of the nested enum type itself.
1505+
clangDecl = dyn_cast<clang::CXXRecordDecl>(clangDecl->getDeclContext());
1506+
sourceDC = sourceNTD->getDeclContext();
1507+
}
1508+
1509+
if (!isa_and_nonnull<clang::CXXRecordDecl>(clangDecl))
14991510
return false;
15001511

1512+
auto recordDecl = cast<clang::CXXRecordDecl>(clangDecl);
1513+
15011514
// Diagnostics should enforce that there is at most SWIFT_PRIVATE_FILEID,
15021515
// but this handles the case where there is more than anyway (whether that
15031516
// is a feature or a bug). Allow access check to proceed if useSF is blessed
15041517
// by any of the SWIFT_PRIVATE_FILEID annotations (i.e., disallow private
15051518
// access if none of them bless useSF).
15061519
if (!llvm::any_of(
1507-
importer::getPrivateFileIDAttrs(clangDecl), [&](auto &blessed) {
1520+
importer::getPrivateFileIDAttrs(recordDecl), [&](auto &blessed) {
15081521
auto blessedFileID = SourceFile::FileIDStr::parse(blessed.first);
15091522
return blessedFileID && blessedFileID->matches(useSF);
15101523
})) {

lib/ClangImporter/ClangImporter.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6285,9 +6285,11 @@ TinyPtrVector<ValueDecl *> ClangRecordMemberLookup::evaluate(
62856285
// to import all non-public members by default; if that is disabled, we only
62866286
// import non-public members annotated with SWIFT_PRIVATE_FILEID (since those
62876287
// are the only classes that need non-public members.)
6288+
auto *cxxRecordDecl =
6289+
dyn_cast<clang::CXXRecordDecl>(inheritingDecl->getClangDecl());
62886290
auto skipIfNonPublic =
62896291
!ctx.LangOpts.hasFeature(Feature::ImportNonPublicCxxMembers) &&
6290-
importer::getPrivateFileIDAttrs(inheritingDecl->getClangDecl()).empty();
6292+
cxxRecordDecl && importer::getPrivateFileIDAttrs(cxxRecordDecl).empty();
62916293

62926294
auto directResults = evaluateOrDefault(
62936295
ctx.evaluator,
@@ -8758,9 +8760,8 @@ void ClangInheritanceInfo::setUnavailableIfNecessary(
87588760
}
87598761

87608762
SmallVector<std::pair<StringRef, clang::SourceLocation>, 1>
8761-
importer::getPrivateFileIDAttrs(const clang::Decl *decl) {
8763+
importer::getPrivateFileIDAttrs(const clang::CXXRecordDecl *decl) {
87628764
llvm::SmallVector<std::pair<StringRef, clang::SourceLocation>, 1> files;
8763-
87648765
constexpr auto prefix = StringRef("private_fileid:");
87658766

87668767
if (decl->hasAttrs()) {

lib/ClangImporter/ImportDecl.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10056,10 +10056,11 @@ void ClangImporter::Implementation::loadAllMembersOfRecordDecl(
1005610056
// to import all non-public members by default; if that is disabled, we only
1005710057
// import non-public members annotated with SWIFT_PRIVATE_FILEID (since those
1005810058
// are the only classes that need non-public members.)
10059-
auto skipIfNonPublic =
10060-
!swiftDecl->getASTContext().LangOpts.hasFeature(
10061-
Feature::ImportNonPublicCxxMembers) &&
10062-
importer::getPrivateFileIDAttrs(swiftDecl->getClangDecl()).empty();
10059+
auto *baseRecord = dyn_cast<clang::CXXRecordDecl>(swiftDecl->getClangDecl());
10060+
auto skipIfNonPublic = !swiftDecl->getASTContext().LangOpts.hasFeature(
10061+
Feature::ImportNonPublicCxxMembers) &&
10062+
baseRecord &&
10063+
importer::getPrivateFileIDAttrs(baseRecord).empty();
1006310064

1006410065
// Import all of the members.
1006510066
llvm::SmallVector<Decl *, 16> members;

test/Interop/Cxx/class/access/Inputs/non-public.h

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@ __attribute__((__swift_attr__("private_fileid:main/blessed.swift"))) MyClass {
2727
typedef int publTypedef;
2828
struct publStruct {};
2929

30-
enum publEnum { publEnumValue1 };
31-
enum class publEnumClass { publEnumClassValue1 };
30+
enum publEnum { variantPublEnum };
3231
enum { publEnumAnonValue1 };
32+
enum class publEnumClass { variantPublEnumClass };
3333
enum publEnumClosed {
34-
publEnumClosedValue1
34+
variantPublEnumClosed
3535
} __attribute__((enum_extensibility(closed)));
3636
enum publEnumOpen {
37-
publEnumOpenValue1
37+
variantPublEnumOpen
3838
} __attribute__((enum_extensibility(open)));
3939
enum publEnumFlag {} __attribute__((flag_enum));
4040

@@ -48,14 +48,14 @@ __attribute__((__swift_attr__("private_fileid:main/blessed.swift"))) MyClass {
4848
typedef int privTypedef;
4949
struct privStruct {};
5050

51-
enum privEnum { privEnumValue1 };
52-
enum class privEnumClass { privEnumClassValue1 };
51+
enum privEnum { variantPrivEnum };
5352
enum { privEnumAnonValue1 };
53+
enum class privEnumClass { variantPrivEnumClass };
5454
enum privEnumClosed {
55-
privEnumClosedValue1
55+
variantPrivEnumClosed
5656
} __attribute__((enum_extensibility(closed)));
5757
enum privEnumOpen {
58-
privEnumOpenValue1
58+
variantPrivEnumOpen
5959
} __attribute__((enum_extensibility(open)));
6060
enum privEnumFlag {} __attribute__((flag_enum));
6161
};

test/Interop/Cxx/class/access/private-fileid-typecheck.swift

Lines changed: 52 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -104,20 +104,20 @@ extension MyClass {
104104
let _: privEnumFlag
105105

106106
// TODO: Enum variants are not being correctly imported. Test the following when that is fixed:
107-
// let _ = publEnum.publEnumValue1
108-
// let _ = privEnum.privEnumValue1
107+
// let _ = variantPublEnum
108+
// let _ = variantPrivEnum
109109
//
110-
// let _ = publEnumClass.publEnumClassValue1
111-
// let _ = privEnumClass.privEnumClassValue1
112-
//
113-
// let _ = publEnumAnonValue1
114-
// let _ = privEnumAnonValue1
115-
//
116-
// let _ = publEnumClosed.Value1
117-
// let _ = privEnumClosed.Value1
118-
//
119-
// let _ = publEnumOpen.Value1
120-
// let _ = privEnumOpen.Value1
110+
// let _ = publEnumAnonVariant
111+
// let _ = privEnumAnonVariant
112+
113+
let _ = publEnumClass.variantPublEnumClass
114+
let _ = privEnumClass.variantPrivEnumClass
115+
116+
let _ = publEnumClosed.variantPublEnumClosed
117+
let _ = privEnumClosed.variantPrivEnumClosed
118+
119+
let _ = publEnumOpen.variantPublEnumOpen
120+
let _ = privEnumOpen.variantPrivEnumOpen
121121
}
122122

123123
func fcutd(_ _: publTypedef) { }
@@ -162,20 +162,21 @@ func notExt(_ c: inout MyClass) {
162162
let _: MyClass.privEnumFlag // expected-error {{'privEnumFlag' is inaccessible due to 'private' protection level}}
163163

164164
// TODO: Enum variants are not being correctly imported. Test the following when that is fixed:
165-
// let _ = MyClass.publEnum.publEnumValue1
166-
// let _ = MyClass.privEnum.privEnumValue1
167165
//
168-
// let _ = MyClass.publEnumClass.publEnumClassValue1
169-
// let _ = MyClass.privEnumClass.privEnumClassValue1
166+
// let _ = MyClass.variantPublEnum
167+
// let _ = MyClass.variantPrivEnum // TODO-error {{'variantPrivEnum' is inaccessible due to 'private' protection level}}
170168
//
171-
// let _ = MyClass.publEnumAnonValue1
172-
// let _ = MyClass.privEnumAnonValue1
173-
//
174-
// let _ = MyClass.publEnumClosed.Value1
175-
// let _ = MyClass.privEnumClosed.Value1
176-
//
177-
// let _ = MyClass.publEnumOpen.Value1
178-
// let _ = MyClass.privEnumOpen.Value1
169+
// let _ = MyClass.publEnumAnonVariant
170+
// let _ = MyClass.privEnumAnonVariant // TODO-error {{'privEnumAnonVariant' is inaccessible due to 'private' protection level}}
171+
172+
let _ = MyClass.publEnumClass.variantPublEnumClass
173+
let _ = MyClass.privEnumClass.variantPrivEnumClass // expected-error {{'privEnumClass' is inaccessible due to 'private' protection level}}
174+
175+
let _ = MyClass.publEnumClosed.variantPublEnumClosed
176+
let _ = MyClass.privEnumClosed.variantPrivEnumClosed // expected-error {{'privEnumClosed' is inaccessible due to 'private' protection level}}
177+
178+
let _ = MyClass.publEnumOpen.variantPublEnumOpen
179+
let _ = MyClass.privEnumOpen.variantPrivEnumOpen // expected-error {{'privEnumOpen' is inaccessible due to 'private' protection level}}
179180
}
180181

181182
//--- cursed.swift
@@ -228,20 +229,20 @@ extension MyClass {
228229
let _: privEnumFlag // expected-error {{'privEnumFlag' is inaccessible due to 'private' protection level}}
229230

230231
// TODO: Enum variants are not being correctly imported. Test the following when that is fixed:
231-
// let _ = publEnum.publEnumValue1
232-
// let _ = privEnum.privEnumValue1
233-
//
234-
// let _ = publEnumClass.publEnumClassValue1
235-
// let _ = privEnumClass.privEnumClassValue1
232+
// let _ = variantPublEnum
233+
// let _ = variantPrivEnum // TODO-error {{'variantPrivEnum' is inaccessible due to 'private' protection level}}
236234
//
237-
// let _ = publEnumAnonValue1
238-
// let _ = privEnumAnonValue1
239-
//
240-
// let _ = publEnumClosed.Value1
241-
// let _ = privEnumClosed.Value1
242-
//
243-
// let _ = publEnumOpen.Value1
244-
// let _ = privEnumOpen.Value1
235+
// let _ = publEnumAnonVariant
236+
// let _ = privEnumAnonVariant // TODO-error {{'privEnumAnonVariant' is inaccessible due to 'private' protection level}}
237+
238+
let _ = publEnumClass.variantPublEnumClass
239+
let _ = privEnumClass.variantPrivEnumClass// expected-error {{'privEnumClass' is inaccessible due to 'private' protection level}}
240+
241+
let _ = publEnumClosed.variantPublEnumClosed
242+
let _ = privEnumClosed.variantPrivEnumClosed // expected-error {{'privEnumClosed' is inaccessible due to 'private' protection level}}
243+
244+
let _ = publEnumOpen.variantPublEnumOpen
245+
let _ = privEnumOpen.variantPrivEnumOpen // expected-error {{'privEnumOpen' is inaccessible due to 'private' protection level}}
245246
}
246247
}
247248

@@ -283,18 +284,18 @@ func notExt(_ c: inout MyClass) {
283284
let _: MyClass.privEnumFlag // expected-error {{'privEnumFlag' is inaccessible due to 'private' protection level}}
284285

285286
// TODO: Enum variants are not being correctly imported. Test the following when that is fixed:
286-
// let _ = MyClass.publEnum.publEnumValue1
287-
// let _ = MyClass.privEnum.privEnumValue1
288-
//
289-
// let _ = MyClass.publEnumClass.publEnumClassValue1
290-
// let _ = MyClass.privEnumClass.privEnumClassValue1
287+
// let _ = MyClass.variantPublEnum
288+
// let _ = MyClass.variantPrivEnum // TODO-error {{'variantPrivEnum' is inaccessible due to 'private' protection level}}
291289
//
292-
// let _ = MyClass.publEnumAnonValue1
293-
// let _ = MyClass.privEnumAnonValue1
294-
//
295-
// let _ = MyClass.publEnumClosed.Value1
296-
// let _ = MyClass.privEnumClosed.Value1
297-
//
298-
// let _ = MyClass.publEnumOpen.Value1
299-
// let _ = MyClass.privEnumOpen.Value1
290+
// let _ = MyClass.publEnumAnonVariant
291+
// let _ = MyClass.privEnumAnonVariant // TODO-error {{'privEnumAnonVariant' is inaccessible due to 'private' protection level}}
292+
293+
let _ = MyClass.publEnumClass.variantPublEnumClass
294+
let _ = MyClass.privEnumClass.variantPrivEnumClass// expected-error {{'privEnumClass' is inaccessible due to 'private' protection level}}
295+
296+
let _ = MyClass.publEnumClosed.variantPublEnumClosed
297+
let _ = MyClass.privEnumClosed.variantPrivEnumClosed // expected-error {{'privEnumClosed' is inaccessible due to 'private' protection level}}
298+
299+
let _ = MyClass.publEnumOpen.variantPublEnumOpen
300+
let _ = MyClass.privEnumOpen.variantPrivEnumOpen // expected-error {{'privEnumOpen' is inaccessible due to 'private' protection level}}
300301
}

0 commit comments

Comments
 (0)