Skip to content

Commit d034eab

Browse files
committed
[Serialization] Show contextual notes on deserialization errors
This PR makes diagnostics on deserialization errors caused by project configuration more helpful by providing contextual information on the issue: - Path to the modules involved (up to 4 modules): the loaded swiftmodule with the broken outgoing reference, the path to the module where the decl was expected, the path to the underlying clang module, and the path to the module where the decl was found. This information should prevent us from having to ask for a different log with `-Rmodule-loading`. - Hint to delete the swiftmodule files when the module is library-evolution enabled. - Hint that clang settings affect clang modules involved in this scenario. - Pointing out when a decl moved between two modules with a similar name (where one name is a prefix of the other). This is a common issue when headers are shared between a clang framework's public and private modules. - Pointing out layering issues when an SDK module imports a local module. - Pointing out Swift language version mismatch which may lead to the compiler viewing the same clang decls differently when they are modified by APINotes files.
1 parent b1e0b89 commit d034eab

File tree

4 files changed

+183
-6
lines changed

4 files changed

+183
-6
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -873,19 +873,55 @@ ERROR(need_hermetic_seal_to_import_module,none,
873873
(Identifier))
874874

875875
ERROR(modularization_issue_decl_moved,Fatal,
876-
"reference to %select{top-level|type}0 %1 broken by a context change; "
876+
"reference to %select{top-level declaration|type}0 %1 broken by a context change; "
877877
"%1 was expected to be in %2, but now a candidate is found only in %3",
878878
(bool, DeclName, const ModuleDecl*, const ModuleDecl*))
879879
ERROR(modularization_issue_decl_type_changed,Fatal,
880-
"reference to %select{top-level|type}0 %1 broken by a context change; "
880+
"reference to %select{top-level declaration|type}0 %1 broken by a context change; "
881881
"the declaration kind of %1 %select{from %2|}5 changed since building '%3'"
882882
"%select{|, it was in %2 and is now a candidate is found only in %4}5",
883883
(bool, DeclName, const ModuleDecl*, StringRef, const ModuleDecl*, bool))
884884
ERROR(modularization_issue_decl_not_found,Fatal,
885-
"reference to %select{top-level|type}0 %1 broken by a context change; "
885+
"reference to %select{top-level declaration|type}0 %1 broken by a context change; "
886886
"%1 is not found, it was expected to be in %2",
887887
(bool, DeclName, const ModuleDecl*))
888888

889+
NOTE(modularization_issue_note_expected,none,
890+
"the %select{declaration|type}0 was expected to be found in module %1 at '%2'",
891+
(bool, const ModuleDecl*, StringRef))
892+
NOTE(modularization_issue_note_expected_underlying,none,
893+
"or expected to be found in the underlying module '%0' defined at '%1'",
894+
(StringRef, StringRef))
895+
NOTE(modularization_issue_note_found,none,
896+
"the %select{declaration|type}0 was actually found in module %1 at '%2'",
897+
(bool, const ModuleDecl*, StringRef))
898+
899+
NOTE(modularization_issue_stale_module,none,
900+
"the module %0 has enabled library-evolution; "
901+
"the following file may need to be deleted if the SDK was modified: '%1'",
902+
(const ModuleDecl *, StringRef))
903+
NOTE(modularization_issue_swift_version,none,
904+
"the module %0 was built with a Swift language version set to %1 "
905+
"while the current invocation uses %2; "
906+
"APINotes may change how clang declarations are imported",
907+
(const ModuleDecl *, llvm::VersionTuple, llvm::VersionTuple))
908+
NOTE(modularization_issue_audit_headers,none,
909+
"declarations in the %select{underlying|}0 clang module %1 "
910+
"may be hidden by clang project settings",
911+
(bool, const ModuleDecl*))
912+
NOTE(modularization_issue_layering_expected_local,none,
913+
"the distributed module %0 refers to the local module %1; "
914+
"this may be caused by clang project settings",
915+
(const ModuleDecl*, const ModuleDecl*))
916+
NOTE(modularization_issue_layering_found_local,none,
917+
"the reference may break layering; the candidate was found in "
918+
"the local module %1 for a reference from the distributed module %0",
919+
(const ModuleDecl*, const ModuleDecl*))
920+
NOTE(modularization_issue_related_modules,none,
921+
"the %select{declaration|type}0 %1 moved between related modules; "
922+
"clang project settings may affect headers shared between these modules",
923+
(bool, DeclName))
924+
889925
NOTE(modularization_issue_side_effect_extension_error,none,
890926
"could not deserialize extension",
891927
())

lib/Serialization/Deserialization.cpp

Lines changed: 100 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include "swift/ClangImporter/SwiftAbstractBasicReader.h"
4141
#include "swift/Serialization/SerializedModuleLoader.h"
4242
#include "clang/AST/DeclTemplate.h"
43+
#include "clang/Basic/SourceManager.h"
4344
#include "llvm/ADT/Statistic.h"
4445
#include "llvm/Support/Compiler.h"
4546
#include "llvm/Support/Debug.h"
@@ -185,7 +186,7 @@ SourceLoc ModuleFile::getSourceLoc() const {
185186
auto filename = getModuleFilename();
186187
auto bufferID = SourceMgr.getIDForBufferIdentifier(filename);
187188
if (!bufferID)
188-
bufferID = SourceMgr.addMemBufferCopy(StringRef(), filename);
189+
bufferID = SourceMgr.addMemBufferCopy("<binary format>", filename);
189190
return SourceMgr.getLocForBufferStart(*bufferID);
190191
}
191192

@@ -238,6 +239,103 @@ ModularizationError::diagnose(const ModuleFile *MF,
238239
// We could pass along the `path` information through notes.
239240
// However, for a top-level decl a path would just duplicate the
240241
// expected module name and the decl name from the diagnostic.
242+
243+
// Show context with relevant file paths.
244+
ctx.Diags.diagnose(SourceLoc(),
245+
diag::modularization_issue_note_expected,
246+
declIsType, expectedModule,
247+
expectedModule->getModuleSourceFilename());
248+
249+
const clang::Module *expectedUnderlying =
250+
expectedModule->findUnderlyingClangModule();
251+
if (!expectedModule->isNonSwiftModule() &&
252+
expectedUnderlying) {
253+
auto CML = ctx.getClangModuleLoader();
254+
auto &CSM = CML->getClangASTContext().getSourceManager();
255+
StringRef filename = CSM.getFilename(expectedUnderlying->DefinitionLoc);
256+
ctx.Diags.diagnose(SourceLoc(),
257+
diag::modularization_issue_note_expected_underlying,
258+
expectedUnderlying->Name,
259+
filename);
260+
}
261+
262+
if (foundModule)
263+
ctx.Diags.diagnose(SourceLoc(),
264+
diag::modularization_issue_note_found,
265+
declIsType, foundModule,
266+
foundModule->getModuleSourceFilename());
267+
268+
// A Swift language version mismatch could lead to a different set of rules
269+
// from APINotes files being applied when building the module vs when reading
270+
// from it.
271+
version::Version
272+
moduleLangVersion = referenceModule->getCompatibilityVersion(),
273+
clientLangVersion = MF->getContext().LangOpts.EffectiveLanguageVersion;
274+
ModuleDecl *referenceModuleDecl = referenceModule->getAssociatedModule();
275+
if (clientLangVersion != moduleLangVersion) {
276+
ctx.Diags.diagnose(SourceLoc(),
277+
diag::modularization_issue_swift_version,
278+
referenceModuleDecl, moduleLangVersion,
279+
clientLangVersion);
280+
}
281+
282+
// If the error is in a resilient swiftmodule adjacent to a swiftinterface,
283+
// deleting the module to rebuild from the swiftinterface may fix the issue.
284+
// Limit this suggestion to distributed Swift modules to not hint at
285+
// deleting local caches and such.
286+
bool referenceModuleIsDistributed = referenceModuleDecl &&
287+
referenceModuleDecl->isNonUserModule();
288+
if (referenceModule->getResilienceStrategy() ==
289+
ResilienceStrategy::Resilient &&
290+
referenceModuleIsDistributed) {
291+
ctx.Diags.diagnose(SourceLoc(),
292+
diag::modularization_issue_stale_module,
293+
referenceModuleDecl,
294+
referenceModule->getModuleFilename());
295+
}
296+
297+
// If the missing decl was expected to be in a clang module,
298+
// it may be hidden by some clang defined passed via `-Xcc` affecting how
299+
// headers are seen.
300+
if (expectedUnderlying) {
301+
ctx.Diags.diagnose(SourceLoc(),
302+
diag::modularization_issue_audit_headers,
303+
expectedModule->isNonSwiftModule(), expectedModule);
304+
}
305+
306+
// If the reference goes from a distributed module to a local module,
307+
// the compiler may have picked up an undesired module. We usually expect
308+
// distributed modules to only reference other distributed modules.
309+
// Local modules can reference both local modules and distributed modules.
310+
if (referenceModuleIsDistributed) {
311+
if (!expectedModule->isNonUserModule()) {
312+
ctx.Diags.diagnose(SourceLoc(),
313+
diag::modularization_issue_layering_expected_local,
314+
referenceModuleDecl, expectedModule);
315+
} else if (foundModule && !foundModule->isNonUserModule()) {
316+
ctx.Diags.diagnose(SourceLoc(),
317+
diag::modularization_issue_layering_found_local,
318+
referenceModuleDecl, foundModule);
319+
}
320+
}
321+
322+
// If a type moved between MyModule and MyModule_Private, it can be caused
323+
// by the use of `-Xcc -D` to change the API of the modules, leading to
324+
// decls moving between both modules.
325+
if (errorKind == Kind::DeclMoved ||
326+
errorKind == Kind::DeclKindChanged) {
327+
StringRef foundModuleName = foundModule->getName().str();
328+
StringRef expectedModuleName = expectedModule->getName().str();
329+
if (foundModuleName != expectedModuleName &&
330+
(foundModuleName.startswith(expectedModuleName) ||
331+
expectedModuleName.startswith(foundModuleName)) &&
332+
(expectedUnderlying ||
333+
expectedModule->findUnderlyingClangModule())) {
334+
ctx.Diags.diagnose(SourceLoc(),
335+
diag::modularization_issue_related_modules,
336+
declIsType, name);
337+
}
338+
}
241339
}
242340

243341
void TypeError::diagnose(const ModuleFile *MF) const {
@@ -2064,7 +2162,7 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) {
20642162
[&](const ModularizationError &modularError) {
20652163
modularError.diagnose(this, DiagnosticBehavior::Warning);
20662164
});
2067-
getContext().Diags.diagnose(getSourceLoc(),
2165+
getContext().Diags.diagnose(SourceLoc(),
20682166
diag::modularization_issue_worked_around);
20692167
} else {
20702168
return std::move(error);

test/Serialization/Recovery/module-recovery-remarks.swift

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,43 @@
1515

1616
/// Main error downgraded to a remark.
1717
// CHECK-MOVED: LibWithXRef.swiftmodule:1:1: remark: reference to type 'MyType' broken by a context change; 'MyType' was expected to be in 'A', but now a candidate is found only in 'A_related'
18+
19+
/// Contextual notes about the modules involved.
20+
// CHECK-MOVED: <unknown>:0: note: the type was expected to be found in module 'A' at '
21+
// CHECK-MOVED-SAME: A.swiftmodule'
22+
// CHECK-MOVED: <unknown>:0: note: or expected to be found in the underlying module 'A' defined at '
23+
// CHECK-MOVED-SAME: module.modulemap'
24+
// CHECK-MOVED: <unknown>:0: note: the type was actually found in module 'A_related' at '
25+
// CHECK-MOVED-SAME: A_related.swiftmodule'
26+
27+
/// More notes depending on the context
28+
// CHECK-MOVED: <unknown>:0: note: the module 'LibWithXRef' was built with a Swift language version set to 5
29+
// CHECK-MOVED-SAME: while the current invocation uses 4
30+
31+
// CHECK-MOVED: <unknown>:0: note: the module 'LibWithXRef' has enabled library-evolution; the following file may need to be deleted if the SDK was modified: '
32+
// CHECK-MOVED-SAME: LibWithXRef.swiftmodule'
33+
// CHECK-MOVED: <unknown>:0: note: declarations in the underlying clang module 'A' may be hidden by clang project settings
34+
// CHECK-MOVED: <unknown>:0: note: the distributed module 'LibWithXRef' refers to the local module 'A'; this may be caused by clang project settings
35+
// CHECK-MOVED: <unknown>:0: note: the type 'MyType' moved between related modules; clang project settings may affect headers shared between these modules
36+
// CHECK-MOVED: LibWithXRef.swiftmodule:1:1: note: could not deserialize type for 'foo()'
37+
// CHECK-MOVED: error: cannot find 'foo' in scope
38+
39+
/// Move A to the SDK, triggering a different note about layering.
40+
// RUN: mv %t/A.swiftmodule %t/sdk/A.swiftmodule
41+
// RUN: not %target-swift-frontend -c -O %t/Client.swift -I %t -I %t/sdk -Rmodule-recovery -sdk %t/sdk 2>&1 \
42+
// RUN: | %FileCheck --check-prefixes CHECK-LAYERING-FOUND %s
43+
// CHECK-LAYERING-FOUND: <unknown>:0: note: the reference may break layering; the candidate was found in the local module 'A_related' for a reference from the distributed module 'LibWithXRef'
44+
// CHECK-LAYERING-FOUND: error: cannot find 'foo' in scope
45+
46+
/// Delete A, keep only the underlying clangmodule for notes about clang modules.
47+
// RUN: rm %t/sdk/A.swiftmodule
48+
// RUN: %target-swift-frontend %t/Empty.swift -emit-module-path %t/A_related.swiftmodule -module-name A_related
49+
// RUN: not %target-swift-frontend -c -O %t/Client.swift -I %t -I %t/sdk -Rmodule-recovery -sdk %t/sdk 2>&1 \
50+
// RUN: | %FileCheck --check-prefixes CHECK-CLANG %s
51+
// CHECK-CLANG: <unknown>:0: note: declarations in the clang module 'A' may be hidden by clang project settings
52+
// CHECK-CLANG: error: cannot find 'foo' in scope
53+
54+
1855
//--- module.modulemap
1956
module A {
2057
header "A.h"

test/Serialization/modularization-error.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,13 @@
1919
// RUN: -experimental-force-workaround-broken-modules 2>&1 \
2020
// RUN: | %FileCheck --check-prefixes CHECK-WORKAROUND %s
2121
// CHECK-WORKAROUND: LibWithXRef.swiftmodule:1:1: warning: reference to type 'MyType' broken by a context change; 'MyType' was expected to be in 'A', but now a candidate is found only in 'B'
22-
// CHECK-WORKAROUND: LibWithXRef.swiftmodule:1:1: note: attempting forced recovery enabled by -experimental-force-workaround-broken-modules
22+
// CHECK-WORKAROUND-NEXT: A.MyType
23+
// CHECK-WORKAROUND-NEXT: ^
24+
// CHECK-WORKAROUND: <unknown>:0: note: the type was expected to be found in module 'A' at '
25+
// CHECK-WORKAROUND-SAME: /A.swiftmodule'
26+
// CHECK-WORKAROUND: <unknown>:0: note: the type was actually found in module 'B' at '
27+
// CHECK-WORKAROUND-SAME: B.swiftmodule'
28+
// CHECK-WORKAROUND: <unknown>:0: note: attempting forced recovery enabled by -experimental-force-workaround-broken-modules
2329
// CHECK-WORKAROUND: func foo() -> some Proto
2430

2531
/// Change MyType into a function.

0 commit comments

Comments
 (0)