Skip to content

Commit 983ea09

Browse files
committed
[ClangImporter] On failing a module lookup, reset Clang's DiagnosticEngine, in addition to filtering out the "module not found" diagnostic
Even though 'ClangDiagnosticConsumer' filters out 'clang::diag::err_module_not_found' from being emitted by the Swift compiler, delegating to Swift's module-loading logic for error-handling, we must also ensure that we reset Clang's 'DiagnosticEngine''s error count. Otherwise, if we do not, final stages of IRGen will query Clang's code-gen to finalize its 'Module', which Clang will not do if its internal DiagnosticEngine contains errors. This will cause Swift's IRGen to fail, even though the only error emitted was one Swift intended to suppress.
1 parent 338d497 commit 983ea09

File tree

4 files changed

+78
-6
lines changed

4 files changed

+78
-6
lines changed

lib/ClangImporter/ClangDiagnosticConsumer.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ void ClangDiagnosticConsumer::HandleDiagnostic(
118118
// we're looking for.
119119
if (clangDiag.getID() == clang::diag::err_module_not_found &&
120120
CurrentImport && clangDiag.getArgStdStr(0) == CurrentImport->getName()) {
121+
CurrentImportNotFound = true;
121122
return;
122123
}
123124

lib/ClangImporter/ClangDiagnosticConsumer.h

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,19 @@ namespace swift {
2323
class ClangDiagnosticConsumer : public clang::TextDiagnosticPrinter {
2424
struct LoadModuleRAII {
2525
ClangDiagnosticConsumer *Consumer;
26+
clang::DiagnosticsEngine *Engine;
27+
bool DiagEngineClearPriorToLookup;
28+
2629
public:
2730
LoadModuleRAII(ClangDiagnosticConsumer &consumer,
31+
clang::DiagnosticsEngine &engine,
2832
const clang::IdentifierInfo *import)
29-
: Consumer(&consumer) {
33+
: Consumer(&consumer), Engine(&engine) {
3034
assert(import);
3135
assert(!Consumer->CurrentImport);
36+
assert(!Consumer->CurrentImportNotFound);
3237
Consumer->CurrentImport = import;
38+
DiagEngineClearPriorToLookup = !engine.hasErrorOccurred();
3339
}
3440

3541
LoadModuleRAII(LoadModuleRAII &) = delete;
@@ -45,8 +51,21 @@ class ClangDiagnosticConsumer : public clang::TextDiagnosticPrinter {
4551
}
4652

4753
~LoadModuleRAII() {
48-
if (Consumer)
54+
if (Consumer) {
55+
// We must reset Clang's diagnostic engine here since we know that only
56+
// the module lookup errors have been emitted. While the ClangDiagnosticConsumer
57+
// takes care of filtering out the diagnostics from the output and from
58+
// being handled by Swift's DiagnosticEngine, we must ensure to also
59+
// reset Clang's DiagnosticEngine because its state is queries in later
60+
// stages of compilation and errors emitted on "module_not_found" should not
61+
// be counted.
62+
// FIXME: We must instead allow for module loading in Clang to fail without
63+
// needing to emit a diagnostic.
64+
if (Engine && Consumer->CurrentImportNotFound && DiagEngineClearPriorToLookup)
65+
Engine->Reset();
4966
Consumer->CurrentImport = nullptr;
67+
Consumer->CurrentImportNotFound = false;
68+
}
5069
}
5170
};
5271

@@ -56,6 +75,7 @@ class ClangDiagnosticConsumer : public clang::TextDiagnosticPrinter {
5675
ClangImporter::Implementation &ImporterImpl;
5776

5877
const clang::IdentifierInfo *CurrentImport = nullptr;
78+
bool CurrentImportNotFound = false;
5979
SourceLoc DiagLoc;
6080
const bool DumpToStderr;
6181

@@ -65,9 +85,10 @@ class ClangDiagnosticConsumer : public clang::TextDiagnosticPrinter {
6585
bool dumpToStderr);
6686

6787
LoadModuleRAII handleImport(const clang::IdentifierInfo *name,
88+
clang::DiagnosticsEngine &engine,
6889
SourceLoc diagLoc) {
6990
DiagLoc = diagLoc;
70-
return LoadModuleRAII(*this, name);
91+
return LoadModuleRAII(*this, engine, name);
7192
}
7293

7394
void HandleDiagnostic(clang::DiagnosticsEngine::Level diagLevel,

lib/ClangImporter/ClangImporter.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2058,14 +2058,16 @@ ModuleDecl *ClangImporter::Implementation::loadModuleClang(
20582058
exportSourceLoc(component.Loc));
20592059
}
20602060

2061-
auto &rawDiagClient = Instance->getDiagnosticClient();
2061+
auto &diagEngine = Instance->getDiagnostics();
2062+
auto &rawDiagClient = *diagEngine.getClient();
20622063
auto &diagClient = static_cast<ClangDiagnosticConsumer &>(rawDiagClient);
20632064

20642065
auto loadModule = [&](clang::ModuleIdPath path,
20652066
clang::Module::NameVisibilityKind visibility)
20662067
-> clang::ModuleLoadResult {
20672068
auto importRAII =
2068-
diagClient.handleImport(clangPath.front().first, importLoc);
2069+
diagClient.handleImport(clangPath.front().first, diagEngine,
2070+
importLoc);
20692071

20702072
std::string preservedIndexStorePathOption;
20712073
auto &clangFEOpts = Instance->getFrontendOpts();
@@ -2079,7 +2081,6 @@ ModuleDecl *ClangImporter::Implementation::loadModuleClang(
20792081
}
20802082

20812083
clang::SourceLocation clangImportLoc = getNextIncludeLoc();
2082-
20832084
clang::ModuleLoadResult result =
20842085
Instance->loadModule(clangImportLoc, path, visibility,
20852086
/*IsInclusionDirective=*/false);
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %empty-directory(%t/clang-module-cache)
3+
// RUN: %empty-directory(%t/Foo.swiftmodule)
4+
// RUN: %empty-directory(%t/inputs)
5+
// RUN: echo "@_implementationOnly import A; public func foo() {}" > %t/Foo.swift
6+
// REQUIRES: executable_test
7+
// REQUIRES: objc_interop
8+
9+
// Create the explicit module inputs map
10+
// RUN: echo "[{" > %/t/inputs/map.json
11+
// RUN: echo "\"moduleName\": \"Foo\"," >> %/t/inputs/map.json
12+
// RUN: echo "\"modulePath\": \"%/t/Foo.swiftmodule/%target-swiftmodule-name\"," >> %/t/inputs/map.json
13+
// RUN: echo "\"isFramework\": false," >> %/t/inputs/map.json
14+
// RUN: echo "}," >> %/t/inputs/map.json
15+
// RUN: echo "{" >> %/t/inputs/map.json
16+
// RUN: echo "\"moduleName\": \"Swift\"," >> %/t/inputs/map.json
17+
// RUN: echo "\"modulePath\": \"%/stdlib_module\"," >> %/t/inputs/map.json
18+
// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json
19+
// RUN: echo "}," >> %/t/inputs/map.json
20+
// RUN: echo "{" >> %/t/inputs/map.json
21+
// RUN: echo "\"moduleName\": \"SwiftOnoneSupport\"," >> %/t/inputs/map.json
22+
// RUN: echo "\"modulePath\": \"%/ononesupport_module\"," >> %/t/inputs/map.json
23+
// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json
24+
// RUN: echo "}," >> %/t/inputs/map.json
25+
// RUN: echo "{" >> %/t/inputs/map.json
26+
// RUN: echo "\"moduleName\": \"SwiftShims\"," >> %/t/inputs/map.json
27+
// RUN: echo "\"isFramework\": false," >> %/t/inputs/map.json
28+
// RUN: echo "\"clangModuleMapPath\": \"%swift-lib-dir/swift/shims/module.modulemap\"," >> %/t/inputs/map.json
29+
// RUN: echo "\"clangModulePath\": \"%t/inputs/SwiftShims.pcm\"" >> %/t/inputs/map.json
30+
// RUN: echo "}]" >> %/t/inputs/map.json
31+
32+
// Emit the shims module PCM for explicit loading
33+
// RUN: %target-swift-emit-pcm -module-name SwiftShims %swift-lib-dir/swift/shims/module.modulemap -o %t/inputs/SwiftShims.pcm
34+
35+
@testable import Foo
36+
37+
// Step 1: build Foo Swift module
38+
// RUN: %target-swift-frontend -emit-module %t/Foo.swift -emit-module-path %t/Foo.swiftmodule/%target-swiftmodule-name -module-name Foo -emit-module-interface-path %t/Foo.swiftmodule/%target-swiftinterface-name -enable-library-evolution -I %S/Inputs/CHeaders -I %S/Inputs/Swift -enable-testing -swift-version 5 -disable-implicit-concurrency-module-import -disable-implicit-string-processing-module-import
39+
40+
// Step 2: scan dependencies and ensure the transitive dependency on "A" is misssing
41+
// RUN: %target-swift-frontend -scan-dependencies %s -o %t/deps.json -I %t -sdk %t -prebuilt-module-cache-path %t/clang-module-cache
42+
// RUN: %validate-json %t/deps.json | %FileCheck -check-prefix CHECK_SCAN %s
43+
// CHECK_SCAN-NOT: "swift": "A"
44+
45+
// Step 3: Run an explicit module compile of this file ensuring object files are produced
46+
// RUN: %target-swift-frontend -emit-object -o %t/optional_transitive_dep_load_fail.o -disable-implicit-swift-modules -disable-implicit-concurrency-module-import -disable-implicit-string-processing-module-import -module-cache-path %t.module-cache -explicit-swift-module-map-file %t/inputs/map.json %s
47+
48+
// Step 4: Ensure the resulting object file exists
49+
// RUN: ls %t/optional_transitive_dep_load_fail.o > /dev/null

0 commit comments

Comments
 (0)