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

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

Issue 2757233004: fix #29108, improve inference error messages (Closed)
Patch Set: rebuild ddc Created 3 years, 9 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
« no previous file with comments | « no previous file | pkg/analyzer/test/generated/strong_mode_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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/ast.dart' show AstNode; 10 import 'package:analyzer/dart/ast/ast.dart' show AstNode;
(...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after
229 {ErrorReporter errorReporter, AstNode errorNode}) { 229 {ErrorReporter errorReporter, AstNode errorNode}) {
230 if (contextType.typeFormals.isNotEmpty || fnType.typeFormals.isEmpty) { 230 if (contextType.typeFormals.isNotEmpty || fnType.typeFormals.isEmpty) {
231 return fnType; 231 return fnType;
232 } 232 }
233 233
234 // Create a TypeSystem that will allow certain type parameters to be 234 // Create a TypeSystem that will allow certain type parameters to be
235 // inferred. It will optimistically assume these type parameters can be 235 // inferred. It will optimistically assume these type parameters can be
236 // subtypes (or supertypes) as necessary, and track the constraints that 236 // subtypes (or supertypes) as necessary, and track the constraints that
237 // are implied by this. 237 // are implied by this.
238 var inferrer = new _GenericInferrer(typeProvider, this, fnType.typeFormals); 238 var inferrer = new _GenericInferrer(typeProvider, this, fnType.typeFormals);
239 239 inferrer.constrainGenericFunctionInContext(fnType, contextType);
240 // Since we're trying to infer the instantiation, we want to ignore type
241 // formals as we check the parameters and return type.
242 var inferFnType =
243 fnType.instantiate(TypeParameterTypeImpl.getTypes(fnType.typeFormals));
244 inferrer.constrainReturnType(inferFnType, contextType);
245 240
246 // Infer and instantiate the resulting type. 241 // Infer and instantiate the resulting type.
247 return inferrer.infer(fnType, fnType.typeFormals, 242 return inferrer.infer(fnType, fnType.typeFormals,
248 errorReporter: errorReporter, errorNode: errorNode); 243 errorReporter: errorReporter, errorNode: errorNode);
249 } 244 }
250 245
251 /// Infers a generic type, function, method, or list/map literal 246 /// Infers a generic type, function, method, or list/map literal
252 /// instantiation, using the downward context type as well as the argument 247 /// instantiation, using the downward context type as well as the argument
253 /// types if available. 248 /// types if available.
254 /// 249 ///
(...skipping 1341 matching lines...) Expand 10 before | Expand all | Expand 10 after
1596 } 1591 }
1597 } 1592 }
1598 1593
1599 /// Apply a return type constraint, which asserts that the [declaredType] 1594 /// Apply a return type constraint, which asserts that the [declaredType]
1600 /// is a subtype of the [contextType]. 1595 /// is a subtype of the [contextType].
1601 void constrainReturnType(DartType declaredType, DartType contextType) { 1596 void constrainReturnType(DartType declaredType, DartType contextType) {
1602 var origin = new _TypeConstraintFromReturnType(declaredType, contextType); 1597 var origin = new _TypeConstraintFromReturnType(declaredType, contextType);
1603 _matchSubtypeOf(declaredType, contextType, null, origin, covariant: true); 1598 _matchSubtypeOf(declaredType, contextType, null, origin, covariant: true);
1604 } 1599 }
1605 1600
1601 /// Constrain a universal function type [fnType] used in a context
1602 /// [contextType].
1603 void constrainGenericFunctionInContext(
1604 FunctionType fnType, DartType contextType) {
1605 var origin = new _TypeConstraintFromFunctionContext(fnType, contextType);
1606
1607 // Since we're trying to infer the instantiation, we want to ignore type
1608 // formals as we check the parameters and return type.
1609 var inferFnType =
1610 fnType.instantiate(TypeParameterTypeImpl.getTypes(fnType.typeFormals));
1611 _matchSubtypeOf(inferFnType, contextType, null, origin, covariant: true);
1612 }
1613
1606 /// Apply an argument constraint, which asserts that the [argument] staticType 1614 /// Apply an argument constraint, which asserts that the [argument] staticType
1607 /// is a subtype of the [parameterType]. 1615 /// is a subtype of the [parameterType].
1608 void constrainArgument( 1616 void constrainArgument(
1609 DartType argumentType, DartType parameterType, String parameterName, 1617 DartType argumentType, DartType parameterType, String parameterName,
1610 {DartType genericType}) { 1618 {DartType genericType}) {
1611 var origin = new _TypeConstraintFromArgument( 1619 var origin = new _TypeConstraintFromArgument(
1612 argumentType, parameterType, parameterName, 1620 argumentType, parameterType, parameterName,
1613 genericType: genericType); 1621 genericType: genericType);
1614 _matchSubtypeOf(argumentType, parameterType, null, origin, 1622 _matchSubtypeOf(argumentType, parameterType, null, origin,
1615 covariant: false); 1623 covariant: false);
(...skipping 228 matching lines...) Expand 10 before | Expand all | Expand 10 after
1844 1852
1845 // Check the inferred types against all of the constraints. 1853 // Check the inferred types against all of the constraints.
1846 var knownTypes = new HashMap<TypeParameterType, DartType>( 1854 var knownTypes = new HashMap<TypeParameterType, DartType>(
1847 equals: (x, y) => x.element == y.element, 1855 equals: (x, y) => x.element == y.element,
1848 hashCode: (x) => x.element.hashCode); 1856 hashCode: (x) => x.element.hashCode);
1849 for (int i = 0; i < fnTypeParams.length; i++) { 1857 for (int i = 0; i < fnTypeParams.length; i++) {
1850 TypeParameterType typeParam = fnTypeParams[i]; 1858 TypeParameterType typeParam = fnTypeParams[i];
1851 var constraints = _constraints[typeParam.element]; 1859 var constraints = _constraints[typeParam.element];
1852 var typeParamBound = 1860 var typeParamBound =
1853 typeParam.bound.substitute2(inferredTypes, fnTypeParams); 1861 typeParam.bound.substitute2(inferredTypes, fnTypeParams);
1854 if (!typeParamBound.isDynamic) { 1862
1855 constraints 1863 var inferred = inferredTypes[i];
1856 .add(new _TypeConstraint.fromExtends(typeParam, typeParamBound)); 1864 bool success =
1865 constraints.every((c) => c.isSatisifedBy(_typeSystem, inferred));
1866 if (success && !typeParamBound.isDynamic) {
1867 // If everything else succeeded, check the `extends` constraint.
1868 var extendsConstraint =
1869 new _TypeConstraint.fromExtends(typeParam, typeParamBound);
1870 constraints.add(extendsConstraint);
1871 success = extendsConstraint.isSatisifedBy(_typeSystem, inferred);
1857 } 1872 }
1858 var inferred = inferredTypes[i]; 1873
1859 if (constraints.any((c) => !c.isSatisifedBy(_typeSystem, inferred))) { 1874 if (!success) {
1860 // Heuristic: keep the erroneous type, it should satisfy at least some 1875 errorReporter?.reportErrorForNode(
1861 // of the constraints (e.g. the return context). If we fall back to 1876 StrongModeCode.COULD_NOT_INFER,
1862 // instantiateToBounds, we'll typically get more errors (e.g. because 1877 errorNode,
1863 // `dynamic` is the most common bound). 1878 [typeParam, _formatError(typeParam, inferred, constraints)]);
1864 knownTypes[typeParam] = inferred; 1879
1865 errorReporter?.reportErrorForNode(StrongModeCode.COULD_NOT_INFER, 1880 // Heuristic: even if we failed, keep the erroneous type.
1866 errorNode, [typeParam, _formatError(inferred, constraints)]); 1881 // It should satisfy at least some of the constraints (e.g. the return
1867 } else if (UnknownInferredType.isKnown(inferred)) { 1882 // context). If we fall back to instantiateToBounds, we'll typically get
1883 // more errors (e.g. because `dynamic` is the most common bound).
1884 }
1885
1886 if (UnknownInferredType.isKnown(inferred)) {
1868 knownTypes[typeParam] = inferred; 1887 knownTypes[typeParam] = inferred;
1869 } 1888 }
1870 } 1889 }
1871 1890
1872 // Use instantiate to bounds to finish things off. 1891 // Use instantiate to bounds to finish things off.
1873 var hasError = new List<bool>.filled(fnTypeParams.length, false); 1892 var hasError = new List<bool>.filled(fnTypeParams.length, false);
1874 var result = _typeSystem.instantiateToBounds(genericType, 1893 var result = _typeSystem.instantiateToBounds(genericType,
1875 hasError: hasError, knownTypes: knownTypes) as dynamic/*=T*/; 1894 hasError: hasError, knownTypes: knownTypes) as dynamic/*=T*/;
1876 1895
1877 // Report any errors from instantiateToBounds. 1896 // Report any errors from instantiateToBounds.
(...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after
2032 if (t2 is InterfaceType && t2.isDartAsyncFutureOr) { 2051 if (t2 is InterfaceType && t2.isDartAsyncFutureOr) {
2033 // GLB(A, FutureOr<B>) == GLB(FutureOr<A>, B) 2052 // GLB(A, FutureOr<B>) == GLB(FutureOr<A>, B)
2034 return _getGreatestLowerBound(t2, t1); 2053 return _getGreatestLowerBound(t2, t1);
2035 } 2054 }
2036 // TODO(jmesserly): fix this rule once we support non-nullable types. 2055 // TODO(jmesserly): fix this rule once we support non-nullable types.
2037 return typeProvider.nullType; 2056 return typeProvider.nullType;
2038 } 2057 }
2039 return result; 2058 return result;
2040 } 2059 }
2041 2060
2042 String _formatError( 2061 String _formatError(TypeParameterType typeParam, DartType inferred,
2043 DartType inferred, Iterable<_TypeConstraint> constraints) { 2062 Iterable<_TypeConstraint> constraints) {
2044 var intro = "Inferred type '$inferred' does not work with constraints:"; 2063 var intro = "Tried to infer '$inferred' for '$typeParam'"
2064 " which doesn't work:";
2045 2065
2046 var constraintsByOrigin = <_TypeConstraintOrigin, List<_TypeConstraint>>{}; 2066 var constraintsByOrigin = <_TypeConstraintOrigin, List<_TypeConstraint>>{};
2047 for (var c in constraints) { 2067 for (var c in constraints) {
2048 constraintsByOrigin.putIfAbsent(c.origin, () => []).add(c); 2068 constraintsByOrigin.putIfAbsent(c.origin, () => []).add(c);
2049 } 2069 }
2050 2070
2071 // Only report unique constraint origins.
2051 Iterable<_TypeConstraint> isSatisified(bool expected) => constraintsByOrigin 2072 Iterable<_TypeConstraint> isSatisified(bool expected) => constraintsByOrigin
2052 .values 2073 .values
2053 .where((l) => 2074 .where((l) =>
2054 l.every((c) => c.isSatisifedBy(_typeSystem, inferred)) == expected) 2075 l.every((c) => c.isSatisifedBy(_typeSystem, inferred)) == expected)
2055 .expand((i) => i); 2076 .expand((i) => i);
2056 2077
2057 // Only report unique constraint origins.
2058 String unsatisified = _formatConstraints(isSatisified(false)); 2078 String unsatisified = _formatConstraints(isSatisified(false));
2059 String satisified = _formatConstraints(isSatisified(true)); 2079 String satisified = _formatConstraints(isSatisified(true));
2060 2080
2061 assert(unsatisified.isNotEmpty); 2081 assert(unsatisified.isNotEmpty);
2062 if (satisified.isNotEmpty) { 2082 if (satisified.isNotEmpty) {
2063 satisified = "\nThe type '$inferred' was inferred from:\n$satisified"; 2083 satisified = "\nThe type '$inferred' was inferred from:\n$satisified";
2064 } 2084 }
2065 2085
2066 return '\n\n$intro\n$unsatisified$satisified\n\n' 2086 return '\n\n$intro\n$unsatisified$satisified\n\n'
2067 'Consider passing explicit type argument(s) to the generic.\n\n'; 2087 'Consider passing explicit type argument(s) to the generic.\n\n';
2068 } 2088 }
2069 2089
2070 static String _formatConstraints(Iterable<_TypeConstraint> constraints) { 2090 static String _formatConstraints(Iterable<_TypeConstraint> constraints) {
2071 List<List<String>> lineParts = 2091 List<List<String>> lineParts =
2072 new Set<_TypeConstraintOrigin>.from(constraints.map((c) => c.origin)) 2092 new Set<_TypeConstraintOrigin>.from(constraints.map((c) => c.origin))
2073 .map((o) => o.formatError()) 2093 .map((o) => o.formatError())
2074 .toList(); 2094 .toList();
2075 2095
2076 int prefixMax = lineParts.map((p) => p[0].length).fold(0, math.max); 2096 int prefixMax = lineParts.map((p) => p[0].length).fold(0, math.max);
2077 int middleMax = lineParts.map((p) => p[1].length).fold(0, math.max);
2078 2097
2079 // Use a set to prevent identical message lines. 2098 // Use a set to prevent identical message lines.
2080 // (It's not uncommon for the same constraint to show up in a few places.) 2099 // (It's not uncommon for the same constraint to show up in a few places.)
2081 var messageLines = new Set<String>.from(lineParts.map((parts) { 2100 var messageLines = new Set<String>.from(lineParts.map((parts) {
2082 var prefix = parts[0]; 2101 var prefix = parts[0];
2083 var middle = parts[1]; 2102 var middle = parts[1];
2084 var prefixPad = ' ' * (prefixMax - prefix.length); 2103 var prefixPad = ' ' * (prefixMax - prefix.length);
2085 var middlePad = ' ' * (middleMax - middle.length); 2104 var middlePad = ' ' * (prefixMax);
2086 return ' $prefix$prefixPad $middle$middlePad ${parts[2]}'.trimRight(); 2105 var end = "";
2106 if (parts.length > 2) {
2107 end = '\n $middlePad ${parts[2]}';
2108 }
2109 return ' $prefix$prefixPad $middle$end';
2087 })); 2110 }));
2088 2111
2089 return messageLines.join('\n'); 2112 return messageLines.join('\n');
2090 } 2113 }
2091 } 2114 }
2092 2115
2093 /// The origin of a type constraint, for the purposes of producing a human 2116 /// The origin of a type constraint, for the purposes of producing a human
2094 /// readable error message during type inference as well as determining whether 2117 /// readable error message during type inference as well as determining whether
2095 /// the constraint was used to fix the type parameter or not. 2118 /// the constraint was used to fix the type parameter or not.
2096 abstract class _TypeConstraintOrigin { 2119 abstract class _TypeConstraintOrigin {
(...skipping 17 matching lines...) Expand all
2114 // available. 2137 // available.
2115 String prefix; 2138 String prefix;
2116 if ((genericType.name == "List" || genericType.name == "Map") && 2139 if ((genericType.name == "List" || genericType.name == "Map") &&
2117 genericType?.element?.library?.isDartCore == true) { 2140 genericType?.element?.library?.isDartCore == true) {
2118 // This will become: 2141 // This will become:
2119 // "List element" 2142 // "List element"
2120 // "Map key" 2143 // "Map key"
2121 // "Map value" 2144 // "Map value"
2122 prefix = "${genericType.name} $parameterName"; 2145 prefix = "${genericType.name} $parameterName";
2123 } else { 2146 } else {
2124 prefix = "Argument '$parameterName'"; 2147 prefix = "Parameter '$parameterName'";
2125 } 2148 }
2126 2149
2127 return [ 2150 return [
2128 prefix, 2151 prefix,
2129 "inferred as '$argumentType'", 2152 "declared as '$parameterType'",
2130 "must be a '$parameterType'." 2153 "but argument is '$argumentType'."
2131 ]; 2154 ];
2132 } 2155 }
2133 } 2156 }
2134 2157
2135 class _TypeConstraintFromReturnType extends _TypeConstraintOrigin { 2158 class _TypeConstraintFromReturnType extends _TypeConstraintOrigin {
2136 final DartType contextType; 2159 final DartType contextType;
2137 final DartType declaredType; 2160 final DartType declaredType;
2138 2161
2139 _TypeConstraintFromReturnType(this.declaredType, this.contextType); 2162 _TypeConstraintFromReturnType(this.declaredType, this.contextType);
2140 2163
2141 @override 2164 @override
2142 formatError() { 2165 formatError() {
2143 return [ 2166 return [
2144 "Return type", 2167 "Return type",
2145 "declared as '$declaredType'", 2168 "declared as '$declaredType'",
2146 "used where a '$contextType' is required." 2169 "used where '$contextType' is required."
2147 ]; 2170 ];
2148 } 2171 }
2149 } 2172 }
2173
2174 class _TypeConstraintFromFunctionContext extends _TypeConstraintOrigin {
2175 final DartType contextType;
2176 final DartType functionType;
2177
2178 _TypeConstraintFromFunctionContext(this.functionType, this.contextType);
2179
2180 @override
2181 formatError() {
2182 return [
2183 "Function type",
2184 "declared as '$functionType'",
2185 "used where '$contextType' is required."
2186 ];
2187 }
2188 }
2150 2189
2151 class _TypeConstraintFromExtendsClause extends _TypeConstraintOrigin { 2190 class _TypeConstraintFromExtendsClause extends _TypeConstraintOrigin {
2152 final TypeParameterType typeParam; 2191 final TypeParameterType typeParam;
2153 final DartType extendsType; 2192 final DartType extendsType;
2154 2193
2155 _TypeConstraintFromExtendsClause(this.typeParam, this.extendsType); 2194 _TypeConstraintFromExtendsClause(this.typeParam, this.extendsType);
2156 2195
2157 @override 2196 @override
2158 formatError() { 2197 formatError() {
2159 return [ 2198 return [
2160 "Type parameter '$typeParam'", 2199 "Type parameter '$typeParam'",
2161 "declared to extend '$extendsType'.", 2200 "declared to extend '$extendsType'."
2162 ""
2163 ]; 2201 ];
2164 } 2202 }
2165 } 2203 }
2166 2204
2167 class _TypeRange { 2205 class _TypeRange {
2168 /// The upper bound of the type parameter. In other words, T <: upperBound. 2206 /// The upper bound of the type parameter. In other words, T <: upperBound.
2169 /// 2207 ///
2170 /// In Dart this can be written as `<T extends UpperBoundType>`. 2208 /// In Dart this can be written as `<T extends UpperBoundType>`.
2171 /// 2209 ///
2172 /// In inference, this can happen as a result of parameters of function type. 2210 /// In inference, this can happen as a result of parameters of function type.
(...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after
2332 if (type is InterfaceTypeImpl) { 2370 if (type is InterfaceTypeImpl) {
2333 return type.typeArguments.any(isUnknown); 2371 return type.typeArguments.any(isUnknown);
2334 } 2372 }
2335 if (type is FunctionType) { 2373 if (type is FunctionType) {
2336 return isUnknown(type.returnType) || 2374 return isUnknown(type.returnType) ||
2337 type.parameters.any((p) => isUnknown(p.type)); 2375 type.parameters.any((p) => isUnknown(p.type));
2338 } 2376 }
2339 return false; 2377 return false;
2340 } 2378 }
2341 } 2379 }
OLDNEW
« no previous file with comments | « no previous file | pkg/analyzer/test/generated/strong_mode_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698