private ISet <Variable> ProcessStatement(ISet <Variable> unavailable, IPStmt statement) { Contract.Requires(statement != null); switch (statement) { case CompoundStmt compoundStmt: unavailable = compoundStmt.Statements.Aggregate(unavailable, ProcessStatement); break; case AssertStmt assertStmt: unavailable = ProcessExpr(unavailable, assertStmt.Assertion); break; case PrintStmt printStmt: unavailable = ProcessArgList(printStmt.Args, unavailable, ArgOptions.SwapNotAllowed); break; case ReturnStmt returnStmt: if (returnStmt.ReturnValue != null) { unavailable = ProcessExpr(unavailable, returnStmt.ReturnValue); } break; case AssignStmt assignStmt: unavailable = ProcessExpr(unavailable, assignStmt.Value); if (assignStmt.Location is VariableAccessExpr assignAccess) { unavailable.Remove(assignAccess.Variable); } else { unavailable = ProcessExpr(unavailable, assignStmt.Location); } break; case MoveAssignStmt moveAssignStmt: if (moveAssignStmt.FromVariable.Role.Equals(VariableRole.Field)) { throw handler.MovedField(moveAssignStmt); } unavailable.Add(moveAssignStmt.FromVariable); if (moveAssignStmt.ToLocation is VariableAccessExpr moveAssignAccess) { unavailable.Remove(moveAssignAccess.Variable); } else { unavailable = ProcessExpr(unavailable, moveAssignStmt.ToLocation); } break; case SwapAssignStmt swapAssignStmt: if (swapAssignStmt.NewLocation is VariableAccessExpr swapAssignAccess) { if (unavailable.Contains(swapAssignAccess.Variable)) { throw handler.SwapAssignUnavailable(swapAssignStmt, swapAssignAccess.Variable); } } else { unavailable = ProcessExpr(unavailable, swapAssignStmt.NewLocation); } if (unavailable.Contains(swapAssignStmt.OldLocation)) { throw handler.SwapAssignUnavailable(swapAssignStmt, swapAssignStmt.OldLocation); } break; case InsertStmt insertStmt: unavailable = ProcessExpr(unavailable, insertStmt.Variable); unavailable = ProcessExpr(unavailable, insertStmt.Index); unavailable = ProcessExpr(unavailable, insertStmt.Value); break; case RemoveStmt removeStmt: unavailable = ProcessExpr(unavailable, removeStmt.Variable); unavailable = ProcessExpr(unavailable, removeStmt.Value); break; case WhileStmt whileStmt: unavailable = ProcessExpr(unavailable, whileStmt.Condition); // process running the body twice. on the first go, the loop can potentially // relinquish additional variables on the second go, either the body will use // one of these variables and throw or reach a fixed point since all paths are // considered simultaneously. Then, we continue our overapproximation by taking // the union of no runs and one or more runs. var bodyUnavailable = ProcessStatement(new HashSet <Variable>(unavailable), whileStmt.Body); bodyUnavailable = ProcessExpr(bodyUnavailable, whileStmt.Condition); // TODO: more efficient way of doing this? bodyUnavailable = ProcessStatement(bodyUnavailable, whileStmt.Body); bodyUnavailable = ProcessExpr(bodyUnavailable, whileStmt.Condition); unavailable.UnionWith(bodyUnavailable); break; case IfStmt ifStmt: unavailable = ProcessExpr(unavailable, ifStmt.Condition); var thenUnavailable = ProcessStatement(new HashSet <Variable>(unavailable), ifStmt.ThenBranch); var elseUnavailable = ProcessStatement(new HashSet <Variable>(unavailable), ifStmt.ElseBranch); thenUnavailable.UnionWith(elseUnavailable); unavailable = thenUnavailable; break; case CtorStmt ctorStmt: unavailable = ProcessArgList(ctorStmt.Arguments, unavailable, ArgOptions.SwapNotAllowed); break; case FunCallStmt funCallStmt: unavailable = ProcessArgList(funCallStmt.ArgsList, unavailable); funCallStmts.Add(funCallStmt); break; case RaiseStmt raiseStmt: unavailable = ProcessExpr(unavailable, raiseStmt.PEvent); unavailable = ProcessArgList(raiseStmt.Payload, unavailable, ArgOptions.SwapNotAllowed); break; case SendStmt sendStmt: unavailable = ProcessExpr(unavailable, sendStmt.MachineExpr); unavailable = ProcessExpr(unavailable, sendStmt.Evt); unavailable = ProcessArgList(sendStmt.Arguments, unavailable, ArgOptions.SwapNotAllowed); break; case AnnounceStmt announceStmt: unavailable = ProcessExpr(unavailable, announceStmt.PEvent); if (announceStmt.Payload != null) { unavailable = ProcessExpr(unavailable, announceStmt.Payload); } break; case GotoStmt gotoStmt: if (gotoStmt.Payload != null) { unavailable = ProcessExpr(unavailable, gotoStmt.Payload); } break; case ReceiveStmt receiveStmt: var postUnavailable = new HashSet <Variable>(); var caseVariables = new HashSet <Variable>(); foreach (var recvCase in receiveStmt.Cases) { var caseUnavailable = ProcessStatement(new HashSet <Variable>(unavailable), recvCase.Value.Body); postUnavailable.UnionWith(caseUnavailable); caseVariables.UnionWith(recvCase.Value.Signature.Parameters); } unavailable = postUnavailable; unavailable.ExceptWith(caseVariables); break; case PopStmt _: case NoStmt _: // nothing to check break; default: throw handler.InternalError(statement.SourceLocation, new ArgumentOutOfRangeException(nameof(statement))); } return(unavailable); }