mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[cxx-interop] Make imported enum members inherit the access of their parent (#84522)
This patch relaxes the access check for members of imported inherited nested enums. Without it, those members become impossible to inherit, which largely defeats the purpose of importing them in the first place. rdar://160803362
This commit is contained in:
@@ -1499,14 +1499,32 @@ bool AccessScope::allowsPrivateAccess(const DeclContext *useDC, const DeclContex
|
||||
auto clangDecl = sourceNTD->getDecl()->getClangDecl();
|
||||
|
||||
if (isa_and_nonnull<clang::EnumDecl>(clangDecl)) {
|
||||
// Consider: class C { private: enum class E { M }; };
|
||||
// If sourceDC is a C++ enum (e.g, E), then we are looking at one of its
|
||||
// members (e.g., E.M). If this is the case, then we should consider
|
||||
// the SWIFT_PRIVATE_FILEID of its enclosing class decl (e.g., C), if any.
|
||||
// Doing so allows the nested private enum's members to inherit the access
|
||||
// of the nested enum type itself.
|
||||
clangDecl = dyn_cast<clang::CXXRecordDecl>(clangDecl->getDeclContext());
|
||||
sourceDC = sourceNTD->getDeclContext();
|
||||
// C/C++ enums are an odd case for access checking because they can only
|
||||
// contain variants as members, and those variants cannot be assigned
|
||||
// access specifiers. Instead, those variants should simply inherit the
|
||||
// access of their parent enum, if any. For instance:
|
||||
//
|
||||
// class Base { protected: enum class E { M }; };
|
||||
// class Derived : public Base {};
|
||||
//
|
||||
// In C++, E::M should be accessible within Base and Derived; we should
|
||||
// follow suit in Swift.
|
||||
//
|
||||
// When we check the access of something like E.M, clangDecl will point to
|
||||
// enum class E (the DeclContext of M), and if we've gotten this far, we
|
||||
// can infer that E was accessible in useDC, so its members should be too.
|
||||
//
|
||||
// This is technically unsound (i.e., encapsulation-breaking), because it
|
||||
// is possible to extend imported Clang enums private in Swift extensions.
|
||||
// With this hack, those private members would be accessible everywhere
|
||||
// even though they shouldn't. But right here, when we're checking the E
|
||||
// of E.M, there's no way to tell whether the M is something we imported
|
||||
// from Clang (which we should allow) versus something we defined in Swift
|
||||
// (which we should disallow), without over-complicating the access check.
|
||||
// To limit the encapsulate the breakage, we do an additional check to
|
||||
// ensure we're checking an imported enum that is *nested* in a Clang
|
||||
// record (which is the only way this enum can be imported as private).
|
||||
return isa_and_nonnull<clang::CXXRecordDecl>(clangDecl->getDeclContext());
|
||||
}
|
||||
|
||||
if (!isa_and_nonnull<clang::CXXRecordDecl>(clangDecl))
|
||||
|
||||
Reference in New Issue
Block a user