Skip to content

Commit a32e11a

Browse files
authored
Merge pull request #6064 from slavapestov/killing-substituted-type-access-check
Refactor accessibility checking to use TypeReprs (most of the time) rather than Types
2 parents 159a1d9 + fcef528 commit a32e11a

File tree

8 files changed

+142
-75
lines changed

8 files changed

+142
-75
lines changed

include/swift/AST/AccessScope.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,11 @@ class AccessScope {
6868
/// Returns the narrowest access scope if this and the specified access scope
6969
/// have common intersection, or None if scopes don't intersect.
7070
const Optional<AccessScope> intersectWith(AccessScope accessScope) const {
71-
if (hasEqualDeclContextWith(accessScope))
72-
return *this;
71+
if (hasEqualDeclContextWith(accessScope)) {
72+
if (isPrivate())
73+
return *this;
74+
return accessScope;
75+
}
7376
if (isChildOf(accessScope))
7477
return *this;
7578
if (accessScope.isChildOf(*this))

lib/Sema/TypeCheckDecl.cpp

Lines changed: 91 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1134,67 +1134,96 @@ static void configureImplicitSelf(TypeChecker &tc,
11341134

11351135
namespace {
11361136

1137-
class TypeAccessScopeChecker : private TypeWalker {
1138-
using TypeAccessScopeCacheMap = TypeChecker::TypeAccessScopeCacheMap;
1139-
TypeAccessScopeCacheMap &Cache;
1137+
class AccessScopeChecker {
11401138
const SourceFile *File;
1141-
SmallVector<Optional<AccessScope>, 8> RawScopeStack;
1139+
TypeChecker::TypeAccessScopeCacheMap &Cache;
11421140

1143-
explicit TypeAccessScopeChecker(TypeAccessScopeCacheMap &cache,
1144-
const SourceFile *file)
1145-
: Cache(cache), File(file) {
1146-
// Always have something on the stack.
1147-
RawScopeStack.push_back(None);
1148-
}
1141+
protected:
1142+
Optional<AccessScope> Scope;
1143+
1144+
AccessScopeChecker(const DeclContext *useDC,
1145+
decltype(TypeChecker::TypeAccessScopeCache) &caches)
1146+
: File(useDC->getParentSourceFile()), Cache(caches[File]),
1147+
Scope(AccessScope::getPublic()) {}
11491148

1150-
bool shouldVisitOriginalSubstitutedType() override { return true; }
1149+
bool visitDecl(ValueDecl *VD) {
1150+
if (!VD || isa<GenericTypeParamDecl>(VD))
1151+
return true;
11511152

1152-
Action walkToTypePre(Type ty) override {
1153-
// Assume failure until we post-visit this node.
1154-
// This will be correct as long as we don't ever have self-referential
1155-
// Types.
1156-
auto cached = Cache.find(ty);
1153+
// FIXME: Figure out why AssociatedTypeDecls don't always have
1154+
// accessibility here.
1155+
if (!VD->hasAccessibility()) {
1156+
if (isa<AssociatedTypeDecl>(VD))
1157+
return true;
1158+
}
1159+
1160+
auto cached = Cache.find(VD);
11571161
if (cached != Cache.end()) {
1158-
Optional<AccessScope> &last = RawScopeStack.back();
1159-
if (last.hasValue())
1160-
last = last.getValue().intersectWith(cached->second);
1161-
return Action::SkipChildren;
1162+
Scope = Scope->intersectWith(cached->second);
1163+
return Scope.hasValue();
11621164
}
11631165

1164-
auto AS = AccessScope::getPublic();
1165-
if (auto alias = dyn_cast<NameAliasType>(ty.getPointer()))
1166-
AS = alias->getDecl()->getFormalAccessScope(File);
1167-
else if (auto nominal = ty->getAnyNominal())
1168-
AS = nominal->getFormalAccessScope(File);
1169-
RawScopeStack.push_back(AS);
1166+
auto AS = VD->getFormalAccessScope(File);
1167+
auto result = Cache.insert(std::make_pair(VD, AS));
1168+
assert(result.second);
1169+
(void) result;
11701170

1171-
return Action::Continue;
1171+
Scope = Scope->intersectWith(AS);
1172+
return Scope.hasValue();
11721173
}
1174+
};
11731175

1174-
Action walkToTypePost(Type ty) override {
1175-
Optional<AccessScope> last = RawScopeStack.pop_back_val();
1176-
if (last.hasValue()) {
1177-
Cache.insert(std::make_pair(ty, *last));
1176+
class TypeReprAccessScopeChecker : private ASTWalker, AccessScopeChecker {
1177+
TypeReprAccessScopeChecker(const DeclContext *useDC,
1178+
decltype(TypeChecker::TypeAccessScopeCache) &caches)
1179+
: AccessScopeChecker(useDC, caches) {}
11781180

1179-
Optional<AccessScope> &prev = RawScopeStack.back();
1180-
if (prev.hasValue())
1181-
prev = prev.getValue().intersectWith(*last);
1182-
}
1181+
bool walkToTypeReprPre(TypeRepr *TR) override {
1182+
auto CITR = dyn_cast<ComponentIdentTypeRepr>(TR);
1183+
if (!CITR)
1184+
return true;
1185+
1186+
return visitDecl(CITR->getBoundDecl());
1187+
}
11831188

1184-
return Action::Continue;
1189+
bool walkToTypeReprPost(TypeRepr *TR) override {
1190+
return Scope.hasValue();
11851191
}
11861192

11871193
public:
11881194
static Optional<AccessScope>
1189-
getAccessScope(Type ty, const DeclContext *useDC,
1195+
getAccessScope(TypeRepr *TR, const DeclContext *useDC,
11901196
decltype(TypeChecker::TypeAccessScopeCache) &caches) {
1191-
const SourceFile *file = useDC->getParentSourceFile();
1192-
auto &cache = caches[file];
1193-
ty.walk(TypeAccessScopeChecker(cache, file));
1194-
auto iter = cache.find(ty);
1195-
if (iter == cache.end())
1196-
return None;
1197-
return iter->second;
1197+
TypeReprAccessScopeChecker checker(useDC, caches);
1198+
TR->walk(checker);
1199+
return checker.Scope;
1200+
}
1201+
};
1202+
1203+
class TypeAccessScopeChecker : private TypeWalker, AccessScopeChecker {
1204+
TypeAccessScopeChecker(const DeclContext *useDC,
1205+
decltype(TypeChecker::TypeAccessScopeCache) &caches)
1206+
: AccessScopeChecker(useDC, caches) {}
1207+
1208+
Action walkToTypePre(Type T) {
1209+
ValueDecl *VD;
1210+
if (auto *TAD = dyn_cast<NameAliasType>(T.getPointer()))
1211+
VD = TAD->getDecl();
1212+
else if (auto *NTD = T->getAnyNominal())
1213+
VD = NTD;
1214+
else
1215+
VD = nullptr;
1216+
1217+
return visitDecl(VD) ? Action::Continue : Action::Stop;
1218+
}
1219+
1220+
public:
1221+
static Optional<AccessScope>
1222+
getAccessScope(Type T, const DeclContext *useDC,
1223+
decltype(TypeChecker::TypeAccessScopeCache) &caches) {
1224+
TypeAccessScopeChecker checker(useDC, caches);
1225+
T.walk(checker);
1226+
return checker.Scope;
11981227
}
11991228
};
12001229

@@ -1223,9 +1252,9 @@ void TypeChecker::computeDefaultAccessibility(ExtensionDecl *ED) {
12231252
if (!TL.getType())
12241253
return Accessibility::Public;
12251254
auto accessScope =
1226-
TypeAccessScopeChecker::getAccessScope(TL.getType(),
1227-
ED->getDeclContext(),
1228-
TypeAccessScopeCache);
1255+
TypeReprAccessScopeChecker::getAccessScope(TL.getTypeRepr(),
1256+
ED->getDeclContext(),
1257+
TypeAccessScopeCache);
12291258
// This is an error case and will be diagnosed elsewhere.
12301259
if (!accessScope.hasValue())
12311260
return Accessibility::Public;
@@ -1406,6 +1435,8 @@ class TypeAccessScopeDiagnoser : private ASTWalker {
14061435
const DeclContext *useDC) {
14071436
assert(!accessScope.isPublic() &&
14081437
"why would we need to find a public access scope?");
1438+
if (TR == nullptr)
1439+
return nullptr;
14091440
TypeAccessScopeDiagnoser diagnoser(accessScope, useDC);
14101441
TR->walk(diagnoser);
14111442
return diagnoser.offendingType;
@@ -1446,9 +1477,15 @@ static void checkTypeAccessibilityImpl(
14461477
contextAccessScope.getDeclContext()->isLocalContext())
14471478
return;
14481479

1480+
// TypeRepr checking is more accurate, but we must also look at TypeLocs
1481+
// without a TypeRepr, for example for 'var' declarations with an inferred
1482+
// type.
14491483
auto typeAccessScope =
1450-
TypeAccessScopeChecker::getAccessScope(TL.getType(), useDC,
1451-
TC.TypeAccessScopeCache);
1484+
(TL.getTypeRepr()
1485+
? TypeReprAccessScopeChecker::getAccessScope(TL.getTypeRepr(), useDC,
1486+
TC.TypeAccessScopeCache)
1487+
: TypeAccessScopeChecker::getAccessScope(TL.getType(), useDC,
1488+
TC.TypeAccessScopeCache));
14521489

14531490
// Note: This means that the type itself is invalid for this particular
14541491
// context, because it references declarations from two incompatible scopes.
@@ -1461,12 +1498,11 @@ static void checkTypeAccessibilityImpl(
14611498
contextAccessScope.isChildOf(*typeAccessScope))
14621499
return;
14631500

1464-
const TypeRepr *complainRepr = nullptr;
1465-
if (TypeRepr *TR = TL.getTypeRepr()) {
1466-
complainRepr =
1467-
TypeAccessScopeDiagnoser::findTypeWithScope(TR, *typeAccessScope,
1468-
useDC);
1469-
}
1501+
const TypeRepr *complainRepr =
1502+
TypeAccessScopeDiagnoser::findTypeWithScope(
1503+
TL.getTypeRepr(),
1504+
*typeAccessScope,
1505+
useDC);
14701506
diagnose(*typeAccessScope, complainRepr);
14711507
}
14721508

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2268,9 +2268,7 @@ void ConformanceChecker::recordTypeWitness(AssociatedTypeDecl *assocType,
22682268
aliasDecl->getAliasType()->setRecursiveProperties(
22692269
type->getRecursiveProperties());
22702270

2271-
Type interfaceTy = aliasDecl->getAliasType();
2272-
if (interfaceTy->hasArchetype())
2273-
interfaceTy = ArchetypeBuilder::mapTypeOutOfContext(DC, type);
2271+
auto interfaceTy = DC->mapTypeOutOfContext(aliasDecl->getAliasType());
22742272
aliasDecl->setInterfaceType(MetatypeType::get(interfaceTy));
22752273

22762274
aliasDecl->setImplicit();

lib/Sema/TypeCheckType.cpp

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -570,13 +570,7 @@ Type TypeChecker::applyUnboundGenericArguments(
570570
type = ArchetypeBuilder::mapTypeOutOfContext(TAD, TAD->getUnderlyingType());
571571
}
572572

573-
type = type.subst(dc->getParentModule(), subs, SubstFlags::UseErrorType);
574-
575-
// FIXME: return a SubstitutedType to preserve the fact that
576-
// we resolved a generic TypeAlias, for availability diagnostics.
577-
// A better fix might be to introduce a BoundGenericAliasType
578-
// which desugars as appropriate.
579-
return SubstitutedType::get(TAD->getAliasType(), type, Context);
573+
return type.subst(dc->getParentModule(), subs, SubstFlags::UseErrorType);
580574
}
581575

582576
// Form the bound generic type.

lib/Sema/TypeChecker.h

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -491,16 +491,13 @@ class TypeChecker final : public LazyResolver {
491491
/// during type checking.
492492
llvm::SetVector<NominalTypeDecl *> ValidatedTypes;
493493

494-
using TypeAccessScopeCacheMap = llvm::DenseMap<Type, AccessScope>;
494+
using TypeAccessScopeCacheMap = llvm::DenseMap<const ValueDecl *, AccessScope>;
495495

496-
/// Caches the outermost scope where a particular type can be used, relative
497-
/// to a particular file.
496+
/// Caches the outermost scope where a particular declaration can be used,
497+
/// relative to a particular file.
498498
///
499499
/// The file is used to handle things like \c \@testable. A null-but-present
500500
/// value means the type is public.
501-
///
502-
/// This can't use CanTypes because typealiases may have more limited types
503-
/// than their underlying types.
504501
llvm::DenseMap<const SourceFile *, TypeAccessScopeCacheMap>
505502
TypeAccessScopeCache;
506503

stdlib/public/core/Arrays.swift.gyb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1771,7 +1771,7 @@ extension ${Self} {
17711771
public mutating func replaceSubrange<C>(
17721772
_ subrange: Range<Int>,
17731773
with newElements: C
1774-
) where C : Collection, C.Iterator.Element == _Buffer.Element {
1774+
) where C : Collection, C.Iterator.Element == Element {
17751775
% if Self in ['Array', 'ContiguousArray']:
17761776
_precondition(subrange.lowerBound >= self._buffer.startIndex,
17771777
"${Self} replace: subrange start is negative")
@@ -2291,7 +2291,7 @@ extension ${Self} {
22912291
public mutating func replaceRange<C>(
22922292
_ subRange: Range<Int>,
22932293
with newElements: C
2294-
) where C : Collection, C.Iterator.Element == _Buffer.Element {
2294+
) where C : Collection, C.Iterator.Element == Element {
22952295
Builtin.unreachable()
22962296
}
22972297

test/Sema/accessibility.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,3 +637,21 @@ internal struct AssocTypeOuterProblem2 {
637637
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}}
638638
}
639639
}
640+
641+
// This code was accepted in Swift 3
642+
public protocol P {
643+
associatedtype Element
644+
645+
func f() -> Element
646+
}
647+
648+
struct S<T> : P {
649+
func f() -> T { while true {} }
650+
}
651+
652+
public struct G<T> {
653+
typealias A = S<T> // expected-note {{type declared here}}
654+
655+
public func foo<U : P>(u: U) where U.Element == A.Element {}
656+
// expected-error@-1 {{instance method cannot be declared public because its generic requirement uses an internal type}}
657+
}

test/Sema/accessibility_private.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,3 +210,24 @@ fileprivate struct SR2579 {
210210
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}}
211211
var outerProperty2 = Inner().innerProperty // expected-warning {{property should be declared private because its type 'SR2579.Inner.InnerPrivateType' uses a private type}}
212212
}
213+
214+
// FIXME: Dependent member lookup of typealiases is not subject
215+
// to accessibility checking.
216+
struct Generic<T> {
217+
fileprivate typealias Dependent = T
218+
}
219+
220+
var x: Generic<Int>.Dependent = 3
221+
// expected-error@-1 {{variable must be declared private or fileprivate because its type uses a fileprivate type}}
222+
223+
func internalFuncWithFileprivateAlias() -> Generic<Int>.Dependent {
224+
// expected-error@-1 {{function must be declared private or fileprivate because its result uses a fileprivate type}}
225+
return 3
226+
}
227+
228+
private func privateFuncWithFileprivateAlias() -> Generic<Int>.Dependent {
229+
return 3
230+
}
231+
232+
// FIXME: No error here
233+
var y = privateFuncWithFileprivateAlias()

0 commit comments

Comments
 (0)