Skip to content

Final variable type promotion is lost inside a closure if followed by try/catch #46532

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

Closed
renatoathaydes opened this issue Jun 30, 2021 · 4 comments
Labels
closed-duplicate Closed in favor of an existing report

Comments

@renatoathaydes
Copy link

  • Dart SDK Version (dart --version)
Dart SDK version: 2.13.4 (stable) (Wed Jun 23 13:08:41 2021 +0200) on "macos_x64"

Description

The following code attempts to use type promotion of a final variable, which should be guaranteed to work in this case because the variable in question is final.

mixin A {
}

class Foo with A {
  final foo = 'foo';
}

class Bar with A {
  final bar = 'bar';
}

void main() {
  final A a;
  try {
    a = Foo();
  } catch (e) {
    print('ERROR: $e');
    return;
  }
  if (a is Foo) {
    print(['foo', 'bar'].firstWhere((i) => i == a.foo)); // fails to compile
  } else if (a is Bar) {
    print(['foo', 'bar'].firstWhere((i) => i == a.bar)); // fails to compile
  }
}

The two lines where it says // fails to compile fail because of the errors (from DartPad):

Error: The getter 'foo' isn't defined for the class 'A'.
 - 'A' is from 'package:dartpad_sample/main.dart' ('lib/main.dart').
    print(['foo', 'bar'].firstWhere((i) => i == a.foo));
                                                  ^^^
lib/main.dart:23:51:
Error: The getter 'bar' isn't defined for the class 'A'.
 - 'A' is from 'package:dartpad_sample/main.dart' ('lib/main.dart').
    print(['foo', 'bar'].firstWhere((i) => i == a.bar));
                                                  ^^^
Error: Compilation failed.

I am almost certain this is a bug because changing the code that assigns a from:

  final A a;
  try {
    a = Foo();
  } on FormatException catch (e) {
    print('ERROR: $e');
    return;
  }

to:

final A a = Foo();

... causes the problem to go away. But at the point where the error occurs, it shouldn't matter which of the two above was used, as a is guaranteed to be assigned and final after this.

This issue might be related to issue 32285?

@renatoathaydes
Copy link
Author

Also, notice that this compiles fine:

    print(a.foo);
    //print(['foo', 'bar'].firstWhere((i) => i == a.foo));

So type promotion seems to work when outside the closure only.

@eernstg
Copy link
Member

eernstg commented Jul 1, 2021

The following reduced example will actually suffice to obtain the error:

void main() {
  final num a;
  a = 1;
  if (a is int) (i) => i == a.isEven; // fails to compile
}

whereas the following compiles without errors:

void main() {
  final num a = 1;
  if (a is int) (i) => i == a.isEven; // fails to compile
}

The reason why there is no promotion of a in the function literal body is that there is an assignment to a (this happens for any assignment to a, no matter where it is located). Some more info can be found in dart-lang/language#1247.

Note that an initializing expression does not count as an assignment, which is the reason why we can use final num a = 1; without cancelling the promotion.

I think we can close this issue as a duplicate, because the discussion about this particular part of the flow analysis is already the topic of that issue.

@eernstg eernstg closed this as completed Jul 1, 2021
@eernstg eernstg added the closed-duplicate Closed in favor of an existing report label Jul 1, 2021
@renatoathaydes
Copy link
Author

The treatment of a final local variable for purpose of flow analysis should not depend on whether it's assigned immediately on declaration, or separately. There's no good reason to do it that way. As long as the variable has been definitely assigned (which the analyzer can easily recognize), the two cases are identical?!

I agree that in the case where the variable is not final, the current behaviour is appropriate. But for final local variables, I believe it's simply a mistake to not promote the type.

@eernstg
Copy link
Member

eernstg commented Jul 1, 2021

I think it's better to make that argument in dart-lang/language#1247!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
closed-duplicate Closed in favor of an existing report
Projects
None yet
Development

No branches or pull requests

2 participants