Skip to content

Commit fb3944f

Browse files
committed
Serialization: Error on mismatch between requirements and confirmances
When deserialization a protocol conformance from a binary swiftmodule file the compiler can encounter inconsistencies caused by stale module files. Replace the hard crash with a proper error and print the list of requirements and conformances being compared to stderr for manual inspection. Recover silently when we can afford to, during indexing or in LLDB.
1 parent ea5658e commit fb3944f

File tree

3 files changed

+74
-6
lines changed

3 files changed

+74
-6
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1043,6 +1043,12 @@ ERROR(modularization_issue_conformance_xref_error,none,
10431043
NOTE(modularization_issue_conformance_xref_note,none,
10441044
"Breaks conformances of '%0' to %1",
10451045
(StringRef, DeclName))
1046+
ERROR(modularization_issue_conformance_error,none,
1047+
"Conformances of '%0' "
1048+
"do not match requirement signature of %1; "
1049+
"%2 conformances for %3 requirements",
1050+
(StringRef, DeclName, unsigned int, unsigned int))
1051+
10461052
ERROR(reserved_member_name,none,
10471053
"type member must not be named %0, since it would conflict with the"
10481054
" 'foo.%1' expression", (const ValueDecl *, StringRef))

lib/Serialization/Deserialization.cpp

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8739,12 +8739,39 @@ void ModuleFile::finishNormalConformance(NormalProtocolConformance *conformance,
87398739
return req.getKind() == RequirementKind::Conformance;
87408740
};
87418741
auto requirements = proto->getRequirementSignature().getRequirements();
8742-
if (!allowCompilerErrors() &&
8743-
conformanceCount != llvm::count_if(requirements,
8744-
isConformanceReq)) {
8745-
fatal(llvm::make_error<llvm::StringError>(
8746-
"serialized conformances do not match requirement signature",
8747-
llvm::inconvertibleErrorCode()));
8742+
unsigned int conformanceRequirementCount =
8743+
llvm::count_if(requirements, isConformanceReq);
8744+
if (conformanceCount != conformanceRequirementCount) {
8745+
// Mismatch between the number of loaded conformances and the expected
8746+
// requirements. One or the other likely comes from a stale module.
8747+
8748+
if (!enableExtendedDeserializationRecovery()) {
8749+
// Error and print full context for visual inspection.
8750+
ASTContext &ctx = getContext();
8751+
std::string typeStr = conformance->getType()->getString();
8752+
ctx.Diags.diagnose(getSourceLoc(),
8753+
diag::modularization_issue_conformance_error,
8754+
typeStr, proto->getName(), conformanceCount,
8755+
conformanceRequirementCount);
8756+
ctx.Diags.flushConsumers();
8757+
8758+
// Print context to stderr.
8759+
PrintOptions Opts;
8760+
llvm::errs() << "Requirements:\n";
8761+
for (auto req: requirements) {
8762+
req.print(llvm::errs(), Opts);
8763+
llvm::errs() << "\n";
8764+
}
8765+
8766+
llvm::errs() << "Conformances:\n";
8767+
for (auto req: reqConformances) {
8768+
req.print(llvm::errs());
8769+
llvm::errs() << "\n";
8770+
}
8771+
}
8772+
8773+
conformance->setInvalid();
8774+
return;
87488775
}
87498776
}
87508777

test/Serialization/Recovery/conformance-xref.swift

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,29 @@
2222
// RUN: %target-swift-frontend -typecheck %t/Client.swift -I %t \
2323
// RUN: -experimental-allow-module-with-compiler-errors -verify
2424

25+
/// Case 2: Remove a requirement from one lib, leave the other as stale and
26+
/// rebuild client. Error on the mismatch.
27+
// RUN: %target-swift-frontend -emit-module %t/ChangingLib.swift -I %t \
28+
// RUN: -emit-module-path %t/ChangingLib.swiftmodule \
29+
// RUN: -D DROP_REQUIREMENT
30+
// RUN: not %target-swift-frontend -typecheck %t/Client.swift -I %t \
31+
// RUN: 2> %t/errors.log
32+
// RUN: %FileCheck %s --input-file=%t/errors.log \
33+
// RUN: --check-prefix CHECK-REMARK-REQUIREMENT
34+
35+
/// No errors when extended recovery is enabled.
36+
// RUN: %target-swift-frontend -typecheck %t/Client.swift -I %t \
37+
// RUN: -experimental-allow-module-with-compiler-errors -verify
38+
39+
/// Combined case: Remove both the conformance and the requirement.
40+
/// Fail on the removed conformance only as it was first.
41+
// RUN: %target-swift-frontend -emit-module %t/ChangingLib.swift -I %t \
42+
// RUN: -emit-module-path %t/ChangingLib.swiftmodule \
43+
// RUN: -D DROP_CONFORMANCE -D DROP_REQUIREMENT
44+
// RUN: not %target-swift-frontend -typecheck %t/Client.swift -I %t \
45+
// RUN: 2> %t/errors.log
46+
// RUN: %FileCheck %s --input-file=%t/errors.log \
47+
// RUN: --check-prefixes CHECK-REMARK-CONFORMANCE
2548

2649
//--- ChangingLib.swift
2750

@@ -37,7 +60,19 @@ extension Counter: SimpleProto {}
3760
#endif
3861

3962
public protocol ProtoUser {
63+
associatedtype Element
64+
#if DROP_REQUIREMENT
65+
// CHECK-REMARK-REQUIREMENT: MiddleLib.swiftmodule:1:1: error: Conformances of 'OneToAThousand' do not match requirement signature of 'ProtoUser'; 5 conformances for 6 requirements
66+
// CHECK-REMARK-REQUIREMENT: Requirements:
67+
// Skipping implicits.
68+
// CHECK-REMARK-REQUIREMENT: Conformances:
69+
// Skipping implicits.
70+
// CHECK-REMARK-REQUIREMENT: (specialized_conformance type="OneToAThousand.Impl" protocol="SimpleProto"
71+
// CHECK-REMARK-REQUIREMENT: (normal_conformance type="Counter<T>" protocol="SimpleProto" lazy))
72+
associatedtype Impl
73+
#else
4074
associatedtype Impl: SimpleProto
75+
#endif
4176

4277
var start: Impl { get }
4378

0 commit comments

Comments
 (0)