Skip to content

Commit 6ec8a8c

Browse files
authored
Merge pull request #64904 from Azoy/keypath-create-spi
[stdlib] Create an offset based keypath at runtime
2 parents f45d9f7 + e9c0b32 commit 6ec8a8c

File tree

2 files changed

+65
-0
lines changed

2 files changed

+65
-0
lines changed

stdlib/public/core/KeyPath.swift

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3893,6 +3893,50 @@ internal func _instantiateKeyPathBuffer(
38933893
return offset
38943894
}
38953895

3896+
@available(SwiftStdlib 5.9, *)
3897+
public func _createOffsetBasedKeyPath(
3898+
root: Any.Type,
3899+
value: Any.Type,
3900+
offset: Int
3901+
) -> AnyKeyPath {
3902+
func openRoot<Root>(_: Root.Type) -> AnyKeyPath.Type {
3903+
func openValue<Value>(_: Value.Type) -> AnyKeyPath.Type {
3904+
KeyPath<Root, Value>.self
3905+
}
3906+
3907+
return _openExistential(value, do: openValue(_:))
3908+
}
3909+
3910+
let kpTy = _openExistential(root, do: openRoot(_:))
3911+
3912+
// The buffer header is 32 bits, but components must start on a word
3913+
// boundary.
3914+
let kpBufferSize = MemoryLayout<Int>.size + MemoryLayout<Int32>.size
3915+
return kpTy._create(capacityInBytes: kpBufferSize) {
3916+
var builder = KeyPathBuffer.Builder($0)
3917+
let header = KeyPathBuffer.Header(
3918+
size: kpBufferSize - MemoryLayout<Int>.size,
3919+
trivial: true,
3920+
hasReferencePrefix: false
3921+
)
3922+
3923+
builder.pushHeader(header)
3924+
3925+
let componentHeader = RawKeyPathComponent.Header(
3926+
stored: .struct,
3927+
mutable: false,
3928+
inlineOffset: UInt32(offset)
3929+
)
3930+
3931+
let component = RawKeyPathComponent(
3932+
header: componentHeader,
3933+
body: UnsafeRawBufferPointer(start: nil, count: 0)
3934+
)
3935+
3936+
component.clone(into: &builder.buffer, endOfReferencePrefix: false)
3937+
}
3938+
}
3939+
38963940
#if SWIFT_ENABLE_REFLECTION
38973941

38983942
@_silgen_name("swift_keyPath_copySymbolName")

test/stdlib/KeyPath.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,5 +1065,26 @@ keyPath.test("ReferenceWritableKeyPath statically typed as WritableKeyPath") {
10651065
expectEqual(outer[keyPath: upcastKeyPath], 46)
10661066
}
10671067

1068+
struct Dog {
1069+
var name: String
1070+
var age: Int
1071+
}
1072+
1073+
if #available(SwiftStdlib 5.9, *) {
1074+
keyPath.test("_createOffsetBasedKeyPath") {
1075+
let dogAgeKp = _createOffsetBasedKeyPath(
1076+
root: Dog.self,
1077+
value: Int.self,
1078+
offset: 16
1079+
) as? KeyPath<Dog, Int>
1080+
1081+
expectNotNil(dogAgeKp)
1082+
1083+
let sparky = Dog(name: "Sparky", age: 7)
1084+
1085+
expectEqual(sparky[keyPath: dogAgeKp!], 7)
1086+
}
1087+
}
1088+
10681089
runAllTests()
10691090

0 commit comments

Comments
 (0)