-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
[Exhaustiveness Checking] Different behavior between statements and expressions #55934
Comments
That's expected behavior. Switch statements are not assumed to be exhaustive unless they are on a sealed type or enum. Type inference proceeds under the assumption that it's not exhaustive (possibly except for some very trivial cases that the type inference can see, like having a Then, if the switch wasn't assumed to be exhaustive, it's not checked whether it is actually exhaustive using the algorithm which can decide that, but which needs to run after type inference is done. Switch expressions, on the other hand, must be exhaustive, so they're assumed to be so, and then it's always checked afterwards to be sure. |
Thanks for the quick response! Why is this the case? Why not run the exhaustiveness algorithm on switch statements as well? I'd argue this is not the expected behavior for the end user, even if it matches the spec. The docs don't seem to indicate a difference in exhaustiveness checking between expressions and statements https://dart.dev/language/branches#exhaustiveness-checking. In fact, the docs demonstrate an exhaustiveness check performed over |
The issue is that type inference is affected by exhaustiveness, but exhaustiveness computation depends on the result of type inference. If we could somehow integrate exhaustiveness computation into the type inference phase, it might be possible to know precisely whether a switch is exhaustive or not right when type inference needs that information, but so far that has not been done. |
My two cents: I'd argue switch statements not being exhaustive is what the user should expect. In my mind, they are logically similar to chaining if-else blocks, so suggesting switch statements should be exhaustive is like suggesting those The question is if something like this is something we want: var myList = [1, "2", 3, 4, "five"];
for (var item in myList) {
switch (item) {
case int(): {
// do something special to numbers only
}
// should I *have* to add `case: String` or `default` here?
}
} I'd argue it is since otherwise it would become very burdensome to write "exhaustive but most cases ignored" switch statements. I'd imagine in most cases where you would want/need the guarantee of an exhaustive switch statement, you could just use a switch expression instead. In those cases I usually just find myself assigning the Curious what your thoughts are on this @mattrberry. All of this is really just a blurb on how I program in my own opinionated way. |
@skylon07 I'm not suggesting that switch statements must be exhaustive; I don't think you should always need a In my example above, both the statement and the expression exhaustively cover all possible cases, yet the compiler doesn't see that for the statement. What's particularly weird is when the compiler does recognize exhaustiveness in statements. For example, it can determine that
There are cases in which I think a statement results in clearer code, since the cases can't be expressed as blocks. There are workarounds, but IMO they're suboptimal |
The inference phase recognizes that That's the kind of "catch-all" patterns that the inference algorithm is capable of recognizing, but it's harder for it to recognize that more than one pattern together exhaust the input, even if neither does by itself. That's what requires the more complicated algorithm, which also requires type inference to have run first. |
@mattrberry Ah, I see what you're saying -- I misunderstood your original point. Also, messing around with this more, I actually came up with an example that was confusing to me... void main() {
num myNum = 5.5;
// this switch errors
switch (myNum) {
case _ when myNum is int: // case int() but more complicated
print("is int");
case _ when myNum is double: // case double() but more complicated
print("is double");
}
Object myObject = 5.5;
// this switch does not error
switch (myObject) {
case _ when myObject is int: // case int() but more complicated
print("is int");
case _ when myObject is double: // case double() but more complicated
print("is double");
}
}
The top |
I can explain that. The type When you cast the Neither switch is exhaustive, cases with a A switch on |
Weird... I guess I just haven't used switch statements enough, but that totally makes sense. Didn't realize Thanks @lrhn for explaining that! |
@lrhn Possible to check with those who implemented it or get some comment here before closing? I feel like my question was not actually answered here. |
That would be a language question then, because the current behavior is matching the specification. |
I'll open this in the language repo then, thanks |
The compiler complains about
switchStatement
, reporting that the switch statement doesn't exhaustively cover the cases.The text was updated successfully, but these errors were encountered: