// RUN: %empty-directory(%t) // RUN: %target-build-swift -import-objc-header %S/Inputs/tail_allocated_c_array.h -swift-version 5 -g %s -o %t/a.out // RUN: %target-codesign %t/a.out // RUN: %target-run %t/a.out // REQUIRES: executable_test // UNSUPPORTED: freestanding @_spi(ObservableRerootKeyPath) import Swift import StdlibUnittest var keyPath = TestSuite("key paths") final class C { var x: Int var y: LifetimeTracked? var z: T let immutable: String private(set) var secretlyMutable: String var computed: T { get { return z } set { z = newValue } } init(x: Int, y: LifetimeTracked?, z: T) { self.x = x self.y = y self.z = z self.immutable = "\(x) \(y) \(z)" self.secretlyMutable = immutable } } struct Point: Equatable { var x: Double var y: Double var trackLifetime = LifetimeTracked(123) let hypotenuse: Double private(set) var secretlyMutableHypotenuse: Double init(x: Double, y: Double) { self.x = x self.y = y hypotenuse = x*x + y*y secretlyMutableHypotenuse = x*x + y*y } static func ==(a: Point, b: Point) -> Bool { return a.x == b.x && a.y == b.y } } struct S: Equatable { var x: Int var y: LifetimeTracked? var z: T var p: Point var c: C 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 } } final class ComputedA { var readOnly: ComputedB { fatalError() } var nonmutating: ComputedB { get { fatalError() } set { fatalError() } } var reabstracted: () -> () = {} } struct ComputedB { var readOnly: ComputedA { fatalError() } var mutating: ComputedA { get { fatalError() } set { fatalError() } } var nonmutating: ComputedA { get { fatalError() } nonmutating set { fatalError() } } var reabstracted: () -> () = {} } typealias Tuple = (S, C) keyPath.test("key path in-place instantiation") { for _ in 1...2 { let s_x = (\S.x as AnyKeyPath) as! WritableKeyPath, Int> let s_y = (\S.y as AnyKeyPath) as! WritableKeyPath, LifetimeTracked?> let s_z = (\S.z as AnyKeyPath) as! WritableKeyPath, Int> let s_p = (\S.p as AnyKeyPath) as! WritableKeyPath, Point> let s_p_x = (\S.p.x as AnyKeyPath) as! WritableKeyPath, Double> let s_p_y = (\S.p.y as AnyKeyPath) as! WritableKeyPath, Double> let s_c = (\S.c as AnyKeyPath) as! WritableKeyPath, C> let s_c_x = (\S.c.x as AnyKeyPath) as! ReferenceWritableKeyPath, Int> let t_0s = (\Tuple.0 as AnyKeyPath) as! WritableKeyPath, S> let t_1c = (\Tuple.1 as AnyKeyPath) as! WritableKeyPath, C> let t_0s_x = (\Tuple.0.x as AnyKeyPath) as! WritableKeyPath, Int> let t_0s_p_hypotenuse = (\Tuple.0.p.hypotenuse as AnyKeyPath) as! KeyPath, Double> let t_1c_x = (\Tuple.1.x as AnyKeyPath) as! ReferenceWritableKeyPath, Int> let t_1c_immutable = (\Tuple.1.immutable as AnyKeyPath) as! KeyPath, String> let c_x = (\C.x as AnyKeyPath) as! ReferenceWritableKeyPath, Int> let s_c_x_2 = s_c.appending(path: c_x) expectEqual(s_c_x, s_c_x_2) expectEqual(s_c_x_2, s_c_x) expectEqual(s_c_x.hashValue, s_c_x_2.hashValue) let t_1c_x_2 = t_1c.appending(path: c_x) expectEqual(t_1c_x, t_1c_x_2) expectEqual(t_1c_x_2, t_1c_x) expectEqual(t_1c_x.hashValue, t_1c_x_2.hashValue) let point_x = (\Point.x as AnyKeyPath) as! WritableKeyPath let point_y = (\Point.y as AnyKeyPath) as! WritableKeyPath let s_p_x_2 = s_p.appending(path: point_x) let s_p_y_2 = s_p.appending(path: point_y) expectEqual(s_p_x, s_p_x_2) expectEqual(s_p_x_2, s_p_x) expectEqual(s_p_x_2.hashValue, s_p_x.hashValue) expectEqual(s_p_y, s_p_y_2) expectEqual(s_p_y_2, s_p_y) expectEqual(s_p_y_2.hashValue, s_p_y.hashValue) let ca_readOnly = (\ComputedA.readOnly as AnyKeyPath) as! KeyPath let ca_nonmutating = (\ComputedA.nonmutating as AnyKeyPath) as! ReferenceWritableKeyPath let ca_reabstracted = (\ComputedA.reabstracted as AnyKeyPath) as! ReferenceWritableKeyPath ()> let cb_readOnly = (\ComputedB.readOnly as AnyKeyPath) as! KeyPath let cb_mutating = (\ComputedB.mutating as AnyKeyPath) as! WritableKeyPath let cb_nonmutating = (\ComputedB.nonmutating as AnyKeyPath) as! ReferenceWritableKeyPath let cb_reabstracted = (\ComputedB.reabstracted as AnyKeyPath) as! WritableKeyPath ()> let ca_readOnly_mutating = (\ComputedA.readOnly.mutating as AnyKeyPath) as! KeyPath let cb_mutating_readOnly = (\ComputedB.mutating.readOnly as AnyKeyPath) as! KeyPath let ca_readOnly_nonmutating = (\ComputedA.readOnly.nonmutating as AnyKeyPath) as! ReferenceWritableKeyPath let cb_readOnly_reabstracted = (\ComputedB.readOnly.reabstracted as AnyKeyPath) as! ReferenceWritableKeyPath ()> let ca_readOnly_mutating2 = ca_readOnly.appending(path: cb_mutating) expectEqual(ca_readOnly_mutating, ca_readOnly_mutating2) expectEqual(ca_readOnly_mutating2, ca_readOnly_mutating) expectEqual(ca_readOnly_mutating.hashValue, ca_readOnly_mutating2.hashValue) let cb_mutating_readOnly2 = cb_mutating.appending(path: ca_readOnly) expectEqual(cb_mutating_readOnly, cb_mutating_readOnly2) expectEqual(cb_mutating_readOnly2, cb_mutating_readOnly) expectEqual(cb_mutating_readOnly.hashValue, cb_mutating_readOnly2.hashValue) let ca_readOnly_nonmutating2 = ca_readOnly.appending(path: cb_nonmutating) expectEqual(ca_readOnly_nonmutating, ca_readOnly_nonmutating2) expectEqual(ca_readOnly_nonmutating2, ca_readOnly_nonmutating) expectEqual(ca_readOnly_nonmutating.hashValue, ca_readOnly_nonmutating2.hashValue) let cb_readOnly_reabstracted2 = cb_readOnly.appending(path: ca_reabstracted) expectEqual(cb_readOnly_reabstracted, cb_readOnly_reabstracted2) expectEqual(cb_readOnly_reabstracted2, cb_readOnly_reabstracted) expectEqual(cb_readOnly_reabstracted2.hashValue, cb_readOnly_reabstracted.hashValue) } } keyPath.test("key path generic instantiation") { func testWithGenericParam(_: T.Type) -> ReferenceWritableKeyPath, Int> { for i in 1...2 { let s_x = (\S.x as AnyKeyPath) as! WritableKeyPath, Int> let s_y = (\S.y as AnyKeyPath) as! WritableKeyPath, LifetimeTracked?> let s_z = (\S.z as AnyKeyPath) as! WritableKeyPath, T> let s_p = (\S.p as AnyKeyPath) as! WritableKeyPath, Point> let s_p_x = (\S.p.x as AnyKeyPath) as! WritableKeyPath, Double> let s_p_y = (\S.p.y as AnyKeyPath) as! WritableKeyPath, Double> let s_c = (\S.c as AnyKeyPath) as! WritableKeyPath, C> let s_c_x = (\S.c.x as AnyKeyPath) as! ReferenceWritableKeyPath, Int> let t_0s = (\Tuple.0 as AnyKeyPath) as! WritableKeyPath, S> let t_1c = (\Tuple.1 as AnyKeyPath) as! WritableKeyPath, C> let t_0s_x = (\Tuple.0.x as AnyKeyPath) as! WritableKeyPath, Int> let t_0s_p_hypotenuse = (\Tuple.0.p.hypotenuse as AnyKeyPath) as! KeyPath, Double> let t_1c_x = (\Tuple.1.x as AnyKeyPath) as! ReferenceWritableKeyPath, Int> let t_1c_immutable = (\Tuple.1.immutable as AnyKeyPath) as! KeyPath, String> let c_x = (\C.x as AnyKeyPath) as! ReferenceWritableKeyPath, Int> let s_c_x_2 = s_c.appending(path: c_x) expectEqual(s_c_x, s_c_x_2) expectEqual(s_c_x_2, s_c_x) expectEqual(s_c_x.hashValue, s_c_x_2.hashValue) let t_1c_x_2 = t_1c.appending(path: c_x) expectEqual(t_1c_x, t_1c_x_2) expectEqual(t_1c_x_2, t_1c_x) expectEqual(t_1c_x.hashValue, t_1c_x_2.hashValue) let point_x = (\Point.x as AnyKeyPath) as! WritableKeyPath let point_y = (\Point.y as AnyKeyPath) as! WritableKeyPath let s_p_x_2 = s_p.appending(path: point_x) let s_p_y_2 = s_p.appending(path: point_y) expectEqual(s_p_x, s_p_x_2) expectEqual(s_p_x_2, s_p_x) expectEqual(s_p_x_2.hashValue, s_p_x.hashValue) expectEqual(s_p_y, s_p_y_2) expectEqual(s_p_y_2, s_p_y) expectEqual(s_p_y_2.hashValue, s_p_y.hashValue) if i == 2 { return s_c_x } } fatalError() } let s_c_x_int = testWithGenericParam(Int.self) let s_c_x_int2 = \S.c.x expectEqual(s_c_x_int, s_c_x_int2) let s_c_x_string = testWithGenericParam(String.self) let s_c_x_string2 = \S.c.x expectEqual(s_c_x_string, s_c_x_string2) let s_c_x_lt = testWithGenericParam(LifetimeTracked.self) let s_c_x_lt2 = \S.c.x expectEqual(s_c_x_lt, s_c_x_lt2) } protocol P {} struct TestComputed: P { static var numNonmutatingSets = 0 static var numMutatingSets = 0 static func resetCounts() { numNonmutatingSets = 0 numMutatingSets = 0 } var canary = LifetimeTracked(0) var readonly: LifetimeTracked { return LifetimeTracked(1) } var nonmutating: LifetimeTracked { get { return LifetimeTracked(2) } nonmutating set { TestComputed.numNonmutatingSets += 1 } } var mutating: LifetimeTracked { get { return LifetimeTracked(3) } set { canary = newValue } } } extension P { var readonlyProtoExt: Self { return self } var mutatingProtoExt: Self { get { return self } set { self = newValue } } } keyPath.test("computed properties") { var test = TestComputed() do { let tc_readonly = \TestComputed.readonly expectTrue(test[keyPath: tc_readonly] !== test[keyPath: tc_readonly]) expectEqual(test[keyPath: tc_readonly].value, test[keyPath: tc_readonly].value) } do { let tc_nonmutating = \TestComputed.nonmutating expectTrue(test[keyPath: tc_nonmutating] !== test[keyPath: tc_nonmutating]) expectEqual(test[keyPath: tc_nonmutating].value, test[keyPath: tc_nonmutating].value) TestComputed.resetCounts() test[keyPath: tc_nonmutating] = LifetimeTracked(4) expectEqual(TestComputed.numNonmutatingSets, 1) } do { let tc_mutating = \TestComputed.mutating expectTrue(test[keyPath: tc_mutating] !== test[keyPath: tc_mutating]) expectEqual(test[keyPath: tc_mutating].value, test[keyPath: tc_mutating].value) let newObject = LifetimeTracked(5) test[keyPath: tc_mutating] = newObject expectTrue(test.canary === newObject) } do { let tc_readonlyProtoExt = \TestComputed.readonlyProtoExt expectTrue(test.canary === test[keyPath: tc_readonlyProtoExt].canary) } do { let tc_mutatingProtoExt = \TestComputed.mutatingProtoExt expectTrue(test.canary === test[keyPath: tc_mutatingProtoExt].canary) let oldTest = test test[keyPath: tc_mutatingProtoExt] = TestComputed() expectTrue(oldTest.canary !== test.canary) expectTrue(test.canary === test[keyPath: tc_mutatingProtoExt].canary) } } keyPath.test("equality") { expectNotEqual(\Array.isEmpty, \Substring.isEmpty) expectNotEqual(\Array.isEmpty, \Substring.isEmpty) expectNotEqual(\Array.isEmpty, \String.isEmpty) expectNotEqual(\Array.isEmpty, \Substring.last) expectNotEqual(\Array.isEmpty, \Array.isEmpty) } class AB { } class ABC: AB, ABCProtocol { var a = LifetimeTracked(1) var b = LifetimeTracked(2) var c = LifetimeTracked(3) subscript(x: Int) -> Int { get { return x + 27 } set { } } } protocol ABCProtocol { var a: LifetimeTracked { get } var b: LifetimeTracked { get set } var c: LifetimeTracked { get nonmutating set } subscript(x: Int) -> Int { get set } } keyPath.test("dynamically-typed application") { let cPaths = [\ABC.a, \ABC.b, \ABC.c] let subject = ABC() do { let fields = cPaths.map { subject[keyPath: $0] } expectTrue(fields[0] as! AnyObject === subject.a) expectTrue(fields[1] as! AnyObject === subject.b) expectTrue(fields[2] as! AnyObject === subject.c) } let erasedSubject: AB = subject let erasedPaths: [AnyKeyPath] = cPaths let wrongSubject = AB() do { let fields = erasedPaths.map { erasedSubject[keyPath: $0] } expectTrue(fields[0]! as! AnyObject === subject.a) expectTrue(fields[1]! as! AnyObject === subject.b) expectTrue(fields[2]! as! AnyObject === subject.c) let wrongFields = erasedPaths.map { wrongSubject[keyPath: $0] } expectTrue(wrongFields[0] == nil) expectTrue(wrongFields[1] == nil) expectTrue(wrongFields[2] == nil) } var protoErasedSubject: ABCProtocol = subject let protoErasedPathA = \ABCProtocol.a let protoErasedPathB = \ABCProtocol.b let protoErasedPathC = \ABCProtocol.c let protoErasedSubscript = \ABCProtocol[100] do { expectTrue(protoErasedSubject.a === protoErasedSubject[keyPath: protoErasedPathA]) let newB = LifetimeTracked(4) expectTrue(protoErasedSubject.b === protoErasedSubject[keyPath: protoErasedPathB]) protoErasedSubject[keyPath: protoErasedPathB] = newB expectTrue(protoErasedSubject.b === protoErasedSubject[keyPath: protoErasedPathB]) expectTrue(protoErasedSubject.b === newB) let newC = LifetimeTracked(5) expectTrue(protoErasedSubject.c === protoErasedSubject[keyPath: protoErasedPathC]) protoErasedSubject[keyPath: protoErasedPathC] = newC expectTrue(protoErasedSubject.c === protoErasedSubject[keyPath: protoErasedPathC]) expectTrue(protoErasedSubject.c === newC) expectTrue(protoErasedSubject[keyPath: protoErasedSubscript] == 127) } } struct TestOptional { var origin: Point? var questionableCanary: LifetimeTracked? = LifetimeTracked(123) init(origin: Point?) { self.origin = origin } } keyPath.test("optional force-unwrapping") { let origin_x = \TestOptional.origin!.x let canary = \TestOptional.questionableCanary! var value = TestOptional(origin: Point(x: 3, y: 4)) expectEqual(value[keyPath: origin_x], 3) expectEqual(value.origin!.x, 3) value[keyPath: origin_x] = 5 expectEqual(value[keyPath: origin_x], 5) expectEqual(value.origin!.x, 5) expectTrue(value[keyPath: canary] === value.questionableCanary) let newCanary = LifetimeTracked(456) value[keyPath: canary] = newCanary expectTrue(value[keyPath: canary] === newCanary) expectTrue(value.questionableCanary === newCanary) } #if !os(WASI) // Trap tests aren't available on WASI. keyPath.test("optional force-unwrapping trap") { let origin_x = \TestOptional.origin!.x var value = TestOptional(origin: nil) expectCrashLater() _ = value[keyPath: origin_x] } #endif struct TestOptional2 { var optional: TestOptional? } keyPath.test("optional chaining") { let origin_x = \TestOptional.origin?.x let canary = \TestOptional.questionableCanary?.value let withPoint = TestOptional(origin: Point(x: 3, y: 4)) expectEqual(withPoint[keyPath: origin_x]!, 3) expectEqual(withPoint[keyPath: canary]!, 123) let withoutPoint = TestOptional(origin: nil) expectNil(withoutPoint[keyPath: origin_x]) let optional2: TestOptional2? = TestOptional2(optional: withPoint) let optional2_optional = \TestOptional2?.?.optional expectEqual(optional2[keyPath: optional2_optional]!.origin!.x, 3) expectEqual(optional2[keyPath: optional2_optional]!.origin!.y, 4) } func makeKeyPathInGenericContext(of: T.Type) -> ReferenceWritableKeyPath, T> { return \C.computed } keyPath.test("computed generic key paths") { let path = makeKeyPathInGenericContext(of: LifetimeTracked.self) let z = LifetimeTracked(456) let c = C(x: 42, y: LifetimeTracked(123), z: z) expectTrue(c[keyPath: path] === z) let z2 = LifetimeTracked(789) c[keyPath: path] = z2 expectTrue(c[keyPath: path] === z2) expectTrue(c.z === z2) let path2 = makeKeyPathInGenericContext(of: LifetimeTracked.self) expectEqual(path, path2) expectEqual(path.hashValue, path2.hashValue) let pathNonGeneric = \C.computed expectEqual(path, pathNonGeneric) expectEqual(path.hashValue, pathNonGeneric.hashValue) let valuePath = path.appending(path: \LifetimeTracked.value) expectEqual(c[keyPath: valuePath], 789) let valuePathNonGeneric = pathNonGeneric.appending(path: \LifetimeTracked.value) expectEqual(valuePath, valuePathNonGeneric) expectEqual(valuePath.hashValue, valuePathNonGeneric.hashValue) } var numberOfMutatingWritebacks = 0 var numberOfNonmutatingWritebacks = 0 struct NoisyWriteback { var canary = LifetimeTracked(246) var mutating: LifetimeTracked { get { return canary } set { numberOfMutatingWritebacks += 1 } } var nonmutating: LifetimeTracked { get { return canary } nonmutating set { numberOfNonmutatingWritebacks += 1 } } } keyPath.test("read-only accesses don't trigger writebacks") { var x = NoisyWriteback() x = NoisyWriteback() // suppress "never mutated" warnings let wkp = \NoisyWriteback.mutating let rkp = \NoisyWriteback.nonmutating numberOfMutatingWritebacks = 0 numberOfNonmutatingWritebacks = 0 _ = x[keyPath: wkp] _ = x[keyPath: rkp] expectEqual(x[keyPath: wkp].value, 246) expectEqual(x[keyPath: rkp].value, 246) expectEqual(numberOfMutatingWritebacks, 0) expectEqual(numberOfNonmutatingWritebacks, 0) let y = x _ = y[keyPath: wkp] _ = y[keyPath: rkp] expectEqual(y[keyPath: wkp].value, 246) expectEqual(y[keyPath: rkp].value, 246) expectEqual(numberOfMutatingWritebacks, 0) expectEqual(numberOfNonmutatingWritebacks, 0) } var nestedWritebackLog = 0 struct NoisyNestingWriteback { var value: Int var nested: NoisyNestingWriteback { get { return NoisyNestingWriteback(value: value + 1) } set { nestedWritebackLog = nestedWritebackLog << 8 | newValue.value value = newValue.value - 1 } } } keyPath.test("writebacks nest properly") { var test = NoisyNestingWriteback(value: 0) nestedWritebackLog = 0 test.nested.nested.nested.value = 0x38 expectEqual(nestedWritebackLog, 0x383736) nestedWritebackLog = 0 let kp = \NoisyNestingWriteback.nested.nested.nested test[keyPath: kp].value = 0x38 expectEqual(nestedWritebackLog, 0x383736) } struct IUOWrapper { var wrapped: IUOWrapped! } struct IUOWrapped { var value: Int } keyPath.test("IUO and key paths") { var subject = IUOWrapper(wrapped: IUOWrapped(value: 1989)) let kp1 = \IUOWrapper.wrapped.value expectEqual(subject[keyPath: kp1], 1989) subject[keyPath: kp1] = 1738 expectEqual(subject[keyPath: kp1], 1738) expectEqual(subject.wrapped.value, 1738) let kp2 = \IUOWrapper.wrapped!.value expectEqual(kp1, kp2) expectEqual(kp1.hashValue, kp2.hashValue) } struct SubscriptResult { var canary = LifetimeTracked(3333) var left: T var right: U init(left: T, right: U) { self.left = left self.right = right } subscript(left: T) -> Bool { return self.left == left } subscript(right: U) -> Bool { return self.right == right } } struct Subscripts { var canary = LifetimeTracked(4444) subscript(x: T, y: U) -> SubscriptResult { return SubscriptResult(left: x, right: y) } subscript(x: Int, y: Int) -> Int { return x + y } } struct KeyA: Hashable { var canary = LifetimeTracked(1111) var value: String init(value: String) { self.value = value } static func ==(a: KeyA, b: KeyA) -> Bool { return a.value == b.value } func hash(into hasher: inout Hasher) { hasher.combine(value) } } struct KeyB: Hashable { var canary = LifetimeTracked(2222) var value: Int init(value: Int) { self.value = value } static func ==(a: KeyB, b: KeyB) -> Bool { return a.value == b.value } func hash(into hasher: inout Hasher) { hasher.combine(value) } } func fullGenericContext(x: T, y: U) -> KeyPath, SubscriptResult> { return \Subscripts.[x, y] } func halfGenericContext(x: KeyA, y: U) -> KeyPath, SubscriptResult> { return \Subscripts.[x, y] } func nonGenericContext(x: KeyA, y: KeyB) -> KeyPath, SubscriptResult> { return \Subscripts.[x, y] } keyPath.test("subscripts") { let a = fullGenericContext(x: KeyA(value: "hey"), y: KeyB(value: 1738)) let b = halfGenericContext(x: KeyA(value: "hey"), y: KeyB(value: 1738)) let c = nonGenericContext(x: KeyA(value: "hey"), y: KeyB(value: 1738)) expectEqual(a, b) expectEqual(a, c) expectEqual(b, a) expectEqual(b, c) expectEqual(c, a) expectEqual(c, b) expectEqual(a.hashValue, b.hashValue) expectEqual(a.hashValue, c.hashValue) expectEqual(b.hashValue, a.hashValue) expectEqual(b.hashValue, c.hashValue) expectEqual(c.hashValue, a.hashValue) expectEqual(c.hashValue, b.hashValue) let base = Subscripts() let kp2 = \SubscriptResult.[KeyA(value: "hey")] for kp in [a, b, c] { let projected = base[keyPath: kp] expectEqual(projected.left.value, "hey") expectEqual(projected.right.value, 1738) expectEqual(projected[keyPath: kp2], true) let kp12 = \Subscripts.[KeyA(value: "hey"), KeyB(value: 1738)][KeyA(value: "hey")] let kp12a = kp.appending(path: kp2) expectEqual(kp12, kp12a) expectEqual(kp12a, kp12) expectEqual(kp12.hashValue, kp12a.hashValue) } let ints = \Subscripts.[17, 38] let ints2 = \Subscripts.[17, 38] let ints3 = \Subscripts.[38, 17] expectEqual(base[keyPath: ints], 17 + 38) expectEqual(ints, ints2) expectEqual(ints2, ints) expectNotEqual(ints, ints3) expectNotEqual(ints2, ints3) expectNotEqual(ints3, ints) expectNotEqual(ints3, ints2) expectEqual(ints.hashValue, ints2.hashValue) let ints_be = ints.appending(path: \Int.bigEndian) expectEqual(base[keyPath: ints_be], (17 + 38).bigEndian) } struct NonOffsetableProperties { // observers var x: Int { didSet {} } // reabstracted var y: () -> () // computed var z: Int { return 0 } } struct TupleProperties { // unlabeled var a: (Int, String) // labeled let b: (x: String, y: Int) // reference writable let c: (m: C, n: C) } func getIdentityKeyPathOfType(_: T.Type) -> KeyPath { return \.self } keyPath.test("offsets") { let SLayout = MemoryLayout>.self expectNotNil(SLayout.offset(of: \S.x)) expectNotNil(SLayout.offset(of: \S.y)) expectNotNil(SLayout.offset(of: \S.z)) expectNotNil(SLayout.offset(of: \S.p)) expectNotNil(SLayout.offset(of: \S.p.x)) expectNotNil(SLayout.offset(of: \S.p.y)) expectNotNil(SLayout.offset(of: \S.c)) expectNil(SLayout.offset(of: \S.c.x)) let NOPLayout = MemoryLayout.self expectNil(NOPLayout.offset(of: \NonOffsetableProperties.x)) expectNil(NOPLayout.offset(of: \NonOffsetableProperties.y)) expectNil(NOPLayout.offset(of: \NonOffsetableProperties.z)) expectEqual(SLayout.offset(of: \.self), 0) expectEqual(SLayout.offset(of: getIdentityKeyPathOfType(S.self)), 0) let TPLayout = MemoryLayout.self expectEqual(TPLayout.offset(of: \TupleProperties.self), 0) expectEqual(TPLayout.offset(of: \TupleProperties.a), 0) expectEqual(TPLayout.offset(of: \TupleProperties.a.0), 0) expectEqual(TPLayout.offset(of: \TupleProperties.a.1), MemoryLayout.size) expectEqual(TPLayout.offset(of: \TupleProperties.b), MemoryLayout<(Int, String)>.size) expectEqual(TPLayout.offset(of: \TupleProperties.b.x), MemoryLayout<(Int, String)>.size) expectEqual(TPLayout.offset(of: \TupleProperties.b.y), MemoryLayout<(Int, String, String)>.size) expectEqual(TPLayout.offset(of: \TupleProperties.c), MemoryLayout<(Int, String, String, Int)>.size) expectEqual(TPLayout.offset(of: \TupleProperties.c.m), MemoryLayout<(Int, String, String, Int)>.size) expectEqual(TPLayout.offset(of: \TupleProperties.c.n), MemoryLayout<(Int, String, String, Int, C)>.size) let TLayout = MemoryLayout>.self expectEqual(TLayout.offset(of: \Tuple.self), 0) expectEqual(TLayout.offset(of: \Tuple.0), 0) expectEqual(TLayout.offset(of: \Tuple.0.x), 0) expectEqual(TLayout.offset(of: \Tuple.1), SLayout.size) } keyPath.test("identity key path") { var x = LifetimeTracked(1738) let id = \LifetimeTracked.self expectTrue(x === x[keyPath: id]) let newX = LifetimeTracked(679) x[keyPath: id] = newX expectTrue(x === newX) let id2 = getIdentityKeyPathOfType(LifetimeTracked.self) expectEqual(id, id2) expectEqual(id.hashValue, id2.hashValue) expectNotNil(id2 as? WritableKeyPath) let id3 = id.appending(path: id2) expectEqual(id, id3) expectEqual(id.hashValue, id3.hashValue) expectNotNil(id3 as? WritableKeyPath) let valueKey = \LifetimeTracked.value let valueKey2 = id.appending(path: valueKey) let valueKey3 = (valueKey as KeyPath).appending(path: \Int.self) expectEqual(valueKey, valueKey2) expectEqual(valueKey.hashValue, valueKey2.hashValue) expectEqual(valueKey, valueKey3) expectEqual(valueKey.hashValue, valueKey3.hashValue) expectEqual(x[keyPath: valueKey2], 679) expectEqual(x[keyPath: valueKey3], 679) } keyPath.test("tuple key path") { let t0 = \TupleProperties.a.0 expectNotNil(t0 as? KeyPath) expectNotNil(t0 as? WritableKeyPath) expectNil(t0 as? ReferenceWritableKeyPath) let t1 = \TupleProperties.a.1 expectNotNil(t1 as? KeyPath) expectNotNil(t1 as? WritableKeyPath) expectNil(t1 as? ReferenceWritableKeyPath) let t2 = \TupleProperties.b.x expectNotNil(t2 as? KeyPath) expectNil(t2 as? WritableKeyPath) expectNil(t2 as? ReferenceWritableKeyPath) let t3 = \TupleProperties.b.y expectNotNil(t3 as? KeyPath) expectNil(t3 as? WritableKeyPath) expectNil(t3 as? ReferenceWritableKeyPath) let t4 = \TupleProperties.c.m expectNotNil(t4 as? KeyPath>) expectNil(t4 as? WritableKeyPath>) expectNil(t4 as? ReferenceWritableKeyPath>) let t5 = \TupleProperties.c.n.z expectNotNil(t5 as? KeyPath) expectNotNil(t5 as? WritableKeyPath) expectNotNil(t5 as? ReferenceWritableKeyPath) } keyPath.test("tuple key path execution") { typealias T0 = (Int, String) typealias T1 = (x: Int, y: String) let kp_t0_0 = \T0.0 let kp_t0_1 = \T0.1 let kp_t1_x = \T1.x let kp_t1_y = \T1.y let kp_t1_0 = \T1.0 let kp_t1_1 = \T1.1 var tuple0 = (1, "Hello") let tuple1 = (x: 2, y: "World") let tuple2 = (a: 3, b: "String") // in some cases, tuple key paths are interchangeable expectEqual(tuple0[keyPath: kp_t0_0], 1) expectEqual(tuple0[keyPath: kp_t1_x], 1) expectEqual(tuple0[keyPath: kp_t1_0], 1) expectEqual(tuple0[keyPath: kp_t0_1], "Hello") expectEqual(tuple0[keyPath: kp_t1_y], "Hello") expectEqual(tuple0[keyPath: kp_t1_1], "Hello") expectEqual(tuple1[keyPath: kp_t0_0], 2) expectEqual(tuple1[keyPath: kp_t1_x], 2) expectEqual(tuple1[keyPath: kp_t1_0], 2) expectEqual(tuple1[keyPath: kp_t0_1], "World") expectEqual(tuple1[keyPath: kp_t1_y], "World") expectEqual(tuple1[keyPath: kp_t1_1], "World") expectEqual(tuple2[keyPath: kp_t0_0], 3) //expectEqual(tuple2[keyPath: kp_t1_x], 3) //expectEqual(tuple2[keyPath: kp_t1_0], 3) expectEqual(tuple2[keyPath: kp_t0_1], "String") //expectEqual(tuple2[keyPath: kp_t1_y], "String") //expectEqual(tuple2[keyPath: kp_t1_1], "String") tuple0[keyPath: kp_t0_1] = "Another String" expectEqual(tuple0[keyPath: kp_t0_1], "Another String") } keyPath.test("tuple key path execution (generic)") { struct Container { let x: (T, U) var y: (a: T, b: U) } func generic(a: A, b: B) { typealias T = (A, B) let kp_t_0 = \T.0 let kp_t_1 = \T.1 let kp_c_x = \Container.x let kp_c_x_0 = kp_c_x.appending(path: kp_t_0) let kp_c_x_1 = kp_c_x.appending(path: kp_t_1) let kp_c_y_a = \Container.y.a let kp_c_y_b = \Container.y.b let tuple = (a, b) let container = Container(x: (a, b), y: (a, b)) expectEqual(tuple[keyPath: kp_t_0], tuple.0) expectEqual(tuple[keyPath: kp_t_1], tuple.1) expectEqual(container[keyPath: kp_c_x_0], container.x.0) expectEqual(container[keyPath: kp_c_x_1], container.x.1) expectEqual(container[keyPath: kp_c_y_a], container.y.0) expectEqual(container[keyPath: kp_c_y_b], container.y.1) } generic(a: 13, b: "Hello Tuples") generic(a: "Tuples Hello", b: 31) } keyPath.test("let-ness") { expectNil(\C.immutable as? ReferenceWritableKeyPath) expectNotNil(\C.secretlyMutable as? ReferenceWritableKeyPath) expectNil(\Point.hypotenuse as? WritableKeyPath) expectNotNil(\Point.secretlyMutableHypotenuse as? WritableKeyPath) } keyPath.test("key path literal closures") { // Property access let fnX: (C) -> Int = \C.x let fnY: (C) -> LifetimeTracked? = \C.y let fnZ: (C) -> String = \C.z let fnImmutable: (C) -> String = \C.immutable let fnSecretlyMutable: (C) -> String = \C.secretlyMutable let fnComputed: (C) -> String = \C.computed let lifetime = LifetimeTracked(249) let base = C(x: 1, y: lifetime, z: "SE-0249") expectEqual(1, fnX(base)) expectEqual(lifetime, fnY(base)) expectEqual("SE-0249", fnZ(base)) expectEqual("1 Optional(249) SE-0249", fnImmutable(base)) expectEqual("1 Optional(249) SE-0249", fnSecretlyMutable(base)) expectEqual("SE-0249", fnComputed(base)) // Subscripts var callsToComputeIndex = 0 func computeIndexWithSideEffect(_ i: Int) -> Int { callsToComputeIndex += 1 return -i } let fnSubscriptResultA: (Subscripts) -> SubscriptResult = \Subscripts.["A", computeIndexWithSideEffect(13)] let fnSubscriptResultB: (Subscripts) -> SubscriptResult = \Subscripts.["B", computeIndexWithSideEffect(42)] let subscripts = Subscripts() expectEqual("A", fnSubscriptResultA(subscripts).left) expectEqual(-13, fnSubscriptResultA(subscripts).right) expectEqual("B", fnSubscriptResultB(subscripts).left) expectEqual(-42, fnSubscriptResultB(subscripts).right) // Did we compute the indices once per closure construction, or once per // closure application? expectEqual(2, callsToComputeIndex) // rdar://problem/59445486 let variadicFn: (String...) -> Int = \.count expectEqual(3, variadicFn("a", "b", "c")) } // https://github.com/apple/swift/issues/48651 protocol P_48651 {} struct S_48651 {} extension P_48651 { var asString: String? { return self as? String } } extension S_48651 where ValueType: P_48651 { func doSomething() { _ = \ValueType.asString?.endIndex } } extension Int: P_48651 {} keyPath.test("optional chaining component that needs generic instantiation") { S_48651().doSomething() } // Nested generics. protocol HasAssoc { associatedtype A } struct Outer { struct Middle { } } extension Outer.Middle where V: HasAssoc, U == V.A, W == X { struct Inner { func foo() -> AnyKeyPath { return \[Y?: [U]].values } } } extension Double: HasAssoc { typealias A = Float } keyPath.test("nested generics") { let nested = Outer.Middle.Inner() let nestedKeyPath = nested.foo() typealias DictType = [UInt? : [Float]] expectTrue(nestedKeyPath is KeyPath) } keyPath.test("tail allocated c array") { let offset = MemoryLayout.offset(of: \foo.tailallocatedarray)! expectEqual(4, offset) } keyPath.test("ReferenceWritableKeyPath statically typed as WritableKeyPath") { let inner = C(x: 42, y: nil, z: 43) var outer = C>(x: 44, y: nil, z: inner) let keyPath = \C>.z.x let upcastKeyPath = keyPath as WritableKeyPath expectEqual(outer[keyPath: keyPath], 42) outer[keyPath: keyPath] = 43 expectEqual(outer[keyPath: keyPath], 43) expectEqual(outer[keyPath: upcastKeyPath], 43) outer[keyPath: upcastKeyPath] = 44 expectEqual(outer[keyPath: upcastKeyPath], 44) func setWithInout(_ lhs: inout T, _ rhs: T) { lhs = rhs } expectEqual(outer[keyPath: keyPath], 44) setWithInout(&outer[keyPath: keyPath], 45); expectEqual(outer[keyPath: keyPath], 45) expectEqual(outer[keyPath: upcastKeyPath], 45) setWithInout(&outer[keyPath: upcastKeyPath], 46) expectEqual(outer[keyPath: upcastKeyPath], 46) } struct Dog { var name: String var age: Int } class Cat { var name: String var age: Int init(name: String, age: Int) { self.name = name self.age = age } } if #available(SwiftStdlib 5.9, *) { keyPath.test("_createOffsetBasedKeyPath") { let dogAgeKp = _createOffsetBasedKeyPath( root: Dog.self, value: Int.self, offset: MemoryLayout.size ) as? KeyPath expectNotNil(dogAgeKp) let sparky = Dog(name: "Sparky", age: 7) expectEqual(sparky[keyPath: dogAgeKp!], 7) let catNameKp = _createOffsetBasedKeyPath( root: Cat.self, value: String.self, offset: 2 * MemoryLayout.size ) as? KeyPath expectNotNil(catNameKp) let chloe = Cat(name: "Chloe", age: 4) expectEqual(chloe[keyPath: catNameKp!], "Chloe") } } class RerootedSuper { var x = "hello world" } class RerootedSub0: RerootedSuper {} class RerootedSub1: RerootedSub0 {} if #available(SwiftStdlib 5.9, *) { keyPath.test("_rerootKeyPath") { let x = \RerootedSub1.x let superValue = RerootedSuper() let sub0 = RerootedSub0() let sub1 = RerootedSub1() let sub0Kp = _rerootKeyPath(x, to: RerootedSub0.self) expectTrue(type(of: sub0Kp) == ReferenceWritableKeyPath.self) let superKp = _rerootKeyPath(x, to: RerootedSuper.self) expectTrue(type(of: superKp) == ReferenceWritableKeyPath.self) let x0 = sub1[keyPath: sub0Kp] as! String expectEqual(x0, "hello world") let x1 = sub1[keyPath: superKp] as! String expectEqual(x1, "hello world") let x2 = sub0[keyPath: sub0Kp] as! String expectEqual(x2, "hello world") let x3 = sub0[keyPath: superKp] as! String expectEqual(x3, "hello world") let x4 = superValue[keyPath: superKp] as! String expectEqual(x4, "hello world") } } @_alignment(8) struct EightAligned { let x = 0 } extension EightAligned: Equatable {} extension EightAligned: Hashable {} @_alignment(16) struct SixteenAligned { let x = 0 } extension SixteenAligned: Equatable {} extension SixteenAligned: Hashable {} struct OveralignedForEight { subscript(getter getter: EightAligned) -> Int { 128 } subscript(setter setter: EightAligned) -> Int { get { 128 } set { fatalError() } } } struct OveralignedForSixteen { subscript(getter getter: SixteenAligned) -> Int { 128 } subscript(setter setter: SixteenAligned) -> Int { get { 128 } set { fatalError() } } } struct SimpleEight { let oa = OveralignedForEight() } struct SimpleSixteen { let oa = OveralignedForSixteen() } #if _pointerBitWidth(_32) if #available(SwiftStdlib 6.3, *) { keyPath.test("eight byte overaligned arguments") { let oa = OveralignedForEight() let kp = \OveralignedForEight.[getter: EightAligned()] let value = oa[keyPath: kp] expectEqual(value, 128) // Test that appends where the argument is no longer overaligned work let simple = SimpleEight() let kp11 = \SimpleEight.oa let kp12 = \OveralignedForEight.[getter: EightAligned()] let kp1 = kp11.appending(path: kp12) let value1 = simple[keyPath: kp1] expectEqual(value1, 128) // Test that appends where the argument is still overaligned works let kp21 = \SimpleEight.oa let kp22 = \OveralignedForEight.[setter: EightAligned()] let kp2 = kp21.appending(path: kp22) let value2 = simple[keyPath: kp2] expectEqual(value2, 128) } } #endif if #available(SwiftStdlib 6.3, *) { keyPath.test("sixteen byte overaligned arguments") { let oa = OveralignedForSixteen() let kp = \OveralignedForSixteen.[getter: SixteenAligned()] let value = oa[keyPath: kp] expectEqual(value, 128) // Test that appends where the argument is no longer overaligned work let simple = SimpleSixteen() let kp11 = \SimpleSixteen.oa let kp12 = \OveralignedForSixteen.[getter: SixteenAligned()] let kp1 = kp11.appending(path: kp12) let value1 = simple[keyPath: kp1] expectEqual(value1, 128) // Test that appends where the argument is still overaligned works let kp21 = \SimpleSixteen.oa let kp22 = \OveralignedForSixteen.[setter: SixteenAligned()] let kp2 = kp21.appending(path: kp22) let value2 = simple[keyPath: kp2] expectEqual(value2, 128) } } struct Thingy { var thingy: Thingy { self } var a = 42 } extension Int { var subscripty: Thingy { Thingy(a: 67) } } keyPath.test("appending keypath with multiple components") { var kp: KeyPath = \Thingy.self kp = kp.appending(path: \.thingy) kp = kp.appending(path: \.a.subscripty) let t = Thingy() let value = t[keyPath: kp] expectEqual(value.a, 67) } runAllTests()