toph42
(Topher Hickman)
1
If the cases of my switch on an Int cover every number from Int.min to Int.max, why does it still make me put a default?
For example:
let number = 5
switch number {
case Int.min ... 0:
print("Too small")
case 1...100:
print("Reasonable")
case 101 ... Int.max:
print("Too big")
}
Even though there isn’t a value number could be that is not covered by these three cases, I still get “Switch must be exhaustive” (in a Swift Playground).
I know I could change the final case to default: or case _: to satisfy the compiler, but at the cost of expressiveness. Why doesn’t the compiler recognize that this switch is exhaustive?
1 Like
Lantua
2
AFAIK, currently, there isn't exhaustive checking in place for types beyond enums & tuples (don't quote me on that).
6 Likes
toph42
(Topher Hickman)
3
That makes sense and is reasonable.
See [SR-766] Switch doesn't need to cover negative Int cases · Issue #43378 · apple/swift · GitHub
Unfortunately, integer operations like '...' and '<' are just plain functions to Swift, so it'd be difficult to do this kind of analysis. Even with special case understanding of integer intervals, I think there are still cases in the full generality of pattern matching for which exhaustiveness matching would be undecidable. We may eventually be able to handle some cases, but there will always be special cases involved in doing so.
and Bool :)
5 Likes
toph42
(Topher Hickman)
5
Huh. I’d never looked up the Bool definition. I’d always assumed it was an enum. Apparently it is a struct.
3 Likes
ole
(Ole Begemann)
6
Bool used to be an enum in Swift’s early days. It was changed to a struct in late 2013.
6 Likes
tera
7
Was hit with this gotcha just now. Nothing major, but feels like a bug.
There are always going to be patterns that the compiler cannot prove are exhaustive. I don't think our current limitations are unreasonable.
1 Like
I wonder if the compiler has improved to the point that Bool could be defined as an enum, ABI notwithstanding.
tera
10
Very similar case:
func foo() {
for i in 0 ... 1 {
switch i { // Error: Switch must be exhaustive
case 0: break
case 1: break
}
}
}
2 Likes
idrougge
(Iggy Drougge)
11
There are always going to be patterns that the compiler cannot catch, but some patterns are trivial and expected to be caught.
toph42
(Topher Hickman)
12
I guess the question is where to draw the line between "trivial and should be automatic" and "compiler magic" (and for someone to implement it).
Then wouldn't it be better for the compiler error to ask for a default case? That would save some time for newbies, who will most likely spend time trying to be exhaustive before adding default case.
1 Like
mgriebling
(Mike Griebling)
14
Or the compiler could just do the "right thing" and put in a default case with an assertion. If the existing cases are truly exhaustive - no harm done; otherwise, for newbies, they get an assertion for a missing case.
neerav512
(Neerav Kothari)
15
enums I can understand. Can you give me an example in tuples?
neerav512
(Neerav Kothari)
16
Btw, I tried this...
//checking if switch is exhaustive
let x = 5
switch x {
case Int.min...Int.max:
print("x=\(x)")
}
...and it didn't compile.
tera
17
func foobar(v: (Bool, Bool)) {
switch v {
case (false, false): print("false false")
case (false, true): print("false true")
case (true, false): print("true false")
case (true, true): print("true true")
// default clause is not needed, all possible cases are enumerated
}
}
As mentioned above this is a known limitation. Also, merely putting things in a struct breaks exhaustiveness checks:
struct S: Equatable {
let bool: Bool
}
func foobar(v: S) {
switch v { // 🛑 Switch must be exhaustive
case S(bool: false): print("false")
case S(bool: true): print("true")
}
}
1 Like
tera
18
Bool exhaustiveness checks are not perfect either:
func foo(_ v: Bool) {
let myfalse = false
switch v {
case false: print("false");
case true: print("true")
case myfalse: print("myfalse") // ✅ expected warning: "Case will never be executed"
}
switch v {
case myfalse: print("myfalse")
case false: print("false") // 👎 no warning here
case true: print("true")
}
switch v {
case false: print("false")
case myfalse: print("myfalse") // 👎 no warning here
case true: print("true")
}
switch v { // 👎 🛑 unexpected error: Switch must be exhaustive
case myfalse: print("myfalse")
case true: print("true")
}
switch v { // 🛑 unexpected error: Switch must be exhaustive
case false: print("myfalse")
case !false: print("true")
}
}
1 Like