Version 2.18.0-275.0.dev

Merge commit '33295f8247d4dfc259f1a275598434316db34a16' into 'dev'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cfad1f4..acadd9a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -57,6 +57,27 @@
   collected earlier than they were before
   (see issue [#36983](https://github.com/dart-lang/sdk/issues/36983) for details).
 
+
+### Tools
+
+#### Dart command line
+
+#### Linter
+
+Updated the Linter to `1.26.0`, which includes changes that
+
+- add new lint: `combinators_ordering`.
+- fix `use_colored_box` and `use_decorated_box` to not over-report on containers without
+  a child.
+- fix `unnecessary_parenthesis` false positives on a map-or-set literal at the start of
+  an expression statement.
+- fix `prefer_final_locals` false positives reporting on fields.
+- fix `unnecessary_overrides` to allow overrides on `@Protected`members.
+- fix `avoid_multiple_declarations_per_line` false positives in `for` statements.
+- fix `prefer_final_locals` false positives on declaration lists with at least one
+  non-final variable.
+- fix`use_build_context_synchronously` to handle `await`s in `if` conditions.
+
 ## 2.18.0
 
 ### Language
diff --git a/DEPS b/DEPS
index 4a6f2e9..f7fc977 100644
--- a/DEPS
+++ b/DEPS
@@ -122,7 +122,7 @@
   "intl_rev": "e9b573679de5e703d89a242b9dca331c772979ef",
   "jinja2_rev": "2222b31554f03e62600cd7e383376a7c187967a1",
   "json_rpc_2_rev": "805e6536dd961d66f6b8cd46d8f3e61774f957c9",
-  "linter_rev": "1ddc70948d94f2449fec69a95e3ceb7b6b6c8348", # manually rev'd
+  "linter_rev": "b4afc10055f3009478da1eac1827f9fef42c3759", # 1.26.0
   "lints_rev": "8294e5648ab49474541527e2911e72e4c5aefe55",
   "logging_rev": "f6979e3bc3b6e1847a08335b7eb6304e18986195",
   "markdown_rev": "e3f4bd28c9e61b522f75f291d4d6cfcfeccd83ee", # b/236358256
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 fbcedf8..ced5a9b 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
@@ -26,27 +26,29 @@
 /// closure.
 class AssignedVariables<Node extends Object, Variable extends Object> {
   /// Mapping from a node to the info for that node.
-  final Map<Node, AssignedVariablesNodeInfo<Variable>> _info =
-      new Map<Node, AssignedVariablesNodeInfo<Variable>>.identity();
+  final Map<Node, AssignedVariablesNodeInfo> _info =
+      new Map<Node, AssignedVariablesNodeInfo>.identity();
 
   /// Info for the variables written or captured anywhere in the code being
   /// analyzed.
-  final AssignedVariablesNodeInfo<Variable> _anywhere =
-      new AssignedVariablesNodeInfo<Variable>();
+  final AssignedVariablesNodeInfo _anywhere = new AssignedVariablesNodeInfo();
 
   /// Stack of info for nodes that have been entered but not yet left.
-  final List<AssignedVariablesNodeInfo<Variable>> _stack = [
-    new AssignedVariablesNodeInfo<Variable>()
+  final List<AssignedVariablesNodeInfo> _stack = [
+    new AssignedVariablesNodeInfo()
   ];
 
   /// When assertions are enabled, the set of info objects that have been
   /// retrieved by [deferNode] but not yet sent to [storeNode].
-  final Set<AssignedVariablesNodeInfo<Variable>> _deferredInfos =
-      new Set<AssignedVariablesNodeInfo<Variable>>.identity();
+  final Set<AssignedVariablesNodeInfo> _deferredInfos =
+      new Set<AssignedVariablesNodeInfo>.identity();
 
   /// Keeps track of whether [finish] has been called.
   bool _isFinished = false;
 
+  final PromotionKeyStore<Variable> _promotionKeyStore =
+      new PromotionKeyStore<Variable>();
+
   /// This method should be called during pre-traversal, to mark the start of a
   /// loop statement, switch statement, try statement, loop collection element,
   /// local function, closure, or late variable initializer which might need to
@@ -60,7 +62,7 @@
   /// switch expression should not.
   void beginNode() {
     assert(!_isFinished);
-    _stack.add(new AssignedVariablesNodeInfo<Variable>());
+    _stack.add(new AssignedVariablesNodeInfo());
   }
 
   /// This method should be called during pre-traversal, to indicate that the
@@ -70,8 +72,9 @@
   /// is to allow for error recovery in the analyzer).
   void declare(Variable variable) {
     assert(!_isFinished);
-    _stack.last._declared.add(variable);
-    _anywhere._declared.add(variable);
+    int variableKey = _promotionKeyStore.keyForVariable(variable);
+    _stack.last._declared.add(variableKey);
+    _anywhere._declared.add(variableKey);
   }
 
   /// This method may be called during pre-traversal, to mark the end of a
@@ -87,15 +90,15 @@
   /// time, the caller should pass the returned data to [storeNodeInfo].
   ///
   /// See [beginNode] for more details.
-  AssignedVariablesNodeInfo<Variable> deferNode(
+  AssignedVariablesNodeInfo deferNode(
       {bool isClosureOrLateVariableInitializer: false}) {
     assert(!_isFinished);
-    AssignedVariablesNodeInfo<Variable> info = _stack.removeLast();
+    AssignedVariablesNodeInfo info = _stack.removeLast();
     info._read.removeAll(info._declared);
     info._written.removeAll(info._declared);
     info._readCaptured.removeAll(info._declared);
     info._captured.removeAll(info._declared);
-    AssignedVariablesNodeInfo<Variable> last = _stack.last;
+    AssignedVariablesNodeInfo last = _stack.last;
     last._read.addAll(info._read);
     last._written.addAll(info._written);
     last._readCaptured.addAll(info._readCaptured);
@@ -124,8 +127,8 @@
   /// calls.
   void discardNode() {
     assert(!_isFinished);
-    AssignedVariablesNodeInfo<Variable> discarded = _stack.removeLast();
-    AssignedVariablesNodeInfo<Variable> last = _stack.last;
+    AssignedVariablesNodeInfo discarded = _stack.removeLast();
+    AssignedVariablesNodeInfo last = _stack.last;
     last._declared.addAll(discarded._declared);
     last._read.addAll(discarded._read);
     last._written.addAll(discarded._written);
@@ -161,15 +164,14 @@
       assert(
           _deferredInfos.isEmpty, "Deferred infos not stored: $_deferredInfos");
       assert(_stack.length == 1, "Unexpected stack: $_stack");
-      AssignedVariablesNodeInfo<Variable> last = _stack.last;
-      Set<Variable> undeclaredReads = last._read.difference(last._declared);
+      AssignedVariablesNodeInfo last = _stack.last;
+      Set<int> undeclaredReads = last._read.difference(last._declared);
       assert(undeclaredReads.isEmpty,
           'Variables read from but not declared: $undeclaredReads');
-      Set<Variable> undeclaredWrites = last._written.difference(last._declared);
+      Set<int> undeclaredWrites = last._written.difference(last._declared);
       assert(undeclaredWrites.isEmpty,
           'Variables written to but not declared: $undeclaredWrites');
-      Set<Variable> undeclaredCaptures =
-          last._captured.difference(last._declared);
+      Set<int> undeclaredCaptures = last._captured.difference(last._declared);
       assert(undeclaredCaptures.isEmpty,
           'Variables captured but not declared: $undeclaredCaptures');
       return true;
@@ -184,21 +186,22 @@
   /// This is used by the front end when building for-elements in lists, maps,
   /// and sets; their initializers are partially built after building their
   /// loop conditions but before completely building their bodies.
-  AssignedVariablesNodeInfo<Variable> popNode() {
+  AssignedVariablesNodeInfo popNode() {
     assert(!_isFinished);
     return _stack.removeLast();
   }
 
   /// Call this method to un-do the effect of [popNode].
-  void pushNode(AssignedVariablesNodeInfo<Variable> node) {
+  void pushNode(AssignedVariablesNodeInfo node) {
     assert(!_isFinished);
     _stack.add(node);
   }
 
   void read(Variable variable) {
     assert(!_isFinished);
-    _stack.last._read.add(variable);
-    _anywhere._read.add(variable);
+    int variableKey = _promotionKeyStore.keyForVariable(variable);
+    _stack.last._read.add(variableKey);
+    _anywhere._read.add(variableKey);
   }
 
   /// Call this method to register that the node [from] for which information
@@ -207,7 +210,7 @@
   // general elements in the front-end.
   void reassignInfo(Node from, Node to) {
     assert(!_info.containsKey(to), "Node $to already has info: ${_info[to]}");
-    AssignedVariablesNodeInfo<Variable>? info = _info.remove(from);
+    AssignedVariablesNodeInfo? info = _info.remove(from);
     assert(
         info != null,
         'No information for $from (${from.hashCode}) in '
@@ -218,7 +221,7 @@
 
   /// This method may be called at any time between a call to [deferNode] and
   /// the call to [finish], to store assigned variable info for the node.
-  void storeInfo(Node node, AssignedVariablesNodeInfo<Variable> info) {
+  void storeInfo(Node node, AssignedVariablesNodeInfo info) {
     assert(!_isFinished);
     // Caller should not try to store the same piece of info more than once.
     assert(_deferredInfos.remove(info));
@@ -237,12 +240,13 @@
   /// variable.
   void write(Variable variable) {
     assert(!_isFinished);
-    _stack.last._written.add(variable);
-    _anywhere._written.add(variable);
+    int variableKey = _promotionKeyStore.keyForVariable(variable);
+    _stack.last._written.add(variableKey);
+    _anywhere._written.add(variableKey);
   }
 
   /// Queries the information stored for the given [node].
-  AssignedVariablesNodeInfo<Variable> _getInfoForNode(Node node) {
+  AssignedVariablesNodeInfo _getInfoForNode(Node node) {
     return _info[node] ??
         (throw new StateError('No information for $node (${node.hashCode}) in '
             '{${_info.keys.map((k) => '$k (${k.hashCode})').join(',')}}'));
@@ -260,26 +264,28 @@
 /// Not intended to be used by clients of flow analysis.
 class AssignedVariablesForTesting<Node extends Object, Variable extends Object>
     extends AssignedVariables<Node, Variable> {
-  Set<Variable> get capturedAnywhere => _anywhere._captured;
+  Set<int> get capturedAnywhere => _anywhere._captured;
 
-  Set<Variable> get declaredAtTopLevel => _stack.first._declared;
+  Set<int> get declaredAtTopLevel => _stack.first._declared;
 
-  Set<Variable> get readAnywhere => _anywhere._read;
+  Set<int> get readAnywhere => _anywhere._read;
 
-  Set<Variable> get readCapturedAnywhere => _anywhere._readCaptured;
+  Set<int> get readCapturedAnywhere => _anywhere._readCaptured;
 
-  Set<Variable> get writtenAnywhere => _anywhere._written;
+  Set<int> get writtenAnywhere => _anywhere._written;
 
-  Set<Variable> capturedInNode(Node node) => _getInfoForNode(node)._captured;
+  Set<int> capturedInNode(Node node) => _getInfoForNode(node)._captured;
 
-  Set<Variable> declaredInNode(Node node) => _getInfoForNode(node)._declared;
+  Set<int> declaredInNode(Node node) => _getInfoForNode(node)._declared;
 
   bool isTracked(Node node) => _info.containsKey(node);
 
-  Set<Variable> readCapturedInNode(Node node) =>
-      _getInfoForNode(node)._readCaptured;
+  int keyForVariable(Variable variable) =>
+      _promotionKeyStore.keyForVariable(variable);
 
-  Set<Variable> readInNode(Node node) => _getInfoForNode(node)._read;
+  Set<int> readCapturedInNode(Node node) => _getInfoForNode(node)._readCaptured;
+
+  Set<int> readInNode(Node node) => _getInfoForNode(node)._read;
 
   String toString() {
     StringBuffer sb = new StringBuffer();
@@ -289,24 +295,26 @@
     return sb.toString();
   }
 
-  Set<Variable> writtenInNode(Node node) => _getInfoForNode(node)._written;
+  Variable variableForKey(int key) => _promotionKeyStore.variableForKey(key);
+
+  Set<int> writtenInNode(Node node) => _getInfoForNode(node)._written;
 }
 
 /// Information tracked by [AssignedVariables] for a single node.
-class AssignedVariablesNodeInfo<Variable extends Object> {
-  final Set<Variable> _read = new Set<Variable>.identity();
+class AssignedVariablesNodeInfo {
+  final Set<int> _read = {};
 
   /// The set of local variables that are potentially written in the node.
-  final Set<Variable> _written = new Set<Variable>.identity();
+  final Set<int> _written = {};
 
-  final Set<Variable> _readCaptured = new Set<Variable>.identity();
+  final Set<int> _readCaptured = {};
 
   /// The set of local variables for which a potential write is captured by a
   /// local function or closure inside the node.
-  final Set<Variable> _captured = new Set<Variable>.identity();
+  final Set<int> _captured = {};
 
   /// The set of local variables that are declared in the node.
-  final Set<Variable> _declared = new Set<Variable>.identity();
+  final Set<int> _declared = {};
 
   String toString() =>
       'AssignedVariablesNodeInfo(_written=$_written, _captured=$_captured, '
@@ -346,17 +354,17 @@
 
 /// Information gathered by flow analysis about an argument to either
 /// `identical` or `operator ==`.
-class EqualityInfo<Variable extends Object, Type extends Object> {
+class EqualityInfo<Type extends Object> {
   /// The [ExpressionInfo] for the expression.  This is used to determine
   /// whether the expression is a `null` literal.
-  final ExpressionInfo<Variable, Type>? _expressionInfo;
+  final ExpressionInfo<Type>? _expressionInfo;
 
   /// The type of the expression on the LHS of `==` or `!=`.
   final Type _type;
 
   /// If the LHS of `==` or `!=` is a reference, the thing being referred to.
   /// Otherwise `null`.
-  final ReferenceWithType<Variable, Type>? _reference;
+  final ReferenceWithType<Type>? _reference;
 
   EqualityInfo._(this._expressionInfo, this._type, this._reference);
 
@@ -368,27 +376,26 @@
 
 /// A collection of flow models representing the possible outcomes of evaluating
 /// an expression that are relevant to flow analysis.
-class ExpressionInfo<Variable extends Object, Type extends Object> {
+class ExpressionInfo<Type extends Object> {
   /// The state after the expression evaluates, if we don't care what it
   /// evaluates to.
-  final FlowModel<Variable, Type> after;
+  final FlowModel<Type> after;
 
   /// The state after the expression evaluates, if it evaluates to `true`.
-  final FlowModel<Variable, Type> ifTrue;
+  final FlowModel<Type> ifTrue;
 
   /// The state after the expression evaluates, if it evaluates to `false`.
-  final FlowModel<Variable, Type> ifFalse;
+  final FlowModel<Type> ifFalse;
 
   ExpressionInfo(this.after, this.ifTrue, this.ifFalse);
 
   /// Computes a new [ExpressionInfo] based on this one, but with the roles of
   /// [ifTrue] and [ifFalse] reversed.
-  ExpressionInfo<Variable, Type> invert() =>
-      new ExpressionInfo<Variable, Type>(after, ifFalse, ifTrue);
+  ExpressionInfo<Type> invert() =>
+      new ExpressionInfo<Type>(after, ifFalse, ifTrue);
 
-  ExpressionInfo<Variable, Type>? rebaseForward(
-          TypeOperations<Type> typeOperations,
-          FlowModel<Variable, Type> base) =>
+  ExpressionInfo<Type>? rebaseForward(
+          TypeOperations<Type> typeOperations, FlowModel<Type> base) =>
       new ExpressionInfo(base, ifTrue.rebaseForward(typeOperations, base),
           ifFalse.rebaseForward(typeOperations, base));
 
@@ -509,25 +516,22 @@
   ///
   /// Note: the return type is nullable because legacy type promotion doesn't
   /// need to record information about equality operands.
-  EqualityInfo<Variable, Type>? equalityOperand_end(
-      Expression operand, Type type);
+  EqualityInfo<Type>? equalityOperand_end(Expression operand, Type type);
 
   /// Call this method just after visiting the operands of a binary `==` or `!=`
   /// expression, or an invocation of `identical`.
   ///
   /// [leftOperandInfo] and [rightOperandInfo] should be the values returned by
   /// [equalityOperand_end].
-  void equalityOperation_end(
-      Expression wholeExpression,
-      EqualityInfo<Variable, Type>? leftOperandInfo,
-      EqualityInfo<Variable, Type>? rightOperandInfo,
+  void equalityOperation_end(Expression wholeExpression,
+      EqualityInfo<Type>? leftOperandInfo, EqualityInfo<Type>? rightOperandInfo,
       {bool notEqual = false});
 
   /// Retrieves the [ExpressionInfo] associated with [target], if known.  Will
   /// return `null` if (a) no info is associated with [target], or (b) another
   /// expression with info has been visited more recently than [target].  For
   /// testing only.
-  ExpressionInfo<Variable, Type>? expressionInfoForTesting(Expression target);
+  ExpressionInfo<Type>? expressionInfoForTesting(Expression target);
 
   /// This method should be called at the conclusion of flow analysis for a top
   /// level function or method.  Performs assertion checks.
@@ -801,7 +805,7 @@
   /// is not associated with an SSA node because it is write captured.  For
   /// testing only.
   @visibleForTesting
-  SsaNode<Variable, Type>? ssaNodeForTesting(Variable variable);
+  SsaNode<Type>? ssaNodeForTesting(Variable variable);
 
   /// Call this method just before visiting one of the cases in the body of a
   /// switch statement.  See [switchStatement_expressionEnd] for details.
@@ -1144,17 +1148,14 @@
   }
 
   @override
-  EqualityInfo<Variable, Type>? equalityOperand_end(
-          Expression operand, Type type) =>
+  EqualityInfo<Type>? equalityOperand_end(Expression operand, Type type) =>
       _wrap('equalityOperand_end($operand, $type)',
           () => _wrapped.equalityOperand_end(operand, type),
           isQuery: true);
 
   @override
-  void equalityOperation_end(
-      Expression wholeExpression,
-      EqualityInfo<Variable, Type>? leftOperandInfo,
-      EqualityInfo<Variable, Type>? rightOperandInfo,
+  void equalityOperation_end(Expression wholeExpression,
+      EqualityInfo<Type>? leftOperandInfo, EqualityInfo<Type>? rightOperandInfo,
       {bool notEqual = false}) {
     _wrap(
         'equalityOperation_end($wholeExpression, $leftOperandInfo, '
@@ -1165,7 +1166,7 @@
   }
 
   @override
-  ExpressionInfo<Variable, Type>? expressionInfoForTesting(Expression target) {
+  ExpressionInfo<Type>? expressionInfoForTesting(Expression target) {
     return _wrap('expressionInfoForTesting($target)',
         () => _wrapped.expressionInfoForTesting(target),
         isQuery: true);
@@ -1423,7 +1424,7 @@
   }
 
   @override
-  SsaNode<Variable, Type>? ssaNodeForTesting(Variable variable) {
+  SsaNode<Type>? ssaNodeForTesting(Variable variable) {
     return _wrap('ssaNodeForTesting($variable)',
         () => _wrapped.ssaNodeForTesting(variable),
         isQuery: true);
@@ -1617,10 +1618,11 @@
 /// Instances of this class are immutable, so the methods below that "update"
 /// the state actually leave `this` unchanged and return a new state object.
 @visibleForTesting
-class FlowModel<Variable extends Object, Type extends Object> {
+class FlowModel<Type extends Object> {
   final Reachability reachable;
 
-  /// For each variable being tracked by flow analysis, the variable's model.
+  /// For each promotable thing being tracked by flow analysis, the
+  /// corresponding model.
   ///
   /// Flow analysis has no awareness of scope, so variables that are out of
   /// scope are retained in the map until such time as their declaration no
@@ -1632,13 +1634,12 @@
   /// analysis results for error-free code, because it is an error to refer to a
   /// variable that is no longer in scope.
   ///
-  /// `null` is allowed as a special key; it represents the pseudo-variable
-  /// `this`.  (This is needed so that we can explain why `this` is not
-  /// promoted, and why properties of `this` are not promoted).
-  final Map<Variable?, VariableModel<Variable, Type> /*!*/ > variableInfo;
+  /// Keys are the unique integers assigned by
+  /// [_FlowAnalysisImpl._promotionKeyStore].
+  final Map<int, VariableModel<Type> /*!*/ > variableInfo;
 
   /// The empty map, used to [join] variables.
-  final Map<Variable?, VariableModel<Variable, Type>> _emptyVariableMap = {};
+  final Map<int, VariableModel<Type>> _emptyVariableMap = {};
 
   /// Creates a state object with the given [reachable] status.  All variables
   /// are assumed to be unpromoted and already assigned, so joining another
@@ -1654,7 +1655,7 @@
     // ignore:unnecessary_null_comparison
     assert(reachable != null);
     assert(() {
-      for (VariableModel<Variable, Type> value in variableInfo.values) {
+      for (VariableModel<Type> value in variableInfo.values) {
         // ignore:unnecessary_null_comparison
         assert(value != null);
       }
@@ -1678,27 +1679,24 @@
   /// an exception, so we want to reinstate the results of any promotions and
   /// assignments that occurred during the `try` block, to the extent that they
   /// weren't invalidated by later assignments in the `finally` block.
-  FlowModel<Variable, Type> attachFinally(
-      TypeOperations<Type> typeOperations,
-      FlowModel<Variable, Type> beforeFinally,
-      FlowModel<Variable, Type> afterFinally) {
+  FlowModel<Type> attachFinally(TypeOperations<Type> typeOperations,
+      FlowModel<Type> beforeFinally, FlowModel<Type> afterFinally) {
     // Code that follows the `try/finally` is reachable iff the end of the `try`
     // block is reachable _and_ the end of the `finally` block is reachable.
     Reachability newReachable = afterFinally.reachable.rebaseForward(reachable);
 
     // Consider each variable that is common to all three models.
-    Map<Variable?, VariableModel<Variable, Type>> newVariableInfo =
-        <Variable?, VariableModel<Variable, Type>>{};
+    Map<int, VariableModel<Type>> newVariableInfo =
+        <int, VariableModel<Type>>{};
     bool variableInfoMatchesThis = true;
     bool variableInfoMatchesAfterFinally = true;
-    for (MapEntry<Variable?, VariableModel<Variable, Type>> entry
-        in variableInfo.entries) {
-      Variable? variable = entry.key;
-      VariableModel<Variable, Type> thisModel = entry.value;
-      VariableModel<Variable, Type>? beforeFinallyModel =
-          beforeFinally.variableInfo[variable];
-      VariableModel<Variable, Type>? afterFinallyModel =
-          afterFinally.variableInfo[variable];
+    for (MapEntry<int, VariableModel<Type>> entry in variableInfo.entries) {
+      int promotionKey = entry.key;
+      VariableModel<Type> thisModel = entry.value;
+      VariableModel<Type>? beforeFinallyModel =
+          beforeFinally.variableInfo[promotionKey];
+      VariableModel<Type>? afterFinallyModel =
+          afterFinally.variableInfo[promotionKey];
       if (beforeFinallyModel == null || afterFinallyModel == null) {
         // The variable is in `this` model but not in one of the `finally`
         // models.  This happens when the variable is declared inside the `try`
@@ -1711,7 +1709,7 @@
       // because any write captures in the `try` block are conservatively
       // considered to take effect in the `finally` block too.
       List<Type>? newPromotedTypes;
-      SsaNode<Variable, Type>? newSsaNode;
+      SsaNode<Type>? newSsaNode;
       if (beforeFinallyModel.ssaNode == afterFinallyModel.ssaNode) {
         // The finally clause doesn't write to the variable, so we want to keep
         // all promotions that were done to it in both the try and finally
@@ -1737,7 +1735,7 @@
       // The `finally` block inherited the "unassigned" state from the `try`
       // block so we can just inherit from it.
       bool newUnassigned = afterFinallyModel.unassigned;
-      VariableModel<Variable, Type> newModel = VariableModel._identicalOrNew(
+      VariableModel<Type> newModel = VariableModel._identicalOrNew(
           thisModel,
           afterFinallyModel,
           newPromotedTypes,
@@ -1745,7 +1743,7 @@
           newAssigned,
           newUnassigned,
           newSsaNode);
-      newVariableInfo[variable] = newModel;
+      newVariableInfo[promotionKey] = newModel;
       if (!identical(newModel, thisModel)) variableInfoMatchesThis = false;
       if (!identical(newModel, afterFinallyModel)) {
         variableInfoMatchesAfterFinally = false;
@@ -1756,8 +1754,8 @@
     // erroneously think that `newVariableInfo` matches `afterFinally`.  If so,
     // correct that.
     if (variableInfoMatchesAfterFinally) {
-      for (Variable? variable in afterFinally.variableInfo.keys) {
-        if (!variableInfo.containsKey(variable)) {
+      for (int promotionKey in afterFinally.variableInfo.keys) {
+        if (!variableInfo.containsKey(promotionKey)) {
           variableInfoMatchesAfterFinally = false;
           break;
         }
@@ -1796,55 +1794,55 @@
   /// and only remove promotions if it can be shown that they aren't restored
   /// later in the loop body.  If we switch to a fixed point analysis, we should
   /// be able to remove this method.
-  FlowModel<Variable, Type> conservativeJoin(
-      Iterable<Variable> writtenVariables,
-      Iterable<Variable> capturedVariables) {
-    Map<Variable?, VariableModel<Variable, Type>>? newVariableInfo;
+  FlowModel<Type> conservativeJoin(
+      Iterable<int> writtenVariables, Iterable<int> capturedVariables) {
+    Map<int, VariableModel<Type>>? newVariableInfo;
 
-    for (Variable variable in writtenVariables) {
-      VariableModel<Variable, Type>? info = variableInfo[variable];
+    for (int variableKey in writtenVariables) {
+      VariableModel<Type>? info = variableInfo[variableKey];
       if (info == null) continue;
-      VariableModel<Variable, Type> newInfo =
+      VariableModel<Type> newInfo =
           info.discardPromotionsAndMarkNotUnassigned();
       if (!identical(info, newInfo)) {
-        (newVariableInfo ??=
-            new Map<Variable?, VariableModel<Variable, Type>>.of(
-                variableInfo))[variable] = newInfo;
+        (newVariableInfo ??= new Map<int, VariableModel<Type>>.of(
+            variableInfo))[variableKey] = newInfo;
       }
     }
 
-    for (Variable variable in capturedVariables) {
-      VariableModel<Variable, Type>? info = variableInfo[variable];
+    for (int variableKey in capturedVariables) {
+      VariableModel<Type>? info = variableInfo[variableKey];
       if (info == null) continue;
       if (!info.writeCaptured) {
-        (newVariableInfo ??=
-            new Map<Variable?, VariableModel<Variable, Type>>.of(
-                variableInfo))[variable] = info.writeCapture();
+        (newVariableInfo ??= new Map<int, VariableModel<Type>>.of(
+            variableInfo))[variableKey] = info.writeCapture();
       }
     }
 
-    FlowModel<Variable, Type> result = newVariableInfo == null
+    FlowModel<Type> result = newVariableInfo == null
         ? this
-        : new FlowModel<Variable, Type>.withInfo(reachable, newVariableInfo);
+        : new FlowModel<Type>.withInfo(reachable, newVariableInfo);
 
     return result;
   }
 
-  /// Register a declaration of the [variable].
+  /// Register a declaration of the variable whose key is [variableKey].
   /// Should also be called for function parameters.
   ///
   /// A local variable is [initialized] if its declaration has an initializer.
   /// A function parameter is always initialized, so [initialized] is `true`.
-  FlowModel<Variable, Type> declare(Variable variable, bool initialized) {
-    VariableModel<Variable, Type> newInfoForVar =
+  FlowModel<Type> declare<Variable extends Object>(
+      Variable variable, int variableKey, bool initialized) {
+    VariableModel<Type> newInfoForVar =
         new VariableModel.fresh(assigned: initialized);
 
-    return _updateVariableInfo(new VariableReference(variable), newInfoForVar);
+    return _updateVariableInfo(
+        new VariableReference(variable, variableKey), newInfoForVar);
   }
 
-  /// Gets the info for the given [variable], creating it if it doesn't exist.
-  VariableModel<Variable, Type> infoFor(Variable variable) =>
-      variableInfo[variable] ?? new VariableModel.fresh();
+  /// Gets the info for the given [promotionKey], creating it if it doesn't
+  /// exist.
+  VariableModel<Type> infoFor(int promotionKey) =>
+      variableInfo[promotionKey] ?? new VariableModel.fresh();
 
   /// Builds a [FlowModel] based on `this`, but extending the `tested` set to
   /// include types from [other].  This is used at the bottom of certain kinds
@@ -1852,29 +1850,25 @@
   /// consistently treated as "of interest" in code that follows the loop,
   /// regardless of the type of loop.
   @visibleForTesting
-  FlowModel<Variable, Type> inheritTested(
-      TypeOperations<Type> typeOperations, FlowModel<Variable, Type> other) {
-    Map<Variable?, VariableModel<Variable, Type>> newVariableInfo =
-        <Variable?, VariableModel<Variable, Type>>{};
-    Map<Variable?, VariableModel<Variable, Type>> otherVariableInfo =
-        other.variableInfo;
+  FlowModel<Type> inheritTested(
+      TypeOperations<Type> typeOperations, FlowModel<Type> other) {
+    Map<int, VariableModel<Type>> newVariableInfo =
+        <int, VariableModel<Type>>{};
+    Map<int, VariableModel<Type>> otherVariableInfo = other.variableInfo;
     bool changed = false;
-    for (MapEntry<Variable?, VariableModel<Variable, Type>> entry
-        in variableInfo.entries) {
-      Variable? variable = entry.key;
-      VariableModel<Variable, Type> variableModel = entry.value;
-      VariableModel<Variable, Type>? otherVariableModel =
-          otherVariableInfo[variable];
-      VariableModel<Variable, Type> newVariableModel =
-          otherVariableModel == null
-              ? variableModel
-              : VariableModel.inheritTested(
-                  typeOperations, variableModel, otherVariableModel.tested);
-      newVariableInfo[variable] = newVariableModel;
+    for (MapEntry<int, VariableModel<Type>> entry in variableInfo.entries) {
+      int promotionKey = entry.key;
+      VariableModel<Type> variableModel = entry.value;
+      VariableModel<Type>? otherVariableModel = otherVariableInfo[promotionKey];
+      VariableModel<Type> newVariableModel = otherVariableModel == null
+          ? variableModel
+          : VariableModel.inheritTested(
+              typeOperations, variableModel, otherVariableModel.tested);
+      newVariableInfo[promotionKey] = newVariableModel;
       if (!identical(newVariableModel, variableModel)) changed = true;
     }
     if (changed) {
-      return new FlowModel<Variable, Type>.withInfo(reachable, newVariableInfo);
+      return new FlowModel<Type>.withInfo(reachable, newVariableInfo);
     } else {
       return this;
     }
@@ -1890,26 +1884,26 @@
   /// example, if a variable is promoted in `this` but not in [base], then it
   /// will be promoted in the output model, provided that hasn't been reassigned
   /// since then (which would make the promotion unsound).
-  FlowModel<Variable, Type> rebaseForward(
-      TypeOperations<Type> typeOperations, FlowModel<Variable, Type> base) {
+  FlowModel<Type> rebaseForward(
+      TypeOperations<Type> typeOperations, FlowModel<Type> base) {
     // The rebased model is reachable iff both `this` and the new base are
     // reachable.
     Reachability newReachable = reachable.rebaseForward(base.reachable);
 
     // Consider each variable in the new base model.
-    Map<Variable?, VariableModel<Variable, Type>> newVariableInfo =
-        <Variable?, VariableModel<Variable, Type>>{};
+    Map<int, VariableModel<Type>> newVariableInfo =
+        <int, VariableModel<Type>>{};
     bool variableInfoMatchesThis = true;
     bool variableInfoMatchesBase = true;
-    for (MapEntry<Variable?, VariableModel<Variable, Type>> entry
+    for (MapEntry<int, VariableModel<Type>> entry
         in base.variableInfo.entries) {
-      Variable? variable = entry.key;
-      VariableModel<Variable, Type> baseModel = entry.value;
-      VariableModel<Variable, Type>? thisModel = variableInfo[variable];
+      int promotionKey = entry.key;
+      VariableModel<Type> baseModel = entry.value;
+      VariableModel<Type>? thisModel = variableInfo[promotionKey];
       if (thisModel == null) {
         // The variable has newly came into scope since `thisModel`, so the
         // information in `baseModel` is up to date.
-        newVariableInfo[variable] = baseModel;
+        newVariableInfo[promotionKey] = baseModel;
         variableInfoMatchesThis = false;
         continue;
       }
@@ -1943,7 +1937,7 @@
       // The variable is definitely unassigned if it was definitely unassigned
       // in both `this` model and the new base model.
       bool newUnassigned = thisModel.unassigned && baseModel.unassigned;
-      VariableModel<Variable, Type> newModel = VariableModel._identicalOrNew(
+      VariableModel<Type> newModel = VariableModel._identicalOrNew(
           thisModel,
           baseModel,
           newPromotedTypes,
@@ -1951,7 +1945,7 @@
           newAssigned,
           newUnassigned,
           newWriteCaptured ? null : baseModel.ssaNode);
-      newVariableInfo[variable] = newModel;
+      newVariableInfo[promotionKey] = newModel;
       if (!identical(newModel, thisModel)) variableInfoMatchesThis = false;
       if (!identical(newModel, baseModel)) variableInfoMatchesBase = false;
     }
@@ -1959,8 +1953,8 @@
     // present in `this` that aren't present in `base`, we may erroneously think
     // that `newVariableInfo` matches `this`.  If so, correct that.
     if (variableInfoMatchesThis) {
-      for (Variable? variable in variableInfo.keys) {
-        if (!base.variableInfo.containsKey(variable)) {
+      for (int promotionKey in variableInfo.keys) {
+        if (!base.variableInfo.containsKey(promotionKey)) {
           variableInfoMatchesThis = false;
           break;
         }
@@ -1980,17 +1974,17 @@
   }
 
   /// Updates the state to indicate that the control flow path is unreachable.
-  FlowModel<Variable, Type> setUnreachable() {
+  FlowModel<Type> setUnreachable() {
     if (!reachable.locallyReachable) return this;
 
-    return new FlowModel<Variable, Type>.withInfo(
+    return new FlowModel<Type>.withInfo(
         reachable.setUnreachable(), variableInfo);
   }
 
   /// Returns a [FlowModel] indicating the result of creating a control flow
   /// split.  See [Reachability.split] for more information.
-  FlowModel<Variable, Type> split() =>
-      new FlowModel<Variable, Type>.withInfo(reachable.split(), variableInfo);
+  FlowModel<Type> split() =>
+      new FlowModel<Type>.withInfo(reachable.split(), variableInfo);
 
   @override
   String toString() => '($reachable, $variableInfo)';
@@ -2000,29 +1994,27 @@
   ///
   /// Note that the state is only changed if the previous type of [variable] was
   /// potentially nullable.
-  ExpressionInfo<Variable, Type> tryMarkNonNullable(
-      TypeOperations<Type> typeOperations,
-      ReferenceWithType<Variable, Type> referenceWithType) {
-    VariableModel<Variable, Type> info =
+  ExpressionInfo<Type> tryMarkNonNullable(TypeOperations<Type> typeOperations,
+      ReferenceWithType<Type> referenceWithType) {
+    VariableModel<Type> info =
         referenceWithType.reference.getInfo(variableInfo);
     if (info.writeCaptured) {
-      return new _TrivialExpressionInfo<Variable, Type>(this);
+      return new _TrivialExpressionInfo<Type>(this);
     }
 
     Type previousType = referenceWithType.type;
     Type newType = typeOperations.promoteToNonNull(previousType);
     if (typeOperations.isSameType(newType, previousType)) {
-      return new _TrivialExpressionInfo<Variable, Type>(this);
+      return new _TrivialExpressionInfo<Type>(this);
     }
     assert(typeOperations.isSubtypeOf(newType, previousType));
 
-    FlowModel<Variable, Type> modelIfSuccessful = _finishTypeTest(
+    FlowModel<Type> modelIfSuccessful = _finishTypeTest(
         typeOperations, referenceWithType.reference, info, null, newType);
 
-    FlowModel<Variable, Type> modelIfFailed = this;
+    FlowModel<Type> modelIfFailed = this;
 
-    return new ExpressionInfo<Variable, Type>(
-        this, modelIfSuccessful, modelIfFailed);
+    return new ExpressionInfo<Type>(this, modelIfSuccessful, modelIfFailed);
   }
 
   /// Returns an [ExpressionInfo] indicating the result of casting the given
@@ -2034,11 +2026,9 @@
   ///
   /// TODO(paulberry): if the type is non-nullable, should this method mark the
   /// variable as definitely assigned?  Does it matter?
-  FlowModel<Variable, Type> tryPromoteForTypeCast(
-      TypeOperations<Type> typeOperations,
-      ReferenceWithType<Variable, Type> referenceWithType,
-      Type type) {
-    VariableModel<Variable, Type> info =
+  FlowModel<Type> tryPromoteForTypeCast(TypeOperations<Type> typeOperations,
+      ReferenceWithType<Type> referenceWithType, Type type) {
+    VariableModel<Type> info =
         referenceWithType.reference.getInfo(variableInfo);
     if (info.writeCaptured) {
       return this;
@@ -2065,18 +2055,18 @@
   ///
   /// TODO(paulberry): if the type is non-nullable, should this method mark the
   /// variable as definitely assigned?  Does it matter?
-  ExpressionInfo<Variable, Type> tryPromoteForTypeCheck(
+  ExpressionInfo<Type> tryPromoteForTypeCheck(
       TypeOperations<Type> typeOperations,
-      ReferenceWithType<Variable, Type> referenceWithType,
+      ReferenceWithType<Type> referenceWithType,
       Type type) {
-    VariableModel<Variable, Type> info =
+    VariableModel<Type> info =
         referenceWithType.reference.getInfo(variableInfo);
     if (info.writeCaptured) {
-      return new _TrivialExpressionInfo<Variable, Type>(this);
+      return new _TrivialExpressionInfo<Type>(this);
     }
 
     Type previousType = referenceWithType.type;
-    FlowModel<Variable, Type> modelIfSuccessful = this;
+    FlowModel<Type> modelIfSuccessful = this;
     Type? typeIfSuccess = typeOperations.tryPromoteToType(type, previousType);
     if (typeIfSuccess != null &&
         !typeOperations.isSameType(typeIfSuccess, previousType)) {
@@ -2098,52 +2088,53 @@
     } else {
       typeIfFailed = factoredType;
     }
-    FlowModel<Variable, Type> modelIfFailed = _finishTypeTest(
+    FlowModel<Type> modelIfFailed = _finishTypeTest(
         typeOperations, referenceWithType.reference, info, type, typeIfFailed);
 
-    return new ExpressionInfo<Variable, Type>(
-        this, modelIfSuccessful, modelIfFailed);
+    return new ExpressionInfo<Type>(this, modelIfSuccessful, modelIfFailed);
   }
 
   /// Returns a [FlowModel] indicating the result of removing a control flow
   /// split.  See [Reachability.unsplit] for more information.
-  FlowModel<Variable, Type> unsplit() =>
-      new FlowModel<Variable, Type>.withInfo(reachable.unsplit(), variableInfo);
+  FlowModel<Type> unsplit() =>
+      new FlowModel<Type>.withInfo(reachable.unsplit(), variableInfo);
 
   /// Removes control flow splits until a [FlowModel] is obtained whose
   /// reachability has the given [parent].
-  FlowModel<Variable, Type> unsplitTo(Reachability parent) {
+  FlowModel<Type> unsplitTo(Reachability parent) {
     if (identical(this.reachable.parent, parent)) return this;
     Reachability reachable = this.reachable.unsplit();
     while (!identical(reachable.parent, parent)) {
       reachable = reachable.unsplit();
     }
-    return new FlowModel<Variable, Type>.withInfo(reachable, variableInfo);
+    return new FlowModel<Type>.withInfo(reachable, variableInfo);
   }
 
-  /// Updates the state to indicate that an assignment was made to the given
-  /// [variable].  The variable is marked as definitely assigned, and any
-  /// previous type promotion is removed.
+  /// Updates the state to indicate that an assignment was made to [variable],
+  /// whose key is [variableKey].  The variable is marked as definitely
+  /// assigned, and any previous type promotion is removed.
   ///
   /// If there is any chance that the write will cause a demotion, the caller
   /// must pass in a non-null value for [nonPromotionReason] describing the
   /// reason for any potential demotion.
-  FlowModel<Variable, Type> write(
+  FlowModel<Type> write<Variable extends Object>(
       NonPromotionReason? nonPromotionReason,
       Variable variable,
+      int variableKey,
       Type writtenType,
-      SsaNode<Variable, Type> newSsaNode,
+      SsaNode<Type> newSsaNode,
       Operations<Variable, Type> operations,
       {bool promoteToTypeOfInterest = true}) {
-    VariableModel<Variable, Type>? infoForVar = variableInfo[variable];
+    VariableModel<Type>? infoForVar = variableInfo[variableKey];
     if (infoForVar == null) return this;
 
-    VariableModel<Variable, Type> newInfoForVar = infoForVar.write(
-        nonPromotionReason, variable, writtenType, operations, newSsaNode,
+    VariableModel<Type> newInfoForVar = infoForVar.write(nonPromotionReason,
+        variable, variableKey, writtenType, operations, newSsaNode,
         promoteToTypeOfInterest: promoteToTypeOfInterest);
     if (identical(newInfoForVar, infoForVar)) return this;
 
-    return _updateVariableInfo(new VariableReference(variable), newInfoForVar);
+    return _updateVariableInfo(
+        new VariableReference(variable, variableKey), newInfoForVar);
   }
 
   /// Common algorithm for [tryMarkNonNullable], [tryPromoteForTypeCast],
@@ -2157,10 +2148,10 @@
   /// - [promotedType] should be a subtype of the currently-promoted type (i.e.
   ///   no redundant or side-promotions)
   /// - The variable should not be write-captured.
-  FlowModel<Variable, Type> _finishTypeTest(
+  FlowModel<Type> _finishTypeTest(
     TypeOperations<Type> typeOperations,
-    Reference<Variable, Type> reference,
-    VariableModel<Variable, Type> info,
+    Reference<Type> reference,
+    VariableModel<Type> info,
     Type? testedType,
     Type? promotedType,
   ) {
@@ -2187,7 +2178,7 @@
         ? this
         : _updateVariableInfo(
             reference,
-            new VariableModel<Variable, Type>(
+            new VariableModel<Type>(
                 promotedTypes: newPromotedTypes,
                 tested: newTested,
                 assigned: info.assigned,
@@ -2199,14 +2190,14 @@
 
   /// Returns a new [FlowModel] where the information for [reference] is
   /// replaced with [model].
-  FlowModel<Variable, Type> _updateVariableInfo(
-      Reference<Variable, Type> reference, VariableModel<Variable, Type> model,
+  FlowModel<Type> _updateVariableInfo(
+      Reference<Type> reference, VariableModel<Type> model,
       {Reachability? reachable}) {
     reachable ??= this.reachable;
-    Map<Variable?, VariableModel<Variable, Type>> newVariableInfo =
-        new Map<Variable?, VariableModel<Variable, Type>>.of(variableInfo);
+    Map<int, VariableModel<Type>> newVariableInfo =
+        new Map<int, VariableModel<Type>>.of(variableInfo);
     reference.storeInfo(newVariableInfo, model);
-    return new FlowModel<Variable, Type>.withInfo(reachable, newVariableInfo);
+    return new FlowModel<Type>.withInfo(reachable, newVariableInfo);
   }
 
   /// Forms a new state to reflect a control flow path that might have come from
@@ -2218,12 +2209,11 @@
   /// are kept only if they are common to both input states; if a variable is
   /// promoted to one type in one state and a subtype in the other state, the
   /// less specific type promotion is kept.
-  static FlowModel<Variable, Type>
-      join<Variable extends Object, Type extends Object>(
+  static FlowModel<Type> join<Type extends Object>(
     TypeOperations<Type> typeOperations,
-    FlowModel<Variable, Type>? first,
-    FlowModel<Variable, Type>? second,
-    Map<Variable?, VariableModel<Variable, Type>> emptyVariableMap,
+    FlowModel<Type>? first,
+    FlowModel<Type>? second,
+    Map<int, VariableModel<Type>> emptyVariableMap,
   ) {
     if (first == null) return second!;
     if (second == null) return first;
@@ -2240,9 +2230,11 @@
 
     Reachability newReachable =
         Reachability.join(first.reachable, second.reachable);
-    Map<Variable?, VariableModel<Variable, Type>> newVariableInfo =
-        FlowModel.joinVariableInfo(typeOperations, first.variableInfo,
-            second.variableInfo, emptyVariableMap);
+    Map<int, VariableModel<Type>> newVariableInfo = FlowModel.joinVariableInfo(
+        typeOperations,
+        first.variableInfo,
+        second.variableInfo,
+        emptyVariableMap);
 
     return FlowModel._identicalOrNew(
         first, second, newReachable, newVariableInfo);
@@ -2250,33 +2242,29 @@
 
   /// Joins two "variable info" maps.  See [join] for details.
   @visibleForTesting
-  static Map<Variable?, VariableModel<Variable, Type>>
-      joinVariableInfo<Variable extends Object, Type extends Object>(
+  static Map<int, VariableModel<Type>> joinVariableInfo<Type extends Object>(
     TypeOperations<Type> typeOperations,
-    Map<Variable?, VariableModel<Variable, Type>> first,
-    Map<Variable?, VariableModel<Variable, Type>> second,
-    Map<Variable?, VariableModel<Variable, Type>> emptyMap,
+    Map<int, VariableModel<Type>> first,
+    Map<int, VariableModel<Type>> second,
+    Map<int, VariableModel<Type>> emptyMap,
   ) {
     if (identical(first, second)) return first;
     if (first.isEmpty || second.isEmpty) {
       return emptyMap;
     }
 
-    Map<Variable?, VariableModel<Variable, Type>> result =
-        <Variable?, VariableModel<Variable, Type>>{};
+    Map<int, VariableModel<Type>> result = <int, VariableModel<Type>>{};
     bool alwaysFirst = true;
     bool alwaysSecond = true;
-    for (MapEntry<Variable?, VariableModel<Variable, Type>> entry
-        in first.entries) {
-      Variable? variable = entry.key;
-      VariableModel<Variable, Type>? secondModel = second[variable];
+    for (MapEntry<int, VariableModel<Type>> entry in first.entries) {
+      int promotionKey = entry.key;
+      VariableModel<Type>? secondModel = second[promotionKey];
       if (secondModel == null) {
         alwaysFirst = false;
       } else {
-        VariableModel<Variable, Type> joined =
-            VariableModel.join<Variable, Type>(
-                typeOperations, entry.value, secondModel);
-        result[variable] = joined;
+        VariableModel<Type> joined =
+            VariableModel.join<Type>(typeOperations, entry.value, secondModel);
+        result[promotionKey] = joined;
         if (!identical(joined, entry.value)) alwaysFirst = false;
         if (!identical(joined, secondModel)) alwaysSecond = false;
       }
@@ -2290,12 +2278,11 @@
 
   /// Models the result of joining the flow models [first] and [second] at the
   /// merge of two control flow paths.
-  static FlowModel<Variable, Type>
-      merge<Variable extends Object, Type extends Object>(
+  static FlowModel<Type> merge<Type extends Object>(
     TypeOperations<Type> typeOperations,
-    FlowModel<Variable, Type>? first,
-    FlowModel<Variable, Type>? second,
-    Map<Variable?, VariableModel<Variable, Type>> emptyVariableMap,
+    FlowModel<Type>? first,
+    FlowModel<Type>? second,
+    Map<int, VariableModel<Type>> emptyVariableMap,
   ) {
     if (first == null) return second!.unsplit();
     if (second == null) return first.unsplit();
@@ -2312,9 +2299,11 @@
 
     Reachability newReachable =
         Reachability.join(first.reachable, second.reachable).unsplit();
-    Map<Variable?, VariableModel<Variable, Type>> newVariableInfo =
-        FlowModel.joinVariableInfo(typeOperations, first.variableInfo,
-            second.variableInfo, emptyVariableMap);
+    Map<int, VariableModel<Type>> newVariableInfo = FlowModel.joinVariableInfo(
+        typeOperations,
+        first.variableInfo,
+        second.variableInfo,
+        emptyVariableMap);
 
     return FlowModel._identicalOrNew(
         first, second, newReachable, newVariableInfo);
@@ -2322,12 +2311,11 @@
 
   /// Creates a new [FlowModel] object, unless it is equivalent to either
   /// [first] or [second], in which case one of those objects is re-used.
-  static FlowModel<Variable, Type>
-      _identicalOrNew<Variable extends Object, Type extends Object>(
-          FlowModel<Variable, Type> first,
-          FlowModel<Variable, Type> second,
-          Reachability newReachable,
-          Map<Variable?, VariableModel<Variable, Type>> newVariableInfo) {
+  static FlowModel<Type> _identicalOrNew<Type extends Object>(
+      FlowModel<Type> first,
+      FlowModel<Type> second,
+      Reachability newReachable,
+      Map<int, VariableModel<Type>> newVariableInfo) {
     if (first.reachable == newReachable &&
         identical(first.variableInfo, newVariableInfo)) {
       return first;
@@ -2337,23 +2325,20 @@
       return second;
     }
 
-    return new FlowModel<Variable, Type>.withInfo(
-        newReachable, newVariableInfo);
+    return new FlowModel<Type>.withInfo(newReachable, newVariableInfo);
   }
 
   /// Determines whether the given "variableInfo" maps are equivalent.
   ///
   /// The equivalence check is shallow; if two variables' models are not
   /// identical, we return `false`.
-  static bool _variableInfosEqual<Variable extends Object, Type extends Object>(
-      Map<Variable?, VariableModel<Variable, Type>> p1,
-      Map<Variable?, VariableModel<Variable, Type>> p2) {
+  static bool _variableInfosEqual<Type extends Object>(
+      Map<int, VariableModel<Type>> p1, Map<int, VariableModel<Type>> p2) {
     if (p1.length != p2.length) return false;
     if (!p1.keys.toSet().containsAll(p2.keys)) return false;
-    for (MapEntry<Variable?, VariableModel<Variable, Type>> entry
-        in p1.entries) {
-      VariableModel<Variable, Type> p1Value = entry.value;
-      VariableModel<Variable, Type>? p2Value = p2[entry.key];
+    for (MapEntry<int, VariableModel<Type>> entry in p1.entries) {
+      VariableModel<Type> p1Value = entry.value;
+      VariableModel<Type>? p2Value = p2[entry.key];
       if (!identical(p1Value, p2Value)) {
         return false;
       }
@@ -2426,6 +2411,48 @@
     extends TypeOperations<Type> implements VariableOperations<Variable, Type> {
 }
 
+/// This data structure assigns a unique integer identifier to everything that
+/// might undergo promotion in the user's code (local variables and properties).
+/// An integer identifier is also assigned to `this` (even though `this` is not
+/// promotable), because promotable properties can be reached using `this` as a
+/// starting point.
+@visibleForTesting
+class PromotionKeyStore<Variable extends Object> {
+  @visibleForTesting
+
+  /// Special promotion key to represent `this`.
+  late final int thisPromotionKey = _makeNewKey(null);
+
+  final Map<Variable, int> _variableKeys = new Map<Variable, int>.identity();
+
+  final List<Variable?> _keyToVariable = [];
+
+  /// List of maps indicating the set of properties of each promotable entity
+  /// being tracked by flow analysis.  The list is indexed by the promotion key
+  /// of the target, and the map is indexed by the property name.
+  ///
+  /// Null list elements are considered equivalent to an empty map (this allows
+  /// us so save memory due to the fact that most entries will not be accessed).
+  final List<Map<String, int>?> _properties = [];
+
+  int getProperty(int targetKey, String propertyName) =>
+      (_properties[targetKey] ??= {})[propertyName] ??= _makeNewKey(null);
+
+  @visibleForTesting
+  int keyForVariable(Variable variable) =>
+      _variableKeys[variable] ??= _makeNewKey(variable);
+
+  @visibleForTesting
+  Variable variableForKey(int variableKey) => _keyToVariable[variableKey]!;
+
+  int _makeNewKey(Variable? variable) {
+    int key = _keyToVariable.length;
+    _keyToVariable.add(variable);
+    _properties.add(null);
+    return key;
+  }
+}
+
 /// Non-promotion reason describing the situation where an expression was not
 /// promoted due to the fact that it's a property get.
 class PropertyNotPromoted<Type extends Object> extends NonPromotionReason {
@@ -2605,39 +2632,45 @@
 /// other references in order to give useful error messages to the user about
 /// why promotion did not occur.
 @visibleForTesting
-abstract class Reference<Variable extends Object, Type extends Object> {
+abstract class Reference<Type extends Object> {
+  /// The reference's corresponding key, as assigned by [PromotionKeyStore].
+  final int promotionKey;
+
+  Reference(this.promotionKey);
+
   /// Gets the info for this reference, creating it if it doesn't exist.
-  VariableModel<Variable, Type> getInfo(
-          Map<Variable?, VariableModel<Variable, Type>> variableInfo) =>
-      _getInfo(variableInfo) ?? new VariableModel<Variable, Type>.fresh();
+  VariableModel<Type> getInfo(Map<int, VariableModel<Type>> variableInfo) =>
+      _getInfo(variableInfo) ?? new VariableModel<Type>.fresh();
 
   /// Gets a map of non-promotion reasons associated with this reference.  This
   /// is the map that will be returned from [FlowAnalysis.whyNotPromoted].
   Map<Type, NonPromotionReason> Function() getNonPromotionReasons(
-      Map<Variable?, VariableModel<Variable, Type>> variableInfo,
+      Map<int, VariableModel<Type>> variableInfo,
       Type staticType,
-      Operations<Variable, Type> operations);
+      TypeOperations<Type> operations);
 
   /// Creates a reference representing a get of a property called [propertyName]
   /// on the reference represented by `this`.
-  Reference<Variable, Type> propertyGet(
+  Reference<Type> propertyGet(PromotionKeyStore promotionKeyStore,
           String propertyName, Object? propertyMember) =>
-      new _PropertyGetReference<Variable, Type>(
-          this, propertyName, propertyMember);
+      new _PropertyGetReference<Type>(propertyName, propertyMember,
+          promotionKeyStore.getProperty(promotionKey, propertyName));
 
   /// Stores info for this reference in [variableInfo].
-  void storeInfo(Map<Variable?, VariableModel<Variable, Type>> variableInfo,
-      VariableModel<Variable, Type> variableModel);
+  void storeInfo(Map<int, VariableModel<Type>> variableInfo,
+      VariableModel<Type> variableModel) {
+    variableInfo[promotionKey] = variableModel;
+  }
 
   /// Gets the info for this reference, or `null` if it doesn't exist.
-  VariableModel<Variable, Type>? _getInfo(
-      Map<Variable?, VariableModel<Variable, Type>> variableInfo);
+  VariableModel<Type>? _getInfo(Map<int, VariableModel<Type>> variableInfo) =>
+      variableInfo[promotionKey];
 }
 
 /// Container object combining a [Reference] object with its static type.
 @visibleForTesting
-class ReferenceWithType<Variable extends Object, Type extends Object> {
-  final Reference<Variable, Type> reference;
+class ReferenceWithType<Type extends Object> {
+  final Reference<Type> reference;
 
   final Type type;
 
@@ -2656,7 +2689,7 @@
 /// (https://en.wikipedia.org/wiki/Static_single_assignment_form) except that it
 /// does not store a complete IR of the code being analyzed.
 @visibleForTesting
-class SsaNode<Variable extends Object, Type extends Object> {
+class SsaNode<Type extends Object> {
   /// Expando mapping SSA nodes to debug ids.  Only used by `toString`.
   static final Expando<int> _debugIds = new Expando<int>();
 
@@ -2671,7 +2704,7 @@
   /// [_TrivialExpressionInfo]) because such information does not lead to
   /// promotions.
   @visibleForTesting
-  final ExpressionInfo<Variable, Type>? expressionInfo;
+  final ExpressionInfo<Type>? expressionInfo;
 
   SsaNode(this.expressionInfo);
 
@@ -2776,7 +2809,7 @@
 /// Instances of this class are immutable, so the methods below that "update"
 /// the state actually leave `this` unchanged and return a new state object.
 @visibleForTesting
-class VariableModel<Variable extends Object, Type extends Object> {
+class VariableModel<Type extends Object> {
   /// Sequence of types that the variable has been promoted to, where each
   /// element of the sequence is a subtype of the previous.  Null if the
   /// variable hasn't been promoted.
@@ -2799,24 +2832,18 @@
   /// value has changed since a time in the past.
   ///
   /// `null` if the variable has been write captured.
-  final SsaNode<Variable, Type>? ssaNode;
+  final SsaNode<Type>? ssaNode;
 
   /// Non-promotion history of this variable.
   final NonPromotionHistory<Type>? nonPromotionHistory;
 
-  /// Promotion information for properties of this variable.  We don't actually
-  /// promote properties, but we track the promotions that would occur if we
-  /// did, so that we can report those as non-promotion reasons.
-  final Map<String, VariableModel<Variable, Type>> properties;
-
   VariableModel(
       {required this.promotedTypes,
       required this.tested,
       required this.assigned,
       required this.unassigned,
       required this.ssaNode,
-      this.nonPromotionHistory,
-      this.properties = const {}}) {
+      this.nonPromotionHistory}) {
     assert(!(assigned && unassigned),
         "Can't be both definitely assigned and unassigned");
     assert(promotedTypes == null || promotedTypes!.isNotEmpty);
@@ -2834,9 +2861,8 @@
       : promotedTypes = null,
         tested = const [],
         unassigned = !assigned,
-        ssaNode = new SsaNode<Variable, Type>(null),
-        nonPromotionHistory = null,
-        properties = const {};
+        ssaNode = new SsaNode<Type>(null),
+        nonPromotionHistory = null;
 
   /// Indicates whether the variable has been write captured.
   bool get writeCaptured => ssaNode == null;
@@ -2846,27 +2872,15 @@
   ///
   /// Used by [conservativeJoin] to update the state of variables at the top of
   /// loops whose bodies write to them.
-  VariableModel<Variable, Type> discardPromotionsAndMarkNotUnassigned() {
-    return new VariableModel<Variable, Type>(
+  VariableModel<Type> discardPromotionsAndMarkNotUnassigned() {
+    return new VariableModel<Type>(
         promotedTypes: null,
         tested: tested,
         assigned: assigned,
         unassigned: false,
-        ssaNode: writeCaptured ? null : new SsaNode<Variable, Type>(null));
+        ssaNode: writeCaptured ? null : new SsaNode<Type>(null));
   }
 
-  /// Updates `this` with a new set of properties.
-  VariableModel<Variable, Type> setProperties(
-          Map<String, VariableModel<Variable, Type>> newProperties) =>
-      new VariableModel<Variable, Type>(
-          promotedTypes: promotedTypes,
-          tested: tested,
-          unassigned: unassigned,
-          assigned: assigned,
-          ssaNode: ssaNode,
-          nonPromotionHistory: nonPromotionHistory,
-          properties: newProperties);
-
   @override
   String toString() {
     List<String> parts = [ssaNode.toString()];
@@ -2888,9 +2902,6 @@
     if (nonPromotionHistory != null) {
       parts.add('nonPromotionHistory: $nonPromotionHistory');
     }
-    if (properties.isNotEmpty) {
-      parts.add('properties: $properties');
-    }
     return 'VariableModel(${parts.join(', ')})';
   }
 
@@ -2900,15 +2911,16 @@
   /// If there is any chance that the write will cause a demotion, the caller
   /// must pass in a non-null value for [nonPromotionReason] describing the
   /// reason for any potential demotion.
-  VariableModel<Variable, Type> write(
+  VariableModel<Type> write<Variable extends Object>(
       NonPromotionReason? nonPromotionReason,
       Variable variable,
+      int variableKey,
       Type writtenType,
       Operations<Variable, Type> operations,
-      SsaNode<Variable, Type> newSsaNode,
+      SsaNode<Type> newSsaNode,
       {required bool promoteToTypeOfInterest}) {
     if (writeCaptured) {
-      return new VariableModel<Variable, Type>(
+      return new VariableModel<Type>(
           promotedTypes: promotedTypes,
           tested: tested,
           assigned: true,
@@ -2928,7 +2940,7 @@
     // TODO(paulberry): remove demotions from demotionResult.nonPromotionHistory
     // that are no longer in effect due to re-promotion.
     if (identical(promotedTypes, newPromotedTypes) && assigned) {
-      return new VariableModel<Variable, Type>(
+      return new VariableModel<Type>(
           promotedTypes: promotedTypes,
           tested: tested,
           assigned: assigned,
@@ -2943,7 +2955,7 @@
       newTested = tested;
     }
 
-    return new VariableModel<Variable, Type>(
+    return new VariableModel<Type>(
         promotedTypes: newPromotedTypes,
         tested: newTested,
         assigned: true,
@@ -2954,8 +2966,8 @@
 
   /// Returns a new [VariableModel] reflecting the fact that the variable has
   /// been write-captured.
-  VariableModel<Variable, Type> writeCapture() {
-    return new VariableModel<Variable, Type>(
+  VariableModel<Type> writeCapture() {
+    return new VariableModel<Type>(
         promotedTypes: null,
         tested: const [],
         assigned: assigned,
@@ -3123,14 +3135,13 @@
   /// are consistently treated as "of interest" in code that follows the loop,
   /// regardless of the type of loop.
   @visibleForTesting
-  static VariableModel<Variable, Type>
-      inheritTested<Variable extends Object, Type extends Object>(
-          TypeOperations<Type> typeOperations,
-          VariableModel<Variable, Type> model,
-          List<Type> tested) {
+  static VariableModel<Type> inheritTested<Type extends Object>(
+      TypeOperations<Type> typeOperations,
+      VariableModel<Type> model,
+      List<Type> tested) {
     List<Type> newTested = joinTested(tested, model.tested, typeOperations);
     if (identical(newTested, model.tested)) return model;
-    return new VariableModel<Variable, Type>(
+    return new VariableModel<Type>(
         promotedTypes: model.promotedTypes,
         tested: newTested,
         assigned: model.assigned,
@@ -3139,11 +3150,10 @@
   }
 
   /// Joins two variable models.  See [FlowModel.join] for details.
-  static VariableModel<Variable, Type>
-      join<Variable extends Object, Type extends Object>(
-          TypeOperations<Type> typeOperations,
-          VariableModel<Variable, Type> first,
-          VariableModel<Variable, Type> second) {
+  static VariableModel<Type> join<Type extends Object>(
+      TypeOperations<Type> typeOperations,
+      VariableModel<Type> first,
+      VariableModel<Type> second) {
     List<Type>? newPromotedTypes = joinPromotedTypes(
         first.promotedTypes, second.promotedTypes, typeOperations);
     newPromotedTypes = typeOperations.refinePromotedTypes(
@@ -3154,11 +3164,11 @@
     List<Type> newTested = newWriteCaptured
         ? const []
         : joinTested(first.tested, second.tested, typeOperations);
-    SsaNode<Variable, Type>? newSsaNode = newWriteCaptured
+    SsaNode<Type>? newSsaNode = newWriteCaptured
         ? null
         : first.ssaNode == second.ssaNode
             ? first.ssaNode
-            : new SsaNode<Variable, Type>(null);
+            : new SsaNode<Type>(null);
     return _identicalOrNew(first, second, newPromotedTypes, newTested,
         newAssigned, newUnassigned, newWriteCaptured ? null : newSsaNode);
   }
@@ -3167,11 +3177,8 @@
   /// chains.  Briefly, we intersect given chains.  The chains are totally
   /// ordered subsets of a global partial order.  Their intersection is a
   /// subset of each, and as such is also totally ordered.
-  static List<Type>?
-      joinPromotedTypes<Variable extends Object, Type extends Object>(
-          List<Type>? chain1,
-          List<Type>? chain2,
-          TypeOperations<Type> typeOperations) {
+  static List<Type>? joinPromotedTypes<Type extends Object>(List<Type>? chain1,
+      List<Type>? chain2, TypeOperations<Type> typeOperations) {
     if (chain1 == null) return chain1;
     if (chain2 == null) return chain2;
 
@@ -3214,10 +3221,8 @@
   /// - The sense of equality for the union operation is determined by
   ///   [TypeOperations.isSameType].
   /// - The types of interests lists are considered immutable.
-  static List<Type> joinTested<Variable extends Object, Type extends Object>(
-      List<Type> types1,
-      List<Type> types2,
-      TypeOperations<Type> typeOperations) {
+  static List<Type> joinTested<Type extends Object>(List<Type> types1,
+      List<Type> types2, TypeOperations<Type> typeOperations) {
     // Ensure that types1 is the shorter list.
     if (types1.length > types2.length) {
       List<Type> tmp = types1;
@@ -3291,24 +3296,22 @@
           ? [promoted]
           : (promotedTypes.toList()..add(promoted));
 
-  static List<Type>
-      _addTypeToUniqueList<Variable extends Object, Type extends Object>(
-          List<Type> types, Type newType, TypeOperations<Type> typeOperations) {
+  static List<Type> _addTypeToUniqueList<Type extends Object>(
+      List<Type> types, Type newType, TypeOperations<Type> typeOperations) {
     if (_typeListContains(typeOperations, types, newType)) return types;
     return new List<Type>.of(types)..add(newType);
   }
 
   /// Creates a new [VariableModel] object, unless it is equivalent to either
   /// [first] or [second], in which case one of those objects is re-used.
-  static VariableModel<Variable, Type>
-      _identicalOrNew<Variable extends Object, Type extends Object>(
-          VariableModel<Variable, Type> first,
-          VariableModel<Variable, Type> second,
-          List<Type>? newPromotedTypes,
-          List<Type> newTested,
-          bool newAssigned,
-          bool newUnassigned,
-          SsaNode<Variable, Type>? newSsaNode) {
+  static VariableModel<Type> _identicalOrNew<Type extends Object>(
+      VariableModel<Type> first,
+      VariableModel<Type> second,
+      List<Type>? newPromotedTypes,
+      List<Type> newTested,
+      bool newAssigned,
+      bool newUnassigned,
+      SsaNode<Type>? newSsaNode) {
     if (identical(first.promotedTypes, newPromotedTypes) &&
         identical(first.tested, newTested) &&
         first.assigned == newAssigned &&
@@ -3322,7 +3325,7 @@
         second.ssaNode == newSsaNode) {
       return second;
     } else {
-      return new VariableModel<Variable, Type>(
+      return new VariableModel<Type>(
           promotedTypes: newPromotedTypes,
           tested: newTested,
           assigned: newAssigned,
@@ -3331,7 +3334,7 @@
     }
   }
 
-  static bool _typeListContains<Variable extends Object, Type extends Object>(
+  static bool _typeListContains<Type extends Object>(
       TypeOperations<Type> typeOperations, List<Type> list, Type searchType) {
     for (Type type in list) {
       if (typeOperations.isSameType(type, searchType)) return true;
@@ -3351,18 +3354,18 @@
 /// (or function parameter).
 @visibleForTesting
 class VariableReference<Variable extends Object, Type extends Object>
-    extends Reference<Variable, Type> {
+    extends Reference<Type> {
   /// The variable being referred to.
   final Variable variable;
 
-  VariableReference(this.variable);
+  VariableReference(this.variable, super.promotionKey);
 
   @override
   Map<Type, NonPromotionReason> Function() getNonPromotionReasons(
-      Map<Variable?, VariableModel<Variable, Type>> variableInfo,
+      Map<int, VariableModel<Type>> variableInfo,
       Type staticType,
-      Operations<Variable, Type> operations) {
-    VariableModel<Variable, Type>? currentVariableInfo = variableInfo[variable];
+      covariant Operations<Variable, Type> operations) {
+    VariableModel<Type>? currentVariableInfo = variableInfo[promotionKey];
     if (currentVariableInfo == null) {
       return () => {};
     }
@@ -3384,27 +3387,15 @@
   }
 
   @override
-  void storeInfo(Map<Variable?, VariableModel<Variable, Type>> variableInfo,
-      VariableModel<Variable, Type> variableModel) {
-    variableInfo[variable] = variableModel;
-  }
-
-  @override
-  String toString() => 'VariableReference($variable)';
-
-  @override
-  VariableModel<Variable, Type>? _getInfo(
-          Map<Variable?, VariableModel<Variable, Type>> variableInfo) =>
-      variableInfo[variable];
+  String toString() => 'VariableReference($variable, $promotionKey)';
 }
 
 class WhyNotPromotedInfo {}
 
 /// [_FlowContext] representing an assert statement or assert initializer.
-class _AssertContext<Variable extends Object, Type extends Object>
-    extends _SimpleContext<Variable, Type> {
+class _AssertContext<Type extends Object> extends _SimpleContext<Type> {
   /// Flow models associated with the condition being asserted.
-  ExpressionInfo<Variable, Type>? _conditionInfo;
+  ExpressionInfo<Type>? _conditionInfo;
 
   _AssertContext(super.previous);
 
@@ -3416,10 +3407,9 @@
 /// [_FlowContext] representing a language construct that branches on a boolean
 /// condition, such as an `if` statement, conditional expression, or a logical
 /// binary operator.
-class _BranchContext<Variable extends Object, Type extends Object>
-    extends _FlowContext {
+class _BranchContext<Type extends Object> extends _FlowContext {
   /// Flow models associated with the condition being branched on.
-  final ExpressionInfo<Variable, Type>? _conditionInfo;
+  final ExpressionInfo<Type>? _conditionInfo;
 
   _BranchContext(this._conditionInfo);
 
@@ -3429,15 +3419,14 @@
 
 /// [_FlowContext] representing a language construct that can be targeted by
 /// `break` or `continue` statements, such as a loop or switch statement.
-class _BranchTargetContext<Variable extends Object, Type extends Object>
-    extends _FlowContext {
+class _BranchTargetContext<Type extends Object> extends _FlowContext {
   /// Accumulated flow model for all `break` statements seen so far, or `null`
   /// if no `break` statements have been seen yet.
-  FlowModel<Variable, Type>? _breakModel;
+  FlowModel<Type>? _breakModel;
 
   /// Accumulated flow model for all `continue` statements seen so far, or
   /// `null` if no `continue` statements have been seen yet.
-  FlowModel<Variable, Type>? _continueModel;
+  FlowModel<Type>? _continueModel;
 
   /// The reachability checkpoint associated with this loop or switch statement.
   /// When analyzing deeply nested `break` and `continue` statements, their flow
@@ -3453,13 +3442,12 @@
 }
 
 /// [_FlowContext] representing a conditional expression.
-class _ConditionalContext<Variable extends Object, Type extends Object>
-    extends _BranchContext<Variable, Type> {
+class _ConditionalContext<Type extends Object> extends _BranchContext<Type> {
   /// Flow models associated with the value of the conditional expression in the
   /// circumstance where the "then" branch is taken.
-  ExpressionInfo<Variable, Type>? _thenInfo;
+  ExpressionInfo<Type>? _thenInfo;
 
-  _ConditionalContext(ExpressionInfo<Variable, Type> super.conditionInfo);
+  _ConditionalContext(ExpressionInfo<Type> super.conditionInfo);
 
   @override
   String toString() => '_ConditionalContext(conditionInfo: $_conditionInfo, '
@@ -3494,11 +3482,9 @@
   /// The mapping from [Statement]s that can act as targets for `break` and
   /// `continue` statements (i.e. loops and switch statements) to the to their
   /// context information.
-  final Map<Statement, _BranchTargetContext<Variable, Type>>
-      _statementToContext = {};
+  final Map<Statement, _BranchTargetContext<Type>> _statementToContext = {};
 
-  FlowModel<Variable, Type> _current =
-      new FlowModel<Variable, Type>(Reachability.initial);
+  FlowModel<Type> _current = new FlowModel<Type>(Reachability.initial);
 
   /// The most recently visited expression for which an [ExpressionInfo] object
   /// exists, or `null` if no expression has been visited that has a
@@ -3507,7 +3493,7 @@
 
   /// If [_expressionWithInfo] is not `null`, the [ExpressionInfo] object
   /// corresponding to it.  Otherwise `null`.
-  ExpressionInfo<Variable, Type>? _expressionInfo;
+  ExpressionInfo<Type>? _expressionInfo;
 
   /// The most recently visited expression which was a reference, or `null` if
   /// no such expression has been visited.
@@ -3515,7 +3501,7 @@
 
   /// If [_expressionVariable] is not `null`, the reference corresponding to it.
   /// Otherwise `null`.
-  ReferenceWithType<Variable, Type>? _expressionReference;
+  ReferenceWithType<Type>? _expressionReference;
 
   final AssignedVariables<Node, Variable> _assignedVariables;
 
@@ -3526,19 +3512,20 @@
   /// analyzing old language versions).
   final bool respectImplicitlyTypedVarInitializers;
 
+  final PromotionKeyStore<Variable> _promotionKeyStore;
+
   _FlowAnalysisImpl(this.operations, this._assignedVariables,
-      {required this.respectImplicitlyTypedVarInitializers}) {
+      {required this.respectImplicitlyTypedVarInitializers})
+      : _promotionKeyStore = _assignedVariables._promotionKeyStore {
     if (!_assignedVariables._isFinished) {
       _assignedVariables.finish();
     }
-    AssignedVariablesNodeInfo<Variable> anywhere = _assignedVariables._anywhere;
-    Set<Variable> implicitlyDeclaredVars = {
-      ...anywhere._read,
-      ...anywhere._written
-    };
+    AssignedVariablesNodeInfo anywhere = _assignedVariables._anywhere;
+    Set<int> implicitlyDeclaredVars = {...anywhere._read, ...anywhere._written};
     implicitlyDeclaredVars.removeAll(anywhere._declared);
-    for (Variable variable in implicitlyDeclaredVars) {
-      declare(variable, true);
+    for (int variableKey in implicitlyDeclaredVars) {
+      _current = _current.declare(
+          _promotionKeyStore.variableForKey(variableKey), variableKey, true);
     }
   }
 
@@ -3547,7 +3534,7 @@
 
   @override
   void asExpression_end(Expression subExpression, Type type) {
-    ReferenceWithType<Variable, Type>? referenceWithType =
+    ReferenceWithType<Type>? referenceWithType =
         _getExpressionReference(subExpression);
     if (referenceWithType == null) return;
     _current =
@@ -3556,9 +3543,8 @@
 
   @override
   void assert_afterCondition(Expression condition) {
-    _AssertContext<Variable, Type> context =
-        _stack.last as _AssertContext<Variable, Type>;
-    ExpressionInfo<Variable, Type> conditionInfo = _expressionEnd(condition);
+    _AssertContext<Type> context = _stack.last as _AssertContext<Type>;
+    ExpressionInfo<Type> conditionInfo = _expressionEnd(condition);
     context._conditionInfo = conditionInfo;
     _current = conditionInfo.ifFalse;
   }
@@ -3566,19 +3552,18 @@
   @override
   void assert_begin() {
     _current = _current.split();
-    _stack.add(new _AssertContext<Variable, Type>(_current));
+    _stack.add(new _AssertContext<Type>(_current));
   }
 
   @override
   void assert_end() {
-    _AssertContext<Variable, Type> context =
-        _stack.removeLast() as _AssertContext<Variable, Type>;
+    _AssertContext<Type> context = _stack.removeLast() as _AssertContext<Type>;
     _current = _merge(context._previous, context._conditionInfo!.ifTrue);
   }
 
   @override
   void booleanLiteral(Expression expression, bool value) {
-    FlowModel<Variable, Type> unreachable = _current.setUnreachable();
+    FlowModel<Type> unreachable = _current.setUnreachable();
     _storeExpressionInfo(
         expression,
         value
@@ -3593,8 +3578,8 @@
 
   @override
   void conditional_elseBegin(Expression thenExpression) {
-    _ConditionalContext<Variable, Type> context =
-        _stack.last as _ConditionalContext<Variable, Type>;
+    _ConditionalContext<Type> context =
+        _stack.last as _ConditionalContext<Type>;
     context._thenInfo = _expressionEnd(thenExpression);
     _current = context._conditionInfo!.ifFalse;
   }
@@ -3602,10 +3587,10 @@
   @override
   void conditional_end(
       Expression conditionalExpression, Expression elseExpression) {
-    _ConditionalContext<Variable, Type> context =
-        _stack.removeLast() as _ConditionalContext<Variable, Type>;
-    ExpressionInfo<Variable, Type> thenInfo = context._thenInfo!;
-    ExpressionInfo<Variable, Type> elseInfo = _expressionEnd(elseExpression);
+    _ConditionalContext<Type> context =
+        _stack.removeLast() as _ConditionalContext<Type>;
+    ExpressionInfo<Type> thenInfo = context._thenInfo!;
+    ExpressionInfo<Type> elseInfo = _expressionEnd(elseExpression);
     _storeExpressionInfo(
         conditionalExpression,
         new ExpressionInfo(
@@ -3616,22 +3601,23 @@
 
   @override
   void conditional_thenBegin(Expression condition, Node conditionalExpression) {
-    ExpressionInfo<Variable, Type> conditionInfo = _expressionEnd(condition);
+    ExpressionInfo<Type> conditionInfo = _expressionEnd(condition);
     _stack.add(new _ConditionalContext(conditionInfo));
     _current = conditionInfo.ifTrue;
   }
 
   @override
   void declare(Variable variable, bool initialized) {
-    _current = _current.declare(variable, initialized);
+    _current = _current.declare(
+        variable, _promotionKeyStore.keyForVariable(variable), initialized);
   }
 
   @override
   void doStatement_bodyBegin(Statement doStatement) {
-    AssignedVariablesNodeInfo<Variable> info =
+    AssignedVariablesNodeInfo info =
         _assignedVariables._getInfoForNode(doStatement);
-    _BranchTargetContext<Variable, Type> context =
-        new _BranchTargetContext<Variable, Type>(_current.reachable);
+    _BranchTargetContext<Type> context =
+        new _BranchTargetContext<Type>(_current.reachable);
     _stack.add(context);
     _current = _current.conservativeJoin(info._written, info._captured).split();
     _statementToContext[doStatement] = context;
@@ -3639,39 +3625,34 @@
 
   @override
   void doStatement_conditionBegin() {
-    _BranchTargetContext<Variable, Type> context =
-        _stack.last as _BranchTargetContext<Variable, Type>;
+    _BranchTargetContext<Type> context =
+        _stack.last as _BranchTargetContext<Type>;
     _current = _join(_current, context._continueModel);
   }
 
   @override
   void doStatement_end(Expression condition) {
-    _BranchTargetContext<Variable, Type> context =
-        _stack.removeLast() as _BranchTargetContext<Variable, Type>;
+    _BranchTargetContext<Type> context =
+        _stack.removeLast() as _BranchTargetContext<Type>;
     _current = _merge(_expressionEnd(condition).ifFalse, context._breakModel);
   }
 
   @override
-  EqualityInfo<Variable, Type> equalityOperand_end(
-          Expression operand, Type type) =>
-      new EqualityInfo<Variable, Type>._(
+  EqualityInfo<Type> equalityOperand_end(Expression operand, Type type) =>
+      new EqualityInfo<Type>._(
           _getExpressionInfo(operand), type, _getExpressionReference(operand));
 
   @override
-  void equalityOperation_end(
-      Expression wholeExpression,
-      EqualityInfo<Variable, Type>? leftOperandInfo,
-      EqualityInfo<Variable, Type>? rightOperandInfo,
+  void equalityOperation_end(Expression wholeExpression,
+      EqualityInfo<Type>? leftOperandInfo, EqualityInfo<Type>? rightOperandInfo,
       {bool notEqual = false}) {
     // Note: leftOperandInfo and rightOperandInfo are nullable in the base class
     // to account for the fact that legacy type promotion doesn't record
     // information about legacy operands.  But since we are currently in full
     // (post null safety) flow analysis logic, we can safely assume that they
     // are not null.
-    ReferenceWithType<Variable, Type>? lhsReference =
-        leftOperandInfo!._reference;
-    ReferenceWithType<Variable, Type>? rhsReference =
-        rightOperandInfo!._reference;
+    ReferenceWithType<Type>? lhsReference = leftOperandInfo!._reference;
+    ReferenceWithType<Type>? rhsReference = rightOperandInfo!._reference;
     TypeClassification leftOperandTypeClassification =
         operations.classifyType(leftOperandInfo._type);
     TypeClassification rightOperandTypeClassification =
@@ -3689,15 +3670,15 @@
       // but weak mode it might produce an "equal" result.  We don't want flow
       // analysis behavior to depend on mode, so we conservatively assume that
       // either result is possible.
-    } else if (leftOperandInfo._expressionInfo is _NullInfo<Variable, Type> &&
+    } else if (leftOperandInfo._expressionInfo is _NullInfo<Type> &&
         rhsReference != null) {
-      ExpressionInfo<Variable, Type> equalityInfo =
+      ExpressionInfo<Type> equalityInfo =
           _current.tryMarkNonNullable(operations, rhsReference);
       _storeExpressionInfo(
           wholeExpression, notEqual ? equalityInfo : equalityInfo.invert());
-    } else if (rightOperandInfo._expressionInfo is _NullInfo<Variable, Type> &&
+    } else if (rightOperandInfo._expressionInfo is _NullInfo<Type> &&
         lhsReference != null) {
-      ExpressionInfo<Variable, Type> equalityInfo =
+      ExpressionInfo<Type> equalityInfo =
           _current.tryMarkNonNullable(operations, lhsReference);
       _storeExpressionInfo(
           wholeExpression, notEqual ? equalityInfo : equalityInfo.invert());
@@ -3705,7 +3686,7 @@
   }
 
   @override
-  ExpressionInfo<Variable, Type>? expressionInfoForTesting(Expression target) =>
+  ExpressionInfo<Type>? expressionInfoForTesting(Expression target) =>
       identical(target, _expressionWithInfo) ? _expressionInfo : null;
 
   @override
@@ -3716,11 +3697,11 @@
 
   @override
   void for_bodyBegin(Statement? node, Expression? condition) {
-    ExpressionInfo<Variable, Type> conditionInfo = condition == null
+    ExpressionInfo<Type> conditionInfo = condition == null
         ? new ExpressionInfo(_current, _current, _current.setUnreachable())
         : _expressionEnd(condition);
-    _WhileContext<Variable, Type> context = new _WhileContext<Variable, Type>(
-        _current.reachable.parent!, conditionInfo);
+    _WhileContext<Type> context =
+        new _WhileContext<Type>(_current.reachable.parent!, conditionInfo);
     _stack.add(context);
     if (node != null) {
       _statementToContext[node] = context;
@@ -3730,18 +3711,16 @@
 
   @override
   void for_conditionBegin(Node node) {
-    AssignedVariablesNodeInfo<Variable> info =
-        _assignedVariables._getInfoForNode(node);
+    AssignedVariablesNodeInfo info = _assignedVariables._getInfoForNode(node);
     _current = _current.conservativeJoin(info._written, info._captured).split();
   }
 
   @override
   void for_end() {
-    _WhileContext<Variable, Type> context =
-        _stack.removeLast() as _WhileContext<Variable, Type>;
+    _WhileContext<Type> context = _stack.removeLast() as _WhileContext<Type>;
     // Tail of the stack: falseCondition, break
-    FlowModel<Variable, Type>? breakState = context._breakModel;
-    FlowModel<Variable, Type> falseCondition = context._conditionInfo.ifFalse;
+    FlowModel<Type>? breakState = context._breakModel;
+    FlowModel<Type> falseCondition = context._conditionInfo.ifFalse;
 
     _current =
         _merge(falseCondition, breakState).inheritTested(operations, _current);
@@ -3749,26 +3728,23 @@
 
   @override
   void for_updaterBegin() {
-    _WhileContext<Variable, Type> context =
-        _stack.last as _WhileContext<Variable, Type>;
+    _WhileContext<Type> context = _stack.last as _WhileContext<Type>;
     _current = _join(_current, context._continueModel);
   }
 
   @override
   void forEach_bodyBegin(Node node) {
-    AssignedVariablesNodeInfo<Variable> info =
-        _assignedVariables._getInfoForNode(node);
+    AssignedVariablesNodeInfo info = _assignedVariables._getInfoForNode(node);
     _current = _current.conservativeJoin(info._written, info._captured).split();
-    _SimpleStatementContext<Variable, Type> context =
-        new _SimpleStatementContext<Variable, Type>(
-            _current.reachable.parent!, _current);
+    _SimpleStatementContext<Type> context =
+        new _SimpleStatementContext<Type>(_current.reachable.parent!, _current);
     _stack.add(context);
   }
 
   @override
   void forEach_end() {
-    _SimpleStatementContext<Variable, Type> context =
-        _stack.removeLast() as _SimpleStatementContext<Variable, Type>;
+    _SimpleStatementContext<Type> context =
+        _stack.removeLast() as _SimpleStatementContext<Type>;
     _current = _merge(_current, context._previous);
   }
 
@@ -3784,8 +3760,7 @@
 
   @override
   void functionExpression_begin(Node node) {
-    AssignedVariablesNodeInfo<Variable> info =
-        _assignedVariables._getInfoForNode(node);
+    AssignedVariablesNodeInfo info = _assignedVariables._getInfoForNode(node);
     _current = _current.conservativeJoin(const [], info._written);
     _stack.add(new _FunctionExpressionContext(_current));
     _current = _current.conservativeJoin(_assignedVariables._anywhere._written,
@@ -3794,14 +3769,14 @@
 
   @override
   void functionExpression_end() {
-    _SimpleContext<Variable, Type> context =
-        _stack.removeLast() as _FunctionExpressionContext<Variable, Type>;
+    _SimpleContext<Type> context =
+        _stack.removeLast() as _FunctionExpressionContext<Type>;
     _current = context._previous;
   }
 
   @override
   void handleBreak(Statement target) {
-    _BranchTargetContext<Variable, Type>? context = _statementToContext[target];
+    _BranchTargetContext<Type>? context = _statementToContext[target];
     if (context != null) {
       context._breakModel =
           _join(context._breakModel, _current.unsplitTo(context._checkpoint));
@@ -3811,7 +3786,7 @@
 
   @override
   void handleContinue(Statement target) {
-    _BranchTargetContext<Variable, Type>? context = _statementToContext[target];
+    _BranchTargetContext<Type>? context = _statementToContext[target];
     if (context != null) {
       context._continueModel = _join(
           context._continueModel, _current.unsplitTo(context._checkpoint));
@@ -3826,27 +3801,27 @@
 
   @override
   void ifNullExpression_end() {
-    _IfNullExpressionContext<Variable, Type> context =
-        _stack.removeLast() as _IfNullExpressionContext<Variable, Type>;
+    _IfNullExpressionContext<Type> context =
+        _stack.removeLast() as _IfNullExpressionContext<Type>;
     _current = _merge(_current, context._previous);
   }
 
   @override
   void ifNullExpression_rightBegin(
       Expression leftHandSide, Type leftHandSideType) {
-    ReferenceWithType<Variable, Type>? lhsReference =
+    ReferenceWithType<Type>? lhsReference =
         _getExpressionReference(leftHandSide);
-    FlowModel<Variable, Type> promoted;
+    FlowModel<Type> promoted;
     _current = _current.split();
     if (lhsReference != null) {
-      ExpressionInfo<Variable, Type> promotionInfo =
+      ExpressionInfo<Type> promotionInfo =
           _current.tryMarkNonNullable(operations, lhsReference);
       _current = promotionInfo.ifFalse;
       promoted = promotionInfo.ifTrue;
     } else {
       promoted = _current;
     }
-    _stack.add(new _IfNullExpressionContext<Variable, Type>(promoted));
+    _stack.add(new _IfNullExpressionContext<Type>(promoted));
   }
 
   @override
@@ -3856,18 +3831,16 @@
 
   @override
   void ifStatement_elseBegin() {
-    _IfContext<Variable, Type> context =
-        _stack.last as _IfContext<Variable, Type>;
+    _IfContext<Type> context = _stack.last as _IfContext<Type>;
     context._afterThen = _current;
     _current = context._conditionInfo!.ifFalse;
   }
 
   @override
   void ifStatement_end(bool hasElse) {
-    _IfContext<Variable, Type> context =
-        _stack.removeLast() as _IfContext<Variable, Type>;
-    FlowModel<Variable, Type> afterThen;
-    FlowModel<Variable, Type> afterElse;
+    _IfContext<Type> context = _stack.removeLast() as _IfContext<Type>;
+    FlowModel<Type> afterThen;
+    FlowModel<Type> afterElse;
     if (hasElse) {
       afterThen = context._afterThen!;
       afterElse = _current;
@@ -3880,7 +3853,7 @@
 
   @override
   void ifStatement_thenBegin(Expression condition, Node ifNode) {
-    ExpressionInfo<Variable, Type> conditionInfo = _expressionEnd(condition);
+    ExpressionInfo<Type> conditionInfo = _expressionEnd(condition);
     _stack.add(new _IfContext(conditionInfo));
     _current = conditionInfo.ifTrue;
   }
@@ -3891,7 +3864,8 @@
       {required bool isFinal,
       required bool isLate,
       required bool isImplicitlyTyped}) {
-    ExpressionInfo<Variable, Type>? expressionInfo;
+    int variableKey = _promotionKeyStore.keyForVariable(variable);
+    ExpressionInfo<Type>? expressionInfo;
     if (isLate) {
       // Don't get expression info for late variables, since we don't know when
       // they'll be initialized.
@@ -3903,17 +3877,17 @@
     } else {
       expressionInfo = _getExpressionInfo(initializerExpression);
     }
-    SsaNode<Variable, Type> newSsaNode = new SsaNode<Variable, Type>(
+    SsaNode<Type> newSsaNode = new SsaNode<Type>(
         expressionInfo is _TrivialExpressionInfo ? null : expressionInfo);
     _current = _current.write(
-        null, variable, initializerType, newSsaNode, operations,
+        null, variable, variableKey, initializerType, newSsaNode, operations,
         promoteToTypeOfInterest: !isImplicitlyTyped && !isFinal);
     if (isImplicitlyTyped && operations.isTypeParameterType(initializerType)) {
       _current = _current
           .tryPromoteForTypeCheck(
               operations,
-              new ReferenceWithType<Variable, Type>(
-                  new VariableReference<Variable, Type>(variable),
+              new ReferenceWithType<Type>(
+                  new VariableReference<Variable, Type>(variable, variableKey),
                   promotedType(variable) ?? operations.variableType(variable)),
               initializerType)
           .ifTrue;
@@ -3922,17 +3896,19 @@
 
   @override
   bool isAssigned(Variable variable) {
-    return _current.infoFor(variable).assigned;
+    return _current
+        .infoFor(_promotionKeyStore.keyForVariable(variable))
+        .assigned;
   }
 
   @override
   void isExpression_end(Expression isExpression, Expression subExpression,
       bool isNot, Type type) {
-    ReferenceWithType<Variable, Type>? subExpressionReference =
+    ReferenceWithType<Type>? subExpressionReference =
         _getExpressionReference(subExpression);
     if (subExpressionReference != null) {
-      ExpressionInfo<Variable, Type> expressionInfo = _current
-          .tryPromoteForTypeCheck(operations, subExpressionReference, type);
+      ExpressionInfo<Type> expressionInfo = _current.tryPromoteForTypeCheck(
+          operations, subExpressionReference, type);
       _storeExpressionInfo(
           isExpression, isNot ? expressionInfo.invert() : expressionInfo);
     }
@@ -3940,22 +3916,24 @@
 
   @override
   bool isUnassigned(Variable variable) {
-    return _current.infoFor(variable).unassigned;
+    return _current
+        .infoFor(_promotionKeyStore.keyForVariable(variable))
+        .unassigned;
   }
 
   @override
   void labeledStatement_begin(Statement node) {
     _current = _current.split();
-    _BranchTargetContext<Variable, Type> context =
-        new _BranchTargetContext<Variable, Type>(_current.reachable.parent!);
+    _BranchTargetContext<Type> context =
+        new _BranchTargetContext<Type>(_current.reachable.parent!);
     _stack.add(context);
     _statementToContext[node] = context;
   }
 
   @override
   void labeledStatement_end() {
-    _BranchTargetContext<Variable, Type> context =
-        _stack.removeLast() as _BranchTargetContext<Variable, Type>;
+    _BranchTargetContext<Type> context =
+        _stack.removeLast() as _BranchTargetContext<Type>;
     _current = _merge(_current, context._breakModel);
   }
 
@@ -3987,12 +3965,11 @@
   @override
   void logicalBinaryOp_end(Expression wholeExpression, Expression rightOperand,
       {required bool isAnd}) {
-    _BranchContext<Variable, Type> context =
-        _stack.removeLast() as _BranchContext<Variable, Type>;
-    ExpressionInfo<Variable, Type> rhsInfo = _expressionEnd(rightOperand);
+    _BranchContext<Type> context = _stack.removeLast() as _BranchContext<Type>;
+    ExpressionInfo<Type> rhsInfo = _expressionEnd(rightOperand);
 
-    FlowModel<Variable, Type> trueResult;
-    FlowModel<Variable, Type> falseResult;
+    FlowModel<Type> trueResult;
+    FlowModel<Type> falseResult;
     if (isAnd) {
       trueResult = rhsInfo.ifTrue;
       falseResult = _join(context._conditionInfo!.ifFalse, rhsInfo.ifFalse);
@@ -4009,20 +3986,20 @@
   @override
   void logicalBinaryOp_rightBegin(Expression leftOperand, Node wholeExpression,
       {required bool isAnd}) {
-    ExpressionInfo<Variable, Type> conditionInfo = _expressionEnd(leftOperand);
-    _stack.add(new _BranchContext<Variable, Type>(conditionInfo));
+    ExpressionInfo<Type> conditionInfo = _expressionEnd(leftOperand);
+    _stack.add(new _BranchContext<Type>(conditionInfo));
     _current = isAnd ? conditionInfo.ifTrue : conditionInfo.ifFalse;
   }
 
   @override
   void logicalNot_end(Expression notExpression, Expression operand) {
-    ExpressionInfo<Variable, Type> conditionInfo = _expressionEnd(operand);
+    ExpressionInfo<Type> conditionInfo = _expressionEnd(operand);
     _storeExpressionInfo(notExpression, conditionInfo.invert());
   }
 
   @override
   void nonNullAssert_end(Expression operand) {
-    ReferenceWithType<Variable, Type>? operandReference =
+    ReferenceWithType<Type>? operandReference =
         _getExpressionReference(operand);
     if (operandReference != null) {
       _current =
@@ -4032,8 +4009,8 @@
 
   @override
   void nullAwareAccess_end() {
-    _NullAwareAccessContext<Variable, Type> context =
-        _stack.removeLast() as _NullAwareAccessContext<Variable, Type>;
+    _NullAwareAccessContext<Type> context =
+        _stack.removeLast() as _NullAwareAccessContext<Type>;
     _current = _merge(_current, context._previous);
   }
 
@@ -4042,9 +4019,8 @@
     // ignore:unnecessary_null_comparison
     assert(targetType != null);
     _current = _current.split();
-    _stack.add(new _NullAwareAccessContext<Variable, Type>(_current));
-    ReferenceWithType<Variable, Type>? targetReference =
-        _getExpressionReference(target);
+    _stack.add(new _NullAwareAccessContext<Type>(_current));
+    ReferenceWithType<Type>? targetReference = _getExpressionReference(target);
     if (targetReference != null) {
       _current =
           _current.tryMarkNonNullable(operations, targetReference).ifTrue;
@@ -4064,32 +4040,35 @@
 
   @override
   Type? promotedType(Variable variable) {
-    return _current.infoFor(variable).promotedTypes?.last;
+    return _current
+        .infoFor(_promotionKeyStore.keyForVariable(variable))
+        .promotedTypes
+        ?.last;
   }
 
   @override
   void propertyGet(Expression wholeExpression, Expression target,
       String propertyName, Object? propertyMember, Type staticType) {
-    Reference<Variable, Type>? reference =
-        _getExpressionReference(target)?.reference;
+    Reference<Type>? reference = _getExpressionReference(target)?.reference;
     if (reference != null) {
       _storeExpressionReference(
           wholeExpression,
-          new ReferenceWithType<Variable, Type>(
-              reference.propertyGet(propertyName, propertyMember), staticType));
+          new ReferenceWithType<Type>(
+              reference.propertyGet(
+                  _promotionKeyStore, propertyName, propertyMember),
+              staticType));
     }
   }
 
   @override
-  SsaNode<Variable, Type>? ssaNodeForTesting(Variable variable) =>
-      _current.variableInfo[variable]?.ssaNode;
+  SsaNode<Type>? ssaNodeForTesting(Variable variable) => _current
+      .variableInfo[_promotionKeyStore.keyForVariable(variable)]?.ssaNode;
 
   @override
   void switchStatement_beginCase(bool hasLabel, Node node) {
-    AssignedVariablesNodeInfo<Variable> info =
-        _assignedVariables._getInfoForNode(node);
-    _SimpleStatementContext<Variable, Type> context =
-        _stack.last as _SimpleStatementContext<Variable, Type>;
+    AssignedVariablesNodeInfo info = _assignedVariables._getInfoForNode(node);
+    _SimpleStatementContext<Type> context =
+        _stack.last as _SimpleStatementContext<Type>;
     if (hasLabel) {
       _current =
           context._previous.conservativeJoin(info._written, info._captured);
@@ -4100,9 +4079,9 @@
 
   @override
   void switchStatement_end(bool isExhaustive) {
-    _SimpleStatementContext<Variable, Type> context =
-        _stack.removeLast() as _SimpleStatementContext<Variable, Type>;
-    FlowModel<Variable, Type>? breakState = context._breakModel;
+    _SimpleStatementContext<Type> context =
+        _stack.removeLast() as _SimpleStatementContext<Type>;
+    FlowModel<Type>? breakState = context._breakModel;
 
     // It is allowed to "fall off" the end of a switch statement, so join the
     // current state to any breaks that were found previously.
@@ -4117,9 +4096,8 @@
   @override
   void switchStatement_expressionEnd(Statement switchStatement) {
     _current = _current.split();
-    _SimpleStatementContext<Variable, Type> context =
-        new _SimpleStatementContext<Variable, Type>(
-            _current.reachable.parent!, _current);
+    _SimpleStatementContext<Type> context =
+        new _SimpleStatementContext<Type>(_current.reachable.parent!, _current);
     _stack.add(context);
     _statementToContext[switchStatement] = context;
   }
@@ -4128,8 +4106,9 @@
   void thisOrSuper(Expression expression, Type staticType) {
     _storeExpressionReference(
         expression,
-        new ReferenceWithType<Variable, Type>(
-            new _ThisReference<Variable, Type>(), staticType));
+        new ReferenceWithType<Type>(
+            new _ThisReference<Type>(_promotionKeyStore.thisPromotionKey),
+            staticType));
   }
 
   @override
@@ -4137,29 +4116,27 @@
       Object? propertyMember, Type staticType) {
     _storeExpressionReference(
         expression,
-        new ReferenceWithType<Variable, Type>(
-            new _ThisReference<Variable, Type>()
-                .propertyGet(propertyName, propertyMember),
+        new ReferenceWithType<Type>(
+            new _ThisReference<Type>(_promotionKeyStore.thisPromotionKey)
+                .propertyGet(_promotionKeyStore, propertyName, propertyMember),
             staticType));
   }
 
   @override
   void tryCatchStatement_bodyBegin() {
     _current = _current.split();
-    _stack.add(new _TryContext<Variable, Type>(_current));
+    _stack.add(new _TryContext<Type>(_current));
   }
 
   @override
   void tryCatchStatement_bodyEnd(Node body) {
-    FlowModel<Variable, Type> afterBody = _current;
+    FlowModel<Type> afterBody = _current;
 
-    _TryContext<Variable, Type> context =
-        _stack.last as _TryContext<Variable, Type>;
-    FlowModel<Variable, Type> beforeBody = context._previous;
+    _TryContext<Type> context = _stack.last as _TryContext<Type>;
+    FlowModel<Type> beforeBody = context._previous;
 
-    AssignedVariablesNodeInfo<Variable> info =
-        _assignedVariables._getInfoForNode(body);
-    FlowModel<Variable, Type> beforeCatch =
+    AssignedVariablesNodeInfo info = _assignedVariables._getInfoForNode(body);
+    FlowModel<Type> beforeCatch =
         beforeBody.conservativeJoin(info._written, info._captured);
 
     context._beforeCatch = beforeCatch;
@@ -4169,51 +4146,52 @@
   @override
   void tryCatchStatement_catchBegin(
       Variable? exceptionVariable, Variable? stackTraceVariable) {
-    _TryContext<Variable, Type> context =
-        _stack.last as _TryContext<Variable, Type>;
+    _TryContext<Type> context = _stack.last as _TryContext<Type>;
     _current = context._beforeCatch!;
     if (exceptionVariable != null) {
-      _current = _current.declare(exceptionVariable, true);
+      int exceptionVariableKey =
+          _promotionKeyStore.keyForVariable(exceptionVariable);
+      _current =
+          _current.declare(exceptionVariable, exceptionVariableKey, true);
     }
     if (stackTraceVariable != null) {
-      _current = _current.declare(stackTraceVariable, true);
+      int stackTraceVariableKey =
+          _promotionKeyStore.keyForVariable(stackTraceVariable);
+      _current =
+          _current.declare(stackTraceVariable, stackTraceVariableKey, true);
     }
   }
 
   @override
   void tryCatchStatement_catchEnd() {
-    _TryContext<Variable, Type> context =
-        _stack.last as _TryContext<Variable, Type>;
+    _TryContext<Type> context = _stack.last as _TryContext<Type>;
     context._afterBodyAndCatches =
         _join(context._afterBodyAndCatches, _current);
   }
 
   @override
   void tryCatchStatement_end() {
-    _TryContext<Variable, Type> context =
-        _stack.removeLast() as _TryContext<Variable, Type>;
+    _TryContext<Type> context = _stack.removeLast() as _TryContext<Type>;
     _current = context._afterBodyAndCatches!.unsplit();
   }
 
   @override
   void tryFinallyStatement_bodyBegin() {
-    _stack.add(new _TryFinallyContext<Variable, Type>(_current));
+    _stack.add(new _TryFinallyContext<Type>(_current));
   }
 
   @override
   void tryFinallyStatement_end() {
-    _TryFinallyContext<Variable, Type> context =
-        _stack.removeLast() as _TryFinallyContext<Variable, Type>;
+    _TryFinallyContext<Type> context =
+        _stack.removeLast() as _TryFinallyContext<Type>;
     _current = context._afterBodyAndCatches!
         .attachFinally(operations, context._beforeFinally, _current);
   }
 
   @override
   void tryFinallyStatement_finallyBegin(Node body) {
-    AssignedVariablesNodeInfo<Variable> info =
-        _assignedVariables._getInfoForNode(body);
-    _TryFinallyContext<Variable, Type> context =
-        _stack.last as _TryFinallyContext<Variable, Type>;
+    AssignedVariablesNodeInfo info = _assignedVariables._getInfoForNode(body);
+    _TryFinallyContext<Type> context = _stack.last as _TryFinallyContext<Type>;
     context._afterBodyAndCatches = _current;
     _current = _join(_current,
         context._previous.conservativeJoin(info._written, info._captured));
@@ -4222,16 +4200,16 @@
 
   @override
   Type? variableRead(Expression expression, Variable variable) {
+    int variableKey = _promotionKeyStore.keyForVariable(variable);
     VariableReference<Variable, Type> variableReference =
-        new VariableReference<Variable, Type>(variable);
-    VariableModel<Variable, Type> variableModel =
+        new VariableReference<Variable, Type>(variable, variableKey);
+    VariableModel<Type> variableModel =
         variableReference.getInfo(_current.variableInfo);
     Type? promotedType = variableModel.promotedTypes?.last;
     Type currentType = promotedType ?? operations.variableType(variable);
     _storeExpressionReference(expression,
-        new ReferenceWithType<Variable, Type>(variableReference, currentType));
-    ExpressionInfo<Variable, Type>? expressionInfo = variableModel
-        .ssaNode?.expressionInfo
+        new ReferenceWithType<Type>(variableReference, currentType));
+    ExpressionInfo<Type>? expressionInfo = variableModel.ssaNode?.expressionInfo
         ?.rebaseForward(operations, _current);
     if (expressionInfo != null) {
       _storeExpressionInfo(expression, expressionInfo);
@@ -4242,9 +4220,9 @@
   @override
   void whileStatement_bodyBegin(
       Statement whileStatement, Expression condition) {
-    ExpressionInfo<Variable, Type> conditionInfo = _expressionEnd(condition);
-    _WhileContext<Variable, Type> context = new _WhileContext<Variable, Type>(
-        _current.reachable.parent!, conditionInfo);
+    ExpressionInfo<Type> conditionInfo = _expressionEnd(condition);
+    _WhileContext<Type> context =
+        new _WhileContext<Type>(_current.reachable.parent!, conditionInfo);
     _stack.add(context);
     _statementToContext[whileStatement] = context;
     _current = conditionInfo.ifTrue;
@@ -4253,15 +4231,13 @@
   @override
   void whileStatement_conditionBegin(Node node) {
     _current = _current.split();
-    AssignedVariablesNodeInfo<Variable> info =
-        _assignedVariables._getInfoForNode(node);
+    AssignedVariablesNodeInfo info = _assignedVariables._getInfoForNode(node);
     _current = _current.conservativeJoin(info._written, info._captured);
   }
 
   @override
   void whileStatement_end() {
-    _WhileContext<Variable, Type> context =
-        _stack.removeLast() as _WhileContext<Variable, Type>;
+    _WhileContext<Type> context = _stack.removeLast() as _WhileContext<Type>;
     _current = _merge(context._conditionInfo.ifFalse, context._breakModel)
         .inheritTested(operations, _current);
   }
@@ -4269,8 +4245,7 @@
   @override
   Map<Type, NonPromotionReason> Function() whyNotPromoted(Expression target) {
     if (identical(target, _expressionWithReference)) {
-      ReferenceWithType<Variable, Type>? referenceWithType =
-          _expressionReference;
+      ReferenceWithType<Type>? referenceWithType = _expressionReference;
       if (referenceWithType != null) {
         return referenceWithType.reference.getNonPromotionReasons(
             _current.variableInfo, referenceWithType.type, operations);
@@ -4282,21 +4257,23 @@
   @override
   Map<Type, NonPromotionReason> Function() whyNotPromotedImplicitThis(
       Type staticType) {
-    return new _ThisReference<Variable, Type>()
+    return new _ThisReference<Type>(_promotionKeyStore.thisPromotionKey)
         .getNonPromotionReasons(_current.variableInfo, staticType, operations);
   }
 
   @override
   void write(Node node, Variable variable, Type writtenType,
       Expression? writtenExpression) {
-    ExpressionInfo<Variable, Type>? expressionInfo = writtenExpression == null
+    int variableKey = _promotionKeyStore.keyForVariable(variable);
+    ExpressionInfo<Type>? expressionInfo = writtenExpression == null
         ? null
         : _getExpressionInfo(writtenExpression);
-    SsaNode<Variable, Type> newSsaNode = new SsaNode<Variable, Type>(
+    SsaNode<Type> newSsaNode = new SsaNode<Type>(
         expressionInfo is _TrivialExpressionInfo ? null : expressionInfo);
     _current = _current.write(
         new DemoteViaExplicitWrite<Variable>(variable, node),
         variable,
+        variableKey,
         writtenType,
         newSsaNode,
         operations);
@@ -4319,16 +4296,16 @@
   /// be the last expression that was traversed).  If there is no
   /// [ExpressionInfo] associated with the [expression], then a fresh
   /// [ExpressionInfo] is created recording the current flow analysis state.
-  ExpressionInfo<Variable, Type> _expressionEnd(Expression expression) =>
+  ExpressionInfo<Type> _expressionEnd(Expression expression) =>
       _getExpressionInfo(expression) ?? new _TrivialExpressionInfo(_current);
 
   /// Gets the [ExpressionInfo] associated with the [expression] (which should
   /// be the last expression that was traversed).  If there is no
   /// [ExpressionInfo] associated with the [expression], then `null` is
   /// returned.
-  ExpressionInfo<Variable, Type>? _getExpressionInfo(Expression expression) {
+  ExpressionInfo<Type>? _getExpressionInfo(Expression expression) {
     if (identical(expression, _expressionWithInfo)) {
-      ExpressionInfo<Variable, Type>? expressionInfo = _expressionInfo;
+      ExpressionInfo<Type>? expressionInfo = _expressionInfo;
       _expressionInfo = null;
       return expressionInfo;
     } else {
@@ -4339,11 +4316,9 @@
   /// Gets the [Reference] associated with the [expression] (which should be the
   /// last expression that was traversed).  If there is no [Reference]
   /// associated with the [expression], then `null` is returned.
-  ReferenceWithType<Variable, Type>? _getExpressionReference(
-      Expression? expression) {
+  ReferenceWithType<Type>? _getExpressionReference(Expression? expression) {
     if (identical(expression, _expressionWithReference)) {
-      ReferenceWithType<Variable, Type>? expressionReference =
-          _expressionReference;
+      ReferenceWithType<Type>? expressionReference = _expressionReference;
       _expressionReference = null;
       return expressionReference;
     } else {
@@ -4351,19 +4326,17 @@
     }
   }
 
-  FlowModel<Variable, Type> _join(FlowModel<Variable, Type>? first,
-          FlowModel<Variable, Type>? second) =>
+  FlowModel<Type> _join(FlowModel<Type>? first, FlowModel<Type>? second) =>
       FlowModel.join(operations, first, second, _current._emptyVariableMap);
 
-  FlowModel<Variable, Type> _merge(
-          FlowModel<Variable, Type> first, FlowModel<Variable, Type>? second) =>
+  FlowModel<Type> _merge(FlowModel<Type> first, FlowModel<Type>? second) =>
       FlowModel.merge(operations, first, second, _current._emptyVariableMap);
 
   /// Associates [expression], which should be the most recently visited
   /// expression, with the given [expressionInfo] object, and updates the
   /// current flow model state to correspond to it.
   void _storeExpressionInfo(
-      Expression expression, ExpressionInfo<Variable, Type> expressionInfo) {
+      Expression expression, ExpressionInfo<Type> expressionInfo) {
     _expressionWithInfo = expression;
     _expressionInfo = expressionInfo;
     _current = expressionInfo.after;
@@ -4371,8 +4344,8 @@
 
   /// Associates [expression], which should be the most recently visited
   /// expression, with the given [Reference] object.
-  void _storeExpressionReference(Expression expression,
-      ReferenceWithType<Variable, Type> expressionReference) {
+  void _storeExpressionReference(
+      Expression expression, ReferenceWithType<Type> expressionReference) {
     _expressionWithReference = expression;
     _expressionReference = expressionReference;
   }
@@ -4383,8 +4356,8 @@
 abstract class _FlowContext {}
 
 /// [_FlowContext] representing a function expression.
-class _FunctionExpressionContext<Variable extends Object, Type extends Object>
-    extends _SimpleContext<Variable, Type> {
+class _FunctionExpressionContext<Type extends Object>
+    extends _SimpleContext<Type> {
   _FunctionExpressionContext(super.previous);
 
   @override
@@ -4392,13 +4365,12 @@
 }
 
 /// [_FlowContext] representing an `if` statement.
-class _IfContext<Variable extends Object, Type extends Object>
-    extends _BranchContext<Variable, Type> {
+class _IfContext<Type extends Object> extends _BranchContext<Type> {
   /// Flow model associated with the state of program execution after the `if`
   /// statement executes, in the circumstance where the "then" branch is taken.
-  FlowModel<Variable, Type>? _afterThen;
+  FlowModel<Type>? _afterThen;
 
-  _IfContext(ExpressionInfo<Variable, Type> super.conditionInfo);
+  _IfContext(ExpressionInfo<Type> super.conditionInfo);
 
   @override
   String toString() =>
@@ -4406,8 +4378,8 @@
 }
 
 /// [_FlowContext] representing an "if-null" (`??`) expression.
-class _IfNullExpressionContext<Variable extends Object, Type extends Object>
-    extends _SimpleContext<Variable, Type> {
+class _IfNullExpressionContext<Type extends Object>
+    extends _SimpleContext<Type> {
   _IfNullExpressionContext(super.previous);
 
   @override
@@ -4416,14 +4388,14 @@
 
 /// Contextual information tracked by legacy type promotion about a binary "and"
 /// expression (`&&`).
-class _LegacyBinaryAndContext<Variable extends Object, Type extends Object>
-    extends _LegacyContext<Variable, Type> {
+class _LegacyBinaryAndContext<Type extends Object>
+    extends _LegacyContext<Type> {
   /// Types that were shown by the LHS of the "and" expression.
-  final Map<Variable, Type> _lhsShownTypes;
+  final Map<int, Type> _lhsShownTypes;
 
   /// Information about variables that might be assigned by the RHS of the "and"
   /// expression.
-  final AssignedVariablesNodeInfo<Variable> _assignedVariablesInfoForRhs;
+  final AssignedVariablesNodeInfo _assignedVariablesInfoForRhs;
 
   _LegacyBinaryAndContext(super.previousKnownTypes, this._lhsShownTypes,
       this._assignedVariablesInfoForRhs);
@@ -4431,22 +4403,22 @@
 
 /// Contextual information tracked by legacy type promotion about a statement or
 /// expression.
-class _LegacyContext<Variable, Type> {
+class _LegacyContext<Type> {
   /// The set of known types in effect before the statement or expression in
   /// question was encountered.
-  final Map<Variable, Type> _previousKnownTypes;
+  final Map<int, Type> _previousKnownTypes;
 
   _LegacyContext(this._previousKnownTypes);
 }
 
 /// Data tracked by legacy type promotion about an expression.
-class _LegacyExpressionInfo<Variable, Type> {
+class _LegacyExpressionInfo<Type> {
   /// Variables whose types are "shown" by the expression in question.
   ///
   /// For example, the spec says that the expression `x is T` "shows" `x` to
   /// have type `T`, so accordingly, the [_LegacyExpressionInfo] for `x is T`
   /// will have an entry in this map that maps `x` to `T`.
-  final Map<Variable, Type> _shownTypes;
+  final Map<int, Type> _shownTypes;
 
   _LegacyExpressionInfo(this._shownTypes);
 
@@ -4474,14 +4446,14 @@
 
   /// If [_expressionWithInfo] is not `null`, the [_LegacyExpressionInfo] object
   /// corresponding to it.  Otherwise `null`.
-  _LegacyExpressionInfo<Variable, Type>? _expressionInfo;
+  _LegacyExpressionInfo<Type>? _expressionInfo;
 
   /// The set of type promotions currently in effect.
-  Map<Variable, Type> _knownTypes = {};
+  Map<int, Type> _knownTypes = {};
 
   /// Stack of [_LegacyContext] objects representing the statements and
   /// expressions that are currently being visited.
-  final List<_LegacyContext<Variable, Type>> _contextStack = [];
+  final List<_LegacyContext<Type>> _contextStack = [];
 
   /// Stack for tracking writes occurring on the LHS of a binary "and" (`&&`)
   /// operation.  Whenever we visit a write, we update the top entry in this
@@ -4489,9 +4461,12 @@
   /// a fresh empty entry onto this stack; accordingly, upon reaching the RHS of
   /// the binary "and", the top entry of the stack contains the set of variables
   /// written to during the LHS of the "and".
-  final List<Set<Variable>> _writeStackForAnd = [{}];
+  final List<Set<int>> _writeStackForAnd = [{}];
 
-  _LegacyTypePromotion(this._operations, this._assignedVariables);
+  final PromotionKeyStore<Variable> _promotionKeyStore;
+
+  _LegacyTypePromotion(this._operations, this._assignedVariables)
+      : _promotionKeyStore = _assignedVariables._promotionKeyStore;
 
   @override
   bool get isReachable => true;
@@ -4544,19 +4519,16 @@
   void doStatement_end(Expression condition) {}
 
   @override
-  EqualityInfo<Variable, Type>? equalityOperand_end(
-          Expression operand, Type type) =>
+  EqualityInfo<Type>? equalityOperand_end(Expression operand, Type type) =>
       null;
 
   @override
-  void equalityOperation_end(
-      Expression wholeExpression,
-      EqualityInfo<Variable, Type>? leftOperandInfo,
-      EqualityInfo<Variable, Type>? rightOperandInfo,
+  void equalityOperation_end(Expression wholeExpression,
+      EqualityInfo<Type>? leftOperandInfo, EqualityInfo<Type>? rightOperandInfo,
       {bool notEqual = false}) {}
 
   @override
-  ExpressionInfo<Variable, Type>? expressionInfoForTesting(Expression target) {
+  ExpressionInfo<Type>? expressionInfoForTesting(Expression target) {
     throw new StateError(
         'expressionInfoForTesting requires null-aware flow analysis');
   }
@@ -4648,19 +4620,18 @@
   @override
   void isExpression_end(Expression isExpression, Expression subExpression,
       bool isNot, Type type) {
-    _LegacyExpressionInfo<Variable, Type>? expressionInfo =
+    _LegacyExpressionInfo<Type>? expressionInfo =
         _getExpressionInfo(subExpression);
     if (!isNot && expressionInfo is _LegacyVariableReadInfo<Variable, Type>) {
       Variable variable = expressionInfo._variable;
+      int variableKey = expressionInfo._variableKey;
       Type currentType =
-          _knownTypes[variable] ?? _operations.variableType(variable);
+          _knownTypes[variableKey] ?? _operations.variableType(variable);
       Type? promotedType = _operations.tryPromoteToType(type, currentType);
       if (promotedType != null &&
           !_operations.isSameType(currentType, promotedType)) {
-        _storeExpressionInfo(
-            isExpression,
-            new _LegacyExpressionInfo<Variable, Type>(
-                {variable: promotedType}));
+        _storeExpressionInfo(isExpression,
+            new _LegacyExpressionInfo<Type>({variableKey: promotedType}));
       }
     }
   }
@@ -4691,13 +4662,13 @@
   void logicalBinaryOp_end(Expression wholeExpression, Expression rightOperand,
       {required bool isAnd}) {
     if (!isAnd) return;
-    _LegacyBinaryAndContext<Variable, Type> context =
-        _contextStack.removeLast() as _LegacyBinaryAndContext<Variable, Type>;
+    _LegacyBinaryAndContext<Type> context =
+        _contextStack.removeLast() as _LegacyBinaryAndContext<Type>;
     _knownTypes = context._previousKnownTypes;
-    AssignedVariablesNodeInfo<Variable> assignedVariablesInfoForRhs =
+    AssignedVariablesNodeInfo assignedVariablesInfoForRhs =
         context._assignedVariablesInfoForRhs;
-    Map<Variable, Type> lhsShownTypes = context._lhsShownTypes;
-    Map<Variable, Type> rhsShownTypes =
+    Map<int, Type> lhsShownTypes = context._lhsShownTypes;
+    Map<int, Type> rhsShownTypes =
         _getExpressionInfo(rightOperand)?._shownTypes ?? {};
     // A logical boolean expression b of the form `e1 && e2` shows that a local
     // variable v has type T if both of the following conditions hold:
@@ -4715,12 +4686,12 @@
     // type T2?  The de facto behavior we have had for a long time is to combine
     // the two types in the same way we would combine it if c were first
     // promoted to T1 and then had a successful `is T2` check.
-    Map<Variable, Type> newShownTypes = {};
-    for (MapEntry<Variable, Type> entry in lhsShownTypes.entries) {
+    Map<int, Type> newShownTypes = {};
+    for (MapEntry<int, Type> entry in lhsShownTypes.entries) {
       if (assignedVariablesInfoForRhs._written.contains(entry.key)) continue;
       newShownTypes[entry.key] = entry.value;
     }
-    for (MapEntry<Variable, Type> entry in rhsShownTypes.entries) {
+    for (MapEntry<int, Type> entry in rhsShownTypes.entries) {
       if (assignedVariablesInfoForRhs._written.contains(entry.key)) continue;
       Type? previouslyShownType = newShownTypes[entry.key];
       if (previouslyShownType == null) {
@@ -4734,24 +4705,24 @@
         }
       }
     }
-    _storeExpressionInfo(wholeExpression,
-        new _LegacyExpressionInfo<Variable, Type>(newShownTypes));
+    _storeExpressionInfo(
+        wholeExpression, new _LegacyExpressionInfo<Type>(newShownTypes));
   }
 
   @override
   void logicalBinaryOp_rightBegin(Expression leftOperand, Node wholeExpression,
       {required bool isAnd}) {
-    Set<Variable> variablesWrittenOnLhs = _writeStackForAnd.removeLast();
+    Set<int> variablesWrittenOnLhs = _writeStackForAnd.removeLast();
     _writeStackForAnd.last.addAll(variablesWrittenOnLhs);
     if (!isAnd) return;
-    AssignedVariablesNodeInfo<Variable> info =
+    AssignedVariablesNodeInfo info =
         _assignedVariables._getInfoForNode(wholeExpression);
-    Map<Variable, Type> lhsShownTypes =
+    Map<int, Type> lhsShownTypes =
         _getExpressionInfo(leftOperand)?._shownTypes ?? {};
-    _contextStack.add(new _LegacyBinaryAndContext<Variable, Type>(
-        _knownTypes, lhsShownTypes, info));
-    Map<Variable, Type>? newKnownTypes;
-    for (MapEntry<Variable, Type> entry in lhsShownTypes.entries) {
+    _contextStack.add(
+        new _LegacyBinaryAndContext<Type>(_knownTypes, lhsShownTypes, info));
+    Map<int, Type>? newKnownTypes;
+    for (MapEntry<int, Type> entry in lhsShownTypes.entries) {
       // Given a statement of the form `e1 && e2`, if e1 shows that a
       // local variable v has type T, then the type of v is known to be T in
       // e2, unless any of the following are true:
@@ -4770,7 +4741,7 @@
           _assignedVariables._anywhere._written.contains(entry.key)) {
         continue;
       }
-      (newKnownTypes ??= new Map<Variable, Type>.of(_knownTypes))[entry.key] =
+      (newKnownTypes ??= new Map<int, Type>.of(_knownTypes))[entry.key] =
           entry.value;
     }
     if (newKnownTypes != null) _knownTypes = newKnownTypes;
@@ -4799,7 +4770,8 @@
 
   @override
   Type? promotedType(Variable variable) {
-    return _knownTypes[variable];
+    int variableKey = _promotionKeyStore.keyForVariable(variable);
+    return _knownTypes[variableKey];
   }
 
   @override
@@ -4807,7 +4779,7 @@
       String propertyName, Object? propertyMember, Type staticType) {}
 
   @override
-  SsaNode<Variable, Type>? ssaNodeForTesting(Variable variable) {
+  SsaNode<Type>? ssaNodeForTesting(Variable variable) {
     throw new StateError('ssaNodeForTesting requires null-aware flow analysis');
   }
 
@@ -4854,9 +4826,10 @@
 
   @override
   Type? variableRead(Expression expression, Variable variable) {
-    _storeExpressionInfo(
-        expression, new _LegacyVariableReadInfo<Variable, Type>(variable));
-    return _knownTypes[variable];
+    int variableKey = _promotionKeyStore.keyForVariable(variable);
+    _storeExpressionInfo(expression,
+        new _LegacyVariableReadInfo<Variable, Type>(variable, variableKey));
+    return _knownTypes[variableKey];
   }
 
   @override
@@ -4883,19 +4856,17 @@
   @override
   void write(Node node, Variable variable, Type writtenType,
       Expression? writtenExpression) {
-    _writeStackForAnd.last.add(variable);
+    int variableKey = _promotionKeyStore.keyForVariable(variable);
+    _writeStackForAnd.last.add(variableKey);
   }
 
   void _conditionalOrIf_thenBegin(Expression condition, Node node) {
-    _contextStack.add(new _LegacyContext<Variable, Type>(_knownTypes));
-    AssignedVariablesNodeInfo<Variable> info =
-        _assignedVariables._getInfoForNode(node);
-    Map<Variable, Type>? newKnownTypes;
-    _LegacyExpressionInfo<Variable, Type>? expressionInfo =
-        _getExpressionInfo(condition);
+    _contextStack.add(new _LegacyContext<Type>(_knownTypes));
+    AssignedVariablesNodeInfo info = _assignedVariables._getInfoForNode(node);
+    Map<int, Type>? newKnownTypes;
+    _LegacyExpressionInfo<Type>? expressionInfo = _getExpressionInfo(condition);
     if (expressionInfo != null) {
-      for (MapEntry<Variable, Type> entry
-          in expressionInfo._shownTypes.entries) {
+      for (MapEntry<int, Type> entry in expressionInfo._shownTypes.entries) {
         // Given an expression of the form n1?n2:n3 or a statement of the form
         // `if (n1) n2 else n3`, if n1 shows that a local variable v has type T,
         // then the type of v is known to be T in n2, unless any of the
@@ -4913,7 +4884,7 @@
             _assignedVariables._anywhere._written.contains(entry.key)) {
           continue;
         }
-        (newKnownTypes ??= new Map<Variable, Type>.of(_knownTypes))[entry.key] =
+        (newKnownTypes ??= new Map<int, Type>.of(_knownTypes))[entry.key] =
             entry.value;
       }
       if (newKnownTypes != null) _knownTypes = newKnownTypes;
@@ -4926,21 +4897,20 @@
     print('  expressionWithInfo: $_expressionWithInfo');
     print('  expressionInfo: $_expressionInfo');
     print('  contextStack:');
-    for (_LegacyContext<Variable, Type> stackEntry in _contextStack.reversed) {
+    for (_LegacyContext<Type> stackEntry in _contextStack.reversed) {
       print('    $stackEntry');
     }
     print('  writeStackForAnd:');
-    for (Set<Variable> stackEntry in _writeStackForAnd.reversed) {
+    for (Set<int> stackEntry in _writeStackForAnd.reversed) {
       print('    $stackEntry');
     }
   }
 
   /// Gets the [_LegacyExpressionInfo] associated with [expression], if any;
   /// otherwise returns `null`.
-  _LegacyExpressionInfo<Variable, Type>? _getExpressionInfo(
-      Expression expression) {
+  _LegacyExpressionInfo<Type>? _getExpressionInfo(Expression expression) {
     if (identical(expression, _expressionWithInfo)) {
-      _LegacyExpressionInfo<Variable, Type>? expressionInfo = _expressionInfo;
+      _LegacyExpressionInfo<Type>? expressionInfo = _expressionInfo;
       _expressionInfo = null;
       return expressionInfo;
     } else {
@@ -4950,8 +4920,8 @@
 
   /// Associates [expressionInfo] with [expression] for use by a future call to
   /// [_getExpressionInfo].
-  void _storeExpressionInfo(Expression expression,
-      _LegacyExpressionInfo<Variable, Type> expressionInfo) {
+  void _storeExpressionInfo(
+      Expression expression, _LegacyExpressionInfo<Type> expressionInfo) {
     _expressionWithInfo = expression;
     _expressionInfo = expressionInfo;
   }
@@ -4960,22 +4930,25 @@
 /// Data tracked by legacy type promotion about an expression that reads the
 /// value of a local variable.
 class _LegacyVariableReadInfo<Variable, Type>
-    implements _LegacyExpressionInfo<Variable, Type> {
+    implements _LegacyExpressionInfo<Type> {
   /// The variable being referred to.
   final Variable _variable;
 
-  _LegacyVariableReadInfo(this._variable);
+  /// The variable's corresponding key, as assigned by [PromotionKeyStore].
+  final int _variableKey;
+
+  _LegacyVariableReadInfo(this._variable, this._variableKey);
 
   @override
-  Map<Variable, Type> get _shownTypes => {};
+  Map<int, Type> get _shownTypes => {};
 
   @override
   String toString() => 'LegacyVariableReadInfo($_variable, $_shownTypes)';
 }
 
 /// [_FlowContext] representing a null aware access (`?.`).
-class _NullAwareAccessContext<Variable extends Object, Type extends Object>
-    extends _SimpleContext<Variable, Type> {
+class _NullAwareAccessContext<Type extends Object>
+    extends _SimpleContext<Type> {
   _NullAwareAccessContext(super.previous);
 
   @override
@@ -4983,21 +4956,20 @@
 }
 
 /// [ExpressionInfo] representing a `null` literal.
-class _NullInfo<Variable extends Object, Type extends Object>
-    implements ExpressionInfo<Variable, Type> {
+class _NullInfo<Type extends Object> implements ExpressionInfo<Type> {
   @override
-  final FlowModel<Variable, Type> after;
+  final FlowModel<Type> after;
 
   _NullInfo(this.after);
 
   @override
-  FlowModel<Variable, Type> get ifFalse => after;
+  FlowModel<Type> get ifFalse => after;
 
   @override
-  FlowModel<Variable, Type> get ifTrue => after;
+  FlowModel<Type> get ifTrue => after;
 
   @override
-  ExpressionInfo<Variable, Type> invert() {
+  ExpressionInfo<Type> invert() {
     // This should only happen if `!null` is encountered.  That should never
     // happen for a properly typed program, but we need to handle it so we can
     // give reasonable errors for an improperly typed program.
@@ -5005,20 +4977,13 @@
   }
 
   @override
-  ExpressionInfo<Variable, Type>? rebaseForward(
-          TypeOperations<Type> typeOperations,
-          FlowModel<Variable, Type> base) =>
+  ExpressionInfo<Type>? rebaseForward(
+          TypeOperations<Type> typeOperations, FlowModel<Type> base) =>
       null;
 }
 
 /// [Reference] object representing a property get applied to another reference.
-class _PropertyGetReference<Variable extends Object, Type extends Object>
-    extends Reference<Variable, Type> {
-  /// The target of the property get.  For example a property get of the form
-  /// `a.b`, where `a` is a local variable, has a target which is a reference to
-  /// `a`.
-  final Reference<Variable, Type> target;
-
+class _PropertyGetReference<Type extends Object> extends Reference<Type> {
   /// The name of the property.
   final String propertyName;
 
@@ -5027,11 +4992,12 @@
   /// [FlowAnalysis.thisOrSuperPropertyGet].
   final Object? propertyMember;
 
-  _PropertyGetReference(this.target, this.propertyName, this.propertyMember);
+  _PropertyGetReference(
+      this.propertyName, this.propertyMember, super.promotionKey);
 
   @override
   Map<Type, NonPromotionReason> Function() getNonPromotionReasons(
-      Map<Variable?, VariableModel<Variable, Type>> variableInfo,
+      Map<int, VariableModel<Type>> variableInfo,
       Type staticType,
       TypeOperations<Type> typeOperations) {
     List<Type>? promotedTypes = _getInfo(variableInfo)?.promotedTypes;
@@ -5049,39 +5015,20 @@
   }
 
   @override
-  void storeInfo(Map<Variable?, VariableModel<Variable, Type>> variableInfo,
-      VariableModel<Variable, Type> variableModel) {
-    VariableModel<Variable, Type> targetInfo = target.getInfo(variableInfo);
-    Map<String, VariableModel<Variable, Type>> newProperties =
-        new Map<String, VariableModel<Variable, Type>>.of(
-            targetInfo.properties);
-    newProperties[propertyName] = variableModel;
-    target.storeInfo(variableInfo, targetInfo.setProperties(newProperties));
-  }
-
-  @override
   String toString() =>
-      '_PropertyGetReference($target, $propertyName, $propertyMember)';
-
-  @override
-  VariableModel<Variable, Type>? _getInfo(
-      Map<Variable?, VariableModel<Variable, Type>> variableInfo) {
-    VariableModel<Variable, Type> targetInfo = target.getInfo(variableInfo);
-    return targetInfo.properties[propertyName];
-  }
+      '_PropertyGetReference($propertyName, $propertyMember, $promotionKey)';
 }
 
 /// [_FlowContext] representing a language construct for which flow analysis
 /// must store a flow model state to be retrieved later, such as a `try`
 /// statement, function expression, or "if-null" (`??`) expression.
-abstract class _SimpleContext<Variable extends Object, Type extends Object>
-    extends _FlowContext {
+abstract class _SimpleContext<Type extends Object> extends _FlowContext {
   /// The stored state.  For a `try` statement, this is the state from the
   /// beginning of the `try` block.  For a function expression, this is the
   /// state at the point the function expression was created.  For an "if-null"
   /// expression, this is the state after execution of the expression before the
   /// `??`.
-  final FlowModel<Variable, Type> _previous;
+  final FlowModel<Type> _previous;
 
   _SimpleContext(this._previous);
 }
@@ -5090,12 +5037,12 @@
 /// `break` or `continue` statements, and for which flow analysis must store a
 /// flow model state to be retrieved later.  Examples include "for each" and
 /// `switch` statements.
-class _SimpleStatementContext<Variable extends Object, Type extends Object>
-    extends _BranchTargetContext<Variable, Type> {
+class _SimpleStatementContext<Type extends Object>
+    extends _BranchTargetContext<Type> {
   /// The stored state.  For a "for each" statement, this is the state after
   /// evaluation of the iterable.  For a `switch` statement, this is the state
   /// after evaluation of the switch expression.
-  final FlowModel<Variable, Type> _previous;
+  final FlowModel<Type> _previous;
 
   _SimpleStatementContext(super.checkpoint, this._previous);
 
@@ -5106,11 +5053,12 @@
 }
 
 /// [Reference] object representing an implicit or explicit reference to `this`.
-class _ThisReference<Variable extends Object, Type extends Object>
-    extends Reference<Variable, Type> {
+class _ThisReference<Type extends Object> extends Reference<Type> {
+  _ThisReference(super.promotionKey);
+
   @override
   Map<Type, NonPromotionReason> Function() getNonPromotionReasons(
-      Map<Variable?, VariableModel<Variable, Type>> variableInfo,
+      Map<int, VariableModel<Type>> variableInfo,
       Type staticType,
       TypeOperations<Type> typeOperations) {
     List<Type>? promotedTypes = _getInfo(variableInfo)?.promotedTypes;
@@ -5125,58 +5073,45 @@
     }
     return () => {};
   }
-
-  @override
-  void storeInfo(Map<Variable?, VariableModel<Variable, Type>> variableInfo,
-      VariableModel<Variable, Type> variableModel) {
-    variableInfo[null] = variableModel;
-  }
-
-  @override
-  VariableModel<Variable, Type>? _getInfo(
-          Map<Variable?, VariableModel<Variable, Type>> variableInfo) =>
-      variableInfo[null];
 }
 
 /// Specialization of [ExpressionInfo] for the case where the information we
 /// have about the expression is trivial (meaning we know by construction that
 /// the expression's [after], [ifTrue], and [ifFalse] models are all the same).
-class _TrivialExpressionInfo<Variable extends Object, Type extends Object>
-    implements ExpressionInfo<Variable, Type> {
+class _TrivialExpressionInfo<Type extends Object>
+    implements ExpressionInfo<Type> {
   @override
-  final FlowModel<Variable, Type> after;
+  final FlowModel<Type> after;
 
   _TrivialExpressionInfo(this.after);
 
   @override
-  FlowModel<Variable, Type> get ifFalse => after;
+  FlowModel<Type> get ifFalse => after;
 
   @override
-  FlowModel<Variable, Type> get ifTrue => after;
+  FlowModel<Type> get ifTrue => after;
 
   @override
-  ExpressionInfo<Variable, Type> invert() => this;
+  ExpressionInfo<Type> invert() => this;
 
   @override
-  ExpressionInfo<Variable, Type> rebaseForward(
-          TypeOperations<Type> typeOperations,
-          FlowModel<Variable, Type> base) =>
+  ExpressionInfo<Type> rebaseForward(
+          TypeOperations<Type> typeOperations, FlowModel<Type> base) =>
       new _TrivialExpressionInfo(base);
 }
 
 /// [_FlowContext] representing a try statement.
-class _TryContext<Variable extends Object, Type extends Object>
-    extends _SimpleContext<Variable, Type> {
+class _TryContext<Type extends Object> extends _SimpleContext<Type> {
   /// If the statement is a "try/catch" statement, the flow model representing
   /// program state at the top of any `catch` block.
-  FlowModel<Variable, Type>? _beforeCatch;
+  FlowModel<Type>? _beforeCatch;
 
   /// If the statement is a "try/catch" statement, the accumulated flow model
   /// representing program state after the `try` block or one of the `catch`
   /// blocks has finished executing.  If the statement is a "try/finally"
   /// statement, the flow model representing program state after the `try` block
   /// has finished executing.
-  FlowModel<Variable, Type>? _afterBodyAndCatches;
+  FlowModel<Type>? _afterBodyAndCatches;
 
   _TryContext(super.previous);
 
@@ -5186,21 +5121,19 @@
       'afterBodyAndCatches: $_afterBodyAndCatches)';
 }
 
-class _TryFinallyContext<Variable extends Object, Type extends Object>
-    extends _TryContext<Variable, Type> {
+class _TryFinallyContext<Type extends Object> extends _TryContext<Type> {
   /// The flow model representing program state at the top of the `finally`
   /// block.
-  late final FlowModel<Variable, Type> _beforeFinally;
+  late final FlowModel<Type> _beforeFinally;
 
   _TryFinallyContext(super.previous);
 }
 
 /// [_FlowContext] representing a `while` loop (or a C-style `for` loop, which
 /// is functionally similar).
-class _WhileContext<Variable extends Object, Type extends Object>
-    extends _BranchTargetContext<Variable, Type> {
+class _WhileContext<Type extends Object> extends _BranchTargetContext<Type> {
   /// Flow models associated with the loop condition.
-  final ExpressionInfo<Variable, Type> _conditionInfo;
+  final ExpressionInfo<Type> _conditionInfo;
 
   _WhileContext(super.checkpoint, this._conditionInfo);
 
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables_test.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables_test.dart
index 8676f23..f3ab112 100644
--- a/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables_test.dart
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables_test.dart
@@ -21,7 +21,8 @@
         isClosureOrLateVariableInitializer: true);
     assignedVariables.read(v3);
     assignedVariables.finish();
-    expect(assignedVariables.readCapturedAnywhere, {v2});
+    expect(assignedVariables.readCapturedAnywhere,
+        {assignedVariables.keyForVariable(v2)});
   });
 
   test('readCapturedAnywhere does not record variables local to a closure', () {
@@ -36,7 +37,8 @@
     assignedVariables.endNode(_Node(),
         isClosureOrLateVariableInitializer: true);
     assignedVariables.finish();
-    expect(assignedVariables.readCapturedAnywhere, {v1});
+    expect(assignedVariables.readCapturedAnywhere,
+        {assignedVariables.keyForVariable(v1)});
   });
 
   test('capturedAnywhere records assignments in closures', () {
@@ -54,7 +56,8 @@
         isClosureOrLateVariableInitializer: true);
     assignedVariables.write(v3);
     assignedVariables.finish();
-    expect(assignedVariables.capturedAnywhere, {v2});
+    expect(assignedVariables.capturedAnywhere,
+        {assignedVariables.keyForVariable(v2)});
   });
 
   test('capturedAnywhere does not record variables local to a closure', () {
@@ -69,7 +72,8 @@
     assignedVariables.endNode(_Node(),
         isClosureOrLateVariableInitializer: true);
     assignedVariables.finish();
-    expect(assignedVariables.capturedAnywhere, {v1});
+    expect(assignedVariables.capturedAnywhere,
+        {assignedVariables.keyForVariable(v1)});
   });
 
   test('readAnywhere records all reads', () {
@@ -87,7 +91,11 @@
         isClosureOrLateVariableInitializer: true);
     assignedVariables.read(v3);
     assignedVariables.finish();
-    expect(assignedVariables.readAnywhere, {v1, v2, v3});
+    expect(assignedVariables.readAnywhere, {
+      assignedVariables.keyForVariable(v1),
+      assignedVariables.keyForVariable(v2),
+      assignedVariables.keyForVariable(v3)
+    });
   });
 
   test('writtenAnywhere records all assignments', () {
@@ -105,7 +113,11 @@
         isClosureOrLateVariableInitializer: true);
     assignedVariables.write(v3);
     assignedVariables.finish();
-    expect(assignedVariables.writtenAnywhere, {v1, v2, v3});
+    expect(assignedVariables.writtenAnywhere, {
+      assignedVariables.keyForVariable(v1),
+      assignedVariables.keyForVariable(v2),
+      assignedVariables.keyForVariable(v3)
+    });
   });
 
   test('readInNode ignores reads outside the node', () {
@@ -132,7 +144,8 @@
     var node = _Node();
     assignedVariables.endNode(node);
     assignedVariables.finish();
-    expect(assignedVariables.readInNode(node), {v1});
+    expect(assignedVariables.readInNode(node),
+        {assignedVariables.keyForVariable(v1)});
   });
 
   test('readInNode records reads in a nested node', () {
@@ -146,7 +159,8 @@
     var node = _Node();
     assignedVariables.endNode(node);
     assignedVariables.finish();
-    expect(assignedVariables.readInNode(node), {v1});
+    expect(assignedVariables.readInNode(node),
+        {assignedVariables.keyForVariable(v1)});
   });
 
   test('readInNode records reads in a closure', () {
@@ -158,7 +172,8 @@
     var node = _Node();
     assignedVariables.endNode(node, isClosureOrLateVariableInitializer: true);
     assignedVariables.finish();
-    expect(assignedVariables.readInNode(node), {v1});
+    expect(assignedVariables.readInNode(node),
+        {assignedVariables.keyForVariable(v1)});
   });
 
   test('writtenInNode ignores assignments outside the node', () {
@@ -185,7 +200,8 @@
     var node = _Node();
     assignedVariables.endNode(node);
     assignedVariables.finish();
-    expect(assignedVariables.writtenInNode(node), {v1});
+    expect(assignedVariables.writtenInNode(node),
+        {assignedVariables.keyForVariable(v1)});
   });
 
   test('writtenInNode records assignments in a nested node', () {
@@ -199,7 +215,8 @@
     var node = _Node();
     assignedVariables.endNode(node);
     assignedVariables.finish();
-    expect(assignedVariables.writtenInNode(node), {v1});
+    expect(assignedVariables.writtenInNode(node),
+        {assignedVariables.keyForVariable(v1)});
   });
 
   test('writtenInNode records assignments in a closure', () {
@@ -211,7 +228,8 @@
     var node = _Node();
     assignedVariables.endNode(node, isClosureOrLateVariableInitializer: true);
     assignedVariables.finish();
-    expect(assignedVariables.writtenInNode(node), {v1});
+    expect(assignedVariables.writtenInNode(node),
+        {assignedVariables.keyForVariable(v1)});
   });
 
   test('readCapturedInNode ignores reads in non-nested closures', () {
@@ -250,8 +268,10 @@
     var outerNode = _Node();
     assignedVariables.endNode(outerNode);
     assignedVariables.finish();
-    expect(assignedVariables.readCapturedInNode(innerNode), {v1});
-    expect(assignedVariables.readCapturedInNode(outerNode), {v1});
+    expect(assignedVariables.readCapturedInNode(innerNode),
+        {assignedVariables.keyForVariable(v1)});
+    expect(assignedVariables.readCapturedInNode(outerNode),
+        {assignedVariables.keyForVariable(v1)});
   });
 
   test('capturedInNode ignores assignments in non-nested closures', () {
@@ -290,8 +310,10 @@
     var outerNode = _Node();
     assignedVariables.endNode(outerNode);
     assignedVariables.finish();
-    expect(assignedVariables.capturedInNode(innerNode), {v1});
-    expect(assignedVariables.capturedInNode(outerNode), {v1});
+    expect(assignedVariables.capturedInNode(innerNode),
+        {assignedVariables.keyForVariable(v1)});
+    expect(assignedVariables.capturedInNode(outerNode),
+        {assignedVariables.keyForVariable(v1)});
   });
 
   group('Variables do not percolate beyond the scope they were declared in',
@@ -371,7 +393,12 @@
     assignedVariables.discardNode();
     assignedVariables.endNode(node);
     assignedVariables.finish();
-    expect(assignedVariables.declaredInNode(node), unorderedEquals([v1, v2]));
+    expect(
+        assignedVariables.declaredInNode(node),
+        unorderedEquals([
+          assignedVariables.keyForVariable(v1),
+          assignedVariables.keyForVariable(v2)
+        ]));
   });
 
   test('discardNode percolates reads to enclosing node', () {
@@ -388,7 +415,12 @@
     assignedVariables.discardNode();
     assignedVariables.endNode(node);
     assignedVariables.finish();
-    expect(assignedVariables.readInNode(node), unorderedEquals([v1, v2]));
+    expect(
+        assignedVariables.readInNode(node),
+        unorderedEquals([
+          assignedVariables.keyForVariable(v1),
+          assignedVariables.keyForVariable(v2)
+        ]));
   });
 
   test('discardNode percolates writes to enclosing node', () {
@@ -405,7 +437,12 @@
     assignedVariables.discardNode();
     assignedVariables.endNode(node);
     assignedVariables.finish();
-    expect(assignedVariables.writtenInNode(node), unorderedEquals([v1, v2]));
+    expect(
+        assignedVariables.writtenInNode(node),
+        unorderedEquals([
+          assignedVariables.keyForVariable(v1),
+          assignedVariables.keyForVariable(v2)
+        ]));
   });
 
   test('discardNode percolates read captures to enclosing node', () {
@@ -426,7 +463,11 @@
     assignedVariables.endNode(node);
     assignedVariables.finish();
     expect(
-        assignedVariables.readCapturedInNode(node), unorderedEquals([v1, v2]));
+        assignedVariables.readCapturedInNode(node),
+        unorderedEquals([
+          assignedVariables.keyForVariable(v1),
+          assignedVariables.keyForVariable(v2)
+        ]));
   });
 
   test('discardNode percolates write captures to enclosing node', () {
@@ -446,7 +487,12 @@
     assignedVariables.discardNode();
     assignedVariables.endNode(node);
     assignedVariables.finish();
-    expect(assignedVariables.capturedInNode(node), unorderedEquals([v1, v2]));
+    expect(
+        assignedVariables.capturedInNode(node),
+        unorderedEquals([
+          assignedVariables.keyForVariable(v1),
+          assignedVariables.keyForVariable(v2)
+        ]));
   });
 
   test('deferNode allows deferring of node info', () {
@@ -472,9 +518,16 @@
     var node = _Node();
     assignedVariables.storeInfo(node, info);
     assignedVariables.finish();
-    expect(assignedVariables.declaredInNode(node), [v3]);
-    expect(assignedVariables.writtenInNode(node), unorderedEquals([v1, v2]));
-    expect(assignedVariables.capturedInNode(node), [v2]);
+    expect(assignedVariables.declaredInNode(node),
+        [assignedVariables.keyForVariable(v3)]);
+    expect(
+        assignedVariables.writtenInNode(node),
+        unorderedEquals([
+          assignedVariables.keyForVariable(v1),
+          assignedVariables.keyForVariable(v2)
+        ]));
+    expect(assignedVariables.capturedInNode(node),
+        [assignedVariables.keyForVariable(v2)]);
   });
 }
 
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart
index a3700cd..d2195c0 100644
--- a/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart
@@ -13,7 +13,7 @@
     test('asExpression_end promotes variables', () {
       var h = Harness();
       var x = Var('x', 'int?');
-      late SsaNode<Var, Type> ssaBeforePromotion;
+      late SsaNode<Type> ssaBeforePromotion;
       h.run([
         declare(x, initialized: true),
         getSsaNodes((nodes) => ssaBeforePromotion = nodes[x]!),
@@ -186,7 +186,7 @@
     test('equalityOp(x != null) promotes true branch', () {
       var h = Harness();
       var x = Var('x', 'int?');
-      late SsaNode<Var, Type> ssaBeforePromotion;
+      late SsaNode<Type> ssaBeforePromotion;
       h.run([
         declare(x, initialized: true),
         getSsaNodes((nodes) => ssaBeforePromotion = nodes[x]!),
@@ -255,7 +255,7 @@
     test('equalityOp(x == null) promotes false branch', () {
       var h = Harness();
       var x = Var('x', 'int?');
-      late SsaNode<Var, Type> ssaBeforePromotion;
+      late SsaNode<Type> ssaBeforePromotion;
       h.run([
         declare(x, initialized: true),
         getSsaNodes((nodes) => ssaBeforePromotion = nodes[x]!),
@@ -289,7 +289,7 @@
     test('equalityOp(null != x) promotes true branch', () {
       var h = Harness();
       var x = Var('x', 'int?');
-      late SsaNode<Var, Type> ssaBeforePromotion;
+      late SsaNode<Type> ssaBeforePromotion;
       h.run([
         declare(x, initialized: true),
         getSsaNodes((nodes) => ssaBeforePromotion = nodes[x]!),
@@ -319,7 +319,7 @@
     test('equalityOp(null == x) promotes false branch', () {
       var h = Harness();
       var x = Var('x', 'int?');
-      late SsaNode<Var, Type> ssaBeforePromotion;
+      late SsaNode<Type> ssaBeforePromotion;
       h.run([
         declare(x, initialized: true),
         getSsaNodes((nodes) => ssaBeforePromotion = nodes[x]!),
@@ -441,7 +441,7 @@
     test('doStatement_bodyBegin() un-promotes', () {
       var h = Harness();
       var x = Var('x', 'int?');
-      late SsaNode<Var, Type> ssaBeforeLoop;
+      late SsaNode<Type> ssaBeforeLoop;
       h.run([
         declare(x, initialized: true),
         x.expr.as_('int').stmt,
@@ -608,7 +608,7 @@
     test('for_conditionBegin() un-promotes', () {
       var h = Harness();
       var x = Var('x', 'int?');
-      late SsaNode<Var, Type> ssaBeforeLoop;
+      late SsaNode<Type> ssaBeforeLoop;
       h.run([
         declare(x, initialized: true),
         x.expr.as_('int').stmt,
@@ -750,8 +750,8 @@
       var h = Harness();
       var x = Var('x', 'int?');
       var y = Var('x', 'int?');
-      late SsaNode<Var, Type> xSsaInsideLoop;
-      late SsaNode<Var, Type> ySsaInsideLoop;
+      late SsaNode<Type> xSsaInsideLoop;
+      late SsaNode<Type> ySsaInsideLoop;
       h.run([
         declare(x, initialized: true),
         declare(y, initialized: true),
@@ -779,8 +779,8 @@
       var h = Harness();
       var x = Var('x', 'int?');
       var y = Var('x', 'int?');
-      late SsaNode<Var, Type> xSsaInsideLoop;
-      late SsaNode<Var, Type> ySsaInsideLoop;
+      late SsaNode<Type> xSsaInsideLoop;
+      late SsaNode<Type> ySsaInsideLoop;
       h.run([
         declare(x, initialized: true),
         declare(y, initialized: true),
@@ -806,7 +806,7 @@
     test('forEach_bodyBegin() un-promotes', () {
       var h = Harness();
       var x = Var('x', 'int?');
-      late SsaNode<Var, Type> ssaBeforeLoop;
+      late SsaNode<Type> ssaBeforeLoop;
       h.run([
         declare(x, initialized: true),
         x.expr.as_('int').stmt,
@@ -966,7 +966,7 @@
       var h = Harness();
       var x = Var('x', 'int?');
       var y = Var('y', 'int?');
-      late SsaNode<Var, Type> ssaBeforeFunction;
+      late SsaNode<Type> ssaBeforeFunction;
       h.run([
         declare(x, initialized: true), declare(y, initialized: true),
         x.expr.as_('int').stmt, y.expr.as_('int').stmt,
@@ -1229,7 +1229,7 @@
       var x = Var('x', 'bool');
       var y = Var('y', 'bool');
       var z = Var('z', 'bool');
-      late SsaNode<Var, Type> xSsaNodeBeforeIf;
+      late SsaNode<Type> xSsaNodeBeforeIf;
       h.run([
         declare(w, initialized: true),
         declare(x, initialized: true),
@@ -1258,7 +1258,7 @@
         'unreachable', () {
       var h = Harness();
       var x = Var('x', 'Object');
-      late SsaNode<Var, Type> xSsaNodeBeforeIf;
+      late SsaNode<Type> xSsaNodeBeforeIf;
       h.run([
         declare(x, initialized: true),
         getSsaNodes((nodes) {
@@ -1279,7 +1279,7 @@
         'unreachable', () {
       var h = Harness();
       var x = Var('x', 'Object');
-      late SsaNode<Var, Type> xSsaNodeBeforeIf;
+      late SsaNode<Type> xSsaNodeBeforeIf;
       h.run([
         declare(x, initialized: true),
         getSsaNodes((nodes) {
@@ -1386,7 +1386,7 @@
       var h = Harness();
       var x = Var('x', 'Object');
       var y = Var('y', 'int?');
-      late ExpressionInfo<Var, Type> writtenValueInfo;
+      late ExpressionInfo<Type> writtenValueInfo;
       h.run([
         declare(y, initialized: true),
         declareInitialized(
@@ -1487,7 +1487,7 @@
         {bool inverted = false}) {
       var h = Harness();
       var x = Var('x', declaredType);
-      late SsaNode<Var, Type> ssaBeforePromotion;
+      late SsaNode<Type> ssaBeforePromotion;
       h.run([
         declare(x, initialized: true),
         getSsaNodes((nodes) => ssaBeforePromotion = nodes[x]!),
@@ -1778,7 +1778,7 @@
     test('nonNullAssert_end(x) promotes', () {
       var h = Harness();
       var x = Var('x', 'int?');
-      late SsaNode<Var, Type> ssaBeforePromotion;
+      late SsaNode<Type> ssaBeforePromotion;
       h.run([
         declare(x, initialized: true),
         getSsaNodes((nodes) => ssaBeforePromotion = nodes[x]!),
@@ -1791,7 +1791,7 @@
     test('nullAwareAccess temporarily promotes', () {
       var h = Harness();
       var x = Var('x', 'int?');
-      late SsaNode<Var, Type> ssaBeforePromotion;
+      late SsaNode<Type> ssaBeforePromotion;
       h.run([
         declare(x, initialized: true),
         getSsaNodes((nodes) => ssaBeforePromotion = nodes[x]!),
@@ -1991,7 +1991,7 @@
     test('switchStatement_beginCase(true) un-promotes', () {
       var h = Harness();
       var x = Var('x', 'int?');
-      late SsaNode<Var, Type> ssaBeforeSwitch;
+      late SsaNode<Type> ssaBeforeSwitch;
       h.run([
         declare(x, initialized: true),
         x.expr.as_('int').stmt,
@@ -2142,7 +2142,7 @@
         () {
       var h = Harness();
       var x = Var('x', 'int?');
-      late SsaNode<Var, Type> ssaAfterTry;
+      late SsaNode<Type> ssaAfterTry;
       h.run([
         declare(x, initialized: true),
         x.expr.as_('int').stmt,
@@ -2277,8 +2277,8 @@
         'body', () {
       var h = Harness();
       var x = Var('x', 'int?');
-      late SsaNode<Var, Type> ssaAtStartOfTry;
-      late SsaNode<Var, Type> ssaAfterTry;
+      late SsaNode<Type> ssaAtStartOfTry;
+      late SsaNode<Type> ssaAfterTry;
       h.run([
         declare(x, initialized: true),
         x.expr.as_('int').stmt,
@@ -2348,8 +2348,8 @@
       var h = Harness();
       var x = Var('x', 'int?');
       var y = Var('y', 'int?');
-      late SsaNode<Var, Type> xSsaAtEndOfFinally;
-      late SsaNode<Var, Type> ySsaAtEndOfFinally;
+      late SsaNode<Type> xSsaAtEndOfFinally;
+      late SsaNode<Type> ySsaAtEndOfFinally;
       h.run([
         declare(x, initialized: true), declare(y, initialized: true),
         try_([
@@ -2387,10 +2387,10 @@
         var h = Harness();
         var x = Var('x', 'int?');
         var y = Var('y', 'int?');
-        late SsaNode<Var, Type> xSsaAtEndOfTry;
-        late SsaNode<Var, Type> ySsaAtEndOfTry;
-        late SsaNode<Var, Type> xSsaAtEndOfFinally;
-        late SsaNode<Var, Type> ySsaAtEndOfFinally;
+        late SsaNode<Type> xSsaAtEndOfTry;
+        late SsaNode<Type> ySsaAtEndOfTry;
+        late SsaNode<Type> xSsaAtEndOfFinally;
+        late SsaNode<Type> ySsaAtEndOfFinally;
         h.run([
           declare(x, initialized: true), declare(y, initialized: true),
           try_([
@@ -2872,7 +2872,7 @@
     test('whileStatement_conditionBegin() un-promotes', () {
       var h = Harness();
       var x = Var('x', 'int?');
-      late SsaNode<Var, Type> ssaBeforeLoop;
+      late SsaNode<Type> ssaBeforeLoop;
       h.run([
         declare(x, initialized: true),
         x.expr.as_('int').stmt,
@@ -2962,8 +2962,8 @@
       var h = Harness();
       var x = Var('x', 'int?');
       var y = Var('x', 'int?');
-      late SsaNode<Var, Type> xSsaInsideLoop;
-      late SsaNode<Var, Type> ySsaInsideLoop;
+      late SsaNode<Type> xSsaInsideLoop;
+      late SsaNode<Type> ySsaInsideLoop;
       h.run([
         declare(x, initialized: true),
         declare(y, initialized: true),
@@ -2991,8 +2991,8 @@
       var h = Harness();
       var x = Var('x', 'int?');
       var y = Var('x', 'int?');
-      late SsaNode<Var, Type> xSsaInsideLoop;
-      late SsaNode<Var, Type> ySsaInsideLoop;
+      late SsaNode<Type> xSsaInsideLoop;
+      late SsaNode<Type> ySsaInsideLoop;
       h.run([
         declare(x, initialized: true),
         declare(y, initialized: true),
@@ -3019,8 +3019,8 @@
       var h = Harness();
       var x = Var('x', 'Object');
       var y = Var('y', 'int?');
-      late SsaNode<Var, Type> ssaBeforeWrite;
-      late ExpressionInfo<Var, Type> writtenValueInfo;
+      late SsaNode<Type> ssaBeforeWrite;
+      late ExpressionInfo<Type> writtenValueInfo;
       h.run([
         declare(x, initialized: true),
         declare(y, initialized: true),
@@ -3045,8 +3045,8 @@
       var h = Harness();
       var x = Var('x', 'Object');
       var y = Var('y', 'int?');
-      late SsaNode<Var, Type> ssaBeforeWrite;
-      late ExpressionInfo<Var, Type> writtenValueInfo;
+      late SsaNode<Type> ssaBeforeWrite;
+      late ExpressionInfo<Type> writtenValueInfo;
       h.run([
         declare(x, initialized: true),
         declare(y, initialized: true),
@@ -3079,8 +3079,8 @@
       var h = Harness();
       var x = Var('x', 'int?');
       var y = Var('y', 'int?');
-      late SsaNode<Var, Type> xSsaBeforeWrite;
-      late SsaNode<Var, Type> ySsa;
+      late SsaNode<Type> xSsaBeforeWrite;
+      late SsaNode<Type> ySsa;
       h.run([
         declare(x, initialized: true),
         declare(y, initialized: true),
@@ -3100,7 +3100,7 @@
       var h = Harness();
       var x = Var('x', 'Object');
       var y = Var('y', 'int?');
-      late SsaNode<Var, Type> ssaBeforeWrite;
+      late SsaNode<Type> ssaBeforeWrite;
       h.run([
         declare(x, initialized: true),
         declare(y, initialized: true),
@@ -3125,7 +3125,7 @@
     test('write() permits expression to be null', () {
       var h = Harness();
       var x = Var('x', 'Object');
-      late SsaNode<Var, Type> ssaBeforeWrite;
+      late SsaNode<Type> ssaBeforeWrite;
       h.run([
         declare(x, initialized: true),
         getSsaNodes((nodes) => ssaBeforeWrite = nodes[x]!),
@@ -3363,15 +3363,14 @@
     var objectQVar = Var('x', 'Object?');
     var nullVar = Var('x', 'Null');
     group('setUnreachable', () {
-      var unreachable =
-          FlowModel<Var, Type>(Reachability.initial.setUnreachable());
-      var reachable = FlowModel<Var, Type>(Reachability.initial);
+      var unreachable = FlowModel<Type>(Reachability.initial.setUnreachable());
+      var reachable = FlowModel<Type>(Reachability.initial);
       test('unchanged', () {
         expect(unreachable.setUnreachable(), same(unreachable));
       });
 
       test('changed', () {
-        void _check(FlowModel<Var, Type> initial) {
+        void _check(FlowModel<Type> initial) {
           var s = initial.setUnreachable();
           expect(s, isNot(same(initial)));
           expect(s.reachable.overallReachable, false);
@@ -3383,33 +3382,33 @@
     });
 
     test('split', () {
-      var s1 = FlowModel<Var, Type>(Reachability.initial);
+      var s1 = FlowModel<Type>(Reachability.initial);
       var s2 = s1.split();
       expect(s2.reachable.parent, same(s1.reachable));
     });
 
     test('unsplit', () {
-      var s1 = FlowModel<Var, Type>(Reachability.initial.split());
+      var s1 = FlowModel<Type>(Reachability.initial.split());
       var s2 = s1.unsplit();
       expect(s2.reachable, same(Reachability.initial));
     });
 
     group('unsplitTo', () {
       test('no change', () {
-        var s1 = FlowModel<Var, Type>(Reachability.initial.split());
+        var s1 = FlowModel<Type>(Reachability.initial.split());
         var result = s1.unsplitTo(s1.reachable.parent!);
         expect(result, same(s1));
       });
 
       test('unsplit once, reachable', () {
-        var s1 = FlowModel<Var, Type>(Reachability.initial.split());
+        var s1 = FlowModel<Type>(Reachability.initial.split());
         var s2 = s1.split();
         var result = s2.unsplitTo(s1.reachable.parent!);
         expect(result.reachable, same(s1.reachable));
       });
 
       test('unsplit once, unreachable', () {
-        var s1 = FlowModel<Var, Type>(Reachability.initial.split());
+        var s1 = FlowModel<Type>(Reachability.initial.split());
         var s2 = s1.split().setUnreachable();
         var result = s2.unsplitTo(s1.reachable.parent!);
         expect(result.reachable.locallyReachable, false);
@@ -3417,7 +3416,7 @@
       });
 
       test('unsplit twice, reachable', () {
-        var s1 = FlowModel<Var, Type>(Reachability.initial.split());
+        var s1 = FlowModel<Type>(Reachability.initial.split());
         var s2 = s1.split();
         var s3 = s2.split();
         var result = s3.unsplitTo(s1.reachable.parent!);
@@ -3425,7 +3424,7 @@
       });
 
       test('unsplit twice, top unreachable', () {
-        var s1 = FlowModel<Var, Type>(Reachability.initial.split());
+        var s1 = FlowModel<Type>(Reachability.initial.split());
         var s2 = s1.split();
         var s3 = s2.split().setUnreachable();
         var result = s3.unsplitTo(s1.reachable.parent!);
@@ -3434,7 +3433,7 @@
       });
 
       test('unsplit twice, previous unreachable', () {
-        var s1 = FlowModel<Var, Type>(Reachability.initial.split());
+        var s1 = FlowModel<Type>(Reachability.initial.split());
         var s2 = s1.split().setUnreachable();
         var s3 = s2.split();
         var result = s3.unsplitTo(s1.reachable.parent!);
@@ -3446,38 +3445,39 @@
     group('tryPromoteForTypeCheck', () {
       test('unpromoted -> unchanged (same)', () {
         var h = Harness();
-        var s1 = FlowModel<Var, Type>(Reachability.initial);
+        var s1 = FlowModel<Type>(Reachability.initial);
         var s2 = s1._tryPromoteForTypeCheck(h, intVar, 'int').ifTrue;
         expect(s2, same(s1));
       });
 
       test('unpromoted -> unchanged (supertype)', () {
         var h = Harness();
-        var s1 = FlowModel<Var, Type>(Reachability.initial);
+        var s1 = FlowModel<Type>(Reachability.initial);
         var s2 = s1._tryPromoteForTypeCheck(h, intVar, 'Object').ifTrue;
         expect(s2, same(s1));
       });
 
       test('unpromoted -> unchanged (unrelated)', () {
         var h = Harness();
-        var s1 = FlowModel<Var, Type>(Reachability.initial);
+        var s1 = FlowModel<Type>(Reachability.initial);
         var s2 = s1._tryPromoteForTypeCheck(h, intVar, 'String').ifTrue;
         expect(s2, same(s1));
       });
 
       test('unpromoted -> subtype', () {
         var h = Harness();
-        var s1 = FlowModel<Var, Type>(Reachability.initial);
+        var s1 = FlowModel<Type>(Reachability.initial);
         var s2 = s1._tryPromoteForTypeCheck(h, intQVar, 'int').ifTrue;
         expect(s2.reachable.overallReachable, true);
         expect(s2.variableInfo, {
-          intQVar: _matchVariableModel(chain: ['int'], ofInterest: ['int'])
+          _promotionKeyStore.keyForVariable(intQVar):
+              _matchVariableModel(chain: ['int'], ofInterest: ['int'])
         });
       });
 
       test('promoted -> unchanged (same)', () {
         var h = Harness();
-        var s1 = FlowModel<Var, Type>(Reachability.initial)
+        var s1 = FlowModel<Type>(Reachability.initial)
             ._tryPromoteForTypeCheck(h, objectQVar, 'int')
             .ifTrue;
         var s2 = s1._tryPromoteForTypeCheck(h, objectQVar, 'int').ifTrue;
@@ -3486,7 +3486,7 @@
 
       test('promoted -> unchanged (supertype)', () {
         var h = Harness();
-        var s1 = FlowModel<Var, Type>(Reachability.initial)
+        var s1 = FlowModel<Type>(Reachability.initial)
             ._tryPromoteForTypeCheck(h, objectQVar, 'int')
             .ifTrue;
         var s2 = s1._tryPromoteForTypeCheck(h, objectQVar, 'Object').ifTrue;
@@ -3495,7 +3495,7 @@
 
       test('promoted -> unchanged (unrelated)', () {
         var h = Harness();
-        var s1 = FlowModel<Var, Type>(Reachability.initial)
+        var s1 = FlowModel<Type>(Reachability.initial)
             ._tryPromoteForTypeCheck(h, objectQVar, 'int')
             .ifTrue;
         var s2 = s1._tryPromoteForTypeCheck(h, objectQVar, 'String').ifTrue;
@@ -3504,13 +3504,13 @@
 
       test('promoted -> subtype', () {
         var h = Harness();
-        var s1 = FlowModel<Var, Type>(Reachability.initial)
+        var s1 = FlowModel<Type>(Reachability.initial)
             ._tryPromoteForTypeCheck(h, objectQVar, 'int?')
             .ifTrue;
         var s2 = s1._tryPromoteForTypeCheck(h, objectQVar, 'int').ifTrue;
         expect(s2.reachable.overallReachable, true);
         expect(s2.variableInfo, {
-          objectQVar: _matchVariableModel(
+          _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
               chain: ['int?', 'int'], ofInterest: ['int?', 'int'])
         });
       });
@@ -3522,21 +3522,21 @@
       test('without declaration', () {
         // This should not happen in valid code, but test that we don't crash.
         var h = Harness();
-        var s = FlowModel<Var, Type>(Reachability.initial).write(
-            null, objectQVar, Type('Object?'), new SsaNode<Var, Type>(null), h);
+        var s = FlowModel<Type>(Reachability.initial)._write(
+            null, objectQVar, Type('Object?'), new SsaNode<Type>(null), h);
         expect(s.variableInfo[objectQVar], isNull);
       });
 
       test('unchanged', () {
         var h = Harness();
-        var s1 = FlowModel<Var, Type>(Reachability.initial)
-            .declare(objectQVar, true);
-        var s2 = s1.write(
-            null, objectQVar, Type('Object?'), new SsaNode<Var, Type>(null), h);
+        var s1 =
+            FlowModel<Type>(Reachability.initial)._declare(objectQVar, true);
+        var s2 = s1._write(
+            null, objectQVar, Type('Object?'), new SsaNode<Type>(null), h);
         expect(s2, isNot(same(s1)));
         expect(s2.reachable, same(s1.reachable));
         expect(
-            s2.infoFor(objectQVar),
+            s2._infoFor(objectQVar),
             _matchVariableModel(
                 chain: null,
                 ofInterest: isEmpty,
@@ -3546,13 +3546,13 @@
 
       test('marks as assigned', () {
         var h = Harness();
-        var s1 = FlowModel<Var, Type>(Reachability.initial)
-            .declare(objectQVar, false);
-        var s2 = s1.write(
-            null, objectQVar, Type('int?'), new SsaNode<Var, Type>(null), h);
+        var s1 =
+            FlowModel<Type>(Reachability.initial)._declare(objectQVar, false);
+        var s2 = s1._write(
+            null, objectQVar, Type('int?'), new SsaNode<Type>(null), h);
         expect(s2.reachable.overallReachable, true);
         expect(
-            s2.infoFor(objectQVar),
+            s2._infoFor(objectQVar),
             _matchVariableModel(
                 chain: null,
                 ofInterest: isEmpty,
@@ -3562,16 +3562,17 @@
 
       test('un-promotes fully', () {
         var h = Harness();
-        var s1 = FlowModel<Var, Type>(Reachability.initial)
-            .declare(objectQVar, true)
+        var s1 = FlowModel<Type>(Reachability.initial)
+            ._declare(objectQVar, true)
             ._tryPromoteForTypeCheck(h, objectQVar, 'int')
             .ifTrue;
-        expect(s1.variableInfo, contains(objectQVar));
-        var s2 = s1.write(_MockNonPromotionReason(), objectQVar, Type('int?'),
-            new SsaNode<Var, Type>(null), h);
+        expect(s1.variableInfo,
+            contains(_promotionKeyStore.keyForVariable(objectQVar)));
+        var s2 = s1._write(_MockNonPromotionReason(), objectQVar, Type('int?'),
+            new SsaNode<Type>(null), h);
         expect(s2.reachable.overallReachable, true);
         expect(s2.variableInfo, {
-          objectQVar: _matchVariableModel(
+          _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
               chain: null,
               ofInterest: isEmpty,
               assigned: true,
@@ -3581,24 +3582,24 @@
 
       test('un-promotes partially, when no exact match', () {
         var h = Harness();
-        var s1 = FlowModel<Var, Type>(Reachability.initial)
-            .declare(objectQVar, true)
+        var s1 = FlowModel<Type>(Reachability.initial)
+            ._declare(objectQVar, true)
             ._tryPromoteForTypeCheck(h, objectQVar, 'num?')
             .ifTrue
             ._tryPromoteForTypeCheck(h, objectQVar, 'int')
             .ifTrue;
         expect(s1.variableInfo, {
-          objectQVar: _matchVariableModel(
+          _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
               chain: ['num?', 'int'],
               ofInterest: ['num?', 'int'],
               assigned: true,
               unassigned: false)
         });
-        var s2 = s1.write(_MockNonPromotionReason(), objectQVar, Type('num'),
-            new SsaNode<Var, Type>(null), h);
+        var s2 = s1._write(_MockNonPromotionReason(), objectQVar, Type('num'),
+            new SsaNode<Type>(null), h);
         expect(s2.reachable.overallReachable, true);
         expect(s2.variableInfo, {
-          objectQVar: _matchVariableModel(
+          _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
               chain: ['num?', 'num'],
               ofInterest: ['num?', 'int'],
               assigned: true,
@@ -3608,8 +3609,8 @@
 
       test('un-promotes partially, when exact match', () {
         var h = Harness();
-        var s1 = FlowModel<Var, Type>(Reachability.initial)
-            .declare(objectQVar, true)
+        var s1 = FlowModel<Type>(Reachability.initial)
+            ._declare(objectQVar, true)
             ._tryPromoteForTypeCheck(h, objectQVar, 'num?')
             .ifTrue
             ._tryPromoteForTypeCheck(h, objectQVar, 'num')
@@ -3617,17 +3618,17 @@
             ._tryPromoteForTypeCheck(h, objectQVar, 'int')
             .ifTrue;
         expect(s1.variableInfo, {
-          objectQVar: _matchVariableModel(
+          _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
               chain: ['num?', 'num', 'int'],
               ofInterest: ['num?', 'num', 'int'],
               assigned: true,
               unassigned: false)
         });
-        var s2 = s1.write(_MockNonPromotionReason(), objectQVar, Type('num'),
-            new SsaNode<Var, Type>(null), h);
+        var s2 = s1._write(_MockNonPromotionReason(), objectQVar, Type('num'),
+            new SsaNode<Type>(null), h);
         expect(s2.reachable.overallReachable, true);
         expect(s2.variableInfo, {
-          objectQVar: _matchVariableModel(
+          _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
               chain: ['num?', 'num'],
               ofInterest: ['num?', 'num', 'int'],
               assigned: true,
@@ -3637,25 +3638,25 @@
 
       test('leaves promoted, when exact match', () {
         var h = Harness();
-        var s1 = FlowModel<Var, Type>(Reachability.initial)
-            .declare(objectQVar, true)
+        var s1 = FlowModel<Type>(Reachability.initial)
+            ._declare(objectQVar, true)
             ._tryPromoteForTypeCheck(h, objectQVar, 'num?')
             .ifTrue
             ._tryPromoteForTypeCheck(h, objectQVar, 'num')
             .ifTrue;
         expect(s1.variableInfo, {
-          objectQVar: _matchVariableModel(
+          _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
               chain: ['num?', 'num'],
               ofInterest: ['num?', 'num'],
               assigned: true,
               unassigned: false)
         });
-        var s2 = s1.write(
-            null, objectQVar, Type('num'), new SsaNode<Var, Type>(null), h);
+        var s2 = s1._write(
+            null, objectQVar, Type('num'), new SsaNode<Type>(null), h);
         expect(s2.reachable.overallReachable, true);
         expect(s2.variableInfo, isNot(same(s1.variableInfo)));
         expect(s2.variableInfo, {
-          objectQVar: _matchVariableModel(
+          _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
               chain: ['num?', 'num'],
               ofInterest: ['num?', 'num'],
               assigned: true,
@@ -3665,25 +3666,25 @@
 
       test('leaves promoted, when writing a subtype', () {
         var h = Harness();
-        var s1 = FlowModel<Var, Type>(Reachability.initial)
-            .declare(objectQVar, true)
+        var s1 = FlowModel<Type>(Reachability.initial)
+            ._declare(objectQVar, true)
             ._tryPromoteForTypeCheck(h, objectQVar, 'num?')
             .ifTrue
             ._tryPromoteForTypeCheck(h, objectQVar, 'num')
             .ifTrue;
         expect(s1.variableInfo, {
-          objectQVar: _matchVariableModel(
+          _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
               chain: ['num?', 'num'],
               ofInterest: ['num?', 'num'],
               assigned: true,
               unassigned: false)
         });
-        var s2 = s1.write(
-            null, objectQVar, Type('int'), new SsaNode<Var, Type>(null), h);
+        var s2 = s1._write(
+            null, objectQVar, Type('int'), new SsaNode<Type>(null), h);
         expect(s2.reachable.overallReachable, true);
         expect(s2.variableInfo, isNot(same(s1.variableInfo)));
         expect(s2.variableInfo, {
-          objectQVar: _matchVariableModel(
+          _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
               chain: ['num?', 'num'],
               ofInterest: ['num?', 'num'],
               assigned: true,
@@ -3696,15 +3697,16 @@
           var h = Harness();
           var x = Var('x', 'int?');
 
-          var s1 = FlowModel<Var, Type>(Reachability.initial).declare(x, true);
+          var s1 = FlowModel<Type>(Reachability.initial)._declare(x, true);
           expect(s1.variableInfo, {
-            x: _matchVariableModel(chain: null),
+            _promotionKeyStore.keyForVariable(x):
+                _matchVariableModel(chain: null),
           });
 
-          var s2 =
-              s1.write(null, x, Type('int'), new SsaNode<Var, Type>(null), h);
+          var s2 = s1._write(null, x, Type('int'), new SsaNode<Type>(null), h);
           expect(s2.variableInfo, {
-            x: _matchVariableModel(chain: ['int']),
+            _promotionKeyStore.keyForVariable(x):
+                _matchVariableModel(chain: ['int']),
           });
         });
 
@@ -3712,40 +3714,42 @@
           var h = Harness();
           var x = Var('x', 'int?');
 
-          var s1 = FlowModel<Var, Type>(Reachability.initial).declare(x, true);
+          var s1 = FlowModel<Type>(Reachability.initial)._declare(x, true);
           expect(s1.variableInfo, {
-            x: _matchVariableModel(chain: null),
+            _promotionKeyStore.keyForVariable(x):
+                _matchVariableModel(chain: null),
           });
 
-          var s2 = s1.conservativeJoin([], [x]);
+          var s2 = s1._conservativeJoin([], [x]);
           expect(s2.variableInfo, {
-            x: _matchVariableModel(chain: null, writeCaptured: true),
+            _promotionKeyStore.keyForVariable(x):
+                _matchVariableModel(chain: null, writeCaptured: true),
           });
 
           // 'x' is write-captured, so not promoted
-          var s3 =
-              s2.write(null, x, Type('int'), new SsaNode<Var, Type>(null), h);
+          var s3 = s2._write(null, x, Type('int'), new SsaNode<Type>(null), h);
           expect(s3.variableInfo, {
-            x: _matchVariableModel(chain: null, writeCaptured: true),
+            _promotionKeyStore.keyForVariable(x):
+                _matchVariableModel(chain: null, writeCaptured: true),
           });
         });
 
         test('when promoted', () {
           var h = Harness();
-          var s1 = FlowModel<Var, Type>(Reachability.initial)
-              .declare(objectQVar, true)
+          var s1 = FlowModel<Type>(Reachability.initial)
+              ._declare(objectQVar, true)
               ._tryPromoteForTypeCheck(h, objectQVar, 'int?')
               .ifTrue;
           expect(s1.variableInfo, {
-            objectQVar: _matchVariableModel(
+            _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
               chain: ['int?'],
               ofInterest: ['int?'],
             ),
           });
-          var s2 = s1.write(
-              null, objectQVar, Type('int'), new SsaNode<Var, Type>(null), h);
+          var s2 = s1._write(
+              null, objectQVar, Type('int'), new SsaNode<Type>(null), h);
           expect(s2.variableInfo, {
-            objectQVar: _matchVariableModel(
+            _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
               chain: ['int?', 'int'],
               ofInterest: ['int?'],
             ),
@@ -3754,20 +3758,20 @@
 
         test('when not promoted', () {
           var h = Harness();
-          var s1 = FlowModel<Var, Type>(Reachability.initial)
-              .declare(objectQVar, true)
+          var s1 = FlowModel<Type>(Reachability.initial)
+              ._declare(objectQVar, true)
               ._tryPromoteForTypeCheck(h, objectQVar, 'int?')
               .ifFalse;
           expect(s1.variableInfo, {
-            objectQVar: _matchVariableModel(
+            _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
               chain: ['Object'],
               ofInterest: ['int?'],
             ),
           });
-          var s2 = s1.write(
-              null, objectQVar, Type('int'), new SsaNode<Var, Type>(null), h);
+          var s2 = s1._write(
+              null, objectQVar, Type('int'), new SsaNode<Type>(null), h);
           expect(s2.variableInfo, {
-            objectQVar: _matchVariableModel(
+            _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
               chain: ['Object', 'int'],
               ofInterest: ['int?'],
             ),
@@ -3777,20 +3781,20 @@
 
       test('Promotes to type of interest when not previously promoted', () {
         var h = Harness();
-        var s1 = FlowModel<Var, Type>(Reachability.initial)
-            .declare(objectQVar, true)
+        var s1 = FlowModel<Type>(Reachability.initial)
+            ._declare(objectQVar, true)
             ._tryPromoteForTypeCheck(h, objectQVar, 'num?')
             .ifFalse;
         expect(s1.variableInfo, {
-          objectQVar: _matchVariableModel(
+          _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
             chain: ['Object'],
             ofInterest: ['num?'],
           ),
         });
-        var s2 = s1.write(_MockNonPromotionReason(), objectQVar, Type('num?'),
-            new SsaNode<Var, Type>(null), h);
+        var s2 = s1._write(_MockNonPromotionReason(), objectQVar, Type('num?'),
+            new SsaNode<Type>(null), h);
         expect(s2.variableInfo, {
-          objectQVar: _matchVariableModel(
+          _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
             chain: ['num?'],
             ofInterest: ['num?'],
           ),
@@ -3799,22 +3803,22 @@
 
       test('Promotes to type of interest when previously promoted', () {
         var h = Harness();
-        var s1 = FlowModel<Var, Type>(Reachability.initial)
-            .declare(objectQVar, true)
+        var s1 = FlowModel<Type>(Reachability.initial)
+            ._declare(objectQVar, true)
             ._tryPromoteForTypeCheck(h, objectQVar, 'num?')
             .ifTrue
             ._tryPromoteForTypeCheck(h, objectQVar, 'int?')
             .ifFalse;
         expect(s1.variableInfo, {
-          objectQVar: _matchVariableModel(
+          _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
             chain: ['num?', 'num'],
             ofInterest: ['num?', 'int?'],
           ),
         });
-        var s2 = s1.write(_MockNonPromotionReason(), objectQVar, Type('int?'),
-            new SsaNode<Var, Type>(null), h);
+        var s2 = s1._write(_MockNonPromotionReason(), objectQVar, Type('int?'),
+            new SsaNode<Type>(null), h);
         expect(s2.variableInfo, {
-          objectQVar: _matchVariableModel(
+          _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
             chain: ['num?', 'int?'],
             ofInterest: ['num?', 'int?'],
           ),
@@ -3868,23 +3872,22 @@
           test('; first', () {
             var x = Var('x', 'Object?');
 
-            var s1 = FlowModel<Var, Type>(Reachability.initial)
-                .declare(x, true)
+            var s1 = FlowModel<Type>(Reachability.initial)
+                ._declare(x, true)
                 ._tryPromoteForTypeCheck(h, x, 'B?')
                 .ifFalse
                 ._tryPromoteForTypeCheck(h, x, 'A?')
                 .ifFalse;
             expect(s1.variableInfo, {
-              x: _matchVariableModel(
+              _promotionKeyStore.keyForVariable(x): _matchVariableModel(
                 chain: ['Object'],
                 ofInterest: ['A?', 'B?'],
               ),
             });
 
-            var s2 =
-                s1.write(null, x, Type('C'), new SsaNode<Var, Type>(null), h);
+            var s2 = s1._write(null, x, Type('C'), new SsaNode<Type>(null), h);
             expect(s2.variableInfo, {
-              x: _matchVariableModel(
+              _promotionKeyStore.keyForVariable(x): _matchVariableModel(
                 chain: ['Object', 'B'],
                 ofInterest: ['A?', 'B?'],
               ),
@@ -3894,23 +3897,22 @@
           test('; second', () {
             var x = Var('x', 'Object?');
 
-            var s1 = FlowModel<Var, Type>(Reachability.initial)
-                .declare(x, true)
+            var s1 = FlowModel<Type>(Reachability.initial)
+                ._declare(x, true)
                 ._tryPromoteForTypeCheck(h, x, 'A?')
                 .ifFalse
                 ._tryPromoteForTypeCheck(h, x, 'B?')
                 .ifFalse;
             expect(s1.variableInfo, {
-              x: _matchVariableModel(
+              _promotionKeyStore.keyForVariable(x): _matchVariableModel(
                 chain: ['Object'],
                 ofInterest: ['A?', 'B?'],
               ),
             });
 
-            var s2 =
-                s1.write(null, x, Type('C'), new SsaNode<Var, Type>(null), h);
+            var s2 = s1._write(null, x, Type('C'), new SsaNode<Type>(null), h);
             expect(s2.variableInfo, {
-              x: _matchVariableModel(
+              _promotionKeyStore.keyForVariable(x): _matchVariableModel(
                 chain: ['Object', 'B'],
                 ofInterest: ['A?', 'B?'],
               ),
@@ -3920,23 +3922,22 @@
           test('; nullable and non-nullable', () {
             var x = Var('x', 'Object?');
 
-            var s1 = FlowModel<Var, Type>(Reachability.initial)
-                .declare(x, true)
+            var s1 = FlowModel<Type>(Reachability.initial)
+                ._declare(x, true)
                 ._tryPromoteForTypeCheck(h, x, 'A')
                 .ifFalse
                 ._tryPromoteForTypeCheck(h, x, 'A?')
                 .ifFalse;
             expect(s1.variableInfo, {
-              x: _matchVariableModel(
+              _promotionKeyStore.keyForVariable(x): _matchVariableModel(
                 chain: ['Object'],
                 ofInterest: ['A', 'A?'],
               ),
             });
 
-            var s2 =
-                s1.write(null, x, Type('B'), new SsaNode<Var, Type>(null), h);
+            var s2 = s1._write(null, x, Type('B'), new SsaNode<Type>(null), h);
             expect(s2.variableInfo, {
-              x: _matchVariableModel(
+              _promotionKeyStore.keyForVariable(x): _matchVariableModel(
                 chain: ['Object', 'A'],
                 ofInterest: ['A', 'A?'],
               ),
@@ -3947,25 +3948,27 @@
         group('; ambiguous', () {
           test('; no promotion', () {
             var h = Harness();
-            var s1 = FlowModel<Var, Type>(Reachability.initial)
-                .declare(objectQVar, true)
+            var s1 = FlowModel<Type>(Reachability.initial)
+                ._declare(objectQVar, true)
                 ._tryPromoteForTypeCheck(h, objectQVar, 'num?')
                 .ifFalse
                 ._tryPromoteForTypeCheck(h, objectQVar, 'num*')
                 .ifFalse;
             expect(s1.variableInfo, {
-              objectQVar: _matchVariableModel(
+              _promotionKeyStore.keyForVariable(objectQVar):
+                  _matchVariableModel(
                 chain: ['Object'],
                 ofInterest: ['num?', 'num*'],
               ),
             });
-            var s2 = s1.write(
-                null, objectQVar, Type('int'), new SsaNode<Var, Type>(null), h);
+            var s2 = s1._write(
+                null, objectQVar, Type('int'), new SsaNode<Type>(null), h);
             // It's ambiguous whether to promote to num? or num*, so we don't
             // promote.
             expect(s2, isNot(same(s1)));
             expect(s2.variableInfo, {
-              objectQVar: _matchVariableModel(
+              _promotionKeyStore.keyForVariable(objectQVar):
+                  _matchVariableModel(
                 chain: ['Object'],
                 ofInterest: ['num?', 'num*'],
               ),
@@ -3975,24 +3978,24 @@
 
         test('exact match', () {
           var h = Harness();
-          var s1 = FlowModel<Var, Type>(Reachability.initial)
-              .declare(objectQVar, true)
+          var s1 = FlowModel<Type>(Reachability.initial)
+              ._declare(objectQVar, true)
               ._tryPromoteForTypeCheck(h, objectQVar, 'num?')
               .ifFalse
               ._tryPromoteForTypeCheck(h, objectQVar, 'num*')
               .ifFalse;
           expect(s1.variableInfo, {
-            objectQVar: _matchVariableModel(
+            _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
               chain: ['Object'],
               ofInterest: ['num?', 'num*'],
             ),
           });
-          var s2 = s1.write(_MockNonPromotionReason(), objectQVar, Type('num?'),
-              new SsaNode<Var, Type>(null), h);
+          var s2 = s1._write(_MockNonPromotionReason(), objectQVar,
+              Type('num?'), new SsaNode<Type>(null), h);
           // It's ambiguous whether to promote to num? or num*, but since the
           // written type is exactly num?, we use that.
           expect(s2.variableInfo, {
-            objectQVar: _matchVariableModel(
+            _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
               chain: ['num?'],
               ofInterest: ['num?', 'num*'],
             ),
@@ -4007,23 +4010,23 @@
 
         var h = Harness();
 
-        var s1 = FlowModel<Var, Type>(Reachability.initial)
-            .declare(x, true)
+        var s1 = FlowModel<Type>(Reachability.initial)
+            ._declare(x, true)
             ._tryPromoteForTypeCheck(h, x, 'num?')
             .ifTrue
             ._tryPromoteForTypeCheck(h, x, 'int?')
             .ifTrue;
         expect(s1.variableInfo, {
-          x: _matchVariableModel(
+          _promotionKeyStore.keyForVariable(x): _matchVariableModel(
             chain: ['num?', 'int?'],
             ofInterest: ['num?', 'int?'],
           ),
         });
 
-        var s2 = s1.write(_MockNonPromotionReason(), x, Type('double'),
-            new SsaNode<Var, Type>(null), h);
+        var s2 = s1._write(_MockNonPromotionReason(), x, Type('double'),
+            new SsaNode<Type>(null), h);
         expect(s2.variableInfo, {
-          x: _matchVariableModel(
+          _promotionKeyStore.keyForVariable(x): _matchVariableModel(
             chain: ['num?', 'num'],
             ofInterest: ['num?', 'int?'],
           ),
@@ -4035,18 +4038,20 @@
       var objectQVar = Var('x', 'Object?');
 
       test('initialized', () {
-        var s = FlowModel<Var, Type>(Reachability.initial)
-            .declare(objectQVar, true);
+        var s =
+            FlowModel<Type>(Reachability.initial)._declare(objectQVar, true);
         expect(s.variableInfo, {
-          objectQVar: _matchVariableModel(assigned: true, unassigned: false),
+          _promotionKeyStore.keyForVariable(objectQVar):
+              _matchVariableModel(assigned: true, unassigned: false),
         });
       });
 
       test('not initialized', () {
-        var s = FlowModel<Var, Type>(Reachability.initial)
-            .declare(objectQVar, false);
+        var s =
+            FlowModel<Type>(Reachability.initial)._declare(objectQVar, false);
         expect(s.variableInfo, {
-          objectQVar: _matchVariableModel(assigned: false, unassigned: true),
+          _promotionKeyStore.keyForVariable(objectQVar):
+              _matchVariableModel(assigned: false, unassigned: true),
         });
       });
     });
@@ -4054,23 +4059,23 @@
     group('markNonNullable', () {
       test('unpromoted -> unchanged', () {
         var h = Harness();
-        var s1 = FlowModel<Var, Type>(Reachability.initial);
+        var s1 = FlowModel<Type>(Reachability.initial);
         var s2 = s1._tryMarkNonNullable(h, intVar).ifTrue;
         expect(s2, same(s1));
       });
 
       test('unpromoted -> promoted', () {
         var h = Harness();
-        var s1 = FlowModel<Var, Type>(Reachability.initial);
+        var s1 = FlowModel<Type>(Reachability.initial);
         var s2 = s1._tryMarkNonNullable(h, intQVar).ifTrue;
         expect(s2.reachable.overallReachable, true);
-        expect(s2.infoFor(intQVar),
+        expect(s2._infoFor(intQVar),
             _matchVariableModel(chain: ['int'], ofInterest: []));
       });
 
       test('promoted -> unchanged', () {
         var h = Harness();
-        var s1 = FlowModel<Var, Type>(Reachability.initial)
+        var s1 = FlowModel<Type>(Reachability.initial)
             ._tryPromoteForTypeCheck(h, objectQVar, 'int')
             .ifTrue;
         var s2 = s1._tryMarkNonNullable(h, objectQVar).ifTrue;
@@ -4079,23 +4084,23 @@
 
       test('promoted -> re-promoted', () {
         var h = Harness();
-        var s1 = FlowModel<Var, Type>(Reachability.initial)
+        var s1 = FlowModel<Type>(Reachability.initial)
             ._tryPromoteForTypeCheck(h, objectQVar, 'int?')
             .ifTrue;
         var s2 = s1._tryMarkNonNullable(h, objectQVar).ifTrue;
         expect(s2.reachable.overallReachable, true);
         expect(s2.variableInfo, {
-          objectQVar:
+          _promotionKeyStore.keyForVariable(objectQVar):
               _matchVariableModel(chain: ['int?', 'int'], ofInterest: ['int?'])
         });
       });
 
       test('promote to Never', () {
         var h = Harness();
-        var s1 = FlowModel<Var, Type>(Reachability.initial);
+        var s1 = FlowModel<Type>(Reachability.initial);
         var s2 = s1._tryMarkNonNullable(h, nullVar).ifTrue;
         expect(s2.reachable.overallReachable, false);
-        expect(s2.infoFor(nullVar),
+        expect(s2._infoFor(nullVar),
             _matchVariableModel(chain: ['Never'], ofInterest: []));
       });
     });
@@ -4103,46 +4108,51 @@
     group('conservativeJoin', () {
       test('unchanged', () {
         var h = Harness();
-        var s1 = FlowModel<Var, Type>(Reachability.initial)
-            .declare(intQVar, true)
+        var s1 = FlowModel<Type>(Reachability.initial)
+            ._declare(intQVar, true)
             ._tryPromoteForTypeCheck(h, objectQVar, 'int')
             .ifTrue;
-        var s2 = s1.conservativeJoin([intQVar], []);
+        var s2 = s1._conservativeJoin([intQVar], []);
         expect(s2, isNot(same(s1)));
         expect(s2.reachable, same(s1.reachable));
         expect(s2.variableInfo, {
-          objectQVar: _matchVariableModel(chain: ['int'], ofInterest: ['int']),
-          intQVar: _matchVariableModel(chain: null, ofInterest: [])
+          _promotionKeyStore.keyForVariable(objectQVar):
+              _matchVariableModel(chain: ['int'], ofInterest: ['int']),
+          _promotionKeyStore.keyForVariable(intQVar):
+              _matchVariableModel(chain: null, ofInterest: [])
         });
       });
 
       test('written', () {
         var h = Harness();
-        var s1 = FlowModel<Var, Type>(Reachability.initial)
+        var s1 = FlowModel<Type>(Reachability.initial)
             ._tryPromoteForTypeCheck(h, objectQVar, 'int')
             .ifTrue
             ._tryPromoteForTypeCheck(h, intQVar, 'int')
             .ifTrue;
-        var s2 = s1.conservativeJoin([intQVar], []);
+        var s2 = s1._conservativeJoin([intQVar], []);
         expect(s2.reachable.overallReachable, true);
         expect(s2.variableInfo, {
-          objectQVar: _matchVariableModel(chain: ['int'], ofInterest: ['int']),
-          intQVar: _matchVariableModel(chain: null, ofInterest: ['int'])
+          _promotionKeyStore.keyForVariable(objectQVar):
+              _matchVariableModel(chain: ['int'], ofInterest: ['int']),
+          _promotionKeyStore.keyForVariable(intQVar):
+              _matchVariableModel(chain: null, ofInterest: ['int'])
         });
       });
 
       test('write captured', () {
         var h = Harness();
-        var s1 = FlowModel<Var, Type>(Reachability.initial)
+        var s1 = FlowModel<Type>(Reachability.initial)
             ._tryPromoteForTypeCheck(h, objectQVar, 'int')
             .ifTrue
             ._tryPromoteForTypeCheck(h, intQVar, 'int')
             .ifTrue;
-        var s2 = s1.conservativeJoin([], [intQVar]);
+        var s2 = s1._conservativeJoin([], [intQVar]);
         expect(s2.reachable.overallReachable, true);
         expect(s2.variableInfo, {
-          objectQVar: _matchVariableModel(chain: ['int'], ofInterest: ['int']),
-          intQVar: _matchVariableModel(
+          _promotionKeyStore.keyForVariable(objectQVar):
+              _matchVariableModel(chain: ['int'], ofInterest: ['int']),
+          _promotionKeyStore.keyForVariable(intQVar): _matchVariableModel(
               chain: null, ofInterest: isEmpty, unassigned: false)
         });
       });
@@ -4151,7 +4161,7 @@
     group('rebaseForward', () {
       test('reachability', () {
         var h = Harness();
-        var reachable = FlowModel<Var, Type>(Reachability.initial);
+        var reachable = FlowModel<Type>(Reachability.initial);
         var unreachable = reachable.setUnreachable();
         expect(reachable.rebaseForward(h, reachable), same(reachable));
         expect(reachable.rebaseForward(h, unreachable), same(unreachable));
@@ -4169,22 +4179,22 @@
         var b = Var('b', 'int');
         var c = Var('c', 'int');
         var d = Var('d', 'int');
-        var s0 = FlowModel<Var, Type>(Reachability.initial)
-            .declare(a, false)
-            .declare(b, false)
-            .declare(c, false)
-            .declare(d, false);
+        var s0 = FlowModel<Type>(Reachability.initial)
+            ._declare(a, false)
+            ._declare(b, false)
+            ._declare(c, false)
+            ._declare(d, false);
         var s1 = s0
-            .write(null, a, Type('int'), new SsaNode<Var, Type>(null), h)
-            .write(null, b, Type('int'), new SsaNode<Var, Type>(null), h);
+            ._write(null, a, Type('int'), new SsaNode<Type>(null), h)
+            ._write(null, b, Type('int'), new SsaNode<Type>(null), h);
         var s2 = s0
-            .write(null, a, Type('int'), new SsaNode<Var, Type>(null), h)
-            .write(null, c, Type('int'), new SsaNode<Var, Type>(null), h);
+            ._write(null, a, Type('int'), new SsaNode<Type>(null), h)
+            ._write(null, c, Type('int'), new SsaNode<Type>(null), h);
         var result = s1.rebaseForward(h, s2);
-        expect(result.infoFor(a).assigned, true);
-        expect(result.infoFor(b).assigned, true);
-        expect(result.infoFor(c).assigned, true);
-        expect(result.infoFor(d).assigned, false);
+        expect(result._infoFor(a).assigned, true);
+        expect(result._infoFor(b).assigned, true);
+        expect(result._infoFor(c).assigned, true);
+        expect(result._infoFor(d).assigned, false);
       });
 
       test('write captured', () {
@@ -4193,29 +4203,29 @@
         var b = Var('b', 'int');
         var c = Var('c', 'int');
         var d = Var('d', 'int');
-        var s0 = FlowModel<Var, Type>(Reachability.initial)
-            .declare(a, false)
-            .declare(b, false)
-            .declare(c, false)
-            .declare(d, false);
+        var s0 = FlowModel<Type>(Reachability.initial)
+            ._declare(a, false)
+            ._declare(b, false)
+            ._declare(c, false)
+            ._declare(d, false);
         // In s1, a and b are write captured.  In s2, a and c are.
-        var s1 = s0.conservativeJoin([a, b], [a, b]);
-        var s2 = s1.conservativeJoin([a, c], [a, c]);
+        var s1 = s0._conservativeJoin([a, b], [a, b]);
+        var s2 = s1._conservativeJoin([a, c], [a, c]);
         var result = s1.rebaseForward(h, s2);
         expect(
-          result.infoFor(a),
+          result._infoFor(a),
           _matchVariableModel(writeCaptured: true, unassigned: false),
         );
         expect(
-          result.infoFor(b),
+          result._infoFor(b),
           _matchVariableModel(writeCaptured: true, unassigned: false),
         );
         expect(
-          result.infoFor(c),
+          result._infoFor(c),
           _matchVariableModel(writeCaptured: true, unassigned: false),
         );
         expect(
-          result.infoFor(d),
+          result._infoFor(d),
           _matchVariableModel(writeCaptured: false, unassigned: true),
         );
       });
@@ -4223,16 +4233,16 @@
       test('write captured and promoted', () {
         var h = Harness();
         var a = Var('a', 'num');
-        var s0 = FlowModel<Var, Type>(Reachability.initial).declare(a, false);
+        var s0 = FlowModel<Type>(Reachability.initial)._declare(a, false);
         // In s1, a is write captured.  In s2 it's promoted.
-        var s1 = s0.conservativeJoin([a], [a]);
+        var s1 = s0._conservativeJoin([a], [a]);
         var s2 = s0._tryPromoteForTypeCheck(h, a, 'int').ifTrue;
         expect(
-          s1.rebaseForward(h, s2).infoFor(a),
+          s1.rebaseForward(h, s2)._infoFor(a),
           _matchVariableModel(writeCaptured: true, chain: isNull),
         );
         expect(
-          s2.rebaseForward(h, s1).infoFor(a),
+          s2.rebaseForward(h, s1)._infoFor(a),
           _matchVariableModel(writeCaptured: true, chain: isNull),
         );
       });
@@ -4242,11 +4252,11 @@
             List<String>? expectedChain) {
           var h = Harness();
           var x = Var('x', 'Object?');
-          var s0 = FlowModel<Var, Type>(Reachability.initial).declare(x, true);
+          var s0 = FlowModel<Type>(Reachability.initial)._declare(x, true);
           var s1 = s0;
           if (unsafe) {
-            s1 = s1.write(
-                null, x, Type('Object?'), new SsaNode<Var, Type>(null), h);
+            s1 =
+                s1._write(null, x, Type('Object?'), new SsaNode<Type>(null), h);
           }
           if (thisType != null) {
             s1 = s1._tryPromoteForTypeCheck(h, x, thisType).ifTrue;
@@ -4256,10 +4266,12 @@
               : s0._tryPromoteForTypeCheck(h, x, otherType).ifTrue;
           var result = s2.rebaseForward(h, s1);
           if (expectedChain == null) {
-            expect(result.variableInfo, contains(x));
-            expect(result.infoFor(x).promotedTypes, isNull);
+            expect(result.variableInfo,
+                contains(_promotionKeyStore.keyForVariable(x)));
+            expect(result._infoFor(x).promotedTypes, isNull);
           } else {
-            expect(result.infoFor(x).promotedTypes!.map((t) => t.type).toList(),
+            expect(
+                result._infoFor(x).promotedTypes!.map((t) => t.type).toList(),
                 expectedChain);
           }
         }
@@ -4300,31 +4312,31 @@
           var h = Harness();
           var x = Var('x', 'Object?');
           var initialModel =
-              FlowModel<Var, Type>(Reachability.initial).declare(x, true);
+              FlowModel<Type>(Reachability.initial)._declare(x, true);
           for (var t in before) {
             initialModel = initialModel._tryPromoteForTypeCheck(h, x, t).ifTrue;
           }
-          _checkChain(initialModel.infoFor(x).promotedTypes, before);
+          _checkChain(initialModel._infoFor(x).promotedTypes, before);
           var tryModel = initialModel;
           for (var t in inTry) {
             tryModel = tryModel._tryPromoteForTypeCheck(h, x, t).ifTrue;
           }
           var expectedTryChain = before.toList()..addAll(inTry);
-          _checkChain(tryModel.infoFor(x).promotedTypes, expectedTryChain);
+          _checkChain(tryModel._infoFor(x).promotedTypes, expectedTryChain);
           var finallyModel = initialModel;
           for (var t in inFinally) {
             finallyModel = finallyModel._tryPromoteForTypeCheck(h, x, t).ifTrue;
           }
           var expectedFinallyChain = before.toList()..addAll(inFinally);
           _checkChain(
-              finallyModel.infoFor(x).promotedTypes, expectedFinallyChain);
+              finallyModel._infoFor(x).promotedTypes, expectedFinallyChain);
           var result = tryModel.rebaseForward(h, finallyModel);
-          _checkChain(result.infoFor(x).promotedTypes, expectedResult);
+          _checkChain(result._infoFor(x).promotedTypes, expectedResult);
           // And verify that the inputs are unchanged.
-          _checkChain(initialModel.infoFor(x).promotedTypes, before);
-          _checkChain(tryModel.infoFor(x).promotedTypes, expectedTryChain);
+          _checkChain(initialModel._infoFor(x).promotedTypes, before);
+          _checkChain(tryModel._infoFor(x).promotedTypes, expectedTryChain);
           _checkChain(
-              finallyModel.infoFor(x).promotedTypes, expectedFinallyChain);
+              finallyModel._infoFor(x).promotedTypes, expectedFinallyChain);
         }
 
         _check(['Object'], ['num', 'int'], ['Iterable', 'List'],
@@ -4351,15 +4363,15 @@
       test('types of interest', () {
         var h = Harness();
         var a = Var('a', 'Object');
-        var s0 = FlowModel<Var, Type>(Reachability.initial).declare(a, false);
+        var s0 = FlowModel<Type>(Reachability.initial)._declare(a, false);
         var s1 = s0._tryPromoteForTypeCheck(h, a, 'int').ifFalse;
         var s2 = s0._tryPromoteForTypeCheck(h, a, 'String').ifFalse;
         expect(
-          s1.rebaseForward(h, s2).infoFor(a),
+          s1.rebaseForward(h, s2)._infoFor(a),
           _matchVariableModel(ofInterest: ['int', 'String']),
         );
         expect(
-          s2.rebaseForward(h, s1).infoFor(a),
+          s2.rebaseForward(h, s1)._infoFor(a),
           _matchVariableModel(ofInterest: ['int', 'String']),
         );
       });
@@ -4367,8 +4379,8 @@
       test('variable present in one state but not the other', () {
         var h = Harness();
         var x = Var('x', 'Object?');
-        var s0 = FlowModel<Var, Type>(Reachability.initial);
-        var s1 = s0.declare(x, true);
+        var s0 = FlowModel<Type>(Reachability.initial);
+        var s1 = s0._declare(x, true);
         expect(s1.rebaseForward(h, s0), same(s0));
         expect(s0.rebaseForward(h, s1), same(s1));
       });
@@ -4536,23 +4548,23 @@
   });
 
   group('join', () {
-    var x = Var('x', 'Object?');
-    var y = Var('y', 'Object?');
-    var z = Var('z', 'Object?');
-    var w = Var('w', 'Object?');
+    var x = _promotionKeyStore.keyForVariable(Var('x', 'Object?'));
+    var y = _promotionKeyStore.keyForVariable(Var('y', 'Object?'));
+    var z = _promotionKeyStore.keyForVariable(Var('z', 'Object?'));
+    var w = _promotionKeyStore.keyForVariable(Var('w', 'Object?'));
     var intType = Type('int');
     var intQType = Type('int?');
     var stringType = Type('String');
-    const emptyMap = const <Var, VariableModel<Var, Type>>{};
+    const emptyMap = const <int, VariableModel<Type>>{};
 
-    VariableModel<Var, Type> model(List<Type>? promotionChain,
+    VariableModel<Type> model(List<Type>? promotionChain,
             {List<Type>? typesOfInterest, bool assigned = false}) =>
-        VariableModel<Var, Type>(
+        VariableModel<Type>(
             promotedTypes: promotionChain,
             tested: typesOfInterest ?? promotionChain ?? [],
             assigned: assigned,
             unassigned: !assigned,
-            ssaNode: new SsaNode<Var, Type>(null));
+            ssaNode: new SsaNode<Type>(null));
 
     group('without input reuse', () {
       test('promoted with unpromoted', () {
@@ -4587,7 +4599,7 @@
           x: model([intType]),
           y: model([stringType])
         };
-        var p2 = <Var, VariableModel<Var, Type>>{};
+        var p2 = <int, VariableModel<Type>>{};
         expect(FlowModel.joinVariableInfo(h, p1, p2, emptyMap), same(emptyMap));
         expect(FlowModel.joinVariableInfo(h, p2, p1, emptyMap), same(emptyMap));
       });
@@ -4724,19 +4736,19 @@
   });
 
   group('merge', () {
-    var x = Var('x', 'Object?');
+    var x = _promotionKeyStore.keyForVariable(Var('x', 'Object?'));
     var intType = Type('int');
     var stringType = Type('String');
-    const emptyMap = const <Var, VariableModel<Var, Type>>{};
+    const emptyMap = const <int, VariableModel<Type>>{};
 
-    VariableModel<Var, Type> varModel(List<Type>? promotionChain,
+    VariableModel<Type> varModel(List<Type>? promotionChain,
             {bool assigned = false}) =>
-        VariableModel<Var, Type>(
+        VariableModel<Type>(
             promotedTypes: promotionChain,
             tested: promotionChain ?? [],
             assigned: assigned,
             unassigned: !assigned,
-            ssaNode: new SsaNode<Var, Type>(null));
+            ssaNode: new SsaNode<Type>(null));
 
     test('first is null', () {
       var h = Harness();
@@ -4817,18 +4829,18 @@
   });
 
   group('inheritTested', () {
-    var x = Var('x', 'Object?');
+    var x = _promotionKeyStore.keyForVariable(Var('x', 'Object?'));
     var intType = Type('int');
     var stringType = Type('String');
-    const emptyMap = const <Var, VariableModel<Var, Type>>{};
+    const emptyMap = const <int, VariableModel<Type>>{};
 
-    VariableModel<Var, Type> model(List<Type> typesOfInterest) =>
-        VariableModel<Var, Type>(
+    VariableModel<Type> model(List<Type> typesOfInterest) =>
+        VariableModel<Type>(
             promotedTypes: null,
             tested: typesOfInterest,
             assigned: true,
             unassigned: false,
-            ssaNode: new SsaNode<Var, Type>(null));
+            ssaNode: new SsaNode<Type>(null));
 
     test('inherits types of interest from other', () {
       var h = Harness();
@@ -5821,6 +5833,8 @@
   });
 }
 
+final _promotionKeyStore = PromotionKeyStore();
+
 /// Returns the appropriate matcher for expecting an assertion error to be
 /// thrown or not, based on whether assertions are enabled.
 Matcher get _asserts {
@@ -5873,7 +5887,7 @@
   Matcher assignedMatcher = wrapMatcher(assigned);
   Matcher unassignedMatcher = wrapMatcher(unassigned);
   Matcher writeCapturedMatcher = wrapMatcher(writeCaptured);
-  return predicate((VariableModel<Var, Type> model) {
+  return predicate((VariableModel<Type> model) {
     if (!chainMatcher.matches(model.promotedTypes, {})) return false;
     if (!ofInterestMatcher.matches(model.tested, {})) return false;
     if (!assignedMatcher.matches(model.assigned, {})) return false;
@@ -5900,18 +5914,50 @@
       fail('Unexpected call to accept');
 }
 
-extension on FlowModel<Var, Type> {
-  ExpressionInfo<Var, Type> _tryMarkNonNullable(Harness h, Var variable) =>
+extension on FlowModel<Type> {
+  FlowModel<Type> _conservativeJoin(
+          Iterable<Var> writtenVariables, Iterable<Var> capturedVariables) =>
+      conservativeJoin([
+        for (Var v in writtenVariables) _promotionKeyStore.keyForVariable(v)
+      ], [
+        for (Var v in capturedVariables) _promotionKeyStore.keyForVariable(v)
+      ]);
+
+  FlowModel<Type> _declare(Var variable, bool initialized) => this.declare(
+      variable, _promotionKeyStore.keyForVariable(variable), initialized);
+
+  VariableModel<Type> _infoFor(Var variable) =>
+      infoFor(_promotionKeyStore.keyForVariable(variable));
+
+  ExpressionInfo<Type> _tryMarkNonNullable(Harness h, Var variable) =>
       tryMarkNonNullable(h, _varRefWithType(variable));
 
-  ExpressionInfo<Var, Type> _tryPromoteForTypeCheck(
+  ExpressionInfo<Type> _tryPromoteForTypeCheck(
           Harness h, Var variable, String type) =>
       tryPromoteForTypeCheck(h, _varRefWithType(variable), Type(type));
 
-  Reference<Var, Type> _varRef(Var variable) =>
-      new VariableReference<Var, Type>(variable);
+  Reference<Type> _varRef(Var variable) => new VariableReference<Var, Type>(
+      variable, _promotionKeyStore.keyForVariable(variable));
 
-  ReferenceWithType<Var, Type> _varRefWithType(Var variable) =>
-      new ReferenceWithType<Var, Type>(_varRef(variable),
-          variableInfo[variable]?.promotedTypes?.last ?? variable.type);
+  ReferenceWithType<Type> _varRefWithType(Var variable) =>
+      new ReferenceWithType<Type>(
+          _varRef(variable),
+          variableInfo[_promotionKeyStore.keyForVariable(variable)]
+                  ?.promotedTypes
+                  ?.last ??
+              variable.type);
+
+  FlowModel<Type> _write(
+          NonPromotionReason? nonPromotionReason,
+          Var variable,
+          Type writtenType,
+          SsaNode<Type> newSsaNode,
+          Operations<Var, Type> operations) =>
+      write(
+          nonPromotionReason,
+          variable,
+          _promotionKeyStore.keyForVariable(variable),
+          writtenType,
+          newSsaNode,
+          operations);
 }
diff --git a/pkg/_fe_analyzer_shared/test/mini_ast.dart b/pkg/_fe_analyzer_shared/test/mini_ast.dart
index 2267aaa..4d4c3ed 100644
--- a/pkg/_fe_analyzer_shared/test/mini_ast.dart
+++ b/pkg/_fe_analyzer_shared/test/mini_ast.dart
@@ -193,8 +193,7 @@
   /// [ExpressionInfo] associated with it.  If the expression has no flow
   /// analysis information associated with it, `null` will be passed to
   /// [callback].
-  Expression getExpressionInfo(
-          void Function(ExpressionInfo<Var, Type>?) callback) =>
+  Expression getExpressionInfo(void Function(ExpressionInfo<Type>?) callback) =>
       new _GetExpressionInfo(this, callback);
 
   /// If `this` is an expression `x`, creates the expression `x ?? other`.
@@ -604,8 +603,7 @@
 
   /// Gets the SSA node associated with [variable] at the current point in
   /// control flow, or `null` if the variable has been write captured.
-  SsaNode<Var, Type>? operator [](Var variable) =>
-      _flow.ssaNodeForTesting(variable);
+  SsaNode<Type>? operator [](Var variable) => _flow.ssaNodeForTesting(variable);
 }
 
 /// Representation of a statement in the pseudo-Dart language used for flow
@@ -1171,7 +1169,7 @@
 class _GetExpressionInfo extends Expression {
   final Expression target;
 
-  final void Function(ExpressionInfo<Var, Type>?) callback;
+  final void Function(ExpressionInfo<Type>?) callback;
 
   _GetExpressionInfo(this.target, this.callback);
 
@@ -1413,7 +1411,7 @@
       flow.logicalBinaryOp_begin();
     }
     var leftType = analyzeExpression(lhs);
-    EqualityInfo<Var, Type>? leftInfo;
+    EqualityInfo<Type>? leftInfo;
     if (isEquals) {
       leftInfo = flow.equalityOperand_end(lhs, leftType);
     } else if (isLogical) {
diff --git a/pkg/analysis_server/benchmark/benchmarks.dart b/pkg/analysis_server/benchmark/benchmarks.dart
index 9dea223..7257714 100644
--- a/pkg/analysis_server/benchmark/benchmarks.dart
+++ b/pkg/analysis_server/benchmark/benchmarks.dart
@@ -33,7 +33,7 @@
   );
   runner.addCommand(ListCommand(benchmarks));
   runner.addCommand(RunCommand(benchmarks));
-  runner.run(args);
+  await runner.run(args);
 }
 
 String get analysisServerSrcPath {
diff --git a/pkg/analysis_server/benchmark/integration/driver.dart b/pkg/analysis_server/benchmark/integration/driver.dart
index b52e80a..5c16d36 100644
--- a/pkg/analysis_server/benchmark/integration/driver.dart
+++ b/pkg/analysis_server/benchmark/integration/driver.dart
@@ -112,7 +112,7 @@
       logger.log(Level.FINE, 'requesting server shutdown');
       // Give the server a short time to comply with the shutdown request; if it
       // doesn't exit, then forcibly terminate it.
-      sendServerShutdown();
+      unawaited(sendServerShutdown());
       await server.exitCode.timeout(timeout, onTimeout: () {
         return server.kill('server failed to exit');
       });
diff --git a/pkg/analysis_server/benchmark/perf/memory_tests.dart b/pkg/analysis_server/benchmark/perf/memory_tests.dart
index 488c61c..6bc4b74 100644
--- a/pkg/analysis_server/benchmark/perf/memory_tests.dart
+++ b/pkg/analysis_server/benchmark/perf/memory_tests.dart
@@ -195,7 +195,7 @@
   @override
   Future<void> shutdown() async {
     _test.tearDown();
-    _logger.shutdown();
+    await _logger.shutdown();
   }
 
   @override
@@ -254,7 +254,7 @@
       total += heapUsage['heapUsage'] + heapUsage['externalUsage'] as int;
     }
 
-    service.dispose();
+    await service.dispose();
 
     return total;
   }
diff --git a/pkg/analysis_server/doc/tutorial/quick_assist.md b/pkg/analysis_server/doc/tutorial/quick_assist.md
new file mode 100644
index 0000000..b42dab9
--- /dev/null
+++ b/pkg/analysis_server/doc/tutorial/quick_assist.md
@@ -0,0 +1,337 @@
+# Writing a quick assist
+
+This document describes what a quick assist is and outlines the basic steps for
+writing a quick assist.
+
+## Overview
+
+A quick assist is an automated code edit that is both local in scope and doesn't
+require any user input. By 'local in scope' we mean that the assist only needs
+information from the local library (the library in which it is invoked) and will
+only make changes to the local library. If a code edit requires user input,
+might depend on knowledge from outside the local library, or could make changes
+outside the local library, then it should probably be implemented using a
+refactoring.
+
+Unlike quick fixes, quick assists are displayed even when there are no
+diagnostics being reported against the code. They are not intended to fix
+problems that are being reported, but are used to perform common and
+straightforward code transformations.
+
+In this document we'll use a simple example of writing an assist to convert a
+decimal representation of an integer into a hexadecimal representation. While
+this wouldn't be a good assist to offer, it's simple enough that the details of
+the implementation won't mask the general process used to implement an assist.
+
+## Design considerations
+
+Because quick assists are computed on demand, they need to be computed quickly.
+(Some clients request quick assists every time the user repositions the cursor.)
+That places a performance requirement on quick assists, one that requires that
+the code to compute a quick assist can't perform any potentially lengthy
+computations such as searching all of the user's code or accessing the network.
+That, in turn, generally means that assists can only support localized changes.
+They can add or remove text in the local library, but generally can't do more
+than that.
+
+Unlike quick fixes, there is no signal to indicate which assists might apply at
+a given location in the code. That means that we have to test every assist to
+see whether it's appropriate, which puts a practical limit on the number of
+assists that we can implement. (Even if each assist only takes 100 milliseconds
+to determine whether it applies, if we have 100 assists it will take 10 seconds
+to return the list of assists to the user, which is too slow.) That means that
+we need to be discerning about which assists are implemented and work to make
+assists return quickly if they are not appropriate.
+
+## Describing the assist
+
+Each assist has an instance of the class `AssistKind` associated with it. The
+existing assists for Dart are defined in the class `DartAssistKind`. An
+assist kind has an identifier, a priority, and a message.
+
+The identifier is used by some LSP-based clients to provide user-defined
+shortcuts. It's a hierarchical dot-separated identifier and should follow the
+pattern seen in the existing assist kinds.
+
+The priority is used to order the list of assists when presented to the user.
+The larger the value the closer to the top of the list it will appear. If you're
+implementing an assist for Dart files, you should use one of the constants
+defined in `DartAssistKindPriority` (typically
+`DartAssistKindPriority.DEFAULT`), or add a new constant if there's a need for
+it.
+
+The message is what will be displayed to the user by the client. This should be
+a sentence fragment (no terminating period) that would be appropriate as a label
+in a menu or on a button. It should describe the change that will be made to the
+user's code.
+
+Create a static field whose value is an `AssistKind` describing the assist
+you're implementing. For our example you might define a new constant in
+`DartAssistKind` like this:
+
+```dart
+static const CONVERT_TO_HEX = AssistKind(
+  'dart.assist.convert.toHex',
+  DartAssistKindPriority.DEFAULT,
+  "Convert to hexadecimal",
+);
+```
+
+### Implementing the assist, part 1
+
+To implement the assist you'll create a subclass of `CorrectionProducer`. Most
+of the existing correction producers are in the directory
+`analysis_server/lib/src/services/correction/dart`, so we'll start by creating
+a file named `convert_to_hex.dart` in that directory that contains the following
+(with the year updated appropriately):
+
+```dart
+// Copyright (c) 2022, 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:analysis_server/src/services/correction/assist.dart';
+import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer_plugin/utilities/assist/assist.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+class ConvertToHex extends CorrectionProducer {
+  @override
+  AssistKind get assistKind => DartAssistKind.CONVERT_TO_HEX;
+
+  @override
+  Future<void> compute(ChangeBuilder builder) async {
+  }
+}
+```
+
+The `compute` method is where the assist will be built. We'll come back to it in
+"Implementing the assist, part 2".
+
+The `assistKind` getter is how you associate the assist kind we created earlier
+with the assist produced by the `compute` method.
+
+There's another getter you might need to override. The message associated with
+the assist kind is actually a template that can be filled in at runtime. The
+placeholders in the message are denoted by integers inside curly braces (such as
+`{0}`). The integers are indexes into a list of replacement values (hence, zero
+based), and the getter `assistArguments` returns the list of replacement values.
+The message we used above doesn't have any placeholders, but if we'd written the
+message as `"Convert to '{0}'"`, then we could return the replacement values by
+implementing:
+
+```dart
+String _hexRepresentation = '';
+
+@override
+List<Object> get assistArguments => [_hexRepresentation];
+```
+
+and assigning the replacement string to the field inside the `compute` method.
+
+If you don't implement this getter, then the inherited getter will return an
+empty list. The number of elements in the list must match the largest index used
+in the message. If it doesn't, an exception will be thrown at runtime.
+
+### Testing the assist
+
+Before we look at implementing the `compute` method, we should probably write
+some tests. Even if you don't normally use a test-driven approach to coding, we
+recommend it in this case because writing the tests can help you think of corner
+cases that the implementation will need to handle. The corresponding tests are
+in the directory `analysis_server/test/src/services/correction/assist`, so we'll
+create a file named `convert_to_hex_test.dart` that contains the following:
+
+```dart
+// Copyright (c) 2022, 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:analysis_server/src/services/correction/assist.dart';
+import 'package:analyzer_plugin/utilities/assist/assist.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'assist_processor.dart';
+
+void main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(ConvertToHexTest);
+  });
+}
+
+@reflectiveTest
+class ConvertToHexTest extends AssistProcessorTest {
+  @override
+  AssistKind get kind => DartAssistKind.CONVERT_TO_HEX;
+}
+```
+
+This getter tells the test framework to expect an assist of the returned kind.
+
+The test can then be written in a method that looks something like this:
+
+```dart
+Future<void> test_positive() async {
+  await resolveTestCode('''
+var c = /*caret*/42;
+''');
+  await assertHasAssist('''
+var c = 0x2a;
+''');
+}
+```
+
+The test framework will look for the marker `/*caret*/`, remember it's offset,
+create a file containing the first piece of code with the marker removed, run
+the assist over the code, use our correction producer to build an edit, apply
+the edit to the file, and textually compare the results with the second piece of
+code.
+
+### Registering the assist
+
+Before we can run the test, we need to register the correction producer so that
+it can be run.
+
+The list of assists is computed by the `AssistProcessor`, which has two static
+lists named `generators` and `multiGenerators` that contain the correction
+producers used to compute the assists.
+
+Actually, the lists contain functions used to create the producers. We do that
+so that producers can't accidentally carry state over from one use to the next.
+These functions are usually a tear-off of the correction producer's constructor.
+
+The last step is to add your correction producer to the list named `generators`.
+We'll talk about the other list in "Multi-assist producers".
+
+At this point you should be able to run the test and see it failing.
+
+### Implementing the assist, part 2
+
+We're now at a point where we can finish the implementation of the assist by
+implementing the `compute` method.
+
+The correction producer has access to most of the information you should need in
+order to write the assist. The change builder passed to `compute` is how you
+construct the edit that will be sent back to the client.
+
+The first step in the implementation of any assist is to find the location in
+the AST where the cursor is positioned and verify that all the conditions on
+which the assist is predicated are valid. For example, for our assist we'll need
+to ensure that the cursor is on an integer literal, that the literal consists of
+decimal digits, and that the value is a valid integer.
+
+Finding the AST node is easy because it's done for you by the assist processor
+before `compute` is invoked. All you have to do is use the getter `node` to find
+the node at the cursor.
+
+```dart
+@override
+Future<void> compute(ChangeBuilder builder) async {
+  var node = this.node;
+}
+```
+
+Then we need to verify that this node is an integer literal:
+
+```dart
+@override
+Future<void> compute(ChangeBuilder builder) async {
+  var node = this.node;
+  if (node is! IntegerLiteral) {
+    return;
+  }
+}
+```
+
+If it isn't, then we'll return. Because we haven't used the builder to create a
+assist, returning now means that no assist from this producer will be sent to
+the client.
+
+We'll also check that the integer has the right form and is valid:
+
+```dart
+@override
+Future<void> compute(ChangeBuilder builder) async {
+  var node = this.node;
+  if (node is! IntegerLiteral) {
+    return;
+  }
+  var value = node.value;
+  if (value == null) {
+    return;
+  }
+  if (node.literal.lexeme.contains(RegExp('[^0-9]'))) {
+    return;
+  }
+}
+```
+
+After all those checks we now know that we have a decimal integer that we can
+convert. Note that we check for a `null` value before checking for non-decimal
+digits because it's a faster check and we want the assist to fail as quickly as
+possible.
+
+We're now ready to create the edit. To do that we're going to use the
+`ChangeBuilder` passed to the `compute` method. In the example below we'll
+introduce a couple of the methods on `ChangeBuilder`, but for more information
+you can read [Creating `SourceChange`s](https://github.com/dart-lang/sdk/blob/main/pkg/analyzer_plugin/doc/tutorial/creating_edits.md).
+
+```dart
+@override
+Future<void> compute(ChangeBuilder builder) async {
+  var node = this.node;
+  if (node is! IntegerLiteral) {
+    return;
+  }
+  var value = node.value;
+  if (value == null) {
+    return;
+  }
+  if (node.literal.lexeme.contains(RegExp('[^0-9]'))) {
+    return;
+  }
+  await builder.addDartFileEdit(file, (builder) {
+    var hexDigits = value.toRadixString(16);
+    builder.addSimpleReplacement(range.node(node), '0x$hexDigits');
+  });
+}
+```
+
+We're using `addDartFileEdit` to create an edit in a `.dart` file. In this case
+the edit is simple: we're just replacing one representation of the integer
+literal with a representation of the same value in a different base. The getter
+`range` returns a `RangeFactory`, a utility class with lots of methods to make
+it easier to create `SourceRange`s.
+
+We're missing several test cases. Minimally we should test that the assist works
+correctly with negative values (it doesn't), and that it will not produce an
+edit if the integer literal isn't valid. We'll leave adding such tests as an
+exercise for the reader.
+
+In this example we're just making a simple replacement, so we're avoiding any
+need to worry about formatting. As a general principle we don't attempt to
+format the code after it's been modified, but we do make an effort to leave the
+code in a reasonably readable state. There's a getter (`eol`) that you can use
+to get the end-of-line marker that should be used in the file, and there's
+another getter (`utils`) that will return an object with several utility methods
+that help with things like getting the right indentation for nested code.
+
+### Multi-assist producers
+
+We skipped over the list named `multiGenerators` earlier, promising that we'd
+return to it later. You'll probably never have a need for it, but in case you do
+this section will hopefully tell you what you need to know.
+
+There's a subclass of `CorrectionProducer` named `MultiCorrectionProducer` and
+this list is how you register one of them. That class exists for rare cases
+where you need to use a single correction producer to produce multiple assists.
+This is generally only needed when you can't know in advance the maximum number
+of assists that might need to be produced. For example, there's a set of assists
+to wrap a Flutter `Widget` in another widget, but the set of widgets that can
+wrap a given widget depends on the widget being wrapped.
+
+If you are able to enumerate the possible assists ahead of time, then you're
+probably better off to create one subclass of `CorrectionProducer` for each of
+the assists.
diff --git a/pkg/analysis_server/lib/src/analysis_server.dart b/pkg/analysis_server/lib/src/analysis_server.dart
index f553073..64db45c 100644
--- a/pkg/analysis_server/lib/src/analysis_server.dart
+++ b/pkg/analysis_server/lib/src/analysis_server.dart
@@ -805,7 +805,7 @@
     _refactoringManager = RefactoringManager(this, refactoringWorkspace);
   }
 
-  Future<void> _scheduleAnalysisImplementedNotification() async {
+  void _scheduleAnalysisImplementedNotification() {
     var files = analysisServices[AnalysisService.IMPLEMENTED];
     if (files != null) {
       scheduleImplementedNotification(this, files);
diff --git a/pkg/analysis_server/lib/src/cider/rename.dart b/pkg/analysis_server/lib/src/cider/rename.dart
index 8d967e5..d1b71bd 100644
--- a/pkg/analysis_server/lib/src/cider/rename.dart
+++ b/pkg/analysis_server/lib/src/cider/rename.dart
@@ -53,7 +53,7 @@
     } else if (element is ConstructorElement) {
       status = validateConstructorName(name);
       _analyzePossibleConflicts(element, status, name);
-    } else if (element is ImportElement) {
+    } else if (element is ImportElement2) {
       status = validateImportPrefixName(name);
     }
 
@@ -156,7 +156,7 @@
           replaceMatches.addMatch(result.path, result.matches.toList());
         }
       }
-    } else if (element is ImportElement) {
+    } else if (element is ImportElement2) {
       var replaceInfo = <ReplaceInfo>[];
       for (var match in matches) {
         for (var ref in match.references) {
@@ -214,29 +214,28 @@
             lineInfo.getLocation(element.setter!.nameOffset),
             element.setter!.nameLength));
       }
-    } else if (element is ImportElement) {
-      var prefix = element.prefix;
+    } else if (element is ImportElement2) {
       var unit =
           (await canRename._fileResolver.resolve2(path: sourcePath)).unit;
-      var index = element.library.imports.indexOf(element);
+      var index = element.library.imports2.indexOf(element);
       var node = unit.directives.whereType<ImportDirective>().elementAt(index);
+      final prefixNode = node.prefix;
       if (newName.isEmpty) {
         // We should not get `prefix == null` because we check in
         // `checkNewName` that the new name is different.
-        if (prefix == null) {
-          return infos;
+        if (prefixNode != null) {
+          var prefixEnd = prefixNode.end;
+          infos.add(ReplaceInfo(newName, lineInfo.getLocation(node.uri.end),
+              prefixEnd - node.uri.end));
         }
-        var prefixEnd = prefix.nameOffset + prefix.nameLength;
-        infos.add(ReplaceInfo(newName, lineInfo.getLocation(node.uri.end),
-            prefixEnd - node.uri.end));
       } else {
-        if (prefix == null) {
+        if (prefixNode == null) {
           var uriEnd = node.uri.end;
           infos.add(
               ReplaceInfo(' as $newName', lineInfo.getLocation(uriEnd), 0));
         } else {
-          var offset = prefix.nameOffset;
-          var length = prefix.nameLength;
+          var offset = prefixNode.offset;
+          var length = prefixNode.length;
           infos.add(ReplaceInfo(newName, lineInfo.getLocation(offset), length));
         }
       }
@@ -387,7 +386,7 @@
     if (element is ConstructorElement) {
       return true;
     }
-    if (element is ImportElement) {
+    if (element is ImportElement2) {
       return true;
     }
     if (element is LabelElement || element is LocalElement) {
diff --git a/pkg/analysis_server/lib/src/computer/computer_call_hierarchy.dart b/pkg/analysis_server/lib/src/computer/computer_call_hierarchy.dart
new file mode 100644
index 0000000..5218d75
--- /dev/null
+++ b/pkg/analysis_server/lib/src/computer/computer_call_hierarchy.dart
@@ -0,0 +1,477 @@
+// Copyright (c) 2022, 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:analysis_server/src/protocol_server.dart' as protocol;
+import 'package:analysis_server/src/search/element_references.dart';
+import 'package:analysis_server/src/services/search/search_engine.dart';
+import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/visitor.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/source/source_range.dart';
+import 'package:analyzer/src/dart/ast/element_locator.dart';
+import 'package:analyzer/src/dart/ast/utilities.dart';
+
+/// A [CallHierarchyItem] and a set of ranges that call to or from it.
+class CallHierarchyCalls {
+  final CallHierarchyItem item;
+  final List<SourceRange> ranges;
+
+  CallHierarchyCalls(this.item, [List<SourceRange>? ranges])
+      : ranges = ranges ?? [];
+}
+
+/// An item that can appear in a Call Hierarchy.
+///
+/// Items are the containers that can contain calls to other items. This
+/// includes not only functions and methods, but classes (because they can call
+/// functions in class field initializers) and files (because they can call
+/// functions in top level variable initializers).
+///
+/// Implicit constructors are represented using the location of their classes
+/// (since do not themselves have a range) but a [kind] of
+/// [CallHierarchyKind.constructor].
+class CallHierarchyItem {
+  /// The kind of this item in the Call Hierarchy.
+  ///
+  /// This may be used to select an icon to show in the Call Hierarchy tree.
+  final CallHierarchyKind kind;
+
+  // The user-visible name for this item in the Call Hierarchy.
+  final String displayName;
+
+  /// The file that contains the declaration of this item.
+  final String file;
+
+  /// The range of the name at the declaration of this item.
+  final SourceRange range;
+
+  // TODO(dantup): This class will need to contain two ranges (codeRange +
+  //   selectionRange) for LSP.
+
+  CallHierarchyItem.forElement(
+    Element element, {
+    required this.range,
+  })  : displayName = element is CompilationUnitElement
+            ? element.source.shortName
+            : element is PropertyAccessorElement
+                ? element.isGetter
+                    ? 'get ${element.displayName}'
+                    : 'set ${element.displayName}'
+                : element.displayName,
+        file = element.source!.fullName,
+        kind = CallHierarchyKind.forElement(element);
+
+  CallHierarchyItem.forProtocolElement(
+    protocol.Element element, {
+    required this.range,
+  })  : displayName = element.name,
+        file = element.location!.file,
+        kind = CallHierarchyKind.forProtocolElement(element);
+}
+
+/// Kinds of [CallHierarchyItem] that can make calls to other
+/// [CallHierarchyItem]s.
+enum CallHierarchyKind {
+  class_,
+  constructor,
+  extension,
+  file,
+  function,
+  method,
+  mixin,
+  property,
+  unknown;
+
+  static const _elementMapping = {
+    ElementKind.CLASS: class_,
+    ElementKind.COMPILATION_UNIT: file,
+    ElementKind.CONSTRUCTOR: constructor,
+    ElementKind.EXTENSION: extension,
+    ElementKind.FUNCTION: function,
+    ElementKind.GETTER: property,
+    ElementKind.METHOD: method,
+    ElementKind.SETTER: property,
+  };
+
+  static const _protocolElementMapping = {
+    protocol.ElementKind.CLASS: class_,
+    protocol.ElementKind.COMPILATION_UNIT: file,
+    protocol.ElementKind.CONSTRUCTOR: constructor,
+    protocol.ElementKind.EXTENSION: extension,
+    protocol.ElementKind.FUNCTION: function,
+    protocol.ElementKind.GETTER: property,
+    protocol.ElementKind.METHOD: method,
+    protocol.ElementKind.MIXIN: mixin,
+    protocol.ElementKind.SETTER: property,
+  };
+
+  static CallHierarchyKind forElement(Element element) =>
+      _elementMapping[element.kind] ?? unknown;
+
+  static CallHierarchyKind forProtocolElement(protocol.Element element) =>
+      _protocolElementMapping[element.kind] ?? unknown;
+}
+
+/// A computer for Call Hierarchies.
+///
+/// Call Hierarchies are computed lazily and roughly follow the LSP model, which
+/// is:
+///
+///   1. Client calls `findTarget(int offset)` to locate a starting point for
+///      navigation. The starting point is always based on the declaration of
+///      the executable code even if invoked at a call site.
+///   2. Client passes the returned target into `findIncomingCalls()` or
+///      `findOutgoingCalls()` to find relevant calls in to/out of that item.
+///      The result will contain a set of other items, along with the ranges
+///      of the calls in or out.
+///
+/// It is expected that clients will call the methods above at different times
+/// (as the user expands items in a tree). It is up to the caller to handle
+/// cases where a target may no longer be valid (due to file modifications) that
+/// may result in inconsistent results.
+class DartCallHierarchyComputer {
+  final ResolvedUnitResult _result;
+
+  DartCallHierarchyComputer(this._result);
+
+  /// Finds incoming calls to [target], returning the elements that call them
+  /// and ranges of those calls within.
+  Future<List<CallHierarchyCalls>> findIncomingCalls(
+    CallHierarchyItem target,
+    SearchEngine searchEngine,
+  ) async {
+    assert(target.file == _result.path);
+    final node = _findTargetNode(target.range.offset);
+    var element = _getElementOfNode(node);
+    if (element == null) {
+      return [];
+    }
+
+    // Implicit constructors are handled using the Class element and a kind of
+    // `constructor`, because we need to locate them using an `offset`, which
+    // implicit constructors do not have.
+    // Here, we map them back to the synthetic constructor element.
+    final isImplicitConstructor =
+        element is ClassElement && target.kind == CallHierarchyKind.constructor;
+    if (isImplicitConstructor) {
+      element = element.unnamedConstructor;
+    }
+
+    // We only find incoming calls to executable elements.
+    if (element is! ExecutableElement) {
+      return [];
+    }
+
+    final computer = ElementReferencesComputer(searchEngine);
+    final references = await computer.compute(element, false);
+
+    // Group results by their container, since we only want to return a single
+    // entry for a body, with a set of ranges within.
+    final resultsByContainer = <protocol.Element, CallHierarchyCalls>{};
+    // We may need to fetch parsed results for the other files, reuse them
+    // across calls.
+    final parsedUnits = <String, SomeParsedUnitResult?>{};
+    for (final reference in references) {
+      final container = _getContainer(reference.path);
+      if (container == null) {
+        continue;
+      }
+
+      // Create an item for a container the first time we see it.
+      final containerCalls = resultsByContainer.putIfAbsent(
+        container,
+        () => CallHierarchyCalls(CallHierarchyItem.forProtocolElement(
+          container,
+          range: _rangeForProtocolElement(container),
+        )),
+      );
+
+      // Add this match to the containers results.
+      final range = _rangeForSearchResult(reference, parsedUnits);
+      containerCalls.ranges.add(range);
+    }
+
+    return resultsByContainer.values.toList();
+  }
+
+  /// Finds outgoing calls from [target], returning the elements that are called
+  /// and the ranges that call them.
+  Future<List<CallHierarchyCalls>> findOutgoingCalls(
+    CallHierarchyItem target,
+  ) async {
+    assert(target.file == _result.path);
+    final node = _findTargetNode(target.range.offset);
+    if (node == null) {
+      return [];
+    }
+
+    // Don't look for outbound calls in things that aren't functions.
+    if (!(node is FunctionDeclaration ||
+        node is ConstructorDeclaration ||
+        node is MethodDeclaration)) {
+      return [];
+    }
+
+    final referenceNodes = <AstNode>{};
+    node.accept(_OutboundCallVisitor(node, referenceNodes.add));
+
+    // Group results by their target, since we only want to return a single
+    // entry for each target, with a set of ranges that call it.
+    final resultsByTarget = <Element, CallHierarchyCalls>{};
+    for (final referenceNode in referenceNodes) {
+      final target = _getElementOfNode(referenceNode);
+      if (target == null) {
+        continue;
+      }
+
+      // Create an item for a target the first time we see it.
+      final calls = resultsByTarget.putIfAbsent(
+        target,
+        () => CallHierarchyCalls(CallHierarchyItem.forElement(
+          target,
+          range: _rangeForElement(target),
+        )),
+      );
+
+      // Add this call to the targets results.
+      final range = _rangeForNode(referenceNode);
+      calls.ranges.add(range);
+    }
+
+    return resultsByTarget.values.toList();
+  }
+
+  /// Finds a target for starting call hierarchy navigation at [offset].
+  ///
+  /// If [offset] is an invocation, returns information about the [Element] it
+  /// refers to.
+  CallHierarchyItem? findTarget(int offset) {
+    final node = _findTargetNode(offset);
+    final element = _getElementOfNode(node);
+
+    // We only return targets that are executable elements.
+    return element is ExecutableElement
+        ? CallHierarchyItem.forElement(element,
+            range: _rangeForElement(element))
+        : null;
+  }
+
+  /// Finds a target node for call hierarchy navigation at [offset].
+  AstNode? _findTargetNode(int offset) {
+    var node = NodeLocator(offset).searchWithin(_result.unit);
+    if (node is SimpleIdentifier &&
+        node.parent != null &&
+        node.parent is! VariableDeclaration &&
+        node.parent is! AssignmentExpression) {
+      node = node.parent;
+    }
+
+    // For consistency with other places, we only treat the type name as the
+    // constructor for unnamed constructor (since we use the constructors name
+    // as the target).
+    if (node is NamedType) {
+      final parent = node.parent;
+      if (parent is ConstructorName) {
+        final name = parent.name;
+        if (name != null && offset < name.offset) {
+          return null;
+        }
+      }
+    } else if (node is ConstructorDeclaration) {
+      final name = node.name;
+      if (name != null && offset < name.offset) {
+        return null;
+      }
+    }
+
+    return node;
+  }
+
+  /// Returns the enclosing element (class, function body, compliation unit) for
+  /// the reference [element].
+  ///
+  /// This is used to construct (and group calls by) a [CallHierarchyItem] that
+  /// contains calls.
+  protocol.Element? _getContainer(List<protocol.Element> elements) {
+    return elements.firstWhere((parent) =>
+        parent.kind == protocol.ElementKind.CLASS ||
+        parent.kind == protocol.ElementKind.COMPILATION_UNIT ||
+        parent.kind == protocol.ElementKind.CONSTRUCTOR ||
+        parent.kind == protocol.ElementKind.ENUM ||
+        parent.kind == protocol.ElementKind.EXTENSION ||
+        parent.kind == protocol.ElementKind.FUNCTION ||
+        parent.kind == protocol.ElementKind.GETTER ||
+        parent.kind == protocol.ElementKind.METHOD ||
+        parent.kind == protocol.ElementKind.MIXIN ||
+        parent.kind == protocol.ElementKind.SETTER);
+  }
+
+  /// Return the [Element] of the given [node], or `null` if [node] is `null`,
+  /// does not have an element, or the element is not a valid target for call
+  /// hierarchy.
+  Element? _getElementOfNode(AstNode? node) {
+    if (node == null) {
+      return null;
+    }
+    final parent = node.parent;
+
+    // ElementLocator returns the class for default constructor calls and null
+    // for constructor names.
+    if (node is NamedType && parent is ConstructorName) {
+      return parent.staticElement;
+    } else if (node is ConstructorName) {
+      return node.staticElement;
+    } else if (node is PropertyAccess) {
+      node = node.propertyName;
+    }
+
+    final element = ElementLocator.locate(node);
+
+    // Don't consider synthetic getter/setter for a field to be executable
+    // since they don't contain any executable code.
+    if (element is PropertyAccessorElement && element.isSynthetic) {
+      return null;
+    }
+
+    return element;
+  }
+
+  /// Returns the [SourceRange] to use for [element].
+  ///
+  /// The returned range covers only the name of the target and does not include
+  /// any parameter list.
+  SourceRange _rangeForElement(Element element) {
+    // For synthetic items (like implicit constructors), use the nonSynthetic
+    // element for the location.
+    element = element.nonSynthetic;
+    return SourceRange(element.nameOffset, element.nameLength);
+  }
+
+  /// Returns the [SourceRange] to use for [node].
+  ///
+  /// The returned range covers only the name of the target and does not include
+  /// the argument list.
+  SourceRange _rangeForNode(AstNode node) {
+    if (node is MethodInvocation) {
+      return SourceRange(
+        node.methodName.offset,
+        node.methodName.length,
+      );
+    } else if (node is InstanceCreationExpression) {
+      return SourceRange(
+        node.constructorName.offset,
+        node.constructorName.length,
+      );
+    } else if (node is PropertyAccess) {
+      return SourceRange(
+        node.propertyName.offset,
+        node.propertyName.length,
+      );
+    }
+    return SourceRange(node.offset, node.length);
+  }
+
+  /// Returns the [SourceRange] to use for [element].
+  ///
+  /// The returned range covers only the name of the target and does not include
+  /// any parameter list.
+  SourceRange _rangeForProtocolElement(protocol.Element element) {
+    return SourceRange(element.location!.offset, element.location!.length);
+  }
+
+  /// Returns the [SourceRange] for [match].
+  ///
+  /// Usually this is the range returned from the search index, but sometimes it
+  /// will be adjusted to reflect the source range that should be highlighted
+  /// for this call. For example, an unnamed constructor may be given a range
+  /// covering the type name (whereas the index has a zero-width range after the
+  /// type name).
+  SourceRange _rangeForSearchResult(
+    protocol.SearchResult match,
+    Map<String, SomeParsedUnitResult?> parsedUnits,
+  ) {
+    var offset = match.location.offset;
+    var length = match.location.length;
+    final file = match.location.file;
+    final result = parsedUnits.putIfAbsent(
+      file,
+      () => _result.session.getParsedUnit(file),
+    );
+
+    if (result is ParsedUnitResult) {
+      final node = NodeLocator(offset).searchWithin(result.unit);
+      if (node != null && !node.isSynthetic) {
+        final parent = node.parent;
+        // Handle named constructors which have their `.` included in the
+        // search index range, but we just want to highlight the name.
+        if (node is SimpleIdentifier && parent is MethodInvocation) {
+          offset = parent.methodName.offset;
+          length = parent.methodName.length;
+        } else if (length == 0) {
+          // If the search index length was 0, prefer the nodes range.
+          offset = node.offset;
+          length = node.length;
+        }
+      }
+    }
+
+    return SourceRange(offset, length);
+  }
+}
+
+/// Collects outbound calls from a node.
+class _OutboundCallVisitor extends RecursiveAstVisitor<void> {
+  final AstNode root;
+  final void Function(AstNode) collect;
+
+  _OutboundCallVisitor(this.root, this.collect);
+
+  @override
+  void visitConstructorName(ConstructorName node) {
+    collect(node.name ?? node);
+    super.visitConstructorName(node);
+  }
+
+  @override
+  void visitFunctionDeclaration(FunctionDeclaration node) {
+    // Only descend into function declarations if they are the target/root
+    // function.
+    if (node == root) {
+      super.visitFunctionDeclaration(node);
+    }
+  }
+
+  @override
+  void visitFunctionReference(FunctionReference node) {
+    collect(node);
+    super.visitFunctionReference(node);
+  }
+
+  @override
+  void visitMethodInvocation(MethodInvocation node) {
+    collect(node.methodName);
+    super.visitMethodInvocation(node);
+  }
+
+  @override
+  void visitPrefixedIdentifier(PrefixedIdentifier node) {
+    collect(node.identifier);
+    super.visitPrefixedIdentifier(node);
+  }
+
+  @override
+  void visitPropertyAccess(PropertyAccess node) {
+    collect(node.propertyName);
+    super.visitPropertyAccess(node);
+  }
+
+  @override
+  void visitSimpleIdentifier(SimpleIdentifier node) {
+    if (node.staticElement is FunctionElement && !node.inDeclarationContext()) {
+      collect(node);
+    }
+    super.visitSimpleIdentifier(node);
+  }
+}
diff --git a/pkg/analysis_server/lib/src/computer/import_elements_computer.dart b/pkg/analysis_server/lib/src/computer/import_elements_computer.dart
index a29230f..e1a152f 100644
--- a/pkg/analysis_server/lib/src/computer/import_elements_computer.dart
+++ b/pkg/analysis_server/lib/src/computer/import_elements_computer.dart
@@ -339,7 +339,7 @@
   /// [importedElements]. They will match if they import the same library using
   /// the same prefix.
   bool _matches(ImportDirective import, ImportedElements importedElements) {
-    var importElement = import.element;
+    var importElement = import.element2;
     if (importElement == null) {
       return false;
     }
diff --git a/pkg/analysis_server/lib/src/domains/completion/available_suggestions.dart b/pkg/analysis_server/lib/src/domains/completion/available_suggestions.dart
index c7ec048..e98d0fa 100644
--- a/pkg/analysis_server/lib/src/domains/completion/available_suggestions.dart
+++ b/pkg/analysis_server/lib/src/domains/completion/available_suggestions.dart
@@ -97,7 +97,7 @@
   var uniqueElements = _UniqueImportedElements();
   var existingImports = <protocol.ExistingImport>[];
 
-  var importElementList = resolvedUnit.libraryElement.imports;
+  var importElementList = resolvedUnit.libraryElement.imports2;
   for (var import in importElementList) {
     var importedLibrary = import.importedLibrary;
     if (importedLibrary == null) continue;
diff --git a/pkg/analysis_server/lib/src/handler/legacy/analysis_reanalyze.dart b/pkg/analysis_server/lib/src/handler/legacy/analysis_reanalyze.dart
index 988d2e9..cbf090a 100644
--- a/pkg/analysis_server/lib/src/handler/legacy/analysis_reanalyze.dart
+++ b/pkg/analysis_server/lib/src/handler/legacy/analysis_reanalyze.dart
@@ -16,14 +16,14 @@
 
   @override
   Future<void> handle() async {
-    server.options.analytics?.sendEvent('analysis', 'reanalyze');
+    unawaited(server.options.analytics?.sendEvent('analysis', 'reanalyze'));
 
     await server.reanalyze();
     //
     // Restart all of the plugins. This is an async operation that will happen
     // in the background.
     //
-    server.pluginManager.restartPlugins();
+    unawaited(server.pluginManager.restartPlugins());
 
     sendResult(AnalysisReanalyzeResult());
   }
diff --git a/pkg/analysis_server/lib/src/handler/legacy/analysis_set_analysis_roots.dart b/pkg/analysis_server/lib/src/handler/legacy/analysis_set_analysis_roots.dart
index 6f2c5e2..d01a068 100644
--- a/pkg/analysis_server/lib/src/handler/legacy/analysis_set_analysis_roots.dart
+++ b/pkg/analysis_server/lib/src/handler/legacy/analysis_set_analysis_roots.dart
@@ -21,8 +21,11 @@
     var includedPathList = params.included;
     var excludedPathList = params.excluded;
 
-    server.options.analytics?.sendEvent('analysis', 'setAnalysisRoots',
-        value: includedPathList.length);
+    unawaited(server.options.analytics?.sendEvent(
+      'analysis',
+      'setAnalysisRoots',
+      value: includedPathList.length,
+    ));
     server.analyticsManager.startedSetAnalysisRoots(params);
 
     // validate
diff --git a/pkg/analysis_server/lib/src/handler/legacy/analytics_send_event.dart b/pkg/analysis_server/lib/src/handler/legacy/analytics_send_event.dart
index afb36ff..2051bd6 100644
--- a/pkg/analysis_server/lib/src/handler/legacy/analytics_send_event.dart
+++ b/pkg/analysis_server/lib/src/handler/legacy/analytics_send_event.dart
@@ -25,7 +25,7 @@
     }
 
     var params = AnalyticsSendEventParams.fromRequest(request);
-    analytics.sendEvent(_clientId, params.action);
+    unawaited(analytics.sendEvent(_clientId, params.action));
     sendResult(AnalyticsSendEventResult());
   }
 }
diff --git a/pkg/analysis_server/lib/src/handler/legacy/analytics_send_timing.dart b/pkg/analysis_server/lib/src/handler/legacy/analytics_send_timing.dart
index bddc7d0..476abfa 100644
--- a/pkg/analysis_server/lib/src/handler/legacy/analytics_send_timing.dart
+++ b/pkg/analysis_server/lib/src/handler/legacy/analytics_send_timing.dart
@@ -25,7 +25,8 @@
     }
 
     var params = AnalyticsSendTimingParams.fromRequest(request);
-    analytics.sendTiming(params.event, params.millis, category: _clientId);
+    unawaited(
+        analytics.sendTiming(params.event, params.millis, category: _clientId));
     sendResult(AnalyticsSendTimingResult());
   }
 }
diff --git a/pkg/analysis_server/lib/src/handler/legacy/completion_get_suggestions.dart b/pkg/analysis_server/lib/src/handler/legacy/completion_get_suggestions.dart
index 62907af..c29308e 100644
--- a/pkg/analysis_server/lib/src/handler/legacy/completion_get_suggestions.dart
+++ b/pkg/analysis_server/lib/src/handler/legacy/completion_get_suggestions.dart
@@ -41,7 +41,7 @@
     }
 
     var performance = OperationPerformanceImpl('<root>');
-    performance.runAsync(
+    await performance.runAsync(
       'request',
       (performance) async {
         if (file.endsWith('.yaml')) {
diff --git a/pkg/analysis_server/lib/src/handler/legacy/completion_get_suggestions2.dart b/pkg/analysis_server/lib/src/handler/legacy/completion_get_suggestions2.dart
index d889bbd..e96ab6a 100644
--- a/pkg/analysis_server/lib/src/handler/legacy/completion_get_suggestions2.dart
+++ b/pkg/analysis_server/lib/src/handler/legacy/completion_get_suggestions2.dart
@@ -147,7 +147,7 @@
     }
 
     var performance = OperationPerformanceImpl('<root>');
-    performance.runAsync(
+    await performance.runAsync(
       'request',
       (performance) async {
         var resolvedUnit = await performance.runAsync(
diff --git a/pkg/analysis_server/lib/src/handler/legacy/edit_format.dart b/pkg/analysis_server/lib/src/handler/legacy/edit_format.dart
index bf77274..d09c2ee 100644
--- a/pkg/analysis_server/lib/src/handler/legacy/edit_format.dart
+++ b/pkg/analysis_server/lib/src/handler/legacy/edit_format.dart
@@ -20,7 +20,7 @@
 
   @override
   Future<void> handle() async {
-    server.options.analytics?.sendEvent('edit', 'format');
+    unawaited(server.options.analytics?.sendEvent('edit', 'format'));
 
     var params = EditFormatParams.fromRequest(request);
     var file = params.file;
diff --git a/pkg/analysis_server/lib/src/handler/legacy/edit_get_postfix_completion.dart b/pkg/analysis_server/lib/src/handler/legacy/edit_get_postfix_completion.dart
index ba79981..db908dd 100644
--- a/pkg/analysis_server/lib/src/handler/legacy/edit_get_postfix_completion.dart
+++ b/pkg/analysis_server/lib/src/handler/legacy/edit_get_postfix_completion.dart
@@ -18,7 +18,10 @@
 
   @override
   Future<void> handle() async {
-    server.options.analytics?.sendEvent('edit', 'getPostfixCompletion');
+    unawaited(server.options.analytics?.sendEvent(
+      'edit',
+      'getPostfixCompletion',
+    ));
 
     var params = EditGetPostfixCompletionParams.fromRequest(request);
     var file = params.file;
diff --git a/pkg/analysis_server/lib/src/handler/legacy/edit_organize_directives.dart b/pkg/analysis_server/lib/src/handler/legacy/edit_organize_directives.dart
index 99967a5..2f06e89 100644
--- a/pkg/analysis_server/lib/src/handler/legacy/edit_organize_directives.dart
+++ b/pkg/analysis_server/lib/src/handler/legacy/edit_organize_directives.dart
@@ -21,7 +21,10 @@
   @override
   Future<void> handle() async {
     // TODO(brianwilkerson) Move analytics tracking out of [handleRequest].
-    server.options.analytics?.sendEvent('edit', 'organizeDirectives');
+    unawaited(server.options.analytics?.sendEvent(
+      'edit',
+      'organizeDirectives',
+    ));
 
     var params = EditOrganizeDirectivesParams.fromRequest(request);
     var file = params.file;
diff --git a/pkg/analysis_server/lib/src/handler/legacy/search_find_element_references.dart b/pkg/analysis_server/lib/src/handler/legacy/search_find_element_references.dart
index ccdbbaf..a0bdfbb 100644
--- a/pkg/analysis_server/lib/src/handler/legacy/search_find_element_references.dart
+++ b/pkg/analysis_server/lib/src/handler/legacy/search_find_element_references.dart
@@ -25,8 +25,8 @@
     var file = params.file;
     // prepare element
     var element = await server.getElementAtOffset(file, params.offset);
-    if (element is ImportElement) {
-      element = element.prefix;
+    if (element is ImportElement2) {
+      element = element.prefix?.element;
     }
     if (element is FieldFormalParameterElement) {
       element = element.field;
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
index 5cd9ec2..5d42f49 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
@@ -223,7 +223,7 @@
   Map<String, Set<String>> _buildLookupOfImportedSymbols(
       ResolvedUnitResult unit) {
     final alreadyImportedSymbols = <String, Set<String>>{};
-    final importElementList = unit.libraryElement.imports;
+    final importElementList = unit.libraryElement.imports2;
     for (var import in importElementList) {
       final importedLibrary = import.importedLibrary;
       if (importedLibrary == null) continue;
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_execute_command.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_execute_command.dart
index e75e0f1..2454202 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_execute_command.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_execute_command.dart
@@ -52,11 +52,11 @@
             ? ProgressReporter.serverCreated(server)
             : ProgressReporter.noop;
 
-    // To make parsing arguments easier in commands, instead of a
+    // To make passing arguments easier in commands, instead of a
     // `List<Object?>` we now use `Map<String, Object?>`.
     //
     // However, some handlers still support the list for compatibility so we
-    // must allow the to convert a `List` to a `Map`.
+    // must allow them to convert a `List` to a `Map`.
     final arguments = params.arguments ?? const [];
     Map<String, Object?> commandParams;
     if (arguments.length == 1 && arguments[0] is Map<String, Object?>) {
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_references.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_references.dart
index 0f72ab1..07fd4ff 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_references.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_references.dart
@@ -56,8 +56,8 @@
   Future<ErrorOr<List<Location>?>> _getReferences(String path, int offset,
       ReferenceParams params, ResolvedUnitResult unit) async {
     var element = await server.getElementAtOffset(path, offset);
-    if (element is ImportElement) {
-      element = element.prefix;
+    if (element is ImportElement2) {
+      element = element.prefix?.element;
     }
     if (element is FieldFormalParameterElement) {
       element = element.field;
diff --git a/pkg/analysis_server/lib/src/plugin/plugin_manager.dart b/pkg/analysis_server/lib/src/plugin/plugin_manager.dart
index 9c89392..df45853 100644
--- a/pkg/analysis_server/lib/src/plugin/plugin_manager.dart
+++ b/pkg/analysis_server/lib/src/plugin/plugin_manager.dart
@@ -343,12 +343,12 @@
       _pluginMap[path] = plugin;
       try {
         var session = await plugin.start(byteStorePath, sdkPath);
-        session?.onDone.then((_) {
+        unawaited(session?.onDone.then((_) {
           if (_pluginMap[path] == plugin) {
             _pluginMap.remove(path);
             _notifyPluginsChanged();
           }
-        });
+        }));
       } catch (exception, stackTrace) {
         // Record the exception (for debugging purposes) and record the fact
         // that we should not try to communicate with the plugin.
@@ -534,9 +534,9 @@
         //
         _pluginMap[path] = plugin;
         var session = await plugin.start(byteStorePath, sdkPath);
-        session?.onDone.then((_) {
+        unawaited(session?.onDone.then((_) {
           _pluginMap.remove(path);
-        });
+        }));
         //
         // Re-initialize the plugin.
         //
@@ -993,7 +993,7 @@
     name = result.name;
     version = result.version;
     if (!isCompatible) {
-      sendRequest(PluginShutdownParams());
+      unawaited(sendRequest(PluginShutdownParams()));
       info.reportException(CaughtException(
           PluginException('Plugin is not compatible.'), StackTrace.current));
       return false;
diff --git a/pkg/analysis_server/lib/src/server/dev_server.dart b/pkg/analysis_server/lib/src/server/dev_server.dart
index d59e2fa..863667e 100644
--- a/pkg/analysis_server/lib/src/server/dev_server.dart
+++ b/pkg/analysis_server/lib/src/server/dev_server.dart
@@ -129,14 +129,15 @@
       }
     });
 
-    _channel.sendRequest(Request('${_nextId++}', 'server.setSubscriptions', {
+    await _channel
+        .sendRequest(Request('${_nextId++}', 'server.setSubscriptions', {
       'subscriptions': ['STATUS'],
     }));
 
     directories =
         directories.map((dir) => path.normalize(path.absolute(dir))).toList();
 
-    _channel.sendRequest(Request(
+    await _channel.sendRequest(Request(
       '${_nextId++}',
       'analysis.setAnalysisRoots',
       {'included': directories, 'excluded': []},
diff --git a/pkg/analysis_server/lib/src/server/driver.dart b/pkg/analysis_server/lib/src/server/driver.dart
index 7234c5b..f553451 100644
--- a/pkg/analysis_server/lib/src/server/driver.dart
+++ b/pkg/analysis_server/lib/src/server/driver.dart
@@ -428,7 +428,7 @@
         }
         await instrumentationService.shutdown();
 
-        socketServer.analysisServer!.shutdown();
+        unawaited(socketServer.analysisServer!.shutdown());
 
         try {
           tempDriverDir.deleteSync(recursive: true);
@@ -454,7 +454,7 @@
             httpServer.close();
           }
           await instrumentationService.shutdown();
-          socketServer.analysisServer!.shutdown();
+          unawaited(socketServer.analysisServer!.shutdown());
           if (sendPort == null) exit(0);
         });
       },
@@ -502,7 +502,7 @@
         // Only shutdown the server and exit if the server is not already
         // handling the shutdown.
         if (!socketServer.analysisServer!.willExit) {
-          socketServer.analysisServer!.shutdown();
+          unawaited(socketServer.analysisServer!.shutdown());
           exit(0);
         }
       });
diff --git a/pkg/analysis_server/lib/src/server/http_server.dart b/pkg/analysis_server/lib/src/server/http_server.dart
index 9826d89..eb8835a 100644
--- a/pkg/analysis_server/lib/src/server/http_server.dart
+++ b/pkg/analysis_server/lib/src/server/http_server.dart
@@ -2,6 +2,7 @@
 // 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 'dart:async';
 import 'dart:io';
 
 import 'package:analysis_server/src/socket_server.dart';
@@ -102,7 +103,7 @@
         response.headers.contentType = ContentType.text;
         response.write(
             'WebSocket connections not supported (${request.uri.path}).');
-        response.close();
+        unawaited(response.close());
       } else {
         _returnUnknownRequest(request);
       }
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/imported_reference_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/imported_reference_contributor.dart
index 56c5db2..1296ec4 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/imported_reference_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/imported_reference_contributor.dart
@@ -18,14 +18,14 @@
     }
 
     // Traverse imports including dart:core
-    var imports = request.libraryElement.imports;
+    var imports = request.libraryElement.imports2;
     for (var importElement in imports) {
       var libraryElement = importElement.importedLibrary;
       if (libraryElement != null) {
         _buildSuggestions(
           libraryElement: libraryElement,
           namespace: importElement.namespace,
-          prefix: importElement.prefix?.name,
+          prefix: importElement.prefix?.element.name,
         );
         if (libraryElement.isDartCore &&
             request.opType.includeTypeNameSuggestions) {
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/library_member_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/library_member_contributor.dart
index 86e8275..2aced27 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/library_member_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/library_member_contributor.dart
@@ -22,18 +22,18 @@
     if (targetId is SimpleIdentifier && !request.target.isCascade) {
       var elem = targetId.staticElement;
       if (elem is PrefixElement && !elem.isSynthetic) {
-        var imports = request.libraryElement.imports;
+        var imports = request.libraryElement.imports2;
         _buildSuggestions(elem, imports);
       }
     }
   }
 
-  void _buildSuggestions(PrefixElement elem, List<ImportElement> imports) {
+  void _buildSuggestions(PrefixElement elem, List<ImportElement2> imports) {
     var parent = request.target.containingNode.parent;
     var typesOnly = parent is NamedType;
     var isConstructor = parent?.parent is ConstructorName;
     for (var importElem in imports) {
-      if (importElem.prefix?.name == elem.name) {
+      if (importElem.prefix?.element.name == elem.name) {
         var library = importElem.importedLibrary;
         if (library != null) {
           builder.libraryUriStr = library.source.uri.toString();
@@ -65,7 +65,7 @@
             }
           }
           // If the import is `deferred` then suggest `loadLibrary`.
-          if (!typesOnly && importElem.isDeferred) {
+          if (!typesOnly && importElem.prefix is DeferredImportElementPrefix) {
             builder.suggestLoadLibraryFunction(library.loadLibraryFunction);
           }
           builder.libraryUriStr = null;
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/library_prefix_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/library_prefix_contributor.dart
index 553c1ec..4e025b9 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/library_prefix_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/library_prefix_contributor.dart
@@ -15,9 +15,9 @@
       return;
     }
 
-    var imports = request.libraryElement.imports;
+    var imports = request.libraryElement.imports2;
     for (var element in imports) {
-      var prefix = element.prefix?.name;
+      var prefix = element.prefix?.element.name;
       if (prefix != null && prefix.isNotEmpty) {
         var libraryElement = element.importedLibrary;
         if (libraryElement != null) {
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/not_imported_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/not_imported_contributor.dart
index 9949e2b..39c8edd 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/not_imported_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/not_imported_contributor.dart
@@ -46,7 +46,7 @@
     }
 
     var importedLibraries = Set<LibraryElement>.identity();
-    for (var import in request.libraryElement.imports) {
+    for (var import in request.libraryElement.imports2) {
       var importedLibrary = import.importedLibrary;
       if (importedLibrary != null) {
         if (import.combinators.isEmpty) {
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/change_to.dart b/pkg/analysis_server/lib/src/services/correction/dart/change_to.dart
index 67c2ff4..eb77c8c 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/change_to.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/change_to.dart
@@ -112,8 +112,8 @@
         }
       }
       // Check elements from imports.
-      for (var importElement in resolvedResult.libraryElement.imports) {
-        if (importElement.prefix?.name == prefixName) {
+      for (var importElement in resolvedResult.libraryElement.imports2) {
+        if (importElement.prefix?.element.name == prefixName) {
           var namespace = getImportNamespace(importElement);
           finder._updateList(namespace.values);
         }
@@ -220,8 +220,8 @@
         }
       }
       // Check unprefixed imports.
-      for (var importElement in resolvedResult.libraryElement.imports) {
-        if (importElement.prefix?.name == prefixName) {
+      for (var importElement in resolvedResult.libraryElement.imports2) {
+        if (importElement.prefix?.element.name == prefixName) {
           var namespace = getImportNamespace(importElement);
           finder._updateList(namespace.values);
         }
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/create_class.dart b/pkg/analysis_server/lib/src/services/correction/dart/create_class.dart
index 00cbb7d..1107023 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/create_class.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/create_class.dart
@@ -72,8 +72,9 @@
       filePath = file;
       prefix = '$eol$eol';
     } else {
-      for (var import in libraryElement.imports) {
-        if (prefixElement is PrefixElement && import.prefix == prefixElement) {
+      for (var import in libraryElement.imports2) {
+        if (prefixElement is PrefixElement &&
+            import.prefix?.element == prefixElement) {
           var library = import.importedLibrary;
           if (library != null) {
             targetUnit = library.definingCompilationUnit;
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/create_mixin.dart b/pkg/analysis_server/lib/src/services/correction/dart/create_mixin.dart
index 01698d0..d66cd28 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/create_mixin.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/create_mixin.dart
@@ -69,8 +69,9 @@
       filePath = file;
       prefix = '$eol$eol';
     } else {
-      for (var import in libraryElement.imports) {
-        if (prefixElement is PrefixElement && import.prefix == prefixElement) {
+      for (var import in libraryElement.imports2) {
+        if (prefixElement is PrefixElement &&
+            import.prefix?.element == prefixElement) {
           var library = import.importedLibrary;
           if (library != null) {
             targetUnit = library.definingCompilationUnit;
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/data_driven.dart b/pkg/analysis_server/lib/src/services/correction/dart/data_driven.dart
index ba96a6c..41ed6d7 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/data_driven.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/data_driven.dart
@@ -10,7 +10,8 @@
 import 'package:analysis_server/src/services/correction/fix/data_driven/transform.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set_manager.dart';
-import 'package:analyzer/dart/element/element.dart' show LibraryElement;
+import 'package:analyzer/dart/element/element.dart'
+    show DirectiveUriWithRelativeUri, LibraryElement;
 import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
 import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
 import 'package:meta/meta.dart';
@@ -24,13 +25,13 @@
   Stream<CorrectionProducer> get producers async* {
     var importedUris = <Uri>[];
     var library = resolvedResult.libraryElement;
-    for (var importElement in library.imports) {
+    for (var importElement in library.imports2) {
       // TODO(brianwilkerson) Filter based on combinators to help avoid making
       //  invalid suggestions.
       var uri = importElement.uri;
-      if (uri != null) {
+      if (uri is DirectiveUriWithRelativeUri) {
         // The [uri] is `null` if the literal string is not a valid URI.
-        importedUris.add(Uri.parse(uri));
+        importedUris.add(uri.relativeUri);
       }
     }
     var matchers = ElementMatcher.matchersForNode(node);
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/import_add_show.dart b/pkg/analysis_server/lib/src/services/correction/dart/import_add_show.dart
index 20fa0ec..c2d0ee8 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/import_add_show.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/import_add_show.dart
@@ -30,7 +30,7 @@
       return;
     }
     // prepare whole import namespace
-    var importElement = importDirective.element;
+    var importElement = importDirective.element2;
     if (importElement == null) {
       return;
     }
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/import_library.dart b/pkg/analysis_server/lib/src/services/correction/dart/import_library.dart
index e6b8f80..ab3f3b4 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/import_library.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/import_library.dart
@@ -164,7 +164,7 @@
     // is, then we can check the extension elements without needing to perform
     // additional analysis.
     var foundImport = false;
-    for (var imp in libraryElement.imports) {
+    for (var imp in libraryElement.imports2) {
       // prepare element
       var importedLibrary = imp.importedLibrary;
       if (importedLibrary == null || importedLibrary != libraryToImport) {
@@ -244,7 +244,7 @@
     // may be there is an existing import,
     // but it is with prefix and we don't use this prefix
     var alreadyImportedWithPrefix = <LibraryElement>{};
-    for (var imp in libraryElement.imports) {
+    for (var imp in libraryElement.imports2) {
       // prepare element
       var libraryElement = imp.importedLibrary;
       if (libraryElement == null) {
@@ -261,7 +261,7 @@
         continue;
       }
       // may be apply prefix
-      var prefix = imp.prefix;
+      var prefix = imp.prefix?.element;
       if (prefix != null) {
         yield _ImportLibraryPrefix(libraryElement, prefix);
         continue;
diff --git a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
index b1fe5b7..3549471 100644
--- a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
+++ b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
@@ -1628,6 +1628,8 @@
   status: needsEvaluation
 LintCode.close_sinks:
   status: needsEvaluation
+LintCode.combinators_ordering:
+  status: needsEvaluation
 LintCode.comment_references:
   status: needsEvaluation
 LintCode.conditional_uri_does_not_exist:
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/element_matcher.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/element_matcher.dart
index 0c23da3..f87525e 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/element_matcher.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/element_matcher.dart
@@ -122,7 +122,7 @@
     if (library == null) {
       return null;
     }
-    for (var importElement in library.imports) {
+    for (var importElement in library.imports2) {
       // TODO(brianwilkerson) Filter based on combinators to help avoid making
       //  invalid suggestions.
       var uri = importElement.importedLibrary?.source.uri;
diff --git a/pkg/analysis_server/lib/src/services/correction/namespace.dart b/pkg/analysis_server/lib/src/services/correction/namespace.dart
index affc53d..c97452d 100644
--- a/pkg/analysis_server/lib/src/services/correction/namespace.dart
+++ b/pkg/analysis_server/lib/src/services/correction/namespace.dart
@@ -14,13 +14,13 @@
   return _getExportNamespaceForLibrary(library)[name];
 }
 
-/// Return the [ImportElement] that is referenced by [prefixNode], or `null` if
+/// Return the [ImportElement2] that is referenced by [prefixNode], or `null` if
 /// the node does not reference a prefix or if we cannot determine which import
 /// is being referenced.
-ImportElement? getImportElement(SimpleIdentifier prefixNode) {
+ImportElement2? getImportElement(SimpleIdentifier prefixNode) {
   var parent = prefixNode.parent;
   if (parent is ImportDirective) {
-    return parent.element;
+    return parent.element2;
   }
   return _getImportElementInfo(prefixNode);
 }
@@ -31,27 +31,27 @@
   return namespace.definedNames;
 }
 
-/// Return the [ImportElement] that declared [prefix] and imports [element].
+/// Return the [ImportElement2] that declared [prefix] and imports [element].
 ///
 /// [libraryElement] - the [LibraryElement] where reference is.
 /// [prefix] - the import prefix, maybe `null`.
 /// [element] - the referenced element.
-/// [importElementsMap] - the cache of [Element]s imported by [ImportElement]s.
-ImportElement? _getImportElement(LibraryElement libraryElement, String prefix,
-    Element element, Map<ImportElement, Set<Element>> importElementsMap) {
+/// [importElementsMap] - the cache of [Element]s imported by [ImportElement2]s.
+ImportElement2? _getImportElement(LibraryElement libraryElement, String prefix,
+    Element element, Map<ImportElement2, Set<Element>> importElementsMap) {
   if (element.enclosingElement is! CompilationUnitElement) {
     return null;
   }
   var usedLibrary = element.library;
   // find ImportElement that imports used library with used prefix
-  List<ImportElement>? candidates;
-  for (var importElement in libraryElement.imports) {
+  List<ImportElement2>? candidates;
+  for (var importElement in libraryElement.imports2) {
     // required library
     if (importElement.importedLibrary != usedLibrary) {
       continue;
     }
     // required prefix
-    var prefixElement = importElement.prefix;
+    var prefixElement = importElement.prefix?.element;
     if (prefixElement == null) {
       continue;
     }
@@ -95,9 +95,9 @@
   return null;
 }
 
-/// Returns the [ImportElement] that is referenced by [prefixNode] with a
+/// Returns the [ImportElement2] that is referenced by [prefixNode] with a
 /// [PrefixElement], maybe `null`.
-ImportElement? _getImportElementInfo(SimpleIdentifier prefixNode) {
+ImportElement2? _getImportElementInfo(SimpleIdentifier prefixNode) {
   // prepare environment
   var parent = prefixNode.parent;
   var unit = prefixNode.thisOrAncestorOfType<CompilationUnit>();
@@ -124,7 +124,7 @@
   }
   // find ImportElement
   var prefix = prefixNode.name;
-  var importElementsMap = <ImportElement, Set<Element>>{};
+  var importElementsMap = <ImportElement2, Set<Element>>{};
   return _getImportElement(
       libraryElement, prefix, usedElement, importElementsMap);
 }
diff --git a/pkg/analysis_server/lib/src/services/correction/util.dart b/pkg/analysis_server/lib/src/services/correction/util.dart
index 3c1c745..603fb4e 100644
--- a/pkg/analysis_server/lib/src/services/correction/util.dart
+++ b/pkg/analysis_server/lib/src/services/correction/util.dart
@@ -326,8 +326,8 @@
   return Precedence.none;
 }
 
-/// Returns the namespace of the given [ImportElement].
-Map<String, Element> getImportNamespace(ImportElement imp) {
+/// Returns the namespace of the given [ImportElement2].
+Map<String, Element> getImportNamespace(ImportElement2 imp) {
   return imp.namespace.definedNames;
 }
 
@@ -1215,8 +1215,8 @@
 
   /// Return the import element used to import given [element] into the library.
   /// May be `null` if was not imported, i.e. declared in the same library.
-  ImportElement? _getImportElement(Element element) {
-    for (var imp in _library.imports) {
+  ImportElement2? _getImportElement(Element element) {
+    for (var imp in _library.imports2) {
       var definedNames = getImportNamespace(imp);
       if (definedNames.containsValue(element)) {
         return imp;
@@ -1270,7 +1270,7 @@
       // ensure import
       var importElement = _getImportElement(element);
       if (importElement != null) {
-        var prefix = importElement.prefix;
+        var prefix = importElement.prefix?.element;
         if (prefix != null) {
           sb.write(prefix.displayName);
           sb.write('.');
diff --git a/pkg/analysis_server/lib/src/services/refactoring/refactoring.dart b/pkg/analysis_server/lib/src/services/refactoring/refactoring.dart
index 7d76a4a..76dd47b 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/refactoring.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/refactoring.dart
@@ -413,7 +413,7 @@
     if (element is ConstructorElement) {
       return RenameConstructorRefactoringImpl(workspace, session, element);
     }
-    if (element is ImportElement) {
+    if (element is ImportElement2) {
       return RenameImportRefactoringImpl(workspace, session, element);
     }
     if (element is LabelElement) {
@@ -452,7 +452,7 @@
     }
 
     // Use the prefix offset/length when renaming an import directive.
-    if (node is ImportDirective && element is ImportElement) {
+    if (node is ImportDirective && element is ImportElement2) {
       var prefix = node.prefix;
       if (prefix != null) {
         offset = prefix.offset;
diff --git a/pkg/analysis_server/lib/src/services/refactoring/rename.dart b/pkg/analysis_server/lib/src/services/refactoring/rename.dart
index 7f93bf1..66c74f0 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/rename.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/rename.dart
@@ -114,8 +114,8 @@
   static String _getOldName(Element element) {
     if (element is ConstructorElement) {
       return element.name;
-    } else if (element is ImportElement) {
-      var prefix = element.prefix;
+    } else if (element is ImportElement2) {
+      var prefix = element.prefix?.element;
       if (prefix != null) {
         return prefix.displayName;
       }
diff --git a/pkg/analysis_server/lib/src/services/refactoring/rename_import.dart b/pkg/analysis_server/lib/src/services/refactoring/rename_import.dart
index 2a0bf1f..94f6500 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/rename_import.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/rename_import.dart
@@ -16,16 +16,16 @@
 import 'package:analyzer/src/generated/source.dart';
 import 'package:analyzer_plugin/utilities/range_factory.dart';
 
-/// A [Refactoring] for renaming [ImportElement]s.
+/// A [Refactoring] for renaming [ImportElement2]s.
 class RenameImportRefactoringImpl extends RenameRefactoringImpl {
   final AnalysisSession session;
 
   RenameImportRefactoringImpl(
-      RefactoringWorkspace workspace, this.session, ImportElement element)
+      RefactoringWorkspace workspace, this.session, ImportElement2 element)
       : super(workspace, element);
 
   @override
-  ImportElement get element => super.element as ImportElement;
+  ImportElement2 get element => super.element as ImportElement2;
 
   @override
   String get refactoringName {
@@ -49,32 +49,28 @@
   Future<void> fillChange() async {
     // update declaration
     {
-      var prefix = element.prefix;
+      final node = await _findNode();
+      if (node == null) {
+        return;
+      }
+
+      final prefixNode = node.prefix;
       SourceEdit? edit;
       if (newName.isEmpty) {
         // We should not get `prefix == null` because we check in
         // `checkNewName` that the new name is different.
-        if (prefix == null) {
-          return;
-        }
-        var node = await _findNode();
-        if (node != null) {
+        if (prefixNode != null) {
           var uriEnd = node.uri.end;
-          var prefixEnd = prefix.nameOffset + prefix.nameLength;
+          var prefixEnd = prefixNode.end;
           edit = newSourceEdit_range(
               range.startOffsetEndOffset(uriEnd, prefixEnd), '');
         }
       } else {
-        if (prefix == null) {
-          var node = await _findNode();
-          if (node != null) {
-            var uriEnd = node.uri.end;
-            edit = newSourceEdit_range(SourceRange(uriEnd, 0), ' as $newName');
-          }
+        if (prefixNode == null) {
+          var uriEnd = node.uri.end;
+          edit = newSourceEdit_range(SourceRange(uriEnd, 0), ' as $newName');
         } else {
-          var offset = prefix.nameOffset;
-          var length = prefix.nameLength;
-          edit = newSourceEdit_range(SourceRange(offset, length), newName);
+          edit = newSourceEdit_range(range.node(prefixNode), newName);
         }
       }
       if (edit != null) {
@@ -111,7 +107,7 @@
       return null;
     }
     var unit = unitResult.unit;
-    var index = library.imports.indexOf(element);
+    var index = library.imports2.indexOf(element);
     return unit.directives.whereType<ImportDirective>().elementAt(index);
   }
 
diff --git a/pkg/analysis_server/lib/src/services/refactoring/rename_unit_member.dart b/pkg/analysis_server/lib/src/services/refactoring/rename_unit_member.dart
index 0479329..4f25f31 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/rename_unit_member.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/rename_unit_member.dart
@@ -183,7 +183,7 @@
       return true;
     }
     // check imports
-    for (var importElement in atLibrary.imports) {
+    for (var importElement in atLibrary.imports2) {
       // ignore if imported with prefix
       if (importElement.prefix != null) {
         continue;
diff --git a/pkg/analysis_server/lib/src/services/search/search_engine.dart b/pkg/analysis_server/lib/src/services/search/search_engine.dart
index 09df9a4..0b88104 100644
--- a/pkg/analysis_server/lib/src/services/search/search_engine.dart
+++ b/pkg/analysis_server/lib/src/services/search/search_engine.dart
@@ -87,9 +87,7 @@
 /// Instances of the class [SearchMatch] represent a match found by
 /// [SearchEngine].
 abstract class SearchMatch {
-  /// Return the [Element] containing the match. Can return `null` if the unit
-  /// does not exist, or its element was invalidated, or the element cannot be
-  /// found, etc.
+  /// Return the [Element] containing the match.
   Element get element;
 
   /// The absolute path of the file containing the match.
diff --git a/pkg/analysis_server/lib/src/status/ast_writer.dart b/pkg/analysis_server/lib/src/status/ast_writer.dart
index b9f610a..b98c3d6 100644
--- a/pkg/analysis_server/lib/src/status/ast_writer.dart
+++ b/pkg/analysis_server/lib/src/status/ast_writer.dart
@@ -87,7 +87,7 @@
     } else if (node is GenericFunctionType) {
       properties['type'] = node.type;
     } else if (node is ImportDirective) {
-      properties['element'] = node.element;
+      properties['element'] = node.element2;
       properties['selectedSource'] = node.selectedSource;
       properties['uriSource'] = node.uriSource;
     } else if (node is IndexExpression) {
diff --git a/pkg/analysis_server/lib/src/status/element_writer.dart b/pkg/analysis_server/lib/src/status/element_writer.dart
index bc09cd5..560fc23 100644
--- a/pkg/analysis_server/lib/src/status/element_writer.dart
+++ b/pkg/analysis_server/lib/src/status/element_writer.dart
@@ -99,9 +99,9 @@
       properties['returnType'] = element.returnType;
       properties['type'] = element.type;
     }
-    if (element is ImportElement) {
+    if (element is ImportElement2) {
       properties['combinators'] = element.combinators;
-      properties['isDeferred'] = element.isDeferred;
+      properties['isDeferred'] = element.prefix is DeferredImportElementPrefix;
       properties['library'] = element.library;
     }
     if (element is LibraryElement) {
diff --git a/pkg/analysis_server/lib/src/status/pages.dart b/pkg/analysis_server/lib/src/status/pages.dart
index 66538d6..130a190 100644
--- a/pkg/analysis_server/lib/src/status/pages.dart
+++ b/pkg/analysis_server/lib/src/status/pages.dart
@@ -2,6 +2,7 @@
 // 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 'dart:async';
 import 'dart:convert';
 import 'dart:io';
 
@@ -192,7 +193,7 @@
       var path = request.uri.path;
 
       if (path == '/') {
-        respondRedirect(request, pages.first.path);
+        unawaited(respondRedirect(request, pages.first.path));
         return;
       }
 
@@ -201,7 +202,7 @@
           var response = request.response;
           response.headers.contentType = ContentType.html;
           response.write(await page.generate(request.uri.queryParameters));
-          response.close();
+          unawaited(response.close());
           return;
         }
       }
@@ -216,7 +217,7 @@
         response.statusCode = HttpStatus.internalServerError;
         response.headers.contentType = ContentType.text;
         response.write('$e\n\n$st');
-        response.close();
+        unawaited(response.close());
       }
     }
   }
diff --git a/pkg/analysis_server/test/analysis/notification_errors_test.dart b/pkg/analysis_server/test/analysis/notification_errors_test.dart
index 4c93ced..fcec6cf 100644
--- a/pkg/analysis_server/test/analysis/notification_errors_test.dart
+++ b/pkg/analysis_server/test/analysis/notification_errors_test.dart
@@ -339,7 +339,6 @@
     expect(error.location.file, testFile.path);
     expect(error.severity, AnalysisErrorSeverity.INFO);
     expect(error.type, AnalysisErrorType.LINT);
-    expect(error.message, lint.description);
   }
 
   Future<void> test_notInAnalysisRoot() async {
diff --git a/pkg/analysis_server/test/completion_test_support.dart b/pkg/analysis_server/test/completion_test_support.dart
index cdf6b9b..a0ebd67 100644
--- a/pkg/analysis_server/test/completion_test_support.dart
+++ b/pkg/analysis_server/test/completion_test_support.dart
@@ -79,7 +79,8 @@
         .toList();
   }
 
-  Future runTest(LocationSpec spec, [Map<String, String>? extraFiles]) async {
+  Future<void> runTest(LocationSpec spec,
+      [Map<String, String>? extraFiles]) async {
     await super.setUp();
 
     try {
@@ -102,7 +103,7 @@
         assertHasNoCompletion(result);
       }
     } finally {
-      super.tearDown();
+      await super.tearDown();
     }
   }
 }
diff --git a/pkg/analysis_server/test/domain_analysis_test.dart b/pkg/analysis_server/test/domain_analysis_test.dart
index b3f9c9b..1c94ed1 100644
--- a/pkg/analysis_server/test/domain_analysis_test.dart
+++ b/pkg/analysis_server/test/domain_analysis_test.dart
@@ -1458,7 +1458,7 @@
   /// Repeat a few times, eventually there will be no work to do.
   Future<void> _waitAnalysisComplete() async {
     for (var i = 0; i < 128; i++) {
-      pumpEventQueue();
+      await pumpEventQueue();
       await server.onAnalysisComplete;
     }
   }
diff --git a/pkg/analysis_server/test/domain_execution_test.dart b/pkg/analysis_server/test/domain_execution_test.dart
index a9f288c..8532002 100644
--- a/pkg/analysis_server/test/domain_execution_test.dart
+++ b/pkg/analysis_server/test/domain_execution_test.dart
@@ -130,7 +130,7 @@
   @override
   Future<void> tearDown() async {
     await _disposeExecutionContext();
-    super.tearDown();
+    await super.tearDown();
   }
 
   Future<void> test_createAndDeleteMultipleContexts() async {
diff --git a/pkg/analysis_server/test/edit/refactoring_test.dart b/pkg/analysis_server/test/edit/refactoring_test.dart
index f90d1e6..e397c48 100644
--- a/pkg/analysis_server/test/edit/refactoring_test.dart
+++ b/pkg/analysis_server/test/edit/refactoring_test.dart
@@ -2,6 +2,8 @@
 // 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 'dart:async';
+
 import 'package:analysis_server/protocol/protocol.dart';
 import 'package:analysis_server/protocol/protocol_generated.dart';
 import 'package:analysis_server/src/services/refactoring/refactoring_manager.dart';
@@ -877,8 +879,7 @@
     var request =
         EditGetAvailableRefactoringsParams(testFile.path, offset, length)
             .toRequest('0');
-    serverChannel.sendRequest(request);
-    var response = await serverChannel.waitForResponse(request);
+    var response = await serverChannel.sendRequest(request);
     var result = EditGetAvailableRefactoringsResult.fromResponse(response);
     kinds = result.kinds;
   }
@@ -2250,7 +2251,8 @@
   print(otherName);
 }
 ''');
-    server.getAnalysisDriver(testFile.path)!.getResult(testFile.path);
+    unawaited(
+        server.getAnalysisDriver(testFile.path)!.getResult(testFile.path));
     // send the second request, with the same kind, file and offset
     await waitForTasksFinished();
     result = await getRefactoringResult(() {
diff --git a/pkg/analysis_server/test/integration/analysis/update_content_list_test.dart b/pkg/analysis_server/test/integration/analysis/update_content_list_test.dart
index b41a1df..1b7f70e 100644
--- a/pkg/analysis_server/test/integration/analysis/update_content_list_test.dart
+++ b/pkg/analysis_server/test/integration/analysis/update_content_list_test.dart
@@ -27,6 +27,7 @@
     // Create a dummy file
     writeFile(pathname, '// dummy text');
     await standardAnalysisSetup();
+    await analysisFinished;
     // Override file contents with badText.
     await sendAnalysisUpdateContent({pathname: AddContentOverlay(badText)});
     await analysisFinished;
diff --git a/pkg/analysis_server/test/lsp/definition_test.dart b/pkg/analysis_server/test/lsp/definition_test.dart
index 1577e53..8c6c351 100644
--- a/pkg/analysis_server/test/lsp/definition_test.dart
+++ b/pkg/analysis_server/test/lsp/definition_test.dart
@@ -173,6 +173,17 @@
     await testContents(contents);
   }
 
+  Future<void> test_fieldFormalParam() async {
+    final contents = '''
+class A {
+  final String [[a]];
+  A(this.^a});
+}
+''';
+
+    await testContents(contents);
+  }
+
   Future<void> test_fromPlugins() async {
     final pluginAnalyzedFilePath = join(projectFolderPath, 'lib', 'foo.foo');
     final pluginAnalyzedFileUri = Uri.file(pluginAnalyzedFilePath);
@@ -402,6 +413,19 @@
     await testContents(contents);
   }
 
+  Future<void> test_superFormalParam() async {
+    final contents = '''
+class A {
+  A({required int [[a]]});
+}
+class B extends A {
+  B({required super.^a}) : assert(a > 0);
+}
+''';
+
+    await testContents(contents);
+  }
+
   Future<void> test_type() async {
     final contents = '''
 f() {
diff --git a/pkg/analysis_server/test/services/completion/dart/completion_manager_test.dart b/pkg/analysis_server/test/services/completion/dart/completion_manager_test.dart
index fb80d3e..62bd4a5 100644
--- a/pkg/analysis_server/test/services/completion/dart/completion_manager_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/completion_manager_test.dart
@@ -59,10 +59,10 @@
 
     var directives = resolvedUnit.unit.directives;
 
-    var imports = request.libraryElement.imports;
+    var imports = request.libraryElement.imports2;
     expect(imports, hasLength(directives.length + 1));
 
-    ImportElement importNamed(String expectedUri) {
+    ImportElement2 importNamed(String expectedUri) {
       var uriList = <String>[];
       for (var importElement in imports) {
         var uri = importElement.importedLibrary!.source.uri.toString();
diff --git a/pkg/analysis_server/test/services/completion/dart/location/field_formal_parameter_test.dart b/pkg/analysis_server/test/services/completion/dart/location/field_formal_parameter_test.dart
index 68b4c68..556b765 100644
--- a/pkg/analysis_server/test/services/completion/dart/location/field_formal_parameter_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/location/field_formal_parameter_test.dart
@@ -31,7 +31,7 @@
 
 mixin SuperFormalParameterTestCases on AbstractCompletionDriverTest {
   Future<void> test_class_replacement_left() async {
-    _checkContainers(
+    await _checkContainers(
       declarations: 'var field = 0;',
       constructorParameters: 'this.f^',
       validator: (response) {
@@ -48,7 +48,7 @@
   }
 
   Future<void> test_class_replacement_right() async {
-    _checkContainers(
+    await _checkContainers(
       declarations: 'var field = 0;',
       constructorParameters: 'this.^f',
       validator: (response) {
@@ -105,7 +105,7 @@
   }
 
   Future<void> test_class_suggestions_onlyNotSpecified_optionalNamed() async {
-    _checkContainers(
+    await _checkContainers(
       declarations: 'final int x; final int y;',
       constructorParameters: '{this.x, this.^}',
       validator: (response) {
@@ -123,7 +123,7 @@
 
   Future<void>
       test_class_suggestions_onlyNotSpecified_requiredPositional() async {
-    _checkContainers(
+    await _checkContainers(
       declarations: 'final int x; final int y;',
       constructorParameters: 'this.x, this.^',
       validator: (response) {
diff --git a/pkg/analysis_server/test/services/refactoring/rename_import_test.dart b/pkg/analysis_server/test/services/refactoring/rename_import_test.dart
index 8ad640e..d8a7f3c 100644
--- a/pkg/analysis_server/test/services/refactoring/rename_import_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/rename_import_test.dart
@@ -236,6 +236,6 @@
 
   void _createRefactoring(String search) {
     var directive = findNode.import(search);
-    createRenameRefactoringForElement(directive.element);
+    createRenameRefactoringForElement(directive.element2);
   }
 }
diff --git a/pkg/analysis_server/test/src/computer/call_hierarchy_computer_test.dart b/pkg/analysis_server/test/src/computer/call_hierarchy_computer_test.dart
new file mode 100644
index 0000000..6bc39cc
--- /dev/null
+++ b/pkg/analysis_server/test/src/computer/call_hierarchy_computer_test.dart
@@ -0,0 +1,1312 @@
+// Copyright (c) 2022, 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:analysis_server/src/computer/computer_call_hierarchy.dart';
+import 'package:analysis_server/src/services/search/search_engine.dart';
+import 'package:analysis_server/src/services/search/search_engine_internal.dart';
+import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/source/source_range.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../../abstract_single_unit.dart';
+
+void main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(CallHierarchyComputerFindTargetTest);
+    defineReflectiveTests(CallHierarchyComputerIncomingCallsTest);
+    defineReflectiveTests(CallHierarchyComputerOutgoingCallsTest);
+  });
+}
+
+/// Matches a [CallHierarchyItem] with the given name/kind/file.
+Matcher _isItem(CallHierarchyKind kind, String displayName, String file,
+        SourceRange range) =>
+    TypeMatcher<CallHierarchyItem>()
+        .having((e) => e.kind, 'kind', kind)
+        .having((e) => e.displayName, 'displayName', displayName)
+        .having((e) => e.file, 'file', file)
+        .having((e) => e.range, 'range', range);
+
+/// Matches a [CallHierarchyCalls] result with the given element/ranges.
+Matcher _isResult(
+  CallHierarchyKind kind,
+  String displayName,
+  String file,
+  SourceRange range, {
+  List<SourceRange>? ranges,
+}) {
+  var matcher = TypeMatcher<CallHierarchyCalls>()
+      .having((c) => c.item, 'item', _isItem(kind, displayName, file, range));
+
+  if (ranges != null) {
+    matcher = matcher.having((c) => c.ranges, 'ranges', ranges);
+  }
+
+  return matcher;
+}
+
+abstract class AbstractCallHierarchyTest extends AbstractSingleUnitTest {
+  final startOfFile = SourceRange(0, 0);
+
+  // Gets the expected range that follows the string [prefix] in [code] with a
+  // length of [match.length].
+  SourceRange rangeAfterPrefix(String prefix, String code, String match) =>
+      SourceRange(
+          withoutMarkers(code).indexOf(prefix) + prefix.length, match.length);
+
+  // Gets the expected range that starts at [search] in [code] with a
+  // length of [match.length].
+  SourceRange rangeAtSearch(String search, String code, [String? match]) {
+    final index = withoutMarkers(code).indexOf(search);
+    expect(index, greaterThanOrEqualTo(0));
+    return SourceRange(index, (match ?? search).length);
+  }
+
+  String withoutMarkers(String code) => code.replaceAll('^', '');
+}
+
+@reflectiveTest
+class CallHierarchyComputerFindTargetTest extends AbstractCallHierarchyTest {
+  late String otherFile;
+
+  Future<void> expectNoTarget(String content) async {
+    await expectTarget(content, isNull);
+  }
+
+  Future<void> expectTarget(String content, Matcher matcher) async {
+    final target = await findTarget(content);
+    expect(target, matcher);
+  }
+
+  Future<CallHierarchyItem?> findTarget(String code) async {
+    final marker = code.indexOf('^');
+    expect(marker, greaterThanOrEqualTo(0));
+    addTestSource(withoutMarkers(code));
+    final result =
+        await (await session).getResolvedUnit(testFile) as ResolvedUnitResult;
+
+    return DartCallHierarchyComputer(result).findTarget(marker);
+  }
+
+  @override
+  void setUp() {
+    super.setUp();
+    otherFile = convertPath('$testPackageLibPath/other.dart');
+  }
+
+  Future<void> test_args() async {
+    await expectNoTarget('main(int ^a) {}');
+  }
+
+  Future<void> test_block() async {
+    await expectNoTarget('f() {^}');
+  }
+
+  Future<void> test_comment() async {
+    await expectNoTarget('f() {} // this is a ^comment');
+  }
+
+  Future<void> test_constructor() async {
+    final contents = '''
+class Foo {
+  Fo^o(String a) {}
+}
+    ''';
+
+    final target = await findTarget(contents);
+    expect(
+      target,
+      _isItem(
+        CallHierarchyKind.constructor,
+        'Foo',
+        testFile,
+        rangeAtSearch('Foo(', contents, 'Foo'),
+      ),
+    );
+  }
+
+  Future<void> test_constructorCall() async {
+    final contents = '''
+import 'other.dart';
+
+f() {
+  final foo = Fo^o();
+}
+    ''';
+
+    final otherContents = '''
+class Foo {
+  Foo();
+}
+    ''';
+
+    addSource(otherFile, otherContents);
+    await expectTarget(
+        contents,
+        _isItem(
+          CallHierarchyKind.constructor,
+          'Foo',
+          otherFile,
+          rangeAtSearch('Foo(', otherContents, 'Foo'),
+        ));
+  }
+
+  Future<void> test_extension_method() async {
+    final contents = '''
+extension StringExtension on String {
+  void myMet^hod() {}
+}
+    ''';
+
+    await expectTarget(
+        contents,
+        _isItem(
+          CallHierarchyKind.method,
+          'myMethod',
+          testFile,
+          rangeAtSearch('myMethod', contents),
+        ));
+  }
+
+  Future<void> test_extension_methodCall() async {
+    final contents = '''
+import 'other.dart';
+
+f() {
+  ''.myMet^hod();
+}
+    ''';
+
+    final otherContents = '''
+extension StringExtension on String {
+  void myMethod() {}
+}
+    ''';
+
+    addSource(otherFile, otherContents);
+    await expectTarget(
+        contents,
+        _isItem(
+          CallHierarchyKind.method,
+          'myMethod',
+          otherFile,
+          rangeAtSearch('myMethod', otherContents),
+        ));
+  }
+
+  Future<void> test_function() async {
+    final contents = '''
+void myFun^ction() {}
+    ''';
+
+    await expectTarget(
+        contents,
+        _isItem(
+          CallHierarchyKind.function,
+          'myFunction',
+          testFile,
+          rangeAtSearch('myFunction', contents),
+        ));
+  }
+
+  Future<void> test_functionCall() async {
+    final contents = '''
+import 'other.dart' as f;
+
+f() {
+  f.myFun^ction();
+}
+    ''';
+
+    final otherContents = '''
+void myFunction() {}
+    ''';
+
+    addSource(otherFile, otherContents);
+    await expectTarget(
+        contents,
+        _isItem(
+          CallHierarchyKind.function,
+          'myFunction',
+          otherFile,
+          rangeAtSearch('myFunction', otherContents),
+        ));
+  }
+
+  Future<void> test_getter() async {
+    final contents = '''
+class Foo {
+  String get fo^o => '';
+}
+    ''';
+
+    await expectTarget(
+        contents,
+        _isItem(
+          CallHierarchyKind.property,
+          'get foo',
+          testFile,
+          rangeAtSearch('foo', contents),
+        ));
+  }
+
+  Future<void> test_getterCall() async {
+    final contents = '''
+import 'other.dart';
+
+f() {
+  final foo = ba^r;
+}
+    ''';
+
+    final otherContents = '''
+String get bar => '';
+    ''';
+
+    addSource(otherFile, otherContents);
+    await expectTarget(
+        contents,
+        _isItem(
+          CallHierarchyKind.property,
+          'get bar',
+          otherFile,
+          rangeAtSearch('bar', otherContents),
+        ));
+  }
+
+  Future<void> test_implicitConstructorCall() async {
+    // Even if a constructor is implicit, we might want to be able to get the
+    // incoming calls, so we should return the class location as a stand-in
+    // (although with the Kind still set to constructor).
+    final contents = '''
+import 'other.dart';
+
+f() {
+  final foo = Fo^o();
+}
+    ''';
+
+    final otherContents = '''
+class Foo {}
+    ''';
+
+    addSource(otherFile, otherContents);
+    await expectTarget(
+        contents,
+        _isItem(
+          CallHierarchyKind.constructor,
+          'Foo',
+          otherFile,
+          rangeAtSearch('Foo {', otherContents, 'Foo'),
+        ));
+  }
+
+  Future<void> test_method() async {
+    final contents = '''
+class Foo {
+  void myMet^hod() {}
+}
+    ''';
+
+    await expectTarget(
+        contents,
+        _isItem(
+          CallHierarchyKind.method,
+          'myMethod',
+          testFile,
+          rangeAtSearch('myMethod', contents),
+        ));
+  }
+
+  Future<void> test_methodCall() async {
+    final contents = '''
+import 'other.dart';
+
+f() {
+  Foo().myMet^hod();
+}
+    ''';
+
+    final otherContents = '''
+class Foo {
+  void myMethod() {}
+}
+    ''';
+
+    addSource(otherFile, otherContents);
+    await expectTarget(
+        contents,
+        _isItem(
+          CallHierarchyKind.method,
+          'myMethod',
+          otherFile,
+          rangeAtSearch('myMethod', otherContents),
+        ));
+  }
+
+  Future<void> test_mixin_method() async {
+    final contents = '''
+mixin Bar {
+  void myMet^hod() {}
+}
+    ''';
+
+    await expectTarget(
+        contents,
+        _isItem(
+          CallHierarchyKind.method,
+          'myMethod',
+          testFile,
+          rangeAtSearch('myMethod', contents),
+        ));
+  }
+
+  Future<void> test_mixin_methodCall() async {
+    final contents = '''
+import 'other.dart';
+
+f() {
+  Foo().myMet^hod();
+}
+    ''';
+
+    final otherContents = '''
+class Bar {
+  void myMethod() {}
+}
+
+class Foo with Bar {}
+    ''';
+
+    addSource(otherFile, otherContents);
+    await expectTarget(
+        contents,
+        _isItem(
+          CallHierarchyKind.method,
+          'myMethod',
+          otherFile,
+          rangeAtSearch('myMethod', otherContents),
+        ));
+  }
+
+  Future<void> test_namedConstructor() async {
+    final contents = '''
+class Foo {
+  Foo.Ba^r(String a) {}
+}
+    ''';
+
+    await expectTarget(
+        contents,
+        _isItem(
+          CallHierarchyKind.constructor,
+          'Foo.Bar',
+          testFile,
+          rangeAtSearch('Bar', contents),
+        ));
+  }
+
+  Future<void> test_namedConstructor_typeName() async {
+    final contents = '''
+class Foo {
+  Fo^o.Bar(String a) {}
+}
+    ''';
+
+    await expectNoTarget(contents);
+  }
+
+  Future<void> test_namedConstructorCall() async {
+    final contents = '''
+import 'other.dart';
+
+f() {
+  final foo = Foo.Ba^r();
+}
+    ''';
+
+    final otherContents = '''
+class Foo {
+  Foo.Bar();
+}
+    ''';
+
+    addSource(otherFile, otherContents);
+    await expectTarget(
+        contents,
+        _isItem(
+          CallHierarchyKind.constructor,
+          'Foo.Bar',
+          otherFile,
+          rangeAtSearch('Bar', otherContents),
+        ));
+  }
+
+  Future<void> test_namedConstructorCall_typeName() async {
+    final contents = '''
+import 'other.dart';
+
+f() {
+  final foo = Fo^o.Bar();
+}
+    ''';
+
+    final otherContents = '''
+class Foo {
+  Foo.Bar();
+}
+    ''';
+
+    addSource(otherFile, otherContents);
+    await expectNoTarget(contents);
+  }
+
+  Future<void> test_setter() async {
+    final contents = '''
+class Foo {
+  set fo^o(Strin value) {};
+}
+    ''';
+
+    await expectTarget(
+        contents,
+        _isItem(
+          CallHierarchyKind.property,
+          'set foo',
+          testFile,
+          rangeAtSearch('foo', contents),
+        ));
+  }
+
+  Future<void> test_setterCall() async {
+    final contents = '''
+import 'other.dart';
+
+f() {
+  ba^r = '';
+}
+    ''';
+
+    final otherContents = '''
+set bar(String value) {}
+    ''';
+
+    addSource(otherFile, otherContents);
+    await expectTarget(
+        contents,
+        _isItem(
+          CallHierarchyKind.property,
+          'set bar',
+          otherFile,
+          rangeAtSearch('bar', otherContents),
+        ));
+  }
+
+  Future<void> test_whitespace() async {
+    await expectNoTarget(' ^  f() {}');
+  }
+}
+
+@reflectiveTest
+class CallHierarchyComputerIncomingCallsTest extends AbstractCallHierarchyTest {
+  late String otherFile;
+  late SearchEngine searchEngine;
+
+  Future<List<CallHierarchyCalls>> findIncomingCalls(String code) async {
+    final marker = code.indexOf('^');
+    expect(marker, greaterThanOrEqualTo(0));
+    addTestSource(withoutMarkers(code));
+    final session_ = await session;
+
+    var result = await session_.getResolvedUnit(testFile) as ResolvedUnitResult;
+    expect(result.errors, isEmpty);
+    var computer = DartCallHierarchyComputer(result);
+    // It's possible that the target is in a different file (because we were
+    // invoked on a call and not a declaration), so fetch the resolved unit
+    // for the target.
+    final target = computer.findTarget(marker)!;
+    if (target.file != testFile) {
+      result =
+          await session_.getResolvedUnit(target.file) as ResolvedUnitResult;
+      expect(result.errors, isEmpty);
+      computer = DartCallHierarchyComputer(result);
+    }
+    return computer.findIncomingCalls(target, searchEngine);
+  }
+
+  @override
+  void setUp() {
+    super.setUp();
+    otherFile = convertPath('$testPackageLibPath/other.dart');
+    searchEngine = SearchEngineImpl([
+      driverFor(testPackageRootPath),
+    ]);
+  }
+
+  Future<void> test_constructor() async {
+    final contents = '''
+class Foo {
+  Fo^o();
+}
+    ''';
+
+    final otherContents = '''
+import 'test.dart';
+
+final foo1 = Foo();
+class Bar {
+  final foo2 = Foo();
+  Foo get foo3 => Foo();
+  Bar() {
+    final foo4 = Foo();
+  }
+  void bar() {
+    final foo5 = Foo();
+    final foo6 = Foo();
+  }
+}
+    ''';
+
+    // Gets the expected range that follows the string [prefix].
+    SourceRange rangeAfter(String prefix) =>
+        rangeAfterPrefix(prefix, otherContents, 'Foo');
+
+    addSource(otherFile, otherContents);
+    final calls = await findIncomingCalls(contents);
+    expect(
+      calls,
+      unorderedEquals([
+        _isResult(CallHierarchyKind.file, 'other.dart', otherFile, startOfFile,
+            ranges: [
+              rangeAfter('foo1 = '),
+            ]),
+        _isResult(CallHierarchyKind.class_, 'Bar', otherFile,
+            rangeAtSearch('Bar {', otherContents, 'Bar'),
+            ranges: [
+              rangeAfter('foo2 = '),
+            ]),
+        _isResult(CallHierarchyKind.property, 'foo3', otherFile,
+            rangeAtSearch('foo3', otherContents),
+            ranges: [
+              rangeAfter('foo3 => '),
+            ]),
+        _isResult(CallHierarchyKind.constructor, 'Bar', otherFile,
+            rangeAtSearch('Bar() {', otherContents, 'Bar'),
+            ranges: [
+              rangeAfter('foo4 = '),
+            ]),
+        _isResult(CallHierarchyKind.method, 'bar', otherFile,
+            rangeAtSearch('bar() {', otherContents, 'bar'),
+            ranges: [
+              rangeAfter('foo5 = '),
+              rangeAfter('foo6 = '),
+            ]),
+      ]),
+    );
+  }
+
+  Future<void> test_extension_method() async {
+    final contents = '''
+extension StringExtension on String {
+  void myMet^hod() {}
+}
+    ''';
+
+    final otherContents = '''
+import 'test.dart';
+
+void f() {
+  ''.myMethod();
+}
+    ''';
+
+    // Gets the expected range that follows the string [prefix].
+    SourceRange rangeAfter(String prefix) =>
+        rangeAfterPrefix(prefix, otherContents, 'myMethod');
+
+    addSource(otherFile, otherContents);
+    final calls = await findIncomingCalls(contents);
+    expect(
+      calls,
+      unorderedEquals([
+        _isResult(CallHierarchyKind.function, 'f', otherFile,
+            rangeAtSearch('f() {', otherContents, 'f'),
+            ranges: [
+              rangeAfter("''."),
+            ]),
+      ]),
+    );
+  }
+
+  Future<void> test_function() async {
+    final contents = '''
+String myFun^ction() => '';
+    ''';
+
+    final otherContents = '''
+import 'test.dart';
+
+final foo1 = myFunction();
+
+class Bar {
+  final foo2 = myFunction();
+  String get foo3 => myFunction();
+  Bar() {
+    final foo4 = myFunction();
+  }
+  void bar() {
+    final foo5 = myFunction();
+    final foo6 = myFunction();
+  }
+}
+    ''';
+
+    // Gets the expected range that follows the string [prefix].
+    SourceRange rangeAfter(String prefix) =>
+        rangeAfterPrefix(prefix, otherContents, 'myFunction');
+
+    addSource(otherFile, otherContents);
+    final calls = await findIncomingCalls(contents);
+    expect(
+      calls,
+      unorderedEquals([
+        _isResult(CallHierarchyKind.file, 'other.dart', otherFile, startOfFile,
+            ranges: [
+              rangeAfter('foo1 = '),
+            ]),
+        _isResult(CallHierarchyKind.class_, 'Bar', otherFile,
+            rangeAtSearch('Bar {', otherContents, 'Bar'),
+            ranges: [
+              rangeAfter('foo2 = '),
+            ]),
+        _isResult(CallHierarchyKind.property, 'foo3', otherFile,
+            rangeAtSearch('foo3', otherContents),
+            ranges: [
+              rangeAfter('foo3 => '),
+            ]),
+        _isResult(CallHierarchyKind.constructor, 'Bar', otherFile,
+            rangeAtSearch('Bar() {', otherContents, 'Bar'),
+            ranges: [
+              rangeAfter('foo4 = '),
+            ]),
+        _isResult(CallHierarchyKind.method, 'bar', otherFile,
+            rangeAtSearch('bar() {', otherContents, 'bar'),
+            ranges: [
+              rangeAfter('foo5 = '),
+              rangeAfter('foo6 = '),
+            ]),
+      ]),
+    );
+  }
+
+  Future<void> test_getter() async {
+    final contents = '''
+String get f^oo => '';
+    ''';
+
+    final otherContents = '''
+import 'test.dart';
+
+final foo1 = foo;
+class Bar {
+  final foo2 = foo;
+  Foo get foo3 => foo;
+  Bar() {
+    final foo4 = foo;
+  }
+  void bar() {
+    final foo5 = foo;
+    final foo6 = foo;
+  }
+}
+    ''';
+
+    // Gets the expected range that follows the string [prefix].
+    SourceRange rangeAfter(String prefix) =>
+        rangeAfterPrefix(prefix, otherContents, 'foo');
+
+    addSource(otherFile, otherContents);
+    final calls = await findIncomingCalls(contents);
+    expect(
+      calls,
+      unorderedEquals([
+        _isResult(CallHierarchyKind.file, 'other.dart', otherFile, startOfFile,
+            ranges: [
+              rangeAfter('foo1 = '),
+            ]),
+        _isResult(CallHierarchyKind.class_, 'Bar', otherFile,
+            rangeAtSearch('Bar {', otherContents, 'Bar'),
+            ranges: [
+              rangeAfter('foo2 = '),
+            ]),
+        _isResult(CallHierarchyKind.property, 'foo3', otherFile,
+            rangeAtSearch('foo3', otherContents),
+            ranges: [
+              rangeAfter('foo3 => '),
+            ]),
+        _isResult(CallHierarchyKind.constructor, 'Bar', otherFile,
+            rangeAtSearch('Bar() {', otherContents, 'Bar'),
+            ranges: [
+              rangeAfter('foo4 = '),
+            ]),
+        _isResult(CallHierarchyKind.method, 'bar', otherFile,
+            rangeAtSearch('bar() {', otherContents, 'bar'),
+            ranges: [
+              rangeAfter('foo5 = '),
+              rangeAfter('foo6 = '),
+            ]),
+      ]),
+    );
+  }
+
+  Future<void> test_implicitConstructor() async {
+    // We still expect to be able to navigate with implicit constructors. This
+    // is done by the target being the class, but with a kind of Constructor.
+    final contents = '''
+// ignore_for_file: unused_local_variable
+import 'other.dart';
+
+f() {
+  final foo1 = Fo^o();
+}
+    ''';
+
+    final otherContents = '''
+class Foo {}
+
+final foo2 = Foo();
+    ''';
+
+    // Gets the expected range that follows the string [prefix].
+    SourceRange rangeAfter(String prefix, String code) =>
+        rangeAfterPrefix(prefix, code, 'Foo');
+
+    addSource(otherFile, otherContents);
+    final calls = await findIncomingCalls(contents);
+    expect(
+      calls,
+      unorderedEquals([
+        _isResult(CallHierarchyKind.function, 'f', testFile,
+            rangeAtSearch('f() {', contents, 'f'),
+            ranges: [
+              rangeAfter('foo1 = ', contents),
+            ]),
+        _isResult(CallHierarchyKind.file, 'other.dart', otherFile, startOfFile,
+            ranges: [
+              rangeAfter('foo2 = ', otherContents),
+            ]),
+      ]),
+    );
+  }
+
+  Future<void> test_method() async {
+    final contents = '''
+class Foo {
+  void myMet^hod() {}
+}
+    ''';
+
+    final otherContents = '''
+import 'test.dart';
+
+void f() {
+  Foo().myMethod();
+  final tearoff = Foo().myMethod;
+}
+    ''';
+
+    // Gets the expected range that follows the string [prefix].
+    SourceRange rangeAfter(String prefix) =>
+        rangeAfterPrefix(prefix, otherContents, 'myMethod');
+
+    addSource(otherFile, otherContents);
+    final calls = await findIncomingCalls(contents);
+    expect(
+      calls,
+      unorderedEquals([
+        _isResult(CallHierarchyKind.function, 'f', otherFile,
+            rangeAtSearch('f() {', otherContents, 'f'),
+            ranges: [
+              rangeAfter('Foo().'),
+              rangeAfter('tearoff = Foo().'),
+            ]),
+      ]),
+    );
+  }
+
+  Future<void> test_mixin_method() async {
+    final contents = '''
+mixin Bar {
+  void myMet^hod() {}
+}
+
+class Foo with Bar {}
+    ''';
+
+    final otherContents = '''
+import 'test.dart';
+
+void f() {
+  Foo().myMethod();
+}
+    ''';
+
+    // Gets the expected range that follows the string [prefix].
+    SourceRange rangeAfter(String prefix) =>
+        rangeAfterPrefix(prefix, otherContents, 'myMethod');
+
+    addSource(otherFile, otherContents);
+    final calls = await findIncomingCalls(contents);
+    expect(
+      calls,
+      unorderedEquals([
+        _isResult(CallHierarchyKind.function, 'f', otherFile,
+            rangeAtSearch('f() {', otherContents, 'f'),
+            ranges: [
+              rangeAfter('Foo().'),
+            ]),
+      ]),
+    );
+  }
+
+  Future<void> test_namedConstructor() async {
+    final contents = '''
+class Foo {
+  Foo.B^ar();
+}
+    ''';
+
+    final otherContents = '''
+import 'test.dart';
+
+f() {
+  final foo = Foo.Bar();
+}
+    ''';
+
+    // Gets the expected range that follows the string [prefix].
+    SourceRange rangeAfter(String prefix) =>
+        rangeAfterPrefix(prefix, otherContents, 'Bar');
+
+    addSource(otherFile, otherContents);
+    final calls = await findIncomingCalls(contents);
+    expect(
+      calls,
+      unorderedEquals([
+        _isResult(CallHierarchyKind.function, 'f', otherFile,
+            rangeAtSearch('f() {', otherContents, 'f'),
+            ranges: [
+              rangeAfter('foo = Foo.'),
+            ]),
+      ]),
+    );
+  }
+
+  Future<void> test_setter() async {
+    final contents = '''
+set fo^o(String value) {}
+    ''';
+
+    final otherContents = '''
+import 'test.dart';
+
+class Bar {
+  Bar() {
+    /*1*/foo = '';
+  }
+  void bar() {
+    /*2*/foo = '';
+    /*3*/foo = '';
+  }
+}
+    ''';
+
+    // Gets the expected range that follows the string [prefix].
+    SourceRange rangeAfter(String prefix) =>
+        rangeAfterPrefix(prefix, otherContents, 'foo');
+
+    addSource(otherFile, otherContents);
+    final calls = await findIncomingCalls(contents);
+    expect(
+      calls,
+      unorderedEquals([
+        _isResult(CallHierarchyKind.constructor, 'Bar', otherFile,
+            rangeAtSearch('Bar() {', otherContents, 'Bar'),
+            ranges: [
+              rangeAfter('/*1*/'),
+            ]),
+        _isResult(CallHierarchyKind.method, 'bar', otherFile,
+            rangeAtSearch('bar() {', otherContents, 'bar'),
+            ranges: [
+              rangeAfter('/*2*/'),
+              rangeAfter('/*3*/'),
+            ]),
+      ]),
+    );
+  }
+}
+
+@reflectiveTest
+class CallHierarchyComputerOutgoingCallsTest extends AbstractCallHierarchyTest {
+  late String otherFile;
+
+  Future<List<CallHierarchyCalls>> findOutgoingCalls(String code) async {
+    final marker = code.indexOf('^');
+    expect(marker, greaterThanOrEqualTo(0));
+    addTestSource(withoutMarkers(code));
+    final session_ = await session;
+
+    var result = await session_.getResolvedUnit(testFile) as ResolvedUnitResult;
+    expect(result.errors, isEmpty);
+    var computer = DartCallHierarchyComputer(result);
+    // It's possible that the target is in a different file (because we were
+    // invoked on a call and not a declaration), so fetch the resolved unit
+    // for the target.
+    final target = computer.findTarget(marker)!;
+    if (target.file != testFile) {
+      result =
+          await session_.getResolvedUnit(target.file) as ResolvedUnitResult;
+      expect(result.errors, isEmpty);
+      computer = DartCallHierarchyComputer(result);
+    }
+    return computer.findOutgoingCalls(target);
+  }
+
+  @override
+  void setUp() {
+    super.setUp();
+    otherFile = convertPath('$testPackageLibPath/other.dart');
+  }
+
+  Future<void> test_constructor() async {
+    final contents = '''
+// ignore_for_file: unused_local_variable
+import 'other.dart';
+
+class Foo {
+  Fo^o() {
+    final a = A();
+    final constructorTearoffA = A.new;
+    final b = B();
+    final constructorTearoffB = B.new;
+  }
+}
+    ''';
+
+    final otherContents = '''
+class A {
+  A();
+}
+
+class B {
+}
+    ''';
+
+    addSource(otherFile, otherContents);
+    final calls = await findOutgoingCalls(contents);
+    expect(
+      calls,
+      unorderedEquals([
+        _isResult(CallHierarchyKind.constructor, 'A', otherFile,
+            rangeAtSearch('A();', otherContents, 'A'),
+            ranges: [
+              rangeAtSearch('A()', contents, 'A'),
+              rangeAfterPrefix('constructorTearoffA = A.', contents, 'new'),
+            ]),
+        _isResult(CallHierarchyKind.constructor, 'B', otherFile,
+            rangeAtSearch('B {', otherContents, 'B'),
+            ranges: [
+              rangeAtSearch('B()', contents, 'B'),
+              rangeAfterPrefix('constructorTearoffB = B.', contents, 'new'),
+            ]),
+      ]),
+    );
+  }
+
+  Future<void> test_extension_method() async {
+    final contents = '''
+// ignore_for_file: unused_local_variable
+import 'other.dart';
+
+extension StringExtension on String {
+  void fo^o() {
+    ''.bar();
+    final tearoff = ''.bar;
+  }
+}
+    ''';
+
+    final otherContents = '''
+extension StringExtension on String {
+  bar() {}
+}
+    ''';
+
+    addSource(otherFile, otherContents);
+    final calls = await findOutgoingCalls(contents);
+    expect(
+      calls,
+      unorderedEquals([
+        _isResult(CallHierarchyKind.method, 'bar', otherFile,
+            rangeAtSearch('bar() {', otherContents, 'bar'),
+            ranges: [
+              rangeAtSearch('bar();', contents, 'bar'),
+              rangeAtSearch('bar;', contents, 'bar'),
+            ]),
+      ]),
+    );
+  }
+
+  Future<void> test_function() async {
+    final contents = '''
+// ignore_for_file: unused_local_variable
+import 'other.dart';
+
+void fo^o() {
+  void nested() {
+    f(); // not a call of 'foo'
+  }
+  f(); // 1
+  final tearoff = f;
+  nested();
+  final nestedTearoff = nested;
+}
+    ''';
+
+    final otherContents = '''
+void f() {}
+    ''';
+
+    addSource(otherFile, otherContents);
+    final calls = await findOutgoingCalls(contents);
+    expect(
+      calls,
+      unorderedEquals([
+        _isResult(CallHierarchyKind.function, 'f', otherFile,
+            rangeAtSearch('f() {', otherContents, 'f'),
+            ranges: [
+              rangeAtSearch('f(); // 1', contents, 'f'),
+              rangeAfterPrefix('tearoff = ', contents, 'f'),
+            ]),
+        _isResult(CallHierarchyKind.function, 'nested', testFile,
+            rangeAtSearch('nested() {', contents, 'nested'),
+            ranges: [
+              rangeAtSearch('nested();', contents, 'nested'),
+              rangeAfterPrefix('nestedTearoff = ', contents, 'nested'),
+            ]),
+      ]),
+    );
+  }
+
+  Future<void> test_getter() async {
+    final contents = '''
+// ignore_for_file: unused_local_variable
+import 'other.dart';
+
+String get fo^o {
+  final a = A();
+  final b = a.b;
+  final c = A().b;
+  return '';
+}
+    ''';
+
+    final otherContents = '''
+class A {
+  String get b => '';
+}
+    ''';
+
+    addSource(otherFile, otherContents);
+    final calls = await findOutgoingCalls(contents);
+    expect(
+      calls,
+      unorderedEquals([
+        _isResult(CallHierarchyKind.constructor, 'A', otherFile,
+            rangeAtSearch('A {', otherContents, 'A')),
+        _isResult(CallHierarchyKind.property, 'get b', otherFile,
+            rangeAtSearch('b => ', otherContents, 'b'),
+            ranges: [
+              rangeAfterPrefix('a.', contents, 'b'),
+              rangeAfterPrefix('A().', contents, 'b'),
+            ]),
+      ]),
+    );
+  }
+
+  Future<void> test_implicitConstructor() async {
+    // We can still begin navigating from an implicit constructor (so we can
+    // search for inbound calls), so we should ensure that trying to fetch
+    // outbound calls returns empty (and doesn't fail).
+    final contents = '''
+// ignore_for_file: unused_local_variable
+import 'other.dart';
+
+f() {
+  final foo1 = Fo^o();
+}
+    ''';
+
+    final otherContents = '''
+class Foo {}
+    ''';
+
+    addSource(otherFile, otherContents);
+    final calls = await findOutgoingCalls(contents);
+    expect(calls, isEmpty);
+  }
+
+  Future<void> test_method() async {
+    final contents = '''
+// ignore_for_file: unused_local_variable
+import 'other.dart';
+
+class Foo {
+  void fo^o() {
+    final a = A();
+    a.bar();
+    final tearoff = a.bar;
+    // non-calls
+    var x = 1;
+    var y = x;
+    a.field;
+  }
+}
+    ''';
+
+    final otherContents = '''
+class A {
+  String field;
+  void bar() {}
+}
+    ''';
+
+    addSource(otherFile, otherContents);
+    final calls = await findOutgoingCalls(contents);
+    expect(
+      calls,
+      unorderedEquals([
+        _isResult(CallHierarchyKind.constructor, 'A', otherFile,
+            rangeAtSearch('A {', otherContents, 'A')),
+        _isResult(CallHierarchyKind.method, 'bar', otherFile,
+            rangeAtSearch('bar() {', otherContents, 'bar'),
+            ranges: [
+              rangeAfterPrefix('a.', contents, 'bar'),
+              rangeAfterPrefix('tearoff = a.', contents, 'bar'),
+            ]),
+      ]),
+    );
+  }
+
+  Future<void> test_mixin_method() async {
+    final contents = '''
+// ignore_for_file: unused_local_variable
+import 'other.dart';
+
+mixin MyMixin {
+  void f^() {
+    final a = A();
+    a.foo();
+    A().foo();
+    final tearoff = a.foo;
+  }
+}
+    ''';
+
+    final otherContents = '''
+mixin OtherMixin {
+  void foo() {}
+}
+
+class A with OtherMixin {}
+    ''';
+
+    addSource(otherFile, otherContents);
+    final calls = await findOutgoingCalls(contents);
+    expect(
+      calls,
+      unorderedEquals([
+        _isResult(CallHierarchyKind.constructor, 'A', otherFile,
+            rangeAtSearch('A with', otherContents, 'A')),
+        _isResult(CallHierarchyKind.method, 'foo', otherFile,
+            rangeAtSearch('foo() {', otherContents, 'foo'),
+            ranges: [
+              rangeAfterPrefix('a.', contents, 'foo'),
+              rangeAfterPrefix('A().', contents, 'foo'),
+              rangeAfterPrefix('tearoff = a.', contents, 'foo'),
+            ]),
+      ]),
+    );
+  }
+
+  Future<void> test_namedConstructor() async {
+    final contents = '''
+// ignore_for_file: unused_local_variable
+import 'other.dart';
+
+class Foo {
+  Foo.B^ar() {
+    final a = A.named();
+    final constructorTearoff = A.named;
+  }
+}
+    ''';
+
+    final otherContents = '''
+void f() {}
+class A {
+  A.named();
+}
+    ''';
+
+    addSource(otherFile, otherContents);
+    final calls = await findOutgoingCalls(contents);
+    expect(
+      calls,
+      unorderedEquals([
+        _isResult(CallHierarchyKind.constructor, 'A.named', otherFile,
+            rangeAtSearch('named', otherContents),
+            ranges: [
+              rangeAfterPrefix('a = A.', contents, 'named'),
+              rangeAfterPrefix('constructorTearoff = A.', contents, 'named'),
+            ]),
+      ]),
+    );
+  }
+
+  Future<void> test_setter() async {
+    final contents = '''
+import 'other.dart';
+
+set fo^o(String value) {
+  final a = A();
+  a.b = '';
+  A().b = '';
+}
+    ''';
+
+    final otherContents = '''
+class A {
+  set b(String value) {}
+}
+    ''';
+
+    addSource(otherFile, otherContents);
+    final calls = await findOutgoingCalls(contents);
+    expect(
+      calls,
+      unorderedEquals([
+        _isResult(CallHierarchyKind.constructor, 'A', otherFile,
+            rangeAtSearch('A {', otherContents, 'A')),
+        _isResult(CallHierarchyKind.property, 'set b', otherFile,
+            rangeAtSearch('b(String ', otherContents, 'b'),
+            ranges: [
+              rangeAfterPrefix('a.', contents, 'b'),
+              rangeAfterPrefix('A().', contents, 'b'),
+            ]),
+      ]),
+    );
+  }
+}
diff --git a/pkg/analysis_server/test/src/computer/test_all.dart b/pkg/analysis_server/test/src/computer/test_all.dart
index af15c51..65087cc 100644
--- a/pkg/analysis_server/test/src/computer/test_all.dart
+++ b/pkg/analysis_server/test/src/computer/test_all.dart
@@ -4,6 +4,7 @@
 
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
+import 'call_hierarchy_computer_test.dart' as call_hierarchy_computer;
 import 'closing_labels_computer_test.dart' as closing_labels_computer;
 import 'color_computer_test.dart' as color_computer;
 import 'folding_computer_test.dart' as folding_computer;
@@ -15,6 +16,7 @@
 
 void main() {
   defineReflectiveSuite(() {
+    call_hierarchy_computer.main();
     closing_labels_computer.main();
     color_computer.main();
     folding_computer.main();
diff --git a/pkg/analysis_server/test/src/domains/completion/available_suggestion_sets_test.dart b/pkg/analysis_server/test/src/domains/completion/available_suggestion_sets_test.dart
index 15124db..3ab015e 100644
--- a/pkg/analysis_server/test/src/domains/completion/available_suggestion_sets_test.dart
+++ b/pkg/analysis_server/test/src/domains/completion/available_suggestion_sets_test.dart
@@ -45,7 +45,7 @@
 
     // Delete the file, the set should be removed.
     deleteFile(path);
-    waitForSetWithUriRemoved(uriStr);
+    await waitForSetWithUriRemoved(uriStr);
   }
 
   Future<void> test_suggestion_class() async {
diff --git a/pkg/analysis_server/test/src/services/correction/assist/assign_to_local_variable_test.dart b/pkg/analysis_server/test/src/services/correction/assist/assign_to_local_variable_test.dart
index 3751d7f..c544b19 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/assign_to_local_variable_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/assign_to_local_variable_test.dart
@@ -102,7 +102,7 @@
   final data['table'][] //marker
 }
 ''');
-    assertNoAssistAt('] //marker');
+    await assertNoAssistAt('] //marker');
   }
 
   Future<void> test_throw() async {
diff --git a/pkg/analysis_server/test/src/services/correction/fix/create_field_test.dart b/pkg/analysis_server/test/src/services/correction/fix/create_field_test.dart
index af26bc4..6f64621 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/create_field_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/create_field_test.dart
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analysis_server/src/services/correction/fix.dart';
-import 'package:analyzer/src/error/codes.dart';
 import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -364,25 +363,6 @@
     await assertNoFix();
   }
 
-  Future<void> test_inPart_imported() async {
-    addSource('$testPackageLibPath/a.dart', '''
-part of lib;
-class A {}
-''');
-
-    await resolveTestCode('''
-import 'package:test/a.dart';
-
-void f(A a) {
-  int v = a.test;
-  print(v);
-}
-''');
-    await assertNoFix(errorFilter: (e) {
-      return e.errorCode == CompileTimeErrorCode.UNDEFINED_GETTER;
-    });
-  }
-
   Future<void> test_inPart_self() async {
     await resolveTestCode('''
 part of lib;
diff --git a/pkg/analysis_server/test/src/services/correction/fix/create_getter_test.dart b/pkg/analysis_server/test/src/services/correction/fix/create_getter_test.dart
index 7566168..ed0884c 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/create_getter_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/create_getter_test.dart
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analysis_server/src/services/correction/fix.dart';
-import 'package:analyzer/src/error/codes.dart';
 import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -306,26 +305,6 @@
 ''');
   }
 
-  Future<void> test_qualified_instance_inPart_imported() async {
-    addSource('$testPackageLibPath/a.dart', '''
-part of lib;
-
-class A {}
-''');
-
-    await resolveTestCode('''
-import 'package:test/a.dart';
-
-void f(A a) {
-  int v = a.test;
-  print(v);
-}
-''');
-    await assertNoFix(errorFilter: (e) {
-      return e.errorCode == CompileTimeErrorCode.UNDEFINED_GETTER;
-    });
-  }
-
   Future<void> test_qualified_instance_inPart_self() async {
     await resolveTestCode('''
 part of lib;
diff --git a/pkg/analysis_server/test/src/services/correction/fix/create_setter_test.dart b/pkg/analysis_server/test/src/services/correction/fix/create_setter_test.dart
index fea7674..930c68e 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/create_setter_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/create_setter_test.dart
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analysis_server/src/services/correction/fix.dart';
-import 'package:analyzer/src/error/codes.dart';
 import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -301,25 +300,6 @@
 ''');
   }
 
-  Future<void> test_qualified_instance_inPart_imported() async {
-    addSource('$testPackageLibPath/a.dart', '''
-part of lib;
-
-class A {}
-''');
-
-    await resolveTestCode('''
-import 'package:test/a.dart';
-
-void f(A a) {
-  a.test = 0;
-}
-''');
-    await assertNoFix(errorFilter: (e) {
-      return e.errorCode == CompileTimeErrorCode.UNDEFINED_SETTER;
-    });
-  }
-
   Future<void> test_qualified_instance_inPart_self() async {
     await resolveTestCode('''
 part of lib;
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/sdk_fix_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/sdk_fix_test.dart
index 181cf6d..fb75558 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/sdk_fix_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/sdk_fix_test.dart
@@ -87,6 +87,6 @@
     await resolveTestCode('''
 var x = '';
 ''');
-    assertNoExceptions();
+    await assertNoExceptions();
   }
 }
diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_empty_catch_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_empty_catch_test.dart
index 1e5bf88..a7b3898 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/remove_empty_catch_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/remove_empty_catch_test.dart
@@ -67,7 +67,7 @@
   } catch 2;
 }
 ''');
-    assertNoExceptions();
+    await assertNoExceptions();
   }
 
   Future<void> test_singleCatch_finally_newLine() async {
diff --git a/pkg/analysis_server/test/stress/utilities/server.dart b/pkg/analysis_server/test/stress/utilities/server.dart
index 93daac0..1e0a635 100644
--- a/pkg/analysis_server/test/stress/utilities/server.dart
+++ b/pkg/analysis_server/test/stress/utilities/server.dart
@@ -572,11 +572,11 @@
 //    stdout.writeln('Launching $serverPath');
 //    stdout.writeln('$dartBinary ${arguments.join(' ')}');
     _process = await Process.start(dartBinary, arguments);
-    _process!.exitCode.then((int code) {
+    unawaited(_process!.exitCode.then((int code) {
       if (code != 0) {
         throw StateError('Server terminated with exit code $code');
       }
-    });
+    }));
     _listenToOutput();
     var completer = Completer<void>();
     _serverConnectedCompleter = completer;
diff --git a/pkg/analysis_server/tool/code_completion/completion_metrics_client.dart b/pkg/analysis_server/tool/code_completion/completion_metrics_client.dart
index 0fa0e39..dc94890 100644
--- a/pkg/analysis_server/tool/code_completion/completion_metrics_client.dart
+++ b/pkg/analysis_server/tool/code_completion/completion_metrics_client.dart
@@ -38,7 +38,8 @@
   var options = CompletionMetricsOptions(result);
   var stopwatch = Stopwatch()..start();
   var client = _AnalysisServerClient(Directory(_sdk.sdkPath), targets);
-  _CompletionClientMetricsComputer(rootPath, options, client).computeMetrics();
+  await _CompletionClientMetricsComputer(rootPath, options, client)
+      .computeMetrics();
   stopwatch.stop();
 
   var duration = Duration(milliseconds: stopwatch.elapsedMilliseconds);
@@ -289,7 +290,7 @@
     _process = process;
     _shutdownResponseReceived = false;
     // This callback hookup can't throw.
-    process.exitCode.whenComplete(() {
+    unawaited(process.exitCode.whenComplete(() {
       _process = null;
 
       if (!_shutdownResponseReceived) {
@@ -312,7 +313,7 @@
 
         _onCrash.complete();
       }
-    });
+    }));
 
     final errorStream = process.stderr
         .transform<String>(utf8.decoder)
@@ -326,7 +327,7 @@
 
     _streamController('server.error').stream.listen(_handleServerError);
 
-    _sendCommand('server.setSubscriptions', params: <String, dynamic>{
+    await _sendCommand('server.setSubscriptions', params: <String, dynamic>{
       'subscriptions': <String>['STATUS'],
     });
 
diff --git a/pkg/analyzer/lib/dart/ast/ast.dart b/pkg/analyzer/lib/dart/ast/ast.dart
index 070e789..5b3cd79 100644
--- a/pkg/analyzer/lib/dart/ast/ast.dart
+++ b/pkg/analyzer/lib/dart/ast/ast.dart
@@ -2877,9 +2877,14 @@
   /// imported URI is not deferred.
   Token? get deferredKeyword;
 
+  @Deprecated('Use element2 instead')
   @override
   ImportElement? get element;
 
+  /// Return the element associated with this directive, or `null` if the AST
+  /// structure has not been resolved.
+  ImportElement2? get element2;
+
   /// The token representing the 'import' keyword.
   Token get importKeyword;
 
diff --git a/pkg/analyzer/lib/dart/element/element.dart b/pkg/analyzer/lib/dart/element/element.dart
index 22fae35..8d6f524 100644
--- a/pkg/analyzer/lib/dart/element/element.dart
+++ b/pkg/analyzer/lib/dart/element/element.dart
@@ -504,6 +504,11 @@
   InterfaceType get returnType;
 }
 
+/// [ImportElementPrefix] that is used together with `deferred`.
+///
+/// Clients may not extend, implement or mix-in this class.
+abstract class DeferredImportElementPrefix implements ImportElementPrefix {}
+
 /// Meaning of a URI referenced in a directive.
 ///
 /// Clients may not extend, implement or mix-in this class.
@@ -1099,8 +1104,11 @@
 
   R? visitGenericFunctionTypeElement(GenericFunctionTypeElement element);
 
+  @Deprecated('Override visitImportElement2() instead')
   R? visitImportElement(ImportElement element);
 
+  R? visitImportElement2(ImportElement2 element);
+
   R? visitLabelElement(LabelElement element);
 
   R? visitLibraryAugmentationElement(LibraryAugmentationElement element);
@@ -1342,6 +1350,7 @@
 /// A single import directive within a library.
 ///
 /// Clients may not extend, implement or mix-in this class.
+@Deprecated('Use ImportElement2 instead')
 abstract class ImportElement implements UriReferencedElement {
   /// Return a list containing the combinators that were specified as part of
   /// the import directive in the order in which they were specified.
@@ -1362,6 +1371,40 @@
   PrefixElement? get prefix;
 }
 
+/// A single import directive within a library.
+///
+/// Clients may not extend, implement or mix-in this class.
+abstract class ImportElement2 implements _ExistingElement {
+  /// Return a list containing the combinators that were specified as part of
+  /// the import directive in the order in which they were specified.
+  List<NamespaceCombinator> get combinators;
+
+  /// Returns the [LibraryElement], if [uri] is a [DirectiveUriWithLibrary].
+  LibraryElement? get importedLibrary;
+
+  /// The offset of the `import` keyword.
+  int get importKeywordOffset;
+
+  /// The [Namespace] that this directive contributes to the containing library.
+  Namespace get namespace;
+
+  /// Return the prefix that was specified as part of the import directive, or
+  /// `null` if there was no prefix specified.
+  ImportElementPrefix? get prefix;
+
+  /// The interpretation of the URI specified in the directive.
+  DirectiveUri get uri;
+}
+
+/// Usage of a [PrefixElement] in an `import` directive.
+///
+/// Clients may not extend, implement or mix-in this class.
+abstract class ImportElementPrefix {
+  /// Return the prefix that was specified as part of the import directive, or
+  /// `null` if there was no prefix specified.
+  PrefixElement get element;
+}
+
 /// A label associated with a statement.
 ///
 /// Clients may not extend, implement or mix-in this class.
@@ -1516,8 +1559,12 @@
   FeatureSet get featureSet;
 
   /// Return a list containing all of the imports defined in this library.
+  @Deprecated('Use imports2 instead')
   List<ImportElement> get imports;
 
+  /// Return a list containing all of the imports defined in this library.
+  List<ImportElement2> get imports2;
+
   bool get isNonNullableByDefault;
 
   /// The language version for this library.
@@ -1726,8 +1773,12 @@
   LibraryOrAugmentationElement get enclosingElement2;
 
   /// Return the imports that share this prefix.
+  @Deprecated('Use imports2 instead')
   List<ImportElement> get imports;
 
+  /// Return the imports that share this prefix.
+  List<ImportElement2> get imports2;
+
   @override
   String get name;
 
diff --git a/pkg/analyzer/lib/dart/element/visitor.dart b/pkg/analyzer/lib/dart/element/visitor.dart
index 86e4839..4735d6f 100644
--- a/pkg/analyzer/lib/dart/element/visitor.dart
+++ b/pkg/analyzer/lib/dart/element/visitor.dart
@@ -126,10 +126,14 @@
   R? visitGenericFunctionTypeElement(GenericFunctionTypeElement element) =>
       visitElement(element);
 
+  @Deprecated('Override visitImportElement2() instead')
   @override
   R? visitImportElement(ImportElement element) => visitElement(element);
 
   @override
+  R? visitImportElement2(ImportElement2 element) => visitElement(element);
+
+  @override
   R? visitLabelElement(LabelElement element) => visitElement(element);
 
   @override
@@ -272,6 +276,7 @@
     return null;
   }
 
+  @Deprecated('Override visitImportElement2() instead')
   @override
   R? visitImportElement(ImportElement element) {
     element.visitChildren(this);
@@ -279,6 +284,12 @@
   }
 
   @override
+  R? visitImportElement2(ImportElement2 element) {
+    element.visitChildren(this);
+    return null;
+  }
+
+  @override
   R? visitLabelElement(LabelElement element) {
     element.visitChildren(this);
     return null;
@@ -405,10 +416,14 @@
   R? visitGenericFunctionTypeElement(GenericFunctionTypeElement element) =>
       null;
 
+  @Deprecated('Override visitImportElement2() instead')
   @override
   R? visitImportElement(ImportElement element) => null;
 
   @override
+  R? visitImportElement2(ImportElement2 element) => null;
+
+  @override
   R? visitLabelElement(LabelElement element) => null;
 
   @override
@@ -498,10 +513,14 @@
   R? visitGenericFunctionTypeElement(GenericFunctionTypeElement element) =>
       _throw(element);
 
+  @Deprecated('Override visitImportElement2() instead')
   @override
   R? visitImportElement(ImportElement element) => _throw(element);
 
   @override
+  R? visitImportElement2(ImportElement2 element) => _throw(element);
+
+  @override
   R? visitLabelElement(LabelElement element) => _throw(element);
 
   @override
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index a55028c..3058d67 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -85,7 +85,7 @@
 /// TODO(scheglov) Clean up the list of implicitly analyzed files.
 class AnalysisDriver implements AnalysisDriverGeneric {
   /// The version of data format, should be incremented on every format change.
-  static const int DATA_VERSION = 229;
+  static const int DATA_VERSION = 230;
 
   /// The number of exception contexts allowed to write. Once this field is
   /// zero, we stop writing any new exception contexts in this process.
diff --git a/pkg/analyzer/lib/src/dart/analysis/file_state.dart b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
index 4675824..8ec05f7 100644
--- a/pkg/analyzer/lib/src/dart/analysis/file_state.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
@@ -212,7 +212,7 @@
 
 /// Information about a single `export` directive.
 class ExportDirectiveState<U extends DirectiveUri> extends DirectiveState {
-  final UnlinkedNamespaceDirective directive;
+  final UnlinkedExportDirective directive;
   final U selectedUri;
   final NamespaceDirectiveUris uris;
 
@@ -927,14 +927,14 @@
     UnlinkedPartOfNameDirective? partOfNameDirective;
     UnlinkedPartOfUriDirective? partOfUriDirective;
     var augmentations = <UnlinkedImportAugmentationDirective>[];
-    var exports = <UnlinkedNamespaceDirective>[];
-    var imports = <UnlinkedNamespaceDirective>[];
+    var exports = <UnlinkedExportDirective>[];
+    var imports = <UnlinkedImportDirective>[];
     var parts = <UnlinkedPartDirective>[];
     var macroClasses = <MacroClass>[];
     var hasDartCoreImport = false;
     for (var directive in unit.directives) {
       if (directive is ExportDirective) {
-        var builder = _serializeNamespaceDirective(directive);
+        var builder = _serializeExport(directive);
         exports.add(builder);
       } else if (directive is AugmentationImportDirectiveImpl) {
         augmentations.add(
@@ -943,7 +943,7 @@
           ),
         );
       } else if (directive is ImportDirectiveImpl) {
-        var builder = _serializeNamespaceDirective(directive);
+        var builder = _serializeImport(directive);
         imports.add(builder);
         if (builder.uri == 'dart:core') {
           hasDartCoreImport = true;
@@ -1016,9 +1016,12 @@
     }
     if (!isDartCore && !hasDartCoreImport) {
       imports.add(
-        UnlinkedNamespaceDirective(
+        UnlinkedImportDirective(
+          combinators: [],
           configurations: [],
-          isSyntheticDartCoreImport: true,
+          importKeywordOffset: -1,
+          isSyntheticDartCore: true,
+          prefix: null,
           uri: 'dart:core',
         ),
       );
@@ -1081,19 +1084,69 @@
     return true;
   }
 
-  static UnlinkedNamespaceDirective _serializeNamespaceDirective(
-      NamespaceDirective directive) {
-    return UnlinkedNamespaceDirective(
-      configurations: directive.configurations.map((configuration) {
-        var name = configuration.name.components.join('.');
-        var value = configuration.value?.stringValue ?? '';
-        return UnlinkedNamespaceDirectiveConfiguration(
-          name: name,
-          value: value,
-          uri: configuration.uri.stringValue,
+  static List<UnlinkedCombinator> _serializeCombinators(
+    List<Combinator> combinators,
+  ) {
+    return combinators.map((combinator) {
+      if (combinator is ShowCombinator) {
+        return UnlinkedCombinator(
+          keywordOffset: combinator.keyword.offset,
+          endOffset: combinator.end,
+          isShow: true,
+          names: combinator.shownNames.map((e) => e.name).toList(),
         );
-      }).toList(),
-      uri: directive.uri.stringValue,
+      } else {
+        combinator as HideCombinator;
+        return UnlinkedCombinator(
+          keywordOffset: combinator.keyword.offset,
+          endOffset: combinator.end,
+          isShow: false,
+          names: combinator.hiddenNames.map((e) => e.name).toList(),
+        );
+      }
+    }).toList();
+  }
+
+  static List<UnlinkedNamespaceDirectiveConfiguration> _serializeConfigurations(
+    List<Configuration> configurations,
+  ) {
+    return configurations.map((configuration) {
+      var name = configuration.name.components.join('.');
+      var value = configuration.value?.stringValue ?? '';
+      return UnlinkedNamespaceDirectiveConfiguration(
+        name: name,
+        value: value,
+        uri: configuration.uri.stringValue,
+      );
+    }).toList();
+  }
+
+  static UnlinkedExportDirective _serializeExport(ExportDirective node) {
+    return UnlinkedExportDirective(
+      combinators: _serializeCombinators(node.combinators),
+      configurations: _serializeConfigurations(node.configurations),
+      uri: node.uri.stringValue,
+    );
+  }
+
+  static UnlinkedImportDirective _serializeImport(ImportDirective node) {
+    UnlinkedImportDirectivePrefix? unlinkedPrefix;
+    final prefix = node.prefix;
+    if (prefix != null) {
+      unlinkedPrefix = UnlinkedImportDirectivePrefix(
+        deferredOffset: node.deferredKeyword?.offset,
+        asOffset: node.asKeyword!.offset,
+        name: prefix.name,
+        nameOffset: prefix.offset,
+      );
+    }
+
+    return UnlinkedImportDirective(
+      combinators: _serializeCombinators(node.combinators),
+      configurations: _serializeConfigurations(node.configurations),
+      importKeywordOffset: node.importKeyword.offset,
+      uri: node.uri.stringValue,
+      prefix: unlinkedPrefix,
     );
   }
 }
@@ -1651,7 +1704,7 @@
 
 /// Information about a single `import` directive.
 class ImportDirectiveState<U extends DirectiveUri> extends DirectiveState {
-  final UnlinkedNamespaceDirective directive;
+  final UnlinkedImportDirective directive;
   final U selectedUri;
   final NamespaceDirectiveUris uris;
 
@@ -1671,7 +1724,7 @@
   /// into a [Source].
   Source? get importedSource => null;
 
-  bool get isSyntheticDartCoreImport => directive.isSyntheticDartCoreImport;
+  bool get isSyntheticDartCore => directive.isSyntheticDartCore;
 }
 
 /// [ImportDirectiveWithUri] that has a valid URI that references a file.
diff --git a/pkg/analyzer/lib/src/dart/analysis/index.dart b/pkg/analyzer/lib/src/dart/analysis/index.dart
index 1a47eb1..695b5cd 100644
--- a/pkg/analyzer/lib/src/dart/analysis/index.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/index.dart
@@ -730,7 +730,7 @@
 
   @override
   void visitImportDirective(ImportDirective node) {
-    ImportElement? element = node.element;
+    final element = node.element2;
     recordUriReference(element?.importedLibrary, node.uri);
     super.visitImportDirective(node);
   }
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
index 2fc3001..fbbea18 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
@@ -582,7 +582,7 @@
       } else if (directive is ImportDirectiveImpl) {
         _resolveImportDirective(
           directive: directive,
-          importElement: _libraryElement.imports[importIndex],
+          importElement: _libraryElement.imports2[importIndex],
           importState: _library.imports[importIndex],
           libraryErrorReporter: libraryErrorReporter,
         );
@@ -715,12 +715,12 @@
 
   void _resolveImportDirective({
     required ImportDirectiveImpl directive,
-    required ImportElement importElement,
+    required ImportElement2 importElement,
     required ImportDirectiveState importState,
     required ErrorReporter libraryErrorReporter,
   }) {
     directive.element = importElement;
-    directive.prefix?.staticElement = importElement.prefix;
+    directive.prefix?.staticElement = importElement.prefix?.element;
     _resolveNamespaceDirective(
       directive: directive,
       primaryUriNode: directive.uri,
diff --git a/pkg/analyzer/lib/src/dart/analysis/search.dart b/pkg/analyzer/lib/src/dart/analysis/search.dart
index 657f117..51c397e 100644
--- a/pkg/analyzer/lib/src/dart/analysis/search.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/search.dart
@@ -130,13 +130,13 @@
 class ImportElementReferencesVisitor extends RecursiveAstVisitor<void> {
   final List<SearchResult> results = <SearchResult>[];
 
-  final ImportElement importElement;
+  final ImportElement2 importElement;
   final CompilationUnitElement enclosingUnitElement;
 
   late final Set<Element> importedElements;
 
   ImportElementReferencesVisitor(
-      ImportElement element, this.enclosingUnitElement)
+      ImportElement2 element, this.enclosingUnitElement)
       : importElement = element {
     importedElements = element.namespace.definedNames.values.toSet();
   }
@@ -153,7 +153,7 @@
       return;
     }
     if (importElement.prefix != null) {
-      if (node.staticElement == importElement.prefix) {
+      if (node.staticElement == importElement.prefix?.element) {
         var parent = node.parent;
         if (parent is PrefixedIdentifier && parent.prefix == node) {
           var element = parent.writeOrReadElement?.declaration;
@@ -262,7 +262,7 @@
             element, (n) => n is Block, searchedFiles);
       }
       return _searchReferences_Function(element, searchedFiles);
-    } else if (element is ImportElement) {
+    } else if (element is ImportElement2) {
       return _searchReferences_Import(element, searchedFiles);
     } else if (kind == ElementKind.LABEL ||
         kind == ElementKind.LOCAL_VARIABLE) {
@@ -556,7 +556,7 @@
   }
 
   Future<List<SearchResult>> _searchReferences_Import(
-      ImportElement element, SearchedFiles searchedFiles) async {
+      ImportElement2 element, SearchedFiles searchedFiles) async {
     String path = element.source.fullName;
     if (!searchedFiles.add(path, this)) {
       return const <SearchResult>[];
diff --git a/pkg/analyzer/lib/src/dart/analysis/unlinked_data.dart b/pkg/analyzer/lib/src/dart/analysis/unlinked_data.dart
index 116cd36..43877b2 100644
--- a/pkg/analyzer/lib/src/dart/analysis/unlinked_data.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/unlinked_data.dart
@@ -90,6 +90,71 @@
   }
 }
 
+class UnlinkedCombinator {
+  final int keywordOffset;
+  final int endOffset;
+  final bool isShow;
+  final List<String> names;
+
+  UnlinkedCombinator({
+    required this.keywordOffset,
+    required this.endOffset,
+    required this.isShow,
+    required this.names,
+  });
+
+  factory UnlinkedCombinator.read(SummaryDataReader reader) {
+    return UnlinkedCombinator(
+      keywordOffset: reader.readUInt30(),
+      endOffset: reader.readUInt30(),
+      isShow: reader.readBool(),
+      names: reader.readStringUtf8List(),
+    );
+  }
+
+  void write(BufferedSink sink) {
+    sink.writeUInt30(keywordOffset);
+    sink.writeUInt30(endOffset);
+    sink.writeBool(isShow);
+    sink.writeStringUtf8Iterable(names);
+  }
+}
+
+/// Unlinked information about an `export` directive.
+class UnlinkedExportDirective extends UnlinkedNamespaceDirective {
+  UnlinkedExportDirective({
+    required super.combinators,
+    required super.configurations,
+    required super.uri,
+  });
+
+  factory UnlinkedExportDirective.read(SummaryDataReader reader) {
+    return UnlinkedExportDirective(
+      combinators: reader.readTypedList(
+        () => UnlinkedCombinator.read(reader),
+      ),
+      configurations: reader.readTypedList(
+        () => UnlinkedNamespaceDirectiveConfiguration.read(reader),
+      ),
+      uri: reader.readOptionalStringUtf8(),
+    );
+  }
+
+  void write(BufferedSink sink) {
+    sink.writeList<UnlinkedCombinator>(
+      combinators,
+      (x) => x.write(sink),
+    );
+    sink.writeList<UnlinkedNamespaceDirectiveConfiguration>(
+      configurations,
+      (x) {
+        x.write(sink);
+      },
+    );
+    sink.writeOptionalStringUtf8(uri);
+  }
+}
+
 class UnlinkedImportAugmentationDirective {
   final String? uri;
 
@@ -110,6 +175,89 @@
   }
 }
 
+/// Unlinked information about an 'import' directive.
+class UnlinkedImportDirective extends UnlinkedNamespaceDirective {
+  final int importKeywordOffset;
+  final bool isSyntheticDartCore;
+  final UnlinkedImportDirectivePrefix? prefix;
+
+  UnlinkedImportDirective({
+    required super.combinators,
+    required super.configurations,
+    required this.importKeywordOffset,
+    this.isSyntheticDartCore = false,
+    required this.prefix,
+    required super.uri,
+  });
+
+  factory UnlinkedImportDirective.read(SummaryDataReader reader) {
+    return UnlinkedImportDirective(
+      combinators: reader.readTypedList(
+        () => UnlinkedCombinator.read(reader),
+      ),
+      configurations: reader.readTypedList(
+        () => UnlinkedNamespaceDirectiveConfiguration.read(reader),
+      ),
+      importKeywordOffset: reader.readUInt30() - 1,
+      isSyntheticDartCore: reader.readBool(),
+      prefix: reader.readOptionalObject(
+        UnlinkedImportDirectivePrefix.read,
+      ),
+      uri: reader.readOptionalStringUtf8(),
+    );
+  }
+
+  void write(BufferedSink sink) {
+    sink.writeList<UnlinkedCombinator>(
+      combinators,
+      (x) => x.write(sink),
+    );
+    sink.writeList<UnlinkedNamespaceDirectiveConfiguration>(
+      configurations,
+      (x) {
+        x.write(sink);
+      },
+    );
+    sink.writeUInt30(1 + importKeywordOffset);
+    sink.writeBool(isSyntheticDartCore);
+    sink.writeOptionalObject<UnlinkedImportDirectivePrefix>(
+      prefix,
+      (x) => x.write(sink),
+    );
+    sink.writeOptionalStringUtf8(uri);
+  }
+}
+
+class UnlinkedImportDirectivePrefix {
+  final int? deferredOffset;
+  final int asOffset;
+  final String name;
+  final int nameOffset;
+
+  UnlinkedImportDirectivePrefix({
+    required this.deferredOffset,
+    required this.asOffset,
+    required this.name,
+    required this.nameOffset,
+  });
+
+  factory UnlinkedImportDirectivePrefix.read(SummaryDataReader reader) {
+    return UnlinkedImportDirectivePrefix(
+      deferredOffset: reader.readOptionalUInt30(),
+      asOffset: reader.readUInt30(),
+      name: reader.readStringUtf8(),
+      nameOffset: reader.readUInt30(),
+    );
+  }
+
+  void write(BufferedSink sink) {
+    sink.writeOptionalUInt30(deferredOffset);
+    sink.writeUInt30(asOffset);
+    sink.writeStringUtf8(name);
+    sink.writeUInt30(nameOffset);
+  }
+}
+
 class UnlinkedLibraryAugmentationDirective {
   final String? uri;
   final UnlinkedSourceRange uriRange;
@@ -154,43 +302,16 @@
   }
 }
 
-/// Unlinked information about a namespace directive.
-class UnlinkedNamespaceDirective {
-  /// The configurations that control which library will actually be used.
+abstract class UnlinkedNamespaceDirective {
+  final List<UnlinkedCombinator> combinators;
   final List<UnlinkedNamespaceDirectiveConfiguration> configurations;
-
-  final bool isSyntheticDartCoreImport;
-
-  /// The URI referenced by this directive, nad used by default when none
-  /// of the [configurations] matches.
   final String? uri;
 
   UnlinkedNamespaceDirective({
+    required this.combinators,
     required this.configurations,
-    this.isSyntheticDartCoreImport = false,
     required this.uri,
   });
-
-  factory UnlinkedNamespaceDirective.read(SummaryDataReader reader) {
-    return UnlinkedNamespaceDirective(
-      configurations: reader.readTypedList(
-        () => UnlinkedNamespaceDirectiveConfiguration.read(reader),
-      ),
-      uri: reader.readOptionalStringUtf8(),
-      isSyntheticDartCoreImport: reader.readBool(),
-    );
-  }
-
-  void write(BufferedSink sink) {
-    sink.writeList<UnlinkedNamespaceDirectiveConfiguration>(
-      configurations,
-      (x) {
-        x.write(sink);
-      },
-    );
-    sink.writeOptionalStringUtf8(uri);
-    sink.writeBool(isSyntheticDartCoreImport);
-  }
 }
 
 /// Unlinked information about a namespace directive configuration.
@@ -342,10 +463,10 @@
   final List<UnlinkedImportAugmentationDirective> augmentations;
 
   /// `export` directives.
-  final List<UnlinkedNamespaceDirective> exports;
+  final List<UnlinkedExportDirective> exports;
 
   /// `import` directives.
-  final List<UnlinkedNamespaceDirective> imports;
+  final List<UnlinkedImportDirective> imports;
 
   /// Encoded informative data.
   final Uint8List informativeBytes;
@@ -397,10 +518,10 @@
         () => UnlinkedImportAugmentationDirective.read(reader),
       ),
       exports: reader.readTypedList(
-        () => UnlinkedNamespaceDirective.read(reader),
+        () => UnlinkedExportDirective.read(reader),
       ),
       imports: reader.readTypedList(
-        () => UnlinkedNamespaceDirective.read(reader),
+        () => UnlinkedImportDirective.read(reader),
       ),
       informativeBytes: reader.readUint8List(),
       libraryAugmentationDirective: reader.readOptionalObject(
@@ -431,10 +552,10 @@
     sink.writeList<UnlinkedImportAugmentationDirective>(augmentations, (x) {
       x.write(sink);
     });
-    sink.writeList<UnlinkedNamespaceDirective>(exports, (x) {
+    sink.writeList<UnlinkedExportDirective>(exports, (x) {
       x.write(sink);
     });
-    sink.writeList<UnlinkedNamespaceDirective>(imports, (x) {
+    sink.writeList<UnlinkedImportDirective>(imports, (x) {
       x.write(sink);
     });
     sink.writeUint8List(informativeBytes);
diff --git a/pkg/analyzer/lib/src/dart/ast/ast.dart b/pkg/analyzer/lib/src/dart/ast/ast.dart
index 60e5845..b8d68c1 100644
--- a/pkg/analyzer/lib/src/dart/ast/ast.dart
+++ b/pkg/analyzer/lib/src/dart/ast/ast.dart
@@ -15,6 +15,7 @@
 import 'package:analyzer/dart/element/type.dart';
 import 'package:analyzer/src/dart/ast/to_source_visitor.dart';
 import 'package:analyzer/src/dart/ast/token.dart';
+import 'package:analyzer/src/dart/element/element.dart';
 import 'package:analyzer/src/dart/resolver/typed_literal_resolver.dart';
 import 'package:analyzer/src/fasta/token_utils.dart' as util show findPrevious;
 import 'package:analyzer/src/generated/resolver.dart';
@@ -6420,8 +6421,18 @@
     _becomeParentOf(_prefix);
   }
 
+  @Deprecated('Use element2 instead')
   @override
-  ImportElement? get element => super.element as ImportElement?;
+  ImportElement? get element {
+    final element2 = this.element2;
+    if (element2 != null) {
+      return ImportElementImpl(element2);
+    }
+    return null;
+  }
+
+  @override
+  ImportElement2? get element2 => super.element as ImportElement2?;
 
   @override
   Token get firstTokenAfterCommentAndMetadata => importKeyword;
@@ -6439,7 +6450,7 @@
 
   @override
   LibraryElement? get uriElement {
-    return element?.importedLibrary;
+    return element2?.importedLibrary;
   }
 
   @override
@@ -8995,11 +9006,11 @@
   bool get isDeferred {
     Element? element = _prefix.staticElement;
     if (element is PrefixElement) {
-      List<ImportElement> imports = element.imports;
+      final imports = element.imports2;
       if (imports.length != 1) {
         return false;
       }
-      return imports[0].isDeferred;
+      return imports[0].prefix is DeferredImportElementPrefix;
     }
     return false;
   }
diff --git a/pkg/analyzer/lib/src/dart/ast/element_locator.dart b/pkg/analyzer/lib/src/dart/ast/element_locator.dart
index e0bfc35..dd37049 100644
--- a/pkg/analyzer/lib/src/dart/ast/element_locator.dart
+++ b/pkg/analyzer/lib/src/dart/ast/element_locator.dart
@@ -111,7 +111,7 @@
 
   @override
   Element? visitImportDirective(ImportDirective node) {
-    return node.element;
+    return node.element2;
   }
 
   @override
diff --git a/pkg/analyzer/lib/src/dart/element/display_string_builder.dart b/pkg/analyzer/lib/src/dart/element/display_string_builder.dart
index e9ff526..c3e6e47 100644
--- a/pkg/analyzer/lib/src/dart/element/display_string_builder.dart
+++ b/pkg/analyzer/lib/src/dart/element/display_string_builder.dart
@@ -138,9 +138,9 @@
     _writeFormalParameters(element.parameters, forElement: true);
   }
 
-  void writeImportElement(ImportElementImpl element) {
+  void writeImportElement(ImportElement2Impl element) {
     _write('import ');
-    (element.importedLibrary as LibraryElementImpl).appendTo(this);
+    _writeDirectiveUri(element.uri);
   }
 
   void writeInterfaceType(InterfaceType type) {
@@ -164,15 +164,7 @@
 
   void writePartElement(PartElementImpl element) {
     _write('part ');
-
-    final uri = element.uri;
-    if (uri is DirectiveUriWithUnitImpl) {
-      _write('unit ${uri.unit.source.uri}');
-    } else if (uri is DirectiveUriWithSourceImpl) {
-      _write('source ${uri.source}');
-    } else {
-      _write('<unknown>');
-    }
+    _writeDirectiveUri(element.uri);
   }
 
   void writePrefixElement(PrefixElementImpl element) {
@@ -241,6 +233,16 @@
     _buffer.write(str);
   }
 
+  void _writeDirectiveUri(DirectiveUri uri) {
+    if (uri is DirectiveUriWithUnitImpl) {
+      _write('unit ${uri.unit.source.uri}');
+    } else if (uri is DirectiveUriWithSourceImpl) {
+      _write('source ${uri.source}');
+    } else {
+      _write('<unknown>');
+    }
+  }
+
   void _writeFormalParameters(
     List<ParameterElement> parameters, {
     required bool forElement,
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index 8d2924b8..4215c02 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -1662,21 +1662,32 @@
   }
 }
 
+class DeferredImportElementPrefixImpl extends ImportElementPrefixImpl
+    implements DeferredImportElementPrefix {
+  DeferredImportElementPrefixImpl({
+    required super.element,
+  });
+}
+
 class DirectiveUriImpl implements DirectiveUri {}
 
-class DirectiveUriWithLibraryImpl extends DirectiveUriWithRelativeUriImpl
+class DirectiveUriWithLibraryImpl extends DirectiveUriWithSourceImpl
     implements DirectiveUriWithLibrary {
   @override
-  final LibraryElementImpl library;
+  late LibraryElementImpl library;
 
   DirectiveUriWithLibraryImpl({
     required super.relativeUriString,
     required super.relativeUri,
+    required super.source,
     required this.library,
   });
 
-  @override
-  Source get source => library.source;
+  DirectiveUriWithLibraryImpl.read({
+    required super.relativeUriString,
+    required super.relativeUri,
+    required super.source,
+  });
 }
 
 class DirectiveUriWithRelativeUriImpl
@@ -3613,42 +3624,38 @@
   }
 }
 
-/// A concrete implementation of an [ImportElement].
-class ImportElementImpl extends UriReferencedElementImpl
-    implements ImportElement {
-  @override
-  LibraryElement? importedLibrary;
-
-  @override
-  PrefixElement? prefix;
-
+class ImportElement2Impl extends _ExistingElementImpl
+    implements ImportElement2 {
   @override
   List<NamespaceCombinator> combinators = const [];
 
-  /// The cached value of [namespace].
+  @override
+  final int importKeywordOffset;
+
+  @override
+  final ImportElementPrefix? prefix;
+
+  @override
+  final DirectiveUri uri;
+
   Namespace? _namespace;
 
-  /// Initialize a newly created import element at the given [offset].
-  /// The offset may be `-1` if the import is synthetic.
-  ImportElementImpl(int offset) : super(null, offset);
+  ImportElement2Impl({
+    required this.importKeywordOffset,
+    required this.prefix,
+    required this.uri,
+  }) : super(null, importKeywordOffset);
 
   @override
-  CompilationUnitElementImpl get enclosingUnit {
-    var enclosingLibrary = enclosingElement as LibraryElementImpl;
-    return enclosingLibrary._definingCompilationUnit;
-  }
+  int get hashCode => identityHashCode(this);
 
   @override
-  String get identifier => "${importedLibrary?.identifier}@$nameOffset";
-
-  @override
-  bool get isDeferred {
-    return hasModifier(Modifier.DEFERRED);
-  }
-
-  /// Set whether this import is for a deferred library.
-  set isDeferred(bool isDeferred) {
-    setModifier(Modifier.DEFERRED, isDeferred);
+  LibraryElement? get importedLibrary {
+    final uri = this.uri;
+    if (uri is DirectiveUriWithLibrary) {
+      return uri.library;
+    }
+    return null;
   }
 
   @override
@@ -3656,25 +3663,264 @@
 
   @override
   Namespace get namespace {
-    return _namespace ??=
-        NamespaceBuilder().createImportNamespaceForDirective(this);
+    final uri = this.uri;
+    if (uri is DirectiveUriWithLibrary) {
+      return _namespace ??=
+          NamespaceBuilder().createImportNamespaceForDirective(
+        importedLibrary: uri.library,
+        combinators: combinators,
+        prefix: prefix?.element,
+      );
+    }
+    return Namespace.EMPTY;
   }
 
   @override
-  T? accept<T>(ElementVisitor<T> visitor) => visitor.visitImportElement(this);
+  bool operator ==(Object other) {
+    return identical(this, other);
+  }
+
+  @override
+  T? accept<T>(ElementVisitor<T> visitor) {
+    return visitor.visitImportElement2(this);
+  }
 
   @override
   void appendTo(ElementDisplayStringBuilder builder) {
     builder.writeImportElement(this);
   }
+}
+
+/// A concrete implementation of an [ImportElement].
+@Deprecated('Use ImportElement2 instead')
+class ImportElementImpl extends ElementImpl implements ImportElement {
+  final ImportElement2 base;
+
+  ImportElementImpl(this.base) : super(base.name, base.nameOffset);
+
+  @override
+  List<NamespaceCombinator> get combinators => base.combinators;
+
+  @override
+  AnalysisContext get context => base.context;
+
+  @override
+  Element get declaration => base.declaration;
+
+  @override
+  String get displayName => base.displayName;
+
+  @override
+  String? get documentationComment => base.documentationComment;
+
+  @override
+  Element? get enclosingElement => base.enclosingElement;
+
+  @override
+  bool get hasAlwaysThrows => base.hasAlwaysThrows;
+
+  @override
+  bool get hasDeprecated => base.hasDeprecated;
+
+  @override
+  bool get hasDoNotStore => base.hasDoNotStore;
+
+  @override
+  bool get hasFactory => base.hasFactory;
+
+  @override
+  bool get hasInternal => base.hasInternal;
+
+  @override
+  bool get hasIsTest => base.hasIsTest;
+
+  @override
+  bool get hasIsTestGroup => base.hasIsTestGroup;
+
+  @override
+  bool get hasJS => base.hasJS;
+
+  @override
+  bool get hasLiteral => base.hasLiteral;
+
+  @override
+  bool get hasMustCallSuper => base.hasMustCallSuper;
+
+  @override
+  bool get hasNonVirtual => base.hasNonVirtual;
+
+  @override
+  bool get hasOptionalTypeArgs => base.hasOptionalTypeArgs;
+
+  @override
+  bool get hasOverride => base.hasOverride;
+
+  @override
+  bool get hasProtected => base.hasProtected;
+
+  @override
+  bool get hasRequired => base.hasRequired;
+
+  @override
+  bool get hasSealed => base.hasSealed;
+
+  @override
+  bool get hasUseResult => base.hasUseResult;
+
+  @override
+  bool get hasVisibleForOverriding => base.hasVisibleForOverriding;
+
+  @override
+  bool get hasVisibleForTemplate => base.hasVisibleForTemplate;
+
+  @override
+  bool get hasVisibleForTesting => base.hasVisibleForTesting;
+
+  @override
+  int get id => base.id;
+
+  @override
+  String get identifier => 'import@$nameOffset';
+
+  @override
+  LibraryElement? get importedLibrary {
+    final uri = base.uri;
+    if (uri is DirectiveUriWithLibrary) {
+      return uri.library;
+    }
+    return null;
+  }
+
+  @override
+  bool get isDeferred => base.prefix is DeferredImportElementPrefix;
+
+  @override
+  bool get isPrivate => base.isPrivate;
+
+  @override
+  bool get isPublic => base.isPublic;
+
+  @override
+  bool get isSynthetic => base.isSynthetic;
+
+  @override
+  ElementKind get kind => base.kind;
+
+  @override
+  LibraryElementImpl get library => base.library as LibraryElementImpl;
+
+  @override
+  Source get librarySource => base.librarySource;
+
+  @override
+  ElementLocation get location => base.location!;
+
+  @override
+  List<ElementAnnotation> get metadata => base.metadata;
+
+  @override
+  String? get name => base.name;
+
+  @override
+  int get nameLength => base.nameLength;
+
+  @override
+  int get nameOffset => base.nameOffset;
+
+  @override
+  Namespace get namespace => base.namespace;
+
+  @override
+  Element get nonSynthetic => this;
+
+  @override
+  PrefixElement? get prefix => base.prefix?.element;
+
+  @override
+  AnalysisSession? get session => base.session;
+
+  @override
+  Source get source => base.source;
+
+  @override
+  String? get uri {
+    final uri = base.uri;
+    if (uri is DirectiveUriWithRelativeUriString) {
+      return uri.relativeUriString;
+    } else {
+      return null;
+    }
+  }
+
+  @override
+  // TODO: implement uriEnd
+  int get uriEnd => throw UnimplementedError();
+
+  @override
+  // TODO: implement uriOffset
+  int get uriOffset => throw UnimplementedError();
+
+  @override
+  bool operator ==(Object other) {
+    return identical(this, other);
+  }
+
+  @override
+  T? accept<T>(ElementVisitor<T> visitor) {
+    return visitor.visitImportElement(this);
+  }
+
+  @override
+  String getDisplayString(
+      {required bool withNullability, bool multiline = false}) {
+    return base.getDisplayString(
+      withNullability: withNullability,
+      multiline: multiline,
+    );
+  }
+
+  @override
+  String getExtendedDisplayName(String? shortName) {
+    return base.getExtendedDisplayName(shortName);
+  }
+
+  @Deprecated('Use isAccessibleIn2() instead')
+  @override
+  bool isAccessibleIn(LibraryElement? library) {
+    return base.isAccessibleIn(library);
+  }
+
+  @override
+  bool isAccessibleIn2(LibraryElement library) {
+    return base.isAccessibleIn2(library);
+  }
+
+  @override
+  E thisOrAncestorMatching<E extends Element>(
+      bool Function(Element p1) predicate) {
+    return base.thisOrAncestorMatching(predicate) as E;
+  }
+
+  @override
+  E? thisOrAncestorOfType<E extends Element>() {
+    return base.thisOrAncestorOfType();
+  }
 
   @override
   void visitChildren(ElementVisitor visitor) {
-    super.visitChildren(visitor);
-    prefix?.accept(visitor);
+    base.visitChildren(visitor);
   }
 }
 
+class ImportElementPrefixImpl implements ImportElementPrefix {
+  @override
+  final PrefixElement element;
+
+  ImportElementPrefixImpl({
+    required this.element,
+  });
+}
+
 /// A concrete implementation of a [LabelElement].
 class LabelElementImpl extends ElementImpl implements LabelElement {
   /// A flag indicating whether this label is associated with a `switch`
@@ -3736,6 +3982,10 @@
   FeatureSet get featureSet => augmented.featureSet;
 
   @override
+  // TODO: implement imports2
+  List<ImportElement2> get imports2 => throw UnimplementedError();
+
+  @override
   bool get isNonNullableByDefault => augmented.isNonNullableByDefault;
 
   @override
@@ -3897,20 +4147,24 @@
 
   @override
   List<LibraryElement> get importedLibraries {
-    HashSet<LibraryElement> libraries = HashSet<LibraryElement>();
-    for (ImportElement element in imports) {
-      var library = element.importedLibrary;
-      if (library != null) {
-        libraries.add(library);
-      }
-    }
-    return libraries.toList(growable: false);
+    return imports2
+        .map((import) => import.uri)
+        .whereType<DirectiveUriWithLibrary>()
+        .map((uri) => uri.library)
+        .toSet()
+        .toList();
+  }
+
+  @Deprecated('Use imports2 instead')
+  @override
+  List<ImportElement> get imports {
+    return imports2.map(ImportElementImpl.new).toList();
   }
 
   @override
-  List<ImportElement> get imports {
+  List<ImportElement2> get imports2 {
     linkedData?.read(this);
-    return _imports;
+    return _imports2;
   }
 
   @override
@@ -4018,7 +4272,7 @@
 
   @override
   List<PrefixElement> get prefixes =>
-      _prefixes ??= buildPrefixesFromImports(imports);
+      _prefixes ??= buildPrefixesFromImports(imports2);
 
   @override
   Namespace get publicNamespace {
@@ -4124,8 +4378,8 @@
     required String? prefix,
     required String name,
   }) {
-    for (var importElement in imports) {
-      if (importElement.prefix?.name == prefix &&
+    for (var importElement in imports2) {
+      if (importElement.prefix?.element.name == prefix &&
           importElement.importedLibrary?.isSynthetic != false) {
         var showCombinators = importElement.combinators
             .whereType<ShowElementCombinator>()
@@ -4193,10 +4447,10 @@
   }
 
   static List<PrefixElement> buildPrefixesFromImports(
-      List<ImportElement> imports) {
+      List<ImportElement2> imports) {
     HashSet<PrefixElement> prefixes = HashSet<PrefixElement>();
-    for (ImportElement element in imports) {
-      var prefix = element.prefix;
+    for (ImportElement2 element in imports) {
+      var prefix = element.prefix?.element;
       if (prefix != null) {
         prefixes.add(prefix);
       }
@@ -4233,7 +4487,7 @@
 
   /// A list containing specifications of all of the imports defined in this
   /// library.
-  List<ImportElement> _imports = _Sentinel.importElement;
+  List<ImportElement2> _imports2 = _Sentinel.importElement2;
 
   /// A list containing specifications of all of the exports defined in this
   /// library.
@@ -4297,32 +4551,29 @@
   @override
   String get identifier => '${_definingCompilationUnit.source.uri}';
 
+  @Deprecated('Use imports2 instead')
   @override
   List<ImportElement> get imports {
-    return _imports;
+    return _imports2.map(ImportElementImpl.new).toList();
   }
 
   /// Set the specifications of all of the imports defined in this library to
   /// the given list of [imports].
-  set imports(List<ImportElement> imports) {
-    for (ImportElement importElement in imports) {
-      (importElement as ImportElementImpl).enclosingElement = this;
-      var prefix = importElement.prefix as PrefixElementImpl?;
-      if (prefix != null) {
-        prefix.enclosingElement = this;
-      }
+  set imports2(List<ImportElement2> imports) {
+    for (final importElement in imports) {
+      (importElement as ImportElement2Impl).enclosingElement = this;
     }
-    _imports = imports;
+    _imports2 = imports;
     _prefixes = null;
   }
 
-  List<ImportElement> get imports_unresolved {
-    return _imports;
+  List<ImportElement2> get imports_unresolved {
+    return _imports2;
   }
 
   @override
   List<PrefixElement> get prefixes =>
-      _prefixes ??= buildPrefixesFromImports(imports);
+      _prefixes ??= buildPrefixesFromImports(imports2);
 
   @override
   AnalysisSessionImpl get session;
@@ -4337,16 +4588,18 @@
     super.visitChildren(visitor);
     _definingCompilationUnit.accept(visitor);
     safelyVisitChildren(exports, visitor);
+    // ignore: deprecated_member_use_from_same_package
     safelyVisitChildren(imports, visitor);
+    safelyVisitChildren(imports2, visitor);
   }
 
   static List<PrefixElement> buildPrefixesFromImports(
-      List<ImportElement> imports) {
+      List<ImportElement2> imports) {
     HashSet<PrefixElement> prefixes = HashSet<PrefixElement>();
-    for (ImportElement element in imports) {
-      var prefix = element.prefix;
+    for (final import in imports) {
+      var prefix = import.prefix;
       if (prefix != null) {
-        prefixes.add(prefix);
+        prefixes.add(prefix.element);
       }
     }
     return prefixes.toList(growable: false);
@@ -5227,6 +5480,7 @@
   LibraryOrAugmentationElementImpl get enclosingElement2 =>
       super.enclosingElement as LibraryOrAugmentationElementImpl;
 
+  @Deprecated('Use imports2 instead')
   @override
   List<ImportElement> get imports {
     return enclosingElement2.imports
@@ -5235,6 +5489,13 @@
   }
 
   @override
+  List<ImportElement2> get imports2 {
+    return enclosingElement2.imports2
+        .where((import) => import.prefix?.element == this)
+        .toList();
+  }
+
+  @override
   ElementKind get kind => ElementKind.PREFIX;
 
   @override
@@ -6271,7 +6532,7 @@
   static final List<FieldElement> fieldElement = List.unmodifiable([]);
   static final List<AugmentationImportElement> augmentationImportElement =
       List.unmodifiable([]);
-  static final List<ImportElement> importElement = List.unmodifiable([]);
+  static final List<ImportElement2> importElement2 = List.unmodifiable([]);
   static final List<MethodElement> methodElement = List.unmodifiable([]);
   static final List<PropertyAccessorElement> propertyAccessorElement =
       List.unmodifiable([]);
diff --git a/pkg/analyzer/lib/src/dart/element/scope.dart b/pkg/analyzer/lib/src/dart/element/scope.dart
index f57b6e0..1fab302 100644
--- a/pkg/analyzer/lib/src/dart/element/scope.dart
+++ b/pkg/analyzer/lib/src/dart/element/scope.dart
@@ -118,6 +118,16 @@
 
     _element.prefixes.forEach(_addGetter);
     _element.units.forEach(_addUnitElements);
+
+    // TODO(scheglov) I don't understand why it used to work, but broke now.
+    // Now: when I'm adding `ImportElement2`.
+    // We used to get it from `exportedReference`, but this is wrong.
+    // These elements are declared in dart:core itself.
+    final reference = _element.reference!;
+    if (reference.name == 'dart:core') {
+      _addGetter(DynamicElementImpl.instance);
+      _addGetter(NeverElementImpl.instance);
+    }
   }
 
   void _addExtension(ExtensionElement element) {
@@ -155,9 +165,11 @@
 
   PrefixScope(this._library, PrefixElement? prefix) {
     final elementFactory = _library.session.elementFactory;
-    for (final import in _library.imports) {
-      if (import.prefix == prefix) {
-        final importedLibrary = import.importedLibrary;
+    for (final import in _library.imports2) {
+      final importedUri = import.uri;
+      if (importedUri is DirectiveUriWithLibrary &&
+          import.prefix?.element == prefix) {
+        final importedLibrary = importedUri.library;
         if (importedLibrary is LibraryElementImpl) {
           final combinators = import.combinators.build();
           for (final exportedReference in importedLibrary.exportedReferences) {
@@ -172,7 +184,7 @@
               _add(importedElement);
             }
           }
-          if (import.isDeferred) {
+          if (import.prefix is DeferredImportElementPrefix) {
             _deferredLibrary ??= importedLibrary;
           }
         }
diff --git a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
index 055b894..0e87bbf 100644
--- a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
+++ b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
@@ -288,7 +288,7 @@
       if (element is LocalVariableElement ||
           (element is ParameterElement && !element.isNamed)) {
         await collectReferences2(element.source!.fullName, performance!);
-      } else if (element is ImportElement) {
+      } else if (element is ImportElement2) {
         return await _searchReferences_Import(element);
       } else {
         var result = performance!.run('getFilesContaining', (performance) {
@@ -842,7 +842,7 @@
   }
 
   Future<List<CiderSearchMatch>> _searchReferences_Import(
-      ImportElement element) async {
+      ImportElement2 element) async {
     var results = <CiderSearchMatch>[];
     LibraryElement libraryElement = element.library;
     for (CompilationUnitElement unitElement in libraryElement.units) {
diff --git a/pkg/analyzer/lib/src/dart/micro/utils.dart b/pkg/analyzer/lib/src/dart/micro/utils.dart
index fc88c98..a4e9419 100644
--- a/pkg/analyzer/lib/src/dart/micro/utils.dart
+++ b/pkg/analyzer/lib/src/dart/micro/utils.dart
@@ -28,7 +28,7 @@
   if (node is SimpleIdentifier && element is PrefixElement) {
     var parent = node.parent;
     if (parent is ImportDirective) {
-      element = parent.element;
+      element = parent.element2;
     } else {
       element = _getImportElementInfo(node);
     }
@@ -62,27 +62,27 @@
   return constructor;
 }
 
-/// Return the [ImportElement] that declared [prefix] and imports [element].
+/// Return the [ImportElement2] that declared [prefix] and imports [element].
 ///
 /// [libraryElement] - the [LibraryElement] where reference is.
 /// [prefix] - the import prefix, maybe `null`.
 /// [element] - the referenced element.
-/// [importElementsMap] - the cache of [Element]s imported by [ImportElement]s.
-ImportElement? _getImportElement(LibraryElement libraryElement, String prefix,
-    Element element, Map<ImportElement, Set<Element>> importElementsMap) {
+/// [importElementsMap] - the cache of [Element]s imported by [ImportElement2]s.
+ImportElement2? _getImportElement(LibraryElement libraryElement, String prefix,
+    Element element, Map<ImportElement2, Set<Element>> importElementsMap) {
   if (element.enclosingElement is! CompilationUnitElement) {
     return null;
   }
   var usedLibrary = element.library;
   // find ImportElement that imports used library with used prefix
-  List<ImportElement>? candidates;
-  for (var importElement in libraryElement.imports) {
+  List<ImportElement2>? candidates;
+  for (var importElement in libraryElement.imports2) {
     // required library
     if (importElement.importedLibrary != usedLibrary) {
       continue;
     }
     // required prefix
-    var prefixElement = importElement.prefix;
+    var prefixElement = importElement.prefix?.element;
     if (prefixElement == null) {
       continue;
     }
@@ -126,9 +126,9 @@
   return null;
 }
 
-/// Returns the [ImportElement] that is referenced by [prefixNode] with a
+/// Returns the [ImportElement2] that is referenced by [prefixNode] with a
 /// [PrefixElement], maybe `null`.
-ImportElement? _getImportElementInfo(SimpleIdentifier prefixNode) {
+ImportElement2? _getImportElementInfo(SimpleIdentifier prefixNode) {
   // prepare environment
   var parent = prefixNode.parent;
   var unit = prefixNode.thisOrAncestorOfType<CompilationUnit>();
@@ -155,7 +155,7 @@
   }
   // find ImportElement
   var prefix = prefixNode.name;
-  var importElementsMap = <ImportElement, Set<Element>>{};
+  var importElementsMap = <ImportElement2, Set<Element>>{};
   return _getImportElement(
       libraryElement, prefix, usedElement, importElementsMap);
 }
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 0906db3..79d5949 100644
--- a/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart
@@ -111,7 +111,7 @@
     left = node.leftOperand;
 
     var flow = _resolver.flowAnalysis.flow;
-    EqualityInfo<PromotableElement, DartType>? leftInfo;
+    EqualityInfo<DartType>? leftInfo;
     var leftExtensionOverride = left is ExtensionOverride;
     if (!leftExtensionOverride) {
       leftInfo = flow?.equalityOperand_end(left, left.typeOrThrow);
diff --git a/pkg/analyzer/lib/src/dart/resolver/invocation_inferrer.dart b/pkg/analyzer/lib/src/dart/resolver/invocation_inferrer.dart
index b9fcb2c..729d28a 100644
--- a/pkg/analyzer/lib/src/dart/resolver/invocation_inferrer.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/invocation_inferrer.dart
@@ -203,8 +203,7 @@
           Substitution.fromPairs(rawType.typeFormals, inferrer.partialInfer());
     }
 
-    List<EqualityInfo<PromotableElement, DartType>?>? identicalInfo =
-        _isIdentical ? [] : null;
+    List<EqualityInfo<DartType>?>? identicalInfo = _isIdentical ? [] : null;
     var parameterMap = _computeParameterMap(rawType?.parameters ?? const []);
     var deferredFunctionLiterals = _visitArguments(
         parameterMap: parameterMap,
@@ -434,8 +433,7 @@
 
   /// If the invocation being processed is a call to `identical`, informs flow
   /// analysis about it, so that it can do appropriate promotions.
-  void _recordIdenticalInfo(
-      List<EqualityInfo<PromotableElement, DartType>?>? identicalInfo) {
+  void _recordIdenticalInfo(List<EqualityInfo<DartType>?>? identicalInfo) {
     var flow = resolver.flowAnalysis.flow;
     if (identicalInfo != null) {
       flow?.equalityOperation_end(argumentList.parent as Expression,
@@ -446,7 +444,7 @@
   /// Resolves any function literals that were deferred by [_visitArguments].
   void _resolveDeferredFunctionLiterals(
       {required List<_DeferredParamInfo> deferredFunctionLiterals,
-      List<EqualityInfo<PromotableElement, DartType>?>? identicalInfo,
+      List<EqualityInfo<DartType>?>? identicalInfo,
       Substitution? substitution,
       GenericInferrer? inferrer}) {
     var flow = resolver.flowAnalysis.flow;
@@ -481,7 +479,7 @@
   /// returned.
   List<_DeferredParamInfo>? _visitArguments(
       {required Map<Object, ParameterElement> parameterMap,
-      List<EqualityInfo<PromotableElement, DartType>?>? identicalInfo,
+      List<EqualityInfo<DartType>?>? identicalInfo,
       Substitution? substitution,
       GenericInferrer? inferrer}) {
     assert(whyNotPromotedList.isEmpty);
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 6d951b3..13368ab 100644
--- a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
@@ -665,8 +665,9 @@
     // Note: prefix?.bar is reported as an error in ElementResolver.
 
     if (name == FunctionElement.LOAD_LIBRARY_NAME) {
-      var imports = prefix.imports;
-      if (imports.length == 1 && imports[0].isDeferred) {
+      var imports = prefix.imports2;
+      if (imports.length == 1 &&
+          imports[0].prefix is DeferredImportElementPrefix) {
         var importedLibrary = imports[0].importedLibrary;
         var element = importedLibrary?.loadLibraryFunction;
         element = _resolver.toLegacyElement(element);
diff --git a/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart b/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart
index 781f48e..5f94025 100644
--- a/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart
@@ -757,8 +757,8 @@
 
   @override
   void visitImportDirective(ImportDirective node) {
-    var element = node.element;
-    if (element is ImportElementImpl) {
+    var element = node.element2;
+    if (element is ImportElement2Impl) {
       _setOrCreateMetadataElements(element, node.metadata);
     }
 
diff --git a/pkg/analyzer/lib/src/dart/resolver/scope.dart b/pkg/analyzer/lib/src/dart/resolver/scope.dart
index 84552fb..79865a7 100644
--- a/pkg/analyzer/lib/src/dart/resolver/scope.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/scope.dart
@@ -158,18 +158,13 @@
 
   /// Create a namespace representing the import namespace of the given
   /// [element].
-  Namespace createImportNamespaceForDirective(ImportElement element) {
-    var importedLibrary = element.importedLibrary;
-    if (importedLibrary == null) {
-      //
-      // The imported library will be null if the URI does not reference a valid
-      // library.
-      //
-      return Namespace.EMPTY;
-    }
+  Namespace createImportNamespaceForDirective({
+    required LibraryElement importedLibrary,
+    required List<NamespaceCombinator> combinators,
+    required PrefixElement? prefix,
+  }) {
     Map<String, Element> exportedNames = _getExportMapping(importedLibrary);
-    exportedNames = _applyCombinators(exportedNames, element.combinators);
-    var prefix = element.prefix;
+    exportedNames = _applyCombinators(exportedNames, combinators);
     if (prefix != null) {
       return PrefixedNamespace(prefix.name, exportedNames);
     }
diff --git a/pkg/analyzer/lib/src/error/best_practices_verifier.dart b/pkg/analyzer/lib/src/error/best_practices_verifier.dart
index 4f5dc3e..bd89227 100644
--- a/pkg/analyzer/lib/src/error/best_practices_verifier.dart
+++ b/pkg/analyzer/lib/src/error/best_practices_verifier.dart
@@ -584,8 +584,9 @@
   @override
   void visitImportDirective(ImportDirective node) {
     _deprecatedVerifier.importDirective(node);
-    var importElement = node.element;
-    if (importElement != null && importElement.isDeferred) {
+    var importElement = node.element2;
+    if (importElement != null &&
+        importElement.prefix is DeferredImportElementPrefix) {
       _checkForLoadLibraryFunction(node, importElement);
     }
     _invalidAccessVerifier.verifyImport(node);
@@ -1039,7 +1040,7 @@
       return;
     }
 
-    var importElement = node.element;
+    var importElement = node.element2;
     if (importElement == null) {
       return;
     }
@@ -1234,14 +1235,14 @@
   /// The import has already been determined to be deferred when this is called.
   ///
   /// @param node the import directive to evaluate
-  /// @param importElement the [ImportElement] retrieved from the node
+  /// @param importElement the [ImportElement2] retrieved from the node
   /// @return `true` if and only if an error code is generated on the passed
   ///         node
   /// See [CompileTimeErrorCode.IMPORT_DEFERRED_LIBRARY_WITH_LOAD_FUNCTION].
   bool _checkForLoadLibraryFunction(
-      ImportDirective node, ImportElement importElement) {
+      ImportDirective node, ImportElement2 importElement) {
     var importedLibrary = importElement.importedLibrary;
-    var prefix = importElement.prefix;
+    var prefix = importElement.prefix?.element;
     if (importedLibrary == null || prefix == null) {
       return false;
     }
diff --git a/pkg/analyzer/lib/src/error/dead_code_verifier.dart b/pkg/analyzer/lib/src/error/dead_code_verifier.dart
index 1890e65..9339c45 100644
--- a/pkg/analyzer/lib/src/error/dead_code_verifier.dart
+++ b/pkg/analyzer/lib/src/error/dead_code_verifier.dart
@@ -62,7 +62,7 @@
 
   @override
   void visitImportDirective(ImportDirective node) {
-    ImportElement? importElement = node.element;
+    final importElement = node.element2;
     if (importElement != null) {
       // The element is null when the URI is invalid, but not when the URI is
       // valid but refers to a non-existent file.
diff --git a/pkg/analyzer/lib/src/error/duplicate_definition_verifier.dart b/pkg/analyzer/lib/src/error/duplicate_definition_verifier.dart
index 55cd57b4..9e725a5 100644
--- a/pkg/analyzer/lib/src/error/duplicate_definition_verifier.dart
+++ b/pkg/analyzer/lib/src/error/duplicate_definition_verifier.dart
@@ -318,8 +318,8 @@
       }
     }
 
-    for (ImportElement importElement in _currentLibrary.imports) {
-      var prefix = importElement.prefix;
+    for (final importElement in _currentLibrary.imports2) {
+      var prefix = importElement.prefix?.element;
       if (prefix != null) {
         definedGetters[prefix.name] = prefix;
       }
diff --git a/pkg/analyzer/lib/src/error/imports_verifier.dart b/pkg/analyzer/lib/src/error/imports_verifier.dart
index 52781b7..4c990ff 100644
--- a/pkg/analyzer/lib/src/error/imports_verifier.dart
+++ b/pkg/analyzer/lib/src/error/imports_verifier.dart
@@ -390,7 +390,7 @@
     for (int i = 0; i < length; i++) {
       ImportDirective unusedImport = _unusedImports[i];
       // Check that the imported URI exists and isn't dart:core
-      var importElement = unusedImport.element;
+      var importElement = unusedImport.element2;
       if (importElement != null) {
         var libraryElement = importElement.importedLibrary;
         if (libraryElement == null ||
@@ -776,7 +776,7 @@
   Namespace? computeNamespace(ImportDirective importDirective) {
     var namespace = this[importDirective];
     if (namespace == null) {
-      var importElement = importDirective.element;
+      var importElement = importDirective.element2;
       if (importElement != null) {
         namespace = importElement.namespace;
         this[importDirective] = namespace;
diff --git a/pkg/analyzer/lib/src/generated/element_resolver.dart b/pkg/analyzer/lib/src/generated/element_resolver.dart
index 9ae6c75..26fca88 100644
--- a/pkg/analyzer/lib/src/generated/element_resolver.dart
+++ b/pkg/analyzer/lib/src/generated/element_resolver.dart
@@ -26,7 +26,7 @@
 ///    * An identifier within the declaration of that name should resolve to the
 ///      element being declared.
 ///    * An identifier denoting a prefix should resolve to the element
-///      representing the import that defines the prefix (an [ImportElement]).
+///      representing the import that defines the prefix (an [ImportElement2]).
 ///    * An identifier denoting a variable should resolve to the element
 ///      representing the variable (a [VariableElement]).
 ///    * An identifier denoting a parameter should resolve to the element
@@ -56,7 +56,7 @@
 ///    specified library does not exist.
 /// 5. Every [ImportDirective] and [ExportDirective] should resolve to the
 ///    element representing the library being specified by the directive unless
-///    the specified library does not exist (an [ImportElement] or
+///    the specified library does not exist (an [ImportElement2] or
 ///    [ExportElement]).
 /// 6. The identifier representing the prefix in an [ImportDirective] should
 ///    resolve to the element representing the prefix (a [PrefixElement]).
@@ -240,7 +240,7 @@
         }
       }
     }
-    var importElement = node.element;
+    var importElement = node.element2;
     if (importElement != null) {
       // The element is null when the URI is invalid
       var library = importElement.importedLibrary;
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index 01f79da..28d5b16 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -857,14 +857,14 @@
 
   @override
   void visitImportDirective(ImportDirective node) {
-    var importElement = node.element;
+    var importElement = node.element2;
     if (node.prefix != null) {
       _checkForBuiltInIdentifierAsName(node.prefix!,
           CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_PREFIX_NAME);
     }
     if (importElement != null) {
       _checkForImportInternalLibrary(node, importElement);
-      if (importElement.isDeferred) {
+      if (importElement.prefix is DeferredImportElementPrefix) {
         _checkForDeferredImportOfExtensions(node, importElement);
       }
     }
@@ -2299,7 +2299,7 @@
   /// Report a diagnostic if there are any extensions in the imported library
   /// that are not hidden.
   void _checkForDeferredImportOfExtensions(
-      ImportDirective directive, ImportElement importElement) {
+      ImportDirective directive, ImportElement2 importElement) {
     for (var element in importElement.namespace.definedNames.values) {
       if (element is ExtensionElement) {
         errorReporter.reportErrorForNode(
@@ -2872,12 +2872,12 @@
 
   /// Check that if the visiting library is not system, then any given library
   /// should not be SDK internal library. The [importElement] is the
-  /// [ImportElement] retrieved from the node, if the element in the node was
+  /// [ImportElement2] retrieved from the node, if the element in the node was
   /// `null`, then this method is not called
   ///
   /// See [CompileTimeErrorCode.IMPORT_INTERNAL_LIBRARY].
   void _checkForImportInternalLibrary(
-      ImportDirective directive, ImportElement importElement) {
+      ImportDirective directive, ImportElement2 importElement) {
     if (_isInSystemLibrary) {
       return;
     }
@@ -5096,7 +5096,7 @@
     if (library == null) {
       return '';
     }
-    List<ImportElement> imports = _currentLibrary.imports;
+    final imports = _currentLibrary.imports2;
     int count = imports.length;
     for (int i = 0; i < count; i++) {
       if (identical(imports[i].importedLibrary, library)) {
diff --git a/pkg/analyzer/lib/src/generated/testing/element_factory.dart b/pkg/analyzer/lib/src/generated/testing/element_factory.dart
index 68896c6..49121ab4 100644
--- a/pkg/analyzer/lib/src/generated/testing/element_factory.dart
+++ b/pkg/analyzer/lib/src/generated/testing/element_factory.dart
@@ -195,16 +195,6 @@
     return getter;
   }
 
-  static ImportElementImpl importFor(
-      LibraryElement importedLibrary, PrefixElement? prefix,
-      [List<NamespaceCombinator> combinators = const <NamespaceCombinator>[]]) {
-    ImportElementImpl spec = ImportElementImpl(0);
-    spec.importedLibrary = importedLibrary;
-    spec.prefix = prefix;
-    spec.combinators = combinators;
-    return spec;
-  }
-
   static LibraryElementImpl library(
       AnalysisContext context, String libraryName) {
     String fileName = "/$libraryName.dart";
diff --git a/pkg/analyzer/lib/src/summary2/ast_binary_tag.dart b/pkg/analyzer/lib/src/summary2/ast_binary_tag.dart
index 83c6aea..a59dff8 100644
--- a/pkg/analyzer/lib/src/summary2/ast_binary_tag.dart
+++ b/pkg/analyzer/lib/src/summary2/ast_binary_tag.dart
@@ -16,6 +16,12 @@
   withNothing,
 }
 
+enum ImportElementPrefixKind {
+  isDeferred,
+  isNotDeferred,
+  isNull,
+}
+
 class Tag {
   static const int Nothing = 0;
   static const int Something = 1;
@@ -103,6 +109,7 @@
   static const int MemberLegacyWithoutTypeArguments = 1;
   static const int MemberLegacyWithTypeArguments = 2;
   static const int MemberWithTypeArguments = 3;
+  static const int ImportPrefixElement = 4;
 
   static const int ParameterKindRequiredPositional = 1;
   static const int ParameterKindOptionalPositional = 2;
diff --git a/pkg/analyzer/lib/src/summary2/bundle_reader.dart b/pkg/analyzer/lib/src/summary2/bundle_reader.dart
index aa14833f..268706f 100644
--- a/pkg/analyzer/lib/src/summary2/bundle_reader.dart
+++ b/pkg/analyzer/lib/src/summary2/bundle_reader.dart
@@ -370,12 +370,15 @@
       unitElement: unitElement,
     );
 
-    for (var import in element.imports) {
-      import as ImportElementImpl;
+    for (var import in element.imports2) {
+      import as ImportElement2Impl;
       import.metadata = reader._readAnnotationList(
         unitElement: unitElement,
       );
-      import.importedLibrary = reader.readElement() as LibraryElementImpl?;
+      final uri = import.uri;
+      if (uri is DirectiveUriWithLibraryImpl) {
+        uri.library = reader.libraryOfUri(uri.source.uri);
+      }
     }
 
     for (var export in element.exports) {
@@ -445,14 +448,22 @@
     libraryElement.reference = _reference;
 
     libraryElement.languageVersion = _readLanguageVersion();
-    libraryElement.imports = _reader.readTypedList(_readImportElement);
+    libraryElement.imports2 = _reader.readTypedList(() {
+      return _readImportElement(
+        libraryElement: libraryElement,
+      );
+    });
     libraryElement.exports = _reader.readTypedList(_readExportElement);
     LibraryElementFlags.read(_reader, libraryElement);
 
-    var unitContainerRef = _reference.getChild('@unit');
+    for (final import in libraryElement.imports2) {
+      final prefixElement = import.prefix?.element;
+      if (prefixElement is PrefixElementImpl) {
+        libraryElement.encloseElement(prefixElement);
+      }
+    }
 
     libraryElement.definingCompilationUnit = _readUnitElement(
-      unitContainerRef: unitContainerRef,
       libraryElement: libraryElement,
       librarySource: librarySource,
       unitSource: librarySource,
@@ -460,7 +471,6 @@
 
     libraryElement.parts2 = _reader.readTypedList(() {
       return _readPartElement(
-        unitContainerRef: unitContainerRef,
         libraryElement: libraryElement,
       );
     });
@@ -581,7 +591,6 @@
   }
 
   DirectiveUri _readDirectiveUri({
-    required Reference unitContainerRef,
     required LibraryElementImpl libraryElement,
   }) {
     DirectiveUriWithRelativeUriStringImpl readWithRelativeUriString() {
@@ -608,12 +617,15 @@
 
       final sourceUriStr = _reader.readStringReference();
       final sourceUri = Uri.parse(sourceUriStr);
-      final source = sourceFactory.forUri2(sourceUri)!;
+      final source = sourceFactory.forUri2(sourceUri);
+
+      // TODO(scheglov) https://github.com/dart-lang/sdk/issues/49431
+      final fixedSource = source ?? sourceFactory.forUri('dart:math')!;
 
       return DirectiveUriWithSourceImpl(
         relativeUriString: parent.relativeUriString,
         relativeUri: parent.relativeUri,
-        source: source,
+        source: fixedSource,
       );
     }
 
@@ -623,7 +635,6 @@
       case DirectiveUriKind.withUnit:
         final parent = readWithSource();
         final unitElement = _readUnitElement(
-          unitContainerRef: unitContainerRef,
           libraryElement: libraryElement,
           librarySource: libraryElement.source,
           unitSource: parent.source,
@@ -634,8 +645,12 @@
           unit: unitElement,
         );
       case DirectiveUriKind.withLibrary:
-        // TODO: Handle this case.
-        throw UnimplementedError();
+        final parent = readWithSource();
+        return DirectiveUriWithLibraryImpl.read(
+          relativeUriString: parent.relativeUriString,
+          relativeUri: parent.relativeUri,
+          source: parent.source,
+        );
       case DirectiveUriKind.withSource:
         return readWithSource();
       case DirectiveUriKind.withRelativeUri:
@@ -859,21 +874,53 @@
     });
   }
 
-  ImportElementImpl _readImportElement() {
-    var element = ImportElementImpl(-1);
+  ImportElement2Impl _readImportElement({
+    required LibraryElementImpl libraryElement,
+  }) {
+    final uri = _readDirectiveUri(
+      libraryElement: libraryElement,
+    );
+    final prefix = _readImportElementPrefix();
+    final combinators = _reader.readTypedList(_readNamespaceCombinator);
+
+    final element = ImportElement2Impl(
+      importKeywordOffset: -1,
+      uri: uri,
+      prefix: prefix,
+    )..combinators = combinators;
     ImportElementFlags.read(_reader, element);
-    element.uri = _reader.readOptionalStringReference();
-    var prefixName = _reader.readOptionalStringReference();
-    if (prefixName != null) {
-      var reference = _reference.getChild('@prefix').getChild(prefixName);
-      var prefixElement =
-          PrefixElementImpl(prefixName, -1, reference: reference);
-      element.prefix = prefixElement;
-    }
-    element.combinators = _reader.readTypedList(_readNamespaceCombinator);
     return element;
   }
 
+  ImportElementPrefixImpl? _readImportElementPrefix() {
+    PrefixElementImpl buildElement(String name) {
+      final reference = _reference.getChild('@prefix').getChild(name);
+      final existing = reference.element;
+      if (existing is PrefixElementImpl) {
+        return existing;
+      } else {
+        return PrefixElementImpl(name, -1, reference: reference);
+      }
+    }
+
+    final kindIndex = _reader.readByte();
+    final kind = ImportElementPrefixKind.values[kindIndex];
+    switch (kind) {
+      case ImportElementPrefixKind.isDeferred:
+        final name = _reader.readStringReference();
+        return DeferredImportElementPrefixImpl(
+          element: buildElement(name),
+        );
+      case ImportElementPrefixKind.isNotDeferred:
+        final name = _reader.readStringReference();
+        return ImportElementPrefixImpl(
+          element: buildElement(name),
+        );
+      case ImportElementPrefixKind.isNull:
+        return null;
+    }
+  }
+
   LibraryLanguageVersion _readLanguageVersion() {
     var packageMajor = _reader.readUInt30();
     var packageMinor = _reader.readUInt30();
@@ -1044,11 +1091,9 @@
   }
 
   PartElement _readPartElement({
-    required Reference unitContainerRef,
     required LibraryElementImpl libraryElement,
   }) {
     final uri = _readDirectiveUri(
-      unitContainerRef: unitContainerRef,
       libraryElement: libraryElement,
     );
 
@@ -1272,7 +1317,6 @@
   }
 
   CompilationUnitElementImpl _readUnitElement({
-    required Reference unitContainerRef,
     required LibraryElementImpl libraryElement,
     required Source librarySource,
     required Source unitSource,
@@ -1285,7 +1329,8 @@
       lineInfo: LineInfo([0]),
     );
 
-    var unitReference = unitContainerRef.getChild('${unitSource.uri}');
+    final unitContainerRef = _reference.getChild('@unit');
+    final unitReference = unitContainerRef.getChild('${unitSource.uri}');
     unitElement.setLinkedData(
       unitReference,
       CompilationUnitElementLinkedData(
@@ -1423,6 +1468,10 @@
     this._reader,
   );
 
+  LibraryElementImpl libraryOfUri(Uri uri) {
+    return _elementFactory.libraryOfUri2(uri);
+  }
+
   int readByte() {
     return _reader.readByte();
   }
diff --git a/pkg/analyzer/lib/src/summary2/bundle_writer.dart b/pkg/analyzer/lib/src/summary2/bundle_writer.dart
index 1008851..1d6844a 100644
--- a/pkg/analyzer/lib/src/summary2/bundle_writer.dart
+++ b/pkg/analyzer/lib/src/summary2/bundle_writer.dart
@@ -102,7 +102,7 @@
     _writeFeatureSet(libraryElement.featureSet);
     _writeLanguageVersion(libraryElement.languageVersion);
     _resolutionSink._writeAnnotationList(libraryElement.metadata);
-    _writeList(libraryElement.imports, _writeImportElement);
+    _writeList(libraryElement.imports2, _writeImportElement);
     _writeList(libraryElement.exports, _writeExportElement);
     for (final partElement in libraryElement.parts2) {
       _resolutionSink._writeAnnotationList(partElement.metadata);
@@ -190,8 +190,8 @@
     }
 
     if (element is DirectiveUriWithLibrary) {
-      // TODO(scheglov) implement
-      throw UnimplementedError();
+      _sink.writeByte(DirectiveUriKind.withLibrary.index);
+      writeWithSource(element);
     } else if (element is DirectiveUriWithUnit) {
       _sink.writeByte(DirectiveUriKind.withUnit.index);
       writeWithSource(element);
@@ -317,14 +317,25 @@
     });
   }
 
-  void _writeImportElement(ImportElement element) {
-    element as ImportElementImpl;
-    ImportElementFlags.write(_sink, element);
-    _sink._writeOptionalStringReference(element.uri);
-    _sink._writeOptionalStringReference(element.prefix?.name);
-    _sink.writeList(element.combinators, _writeNamespaceCombinator);
+  void _writeImportElement(ImportElement2 element) {
+    element as ImportElement2Impl;
     _resolutionSink._writeAnnotationList(element.metadata);
-    _resolutionSink.writeElement(element.importedLibrary);
+    _writeDirectiveUri(element.uri);
+    _writeImportElementPrefix(element.prefix);
+    _sink.writeList(element.combinators, _writeNamespaceCombinator);
+    ImportElementFlags.write(_sink, element);
+  }
+
+  void _writeImportElementPrefix(ImportElementPrefix? prefix) {
+    if (prefix is DeferredImportElementPrefix) {
+      _sink.writeByte(ImportElementPrefixKind.isDeferred.index);
+      _sink._writeStringReference(prefix.element.name);
+    } else if (prefix is ImportElementPrefix) {
+      _sink.writeByte(ImportElementPrefixKind.isNotDeferred.index);
+      _sink._writeStringReference(prefix.element.name);
+    } else {
+      _sink.writeByte(ImportElementPrefixKind.isNull.index);
+    }
   }
 
   void _writeLanguageVersion(LibraryLanguageVersion version) {
diff --git a/pkg/analyzer/lib/src/summary2/element_builder.dart b/pkg/analyzer/lib/src/summary2/element_builder.dart
index e53f62f..da83e3f 100644
--- a/pkg/analyzer/lib/src/summary2/element_builder.dart
+++ b/pkg/analyzer/lib/src/summary2/element_builder.dart
@@ -26,9 +26,8 @@
   final CompilationUnitElementImpl _unitElement;
 
   final _exports = <ExportElement>[];
-  final _imports = <ImportElement>[];
   var _isFirstLibraryDirective = true;
-  var _hasCoreImport = false;
+  var _importDirectiveIndex = 0;
   var _partDirectiveIndex = 0;
 
   _EnclosingContext _enclosingContext;
@@ -66,17 +65,6 @@
 
     _libraryElement.exports = _exports;
 
-    if (!_hasCoreImport) {
-      final dartCore = _linker.elementFactory.dartCoreElement;
-      _imports.add(
-        ImportElementImpl(-1)
-          ..importedLibrary = dartCore
-          ..isSynthetic = true
-          ..uri = 'dart:core',
-      );
-    }
-    _libraryElement.imports = _imports;
-
     if (_isFirstLibraryDirective) {
       _isFirstLibraryDirective = false;
       var firstDirective = unit.directives.firstOrNull;
@@ -757,39 +745,11 @@
 
   @override
   void visitImportDirective(covariant ImportDirectiveImpl node) {
-    var uriStr = node.uri.stringValue;
-
-    var element = ImportElementImpl(node.importKeyword.offset);
-    element.combinators = _buildCombinators(node.combinators);
-
-    try {
-      element.importedLibrary = _selectLibrary(node);
-    } on ArgumentError {
-      // TODO(scheglov) Remove this when using `ImportDirectiveState`.
-    }
-
-    element.isDeferred = node.deferredKeyword != null;
-    element.metadata = _buildAnnotations(node.metadata);
-    element.uri = uriStr;
-
-    var prefixNode = node.prefix;
-    if (prefixNode != null) {
-      element.prefix = PrefixElementImpl(
-        prefixNode.name,
-        prefixNode.offset,
-        reference: _libraryBuilder.reference
-            .getChild('@prefix')
-            .getChild(prefixNode.name),
-      );
-    }
-
-    node.element = element;
-
-    _imports.add(element);
-
-    if (uriStr == 'dart:core') {
-      _hasCoreImport = true;
-    }
+    final index = _importDirectiveIndex++;
+    final importElement = _libraryElement.imports2[index];
+    importElement as ImportElement2Impl;
+    importElement.metadata = _buildAnnotations(node.metadata);
+    node.element = importElement;
   }
 
   @override
diff --git a/pkg/analyzer/lib/src/summary2/element_flags.dart b/pkg/analyzer/lib/src/summary2/element_flags.dart
index a8ba866..eae765c 100644
--- a/pkg/analyzer/lib/src/summary2/element_flags.dart
+++ b/pkg/analyzer/lib/src/summary2/element_flags.dart
@@ -151,18 +151,15 @@
 }
 
 class ImportElementFlags {
-  static const int _isDeferred = 1 << 0;
-  static const int _isSynthetic = 1 << 1;
+  static const int _isSynthetic = 1 << 0;
 
-  static void read(SummaryDataReader reader, ImportElementImpl element) {
+  static void read(SummaryDataReader reader, ImportElement2Impl element) {
     var byte = reader.readByte();
-    element.isDeferred = (byte & _isDeferred) != 0;
     element.isSynthetic = (byte & _isSynthetic) != 0;
   }
 
-  static void write(BufferedSink sink, ImportElementImpl element) {
+  static void write(BufferedSink sink, ImportElement2Impl element) {
     var result = 0;
-    result |= element.isDeferred ? _isDeferred : 0;
     result |= element.isSynthetic ? _isSynthetic : 0;
     sink.writeByte(result);
   }
diff --git a/pkg/analyzer/lib/src/summary2/informative_data.dart b/pkg/analyzer/lib/src/summary2/informative_data.dart
index 086c90d..b684e30 100644
--- a/pkg/analyzer/lib/src/summary2/informative_data.dart
+++ b/pkg/analyzer/lib/src/summary2/informative_data.dart
@@ -448,16 +448,16 @@
       element.documentationComment = info.docComment;
     }
 
-    forCorrespondingPairs<ImportElement, _InfoImport>(
+    forCorrespondingPairs<ImportElement2, _InfoImport>(
       element.imports_unresolved,
       info.imports,
       (element, info) {
-        element as ImportElementImpl;
+        element as ImportElement2Impl;
         element.nameOffset = info.nameOffset;
 
-        var prefix = element.prefix;
-        if (prefix is PrefixElementImpl) {
-          prefix.nameOffset = info.prefixOffset;
+        final prefixElement = element.prefix?.element;
+        if (prefixElement is PrefixElementImpl) {
+          prefixElement.nameOffset = info.prefixOffset;
         }
 
         _applyToCombinators(element.combinators, info.combinators);
@@ -487,7 +487,7 @@
       info.libraryConstantOffsets,
       (applier) {
         applier.applyToMetadata(element);
-        applier.applyToDirectives(element.imports);
+        applier.applyToImports(element.imports2);
         applier.applyToDirectives(element.exports);
         applier.applyToPartDirectives(element.parts2);
       },
@@ -1691,6 +1691,12 @@
     }
   }
 
+  void applyToImports(List<ImportElement2> elements) {
+    for (var element in elements) {
+      applyToMetadata(element);
+    }
+  }
+
   void applyToMetadata(Element element) {
     for (var annotation in element.metadata) {
       var node = (annotation as ElementAnnotationImpl).annotationAst;
diff --git a/pkg/analyzer/lib/src/summary2/library_builder.dart b/pkg/analyzer/lib/src/summary2/library_builder.dart
index 83db6a1..2e9b765 100644
--- a/pkg/analyzer/lib/src/summary2/library_builder.dart
+++ b/pkg/analyzer/lib/src/summary2/library_builder.dart
@@ -132,6 +132,8 @@
   /// Build elements for declarations in the library units, add top-level
   /// declarations to the local scope, for combining into export scopes.
   void buildElements() {
+    element.imports2 = kind.imports.map(_buildImport).toList();
+
     for (var linkingUnit in units) {
       var elementBuilder = ElementBuilder(
         libraryBuilder: this,
@@ -402,6 +404,121 @@
     }
   }
 
+  ImportElement2Impl _buildImport(ImportDirectiveState state) {
+    final importPrefix = state.directive.prefix.mapOrNull((unlinked) {
+      if (unlinked.deferredOffset != null) {
+        return DeferredImportElementPrefixImpl(
+          element: _buildPrefix(
+            name: unlinked.name,
+            nameOffset: unlinked.nameOffset,
+          ),
+        );
+      } else {
+        return ImportElementPrefixImpl(
+          element: _buildPrefix(
+            name: unlinked.name,
+            nameOffset: unlinked.nameOffset,
+          ),
+        );
+      }
+    });
+
+    final combinators = state.directive.combinators.map((unlinked) {
+      if (unlinked.isShow) {
+        return ShowElementCombinatorImpl()
+          ..offset = unlinked.keywordOffset
+          ..end = unlinked.endOffset
+          ..shownNames = unlinked.names;
+      } else {
+        // TODO(scheglov) Why no offsets?
+        return HideElementCombinatorImpl()..hiddenNames = unlinked.names;
+      }
+    }).toList();
+
+    final DirectiveUri uri;
+    if (state is ImportDirectiveWithFile) {
+      final importedLibraryKind = state.importedLibrary;
+      if (importedLibraryKind != null) {
+        final importedFile = importedLibraryKind.file;
+        final importedUri = importedFile.uri;
+        final elementFactory = linker.elementFactory;
+        final importedLibrary = elementFactory.libraryOfUri2(importedUri);
+        uri = DirectiveUriWithLibraryImpl(
+          relativeUriString: state.selectedUri.relativeUriStr,
+          relativeUri: state.selectedUri.relativeUri,
+          source: importedLibrary.source,
+          library: importedLibrary,
+        );
+      } else {
+        uri = DirectiveUriWithSourceImpl(
+          relativeUriString: state.selectedUri.relativeUriStr,
+          relativeUri: state.selectedUri.relativeUri,
+          source: state.importedSource,
+        );
+      }
+    } else if (state is ImportDirectiveWithInSummarySource) {
+      final importedLibrarySource = state.importedLibrarySource;
+      if (importedLibrarySource != null) {
+        final importedUri = importedLibrarySource.uri;
+        final elementFactory = linker.elementFactory;
+        final importedLibrary = elementFactory.libraryOfUri2(importedUri);
+        uri = DirectiveUriWithLibraryImpl(
+          relativeUriString: state.selectedUri.relativeUriStr,
+          relativeUri: state.selectedUri.relativeUri,
+          source: importedLibrary.source,
+          library: importedLibrary,
+        );
+      } else {
+        uri = DirectiveUriWithSourceImpl(
+          relativeUriString: state.selectedUri.relativeUriStr,
+          relativeUri: state.selectedUri.relativeUri,
+          source: state.importedSource,
+        );
+      }
+    } else {
+      final selectedUri = state.selectedUri;
+      if (selectedUri is file_state.DirectiveUriWithUri) {
+        uri = DirectiveUriWithRelativeUriImpl(
+          relativeUriString: selectedUri.relativeUriStr,
+          relativeUri: selectedUri.relativeUri,
+        );
+      } else if (selectedUri is file_state.DirectiveUriWithString) {
+        uri = DirectiveUriWithRelativeUriStringImpl(
+          relativeUriString: selectedUri.relativeUriStr,
+        );
+      } else {
+        uri = DirectiveUriImpl();
+      }
+    }
+
+    return ImportElement2Impl(
+      importKeywordOffset: state.directive.importKeywordOffset,
+      uri: uri,
+      prefix: importPrefix,
+    )
+      ..combinators = combinators
+      ..isSynthetic = state.isSyntheticDartCore;
+  }
+
+  PrefixElementImpl _buildPrefix({
+    required String name,
+    required int nameOffset,
+  }) {
+    final reference = this.reference.getChild('@prefix').getChild(name);
+    final existing = reference.element;
+    if (existing is PrefixElementImpl) {
+      return existing;
+    } else {
+      final result = PrefixElementImpl(
+        name,
+        nameOffset,
+        reference: reference,
+      );
+      element.encloseElement(result);
+      return result;
+    }
+  }
+
   /// These elements are implicitly declared in `dart:core`.
   void _declareDartCoreDynamicNever() {
     if (reference.name == 'dart:core') {
@@ -592,3 +709,10 @@
     super.visitElement(element);
   }
 }
+
+extension<T> on T? {
+  R? mapOrNull<R>(R Function(T) mapper) {
+    final self = this;
+    return self != null ? mapper(self) : null;
+  }
+}
diff --git a/pkg/analyzer/lib/src/summary2/macro_application.dart b/pkg/analyzer/lib/src/summary2/macro_application.dart
index 8260593..8fcd68c 100644
--- a/pkg/analyzer/lib/src/summary2/macro_application.dart
+++ b/pkg/analyzer/lib/src/summary2/macro_application.dart
@@ -258,8 +258,8 @@
       constructorName = annotation.constructorName?.name;
     } else if (nameNode is PrefixedIdentifier) {
       final importPrefixCandidate = nameNode.prefix.name;
-      final hasImportPrefix = libraryBuilder.element.imports
-          .any((import) => import.prefix?.name == importPrefixCandidate);
+      final hasImportPrefix = libraryBuilder.element.imports2.any(
+          (import) => import.prefix?.element.name == importPrefixCandidate);
       if (hasImportPrefix) {
         prefix = importPrefixCandidate;
         name = nameNode.identifier.name;
@@ -273,8 +273,8 @@
       throw StateError('${nameNode.runtimeType} $nameNode');
     }
 
-    for (final import in libraryBuilder.element.imports) {
-      if (import.prefix?.name != prefix) {
+    for (final import in libraryBuilder.element.imports2) {
+      if (import.prefix?.element.name != prefix) {
         continue;
       }
 
diff --git a/pkg/analyzer/lib/src/test_utilities/find_element.dart b/pkg/analyzer/lib/src/test_utilities/find_element.dart
index d2923f5..3d7b0c3 100644
--- a/pkg/analyzer/lib/src/test_utilities/find_element.dart
+++ b/pkg/analyzer/lib/src/test_utilities/find_element.dart
@@ -50,10 +50,10 @@
     throw StateError('Not found: $name');
   }
 
-  ImportElement import(String targetUri, {bool mustBeUnique = true}) {
-    ImportElement? importElement;
+  ImportElement2 import(String targetUri, {bool mustBeUnique = true}) {
+    ImportElement2? importElement;
 
-    for (var import in libraryElement.imports) {
+    for (var import in libraryElement.imports2) {
       var importedUri = import.importedLibrary?.source.uri.toString();
       if (importedUri == targetUri) {
         if (importElement == null) {
@@ -243,10 +243,10 @@
   }
 
   PrefixElement prefix(String name) {
-    for (var import_ in libraryElement.imports) {
-      var prefix = import_.prefix;
-      if (prefix?.name == name) {
-        return prefix!;
+    for (var import_ in libraryElement.imports2) {
+      var prefix = import_.prefix?.element;
+      if (prefix != null && prefix.name == name) {
+        return prefix;
       }
     }
     throw StateError('Not found: $name');
@@ -311,13 +311,13 @@
 
 /// Helper for searching imported elements.
 class ImportFindElement extends _FindElementBase {
-  final ImportElement import;
+  final ImportElement2 import;
 
   ImportFindElement(this.import);
 
   LibraryElement get importedLibrary => import.importedLibrary!;
 
-  PrefixElement? get prefix => import.prefix;
+  PrefixElement? get prefix => import.prefix?.element;
 
   @override
   CompilationUnitElement get unitElement {
diff --git a/pkg/analyzer/test/generated/element_resolver_test.dart b/pkg/analyzer/test/generated/element_resolver_test.dart
index 7cac99c..af43c44 100644
--- a/pkg/analyzer/test/generated/element_resolver_test.dart
+++ b/pkg/analyzer/test/generated/element_resolver_test.dart
@@ -422,7 +422,7 @@
 ''', [
       error(HintCode.UNUSED_IMPORT, 7, 11),
     ]);
-    expect(findNode.import('dart:math').element!.importedLibrary!.name,
+    expect(findNode.import('dart:math').element2!.importedLibrary!.name,
         'dart.math');
   }
 
@@ -432,7 +432,7 @@
 ''', [
       error(HintCode.UNUSED_IMPORT, 7, 11),
     ]);
-    expect(findNode.import('dart:math').element!.importedLibrary!.name,
+    expect(findNode.import('dart:math').element2!.importedLibrary!.name,
         'dart.math');
   }
 
@@ -448,7 +448,7 @@
     ]);
     var importedVariables = findNode
         .import('lib1.dart')
-        .element!
+        .element2!
         .importedLibrary!
         .definingCompilationUnit
         .topLevelVariables;
diff --git a/pkg/analyzer/test/generated/resolver_test_case.dart b/pkg/analyzer/test/generated/resolver_test_case.dart
index 704b831..d0f7199 100644
--- a/pkg/analyzer/test/generated/resolver_test_case.dart
+++ b/pkg/analyzer/test/generated/resolver_test_case.dart
@@ -110,7 +110,7 @@
   void visitImportDirective(ImportDirective node) {
     // Not sure how to test the combinators given that it isn't an error if the
     // names are not defined.
-    _checkResolved(node, node.element, (node) => node is ImportElement);
+    _checkResolved(node, node.element2, (node) => node is ImportElement2);
     var prefix = node.prefix;
     if (prefix == null) {
       return;
diff --git a/pkg/analyzer/test/id_tests/assigned_variables_test.dart b/pkg/analyzer/test/id_tests/assigned_variables_test.dart
index 5107df9..309c55e 100644
--- a/pkg/analyzer/test/id_tests/assigned_variables_test.dart
+++ b/pkg/analyzer/test/id_tests/assigned_variables_test.dart
@@ -97,8 +97,8 @@
         node, () => super.visitVariableDeclaration(node));
   }
 
-  Set<String> _convertVars(Iterable<PromotableElement> x) =>
-      x.map((e) => e.name).toSet();
+  Set<String> _convertVars(Iterable<int> x) =>
+      x.map((e) => _currentAssignedVariables!.variableForKey(e).name).toSet();
 
   void _handlePossibleTopLevelDeclaration(
       AstNode node, void Function() callback) {
diff --git a/pkg/analyzer/test/src/dart/analysis/analyzer_state_printer.dart b/pkg/analyzer/test/src/dart/analysis/analyzer_state_printer.dart
index f55b678..89af3ef 100644
--- a/pkg/analyzer/test/src/dart/analysis/analyzer_state_printer.dart
+++ b/pkg/analyzer/test/src/dart/analysis/analyzer_state_printer.dart
@@ -235,7 +235,7 @@
             sink.write(' ${file.uri}');
           }
 
-          if (import.isSyntheticDartCoreImport) {
+          if (import.isSyntheticDartCore) {
             sink.write(' synthetic');
           }
           sink.writeln();
@@ -250,7 +250,7 @@
             sink.write(' notLibrary');
           }
 
-          if (import.isSyntheticDartCoreImport) {
+          if (import.isSyntheticDartCore) {
             sink.write(' synthetic');
           }
           sink.writeln();
@@ -258,7 +258,7 @@
           final uriStr = _stringOfUriStr(import.selectedUri.relativeUriStr);
           sink.write(_indent);
           sink.write('uri: $uriStr');
-          if (import.isSyntheticDartCoreImport) {
+          if (import.isSyntheticDartCore) {
             sink.write(' synthetic');
           }
           sink.writeln();
diff --git a/pkg/analyzer/test/src/dart/analysis/driver_resolution_test.dart b/pkg/analyzer/test/src/dart/analysis/driver_resolution_test.dart
index 1901bb3..b2fac8c 100644
--- a/pkg/analyzer/test/src/dart/analysis/driver_resolution_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/driver_resolution_test.dart
@@ -412,8 +412,8 @@
     await resolveTestFile();
     CompilationUnit unit = result.unit;
 
-    ImportElement aImport = unit.declaredElement!.library.imports[0];
-    PrefixElement aPrefix = aImport.prefix!;
+    final aImport = unit.declaredElement!.library.imports2[0];
+    PrefixElement aPrefix = aImport.prefix!.element;
     LibraryElement aLibrary = aImport.importedLibrary!;
 
     CompilationUnitElement aUnitElement = aLibrary.definingCompilationUnit;
@@ -451,8 +451,8 @@
     await resolveTestFile();
     CompilationUnit unit = result.unit;
 
-    ImportElement aImport = unit.declaredElement!.library.imports[0];
-    PrefixElement aPrefix = aImport.prefix!;
+    final aImport = unit.declaredElement!.library.imports2[0];
+    PrefixElement aPrefix = aImport.prefix!.element;
     LibraryElement aLibrary = aImport.importedLibrary!;
 
     CompilationUnitElement aUnitElement = aLibrary.definingCompilationUnit;
@@ -492,8 +492,8 @@
     await resolveTestFile();
     CompilationUnit unit = result.unit;
 
-    ImportElement aImport = unit.declaredElement!.library.imports[0];
-    PrefixElement aPrefix = aImport.prefix!;
+    final aImport = unit.declaredElement!.library.imports2[0];
+    PrefixElement aPrefix = aImport.prefix!.element;
     LibraryElement aLibrary = aImport.importedLibrary!;
 
     CompilationUnitElement aUnitElement = aLibrary.definingCompilationUnit;
@@ -533,8 +533,8 @@
     await resolveTestFile();
     CompilationUnit unit = result.unit;
 
-    ImportElement aImport = unit.declaredElement!.library.imports[0];
-    PrefixElement aPrefix = aImport.prefix!;
+    final aImport = unit.declaredElement!.library.imports2[0];
+    PrefixElement aPrefix = aImport.prefix!.element;
     LibraryElement aLibrary = aImport.importedLibrary!;
 
     CompilationUnitElement aUnitElement = aLibrary.definingCompilationUnit;
@@ -1443,7 +1443,7 @@
     assertInvokeType(invocation, 'Future<dynamic> Function()');
 
     var target = invocation.target as SimpleIdentifier;
-    assertElement(target, import.prefix);
+    assertElement(target, import.prefix?.element);
     assertType(target, null);
 
     var name = invocation.methodName;
@@ -1469,7 +1469,7 @@
     assertInvokeType(invocation, 'Future<dynamic> Function()');
 
     var target = invocation.target as SimpleIdentifier;
-    assertElement(target, import.prefix);
+    assertElement(target, import.prefix?.element);
     assertType(target, null);
 
     var name = invocation.methodName;
@@ -1500,7 +1500,7 @@
     assertType(prefixed, 'Future<dynamic> Function()');
 
     var prefix = prefixed.prefix;
-    assertElement(prefix, import.prefix);
+    assertElement(prefix, import.prefix?.element);
     assertType(prefix, null);
 
     var identifier = prefixed.identifier;
@@ -1528,7 +1528,7 @@
       assertElement(prefixed, v.getter);
       assertType(prefixed, 'int');
 
-      assertElement(prefixed.prefix, import.prefix);
+      assertElement(prefixed.prefix, import.prefix?.element);
       assertType(prefixed.prefix, null);
 
       assertElement(prefixed.identifier, v.getter);
@@ -1540,7 +1540,7 @@
       assertElementNull(prefixed);
       assertTypeNull(prefixed);
 
-      assertElement(prefixed.prefix, import.prefix);
+      assertElement(prefixed.prefix, import.prefix?.element);
       assertType(prefixed.prefix, null);
 
       assertUnresolvedSimpleIdentifier(prefixed.identifier);
@@ -2210,7 +2210,7 @@
     await resolveTestFile();
     CompilationUnit unit = result.unit;
 
-    ImportElement aImport = unit.declaredElement!.library.imports[0];
+    final aImport = unit.declaredElement!.library.imports2[0];
     LibraryElement aLibrary = aImport.importedLibrary!;
 
     ClassElement cElement = aLibrary.getType('C')!;
@@ -2241,7 +2241,7 @@
 
       SimpleIdentifier typePrefix = typeIdentifier.prefix;
       expect(typePrefix.name, 'p');
-      expect(typePrefix.staticElement, same(aImport.prefix));
+      expect(typePrefix.staticElement, same(aImport.prefix?.element));
       expect(typePrefix.staticType, isNull);
 
       expect(typeIdentifier.identifier.staticElement, same(cElement));
@@ -2271,7 +2271,7 @@
 
       SimpleIdentifier typePrefix = typeIdentifier.prefix;
       expect(typePrefix.name, 'p');
-      expect(typePrefix.staticElement, same(aImport.prefix));
+      expect(typePrefix.staticElement, same(aImport.prefix?.element));
       expect(typePrefix.staticType, isNull);
 
       expect(typeIdentifier.identifier.staticElement, same(cElement));
@@ -2303,7 +2303,7 @@
 
       SimpleIdentifier typePrefix = typeIdentifier.prefix;
       expect(typePrefix.name, 'p');
-      expect(typePrefix.staticElement, same(aImport.prefix));
+      expect(typePrefix.staticElement, same(aImport.prefix?.element));
       expect(typePrefix.staticType, isNull);
 
       expect(typeIdentifier.identifier.staticElement, same(cElement));
@@ -2898,7 +2898,7 @@
       findNode.namedType('a.Future'),
       futureElement,
       'Future<int>',
-      expectedPrefix: findElement.import('dart:async').prefix,
+      expectedPrefix: findElement.import('dart:async').prefix?.element,
     );
     assertNamedType(findNode.namedType('int>'), intElement, 'int');
   }
@@ -3231,7 +3231,7 @@
     await resolveTestFile();
     expect(result.errors, isNotEmpty);
 
-    ImportElement import = findNode.import('dart:math').element!;
+    final import = findNode.import('dart:math').element2!;
 
     var pRef = findNode.simple('p(a)');
     assertElement(pRef, import.prefix);
@@ -3440,14 +3440,14 @@
     await resolveTestFile();
     expect(result.errors, isNotEmpty);
 
-    ImportElement import = findNode.import('dart:math').element!;
+    final import = findNode.import('dart:math').element2!;
 
     var invocation = findNode.methodInvocation('p(a)');
     expect(invocation.staticType, isDynamicType);
     assertUnresolvedInvokeType(invocation.staticInvokeType!);
 
     var pRef = invocation.methodName;
-    assertElement(pRef, import.prefix);
+    assertElement(pRef, import.prefix?.element);
     assertTypeDynamic(pRef);
 
     var aRef = findNode.simple('a);');
@@ -3613,7 +3613,7 @@
     await resolveTestFile();
     expect(result.errors, isNotEmpty);
 
-    ImportElement import = findNode.import('a.dart').element!;
+    final import = findNode.import('a.dart').element2!;
     var tElement = import.importedLibrary!.publicNamespace.get('T');
 
     var prefixedName = findNode.prefixed('p.T');
@@ -3681,7 +3681,7 @@
     await resolveTestFile();
     expect(result.errors, isNotEmpty);
 
-    ImportElement import = findNode.import('a.dart').element!;
+    final import = findNode.import('a.dart').element2!;
     var tElement = import.importedLibrary!.publicNamespace.get('T');
 
     var prefixedName = findNode.prefixed('p.T');
@@ -5646,7 +5646,7 @@
       assertType(creation, 'C');
 
       assertNamedType(creation.constructorName.type, c, 'C',
-          expectedPrefix: import.prefix);
+          expectedPrefix: import.prefix?.element);
     }
 
     {
@@ -5656,7 +5656,7 @@
       assertType(creation, 'C');
 
       assertNamedType(creation.constructorName.type, c, 'C',
-          expectedPrefix: import.prefix);
+          expectedPrefix: import.prefix?.element);
       assertElement(creation.constructorName.name, namedConstructor);
     }
   }
@@ -5907,14 +5907,14 @@
 my.Future<int> a;
 ''');
     await resolveTestFile();
-    ImportElement myImport = result.libraryElement.imports[0];
+    final myImport = result.libraryElement.imports2[0];
 
     var intRef = findNode.namedType('int> a');
     assertNamedType(intRef, intElement, 'int');
 
     var futureRef = findNode.namedType('my.Future<int> a');
     assertNamedType(futureRef, futureElement, 'Future<int>',
-        expectedPrefix: myImport.prefix);
+        expectedPrefix: myImport.prefix?.element);
   }
 
   test_postfix_increment_of_non_generator() async {
@@ -6163,8 +6163,8 @@
 //    expect(result.errors, isEmpty);
 
     var unitElement = result.unit.declaredElement!;
-    ImportElement myImport = unitElement.library.imports[0];
-    PrefixElement myPrefix = myImport.prefix!;
+    final myImport = unitElement.library.imports2[0];
+    PrefixElement myPrefix = myImport.prefix!.element;
 
     var myLibrary = myImport.importedLibrary!;
     var myUnit = myLibrary.definingCompilationUnit;
@@ -7751,8 +7751,8 @@
     await resolveTestFile();
     CompilationUnit unit = result.unit;
 
-    ImportElement bImport = unit.declaredElement!.library.imports[0];
-    ImportElement cImport = unit.declaredElement!.library.imports[1];
+    final bImport = unit.declaredElement!.library.imports2[0];
+    final cImport = unit.declaredElement!.library.imports2[1];
 
     LibraryElement bLibrary = bImport.importedLibrary!;
     LibraryElement aLibrary = bLibrary.exports[0].exportedLibrary!;
@@ -7766,7 +7766,10 @@
       expect(typeIdentifier.staticElement, aClass);
 
       expect(typeIdentifier.prefix.name, 'b');
-      expect(typeIdentifier.prefix.staticElement, same(bImport.prefix));
+      expect(
+        typeIdentifier.prefix.staticElement,
+        same(bImport.prefix?.element),
+      );
 
       expect(typeIdentifier.identifier.staticElement, aClass);
     }
@@ -7779,7 +7782,10 @@
       expect(typeIdentifier.staticElement, aClass);
 
       expect(typeIdentifier.prefix.name, 'c');
-      expect(typeIdentifier.prefix.staticElement, same(cImport.prefix));
+      expect(
+        typeIdentifier.prefix.staticElement,
+        same(cImport.prefix?.element),
+      );
 
       expect(typeIdentifier.identifier.staticElement, aClass);
     }
@@ -7913,7 +7919,7 @@
     expect(result.errors, isNotEmpty);
 
     var unitElement = result.unit.declaredElement!;
-    var foo = unitElement.library.imports[0].prefix;
+    var foo = unitElement.library.imports2[0].prefix?.element;
 
     List<Statement> statements = _getMainStatements(result);
     var statement = statements[0] as ExpressionStatement;
@@ -7997,8 +8003,8 @@
     expect(result.errors, isNotEmpty);
 
     var unitElement = result.unit.declaredElement!;
-    var mathImport = unitElement.library.imports[0];
-    var foo = mathImport.prefix;
+    var mathImport = unitElement.library.imports2[0];
+    var foo = mathImport.prefix?.element;
 
     List<Statement> statements = _getMainStatements(result);
     var statement = statements[0] as ExpressionStatement;
@@ -8043,8 +8049,8 @@
     expect(result.errors, isNotEmpty);
 
     var unitElement = result.unit.declaredElement!;
-    var mathImport = unitElement.library.imports[0];
-    var foo = mathImport.prefix;
+    var mathImport = unitElement.library.imports2[0];
+    var foo = mathImport.prefix?.element;
     var randomElement = mathImport.importedLibrary!.getType('Random')!;
 
     List<Statement> statements = _getMainStatements(result);
diff --git a/pkg/analyzer/test/src/dart/analysis/driver_test.dart b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
index 0149bed..5226986 100644
--- a/pkg/analyzer/test/src/dart/analysis/driver_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
@@ -2107,7 +2107,7 @@
     ResolvedUnitResult result = await driver.getResultValid(testFile);
     expect(result.path, testFile);
     // Has only imports for valid URIs.
-    List<ImportElement> imports = result.libraryElement.imports;
+    final imports = result.libraryElement.imports2;
     expect(imports.map((import) {
       return import.importedLibrary?.source.uri.toString();
     }), ['dart:async', null, 'dart:math', 'dart:core']);
@@ -3568,9 +3568,9 @@
         '$unit');
   }
 
-  ImportElement _getImportElement(CompilationUnit unit, int directiveIndex) {
+  ImportElement2 _getImportElement(CompilationUnit unit, int directiveIndex) {
     var import = unit.directives[directiveIndex] as ImportDirective;
-    return import.element!;
+    return import.element2!;
   }
 
   Source _getImportSource(CompilationUnit unit, int directiveIndex) {
diff --git a/pkg/analyzer/test/src/dart/analysis/file_state_test.dart b/pkg/analyzer/test/src/dart/analysis/file_state_test.dart
index 1336d79..29bb1a8 100644
--- a/pkg/analyzer/test/src/dart/analysis/file_state_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/file_state_test.dart
@@ -1562,7 +1562,7 @@
 
     final coreKind = core.t1!.kind as LibraryFileStateKind;
     for (final import in coreKind.imports) {
-      if (import.isSyntheticDartCoreImport) {
+      if (import.isSyntheticDartCore) {
         fail('dart:core should not import itself');
       }
     }
diff --git a/pkg/analyzer/test/src/dart/analysis/search_test.dart b/pkg/analyzer/test/src/dart/analysis/search_test.dart
index c59203e..c95f1a0 100644
--- a/pkg/analyzer/test/src/dart/analysis/search_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/search_test.dart
@@ -1131,7 +1131,7 @@
 Random bar() => null;
 ''');
 
-    ImportElement element = findElement.import('dart:math');
+    final element = findElement.import('dart:math');
     var main = findElement.function('main');
     var bar = findElement.function('bar');
     var kind = SearchResultKind.REFERENCE;
@@ -1163,7 +1163,7 @@
   N4 = 0;
 }
 ''');
-    ImportElement element = findElement.import('package:test/a.dart');
+    final element = findElement.import('package:test/a.dart');
     var main = findElement.function('main');
     var kind = SearchResultKind.REFERENCE;
     var expected = [
@@ -1213,14 +1213,14 @@
     var kind = SearchResultKind.REFERENCE;
     var length = 'p.'.length;
     {
-      ImportElement element = findElement.import('dart:async');
+      final element = findElement.import('dart:async');
       var expected = [
         _expectId(main, kind, 'p.Future;', length: length),
       ];
       await _verifyReferences(element, expected);
     }
     {
-      ImportElement element = findElement.import('dart:math');
+      final element = findElement.import('dart:math');
       var expected = [
         _expectId(main, kind, 'p.Random', length: length),
       ];
@@ -1247,7 +1247,7 @@
   a.N4 = 0;
 }
 ''');
-    ImportElement element = findElement.import('package:test/a.dart');
+    final element = findElement.import('package:test/a.dart');
     var main = findElement.function('main');
     var kind = SearchResultKind.REFERENCE;
     var length = 'a.'.length;
@@ -2125,7 +2125,7 @@
   V(); // nq
 }
 ''');
-    ImportElement importElement = findNode.import('show V').element!;
+    final importElement = findNode.import('show V').element2!;
     CompilationUnitElement impUnit =
         importElement.importedLibrary!.definingCompilationUnit;
     TopLevelVariableElement variable = impUnit.topLevelVariables[0];
diff --git a/pkg/analyzer/test/src/dart/element/element_test.dart b/pkg/analyzer/test/src/dart/element/element_test.dart
index b69f6c9..3fa8eab 100644
--- a/pkg/analyzer/test/src/dart/element/element_test.dart
+++ b/pkg/analyzer/test/src/dart/element/element_test.dart
@@ -2,21 +2,17 @@
 // 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/analysis/features.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/constant/value.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/dart/element/nullability_suffix.dart';
 import 'package:analyzer/dart/element/type.dart';
-import 'package:analyzer/src/dart/analysis/session.dart';
 import 'package:analyzer/src/dart/element/element.dart';
 import 'package:analyzer/src/dart/element/type.dart';
-import 'package:analyzer/src/generated/engine.dart' show AnalysisContext;
 import 'package:analyzer/src/generated/testing/element_factory.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
-import '../../../generated/test_analysis_context.dart';
 import '../../../generated/test_support.dart';
 import '../../../generated/type_system_test.dart';
 import '../resolution/context_collection_resolution.dart';
@@ -33,7 +29,6 @@
     defineReflectiveTests(CompilationUnitElementImplTest);
     defineReflectiveTests(ElementLocationImplTest);
     defineReflectiveTests(ElementImplTest);
-    defineReflectiveTests(LibraryElementImplTest);
     defineReflectiveTests(TopLevelVariableElementImplTest);
     defineReflectiveTests(UniqueLocationTest);
   });
@@ -1246,75 +1241,6 @@
 }
 
 @reflectiveTest
-class LibraryElementImplTest {
-  void test_getImportedLibraries() {
-    AnalysisContext context = TestAnalysisContext();
-    LibraryElementImpl library1 = ElementFactory.library(context, "l1");
-    LibraryElementImpl library2 = ElementFactory.library(context, "l2");
-    LibraryElementImpl library3 = ElementFactory.library(context, "l3");
-    LibraryElementImpl library4 = ElementFactory.library(context, "l4");
-    PrefixElement prefixA = PrefixElementImpl('a', -1);
-    PrefixElement prefixB = PrefixElementImpl('b', -1);
-    List<ImportElementImpl> imports = [
-      ElementFactory.importFor(library2, null),
-      ElementFactory.importFor(library2, prefixB),
-      ElementFactory.importFor(library3, null),
-      ElementFactory.importFor(library3, prefixA),
-      ElementFactory.importFor(library3, prefixB),
-      ElementFactory.importFor(library4, prefixA)
-    ];
-    library1.imports = imports;
-    List<LibraryElement> libraries = library1.importedLibraries;
-    expect(libraries,
-        unorderedEquals(<LibraryElement>[library2, library3, library4]));
-  }
-
-  void test_getPrefixes() {
-    AnalysisContext context = TestAnalysisContext();
-    LibraryElementImpl library = ElementFactory.library(context, "l1");
-    PrefixElement prefixA = PrefixElementImpl('a', -1);
-    PrefixElement prefixB = PrefixElementImpl('b', -1);
-    List<ImportElementImpl> imports = [
-      ElementFactory.importFor(ElementFactory.library(context, "l2"), null),
-      ElementFactory.importFor(ElementFactory.library(context, "l3"), null),
-      ElementFactory.importFor(ElementFactory.library(context, "l4"), prefixA),
-      ElementFactory.importFor(ElementFactory.library(context, "l5"), prefixA),
-      ElementFactory.importFor(ElementFactory.library(context, "l6"), prefixB)
-    ];
-    library.imports = imports;
-    List<PrefixElement> prefixes = library.prefixes;
-    expect(prefixes, hasLength(2));
-    if (identical(prefixA, prefixes[0])) {
-      expect(prefixes[1], same(prefixB));
-    } else {
-      expect(prefixes[0], same(prefixB));
-      expect(prefixes[1], same(prefixA));
-    }
-  }
-
-  void test_setImports() {
-    AnalysisContext context = TestAnalysisContext();
-    LibraryElementImpl library = LibraryElementImpl(
-        context,
-        _AnalysisSessionMock(),
-        'l1',
-        -1,
-        0,
-        FeatureSet.latestLanguageVersion());
-    List<ImportElementImpl> expectedImports = [
-      ElementFactory.importFor(ElementFactory.library(context, "l2"), null),
-      ElementFactory.importFor(ElementFactory.library(context, "l3"), null)
-    ];
-    library.imports = expectedImports;
-    List<ImportElement> actualImports = library.imports;
-    expect(actualImports, hasLength(expectedImports.length));
-    for (int i = 0; i < actualImports.length; i++) {
-      expect(actualImports[i], same(expectedImports[i]));
-    }
-  }
-}
-
-@reflectiveTest
 class TopLevelVariableElementImplTest extends PubPackageResolutionTest {
   test_computeConstantValue() async {
     newFile('$testPackageLibPath/a.dart', r'''
@@ -1600,8 +1526,3 @@
     expect(_voidType.resolveToBound(objectNone), same(_voidType));
   }
 }
-
-class _AnalysisSessionMock implements AnalysisSessionImpl {
-  @override
-  noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
-}
diff --git a/pkg/analyzer/test/src/dart/resolution/type_literal_test.dart b/pkg/analyzer/test/src/dart/resolution/type_literal_test.dart
index 5f4fe3c..c43750d 100644
--- a/pkg/analyzer/test/src/dart/resolution/type_literal_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/type_literal_test.dart
@@ -41,7 +41,7 @@
       typeLiteral,
       findElement.importFind('package:test/a.dart').class_('C'),
       'C<int>',
-      expectedPrefix: findElement.import('package:test/a.dart').prefix,
+      expectedPrefix: findElement.import('package:test/a.dart').prefix?.element,
     );
   }
 
@@ -133,7 +133,7 @@
       typeLiteral,
       findElement.importFind('package:test/a.dart').typeAlias('CA'),
       'C<int>',
-      expectedPrefix: findElement.import('package:test/a.dart').prefix,
+      expectedPrefix: findElement.import('package:test/a.dart').prefix?.element,
     );
   }
 
diff --git a/pkg/analyzer/test/src/dart/resolution/type_name_test.dart b/pkg/analyzer/test/src/dart/resolution/type_name_test.dart
index 33d81f0..5c3ff04 100644
--- a/pkg/analyzer/test/src/dart/resolution/type_name_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/type_name_test.dart
@@ -666,7 +666,7 @@
       findNode.namedType('mycore.dynamic a;'),
       dynamicElement,
       'dynamic',
-      expectedPrefix: findElement.import('dart:core').prefix,
+      expectedPrefix: findElement.import('dart:core').prefix?.element,
     );
   }
 
diff --git a/pkg/analyzer/test/src/diagnostics/implements_deferred_class_test.dart b/pkg/analyzer/test/src/diagnostics/implements_deferred_class_test.dart
index d9e36f7..ac8fe41 100644
--- a/pkg/analyzer/test/src/diagnostics/implements_deferred_class_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/implements_deferred_class_test.dart
@@ -75,6 +75,6 @@
 
     var typeRef = findNode.namedType('Random {}');
     assertNamedType(typeRef, randomElement, 'Random',
-        expectedPrefix: mathImport.prefix);
+        expectedPrefix: mathImport.prefix?.element);
   }
 }
diff --git a/pkg/analyzer/test/src/diagnostics/mixin_super_class_constraint_deferred_class_test.dart b/pkg/analyzer/test/src/diagnostics/mixin_super_class_constraint_deferred_class_test.dart
index 4985163..21d8902 100644
--- a/pkg/analyzer/test/src/diagnostics/mixin_super_class_constraint_deferred_class_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/mixin_super_class_constraint_deferred_class_test.dart
@@ -32,6 +32,6 @@
 
     var typeRef = findNode.namedType('Random {}');
     assertNamedType(typeRef, randomElement, 'Random',
-        expectedPrefix: mathImport.prefix);
+        expectedPrefix: mathImport.prefix?.element);
   }
 }
diff --git a/pkg/analyzer/test/src/diagnostics/use_of_nullable_value_test.dart b/pkg/analyzer/test/src/diagnostics/use_of_nullable_value_test.dart
index b6cbd94..b4cce46 100644
--- a/pkg/analyzer/test/src/diagnostics/use_of_nullable_value_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/use_of_nullable_value_test.dart
@@ -407,8 +407,7 @@
 }
 ''', [
       error(CompileTimeErrorCode.UNCHECKED_PROPERTY_ACCESS_OF_NULLABLE_VALUE,
-          104, 1,
-          contextMessages: [message('/home/test/lib/test.dart', 56, 1)]),
+          104, 1),
     ]);
 
     if (isNullSafetyEnabled) {
@@ -660,8 +659,7 @@
 }
 ''', [
       error(CompileTimeErrorCode.UNCHECKED_PROPERTY_ACCESS_OF_NULLABLE_VALUE,
-          105, 1,
-          contextMessages: [message('/home/test/lib/test.dart', 56, 1)]),
+          105, 1),
     ]);
 
     assertResolvedNodeText(findNode.assignment('x += 1'), r'''
@@ -1594,8 +1592,7 @@
 }
 ''', [
       error(CompileTimeErrorCode.UNCHECKED_PROPERTY_ACCESS_OF_NULLABLE_VALUE,
-          105, 1,
-          contextMessages: [message('/home/test/lib/test.dart', 56, 1)]),
+          105, 1),
     ]);
     var propertyAccess1 = findNode.propertyAccess('b.a?.x; // 1');
     var propertyAccess2 = findNode.propertyAccess('b.a.x; // 2');
@@ -1664,8 +1661,7 @@
 }
 ''', [
       error(CompileTimeErrorCode.UNCHECKED_PROPERTY_ACCESS_OF_NULLABLE_VALUE,
-          148, 1,
-          contextMessages: [message('/home/test/lib/test.dart', 56, 1)]),
+          148, 1),
     ]);
     var propertyAccess1 = findNode.propertyAccess('x; // 1');
     var propertyAccess2 = findNode.propertyAccess('x; // 2');
@@ -1702,8 +1698,7 @@
 }
 ''', [
       error(CompileTimeErrorCode.UNCHECKED_PROPERTY_ACCESS_OF_NULLABLE_VALUE,
-          152, 1,
-          contextMessages: [message('/home/test/lib/test.dart', 101, 1)]),
+          152, 1),
     ]);
     var propertyAccess1 = findNode.propertyAccess('x; // 1');
     var propertyAccess2 = findNode.propertyAccess('x; // 2');
diff --git a/pkg/analyzer/test/src/summary/element_text.dart b/pkg/analyzer/test/src/summary/element_text.dart
index 2c955c4..1177e37 100644
--- a/pkg/analyzer/test/src/summary/element_text.dart
+++ b/pkg/analyzer/test/src/summary/element_text.dart
@@ -52,6 +52,7 @@
   bool withDisplayName = false,
   bool withExportScope = false,
   bool withNonSynthetic = false,
+  bool withSyntheticDartCoreImport = false,
 }) {
   var writer = _ElementWriter(
     selfUriStr: '${library.source.uri}',
@@ -59,6 +60,7 @@
     withDisplayName: withDisplayName,
     withExportScope: withExportScope,
     withNonSynthetic: withNonSynthetic,
+    withSyntheticDartCoreImport: withSyntheticDartCoreImport,
   );
   writer.writeLibraryElement(library);
 
@@ -124,6 +126,7 @@
   final bool withDisplayName;
   final bool withExportScope;
   final bool withNonSynthetic;
+  final bool withSyntheticDartCoreImport;
   final StringBuffer buffer = StringBuffer();
 
   String indent = '';
@@ -134,6 +137,7 @@
     required this.withDisplayName,
     required this.withExportScope,
     required this.withNonSynthetic,
+    required this.withSyntheticDartCoreImport,
   });
 
   void writeLibraryElement(LibraryElement e) {
@@ -154,7 +158,9 @@
       _writeDocumentation(e);
       _writeMetadata(e);
 
-      var imports = e.imports.where((import) => !import.isSynthetic).toList();
+      var imports = e.imports2
+          .where((import) => withSyntheticDartCoreImport || !import.isSynthetic)
+          .toList();
       _writeElements('imports', imports, _writeImportElement);
 
       _writeElements('exports', e.exports, _writeExportElement);
@@ -426,16 +432,18 @@
   }
 
   void _writeDirectiveUri(DirectiveUri uri) {
-    if (uri is DirectiveUriWithUnit) {
-      _writelnWithIndent('${uri.unit.source.uri}');
+    if (uri is DirectiveUriWithLibraryImpl) {
+      buffer.write('${uri.library.source.uri}');
+    } else if (uri is DirectiveUriWithUnit) {
+      buffer.write('${uri.unit.source.uri}');
     } else if (uri is DirectiveUriWithSource) {
-      _writelnWithIndent("source '${uri.source.uri}'");
+      buffer.write("source '${uri.source.uri}'");
     } else if (uri is DirectiveUriWithRelativeUri) {
-      _writelnWithIndent("relativeUri '${uri.relativeUri}'");
+      buffer.write("relativeUri '${uri.relativeUri}'");
     } else if (uri is DirectiveUriWithRelativeUriString) {
-      _writelnWithIndent("relativeUriString '${uri.relativeUriString}'");
+      buffer.write("relativeUriString '${uri.relativeUriString}'");
     } else {
-      _writelnWithIndent('noRelativeUriString');
+      buffer.write('noRelativeUriString');
     }
   }
 
@@ -571,24 +579,25 @@
     }
   }
 
-  void _writeImportElement(ImportElement e) {
+  void _writeImportElement(ImportElement2 e) {
     _writeIndentedLine(() {
-      _writeUri(e.importedLibrary?.source);
-      _writeIf(e.isDeferred, ' deferred');
-
-      var prefix = e.prefix;
-      if (prefix != null) {
-        buffer.write(' as ');
-        _writeName(prefix);
-      }
+      _writeDirectiveUri(e.uri);
+      _writeIf(e.isSynthetic, ' synthetic');
+      _writeImportElementPrefix(e.prefix);
     });
 
     _withIndent(() {
       _writeMetadata(e);
       _writeNamespaceCombinators(e.combinators);
     });
+  }
 
-    _assertNonSyntheticElementSelf(e);
+  void _writeImportElementPrefix(ImportElementPrefix? prefix) {
+    if (prefix != null) {
+      _writeIf(prefix is DeferredImportElementPrefix, ' deferred');
+      buffer.write(' as ');
+      _writeName(prefix.element);
+    }
   }
 
   void _writeIndentedLine(void Function() f) {
@@ -727,7 +736,9 @@
 
   void _writePartElement(PartElement e) {
     final uri = e.uri;
-    _writeDirectiveUri(uri);
+    _writeIndentedLine(() {
+      _writeDirectiveUri(e.uri);
+    });
 
     _withIndent(() {
       _writeMetadata(e);
diff --git a/pkg/analyzer/test/src/summary/elements_test.dart b/pkg/analyzer/test/src/summary/elements_test.dart
index 0f4a224..91fbd41 100644
--- a/pkg/analyzer/test/src/summary/elements_test.dart
+++ b/pkg/analyzer/test/src/summary/elements_test.dart
@@ -22788,7 +22788,7 @@
         staticType: null
       element: <null>
   imports
-    <unresolved>
+    relativeUri 'ht:'
       metadata
         Annotation
           atSign: @ @0
@@ -22832,8 +22832,16 @@
     addSource('$testPackageLibPath/a.dart', 'library a; class C {}');
     var library = await buildLibrary('import "a.dart" as a; a.C c;');
 
-    expect(library.imports[0].prefix!.nameOffset, 19);
-    expect(library.imports[0].prefix!.nameLength, 1);
+    // TODO(scheglov) Remove when removing `imports`.
+    {
+      // ignore: deprecated_member_use_from_same_package
+      final prefixElement = library.imports[0].prefix!;
+      expect(prefixElement.nameOffset, 19);
+      expect(prefixElement.nameLength, 1);
+    }
+
+    final prefixElement = library.imports2[0].prefix!.element;
+    expect(prefixElement.nameOffset, 19);
 
     checkElementText(library, r'''
 library
@@ -22860,9 +22868,9 @@
 class C {}
 class D extends p.C {} // Prevent "unused import" warning
 ''');
-    expect(library.imports, hasLength(2));
-    expect(library.imports[0].importedLibrary!.location, library.location);
-    expect(library.imports[1].importedLibrary!.isDartCore, true);
+    expect(library.imports2, hasLength(2));
+    expect(library.imports2[0].importedLibrary!.location, library.location);
+    expect(library.imports2[1].importedLibrary!.isDartCore, true);
     checkElementText(library, r'''
 library
   imports
@@ -22920,7 +22928,7 @@
     var library = await buildLibrary('''
 import "dart:math" show e, pi;
 ''');
-    var import = library.imports[0];
+    var import = library.imports2[0];
     var combinator = import.combinators[0] as ShowElementCombinator;
     expect(combinator.offset, 19);
     expect(combinator.end, 29);
@@ -22930,7 +22938,9 @@
     var library = await buildLibrary('''
 import 'foo.dart';
 ''');
-    expect(library.imports[0].uri, 'foo.dart');
+
+    final uri = library.imports2[0].uri as DirectiveUriWithLibrary;
+    expect(uri.relativeUriString, 'foo.dart');
   }
 
   test_imports() async {
@@ -25366,11 +25376,11 @@
     checkElementText(library, r'''
 library
   imports
-    <unresolved>
-    <unresolved>
+    relativeUriString ':[invaliduri]'
+    relativeUriString ':[invaliduri]:foo.dart'
     package:test/a1.dart
-    <unresolved>
-    <unresolved>
+    relativeUriString ':[invaliduri]'
+    relativeUriString ':[invaliduri]:foo.dart'
   exports
     <unresolved>
     <unresolved>
@@ -25424,6 +25434,121 @@
 ''');
   }
 
+  test_library_imports_noRelativeUriStr() async {
+    final library = await buildLibrary(r'''
+import '${'foo'}.dart';
+''');
+    checkElementText(library, r'''
+library
+  imports
+    noRelativeUriString
+  definingUnit
+''');
+  }
+
+  test_library_imports_prefix_importedLibraries() async {
+    final library = await buildLibrary(r'''
+import 'dart:async' as p1;
+import 'dart:collection' as p2;
+import 'dart:math' as p1;
+''');
+    final p1 = library.prefixes.singleWhere((prefix) => prefix.name == 'p1');
+    final import_async = library.imports2[0];
+    final import_math = library.imports2[2];
+    expect(p1.imports2, unorderedEquals([import_async, import_math]));
+  }
+
+  test_library_imports_syntheticDartCore() async {
+    final library = await buildLibrary('');
+    checkElementText(
+        library,
+        r'''
+library
+  imports
+    dart:core synthetic
+  definingUnit
+''',
+        withSyntheticDartCoreImport: true);
+  }
+
+  test_library_imports_withRelativeUri_emptyUriSelf() async {
+    final library = await buildLibrary(r'''
+import '';
+''');
+    checkElementText(library, r'''
+library
+  imports
+    package:test/test.dart
+  definingUnit
+''');
+  }
+
+  test_library_imports_withRelativeUri_noSource() async {
+    final library = await buildLibrary(r'''
+import 'foo:bar';
+''');
+    checkElementText(library, r'''
+library
+  imports
+    relativeUri 'foo:bar'
+  definingUnit
+''');
+  }
+
+  test_library_imports_withRelativeUri_notExists() async {
+    final library = await buildLibrary(r'''
+import 'a.dart';
+''');
+    checkElementText(library, r'''
+library
+  imports
+    package:test/a.dart
+  definingUnit
+''');
+  }
+
+  test_library_imports_withRelativeUri_notLibrary_augmentation() async {
+    newFile('$testPackageLibPath/a.dart', r'''
+library augment 'test.dart';
+''');
+    final library = await buildLibrary(r'''
+import 'a.dart';
+''');
+    checkElementText(library, r'''
+library
+  imports
+    source 'package:test/a.dart'
+  definingUnit
+''');
+  }
+
+  test_library_imports_withRelativeUri_notLibrary_part() async {
+    newFile('$testPackageLibPath/a.dart', r'''
+part of other.lib;
+''');
+    final library = await buildLibrary(r'''
+import 'a.dart';
+''');
+    checkElementText(library, r'''
+library
+  imports
+    source 'package:test/a.dart'
+  definingUnit
+''');
+  }
+
+  test_library_imports_withRelativeUriString() async {
+    final library = await buildLibrary(r'''
+import ':';
+''');
+    checkElementText(library, r'''
+library
+  imports
+    relativeUriString ':'
+  definingUnit
+''');
+  }
+
   test_library_name_with_spaces() async {
     var library = await buildLibrary('library foo . bar ;');
     checkElementText(library, r'''
@@ -25526,7 +25651,6 @@
   }
 
   test_library_parts_withRelativeUri_noSource() async {
-    newFile('$testPackageLibPath/a.dart', '');
     final library = await buildLibrary(r'''
 part 'foo:bar';
 ''');
@@ -25587,6 +25711,16 @@
 ''');
   }
 
+  test_library_prefixes() async {
+    final library = await buildLibrary(r'''
+import 'dart:async' as p1;
+import 'dart:collection' as p2;
+import 'dart:math' as p1;
+''');
+    final prefixNames = library.prefixes.map((e) => e.name).toList();
+    expect(prefixNames, unorderedEquals(['p1', 'p2']));
+  }
+
   test_localFunctions() async {
     var library = await buildLibrary(r'''
 f() {
@@ -37947,7 +38081,7 @@
 
   test_unresolved_import() async {
     var library = await buildLibrary("import 'foo.dart';", allowErrors: true);
-    var importedLibrary = library.imports[0].importedLibrary!;
+    var importedLibrary = library.imports2[0].importedLibrary!;
     expect(importedLibrary.loadLibraryFunction, isNotNull);
     expect(importedLibrary.publicNamespace, isNotNull);
     expect(importedLibrary.exportNamespace, isNotNull);
diff --git a/pkg/analyzer/test/src/summary/resolved_ast_printer.dart b/pkg/analyzer/test/src/summary/resolved_ast_printer.dart
index 44ad036..c88cc6a 100644
--- a/pkg/analyzer/test/src/summary/resolved_ast_printer.dart
+++ b/pkg/analyzer/test/src/summary/resolved_ast_printer.dart
@@ -667,7 +667,7 @@
     _writeln('ImportDirective');
     _withIndent(() {
       _writeNamedChildEntities(node);
-      _writeElement('element', node.element);
+      _writeElement('element', node.element2);
       _writeSource('selectedSource', node.selectedSource);
       _writeRaw('selectedUriContent', node.selectedUriContent);
       _writeRaw('uriContent', node.uriContent);
diff --git a/pkg/analyzer/test/util/element_type_matchers.dart b/pkg/analyzer/test/util/element_type_matchers.dart
index f286b3f..f05ab87 100644
--- a/pkg/analyzer/test/util/element_type_matchers.dart
+++ b/pkg/analyzer/test/util/element_type_matchers.dart
@@ -30,7 +30,7 @@
 
 const isHideElementCombinator = TypeMatcher<HideElementCombinator>();
 
-const isImportElement = TypeMatcher<ImportElement>();
+const isImportElement = TypeMatcher<ImportElement2>();
 
 const isLabelElement = TypeMatcher<LabelElement>();
 
diff --git a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
index 9ad3e64..b7bb1be 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
@@ -286,7 +286,7 @@
   @override
   void writeImportedName(List<Uri> uris, String name) {
     assert(uris.isNotEmpty);
-    var imports = <ImportElement>[];
+    var imports = <ImportElement2>[];
     for (var uri in uris) {
       imports.addAll(dartFileEditBuilder._getImportsForUri(uri));
     }
@@ -301,7 +301,7 @@
     } else {
       var prefix = import.prefix;
       if (prefix != null) {
-        write(prefix.displayName);
+        write(prefix.element.displayName);
         write('.');
       }
     }
@@ -1005,8 +1005,8 @@
 
   /// Given a list of [imports] that do, or can, make the [name] visible in
   /// scope, return the one that will lead to the cleanest code.
-  ImportElement? _getBestImportForName(
-      List<ImportElement> imports, String name) {
+  ImportElement2? _getBestImportForName(
+      List<ImportElement2> imports, String name) {
     if (imports.isEmpty) {
       return null;
     } else if (imports.length == 1) {
@@ -1160,7 +1160,7 @@
     if (import != null) {
       var prefix = import.prefix;
       if (prefix != null) {
-        write(prefix.displayName);
+        write(prefix.element.displayName);
         write('.');
       }
     } else {
@@ -1464,10 +1464,10 @@
       return ImportLibraryElementResultImpl(null);
     }
 
-    for (var import in resolvedUnit.libraryElement.imports) {
+    for (var import in resolvedUnit.libraryElement.imports2) {
       var importedLibrary = import.importedLibrary;
       if (importedLibrary != null && importedLibrary.source.uri == uri) {
-        return ImportLibraryElementResultImpl(import.prefix?.name);
+        return ImportLibraryElementResultImpl(import.prefix?.element.name);
       }
     }
 
@@ -1489,7 +1489,7 @@
     if (resolvedUnit.libraryElement.source.uri == uri) return false;
 
     // Existing import.
-    for (var import in resolvedUnit.libraryElement.imports) {
+    for (var import in resolvedUnit.libraryElement.imports2) {
       var importedLibrary = import.importedLibrary;
       if (importedLibrary != null && importedLibrary.source.uri == uri) {
         return true;
@@ -1752,8 +1752,8 @@
   /// Return the import element used to import the given [element] into the
   /// target library, or `null` if the element was not imported, such as when
   /// the element is declared in the same library.
-  ImportElement? _getImportElement(Element element) {
-    for (var import in resolvedUnit.libraryElement.imports) {
+  ImportElement2? _getImportElement(Element element) {
+    for (var import in resolvedUnit.libraryElement.imports2) {
       var definedNames = import.namespace.definedNames;
       if (definedNames.containsValue(element)) {
         return import;
@@ -1762,8 +1762,8 @@
     return null;
   }
 
-  Iterable<ImportElement> _getImportsForUri(Uri uri) sync* {
-    for (var import in resolvedUnit.libraryElement.imports) {
+  Iterable<ImportElement2> _getImportsForUri(Uri uri) sync* {
+    for (var import in resolvedUnit.libraryElement.imports2) {
       var importUri = import.importedLibrary?.source.uri;
       if (importUri == uri) {
         yield import;
diff --git a/pkg/analyzer_plugin/lib/utilities/navigation/navigation_dart.dart b/pkg/analyzer_plugin/lib/utilities/navigation/navigation_dart.dart
index dada67b..23df1dd 100644
--- a/pkg/analyzer_plugin/lib/utilities/navigation/navigation_dart.dart
+++ b/pkg/analyzer_plugin/lib/utilities/navigation/navigation_dart.dart
@@ -26,17 +26,39 @@
     unit.accept(visitor);
   } else {
     var node = _getNodeForRange(unit, offset, length);
-    // Take the outer-most node that shares this offset/length so that we get
-    // things like ConstructorName instead of SimpleIdentifier.
-    // https://github.com/dart-lang/sdk/issues/46725
     if (node != null) {
-      node = _getOutermostNode(node);
+      node = _getNavigationTargetNode(node);
     }
     node?.accept(visitor);
   }
   return collector;
 }
 
+/// Gets the nearest node that should be used for navigation.
+///
+/// This is usually the outermost node with the same offset as node but in some
+/// cases may be a different ancestor where required to produce the correct
+/// result.
+AstNode _getNavigationTargetNode(AstNode node) {
+  AstNode? current = node;
+  while (current != null &&
+      current.parent != null &&
+      current.offset == current.parent!.offset) {
+    current = current.parent;
+  }
+  current ??= node;
+
+  // To navigate to formal params, we need to visit the parameter and not just
+  // the identifier but they don't start at the same offset as they have a
+  // prefix.
+  final parent = current.parent;
+  if (parent is FormalParameter) {
+    current = parent;
+  }
+
+  return current;
+}
+
 AstNode? _getNodeForRange(CompilationUnit unit, int offset, int length) {
   var node = NodeLocator(offset, offset + length).searchWithin(unit);
   for (var n = node; n != null; n = n.parent) {
@@ -47,21 +69,6 @@
   return node;
 }
 
-/// Gets the outer-most node with the same offset as node.
-///
-/// This reduces the number of nodes the visitor needs to walk when collecting
-/// navigation for a specific location in the file.
-AstNode _getOutermostNode(AstNode node) {
-  AstNode? current = node;
-  while (current != null &&
-      current.parent != null &&
-      current != current.parent &&
-      current.offset == current.parent!.offset) {
-    current = current.parent;
-  }
-  return current ?? node;
-}
-
 /// A Dart specific wrapper around [NavigationCollector].
 class _DartNavigationCollector {
   final NavigationCollector collector;
@@ -360,7 +367,7 @@
 
   @override
   void visitImportDirective(ImportDirective node) {
-    var importElement = node.element;
+    var importElement = node.element2;
     if (importElement != null) {
       Element? libraryElement = importElement.importedLibrary;
       _addUriDirectiveRegion(node, libraryElement);
diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart
index 5fdd14b..2c08f76 100644
--- a/pkg/compiler/lib/src/compiler.dart
+++ b/pkg/compiler/lib/src/compiler.dart
@@ -685,11 +685,12 @@
 
     // Dump Info.
     if (options.dumpInfo) {
-      runDumpInfo(codegenResults, programSize);
+      await runDumpInfo(codegenResults, programSize);
     }
   }
 
-  void runDumpInfo(CodegenResults codegenResults, int programSize) {
+  Future<void> runDumpInfo(
+      CodegenResults codegenResults, int programSize) async {
     GlobalTypeInferenceResults globalTypeInferenceResults =
         codegenResults.globalTypeInferenceResults;
     JClosedWorld closedWorld = globalTypeInferenceResults.closedWorld;
@@ -698,11 +699,13 @@
     dumpInfoTask.reportSize(programSize);
     if (options.features.newDumpInfo.isEnabled) {
       assert(untrimmedComponentForDumpInfo != null);
-      dumpInfoState = dumpInfoTask.dumpInfoNew(untrimmedComponentForDumpInfo,
-          closedWorld, globalTypeInferenceResults);
+      dumpInfoState = await dumpInfoTask.dumpInfoNew(
+          untrimmedComponentForDumpInfo,
+          closedWorld,
+          globalTypeInferenceResults);
     } else {
       dumpInfoState =
-          dumpInfoTask.dumpInfo(closedWorld, globalTypeInferenceResults);
+          await dumpInfoTask.dumpInfo(closedWorld, globalTypeInferenceResults);
     }
     if (retainDataForTesting) {
       dumpInfoStateForTesting = dumpInfoState;
diff --git a/pkg/compiler/lib/src/dump_info.dart b/pkg/compiler/lib/src/dump_info.dart
index 18cba93..e5d7229 100644
--- a/pkg/compiler/lib/src/dump_info.dart
+++ b/pkg/compiler/lib/src/dump_info.dart
@@ -20,6 +20,7 @@
 import 'common/elements.dart' show JElementEnvironment;
 import 'common/names.dart';
 import 'common/tasks.dart' show CompilerTask;
+import 'common/ram_usage.dart';
 import 'compiler.dart' show Compiler;
 import 'constants/values.dart' show ConstantValue, InterceptorConstantValue;
 import 'deferred_load/output_unit.dart' show OutputUnit, deferredPartFileName;
@@ -1234,30 +1235,32 @@
     return code.map((ast) => _nodeData[ast]).toList();
   }
 
-  DumpInfoStateData dumpInfo(JClosedWorld closedWorld,
-      GlobalTypeInferenceResults globalInferenceResults) {
+  Future<DumpInfoStateData> dumpInfo(JClosedWorld closedWorld,
+      GlobalTypeInferenceResults globalInferenceResults) async {
     DumpInfoStateData dumpInfoState;
-    measure(() {
+    await measure(() async {
       ElementInfoCollector elementInfoCollector = ElementInfoCollector(
           compiler, this, closedWorld, globalInferenceResults)
         ..run();
 
-      dumpInfoState = buildDumpInfoData(closedWorld, elementInfoCollector);
+      dumpInfoState =
+          await buildDumpInfoData(closedWorld, elementInfoCollector);
       if (useBinaryFormat) {
         dumpInfoBinary(dumpInfoState.info);
       } else {
         dumpInfoJson(dumpInfoState.info);
       }
+      return;
     });
     return dumpInfoState;
   }
 
-  DumpInfoStateData dumpInfoNew(
+  Future<DumpInfoStateData> dumpInfoNew(
       ir.Component component,
       JClosedWorld closedWorld,
-      GlobalTypeInferenceResults globalInferenceResults) {
+      GlobalTypeInferenceResults globalInferenceResults) async {
     DumpInfoStateData dumpInfoState;
-    measure(() {
+    await measure(() async {
       KernelInfoCollector kernelInfoCollector =
           KernelInfoCollector(component, compiler, this, closedWorld)..run();
 
@@ -1265,7 +1268,8 @@
           globalInferenceResults)
         ..run();
 
-      dumpInfoState = buildDumpInfoDataNew(closedWorld, kernelInfoCollector);
+      dumpInfoState =
+          await buildDumpInfoDataNew(closedWorld, kernelInfoCollector);
       TreeShakingInfoVisitor().filter(dumpInfoState.info);
 
       if (useBinaryFormat) {
@@ -1308,8 +1312,8 @@
     });
   }
 
-  DumpInfoStateData buildDumpInfoData(
-      JClosedWorld closedWorld, ElementInfoCollector infoCollector) {
+  Future<DumpInfoStateData> buildDumpInfoData(
+      JClosedWorld closedWorld, ElementInfoCollector infoCollector) async {
     Stopwatch stopwatch = Stopwatch();
     stopwatch.start();
 
@@ -1371,6 +1375,7 @@
         entrypoint: infoCollector
             .state.entityToInfo[closedWorld.elementEnvironment.mainFunction],
         size: _programSize,
+        ramUsage: await currentHeapCapacityInMb(),
         dart2jsVersion:
             compiler.options.hasBuildId ? compiler.options.buildId : null,
         compilationMoment: DateTime.now(),
@@ -1387,8 +1392,8 @@
     return result;
   }
 
-  DumpInfoStateData buildDumpInfoDataNew(
-      JClosedWorld closedWorld, KernelInfoCollector infoCollector) {
+  Future<DumpInfoStateData> buildDumpInfoDataNew(
+      JClosedWorld closedWorld, KernelInfoCollector infoCollector) async {
     Stopwatch stopwatch = Stopwatch();
     stopwatch.start();
 
@@ -1453,6 +1458,7 @@
         entrypoint: infoCollector
             .state.entityToInfo[closedWorld.elementEnvironment.mainFunction],
         size: _programSize,
+        ramUsage: await currentHeapCapacityInMb(),
         dart2jsVersion:
             compiler.options.hasBuildId ? compiler.options.buildId : null,
         compilationMoment: DateTime.now(),
diff --git a/pkg/compiler/lib/src/inferrer/builder.dart b/pkg/compiler/lib/src/inferrer/builder.dart
index 6eb60d8..6a4a5f2 100644
--- a/pkg/compiler/lib/src/inferrer/builder.dart
+++ b/pkg/compiler/lib/src/inferrer/builder.dart
@@ -434,10 +434,6 @@
       case ir.AsyncMarker.AsyncStar:
         recordReturnType(_types.asyncStarStreamType);
         break;
-      case ir.AsyncMarker.SyncYielding:
-        failedAt(
-            _analyzedMember, "Unexpected async marker: ${node.asyncMarker}");
-        break;
     }
     assert(_breaksFor.isEmpty);
     assert(_continuesFor.isEmpty);
diff --git a/pkg/compiler/lib/src/inferrer/engine.dart b/pkg/compiler/lib/src/inferrer/engine.dart
index 6cae1c6..f088516 100644
--- a/pkg/compiler/lib/src/inferrer/engine.dart
+++ b/pkg/compiler/lib/src/inferrer/engine.dart
@@ -65,6 +65,7 @@
   @override
   final JsClosedWorld closedWorld;
 
+  @override
   final TypeSystem types;
   final Map<ir.TreeNode, TypeInformation> concreteTypes = {};
   final GlobalLocalsMap globalLocalsMap;
@@ -174,6 +175,7 @@
 
   /// Returns the type for [nativeBehavior]. See documentation on
   /// [NativeBehavior].
+  @override
   TypeInformation typeOfNativeBehavior(NativeBehavior nativeBehavior) {
     if (nativeBehavior == null) return types.dynamicType;
     List<Object> typesReturned = nativeBehavior.typesReturned;
@@ -214,6 +216,7 @@
     return returnType;
   }
 
+  @override
   void updateSelectorInMember(MemberEntity owner, CallType callType,
       ir.Node node, Selector selector, AbstractValue mask) {
     KernelGlobalTypeInferenceElementData data = dataOfMember(owner);
@@ -755,6 +758,7 @@
   /// Update the inputs to parameters in the graph. [remove] tells whether
   /// inputs must be added or removed. If [init] is false, parameters are
   /// added to the work queue.
+  @override
   void updateParameterInputs(TypeInformation caller, MemberEntity callee,
       ArgumentsTypes arguments, Selector selector,
       {bool remove, bool addToQueue = true}) {
@@ -846,6 +850,7 @@
   ///
   /// Invariant: After graph construction, no [PlaceholderTypeInformation] nodes
   /// should be present and a default type for each parameter should exist.
+  @override
   TypeInformation getDefaultTypeOfParameter(Local parameter) {
     return _defaultTypeOfParameter.putIfAbsent(parameter, () {
       return PlaceholderTypeInformation(
@@ -1093,6 +1098,7 @@
   }
 
   /// Returns the type of [element] when being called with [selector].
+  @override
   TypeInformation typeOfMemberWithSelector(
       MemberEntity element, Selector selector) {
     if (element.name == Identifiers.noSuchMethod_ &&
@@ -1176,6 +1182,7 @@
   ///
   /// One category of elements that do not apply is runtime helpers that the
   /// backend calls, but the optimizations don't see those calls.
+  @override
   bool canFieldBeUsedForGlobalOptimizations(FieldEntity element) {
     if (closedWorld.backendUsage.isFieldUsedByBackend(element)) {
       return false;
@@ -1191,12 +1198,14 @@
   ///
   /// One category of elements that do not apply is runtime helpers that the
   /// backend calls, but the optimizations don't see those calls.
+  @override
   bool canFunctionParametersBeUsedForGlobalOptimizations(
       FunctionEntity function) {
     return !closedWorld.backendUsage.isFunctionUsedByBackend(function);
   }
 
   /// Returns `true` if inference of parameter types is disabled for [member].
+  @override
   bool assumeDynamic(MemberEntity member) {
     return closedWorld.annotationsData.hasAssumeDynamic(member);
   }
diff --git a/pkg/compiler/lib/src/inferrer/engine_interfaces.dart b/pkg/compiler/lib/src/inferrer/engine_interfaces.dart
index 80c7d3e..ce45629 100644
--- a/pkg/compiler/lib/src/inferrer/engine_interfaces.dart
+++ b/pkg/compiler/lib/src/inferrer/engine_interfaces.dart
@@ -2,22 +2,42 @@
 // 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:kernel/ast.dart' as ir;
+
 import '../common/elements.dart';
 import '../elements/entities.dart';
 import '../js_backend/inferred_data.dart';
 import '../js_backend/no_such_method_registry_interfaces.dart';
+import '../native/behavior.dart';
 import '../universe/selector.dart';
 import '../world_interfaces.dart';
 import 'abstract_value_domain.dart';
+import 'locals_handler.dart';
+import 'type_graph_nodes.dart';
+import 'type_system.dart';
 
 abstract class InferrerEngine {
   AbstractValueDomain get abstractValueDomain;
+  TypeSystem get types;
   JClosedWorld get closedWorld;
   CommonElements get commonElements;
   InferredDataBuilder get inferredDataBuilder;
   FunctionEntity get mainElement;
   NoSuchMethodData get noSuchMethodData;
 
+  TypeInformation typeOfNativeBehavior(NativeBehavior nativeBehavior);
+  bool canFieldBeUsedForGlobalOptimizations(FieldEntity element);
+  bool assumeDynamic(MemberEntity member);
+  TypeInformation getDefaultTypeOfParameter(Local parameter);
+  bool canFunctionParametersBeUsedForGlobalOptimizations(
+      FunctionEntity function);
+  TypeInformation typeOfMemberWithSelector(
+      MemberEntity element, Selector? selector);
+  void updateSelectorInMember(MemberEntity owner, CallType callType,
+      ir.Node? node, Selector? selector, AbstractValue? mask);
+  void updateParameterInputs(TypeInformation caller, MemberEntity callee,
+      ArgumentsTypes? arguments, Selector? selector,
+      {required bool remove, bool addToQueue = true});
   bool returnsListElementType(Selector selector, AbstractValue mask);
   bool returnsMapValueType(Selector selector, AbstractValue mask);
 }
diff --git a/pkg/compiler/lib/src/inferrer/locals_handler.dart b/pkg/compiler/lib/src/inferrer/locals_handler.dart
index 69f77f5..a9c0265 100644
--- a/pkg/compiler/lib/src/inferrer/locals_handler.dart
+++ b/pkg/compiler/lib/src/inferrer/locals_handler.dart
@@ -2,8 +2,6 @@
 // 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.
 
-// @dart = 2.10
-
 library locals_handler;
 
 import 'dart:collection' show IterableMixin;
@@ -11,7 +9,7 @@
 import '../elements/entities.dart';
 import '../ir/util.dart';
 import '../util/util.dart';
-import 'engine.dart';
+import 'engine_interfaces.dart';
 import 'type_graph_nodes.dart';
 
 /// A variable scope holds types for variables. It has a link to a
@@ -25,15 +23,15 @@
   /// This is used for computing common parents efficiently.
   final int _level;
 
-  Map<Local, TypeInformation> variables;
+  Map<Local, TypeInformation>? variables;
 
   /// The parent of this scope. Null for the root scope.
-  final VariableScope parent;
+  final VariableScope? parent;
 
   /// The [ir.Node] that created this scope.
-  final ir.Node tryBlock;
+  final ir.Node? tryBlock;
 
-  final VariableScope copyOf;
+  final VariableScope? copyOf;
 
   VariableScope({this.parent})
       : this.variables = null,
@@ -52,13 +50,13 @@
   VariableScope.deepCopyOf(VariableScope other)
       : variables = other.variables == null
             ? null
-            : Map<Local, TypeInformation>.from(other.variables),
+            : Map<Local, TypeInformation>.from(other.variables!),
         tryBlock = other.tryBlock,
         copyOf = other.copyOf ?? other,
         _level = other._level,
         parent = other.parent == null
             ? null
-            : VariableScope.deepCopyOf(other.parent);
+            : VariableScope.deepCopyOf(other.parent!);
 
   /// `true` if this scope is for a try block.
   bool get isTry => tryBlock != null;
@@ -70,50 +68,47 @@
   VariableScope get identity => copyOf ?? this;
 
   /// Returns the common parent between this and [other] based on [identity].
-  VariableScope commonParent(VariableScope other) {
+  VariableScope? commonParent(VariableScope other) {
     if (identity == other.identity) {
       return identity;
     } else if (_level > other._level) {
-      return parent.commonParent(other);
+      return parent!.commonParent(other);
     } else if (_level < other._level) {
-      return commonParent(other.parent);
+      return commonParent(other.parent!);
     } else if (_level > 0) {
-      return parent.commonParent(other.parent);
+      return parent!.commonParent(other.parent!);
     } else {
       return null;
     }
   }
 
-  TypeInformation operator [](Local variable) {
-    TypeInformation result;
-    if (variables == null || (result = variables[variable]) == null) {
-      return parent == null ? null : parent[variable];
+  TypeInformation? operator [](Local variable) {
+    TypeInformation? result;
+    if (variables == null || (result = variables![variable]) == null) {
+      return parent == null ? null : parent![variable];
     }
     return result;
   }
 
   void operator []=(Local variable, TypeInformation mask) {
-    assert(mask != null);
-    if (variables == null) {
-      variables = Map<Local, TypeInformation>();
-    }
-    variables[variable] = mask;
+    assert((mask as dynamic) != null); // TODO(48820): Remove when sound.
+    (variables ??= <Local, TypeInformation>{})[variable] = mask;
   }
 
   /// Calls [f] for all variables in this and parent scopes until and including
   /// [scope]. [f] is called at most once for each variable.
   void forEachLocalUntilScope(
-      VariableScope scope, void f(Local variable, TypeInformation type)) {
+      VariableScope? scope, void f(Local variable, TypeInformation type)) {
     _forEachLocalUntilScope(scope, f, Setlet<Local>(), this);
   }
 
   void _forEachLocalUntilScope(
-      VariableScope scope,
+      VariableScope? scope,
       void f(Local variable, TypeInformation type),
       Setlet<Local> seenLocals,
       VariableScope origin) {
     if (variables != null) {
-      variables.forEach((variable, type) {
+      variables!.forEach((variable, type) {
         if (seenLocals.contains(variable)) return;
         seenLocals.add(variable);
         f(variable, type);
@@ -123,7 +118,7 @@
       return;
     }
     if (parent != null) {
-      parent._forEachLocalUntilScope(scope, f, seenLocals, origin);
+      parent!._forEachLocalUntilScope(scope, f, seenLocals, origin);
     } else {
       assert(
           scope == null,
@@ -139,7 +134,7 @@
 
   bool updates(Local variable) {
     if (variables == null) return false;
-    return variables.containsKey(variable);
+    return variables!.containsKey(variable);
   }
 
   String toStructuredText(String indent) {
@@ -155,18 +150,18 @@
       sb.write('\n${indent}  copyOf:VariableScope(${copyOf.hashCode})');
     }
     if (tryBlock != null) {
-      sb.write('\n${indent}  tryBlock: ${nodeToDebugString(tryBlock)}');
+      sb.write('\n${indent}  tryBlock: ${nodeToDebugString(tryBlock!)}');
     }
     if (variables != null) {
       sb.write('\n${indent}  variables:');
-      variables.forEach((Local local, TypeInformation type) {
+      variables!.forEach((Local local, TypeInformation type) {
         sb.write('\n${indent}    $local: ');
         sb.write(type.toStructuredText('${indent}      '));
       });
     }
     if (parent != null) {
       sb.write('\n${indent}  parent:');
-      parent._toStructuredText(sb, '${indent}     ');
+      parent!._toStructuredText(sb, '${indent}     ');
     }
     sb.write(']');
   }
@@ -180,7 +175,7 @@
 
 /// Tracks initializers via initializations and assignments.
 class FieldInitializationScope {
-  Map<FieldEntity, TypeInformation> fields;
+  Map<FieldEntity, TypeInformation>? fields;
   bool isThisExposed;
 
   /// `true` when control flow prevents accumulating definite assignments,
@@ -195,7 +190,7 @@
       : isThisExposed = other.isThisExposed,
         isIndefinite = other.isIndefinite;
 
-  factory FieldInitializationScope.from(FieldInitializationScope other) {
+  static FieldInitializationScope? from(FieldInitializationScope? other) {
     if (other == null) return null;
     return FieldInitializationScope.internalFrom(other);
   }
@@ -203,12 +198,11 @@
   void updateField(FieldEntity field, TypeInformation type) {
     if (isThisExposed) return;
     if (isIndefinite) return;
-    fields ??= Map<FieldEntity, TypeInformation>();
-    fields[field] = type;
+    (fields ??= <FieldEntity, TypeInformation>{})[field] = type;
   }
 
-  TypeInformation readField(FieldEntity field) {
-    return fields == null ? null : fields[field];
+  TypeInformation? readField(FieldEntity field) {
+    return fields == null ? null : fields![field];
   }
 
   void forEach(void f(FieldEntity element, TypeInformation type)) {
@@ -219,7 +213,7 @@
   /// flow through either [thenScope] or [elseScope].
   FieldInitializationScope mergeDiamondFlow(InferrerEngine inferrer,
       FieldInitializationScope thenScope, FieldInitializationScope elseScope) {
-    assert(elseScope != null);
+    assert((elseScope as dynamic) != null); // TODO(48820): Remove when sound.
 
     // Quick bailout check. If [isThisExposed] or [isIndefinite] is true, we
     // know the code following won't do anything.
@@ -230,7 +224,7 @@
         elseScope.fields == null ? this : elseScope;
 
     thenScope.forEach((FieldEntity field, TypeInformation type) {
-      TypeInformation otherType = otherScope.readField(field);
+      final otherType = otherScope.readField(field);
       if (otherType == null) return;
       updateField(field, inferrer.types.allocateDiamondPhi(type, otherType));
     });
@@ -246,10 +240,10 @@
   final List<TypeInformation> positional;
   final Map<String, TypeInformation> named;
 
-  ArgumentsTypes(this.positional, Map<String, TypeInformation> named)
+  ArgumentsTypes(this.positional, Map<String, TypeInformation>? named)
       : this.named = (named == null || named.isEmpty) ? const {} : named {
-    assert(this.positional.every((TypeInformation type) => type != null));
-    assert(this.named.values.every((TypeInformation type) => type != null));
+    assert(this.positional.every((TypeInformation? type) => type != null));
+    assert(this.named.values.every((TypeInformation? type) => type != null));
   }
 
   ArgumentsTypes.empty()
@@ -266,7 +260,8 @@
   String toString() => "{ positional = $positional, named = $named }";
 
   @override
-  bool operator ==(other) {
+  bool operator ==(Object? other) {
+    if (other is! ArgumentsTypes) return false;
     if (positional.length != other.positional.length) return false;
     if (named.length != other.named.length) return false;
     for (int i = 0; i < positional.length; i++) {
@@ -296,7 +291,7 @@
   }
 
   @override
-  bool contains(Object type) {
+  bool contains(Object? type) {
     return positional.contains(type) || named.containsValue(type);
   }
 }
@@ -341,26 +336,25 @@
   LocalsHandler.deepCopyOf(LocalsHandler other)
       : _locals = VariableScope.deepCopyOf(other._locals);
 
-  TypeInformation use(Local local) {
+  TypeInformation? use(Local local) {
     return _locals[local];
   }
 
   void update(InferrerEngine inferrer, Local local, TypeInformation type,
-      LocalsHandler tryBlock) {
+      LocalsHandler? tryBlock) {
     if (tryBlock != null) {
       // We don't know if an assignment in a try block
       // will be executed, so all assignments in that block are
       // potential types after we have left it. We update the parent
       // of the try block so that, at exit of the try block, we get
       // the right phi for it.
-      TypeInformation existing = tryBlock._locals.parent[local];
+      final existing = tryBlock._locals.parent![local];
       if (existing != null) {
-        TypeInformation phiType = inferrer.types.allocatePhi(
+        final phiType = inferrer.types.allocatePhi(
             tryBlock._locals.tryBlock, local, existing,
             isTry: tryBlock._locals.isTry);
-        TypeInformation inputType =
-            inferrer.types.addPhiInput(local, phiType, type);
-        tryBlock._locals.parent[local] = inputType;
+        final inputType = inferrer.types.addPhiInput(local, phiType, type);
+        tryBlock._locals.parent![local] = inputType;
       }
       // Update the current handler unconditionally with the new
       // type.
@@ -378,7 +372,7 @@
   /// from both are merged with a phi type.
   LocalsHandler mergeFlow(InferrerEngine inferrer, LocalsHandler other,
       {bool inPlace = false}) {
-    VariableScope common = _locals.commonParent(other._locals);
+    final common = _locals.commonParent(other._locals);
     assert(
         common != null,
         "No common parent for\n"
@@ -387,11 +381,11 @@
     assert(
         common == _locals || _locals.variables == null,
         "Non-empty common parent for\n"
-        "1:${common.toStructuredText('  ')}\n"
+        "1:${common?.toStructuredText('  ')}\n"
         "2:${_locals.toStructuredText('  ')}");
     other._locals.forEachLocalUntilScope(common,
         (Local local, TypeInformation type) {
-      TypeInformation myType = _locals[local];
+      final myType = _locals[local];
       if (myType == null) return; // Variable is only defined in [other].
       if (type == myType) return;
       _locals[local] =
@@ -404,13 +398,13 @@
   /// flow through either [thenBranch] or [elseBranch].
   LocalsHandler mergeDiamondFlow(InferrerEngine inferrer,
       LocalsHandler thenBranch, LocalsHandler elseBranch) {
-    assert(elseBranch != null);
+    assert((elseBranch as dynamic) != null); // TODO(48820): Remove when sound.
 
     void mergeLocal(Local local) {
-      TypeInformation myType = _locals[local];
+      final myType = _locals[local];
       if (myType == null) return;
-      TypeInformation elseType = elseBranch._locals[local];
-      TypeInformation thenType = thenBranch._locals[local];
+      final elseType = elseBranch._locals[local]!;
+      final thenType = thenBranch._locals[local]!;
       if (thenType == elseType) {
         _locals[local] = thenType;
       } else {
@@ -418,7 +412,7 @@
       }
     }
 
-    VariableScope common = _locals.commonParent(thenBranch._locals);
+    final common = _locals.commonParent(thenBranch._locals);
     assert(
         common != null,
         "No common parent for\n"
@@ -427,12 +421,12 @@
     assert(
         _locals.commonParent(elseBranch._locals) == common,
         "Diff common parent for\n"
-        "1:${common.toStructuredText('  ')}\n2:"
+        "1:${common?.toStructuredText('  ')}\n2:"
         "${_locals.commonParent(elseBranch._locals)?.toStructuredText('  ')}");
     assert(
         common == _locals || _locals.variables == null,
         "Non-empty common parent for\n"
-        "common:${common.toStructuredText('  ')}\n"
+        "common:${common?.toStructuredText('  ')}\n"
         "1:${_locals.toStructuredText('  ')}\n"
         "2:${thenBranch._locals.toStructuredText('  ')}");
     thenBranch._locals.forEachLocalUntilScope(common, (Local local, _) {
@@ -477,7 +471,7 @@
   LocalsHandler mergeAfterBreaks(
       InferrerEngine inferrer, Iterable<LocalsHandler> handlers,
       {bool keepOwnLocals = true}) {
-    ir.Node tryBlock = _locals.tryBlock;
+    final tryBlock = _locals.tryBlock;
     // Use a separate locals handler to perform the merge in, so that Phi
     // creation does not invalidate previous type knowledge while we might
     // still look it up.
@@ -487,7 +481,7 @@
     Set<Local> seenLocals = Setlet<Local>();
     // Merge all other handlers.
     for (LocalsHandler handler in handlers) {
-      VariableScope common = _locals.commonParent(handler._locals);
+      final common = _locals.commonParent(handler._locals);
       assert(
           common != null,
           "No common parent for\n"
@@ -496,11 +490,11 @@
       assert(
           common == _locals || _locals.variables == null,
           "Non-empty common parent for\n"
-          "common:${common.toStructuredText('  ')}\n"
+          "common:${common?.toStructuredText('  ')}\n"
           "1:${_locals.toStructuredText('  ')}\n"
           "2:${handler._locals.toStructuredText('  ')}");
       handler._locals.forEachLocalUntilScope(common, (local, otherType) {
-        TypeInformation myType = merged[local];
+        final myType = merged[local];
         if (myType == null) return;
         TypeInformation newType;
         if (!seenLocals.contains(local)) {
@@ -509,7 +503,8 @@
               isTry: merged.isTry);
           seenLocals.add(local);
         } else {
-          newType = inferrer.types.addPhiInput(local, myType, otherType);
+          newType = inferrer.types.addPhiInput(
+              local, myType as PhiElementTypeInformation, otherType);
         }
         if (newType != myType) {
           merged[local] = newType;
@@ -520,10 +515,10 @@
     // [merged] to update the Phi nodes with original values.
     if (keepOwnLocals) {
       for (Local variable in seenLocals) {
-        TypeInformation originalType = _locals[variable];
+        final originalType = _locals[variable];
         if (originalType != null) {
-          merged[variable] = inferrer.types
-              .addPhiInput(variable, merged[variable], originalType);
+          merged[variable] = inferrer.types.addPhiInput(variable,
+              merged[variable] as PhiElementTypeInformation, originalType);
         }
       }
     }
@@ -531,7 +526,8 @@
     // actual locals handler.
     merged.forEachLocalUntilScope(merged,
         (Local variable, TypeInformation type) {
-      _locals[variable] = inferrer.types.simplifyPhi(tryBlock, variable, type);
+      _locals[variable] = inferrer.types
+          .simplifyPhi(tryBlock, variable, type as PhiElementTypeInformation);
     });
     return this;
   }
@@ -541,7 +537,7 @@
   bool mergeAll(InferrerEngine inferrer, Iterable<LocalsHandler> handlers) {
     bool changed = false;
     handlers.forEach((LocalsHandler other) {
-      VariableScope common = _locals.commonParent(other._locals);
+      final common = _locals.commonParent(other._locals);
       assert(
           common != null,
           "No common parent for\n"
@@ -550,14 +546,14 @@
       assert(
           common == _locals || _locals.variables == null,
           "Non-empty common parent for\n"
-          "common:${common.toStructuredText('  ')}\n"
+          "common:${common?.toStructuredText('  ')}\n"
           "1:${_locals.toStructuredText('  ')}\n"
           "2:${other._locals.toStructuredText('  ')}");
       other._locals.forEachLocalUntilScope(common, (local, otherType) {
-        TypeInformation myType = _locals[local];
+        final myType = _locals[local];
         if (myType == null) return;
-        TypeInformation newType =
-            inferrer.types.addPhiInput(local, myType, otherType);
+        TypeInformation newType = inferrer.types
+            .addPhiInput(local, myType as PhiElementTypeInformation, otherType);
         if (newType != myType) {
           changed = true;
           _locals[local] = newType;
@@ -579,8 +575,8 @@
 
   void endLoop(InferrerEngine inferrer, ir.Node loop) {
     _locals.forEachLocal((Local variable, TypeInformation type) {
-      TypeInformation newType =
-          inferrer.types.simplifyPhi(loop, variable, type);
+      final newType = inferrer.types
+          .simplifyPhi(loop, variable, type as PhiElementTypeInformation);
       if (newType != type) {
         _locals[variable] = newType;
       }
diff --git a/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart b/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart
index 8f28c04..6a698dc 100644
--- a/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart
+++ b/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart
@@ -2,8 +2,6 @@
 // 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.
 
-// @dart = 2.10
-
 library compiler.src.inferrer.type_graph_nodes;
 
 import 'dart:collection' show IterableBase;
@@ -16,11 +14,11 @@
 import '../elements/types.dart';
 import '../universe/selector.dart' show Selector;
 import '../util/util.dart' show Setlet;
-import '../world.dart' show JClosedWorld;
+import '../world_interfaces.dart' show JClosedWorld;
 import 'abstract_value_domain.dart';
 import 'debug.dart' as debug;
 import 'locals_handler.dart' show ArgumentsTypes;
-import 'engine.dart';
+import 'engine_interfaces.dart';
 import 'type_system.dart';
 
 /// Common class for all nodes in the graph. The current nodes are:
@@ -46,10 +44,10 @@
   AbstractValue type;
 
   /// The graph node of the member this [TypeInformation] node belongs to.
-  final MemberTypeInformation context;
+  final MemberTypeInformation? context;
 
   /// The element this [TypeInformation] node belongs to.
-  MemberEntity get contextMember => context?.member;
+  MemberEntity? get contextMember => context?.member;
 
   ParameterInputs get inputs => _inputs;
 
@@ -189,7 +187,7 @@
   /// The [Element] where this [TypeInformation] was created. May be `null`
   /// for some [TypeInformation] nodes, where we do not need to store
   /// the information.
-  MemberEntity get owner => (context != null) ? context.member : null;
+  MemberEntity? get owner => (context != null) ? context?.member : null;
 
   /// Returns whether the type cannot change after it has been
   /// inferred.
@@ -341,10 +339,11 @@
   @override
   Iterator<TypeInformation> get iterator => _inputs.keys.iterator;
   @override
-  Iterable<TypeInformation> where(Function f) => _inputs.keys.where(f);
+  Iterable<TypeInformation> where(bool Function(TypeInformation) f) =>
+      _inputs.keys.where(f);
 
   @override
-  bool contains(Object info) => _inputs.containsKey(info);
+  bool contains(Object? info) => _inputs.containsKey(info);
 
   @override
   String toString() => _inputs.keys.toList().toString();
@@ -380,10 +379,10 @@
   bool disableInferenceForClosures = true;
 
   ElementTypeInformation._internal(
-      AbstractValueDomain abstractValueDomain, MemberTypeInformation context)
+      AbstractValueDomain abstractValueDomain, MemberTypeInformation? context)
       : super(abstractValueDomain.emptyType, context);
   ElementTypeInformation._withInputs(AbstractValueDomain abstractValueDomain,
-      MemberTypeInformation context, ParameterInputs inputs)
+      MemberTypeInformation? context, ParameterInputs inputs)
       : super.withInputs(abstractValueDomain.emptyType, context, inputs);
 
   String getInferredSignature(TypeSystem types);
@@ -410,7 +409,7 @@
 
   // Strict `bool` value is computed in cleanup(). Also used as a flag to see if
   // cleanup has been called.
-  bool _isCalledOnce = null;
+  bool? _isCalledOnce = null;
 
   /// Whether this member is invoked via indirect dynamic calls. In that case
   /// the exact number of call sites cannot be computed precisely.
@@ -423,7 +422,7 @@
   ///
   /// The global information is summarized in [cleanup], after which [_callers]
   /// is set to `null`.
-  Map<MemberEntity, Setlet<ir.Node /*?*/ >> _callers;
+  Map<MemberEntity, Setlet<ir.Node?>>? _callers;
 
   MemberTypeInformation._internal(
       AbstractValueDomain abstractValueDomain, this._member)
@@ -434,22 +433,24 @@
   @override
   String get debugName => '$member';
 
-  void addCall(MemberEntity caller, ir.Node /*?*/ node) {
-    _callers ??= <MemberEntity, Setlet<ir.Node /*?*/ >>{};
-    _callers.putIfAbsent(caller, () => Setlet()).add(node);
+  void addCall(MemberEntity caller, ir.Node? node) {
+    (_callers ??= <MemberEntity, Setlet<ir.Node?>>{})
+        .putIfAbsent(caller, () => Setlet())
+        .add(node);
   }
 
-  void removeCall(MemberEntity caller, ir.Node /*?*/ node) {
-    if (_callers == null) return;
-    Setlet calls = _callers[caller];
+  void removeCall(MemberEntity caller, Object node) {
+    final callers = _callers;
+    if (callers == null) return;
+    final calls = callers[caller];
     if (calls == null) return;
     calls.remove(node);
     if (calls.isEmpty) {
-      _callers.remove(caller);
+      callers.remove(caller);
     }
   }
 
-  Iterable<MemberEntity> get callersForTesting {
+  Iterable<MemberEntity>? get callersForTesting {
     return _callers?.keys;
   }
 
@@ -464,10 +465,11 @@
   }
 
   bool _computeIsCalledOnce() {
+    final callers = _callers;
     if (_calledIndirectly) return false;
-    if (_callers == null) return false;
+    if (callers == null) return false;
     int count = 0;
-    for (var set in _callers.values) {
+    for (var set in callers.values) {
       count += set.length;
       if (count > 1) return false;
     }
@@ -488,9 +490,9 @@
   @override
   bool get isStable => super.isStable && !isClosurized;
 
-  AbstractValue handleSpecialCases(InferrerEngine inferrer);
+  AbstractValue? handleSpecialCases(InferrerEngine inferrer);
 
-  AbstractValue _handleFunctionCase(
+  AbstractValue? _handleFunctionCase(
       FunctionEntity function, InferrerEngine inferrer) {
     if (inferrer.closedWorld.nativeData.isNativeMember(function)) {
       // Use the type annotation as the type for native elements. We
@@ -527,7 +529,7 @@
 
   @override
   AbstractValue computeType(InferrerEngine inferrer) {
-    AbstractValue special = handleSpecialCases(inferrer);
+    final special = handleSpecialCases(inferrer);
     if (special != null) return potentiallyNarrowType(special, inferrer);
     return potentiallyNarrowType(
         inferrer.types.computeTypeMask(inputs), inferrer);
@@ -554,36 +556,37 @@
 
   @override
   String getInferredSignature(TypeSystem types) {
-    return types.getInferredSignatureOfMethod(_member);
+    return types.getInferredSignatureOfMethod(_member as FunctionEntity);
   }
 }
 
 class FieldTypeInformation extends MemberTypeInformation {
-  FieldEntity get _field => _member;
+  @override
+  final FieldEntity _member;
   final DartType _type;
 
   FieldTypeInformation(
-      AbstractValueDomain abstractValueDomain, FieldEntity element, this._type)
-      : super._internal(abstractValueDomain, element);
+      AbstractValueDomain abstractValueDomain, this._member, this._type)
+      : super._internal(abstractValueDomain, _member);
 
   @override
-  AbstractValue handleSpecialCases(InferrerEngine inferrer) {
-    if (!inferrer.canFieldBeUsedForGlobalOptimizations(_field) ||
-        inferrer.assumeDynamic(_field)) {
+  AbstractValue? handleSpecialCases(InferrerEngine inferrer) {
+    if (!inferrer.canFieldBeUsedForGlobalOptimizations(_member) ||
+        inferrer.assumeDynamic(_member)) {
       // Do not infer types for fields that have a corresponding annotation or
       // are assigned by synthesized calls
 
       giveUp(inferrer);
       return safeType(inferrer);
     }
-    if (inferrer.closedWorld.nativeData.isNativeMember(_field)) {
+    if (inferrer.closedWorld.nativeData.isNativeMember(_member)) {
       // Use the type annotation as the type for native elements. We
       // also give up on inferring to make sure this element never
       // goes in the work queue.
       giveUp(inferrer);
       return inferrer
           .typeOfNativeBehavior(inferrer.closedWorld.nativeData
-              .getNativeFieldLoadBehavior(_field))
+              .getNativeFieldLoadBehavior(_member))
           .type;
     }
     return null;
@@ -599,7 +602,7 @@
   bool hasStableType(InferrerEngine inferrer) {
     // The number of inputs of non-final fields is
     // not stable. Therefore such a field cannot be stable.
-    if (!_field.isAssignable) {
+    if (!_member.isAssignable) {
       return false;
     }
     return super.hasStableType(inferrer);
@@ -607,16 +610,17 @@
 }
 
 class GetterTypeInformation extends MemberTypeInformation {
-  FunctionEntity get _getter => _member;
+  @override
+  final FunctionEntity _member;
   final FunctionType _type;
 
-  GetterTypeInformation(AbstractValueDomain abstractValueDomain,
-      FunctionEntity element, this._type)
-      : super._internal(abstractValueDomain, element);
+  GetterTypeInformation(
+      AbstractValueDomain abstractValueDomain, this._member, this._type)
+      : super._internal(abstractValueDomain, _member);
 
   @override
-  AbstractValue handleSpecialCases(InferrerEngine inferrer) {
-    return _handleFunctionCase(_getter, inferrer);
+  AbstractValue? handleSpecialCases(InferrerEngine inferrer) {
+    return _handleFunctionCase(_member, inferrer);
   }
 
   @override
@@ -627,15 +631,15 @@
 }
 
 class SetterTypeInformation extends MemberTypeInformation {
-  FunctionEntity get _setter => _member;
+  @override
+  final FunctionEntity _member;
 
-  SetterTypeInformation(
-      AbstractValueDomain abstractValueDomain, FunctionEntity element)
-      : super._internal(abstractValueDomain, element);
+  SetterTypeInformation(AbstractValueDomain abstractValueDomain, this._member)
+      : super._internal(abstractValueDomain, _member);
 
   @override
-  AbstractValue handleSpecialCases(InferrerEngine inferrer) {
-    return _handleFunctionCase(_setter, inferrer);
+  AbstractValue? handleSpecialCases(InferrerEngine inferrer) {
+    return _handleFunctionCase(_member, inferrer);
   }
 
   @override
@@ -646,22 +650,23 @@
 }
 
 class MethodTypeInformation extends MemberTypeInformation {
-  FunctionEntity get _method => _member;
+  @override
+  final FunctionEntity _member;
   final FunctionType _type;
 
-  MethodTypeInformation(AbstractValueDomain abstractValueDomain,
-      FunctionEntity element, this._type)
-      : super._internal(abstractValueDomain, element);
+  MethodTypeInformation(
+      AbstractValueDomain abstractValueDomain, this._member, this._type)
+      : super._internal(abstractValueDomain, _member);
 
   @override
-  AbstractValue handleSpecialCases(InferrerEngine inferrer) {
-    return _handleFunctionCase(_method, inferrer);
+  AbstractValue? handleSpecialCases(InferrerEngine inferrer) {
+    return _handleFunctionCase(_member, inferrer);
   }
 
   @override
   AbstractValue _potentiallyNarrowType(
       AbstractValue mask, InferrerEngine inferrer) {
-    if (inferrer.commonElements.isLateReadCheck(_method)) {
+    if (inferrer.commonElements.isLateReadCheck(_member)) {
       mask = inferrer.abstractValueDomain.excludeLateSentinel(mask);
     }
     return _narrowType(inferrer.closedWorld, mask, _type.returnType);
@@ -672,31 +677,31 @@
 }
 
 class FactoryConstructorTypeInformation extends MemberTypeInformation {
-  ConstructorEntity get _constructor => _member;
+  @override
+  final ConstructorEntity _member;
   final FunctionType _type;
 
-  FactoryConstructorTypeInformation(AbstractValueDomain abstractValueDomain,
-      ConstructorEntity element, this._type)
-      : super._internal(abstractValueDomain, element);
+  FactoryConstructorTypeInformation(
+      AbstractValueDomain abstractValueDomain, this._member, this._type)
+      : super._internal(abstractValueDomain, _member);
 
   @override
-  AbstractValue handleSpecialCases(InferrerEngine inferrer) {
+  AbstractValue? handleSpecialCases(InferrerEngine inferrer) {
     AbstractValueDomain abstractValueDomain = inferrer.abstractValueDomain;
-    if (_constructor.isFromEnvironmentConstructor) {
-      if (_constructor.enclosingClass == inferrer.commonElements.intClass) {
+    if (_member.isFromEnvironmentConstructor) {
+      if (_member.enclosingClass == inferrer.commonElements.intClass) {
         giveUp(inferrer);
         return abstractValueDomain.includeNull(abstractValueDomain.intType);
-      } else if (_constructor.enclosingClass ==
-          inferrer.commonElements.boolClass) {
+      } else if (_member.enclosingClass == inferrer.commonElements.boolClass) {
         giveUp(inferrer);
         return abstractValueDomain.includeNull(abstractValueDomain.boolType);
-      } else if (_constructor.enclosingClass ==
+      } else if (_member.enclosingClass ==
           inferrer.commonElements.stringClass) {
         giveUp(inferrer);
         return abstractValueDomain.includeNull(abstractValueDomain.stringType);
       }
     }
-    return _handleFunctionCase(_constructor, inferrer);
+    return _handleFunctionCase(_member, inferrer);
   }
 
   @override
@@ -712,15 +717,16 @@
 }
 
 class GenerativeConstructorTypeInformation extends MemberTypeInformation {
-  ConstructorEntity get _constructor => _member;
+  @override
+  final FunctionEntity _member;
 
   GenerativeConstructorTypeInformation(
-      AbstractValueDomain abstractValueDomain, ConstructorEntity element)
-      : super._internal(abstractValueDomain, element);
+      AbstractValueDomain abstractValueDomain, this._member)
+      : super._internal(abstractValueDomain, _member);
 
   @override
-  AbstractValue handleSpecialCases(InferrerEngine inferrer) {
-    return _handleFunctionCase(_constructor, inferrer);
+  AbstractValue? handleSpecialCases(InferrerEngine inferrer) {
+    return _handleFunctionCase(_member, inferrer);
   }
 
   @override
@@ -796,13 +802,12 @@
     _isTearOffClosureParameter = true;
     // We have to add a flow-edge for the default value (if it exists), as we
     // might not see all call-sites and thus miss the use of it.
-    TypeInformation defaultType =
-        inferrer.getDefaultTypeOfParameter(_parameter);
-    if (defaultType != null) defaultType.addUser(this);
+    final defaultType = inferrer.getDefaultTypeOfParameter(_parameter);
+    defaultType.addUser(this);
   }
 
   // TODO(herhut): Cleanup into one conditional.
-  AbstractValue handleSpecialCases(InferrerEngine inferrer) {
+  AbstractValue? handleSpecialCases(InferrerEngine inferrer) {
     if (!inferrer.canFunctionParametersBeUsedForGlobalOptimizations(_method) ||
         inferrer.assumeDynamic(_method)) {
       // Do not infer types for parameters that have a corresponding annotation
@@ -890,7 +895,7 @@
 
   @override
   AbstractValue computeType(InferrerEngine inferrer) {
-    AbstractValue special = handleSpecialCases(inferrer);
+    final special = handleSpecialCases(inferrer);
     if (special != null) return special;
     return potentiallyNarrowType(
         inferrer.types.computeTypeMask(inputs), inferrer);
@@ -953,7 +958,7 @@
   forIn,
 }
 
-bool validCallType(CallType callType, ir.Node /*?*/ call, Selector selector) {
+bool validCallType(CallType callType, ir.Node? call, Selector selector) {
   switch (callType) {
     case CallType.access:
       return call is ir.Node;
@@ -962,7 +967,6 @@
     case CallType.forIn:
       return call is ir.ForInStatement;
   }
-  throw StateError('Unexpected call type $callType.');
 }
 
 /// A [CallSiteTypeInformation] is a call found in the AST, or a
@@ -975,10 +979,10 @@
 /// and [selector] and [receiver] fields for dynamic calls.
 abstract class CallSiteTypeInformation extends TypeInformation
     with ApplyableTypeInformation {
-  final ir.Node /*?*/ _call;
+  final ir.Node? _call;
   final MemberEntity caller;
-  final Selector selector;
-  final ArgumentsTypes arguments;
+  final Selector? selector;
+  final ArgumentsTypes? arguments;
   final bool inLoop;
 
   CallSiteTypeInformation(
@@ -990,7 +994,7 @@
       this.arguments,
       this.inLoop)
       : super.noInputs(abstractValueDomain.emptyType, context) {
-    assert(_call is ir.Node || (_call == null && selector.name == '=='));
+    assert(_call is ir.Node || (_call == null && selector?.name == '=='));
   }
 
   @override
@@ -1011,7 +1015,7 @@
   StaticCallSiteTypeInformation(
       AbstractValueDomain abstractValueDomain,
       MemberTypeInformation context,
-      ir.Node /*?*/ call,
+      ir.Node? call,
       MemberEntity enclosing,
       this.calledElement,
       Selector selector,
@@ -1029,10 +1033,10 @@
   @override
   void addToGraph(InferrerEngine inferrer) {
     MemberTypeInformation callee = _getCalledTypeInfo(inferrer);
-    callee.addCall(caller, _call);
+    callee.addCall(caller, _call!);
     callee.addUser(this);
     if (arguments != null) {
-      arguments.forEach((info) => info.addUser(this));
+      arguments!.forEach((info) => info.addUser(this));
     }
     inferrer.updateParameterInputs(this, calledElement, arguments, selector,
         remove: false, addToQueue: false);
@@ -1072,7 +1076,7 @@
   bool hasStableType(InferrerEngine inferrer) {
     bool isStable = _getCalledTypeInfo(inferrer).isStable;
     return isStable &&
-        (arguments == null || arguments.every((info) => info.isStable)) &&
+        (arguments == null || arguments!.every((info) => info.isStable)) &&
         super.hasStableType(inferrer);
   }
 
@@ -1081,7 +1085,7 @@
     ElementTypeInformation callee = _getCalledTypeInfo(inferrer);
     callee.removeUser(this);
     if (arguments != null) {
-      arguments.forEach((info) => info.removeUser(this));
+      arguments!.forEach((info) => info.removeUser(this));
     }
     super.removeAndClearReferences(inferrer);
   }
@@ -1107,7 +1111,7 @@
   IndirectDynamicCallSiteTypeInformation(
       AbstractValueDomain abstractValueDomain,
       MemberTypeInformation context,
-      ir.Node /*?*/ call,
+      ir.Node? call,
       this.dynamicCall,
       MemberEntity enclosing,
       Selector selector,
@@ -1123,20 +1127,22 @@
   void addToGraph(InferrerEngine inferrer) {
     receiver.addUser(this);
     dynamicCall.receiver.addInput(receiver);
-    List<TypeInformation> positional = arguments.positional;
+    final args = arguments!;
+    List<TypeInformation> positional = args.positional;
+    final dynamicCallArgs = dynamicCall.arguments!;
     for (int i = 0; i < positional.length; i++) {
       positional[i].addUser(this);
-      dynamicCall.arguments.positional[i].addInput(positional[i]);
+      dynamicCallArgs.positional[i].addInput(positional[i]);
     }
-    arguments.named.forEach((name, namedInfo) {
-      dynamicCall.arguments.named[name].addInput(namedInfo);
+    args.named.forEach((name, namedInfo) {
+      dynamicCallArgs.named[name]!.addInput(namedInfo);
     });
     dynamicCall.addUser(this);
   }
 
   @override
   AbstractValue computeType(InferrerEngine inferrer) {
-    AbstractValue typeMask = _computeTypedSelector(inferrer);
+    final typeMask = _computeTypedSelector(inferrer);
     inferrer.updateSelectorInMember(
         caller, CallType.access, _call, selector, typeMask);
 
@@ -1151,7 +1157,7 @@
     return result;
   }
 
-  AbstractValue _computeTypedSelector(InferrerEngine inferrer) {
+  AbstractValue? _computeTypedSelector(InferrerEngine inferrer) {
     AbstractValue receiverType = receiver.type;
     if (mask == receiverType) return mask;
     return receiverType == inferrer.abstractValueDomain.dynamicType
@@ -1185,7 +1191,7 @@
     dynamicCall.removeUser(this);
     receiver.removeUser(this);
     if (arguments != null) {
-      arguments.forEach((info) => info.removeUser(this));
+      arguments!.forEach((info) => info.removeUser(this));
     }
     super.removeAndClearReferences(inferrer);
   }
@@ -1197,16 +1203,16 @@
   final TypeInformation receiver;
   final AbstractValue mask;
   final bool isConditional;
-  bool _hasClosureCallTargets;
+  bool? _hasClosureCallTargets;
 
   /// Cached concrete targets of this call.
-  Iterable<MemberEntity> _concreteTargets;
+  Iterable<MemberEntity>? _concreteTargets;
 
   DynamicCallSiteTypeInformation(
       AbstractValueDomain abstractValueDomain,
       MemberTypeInformation context,
       this._callType,
-      T call,
+      T? call,
       MemberEntity enclosing,
       Selector selector,
       this.mask,
@@ -1223,28 +1229,29 @@
     if (_callType == CallType.indirectAccess) {
       callee._calledIndirectly = true;
     } else {
-      callee.addCall(caller, _call);
+      callee.addCall(caller, _call!);
     }
   }
 
   void _removeCall(MemberTypeInformation callee) {
     if (_callType != CallType.indirectAccess) {
-      callee.removeCall(caller, _call);
+      callee.removeCall(caller, _call!);
     }
   }
 
   @override
   void addToGraph(InferrerEngine inferrer) {
-    assert(receiver != null);
-    AbstractValue typeMask = computeTypedSelector(inferrer);
+    assert((receiver as dynamic) != null); // TODO(48820): Remove when sound.
+    final typeMask = computeTypedSelector(inferrer);
     _hasClosureCallTargets =
-        inferrer.closedWorld.includesClosureCall(selector, typeMask);
-    _concreteTargets = inferrer.closedWorld.locateMembers(selector, typeMask);
+        inferrer.closedWorld.includesClosureCall(selector!, typeMask);
+    final concreteTargets = _concreteTargets =
+        inferrer.closedWorld.locateMembers(selector!, typeMask);
     receiver.addUser(this);
     if (arguments != null) {
-      arguments.forEach((info) => info.addUser(this));
+      arguments!.forEach((info) => info.addUser(this));
     }
-    for (MemberEntity element in _concreteTargets) {
+    for (MemberEntity element in concreteTargets) {
       MemberTypeInformation callee =
           inferrer.types.getInferredTypeOfMember(element);
       _addCall(callee);
@@ -1255,17 +1262,17 @@
   }
 
   /// `true` if this invocation can hit a 'call' method on a closure.
-  bool get hasClosureCallTargets => _hasClosureCallTargets;
+  bool get hasClosureCallTargets => _hasClosureCallTargets!;
 
   /// All concrete targets of this invocation. If [hasClosureCallTargets] is
   /// `true` the invocation can additional target an unknown set of 'call'
   /// methods on closures.
-  Iterable<MemberEntity> get concreteTargets => _concreteTargets;
+  Iterable<MemberEntity> get concreteTargets => _concreteTargets!;
 
   @override
-  Iterable<MemberEntity> get callees => _concreteTargets;
+  Iterable<MemberEntity> get callees => _concreteTargets!;
 
-  AbstractValue computeTypedSelector(InferrerEngine inferrer) {
+  AbstractValue? computeTypedSelector(InferrerEngine inferrer) {
     AbstractValue receiverType = receiver.type;
     if (mask != receiverType) {
       return receiverType == inferrer.abstractValueDomain.dynamicType
@@ -1277,11 +1284,11 @@
   }
 
   bool targetsIncludeComplexNoSuchMethod(InferrerEngine inferrer) {
-    return _concreteTargets.any((MemberEntity e) {
+    return _concreteTargets!.any((MemberEntity e) {
       return e.isFunction &&
           e.isInstanceMember &&
           e.name == Identifiers.noSuchMethod_ &&
-          inferrer.noSuchMethodData.isComplex(e);
+          inferrer.noSuchMethodData.isComplex(e as FunctionEntity);
     });
   }
 
@@ -1292,31 +1299,31 @@
   ///
   /// Returns the more precise TypeInformation, or `null` to defer to the
   /// library code.
-  TypeInformation handleIntrisifiedSelector(
-      Selector selector, AbstractValue mask, InferrerEngine inferrer) {
+  TypeInformation? handleIntrisifiedSelector(
+      Selector selector, AbstractValue? mask, InferrerEngine inferrer) {
     AbstractValueDomain abstractValueDomain = inferrer.abstractValueDomain;
     if (mask == null) return null;
     if (abstractValueDomain.isIntegerOrNull(mask).isPotentiallyFalse) {
       return null;
     }
     if (!selector.isCall && !selector.isOperator) return null;
-    if (!arguments.named.isEmpty) return null;
-    if (arguments.positional.length > 1) return null;
+    final args = arguments!;
+    if (!args.named.isEmpty) return null;
+    if (args.positional.length > 1) return null;
 
-    bool isInt(info) =>
+    bool isInt(TypeInformation info) =>
         abstractValueDomain.isIntegerOrNull(info.type).isDefinitelyTrue;
-    bool isEmpty(info) =>
+    bool isEmpty(TypeInformation info) =>
         abstractValueDomain.isEmpty(info.type).isDefinitelyTrue;
-    bool isUInt31(info) => abstractValueDomain
+    bool isUInt31(TypeInformation info) => abstractValueDomain
         .isUInt31(abstractValueDomain.excludeNull(info.type))
         .isDefinitelyTrue;
-    bool isPositiveInt(info) =>
+    bool isPositiveInt(TypeInformation info) =>
         abstractValueDomain.isPositiveIntegerOrNull(info.type).isDefinitelyTrue;
 
     TypeInformation tryLater() => inferrer.types.nonNullEmptyType;
 
-    TypeInformation argument =
-        arguments.isEmpty ? null : arguments.positional.first;
+    final argument = args.isEmpty ? null : args.positional.first;
 
     String name = selector.name;
     // These are type inference rules only for useful cases that are not
@@ -1331,7 +1338,7 @@
       case '%':
       case 'remainder':
       case '~/':
-        if (isEmpty(argument)) return tryLater();
+        if (isEmpty(argument!)) return tryLater();
         if (isPositiveInt(receiver) && isPositiveInt(argument)) {
           // uint31 + uint31 -> uint32
           if (name == '+' && isUInt31(receiver) && isUInt31(argument)) {
@@ -1346,35 +1353,35 @@
 
       case '|':
       case '^':
-        if (isEmpty(argument)) return tryLater();
+        if (isEmpty(argument!)) return tryLater();
         if (isUInt31(receiver) && isUInt31(argument)) {
           return inferrer.types.uint31Type;
         }
         return null;
 
       case '>>':
-        if (isEmpty(argument)) return tryLater();
+        if (isEmpty(argument!)) return tryLater();
         if (isUInt31(receiver)) {
           return inferrer.types.uint31Type;
         }
         return null;
 
       case '>>>':
-        if (isEmpty(argument)) return tryLater();
+        if (isEmpty(argument!)) return tryLater();
         if (isUInt31(receiver)) {
           return inferrer.types.uint31Type;
         }
         return null;
 
       case '&':
-        if (isEmpty(argument)) return tryLater();
+        if (isEmpty(argument!)) return tryLater();
         if (isUInt31(receiver) || isUInt31(argument)) {
           return inferrer.types.uint31Type;
         }
         return null;
 
       case '-':
-        if (isEmpty(argument)) return tryLater();
+        if (isEmpty(argument!)) return tryLater();
         if (isInt(argument)) {
           return inferrer.types.intType;
         }
@@ -1385,9 +1392,7 @@
         return inferrer.types.intType;
 
       case 'abs':
-        return arguments.hasNoArguments()
-            ? inferrer.types.positiveIntType
-            : null;
+        return args.hasNoArguments() ? inferrer.types.positiveIntType : null;
 
       default:
         return null;
@@ -1398,19 +1403,21 @@
   AbstractValue computeType(InferrerEngine inferrer) {
     JClosedWorld closedWorld = inferrer.closedWorld;
     AbstractValueDomain abstractValueDomain = closedWorld.abstractValueDomain;
-    Iterable<MemberEntity> oldTargets = _concreteTargets;
-    AbstractValue typeMask = computeTypedSelector(inferrer);
+    final oldTargets = _concreteTargets!;
+    final typeMask = computeTypedSelector(inferrer);
+    final localSelector = selector!;
     inferrer.updateSelectorInMember(
-        caller, _callType, _call, selector, typeMask);
+        caller, _callType, _call, localSelector, typeMask);
 
     _hasClosureCallTargets =
-        closedWorld.includesClosureCall(selector, typeMask);
-    _concreteTargets = closedWorld.locateMembers(selector, typeMask);
+        closedWorld.includesClosureCall(localSelector, typeMask);
+    final concreteTargets =
+        _concreteTargets = closedWorld.locateMembers(localSelector, typeMask);
 
     // Update the call graph if the targets could have changed.
-    if (!identical(_concreteTargets, oldTargets)) {
+    if (!identical(concreteTargets, oldTargets)) {
       // Add calls to new targets to the graph.
-      _concreteTargets
+      concreteTargets
           .where((target) => !oldTargets.contains(target))
           .forEach((MemberEntity element) {
         MemberTypeInformation callee =
@@ -1423,7 +1430,7 @@
 
       // Walk over the old targets, and remove calls that cannot happen anymore.
       oldTargets
-          .where((target) => !_concreteTargets.contains(target))
+          .where((target) => !concreteTargets.contains(target))
           .forEach((MemberEntity element) {
         MemberTypeInformation callee =
             inferrer.types.getInferredTypeOfMember(element);
@@ -1437,18 +1444,18 @@
     // Walk over the found targets, and compute the joined union type mask
     // for all these targets.
     AbstractValue result;
-    if (_hasClosureCallTargets) {
+    if (_hasClosureCallTargets!) {
       result = abstractValueDomain.dynamicType;
     } else {
       result = inferrer.types
-          .joinTypeMasks(_concreteTargets.map((MemberEntity element) {
+          .joinTypeMasks(concreteTargets.map((MemberEntity element) {
         if (typeMask != null &&
-            inferrer.returnsListElementType(selector, typeMask)) {
+            inferrer.returnsListElementType(localSelector, typeMask)) {
           return abstractValueDomain.getContainerElementType(receiver.type);
         } else if (typeMask != null &&
-            inferrer.returnsMapValueType(selector, typeMask)) {
+            inferrer.returnsMapValueType(localSelector, typeMask)) {
           if (abstractValueDomain.isDictionary(typeMask)) {
-            AbstractValue arg = arguments.positional[0].type;
+            AbstractValue arg = arguments!.positional[0].type;
             ConstantValue value = abstractValueDomain.getPrimitiveValue(arg);
             if (value is StringConstantValue) {
               String key = value.stringValue;
@@ -1476,8 +1483,8 @@
           }
           return abstractValueDomain.getMapValueType(typeMask);
         } else {
-          TypeInformation info =
-              handleIntrisifiedSelector(selector, typeMask, inferrer);
+          final info =
+              handleIntrisifiedSelector(localSelector, typeMask, inferrer);
           if (info != null) return info.type;
           return inferrer.typeOfMemberWithSelector(element, selector).type;
         }
@@ -1495,16 +1502,19 @@
   @override
   void giveUp(InferrerEngine inferrer, {bool clearInputs = true}) {
     if (!abandonInferencing) {
-      inferrer.updateSelectorInMember(caller, _callType, _call, selector, mask);
-      Iterable<MemberEntity> oldTargets = _concreteTargets;
+      final call = _call!;
+      inferrer.updateSelectorInMember(caller, _callType, call, selector, mask);
+      final oldTargets = concreteTargets;
+      final localSelector = selector!;
       _hasClosureCallTargets =
-          inferrer.closedWorld.includesClosureCall(selector, mask);
-      _concreteTargets = inferrer.closedWorld.locateMembers(selector, mask);
-      for (MemberEntity element in _concreteTargets) {
+          inferrer.closedWorld.includesClosureCall(localSelector, mask);
+      final newConcreteTargets = _concreteTargets =
+          inferrer.closedWorld.locateMembers(localSelector, mask);
+      for (MemberEntity element in newConcreteTargets) {
         if (!oldTargets.contains(element)) {
           MemberTypeInformation callee =
               inferrer.types.getInferredTypeOfMember(element);
-          callee.addCall(caller, _call);
+          callee.addCall(caller, call);
           inferrer.updateParameterInputs(this, element, arguments, selector,
               remove: false, addToQueue: true);
         }
@@ -1515,13 +1525,13 @@
 
   @override
   void removeAndClearReferences(InferrerEngine inferrer) {
-    for (MemberEntity element in _concreteTargets) {
+    for (MemberEntity element in concreteTargets) {
       MemberTypeInformation callee =
           inferrer.types.getInferredTypeOfMember(element);
       callee.removeUser(this);
     }
     if (arguments != null) {
-      arguments.forEach((info) => info.removeUser(this));
+      arguments!.forEach((info) => info.removeUser(this));
     }
     super.removeAndClearReferences(inferrer);
   }
@@ -1537,9 +1547,9 @@
   @override
   bool hasStableType(InferrerEngine inferrer) {
     return receiver.isStable &&
-        _concreteTargets.every((MemberEntity element) =>
+        concreteTargets.every((MemberEntity element) =>
             inferrer.types.getInferredTypeOfMember(element).isStable) &&
-        (arguments == null || arguments.every((info) => info.isStable)) &&
+        (arguments == null || arguments!.every((info) => info.isStable)) &&
         super.hasStableType(inferrer);
   }
 }
@@ -1550,7 +1560,7 @@
   ClosureCallSiteTypeInformation(
       AbstractValueDomain abstractValueDomain,
       MemberTypeInformation context,
-      ir.Node /*?*/ call,
+      ir.Node? call,
       MemberEntity enclosing,
       Selector selector,
       this.closure,
@@ -1561,7 +1571,7 @@
 
   @override
   void addToGraph(InferrerEngine inferrer) {
-    arguments.forEach((info) => info.addUser(this));
+    arguments!.forEach((info) => info.addUser(this));
     closure.addUser(this);
   }
 
@@ -1763,7 +1773,7 @@
   bool inferred = false;
 
   InferredTypeInformation(AbstractValueDomain abstractValueDomain,
-      MemberTypeInformation context, TypeInformation parentType)
+      MemberTypeInformation? context, TypeInformation? parentType)
       : super(abstractValueDomain.emptyType, context) {
     if (parentType != null) addInput(parentType);
   }
@@ -1789,14 +1799,14 @@
   final AbstractValue originalType;
 
   /// The length at the allocation site.
-  final int originalLength;
+  final int? originalLength;
 
   /// The length after the container has been traced.
-  int inferredLength;
+  late int inferredLength;
 
   ListTypeInformation(
       AbstractValueDomain abstractValueDomain,
-      MemberTypeInformation context,
+      MemberTypeInformation? context,
       this.originalType,
       this.elementType,
       this.originalLength)
@@ -1850,7 +1860,7 @@
 /// elements in a [ListTypeInformation].
 class ElementInContainerTypeInformation extends InferredTypeInformation {
   ElementInContainerTypeInformation(AbstractValueDomain abstractValueDomain,
-      MemberTypeInformation context, elementType)
+      MemberTypeInformation? context, elementType)
       : super(abstractValueDomain, context, elementType);
 
   @override
@@ -1869,7 +1879,7 @@
   final AbstractValue originalType;
 
   SetTypeInformation(
-      MemberTypeInformation context, this.originalType, this.elementType)
+      MemberTypeInformation? context, this.originalType, this.elementType)
       : super(originalType, context) {
     elementType.addUser(this);
   }
@@ -1917,7 +1927,7 @@
 /// [SetTypeInformation].
 class ElementInSetTypeInformation extends InferredTypeInformation {
   ElementInSetTypeInformation(AbstractValueDomain abstractValueDomain,
-      MemberTypeInformation context, elementType)
+      MemberTypeInformation? context, elementType)
       : super(abstractValueDomain, context, elementType);
 
   @override
@@ -1945,32 +1955,32 @@
 
   bool get inDictionaryMode => !bailedOut && _allKeysAreStrings;
 
-  MapTypeInformation(MemberTypeInformation context, this.originalType,
+  MapTypeInformation(MemberTypeInformation? context, this.originalType,
       this.keyType, this.valueType)
       : super(originalType, context) {
     keyType.addUser(this);
     valueType.addUser(this);
   }
 
-  TypeInformation addEntryInput(AbstractValueDomain abstractValueDomain,
+  TypeInformation? addEntryInput(AbstractValueDomain abstractValueDomain,
       TypeInformation key, TypeInformation value,
       [bool nonNull = false]) {
-    TypeInformation newInfo = null;
+    ValueInMapTypeInformation? newInfo = null;
     if (_allKeysAreStrings && key is StringLiteralTypeInformation) {
       String keyString = key.asString();
       typeInfoMap.putIfAbsent(keyString, () {
         newInfo = ValueInMapTypeInformation(
             abstractValueDomain, context, null, nonNull);
-        return newInfo;
+        return newInfo!;
       });
-      typeInfoMap[keyString].addInput(value);
+      typeInfoMap[keyString]!.addInput(value);
     } else {
       _allKeysAreStrings = false;
       typeInfoMap.clear();
     }
     keyType.addInput(key);
     valueType.addInput(value);
-    if (newInfo != null) newInfo.addUser(this);
+    newInfo?.addUser(this);
 
     return newInfo;
   }
@@ -1981,12 +1991,12 @@
     if (_allKeysAreStrings && other.inDictionaryMode) {
       other.typeInfoMap.forEach((keyString, value) {
         typeInfoMap.putIfAbsent(keyString, () {
-          TypeInformation newInfo = ValueInMapTypeInformation(
+          final newInfo = ValueInMapTypeInformation(
               abstractValueDomain, context, null, false);
           newInfos.add(newInfo);
           return newInfo;
         });
-        typeInfoMap[keyString].addInput(value);
+        typeInfoMap[keyString]!.addInput(value);
       });
     } else {
       _allKeysAreStrings = false;
@@ -2018,7 +2028,7 @@
     if (inDictionaryMode) {
       Map<String, AbstractValue> mappings = Map<String, AbstractValue>();
       for (var key in typeInfoMap.keys) {
-        mappings[key] = typeInfoMap[key].type;
+        mappings[key] = typeInfoMap[key]!.type;
       }
       return inferrer.abstractValueDomain.createDictionaryValue(
           abstractValueDomain.getGeneralization(originalType),
@@ -2045,14 +2055,14 @@
     } else if (abstractValueDomain.isDictionary(type)) {
       assert(inDictionaryMode);
       for (String key in typeInfoMap.keys) {
-        TypeInformation value = typeInfoMap[key];
+        final value = typeInfoMap[key]!;
         if (!abstractValueDomain.containsDictionaryKey(type, key) &&
             abstractValueDomain.containsAll(value.type).isDefinitelyFalse &&
             abstractValueDomain.isNull(value.type).isDefinitelyFalse) {
           return toTypeMask(inferrer);
         }
         if (abstractValueDomain.getDictionaryValueForKey(type, key) !=
-            typeInfoMap[key].type) {
+            typeInfoMap[key]!.type) {
           return toTypeMask(inferrer);
         }
       }
@@ -2099,7 +2109,7 @@
 /// for the keys in a [MapTypeInformation]
 class KeyInMapTypeInformation extends InferredTypeInformation {
   KeyInMapTypeInformation(AbstractValueDomain abstractValueDomain,
-      MemberTypeInformation context, TypeInformation keyType)
+      MemberTypeInformation? context, TypeInformation keyType)
       : super(abstractValueDomain, context, keyType);
 
   @override
@@ -2120,7 +2130,7 @@
   final bool nonNull;
 
   ValueInMapTypeInformation(AbstractValueDomain abstractValueDomain,
-      MemberTypeInformation context, TypeInformation valueType,
+      MemberTypeInformation? context, TypeInformation? valueType,
       [this.nonNull = false])
       : super(abstractValueDomain, context, valueType);
 
@@ -2143,13 +2153,13 @@
 /// A [PhiElementTypeInformation] is an union of
 /// [ElementTypeInformation], that is local to a method.
 class PhiElementTypeInformation extends TypeInformation {
-  final ir.Node branchNode;
-  final Local variable;
+  final ir.Node? branchNode;
+  final Local? variable;
   final bool isTry;
 
   PhiElementTypeInformation(AbstractValueDomain abstractValueDomain,
-      MemberTypeInformation context, this.branchNode, this.variable,
-      {this.isTry})
+      MemberTypeInformation? context, this.branchNode, this.variable,
+      {required this.isTry})
       : super(abstractValueDomain.emptyType, context);
 
   @override
@@ -2186,7 +2196,7 @@
   final FunctionEntity _element;
 
   ClosureTypeInformation(AbstractValueDomain abstractValueDomain,
-      MemberTypeInformation context, this._element)
+      MemberTypeInformation? context, this._element)
       : super(abstractValueDomain.emptyType, context);
 
   FunctionEntity get closure => _element;
@@ -2227,7 +2237,7 @@
   /// Set to true once analysis is completed.
   bool analyzed = false;
 
-  Set<TypeInformation> _flowsInto;
+  Set<TypeInformation>? _flowsInto;
 
   /// The set of [TypeInformation] nodes where values from the traced node could
   /// flow in.
@@ -2241,7 +2251,7 @@
     if (_flowsInto == null) {
       _flowsInto = nodes.toSet();
     } else {
-      _flowsInto.addAll(nodes);
+      _flowsInto!.addAll(nodes);
     }
   }
 }
@@ -2326,7 +2336,7 @@
     if (isNullable) {
       otherType = abstractValueDomain.includeNull(otherType);
     }
-    if (type == null) return otherType;
+    assert((type as dynamic) != null); // TODO(48820): Remove when sound.
     AbstractValue newType = abstractValueDomain.intersection(type, otherType);
     return abstractValueDomain.isLateSentinel(type).isPotentiallyTrue
         ? abstractValueDomain.includeLateSentinel(newType)
diff --git a/pkg/compiler/lib/src/inferrer/type_system.dart b/pkg/compiler/lib/src/inferrer/type_system.dart
index 9a90a88..34abce3 100644
--- a/pkg/compiler/lib/src/inferrer/type_system.dart
+++ b/pkg/compiler/lib/src/inferrer/type_system.dart
@@ -2,14 +2,12 @@
 // 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.
 
-// @dart = 2.10
-
 import 'package:kernel/ast.dart' as ir;
 import '../common.dart';
 import '../constants/values.dart' show BoolConstantValue;
 import '../elements/entities.dart';
 import '../elements/types.dart';
-import '../world.dart';
+import '../world_interfaces.dart';
 import 'abstract_value_domain.dart';
 import 'type_graph_nodes.dart';
 
@@ -30,7 +28,7 @@
   void forEachParameter(FunctionEntity function, void f(Local parameter));
 
   /// Returns whether [node] is valid as a general phi node.
-  bool checkPhiNode(ir.Node node);
+  bool checkPhiNode(ir.Node? node);
 
   /// Returns whether [node] is valid as a loop phi node.
   bool checkLoopPhiNode(ir.Node node);
@@ -81,8 +79,7 @@
   final Set<TypeInformation> allocatedClosures = Set<TypeInformation>();
 
   /// Cache of [ConcreteTypeInformation].
-  final Map<AbstractValue, TypeInformation> concreteTypes =
-      Map<AbstractValue, TypeInformation>();
+  final Map<AbstractValue, ConcreteTypeInformation> concreteTypes = {};
 
   /// Cache of some primitive constant types.
   final Map<Object, TypeInformation> primitiveConstantTypes = {};
@@ -113,17 +110,15 @@
         allocatedTypes,
       ].expand((x) => x);
 
-  TypeSystem(this._closedWorld, this.strategy) {
-    nonNullEmptyType = getConcreteTypeFor(_abstractValueDomain.emptyType);
-  }
+  TypeSystem(this._closedWorld, this.strategy);
 
   AbstractValueDomain get _abstractValueDomain =>
       _closedWorld.abstractValueDomain;
 
   /// Used to group [TypeInformation] nodes by the element that triggered their
   /// creation.
-  MemberTypeInformation _currentMember = null;
-  MemberTypeInformation get currentMember => _currentMember;
+  MemberTypeInformation? _currentMember = null;
+  MemberTypeInformation? get currentMember => _currentMember;
 
   void withMember(MemberEntity element, void action()) {
     assert(_currentMember == null,
@@ -133,157 +128,81 @@
     _currentMember = null;
   }
 
-  TypeInformation nullTypeCache;
-  TypeInformation get nullType {
-    if (nullTypeCache != null) return nullTypeCache;
-    return nullTypeCache = getConcreteTypeFor(_abstractValueDomain.nullType);
-  }
+  late final TypeInformation nullType =
+      getConcreteTypeFor(_abstractValueDomain.nullType);
 
-  TypeInformation intTypeCache;
-  TypeInformation get intType {
-    if (intTypeCache != null) return intTypeCache;
-    return intTypeCache = getConcreteTypeFor(_abstractValueDomain.intType);
-  }
+  late final TypeInformation intType =
+      getConcreteTypeFor(_abstractValueDomain.intType);
 
-  TypeInformation uint32TypeCache;
-  TypeInformation get uint32Type {
-    if (uint32TypeCache != null) return uint32TypeCache;
-    return uint32TypeCache =
-        getConcreteTypeFor(_abstractValueDomain.uint32Type);
-  }
+  late final TypeInformation uint32Type =
+      getConcreteTypeFor(_abstractValueDomain.uint32Type);
 
-  TypeInformation uint31TypeCache;
-  TypeInformation get uint31Type {
-    if (uint31TypeCache != null) return uint31TypeCache;
-    return uint31TypeCache =
-        getConcreteTypeFor(_abstractValueDomain.uint31Type);
-  }
+  late final TypeInformation uint31Type =
+      getConcreteTypeFor(_abstractValueDomain.uint31Type);
 
-  TypeInformation positiveIntTypeCache;
-  TypeInformation get positiveIntType {
-    if (positiveIntTypeCache != null) return positiveIntTypeCache;
-    return positiveIntTypeCache =
-        getConcreteTypeFor(_abstractValueDomain.positiveIntType);
-  }
+  late final TypeInformation positiveIntType =
+      getConcreteTypeFor(_abstractValueDomain.positiveIntType);
 
-  TypeInformation numTypeCache;
-  TypeInformation get numType {
-    if (numTypeCache != null) return numTypeCache;
-    return numTypeCache = getConcreteTypeFor(_abstractValueDomain.numType);
-  }
+  late final TypeInformation numType =
+      getConcreteTypeFor(_abstractValueDomain.numType);
 
-  TypeInformation boolTypeCache;
-  TypeInformation get boolType {
-    if (boolTypeCache != null) return boolTypeCache;
-    return boolTypeCache = getConcreteTypeFor(_abstractValueDomain.boolType);
-  }
+  late final TypeInformation boolType =
+      getConcreteTypeFor(_abstractValueDomain.boolType);
 
-  TypeInformation functionTypeCache;
-  TypeInformation get functionType {
-    if (functionTypeCache != null) return functionTypeCache;
-    return functionTypeCache =
-        getConcreteTypeFor(_abstractValueDomain.functionType);
-  }
+  late final TypeInformation functionType =
+      getConcreteTypeFor(_abstractValueDomain.functionType);
 
-  TypeInformation listTypeCache;
-  TypeInformation get listType {
-    if (listTypeCache != null) return listTypeCache;
-    return listTypeCache = getConcreteTypeFor(_abstractValueDomain.listType);
-  }
+  late final TypeInformation listType =
+      getConcreteTypeFor(_abstractValueDomain.listType);
 
-  TypeInformation constListTypeCache;
-  TypeInformation get constListType {
-    if (constListTypeCache != null) return constListTypeCache;
-    return constListTypeCache =
-        getConcreteTypeFor(_abstractValueDomain.constListType);
-  }
+  late final TypeInformation constListType =
+      getConcreteTypeFor(_abstractValueDomain.constListType);
 
-  TypeInformation fixedListTypeCache;
-  TypeInformation get fixedListType {
-    if (fixedListTypeCache != null) return fixedListTypeCache;
-    return fixedListTypeCache =
-        getConcreteTypeFor(_abstractValueDomain.fixedListType);
-  }
+  late final TypeInformation fixedListType =
+      getConcreteTypeFor(_abstractValueDomain.fixedListType);
 
-  TypeInformation growableListTypeCache;
-  TypeInformation get growableListType {
-    if (growableListTypeCache != null) return growableListTypeCache;
-    return growableListTypeCache =
-        getConcreteTypeFor(_abstractValueDomain.growableListType);
-  }
+  late final TypeInformation growableListType =
+      getConcreteTypeFor(_abstractValueDomain.growableListType);
 
-  TypeInformation _mutableArrayType;
-  TypeInformation get mutableArrayType => _mutableArrayType ??=
+  late final TypeInformation mutableArrayType =
       getConcreteTypeFor(_abstractValueDomain.mutableArrayType);
 
-  TypeInformation setTypeCache;
-  TypeInformation get setType =>
-      setTypeCache ??= getConcreteTypeFor(_abstractValueDomain.setType);
+  late final TypeInformation setType =
+      getConcreteTypeFor(_abstractValueDomain.setType);
 
-  TypeInformation constSetTypeCache;
-  TypeInformation get constSetType => constSetTypeCache ??=
+  late final TypeInformation constSetType =
       getConcreteTypeFor(_abstractValueDomain.constSetType);
 
-  TypeInformation mapTypeCache;
-  TypeInformation get mapType {
-    if (mapTypeCache != null) return mapTypeCache;
-    return mapTypeCache = getConcreteTypeFor(_abstractValueDomain.mapType);
-  }
+  late final TypeInformation mapType =
+      getConcreteTypeFor(_abstractValueDomain.mapType);
 
-  TypeInformation constMapTypeCache;
-  TypeInformation get constMapType {
-    if (constMapTypeCache != null) return constMapTypeCache;
-    return constMapTypeCache =
-        getConcreteTypeFor(_abstractValueDomain.constMapType);
-  }
+  late final TypeInformation constMapType =
+      getConcreteTypeFor(_abstractValueDomain.constMapType);
 
-  TypeInformation stringTypeCache;
-  TypeInformation get stringType {
-    if (stringTypeCache != null) return stringTypeCache;
-    return stringTypeCache =
-        getConcreteTypeFor(_abstractValueDomain.stringType);
-  }
+  late final TypeInformation stringType =
+      getConcreteTypeFor(_abstractValueDomain.stringType);
 
-  TypeInformation typeTypeCache;
-  TypeInformation get typeType {
-    if (typeTypeCache != null) return typeTypeCache;
-    return typeTypeCache = getConcreteTypeFor(_abstractValueDomain.typeType);
-  }
+  late final TypeInformation typeType =
+      getConcreteTypeFor(_abstractValueDomain.typeType);
 
-  TypeInformation dynamicTypeCache;
-  TypeInformation get dynamicType {
-    if (dynamicTypeCache != null) return dynamicTypeCache;
-    return dynamicTypeCache =
-        getConcreteTypeFor(_abstractValueDomain.dynamicType);
-  }
+  late final TypeInformation dynamicType =
+      getConcreteTypeFor(_abstractValueDomain.dynamicType);
 
-  TypeInformation asyncFutureTypeCache;
   // Subtype of Future returned by async methods.
-  TypeInformation get asyncFutureType {
-    if (asyncFutureTypeCache != null) return asyncFutureTypeCache;
-    return asyncFutureTypeCache =
-        getConcreteTypeFor(_abstractValueDomain.asyncFutureType);
-  }
+  late final TypeInformation asyncFutureType =
+      getConcreteTypeFor(_abstractValueDomain.asyncFutureType);
 
-  TypeInformation syncStarIterableTypeCache;
-  TypeInformation get syncStarIterableType {
-    if (syncStarIterableTypeCache != null) return syncStarIterableTypeCache;
-    return syncStarIterableTypeCache =
-        getConcreteTypeFor(_abstractValueDomain.syncStarIterableType);
-  }
+  late final TypeInformation syncStarIterableType =
+      getConcreteTypeFor(_abstractValueDomain.syncStarIterableType);
 
-  TypeInformation asyncStarStreamTypeCache;
-  TypeInformation get asyncStarStreamType {
-    if (asyncStarStreamTypeCache != null) return asyncStarStreamTypeCache;
-    return asyncStarStreamTypeCache =
-        getConcreteTypeFor(_abstractValueDomain.asyncStarStreamType);
-  }
+  late final TypeInformation asyncStarStreamType =
+      getConcreteTypeFor(_abstractValueDomain.asyncStarStreamType);
 
-  TypeInformation _lateSentinelType;
-  TypeInformation get lateSentinelType => _lateSentinelType ??=
+  late final TypeInformation lateSentinelType =
       getConcreteTypeFor(_abstractValueDomain.lateSentinelType);
 
-  TypeInformation nonNullEmptyType;
+  late final TypeInformation nonNullEmptyType =
+      getConcreteTypeFor(_abstractValueDomain.emptyType);
 
   TypeInformation stringLiteralType(String value) {
     return StringLiteralTypeInformation(
@@ -312,7 +231,7 @@
   /// Returns the least upper bound between [firstType] and
   /// [secondType].
   TypeInformation computeLUB(
-      TypeInformation firstType, TypeInformation secondType) {
+      TypeInformation? firstType, TypeInformation secondType) {
     if (firstType == null) return secondType;
     if (firstType == secondType) return firstType;
     if (firstType == nonNullEmptyType) return secondType;
@@ -431,7 +350,7 @@
 
   /// Returns the internal inferrer representation for [mask].
   ConcreteTypeInformation getConcreteTypeFor(AbstractValue mask) {
-    assert(mask != null);
+    assert((mask as dynamic) != null); // TODO(48820): Remove when sound.
     return concreteTypes.putIfAbsent(mask, () {
       return ConcreteTypeInformation(mask);
     });
@@ -471,24 +390,24 @@
     return type == nullType;
   }
 
-  TypeInformation allocateList(
-      TypeInformation type, ir.TreeNode node, MemberEntity enclosing,
-      [TypeInformation elementType, int length]) {
+  TypeInformation allocateList(TypeInformation type, ir.TreeNode node,
+      MemberEntity enclosing, TypeInformation elementType,
+      [int? length]) {
     assert(strategy.checkListNode(node));
-    ClassEntity typedDataClass = _closedWorld.commonElements.typedDataClass;
-    bool isTypedArray = typedDataClass != null &&
+    final typedDataClass = _closedWorld.commonElements.typedDataClass;
+    bool isTypedArray =
         _closedWorld.classHierarchy.isInstantiated(typedDataClass) &&
-        _abstractValueDomain
-            .isInstanceOfOrNull(type.type, typedDataClass)
-            .isDefinitelyTrue;
+            _abstractValueDomain
+                .isInstanceOfOrNull(type.type, typedDataClass)
+                .isDefinitelyTrue;
     bool isConst = (type.type == _abstractValueDomain.constListType);
     bool isFixed = (type.type == _abstractValueDomain.fixedListType) ||
         isConst ||
         isTypedArray;
     bool isElementInferred = isConst || isTypedArray;
 
-    int inferredLength = isFixed ? length : null;
-    AbstractValue elementTypeMask =
+    final inferredLength = isFixed ? length : null;
+    final elementTypeMask =
         isElementInferred ? elementType.type : dynamicType.type;
     AbstractValue mask = _abstractValueDomain.createContainerValue(
         type.type, node, enclosing, elementTypeMask, inferredLength);
@@ -512,9 +431,8 @@
     return result;
   }
 
-  TypeInformation allocateSet(
-      TypeInformation type, ir.TreeNode node, MemberEntity enclosing,
-      [TypeInformation elementType]) {
+  TypeInformation allocateSet(TypeInformation type, ir.TreeNode node,
+      MemberEntity enclosing, TypeInformation elementType) {
     assert(strategy.checkSetNode(node));
     bool isConst = type.type == _abstractValueDomain.constSetType;
 
@@ -532,44 +450,47 @@
   }
 
   TypeInformation allocateMap(
-      ConcreteTypeInformation type, ir.TreeNode node, MemberEntity element,
-      [List<TypeInformation> keyTypes, List<TypeInformation> valueTypes]) {
+      ConcreteTypeInformation type,
+      ir.TreeNode node,
+      MemberEntity element,
+      List<TypeInformation> keyTypes,
+      List<TypeInformation> valueTypes) {
     assert(strategy.checkMapNode(node));
     assert(keyTypes.length == valueTypes.length);
     bool isFixed = (type.type == _abstractValueDomain.constMapType);
 
-    TypeInformation keyType, valueType;
+    PhiElementTypeInformation? keyType, valueType;
     for (int i = 0; i < keyTypes.length; ++i) {
-      TypeInformation type = keyTypes[i];
+      final typeForKey = keyTypes[i];
       keyType = keyType == null
-          ? allocatePhi(null, null, type, isTry: false)
-          : addPhiInput(null, keyType, type);
+          ? allocatePhi(null, null, typeForKey, isTry: false)
+          : addPhiInput(null, keyType, typeForKey);
 
-      type = valueTypes[i];
+      final typeForValue = valueTypes[i];
       valueType = valueType == null
-          ? allocatePhi(null, null, type, isTry: false)
-          : addPhiInput(null, valueType, type);
+          ? allocatePhi(null, null, typeForValue, isTry: false)
+          : addPhiInput(null, valueType, typeForValue);
     }
 
-    keyType =
+    final simplifiedKeyType =
         keyType == null ? nonNullEmpty() : simplifyPhi(null, null, keyType);
-    valueType =
+    final simplifiedValueType =
         valueType == null ? nonNullEmpty() : simplifyPhi(null, null, valueType);
 
     AbstractValue keyTypeMask, valueTypeMask;
     if (isFixed) {
-      keyTypeMask = keyType.type;
-      valueTypeMask = valueType.type;
+      keyTypeMask = simplifiedKeyType.type;
+      valueTypeMask = simplifiedValueType.type;
     } else {
       keyTypeMask = valueTypeMask = dynamicType.type;
     }
     AbstractValue mask = _abstractValueDomain.createMapValue(
         type.type, node, element, keyTypeMask, valueTypeMask);
 
-    TypeInformation keyTypeInfo =
-        KeyInMapTypeInformation(_abstractValueDomain, currentMember, keyType);
-    TypeInformation valueTypeInfo = ValueInMapTypeInformation(
-        _abstractValueDomain, currentMember, valueType);
+    final keyTypeInfo = KeyInMapTypeInformation(
+        _abstractValueDomain, currentMember, simplifiedKeyType);
+    final valueTypeInfo = ValueInMapTypeInformation(
+        _abstractValueDomain, currentMember, simplifiedValueType);
     allocatedTypes.add(keyTypeInfo);
     allocatedTypes.add(valueTypeInfo);
 
@@ -577,7 +498,7 @@
         MapTypeInformation(currentMember, mask, keyTypeInfo, valueTypeInfo);
 
     for (int i = 0; i < keyTypes.length; ++i) {
-      TypeInformation newType = map.addEntryInput(
+      final newType = map.addEntryInput(
           _abstractValueDomain, keyTypes[i], valueTypes[i], true);
       if (newType != null) allocatedTypes.add(newType);
     }
@@ -610,7 +531,7 @@
   }
 
   PhiElementTypeInformation _addPhi(
-      ir.Node node, Local variable, TypeInformation inputType, bool isTry) {
+      ir.Node? node, Local? variable, TypeInformation inputType, bool isTry) {
     PhiElementTypeInformation result = PhiElementTypeInformation(
         _abstractValueDomain, currentMember, node, variable,
         isTry: isTry);
@@ -622,8 +543,8 @@
   /// Returns a new type for holding the potential types of [element].
   /// [inputType] is the first incoming type of the phi.
   PhiElementTypeInformation allocatePhi(
-      ir.Node node, Local variable, TypeInformation inputType,
-      {bool isTry}) {
+      ir.Node? node, Local? variable, TypeInformation inputType,
+      {required bool isTry}) {
     assert(strategy.checkPhiNode(node));
     // Check if [inputType] is a phi for a local updated in
     // the try/catch block [node]. If it is, no need to allocate a new
@@ -643,7 +564,7 @@
   /// from other merging uses.
   PhiElementTypeInformation allocateLoopPhi(
       ir.Node node, Local variable, TypeInformation inputType,
-      {bool isTry}) {
+      {required bool isTry}) {
     assert(strategy.checkLoopPhiNode(node));
     return _addPhi(node, variable, inputType, isTry);
   }
@@ -653,14 +574,14 @@
   /// implementation of this method could just return that incoming
   /// input type.
   TypeInformation simplifyPhi(
-      ir.Node node, Local variable, PhiElementTypeInformation phiType) {
+      ir.Node? node, Local? variable, PhiElementTypeInformation phiType) {
     assert(phiType.branchNode == node);
     if (phiType.inputs.length == 1) return phiType.inputs.first;
     return phiType;
   }
 
   /// Adds [newType] as an input of [phiType].
-  PhiElementTypeInformation addPhiInput(Local variable,
+  PhiElementTypeInformation addPhiInput(Local? variable,
       PhiElementTypeInformation phiType, TypeInformation newType) {
     phiType.addInput(newType);
     return phiType;
@@ -697,7 +618,7 @@
       list.add(mask);
     }
 
-    AbstractValue newType = null;
+    AbstractValue? newType;
     for (AbstractValue mask in list) {
       newType =
           newType == null ? mask : _abstractValueDomain.union(newType, mask);
diff --git a/pkg/compiler/lib/src/ir/impact_data.dart b/pkg/compiler/lib/src/ir/impact_data.dart
index fb1f3b2..2a65114 100644
--- a/pkg/compiler/lib/src/ir/impact_data.dart
+++ b/pkg/compiler/lib/src/ir/impact_data.dart
@@ -192,10 +192,6 @@
         }
         registerAsyncStar(elementType);
         break;
-
-      case ir.AsyncMarker.SyncYielding:
-        failedAt(CURRENT_ELEMENT_SPANNABLE,
-            "Unexpected async marker: ${asyncMarker}");
     }
   }
 
diff --git a/pkg/compiler/lib/src/ir/util.dart b/pkg/compiler/lib/src/ir/util.dart
index c9fa392..d96ad3d 100644
--- a/pkg/compiler/lib/src/ir/util.dart
+++ b/pkg/compiler/lib/src/ir/util.dart
@@ -58,7 +58,6 @@
       return AsyncMarker.SYNC;
     case ir.AsyncMarker.SyncStar:
       return AsyncMarker.SYNC_STAR;
-    case ir.AsyncMarker.SyncYielding:
     default:
       throw UnsupportedError(
           "Async marker ${node.asyncMarker} is not supported.");
diff --git a/pkg/compiler/lib/src/js_backend/custom_elements_analysis.dart b/pkg/compiler/lib/src/js_backend/custom_elements_analysis.dart
index a542b6c..a5a1606 100644
--- a/pkg/compiler/lib/src/js_backend/custom_elements_analysis.dart
+++ b/pkg/compiler/lib/src/js_backend/custom_elements_analysis.dart
@@ -2,8 +2,6 @@
 // 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.
 
-// @dart = 2.10
-
 import '../common/elements.dart';
 import '../constants/constant_system.dart' as constant_system;
 import '../constants/values.dart';
@@ -71,7 +69,7 @@
   }
 
   void registerStaticUse(MemberEntity element) {
-    assert(element != null);
+    assert((element as dynamic) != null); // TODO(48820): Remove.
     if (_commonElements.isFindIndexForNativeSubclassType(element)) {
       join.demanded = true;
     }
@@ -151,9 +149,7 @@
   final ElementEnvironment _elementEnvironment;
   final CommonElements _commonElements;
   final NativeBasicData _nativeData;
-  final BackendUsageBuilder _backendUsageBuilder;
-
-  final bool forResolution;
+  final BackendUsageBuilder? _backendUsageBuilder;
 
   // Classes that are candidates for needing constructors.  Classes are moved to
   // [activeClasses] when we know they need constructors.
@@ -173,9 +169,8 @@
 
   CustomElementsAnalysisJoin(
       this._elementEnvironment, this._commonElements, this._nativeData,
-      {BackendUsageBuilder backendUsageBuilder})
-      : this._backendUsageBuilder = backendUsageBuilder,
-        this.forResolution = backendUsageBuilder != null;
+      {BackendUsageBuilder? backendUsageBuilder})
+      : this._backendUsageBuilder = backendUsageBuilder;
 
   WorldImpact flush() {
     if (!demanded) return const WorldImpact();
@@ -196,13 +191,13 @@
           impactBuilder.registerStaticUse(
               StaticUse.constructorInvoke(constructor, CallStructure.NO_ARGS));
         }
-        if (forResolution) {
+        if (_backendUsageBuilder != null) {
           escapingConstructors
-              .forEach(_backendUsageBuilder.registerGlobalFunctionDependency);
+              .forEach(_backendUsageBuilder!.registerGlobalFunctionDependency);
         }
         // Force the generation of the type constant that is the key to an entry
         // in the generated table.
-        ConstantValue constant = _makeTypeConstant(cls);
+        final constant = _makeTypeConstant(cls);
         impactBuilder.registerConstantUse(ConstantUse.customElements(constant));
       }
     }
diff --git a/pkg/compiler/lib/src/js_emitter/native_generator.dart b/pkg/compiler/lib/src/js_emitter/native_generator.dart
index a47b6be..ab7d73a 100644
--- a/pkg/compiler/lib/src/js_emitter/native_generator.dart
+++ b/pkg/compiler/lib/src/js_emitter/native_generator.dart
@@ -2,8 +2,6 @@
 // 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.
 
-// @dart = 2.10
-
 library dart2js.js_emitter.native_generator;
 
 import 'package:js_runtime/synced/embedded_names.dart' as embeddedNames;
diff --git a/pkg/compiler/lib/src/source_file_provider.dart b/pkg/compiler/lib/src/source_file_provider.dart
index df9763b..ee9dea5 100644
--- a/pkg/compiler/lib/src/source_file_provider.dart
+++ b/pkg/compiler/lib/src/source_file_provider.dart
@@ -559,13 +559,18 @@
     }
     api.Input<List<int>> result =
         await readBytesFromUri(resolvedUri, inputKind);
-    switch (inputKind) {
-      case api.InputKind.UTF8:
-        utf8SourceFiles[uri] = utf8SourceFiles[resolvedUri];
-        break;
-      case api.InputKind.binary:
-        binarySourceFiles[uri] = binarySourceFiles[resolvedUri];
-        break;
+    if (uri != resolvedUri) {
+      if (!resolvedUri.isAbsolute) {
+        resolvedUri = cwd.resolveUri(resolvedUri);
+      }
+      switch (inputKind) {
+        case api.InputKind.UTF8:
+          utf8SourceFiles[uri] = utf8SourceFiles[resolvedUri];
+          break;
+        case api.InputKind.binary:
+          binarySourceFiles[uri] = binarySourceFiles[resolvedUri];
+          break;
+      }
     }
     return result;
   }
diff --git a/pkg/compiler/test/analyses/dart2js_allowed.json b/pkg/compiler/test/analyses/dart2js_allowed.json
index 107ef15..746d0ef 100644
--- a/pkg/compiler/test/analyses/dart2js_allowed.json
+++ b/pkg/compiler/test/analyses/dart2js_allowed.json
@@ -27,15 +27,6 @@
     "Dynamic access of 'memberContext'.": 1,
     "Dynamic access of 'name'.": 1
   },
-  "pkg/compiler/lib/src/inferrer/locals_handler.dart": {
-    "Dynamic access of 'length'.": 2,
-    "Dynamic access of 'named'.": 2,
-    "Dynamic access of 'positional'.": 2,
-    "Dynamic invocation of '[]'.": 2
-  },
-  "pkg/compiler/lib/src/inferrer/type_graph_nodes.dart": {
-    "Dynamic access of 'type'.": 4
-  },
   "pkg/compiler/lib/src/inferrer/typemasks/type_mask.dart": {
     "Dynamic access of 'forwardTo'.": 1,
     "Dynamic access of 'isForwarding'.": 1
diff --git a/pkg/compiler/test/serialization/serialization_test_helper.dart b/pkg/compiler/test/serialization/serialization_test_helper.dart
index f50d65b..aa3474c 100644
--- a/pkg/compiler/test/serialization/serialization_test_helper.dart
+++ b/pkg/compiler/test/serialization/serialization_test_helper.dart
@@ -24,17 +24,18 @@
 const List<String> dumpInfoExceptions = [
   '"compilationMoment":',
   '"compilationDuration":',
-  '"toJsonDuration":'
+  '"toJsonDuration":',
+  '"ramUsage":'
 ];
 
-void generateJavaScriptCode(
-    Compiler compiler, GlobalTypeInferenceResults globalTypeInferenceResults) {
+void generateJavaScriptCode(Compiler compiler,
+    GlobalTypeInferenceResults globalTypeInferenceResults) async {
   final codegenInputs = compiler.initializeCodegen(globalTypeInferenceResults);
   final codegenResults = OnDemandCodegenResults(globalTypeInferenceResults,
       codegenInputs, compiler.backendStrategy.functionCompiler);
   final programSize = compiler.runCodegenEnqueuer(codegenResults);
   if (compiler.options.dumpInfo) {
-    compiler.runDumpInfo(codegenResults, programSize);
+    await compiler.runDumpInfo(codegenResults, programSize);
   }
 }
 
@@ -44,7 +45,7 @@
     Compiler compiler,
     SerializationStrategy strategy,
     {bool stoppedAfterClosedWorld = false,
-    bool stoppedAfterTypeInference = false}) {
+    bool stoppedAfterTypeInference = false}) async {
   if (stoppedAfterClosedWorld) {
     JsClosedWorld closedWorld = compiler.backendClosedWorldForTesting;
     var newClosedWorldAndIndices =
@@ -59,7 +60,7 @@
     GlobalTypeInferenceResults newGlobalInferenceResults =
         cloneInferenceResults(
             indices, compiler, globalInferenceResults, strategy);
-    generateJavaScriptCode(compiler, newGlobalInferenceResults);
+    await generateJavaScriptCode(compiler, newGlobalInferenceResults);
   }
   var actualOutput = actualOutputCollector.clear();
   Expect.setEquals(
@@ -181,10 +182,10 @@
       });
   Expect.isTrue(result3b.isSuccess);
 
-  finishCompileAndCompare(
+  await finishCompileAndCompare(
       expectedOutput, collector2, result2.compiler, strategy,
       stoppedAfterClosedWorld: true);
-  finishCompileAndCompare(
+  await finishCompileAndCompare(
       expectedOutput, collector3b, result3b.compiler, strategy,
       stoppedAfterTypeInference: true);
   await dir.delete(recursive: true);
diff --git a/pkg/dart2js_info/lib/binary_serialization.dart b/pkg/dart2js_info/lib/binary_serialization.dart
index 8f788b2..0d707ce 100644
--- a/pkg/dart2js_info/lib/binary_serialization.dart
+++ b/pkg/dart2js_info/lib/binary_serialization.dart
@@ -75,6 +75,7 @@
   void visitProgram(ProgramInfo info) {
     visitFunction(info.entrypoint);
     sink.writeInt(info.size);
+    sink.writeString(info.ramUsage);
     sink.writeStringOrNull(info.dart2jsVersion);
     writeDate(info.compilationMoment);
     writeDuration(info.compilationDuration);
@@ -324,6 +325,7 @@
   ProgramInfo readProgram() {
     final entrypoint = readFunction();
     final size = source.readInt();
+    final ramUsage = source.readString();
     final dart2jsVersion = source.readStringOrNull();
     final compilationMoment = readDate();
     final compilationDuration = readDuration();
@@ -338,6 +340,7 @@
     return ProgramInfo(
         entrypoint: entrypoint,
         size: size,
+        ramUsage: ramUsage,
         dart2jsVersion: dart2jsVersion,
         compilationMoment: compilationMoment,
         compilationDuration: compilationDuration,
diff --git a/pkg/dart2js_info/lib/info.dart b/pkg/dart2js_info/lib/info.dart
index 1462964..509edb0 100644
--- a/pkg/dart2js_info/lib/info.dart
+++ b/pkg/dart2js_info/lib/info.dart
@@ -141,6 +141,7 @@
 
 class ProgramInfo {
   final FunctionInfo entrypoint;
+  final String ramUsage;
   final int size;
   final String? dart2jsVersion;
   final DateTime compilationMoment;
@@ -167,6 +168,7 @@
 
   ProgramInfo(
       {required this.entrypoint,
+      required this.ramUsage,
       required this.size,
       required this.dart2jsVersion,
       required this.compilationMoment,
diff --git a/pkg/dart2js_info/lib/json_info_codec.dart b/pkg/dart2js_info/lib/json_info_codec.dart
index 639776a..937ea4d 100644
--- a/pkg/dart2js_info/lib/json_info_codec.dart
+++ b/pkg/dart2js_info/lib/json_info_codec.dart
@@ -206,6 +206,7 @@
     final programInfo = ProgramInfo(
         entrypoint: parseId(json['entrypoint']) as FunctionInfo,
         size: json['size'],
+        ramUsage: json['ramUsage'],
         compilationMoment: DateTime.parse(json['compilationMoment']),
         dart2jsVersion: json['dart2jsVersion'],
         noSuchMethodEnabled: json['noSuchMethodEnabled'],
@@ -455,6 +456,7 @@
     return {
       'entrypoint': idFor(info.entrypoint).serializedId,
       'size': info.size,
+      'ramUsage': info.ramUsage,
       'dart2jsVersion': info.dart2jsVersion,
       'compilationMoment': '${info.compilationMoment}',
       'compilationDuration': info.compilationDuration.inMicroseconds,
diff --git a/pkg/dart2js_info/test/hello_world/hello_world.js.info.json b/pkg/dart2js_info/test/hello_world/hello_world.js.info.json
index 9006151..c0bf81b 100644
--- a/pkg/dart2js_info/test/hello_world/hello_world.js.info.json
+++ b/pkg/dart2js_info/test/hello_world/hello_world.js.info.json
@@ -47,7 +47,7 @@
         "id": "library/dart:_js_helper::",
         "kind": "library",
         "name": "_js_helper",
-        "size": 12952,
+        "size": 12859,
         "children": [
           "class/dart:_js_helper::BoundClosure",
           "class/dart:_js_helper::Closure",
@@ -262,7 +262,7 @@
         "children": [
           "function/hello_world.dart::main"
         ],
-        "canonicalUri": "file:///usr/local/google/home/natebiggs/dart-sdk/sdk/pkg/dart2js_info/test/hello_world/hello_world.dart"
+        "canonicalUri": "file:///Users/markzipan/Projects/dart-sdk/sdk/pkg/dart2js_info/test/hello_world/hello_world.dart"
       }
     },
     "class": {
@@ -2303,7 +2303,7 @@
         "id": "function/dart:_js_helper::Primitives._objectTypeNameNewRti",
         "kind": "function",
         "name": "_objectTypeNameNewRti",
-        "size": 959,
+        "size": 866,
         "outputUnit": "outputUnit/main",
         "parent": "class/dart:_js_helper::Primitives",
         "children": [],
@@ -2324,7 +2324,7 @@
         ],
         "sideEffects": "SideEffects(reads anything; writes anything)",
         "inlinedCount": 0,
-        "code": "Primitives__objectTypeNameNewRti(object) {\n      var interceptor, dispatchName, t1, $constructor, constructorName;\n      if (object instanceof A.Object)\n        return A._rtiToString(A.instanceType(object), null);\n      interceptor = J.getInterceptor$(object);\n      if (interceptor === B.Interceptor_methods || interceptor === B.JavaScriptObject_methods || false) {\n        dispatchName = B.C_JS_CONST(object);\n        t1 = dispatchName !== \"Object\" && dispatchName !== \"\";\n        if (t1)\n          return dispatchName;\n        $constructor = object.constructor;\n        if (typeof $constructor == \"function\") {\n          constructorName = $constructor.name;\n          if (typeof constructorName == \"string\")\n            t1 = constructorName !== \"Object\" && constructorName !== \"\";\n          else\n            t1 = false;\n          if (t1)\n            return constructorName;\n        }\n      }\n      return A._rtiToString(A.instanceType(object), null);\n    }",
+        "code": "Primitives__objectTypeNameNewRti(object) {\n      var interceptor, dispatchName, $constructor, constructorName;\n      if (object instanceof A.Object)\n        return A._rtiToString(A.instanceType(object), null);\n      interceptor = J.getInterceptor$(object);\n      if (interceptor === B.Interceptor_methods || interceptor === B.JavaScriptObject_methods || false) {\n        dispatchName = B.C_JS_CONST(object);\n        if (dispatchName !== \"Object\" && dispatchName !== \"\")\n          return dispatchName;\n        $constructor = object.constructor;\n        if (typeof $constructor == \"function\") {\n          constructorName = $constructor.name;\n          if (typeof constructorName == \"string\" && constructorName !== \"Object\" && constructorName !== \"\")\n            return constructorName;\n        }\n      }\n      return A._rtiToString(A.instanceType(object), null);\n    }",
         "type": "String Function(Object?)",
         "functionKind": 0
       },
@@ -28711,7 +28711,7 @@
       "id": "outputUnit/main",
       "kind": "outputUnit",
       "name": "main",
-      "size": 90362,
+      "size": 90269,
       "filename": "hello_world.js",
       "imports": []
     }
@@ -28721,11 +28721,12 @@
   "dump_minor_version": 1,
   "program": {
     "entrypoint": "function/hello_world.dart::main",
-    "size": 90362,
+    "size": 90269,
+    "ramUsage": "194.188 MB",
     "dart2jsVersion": null,
-    "compilationMoment": "2022-05-26 21:08:43.608041",
-    "compilationDuration": 3177312,
-    "toJsonDuration": 3000,
+    "compilationMoment": "2022-07-08 12:57:36.142141",
+    "compilationDuration": 1321045,
+    "toJsonDuration": 1000,
     "dumpInfoDuration": 0,
     "noSuchMethodEnabled": false,
     "isRuntimeTypeUsed": false,
diff --git a/pkg/dart2js_info/test/hello_world_deferred/hello_world_deferred.js.info.json b/pkg/dart2js_info/test/hello_world_deferred/hello_world_deferred.js.info.json
index cc4f282..f583ac5 100644
--- a/pkg/dart2js_info/test/hello_world_deferred/hello_world_deferred.js.info.json
+++ b/pkg/dart2js_info/test/hello_world_deferred/hello_world_deferred.js.info.json
@@ -54,7 +54,7 @@
         "id": "library/dart:_js_helper::",
         "kind": "library",
         "name": "_js_helper",
-        "size": 47353,
+        "size": 47260,
         "children": [
           "class/dart:_js_helper::BoundClosure",
           "class/dart:_js_helper::Closure",
@@ -274,7 +274,7 @@
         "id": "library/dart:async::",
         "kind": "library",
         "name": "dart.async",
-        "size": 41039,
+        "size": 41197,
         "children": [
           "class/dart:async::AsyncError",
           "class/dart:async::Completer",
@@ -397,7 +397,7 @@
         "children": [
           "function/hello_world_deferred.dart::main"
         ],
-        "canonicalUri": "file:///usr/local/google/home/natebiggs/dart-sdk/sdk/pkg/dart2js_info/test/hello_world_deferred/hello_world_deferred.dart"
+        "canonicalUri": "file:///Users/markzipan/Projects/dart-sdk/sdk/pkg/dart2js_info/test/hello_world_deferred/hello_world_deferred.dart"
       }
     },
     "class": {
@@ -5410,7 +5410,7 @@
         "id": "function/dart:_js_helper::Primitives._objectTypeNameNewRti",
         "kind": "function",
         "name": "_objectTypeNameNewRti",
-        "size": 995,
+        "size": 902,
         "outputUnit": "outputUnit/main",
         "parent": "class/dart:_js_helper::Primitives",
         "children": [],
@@ -5431,7 +5431,7 @@
         ],
         "sideEffects": "SideEffects(reads anything; writes anything)",
         "inlinedCount": 0,
-        "code": "Primitives__objectTypeNameNewRti(object) {\n      var interceptor, dispatchName, t1, $constructor, constructorName;\n      if (object instanceof A.Object)\n        return A._rtiToString(A.instanceType(object), null);\n      interceptor = J.getInterceptor$(object);\n      if (interceptor === B.Interceptor_methods || interceptor === B.JavaScriptObject_methods || type$.UnknownJavaScriptObject._is(object)) {\n        dispatchName = B.C_JS_CONST(object);\n        t1 = dispatchName !== \"Object\" && dispatchName !== \"\";\n        if (t1)\n          return dispatchName;\n        $constructor = object.constructor;\n        if (typeof $constructor == \"function\") {\n          constructorName = $constructor.name;\n          if (typeof constructorName == \"string\")\n            t1 = constructorName !== \"Object\" && constructorName !== \"\";\n          else\n            t1 = false;\n          if (t1)\n            return constructorName;\n        }\n      }\n      return A._rtiToString(A.instanceType(object), null);\n    }",
+        "code": "Primitives__objectTypeNameNewRti(object) {\n      var interceptor, dispatchName, $constructor, constructorName;\n      if (object instanceof A.Object)\n        return A._rtiToString(A.instanceType(object), null);\n      interceptor = J.getInterceptor$(object);\n      if (interceptor === B.Interceptor_methods || interceptor === B.JavaScriptObject_methods || type$.UnknownJavaScriptObject._is(object)) {\n        dispatchName = B.C_JS_CONST(object);\n        if (dispatchName !== \"Object\" && dispatchName !== \"\")\n          return dispatchName;\n        $constructor = object.constructor;\n        if (typeof $constructor == \"function\") {\n          constructorName = $constructor.name;\n          if (typeof constructorName == \"string\" && constructorName !== \"Object\" && constructorName !== \"\")\n            return constructorName;\n        }\n      }\n      return A._rtiToString(A.instanceType(object), null);\n    }",
         "type": "String Function(Object?)",
         "functionKind": 0
       },
@@ -19721,7 +19721,7 @@
         "id": "function/dart:async::scheduleMicrotask",
         "kind": "function",
         "name": "scheduleMicrotask",
-        "size": 367,
+        "size": 525,
         "outputUnit": "outputUnit/main",
         "parent": "library/dart:async::",
         "children": [],
@@ -19742,7 +19742,7 @@
         ],
         "sideEffects": "SideEffects(reads anything; writes anything)",
         "inlinedCount": 0,
-        "code": "scheduleMicrotask(callback) {\n      var _null = null,\n        currentZone = $.Zone__current;\n      if (B.C__RootZone === currentZone) {\n        A._rootScheduleMicrotask(_null, _null, B.C__RootZone, callback);\n        return;\n      }\n      A._rootScheduleMicrotask(_null, _null, currentZone, type$.void_Function._as(currentZone.bindCallbackGuarded$1(callback)));\n    }",
+        "code": "scheduleMicrotask(callback) {\n      var t1, _null = null,\n        currentZone = $.Zone__current;\n      if (B.C__RootZone === currentZone) {\n        A._rootScheduleMicrotask(_null, _null, B.C__RootZone, callback);\n        return;\n      }\n      t1 = false;\n      if (t1) {\n        A._rootScheduleMicrotask(_null, _null, currentZone, type$.void_Function._as(callback));\n        return;\n      }\n      A._rootScheduleMicrotask(_null, _null, currentZone, type$.void_Function._as(currentZone.bindCallbackGuarded$1(callback)));\n    }",
         "type": "void Function(void Function())",
         "functionKind": 0
       },
@@ -67168,7 +67168,7 @@
       "id": "outputUnit/main",
       "kind": "outputUnit",
       "name": "main",
-      "size": 188050,
+      "size": 188115,
       "filename": "hello_world_deferred.js",
       "imports": []
     }
@@ -67187,11 +67187,12 @@
   "dump_minor_version": 1,
   "program": {
     "entrypoint": "function/hello_world_deferred.dart::main",
-    "size": 188644,
+    "size": 188709,
+    "ramUsage": "218.516 MB",
     "dart2jsVersion": null,
-    "compilationMoment": "2022-05-26 21:07:11.259922",
-    "compilationDuration": 4395336,
-    "toJsonDuration": 9000,
+    "compilationMoment": "2022-07-08 12:58:35.955576",
+    "compilationDuration": 1806706,
+    "toJsonDuration": 3000,
     "dumpInfoDuration": 0,
     "noSuchMethodEnabled": false,
     "isRuntimeTypeUsed": false,
diff --git a/pkg/dart2js_info/test/json_to_proto_test.dart b/pkg/dart2js_info/test/json_to_proto_test.dart
index 240f3c2..5b20745 100644
--- a/pkg/dart2js_info/test/json_to_proto_test.dart
+++ b/pkg/dart2js_info/test/json_to_proto_test.dart
@@ -20,11 +20,11 @@
       final proto = AllInfoProtoCodec().encode(decoded);
 
       expect(proto.program.entrypointId, isNotNull);
-      expect(proto.program.size, 90362);
+      expect(proto.program.size, 90269);
       expect(proto.program.compilationMoment.toInt(),
-          DateTime.parse("2022-05-26 21:08:43.608041").microsecondsSinceEpoch);
+          DateTime.parse("2022-07-08 12:57:36.142141").microsecondsSinceEpoch);
       expect(proto.program.toProtoDuration.toInt(),
-          Duration(milliseconds: 3).inMicroseconds);
+          Duration(milliseconds: 1).inMicroseconds);
       expect(proto.program.dumpInfoDuration.toInt(),
           Duration(milliseconds: 0).inMicroseconds);
       expect(proto.program.noSuchMethodEnabled, isFalse);
diff --git a/pkg/dart2js_info/test/parse_test.dart b/pkg/dart2js_info/test/parse_test.dart
index 92c34d0..be06468 100644
--- a/pkg/dart2js_info/test/parse_test.dart
+++ b/pkg/dart2js_info/test/parse_test.dart
@@ -20,11 +20,11 @@
       expect(program, isNotNull);
 
       expect(program!.entrypoint, isNotNull);
-      expect(program.size, 90362);
+      expect(program.size, 90269);
       expect(program.compilationMoment,
-          DateTime.parse("2022-05-26 21:08:43.608041"));
-      expect(program.compilationDuration, Duration(microseconds: 3177312));
-      expect(program.toJsonDuration, Duration(milliseconds: 3));
+          DateTime.parse("2022-07-08 12:57:36.142141"));
+      expect(program.compilationDuration, Duration(microseconds: 1321045));
+      expect(program.toJsonDuration, Duration(milliseconds: 1));
       expect(program.dumpInfoDuration, Duration(seconds: 0));
       expect(program.noSuchMethodEnabled, false);
       expect(program.minified, false);
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 94bac69..5d15880 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -76,8 +76,7 @@
 import '../modifier.dart'
     show Modifier, constMask, covariantMask, finalMask, lateMask, requiredMask;
 import '../names.dart' show emptyName, minusName, plusName;
-import '../problems.dart'
-    show internalProblem, unexpected, unhandled, unsupported;
+import '../problems.dart' show internalProblem, unhandled, unsupported;
 import '../scope.dart';
 import '../source/diet_parser.dart';
 import '../source/source_class_builder.dart';
@@ -1415,9 +1414,6 @@
 
       case AsyncMarker.Sync:
         break; // skip
-      case AsyncMarker.SyncYielding:
-        unexpected("async, async*, sync, or sync*", "$asyncModifier",
-            member.charOffset, uri);
     }
 
     if (problem != null) {
@@ -3284,8 +3280,8 @@
   @override
   void endIfStatement(Token ifToken, Token? elseToken) {
     Statement? elsePart = popStatementIfNotNull(elseToken);
-    AssignedVariablesNodeInfo<VariableDeclaration> assignedVariablesInfo =
-        pop() as AssignedVariablesNodeInfo<VariableDeclaration>;
+    AssignedVariablesNodeInfo assignedVariablesInfo =
+        pop() as AssignedVariablesNodeInfo;
     Statement thenPart = popStatement();
     Expression condition = pop() as Expression;
     Statement node = forest.createIfStatement(
@@ -3308,7 +3304,7 @@
   void endVariableInitializer(Token assignmentOperator) {
     debugEvent("VariableInitializer");
     assert(assignmentOperator.stringValue == "=");
-    AssignedVariablesNodeInfo<VariableDeclaration>? assignedVariablesInfo;
+    AssignedVariablesNodeInfo? assignedVariablesInfo;
     bool isLate = (currentLocalVariableModifiers & lateMask) != 0;
     Expression initializer = popForValue();
     if (isLate) {
@@ -3535,8 +3531,8 @@
   /// ends. Since these need to be associated with the try statement created in
   /// in [endTryStatement] we store them the stack until the try statement is
   /// created.
-  Link<AssignedVariablesNodeInfo<VariableDeclaration>> tryStatementInfoStack =
-      const Link<AssignedVariablesNodeInfo<VariableDeclaration>>();
+  Link<AssignedVariablesNodeInfo> tryStatementInfoStack =
+      const Link<AssignedVariablesNodeInfo>();
 
   @override
   void beginBlock(Token token, BlockKind blockKind) {
@@ -3728,7 +3724,7 @@
     // [handleForInitializerEmptyStatement],
     // [handleForInitializerExpressionStatement], and
     // [handleForInitializerLocalVariableDeclaration].
-    AssignedVariablesNodeInfo<VariableDeclaration> assignedVariablesNodeInfo =
+    AssignedVariablesNodeInfo assignedVariablesNodeInfo =
         typeInferrer.assignedVariables.popNode();
 
     Object? variableOrExpression = pop();
@@ -3795,7 +3791,7 @@
     // [handleForInitializerEmptyStatement],
     // [handleForInitializerExpressionStatement], and
     // [handleForInitializerLocalVariableDeclaration].
-    AssignedVariablesNodeInfo<VariableDeclaration> assignedVariablesNodeInfo =
+    AssignedVariablesNodeInfo assignedVariablesNodeInfo =
         typeInferrer.assignedVariables.deferNode();
 
     Object? variableOrExpression = pop();
@@ -4383,8 +4379,8 @@
     debugEvent("ConditionalExpression");
     Expression elseExpression = popForValue();
     Expression thenExpression = pop() as Expression;
-    AssignedVariablesNodeInfo<VariableDeclaration> assignedVariablesInfo =
-        pop() as AssignedVariablesNodeInfo<VariableDeclaration>;
+    AssignedVariablesNodeInfo assignedVariablesInfo =
+        pop() as AssignedVariablesNodeInfo;
     Expression condition = pop() as Expression;
     Expression node = forest.createConditionalExpression(
         offsetForToken(question), condition, thenExpression, elseExpression);
@@ -5757,8 +5753,8 @@
     debugEvent("endIfElseControlFlow");
     Object? elseEntry = pop(); // else entry
     Object? thenEntry = pop(); // then entry
-    AssignedVariablesNodeInfo<VariableDeclaration> assignedVariablesInfo =
-        pop() as AssignedVariablesNodeInfo<VariableDeclaration>;
+    AssignedVariablesNodeInfo assignedVariablesInfo =
+        pop() as AssignedVariablesNodeInfo;
     Object? condition = pop(); // parenthesized expression
     Token ifToken = pop() as Token;
 
@@ -6232,7 +6228,7 @@
     }
 
     // This is matched by the call to [beginNode] in [handleForInLoopParts].
-    AssignedVariablesNodeInfo<VariableDeclaration> assignedVariablesNodeInfo =
+    AssignedVariablesNodeInfo assignedVariablesNodeInfo =
         typeInferrer.assignedVariables.popNode();
 
     Expression iterable = popForValue();
@@ -6348,7 +6344,7 @@
     Token? awaitToken = pop(NullValue.AwaitToken) as Token?;
 
     // This is matched by the call to [beginNode] in [handleForInLoopParts].
-    AssignedVariablesNodeInfo<VariableDeclaration> assignedVariablesNodeInfo =
+    AssignedVariablesNodeInfo assignedVariablesNodeInfo =
         typeInferrer.assignedVariables.deferNode();
 
     Expression expression = popForValue();
diff --git a/pkg/front_end/lib/src/fasta/source/source_loader.dart b/pkg/front_end/lib/src/fasta/source/source_loader.dart
index 4f62e70..c039e08 100644
--- a/pkg/front_end/lib/src/fasta/source/source_loader.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_loader.dart
@@ -2730,45 +2730,14 @@
 
 class num {}
 
-class _SyncIterable {}
-
-class _SyncIterator {
-  var _current;
-  var _yieldEachIterable;
-}
-
 class Function {}
 """;
 
 /// A minimal implementation of dart:async that is sufficient to create an
 /// instance of [CoreTypes] and compile program.
 const String defaultDartAsyncSource = """
-_asyncErrorWrapperHelper(continuation) {}
-
 void _asyncStarMoveNextHelper(var stream) {}
 
-_asyncThenWrapperHelper(continuation) {}
-
-_awaitHelper(object, thenCallback, errorCallback) {}
-
-_completeOnAsyncReturn(_future, value, async_jump_var) {}
-
-_completeWithNoFutureOnAsyncReturn(_future, value, async_jump_var) {}
-
-_completeOnAsyncError(_future, e, st, async_jump_var) {}
-
-class _AsyncStarStreamController {
-  add(event) {}
-
-  addError(error, stackTrace) {}
-
-  addStream(stream) {}
-
-  close() {}
-
-  get stream => null;
-}
-
 abstract class Completer {
   factory Completer.sync() => null;
 
diff --git a/pkg/front_end/lib/src/fasta/type_inference/inference_visitor.dart b/pkg/front_end/lib/src/fasta/type_inference/inference_visitor.dart
index 8b82433..5926dff 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/inference_visitor.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/inference_visitor.dart
@@ -4218,7 +4218,7 @@
       {required bool isNot}) {
     // ignore: unnecessary_null_comparison
     assert(isNot != null);
-    EqualityInfo<VariableDeclaration, DartType>? equalityInfo =
+    EqualityInfo<DartType>? equalityInfo =
         flowAnalysis.equalityOperand_end(left, leftType);
     bool typeNeeded = !isTopLevel;
 
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 bb686bf..85db894 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
@@ -2456,7 +2456,7 @@
               typeChecksNeeded);
     }
 
-    List<EqualityInfo<VariableDeclaration, DartType>?>? identicalInfo =
+    List<EqualityInfo<DartType>?>? identicalInfo =
         isIdentical && arguments.positional.length == 2 ? [] : null;
     int positionalIndex = 0;
     int namedIndex = 0;
diff --git a/pkg/front_end/test/id_tests/assigned_variables_test.dart b/pkg/front_end/test/id_tests/assigned_variables_test.dart
index 23a5b09..53af2eb 100644
--- a/pkg/front_end/test/id_tests/assigned_variables_test.dart
+++ b/pkg/front_end/test/id_tests/assigned_variables_test.dart
@@ -74,8 +74,8 @@
         _convertVars(_assignedVariables.capturedAnywhere));
   }
 
-  Set<String> _convertVars(Iterable<VariableDeclaration> x) =>
-      x.map((e) => e.name!).toSet();
+  Set<String> _convertVars(Iterable<int> x) =>
+      x.map((e) => _assignedVariables.variableForKey(e).name!).toSet();
 
   @override
   _Data? computeNodeValue(Id id, TreeNode node) {
diff --git a/pkg/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt
index e401254..7105235e 100644
--- a/pkg/front_end/test/spell_checking_list_code.txt
+++ b/pkg/front_end/test/spell_checking_list_code.txt
@@ -1081,6 +1081,7 @@
 reexports
 ref
 refactoring
+reference's
 refined
 reflect
 reflectee
@@ -1496,6 +1497,7 @@
 url
 urls
 usages
+user's
 usr
 usual
 usually
diff --git a/pkg/front_end/testcases/general/issue38253c.dart.strong.transformed.expect b/pkg/front_end/testcases/general/issue38253c.dart.strong.transformed.expect
deleted file mode 100644
index eb54dc9..0000000
--- a/pkg/front_end/testcases/general/issue38253c.dart.strong.transformed.expect
+++ /dev/null
@@ -1,69 +0,0 @@
-library;
-//
-// Problems in library:
-//
-// pkg/front_end/testcases/general/issue38253c.dart:6:3: Error: 'g' isn't a type.
-//   g f1() {}
-//   ^
-//
-// pkg/front_end/testcases/general/issue38253c.dart:7:3: Error: 'g' isn't a type.
-//   g f2() async {}
-//   ^
-//
-import self as self;
-import "dart:async" as asy;
-import "dart:core" as core;
-
-static field () →* Null a = () → Null {
-  function f1() → invalid-type {}
-  function f2() → invalid-type /* originally async */ {
-    final asy::_Future<dynamic>* :async_future = new asy::_Future::•<dynamic>();
-    core::bool* :is_sync = false;
-    FutureOr<dynamic>* :return_value;
-    (dynamic) →* dynamic :async_op_then;
-    (core::Object*, core::StackTrace*) →* dynamic :async_op_error;
-    core::int* :await_jump_var = 0;
-    dynamic :await_ctx_var;
-    function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding 
-      try {
-        #L1:
-        {}
-        asy::_completeOnAsyncReturn(:async_future, :return_value, :is_sync);
-        return;
-      }
-      on dynamic catch(dynamic exception, core::StackTrace* stack_trace) {
-        asy::_completeOnAsyncError(:async_future, exception, stack_trace, :is_sync);
-      }
-    :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
-    :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
-    :async_op.call();
-    :is_sync = true;
-    return :async_future;
-  }
-  function f3() → core::int* {}
-  function f4() → asy::Future<core::int*>* /* originally async */ {
-    final asy::_Future<core::int*>* :async_future = new asy::_Future::•<core::int*>();
-    core::bool* :is_sync = false;
-    FutureOr<core::int*>* :return_value;
-    (dynamic) →* dynamic :async_op_then;
-    (core::Object*, core::StackTrace*) →* dynamic :async_op_error;
-    core::int* :await_jump_var = 0;
-    dynamic :await_ctx_var;
-    function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding 
-      try {
-        #L2:
-        {}
-        asy::_completeOnAsyncReturn(:async_future, :return_value, :is_sync);
-        return;
-      }
-      on dynamic catch(dynamic exception, core::StackTrace* stack_trace) {
-        asy::_completeOnAsyncError(:async_future, exception, stack_trace, :is_sync);
-      }
-    :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
-    :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
-    :async_op.call();
-    :is_sync = true;
-    return :async_future;
-  }
-};
-static method main() → dynamic {}
diff --git a/pkg/frontend_server/lib/frontend_server.dart b/pkg/frontend_server/lib/frontend_server.dart
index 5d7088c..9065163 100644
--- a/pkg/frontend_server/lib/frontend_server.dart
+++ b/pkg/frontend_server/lib/frontend_server.dart
@@ -58,8 +58,7 @@
       help: 'Whether dart:mirrors is supported. By default dart:mirrors is '
           'supported when --aot and --minimal-kernel are not used.',
       defaultsTo: null)
-  ..addFlag('compact-async',
-      help: 'Enable new compact async/await implementation.', defaultsTo: true)
+  ..addFlag('compact-async', help: 'Obsolete, ignored.', hide: true)
   ..addFlag('tfa',
       help:
           'Enable global type flow analysis and related transformations in AOT mode.',
@@ -543,7 +542,6 @@
       nullSafety: compilerOptions.nnbdMode == NnbdMode.Strong,
       supportMirrors: options['support-mirrors'] ??
           !(options['aot'] || options['minimal-kernel']),
-      compactAsync: options['compact-async'],
     );
     if (compilerOptions.target == null) {
       print('Failed to create front-end target ${options['target']}.');
diff --git a/pkg/kernel/binary.md b/pkg/kernel/binary.md
index 46ecd90..db46616 100644
--- a/pkg/kernel/binary.md
+++ b/pkg/kernel/binary.md
@@ -147,7 +147,7 @@
 
 type ComponentFile {
   UInt32 magic = 0x90ABCDEF;
-  UInt32 formatVersion = 82;
+  UInt32 formatVersion = 83;
   Byte[10] shortSdkHash;
   List<String> problemsAsJson; // Described in problems.md.
   Library[] libraries;
@@ -510,8 +510,7 @@
   Sync,
   SyncStar,
   Async,
-  AsyncStar,
-  SyncYielding
+  AsyncStar
 }
 */
 
@@ -1398,7 +1397,7 @@
 type YieldStatement extends Statement {
   Byte tag = 77;
   FileOffset fileOffset;
-  Byte flags (isYieldStar, isNative);
+  Byte flags (isYieldStar);
   Expression expression;
 }
 
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index e8a0d0e..74fe943 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -3956,43 +3956,6 @@
   SyncStar,
   Async,
   AsyncStar,
-
-  // `SyncYielding` is a marker that tells Dart VM that this function is an
-  // artificial closure introduced by an async transformer which desugared all
-  // async syntax into a combination of native yields and helper method calls.
-  //
-  // Native yields (formatted as `[yield]`) are semantically close to
-  // `yield x` statement: they denote a yield/resume point within a function
-  // but are completely decoupled from the notion of iterators. When
-  // execution of the closure reaches `[yield] x` it stops and return the
-  // value of `x` to the caller. If closure is called again it continues
-  // to the next statement after this yield as if it was suspended and resumed.
-  //
-  // Consider this example:
-  //
-  //   g() {
-  //     var :await_jump_var = 0;
-  //     var :await_ctx_var;
-  //
-  //     f(x) yielding {
-  //       [yield] '${x}:0';
-  //       [yield] '${x}:1';
-  //       [yield] '${x}:2';
-  //     }
-  //
-  //     return f;
-  //   }
-  //
-  //   print(f('a'));  /* prints 'a:0', :await_jump_var = 1  */
-  //   print(f('b'));  /* prints 'b:1', :await_jump_var = 2  */
-  //   print(f('c'));  /* prints 'c:2', :await_jump_var = 3  */
-  //
-  // Note: currently Dart VM implicitly relies on async transformer to
-  // inject certain artificial variables into g (like `:await_jump_var`).
-  // As such SyncYielding and native yield are not intended to be used on their
-  // own, but are rather an implementation artifact of the async transformer
-  // itself.
-  SyncYielding,
 }
 
 // ------------------------------------------------------------------------
@@ -10523,33 +10486,23 @@
 }
 
 /// Statement of form `yield x` or `yield* x`.
-///
-/// For native yield semantics see `AsyncMarker.SyncYielding`.
 class YieldStatement extends Statement {
   Expression expression;
   int flags = 0;
 
-  YieldStatement(this.expression,
-      {bool isYieldStar: false, bool isNative: false}) {
+  YieldStatement(this.expression, {bool isYieldStar: false}) {
     expression.parent = this;
     this.isYieldStar = isYieldStar;
-    this.isNative = isNative;
   }
 
   static const int FlagYieldStar = 1 << 0;
-  static const int FlagNative = 1 << 1;
 
   bool get isYieldStar => flags & FlagYieldStar != 0;
-  bool get isNative => flags & FlagNative != 0;
 
   void set isYieldStar(bool value) {
     flags = value ? (flags | FlagYieldStar) : (flags & ~FlagYieldStar);
   }
 
-  void set isNative(bool value) {
-    flags = value ? (flags | FlagNative) : (flags & ~FlagNative);
-  }
-
   @override
   R accept<R>(StatementVisitor<R> v) => v.visitYieldStatement(this);
 
diff --git a/pkg/kernel/lib/binary/ast_from_binary.dart b/pkg/kernel/lib/binary/ast_from_binary.dart
index a4fd2ba..f37d04d 100644
--- a/pkg/kernel/lib/binary/ast_from_binary.dart
+++ b/pkg/kernel/lib/binary/ast_from_binary.dart
@@ -2895,8 +2895,7 @@
     int offset = readOffset();
     int flags = readByte();
     return new YieldStatement(readExpression(),
-        isYieldStar: flags & YieldStatement.FlagYieldStar != 0,
-        isNative: flags & YieldStatement.FlagNative != 0)
+        isYieldStar: flags & YieldStatement.FlagYieldStar != 0)
       ..fileOffset = offset;
   }
 
diff --git a/pkg/kernel/lib/binary/tag.dart b/pkg/kernel/lib/binary/tag.dart
index 754bbd6..2e861b6 100644
--- a/pkg/kernel/lib/binary/tag.dart
+++ b/pkg/kernel/lib/binary/tag.dart
@@ -179,7 +179,7 @@
   /// Internal version of kernel binary format.
   /// Bump it when making incompatible changes in kernel binaries.
   /// Keep in sync with runtime/vm/kernel_binary.h, pkg/kernel/binary.md.
-  static const int BinaryFormatVersion = 82;
+  static const int BinaryFormatVersion = 83;
 }
 
 abstract class ConstantTag {
diff --git a/pkg/kernel/lib/core_types.dart b/pkg/kernel/lib/core_types.dart
index 70edb2c..1734950 100644
--- a/pkg/kernel/lib/core_types.dart
+++ b/pkg/kernel/lib/core_types.dart
@@ -119,57 +119,15 @@
   CoreTypes(Component component)
       : index = new LibraryIndex.coreLibraries(component);
 
-  late final Procedure asyncErrorWrapperHelperProcedure =
-      index.getTopLevelProcedure('dart:async', '_asyncErrorWrapperHelper');
-
   late final Library asyncLibrary = index.getLibrary('dart:async');
 
-  late final Procedure asyncStarStreamControllerAdd =
-      index.getProcedure('dart:async', '_AsyncStarStreamController', 'add');
-
-  late final Procedure asyncStarStreamControllerAddError = index.getProcedure(
-      'dart:async', '_AsyncStarStreamController', 'addError');
-
-  late final Procedure asyncStarStreamControllerAddStream = index.getProcedure(
-      'dart:async', '_AsyncStarStreamController', 'addStream');
-
-  late final Class asyncStarStreamControllerClass =
-      index.getClass('dart:async', '_AsyncStarStreamController');
-
-  late final Procedure asyncStarStreamControllerClose =
-      index.getProcedure('dart:async', '_AsyncStarStreamController', 'close');
-
-  late final Constructor asyncStarStreamControllerDefaultConstructor =
-      index.getConstructor('dart:async', '_AsyncStarStreamController', '');
-
-  late final Member asyncStarStreamControllerStream =
-      index.getMember('dart:async', '_AsyncStarStreamController', 'get:stream');
-
   late final Procedure asyncStarMoveNextHelper =
       index.getTopLevelProcedure('dart:async', '_asyncStarMoveNextHelper');
 
-  late final Procedure asyncThenWrapperHelperProcedure =
-      index.getTopLevelProcedure('dart:async', '_asyncThenWrapperHelper');
-
-  late final Procedure awaitHelperProcedure =
-      index.getTopLevelProcedure('dart:async', '_awaitHelper');
-
   late final Class boolClass = index.getClass('dart:core', 'bool');
 
   late final Class futureImplClass = index.getClass('dart:async', '_Future');
 
-  late final Constructor futureImplConstructor =
-      index.getConstructor('dart:async', '_Future', '');
-
-  late final Procedure completeOnAsyncReturn =
-      index.getTopLevelProcedure('dart:async', '_completeOnAsyncReturn');
-
-  late final Procedure completeWithNoFutureOnAsyncReturn = index
-      .getTopLevelProcedure('dart:async', '_completeWithNoFutureOnAsyncReturn');
-
-  late final Procedure completeOnAsyncError =
-      index.getTopLevelProcedure('dart:async', '_completeOnAsyncError');
-
   late final Library coreLibrary = index.getLibrary('dart:core');
 
   late final Class doubleClass = index.getClass('dart:core', 'double');
@@ -287,18 +245,6 @@
 
   late final Class symbolClass = index.getClass('dart:core', 'Symbol');
 
-  late final Constructor syncIterableDefaultConstructor =
-      index.getConstructor('dart:core', '_SyncIterable', '');
-
-  late final Class syncIteratorClass =
-      index.getClass('dart:core', '_SyncIterator');
-
-  late final Member syncIteratorCurrent =
-      index.getMember('dart:core', '_SyncIterator', '_current');
-
-  late final Member syncIteratorYieldEachIterable =
-      index.getMember('dart:core', '_SyncIterator', '_yieldEachIterable');
-
   late final Class typeClass = index.getClass('dart:core', 'Type');
 
   late final Constructor fallThroughErrorUrlAndLineConstructor =
diff --git a/pkg/kernel/lib/target/targets.dart b/pkg/kernel/lib/target/targets.dart
index c466238..477f255 100644
--- a/pkg/kernel/lib/target/targets.dart
+++ b/pkg/kernel/lib/target/targets.dart
@@ -16,13 +16,11 @@
   final bool trackWidgetCreation;
   final bool enableNullSafety;
   final bool supportMirrors;
-  final bool compactAsync;
 
   const TargetFlags(
       {this.trackWidgetCreation = false,
       this.enableNullSafety = false,
-      this.supportMirrors = true,
-      this.compactAsync = true});
+      this.supportMirrors = true});
 
   @override
   bool operator ==(other) {
@@ -30,8 +28,7 @@
     return other is TargetFlags &&
         trackWidgetCreation == other.trackWidgetCreation &&
         enableNullSafety == other.enableNullSafety &&
-        supportMirrors == other.supportMirrors &&
-        compactAsync == other.compactAsync;
+        supportMirrors == other.supportMirrors;
   }
 
   @override
@@ -40,7 +37,6 @@
     hash = 0x3fffffff & (hash * 31 + (hash ^ trackWidgetCreation.hashCode));
     hash = 0x3fffffff & (hash * 31 + (hash ^ enableNullSafety.hashCode));
     hash = 0x3fffffff & (hash * 31 + (hash ^ supportMirrors.hashCode));
-    hash = 0x3fffffff & (hash * 31 + (hash ^ compactAsync.hashCode));
     return hash;
   }
 }
diff --git a/pkg/kernel/lib/text/ast_to_text.dart b/pkg/kernel/lib/text/ast_to_text.dart
index d7946d2..9b97a47 100644
--- a/pkg/kernel/lib/text/ast_to_text.dart
+++ b/pkg/kernel/lib/text/ast_to_text.dart
@@ -818,8 +818,6 @@
         return 'async';
       case AsyncMarker.AsyncStar:
         return 'async*';
-      case AsyncMarker.SyncYielding:
-        return 'yielding';
       default:
         return '<Invalid async marker: $marker>';
     }
@@ -2414,8 +2412,6 @@
     writeIndentation();
     if (node.isYieldStar) {
       writeWord('yield*');
-    } else if (node.isNative) {
-      writeWord('[yield]');
     } else {
       writeWord('yield');
     }
diff --git a/pkg/kernel/lib/type_checker.dart b/pkg/kernel/lib/type_checker.dart
index 38dec89..6f2a341 100644
--- a/pkg/kernel/lib/type_checker.dart
+++ b/pkg/kernel/lib/type_checker.dart
@@ -363,26 +363,6 @@
       case AsyncMarker.AsyncStar:
         return null;
 
-      case AsyncMarker.SyncYielding:
-        // The SyncStar transform wraps the original function body twice,
-        // where the inner most function returns bool.
-        TreeNode? parent = function.parent;
-        while (parent is! FunctionNode) {
-          parent = parent!.parent;
-        }
-        FunctionNode enclosingFunction = parent;
-        if (enclosingFunction.dartAsyncMarker == AsyncMarker.Sync) {
-          parent = enclosingFunction.parent;
-          while (parent is! FunctionNode) {
-            parent = parent!.parent;
-          }
-          enclosingFunction = parent;
-          if (enclosingFunction.dartAsyncMarker == AsyncMarker.SyncStar) {
-            return coreTypes.boolLegacyRawType;
-          }
-        }
-        return null;
-
       default:
         throw 'Unexpected async marker: ${function.asyncMarker}';
     }
@@ -405,9 +385,6 @@
         }
         return const DynamicType();
 
-      case AsyncMarker.SyncYielding:
-        return function.returnType;
-
       default:
         throw 'Unexpected async marker: ${function.asyncMarker}';
     }
diff --git a/pkg/kernel/lib/verifier.dart b/pkg/kernel/lib/verifier.dart
index 20c670e..9202afa 100644
--- a/pkg/kernel/lib/verifier.dart
+++ b/pkg/kernel/lib/verifier.dart
@@ -528,7 +528,6 @@
     switch (currentAsyncMarker) {
       case AsyncMarker.Sync:
       case AsyncMarker.Async:
-      case AsyncMarker.SyncYielding:
         // ok
         break;
       case AsyncMarker.SyncStar:
@@ -556,7 +555,6 @@
         break;
       case AsyncMarker.SyncStar:
       case AsyncMarker.AsyncStar:
-      case AsyncMarker.SyncYielding:
         // ok
         break;
     }
diff --git a/pkg/nnbd_migration/lib/src/edge_builder.dart b/pkg/nnbd_migration/lib/src/edge_builder.dart
index d6bfd32..ba12bca 100644
--- a/pkg/nnbd_migration/lib/src/edge_builder.dart
+++ b/pkg/nnbd_migration/lib/src/edge_builder.dart
@@ -1595,7 +1595,7 @@
 
   @override
   DecoratedType? visitPrefixedIdentifier(PrefixedIdentifier node) {
-    if (node.prefix.staticElement is ImportElement) {
+    if (node.prefix.staticElement is ImportElement2) {
       // TODO(paulberry)
       _unimplemented(node, 'PrefixedIdentifier with a prefix');
     } else {
diff --git a/pkg/nnbd_migration/lib/src/fix_aggregator.dart b/pkg/nnbd_migration/lib/src/fix_aggregator.dart
index ddb14d6..1f1a071 100644
--- a/pkg/nnbd_migration/lib/src/fix_aggregator.dart
+++ b/pkg/nnbd_migration/lib/src/fix_aggregator.dart
@@ -60,11 +60,11 @@
 
   FixAggregator._(this.planner, this._changes, this._warnOnWeakCode,
       CompilationUnitElement compilationUnitElement) {
-    for (var importElement in compilationUnitElement.library.imports) {
+    for (var importElement in compilationUnitElement.library.imports2) {
       // TODO(paulberry): the `??=` should ensure that if there are two imports,
       // one prefixed and one not, we prefer the prefix.  Test this.
       _importPrefixes[importElement.importedLibrary] ??=
-          importElement.prefix?.name;
+          importElement.prefix?.element.name;
     }
   }
 
diff --git a/pkg/vm/lib/kernel_front_end.dart b/pkg/vm/lib/kernel_front_end.dart
index 6e70384..7ffdc7f 100644
--- a/pkg/vm/lib/kernel_front_end.dart
+++ b/pkg/vm/lib/kernel_front_end.dart
@@ -81,8 +81,7 @@
       help: 'Whether dart:mirrors is supported. By default dart:mirrors is '
           'supported when --aot and --minimal-kernel are not used.',
       defaultsTo: null);
-  args.addFlag('compact-async',
-      help: 'Enable new compact async/await implementation.', defaultsTo: true);
+  args.addFlag('compact-async', help: 'Obsolete, ignored.', hide: true);
   args.addOption('depfile', help: 'Path to output Ninja depfile');
   args.addOption('from-dill',
       help: 'Read existing dill file instead of compiling from sources',
@@ -203,7 +202,6 @@
   final String? manifestFilename = options['manifest'];
   final String? dataDir = options['component-name'] ?? options['data-dir'];
   final bool? supportMirrors = options['support-mirrors'];
-  final bool compactAsync = options['compact-async'];
 
   final bool minimalKernel = options['minimal-kernel'];
   final bool treeShakeWriteOnlyFields = options['tree-shake-write-only-fields'];
@@ -287,8 +285,7 @@
   compilerOptions.target = createFrontEndTarget(targetName,
       trackWidgetCreation: options['track-widget-creation'],
       nullSafety: compilerOptions.nnbdMode == NnbdMode.Strong,
-      supportMirrors: supportMirrors ?? !(aot || minimalKernel),
-      compactAsync: compactAsync);
+      supportMirrors: supportMirrors ?? !(aot || minimalKernel));
   if (compilerOptions.target == null) {
     print('Failed to create front-end target $targetName.');
     return badUsageExitCode;
@@ -617,16 +614,14 @@
 Target? createFrontEndTarget(String targetName,
     {bool trackWidgetCreation = false,
     bool nullSafety = false,
-    bool supportMirrors = true,
-    bool compactAsync = true}) {
+    bool supportMirrors = true}) {
   // Make sure VM-specific targets are available.
   installAdditionalTargets();
 
   final TargetFlags targetFlags = new TargetFlags(
       trackWidgetCreation: trackWidgetCreation,
       enableNullSafety: nullSafety,
-      supportMirrors: supportMirrors,
-      compactAsync: compactAsync);
+      supportMirrors: supportMirrors);
   return getTarget(targetName, targetFlags);
 }
 
diff --git a/pkg/vm/lib/target/vm.dart b/pkg/vm/lib/target/vm.dart
index b706bd5..74c4842 100644
--- a/pkg/vm/lib/target/vm.dart
+++ b/pkg/vm/lib/target/vm.dart
@@ -9,11 +9,8 @@
 import 'package:kernel/reference_from_index.dart';
 import 'package:kernel/target/changed_structure_notifier.dart';
 import 'package:kernel/target/targets.dart';
-import 'package:kernel/type_environment.dart';
 
 import '../transformations/call_site_annotator.dart' as callSiteAnnotator;
-import '../transformations/continuation.dart' as transformAsync
-    show transformLibraries, transformProcedure;
 import '../transformations/lowering.dart' as lowering
     show transformLibraries, transformProcedure;
 import '../transformations/mixin_full_resolution.dart' as transformMixins
@@ -183,15 +180,9 @@
       logger?.call("Transformed ffi annotations");
     }
 
-    // TODO(kmillikin): Make this run on a per-method basis.
     bool productMode = environmentDefines!["dart.vm.product"] == "true";
-    transformAsync.transformLibraries(
-        new TypeEnvironment(coreTypes, hierarchy), libraries,
-        productMode: productMode, desugarAsync: !flags.compactAsync);
-    logger?.call("Transformed async methods");
-
-    lowering.transformLibraries(
-        libraries, coreTypes, hierarchy, flags.enableNullSafety);
+    lowering.transformLibraries(libraries, coreTypes, hierarchy,
+        nullSafety: flags.enableNullSafety, productMode: productMode);
     logger?.call("Lowering transformations performed");
 
     callSiteAnnotator.transformLibraries(
@@ -207,13 +198,8 @@
       Map<String, String>? environmentDefines,
       {void Function(String msg)? logger}) {
     bool productMode = environmentDefines!["dart.vm.product"] == "true";
-    transformAsync.transformProcedure(
-        new TypeEnvironment(coreTypes, hierarchy), procedure,
-        productMode: productMode, desugarAsync: !flags.compactAsync);
-    logger?.call("Transformed async functions");
-
-    lowering.transformProcedure(
-        procedure, coreTypes, hierarchy, flags.enableNullSafety);
+    lowering.transformProcedure(procedure, coreTypes, hierarchy,
+        nullSafety: flags.enableNullSafety, productMode: productMode);
     logger?.call("Lowering transformations performed");
   }
 
diff --git a/pkg/vm/lib/transformations/async.dart b/pkg/vm/lib/transformations/async.dart
deleted file mode 100644
index 6a6628b..0000000
--- a/pkg/vm/lib/transformations/async.dart
+++ /dev/null
@@ -1,672 +0,0 @@
-// Copyright (c) 2016, 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:kernel/kernel.dart';
-import 'package:kernel/type_environment.dart';
-import 'continuation.dart';
-
-/// A transformer that introduces temporary variables for all subexpressions
-/// that are alive across yield points (AwaitExpression).
-///
-/// The transformer is invoked by passing [rewrite] a top-level expression.
-///
-/// All intermediate values that are possible live across an await are named in
-/// local variables.
-///
-/// Await expressions are translated into a call to a helper function and a
-/// native yield.
-class ExpressionLifter extends Transformer {
-  final AsyncRewriterBase continuationRewriter;
-
-  /// Have we seen an await to the right in the expression tree.
-  ///
-  /// Subexpressions are visited right-to-left in the reverse of evaluation
-  /// order.
-  ///
-  /// On entry to an expression's visit method, [seenAwait] indicates whether a
-  /// sibling to the right contains an await.  If so the expression will be
-  /// named in a temporary variable because it is potentially live across an
-  /// await.
-  ///
-  /// On exit from an expression's visit method, [seenAwait] indicates whether
-  /// the expression itself or a sibling to the right contains an await.
-  bool seenAwait = false;
-
-  /// The (reverse order) sequence of statements that have been emitted.
-  ///
-  /// Transformation of an expression produces a transformed expression and a
-  /// sequence of statements which are assignments to local variables, calls to
-  /// helper functions, and yield points.  Only the yield points need to be a
-  /// statements, and they are statements so an implementation does not have to
-  /// handle unnamed expression intermediate live across yield points.
-  ///
-  /// The visit methods return the transformed expression and build a sequence
-  /// of statements by emitting statements into this list.  This list is built
-  /// in reverse because children are visited right-to-left.
-  ///
-  /// If an expression should be named it is named before visiting its children
-  /// so the naming assignment appears in the list before all statements
-  /// implementing the translation of the children.
-  ///
-  /// Children that are conditionally evaluated, such as some parts of logical
-  /// and conditional expressions, must be delimited so that they do not emit
-  /// unguarded statements into [statements].  This is implemented by setting
-  /// [statements] to a fresh empty list before transforming those children.
-  List<Statement> statements = <Statement>[];
-
-  /// The number of currently live named intermediate values.
-  ///
-  /// This index is used to allocate names to temporary values.  Because
-  /// children are visited right-to-left, names are assigned in reverse order of
-  /// index.
-  ///
-  /// When an assignment is emitted into [statements] to name an expression
-  /// before visiting its children, the index is not immediately reserved
-  /// because a child can freely use the same name as its parent.  In practice,
-  /// this will be the rightmost named child.
-  ///
-  /// After visiting the children of a named expression, [nameIndex] is set to
-  /// indicate one more live value (the value of the expression) than before
-  /// visiting the expression.
-  ///
-  /// After visiting the children of an expression that is not named,
-  /// [nameIndex] may still account for names of subexpressions.
-  int nameIndex = 0;
-
-  final VariableDeclaration asyncResult =
-      new VariableDeclaration(':result_or_exception');
-  final List<VariableDeclaration> variables = <VariableDeclaration>[];
-
-  ExpressionLifter(this.continuationRewriter);
-
-  StatefulStaticTypeContext get _staticTypeContext =>
-      continuationRewriter.staticTypeContext;
-
-  Block blockOf(List<Statement> statements) {
-    return new Block(statements.reversed.toList());
-  }
-
-  /// Rewrite a toplevel expression (toplevel wrt. a statement).
-  ///
-  /// Rewriting an expression produces a sequence of statements and an
-  /// expression.  The sequence of statements are added to the given list.  Pass
-  /// an empty list if the rewritten expression should be delimited from the
-  /// surrounding context.
-  Expression rewrite(Expression expression, List<Statement> outer) {
-    assert(statements.isEmpty);
-    var saved = seenAwait;
-    seenAwait = false;
-    Expression result = transform(expression);
-    outer.addAll(statements.reversed);
-    statements.clear();
-    seenAwait = seenAwait || saved;
-    return result;
-  }
-
-  // Perform an action with a given list of statements so that it cannot emit
-  // statements into the 'outer' list.
-  Expression delimit(Expression action(), List<Statement> inner) {
-    var outer = statements;
-    statements = inner;
-    Expression result = action();
-    statements = outer;
-    return result;
-  }
-
-  // Wraps VariableGet in an unsafeCast if `type` isn't dynamic.
-  Expression unsafeCastVariableGet(
-      VariableDeclaration variable, DartType type) {
-    if (type != const DynamicType()) {
-      return StaticInvocation(
-          continuationRewriter.helper.unsafeCast,
-          Arguments(<Expression>[VariableGet(variable)],
-              types: <DartType>[type]));
-    }
-    return VariableGet(variable);
-  }
-
-  // Name an expression by emitting an assignment to a temporary variable.
-  Expression name(Expression expr) {
-    DartType type = expr.getStaticType(_staticTypeContext);
-    VariableDeclaration temp = allocateTemporary(nameIndex, type);
-    statements.add(ExpressionStatement(VariableSet(temp, expr)));
-    // Wrap in unsafeCast to make sure we pass type information even if we later
-    // have to re-type the temporary variable to dynamic.
-    return unsafeCastVariableGet(temp, type);
-  }
-
-  VariableDeclaration allocateTemporary(int index,
-      [DartType type = const DynamicType()]) {
-    if (variables.length > index) {
-      // Re-type temporary to dynamic if we detect reuse with different type.
-      // Note: We should make sure all uses use `unsafeCast(...)` to pass their
-      // type information on, as that is lost otherwise.
-      if (variables[index].type != const DynamicType() &&
-          variables[index].type != type) {
-        variables[index].type = const DynamicType();
-      }
-      return variables[index];
-    }
-    for (var i = variables.length; i <= index; i++) {
-      variables.add(VariableDeclaration(":async_temporary_${i}", type: type));
-    }
-    return variables[index];
-  }
-
-  // Simple literals.  These are pure expressions so they can be evaluated after
-  // an await to their right.
-  @override
-  TreeNode visitSymbolLiteral(SymbolLiteral expr) => expr;
-  @override
-  TreeNode visitTypeLiteral(TypeLiteral expr) => expr;
-  @override
-  TreeNode visitThisExpression(ThisExpression expr) => expr;
-  @override
-  TreeNode visitStringLiteral(StringLiteral expr) => expr;
-  @override
-  TreeNode visitIntLiteral(IntLiteral expr) => expr;
-  @override
-  TreeNode visitDoubleLiteral(DoubleLiteral expr) => expr;
-  @override
-  TreeNode visitBoolLiteral(BoolLiteral expr) => expr;
-  @override
-  TreeNode visitNullLiteral(NullLiteral expr) => expr;
-
-  // Nullary expressions with effects.
-  Expression nullary(Expression expr) {
-    if (seenAwait) {
-      expr = name(expr);
-      ++nameIndex;
-    }
-    return expr;
-  }
-
-  @override
-  TreeNode visitSuperPropertyGet(SuperPropertyGet expr) => nullary(expr);
-  @override
-  TreeNode visitStaticGet(StaticGet expr) => nullary(expr);
-  @override
-  TreeNode visitStaticTearOff(StaticTearOff expr) => nullary(expr);
-  @override
-  TreeNode visitRethrow(Rethrow expr) => nullary(expr);
-
-  // Getting a final or const variable is not an effect so it can be evaluated
-  // after an await to its right.
-  @override
-  TreeNode visitVariableGet(VariableGet expr) {
-    Expression result = expr;
-    if (seenAwait && !expr.variable.isFinal && !expr.variable.isConst) {
-      result = name(expr);
-      ++nameIndex;
-    }
-    return result;
-  }
-
-  // Transform an expression given an action to transform the children.  For
-  // this purposes of the await transformer the children should generally be
-  // translated from right to left, in the reverse of evaluation order.
-  Expression transformTreeNode(Expression expr, void action()) {
-    var shouldName = seenAwait;
-
-    // 1. If there is an await in a sibling to the right, emit an assignment to
-    // a temporary variable before transforming the children.
-    var result = shouldName ? name(expr) : expr;
-
-    // 2. Remember the number of live temporaries before transforming the
-    // children.
-    var index = nameIndex;
-
-    // 3. Transform the children.  Initially they do not have an await in a
-    // sibling to their right.
-    seenAwait = false;
-    action();
-
-    // 4. If the expression was named then the variables used for children are
-    // no longer live but the variable used for the expression is.
-    // On the other hand, a sibling to the left (yet to be processed) cannot
-    // reuse any of the variables used here, as the assignments in the children
-    // (here) would overwrite assignments in the siblings to the left,
-    // possibly before the use of the overwritten values.
-    if (shouldName) {
-      if (index + 1 > nameIndex) nameIndex = index + 1;
-      seenAwait = true;
-    }
-    return result;
-  }
-
-  // Unary expressions.
-  Expression unary(Expression expr) {
-    return transformTreeNode(expr, () {
-      expr.transformChildren(this);
-    });
-  }
-
-  @override
-  TreeNode visitInvalidExpression(InvalidExpression expr) => unary(expr);
-  @override
-  TreeNode visitVariableSet(VariableSet expr) => unary(expr);
-  @override
-  TreeNode visitInstanceGet(InstanceGet expr) => unary(expr);
-  @override
-  TreeNode visitDynamicGet(DynamicGet expr) => unary(expr);
-  @override
-  TreeNode visitInstanceTearOff(InstanceTearOff expr) => unary(expr);
-  @override
-  TreeNode visitFunctionTearOff(FunctionTearOff expr) => unary(expr);
-  @override
-  TreeNode visitSuperPropertySet(SuperPropertySet expr) => unary(expr);
-  @override
-  TreeNode visitStaticSet(StaticSet expr) => unary(expr);
-  @override
-  TreeNode visitNot(Not expr) => unary(expr);
-  @override
-  TreeNode visitIsExpression(IsExpression expr) => unary(expr);
-  @override
-  TreeNode visitAsExpression(AsExpression expr) => unary(expr);
-  @override
-  TreeNode visitThrow(Throw expr) => unary(expr);
-
-  @override
-  TreeNode visitInstanceSet(InstanceSet expr) {
-    return transformTreeNode(expr, () {
-      expr.value = transform(expr.value)..parent = expr;
-      expr.receiver = transform(expr.receiver)..parent = expr;
-    });
-  }
-
-  @override
-  TreeNode visitDynamicSet(DynamicSet expr) {
-    return transformTreeNode(expr, () {
-      expr.value = transform(expr.value)..parent = expr;
-      expr.receiver = transform(expr.receiver)..parent = expr;
-    });
-  }
-
-  @override
-  TreeNode visitArguments(Arguments args) {
-    for (var named in args.named.reversed) {
-      named.value = transform(named.value)..parent = named;
-    }
-    var positional = args.positional;
-    for (var i = positional.length - 1; i >= 0; --i) {
-      positional[i] = transform(positional[i])..parent = args;
-    }
-    // Returns the arguments, which is assumed at the call sites because they do
-    // not replace the arguments or set parent pointers.
-    return args;
-  }
-
-  @override
-  TreeNode visitInstanceInvocation(InstanceInvocation expr) {
-    return transformTreeNode(expr, () {
-      visitArguments(expr.arguments);
-      expr.receiver = transform(expr.receiver)..parent = expr;
-    });
-  }
-
-  @override
-  TreeNode visitLocalFunctionInvocation(LocalFunctionInvocation expr) {
-    return transformTreeNode(expr, () {
-      visitArguments(expr.arguments);
-    });
-  }
-
-  @override
-  TreeNode visitDynamicInvocation(DynamicInvocation expr) {
-    return transformTreeNode(expr, () {
-      visitArguments(expr.arguments);
-      expr.receiver = transform(expr.receiver)..parent = expr;
-    });
-  }
-
-  @override
-  TreeNode visitFunctionInvocation(FunctionInvocation expr) {
-    return transformTreeNode(expr, () {
-      visitArguments(expr.arguments);
-      expr.receiver = transform(expr.receiver)..parent = expr;
-    });
-  }
-
-  @override
-  TreeNode visitEqualsNull(EqualsNull expr) => unary(expr);
-
-  @override
-  TreeNode visitEqualsCall(EqualsCall expr) {
-    return transformTreeNode(expr, () {
-      expr.right = transform(expr.right)..parent = expr;
-      expr.left = transform(expr.left)..parent = expr;
-    });
-  }
-
-  @override
-  TreeNode visitSuperMethodInvocation(SuperMethodInvocation expr) {
-    return transformTreeNode(expr, () {
-      visitArguments(expr.arguments);
-    });
-  }
-
-  @override
-  TreeNode visitStaticInvocation(StaticInvocation expr) {
-    return transformTreeNode(expr, () {
-      visitArguments(expr.arguments);
-    });
-  }
-
-  @override
-  TreeNode visitConstructorInvocation(ConstructorInvocation expr) {
-    return transformTreeNode(expr, () {
-      visitArguments(expr.arguments);
-    });
-  }
-
-  @override
-  TreeNode visitStringConcatenation(StringConcatenation expr) {
-    return transformTreeNode(expr, () {
-      var expressions = expr.expressions;
-      for (var i = expressions.length - 1; i >= 0; --i) {
-        expressions[i] = transform(expressions[i])..parent = expr;
-      }
-    });
-  }
-
-  @override
-  TreeNode visitListLiteral(ListLiteral expr) {
-    return transformTreeNode(expr, () {
-      var expressions = expr.expressions;
-      for (var i = expressions.length - 1; i >= 0; --i) {
-        expressions[i] = transform(expr.expressions[i])..parent = expr;
-      }
-    });
-  }
-
-  @override
-  TreeNode visitMapLiteral(MapLiteral expr) {
-    return transformTreeNode(expr, () {
-      for (var entry in expr.entries.reversed) {
-        entry.value = transform(entry.value)..parent = entry;
-        entry.key = transform(entry.key)..parent = entry;
-      }
-    });
-  }
-
-  // Control flow.
-  @override
-  TreeNode visitLogicalExpression(LogicalExpression expr) {
-    var shouldName = seenAwait;
-
-    // Right is delimited because it is conditionally evaluated.
-    var rightStatements = <Statement>[];
-    seenAwait = false;
-    expr.right = delimit(() => transform(expr.right), rightStatements)
-      ..parent = expr;
-    var rightAwait = seenAwait;
-
-    if (rightStatements.isEmpty) {
-      // Easy case: right did not emit any statements.
-      seenAwait = shouldName;
-      return transformTreeNode(expr, () {
-        expr.left = transform(expr.left)..parent = expr;
-        seenAwait = seenAwait || rightAwait;
-      });
-    }
-
-    // If right has emitted statements we will produce a temporary t and emit
-    // for && (there is an analogous case for ||):
-    //
-    // t = [left] == true;
-    // if (t) {
-    //   t = [right] == true;
-    // }
-
-    // Recall that statements are emitted in reverse order, so first emit the if
-    // statement, then the assignment of [left] == true, and then translate left
-    // so any statements it emits occur after in the accumulated list (that is,
-    // so they occur before in the corresponding block).
-    var rightBody = blockOf(rightStatements);
-    final type = _staticTypeContext.typeEnvironment.coreTypes
-        .boolRawType(_staticTypeContext.nonNullable);
-    final result = allocateTemporary(nameIndex, type);
-    final objectEquals = continuationRewriter.helper.coreTypes.objectEquals;
-    rightBody.addStatement(new ExpressionStatement(new VariableSet(
-        result,
-        new EqualsCall(expr.right, new BoolLiteral(true),
-            interfaceTarget: objectEquals,
-            functionType: objectEquals.getterType as FunctionType))));
-    Statement then;
-    Statement? otherwise;
-    if (expr.operatorEnum == LogicalExpressionOperator.AND) {
-      then = rightBody;
-      otherwise = null;
-    } else {
-      then = new EmptyStatement();
-      otherwise = rightBody;
-    }
-    statements.add(
-        new IfStatement(unsafeCastVariableGet(result, type), then, otherwise));
-
-    final test = new EqualsCall(expr.left, new BoolLiteral(true),
-        interfaceTarget: objectEquals,
-        functionType: objectEquals.getterType as FunctionType);
-    statements.add(new ExpressionStatement(new VariableSet(result, test)));
-
-    seenAwait = false;
-    test.left = transform(test.left)..parent = test;
-
-    ++nameIndex;
-    seenAwait = seenAwait || rightAwait;
-    return unsafeCastVariableGet(result, type);
-  }
-
-  @override
-  TreeNode visitConditionalExpression(ConditionalExpression expr) {
-    // Then and otherwise are delimited because they are conditionally
-    // evaluated.
-    var shouldName = seenAwait;
-
-    final savedNameIndex = nameIndex;
-
-    var thenStatements = <Statement>[];
-    seenAwait = false;
-    expr.then = delimit(() => transform(expr.then), thenStatements)
-      ..parent = expr;
-    var thenAwait = seenAwait;
-
-    final thenNameIndex = nameIndex;
-    nameIndex = savedNameIndex;
-
-    var otherwiseStatements = <Statement>[];
-    seenAwait = false;
-    expr.otherwise =
-        delimit(() => transform(expr.otherwise), otherwiseStatements)
-          ..parent = expr;
-    var otherwiseAwait = seenAwait;
-
-    // Only one side of this branch will get executed at a time, so just make
-    // sure we have enough temps for either, not both at the same time.
-    if (thenNameIndex > nameIndex) {
-      nameIndex = thenNameIndex;
-    }
-
-    if (thenStatements.isEmpty && otherwiseStatements.isEmpty) {
-      // Easy case: neither then nor otherwise emitted any statements.
-      seenAwait = shouldName;
-      return transformTreeNode(expr, () {
-        expr.condition = transform(expr.condition)..parent = expr;
-        seenAwait = seenAwait || thenAwait || otherwiseAwait;
-      });
-    }
-
-    // If `then` or `otherwise` has emitted statements we will produce a
-    // temporary t and emit:
-    //
-    // if ([condition]) {
-    //   t = [left];
-    // } else {
-    //   t = [right];
-    // }
-    final result = allocateTemporary(nameIndex, expr.staticType);
-    var thenBody = blockOf(thenStatements);
-    var otherwiseBody = blockOf(otherwiseStatements);
-    thenBody.addStatement(
-        new ExpressionStatement(new VariableSet(result, expr.then)));
-    otherwiseBody.addStatement(
-        new ExpressionStatement(new VariableSet(result, expr.otherwise)));
-    var branch = new IfStatement(expr.condition, thenBody, otherwiseBody);
-    statements.add(branch);
-
-    seenAwait = false;
-    branch.condition = transform(branch.condition)..parent = branch;
-
-    ++nameIndex;
-    seenAwait = seenAwait || thenAwait || otherwiseAwait;
-    return unsafeCastVariableGet(result, expr.staticType);
-  }
-
-  // Others.
-  @override
-  TreeNode visitAwaitExpression(AwaitExpression expr) {
-    final R = continuationRewriter;
-    var shouldName = seenAwait;
-    var type = expr.getStaticType(_staticTypeContext);
-    Expression result = unsafeCastVariableGet(asyncResult, type);
-
-    // The statements are in reverse order, so name the result first if
-    // necessary and then add the two other statements in reverse.
-    if (shouldName) result = name(result);
-    Arguments arguments = new Arguments(<Expression>[
-      expr.operand,
-      new VariableGet(R.thenContinuationVariable),
-      new VariableGet(R.catchErrorContinuationVariable),
-    ]);
-
-    // We are building
-    //
-    //     [yield] (let _ = _awaitHelper(...) in null)
-    //
-    // to ensure that :await_jump_var and :await_jump_ctx are updated
-    // before _awaitHelper is invoked (see BuildYieldStatement in
-    // StreamingFlowGraphBuilder for details of how [yield] is translated to
-    // IL). This guarantees that recursive invocation of the current function
-    // would continue from the correct "jump" position. Recursive invocations
-    // arise if future we are awaiting completes synchronously. Builtin Future
-    // implementation don't complete synchronously, but Flutter's
-    // SynchronousFuture do (see bug http://dartbug.com/32098 for more details).
-    statements.add(R.createContinuationPoint(new Let(
-        new VariableDeclaration(null,
-            initializer: new StaticInvocation(R.helper.awaitHelper, arguments)
-              ..fileOffset = expr.fileOffset),
-        new NullLiteral()))
-      ..fileOffset = expr.fileOffset);
-
-    seenAwait = false;
-    var index = nameIndex;
-    arguments.positional[0] = transform(expr.operand)..parent = arguments;
-
-    if (shouldName && index + 1 > nameIndex) nameIndex = index + 1;
-    seenAwait = true;
-    return result;
-  }
-
-  @override
-  TreeNode visitFunctionExpression(FunctionExpression expr) {
-    expr.transformChildren(this);
-    return expr;
-  }
-
-  @override
-  TreeNode visitLet(Let expr) {
-    var body = transform(expr.body);
-
-    VariableDeclaration variable = expr.variable;
-    if (seenAwait) {
-      // There is an await in the body of `let var x = initializer in body` or
-      // to its right.  We will produce the sequence of statements:
-      //
-      // <initializer's statements>
-      // var x = <initializer's value>
-      // <body's statements>
-      //
-      // and return the body's value.
-      //
-      // So x is in scope for all the body's statements and the body's value.
-      // This has the unpleasant consequence that all let-bound variables with
-      // await in the let's body will end up hoisted out of the expression and
-      // allocated to the context in the VM, even if they have no uses
-      // (`let _ = e0 in e1` can be used for sequencing of `e0` and `e1`).
-      statements.add(variable);
-      var index = nameIndex;
-      seenAwait = false;
-      variable.initializer = transform(variable.initializer!)
-        ..parent = variable;
-      // Temporaries used in the initializer or the body are not live but the
-      // temporary used for the body is.
-      if (index + 1 > nameIndex) nameIndex = index + 1;
-      seenAwait = true;
-      return body;
-    } else {
-      // The body in `let x = initializer in body` did not contain an await.  We
-      // can leave a let expression.
-      return transformTreeNode(expr, () {
-        // The body has already been translated.
-        expr.body = body..parent = expr;
-        variable.initializer = transform(variable.initializer!)
-          ..parent = variable;
-      });
-    }
-  }
-
-  @override
-  TreeNode visitFunctionNode(FunctionNode node) {
-    var nestedRewriter = new RecursiveContinuationRewriter(
-        continuationRewriter.helper,
-        _staticTypeContext,
-        continuationRewriter.desugarAsync);
-    return nestedRewriter.transform(node);
-  }
-
-  @override
-  TreeNode visitBlockExpression(BlockExpression expr) {
-    return transformTreeNode(expr, () {
-      expr.value = transform(expr.value)..parent = expr;
-      List<Statement> body = <Statement>[];
-      for (Statement stmt in expr.body.statements.reversed) {
-        Statement? translation = _rewriteStatement(stmt);
-        if (translation != null) body.add(translation);
-      }
-      expr.body = new Block(body.reversed.toList())..parent = expr;
-    });
-  }
-
-  Statement? _rewriteStatement(Statement stmt) {
-    // This method translates a statement nested in an expression (e.g., in a
-    // block expression).  It produces a translated statement, a list of
-    // statements which are side effects necessary for any await, and a flag
-    // indicating whether there was an await in the statement or to its right.
-    // The translated statement can be null in the case where there was already
-    // an await to the right.
-
-    // The translation is accumulating two lists of statements, an inner list
-    // which is a reversed list of effects needed for the current expression and
-    // an outer list which represents the block containing the current
-    // statement.  We need to preserve both of those from side effects.
-    List<Statement> savedInner = statements;
-    List<Statement> savedOuter = continuationRewriter.statements;
-    statements = <Statement>[];
-    continuationRewriter.statements = <Statement>[];
-    continuationRewriter.transform(stmt);
-
-    List<Statement> results = continuationRewriter.statements;
-    statements = savedInner;
-    continuationRewriter.statements = savedOuter;
-    if (!seenAwait && results.length == 1) return results.first;
-    statements.addAll(results.reversed);
-    return null;
-  }
-
-  @override
-  TreeNode defaultStatement(Statement stmt) {
-    throw new UnsupportedError(
-        "Use _rewriteStatement to transform statement: ${stmt}");
-  }
-}
diff --git a/pkg/vm/lib/transformations/continuation.dart b/pkg/vm/lib/transformations/continuation.dart
deleted file mode 100644
index d23954f..0000000
--- a/pkg/vm/lib/transformations/continuation.dart
+++ /dev/null
@@ -1,1667 +0,0 @@
-// Copyright (c) 2016, 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 'dart:math' as math;
-
-import 'package:kernel/ast.dart';
-import 'package:kernel/core_types.dart';
-import 'package:kernel/type_algebra.dart' show Substitution;
-import 'package:kernel/type_environment.dart';
-
-import 'async.dart';
-
-class ContinuationVariables {
-  static const awaitJumpVar = ':await_jump_var';
-  static const asyncFuture = ':async_future';
-  static const isSync = ":is_sync";
-  static const awaitContextVar = ':await_ctx_var';
-  static const asyncCompleter = ':async_completer';
-  static const asyncOp = ':async_op';
-  static const asyncOpThen = ':async_op_then';
-  static const asyncOpError = ':async_op_error';
-  static const controller = ':controller';
-  static const controllerStreamVar = ':controller_stream';
-  static const forIterator = ':for-iterator';
-  static const returnValue = ':return_value';
-  static const stream = ':stream';
-  static const syncForIterator = ':sync-for-iterator';
-  static const syncOpGen = ':sync_op_gen';
-  static const syncOp = ':sync_op';
-  // sync_op(..) parameter.
-  static const iteratorParam = ':iterator';
-  // (a)sync_op(..) parameters.
-  static const exceptionParam = ':exception';
-  static const stackTraceParam = ':stack_trace';
-
-  static const savedTryContextVarPrefix = ':saved_try_context_var';
-  static const exceptionVarPrefix = ':exception';
-  static const stackTraceVarPrefix = ':stack_trace';
-
-  static String savedTryContextVar(int depth) =>
-      '$savedTryContextVarPrefix$depth';
-  static String exceptionVar(int depth) => '$exceptionVarPrefix$depth';
-  static String stackTraceVar(int depth) => '$stackTraceVarPrefix$depth';
-}
-
-void transformLibraries(
-    TypeEnvironment typeEnvironment, List<Library> libraries,
-    {required bool productMode, required bool desugarAsync}) {
-  var helper =
-      new HelperNodes.fromCoreTypes(typeEnvironment.coreTypes, productMode);
-  var rewriter = new RecursiveContinuationRewriter(helper,
-      new StatefulStaticTypeContext.stacked(typeEnvironment), desugarAsync);
-  for (var library in libraries) {
-    rewriter.rewriteLibrary(library);
-  }
-}
-
-Component transformComponent(
-    TypeEnvironment typeEnvironment, Component component,
-    {required bool productMode, required bool desugarAsync}) {
-  var helper =
-      new HelperNodes.fromCoreTypes(typeEnvironment.coreTypes, productMode);
-  var rewriter = new RecursiveContinuationRewriter(helper,
-      new StatefulStaticTypeContext.stacked(typeEnvironment), desugarAsync);
-  return rewriter.rewriteComponent(component);
-}
-
-Procedure transformProcedure(
-    TypeEnvironment typeEnvironment, Procedure procedure,
-    {required bool productMode, required bool desugarAsync}) {
-  var helper =
-      new HelperNodes.fromCoreTypes(typeEnvironment.coreTypes, productMode);
-  var rewriter = new RecursiveContinuationRewriter(helper,
-      new StatefulStaticTypeContext.stacked(typeEnvironment), desugarAsync);
-  return rewriter.transform(procedure);
-}
-
-class RecursiveContinuationRewriter extends RemovingTransformer {
-  final HelperNodes helper;
-
-  final VariableDeclaration awaitJumpVariable = new VariableDeclaration(
-      ContinuationVariables.awaitJumpVar,
-      initializer: new IntLiteral(0));
-  final VariableDeclaration awaitContextVariable =
-      new VariableDeclaration(ContinuationVariables.awaitContextVar);
-
-  StatefulStaticTypeContext staticTypeContext;
-  final bool desugarAsync;
-  final bool desugarAwaitFor;
-
-  RecursiveContinuationRewriter(
-      this.helper, this.staticTypeContext, this.desugarAsync,
-      {this.desugarAwaitFor = false});
-
-  Component rewriteComponent(Component node) {
-    return transform(node);
-  }
-
-  Library rewriteLibrary(Library node) {
-    return transform(node);
-  }
-
-  @override
-  TreeNode visitField(Field node, TreeNode? removalSentinel) {
-    staticTypeContext.enterMember(node);
-    final result = super.visitField(node, removalSentinel);
-    staticTypeContext.leaveMember(node);
-    return result;
-  }
-
-  @override
-  TreeNode visitConstructor(Constructor node, TreeNode? removalSentinel) {
-    staticTypeContext.enterMember(node);
-    final result = super.visitConstructor(node, removalSentinel);
-    staticTypeContext.leaveMember(node);
-    return result;
-  }
-
-  @override
-  TreeNode visitProcedure(Procedure node, TreeNode? removalSentinel) {
-    staticTypeContext.enterMember(node);
-    final result =
-        node.isAbstract ? node : super.visitProcedure(node, removalSentinel);
-    staticTypeContext.leaveMember(node);
-    return result;
-  }
-
-  @override
-  TreeNode visitLibrary(Library node, TreeNode? removalSentinel) {
-    staticTypeContext.enterLibrary(node);
-    Library result = super.visitLibrary(node, removalSentinel) as Library;
-    staticTypeContext.leaveLibrary(node);
-    return result;
-  }
-
-  @override
-  TreeNode visitFunctionNode(FunctionNode node, TreeNode? removalSentinel) {
-    switch (node.asyncMarker) {
-      case AsyncMarker.Sync:
-      case AsyncMarker.SyncYielding:
-        node.transformOrRemoveChildren(new RecursiveContinuationRewriter(
-            helper, staticTypeContext, desugarAsync));
-        return node;
-      case AsyncMarker.SyncStar:
-        if (desugarAsync) {
-          return new SyncStarFunctionRewriter(
-                  helper, node, staticTypeContext, desugarAsync)
-              .rewrite();
-        } else {
-          node.transformOrRemoveChildren(new RecursiveContinuationRewriter(
-              helper, staticTypeContext, desugarAsync));
-          return node;
-        }
-      case AsyncMarker.Async:
-        if (desugarAsync) {
-          return new AsyncFunctionRewriter(
-                  helper, node, staticTypeContext, desugarAsync)
-              .rewrite();
-        } else {
-          node.transformOrRemoveChildren(new RecursiveContinuationRewriter(
-              helper, staticTypeContext, desugarAsync,
-              desugarAwaitFor: true));
-          return node;
-        }
-      case AsyncMarker.AsyncStar:
-        if (desugarAsync) {
-          return new AsyncStarFunctionRewriter(
-                  helper, node, staticTypeContext, desugarAsync)
-              .rewrite();
-        } else {
-          node.transformOrRemoveChildren(new RecursiveContinuationRewriter(
-              helper, staticTypeContext, desugarAsync,
-              desugarAwaitFor: true));
-          return node;
-        }
-    }
-  }
-
-  @override
-  TreeNode visitForInStatement(ForInStatement stmt, TreeNode? removalSentinel) {
-    if (stmt.isAsync) {
-      if (!desugarAwaitFor) {
-        return super.visitForInStatement(stmt, removalSentinel);
-      }
-      // Transform
-      //
-      //   await for (T variable in <stream-expression>) { ... }
-      //
-      // To (in product mode):
-      //
-      //   {
-      //     :stream = <stream-expression>;
-      //     _StreamIterator<T> :for-iterator = new _StreamIterator<T>(:stream);
-      //     try {
-      //       while (await :for-iterator.moveNext()) {
-      //         T <variable> = :for-iterator.current;
-      //         ...
-      //       }
-      //     } finally {
-      //       if (:for-iterator._subscription != null)
-      //           await :for-iterator.cancel();
-      //     }
-      //   }
-      //
-      // Or (in non-product mode):
-      //
-      //   {
-      //     :stream = <stream-expression>;
-      //     _StreamIterator<T> :for-iterator = new _StreamIterator<T>(:stream);
-      //     try {
-      //       while (let _ = _asyncStarMoveNextHelper(:stream) in
-      //           await :for-iterator.moveNext()) {
-      //         T <variable> = :for-iterator.current;
-      //         ...
-      //       }
-      //     } finally {
-      //       if (:for-iterator._subscription != null)
-      //           await :for-iterator.cancel();
-      //     }
-      //   }
-      var valueVariable = stmt.variable;
-
-      var streamVariable = new VariableDeclaration(ContinuationVariables.stream,
-          initializer: stmt.iterable,
-          type: stmt.iterable.getStaticType(staticTypeContext));
-
-      final streamIteratorType = new InterfaceType(helper.streamIteratorClass,
-          staticTypeContext.nullable, [valueVariable.type]);
-      var forIteratorVariable = VariableDeclaration(
-          ContinuationVariables.forIterator,
-          initializer: new ConstructorInvocation(
-              helper.streamIteratorConstructor,
-              new Arguments(<Expression>[new VariableGet(streamVariable)],
-                  types: [valueVariable.type])),
-          type: streamIteratorType);
-
-      // await :for-iterator.moveNext()
-      var condition = new AwaitExpression(new InstanceInvocation(
-          InstanceAccessKind.Instance,
-          VariableGet(forIteratorVariable),
-          helper.streamIteratorMoveNext.name,
-          new Arguments([]),
-          interfaceTarget: helper.streamIteratorMoveNext,
-          functionType:
-              helper.streamIteratorMoveNext.getterType as FunctionType))
-        ..fileOffset = stmt.fileOffset;
-
-      Expression whileCondition;
-      if (helper.productMode) {
-        whileCondition = condition;
-      } else {
-        // _asyncStarMoveNextHelper(:stream)
-        var asyncStarMoveNextCall = new StaticInvocation(
-            helper.asyncStarMoveNextHelper,
-            new Arguments([new VariableGet(streamVariable)]))
-          ..fileOffset = stmt.fileOffset;
-
-        // let _ = asyncStarMoveNextCall in (condition)
-        whileCondition = new Let(
-            new VariableDeclaration(null, initializer: asyncStarMoveNextCall),
-            condition);
-      }
-
-      // T <variable> = :for-iterator.current;
-      valueVariable.initializer = new InstanceGet(InstanceAccessKind.Instance,
-          VariableGet(forIteratorVariable), helper.streamIteratorCurrent.name,
-          interfaceTarget: helper.streamIteratorCurrent,
-          resultType: valueVariable.type)
-        ..fileOffset = stmt.bodyOffset;
-      valueVariable.initializer!.parent = valueVariable;
-
-      var whileBody = new Block(<Statement>[valueVariable, stmt.body]);
-      var tryBody = new WhileStatement(whileCondition, whileBody);
-
-      // if (:for-iterator._subscription != null) await :for-iterator.cancel();
-      final DartType subscriptionType =
-          Substitution.fromInterfaceType(streamIteratorType).substituteType(
-              helper.coreTypes.streamIteratorSubscription.getterType);
-      var tryFinalizer = new IfStatement(
-          new Not(new EqualsNull(new InstanceGet(
-              InstanceAccessKind.Instance,
-              VariableGet(forIteratorVariable),
-              helper.coreTypes.streamIteratorSubscription.name,
-              interfaceTarget: helper.coreTypes.streamIteratorSubscription,
-              resultType: subscriptionType))),
-          new ExpressionStatement(new AwaitExpression(new InstanceInvocation(
-              InstanceAccessKind.Instance,
-              VariableGet(forIteratorVariable),
-              helper.streamIteratorCancel.name,
-              new Arguments(<Expression>[]),
-              interfaceTarget: helper.streamIteratorCancel,
-              functionType:
-                  helper.streamIteratorCancel.getterType as FunctionType))),
-          null);
-
-      var tryFinally = new TryFinally(tryBody, tryFinalizer);
-
-      var block = new Block(
-          <Statement>[streamVariable, forIteratorVariable, tryFinally]);
-      return transform(block);
-    }
-
-    // Transform
-    //
-    //   for ({var/final} T <variable> in <iterable>) { ... }
-    //
-    // Into
-    //
-    //  {
-    //    final Iterator<T> :sync-for-iterator = <iterable>.iterator;
-    //    for (; :sync-for-iterator.moveNext() ;) {
-    //        {var/final} T variable = :sync-for-iterator.current;
-    //        ...
-    //      }
-    //    }
-    //  }
-    final CoreTypes coreTypes = staticTypeContext.typeEnvironment.coreTypes;
-
-    // The CFE might invoke this transformation despite the program having
-    // compile-time errors. So we will not transform this [stmt] if the
-    // `stmt.iterable` is an invalid expression or has an invalid type and
-    // instead eliminate the entire for-in and replace it with a invalid
-    // expression statement.
-    final iterable = stmt.iterable;
-    final iterableType = iterable.getStaticType(staticTypeContext);
-    if (iterableType is InvalidType) {
-      return ExpressionStatement(
-          InvalidExpression('Invalid iterable type in for-in'));
-    }
-
-    // The NNBD sdk declares that Iterable.get:iterator returns a non-nullable
-    // `Iterator<E>`.
-    assert(const [
-      Nullability.nonNullable,
-      Nullability.legacy
-    ].contains(coreTypes.iterableGetIterator.function.returnType.nullability));
-
-    final DartType elementType = stmt.getElementType(staticTypeContext);
-    final iteratorType = InterfaceType(
-        coreTypes.iteratorClass, staticTypeContext.nonNullable, [elementType]);
-
-    final syncForIterator = VariableDeclaration(
-        ContinuationVariables.syncForIterator,
-        initializer: InstanceGet(InstanceAccessKind.Instance, iterable,
-            coreTypes.iterableGetIterator.name,
-            interfaceTarget: coreTypes.iterableGetIterator,
-            resultType: iteratorType)
-          ..fileOffset = iterable.fileOffset,
-        type: iteratorType)
-      ..fileOffset = iterable.fileOffset;
-
-    final condition = InstanceInvocation(
-        InstanceAccessKind.Instance,
-        VariableGet(syncForIterator),
-        coreTypes.iteratorMoveNext.name,
-        Arguments([]),
-        interfaceTarget: coreTypes.iteratorMoveNext,
-        functionType: coreTypes.iteratorMoveNext.getterType as FunctionType)
-      ..fileOffset = iterable.fileOffset;
-
-    final variable = stmt.variable
-      ..initializer = (InstanceGet(InstanceAccessKind.Instance,
-          VariableGet(syncForIterator), coreTypes.iteratorGetCurrent.name,
-          interfaceTarget: coreTypes.iteratorGetCurrent,
-          resultType: elementType)
-        ..fileOffset = stmt.bodyOffset);
-
-    final Block body = Block([variable, stmt.body])
-      ..fileOffset = stmt.bodyOffset;
-
-    return transform(
-        Block([syncForIterator, ForStatement([], condition, [], body)]));
-  }
-}
-
-abstract class ContinuationRewriterBase extends RecursiveContinuationRewriter {
-  final FunctionNode enclosingFunction;
-
-  int currentTryDepth = 0; // Nesting depth for try-blocks.
-  int currentCatchDepth = 0; // Nesting depth for catch-blocks.
-  int capturedTryDepth = 0; // Deepest yield point within a try-block.
-  int capturedCatchDepth = 0; // Deepest yield point within a catch-block.
-
-  ContinuationRewriterBase(HelperNodes helper, this.enclosingFunction,
-      StatefulStaticTypeContext staticTypeContext, bool desugarAsync,
-      {bool desugarAwaitFor = false})
-      : super(helper, staticTypeContext, desugarAsync,
-            desugarAwaitFor: desugarAwaitFor);
-
-  /// Given a container [type], which is an instantiation of the given
-  /// [containerClass] extract its element type.
-  ///
-  /// This is used to extract element type from Future<T>, Iterable<T> and
-  /// Stream<T> instantiations.
-  ///
-  /// If instantiation is not valid (has more than 1 type argument) then
-  /// this function returns [InvalidType].
-  static DartType elementTypeFrom(Class containerClass, DartType type) {
-    if (type is InterfaceType) {
-      if (type.classNode == containerClass) {
-        if (type.typeArguments.isEmpty) {
-          return const DynamicType();
-        } else if (type.typeArguments.length == 1) {
-          return type.typeArguments[0];
-        } else {
-          return const InvalidType();
-        }
-      }
-    }
-    return const DynamicType();
-  }
-
-  static DartType elementTypeFromFutureOr(DartType type) {
-    if (type is FutureOrType) {
-      return type.typeArgument;
-    }
-    return const DynamicType();
-  }
-
-  DartType elementTypeFromReturnType(Class expected) =>
-      elementTypeFrom(expected, enclosingFunction.returnType);
-
-  DartType elementTypeFromAsyncReturnType() =>
-      elementTypeFromFutureOr(enclosingFunction.returnType);
-
-  Statement createContinuationPoint([Expression? value]) {
-    if (value == null) value = new NullLiteral();
-    capturedTryDepth = math.max(capturedTryDepth, currentTryDepth);
-    capturedCatchDepth = math.max(capturedCatchDepth, currentCatchDepth);
-    return new YieldStatement(value, isNative: true);
-  }
-
-  @override
-  TreeNode visitTryCatch(TryCatch node, TreeNode? removalSentinel) {
-    // ignore: unnecessary_null_comparison
-    if (node.body != null) {
-      ++currentTryDepth;
-      node.body = transform(node.body);
-      node.body.parent = node;
-      --currentTryDepth;
-    }
-
-    ++currentCatchDepth;
-    transformCatchList(node.catches, node);
-    --currentCatchDepth;
-    return node;
-  }
-
-  @override
-  TreeNode visitTryFinally(TryFinally node, TreeNode? removalSentinel) {
-    // ignore: unnecessary_null_comparison
-    if (node.body != null) {
-      ++currentTryDepth;
-      node.body = transform(node.body);
-      node.body.parent = node;
-      --currentTryDepth;
-    }
-    // ignore: unnecessary_null_comparison
-    if (node.finalizer != null) {
-      ++currentCatchDepth;
-      node.finalizer = transform(node.finalizer);
-      node.finalizer.parent = node;
-      --currentCatchDepth;
-    }
-    return node;
-  }
-
-  Iterable<VariableDeclaration> createCapturedTryVariables() =>
-      new Iterable.generate(
-          capturedTryDepth,
-          (depth) => new VariableDeclaration(
-              ContinuationVariables.savedTryContextVar(depth)));
-
-  Iterable<VariableDeclaration> createCapturedCatchVariables() =>
-      new Iterable.generate(capturedCatchDepth).expand((depth) => [
-            new VariableDeclaration(ContinuationVariables.exceptionVar(depth)),
-            new VariableDeclaration(ContinuationVariables.stackTraceVar(depth)),
-          ]);
-
-  List<VariableDeclaration> variableDeclarations() {
-    awaitJumpVariable.type = staticTypeContext.typeEnvironment.coreTypes
-        .intRawType(staticTypeContext.nonNullable);
-    return [awaitJumpVariable, awaitContextVariable]
-      ..addAll(createCapturedTryVariables())
-      ..addAll(createCapturedCatchVariables());
-  }
-}
-
-// Transformer that rewrites all variable references to a given function's
-// parameters.
-// This allows us to e.g. "shadow" the original parameter variables with copies
-// unique to given sub-closure to prevent shared variables being overwritten.
-class ShadowRewriter extends Transformer {
-  final FunctionNode enclosingFunction;
-  Map<VariableDeclaration, VariableDeclaration?> _shadowedParameters = {};
-
-  ShadowRewriter(this.enclosingFunction) {
-    for (final parameter in enclosingFunction.positionalParameters
-        .followedBy(enclosingFunction.namedParameters)) {
-      // Put in placeholders so we can allocate new variables lazily- i.e. only
-      // if they're later referenced.
-      _shadowedParameters[parameter] = null;
-    }
-  }
-
-  // Return all used parameters.
-  Iterable<VariableDeclaration> get shadowedParameters =>
-      _shadowedParameters.values.whereType<VariableDeclaration>();
-
-  VariableDeclaration _rewrite(VariableDeclaration variable) {
-    if (_shadowedParameters.containsKey(variable)) {
-      // Fill in placeholder.
-      VariableDeclaration? placeholder = _shadowedParameters[variable];
-      if (placeholder == null) {
-        placeholder = _shadowedParameters[variable] = VariableDeclaration(
-          variable.name,
-          type: variable.type,
-          initializer: VariableGet(variable),
-        );
-      }
-      variable = placeholder;
-    }
-    return variable;
-  }
-
-  @override
-  TreeNode visitVariableGet(VariableGet node) {
-    node = super.visitVariableGet(node) as VariableGet;
-    return node..variable = _rewrite(node.variable);
-  }
-
-  @override
-  TreeNode visitVariableSet(VariableSet node) {
-    node = super.visitVariableSet(node) as VariableSet;
-    return node..variable = _rewrite(node.variable);
-  }
-}
-
-class SyncStarFunctionRewriter extends ContinuationRewriterBase {
-  final VariableDeclaration iteratorParameter;
-
-  SyncStarFunctionRewriter(HelperNodes helper, FunctionNode enclosingFunction,
-      StatefulStaticTypeContext staticTypeContext, bool desugarAsync)
-      : iteratorParameter =
-            VariableDeclaration(ContinuationVariables.iteratorParam)
-              ..type = InterfaceType(
-                  helper.syncIteratorClass, staticTypeContext.nullable, [
-                // Note: This is dynamic since nested iterators (of potentially
-                // different type) are handled by shared internal synthetic
-                // code.
-                const DynamicType(),
-              ]),
-        super(helper, enclosingFunction, staticTypeContext, desugarAsync,
-            desugarAwaitFor: false);
-
-  FunctionNode rewrite() {
-    // We need to preserve the original parameters passed to the sync* function
-    // because each iteration should start from those parameters. To achieve
-    // this we shadow the original parameters with new variables (which are
-    // initialised to the original parameter values) and rewrite
-    // the body to use these variables instead.
-    final shadowRewriter = ShadowRewriter(enclosingFunction);
-    enclosingFunction.body = shadowRewriter.transform(enclosingFunction.body!)
-      ..parent = enclosingFunction;
-
-    // TODO(cskau): Figure out why inlining this below causes segfaults.
-    // Maybe related to http://dartbug.com/41596 ?
-    final syncOpFN = FunctionNode(buildClosureBody(),
-        positionalParameters: [
-          iteratorParameter,
-          new VariableDeclaration(ContinuationVariables.exceptionParam),
-          new VariableDeclaration(ContinuationVariables.stackTraceParam),
-        ],
-        requiredParameterCount: 3,
-        // Note: SyncYielding functions have no Dart equivalent. Since they are
-        // synchronous, we use Sync. (Note also that the Dart VM backend uses
-        // the Dart async marker to decide if functions are debuggable.)
-        asyncMarker: AsyncMarker.SyncYielding,
-        dartAsyncMarker: AsyncMarker.Sync,
-        returnType: helper.coreTypes.boolLegacyRawType)
-      ..fileOffset = enclosingFunction.fileOffset
-      ..fileEndOffset = enclosingFunction.fileEndOffset;
-    final syncOpType =
-        syncOpFN.computeThisFunctionType(staticTypeContext.nonNullable);
-
-    final syncOpGenVariable = VariableDeclaration(
-        ContinuationVariables.syncOpGen,
-        type: FunctionType([], syncOpType, staticTypeContext.nonNullable));
-
-    final syncOpVariable = VariableDeclaration(ContinuationVariables.syncOp);
-    final syncOpDecl = FunctionDeclaration(syncOpVariable, syncOpFN)
-      ..fileOffset = enclosingFunction.fileOffset;
-
-    enclosingFunction.body = Block([
-      // :sync_op_gen() {
-      //   :await_jump_var;
-      //   :await_ctx_var;
-      //   bool sync_op(:iterator, e, st) yielding {
-      //     modified <node.body> ...
-      //   };
-      //   return sync_op;
-      // }
-      FunctionDeclaration(
-          syncOpGenVariable,
-          FunctionNode(
-              Block([
-                // :await_jump_var, :await_ctx_var.
-                ...variableDeclarations(),
-                // Shadow any used function parameters with local copies.
-                ...shadowRewriter.shadowedParameters,
-                // :sync_op(..) { .. }
-                syncOpDecl,
-                // return sync_op;
-                ReturnStatement(VariableGet(syncOpVariable)),
-              ]),
-              returnType: syncOpType))
-        ..fileOffset = enclosingFunction.fileOffset,
-
-      // return _SyncIterable<T>(:sync_op_gen);
-      ReturnStatement(ConstructorInvocation(
-          helper.syncIterableConstructor,
-          Arguments([
-            VariableGet(syncOpGenVariable)
-          ], types: [
-            ContinuationRewriterBase.elementTypeFrom(
-                helper.iterableClass, enclosingFunction.returnType)
-          ]))),
-    ])
-      ..parent = enclosingFunction;
-    enclosingFunction.asyncMarker = AsyncMarker.Sync;
-
-    return enclosingFunction;
-  }
-
-  Statement buildClosureBody() {
-    // The body will insert calls to
-    //    :iterator.current_=
-    //    :iterator.isYieldEach=
-    // and return `true` as long as it did something and `false` when it's done.
-    return new Block(<Statement>[
-      transform(enclosingFunction.body!),
-      new ReturnStatement(new BoolLiteral(false))
-        ..fileOffset = enclosingFunction.fileEndOffset
-    ]);
-  }
-
-  @override
-  TreeNode visitYieldStatement(YieldStatement node, TreeNode? removalSentinel) {
-    Expression transformedExpression = transform(node.expression);
-
-    var statements = <Statement>[];
-    if (node.isYieldStar) {
-      statements.add(new ExpressionStatement(new InstanceSet(
-          InstanceAccessKind.Instance,
-          VariableGet(iteratorParameter),
-          helper.syncIteratorYieldEachIterable.name,
-          transformedExpression,
-          interfaceTarget: helper.syncIteratorYieldEachIterable)));
-    } else {
-      statements.add(new ExpressionStatement(new InstanceSet(
-          InstanceAccessKind.Instance,
-          VariableGet(iteratorParameter),
-          helper.syncIteratorCurrent.name,
-          transformedExpression,
-          interfaceTarget: helper.syncIteratorCurrent)));
-    }
-
-    statements.add(createContinuationPoint(new BoolLiteral(true))
-      ..fileOffset = node.fileOffset);
-    return new Block(statements);
-  }
-
-  @override
-  TreeNode visitReturnStatement(
-      ReturnStatement node, TreeNode? removalSentinel) {
-    // sync* functions cannot return a value.
-    assert(node.expression == null || node.expression is NullLiteral);
-    node.expression = new BoolLiteral(false)..parent = node;
-    return node;
-  }
-}
-
-abstract class AsyncRewriterBase extends ContinuationRewriterBase {
-  // :async_op has type (dynamic result_or_exception, StackTrace? s) -> dynamic
-  final VariableDeclaration nestedClosureVariable;
-
-  // :async_op_then has type (dynamic result) -> dynamic
-  final VariableDeclaration thenContinuationVariable;
-
-  // :async_op_error has type (Object e, StackTrace s) -> dynamic
-  final VariableDeclaration catchErrorContinuationVariable;
-
-  LabeledStatement? labeledBody;
-
-  ExpressionLifter? expressionRewriter;
-
-  AsyncRewriterBase(HelperNodes helper, FunctionNode enclosingFunction,
-      StatefulStaticTypeContext staticTypeContext, bool desugarAsync)
-      : nestedClosureVariable =
-            VariableDeclaration(ContinuationVariables.asyncOp,
-                type: FunctionType([
-                  const DynamicType(),
-                  helper.coreTypes
-                      .stackTraceRawType(staticTypeContext.nullable),
-                ], const DynamicType(), staticTypeContext.nonNullable)),
-        thenContinuationVariable = VariableDeclaration(
-            ContinuationVariables.asyncOpThen,
-            type: FunctionType(const [const DynamicType()], const DynamicType(),
-                staticTypeContext.nonNullable)),
-        catchErrorContinuationVariable =
-            VariableDeclaration(ContinuationVariables.asyncOpError,
-                type: FunctionType([
-                  helper.coreTypes.objectRawType(staticTypeContext.nonNullable),
-                  helper.coreTypes
-                      .stackTraceRawType(staticTypeContext.nonNullable),
-                ], const DynamicType(), staticTypeContext.nonNullable)),
-        super(helper, enclosingFunction, staticTypeContext, desugarAsync,
-            desugarAwaitFor: true) {}
-
-  void setupAsyncContinuations(List<Statement> statements) {
-    expressionRewriter = new ExpressionLifter(this);
-
-    // var :async_op_then;
-    statements.add(thenContinuationVariable);
-
-    // var :async_op_error;
-    statements.add(catchErrorContinuationVariable);
-
-    // :async_op(:result_or_exception, :stack_trace) {
-    //     modified <node.body>;
-    // }
-    final parameters = <VariableDeclaration>[
-      expressionRewriter!.asyncResult,
-      new VariableDeclaration(ContinuationVariables.stackTraceParam),
-    ];
-
-    // Note: SyncYielding functions have no Dart equivalent. Since they are
-    // synchronous, we use Sync. (Note also that the Dart VM backend uses the
-    // Dart async marker to decide if functions are debuggable.)
-    final function = new FunctionNode(buildWrappedBody(),
-        positionalParameters: parameters,
-        asyncMarker: AsyncMarker.SyncYielding,
-        dartAsyncMarker: AsyncMarker.Sync)
-      ..fileOffset = enclosingFunction.fileOffset
-      ..fileEndOffset = enclosingFunction.fileEndOffset;
-
-    // The await expression lifter might have created a number of
-    // [VariableDeclarations].
-    // TODO(kustermann): If we didn't need any variables we should not emit
-    // these.
-    statements.addAll(variableDeclarations());
-    statements.addAll(expressionRewriter!.variables);
-
-    // Now add the closure function itself.
-    final closureFunction =
-        new FunctionDeclaration(nestedClosureVariable, function)
-          ..fileOffset = enclosingFunction.parent!.fileOffset;
-    statements.add(closureFunction);
-
-    // :async_op_then = _asyncThenWrapperHelper(asyncBody);
-    final boundThenClosure = new StaticInvocation(helper.asyncThenWrapper,
-        new Arguments(<Expression>[new VariableGet(nestedClosureVariable)]));
-    final thenClosureVariableAssign = new ExpressionStatement(
-        new VariableSet(thenContinuationVariable, boundThenClosure));
-    statements.add(thenClosureVariableAssign);
-
-    // :async_op_error = _asyncErrorWrapperHelper(asyncBody);
-    final boundCatchErrorClosure = new StaticInvocation(
-        helper.asyncErrorWrapper,
-        new Arguments(<Expression>[new VariableGet(nestedClosureVariable)]));
-    final catchErrorClosureVariableAssign = new ExpressionStatement(
-        new VariableSet(
-            catchErrorContinuationVariable, boundCatchErrorClosure));
-    statements.add(catchErrorClosureVariableAssign);
-  }
-
-  Statement buildWrappedBody() {
-    ++currentTryDepth;
-    labeledBody = new LabeledStatement(null);
-    labeledBody!.body = visitDelimited(enclosingFunction.body!)
-      ..parent = labeledBody;
-    --currentTryDepth;
-
-    var exceptionVariable = VariableDeclaration('exception');
-    var stackTraceVariable = VariableDeclaration('stack_trace',
-        type:
-            helper.coreTypes.stackTraceRawType(staticTypeContext.nonNullable));
-
-    return new TryCatch(
-      buildReturn(labeledBody!),
-      <Catch>[
-        new Catch(
-            exceptionVariable,
-            new Block(<Statement>[
-              buildCatchBody(exceptionVariable, stackTraceVariable)
-            ]),
-            stackTrace: stackTraceVariable)
-      ],
-      isSynthetic: true,
-    );
-  }
-
-  Statement buildCatchBody(VariableDeclaration exceptionVariable,
-      VariableDeclaration stackTraceVariable);
-
-  Statement buildReturn(Statement body);
-
-  List<Statement> statements = <Statement>[];
-
-  @override
-  TreeNode visitExpressionStatement(
-      ExpressionStatement stmt, TreeNode? removalSentinel) {
-    stmt.expression = expressionRewriter!.rewrite(stmt.expression, statements)
-      ..parent = stmt;
-    statements.add(stmt);
-    return removalSentinel ?? EmptyStatement();
-  }
-
-  @override
-  TreeNode visitBlock(Block stmt, TreeNode? removalSentinel) {
-    var saved = statements;
-    statements = <Statement>[];
-    for (var statement in stmt.statements) {
-      transform(statement);
-    }
-    saved.add(new Block(statements));
-    statements = saved;
-    return removalSentinel ?? EmptyStatement();
-  }
-
-  @override
-  TreeNode visitEmptyStatement(EmptyStatement stmt, TreeNode? removalSentinel) {
-    statements.add(stmt);
-    return removalSentinel ?? EmptyStatement();
-  }
-
-  @override
-  TreeNode visitAssertBlock(AssertBlock stmt, TreeNode? removalSentinel) {
-    var saved = statements;
-    statements = <Statement>[];
-    for (var statement in stmt.statements) {
-      transform(statement);
-    }
-    saved.add(new Block(statements));
-    statements = saved;
-    return removalSentinel ?? EmptyStatement();
-  }
-
-  @override
-  TreeNode visitAssertStatement(
-      AssertStatement stmt, TreeNode? removalSentinel) {
-    var condEffects = <Statement>[];
-    var cond = expressionRewriter!.rewrite(stmt.condition, condEffects);
-    if (stmt.message == null) {
-      stmt.condition = cond..parent = stmt;
-      // If the translation of the condition produced a non-empty list of
-      // statements, ensure they are guarded by whether asserts are enabled.
-      statements.add(
-          condEffects.isEmpty ? stmt : new AssertBlock(condEffects..add(stmt)));
-      return removalSentinel ?? EmptyStatement();
-    }
-
-    // The translation depends on the translation of the message, by cases.
-    Statement result;
-    var msgEffects = <Statement>[];
-    stmt.message = expressionRewriter!.rewrite(stmt.message!, msgEffects)
-      ..parent = stmt;
-    if (condEffects.isEmpty) {
-      if (msgEffects.isEmpty) {
-        // The condition rewrote to ([], C) and the message rewrote to ([], M).
-        // The result is
-        //
-        // assert(C, M)
-        stmt.condition = cond..parent = stmt;
-        result = stmt;
-      } else {
-        // The condition rewrote to ([], C) and the message rewrote to (S*, M)
-        // where S* is non-empty.  The result is
-        //
-        // assert { if (C) {} else { S*; assert(false, M); }}
-        stmt.condition = new BoolLiteral(false)..parent = stmt;
-        result = new AssertBlock([
-          new IfStatement(
-              cond, new EmptyStatement(), new Block(msgEffects..add(stmt)))
-        ]);
-      }
-    } else {
-      if (msgEffects.isEmpty) {
-        // The condition rewrote to (S*, C) where S* is non-empty and the
-        // message rewrote to ([], M).  The result is
-        //
-        // assert { S*; assert(C, M); }
-        stmt.condition = cond..parent = stmt;
-        condEffects.add(stmt);
-      } else {
-        // The condition rewrote to (S0*, C) and the message rewrote to (S1*, M)
-        // where both S0* and S1* are non-empty.  The result is
-        //
-        // assert { S0*; if (C) {} else { S1*; assert(false, M); }}
-        stmt.condition = new BoolLiteral(false)..parent = stmt;
-        condEffects.add(new IfStatement(
-            cond, new EmptyStatement(), new Block(msgEffects..add(stmt))));
-      }
-      result = new AssertBlock(condEffects);
-    }
-    statements.add(result);
-    return removalSentinel ?? EmptyStatement();
-  }
-
-  Statement visitDelimited(Statement stmt) {
-    var saved = statements;
-    statements = <Statement>[];
-    transform(stmt);
-    Statement result =
-        statements.length == 1 ? statements.first : new Block(statements);
-    statements = saved;
-    return result;
-  }
-
-  @override
-  TreeNode visitLabeledStatement(
-      LabeledStatement stmt, TreeNode? removalSentinel) {
-    stmt.body = visitDelimited(stmt.body)..parent = stmt;
-    statements.add(stmt);
-    return removalSentinel ?? EmptyStatement();
-  }
-
-  @override
-  TreeNode visitBreakStatement(BreakStatement stmt, TreeNode? removalSentinel) {
-    statements.add(stmt);
-    return removalSentinel ?? EmptyStatement();
-  }
-
-  @override
-  TreeNode visitWhileStatement(WhileStatement stmt, TreeNode? removalSentinel) {
-    Statement body = visitDelimited(stmt.body);
-    List<Statement> effects = <Statement>[];
-    Expression cond = expressionRewriter!.rewrite(stmt.condition, effects);
-    if (effects.isEmpty) {
-      stmt.condition = cond..parent = stmt;
-      stmt.body = body..parent = stmt;
-      statements.add(stmt);
-    } else {
-      // The condition rewrote to a non-empty sequence of statements S* and
-      // value V.  Rewrite the loop to:
-      //
-      // L: while (true) {
-      //   S*
-      //   if (V) {
-      //     [body]
-      //   else {
-      //     break L;
-      //   }
-      // }
-      LabeledStatement labeled = new LabeledStatement(stmt);
-      stmt.condition = new BoolLiteral(true)..parent = stmt;
-      effects.add(new IfStatement(cond, body, new BreakStatement(labeled)));
-      stmt.body = new Block(effects)..parent = stmt;
-      statements.add(labeled);
-    }
-    return removalSentinel ?? EmptyStatement();
-  }
-
-  @override
-  TreeNode visitDoStatement(DoStatement stmt, TreeNode? removalSentinel) {
-    Statement body = visitDelimited(stmt.body);
-    List<Statement> effects = <Statement>[];
-    stmt.condition = expressionRewriter!.rewrite(stmt.condition, effects)
-      ..parent = stmt;
-    if (effects.isNotEmpty) {
-      // The condition rewrote to a non-empty sequence of statements S* and
-      // value V.  Add the statements to the end of the loop body.
-      Block block = body is Block ? body : body = new Block(<Statement>[body]);
-      for (var effect in effects) {
-        block.statements.add(effect);
-        effect.parent = body;
-      }
-    }
-    stmt.body = body..parent = stmt;
-    statements.add(stmt);
-    return removalSentinel ?? EmptyStatement();
-  }
-
-  @override
-  TreeNode visitForStatement(ForStatement stmt, TreeNode? removalSentinel) {
-    // Because of for-loop scoping and variable capture, it is tricky to deal
-    // with await in the loop's variable initializers or update expressions.
-    bool isSimple = true;
-    int length = stmt.variables.length;
-    List<List<Statement>> initEffects =
-        new List<List<Statement>>.generate(length, (int i) {
-      VariableDeclaration decl = stmt.variables[i];
-      List<Statement> statements = <Statement>[];
-      if (decl.initializer != null) {
-        decl.initializer = expressionRewriter!
-            .rewrite(decl.initializer!, statements)
-          ..parent = decl;
-      }
-      isSimple = isSimple && statements.isEmpty;
-      return statements;
-    });
-
-    length = stmt.updates.length;
-    List<List<Statement>> updateEffects =
-        new List<List<Statement>>.generate(length, (int i) {
-      List<Statement> statements = <Statement>[];
-      stmt.updates[i] = expressionRewriter!.rewrite(stmt.updates[i], statements)
-        ..parent = stmt;
-      isSimple = isSimple && statements.isEmpty;
-      return statements;
-    });
-
-    Statement body = visitDelimited(stmt.body);
-    Expression? cond = stmt.condition;
-    List<Statement>? condEffects;
-    if (cond != null) {
-      condEffects = <Statement>[];
-      cond = expressionRewriter!.rewrite(stmt.condition!, condEffects);
-    }
-
-    if (isSimple) {
-      // If the condition contains await, we use a translation like the one for
-      // while loops, but leaving the variable declarations and the update
-      // expressions in place.
-      if (condEffects == null || condEffects.isEmpty) {
-        if (cond != null) stmt.condition = cond..parent = stmt;
-        stmt.body = body..parent = stmt;
-        statements.add(stmt);
-      } else {
-        LabeledStatement labeled = new LabeledStatement(stmt);
-        // No condition in a for loop is the same as true.
-        stmt.condition = null;
-        condEffects
-            .add(new IfStatement(cond!, body, new BreakStatement(labeled)));
-        stmt.body = new Block(condEffects)..parent = stmt;
-        statements.add(labeled);
-      }
-      return removalSentinel ?? EmptyStatement();
-    }
-
-    // If the rewrite of the initializer or update expressions produces a
-    // non-empty sequence of statements then the loop is desugared.  If the loop
-    // has the form:
-    //
-    // label: for (Type x = init; cond; update) body
-    //
-    // it is translated as if it were:
-    //
-    // {
-    //   bool first = true;
-    //   Type temp;
-    //   label: while (true) {
-    //     Type x;
-    //     if (first) {
-    //       first = false;
-    //       x = init;
-    //     } else {
-    //       x = temp;
-    //       update;
-    //     }
-    //     if (cond) {
-    //       body;
-    //       temp = x;
-    //     } else {
-    //       break;
-    //     }
-    //   }
-    // }
-
-    // Place the loop variable declarations at the beginning of the body
-    // statements and move their initializers to a guarded list of statements.
-    // Add assignments to the loop variables from the previous iterations temp
-    // variables before the updates.
-    //
-    // temps.first is the flag 'first'.
-    // TODO(kmillikin) bool type for first.
-    List<VariableDeclaration> temps = <VariableDeclaration>[
-      new VariableDeclaration.forValue(new BoolLiteral(true), isFinal: false)
-    ];
-    List<Statement> loopBody = <Statement>[];
-    List<Statement> initializers = <Statement>[
-      new ExpressionStatement(
-          new VariableSet(temps.first, new BoolLiteral(false)))
-    ];
-    List<Statement> updates = <Statement>[];
-    List<Statement> newBody = <Statement>[body];
-    for (int i = 0; i < stmt.variables.length; ++i) {
-      VariableDeclaration decl = stmt.variables[i];
-      temps.add(new VariableDeclaration(null, type: decl.type));
-      loopBody.add(decl);
-      if (decl.initializer != null) {
-        initializers.addAll(initEffects[i]);
-        initializers.add(
-            new ExpressionStatement(new VariableSet(decl, decl.initializer!)));
-        decl.initializer = null;
-      }
-      updates.add(new ExpressionStatement(
-          new VariableSet(decl, new VariableGet(temps.last))));
-      newBody.add(new ExpressionStatement(
-          new VariableSet(temps.last, new VariableGet(decl))));
-    }
-    // Add the updates to their guarded list of statements.
-    for (int i = 0; i < stmt.updates.length; ++i) {
-      updates.addAll(updateEffects[i]);
-      updates.add(new ExpressionStatement(stmt.updates[i]));
-    }
-    // Initializers or updates could be empty.
-    loopBody.add(new IfStatement(new VariableGet(temps.first),
-        new Block(initializers), new Block(updates)));
-
-    LabeledStatement labeled = new LabeledStatement(null);
-    if (cond != null) {
-      loopBody.addAll(condEffects!);
-    } else {
-      cond = new BoolLiteral(true);
-    }
-    loopBody.add(
-        new IfStatement(cond, new Block(newBody), new BreakStatement(labeled)));
-    labeled.body =
-        new WhileStatement(new BoolLiteral(true), new Block(loopBody))
-          ..parent = labeled;
-    statements.add(new Block(<Statement>[]
-      ..addAll(temps)
-      ..add(labeled)));
-    return removalSentinel ?? EmptyStatement();
-  }
-
-  @override
-  TreeNode visitSwitchStatement(
-      SwitchStatement stmt, TreeNode? removalSentinel) {
-    stmt.expression = expressionRewriter!.rewrite(stmt.expression, statements)
-      ..parent = stmt;
-    for (var switchCase in stmt.cases) {
-      // Expressions in switch cases cannot contain await so they do not need to
-      // be translated.
-      switchCase.body = visitDelimited(switchCase.body)..parent = switchCase;
-    }
-    statements.add(stmt);
-    return removalSentinel ?? EmptyStatement();
-  }
-
-  @override
-  TreeNode visitContinueSwitchStatement(
-      ContinueSwitchStatement stmt, TreeNode? removalSentinel) {
-    statements.add(stmt);
-    return removalSentinel ?? EmptyStatement();
-  }
-
-  @override
-  TreeNode visitIfStatement(IfStatement stmt, TreeNode? removalSentinel) {
-    stmt.condition = expressionRewriter!.rewrite(stmt.condition, statements)
-      ..parent = stmt;
-    stmt.then = visitDelimited(stmt.then)..parent = stmt;
-    if (stmt.otherwise != null) {
-      stmt.otherwise = visitDelimited(stmt.otherwise!)..parent = stmt;
-    }
-    statements.add(stmt);
-    return removalSentinel ?? EmptyStatement();
-  }
-
-  @override
-  TreeNode visitTryCatch(TryCatch stmt, TreeNode? removalSentinel) {
-    ++currentTryDepth;
-    stmt.body = visitDelimited(stmt.body)..parent = stmt;
-    --currentTryDepth;
-
-    ++currentCatchDepth;
-    for (var clause in stmt.catches) {
-      clause.body = visitDelimited(clause.body)..parent = clause;
-    }
-    --currentCatchDepth;
-    statements.add(stmt);
-    return removalSentinel ?? EmptyStatement();
-  }
-
-  @override
-  TreeNode visitTryFinally(TryFinally stmt, TreeNode? removalSentinel) {
-    ++currentTryDepth;
-    stmt.body = visitDelimited(stmt.body)..parent = stmt;
-    --currentTryDepth;
-    ++currentCatchDepth;
-    stmt.finalizer = visitDelimited(stmt.finalizer)..parent = stmt;
-    --currentCatchDepth;
-    statements.add(stmt);
-    return removalSentinel ?? EmptyStatement();
-  }
-
-  @override
-  TreeNode visitYieldStatement(YieldStatement stmt, TreeNode? removalSentinel) {
-    stmt.expression = expressionRewriter!.rewrite(stmt.expression, statements)
-      ..parent = stmt;
-    statements.add(stmt);
-    return removalSentinel ?? EmptyStatement();
-  }
-
-  @override
-  TreeNode visitVariableDeclaration(
-      VariableDeclaration stmt, TreeNode? removalSentinel) {
-    if (stmt.initializer != null) {
-      stmt.initializer = expressionRewriter!
-          .rewrite(stmt.initializer!, statements)
-        ..parent = stmt;
-    }
-    statements.add(stmt);
-    return removalSentinel ?? EmptyStatement();
-  }
-
-  @override
-  TreeNode visitFunctionDeclaration(
-      FunctionDeclaration stmt, TreeNode? removalSentinel) {
-    stmt.function = transform(stmt.function)..parent = stmt;
-    statements.add(stmt);
-    return removalSentinel ?? EmptyStatement();
-  }
-
-  @override
-  TreeNode defaultExpression(TreeNode node, TreeNode? removalSentinel) =>
-      throw 'unreachable $node';
-}
-
-class AsyncStarFunctionRewriter extends AsyncRewriterBase {
-  VariableDeclaration? controllerVariable;
-
-  AsyncStarFunctionRewriter(HelperNodes helper, FunctionNode enclosingFunction,
-      StatefulStaticTypeContext staticTypeContext, bool desugarAsync)
-      : super(helper, enclosingFunction, staticTypeContext, desugarAsync);
-
-  FunctionNode rewrite() {
-    var statements = <Statement>[];
-
-    final elementType = elementTypeFromReturnType(helper.streamClass);
-
-    // _AsyncStarStreamController<T> :controller;
-    controllerVariable = new VariableDeclaration(
-        ContinuationVariables.controller,
-        type: new InterfaceType(helper.asyncStarStreamControllerClass,
-            staticTypeContext.nullable, [elementType]));
-    statements.add(controllerVariable!);
-
-    // dynamic :controller_stream;
-    VariableDeclaration controllerStreamVariable =
-        new VariableDeclaration(ContinuationVariables.controllerStreamVar);
-    statements.add(controllerStreamVariable);
-
-    setupAsyncContinuations(statements);
-
-    // :controller = new _AsyncStarStreamController<T>(:async_op);
-    var arguments = new Arguments(
-        <Expression>[new VariableGet(nestedClosureVariable)],
-        types: [elementType]);
-    var buildController = new ConstructorInvocation(
-        helper.asyncStarStreamControllerConstructor, arguments)
-      ..fileOffset = enclosingFunction.fileOffset;
-    var setController = new ExpressionStatement(
-        new VariableSet(controllerVariable!, buildController));
-    statements.add(setController);
-
-    // :controller_stream = :controller.stream;
-    var completerGet = new VariableGet(controllerVariable!);
-    statements.add(new ExpressionStatement(new VariableSet(
-        controllerStreamVariable,
-        new InstanceGet(InstanceAccessKind.Instance, completerGet,
-            helper.asyncStarStreamControllerStream.name,
-            interfaceTarget: helper.asyncStarStreamControllerStream,
-            resultType: Substitution.fromInterfaceType(
-                    controllerVariable!.type as InterfaceType)
-                .substituteType(
-                    helper.asyncStarStreamControllerStream.getterType)))));
-
-    // return :controller_stream;
-    var returnStatement =
-        new ReturnStatement(new VariableGet(controllerStreamVariable));
-    statements.add(returnStatement);
-
-    enclosingFunction.body = new Block(statements)..parent = enclosingFunction;
-    enclosingFunction.asyncMarker = AsyncMarker.Sync;
-    return enclosingFunction;
-  }
-
-  @override
-  Statement buildWrappedBody() {
-    ++currentTryDepth;
-    Statement body = super.buildWrappedBody();
-    --currentTryDepth;
-
-    var finallyBody = new ExpressionStatement(new InstanceInvocation(
-        InstanceAccessKind.Instance,
-        new VariableGet(controllerVariable!),
-        helper.asyncStarStreamControllerClose.name,
-        new Arguments([]),
-        interfaceTarget: helper.asyncStarStreamControllerClose,
-        functionType:
-            helper.asyncStarStreamControllerClose.getterType as FunctionType));
-
-    var tryFinally = new TryFinally(body, new Block(<Statement>[finallyBody]));
-    return tryFinally;
-  }
-
-  @override
-  Statement buildCatchBody(VariableDeclaration exceptionVariable,
-      VariableDeclaration stackTraceVariable) {
-    return new ExpressionStatement(new InstanceInvocation(
-        InstanceAccessKind.Instance,
-        new VariableGet(controllerVariable!),
-        helper.asyncStarStreamControllerAddError.name,
-        new Arguments(<Expression>[
-          new VariableGet(exceptionVariable),
-          new VariableGet(stackTraceVariable)
-        ]),
-        interfaceTarget: helper.asyncStarStreamControllerAddError,
-        functionType: helper.asyncStarStreamControllerAddError.getterType
-            as FunctionType));
-  }
-
-  @override
-  Statement buildReturn(Statement body) {
-    // Async* functions cannot return a value.  The returns from the function
-    // have been translated into breaks from the labeled body.
-    return new Block(<Statement>[
-      body,
-      new ReturnStatement()..fileOffset = enclosingFunction.fileEndOffset,
-    ]);
-  }
-
-  @override
-  TreeNode visitYieldStatement(YieldStatement stmt, TreeNode? removalSentinel) {
-    Expression expr = expressionRewriter!.rewrite(stmt.expression, statements);
-
-    final Procedure addMethod = stmt.isYieldStar
-        ? helper.asyncStarStreamControllerAddStream
-        : helper.asyncStarStreamControllerAdd;
-    final FunctionType addMethodFunctionType = Substitution.fromInterfaceType(
-            controllerVariable!.type as InterfaceType)
-        .substituteType(addMethod.getterType) as FunctionType;
-    var addExpression = new InstanceInvocation(
-        InstanceAccessKind.Instance,
-        new VariableGet(controllerVariable!),
-        addMethod.name,
-        new Arguments(<Expression>[expr]),
-        interfaceTarget: addMethod,
-        functionType: addMethodFunctionType)
-      ..fileOffset = stmt.fileOffset;
-
-    if (stmt.isYieldStar) {
-      statements.add(ExpressionStatement(addExpression));
-      statements.add(createContinuationPoint()..fileOffset = stmt.fileOffset);
-      final wasCancelled = StaticInvocation(
-          helper.unsafeCast,
-          Arguments(<Expression>[VariableGet(expressionRewriter!.asyncResult)],
-              types: <DartType>[helper.coreTypes.boolNonNullableRawType]));
-      statements
-          .add(IfStatement(wasCancelled, ReturnStatement(NullLiteral()), null));
-    } else {
-      statements.add(new IfStatement(
-          addExpression,
-          new ReturnStatement(new NullLiteral()),
-          createContinuationPoint()..fileOffset = stmt.fileOffset));
-    }
-    return removalSentinel ?? EmptyStatement();
-  }
-
-  @override
-  TreeNode visitReturnStatement(
-      ReturnStatement node, TreeNode? removalSentinel) {
-    // Async* functions cannot return a value.
-    assert(node.expression == null || node.expression is NullLiteral);
-    statements
-        .add(new BreakStatement(labeledBody!)..fileOffset = node.fileOffset);
-    return removalSentinel ?? EmptyStatement();
-  }
-}
-
-class AsyncFunctionRewriter extends AsyncRewriterBase {
-  VariableDeclaration? returnVariable;
-  VariableDeclaration? asyncFutureVariable;
-  VariableDeclaration? isSyncVariable;
-
-  // In general an async functions such as
-  //
-  //     Future<X> foo() async { return <expr>; }
-  //
-  // can return as `<expr>` either X or Future<X>, i.e. it can return
-  // FutureOr<X>
-  //
-  // If we know it doesn't return any object of type `Future`, we can optimize
-  // the future completion process by avoiding some expensive `is Future<T>`
-  // type checks on the returned value.
-  late bool canReturnFuture;
-
-  AsyncFunctionRewriter(HelperNodes helper, FunctionNode enclosingFunction,
-      StatefulStaticTypeContext staticTypeContext, bool desugarAsync)
-      : super(helper, enclosingFunction, staticTypeContext, desugarAsync);
-
-  FunctionNode rewrite() {
-    var statements = <Statement>[];
-
-    // The original function return type should be Future<T> or FutureOr<T>
-    // because the function is async. If it was, we make a Completer<T>,
-    // otherwise we make a malformed type.  In a "Future<T> foo() async {}"
-    // function the body can either return a "T" or a "Future<T>" => a
-    // "FutureOr<T>".
-    DartType valueType = elementTypeFromReturnType(helper.futureClass);
-    if (valueType == const DynamicType()) {
-      valueType = elementTypeFromAsyncReturnType();
-    }
-    final DartType returnType =
-        FutureOrType(valueType, staticTypeContext.nullable);
-    final futureTypeArguments = <DartType>[valueType];
-
-    final futureType = InterfaceType(helper.futureImplClass,
-        staticTypeContext.nonNullable, futureTypeArguments);
-
-    // final _Future<T> :async_future = _Future<T>();
-    asyncFutureVariable = VariableDeclaration(ContinuationVariables.asyncFuture,
-        initializer: ConstructorInvocation(helper.futureImplConstructor,
-            Arguments([], types: futureTypeArguments))
-          ..fileOffset = enclosingFunction.body?.fileOffset ?? -1,
-        isFinal: true,
-        type: futureType);
-    statements.add(asyncFutureVariable!);
-
-    // bool :is_sync = false;
-    isSyncVariable = VariableDeclaration(ContinuationVariables.isSync,
-        initializer: BoolLiteral(false),
-        type: helper.coreTypes.boolLegacyRawType);
-    statements.add(isSyncVariable!);
-
-    // asy::FutureOr<T>* :return_value;
-    returnVariable = VariableDeclaration(ContinuationVariables.returnValue,
-        type: returnType);
-    statements.add(returnVariable!);
-
-    canReturnFuture = false;
-
-    setupAsyncContinuations(statements);
-
-    // If we could prove the function doesn't return a `Future` we change the
-    // type of `:return_value`.
-    if (!canReturnFuture) {
-      returnVariable!.type =
-          valueType.withDeclaredNullability(Nullability.nullable);
-    }
-
-    // :async_op(null, null);
-    final startStatement = ExpressionStatement(LocalFunctionInvocation(
-        nestedClosureVariable, Arguments([NullLiteral(), NullLiteral()]),
-        functionType: FunctionType(
-            [], const DynamicType(), staticTypeContext.nonNullable))
-      ..fileOffset = enclosingFunction.fileOffset);
-    statements.add(startStatement);
-
-    // :is_sync = true;
-    final setIsSync =
-        ExpressionStatement(VariableSet(isSyncVariable!, BoolLiteral(true)));
-    statements.add(setIsSync);
-
-    // return :async_future;
-    statements.add(ReturnStatement(VariableGet(asyncFutureVariable!)));
-
-    enclosingFunction.body = Block(statements)..parent = enclosingFunction;
-    enclosingFunction.asyncMarker = AsyncMarker.Sync;
-    return enclosingFunction;
-  }
-
-  // :async_op's try-catch catch body:
-  @override
-  Statement buildCatchBody(exceptionVariable, stackTraceVariable) {
-    // _completeOnAsyncError(_future, e, st, :is_sync)
-    return ExpressionStatement(StaticInvocation(
-        helper.completeOnAsyncError,
-        Arguments([
-          VariableGet(asyncFutureVariable!),
-          VariableGet(exceptionVariable),
-          VariableGet(stackTraceVariable),
-          VariableGet(isSyncVariable!)
-        ])));
-  }
-
-  // :async_op's try-catch try body:
-  @override
-  Statement buildReturn(Statement body) {
-    // Returns from the body have all been translated into assignments to the
-    // return value variable followed by a break from the labeled body.
-
-    // .. body ..
-    // _completeOnAsyncReturn(_future, returnVariable, :is_sync)
-    // return;
-    return Block(<Statement>[
-      body,
-      ExpressionStatement(StaticInvocation(
-          canReturnFuture
-              ? helper.completeOnAsyncReturn
-              : helper.completeWithNoFutureOnAsyncReturn,
-          Arguments([
-            VariableGet(asyncFutureVariable!),
-            VariableGet(returnVariable!),
-            VariableGet(isSyncVariable!)
-          ]))),
-      ReturnStatement()..fileOffset = enclosingFunction.fileEndOffset
-    ]);
-  }
-
-  @override
-  TreeNode visitReturnStatement(
-      ReturnStatement node, TreeNode? removalSentinel) {
-    final expression = node.expression;
-    if (expression != null && !canReturnFuture) {
-      final returnedType = staticTypeContext.getExpressionType(expression);
-      canReturnFuture = _canHoldFutureObject(returnedType);
-    }
-
-    final transformedExpression = node.expression == null
-        ? NullLiteral()
-        : expressionRewriter!.rewrite(node.expression!, statements);
-    statements.add(ExpressionStatement(
-        VariableSet(returnVariable!, transformedExpression)
-          ..fileOffset = node.fileOffset));
-    statements.add(BreakStatement(labeledBody!));
-    return removalSentinel ?? EmptyStatement();
-  }
-
-  bool _canHoldFutureObject(DartType type) {
-    // Any supertype or subtype of `FutureOr` may hold a `Future` object.
-    final env = staticTypeContext.typeEnvironment;
-
-    if (type is TypeParameterType) {
-      type = type.parameter.defaultType;
-    }
-
-    if (type is FutureOrType) return true;
-
-    // Any supertype of Future (which includes Future/Object/dynamic) can hold
-    // Future objects.
-    if (env.isSubtypeOf(
-        helper.futureType, type, SubtypeCheckMode.ignoringNullabilities)) {
-      return true;
-    }
-
-    // Any subtype of Future (which includes Future/_Future and any user-defined
-    // implementations) can hold Future objects.
-    if (env.isSubtypeOf(
-        type, helper.futureType, SubtypeCheckMode.ignoringNullabilities)) {
-      return true;
-    }
-    return false;
-  }
-}
-
-class HelperNodes {
-  final Procedure asyncErrorWrapper;
-  final Library asyncLibrary;
-  final Procedure asyncStarStreamControllerAdd;
-  final Procedure asyncStarStreamControllerAddError;
-  final Procedure asyncStarStreamControllerAddStream;
-  final Class asyncStarStreamControllerClass;
-  final Procedure asyncStarStreamControllerClose;
-  final Constructor asyncStarStreamControllerConstructor;
-  final Member asyncStarStreamControllerStream;
-  final Procedure asyncStarMoveNextHelper;
-  final Procedure asyncThenWrapper;
-  final Procedure awaitHelper;
-  final Procedure completeOnAsyncReturn;
-  final Procedure completeWithNoFutureOnAsyncReturn;
-  final Procedure completeOnAsyncError;
-  final Library coreLibrary;
-  final CoreTypes coreTypes;
-  final Class futureClass;
-  final Class futureOrClass;
-  final Class futureImplClass;
-  final Constructor futureImplConstructor;
-  final Class iterableClass;
-  final Class streamClass;
-  final Procedure streamIteratorCancel;
-  final Class streamIteratorClass;
-  final Constructor streamIteratorConstructor;
-  final Member streamIteratorCurrent;
-  final Procedure streamIteratorMoveNext;
-  final Constructor syncIterableConstructor;
-  final Class syncIteratorClass;
-  final Member syncIteratorCurrent;
-  final Member syncIteratorYieldEachIterable;
-  final Class boolClass;
-  final Procedure unsafeCast;
-  final DartType futureType;
-
-  bool productMode;
-
-  HelperNodes._(
-      this.asyncErrorWrapper,
-      this.asyncLibrary,
-      this.asyncStarStreamControllerAdd,
-      this.asyncStarStreamControllerAddError,
-      this.asyncStarStreamControllerAddStream,
-      this.asyncStarStreamControllerClass,
-      this.asyncStarStreamControllerClose,
-      this.asyncStarStreamControllerConstructor,
-      this.asyncStarStreamControllerStream,
-      this.asyncStarMoveNextHelper,
-      this.asyncThenWrapper,
-      this.awaitHelper,
-      this.completeOnAsyncReturn,
-      this.completeWithNoFutureOnAsyncReturn,
-      this.completeOnAsyncError,
-      this.coreLibrary,
-      this.coreTypes,
-      this.futureClass,
-      this.futureOrClass,
-      this.futureImplClass,
-      this.futureImplConstructor,
-      this.iterableClass,
-      this.streamClass,
-      this.streamIteratorCancel,
-      this.streamIteratorClass,
-      this.streamIteratorConstructor,
-      this.streamIteratorCurrent,
-      this.streamIteratorMoveNext,
-      this.syncIterableConstructor,
-      this.syncIteratorClass,
-      this.syncIteratorCurrent,
-      this.syncIteratorYieldEachIterable,
-      this.boolClass,
-      this.productMode,
-      this.unsafeCast)
-      : futureType = InterfaceType(
-            futureClass, Nullability.nonNullable, [DynamicType()]);
-
-  factory HelperNodes.fromCoreTypes(CoreTypes coreTypes, bool productMode) {
-    return new HelperNodes._(
-        coreTypes.asyncErrorWrapperHelperProcedure,
-        coreTypes.asyncLibrary,
-        coreTypes.asyncStarStreamControllerAdd,
-        coreTypes.asyncStarStreamControllerAddError,
-        coreTypes.asyncStarStreamControllerAddStream,
-        coreTypes.asyncStarStreamControllerClass,
-        coreTypes.asyncStarStreamControllerClose,
-        coreTypes.asyncStarStreamControllerDefaultConstructor,
-        coreTypes.asyncStarStreamControllerStream,
-        coreTypes.asyncStarMoveNextHelper,
-        coreTypes.asyncThenWrapperHelperProcedure,
-        coreTypes.awaitHelperProcedure,
-        coreTypes.completeOnAsyncReturn,
-        coreTypes.completeWithNoFutureOnAsyncReturn,
-        coreTypes.completeOnAsyncError,
-        coreTypes.coreLibrary,
-        coreTypes,
-        coreTypes.futureClass,
-        coreTypes.deprecatedFutureOrClass,
-        coreTypes.futureImplClass,
-        coreTypes.futureImplConstructor,
-        coreTypes.iterableClass,
-        coreTypes.streamClass,
-        coreTypes.streamIteratorCancel,
-        coreTypes.streamIteratorClass,
-        coreTypes.streamIteratorDefaultConstructor,
-        coreTypes.streamIteratorCurrent,
-        coreTypes.streamIteratorMoveNext,
-        coreTypes.syncIterableDefaultConstructor,
-        coreTypes.syncIteratorClass,
-        coreTypes.syncIteratorCurrent,
-        coreTypes.syncIteratorYieldEachIterable,
-        coreTypes.boolClass,
-        productMode,
-        coreTypes.index.getTopLevelMember('dart:_internal', 'unsafeCast')
-            as Procedure);
-  }
-}
diff --git a/pkg/vm/lib/transformations/for_in_lowering.dart b/pkg/vm/lib/transformations/for_in_lowering.dart
new file mode 100644
index 0000000..f9576ed
--- /dev/null
+++ b/pkg/vm/lib/transformations/for_in_lowering.dart
@@ -0,0 +1,222 @@
+// Copyright (c) 2022, 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:kernel/ast.dart';
+import 'package:kernel/core_types.dart' show CoreTypes;
+import 'package:kernel/type_algebra.dart' show Substitution;
+import 'package:kernel/type_environment.dart' show StaticTypeContext;
+
+class ForInVariables {
+  static const stream = ':stream';
+  static const forIterator = ':for-iterator';
+  static const syncForIterator = ':sync-for-iterator';
+}
+
+/// VM-specific desugaring of for-in loops.
+class ForInLowering {
+  final CoreTypes coreTypes;
+  final bool productMode;
+
+  ForInLowering(this.coreTypes, {required this.productMode});
+
+  Statement transformForInStatement(ForInStatement stmt,
+      FunctionNode? enclosingFunction, StaticTypeContext staticTypeContext) {
+    if (stmt.isAsync) {
+      if (enclosingFunction == null ||
+          (enclosingFunction.asyncMarker != AsyncMarker.Async &&
+              enclosingFunction.asyncMarker != AsyncMarker.AsyncStar)) {
+        return stmt;
+      }
+      // Transform
+      //
+      //   await for (T variable in <stream-expression>) { ... }
+      //
+      // To (in product mode):
+      //
+      //   {
+      //     :stream = <stream-expression>;
+      //     _StreamIterator<T> :for-iterator = new _StreamIterator<T>(:stream);
+      //     try {
+      //       while (await :for-iterator.moveNext()) {
+      //         T <variable> = :for-iterator.current;
+      //         ...
+      //       }
+      //     } finally {
+      //       if (:for-iterator._subscription != null)
+      //           await :for-iterator.cancel();
+      //     }
+      //   }
+      //
+      // Or (in non-product mode):
+      //
+      //   {
+      //     :stream = <stream-expression>;
+      //     _StreamIterator<T> :for-iterator = new _StreamIterator<T>(:stream);
+      //     try {
+      //       while (let _ = _asyncStarMoveNextHelper(:stream) in
+      //           await :for-iterator.moveNext()) {
+      //         T <variable> = :for-iterator.current;
+      //         ...
+      //       }
+      //     } finally {
+      //       if (:for-iterator._subscription != null)
+      //           await :for-iterator.cancel();
+      //     }
+      //   }
+      final valueVariable = stmt.variable;
+
+      final streamVariable = new VariableDeclaration(ForInVariables.stream,
+          initializer: stmt.iterable,
+          type: stmt.iterable.getStaticType(staticTypeContext));
+
+      final streamIteratorType = new InterfaceType(
+          coreTypes.streamIteratorClass,
+          staticTypeContext.nullable,
+          [valueVariable.type]);
+      final forIteratorVariable = VariableDeclaration(
+          ForInVariables.forIterator,
+          initializer: new ConstructorInvocation(
+              coreTypes.streamIteratorDefaultConstructor,
+              new Arguments(<Expression>[new VariableGet(streamVariable)],
+                  types: [valueVariable.type])),
+          type: streamIteratorType);
+
+      // await :for-iterator.moveNext()
+      final condition = new AwaitExpression(new InstanceInvocation(
+          InstanceAccessKind.Instance,
+          VariableGet(forIteratorVariable),
+          coreTypes.streamIteratorMoveNext.name,
+          new Arguments([]),
+          interfaceTarget: coreTypes.streamIteratorMoveNext,
+          functionType:
+              coreTypes.streamIteratorMoveNext.getterType as FunctionType))
+        ..fileOffset = stmt.fileOffset;
+
+      Expression whileCondition;
+      if (productMode) {
+        whileCondition = condition;
+      } else {
+        // _asyncStarMoveNextHelper(:stream)
+        final asyncStarMoveNextCall = new StaticInvocation(
+            coreTypes.asyncStarMoveNextHelper,
+            new Arguments([new VariableGet(streamVariable)]))
+          ..fileOffset = stmt.fileOffset;
+
+        // let _ = asyncStarMoveNextCall in (condition)
+        whileCondition = new Let(
+            new VariableDeclaration(null, initializer: asyncStarMoveNextCall),
+            condition);
+      }
+
+      // T <variable> = :for-iterator.current;
+      valueVariable.initializer = new InstanceGet(
+          InstanceAccessKind.Instance,
+          VariableGet(forIteratorVariable),
+          coreTypes.streamIteratorCurrent.name,
+          interfaceTarget: coreTypes.streamIteratorCurrent,
+          resultType: valueVariable.type)
+        ..fileOffset = stmt.bodyOffset;
+      valueVariable.initializer!.parent = valueVariable;
+
+      final whileBody = new Block(<Statement>[valueVariable, stmt.body]);
+      final tryBody = new WhileStatement(whileCondition, whileBody);
+
+      // if (:for-iterator._subscription != null) await :for-iterator.cancel();
+      final DartType subscriptionType =
+          Substitution.fromInterfaceType(streamIteratorType)
+              .substituteType(coreTypes.streamIteratorSubscription.getterType);
+      final tryFinalizer = new IfStatement(
+          new Not(new EqualsNull(new InstanceGet(
+              InstanceAccessKind.Instance,
+              VariableGet(forIteratorVariable),
+              coreTypes.streamIteratorSubscription.name,
+              interfaceTarget: coreTypes.streamIteratorSubscription,
+              resultType: subscriptionType))),
+          new ExpressionStatement(new AwaitExpression(new InstanceInvocation(
+              InstanceAccessKind.Instance,
+              VariableGet(forIteratorVariable),
+              coreTypes.streamIteratorCancel.name,
+              new Arguments(<Expression>[]),
+              interfaceTarget: coreTypes.streamIteratorCancel,
+              functionType:
+                  coreTypes.streamIteratorCancel.getterType as FunctionType))),
+          null);
+
+      final tryFinally = new TryFinally(tryBody, tryFinalizer);
+
+      final block = new Block(
+          <Statement>[streamVariable, forIteratorVariable, tryFinally]);
+      return block;
+    }
+
+    // Transform
+    //
+    //   for ({var/final} T <variable> in <iterable>) { ... }
+    //
+    // Into
+    //
+    //  {
+    //    final Iterator<T> :sync-for-iterator = <iterable>.iterator;
+    //    for (; :sync-for-iterator.moveNext() ;) {
+    //        {var/final} T variable = :sync-for-iterator.current;
+    //        ...
+    //      }
+    //    }
+    //  }
+
+    // The CFE might invoke this transformation despite the program having
+    // compile-time errors. So we will not transform this [stmt] if the
+    // `stmt.iterable` is an invalid expression or has an invalid type and
+    // instead eliminate the entire for-in and replace it with a invalid
+    // expression statement.
+    final iterable = stmt.iterable;
+    final iterableType = iterable.getStaticType(staticTypeContext);
+    if (iterableType is InvalidType) {
+      return ExpressionStatement(
+          InvalidExpression('Invalid iterable type in for-in'));
+    }
+
+    // The NNBD sdk declares that Iterable.get:iterator returns a non-nullable
+    // `Iterator<E>`.
+    assert(const [
+      Nullability.nonNullable,
+      Nullability.legacy
+    ].contains(coreTypes.iterableGetIterator.function.returnType.nullability));
+
+    final DartType elementType = stmt.getElementType(staticTypeContext);
+    final iteratorType = InterfaceType(
+        coreTypes.iteratorClass, staticTypeContext.nonNullable, [elementType]);
+
+    final syncForIterator = VariableDeclaration(ForInVariables.syncForIterator,
+        initializer: InstanceGet(InstanceAccessKind.Instance, iterable,
+            coreTypes.iterableGetIterator.name,
+            interfaceTarget: coreTypes.iterableGetIterator,
+            resultType: iteratorType)
+          ..fileOffset = iterable.fileOffset,
+        type: iteratorType)
+      ..fileOffset = iterable.fileOffset;
+
+    final condition = InstanceInvocation(
+        InstanceAccessKind.Instance,
+        VariableGet(syncForIterator),
+        coreTypes.iteratorMoveNext.name,
+        Arguments([]),
+        interfaceTarget: coreTypes.iteratorMoveNext,
+        functionType: coreTypes.iteratorMoveNext.getterType as FunctionType)
+      ..fileOffset = iterable.fileOffset;
+
+    final variable = stmt.variable
+      ..initializer = (InstanceGet(InstanceAccessKind.Instance,
+          VariableGet(syncForIterator), coreTypes.iteratorGetCurrent.name,
+          interfaceTarget: coreTypes.iteratorGetCurrent,
+          resultType: elementType)
+        ..fileOffset = stmt.bodyOffset);
+    variable.initializer!.parent = variable;
+
+    final Block body = Block([variable, stmt.body])
+      ..fileOffset = stmt.bodyOffset;
+
+    return Block([syncForIterator, ForStatement([], condition, [], body)]);
+  }
+}
diff --git a/pkg/vm/lib/transformations/lowering.dart b/pkg/vm/lib/transformations/lowering.dart
index 4242ff4..f86ff95 100644
--- a/pkg/vm/lib/transformations/lowering.dart
+++ b/pkg/vm/lib/transformations/lowering.dart
@@ -10,6 +10,7 @@
 
 import 'package:vm/transformations/specializer/factory_specializer.dart';
 
+import 'for_in_lowering.dart' show ForInLowering;
 import 'late_var_init_transformer.dart' show LateVarInitTransformer;
 import 'list_literals_lowering.dart' show ListLiteralsLowering;
 import 'type_casts_optimizer.dart' as typeCastsOptimizer
@@ -20,15 +21,19 @@
 ///
 /// Each transformation is applied locally to AST nodes of certain types
 /// after transforming children nodes.
-void transformLibraries(List<Library> libraries, CoreTypes coreTypes,
-    ClassHierarchy hierarchy, bool nullSafety) {
-  final transformer = _Lowering(coreTypes, hierarchy, nullSafety);
+void transformLibraries(
+    List<Library> libraries, CoreTypes coreTypes, ClassHierarchy hierarchy,
+    {required bool nullSafety, required bool productMode}) {
+  final transformer = _Lowering(coreTypes, hierarchy,
+      nullSafety: nullSafety, productMode: productMode);
   libraries.forEach(transformer.visitLibrary);
 }
 
-void transformProcedure(Procedure procedure, CoreTypes coreTypes,
-    ClassHierarchy hierarchy, bool nullSafety) {
-  final transformer = _Lowering(coreTypes, hierarchy, nullSafety);
+void transformProcedure(
+    Procedure procedure, CoreTypes coreTypes, ClassHierarchy hierarchy,
+    {required bool nullSafety, required bool productMode}) {
+  final transformer = _Lowering(coreTypes, hierarchy,
+      nullSafety: nullSafety, productMode: productMode);
   procedure.accept(transformer);
 }
 
@@ -38,15 +43,19 @@
   final LateVarInitTransformer lateVarInitTransformer;
   final FactorySpecializer factorySpecializer;
   final ListLiteralsLowering listLiteralsLowering;
+  final ForInLowering forInLowering;
 
   Member? _currentMember;
+  FunctionNode? _currentFunctionNode;
   StaticTypeContext? _cachedStaticTypeContext;
 
-  _Lowering(CoreTypes coreTypes, ClassHierarchy hierarchy, this.nullSafety)
+  _Lowering(CoreTypes coreTypes, ClassHierarchy hierarchy,
+      {required this.nullSafety, required bool productMode})
       : env = TypeEnvironment(coreTypes, hierarchy),
         lateVarInitTransformer = LateVarInitTransformer(),
         factorySpecializer = FactorySpecializer(coreTypes),
-        listLiteralsLowering = ListLiteralsLowering(coreTypes);
+        listLiteralsLowering = ListLiteralsLowering(coreTypes),
+        forInLowering = ForInLowering(coreTypes, productMode: productMode);
 
   StaticTypeContext get _staticTypeContext =>
       _cachedStaticTypeContext ??= StaticTypeContext(_currentMember!, env);
@@ -70,6 +79,17 @@
   }
 
   @override
+  visitFunctionNode(FunctionNode node) {
+    final savedFunctionNode = _currentFunctionNode;
+    _currentFunctionNode = node;
+
+    final result = super.visitFunctionNode(node);
+
+    _currentFunctionNode = savedFunctionNode;
+    return result;
+  }
+
+  @override
   visitStaticInvocation(StaticInvocation node) {
     node.transformChildren(this);
     return factorySpecializer.transformStaticInvocation(node);
@@ -99,4 +119,11 @@
     node.transformChildren(this);
     return listLiteralsLowering.transformListLiteral(node);
   }
+
+  @override
+  visitForInStatement(ForInStatement node) {
+    node.transformChildren(this);
+    return forInLowering.transformForInStatement(
+        node, _currentFunctionNode, _staticTypeContext);
+  }
 }
diff --git a/pkg/vm/lib/transformations/type_flow/summary_collector.dart b/pkg/vm/lib/transformations/type_flow/summary_collector.dart
index 6bd2768..b89c8a7 100644
--- a/pkg/vm/lib/transformations/type_flow/summary_collector.dart
+++ b/pkg/vm/lib/transformations/type_flow/summary_collector.dart
@@ -363,17 +363,6 @@
     final function = node.function;
     function.accept(this);
 
-    if (function.asyncMarker == AsyncMarker.SyncYielding) {
-      // Mark parameters of synthetic async_op closures as captured
-      // to make sure their updates at yield points are taken into account.
-      for (var v in function.positionalParameters) {
-        _captureVariable(v);
-      }
-      for (var v in function.namedParameters) {
-        _captureVariable(v);
-      }
-    }
-
     activeStatements = savedActiveStatements;
     numVariablesAtActiveStatements = savedNumVariablesAtActiveStatements;
     numVariablesAtFunctionEntry = savedNumVariablesAtFunctionEntry;
diff --git a/runtime/docs/index.md b/runtime/docs/index.md
index 8e261b0..23a1a14 100644
--- a/runtime/docs/index.md
+++ b/runtime/docs/index.md
@@ -98,7 +98,7 @@
 At this point enough information is loaded from Kernel binary for runtime to successfully resolve and invoke methods. For example, it could resolve and invoke `main` function from a library.
 
 !!! sourcecode "Source to read"
-    @{package:kernel/ast.dart} defines classes describing the Kernel AST. @{package:front_end} handles parsing Dart source and building Kernel AST from it. @{dart::kernel::KernelLoader::LoadEntireProgram} is an entry point for deserialization of Kernel AST into corresponding VM objects. @{pkg/vm/bin/kernel_service.dart} implements the Kernel Service isolate, @{runtime/vm/kernel_isolate.cc} glues Dart implementation to the rest of the VM. @{package:vm} hosts most of the Kernel based VM specific functionality, e.g various Kernel-to-Kernel transformations. However some VM specific transformations still live in @{package:kernel} for historical reasons. A good example of a complicated transformation is @{package:kernel/transformations/continuation.dart}, which desugars `async`,`async*` and `sync*` functions.
+    @{package:kernel/ast.dart} defines classes describing the Kernel AST. @{package:front_end} handles parsing Dart source and building Kernel AST from it. @{dart::kernel::KernelLoader::LoadEntireProgram} is an entry point for deserialization of Kernel AST into corresponding VM objects. @{pkg/vm/bin/kernel_service.dart} implements the Kernel Service isolate, @{runtime/vm/kernel_isolate.cc} glues Dart implementation to the rest of the VM. @{package:vm} hosts most of the Kernel based VM specific functionality, e.g various Kernel-to-Kernel transformations.
 
 
 !!! tryit "Trying it"
diff --git a/runtime/tests/vm/dart/splay_test.dart b/runtime/tests/vm/dart/splay_test.dart
index e508561..ef51ede 100644
--- a/runtime/tests/vm/dart/splay_test.dart
+++ b/runtime/tests/vm/dart/splay_test.dart
@@ -28,6 +28,9 @@
 // VMOptions=--stress_write_barrier_elimination
 // VMOptions=--old_gen_heap_size=100
 // VMOptions=--mark_when_idle
+// VMOptions=--no_load_cse
+// VMOptions=--no_dead_store_elimination
+// VMOptions=--no_load_cse --no_dead_store_elimination
 
 import "dart:math";
 import 'package:benchmark_harness/benchmark_harness.dart';
diff --git a/runtime/tests/vm/dart_2/splay_test.dart b/runtime/tests/vm/dart_2/splay_test.dart
index f0474f6..6cd3050 100644
--- a/runtime/tests/vm/dart_2/splay_test.dart
+++ b/runtime/tests/vm/dart_2/splay_test.dart
@@ -32,6 +32,9 @@
 // VMOptions=--stress_write_barrier_elimination
 // VMOptions=--old_gen_heap_size=100
 // VMOptions=--mark_when_idle
+// VMOptions=--no_load_cse
+// VMOptions=--no_dead_store_elimination
+// VMOptions=--no_load_cse --no_dead_store_elimination
 
 import "dart:math";
 import 'package:benchmark_harness/benchmark_harness.dart';
diff --git a/runtime/vm/compiler/backend/il.h b/runtime/vm/compiler/backend/il.h
index c7626f7..ad51a54 100644
--- a/runtime/vm/compiler/backend/il.h
+++ b/runtime/vm/compiler/backend/il.h
@@ -9412,7 +9412,7 @@
   SIMD_BINARY_FLOAT_OP_LIST(M, BINARY_OP, Float32x4)                           \
   SIMD_BINARY_FLOAT_OP_LIST(M, BINARY_OP, Float64x2)                           \
   SIMD_BINARY_INTEGER_OP_LIST(M, BINARY_OP, Int32x4)                           \
-  SIMD_PER_COMPONENT_XYZW(M, 1, Float32x4Shuffle, (Float32x4), Double)         \
+  SIMD_PER_COMPONENT_XYZW(M, 1, Float32x4Get, (Float32x4), Double)             \
   SIMD_PER_COMPONENT_XYZW(M, 2, Float32x4With, (Double, Float32x4), Float32x4) \
   SIMD_PER_COMPONENT_XYZW(M, 1, Int32x4GetFlag, (Int32x4), Bool)               \
   SIMD_PER_COMPONENT_XYZW(M, 2, Int32x4WithFlag, (Int32x4, Bool), Int32x4)     \
diff --git a/runtime/vm/compiler/backend/il_arm.cc b/runtime/vm/compiler/backend/il_arm.cc
index d42f1c2..2775dde 100644
--- a/runtime/vm/compiler/backend/il_arm.cc
+++ b/runtime/vm/compiler/backend/il_arm.cc
@@ -5154,16 +5154,16 @@
   // instructions. For arbitrary shuffles, use vtbl.
 
   switch (instr->kind()) {
-    case SimdOpInstr::kFloat32x4ShuffleX:
+    case SimdOpInstr::kFloat32x4GetX:
       __ vcvtds(result.d(0), value.s(0));
       break;
-    case SimdOpInstr::kFloat32x4ShuffleY:
+    case SimdOpInstr::kFloat32x4GetY:
       __ vcvtds(result.d(0), value.s(1));
       break;
-    case SimdOpInstr::kFloat32x4ShuffleZ:
+    case SimdOpInstr::kFloat32x4GetZ:
       __ vcvtds(result.d(0), value.s(2));
       break;
-    case SimdOpInstr::kFloat32x4ShuffleW:
+    case SimdOpInstr::kFloat32x4GetW:
       __ vcvtds(result.d(0), value.s(3));
       break;
     case SimdOpInstr::kInt32x4Shuffle:
@@ -5597,10 +5597,10 @@
   CASE(Float64x2Mul)                                                           \
   CASE(Float64x2Div)                                                           \
   ____(Float64x2BinaryOp)                                                      \
-  CASE(Float32x4ShuffleX)                                                      \
-  CASE(Float32x4ShuffleY)                                                      \
-  CASE(Float32x4ShuffleZ)                                                      \
-  CASE(Float32x4ShuffleW)                                                      \
+  CASE(Float32x4GetX)                                                          \
+  CASE(Float32x4GetY)                                                          \
+  CASE(Float32x4GetZ)                                                          \
+  CASE(Float32x4GetW)                                                          \
   CASE(Int32x4Shuffle)                                                         \
   CASE(Float32x4Shuffle)                                                       \
   ____(Simd32x4Shuffle)                                                        \
diff --git a/runtime/vm/compiler/backend/il_arm64.cc b/runtime/vm/compiler/backend/il_arm64.cc
index 2dfcd42..f0b8f86 100644
--- a/runtime/vm/compiler/backend/il_arm64.cc
+++ b/runtime/vm/compiler/backend/il_arm64.cc
@@ -4414,19 +4414,19 @@
     break;
     SIMD_OP_SIMPLE_UNARY(EMIT)
 #undef EMIT
-    case SimdOpInstr::kFloat32x4ShuffleX:
+    case SimdOpInstr::kFloat32x4GetX:
       __ vinss(result, 0, value, 0);
       __ fcvtds(result, result);
       break;
-    case SimdOpInstr::kFloat32x4ShuffleY:
+    case SimdOpInstr::kFloat32x4GetY:
       __ vinss(result, 0, value, 1);
       __ fcvtds(result, result);
       break;
-    case SimdOpInstr::kFloat32x4ShuffleZ:
+    case SimdOpInstr::kFloat32x4GetZ:
       __ vinss(result, 0, value, 2);
       __ fcvtds(result, result);
       break;
-    case SimdOpInstr::kFloat32x4ShuffleW:
+    case SimdOpInstr::kFloat32x4GetW:
       __ vinss(result, 0, value, 3);
       __ fcvtds(result, result);
       break;
@@ -4705,10 +4705,10 @@
   CASE(Float64x2Scale)                                                         \
   ____(SimdBinaryOp)                                                           \
   SIMD_OP_SIMPLE_UNARY(CASE)                                                   \
-  CASE(Float32x4ShuffleX)                                                      \
-  CASE(Float32x4ShuffleY)                                                      \
-  CASE(Float32x4ShuffleZ)                                                      \
-  CASE(Float32x4ShuffleW)                                                      \
+  CASE(Float32x4GetX)                                                          \
+  CASE(Float32x4GetY)                                                          \
+  CASE(Float32x4GetZ)                                                          \
+  CASE(Float32x4GetW)                                                          \
   CASE(Int32x4Shuffle)                                                         \
   CASE(Float32x4Shuffle)                                                       \
   CASE(Float32x4Splat)                                                         \
diff --git a/runtime/vm/compiler/backend/il_ia32.cc b/runtime/vm/compiler/backend/il_ia32.cc
index 9d1ffa4..355c0c0 100644
--- a/runtime/vm/compiler/backend/il_ia32.cc
+++ b/runtime/vm/compiler/backend/il_ia32.cc
@@ -4507,19 +4507,19 @@
     break;
     SIMD_OP_SIMPLE_UNARY(EMIT)
 #undef EMIT
-    case SimdOpInstr::kFloat32x4ShuffleX:
+    case SimdOpInstr::kFloat32x4GetX:
       // Shuffle not necessary.
       __ cvtss2sd(value, value);
       break;
-    case SimdOpInstr::kFloat32x4ShuffleY:
+    case SimdOpInstr::kFloat32x4GetY:
       __ shufps(value, value, compiler::Immediate(0x55));
       __ cvtss2sd(value, value);
       break;
-    case SimdOpInstr::kFloat32x4ShuffleZ:
+    case SimdOpInstr::kFloat32x4GetZ:
       __ shufps(value, value, compiler::Immediate(0xAA));
       __ cvtss2sd(value, value);
       break;
-    case SimdOpInstr::kFloat32x4ShuffleW:
+    case SimdOpInstr::kFloat32x4GetW:
       __ shufps(value, value, compiler::Immediate(0xFF));
       __ cvtss2sd(value, value);
       break;
@@ -4739,10 +4739,10 @@
   CASE(Float32x4WithW)                                                         \
   ____(SimdBinaryOp)                                                           \
   SIMD_OP_SIMPLE_UNARY(CASE)                                                   \
-  CASE(Float32x4ShuffleX)                                                      \
-  CASE(Float32x4ShuffleY)                                                      \
-  CASE(Float32x4ShuffleZ)                                                      \
-  CASE(Float32x4ShuffleW)                                                      \
+  CASE(Float32x4GetX)                                                          \
+  CASE(Float32x4GetY)                                                          \
+  CASE(Float32x4GetZ)                                                          \
+  CASE(Float32x4GetW)                                                          \
   CASE(Float32x4Shuffle)                                                       \
   CASE(Int32x4Shuffle)                                                         \
   CASE(Float32x4Splat)                                                         \
diff --git a/runtime/vm/compiler/backend/il_riscv.cc b/runtime/vm/compiler/backend/il_riscv.cc
index ba8d560..140c21d 100644
--- a/runtime/vm/compiler/backend/il_riscv.cc
+++ b/runtime/vm/compiler/backend/il_riscv.cc
@@ -4990,10 +4990,10 @@
   CASE(Float64x2Scale)                                                         \
   ____(SimdBinaryOp)                                                           \
   SIMD_OP_SIMPLE_UNARY(CASE)                                                   \
-  CASE(Float32x4ShuffleX)                                                      \
-  CASE(Float32x4ShuffleY)                                                      \
-  CASE(Float32x4ShuffleZ)                                                      \
-  CASE(Float32x4ShuffleW)                                                      \
+  CASE(Float32x4GetX)                                                          \
+  CASE(Float32x4GetY)                                                          \
+  CASE(Float32x4GetZ)                                                          \
+  CASE(Float32x4GetW)                                                          \
   CASE(Int32x4Shuffle)                                                         \
   CASE(Float32x4Shuffle)                                                       \
   CASE(Float32x4Splat)                                                         \
diff --git a/runtime/vm/compiler/backend/il_x64.cc b/runtime/vm/compiler/backend/il_x64.cc
index 55fc995..8364d7a 100644
--- a/runtime/vm/compiler/backend/il_x64.cc
+++ b/runtime/vm/compiler/backend/il_x64.cc
@@ -4734,19 +4734,19 @@
     break;
     SIMD_OP_SIMPLE_UNARY(EMIT)
 #undef EMIT
-    case SimdOpInstr::kFloat32x4ShuffleX:
+    case SimdOpInstr::kFloat32x4GetX:
       // Shuffle not necessary.
       __ cvtss2sd(value, value);
       break;
-    case SimdOpInstr::kFloat32x4ShuffleY:
+    case SimdOpInstr::kFloat32x4GetY:
       __ shufps(value, value, compiler::Immediate(0x55));
       __ cvtss2sd(value, value);
       break;
-    case SimdOpInstr::kFloat32x4ShuffleZ:
+    case SimdOpInstr::kFloat32x4GetZ:
       __ shufps(value, value, compiler::Immediate(0xAA));
       __ cvtss2sd(value, value);
       break;
-    case SimdOpInstr::kFloat32x4ShuffleW:
+    case SimdOpInstr::kFloat32x4GetW:
       __ shufps(value, value, compiler::Immediate(0xFF));
       __ cvtss2sd(value, value);
       break;
@@ -4965,10 +4965,10 @@
   CASE(Float32x4WithW)                                                         \
   ____(SimdBinaryOp)                                                           \
   SIMD_OP_SIMPLE_UNARY(CASE)                                                   \
-  CASE(Float32x4ShuffleX)                                                      \
-  CASE(Float32x4ShuffleY)                                                      \
-  CASE(Float32x4ShuffleZ)                                                      \
-  CASE(Float32x4ShuffleW)                                                      \
+  CASE(Float32x4GetX)                                                          \
+  CASE(Float32x4GetY)                                                          \
+  CASE(Float32x4GetZ)                                                          \
+  CASE(Float32x4GetW)                                                          \
   CASE(Float32x4Shuffle)                                                       \
   CASE(Int32x4Shuffle)                                                         \
   CASE(Float32x4Splat)                                                         \
diff --git a/runtime/vm/compiler/backend/inliner.cc b/runtime/vm/compiler/backend/inliner.cc
index 575121f..4c8e06f 100644
--- a/runtime/vm/compiler/backend/inliner.cc
+++ b/runtime/vm/compiler/backend/inliner.cc
@@ -4058,10 +4058,10 @@
     case MethodRecognizer::kFloat32x4Reciprocal:
     case MethodRecognizer::kFloat32x4ReciprocalSqrt:
     case MethodRecognizer::kFloat32x4Scale:
-    case MethodRecognizer::kFloat32x4ShuffleW:
-    case MethodRecognizer::kFloat32x4ShuffleX:
-    case MethodRecognizer::kFloat32x4ShuffleY:
-    case MethodRecognizer::kFloat32x4ShuffleZ:
+    case MethodRecognizer::kFloat32x4GetW:
+    case MethodRecognizer::kFloat32x4GetX:
+    case MethodRecognizer::kFloat32x4GetY:
+    case MethodRecognizer::kFloat32x4GetZ:
     case MethodRecognizer::kFloat32x4Splat:
     case MethodRecognizer::kFloat32x4Sqrt:
     case MethodRecognizer::kFloat32x4ToFloat64x2:
diff --git a/runtime/vm/compiler/backend/redundancy_elimination.cc b/runtime/vm/compiler/backend/redundancy_elimination.cc
index 7d37d1f..5edf25f 100644
--- a/runtime/vm/compiler/backend/redundancy_elimination.cc
+++ b/runtime/vm/compiler/backend/redundancy_elimination.cc
@@ -3197,7 +3197,7 @@
 };
 
 void DeadStoreElimination::Optimize(FlowGraph* graph) {
-  if (FLAG_dead_store_elimination) {
+  if (FLAG_dead_store_elimination && FLAG_load_cse) {
     StoreOptimizer::OptimizeGraph(graph);
   }
 }
@@ -3599,6 +3599,12 @@
 }
 
 void AllocationSinking::Optimize() {
+  // Allocation sinking depends on load forwarding, so give up early if load
+  // forwarding is disabled.
+  if (!FLAG_load_cse || flow_graph_->is_huge_method()) {
+    return;
+  }
+
   CollectCandidates();
 
   // Insert MaterializeObject instructions that will describe the state of the
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index ff0c9bc..db660b5 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -5303,188 +5303,108 @@
 
   const uint8_t flags = ReadByte();  // read flags.
 
-  if ((flags & kYieldStatementFlagNative) == 0) {
-    Fragment instructions;
-    const bool is_yield_star = (flags & kYieldStatementFlagYieldStar) != 0;
+  Fragment instructions;
+  const bool is_yield_star = (flags & kYieldStatementFlagYieldStar) != 0;
 
-    // Load :suspend_state variable using low-level FP-relative load
-    // in order to avoid confusing SSA construction (which cannot
-    // track its value as it is modified implicitly by stubs).
-    LocalVariable* suspend_state = parsed_function()->suspend_state_var();
-    ASSERT(suspend_state != nullptr);
-    instructions += IntConstant(0);
-    instructions += B->LoadFpRelativeSlot(
-        compiler::target::frame_layout.FrameSlotForVariable(suspend_state) *
-            compiler::target::kWordSize,
-        CompileType::Dynamic(), kTagged);
-    instructions += LoadNativeField(Slot::SuspendState_function_data());
+  // Load :suspend_state variable using low-level FP-relative load
+  // in order to avoid confusing SSA construction (which cannot
+  // track its value as it is modified implicitly by stubs).
+  LocalVariable* suspend_state = parsed_function()->suspend_state_var();
+  ASSERT(suspend_state != nullptr);
+  instructions += IntConstant(0);
+  instructions += B->LoadFpRelativeSlot(
+      compiler::target::frame_layout.FrameSlotForVariable(suspend_state) *
+          compiler::target::kWordSize,
+      CompileType::Dynamic(), kTagged);
+  instructions += LoadNativeField(Slot::SuspendState_function_data());
 
-    instructions += BuildExpression();  // read expression.
-    if (NeedsDebugStepCheck(parsed_function()->function(), pos)) {
-      instructions += DebugStepCheck(pos);
-    }
-
-    if (parsed_function()->function().IsCompactAsyncStarFunction()) {
-      // In the async* functions, generate the following code for yield <expr>:
-      //
-      // _AsyncStarStreamController controller = :suspend_state._functionData;
-      // if (controller.add(<expr>)) {
-      //   return;
-      // }
-      // suspend();
-      //
-      // Generate the following code for yield* <expr>:
-      //
-      // _AsyncStarStreamController controller = :suspend_state._functionData;
-      // controller.addStream(<expr>);
-      // if (suspend()) {
-      //   return;
-      // }
-      //
-
-      auto& add_method = Function::ZoneHandle(Z);
-      if (is_yield_star) {
-        add_method =
-            IG->object_store()->async_star_stream_controller_add_stream();
-      } else {
-        add_method = IG->object_store()->async_star_stream_controller_add();
-      }
-      instructions += StaticCall(TokenPosition::kNoSource, add_method, 2,
-                                 ICData::kNoRebind);
-
-      if (is_yield_star) {
-        // Discard result of _AsyncStarStreamController.addStream().
-        instructions += Drop();
-        // Suspend and test value passed to the resumed async* body.
-        instructions += NullConstant();
-        instructions += B->Suspend(pos, SuspendInstr::StubId::kYieldAsyncStar);
-      } else {
-        // Test value returned by _AsyncStarStreamController.add().
-      }
-
-      TargetEntryInstr* exit;
-      TargetEntryInstr* continue_execution;
-      instructions += BranchIfTrue(&exit, &continue_execution, false);
-
-      Fragment do_exit(exit);
-      do_exit += TranslateFinallyFinalizers(nullptr, -1);
-      do_exit += NullConstant();
-      do_exit += Return(TokenPosition::kNoSource);
-
-      instructions = Fragment(instructions.entry, continue_execution);
-      if (!is_yield_star) {
-        instructions += NullConstant();
-        instructions += B->Suspend(pos, SuspendInstr::StubId::kYieldAsyncStar);
-        instructions += Drop();
-      }
-
-    } else if (parsed_function()->function().IsCompactSyncStarFunction()) {
-      // In the sync* functions, generate the following code for yield <expr>:
-      //
-      // _SyncStarIterator iterator = :suspend_state._functionData;
-      // iterator._current = <expr>;
-      // suspend();
-      //
-      // Generate the following code for yield* <expr>:
-      //
-      // _SyncStarIterator iterator = :suspend_state._functionData;
-      // iterator._yieldStarIterable = <expr>;
-      // suspend();
-      //
-      auto& field = Field::ZoneHandle(Z);
-      if (is_yield_star) {
-        field = IG->object_store()->sync_star_iterator_yield_star_iterable();
-      } else {
-        field = IG->object_store()->sync_star_iterator_current();
-      }
-      instructions += B->StoreInstanceFieldGuarded(field);
-      instructions += NullConstant();
-      instructions += B->Suspend(pos, SuspendInstr::StubId::kYieldSyncStar);
-      instructions += Drop();
-    } else {
-      UNREACHABLE();
-    }
-
-    return instructions;
-  }
-
-  ASSERT(flags == kYieldStatementFlagNative);  // Must have been desugared.
-
-  // Setup yield/continue point:
-  //
-  //   ...
-  //   :await_jump_var = index;
-  //   :await_ctx_var = :current_context_var
-  //   return <expr>
-  //
-  // Continuation<index>:
-  //   Drop(1)
-  //   ...
-  //
-  // BuildGraphOfFunction will create a dispatch that jumps to
-  // Continuation<:await_jump_var> upon entry to the function.
-  //
-  const intptr_t new_yield_pos = yield_continuations().length() + 1;
-  Fragment instructions = IntConstant(new_yield_pos);
-  instructions +=
-      StoreLocal(TokenPosition::kNoSource, scopes()->yield_jump_variable);
-  instructions += Drop();
-  instructions += LoadLocal(parsed_function()->current_context_var());
-  instructions +=
-      StoreLocal(TokenPosition::kNoSource, scopes()->yield_context_variable);
-  instructions += Drop();
   instructions += BuildExpression();  // read expression.
-  instructions += Return(pos, new_yield_pos);
-
-  // Note: DropTempsInstr serves as an anchor instruction. It will not
-  // be linked into the resulting graph.
-  DropTempsInstr* anchor = new (Z) DropTempsInstr(0, nullptr);
-  yield_continuations().Add(YieldContinuation(anchor, CurrentTryIndex()));
-
-  Fragment continuation(instructions.entry, anchor);
-  RELEASE_ASSERT(parsed_function()->function().IsAsyncClosure() ||
-                 parsed_function()->function().IsAsyncGenClosure() ||
-                 parsed_function()->function().IsSyncGenClosure());
-
-  // TODO(43900): Only emit this when needed.
-  {
-    // Our sync-yielding functions can be invoked with either a yield result or
-    // with an non-null exception & stacktrace.
-    //
-    // We detect the case we're in based on the nullability of stacktrace in
-    //
-    //   :sync_op(:iterator, [:exception, :stack_trace]) { }
-    //
-    // or:
-    //
-    //   :async_op(:result_or_exception, :stack_trace) { }
-    //
-    const auto& fun = parsed_function()->function();
-    LocalVariable* exception_var =
-        parsed_function()->ParameterVariable(fun.IsSyncGenClosure() ? 2 : 1);
-    LocalVariable* stack_trace_var =
-        parsed_function()->ParameterVariable(fun.IsSyncGenClosure() ? 3 : 2);
-    ASSERT(stack_trace_var->name().ptr() ==
-           Symbols::StackTraceParameter().ptr());
-
-    TargetEntryInstr* no_error;
-    TargetEntryInstr* error;
-
-    continuation += LoadLocal(stack_trace_var);
-    continuation += BranchIfNull(&no_error, &error);
-
-    Fragment rethrow(/*instruction=*/error);
-    rethrow += LoadLocal(exception_var);
-    rethrow += LoadLocal(stack_trace_var);
-
-    rethrow += RethrowException(pos, kInvalidTryIndex);
-    Drop();
-
-    // Set current to the end of the no_error branch.
-    continuation = Fragment(/*entry=*/continuation.entry, /*current=*/no_error);
+  if (NeedsDebugStepCheck(parsed_function()->function(), pos)) {
+    instructions += DebugStepCheck(pos);
   }
 
-  return continuation;
+  if (parsed_function()->function().IsCompactAsyncStarFunction()) {
+    // In the async* functions, generate the following code for yield <expr>:
+    //
+    // _AsyncStarStreamController controller = :suspend_state._functionData;
+    // if (controller.add(<expr>)) {
+    //   return;
+    // }
+    // suspend();
+    //
+    // Generate the following code for yield* <expr>:
+    //
+    // _AsyncStarStreamController controller = :suspend_state._functionData;
+    // controller.addStream(<expr>);
+    // if (suspend()) {
+    //   return;
+    // }
+    //
+
+    auto& add_method = Function::ZoneHandle(Z);
+    if (is_yield_star) {
+      add_method =
+          IG->object_store()->async_star_stream_controller_add_stream();
+    } else {
+      add_method = IG->object_store()->async_star_stream_controller_add();
+    }
+    instructions +=
+        StaticCall(TokenPosition::kNoSource, add_method, 2, ICData::kNoRebind);
+
+    if (is_yield_star) {
+      // Discard result of _AsyncStarStreamController.addStream().
+      instructions += Drop();
+      // Suspend and test value passed to the resumed async* body.
+      instructions += NullConstant();
+      instructions += B->Suspend(pos, SuspendInstr::StubId::kYieldAsyncStar);
+    } else {
+      // Test value returned by _AsyncStarStreamController.add().
+    }
+
+    TargetEntryInstr* exit;
+    TargetEntryInstr* continue_execution;
+    instructions += BranchIfTrue(&exit, &continue_execution, false);
+
+    Fragment do_exit(exit);
+    do_exit += TranslateFinallyFinalizers(nullptr, -1);
+    do_exit += NullConstant();
+    do_exit += Return(TokenPosition::kNoSource);
+
+    instructions = Fragment(instructions.entry, continue_execution);
+    if (!is_yield_star) {
+      instructions += NullConstant();
+      instructions += B->Suspend(pos, SuspendInstr::StubId::kYieldAsyncStar);
+      instructions += Drop();
+    }
+
+  } else if (parsed_function()->function().IsCompactSyncStarFunction()) {
+    // In the sync* functions, generate the following code for yield <expr>:
+    //
+    // _SyncStarIterator iterator = :suspend_state._functionData;
+    // iterator._current = <expr>;
+    // suspend();
+    //
+    // Generate the following code for yield* <expr>:
+    //
+    // _SyncStarIterator iterator = :suspend_state._functionData;
+    // iterator._yieldStarIterable = <expr>;
+    // suspend();
+    //
+    auto& field = Field::ZoneHandle(Z);
+    if (is_yield_star) {
+      field = IG->object_store()->sync_star_iterator_yield_star_iterable();
+    } else {
+      field = IG->object_store()->sync_star_iterator_current();
+    }
+    instructions += B->StoreInstanceFieldGuarded(field);
+    instructions += NullConstant();
+    instructions += B->Suspend(pos, SuspendInstr::StubId::kYieldSyncStar);
+    instructions += Drop();
+  } else {
+    UNREACHABLE();
+  }
+
+  return instructions;
 }
 
 Fragment StreamingFlowGraphBuilder::BuildVariableDeclaration(
@@ -5667,10 +5587,8 @@
           function.set_is_visible(true);
           ASSERT(function.IsCompactSyncStarFunction());
         } else {
-          ASSERT((function_node_helper.async_marker_ ==
-                  FunctionNodeHelper::kSync) ||
-                 (function_node_helper.async_marker_ ==
-                  FunctionNodeHelper::kSyncYielding));
+          ASSERT(function_node_helper.async_marker_ ==
+                 FunctionNodeHelper::kSync);
           function.set_is_debuggable(function_node_helper.dart_async_marker_ ==
                                      FunctionNodeHelper::kSync);
           switch (function_node_helper.dart_async_marker_) {
@@ -5687,8 +5605,7 @@
               // no special modifier
               break;
           }
-          function.set_is_generated_body(function_node_helper.async_marker_ ==
-                                         FunctionNodeHelper::kSyncYielding);
+          function.set_is_generated_body(false);
           // sync* functions contain two nested synthetic functions,
           // the first of which (sync_op_gen) is a regular sync function so we
           // need to manually label it generated:
diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.h b/runtime/vm/compiler/frontend/kernel_translation_helper.h
index e8067f4..314cab9 100644
--- a/runtime/vm/compiler/frontend/kernel_translation_helper.h
+++ b/runtime/vm/compiler/frontend/kernel_translation_helper.h
@@ -307,7 +307,6 @@
     kSyncStar = 1,
     kAsync = 2,
     kAsyncStar = 3,
-    kSyncYielding = 4,
   };
 
   explicit FunctionNodeHelper(KernelReaderHelper* helper) {
diff --git a/runtime/vm/compiler/frontend/scope_builder.cc b/runtime/vm/compiler/frontend/scope_builder.cc
index 2db5f8f..3e7f2f8 100644
--- a/runtime/vm/compiler/frontend/scope_builder.cc
+++ b/runtime/vm/compiler/frontend/scope_builder.cc
@@ -570,50 +570,6 @@
     first_body_token_position_ = helper_.reader_.min_position();
   }
 
-  // Ensure that :await_jump_var, :await_ctx_var, :async_op, :is_sync and
-  // :async_future are captured.
-  if (function_node_helper.async_marker_ == FunctionNodeHelper::kSyncYielding) {
-    {
-      LocalVariable* temp = nullptr;
-      LookupCapturedVariableByName(
-          (depth_.function_ == 0) ? &result_->yield_jump_variable : &temp,
-          Symbols::AwaitJumpVar());
-    }
-    {
-      LocalVariable* temp = nullptr;
-      LookupCapturedVariableByName(
-          (depth_.function_ == 0) ? &result_->yield_context_variable : &temp,
-          Symbols::AwaitContextVar());
-    }
-    {
-      LocalVariable* temp =
-          scope_->LookupVariable(Symbols::AsyncOperation(), true);
-      if (temp != nullptr) {
-        scope_->CaptureVariable(temp);
-      }
-    }
-    {
-      LocalVariable* temp =
-          scope_->LookupVariable(Symbols::AsyncFuture(), true);
-      if (temp != nullptr) {
-        scope_->CaptureVariable(temp);
-      }
-    }
-    {
-      LocalVariable* temp = scope_->LookupVariable(Symbols::is_sync(), true);
-      if (temp != nullptr) {
-        scope_->CaptureVariable(temp);
-      }
-    }
-    {
-      LocalVariable* temp =
-          scope_->LookupVariable(Symbols::ControllerStream(), true);
-      if (temp != nullptr) {
-        scope_->CaptureVariable(temp);
-      }
-    }
-  }
-
   // Mark known chained futures such as _Future::timeout()'s _future.
   if (function.recognized_kind() == MethodRecognizer::kFutureTimeout &&
       depth_.function_ == 1) {
@@ -1289,20 +1245,8 @@
     }
     case kYieldStatement: {
       helper_.ReadPosition();           // read position.
-      word flags = helper_.ReadByte();  // read flags.
+      helper_.ReadByte();               // read flags.
       VisitExpression();                // read expression.
-
-      if ((flags & kYieldStatementFlagNative) != 0) {
-        if (depth_.function_ == 0) {
-          AddSwitchVariable();
-          // Promote all currently visible local variables into the context.
-          // TODO(27590) CaptureLocalVariables promotes to many variables into
-          // the scope. Mark those variables as stack_local.
-          // TODO(27590) we don't need to promote those variables that are
-          // not used across yields.
-          scope_->CaptureLocalVariables(current_function_scope_);
-        }
-      }
       return;
     }
     case kVariableDeclaration:
@@ -1635,13 +1579,6 @@
     variable->set_is_explicit_covariant_parameter();
   }
 
-  // The :sync_op and :async_op continuations are called multiple times. So we
-  // don't want the parameters from the first invocation to get stored in the
-  // context and reused on later invocations with different parameters.
-  if (current_function_async_marker_ == FunctionNodeHelper::kSyncYielding) {
-    variable->set_is_forced_stack();
-  }
-
   const bool needs_covariant_check_in_method =
       helper.IsCovariant() ||
       (helper.IsGenericCovariantImpl() &&
@@ -1730,23 +1667,6 @@
     intptr_t nesting_depth) {
   LocalVariable* v = NULL;
 
-  // If we are inside a function with yield points then Kernel transformer
-  // could have lifted some of the auxiliary exception variables into the
-  // context to preserve them across yield points because they might
-  // be needed for rethrow.
-  // Check if it did and capture such variables instead of introducing
-  // new local ones.
-  // Note: function that wrap kSyncYielding function does not contain
-  // its own try/catches.
-  if (current_function_async_marker_ == FunctionNodeHelper::kSyncYielding) {
-    ASSERT(current_function_scope_->parent() != NULL);
-    v = current_function_scope_->parent()->LocalLookupVariable(
-        GenerateName(prefix, nesting_depth - 1));
-    if (v != NULL) {
-      scope_->CaptureVariable(v);
-    }
-  }
-
   // No need to create variables for try/catch-statements inside
   // nested functions.
   if (depth_.function_ > 0) return;
diff --git a/runtime/vm/compiler/graph_intrinsifier.cc b/runtime/vm/compiler/graph_intrinsifier.cc
index 148a752..9765e4e 100644
--- a/runtime/vm/compiler/graph_intrinsifier.cc
+++ b/runtime/vm/compiler/graph_intrinsifier.cc
@@ -673,8 +673,8 @@
   return BuildSimdOp(flow_graph, kFloat64x2Cid, Token::kADD);
 }
 
-static bool BuildFloat32x4Shuffle(FlowGraph* flow_graph,
-                                  MethodRecognizer::Kind kind) {
+static bool BuildFloat32x4Get(FlowGraph* flow_graph,
+                              MethodRecognizer::Kind kind) {
   if (!FlowGraphCompiler::SupportsUnboxedDoubles() ||
       !FlowGraphCompiler::SupportsUnboxedSimd128()) {
     return false;
@@ -702,24 +702,20 @@
   return true;
 }
 
-bool GraphIntrinsifier::Build_Float32x4ShuffleX(FlowGraph* flow_graph) {
-  return BuildFloat32x4Shuffle(flow_graph,
-                               MethodRecognizer::kFloat32x4ShuffleX);
+bool GraphIntrinsifier::Build_Float32x4GetX(FlowGraph* flow_graph) {
+  return BuildFloat32x4Get(flow_graph, MethodRecognizer::kFloat32x4GetX);
 }
 
-bool GraphIntrinsifier::Build_Float32x4ShuffleY(FlowGraph* flow_graph) {
-  return BuildFloat32x4Shuffle(flow_graph,
-                               MethodRecognizer::kFloat32x4ShuffleY);
+bool GraphIntrinsifier::Build_Float32x4GetY(FlowGraph* flow_graph) {
+  return BuildFloat32x4Get(flow_graph, MethodRecognizer::kFloat32x4GetY);
 }
 
-bool GraphIntrinsifier::Build_Float32x4ShuffleZ(FlowGraph* flow_graph) {
-  return BuildFloat32x4Shuffle(flow_graph,
-                               MethodRecognizer::kFloat32x4ShuffleZ);
+bool GraphIntrinsifier::Build_Float32x4GetZ(FlowGraph* flow_graph) {
+  return BuildFloat32x4Get(flow_graph, MethodRecognizer::kFloat32x4GetZ);
 }
 
-bool GraphIntrinsifier::Build_Float32x4ShuffleW(FlowGraph* flow_graph) {
-  return BuildFloat32x4Shuffle(flow_graph,
-                               MethodRecognizer::kFloat32x4ShuffleW);
+bool GraphIntrinsifier::Build_Float32x4GetW(FlowGraph* flow_graph) {
+  return BuildFloat32x4Get(flow_graph, MethodRecognizer::kFloat32x4GetW);
 }
 
 static bool BuildLoadField(FlowGraph* flow_graph, const Slot& field) {
diff --git a/runtime/vm/compiler/recognized_methods_list.h b/runtime/vm/compiler/recognized_methods_list.h
index 010e653..726488f 100644
--- a/runtime/vm/compiler/recognized_methods_list.h
+++ b/runtime/vm/compiler/recognized_methods_list.h
@@ -386,10 +386,10 @@
   V(_Float64x2List, []=, Float64x2ArraySetIndexed, 0x8af8aa58)                 \
   V(_TypedListBase, get:length, TypedListBaseLength, 0x5850f06b)               \
   V(_ByteDataView, get:length, ByteDataViewLength, 0x5850f06b)                 \
-  V(_Float32x4, get:x, Float32x4ShuffleX, 0x3a398530)                          \
-  V(_Float32x4, get:y, Float32x4ShuffleY, 0x27cae053)                          \
-  V(_Float32x4, get:z, Float32x4ShuffleZ, 0x5d964be9)                          \
-  V(_Float32x4, get:w, Float32x4ShuffleW, 0x3fd6906b)                          \
+  V(_Float32x4, get:x, Float32x4GetX, 0x3a398530)                              \
+  V(_Float32x4, get:y, Float32x4GetY, 0x27cae053)                              \
+  V(_Float32x4, get:z, Float32x4GetZ, 0x5d964be9)                              \
+  V(_Float32x4, get:w, Float32x4GetW, 0x3fd6906b)                              \
   V(_Float32x4, *, Float32x4Mul, 0xe5507c87)                                   \
   V(_Float32x4, /, Float32x4Div, 0xc09f2f62)                                   \
   V(_Float32x4, -, Float32x4Sub, 0xdd326c4a)                                   \
diff --git a/runtime/vm/kernel_binary.h b/runtime/vm/kernel_binary.h
index 3801578..b0658b3 100644
--- a/runtime/vm/kernel_binary.h
+++ b/runtime/vm/kernel_binary.h
@@ -20,8 +20,8 @@
 static const uint32_t kMagicProgramFile = 0x90ABCDEFu;
 
 // Both version numbers are inclusive.
-static const uint32_t kMinSupportedKernelFormatVersion = 82;
-static const uint32_t kMaxSupportedKernelFormatVersion = 82;
+static const uint32_t kMinSupportedKernelFormatVersion = 83;
+static const uint32_t kMaxSupportedKernelFormatVersion = 83;
 
 // Keep in sync with package:kernel/lib/binary/tag.dart
 #define KERNEL_TAG_LIST(V)                                                     \
@@ -218,7 +218,6 @@
 // Keep in sync with package:kernel/lib/ast.dart
 enum YieldStatementFlags {
   kYieldStatementFlagYieldStar = 1 << 0,
-  kYieldStatementFlagNative = 1 << 1,
 };
 
 // Keep in sync with package:kernel/lib/ast.dart
diff --git a/sdk/lib/_internal/js_dev_runtime/patch/developer_patch.dart b/sdk/lib/_internal/js_dev_runtime/patch/developer_patch.dart
index c451d7f..729dc22 100644
--- a/sdk/lib/_internal/js_dev_runtime/patch/developer_patch.dart
+++ b/sdk/lib/_internal/js_dev_runtime/patch/developer_patch.dart
@@ -213,9 +213,9 @@
       return existingTag;
     }
     // Throw an exception if we've reached the maximum number of user tags.
-    if (_instances.length == UserTag.MAX_USER_TAGS) {
+    if (_instances.length == UserTag.maxUserTags) {
       throw UnsupportedError(
-          'UserTag instance limit (${UserTag.MAX_USER_TAGS}) reached.');
+          'UserTag instance limit (${UserTag.maxUserTags}) reached.');
     }
     return _instances[label] = _FakeUserTag.real(label);
   }
diff --git a/sdk/lib/_internal/js_runtime/lib/developer_patch.dart b/sdk/lib/_internal/js_runtime/lib/developer_patch.dart
index 54f7ca1..05db6d6 100644
--- a/sdk/lib/_internal/js_runtime/lib/developer_patch.dart
+++ b/sdk/lib/_internal/js_runtime/lib/developer_patch.dart
@@ -136,9 +136,9 @@
       return existingTag;
     }
     // Throw an exception if we've reached the maximum number of user tags.
-    if (_instances.length == UserTag.MAX_USER_TAGS) {
+    if (_instances.length == UserTag.maxUserTags) {
       throw UnsupportedError(
-          'UserTag instance limit (${UserTag.MAX_USER_TAGS}) reached.');
+          'UserTag instance limit (${UserTag.maxUserTags}) reached.');
     }
     return _instances[label] = _FakeUserTag.real(label);
   }
diff --git a/sdk/lib/developer/profiler.dart b/sdk/lib/developer/profiler.dart
index 54bfd25..bf6ae6b 100644
--- a/sdk/lib/developer/profiler.dart
+++ b/sdk/lib/developer/profiler.dart
@@ -8,7 +8,12 @@
 /// [DevTools CPU profiler](https://flutter.dev/docs/development/tools/devtools/cpu-profiler).
 abstract class UserTag {
   /// The maximum number of UserTag instances that can be created by a program.
-  static const MAX_USER_TAGS = 64;
+  static const maxUserTags = 64;
+
+  @deprecated
+  // TODO: We shouldn't be using SCREAMING_CAPS for constants, so this should
+  // be removed for Dart 3.0.
+  static const MAX_USER_TAGS = maxUserTags;
 
   external factory UserTag(String label);
 
@@ -112,7 +117,7 @@
   /// The current set of registered [Metric]s.
   static UnmodifiableMapView<String, Metric> get current =>
       UnmodifiableMapView<String, Metric>(_metrics);
-  static final Map<String, Metric> _metrics = new Map<String, Metric>();
+  static final _metrics = <String, Metric>{};
 
   /// Register [Metric]s to make them visible to Observatory.
   static void register(Metric metric) {
diff --git a/tests/lib/async/syncstar_mixed_iterators_test.dart b/tests/lib/async/syncstar_mixed_iterators_test.dart
index 2b2ecf1..338c52b 100644
--- a/tests/lib/async/syncstar_mixed_iterators_test.dart
+++ b/tests/lib/async/syncstar_mixed_iterators_test.dart
@@ -3,13 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 //
 // Test that sync* correctly iterates through nested iterators of both
-// the internal sync* transform _SyncIterable and generic Iterable types.
+// the internal sync* iterable and generic Iterable types.
 
 import "package:expect/expect.dart";
 
 Iterable<int> outerSyncStar() sync* {
   yield 1;
-  // _SyncIterable<int>:
+  // internal sync* iterable:
   yield* innerSyncStar();
   yield 4;
   // Generic Iterable<int>:
diff --git a/tools/VERSION b/tools/VERSION
index 2c1d3fb..1ea2e37 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 18
 PATCH 0
-PRERELEASE 274
+PRERELEASE 275
 PRERELEASE_PATCH 0
\ No newline at end of file