Skip to content

Commit f57ed4d

Browse files
author
John Messerly
committed
fix #26992, inference failures are now an error
[email protected] Review URL: https://codereview.chromium.org/2295853002 .
1 parent 4024fe1 commit f57ed4d

File tree

6 files changed

+108
-39
lines changed

6 files changed

+108
-39
lines changed

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,24 @@
1717
* Added `WebSocket.addUtf8Text` to allow sending a pre-encoded text message
1818
without a round-trip UTF-8 conversion.
1919

20+
## Strong Mode
21+
22+
* Breaking change - it is an error if a generic type parameter cannot be
23+
inferred (SDK issue [26992](https://github.com/dart-lang/sdk/issues/26992)).
24+
25+
```dart
26+
class Cup<T> {
27+
Cup(T t);
28+
}
29+
main() {
30+
// Error because:
31+
// - if we choose Cup<num> it is not assignable to `cOfInt`,
32+
// - if we choose Cup<int> then `n` is not assignable to int.
33+
num n;
34+
C<int> cOfInt = new C(n);
35+
}
36+
```
37+
2038
## 1.19.0
2139
2240
### Language changes

pkg/analyzer/lib/src/generated/error.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5969,6 +5969,11 @@ class StrongModeCode extends ErrorCode {
59695969
const StrongModeCode(ErrorType.COMPILE_TIME_ERROR,
59705970
'INVALID_PARAMETER_DECLARATION', _typeCheckMessage);
59715971

5972+
static const StrongModeCode COULD_NOT_INFER = const StrongModeCode(
5973+
ErrorType.COMPILE_TIME_ERROR,
5974+
'COULD_NOT_INFER',
5975+
"Could not infer type parameter {0}, {1} must be of type {2}.");
5976+
59725977
static const StrongModeCode INFERRED_TYPE = const StrongModeCode(
59735978
ErrorType.HINT, 'INFERRED_TYPE', _inferredTypeMessage);
59745979

pkg/analyzer/lib/src/generated/static_type_analyzer.dart

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1905,8 +1905,8 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
19051905
*/
19061906
void _inferGenericInvocationExpression(InvocationExpression node) {
19071907
ArgumentList arguments = node.argumentList;
1908-
FunctionType inferred = _inferGenericInvoke(
1909-
node, node.function.staticType, node.typeArguments, arguments);
1908+
FunctionType inferred = _inferGenericInvoke(node, node.function.staticType,
1909+
node.typeArguments, arguments, node.function);
19101910
if (inferred != null && inferred != node.staticInvokeType) {
19111911
// Fix up the parameter elements based on inferred method.
19121912
arguments.correspondingStaticParameters = ResolverVisitor
@@ -1923,8 +1923,12 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
19231923
* This takes into account both the context type, as well as information from
19241924
* the argument types.
19251925
*/
1926-
FunctionType _inferGenericInvoke(Expression node, DartType fnType,
1927-
TypeArgumentList typeArguments, ArgumentList argumentList) {
1926+
FunctionType _inferGenericInvoke(
1927+
Expression node,
1928+
DartType fnType,
1929+
TypeArgumentList typeArguments,
1930+
ArgumentList argumentList,
1931+
AstNode errorNode) {
19281932
TypeSystem ts = _typeSystem;
19291933
if (typeArguments == null &&
19301934
fnType is FunctionType &&
@@ -1982,7 +1986,8 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
19821986
}
19831987
}
19841988
return ts.inferGenericFunctionCall(_typeProvider, fnType, paramTypes,
1985-
argTypes, InferenceContext.getContext(node));
1989+
argTypes, InferenceContext.getContext(node),
1990+
errorReporter: _resolver.errorReporter, errorNode: errorNode);
19861991
}
19871992
return null;
19881993
}
@@ -2020,8 +2025,8 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
20202025
_constructorToGenericFunctionType(rawElement);
20212026

20222027
ArgumentList arguments = node.argumentList;
2023-
FunctionType inferred = _inferGenericInvoke(
2024-
node, constructorType, constructor.type.typeArguments, arguments);
2028+
FunctionType inferred = _inferGenericInvoke(node, constructorType,
2029+
constructor.type.typeArguments, arguments, node.constructorName);
20252030

20262031
if (inferred != null && inferred != originalElement.type) {
20272032
// Fix up the parameter elements based on inferred method.

pkg/analyzer/lib/src/generated/type_system.dart

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,16 @@ library analyzer.src.generated.type_system;
77
import 'dart:collection';
88
import 'dart:math' as math;
99

10+
import 'package:analyzer/dart/ast/ast.dart' show AstNode;
1011
import 'package:analyzer/dart/ast/token.dart' show TokenType;
1112
import 'package:analyzer/dart/element/element.dart';
1213
import 'package:analyzer/dart/element/type.dart';
1314
import 'package:analyzer/src/dart/element/element.dart';
1415
import 'package:analyzer/src/dart/element/type.dart';
1516
import 'package:analyzer/src/generated/engine.dart'
1617
show AnalysisContext, AnalysisOptionsImpl;
18+
import 'package:analyzer/src/generated/error.dart'
19+
show ErrorCode, ErrorReporter, StrongModeCode;
1720
import 'package:analyzer/src/generated/resolver.dart' show TypeProvider;
1821
import 'package:analyzer/src/generated/utilities_dart.dart' show ParameterKind;
1922
import 'package:analyzer/src/generated/utilities_general.dart'
@@ -248,6 +251,9 @@ class StrongTypeSystemImpl extends TypeSystem {
248251
//
249252
// It would be safe to return a partial solution here, but the user
250253
// experience may be better if we simply do not infer in this case.
254+
//
255+
// TODO(jmesserly): this heuristic is old. Maybe we should we issue the
256+
// inference error?
251257
return resultType ?? fnType;
252258
}
253259

@@ -272,7 +278,9 @@ class StrongTypeSystemImpl extends TypeSystem {
272278
FunctionType fnType,
273279
List<DartType> correspondingParameterTypes,
274280
List<DartType> argumentTypes,
275-
DartType returnContextType) {
281+
DartType returnContextType,
282+
{ErrorReporter errorReporter,
283+
AstNode errorNode}) {
276284
if (fnType.typeFormals.isEmpty) {
277285
return fnType;
278286
}
@@ -305,7 +313,7 @@ class StrongTypeSystemImpl extends TypeSystem {
305313
argumentTypes[i], correspondingParameterTypes[i]);
306314
}
307315

308-
return inferringTypeSystem._infer(fnType);
316+
return inferringTypeSystem._infer(fnType, errorReporter, errorNode);
309317
}
310318

311319
/**
@@ -1371,7 +1379,8 @@ class _StrongInferenceTypeSystem extends StrongTypeSystemImpl {
13711379

13721380
/// Given the constraints that were given by calling [isSubtypeOf], find the
13731381
/// instantiation of the generic function that satisfies these constraints.
1374-
FunctionType _infer(FunctionType fnType) {
1382+
FunctionType _infer(FunctionType fnType,
1383+
[ErrorReporter errorReporter, AstNode errorNode]) {
13751384
List<TypeParameterType> fnTypeParams =
13761385
TypeParameterTypeImpl.getTypes(fnType.typeFormals);
13771386

@@ -1423,15 +1432,39 @@ class _StrongInferenceTypeSystem extends StrongTypeSystemImpl {
14231432
new _TypeParameterVariance.from(typeParam, fnType.returnType);
14241433

14251434
_TypeParameterBound bound = _bounds[typeParam];
1426-
inferredTypes[i] =
1427-
variance.passedIn || bound.lower.isBottom ? bound.upper : bound.lower;
1435+
DartType lowerBound = bound.lower;
1436+
DartType upperBound = bound.upper;
14281437

14291438
// See if the bounds can be satisfied.
1430-
if (bound.upper.isBottom ||
1431-
!_typeSystem.isSubtypeOf(bound.lower, bound.upper)) {
1432-
// Inference failed. Bail.
1433-
return null;
1439+
// TODO(jmesserly): also we should have an error for unconstrained type
1440+
// parameters, rather than silently inferring dynamic.
1441+
if (upperBound.isBottom ||
1442+
!_typeSystem.isSubtypeOf(lowerBound, upperBound)) {
1443+
// Inference failed.
1444+
if (errorReporter == null) {
1445+
return null;
1446+
}
1447+
errorReporter.reportErrorForNode(StrongModeCode.COULD_NOT_INFER,
1448+
errorNode, [typeParam, lowerBound, upperBound]);
1449+
1450+
// To make the errors more useful, we swap the normal heuristic.
1451+
//
1452+
// The normal heuristic prefers using the argument types (upwards
1453+
// inference, lower bound) to choose a tighter type.
1454+
//
1455+
// Here we want to prefer the return context type, so we can put the
1456+
// blame on the arguments to the function. That will result in narrow
1457+
// error spans. But ultimately it's just a heuristic, as the code is
1458+
// already erroneous.
1459+
//
1460+
// (we may adjust the normal heuristic too, once upwards+downwards
1461+
// inference are fully integrated, to prefer downwards info).
1462+
lowerBound = bound.upper;
1463+
upperBound = bound.lower;
14341464
}
1465+
1466+
inferredTypes[i] =
1467+
variance.passedIn || lowerBound.isBottom ? upperBound : lowerBound;
14351468
}
14361469

14371470
// Return the instantiated type.

pkg/analyzer/test/src/task/strong/checker_test.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1857,16 +1857,16 @@ main() {
18571857
x = foo/*error:EXTRA_POSITIONAL_ARGUMENTS*/('1', '2', '3');
18581858
foo/*error:NOT_ENOUGH_REQUIRED_ARGUMENTS*/(1);
18591859
x = foo/*error:NOT_ENOUGH_REQUIRED_ARGUMENTS*/('1');
1860-
x = /*info:DYNAMIC_CAST*/foo/*error:EXTRA_POSITIONAL_ARGUMENTS*/(1, 2, 3);
1861-
x = /*info:DYNAMIC_CAST*/foo/*error:NOT_ENOUGH_REQUIRED_ARGUMENTS*/(1);
1860+
x = /*error:COULD_NOT_INFER*/foo/*error:EXTRA_POSITIONAL_ARGUMENTS*/(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/1, /*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/2, 3);
1861+
x = /*error:COULD_NOT_INFER*/foo/*error:NOT_ENOUGH_REQUIRED_ARGUMENTS*/(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/1);
18621862
18631863
// named arguments
18641864
bar(y: 1, x: 2, /*error:UNDEFINED_NAMED_PARAMETER*/z: 3);
18651865
x = bar(/*error:UNDEFINED_NAMED_PARAMETER*/z: '1', x: '2', y: '3');
18661866
bar(y: 1);
18671867
x = bar(x: '1', /*error:UNDEFINED_NAMED_PARAMETER*/z: 42);
1868-
x = /*info:DYNAMIC_CAST*/bar(y: 1, x: 2, /*error:UNDEFINED_NAMED_PARAMETER*/z: 3);
1869-
x = /*info:DYNAMIC_CAST*/bar(x: 1);
1868+
x = /*error:COULD_NOT_INFER*/bar(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/y: 1, /*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/x: 2, /*error:UNDEFINED_NAMED_PARAMETER*/z: 3);
1869+
x = /*error:COULD_NOT_INFER*/bar(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/x: 1);
18701870
}
18711871
''');
18721872
}

pkg/analyzer/test/src/task/strong/inferred_type_test.dart

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -625,9 +625,8 @@ class C<T> {
625625
626626
var x = /*info:INFERRED_TYPE_ALLOCATION*/new C(42);
627627
628-
// Don't infer if we had a context type.
629628
num y;
630-
C<int> c_int = /*info:INFERRED_TYPE_ALLOCATION*/new C(/*info:DOWN_CAST_IMPLICIT*/y);
629+
C<int> c_int = /*info:INFERRED_TYPE_ALLOCATION*/new /*error:COULD_NOT_INFER*/C(/*info:DOWN_CAST_IMPLICIT*/y);
631630
632631
// These hints are not reported because we resolve with a null error listener.
633632
C<num> c_num = /*pass should be info:INFERRED_TYPE_ALLOCATION*/new C(123);
@@ -1225,10 +1224,10 @@ void main() {
12251224
A<int, String> a5 = /*error:STATIC_TYPE_ERROR*/new A<dynamic, dynamic>.named(3, "hello");
12261225
}
12271226
{
1228-
A<int, String> a0 = /*info:INFERRED_TYPE_ALLOCATION*/new A(
1227+
A<int, String> a0 = /*info:INFERRED_TYPE_ALLOCATION*/new /*error:COULD_NOT_INFER,error:COULD_NOT_INFER*/A(
12291228
/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/"hello",
12301229
/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/3);
1231-
A<int, String> a1 = /*info:INFERRED_TYPE_ALLOCATION*/new A.named(
1230+
A<int, String> a1 = /*info:INFERRED_TYPE_ALLOCATION*/new /*error:COULD_NOT_INFER,error:COULD_NOT_INFER*/A.named(
12321231
/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/"hello",
12331232
/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/3);
12341233
}
@@ -1241,10 +1240,10 @@ void main() {
12411240
A<int, String> a5 = /*error:INVALID_ASSIGNMENT*/new B<dynamic, dynamic>.named("hello", 3);
12421241
}
12431242
{
1244-
A<int, String> a0 = /*info:INFERRED_TYPE_ALLOCATION*/new B(
1243+
A<int, String> a0 = /*info:INFERRED_TYPE_ALLOCATION*/new /*error:COULD_NOT_INFER,error:COULD_NOT_INFER*/B(
12451244
/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/3,
12461245
/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/"hello");
1247-
A<int, String> a1 = /*info:INFERRED_TYPE_ALLOCATION*/new B.named(
1246+
A<int, String> a1 = /*info:INFERRED_TYPE_ALLOCATION*/new /*error:COULD_NOT_INFER,error:COULD_NOT_INFER*/B.named(
12481247
/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/3,
12491248
/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/"hello");
12501249
}
@@ -1257,9 +1256,9 @@ void main() {
12571256
A<int, int> a5 = /*error:INVALID_ASSIGNMENT*/new C<dynamic>.named(3);
12581257
}
12591258
{
1260-
A<int, int> a0 = /*info:INFERRED_TYPE_ALLOCATION*/new C(
1259+
A<int, int> a0 = /*info:INFERRED_TYPE_ALLOCATION*/new /*error:COULD_NOT_INFER*/C(
12611260
/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/"hello");
1262-
A<int, int> a1 = /*info:INFERRED_TYPE_ALLOCATION*/new C.named(
1261+
A<int, int> a1 = /*info:INFERRED_TYPE_ALLOCATION*/new /*error:COULD_NOT_INFER*/C.named(
12631262
/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/"hello");
12641263
}
12651264
{
@@ -1271,9 +1270,9 @@ void main() {
12711270
A<int, String> a5 = /*error:INVALID_ASSIGNMENT*/new D<dynamic, dynamic>.named("hello");
12721271
}
12731272
{
1274-
A<int, String> a0 = /*info:INFERRED_TYPE_ALLOCATION*/new D(
1273+
A<int, String> a0 = /*info:INFERRED_TYPE_ALLOCATION*/new /*error:COULD_NOT_INFER*/D(
12751274
/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/3);
1276-
A<int, String> a1 = /*info:INFERRED_TYPE_ALLOCATION*/new D.named(
1275+
A<int, String> a1 = /*info:INFERRED_TYPE_ALLOCATION*/new /*error:COULD_NOT_INFER*/D.named(
12771276
/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/3);
12781277
}
12791278
{
@@ -1288,9 +1287,9 @@ void main() {
12881287
b: /*info:INFERRED_TYPE_LITERAL*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/3]);
12891288
A<int, String> a2 = /*info:INFERRED_TYPE_ALLOCATION*/new F.named(3, "hello", 3, "hello");
12901289
A<int, String> a3 = /*info:INFERRED_TYPE_ALLOCATION*/new F.named(3, "hello");
1291-
A<int, String> a4 = /*info:INFERRED_TYPE_ALLOCATION*/new F.named(3, "hello",
1290+
A<int, String> a4 = /*info:INFERRED_TYPE_ALLOCATION*/new /*error:COULD_NOT_INFER,error:COULD_NOT_INFER*/F.named(3, "hello",
12921291
/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/"hello", /*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/3);
1293-
A<int, String> a5 = /*info:INFERRED_TYPE_ALLOCATION*/new F.named(3, "hello",
1292+
A<int, String> a5 = /*info:INFERRED_TYPE_ALLOCATION*/new /*error:COULD_NOT_INFER*/F.named(3, "hello",
12941293
/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/"hello");
12951294
}
12961295
}
@@ -1706,7 +1705,7 @@ class MyFuture<T> implements Future<T> {
17061705
$declared f;
17071706
// Instantiates Future<int>
17081707
$downwards<int> t1 = f.then((_) =>
1709-
${allocInfo}new $upwards.value(
1708+
${allocInfo}new /*error:COULD_NOT_INFER*/$upwards.value(
17101709
/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/'hi'));
17111710
17121711
// Instantiates List<int>
@@ -1774,7 +1773,7 @@ main() {
17741773
var c = new Foo().method("str");
17751774
s = c;
17761775
1777-
new Foo<String>().method(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/42);
1776+
new Foo<String>()./*error:COULD_NOT_INFER*/method(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/42);
17781777
}
17791778
''');
17801779
}
@@ -1800,14 +1799,14 @@ main() {
18001799
printInt(myMax(1, 2) as int);
18011800
18021801
// Mixing int and double means return type is num.
1803-
printInt(/*info:DOWN_CAST_IMPLICIT*/max(1, 2.0));
1804-
printInt(/*info:DOWN_CAST_IMPLICIT*/min(1, 2.0));
1805-
printDouble(/*info:DOWN_CAST_IMPLICIT*/max(1, 2.0));
1806-
printDouble(/*info:DOWN_CAST_IMPLICIT*/min(1, 2.0));
1802+
printInt(/*error:COULD_NOT_INFER*/max(1, /*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/2.0));
1803+
printInt(/*error:COULD_NOT_INFER*/min(1, /*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/2.0));
1804+
printDouble(/*error:COULD_NOT_INFER*/max(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/1, 2.0));
1805+
printDouble(/*error:COULD_NOT_INFER*/min(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/1, 2.0));
18071806
18081807
// Types other than int and double are not accepted.
18091808
printInt(
1810-
/*info:DOWN_CAST_IMPLICIT*/min(
1809+
/*error:COULD_NOT_INFER*/min(
18111810
/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/"hi",
18121811
/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/"there"));
18131812
}
@@ -1877,6 +1876,15 @@ main() {
18771876
''');
18781877
}
18791878

1879+
void test_genericMethods_inferenceError() {
1880+
checkFile(r'''
1881+
main() {
1882+
List<String> y;
1883+
Iterable<String> x = y./*error:COULD_NOT_INFER*/map(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/(String z) => 1.0);
1884+
}
1885+
''');
1886+
}
1887+
18801888
void test_genericMethods_inferGenericFunctionParameterType() {
18811889
var mainUnit = checkFile('''
18821890
class C<T> extends D<T> {

0 commit comments

Comments
 (0)