mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
We cannot use field offset globals if *any* field of a generic class with Objective-C ancestry is dependent. This is because the Swift runtime first performs layout starting from a static instance start offset, and then asks the Objective-C runtime to slide the offsets based on the dynamic superclass size. So if the class has a field of generic type, the alignment of that type can change the offsets of fields *before* it as well as after. So we cannot assuem that any fields in such a class have the same offset across instantiations at all. The previous fix captured the intent of the above, but it only kicked in if the immediate superclass of the class was imported from Objective-C. But really we need to do this for any class with Objective-C ancestry. While fixing this, re-organize the code in ClassLayoutBuilder a little bit to untangle the stored property iteration from the interesting FieldAccess adjustments that take place after.
327 lines
6.7 KiB
Swift
327 lines
6.7 KiB
Swift
// RUN: %empty-directory(%t)
|
|
//
|
|
// RUN: %target-clang -fobjc-arc %S/Inputs/ObjCClasses/ObjCClasses.m -c -o %t/ObjCClasses.o
|
|
// RUN: %target-build-swift -I %S/Inputs/ObjCClasses/ -Xlinker %t/ObjCClasses.o %s -o %t/a.out
|
|
// RUN: %target-run %t/a.out | %FileCheck %s
|
|
|
|
// REQUIRES: executable_test
|
|
// REQUIRES: objc_interop
|
|
|
|
import Foundation
|
|
import ObjCClasses
|
|
|
|
@objc protocol P {
|
|
func calculatePrice() -> Int
|
|
}
|
|
|
|
protocol PP {
|
|
func calculateTaxes() -> Int
|
|
}
|
|
|
|
//
|
|
// Generic subclass of an @objc class
|
|
//
|
|
|
|
class A<T> : HasHiddenIvars, P {
|
|
var first: Int = 16
|
|
var second: T?
|
|
var third: Int = 61
|
|
|
|
override var description: String {
|
|
return "Grilled artichokes"
|
|
}
|
|
|
|
func calculatePrice() -> Int {
|
|
return 400
|
|
}
|
|
}
|
|
|
|
let a = A<Int>()
|
|
|
|
// CHECK: Grilled artichokes
|
|
// CHECK: Grilled artichokes
|
|
print(a.description)
|
|
print((a as NSObject).description)
|
|
|
|
let f = { (a.x, a.y, a.z, a.t, a.first, a.second, a.third) }
|
|
|
|
// CHECK: (0, 0, 0, 0, 16, nil, 61)
|
|
print(f())
|
|
|
|
// CHECK: (25, 225, 255, 2255, 16, nil, 61)
|
|
a.x = 25
|
|
a.y = 225
|
|
a.z = 255
|
|
a.t = 2255
|
|
print(f())
|
|
|
|
// CHECK: (36, 225, 255, 2255, 16, nil, 61)
|
|
a.x = 36
|
|
print(f())
|
|
|
|
// CHECK: (36, 225, 255, 2255, 16, Optional(121), 61)
|
|
a.second = 121
|
|
print(f())
|
|
|
|
//
|
|
// Instantiate the class with a different set of generic parameters
|
|
//
|
|
|
|
let aa = A<(Int, Int)>()
|
|
let ff = { (aa.x, aa.y, aa.z, aa.t, aa.first, aa.second, aa.third) }
|
|
|
|
// CHECK: (0, 0, 0, 0, 16, nil, 61)
|
|
print(ff())
|
|
|
|
aa.x = 101
|
|
aa.second = (19, 84)
|
|
aa.third = 17
|
|
|
|
// CHECK: (101, 0, 0, 0, 16, Optional((19, 84)), 17)
|
|
print(ff())
|
|
|
|
//
|
|
// Concrete subclass of generic subclass of @objc class
|
|
//
|
|
|
|
class B : A<(Int, Int)> {
|
|
override var description: String {
|
|
return "Salmon"
|
|
}
|
|
|
|
@nonobjc override func calculatePrice() -> Int {
|
|
return 1675
|
|
}
|
|
}
|
|
|
|
class BB : B {}
|
|
|
|
class C : A<(Int, Int)>, PP {
|
|
@nonobjc override var description: String {
|
|
return "Invisible Chicken"
|
|
}
|
|
|
|
override func calculatePrice() -> Int {
|
|
return 650
|
|
}
|
|
|
|
func calculateTaxes() -> Int {
|
|
return 110
|
|
}
|
|
}
|
|
|
|
// CHECK: 400
|
|
// CHECK: 400
|
|
// CHECK: 650
|
|
// CHECK: 110
|
|
print((BB() as P).calculatePrice())
|
|
print((B() as P).calculatePrice())
|
|
print((C() as P).calculatePrice())
|
|
print((C() as PP).calculateTaxes())
|
|
|
|
// CHECK: Salmon
|
|
// CHECK: Grilled artichokes
|
|
print((B() as NSObject).description)
|
|
print((C() as NSObject).description)
|
|
|
|
let b = B()
|
|
let g = { (b.x, b.y, b.z, b.t, b.first, b.second, b.third) }
|
|
|
|
// CHECK: (0, 0, 0, 0, 16, nil, 61)
|
|
print(g())
|
|
|
|
b.x = 101
|
|
b.second = (19, 84)
|
|
b.third = 17
|
|
|
|
// CHECK: (101, 0, 0, 0, 16, Optional((19, 84)), 17)
|
|
print(g())
|
|
|
|
//
|
|
// Generic subclass of @objc class without any generically-sized members
|
|
//
|
|
|
|
class FixedA<T> : HasHiddenIvars, P {
|
|
var first: Int = 16
|
|
var second: [T] = []
|
|
var third: Int = 61
|
|
|
|
override var description: String {
|
|
return "Grilled artichokes"
|
|
}
|
|
|
|
func calculatePrice() -> Int {
|
|
return 400
|
|
}
|
|
}
|
|
|
|
let fixedA = FixedA<Int>()
|
|
|
|
// CHECK: Grilled artichokes
|
|
// CHECK: Grilled artichokes
|
|
print(fixedA.description)
|
|
print((fixedA as NSObject).description)
|
|
|
|
let fixedF = { (fixedA.x, fixedA.y, fixedA.z, fixedA.t, fixedA.first, fixedA.second, fixedA.third) }
|
|
|
|
// CHECK: (0, 0, 0, 0, 16, [], 61)
|
|
print(fixedF())
|
|
|
|
// CHECK: (25, 225, 255, 2255, 16, [], 61)
|
|
fixedA.x = 25
|
|
fixedA.y = 225
|
|
fixedA.z = 255
|
|
fixedA.t = 2255
|
|
print(fixedF())
|
|
|
|
// CHECK: (36, 225, 255, 2255, 16, [], 61)
|
|
fixedA.x = 36
|
|
print(fixedF())
|
|
|
|
// CHECK: (36, 225, 255, 2255, 16, [121], 61)
|
|
fixedA.second = [121]
|
|
print(fixedF())
|
|
|
|
//
|
|
// Instantiate the class with a different set of generic parameters
|
|
//
|
|
|
|
let fixedAA = FixedA<(Int, Int)>()
|
|
let fixedFF = { (fixedAA.x, fixedAA.y, fixedAA.z, fixedAA.t, fixedAA.first, fixedAA.second, fixedAA.third) }
|
|
|
|
// CHECK: (0, 0, 0, 0, 16, [], 61)
|
|
print(fixedFF())
|
|
|
|
fixedAA.x = 101
|
|
fixedAA.second = [(19, 84)]
|
|
fixedAA.third = 17
|
|
|
|
// CHECK: (101, 0, 0, 0, 16, [(19, 84)], 17)
|
|
print(fixedFF())
|
|
|
|
//
|
|
// Concrete subclass of generic subclass of @objc class
|
|
// without any generically-sized members
|
|
//
|
|
|
|
class FixedB : FixedA<Int> {
|
|
override var description: String {
|
|
return "Salmon"
|
|
}
|
|
|
|
override func calculatePrice() -> Int {
|
|
return 1675
|
|
}
|
|
}
|
|
|
|
// CHECK: 675
|
|
print((FixedB() as P).calculatePrice())
|
|
|
|
// CHECK: Salmon
|
|
print((FixedB() as NSObject).description)
|
|
|
|
let fixedB = FixedB()
|
|
let fixedG = { (fixedB.x, fixedB.y, fixedB.z, fixedB.t, fixedB.first, fixedB.second, fixedB.third) }
|
|
|
|
// CHECK: (0, 0, 0, 0, 16, [], 61)
|
|
print(fixedG())
|
|
|
|
fixedB.x = 101
|
|
fixedB.second = [19, 84]
|
|
fixedB.third = 17
|
|
|
|
// CHECK: (101, 0, 0, 0, 16, [19, 84], 17)
|
|
print(fixedG())
|
|
|
|
// Problem with field alignment in direct generic subclass of NSObject -
|
|
// <https://bugs.swift.org/browse/SR-2586>
|
|
public class PandorasBox<T>: NSObject {
|
|
final public var value: T
|
|
|
|
public init(_ value: T) {
|
|
// Uses ConstantIndirect access pattern
|
|
self.value = value
|
|
}
|
|
}
|
|
|
|
let c = PandorasBox(30)
|
|
// CHECK: 30
|
|
// Uses ConstantDirect access pattern
|
|
print(c.value)
|
|
|
|
// Super method calls from a generic subclass of an @objc class
|
|
class HasDynamicMethod : NSObject {
|
|
@objc dynamic class func funkyTown() {
|
|
print("Here we are with \(self)")
|
|
}
|
|
}
|
|
|
|
class GenericOverrideOfDynamicMethod<T> : HasDynamicMethod {
|
|
override class func funkyTown() {
|
|
print("Hello from \(self) with T = \(T.self)")
|
|
super.funkyTown()
|
|
print("Goodbye from \(self) with T = \(T.self)")
|
|
}
|
|
}
|
|
|
|
class ConcreteOverrideOfDynamicMethod : GenericOverrideOfDynamicMethod<Int> {
|
|
override class func funkyTown() {
|
|
print("Hello from \(self)")
|
|
super.funkyTown()
|
|
print("Goodbye from \(self)")
|
|
}
|
|
}
|
|
|
|
// CHECK: Hello from ConcreteOverrideOfDynamicMethod
|
|
// CHECK: Hello from ConcreteOverrideOfDynamicMethod with T = Int
|
|
// CHECK: Here we are with ConcreteOverrideOfDynamicMethod
|
|
// CHECK: Goodbye from ConcreteOverrideOfDynamicMethod with T = Int
|
|
// CHECK: Goodbye from ConcreteOverrideOfDynamicMethod
|
|
ConcreteOverrideOfDynamicMethod.funkyTown()
|
|
|
|
class Foo {}
|
|
class Bar {}
|
|
class DependOnAlignOf<T> : HasHiddenIvars2 {
|
|
var first = Foo()
|
|
var second = Bar()
|
|
var third: T?
|
|
}
|
|
|
|
let ad = DependOnAlignOf<Double>()
|
|
let ai = DependOnAlignOf<Int>()
|
|
|
|
do {
|
|
let fd = { (ad.x, ad.first, ad.second, ad.third) }
|
|
let fi = { (ai.x, ai.first, ai.second, ai.third) }
|
|
|
|
// CHECK: (nil, a.Foo, a.Bar, nil)
|
|
print(fd())
|
|
|
|
// CHECK: (nil, a.Foo, a.Bar, nil)
|
|
print(fi())
|
|
}
|
|
|
|
// Same as above, but there's another class in between the
|
|
// Objective-C class and us
|
|
class HasHiddenIvars3 : HasHiddenIvars2 { }
|
|
|
|
class AlsoDependOnAlignOf<T> : HasHiddenIvars3 {
|
|
var first = Foo()
|
|
var second = Bar()
|
|
var third: T?
|
|
}
|
|
|
|
do {
|
|
let ad = AlsoDependOnAlignOf<Double>()
|
|
let ai = AlsoDependOnAlignOf<Int>()
|
|
|
|
let fd = { (ad.x, ad.first, ad.second, ad.third) }
|
|
let fi = { (ai.x, ai.first, ai.second, ai.third) }
|
|
|
|
// CHECK: (nil, a.Foo, a.Bar, nil)
|
|
print(fd())
|
|
|
|
// CHECK: (nil, a.Foo, a.Bar, nil)
|
|
print(fi())
|
|
}
|