Replace CFE and analyzer legacy type promotion with a shared implementation.

This allows us to remove a substantial amount of CFE and analyzer
code.

It also fixes a minor CFE type promotion bug
(language_2/type_promotion/assignment_defeats_promotion_lhs_and_test).

TEST=standard trybots
Change-Id: Ia0c159bdb9161d73648c9eb73b92822168f28d84
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/175583
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart b/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
index a8af89d..b050ba3 100644
--- a/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
@@ -4794,10 +4794,6 @@
   @override
   void write(Node node, Variable variable, Type writtenType,
       Expression? writtenExpression) {
-    assert(
-        _assignedVariables._anywhere._written.contains(variable),
-        "Variable is written to, but was not included in "
-        "_variablesWrittenAnywhere: $variable");
     _writeStackForAnd.last.add(variable);
   }
 
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
index 399bc43..e85f8b7 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
@@ -684,13 +684,10 @@
     // Nothing for RESOLVED_UNIT9?
     // Nothing for RESOLVED_UNIT10?
 
-    FlowAnalysisHelper? flowAnalysisHelper;
-    if (unit.featureSet.isEnabled(Feature.non_nullable)) {
-      flowAnalysisHelper =
-          FlowAnalysisHelper(_typeSystem, _testingData != null);
-      _testingData?.recordFlowAnalysisDataForTesting(
-          file.uri, flowAnalysisHelper.dataForTesting!);
-    }
+    FlowAnalysisHelper flowAnalysisHelper = FlowAnalysisHelper(_typeSystem,
+        _testingData != null, unit.featureSet.isEnabled(Feature.non_nullable));
+    _testingData?.recordFlowAnalysisDataForTesting(
+        file.uri, flowAnalysisHelper.dataForTesting!);
 
     unit.accept(ResolverVisitor(
         _inheritance, _libraryElement, source, _typeProvider, errorListener,
diff --git a/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart b/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart
index c394437..2d5a144 100644
--- a/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart
@@ -703,10 +703,8 @@
     // Nothing for RESOLVED_UNIT9?
     // Nothing for RESOLVED_UNIT10?
 
-    FlowAnalysisHelper? flowAnalysisHelper;
-    if (unit.featureSet.isEnabled(Feature.non_nullable)) {
-      flowAnalysisHelper = FlowAnalysisHelper(_typeSystem, false);
-    }
+    FlowAnalysisHelper flowAnalysisHelper = FlowAnalysisHelper(
+        _typeSystem, false, unit.featureSet.isEnabled(Feature.non_nullable));
 
     var resolverVisitor = ResolverVisitor(
         _inheritance, _libraryElement, source, _typeProvider, errorListener,
diff --git a/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart
index 3b63d6c..b975efd 100644
--- a/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart
@@ -74,7 +74,7 @@
       var leftType = node.writeType;
       if (writeElement is VariableElement) {
         leftType = _resolver.localVariableTypeProvider
-            .getType(left as SimpleIdentifier);
+            .getType(left as SimpleIdentifier, isRead: false);
       }
       _setRhsContext(node, leftType!, operator, right);
     }
diff --git a/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart
index e4b016a..4e6836f 100644
--- a/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart
@@ -18,20 +18,16 @@
 import 'package:analyzer/src/dart/resolver/type_property_resolver.dart';
 import 'package:analyzer/src/error/codes.dart';
 import 'package:analyzer/src/generated/resolver.dart';
-import 'package:analyzer/src/generated/type_promotion_manager.dart';
 
 /// Helper for resolving [BinaryExpression]s.
 class BinaryExpressionResolver {
   final ResolverVisitor _resolver;
-  final TypePromotionManager _promoteManager;
   final TypePropertyResolver _typePropertyResolver;
   final InvocationInferenceHelper _inferenceHelper;
 
   BinaryExpressionResolver({
     required ResolverVisitor resolver,
-    required TypePromotionManager promoteManager,
   })   : _resolver = resolver,
-        _promoteManager = promoteManager,
         _typePropertyResolver = resolver.typePropertyResolver,
         _inferenceHelper = resolver.inferenceHelper;
 
@@ -176,27 +172,16 @@
     left = node.leftOperand;
     var leftWhyNotPromoted = _resolver.flowAnalysis?.flow?.whyNotPromoted(left);
 
-    Map<DartType, NonPromotionReason> Function()? rightWhyNotPromoted;
-    if (_resolver.flowAnalysis != null) {
-      flow?.logicalBinaryOp_rightBegin(left, node, isAnd: true);
-      _resolver.checkUnreachableNode(right);
+    flow?.logicalBinaryOp_rightBegin(left, node, isAnd: true);
+    _resolver.checkUnreachableNode(right);
 
-      right.accept(_resolver);
-      right = node.rightOperand;
-      rightWhyNotPromoted = _resolver.flowAnalysis!.flow?.whyNotPromoted(right);
+    right.accept(_resolver);
+    right = node.rightOperand;
+    var rightWhyNotPromoted =
+        _resolver.flowAnalysis!.flow?.whyNotPromoted(right);
 
-      _resolver.nullSafetyDeadCodeVerifier.flowEnd(right);
-      flow?.logicalBinaryOp_end(node, right, isAnd: true);
-    } else {
-      _promoteManager.visitBinaryExpression_and_rhs(
-        left,
-        right,
-        () {
-          right.accept(_resolver);
-          right = node.rightOperand;
-        },
-      );
-    }
+    _resolver.nullSafetyDeadCodeVerifier.flowEnd(right);
+    flow?.logicalBinaryOp_end(node, right, isAnd: true);
 
     _checkNonBoolOperand(left, '&&', whyNotPromoted: leftWhyNotPromoted);
     _checkNonBoolOperand(right, '&&', whyNotPromoted: rightWhyNotPromoted);
diff --git a/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart b/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
index 2478a00..b78323d 100644
--- a/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
@@ -39,8 +39,7 @@
 
   /// For each top level or class level declaration, the assigned variables
   /// information that was computed for it.
-  final Map<Declaration,
-          AssignedVariablesForTesting<AstNode, PromotableElement>>
+  final Map<AstNode, AssignedVariablesForTesting<AstNode, PromotableElement>>
       assignedVariables = {};
 
   /// For each expression that led to an error because it was not promoted, a
@@ -67,15 +66,21 @@
   /// The result for post-resolution stages of analysis, for testing only.
   final FlowAnalysisDataForTesting? dataForTesting;
 
+  final bool isNonNullableByDefault;
+
   /// The current flow, when resolving a function body, or `null` otherwise.
   FlowAnalysis<AstNode, Statement, Expression, PromotableElement, DartType>?
       flow;
 
-  FlowAnalysisHelper(TypeSystemImpl typeSystem, bool retainDataForTesting)
-      : this._(TypeSystemTypeOperations(typeSystem),
-            retainDataForTesting ? FlowAnalysisDataForTesting() : null);
+  FlowAnalysisHelper(TypeSystemImpl typeSystem, bool retainDataForTesting,
+      bool isNonNullableByDefault)
+      : this._(
+            TypeSystemTypeOperations(typeSystem),
+            retainDataForTesting ? FlowAnalysisDataForTesting() : null,
+            isNonNullableByDefault);
 
-  FlowAnalysisHelper._(this._typeOperations, this.dataForTesting);
+  FlowAnalysisHelper._(
+      this._typeOperations, this.dataForTesting, this.isNonNullableByDefault);
 
   LocalVariableTypeProvider get localVariableTypeProvider {
     return _LocalVariableTypeProvider(this);
@@ -154,11 +159,11 @@
   }
 
   void for_bodyBegin(AstNode node, Expression? condition) {
-    flow!.for_bodyBegin(node is Statement ? node : null, condition);
+    flow?.for_bodyBegin(node is Statement ? node : null, condition);
   }
 
   void for_conditionBegin(AstNode node) {
-    flow!.for_conditionBegin(node);
+    flow?.for_conditionBegin(node);
   }
 
   bool isDefinitelyAssigned(
@@ -218,7 +223,7 @@
   }
 
   void topLevelDeclaration_enter(
-      Declaration node, FormalParameterList? parameters, FunctionBody? body) {
+      AstNode node, FormalParameterList? parameters, FunctionBody? body) {
     assert(flow == null);
     assignedVariables = computeAssignedVariables(node, parameters,
         retainDataForTesting: dataForTesting != null);
@@ -226,8 +231,11 @@
       dataForTesting!.assignedVariables[node] = assignedVariables
           as AssignedVariablesForTesting<AstNode, PromotableElement>;
     }
-    flow = FlowAnalysis<AstNode, Statement, Expression, PromotableElement,
-        DartType>(_typeOperations, assignedVariables!);
+    flow = isNonNullableByDefault
+        ? FlowAnalysis<AstNode, Statement, Expression, PromotableElement,
+            DartType>(_typeOperations, assignedVariables!)
+        : FlowAnalysis<AstNode, Statement, Expression, PromotableElement,
+            DartType>.legacy(_typeOperations, assignedVariables!);
   }
 
   void topLevelDeclaration_exit() {
@@ -267,7 +275,7 @@
 
   /// Computes the [AssignedVariables] map for the given [node].
   static AssignedVariables<AstNode, PromotableElement> computeAssignedVariables(
-      Declaration node, FormalParameterList? parameters,
+      AstNode node, FormalParameterList? parameters,
       {bool retainDataForTesting = false}) {
     AssignedVariables<AstNode, PromotableElement> assignedVariables =
         retainDataForTesting
@@ -331,13 +339,13 @@
 class FlowAnalysisHelperForMigration extends FlowAnalysisHelper {
   final MigrationResolutionHooks migrationResolutionHooks;
 
-  FlowAnalysisHelperForMigration(
-      TypeSystemImpl typeSystem, this.migrationResolutionHooks)
-      : super(typeSystem, false);
+  FlowAnalysisHelperForMigration(TypeSystemImpl typeSystem,
+      this.migrationResolutionHooks, bool isNonNullableByDefault)
+      : super(typeSystem, false, isNonNullableByDefault);
 
   @override
   void topLevelDeclaration_enter(
-      Declaration node, FormalParameterList? parameters, FunctionBody? body) {
+      AstNode node, FormalParameterList? parameters, FunctionBody? body) {
     super.topLevelDeclaration_enter(node, parameters, body);
     migrationResolutionHooks.setFlowAnalysis(flow);
   }
@@ -672,10 +680,12 @@
   _LocalVariableTypeProvider(this._manager);
 
   @override
-  DartType getType(SimpleIdentifier node) {
+  DartType getType(SimpleIdentifier node, {required bool isRead}) {
     var variable = node.staticElement as VariableElement;
     if (variable is PromotableElement) {
-      var promotedType = _manager.flow?.variableRead(node, variable);
+      var promotedType = isRead
+          ? _manager.flow?.variableRead(node, variable)
+          : _manager.flow?.promotedType(variable);
       if (promotedType != null) return promotedType;
     }
     return variable.type;
diff --git a/pkg/analyzer/lib/src/dart/resolver/for_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/for_resolver.dart
index 1797cfc..1c4f15f 100644
--- a/pkg/analyzer/lib/src/dart/resolver/for_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/for_resolver.dart
@@ -95,7 +95,8 @@
     if (identifier != null) {
       identifierElement = identifier.staticElement;
       if (identifierElement is VariableElement) {
-        valueType = _resolver.localVariableTypeProvider.getType(identifier);
+        valueType = _resolver.localVariableTypeProvider
+            .getType(identifier, isRead: false);
       } else if (identifierElement is PropertyAccessorElement) {
         var parameters = identifierElement.parameters;
         if (parameters.isNotEmpty) {
diff --git a/pkg/analyzer/lib/src/dart/resolver/function_expression_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/function_expression_resolver.dart
index abb6f0a..ed02e6a 100644
--- a/pkg/analyzer/lib/src/dart/resolver/function_expression_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/function_expression_resolver.dart
@@ -13,23 +13,19 @@
 import 'package:analyzer/src/dart/resolver/invocation_inference_helper.dart';
 import 'package:analyzer/src/generated/migration.dart';
 import 'package:analyzer/src/generated/resolver.dart';
-import 'package:analyzer/src/generated/type_promotion_manager.dart';
 import 'package:collection/collection.dart';
 
 class FunctionExpressionResolver {
   final ResolverVisitor _resolver;
   final MigrationResolutionHooks? _migrationResolutionHooks;
   final InvocationInferenceHelper _inferenceHelper;
-  final TypePromotionManager _promoteManager;
 
   FunctionExpressionResolver({
     required ResolverVisitor resolver,
     required MigrationResolutionHooks? migrationResolutionHooks,
-    required TypePromotionManager promoteManager,
   })   : _resolver = resolver,
         _migrationResolutionHooks = migrationResolutionHooks,
-        _inferenceHelper = resolver.inferenceHelper,
-        _promoteManager = promoteManager;
+        _inferenceHelper = resolver.inferenceHelper;
 
   bool get _isNonNullableByDefault => _typeSystem.isNonNullableByDefault;
 
@@ -39,13 +35,9 @@
     var isFunctionDeclaration = node.parent is FunctionDeclaration;
     var body = node.body;
 
-    if (_resolver.flowAnalysis != null) {
-      if (_resolver.flowAnalysis!.flow != null && !isFunctionDeclaration) {
-        _resolver.flowAnalysis!
-            .executableDeclaration_enter(node, node.parameters, true);
-      }
-    } else {
-      _promoteManager.enterFunctionBody(body);
+    if (_resolver.flowAnalysis!.flow != null && !isFunctionDeclaration) {
+      _resolver.flowAnalysis!
+          .executableDeclaration_enter(node, node.parameters, true);
     }
 
     var contextType = InferenceContext.getContext(node);
@@ -63,19 +55,15 @@
     node.visitChildren(_resolver);
     _resolve2(node);
 
-    if (_resolver.flowAnalysis != null) {
-      if (_resolver.flowAnalysis!.flow != null && !isFunctionDeclaration) {
-        var bodyContext = BodyInferenceContext.of(node.body);
-        _resolver.checkForBodyMayCompleteNormally(
-          returnType: bodyContext?.contextType,
-          body: body,
-          errorNode: body,
-        );
-        _resolver.flowAnalysis!.flow?.functionExpression_end();
-        _resolver.nullSafetyDeadCodeVerifier.flowEnd(node);
-      }
-    } else {
-      _promoteManager.exitFunctionBody();
+    if (_resolver.flowAnalysis!.flow != null && !isFunctionDeclaration) {
+      var bodyContext = BodyInferenceContext.of(node.body);
+      _resolver.checkForBodyMayCompleteNormally(
+        returnType: bodyContext?.contextType,
+        body: body,
+        errorNode: body,
+      );
+      _resolver.flowAnalysis!.flow?.functionExpression_end();
+      _resolver.nullSafetyDeadCodeVerifier.flowEnd(node);
     }
   }
 
diff --git a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
index a6ab6e7..ad2fd4f 100644
--- a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
@@ -544,7 +544,8 @@
       }
       if (element is VariableElement) {
         _resolver.checkReadOfNotAssignedLocalVariable(nameNode, element);
-        var targetType = _localVariableTypeProvider.getType(nameNode);
+        var targetType =
+            _localVariableTypeProvider.getType(nameNode, isRead: true);
         return _rewriteAsFunctionExpressionInvocation(node, targetType);
       }
       // TODO(scheglov) This is a questionable distinction.
diff --git a/pkg/analyzer/lib/src/dart/resolver/simple_identifier_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/simple_identifier_resolver.dart
index fb7dbb0..f1b1859 100644
--- a/pkg/analyzer/lib/src/dart/resolver/simple_identifier_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/simple_identifier_resolver.dart
@@ -243,7 +243,8 @@
     } else if (element is TypeParameterElement) {
       staticType = _typeProvider.typeType;
     } else if (element is VariableElement) {
-      staticType = _resolver.localVariableTypeProvider.getType(node);
+      staticType = _resolver.localVariableTypeProvider
+          .getType(node, isRead: node.inGetterContext());
     } else if (element is PrefixElement) {
       var parent = node.parent;
       if (parent is PrefixedIdentifier && parent.prefix == node ||
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index a6f3800..71ca8bb 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -64,7 +64,6 @@
 import 'package:analyzer/src/generated/source.dart';
 import 'package:analyzer/src/generated/static_type_analyzer.dart';
 import 'package:analyzer/src/generated/this_access_tracker.dart';
-import 'package:analyzer/src/generated/type_promotion_manager.dart';
 import 'package:analyzer/src/generated/variable_type_provider.dart';
 import 'package:analyzer/src/util/ast_data_extractor.dart';
 import 'package:meta/meta.dart';
@@ -233,9 +232,6 @@
   /// Otherwise `null`.
   DartType? _thisType;
 
-  /// The object keeping track of which elements have had their types promoted.
-  late final TypePromotionManager _promoteManager;
-
   final FlowAnalysisHelper? flowAnalysis;
 
   /// A comment before a function should be resolved in the context of the
@@ -314,8 +310,6 @@
         super(definingLibrary, source, typeProvider as TypeProviderImpl,
             errorListener,
             nameScope: nameScope) {
-    _promoteManager = TypePromotionManager(typeSystem);
-
     var analysisOptions =
         definingLibrary.context.analysisOptions as AnalysisOptionsImpl;
 
@@ -345,7 +339,6 @@
     );
     _binaryExpressionResolver = BinaryExpressionResolver(
       resolver: this,
-      promoteManager: _promoteManager,
     );
     _functionExpressionInvocationResolver =
         FunctionExpressionInvocationResolver(
@@ -354,7 +347,6 @@
     _functionExpressionResolver = FunctionExpressionResolver(
       resolver: this,
       migrationResolutionHooks: migrationResolutionHooks,
-      promoteManager: _promoteManager,
     );
     _forResolver = ForResolver(
       resolver: this,
@@ -396,7 +388,7 @@
     if (flowAnalysis != null) {
       return flowAnalysis!.localVariableTypeProvider;
     } else {
-      return _promoteManager.localVariableTypeProvider;
+      return const NonPromotingLocalVariableTypeProvider();
     }
   }
 
@@ -442,6 +434,7 @@
     required FunctionBody body,
     required AstNode errorNode,
   }) {
+    if (!_isNonNullableByDefault) return;
     if (!flowAnalysis!.flow!.isReachable) {
       return;
     }
@@ -630,11 +623,6 @@
     // TODO(brianwilkerson) Remove this method.
   }
 
-  /// Set the enclosing function body when partial AST is resolved.
-  void prepareCurrentFunctionBody(FunctionBody body) {
-    _promoteManager.enterFunctionBody(body);
-  }
-
   /// Set information about enclosing declarations.
   void prepareEnclosingDeclarations({
     ClassElement? enclosingClassElement,
@@ -775,7 +763,8 @@
       if (element is PropertyAccessorElement && element.isGetter) {
         readType = element.returnType;
       } else if (element is VariableElement) {
-        readType = localVariableTypeProvider.getType(node as SimpleIdentifier);
+        readType = localVariableTypeProvider.getType(node as SimpleIdentifier,
+            isRead: true);
       }
     }
 
@@ -1199,22 +1188,12 @@
     Expression thenExpression = node.thenExpression;
     InferenceContext.setTypeFromNode(thenExpression, node);
 
-    if (flowAnalysis != null) {
-      if (flow != null) {
-        flow.conditional_thenBegin(condition, node);
-        checkUnreachableNode(thenExpression);
-      }
-      thenExpression.accept(this);
-      nullSafetyDeadCodeVerifier.flowEnd(thenExpression);
-    } else {
-      _promoteManager.visitConditionalExpression_then(
-        condition,
-        thenExpression,
-        () {
-          thenExpression.accept(this);
-        },
-      );
+    if (flow != null) {
+      flow.conditional_thenBegin(condition, node);
+      checkUnreachableNode(thenExpression);
     }
+    thenExpression.accept(this);
+    nullSafetyDeadCodeVerifier.flowEnd(thenExpression);
 
     Expression elseExpression = node.elseExpression;
     InferenceContext.setTypeFromNode(elseExpression, node);
@@ -1244,33 +1223,25 @@
     var outerFunction = _enclosingFunction;
     _enclosingFunction = node.declaredElement!;
 
-    if (flowAnalysis != null) {
-      flowAnalysis!.topLevelDeclaration_enter(node, node.parameters, node.body);
-      flowAnalysis!.executableDeclaration_enter(node, node.parameters, false);
-    } else {
-      _promoteManager.enterFunctionBody(node.body);
-    }
+    flowAnalysis!.topLevelDeclaration_enter(node, node.parameters, node.body);
+    flowAnalysis!.executableDeclaration_enter(node, node.parameters, false);
 
     var returnType = _enclosingFunction!.type.returnType;
     InferenceContext.setType(node.body, returnType);
 
     super.visitConstructorDeclaration(node);
 
-    if (flowAnalysis != null) {
-      if (node.factoryKeyword != null) {
-        var bodyContext = BodyInferenceContext.of(node.body);
-        checkForBodyMayCompleteNormally(
-          returnType: bodyContext?.contextType,
-          body: node.body,
-          errorNode: node,
-        );
-      }
-      flowAnalysis!.executableDeclaration_exit(node.body, false);
-      flowAnalysis!.topLevelDeclaration_exit();
-      nullSafetyDeadCodeVerifier.flowEnd(node);
-    } else {
-      _promoteManager.exitFunctionBody();
+    if (node.factoryKeyword != null) {
+      var bodyContext = BodyInferenceContext.of(node.body);
+      checkForBodyMayCompleteNormally(
+        returnType: bodyContext?.contextType,
+        body: node.body,
+        errorNode: node,
+      );
     }
+    flowAnalysis!.executableDeclaration_exit(node.body, false);
+    flowAnalysis!.topLevelDeclaration_exit();
+    nullSafetyDeadCodeVerifier.flowEnd(node);
 
     var constructor = node.declaredElement as ConstructorElementImpl;
     constructor.constantInitializers =
@@ -1487,53 +1458,45 @@
 
     bool isLocal = node.parent is FunctionDeclarationStatement;
 
-    if (flowAnalysis != null) {
-      if (isLocal) {
-        flowAnalysis!.flow!.functionExpression_begin(node);
-      } else {
-        flowAnalysis!.topLevelDeclaration_enter(
-          node,
-          node.functionExpression.parameters,
-          node.functionExpression.body,
-        );
-      }
-      flowAnalysis!.executableDeclaration_enter(
+    if (isLocal) {
+      flowAnalysis!.flow!.functionExpression_begin(node);
+    } else {
+      flowAnalysis!.topLevelDeclaration_enter(
         node,
         node.functionExpression.parameters,
-        isLocal,
+        node.functionExpression.body,
       );
-    } else {
-      _promoteManager.enterFunctionBody(node.functionExpression.body);
     }
+    flowAnalysis!.executableDeclaration_enter(
+      node,
+      node.functionExpression.parameters,
+      isLocal,
+    );
 
     var functionType = _enclosingFunction!.type;
     InferenceContext.setType(node.functionExpression, functionType);
 
     super.visitFunctionDeclaration(node);
 
-    if (flowAnalysis != null) {
-      // TODO(scheglov) encapsulate
-      var bodyContext = BodyInferenceContext.of(
-        node.functionExpression.body,
-      );
-      checkForBodyMayCompleteNormally(
-        returnType: bodyContext?.contextType,
-        body: node.functionExpression.body,
-        errorNode: node.name,
-      );
-      flowAnalysis!.executableDeclaration_exit(
-        node.functionExpression.body,
-        isLocal,
-      );
-      if (isLocal) {
-        flowAnalysis!.flow!.functionExpression_end();
-      } else {
-        flowAnalysis!.topLevelDeclaration_exit();
-      }
-      nullSafetyDeadCodeVerifier.flowEnd(node);
+    // TODO(scheglov) encapsulate
+    var bodyContext = BodyInferenceContext.of(
+      node.functionExpression.body,
+    );
+    checkForBodyMayCompleteNormally(
+      returnType: bodyContext?.contextType,
+      body: node.functionExpression.body,
+      errorNode: node.name,
+    );
+    flowAnalysis!.executableDeclaration_exit(
+      node.functionExpression.body,
+      isLocal,
+    );
+    if (isLocal) {
+      flowAnalysis!.flow!.functionExpression_end();
     } else {
-      _promoteManager.exitFunctionBody();
+      flowAnalysis!.topLevelDeclaration_exit();
     }
+    nullSafetyDeadCodeVerifier.flowEnd(node);
 
     _enclosingFunction = outerFunction;
   }
@@ -1620,18 +1583,8 @@
         whyNotPromoted: whyNotPromoted);
 
     CollectionElement thenElement = node.thenElement;
-    if (flowAnalysis != null) {
-      flowAnalysis!.flow?.ifStatement_thenBegin(condition, node);
-      thenElement.accept(this);
-    } else {
-      _promoteManager.visitIfElement_thenElement(
-        condition,
-        thenElement,
-        () {
-          thenElement.accept(this);
-        },
-      );
-    }
+    flowAnalysis!.flow?.ifStatement_thenBegin(condition, node);
+    thenElement.accept(this);
 
     var elseElement = node.elseElement;
     if (elseElement != null) {
@@ -1661,19 +1614,9 @@
         whyNotPromoted: whyNotPromoted);
 
     Statement thenStatement = node.thenStatement;
-    if (flowAnalysis != null) {
-      flowAnalysis!.flow?.ifStatement_thenBegin(condition, node);
-      visitStatementInScope(thenStatement);
-      nullSafetyDeadCodeVerifier.flowEnd(thenStatement);
-    } else {
-      _promoteManager.visitIfStatement_thenStatement(
-        condition,
-        thenStatement,
-        () {
-          visitStatementInScope(thenStatement);
-        },
-      );
-    }
+    flowAnalysis!.flow?.ifStatement_thenBegin(condition, node);
+    visitStatementInScope(thenStatement);
+    nullSafetyDeadCodeVerifier.flowEnd(thenStatement);
 
     var elseStatement = node.elseStatement;
     if (elseStatement != null) {
@@ -1770,32 +1713,24 @@
     var outerFunction = _enclosingFunction;
     _enclosingFunction = node.declaredElement!;
 
-    if (flowAnalysis != null) {
-      flowAnalysis!.topLevelDeclaration_enter(node, node.parameters, node.body);
-      flowAnalysis!.executableDeclaration_enter(node, node.parameters, false);
-    } else {
-      _promoteManager.enterFunctionBody(node.body);
-    }
+    flowAnalysis!.topLevelDeclaration_enter(node, node.parameters, node.body);
+    flowAnalysis!.executableDeclaration_enter(node, node.parameters, false);
 
     DartType returnType = _enclosingFunction!.returnType;
     InferenceContext.setType(node.body, returnType);
 
     super.visitMethodDeclaration(node);
 
-    if (flowAnalysis != null) {
-      // TODO(scheglov) encapsulate
-      var bodyContext = BodyInferenceContext.of(node.body);
-      checkForBodyMayCompleteNormally(
-        returnType: bodyContext?.contextType,
-        body: node.body,
-        errorNode: node.name,
-      );
-      flowAnalysis!.executableDeclaration_exit(node.body, false);
-      flowAnalysis!.topLevelDeclaration_exit();
-      nullSafetyDeadCodeVerifier.flowEnd(node);
-    } else {
-      _promoteManager.exitFunctionBody();
-    }
+    // TODO(scheglov) encapsulate
+    var bodyContext = BodyInferenceContext.of(node.body);
+    checkForBodyMayCompleteNormally(
+      returnType: bodyContext?.contextType,
+      body: node.body,
+      errorNode: node.name,
+    );
+    flowAnalysis!.executableDeclaration_exit(node.body, false);
+    flowAnalysis!.topLevelDeclaration_exit();
+    nullSafetyDeadCodeVerifier.flowEnd(node);
 
     _enclosingFunction = outerFunction;
   }
@@ -2030,7 +1965,7 @@
     super.visitSwitchCase(node);
 
     var flow = flowAnalysis?.flow;
-    if (flow != null && flow.isReachable) {
+    if (flow != null && flow.isReachable && _isNonNullableByDefault) {
       var switchStatement = node.parent as SwitchStatement;
       if (switchStatement.members.last != node && node.statements.isNotEmpty) {
         errorReporter.reportErrorForToken(
@@ -2162,7 +2097,8 @@
     if (initializer != null) {
       var initializerStaticType = initializer.typeOrThrow;
       if (declaredType == null) {
-        if (initializerStaticType is TypeParameterType) {
+        if (_isNonNullableByDefault &&
+            initializerStaticType is TypeParameterType) {
           flowAnalysis?.flow?.promote(
               declaredElement as PromotableElement, initializerStaticType);
         }
@@ -2482,7 +2418,7 @@
             null,
             true,
             FlowAnalysisHelperForMigration(
-                typeSystem, migrationResolutionHooks),
+                typeSystem, migrationResolutionHooks, true),
             migrationResolutionHooks,
             migrationResolutionHooks);
 
diff --git a/pkg/analyzer/lib/src/generated/type_promotion_manager.dart b/pkg/analyzer/lib/src/generated/type_promotion_manager.dart
deleted file mode 100644
index 965b30f..0000000
--- a/pkg/analyzer/lib/src/generated/type_promotion_manager.dart
+++ /dev/null
@@ -1,388 +0,0 @@
-// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-import 'package:analyzer/dart/ast/ast.dart';
-import 'package:analyzer/dart/ast/token.dart';
-import 'package:analyzer/dart/ast/visitor.dart';
-import 'package:analyzer/dart/element/element.dart';
-import 'package:analyzer/dart/element/type.dart';
-import 'package:analyzer/src/dart/ast/extensions.dart';
-import 'package:analyzer/src/dart/element/type_system.dart';
-import 'package:analyzer/src/generated/variable_type_provider.dart';
-
-/// Instances of the class `TypePromotionManager` manage the ability to promote
-/// types of local variables and formal parameters from their declared types
-/// based on control flow.
-class TypePromotionManager {
-  final TypeSystemImpl _typeSystem;
-
-  /// The current promotion scope.
-  _TypePromoteScope _currentScope = _TypePromoteScope(null);
-
-  final List<FunctionBody?> _functionBodyStack = [];
-
-  /// Body of the function currently being analyzed, if any.
-  FunctionBody? _currentFunctionBody;
-
-  TypePromotionManager(this._typeSystem);
-
-  LocalVariableTypeProvider get localVariableTypeProvider {
-    return _LegacyLocalVariableTypeProvider(this);
-  }
-
-  /// Returns the elements with promoted types.
-  Iterable<Element> get _promotedElements {
-    return _currentScope.promotedElements;
-  }
-
-  void enterFunctionBody(FunctionBody body) {
-    _functionBodyStack.add(_currentFunctionBody);
-    _currentFunctionBody = body;
-  }
-
-  void exitFunctionBody() {
-    if (_functionBodyStack.isEmpty) {
-      assert(false, 'exitFunctionBody without a matching enterFunctionBody');
-    } else {
-      _currentFunctionBody = _functionBodyStack.removeLast();
-    }
-  }
-
-  void visitBinaryExpression_and_rhs(
-      Expression leftOperand, Expression rightOperand, void Function() f) {
-    _enterScope();
-    try {
-      // Type promotion.
-      _promoteTypes(leftOperand);
-      _clearTypePromotionsIfPotentiallyMutatedIn(leftOperand);
-      _clearTypePromotionsIfPotentiallyMutatedIn(rightOperand);
-      _clearTypePromotionsIfAccessedInClosureAndPotentiallyMutated(
-          rightOperand);
-      // Visit right operand.
-      f();
-    } finally {
-      _exitScope();
-    }
-  }
-
-  void visitConditionalExpression_then(
-      Expression condition, Expression thenExpression, void Function() f) {
-    _enterScope();
-    try {
-      // Type promotion.
-      _promoteTypes(condition);
-      _clearTypePromotionsIfPotentiallyMutatedIn(thenExpression);
-      _clearTypePromotionsIfAccessedInClosureAndPotentiallyMutated(
-        thenExpression,
-      );
-      // Visit "then" expression.
-      f();
-    } finally {
-      _exitScope();
-    }
-  }
-
-  void visitIfElement_thenElement(
-      Expression condition, CollectionElement thenElement, void Function() f) {
-    _enterScope();
-    try {
-      // Type promotion.
-      _promoteTypes(condition);
-      _clearTypePromotionsIfPotentiallyMutatedIn(thenElement);
-      _clearTypePromotionsIfAccessedInClosureAndPotentiallyMutated(thenElement);
-      // Visit "then".
-      f();
-    } finally {
-      _exitScope();
-    }
-  }
-
-  void visitIfStatement_thenStatement(
-      Expression condition, Statement thenStatement, void Function() f) {
-    _enterScope();
-    try {
-      // Type promotion.
-      _promoteTypes(condition);
-      _clearTypePromotionsIfPotentiallyMutatedIn(thenStatement);
-      _clearTypePromotionsIfAccessedInClosureAndPotentiallyMutated(
-          thenStatement);
-      // Visit "then".
-      f();
-    } finally {
-      _exitScope();
-    }
-  }
-
-  /// Checks each promoted variable in the current scope for compliance with the
-  /// following specification statement:
-  ///
-  /// If the variable <i>v</i> is accessed by a closure in <i>s<sub>1</sub></i>
-  /// then the variable <i>v</i> is not potentially mutated anywhere in the
-  /// scope of <i>v</i>.
-  void _clearTypePromotionsIfAccessedInClosureAndPotentiallyMutated(
-      AstNode target) {
-    for (Element element in _promotedElements) {
-      if (_currentFunctionBody!
-          .isPotentiallyMutatedInScope(element as VariableElement)) {
-        if (_isVariableAccessedInClosure(element, target)) {
-          _setType(element, null);
-        }
-      }
-    }
-  }
-
-  /// Checks each promoted variable in the current scope for compliance with the
-  /// following specification statement:
-  ///
-  /// <i>v</i> is not potentially mutated in <i>s<sub>1</sub></i> or within a
-  /// closure.
-  void _clearTypePromotionsIfPotentiallyMutatedIn(AstNode target) {
-    for (Element element in _promotedElements) {
-      if (_isVariablePotentiallyMutatedIn(element, target)) {
-        _setType(element, null);
-      }
-    }
-  }
-
-  /// Enter a new promotions scope.
-  void _enterScope() {
-    _currentScope = _TypePromoteScope(_currentScope);
-  }
-
-  /// Exit the current promotion scope.
-  void _exitScope() {
-    var outerScope = _currentScope._outerScope;
-    if (outerScope == null) {
-      throw StateError("No scope to exit");
-    }
-    _currentScope = outerScope;
-  }
-
-  /// Return the promoted type of the given [element], or `null` if the type of
-  /// the element has not been promoted.
-  DartType? _getPromotedType(Element element) {
-    return _currentScope.getType(element);
-  }
-
-  /// Return the static element associated with the given expression whose type
-  /// can be promoted, or `null` if there is no element whose type can be
-  /// promoted.
-  VariableElement? _getPromotionStaticElement(Expression expression) {
-    expression = expression.unParenthesized;
-    if (expression is SimpleIdentifier) {
-      var element = expression.staticElement;
-      if (element is VariableElement) {
-        ElementKind kind = element.kind;
-        if (kind == ElementKind.LOCAL_VARIABLE ||
-            kind == ElementKind.PARAMETER) {
-          return element;
-        }
-      }
-    }
-    return null;
-  }
-
-  /// Given that the [node] is a reference to a [VariableElement], return the
-  /// static type of the variable at this node - declared or promoted.
-  DartType _getType(SimpleIdentifier node) {
-    var variable = node.staticElement as VariableElement;
-    return _getPromotedType(variable) ?? variable.type;
-  }
-
-  /// Return `true` if the given variable is accessed within a closure in the
-  /// given [AstNode] and also mutated somewhere in variable scope. This
-  /// information is only available for local variables (including parameters).
-  ///
-  /// @param variable the variable to check
-  /// @param target the [AstNode] to check within
-  /// @return `true` if this variable is potentially mutated somewhere in the
-  ///         given ASTNode
-  bool _isVariableAccessedInClosure(Element variable, AstNode target) {
-    _ResolverVisitor_isVariableAccessedInClosure visitor =
-        _ResolverVisitor_isVariableAccessedInClosure(variable);
-    target.accept(visitor);
-    return visitor.result;
-  }
-
-  /// Return `true` if the given variable is potentially mutated somewhere in
-  /// the given [AstNode]. This information is only available for local
-  /// variables (including parameters).
-  ///
-  /// @param variable the variable to check
-  /// @param target the [AstNode] to check within
-  /// @return `true` if this variable is potentially mutated somewhere in the
-  ///         given ASTNode
-  bool _isVariablePotentiallyMutatedIn(Element variable, AstNode target) {
-    _ResolverVisitor_isVariablePotentiallyMutatedIn visitor =
-        _ResolverVisitor_isVariablePotentiallyMutatedIn(variable);
-    target.accept(visitor);
-    return visitor.result;
-  }
-
-  /// If it is appropriate to do so, promotes the current type of the static
-  /// element associated with the given expression with the given type.
-  /// Generally speaking, it is appropriate if the given type is more specific
-  /// than the current type.
-  ///
-  /// @param expression the expression used to access the static element whose
-  ///        types might be promoted
-  /// @param potentialType the potential type of the elements
-  void _promote(Expression expression, DartType potentialType) {
-    var element = _getPromotionStaticElement(expression);
-    if (element != null) {
-      // may be mutated somewhere in closure
-      if (_currentFunctionBody!.isPotentiallyMutatedInClosure(element)) {
-        return;
-      }
-      // prepare current variable type
-      DartType type = _getPromotedType(element) ?? expression.typeOrThrow;
-
-      // Check if we can promote to potentialType from type.
-      var promoteType = _typeSystem.tryPromoteToType(potentialType, type);
-      if (promoteType != null) {
-        // Do promote type of variable.
-        _setType(element, promoteType);
-      }
-    }
-  }
-
-  /// Promotes type information using given condition.
-  void _promoteTypes(Expression condition) {
-    if (condition is BinaryExpression) {
-      if (condition.operator.type == TokenType.AMPERSAND_AMPERSAND) {
-        Expression left = condition.leftOperand;
-        Expression right = condition.rightOperand;
-        _promoteTypes(left);
-        _promoteTypes(right);
-        _clearTypePromotionsIfPotentiallyMutatedIn(right);
-      }
-    } else if (condition is IsExpression) {
-      if (condition.notOperator == null) {
-        _promote(condition.expression, condition.type.typeOrThrow);
-      }
-    } else if (condition is ParenthesizedExpression) {
-      _promoteTypes(condition.expression);
-    }
-  }
-
-  /// Set the promoted type of the given element to the given type.
-  ///
-  /// @param element the element whose type might have been promoted
-  /// @param type the promoted type of the given element
-  void _setType(Element element, DartType? type) {
-    if (_currentScope._outerScope == null) {
-      throw StateError("Cannot promote without a scope");
-    }
-    _currentScope.setType(element, type);
-  }
-}
-
-/// The legacy, pre-NNBD implementation of [LocalVariableTypeProvider].
-class _LegacyLocalVariableTypeProvider implements LocalVariableTypeProvider {
-  final TypePromotionManager _manager;
-
-  _LegacyLocalVariableTypeProvider(this._manager);
-
-  @override
-  DartType getType(SimpleIdentifier node) {
-    return _manager._getType(node);
-  }
-}
-
-class _ResolverVisitor_isVariableAccessedInClosure
-    extends RecursiveAstVisitor<void> {
-  final Element variable;
-
-  bool result = false;
-
-  bool _inClosure = false;
-
-  _ResolverVisitor_isVariableAccessedInClosure(this.variable);
-
-  @override
-  void visitFunctionExpression(FunctionExpression node) {
-    bool inClosure = _inClosure;
-    try {
-      _inClosure = true;
-      super.visitFunctionExpression(node);
-    } finally {
-      _inClosure = inClosure;
-    }
-  }
-
-  @override
-  void visitSimpleIdentifier(SimpleIdentifier node) {
-    if (result) {
-      return;
-    }
-    if (_inClosure && identical(node.staticElement, variable)) {
-      result = true;
-    }
-  }
-}
-
-class _ResolverVisitor_isVariablePotentiallyMutatedIn
-    extends RecursiveAstVisitor<void> {
-  final Element variable;
-
-  bool result = false;
-
-  _ResolverVisitor_isVariablePotentiallyMutatedIn(this.variable);
-
-  @override
-  void visitSimpleIdentifier(SimpleIdentifier node) {
-    if (result) {
-      return;
-    }
-    if (identical(node.staticElement, variable)) {
-      if (node.inSetterContext()) {
-        result = true;
-      }
-    }
-  }
-}
-
-/// Instances of the class `TypePromoteScope` represent a scope in which the
-/// types of elements can be promoted.
-class _TypePromoteScope {
-  /// The outer scope in which types might be promoted.
-  final _TypePromoteScope? _outerScope;
-
-  /// A table mapping elements to the promoted type of that element.
-  final Map<Element, DartType?> _promotedTypes = {};
-
-  /// Initialize a newly created scope to be an empty child of the given scope.
-  ///
-  /// @param outerScope the outer scope in which types might be promoted
-  _TypePromoteScope(this._outerScope);
-
-  /// Returns the elements with promoted types.
-  Iterable<Element> get promotedElements => _promotedTypes.keys.toSet();
-
-  /// Return the promoted type of the given element, or `null` if the type of
-  /// the element has not been promoted.
-  ///
-  /// @param element the element whose type might have been promoted
-  /// @return the promoted type of the given element
-  DartType? getType(Element element) {
-    var type = _promotedTypes[element];
-    if (type == null && element is PropertyAccessorElement) {
-      type = _promotedTypes[element.variable];
-    }
-    if (type != null) {
-      return type;
-    } else if (_outerScope != null) {
-      return _outerScope!.getType(element);
-    }
-    return null;
-  }
-
-  /// Set the promoted type of the given element to the given type.
-  ///
-  /// @param element the element whose type might have been promoted
-  /// @param type the promoted type of the given element
-  void setType(Element element, DartType? type) {
-    _promotedTypes[element] = type;
-  }
-}
diff --git a/pkg/analyzer/lib/src/generated/variable_type_provider.dart b/pkg/analyzer/lib/src/generated/variable_type_provider.dart
index 376d102..4258443 100644
--- a/pkg/analyzer/lib/src/generated/variable_type_provider.dart
+++ b/pkg/analyzer/lib/src/generated/variable_type_provider.dart
@@ -3,11 +3,24 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/dart/element/type.dart';
 
 /// Provider of types for local variables and formal parameters.
 abstract class LocalVariableTypeProvider {
   /// Given that the [node] is a reference to a local variable, or a parameter,
   /// return the type of the variable at the node - declared or promoted.
-  DartType getType(SimpleIdentifier node);
+  DartType getType(SimpleIdentifier node, {required bool isRead});
+}
+
+/// Implementation of [LocalVariableTypeProvider] that does not promote, for use
+/// in situations where no flow analysis is being done.  This happens in some
+/// analyzer internal unit tests.
+class NonPromotingLocalVariableTypeProvider
+    implements LocalVariableTypeProvider {
+  const NonPromotingLocalVariableTypeProvider();
+
+  @override
+  DartType getType(SimpleIdentifier node, {required bool isRead}) =>
+      (node.staticElement as VariableElement).type;
 }
diff --git a/pkg/analyzer/lib/src/summary2/ast_resolver.dart b/pkg/analyzer/lib/src/summary2/ast_resolver.dart
index 85a2151..3be65c2 100644
--- a/pkg/analyzer/lib/src/summary2/ast_resolver.dart
+++ b/pkg/analyzer/lib/src/summary2/ast_resolver.dart
@@ -53,16 +53,12 @@
       node.accept(variableResolverVisitor);
     }
 
-    FlowAnalysisHelper? flowAnalysis;
+    FlowAnalysisHelper flowAnalysis = FlowAnalysisHelper(
+        _unitElement.library.typeSystem,
+        false,
+        _unitElement.library.isNonNullableByDefault);
     if (isTopLevelVariableInitializer) {
-      if (_unitElement.library.isNonNullableByDefault) {
-        flowAnalysis = FlowAnalysisHelper(
-          _unitElement.library.typeSystem,
-          false,
-        );
-        flowAnalysis.topLevelDeclaration_enter(
-            node.parent as Declaration, null, null);
-      }
+      flowAnalysis.topLevelDeclaration_enter(node.parent!, null, null);
     }
 
     var resolverVisitor = ResolverVisitor(
@@ -80,14 +76,11 @@
       enclosingClassElement: enclosingClassElement,
       enclosingExecutableElement: enclosingExecutableElement,
     );
-    if (enclosingFunctionBody != null) {
-      resolverVisitor.prepareCurrentFunctionBody(enclosingFunctionBody);
-    }
 
     node.accept(resolverVisitor);
 
     if (isTopLevelVariableInitializer) {
-      flowAnalysis?.topLevelDeclaration_exit();
+      flowAnalysis.topLevelDeclaration_exit();
     }
   }
 }
diff --git a/pkg/analyzer/lib/src/summary2/default_value_resolver.dart b/pkg/analyzer/lib/src/summary2/default_value_resolver.dart
index 9566af2..f5d81ef 100644
--- a/pkg/analyzer/lib/src/summary2/default_value_resolver.dart
+++ b/pkg/analyzer/lib/src/summary2/default_value_resolver.dart
@@ -119,6 +119,7 @@
       },
       enclosingClassElement: _classElement,
       enclosingExecutableElement: _executableElement,
+      isTopLevelVariableInitializer: true,
     );
   }
 
diff --git a/pkg/front_end/lib/src/fasta/builder/procedure_builder.dart b/pkg/front_end/lib/src/fasta/builder/procedure_builder.dart
index 4b2720b..a7659a2 100644
--- a/pkg/front_end/lib/src/fasta/builder/procedure_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/procedure_builder.dart
@@ -799,23 +799,15 @@
           for (VariableDeclaration parameter
               in member.function.positionalParameters) {
             inferrer.flowAnalysis?.declare(parameter, true);
-            positionalArguments.add(new VariableGetImpl(
-                parameter,
-                inferrer.typePromoter.getFactForAccess(parameter, 0),
-                inferrer.typePromoter.currentScope,
-                forNullGuardedAccess: false));
+            positionalArguments.add(
+                new VariableGetImpl(parameter, forNullGuardedAccess: false));
           }
           List<NamedExpression> namedArguments = <NamedExpression>[];
           for (VariableDeclaration parameter
               in member.function.namedParameters) {
             inferrer.flowAnalysis?.declare(parameter, true);
-            namedArguments.add(new NamedExpression(
-                parameter.name,
-                new VariableGetImpl(
-                    parameter,
-                    inferrer.typePromoter.getFactForAccess(parameter, 0),
-                    inferrer.typePromoter.currentScope,
-                    forNullGuardedAccess: false)));
+            namedArguments.add(new NamedExpression(parameter.name,
+                new VariableGetImpl(parameter, forNullGuardedAccess: false)));
           }
           // If arguments are created using [Forest.createArguments], and the
           // type arguments are omitted, they are to be inferred.
diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
index 8fc537e..3f3cf82 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -110,9 +110,6 @@
 import '../type_inference/type_inferrer.dart'
     show TypeInferrer, InferredFunctionBody;
 
-import '../type_inference/type_promotion.dart'
-    show TypePromoter, TypePromotionFact, TypePromotionScope;
-
 import '../type_inference/type_schema.dart' show UnknownType;
 
 import '../util/helpers.dart' show DelayedActionPerformer;
@@ -202,8 +199,6 @@
 
   final TypeInferrer typeInferrer;
 
-  final TypePromoter typePromoter;
-
   /// Only used when [member] is a constructor. It tracks if an implicit super
   /// initializer is needed.
   ///
@@ -375,7 +370,6 @@
                     libraryBuilder.importUri.path == "ui"),
         needsImplicitSuperInitializer = declarationBuilder is ClassBuilder &&
             coreTypes?.objectClass != declarationBuilder.cls,
-        typePromoter = typeInferrer?.typePromoter,
         super(enclosingScope) {
     formalParameterScope?.forEach((String name, Builder builder) {
       if (builder is VariableBuilder) {
@@ -463,7 +457,6 @@
 
   @override
   void registerVariableAssignment(VariableDeclaration variable) {
-    typePromoter?.mutateVariable(variable, functionNestingLevel);
     typeInferrer?.assignedVariables?.write(variable);
   }
 
@@ -948,7 +941,6 @@
   void finishFunction(
       FormalParameters formals, AsyncMarker asyncModifier, Statement body) {
     debugEvent("finishFunction");
-    typePromoter?.finished();
     typeInferrer?.assignedVariables?.finish();
 
     FunctionBuilder builder = member;
@@ -1791,7 +1783,6 @@
     bool isAnd = optional("&&", token);
     if (isAnd || optional("||", token)) {
       Expression lhs = popForValue();
-      typePromoter?.enterLogicalExpression(lhs, token.stringValue);
       // This is matched by the call to [endNode] in
       // [doLogicalExpression].
       if (isAnd) {
@@ -1879,7 +1870,6 @@
     Expression receiver = pop();
     Expression logicalExpression = forest.createLogicalExpression(
         offsetForToken(token), receiver, token.stringValue, argument);
-    typePromoter?.exitLogicalExpression(argument, logicalExpression);
     push(logicalExpression);
     if (optional("&&", token)) {
       // This is matched by the call to [beginNode] in
@@ -2105,10 +2095,7 @@
     if (!(variable as VariableDeclarationImpl).isLocalFunction) {
       typeInferrer?.assignedVariables?.read(variable);
     }
-    Object fact =
-        typePromoter?.getFactForAccess(variable, functionNestingLevel);
-    Object scope = typePromoter?.currentScope;
-    return new VariableGetImpl(variable, fact, scope,
+    return new VariableGetImpl(variable,
         forNullGuardedAccess: forNullGuardedAccess)
       ..fileOffset = charOffset;
   }
@@ -2472,7 +2459,6 @@
   @override
   void beginThenStatement(Token token) {
     Expression condition = popForValue();
-    enterThenForTypePromotion(condition);
     // This is matched by the call to [deferNode] in
     // [endThenStatement].
     typeInferrer?.assignedVariables?.beginNode();
@@ -2482,7 +2468,6 @@
 
   @override
   void endThenStatement(Token token) {
-    typePromoter?.enterElse();
     super.endThenStatement(token);
     // This is matched by the call to [beginNode] in
     // [beginThenStatement] and by the call to [storeInfo] in
@@ -2497,7 +2482,6 @@
         pop();
     Statement thenPart = popStatement();
     Expression condition = pop();
-    typePromoter?.exitConditional();
     Statement node = forest.createIfStatement(
         offsetForToken(ifToken), condition, thenPart, elsePart);
     // This is matched by the call to [deferNode] in
@@ -3535,24 +3519,18 @@
     DartType type = buildDartType(pop(),
         allowPotentiallyConstantType: libraryBuilder.isNonNullableByDefault);
     Expression operand = popForValue();
-    bool isInverted = not != null;
     Expression isExpression = forest.createIsExpression(
         offsetForToken(isOperator), operand, type,
         forNonNullableByDefault: libraryBuilder.isNonNullableByDefault,
         notFileOffset: not != null ? offsetForToken(not) : null);
     libraryBuilder.checkBoundsInType(
         type, typeEnvironment, uri, isOperator.charOffset);
-    if (operand is VariableGet) {
-      typePromoter?.handleIsCheck(isExpression, isInverted, operand.variable,
-          type, functionNestingLevel);
-    }
     push(isExpression);
   }
 
   @override
   void beginConditionalExpression(Token question) {
     Expression condition = popForValue();
-    typePromoter?.enterThen(condition);
     // This is matched by the call to [deferNode] in
     // [handleConditionalExpressionColon].
     typeInferrer?.assignedVariables?.beginNode();
@@ -3563,7 +3541,6 @@
   @override
   void handleConditionalExpressionColon() {
     Expression then = popForValue();
-    typePromoter?.enterElse();
     // This is matched by the call to [beginNode] in
     // [beginConditionalExpression] and by the call to [storeInfo] in
     // [endConditionalExpression].
@@ -3580,7 +3557,6 @@
     AssignedVariablesNodeInfo<VariableDeclaration> assignedVariablesInfo =
         pop();
     Expression condition = pop();
-    typePromoter?.exitConditional();
     Expression node = forest.createConditionalExpression(
         offsetForToken(question), condition, thenExpression, elseExpression);
     push(node);
@@ -4791,7 +4767,6 @@
   @override
   void handleThenControlFlow(Token token) {
     Expression condition = popForValue();
-    enterThenForTypePromotion(condition);
     // This is matched by the call to [deferNode] in
     // [handleElseControlFlow] and by the call to [endNode] in
     // [endIfControlFlow].
@@ -4811,7 +4786,6 @@
     // [endIfElseControlFlow].
     push(typeInferrer?.assignedVariables?.deferNode());
     push(node);
-    typePromoter?.enterElse();
   }
 
   @override
@@ -4831,8 +4805,6 @@
           offsetForToken(ifToken), toValue(condition), toValue(entry));
     }
     push(node);
-    typePromoter?.enterElse();
-    typePromoter?.exitConditional();
     // This is matched by the call to [beginNode] in
     // [handleThenControlFlow].
     typeInferrer?.assignedVariables?.endNode(node);
@@ -4847,7 +4819,6 @@
         pop();
     Object condition = pop(); // parenthesized expression
     Token ifToken = pop();
-    typePromoter?.exitConditional();
 
     transformCollections = true;
     TreeNode node;
@@ -5343,12 +5314,8 @@
         ///       lvalue = #t;
         ///       body;
         ///     }
-        TypePromotionFact fact =
-            typePromoter?.getFactForAccess(variable, functionNestingLevel);
-        TypePromotionScope scope = typePromoter?.currentScope;
         elements.syntheticAssignment = lvalue.buildAssignment(
-            new VariableGetImpl(variable, fact, scope,
-                forNullGuardedAccess: false)
+            new VariableGetImpl(variable, forNullGuardedAccess: false)
               ..fileOffset = inToken.offset,
             voidContext: true);
       } else {
@@ -6560,12 +6527,6 @@
     return new DeferredCheck(check, expression)..fileOffset = charOffset;
   }
 
-  /// TODO(ahe): This method is temporarily implemented. Once type promotion is
-  /// independent of shadow nodes, remove this method.
-  void enterThenForTypePromotion(Expression condition) {
-    typePromoter?.enterThen(condition);
-  }
-
   bool isErroneousNode(TreeNode node) {
     return libraryBuilder.loader.handledErrors.isNotEmpty &&
         forest.isErroneousNode(node);
diff --git a/pkg/front_end/lib/src/fasta/kernel/collections.dart b/pkg/front_end/lib/src/fasta/kernel/collections.dart
index e2fb85b..b29d126 100644
--- a/pkg/front_end/lib/src/fasta/kernel/collections.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/collections.dart
@@ -48,10 +48,11 @@
   /// Returns this control flow element as a [MapEntry], or `null` if this
   /// control flow element cannot be converted into a [MapEntry].
   ///
-  /// [onConvertForElement] is called when a [ForElement] or [ForInElement] is
-  /// converted to a [ForMapEntry] or [ForInMapEntry], respectively.
+  /// [onConvertElement] is called when a [ForElement], [ForInElement], or
+  /// [IfElement] is converted to a [ForMapEntry], [ForInMapEntry], or
+  /// [IfMapEntry], respectively.
   // TODO(johnniwinther): Merge this with [convertToMapEntry].
-  MapEntry toMapEntry(void onConvertForElement(TreeNode from, TreeNode to));
+  MapEntry toMapEntry(void onConvertElement(TreeNode from, TreeNode to));
 }
 
 /// A spread element in a list or set literal.
@@ -91,8 +92,7 @@
   }
 
   @override
-  SpreadMapEntry toMapEntry(
-      void onConvertForElement(TreeNode from, TreeNode to)) {
+  SpreadMapEntry toMapEntry(void onConvertElement(TreeNode from, TreeNode to)) {
     return new SpreadMapEntry(expression, isNullAware)..fileOffset = fileOffset;
   }
 
@@ -163,23 +163,25 @@
   }
 
   @override
-  MapEntry toMapEntry(void onConvertForElement(TreeNode from, TreeNode to)) {
+  MapEntry toMapEntry(void onConvertElement(TreeNode from, TreeNode to)) {
     MapEntry thenEntry;
     if (then is ControlFlowElement) {
       ControlFlowElement thenElement = then;
-      thenEntry = thenElement.toMapEntry(onConvertForElement);
+      thenEntry = thenElement.toMapEntry(onConvertElement);
     }
     if (thenEntry == null) return null;
     MapEntry otherwiseEntry;
     if (otherwise != null) {
       if (otherwise is ControlFlowElement) {
         ControlFlowElement otherwiseElement = otherwise;
-        otherwiseEntry = otherwiseElement.toMapEntry(onConvertForElement);
+        otherwiseEntry = otherwiseElement.toMapEntry(onConvertElement);
       }
       if (otherwiseEntry == null) return null;
     }
-    return new IfMapEntry(condition, thenEntry, otherwiseEntry)
+    IfMapEntry result = new IfMapEntry(condition, thenEntry, otherwiseEntry)
       ..fileOffset = fileOffset;
+    onConvertElement(this, result);
+    return result;
   }
 
   @override
@@ -251,17 +253,17 @@
   }
 
   @override
-  MapEntry toMapEntry(void onConvertForElement(TreeNode from, TreeNode to)) {
+  MapEntry toMapEntry(void onConvertElement(TreeNode from, TreeNode to)) {
     MapEntry bodyEntry;
     if (body is ControlFlowElement) {
       ControlFlowElement bodyElement = body;
-      bodyEntry = bodyElement.toMapEntry(onConvertForElement);
+      bodyEntry = bodyElement.toMapEntry(onConvertElement);
     }
     if (bodyEntry == null) return null;
     ForMapEntry result =
         new ForMapEntry(variables, condition, updates, bodyEntry)
           ..fileOffset = fileOffset;
-    onConvertForElement(this, result);
+    onConvertElement(this, result);
     return result;
   }
 
@@ -367,18 +369,18 @@
   }
 
   @override
-  MapEntry toMapEntry(void onConvertForElement(TreeNode from, TreeNode to)) {
+  MapEntry toMapEntry(void onConvertElement(TreeNode from, TreeNode to)) {
     MapEntry bodyEntry;
     if (body is ControlFlowElement) {
       ControlFlowElement bodyElement = body;
-      bodyEntry = bodyElement.toMapEntry(onConvertForElement);
+      bodyEntry = bodyElement.toMapEntry(onConvertElement);
     }
     if (bodyEntry == null) return null;
     ForInMapEntry result = new ForInMapEntry(variable, iterable,
         syntheticAssignment, expressionEffects, bodyEntry, problem,
         isAsync: isAsync)
       ..fileOffset = fileOffset;
-    onConvertForElement(this, result);
+    onConvertElement(this, result);
     return result;
   }
 
@@ -707,31 +709,31 @@
 /// converted an error reported through [helper] and an invalid expression is
 /// returned.
 ///
-/// [onConvertForMapEntry] is called when a [ForMapEntry] or [ForInMapEntry] is
-/// converted to a [ForElement] or [ForInElement], respectively.
+/// [onConvertMapEntry] is called when a [ForMapEntry], [ForInMapEntry], or
+/// [IfMapEntry] is converted to a [ForElement], [ForInElement], or [IfElement],
+/// respectively.
 Expression convertToElement(MapEntry entry, InferenceHelper helper,
-    void onConvertForMapEntry(TreeNode from, TreeNode to)) {
+    void onConvertMapEntry(TreeNode from, TreeNode to)) {
   if (entry is SpreadMapEntry) {
     return new SpreadElement(entry.expression, entry.isNullAware)
       ..fileOffset = entry.expression.fileOffset;
   }
   if (entry is IfMapEntry) {
-    return new IfElement(
+    IfElement result = new IfElement(
         entry.condition,
-        convertToElement(entry.then, helper, onConvertForMapEntry),
+        convertToElement(entry.then, helper, onConvertMapEntry),
         entry.otherwise == null
             ? null
-            : convertToElement(entry.otherwise, helper, onConvertForMapEntry))
+            : convertToElement(entry.otherwise, helper, onConvertMapEntry))
       ..fileOffset = entry.fileOffset;
+    onConvertMapEntry(entry, result);
+    return result;
   }
   if (entry is ForMapEntry) {
-    ForElement result = new ForElement(
-        entry.variables,
-        entry.condition,
-        entry.updates,
-        convertToElement(entry.body, helper, onConvertForMapEntry))
+    ForElement result = new ForElement(entry.variables, entry.condition,
+        entry.updates, convertToElement(entry.body, helper, onConvertMapEntry))
       ..fileOffset = entry.fileOffset;
-    onConvertForMapEntry(entry, result);
+    onConvertMapEntry(entry, result);
     return result;
   }
   if (entry is ForInMapEntry) {
@@ -740,11 +742,11 @@
         entry.iterable,
         entry.syntheticAssignment,
         entry.expressionEffects,
-        convertToElement(entry.body, helper, onConvertForMapEntry),
+        convertToElement(entry.body, helper, onConvertMapEntry),
         entry.problem,
         isAsync: entry.isAsync)
       ..fileOffset = entry.fileOffset;
-    onConvertForMapEntry(entry, result);
+    onConvertMapEntry(entry, result);
     return result;
   }
   Expression key = entry.key;
@@ -782,31 +784,34 @@
 /// converted an error reported through [helper] and a map entry holding an
 /// invalid expression is returned.
 ///
-/// [onConvertForElement] is called when a [ForElement] or [ForInElement] is
-/// converted to a [ForMapEntry] or [ForInMapEntry], respectively.
+/// [onConvertElement] is called when a [ForElement], [ForInElement], or
+/// [IfElement] is converted to a [ForMapEntry], [ForInMapEntry], or
+/// [IfMapEntry], respectively.
 MapEntry convertToMapEntry(Expression element, InferenceHelper helper,
-    void onConvertForElement(TreeNode from, TreeNode to)) {
+    void onConvertElement(TreeNode from, TreeNode to)) {
   if (element is SpreadElement) {
     return new SpreadMapEntry(element.expression, element.isNullAware)
       ..fileOffset = element.expression.fileOffset;
   }
   if (element is IfElement) {
-    return new IfMapEntry(
+    IfMapEntry result = new IfMapEntry(
         element.condition,
-        convertToMapEntry(element.then, helper, onConvertForElement),
+        convertToMapEntry(element.then, helper, onConvertElement),
         element.otherwise == null
             ? null
-            : convertToMapEntry(element.otherwise, helper, onConvertForElement))
+            : convertToMapEntry(element.otherwise, helper, onConvertElement))
       ..fileOffset = element.fileOffset;
+    onConvertElement(element, result);
+    return result;
   }
   if (element is ForElement) {
     ForMapEntry result = new ForMapEntry(
         element.variables,
         element.condition,
         element.updates,
-        convertToMapEntry(element.body, helper, onConvertForElement))
+        convertToMapEntry(element.body, helper, onConvertElement))
       ..fileOffset = element.fileOffset;
-    onConvertForElement(element, result);
+    onConvertElement(element, result);
     return result;
   }
   if (element is ForInElement) {
@@ -815,11 +820,11 @@
         element.iterable,
         element.syntheticAssignment,
         element.expressionEffects,
-        convertToMapEntry(element.body, helper, onConvertForElement),
+        convertToMapEntry(element.body, helper, onConvertElement),
         element.problem,
         isAsync: element.isAsync)
       ..fileOffset = element.fileOffset;
-    onConvertForElement(element, result);
+    onConvertElement(element, result);
     return result;
   }
   return new MapEntry(
diff --git a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
index 3e36973..20a67a7 100644
--- a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
@@ -6545,7 +6545,8 @@
     if (initializerResult != null) {
       DartType initializerType = initializerResult.inferredType;
       if (node.isImplicitlyTyped) {
-        if (initializerType is TypeParameterType) {
+        if (inferrer.isNonNullableByDefault &&
+            initializerType is TypeParameterType) {
           inferrer.flowAnalysis.promote(node, initializerType);
         }
       } else {
@@ -6735,20 +6736,14 @@
     DartType declaredOrInferredType = variable.lateType ?? variable.type;
     if (isExtensionThis(variable)) {
       inferrer.flowAnalysis.thisOrSuper(node, variable.type);
-    } else if (inferrer.isNonNullableByDefault) {
-      if (node.forNullGuardedAccess) {
-        DartType nonNullableType = variable.type.toNonNull();
-        if (nonNullableType != variable.type) {
-          promotedType = nonNullableType;
-        }
-      } else if (!variable.isLocalFunction) {
-        // Don't promote local functions.
-        promotedType = inferrer.flowAnalysis.variableRead(node, variable);
+    } else if (inferrer.isNonNullableByDefault && node.forNullGuardedAccess) {
+      DartType nonNullableType = variable.type.toNonNull();
+      if (nonNullableType != variable.type) {
+        promotedType = nonNullableType;
       }
-    } else {
-      bool mutatedInClosure = variable.mutatedInClosure;
-      promotedType = inferrer.typePromoter
-          .computePromotedType(node.fact, node.scope, mutatedInClosure);
+    } else if (!variable.isLocalFunction) {
+      // Don't promote local functions.
+      promotedType = inferrer.flowAnalysis.variableRead(node, variable);
     }
     if (promotedType != null) {
       inferrer.instrumentation?.record(
diff --git a/pkg/front_end/lib/src/fasta/kernel/internal_ast.dart b/pkg/front_end/lib/src/fasta/kernel/internal_ast.dart
index 35da3d2..46afae9 100644
--- a/pkg/front_end/lib/src/fasta/kernel/internal_ast.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/internal_ast.dart
@@ -40,14 +40,8 @@
 import '../type_inference/type_inference_engine.dart';
 import '../type_inference/type_inferrer.dart';
 
-import '../type_inference/type_promotion.dart'
-    show TypePromoter, TypePromoterImpl, TypePromotionFact, TypePromotionScope;
-
 import '../type_inference/type_schema.dart' show UnknownType;
 
-import '../type_inference/type_schema_environment.dart'
-    show TypeSchemaEnvironment;
-
 import 'inference_visitor.dart';
 
 /// Computes the return type of a (possibly factory) constructor.
@@ -1449,50 +1443,6 @@
   }
 }
 
-/// Concrete implementation of [TypePromoter] specialized to work with kernel
-/// objects.
-class ShadowTypePromoter extends TypePromoterImpl {
-  ShadowTypePromoter.private(TypeSchemaEnvironment typeSchemaEnvironment)
-      : super.private(typeSchemaEnvironment);
-
-  @override
-  int getVariableFunctionNestingLevel(VariableDeclaration variable) {
-    if (variable is VariableDeclarationImpl) {
-      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 isPromotionCandidate(VariableDeclaration variable) {
-    assert(variable is VariableDeclarationImpl);
-    VariableDeclarationImpl kernelVariableDeclaration = variable;
-    return !kernelVariableDeclaration.isLocalFunction;
-  }
-
-  @override
-  bool sameExpressions(Expression a, Expression b) {
-    return identical(a, b);
-  }
-
-  @override
-  void setVariableMutatedInClosure(VariableDeclaration variable) {
-    if (variable is VariableDeclarationImpl) {
-      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.
-    }
-  }
-}
-
 /// Front end specific implementation of [VariableDeclaration].
 class VariableDeclarationImpl extends VariableDeclaration {
   final bool forSyntheticToken;
@@ -1635,16 +1585,11 @@
 
 /// Front end specific implementation of [VariableGet].
 class VariableGetImpl extends VariableGet {
-  final TypePromotionFact fact;
-
-  final TypePromotionScope scope;
-
   // TODO(johnniwinther): Remove the need for this by encoding all null aware
   // expressions explicitly.
   final bool forNullGuardedAccess;
 
-  VariableGetImpl(VariableDeclaration variable, this.fact, this.scope,
-      {this.forNullGuardedAccess})
+  VariableGetImpl(VariableDeclaration variable, {this.forNullGuardedAccess})
       : assert(forNullGuardedAccess != null),
         super(variable);
 
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
index eafc3e4..aa896d9 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
@@ -59,8 +59,6 @@
 
 import 'type_inference_engine.dart';
 
-import 'type_promotion.dart' show TypePromoter;
-
 import 'type_schema.dart' show isKnown, UnknownType;
 
 import 'type_schema_elimination.dart' show greatestClosure;
@@ -123,10 +121,6 @@
 abstract class TypeInferrer {
   SourceLibraryBuilder get library;
 
-  /// Gets the [TypePromoter] that can be used to perform type promotion within
-  /// this method body or initializer.
-  TypePromoter get typePromoter;
-
   /// Gets the [TypeSchemaEnvironment] being used for type inference.
   TypeSchemaEnvironment get typeSchemaEnvironment;
 
@@ -183,9 +177,6 @@
 
   final TypeInferenceEngine engine;
 
-  @override
-  final TypePromoter typePromoter;
-
   final FlowAnalysis<TreeNode, Statement, Expression, VariableDeclaration,
       DartType> flowAnalysis;
 
@@ -227,10 +218,13 @@
         instrumentation = topLevel ? null : engine.instrumentation,
         typeSchemaEnvironment = engine.typeSchemaEnvironment,
         isTopLevel = topLevel,
-        typePromoter = new TypePromoter(engine.typeSchemaEnvironment),
-        flowAnalysis = new FlowAnalysis(
-            new TypeOperationsCfe(engine.typeSchemaEnvironment),
-            assignedVariables);
+        flowAnalysis = library.isNonNullableByDefault
+            ? new FlowAnalysis(
+                new TypeOperationsCfe(engine.typeSchemaEnvironment),
+                assignedVariables)
+            : new FlowAnalysis.legacy(
+                new TypeOperationsCfe(engine.typeSchemaEnvironment),
+                assignedVariables);
 
   CoreTypes get coreTypes => engine.coreTypes;
 
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_promotion.dart b/pkg/front_end/lib/src/fasta/type_inference/type_promotion.dart
deleted file mode 100644
index f94934e..0000000
--- a/pkg/front_end/lib/src/fasta/type_inference/type_promotion.dart
+++ /dev/null
@@ -1,704 +0,0 @@
-// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE.md file.
-
-// @dart = 2.9
-
-import 'package:kernel/ast.dart'
-    show DartType, Expression, TypeParameterType, VariableDeclaration;
-
-import 'package:kernel/type_environment.dart' show SubtypeCheckMode;
-
-import '../fasta_codes.dart' show templateInternalProblemStackNotEmpty;
-
-import '../problems.dart' show internalProblem;
-
-import '../kernel/internal_ast.dart' show ShadowTypePromoter;
-
-import 'type_schema_environment.dart' show TypeSchemaEnvironment;
-
-/// Keeps track of the state necessary to perform type promotion.
-///
-/// Theory of operation: during parsing, the BodyBuilder calls methods in this
-/// class to inform it of syntactic constructs that are encountered.  Those
-/// methods maintain a linked list of [TypePromotionFact] objects tracking what
-/// is known about the state of each variable at the current point in the code,
-/// as well as a linked list of [TypePromotionScope] objects tracking the
-/// component's nesting structure.  Whenever a variable is read, the current
-/// [TypePromotionFact] and [TypePromotionScope] are recorded for later use.
-///
-/// During type inference, the [TypeInferrer] calls back into this class to ask
-/// whether each variable read is a promoted read.  This is determined by
-/// examining the [TypePromotionScope] and [TypePromotionFact] objects that were
-/// recorded at the time the variable read was parsed, as well as other state
-/// that may have been updated later during the parsing process.
-///
-/// This class abstracts away the representation of the underlying AST using
-/// generic parameters.  Derived classes should set E and V to the class they
-/// use to represent expressions and variable declarations, respectively.
-abstract class TypePromoter {
-  TypePromoter.private();
-
-  factory TypePromoter(TypeSchemaEnvironment typeSchemaEnvironment) =
-      ShadowTypePromoter.private;
-
-  factory TypePromoter.disabled() = TypePromoterDisabled.private;
-
-  /// Returns the current type promotion scope.
-  TypePromotionScope get currentScope;
-
-  /// Computes the promoted type of a variable read having the given [fact] and
-  /// [scope].  Returns `null` if there is no promotion.
-  ///
-  /// [mutatedInClosure] indicates whether the variable was mutated in a closure
-  /// somewhere in the method.
-  DartType computePromotedType(
-      TypePromotionFact fact, TypePromotionScope scope, bool mutatedInClosure);
-
-  /// Updates the state to reflect the fact that we are entering an "else"
-  /// branch.
-  void enterElse();
-
-  /// Updates the state to reflect the fact that the LHS of an "&&" or "||"
-  /// expression has just been parsed, and we are entering the RHS.
-  void enterLogicalExpression(Expression lhs, String operator);
-
-  /// Updates the state to reflect the fact that the "condition" part of an "if"
-  /// statement or conditional expression has just been parsed, and we are
-  /// entering the "then" branch.
-  void enterThen(Expression condition);
-
-  /// Updates the state to reflect the fact that we have exited the "else"
-  /// branch of an "if" statement or conditional expression.
-  void exitConditional();
-
-  /// Updates the state to reflect the fact that we have exited the RHS of an
-  /// "&&" or "||" expression.
-  void exitLogicalExpression(Expression rhs, Expression logicalExpression);
-
-  /// Verifies that enter/exit calls were properly nested.
-  void finished();
-
-  /// Records that the given [variable] was accessed for reading, and returns a
-  /// [TypePromotionFact] describing the variable's current type promotion
-  /// state.
-  ///
-  /// [functionNestingLevel] should be the current nesting level of closures.
-  /// This is used to determine if the variable was accessed in a closure.
-  TypePromotionFact getFactForAccess(
-      VariableDeclaration variable, int functionNestingLevel);
-
-  /// Updates the state to reflect the fact that an "is" check of a local
-  /// variable was just parsed.
-  void handleIsCheck(Expression isExpression, bool isInverted,
-      VariableDeclaration variable, DartType type, int functionNestingLevel);
-
-  /// Updates the state to reflect the fact that the given [variable] was
-  /// mutated.
-  void mutateVariable(VariableDeclaration variable, int functionNestingLevel);
-}
-
-/// Implementation of [TypePromoter] which doesn't do any type promotion.
-///
-/// This is intended for profiling, to ensure that type inference and type
-/// promotion do not slow down compilation too much.
-class TypePromoterDisabled extends TypePromoter {
-  TypePromoterDisabled.private() : super.private();
-
-  @override
-  TypePromotionScope get currentScope => null;
-
-  @override
-  DartType computePromotedType(TypePromotionFact fact, TypePromotionScope scope,
-          bool mutatedInClosure) =>
-      null;
-
-  @override
-  void enterElse() {}
-
-  @override
-  void enterLogicalExpression(Expression lhs, String operator) {}
-
-  @override
-  void enterThen(Expression condition) {}
-
-  @override
-  void exitConditional() {}
-
-  @override
-  void exitLogicalExpression(Expression rhs, Expression logicalExpression) {}
-
-  @override
-  void finished() {}
-
-  @override
-  TypePromotionFact getFactForAccess(
-          VariableDeclaration variable, int functionNestingLevel) =>
-      null;
-
-  @override
-  void handleIsCheck(Expression isExpression, bool isInverted,
-      VariableDeclaration variable, DartType type, int functionNestingLevel) {}
-
-  @override
-  void mutateVariable(VariableDeclaration variable, int functionNestingLevel) {}
-}
-
-/// Derived class containing generic implementations of [TypePromoter].
-///
-/// This class contains as much of the implementation of type promotion as
-/// possible without needing access to private members of shadow objects.  It
-/// defers to abstract methods for everything else.
-abstract class TypePromoterImpl extends TypePromoter {
-  final TypeSchemaEnvironment typeSchemaEnvironment;
-
-  /// [TypePromotionFact] representing the initial state (no facts have been
-  /// determined yet).
-  ///
-  /// All linked lists of facts terminate in this object.
-  final _NullFact _nullFacts;
-
-  /// Map from variable declaration to the most recent [TypePromotionFact]
-  /// associated with the variable.
-  ///
-  /// [TypePromotionFact]s that are not associated with any variable show up in
-  /// this map under the key `null`.
-  final _factCache = <VariableDeclaration, TypePromotionFact>{};
-
-  /// Linked list of [TypePromotionFact]s that was current at the time the
-  /// [_factCache] was last updated.
-  TypePromotionFact _factCacheState;
-
-  /// Linked list of [TypePromotionFact]s describing what is known to be true
-  /// after execution of the expression or statement that was most recently
-  /// parsed.
-  TypePromotionFact _currentFacts;
-
-  /// The most recently parsed expression whose outcome potentially affects what
-  /// is known to be true (e.g. an "is" check or a logical expression).  May be
-  /// `null` if no such expression has been encountered yet.
-  Expression _promotionExpression;
-
-  /// Linked list of [TypePromotionFact]s describing what is known to be true
-  /// after execution of [_promotionExpression], assuming that
-  /// [_promotionExpression] evaluates to `true`.
-  TypePromotionFact _trueFactsForPromotionExpression;
-
-  /// Linked list of [TypePromotionScope]s describing the nesting structure that
-  /// contains the expression or statement that was most recently parsed.
-  TypePromotionScope _currentScope = const _TopLevelScope();
-
-  /// The sequence number of the [TypePromotionFact] that was most recently
-  /// created.
-  int _lastFactSequenceNumber = 0;
-
-  /// Map from variables to the set of scopes in which the variable is mutated.
-  /// If a variable is missing from the map, it is not mutated anywhere.
-  Map<VariableDeclaration, Set<TypePromotionScope>> _variableMutationScopes =
-      new Map<VariableDeclaration, Set<TypePromotionScope>>.identity();
-
-  TypePromoterImpl.private(TypeSchemaEnvironment typeSchemaEnvironment)
-      : this._(typeSchemaEnvironment, new _NullFact());
-
-  TypePromoterImpl._(this.typeSchemaEnvironment, _NullFact this._nullFacts)
-      : _factCacheState = _nullFacts,
-        _currentFacts = _nullFacts,
-        super.private() {
-    _factCache[null] = _nullFacts;
-  }
-
-  @override
-  TypePromotionScope get currentScope => _currentScope;
-
-  @override
-  DartType computePromotedType(
-      TypePromotionFact fact, TypePromotionScope scope, bool mutatedInClosure) {
-    if (mutatedInClosure) return null;
-    return fact?._computePromotedType(
-        this, scope, _variableMutationScopes[fact.variable]);
-  }
-
-  /// For internal debugging use, optionally prints the current state followed
-  /// by the event name.  Uncomment the call to [_printEvent] to see the
-  /// sequence of calls into the type promoter and the corresponding states.
-  void debugEvent(String name) {
-    // _printEvent(name);
-  }
-
-  @override
-  void enterElse() {
-    debugEvent('enterElse');
-    // Pop the scope and restore the facts to the state they were in before we
-    // entered the conditional.  No promotion happens in the "else" branch.
-    _ConditionalScope scope = _currentScope;
-    _currentScope = _currentScope._enclosing;
-    _currentFacts = scope.beforeElse;
-  }
-
-  @override
-  void enterLogicalExpression(Expression lhs, String operator) {
-    debugEvent('enterLogicalExpression');
-    if (!identical(operator, '&&')) {
-      // We don't promote for `||`.
-      _currentScope = new _LogicalScope(_currentScope, false, _currentFacts);
-    } else {
-      // Figure out what the facts are based on possible LHS outcomes.
-      TypePromotionFact trueFacts = _factsWhenTrue(lhs);
-      // Record the fact that we are entering a new scope, and save the
-      // appropriate facts for the case where the expression gets short-cut.
-      _currentScope = new _LogicalScope(_currentScope, true, _currentFacts);
-      // While processing the RHS, assume the condition was true.
-      _currentFacts = _addBlockingScopeToFacts(trueFacts);
-    }
-  }
-
-  @override
-  void enterThen(Expression condition) {
-    debugEvent('enterThen');
-    // Figure out what the facts are based on possible condition outcomes.
-    TypePromotionFact trueFacts = _factsWhenTrue(condition);
-    // Record the fact that we are entering a new scope, and save the current
-    // facts for when we enter the "else" branch.
-    _currentScope = new _ConditionalScope(_currentScope, _currentFacts);
-    // While processing the "then" block, assume the condition was true.
-    _currentFacts = _addBlockingScopeToFacts(trueFacts);
-  }
-
-  @override
-  void exitConditional() {
-    debugEvent('exitConditional');
-  }
-
-  @override
-  void exitLogicalExpression(Expression rhs, Expression logicalExpression) {
-    debugEvent('exitLogicalExpression');
-    _LogicalScope scope = _currentScope;
-    if (scope.isAnd) {
-      TypePromotionFact factsWhenTrue = _factsWhenTrue(rhs);
-      _currentFacts = scope.shortcutFacts;
-      _recordPromotionExpression(
-          logicalExpression, _addBlockingScopeToFacts(factsWhenTrue));
-    }
-    _currentScope = _currentScope._enclosing;
-  }
-
-  @override
-  void finished() {
-    debugEvent('finished');
-    if (_currentScope is! _TopLevelScope) {
-      internalProblem(
-          templateInternalProblemStackNotEmpty.withArguments(
-              "$runtimeType", "$_currentScope"),
-          -1,
-          null);
-    }
-  }
-
-  @override
-  TypePromotionFact getFactForAccess(
-      VariableDeclaration variable, int functionNestingLevel) {
-    debugEvent('getFactForAccess');
-    TypePromotionFact fact = _computeCurrentFactMap()[variable];
-    TypePromotionFact._recordAccessedInScope(
-        fact, _currentScope, functionNestingLevel);
-    return fact;
-  }
-
-  /// Returns the nesting level that was in effect when [variable] was declared.
-  int getVariableFunctionNestingLevel(VariableDeclaration variable);
-
-  @override
-  void handleIsCheck(Expression isExpression, bool isInverted,
-      VariableDeclaration variable, DartType type, int functionNestingLevel) {
-    debugEvent('handleIsCheck');
-    if (!isPromotionCandidate(variable)) return;
-    _IsCheck isCheck = new _IsCheck(
-        ++_lastFactSequenceNumber,
-        variable,
-        _currentFacts,
-        _computeCurrentFactMap()[variable],
-        functionNestingLevel,
-        type, []);
-    if (!isInverted) {
-      _recordPromotionExpression(isExpression, isCheck);
-    }
-  }
-
-  /// Determines whether the given variable should be considered for promotion
-  /// at all.
-  ///
-  /// This is needed because in kernel, [VariableDeclaration] objects are
-  /// sometimes used to represent local functions, which are not subject to
-  /// promotion.
-  bool isPromotionCandidate(VariableDeclaration variable);
-
-  /// Updates the state to reflect the fact that the given [variable] was
-  /// mutated.
-  void mutateVariable(VariableDeclaration variable, int functionNestingLevel) {
-    debugEvent('mutateVariable');
-    (_variableMutationScopes[variable] ??=
-            new Set<TypePromotionScope>.identity())
-        .add(_currentScope);
-    if (getVariableFunctionNestingLevel(variable) < functionNestingLevel) {
-      setVariableMutatedInClosure(variable);
-    }
-  }
-
-  /// Determines whether [a] and [b] represent the same expression, after
-  /// dropping redundant enclosing parentheses.
-  bool sameExpressions(Expression a, Expression b);
-
-  /// Records that the given variable was mutated inside a closure.
-  void setVariableMutatedInClosure(VariableDeclaration variable);
-
-  /// Updates any facts that are present in [facts] but not in [_currentFacts]
-  /// so that they include [_currentScope] in their list of blocking scopes, and
-  /// returns the resulting new linked list of facts.
-  ///
-  /// This is used when entering the body of a conditional, or the RHS of a
-  /// logical "and", to ensure that promotions are blocked if the construct
-  /// being entered contains any modifications of the corresponding variables.
-  /// It is also used when leaving the RHS of a logical "and", to ensure that
-  /// any promotions induced by the RHS of the "and" are blocked if the RHS of
-  /// the "and" contains any modifications of the corresponding variables.
-  TypePromotionFact _addBlockingScopeToFacts(TypePromotionFact facts) {
-    List<TypePromotionFact> factsToUpdate = [];
-    while (facts != _currentFacts) {
-      factsToUpdate.add(facts);
-      facts = facts.previous;
-    }
-    Map<VariableDeclaration, TypePromotionFact> factMap =
-        _computeCurrentFactMap();
-    for (TypePromotionFact fact in factsToUpdate.reversed) {
-      _IsCheck isCheck = fact as _IsCheck;
-      VariableDeclaration variable = isCheck.variable;
-      facts = new _IsCheck(
-          ++_lastFactSequenceNumber,
-          variable,
-          facts,
-          factMap[variable],
-          isCheck.functionNestingLevel,
-          isCheck.checkedType,
-          [...isCheck._blockingScopes, _currentScope]);
-      factMap[variable] = facts;
-      _factCacheState = facts;
-    }
-    return facts;
-  }
-
-  /// Returns a map from variable declaration to the most recent
-  /// [TypePromotionFact] associated with the variable.
-  Map<VariableDeclaration, TypePromotionFact> _computeCurrentFactMap() {
-    // Roll back any map entries associated with facts that are no longer in
-    // effect, and set [commonAncestor] to the fact that is an ancestor of
-    // the current state and the previously cached state.  To do this, we set a
-    // variable pointing to [_currentFacts], and then walk both it and
-    // [_factCacheState] back to their common ancestor, updating [_factCache] as
-    // we go.
-    TypePromotionFact commonAncestor = _currentFacts;
-    while (commonAncestor.sequenceNumber != _factCacheState.sequenceNumber) {
-      if (commonAncestor.sequenceNumber > _factCacheState.sequenceNumber) {
-        // The currently cached state is older than the common ancestor guess,
-        // so the common ancestor guess needs to be walked back.
-        commonAncestor = commonAncestor.previous;
-      } else {
-        // The common ancestor guess is older than the currently cached state,
-        // so we need to roll back the map entry associated with the currently
-        // cached state.
-        _factCache[_factCacheState.variable] =
-            _factCacheState.previousForVariable;
-        _factCacheState = _factCacheState.previous;
-      }
-    }
-    assert(identical(commonAncestor, _factCacheState));
-    // Roll forward any map entries associated with facts that are newly in
-    // effect.  Since newer facts link to older ones, it is easiest to do roll
-    // forward the most recent facts first.
-    for (TypePromotionFact newState = _currentFacts;
-        !identical(newState, commonAncestor);
-        newState = newState.previous) {
-      TypePromotionFact currentlyCached = _factCache[newState.variable];
-      // Note: Since we roll forward the most recent facts first, we need to be
-      // careful not write an older fact over a newer one.
-      if (currentlyCached == null ||
-          newState.sequenceNumber > currentlyCached.sequenceNumber) {
-        _factCache[newState.variable] = newState;
-      }
-    }
-    _factCacheState = _currentFacts;
-    return _factCache;
-  }
-
-  /// Returns the set of facts known to be true after the execution of [e]
-  /// assuming it evaluates to `true`.
-  ///
-  /// [e] must be the most recently parsed expression or statement.
-  TypePromotionFact _factsWhenTrue(Expression e) =>
-      sameExpressions(_promotionExpression, e)
-          ? _trueFactsForPromotionExpression
-          : _currentFacts;
-
-  /// For internal debugging use, prints the current state followed by the event
-  /// name.
-  // ignore: unused_element
-  void _printEvent(String name) {
-    Iterable<TypePromotionFact> factChain(TypePromotionFact fact) sync* {
-      while (fact != null) {
-        yield fact;
-        fact = fact.previousForVariable;
-      }
-    }
-
-    _computeCurrentFactMap().forEach((variable, fact) {
-      if (fact == null) return;
-      print('  ${variable ?? '(null)'}: ${factChain(fact).join(' -> ')}');
-    });
-    if (_promotionExpression != null) {
-      print('  _promotionExpression: $_promotionExpression');
-      if (!identical(_trueFactsForPromotionExpression, _currentFacts)) {
-        print('    if true: '
-            '${factChain(_trueFactsForPromotionExpression).join(' -> ')}');
-      }
-    }
-    print(name);
-  }
-
-  /// Records that after the evaluation of [expression], the facts will be
-  /// [ifTrue] on a branch where the expression evaluated to `true`.
-  void _recordPromotionExpression(
-      Expression expression, TypePromotionFact ifTrue) {
-    _promotionExpression = expression;
-    _trueFactsForPromotionExpression = ifTrue;
-  }
-}
-
-/// A single fact which is known to the type promotion engine about the state of
-/// a variable (or about the flow control of the component).
-///
-/// The type argument V represents is the class which represents local variable
-/// declarations.
-///
-/// Facts are linked together into linked lists via the [previous] pointer into
-/// a data structure called a "fact chain" (or sometimes a "fact state"), which
-/// represents all facts that are known to hold at a certain point in the
-/// component.
-///
-/// The fact is said to "apply" to a given point in the execution of the
-/// component if the fact is part of the current fact state at the point the
-/// parser reaches that point in the component.
-///
-/// Note: just because a fact "applies" to a given point in the execution of
-/// the component doesn't mean a type will be promoted--it simply means that
-/// the fact was deduced at a previous point in the straight line execution of
-/// the code.  It's possible that the fact will be overshadowed by a later
-/// fact, or its effect will be cancelled by a later assignment.  The final
-/// determination of whether promotion occurs is left to [_computePromotedType].
-abstract class TypePromotionFact {
-  /// The variable this fact records information about, or `null` if this fact
-  /// records information about general flow control.
-  final VariableDeclaration variable;
-
-  /// The fact chain that was in effect prior to execution of the statement or
-  /// expression that caused this fact to be true.
-  final TypePromotionFact previous;
-
-  /// Integer associated with this fact.  Each time a fact is created it is
-  /// given a sequence number one greater than the previously generated fact.
-  /// This simplifies the algorithm for finding the common ancestor of two
-  /// facts; we repeatedly walk backward the fact with the larger sequence
-  /// number until the sequence numbers are the same.
-  final int sequenceNumber;
-
-  /// The most recent fact appearing in the fact chain [previous] whose
-  /// [variable] matches this one, or `null` if there is no such fact.
-  final TypePromotionFact previousForVariable;
-
-  /// The function nesting level of the expression that led to this fact.
-  final int functionNestingLevel;
-
-  /// Indicates whether this fact's variable was accessed inside a closure
-  /// within the scope the fact applies to.
-  bool _accessedInClosureInScope = false;
-
-  TypePromotionFact(this.sequenceNumber, this.variable, this.previous,
-      this.previousForVariable, this.functionNestingLevel);
-
-  /// Computes the promoted type for [variable] at a location in the code where
-  /// this fact applies.
-  ///
-  /// [scope] is the scope containing the read that might be promoted, and
-  /// [mutationScopes] is the set of scopes in which the variable is mutated, or
-  /// `null` if the variable isn't mutated anywhere.
-  ///
-  /// Should not be called until after parsing of the entire method is complete.
-  DartType _computePromotedType(TypePromoterImpl promoter,
-      TypePromotionScope scope, Iterable<TypePromotionScope> mutationScopes);
-
-  /// Records the fact that the variable referenced by [fact] was accessed
-  /// within the given scope, at the given function nesting level.
-  ///
-  /// If `null` is passed in for [fact], there is no effect.
-  static void _recordAccessedInScope(TypePromotionFact fact,
-      TypePromotionScope scope, int functionNestingLevel) {
-    // TODO(paulberry): make some integration test cases that exercise the
-    // behaviors of this function.  In particular verify that it's correct to
-    // test functionNestingLevel against fact.functionNestingLevel (as opposed
-    // to testing it against getVariableFunctionNestingLevel(variable)).
-    while (fact != null) {
-      if (functionNestingLevel > fact.functionNestingLevel) {
-        if (fact._accessedInClosureInScope) {
-          // The variable has already been accessed in a closure in the scope of
-          // the current promotion (and this, any enclosing promotions), so
-          // no further information needs to be updated.
-          return;
-        }
-        fact._accessedInClosureInScope = true;
-      }
-      fact = fact.previousForVariable;
-    }
-  }
-}
-
-/// Represents a contiguous block of program text in which variables may or may
-/// not be promoted.  Also used as a stack to keep track of state while the
-/// method is being parsed.
-class TypePromotionScope {
-  /// The nesting depth of this scope.  The outermost scope (representing the
-  /// whole method body) has a depth of 0.
-  final int _depth;
-
-  /// The [TypePromotionScope] representing the scope enclosing this one.
-  final TypePromotionScope _enclosing;
-
-  TypePromotionScope(this._enclosing) : _depth = _enclosing._depth + 1;
-
-  const TypePromotionScope._topLevel()
-      : _enclosing = null,
-        _depth = 0;
-
-  /// Determines whether this scope completely encloses (or is the same as)
-  /// [other].
-  bool containsScope(TypePromotionScope other) {
-    if (this._depth > other._depth) {
-      // We can't possibly contain a scope if we are at greater nesting depth
-      // than it is.
-      return false;
-    }
-    while (this._depth < other._depth) {
-      other = other._enclosing;
-    }
-    return identical(this, other);
-  }
-}
-
-/// [TypePromotionScope] representing the "then" and "else" bodies of an "if"
-/// statement or conditional expression.
-class _ConditionalScope extends TypePromotionScope {
-  /// The fact state in effect at the top of the "else" block.
-  final TypePromotionFact beforeElse;
-
-  _ConditionalScope(TypePromotionScope enclosing, this.beforeElse)
-      : super(enclosing);
-}
-
-/// [TypePromotionFact] representing an "is" check which succeeded.
-class _IsCheck extends TypePromotionFact {
-  /// The type appearing on the right hand side of "is".
-  final DartType checkedType;
-
-  /// List of the scopes in which a mutation to the variable would block
-  /// promotion.
-  final List<TypePromotionScope> _blockingScopes;
-
-  _IsCheck(
-      int sequenceNumber,
-      VariableDeclaration variable,
-      TypePromotionFact previous,
-      TypePromotionFact previousForVariable,
-      int functionNestingLevel,
-      this.checkedType,
-      this._blockingScopes)
-      : super(sequenceNumber, variable, previous, previousForVariable,
-            functionNestingLevel);
-
-  @override
-  String toString() => 'isCheck($checkedType)';
-
-  @override
-  DartType _computePromotedType(TypePromoterImpl promoter,
-      TypePromotionScope scope, Iterable<TypePromotionScope> mutationScopes) {
-    DartType previousPromotedType = previousForVariable?._computePromotedType(
-        promoter, scope, mutationScopes);
-
-    if (mutationScopes != null) {
-      // If the variable was mutated somewhere in a that blocks the promotion,
-      // promotion does not occur.
-      for (TypePromotionScope blockingScope in _blockingScopes) {
-        for (TypePromotionScope mutationScope in mutationScopes) {
-          if (blockingScope.containsScope(mutationScope)) {
-            return previousPromotedType;
-          }
-        }
-      }
-
-      // If the variable was mutated anywhere, and it was accessed inside a
-      // closure somewhere in the scope of the potential promotion, promotion
-      // does not occur.
-      if (_accessedInClosureInScope) {
-        return previousPromotedType;
-      }
-    }
-
-    // What we do now depends on the relationship between the previous type of
-    // the variable and the type we are checking against.
-    DartType previousType = previousPromotedType ?? variable.type;
-    if (promoter.typeSchemaEnvironment.isSubtypeOf(
-        checkedType, previousType, SubtypeCheckMode.withNullabilities)) {
-      // The type we are checking against is a subtype of the previous type of
-      // the variable, so this is a refinement; we can promote.
-      return checkedType;
-    } else if (previousType is TypeParameterType &&
-        promoter.typeSchemaEnvironment.isSubtypeOf(checkedType,
-            previousType.bound, SubtypeCheckMode.withNullabilities)) {
-      // The type we are checking against is a subtype of the bound of the
-      // previous type of the variable; we can promote the bound.
-      return new TypeParameterType.intersection(
-          previousType.parameter, previousType.nullability, checkedType);
-    } else {
-      // The types aren't sufficiently related; we can't promote.
-      return previousPromotedType;
-    }
-  }
-}
-
-/// [TypePromotionScope] representing the RHS of a logical expression.
-class _LogicalScope extends TypePromotionScope {
-  /// Indicates whether the logical operation is an `&&` or an `||`.
-  final bool isAnd;
-
-  /// The fact state in effect if the logical expression gets short-cut.
-  final TypePromotionFact shortcutFacts;
-
-  _LogicalScope(TypePromotionScope enclosing, this.isAnd, this.shortcutFacts)
-      : super(enclosing);
-}
-
-/// Instance of [TypePromotionFact] representing the facts which are known on
-/// entry to the method (i.e. nothing).
-class _NullFact extends TypePromotionFact {
-  _NullFact() : super(0, null, null, null, 0);
-
-  @override
-  String toString() => 'null';
-
-  @override
-  DartType _computePromotedType(TypePromoter promoter, TypePromotionScope scope,
-      Iterable<TypePromotionScope> mutationScopes) {
-    throw new StateError('Tried to create promoted type for no variable');
-  }
-}
-
-/// Instance of [TypePromotionScope] representing the entire method body.
-class _TopLevelScope extends TypePromotionScope {
-  const _TopLevelScope() : super._topLevel();
-}
diff --git a/pkg/front_end/test/text_representation/internal_ast_text_representation_test.dart b/pkg/front_end/test/text_representation/internal_ast_text_representation_test.dart
index b51b99d..543a79f 100644
--- a/pkg/front_end/test/text_representation/internal_ast_text_representation_test.dart
+++ b/pkg/front_end/test/text_representation/internal_ast_text_representation_test.dart
@@ -511,15 +511,12 @@
 
 void _testVariableGetImpl() {
   VariableDeclaration variable = new VariableDeclaration('foo');
-  testExpression(
-      new VariableGetImpl(variable, null, null, forNullGuardedAccess: false),
-      '''
+  testExpression(new VariableGetImpl(variable, forNullGuardedAccess: false), '''
+foo''');
+  testExpression(new VariableGetImpl(variable, forNullGuardedAccess: true), '''
 foo''');
   testExpression(
-      new VariableGetImpl(variable, null, null, forNullGuardedAccess: true), '''
-foo''');
-  testExpression(
-      new VariableGetImpl(variable, null, null, forNullGuardedAccess: false)
+      new VariableGetImpl(variable, forNullGuardedAccess: false)
         ..promotedType = const VoidType(),
       '''
 foo{void}''');
diff --git a/pkg/vm/testcases/transformations/type_flow/summary_collector/bool_expressions.dart.expect b/pkg/vm/testcases/transformations/type_flow/summary_collector/bool_expressions.dart.expect
index 96eea1d..625d97f 100644
--- a/pkg/vm/testcases/transformations/type_flow/summary_collector/bool_expressions.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/summary_collector/bool_expressions.dart.expect
@@ -14,7 +14,7 @@
 t6* = _Call direct [#lib::bar] ()
 t7* = _Call direct [#lib::foo] ()
 t8 = _Join [dynamic] (_T (dart.core::bool, true), t7)
-t9 = _TypeCheck (t8 against dart.core::bool*) (for (x{dart.core::bool*} ?{dynamic} true : #lib::foo()) as dart.core::bool*)
+t9 = _TypeCheck (t8 against dart.core::bool*) (for (x ?{dynamic} true : #lib::foo()) as dart.core::bool*)
 t10* = _Call direct [#lib::bar] ()
 t11* = _Call direct [#lib::bar] ()
 t12* = _Call direct [#lib::foo] ()