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

Side by Side Diff: pkg/analyzer/lib/src/task/strong/checker.dart

Issue 2221233002: fix #27036, pass definite function types to LUB (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: rename Created 4 years, 4 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
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 // TODO(jmesserly): this was ported from package:dev_compiler, and needs to be 5 // TODO(jmesserly): this was ported from package:dev_compiler, and needs to be
6 // refactored to fit into analyzer. 6 // refactored to fit into analyzer.
7 library analyzer.src.task.strong.checker; 7 library analyzer.src.task.strong.checker;
8 8
9 import 'package:analyzer/analyzer.dart'; 9 import 'package:analyzer/analyzer.dart';
10 import 'package:analyzer/dart/ast/ast.dart'; 10 import 'package:analyzer/dart/ast/ast.dart';
(...skipping 11 matching lines...) Expand all
22 import 'ast_properties.dart'; 22 import 'ast_properties.dart';
23 23
24 bool isKnownFunction(Expression expression) { 24 bool isKnownFunction(Expression expression) {
25 var element = _getKnownElement(expression); 25 var element = _getKnownElement(expression);
26 // First class functions and static methods, where we know the original 26 // First class functions and static methods, where we know the original
27 // declaration, will have an exact type, so we know a downcast will fail. 27 // declaration, will have an exact type, so we know a downcast will fail.
28 return element is FunctionElement || 28 return element is FunctionElement ||
29 element is MethodElement && element.isStatic; 29 element is MethodElement && element.isStatic;
30 } 30 }
31 31
32 /// Given an [expression] and a corresponding [typeSystem] and [typeProvider],
33 /// gets the known static type of the expression.
34 ///
35 /// Normally when we ask for an expression's type, we get the type of the
36 /// storage slot that would contain it. For function types, this is necessarily
37 /// a "fuzzy arrow" that treats `dynamic` as bottom. However, if we're
38 /// interested in the expression's own type, it can often be a "strict arrow"
39 /// because we know it evaluates to a specific, concrete function, and we can
40 /// treat "dynamic" as top for that case, which is more permissive.
41 DartType getDefiniteType(
42 Expression expression, TypeSystem typeSystem, TypeProvider typeProvider) {
43 DartType type = expression.staticType ?? DynamicTypeImpl.instance;
44 if (typeSystem is StrongTypeSystemImpl &&
45 type is FunctionType &&
46 _hasStrictArrow(expression)) {
47 // Remove fuzzy arrow if possible.
48 return typeSystem.functionTypeToConcreteType(typeProvider, type);
49 }
50 return type;
51 }
52
32 bool _hasStrictArrow(Expression expression) { 53 bool _hasStrictArrow(Expression expression) {
33 var element = _getKnownElement(expression); 54 var element = _getKnownElement(expression);
34 return element is FunctionElement || element is MethodElement; 55 return element is FunctionElement || element is MethodElement;
35 } 56 }
36 57
37 Element _getKnownElement(Expression expression) { 58 Element _getKnownElement(Expression expression) {
38 if (expression is ParenthesizedExpression) { 59 if (expression is ParenthesizedExpression) {
39 expression = (expression as ParenthesizedExpression).expression; 60 expression = (expression as ParenthesizedExpression).expression;
40 } 61 }
41 if (expression is FunctionExpression) { 62 if (expression is FunctionExpression) {
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after
180 } 201 }
181 } 202 }
182 } 203 }
183 204
184 /// Analyzer checks boolean conversions, but we need to check too, because 205 /// Analyzer checks boolean conversions, but we need to check too, because
185 /// it uses the default assignability rules that allow `dynamic` and `Object` 206 /// it uses the default assignability rules that allow `dynamic` and `Object`
186 /// to be assigned to bool with no message. 207 /// to be assigned to bool with no message.
187 void checkBoolean(Expression expr) => 208 void checkBoolean(Expression expr) =>
188 checkAssignment(expr, typeProvider.boolType); 209 checkAssignment(expr, typeProvider.boolType);
189 210
190 void checkFunctionApplication( 211 void checkFunctionApplication(InvocationExpression node) {
191 Expression node, Expression f, ArgumentList list) { 212 var ft = _getTypeAsCaller(node);
192 if (_isDynamicCall(f)) { 213
214 if (_isDynamicCall(node, ft)) {
193 // If f is Function and this is a method invocation, we should have 215 // If f is Function and this is a method invocation, we should have
194 // gotten an analyzer error, so no need to issue another error. 216 // gotten an analyzer error, so no need to issue another error.
195 _recordDynamicInvoke(node, f); 217 _recordDynamicInvoke(node, node.function);
196 } else { 218 } else {
197 checkArgumentList(list, _getTypeAsCaller(f)); 219 checkArgumentList(node.argumentList, ft);
198 } 220 }
199 } 221 }
200 222
201 DartType getType(TypeName name) { 223 DartType getType(TypeName name) {
202 return (name == null) ? DynamicTypeImpl.instance : name.type; 224 return (name == null) ? DynamicTypeImpl.instance : name.type;
203 } 225 }
204 226
205 void reset() { 227 void reset() {
206 _failure = false; 228 _failure = false;
207 } 229 }
208 230
209 @override 231 @override
210 void visitAsExpression(AsExpression node) { 232 void visitAsExpression(AsExpression node) {
211 // We could do the same check as the IsExpression below, but that is 233 // We could do the same check as the IsExpression below, but that is
212 // potentially too conservative. Instead, at runtime, we must fail hard 234 // potentially too conservative. Instead, at runtime, we must fail hard
213 // if the Dart as and the DDC as would return different values. 235 // if the Dart as and the DDC as would return different values.
214 node.visitChildren(this); 236 node.visitChildren(this);
215 } 237 }
216 238
217 @override 239 @override
218 void visitAssignmentExpression(AssignmentExpression node) { 240 void visitAssignmentExpression(AssignmentExpression node) {
219 Token operator = node.operator; 241 Token operator = node.operator;
220 TokenType operatorType = operator.type; 242 TokenType operatorType = operator.type;
221 if (operatorType == TokenType.EQ || 243 if (operatorType == TokenType.EQ ||
222 operatorType == TokenType.QUESTION_QUESTION_EQ) { 244 operatorType == TokenType.QUESTION_QUESTION_EQ) {
223 DartType staticType = _getStaticType(node.leftHandSide); 245 DartType staticType = _getDefiniteType(node.leftHandSide);
224 checkAssignment(node.rightHandSide, staticType); 246 checkAssignment(node.rightHandSide, staticType);
225 } else if (operatorType == TokenType.AMPERSAND_AMPERSAND_EQ || 247 } else if (operatorType == TokenType.AMPERSAND_AMPERSAND_EQ ||
226 operatorType == TokenType.BAR_BAR_EQ) { 248 operatorType == TokenType.BAR_BAR_EQ) {
227 checkAssignment(node.leftHandSide, typeProvider.boolType); 249 checkAssignment(node.leftHandSide, typeProvider.boolType);
228 checkAssignment(node.rightHandSide, typeProvider.boolType); 250 checkAssignment(node.rightHandSide, typeProvider.boolType);
229 } else { 251 } else {
230 _checkCompoundAssignment(node); 252 _checkCompoundAssignment(node);
231 } 253 }
232 node.visitChildren(this); 254 node.visitChildren(this);
233 } 255 }
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after
370 @override 392 @override
371 void visitForEachStatement(ForEachStatement node) { 393 void visitForEachStatement(ForEachStatement node) {
372 var loopVariable = node.identifier ?? node.loopVariable?.identifier; 394 var loopVariable = node.identifier ?? node.loopVariable?.identifier;
373 395
374 // Safely handle malformed statements. 396 // Safely handle malformed statements.
375 if (loopVariable != null) { 397 if (loopVariable != null) {
376 // Find the element type of the sequence. 398 // Find the element type of the sequence.
377 var sequenceInterface = node.awaitKeyword != null 399 var sequenceInterface = node.awaitKeyword != null
378 ? typeProvider.streamType 400 ? typeProvider.streamType
379 : typeProvider.iterableType; 401 : typeProvider.iterableType;
380 var iterableType = _getStaticType(node.iterable); 402 var iterableType = _getDefiniteType(node.iterable);
381 var elementType = 403 var elementType =
382 rules.mostSpecificTypeArgument(iterableType, sequenceInterface); 404 rules.mostSpecificTypeArgument(iterableType, sequenceInterface);
383 405
384 // If the sequence is not an Iterable (or Stream for await for) but is a 406 // If the sequence is not an Iterable (or Stream for await for) but is a
385 // supertype of it, do an implicit downcast to Iterable<dynamic>. Then 407 // supertype of it, do an implicit downcast to Iterable<dynamic>. Then
386 // we'll do a separate cast of the dynamic element to the variable's type. 408 // we'll do a separate cast of the dynamic element to the variable's type.
387 if (elementType == null) { 409 if (elementType == null) {
388 var sequenceType = 410 var sequenceType =
389 sequenceInterface.instantiate([DynamicTypeImpl.instance]); 411 sequenceInterface.instantiate([DynamicTypeImpl.instance]);
390 412
391 if (rules.isSubtypeOf(sequenceType, iterableType)) { 413 if (rules.isSubtypeOf(sequenceType, iterableType)) {
392 _recordImplicitCast(node.iterable, iterableType, sequenceType); 414 _recordImplicitCast(node.iterable, iterableType, sequenceType);
393 elementType = DynamicTypeImpl.instance; 415 elementType = DynamicTypeImpl.instance;
394 } 416 }
395 } 417 }
396 418
397 // If the sequence doesn't implement the interface at all, [ErrorVerifier] 419 // If the sequence doesn't implement the interface at all, [ErrorVerifier]
398 // will report the error, so ignore it here. 420 // will report the error, so ignore it here.
399 if (elementType != null) { 421 if (elementType != null) {
400 // Insert a cast from the sequence's element type to the loop variable's 422 // Insert a cast from the sequence's element type to the loop variable's
401 // if needed. 423 // if needed.
402 _checkDowncast(loopVariable, _getStaticType(loopVariable), 424 _checkDowncast(loopVariable, _getDefiniteType(loopVariable),
403 from: elementType); 425 from: elementType);
404 } 426 }
405 } 427 }
406 428
407 node.visitChildren(this); 429 node.visitChildren(this);
408 } 430 }
409 431
410 @override 432 @override
411 void visitForStatement(ForStatement node) { 433 void visitForStatement(ForStatement node) {
412 if (node.condition != null) { 434 if (node.condition != null) {
413 checkBoolean(node.condition); 435 checkBoolean(node.condition);
414 } 436 }
415 node.visitChildren(this); 437 node.visitChildren(this);
416 } 438 }
417 439
418 @override 440 @override
419 void visitFunctionExpression(FunctionExpression node) { 441 void visitFunctionExpression(FunctionExpression node) {
420 _checkForUnsafeBlockClosureInference(node); 442 _checkForUnsafeBlockClosureInference(node);
421 super.visitFunctionExpression(node); 443 super.visitFunctionExpression(node);
422 } 444 }
423 445
424 @override 446 @override
425 void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { 447 void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
426 checkFunctionApplication(node, node.function, node.argumentList); 448 checkFunctionApplication(node);
427 node.visitChildren(this); 449 node.visitChildren(this);
428 } 450 }
429 451
430 @override 452 @override
431 void visitIfStatement(IfStatement node) { 453 void visitIfStatement(IfStatement node) {
432 checkBoolean(node.condition); 454 checkBoolean(node.condition);
433 node.visitChildren(this); 455 node.visitChildren(this);
434 } 456 }
435 457
436 @override 458 @override
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after
543 // 565 //
544 // ... from case like: 566 // ... from case like:
545 // 567 //
546 // SomeType s; 568 // SomeType s;
547 // s.someDynamicField(...); // static get, followed by dynamic call. 569 // s.someDynamicField(...); // static get, followed by dynamic call.
548 // 570 //
549 // The first case is handled here, the second case is handled below when 571 // The first case is handled here, the second case is handled below when
550 // we call [checkFunctionApplication]. 572 // we call [checkFunctionApplication].
551 setIsDynamicInvoke(node.methodName, true); 573 setIsDynamicInvoke(node.methodName, true);
552 } else { 574 } else {
553 checkFunctionApplication(node, node.methodName, node.argumentList); 575 checkFunctionApplication(node);
554 } 576 }
555 node.visitChildren(this); 577 node.visitChildren(this);
556 } 578 }
557 579
558 @override 580 @override
559 void visitPostfixExpression(PostfixExpression node) { 581 void visitPostfixExpression(PostfixExpression node) {
560 _checkUnary(node, node.staticElement); 582 _checkUnary(node, node.staticElement);
561 node.visitChildren(this); 583 node.visitChildren(this);
562 } 584 }
563 585
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after
673 } else { 695 } else {
674 // Sanity check the operator. 696 // Sanity check the operator.
675 assert(methodElement.isOperator); 697 assert(methodElement.isOperator);
676 var functionType = methodElement.type; 698 var functionType = methodElement.type;
677 var paramTypes = functionType.normalParameterTypes; 699 var paramTypes = functionType.normalParameterTypes;
678 assert(paramTypes.length == 1); 700 assert(paramTypes.length == 1);
679 assert(functionType.namedParameterTypes.isEmpty); 701 assert(functionType.namedParameterTypes.isEmpty);
680 assert(functionType.optionalParameterTypes.isEmpty); 702 assert(functionType.optionalParameterTypes.isEmpty);
681 703
682 // Check the LHS type. 704 // Check the LHS type.
683 var rhsType = _getStaticType(expr.rightHandSide); 705 var rhsType = _getDefiniteType(expr.rightHandSide);
684 var lhsType = _getStaticType(expr.leftHandSide); 706 var lhsType = _getDefiniteType(expr.leftHandSide);
685 var returnType = rules.refineBinaryExpressionType( 707 var returnType = rules.refineBinaryExpressionType(
686 typeProvider, lhsType, op, rhsType, functionType.returnType); 708 typeProvider, lhsType, op, rhsType, functionType.returnType);
687 709
688 if (!rules.isSubtypeOf(returnType, lhsType)) { 710 if (!rules.isSubtypeOf(returnType, lhsType)) {
689 final numType = typeProvider.numType; 711 final numType = typeProvider.numType;
690 // TODO(jmesserly): this seems to duplicate logic in StaticTypeAnalyzer. 712 // TODO(jmesserly): this seems to duplicate logic in StaticTypeAnalyzer.
691 // Try to fix up the numerical case if possible. 713 // Try to fix up the numerical case if possible.
692 if (rules.isSubtypeOf(lhsType, numType) && 714 if (rules.isSubtypeOf(lhsType, numType) &&
693 rules.isSubtypeOf(lhsType, rhsType)) { 715 rules.isSubtypeOf(lhsType, rhsType)) {
694 // This is also slightly different from spec, but allows us to keep 716 // This is also slightly different from spec, but allows us to keep
(...skipping 16 matching lines...) Expand all
711 } 733 }
712 734
713 /// Records a [DownCast] of [expr] from [from] to [to], if there is one. 735 /// Records a [DownCast] of [expr] from [from] to [to], if there is one.
714 /// 736 ///
715 /// If [from] is omitted, uses the static type of [expr]. 737 /// If [from] is omitted, uses the static type of [expr].
716 /// 738 ///
717 /// If [expr] does not require a downcast because it is not related to [to] 739 /// If [expr] does not require a downcast because it is not related to [to]
718 /// or is already a subtype of it, does nothing. 740 /// or is already a subtype of it, does nothing.
719 void _checkDowncast(Expression expr, DartType to, {DartType from}) { 741 void _checkDowncast(Expression expr, DartType to, {DartType from}) {
720 if (from == null) { 742 if (from == null) {
721 from = _getStaticType(expr); 743 from = _getDefiniteType(expr);
722 } 744 }
723 745
724 // We can use anything as void. 746 // We can use anything as void.
725 if (to.isVoid) return; 747 if (to.isVoid) return;
726 748
727 // fromT <: toT, no coercion needed. 749 // fromT <: toT, no coercion needed.
728 if (rules.isSubtypeOf(from, to)) return; 750 if (rules.isSubtypeOf(from, to)) return;
729 751
730 // TODO(vsm): We can get rid of the second clause if we disallow 752 // TODO(vsm): We can get rid of the second clause if we disallow
731 // all sideways casts - see TODO below. 753 // all sideways casts - see TODO below.
(...skipping 229 matching lines...) Expand 10 before | Expand all | Expand 10 after
961 if (type.isDynamic) { 983 if (type.isDynamic) {
962 return type; 984 return type;
963 } else if (type is InterfaceType && type.element == expectedType.element) { 985 } else if (type is InterfaceType && type.element == expectedType.element) {
964 return type.typeArguments[0]; 986 return type.typeArguments[0];
965 } else { 987 } else {
966 // Malformed type - fallback on analyzer error. 988 // Malformed type - fallback on analyzer error.
967 return null; 989 return null;
968 } 990 }
969 } 991 }
970 992
971 DartType _getStaticType(Expression expr) { 993 DartType _getDefiniteType(Expression expr) =>
972 DartType t = expr.staticType ?? DynamicTypeImpl.instance; 994 getDefiniteType(expr, rules, typeProvider);
973
974 // Remove fuzzy arrow if possible.
975 if (t is FunctionType && _hasStrictArrow(expr)) {
976 t = rules.functionTypeToConcreteType(typeProvider, t);
977 }
978
979 return t;
980 }
981 995
982 /// Given an expression, return its type assuming it is 996 /// Given an expression, return its type assuming it is
983 /// in the caller position of a call (that is, accounting 997 /// in the caller position of a call (that is, accounting
984 /// for the possibility of a call method). Returns null 998 /// for the possibility of a call method). Returns null
985 /// if expression is not statically callable. 999 /// if expression is not statically callable.
986 FunctionType _getTypeAsCaller(Expression node) { 1000 FunctionType _getTypeAsCaller(InvocationExpression node) {
987 DartType t = _getStaticType(node); 1001 DartType type = node.staticInvokeType;
988 if (node is SimpleIdentifier) { 1002 if (type is FunctionType) {
989 Expression parent = node.parent; 1003 return type;
990 if (parent is MethodInvocation) { 1004 } else if (type is InterfaceType) {
991 t = parent.staticInvokeType; 1005 return rules.getCallMethodType(type);
992 }
993 }
994 if (t is InterfaceType) {
995 return rules.getCallMethodType(t);
996 }
997 if (t is FunctionType) {
998 return t;
999 } 1006 }
1000 return null; 1007 return null;
1001 } 1008 }
1002 1009
1003 /// Returns `true` if the expression is a dynamic function call or method 1010 /// Returns `true` if the expression is a dynamic function call or method
1004 /// invocation. 1011 /// invocation.
1005 bool _isDynamicCall(Expression call) { 1012 bool _isDynamicCall(InvocationExpression call, FunctionType ft) {
1006 var ft = _getTypeAsCaller(call);
1007 // TODO(leafp): This will currently return true if t is Function 1013 // TODO(leafp): This will currently return true if t is Function
1008 // This is probably the most correct thing to do for now, since 1014 // This is probably the most correct thing to do for now, since
1009 // this code is also used by the back end. Maybe revisit at some 1015 // this code is also used by the back end. Maybe revisit at some
1010 // point? 1016 // point?
1011 if (ft == null) return true; 1017 if (ft == null) return true;
1012 // Dynamic as the parameter type is treated as bottom. A function with 1018 // Dynamic as the parameter type is treated as bottom. A function with
1013 // a dynamic parameter type requires a dynamic call in general. 1019 // a dynamic parameter type requires a dynamic call in general.
1014 // However, as an optimization, if we have an original definition, we know 1020 // However, as an optimization, if we have an original definition, we know
1015 // dynamic is reified as Object - in this case a regular call is fine. 1021 // dynamic is reified as Object - in this case a regular call is fine.
1016 if (_hasStrictArrow(call)) { 1022 if (_hasStrictArrow(call.function)) {
1017 return false; 1023 return false;
1018 } 1024 }
1019 return rules.anyParameterType(ft, (pt) => pt.isDynamic); 1025 return rules.anyParameterType(ft, (pt) => pt.isDynamic);
1020 } 1026 }
1021 1027
1022 bool _isObjectGetter(Expression target, SimpleIdentifier id) { 1028 bool _isObjectGetter(Expression target, SimpleIdentifier id) {
1023 PropertyAccessorElement element = 1029 PropertyAccessorElement element =
1024 typeProvider.objectType.element.getGetter(id.name); 1030 typeProvider.objectType.element.getGetter(id.name);
1025 return (element != null && !element.isStatic); 1031 return (element != null && !element.isStatic);
1026 } 1032 }
(...skipping 471 matching lines...) Expand 10 before | Expand all | Expand 10 after
1498 var visited = new Set<InterfaceType>(); 1504 var visited = new Set<InterfaceType>();
1499 do { 1505 do {
1500 visited.add(current); 1506 visited.add(current);
1501 current.mixins.reversed.forEach( 1507 current.mixins.reversed.forEach(
1502 (m) => _checkIndividualOverridesFromClass(node, m, seen, true)); 1508 (m) => _checkIndividualOverridesFromClass(node, m, seen, true));
1503 _checkIndividualOverridesFromClass(node, current.superclass, seen, true); 1509 _checkIndividualOverridesFromClass(node, current.superclass, seen, true);
1504 current = current.superclass; 1510 current = current.superclass;
1505 } while (!current.isObject && !visited.contains(current)); 1511 } while (!current.isObject && !visited.contains(current));
1506 } 1512 }
1507 } 1513 }
OLDNEW
« no previous file with comments | « pkg/analyzer/lib/src/generated/static_type_analyzer.dart ('k') | pkg/analyzer/test/src/task/strong/checker_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698