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

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

Issue 2456803004: fixes #27586, prefer context type in generic inference (Closed)
Patch Set: more tweaks Created 4 years 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
OLDNEW
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2014, 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.resolver; 5 library analyzer.src.generated.resolver;
6 6
7 import 'dart:collection'; 7 import 'dart:collection';
8 8
9 import 'package:analyzer/dart/ast/ast.dart'; 9 import 'package:analyzer/dart/ast/ast.dart';
10 import 'package:analyzer/dart/ast/token.dart'; 10 import 'package:analyzer/dart/ast/token.dart';
(...skipping 4300 matching lines...) Expand 10 before | Expand all | Expand 10 after
4311 * Look for a single contextual type attached to [node], and returns the type 4311 * Look for a single contextual type attached to [node], and returns the type
4312 * if found, otherwise null. 4312 * if found, otherwise null.
4313 * 4313 *
4314 * If [node] has a contextual union type like `T | Future<T>` this will 4314 * If [node] has a contextual union type like `T | Future<T>` this will
4315 * simplify it to only return `T`. If the caller can handle a union type, 4315 * simplify it to only return `T`. If the caller can handle a union type,
4316 * [getContext] should be used instead. 4316 * [getContext] should be used instead.
4317 */ 4317 */
4318 static DartType getType(AstNode node) { 4318 static DartType getType(AstNode node) {
4319 DartType t = getContext(node); 4319 DartType t = getContext(node);
4320 if (t is FutureUnionType) { 4320 if (t is FutureUnionType) {
4321 return t.type; 4321 return _substituteForUnknown(t.type);
4322 } 4322 }
4323 return t; 4323 return _substituteForUnknown(t);
4324 } 4324 }
4325 4325
4326 /** 4326 /**
4327 * Like [getContext] but expands a union type into a list of types. 4327 * Like [getContext] but expands a union type into a list of types.
4328 */ 4328 */
4329 static Iterable<DartType> getTypes(AstNode node) { 4329 static Iterable<DartType> getTypes(AstNode node) {
4330 DartType t = getContext(node); 4330 DartType t = getContext(node);
4331 if (t == null) { 4331 if (t == null) {
4332 return DartType.EMPTY_LIST; 4332 return DartType.EMPTY_LIST;
4333 } 4333 }
4334 if (t is FutureUnionType) { 4334 Iterable<DartType> result = t is FutureUnionType ? t.types : [t];
4335 return t.types; 4335 return result.map(_substituteForUnknown).where((t) => t != null);
4336 } 4336 }
4337 return <DartType>[t]; 4337
4338 static DartType _substituteForUnknown(DartType t) {
4339 if (t == null) return null;
4340 // Since the type is being used for downwards inference, the expression
4341 // type E must be a subtype of the context type T, i.e. T is an upper bound.
4342 //
4343 // TODO(jmesserly): our downwards inference code is not designed to handle
4344 // the bottom type, so we need to prevent it from resulting here.
4345 // Instead use `dynamic`.
4346 //return UnknownInferredType.upperBoundForType(t);
vsm 2016/11/30 04:05:32 Did you mean to keep this commented line?
Jennifer Messerly 2016/11/30 04:35:04 Yeah, I can remove it. I kinda put it there to go
4347 return UnknownInferredType.substituteDynamic(t);
4338 } 4348 }
4339 4349
4340 /** 4350 /**
4341 * Attach contextual type information [type] to [node] for use during 4351 * Attach contextual type information [type] to [node] for use during
4342 * inference. 4352 * inference.
4343 */ 4353 */
4344 static void setType(AstNode node, DartType type) { 4354 static void setType(AstNode node, DartType type) {
4345 if (type == null || type.isDynamic) { 4355 if (type == null || type.isDynamic) {
4346 clearType(node); 4356 clearType(node);
4347 } else { 4357 } else {
(...skipping 1043 matching lines...) Expand 10 before | Expand all | Expand 10 after
5391 assert(parent is PartOfDirective); 5401 assert(parent is PartOfDirective);
5392 } else { 5402 } else {
5393 elementAnnotationImpl.annotationAst = 5403 elementAnnotationImpl.annotationAst =
5394 new ConstantAstCloner().cloneNode(node); 5404 new ConstantAstCloner().cloneNode(node);
5395 } 5405 }
5396 return null; 5406 return null;
5397 } 5407 }
5398 5408
5399 @override 5409 @override
5400 Object visitArgumentList(ArgumentList node) { 5410 Object visitArgumentList(ArgumentList node) {
5401 DartType callerType = InferenceContext.getType(node); 5411 DartType callerType = InferenceContext.getContext(node);
5402 if (callerType is FunctionType) { 5412 if (callerType is FunctionType) {
5403 Map<String, DartType> namedParameterTypes = 5413 Map<String, DartType> namedParameterTypes =
5404 callerType.namedParameterTypes; 5414 callerType.namedParameterTypes;
5405 List<DartType> normalParameterTypes = callerType.normalParameterTypes; 5415 List<DartType> normalParameterTypes = callerType.normalParameterTypes;
5406 List<DartType> optionalParameterTypes = callerType.optionalParameterTypes; 5416 List<DartType> optionalParameterTypes = callerType.optionalParameterTypes;
5407 int normalCount = normalParameterTypes.length; 5417 int normalCount = normalParameterTypes.length;
5408 int optionalCount = optionalParameterTypes.length; 5418 int optionalCount = optionalParameterTypes.length;
5409 5419
5410 NodeList<Expression> arguments = node.arguments; 5420 NodeList<Expression> arguments = node.arguments;
5411 Iterable<Expression> positional = 5421 Iterable<Expression> positional =
(...skipping 736 matching lines...) Expand 10 before | Expand all | Expand 10 after
6148 6158
6149 @override 6159 @override
6150 Object visitInstanceCreationExpression(InstanceCreationExpression node) { 6160 Object visitInstanceCreationExpression(InstanceCreationExpression node) {
6151 TypeName classTypeName = node.constructorName.type; 6161 TypeName classTypeName = node.constructorName.type;
6152 // TODO(leafp): Currently, we may re-infer types here, since we 6162 // TODO(leafp): Currently, we may re-infer types here, since we
6153 // sometimes resolve multiple times. We should really check that we 6163 // sometimes resolve multiple times. We should really check that we
6154 // have not already inferred something. However, the obvious ways to 6164 // have not already inferred something. However, the obvious ways to
6155 // check this don't work, since we may have been instantiated 6165 // check this don't work, since we may have been instantiated
6156 // to bounds in an earlier phase, and we *do* want to do inference 6166 // to bounds in an earlier phase, and we *do* want to do inference
6157 // in that case. 6167 // in that case.
6158 if (classTypeName.typeArguments == null) { 6168
6169 if (strongMode && classTypeName.typeArguments == null) {
6159 // Given a union of context types ` T0 | T1 | ... | Tn`, find the first 6170 // Given a union of context types ` T0 | T1 | ... | Tn`, find the first
6160 // valid instantiation `new C<Ti>`, if it exists. 6171 // valid instantiation `new C<Ti>`, if it exists.
6161 // TODO(jmesserly): if we support union types for real, `new C<Ti | Tj>` 6172 // TODO(jmesserly): if we support union types for real, `new C<Ti | Tj>`
6162 // will become a valid possibility. Right now the only allowed union is 6173 // will become a valid possibility. Right now the only allowed union is
6163 // `T | Future<T>` so we can take a simple approach. 6174 // `T | Future<T>` so we can take a simple approach.
6164 for (var contextType in InferenceContext.getTypes(node)) { 6175
6176 TypeDefiningElement classElement = classTypeName.type?.element;
6177 DartType rawType = classElement?.type;
Leaf 2016/11/30 05:24:29 Comment here, just for the reader, maybe? Here's
Jennifer Messerly 2016/11/30 21:19:28 sure :) to be honest I don't understand this code
6178 Iterable<DartType> contextTypes = InferenceContext.getTypes(node);
6179 for (var contextType in contextTypes) {
6165 if (contextType is InterfaceType && 6180 if (contextType is InterfaceType &&
6166 contextType.typeArguments != null && 6181 contextType.typeArguments != null &&
6167 contextType.typeArguments.isNotEmpty) { 6182 contextType.typeArguments.isNotEmpty) {
6168 // TODO(jmesserly): for generic methods we use the 6183 // TODO(jmesserly): for generic methods we use the
6169 // StrongTypeSystemImpl.inferGenericFunctionCall, which appears to 6184 // StrongTypeSystemImpl.inferGenericFunctionOrType, which appears to
6170 // be a tad more powerful than matchTypes. 6185 // be a tad more powerful than matchTypes.
6171 // 6186 //
6172 // For example it can infer this case: 6187 // For example it can infer this case:
6173 // 6188 //
6174 // class E<S, T> extends A<C<S>, T> { ... } 6189 // class E<S, T> extends A<C<S>, T> { ... }
6175 // A<C<int>, String> a0 = /*infer<int, String>*/new E("hello"); 6190 // A<C<int>, String> a0 = /*infer<int, String>*/new E("hello");
6176 // 6191 //
6177 // See _inferArgumentTypesFromContext in this file for use of it. 6192 // See _inferArgumentTypesFromContext in this file for use of it.
6178 List<DartType> targs = 6193 List<DartType> targs =
6179 inferenceContext.matchTypes(classTypeName.type, contextType); 6194 inferenceContext.matchTypes(classTypeName.type, contextType);
Leaf 2016/11/30 05:24:29 Aargh... I'm lost again. What's the difference be
Jennifer Messerly 2016/11/30 21:19:28 I would but.... I don't understand this code eithe
6180 if (targs != null && targs.any((t) => !t.isDynamic)) { 6195 if (targs != null &&
6181 ClassElement classElement = classTypeName.type.element; 6196 targs.any((t) => !t.isDynamic) &&
6182 InterfaceType rawType = classElement.type; 6197 rawType is InterfaceType) {
Leaf 2016/11/30 05:24:29 Can matchTypes return ? for one of the targs? If
Jennifer Messerly 2016/11/30 21:19:28 it shouldn't be able to, because` InferenceContext
6183 InterfaceType fullType = 6198 InterfaceType fullType =
6184 rawType.substitute2(targs, rawType.typeArguments); 6199 rawType.substitute2(targs, rawType.typeArguments);
6185 // The element resolver uses the type on the constructor name, so 6200 // The element resolver uses the type on the constructor name, so
6186 // infer it first 6201 // infer it first
6187 typeAnalyzer.inferConstructorName(node.constructorName, fullType); 6202 typeAnalyzer.inferConstructorName(node.constructorName, fullType);
6188 break; 6203 break;
6189 } 6204 }
6190 } 6205 }
6191 } 6206 }
6207 if (contextTypes.isEmpty &&
Leaf 2016/11/30 05:24:29 //comment I think this is instance failure? In w
Jennifer Messerly 2016/11/30 21:19:28 I think the idea was: for downwards inference, e.g
Leaf 2016/12/01 04:36:27 Ok, I think this makes sense then. I didn't get t
Jennifer Messerly 2017/01/06 22:31:45 yeah, I think so. Maybe I should take a shot at un
Jennifer Messerly 2017/01/11 02:24:38 Hello again! I've now removed matchTypes and this
6208 rawType is InterfaceType &&
6209 rawType.typeArguments.isNotEmpty) {
6210 node.constructorName.type.type = rawType.substitute2(
6211 new List.filled(
6212 rawType.typeArguments.length, UnknownInferredType.instance),
6213 rawType.typeArguments);
6214 }
6192 } 6215 }
6193 node.constructorName?.accept(this); 6216 node.constructorName?.accept(this);
6194 FunctionType constructorType = node.constructorName.staticElement?.type; 6217 ConstructorElement constructor = node.constructorName.staticElement;
6195 if (constructorType != null) { 6218 FunctionType constructorType = constructor?.type;
6219 if (strongMode && constructorType != null) {
6196 InferenceContext.setType(node.argumentList, constructorType); 6220 InferenceContext.setType(node.argumentList, constructorType);
6197 } 6221 }
6198 node.argumentList?.accept(this); 6222 node.argumentList?.accept(this);
6199 node.accept(elementResolver); 6223 node.accept(elementResolver);
6200 node.accept(typeAnalyzer); 6224 node.accept(typeAnalyzer);
6201 return null; 6225 return null;
6202 } 6226 }
6203 6227
6204 @override 6228 @override
6205 Object visitLabel(Label node) => null; 6229 Object visitLabel(Label node) => null;
6206 6230
6207 @override 6231 @override
6208 Object visitLibraryIdentifier(LibraryIdentifier node) => null; 6232 Object visitLibraryIdentifier(LibraryIdentifier node) => null;
6209 6233
6210 @override 6234 @override
6211 Object visitListLiteral(ListLiteral node) { 6235 Object visitListLiteral(ListLiteral node) {
6212 DartType contextType = InferenceContext.getType(node); 6236 DartType contextType = InferenceContext.getContext(node);
6237 if (contextType is FutureUnionType) {
Leaf 2016/11/30 05:24:29 Consider making this pattern a method on Inference
Jennifer Messerly 2016/11/30 21:19:28 great idea, done!
6238 contextType = (contextType as FutureUnionType).type;
6239 }
6213 List<DartType> targs = null; 6240 List<DartType> targs = null;
6214 if (node.typeArguments != null) { 6241 if (node.typeArguments != null) {
6215 targs = node.typeArguments.arguments.map((t) => t.type).toList(); 6242 targs = node.typeArguments.arguments.map((t) => t.type).toList();
6216 } else if (contextType is InterfaceType) { 6243 } else if (contextType is InterfaceType) {
6217 InterfaceType listD = 6244 InterfaceType listD =
6218 typeProvider.listType.instantiate([typeProvider.dynamicType]); 6245 typeProvider.listType.instantiate([typeProvider.dynamicType]);
6219 targs = inferenceContext.matchTypes(listD, contextType); 6246 targs = inferenceContext.matchTypes(listD, contextType);
6220 } 6247 }
6221 if (targs != null && targs.length == 1 && !targs[0].isDynamic) { 6248 if (targs != null && targs.length == 1 && !targs[0].isDynamic) {
6222 DartType eType = targs[0]; 6249 DartType eType = targs[0];
6223 InterfaceType listT = typeProvider.listType.instantiate([eType]); 6250 InterfaceType listT = typeProvider.listType.instantiate([eType]);
6224 for (Expression child in node.elements) { 6251 for (Expression child in node.elements) {
6225 InferenceContext.setType(child, eType); 6252 InferenceContext.setType(child, eType);
6226 } 6253 }
6227 InferenceContext.setType(node, listT); 6254 InferenceContext.setType(node, listT);
6228 } else { 6255 } else {
6229 InferenceContext.clearType(node); 6256 InferenceContext.clearType(node);
6230 } 6257 }
6231 super.visitListLiteral(node); 6258 super.visitListLiteral(node);
6232 return null; 6259 return null;
6233 } 6260 }
6234 6261
6235 @override 6262 @override
6236 Object visitMapLiteral(MapLiteral node) { 6263 Object visitMapLiteral(MapLiteral node) {
6237 DartType contextType = InferenceContext.getType(node); 6264 DartType contextType = InferenceContext.getContext(node);
6265 if (contextType is FutureUnionType) {
6266 contextType = (contextType as FutureUnionType).type;
6267 }
6238 List<DartType> targs = null; 6268 List<DartType> targs = null;
6239 if (node.typeArguments != null) { 6269 if (node.typeArguments != null) {
6240 targs = node.typeArguments.arguments.map((t) => t.type).toList(); 6270 targs = node.typeArguments.arguments.map((t) => t.type).toList();
6241 } else if (contextType is InterfaceType) { 6271 } else if (contextType is InterfaceType) {
6242 InterfaceType mapD = typeProvider.mapType 6272 InterfaceType mapD = typeProvider.mapType
6243 .instantiate([typeProvider.dynamicType, typeProvider.dynamicType]); 6273 .instantiate([typeProvider.dynamicType, typeProvider.dynamicType]);
6244 targs = inferenceContext.matchTypes(mapD, contextType); 6274 targs = inferenceContext.matchTypes(mapD, contextType);
6245 } 6275 }
6246 if (targs != null && targs.length == 2 && targs.any((t) => !t.isDynamic)) { 6276 if (targs != null && targs.length == 2 && targs.any((t) => !t.isDynamic)) {
6247 DartType kType = targs[0]; 6277 DartType kType = targs[0];
(...skipping 431 matching lines...) Expand 10 before | Expand all | Expand 10 after
6679 // Use propagated type inference for lambdas if not in strong mode. 6709 // Use propagated type inference for lambdas if not in strong mode.
6680 _inferFunctionExpressionsParametersTypes(node.argumentList); 6710 _inferFunctionExpressionsParametersTypes(node.argumentList);
6681 return; 6711 return;
6682 } 6712 }
6683 6713
6684 DartType contextType = node.staticInvokeType; 6714 DartType contextType = node.staticInvokeType;
6685 if (contextType is FunctionType) { 6715 if (contextType is FunctionType) {
6686 DartType originalType = node.function.staticType; 6716 DartType originalType = node.function.staticType;
6687 DartType returnContextType = InferenceContext.getContext(node); 6717 DartType returnContextType = InferenceContext.getContext(node);
6688 TypeSystem ts = typeSystem; 6718 TypeSystem ts = typeSystem;
6689 if (returnContextType != null && 6719 if (node.typeArguments == null &&
6690 node.typeArguments == null &&
6691 originalType is FunctionType && 6720 originalType is FunctionType &&
6692 originalType.typeFormals.isNotEmpty && 6721 originalType.typeFormals.isNotEmpty &&
6693 ts is StrongTypeSystemImpl) { 6722 ts is StrongTypeSystemImpl) {
6694 contextType = ts.inferGenericFunctionCall( 6723 contextType = ts.inferGenericFunctionOrType/*<FunctionType>*/(
6695 typeProvider, 6724 typeProvider,
6696 originalType, 6725 originalType,
6697 DartType.EMPTY_LIST, 6726 ParameterElement.EMPTY_LIST,
6698 DartType.EMPTY_LIST, 6727 DartType.EMPTY_LIST,
6699 originalType.returnType, 6728 originalType.returnType,
6700 returnContextType); 6729 returnContextType,
6730 downwards: true);
6701 } 6731 }
6702 6732
6703 InferenceContext.setType(node.argumentList, contextType); 6733 InferenceContext.setType(node.argumentList, contextType);
6704 } 6734 }
6705 } 6735 }
6706 6736
6707 void _inferFormalParameterList(FormalParameterList node, DartType type) { 6737 void _inferFormalParameterList(FormalParameterList node, DartType type) {
6708 if (typeAnalyzer.inferFormalParameterList(node, type)) { 6738 if (typeAnalyzer.inferFormalParameterList(node, type)) {
6709 // TODO(leafp): This gets dropped on the floor if we're in the field 6739 // TODO(leafp): This gets dropped on the floor if we're in the field
6710 // inference task. We should probably keep these infos. 6740 // inference task. We should probably keep these infos.
(...skipping 4051 matching lines...) Expand 10 before | Expand all | Expand 10 after
10762 return null; 10792 return null;
10763 } 10793 }
10764 if (identical(node.staticElement, variable)) { 10794 if (identical(node.staticElement, variable)) {
10765 if (node.inSetterContext()) { 10795 if (node.inSetterContext()) {
10766 result = true; 10796 result = true;
10767 } 10797 }
10768 } 10798 }
10769 return null; 10799 return null;
10770 } 10800 }
10771 } 10801 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698