mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
The runtime hash table for protocol conformances is keyed by (type, protocol), where the type can be either a type metadata pointer or a type context descriptor. The latter is preferred for generic and resilient types, because a single entry in the cache can work for any instantiation. However, not all type metadata has a corresponding type context descriptor. For example, a class that is dynamically subclassed by the Objective-C runtime won't have a type context descriptor. In such cases, our key into the hash table was (NULL, protocol) leading to mistaken conformances for different dynamically-subclassed types. Introduce a NULL check for the type of the hash table key, which illustrates the problem in a number of existing tests that exercise the runtime, and teach the runtime to use the type context descriptor as the key only when it's non-NULL, falling back to the type metadata pointer otherwise. Fixes rdar://problem/38053213.
82 lines
1.6 KiB
Swift
82 lines
1.6 KiB
Swift
// RUN: %target-run-simple-swift
|
|
// REQUIRES: executable_test
|
|
|
|
|
|
// REQUIRES: objc_interop
|
|
|
|
import StdlibUnittest
|
|
import Foundation
|
|
|
|
protocol P { }
|
|
protocol Q { }
|
|
|
|
class Supermodel : NSObject { }
|
|
|
|
class Model1 : Supermodel {
|
|
@objc dynamic var name = ""
|
|
}
|
|
|
|
class Model2 : NSObject, Q {
|
|
@objc dynamic var name = ""
|
|
}
|
|
|
|
|
|
extension Supermodel: P { }
|
|
|
|
var kvoContext = 0
|
|
|
|
class Observer: NSObject {
|
|
let model1 = Model1()
|
|
let model2 = Model2()
|
|
|
|
override init() {
|
|
super.init()
|
|
model1.addObserver(self, forKeyPath: "name", options: [], context: &kvoContext)
|
|
model2.addObserver(self, forKeyPath: "name", options: [], context: &kvoContext)
|
|
}
|
|
|
|
deinit {
|
|
model1.removeObserver(self, forKeyPath: "name")
|
|
model2.removeObserver(self, forKeyPath: "name")
|
|
}
|
|
}
|
|
|
|
let allTests = TestSuite("Dynamic subclasses conformance lookups")
|
|
|
|
allTests.test("Lookup via dynamic subclasses") {
|
|
let observer = Observer()
|
|
|
|
// Check via "AnyObject"
|
|
let model1obj: AnyObject = observer.model1
|
|
let model2obj: AnyObject = observer.model2
|
|
|
|
expectTrue(model1obj is P)
|
|
expectFalse(model1obj is Q)
|
|
expectFalse(model2obj is P)
|
|
expectTrue(model2obj is Q)
|
|
|
|
expectNotNil(model1obj as? P)
|
|
expectNil(model1obj as? Q)
|
|
expectNil(model2obj as? P)
|
|
expectNotNil(model2obj as? Q)
|
|
|
|
// Check via "Any"
|
|
let model1: Any = observer.model1
|
|
let model2: Any = observer.model2
|
|
|
|
expectTrue(model1 is P)
|
|
expectFalse(model1 is Q)
|
|
expectFalse(model2 is P)
|
|
expectTrue(model2 is Q)
|
|
|
|
expectNotNil(model1 as? P)
|
|
expectNil(model1 as? Q)
|
|
expectNil(model2 as? P)
|
|
expectNotNil(model2 as? Q)
|
|
|
|
print(model1)
|
|
print(model2)
|
|
}
|
|
|
|
runAllTests()
|