-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Flow analysis doesn't work after Null check operator #42021
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
I'm not sure we recognize "definitely false" checks as causing dead code in the specified definite-assignment algorithm. The analyzer might be doing more than required here when it recognizes the value as always false (that's why it's only a hint), and in that case, it can't change the language semantics. |
I think then this goes back to the flow analysis spec. @stereotype441 |
Yeah, this behavior is surprising, but believe it or not it is working as intended. Here's the subtle reason why we need flow analysis to behave this way. When a mixed version program is run with unsound null safety, it's possible for a variable whose type is (non-nullable) // foo.dart
// @dart = 2.9
import 'bar.dart';
main() {
// It's valid to pass `null` to `bar` because null safety is disabled in this file.
bar(null);
} // bar.dart
// @dart = 2.12
bar(String s) {
if (s == null) {
// It might seem like this code should be unreachable, because this file has
// null safety enabled, and `s` has a non-nullable type. But actually it's
// reachable because of the non-null-safe code in `foo.dart`.
}
} We want flow analysis to behave the same regardless of whether the program is being compiled with sound or unsound null safety (because we don't want people to start getting compile errors if they switch modes), so that means that flow analysis has to treat the "if" block as reachable even for fully sound programs. Similarly, we want flow analysis to be based on the types of expressions; we don't want it to perform extra reasoning steps that go beyond the type system. So for consistency, we need it to treat any So, returning to the original example: main () {
late int i;
String? s = "";
if (s! == null) {
i = 42;
}
i;
} Even though we can see from human reasoning that You can find more information at dart-lang/language#1143 about why we decided to make things work this way. Hope that helps! |
@diego-lipinski-de-castro The short answer is that I think you want this: Delivery? driverDelivery = driverDeliveries.firstWhereOrNull((d) => d.id == deliveryId); Note that import 'package:collection/collection.dart' show IterableExtension; And you may have to upgrade your pubspec to ensure that you get a version of The longer answer is that Before null safety, if you wanted the behavior "if nothing can be found, return Delivery? driverDelivery = driverDeliveries.firstWhere((d) => d.id == deliveryId, orElse: () => null); (Note the Unfortunately, this doesn't work with null safety, because the return type of the |
Thanks, I got what I was doing wrong due to your return, is this documented somewhere? Especially the firstWhereOrNull part, I dont think I would find this elsewhere |
Good point. It should be better documented. I filed #45680 to track this issue. |
The following test fails
There must be a compile time error but in fact analyzer produces hints only
CFE also produces no error but
LateInitializationError
at a runtime onlyIf to modify the test and replace
s! == null
byfalse
the test starts to work as expected (produces a compile time error)Dart VM version: 2.9.0-10.0.dev (dev) (Tue May 19 15:16:48 2020 +0200) on "windows_x64"
The text was updated successfully, but these errors were encountered: