SE-0279: Multiple Trailing Closures

The issue with {} that you don't have with () is that the former is already in use for closures. If what you put in your {} can be parsed both as a closure and an argument list, then the syntax is ambiguous.

Consider this:

func foo<T>(_ arg: T) {}

foo {
   bar()
}

If the braces represent an argument list, then it's equivalent to foo(bar()). But this is also a valid trailing closure in today's Swift and it means foo({ bar() }). It can't be both, obviously.

For the thing to be unambiguous, it needs to have at least one argument label. Then the parser could start as usual, assuming it's a trailing closure, and then backtrack and reinterpret everything as an argument list on the first label it sees (that isn't followed by a loop keyword). That seems like a recipe for confusing diagnostic messages however: if reinterpreting as an argument list fails too, it won't be able to tell you whether your argument list is malformed or whether you have an incorrect statement in your closure.

Honestly, I'm pretty sure we can't have label-less parameters appearing in a brace-delimited parameter list.


It can made unambiguous for the parser by looking at the keyword that follows. But for the human eye I think it'll be extremely confusing if this:

foo {
   label: while true { ... }
}

is a trailing closure as in

foo({ label: while true { ... } })

while this:

foo {
   label: if true { ... } else { ... }
}

is an argument list as in

foo(label: if true { ... } else { ... })

Obviously, that's only a problem if we want to pursue the idea of making if/else an expression.

5 Likes