mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Rework access checking in terms of "access scopes".
(in preparation for the private/fileprivate split)
An "access scope" is the outermost DeclContext where a particular
declaration may be referenced: for a 'fileprivate' declaration it's
the enclosing file, and for an 'internal' declaration it's the module.
'public' corresponds to a scope of "everything", represented by a null
DeclContext.
This model extends naturally to the (not-yet-implemented) SE-0025
notion of 'private', where the access scope is a declaration's
immediately enclosing DeclContext.
Complicating this model is the revised rules that allow, e.g., a public
declaration to be declared within an internal type. The access scope
for this declaration is still just the module, not "everything".
This commit reworks formal access control checking in terms of this
model, including tightening up some of the handling for '@testable'.
This implements the rule that you must be able to access a declaration's
type everywhere you can reference the declaration.
This was not intended to change compiler behavior, but in practice it
has made cross-file dependency tracking a bit more conservative
(unnecessarily), caught a mistake in diagnosing access violations,
and fixed a fuzzer-based crasher (see test changes).
Progress on SE-0025 ('private' and 'fileprivate')
This commit is contained in:
@@ -2167,16 +2167,42 @@ public:
|
|||||||
return TypeAndAccess.getInt().hasValue();
|
return TypeAndAccess.getInt().hasValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// \see getFormalAccess
|
||||||
|
Accessibility getFormalAccessImpl(const DeclContext *useDC) const;
|
||||||
|
|
||||||
/// Returns the access level specified explicitly by the user, or provided by
|
/// Returns the access level specified explicitly by the user, or provided by
|
||||||
/// default according to language rules.
|
/// default according to language rules.
|
||||||
///
|
///
|
||||||
/// This is the access used when calculating if access control is being used
|
/// This is the access used when calculating if access control is being used
|
||||||
/// consistently.
|
/// consistently. If \p useDC is provided (the location where the value is
|
||||||
Accessibility getFormalAccess() const {
|
/// being used), features that affect formal access such as \c \@testable are
|
||||||
|
/// taken into account.
|
||||||
|
///
|
||||||
|
/// \sa getFormalAccessScope
|
||||||
|
Accessibility getFormalAccess(const DeclContext *useDC = nullptr) const {
|
||||||
assert(hasAccessibility() && "accessibility not computed yet");
|
assert(hasAccessibility() && "accessibility not computed yet");
|
||||||
return TypeAndAccess.getInt().getValue();
|
Accessibility result = TypeAndAccess.getInt().getValue();
|
||||||
|
if (useDC && result == Accessibility::Internal)
|
||||||
|
return getFormalAccessImpl(useDC);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the outermost DeclContext from which this declaration can be
|
||||||
|
/// accessed, or null if the declaration is public.
|
||||||
|
///
|
||||||
|
/// This is used when calculating if access control is being used
|
||||||
|
/// consistently. If \p useDC is provided (the location where the value is
|
||||||
|
/// being used), features that affect formal access such as \c \@testable are
|
||||||
|
/// taken into account.
|
||||||
|
///
|
||||||
|
/// \invariant
|
||||||
|
/// <code>value.isAccessibleFrom(value.getFormalAccessScope())</code>
|
||||||
|
///
|
||||||
|
/// \sa getFormalAccess
|
||||||
|
/// \sa isAccessibleFrom
|
||||||
|
const DeclContext *
|
||||||
|
getFormalAccessScope(const DeclContext *useDC = nullptr) const;
|
||||||
|
|
||||||
/// Returns the access level that actually controls how a declaration should
|
/// Returns the access level that actually controls how a declaration should
|
||||||
/// be emitted and may be used.
|
/// be emitted and may be used.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -1847,6 +1847,57 @@ Accessibility ValueDecl::getEffectiveAccess() const {
|
|||||||
return effectiveAccess;
|
return effectiveAccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Accessibility ValueDecl::getFormalAccessImpl(const DeclContext *useDC) const {
|
||||||
|
assert(getFormalAccess() == Accessibility::Internal &&
|
||||||
|
"should be able to fast-path non-internal cases");
|
||||||
|
assert(useDC && "should fast-path non-scoped cases");
|
||||||
|
if (auto *useSF = dyn_cast<SourceFile>(useDC->getModuleScopeContext()))
|
||||||
|
if (useSF->hasTestableImport(getModuleContext()))
|
||||||
|
return Accessibility::Public;
|
||||||
|
return Accessibility::Internal;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DeclContext *
|
||||||
|
ValueDecl::getFormalAccessScope(const DeclContext *useDC) const {
|
||||||
|
const DeclContext *result = getDeclContext();
|
||||||
|
Accessibility access = getFormalAccess(useDC);
|
||||||
|
|
||||||
|
while (!result->isModuleScopeContext()) {
|
||||||
|
if (result->isLocalContext())
|
||||||
|
return result;
|
||||||
|
|
||||||
|
if (auto enclosingNominal = dyn_cast<NominalTypeDecl>(result)) {
|
||||||
|
access = std::min(access, enclosingNominal->getFormalAccess(useDC));
|
||||||
|
|
||||||
|
} else if (auto enclosingExt = dyn_cast<ExtensionDecl>(result)) {
|
||||||
|
// Just check the base type. If it's a constrained extension, Sema should
|
||||||
|
// have already enforced access more strictly.
|
||||||
|
if (auto extendedTy = enclosingExt->getExtendedType()) {
|
||||||
|
if (auto nominal = extendedTy->getAnyNominal()) {
|
||||||
|
access = std::min(access, nominal->getFormalAccess(useDC));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
llvm_unreachable("unknown DeclContext kind");
|
||||||
|
}
|
||||||
|
|
||||||
|
result = result->getParent();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (access) {
|
||||||
|
case Accessibility::Private:
|
||||||
|
assert(result->isModuleScopeContext());
|
||||||
|
return result;
|
||||||
|
case Accessibility::Internal:
|
||||||
|
return result->getParentModule();
|
||||||
|
case Accessibility::Public:
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Type TypeDecl::getDeclaredType() const {
|
Type TypeDecl::getDeclaredType() const {
|
||||||
if (auto TAD = dyn_cast<TypeAliasDecl>(this)) {
|
if (auto TAD = dyn_cast<TypeAliasDecl>(this)) {
|
||||||
|
|||||||
@@ -379,6 +379,26 @@ static OperatorFixity getDeclFixity(const ValueDecl *decl) {
|
|||||||
llvm_unreachable("bad UnaryOperatorKind");
|
llvm_unreachable("bad UnaryOperatorKind");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if one of the ancestor DeclContexts of \p D is either marked
|
||||||
|
/// private or is a local context.
|
||||||
|
static bool isInPrivateOrLocalContext(const ValueDecl *D) {
|
||||||
|
const DeclContext *DC = D->getDeclContext();
|
||||||
|
if (!DC->isTypeContext()) {
|
||||||
|
assert((DC->isModuleScopeContext() || DC->isLocalContext()) &&
|
||||||
|
"unexpected context kind");
|
||||||
|
return DC->isLocalContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto declaredType = DC->getDeclaredTypeOfContext();
|
||||||
|
if (!declaredType || declaredType->is<ErrorType>())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto *nominal = declaredType->getAnyNominal();
|
||||||
|
if (nominal->getFormalAccess() == Accessibility::Private)
|
||||||
|
return true;
|
||||||
|
return isInPrivateOrLocalContext(nominal);
|
||||||
|
}
|
||||||
|
|
||||||
void Mangler::mangleDeclName(const ValueDecl *decl) {
|
void Mangler::mangleDeclName(const ValueDecl *decl) {
|
||||||
if (decl->getDeclContext()->isLocalContext()) {
|
if (decl->getDeclContext()->isLocalContext()) {
|
||||||
// Mangle local declarations with a numeric discriminator.
|
// Mangle local declarations with a numeric discriminator.
|
||||||
@@ -387,47 +407,28 @@ void Mangler::mangleDeclName(const ValueDecl *decl) {
|
|||||||
// Fall through to mangle the <identifier>.
|
// Fall through to mangle the <identifier>.
|
||||||
|
|
||||||
} else if (decl->hasAccessibility() &&
|
} else if (decl->hasAccessibility() &&
|
||||||
decl->getFormalAccess() == Accessibility::Private) {
|
decl->getFormalAccess() == Accessibility::Private &&
|
||||||
|
!isInPrivateOrLocalContext(decl)) {
|
||||||
// Mangle non-local private declarations with a textual discriminator
|
// Mangle non-local private declarations with a textual discriminator
|
||||||
// based on their enclosing file.
|
// based on their enclosing file.
|
||||||
// decl-name ::= 'P' identifier identifier
|
// decl-name ::= 'P' identifier identifier
|
||||||
|
|
||||||
// Don't bother to use the private discriminator if the enclosing context
|
// The first <identifier> is a discriminator string unique to the decl's
|
||||||
// is also private.
|
// original source file.
|
||||||
auto isWithinPrivateNominal = [](const Decl *D) -> bool {
|
auto topLevelContext = decl->getDeclContext()->getModuleScopeContext();
|
||||||
const DeclContext *DC = D->getDeclContext();
|
auto fileUnit = cast<FileUnit>(topLevelContext);
|
||||||
if (!DC->isTypeContext()) {
|
|
||||||
assert((DC->isModuleScopeContext() || DC->isLocalContext()) &&
|
|
||||||
"do we need a private discriminator for this context?");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto declaredType = DC->getDeclaredTypeOfContext();
|
Identifier discriminator =
|
||||||
if (!declaredType || declaredType->is<ErrorType>())
|
fileUnit->getDiscriminatorForPrivateValue(decl);
|
||||||
return false;
|
assert(!discriminator.empty());
|
||||||
|
assert(!isNonAscii(discriminator.str()) &&
|
||||||
|
"discriminator contains non-ASCII characters");
|
||||||
|
(void)isNonAscii;
|
||||||
|
assert(!clang::isDigit(discriminator.str().front()) &&
|
||||||
|
"not a valid identifier");
|
||||||
|
|
||||||
return (declaredType->getAnyNominal()->getFormalAccess()
|
Buffer << 'P';
|
||||||
== Accessibility::Private);
|
mangleIdentifier(discriminator);
|
||||||
};
|
|
||||||
|
|
||||||
if (!isWithinPrivateNominal(decl)) {
|
|
||||||
// The first <identifier> is a discriminator string unique to the decl's
|
|
||||||
// original source file.
|
|
||||||
auto topLevelContext = decl->getDeclContext()->getModuleScopeContext();
|
|
||||||
auto fileUnit = cast<FileUnit>(topLevelContext);
|
|
||||||
|
|
||||||
Identifier discriminator =
|
|
||||||
fileUnit->getDiscriminatorForPrivateValue(decl);
|
|
||||||
assert(!discriminator.empty());
|
|
||||||
assert(!isNonAscii(discriminator.str()) &&
|
|
||||||
"discriminator contains non-ASCII characters");
|
|
||||||
(void) isNonAscii;
|
|
||||||
assert(!clang::isDigit(discriminator.str().front()) &&
|
|
||||||
"not a valid identifier");
|
|
||||||
|
|
||||||
Buffer << 'P';
|
|
||||||
mangleIdentifier(discriminator);
|
|
||||||
}
|
|
||||||
// Fall through to mangle the name.
|
// Fall through to mangle the name.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1168,54 +1168,84 @@ void swift::configureConstructorType(ConstructorDecl *ctor,
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
class TypeAccessibilityChecker : private TypeWalker {
|
class TypeAccessScopeChecker : private TypeWalker {
|
||||||
using TypeAccessibilityCacheMap =
|
using TypeAccessScopeCacheMap = TypeChecker::TypeAccessScopeCacheMap;
|
||||||
decltype(TypeChecker::TypeAccessibilityCache);
|
TypeAccessScopeCacheMap &Cache;
|
||||||
TypeAccessibilityCacheMap &Cache;
|
const SourceFile *File;
|
||||||
SmallVector<Accessibility, 8> AccessStack;
|
SmallVector<intptr_t, 8> RawScopeStack;
|
||||||
|
|
||||||
explicit TypeAccessibilityChecker(TypeAccessibilityCacheMap &cache)
|
static constexpr const intptr_t INVALID = -1;
|
||||||
: Cache(cache) {
|
|
||||||
|
explicit TypeAccessScopeChecker(TypeAccessScopeCacheMap &cache,
|
||||||
|
const SourceFile *file)
|
||||||
|
: Cache(cache), File(file) {
|
||||||
// Always have something on the stack.
|
// Always have something on the stack.
|
||||||
AccessStack.push_back(Accessibility::Private);
|
RawScopeStack.push_back((intptr_t)INVALID);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool shouldVisitOriginalSubstitutedType() override { return true; }
|
bool shouldVisitOriginalSubstitutedType() override { return true; }
|
||||||
|
|
||||||
|
static intptr_t intersectAccess(const intptr_t first, const intptr_t second) {
|
||||||
|
if (first == INVALID || second == INVALID)
|
||||||
|
return INVALID;
|
||||||
|
if (!first)
|
||||||
|
return second;
|
||||||
|
if (!second)
|
||||||
|
return first;
|
||||||
|
if (first == second)
|
||||||
|
return first;
|
||||||
|
|
||||||
|
auto firstDC = reinterpret_cast<const DeclContext *>(first);
|
||||||
|
auto secondDC = reinterpret_cast<const DeclContext *>(second);
|
||||||
|
if (firstDC->isChildContextOf(secondDC))
|
||||||
|
return first;
|
||||||
|
if (secondDC->isChildContextOf(firstDC))
|
||||||
|
return second;
|
||||||
|
return INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
Action walkToTypePre(Type ty) override {
|
Action walkToTypePre(Type ty) override {
|
||||||
// Assume failure until we post-visit this node.
|
// Assume failure until we post-visit this node.
|
||||||
// This will be correct as long as we don't ever have self-referential
|
// This will be correct as long as we don't ever have self-referential
|
||||||
// Types.
|
// Types.
|
||||||
auto cached = Cache.find(ty);
|
auto cached = Cache.find(ty);
|
||||||
if (cached != Cache.end()) {
|
if (cached != Cache.end()) {
|
||||||
AccessStack.back() = std::min(AccessStack.back(), cached->second);
|
auto opaqueCached = reinterpret_cast<intptr_t>(cached->second);
|
||||||
|
RawScopeStack.back() = intersectAccess(RawScopeStack.back(),opaqueCached);
|
||||||
return Action::SkipChildren;
|
return Action::SkipChildren;
|
||||||
}
|
}
|
||||||
|
|
||||||
Accessibility current;
|
const DeclContext *DC;
|
||||||
if (auto alias = dyn_cast<NameAliasType>(ty.getPointer()))
|
if (auto alias = dyn_cast<NameAliasType>(ty.getPointer()))
|
||||||
current = alias->getDecl()->getFormalAccess();
|
DC = alias->getDecl()->getFormalAccessScope(File);
|
||||||
else if (auto nominal = ty->getAnyNominal())
|
else if (auto nominal = ty->getAnyNominal())
|
||||||
current = nominal->getFormalAccess();
|
DC = nominal->getFormalAccessScope(File);
|
||||||
else
|
else
|
||||||
current = Accessibility::Public;
|
DC = nullptr;
|
||||||
AccessStack.push_back(current);
|
RawScopeStack.push_back(reinterpret_cast<intptr_t>(DC));
|
||||||
|
|
||||||
return Action::Continue;
|
return Action::Continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Action walkToTypePost(Type ty) override {
|
Action walkToTypePost(Type ty) override {
|
||||||
Accessibility last = AccessStack.pop_back_val();
|
auto last = RawScopeStack.pop_back_val();
|
||||||
Cache[ty] = last;
|
if (last != INVALID)
|
||||||
AccessStack.back() = std::min(AccessStack.back(), last);
|
Cache[ty] = reinterpret_cast<const DeclContext *>(last);
|
||||||
|
RawScopeStack.back() = intersectAccess(RawScopeStack.back(), last);
|
||||||
return Action::Continue;
|
return Action::Continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static Accessibility getAccessibility(Type ty,
|
static Optional<const DeclContext *>
|
||||||
TypeAccessibilityCacheMap &cache) {
|
getAccessScope(Type ty, const DeclContext *useDC,
|
||||||
ty.walk(TypeAccessibilityChecker(cache));
|
decltype(TypeChecker::TypeAccessScopeCache) &caches) {
|
||||||
return cache[ty];
|
const SourceFile *file = useDC->getParentSourceFile();
|
||||||
|
auto &cache = caches[file];
|
||||||
|
ty.walk(TypeAccessScopeChecker(cache, file));
|
||||||
|
auto iter = cache.find(ty);
|
||||||
|
if (iter == cache.end())
|
||||||
|
return None;
|
||||||
|
return iter->second;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1239,11 +1269,26 @@ void TypeChecker::computeDefaultAccessibility(ExtensionDecl *ED) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (const GenericParamList *genericParams = ED->getGenericParams()) {
|
if (const GenericParamList *genericParams = ED->getGenericParams()) {
|
||||||
auto getTypeAccess = [this](const TypeLoc &TL) {
|
auto getTypeAccess = [this, ED](const TypeLoc &TL) -> Accessibility {
|
||||||
if (!TL.getType())
|
if (!TL.getType())
|
||||||
return Accessibility::Public;
|
return Accessibility::Public;
|
||||||
return TypeAccessibilityChecker::getAccessibility(TL.getType(),
|
auto scopeDC =
|
||||||
TypeAccessibilityCache);
|
TypeAccessScopeChecker::getAccessScope(TL.getType(),
|
||||||
|
ED->getDeclContext(),
|
||||||
|
TypeAccessScopeCache);
|
||||||
|
// This is an error case and will be diagnosed elsewhere.
|
||||||
|
if (!scopeDC.hasValue())
|
||||||
|
return Accessibility::Public;
|
||||||
|
|
||||||
|
if (!scopeDC.getValue())
|
||||||
|
return Accessibility::Public;
|
||||||
|
if (isa<ModuleDecl>(scopeDC.getValue()))
|
||||||
|
return Accessibility::Internal;
|
||||||
|
// 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.
|
||||||
|
return Accessibility::Private;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Only check the trailing 'where' requirements. Other requirements come
|
// Only check the trailing 'where' requirements. Other requirements come
|
||||||
@@ -1352,10 +1397,16 @@ void TypeChecker::computeAccessibility(ValueDecl *D) {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
class TypeAccessibilityDiagnoser : private ASTWalker {
|
class TypeAccessScopeDiagnoser : private ASTWalker {
|
||||||
const ComponentIdentTypeRepr *minAccessibilityType = nullptr;
|
const DeclContext *accessScope;
|
||||||
|
const DeclContext *useDC;
|
||||||
|
const ComponentIdentTypeRepr *offendingType = nullptr;
|
||||||
|
|
||||||
bool walkToTypeReprPre(TypeRepr *TR) override {
|
bool walkToTypeReprPre(TypeRepr *TR) override {
|
||||||
|
// Exit early if we've already found a problem type.
|
||||||
|
if (offendingType)
|
||||||
|
return false;
|
||||||
|
|
||||||
auto CITR = dyn_cast<ComponentIdentTypeRepr>(TR);
|
auto CITR = dyn_cast<ComponentIdentTypeRepr>(TR);
|
||||||
if (!CITR)
|
if (!CITR)
|
||||||
return true;
|
return true;
|
||||||
@@ -1364,55 +1415,84 @@ class TypeAccessibilityDiagnoser : private ASTWalker {
|
|||||||
if (!VD)
|
if (!VD)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (minAccessibilityType) {
|
if (VD->getFormalAccessScope(useDC) != accessScope)
|
||||||
const ValueDecl *minDecl = minAccessibilityType->getBoundDecl();
|
return true;
|
||||||
if (minDecl->getFormalAccess() <= VD->getFormalAccess())
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
minAccessibilityType = CITR;
|
offendingType = CITR;
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool walkToTypeReprPost(TypeRepr *T) override {
|
||||||
|
// Exit early if we've already found a problem type.
|
||||||
|
return offendingType != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit TypeAccessScopeDiagnoser(const DeclContext *accessScope,
|
||||||
|
const DeclContext *useDC)
|
||||||
|
: accessScope(accessScope), useDC(useDC) {}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static const TypeRepr *findMinAccessibleType(TypeRepr *TR) {
|
static const TypeRepr *findTypeWithScope(TypeRepr *TR,
|
||||||
TypeAccessibilityDiagnoser diagnoser;
|
const DeclContext *accessScope,
|
||||||
|
const DeclContext *useDC) {
|
||||||
|
assert(accessScope && "why would we need to find a public access scope?");
|
||||||
|
TypeAccessScopeDiagnoser diagnoser(accessScope, useDC);
|
||||||
TR->walk(diagnoser);
|
TR->walk(diagnoser);
|
||||||
return diagnoser.minAccessibilityType;
|
return diagnoser.offendingType;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // end anonymous namespace
|
} // end anonymous namespace
|
||||||
|
|
||||||
/// Checks if the accessibility of the type described by \p TL is at least
|
/// Checks if the access scope of the type described by \p TL contains
|
||||||
/// \p contextAccess. If it isn't, calls \p diagnose with a TypeRepr
|
/// \p contextAccessScope. If it isn't, calls \p diagnose with a TypeRepr
|
||||||
/// representing the offending part of \p TL.
|
/// representing the offending part of \p TL.
|
||||||
///
|
///
|
||||||
|
/// If \p contextAccessScope is null, checks that \p TL is only made up of
|
||||||
|
/// public types.
|
||||||
|
///
|
||||||
/// The TypeRepr passed to \p diagnose may be null, in which case a particular
|
/// The TypeRepr passed to \p diagnose may be null, in which case a particular
|
||||||
/// part of the type that caused the problem could not be found.
|
/// part of the type that caused the problem could not be found. The DeclContext
|
||||||
static void checkTypeAccessibility(
|
/// is never null.
|
||||||
TypeChecker &TC, TypeLoc TL, Accessibility contextAccess,
|
static void checkTypeAccessibilityImpl(
|
||||||
llvm::function_ref<void(Accessibility, const TypeRepr *)> diagnose) {
|
TypeChecker &TC, TypeLoc TL, const DeclContext *contextAccessScope,
|
||||||
// Don't spend time checking private access; this is always valid.
|
const DeclContext *useDC,
|
||||||
// This includes local declarations.
|
llvm::function_ref<void(const DeclContext *, const TypeRepr *)> diagnose) {
|
||||||
if (contextAccess == Accessibility::Private || !TL.getType())
|
if (!TC.getLangOpts().EnableAccessControl)
|
||||||
|
return;
|
||||||
|
if (!TL.getType())
|
||||||
|
return;
|
||||||
|
// Don't spend time checking private access; this is always valid by the time
|
||||||
|
// we get to this point. This includes local declarations.
|
||||||
|
if (contextAccessScope && !isa<ModuleDecl>(contextAccessScope))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Accessibility typeAccess =
|
Optional<const DeclContext *> typeAccessScope =
|
||||||
TypeAccessibilityChecker::getAccessibility(TL.getType(),
|
TypeAccessScopeChecker::getAccessScope(TL.getType(), useDC,
|
||||||
TC.TypeAccessibilityCache);
|
TC.TypeAccessScopeCache);
|
||||||
if (typeAccess >= contextAccess)
|
|
||||||
|
// Note: This means that the type itself is invalid for this particular
|
||||||
|
// context, because it references declarations from two incompatible scopes.
|
||||||
|
// In this case we should have diagnosed the bad reference already.
|
||||||
|
if (!typeAccessScope.hasValue())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!*typeAccessScope || *typeAccessScope == contextAccessScope)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const TypeRepr *complainRepr = nullptr;
|
const TypeRepr *complainRepr = nullptr;
|
||||||
if (TypeRepr *TR = TL.getTypeRepr())
|
if (TypeRepr *TR = TL.getTypeRepr()) {
|
||||||
complainRepr = TypeAccessibilityDiagnoser::findMinAccessibleType(TR);
|
complainRepr =
|
||||||
diagnose(typeAccess, complainRepr);
|
TypeAccessScopeDiagnoser::findTypeWithScope(TR, *typeAccessScope,
|
||||||
|
useDC);
|
||||||
|
}
|
||||||
|
diagnose(*typeAccessScope, complainRepr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void checkTypeAccessibility(
|
static void checkTypeAccessibility(
|
||||||
TypeChecker &TC, TypeLoc TL, const ValueDecl *context,
|
TypeChecker &TC, TypeLoc TL, const ValueDecl *context,
|
||||||
llvm::function_ref<void(Accessibility, const TypeRepr *)> diagnose) {
|
llvm::function_ref<void(const DeclContext *, const TypeRepr *)> diagnose) {
|
||||||
checkTypeAccessibility(TC, TL, context->getFormalAccess(), diagnose);
|
checkTypeAccessibilityImpl(TC, TL, context->getFormalAccessScope(),
|
||||||
|
context->getDeclContext(), diagnose);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Highlights the given TypeRepr, and adds a note pointing to the type's
|
/// Highlights the given TypeRepr, and adds a note pointing to the type's
|
||||||
@@ -1438,6 +1518,7 @@ static void highlightOffendingType(TypeChecker &TC, InFlightDiagnostic &diag,
|
|||||||
static void checkGenericParamAccessibility(TypeChecker &TC,
|
static void checkGenericParamAccessibility(TypeChecker &TC,
|
||||||
const GenericParamList *params,
|
const GenericParamList *params,
|
||||||
const Decl *owner,
|
const Decl *owner,
|
||||||
|
const DeclContext *accessScope,
|
||||||
Accessibility contextAccess) {
|
Accessibility contextAccess) {
|
||||||
if (!params)
|
if (!params)
|
||||||
return;
|
return;
|
||||||
@@ -1447,18 +1528,21 @@ static void checkGenericParamAccessibility(TypeChecker &TC,
|
|||||||
AEK_Parameter = 0,
|
AEK_Parameter = 0,
|
||||||
AEK_Requirement
|
AEK_Requirement
|
||||||
} accessibilityErrorKind;
|
} accessibilityErrorKind;
|
||||||
Optional<Accessibility> minAccess;
|
const DeclContext *minAccessScope = nullptr;
|
||||||
const TypeRepr *complainRepr = nullptr;
|
const TypeRepr *complainRepr = nullptr;
|
||||||
|
|
||||||
for (auto param : *params) {
|
for (auto param : *params) {
|
||||||
if (param->getInherited().empty())
|
if (param->getInherited().empty())
|
||||||
continue;
|
continue;
|
||||||
assert(param->getInherited().size() == 1);
|
assert(param->getInherited().size() == 1);
|
||||||
checkTypeAccessibility(TC, param->getInherited().front(), contextAccess,
|
checkTypeAccessibilityImpl(TC, param->getInherited().front(), accessScope,
|
||||||
[&](Accessibility typeAccess,
|
owner->getDeclContext(),
|
||||||
const TypeRepr *thisComplainRepr) {
|
[&](const DeclContext *typeAccessScope,
|
||||||
if (!minAccess || *minAccess > typeAccess) {
|
const TypeRepr *thisComplainRepr) {
|
||||||
minAccess = typeAccess;
|
if (!minAccessScope ||
|
||||||
|
typeAccessScope->isChildContextOf(minAccessScope) ||
|
||||||
|
(!complainRepr && typeAccessScope == minAccessScope)) {
|
||||||
|
minAccessScope = typeAccessScope;
|
||||||
complainRepr = thisComplainRepr;
|
complainRepr = thisComplainRepr;
|
||||||
accessibilityErrorKind = AEK_Parameter;
|
accessibilityErrorKind = AEK_Parameter;
|
||||||
}
|
}
|
||||||
@@ -1466,37 +1550,47 @@ static void checkGenericParamAccessibility(TypeChecker &TC,
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (auto &requirement : params->getRequirements()) {
|
for (auto &requirement : params->getRequirements()) {
|
||||||
auto callback = [&](Accessibility typeAccess,
|
auto callback = [&](const DeclContext *typeAccessScope,
|
||||||
const TypeRepr *thisComplainRepr) {
|
const TypeRepr *thisComplainRepr) {
|
||||||
if (!minAccess || *minAccess > typeAccess) {
|
if (!minAccessScope ||
|
||||||
minAccess = typeAccess;
|
typeAccessScope->isChildContextOf(minAccessScope) ||
|
||||||
|
(!complainRepr && typeAccessScope == minAccessScope)) {
|
||||||
|
minAccessScope = typeAccessScope;
|
||||||
complainRepr = thisComplainRepr;
|
complainRepr = thisComplainRepr;
|
||||||
accessibilityErrorKind = AEK_Requirement;
|
accessibilityErrorKind = AEK_Requirement;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
switch (requirement.getKind()) {
|
switch (requirement.getKind()) {
|
||||||
case RequirementReprKind::TypeConstraint:
|
case RequirementReprKind::TypeConstraint:
|
||||||
checkTypeAccessibility(TC, requirement.getSubjectLoc(), contextAccess,
|
checkTypeAccessibilityImpl(TC, requirement.getSubjectLoc(),
|
||||||
callback);
|
accessScope, owner->getDeclContext(),
|
||||||
checkTypeAccessibility(TC, requirement.getConstraintLoc(), contextAccess,
|
callback);
|
||||||
callback);
|
checkTypeAccessibilityImpl(TC, requirement.getConstraintLoc(),
|
||||||
|
accessScope, owner->getDeclContext(),
|
||||||
|
callback);
|
||||||
break;
|
break;
|
||||||
case RequirementReprKind::SameType:
|
case RequirementReprKind::SameType:
|
||||||
checkTypeAccessibility(TC, requirement.getFirstTypeLoc(), contextAccess,
|
checkTypeAccessibilityImpl(TC, requirement.getFirstTypeLoc(),
|
||||||
callback);
|
accessScope, owner->getDeclContext(),
|
||||||
checkTypeAccessibility(TC, requirement.getSecondTypeLoc(), contextAccess,
|
callback);
|
||||||
callback);
|
checkTypeAccessibilityImpl(TC, requirement.getSecondTypeLoc(),
|
||||||
|
accessScope, owner->getDeclContext(),
|
||||||
|
callback);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (minAccess.hasValue()) {
|
if (minAccessScope) {
|
||||||
|
auto minAccess = Accessibility::Private;
|
||||||
|
if (isa<Module>(minAccessScope))
|
||||||
|
minAccess = Accessibility::Internal;
|
||||||
|
|
||||||
bool isExplicit =
|
bool isExplicit =
|
||||||
owner->getAttrs().hasAttribute<AccessibilityAttr>() ||
|
owner->getAttrs().hasAttribute<AccessibilityAttr>() ||
|
||||||
owner->getDeclContext()->getAsProtocolOrProtocolExtensionContext();
|
owner->getDeclContext()->getAsProtocolOrProtocolExtensionContext();
|
||||||
auto diag = TC.diagnose(owner, diag::generic_param_access,
|
auto diag = TC.diagnose(owner, diag::generic_param_access,
|
||||||
owner->getDescriptiveKind(), isExplicit,
|
owner->getDescriptiveKind(), isExplicit,
|
||||||
contextAccess, minAccess.getValue(),
|
contextAccess, minAccess,
|
||||||
accessibilityErrorKind);
|
accessibilityErrorKind);
|
||||||
highlightOffendingType(TC, diag, complainRepr);
|
highlightOffendingType(TC, diag, complainRepr);
|
||||||
}
|
}
|
||||||
@@ -1505,7 +1599,22 @@ static void checkGenericParamAccessibility(TypeChecker &TC,
|
|||||||
static void checkGenericParamAccessibility(TypeChecker &TC,
|
static void checkGenericParamAccessibility(TypeChecker &TC,
|
||||||
const GenericParamList *params,
|
const GenericParamList *params,
|
||||||
const ValueDecl *owner) {
|
const ValueDecl *owner) {
|
||||||
checkGenericParamAccessibility(TC, params, owner, owner->getFormalAccess());
|
checkGenericParamAccessibility(TC, params, owner,
|
||||||
|
owner->getFormalAccessScope(),
|
||||||
|
owner->getFormalAccess());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the access level associated with \p accessScope, for diagnostic
|
||||||
|
/// purposes.
|
||||||
|
///
|
||||||
|
/// \sa ValueDecl::getFormalAccessScope
|
||||||
|
static Accessibility
|
||||||
|
accessibilityFromScopeForDiagnostics(const DeclContext *accessScope) {
|
||||||
|
if (!accessScope)
|
||||||
|
return Accessibility::Public;
|
||||||
|
if (isa<ModuleDecl>(accessScope))
|
||||||
|
return Accessibility::Internal;
|
||||||
|
return Accessibility::Private;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks the given declaration's accessibility to make sure it is valid given
|
/// Checks the given declaration's accessibility to make sure it is valid given
|
||||||
@@ -1556,8 +1665,10 @@ static void checkAccessibility(TypeChecker &TC, const Decl *D) {
|
|||||||
|
|
||||||
checkTypeAccessibility(TC, TypeLoc::withoutLoc(theVar->getType()),
|
checkTypeAccessibility(TC, TypeLoc::withoutLoc(theVar->getType()),
|
||||||
theVar,
|
theVar,
|
||||||
[&](Accessibility typeAccess,
|
[&](const DeclContext *typeAccessScope,
|
||||||
const TypeRepr *complainRepr) {
|
const TypeRepr *complainRepr) {
|
||||||
|
auto typeAccess =
|
||||||
|
accessibilityFromScopeForDiagnostics(typeAccessScope);
|
||||||
bool isExplicit =
|
bool isExplicit =
|
||||||
theVar->getAttrs().hasAttribute<AccessibilityAttr>();
|
theVar->getAttrs().hasAttribute<AccessibilityAttr>();
|
||||||
auto diag = TC.diagnose(P->getLoc(),
|
auto diag = TC.diagnose(P->getLoc(),
|
||||||
@@ -1588,8 +1699,9 @@ static void checkAccessibility(TypeChecker &TC, const Decl *D) {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
checkTypeAccessibility(TC, TP->getTypeLoc(), anyVar,
|
checkTypeAccessibility(TC, TP->getTypeLoc(), anyVar,
|
||||||
[&](Accessibility typeAccess,
|
[&](const DeclContext *typeAccessScope,
|
||||||
const TypeRepr *complainRepr) {
|
const TypeRepr *complainRepr) {
|
||||||
|
auto typeAccess = accessibilityFromScopeForDiagnostics(typeAccessScope);
|
||||||
bool isExplicit =
|
bool isExplicit =
|
||||||
anyVar->getAttrs().hasAttribute<AccessibilityAttr>() ||
|
anyVar->getAttrs().hasAttribute<AccessibilityAttr>() ||
|
||||||
anyVar->getDeclContext()->getAsProtocolOrProtocolExtensionContext();
|
anyVar->getDeclContext()->getAsProtocolOrProtocolExtensionContext();
|
||||||
@@ -1609,8 +1721,9 @@ static void checkAccessibility(TypeChecker &TC, const Decl *D) {
|
|||||||
auto TAD = cast<TypeAliasDecl>(D);
|
auto TAD = cast<TypeAliasDecl>(D);
|
||||||
|
|
||||||
checkTypeAccessibility(TC, TAD->getUnderlyingTypeLoc(), TAD,
|
checkTypeAccessibility(TC, TAD->getUnderlyingTypeLoc(), TAD,
|
||||||
[&](Accessibility typeAccess,
|
[&](const DeclContext *typeAccessScope,
|
||||||
const TypeRepr *complainRepr) {
|
const TypeRepr *complainRepr) {
|
||||||
|
auto typeAccess = accessibilityFromScopeForDiagnostics(typeAccessScope);
|
||||||
bool isExplicit = TAD->getAttrs().hasAttribute<AccessibilityAttr>();
|
bool isExplicit = TAD->getAttrs().hasAttribute<AccessibilityAttr>();
|
||||||
auto diag = TC.diagnose(TAD, diag::type_alias_underlying_type_access,
|
auto diag = TC.diagnose(TAD, diag::type_alias_underlying_type_access,
|
||||||
isExplicit, TAD->getFormalAccess(),
|
isExplicit, TAD->getFormalAccess(),
|
||||||
@@ -1629,36 +1742,41 @@ static void checkAccessibility(TypeChecker &TC, const Decl *D) {
|
|||||||
AEK_DefaultDefinition = 0,
|
AEK_DefaultDefinition = 0,
|
||||||
AEK_Requirement
|
AEK_Requirement
|
||||||
} accessibilityErrorKind;
|
} accessibilityErrorKind;
|
||||||
Optional<Accessibility> minAccess;
|
const DeclContext *minAccessScope = nullptr;
|
||||||
const TypeRepr *complainRepr = nullptr;
|
const TypeRepr *complainRepr = nullptr;
|
||||||
|
|
||||||
std::for_each(assocType->getInherited().begin(),
|
std::for_each(assocType->getInherited().begin(),
|
||||||
assocType->getInherited().end(),
|
assocType->getInherited().end(),
|
||||||
[&](TypeLoc requirement) {
|
[&](TypeLoc requirement) {
|
||||||
checkTypeAccessibility(TC, requirement, assocType,
|
checkTypeAccessibility(TC, requirement, assocType,
|
||||||
[&](Accessibility typeAccess,
|
[&](const DeclContext *typeAccessScope,
|
||||||
const TypeRepr *thisComplainRepr) {
|
const TypeRepr *thisComplainRepr) {
|
||||||
if (!minAccess || *minAccess > typeAccess) {
|
if (!minAccessScope ||
|
||||||
minAccess = typeAccess;
|
typeAccessScope->isChildContextOf(minAccessScope) ||
|
||||||
|
(!complainRepr && typeAccessScope == minAccessScope)) {
|
||||||
|
minAccessScope = typeAccessScope;
|
||||||
complainRepr = thisComplainRepr;
|
complainRepr = thisComplainRepr;
|
||||||
accessibilityErrorKind = AEK_Requirement;
|
accessibilityErrorKind = AEK_Requirement;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
checkTypeAccessibility(TC, assocType->getDefaultDefinitionLoc(), assocType,
|
checkTypeAccessibility(TC, assocType->getDefaultDefinitionLoc(), assocType,
|
||||||
[&](Accessibility typeAccess,
|
[&](const DeclContext *typeAccessScope,
|
||||||
const TypeRepr *thisComplainRepr) {
|
const TypeRepr *thisComplainRepr) {
|
||||||
if (!minAccess || *minAccess > typeAccess) {
|
if (!minAccessScope ||
|
||||||
minAccess = typeAccess;
|
typeAccessScope->isChildContextOf(minAccessScope) ||
|
||||||
|
(!complainRepr && typeAccessScope == minAccessScope)) {
|
||||||
|
minAccessScope = typeAccessScope;
|
||||||
complainRepr = thisComplainRepr;
|
complainRepr = thisComplainRepr;
|
||||||
accessibilityErrorKind = AEK_DefaultDefinition;
|
accessibilityErrorKind = AEK_DefaultDefinition;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (minAccess) {
|
if (minAccessScope) {
|
||||||
|
auto minAccess = accessibilityFromScopeForDiagnostics(minAccessScope);
|
||||||
auto diag = TC.diagnose(assocType, diag::associated_type_access,
|
auto diag = TC.diagnose(assocType, diag::associated_type_access,
|
||||||
assocType->getFormalAccess(),
|
assocType->getFormalAccess(),
|
||||||
*minAccess, accessibilityErrorKind);
|
minAccess, accessibilityErrorKind);
|
||||||
highlightOffendingType(TC, diag, complainRepr);
|
highlightOffendingType(TC, diag, complainRepr);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -1681,8 +1799,9 @@ static void checkAccessibility(TypeChecker &TC, const Decl *D) {
|
|||||||
if (rawTypeLocIter == ED->getInherited().end())
|
if (rawTypeLocIter == ED->getInherited().end())
|
||||||
return;
|
return;
|
||||||
checkTypeAccessibility(TC, *rawTypeLocIter, ED,
|
checkTypeAccessibility(TC, *rawTypeLocIter, ED,
|
||||||
[&](Accessibility typeAccess,
|
[&](const DeclContext *typeAccessScope,
|
||||||
const TypeRepr *complainRepr) {
|
const TypeRepr *complainRepr) {
|
||||||
|
auto typeAccess = accessibilityFromScopeForDiagnostics(typeAccessScope);
|
||||||
bool isExplicit = ED->getAttrs().hasAttribute<AccessibilityAttr>();
|
bool isExplicit = ED->getAttrs().hasAttribute<AccessibilityAttr>();
|
||||||
auto diag = TC.diagnose(ED, diag::enum_raw_type_access,
|
auto diag = TC.diagnose(ED, diag::enum_raw_type_access,
|
||||||
isExplicit, ED->getFormalAccess(),
|
isExplicit, ED->getFormalAccess(),
|
||||||
@@ -1717,8 +1836,9 @@ static void checkAccessibility(TypeChecker &TC, const Decl *D) {
|
|||||||
if (superclassLocIter == CD->getInherited().end())
|
if (superclassLocIter == CD->getInherited().end())
|
||||||
return;
|
return;
|
||||||
checkTypeAccessibility(TC, *superclassLocIter, CD,
|
checkTypeAccessibility(TC, *superclassLocIter, CD,
|
||||||
[&](Accessibility typeAccess,
|
[&](const DeclContext *typeAccessScope,
|
||||||
const TypeRepr *complainRepr) {
|
const TypeRepr *complainRepr) {
|
||||||
|
auto typeAccess = accessibilityFromScopeForDiagnostics(typeAccessScope);
|
||||||
bool isExplicit = CD->getAttrs().hasAttribute<AccessibilityAttr>();
|
bool isExplicit = CD->getAttrs().hasAttribute<AccessibilityAttr>();
|
||||||
auto diag = TC.diagnose(CD, diag::class_super_access,
|
auto diag = TC.diagnose(CD, diag::class_super_access,
|
||||||
isExplicit, CD->getFormalAccess(),
|
isExplicit, CD->getFormalAccess(),
|
||||||
@@ -1733,28 +1853,29 @@ static void checkAccessibility(TypeChecker &TC, const Decl *D) {
|
|||||||
case DeclKind::Protocol: {
|
case DeclKind::Protocol: {
|
||||||
auto proto = cast<ProtocolDecl>(D);
|
auto proto = cast<ProtocolDecl>(D);
|
||||||
|
|
||||||
Optional<Accessibility> minAccess;
|
const DeclContext *minAccessScope = nullptr;
|
||||||
const TypeRepr *complainRepr = nullptr;
|
const TypeRepr *complainRepr = nullptr;
|
||||||
|
|
||||||
std::for_each(proto->getInherited().begin(),
|
std::for_each(proto->getInherited().begin(),
|
||||||
proto->getInherited().end(),
|
proto->getInherited().end(),
|
||||||
[&](TypeLoc requirement) {
|
[&](TypeLoc requirement) {
|
||||||
checkTypeAccessibility(TC, requirement, proto,
|
checkTypeAccessibility(TC, requirement, proto,
|
||||||
[&](Accessibility typeAccess,
|
[&](const DeclContext *typeAccessScope,
|
||||||
const TypeRepr *thisComplainRepr) {
|
const TypeRepr *thisComplainRepr) {
|
||||||
if (!minAccess || *minAccess > typeAccess) {
|
if (!minAccessScope ||
|
||||||
minAccess = typeAccess;
|
typeAccessScope->isChildContextOf(minAccessScope) ||
|
||||||
|
(!complainRepr && typeAccessScope == minAccessScope)) {
|
||||||
|
minAccessScope = typeAccessScope;
|
||||||
complainRepr = thisComplainRepr;
|
complainRepr = thisComplainRepr;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (minAccess) {
|
if (minAccessScope) {
|
||||||
|
auto minAccess = accessibilityFromScopeForDiagnostics(minAccessScope);
|
||||||
bool isExplicit = proto->getAttrs().hasAttribute<AccessibilityAttr>();
|
bool isExplicit = proto->getAttrs().hasAttribute<AccessibilityAttr>();
|
||||||
auto diag = TC.diagnose(proto, diag::protocol_refine_access,
|
auto diag = TC.diagnose(proto, diag::protocol_refine_access,
|
||||||
isExplicit,
|
isExplicit, proto->getFormalAccess(), minAccess);
|
||||||
proto->getFormalAccess(),
|
|
||||||
*minAccess);
|
|
||||||
highlightOffendingType(TC, diag, complainRepr);
|
highlightOffendingType(TC, diag, complainRepr);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -1763,38 +1884,43 @@ static void checkAccessibility(TypeChecker &TC, const Decl *D) {
|
|||||||
case DeclKind::Subscript: {
|
case DeclKind::Subscript: {
|
||||||
auto SD = cast<SubscriptDecl>(D);
|
auto SD = cast<SubscriptDecl>(D);
|
||||||
|
|
||||||
Optional<Accessibility> minAccess;
|
const DeclContext *minAccessScope = nullptr;
|
||||||
const TypeRepr *complainRepr = nullptr;
|
const TypeRepr *complainRepr = nullptr;
|
||||||
bool problemIsElement = false;
|
bool problemIsElement = false;
|
||||||
for (auto &P : *SD->getIndices()) {
|
for (auto &P : *SD->getIndices()) {
|
||||||
checkTypeAccessibility(TC, P->getTypeLoc(), SD,
|
checkTypeAccessibility(TC, P->getTypeLoc(), SD,
|
||||||
[&](Accessibility typeAccess,
|
[&](const DeclContext *typeAccessScope,
|
||||||
const TypeRepr *thisComplainRepr) {
|
const TypeRepr *thisComplainRepr) {
|
||||||
if (!minAccess || *minAccess > typeAccess) {
|
if (!minAccessScope ||
|
||||||
minAccess = typeAccess;
|
typeAccessScope->isChildContextOf(minAccessScope) ||
|
||||||
|
(!complainRepr && typeAccessScope == minAccessScope)) {
|
||||||
|
minAccessScope = typeAccessScope;
|
||||||
complainRepr = thisComplainRepr;
|
complainRepr = thisComplainRepr;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
checkTypeAccessibility(TC, SD->getElementTypeLoc(), SD,
|
checkTypeAccessibility(TC, SD->getElementTypeLoc(), SD,
|
||||||
[&](Accessibility typeAccess,
|
[&](const DeclContext *typeAccessScope,
|
||||||
const TypeRepr *thisComplainRepr) {
|
const TypeRepr *thisComplainRepr) {
|
||||||
if (!minAccess || *minAccess > typeAccess) {
|
if (!minAccessScope ||
|
||||||
minAccess = typeAccess;
|
typeAccessScope->isChildContextOf(minAccessScope) ||
|
||||||
|
(!complainRepr && typeAccessScope == minAccessScope)) {
|
||||||
|
minAccessScope = typeAccessScope;
|
||||||
complainRepr = thisComplainRepr;
|
complainRepr = thisComplainRepr;
|
||||||
problemIsElement = true;
|
problemIsElement = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (minAccess) {
|
if (minAccessScope) {
|
||||||
|
auto minAccess = accessibilityFromScopeForDiagnostics(minAccessScope);
|
||||||
bool isExplicit =
|
bool isExplicit =
|
||||||
SD->getAttrs().hasAttribute<AccessibilityAttr>() ||
|
SD->getAttrs().hasAttribute<AccessibilityAttr>() ||
|
||||||
SD->getDeclContext()->getAsProtocolOrProtocolExtensionContext();
|
SD->getDeclContext()->getAsProtocolOrProtocolExtensionContext();
|
||||||
auto diag = TC.diagnose(SD, diag::subscript_type_access,
|
auto diag = TC.diagnose(SD, diag::subscript_type_access,
|
||||||
isExplicit,
|
isExplicit,
|
||||||
SD->getFormalAccess(),
|
SD->getFormalAccess(),
|
||||||
*minAccess,
|
minAccess,
|
||||||
problemIsElement);
|
problemIsElement);
|
||||||
highlightOffendingType(TC, diag, complainRepr);
|
highlightOffendingType(TC, diag, complainRepr);
|
||||||
}
|
}
|
||||||
@@ -1818,15 +1944,17 @@ static void checkAccessibility(TypeChecker &TC, const Decl *D) {
|
|||||||
FK_Initializer
|
FK_Initializer
|
||||||
};
|
};
|
||||||
|
|
||||||
Optional<Accessibility> minAccess;
|
const DeclContext *minAccessScope = nullptr;
|
||||||
const TypeRepr *complainRepr = nullptr;
|
const TypeRepr *complainRepr = nullptr;
|
||||||
for (auto *PL : fn->getParameterLists().slice(isTypeContext)) {
|
for (auto *PL : fn->getParameterLists().slice(isTypeContext)) {
|
||||||
for (auto &P : *PL) {
|
for (auto &P : *PL) {
|
||||||
checkTypeAccessibility(TC, P->getTypeLoc(), fn,
|
checkTypeAccessibility(TC, P->getTypeLoc(), fn,
|
||||||
[&](Accessibility typeAccess,
|
[&](const DeclContext *typeAccessScope,
|
||||||
const TypeRepr *thisComplainRepr) {
|
const TypeRepr *thisComplainRepr) {
|
||||||
if (!minAccess || *minAccess > typeAccess) {
|
if (!minAccessScope ||
|
||||||
minAccess = typeAccess;
|
typeAccessScope->isChildContextOf(minAccessScope) ||
|
||||||
|
(!complainRepr && typeAccessScope == minAccessScope)) {
|
||||||
|
minAccessScope = typeAccessScope;
|
||||||
complainRepr = thisComplainRepr;
|
complainRepr = thisComplainRepr;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -1836,24 +1964,27 @@ static void checkAccessibility(TypeChecker &TC, const Decl *D) {
|
|||||||
bool problemIsResult = false;
|
bool problemIsResult = false;
|
||||||
if (auto FD = dyn_cast<FuncDecl>(fn)) {
|
if (auto FD = dyn_cast<FuncDecl>(fn)) {
|
||||||
checkTypeAccessibility(TC, FD->getBodyResultTypeLoc(), FD,
|
checkTypeAccessibility(TC, FD->getBodyResultTypeLoc(), FD,
|
||||||
[&](Accessibility typeAccess,
|
[&](const DeclContext *typeAccessScope,
|
||||||
const TypeRepr *thisComplainRepr) {
|
const TypeRepr *thisComplainRepr) {
|
||||||
if (!minAccess || *minAccess > typeAccess) {
|
if (!minAccessScope ||
|
||||||
minAccess = typeAccess;
|
typeAccessScope->isChildContextOf(minAccessScope) ||
|
||||||
|
(!complainRepr && typeAccessScope == minAccessScope)) {
|
||||||
|
minAccessScope = typeAccessScope;
|
||||||
complainRepr = thisComplainRepr;
|
complainRepr = thisComplainRepr;
|
||||||
problemIsResult = true;
|
problemIsResult = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (minAccess) {
|
if (minAccessScope) {
|
||||||
|
auto minAccess = accessibilityFromScopeForDiagnostics(minAccessScope);
|
||||||
bool isExplicit =
|
bool isExplicit =
|
||||||
fn->getAttrs().hasAttribute<AccessibilityAttr>() ||
|
fn->getAttrs().hasAttribute<AccessibilityAttr>() ||
|
||||||
D->getDeclContext()->getAsProtocolOrProtocolExtensionContext();
|
D->getDeclContext()->getAsProtocolOrProtocolExtensionContext();
|
||||||
auto diag = TC.diagnose(fn, diag::function_type_access,
|
auto diag = TC.diagnose(fn, diag::function_type_access,
|
||||||
isExplicit,
|
isExplicit,
|
||||||
fn->getFormalAccess(),
|
fn->getFormalAccess(),
|
||||||
*minAccess,
|
minAccess,
|
||||||
isa<ConstructorDecl>(fn) ? FK_Initializer :
|
isa<ConstructorDecl>(fn) ? FK_Initializer :
|
||||||
isTypeContext ? FK_Method : FK_Function,
|
isTypeContext ? FK_Method : FK_Function,
|
||||||
problemIsResult);
|
problemIsResult);
|
||||||
@@ -1868,8 +1999,9 @@ static void checkAccessibility(TypeChecker &TC, const Decl *D) {
|
|||||||
if (!EED->hasArgumentType())
|
if (!EED->hasArgumentType())
|
||||||
return;
|
return;
|
||||||
checkTypeAccessibility(TC, EED->getArgumentTypeLoc(), EED,
|
checkTypeAccessibility(TC, EED->getArgumentTypeLoc(), EED,
|
||||||
[&](Accessibility typeAccess,
|
[&](const DeclContext *typeAccessScope,
|
||||||
const TypeRepr *complainRepr) {
|
const TypeRepr *complainRepr) {
|
||||||
|
auto typeAccess = accessibilityFromScopeForDiagnostics(typeAccessScope);
|
||||||
auto diag = TC.diagnose(EED, diag::enum_case_access,
|
auto diag = TC.diagnose(EED, diag::enum_case_access,
|
||||||
EED->getFormalAccess(), typeAccess);
|
EED->getFormalAccess(), typeAccess);
|
||||||
highlightOffendingType(TC, diag, complainRepr);
|
highlightOffendingType(TC, diag, complainRepr);
|
||||||
@@ -5762,8 +5894,20 @@ public:
|
|||||||
if (!IsFirstPass) {
|
if (!IsFirstPass) {
|
||||||
TC.computeDefaultAccessibility(ED);
|
TC.computeDefaultAccessibility(ED);
|
||||||
if (auto *AA = ED->getAttrs().getAttribute<AccessibilityAttr>()) {
|
if (auto *AA = ED->getAttrs().getAttribute<AccessibilityAttr>()) {
|
||||||
|
const DeclContext *desiredAccessScope;
|
||||||
|
switch (AA->getAccess()) {
|
||||||
|
case Accessibility::Private:
|
||||||
|
desiredAccessScope = ED->getModuleScopeContext();
|
||||||
|
break;
|
||||||
|
case Accessibility::Internal:
|
||||||
|
desiredAccessScope = ED->getModuleContext();
|
||||||
|
break;
|
||||||
|
case Accessibility::Public:
|
||||||
|
desiredAccessScope = nullptr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
checkGenericParamAccessibility(TC, ED->getGenericParams(), ED,
|
checkGenericParamAccessibility(TC, ED->getGenericParams(), ED,
|
||||||
AA->getAccess());
|
desiredAccessScope, AA->getAccess());
|
||||||
}
|
}
|
||||||
TC.checkConformancesInContext(ED, ED);
|
TC.checkConformancesInContext(ED, ED);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1231,7 +1231,7 @@ checkWitnessAccessibility(Accessibility *requiredAccess,
|
|||||||
*requiredAccess = std::min(Proto->getFormalAccess(), *requiredAccess);
|
*requiredAccess = std::min(Proto->getFormalAccess(), *requiredAccess);
|
||||||
|
|
||||||
if (*requiredAccess > Accessibility::Private) {
|
if (*requiredAccess > Accessibility::Private) {
|
||||||
if (witness->getFormalAccess() < *requiredAccess)
|
if (witness->getFormalAccess(DC) < *requiredAccess)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (requirement->isSettable(DC)) {
|
if (requirement->isSettable(DC)) {
|
||||||
@@ -4609,7 +4609,13 @@ void TypeChecker::checkConformancesInContext(DeclContext *dc,
|
|||||||
// For anything with a Clang node, lazily check conformances.
|
// For anything with a Clang node, lazily check conformances.
|
||||||
if (ext->hasClangNode()) return;
|
if (ext->hasClangNode()) return;
|
||||||
|
|
||||||
defaultAccessibility = ext->getDefaultAccessibility();
|
Type extendedTy = ext->getExtendedType();
|
||||||
|
if (!extendedTy)
|
||||||
|
return;
|
||||||
|
const NominalTypeDecl *nominal = extendedTy->getAnyNominal();
|
||||||
|
if (!nominal)
|
||||||
|
return;
|
||||||
|
defaultAccessibility = nominal->getFormalAccess();
|
||||||
} else {
|
} else {
|
||||||
// For anything with a Clang node, lazily check conformances.
|
// For anything with a Clang node, lazily check conformances.
|
||||||
auto nominal = cast<NominalTypeDecl>(dc);
|
auto nominal = cast<NominalTypeDecl>(dc);
|
||||||
|
|||||||
@@ -460,12 +460,18 @@ public:
|
|||||||
/// during type checking.
|
/// during type checking.
|
||||||
llvm::SetVector<NominalTypeDecl *> ValidatedTypes;
|
llvm::SetVector<NominalTypeDecl *> ValidatedTypes;
|
||||||
|
|
||||||
/// Caches whether a particular type is accessible from a particular file
|
using TypeAccessScopeCacheMap = llvm::DenseMap<Type, const DeclContext *>;
|
||||||
/// unit.
|
|
||||||
|
/// Caches the outermost scope where a particular type can be used, relative
|
||||||
|
/// to a particular file.
|
||||||
|
///
|
||||||
|
/// The file is used to handle things like \c \@testable. A null-but-present
|
||||||
|
/// value means the type is public.
|
||||||
///
|
///
|
||||||
/// This can't use CanTypes because typealiases may have more limited types
|
/// This can't use CanTypes because typealiases may have more limited types
|
||||||
/// than their underlying types.
|
/// than their underlying types.
|
||||||
llvm::DenseMap<Type, Accessibility> TypeAccessibilityCache;
|
llvm::DenseMap<const SourceFile *, TypeAccessScopeCacheMap>
|
||||||
|
TypeAccessScopeCache;
|
||||||
|
|
||||||
// Caches whether a given declaration is "as specialized" as another.
|
// Caches whether a given declaration is "as specialized" as another.
|
||||||
llvm::DenseMap<std::pair<ValueDecl*, ValueDecl*>, bool>
|
llvm::DenseMap<std::pair<ValueDecl*, ValueDecl*>, bool>
|
||||||
|
|||||||
@@ -307,7 +307,9 @@ private extension Use4 {
|
|||||||
// CHECK-DAG: !private "PrivateTopLevelTy2"
|
// CHECK-DAG: !private "PrivateTopLevelTy2"
|
||||||
// CHECK-DAG: "PrivateProto2"
|
// CHECK-DAG: "PrivateProto2"
|
||||||
extension Private2 : PrivateProto2 {
|
extension Private2 : PrivateProto2 {
|
||||||
var privateTy2: PrivateTopLevelTy2? { return nil }
|
// FIXME: This test is supposed to check that we get this behavior /without/
|
||||||
|
// marking the property private, just from the base type.
|
||||||
|
private var privateTy2: PrivateTopLevelTy2? { return nil }
|
||||||
}
|
}
|
||||||
// CHECK-DAG: !private "PrivateTopLevelTy3"
|
// CHECK-DAG: !private "PrivateTopLevelTy3"
|
||||||
func outerPrivateTy3() {
|
func outerPrivateTy3() {
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ internal struct InternalStruct {} // expected-note * {{declared here}}
|
|||||||
private struct PrivateStruct {} // expected-note * {{declared here}}
|
private struct PrivateStruct {} // expected-note * {{declared here}}
|
||||||
|
|
||||||
protocol InternalProto { // expected-note * {{declared here}}
|
protocol InternalProto { // expected-note * {{declared here}}
|
||||||
associatedtype Assoc // expected-note {{type declared here}}
|
associatedtype Assoc
|
||||||
}
|
}
|
||||||
public extension InternalProto {} // expected-error {{extension of internal protocol cannot be declared public}} {{1-8=}}
|
public extension InternalProto {} // expected-error {{extension of internal protocol cannot be declared public}} {{1-8=}}
|
||||||
internal extension InternalProto where Assoc == PublicStruct {}
|
internal extension InternalProto where Assoc == PublicStruct {}
|
||||||
@@ -169,7 +169,7 @@ private extension InternalProto where Assoc == InternalStruct {}
|
|||||||
private extension InternalProto where Assoc == PrivateStruct {}
|
private extension InternalProto where Assoc == PrivateStruct {}
|
||||||
|
|
||||||
public protocol PublicProto {
|
public protocol PublicProto {
|
||||||
associatedtype Assoc // expected-note * {{type declared here}}
|
associatedtype Assoc
|
||||||
}
|
}
|
||||||
public extension PublicProto {}
|
public extension PublicProto {}
|
||||||
public extension PublicProto where Assoc == PublicStruct {}
|
public extension PublicProto where Assoc == PublicStruct {}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
// See http://swift.org/LICENSE.txt for license information
|
// See http://swift.org/LICENSE.txt for license information
|
||||||
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||||
|
|
||||||
// RUN: not --crash %target-swift-frontend %s -parse
|
// RUN: not %target-swift-frontend %s -parse
|
||||||
// REQUIRES: asserts
|
// REQUIRES: asserts
|
||||||
private class B
|
private class B
|
||||||
class d<T where B<T>:w
|
class d<T where B<T>:w
|
||||||
Reference in New Issue
Block a user