Skip to content

Commit 4c914f5

Browse files
committed
Sema: Use TypeReprs to check accessibility
This eliminates a usage of SubstitutedType. Unfortunately, we still need a simpler version of the old check for the case where a 'var' has no explicit type written, and instead the type is inferred from the initializer expression. The check for inferred types of vars no longer looks at SubstitutedType, so it misses the accessibility of dependent typealias members. A new test case demonstrates the problem. Note that dependent typealiases were substituted away as far as the user is concerned anyway, not showing up in generated interfaces or diagnostics. Also the new logic is more accurate in the case where a TypeRepr is present, which is most of the time.
1 parent d1b753c commit 4c914f5

File tree

3 files changed

+115
-61
lines changed

3 files changed

+115
-61
lines changed

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/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

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)