-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Object patterns with extension methods in switch showing as inexhaustive in analyzer but program compiles and runs successfully #51854
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
Comments
This specific pattern is actually built right in to pattern matching - it's even called a So you could write your example like: void main() {
print(sum([1, 2, 3, 4, 5]));
}
int sum(List<int> list) => switch (list) {
[] => 0,
[var x, ...var xs] => x + sum(xs),
}; (which, can I just say, looks so clean. Hats off to the Dart team 🎉) I tried to come up with a good way to explain why your original example isn't exhaustive, but the mental model that works for me is that exhaustiveness checking is based on a type's "cardinality" - how many possible values can inhabit a type. A set of patterns is exhaustive if it covers all the possible values. For most primitive types - numbers, strings, lists, maps - the cardinality is effectively infinite. So to exhaustively check some property of one of these types, you want to try expressing your patterns in terms of types with lower cardinality (like For example: // not exhaustive, can't reason about the relationship between `isEmpty` and `isNotEmpty`
final x = switch([1,2,3]) {
List(isEmpty: true) => 'a',
List(isNotEmpty: true) => 'b',
};
// exhaustive, covered the only 2 possible values for a `bool`
final x = switch([1,2,3]) {
List(isEmpty: true) => 'a',
List(isEmpty: false) => 'b',
}; Some good related reading: dart-lang/language#2693 |
I am aware that list patterns support The code I posted is considered exhaustive by the compiler, but not by the analyzer so clearly there is a discrepancy here. I believe that the compiler is correct and that there should be no analyzer warning.
It is exhaustive because an Additionally, as I mentioned in the original post, if you use the pattern |
Right, I missed the lack of constraints on |
The switch should be exhaustive, as you say the, (Also, be aware that code written like this is very likely to have atrocious performance. sum(Iterable<int> it) => switch (it) {
Iterable(isEmpty: true) => 0,
Iterable(first: var x, rest: xs) => x + sum(xs)
};` on an iterable of length n would build up a stack of n |
@johnniwinther I'm guessing that the exhaustiveness checker is working correctly and that the bug is in the way the analyzer interacts with the checker. Can you confirm that, and can you point me to where the interaction occurs? |
I think this is a problem in both the CFE and the analyzer. Currently the exhaustiveness only recognizes the types of instance properties and just has a fallback for unknown properties (wrongfully) assuming that these are error cases. For these it uses the top type |
@bwilkerson do you know if available extensions methods are easily available in the analyzer (from the |
Sorry I missed your comment earlier. I believe that we can fairly easily look through the import scope for visible extensions that match the type. @scheglov In case I'm wrong. |
@scheglov Could you give me a pointer of how to do this in the analyzer? Then I'll implement this in the shared exhaustiveness code. |
See |
If a pattern field in an object pattern resolves to an extension method, is that information available on the pattern field object (the |
Yes, I suspect there is a bug in the integration of the exhaustiveness checking with the analyzer. But if we want to know every property available, it seems to be similar to code completion. Yes, the resolved property is available in |
We only need to check the individual properties mentioned but we have to get their declared static types to do the matching. This what goes wrong here; the I hope I'll be able to derive the needed type from the |
Btw this isn't a bug in the integration with the analyzer but a missing piece of the exhaustiveness checking - it's also missing in the CFE. I forgot about the existence of extension method when adding support for member access. |
Yeah, I think I mentioned once in the review that we need to support extension methods, but forgot about it later. |
Dart SDK version: 3.0.0-366.0.dev (dev) (Fri Mar 24 06:44:03 2023 -0700) on "windows_x64"
Consider this program.
example.dart
analysis_options.yaml
The above program compiles and runs successfully using
However the analyzer is reporting the following error.
The error message goes away if you declare
rest: Object? xs
, orrest: dynamic xs
, instead ofrest: var xs
, so maybe type inference has something to do with it? Possibly related to #51437.The text was updated successfully, but these errors were encountered: