swiftlang / swift

The Swift Programming Language
https://swift.org
Apache License 2.0
67.58k stars 10.36k forks source link

[SR-12908] Enum comparison diagnostics not helpful #55354

Open swift-ci opened 4 years ago

swift-ci commented 4 years ago
Previous ID SR-12908
Radar rdar://problem/64129806
Original Reporter stevex (JIRA User)
Type Bug
Environment Xcode 11.5
Additional Detail from JIRA | | | |------------------|-----------------| |Votes | 0 | |Component/s | Compiler | |Labels | Bug, DiagnosticsQoI, TypeChecker | |Assignee | None | |Priority | Medium | md5: 8da8c52d1253a9ea892c633357d60129

Issue Description:

Not a bug, but maybe an enum usability enhancement via better diagnostics. I went through some head scratching trying to figure out how to use an if statement to check a Result (or any enum with an associated value) for success.

The message that mentioned DispatchTimeoutResult seems out of left field, but neither message was actually helpful.

import Cocoa
import Foundation

enum TwoValues {
    case one
    case two
}

let one = TwoValues.one

if one == .one {
    print("As expected")
}

if case .one = one {
    print("Also ok but not how you'd usually use an enum")
}

let r = Result<String, Error>.success("Yay")

// Cannot convert value of type 'Result<String, Error>' to expected argument type 'DispatchTimeoutResult'
if r == .success {
    print("yay?")
}

// Binary operator '==' cannot be applied to two 'Result<String, Error>' operands
if r == .success(_) {
    print("yay?")
}

// This works
if case .success(_) = r {
    print("yay!")
}

Thanks

LucianoPAlmeida commented 4 years ago

cc @xedin @hamishknight

LucianoPAlmeida commented 4 years ago

The problem here seems like for the case:

// Cannot convert value of type 'Result<String, Error>' to expected argument type 'DispatchTimeoutResult'
if r == .success {
    print("yay?")
}

The solver is inferring .success as DispatchTimeoutResult.result, I didn't debug it, but apparently that's the best solution it is finding of all disjunction choices.
So maybe adjusting this to favor the binding Result\<String, Error> in some way (since the type of r is known upfront) could improve the situation? @xedin

For the other case

// Binary operator '==' cannot be applied to two 'Result<String, Error>' operands
if r == .success(_) {
    print("yay?")
}

Just tweak the diagnostics when involving an enum with value?

xedin commented 4 years ago

Diagnostic for `r == .success` is definitely incorrect it should at least be `cannot be applied to operands`, but the main issue here is that `==` can't be used to pattern match `r` to `.success(_)`, that's the job of `if case`.

xedin commented 4 years ago

@swift-ci create

LucianoPAlmeida commented 4 years ago

@xedin Yeah, there is something interesting happening here... if we remove the Foundation import, we got a `cannot be applied to operands` for `r == .success` that is why I mentioned that it could be that the solver is finding `DispatchTimeoutResult` defined in Foundation as the solution for the base type of `.success`(DispatchTimeoutResult.success that is a case without associated value) which leads to bad diagnostic.

I still didn't debug it so this is just a guess, but I was curious about why this may be happening.