-
Notifications
You must be signed in to change notification settings - Fork 213
Change flow analysis to assume sound null safety once unsound null safety support is fully removed #3100
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 actually worried that we assume some syntactically allowed control flow cannot happen because of type analysis. If code contains a branch, I think we should assume that all branches can be taken, if at all possible. The problem with that is that an unreachable branch is dead code. Dead code is likely a mistake, or at least a temporary thing. At some point that code will either be deleted, or the branch will be enabled. If type analysis and definite assignment has always considered the branch to be executable, then twiddling with the conditions won't suddenly make your code have type errors. The one issue I see with such branches, is that it's not always clear which types promotable variables have on entry, since the reason the branch is unreachable might be a type test that rules out everything. Say (probably not a correct example, but maybe there are valid examples): // ignore_for_file: prefer_void_to_null
import "dart:async";
void main() {
FutureOr<Null> x = null as dynamic;
int y;
int? z = 0;
if (x is Future<Null>) {
// x promoted to `Future<Nul>`
y = 0;
} else {
// x promoted to `Null`
if (x != null) {
// x promoted to `Never` ? (Actually not, it's still `Null`)
z = null;
} else {
y = 2;
}
}
print(y);
z.toRadixString(10); // Allowed because `z=null` doesn't demote.
} Here We do analyze the "unreacable" branch (can be seen by introducing type errors there), we just don't merge it back into the result afterwards That's "very clever" from an analysis which cannot determine that Which brings me back to the argument that unreachable code should probably not be considered to be deliberately unreachable, because nobody needs unreachable code in their final production code. If the code is still there, we should, IMO, assume that it's intended to be reachable, and analyze the program as if it was. It should even be easier, just assume that all branches in the code, and all merges after them, are (intended to be) possible. |
Now that dart-lang/sdk@0060b0f has removed support for running in unsound null safety mode, I'm going to try prototyping this to see how breaking of a change it would be. Once I have that information, I'll report my findings and we can decide whether we want to go ahead with this change or not. |
In fully sound null safety mode, flow analysis should know that a test like `expr == null` is guaranteed to evaluate to `false` if the static type of `expr` is non-nullable. But in unsound null safety mode, no such guarantee can be made. Since support for unsound null safety was only recently removed from the CFE (see dart-lang/sdk@0060b0f), flow analysis still conservatively assumes that an expression with a non-nullable static type might, nonetheless, still be `null`. This change updates the spec to match the implementation in this regard, and adds a reference to dart-lang#3100, where we are discussing the possibility of changing the behavior.
In fully sound null safety mode, flow analysis should know that a test like `expr == null` is guaranteed to evaluate to `false` if the static type of `expr` is non-nullable. But in unsound null safety mode, no such guarantee can be made. Since support for unsound null safety was only recently removed from the CFE (see dart-lang/sdk@0060b0f), flow analysis still conservatively assumes that an expression with a non-nullable static type might, nonetheless, still be `null`. This change updates the spec to match the implementation in this regard, and adds a reference to #3100, where we are discussing the possibility of changing the behavior.
At the meeting Wednesday Mar 5 2025 the language team decided to proceed with this proposal. |
All work on dart-lang/language#3100 (Change flow analysis to assume sound null safety once unsound null safety support is fully removed) will be guarded by this flag. Bug: dart-lang/language#3100 Change-Id: I0a6e7b732520ed70944141b4bb02832b7d0d969b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/417906 Reviewed-by: Kevin Moore <[email protected]> Reviewed-by: Konstantin Shcheglov <[email protected]> Commit-Queue: Paul Berry <[email protected]>
@stereotype441, I created dart-lang/sdk#60438 in order to make it possible to see the existence and status of the ongoing implementation effort as usual. However, the old approach (one sub-issue for the analyzer and another one for the CFE, and at times lots of other issues for other things) doesn't fit the reality any more. Seems like it would suffice to just have dart-lang/sdk#60438? |
Thanks, I'll attach all of my CLs to dart-lang/sdk#60438. I think a single implementation issue is sufficient. |
Currently, flow analysis conservatively assumes that a variable with a non-nullable type might still be
null
because the program might be running with unsound null safety, and we don't want null-related unsoundness to be able to be escalated into full unsoundness (see #1143 for details).Although we don't officially support running with unsound null safety anymore, support has not yet been fully removed. Once we remove it, we should remove this conservative assumption from flow analysis.
Note that this is a significant enough change that it probably should be tied to a language version.
The text was updated successfully, but these errors were encountered: