Skip to content

Commit

Permalink
fix for #27586, rebased against master
Browse files Browse the repository at this point in the history
  • Loading branch information
Jennifer Messerly committed Nov 30, 2016
1 parent 0f9a78f commit 3e6eaaa
Show file tree
Hide file tree
Showing 15 changed files with 796 additions and 394 deletions.
2 changes: 1 addition & 1 deletion pkg/analyzer/lib/src/error/codes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4742,7 +4742,7 @@ class StrongModeCode extends ErrorCode {
static const StrongModeCode COULD_NOT_INFER = const StrongModeCode(
ErrorType.COMPILE_TIME_ERROR,
'COULD_NOT_INFER',
"Couldn't infer type parameter '{0}'; '{1}' must be of type '{2}'.");
"Couldn't infer type parameter '{0}'.{1}");

static const StrongModeCode INFERRED_TYPE = const StrongModeCode(
ErrorType.HINT, 'INFERRED_TYPE', _inferredTypeMessage);
Expand Down
87 changes: 61 additions & 26 deletions pkg/analyzer/lib/src/generated/resolver.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4299,14 +4299,26 @@ class InferenceContext {
}

/**
* Look for contextual type information attached to [node]. Returns
* the type if found, otherwise null.
* Look for contextual type information attached to [node], and returns
* the type if found.
*
* If [node] has a contextual union type like `T | Future<T>` this will be
* returned. You can use [getType] if you prefer to only get the `T`.
* If [node] has a contextual union type like `T | Future<T>` or a type that
* contains `?` this will be returned. You can use [getValueContext] if you
* prefer to eliminate the Future union, or [getType] to eliminate both.
*/
static DartType getContext(AstNode node) => node?.getProperty(_typeProperty);

/**
* Look for contextual type information attached to [node] and returns it,
* eliminating future unions by choosing `T` from `T | Future<T>`.
*
* See also [getContext].
*/
static DartType getValueContext(AstNode node) {
var context = getContext(node);
return context is FutureUnionType ? context.type : context;
}

/**
* Look for a single contextual type attached to [node], and returns the type
* if found, otherwise null.
Expand All @@ -4318,9 +4330,9 @@ class InferenceContext {
static DartType getType(AstNode node) {
DartType t = getContext(node);
if (t is FutureUnionType) {
return t.type;
return _substituteForUnknown(t.type);
}
return t;
return _substituteForUnknown(t);
}

/**
Expand All @@ -4331,10 +4343,19 @@ class InferenceContext {
if (t == null) {
return DartType.EMPTY_LIST;
}
if (t is FutureUnionType) {
return t.types;
}
return <DartType>[t];
Iterable<DartType> result = t is FutureUnionType ? t.types : [t];
return result.map(_substituteForUnknown).where((t) => t != null);
}

static DartType _substituteForUnknown(DartType t) {
if (t == null) return null;
// Since the type is being used for downwards inference, the expression
// type E must be a subtype of the context type T, i.e. T is an upper bound.
//
// TODO(jmesserly): our downwards inference code is not designed to handle
// the bottom type, so we need to prevent it from resulting here.
// Instead use `dynamic`. See `UnknownInferredType.upperBoundForType`.
return UnknownInferredType.substituteDynamic(t);
}

/**
Expand Down Expand Up @@ -5398,7 +5419,7 @@ class ResolverVisitor extends ScopedVisitor {

@override
Object visitArgumentList(ArgumentList node) {
DartType callerType = InferenceContext.getType(node);
DartType callerType = InferenceContext.getContext(node);
if (callerType is FunctionType) {
Map<String, DartType> namedParameterTypes =
callerType.namedParameterTypes;
Expand Down Expand Up @@ -6155,18 +6176,26 @@ class ResolverVisitor extends ScopedVisitor {
// check this don't work, since we may have been instantiated
// to bounds in an earlier phase, and we *do* want to do inference
// in that case.
if (classTypeName.typeArguments == null) {

if (strongMode && classTypeName.typeArguments == null) {
// Given a union of context types ` T0 | T1 | ... | Tn`, find the first
// valid instantiation `new C<Ti>`, if it exists.
// TODO(jmesserly): if we support union types for real, `new C<Ti | Tj>`
// will become a valid possibility. Right now the only allowed union is
// `T | Future<T>` so we can take a simple approach.
for (var contextType in InferenceContext.getTypes(node)) {

TypeDefiningElement classElement = classTypeName.type?.element;
// At this point rawType is the interface type whose class is the class
// being created, and whose type arguments are the type parameters of the
// class declaration.
DartType rawType = classElement?.type;
Iterable<DartType> contextTypes = InferenceContext.getTypes(node);
for (var contextType in contextTypes) {
if (contextType is InterfaceType &&
contextType.typeArguments != null &&
contextType.typeArguments.isNotEmpty) {
// TODO(jmesserly): for generic methods we use the
// StrongTypeSystemImpl.inferGenericFunctionCall, which appears to
// StrongTypeSystemImpl.inferGenericFunctionOrType, which appears to
// be a tad more powerful than matchTypes.
//
// For example it can infer this case:
Expand All @@ -6175,11 +6204,16 @@ class ResolverVisitor extends ScopedVisitor {
// A<C<int>, String> a0 = /*infer<int, String>*/new E("hello");
//
// See _inferArgumentTypesFromContext in this file for use of it.

// TODO(jmesserly): classTypeName.type is the default dynamic
// substitution, this is required by matchTypes implementation.
// TODO(jmesserly): does this mean we fail when the type has an upper
// bound?
List<DartType> targs =
inferenceContext.matchTypes(classTypeName.type, contextType);
if (targs != null && targs.any((t) => !t.isDynamic)) {
ClassElement classElement = classTypeName.type.element;
InterfaceType rawType = classElement.type;
if (targs != null &&
targs.any((t) => !t.isDynamic) &&
rawType is InterfaceType) {
InterfaceType fullType =
rawType.substitute2(targs, rawType.typeArguments);
// The element resolver uses the type on the constructor name, so
Expand All @@ -6191,8 +6225,9 @@ class ResolverVisitor extends ScopedVisitor {
}
}
node.constructorName?.accept(this);
FunctionType constructorType = node.constructorName.staticElement?.type;
if (constructorType != null) {
ConstructorElement constructor = node.constructorName.staticElement;
FunctionType constructorType = constructor?.type;
if (strongMode && constructorType != null) {
InferenceContext.setType(node.argumentList, constructorType);
}
node.argumentList?.accept(this);
Expand All @@ -6209,7 +6244,7 @@ class ResolverVisitor extends ScopedVisitor {

@override
Object visitListLiteral(ListLiteral node) {
DartType contextType = InferenceContext.getType(node);
DartType contextType = InferenceContext.getValueContext(node);
List<DartType> targs = null;
if (node.typeArguments != null) {
targs = node.typeArguments.arguments.map((t) => t.type).toList();
Expand All @@ -6234,7 +6269,7 @@ class ResolverVisitor extends ScopedVisitor {

@override
Object visitMapLiteral(MapLiteral node) {
DartType contextType = InferenceContext.getType(node);
DartType contextType = InferenceContext.getValueContext(node);
List<DartType> targs = null;
if (node.typeArguments != null) {
targs = node.typeArguments.arguments.map((t) => t.type).toList();
Expand Down Expand Up @@ -6686,18 +6721,18 @@ class ResolverVisitor extends ScopedVisitor {
DartType originalType = node.function.staticType;
DartType returnContextType = InferenceContext.getContext(node);
TypeSystem ts = typeSystem;
if (returnContextType != null &&
node.typeArguments == null &&
if (node.typeArguments == null &&
originalType is FunctionType &&
originalType.typeFormals.isNotEmpty &&
ts is StrongTypeSystemImpl) {
contextType = ts.inferGenericFunctionCall(
contextType = ts.inferGenericFunctionOrType/*<FunctionType>*/(
typeProvider,
originalType,
DartType.EMPTY_LIST,
ParameterElement.EMPTY_LIST,
DartType.EMPTY_LIST,
originalType.returnType,
returnContextType);
returnContextType,
downwards: true);
}

InferenceContext.setType(node.argumentList, contextType);
Expand Down
41 changes: 27 additions & 14 deletions pkg/analyzer/lib/src/generated/static_type_analyzer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -629,7 +629,7 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
// If there are no type arguments and we are in strong mode, try to infer
// some arguments.
if (_strongMode) {
DartType contextType = InferenceContext.getType(node);
DartType contextType = InferenceContext.getContext(node);

// Use both downwards and upwards information to infer the type.
var ts = _typeSystem as StrongTypeSystemImpl;
Expand All @@ -638,11 +638,13 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
.where((t) => t != null)
.toList();
var listTypeParam = _typeProvider.listType.typeParameters[0].type;
var syntheticParamElement = new ParameterElementImpl.synthetic(
'element', listTypeParam, ParameterKind.POSITIONAL);

DartType inferred = ts.inferGenericFunctionCall(
DartType inferred = ts.inferGenericFunctionOrType/*<InterfaceType>*/(
_typeProvider,
_typeProvider.listType,
new List.filled(elementTypes.length, listTypeParam),
new List.filled(elementTypes.length, syntheticParamElement),
elementTypes,
_typeProvider.listType,
contextType,
Expand Down Expand Up @@ -711,7 +713,7 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
// If we have no explicit type arguments, and we are in strong mode
// then try to infer type arguments.
if (_strongMode) {
DartType contextType = InferenceContext.getType(node);
DartType contextType = InferenceContext.getContext(node);

// Use both downwards and upwards information to infer the type.
var ts = _typeSystem as StrongTypeSystemImpl;
Expand All @@ -721,13 +723,19 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
node.entries.map((e) => e.value.staticType).where((t) => t != null);
var keyTypeParam = _typeProvider.mapType.typeParameters[0].type;
var valueTypeParam = _typeProvider.mapType.typeParameters[1].type;
var syntheticKeyParameter = new ParameterElementImpl.synthetic(
'key', keyTypeParam, ParameterKind.POSITIONAL);
var syntheticValueParameter = new ParameterElementImpl.synthetic(
'value', valueTypeParam, ParameterKind.POSITIONAL);

DartType inferred = ts.inferGenericFunctionCall(
ParameterizedType inferred = ts.inferGenericFunctionOrType(
_typeProvider,
_typeProvider.mapType,
new List.filled(keyTypes.length, keyTypeParam, growable: true)
..addAll(new List.filled(valueTypes.length, valueTypeParam)),
new List.from(keyTypes)..addAll(valueTypes),
new List.filled(keyTypes.length, syntheticKeyParameter,
growable: true)
..addAll(
new List.filled(valueTypes.length, syntheticValueParameter)),
new List<DartType>.from(keyTypes)..addAll(valueTypes),
_typeProvider.mapType,
contextType,
errorReporter: _resolver.errorReporter,
Expand Down Expand Up @@ -1629,7 +1637,11 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {

type = type.substitute2(freshTypeVars, typeVars);

var function = new FunctionElementImpl("", -1);
var name = cls.name;
if (constructor.name != null) {
name += '.' + constructor.name;
}
var function = new FunctionElementImpl(name, -1);
function.isSynthetic = true;
function.returnType = type.returnType;
function.typeParameters = freshVarElements;
Expand Down Expand Up @@ -1964,12 +1976,12 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
List<ParameterElement> rawParameters = ResolverVisitor
.resolveArgumentsToParameters(argumentList, fnType.parameters, null);

List<DartType> paramTypes = <DartType>[];
List<ParameterElement> params = <ParameterElement>[];
List<DartType> argTypes = <DartType>[];
for (int i = 0, length = rawParameters.length; i < length; i++) {
ParameterElement parameter = rawParameters[i];
if (parameter != null) {
paramTypes.add(parameter.type);
params.add(parameter);
argTypes.add(argumentList.arguments[i].staticType);
}
}
Expand All @@ -1986,7 +1998,7 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
// ... and finish the inference using that.
if (argTypes.isNotEmpty && _resolver.isFutureThen(fnType.element)) {
var firstArgType = argTypes[0];
var firstParamType = paramTypes[0] as FunctionType;
var firstParamType = params[0].type as FunctionType;
if (firstArgType is FunctionType) {
var argReturnType = firstArgType.returnType;
// Skip the inference if we have the top type. It can only lead to
Expand All @@ -2007,11 +2019,12 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
..returnType = paramReturnType;
function.type = new FunctionTypeImpl(function);
// Use this as the expected 1st parameter type.
paramTypes[0] = function.type;
params[0] = new ParameterElementImpl.synthetic(
params[0].name, function.type, params[0].parameterKind);
}
}
}
return ts.inferGenericFunctionCall(_typeProvider, fnType, paramTypes,
return ts.inferGenericFunctionOrType(_typeProvider, fnType, params,
argTypes, fnType.returnType, InferenceContext.getContext(node),
errorReporter: _resolver.errorReporter, errorNode: errorNode);
}
Expand Down
Loading

0 comments on commit 3e6eaaa

Please sign in to comment.