Chromium Code Reviews| Index: pkg/compiler/lib/src/cps_ir/type_propagation.dart |
| diff --git a/pkg/compiler/lib/src/cps_ir/type_propagation.dart b/pkg/compiler/lib/src/cps_ir/type_propagation.dart |
| index b9db352a20ab40db6ae8c897b9f680390835d5bb..ebae371eaf2b5c2d4785b660f5a269e8c420f612 100644 |
| --- a/pkg/compiler/lib/src/cps_ir/type_propagation.dart |
| +++ b/pkg/compiler/lib/src/cps_ir/type_propagation.dart |
| @@ -25,6 +25,7 @@ abstract class TypeSystem<T> { |
| T get functionType; |
| T get boolType; |
| T get intType; |
| + T get numType; |
| T get stringType; |
| T get listType; |
| T get mapType; |
| @@ -44,6 +45,8 @@ abstract class TypeSystem<T> { |
| bool isDefinitelyBool(T type); |
| bool isDefinitelyNotNull(T type); |
| + |
| + AbstractBool isSubtypeOf(T value, types.DartType type, {bool allowNull}); |
| } |
| class UnitTypeSystem implements TypeSystem<String> { |
| @@ -53,6 +56,7 @@ class UnitTypeSystem implements TypeSystem<String> { |
| get dynamicType => UNIT; |
| get functionType => UNIT; |
| get intType => UNIT; |
| + get numType => UNIT; |
| get listType => UNIT; |
| get mapType => UNIT; |
| get stringType => UNIT; |
| @@ -67,15 +71,27 @@ class UnitTypeSystem implements TypeSystem<String> { |
| exact(_) => UNIT; |
| getTypeOf(_) => UNIT; |
| + bool areDisjoint(String leftType, String rightType) { |
| + return false; |
| + } |
| + |
| bool isDefinitelyBool(_) => false; |
| bool isDefinitelyNotNull(_) => false; |
| - bool areDisjoint(String leftType, String rightType) { |
| - return false; |
| + AbstractBool isSubtypeOf(value, type, {bool allowNull}) { |
| + return AbstractBool.Maybe; |
| } |
| } |
| +enum AbstractBool { |
| + True, False, Maybe, Nothing |
| +} |
| + |
| +AbstractBool abstractBool(bool b) { |
| + return b ? AbstractBool.True : AbstractBool.False; |
| +} |
| + |
| class TypeMaskSystem implements TypeSystem<TypeMask> { |
| final TypesTask inferrer; |
| final ClassWorld classWorld; |
| @@ -85,6 +101,7 @@ class TypeMaskSystem implements TypeSystem<TypeMask> { |
| TypeMask get functionType => inferrer.functionType; |
| TypeMask get boolType => inferrer.boolType; |
| TypeMask get intType => inferrer.intType; |
| + TypeMask get numType => inferrer.numType; |
| TypeMask get stringType => inferrer.stringType; |
| TypeMask get listType => inferrer.listType; |
| TypeMask get mapType => inferrer.mapType; |
| @@ -140,6 +157,149 @@ class TypeMaskSystem implements TypeSystem<TypeMask> { |
| TypeMask intersection = leftType.intersection(rightType, classWorld); |
| return intersection.isEmpty && !intersection.isNullable; |
| } |
| + |
| + AbstractBool isSubtypeOf(TypeMask value, |
| + types.DartType type, |
| + {bool allowNull}) { |
| + assert(allowNull != null); |
| + if (type is types.DynamicType) { |
| + if (!allowNull && value.isNullable) return AbstractBool.Maybe; |
| + return AbstractBool.True; |
| + } |
| + if (type is types.InterfaceType) { |
| + TypeMask typeAsMask = allowNull |
| + ? new TypeMask.subtype(type.element, classWorld) |
| + : new TypeMask.nonNullSubtype(type.element, classWorld); |
| + if (areDisjoint(value, typeAsMask)) { |
| + // Disprove the subtype relation based on the class alone. |
| + return AbstractBool.False; |
| + } |
| + if (!type.treatAsRaw) { |
| + // If there are type arguments, we cannot prove the subtype relation, |
| + // because the type arguments are unknown on both the value and type. |
| + return AbstractBool.Maybe; |
| + } |
| + if (typeAsMask.containsMask(value, classWorld)) { |
| + // All possible values are contained in the set of allowed values. |
| + // Note that we exploit the fact that [typeAsMask] is an exact |
| + // representation of [type], not an approximation. |
| + return AbstractBool.True; |
| + } |
| + // The value is neither contained in the type, nor disjoint from the type. |
| + return AbstractBool.Maybe; |
| + } |
| + // TODO(asgerf): Support function types, and what else might be missing. |
| + return AbstractBool.Maybe; |
| + } |
| +} |
| + |
| +class ConstantPropagationLattice<T> { |
| + final TypeSystem<T> typeSystem; |
| + final ConstantSystem constantSystem; |
| + final types.DartTypes dartTypes; |
| + |
| + ConstantPropagationLattice(this.typeSystem, |
| + this.constantSystem, |
| + this.dartTypes); |
| + |
| + final _AbstractValue<T> nothing = new _AbstractValue<T>.nothing(); |
| + |
| + _AbstractValue<T> constant(ConstantValue value) { |
| + return new _AbstractValue<T>.constantValue(value, |
| + typeSystem.getTypeOf(value)); |
| + } |
| + |
| + _AbstractValue<T> nonConstant(T type) { |
| + return new _AbstractValue<T>.nonConstant(type); |
| + } |
| + |
| + _AbstractValue<T> get anything { |
| + return new _AbstractValue<T>.nonConstant(typeSystem.dynamicType); |
| + } |
| + |
| + /// Returns whether the given [value] is an instance of [type]. |
| + /// |
| + /// Since [value] and [type] are not always known, [AbstractBool.Maybe] is |
| + /// returned if the answer is not known. |
| + /// |
| + /// [AbstractBool.Nothing] is returned if [value] is nothing. |
| + /// |
| + /// If [allowNull] is true, `null` is considered to an instance of anything, |
| + /// otherwise it is only considered an instance of [Object], [dynamic], and |
| + /// [Null]. |
| + AbstractBool isSubtypeOf(_AbstractValue<T> value, |
| + types.DartType type, |
| + {bool allowNull}) { |
| + assert(allowNull != null); |
| + if (value.isNothing) { |
| + return AbstractBool.Nothing; |
| + } |
| + if (value.isConstant) { |
| + if (value.constant.isNull) { |
| + if (allowNull || |
| + type.isObject || |
| + type.isDynamic || |
| + type == dartTypes.coreTypes.nullType) { |
| + return AbstractBool.True; |
| + } |
| + if (type is types.TypeVariableType) { |
| + return AbstractBool.Maybe; |
| + } |
| + return AbstractBool.False; |
| + } |
| + types.DartType valueType = value.constant.getType(dartTypes.coreTypes); |
| + if (constantSystem.isSubtype(dartTypes, valueType, type)) { |
| + return AbstractBool.True; |
| + } |
| + if (!dartTypes.isPotentialSubtype(valueType, type)) { |
| + return AbstractBool.False; |
| + } |
| + return AbstractBool.Maybe; |
| + } |
| + return typeSystem.isSubtypeOf(value.type, type, allowNull: allowNull); |
| + } |
| + |
| + /// Returns the possible results of applying [operator] to [value], |
| + /// assuming the operation does not throw. |
| + /// |
| + /// Because we do not explicitly track thrown values, we currently use the |
| + /// convention that constant values are returned from this method only |
| + /// if the operation is known not to throw. |
| + _AbstractValue<T> unaryOp(UnaryOperator operator, |
| + _AbstractValue<T> value) { |
| + // TODO(asgerf): Also return information about whether this can throw? |
| + if (value.isNothing) { |
| + return nothing; |
| + } |
| + if (value.isConstant) { |
| + UnaryOperation operation = constantSystem.lookupUnary(operator); |
| + ConstantValue result = operation.fold(value.constant); |
| + if (result == null) return anything; |
| + return constant(result); |
| + } |
| + return anything; // TODO(asgerf): Look up type. |
| + } |
| + |
| + /// Returns the possible results of applying [operator] to [left], [right], |
| + /// assuming the operation does not throw. |
| + /// |
| + /// Because we do not explicitly track thrown values, we currently use the |
| + /// convention that constant values are returned from this method only |
| + /// if the operation is known not to throw. |
| + _AbstractValue<T> binaryOp(BinaryOperator operator, |
| + _AbstractValue<T> left, |
| + _AbstractValue<T> right) { |
| + if (left.isNothing || right.isNothing) { |
| + return nothing; |
| + } |
| + if (left.isConstant && right.isConstant) { |
| + BinaryOperation operation = constantSystem.lookupBinary(operator); |
| + ConstantValue result = operation.fold(left.constant, right.constant); |
| + if (result == null) return anything; |
| + return constant(result); |
| + } |
| + return anything; // TODO(asgerf): Look up type. |
| + } |
| } |
| /** |
| @@ -162,35 +322,41 @@ class TypePropagator<T> extends Pass { |
| final ConstantSystem _constantSystem; |
| final TypeSystem _typeSystem; |
| final dart2js.InternalErrorFunction _internalError; |
| - final Map<Node, _AbstractValue> _types; |
| + final Map<Primitive, _AbstractValue> _types = <Primitive, _AbstractValue>{}; |
| TypePropagator(this._dartTypes, |
| this._constantSystem, |
| this._typeSystem, |
| - this._internalError) |
| - : _types = <Node, _AbstractValue>{}; |
| + this._internalError); |
| @override |
| void rewrite(FunctionDefinition root) { |
| // Set all parent pointers. |
| new ParentVisitor().visit(root); |
| + ConstantPropagationLattice<T> lattice = new ConstantPropagationLattice<T>( |
| + _typeSystem, _constantSystem, _dartTypes); |
| + Map<Expression, ConstantValue> replacements = <Expression, ConstantValue>{}; |
| + |
| // Analyze. In this phase, the entire term is analyzed for reachability |
| // and the abstract value of each expression. |
| _TypePropagationVisitor<T> analyzer = new _TypePropagationVisitor<T>( |
| - _constantSystem, |
| - _typeSystem, |
| + lattice, |
| _types, |
| - _internalError, |
| - _dartTypes); |
| + replacements, |
| + _internalError); |
| analyzer.analyze(root); |
| // Transform. Uses the data acquired in the previous analysis phase to |
| // replace branches with fixed targets and side-effect-free expressions |
| - // with constant results. |
| + // with constant results or existing values that are in scope. |
| _TransformingVisitor<T> transformer = new _TransformingVisitor<T>( |
| - analyzer.reachableNodes, analyzer.values, _internalError, _typeSystem); |
| + lattice, |
| + analyzer.reachableNodes, |
| + analyzer.values, |
| + replacements, |
| + _internalError); |
| transformer.transform(root); |
| } |
| @@ -204,49 +370,52 @@ class TypePropagator<T> extends Pass { |
| class _TransformingVisitor<T> extends RecursiveVisitor { |
| final Set<Node> reachable; |
| final Map<Node, _AbstractValue> values; |
| - final TypeSystem<T> typeSystem; |
| + final Map<Expression, ConstantValue> replacements; |
| + final ConstantPropagationLattice<T> valueLattice; |
| + |
| + TypeSystem<T> get typeSystem => valueLattice.typeSystem; |
| final dart2js.InternalErrorFunction internalError; |
| - _TransformingVisitor(this.reachable, |
| + _TransformingVisitor(this.valueLattice, |
| + this.reachable, |
| this.values, |
| - this.internalError, |
| - this.typeSystem); |
| + this.replacements, |
| + this.internalError); |
| void transform(FunctionDefinition root) { |
| visit(root); |
| } |
| + Constant makeConstantPrimitive(ConstantValue constant) { |
| + ConstantExpression constExp = |
| + const ConstantExpressionCreator().convert(constant); |
| + Constant primitive = new Constant(constExp, constant); |
| + values[primitive] = new _AbstractValue.constantValue(constant, |
| + typeSystem.getTypeOf(constant)); |
| + return primitive; |
| + } |
| + |
| /// Given an expression with a known constant result and a continuation, |
| /// replaces the expression by a new LetPrim / InvokeContinuation construct. |
| /// `unlink` is a closure responsible for unlinking all removed references. |
| LetPrim constifyExpression(Expression node, |
| Continuation continuation, |
| void unlink()) { |
| - _AbstractValue value = values[node]; |
| - if (value == null || !value.isConstant) { |
| - return null; |
| - } |
| + ConstantValue constant = replacements[node]; |
| + if (constant == null) return null; |
| assert(continuation.parameters.length == 1); |
| + InteriorNode parent = node.parent; |
| + Constant primitive = makeConstantPrimitive(constant); |
| + LetPrim letPrim = new LetPrim(primitive); |
| - // Set up the replacement structure. |
| - PrimitiveConstantValue primitiveConstant = value.constant; |
| - ConstantExpression constExp = |
| - const ConstantExpressionCreator().convert(primitiveConstant); |
| - Constant constant = new Constant(constExp, primitiveConstant); |
| - LetPrim letPrim = new LetPrim(constant); |
| InvokeContinuation invoke = |
| - new InvokeContinuation(continuation, <Primitive>[constant]); |
| - |
| - invoke.parent = constant.parent = letPrim; |
| + new InvokeContinuation(continuation, <Primitive>[primitive]); |
| + parent.body = letPrim; |
| letPrim.body = invoke; |
| - |
| - // Replace the method invocation. |
| - |
| - InteriorNode parent = node.parent; |
| + invoke.parent = letPrim; |
| letPrim.parent = parent; |
| - parent.body = letPrim; |
| unlink(); |
| @@ -334,20 +503,34 @@ class _TransformingVisitor<T> extends RecursiveVisitor { |
| } |
| } |
| - // See [visitInvokeMethod]. |
| - void visitTypeOperator(TypeOperator node) { |
| + void visitTypeCast(TypeCast node) { |
| Continuation cont = node.continuation.definition; |
| - LetPrim letPrim = constifyExpression(node, cont, () { |
| - node.value.unlink(); |
| - node.typeArguments.forEach((Reference ref) => ref.unlink()); |
| - node.continuation.unlink(); |
| - }); |
| + InteriorNode parent = node.parent; |
| - if (letPrim == null) { |
| - super.visitTypeOperator(node); |
| - } else { |
| - visitLetPrim(letPrim); |
| + _AbstractValue<T> value = getValue(node.value.definition); |
| + switch (valueLattice.isSubtypeOf(value, node.type, allowNull: true)) { |
| + case AbstractBool.Maybe: |
| + case AbstractBool.Nothing: |
| + break; |
| + |
| + case AbstractBool.True: |
| + // Cast always succeeds, replace it with InvokeContinuation. |
| + InvokeContinuation invoke = |
| + new InvokeContinuation.fromCall(node.continuation, node.value); |
| + parent.body = invoke; |
| + invoke.parent = parent; |
| + super.visitInvokeContinuation(invoke); |
| + return; |
| + |
| + case AbstractBool.False: |
| + // Cast always fails, remove unreachable continuation body. |
| + assert(!reachable.contains(cont)); |
| + RemovalVisitor.remove(cont.body); |
| + cont.body = new Unreachable()..parent = cont; |
|
karlklose
2015/06/08 09:46:53
How about making parent a constructor parameter?
asgerf
2015/06/08 10:46:16
For all CPS nodes? That would be great burden on t
|
| + break; |
| } |
| + |
| + super.visitTypeCast(node); |
| } |
| _AbstractValue<T> getValue(Primitive primitive) { |
| @@ -367,6 +550,24 @@ class _TransformingVisitor<T> extends RecursiveVisitor { |
| left.substituteFor(node); |
| } |
| } |
| + |
| + void visitLetPrim(LetPrim node) { |
| + _AbstractValue<T> value = getValue(node.primitive); |
| + if (node.primitive is! Constant && value.isConstant) { |
| + // If the value is a known constant, compile it as a constant. |
| + Constant newPrim = makeConstantPrimitive(value.constant); |
| + LetPrim newLet = new LetPrim(newPrim); |
| + node.parent.body = newLet; |
| + newLet.body = node.body; |
| + node.body.parent = newLet; |
| + newLet.parent = node.parent; |
| + newPrim.substituteFor(node.primitive); |
| + RemovalVisitor.remove(node.primitive); |
| + visit(newLet.body); |
| + } else { |
| + super.visitLetPrim(node); |
| + } |
| + } |
| } |
| /** |
| @@ -391,10 +592,10 @@ class _TypePropagationVisitor<T> implements Visitor { |
| // since their lattice value has changed. |
| final Set<Definition> defWorkset = new Set<Definition>(); |
| - final ConstantSystem constantSystem; |
| - final TypeSystem<T> typeSystem; |
| + final ConstantPropagationLattice<T> valueLattice; |
| final dart2js.InternalErrorFunction internalError; |
| - final types.DartTypes _dartTypes; |
| + |
| + TypeSystem get typeSystem => valueLattice.typeSystem; |
| _AbstractValue<T> nothing = new _AbstractValue.nothing(); |
| @@ -412,14 +613,16 @@ class _TypePropagationVisitor<T> implements Visitor { |
| // Stores the current lattice value for nodes. Note that it contains not only |
| // definitions as keys, but also expressions such as method invokes. |
| // Access through [getValue] and [setValue]. |
| - final Map<Node, _AbstractValue<T>> values; |
| + final Map<Primitive, _AbstractValue<T>> values; |
| + |
| + /// Expressions that invoke their call continuation with a constant value |
| + /// and without any side effects. |
|
karlklose
2015/06/08 09:46:53
Perhaps add: 'These expressions can be replaced by
asgerf
2015/06/08 10:46:16
Right, the comment did not really explain why the
|
| + final Map<Expression, ConstantValue> replacements; |
| - _TypePropagationVisitor(this.constantSystem, |
| - TypeSystem typeSystem, |
| + _TypePropagationVisitor(this.valueLattice, |
| this.values, |
| - this.internalError, |
| - this._dartTypes) |
| - : this.typeSystem = typeSystem; |
| + this.replacements, |
| + this.internalError); |
| void analyze(FunctionDefinition root) { |
| reachableNodes.clear(); |
| @@ -560,63 +763,54 @@ class _TypePropagationVisitor<T> implements Visitor { |
| Continuation cont = node.continuation.definition; |
| setReachable(cont); |
| - /// Sets the value of both the current node and the target continuation |
| - /// parameter. |
| - void setValues(_AbstractValue<T> updateValue) { |
| - setValue(node, updateValue); |
| + /// Sets the value of the target continuation parameter, and possibly |
| + /// try to replace the whole invocation with a constant. |
| + void setResult(_AbstractValue<T> updateValue, {bool canReplace: false}) { |
| Parameter returnValue = cont.parameters[0]; |
| setValue(returnValue, updateValue); |
| + if (canReplace && updateValue.isConstant) { |
| + replacements[node] = updateValue.constant; |
| + } else { |
| + // A previous iteration might have tried to replace this. |
| + replacements.remove(node); |
| + } |
| } |
| _AbstractValue<T> lhs = getValue(node.receiver.definition); |
| if (lhs.isNothing) { |
| return; // And come back later. |
| - } else if (lhs.isNonConst) { |
| - setValues(nonConstant(typeSystem.getSelectorReturnType(node.selector))); |
| - return; |
| - } else if (!node.selector.isOperator) { |
| + } |
| + if (!node.selector.isOperator) { |
| // TODO(jgruber): Handle known methods on constants such as String.length. |
| - setValues(nonConstant()); |
| + setResult(nonConstant(typeSystem.getSelectorReturnType(node.selector))); |
| return; |
| } |
| + // TODO(asgerf): Support constant folding on intercepted calls! |
| + |
| // Calculate the resulting constant if possible. |
| - ConstantValue result; |
| + _AbstractValue<T> result; |
| String opname = node.selector.name; |
| if (node.selector.argumentCount == 0) { |
| // Unary operator. |
| - |
| if (opname == "unary-") { |
| opname = "-"; |
| } |
| - UnaryOperation operation = constantSystem.lookupUnary( |
| - UnaryOperator.parse(opname)); |
| - if (operation != null) { |
| - result = operation.fold(lhs.constant); |
| - } |
| + UnaryOperator operator = UnaryOperator.parse(opname); |
| + result = valueLattice.unaryOp(operator, lhs); |
| } else if (node.selector.argumentCount == 1) { |
| // Binary operator. |
| - |
| _AbstractValue<T> rhs = getValue(node.arguments[0].definition); |
| - if (!rhs.isConstant) { |
| - setValues(nonConstant()); |
| - return; |
| - } |
| - |
| - BinaryOperation operation = constantSystem.lookupBinary( |
| - BinaryOperator.parse(opname)); |
| - if (operation != null) { |
| - result = operation.fold(lhs.constant, rhs.constant); |
| - } |
| + BinaryOperator operator = BinaryOperator.parse(opname); |
| + result = valueLattice.binaryOp(operator, lhs, rhs); |
| } |
| // Update value of the continuation parameter. Again, this is effectively |
| // a phi. |
| if (result == null) { |
| - setValues(nonConstant()); |
| + setResult(nonConstant()); |
| } else { |
| - T type = typeSystem.getTypeOf(result); |
| - setValues(constantValue(result, type)); |
| + setResult(result, canReplace: true); |
| } |
| } |
| @@ -643,10 +837,17 @@ class _TypePropagationVisitor<T> implements Visitor { |
| Continuation cont = node.continuation.definition; |
| setReachable(cont); |
| - void setValues(_AbstractValue<T> updateValue) { |
| - setValue(node, updateValue); |
| + /// Sets the value of the target continuation parameter, and possibly |
| + /// try to replace the whole invocation with a constant. |
| + void setResult(_AbstractValue<T> updateValue, {bool canReplace: false}) { |
| Parameter returnValue = cont.parameters[0]; |
| setValue(returnValue, updateValue); |
| + if (canReplace && updateValue.isConstant) { |
| + replacements[node] = updateValue.constant; |
| + } else { |
| + // A previous iteration might have tried to replace this. |
| + replacements.remove(node); |
| + } |
| } |
| // TODO(jgruber): Currently we only optimize if all arguments are string |
| @@ -670,9 +871,9 @@ class _TypePropagationVisitor<T> implements Visitor { |
| }); |
| LiteralDartString dartString = new LiteralDartString(allStrings.join()); |
| ConstantValue constant = new StringConstantValue(dartString); |
| - setValues(constantValue(constant, type)); |
| + setResult(constantValue(constant, type), canReplace: true); |
| } else { |
| - setValues(nonConstant(type)); |
| + setResult(nonConstant(type)); |
| } |
| } |
| @@ -682,6 +883,9 @@ class _TypePropagationVisitor<T> implements Visitor { |
| void visitRethrow(Rethrow node) { |
| } |
| + void visitUnreachable(Unreachable node) { |
| + } |
| + |
| void visitNonTailThrow(NonTailThrow node) { |
| internalError(null, 'found non-tail throw after they were eliminated'); |
| } |
| @@ -709,50 +913,47 @@ class _TypePropagationVisitor<T> implements Visitor { |
| } |
| } |
| - void visitTypeOperator(TypeOperator node) { |
| - Continuation cont = node.continuation.definition; |
| - setReachable(cont); |
| + void visitTypeTest(TypeTest node) { |
| + _AbstractValue<T> input = getValue(node.value.definition); |
| + T boolType = typeSystem.boolType; |
| + switch(valueLattice.isSubtypeOf(input, node.type, allowNull: false)) { |
| + case AbstractBool.Nothing: |
| + break; // And come back later. |
| - void setValues(_AbstractValue<T> updateValue) { |
| - setValue(node, updateValue); |
| - Parameter returnValue = cont.parameters[0]; |
| - setValue(returnValue, updateValue); |
| - } |
| + case AbstractBool.True: |
| + setValue(node, constantValue(new TrueConstantValue(), boolType)); |
| + break; |
| - if (node.isTypeCast) { |
| - // TODO(jgruber): Add support for `as` casts. |
| - setValues(nonConstant()); |
| - return; |
| + case AbstractBool.False: |
| + setValue(node, constantValue(new FalseConstantValue(), boolType)); |
| + break; |
| + |
| + case AbstractBool.Maybe: |
| + setValue(node, nonConstant(boolType)); |
| + break; |
| } |
| + } |
| - _AbstractValue<T> cell = getValue(node.value.definition); |
| - if (cell.isNothing) { |
| - return; // And come back later. |
| - } else if (cell.isConstant && node.type.kind == types.TypeKind.INTERFACE) { |
| - // Receiver is a constant, perform is-checks at compile-time. |
| - |
| - types.InterfaceType checkedType = node.type; |
| - ConstantValue constant = cell.constant; |
| - types.DartType constantType = constant.getType(_dartTypes.coreTypes); |
| - |
| - T type = typeSystem.boolType; |
| - _AbstractValue<T> result; |
| - if (constant.isNull && |
| - checkedType != _dartTypes.coreTypes.nullType && |
| - checkedType != _dartTypes.coreTypes.objectType) { |
| - // `(null is Type)` is true iff Type is in { Null, Object }. |
| - result = constantValue(new FalseConstantValue(), type); |
| - } else { |
| - // Otherwise, perform a standard subtype check. |
| - result = constantValue( |
| - constantSystem.isSubtype(_dartTypes, constantType, checkedType) |
| - ? new TrueConstantValue() |
| - : new FalseConstantValue(), |
| - type); |
| - } |
| - setValues(result); |
| - } else { |
| - setValues(nonConstant(typeSystem.boolType)); |
| + void visitTypeCast(TypeCast node) { |
| + Continuation cont = node.continuation.definition; |
| + _AbstractValue<T> input = getValue(node.value.definition); |
| + switch (valueLattice.isSubtypeOf(input, node.type, allowNull: true)) { |
| + case AbstractBool.Nothing: |
| + break; // And come back later. |
| + |
| + case AbstractBool.True: |
| + setReachable(cont); |
| + setValue(cont.parameters.single, input); |
| + break; |
| + |
| + case AbstractBool.False: |
| + break; // Cast fails. Continuation should remain unreachable. |
| + |
| + case AbstractBool.Maybe: |
| + // TODO(asgerf): Narrow type of output to those that survive the cast. |
| + setReachable(cont); |
| + setValue(cont.parameters.single, input); |
| + break; |
| } |
| } |