Skip to content

Commit a7a5e34

Browse files
committed
Improve diagnostic for broken module interfaces
Currently, when a swiftinterface file fails to load, we emit the specific diagnostics for the failures, followed by a generic “failed to load module ‘Foo’” message. This PR improves that final diagnostic, particularly when the cause may be that the interface was emitted by a newer compiler using backwards-incompatible syntax.
1 parent 000572d commit a7a5e34

File tree

8 files changed

+80
-15
lines changed

8 files changed

+80
-15
lines changed

include/swift/AST/DiagnosticsSema.def

+7
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,13 @@ ERROR(sema_opening_import,Fatal,
686686

687687
ERROR(serialization_load_failed,Fatal,
688688
"failed to load module '%0'", (StringRef))
689+
ERROR(module_interface_build_failed,Fatal,
690+
"failed to build module '%0' from its module interface; "
691+
"%select{the compiler that produced it, '%2', may have used features "
692+
"that aren't supported by this compiler, '%3'"
693+
"|it may have been damaged or it may have triggered a bug in the Swift "
694+
"compiler when it was produced}1",
695+
(StringRef, bool, StringRef, StringRef))
689696
ERROR(serialization_malformed_module,Fatal,
690697
"malformed compiled module: %0", (StringRef))
691698
ERROR(serialization_module_too_new,Fatal,

include/swift/Frontend/ModuleInterfaceSupport.h

+3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
namespace swift {
2525

26+
class ASTContext;
2627
class ModuleDecl;
2728

2829
/// Options for controlling the generation of the .swiftinterface output.
@@ -45,8 +46,10 @@ struct ModuleInterfaceOptions {
4546
};
4647

4748
extern version::Version InterfaceFormatVersion;
49+
std::string getSwiftInterfaceCompilerVersionForCurrentCompiler(ASTContext &ctx);
4850

4951
llvm::Regex getSwiftInterfaceFormatVersionRegex();
52+
llvm::Regex getSwiftInterfaceCompilerVersionRegex();
5053
llvm::Regex getSwiftInterfaceModuleFlagsRegex();
5154

5255
/// Emit a stable module interface for \p M, which can be used by a client

lib/Frontend/ModuleInterfaceBuilder.cpp

+23-6
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,8 @@ void ModuleInterfaceBuilder::configureSubInvocation(
135135
}
136136

137137
bool ModuleInterfaceBuilder::extractSwiftInterfaceVersionAndArgs(
138-
swift::version::Version &Vers, llvm::StringSaver &SubArgSaver,
139-
SmallVectorImpl<const char *> &SubArgs) {
138+
swift::version::Version &Vers, StringRef &CompilerVersion,
139+
llvm::StringSaver &SubArgSaver, SmallVectorImpl<const char *> &SubArgs) {
140140
auto FileOrError = swift::vfs::getFileOrSTDIN(fs, interfacePath);
141141
if (!FileOrError) {
142142
diags.diagnose(diagnosticLoc, diag::error_open_input_file,
@@ -145,8 +145,9 @@ bool ModuleInterfaceBuilder::extractSwiftInterfaceVersionAndArgs(
145145
}
146146
auto SB = FileOrError.get()->getBuffer();
147147
auto VersRe = getSwiftInterfaceFormatVersionRegex();
148+
auto CompRe = getSwiftInterfaceCompilerVersionRegex();
148149
auto FlagRe = getSwiftInterfaceModuleFlagsRegex();
149-
SmallVector<StringRef, 1> VersMatches, FlagMatches;
150+
SmallVector<StringRef, 1> VersMatches, FlagMatches, CompMatches;
150151
if (!VersRe.match(SB, &VersMatches)) {
151152
diags.diagnose(diagnosticLoc,
152153
diag::error_extracting_version_from_module_interface);
@@ -161,6 +162,16 @@ bool ModuleInterfaceBuilder::extractSwiftInterfaceVersionAndArgs(
161162
assert(FlagMatches.size() == 2);
162163
Vers = swift::version::Version(VersMatches[1], SourceLoc(), &diags);
163164
llvm::cl::TokenizeGNUCommandLine(FlagMatches[1], SubArgSaver, SubArgs);
165+
166+
if (CompRe.match(SB, &CompMatches)) {
167+
assert(CompMatches.size() == 2);
168+
CompilerVersion = SubArgSaver.save(CompMatches[1]);
169+
}
170+
else {
171+
// Don't diagnose; handwritten module interfaces don't include this field.
172+
CompilerVersion = "(unspecified, file possibly handwritten)";
173+
}
174+
164175
return false;
165176
}
166177

@@ -274,7 +285,9 @@ bool ModuleInterfaceBuilder::buildSwiftModuleInternal(
274285
llvm::StringSaver SubArgSaver(SubArgsAlloc);
275286
SmallVector<const char *, 16> SubArgs;
276287
swift::version::Version Vers;
277-
if (extractSwiftInterfaceVersionAndArgs(Vers, SubArgSaver, SubArgs)) {
288+
StringRef emittedByCompiler;
289+
if (extractSwiftInterfaceVersionAndArgs(Vers, emittedByCompiler,
290+
SubArgSaver, SubArgs)) {
278291
SubError = true;
279292
return;
280293
}
@@ -325,8 +338,12 @@ bool ModuleInterfaceBuilder::buildSwiftModuleInternal(
325338
// emitted an error in the parent diagnostic engine, which is what
326339
// determines whether the process exits with a proper failure status.
327340
if (SubInstance.getASTContext().hadError()) {
328-
diags.diagnose(diagnosticLoc, diag::serialization_load_failed,
329-
moduleName);
341+
auto builtByCompiler =
342+
getSwiftInterfaceCompilerVersionForCurrentCompiler(
343+
SubInstance.getASTContext());
344+
diags.diagnose(diagnosticLoc, diag::module_interface_build_failed,
345+
moduleName, emittedByCompiler == builtByCompiler,
346+
emittedByCompiler, builtByCompiler);
330347
}
331348
};
332349

lib/Frontend/ModuleInterfaceBuilder.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ class ModuleInterfaceBuilder {
6666
bool IsHashBased);
6767

6868
bool extractSwiftInterfaceVersionAndArgs(
69-
version::Version &Vers, llvm::StringSaver &SubArgSaver,
70-
SmallVectorImpl<const char *> &SubArgs);
69+
version::Version &Vers, StringRef &CompilerVersion,
70+
llvm::StringSaver &SubArgSaver, SmallVectorImpl<const char *> &SubArgs);
7171

7272
bool buildSwiftModuleInternal(StringRef OutPath, bool ShouldSerializeDeps,
7373
std::unique_ptr<llvm::MemoryBuffer> *ModuleBuffer);

lib/Frontend/ModuleInterfaceSupport.cpp

+13-2
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ static void printToolVersionAndFlagsComment(raw_ostream &out,
6161
ModuleInterfaceOptions const &Opts,
6262
ModuleDecl *M) {
6363
auto &Ctx = M->getASTContext();
64-
auto ToolsVersion = swift::version::getSwiftFullVersion(
65-
Ctx.LangOpts.EffectiveLanguageVersion);
64+
auto ToolsVersion =
65+
getSwiftInterfaceCompilerVersionForCurrentCompiler(Ctx);
6666
out << "// " SWIFT_INTERFACE_FORMAT_VERSION_KEY ": "
6767
<< InterfaceFormatVersion << "\n";
6868
out << "// " SWIFT_COMPILER_VERSION_KEY ": "
@@ -71,6 +71,12 @@ static void printToolVersionAndFlagsComment(raw_ostream &out,
7171
<< Opts.Flags << "\n";
7272
}
7373

74+
std::string
75+
swift::getSwiftInterfaceCompilerVersionForCurrentCompiler(ASTContext &ctx) {
76+
return swift::version::getSwiftFullVersion(
77+
ctx.LangOpts.EffectiveLanguageVersion);
78+
}
79+
7480
llvm::Regex swift::getSwiftInterfaceFormatVersionRegex() {
7581
return llvm::Regex("^// " SWIFT_INTERFACE_FORMAT_VERSION_KEY
7682
": ([0-9\\.]+)$", llvm::Regex::Newline);
@@ -81,6 +87,11 @@ llvm::Regex swift::getSwiftInterfaceModuleFlagsRegex() {
8187
llvm::Regex::Newline);
8288
}
8389

90+
llvm::Regex swift::getSwiftInterfaceCompilerVersionRegex() {
91+
return llvm::Regex("^// " SWIFT_COMPILER_VERSION_KEY
92+
": (.+)$", llvm::Regex::Newline);
93+
}
94+
8495
/// Prints the imported modules in \p M to \p out in the form of \c import
8596
/// source declarations.
8697
static void printImports(raw_ostream &out,

test/ModuleInterface/ModuleCache/module-cache-diagnostics.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@
5757
// RUN: %{python} %S/Inputs/check-is-old.py %t/modulecache/OtherModule-*.swiftmodule %t/modulecache/LeafModule-*.swiftmodule
5858
// RUN: %FileCheck %s -check-prefix=CHECK-ERROR <%t/err.txt
5959
// CHECK-ERROR: LeafModule.swiftinterface:7:8: error: no such module 'NotAModule'
60-
// CHECK-ERROR: OtherModule.swiftinterface:4:8: error: failed to load module 'LeafModule'
61-
// CHECK-ERROR: module-cache-diagnostics.swift:{{[0-9]+}}:8: error: failed to load module 'OtherModule'
60+
// CHECK-ERROR: OtherModule.swiftinterface:4:8: error: failed to build module 'LeafModule' from its module interface; it may have been damaged or it may have triggered a bug in the Swift compiler when it was produced
61+
// CHECK-ERROR: module-cache-diagnostics.swift:{{[0-9]+}}:8: error: failed to build module 'OtherModule' from its module interface; it may have been damaged or it may have triggered a bug in the Swift compiler when it was produced
6262
// CHECK-ERROR-NOT: error:
6363
//
6464
//
@@ -82,8 +82,8 @@
8282
// RUN: %{python} %S/Inputs/check-is-old.py %t/modulecache/OtherModule-*.swiftmodule %t/modulecache/LeafModule-*.swiftmodule
8383
// RUN: %FileCheck %s -check-prefix=CHECK-ERROR-INLINE <%t/err-inline.txt
8484
// CHECK-ERROR-INLINE: LeafModule.swiftinterface:6:33: error: use of unresolved identifier 'unresolved'
85-
// CHECK-ERROR-INLINE: OtherModule.swiftinterface:4:8: error: failed to load module 'LeafModule'
86-
// CHECK-ERROR-INLINE: module-cache-diagnostics.swift:{{[0-9]+}}:8: error: failed to load module 'OtherModule'
85+
// CHECK-ERROR-INLINE: OtherModule.swiftinterface:4:8: error: failed to build module 'LeafModule' from its module interface; it may have been damaged or it may have triggered a bug in the Swift compiler when it was produced
86+
// CHECK-ERROR-INLINE: module-cache-diagnostics.swift:{{[0-9]+}}:8: error: failed to build module 'OtherModule' from its module interface; it may have been damaged or it may have triggered a bug in the Swift compiler when it was produced
8787
// CHECK-ERROR-INLINE-NOT: error:
8888
//
8989
//
+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Tests the diagnostics emitted when we can't build a module interface, and
2+
// particularly whether or not they call out compiler version mismatches.
3+
//
4+
// First, emit an empty module interface from the current compiler, then add
5+
// some invalid syntax to it:
6+
//
7+
// RUN: %empty-directory(%t)
8+
// RUN: echo "" | %target-swift-frontend -typecheck -emit-module-interface-path %t/UnbuildableCurrent.swiftinterface -enable-library-evolution -swift-version 5 -module-name UnbuildableCurrent -
9+
// RUN: echo "public func fn(_: String = #somethingYouveNeverHeardOf)" >> %t/UnbuildableCurrent.swiftinterface
10+
//
11+
// Next, modify a version of it so it looks like it was built with a different Swift compiler:
12+
//
13+
// RUN: sed -E -e 's|swift-compiler-version: (.*)|swift-compiler-version: NeoTokyoSwift 2000.42|' -e 's|UnbuildableCurrent|UnbuildableFuture|g' %t/UnbuildableCurrent.swiftinterface > %t/UnbuildableFuture.swiftinterface
14+
//
15+
// And finally, test:
16+
//
17+
// RUN: not %target-swift-frontend -typecheck %s -I %t -DCURRENT 2>&1 | %FileCheck -check-prefix=CURRENT %s
18+
// RUN: not %target-swift-frontend -typecheck %s -I %t -DFUTURE 2>&1 | %FileCheck -check-prefix=FUTURE %s
19+
20+
#if CURRENT
21+
import UnbuildableCurrent
22+
// CURRENT: unbuildable.swift:[[@LINE-1]]:8: error: failed to build module 'UnbuildableCurrent' from its module interface; it may have been damaged or it may have triggered a bug in the Swift compiler when it was produced
23+
#else
24+
import UnbuildableFuture
25+
// FUTURE: unbuildable.swift:[[@LINE-1]]:8: error: failed to build module 'UnbuildableFuture' from its module interface; the compiler that produced it, 'NeoTokyoSwift 2000.42', may have used features that aren't supported by this compiler, '{{.*Swift version.*}}'
26+
#endif
27+

validation-test/ParseableInterface/failing-overlay.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ import ImportsOverlay
55

66
// FIXME: It would be better if this had a useful location, like inside the
77
// C header that caused the importing of the overlay.
8-
// CHECK: <unknown>:0: error: failed to load module 'HasOverlay'
8+
// CHECK: <unknown>:0: error: failed to build module 'HasOverlay' from its module interface; the compiler that produced it, '(unspecified, file possibly handwritten)', may have used features that aren't supported by this compiler, '{{.*}}Swift version{{.*}}'

0 commit comments

Comments
 (0)