From b996655b2af5e5893b77b76749bc17e039f38c1e Mon Sep 17 00:00:00 2001 From: Philippe Suter <philippe.suter@gmail.com> Date: Sat, 12 Jun 2010 12:59:46 +0000 Subject: [PATCH] First "program" was just verified ! --- build.xml | 9 ++--- lib/z3.jar | Bin 0 -> 25901 bytes s-f | 6 ++++ src/purescala/Analysis.scala | 66 +++++++++++++++++++++++++++++++++- src/purescala/Trees.scala | 39 +++++++++++++++++--- testcases/IntOperations.scala | 6 ++++ 6 files changed, 117 insertions(+), 9 deletions(-) create mode 100644 lib/z3.jar create mode 100755 s-f create mode 100644 testcases/IntOperations.scala diff --git a/build.xml b/build.xml index 36a4dbc40..9d8f97144 100644 --- a/build.xml +++ b/build.xml @@ -60,8 +60,9 @@ <path id="build.path"> <path refid="scalac.class.path"/> - <path refid="funcheck.lib.path"/> - <pathelement location="${build.plugin.funcheck.dir}" /> + <!-- <path refid="funcheck.lib.path"/> --> + <pathelement location="${lib.dir}/z3.jar"/> + <!-- <pathelement location="${build.plugin.funcheck.dir}" /> --> </path> <!-- @@ -105,7 +106,7 @@ <mkdir dir="${build.plugin.funcheck.dir}" /> <scalac srcdir="${sources.dir}" destdir="${build.plugin.funcheck.dir}" force="changed" addparams="${scalac.default.params}"> <classpath> - <path refid="scalac.class.path" /> + <path refid="build.path" /> </classpath> <exclude name="scala/collection/**/*"/> <exclude name="funcheck/lib/**/*"/> @@ -127,7 +128,7 @@ <fileset file="./scalac-plugin.xml" /> </jar> - <echo file="${script.file}" message="#!/bin/sh${line.separator}scalac -Xplugin:${dist.jar} $@${line.separator}" /> + <echo file="${script.file}" message="#!/bin/sh${line.separator}scalac -cp ./lib/z3.jar -Xplugin:${dist.jar} $@${line.separator}" /> <chmod file="${script.file}" perm="u+x" /> </target> diff --git a/lib/z3.jar b/lib/z3.jar new file mode 100644 index 0000000000000000000000000000000000000000..2fd797b68a3ae3d5a17bad12f77b857b7e2b4ef3 GIT binary patch literal 25901 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 DM^e=H literal 0 HcmV?d00001 diff --git a/s-f b/s-f new file mode 100755 index 000000000..0e04980aa --- /dev/null +++ b/s-f @@ -0,0 +1,6 @@ +#!/bin/sh +SCALALIBDIR=/home/psuter/scala/current/lib +DISTDIR=/home/psuter/guru-svn/funcheck/dist +LIBDIR=/home/psuter/guru-svn/funcheck/lib +SCALAHOME=/home/psuter/scala/current +java -Dscala.home=${SCALAHOME} -classpath ${SCALALIBDIR}/scala-compiler.jar:${SCALALIBDIR}/scala-library.jar:${DISTDIR}/funcheck-plugin.jar:${LIBDIR}/z3.jar scala.tools.nsc.Main -Xplugin:${DISTDIR}/funcheck-plugin.jar $@ diff --git a/src/purescala/Analysis.scala b/src/purescala/Analysis.scala index e889f1f3f..f01145dae 100644 --- a/src/purescala/Analysis.scala +++ b/src/purescala/Analysis.scala @@ -1,7 +1,10 @@ package purescala +import Common._ import Definitions._ import Trees._ +import TypeTrees._ +import z3.scala._ object Analysis { // Analysis should check that: @@ -13,12 +16,19 @@ object Analysis { // - injective functions are injective // - all global invariants are satisfied def analyze(program: Program) : Unit = { + val z3cfg = new Z3Config() + //z3cfg.setParamValue("MODEL", "TRUE") + val z3 = new Z3Context(z3cfg) + program.mainObject.defs.filter(_.isInstanceOf[FunDef]).foreach(df => { val funDef = df.asInstanceOf[FunDef] val vc = postconditionVC(funDef) if(vc != BooleanLiteral(true)) { println("Verification condition for " + funDef.id + ":") println(vc) + val (z3f,stupidMap) = toZ3Formula(z3, vc) + z3.assertCnstr(z3.mkNot(z3f)) + println("Valid? " + z3.check()) } }) } @@ -30,7 +40,7 @@ object Analysis { if(post.isEmpty) { BooleanLiteral(true) } else { - BooleanLiteral(false) + replaceInExpr(Map(ResultVariable() -> functionDefinition.body), post.get) } } @@ -41,6 +51,60 @@ object Analysis { case _ => throw new Exception("ah ha !") } + (expr, Nil) + } + + def replaceInExpr(substs: Map[Expr,Expr], expr: Expr) : Expr = { + def rec(ex: Expr) : Expr = ex match { + case _ if (substs.get(ex).isDefined) => substs(ex) + case FunctionInvocation(fd, args) => FunctionInvocation(fd, args.map(rec(_))) + case IfExpr(t1,t2,t3) => IfExpr(rec(t1),rec(t2),rec(t3)) + case MatchExpr(_,_) => ex + case And(exs) => And(exs.map(rec(_))) + case Or(exs) => Or(exs.map(rec(_))) + case Not(e) => Not(rec(e)) + case BinaryOperator(t1,t2,recons) => recons(rec(t1),rec(t2)) + case _ => ex + } + + rec(expr) } + def toZ3Formula(z3: Z3Context, expr: Expr) : (Z3AST,Map[Identifier,Z3AST]) = { + val intSort = z3.mkIntSort() + var varMap: Map[Identifier,Z3AST] = Map.empty + + def rec(ex: Expr) : Z3AST = ex match { + case v @ Variable(id) => varMap.get(id) match { + case Some(ast) => ast + case None => { + assert(v.getType == Int32Type) + val newAST = z3.mkConst(z3.mkStringSymbol(id.name), intSort) + varMap = varMap + (id -> newAST) + newAST + } + } + case IfExpr(c,t,e) => z3.mkITE(rec(c), rec(t), rec(e)) + case And(exs) => z3.mkAnd(exs.map(rec(_)) : _*) + case Or(exs) => z3.mkOr(exs.map(rec(_)) : _*) + case Not(Equals(l,r)) => z3.mkDistinct(rec(l),rec(r)) + case Not(e) => z3.mkNot(rec(e)) + case IntLiteral(v) => z3.mkInt(v, intSort) + case BooleanLiteral(v) => if (v) z3.mkTrue() else z3.mkFalse() + case Equals(l,r) => z3.mkEq(rec(l),rec(r)) + case Plus(l,r) => z3.mkAdd(rec(l), rec(r)) + case Minus(l,r) => z3.mkSub(rec(l), rec(r)) + case Times(l,r) => z3.mkMul(rec(l), rec(r)) + case Division(l,r) => z3.mkDiv(rec(l), rec(r)) + case UMinus(e) => z3.mkUnaryMinus(rec(e)) + case LessThan(l,r) => z3.mkLT(rec(l), rec(r)) + case LessEquals(l,r) => z3.mkLE(rec(l), rec(r)) + case GreaterThan(l,r) => z3.mkGT(rec(l), rec(r)) + case GreaterEquals(l,r) => z3.mkGE(rec(l), rec(r)) + case _ => scala.Predef.error("Can't handle this in translation to Z3: " + ex) + } + + val res = rec(expr) + (res,varMap) + } } diff --git a/src/purescala/Trees.scala b/src/purescala/Trees.scala index 17883d57b..e6bbcaf74 100644 --- a/src/purescala/Trees.scala +++ b/src/purescala/Trees.scala @@ -40,26 +40,36 @@ object Trees { // We don't handle Seq stars for now. /* Propositional logic */ - case object And { + object And { + def apply(exprs: Seq[Expr]) : And = new And(exprs) + def apply(l: Expr, r: Expr): Expr = (l,r) match { case (And(exs1), And(exs2)) => And(exs1 ++ exs2) case (And(exs1), ex2) => And(exs1 :+ ex2) case (ex1, And(exs2)) => And(exs2 :+ ex1) case (ex1, ex2) => And(List(ex1, ex2)) } + + def unapply(and: And) : Option[Seq[Expr]] = + if(and == null) None else Some(and.exprs) } - case object Or { + object Or { + def apply(exprs: Seq[Expr]) : Or = new Or(exprs) + def apply(l: Expr, r: Expr): Expr = (l,r) match { case (Or(exs1), Or(exs2)) => Or(exs1 ++ exs2) case (Or(exs1), ex2) => Or(exs1 :+ ex2) case (ex1, Or(exs2)) => Or(exs2 :+ ex1) case (ex1, ex2) => Or(List(ex1, ex2)) } + + def unapply(or: Or) : Option[Seq[Expr]] = + if(or == null) None else Some(or.exprs) } - case class And(exprs: Seq[Expr]) extends Expr - case class Or(exprs: Seq[Expr]) extends Expr + class And(val exprs: Seq[Expr]) extends Expr + class Or(val exprs: Seq[Expr]) extends Expr case class Not(expr: Expr) extends Expr /* Maybe we should split this one depending on types? */ @@ -141,4 +151,25 @@ object Trees { case class Cdr(list: Expr) extends Expr case class Concat(list1: Expr, list2: Expr) extends Expr case class ListAt(list: Expr, index: Expr) extends Expr + + object BinaryOperator { + def unapply(expr: Expr) : Option[(Expr,Expr,(Expr,Expr)=>Expr)] = expr match { + case Equals(t1,t2) => Some((t1,t2,Equals)) + case Plus(t1,t2) => Some((t1,t2,Plus)) + case Minus(t1,t2) => Some((t1,t2,Minus)) + case Times(t1,t2) => Some((t1,t2,Times)) + case Division(t1,t2) => Some((t1,t2,Division)) + case LessThan(t1,t2) => Some((t1,t2,LessThan)) + case GreaterThan(t1,t2) => Some((t1,t2,GreaterThan)) + case LessEquals(t1,t2) => Some((t1,t2,LessEquals)) + case GreaterEquals(t1,t2) => Some((t1,t2,GreaterEquals)) + case ElementOfSet(t1,t2) => Some((t1,t2,ElementOfSet)) + case SetEquals(t1,t2) => Some((t1,t2,SetEquals)) + case SubsetOf(t1,t2) => Some((t1,t2,SubsetOf)) + case SetIntersection(t1,t2) => Some((t1,t2,SetIntersection)) + case SetUnion(t1,t2) => Some((t1,t2,SetUnion)) + case SetDifference(t1,t2) => Some((t1,t2,SetDifference)) + case _ => None + } + } } diff --git a/testcases/IntOperations.scala b/testcases/IntOperations.scala new file mode 100644 index 000000000..03256597c --- /dev/null +++ b/testcases/IntOperations.scala @@ -0,0 +1,6 @@ +object IntOperations { + def sum(a: Int, b: Int) : Int = { + a + (if(b < 0) -b else b) + } ensuring(_ >= a) + +} -- GitLab