Skip to content

Commit bf1b17d

Browse files
authored
Merge pull request #31872 from hamishknight/implementation-details
2 parents 0e008e0 + 851f640 commit bf1b17d

File tree

6 files changed

+115
-88
lines changed

6 files changed

+115
-88
lines changed

include/swift/AST/TypeCheckRequests.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2430,6 +2430,32 @@ class ResolveTypeRequest
24302430
void simple_display(llvm::raw_ostream &out, const TypeResolution *resolution);
24312431
SourceLoc extractNearestSourceLoc(const TypeRepr *repr);
24322432

2433+
/// Checks to see if any of the imports in a module use `@_implementationOnly`
2434+
/// in one file and not in another.
2435+
///
2436+
/// Like redeclaration checking, but for imports.
2437+
///
2438+
/// This is a request purely to ensure that we don't need to perform the same
2439+
/// checking for each file we resolve imports for.
2440+
/// FIXME: Once import resolution operates at module-level, this checking can
2441+
/// integrated into it.
2442+
class CheckInconsistentImplementationOnlyImportsRequest
2443+
: public SimpleRequest<CheckInconsistentImplementationOnlyImportsRequest,
2444+
evaluator::SideEffect(ModuleDecl *),
2445+
RequestFlags::Cached> {
2446+
public:
2447+
using SimpleRequest::SimpleRequest;
2448+
2449+
private:
2450+
friend SimpleRequest;
2451+
2452+
evaluator::SideEffect evaluate(Evaluator &evaluator, ModuleDecl *mod) const;
2453+
2454+
public:
2455+
// Cached.
2456+
bool isCached() const { return true; }
2457+
};
2458+
24332459
// Allow AnyValue to compare two Type values, even though Type doesn't
24342460
// support ==.
24352461
template<>

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ SWIFT_REQUEST(TypeChecker, AttachedPropertyWrappersRequest,
2929
NoLocationInfo)
3030
SWIFT_REQUEST(TypeChecker, CallerSideDefaultArgExprRequest,
3131
Expr *(DefaultArgumentExpr *), SeparatelyCached, NoLocationInfo)
32+
SWIFT_REQUEST(TypeChecker, CheckInconsistentImplementationOnlyImportsRequest,
33+
evaluator::SideEffect(ModuleDecl *), Cached, NoLocationInfo)
3234
SWIFT_REQUEST(TypeChecker, CheckRedeclarationRequest,
3335
evaluator::SideEffect(ValueDecl *),
3436
Uncached, NoLocationInfo)

include/swift/Subsystems.h

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -152,14 +152,6 @@ namespace swift {
152152
/// emitted.
153153
void performWholeModuleTypeChecking(SourceFile &SF);
154154

155-
/// Checks to see if any of the imports in \p M use `@_implementationOnly` in
156-
/// one file and not in another.
157-
///
158-
/// Like redeclaration checking, but for imports. This isn't part of
159-
/// swift::performWholeModuleTypeChecking because it's linear in the number
160-
/// of declarations in the module.
161-
void checkInconsistentImplementationOnlyImports(ModuleDecl *M);
162-
163155
/// Recursively validate the specified type.
164156
///
165157
/// This is used when dealing with partial source files (e.g. SIL parsing,

lib/Frontend/Frontend.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -897,8 +897,6 @@ void CompilerInstance::finishTypeChecking() {
897897
performWholeModuleTypeChecking(SF);
898898
});
899899
}
900-
901-
checkInconsistentImplementationOnlyImports(MainModule);
902900
}
903901

904902
SourceFile *CompilerInstance::createSourceFileForMainModule(

lib/Sema/ImportResolution.cpp

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,87 @@ void UnboundImport::diagnoseInvalidAttr(DeclAttrKind attrKind,
674674
attr->setInvalid();
675675
}
676676

677+
evaluator::SideEffect
678+
CheckInconsistentImplementationOnlyImportsRequest::evaluate(
679+
Evaluator &evaluator, ModuleDecl *mod) const {
680+
bool hasAnyImplementationOnlyImports =
681+
llvm::any_of(mod->getFiles(), [](const FileUnit *F) -> bool {
682+
auto *SF = dyn_cast<SourceFile>(F);
683+
return SF && SF->hasImplementationOnlyImports();
684+
});
685+
if (!hasAnyImplementationOnlyImports)
686+
return {};
687+
688+
auto diagnose = [mod](const ImportDecl *normalImport,
689+
const ImportDecl *implementationOnlyImport) {
690+
auto &diags = mod->getDiags();
691+
{
692+
InFlightDiagnostic warning =
693+
diags.diagnose(normalImport, diag::warn_implementation_only_conflict,
694+
normalImport->getModule()->getName());
695+
if (normalImport->getAttrs().isEmpty()) {
696+
// Only try to add a fix-it if there's no other annotations on the
697+
// import to avoid creating things like
698+
// `@_implementationOnly @_exported import Foo`. The developer can
699+
// resolve those manually.
700+
warning.fixItInsert(normalImport->getStartLoc(),
701+
"@_implementationOnly ");
702+
}
703+
}
704+
diags.diagnose(implementationOnlyImport,
705+
diag::implementation_only_conflict_here);
706+
};
707+
708+
llvm::DenseMap<ModuleDecl *, std::vector<const ImportDecl *>> normalImports;
709+
llvm::DenseMap<ModuleDecl *, const ImportDecl *> implementationOnlyImports;
710+
711+
for (const FileUnit *file : mod->getFiles()) {
712+
auto *SF = dyn_cast<SourceFile>(file);
713+
if (!SF)
714+
continue;
715+
716+
for (auto *topLevelDecl : SF->getTopLevelDecls()) {
717+
auto *nextImport = dyn_cast<ImportDecl>(topLevelDecl);
718+
if (!nextImport)
719+
continue;
720+
721+
ModuleDecl *module = nextImport->getModule();
722+
if (!module)
723+
continue;
724+
725+
if (nextImport->getAttrs().hasAttribute<ImplementationOnlyAttr>()) {
726+
// We saw an implementation-only import.
727+
bool isNew =
728+
implementationOnlyImports.insert({module, nextImport}).second;
729+
if (!isNew)
730+
continue;
731+
732+
auto seenNormalImportPosition = normalImports.find(module);
733+
if (seenNormalImportPosition != normalImports.end()) {
734+
for (auto *seenNormalImport : seenNormalImportPosition->getSecond())
735+
diagnose(seenNormalImport, nextImport);
736+
737+
// We're done with these; keep the map small if possible.
738+
normalImports.erase(seenNormalImportPosition);
739+
}
740+
continue;
741+
}
742+
743+
// We saw a non-implementation-only import. Is that in conflict with what
744+
// we've seen?
745+
if (auto *seenImplementationOnlyImport =
746+
implementationOnlyImports.lookup(module)) {
747+
diagnose(nextImport, seenImplementationOnlyImport);
748+
continue;
749+
}
750+
751+
// Otherwise, record it for later.
752+
normalImports[module].push_back(nextImport);
753+
}
754+
}
755+
return {};
756+
}
757+
677758
//===----------------------------------------------------------------------===//
678759
// MARK: Scoped imports
679760
//===----------------------------------------------------------------------===//

lib/Sema/TypeChecker.cpp

Lines changed: 6 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,12 @@ TypeCheckSourceFileRequest::evaluate(Evaluator &eval, SourceFile *SF) const {
351351
typeCheckDelayedFunctions(*SF);
352352
}
353353

354+
// Check to see if there's any inconsistent @_implementationOnly imports.
355+
evaluateOrDefault(
356+
Ctx.evaluator,
357+
CheckInconsistentImplementationOnlyImportsRequest{SF->getParentModule()},
358+
{});
359+
354360
// Checking that benefits from having the whole module available.
355361
if (!Ctx.TypeCheckerOpts.DelayWholeModuleChecking) {
356362
performWholeModuleTypeChecking(*SF);
@@ -406,84 +412,6 @@ bool swift::isAdditiveArithmeticConformanceDerivationEnabled(SourceFile &SF) {
406412
return isDifferentiableProgrammingEnabled(SF);
407413
}
408414

409-
void swift::checkInconsistentImplementationOnlyImports(ModuleDecl *MainModule) {
410-
bool hasAnyImplementationOnlyImports =
411-
llvm::any_of(MainModule->getFiles(), [](const FileUnit *F) -> bool {
412-
auto *SF = dyn_cast<SourceFile>(F);
413-
return SF && SF->hasImplementationOnlyImports();
414-
});
415-
if (!hasAnyImplementationOnlyImports)
416-
return;
417-
418-
auto diagnose = [MainModule](const ImportDecl *normalImport,
419-
const ImportDecl *implementationOnlyImport) {
420-
auto &diags = MainModule->getDiags();
421-
{
422-
InFlightDiagnostic warning =
423-
diags.diagnose(normalImport, diag::warn_implementation_only_conflict,
424-
normalImport->getModule()->getName());
425-
if (normalImport->getAttrs().isEmpty()) {
426-
// Only try to add a fix-it if there's no other annotations on the
427-
// import to avoid creating things like
428-
// `@_implementationOnly @_exported import Foo`. The developer can
429-
// resolve those manually.
430-
warning.fixItInsert(normalImport->getStartLoc(),
431-
"@_implementationOnly ");
432-
}
433-
}
434-
diags.diagnose(implementationOnlyImport,
435-
diag::implementation_only_conflict_here);
436-
};
437-
438-
llvm::DenseMap<ModuleDecl *, std::vector<const ImportDecl *>> normalImports;
439-
llvm::DenseMap<ModuleDecl *, const ImportDecl *> implementationOnlyImports;
440-
441-
for (const FileUnit *file : MainModule->getFiles()) {
442-
auto *SF = dyn_cast<SourceFile>(file);
443-
if (!SF)
444-
continue;
445-
446-
for (auto *topLevelDecl : SF->getTopLevelDecls()) {
447-
auto *nextImport = dyn_cast<ImportDecl>(topLevelDecl);
448-
if (!nextImport)
449-
continue;
450-
451-
ModuleDecl *module = nextImport->getModule();
452-
if (!module)
453-
continue;
454-
455-
if (nextImport->getAttrs().hasAttribute<ImplementationOnlyAttr>()) {
456-
// We saw an implementation-only import.
457-
bool isNew =
458-
implementationOnlyImports.insert({module, nextImport}).second;
459-
if (!isNew)
460-
continue;
461-
462-
auto seenNormalImportPosition = normalImports.find(module);
463-
if (seenNormalImportPosition != normalImports.end()) {
464-
for (auto *seenNormalImport : seenNormalImportPosition->getSecond())
465-
diagnose(seenNormalImport, nextImport);
466-
467-
// We're done with these; keep the map small if possible.
468-
normalImports.erase(seenNormalImportPosition);
469-
}
470-
continue;
471-
}
472-
473-
// We saw a non-implementation-only import. Is that in conflict with what
474-
// we've seen?
475-
if (auto *seenImplementationOnlyImport =
476-
implementationOnlyImports.lookup(module)) {
477-
diagnose(nextImport, seenImplementationOnlyImport);
478-
continue;
479-
}
480-
481-
// Otherwise, record it for later.
482-
normalImports[module].push_back(nextImport);
483-
}
484-
}
485-
}
486-
487415
bool swift::performTypeLocChecking(ASTContext &Ctx, TypeLoc &T,
488416
DeclContext *DC,
489417
bool ProduceDiagnostics) {

0 commit comments

Comments
 (0)