Files
swift-mirror/test/stdlib/KeyPathImplementation.swift
Joe Groff 4c2dde56a0 IRGen: Lower external key path components.
The key path pattern needs to include a reference to the external descriptor, along with hooks for lowering its type arguments and indices, if any. The runtime will need to instantiate and interpolate the external component when the key path object is instantiated.

While we're here, let's also reserve some more component header bytes for future expansion, since this is an ABI we're going to be living with for a while.
2018-02-23 19:03:15 -08:00

1105 lines
38 KiB
Swift

// RUN: %empty-directory(%t)
// RUN: %target-build-swift %s -g -o %t/a.out
// RUN: %target-run %t/a.out
// REQUIRES: executable_test
import StdlibUnittest
var keyPathImpl = TestSuite("key path implementation")
func align<T>(_ offset: Int, to: T.Type) -> Int {
let alignMask = MemoryLayout<T>.alignment - 1
return (offset + alignMask) & ~alignMask
}
let classHeaderSize = MemoryLayout<Int>.size * 2
class C<T> {
var x: Int
var y: LifetimeTracked?
var z: T
init(x: Int, y: LifetimeTracked?, z: T) {
self.x = x
self.y = y
self.z = z
}
static var x_offset: Int { return classHeaderSize }
static var y_offset: Int { return x_offset + MemoryLayout<Int>.size }
static var z_offset: Int {
return align(y_offset + MemoryLayout<LifetimeTracked?>.size, to: T.self)
}
}
struct Point: Equatable {
var x: Double
var y: Double
var trackLifetime = LifetimeTracked(123)
init(x: Double, y: Double) {
self.x = x
self.y = y
}
static func ==(a: Point, b: Point) -> Bool {
return a.x == b.x && a.y == b.y
}
static var x_offset: Int {
return 0
}
static var y_offset: Int {
return MemoryLayout<Double>.size
}
}
struct S<T: Equatable>: Equatable {
var x: Int
var y: LifetimeTracked?
var z: T
var p: Point
var c: C<T>
static func ==(a: S, b: S) -> Bool {
return a.x == b.x
&& a.y === b.y
&& a.z == b.z
&& a.p == b.p
&& a.c === b.c
}
static var x_offset: Int { return 0 }
static var y_offset: Int { return MemoryLayout<Int>.size }
static var z_offset: Int {
return align(y_offset + MemoryLayout<LifetimeTracked?>.size,
to: T.self)
}
static var p_offset: Int {
return align(z_offset + MemoryLayout<T>.size, to: Point.self)
}
static var c_offset: Int {
return p_offset + MemoryLayout<Point>.size
}
}
class Oroborous {
var o: Oroborous
init() { fatalError() }
}
struct CratePair<T, U> {
var left: Crate<T>
var right: Crate<U>
static var left_offset: Int { return 0 }
static var right_offset: Int { return MemoryLayout<Crate<T>>.size }
}
class Crate<T> {
var value: T
init(value: T) { self.value = value }
static var value_offset: Int { return align(classHeaderSize, to: T.self) }
}
struct ComputedArgumentWitnesses {
typealias Destroy = @convention(thin)
(_ instanceArguments: UnsafeMutableRawPointer, _ size: Int) -> ()
typealias Copy = @convention(thin)
(_ srcInstanceArguments: UnsafeRawPointer,
_ destInstanceArguments: UnsafeMutableRawPointer,
_ size: Int) -> ()
typealias Equals = @convention(thin)
(_ xInstanceArguments: UnsafeRawPointer,
_ yInstanceArguments: UnsafeRawPointer,
_ size: Int) -> Bool
typealias Hash = @convention(thin)
(_ instanceArguments: UnsafeRawPointer,
_ size: Int) -> Int
let destroy: Destroy?
let copy: Copy
let equals: Equals
let hash: Hash
}
// Helper to build keypaths with specific layouts
struct TestKeyPathBuilder {
var buffer: UnsafeMutableRawBufferPointer
var state: State = .header
var hasReferencePrefix = false
init(buffer: UnsafeMutableRawBufferPointer) {
self.buffer = buffer
}
enum State {
case header, component, type
mutating func advance() {
switch self {
case .header, .type: self = .component
case .component: self = .type
}
}
}
func finish() {
assert(buffer.count == 0,
"Did not fill entire buffer")
assert(state == .type, "should end expecting a type")
assert(!hasReferencePrefix, "unterminated reference prefix")
}
mutating func push(_ value: UInt32) {
buffer.storeBytes(of: value, as: UInt32.self)
buffer = .init(start: buffer.baseAddress! + 4, count: buffer.count - 4)
}
mutating func push(_ value: Any.Type) {
pushWord(value)
}
mutating func pushWord<T>(_ value: T) {
precondition(_isPOD(T.self) && MemoryLayout<T>.size == MemoryLayout<Int>.size)
var misalign = Int(bitPattern: buffer.baseAddress) % MemoryLayout<Int>.alignment
if misalign != 0 {
misalign = MemoryLayout<Int>.alignment - misalign
buffer = .init(start: buffer.baseAddress! + misalign,
count: buffer.count - misalign)
}
buffer.storeBytes(of: value, as: T.self)
buffer = .init(start: buffer.baseAddress! + MemoryLayout<Int>.size,
count: buffer.count - MemoryLayout<Int>.size)
}
mutating func addHeader(trivial: Bool, hasReferencePrefix: Bool) {
assert(state == .header, "not expecting a header")
let size = buffer.count - MemoryLayout<Int>.size
assert(buffer.count > 0 && buffer.count <= 0x3FFF_FFFF,
"invalid buffer size")
let header: UInt32 = UInt32(size)
| (trivial ? 0x8000_0000 : 0)
| (hasReferencePrefix ? 0x4000_0000 : 0)
push(header)
if MemoryLayout<Int>.size == 8 {
push(0)
}
self.hasReferencePrefix = hasReferencePrefix
state.advance()
}
mutating func addOffsetComponent(offset: Int,
kindMask: UInt32,
forceOverflow: Bool,
endsReferencePrefix: Bool) {
assert(state == .component, "not expecting a component")
assert(offset >= 0 && offset <= 0x7FFF_FFFF,
"invalid offset")
let referencePrefixMask: UInt32 = endsReferencePrefix ? 0x8000_0000 : 0
if forceOverflow || offset >= 0x00FF_FFFF {
// Offset is overflowed into another word
push(referencePrefixMask | kindMask | 0x00FF_FFFF)
push(UInt32(offset))
} else {
// Offset is packed in-line
push(referencePrefixMask | kindMask | UInt32(offset))
}
if endsReferencePrefix {
assert(hasReferencePrefix, "ending nonexistent reference prefix")
hasReferencePrefix = false
}
state.advance()
}
mutating func addStructComponent(offset: Int,
forceOverflow: Bool = false,
endsReferencePrefix: Bool = false) {
addOffsetComponent(offset: offset, kindMask: 0,
forceOverflow: forceOverflow,
endsReferencePrefix: endsReferencePrefix)
}
mutating func addClassComponent(offset: Int,
forceOverflow: Bool = false,
endsReferencePrefix: Bool = false) {
addOffsetComponent(offset: offset, kindMask: 0x0200_0000,
forceOverflow: forceOverflow,
endsReferencePrefix: endsReferencePrefix)
}
mutating func addGetterComponent(
id: UnsafeRawPointer,
getter: UnsafeRawPointer,
witnesses: UnsafePointer<ComputedArgumentWitnesses>,
args: UnsafeRawBufferPointer,
endsReferencePrefix: Bool = false
) {
assert(state == .component, "not expecting a component")
push(0x0108_0000 | (endsReferencePrefix ? 0x8000_0000 : 0))
pushWord(id)
pushWord(getter)
pushWord(args.count)
pushWord(witnesses)
buffer.copyMemory(from: args)
buffer = .init(start: buffer.baseAddress! + args.count,
count: buffer.count - args.count)
if endsReferencePrefix {
assert(hasReferencePrefix, "ending nonexistent reference prefix")
hasReferencePrefix = false
}
state.advance()
}
mutating func addType(_ type: Any.Type) {
assert(state == .type, "not expecting a type")
push(type)
state.advance()
}
}
extension AnyKeyPath {
static func build(capacityInBytes: Int,
withBuilder: (inout TestKeyPathBuilder) -> Void) -> Self {
return _create(capacityInBytes: capacityInBytes) {
var builder = TestKeyPathBuilder(buffer: $0)
withBuilder(&builder)
builder.finish()
}
}
}
keyPathImpl.test("struct components") {
let s_x = WritableKeyPath<S<String>, Int>
.build(capacityInBytes: MemoryLayout<Int>.size + 4) {
$0.addHeader(trivial: true, hasReferencePrefix: false)
$0.addStructComponent(offset: S<String>.x_offset)
}
let s_y = WritableKeyPath<S<String>, LifetimeTracked?>
.build(capacityInBytes: MemoryLayout<Int>.size + 4) {
$0.addHeader(trivial: true, hasReferencePrefix: false)
$0.addStructComponent(offset: S<String>.y_offset)
}
let s_z = WritableKeyPath<S<String>, String>
.build(capacityInBytes: MemoryLayout<Int>.size + 4) {
$0.addHeader(trivial: true, hasReferencePrefix: false)
$0.addStructComponent(offset: S<String>.z_offset)
}
let s_p = WritableKeyPath<S<String>, Point>
.build(capacityInBytes: MemoryLayout<Int>.size + 4) {
$0.addHeader(trivial: true, hasReferencePrefix: false)
$0.addStructComponent(offset: S<String>.p_offset)
}
let twoComponentSize = MemoryLayout<Int>.size * 3 + 4
let s_p_x = WritableKeyPath<S<String>, Double>
.build(capacityInBytes: twoComponentSize) {
$0.addHeader(trivial: true, hasReferencePrefix: false)
$0.addStructComponent(offset: S<String>.p_offset)
$0.addType(Point.self)
$0.addStructComponent(offset: Point.x_offset)
}
let s_p_y = WritableKeyPath<S<String>, Double>
.build(capacityInBytes: twoComponentSize) {
$0.addHeader(trivial: true, hasReferencePrefix: false)
$0.addStructComponent(offset: S<String>.p_offset)
$0.addType(Point.self)
$0.addStructComponent(offset: Point.y_offset)
}
// \("") forces the string to be computed at runtime (and therefore allocated)
let c = C(x: 679, y: nil, z: "buffalo\("")")
var value = S(x: 1738, y: nil, z: "bottles of beer\("")",
p: .init(x: 0.5, y: -0.5), c: c)
expectEqual(value[keyPath: s_x], 1738)
value[keyPath: s_x] = 679
expectEqual(value[keyPath: s_x], 679)
expectTrue(value[keyPath: s_y] === nil)
let object1 = LifetimeTracked(1739)
value[keyPath: s_y] = object1
expectTrue(value[keyPath: s_y] === object1)
expectTrue(value.y === object1)
let object2 = LifetimeTracked(1740)
value[keyPath: s_y] = object2
expectTrue(value[keyPath: s_y] === object2)
expectTrue(value.y === object2)
expectEqual(value[keyPath: s_z], "bottles of beer")
value[keyPath: s_z] = "cans of lemonade\("")"
expectEqual(value[keyPath: s_z], "cans of lemonade")
expectEqual(value.z, "cans of lemonade")
expectEqual(value[keyPath: s_p], Point(x: 0.5, y: -0.5))
expectEqual(value.p, Point(x: 0.5, y: -0.5))
expectEqual(value[keyPath: s_p_x], 0.5)
expectEqual(value.p.x, 0.5)
expectEqual(value[keyPath: s_p_y], -0.5)
expectEqual(value.p.y, -0.5)
value[keyPath: s_p] = Point(x: -3.0, y: 4.0)
expectEqual(value[keyPath: s_p], Point(x: -3.0, y: 4.0))
expectEqual(value.p, Point(x: -3.0, y: 4.0))
expectEqual(value[keyPath: s_p_x], -3.0)
expectEqual(value.p.x, -3.0)
expectEqual(value[keyPath: s_p_y], 4.0)
expectEqual(value.p.y, 4.0)
value[keyPath: s_p_x] = 5.0
expectEqual(value[keyPath: s_p], Point(x: 5.0, y: 4.0))
expectEqual(value.p, Point(x: 5.0, y: 4.0))
expectEqual(value[keyPath: s_p_x], 5.0)
expectEqual(value.p.x, 5.0)
expectEqual(value[keyPath: s_p_y], 4.0)
expectEqual(value.p.y, 4.0)
value[keyPath: s_p_y] = -11.0
expectEqual(value[keyPath: s_p], Point(x: 5.0, y: -11.0))
expectEqual(value.p, Point(x: 5.0, y: -11.0))
expectEqual(value[keyPath: s_p_x], 5.0)
expectEqual(value.p.x, 5.0)
expectEqual(value[keyPath: s_p_y], -11.0)
expectEqual(value.p.y, -11.0)
value[keyPath: s_p].x = 65.0
expectEqual(value[keyPath: s_p], Point(x: 65.0, y: -11.0))
expectEqual(value.p, Point(x: 65.0, y: -11.0))
expectEqual(value[keyPath: s_p_x], 65.0)
expectEqual(value.p.x, 65.0)
expectEqual(value[keyPath: s_p_y], -11.0)
expectEqual(value.p.y, -11.0)
}
keyPathImpl.test("class components") {
let c_x = ReferenceWritableKeyPath<C<String>, Int>
.build(capacityInBytes: MemoryLayout<Int>.size + 4) {
$0.addHeader(trivial: true, hasReferencePrefix: false)
$0.addClassComponent(offset: C<String>.x_offset)
}
let c_y = ReferenceWritableKeyPath<C<String>, LifetimeTracked?>
.build(capacityInBytes: MemoryLayout<Int>.size + 4) {
$0.addHeader(trivial: true, hasReferencePrefix: false)
$0.addClassComponent(offset: C<String>.y_offset)
}
let c_z = ReferenceWritableKeyPath<C<String>, String>
.build(capacityInBytes: MemoryLayout<Int>.size + 4) {
$0.addHeader(trivial: true, hasReferencePrefix: false)
$0.addClassComponent(offset: C<String>.z_offset)
}
let c = C(x: 679, y: nil, z: "buffalo\("")")
let value = c
expectEqual(value[keyPath: c_x], 679)
value[keyPath: c_x] = 1738
expectEqual(value[keyPath: c_x], 1738)
expectEqual(value.x, 1738)
expectTrue(value[keyPath: c_y] === nil)
let object1 = LifetimeTracked(680)
value[keyPath: c_y] = object1
expectTrue(value[keyPath: c_y] === object1)
expectTrue(value.y === object1)
let object2 = LifetimeTracked(681)
value[keyPath: c_y] = object2
expectTrue(value[keyPath: c_y] === object2)
expectTrue(value.y === object2)
expectEqual(value[keyPath: c_z], "buffalo")
value[keyPath: c_z] = "water buffalo\("")"
expectEqual(value[keyPath: c_z], "water buffalo")
expectEqual(value.z, "water buffalo")
var mutValue = value
let value_c_x: WritableKeyPath = c_x
let value_c_y: WritableKeyPath = c_y
expectEqual(value[keyPath: value_c_x], 1738)
mutValue[keyPath: value_c_x] = 86
expectTrue(value === mutValue)
expectEqual(value[keyPath: c_x], 86)
expectEqual(value[keyPath: value_c_x], 86)
expectEqual(value.x, 86)
expectTrue(value[keyPath: value_c_y] === object2)
mutValue[keyPath: value_c_y] = object1
expectTrue(value === mutValue)
expectTrue(value[keyPath: c_y] === object1)
expectTrue(value[keyPath: value_c_y] === object1)
expectTrue(value.y === object1)
}
keyPathImpl.test("reference prefix") {
let s_c_x = ReferenceWritableKeyPath<S<String>, Int>
.build(capacityInBytes: 3 * MemoryLayout<Int>.size + 4) {
$0.addHeader(trivial: true, hasReferencePrefix: true)
$0.addStructComponent(offset: S<String>.c_offset,
endsReferencePrefix: true)
$0.addType(C<String>.self)
$0.addClassComponent(offset: C<String>.x_offset)
}
let s_c_y = ReferenceWritableKeyPath<S<String>, LifetimeTracked?>
.build(capacityInBytes: 3 * MemoryLayout<Int>.size + 4) {
$0.addHeader(trivial: true, hasReferencePrefix: true)
$0.addStructComponent(offset: S<String>.c_offset,
endsReferencePrefix: true)
$0.addType(C<String>.self)
$0.addClassComponent(offset: C<String>.y_offset)
}
let s_c_z = ReferenceWritableKeyPath<S<String>, String>
.build(capacityInBytes: 3 * MemoryLayout<Int>.size + 4) {
$0.addHeader(trivial: true, hasReferencePrefix: true)
$0.addStructComponent(offset: S<String>.c_offset,
endsReferencePrefix: true)
$0.addType(C<String>.self)
$0.addClassComponent(offset: C<String>.z_offset)
}
let c = C(x: 679, y: nil, z: "buffalo\("")")
let value = S(x: 1738, y: nil, z: "bottles of beer\("")",
p: .init(x: 0.5, y: -0.5), c: c)
expectEqual(value[keyPath: s_c_x], 679)
value[keyPath: s_c_x] = 6
expectEqual(value[keyPath: s_c_x], 6)
expectEqual(value.c.x, 6)
expectTrue(value.c.y === nil)
expectEqual(value.c.z, "buffalo")
let object1 = LifetimeTracked(7)
let object2 = LifetimeTracked(8)
expectTrue(value[keyPath: s_c_y] === nil)
value[keyPath: s_c_y] = object1
expectTrue(value[keyPath: s_c_y] === object1)
expectTrue(value.c.y === object1)
expectEqual(value.c.x, 6)
expectEqual(value.c.z, "buffalo")
value[keyPath: s_c_y] = object2
expectTrue(value[keyPath: s_c_y] === object2)
expectTrue(value.c.y === object2)
expectEqual(value.c.x, 6)
expectEqual(value.c.z, "buffalo")
expectEqual(value[keyPath: s_c_z], "buffalo")
value[keyPath: s_c_z] = "antelope"
expectTrue(value[keyPath: s_c_z] == "antelope")
expectTrue(value.c.z == "antelope")
expectEqual(value.c.x, 6)
expectTrue(value.c.y === object2)
let value_s_c_x: WritableKeyPath = s_c_x
let value_s_c_y: WritableKeyPath = s_c_y
let value_s_c_z: WritableKeyPath = s_c_z
var mutValue = value
mutValue[keyPath: value_s_c_x] = 7
expectEqual(value, mutValue)
expectEqual(value[keyPath: s_c_x], 7)
expectEqual(value.c.x, 7)
expectTrue(value.c.y === object2)
expectEqual(value.c.z, "antelope")
mutValue[keyPath: value_s_c_y] = object1
expectEqual(value, mutValue)
expectTrue(value[keyPath: s_c_y] === object1)
expectTrue(value.c.y === object1)
expectEqual(value.c.x, 7)
expectEqual(value.c.z, "antelope")
mutValue[keyPath: value_s_c_z] = "elk"
expectEqual(value, mutValue)
expectTrue(value[keyPath: s_c_z] == "elk")
expectTrue(value.c.z == "elk")
expectEqual(value.c.x, 7)
expectTrue(value.c.y === object1)
}
keyPathImpl.test("overflowed offsets") {
let s_p = WritableKeyPath<S<String>, Point>
.build(capacityInBytes: MemoryLayout<Int>.size + 8) {
$0.addHeader(trivial: true, hasReferencePrefix: false)
$0.addStructComponent(offset: S<String>.p_offset,
forceOverflow: true)
}
let c_z = ReferenceWritableKeyPath<C<String>, String>
.build(capacityInBytes: MemoryLayout<Int>.size + 8) {
$0.addHeader(trivial: true, hasReferencePrefix: false)
$0.addClassComponent(offset: C<String>.z_offset,
forceOverflow: true)
}
let c = C(x: 679, y: LifetimeTracked(42), z: "buffalo\("")")
var sValue = S(x: 1738, y: LifetimeTracked(43),
z: "bottles of beer\("")",
p: .init(x: 0.5, y: -0.5), c: c)
let cValue = c
expectEqual(sValue[keyPath: s_p], Point(x: 0.5, y: -0.5))
sValue[keyPath: s_p] = Point(x: 1.0, y: -1.0)
expectEqual(sValue.p, Point(x: 1.0, y: -1.0))
expectEqual(sValue[keyPath: s_p], Point(x: 1.0, y: -1.0))
expectEqual(cValue[keyPath: c_z], "buffalo")
cValue[keyPath: c_z] = "dik dik"
expectEqual(cValue.z, "dik dik")
expectEqual(cValue[keyPath: c_z], "dik dik")
}
keyPathImpl.test("equality") {
let s_c_z_p_x = ReferenceWritableKeyPath<S<S<String>>, Double>
.build(capacityInBytes: 7 * MemoryLayout<Int>.size + 4) {
$0.addHeader(trivial: true, hasReferencePrefix: true)
// S<S<String>>.c
$0.addStructComponent(offset: S<S<String>>.c_offset,
endsReferencePrefix: true)
$0.addType(C<S<String>>.self)
// C<S<String>>.z
$0.addClassComponent(offset: C<S<String>>.z_offset)
$0.addType(S<String>.self)
// S<String>.p
$0.addStructComponent(offset: S<String>.p_offset)
$0.addType(Point.self)
// Point.x
$0.addStructComponent(offset: Point.x_offset)
}
expectEqual(s_c_z_p_x, s_c_z_p_x)
expectEqual(s_c_z_p_x.hashValue, s_c_z_p_x.hashValue)
// Structurally equivalent to s_c_z_p_x
let s_c_z_p_x_2 = ReferenceWritableKeyPath<S<S<String>>, Double>
.build(capacityInBytes: 7 * MemoryLayout<Int>.size + 4) {
$0.addHeader(trivial: true, hasReferencePrefix: true)
// S<S<String>>.c
$0.addStructComponent(offset: S<S<String>>.c_offset,
endsReferencePrefix: true)
$0.addType(C<S<String>>.self)
// C<S<String>>.z
$0.addClassComponent(offset: C<S<String>>.z_offset)
$0.addType(S<String>.self)
// S<String>.p
$0.addStructComponent(offset: S<String>.p_offset)
$0.addType(Point.self)
// Point.x
$0.addStructComponent(offset: Point.x_offset)
}
expectEqual(s_c_z_p_x, s_c_z_p_x_2)
expectEqual(s_c_z_p_x.hashValue, s_c_z_p_x_2.hashValue)
expectEqual(s_c_z_p_x_2, s_c_z_p_x)
expectEqual(s_c_z_p_x_2.hashValue, s_c_z_p_x.hashValue)
// Structurally equivalent, force-overflowed offset components
let s_c_z_p_x_3 = ReferenceWritableKeyPath<S<S<String>>, Double>
.build(capacityInBytes: 4 * MemoryLayout<Int>.size + 4 * 8) {
$0.addHeader(trivial: true, hasReferencePrefix: true)
// S<S<String>>.c
$0.addStructComponent(offset: S<S<String>>.c_offset,
forceOverflow: true,
endsReferencePrefix: true)
$0.addType(C<S<String>>.self)
// C<S<String>>.z
$0.addClassComponent(offset: C<S<String>>.z_offset,
forceOverflow: true)
$0.addType(S<String>.self)
// S<String>.p
$0.addStructComponent(offset: S<String>.p_offset,
forceOverflow: true)
$0.addType(Point.self)
// Point.x
$0.addStructComponent(offset: Point.x_offset,
forceOverflow: true)
}
expectEqual(s_c_z_p_x, s_c_z_p_x_3)
expectEqual(s_c_z_p_x.hashValue, s_c_z_p_x_3.hashValue)
expectEqual(s_c_z_p_x_3, s_c_z_p_x)
expectEqual(s_c_z_p_x_3.hashValue, s_c_z_p_x.hashValue)
// Same path type, different suffixes
let s_c_z_p_y = ReferenceWritableKeyPath<S<S<String>>, Double>
.build(capacityInBytes: 7 * MemoryLayout<Int>.size + 4) {
$0.addHeader(trivial: true, hasReferencePrefix: true)
// S<S<String>>.c
$0.addStructComponent(offset: S<S<String>>.c_offset,
endsReferencePrefix: true)
$0.addType(C<S<String>>.self)
// C<S<String>>.z
$0.addClassComponent(offset: C<S<String>>.z_offset)
$0.addType(S<String>.self)
// S<String>.p
$0.addStructComponent(offset: S<String>.p_offset)
$0.addType(Point.self)
// Point.y
$0.addStructComponent(offset: Point.y_offset)
}
expectNotEqual(s_c_z_p_x, s_c_z_p_y)
expectNotEqual(s_c_z_p_y, s_c_z_p_x)
// Different path type
let s_c_z_p = ReferenceWritableKeyPath<S<S<String>>, Point>
.build(capacityInBytes: 5 * MemoryLayout<Int>.size + 4) {
$0.addHeader(trivial: true, hasReferencePrefix: true)
// S<S<String>>.c
$0.addStructComponent(offset: S<S<String>>.c_offset,
endsReferencePrefix: true)
$0.addType(C<S<String>>.self)
// C<S<String>>.z
$0.addClassComponent(offset: C<S<String>>.z_offset)
$0.addType(S<String>.self)
// S<String>.p
$0.addStructComponent(offset: S<String>.p_offset)
}
expectNotEqual(s_c_z_p_x, s_c_z_p)
expectNotEqual(s_c_z_p, s_c_z_p_x)
// Same path, no reference prefix
let s_c_z_p_x_readonly = KeyPath<S<S<String>>, Double>
.build(capacityInBytes: 7 * MemoryLayout<Int>.size + 4) {
$0.addHeader(trivial: true, hasReferencePrefix: false)
// S<S<String>>.c
$0.addStructComponent(offset: S<S<String>>.c_offset)
$0.addType(C<S<String>>.self)
// C<S<String>>.z
$0.addClassComponent(offset: C<S<String>>.z_offset)
$0.addType(S<String>.self)
// S<String>.p
$0.addStructComponent(offset: S<String>.p_offset)
$0.addType(Point.self)
// Point.x
$0.addStructComponent(offset: Point.x_offset)
}
expectNotEqual(s_c_z_p_x, s_c_z_p_x_readonly)
expectNotEqual(s_c_z_p_x_readonly, s_c_z_p_x)
// Same path type, different paths
let s_p_y_readonly = KeyPath<S<S<String>>, Double>
.build(capacityInBytes: 3 * MemoryLayout<Int>.size + 4) {
$0.addHeader(trivial: true, hasReferencePrefix: false)
// S<S<String>>.p
$0.addStructComponent(offset: S<S<String>>.p_offset)
$0.addType(Point.self)
// Point.y
$0.addStructComponent(offset: Point.y_offset)
}
expectNotEqual(s_p_y_readonly, s_c_z_p_x_readonly)
expectNotEqual(s_c_z_p_x_readonly, s_p_y_readonly)
let o_o_o_o = ReferenceWritableKeyPath<Oroborous, Oroborous>
.build(capacityInBytes: 5 * MemoryLayout<Int>.size + 4) {
$0.addHeader(trivial: true, hasReferencePrefix: false)
// O.o
$0.addClassComponent(offset: classHeaderSize)
$0.addType(Oroborous.self)
// O.o
$0.addClassComponent(offset: classHeaderSize)
$0.addType(Oroborous.self)
// O.o
$0.addClassComponent(offset: classHeaderSize)
}
// Different reference prefix length
let o_o_o_o_rp1 = ReferenceWritableKeyPath<Oroborous, Oroborous>
.build(capacityInBytes: 5 * MemoryLayout<Int>.size + 4) {
$0.addHeader(trivial: true, hasReferencePrefix: true)
// O.o
$0.addClassComponent(offset: classHeaderSize,
endsReferencePrefix: true)
$0.addType(Oroborous.self)
// O.o
$0.addClassComponent(offset: classHeaderSize)
$0.addType(Oroborous.self)
// O.o
$0.addClassComponent(offset: classHeaderSize)
}
let o_o_o_o_rp2 = ReferenceWritableKeyPath<Oroborous, Oroborous>
.build(capacityInBytes: 5 * MemoryLayout<Int>.size + 4) {
$0.addHeader(trivial: true, hasReferencePrefix: true)
// O.o
$0.addClassComponent(offset: classHeaderSize)
$0.addType(Oroborous.self)
// O.o
$0.addClassComponent(offset: classHeaderSize,
endsReferencePrefix: true)
$0.addType(Oroborous.self)
// O.o
$0.addClassComponent(offset: classHeaderSize)
}
let o_o_o_o_rp2_2 = ReferenceWritableKeyPath<Oroborous, Oroborous>
.build(capacityInBytes: 5 * MemoryLayout<Int>.size + 4) {
$0.addHeader(trivial: true, hasReferencePrefix: true)
// O.o
$0.addClassComponent(offset: classHeaderSize)
$0.addType(Oroborous.self)
// O.o
$0.addClassComponent(offset: classHeaderSize,
endsReferencePrefix: true)
$0.addType(Oroborous.self)
// O.o
$0.addClassComponent(offset: classHeaderSize)
}
expectNotEqual(o_o_o_o, o_o_o_o_rp1)
expectNotEqual(o_o_o_o_rp1, o_o_o_o)
expectNotEqual(o_o_o_o_rp1, o_o_o_o_rp2)
expectNotEqual(o_o_o_o_rp2, o_o_o_o_rp1)
expectNotEqual(o_o_o_o, o_o_o_o_rp2)
expectNotEqual(o_o_o_o_rp2, o_o_o_o)
expectEqual(o_o_o_o_rp2, o_o_o_o_rp2_2)
expectEqual(o_o_o_o_rp2_2, o_o_o_o_rp2)
expectEqual(o_o_o_o_rp2.hashValue, o_o_o_o_rp2_2.hashValue)
// Same type, different length of components with same prefix
let o_o_o = ReferenceWritableKeyPath<Oroborous, Oroborous>
.build(capacityInBytes: 3 * MemoryLayout<Int>.size + 4) {
$0.addHeader(trivial: true, hasReferencePrefix: false)
// O.o
$0.addClassComponent(offset: classHeaderSize)
$0.addType(Oroborous.self)
// O.o
$0.addClassComponent(offset: classHeaderSize)
}
expectNotEqual(o_o_o, o_o_o_o)
expectNotEqual(o_o_o_o, o_o_o)
}
keyPathImpl.test("appending") {
let s_p = WritableKeyPath<S<String>, Point>
.build(capacityInBytes: MemoryLayout<Int>.size + 4) {
$0.addHeader(trivial: true, hasReferencePrefix: false)
$0.addStructComponent(offset: S<String>.p_offset)
}
let p_y = WritableKeyPath<Point, Double>
.build(capacityInBytes: MemoryLayout<Int>.size + 4) {
$0.addHeader(trivial: true, hasReferencePrefix: false)
$0.addStructComponent(offset: Point.y_offset)
}
let s_p_y = s_p.appending(path: p_y)
let c = C(x: 679, y: nil, z: "buffalo\("")")
var value = S(x: 1738, y: nil, z: "bottles of beer\("")",
p: .init(x: 0.5, y: -0.5), c: c)
expectEqual(value[keyPath: s_p_y], -0.5)
value[keyPath: s_p_y] = 4.0
expectEqual(value[keyPath: s_p_y], 4.0)
expectEqual(value.p.x, 0.5)
expectEqual(value.p.y, 4.0)
let s_p_y2 = s_p.appending(path: p_y)
expectEqual(s_p_y, s_p_y2)
expectEqual(s_p_y2, s_p_y)
expectEqual(s_p_y.hashValue, s_p_y2.hashValue)
let s_p_y_manual = WritableKeyPath<S<String>, Double>
.build(capacityInBytes: 3 * MemoryLayout<Int>.size + 4) {
$0.addHeader(trivial: true, hasReferencePrefix: false)
$0.addStructComponent(offset: S<String>.p_offset)
$0.addType(Point.self)
$0.addStructComponent(offset: Point.y_offset)
}
expectEqual(s_p_y, s_p_y_manual)
expectEqual(s_p_y_manual, s_p_y)
expectEqual(s_p_y.hashValue, s_p_y_manual.hashValue)
let c_z = ReferenceWritableKeyPath<C<S<String>>, S<String>>
.build(capacityInBytes: MemoryLayout<Int>.size + 4) {
$0.addHeader(trivial: true, hasReferencePrefix: false)
$0.addClassComponent(offset: C<S<String>>.z_offset)
}
let value2 = C(x: 17, y: LifetimeTracked(38), z: value)
let c_z_p_y = c_z.appending(path: s_p_y)
expectEqual(value2[keyPath: c_z_p_y], 4.0)
value2[keyPath: c_z_p_y] = 5.0
expectEqual(value2[keyPath: c_z_p_y], 5.0)
expectEqual(value2.z.p.y, 5.0)
expectEqual(value2.z.p.x, 0.5)
let c_z_p_y_manual = ReferenceWritableKeyPath<C<S<String>>, Double>
.build(capacityInBytes: 5 * MemoryLayout<Int>.size + 4) {
$0.addHeader(trivial: true, hasReferencePrefix: false)
$0.addClassComponent(offset: C<S<String>>.z_offset)
$0.addType(S<String>.self)
$0.addStructComponent(offset: S<String>.p_offset)
$0.addType(Point.self)
$0.addStructComponent(offset: Point.y_offset)
}
expectEqual(c_z_p_y, c_z_p_y_manual)
expectEqual(c_z_p_y_manual, c_z_p_y)
expectEqual(c_z_p_y.hashValue, c_z_p_y_manual.hashValue)
let s_c = WritableKeyPath<S<S<String>>, C<S<String>>>
.build(capacityInBytes: MemoryLayout<Int>.size + 4) {
$0.addHeader(trivial: true, hasReferencePrefix: false)
$0.addStructComponent(offset: S<S<String>>.c_offset)
}
let s_c_z_p_y = s_c.appending(path: c_z_p_y)
let value3 = S(x: 679, y: nil, z: value,
p: value.p, c: value2)
expectEqual(value3[keyPath: s_c_z_p_y], 5.0)
value3[keyPath: s_c_z_p_y] = 11.0
expectEqual(value3[keyPath: s_c_z_p_y], 11.0)
expectEqual(value2[keyPath: c_z_p_y], 11.0)
let s_c_z_p_y_manual = ReferenceWritableKeyPath<S<S<String>>, Double>
.build(capacityInBytes: 7 * MemoryLayout<Int>.size + 4) {
$0.addHeader(trivial: true, hasReferencePrefix: true)
$0.addStructComponent(offset: S<S<String>>.c_offset,
endsReferencePrefix: true)
$0.addType(C<S<String>>.self)
$0.addClassComponent(offset: C<S<String>>.z_offset)
$0.addType(S<String>.self)
$0.addStructComponent(offset: S<String>.p_offset)
$0.addType(Point.self)
$0.addStructComponent(offset: Point.y_offset)
}
expectEqual(s_c_z_p_y, s_c_z_p_y_manual)
expectEqual(s_c_z_p_y_manual, s_c_z_p_y)
expectEqual(s_c_z_p_y_manual.hashValue, s_c_z_p_y.hashValue)
typealias CP = CratePair<S<S<String>>, Int>
let cratePair_left_value = ReferenceWritableKeyPath<CP, S<S<String>>>
.build(capacityInBytes: 3 * MemoryLayout<Int>.size + 4) {
$0.addHeader(trivial: true, hasReferencePrefix: true)
$0.addStructComponent(offset: CratePair<S<S<String>>, Int>.left_offset,
endsReferencePrefix: true)
$0.addType(Crate<S<S<String>>>.self)
$0.addClassComponent(offset: Crate<S<S<String>>>.value_offset)
}
let cratePair_left_value_c_z_p_y
= cratePair_left_value.appending(path: s_c_z_p_y)
let crate1 = Crate(value: value3)
let crate2 = Crate(value: 9)
let cratePair = CratePair(left: crate1, right: crate2)
expectEqual(cratePair[keyPath: cratePair_left_value_c_z_p_y], 11.0)
cratePair[keyPath: cratePair_left_value_c_z_p_y] = 13.0
expectEqual(cratePair[keyPath: cratePair_left_value_c_z_p_y], 13.0)
expectEqual(value3[keyPath: s_c_z_p_y], 13.0)
expectEqual(value2[keyPath: c_z_p_y], 13.0)
let cratePair_left_value_c_z_p_y_manual
= ReferenceWritableKeyPath<CP, Double>
.build(capacityInBytes: 11 * MemoryLayout<Int>.size + 4) {
$0.addHeader(trivial: true, hasReferencePrefix: true)
$0.addStructComponent(offset: CP.left_offset)
$0.addType(Crate<S<S<String>>>.self)
$0.addClassComponent(offset: Crate<S<S<String>>>.value_offset)
$0.addType(S<S<String>>.self)
$0.addStructComponent(offset: S<S<String>>.c_offset,
endsReferencePrefix: true)
$0.addType(C<S<String>>.self)
$0.addClassComponent(offset: C<S<String>>.z_offset)
$0.addType(S<String>.self)
$0.addStructComponent(offset: S<String>.p_offset)
$0.addType(Point.self)
$0.addStructComponent(offset: Point.y_offset)
}
expectEqual(cratePair_left_value_c_z_p_y,
cratePair_left_value_c_z_p_y_manual)
expectEqual(cratePair_left_value_c_z_p_y_manual,
cratePair_left_value_c_z_p_y)
expectEqual(cratePair_left_value_c_z_p_y_manual.hashValue,
cratePair_left_value_c_z_p_y.hashValue)
}
var numberOfDestroyOperations = 0
func testDestroy(_ data: UnsafeMutableRawPointer, _ count: Int) {
numberOfDestroyOperations += 1
}
var numberOfCopyOperations = 0
func testCopy(from: UnsafeRawPointer,
to: UnsafeMutableRawPointer,
count: Int) {
numberOfCopyOperations += 1
to.copyMemory(from: from, byteCount: count)
}
var numberOfEqualsOperations = 0
func testEquals(_ a: UnsafeRawPointer,
_ b: UnsafeRawPointer,
_ count: Int) -> Bool {
numberOfEqualsOperations += 1
return UnsafeRawBufferPointer(start: a, count: count)
.elementsEqual(UnsafeRawBufferPointer(start: b, count: count))
}
var numberOfHashOperations = 0
func testHash(_ a: UnsafeRawPointer, _ count: Int) -> Int {
numberOfHashOperations += 1
// Don't use this hash function at home
return count
}
var testWitnesses = ComputedArgumentWitnesses(
destroy: testDestroy,
copy: testCopy,
equals: testEquals,
hash: testHash)
protocol IntP { var int: Int { get } }
extension Int: IntP { var int: Int { return self } }
struct TestGetter {
// Hack: force address-only-ness so that the type is passed and returned
// indirectly, compatible with key path runtime
var value: IntP
}
func testGet(_ x: TestGetter, _ args: UnsafeRawPointer) -> TestGetter {
return TestGetter(value: x.value.int
+ args.load(as: Int.self)
+ args.load(fromByteOffset: MemoryLayout<Int>.size, as: Int.self))
}
keyPathImpl.test("computed property arguments") {
let testGetPtr: @convention(thin) (TestGetter, UnsafeRawPointer) -> TestGetter
= testGet
numberOfDestroyOperations = 0
do {
let getter1a = KeyPath<TestGetter, TestGetter>
.build(capacityInBytes: MemoryLayout<Int>.size * 8) { b in
b.addHeader(trivial: false, hasReferencePrefix: false)
[0x1111_1111, 0x1111_1111].withUnsafeBytes { argData in
b.addGetterComponent(id: UnsafeRawPointer(bitPattern: 0x1)!,
getter: unsafeBitCast(testGetPtr, to: UnsafeRawPointer.self),
witnesses: &testWitnesses,
args: argData)
}
}
let getter1b = KeyPath<TestGetter, TestGetter>
.build(capacityInBytes: MemoryLayout<Int>.size * 8) { b in
b.addHeader(trivial: false, hasReferencePrefix: false)
[0x1111_1111, 0x1111_1111].withUnsafeBytes { argData in
b.addGetterComponent(id: UnsafeRawPointer(bitPattern: 0x1)!,
getter: unsafeBitCast(testGetPtr, to: UnsafeRawPointer.self),
witnesses: &testWitnesses,
args: argData)
}
}
let getter2 = KeyPath<TestGetter, TestGetter>
.build(capacityInBytes: MemoryLayout<Int>.size * 8) { b in
b.addHeader(trivial: false, hasReferencePrefix: false)
[0x2222_2222, 0x2222_2222].withUnsafeBytes { argData in
b.addGetterComponent(id: UnsafeRawPointer(bitPattern: 0x1)!,
getter: unsafeBitCast(testGetPtr, to: UnsafeRawPointer.self),
witnesses: &testWitnesses,
args: argData)
}
}
// Should be equal, same getter ID and arguments compare equal
numberOfEqualsOperations = 0
expectEqual(getter1a, getter1b)
expectEqual(numberOfEqualsOperations, 1)
numberOfHashOperations = 0
expectEqual(getter1a.hashValue, getter1b.hashValue)
expectEqual(numberOfHashOperations, 2)
// Should be unequal, same getter ID but arguments don't compare equal
expectNotEqual(getter1a, getter2)
expectEqual(numberOfEqualsOperations, 2)
numberOfCopyOperations = 0
let getter1a1a = getter1a.appending(path: getter1a)
expectEqual(numberOfCopyOperations, 2)
let getter1a1b = getter1a.appending(path: getter1b)
expectEqual(numberOfCopyOperations, 4)
let getter1b1a = getter1b.appending(path: getter1a)
expectEqual(numberOfCopyOperations, 6)
let getter1b1b = getter1b.appending(path: getter1b)
expectEqual(numberOfCopyOperations, 8)
expectEqual(getter1a1a, getter1a1b)
expectEqual(getter1a1a.hashValue, getter1a1b.hashValue)
expectEqual(getter1a1b, getter1b1a)
expectEqual(getter1a1b.hashValue, getter1b1a.hashValue)
expectEqual(getter1b1a, getter1b1b)
expectEqual(getter1b1a.hashValue, getter1b1b.hashValue)
expectNotEqual(getter1a, getter1a1a)
expectNotEqual(getter1b, getter1a1a)
let getter1a2 = getter1a.appending(path: getter2)
expectNotEqual(getter1a1a, getter1a2)
let getter21a = getter2.appending(path: getter1a)
expectNotEqual(getter1a1a, getter21a)
let value = TestGetter(value: 0x1111_1111)
// Getter adds first two words from the argument area
expectEqual(value[keyPath: getter1a].value.int,
0x1111_1111 + 0x1111_1111 + 0x1111_1111)
expectEqual(value[keyPath: getter2].value.int,
0x1111_1111 + 0x2222_2222 + 0x2222_2222)
expectEqual(value[keyPath: getter1a2].value.int,
0x1111_1111 + 0x1111_1111 + 0x1111_1111
+ 0x2222_2222 + 0x2222_2222)
}
// Should have destroyed three original components and all copies
expectEqual(numberOfDestroyOperations, 3 + numberOfCopyOperations)
numberOfDestroyOperations = 0
do {
let getter1a_trivial = KeyPath<TestGetter, TestGetter>
.build(capacityInBytes: MemoryLayout<Int>.size * 8) { b in
b.addHeader(trivial: true, hasReferencePrefix: false)
[0x1111_1111, 0x1111_1111].withUnsafeBytes { argData in
b.addGetterComponent(id: UnsafeRawPointer(bitPattern: 0x1)!,
getter: unsafeBitCast(testGetPtr, to: UnsafeRawPointer.self),
witnesses: &testWitnesses,
args: argData)
}
}
}
// Should not have run the destructor this time, since key path was tagged
// trivial
expectEqual(numberOfDestroyOperations, 0)
}
runAllTests()