Skip to content
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

Variable not promoted to Null after null-check #48504

Closed
julemand101 opened this issue Mar 3, 2022 · 3 comments
Closed

Variable not promoted to Null after null-check #48504

julemand101 opened this issue Mar 3, 2022 · 3 comments
Labels
area-meta Cross-cutting, high-level issues (for tracking many other implementation issues, ...). type-bug Incorrect behavior (everything from a crash to more subtle misbehavior)

Comments

@julemand101
Copy link
Contributor

Tested against the following Dart versions:

Dart SDK version: 2.16.1 (stable) (Tue Feb 8 12:02:33 2022 +0100) on "windows_x64"
Dart SDK version: 2.17.0-150.0.dev (dev) (Fri Feb 25 22:29:59 2022 -0800) on "windows_x64"

The following program shows some "interesting" behavior when it comes to promote a variable to Null when doing a null-check:

extension NullExtension on Null {
  void someMethod() {
    print("Called Null.someMethod");
  }
}

String? getString() => null;

void main() {
  final string = getString();

  if (string == null) {
    //(string as Null).someMethod();
    string.someMethod();
  }
}

This fails by the analyzer with:

The method 'someMethod' can't be unconditionally invoked because the receiver can be 'null'. (Documentation)
Try making the call conditional (using '?.') or adding a null check to the target ('!').

And when running the program:

bin/test.dart:14:12: Error: The method 'someMethod' isn't defined for the class 'String?'.
Try correcting the name to the name of an existing method, or defining a method named 'someMethod'.
    string.someMethod();
           ^^^^^^^^^^

If I uncomment (string as Null).someMethod(); as:

extension NullExtension on Null {
  void someMethod() {
    print("Called Null.someMethod");
  }
}

String? getString() => null;

void main() {
  final string = getString();

  if (string == null) {
    (string as Null).someMethod();
    string.someMethod();
  }
}

The program does no longer have any warning or error from the analyzer and runs with the following result:

Called Null.someMethod
Called Null.someMethod

This behavior seems kinda weird since the promotion to Null seems to happen based on a local type-cast that should not affect the rest of the program.

This might also explain why auto-completion is not that smart (tested with IntelliJ) when it comes to showing what methods can be called on the value. If I use auto-complete before (string as Null).someMethod(); I will get the full list of methods as if the string variable was a String:
image

While if I do the same after (string as Null).someMethod(); I will get the possible methods for Null:
image

@eernstg eernstg added area-meta Cross-cutting, high-level issues (for tracking many other implementation issues, ...). type-bug Incorrect behavior (everything from a crash to more subtle misbehavior) labels Mar 3, 2022
@eernstg
Copy link
Member

eernstg commented Mar 3, 2022

We do have the following in the null safety spec:

A check of the form e == null or of the form e is Null where e has static
type T promotes the type of e to Null in the true continuation, and to
NonNull(T) in the
false continuation.

I think we've discussed not performing this particular promotion (because of the rather low value of having an expression of type Null), but I can't find any evidence that we shouldn't promote.

@scheglov, @johnniwinther, WDYT?

@johnniwinther
Copy link
Member

I can recall some late changes to how we handle null in the promotion (something to do with unsound mode) but can't recall the details. @stereotype441 might remember more since he did most of the implementation.

@stereotype441
Copy link
Member

I don't recall whether there was a deliberate decision at the time we implemented it, but sometime later, we did consider adding this functionality, in response to dart-lang/language#1505. I did some analysis back in November, and IMHO, enough code would be broken by the change that it's not worth it (you can see my analysis here: dart-lang/language#1505 (comment)). But the question hasn't risen to a high enough priority for the language team to discuss it and make a firm yes/no decision, so the issue remains open.

(@johnniwinther I think you may have been thinking of dart-lang/language#1143, which is a different issue).

I'm going to close this issue on the grounds that it's a duplicate of dart-lang/language#1505. If folks want to discuss it further, let's have the discussion there so that all the information is in one place.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-meta Cross-cutting, high-level issues (for tracking many other implementation issues, ...). type-bug Incorrect behavior (everything from a crash to more subtle misbehavior)
Projects
None yet
Development

No branches or pull requests

4 participants