-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Description
Given:
import 'dart:async';
class Divergent<T> implements Future<Divergent<Divergent<T>>> {
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
test() async {
// flatten(Divergent<int>) = Divergent<Divergent<int>>
Divergent<Divergent<int>> x = await new Divergent<int>(); /// 09: runtime error
Future<Divergent<Divergent<int>>> f() async => new Divergent<int>(); /// 10: ok
Future<Divergent<Divergent<int>>> f() async { return new Divergent<int>(); } /// 11: ok
Future<Divergent<Divergent<int>>> x = (() async => new Divergent<int>())(); /// 12: runtime error
}
When we flatten this, we sometimes run into problems. Note that this is hard to reproduce, it seems to happen when dev_compiler tries to compile this example, but a straightforward test in Analyzer works correctly. We seem to get confused sometimes whether we're in strong mode or not when we recurse into Future, letting us short circuit the problem (not for the right reason).
I did some investigation and I think there's a problem in how the code is structured:
flattenFutures calls _searchTypeHierarchyForFutureTypeParameters.
That uses "type.superclass" and "type.interfaces"
BTW: mixins aren't looked at. This could be another, unrelated bug in normal mode, that existed before the Future< flatten(T) > change. Need to investigate.
type.superclass performs substitutions. That's good too, if you've got class Foo<T> implements Map<int, List<T>>
... you want to Map<int, List<T>>
, not Map<K, V>
But then in substitute, we try to flatten again. BAD. We're already flattening! Flatten is not supposed to recurse. It's specifically defined so recursion doesn't happen.
Easy to fix. The question is, what is the cleanest way to make "we're already flattening" known at that point in the code where we choose to flatten.