Thank you for reaching out @xedin! I'm excited to keep digging into property wrappers (and I'll definitely message you with questions!)
I've been following your pointer on how closure parameters are handled and reading through SE-293. Additionally, I've been stepping through this small example I made:
@propertyWrapper
struct A {
var wrappedValue: Int
}
let c = { (@A x) in
let i = x
let j = _x
return (i, j)
}
and looking at the SIL the compiler generates along with what is happening as we move through the type checker. I think I've got it down to what would be an equivalent surface syntax (modulo names and usages of @_silgen_name:
struct A {
var wrappedValue: Int
}
let c: (Int) -> (Int, A) = { x in
func body(_ _x: A) -> (Int, A) {
var x: Int {
get {
_x.wrappedValue
}
}
let i = x
let j = _x
return (i, j)
}
func property_wrapper_backing_initializer(_ x: Int) -> A {
A(wrappedValue: x)
}
return body(property_wrapper_backing_initializer(x))
}
It gives me hope that this produces exactly the SIL that the property wrapped example currently does! Granted, this is a simple example and I'm a human who doesn't have to algorithmically infer types, but it's cool to see that (hypothetically) I'm on the right track.
However, I do have one concern centering around names. Property wrappers influence name mangling, meaning we need some way of ensuring that names are kept consistent with the current implementation when lowering property wrappers through macros. I'm thinking this means that we need some way of introducing a function that is local to the closure body (like we have in the example; body and property_wrapper_backing_initializer are scoped to c) but still able to be named by @_silgen_name (as an aside, usage of the attribute for ABI capability reasons seems to be within the accepted use, but I'd just want to confirm that's true). This means that we'd also need to translate some of the name mangling generation logic to our macro? Or we'd need to query the compiler through context to give us a mangled name for a syntax node? Of course, we'd also need to add macros to function/closure parameters that allows injection into the function body (as well as at the call site for function parameter property wrappers).
As far as type inference goes, there seemed to be a good idea last year about a compiler intrinsic #inferredType (or maybe a method on context?) that would act as a "type hole" for macros to produce and allow the solver to plug back in when the macro is fully expanded. When the syntax returned is converted into an actual tree, we could collect all usage points of #inferredType as pointers to AST nodes that the solver needs to go back and solve for us. The compiler should have the same type information pre- and post- property wrapper expansion so hopefully this would work, but I still need to look at the property wrapper specific type check requests in this context to see what actually happens.
While playing around with this, I found a fun issue with globally scoped closures with property wrapped parameters that I'll look into as I work!