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