Hi Slava,
> On Jan 4, 2017, at 6:28 PM, Slava Pestov via swift-dev <[email protected]>
> 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
[email protected]
https://lists.swift.org/mailman/listinfo/swift-dev