Skip to content

Commit bf074b0

Browse files
committed
Run some implicit imports through import resolution
Unloaded implicit imports (e.g. the `-import-module` flag) will now be processed by import resolution, and will be fully validated and cross-imported. Preloaded imports, like the standard library import, are still not run through full import resolution, but this is a definite improvement over the status quo. This also folds `-import-underlying-module` and `@_exported import <ParentModule>` into a single code path, slightly changing the diagnostic for a failed overlay-style underlying module import.
1 parent 7f14aff commit bf074b0

File tree

5 files changed

+121
-40
lines changed

5 files changed

+121
-40
lines changed

include/swift/AST/Import.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,10 @@ struct AttributedImport {
574574
void simple_display(llvm::raw_ostream &out,
575575
const AttributedImport<ImportedModule> &import);
576576

577+
/// A module which will be implicitly imported.
578+
void simple_display(llvm::raw_ostream &out,
579+
const AttributedImport<UnloadedImportedModule> &import);
580+
577581
// MARK: - Implicit imports
578582

579583
/// The kind of stdlib that should be imported.
@@ -617,10 +621,12 @@ struct ImplicitImportInfo {
617621
/// Contains names of and pointers to modules that must be implicitly imported.
618622
struct ImplicitImportList {
619623
ArrayRef<AttributedImport<ImportedModule>> imports;
624+
ArrayRef<AttributedImport<UnloadedImportedModule>> unloadedImports;
620625

621626
friend bool operator==(const ImplicitImportList &lhs,
622627
const ImplicitImportList &rhs) {
623-
return lhs.imports == rhs.imports;
628+
return lhs.imports == rhs.imports
629+
&& lhs.unloadedImports == rhs.unloadedImports;
624630
}
625631
};
626632

lib/AST/TypeCheckRequests.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1423,12 +1423,50 @@ void swift::simple_display(llvm::raw_ostream &out,
14231423
out << " ]";
14241424
}
14251425

1426+
void swift::simple_display(llvm::raw_ostream &out,
1427+
const AttributedImport<UnloadedImportedModule> &import) {
1428+
out << "import of unloaded ";
1429+
1430+
import.module.getModulePath().print(out);
1431+
1432+
out << " [";
1433+
if (!import.module.getAccessPath().empty()) {
1434+
out << " scoped(";
1435+
import.module.getAccessPath().print(out);
1436+
out << ")";
1437+
}
1438+
if (import.options.contains(ImportFlags::Exported))
1439+
out << " exported";
1440+
if (import.options.contains(ImportFlags::Testable))
1441+
out << " testable";
1442+
if (import.options.contains(ImportFlags::ImplementationOnly))
1443+
out << " implementation-only";
1444+
if (import.options.contains(ImportFlags::PrivateImport))
1445+
out << " private(" << import.sourceFileArg << ")";
1446+
1447+
if (import.options.contains(ImportFlags::SPIAccessControl)) {
1448+
out << " spi(";
1449+
llvm::interleaveComma(import.spiGroups, out, [&out](Identifier name) {
1450+
simple_display(out, name);
1451+
});
1452+
out << ")";
1453+
}
1454+
1455+
out << " ]";
1456+
}
1457+
14261458
void swift::simple_display(llvm::raw_ostream &out,
14271459
const ImplicitImportList &importList) {
14281460
llvm::interleaveComma(importList.imports, out,
14291461
[&](const auto &import) {
14301462
simple_display(out, import);
14311463
});
1464+
if (!importList.imports.empty() && !importList.unloadedImports.empty())
1465+
out << ", ";
1466+
llvm::interleaveComma(importList.unloadedImports, out,
1467+
[&](const auto &import) {
1468+
simple_display(out, import);
1469+
});
14321470
}
14331471

14341472
//----------------------------------------------------------------------------//

lib/Sema/ImportResolution.cpp

Lines changed: 64 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,12 @@ struct UnboundImport {
6565
///
6666
/// If this UnboundImport represents a cross-import, contains the declaring
6767
/// module's \c ModuleDecl.
68-
PointerUnion<ImportDecl *, ModuleDecl *> importOrUnderlyingModuleDecl;
68+
PointerUnion<NullablePtr<ImportDecl>, ModuleDecl *>
69+
importOrUnderlyingModuleDecl;
6970

7071
NullablePtr<ImportDecl> getImportDecl() const {
71-
return importOrUnderlyingModuleDecl.is<ImportDecl *>() ?
72-
importOrUnderlyingModuleDecl.get<ImportDecl *>() : nullptr;
72+
return importOrUnderlyingModuleDecl.is<NullablePtr<ImportDecl>>() ?
73+
importOrUnderlyingModuleDecl.get<NullablePtr<ImportDecl>>() : nullptr;
7374
}
7475

7576
NullablePtr<ModuleDecl> getUnderlyingModule() const {
@@ -80,6 +81,9 @@ struct UnboundImport {
8081
/// Create an UnboundImport for a user-written import declaration.
8182
explicit UnboundImport(ImportDecl *ID);
8283

84+
/// Create an UnboundImport for an unloaded implicit import.
85+
explicit UnboundImport(AttributedImport<UnloadedImportedModule> implicit);
86+
8387
/// Create an UnboundImport for a cross-import overlay.
8488
explicit UnboundImport(ASTContext &ctx,
8589
const UnboundImport &base, Identifier overlayName,
@@ -192,6 +196,9 @@ class ImportResolver final : public DeclVisitor<ImportResolver> {
192196
return ctx.Diags.diagnose(std::forward<ArgTypes>(Args)...);
193197
}
194198

199+
/// Calls \c bindImport() on unbound imports until \c boundImports is drained.
200+
void bindPendingImports();
201+
195202
/// Check a single unbound import, bind it, add it to \c boundImports,
196203
/// and add its cross-import overlays to \c unboundImports.
197204
void bindImport(UnboundImport &&I);
@@ -284,6 +291,10 @@ void ImportResolver::visitImportDecl(ImportDecl *ID) {
284291
assert(unboundImports.empty());
285292

286293
unboundImports.emplace_back(ID);
294+
bindPendingImports();
295+
}
296+
297+
void ImportResolver::bindPendingImports() {
287298
while(!unboundImports.empty())
288299
bindImport(unboundImports.pop_back_val());
289300
}
@@ -336,14 +347,16 @@ void ImportResolver::addImport(const UnboundImport &I, ModuleDecl *M) {
336347
// MARK: Import module loading
337348
//===----------------------------------------------------------------------===//
338349

339-
ModuleDecl *
340-
ImportResolver::getModule(ImportPath::Module modulePath) {
350+
static ModuleDecl *
351+
getModuleImpl(ImportPath::Module modulePath, ModuleDecl *loadingModule,
352+
bool canImportBuiltin) {
353+
ASTContext &ctx = loadingModule->getASTContext();
354+
341355
assert(!modulePath.empty());
342356
auto moduleID = modulePath[0];
343357

344358
// The Builtin module cannot be explicitly imported unless we're a .sil file.
345-
if (SF.Kind == SourceFileKind::SIL &&
346-
moduleID.Item == ctx.TheBuiltinModule->getName())
359+
if (canImportBuiltin && moduleID.Item == ctx.TheBuiltinModule->getName())
347360
return ctx.TheBuiltinModule;
348361

349362
// If the imported module name is the same as the current module,
@@ -352,8 +365,7 @@ ImportResolver::getModule(ImportPath::Module modulePath) {
352365
//
353366
// FIXME: We'd like to only use this in SIL mode, but unfortunately we use it
354367
// for clang overlays as well.
355-
if (moduleID.Item == SF.getParentModule()->getName() &&
356-
modulePath.size() == 1) {
368+
if (moduleID.Item == loadingModule->getName() && modulePath.size() == 1) {
357369
if (auto importer = ctx.getClangModuleLoader())
358370
return importer->loadModule(moduleID.Loc, modulePath);
359371
return nullptr;
@@ -362,6 +374,12 @@ ImportResolver::getModule(ImportPath::Module modulePath) {
362374
return ctx.getModule(modulePath);
363375
}
364376

377+
ModuleDecl *
378+
ImportResolver::getModule(ImportPath::Module modulePath) {
379+
return getModuleImpl(modulePath, SF.getParentModule(),
380+
/*canImportBuiltin=*/SF.Kind == SourceFileKind::SIL);
381+
}
382+
365383
NullablePtr<ModuleDecl>
366384
UnboundImport::getTopLevelModule(ModuleDecl *M, SourceFile &SF) {
367385
if (import.module.getModulePath().size() == 1)
@@ -391,16 +409,25 @@ UnboundImport::getTopLevelModule(ModuleDecl *M, SourceFile &SF) {
391409
// MARK: Implicit imports
392410
//===----------------------------------------------------------------------===//
393411

394-
static void diagnoseNoSuchModule(ASTContext &ctx, SourceLoc importLoc,
412+
static void diagnoseNoSuchModule(ModuleDecl *importingModule,
413+
SourceLoc importLoc,
395414
ImportPath::Module modulePath,
396415
bool nonfatalInREPL) {
397-
SmallString<64> modulePathStr;
398-
modulePath.getString(modulePathStr);
399-
400-
auto diagKind = diag::sema_no_import;
401-
if (nonfatalInREPL && ctx.LangOpts.DebuggerSupport)
402-
diagKind = diag::sema_no_import_repl;
403-
ctx.Diags.diagnose(importLoc, diagKind, modulePathStr);
416+
ASTContext &ctx = importingModule->getASTContext();
417+
418+
if (modulePath.size() == 1 &&
419+
importingModule->getName() == modulePath.front().Item) {
420+
ctx.Diags.diagnose(importLoc, diag::error_underlying_module_not_found,
421+
importingModule->getName());
422+
} else {
423+
SmallString<64> modulePathStr;
424+
modulePath.getString(modulePathStr);
425+
426+
auto diagKind = diag::sema_no_import;
427+
if (nonfatalInREPL && ctx.LangOpts.DebuggerSupport)
428+
diagKind = diag::sema_no_import_repl;
429+
ctx.Diags.diagnose(importLoc, diagKind, modulePathStr);
430+
}
404431

405432
if (ctx.SearchPathOpts.SDKPath.empty() &&
406433
llvm::Triple(llvm::sys::getProcessTriple()).isMacOSX()) {
@@ -413,6 +440,7 @@ ImplicitImportList
413440
ModuleImplicitImportsRequest::evaluate(Evaluator &evaluator,
414441
ModuleDecl *module) const {
415442
SmallVector<AttributedImport<ImportedModule>, 4> imports;
443+
SmallVector<AttributedImport<UnloadedImportedModule>, 4> unloadedImports;
416444

417445
auto &ctx = module->getASTContext();
418446
auto &importInfo = module->getImplicitImportInfo();
@@ -436,16 +464,8 @@ ModuleImplicitImportsRequest::evaluate(Evaluator &evaluator,
436464
imports.emplace_back(ImportedModule(stdlib));
437465

438466
// Add any modules we were asked to implicitly import.
439-
for (auto unloadedImport : importInfo.AdditionalUnloadedImports) {
440-
auto *importModule = ctx.getModule(unloadedImport.module.getModulePath());
441-
if (!importModule) {
442-
diagnoseNoSuchModule(ctx, SourceLoc(),
443-
unloadedImport.module.getModulePath(),
444-
/*nonfatalInREPL=*/false);
445-
continue;
446-
}
447-
imports.push_back(unloadedImport.getLoaded(importModule));
448-
}
467+
llvm::copy(importInfo.AdditionalUnloadedImports,
468+
std::back_inserter(unloadedImports));
449469

450470
// Add any pre-loaded modules.
451471
llvm::copy(importInfo.AdditionalImports, std::back_inserter(imports));
@@ -464,18 +484,15 @@ ModuleImplicitImportsRequest::evaluate(Evaluator &evaluator,
464484

465485
// Implicitly import the underlying Clang half of this module if needed.
466486
if (importInfo.ShouldImportUnderlyingModule) {
467-
auto *underlyingMod = clangImporter->loadModule(
468-
SourceLoc(), ImportPath::Module::Builder(module->getName()).get());
469-
if (underlyingMod) {
470-
imports.emplace_back(ImportedModule(underlyingMod),
471-
ImportFlags::Exported);
472-
} else {
473-
ctx.Diags.diagnose(SourceLoc(), diag::error_underlying_module_not_found,
474-
module->getName());
475-
}
487+
// An @_exported self-import is loaded from ClangImporter instead of being
488+
// rejected; see the special case in getModuleImpl() for details.
489+
ImportPath::Builder importPath(module->getName());
490+
unloadedImports.emplace_back(UnloadedImportedModule(importPath.copyTo(ctx),
491+
/*isScoped=*/false),
492+
ImportFlags::Exported);
476493
}
477494

478-
return { ctx.AllocateCopy(imports) };
495+
return { ctx.AllocateCopy(imports), ctx.AllocateCopy(unloadedImports) };
479496
}
480497

481498
void ImportResolver::addImplicitImports() {
@@ -487,8 +504,17 @@ void ImportResolver::addImplicitImports() {
487504
import.module.importedModule->isStdlibModule()));
488505
boundImports.push_back(import);
489506
}
507+
508+
for (auto &unloadedImport : implicitImports.unloadedImports)
509+
unboundImports.emplace_back(unloadedImport);
510+
511+
bindPendingImports();
490512
}
491513

514+
UnboundImport::UnboundImport(AttributedImport<UnloadedImportedModule> implicit)
515+
: import(implicit), importLoc(),
516+
importOrUnderlyingModuleDecl(static_cast<ImportDecl *>(nullptr)) {}
517+
492518
//===----------------------------------------------------------------------===//
493519
// MARK: Import validation (except for scoped imports)
494520
//===----------------------------------------------------------------------===//
@@ -552,7 +578,7 @@ bool UnboundImport::checkModuleLoaded(ModuleDecl *M, SourceFile &SF) {
552578
if (M)
553579
return true;
554580

555-
diagnoseNoSuchModule(SF.getASTContext(), importLoc,
581+
diagnoseNoSuchModule(SF.getParentModule(), importLoc,
556582
import.module.getModulePath(), /*nonfatalInREPL=*/true);
557583
return false;
558584
}

test/ClangImporter/MixedSource/mixed-target-using-module.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -F %S/Inputs/mixed-target/ -module-name Mixed -import-underlying-module -typecheck %s -verify -enable-objc-interop -disable-objc-attr-requires-foundation-module
22
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -F %S/Inputs/mixed-target/ -module-name Mixed -import-underlying-module -enable-objc-interop -emit-ir %S/../../Inputs/empty.swift | %FileCheck -check-prefix=CHECK-AUTOLINK %s
3+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -F %S/Inputs/mixed-target/ -module-name Mixed -DOVERLAY_STYLE_RIGHT -enable-objc-interop -emit-ir %S/../../Inputs/empty.swift | %FileCheck -check-prefix=CHECK-AUTOLINK %s
34
// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -F %S/Inputs/mixed-target/ -module-name WrongName -import-underlying-module -typecheck %s -enable-objc-interop -disable-objc-attr-requires-foundation-module 2>&1 | %FileCheck -check-prefix=CHECK-WRONG-NAME %s
5+
// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -F %S/Inputs/mixed-target/ -module-name WrongName -DOVERLAY_STYLE_WRONG -typecheck %s -enable-objc-interop -disable-objc-attr-requires-foundation-module 2>&1 | %FileCheck -check-prefix=CHECK-WRONG-NAME %s
6+
7+
#if OVERLAY_STYLE_RIGHT
8+
@_exported import Mixed
9+
#elseif OVERLAY_STYLE_WRONG
10+
@_exported import WrongName
11+
#endif
412

513
// CHECK-AUTOLINK: !llvm.linker.options = !{
614
// CHECK-AUTOLINK-NOT: !"-framework"

test/CrossImport/common-case.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@
55
// RUN: cp -r %S/Inputs/lib-templates/* %t/
66
// RUN: %{python} %S/Inputs/rewrite-module-triples.py %t %module-target-triple
77

8-
// RUN: %target-typecheck-verify-swift -enable-cross-import-overlays -I %t/include -I %t/lib/swift -F %t/Frameworks
8+
// RUN: %target-typecheck-verify-swift -enable-cross-import-overlays -DIMPORT_BYSTANDING_LIBRARY -I %t/include -I %t/lib/swift -F %t/Frameworks
9+
// RUN: %target-typecheck-verify-swift -enable-cross-import-overlays -import-module BystandingLibrary -I %t/include -I %t/lib/swift -F %t/Frameworks
910

1011
// Each framework has a cross-import overlay with this library:
12+
#if IMPORT_BYSTANDING_LIBRARY
1113
import BystandingLibrary
14+
#endif
1215

1316
// 1. A Swift framework
1417

0 commit comments

Comments
 (0)