Skip to content

Commit 4e0038f

Browse files
committed
[Serialization] Recover from conformances with missing decls
Components of a requirement may be hidden behind an implementation-only import. Attempts at deserializing them would fail on a 'module not loaded' error. We only see failures in non-compilation paths, either in indexing or with tools like ide-test as they try to deserialize things that are private.
1 parent ea456f7 commit 4e0038f

File tree

3 files changed

+47
-13
lines changed

3 files changed

+47
-13
lines changed

lib/Serialization/Deserialization.cpp

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -571,7 +571,11 @@ ModuleFile::readConformanceChecked(llvm::BitstreamCursor &Cursor,
571571
case NORMAL_PROTOCOL_CONFORMANCE_ID: {
572572
NormalConformanceID conformanceID;
573573
NormalProtocolConformanceIdLayout::readRecord(scratch, conformanceID);
574-
return ProtocolConformanceRef(readNormalConformance(conformanceID));
574+
575+
auto conformance = readNormalConformanceChecked(conformanceID);
576+
if (!conformance)
577+
return conformance.takeError();
578+
return ProtocolConformanceRef(conformance.get());
575579
}
576580

577581
case PROTOCOL_CONFORMANCE_XREF: {
@@ -614,7 +618,7 @@ ModuleFile::readConformanceChecked(llvm::BitstreamCursor &Cursor,
614618
}
615619
}
616620

617-
NormalProtocolConformance *ModuleFile::readNormalConformance(
621+
Expected<NormalProtocolConformance *> ModuleFile::readNormalConformanceChecked(
618622
NormalConformanceID conformanceID) {
619623
auto &conformanceEntry = NormalConformances[conformanceID-1];
620624
if (conformanceEntry.isComplete()) {
@@ -653,7 +657,11 @@ NormalProtocolConformance *ModuleFile::readNormalConformance(
653657
Type conformingType = dc->getDeclaredInterfaceType();
654658
PrettyStackTraceType trace(ctx, "reading conformance for", conformingType);
655659

656-
auto proto = cast<ProtocolDecl>(getDecl(protoID));
660+
auto protoOrError = getDeclChecked(protoID);
661+
if (!protoOrError)
662+
return protoOrError.takeError();
663+
auto proto = cast<ProtocolDecl>(protoOrError.get());
664+
657665
PrettyStackTraceDecl traceTo("... to", proto);
658666
++NumNormalProtocolConformancesLoaded;
659667

@@ -5761,9 +5769,22 @@ ModuleFile::loadAllConformances(const Decl *D, uint64_t contextData,
57615769
fatalIfNotSuccess(DeclTypeCursor.JumpToBit(bitPosition));
57625770

57635771
while (numConformances--) {
5764-
auto conf = readConformance(DeclTypeCursor);
5765-
if (conf.isConcrete())
5766-
conformances.push_back(conf.getConcrete());
5772+
auto conformance = readConformanceChecked(DeclTypeCursor);
5773+
5774+
if (!conformance) {
5775+
// Missing module errors are most likely caused by an
5776+
// implementation-only import hiding types and decls.
5777+
// rdar://problem/60291019
5778+
if (conformance.errorIsA<XRefNonLoadedModuleError>()) {
5779+
consumeError(conformance.takeError());
5780+
return;
5781+
}
5782+
else
5783+
fatal(conformance.takeError());
5784+
}
5785+
5786+
if (conformance.get().isConcrete())
5787+
conformances.push_back(conformance.get().getConcrete());
57675788
}
57685789
}
57695790

lib/Serialization/ModuleFile.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -978,13 +978,14 @@ class ModuleFile
978978
llvm::Expected<ProtocolConformanceRef>
979979
readConformanceChecked(llvm::BitstreamCursor &Cursor,
980980
GenericEnvironment *genericEnv = nullptr);
981-
981+
982982
/// Read a SILLayout from the given cursor.
983983
SILLayout *readSILLayout(llvm::BitstreamCursor &Cursor);
984984

985-
/// Read the given normal conformance from the current module file.
986-
NormalProtocolConformance *
987-
readNormalConformance(serialization::NormalConformanceID id);
985+
/// Read the given normal conformance from the current module file,
986+
/// returns the conformance or the first error.
987+
llvm::Expected<NormalProtocolConformance *>
988+
readNormalConformanceChecked(serialization::NormalConformanceID id);
988989

989990
/// Reads a foreign error conformance from \c DeclTypeCursor, if present.
990991
Optional<ForeignErrorConvention> maybeReadForeignErrorConvention();

test/Serialization/Recovery/implementation-only-missing.swift

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
// RUN: %target-swift-frontend -typecheck -DCLIENT_APP -primary-file %s -I %t -index-system-modules -index-store-path %t
1616
// RUN: %target-swift-frontend -emit-sil -DCLIENT_APP -primary-file %s -I %t -module-name client
1717

18+
//// Printing the public module should not crash when checking for overrides of
19+
//// methods from the private module.
20+
// RUN: %target-swift-ide-test -print-module -module-to-print=public_lib -source-filename=x -skip-overrides -I %t
21+
1822
#if PRIVATE_LIB
1923

2024
@propertyWrapper
@@ -38,11 +42,15 @@ public protocol HiddenProtocol {
3842
associatedtype Value
3943
}
4044

45+
public protocol HiddenProtocolWithOverride {
46+
func hiddenOverride()
47+
}
48+
4149
#elseif PUBLIC_LIB
4250

4351
@_implementationOnly import private_lib
4452

45-
struct LibProtocolContraint { }
53+
struct LibProtocolConstraint { }
4654

4755
protocol LibProtocolTABound { }
4856
struct LibProtocolTA: LibProtocolTABound { }
@@ -52,13 +60,13 @@ protocol LibProtocol {
5260

5361
func hiddenRequirement<A>(
5462
param: HiddenGenStruct<A>
55-
) where A.Value == LibProtocolContraint
63+
) where A.Value == LibProtocolConstraint
5664
}
5765

5866
extension LibProtocol where TA == LibProtocolTA {
5967
func hiddenRequirement<A>(
6068
param: HiddenGenStruct<A>
61-
) where A.Value == LibProtocolContraint { }
69+
) where A.Value == LibProtocolConstraint { }
6270
}
6371

6472
public struct PublicStruct: LibProtocol {
@@ -70,6 +78,10 @@ public struct PublicStruct: LibProtocol {
7078
public var wrappedVar: String
7179
}
7280

81+
struct StructWithOverride: HiddenProtocolWithOverride {
82+
func hiddenOverride() {}
83+
}
84+
7385
#elseif CLIENT_APP
7486

7587
import public_lib

0 commit comments

Comments
 (0)