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:
Jordan Rose
2016-07-21 14:54:48 -07:00
parent c7facf12b2
commit 758cf64283
9 changed files with 399 additions and 163 deletions

View File

@@ -2167,16 +2167,42 @@ public:
return TypeAndAccess.getInt().hasValue();
}
/// \see getFormalAccess
Accessibility getFormalAccessImpl(const DeclContext *useDC) const;
/// Returns the access level specified explicitly by the user, or provided by
/// default according to language rules.
///
/// This is the access used when calculating if access control is being used
/// consistently.
Accessibility getFormalAccess() const {
/// 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.
///
/// \sa getFormalAccessScope
Accessibility getFormalAccess(const DeclContext *useDC = nullptr) const {
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
/// be emitted and may be used.
///

View File

@@ -1847,6 +1847,57 @@ Accessibility ValueDecl::getEffectiveAccess() const {
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 {
if (auto TAD = dyn_cast<TypeAliasDecl>(this)) {

View File

@@ -379,6 +379,26 @@ static OperatorFixity getDeclFixity(const ValueDecl *decl) {
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) {
if (decl->getDeclContext()->isLocalContext()) {
// Mangle local declarations with a numeric discriminator.
@@ -387,47 +407,28 @@ void Mangler::mangleDeclName(const ValueDecl *decl) {
// Fall through to mangle the <identifier>.
} else if (decl->hasAccessibility() &&
decl->getFormalAccess() == Accessibility::Private) {
decl->getFormalAccess() == Accessibility::Private &&
!isInPrivateOrLocalContext(decl)) {
// Mangle non-local private declarations with a textual discriminator
// based on their enclosing file.
// decl-name ::= 'P' identifier identifier
// Don't bother to use the private discriminator if the enclosing context
// is also private.
auto isWithinPrivateNominal = [](const Decl *D) -> bool {
const DeclContext *DC = D->getDeclContext();
if (!DC->isTypeContext()) {
assert((DC->isModuleScopeContext() || DC->isLocalContext()) &&
"do we need a private discriminator for this context?");
return false;
}
// 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);
auto declaredType = DC->getDeclaredTypeOfContext();
if (!declaredType || declaredType->is<ErrorType>())
return false;
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");
return (declaredType->getAnyNominal()->getFormalAccess()
== Accessibility::Private);
};
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);
}
Buffer << 'P';
mangleIdentifier(discriminator);
// Fall through to mangle the name.
}

View File

@@ -1168,54 +1168,84 @@ void swift::configureConstructorType(ConstructorDecl *ctor,
namespace {
class TypeAccessibilityChecker : private TypeWalker {
using TypeAccessibilityCacheMap =
decltype(TypeChecker::TypeAccessibilityCache);
TypeAccessibilityCacheMap &Cache;
SmallVector<Accessibility, 8> AccessStack;
class TypeAccessScopeChecker : private TypeWalker {
using TypeAccessScopeCacheMap = TypeChecker::TypeAccessScopeCacheMap;
TypeAccessScopeCacheMap &Cache;
const SourceFile *File;
SmallVector<intptr_t, 8> RawScopeStack;
explicit TypeAccessibilityChecker(TypeAccessibilityCacheMap &cache)
: Cache(cache) {
static constexpr const intptr_t INVALID = -1;
explicit TypeAccessScopeChecker(TypeAccessScopeCacheMap &cache,
const SourceFile *file)
: Cache(cache), File(file) {
// Always have something on the stack.
AccessStack.push_back(Accessibility::Private);
RawScopeStack.push_back((intptr_t)INVALID);
}
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 {
// Assume failure until we post-visit this node.
// This will be correct as long as we don't ever have self-referential
// Types.
auto cached = Cache.find(ty);
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;
}
Accessibility current;
const DeclContext *DC;
if (auto alias = dyn_cast<NameAliasType>(ty.getPointer()))
current = alias->getDecl()->getFormalAccess();
DC = alias->getDecl()->getFormalAccessScope(File);
else if (auto nominal = ty->getAnyNominal())
current = nominal->getFormalAccess();
DC = nominal->getFormalAccessScope(File);
else
current = Accessibility::Public;
AccessStack.push_back(current);
DC = nullptr;
RawScopeStack.push_back(reinterpret_cast<intptr_t>(DC));
return Action::Continue;
}
Action walkToTypePost(Type ty) override {
Accessibility last = AccessStack.pop_back_val();
Cache[ty] = last;
AccessStack.back() = std::min(AccessStack.back(), last);
auto last = RawScopeStack.pop_back_val();
if (last != INVALID)
Cache[ty] = reinterpret_cast<const DeclContext *>(last);
RawScopeStack.back() = intersectAccess(RawScopeStack.back(), last);
return Action::Continue;
}
public:
static Accessibility getAccessibility(Type ty,
TypeAccessibilityCacheMap &cache) {
ty.walk(TypeAccessibilityChecker(cache));
return cache[ty];
static Optional<const DeclContext *>
getAccessScope(Type ty, const DeclContext *useDC,
decltype(TypeChecker::TypeAccessScopeCache) &caches) {
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()) {
auto getTypeAccess = [this](const TypeLoc &TL) {
auto getTypeAccess = [this, ED](const TypeLoc &TL) -> Accessibility {
if (!TL.getType())
return Accessibility::Public;
return TypeAccessibilityChecker::getAccessibility(TL.getType(),
TypeAccessibilityCache);
auto scopeDC =
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
@@ -1352,10 +1397,16 @@ void TypeChecker::computeAccessibility(ValueDecl *D) {
namespace {
class TypeAccessibilityDiagnoser : private ASTWalker {
const ComponentIdentTypeRepr *minAccessibilityType = nullptr;
class TypeAccessScopeDiagnoser : private ASTWalker {
const DeclContext *accessScope;
const DeclContext *useDC;
const ComponentIdentTypeRepr *offendingType = nullptr;
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);
if (!CITR)
return true;
@@ -1364,55 +1415,84 @@ class TypeAccessibilityDiagnoser : private ASTWalker {
if (!VD)
return true;
if (minAccessibilityType) {
const ValueDecl *minDecl = minAccessibilityType->getBoundDecl();
if (minDecl->getFormalAccess() <= VD->getFormalAccess())
return true;
}
if (VD->getFormalAccessScope(useDC) != accessScope)
return true;
minAccessibilityType = CITR;
return true;
offendingType = CITR;
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:
static const TypeRepr *findMinAccessibleType(TypeRepr *TR) {
TypeAccessibilityDiagnoser diagnoser;
static const TypeRepr *findTypeWithScope(TypeRepr *TR,
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);
return diagnoser.minAccessibilityType;
return diagnoser.offendingType;
}
};
} // end anonymous namespace
/// Checks if the accessibility of the type described by \p TL is at least
/// \p contextAccess. If it isn't, calls \p diagnose with a TypeRepr
/// Checks if the access scope of the type described by \p TL contains
/// \p contextAccessScope. If it isn't, calls \p diagnose with a TypeRepr
/// 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
/// part of the type that caused the problem could not be found.
static void checkTypeAccessibility(
TypeChecker &TC, TypeLoc TL, Accessibility contextAccess,
llvm::function_ref<void(Accessibility, const TypeRepr *)> diagnose) {
// Don't spend time checking private access; this is always valid.
// This includes local declarations.
if (contextAccess == Accessibility::Private || !TL.getType())
/// part of the type that caused the problem could not be found. The DeclContext
/// is never null.
static void checkTypeAccessibilityImpl(
TypeChecker &TC, TypeLoc TL, const DeclContext *contextAccessScope,
const DeclContext *useDC,
llvm::function_ref<void(const DeclContext *, const TypeRepr *)> diagnose) {
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;
Accessibility typeAccess =
TypeAccessibilityChecker::getAccessibility(TL.getType(),
TC.TypeAccessibilityCache);
if (typeAccess >= contextAccess)
Optional<const DeclContext *> typeAccessScope =
TypeAccessScopeChecker::getAccessScope(TL.getType(), useDC,
TC.TypeAccessScopeCache);
// 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;
const TypeRepr *complainRepr = nullptr;
if (TypeRepr *TR = TL.getTypeRepr())
complainRepr = TypeAccessibilityDiagnoser::findMinAccessibleType(TR);
diagnose(typeAccess, complainRepr);
if (TypeRepr *TR = TL.getTypeRepr()) {
complainRepr =
TypeAccessScopeDiagnoser::findTypeWithScope(TR, *typeAccessScope,
useDC);
}
diagnose(*typeAccessScope, complainRepr);
}
static void checkTypeAccessibility(
TypeChecker &TC, TypeLoc TL, const ValueDecl *context,
llvm::function_ref<void(Accessibility, const TypeRepr *)> diagnose) {
checkTypeAccessibility(TC, TL, context->getFormalAccess(), diagnose);
llvm::function_ref<void(const DeclContext *, const TypeRepr *)> diagnose) {
checkTypeAccessibilityImpl(TC, TL, context->getFormalAccessScope(),
context->getDeclContext(), diagnose);
}
/// 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,
const GenericParamList *params,
const Decl *owner,
const DeclContext *accessScope,
Accessibility contextAccess) {
if (!params)
return;
@@ -1447,18 +1528,21 @@ static void checkGenericParamAccessibility(TypeChecker &TC,
AEK_Parameter = 0,
AEK_Requirement
} accessibilityErrorKind;
Optional<Accessibility> minAccess;
const DeclContext *minAccessScope = nullptr;
const TypeRepr *complainRepr = nullptr;
for (auto param : *params) {
if (param->getInherited().empty())
continue;
assert(param->getInherited().size() == 1);
checkTypeAccessibility(TC, param->getInherited().front(), contextAccess,
[&](Accessibility typeAccess,
const TypeRepr *thisComplainRepr) {
if (!minAccess || *minAccess > typeAccess) {
minAccess = typeAccess;
checkTypeAccessibilityImpl(TC, param->getInherited().front(), accessScope,
owner->getDeclContext(),
[&](const DeclContext *typeAccessScope,
const TypeRepr *thisComplainRepr) {
if (!minAccessScope ||
typeAccessScope->isChildContextOf(minAccessScope) ||
(!complainRepr && typeAccessScope == minAccessScope)) {
minAccessScope = typeAccessScope;
complainRepr = thisComplainRepr;
accessibilityErrorKind = AEK_Parameter;
}
@@ -1466,37 +1550,47 @@ static void checkGenericParamAccessibility(TypeChecker &TC,
}
for (auto &requirement : params->getRequirements()) {
auto callback = [&](Accessibility typeAccess,
auto callback = [&](const DeclContext *typeAccessScope,
const TypeRepr *thisComplainRepr) {
if (!minAccess || *minAccess > typeAccess) {
minAccess = typeAccess;
if (!minAccessScope ||
typeAccessScope->isChildContextOf(minAccessScope) ||
(!complainRepr && typeAccessScope == minAccessScope)) {
minAccessScope = typeAccessScope;
complainRepr = thisComplainRepr;
accessibilityErrorKind = AEK_Requirement;
}
};
switch (requirement.getKind()) {
case RequirementReprKind::TypeConstraint:
checkTypeAccessibility(TC, requirement.getSubjectLoc(), contextAccess,
callback);
checkTypeAccessibility(TC, requirement.getConstraintLoc(), contextAccess,
callback);
checkTypeAccessibilityImpl(TC, requirement.getSubjectLoc(),
accessScope, owner->getDeclContext(),
callback);
checkTypeAccessibilityImpl(TC, requirement.getConstraintLoc(),
accessScope, owner->getDeclContext(),
callback);
break;
case RequirementReprKind::SameType:
checkTypeAccessibility(TC, requirement.getFirstTypeLoc(), contextAccess,
callback);
checkTypeAccessibility(TC, requirement.getSecondTypeLoc(), contextAccess,
callback);
checkTypeAccessibilityImpl(TC, requirement.getFirstTypeLoc(),
accessScope, owner->getDeclContext(),
callback);
checkTypeAccessibilityImpl(TC, requirement.getSecondTypeLoc(),
accessScope, owner->getDeclContext(),
callback);
break;
}
}
if (minAccess.hasValue()) {
if (minAccessScope) {
auto minAccess = Accessibility::Private;
if (isa<Module>(minAccessScope))
minAccess = Accessibility::Internal;
bool isExplicit =
owner->getAttrs().hasAttribute<AccessibilityAttr>() ||
owner->getDeclContext()->getAsProtocolOrProtocolExtensionContext();
auto diag = TC.diagnose(owner, diag::generic_param_access,
owner->getDescriptiveKind(), isExplicit,
contextAccess, minAccess.getValue(),
contextAccess, minAccess,
accessibilityErrorKind);
highlightOffendingType(TC, diag, complainRepr);
}
@@ -1505,7 +1599,22 @@ static void checkGenericParamAccessibility(TypeChecker &TC,
static void checkGenericParamAccessibility(TypeChecker &TC,
const GenericParamList *params,
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
@@ -1556,8 +1665,10 @@ static void checkAccessibility(TypeChecker &TC, const Decl *D) {
checkTypeAccessibility(TC, TypeLoc::withoutLoc(theVar->getType()),
theVar,
[&](Accessibility typeAccess,
[&](const DeclContext *typeAccessScope,
const TypeRepr *complainRepr) {
auto typeAccess =
accessibilityFromScopeForDiagnostics(typeAccessScope);
bool isExplicit =
theVar->getAttrs().hasAttribute<AccessibilityAttr>();
auto diag = TC.diagnose(P->getLoc(),
@@ -1588,8 +1699,9 @@ static void checkAccessibility(TypeChecker &TC, const Decl *D) {
return;
checkTypeAccessibility(TC, TP->getTypeLoc(), anyVar,
[&](Accessibility typeAccess,
[&](const DeclContext *typeAccessScope,
const TypeRepr *complainRepr) {
auto typeAccess = accessibilityFromScopeForDiagnostics(typeAccessScope);
bool isExplicit =
anyVar->getAttrs().hasAttribute<AccessibilityAttr>() ||
anyVar->getDeclContext()->getAsProtocolOrProtocolExtensionContext();
@@ -1609,8 +1721,9 @@ static void checkAccessibility(TypeChecker &TC, const Decl *D) {
auto TAD = cast<TypeAliasDecl>(D);
checkTypeAccessibility(TC, TAD->getUnderlyingTypeLoc(), TAD,
[&](Accessibility typeAccess,
[&](const DeclContext *typeAccessScope,
const TypeRepr *complainRepr) {
auto typeAccess = accessibilityFromScopeForDiagnostics(typeAccessScope);
bool isExplicit = TAD->getAttrs().hasAttribute<AccessibilityAttr>();
auto diag = TC.diagnose(TAD, diag::type_alias_underlying_type_access,
isExplicit, TAD->getFormalAccess(),
@@ -1629,36 +1742,41 @@ static void checkAccessibility(TypeChecker &TC, const Decl *D) {
AEK_DefaultDefinition = 0,
AEK_Requirement
} accessibilityErrorKind;
Optional<Accessibility> minAccess;
const DeclContext *minAccessScope = nullptr;
const TypeRepr *complainRepr = nullptr;
std::for_each(assocType->getInherited().begin(),
assocType->getInherited().end(),
[&](TypeLoc requirement) {
checkTypeAccessibility(TC, requirement, assocType,
[&](Accessibility typeAccess,
[&](const DeclContext *typeAccessScope,
const TypeRepr *thisComplainRepr) {
if (!minAccess || *minAccess > typeAccess) {
minAccess = typeAccess;
if (!minAccessScope ||
typeAccessScope->isChildContextOf(minAccessScope) ||
(!complainRepr && typeAccessScope == minAccessScope)) {
minAccessScope = typeAccessScope;
complainRepr = thisComplainRepr;
accessibilityErrorKind = AEK_Requirement;
}
});
});
checkTypeAccessibility(TC, assocType->getDefaultDefinitionLoc(), assocType,
[&](Accessibility typeAccess,
[&](const DeclContext *typeAccessScope,
const TypeRepr *thisComplainRepr) {
if (!minAccess || *minAccess > typeAccess) {
minAccess = typeAccess;
if (!minAccessScope ||
typeAccessScope->isChildContextOf(minAccessScope) ||
(!complainRepr && typeAccessScope == minAccessScope)) {
minAccessScope = typeAccessScope;
complainRepr = thisComplainRepr;
accessibilityErrorKind = AEK_DefaultDefinition;
}
});
if (minAccess) {
if (minAccessScope) {
auto minAccess = accessibilityFromScopeForDiagnostics(minAccessScope);
auto diag = TC.diagnose(assocType, diag::associated_type_access,
assocType->getFormalAccess(),
*minAccess, accessibilityErrorKind);
minAccess, accessibilityErrorKind);
highlightOffendingType(TC, diag, complainRepr);
}
return;
@@ -1681,8 +1799,9 @@ static void checkAccessibility(TypeChecker &TC, const Decl *D) {
if (rawTypeLocIter == ED->getInherited().end())
return;
checkTypeAccessibility(TC, *rawTypeLocIter, ED,
[&](Accessibility typeAccess,
[&](const DeclContext *typeAccessScope,
const TypeRepr *complainRepr) {
auto typeAccess = accessibilityFromScopeForDiagnostics(typeAccessScope);
bool isExplicit = ED->getAttrs().hasAttribute<AccessibilityAttr>();
auto diag = TC.diagnose(ED, diag::enum_raw_type_access,
isExplicit, ED->getFormalAccess(),
@@ -1717,8 +1836,9 @@ static void checkAccessibility(TypeChecker &TC, const Decl *D) {
if (superclassLocIter == CD->getInherited().end())
return;
checkTypeAccessibility(TC, *superclassLocIter, CD,
[&](Accessibility typeAccess,
[&](const DeclContext *typeAccessScope,
const TypeRepr *complainRepr) {
auto typeAccess = accessibilityFromScopeForDiagnostics(typeAccessScope);
bool isExplicit = CD->getAttrs().hasAttribute<AccessibilityAttr>();
auto diag = TC.diagnose(CD, diag::class_super_access,
isExplicit, CD->getFormalAccess(),
@@ -1733,28 +1853,29 @@ static void checkAccessibility(TypeChecker &TC, const Decl *D) {
case DeclKind::Protocol: {
auto proto = cast<ProtocolDecl>(D);
Optional<Accessibility> minAccess;
const DeclContext *minAccessScope = nullptr;
const TypeRepr *complainRepr = nullptr;
std::for_each(proto->getInherited().begin(),
proto->getInherited().end(),
[&](TypeLoc requirement) {
checkTypeAccessibility(TC, requirement, proto,
[&](Accessibility typeAccess,
[&](const DeclContext *typeAccessScope,
const TypeRepr *thisComplainRepr) {
if (!minAccess || *minAccess > typeAccess) {
minAccess = typeAccess;
if (!minAccessScope ||
typeAccessScope->isChildContextOf(minAccessScope) ||
(!complainRepr && typeAccessScope == minAccessScope)) {
minAccessScope = typeAccessScope;
complainRepr = thisComplainRepr;
}
});
});
if (minAccess) {
if (minAccessScope) {
auto minAccess = accessibilityFromScopeForDiagnostics(minAccessScope);
bool isExplicit = proto->getAttrs().hasAttribute<AccessibilityAttr>();
auto diag = TC.diagnose(proto, diag::protocol_refine_access,
isExplicit,
proto->getFormalAccess(),
*minAccess);
isExplicit, proto->getFormalAccess(), minAccess);
highlightOffendingType(TC, diag, complainRepr);
}
return;
@@ -1763,38 +1884,43 @@ static void checkAccessibility(TypeChecker &TC, const Decl *D) {
case DeclKind::Subscript: {
auto SD = cast<SubscriptDecl>(D);
Optional<Accessibility> minAccess;
const DeclContext *minAccessScope = nullptr;
const TypeRepr *complainRepr = nullptr;
bool problemIsElement = false;
for (auto &P : *SD->getIndices()) {
checkTypeAccessibility(TC, P->getTypeLoc(), SD,
[&](Accessibility typeAccess,
[&](const DeclContext *typeAccessScope,
const TypeRepr *thisComplainRepr) {
if (!minAccess || *minAccess > typeAccess) {
minAccess = typeAccess;
if (!minAccessScope ||
typeAccessScope->isChildContextOf(minAccessScope) ||
(!complainRepr && typeAccessScope == minAccessScope)) {
minAccessScope = typeAccessScope;
complainRepr = thisComplainRepr;
}
});
}
checkTypeAccessibility(TC, SD->getElementTypeLoc(), SD,
[&](Accessibility typeAccess,
[&](const DeclContext *typeAccessScope,
const TypeRepr *thisComplainRepr) {
if (!minAccess || *minAccess > typeAccess) {
minAccess = typeAccess;
if (!minAccessScope ||
typeAccessScope->isChildContextOf(minAccessScope) ||
(!complainRepr && typeAccessScope == minAccessScope)) {
minAccessScope = typeAccessScope;
complainRepr = thisComplainRepr;
problemIsElement = true;
}
});
if (minAccess) {
if (minAccessScope) {
auto minAccess = accessibilityFromScopeForDiagnostics(minAccessScope);
bool isExplicit =
SD->getAttrs().hasAttribute<AccessibilityAttr>() ||
SD->getDeclContext()->getAsProtocolOrProtocolExtensionContext();
auto diag = TC.diagnose(SD, diag::subscript_type_access,
isExplicit,
SD->getFormalAccess(),
*minAccess,
minAccess,
problemIsElement);
highlightOffendingType(TC, diag, complainRepr);
}
@@ -1818,15 +1944,17 @@ static void checkAccessibility(TypeChecker &TC, const Decl *D) {
FK_Initializer
};
Optional<Accessibility> minAccess;
const DeclContext *minAccessScope = nullptr;
const TypeRepr *complainRepr = nullptr;
for (auto *PL : fn->getParameterLists().slice(isTypeContext)) {
for (auto &P : *PL) {
checkTypeAccessibility(TC, P->getTypeLoc(), fn,
[&](Accessibility typeAccess,
[&](const DeclContext *typeAccessScope,
const TypeRepr *thisComplainRepr) {
if (!minAccess || *minAccess > typeAccess) {
minAccess = typeAccess;
if (!minAccessScope ||
typeAccessScope->isChildContextOf(minAccessScope) ||
(!complainRepr && typeAccessScope == minAccessScope)) {
minAccessScope = typeAccessScope;
complainRepr = thisComplainRepr;
}
});
@@ -1836,24 +1964,27 @@ static void checkAccessibility(TypeChecker &TC, const Decl *D) {
bool problemIsResult = false;
if (auto FD = dyn_cast<FuncDecl>(fn)) {
checkTypeAccessibility(TC, FD->getBodyResultTypeLoc(), FD,
[&](Accessibility typeAccess,
[&](const DeclContext *typeAccessScope,
const TypeRepr *thisComplainRepr) {
if (!minAccess || *minAccess > typeAccess) {
minAccess = typeAccess;
if (!minAccessScope ||
typeAccessScope->isChildContextOf(minAccessScope) ||
(!complainRepr && typeAccessScope == minAccessScope)) {
minAccessScope = typeAccessScope;
complainRepr = thisComplainRepr;
problemIsResult = true;
}
});
}
if (minAccess) {
if (minAccessScope) {
auto minAccess = accessibilityFromScopeForDiagnostics(minAccessScope);
bool isExplicit =
fn->getAttrs().hasAttribute<AccessibilityAttr>() ||
D->getDeclContext()->getAsProtocolOrProtocolExtensionContext();
auto diag = TC.diagnose(fn, diag::function_type_access,
isExplicit,
fn->getFormalAccess(),
*minAccess,
minAccess,
isa<ConstructorDecl>(fn) ? FK_Initializer :
isTypeContext ? FK_Method : FK_Function,
problemIsResult);
@@ -1868,8 +1999,9 @@ static void checkAccessibility(TypeChecker &TC, const Decl *D) {
if (!EED->hasArgumentType())
return;
checkTypeAccessibility(TC, EED->getArgumentTypeLoc(), EED,
[&](Accessibility typeAccess,
[&](const DeclContext *typeAccessScope,
const TypeRepr *complainRepr) {
auto typeAccess = accessibilityFromScopeForDiagnostics(typeAccessScope);
auto diag = TC.diagnose(EED, diag::enum_case_access,
EED->getFormalAccess(), typeAccess);
highlightOffendingType(TC, diag, complainRepr);
@@ -5762,8 +5894,20 @@ public:
if (!IsFirstPass) {
TC.computeDefaultAccessibility(ED);
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,
AA->getAccess());
desiredAccessScope, AA->getAccess());
}
TC.checkConformancesInContext(ED, ED);
}

View File

@@ -1231,7 +1231,7 @@ checkWitnessAccessibility(Accessibility *requiredAccess,
*requiredAccess = std::min(Proto->getFormalAccess(), *requiredAccess);
if (*requiredAccess > Accessibility::Private) {
if (witness->getFormalAccess() < *requiredAccess)
if (witness->getFormalAccess(DC) < *requiredAccess)
return true;
if (requirement->isSettable(DC)) {
@@ -4609,7 +4609,13 @@ void TypeChecker::checkConformancesInContext(DeclContext *dc,
// For anything with a Clang node, lazily check conformances.
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 {
// For anything with a Clang node, lazily check conformances.
auto nominal = cast<NominalTypeDecl>(dc);

View File

@@ -460,12 +460,18 @@ public:
/// during type checking.
llvm::SetVector<NominalTypeDecl *> ValidatedTypes;
/// Caches whether a particular type is accessible from a particular file
/// unit.
using TypeAccessScopeCacheMap = llvm::DenseMap<Type, const DeclContext *>;
/// 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
/// 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.
llvm::DenseMap<std::pair<ValueDecl*, ValueDecl*>, bool>

View File

@@ -307,7 +307,9 @@ private extension Use4 {
// CHECK-DAG: !private "PrivateTopLevelTy2"
// CHECK-DAG: "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"
func outerPrivateTy3() {

View File

@@ -158,7 +158,7 @@ internal struct InternalStruct {} // expected-note * {{declared here}}
private struct PrivateStruct {} // 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=}}
internal extension InternalProto where Assoc == PublicStruct {}
@@ -169,7 +169,7 @@ private extension InternalProto where Assoc == InternalStruct {}
private extension InternalProto where Assoc == PrivateStruct {}
public protocol PublicProto {
associatedtype Assoc // expected-note * {{type declared here}}
associatedtype Assoc
}
public extension PublicProto {}
public extension PublicProto where Assoc == PublicStruct {}

View File

@@ -5,7 +5,7 @@
// See http://swift.org/LICENSE.txt for license information
// 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
private class B
class d<T where B<T>:w