Skip to content

Commit 6c29efa

Browse files
authored
Merge pull request #15084 from DougGregor/runtime-conformance-null-descriptor-4.2
[4.2] [Runtime] Don't use NULL as a conformance cache key.
2 parents 28ccd58 + 283eb89 commit 6c29efa

File tree

2 files changed

+97
-5
lines changed

2 files changed

+97
-5
lines changed

stdlib/public/runtime/ProtocolConformance.cpp

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,9 @@ namespace {
166166
const ProtocolDescriptor *Proto;
167167

168168
ConformanceCacheKey(const void *type, const ProtocolDescriptor *proto)
169-
: Type(type), Proto(proto) {}
169+
: Type(type), Proto(proto) {
170+
assert(type);
171+
}
170172
};
171173

172174
struct ConformanceCacheEntry {
@@ -331,6 +333,15 @@ struct ConformanceCacheResult {
331333
}
332334
};
333335

336+
/// Retrieve the type key from the given metadata, to be used when looking
337+
/// into the conformance cache.
338+
static const void *getConformanceCacheTypeKey(const Metadata *type) {
339+
if (auto description = type->getNominalTypeDescriptor())
340+
return description;
341+
342+
return type;
343+
}
344+
334345
/// Search for a witness table in the ConformanceCache.
335346
static
336347
ConformanceCacheResult
@@ -385,10 +396,10 @@ searchInConformanceCache(const Metadata *type,
385396
// For generic and resilient types, nondependent conformances
386397
// are keyed by the nominal type descriptor rather than the
387398
// metadata, so try that.
388-
const auto *description = type->getNominalTypeDescriptor();
399+
auto typeKey = getConformanceCacheTypeKey(type);
389400

390401
// Hash and lookup the type-protocol pair in the cache.
391-
if (auto *Value = C.findCached(description, protocol)) {
402+
if (auto *Value = C.findCached(typeKey, protocol)) {
392403
if (Value->isSuccessful())
393404
return ConformanceCacheResult::cachedSuccess(Value->getWitnessTable());
394405

@@ -435,7 +446,7 @@ bool isRelatedType(const Metadata *type, const void *candidate,
435446
if (!candidateIsMetadata) {
436447
const auto *description = type->getNominalTypeDescriptor();
437448
auto candidateDescription =
438-
static_cast<const NominalTypeDescriptor *>(candidate);
449+
static_cast<const NominalTypeDescriptor *>(candidate);
439450
if (description && description->isEqual(candidateDescription))
440451
return true;
441452
}
@@ -564,7 +575,7 @@ swift::swift_conformsToProtocol(const Metadata * const type,
564575
case ProtocolConformanceReferenceKind::WitnessTable:
565576
// If the record provides a nondependent witness table for all
566577
// instances of a generic type, cache it for the generic pattern.
567-
C.cacheSuccess(type->getNominalTypeDescriptor(), P,
578+
C.cacheSuccess(getConformanceCacheTypeKey(type), P,
568579
record.getStaticWitnessTable());
569580
break;
570581

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)