1
0
mirror of https://github.com/dart-lang/sdk synced 2024-07-03 08:19:13 +00:00

fix #26992, inference failures are now an error

R=leafp@google.com

Review URL: https://codereview.chromium.org/2295853002 .
This commit is contained in:
John Messerly 2016-09-01 13:38:09 -07:00
parent 4024fe154a
commit f57ed4d894
6 changed files with 108 additions and 39 deletions

View File

@ -17,6 +17,24 @@
* Added `WebSocket.addUtf8Text` to allow sending a pre-encoded text message
without a round-trip UTF-8 conversion.
## Strong Mode
* Breaking change - it is an error if a generic type parameter cannot be
inferred (SDK issue [26992](https://github.com/dart-lang/sdk/issues/26992)).
```dart
class Cup<T> {
Cup(T t);
}
main() {
// Error because:
// - if we choose Cup<num> it is not assignable to `cOfInt`,
// - if we choose Cup<int> then `n` is not assignable to int.
num n;
C<int> cOfInt = new C(n);
}
```
## 1.19.0
### Language changes

View File

@ -5969,6 +5969,11 @@ class StrongModeCode extends ErrorCode {
const StrongModeCode(ErrorType.COMPILE_TIME_ERROR,
'INVALID_PARAMETER_DECLARATION', _typeCheckMessage);
static const StrongModeCode COULD_NOT_INFER = const StrongModeCode(
ErrorType.COMPILE_TIME_ERROR,
'COULD_NOT_INFER',
"Could not infer type parameter {0}, {1} must be of type {2}.");
static const StrongModeCode INFERRED_TYPE = const StrongModeCode(
ErrorType.HINT, 'INFERRED_TYPE', _inferredTypeMessage);

View File

@ -1905,8 +1905,8 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
*/
void _inferGenericInvocationExpression(InvocationExpression node) {
ArgumentList arguments = node.argumentList;
FunctionType inferred = _inferGenericInvoke(
node, node.function.staticType, node.typeArguments, arguments);
FunctionType inferred = _inferGenericInvoke(node, node.function.staticType,
node.typeArguments, arguments, node.function);
if (inferred != null && inferred != node.staticInvokeType) {
// Fix up the parameter elements based on inferred method.
arguments.correspondingStaticParameters = ResolverVisitor
@ -1923,8 +1923,12 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
* This takes into account both the context type, as well as information from
* the argument types.
*/
FunctionType _inferGenericInvoke(Expression node, DartType fnType,
TypeArgumentList typeArguments, ArgumentList argumentList) {
FunctionType _inferGenericInvoke(
Expression node,
DartType fnType,
TypeArgumentList typeArguments,
ArgumentList argumentList,
AstNode errorNode) {
TypeSystem ts = _typeSystem;
if (typeArguments == null &&
fnType is FunctionType &&
@ -1982,7 +1986,8 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
}
}
return ts.inferGenericFunctionCall(_typeProvider, fnType, paramTypes,
argTypes, InferenceContext.getContext(node));
argTypes, InferenceContext.getContext(node),
errorReporter: _resolver.errorReporter, errorNode: errorNode);
}
return null;
}
@ -2020,8 +2025,8 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
_constructorToGenericFunctionType(rawElement);
ArgumentList arguments = node.argumentList;
FunctionType inferred = _inferGenericInvoke(
node, constructorType, constructor.type.typeArguments, arguments);
FunctionType inferred = _inferGenericInvoke(node, constructorType,
constructor.type.typeArguments, arguments, node.constructorName);
if (inferred != null && inferred != originalElement.type) {
// Fix up the parameter elements based on inferred method.

View File

@ -7,6 +7,7 @@ library analyzer.src.generated.type_system;
import 'dart:collection';
import 'dart:math' as math;
import 'package:analyzer/dart/ast/ast.dart' show AstNode;
import 'package:analyzer/dart/ast/token.dart' show TokenType;
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
@ -14,6 +15,8 @@ import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/generated/engine.dart'
show AnalysisContext, AnalysisOptionsImpl;
import 'package:analyzer/src/generated/error.dart'
show ErrorCode, ErrorReporter, StrongModeCode;
import 'package:analyzer/src/generated/resolver.dart' show TypeProvider;
import 'package:analyzer/src/generated/utilities_dart.dart' show ParameterKind;
import 'package:analyzer/src/generated/utilities_general.dart'
@ -248,6 +251,9 @@ class StrongTypeSystemImpl extends TypeSystem {
//
// It would be safe to return a partial solution here, but the user
// experience may be better if we simply do not infer in this case.
//
// TODO(jmesserly): this heuristic is old. Maybe we should we issue the
// inference error?
return resultType ?? fnType;
}
@ -272,7 +278,9 @@ class StrongTypeSystemImpl extends TypeSystem {
FunctionType fnType,
List<DartType> correspondingParameterTypes,
List<DartType> argumentTypes,
DartType returnContextType) {
DartType returnContextType,
{ErrorReporter errorReporter,
AstNode errorNode}) {
if (fnType.typeFormals.isEmpty) {
return fnType;
}
@ -305,7 +313,7 @@ class StrongTypeSystemImpl extends TypeSystem {
argumentTypes[i], correspondingParameterTypes[i]);
}
return inferringTypeSystem._infer(fnType);
return inferringTypeSystem._infer(fnType, errorReporter, errorNode);
}
/**
@ -1371,7 +1379,8 @@ class _StrongInferenceTypeSystem extends StrongTypeSystemImpl {
/// Given the constraints that were given by calling [isSubtypeOf], find the
/// instantiation of the generic function that satisfies these constraints.
FunctionType _infer(FunctionType fnType) {
FunctionType _infer(FunctionType fnType,
[ErrorReporter errorReporter, AstNode errorNode]) {
List<TypeParameterType> fnTypeParams =
TypeParameterTypeImpl.getTypes(fnType.typeFormals);
@ -1423,15 +1432,39 @@ class _StrongInferenceTypeSystem extends StrongTypeSystemImpl {
new _TypeParameterVariance.from(typeParam, fnType.returnType);
_TypeParameterBound bound = _bounds[typeParam];
inferredTypes[i] =
variance.passedIn || bound.lower.isBottom ? bound.upper : bound.lower;
DartType lowerBound = bound.lower;
DartType upperBound = bound.upper;
// See if the bounds can be satisfied.
if (bound.upper.isBottom ||
!_typeSystem.isSubtypeOf(bound.lower, bound.upper)) {
// Inference failed. Bail.
return null;
// TODO(jmesserly): also we should have an error for unconstrained type
// parameters, rather than silently inferring dynamic.
if (upperBound.isBottom ||
!_typeSystem.isSubtypeOf(lowerBound, upperBound)) {
// Inference failed.
if (errorReporter == null) {
return null;
}
errorReporter.reportErrorForNode(StrongModeCode.COULD_NOT_INFER,
errorNode, [typeParam, lowerBound, upperBound]);
// To make the errors more useful, we swap the normal heuristic.
//
// The normal heuristic prefers using the argument types (upwards
// inference, lower bound) to choose a tighter type.
//
// Here we want to prefer the return context type, so we can put the
// blame on the arguments to the function. That will result in narrow
// error spans. But ultimately it's just a heuristic, as the code is
// already erroneous.
//
// (we may adjust the normal heuristic too, once upwards+downwards
// inference are fully integrated, to prefer downwards info).
lowerBound = bound.upper;
upperBound = bound.lower;
}
inferredTypes[i] =
variance.passedIn || lowerBound.isBottom ? upperBound : lowerBound;
}
// Return the instantiated type.

View File

@ -1857,16 +1857,16 @@ main() {
x = foo/*error:EXTRA_POSITIONAL_ARGUMENTS*/('1', '2', '3');
foo/*error:NOT_ENOUGH_REQUIRED_ARGUMENTS*/(1);
x = foo/*error:NOT_ENOUGH_REQUIRED_ARGUMENTS*/('1');
x = /*info:DYNAMIC_CAST*/foo/*error:EXTRA_POSITIONAL_ARGUMENTS*/(1, 2, 3);
x = /*info:DYNAMIC_CAST*/foo/*error:NOT_ENOUGH_REQUIRED_ARGUMENTS*/(1);
x = /*error:COULD_NOT_INFER*/foo/*error:EXTRA_POSITIONAL_ARGUMENTS*/(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/1, /*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/2, 3);
x = /*error:COULD_NOT_INFER*/foo/*error:NOT_ENOUGH_REQUIRED_ARGUMENTS*/(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/1);
// named arguments
bar(y: 1, x: 2, /*error:UNDEFINED_NAMED_PARAMETER*/z: 3);
x = bar(/*error:UNDEFINED_NAMED_PARAMETER*/z: '1', x: '2', y: '3');
bar(y: 1);
x = bar(x: '1', /*error:UNDEFINED_NAMED_PARAMETER*/z: 42);
x = /*info:DYNAMIC_CAST*/bar(y: 1, x: 2, /*error:UNDEFINED_NAMED_PARAMETER*/z: 3);
x = /*info:DYNAMIC_CAST*/bar(x: 1);
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);
x = /*error:COULD_NOT_INFER*/bar(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/x: 1);
}
''');
}

View File

@ -625,9 +625,8 @@ class C<T> {
var x = /*info:INFERRED_TYPE_ALLOCATION*/new C(42);
// Don't infer if we had a context type.
num y;
C<int> c_int = /*info:INFERRED_TYPE_ALLOCATION*/new C(/*info:DOWN_CAST_IMPLICIT*/y);
C<int> c_int = /*info:INFERRED_TYPE_ALLOCATION*/new /*error:COULD_NOT_INFER*/C(/*info:DOWN_CAST_IMPLICIT*/y);
// These hints are not reported because we resolve with a null error listener.
C<num> c_num = /*pass should be info:INFERRED_TYPE_ALLOCATION*/new C(123);
@ -1225,10 +1224,10 @@ void main() {
A<int, String> a5 = /*error:STATIC_TYPE_ERROR*/new A<dynamic, dynamic>.named(3, "hello");
}
{
A<int, String> a0 = /*info:INFERRED_TYPE_ALLOCATION*/new A(
A<int, String> a0 = /*info:INFERRED_TYPE_ALLOCATION*/new /*error:COULD_NOT_INFER,error:COULD_NOT_INFER*/A(
/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/"hello",
/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/3);
A<int, String> a1 = /*info:INFERRED_TYPE_ALLOCATION*/new A.named(
A<int, String> a1 = /*info:INFERRED_TYPE_ALLOCATION*/new /*error:COULD_NOT_INFER,error:COULD_NOT_INFER*/A.named(
/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/"hello",
/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/3);
}
@ -1241,10 +1240,10 @@ void main() {
A<int, String> a5 = /*error:INVALID_ASSIGNMENT*/new B<dynamic, dynamic>.named("hello", 3);
}
{
A<int, String> a0 = /*info:INFERRED_TYPE_ALLOCATION*/new B(
A<int, String> a0 = /*info:INFERRED_TYPE_ALLOCATION*/new /*error:COULD_NOT_INFER,error:COULD_NOT_INFER*/B(
/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/3,
/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/"hello");
A<int, String> a1 = /*info:INFERRED_TYPE_ALLOCATION*/new B.named(
A<int, String> a1 = /*info:INFERRED_TYPE_ALLOCATION*/new /*error:COULD_NOT_INFER,error:COULD_NOT_INFER*/B.named(
/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/3,
/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/"hello");
}
@ -1257,9 +1256,9 @@ void main() {
A<int, int> a5 = /*error:INVALID_ASSIGNMENT*/new C<dynamic>.named(3);
}
{
A<int, int> a0 = /*info:INFERRED_TYPE_ALLOCATION*/new C(
A<int, int> a0 = /*info:INFERRED_TYPE_ALLOCATION*/new /*error:COULD_NOT_INFER*/C(
/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/"hello");
A<int, int> a1 = /*info:INFERRED_TYPE_ALLOCATION*/new C.named(
A<int, int> a1 = /*info:INFERRED_TYPE_ALLOCATION*/new /*error:COULD_NOT_INFER*/C.named(
/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/"hello");
}
{
@ -1271,9 +1270,9 @@ void main() {
A<int, String> a5 = /*error:INVALID_ASSIGNMENT*/new D<dynamic, dynamic>.named("hello");
}
{
A<int, String> a0 = /*info:INFERRED_TYPE_ALLOCATION*/new D(
A<int, String> a0 = /*info:INFERRED_TYPE_ALLOCATION*/new /*error:COULD_NOT_INFER*/D(
/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/3);
A<int, String> a1 = /*info:INFERRED_TYPE_ALLOCATION*/new D.named(
A<int, String> a1 = /*info:INFERRED_TYPE_ALLOCATION*/new /*error:COULD_NOT_INFER*/D.named(
/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/3);
}
{
@ -1288,9 +1287,9 @@ void main() {
b: /*info:INFERRED_TYPE_LITERAL*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/3]);
A<int, String> a2 = /*info:INFERRED_TYPE_ALLOCATION*/new F.named(3, "hello", 3, "hello");
A<int, String> a3 = /*info:INFERRED_TYPE_ALLOCATION*/new F.named(3, "hello");
A<int, String> a4 = /*info:INFERRED_TYPE_ALLOCATION*/new F.named(3, "hello",
A<int, String> a4 = /*info:INFERRED_TYPE_ALLOCATION*/new /*error:COULD_NOT_INFER,error:COULD_NOT_INFER*/F.named(3, "hello",
/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/"hello", /*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/3);
A<int, String> a5 = /*info:INFERRED_TYPE_ALLOCATION*/new F.named(3, "hello",
A<int, String> a5 = /*info:INFERRED_TYPE_ALLOCATION*/new /*error:COULD_NOT_INFER*/F.named(3, "hello",
/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/"hello");
}
}
@ -1706,7 +1705,7 @@ class MyFuture<T> implements Future<T> {
$declared f;
// Instantiates Future<int>
$downwards<int> t1 = f.then((_) =>
${allocInfo}new $upwards.value(
${allocInfo}new /*error:COULD_NOT_INFER*/$upwards.value(
/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/'hi'));
// Instantiates List<int>
@ -1774,7 +1773,7 @@ main() {
var c = new Foo().method("str");
s = c;
new Foo<String>().method(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/42);
new Foo<String>()./*error:COULD_NOT_INFER*/method(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/42);
}
''');
}
@ -1800,14 +1799,14 @@ main() {
printInt(myMax(1, 2) as int);
// Mixing int and double means return type is num.
printInt(/*info:DOWN_CAST_IMPLICIT*/max(1, 2.0));
printInt(/*info:DOWN_CAST_IMPLICIT*/min(1, 2.0));
printDouble(/*info:DOWN_CAST_IMPLICIT*/max(1, 2.0));
printDouble(/*info:DOWN_CAST_IMPLICIT*/min(1, 2.0));
printInt(/*error:COULD_NOT_INFER*/max(1, /*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/2.0));
printInt(/*error:COULD_NOT_INFER*/min(1, /*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/2.0));
printDouble(/*error:COULD_NOT_INFER*/max(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/1, 2.0));
printDouble(/*error:COULD_NOT_INFER*/min(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/1, 2.0));
// Types other than int and double are not accepted.
printInt(
/*info:DOWN_CAST_IMPLICIT*/min(
/*error:COULD_NOT_INFER*/min(
/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/"hi",
/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/"there"));
}
@ -1877,6 +1876,15 @@ main() {
''');
}
void test_genericMethods_inferenceError() {
checkFile(r'''
main() {
List<String> y;
Iterable<String> x = y./*error:COULD_NOT_INFER*/map(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/(String z) => 1.0);
}
''');
}
void test_genericMethods_inferGenericFunctionParameterType() {
var mainUnit = checkFile('''
class C<T> extends D<T> {