New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[SR-13454] Wrong overloaded function called #55896
Comments
Simplified reproducer: extension Array {
func foo<T>(_: @escaping @autoclosure () -> T) -> Self {
print("A")
return self
}
func foo<T>(_: @escaping (Element) -> Array<T>) -> Self {
print("B")
return self
}
}
let closure: (Int, Int) -> [Int] = { _, _ in return [] }
_ = [].flatMap { _ in [(0, 1)] }.foo(closure) Prints |
@xedin @DougGregor A side effect of forward-scan for trailing closures? |
I was looking at debug constraints output and I noticed it was choosing one overload over the other due to a failed constraint... not sure if it’s related to trailing closures though. |
@swift-ci create |
The behavior in Xcode 12 is correct, because picking `run` overload with signature `(Element) -> Array<T>` requires a function conversion to be called with `doSomething`. Convertion would transform two arguments of `doSomething` into a single tuple type to be used for a `Element` when calling `run`. So when `run` is used in combination with `flatMap` it would mean that we’d always prefer an overload which requires the least number of conversions - in this case `@autoclosure () -> T` and there is no other contextual information available to clarify the choice. The reason why it picks another overload of `run` when `run` is used separately from `flatMap` is due to an incorrect hack in constraint solver which tries to compare two generic overloads to determine which one is better and ignoring all of the other context in the process, this is something which we need to fix and once that's done, calls to `f1` and `f2` are going to consistently pick `run<T>(_ effect: @escaping @autoclosure () -> T) -> Self` overload. This is also behavior which is consistent with SE-0110. |
Comment by Alex Robinson (JIRA) Thanks for the explanation @xedin. Even with that insight, it's still surprising behaviour to me. Annotating with a return type (below) makes it run the function that I expect, but to anyone reading or writing this code there's no ambiguity at the call site, and nothing warns users about possible ambiguity either. [1].flatMap { _ -> [(String, String)] in
[("X", "x")]
}
.run(doSomething)
// prints: Run; flat mapping: [("X", "x")]
Or is this just another version of the bug in f2() and this should also not end up calling the flatMap version? |
The difference here is that result of closure argument passed to flatMap is specified explicitly which rules out overload of flatMap which accepts a closure that returns `ElementOfResult?` so "expected" overload `run` gets picked as a result. |
Environment
macOS 10.15.6 (19G2021)
Xcode 12 beta 5
Xcode 12 beta 6
Additional Detail from JIRA
md5: 8417e922afc81d139a2d6f25a08b54a1
Issue Description:
Using this code as an example:
Expected output:
Actual output:
I expect the implementations of
f1()
andf2()
to follow the exact same code path, calling therun
function that usesflatMap
. This is the case in Xcode 11, and I believe earlier Xcode 12 betas.In Xcode 12 betas 5 and 6,
f1()
uses themap
version.It may be related to tuples, as
f3()
andf4()
– which don't use tuples – both run through the flat mapping version as expected.The text was updated successfully, but these errors were encountered: