Hi Slava,

> On Jan 4, 2017, at 6:28 PM, Slava Pestov via swift-dev <swift-dev@swift.org> 
> wrote:
> 
> Hi all,
> 
> In Swift 3.0, the following examples both typecheck:
> 
> let fn1: ([Int]) -> () = {
>  let _: [Int] = $0
> }
> 
> let fn2: (Int...) -> () = {
>  let _: [Int] = $0
> }
> 
> This stopped working due to a regression in master so I'm looking at fixing 
> it.

Whoops, thanks!

> 
> While investigating this, I noticed that this variant with two parameters 
> doesn’t work in either release:
> 
> let fn3: (Int, Int...) -> () = { // cannot convert value of type '(_, _) -> 
> ()' to specified type '(Int, Int...) -> ()'
>  let _: Int = $0
>  let _: [Int] = $1
> }
> 
> The diagnostic doesn’t make sense, which suggests there’s a deeper underlying 
> problem.

Yeah.

> 
> Indeed, the reason the ‘fn2’ example works in Swift 3.0 is because we bind $0 
> to the single-element tuple type (Int…), which admits an implicit conversion 
> to [Int]. The closure literal gets the type ((Int…)) -> () — note the extra 
> pair of parentheses here. This works mostly on accident. For example if we 
> bind $0 to a generic parameter, SILGen blows up:
> 
> func id<T>(t: T) -> T {
>  return t
> }
> 
> let fn4: (Int...) -> () = {
>  id(t: $0) // segfault here
> }

Hmm. We’re not modeling the difference between the type of the parameter as 
seen in the body of the closure and the function input type as separate things.

> I think it would be better if we permitted an implicit conversion between 
> (T…) -> () and ([T]) -> (), or more precisely, erase varargs when matching 
> function arguments in matchFunctionTypes() if we’re performing a Subtype 
> conversion or higher.
> 
> After adding this to CSSimplify, I notice that in fn2, $0 now gets type [Int] 
> and the closure has type ([Int]) -> (), wrapped in a FunctionConversionExpr 
> converting to (Int…) -> (), which ends up being a no-op since varargs are 
> erased in SILGen. Also, fn3 and fn4 start working; in fn3, $1 gets type 
> [Int], and in fn4, we also correctly bind the generic parameter to [Int]. I 
> think this is a better situation overall. Values should not have types 
> containing vararg tuples, and we should prevent these types from showing up 
> in the type system as much as possible.
> 
> However, this more general conversion rule also means the following is 
> allowed, whereas it did not typecheck before:
> 
> func varargToArray<T>(fn: @escaping (T...) -> ()) -> ([T]) -> () {
>  return fn
> }
> 
> func arrayToVararg<T>(fn: @escaping ([T]) -> ()) -> (T...) -> () {
>  return fn
> }
> 
> This is essentially what Dollar.swift was doing, but they were using a 
> closure literal to achieve it.
> 
> At this time, the conversion can be performed without thunking, but if 
> varargs ever get a different representation, we can still thunk the 
> conversion like we do for re-abstraction, optionality changes, existential 
> erasure in function types, etc.
> 
> Does anyone foresee any problems with this approach? We could also 
> conceivably limit this conversion to closure literals only, and not general 
> subtype conversions.

Personally, I would prefer to limit this conversion to closure literals only. 
It’s a narrower change, and it avoids having to build a thunk if we do improve 
the representation of varargs.

        - Doug

_______________________________________________
swift-dev mailing list
swift-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-dev

Reply via email to