Skip to content

Commit 31c6cac

Browse files
authored
Merge pull request #11661 from jrose-apple/4.0-lazy-signature-deserialization
[4.0] [Serialization] Make requirement signature conformance loading lazy.
2 parents 9ce791a + b38500b commit 31c6cac

File tree

3 files changed

+84
-57
lines changed

3 files changed

+84
-57
lines changed

lib/Serialization/Deserialization.cpp

Lines changed: 53 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -647,59 +647,6 @@ NormalProtocolConformance *ModuleFile::readNormalConformance(
647647
dc->getAsNominalTypeOrNominalTypeExtensionContext()
648648
->registerProtocolConformance(conformance);
649649

650-
// Read requirement signature conformances.
651-
SmallVector<ProtocolConformanceRef, 4> reqConformances;
652-
653-
if (proto->isObjC() && getContext().LangOpts.EnableDeserializationRecovery) {
654-
// Don't crash if inherited protocols are added or removed.
655-
// This is limited to Objective-C protocols because we know their only
656-
// conformance requirements are on Self. This isn't actually a /safe/ change
657-
// even in Objective-C, but we mostly just don't want to crash.
658-
659-
// FIXME: DenseMap requires that its value type be default-constructible,
660-
// which ProtocolConformanceRef is not, hence the extra Optional.
661-
llvm::SmallDenseMap<ProtocolDecl *, Optional<ProtocolConformanceRef>, 16>
662-
conformancesForProtocols;
663-
while (conformanceCount--) {
664-
ProtocolConformanceRef nextConformance = readConformance(DeclTypeCursor);
665-
ProtocolDecl *confProto = nextConformance.getRequirement();
666-
conformancesForProtocols[confProto] = nextConformance;
667-
}
668-
669-
for (const auto &req : proto->getRequirementSignature()) {
670-
if (req.getKind() != RequirementKind::Conformance)
671-
continue;
672-
ProtocolDecl *proto =
673-
req.getSecondType()->castTo<ProtocolType>()->getDecl();
674-
auto iter = conformancesForProtocols.find(proto);
675-
if (iter != conformancesForProtocols.end()) {
676-
reqConformances.push_back(iter->getSecond().getValue());
677-
} else {
678-
// Put in an abstract conformance as a placeholder. This is a lie, but
679-
// there's not much better we can do. We're relying on the fact that
680-
// the rest of the compiler doesn't actually need to check the
681-
// conformance to an Objective-C protocol for anything important.
682-
// There are no associated types and we don't emit a Swift conformance
683-
// record.
684-
reqConformances.push_back(ProtocolConformanceRef(proto));
685-
}
686-
}
687-
688-
} else {
689-
auto isConformanceReq = [](const Requirement &req) {
690-
return req.getKind() == RequirementKind::Conformance;
691-
};
692-
if (conformanceCount != llvm::count_if(proto->getRequirementSignature(),
693-
isConformanceReq)) {
694-
fatal(llvm::make_error<llvm::StringError>(
695-
"serialized conformances do not match requirement signature",
696-
llvm::inconvertibleErrorCode()));
697-
}
698-
while (conformanceCount--)
699-
reqConformances.push_back(readConformance(DeclTypeCursor));
700-
}
701-
conformance->setSignatureConformances(reqConformances);
702-
703650
// If the conformance is complete, we're done.
704651
if (conformance->isComplete())
705652
return conformance;
@@ -4633,10 +4580,59 @@ void ModuleFile::finishNormalConformance(NormalProtocolConformance *conformance,
46334580
typeCount, conformanceCount,
46344581
rawIDs);
46354582

4636-
// Skip requirement signature conformances.
4637-
auto proto = conformance->getProtocol();
4638-
while (conformanceCount--)
4639-
(void)readConformance(DeclTypeCursor);
4583+
// Read requirement signature conformances.
4584+
const ProtocolDecl *proto = conformance->getProtocol();
4585+
SmallVector<ProtocolConformanceRef, 4> reqConformances;
4586+
4587+
if (proto->isObjC() && getContext().LangOpts.EnableDeserializationRecovery) {
4588+
// Don't crash if inherited protocols are added or removed.
4589+
// This is limited to Objective-C protocols because we know their only
4590+
// conformance requirements are on Self. This isn't actually a /safe/ change
4591+
// even in Objective-C, but we mostly just don't want to crash.
4592+
4593+
// FIXME: DenseMap requires that its value type be default-constructible,
4594+
// which ProtocolConformanceRef is not, hence the extra Optional.
4595+
llvm::SmallDenseMap<ProtocolDecl *, Optional<ProtocolConformanceRef>, 16>
4596+
conformancesForProtocols;
4597+
while (conformanceCount--) {
4598+
ProtocolConformanceRef nextConformance = readConformance(DeclTypeCursor);
4599+
ProtocolDecl *confProto = nextConformance.getRequirement();
4600+
conformancesForProtocols[confProto] = nextConformance;
4601+
}
4602+
4603+
for (const auto &req : proto->getRequirementSignature()) {
4604+
if (req.getKind() != RequirementKind::Conformance)
4605+
continue;
4606+
ProtocolDecl *proto =
4607+
req.getSecondType()->castTo<ProtocolType>()->getDecl();
4608+
auto iter = conformancesForProtocols.find(proto);
4609+
if (iter != conformancesForProtocols.end()) {
4610+
reqConformances.push_back(iter->getSecond().getValue());
4611+
} else {
4612+
// Put in an abstract conformance as a placeholder. This is a lie, but
4613+
// there's not much better we can do. We're relying on the fact that
4614+
// the rest of the compiler doesn't actually need to check the
4615+
// conformance to an Objective-C protocol for anything important.
4616+
// There are no associated types and we don't emit a Swift conformance
4617+
// record.
4618+
reqConformances.push_back(ProtocolConformanceRef(proto));
4619+
}
4620+
}
4621+
4622+
} else {
4623+
auto isConformanceReq = [](const Requirement &req) {
4624+
return req.getKind() == RequirementKind::Conformance;
4625+
};
4626+
if (conformanceCount != llvm::count_if(proto->getRequirementSignature(),
4627+
isConformanceReq)) {
4628+
fatal(llvm::make_error<llvm::StringError>(
4629+
"serialized conformances do not match requirement signature",
4630+
llvm::inconvertibleErrorCode()));
4631+
}
4632+
while (conformanceCount--)
4633+
reqConformances.push_back(readConformance(DeclTypeCursor));
4634+
}
4635+
conformance->setSignatureConformances(reqConformances);
46404636

46414637
ArrayRef<uint64_t>::iterator rawIDIter = rawIDs.begin();
46424638

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
protocol FooBaseProto {}
2+
3+
protocol FooProto: FooBaseProto {}
4+
5+
protocol BarProto {
6+
associatedtype Foo: FooProto
7+
init(foo: Foo)
8+
}
9+
10+
protocol BazProto {
11+
associatedtype Bar: BarProto
12+
init(bar: Bar)
13+
}
14+
15+
struct BarImpl: BarProto {
16+
init(foo: FooImpl) {}
17+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift -module-name SwiftCrash -emit-module -o %t/SR5191.swiftmodule %s %S/Inputs/SR5191-other.swift
3+
// RUN: %target-build-swift -module-name SwiftCrash -emit-module -o %t/SR5191_reversed.swiftmodule %S/Inputs/SR5191-other.swift %s
4+
5+
// REQUIRES: objc_interop
6+
// The module name is significant here; it must be later ASCIIbetically than
7+
// "Swift". This has to do with the canonical ordering of protocols, including
8+
// those inherited by extending NSObject.
9+
10+
import Foundation
11+
12+
class FooImpl: NSObject, FooProto, BazProto {
13+
required init(bar: BarImpl) {}
14+
}

0 commit comments

Comments
 (0)