Skip to content

Commit c7f4de5

Browse files
authored
(113652078) Predicate incorrectly asserts on some keypaths
* (113652078) Predicate incorrectly asserts on some keypaths to resilient types * (113652078) Switch to using MemoryLayout.offset(of:) for stored property inspection * (113652078) Fix AnyKeyPath to PartialKeyPath casting
1 parent feed84d commit c7f4de5

File tree

2 files changed

+41
-5
lines changed

2 files changed

+41
-5
lines changed

Sources/FoundationEssentials/Predicate/KeyPath+Inspection.swift

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ extension UInt32 {
1919
private static var COMPUTED_COMPONENT_PAYLOAD_ARGUMENTS_MASK: UInt32 { 0x0008_0000 }
2020
private static var COMPUTED_COMPONENT_PAYLOAD_SETTABLE_MASK: UInt32 { 0x0040_0000 }
2121

22-
fileprivate static var PROPERTY_OFFSET_TOO_LARGE: UInt32 { 0x00FF_FFFF }
23-
2422
fileprivate var _keyPathHeader_bufferSize: Int {
2523
Int(self & Self.KEYPATH_HEADER_BUFFER_SIZE_MASK)
2624
}
@@ -42,6 +40,10 @@ extension UInt32 {
4240
}
4341
}
4442

43+
private func _keyPathOffset<T>(_ root: T.Type, _ keyPath: AnyKeyPath) -> Int? {
44+
MemoryLayout<T>.offset(of: keyPath as! PartialKeyPath<T>)
45+
}
46+
4547
extension AnyKeyPath {
4648
private static var WORD_SIZE: Int { MemoryLayout<Int>.size }
4749

@@ -55,9 +57,11 @@ extension AnyKeyPath {
5557
case 1: // struct/tuple/self stored property
5658
fallthrough
5759
case 3: // class stored property
58-
// Stored property components are either just the payload, or the payload plus 32 bits if the payload is the sentinel value
59-
let size = (firstComponentHeader._keyPathComponentHeader_payload == .PROPERTY_OFFSET_TOO_LARGE) ? MemoryLayout<UInt64>.size : MemoryLayout<UInt32>.size
60-
if header._keyPathHeader_bufferSize > size {
60+
// Key paths to stored properties are only single-component if MemoryLayout.offset(of:) returns an offset
61+
func project<T>(_: T.Type) -> Bool {
62+
_keyPathOffset(T.self, self) == nil
63+
}
64+
if _openExistential(Self.rootType, do: project) {
6165
fatalError("Predicate does not support keypaths with multiple components")
6266
}
6367
case 2: // computed

Tests/FoundationEssentialsTests/PredicateTests.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,38 @@ final class PredicateTests: XCTestCase {
680680
XCTAssertTrue(try _build(true).evaluate(1))
681681
XCTAssertFalse(try _build(false).evaluate(1))
682682
}
683+
684+
func testResilientKeyPaths() {
685+
// Local, non-resilient type
686+
struct Foo {
687+
let a: String // Non-resilient
688+
let b: Date // Resilient (in Foundation)
689+
let c: String // Non-resilient
690+
}
691+
692+
let now = Date.now
693+
let _ = Predicate<Foo> {
694+
PredicateExpressions.build_Conjunction(
695+
lhs: PredicateExpressions.build_Equal(
696+
lhs: PredicateExpressions.build_KeyPath(
697+
root: PredicateExpressions.build_Arg($0),
698+
keyPath: \.a
699+
),
700+
rhs: PredicateExpressions.build_KeyPath(
701+
root: PredicateExpressions.build_Arg($0),
702+
keyPath: \.c
703+
)
704+
),
705+
rhs: PredicateExpressions.build_Equal(
706+
lhs: PredicateExpressions.build_KeyPath(
707+
root: PredicateExpressions.build_Arg($0),
708+
keyPath: \.b
709+
),
710+
rhs: PredicateExpressions.build_Arg(now)
711+
)
712+
)
713+
}
714+
}
683715
}
684716

685717
#endif

0 commit comments

Comments
 (0)