I'm trying to implement a macro that expands the following code
class Klass {
@ThreadSafe lazy var value: Int = compute()
func compute() -> Int { 42 }
}
to
class Klass {
var value: Int {
_read {
__macro_local_valueLock.lock()
defer { __macro_local_valueLock.unlock() }
yield __macro_local_value
}
_modify {
__macro_local_valueLock.lock()
defer { __macro_local_valueLock.unlock() }
yield &__macro_local_value
}
}
private nonisolated(unsafe) lazy var __macro_local_value: Int = compute()
private nonisolated let __macro_local_valueLock = UnfairLock()
func compute() -> Int { 42 }
}
but I'm getting an error
'lazy' cannot be used on a computed property
I suppose that the lazy properties aren't being handled.
The compiler removes the initializer for the generated property. Could it also remove the 'lazy' modifier?
I think the compiler error is correct here. Removing the lazy modifier is confusing because the accessor provided by the macro might nota actually be lazy, in which case the lazy modifier would be misleading.
The same can be said about the initial value. Removing it can lead to confusion, because the value may not be used by the macro. However, it is removed for calculated properties.
From the code, you can understand that the attribute is a macro, because the property wrapper cannot be used for lazy properties. And the fact that macros can change the code to which they are applied is expected behavior.
Macros can use the presence of lazy to support more usage scenarios.
Compare:
@ThreadSafe lazy var value: Int = compute()
And
@ThreadSafe(isLazy: true) var value: Int = compute()
First one feels more natural. And only first allows to use instance methods (for the second one, there will be an error “ Cannot use instance member 'compute' within property initializer; property initializers run before 'self' is available”).
The problem here is that the macro must be applied to valid Swift code. And the following code will be valid only if the value property has the lazy modifier:
class Klass {
var value: Int = compute()
func compute() -> Int { 42 }
}