mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 00:39:49 +00:00
Implement type inference for if-null expressions (a ?? b).
R=sigmund@google.com Review-Url: https://codereview.chromium.org/2944903002 .
This commit is contained in:
parent
22042b691f
commit
fdf77ba36f
|
@ -864,7 +864,7 @@ class BodyBuilder extends ScopeListener<JumpTarget> implements BuilderHelper {
|
|||
Expression b = popForValue();
|
||||
Expression a = popForValue();
|
||||
VariableDeclaration variable = new VariableDeclaration.forValue(a);
|
||||
push(makeLet(
|
||||
push(new KernelIfNullExpression(
|
||||
variable,
|
||||
new KernelConditionalExpression(
|
||||
buildIsNull(new VariableGet(variable), offsetForToken(token)),
|
||||
|
|
|
@ -935,6 +935,56 @@ class KernelFunctionExpression extends FunctionExpression
|
|||
}
|
||||
}
|
||||
|
||||
/// Concrete shadow object representing an if-null expression.
|
||||
///
|
||||
/// An if-null expression of the form `a ?? b` is represented as the kernel
|
||||
/// expression:
|
||||
///
|
||||
/// let v = a in v == null ? b : v
|
||||
class KernelIfNullExpression extends Let implements KernelExpression {
|
||||
KernelIfNullExpression(VariableDeclaration variable, Expression body)
|
||||
: super(variable, body);
|
||||
|
||||
@override
|
||||
ConditionalExpression get body => super.body;
|
||||
|
||||
/// Returns the expression to the left of `??`.
|
||||
Expression get _lhs => variable.initializer;
|
||||
|
||||
/// Returns the expression to the right of `??`.
|
||||
Expression get _rhs => body.then;
|
||||
|
||||
@override
|
||||
void _collectDependencies(KernelDependencyCollector collector) {
|
||||
// If-null expressions are not immediately evident expressions.
|
||||
collector.recordNotImmediatelyEvident(fileOffset);
|
||||
}
|
||||
|
||||
@override
|
||||
DartType _inferExpression(
|
||||
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
|
||||
typeNeeded = inferrer.listener.ifNullEnter(this, typeContext) || typeNeeded;
|
||||
// To infer `e0 ?? e1` in context K:
|
||||
// - Infer e0 in context K to get T0
|
||||
var lhsType = inferrer.inferExpression(_lhs, typeContext, true);
|
||||
variable.type = lhsType;
|
||||
// - Let J = T0 if K is `_` else K.
|
||||
var rhsContext = typeContext ?? lhsType;
|
||||
// - Infer e1 in context J to get T1
|
||||
var rhsType =
|
||||
inferrer.inferExpression(_rhs, rhsContext, typeContext == null);
|
||||
// - Let T = greatest closure of K with respect to `?` if K is not `_`, else
|
||||
// UP(t0, t1)
|
||||
// - Then the inferred type is T.
|
||||
var inferredType = typeContext == null
|
||||
? inferrer.typeSchemaEnvironment.getLeastUpperBound(lhsType, rhsType)
|
||||
: greatestClosure(inferrer.coreTypes, typeContext);
|
||||
body.staticType = inferredType;
|
||||
inferrer.listener.ifNullExit(this, inferredType);
|
||||
return inferredType;
|
||||
}
|
||||
}
|
||||
|
||||
/// Concrete shadow object representing an if statement in kernel form.
|
||||
class KernelIfStatement extends IfStatement implements KernelStatement {
|
||||
KernelIfStatement(Expression condition, Statement then, Statement otherwise)
|
||||
|
|
|
@ -163,6 +163,12 @@ class TypeInferenceListener
|
|||
FunctionExpression expression, DartType inferredType) =>
|
||||
debugExpressionExit("functionExpression", expression, inferredType);
|
||||
|
||||
bool ifNullEnter(Expression expression, DartType typeContext) =>
|
||||
debugExpressionEnter('ifNull', expression, typeContext);
|
||||
|
||||
void ifNullExit(Expression expression, DartType inferredType) =>
|
||||
debugExpressionExit('ifNull', expression, inferredType);
|
||||
|
||||
void ifStatementEnter(IfStatement statement) =>
|
||||
debugStatementEnter('ifStatement', statement);
|
||||
|
||||
|
|
|
@ -67,8 +67,6 @@ inference/infer_types_on_loop_indices_for_loop_with_inference: Fail
|
|||
inference/lambda_does_not_have_propagated_type_hint: Fail
|
||||
inference/list_literals_can_infer_null_top_level: Fail
|
||||
inference/map_literals_can_infer_null_top_level: Fail
|
||||
inference/null_coalescing_operator: Fail
|
||||
inference/null_coalescing_operator_2: Fail
|
||||
inference/property_set: Fail
|
||||
inference/property_set_bad_setter: Crash
|
||||
inference/reference_to_typedef: Fail
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
library test;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
static method main() → dynamic {
|
||||
core::List<core::int> x;
|
||||
core::List<core::int> y = let final core::List<core::int> #t1 = x in #t1.==(null) ? <core::int>[] : #t1;
|
||||
core::List<core::int> z = y;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
library test;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
static method main() → dynamic {
|
||||
core::List<core::int> x;
|
||||
core::List<core::num> y = let final core::List<core::int> #t1 = x in #t1.==(null) ? <core::num>[] : #t1;
|
||||
}
|
Loading…
Reference in a new issue