As macros arrive to swift, I would like to discuss a topic about code generation that annoyed me for some time.
The main problem is that currently @escaping
modifier might be applied only to function types, but, on syntax level, there is no way reliably understand that type is function type or not.
Suppose I want to write a macro that generates memberwise public initializer:
enum Visibility { case `private`, `internal`, `public` }
@attached(member)
public macro initializer(_ visibility: Visibility = .internal)
@initializer(.public)
struct Foo {
var a: A
var b: B
/* Macro generates:
public init(a: A, b: B) {
self.a = a
self.b = b
} */
}
It's all well and good, before discovering that A
or B
is actually a function type.
And, afaik, there is no good way for doing that, other than explicitly specify to macro which types are function types, something like:
@attached(member)
public macro initializer(_ visibility: Visibility = .internal, funcTypes: Any.Type...)
@initializer(.public, funcTypes: Callback.self)
struct Foo {
typealias Callback = () -> Void
var a: A
var b: Callback
/* Macro generates:
public init(a: A, b: @escaping Callback) {
self.a = a
self.b = b
} */
}
Problem with this is that it's ugly and makes hard to understand and follow compilation errors. Imagine adding new field to struct and it yells somewhere that it's escapes, and to fix that you need to go to third place where macro is called.
May be we could consider that @escaping
attribute can be applied to any type that actually escapes? Or make another attribute with this meaning.