Skip to content

Callable class has incorrect return type #33194

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
DanTup opened this issue May 22, 2018 · 10 comments
Closed

Callable class has incorrect return type #33194

DanTup opened this issue May 22, 2018 · 10 comments
Assignees
Labels
legacy-area-analyzer Use area-devexp instead. type-bug Incorrect behavior (everything from a crash to more subtle misbehavior)
Milestone

Comments

@DanTup
Copy link
Collaborator

DanTup commented May 22, 2018

Given this code:

class Test {
  T get<T>() {
    return null;
  }

  T call<T>() {
    return get<T>();
  }
}

main() {
  var test = new Test();
  var a = test.get<String>();
  var b = test<String>();
  var c = test.call<String>();
}

a and c are typed as String, but b is typed as T (even though no such class exists, and there are no warnings in this code). If I remove the T as the return type from the call method, then the type becomes dynamic.

I presume this should work, but I couldn't find any info on it. I found a couple of open issues about callable classes, but I don't think any are describing this.

Tested with the latest analyzer that's in Flutter master.

@escamoteur
Copy link

This came up when implementing this https://github.com/escamoteur/get_it

@lrhn lrhn added legacy-area-analyzer Use area-devexp instead. type-bug Incorrect behavior (everything from a crash to more subtle misbehavior) labels May 22, 2018
@eernstg
Copy link
Member

eernstg commented May 23, 2018

Here's some info on the mechanism itself:

We are working on a specification update to include call method extraction, which is the transformation that takes e of a static type which is a class with a call method and transforms it into e.call, in contexts where the expected type is Function or a function type (e.g., void Function(int)). We may end up reorganizing that update some more, but you can take a look here to get the general idea: https://dart-review.googlesource.com/c/sdk/+/51323.

In your examples, we're relying on Dart 2 inference to give test the static type Test. That's a type which satisfies the criterion (it has a call method), aka "test is a callable object", and in general any expression of static type Test is potentially subject to call method extraction.

In the first case, var a = test.get<String>(), there are no special mechanisms involved and a will be the null that Test.get returns.

In var b = test<String>() we have a callable object (test), and it is used in a context where a function is expected (it is invoked, as indicated by the presence of <String>()), so it will be transformed into var b = test.call<String>().

In var c = test.call<String>() we have an explicit reference to call; there is nothing special about call in this context, it's just a method that gets invoked.

About the incorrect return type issue: I'm not quite sure how you detect the type of b (hover in IntelliJ?), but you're right that it certainly should not be a free type variable. ;-)

b should be treated exactly like c (because call method extraction should make the initializing expressions identical) and b should then also get the inferred type annotation String. So that part is evidence of a bug in the relevant version of the analyzer.

@DanTup
Copy link
Collaborator Author

DanTup commented May 23, 2018

@eernstg Sorry if it seemed like I meant at runtime; it was purely the analysis we had issues with (the tooltips from the analyzer and the lack of code completion). I believe at runtime it's all behaving fine (@escamoteur can confirm though).

@escamoteur
Copy link

Yes, compiling and runtime is working

@eernstg
Copy link
Member

eernstg commented May 24, 2018

@DanTup, sorry about being so long-winded. ;-)

For the run-time behavior it's worth noting that Dart 1 callable objects were full-fledged functions, but with the Dart 2 approach it is a matter of static analysis that some tear-offs of call are performed implicitly, and the "callable objects" can not be passed around under the type Function or any function type.

@leafpetersen leafpetersen added this to the Dart2Stable milestone Jul 10, 2018
@dgrove
Copy link
Contributor

dgrove commented Jul 12, 2018

tentatlvely assigning to @MichaelRFairhurst

@MichaelRFairhurst
Copy link
Contributor

Quick update on the problem here.

It looks like the tooltip shows the free variable type T, and autocompletion indeed does not work.

var b = test<String>(); // hover shows b is T
var b = test<String>(). // autocompletion does not work

However, the program itself does not analyze as if b is T, but rather Object.

b.x; // error: no such method x on Object.
test<String>().x; // error: no such method x on Object
test<String>() as Object; // error: unnecessary cast from Object to Object

So this is an improvement, but still wrong.

Looking into it, but I won't be prioritizing fixing hover or autocomplete. I'll be prioritizing it choosing Object instead of String. I hope that they are the same issue but can't say for sure.

@MichaelRFairhurst
Copy link
Contributor

@escamoteur
Copy link

So it works now?

@DanTup
Copy link
Collaborator Author

DanTup commented Jul 16, 2018

The test suggests so, but you'll need a new Dart for it (for standalone Dart SDKs that probably means the next dev release, but for Flutter it might take a little longer to be rolled in).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
legacy-area-analyzer Use area-devexp instead. type-bug Incorrect behavior (everything from a crash to more subtle misbehavior)
Projects
None yet
Development

No branches or pull requests

7 participants