this is an interesting question... the supported/intended interactions between property wrappers and observing accessors do appear to be rather vague to me. the example from the evolution document gives the following description of how the wrapper 'expansion' works:
@Lazy var foo = 1738
// translates to
private var _foo: Lazy<Int> = Lazy<Int>(wrappedValue: 1738)
var foo: Int {
get { return _foo.wrappedValue }
set { _foo.wrappedValue = newValue }
}
but the fact that you can include an observing accessor on the wrapped property declaration means that the use of a property wrapper enables functionality that cannot be re-created by simply 'desugaring' the wrapper annotation itself. e.g. if we explicitly use the codegen pattern that the wrapper expansion is said to provide on the given example, it is rejected:
var _value: Wrap<Int> = Wrap<Int>(wrappedValue: 0)
var value: Int {
get { _value.wrappedValue }
set { _value.wrappedValue = newValue }
didSet { print("didSet") } // 🛑 'didSet' cannot be provided together with a getter
}
so it seems that in this manner, 'wrapped properties' are something of a hybrid between computed properties, which don't admit observing accessors, and stored properties, which do. if we think of these as essentially just slightly-special computed properties, then the current behavior/limitations perhaps makes more sense, but i can see arguments in both directions.
cc @John_McCall – since accessors have presumably been on your mind recently, do you have any insights to share on this matter?