Skip to content

Commit 4cef8ae

Browse files
authored
Merge pull request swiftlang#15082 from DougGregor/runtime-conformance-null-descriptor
2 parents e7b63e5 + 2dd61a9 commit 4cef8ae

File tree

2 files changed

+96
-4
lines changed

2 files changed

+96
-4
lines changed

stdlib/public/runtime/ProtocolConformance.cpp

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,9 @@ namespace {
217217
const ProtocolDescriptor *Proto;
218218

219219
ConformanceCacheKey(const void *type, const ProtocolDescriptor *proto)
220-
: Type(type), Proto(proto) {}
220+
: Type(type), Proto(proto) {
221+
assert(type);
222+
}
221223
};
222224

223225
struct ConformanceCacheEntry {
@@ -401,6 +403,15 @@ struct ConformanceCacheResult {
401403
}
402404
};
403405

406+
/// Retrieve the type key from the given metadata, to be used when looking
407+
/// into the conformance cache.
408+
static const void *getConformanceCacheTypeKey(const Metadata *type) {
409+
if (auto description = type->getTypeContextDescriptor())
410+
return description;
411+
412+
return type;
413+
}
414+
404415
/// Search for a witness table in the ConformanceCache.
405416
static
406417
ConformanceCacheResult
@@ -455,10 +466,10 @@ searchInConformanceCache(const Metadata *type,
455466
// For generic and resilient types, nondependent conformances
456467
// are keyed by the nominal type descriptor rather than the
457468
// metadata, so try that.
458-
const auto *description = type->getTypeContextDescriptor();
469+
auto typeKey = getConformanceCacheTypeKey(type);
459470

460471
// Hash and lookup the type-protocol pair in the cache.
461-
if (auto *Value = C.findCached(description, protocol)) {
472+
if (auto *Value = C.findCached(typeKey, protocol)) {
462473
if (Value->isSuccessful())
463474
return ConformanceCacheResult::cachedSuccess(Value->getWitnessTable());
464475

@@ -505,7 +516,7 @@ bool isRelatedType(const Metadata *type, const void *candidate,
505516
if (!candidateIsMetadata) {
506517
const auto *description = type->getTypeContextDescriptor();
507518
auto candidateDescription =
508-
static_cast<const TypeContextDescriptor *>(candidate);
519+
static_cast<const TypeContextDescriptor *>(candidate);
509520
if (description && equalContexts(description, candidateDescription))
510521
return true;
511522
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// RUN: %target-run-simple-swift
2+
// REQUIRES: executable_test
3+
4+
5+
// REQUIRES: objc_interop
6+
7+
import StdlibUnittest
8+
import Foundation
9+
10+
protocol P { }
11+
protocol Q { }
12+
13+
class Supermodel : NSObject { }
14+
15+
class Model1 : Supermodel {
16+
@objc dynamic var name = ""
17+
}
18+
19+
class Model2 : NSObject, Q {
20+
@objc dynamic var name = ""
21+
}
22+
23+
24+
extension Supermodel: P { }
25+
26+
var kvoContext = 0
27+
28+
class Observer: NSObject {
29+
let model1 = Model1()
30+
let model2 = Model2()
31+
32+
override init() {
33+
super.init()
34+
model1.addObserver(self, forKeyPath: "name", options: [], context: &kvoContext)
35+
model2.addObserver(self, forKeyPath: "name", options: [], context: &kvoContext)
36+
}
37+
38+
deinit {
39+
model1.removeObserver(self, forKeyPath: "name")
40+
model2.removeObserver(self, forKeyPath: "name")
41+
}
42+
}
43+
44+
let allTests = TestSuite("Dynamic subclasses conformance lookups")
45+
46+
allTests.test("Lookup via dynamic subclasses") {
47+
let observer = Observer()
48+
49+
// Check via "AnyObject"
50+
let model1obj: AnyObject = observer.model1
51+
let model2obj: AnyObject = observer.model2
52+
53+
expectTrue(model1obj is P)
54+
expectFalse(model1obj is Q)
55+
expectFalse(model2obj is P)
56+
expectTrue(model2obj is Q)
57+
58+
expectNotNil(model1obj as? P)
59+
expectNil(model1obj as? Q)
60+
expectNil(model2obj as? P)
61+
expectNotNil(model2obj as? Q)
62+
63+
// Check via "Any"
64+
let model1: Any = observer.model1
65+
let model2: Any = observer.model2
66+
67+
expectTrue(model1 is P)
68+
expectFalse(model1 is Q)
69+
expectFalse(model2 is P)
70+
expectTrue(model2 is Q)
71+
72+
expectNotNil(model1 as? P)
73+
expectNil(model1 as? Q)
74+
expectNil(model2 as? P)
75+
expectNotNil(model2 as? Q)
76+
77+
print(model1)
78+
print(model2)
79+
}
80+
81+
runAllTests()

0 commit comments

Comments
 (0)