Merge pull request #6064 from slavapestov/killing-substituted-type-access-check

Refactor accessibility checking to use TypeReprs (most of the time) rather than Types
This commit is contained in:
Slava Pestov
2016-12-05 13:19:58 -08:00
committed by GitHub
8 changed files with 144 additions and 77 deletions

View File

@@ -68,8 +68,11 @@ public:
/// Returns the narrowest access scope if this and the specified access scope /// Returns the narrowest access scope if this and the specified access scope
/// have common intersection, or None if scopes don't intersect. /// have common intersection, or None if scopes don't intersect.
const Optional<AccessScope> intersectWith(AccessScope accessScope) const { const Optional<AccessScope> intersectWith(AccessScope accessScope) const {
if (hasEqualDeclContextWith(accessScope)) if (hasEqualDeclContextWith(accessScope)) {
return *this; if (isPrivate())
return *this;
return accessScope;
}
if (isChildOf(accessScope)) if (isChildOf(accessScope))
return *this; return *this;
if (accessScope.isChildOf(*this)) if (accessScope.isChildOf(*this))

View File

@@ -1134,67 +1134,96 @@ static void configureImplicitSelf(TypeChecker &tc,
namespace { namespace {
class TypeAccessScopeChecker : private TypeWalker { class AccessScopeChecker {
using TypeAccessScopeCacheMap = TypeChecker::TypeAccessScopeCacheMap;
TypeAccessScopeCacheMap &Cache;
const SourceFile *File; const SourceFile *File;
SmallVector<Optional<AccessScope>, 8> RawScopeStack; TypeChecker::TypeAccessScopeCacheMap &Cache;
explicit TypeAccessScopeChecker(TypeAccessScopeCacheMap &cache, protected:
const SourceFile *file) Optional<AccessScope> Scope;
: Cache(cache), File(file) {
// Always have something on the stack.
RawScopeStack.push_back(None);
}
bool shouldVisitOriginalSubstitutedType() override { return true; } AccessScopeChecker(const DeclContext *useDC,
decltype(TypeChecker::TypeAccessScopeCache) &caches)
: File(useDC->getParentSourceFile()), Cache(caches[File]),
Scope(AccessScope::getPublic()) {}
Action walkToTypePre(Type ty) override { bool visitDecl(ValueDecl *VD) {
// Assume failure until we post-visit this node. if (!VD || isa<GenericTypeParamDecl>(VD))
// This will be correct as long as we don't ever have self-referential return true;
// Types.
auto cached = Cache.find(ty); // FIXME: Figure out why AssociatedTypeDecls don't always have
// accessibility here.
if (!VD->hasAccessibility()) {
if (isa<AssociatedTypeDecl>(VD))
return true;
}
auto cached = Cache.find(VD);
if (cached != Cache.end()) { if (cached != Cache.end()) {
Optional<AccessScope> &last = RawScopeStack.back(); Scope = Scope->intersectWith(cached->second);
if (last.hasValue()) return Scope.hasValue();
last = last.getValue().intersectWith(cached->second);
return Action::SkipChildren;
} }
auto AS = AccessScope::getPublic(); auto AS = VD->getFormalAccessScope(File);
if (auto alias = dyn_cast<NameAliasType>(ty.getPointer())) auto result = Cache.insert(std::make_pair(VD, AS));
AS = alias->getDecl()->getFormalAccessScope(File); assert(result.second);
else if (auto nominal = ty->getAnyNominal()) (void) result;
AS = nominal->getFormalAccessScope(File);
RawScopeStack.push_back(AS);
return Action::Continue; Scope = Scope->intersectWith(AS);
return Scope.hasValue();
}
};
class TypeReprAccessScopeChecker : private ASTWalker, AccessScopeChecker {
TypeReprAccessScopeChecker(const DeclContext *useDC,
decltype(TypeChecker::TypeAccessScopeCache) &caches)
: AccessScopeChecker(useDC, caches) {}
bool walkToTypeReprPre(TypeRepr *TR) override {
auto CITR = dyn_cast<ComponentIdentTypeRepr>(TR);
if (!CITR)
return true;
return visitDecl(CITR->getBoundDecl());
} }
Action walkToTypePost(Type ty) override { bool walkToTypeReprPost(TypeRepr *TR) override {
Optional<AccessScope> last = RawScopeStack.pop_back_val(); return Scope.hasValue();
if (last.hasValue()) {
Cache.insert(std::make_pair(ty, *last));
Optional<AccessScope> &prev = RawScopeStack.back();
if (prev.hasValue())
prev = prev.getValue().intersectWith(*last);
}
return Action::Continue;
} }
public: public:
static Optional<AccessScope> static Optional<AccessScope>
getAccessScope(Type ty, const DeclContext *useDC, getAccessScope(TypeRepr *TR, const DeclContext *useDC,
decltype(TypeChecker::TypeAccessScopeCache) &caches) { decltype(TypeChecker::TypeAccessScopeCache) &caches) {
const SourceFile *file = useDC->getParentSourceFile(); TypeReprAccessScopeChecker checker(useDC, caches);
auto &cache = caches[file]; TR->walk(checker);
ty.walk(TypeAccessScopeChecker(cache, file)); return checker.Scope;
auto iter = cache.find(ty); }
if (iter == cache.end()) };
return None;
return iter->second; class TypeAccessScopeChecker : private TypeWalker, AccessScopeChecker {
TypeAccessScopeChecker(const DeclContext *useDC,
decltype(TypeChecker::TypeAccessScopeCache) &caches)
: AccessScopeChecker(useDC, caches) {}
Action walkToTypePre(Type T) {
ValueDecl *VD;
if (auto *TAD = dyn_cast<NameAliasType>(T.getPointer()))
VD = TAD->getDecl();
else if (auto *NTD = T->getAnyNominal())
VD = NTD;
else
VD = nullptr;
return visitDecl(VD) ? Action::Continue : Action::Stop;
}
public:
static Optional<AccessScope>
getAccessScope(Type T, const DeclContext *useDC,
decltype(TypeChecker::TypeAccessScopeCache) &caches) {
TypeAccessScopeChecker checker(useDC, caches);
T.walk(checker);
return checker.Scope;
} }
}; };
@@ -1223,9 +1252,9 @@ void TypeChecker::computeDefaultAccessibility(ExtensionDecl *ED) {
if (!TL.getType()) if (!TL.getType())
return Accessibility::Public; return Accessibility::Public;
auto accessScope = auto accessScope =
TypeAccessScopeChecker::getAccessScope(TL.getType(), TypeReprAccessScopeChecker::getAccessScope(TL.getTypeRepr(),
ED->getDeclContext(), ED->getDeclContext(),
TypeAccessScopeCache); TypeAccessScopeCache);
// This is an error case and will be diagnosed elsewhere. // This is an error case and will be diagnosed elsewhere.
if (!accessScope.hasValue()) if (!accessScope.hasValue())
return Accessibility::Public; return Accessibility::Public;
@@ -1406,6 +1435,8 @@ public:
const DeclContext *useDC) { const DeclContext *useDC) {
assert(!accessScope.isPublic() && assert(!accessScope.isPublic() &&
"why would we need to find a public access scope?"); "why would we need to find a public access scope?");
if (TR == nullptr)
return nullptr;
TypeAccessScopeDiagnoser diagnoser(accessScope, useDC); TypeAccessScopeDiagnoser diagnoser(accessScope, useDC);
TR->walk(diagnoser); TR->walk(diagnoser);
return diagnoser.offendingType; return diagnoser.offendingType;
@@ -1446,9 +1477,15 @@ static void checkTypeAccessibilityImpl(
contextAccessScope.getDeclContext()->isLocalContext()) contextAccessScope.getDeclContext()->isLocalContext())
return; return;
// TypeRepr checking is more accurate, but we must also look at TypeLocs
// without a TypeRepr, for example for 'var' declarations with an inferred
// type.
auto typeAccessScope = auto typeAccessScope =
TypeAccessScopeChecker::getAccessScope(TL.getType(), useDC, (TL.getTypeRepr()
TC.TypeAccessScopeCache); ? TypeReprAccessScopeChecker::getAccessScope(TL.getTypeRepr(), useDC,
TC.TypeAccessScopeCache)
: TypeAccessScopeChecker::getAccessScope(TL.getType(), useDC,
TC.TypeAccessScopeCache));
// Note: This means that the type itself is invalid for this particular // Note: This means that the type itself is invalid for this particular
// context, because it references declarations from two incompatible scopes. // context, because it references declarations from two incompatible scopes.
@@ -1461,12 +1498,11 @@ static void checkTypeAccessibilityImpl(
contextAccessScope.isChildOf(*typeAccessScope)) contextAccessScope.isChildOf(*typeAccessScope))
return; return;
const TypeRepr *complainRepr = nullptr; const TypeRepr *complainRepr =
if (TypeRepr *TR = TL.getTypeRepr()) { TypeAccessScopeDiagnoser::findTypeWithScope(
complainRepr = TL.getTypeRepr(),
TypeAccessScopeDiagnoser::findTypeWithScope(TR, *typeAccessScope, *typeAccessScope,
useDC); useDC);
}
diagnose(*typeAccessScope, complainRepr); diagnose(*typeAccessScope, complainRepr);
} }

View File

@@ -2268,9 +2268,7 @@ void ConformanceChecker::recordTypeWitness(AssociatedTypeDecl *assocType,
aliasDecl->getAliasType()->setRecursiveProperties( aliasDecl->getAliasType()->setRecursiveProperties(
type->getRecursiveProperties()); type->getRecursiveProperties());
Type interfaceTy = aliasDecl->getAliasType(); auto interfaceTy = DC->mapTypeOutOfContext(aliasDecl->getAliasType());
if (interfaceTy->hasArchetype())
interfaceTy = ArchetypeBuilder::mapTypeOutOfContext(DC, type);
aliasDecl->setInterfaceType(MetatypeType::get(interfaceTy)); aliasDecl->setInterfaceType(MetatypeType::get(interfaceTy));
aliasDecl->setImplicit(); aliasDecl->setImplicit();

View File

@@ -570,13 +570,7 @@ Type TypeChecker::applyUnboundGenericArguments(
type = ArchetypeBuilder::mapTypeOutOfContext(TAD, TAD->getUnderlyingType()); type = ArchetypeBuilder::mapTypeOutOfContext(TAD, TAD->getUnderlyingType());
} }
type = type.subst(dc->getParentModule(), subs, SubstFlags::UseErrorType); return type.subst(dc->getParentModule(), subs, SubstFlags::UseErrorType);
// FIXME: return a SubstitutedType to preserve the fact that
// we resolved a generic TypeAlias, for availability diagnostics.
// A better fix might be to introduce a BoundGenericAliasType
// which desugars as appropriate.
return SubstitutedType::get(TAD->getAliasType(), type, Context);
} }
// Form the bound generic type. // Form the bound generic type.

View File

@@ -491,16 +491,13 @@ public:
/// during type checking. /// during type checking.
llvm::SetVector<NominalTypeDecl *> ValidatedTypes; llvm::SetVector<NominalTypeDecl *> ValidatedTypes;
using TypeAccessScopeCacheMap = llvm::DenseMap<Type, AccessScope>; using TypeAccessScopeCacheMap = llvm::DenseMap<const ValueDecl *, AccessScope>;
/// Caches the outermost scope where a particular type can be used, relative /// Caches the outermost scope where a particular declaration can be used,
/// to a particular file. /// relative to a particular file.
/// ///
/// The file is used to handle things like \c \@testable. A null-but-present /// The file is used to handle things like \c \@testable. A null-but-present
/// value means the type is public. /// value means the type is public.
///
/// This can't use CanTypes because typealiases may have more limited types
/// than their underlying types.
llvm::DenseMap<const SourceFile *, TypeAccessScopeCacheMap> llvm::DenseMap<const SourceFile *, TypeAccessScopeCacheMap>
TypeAccessScopeCache; TypeAccessScopeCache;

View File

@@ -1771,7 +1771,7 @@ extension ${Self} {
public mutating func replaceSubrange<C>( public mutating func replaceSubrange<C>(
_ subrange: Range<Int>, _ subrange: Range<Int>,
with newElements: C with newElements: C
) where C : Collection, C.Iterator.Element == _Buffer.Element { ) where C : Collection, C.Iterator.Element == Element {
% if Self in ['Array', 'ContiguousArray']: % if Self in ['Array', 'ContiguousArray']:
_precondition(subrange.lowerBound >= self._buffer.startIndex, _precondition(subrange.lowerBound >= self._buffer.startIndex,
"${Self} replace: subrange start is negative") "${Self} replace: subrange start is negative")
@@ -2291,7 +2291,7 @@ extension ${Self} {
public mutating func replaceRange<C>( public mutating func replaceRange<C>(
_ subRange: Range<Int>, _ subRange: Range<Int>,
with newElements: C with newElements: C
) where C : Collection, C.Iterator.Element == _Buffer.Element { ) where C : Collection, C.Iterator.Element == Element {
Builtin.unreachable() Builtin.unreachable()
} }

View File

@@ -637,3 +637,21 @@ internal struct AssocTypeOuterProblem2 {
fileprivate typealias Assoc = Int // expected-error {{type alias 'Assoc' must be as accessible as its enclosing type because it matches a requirement in protocol 'AssocTypeProto'}} {{5-16=internal}} fileprivate typealias Assoc = Int // expected-error {{type alias 'Assoc' must be as accessible as its enclosing type because it matches a requirement in protocol 'AssocTypeProto'}} {{5-16=internal}}
} }
} }
// This code was accepted in Swift 3
public protocol P {
associatedtype Element
func f() -> Element
}
struct S<T> : P {
func f() -> T { while true {} }
}
public struct G<T> {
typealias A = S<T> // expected-note {{type declared here}}
public func foo<U : P>(u: U) where U.Element == A.Element {}
// expected-error@-1 {{instance method cannot be declared public because its generic requirement uses an internal type}}
}

View File

@@ -210,3 +210,24 @@ fileprivate struct SR2579 {
private var outerProperty = Inner().innerProperty // expected-warning {{property should not be declared in this context because its type 'SR2579.Inner.InnerPrivateType' uses a private type}} private var outerProperty = Inner().innerProperty // expected-warning {{property should not be declared in this context because its type 'SR2579.Inner.InnerPrivateType' uses a private type}}
var outerProperty2 = Inner().innerProperty // expected-warning {{property should be declared private because its type 'SR2579.Inner.InnerPrivateType' uses a private type}} var outerProperty2 = Inner().innerProperty // expected-warning {{property should be declared private because its type 'SR2579.Inner.InnerPrivateType' uses a private type}}
} }
// FIXME: Dependent member lookup of typealiases is not subject
// to accessibility checking.
struct Generic<T> {
fileprivate typealias Dependent = T
}
var x: Generic<Int>.Dependent = 3
// expected-error@-1 {{variable must be declared private or fileprivate because its type uses a fileprivate type}}
func internalFuncWithFileprivateAlias() -> Generic<Int>.Dependent {
// expected-error@-1 {{function must be declared private or fileprivate because its result uses a fileprivate type}}
return 3
}
private func privateFuncWithFileprivateAlias() -> Generic<Int>.Dependent {
return 3
}
// FIXME: No error here
var y = privateFuncWithFileprivateAlias()