-
Etienne Kneuss authored
Solvers now supports: - pop(lvl) - push - check - assertCnstr (assert would collapse with the usual assert()) - checkAssumptions - getModel - getUnsatCore This API is supported naively by all non-z3 solvers
Etienne Kneuss authoredSolvers now supports: - pop(lvl) - push - check - assertCnstr (assert would collapse with the usual assert()) - checkAssumptions - getModel - getUnsatCore This API is supported naively by all non-z3 solvers
FairZ3Solver.scala 26.37 KiB
package leon
package solvers.z3
import z3.scala._
import leon.solvers.Solver
import purescala.Common._
import purescala.Definitions._
import purescala.Trees._
import purescala.Extractors._
import purescala.TreeOps._
import purescala.TypeTrees._
import scala.collection.mutable.{Map => MutableMap}
import scala.collection.mutable.{Set => MutableSet}
class FairZ3Solver(context : LeonContext) extends Solver(context) with AbstractZ3Solver with Z3ModelReconstruction {
// have to comment this to use the solver for constraint solving...
// assert(Settings.useFairInstantiator)
private val UNKNOWNASSAT : Boolean = !Settings.noForallAxioms
val description = "Fair Z3 Solver"
override val shortDescription = "Z3-f"
// this is fixed
protected[leon] val z3cfg = new Z3Config(
"MODEL" -> true,
"MBQI" -> false,
// "SOFT_TIMEOUT" -> 100,
"TYPE_CHECK" -> true,
"WELL_SORTED_CHECK" -> true
)
toggleWarningMessages(true)
private var unrollingBank: UnrollingBank = null
private var blockingSet: Set[Expr] = Set.empty
private var toCheckAgainstModels: Expr = BooleanLiteral(true)
private var varsInVC: Set[Identifier] = Set.empty
override def restartZ3 : Unit = {
super.restartZ3
unrollingBank = new UnrollingBank
blockingSet = Set.empty
toCheckAgainstModels = BooleanLiteral(true)
varsInVC = Set.empty
}
def isKnownDef(funDef: FunDef) : Boolean = functionMap.isDefinedAt(funDef)
def functionDefToDecl(funDef: FunDef) : Z3FuncDecl =
functionMap.getOrElse(funDef, scala.sys.error("No Z3 definition found for function symbol " + funDef.id.name + "."))
def isKnownDecl(decl: Z3FuncDecl) : Boolean = reverseFunctionMap.isDefinedAt(decl)
def functionDeclToDef(decl: Z3FuncDecl) : FunDef =
reverseFunctionMap.getOrElse(decl, scala.sys.error("No FunDef corresponds to Z3 definition " + decl + "."))
private var functionMap: Map[FunDef, Z3FuncDecl] = Map.empty
private var reverseFunctionMap: Map[Z3FuncDecl, FunDef] = Map.empty
private var axiomatizedFunctions : Set[FunDef] = Set.empty
def prepareFunctions: Unit = {
functionMap = Map.empty
reverseFunctionMap = Map.empty
for (funDef <- program.definedFunctions) {
val sortSeq = funDef.args.map(vd => typeToSort(vd.tpe))
val returnSort = typeToSort(funDef.returnType)
val z3Decl = z3.mkFreshFuncDecl(funDef.id.name, sortSeq, returnSort)
functionMap = functionMap + (funDef -> z3Decl)
reverseFunctionMap = reverseFunctionMap + (z3Decl -> funDef)
}
if(!Settings.noForallAxioms) {
prepareAxioms
}
for(funDef <- program.definedFunctions) {
if (funDef.annotations.contains("axiomatize") && !axiomatizedFunctions(funDef)) {
reporter.warning("Function " + funDef.id + " was marked for axiomatization but could not be handled.")
}
}
}
private def prepareAxioms : Unit = {
assert(!Settings.noForallAxioms)
program.definedFunctions.foreach(_ match {
case fd @ Catamorphism(acd, cases) => {
assert(!fd.hasPrecondition && fd.hasImplementation)
reporter.info("Will attempt to axiomatize the function definition:")
reporter.info(fd)
for(cse <- cases) {
val (cc @ CaseClass(ccd, args), expr) = cse
assert(args.forall(_.isInstanceOf[Variable]))
val argsAsIDs = args.map(_.asInstanceOf[Variable].id)
assert(variablesOf(expr) -- argsAsIDs.toSet == Set.empty)
val axiom : Z3AST = if(args.isEmpty) {
val eq = Equals(FunctionInvocation(fd, Seq(cc)), expr)
toZ3Formula(eq).get
} else {
val z3ArgSorts = argsAsIDs.map(i => typeToSort(i.getType))
val boundVars = z3ArgSorts.zipWithIndex.map(p => z3.mkBound(p._2, p._1))
val map : Map[Identifier,Z3AST] = (argsAsIDs zip boundVars).toMap
val eq = Equals(FunctionInvocation(fd, Seq(cc)), expr)
val z3IzedEq = toZ3Formula(eq, map).get
val z3IzedCC = toZ3Formula(cc, map).get
val pattern = z3.mkPattern(functionDefToDecl(fd)(z3IzedCC))
val nameTypePairs = z3ArgSorts.map(s => (z3.mkFreshIntSymbol, s))
z3.mkForAll(0, List(pattern), nameTypePairs, z3IzedEq)
}
// println("I'll assert now an axiom: " + axiom)
// println("Case axiom:")
// println(axiom)
solver.assertCnstr(axiom)
}
if(fd.hasPostcondition) {
// we know it doesn't contain any function invocation
val cleaned = matchToIfThenElse(expandLets(fd.postcondition.get))
val argSort = typeToSort(fd.args(0).getType)
val bound = z3.mkBound(0, argSort)
val subst = replace(Map(ResultVariable() -> FunctionInvocation(fd, Seq(fd.args(0).toVariable))), cleaned)
val z3IzedPost = toZ3Formula(subst, Map(fd.args(0).id -> bound)).get
val pattern = z3.mkPattern(functionDefToDecl(fd)(bound))
val nameTypePairs = Seq((z3.mkFreshIntSymbol, argSort))
val postAxiom = z3.mkForAll(0, List(pattern), nameTypePairs, z3IzedPost)
//println("Post axiom:")
//println(postAxiom)
solver.assertCnstr(postAxiom)
}
axiomatizedFunctions += fd
}
case _ => ;
})
}
override def solve(vc: Expr) = decide(vc, true)
override def solveSAT(vc : Expr) : (Option[Boolean],Map[Identifier,Expr]) = {
restartZ3
val (res, model, core) = decideWithModel(vc, false)
(res, model)
}
def decide(vc: Expr, forValidity: Boolean):Option[Boolean] = {
restartZ3
decideWithModel(vc, forValidity)._1
}
private var foundDefinitiveAnswer : Boolean = false
override def halt() {
if(!foundDefinitiveAnswer) {
super.halt
if(z3 != null)
z3.softCheckCancel
}
}
override def getModel = {
modelToMap(solver.getModel, varsInVC)
}
override def getUnsatCore = {
solver.getUnsatCore.map(ast => fromZ3Formula(null, ast, None) match {
case n @ Not(Variable(_)) => n
case v @ Variable(_) => v
case x => scala.sys.error("Impossible element extracted from core: " + ast + " (as Leon tree : " + x + ")")
}).toSet
}
override def solveSATWithCores(expression: Expr, assumptions: Set[Expr]): (Option[Boolean], Map[Identifier, Expr], Set[Expr]) = {
restartZ3
decideWithModel(expression, false, None, Some(assumptions))
}
private[this] def decideWithModel(vc: Expr, forValidity: Boolean, evaluator: Option[(Map[Identifier,Expr]) => Boolean] = None, assumptions: Option[Set[Expr]] = None): (Option[Boolean], Map[Identifier,Expr], Set[Expr]) = {
val initializationStopwatch = new Stopwatch("init", false)
val blocking2Z3Stopwatch = new Stopwatch("blocking-set-to-z3", false)
val z3SearchStopwatch = new Stopwatch("z3-search-1", false)
val secondZ3SearchStopwatch = new Stopwatch("z3-search-2", false)
val unrollingStopwatch = new Stopwatch("unrolling", false)
val luckyStopwatch = new Stopwatch("lucky", false)
val validatingStopwatch = new Stopwatch("validating", false)
val decideTopLevelSw = new Stopwatch("top-level", false).start
// println("Deciding : " + vc)
assumptions match {
case Some(set) => {
if (Settings.useCores)
throw new Exception("Use of cores while checking assumptions is not supported")
if (set.exists(e => e match {
case Variable(_) => false
case Not(Variable(_)) => false
case _ => true
}))
throw new Exception("assumptions may only be boolean literals")
}
case None =>
}
initializationStopwatch.start
foundDefinitiveAnswer = false
var definitiveAnswer : Option[Boolean] = None
var definitiveModel : Map[Identifier,Expr] = Map.empty
var definitiveCore : Set[Expr] = Set.empty
def foundAnswer(answer : Option[Boolean], model : Map[Identifier,Expr] = Map.empty, core: Set[Expr] = Set.empty) : Unit = {
foundDefinitiveAnswer = true
definitiveAnswer = answer
definitiveModel = model
definitiveCore = core
//timer.foreach(t => t.halt)
}
if (program == null) {
reporter.error("Z3 Solver was not initialized with a PureScala Program.")
None
}
// naive implementation of unrolling everything every n-th iteration
val unrollingThreshold = 100
var unrollingCounter = 0
val expandedVC = expandLets(if (forValidity) negate(vc) else vc)
toCheckAgainstModels = And(toCheckAgainstModels,expandedVC)
varsInVC ++= variablesOf(expandedVC)
reporter.info(" - Initial unrolling...")
val (clauses, guards) = unrollingBank.initialUnrolling(expandedVC)
val cc = toZ3Formula(And(clauses)).get
solver.assertCnstr(cc)
// these are the optional sequence of assumption literals
val assumptionsAsZ3: Option[Seq[Z3AST]] = assumptions.map(set => set.toSeq.map(toZ3Formula(_).get))
blockingSet ++= Set(guards.map(p => if(p._2) Not(Variable(p._1)) else Variable(p._1)) : _*)
var iterationsLeft : Int = if(Settings.unrollingLevel > 0) Settings.unrollingLevel else 16121984
initializationStopwatch.stop
while(!foundDefinitiveAnswer && !forceStop) {
iterationsLeft -= 1
blocking2Z3Stopwatch.start
val blockingSetAsZ3 : Seq[Z3AST] = blockingSet.toSeq.map(toZ3Formula(_).get)
// println("Blocking set : " + blockingSet)
blocking2Z3Stopwatch.stop
solver.push
if(!blockingSetAsZ3.isEmpty)
solver.assertCnstr(z3.mkAnd(blockingSetAsZ3 : _*))
reporter.info(" - Running Z3 search...")
val answerModelCore : (Option[Boolean], Z3Model, Set[Expr]) = {
z3SearchStopwatch.start
val res = assumptionsAsZ3 match {
case Some(as) => solver.checkAssumptions(as: _*)
case None => solver.check()
}
val z3model = res match {
case Some(true) => solver.getModel
case _ => null
}
val z3core: Seq[Z3AST] = (res, assumptionsAsZ3) match {
case (Some(false), ass) if ass.isDefined => solver.getUnsatCore.toSeq
case _ => Seq()
}
z3SearchStopwatch.stop
val core: Set[Expr] = z3core.map(ast => fromZ3Formula(z3model, ast, Some(BooleanType)) match {
case n @ Not(Variable(_)) => n
case v @ Variable(_) => v
case x => scala.sys.error("Impossible element extracted from core: " + ast + " (as Leon tree : " + x + ")")
}).toSet
(res, z3model, core)
}
val (answer, model, core) = answerModelCore // to work around the stupid type inferer
reporter.info(" - Finished search with blocked literals")
// if (Settings.useCores)
// reporter.info(" - Core is : " + core)
val reinterpretedAnswer = if(!UNKNOWNASSAT) answer else (answer match {
case None | Some(true) => Some(true)
case Some(false) => Some(false)
})
(reinterpretedAnswer, model) match {
case (None, m) => { // UNKNOWN
// reporter.warning("Z3 doesn't know because: " + z3.getSearchFailure.message)
reporter.warning("Z3 doesn't know because ??")
foundAnswer(None)
m.delete
}
case (Some(true), m) => { // SAT
validatingStopwatch.start
val (trueModel, model) = if(Settings.verifyModel)
validateAndDeleteModel(m, toCheckAgainstModels, varsInVC, evaluator)
else {
val res = (true, modelToMap(m, varsInVC))
lazy val modelAsString = res._2.toList.map(p => p._1 + " -> " + p._2).mkString("\n")
reporter.info("- Found a model:")
reporter.info(modelAsString)
res
}
validatingStopwatch.stop
if (trueModel) {
foundAnswer(Some(false), model)
} else {
reporter.error("Something went wrong. The model should have been valid, yet we got this : ")
reporter.error(model)
foundAnswer(None, model)
}
}
case (Some(false), m) if blockingSet.isEmpty => {
foundAnswer(Some(true), core = core)
solver.pop(1)
}
// This branch is both for with and without unsat cores. The
// distinction is made inside.
case (Some(false), m) => {
//m.delete
if(!Settings.useCores)
solver.pop(1)
if (Settings.luckyTest && !forceStop) {
// we need the model to perform the additional test
reporter.info(" - Running search without blocked literals (w/ lucky test)")
secondZ3SearchStopwatch.start
val res2 = assumptionsAsZ3 match {
case Some(as) => solver.checkAssumptions(as: _*)
case None => solver.check()
}
val z3model2 = res2 match {
case Some(true) => solver.getModel
case _ => null
}
val z3core2: Seq[Z3AST] = (res2, assumptionsAsZ3) match {
case (Some(false), ass) if ass.isDefined => solver.getUnsatCore.toSeq
case _ => Seq()
}
secondZ3SearchStopwatch.stop
reporter.info(" - Finished search without blocked literals")
if (res2 == Some(false)) {
val core2: Set[Expr] = z3core2.map(ast => fromZ3Formula(z3model2, ast, Some(BooleanType)) match {
case n @ Not(Variable(_)) => n
case v @ Variable(_) => v
case x => scala.sys.error("Impossible element extracted from core: " + ast + " (as Leon tree : " + x + ")")
}).toSet
foundAnswer(Some(true), core = core2)
} else {
luckyStopwatch.start
// we might have been lucky :D
val (wereWeLucky, cleanModel) = validateAndDeleteModel(z3model2, toCheckAgainstModels, varsInVC, evaluator)
if(wereWeLucky) {
foundAnswer(Some(false), cleanModel)
}
luckyStopwatch.stop
}
} else {
// only checking will suffice
reporter.info(" - Running search without blocked literals (w/o lucky test)")
secondZ3SearchStopwatch.start
val result2 = assumptionsAsZ3 match {
case Some(as) => solver.checkAssumptions(as: _*)
case None => solver.check()
}
secondZ3SearchStopwatch.stop
reporter.info(" - Finished search without blocked literals")
if (result2 == Some(false)) {
val z3model = solver.getModel()
val core: Set[Expr] = solver.getUnsatCore.map(ast => fromZ3Formula(z3model, ast, Some(BooleanType)) match {
case n @ Not(Variable(_)) => n
case v @ Variable(_) => v
case x => scala.sys.error("Impossible element extracted from core: " + ast + " (as Leon tree : " + x + ")")
}).toSet
foundAnswer(Some(true), core = core)
}
}
if(forceStop) {
foundAnswer(None)
}
if(!foundDefinitiveAnswer) {
reporter.info("- We need to keep going.")
unrollingStopwatch.start
val toRelease : Set[Expr] = blockingSet
// reporter.info(" - toRelease : " + toRelease)
// reporter.info(" - blockingSet : " + blockingSet)
var fixedForEver : Set[Expr] = Set.empty
if(Settings.pruneBranches) {
for(ex <- blockingSet) ex match {
case Not(Variable(b)) => {
solver.push
solver.assertCnstr(toZ3Formula(Variable(b)).get)
solver.check match {
case Some(false) => {
//println("We had ~" + b + " in the blocking set. We now know it should stay there forever.")
solver.pop(1)
solver.assertCnstr(toZ3Formula(Not(Variable(b))).get)
fixedForEver = fixedForEver + ex
}
case _ => solver.pop(1)
}
}
case Variable(b) => {
solver.push
solver.assertCnstr(toZ3Formula(Not(Variable(b))).get)
solver.check match {
case Some(false) => {
//println("We had " + b + " in the blocking set. We now know it should stay there forever.")
solver.pop(1)
solver.assertCnstr(toZ3Formula(Variable(b)).get)
fixedForEver = fixedForEver + ex
}
case _ => solver.pop(1)
}
}
case _ => scala.sys.error("Should not have happened.")
}
if(fixedForEver.size > 0) {
reporter.info("- Pruned out " + fixedForEver.size + " branches.")
}
}
blockingSet = blockingSet -- toRelease
val toReleaseAsPairs : Set[(Identifier,Boolean)] = (toRelease -- fixedForEver).map(b => b match {
case Variable(id) => (id, true)
case Not(Variable(id)) => (id, false)
case _ => scala.sys.error("Impossible value in release set: " + b)
})
reporter.info(" - more unrollings")
for((id,polarity) <- toReleaseAsPairs) {
val (newClauses,newBlockers) = unrollingBank.unlock(id, !polarity)
for(ncl <- newClauses) {
solver.assertCnstr(toZ3Formula(ncl).get)
}
blockingSet = blockingSet ++ Set(newBlockers.map(p => if(p._2) Not(Variable(p._1)) else Variable(p._1)) : _*)
}
reporter.info(" - finished unrolling")
unrollingStopwatch.stop
}
}
}
if(iterationsLeft <= 0) {
reporter.error(" - Max. number of iterations reached.")
foundAnswer(None)
}
}
decideTopLevelSw.stop
decideTopLevelSw.writeToSummary
initializationStopwatch.writeToSummary
blocking2Z3Stopwatch.writeToSummary
z3SearchStopwatch.writeToSummary
secondZ3SearchStopwatch.writeToSummary
unrollingStopwatch.writeToSummary
luckyStopwatch.writeToSummary
validatingStopwatch.writeToSummary
if(forceStop) {
(None, Map.empty, Set.empty)
} else {
val realAnswer = if(forValidity) {
definitiveAnswer
} else {
definitiveAnswer.map(!_)
}
(realAnswer, definitiveModel, definitiveCore)
}
}
private def validateAndDeleteModel(model: Z3Model, formula: Expr, variables: Set[Identifier], evaluator: Option[(Map[Identifier,Expr])=>Boolean]) : (Boolean, Map[Identifier,Expr]) = {
import Evaluator._
if(!forceStop) {
val functionsModel: Map[Z3FuncDecl, (Seq[(Seq[Z3AST], Z3AST)], Z3AST)] = model.getModelFuncInterpretations.map(i => (i._1, (i._2, i._3))).toMap
val functionsAsMap: Map[Identifier, Expr] = functionsModel.flatMap(p => {
if(isKnownDecl(p._1)) {
val fd = functionDeclToDef(p._1)
if(!fd.hasImplementation) {
val (cses, default) = p._2
val ite = cses.foldLeft(fromZ3Formula(model, default, Some(fd.returnType)))((expr, q) => IfExpr(
And(
q._1.zip(fd.args).map(a12 => Equals(fromZ3Formula(model, a12._1, Some(a12._2.tpe)), Variable(a12._2.id)))
),
fromZ3Formula(model, q._2, Some(fd.returnType)),
expr))
Seq((fd.id, ite))
} else Seq()
} else Seq()
}).toMap
val constantFunctionsAsMap: Map[Identifier, Expr] = model.getModelConstantInterpretations.flatMap(p => {
if(isKnownDecl(p._1)) {
val fd = functionDeclToDef(p._1)
if(!fd.hasImplementation) {
Seq((fd.id, fromZ3Formula(model, p._2, Some(fd.returnType))))
} else Seq()
} else Seq()
}).toMap
val asMap = modelToMap(model, variables) ++ functionsAsMap ++ constantFunctionsAsMap
model.delete
lazy val modelAsString = asMap.toList.map(p => p._1 + " -> " + p._2).mkString("\n")
val evalResult = eval(asMap, formula, evaluator)
evalResult match {
case OK(BooleanLiteral(true)) => {
reporter.info("- Model validated:")
reporter.info(modelAsString)
(true, asMap)
}
case RuntimeError(msg) => {
reporter.info("- Model leads to runtime error: " + msg)
reporter.error(modelAsString)
(true, asMap)
}
case OK(BooleanLiteral(false)) => {
reporter.info("- Invalid model.")
(false, asMap)
}
case ImpossibleComputation() => {
reporter.info("- Invalid Model: the model could not be verified because of insufficient information.")
(false, asMap)
}
case other => {
reporter.error("Something went wrong. While evaluating the model, we got this: " + other)
(false, asMap)
}
}
} else {
(false, Map.empty)
}
}
class UnrollingBank {
private val blockMap : MutableMap[(Identifier,Boolean),Set[FunctionInvocation]] = MutableMap.empty
private def registerBlocked(id: Identifier, pol: Boolean, fi: FunctionInvocation) : Boolean =
registerBlocked(id,pol,Set(fi))
private def registerBlocked(id: Identifier, pol: Boolean, fis: Set[FunctionInvocation]) : Boolean = {
val filtered = fis.filter(i => {
val FunctionInvocation(fd, _) = i
!axiomatizedFunctions(fd)
})
if(!filtered.isEmpty) {
val pair = (id, pol)
val alreadyBlocked = blockMap.get(pair)
alreadyBlocked match {
case None => blockMap(pair) = filtered
case Some(prev) => blockMap(pair) = prev ++ filtered
}
true
} else {
false
}
}
private def treatFunctionInvocationSet(sVar : Identifier, pol : Boolean, fis : Set[FunctionInvocation]) : (Seq[Expr],Seq[(Identifier,Boolean)]) = {
val allBlocks : MutableSet[(Identifier,Boolean)] = MutableSet.empty
var allNewExprs : Seq[Expr] = Seq.empty
for(fi <- fis) {
val temp = FunctionTemplate.mkTemplate(fi.funDef)
val (newExprs,newBlocks) = temp.instantiate(sVar, pol, fi.args)
for(((i,p),fis) <- newBlocks) {
if(registerBlocked(i,p,fis)) {
allBlocks += ((i,p))
}
}
allNewExprs = allNewExprs ++ newExprs
}
(allNewExprs, allBlocks.toSeq)
}
// This is called just once, for the "initial unrolling". FIXME: TODO:
// Wouldn't it be better/more uniform to pretend the initial formula is a
// function and generate a template for it?
def initialUnrolling(formula : Expr) : (Seq[Expr], Seq[(Identifier,Boolean)]) = {
initialUnrolling0(formula)
}
def unlock(id: Identifier, pol: Boolean) : (Seq[Expr], Seq[(Identifier,Boolean)]) = {
if(!blockMap.isDefinedAt((id,pol))) {
(Seq.empty,Seq.empty)
} else {
val copy = blockMap((id,pol))
blockMap((id,pol)) = Set.empty
treatFunctionInvocationSet(id, pol, copy)
}
}
//this is mostly copied from FunctionTemplate. This is sort of a quick hack to the problem
//of the initial unrolling
def initialUnrolling0(formula: Expr): (Seq[Expr], Seq[(Identifier,Boolean)]) = {
var guardedExprs : Map[(Identifier,Boolean),Seq[Expr]] = Map.empty
def storeGuarded(guardVar : Identifier, guardPol : Boolean, expr : Expr) : Unit = {
assert(expr.getType == BooleanType)
val p : (Identifier,Boolean) = (guardVar,guardPol)
if(guardedExprs.isDefinedAt(p)) {
val prev : Seq[Expr] = guardedExprs(p)
guardedExprs += (p -> (expr +: prev))
} else {
guardedExprs += (p -> Seq(expr))
}
}
def rec(pathVar : Identifier, pathPol : Boolean, expr : Expr) : Expr = {
expr match {
case l : Let => sys.error("Let's should have been eliminated.")
case m : MatchExpr => sys.error("MatchExpr's should have been eliminated.")
case i @ IfExpr(cond, then, elze) => {
if(!containsFunctionCalls(cond) && !containsFunctionCalls(then) && !containsFunctionCalls(elze)) {
i
} else {
val newBool : Identifier = FreshIdentifier("b", true).setType(BooleanType)
val newExpr : Identifier = FreshIdentifier("e", true).setType(i.getType)
val crec = rec(pathVar, pathPol, cond)
val trec = rec(newBool, true, then)
val erec = rec(newBool, false, elze)
storeGuarded(pathVar, pathPol, Iff(Variable(newBool), crec))
storeGuarded(newBool, true, Equals(Variable(newExpr), trec))
storeGuarded(newBool, false, Equals(Variable(newExpr), erec))
Variable(newExpr)
}
}
case n @ NAryOperator(as, r) => r(as.map(a => rec(pathVar, pathPol, a))).setType(n.getType)
case b @ BinaryOperator(a1, a2, r) => r(rec(pathVar, pathPol, a1), rec(pathVar, pathPol, a2)).setType(b.getType)
case u @ UnaryOperator(a, r) => r(rec(pathVar, pathPol, a)).setType(u.getType)
case t : Terminal => t
case a : AnonymousFunction => {
Settings.reporter.warning("AnonymousFunction literal showed up in the construction of a FunctionTemplate.")
a
}
}
}
val startingVar : Identifier = FreshIdentifier("start", true).setType(BooleanType)
storeGuarded(startingVar, false, BooleanLiteral(false))
val newFormula = rec(startingVar, true, formula)
storeGuarded(startingVar, true, newFormula)
val asClauses : Seq[Expr] = {
(for(((b,p),es) <- guardedExprs; e <- es) yield {
Implies(if(!p) Not(Variable(b)) else Variable(b), e)
}).toSeq
}
val blockers : Map[(Identifier,Boolean),Set[FunctionInvocation]] = {
Map((for((p, es) <- guardedExprs) yield {
val calls = es.foldLeft(Set.empty[FunctionInvocation])((s,e) => s ++ functionCallsOf(e))
if(calls.isEmpty) {
None
} else {
registerBlocked(p._1, p._2, calls)
Some((p, calls))
}
}).flatten.toSeq : _*)
}
(asClauses, blockers.keys.toSeq)
}
}
}