Skip to content

Commit 53b3a69

Browse files
committed
Runtime: Fix dynamic casts to support resilient protocols
If a protocol witness table requires instantiation, the runtime needs to call the witness table accessor when looking up the conformance in swift_conformsToProtocol(). We had a bit of code for this already, but it wasn't fully hooked up. Change IRGen to emit a reference to the witness table accessor rather than the witness table itself if the witness table needs instantiation, and add support to the runtime for calling the accessor.
1 parent 1a2beb7 commit 53b3a69

File tree

5 files changed

+65
-23
lines changed

5 files changed

+65
-23
lines changed

include/swift/ABI/MetadataValues.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ struct TypeMetadataRecordFlags {
170170
constexpr TypeMetadataRecordFlags(int_type Data) : Data(Data) {}
171171

172172
constexpr TypeMetadataRecordKind getTypeKind() const {
173-
return TypeMetadataRecordKind((Data >> TypeKindShift) & TypeKindMask);
173+
return TypeMetadataRecordKind((Data & TypeKindMask) >> TypeKindShift);
174174
}
175175
constexpr TypeMetadataRecordFlags withTypeKind(
176176
TypeMetadataRecordKind ptk) const {
@@ -199,8 +199,8 @@ struct ProtocolConformanceFlags : public TypeMetadataRecordFlags {
199199
(Data & ~TypeKindMask) | (int_type(ptk) << TypeKindShift));
200200
}
201201
constexpr ProtocolConformanceReferenceKind getConformanceKind() const {
202-
return ProtocolConformanceReferenceKind((Data >> ConformanceKindShift)
203-
& ConformanceKindMask);
202+
return ProtocolConformanceReferenceKind((Data & ConformanceKindMask)
203+
>> ConformanceKindShift);
204204
}
205205
constexpr ProtocolConformanceFlags withConformanceKind(
206206
ProtocolConformanceReferenceKind pck) const {

lib/IRGen/GenDecl.cpp

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2174,17 +2174,27 @@ llvm::Constant *IRGenModule::emitProtocolConformances() {
21742174
getPointerAlignment(), ProtocolDescriptorStructTy);
21752175
auto typeEntity = getTypeEntityInfo(*this,
21762176
conformance->getType()->getCanonicalType());
2177-
auto flags = typeEntity.flags
2178-
.withConformanceKind(ProtocolConformanceReferenceKind::WitnessTable);
2179-
2180-
// If the conformance is in this object's table, then the witness table
2181-
// should also be in this object file, so we can always directly reference
2182-
// it.
2183-
// TODO: Should use accessor kind for lazy conformances
2184-
// TODO: Produce a relative reference to a private generator function
2185-
// if the witness table requires lazy initialization, instantiation, or
2186-
// conditional conformance checking.
2187-
auto witnessTableVar = getAddrOfWitnessTable(conformance);
2177+
auto flags = typeEntity.flags;
2178+
2179+
llvm::Constant *witnessTableVar;
2180+
2181+
if (!isResilient(conformance->getProtocol(),
2182+
ResilienceExpansion::Maximal)) {
2183+
flags = flags.withConformanceKind(
2184+
ProtocolConformanceReferenceKind::WitnessTable);
2185+
2186+
// If the conformance is in this object's table, then the witness table
2187+
// should also be in this object file, so we can always directly reference
2188+
// it.
2189+
witnessTableVar = getAddrOfWitnessTable(conformance);
2190+
} else {
2191+
flags = flags.withConformanceKind(
2192+
ProtocolConformanceReferenceKind::WitnessTableAccessor);
2193+
2194+
witnessTableVar = getAddrOfWitnessTableAccessFunction(
2195+
conformance, ForDefinition);
2196+
}
2197+
21882198
auto witnessTableRef =
21892199
ConstantReference(witnessTableVar, ConstantReference::Direct);
21902200

stdlib/public/runtime/ProtocolConformance.cpp

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -637,15 +637,11 @@ swift::swift_conformsToProtocol(const Metadata *type,
637637
C.cacheFailure(metadata, P);
638638
}
639639

640-
// If the record provides a nondependent witness table for all instances
641-
// of a generic type, cache it for the generic pattern.
642640
// TODO: "Nondependent witness table" probably deserves its own flag.
643641
// An accessor function might still be necessary even if the witness table
644642
// can be shared.
645643
} else if (record.getTypeKind()
646-
== TypeMetadataRecordKind::UniqueNominalTypeDescriptor
647-
&& record.getConformanceKind()
648-
== ProtocolConformanceReferenceKind::WitnessTable) {
644+
== TypeMetadataRecordKind::UniqueNominalTypeDescriptor) {
649645

650646
auto R = record.getNominalTypeDescriptor();
651647
auto P = record.getProtocol();
@@ -658,7 +654,20 @@ swift::swift_conformsToProtocol(const Metadata *type,
658654
continue;
659655

660656
// Store the type-protocol pair in the cache.
661-
C.cacheSuccess(R, P, record.getStaticWitnessTable());
657+
switch (record.getConformanceKind()) {
658+
case ProtocolConformanceReferenceKind::WitnessTable:
659+
// If the record provides a nondependent witness table for all
660+
// instances of a generic type, cache it for the generic pattern.
661+
C.cacheSuccess(R, P, record.getStaticWitnessTable());
662+
break;
663+
664+
case ProtocolConformanceReferenceKind::WitnessTableAccessor:
665+
// If the record provides a dependent witness table accessor,
666+
// cache the result for the instantiated type metadata.
667+
C.cacheSuccess(type, P, record.getWitnessTable(type));
668+
break;
669+
670+
}
662671
}
663672
}
664673
}

test/Inputs/resilient_protocol.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,9 @@ extension OtherResilientProtocol {
1515
set { x = newValue }
1616
}
1717
}
18+
19+
public protocol ResilientBaseProtocol {
20+
func requirement() -> Int
21+
}
22+
23+
public protocol ResilientDerivedProtocol : ResilientBaseProtocol {}

test/Interpreter/protocol_resilience.swift

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,6 @@
1010
// requirements with default implementations is actually tested in
1111
// validation-test/Evolution/test_protocol_*.
1212
//
13-
// This test just ensures we can call materializeForSet defined in a
14-
// protocol extension from a different resilience domain.
15-
//
1613

1714
import StdlibUnittest
1815

@@ -27,6 +24,8 @@ func increment(_ x: inout Int, by: Int) {
2724

2825
struct OtherConformingType : OtherResilientProtocol { }
2926

27+
// Ensure we can call materializeForSet defined in a protocol extension
28+
// from a different resilience domain.
3029
ResilientProtocolTestSuite.test("PropertyInProtocolExtension") {
3130
var o = OtherConformingType()
3231

@@ -36,4 +35,22 @@ ResilientProtocolTestSuite.test("PropertyInProtocolExtension") {
3635
expectEqual(OtherConformingType.staticPropertyInExtension, 12)
3736
}
3837

38+
struct DerivedConformingType : ResilientDerivedProtocol {
39+
func requirement() -> Int { return 42 }
40+
}
41+
42+
// Ensure dynamic casts to resilient types work.
43+
func callBaseRequirement(t: ResilientBaseProtocol) -> Int {
44+
return t.requirement()
45+
}
46+
47+
@_semantics("optimize.sil.never")
48+
func castToDerivedProtocol<T>(t: T) -> Int {
49+
return callBaseRequirement(t: t as! ResilientDerivedProtocol)
50+
}
51+
52+
ResilientProtocolTestSuite.test("DynamicCastToResilientProtocol") {
53+
expectEqual(castToDerivedProtocol(t: DerivedConformingType()), 42)
54+
}
55+
3956
runAllTests()

0 commit comments

Comments
 (0)