diff --git a/pkg/compiler/lib/src/inferrer/builder_kernel.dart b/pkg/compiler/lib/src/inferrer/builder_kernel.dart index 3009a68bf1c..8a7dfdd5194 100644 --- a/pkg/compiler/lib/src/inferrer/builder_kernel.dart +++ b/pkg/compiler/lib/src/inferrer/builder_kernel.dart @@ -48,13 +48,12 @@ class KernelTypeGraphBuilder extends ir.Visitor { final GlobalTypeInferenceElementData _memberData; final bool _inGenerativeConstructor; - LocalsHandler _locals; - FieldInitializationScope _fieldScope; + LocalState _state; final SideEffectsBuilder _sideEffectsBuilder; - final Map> _breaksFor = - >{}; - final Map> _continuesFor = - >{}; + final Map> _breaksFor = + >{}; + final Map> _continuesFor = + >{}; TypeInformation _returnType; final Set _capturedVariables = new Set(); final Map _capturedAndBoxed; @@ -71,9 +70,7 @@ class KernelTypeGraphBuilder extends ir.Visitor { KernelTypeGraphBuilder(this._options, this._closedWorld, this._inferrer, this._analyzedMember, this._analyzedNode, this._localsMap, - [this._locals, - this._fieldScope, - Map capturedAndBoxed]) + [this._state, Map capturedAndBoxed]) : this._types = _inferrer.types, this._memberData = _inferrer.dataOfMember(_analyzedMember), // TODO(johnniwinther): Should side effects also be tracked for field @@ -86,11 +83,10 @@ class KernelTypeGraphBuilder extends ir.Visitor { this._capturedAndBoxed = capturedAndBoxed != null ? new Map.from(capturedAndBoxed) : {} { - if (_locals != null) return; + if (_state != null) return; - _fieldScope = - _inGenerativeConstructor ? new FieldInitializationScope() : null; - _locals = new LocalsHandler(_analyzedNode); + _state = new LocalState.initial(_analyzedNode, + inGenerativeConstructor: _inGenerativeConstructor); } JsToElementMap get _elementMap => _closedWorld.elementMap; @@ -101,16 +97,6 @@ class KernelTypeGraphBuilder extends ir.Visitor { bool get inLoop => _loopLevel > 0; - bool get _isThisExposed { - return _inGenerativeConstructor ? _fieldScope.isThisExposed : true; - } - - void _markThisAsExposed() { - if (_inGenerativeConstructor) { - _fieldScope.isThisExposed = true; - } - } - /// Returns `true` if [member] is defined in a subclass of the current this /// type. bool _isInClassOrSubclass(MemberEntity member) { @@ -129,14 +115,14 @@ class KernelTypeGraphBuilder extends ir.Visitor { /// field is considered to have been read before initialization and the field /// is assumed to be potentially `null`. void _checkIfExposesThis(Selector selector, AbstractValue mask) { - if (_isThisExposed) { + if (_state.isThisExposed) { // We already consider `this` to have been exposed. return; } if (_inferrer.closedWorld.includesClosureCall(selector, mask)) { // TODO(ngeoffray): We could do better here if we knew what we // are calling does not expose this. - _markThisAsExposed(); + _state.markThisAsExposed(); } else { _inferrer.forEachElementMatching(selector, mask, (MemberEntity element) { if (element != null && element.isField) { @@ -144,7 +130,7 @@ class KernelTypeGraphBuilder extends ir.Visitor { if (!selector.isSetter && _isInClassOrSubclass(field) && field.isAssignable && - _fieldScope.readField(field) == null && + _state.readField(field) == null && getFieldInitializer(_elementMap, field) == null) { // If the field is being used before this constructor // actually had a chance to initialize it, say it can be @@ -156,7 +142,7 @@ class KernelTypeGraphBuilder extends ir.Visitor { } // TODO(ngeoffray): We could do better here if we knew what we // are calling does not expose this. - _markThisAsExposed(); + _state.markThisAsExposed(); return false; }); } @@ -199,12 +185,6 @@ class KernelTypeGraphBuilder extends ir.Visitor { _inferrer.addReturnTypeForMethod(analyzedMethod, _returnType, type); } - void initializationIsIndefinite() { - if (_inGenerativeConstructor) { - _fieldScope.isIndefinite = true; - } - } - TypeInformation _thisType; TypeInformation get thisType { if (_thisType != null) return _thisType; @@ -232,7 +212,7 @@ class KernelTypeGraphBuilder extends ir.Visitor { void handleParameter(ir.VariableDeclaration node, {bool isOptional}) { Local local = _localsMap.getLocalVariable(node); DartType type = _localsMap.getLocalType(_elementMap, local); - _locals.update(_inferrer, _capturedAndBoxed, local, + _state.updateLocal(_inferrer, _capturedAndBoxed, local, _inferrer.typeOfParameter(local), node, type); if (isOptional) { TypeInformation type; @@ -260,7 +240,7 @@ class KernelTypeGraphBuilder extends ir.Visitor { _elementMap.elementEnvironment.forEachLocalClassMember(cls, (MemberEntity member) { if (member.isField && member.isInstanceMember && member.isAssignable) { - TypeInformation type = _fieldScope.readField(member); + TypeInformation type = _state.readField(member); MemberDefinition definition = _elementMap.getMemberDefinition(member); assert(definition.kind == MemberKind.regular); ir.Field node = definition.node; @@ -272,7 +252,7 @@ class KernelTypeGraphBuilder extends ir.Visitor { } }); } - _inferrer.recordExposesThis(_analyzedMember, _isThisExposed); + _inferrer.recordExposesThis(_analyzedMember, _state.isThisExposed); if (cls.isAbstract) { if (_closedWorld.classHierarchy.isInstantiated(cls)) { @@ -294,7 +274,7 @@ class KernelTypeGraphBuilder extends ir.Visitor { visitFieldInitializer(ir.FieldInitializer node) { TypeInformation rhsType = visit(node.value); FieldEntity field = _elementMap.getField(node.field); - _fieldScope.updateField(field, rhsType); + _state.updateField(field, rhsType); _inferrer.recordTypeOfField(field, rhsType); return null; } @@ -311,7 +291,7 @@ class KernelTypeGraphBuilder extends ir.Visitor { _inferrer.analyze(constructor); if (_inferrer.checkIfExposesThis(constructor)) { - _markThisAsExposed(); + _state.markThisAsExposed(); } return null; } @@ -328,7 +308,7 @@ class KernelTypeGraphBuilder extends ir.Visitor { _inferrer.analyze(constructor); if (_inferrer.checkIfExposesThis(constructor)) { - _markThisAsExposed(); + _state.markThisAsExposed(); } return null; } @@ -366,10 +346,10 @@ class KernelTypeGraphBuilder extends ir.Visitor { case ir.AsyncMarker.Sync: if (_returnType == null) { // No return in the body. - _returnType = _locals.seenReturnOrThrow + _returnType = _state.seenReturnOrThrow ? _types.nonNullEmpty() // Body always throws. : _types.nullType; - } else if (!_locals.seenReturnOrThrow) { + } else if (!_state.seenReturnOrThrow) { // We haven'TypeInformation seen returns on all branches. So the // method may also return null. recordReturnType(_types.nullType); @@ -429,7 +409,7 @@ class KernelTypeGraphBuilder extends ir.Visitor { visitBlock(ir.Block block) { for (ir.Statement statement in block.statements) { visit(statement); - if (_locals.aborts) break; + if (_state.aborts) break; } return null; } @@ -458,39 +438,33 @@ class KernelTypeGraphBuilder extends ir.Visitor { List negativeTests = []; bool simpleCondition = handleCondition(node.condition, positiveTests, negativeTests); - LocalsHandler localsBefore = _locals; - FieldInitializationScope fieldScopeBefore = _fieldScope; + LocalState stateBefore = _state; - _locals = new LocalsHandler.from(localsBefore, node); + _state = new LocalState.childPath(stateBefore, node); _updateIsChecks(positiveTests, negativeTests); - LocalsHandler localsAfterCondition = _locals; - FieldInitializationScope fieldScopeAfterThen = _fieldScope; + LocalState stateAfterCondition = _state; - _locals = new LocalsHandler.from(localsBefore, node); - _fieldScope = new FieldInitializationScope.from(fieldScopeBefore); + _state = new LocalState.childPath(stateBefore, node); if (simpleCondition) _updateIsChecks(negativeTests, positiveTests); visit(node.message); - LocalsHandler localsAfterMessage = _locals; - FieldInitializationScope fieldScopeAfterMessage = _fieldScope; - localsAfterMessage.seenReturnOrThrow = true; - _locals = localsBefore.mergeDiamondFlow( - _inferrer, localsAfterCondition, localsAfterMessage); - _fieldScope = fieldScopeBefore?.mergeDiamondFlow( - _inferrer, fieldScopeAfterThen, fieldScopeAfterMessage); + LocalState stateAfterMessage = _state; + stateAfterMessage.seenReturnOrThrow = true; + _state = stateBefore.mergeDiamondFlow( + _inferrer, stateAfterCondition, stateAfterMessage); return null; } @override visitBreakStatement(ir.BreakStatement node) { JumpTarget target = _localsMap.getJumpTargetForBreak(node); - _locals.seenBreakOrContinue = true; + _state.seenBreakOrContinue = true; // Do a deep-copy of the locals, because the code following the // break will change them. if (_localsMap.generateContinueForBreak(node)) { - _continuesFor[target].add(new LocalsHandler.deepCopyOf(_locals)); + _continuesFor[target].add(new LocalState.deepCopyOf(_state)); } else { - _breaksFor[target].add(new LocalsHandler.deepCopyOf(_locals)); + _breaksFor[target].add(new LocalState.deepCopyOf(_state)); } return null; } @@ -505,7 +479,7 @@ class KernelTypeGraphBuilder extends ir.Visitor { JumpTarget jumpTarget = _localsMap.getJumpTargetForLabel(node); _setupBreaksAndContinues(jumpTarget); visit(body); - _locals.mergeAfterBreaks(_inferrer, _getBreaks(jumpTarget)); + _state.mergeAfterBreaks(_inferrer, _getBreaks(jumpTarget)); _clearBreaksAndContinues(jumpTarget); } return null; @@ -533,43 +507,38 @@ class KernelTypeGraphBuilder extends ir.Visitor { // visit all cases and update [locals] until we have reached a // fixed point. bool changed; - _locals.startLoop(_inferrer, node); + _state.startLoop(_inferrer, node); do { changed = false; for (ir.SwitchCase switchCase in node.cases) { - LocalsHandler localsBeforeCase = _locals; - FieldInitializationScope fieldScopeBeforeCase = _fieldScope; - _locals = new LocalsHandler.from(localsBeforeCase, switchCase); - _fieldScope = new FieldInitializationScope.from(fieldScopeBeforeCase); + LocalState stateBeforeCase = _state; + _state = new LocalState.childPath(stateBeforeCase, switchCase); visit(switchCase); - LocalsHandler localsAfterCase = _locals; - changed = localsBeforeCase.mergeAll(_inferrer, [localsAfterCase]) || - changed; - _locals = localsBeforeCase; - _fieldScope = fieldScopeBeforeCase; + LocalState stateAfterCase = _state; + changed = + stateBeforeCase.mergeAll(_inferrer, [stateAfterCase]) || changed; + _state = stateBeforeCase; } } while (changed); - _locals.endLoop(_inferrer, node); + _state.endLoop(_inferrer, node); continueTargets.forEach(_clearBreaksAndContinues); } else { - LocalsHandler localsBeforeCase = _locals; - FieldInitializationScope fieldScopeBeforeCase = _fieldScope; - List localsToMerge = []; + LocalState stateBeforeCase = _state; + List statesToMerge = []; bool hasDefaultCase = false; for (ir.SwitchCase switchCase in node.cases) { if (switchCase.isDefault) { hasDefaultCase = true; } - _locals = new LocalsHandler.from(localsBeforeCase, switchCase); + _state = new LocalState.childPath(stateBeforeCase, switchCase); visit(switchCase); - localsToMerge.add(_locals); + statesToMerge.add(_state); } - localsBeforeCase.mergeAfterBreaks(_inferrer, localsToMerge, + stateBeforeCase.mergeAfterBreaks(_inferrer, statesToMerge, keepOwnLocals: !hasDefaultCase); - _locals = localsBeforeCase; - _fieldScope = fieldScopeBeforeCase; + _state = stateBeforeCase; } _clearBreaksAndContinues(jumpTarget); return null; @@ -584,10 +553,10 @@ class KernelTypeGraphBuilder extends ir.Visitor { @override visitContinueSwitchStatement(ir.ContinueSwitchStatement node) { JumpTarget target = _localsMap.getJumpTargetForContinueSwitch(node); - _locals.seenBreakOrContinue = true; + _state.seenBreakOrContinue = true; // Do a deep-copy of the locals, because the code following the // break will change them. - _continuesFor[target].add(new LocalsHandler.deepCopyOf(_locals)); + _continuesFor[target].add(new LocalState.deepCopyOf(_state)); return null; } @@ -637,8 +606,8 @@ class KernelTypeGraphBuilder extends ir.Visitor { TypeInformation visitReturnStatement(ir.ReturnStatement node) { ir.Node expression = node.expression; recordReturnType(expression == null ? _types.nullType : visit(expression)); - _locals.seenReturnOrThrow = true; - initializationIsIndefinite(); + _state.seenReturnOrThrow = true; + _state.markInitializationAsIndefinite(); return null; } @@ -706,14 +675,14 @@ class KernelTypeGraphBuilder extends ir.Visitor { Local local = _localsMap.getLocalVariable(node); DartType type = _localsMap.getLocalType(_elementMap, local); if (node.initializer == null) { - _locals.update( + _state.updateLocal( _inferrer, _capturedAndBoxed, local, _types.nullType, node, type); } else { - _locals.update(_inferrer, _capturedAndBoxed, local, + _state.updateLocal(_inferrer, _capturedAndBoxed, local, visit(node.initializer), node, type); } if (node.initializer is ir.ThisExpression) { - _markThisAsExposed(); + _state.markThisAsExposed(); } return null; } @@ -721,7 +690,8 @@ class KernelTypeGraphBuilder extends ir.Visitor { @override TypeInformation visitVariableGet(ir.VariableGet node) { Local local = _localsMap.getLocalVariable(node.variable); - TypeInformation type = _locals.use(_inferrer, _capturedAndBoxed, local); + TypeInformation type = + _state.readLocal(_inferrer, _capturedAndBoxed, local); assert(type != null, "Missing type information for $local."); return type; } @@ -730,11 +700,12 @@ class KernelTypeGraphBuilder extends ir.Visitor { TypeInformation visitVariableSet(ir.VariableSet node) { TypeInformation rhsType = visit(node.value); if (node.value is ir.ThisExpression) { - _markThisAsExposed(); + _state.markThisAsExposed(); } Local local = _localsMap.getLocalVariable(node.variable); DartType type = _localsMap.getLocalType(_elementMap, local); - _locals.update(_inferrer, _capturedAndBoxed, local, rhsType, node, type); + _state.updateLocal( + _inferrer, _capturedAndBoxed, local, rhsType, node, type); return rhsType; } @@ -745,7 +716,7 @@ class KernelTypeGraphBuilder extends ir.Visitor { // TODO(ngeoffray): We could do better here if we knew what we // are calling does not expose this. if (argument is ir.ThisExpression) { - _markThisAsExposed(); + _state.markThisAsExposed(); } positional.add(visit(argument)); } @@ -755,7 +726,7 @@ class KernelTypeGraphBuilder extends ir.Visitor { // TODO(ngeoffray): We could do better here if we knew what we // are calling does not expose this. if (value is ir.ThisExpression) { - _markThisAsExposed(); + _state.markThisAsExposed(); } named[argument.name] = visit(value); } @@ -846,7 +817,7 @@ class KernelTypeGraphBuilder extends ir.Visitor { TypeInformation refinedType = _types .refineReceiver(selector, mask, receiverType, isConditional: false); DartType type = _localsMap.getLocalType(_elementMap, local); - _locals.update( + _state.updateLocal( _inferrer, _capturedAndBoxed, local, refinedType, node, type); List refinements = _localRefinementMap[variable]; if (refinements != null) { @@ -931,12 +902,12 @@ class KernelTypeGraphBuilder extends ir.Visitor { Local local = _localsMap.getLocalVariable(alias); DartType type = _localsMap.getLocalType(_elementMap, local); TypeInformation localType = - _locals.use(_inferrer, _capturedAndBoxed, local); + _state.readLocal(_inferrer, _capturedAndBoxed, local); for (Refinement refinement in refinements) { localType = _types.refineReceiver( refinement.selector, refinement.mask, localType, isConditional: true); - _locals.update( + _state.updateLocal( _inferrer, _capturedAndBoxed, local, localType, node, type); } } @@ -949,7 +920,7 @@ class KernelTypeGraphBuilder extends ir.Visitor { if (node.iterable is ir.ThisExpression) { // Any reasonable implementation of an iterator would expose // this, so we play it safe and assume it will. - _markThisAsExposed(); + _state.markThisAsExposed(); } AbstractValue currentMask; @@ -990,7 +961,7 @@ class KernelTypeGraphBuilder extends ir.Visitor { Local variable = _localsMap.getLocalVariable(node.variable); DartType variableType = _localsMap.getLocalType(_elementMap, variable); - _locals.update(_inferrer, _capturedAndBoxed, variable, currentType, + _state.updateLocal(_inferrer, _capturedAndBoxed, variable, currentType, node.variable, variableType); JumpTarget target = _localsMap.getJumpTargetForForIn(node); @@ -1002,10 +973,10 @@ class KernelTypeGraphBuilder extends ir.Visitor { void _setupBreaksAndContinues(JumpTarget target) { if (target == null) return; if (target.isContinueTarget) { - _continuesFor[target] = []; + _continuesFor[target] = []; } if (target.isBreakTarget) { - _breaksFor[target] = []; + _breaksFor[target] = []; } } @@ -1014,15 +985,15 @@ class KernelTypeGraphBuilder extends ir.Visitor { _breaksFor.remove(element); } - List _getBreaks(JumpTarget target) { - List list = [_locals]; + List _getBreaks(JumpTarget target) { + List list = [_state]; if (target == null) return list; if (!target.isBreakTarget) return list; return list..addAll(_breaksFor[target]); } - List _getLoopBackEdges(JumpTarget target) { - List list = [_locals]; + List _getLoopBackEdges(JumpTarget target) { + List list = [_state]; if (target == null) return list; if (!target.isContinueTarget) return list; return list..addAll(_continuesFor[target]); @@ -1031,24 +1002,21 @@ class KernelTypeGraphBuilder extends ir.Visitor { TypeInformation handleLoop(ir.Node node, JumpTarget target, void logic()) { _loopLevel++; bool changed = false; - LocalsHandler localsBefore = _locals; - FieldInitializationScope fieldScopeBefore = _fieldScope; - localsBefore.startLoop(_inferrer, node); + LocalState stateBefore = _state; + stateBefore.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(localsBefore, node); - _fieldScope = new FieldInitializationScope.from(fieldScopeBefore); + _state = new LocalState.childPath(stateBefore, node); logic(); - changed = localsBefore.mergeAll(_inferrer, _getLoopBackEdges(target)); + changed = stateBefore.mergeAll(_inferrer, _getLoopBackEdges(target)); } while (changed); _loopLevel--; - localsBefore.endLoop(_inferrer, node); + stateBefore.endLoop(_inferrer, node); bool keepOwnLocals = node is! ir.DoStatement; - _locals = localsBefore.mergeAfterBreaks(_inferrer, _getBreaks(target), + _state = stateBefore.mergeAfterBreaks(_inferrer, _getBreaks(target), keepOwnLocals: keepOwnLocals); - _fieldScope = fieldScopeBefore; _clearBreaksAndContinues(target); return null; } @@ -1249,7 +1217,7 @@ class KernelTypeGraphBuilder extends ir.Visitor { TypeInformation visitStaticSet(ir.StaticSet node) { TypeInformation rhsType = visit(node.value); if (node.value is ir.ThisExpression) { - _markThisAsExposed(); + _state.markThisAsExposed(); } MemberEntity member = _elementMap.getMember(node.target); AbstractValue mask = _memberData.typeOfSend(node); @@ -1299,7 +1267,7 @@ class KernelTypeGraphBuilder extends ir.Visitor { TypeInformation rhsType = visit(node.value); if (node.value is ir.ThisExpression) { - _markThisAsExposed(); + _state.markThisAsExposed(); } if (_inGenerativeConstructor && node.receiver is ir.ThisExpression) { @@ -1314,7 +1282,7 @@ class KernelTypeGraphBuilder extends ir.Visitor { MemberEntity single = targets.first; if (single.isField) { FieldEntity field = single; - _fieldScope.updateField(field, rhsType); + _state.updateField(field, rhsType); } } } @@ -1375,11 +1343,11 @@ class KernelTypeGraphBuilder extends ir.Visitor { List positiveTests, List negativeTests) { for (IsCheck check in positiveTests) { if (check.type != null) { - _locals.narrow( + _state.narrowLocal( _inferrer, _capturedAndBoxed, check.local, check.type, check.node); } else { DartType localType = _localsMap.getLocalType(_elementMap, check.local); - _locals.update(_inferrer, _capturedAndBoxed, check.local, + _state.updateLocal(_inferrer, _capturedAndBoxed, check.local, _types.nullType, check.node, localType); } } @@ -1387,7 +1355,7 @@ class KernelTypeGraphBuilder extends ir.Visitor { if (check.type != null) { // TODO(johnniwinther): Use negative type knowledge. } else { - _locals.narrow(_inferrer, _capturedAndBoxed, check.local, + _state.narrowLocal(_inferrer, _capturedAndBoxed, check.local, _closedWorld.commonElements.objectType, check.node); } } @@ -1399,28 +1367,21 @@ class KernelTypeGraphBuilder extends ir.Visitor { List negativeTests = []; bool simpleCondition = handleCondition(node.condition, positiveTests, negativeTests); - LocalsHandler localsBefore = _locals; - FieldInitializationScope fieldScopeBefore = _fieldScope; - _locals = new LocalsHandler.from(localsBefore, node); - _fieldScope = new FieldInitializationScope.from(fieldScopeBefore); + LocalState stateBefore = _state; + _state = new LocalState.childPath(stateBefore, node); _updateIsChecks(positiveTests, negativeTests); visit(node.then); - LocalsHandler localsAfterThen = _locals; - FieldInitializationScope fieldScopeAfterThen = _fieldScope; - _locals = new LocalsHandler.from(localsBefore, node); - _fieldScope = new FieldInitializationScope.from(fieldScopeBefore); + LocalState stateAfterThen = _state; + _state = new LocalState.childPath(stateBefore, node); if (simpleCondition) { _updateIsChecks(negativeTests, positiveTests); } visit(node.otherwise); - LocalsHandler localsAfterElse = _locals; - FieldInitializationScope fieldScopeAfterElse = _fieldScope; + LocalState stateAfterElse = _state; - _locals = localsBefore.mergeDiamondFlow( - _inferrer, localsAfterThen, localsAfterElse); - _fieldScope = fieldScopeBefore?.mergeDiamondFlow( - _inferrer, fieldScopeAfterThen, fieldScopeAfterElse); + _state = + stateBefore.mergeDiamondFlow(_inferrer, stateAfterThen, stateAfterElse); return null; } @@ -1456,15 +1417,13 @@ class KernelTypeGraphBuilder extends ir.Visitor { _negativeIsChecks = []; } visit(node.left, conditionContext: _accumulateIsChecks); - LocalsHandler localsBefore = _locals; - FieldInitializationScope fieldScopeBefore = _fieldScope; - _locals = new LocalsHandler.from(localsBefore, node); - _fieldScope = new FieldInitializationScope.from(fieldScopeBefore); + LocalState stateBefore = _state; + _state = new LocalState.childPath(stateBefore, node); _updateIsChecks(_positiveIsChecks, _negativeIsChecks); - LocalsHandler narrowed; + LocalState narrowed; if (oldAccumulateIsChecks) { - narrowed = new LocalsHandler.topLevelCopyOf(_locals); + narrowed = new LocalState.deepCopyOf(_state); } else { _accumulateIsChecks = false; _positiveIsChecks = oldPositiveIsChecks; @@ -1474,17 +1433,16 @@ class KernelTypeGraphBuilder extends ir.Visitor { if (oldAccumulateIsChecks) { bool invalidatedInRightHandSide(IsCheck check) { TypeInformation narrowedType = - narrowed.use(_inferrer, _capturedAndBoxed, check.local); + narrowed.readLocal(_inferrer, _capturedAndBoxed, check.local); TypeInformation currentType = - _locals.use(_inferrer, _capturedAndBoxed, check.local); + _state.readLocal(_inferrer, _capturedAndBoxed, check.local); return narrowedType != currentType; } _positiveIsChecks.removeWhere(invalidatedInRightHandSide); _negativeIsChecks.removeWhere(invalidatedInRightHandSide); } - _locals = localsBefore.mergeFlow(_inferrer, _locals); - _fieldScope = fieldScopeBefore; + _state = stateBefore.mergeFlow(_inferrer, _state); return _types.boolType; } else if (node.operator == '||') { _conditionIsSimple = false; @@ -1492,16 +1450,13 @@ class KernelTypeGraphBuilder extends ir.Visitor { List negativeIsChecks = []; bool isSimple = handleCondition(node.left, positiveIsChecks, negativeIsChecks); - LocalsHandler localsBefore = _locals; - FieldInitializationScope fieldScopeBefore = _fieldScope; - _locals = new LocalsHandler.from(_locals, node); - _fieldScope = new FieldInitializationScope.from(fieldScopeBefore); + LocalState stateBefore = _state; + _state = new LocalState.childPath(stateBefore, node); if (isSimple) { _updateIsChecks(negativeIsChecks, positiveIsChecks); } visit(node.right, conditionContext: false); - _locals = localsBefore.mergeFlow(_inferrer, _locals); - _fieldScope = fieldScopeBefore; + _state = stateBefore.mergeFlow(_inferrer, _state); return _types.boolType; } failedAt(CURRENT_ELEMENT_SPANNABLE, @@ -1515,24 +1470,17 @@ class KernelTypeGraphBuilder extends ir.Visitor { List negativeTests = []; bool simpleCondition = handleCondition(node.condition, positiveTests, negativeTests); - LocalsHandler localsBefore = _locals; - _locals = new LocalsHandler.from(localsBefore, node); - FieldInitializationScope fieldScopeBefore = _fieldScope; - _fieldScope = new FieldInitializationScope.from(fieldScopeBefore); + LocalState stateBefore = _state; + _state = new LocalState.childPath(stateBefore, node); _updateIsChecks(positiveTests, negativeTests); TypeInformation firstType = visit(node.then); - LocalsHandler localsAfterThen = _locals; - FieldInitializationScope fieldScopeAfterThen = _fieldScope; - _locals = new LocalsHandler.from(localsBefore, node); - _fieldScope = new FieldInitializationScope.from(fieldScopeBefore); + LocalState stateAfterThen = _state; + _state = new LocalState.childPath(stateBefore, node); if (simpleCondition) _updateIsChecks(negativeTests, positiveTests); TypeInformation secondType = visit(node.otherwise); - LocalsHandler localsAfterElse = _locals; - FieldInitializationScope fieldScopeAfterElse = _fieldScope; - _locals = localsBefore.mergeDiamondFlow( - _inferrer, localsAfterThen, localsAfterElse); - _fieldScope = fieldScopeBefore?.mergeDiamondFlow( - _inferrer, fieldScopeAfterThen, fieldScopeAfterElse); + LocalState stateAfterElse = _state; + _state = + stateBefore.mergeDiamondFlow(_inferrer, stateAfterThen, stateAfterElse); return _types.allocateDiamondPhi(firstType, secondType); } @@ -1544,7 +1492,7 @@ class KernelTypeGraphBuilder extends ir.Visitor { // analyzing the closure. // TODO(herhut): Analyze whether closure exposes this. Possibly using // whether the created closure as a `thisLocal`. - _markThisAsExposed(); + _state.markThisAsExposed(); ClosureRepresentationInfo info = _closureDataLookup.getClosureInfo(node); @@ -1557,7 +1505,7 @@ class KernelTypeGraphBuilder extends ir.Visitor { _inferrer.recordTypeOfField(field, thisType); } TypeInformation localType = - _locals.use(_inferrer, _capturedAndBoxed, variable); + _state.readLocal(_inferrer, _capturedAndBoxed, variable); // The type is null for type parameters. if (localType != null) { _inferrer.recordTypeOfField(field, localType); @@ -1573,17 +1521,14 @@ class KernelTypeGraphBuilder extends ir.Visitor { if (variable != null) { Local local = _localsMap.getLocalVariable(variable); DartType type = _localsMap.getLocalType(_elementMap, local); - _locals.update( + _state.updateLocal( _inferrer, _capturedAndBoxed, local, localFunctionType, node, type); } // We don't put the closure in the work queue of the // inferrer, because it will share information with its enclosing // method, like for example the types of local variables. - LocalsHandler closureLocals = - new LocalsHandler.from(_locals, node, useOtherTryBlock: false); - FieldInitializationScope closureFieldScope = - new FieldInitializationScope.from(_fieldScope); + LocalState closureState = new LocalState.closure(_state, node); KernelTypeGraphBuilder visitor = new KernelTypeGraphBuilder( _options, _closedWorld, @@ -1591,8 +1536,7 @@ class KernelTypeGraphBuilder extends ir.Visitor { info.callMethod, functionNode, _localsMap, - closureLocals, - closureFieldScope, + closureState, _capturedAndBoxed); visitor.run(); _inferrer.recordReturnType(info.callMethod, visitor._returnType); @@ -1655,41 +1599,30 @@ class KernelTypeGraphBuilder extends ir.Visitor { @override visitTryCatch(ir.TryCatch node) { - LocalsHandler localsBefore = _locals; - FieldInitializationScope fieldScopeBefore = _fieldScope; - _locals = new LocalsHandler.from(localsBefore, node, - isTry: true, useOtherTryBlock: false); - _fieldScope = new FieldInitializationScope.from(fieldScopeBefore); - initializationIsIndefinite(); + LocalState stateBefore = _state; + _state = new LocalState.tryBlock(stateBefore, node); + _state.markInitializationAsIndefinite(); visit(node.body); - LocalsHandler localsAfterBody = _locals; - _locals = localsBefore.mergeFlow(_inferrer, localsAfterBody); - _fieldScope = fieldScopeBefore; + LocalState stateAfterBody = _state; + _state = stateBefore.mergeFlow(_inferrer, stateAfterBody); for (ir.Catch catchBlock in node.catches) { - LocalsHandler localsBeforeCatch = _locals; - FieldInitializationScope fieldScopeBeforeCatch = _fieldScope; - _locals = new LocalsHandler.from(localsBeforeCatch, catchBlock); - _fieldScope = new FieldInitializationScope.from(fieldScopeBeforeCatch); + LocalState stateBeforeCatch = _state; + _state = new LocalState.childPath(stateBeforeCatch, catchBlock); visit(catchBlock); - LocalsHandler localsAfterCatch = _locals; - _locals = localsBeforeCatch.mergeFlow(_inferrer, localsAfterCatch); - _fieldScope = fieldScopeBeforeCatch; + LocalState stateAfterCatch = _state; + _state = stateBeforeCatch.mergeFlow(_inferrer, stateAfterCatch); } return null; } @override visitTryFinally(ir.TryFinally node) { - LocalsHandler localsBefore = _locals; - FieldInitializationScope fieldScopeBefore = _fieldScope; - _locals = new LocalsHandler.from(localsBefore, node, - isTry: true, useOtherTryBlock: false); - _fieldScope = new FieldInitializationScope.from(fieldScopeBefore); - initializationIsIndefinite(); + LocalState stateBefore = _state; + _state = new LocalState.tryBlock(stateBefore, node); + _state.markInitializationAsIndefinite(); visit(node.body); - LocalsHandler localsAfterBody = _locals; - _locals = localsBefore.mergeFlow(_inferrer, localsAfterBody); - _fieldScope = fieldScopeBefore; + LocalState stateAfterBody = _state; + _state = stateBefore.mergeFlow(_inferrer, stateAfterBody); visit(node.finalizer); return null; } @@ -1709,15 +1642,15 @@ class KernelTypeGraphBuilder extends ir.Visitor { mask = _types.dynamicType; } Local local = _localsMap.getLocalVariable(exception); - _locals.update( + _state.updateLocal( _inferrer, _capturedAndBoxed, 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(_inferrer, _capturedAndBoxed, local, _types.dynamicType, - node, const DynamicType()); + _state.updateLocal(_inferrer, _capturedAndBoxed, local, + _types.dynamicType, node, const DynamicType()); } visit(node.body); return null; @@ -1726,13 +1659,13 @@ class KernelTypeGraphBuilder extends ir.Visitor { @override TypeInformation visitThrow(ir.Throw node) { visit(node.expression); - _locals.seenReturnOrThrow = true; + _state.seenReturnOrThrow = true; return _types.nonNullEmpty(); } @override TypeInformation visitRethrow(ir.Rethrow node) { - _locals.seenReturnOrThrow = true; + _state.seenReturnOrThrow = true; return _types.nonNullEmpty(); } @@ -1749,7 +1682,7 @@ class KernelTypeGraphBuilder extends ir.Visitor { TypeInformation visitSuperPropertyGet(ir.SuperPropertyGet node) { // TODO(herhut): We could do better here if we knew what we // are calling does not expose this. - _markThisAsExposed(); + _state.markThisAsExposed(); MemberEntity member = _elementMap.getSuperMember(_analyzedMember, node.name); @@ -1771,7 +1704,7 @@ class KernelTypeGraphBuilder extends ir.Visitor { TypeInformation visitSuperPropertySet(ir.SuperPropertySet node) { // TODO(herhut): We could do better here if we knew what we // are calling does not expose this. - _markThisAsExposed(); + _state.markThisAsExposed(); TypeInformation rhsType = visit(node.value); MemberEntity member = @@ -1791,7 +1724,7 @@ class KernelTypeGraphBuilder extends ir.Visitor { TypeInformation visitSuperMethodInvocation(ir.SuperMethodInvocation node) { // TODO(herhut): We could do better here if we knew what we // are calling does not expose this. - _markThisAsExposed(); + _state.markThisAsExposed(); MemberEntity member = _elementMap.getSuperMember(_analyzedMember, node.name); @@ -1867,3 +1800,188 @@ class Refinement { Refinement(this.selector, this.mask); } + +class LocalState { + final LocalsHandler _locals; + final FieldInitializationScope _fields; + bool seenReturnOrThrow = false; + bool seenBreakOrContinue = false; + LocalsHandler _tryBlock; + + LocalState.initial(ir.TreeNode node, {bool inGenerativeConstructor}) + : this.internal( + new LocalsHandler(node), + inGenerativeConstructor ? new FieldInitializationScope() : null, + null, + seenReturnOrThrow: false, + seenBreakOrContinue: false); + + LocalState.childPath(LocalState other, ir.TreeNode node) + : this.internal(new LocalsHandler.from(other._locals, node, isTry: false), + new FieldInitializationScope.from(other._fields), other._tryBlock, + seenReturnOrThrow: false, seenBreakOrContinue: false); + + LocalState.closure(LocalState other, ir.TreeNode node) + : this.internal(new LocalsHandler.from(other._locals, node, isTry: false), + new FieldInitializationScope.from(other._fields), null, + seenReturnOrThrow: false, seenBreakOrContinue: false); + + factory LocalState.tryBlock(LocalState other, ir.TreeNode node) { + LocalsHandler locals = + new LocalsHandler.from(other._locals, node, isTry: true); + FieldInitializationScope fieldScope = + new FieldInitializationScope.from(other._fields); + LocalsHandler tryBlock = locals; + return new LocalState.internal(locals, fieldScope, tryBlock, + seenReturnOrThrow: false, seenBreakOrContinue: false); + } + + LocalState.deepCopyOf(LocalState other) + : _locals = new LocalsHandler.deepCopyOf(other._locals), + _tryBlock = other._tryBlock, + _fields = other._fields; + + LocalState.internal(this._locals, this._fields, this._tryBlock, + {this.seenReturnOrThrow, this.seenBreakOrContinue}); + + bool get aborts { + return seenReturnOrThrow || seenBreakOrContinue; + } + + bool get isThisExposed { + return _fields == null || _fields.isThisExposed; + } + + void markThisAsExposed() { + _fields?.isThisExposed = true; + } + + void markInitializationAsIndefinite() { + _fields?.isIndefinite = true; + } + + TypeInformation readField(FieldEntity field) { + return _fields.readField(field); + } + + void updateField(FieldEntity field, TypeInformation type) { + _fields.updateField(field, type); + } + + TypeInformation readLocal(InferrerEngine inferrer, + Map capturedAndBoxed, Local local) { + FieldEntity field = capturedAndBoxed[local]; + if (field != null) { + return inferrer.typeOfMember(field); + } else { + return _locals.use(inferrer, local); + } + } + + void updateLocal( + InferrerEngine inferrer, + Map capturedAndBoxed, + Local local, + TypeInformation type, + ir.Node node, + DartType staticType) { + assert(type != null); + type = inferrer.types.narrowType(type, staticType); + + FieldEntity field = capturedAndBoxed[local]; + if (field != null) { + inferrer.recordTypeOfField(field, type); + } else { + _locals.update(inferrer, local, type, node, staticType, _tryBlock); + } + } + + void narrowLocal( + InferrerEngine inferrer, + Map capturedAndBoxed, + Local local, + DartType type, + ir.Node node) { + TypeInformation existing = readLocal(inferrer, capturedAndBoxed, local); + TypeInformation newType = + inferrer.types.narrowType(existing, type, isNullable: false); + updateLocal(inferrer, capturedAndBoxed, local, newType, node, type); + } + + LocalState mergeFlow(InferrerEngine inferrer, LocalState other) { + seenReturnOrThrow = false; + seenBreakOrContinue = false; + + if (other.aborts) { + return this; + } + LocalsHandler locals = _locals.mergeFlow(inferrer, other._locals); + return new LocalState.internal(locals, _fields, _tryBlock, + seenReturnOrThrow: seenReturnOrThrow, + seenBreakOrContinue: seenBreakOrContinue); + } + + LocalState mergeDiamondFlow( + InferrerEngine inferrer, LocalState thenBranch, LocalState elseBranch) { + seenReturnOrThrow = + thenBranch.seenReturnOrThrow && elseBranch.seenReturnOrThrow; + seenBreakOrContinue = + thenBranch.seenBreakOrContinue && elseBranch.seenBreakOrContinue; + + LocalsHandler locals; + if (aborts) { + locals = _locals; + } else if (thenBranch.aborts) { + locals = _locals.mergeFlow(inferrer, elseBranch._locals, inPlace: true); + } else if (elseBranch.aborts) { + locals = _locals.mergeFlow(inferrer, thenBranch._locals, inPlace: true); + } else { + locals = _locals.mergeDiamondFlow( + inferrer, thenBranch._locals, elseBranch._locals); + } + + FieldInitializationScope fieldScope = _fields?.mergeDiamondFlow( + inferrer, thenBranch._fields, elseBranch._fields); + return new LocalState.internal(locals, fieldScope, _tryBlock, + seenReturnOrThrow: seenReturnOrThrow, + seenBreakOrContinue: seenBreakOrContinue); + } + + LocalState mergeAfterBreaks(InferrerEngine inferrer, List states, + {bool keepOwnLocals: true}) { + bool allBranchesAbort = true; + for (LocalState state in states) { + allBranchesAbort = allBranchesAbort && state.seenReturnOrThrow; + } + + keepOwnLocals = keepOwnLocals && !seenReturnOrThrow; + + LocalsHandler locals = _locals.mergeAfterBreaks( + inferrer, + states + .where((LocalState state) => !state.seenReturnOrThrow) + .map((LocalState state) => state._locals), + keepOwnLocals: keepOwnLocals); + seenReturnOrThrow = allBranchesAbort && !keepOwnLocals; + return new LocalState.internal(locals, _fields, _tryBlock, + seenReturnOrThrow: seenReturnOrThrow, + seenBreakOrContinue: seenBreakOrContinue); + } + + bool mergeAll(InferrerEngine inferrer, List states) { + assert(!seenReturnOrThrow); + return _locals.mergeAll( + inferrer, + states + .where((LocalState state) => !state.seenReturnOrThrow) + .map((LocalState state) => state._locals)); + } + + void startLoop(InferrerEngine inferrer, ir.Node loop) { + _locals.startLoop(inferrer, loop); + } + + void endLoop(InferrerEngine inferrer, ir.Node loop) { + _locals.endLoop(inferrer, loop); + } +} diff --git a/pkg/compiler/lib/src/inferrer/locals_handler.dart b/pkg/compiler/lib/src/inferrer/locals_handler.dart index 2ebc4461e8f..16fc7575536 100644 --- a/pkg/compiler/lib/src/inferrer/locals_handler.dart +++ b/pkg/compiler/lib/src/inferrer/locals_handler.dart @@ -251,69 +251,37 @@ class ArgumentsTypesIterator implements Iterator { */ class LocalsHandler { final VariableScope _locals; - LocalsHandler _tryBlock; - bool seenReturnOrThrow = false; - bool seenBreakOrContinue = false; - - bool get aborts { - return seenReturnOrThrow || seenBreakOrContinue; - } - - bool get inTryBlock => _tryBlock != null; - - LocalsHandler.internal(ir.Node block, this._locals, this._tryBlock); LocalsHandler(ir.Node block) - : _locals = new VariableScope(block, isTry: false), - _tryBlock = null; + : _locals = new VariableScope(block, isTry: false); LocalsHandler.from(LocalsHandler other, ir.Node block, {bool isTry: false, bool useOtherTryBlock: true}) - : _locals = - new VariableScope(block, isTry: isTry, parent: other._locals) { - _tryBlock = useOtherTryBlock ? other._tryBlock : this; - } + : _locals = new VariableScope(block, isTry: isTry, parent: other._locals); LocalsHandler.deepCopyOf(LocalsHandler other) - : _locals = new VariableScope.deepCopyOf(other._locals), - _tryBlock = other._tryBlock; + : _locals = new VariableScope.deepCopyOf(other._locals); - LocalsHandler.topLevelCopyOf(LocalsHandler other) - : _locals = new VariableScope.topLevelCopyOf(other._locals), - _tryBlock = other._tryBlock; - - TypeInformation use(InferrerEngine inferrer, - Map capturedAndBoxed, Local local) { - FieldEntity field = capturedAndBoxed[local]; - if (field != null) { - return inferrer.typeOfMember(field); - } else { - return _locals[local]; - } + TypeInformation use(InferrerEngine inferrer, Local local) { + return _locals[local]; } - void update(InferrerEngine inferrer, Map capturedAndBoxed, - Local local, TypeInformation type, ir.Node node, DartType staticType) { - assert(type != null); - type = inferrer.types.narrowType(type, staticType); - - FieldEntity field = capturedAndBoxed[local]; - if (field != null) { - inferrer.recordTypeOfField(field, type); - } else if (inTryBlock) { + void update(InferrerEngine inferrer, Local local, TypeInformation type, + ir.Node node, DartType staticType, 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]; + TypeInformation existing = tryBlock._locals.parent[local]; if (existing != null) { TypeInformation phiType = inferrer.types.allocatePhi( - _tryBlock._locals.block, local, existing, - isTry: _tryBlock._locals.isTry); + tryBlock._locals.block, local, existing, + isTry: tryBlock._locals.isTry); TypeInformation inputType = inferrer.types.addPhiInput(local, phiType, type); - _tryBlock._locals.parent[local] = inputType; + tryBlock._locals.parent[local] = inputType; } // Update the current handler unconditionally with the new // type. @@ -323,30 +291,21 @@ class LocalsHandler { } } - void narrow(InferrerEngine inferrer, Map capturedAndBoxed, - Local local, DartType type, ir.Node node) { - TypeInformation existing = use(inferrer, capturedAndBoxed, local); - TypeInformation newType = - inferrer.types.narrowType(existing, type, isNullable: false); - update(inferrer, capturedAndBoxed, local, newType, node, type); - } - /// Returns the join between this locals handler and [other] which models the /// flow through either this or [other]. - LocalsHandler mergeFlow(InferrerEngine inferrer, LocalsHandler other) { - seenReturnOrThrow = false; - seenBreakOrContinue = false; - - if (other.aborts) { - return this; - } else { - other._locals.forEachOwnLocal((Local local, TypeInformation type) { - TypeInformation myType = _locals[local]; - if (myType == null) return; // Variable is only defined in [other]. - if (type == myType) return; - _locals[local] = inferrer.types.allocateDiamondPhi(myType, type); - }); - } + /// + /// If [inPlace] is `true`, the variable types in this locals handler are + /// replaced by the variables types in [other]. Otherwise the variable types + /// from both are merged with a phi type. + LocalsHandler mergeFlow(InferrerEngine inferrer, LocalsHandler other, + {bool inPlace: false}) { + other._locals.forEachOwnLocal((Local local, TypeInformation type) { + TypeInformation myType = _locals[local]; + if (myType == null) return; // Variable is only defined in [other]. + if (type == myType) return; + _locals[local] = + inPlace ? type : inferrer.types.allocateDiamondPhi(myType, type); + }); return this; } @@ -355,48 +314,27 @@ class LocalsHandler { LocalsHandler mergeDiamondFlow(InferrerEngine inferrer, LocalsHandler thenBranch, LocalsHandler elseBranch) { assert(elseBranch != null); - seenReturnOrThrow = - thenBranch.seenReturnOrThrow && elseBranch.seenReturnOrThrow; - seenBreakOrContinue = - thenBranch.seenBreakOrContinue && elseBranch.seenBreakOrContinue; - if (aborts) return this; - void inPlaceUpdateOneBranch(LocalsHandler other) { - other._locals.forEachOwnLocal((Local local, TypeInformation type) { - TypeInformation myType = _locals[local]; - if (myType == null) return; // Variable is only defined in [other]. - if (type == myType) return; - _locals[local] = type; - }); - } - - if (thenBranch.aborts) { - inPlaceUpdateOneBranch(elseBranch); - } else if (elseBranch.aborts) { - inPlaceUpdateOneBranch(thenBranch); - } else { - void mergeLocal(Local local) { - TypeInformation myType = _locals[local]; - if (myType == null) return; - TypeInformation elseType = elseBranch._locals[local]; - TypeInformation thenType = thenBranch._locals[local]; - if (thenType == elseType) { - _locals[local] = thenType; - } else { - _locals[local] = - inferrer.types.allocateDiamondPhi(thenType, elseType); - } + void mergeLocal(Local local) { + TypeInformation myType = _locals[local]; + if (myType == null) return; + TypeInformation elseType = elseBranch._locals[local]; + TypeInformation thenType = thenBranch._locals[local]; + if (thenType == elseType) { + _locals[local] = thenType; + } else { + _locals[local] = inferrer.types.allocateDiamondPhi(thenType, elseType); } - - thenBranch._locals.forEachOwnLocal((Local local, _) { - mergeLocal(local); - }); - elseBranch._locals.forEachOwnLocal((Local local, _) { - // Discard locals we already processed when iterating over - // [thenBranch]'s locals. - if (!thenBranch._locals.updates(local)) mergeLocal(local); - }); } + + thenBranch._locals.forEachOwnLocal((Local local, _) { + mergeLocal(local); + }); + elseBranch._locals.forEachOwnLocal((Local local, _) { + // Discard locals we already processed when iterating over + // [thenBranch]'s locals. + if (!thenBranch._locals.updates(local)) mergeLocal(local); + }); return this; } @@ -431,7 +369,7 @@ class LocalsHandler { * labeled statement that do not break out. */ LocalsHandler mergeAfterBreaks( - InferrerEngine inferrer, List handlers, + InferrerEngine inferrer, Iterable handlers, {bool keepOwnLocals: true}) { ir.Node level = _locals.block; // Use a separate locals handler to perform the merge in, so that Phi @@ -440,15 +378,13 @@ class LocalsHandler { LocalsHandler merged = new LocalsHandler.from(this, level, isTry: _locals.isTry); Set seenLocals = new Setlet(); - bool allBranchesAbort = true; // Merge all other handlers. for (LocalsHandler handler in handlers) { - allBranchesAbort = allBranchesAbort && handler.seenReturnOrThrow; 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. - if (keepOwnLocals && !seenReturnOrThrow) { + if (keepOwnLocals) { for (Local variable in seenLocals) { TypeInformation originalType = _locals[variable]; if (originalType != null) { @@ -462,8 +398,6 @@ class LocalsHandler { merged._locals.forEachOwnLocal((Local variable, TypeInformation type) { _locals[variable] = inferrer.types.simplifyPhi(level, variable, type); }); - seenReturnOrThrow = - allBranchesAbort && (!keepOwnLocals || seenReturnOrThrow); return this; } @@ -475,7 +409,6 @@ class LocalsHandler { */ bool _mergeHandler(InferrerEngine inferrer, LocalsHandler other, [Set seen]) { - if (other.seenReturnOrThrow) return false; bool changed = false; other._locals.forEachLocalUntilNode(_locals.block, (local, otherType) { TypeInformation myType = _locals[local]; @@ -500,9 +433,8 @@ class LocalsHandler { * Merge all [LocalsHandler] in [handlers] into this handler. * Returns whether a local in this handler has changed. */ - bool mergeAll(InferrerEngine inferrer, List handlers) { + bool mergeAll(InferrerEngine inferrer, Iterable handlers) { bool changed = false; - assert(!seenReturnOrThrow); handlers.forEach((other) { changed = _mergeHandler(inferrer, other) || changed; });