Swift 1.2b1: Type inferencing prefers () to generic types in context surrounding closure literals, dropping results
| Originator: | rix.rob | ||
| Number: | rdar://19917034 | Date Originated: | 22-Feb-2015 04:46 PM |
| Status: | Open | Resolved: | |
| Product: | Developer Tools | Product Version: | Xcode-Beta (6D520o) |
| Classification: | Serious Bug | Reproducible: | Always |
Summary:
In Swift 1.2, single-expression closures can be inferred to return () based on the surrounding context. However, when type inferencing is presented with two overloads of a function accepting the closure, one which accepts a closure returning () and another which accepts a closure returning some generic type, Swift seems to prefer the () one to the generic one.
In practice, this is extremely counterintuitive. My parser combinator framework Madness has run into this with the overloads of | (which produces the alternation of its operands).
Given this handy typealias for parsers:
struct Parser<C: CollectionType, T> {
typealias Function = (C, C.Index) -> (T, C.Index)
}
and the two relevant overloads of the | operator:
- (Parser<C, T>.Function, Parser<C, U>.Function) -> Parser<C, Either<T, U>>.Function
- (Parser<C, T>.Function, Parser<C, ()>.Function) -> Parser<C, T?>.Function
then:
let y = %"y" --> { _ in 1 } // % makes a literal, --> maps the parse trees of lhs using the rhs function
is inferred to have type Parser<String, Int>.Function, whereas:
let xy = %"x" | (%"y" --> { _ in 1 })
is inferred to have type Parser<String, String?>.Function instead of the expected—and actual, in Swift 1.1—type of Parser<String, Either<String, Int>>.Function.
Steps to Reproduce:
1. Compile this code:
func f<T>(x: Int -> T) -> T { return x(0) }
func f(x: Int -> ()) -> String { return "woo" }
let a = f { _ in 1 }
println(a + 1)
Expected Results:
I expected this to compile, because clearly given the integer literal returned by the closure passed to f, I intend it to select the first definition of f, the generic one.
Actual Results:
It actually errors:
void.swift:7:9: error: binary operator '+' cannot be applied to operands of type 'String' and 'Int'
println(a + 1)
^
void.swift:7:9: note: Overloads for '+' exist with these partially matching parameter lists: (Int, Int), (String, String), (UnsafeMutablePointer<T>, Int), (UnsafePointer<T>, Int)
println(a + 1)
^
Regression:
This worked fine in Swift 1.1, because Swift 1.1 didn’t ignore my closure’s return type in the face of ().
Providing a type annotation with let or as causes it to select the correct version, of course. But the whole point is that I don’t want to do that :)
Notes:
N/A
Comments
Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!