mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
An assertion I added recently to check property overrides in the ASTVerifier uncovered some bugs in this area: - We did not synthesize a materializeForSet for properties defined in extensions of imported classes unless they witnessed a protocol requirement. This triggered an assertion if the property had an override that was checked before the protocol conformance, since the override's materializeForSet would not be marked as an override of the base materializeForSet. - materializeForSet for properties defined in extensions would statically dispatch the getter and setter instead of dynamically dispatching. This is incorrect since we statically dispatch to the materializeForSet in this case, and we can in fact override it in a subclass. Fixes <rdar://problem/31334272>.
243 lines
5.7 KiB
Swift
243 lines
5.7 KiB
Swift
// RUN: rm -rf %t && mkdir -p %t
|
|
|
|
// RUN: %clang %target-cc-options -isysroot %sdk -fobjc-arc %S/Inputs/ObjCClasses/ObjCClasses.m -c -o %t/ObjCClasses.o
|
|
// RUN: %target-build-swift -I %S/Inputs/ObjCClasses/ %t/ObjCClasses.o %s -o %t/a.out
|
|
// RUN: %target-run %t/a.out
|
|
|
|
// REQUIRES: executable_test
|
|
// REQUIRES: objc_interop
|
|
|
|
import Foundation
|
|
import StdlibUnittest
|
|
import ObjCClasses
|
|
|
|
class SwiftClass : ProtoWithClassProperty {
|
|
static var getCount = 0
|
|
static var setCount = 0
|
|
|
|
private static var _value: CInt = 0
|
|
|
|
@objc class func reset() {
|
|
getCount = 0
|
|
setCount = 0
|
|
_value = 0
|
|
}
|
|
|
|
@objc class var value: CInt {
|
|
get {
|
|
getCount += 1
|
|
return _value
|
|
}
|
|
set {
|
|
setCount += 1
|
|
_value = newValue
|
|
}
|
|
}
|
|
|
|
@objc class var optionalClassProp: Bool {
|
|
return true
|
|
}
|
|
}
|
|
|
|
class Subclass : ClassWithClassProperty {
|
|
static var getCount = 0
|
|
static var setCount = 0
|
|
|
|
override class func reset() {
|
|
getCount = 0
|
|
setCount = 0
|
|
super.reset()
|
|
}
|
|
|
|
override class var value: CInt {
|
|
get {
|
|
getCount += 1
|
|
return super.value
|
|
}
|
|
set {
|
|
setCount += 1
|
|
super.value = newValue
|
|
}
|
|
}
|
|
|
|
override class var optionalClassProp: Bool {
|
|
return true
|
|
}
|
|
}
|
|
|
|
var ClassProperties = TestSuite("ClassProperties")
|
|
|
|
ClassProperties.test("direct") {
|
|
ClassWithClassProperty.reset()
|
|
expectEqual(0, ClassWithClassProperty.value)
|
|
ClassWithClassProperty.value = 4
|
|
expectEqual(4, ClassWithClassProperty.value)
|
|
|
|
Subclass.reset()
|
|
expectEqual(0, Subclass.value)
|
|
Subclass.value = 4
|
|
expectEqual(4, Subclass.value)
|
|
expectEqual(2, Subclass.getCount)
|
|
expectEqual(1, Subclass.setCount)
|
|
}
|
|
|
|
func testExistential(_ e: ProtoWithClassProperty.Type) {
|
|
e.reset()
|
|
expectEqual(0, e.value)
|
|
e.value = 4
|
|
expectEqual(4, e.value)
|
|
}
|
|
|
|
ClassProperties.test("existentials") {
|
|
testExistential(ClassWithClassProperty.self)
|
|
testExistential(ObjCSubclassWithClassProperty.self)
|
|
|
|
testExistential(SwiftClass.self)
|
|
expectEqual(2, SwiftClass.getCount)
|
|
expectEqual(1, SwiftClass.setCount)
|
|
|
|
testExistential(Subclass.self)
|
|
expectEqual(2, Subclass.getCount)
|
|
expectEqual(1, Subclass.setCount)
|
|
}
|
|
|
|
func testGeneric<T: ProtoWithClassProperty>(_ e: T.Type) {
|
|
e.reset()
|
|
expectEqual(0, e.value)
|
|
e.value = 4
|
|
expectEqual(4, e.value)
|
|
}
|
|
|
|
ClassProperties.test("generics") {
|
|
testGeneric(ClassWithClassProperty.self)
|
|
testGeneric(ObjCSubclassWithClassProperty.self)
|
|
|
|
testGeneric(SwiftClass.self)
|
|
expectEqual(2, SwiftClass.getCount)
|
|
expectEqual(1, SwiftClass.setCount)
|
|
|
|
testGeneric(Subclass.self)
|
|
expectEqual(2, Subclass.getCount)
|
|
expectEqual(1, Subclass.setCount)
|
|
}
|
|
|
|
func testInheritance(_ e: ClassWithClassProperty.Type) {
|
|
e.reset()
|
|
expectEqual(0, e.value)
|
|
e.value = 4
|
|
expectEqual(4, e.value)
|
|
}
|
|
|
|
ClassProperties.test("inheritance") {
|
|
testInheritance(ClassWithClassProperty.self)
|
|
testInheritance(ObjCSubclassWithClassProperty.self)
|
|
|
|
testInheritance(Subclass.self)
|
|
expectEqual(2, Subclass.getCount)
|
|
expectEqual(1, Subclass.setCount)
|
|
}
|
|
|
|
func testInheritanceGeneric<T: ClassWithClassProperty>(_ e: T.Type) {
|
|
e.reset()
|
|
expectEqual(0, e.value)
|
|
e.value = 4
|
|
expectEqual(4, e.value)
|
|
}
|
|
|
|
ClassProperties.test("inheritance/generic") {
|
|
testInheritanceGeneric(ClassWithClassProperty.self)
|
|
testInheritanceGeneric(ObjCSubclassWithClassProperty.self)
|
|
|
|
testInheritanceGeneric(Subclass.self)
|
|
expectEqual(2, Subclass.getCount)
|
|
expectEqual(1, Subclass.setCount)
|
|
}
|
|
|
|
ClassProperties.test("optionalProp") {
|
|
let noProp: ProtoWithClassProperty.Type = ClassWithClassProperty.self
|
|
expectNil(noProp.optionalClassProp)
|
|
|
|
let hasProp: ProtoWithClassProperty.Type = Subclass.self
|
|
expectNotNil(hasProp.optionalClassProp)
|
|
expectEqual(true, hasProp.optionalClassProp!)
|
|
|
|
let hasOwnProp: ProtoWithClassProperty.Type = SwiftClass.self
|
|
expectNotNil(hasOwnProp.optionalClassProp)
|
|
expectEqual(true, hasOwnProp.optionalClassProp!)
|
|
|
|
let hasPropObjC: ProtoWithClassProperty.Type = ObjCSubclassWithClassProperty.self
|
|
expectNotNil(hasPropObjC.optionalClassProp)
|
|
expectEqual(true, hasPropObjC.optionalClassProp!)
|
|
}
|
|
|
|
class NamingConflictSubclass : PropertyNamingConflict {
|
|
override var prop: Any? { return nil }
|
|
override class var prop: Any? { return NamingConflictSubclass() }
|
|
}
|
|
|
|
ClassProperties.test("namingConflict") {
|
|
let obj = PropertyNamingConflict()
|
|
expectTrue(obj === obj.prop.map { $0 as AnyObject })
|
|
expectNil(type(of: obj).prop)
|
|
expectNil(PropertyNamingConflict.prop)
|
|
|
|
let sub = NamingConflictSubclass()
|
|
expectNil(sub.prop)
|
|
expectNotNil(type(of: sub).prop)
|
|
expectNotNil(NamingConflictSubclass.prop)
|
|
}
|
|
|
|
extension NamingConflictSubclass : PropertyNamingConflictProto {
|
|
var protoProp: Any? {
|
|
get { return self }
|
|
set {}
|
|
}
|
|
class var protoProp: Any? {
|
|
get { return nil }
|
|
set {}
|
|
}
|
|
}
|
|
|
|
ClassProperties.test("namingConflict/protocol") {
|
|
let obj: PropertyNamingConflictProto = NamingConflictSubclass()
|
|
expectTrue(obj === obj.protoProp.map { $0 as AnyObject })
|
|
expectNil(type(of: obj).protoProp)
|
|
|
|
let type: PropertyNamingConflictProto.Type = NamingConflictSubclass.self
|
|
expectNil(type.protoProp)
|
|
}
|
|
|
|
var global1: Int = 0
|
|
|
|
var global2: Int = 0
|
|
|
|
class Steak : NSObject {
|
|
@objc override var thickness: Int {
|
|
get { return global1 } set { global1 = newValue }
|
|
}
|
|
}
|
|
|
|
extension NSObject : HasThickness {
|
|
@objc var thickness: Int { get { return global2 } set { global2 = newValue } }
|
|
}
|
|
|
|
protocol HasThickness : class {
|
|
var thickness: Int { get set }
|
|
}
|
|
|
|
ClassProperties.test("dynamicOverride") {
|
|
// Calls NSObject.thickness
|
|
NSObject().thickness += 1
|
|
|
|
// Calls Steak.thickness
|
|
(Steak() as NSObject).thickness += 1
|
|
Steak().thickness += 1
|
|
(Steak() as HasThickness).thickness += 1
|
|
|
|
expectEqual(3, global1)
|
|
expectEqual(1, global2)
|
|
}
|
|
|
|
runAllTests()
|
|
|