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

Side by Side Diff: pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart

Issue 2926763003: Add type inference for complex assignments whose LHS is an index expression. (Closed)
Patch Set: Created 3 years, 6 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) 2017, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2017, 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.md file. 3 // BSD-style license that can be found in the LICENSE.md file.
4 4
5 import 'package:front_end/src/base/instrumentation.dart'; 5 import 'package:front_end/src/base/instrumentation.dart';
6 import 'package:front_end/src/fasta/errors.dart';
6 import 'package:front_end/src/fasta/kernel/kernel_shadow_ast.dart'; 7 import 'package:front_end/src/fasta/kernel/kernel_shadow_ast.dart';
7 import 'package:front_end/src/fasta/names.dart' show callName; 8 import 'package:front_end/src/fasta/names.dart'
9 show callName, indexGetName, indexSetName;
8 import 'package:front_end/src/fasta/type_inference/type_inference_engine.dart'; 10 import 'package:front_end/src/fasta/type_inference/type_inference_engine.dart';
9 import 'package:front_end/src/fasta/type_inference/type_inference_listener.dart' ; 11 import 'package:front_end/src/fasta/type_inference/type_inference_listener.dart' ;
10 import 'package:front_end/src/fasta/type_inference/type_promotion.dart'; 12 import 'package:front_end/src/fasta/type_inference/type_promotion.dart';
11 import 'package:front_end/src/fasta/type_inference/type_schema.dart'; 13 import 'package:front_end/src/fasta/type_inference/type_schema.dart';
12 import 'package:front_end/src/fasta/type_inference/type_schema_elimination.dart' ; 14 import 'package:front_end/src/fasta/type_inference/type_schema_elimination.dart' ;
13 import 'package:front_end/src/fasta/type_inference/type_schema_environment.dart' ; 15 import 'package:front_end/src/fasta/type_inference/type_schema_environment.dart' ;
14 import 'package:kernel/ast.dart' 16 import 'package:kernel/ast.dart'
15 show 17 show
16 Arguments, 18 Arguments,
17 AsyncMarker, 19 AsyncMarker,
18 BottomType, 20 BottomType,
19 Class, 21 Class,
20 DartType, 22 DartType,
21 DynamicType, 23 DynamicType,
22 Expression, 24 Expression,
23 Field, 25 Field,
24 FunctionType, 26 FunctionType,
25 Initializer, 27 Initializer,
26 InterfaceType, 28 InterfaceType,
29 Let,
27 Member, 30 Member,
31 MethodInvocation,
28 Name, 32 Name,
29 Procedure, 33 Procedure,
30 ProcedureKind, 34 ProcedureKind,
31 Statement, 35 Statement,
32 TypeParameterType, 36 TypeParameterType,
37 VariableDeclaration,
38 VariableGet,
33 VoidType; 39 VoidType;
34 import 'package:kernel/class_hierarchy.dart'; 40 import 'package:kernel/class_hierarchy.dart';
35 import 'package:kernel/core_types.dart'; 41 import 'package:kernel/core_types.dart';
36 import 'package:kernel/type_algebra.dart'; 42 import 'package:kernel/type_algebra.dart';
37 43
38 /// Keeps track of information about the innermost function or closure being 44 /// Keeps track of information about the innermost function or closure being
39 /// inferred. 45 /// inferred.
40 class ClosureContext { 46 class ClosureContext {
41 final bool isAsync; 47 final bool isAsync;
42 48
(...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after
229 strongMode = engine.strongMode, 235 strongMode = engine.strongMode,
230 classHierarchy = engine.classHierarchy, 236 classHierarchy = engine.classHierarchy,
231 instrumentation = topLevel ? null : engine.instrumentation, 237 instrumentation = topLevel ? null : engine.instrumentation,
232 typeSchemaEnvironment = engine.typeSchemaEnvironment, 238 typeSchemaEnvironment = engine.typeSchemaEnvironment,
233 isTopLevel = topLevel; 239 isTopLevel = topLevel;
234 240
235 /// Gets the type promoter that should be used to promote types during 241 /// Gets the type promoter that should be used to promote types during
236 /// inference. 242 /// inference.
237 TypePromoter get typePromoter; 243 TypePromoter get typePromoter;
238 244
245 /// Finds a member of [receiverType] called [name], and if it is found,
246 /// reports it through instrumentation using [fileOffset].
247 Member findInterfaceMember(DartType receiverType, Name name, int fileOffset,
248 {bool setter: false, bool silent: false}) {
249 // Our non-strong golden files currently don't include interface
250 // targets, so we can't store the interface target without causing tests
251 // to fail. TODO(paulberry): fix this.
ahe 2017/06/08 14:16:33 As far as I understand, interface targets are for
Paul Berry 2017/06/08 16:17:04 Acknowledged.
252 if (!strongMode) return null;
253
254 if (receiverType is InterfaceType) {
255 var interfaceMember = classHierarchy
256 .getInterfaceMember(receiverType.classNode, name, setter: setter);
257 if (!silent && interfaceMember != null) {
258 instrumentation?.record(Uri.parse(uri), fileOffset, 'target',
259 new InstrumentationValueForMember(interfaceMember));
260 }
261 return interfaceMember;
262 }
263 return null;
264 }
265
266 /// Finds a member of [receiverType] called [name], and if it is found,
267 /// reports it through instrumentation and records it in [methodInvocation].
268 Member findMethodInvocationMember(
269 DartType receiverType, MethodInvocation methodInvocation,
270 {bool silent: false}) {
271 var interfaceMember = findInterfaceMember(
272 receiverType, methodInvocation.name, methodInvocation.fileOffset,
273 silent: silent);
274 // interfaceTarget is currently required to be a procedure, so we skip
275 // if it's anything else. TODO(paulberry): fix this - see
276 // https://codereview.chromium.org/2923653003/.
277 if (interfaceMember is Procedure) {
278 methodInvocation.interfaceTarget = interfaceMember;
279 }
280 return interfaceMember;
281 }
282
239 FunctionType getCalleeFunctionType(Member interfaceMember, 283 FunctionType getCalleeFunctionType(Member interfaceMember,
240 DartType receiverType, Name methodName, bool followCall) { 284 DartType receiverType, Name methodName, bool followCall) {
241 var type = getCalleeType(interfaceMember, receiverType, methodName); 285 var type = getCalleeType(interfaceMember, receiverType, methodName);
242 if (type is FunctionType) { 286 if (type is FunctionType) {
243 return type; 287 return type;
244 } else if (followCall && type is InterfaceType) { 288 } else if (followCall && type is InterfaceType) {
245 var member = classHierarchy.getInterfaceMember(type.classNode, callName); 289 var member = classHierarchy.getInterfaceMember(type.classNode, callName);
246 var callType = member?.getterType; 290 var callType = member?.getterType;
247 if (callType is FunctionType) { 291 if (callType is FunctionType) {
248 return callType; 292 return callType;
(...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after
404 448
405 @override 449 @override
406 void inferFunctionBody( 450 void inferFunctionBody(
407 DartType returnType, AsyncMarker asyncMarker, Statement body) { 451 DartType returnType, AsyncMarker asyncMarker, Statement body) {
408 assert(closureContext == null); 452 assert(closureContext == null);
409 closureContext = new ClosureContext(this, asyncMarker, returnType); 453 closureContext = new ClosureContext(this, asyncMarker, returnType);
410 inferStatement(body); 454 inferStatement(body);
411 closureContext = null; 455 closureContext = null;
412 } 456 }
413 457
458 /// Performs type inference for a (possibly compound) assignment whose LHS is
459 /// an index expression.
460 DartType inferIndexAssign(
461 Expression expression, DartType typeContext, bool typeNeeded) {
462 typeNeeded =
463 listener.indexAssignEnter(expression, typeContext) || typeNeeded;
464
465 // Decompose an expression like `a[b] += c` into subexpressions and record
466 // the variables holding those subexpressions (if any):
467 // receiver: a
468 // index: b
469 // read: a[b]
470 // rhs: c
471 // value: a[b] + c
472 // write: a[b] = a[b] + c
473 Expression receiver;
474 VariableDeclaration receiverVariable;
475 Expression index;
476 VariableDeclaration indexVariable;
477 Expression read;
478 VariableDeclaration readVariable;
479 Expression rhs;
480 VariableDeclaration rhsVariable;
481 Expression value;
482 VariableDeclaration valueVariable;
483 MethodInvocation write;
484 KernelConditionalExpression nullAwareCombiner;
485 MethodInvocation combiner;
486 List<VariableDeclaration> syntheticVariables = [];
487 VariableGet finalValue;
488 bool isPostIncDec = false;
489
490 // Dig in to the tree to find the expression with the main effect.
491 Expression e = expression;
492 while (true) {
493 if (e is Let) {
494 Let let = e;
495 var variable = let.variable;
496 syntheticVariables.add(variable);
497 if (KernelVariableDeclaration.isDiscarding(variable)) {
498 finalValue = let.body;
499 e = variable.initializer;
ahe 2017/06/08 14:16:33 I don't understand this. Isn't [e] supposed to be
Paul Berry 2017/06/08 16:17:04 Again considering the example of `var x = a[b] = c
500 } else {
501 e = let.body;
502 }
503 } else if (e is KernelConditionalExpression &&
504 KernelConditionalExpression.isNullAwareCombiner(e)) {
505 KernelConditionalExpression conditional = e;
506 nullAwareCombiner = conditional;
507 e = conditional.then;
508 } else {
509 break;
510 }
511 }
512
513 Expression deref(Expression e, void callback(VariableDeclaration v)) {
514 if (e is VariableGet) {
515 var variable = e.variable;
516 if (syntheticVariables.contains(variable)) {
517 callback(variable);
518 return variable.initializer;
519 }
520 }
521 return e;
522 }
523
524 if (e is KernelMethodInvocation && identical(e.name.name, '[]=')) {
525 write = e;
526 receiver = deref(e.receiver, (v) => receiverVariable = v);
527 index = deref(e.arguments.positional[0], (v) => indexVariable = v);
528 value = deref(e.arguments.positional[1], (v) => valueVariable = v);
529 if (value is KernelMethodInvocation &&
530 KernelMethodInvocation.isCombiner(value)) {
531 combiner = value;
532 read = deref(value.receiver, (v) => readVariable = v);
533 rhs = deref(value.arguments.positional[0], (v) => rhsVariable = v);
534 isPostIncDec =
535 finalValue is VariableGet && finalValue.variable == readVariable;
536 } else {
537 rhs = value;
538 if (nullAwareCombiner != null) {
539 MethodInvocation equalsNullCheck = nullAwareCombiner.condition;
540 read = deref(equalsNullCheck.receiver, (v) => readVariable = v);
541 }
542 }
543 } else {
544 internalError('Unexpected expression type: $e');
545 }
546
547 var receiverType = inferExpression(receiver, null, true);
548 receiverVariable?.type = receiverType;
549 if (read != null) {
550 var readMember =
551 findMethodInvocationMember(receiverType, read, silent: true);
552 if (readVariable != null) {
553 var calleeType = getCalleeType(readMember, receiverType, indexGetName);
554 if (calleeType is FunctionType) {
555 readVariable.type = calleeType.returnType;
556 }
557 }
558 }
559 var writeMember = findMethodInvocationMember(receiverType, write);
560 // To replicate analyzer behavior, we base type inference on the write
561 // member. TODO(paulberry): would it be better to use the read member
562 // when doing compound assignment?
563 var calleeType = getCalleeType(writeMember, receiverType, indexSetName);
564 DartType indexContext;
565 DartType rhsContext;
566 DartType inferredType;
567 if (calleeType is FunctionType &&
568 calleeType.positionalParameters.length >= 2) {
569 // TODO(paulberry): we ought to get a context for the index expression
570 // from the index formal parameter, but analyzer doesn't so for now we
571 // replicate its behavior.
572 indexContext = null;
573 rhsContext = calleeType.positionalParameters[1];
574 if (combiner != null) {
575 var combinerMember =
576 findMethodInvocationMember(rhsContext, combiner, silent: true);
577 if (isPostIncDec) {
578 inferredType = rhsContext;
579 } else {
580 inferredType = getCalleeFunctionType(
581 combinerMember, rhsContext, combiner.name, false)
582 .returnType;
583 }
584 // Analyzer uses a null context for the RHS here.
585 // TODO(paulberry): improve on this.
586 rhsContext = null;
587 } else {
588 inferredType = rhsContext;
589 }
590 } else {
591 inferredType = const DynamicType();
592 }
593 var indexType = inferExpression(index, indexContext, true);
594 indexVariable?.type = indexType;
595 var rhsType = inferExpression(rhs, rhsContext, true);
596 rhsVariable?.type = rhsType;
597 valueVariable?.type = rhsContext ?? const DynamicType();
598 if (nullAwareCombiner != null) {
599 MethodInvocation equalsInvocation = nullAwareCombiner.condition;
600 findMethodInvocationMember(rhsContext, equalsInvocation);
601 nullAwareCombiner.staticType = inferredType;
602 }
603 listener.indexAssignExit(expression, inferredType);
604 return inferredType;
605 }
606
414 /// Performs the type inference steps that are shared by all kinds of 607 /// Performs the type inference steps that are shared by all kinds of
415 /// invocations (constructors, instance methods, and static methods). 608 /// invocations (constructors, instance methods, and static methods).
416 DartType inferInvocation(DartType typeContext, bool typeNeeded, int offset, 609 DartType inferInvocation(DartType typeContext, bool typeNeeded, int offset,
417 FunctionType calleeType, DartType returnType, Arguments arguments, 610 FunctionType calleeType, DartType returnType, Arguments arguments,
418 {bool isOverloadedArithmeticOperator: false, 611 {bool isOverloadedArithmeticOperator: false,
419 DartType receiverType, 612 DartType receiverType,
420 bool skipTypeArgumentInference: false}) { 613 bool skipTypeArgumentInference: false}) {
421 var calleeTypeParameters = calleeType.typeParameters; 614 var calleeTypeParameters = calleeType.typeParameters;
422 List<DartType> explicitTypeArguments = getExplicitTypeArguments(arguments); 615 List<DartType> explicitTypeArguments = getExplicitTypeArguments(arguments);
423 bool inferenceNeeded = !skipTypeArgumentInference && 616 bool inferenceNeeded = !skipTypeArgumentInference &&
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after
558 void _forEachArgument( 751 void _forEachArgument(
559 Arguments arguments, void callback(String name, Expression expression)) { 752 Arguments arguments, void callback(String name, Expression expression)) {
560 for (var expression in arguments.positional) { 753 for (var expression in arguments.positional) {
561 callback(null, expression); 754 callback(null, expression);
562 } 755 }
563 for (var namedExpression in arguments.named) { 756 for (var namedExpression in arguments.named) {
564 callback(namedExpression.name, namedExpression.value); 757 callback(namedExpression.name, namedExpression.value);
565 } 758 }
566 } 759 }
567 } 760 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698