blob: 19640fbfe040c49bf068d69de9b19a9ba95ccaaf [file] [log] [blame]
//===--- AccessRequests.cpp - AccessLevel and AccessScope Requests --------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "swift/Subsystems.h"
#include "swift/AST/AccessRequests.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/Decl.h"
#include "swift/AST/DiagnosticsCommon.h"
#include "swift/AST/Module.h"
#include "swift/AST/NameLookupRequests.h"
#include "swift/AST/Types.h"
#include "llvm/Support/MathExtras.h"
using namespace swift;
namespace swift {
// Implement the access-control type zone.
#define SWIFT_TYPEID_ZONE SWIFT_ACCESSS_REQUESTS_TYPEID_ZONE
#define SWIFT_TYPEID_HEADER "swift/AST/AccessTypeIDZone.def"
#include "swift/Basic/ImplementTypeIDZone.h"
#undef SWIFT_TYPEID_ZONE
#undef SWIFT_TYPEID_HEADER
}
//----------------------------------------------------------------------------//
// AccessLevel computation
//----------------------------------------------------------------------------//
llvm::Expected<AccessLevel>
AccessLevelRequest::evaluate(Evaluator &evaluator, ValueDecl *D) const {
assert(!D->hasAccess());
// Check if the decl has an explicit access control attribute.
if (auto *AA = D->getAttrs().getAttribute<AccessControlAttr>())
return AA->getAccess();
// Special case for accessors, which inherit the access of their storage.
// decl. A setter attribute can also override this.
if (auto accessor = dyn_cast<AccessorDecl>(D)) {
AbstractStorageDecl *storage = accessor->getStorage();
switch (accessor->getAccessorKind()) {
case AccessorKind::Get:
case AccessorKind::Address:
case AccessorKind::Read:
return storage->getFormalAccess();
case AccessorKind::Set:
case AccessorKind::MutableAddress:
case AccessorKind::Modify:
return storage->getSetterFormalAccess();
case AccessorKind::WillSet:
case AccessorKind::DidSet:
// These are only needed to synthesize the setter.
return AccessLevel::Private;
}
}
DeclContext *DC = D->getDeclContext();
// Special case for generic parameters; we just give them a dummy
// access level.
if (auto genericParam = dyn_cast<GenericTypeParamDecl>(D)) {
return AccessLevel::Internal;
}
// Special case for associated types: inherit access from protocol.
if (auto assocType = dyn_cast<AssociatedTypeDecl>(D)) {
auto prot = assocType->getProtocol();
return std::max(prot->getFormalAccess(), AccessLevel::Internal);
}
// Special case for dtors and enum elements: inherit from container
if (D->getKind() == DeclKind::Destructor ||
D->getKind() == DeclKind::EnumElement) {
if (D->isInvalid()) {
return AccessLevel::Private;
} else {
auto container = cast<NominalTypeDecl>(D->getDeclContext());
return std::max(container->getFormalAccess(), AccessLevel::Internal);
}
}
switch (DC->getContextKind()) {
case DeclContextKind::TopLevelCodeDecl:
// Variables declared in a top-level 'guard' statement can be accessed in
// later top-level code.
return AccessLevel::FilePrivate;
case DeclContextKind::AbstractClosureExpr:
if (isa<ParamDecl>(D)) {
// Closure parameters may need to be accessible to the enclosing
// context, for single-expression closures.
return AccessLevel::FilePrivate;
} else {
return AccessLevel::Private;
}
case DeclContextKind::SerializedLocal:
case DeclContextKind::Initializer:
case DeclContextKind::AbstractFunctionDecl:
case DeclContextKind::SubscriptDecl:
case DeclContextKind::EnumElementDecl:
return AccessLevel::Private;
case DeclContextKind::Module:
case DeclContextKind::FileUnit:
return AccessLevel::Internal;
case DeclContextKind::GenericTypeDecl: {
auto generic = cast<GenericTypeDecl>(DC);
AccessLevel access = AccessLevel::Internal;
if (isa<ProtocolDecl>(generic))
access = std::max(AccessLevel::FilePrivate, generic->getFormalAccess());
return access;
}
case DeclContextKind::ExtensionDecl:
return cast<ExtensionDecl>(DC)->getDefaultAccessLevel();
}
llvm_unreachable("unhandled kind");
}
void AccessLevelRequest::diagnoseCycle(DiagnosticEngine &diags) const {
// FIXME: Improve this diagnostic.
auto valueDecl = std::get<0>(getStorage());
diags.diagnose(valueDecl, diag::circular_reference);
}
void AccessLevelRequest::noteCycleStep(DiagnosticEngine &diags) const {
auto valueDecl = std::get<0>(getStorage());
// FIXME: Customize this further.
diags.diagnose(valueDecl, diag::circular_reference_through);
}
Optional<AccessLevel> AccessLevelRequest::getCachedResult() const {
auto valueDecl = std::get<0>(getStorage());
if (valueDecl->hasAccess())
return valueDecl->TypeAndAccess.getInt().getValue();
return None;
}
void AccessLevelRequest::cacheResult(AccessLevel value) const {
auto valueDecl = std::get<0>(getStorage());
valueDecl->setAccess(value);
}
//----------------------------------------------------------------------------//
// SetterAccessLevel computation
//----------------------------------------------------------------------------//
//
// An AbstractStorageDecl has both its own formal access and also a special
// "setter" formal access like "private(set)" that might override (and lower)
// the normal one, when evaluating the accessibility of mutating accessors.
//
// As this value can be computed, stored, synthesized and set independently from
// the cycle of computation associated with formal accesses, we give it its own
// request.
llvm::Expected<AccessLevel>
SetterAccessLevelRequest::evaluate(Evaluator &evaluator,
AbstractStorageDecl *ASD) const {
assert(!ASD->Accessors.getInt().hasValue());
if (auto *AA = ASD->getAttrs().getAttribute<SetterAccessAttr>())
return AA->getAccess();
return ASD->getFormalAccess();
}
void SetterAccessLevelRequest::diagnoseCycle(DiagnosticEngine &diags) const {
// FIXME: Improve this diagnostic.
auto abstractStorageDecl = std::get<0>(getStorage());
diags.diagnose(abstractStorageDecl, diag::circular_reference);
}
void SetterAccessLevelRequest::noteCycleStep(DiagnosticEngine &diags) const {
auto abstractStorageDecl = std::get<0>(getStorage());
// FIXME: Customize this further.
diags.diagnose(abstractStorageDecl, diag::circular_reference_through);
}
Optional<AccessLevel> SetterAccessLevelRequest::getCachedResult() const {
auto abstractStorageDecl = std::get<0>(getStorage());
if (abstractStorageDecl->Accessors.getInt().hasValue())
return abstractStorageDecl->Accessors.getInt().getValue();
return None;
}
void SetterAccessLevelRequest::cacheResult(AccessLevel value) const {
auto abstractStorageDecl = std::get<0>(getStorage());
// NB: don't call setSetterAccess here because it drives values through to the
// associated accessors' formalAccess, which we might also be in the middle of
// doing a request for. Reserve setSetterAccess for deserialization &
// clangImporter use.
assert(!abstractStorageDecl->Accessors.getInt().hasValue());
abstractStorageDecl->Accessors.setInt(value);
}
//----------------------------------------------------------------------------//
// DefaultAccessLevel computation
//----------------------------------------------------------------------------//
llvm::Expected<std::pair<AccessLevel, AccessLevel>>
DefaultAndMaxAccessLevelRequest::evaluate(Evaluator &evaluator,
ExtensionDecl *ED) const {
auto &Ctx = ED->getASTContext();
assert(!ED->hasDefaultAccessLevel());
AccessLevel maxAccess = AccessLevel::Public;
if (GenericParamList *genericParams = ED->getGenericParams()) {
// Only check the trailing 'where' requirements. Other requirements come
// from the extended type and have already been checked.
DirectlyReferencedTypeDecls typeDecls =
evaluateOrDefault(Ctx.evaluator, TypeDeclsFromWhereClauseRequest{ED}, {});
Optional<AccessScope> maxScope = AccessScope::getPublic();
for (auto *typeDecl : typeDecls) {
if (isa<TypeAliasDecl>(typeDecl) || isa<NominalTypeDecl>(typeDecl)) {
auto scope = typeDecl->getFormalAccessScope(ED->getDeclContext());
maxScope = maxScope->intersectWith(scope);
}
}
if (!maxScope.hasValue()) {
// This is an error case and will be diagnosed elsewhere.
maxAccess = AccessLevel::Public;
} else if (maxScope->isPublic()) {
maxAccess = AccessLevel::Public;
} else if (isa<ModuleDecl>(maxScope->getDeclContext())) {
maxAccess = AccessLevel::Internal;
} else {
// Because extensions are always at top-level, they should never
// reference declarations not at the top level. (And any such references
// should be diagnosed elsewhere.) This code should not crash if that
// occurs, though.
maxAccess = AccessLevel::FilePrivate;
}
}
if (NominalTypeDecl *nominal = ED->getExtendedNominal()) {
maxAccess = std::min(maxAccess,
std::max(nominal->getFormalAccess(),
AccessLevel::FilePrivate));
}
AccessLevel defaultAccess;
if (auto *AA = ED->getAttrs().getAttribute<AccessControlAttr>())
defaultAccess = std::max(AA->getAccess(), AccessLevel::FilePrivate);
else
defaultAccess = AccessLevel::Internal;
// Don't set the max or default access level to 'open'. This should
// be diagnosed as invalid anyway.
defaultAccess = std::min(defaultAccess, AccessLevel::Public);
maxAccess = std::min(maxAccess, AccessLevel::Public);
// Normally putting a public member in an internal extension is harmless,
// because that member can never be used elsewhere. But if some of the types
// in the signature are public, it could actually end up getting picked in
// overload resolution. Therefore, we only enforce the maximum access if the
// extension has a 'where' clause.
if (ED->getTrailingWhereClause())
defaultAccess = std::min(defaultAccess, maxAccess);
else
maxAccess = AccessLevel::Public;
return std::make_pair(defaultAccess, maxAccess);
}
void DefaultAndMaxAccessLevelRequest::diagnoseCycle(DiagnosticEngine &diags) const {
// FIXME: Improve this diagnostic.
auto extensionDecl = std::get<0>(getStorage());
diags.diagnose(extensionDecl, diag::circular_reference);
}
void DefaultAndMaxAccessLevelRequest::noteCycleStep(DiagnosticEngine &diags) const {
auto extensionDecl = std::get<0>(getStorage());
// FIXME: Customize this further.
diags.diagnose(extensionDecl, diag::circular_reference_through);
}
// Default and Max access levels are stored combined as a 3-bit bitset. The Bits
// are numbered using the 3 middle values of the AccessLevel enumeration, and
// the combined value is just the bitwise-OR of the bits for Default and Max.
//
// For example, if Max=Internal and Default=FilePrivate, we will see:
//
// 0 1 1
// | | |
// | | [FilePrivate]
// | |
// | [Internal]
// |
// [Public]
//
// This is unambiguous to decode because of the following facts:
//
// - At least one of the bits is set (all-zero means "not yet set").
// - At most two of the bits are set.
// - Max >= Default by definition.
//
// So we decode Max as the last (high) bit that is set, and Default as the first
// (low). And add one to each, to map them back into AccessLevels.
Optional<std::pair<AccessLevel,AccessLevel>>
DefaultAndMaxAccessLevelRequest::getCachedResult() const {
auto extensionDecl = std::get<0>(getStorage());
if (extensionDecl->hasDefaultAccessLevel()) {
uint8_t Bits = extensionDecl->getDefaultAndMaxAccessLevelBits();
assert(Bits != 0x7 && "more than two bits set for Default and Max");
AccessLevel Max = static_cast<AccessLevel>(llvm::findLastSet(Bits) + 1);
AccessLevel Default = static_cast<AccessLevel>(llvm::findFirstSet(Bits) + 1);
assert(Max >= Default);
return std::make_pair(Default, Max);
}
return None;
}
void
DefaultAndMaxAccessLevelRequest::cacheResult(
std::pair<AccessLevel, AccessLevel> value) const {
auto extensionDecl = std::get<0>(getStorage());
extensionDecl->setDefaultAndMaxAccessLevelBits(value.first, value.second);
assert(getCachedResult().getValue().first == value.first);
assert(getCachedResult().getValue().second == value.second);
}
// Define request evaluation functions for each of the access requests.
static AbstractRequestFunction *accessRequestFunctions[] = {
#define SWIFT_TYPEID(Name) \
reinterpret_cast<AbstractRequestFunction *>(&Name::evaluateRequest),
#include "swift/AST/AccessTypeIDZone.def"
#undef SWIFT_TYPEID
};
void swift::registerAccessRequestFunctions(Evaluator &evaluator) {
evaluator.registerRequestFunctions(SWIFT_ACCESS_REQUESTS_TYPEID_ZONE,
accessRequestFunctions);
}