| Index: pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart
 | 
| diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart
 | 
| index 0b6af073e67b018e7e24b006aecd0719b9b6f6ef..c61fe0bb5a2db33dab7c98bd626c7711a8b17f38 100644
 | 
| --- a/pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart
 | 
| +++ b/pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart
 | 
| @@ -20,6 +20,7 @@
 | 
|  import 'package:front_end/src/base/instrumentation.dart';
 | 
|  import 'package:front_end/src/fasta/type_inference/type_inference_engine.dart';
 | 
|  import 'package:front_end/src/fasta/type_inference/type_inferrer.dart';
 | 
| +import 'package:front_end/src/fasta/type_inference/type_promotion.dart';
 | 
|  import 'package:kernel/ast.dart';
 | 
|  
 | 
|  /// Concrete shadow object representing a statement block in kernel form.
 | 
| @@ -43,6 +44,17 @@ abstract class KernelExpression implements Expression {
 | 
|        KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded);
 | 
|  }
 | 
|  
 | 
| +/// Concrete shadow object representing an expression statement in kernel form.
 | 
| +class KernelExpressionStatement extends ExpressionStatement
 | 
| +    implements KernelStatement {
 | 
| +  KernelExpressionStatement(Expression expression) : super(expression);
 | 
| +
 | 
| +  @override
 | 
| +  void _inferStatement(KernelTypeInferrer inferrer) {
 | 
| +    inferrer.inferExpressionStatement(expression);
 | 
| +  }
 | 
| +}
 | 
| +
 | 
|  /// Concrete shadow object representing a field in kernel form.
 | 
|  class KernelField extends Field {
 | 
|    bool _implicitlyTyped = true;
 | 
| @@ -83,8 +95,32 @@ class KernelFunctionExpression extends FunctionExpression
 | 
|    @override
 | 
|    DartType _inferExpression(
 | 
|        KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
 | 
| -    // TODO(paulberry): implement.
 | 
| -    return typeNeeded ? const DynamicType() : null;
 | 
| +    var asyncMarker = function.asyncMarker;
 | 
| +    bool isAsync = asyncMarker == AsyncMarker.Async ||
 | 
| +        asyncMarker == AsyncMarker.AsyncStar;
 | 
| +    bool isGenerator = asyncMarker == AsyncMarker.SyncStar ||
 | 
| +        asyncMarker == AsyncMarker.AsyncStar;
 | 
| +    return inferrer.inferFunctionExpression(
 | 
| +        typeContext,
 | 
| +        typeNeeded,
 | 
| +        function.body,
 | 
| +        function.body is ReturnStatement,
 | 
| +        isAsync,
 | 
| +        isGenerator,
 | 
| +        fileOffset, (type) {
 | 
| +      function.returnType = type;
 | 
| +    }, () => function.functionType);
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +/// Concrete shadow object representing an if statement in kernel form.
 | 
| +class KernelIfStatement extends IfStatement implements KernelStatement {
 | 
| +  KernelIfStatement(Expression condition, Statement then, Statement otherwise)
 | 
| +      : super(condition, then, otherwise);
 | 
| +
 | 
| +  @override
 | 
| +  void _inferStatement(KernelTypeInferrer inferrer) {
 | 
| +    inferrer.inferIfStatement(condition, then, otherwise);
 | 
|    }
 | 
|  }
 | 
|  
 | 
| @@ -99,6 +135,31 @@ class KernelIntLiteral extends IntLiteral implements KernelExpression {
 | 
|    }
 | 
|  }
 | 
|  
 | 
| +/// Concrete shadow object representing a non-inverted "is" test in kernel form.
 | 
| +class KernelIsExpression extends IsExpression implements KernelExpression {
 | 
| +  KernelIsExpression(Expression operand, DartType type) : super(operand, type);
 | 
| +
 | 
| +  @override
 | 
| +  DartType _inferExpression(
 | 
| +      KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
 | 
| +    return inferrer.inferIsExpression(typeContext, typeNeeded, operand);
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +/// Concrete shadow object representing an inverted "is" test in kernel form.
 | 
| +class KernelIsNotExpression extends Not implements KernelExpression {
 | 
| +  KernelIsNotExpression(Expression operand, DartType type, int charOffset)
 | 
| +      : super(new IsExpression(operand, type)..fileOffset = charOffset);
 | 
| +
 | 
| +  @override
 | 
| +  DartType _inferExpression(
 | 
| +      KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
 | 
| +    IsExpression isExpression = this.operand;
 | 
| +    return inferrer.inferIsExpression(
 | 
| +        typeContext, typeNeeded, isExpression.operand);
 | 
| +  }
 | 
| +}
 | 
| +
 | 
|  /// Concrete shadow object representing a list literal in kernel form.
 | 
|  class KernelListLiteral extends ListLiteral implements KernelExpression {
 | 
|    KernelListLiteral(List<KernelExpression> expressions,
 | 
| @@ -226,7 +287,10 @@ class KernelTypeInferenceEngine extends TypeInferenceEngineImpl<KernelField> {
 | 
|  /// Concrete implementation of [TypeInferrer] specialized to work with kernel
 | 
|  /// objects.
 | 
|  class KernelTypeInferrer extends TypeInferrerImpl<Statement, Expression,
 | 
| -    KernelVariableDeclaration, KernelField> {
 | 
| +    VariableDeclaration, KernelField> {
 | 
| +  @override
 | 
| +  final typePromoter = new KernelTypePromoter();
 | 
| +
 | 
|    KernelTypeInferrer._(KernelTypeInferenceEngine engine, String uri)
 | 
|        : super(engine, uri);
 | 
|  
 | 
| @@ -287,12 +351,83 @@ class KernelTypeInferrer extends TypeInferrerImpl<Statement, Expression,
 | 
|    }
 | 
|  }
 | 
|  
 | 
| +/// Concrete implementation of [TypePromoter] specialized to work with kernel
 | 
| +/// objects.
 | 
| +///
 | 
| +/// Note: the second type parameter really ought to be
 | 
| +/// KernelVariableDeclaration, but we can't do that yet because BodyBuilder
 | 
| +/// still uses raw VariableDeclaration objects sometimes.
 | 
| +/// TODO(paulberry): fix this.
 | 
| +class KernelTypePromoter
 | 
| +    extends TypePromoterImpl<Expression, VariableDeclaration> {
 | 
| +  @override
 | 
| +  int getVariableFunctionNestingLevel(VariableDeclaration variable) {
 | 
| +    if (variable is KernelVariableDeclaration) {
 | 
| +      return variable._functionNestingLevel;
 | 
| +    } else {
 | 
| +      // Hack to deal with the fact that BodyBuilder still creates raw
 | 
| +      // VariableDeclaration objects sometimes.
 | 
| +      // TODO(paulberry): get rid of this once the type parameter is
 | 
| +      // KernelVariableDeclaration.
 | 
| +      return 0;
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  @override
 | 
| +  bool sameExpressions(Expression a, Expression b) {
 | 
| +    return identical(a, b);
 | 
| +  }
 | 
| +
 | 
| +  @override
 | 
| +  void setVariableMutatedAnywhere(VariableDeclaration variable) {
 | 
| +    if (variable is KernelVariableDeclaration) {
 | 
| +      variable._mutatedAnywhere = true;
 | 
| +    } else {
 | 
| +      // Hack to deal with the fact that BodyBuilder still creates raw
 | 
| +      // VariableDeclaration objects sometimes.
 | 
| +      // TODO(paulberry): get rid of this once the type parameter is
 | 
| +      // KernelVariableDeclaration.
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  @override
 | 
| +  void setVariableMutatedInClosure(VariableDeclaration variable) {
 | 
| +    if (variable is KernelVariableDeclaration) {
 | 
| +      variable._mutatedInClosure = true;
 | 
| +    } else {
 | 
| +      // Hack to deal with the fact that BodyBuilder still creates raw
 | 
| +      // VariableDeclaration objects sometimes.
 | 
| +      // TODO(paulberry): get rid of this once the type parameter is
 | 
| +      // KernelVariableDeclaration.
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  @override
 | 
| +  bool wasVariableMutatedAnywhere(VariableDeclaration variable) {
 | 
| +    if (variable is KernelVariableDeclaration) {
 | 
| +      return variable._mutatedAnywhere;
 | 
| +    } else {
 | 
| +      // Hack to deal with the fact that BodyBuilder still creates raw
 | 
| +      // VariableDeclaration objects sometimes.
 | 
| +      // TODO(paulberry): get rid of this once the type parameter is
 | 
| +      // KernelVariableDeclaration.
 | 
| +      return true;
 | 
| +    }
 | 
| +  }
 | 
| +}
 | 
| +
 | 
|  /// Concrete shadow object representing a variable declaration in kernel form.
 | 
|  class KernelVariableDeclaration extends VariableDeclaration
 | 
|      implements KernelStatement {
 | 
|    final bool _implicitlyTyped;
 | 
|  
 | 
| -  KernelVariableDeclaration(String name,
 | 
| +  final int _functionNestingLevel;
 | 
| +
 | 
| +  bool _mutatedInClosure = false;
 | 
| +
 | 
| +  bool _mutatedAnywhere = false;
 | 
| +
 | 
| +  KernelVariableDeclaration(String name, this._functionNestingLevel,
 | 
|        {Expression initializer,
 | 
|        DartType type,
 | 
|        bool isFinal: false,
 | 
| @@ -304,6 +439,8 @@ class KernelVariableDeclaration extends VariableDeclaration
 | 
|              isFinal: isFinal,
 | 
|              isConst: isConst);
 | 
|  
 | 
| +  DartType get _declaredType => _implicitlyTyped ? null : type;
 | 
| +
 | 
|    @override
 | 
|    void _inferStatement(KernelTypeInferrer inferrer) {
 | 
|      inferrer.inferVariableDeclaration(
 | 
| @@ -315,13 +452,33 @@ class KernelVariableDeclaration extends VariableDeclaration
 | 
|  
 | 
|  /// Concrete shadow object representing a read from a variable in kernel form.
 | 
|  class KernelVariableGet extends VariableGet implements KernelExpression {
 | 
| -  KernelVariableGet(VariableDeclaration variable, [DartType promotedType])
 | 
| -      : super(variable, promotedType);
 | 
| +  final TypePromotionFact<VariableDeclaration> _fact;
 | 
| +
 | 
| +  final TypePromotionScope _scope;
 | 
| +
 | 
| +  KernelVariableGet(VariableDeclaration variable, this._fact, this._scope)
 | 
| +      : super(variable);
 | 
|  
 | 
|    @override
 | 
|    DartType _inferExpression(
 | 
|        KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
 | 
| -    // TODO(paulberry): implement.
 | 
| -    return typeNeeded ? const DynamicType() : null;
 | 
| +    bool mutatedInClosure;
 | 
| +    DartType declaredType;
 | 
| +    var variable = this.variable;
 | 
| +    if (variable is KernelVariableDeclaration) {
 | 
| +      mutatedInClosure = variable._mutatedInClosure;
 | 
| +      declaredType = variable._declaredType;
 | 
| +    } else {
 | 
| +      // Hack to deal with the fact that BodyBuilder still creates raw
 | 
| +      // VariableDeclaration objects sometimes.
 | 
| +      // TODO(paulberry): get rid of this once the type parameter is
 | 
| +      // KernelVariableDeclaration.
 | 
| +      mutatedInClosure = true;
 | 
| +      declaredType = variable.type;
 | 
| +    }
 | 
| +    return inferrer.inferVariableGet(typeContext, typeNeeded, mutatedInClosure,
 | 
| +        _fact, _scope, fileOffset, declaredType, (type) {
 | 
| +      promotedType = type;
 | 
| +    });
 | 
|    }
 | 
|  }
 | 
| 
 |