Value and Type Parameter Packs implicitly unwrap optionals

When using Value and Type Parameter Packs, I can mix optional and non-optional values.
Example code:

func weakify<T: AnyObject, each U>(_ obj: T?, _ block: @escaping (T, repeat each U) -> Void) -> (repeat each U) -> Void {
    return { [weak obj] (values: repeat each U) in
        guard let obj else { return }

        block(obj, repeat each values)
    }
}

class SomeClass {

    func someFunc(number: Int, string: String) {
        print(number, string)
    }
}

let someClass = SomeClass()
var someClosure: ((Int, String?) -> Void)?

someClosure = weakify(someClass) { someClass, number, string in
    someClass.someFunc(number: number, string: string)
}
someClosure?(1, "")

It compiles and works, but crashes when passing nil:

someClosure?(1, nil)

1 Like

What version of Swift are you using?
In Xcode 15.3 (Swift 5.10), weakify does not compile, with the error given as follows:

error: cannot fully abstract a value of variadic function type '(repeat each U) -> ()' because different contexts will not be able to reliably agree on a calling convention; try wrapping it in a struct

Xcode 15.0.1(Swift 5.9), 15.2(Swift 5.9.2), 15.3(Swift 5.10).

This error appears to be a known bug in Playground:

2 Likes

Ah yes, good point.
I can indeed reproduce and reduce the test case:

func f<each U>(_ block: @escaping (repeat each U) -> Void) -> (repeat each U) -> Void {
    return { (values: repeat each U) in block(repeat each values) }
}
func g(_ x: Int) { print(x) }

let closure: (Int?) -> Void = f { g($0) }
closure(nil) // compiles; runtime error

It does seem unexpected that the compiler permits this with implicit unwrapping.
cc @hborla

The same is of course not permitted when parameter packs are not involved:

func f2<U>(_ block: @escaping (U) -> Void) -> (U) -> Void {
    return { (value: U) in block(value) }
}
func g(_ x: Int) { print(x) }

let closure: (Int?) -> Void = f2 { g($0) } // compile-time error
closure(nil)
2 Likes