Skip to content

Commit 40908e0

Browse files
committed
[Sema] Extend TypeAccessScopeChecker to return any limiting import
Using an access-level on imports limit how imported types can be used in API. This change extends TypeAccessScopeChecker to return both the access scope of the target type and any import that limits where it can be used. Diagnostics should use this information to raise errors and point to related imports.
1 parent 0d82ba3 commit 40908e0

File tree

5 files changed

+63
-8
lines changed

5 files changed

+63
-8
lines changed

Diff for: include/swift/AST/SourceFile.h

+7
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ enum class RestrictedImportKind {
4545
None // No restriction, i.e. the module is imported publicly.
4646
};
4747

48+
/// Import that limits the access level of imported entities.
49+
using ImportAccessLevel = Optional<AttributedImport<ImportedModule>>;
50+
4851
/// A file containing Swift source code.
4952
///
5053
/// This is a .swift or .sil file (or a virtual file, such as the contents of
@@ -382,6 +385,10 @@ class SourceFile final : public FileUnit {
382385
/// Get the most permissive restriction applied to the imports of \p module.
383386
RestrictedImportKind getRestrictedImportKind(const ModuleDecl *module) const;
384387

388+
/// Return the import of \p targetModule from this file with the most
389+
/// permissive access level.
390+
ImportAccessLevel getImportAccessLevel(const ModuleDecl *targetModule) const;
391+
385392
/// Find all SPI names imported from \p importedModule by this file,
386393
/// collecting the identifiers in \p spiGroups.
387394
virtual void

Diff for: lib/AST/Module.cpp

+23
Original file line numberDiff line numberDiff line change
@@ -2944,6 +2944,29 @@ RestrictedImportKind SourceFile::getRestrictedImportKind(const ModuleDecl *modul
29442944
return importKind;
29452945
}
29462946

2947+
ImportAccessLevel
2948+
SourceFile::getImportAccessLevel(const ModuleDecl *targetModule) const {
2949+
assert(Imports.hasValue());
2950+
2951+
// Leave it to the caller to avoid calling this service for a self import.
2952+
// We want to return AccessLevel::Public, but there's no import site to return.
2953+
assert(targetModule != getParentModule() &&
2954+
"getImportAccessLevel doesn't support checking for a self-import");
2955+
2956+
auto &imports = getASTContext().getImportCache();
2957+
ImportAccessLevel restrictiveImport = None;
2958+
2959+
for (auto &import : *Imports) {
2960+
if ((!restrictiveImport.has_value() ||
2961+
import.accessLevel > restrictiveImport->accessLevel) &&
2962+
imports.isImportedBy(targetModule, import.module.importedModule)) {
2963+
restrictiveImport = import;
2964+
}
2965+
}
2966+
2967+
return restrictiveImport;
2968+
}
2969+
29472970
bool ModuleDecl::isImportedImplementationOnly(const ModuleDecl *module) const {
29482971
if (module == this) return false;
29492972

Diff for: lib/Sema/TypeAccessScopeChecker.h

+27-4
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class TypeAccessScopeChecker {
3131
bool TreatUsableFromInlineAsPublic;
3232

3333
Optional<AccessScope> Scope = AccessScope::getPublic();
34+
ImportAccessLevel ImportRestriction = None;
3435

3536
TypeAccessScopeChecker(const DeclContext *useDC,
3637
bool treatUsableFromInlineAsPublic)
@@ -43,21 +44,41 @@ class TypeAccessScopeChecker {
4344

4445
auto AS = VD->getFormalAccessScope(File, TreatUsableFromInlineAsPublic);
4546
Scope = Scope->intersectWith(AS);
47+
48+
auto targetModule = VD->getDeclContext()->getParentModule();
49+
if (targetModule != File->getParentModule()) {
50+
auto localImportRestriction = File->getImportAccessLevel(targetModule);
51+
52+
if (localImportRestriction.has_value() &&
53+
(!ImportRestriction.has_value() ||
54+
localImportRestriction.value().accessLevel <
55+
ImportRestriction.value().accessLevel)) {
56+
ImportRestriction = localImportRestriction;
57+
}
58+
}
59+
4660
return Scope.has_value();
4761
}
4862

4963
public:
50-
static Optional<AccessScope>
64+
65+
struct Result {
66+
Optional<AccessScope> Scope;
67+
ImportAccessLevel Import;
68+
};
69+
70+
static Result
5171
getAccessScope(TypeRepr *TR, const DeclContext *useDC,
5272
bool treatUsableFromInlineAsPublic = false) {
5373
TypeAccessScopeChecker checker(useDC, treatUsableFromInlineAsPublic);
5474
TR->walk(TypeReprIdentFinder([&](const IdentTypeRepr *typeRepr) {
5575
return checker.visitDecl(typeRepr->getBoundDecl());
5676
}));
57-
return checker.Scope;
77+
return {checker.Scope,
78+
checker.ImportRestriction};
5879
}
5980

60-
static Optional<AccessScope>
81+
static Result
6182
getAccessScope(Type T, const DeclContext *useDC,
6283
bool treatUsableFromInlineAsPublic = false) {
6384
TypeAccessScopeChecker checker(useDC, treatUsableFromInlineAsPublic);
@@ -66,7 +87,9 @@ class TypeAccessScopeChecker {
6687
return TypeWalker::Action::Continue;
6788
return TypeWalker::Action::Stop;
6889
}));
69-
return checker.Scope;
90+
91+
return {checker.Scope,
92+
checker.ImportRestriction};
7093
}
7194
};
7295

Diff for: lib/Sema/TypeCheckAccess.cpp

+5-3
Original file line numberDiff line numberDiff line change
@@ -188,13 +188,14 @@ void AccessControlCheckerBase::checkTypeAccessImpl(
188188

189189
AccessScope problematicAccessScope = AccessScope::getPublic();
190190
if (type) {
191-
Optional<AccessScope> typeAccessScope =
191+
auto scopeAndImport =
192192
TypeAccessScopeChecker::getAccessScope(type, useDC,
193193
checkUsableFromInline);
194194

195195
// Note: This means that the type itself is invalid for this particular
196196
// context, because it references declarations from two incompatible scopes.
197197
// In this case we should have diagnosed the bad reference already.
198+
Optional<AccessScope> typeAccessScope = scopeAndImport.Scope;
198199
if (!typeAccessScope.has_value())
199200
return;
200201
problematicAccessScope = *typeAccessScope;
@@ -210,9 +211,10 @@ void AccessControlCheckerBase::checkTypeAccessImpl(
210211
if (!typeRepr)
211212
return;
212213

213-
Optional<AccessScope> typeReprAccessScope =
214+
auto typeReprAccessScopeAndImport =
214215
TypeAccessScopeChecker::getAccessScope(typeRepr, useDC,
215216
checkUsableFromInline);
217+
auto typeReprAccessScope = typeReprAccessScopeAndImport.Scope;
216218
if (!typeReprAccessScope.has_value())
217219
return;
218220

@@ -240,7 +242,7 @@ void AccessControlCheckerBase::checkTypeAccessImpl(
240242
// Downgrade the error to a warning in this case for source compatibility.
241243
Optional<AccessScope> typeReprAccessScope =
242244
TypeAccessScopeChecker::getAccessScope(typeRepr, useDC,
243-
checkUsableFromInline);
245+
checkUsableFromInline).Scope;
244246
assert(typeReprAccessScope && "valid Type but not valid TypeRepr?");
245247
if (contextAccessScope.hasEqualDeclContextWith(*typeReprAccessScope) ||
246248
contextAccessScope.isChildOf(*typeReprAccessScope)) {

Diff for: lib/Sema/TypeCheckProtocol.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -3406,7 +3406,7 @@ void ConformanceChecker::recordTypeWitness(AssociatedTypeDecl *assocType,
34063406
// non-resilient modules.
34073407
Optional<AccessScope> underlyingTypeScope =
34083408
TypeAccessScopeChecker::getAccessScope(type, DC,
3409-
/*usableFromInline*/false);
3409+
/*usableFromInline*/false).Scope;
34103410
assert(underlyingTypeScope.has_value() &&
34113411
"the type is already invalid and we shouldn't have gotten here");
34123412

0 commit comments

Comments
 (0)