Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 dev_compiler.src.checker.rules; | 5 library dev_compiler.src.checker.rules; |
| 6 | 6 |
| 7 import 'package:analyzer/src/generated/ast.dart'; | 7 import 'package:analyzer/src/generated/ast.dart'; |
| 8 import 'package:analyzer/src/generated/element.dart'; | 8 import 'package:analyzer/src/generated/element.dart'; |
| 9 import 'package:analyzer/src/generated/resolver.dart'; | 9 import 'package:analyzer/src/generated/resolver.dart'; |
| 10 | 10 |
| 11 import 'package:dev_compiler/src/info.dart'; | 11 import 'package:dev_compiler/src/info.dart'; |
| 12 import 'package:dev_compiler/src/options.dart'; | 12 import 'package:dev_compiler/src/options.dart'; |
| 13 import 'package:dev_compiler/src/report.dart' show CheckerReporter; | 13 import 'package:dev_compiler/src/report.dart' show CheckerReporter; |
| 14 | 14 |
| 15 abstract class TypeRules { | 15 abstract class TypeRules { |
| 16 final TypeProvider provider; | 16 final TypeProvider provider; |
| 17 LibraryInfo currentLibraryInfo = null; | 17 LibraryInfo currentLibraryInfo = null; |
| 18 | 18 |
| 19 TypeRules(TypeProvider this.provider); | 19 TypeRules(TypeProvider this.provider); |
| 20 | 20 |
| 21 bool isSubTypeOf(DartType t1, DartType t2); | 21 bool isSubTypeOf(DartType t1, DartType t2); |
| 22 bool isAssignable(DartType t1, DartType t2); | 22 bool isAssignable(DartType t1, DartType t2); |
| 23 | 23 |
| 24 bool isGroundType(DartType t) => true; | 24 bool isGroundType(DartType t) => true; |
| 25 // TODO(vsm): The default implementation is not ignoring the return type, | 25 // TODO(vsm): The default implementation is not ignoring the return type, |
| 26 // only the restricted override is. | 26 // only the restricted override is. |
| 27 bool isFunctionSubTypeOf(FunctionType f1, FunctionType f2, | 27 bool isFunctionSubTypeOf(FunctionType f1, FunctionType f2, |
| 28 {bool ignoreReturn: false}) => isSubTypeOf(f1, f2); | 28 {bool fuzzyArrows: true, bool ignoreReturn: false}) => |
| 29 isSubTypeOf(f1, f2); | |
| 29 | 30 |
| 30 bool isBoolType(DartType t) => t == provider.boolType; | 31 bool isBoolType(DartType t) => t == provider.boolType; |
| 31 bool isDoubleType(DartType t) => t == provider.doubleType; | 32 bool isDoubleType(DartType t) => t == provider.doubleType; |
| 32 bool isIntType(DartType t) => t == provider.intType; | 33 bool isIntType(DartType t) => t == provider.intType; |
| 33 bool isNumType(DartType t) => t == provider.intType.superclass; | 34 bool isNumType(DartType t) => t == provider.intType.superclass; |
| 34 bool isStringType(DartType t) => t == provider.stringType; | 35 bool isStringType(DartType t) => t == provider.stringType; |
| 35 bool isNonNullableType(DartType t) => false; | 36 bool isNonNullableType(DartType t) => false; |
| 36 bool maybeNonNullableType(DartType t) => false; | 37 bool maybeNonNullableType(DartType t) => false; |
| 37 | 38 |
| 38 StaticInfo checkAssignment(Expression expr, DartType t, bool constContext); | 39 StaticInfo checkAssignment(Expression expr, DartType t, bool constContext); |
| (...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 186 InheritanceManager manager = new InheritanceManager(element.library); | 187 InheritanceManager manager = new InheritanceManager(element.library); |
| 187 FunctionType callType = manager.lookupMemberType(t, "call"); | 188 FunctionType callType = manager.lookupMemberType(t, "call"); |
| 188 return callType; | 189 return callType; |
| 189 } | 190 } |
| 190 return null; | 191 return null; |
| 191 } | 192 } |
| 192 | 193 |
| 193 /// Check that f1 is a subtype of f2. [ignoreReturn] is used in the DDC | 194 /// Check that f1 is a subtype of f2. [ignoreReturn] is used in the DDC |
| 194 /// checker to determine whether f1 would be a subtype of f2 if the return | 195 /// checker to determine whether f1 would be a subtype of f2 if the return |
| 195 /// type of f1 is set to match f2's return type. | 196 /// type of f1 is set to match f2's return type. |
| 197 // [fuzzyArrows] indicates whether or not the f1 and f2 should be | |
| 198 // treated as fuzzy arrow types (and hence dynamic parameters to f2 treated as | |
| 199 // bottom). | |
| 196 bool isFunctionSubTypeOf(FunctionType f1, FunctionType f2, | 200 bool isFunctionSubTypeOf(FunctionType f1, FunctionType f2, |
| 197 {bool dynamicIsBottom: false, bool ignoreReturn: false}) { | 201 {bool fuzzyArrows: true, bool ignoreReturn: false}) { |
| 198 final r1s = f1.normalParameterTypes; | 202 final r1s = f1.normalParameterTypes; |
| 199 final o1s = f1.optionalParameterTypes; | 203 final o1s = f1.optionalParameterTypes; |
| 200 final n1s = f1.namedParameterTypes; | 204 final n1s = f1.namedParameterTypes; |
| 201 final r2s = f2.normalParameterTypes; | 205 final r2s = f2.normalParameterTypes; |
| 202 final o2s = f2.optionalParameterTypes; | 206 final o2s = f2.optionalParameterTypes; |
| 203 final n2s = f2.namedParameterTypes; | 207 final n2s = f2.namedParameterTypes; |
| 204 final ret1 = ignoreReturn ? f2.returnType : f1.returnType; | 208 final ret1 = ignoreReturn ? f2.returnType : f1.returnType; |
| 205 final ret2 = f2.returnType; | 209 final ret2 = f2.returnType; |
| 206 | 210 |
| 207 // A -> B <: C -> D if C <: A and | 211 // A -> B <: C -> D if C <: A and |
| 208 // either D is void or B <: D | 212 // either D is void or B <: D |
| 209 if (!ret2.isVoid && !isSubTypeOf(ret1, ret2)) return false; | 213 if (!ret2.isVoid && !isSubTypeOf(ret1, ret2)) return false; |
| 210 | 214 |
| 211 // Reject if one has named and the other has optional | 215 // Reject if one has named and the other has optional |
| 212 if (n1s.length > 0 && o2s.length > 0) return false; | 216 if (n1s.length > 0 && o2s.length > 0) return false; |
| 213 if (n2s.length > 0 && o1s.length > 0) return false; | 217 if (n2s.length > 0 && o1s.length > 0) return false; |
| 214 | 218 |
| 215 // f2 has named parameters | 219 // f2 has named parameters |
| 216 if (n2s.length > 0) { | 220 if (n2s.length > 0) { |
| 217 // Check that every named parameter in f2 has a match in f1 | 221 // Check that every named parameter in f2 has a match in f1 |
| 218 for (String k2 in n2s.keys) { | 222 for (String k2 in n2s.keys) { |
| 219 if (!n1s.containsKey(k2)) return false; | 223 if (!n1s.containsKey(k2)) return false; |
| 220 if (!isSubTypeOf(n2s[k2], n1s[k2], dynamicIsBottom: true)) return false; | 224 if (!isSubTypeOf(n2s[k2], n1s[k2], |
| 225 dynamicIsBottom: fuzzyArrows)) return false; | |
| 221 } | 226 } |
| 222 } | 227 } |
| 223 // If we get here, we either have no named parameters, | 228 // If we get here, we either have no named parameters, |
| 224 // or else the named parameters match and we have no optional | 229 // or else the named parameters match and we have no optional |
| 225 // parameters | 230 // parameters |
| 226 | 231 |
| 227 // If f1 has more required parameters, reject | 232 // If f1 has more required parameters, reject |
| 228 if (r1s.length > r2s.length) return false; | 233 if (r1s.length > r2s.length) return false; |
| 229 | 234 |
| 230 // If f2 has more required + optional parameters, reject | 235 // If f2 has more required + optional parameters, reject |
| 231 if (r2s.length + o2s.length > r1s.length + o1s.length) return false; | 236 if (r2s.length + o2s.length > r1s.length + o1s.length) return false; |
| 232 | 237 |
| 233 // The parameter lists must look like the following at this point | 238 // The parameter lists must look like the following at this point |
| 234 // where rrr is a region of required, and ooo is a region of optionals. | 239 // where rrr is a region of required, and ooo is a region of optionals. |
| 235 // f1: rrr ooo ooo ooo | 240 // f1: rrr ooo ooo ooo |
| 236 // f2: rrr rrr ooo | 241 // f2: rrr rrr ooo |
| 237 int rr = r1s.length; // required in both | 242 int rr = r1s.length; // required in both |
| 238 int or = r2s.length - r1s.length; // optional in f1, required in f2 | 243 int or = r2s.length - r1s.length; // optional in f1, required in f2 |
| 239 int oo = o2s.length; // optional in both | 244 int oo = o2s.length; // optional in both |
| 240 | 245 |
| 241 for (int i = 0; i < rr; ++i) { | 246 for (int i = 0; i < rr; ++i) { |
| 242 if (!isSubTypeOf(r2s[i], r1s[i], dynamicIsBottom: true)) return false; | 247 if (!isSubTypeOf(r2s[i], r1s[i], |
| 248 dynamicIsBottom: fuzzyArrows)) return false; | |
| 243 } | 249 } |
| 244 for (int i = 0, j = rr; i < or; ++i, ++j) { | 250 for (int i = 0, j = rr; i < or; ++i, ++j) { |
| 245 if (!isSubTypeOf(r2s[j], o1s[i], dynamicIsBottom: true)) return false; | 251 if (!isSubTypeOf(r2s[j], o1s[i], |
| 252 dynamicIsBottom: fuzzyArrows)) return false; | |
| 246 } | 253 } |
| 247 for (int i = or, j = 0; i < oo; ++i, ++j) { | 254 for (int i = or, j = 0; i < oo; ++i, ++j) { |
| 248 if (!isSubTypeOf(o2s[j], o1s[i], dynamicIsBottom: true)) return false; | 255 if (!isSubTypeOf(o2s[j], o1s[i], |
| 256 dynamicIsBottom: fuzzyArrows)) return false; | |
| 249 } | 257 } |
| 250 return true; | 258 return true; |
| 251 } | 259 } |
| 252 | 260 |
| 253 bool _isInterfaceSubTypeOf(InterfaceType i1, InterfaceType i2) { | 261 bool _isInterfaceSubTypeOf(InterfaceType i1, InterfaceType i2) { |
| 254 if (i1 == i2) return true; | 262 if (i1 == i2) return true; |
| 255 | 263 |
| 256 if (i1.element == i2.element) { | 264 if (i1.element == i2.element) { |
| 257 List<DartType> tArgs1 = i1.typeArguments; | 265 List<DartType> tArgs1 = i1.typeArguments; |
| 258 List<DartType> tArgs2 = i2.typeArguments; | 266 List<DartType> tArgs2 = i2.typeArguments; |
| (...skipping 305 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 564 /// Called for each list literal which gets inferred | 572 /// Called for each list literal which gets inferred |
| 565 void annotateListLiteral(ListLiteral e, List<DartType> targs) {} | 573 void annotateListLiteral(ListLiteral e, List<DartType> targs) {} |
| 566 | 574 |
| 567 /// Called for each map literal which gets inferred | 575 /// Called for each map literal which gets inferred |
| 568 void annotateMapLiteral(MapLiteral e, List<DartType> targs) {} | 576 void annotateMapLiteral(MapLiteral e, List<DartType> targs) {} |
| 569 | 577 |
| 570 /// Called for each new/const which gets inferred | 578 /// Called for each new/const which gets inferred |
| 571 void annotateInstanceCreationExpression( | 579 void annotateInstanceCreationExpression( |
| 572 InstanceCreationExpression e, List<DartType> targs) {} | 580 InstanceCreationExpression e, List<DartType> targs) {} |
| 573 | 581 |
| 582 /// Called for cast from dynamic required for inference to succeed | |
| 583 void annotateCastFromDynamic(Expression e, DartType t) {} | |
| 584 | |
| 585 /// Called for each function expression return type inferred | |
| 586 void annotateFunctionExpression(FunctionExpression e, DartType returnType) {} | |
| 587 | |
| 574 /// Downward inference | 588 /// Downward inference |
| 575 bool inferExpression(Expression e, DartType t, List<String> errors) { | 589 bool inferExpression(Expression e, DartType t, List<String> errors) { |
| 576 if (e is Conversion) return inferExpression(e.node, t, errors); | 590 // Don't cast top level expressions, only sub-expressions |
| 591 return _inferExpression(e, t, errors, cast: false); | |
| 592 } | |
| 593 | |
| 594 /// Downward inference | |
| 595 bool _inferExpression(Expression e, DartType t, List<String> errors, | |
| 596 {cast: true}) { | |
| 597 if (e is Conversion) return _inferExpression(e.node, t, errors); | |
| 577 if (rules.isSubTypeOf(rules.getStaticType(e), t)) return true; | 598 if (rules.isSubTypeOf(rules.getStaticType(e), t)) return true; |
| 599 if (cast && rules.getStaticType(e).isDynamic) { | |
| 600 annotateCastFromDynamic(e, t); | |
| 601 return true; | |
| 602 } | |
| 578 if (e is FunctionExpression) return _inferFunctionExpression(e, t, errors); | 603 if (e is FunctionExpression) return _inferFunctionExpression(e, t, errors); |
| 579 if (e is ListLiteral) return _inferListLiteral(e, t, errors); | 604 if (e is ListLiteral) return _inferListLiteral(e, t, errors); |
| 580 if (e is MapLiteral) return _inferMapLiteral(e, t, errors); | 605 if (e is MapLiteral) return _inferMapLiteral(e, t, errors); |
| 581 if (e is NamedExpression) return _inferNamedExpression(e, t, errors); | 606 if (e is NamedExpression) return _inferNamedExpression(e, t, errors); |
| 582 if (e is InstanceCreationExpression) return _inferInstanceCreationExpression ( | 607 if (e is InstanceCreationExpression) return _inferInstanceCreationExpression ( |
| 583 e, t, errors); | 608 e, t, errors); |
| 584 errors.add("$e cannot be typed as $t"); | 609 errors.add("$e cannot be typed as $t"); |
| 585 return false; | 610 return false; |
| 586 } | 611 } |
| 587 | 612 |
| (...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 701 var fType = baseType.substitute2(targs, tparams); | 726 var fType = baseType.substitute2(targs, tparams); |
| 702 { | 727 { |
| 703 var rTypes = fType.normalParameterTypes; | 728 var rTypes = fType.normalParameterTypes; |
| 704 var oTypes = fType.optionalParameterTypes; | 729 var oTypes = fType.optionalParameterTypes; |
| 705 var pTypes = new List.from(rTypes)..addAll(oTypes); | 730 var pTypes = new List.from(rTypes)..addAll(oTypes); |
| 706 var pArgs = arguments.where((x) => x is! NamedExpression); | 731 var pArgs = arguments.where((x) => x is! NamedExpression); |
| 707 var pi = 0; | 732 var pi = 0; |
| 708 for (var arg in pArgs) { | 733 for (var arg in pArgs) { |
| 709 if (pi >= pTypes.length) return false; | 734 if (pi >= pTypes.length) return false; |
| 710 var argType = pTypes[pi]; | 735 var argType = pTypes[pi]; |
| 711 if (!inferExpression(arg, argType, errors)) return false; | 736 if (!_inferExpression(arg, argType, errors)) return false; |
| 712 pi++; | 737 pi++; |
| 713 } | 738 } |
| 714 var nTypes = fType.namedParameterTypes; | 739 var nTypes = fType.namedParameterTypes; |
| 715 for (var arg0 in arguments) { | 740 for (var arg0 in arguments) { |
| 716 if (arg0 is! NamedExpression) continue; | 741 if (arg0 is! NamedExpression) continue; |
| 717 var arg = arg0 as NamedExpression; | 742 var arg = arg0 as NamedExpression; |
| 718 SimpleIdentifier nameNode = arg.name.label; | 743 SimpleIdentifier nameNode = arg.name.label; |
| 719 String name = nameNode.name; | 744 String name = nameNode.name; |
| 720 var argType = nTypes[name]; | 745 var argType = nTypes[name]; |
| 721 if (argType == null) return false; | 746 if (argType == null) return false; |
| 722 if (!inferExpression(arg, argType, errors)) return false; | 747 if (!_inferExpression(arg, argType, errors)) return false; |
| 723 } | 748 } |
| 724 } | 749 } |
| 725 annotateInstanceCreationExpression(e, targs); | 750 annotateInstanceCreationExpression(e, targs); |
| 726 return true; | 751 return true; |
| 727 } | 752 } |
| 728 | 753 |
| 729 bool _inferNamedExpression(NamedExpression e, DartType t, errors) { | 754 bool _inferNamedExpression(NamedExpression e, DartType t, errors) { |
| 730 return inferExpression(e.expression, t, errors); | 755 return _inferExpression(e.expression, t, errors); |
| 731 } | 756 } |
| 732 | 757 |
| 733 bool _inferFunctionExpression(FunctionExpression e, DartType t, errors) { | 758 bool _inferFunctionExpression(FunctionExpression e, DartType t, errors) { |
| 734 if (t is! FunctionType) return false; | 759 if (t is! FunctionType) return false; |
| 735 var returnT = (t as FunctionType).returnType; | 760 var fType = (t as FunctionType); |
| 736 if (returnT.isDynamic) return false; | |
| 737 var eType = e.staticType; | 761 var eType = e.staticType; |
| 738 if (eType is! FunctionType) return false; | 762 if (eType is! FunctionType) return false; |
| 763 | |
| 764 // We have a function literal, so we can treat the arrow type | |
| 765 // as non-fuzzy. Since we're not improving on parameter types | |
| 766 // currently, if this check fails then we cannot succeed, so | |
| 767 // bail out. Otherwise, we never need to check the parameter types | |
| 768 // again. | |
| 769 if (!rules.isFunctionSubTypeOf(eType, fType, | |
| 770 fuzzyArrows: false, ignoreReturn: true)) return false; | |
| 771 | |
| 772 // This only entered inference because of fuzzy typing. | |
| 773 // The function type is already specific enough, we can just | |
| 774 // succeed and treat it as a succesful inference | |
|
vsm
2015/04/03 22:32:59
s/succesful/successful/
| |
| 775 if (rules.isSubTypeOf(eType.returnType, fType.returnType)) return true; | |
| 776 | |
| 777 // Fuzzy typing again, handle the void case (not caught by the previous) | |
| 778 if (fType.returnType.isVoid) return true; | |
| 779 | |
| 739 if (e.body is! ExpressionFunctionBody) return false; | 780 if (e.body is! ExpressionFunctionBody) return false; |
| 740 var body = (e.body as ExpressionFunctionBody).expression; | 781 var body = (e.body as ExpressionFunctionBody).expression; |
| 741 if (!inferExpression(body, returnT, errors)) return false; | 782 if (!_inferExpression(body, fType.returnType, errors)) return false; |
| 783 | |
| 742 // TODO(leafp): Try narrowing the argument types if possible | 784 // TODO(leafp): Try narrowing the argument types if possible |
| 743 // to get better code in the function body. This requires checking | 785 // to get better code in the function body. This requires checking |
| 744 // that the body is well-typed at the more specific type. | 786 // that the body is well-typed at the more specific type. |
| 745 var element = (e.element as ExecutableElementImpl); | 787 |
| 746 var oldReturnT = element.returnType; | 788 // At this point, we know that the parameter types are in the appropriate su btype |
| 747 element.returnType = returnT; | 789 // relation, and we have checked that we can type the body at the appropriat e return |
| 748 // Work around dynamic as bottom for now by handling function literals | 790 // type, so we can are done. |
| 749 // with dynamic arguments specially. We already know the body is typable | 791 annotateFunctionExpression(e, fType.returnType); |
| 750 // at the chosen type, and if all args are dynamic, then function must be | 792 return true; |
| 751 // typeable. | |
| 752 if ((eType as FunctionType).parameters.every((x) => x.type.isDynamic)) { | |
| 753 return true; | |
| 754 } | |
| 755 if (rules.isSubTypeOf(e.staticType, t)) return true; | |
| 756 element.returnType = oldReturnT; | |
| 757 return false; | |
| 758 } | 793 } |
| 759 | 794 |
| 760 bool _inferListLiteral(ListLiteral e, DartType t, errors) { | 795 bool _inferListLiteral(ListLiteral e, DartType t, errors) { |
| 761 var dyn = rules.provider.dynamicType; | 796 var dyn = rules.provider.dynamicType; |
| 762 var listT = rules.provider.listType.substitute4([dyn]); | 797 var listT = rules.provider.listType.substitute4([dyn]); |
| 763 // List <: t (using dart rules) must be true | 798 // List <: t (using dart rules) must be true |
| 764 if (!listT.isSubtypeOf(t)) return false; | 799 if (!listT.isSubtypeOf(t)) return false; |
| 765 // The list literal must have no type arguments | 800 // The list literal must have no type arguments |
| 766 if (e.typeArguments != null) return false; | 801 if (e.typeArguments != null) return false; |
| 767 if (t is! InterfaceType) return false; | 802 if (t is! InterfaceType) return false; |
| 768 var targs = _matchTypes(listT, t); | 803 var targs = _matchTypes(listT, t); |
| 769 if (targs == null) return false; | 804 if (targs == null) return false; |
| 770 assert(targs.length == 1); | 805 assert(targs.length == 1); |
| 771 var etype = targs[0]; | 806 var etype = targs[0]; |
| 772 assert(!etype.isDynamic); | 807 assert(!etype.isDynamic); |
| 773 var elements = e.elements; | 808 var elements = e.elements; |
| 774 var b = elements.every((e) => inferExpression(e, etype, errors)); | 809 var b = elements.every((e) => _inferExpression(e, etype, errors)); |
| 775 if (b) annotateListLiteral(e, targs); | 810 if (b) annotateListLiteral(e, targs); |
| 776 return b; | 811 return b; |
| 777 } | 812 } |
| 778 | 813 |
| 779 bool _inferMapLiteral(MapLiteral e, DartType t, errors) { | 814 bool _inferMapLiteral(MapLiteral e, DartType t, errors) { |
| 780 var dyn = rules.provider.dynamicType; | 815 var dyn = rules.provider.dynamicType; |
| 781 var mapT = rules.provider.mapType.substitute4([dyn, dyn]); | 816 var mapT = rules.provider.mapType.substitute4([dyn, dyn]); |
| 782 // Map <: t (using dart rules) must be true | 817 // Map <: t (using dart rules) must be true |
| 783 if (!mapT.isSubtypeOf(t)) return false; | 818 if (!mapT.isSubtypeOf(t)) return false; |
| 784 // The map literal must have no type arguments | 819 // The map literal must have no type arguments |
| 785 if (e.typeArguments != null) return false; | 820 if (e.typeArguments != null) return false; |
| 786 if (t is! InterfaceType) return false; | 821 if (t is! InterfaceType) return false; |
| 787 var targs = _matchTypes(mapT, t); | 822 var targs = _matchTypes(mapT, t); |
| 788 if (targs == null) return false; | 823 if (targs == null) return false; |
| 789 assert(targs.length == 2); | 824 assert(targs.length == 2); |
| 790 var kType = targs[0]; | 825 var kType = targs[0]; |
| 791 var vType = targs[1]; | 826 var vType = targs[1]; |
| 792 assert(!(kType.isDynamic && vType.isDynamic)); | 827 assert(!(kType.isDynamic && vType.isDynamic)); |
| 793 var entries = e.entries; | 828 var entries = e.entries; |
| 794 bool inferEntry(MapLiteralEntry entry) { | 829 bool inferEntry(MapLiteralEntry entry) { |
| 795 return inferExpression(entry.key, kType, errors) && | 830 return _inferExpression(entry.key, kType, errors) && |
| 796 inferExpression(entry.value, vType, errors); | 831 _inferExpression(entry.value, vType, errors); |
| 797 } | 832 } |
| 798 var b = entries.every(inferEntry); | 833 var b = entries.every(inferEntry); |
| 799 if (b) annotateMapLiteral(e, targs); | 834 if (b) annotateMapLiteral(e, targs); |
| 800 return b; | 835 return b; |
| 801 } | 836 } |
| 802 } | 837 } |
| OLD | NEW |