Assignment expressions in `if`-statements don't correctly promote nullable variables
This program errors:
void main() {
String? getString() => "asdf";
String? someString;
if ((someString = getString()) != null) {
// errors:
// "The property 'isNotEmpty' can't be unconditionally accessed because the receiver can be 'null'."
print(someString.isNotEmpty);
}
}
This one does not:
void main() {
String? getString() => "asdf";
String? someString;
someString = getString();
if (someString != null) {
// does not error
print(someString.isNotEmpty);
}
}
I feel like the first program shouldn't error... Doesn't it guarantee that someString is not nullable just as much as the second program does? I might be wrong here -- I don't usually use assignment syntax in if-statements like this. (If you're curious, I'm using this syntax to store the results of a chain of nullable RegExp matches, which looks neater than having several assignments put between unchained if-statements.)
Infos
- Dart 3.3.1 (stable) (Wed Mar 6 13:09:19 2024 +0000) on "macos_arm64"
- on macos / Version 14.0 (Build 23A344)
- locale is en-US
There is a request for generalisation of the flow analysis in https://github.com/dart-lang/language/issues/1224, in order to get a larger set of reasons for promoting a local variable. In that issue the topic is that if (foo?.something == somethingNotNull) ... could promote foo to have a non-nullable type in the true continuation (because the condition would necessarily be false when foo == null).
This issue is similar in that it is a request for detecting that x can't be null in the true continuation of (x = e) != null.
I'll move this issue to the language repository and label it in a way which is similar to the existing issue.
@stereotype441, @johnniwinther, it seems that we may have a group of somewhat similar requests in this area (at least, #1224 seems related). Would you prefer to make it a single issue covering all the variants? What's the best labeling?
A single meta-issue with links to individual issues for concrete enhancement ideas is probably better than combining different ideas into one issue, in case we choose to do some, but not all, of them.
Maybe we'd just create a new label (e.g., enhanced-promotion) and then use that for proposals about new ways to establish that a given promotion is justified.
This came up in some internal code recently. Here's a stripped-down version of what the code looked like (with the identifier names and types changed to protect IP):
void main() {
int? x;
[
for (var y in [1, 2, null, 4, 5])
if ((x = y) != null) x!.isEven, // It would be nice if the `!` in `x!` were unnecessary
];
}