From 63477d6cc365eb0b2f6d56970e66841f7ea74152 Mon Sep 17 00:00:00 2001 From: Etienne Kneuss <colder@php.net> Date: Tue, 19 Mar 2013 17:12:17 +0100 Subject: [PATCH] Correct handling of choose in verification. - Choose expressions becomes uninterpreted functions under the same constraints. - Fix bug with variablesOf considering choose binders as free. - Silence evaluator errors when occuring with tentative lucky models. Note that choose expressions cannot be evaluated nor compiled. --- src/main/scala/leon/purescala/TreeOps.scala | 1 + .../scala/leon/solvers/z3/FairZ3Solver.scala | 12 ++++-- .../leon/solvers/z3/FunctionTemplate.scala | 13 ++++++- .../purescala/invalid/Choose1.scala | 39 +++++++++++++++++++ .../purescala/valid/Choose1.scala | 39 +++++++++++++++++++ 5 files changed, 99 insertions(+), 5 deletions(-) create mode 100644 src/test/resources/regression/verification/purescala/invalid/Choose1.scala create mode 100644 src/test/resources/regression/verification/purescala/valid/Choose1.scala diff --git a/src/main/scala/leon/purescala/TreeOps.scala b/src/main/scala/leon/purescala/TreeOps.scala index 8ec32c5d8..b70e88998 100644 --- a/src/main/scala/leon/purescala/TreeOps.scala +++ b/src/main/scala/leon/purescala/TreeOps.scala @@ -361,6 +361,7 @@ object TreeOps { def combine(s1: Set[Identifier], s2: Set[Identifier]) = s1 ++ s2 def compute(t: Expr, s: Set[Identifier]) = t match { case Let(i,_,_) => s -- Set(i) + case Choose(is,_) => s -- is case MatchExpr(_, cses) => s -- (cses.map(_.pattern.binders).foldLeft(Set[Identifier]())((a, b) => a ++ b)) case _ => s } diff --git a/src/main/scala/leon/solvers/z3/FairZ3Solver.scala b/src/main/scala/leon/solvers/z3/FairZ3Solver.scala index 59da55025..f736bf1fb 100644 --- a/src/main/scala/leon/solvers/z3/FairZ3Solver.scala +++ b/src/main/scala/leon/solvers/z3/FairZ3Solver.scala @@ -139,7 +139,7 @@ class FairZ3Solver(context : LeonContext) (solver.checkAssumptions(assumptions), solver.getModel, solver.getUnsatCore) } - private def validateModel(model: Z3Model, formula: Expr, variables: Set[Identifier]) : (Boolean, Map[Identifier,Expr]) = { + private def validateModel(model: Z3Model, formula: Expr, variables: Set[Identifier], silenceErrors: Boolean) : (Boolean, Map[Identifier,Expr]) = { if(!forceStop) { val functionsModel: Map[Z3FuncDecl, (Seq[(Seq[Z3AST], Z3AST)], Z3AST)] = model.getModelFuncInterpretations.map(i => (i._1, (i._2, i._3))).toMap @@ -185,7 +185,11 @@ class FairZ3Solver(context : LeonContext) (false, asMap) case EvaluationResults.EvaluatorError(msg) => - reporter.warning("Something went wrong. While evaluating the model, we got this : " + msg) + if (silenceErrors) { + reporter.info("- Model leads to evaluator error: " + msg) + } else { + reporter.warning("Something went wrong. While evaluating the model, we got this : " + msg) + } (false, asMap) } @@ -502,7 +506,7 @@ class FairZ3Solver(context : LeonContext) val z3model = solver.getModel if (this.checkModels) { - val (isValid, model) = validateModel(z3model, entireFormula, varsInVC) + val (isValid, model) = validateModel(z3model, entireFormula, varsInVC, silenceErrors = false) if (isValid) { foundAnswer(Some(true), model) @@ -588,7 +592,7 @@ class FairZ3Solver(context : LeonContext) if (this.feelingLucky && !forceStop) { // we might have been lucky :D luckyTime.start - val (wereWeLucky, cleanModel) = validateModel(solver.getModel, entireFormula, varsInVC) + val (wereWeLucky, cleanModel) = validateModel(solver.getModel, entireFormula, varsInVC, silenceErrors = true) luckyTime.stop if(wereWeLucky) { diff --git a/src/main/scala/leon/solvers/z3/FunctionTemplate.scala b/src/main/scala/leon/solvers/z3/FunctionTemplate.scala index 44963afa4..1c8ca1294 100644 --- a/src/main/scala/leon/solvers/z3/FunctionTemplate.scala +++ b/src/main/scala/leon/solvers/z3/FunctionTemplate.scala @@ -272,7 +272,18 @@ object FunctionTemplate { } } - case c @ Choose(_, _) => Variable(FreshIdentifier("choose", true).setType(c.getType)) + case c @ Choose(ids, cond) => + val cid = FreshIdentifier("choose", true).setType(c.getType) + exprVars += cid + + val m: Map[Expr, Expr] = if (ids.size == 1) { + Map(Variable(ids.head) -> Variable(cid)) + } else { + ids.zipWithIndex.map{ case (id, i) => Variable(id) -> TupleSelect(Variable(cid), i+1) }.toMap + } + + storeGuarded(pathVar, replace(m, cond)) + Variable(cid) case n @ NAryOperator(as, r) => r(as.map(a => rec(pathVar, a))).setType(n.getType) case b @ BinaryOperator(a1, a2, r) => r(rec(pathVar, a1), rec(pathVar, a2)).setType(b.getType) diff --git a/src/test/resources/regression/verification/purescala/invalid/Choose1.scala b/src/test/resources/regression/verification/purescala/invalid/Choose1.scala new file mode 100644 index 000000000..a0ac78daf --- /dev/null +++ b/src/test/resources/regression/verification/purescala/invalid/Choose1.scala @@ -0,0 +1,39 @@ +import leon.Annotations._ +import leon.Utils._ + +object Choose1 { + sealed abstract class List + case class Cons(head: Int, tail: List) extends List + case class Nil() extends List + + def size(l: List) : Int = (l match { + case Nil() => 0 + case Cons(_, t) => 1 + size(t) + }) ensuring(res => res >= 0) + + def content(l: List) : Set[Int] = l match { + case Nil() => Set.empty[Int] + case Cons(x, xs) => Set(x) ++ content(xs) + } + + def listOfSize(i: Int): List = { + require(i >= 0) + + if (i == 0) { + Nil() + } else { + choose { (res: List) => size(res) == i-1 } + } + } ensuring ( size(_) == i ) + + + def listOfSize2(i: Int): List = { + require(i >= 0) + + if (i > 0) { + Cons(0, listOfSize(i-1)) + } else { + Nil() + } + } ensuring ( size(_) == i ) +} diff --git a/src/test/resources/regression/verification/purescala/valid/Choose1.scala b/src/test/resources/regression/verification/purescala/valid/Choose1.scala new file mode 100644 index 000000000..620b87811 --- /dev/null +++ b/src/test/resources/regression/verification/purescala/valid/Choose1.scala @@ -0,0 +1,39 @@ +import leon.Annotations._ +import leon.Utils._ + +object Choose1 { + sealed abstract class List + case class Cons(head: Int, tail: List) extends List + case class Nil() extends List + + def size(l: List) : Int = (l match { + case Nil() => 0 + case Cons(_, t) => 1 + size(t) + }) ensuring(res => res >= 0) + + def content(l: List) : Set[Int] = l match { + case Nil() => Set.empty[Int] + case Cons(x, xs) => Set(x) ++ content(xs) + } + + def listOfSize(i: Int): List = { + require(i >= 0) + + if (i == 0) { + Nil() + } else { + choose { (res: List) => size(res) == i } + } + } ensuring ( size(_) == i ) + + + def listOfSize2(i: Int): List = { + require(i >= 0) + + if (i > 0) { + Cons(0, listOfSize(i-1)) + } else { + Nil() + } + } ensuring ( size(_) == i ) +} -- GitLab