diff --git a/stdlib/public/core/KeyPath.swift b/stdlib/public/core/KeyPath.swift index 05102ed10389e..d7914b979afe5 100644 --- a/stdlib/public/core/KeyPath.swift +++ b/stdlib/public/core/KeyPath.swift @@ -3893,6 +3893,50 @@ internal func _instantiateKeyPathBuffer( return offset } +@available(SwiftStdlib 5.9, *) +public func _createOffsetBasedKeyPath( + root: Any.Type, + value: Any.Type, + offset: Int +) -> AnyKeyPath { + func openRoot(_: Root.Type) -> AnyKeyPath.Type { + func openValue(_: Value.Type) -> AnyKeyPath.Type { + KeyPath.self + } + + return _openExistential(value, do: openValue(_:)) + } + + let kpTy = _openExistential(root, do: openRoot(_:)) + + // The buffer header is 32 bits, but components must start on a word + // boundary. + let kpBufferSize = MemoryLayout.size + MemoryLayout.size + return kpTy._create(capacityInBytes: kpBufferSize) { + var builder = KeyPathBuffer.Builder($0) + let header = KeyPathBuffer.Header( + size: kpBufferSize - MemoryLayout.size, + trivial: true, + hasReferencePrefix: false + ) + + builder.pushHeader(header) + + let componentHeader = RawKeyPathComponent.Header( + stored: .struct, + mutable: false, + inlineOffset: UInt32(offset) + ) + + let component = RawKeyPathComponent( + header: componentHeader, + body: UnsafeRawBufferPointer(start: nil, count: 0) + ) + + component.clone(into: &builder.buffer, endOfReferencePrefix: false) + } +} + #if SWIFT_ENABLE_REFLECTION @_silgen_name("swift_keyPath_copySymbolName") diff --git a/test/stdlib/KeyPath.swift b/test/stdlib/KeyPath.swift index 734de6fc9d064..68ea4257c34c6 100644 --- a/test/stdlib/KeyPath.swift +++ b/test/stdlib/KeyPath.swift @@ -1065,5 +1065,26 @@ keyPath.test("ReferenceWritableKeyPath statically typed as WritableKeyPath") { expectEqual(outer[keyPath: upcastKeyPath], 46) } +struct Dog { + var name: String + var age: Int +} + +if #available(SwiftStdlib 5.9, *) { + keyPath.test("_createOffsetBasedKeyPath") { + let dogAgeKp = _createOffsetBasedKeyPath( + root: Dog.self, + value: Int.self, + offset: 16 + ) as? KeyPath + + expectNotNil(dogAgeKp) + + let sparky = Dog(name: "Sparky", age: 7) + + expectEqual(sparky[keyPath: dogAgeKp!], 7) + } +} + runAllTests()