diff --git a/lib/z3.jar b/lib/z3.jar index ecba23f0612945a40e960b6f83fc97f7fa87cd8a..9bc6f3adc745e32c2d4190d2ed452871962e7a7d 100644 Binary files a/lib/z3.jar and b/lib/z3.jar differ diff --git a/pldi2011-testcases/RedBlackTree.scala b/pldi2011-testcases/RedBlackTree.scala index ed076c09192a3e0c50d383a35b3d0d02e3c40816..1994a857cb8f58cf7bc9553201c1c36db1886852 100644 --- a/pldi2011-testcases/RedBlackTree.scala +++ b/pldi2011-testcases/RedBlackTree.scala @@ -65,11 +65,11 @@ object RedBlackTree { makeBlack(ins(x, t)) } ensuring (res => content(res) == content(t) ++ Set(x) && redNodesHaveBlackChildren(res)) - def buggyAdd(x: Int, t: Tree): Tree = { - require(redNodesHaveBlackChildren(t)) - // makeBlack(ins(x, t)) - ins(x, t) - } ensuring (res => content(res) == content(t) ++ Set(x) && redNodesHaveBlackChildren(res)) +// def buggyAdd(x: Int, t: Tree): Tree = { +// require(redNodesHaveBlackChildren(t)) +// // makeBlack(ins(x, t)) +// ins(x, t) +// } ensuring (res => content(res) == content(t) ++ Set(x) && redNodesHaveBlackChildren(res)) def balance(c: Color, a: Tree, x: Int, b: Tree): Tree = { require( diff --git a/project/build/funcheck.scala b/project/build/funcheck.scala index 7c70f935db8222c3fda6e9ba3714c830606e55c1..fd0aac5b3c1ef18c1210fbb8cc652069dce2a2ff 100644 --- a/project/build/funcheck.scala +++ b/project/build/funcheck.scala @@ -48,7 +48,7 @@ class FunCheckProject(info: ProjectInfo) extends DefaultProject(info) with FileT fw.write(plugin.jarPath.absolutePath) fw.write("\"" + nl + nl) fw.write("LD_LIBRARY_PATH=" + ("." / "lib-bin").absolutePath + " \\" + nl) - fw.write("java \\" + nl) + fw.write("java -Xmx1024M \\" + nl) // This is a hack :( val libStr = (buildLibraryJar.absolutePath).toString diff --git a/src/purescala/FairZ3Solver.scala b/src/purescala/FairZ3Solver.scala index 51fb1170218ce95ad8f29d9f54a7f436baf36f71..87cd67786d7bd1c0d8a127f2dfb0131486acadad 100644 --- a/src/purescala/FairZ3Solver.scala +++ b/src/purescala/FairZ3Solver.scala @@ -7,7 +7,8 @@ import Extensions._ import Trees._ import TypeTrees._ -import scala.collection.mutable.{HashMap => MutableHashMap} +import scala.collection.mutable.{Map => MutableMap} +import scala.collection.mutable.{Set => MutableSet} class FairZ3Solver(val reporter: Reporter) extends Solver(reporter) with AbstractZ3Solver with Z3ModelReconstruction { assert(Settings.useFairInstantiator) @@ -224,85 +225,343 @@ class FairZ3Solver(val reporter: Reporter) extends Solver(reporter) with Abstrac def decideWithModel(vc: Expr, forValidity: Boolean): (Option[Boolean], Map[Identifier,Expr]) = { restartZ3 + val unrollingBank = new UnrollingBank + lazy val varsInVC = variablesOf(vc) if (neverInitialized) { reporter.error("Z3 Solver was not initialized with a PureScala Program.") None } - val toConvert = if (forValidity) negate(vc) else vc - val toCheckAgainstModels = toConvert - - val result : (Option[Boolean],Map[Identifier,Expr]) = toZ3Formula(z3, toConvert) match { - case None => (None, Map.empty) // means it could not be translated - case Some((z3f, clauses)) => { - z3.assertCnstr(z3f) - println(z3f) - for(clause <- clauses) { - z3.assertCnstr(clause) - println(clause) + + val toCheckAgainstModels = expandLets(if (forValidity) negate(vc) else vc) + + reporter.info(" - Initial unrolling...") + val (basis, clauses, guards) = unrollingBank.closeUnrollings(toCheckAgainstModels) + // println("Here is what we want to check against models : " + toCheckAgainstModels) + // println("New basis: " + e) + // println("Bunch of clauses:\n" + se.map(_.toString).mkString("\n")) + // println("Bunch of blocking bools: " + sib.map(_.toString).mkString(", ")) + var foundDefinitiveAnswer : Boolean = false + var definitiveAnswer : Option[Boolean] = None + var definitiveModel : Map[Identifier,Expr] = Map.empty + + // println("Basis : " + basis) + z3.assertCnstr(toZ3Formula(z3, basis).get) + // println(toZ3Formula(z3, basis).get) + // for(clause <- clauses) { + // println("we're getting a new clause " + clause) + // z3.assertCnstr(toZ3Formula(z3, clause).get) + // } + z3.assertCnstr(toZ3Formula(z3, And(clauses)).get) + + var blockingSet : Set[Expr] = Set(guards.map(p => if(p._2) Not(Variable(p._1)) else Variable(p._1)) : _*) + + while(!foundDefinitiveAnswer) { + val blockingSetAsZ3 : Seq[Z3AST] = blockingSet.map(toZ3Formula(z3, _).get).toSeq + // println("Blocking set : " + blockingSet) + + z3.push + if(!blockingSetAsZ3.isEmpty) + z3.assertCnstr(z3.mkAnd(blockingSetAsZ3 : _*)) + + //z3.checkAssumptions(blockingSetAsZ3 : _*) match { + reporter.info(" - Running Z3 search...") + z3.checkAndGetModel() match { + // case (None, m, _) => { // UNKNOWN + case (None, m) => { // UNKNOWN + reporter.warning("Z3 doesn't know because: " + z3.getSearchFailure.message) + foundDefinitiveAnswer = true + definitiveAnswer = None + definitiveModel = Map.empty + m.delete + } + //case (Some(true), m, _) => { // SAT + case (Some(true), m) => { // SAT + import Evaluator._ + + val asMap = modelToMap(m, varsInVC) + lazy val modelAsString = asMap.toList.map(p => p._1 + " -> " + p._2).mkString("\n") + reporter.info("A candidate counter-example was found. It should be valid, but let's check anyway.") + reporter.info(modelAsString) + val evalResult = eval(asMap, toCheckAgainstModels) + + evalResult match { + case OK(BooleanLiteral(true)) => { + reporter.error("Counter-example found and confirmed:") + reporter.error(modelAsString) + foundDefinitiveAnswer = true + definitiveAnswer = Some(false) + definitiveModel = asMap + } + case other => { + //println(z3) + reporter.error("Something went wrong. The model should have been valid, yet we got this: " + other) + reporter.error(m) + foundDefinitiveAnswer = true + definitiveAnswer = None + definitiveModel = asMap + } + } + m.delete } + // case (Some(false), m, core) if core.isEmpty => { + // reporter.info("Empty core, definitively valid.") + // m.delete + // foundDefinitiveAnswer = true + // definitiveAnswer = Some(true) + // definitiveModel = Map.empty + // } + case (Some(false), m) if blockingSet.isEmpty => { + foundDefinitiveAnswer = true + definitiveAnswer = Some(true) + definitiveModel = Map.empty + } + // case (Some(false), m, core) => { + case (Some(false), m) => { + m.delete + val USEUNSATCORE = false + + z3.pop(1) + val (result2,m2) = z3.checkAndGetModel() + m2.delete + if (result2 == Some(false)) { + foundDefinitiveAnswer = true + definitiveAnswer = Some(true) + definitiveModel = Map.empty + } else { + reporter.info("We need to keep going.") + + val toRelease : Set[Expr] = if(USEUNSATCORE) { + blockingSet//core.map(fromZ3Formula(_)).toSet + } else { + blockingSet + } - var foundDefinitiveSolution = false + // println("Release set : " + toRelease) - val actualResult : (Option[Boolean],Map[Identifier,Expr]) = (z3.checkAndGetModel() match { - case (Some(true), m) => { - import Evaluator._ + blockingSet = blockingSet -- toRelease - // WE HAVE TO CHECK THE COUNTER-EXAMPLE. - val asMap = modelToMap(m, varsInVC) - lazy val modelAsString = asMap.toList.map(p => p._1 + " -> " + p._2).mkString("\n") - reporter.info(" - A candidate counter-example was found... Examining...") - reporter.info(modelAsString) + val toReleaseAsPairs : Set[(Identifier,Boolean)] = toRelease.map(b => b match { + case Variable(id) => (id, true) + case Not(Variable(id)) => (id, false) + case _ => scala.Predef.error("Impossible value in release set: " + b) + }) - val evalResult = eval(asMap, toCheckAgainstModels) - - evalResult match { - case OK(BooleanLiteral(true)) => { - reporter.error("Counter-example found and confirmed:") - reporter.error(modelAsString) - foundDefinitiveSolution = true - (Some(false), asMap) - } - case OK(BooleanLiteral(false)) => { - reporter.info("Not a valid counter-example. Must.. keep.. going..") - (None, asMap) - } - case InfiniteComputation() => { - reporter.info("Model seems to lead to divergent computation.") - reporter.error(modelAsString) - foundDefinitiveSolution = true - (None, asMap) - } - case RuntimeError(msg) => { - reporter.info("Model leads to runtime error: " + msg) - reporter.error(modelAsString) - foundDefinitiveSolution = true - (Some(false), asMap) - } - case t @ TypeError(_,_) => { - scala.Predef.error("Type error in model evaluation.\n" + t.msg) - } - case _ => { - scala.Predef.error("Why am I here?") - } + reporter.info(" - more unrollings") + for((id,polarity) <- toReleaseAsPairs) { + val (newClauses,newBlockers) = unrollingBank.unlock(id, !polarity) + // for(clause <- newClauses) { + // println("we're getting a new clause " + clause) + // z3.assertCnstr(toZ3Formula(z3, clause).get) + // } + + z3.assertCnstr(toZ3Formula(z3, And(newClauses)).get) + blockingSet = blockingSet ++ Set(newBlockers.map(p => if(p._2) Not(Variable(p._1)) else Variable(p._1)) : _*) } + + // scala.Predef.error("...but not quite now.") } - case (Some(false), _) => (Some(true), Map.empty) - case (None, m) => { - reporter.warning("Z3 doesn't know because: " + z3.getSearchFailure.message) - (None, Map.empty) - } - }) - actualResult + } } } - result + + (definitiveAnswer, definitiveModel) + + //var toUnroll : Set[FunctionInvocation] = Set.empty + //var everythingEverUnrolled : Set[FunctionInvocation] = Set.empty + //var blockingClause : List[Expr] = Nil + //val (basis, cls, bools) = clausifyITE(toCheckAgainstModels) + //var clauses = cls + + //toUnroll = toUnroll ++ topLevelFunctionCallsOf(basis) + + //println("The basis, with function calls " + topLevelFunctionCallsOf(basis)) + //println(basis) + //println("The clauses") + //for (clause <- clauses) { + // println(clause) + + // clause match { + // case Iff(Variable(switch), cond) => { + // toUnroll = toUnroll ++ topLevelFunctionCallsOf(cond) + // } + // case Implies(v @ Variable(switch), then) => { + // if(!topLevelFunctionCallsOf(then).isEmpty) { + // blockingClause = Not(v) :: blockingClause + // } + // } + // case Implies(Not(v @ Variable(switch)), elze) => { + // if(!topLevelFunctionCallsOf(elze).isEmpty) { + // blockingClause = v :: blockingClause + // } + // } + // case _ => scala.Predef.error("Something wrong happened. Who added this clause? " + clause) + // } + //} + + //// I'm conjecturing that this loop terminates :D + //while(!toUnroll.isEmpty) { + // println("Still to unroll now:") + // println(toUnroll) + // val copy = toUnroll + // toUnroll = Set.empty + + // for(tu <- copy) { + // val unrolled = unroll(tu) // this is a list of formulas + // for(formula <- unrolled) { + // val (basis2, clauses2, _) = clausifyITE(formula) + // println(tu + " unrolled gives us " + basis2 + "\n" + clauses2.map(_.toString).mkString("\n")) + // toUnroll = toUnroll ++ topLevelFunctionCallsOf(basis2) + // clauses = basis2 :: (clauses2 ::: clauses) + // for (clause <- clauses2) { + // clause match { + // case Iff(Variable(switch), cond) => { + // toUnroll = toUnroll ++ topLevelFunctionCallsOf(cond) + // } + // case Implies(v @ Variable(switch), then) => { + // if(!topLevelFunctionCallsOf(then).isEmpty) { + // blockingClause = Not(v) :: blockingClause + // } + // } + // case Implies(Not(v @ Variable(switch)), elze) => { + // if(!topLevelFunctionCallsOf(elze).isEmpty) { + // blockingClause = v :: blockingClause + // } + // } + // case _ => scala.Predef.error("Something wrong happened. Who added this clause? " + clause) + // } + // } + // } + // } + // everythingEverUnrolled = everythingEverUnrolled ++ copy + // toUnroll = toUnroll -- everythingEverUnrolled + //} + //println("The booleans.") + //println(bools) + + //println("The blocking clause.") + //println(And(blockingClause)) + + + //val toConvert = And(basis, And(clauses)) + + //val result : (Option[Boolean],Map[Identifier,Expr]) = toZ3Formula(z3, toConvert) match { + // case None => (None, Map.empty) // means it could not be translated + // case Some(z3f) => { + // z3.assertCnstr(z3f) + // //println(z3f) + + // //z3.assertCnstr(toZ3Formula(z3, And(blockingClause)).get) + + // var foundDefinitiveSolution = false + + // val blockingClauseAsZ3 : Seq[Z3AST] = blockingClause.map(toZ3Formula(z3, _).get) + + // val actualResult : (Option[Boolean],Map[Identifier,Expr]) = (z3.checkAssumptions(blockingClauseAsZ3 : _*) match { + // case (Some(true), m, _) => { + // import Evaluator._ + + // // WE HAVE TO CHECK THE COUNTER-EXAMPLE. + // val asMap = modelToMap(m, varsInVC) + // lazy val modelAsString = asMap.toList.map(p => p._1 + " -> " + p._2).mkString("\n") + // reporter.info(" - A candidate counter-example was found... Examining...") + // reporter.info(modelAsString) + + // val evalResult = eval(asMap, toCheckAgainstModels) + // + // evalResult match { + // case OK(BooleanLiteral(true)) => { + // reporter.error("Counter-example found and confirmed:") + // reporter.error(modelAsString) + // foundDefinitiveSolution = true + // (Some(false), asMap) + // } + // case OK(BooleanLiteral(false)) => { + // reporter.info("Not a valid counter-example. Must.. keep.. going..") + // (None, asMap) + // } + // case InfiniteComputation() => { + // reporter.info("Model seems to lead to divergent computation.") + // reporter.error(modelAsString) + // foundDefinitiveSolution = true + // (None, asMap) + // } + // case RuntimeError(msg) => { + // reporter.info("Model leads to runtime error: " + msg) + // reporter.error(modelAsString) + // foundDefinitiveSolution = true + // (Some(false), asMap) + // } + // case t @ TypeError(_,_) => { + // scala.Predef.error("Type error in model evaluation.\n" + t.msg) + // } + // case _ => { + // scala.Predef.error("Why am I here?") + // } + // } + // } + // case (Some(false), _, core) => { + // reporter.info("Here is the core : " + core.map(_.toString).mkString(", ")) + // (Some(true), Map.empty) + // } + // case (None, m, _) => { + // reporter.warning("Z3 doesn't know because: " + z3.getSearchFailure.message) + // (None, Map.empty) + // } + // }) + // actualResult + // } + //} + //result + } + + // Returns between 0 and 2 tautologies with respect to the interpretation of + // the function and its postcondition. + private def unroll(functionInvocation: FunctionInvocation) : List[Expr] = { + val FunctionInvocation(fd, args) = functionInvocation + val postExpr = if(fd.hasPostcondition) { + val post = expandLets(matchToIfThenElse(fd.postcondition.get)) + val substMap = Map[Expr,Expr]((fd.args.map(_.toVariable) zip args) : _*) + (ResultVariable() -> functionInvocation) + val newBody = /*partialEvaluator(*/replace(substMap, post)/*)*/ + List(newBody) + } else Nil + + val bodyExpr = if(fd.hasBody) { + val body = expandLets(matchToIfThenElse(fd.body.get)) + val substMap = Map[Expr,Expr]((fd.args.map(_.toVariable) zip args) : _*) + val newBody = /*partialEvaluator(*/replace(substMap, body)/*)*/ + List(Equals(functionInvocation, newBody)) + } else Nil + + postExpr ::: bodyExpr + } + + private def clausifyITE(formula : Expr) : (Expr, List[Expr], Set[Identifier]) = { + var newClauses : List[Expr] = Nil + var newBools : Set[Identifier] = Set.empty + + def clausify(ex : Expr) : Option[Expr] = ex match { + case ie @ IfExpr(cond, then, elze) => { + val switch = FreshIdentifier("path", true).setType(BooleanType).toVariable + val placeHolder = FreshIdentifier("iteval", true).setType(ie.getType).toVariable + newClauses = Iff(switch, cond) :: newClauses + newClauses = Implies(switch, Equals(placeHolder, then)) :: newClauses + newClauses = Implies(Not(switch), Equals(placeHolder, elze)) :: newClauses + newBools = newBools + switch.id + Some(placeHolder) + } + case _ => None + } + + val cleanedUp = searchAndReplaceDFS(clausify)(formula) + + (cleanedUp, newClauses.reverse, newBools) } protected[purescala] var exprToZ3Id : Map[Expr,Z3AST] = Map.empty protected[purescala] var z3IdToExpr : Map[Z3AST,Expr] = Map.empty - protected[purescala] def toZ3Formula(z3: Z3Context, expr: Expr, initialMap: Map[Identifier,Z3AST] = Map.empty, extraClauses : List[Z3AST] = Nil) : Option[(Z3AST, List[Z3AST])] = { + protected[purescala] def toZ3Formula(z3: Z3Context, expr: Expr, initialMap: Map[Identifier,Z3AST] = Map.empty) : Option[Z3AST] = { class CantTranslateException extends Exception val varsInformula: Set[Identifier] = variablesOf(expr) @@ -322,8 +581,6 @@ class FairZ3Solver(val reporter: Reporter) extends Solver(reporter) with Abstrac z3IdToExpr += (v -> k.toVariable) } - var accumulatedClauses : List[Z3AST] = extraClauses - def rec(ex: Expr): Z3AST = { //println("Stacking up call for:") //println(ex) @@ -346,26 +603,26 @@ class FairZ3Solver(val reporter: Reporter) extends Solver(reporter) with Abstrac case Some(ast) => ast case None => { if (id.isLetBinder) { - //scala.Predef.error("Error in formula being translated to Z3: identifier " + id + " seems to have escaped its let-definition") + scala.Predef.error("Error in formula being translated to Z3: identifier " + id + " seems to have escaped its let-definition") } - val newAST = z3.mkFreshConst(id.name, typeToSort(v.getType)) + val newAST = z3.mkFreshConst(id.uniqueName/*name*/, typeToSort(v.getType)) z3Vars = z3Vars + (id -> newAST) exprToZ3Id += (v -> newAST) z3IdToExpr += (newAST -> v) newAST } } - case ite @ IfExpr(c, t, e) => { - val switch = z3.mkFreshConst("path", z3.mkBoolSort) - val placeHolder = z3.mkFreshConst("ite", typeToSort(ite.getType)) - val clause1 = z3.mkIff(switch, rec(c)) - val clause2 = z3.mkImplies(switch, z3.mkEq(placeHolder, rec(t))) - val clause3 = z3.mkImplies(z3.mkNot(switch), z3.mkEq(placeHolder, rec(e))) + // case ite @ IfExpr(c, t, e) => { + // val switch = z3.mkFreshConst("path", z3.mkBoolSort) + // val placeHolder = z3.mkFreshConst("ite", typeToSort(ite.getType)) + // val clause1 = z3.mkIff(switch, rec(c)) + // val clause2 = z3.mkImplies(switch, z3.mkEq(placeHolder, rec(t))) + // val clause3 = z3.mkImplies(z3.mkNot(switch), z3.mkEq(placeHolder, rec(e))) - accumulatedClauses = clause3 :: clause2 :: clause1 :: accumulatedClauses + // accumulatedClauses = clause3 :: clause2 :: clause1 :: accumulatedClauses - placeHolder - } + // placeHolder + // } case And(exs) => z3.mkAnd(exs.map(rec(_)): _*) case Or(exs) => z3.mkOr(exs.map(rec(_)): _*) case Implies(l, r) => z3.mkImplies(rec(l), rec(r)) @@ -425,19 +682,11 @@ class FairZ3Solver(val reporter: Reporter) extends Solver(reporter) with Abstrac throw new CantTranslateException } }) - // println("Encoding of:") - // println(ex) - // println("...was encoded as:") - // println(recResult) recResult } try { - val res = Some((rec(expr), accumulatedClauses.reverse)) - // val usedInZ3Form = z3Vars.keys.toSet - // println("Variables in formula: " + varsInformula.map(_.uniqueName)) - // println("Variables passed to Z3: " + usedInZ3Form.map(_.uniqueName)) - + val res = Some(rec(expr)) res } catch { case e: CantTranslateException => None @@ -537,12 +786,122 @@ class FairZ3Solver(val reporter: Reporter) extends Solver(reporter) with Abstrac System.err.println(reverseFunctionMap.toSeq.mkString("\n")) System.err.println("REVERSE CONS MAP:") System.err.println(reverseADTConstructors.toSeq.mkString("\n")) - // System.exit(-1) throw new CantTranslateException(t) } } rec(tree) } + + // This remembers everything that was unrolled, which literal is blocking which calls, etc. + class UnrollingBank { + private val everythingEverUnrolled : MutableSet[FunctionInvocation] = MutableSet.empty + + // stores the invocations that boolean literals are guarding. + private val blocked : MutableMap[(Identifier,Boolean),Set[FunctionInvocation]] = MutableMap.empty + + def wasUnrolledBefore(functionInvocation : FunctionInvocation) : Boolean = { + everythingEverUnrolled(functionInvocation) + } + + private def registerAsUnrolled(functionInvocation : FunctionInvocation) : Unit = { + everythingEverUnrolled += functionInvocation + } + + def closeUnrollings(formula : Expr) : (Expr, Seq[Expr], Seq[(Identifier,Boolean)]) = { + var (basis, clauses, _) = clausifyITE(formula) + + var unrolledNow : Set[FunctionInvocation] = Set.empty + var stillToUnroll : Set[FunctionInvocation] = Set.empty + var treatedClauses : List[Expr] = Nil + var blockers : List[(Identifier,Boolean)] = Nil + + stillToUnroll = stillToUnroll ++ topLevelFunctionCallsOf(basis) + do { + // We go through each clause and figure out what must be enrolled and + // what must be blocked. We register everything. + for(clause <- clauses) { + clause match { + case Iff(Variable(_), cond) => { + stillToUnroll = stillToUnroll ++ topLevelFunctionCallsOf(cond) + } + // TODO : sort out the functions that are not recursive and unroll + // them in any case + case Implies(v @ Variable(id), then) => { + val calls = topLevelFunctionCallsOf(then) + if(!calls.isEmpty) { + assert(!blocked.isDefinedAt((id,true))) + blocked((id,true)) = calls + blockers = (id,true) :: blockers + } + } + case Implies(Not(v @ Variable(id)), elze) => { + val calls = topLevelFunctionCallsOf(elze) + if(!calls.isEmpty) { + assert(!blocked.isDefinedAt((id,false))) + blocked((id,false)) = calls + blockers = (id,false) :: blockers + } + } + case _ => scala.Predef.error("Who added this as a clause? " + clause) + } + treatedClauses = clause :: treatedClauses + } + + clauses = Nil + + while(!stillToUnroll.isEmpty) { + val copy = stillToUnroll + stillToUnroll = Set.empty + + for(stu <- (copy -- unrolledNow) if !wasUnrolledBefore(stu)) { + val unrolled : Seq[Expr] = unroll(stu) // that's between 0 and two formulas + registerAsUnrolled(stu) + unrolledNow = unrolledNow + stu + + for(formula <- unrolled) { + val (basis2, clauses2, _) = clausifyITE(formula) + stillToUnroll = stillToUnroll ++ topLevelFunctionCallsOf(basis2) + clauses = clauses2 ::: clauses + treatedClauses = basis2 :: treatedClauses + } + } + } + + } while(!clauses.isEmpty) + + (basis, treatedClauses.reverse, blockers.reverse) + } + + // if polarity == true, means that we used to have !id in the blocking set, + // and that now it's gone. As a result, we need to unroll everything that + // id is guarding. Similarly for polarity == false + def unlock(id: Identifier, polarity: Boolean) : (Seq[Expr], Seq[(Identifier,Boolean)]) = { + if(!blocked.isDefinedAt((id,polarity))) { + (Seq.empty,Seq.empty) + } else { + var newBlockers : Set[(Identifier,Boolean)] = Set.empty + var newClauses : List[Expr] = Nil + + val blockedSet = blocked((id,polarity)) + + for(functionInvocation <- blockedSet) { + val (_, clauses, blockers) = closeUnrollings(functionInvocation) + newClauses = newClauses ++ clauses + newBlockers = newBlockers ++ blockers + } + + (newClauses, newBlockers.toSeq) + } + } + + private var invocations : List[FunctionInvocation] = Nil + + def addInvocation(blocker: Identifier, polarity: Boolean, invocation: FunctionInvocation) : Unit = { + invocations = invocation :: invocations + } + + + } } diff --git a/src/purescala/Trees.scala b/src/purescala/Trees.scala index 0ffbd5311a7a85beeeb91f41550fde402346dc49..20d2d8f014cfac64424a9f9118ed31946e205afa 100644 --- a/src/purescala/Trees.scala +++ b/src/purescala/Trees.scala @@ -654,6 +654,19 @@ object Trees { treeCatamorphism(convert, combine, compute, expr) } + def topLevelFunctionCallsOf(expr: Expr) : Set[FunctionInvocation] = { + def convert(t: Expr) : Set[FunctionInvocation] = t match { + case f @ FunctionInvocation(_, _) => Set(f) + case _ => Set.empty + } + def combine(s1: Set[FunctionInvocation], s2: Set[FunctionInvocation]) = s1 ++ s2 + def compute(t: Expr, s: Set[FunctionInvocation]) = t match { + case f @ FunctionInvocation(_, _) => Set(f) // ++ s that's the difference with the one below + case _ => s + } + treeCatamorphism(convert, combine, compute, expr) + } + def functionCallsOf(expr: Expr) : Set[FunctionInvocation] = { def convert(t: Expr) : Set[FunctionInvocation] = t match { case f @ FunctionInvocation(_, _) => Set(f)