mirror of
https://github.com/dart-lang/sdk
synced 2024-09-15 22:00:09 +00:00
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:
parent
3859d2eb71
commit
4b9485db73
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) =>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -21,3 +21,5 @@ test2() {
|
|||
Iterable<num> w = y;
|
||||
Iterable<int> z = /*info:ASSIGNMENT_CAST*/ y;
|
||||
}
|
||||
|
||||
main() {}
|
||||
|
|
|
@ -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 {}
|
Loading…
Reference in a new issue