Motivation:
Presently when working with Swift closures there's a shortfall when working with closures that need to be declared with the @convention(...) attribute. Consider the following situation when working with the JavaScriptCore framework:
func makeJavaScriptFunctionValue(with context: JSContext) -> JSValue {
let block: @convention(block) (JSValue) -> JSValue = { value in
...
return value
}
return JSValue(object: block, in: context)
}
Or the scenario where we need to use objc_msgSend to send an Objective-C message:
func send(message: String, to object: AnyObject) {
typealias Function = @convention(c) (AnyObject, Selector, String) -> Void
let function = unsafeBitCast(objc_msgSend, Function.self)
function(object, #selector(sendMessage(_:)), to: message)
}
In both of these cases we need are unable to inline the @convention(...) attribute and need to either create a local variable with explicit typing, or create a new typealias to accommodate the attribute.
Proposed Solution:
Extend the Swift complier to allow these attributes to be inlined with the declaration of the closure/type as we can currently do with closures that don't use these attributes.
With this solution, the above scenarios would look like this:
func makeJavaScriptFunctionValue(with context: JSContext) -> JSValue {
return JSValue(object: { @convention(block) (value: JSValue) -> JSValue in
...
return value
}, in: context)
}
func send(message: String, to object: AnyObject) {
let function = unsafeBitCast(objc_msgSend, to: (@convention(c) (AnyObject, Selector, String) -> Void).self)
function(object, #selector(sendMessage(_:)), message)
}
This change would merely be a convenience or cosmetic change as there is currently a way to declare blocks with @convention(...) attributes, albeit a more cumbersome way. Adding this proposed change would allow for using closures in this way with other convenience features of the language, e.g. trailing closures, that are not currently possible when using these attributes.
As part of this change the parameters & return type of the closure could be omitted if the compiler is able to infer those from context:
// func sort<T>(_ array: [T], by handler: (T, T) -> Bool) where T: Comparable
func foobar(_ array: [Int]) {
let sortedArray = sort(array) { @convention(block) in $0 < $1 }
...
}
And lastly, @convention(...) attributes should be able to used in as statements when declaring new closures:
let closure = { int1, int2 in
return int1 + int2
} as (@convention(c) (Int, Int) -> Int)
// `closure` is of type `@convention(c) (Int, Int) -> Int`
Impact on Existing Code:
None that I can think of; this change would simply provide an alternative way of declaring closures with @convention(...) attributes.
Alternatives Considered:
The only other alternative that was considered was to place the @convention(...) attribute outside of the closure. Upon consideration I believe that doing this would interrupt the flow of how the code reads as well as potentially add confusion as to where the attribute applies.