-
Notifications
You must be signed in to change notification settings - Fork 10.6k
Description
Description
Swift tries to optimize out KeyPath accesses where possible. However, I could not get it to do this when the KeyPaths are stored within a parameter pack in a struct, even if all the code constructing and then using the KeyPaths is inlined into the client function. By comparison the alternative implementation, using a recursive generic struct instead of a parameter pack, does eliminate the KeyPath accesses.
Reproduction
Roughly, this implementation, which is supposed to reduce boilerplate for custom Comparable implementations and ad-hoc comparisons. (See the Compiler Explorer link for a complete example including usage)
import Foundation
infix operator <=>
func <=> <C: Comparable>(_ a: C, _ b: C) -> ComparisonResult {
if a < b {
return .orderedAscending
} else if a > b {
return .orderedDescending
} else {
return .orderedSame
}
}
struct ComponentwiseComparator<Root, each T: Comparable> {
let keyPaths: (repeat KeyPath<Root, each T>)
@inline(__always)
init(_ keyPaths: repeat KeyPath<Root, each T>) {
self.keyPaths = (repeat each keyPaths)
}
@inline(__always)
func compare(_ left: Root, _ right: Root) -> ComparisonResult {
for path in repeat each keyPaths {
switch left[keyPath: path] <=> right[keyPath: path] {
case .orderedSame:
break
case let x:
return x
}
}
return .orderedSame
}
}
Expected behavior
Above code is inlined the same as the recursive implementation, which ends up greatly reducing code size (compare static output.A.< infix(output.A, output.A) -> Swift.Bool
in the two assembly outputs)
Environment
Swift version 6.2 (swift-6.2-RELEASE)
Target: x86_64-unknown-linux-gnu
Additional information
No response