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

Unexpected nullable error for null-checked object. #40077

Closed
Cat-sushi opened this issue Jan 13, 2020 · 7 comments
Closed

Unexpected nullable error for null-checked object. #40077

Cat-sushi opened this issue Jan 13, 2020 · 7 comments

Comments

@Cat-sushi
Copy link

The following code cause unexpected error.

class Person {
  String firstName;
  DateTime? birthday;
  Person(this.firstName, {this.birthday});
  void describe() {
    print(firstName);
    if (birthday != null) {
      int birthyear = birthday.year;
      print('Born ${DateTime.now().year - birthyear} years ago'); // The expression is nullable and must be null-checked before it can be used
    }
  }
}

Based on Dart SDK 2.7.0-edge.134e0e28cda1f8110a69bad34ae70e8123475a80
https://nullsafety.dartpad.dev/d17785edcd0c3cbcedd381890a4ed1e3

@Cat-sushi
Copy link
Author

Smart cast works for parameters, so it seems specific to fields.

@lrhn
Copy link
Member

lrhn commented Jan 13, 2020

You can only promote local variables, not instance variables or static variables. So, try adding:

  var birthday = this.birthday;

before the if and see if it helps.

@Cat-sushi
Copy link
Author

Yes, that is the solution which the release note shows.
I understand instance variables are not subject to smart promotion, but why?
Is there a plan to widen the target to instance variables?

@Cat-sushi
Copy link
Author

The spec can't read such a restriction, but I haven't understood "normal promotion".
https://github.com/dart-lang/language/blob/master/accepted/future-releases/nnbd/feature-specification.md#null-promotion

@lrhn
Copy link
Member

lrhn commented Jan 13, 2020

The reason to not promote instance variables or static variables, or any variable where we cannot convince ourselves that there is no assignment to it between the test and the use is ... well, that we can't be sure that the value doesn't change. So if you do:

if (foo is Bar) {
  flop();
  foo.barMethod();
}

then we cannot be sure that the foo that you test to be a Bar is the same value that you try to call barMethod on. So, the promotion is not safe. We only support safe promotions.

It's even possible to make local variable promotion unsafe.
Example:

void bar() {
   num x;
   void update(num z) { x = z; };
   if (x is int) {
     foo(update);
     print(x.toRadixString(16));
   }
}

Here we cannot convince ourselves that the local variable x does not change between the x is int test and the x.toRadixString(16) call, so we cannot allow the call.

So, promotion only works when we know (based on our compiler's somewhat simplistic analyses) that the value we check is the same value that we later use at the checked type. We are working on improvements for the compiler as part of the non-nullable types feature, so it's not as completely inane as the current promotion, but it still won't be smart enough to figure out whether you assign to a non-local variable or not.

@Cat-sushi
Copy link
Author

I understand.

@Cat-sushi
Copy link
Author

another discussion
dart-lang/language#1188

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants