Skip to content

Future.then((value) {}) does not have a type. #27088

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
scheglov opened this issue Aug 16, 2016 · 20 comments
Closed

Future.then((value) {}) does not have a type. #27088

scheglov opened this issue Aug 16, 2016 · 20 comments
Assignees
Labels
legacy-area-analyzer Use area-devexp instead. P1 A high priority bug; for example, a single project is unusable or has many test failures
Milestone

Comments

@scheglov
Copy link
Contributor

scheglov commented Aug 16, 2016

@jmesserly @leafpetersen

Test case:

import 'dart:async';

main() {
  getA().then((a) => a.getItems()).then((items) => items.length);
}

Future<A> getA() async => new A();

class A {
  Future<List<int>> getItems() async => <int>[];
}

We know the type of a.getItems(), but later in (items) => items.length the type of items is not set.

image

Is it the same as #25944 ?

@scheglov scheglov added P1 A high priority bug; for example, a single project is unusable or has many test failures legacy-area-analyzer Use area-devexp instead. analyzer-strong-mode labels Aug 16, 2016
@scheglov scheglov added this to the 1.19 milestone Aug 16, 2016
@jmesserly
Copy link

jmesserly commented Aug 16, 2016

by "propagated type" did you mean the inferred static type?

@scheglov
Copy link
Contributor Author

a type :-)

@scheglov scheglov changed the title Future.then((value) {}) does not have a propagated type. Future.then((value) {}) does not have a type. Aug 16, 2016
@jmesserly
Copy link

ah gotcha. Yeah I think this was related to the fix for #25944. What happens now is the return type of the Future.then first parameter is Future<S> | S, however it is declared as dynamic in the SDK. We know to "pretend" Future<S> | S during downwards inference, but not during upwards inference. I suspect it wouldn't be all that hard to fix.

(The reason it's so tricky is we don't want our Future<S> | S type to ever end up in the Element model or in a FunctionType or staticType field. So we have to be really careful about introducing it, even though it does help out inference.)

@bwilkerson
Copy link
Member

Analyzer tried adding union types at one point as an experiment, but the confusion it caused users more than offset any value we got for it. If we're only using this for a very limited (and local) form of inference and if we're being really careful about how we're reporting errors to the user, then we might be able to avoid the problems we ran into earlier, but we need to tread very carefully.

@jmesserly
Copy link

hah, yeah that was exactly our fear. So far we've made sure these types aren't user-visible, we always pick one or the other before we'd report anything about them (impl details: it only exists inside InferenceContext).

@jmesserly
Copy link

fwiw, I think I can fix this bug without any user-visible side effects.

@scheglov
Copy link
Contributor Author

scheglov commented Aug 16, 2016

Another test case related to Future.then().

import 'dart:async';

main() {
  var fff = foo().then((_) => 2.3);
}

Future<int> foo() async => 1;

I'd expect that the static type of fff is Future<double>, but actually it's Future<dynamic>.

@jmesserly
Copy link

strange. I would expect at least Future<dynamic>.

@scheglov
Copy link
Contributor Author

scheglov commented Aug 16, 2016

I've updated my comment.

You're right, it's Future<dynamic>.
But it still does not seem right.

@jmesserly
Copy link

Definitely not right :)

We used to say the signature of Future<T>.then<S> was <S>(T -> S) -> Future<S> which is how these were working. But that broke a lot of things that returned Future<S> from the body. So now the type signature is <S>(T -> dynamic) -> Future<S> and we'll have to special case that dynamic during upwards inference for generic methods (somewhere inside StaticTypeAnalyzer._inferGenericInvoke or when it calls inferGenericFunctionCall)

@jmesserly jmesserly self-assigned this Aug 16, 2016
@kevmoo
Copy link
Member

kevmoo commented Aug 17, 2016

@jmesserly @scheglov Do we consider this a regression? Or a crazy gnarly issue?

Just want to be super clear if it's a 1.19 blocker.

CC @mit-mit

@scheglov
Copy link
Contributor Author

It breaks rolling 1.19 internally.
AFAIK @keertip sees issues with existing code.

@jmesserly
Copy link

yeah it's a regression

@jmesserly
Copy link

I'm almost ready to send a CL out

@jmesserly
Copy link

Oh wow, I just discovered why this regression happened ... we had a test for it, but there's more than one mock SDK for Analyzer tests. I'd updated one of them without discovering the other, so the test didn't catch the change in Future.then's signature =(

@bwilkerson
Copy link
Member

There are three classes named MockSdk (spread across three of our projects; primarily because it's hard to share test support code) and AnalysisContextFactory creates yet another mock SDK. Which one was updated?

@jmesserly
Copy link

AnalysisContextFactory was the problematic one. Here's the fix: https://codereview.chromium.org/2253923002/

I don't generally work outside package:analyzer so if the other ones are outside of it, I didn't see them. Suggestion: move mock_sdk under lib/src so other pkgs can use it.

@bwilkerson
Copy link
Member

Yep. I think that's the pattern we agreed on, it's just not been high enough priority to actually get done yet. So far we've been putting this kind of code in analyzer/lib/src/generated/testing (though it really ought to get moved out of generated).

@kevmoo
Copy link
Member

kevmoo commented Aug 17, 2016

There are three classes named MockSdk (spread across three of our projects; primarily because it's hard to share test support code) and AnalysisContextFactory creates yet another mock SDK.

@bwilkerson since these are all in the SDK repo, could we just create a pkg/_analyzer_test_shared pseudo-package for all of this logic?

@bwilkerson
Copy link
Member

We could, and we discussed that possibility, but we decided it was better for the test support to be carried along with the analyzer package (less chance of it getting out of sync, and better access to internal code).

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. P1 A high priority bug; for example, a single project is unusable or has many test failures
Projects
None yet
Development

No branches or pull requests

4 participants