Closed swift-ci closed 7 years ago
This is expected behavior: the compiler cannot tell that the two blacks and the two whites are semantically identical, and so needs to unambiguously choose one of them whenever those names are referenced. By default, enums with raw values automatically get == comparisons with their own type, meaning the following functions exist:
func == (lhs: BlackWhite, rhs: BlackWhite) -> Bool {
// ...
}
func != (lhs: BlackWhite, rhs: BlackWhite) -> Bool {
// ...
}
func == (lhs: EmptyBlackWhite, rhs: EmptyBlackWhite) -> Bool {
// ...
}
func != (lhs: EmptyBlackWhite, rhs: EmptyBlackWhite) -> Bool {
// ...
}
With just these implicit functions, references like `someBlackWhite == .black` or `someEmptyBlackWhite != .black` is unambiguous: for the first case, `someBlackWhite` has type `BlackWhite` and so the only `==` operator that can be used is the first one above, this forces the second argument to also have type `BlackWhite` and thus `.black` must be a member of this type. Similarly, the second example can only use the last `!=` operator and so the second argument is also forced to be `EmptyBlackWhite`.
With the new operators, there's no longer a single choice when the type of the first argument is chosen. For `someBlackWhite == .black`, this could refer to either
func == (lhs: BlackWhite, rhs: BlackWhite) -> Bool
func == (lhs: BlackWhite, rhs: EmptyBlackWhite) -> Bool
And the compiler has no way to work out which you wanted.
A good way to fix this is to not have two separate types, and, assuming the example is close to your real code, the correct way to add an "empty" case is to use `Optional`. Instead of `EmptyBlackWhite`, one can write `BlackWhite?`, with `nil` being `.empty`. This is much "Swiftier" than having two separate enums, and also works nicely with operators like == due to careful overloads in the standard library, e.g. all of the following compile and behave as one might expect (and so do all the various permutations/combinations)
let x: BlackWhite = ...
let y: BlackWhite? = ...
x == y
x == .black
y == .black
y == nil
Comment by Anders Kierulf (JIRA)
Thanks for the explanation, makes sense; the bug was that this worked before, not that it is no longer working.
Using optional BlackWhite instead of EmptyBlackWhite is a good suggestion that will work in some cases where I'm using EmptyBlackWhite, but not in general, as it also includes a fourth case (blackAndWhite) that's used in low-level bitboards (for the game of Go).
Oh, I apologize, I completely missed that it was a difference between versions. I can reproduce it, and I have no idea what's going on.
@swift-ci create
The current behavior looks correct to me, although I do not know what changed.
You can always disambiguate by using the full name rather than just the dot followed by member.
I took a closer look at this. We used to compile this without error due to a hack in the type checker that would stop looking at overloads under some conditions. That hack was narrowed, which is why we are now (correctly) diagnosing this as ambiguous.
Environment
Xcode 9.0 beta 3 (9M174d), both when compiled as Swift 3.2 and Swift 4.0.Additional Detail from JIRA
| | | |------------------|-----------------| |Votes | 0 | |Component/s | Compiler | |Labels | Bug, 4.0Regression | |Assignee | None | |Priority | Medium | md5: 7001de01ec0f975646ce9040af0d79b9Issue Description:
The following code shows a bug in Xcode 9.0 beta 3 (9M174d) that was not there in beta 2. The references to .black and .white are marked as errors, as it doesn't know whether to use the one in BlackWhite or the one in EmptyBlackWhite:
Ambiguous use of 'black'
When commenting out the comparison functions, the error disappears.