Skip to content

Commit cef6356

Browse files
committed
Serialization: report type mismatch when it causes a deserialization failure
Deserialization may fail if a decl in a dependency changed type between the time a swiftmodule was built and when it was imported. This can happen because of changes to the SDK or use of C preprocessor macros. To help understand these problems, note the specific types causing the mismatch when it leads to a deserialization failure. ``` .../LibWithXRef.swiftmodule:1:1: error: reference to top-level declaration 'foo' broken by a context change; the declaration kind of 'foo' from 'A' changed since building 'LibWithXRef' 1 │ A.foo │ │ ├─ ... │ ├─ note: a candidate was filtered out because of a type mismatch; expected: '() -> ()', found: '(Int) -> Float' ```
1 parent 59e0af7 commit cef6356

File tree

4 files changed

+78
-5
lines changed

4 files changed

+78
-5
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -954,6 +954,10 @@ NOTE(modularization_issue_stale_module,none,
954954
"the module %0 has enabled library-evolution; "
955955
"the following file may need to be deleted if the SDK was modified: '%1'",
956956
(const ModuleDecl *, StringRef))
957+
NOTE(modularization_issue_type_mismatch,none,
958+
"a candidate was filtered out because of a type mismatch; "
959+
"expected: %0, found: %1",
960+
(const Type, const Type))
957961
NOTE(modularization_issue_swift_version,none,
958962
"the module %0 was built with a Swift language version set to %1 "
959963
"while the current invocation uses %2; "

lib/Serialization/Deserialization.cpp

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,12 @@ ModularizationError::diagnose(const ModuleFile *MF,
270270
declIsType, foundModule,
271271
foundModule->getModuleSourceFilename());
272272

273+
if (mismatchingTypes.has_value()) {
274+
ctx.Diags.diagnose(loc,
275+
diag::modularization_issue_type_mismatch,
276+
mismatchingTypes->first, mismatchingTypes->second);
277+
}
278+
273279
// A Swift language version mismatch could lead to a different set of rules
274280
// from APINotes files being applied when building the module vs when reading
275281
// from it.
@@ -2145,6 +2151,7 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) {
21452151

21462152
auto errorKind = ModularizationError::Kind::DeclNotFound;
21472153
ModuleDecl *foundIn = nullptr;
2154+
std::optional<std::pair<Type, Type>> mismatchingTypes;
21482155
bool isType = false;
21492156

21502157
if (recordID == XREF_TYPE_PATH_PIECE ||
@@ -2188,7 +2195,10 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) {
21882195
values);
21892196
}
21902197

2191-
bool hadAMatchBeforeFiltering = !values.empty();
2198+
std::optional<ValueDecl*> matchBeforeFiltering = std::nullopt;
2199+
if (!values.empty()) {
2200+
matchBeforeFiltering = values[0];
2201+
}
21922202
filterValues(filterTy, nullptr, nullptr, isType, inProtocolExt,
21932203
importedFromClang, isStatic, std::nullopt, values);
21942204

@@ -2200,13 +2210,21 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) {
22002210
errorKind = ModularizationError::Kind::DeclMoved;
22012211
foundIn = otherModule;
22022212
break;
2203-
} else if (hadAMatchBeforeFiltering) {
2213+
} else if (matchBeforeFiltering.has_value()) {
22042214
// Found a match that was filtered out. This may be from the same
22052215
// expected module if there's a type difference. This can be caused
22062216
// by the use of different Swift language versions between a library
22072217
// with serialized SIL and a client.
22082218
errorKind = ModularizationError::Kind::DeclKindChanged;
22092219
foundIn = otherModule;
2220+
2221+
if (filterTy) {
2222+
auto expectedTy = filterTy->getCanonicalType();
2223+
auto foundTy = (*matchBeforeFiltering)->getInterfaceType();
2224+
if (expectedTy && foundTy && !expectedTy->isEqual(foundTy))
2225+
mismatchingTypes = std::make_pair(expectedTy, foundTy);
2226+
}
2227+
22102228
break;
22112229
}
22122230
}
@@ -2219,7 +2237,8 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) {
22192237
baseModule,
22202238
this,
22212239
foundIn,
2222-
pathTrace);
2240+
pathTrace,
2241+
mismatchingTypes);
22232242

22242243
// If we want to workaround broken modularization, we can keep going if
22252244
// we found a matching top-level decl in a different module. This is

lib/Serialization/DeserializationErrors.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -367,16 +367,21 @@ class ModularizationError : public llvm::ErrorInfo<ModularizationError> {
367367

368368
XRefTracePath path;
369369

370+
/// Expected vs found type if the mismatch caused a decl to be rejected.
371+
std::optional<std::pair<Type, Type>> mismatchingTypes;
372+
370373
public:
371374
explicit ModularizationError(DeclName name, bool declIsType, Kind errorKind,
372375
const ModuleDecl *expectedModule,
373376
const ModuleFile *referenceModule,
374377
const ModuleDecl *foundModule,
375-
XRefTracePath path):
378+
XRefTracePath path,
379+
std::optional<std::pair<Type, Type>> mismatchingTypes):
376380
name(name), declIsType(declIsType), errorKind(errorKind),
377381
expectedModule(expectedModule),
378382
referenceModule(referenceModule),
379-
foundModule(foundModule), path(path) {}
383+
foundModule(foundModule), path(path),
384+
mismatchingTypes(mismatchingTypes) {}
380385

381386
void diagnose(const ModuleFile *MF,
382387
DiagnosticBehavior limit = DiagnosticBehavior::Fatal) const;
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: split-file %s %t
3+
4+
/// Compile two libraries A and LibWithXRef.
5+
// RUN: %target-swift-frontend -emit-module %t/LibWithXRef.swift -I %t \
6+
// RUN: -module-name LibWithXRef -o %t/LibWithXRef.swiftmodule \
7+
// RUN: -swift-version 5
8+
// RUN: %target-swift-frontend -c -O %t/Client.swift -I %t \
9+
// RUN: -validate-tbd-against-ir=none -swift-version 5
10+
11+
// Replace headers, changing the type of `foo`.
12+
// RUN: mv %t/A_DifferentAPI.h %t/A.h
13+
// RUN: not --crash %target-swift-frontend -c -O %t/Client.swift -I %t \
14+
// RUN: -validate-tbd-against-ir=none -swift-version 5 2>&1 \
15+
// RUN: | %FileCheck %s
16+
// CHECK: error: reference to top-level declaration 'foo' broken by a context change; the declaration kind of 'foo' from 'A' changed since building 'LibWithXRef'
17+
// CHECK: note: the declaration was expected to be found in module 'A' at '{{.*}}module.modulemap'
18+
// CHECK: note: the declaration was actually found in module 'A' at '{{.*}}module.modulemap'
19+
// CHECK: note: a candidate was filtered out because of a type mismatch; expected: '() -> ()', found: '() -> Float'
20+
21+
//--- module.modulemap
22+
module A {
23+
header "A.h"
24+
}
25+
26+
//--- A.h
27+
void foo() {}
28+
29+
//--- A_DifferentAPI.h
30+
float foo() {
31+
return 1.2;
32+
}
33+
34+
//--- LibWithXRef.swift
35+
import A
36+
37+
@inlinable
38+
public func bar() {
39+
foo()
40+
}
41+
42+
//--- Client.swift
43+
import LibWithXRef
44+
45+
bar()

0 commit comments

Comments
 (0)