Remove redundant fields from LocalsHandler

Change-Id: I6ba0db5996e0a6e1b9799cba01a1c5b6f9d81a42
Reviewed-on: https://dart-review.googlesource.com/c/84427
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/compiler/lib/src/inferrer/builder_kernel.dart b/pkg/compiler/lib/src/inferrer/builder_kernel.dart
index e2fd017..cbb7f3a 100644
--- a/pkg/compiler/lib/src/inferrer/builder_kernel.dart
+++ b/pkg/compiler/lib/src/inferrer/builder_kernel.dart
@@ -82,9 +82,8 @@
     if (_locals != null) return;
 
     FieldInitializationScope fieldScope =
-        _inGenerativeConstructor ? new FieldInitializationScope(_types) : null;
-    _locals = new LocalsHandler(
-        _inferrer, _types, _options, _analyzedNode, fieldScope);
+        _inGenerativeConstructor ? new FieldInitializationScope() : null;
+    _locals = new LocalsHandler(_analyzedNode, fieldScope);
   }
 
   JsToElementMap get _elementMap => _closedWorld.elementMap;
@@ -226,7 +225,8 @@
   void handleParameter(ir.VariableDeclaration node, {bool isOptional}) {
     Local local = _localsMap.getLocalVariable(node);
     DartType type = _localsMap.getLocalType(_elementMap, local);
-    _locals.update(local, _inferrer.typeOfParameter(local), node, type);
+    _locals.update(
+        _inferrer, local, _inferrer.typeOfParameter(local), node, type);
     if (isOptional) {
       TypeInformation type;
       if (node.initializer != null) {
@@ -460,7 +460,7 @@
     if (simpleCondition) _updateIsChecks(negativeTests, positiveTests);
     visit(node.message);
     _locals.seenReturnOrThrow = true;
-    saved.mergeDiamondFlow(thenLocals, _locals);
+    saved.mergeDiamondFlow(_inferrer, thenLocals, _locals);
     _locals = saved;
     return null;
   }
@@ -489,7 +489,7 @@
       JumpTarget jumpTarget = _localsMap.getJumpTargetForLabel(node);
       _setupBreaksAndContinues(jumpTarget);
       visit(body);
-      _locals.mergeAfterBreaks(_getBreaks(jumpTarget));
+      _locals.mergeAfterBreaks(_inferrer, _getBreaks(jumpTarget));
       _clearBreaksAndContinues(jumpTarget);
     }
     return null;
@@ -517,18 +517,18 @@
       // visit all cases and update [locals] until we have reached a
       // fixed point.
       bool changed;
-      _locals.startLoop(node);
+      _locals.startLoop(_inferrer, node);
       do {
         changed = false;
         for (ir.SwitchCase switchCase in node.cases) {
           LocalsHandler saved = _locals;
           _locals = new LocalsHandler.from(_locals, switchCase);
           visit(switchCase);
-          changed = saved.mergeAll([_locals]) || changed;
+          changed = saved.mergeAll(_inferrer, [_locals]) || changed;
           _locals = saved;
         }
       } while (changed);
-      _locals.endLoop(node);
+      _locals.endLoop(_inferrer, node);
 
       continueTargets.forEach(_clearBreaksAndContinues);
     } else {
@@ -544,7 +544,8 @@
         visit(switchCase);
         localsToMerge.add(_locals);
       }
-      saved.mergeAfterBreaks(localsToMerge, keepOwnLocals: !hasDefaultCase);
+      saved.mergeAfterBreaks(_inferrer, localsToMerge,
+          keepOwnLocals: !hasDefaultCase);
       _locals = saved;
     }
     _clearBreaksAndContinues(jumpTarget);
@@ -682,9 +683,9 @@
     Local local = _localsMap.getLocalVariable(node);
     DartType type = _localsMap.getLocalType(_elementMap, local);
     if (node.initializer == null) {
-      _locals.update(local, _types.nullType, node, type);
+      _locals.update(_inferrer, local, _types.nullType, node, type);
     } else {
-      _locals.update(local, visit(node.initializer), node, type);
+      _locals.update(_inferrer, local, visit(node.initializer), node, type);
     }
     if (node.initializer is ir.ThisExpression) {
       _markThisAsExposed();
@@ -695,7 +696,7 @@
   @override
   TypeInformation visitVariableGet(ir.VariableGet node) {
     Local local = _localsMap.getLocalVariable(node.variable);
-    TypeInformation type = _locals.use(local);
+    TypeInformation type = _locals.use(_inferrer, local);
     assert(type != null, "Missing type information for $local.");
     return type;
   }
@@ -708,7 +709,7 @@
     }
     Local local = _localsMap.getLocalVariable(node.variable);
     DartType type = _localsMap.getLocalType(_elementMap, local);
-    _locals.update(local, rhsType, node, type);
+    _locals.update(_inferrer, local, rhsType, node, type);
     return rhsType;
   }
 
@@ -820,7 +821,7 @@
         TypeInformation refinedType = _types
             .refineReceiver(selector, mask, receiverType, isConditional: false);
         DartType type = _localsMap.getLocalType(_elementMap, local);
-        _locals.update(local, refinedType, node, type);
+        _locals.update(_inferrer, local, refinedType, node, type);
         List<Refinement> refinements = _localRefinementMap[variable];
         if (refinements != null) {
           refinements.add(new Refinement(selector, mask));
@@ -903,12 +904,12 @@
       if (refinements.isNotEmpty) {
         Local local = _localsMap.getLocalVariable(alias);
         DartType type = _localsMap.getLocalType(_elementMap, local);
-        TypeInformation localType = _locals.use(local);
+        TypeInformation localType = _locals.use(_inferrer, local);
         for (Refinement refinement in refinements) {
           localType = _types.refineReceiver(
               refinement.selector, refinement.mask, localType,
               isConditional: true);
-          _locals.update(local, localType, node, type);
+          _locals.update(_inferrer, local, localType, node, type);
         }
       }
     }
@@ -961,7 +962,8 @@
 
     Local variable = _localsMap.getLocalVariable(node.variable);
     DartType variableType = _localsMap.getLocalType(_elementMap, variable);
-    _locals.update(variable, currentType, node.variable, variableType);
+    _locals.update(
+        _inferrer, variable, currentType, node.variable, variableType);
 
     JumpTarget target = _localsMap.getJumpTargetForForIn(node);
     return handleLoop(node, target, () {
@@ -1002,19 +1004,20 @@
     _loopLevel++;
     bool changed = false;
     LocalsHandler saved = _locals;
-    saved.startLoop(node);
+    saved.startLoop(_inferrer, node);
     do {
       // Setup (and clear in case of multiple iterations of the loop)
       // the lists of breaks and continues seen in the loop.
       _setupBreaksAndContinues(target);
       _locals = new LocalsHandler.from(saved, node);
       logic();
-      changed = saved.mergeAll(_getLoopBackEdges(target));
+      changed = saved.mergeAll(_inferrer, _getLoopBackEdges(target));
     } while (changed);
     _loopLevel--;
-    saved.endLoop(node);
+    saved.endLoop(_inferrer, node);
     bool keepOwnLocals = node is! ir.DoStatement;
-    saved.mergeAfterBreaks(_getBreaks(target), keepOwnLocals: keepOwnLocals);
+    saved.mergeAfterBreaks(_inferrer, _getBreaks(target),
+        keepOwnLocals: keepOwnLocals);
     _locals = saved;
     _clearBreaksAndContinues(target);
     return null;
@@ -1342,18 +1345,19 @@
       List<IsCheck> positiveTests, List<IsCheck> negativeTests) {
     for (IsCheck check in positiveTests) {
       if (check.type != null) {
-        _locals.narrow(check.local, check.type, check.node);
+        _locals.narrow(_inferrer, check.local, check.type, check.node);
       } else {
         DartType localType = _localsMap.getLocalType(_elementMap, check.local);
-        _locals.update(check.local, _types.nullType, check.node, localType);
+        _locals.update(
+            _inferrer, check.local, _types.nullType, check.node, localType);
       }
     }
     for (IsCheck check in negativeTests) {
       if (check.type != null) {
         // TODO(johnniwinther): Use negative type knowledge.
       } else {
-        _locals.narrow(
-            check.local, _closedWorld.commonElements.objectType, check.node);
+        _locals.narrow(_inferrer, check.local,
+            _closedWorld.commonElements.objectType, check.node);
       }
     }
   }
@@ -1374,7 +1378,7 @@
       _updateIsChecks(negativeTests, positiveTests);
     }
     visit(node.otherwise);
-    saved.mergeDiamondFlow(thenLocals, _locals);
+    saved.mergeDiamondFlow(_inferrer, thenLocals, _locals);
     _locals = saved;
     return null;
   }
@@ -1431,7 +1435,7 @@
         _positiveIsChecks.removeWhere(invalidatedInRightHandSide);
         _negativeIsChecks.removeWhere(invalidatedInRightHandSide);
       }
-      saved.mergeDiamondFlow(_locals, null);
+      saved.mergeDiamondFlow(_inferrer, _locals, null);
       _locals = saved;
       return _types.boolType;
     } else if (node.operator == '||') {
@@ -1446,7 +1450,7 @@
         _updateIsChecks(negativeIsChecks, positiveIsChecks);
       }
       visit(node.right, conditionContext: false);
-      saved.mergeDiamondFlow(_locals, null);
+      saved.mergeDiamondFlow(_inferrer, _locals, null);
       _locals = saved;
       return _types.boolType;
     }
@@ -1469,7 +1473,7 @@
     _locals = new LocalsHandler.from(saved, node);
     if (simpleCondition) _updateIsChecks(negativeTests, positiveTests);
     TypeInformation secondType = visit(node.otherwise);
-    saved.mergeDiamondFlow(thenLocals, _locals);
+    saved.mergeDiamondFlow(_inferrer, thenLocals, _locals);
     _locals = saved;
     return _types.allocateDiamondPhi(firstType, secondType);
   }
@@ -1508,7 +1512,7 @@
     if (variable != null) {
       Local local = _localsMap.getLocalVariable(variable);
       DartType type = _localsMap.getLocalType(_elementMap, local);
-      _locals.update(local, localFunctionType, node, type);
+      _locals.update(_inferrer, local, localFunctionType, node, type);
     }
 
     // We don't put the closure in the work queue of the
@@ -1590,13 +1594,13 @@
         isTry: true, useOtherTryBlock: false);
     initializationIsIndefinite();
     visit(node.body);
-    saved.mergeDiamondFlow(_locals, null);
+    saved.mergeDiamondFlow(_inferrer, _locals, null);
     _locals = saved;
     for (ir.Catch catchBlock in node.catches) {
       saved = _locals;
       _locals = new LocalsHandler.from(_locals, catchBlock);
       visit(catchBlock);
-      saved.mergeDiamondFlow(_locals, null);
+      saved.mergeDiamondFlow(_inferrer, _locals, null);
       _locals = saved;
     }
     return null;
@@ -1609,7 +1613,7 @@
         isTry: true, useOtherTryBlock: false);
     initializationIsIndefinite();
     visit(node.body);
-    saved.mergeDiamondFlow(_locals, null);
+    saved.mergeDiamondFlow(_inferrer, _locals, null);
     _locals = saved;
     visit(node.finalizer);
     return null;
@@ -1630,13 +1634,14 @@
         mask = _types.dynamicType;
       }
       Local local = _localsMap.getLocalVariable(exception);
-      _locals.update(local, mask, node, const DynamicType());
+      _locals.update(_inferrer, local, mask, node, const DynamicType());
     }
     ir.VariableDeclaration stackTrace = node.stackTrace;
     if (stackTrace != null) {
       Local local = _localsMap.getLocalVariable(stackTrace);
       // TODO(johnniwinther): Use a mask based on [StackTrace].
-      _locals.update(local, _types.dynamicType, node, const DynamicType());
+      _locals.update(
+          _inferrer, local, _types.dynamicType, node, const DynamicType());
     }
     visit(node.body);
     return null;
diff --git a/pkg/compiler/lib/src/inferrer/locals_handler.dart b/pkg/compiler/lib/src/inferrer/locals_handler.dart
index ae92797..b0b1fd8 100644
--- a/pkg/compiler/lib/src/inferrer/locals_handler.dart
+++ b/pkg/compiler/lib/src/inferrer/locals_handler.dart
@@ -6,13 +6,11 @@
 
 import 'dart:collection' show IterableMixin;
 import 'package:kernel/ast.dart' as ir;
-import '../options.dart' show CompilerOptions;
 import '../elements/entities.dart';
 import '../elements/types.dart';
 import '../util/util.dart';
 import 'inferrer_engine.dart';
 import 'type_graph_nodes.dart';
-import 'type_system.dart';
 
 /**
  * A variable scope holds types for variables. It has a link to a
@@ -111,7 +109,6 @@
 
 /// Tracks initializers via initializations and assignments.
 class FieldInitializationScope {
-  final TypeSystem types;
   Map<FieldEntity, TypeInformation> fields;
   bool isThisExposed;
 
@@ -119,13 +116,12 @@
   /// e.g. an early return or caught exception.
   bool isIndefinite;
 
-  FieldInitializationScope(this.types)
+  FieldInitializationScope()
       : isThisExposed = false,
         isIndefinite = false;
 
   FieldInitializationScope.internalFrom(FieldInitializationScope other)
-      : types = other.types,
-        isThisExposed = other.isThisExposed,
+      : isThisExposed = other.isThisExposed,
         isIndefinite = other.isIndefinite;
 
   factory FieldInitializationScope.from(FieldInitializationScope other) {
@@ -148,7 +144,7 @@
     fields?.forEach(f);
   }
 
-  void mergeDiamondFlow(
+  void mergeDiamondFlow(InferrerEngine inferrer,
       FieldInitializationScope thenScope, FieldInitializationScope elseScope) {
     // Quick bailout check. If [isThisExposed] or [isIndefinite] is true, we
     // know the code following won'TypeInformation do anything.
@@ -161,7 +157,7 @@
     thenScope.forEach((FieldEntity field, TypeInformation type) {
       TypeInformation otherType = otherScope.readField(field);
       if (otherType == null) return;
-      updateField(field, types.allocateDiamondPhi(type, otherType));
+      updateField(field, inferrer.types.allocateDiamondPhi(type, otherType));
     });
 
     isThisExposed = thenScope.isThisExposed || elseScope.isThisExposed;
@@ -249,9 +245,6 @@
  * Placeholder for inferred types of local variables.
  */
 class LocalsHandler {
-  final CompilerOptions options;
-  final TypeSystem types;
-  final InferrerEngine inferrer;
   final VariableScope locals;
   final Map<Local, FieldEntity> _capturedAndBoxed;
   final FieldInitializationScope fieldScope;
@@ -265,8 +258,10 @@
 
   bool get inTryBlock => tryBlock != null;
 
-  LocalsHandler(this.inferrer, this.types, this.options, ir.Node block,
-      [this.fieldScope])
+  LocalsHandler.internal(ir.Node block, this.fieldScope, this.locals,
+      this._capturedAndBoxed, this.tryBlock);
+
+  LocalsHandler(ir.Node block, [this.fieldScope])
       : locals = new VariableScope(block, isTry: false),
         _capturedAndBoxed = new Map<Local, FieldEntity>(),
         tryBlock = null;
@@ -275,10 +270,7 @@
       {bool isTry: false, bool useOtherTryBlock: true})
       : locals = new VariableScope(block, isTry: isTry, parent: other.locals),
         fieldScope = new FieldInitializationScope.from(other.fieldScope),
-        _capturedAndBoxed = other._capturedAndBoxed,
-        types = other.types,
-        inferrer = other.inferrer,
-        options = other.options {
+        _capturedAndBoxed = other._capturedAndBoxed {
     tryBlock = useOtherTryBlock ? other.tryBlock : this;
   }
 
@@ -286,21 +278,15 @@
       : locals = new VariableScope.deepCopyOf(other.locals),
         fieldScope = new FieldInitializationScope.from(other.fieldScope),
         _capturedAndBoxed = other._capturedAndBoxed,
-        tryBlock = other.tryBlock,
-        types = other.types,
-        inferrer = other.inferrer,
-        options = other.options;
+        tryBlock = other.tryBlock;
 
   LocalsHandler.topLevelCopyOf(LocalsHandler other)
       : locals = new VariableScope.topLevelCopyOf(other.locals),
         fieldScope = new FieldInitializationScope.from(other.fieldScope),
         _capturedAndBoxed = other._capturedAndBoxed,
-        tryBlock = other.tryBlock,
-        types = other.types,
-        inferrer = other.inferrer,
-        options = other.options;
+        tryBlock = other.tryBlock;
 
-  TypeInformation use(Local local) {
+  TypeInformation use(InferrerEngine inferrer, Local local) {
     if (_capturedAndBoxed.containsKey(local)) {
       FieldEntity field = _capturedAndBoxed[local];
       return inferrer.typeOfMember(field);
@@ -309,12 +295,12 @@
     }
   }
 
-  void update(
-      Local local, TypeInformation type, ir.Node node, DartType staticType,
+  void update(InferrerEngine inferrer, Local local, TypeInformation type,
+      ir.Node node, DartType staticType,
       {bool isSetIfNull: false}) {
     assert(type != null);
-    if (!options.assignmentCheckPolicy.isIgnored) {
-      type = types.narrowType(type, staticType);
+    if (!inferrer.options.assignmentCheckPolicy.isIgnored) {
+      type = inferrer.types.narrowType(type, staticType);
     }
     updateLocal() {
       TypeInformation currentType = locals[local];
@@ -322,10 +308,10 @@
       if (isSetIfNull && currentType != null) {
         // If-null assignments may return either the new or the original value
         // narrowed to non-null.
-        type = types.addPhiInput(
+        type = inferrer.types.addPhiInput(
             local,
-            types.allocatePhi(
-                locals.block, local, types.narrowNotNull(currentType),
+            inferrer.types.allocatePhi(
+                locals.block, local, inferrer.types.narrowNotNull(currentType),
                 isTry: locals.isTry),
             type);
       }
@@ -342,10 +328,11 @@
       // the right phi for it.
       TypeInformation existing = tryBlock.locals.parent[local];
       if (existing != null) {
-        TypeInformation phiType = types.allocatePhi(
+        TypeInformation phiType = inferrer.types.allocatePhi(
             tryBlock.locals.block, local, existing,
             isTry: tryBlock.locals.isTry);
-        TypeInformation inputType = types.addPhiInput(local, phiType, type);
+        TypeInformation inputType =
+            inferrer.types.addPhiInput(local, phiType, type);
         tryBlock.locals.parent[local] = inputType;
       }
       // Update the current handler unconditionally with the new
@@ -356,21 +343,23 @@
     }
   }
 
-  void narrow(Local local, DartType type, ir.Node node,
+  void narrow(InferrerEngine inferrer, Local local, DartType type, ir.Node node,
       {bool isSetIfNull: false}) {
-    TypeInformation existing = use(local);
+    TypeInformation existing = use(inferrer, local);
     TypeInformation newType =
-        types.narrowType(existing, type, isNullable: false);
-    update(local, newType, node, type, isSetIfNull: isSetIfNull);
+        inferrer.types.narrowType(existing, type, isNullable: false);
+    update(inferrer, local, newType, node, type, isSetIfNull: isSetIfNull);
   }
 
   void setCapturedAndBoxed(Local local, FieldEntity field) {
     _capturedAndBoxed[local] = field;
   }
 
-  void mergeDiamondFlow(LocalsHandler thenBranch, LocalsHandler elseBranch) {
+  void mergeDiamondFlow(InferrerEngine inferrer, LocalsHandler thenBranch,
+      LocalsHandler elseBranch) {
     if (fieldScope != null && elseBranch != null) {
-      fieldScope.mergeDiamondFlow(thenBranch.fieldScope, elseBranch.fieldScope);
+      fieldScope.mergeDiamondFlow(
+          inferrer, thenBranch.fieldScope, elseBranch.fieldScope);
     }
     seenReturnOrThrow = thenBranch.seenReturnOrThrow &&
         elseBranch != null &&
@@ -385,7 +374,7 @@
         TypeInformation myType = locals[local];
         if (myType == null) return; // Variable is only defined in [other].
         if (type == myType) return;
-        locals[local] = types.allocateDiamondPhi(myType, type);
+        locals[local] = inferrer.types.allocateDiamondPhi(myType, type);
       });
     }
 
@@ -414,7 +403,7 @@
         if (thenType == elseType) {
           locals[local] = thenType;
         } else {
-          locals[local] = types.allocateDiamondPhi(thenType, elseType);
+          locals[local] = inferrer.types.allocateDiamondPhi(thenType, elseType);
         }
       }
 
@@ -459,7 +448,7 @@
    * where [:this:] is the [LocalsHandler] for the paths through the
    * labeled statement that do not break out.
    */
-  void mergeAfterBreaks(List<LocalsHandler> handlers,
+  void mergeAfterBreaks(InferrerEngine inferrer, List<LocalsHandler> handlers,
       {bool keepOwnLocals: true}) {
     ir.Node level = locals.block;
     // Use a separate locals handler to perform the merge in, so that Phi
@@ -472,7 +461,7 @@
     // Merge all other handlers.
     for (LocalsHandler handler in handlers) {
       allBranchesAbort = allBranchesAbort && handler.seenReturnOrThrow;
-      merged.mergeHandler(handler, seenLocals);
+      merged.mergeHandler(inferrer, handler, seenLocals);
     }
     // If we want to keep own locals, we merge [seenLocals] from [this] into
     // [merged] to update the Phi nodes with original values.
@@ -480,15 +469,15 @@
       for (Local variable in seenLocals) {
         TypeInformation originalType = locals[variable];
         if (originalType != null) {
-          merged.locals[variable] = types.addPhiInput(
-              variable, merged.locals[variable], originalType);
+          merged.locals[variable] = inferrer.types
+              .addPhiInput(variable, merged.locals[variable], originalType);
         }
       }
     }
     // Clean up Phi nodes with single input and store back result into
     // actual locals handler.
     merged.locals.forEachOwnLocal((Local variable, TypeInformation type) {
-      locals[variable] = types.simplifyPhi(level, variable, type);
+      locals[variable] = inferrer.types.simplifyPhi(level, variable, type);
     });
     seenReturnOrThrow =
         allBranchesAbort && (!keepOwnLocals || seenReturnOrThrow);
@@ -500,7 +489,8 @@
    * unless the local is already present in the set [seen]. This effectively
    * overwrites the current type knowledge in this handler.
    */
-  bool mergeHandler(LocalsHandler other, [Set<Local> seen]) {
+  bool mergeHandler(InferrerEngine inferrer, LocalsHandler other,
+      [Set<Local> seen]) {
     if (other.seenReturnOrThrow) return false;
     bool changed = false;
     other.locals.forEachLocalUntilNode(locals.block, (local, otherType) {
@@ -508,11 +498,11 @@
       if (myType == null) return;
       TypeInformation newType;
       if (seen != null && !seen.contains(local)) {
-        newType = types.allocatePhi(locals.block, local, otherType,
-            isTry: locals.isTry);
+        newType = inferrer.types
+            .allocatePhi(locals.block, local, otherType, isTry: locals.isTry);
         seen.add(local);
       } else {
-        newType = types.addPhiInput(local, myType, otherType);
+        newType = inferrer.types.addPhiInput(local, myType, otherType);
       }
       if (newType != myType) {
         changed = true;
@@ -526,28 +516,29 @@
    * Merge all [LocalsHandler] in [handlers] into this handler.
    * Returns whether a local in this handler has changed.
    */
-  bool mergeAll(List<LocalsHandler> handlers) {
+  bool mergeAll(InferrerEngine inferrer, List<LocalsHandler> handlers) {
     bool changed = false;
     assert(!seenReturnOrThrow);
     handlers.forEach((other) {
-      changed = mergeHandler(other) || changed;
+      changed = mergeHandler(inferrer, other) || changed;
     });
     return changed;
   }
 
-  void startLoop(ir.Node loop) {
+  void startLoop(InferrerEngine inferrer, ir.Node loop) {
     locals.forEachLocal((Local variable, TypeInformation type) {
       TypeInformation newType =
-          types.allocateLoopPhi(loop, variable, type, isTry: false);
+          inferrer.types.allocateLoopPhi(loop, variable, type, isTry: false);
       if (newType != type) {
         locals[variable] = newType;
       }
     });
   }
 
-  void endLoop(ir.Node loop) {
+  void endLoop(InferrerEngine inferrer, ir.Node loop) {
     locals.forEachLocal((Local variable, TypeInformation type) {
-      TypeInformation newType = types.simplifyPhi(loop, variable, type);
+      TypeInformation newType =
+          inferrer.types.simplifyPhi(loop, variable, type);
       if (newType != type) {
         locals[variable] = newType;
       }