diff --git a/include/swift/AST/AccessScope.h b/include/swift/AST/AccessScope.h index 684a6a7f7ee57..88ea5ed5bd64c 100644 --- a/include/swift/AST/AccessScope.h +++ b/include/swift/AST/AccessScope.h @@ -20,20 +20,40 @@ namespace swift { +/// Used to provide the kind of scope limitation in AccessScope::Value +enum class AccessLimitKind : uint8_t { None = 0, Private, Package }; + /// The wrapper around the outermost DeclContext from which /// a particular declaration can be accessed. class AccessScope { - /// The declaration context (if not public) along with a bit saying - /// whether this scope is private, SPI or not. - /// If the declaration context is set, the bit means that the scope is - /// private or not. If the declaration context is null, the bit means that - /// this scope is SPI or not. - llvm::PointerIntPair Value; + /// The declaration context along with an enum indicating the level of + /// scope limitation. + /// If the declaration context is set, and the limit kind is Private, the + /// access level is considered 'private'. Whether it's 'internal' or + /// 'fileprivate' is determined by what the declaration context casts to. If + /// the declaration context is null, and the limit kind is None, the access + /// level is considered 'public'. If the limit kind is Private, the access + /// level is considered SPI. If it's Package, the access level is considered + /// 'package'. Below is a table showing the combinations. + /// + /// AccessLimitKind DC == nullptr DC != nullptr + /// --------------------------------------------------------------------------- + /// None public fileprivate or internal (check DC to tell which) + /// Private `@_spi` public private + /// Package package (unused) + + llvm::PointerIntPair Value; public: - AccessScope(const DeclContext *DC, bool isPrivate = false); + AccessScope(const DeclContext *DC, + AccessLimitKind limitKind = AccessLimitKind::None); - static AccessScope getPublic() { return AccessScope(nullptr, false); } + static AccessScope getPublic() { + return AccessScope(nullptr, AccessLimitKind::None); + } + static AccessScope getPackage() { + return AccessScope(nullptr, AccessLimitKind::Package); + } /// Check if private access is allowed. This is a lexical scope check in Swift /// 3 mode. In Swift 4 mode, declarations and extensions of the same type will @@ -46,25 +66,46 @@ class AccessScope { bool operator==(AccessScope RHS) const { return Value == RHS.Value; } bool operator!=(AccessScope RHS) const { return !(*this == RHS); } bool hasEqualDeclContextWith(AccessScope RHS) const { + if (isPublic()) + return RHS.isPublic(); + if (isPackage()) + return RHS.isPackage(); return getDeclContext() == RHS.getDeclContext(); } - bool isPublic() const { return !Value.getPointer(); } - bool isPrivate() const { return Value.getPointer() && Value.getInt(); } + bool isPublic() const { + return !Value.getPointer() && Value.getInt() == AccessLimitKind::None; + } + bool isPrivate() const { + return Value.getPointer() && Value.getInt() == AccessLimitKind::Private; + } bool isFileScope() const; bool isInternal() const; + bool isPackage() const { + return !Value.getPointer() && Value.getInt() == AccessLimitKind::Package; + } - /// Returns true if this is a child scope of the specified other access scope. - /// + /// Returns true if this scope is more restrictive than the argument scope. + /// It's often used to compute the min access scope. The order of restrictiveness + /// is: private (most restrictive), fileprivate, internal, package, public (least restrictive). /// \see DeclContext::isChildContextOf bool isChildOf(AccessScope AS) const { - if (!isPublic() && !AS.isPublic()) - return allowsPrivateAccess(getDeclContext(), AS.getDeclContext()); - if (isPublic() && AS.isPublic()) - return false; - return AS.isPublic(); + if (isInternalOrLess()) { + if (AS.isInternalOrLess()) + return allowsPrivateAccess(getDeclContext(), AS.getDeclContext()); + else + return AS.isPackage() || AS.isPublic(); + } + if (isPackage()) + return AS.isPublic(); + + // If this is public, it can't be less than access level of AS + // so return false + return false; } + bool isInternalOrLess() const { return getDeclContext() != nullptr; } + /// Returns the associated access level for diagnostic purposes. AccessLevel accessLevelForDiagnostics() const; diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 3ae82180e456d..3b9dd5f6d20be 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -3678,11 +3678,13 @@ getAccessScopeForFormalAccess(const ValueDecl *VD, while (!resultDC->isModuleScopeContext()) { if (isa(resultDC)) { return AccessScope(resultDC->getModuleScopeContext(), - access == AccessLevel::Private); + access == AccessLevel::Private + ? AccessLimitKind::Private + : AccessLimitKind::None); } if (resultDC->isLocalContext() || access == AccessLevel::Private) - return AccessScope(resultDC, /*private*/true); + return AccessScope(resultDC, AccessLimitKind::Private); if (auto enclosingNominal = dyn_cast(resultDC)) { auto enclosingAccess = @@ -3713,7 +3715,9 @@ getAccessScopeForFormalAccess(const ValueDecl *VD, case AccessLevel::Private: case AccessLevel::FilePrivate: assert(resultDC->isModuleScopeContext()); - return AccessScope(resultDC, access == AccessLevel::Private); + return AccessScope(resultDC, access == AccessLevel::Private + ? AccessLimitKind::Private + : AccessLimitKind::None); case AccessLevel::Internal: return AccessScope(resultDC->getParentModule()); case AccessLevel::Public: diff --git a/lib/AST/DeclContext.cpp b/lib/AST/DeclContext.cpp index 9886b060838f4..21cff61384596 100644 --- a/lib/AST/DeclContext.cpp +++ b/lib/AST/DeclContext.cpp @@ -1136,11 +1136,13 @@ getPrivateDeclContext(const DeclContext *DC, const SourceFile *useSF) { return lastExtension ? lastExtension : DC; } -AccessScope::AccessScope(const DeclContext *DC, bool isPrivate) - : Value(DC, isPrivate) { - if (isPrivate) { +AccessScope::AccessScope(const DeclContext *DC, AccessLimitKind limitKind) + : Value(DC, limitKind) { + auto isPrivate = false; + if (limitKind == AccessLimitKind::Private) { DC = getPrivateDeclContext(DC, DC->getParentSourceFile()); Value.setPointer(DC); + isPrivate = true; } if (!DC || isa(DC)) assert(!isPrivate && "public or internal scope can't be private"); diff --git a/lib/Sema/TypeCheckAccess.cpp b/lib/Sema/TypeCheckAccess.cpp index 3c2683e2cf48b..354b07d692142 100644 --- a/lib/Sema/TypeCheckAccess.cpp +++ b/lib/Sema/TypeCheckAccess.cpp @@ -2095,7 +2095,8 @@ static void checkExtensionGenericParamAccess(const ExtensionDecl *ED) { case AccessLevel::FilePrivate: { const DeclContext *DC = ED->getModuleScopeContext(); bool isPrivate = (userSpecifiedAccess == AccessLevel::Private); - desiredAccessScope = AccessScope(DC, isPrivate); + desiredAccessScope = AccessScope(DC, isPrivate ? AccessLimitKind::Private + : AccessLimitKind::None); break; } case AccessLevel::Internal: