Skip to content

Commit 3360e41

Browse files
committed
Add SPI to reroot a keypath for a given superclass
1 parent 1c3667b commit 3360e41

File tree

2 files changed

+103
-0
lines changed

2 files changed

+103
-0
lines changed

stdlib/public/core/KeyPath.swift

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3937,6 +3937,66 @@ public func _createOffsetBasedKeyPath(
39373937
}
39383938
}
39393939

3940+
@_spi(ObservableRerootKeyPath)
3941+
@available(SwiftStdlib 5.9, *)
3942+
public func _rerootKeyPath<NewRoot>(
3943+
_ existingKp: AnyKeyPath,
3944+
to newRoot: NewRoot.Type
3945+
) -> PartialKeyPath<NewRoot> {
3946+
let (isTrivial, hasReferencePrefix, componentSize) = existingKp.withBuffer {
3947+
($0.trivial, $0.hasReferencePrefix, $0.data.count)
3948+
}
3949+
3950+
let existingKpTy = type(of: existingKp)
3951+
3952+
func openedRoot<Root>(_: Root.Type) -> AnyKeyPath.Type {
3953+
func openedValue<Value>(_: Value.Type) -> AnyKeyPath.Type {
3954+
if existingKpTy == ReferenceWritableKeyPath<Root, Value>.self {
3955+
return ReferenceWritableKeyPath<NewRoot, Value>.self
3956+
} else if existingKpTy == KeyPath<Root, Value>.self {
3957+
return KeyPath<NewRoot, Value>.self
3958+
} else {
3959+
fatalError("Unsupported KeyPath type to be rerooted")
3960+
}
3961+
}
3962+
3963+
return _openExistential(existingKpTy.valueType, do: openedValue(_:))
3964+
}
3965+
3966+
let newKpTy = _openExistential(existingKpTy.rootType, do: openedRoot(_:))
3967+
3968+
return newKpTy._create(
3969+
// This is the buffer header + padding (if needed) + size of components
3970+
capacityInBytes: MemoryLayout<Int>.size + componentSize
3971+
) {
3972+
var builder = KeyPathBuffer.Builder($0)
3973+
let header = KeyPathBuffer.Header(
3974+
size: componentSize,
3975+
trivial: isTrivial,
3976+
hasReferencePrefix: hasReferencePrefix
3977+
)
3978+
3979+
builder.pushHeader(header)
3980+
3981+
existingKp.withBuffer {
3982+
var existingBuffer = $0
3983+
3984+
while true {
3985+
let (rawComponent, componentTy) = existingBuffer.next()
3986+
3987+
rawComponent.clone(
3988+
into: &builder.buffer,
3989+
endOfReferencePrefix: rawComponent.header.endOfReferencePrefix
3990+
)
3991+
3992+
if componentTy == nil {
3993+
break
3994+
}
3995+
}
3996+
}
3997+
} as! PartialKeyPath<NewRoot>
3998+
}
3999+
39404000
#if SWIFT_ENABLE_REFLECTION
39414001

39424002
@_silgen_name("swift_keyPath_copySymbolName")

test/stdlib/KeyPath.swift

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
// REQUIRES: executable_test
66
// UNSUPPORTED: freestanding
77

8+
@_spi(ObservableRerootKeyPath)
9+
import Swift
10+
811
import StdlibUnittest
912

1013
var keyPath = TestSuite("key paths")
@@ -1086,5 +1089,45 @@ if #available(SwiftStdlib 5.9, *) {
10861089
}
10871090
}
10881091

1092+
class RerootedSuper {
1093+
var x = "hello world"
1094+
}
1095+
1096+
class RerootedSub0: RerootedSuper {}
1097+
class RerootedSub1: RerootedSub0 {}
1098+
1099+
if #available(SwiftStdlib 5.9, *) {
1100+
keyPath.test("_rerootKeyPath") {
1101+
let x = \RerootedSub1.x
1102+
1103+
let superValue = RerootedSuper()
1104+
let sub0 = RerootedSub0()
1105+
let sub1 = RerootedSub1()
1106+
1107+
let sub0Kp = _rerootKeyPath(x, to: RerootedSub0.self)
1108+
1109+
expectTrue(type(of: sub0Kp) == ReferenceWritableKeyPath<RerootedSub0, String>.self)
1110+
1111+
let superKp = _rerootKeyPath(x, to: RerootedSuper.self)
1112+
1113+
expectTrue(type(of: superKp) == ReferenceWritableKeyPath<RerootedSuper, String>.self)
1114+
1115+
let x0 = sub1[keyPath: sub0Kp] as! String
1116+
expectEqual(x0, "hello world")
1117+
1118+
let x1 = sub1[keyPath: superKp] as! String
1119+
expectEqual(x1, "hello world")
1120+
1121+
let x2 = sub0[keyPath: sub0Kp] as! String
1122+
expectEqual(x2, "hello world")
1123+
1124+
let x3 = sub0[keyPath: superKp] as! String
1125+
expectEqual(x3, "hello world")
1126+
1127+
let x4 = superValue[keyPath: superKp] as! String
1128+
expectEqual(x4, "hello world")
1129+
}
1130+
}
1131+
10891132
runAllTests()
10901133

0 commit comments

Comments
 (0)