Implement AstBuilder integration for method/function invocations.

This is tricky because there isn't a clean correspondence between
analyzer's AST (which reflects user syntax) and kernel representation.
For example:

  f(args);

could be a call to a static function f, a method f in the current
class, or an invocation of a function-typed object stored in a
variable called f.  ResolutionStorer operates on the kernel
representation, so it sees the difference, but ResolutionApplier
operates on the analyzer AST, so it does not.  So we go to some extra
work to make sure a type is stored for f, regardless of whether f is a
function, method, or a variable.  This requires some re-ordering logic
in ResolutionStorer, since the inferred type of f is not known until
after the args have been visited.

The implementation is not complete; currently the type that we store
in the first two cases is `dynamic` because the type inference
mechanism doesn't preserve enough information to allow us to determine
the correct type; this will be remidied in a future CL.

A similar situation occurs for:

  x.m(args);

which could be a call to a method m in the object x, or an invocation
of a function-typed object returned by the getter m in the object x.

R=scheglov@google.com

Review-Url: https://codereview.chromium.org/2984013002 .
This commit is contained in:
Paul Berry 2017-07-21 10:47:22 -07:00
parent ebd3ed5ade
commit c057f091c2
6 changed files with 88 additions and 16 deletions

View file

@ -88,6 +88,7 @@ class AnalyzerDietListener extends DietListener {
// the body builder to do type inference, and then copy the inferred types
// over to the analyzer AST.
parserCallback();
resolutionStorer.finished();
// The inferred types and the body builder are no longer needed.
_bodyBuilder = null;
_kernelTypes = null;

View file

@ -38,10 +38,11 @@ class ResolutionApplier extends GeneralizingAstVisitor {
@override
void visitMethodInvocation(MethodInvocation node) {
node.target?.accept(this);
node.methodName.staticType = _getTypeFor(node.methodName);
// TODO(paulberry): store resolution of node.methodName.
// TODO(paulberry): store resolution of node.typeArguments.
node.argumentList.accept(this);
node.staticType = _getTypeFor(node.methodName);
node.staticType = _getTypeFor(node.argumentList);
}
@override

View file

@ -17,13 +17,13 @@ class InstrumentedResolutionStorer extends ResolutionStorer {
: super(types);
@override
void _recordType(DartType type, int offset) {
int _recordType(DartType type, int offset) {
if (_debug) {
print('Recording type $type for offset $offset');
}
assert(_types.length == _typeOffsets.length);
_typeOffsets.add(offset);
super._recordType(type, offset);
return super._recordType(type, offset);
}
}
@ -32,8 +32,16 @@ class InstrumentedResolutionStorer extends ResolutionStorer {
class ResolutionStorer extends TypeInferenceListener {
final List<DartType> _types;
/// Indices into [_types] which need to be filled in later.
final _deferredTypeSlots = <int>[];
ResolutionStorer(this._types);
/// Verifies that all deferred work has been completed.
void finished() {
assert(_deferredTypeSlots.isEmpty);
}
@override
bool genericExpressionEnter(
String expressionType, Expression expression, DartType typeContext) {
@ -48,13 +56,76 @@ class ResolutionStorer extends TypeInferenceListener {
super.genericExpressionExit(expressionType, expression, inferredType);
}
@override
void methodInvocationBeforeArgs(Expression expression, bool isImplicitCall) {
if (!isImplicitCall) {
// We are visiting a method invocation like: a.f(args). We have visited a
// but we haven't visited the args yet.
//
// The analyzer AST will expect a type for f at this point. (It can't
// wait until later, because for all it knows, a.f might be a property
// access, in which case the appropriate time for the type is now). But
// the type isn't known yet (because it may depend on type inference based
// on arguments).
//
// So we add a `null` to our list of types; we'll update it with the
// actual type later.
_deferredTypeSlots.add(_recordType(null, expression.fileOffset));
}
super.methodInvocationBeforeArgs(expression, isImplicitCall);
}
@override
void methodInvocationExit(Expression expression, Arguments arguments,
bool isImplicitCall, DartType inferredType) {
if (!isImplicitCall) {
// TODO(paulberry): get the actual callee function type from the inference
// engine
var calleeType = const DynamicType();
_types[_deferredTypeSlots.removeLast()] = calleeType;
}
_recordType(inferredType, arguments.fileOffset);
super.genericExpressionExit("methodInvocation", expression, inferredType);
}
@override
bool staticInvocationEnter(
StaticInvocation expression, DartType typeContext) {
// We are visiting a static invocation like: f(args), and we haven't visited
// args yet.
//
// The analyzer AST will expect a type for f at this point. (It can't wait
// until later, because for all it knows, f is a method on `this`, and
// methods need a type for f at this point--see comments in
// [methodInvocationBeforeArgs]). But the type isn't known yet (because it
// may depend on type inference based on arguments).
//
// So we add a `null` to our list of types; we'll update it with the actual
// type later.
_deferredTypeSlots.add(_recordType(null, expression.fileOffset));
return super.staticInvocationEnter(expression, typeContext);
}
@override
void staticInvocationExit(
StaticInvocation expression, DartType inferredType) {
// TODO(paulberry): get the actual callee function type from the inference
// engine
var calleeType = const DynamicType();
_types[_deferredTypeSlots.removeLast()] = calleeType;
_recordType(inferredType, expression.arguments.fileOffset);
super.genericExpressionExit("staticInvocation", expression, inferredType);
}
@override
void variableDeclarationEnter(VariableDeclaration statement) {
_recordType(statement.type, statement.fileOffset);
super.variableDeclarationEnter(statement);
}
void _recordType(DartType type, int offset) {
int _recordType(DartType type, int offset) {
int slot = _types.length;
_types.add(type);
return slot;
}
}

View file

@ -261,7 +261,10 @@ class TypeInferenceListener
bool methodInvocationEnter(Expression expression, DartType typeContext) =>
genericExpressionEnter("methodInvocation", expression, typeContext);
void methodInvocationExit(Expression expression, DartType inferredType) =>
void methodInvocationBeforeArgs(Expression expression, bool isImplicitCall) {}
void methodInvocationExit(Expression expression, Arguments arguments,
bool isImplicitCall, DartType inferredType) =>
genericExpressionExit("methodInvocation", expression, inferredType);
bool notEnter(Not expression, DartType typeContext) =>

View file

@ -847,6 +847,7 @@ abstract class TypeInferrerImpl extends TypeInferrer {
listener.methodInvocationEnter(expression, typeContext) || typeNeeded;
// First infer the receiver so we can look up the method that was invoked.
var receiverType = inferExpression(receiver, null, true);
listener.methodInvocationBeforeArgs(expression, isImplicitCall);
if (strongMode) {
receiverVariable?.type = receiverType;
}
@ -882,7 +883,8 @@ abstract class TypeInferrerImpl extends TypeInferrer {
isOverloadedArithmeticOperator: isOverloadedArithmeticOperator,
receiverType: receiverType,
forceArgumentInference: forceArgumentInference);
listener.methodInvocationExit(expression, inferredType);
listener.methodInvocationExit(
expression, arguments, isImplicitCall, inferredType);
return inferredType;
}

View file

@ -6,7 +6,6 @@
# generating analyzer ASTs and connecting up type inference information.
DeltaBlue: Crash
accessors: Crash
argument: Crash
arithmetic: Crash
bad_store: Crash
call: Crash
@ -22,17 +21,12 @@ external: Crash
fallthrough: Crash
fibonacci: Crash
for_in_scope: Crash
function_in_field: Crash
function_type_is_check: Crash
functions: Crash
implicit_scope_test: Crash
implicit_this: Crash
inference/abstract_class_instantiation: Crash
inference/assign_local: Crash
inference/block_bodied_lambdas_async_all_returns_are_futures: Crash
inference/block_bodied_lambdas_async_all_returns_are_values: Crash
inference/block_bodied_lambdas_async_mix_of_values_and_futures: Crash
inference/block_bodied_lambdas_async_star: Crash
inference/block_bodied_lambdas_basic: Crash
inference/block_bodied_lambdas_basic_void: Crash
inference/block_bodied_lambdas_downwards_incompatible_with_upwards_inference: Crash
@ -44,7 +38,6 @@ inference/block_bodied_lambdas_lub: Crash
inference/block_bodied_lambdas_nested_lambdas: Crash
inference/block_bodied_lambdas_no_return: Crash
inference/block_bodied_lambdas_returns: Crash
inference/block_bodied_lambdas_sync_star: Crash
inference/block_bodied_lambdas_void_context: Crash
inference/call_corner_cases: Crash
inference/conditional_lub: Crash
@ -212,6 +205,8 @@ inference/infer_local_function_return_type: Crash
inference/infer_method_missing_params: Crash
inference/infer_parameter_type_setter_from_field: Crash
inference/infer_parameter_type_setter_from_setter: Fail
inference/infer_prefix_expression: Crash
inference/infer_prefix_expression_custom: Crash
inference/infer_rethrow: Crash
inference/infer_return_of_statement_lambda: Crash
inference/infer_setter_cross_to_getter: Fail
@ -251,6 +246,8 @@ inference/inferred_type_custom_binary_op: Crash
inference/inferred_type_custom_binary_op_via_interface: Crash
inference/inferred_type_custom_index_op: Crash
inference/inferred_type_custom_index_op_via_interface: Crash
inference/inferred_type_custom_unary_op: Crash
inference/inferred_type_custom_unary_op_via_interface: Crash
inference/inferred_type_extract_method_tear_off: Crash
inference/inferred_type_extract_method_tear_off_via_interface: Crash
inference/inferred_type_is_enum: Crash
@ -304,7 +301,6 @@ inference/top_level_return_and_yield: Crash
inference/try_catch: Crash
inference/try_catch_finally: Crash
inference/try_catch_promotion: Crash
inference/try_finally: Crash
inference/type_cast: Crash
inference/type_promotion_ignores_local_functions: Crash
inference/type_promotion_not_and_not: Crash
@ -347,7 +343,6 @@ inference_new/property_get_toplevel: Crash
inference_new/strongly_connected_component: Crash
inference_new/unsafe_block_closure_inference_function_call_explicit_dynamic_param_via_expr2: Crash
inference_new/unsafe_block_closure_inference_function_call_explicit_type_param_via_expr2: Crash
inference_new/void_return_type_subtypes_dynamic: Crash
invocations: Crash
micro: Crash
named_parameters: Crash
@ -363,7 +358,6 @@ rasta/breaking_bad: Crash
rasta/cascades: Crash
rasta/class_hierarchy: Crash
rasta/class_member: Crash
rasta/constant_get_and_invoke: Crash
rasta/deferred_load: Crash
rasta/duplicated_mixin: Crash
rasta/for_loop: Crash