I wrote a property wrapper that works around this issue and the compiler is able to optimize into a faster set of instructions, based on a solution discovered by @Karl, assuming (like me) you're stuck working with the current version of Swift that has this issue and not the nightly, which seems to have largely fixed it.
@propertyWrapper
struct ExpensiveValue<Value> {
var wrappedValue: Value
var projectedValue: Value { optimizableFastGet() }
func optimizableFastGet() -> Value {
// Figure out the byte offset of Value, and return a copy of it
var copy = self
return withUnsafeMutablePointer(to: ©) {
copyPtr in
let rawCopyPtr = UnsafeRawPointer(copyPtr)
// MemoryLayout.offset(of:) would give us the value's byte offset too.
// The downside of that method is that it doesn't work well with generic types,
// when the compiler can't directly "see" what property the key path references
// and instead dynamically generates a key path at runtime, which is _expensive_.
//
// Honestly Swift's algorithm for the memory layout of structs means that
// wrappedValue will always be stored at byte offset 0,
// but that could change and therefore can't be relied upon.
let byteOffset = withUnsafeMutablePointer(to: ©Ptr.pointee.wrappedValue) {
rawCopyPtr.distance(to: $0)
}
// Safety checks
assert(
MemoryLayout<Self>.offset(of: \.wrappedValue) == byteOffset,
"wrappedValue stored outside of self!"
)
precondition(
(0..<MemoryLayout<Self>.size).contains(byteOffset),
"wrappedValue stored outside of self!"
)
return rawCopyPtr.load(fromByteOffset: byteOffset, as: Value.self)
}
}
}