Skip to content
Merged
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
75 changes: 58 additions & 17 deletions include/swift/AST/AccessScope.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<const DeclContext *, 1, bool> 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<const DeclContext *, 2, AccessLimitKind> 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
Expand All @@ -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;

Expand Down
10 changes: 7 additions & 3 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3678,11 +3678,13 @@ getAccessScopeForFormalAccess(const ValueDecl *VD,
while (!resultDC->isModuleScopeContext()) {
if (isa<TopLevelCodeDecl>(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<GenericTypeDecl>(resultDC)) {
auto enclosingAccess =
Expand Down Expand Up @@ -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:
Expand Down
8 changes: 5 additions & 3 deletions lib/AST/DeclContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<ModuleDecl>(DC))
assert(!isPrivate && "public or internal scope can't be private");
Expand Down
3 changes: 2 additions & 1 deletion lib/Sema/TypeCheckAccess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down