Skip to content

breaking change: bug fix makes no class implement Function #41362

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
sigmundch opened this issue Apr 7, 2020 · 3 comments
Closed

breaking change: bug fix makes no class implement Function #41362

sigmundch opened this issue Apr 7, 2020 · 3 comments
Labels
breaking-change-request This tracks requests for feedback on breaking changes legacy-area-analyzer Use area-devexp instead. legacy-area-front-end Legacy: Use area-dart-model instead.
Milestone

Comments

@sigmundch
Copy link
Member

Summary

Dart 2.0.0 made the clauses implements Function, extends Function, or with Function have no effect (spec section 19.6). There was a bug in how this was implemented under the common front-end. We have fixed the issue, but this may be visible on some rare scenarios and may require fixes on your application.

Details

Since Dart 2.0.0, the language specification indicates that instance types are not a subtype of Function. However, Dart still allows the function call syntax to be used to invoke the call method on object instances of classes that have such a method. Since those instances do not implement the Function type, our tools have special support for it by tearing off the call method and using the regular function invocation on it.

Recently we found a bug showing that our tools didn't completely ignore some implements Function and similar clauses in the code. We have fixed this, but as a result there were new cases where our tools will tear-off the call method. In some rare cases, that can cause a breaking change.

What can break?

Inspecting the runtimeType of variables typed as Function may change.

For example, consider this program:

class MyClass1 implements Function {
  void call() {}
}
class MyClass2 {
  void call() {}
}

main() {
  Function f1 = MyClass1();
  print(f1.runtimeType);
  Function f2 = MyClass2();
  print(f2.runtimeType);

}

Before the fix this program printed MyClass1 and () => void, after the fix it will print () => void twice.

Tear-off the call method on JS-interop types (web only).

JS-interop classes currently do not support tear-off methods, and as a result, using a js-interop type where Function was expected will not work.

We know of one such rare scenario: when modeling JavaScript constructor functions via JS-interop, some APIs in dart:js_util incorrectly expected those instances to have the Function interface. Those APIs have been fixed, but similar patterns on user code could run into the same problem.

Possibly compile-time warnings. At this time we are also considering adding to our tools a warning to alert when programs contain clauses such as implements Function. If a tool chain treats warnings as errors, this too could potentially be a breaking change.

Mitigation

The main action to take is to remove theimplements Function, extends Function, and mixin Function clauses, and then iterate on fixing downstream issues specific to your application, if there are any.

@sigmundch sigmundch added the breaking-change-request This tracks requests for feedback on breaking changes label Apr 7, 2020
@sigmundch sigmundch added this to the D28 Release milestone Apr 7, 2020
@a-siva a-siva added legacy-area-analyzer Use area-devexp instead. legacy-area-front-end Legacy: Use area-dart-model instead. labels Apr 7, 2020
@RudyReeves
Copy link

RudyReeves commented Apr 15, 2020

I know this doesn't directly address the issue, but this issue may be a good example why the call() method should not be some specially named method, but rather an operator-overriding method (essentially overriding the parentheses operator); like how the subscript operator (operator []) can be overriden, and with that there's less concern about implementing List or Iterable since it's just a method, and declares itself to be an operator.

Keeping it named call() might make it align better with other languages but it feels inconsistent with Dart's operator overriding, and there's also Function.apply which is different than Function.call and it becomes messy.

@mit-mit
Copy link
Member

mit-mit commented May 6, 2020

@sigmundch did this land? If not, please move to D29

@sigmundch
Copy link
Member Author

It did land. It's also referred to in the 2nd breaking change under Language in https://github.com/dart-lang/sdk/blob/master/CHANGELOG.md#281---2020-05-06

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking-change-request This tracks requests for feedback on breaking changes legacy-area-analyzer Use area-devexp instead. legacy-area-front-end Legacy: Use area-dart-model instead.
Projects
None yet
Development

No branches or pull requests

4 participants