Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(212)

Side by Side Diff: pkg/analyzer/lib/src/generated/type_system.dart

Issue 2208953002: fix #25944, improve Future.then inference (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: fix names, relationship between FutureUnion and DartType Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library analyzer.src.generated.type_system; 5 library analyzer.src.generated.type_system;
6 6
7 import 'dart:collection'; 7 import 'dart:collection';
8 import 'dart:math' as math; 8 import 'dart:math' as math;
9 9
10 import 'package:analyzer/dart/ast/token.dart' show TokenType; 10 import 'package:analyzer/dart/ast/token.dart' show TokenType;
11 import 'package:analyzer/dart/element/element.dart'; 11 import 'package:analyzer/dart/element/element.dart';
12 import 'package:analyzer/dart/element/type.dart'; 12 import 'package:analyzer/dart/element/type.dart';
13 import 'package:analyzer/src/dart/element/element.dart'; 13 import 'package:analyzer/src/dart/element/element.dart';
14 import 'package:analyzer/src/dart/element/type.dart'; 14 import 'package:analyzer/src/dart/element/type.dart';
15 import 'package:analyzer/src/generated/engine.dart' 15 import 'package:analyzer/src/generated/engine.dart'
16 show AnalysisContext, AnalysisOptionsImpl; 16 show AnalysisContext, AnalysisOptionsImpl;
17 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; 17 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider;
18 import 'package:analyzer/src/generated/utilities_dart.dart'; 18 import 'package:analyzer/src/generated/utilities_dart.dart' show ParameterKind;
19 import 'package:analyzer/src/generated/utilities_general.dart'
20 show JenkinsSmiHash;
19 21
20 typedef bool _GuardedSubtypeChecker<T>(T t1, T t2, Set<Element> visited); 22 typedef bool _GuardedSubtypeChecker<T>(T t1, T t2, Set<Element> visited);
21 23
22 /** 24 /**
23 * Implementation of [TypeSystem] using the strong mode rules. 25 * Implementation of [TypeSystem] using the strong mode rules.
24 * https://github.com/dart-lang/dev_compiler/blob/master/STRONG_MODE.md 26 * https://github.com/dart-lang/dev_compiler/blob/master/STRONG_MODE.md
25 */ 27 */
26 class StrongTypeSystemImpl extends TypeSystem { 28 class StrongTypeSystemImpl extends TypeSystem {
27 /** 29 /**
28 * True if implicit casts should be allowed, otherwise false. 30 * True if implicit casts should be allowed, otherwise false.
(...skipping 246 matching lines...) Expand 10 before | Expand all | Expand 10 after
275 return fnType; 277 return fnType;
276 } 278 }
277 279
278 // Create a TypeSystem that will allow certain type parameters to be 280 // Create a TypeSystem that will allow certain type parameters to be
279 // inferred. It will optimistically assume these type parameters can be 281 // inferred. It will optimistically assume these type parameters can be
280 // subtypes (or supertypes) as necessary, and track the constraints that 282 // subtypes (or supertypes) as necessary, and track the constraints that
281 // are implied by this. 283 // are implied by this.
282 var inferringTypeSystem = 284 var inferringTypeSystem =
283 new _StrongInferenceTypeSystem(typeProvider, this, fnType.typeFormals); 285 new _StrongInferenceTypeSystem(typeProvider, this, fnType.typeFormals);
284 286
285 // Special case inference for Future.then.
286 //
287 // We don't have union types, so Future<T>.then<S> is typed to take a
288 // callback `T -> S`. However, the lambda might actually return a
289 // Future<S>. So we handle that special case here.
290 if (argumentTypes.isNotEmpty && argumentTypes[0] is FunctionType) {
291 Element element = fnType?.element;
292 bool isFutureThen = element is MethodElement &&
293 element.name == 'then' &&
294 element.enclosingElement.type.isDartAsyncFuture;
295 if (isFutureThen) {
296 // Ignore return context. We'll let the onValue function's return type
297 // drive inference.
298 returnContextType = null;
299 }
300 }
301
302 if (returnContextType != null) { 287 if (returnContextType != null) {
303 inferringTypeSystem.isSubtypeOf(fnType.returnType, returnContextType); 288 inferringTypeSystem.isSubtypeOf(fnType.returnType, returnContextType);
304 } 289 }
305 290
306 for (int i = 0; i < argumentTypes.length; i++) { 291 for (int i = 0; i < argumentTypes.length; i++) {
307 // Try to pass each argument to each parameter, recording any type 292 // Try to pass each argument to each parameter, recording any type
308 // parameter bounds that were implied by this assignment. 293 // parameter bounds that were implied by this assignment.
309 inferringTypeSystem.isSubtypeOf( 294 inferringTypeSystem.isSubtypeOf(
310 argumentTypes[i], correspondingParameterTypes[i]); 295 argumentTypes[i], correspondingParameterTypes[i]);
311 } 296 }
(...skipping 432 matching lines...) Expand 10 before | Expand all | Expand 10 after
744 bool _isSubtypeOf(DartType t1, DartType t2, Set<Element> visited, 729 bool _isSubtypeOf(DartType t1, DartType t2, Set<Element> visited,
745 {bool dynamicIsBottom: false}) { 730 {bool dynamicIsBottom: false}) {
746 // Guard recursive calls 731 // Guard recursive calls
747 _GuardedSubtypeChecker<DartType> guardedSubtype = _guard(_isSubtypeOf); 732 _GuardedSubtypeChecker<DartType> guardedSubtype = _guard(_isSubtypeOf);
748 _GuardedSubtypeChecker<DartType> guardedInferTypeParameter = 733 _GuardedSubtypeChecker<DartType> guardedInferTypeParameter =
749 _guard(_inferTypeParameterSubtypeOf); 734 _guard(_inferTypeParameterSubtypeOf);
750 if (t1 == t2) { 735 if (t1 == t2) {
751 return true; 736 return true;
752 } 737 }
753 738
754 // The types are void, dynamic, bottom, interface types, function types 739 // The types are void, dynamic, bottom, interface types, function types,
755 // and type parameters. We proceed by eliminating these different classes 740 // and type parameters. We proceed by eliminating these different classes
756 // from consideration. 741 // from consideration.
757 742
758 // Trivially true. 743 // Trivially true.
759 if (_isTop(t2, dynamicIsBottom: dynamicIsBottom) || 744 if (_isTop(t2, dynamicIsBottom: dynamicIsBottom) ||
760 _isBottom(t1, dynamicIsBottom: dynamicIsBottom)) { 745 _isBottom(t1, dynamicIsBottom: dynamicIsBottom)) {
761 return true; 746 return true;
762 } 747 }
763 748
764 // Trivially false. 749 // Trivially false.
765 if (_isTop(t1, dynamicIsBottom: dynamicIsBottom) || 750 if (_isTop(t1, dynamicIsBottom: dynamicIsBottom) ||
(...skipping 567 matching lines...) Expand 10 before | Expand all | Expand 10 after
1333 @override 1318 @override
1334 DartType _typeParameterLeastUpperBound( 1319 DartType _typeParameterLeastUpperBound(
1335 TypeProvider provider, DartType type1, DartType type2) { 1320 TypeProvider provider, DartType type1, DartType type2) {
1336 type1 = type1.resolveToBound(provider.objectType); 1321 type1 = type1.resolveToBound(provider.objectType);
1337 type2 = type2.resolveToBound(provider.objectType); 1322 type2 = type2.resolveToBound(provider.objectType);
1338 return getLeastUpperBound(provider, type1, type2); 1323 return getLeastUpperBound(provider, type1, type2);
1339 } 1324 }
1340 } 1325 }
1341 1326
1342 /// Tracks upper and lower type bounds for a set of type parameters. 1327 /// Tracks upper and lower type bounds for a set of type parameters.
1328 ///
1329 /// This class is used by calling [isSubtypeOf]. When it encounters one of
1330 /// the type parameters it is inferring, it will record the constraint, and
1331 /// optimistically assume the constraint will be satisfied.
1332 ///
1333 /// For example if we are inferring type parameter A, and we ask if
1334 /// `A <: num`, this will record that A must be a subytpe of `num`. It also
1335 /// handles cases when A appears as part of the structure of another type, for
1336 /// example `Iterable<A> <: Iterable<num>` would infer the same constraint
1337 /// (due to covariant generic types) as would `() -> A <: () -> int`. In
vsm 2016/08/09 20:29:33 nit: () -> num not () -> int
Jennifer Messerly 2016/08/09 20:56:57 You have keen eyes my friend ;). Done!
1338 /// contrast `(A) -> void <: (num) -> void`.
1339 ///
1340 /// Once the lower/upper bounds are determined, [_infer] should be called to
1341 /// finish the inference. It will instantiate a generic function type with the
1342 /// inferred types for each type parameter.
1343 ///
1344 /// It can also optionally compute a partial solution, in case some of the type
1345 /// parameters could not be inferred (because the constraints cannot be
1346 /// satisfied), or bail on the inference when this happens.
1347 ///
1348 /// As currently designed, an instance of this class should only be used to
1349 /// infer a single call and discarded immediately afterwards.
1343 class _StrongInferenceTypeSystem extends StrongTypeSystemImpl { 1350 class _StrongInferenceTypeSystem extends StrongTypeSystemImpl {
1344 final TypeProvider _typeProvider; 1351 final TypeProvider _typeProvider;
1345 1352
1346 /// The outer strong mode type system, used for GLB and LUB, so we don't 1353 /// The outer strong mode type system, used for GLB and LUB, so we don't
1347 /// recurse into our constraint solving code. 1354 /// recurse into our constraint solving code.
1348 final StrongTypeSystemImpl _typeSystem; 1355 final StrongTypeSystemImpl _typeSystem;
1349 final Map<TypeParameterType, _TypeParameterBound> _bounds; 1356 final Map<TypeParameterType, _TypeParameterBound> _bounds;
1350 1357
1351 _StrongInferenceTypeSystem(this._typeProvider, this._typeSystem, 1358 _StrongInferenceTypeSystem(this._typeProvider, this._typeSystem,
1352 Iterable<TypeParameterElement> typeFormals) 1359 Iterable<TypeParameterElement> typeFormals)
(...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after
1571 passedOut = true; 1578 passedOut = true;
1572 } 1579 }
1573 } else if (type is FunctionType) { 1580 } else if (type is FunctionType) {
1574 _visitFunctionType(typeParam, type, paramIn); 1581 _visitFunctionType(typeParam, type, paramIn);
1575 } else if (type is InterfaceType) { 1582 } else if (type is InterfaceType) {
1576 _visitInterfaceType(typeParam, type, paramIn); 1583 _visitInterfaceType(typeParam, type, paramIn);
1577 } 1584 }
1578 } 1585 }
1579 } 1586 }
1580 1587
1588 /**
1589 * A special union type of `Future<T> | T` used for Strong Mode inference.
1590 */
1591 class FutureUnionType extends TypeImpl {
1592 // TODO(jmesserly): a Set would be better.
1593 //
1594 // For now we know `Future<T> | T` is the only valid use, so we can rely on
1595 // the order, which simplifies some things.
1596 //
1597 // This will need clean up before this can function as a real union type.
1598 final List<DartType> types;
1599
1600 /**
1601 * Creates a union of `Future< flatten(T) > | flatten(T)`.
1602 */
1603 factory FutureUnionType(
1604 DartType type, TypeProvider provider, TypeSystem system) {
1605 type = type.flattenFutures(system);
1606
1607 // The order of these types is important: T could be a type variable, so
1608 // we want to try and match `Future<T>` before we try and match `T`.
1609 return new FutureUnionType._([
vsm 2016/08/09 20:29:33 maybe wrap the list in an UnmodifiableListView?
Jennifer Messerly 2016/08/09 20:56:57 yeah I tend to agree and I think we should use imm
1610 provider.futureType.instantiate([type]),
1611 type
1612 ]);
1613 }
1614
1615 FutureUnionType._(this.types) : super(null, null);
1616
1617 DartType get futureOfType => types[0];
1618
1619 DartType get type => types[1];
1620
1621 @override
1622 void appendTo(StringBuffer buffer) {
1623 buffer.write('(');
1624 for (int i = 0; i < types.length; i++) {
1625 if (i != 0) {
1626 buffer.write(' | ');
1627 }
1628 (types[i] as TypeImpl).appendTo(buffer);
1629 }
1630 buffer.write(')');
1631 }
1632
1633 @override
1634 int get hashCode {
1635 int hash = 0;
1636 for (var t in types) {
1637 hash = JenkinsSmiHash.combine(hash, t.hashCode);
1638 }
1639 return JenkinsSmiHash.finish(hash);
1640 }
1641
1642 @override
1643 bool operator ==(Object obj) {
1644 if (obj is FutureUnionType) {
1645 if (identical(obj, this)) return true;
1646 return types.length == obj.types.length &&
1647 types.toSet().containsAll(obj.types);
1648 }
1649 return false;
1650 }
1651
1652 @override
1653 bool isMoreSpecificThan(DartType type,
1654 [bool withDynamic = false, Set<Element> visitedElements]) =>
1655 throw new UnsupportedError(
1656 'Future unions are not part of the Dart 1 type system');
1657
1658 @override
1659 TypeImpl pruned(List<FunctionTypeAliasElement> prune) =>
1660 throw new UnsupportedError('Future unions are not substituted');
1661
1662 @override
1663 DartType substitute2(List<DartType> args, List<DartType> params,
1664 [List<FunctionTypeAliasElement> prune]) =>
1665 throw new UnsupportedError('Future unions are not used in typedefs');
1666
1667 /**
1668 * Creates a union of `T | Future<T>`, unless `T` is already a future-union,
1669 * in which case it simply returns `T`
1670 */
1671 static DartType from(
1672 DartType type, TypeProvider provider, TypeSystem system) {
1673 if (type is FutureUnionType) {
1674 return type;
1675 }
1676 return new FutureUnionType(type, provider, system);
1677 }
1678 }
1679
1581 bool _isBottom(DartType t, {bool dynamicIsBottom: false}) { 1680 bool _isBottom(DartType t, {bool dynamicIsBottom: false}) {
1582 return (t.isDynamic && dynamicIsBottom) || t.isBottom; 1681 return (t.isDynamic && dynamicIsBottom) || t.isBottom;
1583 } 1682 }
1584 1683
1585 bool _isTop(DartType t, {bool dynamicIsBottom: false}) { 1684 bool _isTop(DartType t, {bool dynamicIsBottom: false}) {
1586 // TODO(leafp): Document the rules in play here 1685 // TODO(leafp): Document the rules in play here
1587 return (t.isDynamic && !dynamicIsBottom) || t.isObject; 1686 return (t.isDynamic && !dynamicIsBottom) || t.isObject;
1588 } 1687 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698