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

Side by Side Diff: pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart

Issue 2950213002: Infer the return types of local functions where appropriate. (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 file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 /// This file declares a "shadow hierarchy" of concrete classes which extend 5 /// This file declares a "shadow hierarchy" of concrete classes which extend
6 /// the kernel class hierarchy, adding methods and fields needed by the 6 /// the kernel class hierarchy, adding methods and fields needed by the
7 /// BodyBuilder. 7 /// BodyBuilder.
8 /// 8 ///
9 /// Instances of these classes may be created using the factory methods in 9 /// Instances of these classes may be created using the factory methods in
10 /// `ast_factory.dart`. 10 /// `ast_factory.dart`.
11 /// 11 ///
12 /// Note that these classes represent the Dart language prior to desugaring. 12 /// Note that these classes represent the Dart language prior to desugaring.
13 /// When a single Dart construct desugars to a tree containing multiple kernel 13 /// When a single Dart construct desugars to a tree containing multiple kernel
14 /// AST nodes, the shadow class extends the kernel object at the top of the 14 /// AST nodes, the shadow class extends the kernel object at the top of the
15 /// desugared tree. 15 /// desugared tree.
16 /// 16 ///
17 /// This means that in some cases multiple shadow classes may extend the same 17 /// This means that in some cases multiple shadow classes may extend the same
18 /// kernel class, because multiple constructs in Dart may desugar to a tree 18 /// kernel class, because multiple constructs in Dart may desugar to a tree
19 /// with the same kind of root node. 19 /// with the same kind of root node.
20 import 'package:front_end/src/base/instrumentation.dart'; 20 import 'package:front_end/src/base/instrumentation.dart';
21 import 'package:front_end/src/fasta/type_inference/dependency_collector.dart'; 21 import 'package:front_end/src/fasta/type_inference/dependency_collector.dart';
22 import 'package:front_end/src/fasta/type_inference/type_inference_engine.dart'; 22 import 'package:front_end/src/fasta/type_inference/type_inference_engine.dart';
23 import 'package:front_end/src/fasta/type_inference/type_inference_listener.dart' ; 23 import 'package:front_end/src/fasta/type_inference/type_inference_listener.dart' ;
24 import 'package:front_end/src/fasta/type_inference/type_inferrer.dart'; 24 import 'package:front_end/src/fasta/type_inference/type_inferrer.dart';
25 import 'package:front_end/src/fasta/type_inference/type_promotion.dart'; 25 import 'package:front_end/src/fasta/type_inference/type_promotion.dart';
26 import 'package:front_end/src/fasta/type_inference/type_schema.dart'; 26 import 'package:front_end/src/fasta/type_inference/type_schema.dart';
27 import 'package:front_end/src/fasta/type_inference/type_schema_elimination.dart' ; 27 import 'package:front_end/src/fasta/type_inference/type_schema_elimination.dart' ;
28 import 'package:front_end/src/fasta/type_inference/type_schema_environment.dart' ;
29 import 'package:kernel/ast.dart' 28 import 'package:kernel/ast.dart'
30 hide InvalidExpression, InvalidInitializer, InvalidStatement; 29 hide InvalidExpression, InvalidInitializer, InvalidStatement;
31 import 'package:kernel/frontend/accessors.dart'; 30 import 'package:kernel/frontend/accessors.dart';
32 import 'package:kernel/type_algebra.dart';
33 import 'package:kernel/type_environment.dart'; 31 import 'package:kernel/type_environment.dart';
34 32
35 import '../errors.dart' show internalError; 33 import '../errors.dart' show internalError;
36 34
37 /// Computes the return type of a (possibly factory) constructor. 35 /// Computes the return type of a (possibly factory) constructor.
38 InterfaceType computeConstructorReturnType(Member constructor) { 36 InterfaceType computeConstructorReturnType(Member constructor) {
39 if (constructor is Constructor) { 37 if (constructor is Constructor) {
40 return constructor.enclosingClass.thisType; 38 return constructor.enclosingClass.thisType;
41 } else { 39 } else {
42 return constructor.function.returnType; 40 return constructor.function.returnType;
(...skipping 665 matching lines...) Expand 10 before | Expand all | Expand 10 after
708 } 706 }
709 inferrer.inferStatement(body); 707 inferrer.inferStatement(body);
710 inferrer.listener.forInStatementExit(this); 708 inferrer.listener.forInStatementExit(this);
711 } 709 }
712 } 710 }
713 711
714 /// Concrete shadow object representing a local function declaration in kernel 712 /// Concrete shadow object representing a local function declaration in kernel
715 /// form. 713 /// form.
716 class KernelFunctionDeclaration extends FunctionDeclaration 714 class KernelFunctionDeclaration extends FunctionDeclaration
717 implements KernelStatement { 715 implements KernelStatement {
716 bool _hasImplicitReturnType = false;
717
718 KernelFunctionDeclaration(VariableDeclaration variable, FunctionNode function) 718 KernelFunctionDeclaration(VariableDeclaration variable, FunctionNode function)
719 : super(variable, function); 719 : super(variable, function);
720 720
721 @override 721 @override
722 void _inferStatement(KernelTypeInferrer inferrer) { 722 void _inferStatement(KernelTypeInferrer inferrer) {
723 inferrer.listener.functionDeclarationEnter(this); 723 inferrer.listener.functionDeclarationEnter(this);
724 for (var parameter in function.positionalParameters) { 724 inferrer.inferLocalFunction(function, null, false, fileOffset,
725 if (parameter.initializer != null) { 725 _hasImplicitReturnType ? null : function.returnType, true);
726 inferrer.inferExpression(parameter.initializer, parameter.type, false); 726 variable.type = function.functionType;
727 }
728 }
729 for (var parameter in function.namedParameters) {
730 if (parameter.initializer != null) {
731 inferrer.inferExpression(parameter.initializer, parameter.type, false);
732 }
733 }
734 if (!inferrer.isTopLevel) {
735 var oldClosureContext = inferrer.closureContext;
736 inferrer.closureContext = new ClosureContext(
737 inferrer, function.asyncMarker, function.returnType);
738 inferrer.inferStatement(function.body);
739 inferrer.closureContext = oldClosureContext;
740 }
741 inferrer.listener.functionDeclarationExit(this); 727 inferrer.listener.functionDeclarationExit(this);
742 } 728 }
729
730 static void setHasImplicitReturnType(
731 KernelFunctionDeclaration declaration, bool hasImplicitReturnType) {
732 declaration._hasImplicitReturnType = hasImplicitReturnType;
733 }
743 } 734 }
744 735
745 /// Concrete shadow object representing a function expression in kernel form. 736 /// Concrete shadow object representing a function expression in kernel form.
746 class KernelFunctionExpression extends FunctionExpression 737 class KernelFunctionExpression extends FunctionExpression
747 implements KernelExpression { 738 implements KernelExpression {
748 KernelFunctionExpression(FunctionNode function) : super(function); 739 KernelFunctionExpression(FunctionNode function) : super(function);
749 740
750 @override 741 @override
751 void _collectDependencies(KernelDependencyCollector collector) { 742 void _collectDependencies(KernelDependencyCollector collector) {
752 for (KernelVariableDeclaration parameter in function.positionalParameters) { 743 for (KernelVariableDeclaration parameter in function.positionalParameters) {
(...skipping 14 matching lines...) Expand all
767 } else { 758 } else {
768 collector.recordNotImmediatelyEvident(fileOffset); 759 collector.recordNotImmediatelyEvident(fileOffset);
769 } 760 }
770 } 761 }
771 762
772 @override 763 @override
773 DartType _inferExpression( 764 DartType _inferExpression(
774 KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { 765 KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
775 typeNeeded = inferrer.listener.functionExpressionEnter(this, typeContext) || 766 typeNeeded = inferrer.listener.functionExpressionEnter(this, typeContext) ||
776 typeNeeded; 767 typeNeeded;
777 768 var inferredType = inferrer.inferLocalFunction(
778 if (!inferrer.isTopLevel) { 769 function, typeContext, typeNeeded, fileOffset, null, false);
779 for (var parameter in function.positionalParameters) {
780 if (parameter.initializer != null) {
781 inferrer.inferExpression(
782 parameter.initializer, parameter.type, false);
783 }
784 }
785 for (var parameter in function.namedParameters) {
786 if (parameter.initializer != null) {
787 inferrer.inferExpression(
788 parameter.initializer, parameter.type, false);
789 }
790 }
791 }
792
793 // Let `<T0, ..., Tn>` be the set of type parameters of the closure (with
794 // `n`=0 if there are no type parameters).
795 List<TypeParameter> typeParameters = function.typeParameters;
796
797 // Let `(P0 x0, ..., Pm xm)` be the set of formal parameters of the closure
798 // (including required, positional optional, and named optional parameters).
799 // If any type `Pi` is missing, denote it as `_`.
800 List<VariableDeclaration> formals = function.positionalParameters.toList()
801 ..addAll(function.namedParameters);
802
803 // Let `B` denote the closure body. If `B` is an expression function body
804 // (`=> e`), treat it as equivalent to a block function body containing a
805 // single `return` statement (`{ return e; }`).
806
807 // Attempt to match `K` as a function type compatible with the closure (that
808 // is, one having n type parameters and a compatible set of formal
809 // parameters). If there is a successful match, let `<S0, ..., Sn>` be the
810 // set of matched type parameters and `(Q0, ..., Qm)` be the set of matched
811 // formal parameter types, and let `N` be the return type.
812 Substitution substitution;
813 List<DartType> formalTypesFromContext =
814 new List<DartType>.filled(formals.length, null);
815 DartType returnContext;
816 if (inferrer.strongMode && typeContext is FunctionType) {
817 for (int i = 0; i < formals.length; i++) {
818 if (i < function.positionalParameters.length) {
819 formalTypesFromContext[i] =
820 getPositionalParameterType(typeContext, i);
821 } else {
822 formalTypesFromContext[i] =
823 getNamedParameterType(typeContext, formals[i].name);
824 }
825 }
826 returnContext = typeContext.returnType;
827
828 // Let `[T/S]` denote the type substitution where each `Si` is replaced wi th
829 // the corresponding `Ti`.
830 var substitutionMap = <TypeParameter, DartType>{};
831 for (int i = 0; i < typeContext.typeParameters.length; i++) {
832 substitutionMap[typeContext.typeParameters[i]] =
833 i < typeParameters.length
834 ? new TypeParameterType(typeParameters[i])
835 : const DynamicType();
836 }
837 substitution = Substitution.fromMap(substitutionMap);
838 } else {
839 // If the match is not successful because `K` is `_`, let all `Si`, all
840 // `Qi`, and `N` all be `_`.
841
842 // If the match is not successful for any other reason, this will result i n
843 // a type error, so the implementation is free to choose the best error
844 // recovery path.
845 substitution = Substitution.empty;
846 }
847
848 // Define `Ri` as follows: if `Pi` is not `_`, let `Ri` be `Pi`.
849 // Otherwise, if `Qi` is not `_`, let `Ri` be the greatest closure of
850 // `Qi[T/S]` with respect to `?`. Otherwise, let `Ri` be `dynamic`.
851 for (int i = 0; i < formals.length; i++) {
852 KernelVariableDeclaration formal = formals[i];
853 if (KernelVariableDeclaration.isImplicitlyTyped(formal)) {
854 DartType inferredType;
855 if (formalTypesFromContext[i] != null) {
856 inferredType = greatestClosure(inferrer.coreTypes,
857 substitution.substituteType(formalTypesFromContext[i]));
858 } else {
859 inferredType = const DynamicType();
860 }
861 inferrer.instrumentation?.record(
862 Uri.parse(inferrer.uri),
863 formal.fileOffset,
864 'type',
865 new InstrumentationValueForType(inferredType));
866 formal.type = inferredType;
867 }
868 }
869
870 // Let `N'` be `N[T/S]`. The [ClosureContext] constructor will adjust
871 // accordingly if the closure is declared with `async`, `async*`, or
872 // `sync*`.
873 if (returnContext != null) {
874 returnContext = substitution.substituteType(returnContext);
875 }
876
877 // Apply type inference to `B` in return context `N’`, with any references
878 // to `xi` in `B` having type `Pi`. This produces `B’`.
879 bool isExpressionFunction = function.body is ReturnStatement;
880 bool needToSetReturnType = isExpressionFunction || inferrer.strongMode;
881 ClosureContext oldClosureContext = inferrer.closureContext;
882 ClosureContext closureContext =
883 new ClosureContext(inferrer, function.asyncMarker, returnContext);
884 inferrer.closureContext = closureContext;
885 inferrer.inferStatement(function.body);
886
887 // If the closure is declared with `async*` or `sync*`, let `M` be the least
888 // upper bound of the types of the `yield` expressions in `B’`, or `void` if
889 // `B’` contains no `yield` expressions. Otherwise, let `M` be the least
890 // upper bound of the types of the `return` expressions in `B’`, or `void`
891 // if `B’` contains no `return` expressions.
892 DartType inferredReturnType;
893 if (needToSetReturnType || typeNeeded) {
894 inferredReturnType =
895 closureContext.inferReturnType(inferrer, isExpressionFunction);
896 }
897
898 // Then the result of inference is `<T0, ..., Tn>(R0 x0, ..., Rn xn) B` with
899 // type `<T0, ..., Tn>(R0, ..., Rn) -> M’` (with some of the `Ri` and `xi`
900 // denoted as optional or named parameters, if appropriate).
901 if (needToSetReturnType) {
902 inferrer.instrumentation?.record(Uri.parse(inferrer.uri), fileOffset,
903 'returnType', new InstrumentationValueForType(inferredReturnType));
904 function.returnType = inferredReturnType;
905 }
906 inferrer.closureContext = oldClosureContext;
907 var inferredType = typeNeeded ? function.functionType : null;
908 inferrer.listener.functionExpressionExit(this, inferredType); 770 inferrer.listener.functionExpressionExit(this, inferredType);
909 return inferredType; 771 return inferredType;
910 } 772 }
911 } 773 }
912 774
913 /// Concrete shadow object representing an if-null expression. 775 /// Concrete shadow object representing an if-null expression.
914 /// 776 ///
915 /// An if-null expression of the form `a ?? b` is represented as the kernel 777 /// An if-null expression of the form `a ?? b` is represented as the kernel
916 /// expression: 778 /// expression:
917 /// 779 ///
(...skipping 1436 matching lines...) Expand 10 before | Expand all | Expand 10 after
2354 } 2216 }
2355 2217
2356 transformChildren(v) { 2218 transformChildren(v) {
2357 return internalError("Internal error: Unsupported operation."); 2219 return internalError("Internal error: Unsupported operation.");
2358 } 2220 }
2359 2221
2360 visitChildren(v) { 2222 visitChildren(v) {
2361 return internalError("Internal error: Unsupported operation."); 2223 return internalError("Internal error: Unsupported operation.");
2362 } 2224 }
2363 } 2225 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698