Fix type inference of factory constructor invocations.

Factory constructors are represented as static methods with dynamic
return type; in order to type infer them properly we must use the type
of the enclosing class as the return type (with type parameters
substituted appropriately).

R=scheglov@google.com

Review-Url: https://codereview.chromium.org/2900553003 .
This commit is contained in:
Paul Berry 2017-05-22 11:18:29 -07:00
parent 3859d2eb71
commit 4b9485db73
8 changed files with 145 additions and 5 deletions

View file

@ -186,6 +186,10 @@ class KernelAstFactory implements AstFactory<VariableDeclaration> {
@override
StaticInvocation staticInvocation(Procedure target, Arguments arguments,
{bool isConst: false}) {
if (target.kind == ProcedureKind.Factory) {
return new KernelFactoryConstructorInvocation(target, arguments,
isConst: isConst);
}
return new KernelStaticInvocation(target, arguments, isConst: isConst);
}

View file

@ -267,6 +267,42 @@ class KernelExpressionStatement extends ExpressionStatement
}
}
/// Shadow object for [StaticInvocation] when the procedure being invoked is a
/// factory constructor.
class KernelFactoryConstructorInvocation extends StaticInvocation
implements KernelExpression {
KernelFactoryConstructorInvocation(Procedure target, Arguments arguments,
{bool isConst: false})
: super(target, arguments, isConst: isConst);
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
typeNeeded =
inferrer.listener.constructorInvocationEnter(this, typeContext) ||
typeNeeded;
var returnType = target.enclosingClass.thisType;
if (target.enclosingClass.typeParameters.isNotEmpty) {
// target.enclosingClass.typeParameters is not the same as
// target.function.functionType.typeParameters, so we have to substitute.
// TODO(paulberrry): it would be easier if we could just use
// target.function.functionType.returnType, but that's `dynamic` for
// factory constructors. Investigate whether this can be changed.
returnType = Substitution
.fromPairs(
target.enclosingClass.typeParameters,
target.function.functionType.typeParameters
.map((p) => new TypeParameterType(p))
.toList())
.substituteType(returnType);
}
var inferredType = inferrer.inferInvocation(typeContext, typeNeeded,
fileOffset, target.function.functionType, returnType, arguments);
inferrer.listener.constructorInvocationExit(this, inferredType);
return inferredType;
}
}
/// Concrete shadow object representing a field in kernel form.
class KernelField extends Field {
bool _implicitlyTyped = true;

View file

@ -87,11 +87,11 @@ class TypeInferenceListener
debugExpressionExit("conditionalExpression", expression, inferredType);
bool constructorInvocationEnter(
ConstructorInvocation expression, DartType typeContext) =>
InvocationExpression expression, DartType typeContext) =>
debugExpressionEnter("constructorInvocation", expression, typeContext);
void constructorInvocationExit(
ConstructorInvocation expression, DartType inferredType) =>
InvocationExpression expression, DartType inferredType) =>
debugExpressionExit("constructorInvocation", expression, inferredType);
bool doubleLiteralEnter(DoubleLiteral expression, DartType typeContext) =>

View file

@ -56,13 +56,10 @@ uninitialized_fields: Fail
unused_methods: Fail
void-methods: Fail
inference/block_bodied_lambdas_async_all_returns_are_futures: Fail
inference/block_bodied_lambdas_async_all_returns_are_values: Fail
inference/block_bodied_lambdas_async_mix_of_values_and_futures: Fail
inference/block_bodied_lambdas_async_star: Fail
inference/block_bodied_lambdas_downwards_incompatible_with_upwards_inference: Fail
inference/block_bodied_lambdas_infer_bottom_sync: Fail
inference/block_bodied_lambdas_lub: Fail
inference/block_bodied_lambdas_sync_star: Fail
inference/circular_reference_via_closures: Fail
inference/circular_reference_via_closures_initializer_types: Fail

View file

@ -0,0 +1,41 @@
library test;
import self as self;
import "dart:async" as asy;
import "dart:core" as core;
import "dart:math" as math;
static method main() → dynamic {
() → asy::Future<core::num> f = () → asy::Future<core::num> /* originally async */ {
final asy::Completer<asy::FutureOr<core::num>> :completer = asy::Completer::sync<asy::FutureOr<core::num>>();
asy::FutureOr<core::num> :return_value;
dynamic :async_op_then;
dynamic :async_op_error;
dynamic :await_jump_var = 0;
dynamic :await_ctx_var;
function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
try {
#L1:
{
if(math::Random::•().{math::Random::nextBool}()) {
:return_value = asy::Future::value<core::int>(1);
break #L1;
}
else {
:return_value = asy::Future::value<core::double>(2.0);
break #L1;
}
}
:completer.complete(:return_value);
return;
}
on dynamic catch(dynamic :exception, dynamic :stack_trace) {
:completer.completeError(:exception, :stack_trace);
}
:async_op_then = asy::_asyncThenWrapperHelper(:async_op);
:async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
asy::Future::microtask<dynamic>(:async_op);
return :completer.future;
};
asy::Future<core::num> g = f.call();
asy::Future<core::int> h = f.call();
}

View file

@ -0,0 +1,41 @@
library test;
import self as self;
import "dart:async" as asy;
import "dart:core" as core;
import "dart:math" as math;
static method main() → dynamic {
() → asy::Future<core::num> f = () → asy::Future<core::num> /* originally async */ {
final asy::Completer<asy::FutureOr<core::num>> :completer = asy::Completer::sync<asy::FutureOr<core::num>>();
asy::FutureOr<core::num> :return_value;
dynamic :async_op_then;
dynamic :async_op_error;
dynamic :await_jump_var = 0;
dynamic :await_ctx_var;
function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
try {
#L1:
{
if(math::Random::•().{math::Random::nextBool}()) {
:return_value = 1;
break #L1;
}
else {
:return_value = 2.0;
break #L1;
}
}
:completer.complete(:return_value);
return;
}
on dynamic catch(dynamic :exception, dynamic :stack_trace) {
:completer.completeError(:exception, :stack_trace);
}
:async_op_then = asy::_asyncThenWrapperHelper(:async_op);
:async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
asy::Future::microtask<dynamic>(:async_op);
return :completer.future;
};
asy::Future<core::num> g = f.call();
asy::Future<core::int> h = f.call();
}

View file

@ -21,3 +21,5 @@ test2() {
Iterable<num> w = y;
Iterable<int> z = /*info:ASSIGNMENT_CAST*/ y;
}
main() {}

View file

@ -0,0 +1,19 @@
library test;
import self as self;
import "dart:core" as core;
import "dart:math" as math;
static method test2() → dynamic {
core::List<core::num> o;
core::Iterable<core::num> y = o.{core::Iterable::map}<core::num>((core::num x) → core::num {
if(math::Random::•().{math::Random::nextBool}()) {
return x.{core::num::toInt}().{core::num::+}(1);
}
else {
return x.{core::num::toDouble}();
}
});
core::Iterable<core::num> w = y;
core::Iterable<core::int> z = y;
}
static method main() → dynamic {}