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

Null safety flow analysis on nullable object fields #1722

Open
KristianBalaj opened this issue Jul 3, 2021 · 3 comments
Open

Null safety flow analysis on nullable object fields #1722

KristianBalaj opened this issue Jul 3, 2021 · 3 comments
Labels
field-promotion Issues related to addressing the lack of field promotion

Comments

@KristianBalaj
Copy link

I'm interested in the flow analysis of nullable object fields. The problem is that it is not actually working so I'm wondering if this is an issue or a design decision.

Flutter + Dart version:

[✓] Flutter (Channel stable, 2.2.0, on macOS 11.4 20F71 darwin-x64, locale en-GB)
• Flutter version 2.2.0 at /Users/kiko.balaj/Documents/flutter
• Framework revision b22742018b (7 weeks ago), 2021-05-14 19:12:57 -0700
• Engine revision a9d88a4d18
• Dart version 2.13.0

Example

class Foo {
  final String? a;

  Foo(this.a);
}

void bar(Foo myObj) {
  if (myObj.a != null) {
    print(myObj.a?.length);
  }
}

The issue is in the bar method. In my opinion, in the print argument there doesn't have to be the questionmark operator after the myObj.a because I've already checked myObj.a != null.

Thanks for the response!

@lrhn
Copy link
Member

lrhn commented Jul 3, 2021

It's a design decision. Dart can only promote local variables.

The a getter of a Foo object can theoretically return anything the next time you read it, so it can't be promoted based on checking that the value you read in one place is not null.
Even if this program doesn't have a getter which changes value, promoting based on that knowledge would prevent the class author from changing the final field to a getter in the future. So, promotion is only for local variables.

You can copy the value into a local variable, then it can be promoted:

  var a = myObj.a;
  if (a != null) print(a.length);
}

@lrhn lrhn transferred this issue from dart-lang/sdk Jul 3, 2021
@lrhn lrhn added the field-promotion Issues related to addressing the lack of field promotion label Jul 3, 2021
@caioaao
Copy link

caioaao commented Sep 6, 2021

What about when bar is a method from Foo. I see the same behavior with this snippet:

class Foo {
  final String? a;

  Foo(this.a);

  void bar() {
    if (a != null) {
      print(a?.length);
    }
  }
}

it's not a local variable but it's close enough, don't you think?

@Levi-Lesches
Copy link

Levi-Lesches commented Sep 6, 2021

The problem is that a isn't a value, it's a function that returns a value -- aka, a getter.

Your code is equivalent to this:

class Foo {
  final String? _a;
  Foo(this.a);

  /// No setter means that [a] is `final`.
  String? get a => _a;
  
  void bar() {
    if (a != null) print(a?.length);
  }
}

Which is simple enough. But because getters are just regular functions with no parameters, it can also have arbitrary logic. Consider:

import "dart:math";

class Foo {
  final String? _a;
  Foo(this.a);

  /// Sometimes returns [null], sometimes returns [_a].
  ///
  /// In a real program, this wouldn't be random, but instead depend on some state.
  String? get a => Random().nextBool() ? _a : null;

  /// A more realistic example of a getter that returns different values.
  String? get username => MyAuth.isSignedIn ? MyAuth.user.id : null;

  void bar() {
    if (a != null) {  // Just because a isn't null here...
      print(a.length);  // ...doesn't mean it isn't null here!
    }
  }
}

There are a few workarounds being discussed:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
field-promotion Issues related to addressing the lack of field promotion
Projects
None yet
Development

No branches or pull requests

4 participants