Property wrapper on a function's signature

@propertyWrapper
final class Wrapper<T> {

    final class InnerWrapper {
        let val: T
        init(val: T) {self.val = val}
    }

    var storedValue: InnerWrapper

    var projectedValue: InnerWrapper {
        storedValue
    }

    var wrappedValue: T {
        self.storedValue.val
    }
    
    init(wrappedValue: T) {
        self.storedValue = InnerWrapper(val: wrappedValue)
    }
    
    init(projectedValue: Wrapper) {
        self.storedValue = projectedValue.storedValue
    }
}

in a type's body, it works fine:

class Base {
    @Wrapper // It works fine.
    var x = 0

    func run() {
        let y = x
        // foo(val: $x)
    }
}

when using it on a function, I got an error:
cannot convert value of type 'Wrapper<T>.InnerWrapper' to expected argument type 'Wrapper<T>'

func foo<T>(@Wrapper val: T) {}

I think it is a bug??

It might be a bug. But does it matter? The reason you're seeing it is because your projectedValue labeling is inaccurate.

There are two ways to solve it:

  1. Don't use the argument label projectedValue.
init(_ projectedValue: Wrapper) {
  storedValue = projectedValue.storedValue
}
  1. Don't use the obfuscation of storedValue, which isn't doing anything useful.
var wrappedValue: T { projectedValue.val }
var projectedValue: InnerWrapper

init(wrappedValue: T) {
  projectedValue = .init(val: wrappedValue)
}

init(_ wrapper: Wrapper) {
  projectedValue = wrapper.projectedValue
}

The error message is not helpful, but the error is because your init(projectedValue) accepts the wrapper type instead of the projected value type. This fixes the problem:

@propertyWrapper
final class Wrapper<T> {

    final class InnerWrapper {
        let val: T
        init(val: T) {self.val = val}
    }

    var storedValue: InnerWrapper

    var projectedValue: InnerWrapper {
        storedValue
    }

    var wrappedValue: T {
        self.storedValue.val
    }

    init(wrappedValue: T) {
        self.storedValue = InnerWrapper(val: wrappedValue)
    }

    init(projectedValue: InnerWrapper) {
        self.storedValue = projectedValue
    }
}

func foo<T>(@Wrapper val: T) {}

It's worth filing a bug Issues · apple/swift · GitHub to improve the error message!

1 Like