mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
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:
@@ -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))
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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}}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
Reference in New Issue
Block a user