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-7478] type checker pretty confused #50021
Comments
@swift-ci create |
I think this is correct-ish behavior, in that we consider it legal to convert a closure-returning-T to a closure-returning-Void. @xedin? |
@belkadan are you sure? The inner boxIt { 3 } returns a boxIt { boxIt { 3 } } |
Sure you can. The closure
has type |
@weissi @belkadan is right, there is an implicit conversion from `() -> T` to `() -> Void` for trailing closures which we support and because the type-checker is very smart it infers contextual `Void` as `T` (as only possible for this expression) and then converts result of `() -> Box<Int>` into `() -> Void`. |
This is related to the behavior of the type-checker where it would try to find a solution based on the information given and if there is a possible implicit conversion, it would be taken but the score is going to get increased, so if there are solutions without such conversion they would be considered a "better" match. For single-expression closures we don't model `return` statement so both { return 42 } and { 42 } are equivalent which leads to this behavior. |
thanks @xedin that makes sense. Isn't that a bug though? Where it caused trouble for us is the following in the NIO tests: (https://github.com/apple/swift-nio/blob/9f01374169e4f19785edfebdeda0bfef9ae44abf/Tests/NIOTests/SocketChannelTest.swift#L428-L437) XCTAssertNoThrow(try channel.eventLoop.submit {
channel.register().map { () -> Void in
channel.connect(to: serverChannel.localAddress!, promise: connectPromise)
}.map { () -> Void in
XCTAssertFalse(connectPromise.futureResult.isFulfilled)
// The close needs to happen in the then { ... } block to ensure we close the channel
// before we have the chance to register it for .write.
channel.close(promise: closePromise)
}
}.wait().wait() as Void) the return value from What is reason we allow implicitly converting a closure |
I don't think I follow this argument, in the minimized example as in this one the contextual type is Void. If there is no Void type in the context it would not be inferred e.g. `let _ = { 42 }` and `let _ = { return 42 }` would both return `() -> Int`. |
@xedin but you could write `{ _ = 42 }` (if 42 had side-effects) to make it `() -> Void` if you really wanted. If I write `{ 42 }` I really mean `() -> Int` and I don't want it to be converted to `() -> Void` |
Sure, but once again if your context requires () -> Void there is an implicit conversion for that, e.g.
This is very much intentional way the language works but only for `Void` and only single-expression closures. If this is good or bad aside, changing that would require evolution proposal. |
@xedin wow, I did not know this would compile, I think that's really bad but you're right evolution is the place to sort that out. |
Additional Detail from JIRA
md5: 28e7ee8b48b9b181087d7b3524a3c9c9
Issue Description:
The type checker in quite a few versions of Swift is very confused and thinks
Box<Int>
is equal toVoid
. See this little demo program:it clearly should compile error on the last line telling that
Box<Int>
isn'tVoid
. This is what various Swift versions do. (Myjw-swift-...
wrappers just invokexcrun
with the right parameters)Swift 4.0 release:
which is VERY BAD as that compiled just fine with just a warning!
Swift 4.1 release (the only one that caught this error)
which is good as it errors.
very recent Swift master
or
which is very bad as it compiled JUST FINE again, so regressed back to where 4.0.3 was 🙁
which is bad as as regressed back to where Swift 4.0 was 🙁
The text was updated successfully, but these errors were encountered: