[dart2js] Migrate locals_handler type_graph_node and type_system to nnbd

Change-Id: I77f66764943bcdf204744da6bcc1932381895c5d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/249964
Commit-Queue: Nate Biggs <natebiggs@google.com>
Reviewed-by: Joshua Litt <joshualitt@google.com>
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/test/analyses/dart2js_allowed.json b/pkg/compiler/test/analyses/dart2js_allowed.json
index 107ef15..746d0ef61 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