mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[Serialization] Recover from ObjC protocols changing inheritance. (#11109)
When there's an Objective-C protocol that adopts other protocols, the other protocols become part of the requirement signature. If that can change, Swift conformances to that protocol will get very confused when it comes time to deserialize the conformances that satisfy the requirement signature. To recover from this, just deserialize /all/ trailing conformances, rather than follow the requirement signature, and match them up after the fact. (This only works for Objective-C protocols where we know all conformance requirements represent inherited protocols, as opposed to constraints on associated types.) rdar://problem/33356098
This commit is contained in:
@@ -54,7 +54,7 @@ const uint16_t VERSION_MAJOR = 0;
|
||||
/// in source control, you should also update the comment to briefly
|
||||
/// describe what change you made. The content of this comment isn't important;
|
||||
/// it just ensures a conflict if two people change the module format.
|
||||
const uint16_t VERSION_MINOR = 352; // Last change: 'shared' type attribute
|
||||
const uint16_t VERSION_MINOR = 353; // Last change: count inherited conformances
|
||||
|
||||
using DeclID = PointerEmbeddedInt<unsigned, 31>;
|
||||
using DeclIDField = BCFixed<31>;
|
||||
@@ -1170,10 +1170,11 @@ namespace decls_block {
|
||||
DeclContextIDField, // the decl that provided this conformance
|
||||
BCVBR<5>, // value mapping count
|
||||
BCVBR<5>, // type mapping count
|
||||
BCVBR<5>, // requirement signature conformance count
|
||||
BCArray<DeclIDField>
|
||||
// The array contains archetype-value pairs, then type declarations.
|
||||
// Inherited conformances follow, then the substitution records for the
|
||||
// associated types.
|
||||
// Requirement signature conformances follow, then the substitution records
|
||||
// for the associated types.
|
||||
>;
|
||||
|
||||
using SpecializedProtocolConformanceLayout = BCRecordLayout<
|
||||
|
||||
@@ -611,7 +611,7 @@ NormalProtocolConformance *ModuleFile::readNormalConformance(
|
||||
|
||||
DeclID protoID;
|
||||
DeclContextID contextID;
|
||||
unsigned valueCount, typeCount;
|
||||
unsigned valueCount, typeCount, conformanceCount;
|
||||
ArrayRef<uint64_t> rawIDs;
|
||||
SmallVector<uint64_t, 16> scratch;
|
||||
|
||||
@@ -622,7 +622,7 @@ NormalProtocolConformance *ModuleFile::readNormalConformance(
|
||||
}
|
||||
NormalProtocolConformanceLayout::readRecord(scratch, protoID,
|
||||
contextID, valueCount,
|
||||
typeCount,
|
||||
typeCount, conformanceCount,
|
||||
rawIDs);
|
||||
|
||||
ASTContext &ctx = getContext();
|
||||
@@ -4585,7 +4585,7 @@ void ModuleFile::finishNormalConformance(NormalProtocolConformance *conformance,
|
||||
|
||||
DeclID protoID;
|
||||
DeclContextID contextID;
|
||||
unsigned valueCount, typeCount;
|
||||
unsigned valueCount, typeCount, conformanceCount;
|
||||
ArrayRef<uint64_t> rawIDs;
|
||||
SmallVector<uint64_t, 16> scratch;
|
||||
|
||||
@@ -4595,17 +4595,60 @@ void ModuleFile::finishNormalConformance(NormalProtocolConformance *conformance,
|
||||
"registered lazy loader incorrectly");
|
||||
NormalProtocolConformanceLayout::readRecord(scratch, protoID,
|
||||
contextID, valueCount,
|
||||
typeCount,
|
||||
typeCount, conformanceCount,
|
||||
rawIDs);
|
||||
|
||||
// Read requirement signature conformances.
|
||||
const ProtocolDecl *proto = conformance->getProtocol();
|
||||
SmallVector<ProtocolConformanceRef, 4> reqConformances;
|
||||
for (const auto &req : proto->getRequirementSignature()) {
|
||||
if (req.getKind() == RequirementKind::Conformance) {
|
||||
auto reqConformance = readConformance(DeclTypeCursor);
|
||||
reqConformances.push_back(reqConformance);
|
||||
|
||||
if (proto->isObjC() && getContext().LangOpts.EnableDeserializationRecovery) {
|
||||
// Don't crash if inherited protocols are added or removed.
|
||||
// This is limited to Objective-C protocols because we know their only
|
||||
// conformance requirements are on Self. This isn't actually a /safe/ change
|
||||
// even in Objective-C, but we mostly just don't want to crash.
|
||||
|
||||
// FIXME: DenseMap requires that its value type be default-constructible,
|
||||
// which ProtocolConformanceRef is not, hence the extra Optional.
|
||||
llvm::SmallDenseMap<ProtocolDecl *, Optional<ProtocolConformanceRef>, 16>
|
||||
conformancesForProtocols;
|
||||
while (conformanceCount--) {
|
||||
ProtocolConformanceRef nextConformance = readConformance(DeclTypeCursor);
|
||||
ProtocolDecl *confProto = nextConformance.getRequirement();
|
||||
conformancesForProtocols[confProto] = nextConformance;
|
||||
}
|
||||
|
||||
for (const auto &req : proto->getRequirementSignature()) {
|
||||
if (req.getKind() != RequirementKind::Conformance)
|
||||
continue;
|
||||
ProtocolDecl *proto =
|
||||
req.getSecondType()->castTo<ProtocolType>()->getDecl();
|
||||
auto iter = conformancesForProtocols.find(proto);
|
||||
if (iter != conformancesForProtocols.end()) {
|
||||
reqConformances.push_back(iter->getSecond().getValue());
|
||||
} else {
|
||||
// Put in an abstract conformance as a placeholder. This is a lie, but
|
||||
// there's not much better we can do. We're relying on the fact that
|
||||
// the rest of the compiler doesn't actually need to check the
|
||||
// conformance to an Objective-C protocol for anything important.
|
||||
// There are no associated types and we don't emit a Swift conformance
|
||||
// record.
|
||||
reqConformances.push_back(ProtocolConformanceRef(proto));
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
auto isConformanceReq = [](const Requirement &req) {
|
||||
return req.getKind() == RequirementKind::Conformance;
|
||||
};
|
||||
if (conformanceCount != llvm::count_if(proto->getRequirementSignature(),
|
||||
isConformanceReq)) {
|
||||
fatal(llvm::make_error<llvm::StringError>(
|
||||
"serialized conformances do not match requirement signature",
|
||||
llvm::inconvertibleErrorCode()));
|
||||
}
|
||||
while (conformanceCount--)
|
||||
reqConformances.push_back(readConformance(DeclTypeCursor));
|
||||
}
|
||||
conformance->setSignatureConformances(reqConformances);
|
||||
|
||||
|
||||
@@ -1361,6 +1361,9 @@ void Serializer::writeNormalConformance(
|
||||
return false;
|
||||
});
|
||||
|
||||
unsigned numSignatureConformances =
|
||||
conformance->getSignatureConformances().size();
|
||||
|
||||
unsigned abbrCode
|
||||
= DeclTypeAbbrCodes[NormalProtocolConformanceLayout::Code];
|
||||
auto ownerID = addDeclContextRef(conformance->getDeclContext());
|
||||
@@ -1368,6 +1371,7 @@ void Serializer::writeNormalConformance(
|
||||
addDeclRef(protocol), ownerID,
|
||||
numValueWitnesses,
|
||||
numTypeWitnesses,
|
||||
numSignatureConformances,
|
||||
data);
|
||||
|
||||
// Write requirement signature conformances.
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
@protocol Order2_ConsistentBaseProto
|
||||
- (void)consistent;
|
||||
@end
|
||||
|
||||
@protocol Order4_ConsistentBaseProto
|
||||
- (void)consistent;
|
||||
@end
|
||||
|
||||
@protocol Order1_FickleBaseProto
|
||||
- (void)fickle;
|
||||
@optional
|
||||
- (void)extraFickle;
|
||||
@end
|
||||
|
||||
@protocol Order3_FickleBaseProto
|
||||
- (void)fickle;
|
||||
@optional
|
||||
- (void)extraFickle;
|
||||
@end
|
||||
|
||||
@protocol Order5_FickleBaseProto
|
||||
- (void)fickle;
|
||||
@optional
|
||||
- (void)extraFickle;
|
||||
@end
|
||||
|
||||
// The actual order here is determined by the protocol names.
|
||||
#if EXTRA_PROTOCOL_FIRST
|
||||
@protocol SubProto <Order1_FickleBaseProto, Order2_ConsistentBaseProto, Order4_ConsistentBaseProto>
|
||||
@end
|
||||
#elif EXTRA_PROTOCOL_MIDDLE
|
||||
@protocol SubProto <Order2_ConsistentBaseProto, Order3_FickleBaseProto, Order4_ConsistentBaseProto>
|
||||
@end
|
||||
#elif EXTRA_PROTOCOL_LAST
|
||||
@protocol SubProto <Order2_ConsistentBaseProto, Order4_ConsistentBaseProto, Order5_FickleBaseProto>
|
||||
@end
|
||||
#elif NO_EXTRA_PROTOCOLS
|
||||
@protocol SubProto <Order2_ConsistentBaseProto, Order4_ConsistentBaseProto>
|
||||
@end
|
||||
#else
|
||||
# error "Missing -D flag"
|
||||
#endif
|
||||
@@ -1,4 +1,5 @@
|
||||
module Overrides { header "Overrides.h" }
|
||||
module ProtocolInheritance { header "ProtocolInheritance.h" }
|
||||
module Typedefs { header "Typedefs.h" }
|
||||
module TypeRemovalObjC { header "TypeRemovalObjC.h" }
|
||||
module Types { header "Types.h" }
|
||||
|
||||
75
test/Serialization/Recovery/protocol-inheritance.swift
Normal file
75
test/Serialization/Recovery/protocol-inheritance.swift
Normal file
@@ -0,0 +1,75 @@
|
||||
// RUN: %empty-directory(%t)
|
||||
|
||||
// RUN: %target-swift-frontend -emit-module -o %t -module-name Lib -I %S/Inputs/custom-modules -Xcc -DNO_EXTRA_PROTOCOLS %s
|
||||
// RUN: %target-swift-frontend -typecheck -DTEST -Xcc -DNO_EXTRA_PROTOCOLS -I %t -I %S/Inputs/custom-modules %s
|
||||
// RUN: %target-swift-frontend -emit-ir -DTEST -Xcc -DEXTRA_PROTOCOL_FIRST -I %t -I %S/Inputs/custom-modules %s -o /dev/null
|
||||
|
||||
// RUN: %target-swift-frontend -emit-module -o %t -module-name Lib -I %S/Inputs/custom-modules -Xcc -DEXTRA_PROTOCOL_FIRST %s
|
||||
// RUN: %target-swift-frontend -typecheck -DTEST -Xcc -DEXTRA_PROTOCOL_FIRST -I %t -I %S/Inputs/custom-modules %s
|
||||
// RUN: %target-swift-frontend -emit-ir -DTEST -Xcc -DNO_EXTRA_PROTOCOLS -I %t -I %S/Inputs/custom-modules %s -o /dev/null
|
||||
|
||||
|
||||
// RUN: %target-swift-frontend -emit-module -o %t -module-name Lib -I %S/Inputs/custom-modules -Xcc -DNO_EXTRA_PROTOCOLS %s
|
||||
// RUN: %target-swift-frontend -typecheck -DTEST -Xcc -DNO_EXTRA_PROTOCOLS -I %t -I %S/Inputs/custom-modules %s
|
||||
// RUN: %target-swift-frontend -emit-ir -DTEST -Xcc -DEXTRA_PROTOCOL_MIDDLE -I %t -I %S/Inputs/custom-modules %s -o /dev/null
|
||||
|
||||
// RUN: %target-swift-frontend -emit-module -o %t -module-name Lib -I %S/Inputs/custom-modules -Xcc -DEXTRA_PROTOCOL_MIDDLE %s
|
||||
// RUN: %target-swift-frontend -typecheck -DTEST -Xcc -DEXTRA_PROTOCOL_MIDDLE -I %t -I %S/Inputs/custom-modules %s
|
||||
// RUN: %target-swift-frontend -emit-ir -DTEST -Xcc -DNO_EXTRA_PROTOCOLS -I %t -I %S/Inputs/custom-modules %s -o /dev/null
|
||||
|
||||
|
||||
// RUN: %target-swift-frontend -emit-module -o %t -module-name Lib -I %S/Inputs/custom-modules -Xcc -DNO_EXTRA_PROTOCOLS %s
|
||||
// RUN: %target-swift-frontend -typecheck -DTEST -Xcc -DNO_EXTRA_PROTOCOLS -I %t -I %S/Inputs/custom-modules %s
|
||||
// RUN: %target-swift-frontend -emit-ir -DTEST -Xcc -DEXTRA_PROTOCOL_LAST -I %t -I %S/Inputs/custom-modules %s -o /dev/null
|
||||
|
||||
// RUN: %target-swift-frontend -emit-module -o %t -module-name Lib -I %S/Inputs/custom-modules -Xcc -DEXTRA_PROTOCOL_LAST %s
|
||||
// RUN: %target-swift-frontend -typecheck -DTEST -Xcc -DEXTRA_PROTOCOL_LAST -I %t -I %S/Inputs/custom-modules %s
|
||||
// RUN: %target-swift-frontend -emit-ir -DTEST -Xcc -DNO_EXTRA_PROTOCOLS -I %t -I %S/Inputs/custom-modules %s -o /dev/null
|
||||
|
||||
|
||||
// REQUIRES: objc_interop
|
||||
|
||||
#if TEST
|
||||
|
||||
import Lib
|
||||
import ProtocolInheritance
|
||||
|
||||
func useSubProto<T: SubProto>(_: T) {}
|
||||
func useConsistentProto<T: Order2_ConsistentBaseProto>(_: T) {}
|
||||
func useFickleProto<T: Order1_FickleBaseProto>(_: T) {}
|
||||
|
||||
func test(obj: Impl) {
|
||||
useConsistentProto(obj)
|
||||
useFickleProto(obj)
|
||||
useSubProto(obj)
|
||||
}
|
||||
|
||||
protocol ForceDeserializationProto: SubProto {}
|
||||
extension Impl: ForceDeserializationProto {}
|
||||
|
||||
func test(obj: PartialImpl) {
|
||||
useConsistentProto(obj)
|
||||
useSubProto(obj)
|
||||
}
|
||||
|
||||
extension PartialImpl: ForceDeserializationProto {}
|
||||
|
||||
#else // TEST
|
||||
|
||||
import ProtocolInheritance
|
||||
|
||||
open class Impl: SubProto {
|
||||
public func consistent() {}
|
||||
}
|
||||
|
||||
extension Impl: Order1_FickleBaseProto, Order3_FickleBaseProto, Order5_FickleBaseProto {
|
||||
public func fickle() {}
|
||||
}
|
||||
|
||||
open class PartialImpl: SubProto {
|
||||
public func consistent() {}
|
||||
public func fickle() {}
|
||||
}
|
||||
|
||||
|
||||
#endif // TEST
|
||||
Reference in New Issue
Block a user