diff --git a/PERMISSIONS b/PERMISSIONS index dbe09f25f5c0ed5a33d5f2fe757e84f00f3a516d..20019048dd418bfd5e6eba56ffaa50c6a279e1d3 100755 --- a/PERMISSIONS +++ b/PERMISSIONS @@ -12,5 +12,5 @@ tail -2 "$0" | ssh git@laragit.epfl.ch setperms ${thisrepo} exit $_ # **Do not** add new lines after the following two! -WRITERS kuncak psuter rblanc edarulov -READERS @lara mazimmer hardy +WRITERS kuncak psuter rblanc edarulov hojjat +READERS @lara mazimmer hardy amin diff --git a/demo/Arith.scala b/demo/Arith.scala new file mode 100644 index 0000000000000000000000000000000000000000..d4b02f51b5e532a15b9fb3571534316e0cc962ce --- /dev/null +++ b/demo/Arith.scala @@ -0,0 +1,41 @@ +import leon.Utils._ + +object Arith { + + def mult(x : Int, y : Int): Int = ({ + var r = 0 + if(y < 0) { + var n = y + (while(n != 0) { + r = r - x + n = n + 1 + }) invariant(r == x * (y - n) && 0 <= -n) + } else { + var n = y + (while(n != 0) { + r = r + x + n = n - 1 + }) invariant(r == x * (y - n) && 0 <= n) + } + r + }) ensuring(_ == x*y) + + def add(x : Int, y : Int): Int = ({ + var r = x + if(y < 0) { + var n = y + (while(n != 0) { + r = r - 1 + n = n + 1 + }) invariant(r == x + y - n && 0 <= -n) + } else { + var n = y + (while(n != 0) { + r = r + 1 + n = n - 1 + }) invariant(r == x + y - n && 0 <= n) + } + r + }) ensuring(_ == x+y) + +} diff --git a/demo/BubbleSortBug.scala b/demo/BubbleSortBug.scala new file mode 100644 index 0000000000000000000000000000000000000000..a25554f0305d57c53774cdc0b4d514393217b4f2 --- /dev/null +++ b/demo/BubbleSortBug.scala @@ -0,0 +1,39 @@ +import leon.Utils._ + +/* The calculus of Computation textbook */ + +object BubbleSortBug { + + def sort(a: Array[Int]): Array[Int] = ({ + require(a.length >= 1) + var i = a.length - 1 + var j = 0 + val sa = a.clone + (while(i > 0) { + j = 0 + (while(j < i) { + if(sa(j) < sa(j+1)) { + val tmp = sa(j) + sa(j) = sa(j+1) + sa(j+1) = tmp + } + j = j + 1 + }) invariant(j >= 0 && j <= i && i < sa.length) + i = i - 1 + }) invariant(i >= 0 && i < sa.length) + sa + }) ensuring(res => sorted(res, 0, a.length-1)) + + def sorted(a: Array[Int], l: Int, u: Int): Boolean = { + require(a.length >= 0 && l >= 0 && u < a.length && l <= u) + var k = l + var isSorted = true + (while(k < u) { + if(a(k) > a(k+1)) + isSorted = false + k = k + 1 + }) invariant(k <= u && k >= l) + isSorted + } + +} diff --git a/demo/List.scala b/demo/List.scala new file mode 100644 index 0000000000000000000000000000000000000000..302e86774c7d1e68e08d48ce957c87ec483b3d56 --- /dev/null +++ b/demo/List.scala @@ -0,0 +1,20 @@ +import leon.Utils._ + +object List { + + abstract class List + case class Cons(head: Int, tail: List) extends List + case class Nil() extends List + + def size(l: List): Int = waypoint(1, (l match { + case Cons(_, tail) => sizeTail(tail, 1) + case Nil() => 0 + })) ensuring(_ >= 0) + + + def sizeTail(l2: List, acc: Int): Int = l2 match { + case Cons(_, tail) => sizeTail(tail, acc+1) + case Nil() => acc + } + +} diff --git a/demo/ListOperations.scala b/demo/ListOperations.scala new file mode 100644 index 0000000000000000000000000000000000000000..a4fc4f8dc44a90f59a772b52b1a05053316e94d2 --- /dev/null +++ b/demo/ListOperations.scala @@ -0,0 +1,107 @@ +import scala.collection.immutable.Set +import leon.Annotations._ +import leon.Utils._ + +object ListOperations { + sealed abstract class List + case class Cons(head: Int, tail: List) extends List + case class Nil() extends List + + sealed abstract class IntPairList + case class IPCons(head: IntPair, tail: IntPairList) extends IntPairList + case class IPNil() extends IntPairList + + sealed abstract class IntPair + case class IP(fst: Int, snd: Int) extends IntPair + + def size(l: List) : Int = (l match { + case Nil() => 0 + case Cons(_, t) => 1 + size(t) + }) ensuring(res => res >= 0) + + def iplSize(l: IntPairList) : Int = (l match { + case IPNil() => 0 + case IPCons(_, xs) => 1 + iplSize(xs) + }) ensuring(_ >= 0) + + def zip(l1: List, l2: List) : IntPairList = { + // try to comment this and see how pattern-matching becomes + // non-exhaustive and post-condition fails + require(size(l1) == size(l2)) + + l1 match { + case Nil() => IPNil() + case Cons(x, xs) => l2 match { + case Cons(y, ys) => IPCons(IP(x, y), zip(xs, ys)) + } + } + } ensuring(iplSize(_) == size(l1)) + + def sizeTailRec(l: List) : Int = sizeTailRecAcc(l, 0) + def sizeTailRecAcc(l: List, acc: Int) : Int = { + require(acc >= 0) + l match { + case Nil() => acc + case Cons(_, xs) => sizeTailRecAcc(xs, acc+1) + } + } ensuring(res => res == size(l) + acc) + + def sizesAreEquiv(l: List) : Boolean = { + size(l) == sizeTailRec(l) + } holds + + def content(l: List) : Set[Int] = l match { + case Nil() => Set.empty[Int] + case Cons(x, xs) => Set(x) ++ content(xs) + } + + def sizeAndContent(l: List) : Boolean = { + size(l) == 0 || content(l) != Set.empty[Int] + } holds + + def drunk(l : List) : List = (l match { + case Nil() => Nil() + case Cons(x,l1) => Cons(x,Cons(x,drunk(l1))) + }) ensuring (size(_) == 2 * size(l)) + + def reverse(l: List) : List = reverse0(l, Nil()) ensuring(content(_) == content(l)) + def reverse0(l1: List, l2: List) : List = (l1 match { + case Nil() => l2 + case Cons(x, xs) => reverse0(xs, Cons(x, l2)) + }) ensuring(content(_) == content(l1) ++ content(l2)) + + def append(l1 : List, l2 : List) : List = (l1 match { + case Nil() => l2 + case Cons(x,xs) => Cons(x, append(xs, l2)) + }) ensuring(content(_) == content(l1) ++ content(l2)) + + @induct + def nilAppend(l : List) : Boolean = (append(l, Nil()) == l) holds + + @induct + def appendAssoc(xs : List, ys : List, zs : List) : Boolean = + (append(append(xs, ys), zs) == append(xs, append(ys, zs))) holds + + def revAuxBroken(l1 : List, e : Int, l2 : List) : Boolean = { + (append(reverse(l1), Cons(e,l2)) == reverse0(l1, l2)) + } holds + + @induct + def sizeAppend(l1 : List, l2 : List) : Boolean = + (size(append(l1, l2)) == size(l1) + size(l2)) holds + + @induct + def concat(l1: List, l2: List) : List = + concat0(l1, l2, Nil()) ensuring(content(_) == content(l1) ++ content(l2)) + + @induct + def concat0(l1: List, l2: List, l3: List) : List = (l1 match { + case Nil() => l2 match { + case Nil() => reverse(l3) + case Cons(y, ys) => { + concat0(Nil(), ys, Cons(y, l3)) + } + } + case Cons(x, xs) => concat0(xs, l2, Cons(x, l3)) + }) ensuring(content(_) == content(l1) ++ content(l2) ++ content(l3)) +} diff --git a/demo/MaxSum.scala b/demo/MaxSum.scala new file mode 100644 index 0000000000000000000000000000000000000000..ba724d255c23b782e1b4de716ccc8d50043e2059 --- /dev/null +++ b/demo/MaxSum.scala @@ -0,0 +1,38 @@ +import leon.Utils._ + +/* VSTTE 2010 challenge 1 */ + +object MaxSum { + + def maxSum(a: Array[Int]): (Int, Int) = ({ + require(a.length >= 0 && isPositive(a)) + var sum = 0 + var max = 0 + var i = 0 + (while(i < a.length) { + if(max < a(i)) + max = a(i) + sum = sum + a(i) + i = i + 1 + }) invariant (sum <= i * max && i >= 0 && i <= a.length) + (sum, max) + }) ensuring(res => res._1 <= a.length * res._2) + + + def isPositive(a: Array[Int]): Boolean = { + require(a.length >= 0) + def rec(i: Int): Boolean = { + require(i >= 0) + if(i >= a.length) + true + else { + if(a(i) < 0) + false + else + rec(i+1) + } + } + rec(0) + } + +} diff --git a/demo/RedBlackTree.scala b/demo/RedBlackTree.scala new file mode 100644 index 0000000000000000000000000000000000000000..bc2de6ba96ee699736d4558932b752eea9ebba9f --- /dev/null +++ b/demo/RedBlackTree.scala @@ -0,0 +1,117 @@ +import scala.collection.immutable.Set +import leon.Annotations._ +import leon.Utils._ + +object RedBlackTree { + sealed abstract class Color + case class Red() extends Color + case class Black() extends Color + + sealed abstract class Tree + case class Empty() extends Tree + case class Node(color: Color, left: Tree, value: Int, right: Tree) extends Tree + + sealed abstract class OptionInt + case class Some(v : Int) extends OptionInt + case class None() extends OptionInt + + def content(t: Tree) : Set[Int] = t match { + case Empty() => Set.empty + case Node(_, l, v, r) => content(l) ++ Set(v) ++ content(r) + } + + def size(t: Tree) : Int = (t match { + case Empty() => 0 + case Node(_, l, v, r) => size(l) + 1 + size(r) + }) ensuring(_ >= 0) + + /* We consider leaves to be black by definition */ + def isBlack(t: Tree) : Boolean = t match { + case Empty() => true + case Node(Black(),_,_,_) => true + case _ => false + } + + def redNodesHaveBlackChildren(t: Tree) : Boolean = t match { + case Empty() => true + case Node(Black(), l, _, r) => redNodesHaveBlackChildren(l) && redNodesHaveBlackChildren(r) + case Node(Red(), l, _, r) => isBlack(l) && isBlack(r) && redNodesHaveBlackChildren(l) && redNodesHaveBlackChildren(r) + } + + def redDescHaveBlackChildren(t: Tree) : Boolean = t match { + case Empty() => true + case Node(_,l,_,r) => redNodesHaveBlackChildren(l) && redNodesHaveBlackChildren(r) + } + + def blackBalanced(t : Tree) : Boolean = t match { + case Node(_,l,_,r) => blackBalanced(l) && blackBalanced(r) && blackHeight(l) == blackHeight(r) + case Empty() => true + } + + def blackHeight(t : Tree) : Int = t match { + case Empty() => 1 + case Node(Black(), l, _, _) => blackHeight(l) + 1 + case Node(Red(), l, _, _) => blackHeight(l) + } + + // <<insert element x into the tree t>> + def ins(x: Int, t: Tree): Tree = { + require(redNodesHaveBlackChildren(t) && blackBalanced(t)) + t match { + case Empty() => Node(Red(),Empty(),x,Empty()) + case Node(c,a,y,b) => + if (x < y) balance(c, ins(x, a), y, b) + else if (x == y) Node(c,a,y,b) + else balance(c,a,y,ins(x, b)) + } + } ensuring (res => content(res) == content(t) ++ Set(x) + && size(t) <= size(res) && size(res) <= size(t) + 1 + && redDescHaveBlackChildren(res) + && blackBalanced(res)) + + def makeBlack(n: Tree): Tree = { + require(redDescHaveBlackChildren(n) && blackBalanced(n)) + n match { + case Node(Red(),l,v,r) => Node(Black(),l,v,r) + case _ => n + } + } ensuring(res => redNodesHaveBlackChildren(res) && blackBalanced(res)) + + def add(x: Int, t: Tree): Tree = { + require(redNodesHaveBlackChildren(t) && blackBalanced(t)) + makeBlack(ins(x, t)) + } ensuring (res => content(res) == content(t) ++ Set(x) && redNodesHaveBlackChildren(res) && blackBalanced(res)) + + def buggyAdd(x: Int, t: Tree): Tree = { + require(redNodesHaveBlackChildren(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 = { + Node(c,a,x,b) match { + case Node(Black(),Node(Red(),Node(Red(),a,xV,b),yV,c),zV,d) => + Node(Red(),Node(Black(),a,xV,b),yV,Node(Black(),c,zV,d)) + case Node(Black(),Node(Red(),a,xV,Node(Red(),b,yV,c)),zV,d) => + Node(Red(),Node(Black(),a,xV,b),yV,Node(Black(),c,zV,d)) + case Node(Black(),a,xV,Node(Red(),Node(Red(),b,yV,c),zV,d)) => + Node(Red(),Node(Black(),a,xV,b),yV,Node(Black(),c,zV,d)) + case Node(Black(),a,xV,Node(Red(),b,yV,Node(Red(),c,zV,d))) => + Node(Red(),Node(Black(),a,xV,b),yV,Node(Black(),c,zV,d)) + case Node(c,a,xV,b) => Node(c,a,xV,b) + } + } ensuring (res => content(res) == content(Node(c,a,x,b)))// && redDescHaveBlackChildren(res)) + + def buggyBalance(c: Color, a: Tree, x: Int, b: Tree): Tree = { + Node(c,a,x,b) match { + case Node(Black(),Node(Red(),Node(Red(),a,xV,b),yV,c),zV,d) => + Node(Red(),Node(Black(),a,xV,b),yV,Node(Black(),c,zV,d)) + case Node(Black(),Node(Red(),a,xV,Node(Red(),b,yV,c)),zV,d) => + Node(Red(),Node(Black(),a,xV,b),yV,Node(Black(),c,zV,d)) + case Node(Black(),a,xV,Node(Red(),Node(Red(),b,yV,c),zV,d)) => + Node(Red(),Node(Black(),a,xV,b),yV,Node(Black(),c,zV,d)) + case Node(Black(),a,xV,Node(Red(),b,yV,Node(Red(),c,zV,d))) => + Node(Red(),Node(Black(),a,xV,b),yV,Node(Black(),c,zV,d)) + // case Node(c,a,xV,b) => Node(c,a,xV,b) + } + } ensuring (res => content(res) == content(Node(c,a,x,b)))// && redDescHaveBlackChildren(res)) +} diff --git a/run-demo b/run-demo new file mode 100755 index 0000000000000000000000000000000000000000..a3db30e6682f1483ccc66b79cc65d5222e82bb90 --- /dev/null +++ b/run-demo @@ -0,0 +1 @@ +./leon --timeout=10 --noLuckyTests $@ diff --git a/run-demo-testgen b/run-demo-testgen new file mode 100755 index 0000000000000000000000000000000000000000..90b0479f2dc548a4b2df35042cc802604aca3ee3 --- /dev/null +++ b/run-demo-testgen @@ -0,0 +1 @@ +./leon --nodefaults --extensions=leon.TestGeneration $@ diff --git a/run-tests.sh b/run-tests.sh index cedaea2d3ec3e75c79161e5a33ac3225368a372d..e39418fb354226b767aedf5c7d1de462707bdf5e 100755 --- a/run-tests.sh +++ b/run-tests.sh @@ -6,7 +6,7 @@ failedtests="" for f in testcases/regression/valid/*.scala; do echo -n "Running $f, expecting VALID, got: " - res=`./leon --timeout=5 --oneline "$f"` + res=`./leon --noLuckyTests --timeout=10 --oneline "$f"` echo $res | tr [a-z] [A-Z] if [ $res = valid ]; then nbsuccess=$((nbsuccess + 1)) @@ -17,7 +17,7 @@ done for f in testcases/regression/invalid/*.scala; do echo -n "Running $f, expecting INVALID, got: " - res=`./leon --timeout=5 --oneline "$f"` + res=`./leon --noLuckyTests --timeout=10 --oneline "$f"` echo $res | tr [a-z] [A-Z] if [ $res = invalid ]; then nbsuccess=$((nbsuccess + 1)) @@ -28,7 +28,7 @@ done for f in testcases/regression/error/*.scala; do echo -n "Running $f, expecting ERROR, got: " - res=`./leon --timeout=5 --oneline "$f"` + res=`./leon --noLuckyTests --timeout=10 --oneline "$f"` echo $res | tr [a-z] [A-Z] if [ $res = error ]; then nbsuccess=$((nbsuccess + 1)) diff --git a/src/main/scala/leon/Analysis.scala b/src/main/scala/leon/Analysis.scala index 8c950fd87215da16ebdebc1b13c88dbb78ba66e3..176cb96b1f9fe411be0553afca3a31895d7e962a 100644 --- a/src/main/scala/leon/Analysis.scala +++ b/src/main/scala/leon/Analysis.scala @@ -68,6 +68,7 @@ class Analysis(val program : Program, val reporter: Reporter = Settings.reporter allVCs ++= tactic.generatePatternMatchingExhaustivenessChecks(funDef) allVCs ++= tactic.generatePostconditions(funDef) allVCs ++= tactic.generateMiscCorrectnessConditions(funDef) + allVCs ++= tactic.generateArrayAccessChecks(funDef) } allVCs = allVCs.sortWith((vc1, vc2) => { val id1 = vc1.funDef.id.name diff --git a/src/main/scala/leon/Annotations.scala b/src/main/scala/leon/Annotations.scala index 0831c8ff81ae212d6e0fd10d5a847c77c88186f4..85e8c00e0379c42f000b61ac14861190bb46963a 100644 --- a/src/main/scala/leon/Annotations.scala +++ b/src/main/scala/leon/Annotations.scala @@ -3,4 +3,5 @@ package leon object Annotations { class induct extends StaticAnnotation class axiomatize extends StaticAnnotation + class main extends StaticAnnotation } diff --git a/src/main/scala/leon/ArrayTransformation.scala b/src/main/scala/leon/ArrayTransformation.scala new file mode 100644 index 0000000000000000000000000000000000000000..7d0d94458291b71381d8719a25e13751ca3cdd9f --- /dev/null +++ b/src/main/scala/leon/ArrayTransformation.scala @@ -0,0 +1,298 @@ +package leon + +import purescala.Common._ +import purescala.Definitions._ +import purescala.Trees._ +import purescala.TypeTrees._ + +object ArrayTransformation extends Pass { + + val description = "Add bound checking for array access and remove array update with side effect" + + def apply(pgm: Program): Program = { + + val allFuns = pgm.definedFunctions + allFuns.foreach(fd => { + id2FreshId = Map() + fd.precondition = fd.precondition.map(transform) + fd.body = fd.body.map(transform) + fd.postcondition = fd.postcondition.map(transform) + }) + pgm + } + + private var id2FreshId = Map[Identifier, Identifier]() + + def transform(expr: Expr): Expr = expr match { + case sel@ArraySelect(a, i) => { + val ra = transform(a) + val ri = transform(i) + val length = ArrayLength(ra) + val res = IfExpr( + And(LessEquals(IntLiteral(0), ri), LessThan(ri, length)), + ArraySelect(ra, ri).setType(sel.getType).setPosInfo(sel), + Error("Index out of bound").setType(sel.getType).setPosInfo(sel) + ).setType(sel.getType) + res + } + case up@ArrayUpdate(a, i, v) => { + val ra = transform(a) + val ri = transform(i) + val rv = transform(v) + val Variable(id) = ra + val length = ArrayLength(ra) + val res = IfExpr( + And(LessEquals(IntLiteral(0), ri), LessThan(ri, length)), + Assignment(id, ArrayUpdated(ra, ri, rv).setType(ra.getType).setPosInfo(up)), + Error("Index out of bound").setType(UnitType).setPosInfo(up) + ).setType(UnitType) + res + } + case up@ArrayUpdated(a, i, v) => { + val ra = transform(a) + val ri = transform(i) + val rv = transform(v) + val length = ArrayLength(ra) + val res = IfExpr( + And(LessEquals(IntLiteral(0), ri), LessThan(ri, length)), + ArrayUpdated(ra, ri, rv).setType(ra.getType).setPosInfo(up), + Error("Index out of bound").setType(ra.getType).setPosInfo(up) + ).setType(ra.getType) + res + } + case ArrayClone(a) => { + val ra = transform(a) + ra + } + case Let(i, v, b) => { + v.getType match { + case ArrayType(_) => { + val freshIdentifier = FreshIdentifier("t").setType(i.getType) + id2FreshId += (i -> freshIdentifier) + LetVar(freshIdentifier, transform(v), transform(b)) + } + case _ => Let(i, transform(v), transform(b)) + } + } + case Variable(i) => { + val freshId = id2FreshId.get(i).getOrElse(i) + Variable(freshId) + } + + case LetVar(id, e, b) => { + val er = transform(e) + val br = transform(b) + LetVar(id, er, br) + } + case wh@While(c, e) => { + val newWh = While(transform(c), transform(e)) + newWh.invariant = wh.invariant.map(i => transform(i)) + newWh.setPosInfo(wh) + newWh + } + + case ite@IfExpr(c, t, e) => { + val rc = transform(c) + val rt = transform(t) + val re = transform(e) + IfExpr(rc, rt, re).setType(rt.getType) + } + + case m @ MatchExpr(scrut, cses) => { + val scrutRec = transform(scrut) + val csesRec = cses.map{ + case SimpleCase(pat, rhs) => SimpleCase(pat, transform(rhs)) + case GuardedCase(pat, guard, rhs) => GuardedCase(pat, transform(guard), transform(rhs)) + } + val tpe = csesRec.head.rhs.getType + MatchExpr(scrutRec, csesRec).setType(tpe).setPosInfo(m) + } + case LetDef(fd, b) => { + fd.precondition = fd.precondition.map(transform) + fd.body = fd.body.map(transform) + fd.postcondition = fd.postcondition.map(transform) + val rb = transform(b) + LetDef(fd, rb) + } + case n @ NAryOperator(args, recons) => recons(args.map(transform)).setType(n.getType) + case b @ BinaryOperator(a1, a2, recons) => recons(transform(a1), transform(a2)).setType(b.getType) + case u @ UnaryOperator(a, recons) => recons(transform(a)).setType(u.getType) + + case (t: Terminal) => t + case unhandled => scala.sys.error("Non-terminal case should be handled in ArrayTransformation: " + unhandled) + } + + //val newFuns: Seq[FunDef] = allFuns.map(fd => { + // if(fd.hasImplementation) { + // val args = fd.args + // if(args.exists(vd => containsArrayType(vd.tpe)) || containsArrayType(fd.returnType)) { + // val newArgs = args.map(vd => { + // val freshId = FreshIdentifier(vd.id.name).setType(transform(vd.tpe)) + // id2id += (vd.id -> freshId) + // val newTpe = transform(vd.tpe) + // VarDecl(freshId, newTpe) + // }) + // val freshFunName = FreshIdentifier(fd.id.name) + // val freshFunDef = new FunDef(freshFunName, transform(fd.returnType), newArgs) + // fd2fd += (fd -> freshFunDef) + // freshFunDef.fromLoop = fd.fromLoop + // freshFunDef.parent = fd.parent + // freshFunDef.addAnnotation(fd.annotations.toSeq:_*) + // freshFunDef + // } else fd + // } else fd + //}) + + //allFuns.zip(newFuns).foreach{ case (ofd, nfd) => ofd.body.map(body => { + // nfd.precondition = ofd.precondition.map(transform) + // nfd.postcondition = ofd.postcondition.map(transform) + // val newBody = transform(body) + // nfd.body = Some(newBody) + //})} + + //val Program(id, ObjectDef(objId, _, invariants)) = pgm + //val allClasses: Seq[Definition] = pgm.definedClasses + //Program(id, ObjectDef(objId, allClasses ++ newFuns, invariants)) + + + //private def transform(tpe: TypeTree): TypeTree = tpe match { + // case ArrayType(base) => TupleType(Seq(ArrayType(transform(base)), Int32Type)) + // case TupleType(tpes) => TupleType(tpes.map(transform)) + // case t => t + //} + //private def containsArrayType(tpe: TypeTree): Boolean = tpe match { + // case ArrayType(base) => true + // case TupleType(tpes) => tpes.exists(containsArrayType) + // case t => false + //} + + //private var id2id: Map[Identifier, Identifier] = Map() + //private var fd2fd: Map[FunDef, FunDef] = Map() + + //private def transform(expr: Expr): Expr = expr match { + // case fill@ArrayFill(length, default) => { + // var rLength = transform(length) + // val rDefault = transform(default) + // val rFill = ArrayMake(rDefault).setType(fill.getType) + // Tuple(Seq(rFill, rLength)).setType(TupleType(Seq(fill.getType, Int32Type))) + // } + // case sel@ArraySelect(a, i) => { + // val ar = transform(a) + // val ir = transform(i) + // val length = TupleSelect(ar, 2).setType(Int32Type) + // IfExpr( + // And(GreaterEquals(ir, IntLiteral(0)), LessThan(ir, length)), + // ArraySelect(TupleSelect(ar, 1).setType(ArrayType(sel.getType)), ir).setType(sel.getType).setPosInfo(sel), + // Error("Index out of bound").setType(sel.getType).setPosInfo(sel) + // ).setType(sel.getType) + // } + // case up@ArrayUpdate(a, i, v) => { + // val ar = transform(a) + // val ir = transform(i) + // val vr = transform(v) + // val Variable(id) = ar + // val length = TupleSelect(ar, 2).setType(Int32Type) + // val array = TupleSelect(ar, 1).setType(ArrayType(v.getType)) + // IfExpr( + // And(GreaterEquals(i, IntLiteral(0)), LessThan(i, length)), + // Assignment( + // id, + // Tuple(Seq( + // ArrayUpdated(array, ir, vr).setType(array.getType).setPosInfo(up), + // length) + // ).setType(TupleType(Seq(array.getType, Int32Type)))), + // Error("Index out of bound").setType(UnitType).setPosInfo(up) + // ).setType(UnitType) + // } + // case ArrayLength(a) => { + // val ar = transform(a) + // TupleSelect(ar, 2).setType(Int32Type) + // } + // case Let(i, v, b) => { + // val vr = transform(v) + // v.getType match { + // case ArrayType(_) => { + // val freshIdentifier = FreshIdentifier("t").setType(vr.getType) + // id2id += (i -> freshIdentifier) + // val br = transform(b) + // LetVar(freshIdentifier, vr, br) + // } + // case _ => { + // val br = transform(b) + // Let(i, vr, br) + // } + // } + // } + // case LetVar(id, e, b) => { + // val er = transform(e) + // val br = transform(b) + // LetVar(id, er, br) + // } + // case wh@While(c, e) => { + // val newWh = While(transform(c), transform(e)) + // newWh.invariant = wh.invariant.map(i => transform(i)) + // newWh.setPosInfo(wh) + // } + + // case ite@IfExpr(c, t, e) => { + // val rc = transform(c) + // val rt = transform(t) + // val re = transform(e) + // IfExpr(rc, rt, re).setType(rt.getType) + // } + + // case m @ MatchExpr(scrut, cses) => { + // val scrutRec = transform(scrut) + // val csesRec = cses.map{ + // case SimpleCase(pat, rhs) => SimpleCase(pat, transform(rhs)) + // case GuardedCase(pat, guard, rhs) => GuardedCase(pat, transform(guard), transform(rhs)) + // } + // val tpe = csesRec.head.rhs.getType + // MatchExpr(scrutRec, csesRec).setType(tpe).setPosInfo(m) + // } + // case LetDef(fd, b) => { + // val newFd = if(fd.hasImplementation) { + // val body = fd.body.get + // val args = fd.args + // val newFd = + // if(args.exists(vd => containsArrayType(vd.tpe)) || containsArrayType(fd.returnType)) { + // val newArgs = args.map(vd => { + // val freshId = FreshIdentifier(vd.id.name).setType(transform(vd.tpe)) + // id2id += (vd.id -> freshId) + // val newTpe = transform(vd.tpe) + // VarDecl(freshId, newTpe) + // }) + // val freshFunName = FreshIdentifier(fd.id.name) + // val freshFunDef = new FunDef(freshFunName, transform(fd.returnType), newArgs) + // fd2fd += (fd -> freshFunDef) + // freshFunDef.fromLoop = fd.fromLoop + // freshFunDef.parent = fd.parent + // freshFunDef.precondition = fd.precondition.map(transform) + // freshFunDef.postcondition = fd.postcondition.map(transform) + // freshFunDef.addAnnotation(fd.annotations.toSeq:_*) + // freshFunDef + // } else fd + // val newBody = transform(body) + // newFd.body = Some(newBody) + // newFd + // } else fd + // val rb = transform(b) + // LetDef(newFd, rb) + // } + // case FunctionInvocation(fd, args) => { + // val rargs = args.map(transform) + // val rfd = fd2fd.get(fd).getOrElse(fd) + // FunctionInvocation(rfd, rargs) + // } + + // case n @ NAryOperator(args, recons) => recons(args.map(transform)).setType(n.getType) + // case b @ BinaryOperator(a1, a2, recons) => recons(transform(a1), transform(a2)).setType(b.getType) + // case u @ UnaryOperator(a, recons) => recons(transform(a)).setType(u.getType) + + // case v @ Variable(id) => if(id2id.isDefinedAt(id)) Variable(id2id(id)) else v + // case (t: Terminal) => t + // case unhandled => scala.sys.error("Non-terminal case should be handled in ArrayTransformation: " + unhandled) + + //} + +} diff --git a/src/main/scala/leon/DefaultTactic.scala b/src/main/scala/leon/DefaultTactic.scala index a01d025431c2ec3c704a1950410334fd322abae9..70ad17f36150a47f846fe67a8bf8db350e03a9bf 100644 --- a/src/main/scala/leon/DefaultTactic.scala +++ b/src/main/scala/leon/DefaultTactic.scala @@ -194,6 +194,35 @@ class DefaultTactic(reporter: Reporter) extends Tactic(reporter) { toRet } + def generateArrayAccessChecks(function: FunDef) : Seq[VerificationCondition] = { + val toRet = if (function.hasBody) { + val cleanBody = matchToIfThenElse(function.body.get) + + val allPathConds = collectWithPathCondition((t => t match { + case Error("Index out of bound") => true + case _ => false + }), cleanBody) + + def withPrecIfDefined(conds: Seq[Expr]) : Expr = if (function.hasPrecondition) { + Not(And(mapGetWithChecks(matchToIfThenElse(function.precondition.get)), And(conds))) + } else { + Not(And(conds)) + } + + allPathConds.map(pc => + new VerificationCondition( + withPrecIfDefined(pc._1), + if(function.fromLoop) function.parent.get else function, + VCKind.ArrayAccess, + this.asInstanceOf[DefaultTactic]).setPosInfo(pc._2.asInstanceOf[Error]) + ).toSeq + } else { + Seq.empty + } + + toRet + } + def generateMiscCorrectnessConditions(function: FunDef) : Seq[VerificationCondition] = { generateMapAccessChecks(function) } diff --git a/src/main/scala/leon/Evaluator.scala b/src/main/scala/leon/Evaluator.scala index e8409aa317356679938accb064985c284e1e0e23..d5c5745c19bcb8b3c58c1496c6a63547d179dcee 100644 --- a/src/main/scala/leon/Evaluator.scala +++ b/src/main/scala/leon/Evaluator.scala @@ -77,6 +77,7 @@ object Evaluator { case _ => throw TypeErrorEx(TypeError(first, BooleanType)) } } + case Waypoint(_, arg) => rec(ctx, arg) case FunctionInvocation(fd, args) => { val evArgs = args.map(a => rec(ctx, a)) // build a context for the function... @@ -85,7 +86,9 @@ object Evaluator { if(fd.hasPrecondition) { rec(frame, matchToIfThenElse(fd.precondition.get)) match { case BooleanLiteral(true) => ; - case BooleanLiteral(false) => throw RuntimeErrorEx("Precondition violation for " + fd.id.name + " reached in evaluation.") + case BooleanLiteral(false) => { + throw RuntimeErrorEx("Precondition violation for " + fd.id.name + " reached in evaluation.: " + fd.precondition.get) + } case other => throw TypeErrorEx(TypeError(other, BooleanType)) } } @@ -242,11 +245,50 @@ object Evaluator { case (e, f @ FiniteSet(els)) => BooleanLiteral(els.contains(e)) case (l,r) => throw TypeErrorEx(TypeError(r, SetType(l.getType))) } + case SetCardinality(s) => { + val sr = rec(ctx, s) + sr match { + case EmptySet(_) => IntLiteral(0) + case FiniteSet(els) => IntLiteral(els.size) + case _ => throw TypeErrorEx(TypeError(sr, SetType(AnyType))) + } + } case f @ FiniteSet(els) => FiniteSet(els.map(rec(ctx,_)).distinct).setType(f.getType) case e @ EmptySet(_) => e case i @ IntLiteral(_) => i case b @ BooleanLiteral(_) => b + case u @ UnitLiteral => u + + case f @ ArrayFill(length, default) => { + val rDefault = rec(ctx, default) + val rLength = rec(ctx, length) + val IntLiteral(iLength) = rLength + FiniteArray((1 to iLength).map(_ => rDefault).toSeq) + } + case ArrayLength(a) => { + var ra = rec(ctx, a) + while(!ra.isInstanceOf[FiniteArray]) + ra = ra.asInstanceOf[ArrayUpdated].array + IntLiteral(ra.asInstanceOf[FiniteArray].exprs.size) + } + case ArrayUpdated(a, i, v) => { + val ra = rec(ctx, a) + val ri = rec(ctx, i) + val rv = rec(ctx, v) + + val IntLiteral(index) = ri + val FiniteArray(exprs) = ra + FiniteArray(exprs.updated(index, rv)) + } + case ArraySelect(a, i) => { + val IntLiteral(index) = rec(ctx, i) + val FiniteArray(exprs) = rec(ctx, a) + exprs(index) + } + case FiniteArray(exprs) => { + FiniteArray(exprs.map(e => rec(ctx, e))) + } case f @ FiniteMap(ss) => FiniteMap(ss.map(rec(ctx,_)).distinct.asInstanceOf[Seq[SingletonMap]]).setType(f.getType) case e @ EmptyMap(_,_) => e diff --git a/src/main/scala/leon/Extensions.scala b/src/main/scala/leon/Extensions.scala index 5905ec99820c7dff51cef6fef88705017a17614d..e9fdde89c3555b41b47b696c542733b0bf411830 100644 --- a/src/main/scala/leon/Extensions.scala +++ b/src/main/scala/leon/Extensions.scala @@ -46,6 +46,7 @@ object Extensions { def generatePreconditions(function: FunDef) : Seq[VerificationCondition] def generatePatternMatchingExhaustivenessChecks(function: FunDef) : Seq[VerificationCondition] def generateMiscCorrectnessConditions(function: FunDef) : Seq[VerificationCondition] + def generateArrayAccessChecks(function: FunDef) : Seq[VerificationCondition] } // The rest of the code is for dynamically loading extensions @@ -98,6 +99,7 @@ object Extensions { allLoaded = defaultExtensions ++ loaded analysisExtensions = allLoaded.filter(_.isInstanceOf[Analyser]).map(_.asInstanceOf[Analyser]) + //analysisExtensions = new TestGeneration(extensionsReporter) +: analysisExtensions val solverExtensions0 = allLoaded.filter(_.isInstanceOf[Solver]).map(_.asInstanceOf[Solver]) val solverExtensions1 = if(Settings.useQuickCheck) new RandomSolver(extensionsReporter) +: solverExtensions0 else solverExtensions0 diff --git a/src/main/scala/leon/FairZ3Solver.scala b/src/main/scala/leon/FairZ3Solver.scala index 919b5b4f9740768bb6c8aeba5686b19966027df2..2c429bab38594f4109c467a89e62106d1cc60d6b 100644 --- a/src/main/scala/leon/FairZ3Solver.scala +++ b/src/main/scala/leon/FairZ3Solver.scala @@ -59,6 +59,7 @@ class FairZ3Solver(reporter: Reporter) extends Solver(reporter) with AbstractZ3S fallbackSorts = Map.empty mapSorts = Map.empty + arraySorts = Map.empty funSorts = Map.empty funDomainConstructors = Map.empty funDomainSelectors = Map.empty @@ -98,6 +99,9 @@ class FairZ3Solver(reporter: Reporter) extends Solver(reporter) with AbstractZ3S private var setSorts: Map[TypeTree, Z3Sort] = Map.empty private var mapSorts: Map[TypeTree, Z3Sort] = Map.empty + private var unitSort: Z3Sort = null + private var unitValue: Z3AST = null + protected[leon] var funSorts: Map[TypeTree, Z3Sort] = Map.empty protected[leon] var funDomainConstructors: Map[TypeTree, Z3FuncDecl] = Map.empty protected[leon] var funDomainSelectors: Map[TypeTree, Seq[Z3FuncDecl]] = Map.empty @@ -106,6 +110,14 @@ class FairZ3Solver(reporter: Reporter) extends Solver(reporter) with AbstractZ3S protected[leon] var tupleConstructors: Map[TypeTree, Z3FuncDecl] = Map.empty protected[leon] var tupleSelectors: Map[TypeTree, Seq[Z3FuncDecl]] = Map.empty + private var arraySorts: Map[TypeTree, Z3Sort] = Map.empty + protected[leon] var arrayTupleCons: Map[TypeTree, Z3FuncDecl] = Map.empty + protected[leon] var arrayTupleSelectorArray: Map[TypeTree, Z3FuncDecl] = Map.empty + protected[leon] var arrayTupleSelectorLength: Map[TypeTree, Z3FuncDecl] = Map.empty + + private var reverseTupleConstructors: Map[Z3FuncDecl, TupleType] = Map.empty + private var reverseTupleSelectors: Map[Z3FuncDecl, (TupleType, Int)] = Map.empty + private var intSetMinFun: Z3FuncDecl = null private var intSetMaxFun: Z3FuncDecl = null private var setCardFuns: Map[TypeTree, Z3FuncDecl] = Map.empty @@ -189,6 +201,27 @@ class FairZ3Solver(reporter: Reporter) extends Solver(reporter) with AbstractZ3S setSorts = Map.empty setCardFuns = Map.empty + //unitSort = z3.mkUninterpretedSort("unit") + //unitValue = z3.mkFreshConst("Unit", unitSort) + //val bound = z3.mkBound(0, unitSort) + //val eq = z3.mkEq(bound, unitValue) + //val decls = Seq((z3.mkFreshStringSymbol("u"), unitSort)) + //val unitAxiom = z3.mkForAll(0, Seq(), decls, eq) + //println(unitAxiom) + //println(unitValue) + //z3.assertCnstr(unitAxiom) + val Seq((us, Seq(unitCons), Seq(unitTester), _)) = z3.mkADTSorts( + Seq( + ( + "Unit", + Seq("Unit"), + Seq(Seq()) + ) + ) + ) + unitSort = us + unitValue = unitCons() + val intSetSort = typeToSort(SetType(Int32Type)) intSetMinFun = z3.mkFreshFuncDecl("setMin", Seq(intSetSort), intSort) intSetMaxFun = z3.mkFreshFuncDecl("setMax", Seq(intSetSort), intSort) @@ -358,6 +391,7 @@ class FairZ3Solver(reporter: Reporter) extends Solver(reporter) with AbstractZ3S def typeToSort(tt: TypeTree): Z3Sort = tt match { case Int32Type => intSort case BooleanType => boolSort + case UnitType => unitSort case AbstractClassType(cd) => adtSorts(cd) case CaseClassType(cd) => { if (cd.hasParent) { @@ -386,6 +420,21 @@ class FairZ3Solver(reporter: Reporter) extends Solver(reporter) with AbstractZ3S ms } } + case at @ ArrayType(base) => arraySorts.get(at) match { + case Some(s) => s + case None => { + val intSort = typeToSort(Int32Type) + val toSort = typeToSort(base) + val as = z3.mkArraySort(intSort, toSort) + val tupleSortSymbol = z3.mkFreshStringSymbol("Array") + val (arrayTupleSort, arrayTupleCons_, Seq(arrayTupleSelectorArray_, arrayTupleSelectorLength_)) = z3.mkTupleSort(tupleSortSymbol, as, intSort) + arraySorts += (at -> arrayTupleSort) + arrayTupleCons += (at -> arrayTupleCons_) + arrayTupleSelectorArray += (at -> arrayTupleSelectorArray_) + arrayTupleSelectorLength += (at -> arrayTupleSelectorLength_) + arrayTupleSort + } + } case ft @ FunctionType(fts, tt) => funSorts.get(ft) match { case Some(s) => s case None => { @@ -403,11 +452,13 @@ class FairZ3Solver(reporter: Reporter) extends Solver(reporter) with AbstractZ3S case Some(s) => s case None => { val tpesSorts = tpes.map(typeToSort) - val sortSymbol = z3.mkFreshStringSymbol("TupleSort") + val sortSymbol = z3.mkFreshStringSymbol("Tuple") val (tupleSort, consTuple, projsTuple) = z3.mkTupleSort(sortSymbol, tpesSorts: _*) tupleSorts += (tt -> tupleSort) tupleConstructors += (tt -> consTuple) + reverseTupleConstructors += (consTuple -> tt) tupleSelectors += (tt -> projsTuple) + projsTuple.zipWithIndex.foreach{ case (proj, i) => reverseTupleSelectors += (proj -> (tt, i)) } tupleSort } } @@ -513,14 +564,11 @@ class FairZ3Solver(reporter: Reporter) extends Solver(reporter) with AbstractZ3S reporter.info(" - Initial unrolling...") val (clauses, guards) = unrollingBank.initialUnrolling(expandedVC) - //for(clause <- clauses) { - //println("we're getting a new clause " + clause) - // z3.assertCnstr(toZ3Formula(z3, clause).get) - //} + for(clause <- clauses) { + Logger.debug("we're getting a new clause " + clause, 4, "z3solver") + } - //println("The blocking guards: " + guards) val cc = toZ3Formula(z3, And(clauses)).get - // println("CC : " + cc) z3.assertCnstr(cc) // these are the optional sequence of assumption literals @@ -561,6 +609,7 @@ class FairZ3Solver(reporter: Reporter) extends Solver(reporter) with AbstractZ3S (a, m, Seq.empty[Z3AST]) } reporter.info(" - Finished search with blocked literals") + Logger.debug("The blocking guards are: " + blockingSet.mkString(", "), 4, "z3solver") // if (Settings.useCores) // reporter.info(" - Core is : " + core) @@ -582,10 +631,16 @@ class FairZ3Solver(reporter: Reporter) extends Solver(reporter) with AbstractZ3S } case (Some(true), m) => { // SAT validatingStopwatch.start + Logger.debug("Model Found: " + m, 4, "z3solver") val (trueModel, model) = if(Settings.verifyModel) validateAndDeleteModel(m, toCheckAgainstModels, varsInVC, evaluator) - else - (true, modelToMap(m, varsInVC)) + 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) { @@ -632,6 +687,7 @@ class FairZ3Solver(reporter: Reporter) extends Solver(reporter) with AbstractZ3S foundAnswer(Some(true)) } else { luckyStopwatch.start + Logger.debug("Model Found: " + m2, 4, "z3solver") // we might have been lucky :D val (wereWeLucky, cleanModel) = validateAndDeleteModel(m2, toCheckAgainstModels, varsInVC, evaluator) if(wereWeLucky) { @@ -722,7 +778,6 @@ class FairZ3Solver(reporter: Reporter) extends Solver(reporter) with AbstractZ3S } } - // println("Release set : " + toRelease) blockingSet = blockingSet -- toRelease @@ -735,16 +790,13 @@ class FairZ3Solver(reporter: Reporter) extends Solver(reporter) with AbstractZ3S reporter.info(" - more unrollings") for((id,polarity) <- toReleaseAsPairs) { val (newClauses,newBlockers) = unrollingBank.unlock(id, !polarity) - //println("Unlocked : " + (id, !polarity)) for(clause <- newClauses) { - //println("we're getting a new clause " + clause) - // z3.assertCnstr(toZ3Formula(z3, clause).get) + Logger.debug("we're getting a new clause " + clause, 4, "z3solver") } for(ncl <- newClauses) { z3.assertCnstr(toZ3Formula(z3, ncl).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)) : _*) } reporter.info(" - finished unrolling") @@ -912,6 +964,7 @@ class FairZ3Solver(reporter: Reporter) extends Solver(reporter) with AbstractZ3S z3Vars = z3Vars - i rb } + case Waypoint(_, e) => rec(e) case e @ Error(_) => { val tpe = e.getType val newAST = z3.mkFreshConst("errorValue", typeToSort(tpe)) @@ -960,6 +1013,7 @@ class FairZ3Solver(reporter: Reporter) extends Solver(reporter) with AbstractZ3S 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 UnitLiteral => unitValue case Equals(l, r) => z3.mkEq(rec(l), rec(r)) case Plus(l, r) => if(USEBV) z3.mkBVAdd(rec(l), rec(r)) else z3.mkAdd(rec(l), rec(r)) case Minus(l, r) => if(USEBV) z3.mkBVSub(rec(l), rec(r)) else z3.mkSub(rec(l), rec(r)) @@ -1043,6 +1097,38 @@ class FairZ3Solver(reporter: Reporter) extends Solver(reporter) with AbstractZ3S case MapType(ft, tt) => z3.mkDistinct(z3.mkSelect(rec(m), rec(k)), mapRangeNoneConstructors(tt)()) case errorType => scala.sys.error("Unexpected type for map: " + (ex, errorType)) } + case fill@ArrayFill(length, default) => { + val at@ArrayType(base) = fill.getType + typeToSort(at) + val cons = arrayTupleCons(at) + val ar = z3.mkConstArray(typeToSort(base), rec(default)) + val res = cons(ar, rec(length)) + res + } + case ArraySelect(a, index) => { + typeToSort(a.getType) + val ar = rec(a) + val getArray = arrayTupleSelectorArray(a.getType) + val res = z3.mkSelect(getArray(ar), rec(index)) + res + } + case ArrayUpdated(a, index, newVal) => { + typeToSort(a.getType) + val ar = rec(a) + val getArray = arrayTupleSelectorArray(a.getType) + val getLength = arrayTupleSelectorLength(a.getType) + val cons = arrayTupleCons(a.getType) + val store = z3.mkStore(getArray(ar), rec(index), rec(newVal)) + val res = cons(store, getLength(ar)) + res + } + case ArrayLength(a) => { + typeToSort(a.getType) + val ar = rec(a) + val getLength = arrayTupleSelectorLength(a.getType) + val res = getLength(ar) + res + } case AnonymousFunctionInvocation(id, args) => id.getType match { case ft @ FunctionType(fts, tt) => { val consFD = funDomainConstructors(ft) @@ -1100,8 +1186,28 @@ class FairZ3Solver(reporter: Reporter) extends Solver(reporter) with AbstractZ3S (if (elems.isEmpty) EmptySet(dt) else FiniteSet(elems.toSeq)).setType(expType.get) } } + case Some(ArrayType(dt)) => { + val Z3AppAST(decl, args) = z3.getASTKind(t) + assert(args.size == 2) + val IntLiteral(length) = rec(args(1), Some(Int32Type)) + val array = model.getArrayValue(args(0)) match { + case None => throw new CantTranslateException(t) + case Some((map, elseValue)) => { + val exprs = map.foldLeft((1 to length).map(_ => rec(elseValue, Some(dt))).toSeq)((acc, p) => { + val IntLiteral(index) = rec(p._1, Some(Int32Type)) + if(index >= 0 && index < length) + acc.updated(index, rec(p._2, Some(dt))) + else acc + }) + FiniteArray(exprs) + } + } + array + } case other => - z3.getASTKind(t) match { + if(t == unitValue) + UnitLiteral + else z3.getASTKind(t) match { case Z3AppAST(decl, args) => { val argsSize = args.size if(argsSize == 0 && z3IdToExpr.isDefinedAt(t)) { @@ -1121,6 +1227,10 @@ class FairZ3Solver(reporter: Reporter) extends Solver(reporter) with AbstractZ3S val ccd = reverseADTConstructors(decl) assert(argsSize == ccd.fields.size) CaseClass(ccd, (args zip ccd.fields).map(p => rec(p._1, Some(p._2.tpe)))) + } else if(reverseTupleConstructors.isDefinedAt(decl)) { + val TupleType(subTypes) = reverseTupleConstructors(decl) + val rargs = args.zip(subTypes).map(p => rec(p._1, Some(p._2))) + Tuple(rargs) } else { import Z3DeclKind._ val rargs = args.map(rec(_)) @@ -1134,7 +1244,7 @@ class FairZ3Solver(reporter: Reporter) extends Solver(reporter) with AbstractZ3S val r1 = rargs(1) val r2 = rargs(2) try { - IfExpr(r0, r1, r2).setType(leastUpperBound(r1.getType, r2.getType)) + IfExpr(r0, r1, r2).setType(leastUpperBound(r1.getType, r2.getType).get) } catch { case e => { println("I was asking for lub because of this.") @@ -1194,6 +1304,11 @@ class FairZ3Solver(reporter: Reporter) extends Solver(reporter) with AbstractZ3S } case Z3NumeralAST(Some(v)) => IntLiteral(v) + case Z3NumeralAST(None) => { + reporter.info("Cannot read exact model from Z3: Integer does not fit in machine word") + reporter.info("Exiting procedure now") + sys.exit(0) + } case other @ _ => { System.err.println("Don't know what this is " + other) System.err.println("REVERSE FUNCTION MAP:") @@ -1252,16 +1367,7 @@ class FairZ3Solver(reporter: Reporter) extends Solver(reporter) with AbstractZ3S // 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)]) = { - val fi = functionCallsOf(formula) - if(fi.isEmpty) { - (Seq(formula), Seq.empty) - } else { - val startingVar : Identifier = FreshIdentifier("start", true).setType(BooleanType) - - val result = treatFunctionInvocationSet(startingVar, true, functionCallsOf(formula)) - //reporter.info(result) - (Variable(startingVar) +: formula +: result._1, result._2) - } + initialUnrolling0(formula) } def unlock(id: Identifier, pol: Boolean) : (Seq[Expr], Seq[(Identifier,Boolean)]) = { @@ -1273,6 +1379,82 @@ class FairZ3Solver(reporter: Reporter) extends Solver(reporter) with AbstractZ3S 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) + + } } } diff --git a/src/main/scala/leon/FunctionClosure.scala b/src/main/scala/leon/FunctionClosure.scala index d9426510ee76f9703514df4219cff8761db26995..8a6fd5bffd1a7c6a9a6a9b6fdde5e8bd3598f940 100644 --- a/src/main/scala/leon/FunctionClosure.scala +++ b/src/main/scala/leon/FunctionClosure.scala @@ -9,121 +9,153 @@ object FunctionClosure extends Pass { val description = "Closing function with its scoping variables" - private var enclosingPreconditions: List[Expr] = Nil - private var pathConstraints: List[Expr] = Nil + private var enclosingLets: List[(Identifier, Expr)] = Nil private var newFunDefs: Map[FunDef, FunDef] = Map() + private var topLevelFuns: Set[FunDef] = Set() def apply(program: Program): Program = { newFunDefs = Map() val funDefs = program.definedFunctions funDefs.foreach(fd => { - enclosingPreconditions = fd.precondition.toList pathConstraints = fd.precondition.toList - fd.body = fd.body.map(b => functionClosure(b, fd.args.map(_.id).toSet)) - fd.postcondition = fd.postcondition.map(b => functionClosure(b, fd.args.map(_.id).toSet)) + fd.body = fd.body.map(b => functionClosure(b, fd.args.map(_.id).toSet, Map(), Map())) }) - program + val Program(id, ObjectDef(objId, defs, invariants)) = program + Program(id, ObjectDef(objId, defs ++ topLevelFuns, invariants)) } - private def functionClosure(expr: Expr, bindedVars: Set[Identifier]): Expr = expr match { + private def functionClosure(expr: Expr, bindedVars: Set[Identifier], id2freshId: Map[Identifier, Identifier], fd2FreshFd: Map[FunDef, (FunDef, Seq[Variable])]): Expr = expr match { case l @ LetDef(fd, rest) => { - - val id = fd.id - val rt = fd.returnType - val varDecl = fd.args - val precondition = fd.precondition - val postcondition = fd.postcondition - - val bodyVars: Set[Identifier] = variablesOf(fd.body.getOrElse(BooleanLiteral(true))) ++ - variablesOf(precondition.getOrElse(BooleanLiteral(true))) ++ - variablesOf(postcondition.getOrElse(BooleanLiteral(true))) - - val capturedVars = bodyVars.intersect(bindedVars)// this should be the variable used that are in the scope - val (constraints, allCapturedVars) = filterConstraints(capturedVars) //all relevant path constraints - val capturedVarsWithConstraints = allCapturedVars.toSeq - - val freshVars: Map[Identifier, Identifier] = capturedVarsWithConstraints.map(v => (v, FreshIdentifier(v.name).setType(v.getType))).toMap - val freshVarsExpr: Map[Expr, Expr] = freshVars.map(p => (p._1.toVariable, p._2.toVariable)) - - val extraVarDecls = freshVars.map{ case (_, v2) => VarDecl(v2, v2.getType) } - val newVarDecls = varDecl ++ extraVarDecls - val newFunId = FreshIdentifier(id.name) - val newFunDef = new FunDef(newFunId, rt, newVarDecls).setPosInfo(fd) + val capturedVars: Set[Identifier] = bindedVars.diff(enclosingLets.map(_._1).toSet) + val capturedConstraints: Set[Expr] = pathConstraints.toSet + + val freshIds: Map[Identifier, Identifier] = capturedVars.map(id => (id, FreshIdentifier(id.name).setType(id.getType))).toMap + val freshVars: Map[Expr, Expr] = freshIds.map(p => (p._1.toVariable, p._2.toVariable)) + + val extraVarDeclOldIds: Seq[Identifier] = capturedVars.toSeq + val extraVarDeclFreshIds: Seq[Identifier] = extraVarDeclOldIds.map(freshIds(_)) + val extraVarDecls: Seq[VarDecl] = extraVarDeclFreshIds.map(id => VarDecl(id, id.getType)) + val newVarDecls: Seq[VarDecl] = fd.args ++ extraVarDecls + val newBindedVars: Set[Identifier] = bindedVars ++ fd.args.map(_.id) + val newFunId = FreshIdentifier(fd.id.name) + + val newFunDef = new FunDef(newFunId, fd.returnType, newVarDecls).setPosInfo(fd) + topLevelFuns += newFunDef newFunDef.fromLoop = fd.fromLoop newFunDef.parent = fd.parent newFunDef.addAnnotation(fd.annotations.toSeq:_*) - val freshPrecondition = precondition.map(expr => replace(freshVarsExpr, expr)) - val freshPostcondition = postcondition.map(expr => replace(freshVarsExpr, expr)) - val freshBody = fd.body.map(b => replace(freshVarsExpr, b)) - val freshConstraints = constraints.map(expr => replace(freshVarsExpr, expr)) - - def substFunInvocInDef(expr: Expr): Option[Expr] = expr match { - case fi@FunctionInvocation(fd, args) if fd.id == id => Some(FunctionInvocation(newFunDef, args ++ extraVarDecls.map(_.id.toVariable)).setPosInfo(fi)) - case _ => None + def introduceLets(expr: Expr, fd2FreshFd: Map[FunDef, (FunDef, Seq[Variable])]): Expr = { + val (newExpr, _) = enclosingLets.foldLeft((expr, Map[Identifier, Identifier]()))((acc, p) => { + val newId = FreshIdentifier(p._1.name).setType(p._1.getType) + val newMap = acc._2 + (p._1 -> newId) + val newBody = functionClosure(acc._1, newBindedVars, freshIds ++ newMap, fd2FreshFd) + (Let(newId, p._2, newBody), newMap) + }) + functionClosure(newExpr, newBindedVars, freshIds, fd2FreshFd) } - val oldPathConstraints = pathConstraints - pathConstraints = (precondition.getOrElse(BooleanLiteral(true)) :: pathConstraints).map(e => replace(freshVarsExpr, e)) - val recPrecondition = freshConstraints match { //Actually, we do not allow nested fundef in precondition - case List() => freshPrecondition - case precs => Some(And(freshPrecondition.getOrElse(BooleanLiteral(true)) +: precs)) - } - val recBody = freshBody.map(b => - functionClosure(b, bindedVars ++ newVarDecls.map(_.id)) - ).map(b => searchAndReplaceDFS(substFunInvocInDef)(b)) - pathConstraints = oldPathConstraints - newFunDef.precondition = recPrecondition - newFunDef.body = recBody + + val newPrecondition = simplifyLets(introduceLets(And((capturedConstraints ++ fd.precondition).toSeq), fd2FreshFd)) + newFunDef.precondition = if(newPrecondition == BooleanLiteral(true)) None else Some(newPrecondition) + + val freshPostcondition = fd.postcondition.map(post => introduceLets(post, fd2FreshFd)) newFunDef.postcondition = freshPostcondition + + pathConstraints = fd.precondition.getOrElse(BooleanLiteral(true)) :: pathConstraints + //val freshBody = fd.body.map(body => introduceLets(body, fd2FreshFd + (fd -> ((newFunDef, extraVarDeclFreshIds.map(_.toVariable)))))) + val freshBody = fd.body.map(body => introduceLets(body, fd2FreshFd + (fd -> ((newFunDef, extraVarDeclOldIds.map(_.toVariable)))))) + newFunDef.body = freshBody + pathConstraints = pathConstraints.tail - def substFunInvocInRest(expr: Expr): Option[Expr] = expr match { - case fi@FunctionInvocation(fd, args) if fd.id == id => Some(FunctionInvocation(newFunDef, args ++ capturedVarsWithConstraints.map(_.toVariable)).setPosInfo(fi)) - case _ => None - } - val recRest = searchAndReplaceDFS(substFunInvocInRest)(functionClosure(rest, bindedVars)) - LetDef(newFunDef, recRest).setType(l.getType) + //val freshRest = functionClosure(rest, bindedVars, id2freshId, fd2FreshFd + (fd -> + // ((newFunDef, extraVarDeclOldIds.map(id => id2freshId.get(id).getOrElse(id).toVariable))))) + val freshRest = functionClosure(rest, bindedVars, id2freshId, fd2FreshFd + (fd -> ((newFunDef, extraVarDeclOldIds.map(_.toVariable))))) + freshRest.setType(l.getType) } case l @ Let(i,e,b) => { - val re = functionClosure(e, bindedVars) - pathConstraints ::= Equals(Variable(i), re) - val rb = functionClosure(b, bindedVars + i) - pathConstraints = pathConstraints.tail + val re = functionClosure(e, bindedVars, id2freshId, fd2FreshFd) + //we need the enclosing lets to always refer to the original ids, because it might be expand later in a highly nested function + enclosingLets ::= (i, replace(id2freshId.map(p => (p._2.toVariable, p._1.toVariable)), re)) + //pathConstraints :: Equals(i.toVariable, re) + val rb = functionClosure(b, bindedVars + i, id2freshId, fd2FreshFd) + enclosingLets = enclosingLets.tail + //pathConstraints = pathConstraints.tail Let(i, re, rb).setType(l.getType) } + case i @ IfExpr(cond,then,elze) => { + /* + when acumulating path constraints, take the condition without closing it first, so this + might not work well with nested fundef in if then else condition + */ + val rCond = functionClosure(cond, bindedVars, id2freshId, fd2FreshFd) + pathConstraints ::= cond//rCond + val rThen = functionClosure(then, bindedVars, id2freshId, fd2FreshFd) + pathConstraints = pathConstraints.tail + pathConstraints ::= Not(cond)//Not(rCond) + val rElze = functionClosure(elze, bindedVars, id2freshId, fd2FreshFd) + pathConstraints = pathConstraints.tail + IfExpr(rCond, rThen, rElze).setType(i.getType) + } + case fi @ FunctionInvocation(fd, args) => fd2FreshFd.get(fd) match { + case None => FunctionInvocation(fd, args.map(arg => functionClosure(arg, bindedVars, id2freshId, fd2FreshFd))).setPosInfo(fi) + case Some((nfd, extraArgs)) => + FunctionInvocation(nfd, args.map(arg => functionClosure(arg, bindedVars, id2freshId, fd2FreshFd)) ++ + extraArgs.map(v => replace(id2freshId.map(p => (p._1.toVariable, p._2.toVariable)), v))).setPosInfo(fi) + } case n @ NAryOperator(args, recons) => { - var change = false - val rargs = args.map(a => functionClosure(a, bindedVars)) + val rargs = args.map(a => functionClosure(a, bindedVars, id2freshId, fd2FreshFd)) recons(rargs).setType(n.getType) } case b @ BinaryOperator(t1,t2,recons) => { - val r1 = functionClosure(t1, bindedVars) - val r2 = functionClosure(t2, bindedVars) + val r1 = functionClosure(t1, bindedVars, id2freshId, fd2FreshFd) + val r2 = functionClosure(t2, bindedVars, id2freshId, fd2FreshFd) recons(r1,r2).setType(b.getType) } case u @ UnaryOperator(t,recons) => { - val r = functionClosure(t, bindedVars) + val r = functionClosure(t, bindedVars, id2freshId, fd2FreshFd) recons(r).setType(u.getType) } - case i @ IfExpr(cond,then,elze) => { - val rCond = functionClosure(cond, bindedVars) - pathConstraints ::= rCond - val rThen = functionClosure(then, bindedVars) - pathConstraints = pathConstraints.tail - pathConstraints ::= Not(rCond) - val rElze = functionClosure(elze, bindedVars) - pathConstraints = pathConstraints.tail - IfExpr(rCond, rThen, rElze).setType(i.getType) + case m @ MatchExpr(scrut,cses) => { + val scrutRec = functionClosure(scrut, bindedVars, id2freshId, fd2FreshFd) + val csesRec = cses.map{ + case SimpleCase(pat, rhs) => { + val binders = pat.binders + val cond = conditionForPattern(scrut, pat) + pathConstraints ::= cond + val rRhs = functionClosure(rhs, bindedVars ++ binders, id2freshId, fd2FreshFd) + pathConstraints = pathConstraints.tail + SimpleCase(pat, rRhs) + } + case GuardedCase(pat, guard, rhs) => { + val binders = pat.binders + val cond = conditionForPattern(scrut, pat) + pathConstraints ::= cond + val rRhs = functionClosure(rhs, bindedVars ++ binders, id2freshId, fd2FreshFd) + val rGuard = functionClosure(guard, bindedVars ++ binders, id2freshId, fd2FreshFd) + pathConstraints = pathConstraints.tail + GuardedCase(pat, rGuard, rRhs) + } + } + val tpe = csesRec.head.rhs.getType + MatchExpr(scrutRec, csesRec).setType(tpe).setPosInfo(m) } - case m @ MatchExpr(scrut,cses) => { //TODO: will not work if there are actual nested function in cases - //val rScrut = functionClosure(scrut, bindedVars) - m + case v @ Variable(id) => id2freshId.get(id) match { + case None => v + case Some(nid) => Variable(nid) } case t if t.isInstanceOf[Terminal] => t case unhandled => scala.sys.error("Non-terminal case should be handled in FunctionClosure: " + unhandled) } + def freshIdInPat(pat: Pattern, id2freshId: Map[Identifier, Identifier]): Pattern = pat match { + case InstanceOfPattern(binder, classTypeDef) => InstanceOfPattern(binder.map(id2freshId(_)), classTypeDef) + case WildcardPattern(binder) => WildcardPattern(binder.map(id2freshId(_))) + case CaseClassPattern(binder, caseClassDef, subPatterns) => CaseClassPattern(binder.map(id2freshId(_)), caseClassDef, subPatterns.map(freshIdInPat(_, id2freshId))) + case TuplePattern(binder, subPatterns) => TuplePattern(binder.map(id2freshId(_)), subPatterns.map(freshIdInPat(_, id2freshId))) + } + //filter the list of constraints, only keeping those relevant to the set of variables def filterConstraints(vars: Set[Identifier]): (List[Expr], Set[Identifier]) = { var allVars = vars diff --git a/src/main/scala/leon/ImperativeCodeElimination.scala b/src/main/scala/leon/ImperativeCodeElimination.scala index eb139f824166e44feda604bd36976689a3ae7541..d3cd29907c5cd8c30fb574d06ddd1682e5bfa59a 100644 --- a/src/main/scala/leon/ImperativeCodeElimination.scala +++ b/src/main/scala/leon/ImperativeCodeElimination.scala @@ -15,11 +15,9 @@ object ImperativeCodeElimination extends Pass { def apply(pgm: Program): Program = { val allFuns = pgm.definedFunctions allFuns.foreach(fd => fd.body.map(body => { - Logger.debug("Transforming to functional code the following function:\n" + fd, 5, "imperative") parent = fd val (res, scope, _) = toFunction(body) fd.body = Some(scope(res)) - Logger.debug("Resulting functional code is:\n" + fd, 5, "imperative") })) pgm } @@ -52,8 +50,10 @@ object ImperativeCodeElimination extends Pass { val (tRes, tScope, tFun) = toFunction(tExpr) val (eRes, eScope, eFun) = toFunction(eExpr) + val iteRType = leastUpperBound(tRes.getType, eRes.getType).get + val modifiedVars: Seq[Identifier] = (tFun.keys ++ eFun.keys).toSet.intersect(varInScope).toSeq - val resId = FreshIdentifier("res").setType(ite.getType) + val resId = FreshIdentifier("res").setType(iteRType) val freshIds = modifiedVars.map(id => FreshIdentifier(id.name).setType(id.getType)) val iteType = if(modifiedVars.isEmpty) resId.getType else TupleType(resId.getType +: freshIds.map(_.getType)) @@ -76,7 +76,7 @@ object ImperativeCodeElimination extends Pass { if(freshIds.isEmpty) Let(resId, tupleId.toVariable, body) else - Let(resId, TupleSelect(tupleId.toVariable, 1), + Let(resId, TupleSelect(tupleId.toVariable, 1).setType(iteRType), freshIds.zipWithIndex.foldLeft(body)((b, id) => Let(id._1, TupleSelect(tupleId.toVariable, id._2 + 2).setType(id._1.getType), @@ -225,8 +225,13 @@ object ImperativeCodeElimination extends Pass { } case LetDef(fd, b) => { //Recall that here the nested function should not access mutable variables from an outside scope + val newFd = if(!fd.hasImplementation) fd else { + val (fdRes, fdScope, fdFun) = toFunction(fd.getBody) + fd.body = Some(fdScope(fdRes)) + fd + } val (bodyRes, bodyScope, bodyFun) = toFunction(b) - (bodyRes, (b2: Expr) => LetDef(fd, bodyScope(b2)), bodyFun) + (bodyRes, (b2: Expr) => LetDef(newFd, bodyScope(b2)), bodyFun) } case n @ NAryOperator(Seq(), recons) => (n, (body: Expr) => body, Map()) case n @ NAryOperator(args, recons) => { diff --git a/src/main/scala/leon/Main.scala b/src/main/scala/leon/Main.scala index e9818f16debfb19ccdbeaec6f1212f169918990f..9af1b41e731e32ff4b25942c4ec4f6ac63f91b0a 100644 --- a/src/main/scala/leon/Main.scala +++ b/src/main/scala/leon/Main.scala @@ -32,8 +32,9 @@ object Main { private def defaultAction(program: Program, reporter: Reporter) : Unit = { Logger.debug("Default action on program: " + program, 3, "main") - val passManager = new PassManager(Seq(EpsilonElimination, ImperativeCodeElimination, UnitElimination, FunctionClosure, FunctionHoisting, Simplificator)) + val passManager = new PassManager(Seq(ArrayTransformation, EpsilonElimination, ImperativeCodeElimination, /*UnitElimination,*/ FunctionClosure, /*FunctionHoisting,*/ Simplificator)) val program2 = passManager.run(program) + assert(program2.isPure) val analysis = new Analysis(program2, reporter) analysis.analyse } diff --git a/src/main/scala/leon/PassManager.scala b/src/main/scala/leon/PassManager.scala index bebd985778beeda1a7995ebb9917712aa7403e25..307a227ffb0ce0decb39624b1dde34bc6a589a39 100644 --- a/src/main/scala/leon/PassManager.scala +++ b/src/main/scala/leon/PassManager.scala @@ -8,6 +8,7 @@ class PassManager(passes: Seq[Pass]) { passes.foldLeft(program)((pgm, pass) => { Logger.debug("Running Pass: " + pass.description, 1, "passman") val newPgm = pass(pgm) + TypeChecking(newPgm) Logger.debug("Resulting program: " + newPgm, 3, "passman") newPgm }) diff --git a/src/main/scala/leon/Simplificator.scala b/src/main/scala/leon/Simplificator.scala index 5562c75db5965e7d5f5916aa8923da065b1e3bb6..d52d6fe0d380b90424c239f3d302fb3620d502c4 100644 --- a/src/main/scala/leon/Simplificator.scala +++ b/src/main/scala/leon/Simplificator.scala @@ -12,9 +12,11 @@ object Simplificator extends Pass { def apply(pgm: Program): Program = { val allFuns = pgm.definedFunctions - allFuns.foreach(fd => fd.body.map(body => { - fd.body = Some(simplifyLets(body)) - })) + allFuns.foreach(fd => { + fd.body = fd.body.map(simplifyLets) + fd.precondition = fd.precondition.map(simplifyLets) + fd.postcondition = fd.postcondition.map(simplifyLets) + }) pgm } diff --git a/src/main/scala/leon/TypeChecking.scala b/src/main/scala/leon/TypeChecking.scala new file mode 100644 index 0000000000000000000000000000000000000000..f6d4b259f19ab5e19e462cd4ba137d39bba16917 --- /dev/null +++ b/src/main/scala/leon/TypeChecking.scala @@ -0,0 +1,120 @@ +package leon + +import purescala.Common._ +import purescala.Definitions._ +import purescala.Trees._ +import purescala.TypeTrees._ + +object TypeChecking extends Pass { + + val description = "Type check the AST" + + def apply(pgm: Program): Program = { + val allFuns = pgm.definedFunctions + + allFuns.foreach(fd => { + fd.precondition.foreach(typeCheck) + fd.postcondition.foreach(typeCheck) + fd.body.foreach(typeCheck) + }) + + pgm + } + + private def typeCheck(expr: Expr): Unit = { //expr match { + //quick hack + //searchAndReplaceDFS(e => { + // if(e.getType == Untyped) { + // println("Expression is untyped: " + e) + // } + // None + //})(expr) + + //case l@Let(i, v, b) => { + // if(l.getType == Untyp + // val vr = transform(v) + // v.getType match { + // case ArrayType(_) => { + // val freshIdentifier = FreshIdentifier("t").setType(vr.getType) + // id2id += (i -> freshIdentifier) + // val br = transform(b) + // LetVar(freshIdentifier, vr, br) + // } + // case _ => { + // val br = transform(b) + // Let(i, vr, br) + // } + // } + //} + //case LetVar(id, e, b) => { + // val er = transform(e) + // val br = transform(b) + // LetVar(id, er, br) + //} + //case wh@While(c, e) => { + // val newWh = While(transform(c), transform(e)) + // newWh.invariant = wh.invariant.map(i => transform(i)) + // newWh.setPosInfo(wh) + //} + + //case ite@IfExpr(c, t, e) => { + // val rc = transform(c) + // val rt = transform(t) + // val re = transform(e) + // IfExpr(rc, rt, re).setType(rt.getType) + //} + + //case m @ MatchExpr(scrut, cses) => { + // val scrutRec = transform(scrut) + // val csesRec = cses.map{ + // case SimpleCase(pat, rhs) => SimpleCase(pat, transform(rhs)) + // case GuardedCase(pat, guard, rhs) => GuardedCase(pat, transform(guard), transform(rhs)) + // } + // val tpe = csesRec.head.rhs.getType + // MatchExpr(scrutRec, csesRec).setType(tpe).setPosInfo(m) + //} + //case LetDef(fd, b) => { + // val newFd = if(fd.hasImplementation) { + // val body = fd.body.get + // val args = fd.args + // val newFd = + // if(args.exists(vd => containsArrayType(vd.tpe)) || containsArrayType(fd.returnType)) { + // val newArgs = args.map(vd => { + // val freshId = FreshIdentifier(vd.id.name).setType(transform(vd.tpe)) + // id2id += (vd.id -> freshId) + // val newTpe = transform(vd.tpe) + // VarDecl(freshId, newTpe) + // }) + // val freshFunName = FreshIdentifier(fd.id.name) + // val freshFunDef = new FunDef(freshFunName, transform(fd.returnType), newArgs) + // fd2fd += (fd -> freshFunDef) + // freshFunDef.fromLoop = fd.fromLoop + // freshFunDef.parent = fd.parent + // freshFunDef.precondition = fd.precondition.map(transform) + // freshFunDef.postcondition = fd.postcondition.map(transform) + // freshFunDef.addAnnotation(fd.annotations.toSeq:_*) + // freshFunDef + // } else fd + // val newBody = transform(body) + // newFd.body = Some(newBody) + // newFd + // } else fd + // val rb = transform(b) + // LetDef(newFd, rb) + //} + //case FunctionInvocation(fd, args) => { + // val rargs = args.map(transform) + // val rfd = fd2fd.get(fd).getOrElse(fd) + // FunctionInvocation(rfd, rargs) + //} + + //case n @ NAryOperator(args, recons) => recons(args.map(transform)).setType(n.getType) + //case b @ BinaryOperator(a1, a2, recons) => recons(transform(a1), transform(a2)).setType(b.getType) + //case u @ UnaryOperator(a, recons) => recons(transform(a)).setType(u.getType) + + //case v @ Variable(id) => if(id2id.isDefinedAt(id)) Variable(id2id(id)) else v + //case (t: Terminal) => t + //case unhandled => scala.sys.error("Non-terminal case should be handled in ArrayTransformation: " + unhandled) + } + +} diff --git a/src/main/scala/leon/Utils.scala b/src/main/scala/leon/Utils.scala index ae60002b693a0b74657fa8640a7eef56a641703f..15006c7bc0820fae25274411ac4c5f5c9ef6a70f 100644 --- a/src/main/scala/leon/Utils.scala +++ b/src/main/scala/leon/Utils.scala @@ -16,4 +16,7 @@ object Utils { def invariant(x: Boolean): Unit = () } implicit def while2Invariant(u: Unit) = InvariantFunction + + + def waypoint[A](i: Int, expr: A): A = expr } diff --git a/src/main/scala/leon/VerificationCondition.scala b/src/main/scala/leon/VerificationCondition.scala index 01545bc3e87bd3606591b464f59098a5b0d8dd8c..e5556f1acf52fa3f8472556bcc66d3572c032488 100644 --- a/src/main/scala/leon/VerificationCondition.scala +++ b/src/main/scala/leon/VerificationCondition.scala @@ -49,6 +49,7 @@ object VCKind extends Enumeration { val Postcondition = Value("postcond.") val ExhaustiveMatch = Value("match.") val MapAccess = Value("map acc.") + val ArrayAccess = Value("arr. acc.") val InvariantInit = Value("inv init.") val InvariantInd = Value("inv ind.") val InvariantPost = Value("inv post.") diff --git a/src/main/scala/leon/plugin/CodeExtraction.scala b/src/main/scala/leon/plugin/CodeExtraction.scala index b64d6cb05d58ad1dd0420d27f21cfd0b3955b2bd..1ff95e4c922d08cf0b400c2049dc0d8139da6380 100644 --- a/src/main/scala/leon/plugin/CodeExtraction.scala +++ b/src/main/scala/leon/plugin/CodeExtraction.scala @@ -17,6 +17,7 @@ trait CodeExtraction extends Extractors { import StructuralExtractors._ import ExpressionExtractors._ + private lazy val setTraitSym = definitions.getClass("scala.collection.immutable.Set") private lazy val mapTraitSym = definitions.getClass("scala.collection.immutable.Map") private lazy val multisetTraitSym = try { @@ -28,6 +29,10 @@ trait CodeExtraction extends Extractors { private lazy val someClassSym = definitions.getClass("scala.Some") private lazy val function1TraitSym = definitions.getClass("scala.Function1") + private lazy val arraySym = definitions.getClass("scala.Array") + + def isArrayClassSym(sym: Symbol): Boolean = sym == arraySym + def isTuple2(sym : Symbol) : Boolean = sym == tuple2Sym def isTuple3(sym : Symbol) : Boolean = sym == tuple3Sym def isTuple4(sym : Symbol) : Boolean = sym == tuple4Sym @@ -198,6 +203,7 @@ trait CodeExtraction extends Extractors { a.atp.safeToString match { case "leon.Annotations.induct" => funDef.addAnnotation("induct") case "leon.Annotations.axiomatize" => funDef.addAnnotation("axiomatize") + case "leon.Annotations.main" => funDef.addAnnotation("main") case _ => ; } } @@ -245,6 +251,7 @@ trait CodeExtraction extends Extractors { val newParams = params.map(p => { val ptpe = st2ps(p.tpt.tpe) val newID = FreshIdentifier(p.name.toString).setType(ptpe) + owners += (Variable(newID) -> None) varSubsts(p.symbol) = (() => Variable(newID)) VarDecl(newID, ptpe) }) @@ -255,6 +262,8 @@ trait CodeExtraction extends Extractors { var realBody = body var reqCont: Option[Expr] = None var ensCont: Option[Expr] = None + + currentFunDef = funDef realBody match { case ExEnsuredExpression(body2, resSym, contract) => { @@ -285,15 +294,23 @@ trait CodeExtraction extends Extractors { case e: ImpureCodeEncounteredException => None } + bodyAttempt.foreach(e => + if(e.getType.isInstanceOf[ArrayType]) { + //println(owners) + //println(getOwner(e)) + getOwner(e) match { + case Some(Some(fd)) if fd == funDef => + case None => + case _ => unit.error(realBody.pos, "Function cannot return an array that is not locally defined") + } + }) reqCont.map(e => if(containsLetDef(e)) { unit.error(realBody.pos, "Function precondtion should not contain nested function definition") - throw ImpureCodeEncounteredException(realBody) }) ensCont.map(e => if(containsLetDef(e)) { unit.error(realBody.pos, "Function postcondition should not contain nested function definition") - throw ImpureCodeEncounteredException(realBody) }) funDef.body = bodyAttempt funDef.precondition = reqCont @@ -335,6 +352,13 @@ trait CodeExtraction extends Extractors { } } + + private var currentFunDef: FunDef = null + + //This is a bit missleading, if an expr is not mapped then it has no owner, if it is mapped to None it means + //that it can have any owner + private var owners: Map[Expr, Option[FunDef]] = Map() + /** Forces conversion from scalac AST to purescala AST, throws an Exception * if impossible. If not in 'silent mode', non-pure AST nodes are reported as * errors. */ @@ -400,6 +424,7 @@ trait CodeExtraction extends Extractors { val newParams = params.map(p => { val ptpe = scalaType2PureScala(unit, silent) (p.tpt.tpe) val newID = FreshIdentifier(p.name.toString).setType(ptpe) + owners += (Variable(newID) -> None) varSubsts(p.symbol) = (() => Variable(newID)) VarDecl(newID, ptpe) }) @@ -411,6 +436,8 @@ trait CodeExtraction extends Extractors { var reqCont: Option[Expr] = None var ensCont: Option[Expr] = None + currentFunDef = funDef + realBody match { case ExEnsuredExpression(body2, resSym, contract) => { varSubsts(resSym) = (() => ResultVariable().setType(funDef.returnType)) @@ -440,15 +467,22 @@ trait CodeExtraction extends Extractors { case e: ImpureCodeEncounteredException => None } - reqCont.map(e => + bodyAttempt.foreach(e => + if(e.getType.isInstanceOf[ArrayType]) { + getOwner(e) match { + case Some(Some(fd)) if fd == funDef => + case None => + case _ => unit.error(realBody.pos, "Function cannot return an array that is not locally defined") + } + }) + + reqCont.foreach(e => if(containsLetDef(e)) { unit.error(realBody.pos, "Function precondtion should not contain nested function definition") - throw ImpureCodeEncounteredException(realBody) }) - ensCont.map(e => + ensCont.foreach(e => if(containsLetDef(e)) { unit.error(realBody.pos, "Function postcondition should not contain nested function definition") - throw ImpureCodeEncounteredException(realBody) }) funDef.body = bodyAttempt funDef.precondition = reqCont @@ -465,429 +499,530 @@ trait CodeExtraction extends Extractors { case _ => (tr, None) } - var handleRest = true - val psExpr = nextExpr match { - case ExTuple(tpes, exprs) => { - val tupleType = TupleType(tpes.map(tpe => scalaType2PureScala(unit, silent)(tpe))) - val tupleExprs = exprs.map(e => rec(e)) - Tuple(tupleExprs).setType(tupleType) - } - case ExTupleExtract(tuple, index) => { - val tupleExpr = rec(tuple) - val TupleType(tpes) = tupleExpr.getType - if(tpes.size < index) - throw ImpureCodeEncounteredException(tree) - else - TupleSelect(tupleExpr, index).setType(tpes(index-1)) - } - case ExValDef(vs, tpt, bdy) => { - val binderTpe = scalaType2PureScala(unit, silent)(tpt.tpe) - val newID = FreshIdentifier(vs.name.toString).setType(binderTpe) - val valTree = rec(bdy) - val restTree = rest match { - case Some(rst) => { - varSubsts(vs) = (() => Variable(newID)) - val res = rec(rst) - varSubsts.remove(vs) - res - } - case None => UnitLiteral - } - handleRest = false - val res = Let(newID, valTree, restTree) - res - } - case dd@ExFunctionDef(n, p, t, b) => { - val funDef = extractFunSig(n, p, t).setPosInfo(dd.pos.line, dd.pos.column) - defsToDefs += (dd.symbol -> funDef) - val oldMutableVarSubst = mutableVarSubsts.toMap //take an immutable snapshot of the map - mutableVarSubsts.clear //reseting the visible mutable vars, we do not handle mutable variable closure in nested functions - val funDefWithBody = extractFunDef(funDef, b) - mutableVarSubsts ++= oldMutableVarSubst - val restTree = rest match { - case Some(rst) => rec(rst) - case None => UnitLiteral - } - defsToDefs.remove(dd.symbol) - handleRest = false - LetDef(funDefWithBody, restTree) - } - case ExVarDef(vs, tpt, bdy) => { - val binderTpe = scalaType2PureScala(unit, silent)(tpt.tpe) - val newID = FreshIdentifier(vs.name.toString).setType(binderTpe) - val valTree = rec(bdy) - mutableVarSubsts += (vs -> (() => Variable(newID))) - val restTree = rest match { - case Some(rst) => { - varSubsts(vs) = (() => Variable(newID)) - val res = rec(rst) - varSubsts.remove(vs) - res - } - case None => UnitLiteral - } - handleRest = false - val res = LetVar(newID, valTree, restTree) - res + val e2: Option[Expr] = nextExpr match { + case ExParameterlessMethodCall(t,n) => { + val selector = rec(t) + val selType = selector.getType + + if(!selType.isInstanceOf[CaseClassType]) + None + else { + + val selDef: CaseClassDef = selType.asInstanceOf[CaseClassType].classDef + + val fieldID = selDef.fields.find(_.id.name == n.toString) match { + case None => { + if(!silent) + unit.error(tr.pos, "Invalid method or field invocation (not a case class arg?)") + throw ImpureCodeEncounteredException(tr) + } + case Some(vd) => vd.id + } + + Some(CaseClassSelector(selDef, selector, fieldID).setType(fieldID.getType)) + } } + case _ => None + } + var handleRest = true + val psExpr = e2 match { + case Some(e3) => e3 + case None => nextExpr match { + case ExTuple(tpes, exprs) => { + val tupleExprs = exprs.map(e => rec(e)) + val tupleType = TupleType(tupleExprs.map(expr => bestRealType(expr.getType))) + Tuple(tupleExprs).setType(tupleType) + } + case ExTupleExtract(tuple, index) => { + val tupleExpr = rec(tuple) + val TupleType(tpes) = tupleExpr.getType + if(tpes.size < index) + throw ImpureCodeEncounteredException(tree) + else + TupleSelect(tupleExpr, index).setType(tpes(index-1)) + } + case ExValDef(vs, tpt, bdy) => { + val binderTpe = scalaType2PureScala(unit, silent)(tpt.tpe) + val newID = FreshIdentifier(vs.name.toString).setType(binderTpe) + val valTree = rec(bdy) + handleRest = false + if(valTree.getType.isInstanceOf[ArrayType]) { + getOwner(valTree) match { + case None => + owners += (Variable(newID) -> Some(currentFunDef)) + case Some(_) => + unit.error(nextExpr.pos, "Cannot alias array") + throw ImpureCodeEncounteredException(nextExpr) + } + } + val restTree = rest match { + case Some(rst) => { + varSubsts(vs) = (() => Variable(newID)) + val res = rec(rst) + varSubsts.remove(vs) + res + } + case None => UnitLiteral + } + val res = Let(newID, valTree, restTree) + res + } + case dd@ExFunctionDef(n, p, t, b) => { + val funDef = extractFunSig(n, p, t).setPosInfo(dd.pos.line, dd.pos.column) + defsToDefs += (dd.symbol -> funDef) + val oldMutableVarSubst = mutableVarSubsts.toMap //take an immutable snapshot of the map + val oldCurrentFunDef = currentFunDef + mutableVarSubsts.clear //reseting the visible mutable vars, we do not handle mutable variable closure in nested functions + val funDefWithBody = extractFunDef(funDef, b) + mutableVarSubsts ++= oldMutableVarSubst + currentFunDef = oldCurrentFunDef + val restTree = rest match { + case Some(rst) => rec(rst) + case None => UnitLiteral + } + defsToDefs.remove(dd.symbol) + handleRest = false + LetDef(funDefWithBody, restTree) + } + case ExVarDef(vs, tpt, bdy) => { + val binderTpe = scalaType2PureScala(unit, silent)(tpt.tpe) + //binderTpe match { + // case ArrayType(_) => + // unit.error(tree.pos, "Cannot declare array variables, only val are alllowed") + // throw ImpureCodeEncounteredException(tree) + // case _ => + //} + val newID = FreshIdentifier(vs.name.toString).setType(binderTpe) + val valTree = rec(bdy) + mutableVarSubsts += (vs -> (() => Variable(newID))) + handleRest = false + if(valTree.getType.isInstanceOf[ArrayType]) { + getOwner(valTree) match { + case None => + owners += (Variable(newID) -> Some(currentFunDef)) + case Some(_) => + unit.error(nextExpr.pos, "Cannot alias array") + throw ImpureCodeEncounteredException(nextExpr) + } + } + val restTree = rest match { + case Some(rst) => { + varSubsts(vs) = (() => Variable(newID)) + val res = rec(rst) + varSubsts.remove(vs) + res + } + case None => UnitLiteral + } + val res = LetVar(newID, valTree, restTree) + res + } - case ExAssign(sym, rhs) => mutableVarSubsts.get(sym) match { - case Some(fun) => { - val Variable(id) = fun() - val rhsTree = rec(rhs) - Assignment(id, rhsTree) + case ExAssign(sym, rhs) => mutableVarSubsts.get(sym) match { + case Some(fun) => { + val Variable(id) = fun() + val rhsTree = rec(rhs) + if(rhsTree.getType.isInstanceOf[ArrayType]) { + getOwner(rhsTree) match { + case None => + case Some(_) => + unit.error(nextExpr.pos, "Cannot alias array") + throw ImpureCodeEncounteredException(nextExpr) + } + } + Assignment(id, rhsTree) + } + case None => { + unit.error(tr.pos, "Undeclared variable.") + throw ImpureCodeEncounteredException(tr) + } } - case None => { - unit.error(tr.pos, "Undeclared variable.") - throw ImpureCodeEncounteredException(tr) + case wh@ExWhile(cond, body) => { + val condTree = rec(cond) + val bodyTree = rec(body) + While(condTree, bodyTree).setPosInfo(wh.pos.line,wh.pos.column) + } + case wh@ExWhileWithInvariant(cond, body, inv) => { + val condTree = rec(cond) + val bodyTree = rec(body) + val invTree = rec(inv) + val w = While(condTree, bodyTree).setPosInfo(wh.pos.line,wh.pos.column) + w.invariant = Some(invTree) + w } - } - case wh@ExWhile(cond, body) => { - val condTree = rec(cond) - val bodyTree = rec(body) - While(condTree, bodyTree).setPosInfo(wh.pos.line,wh.pos.column) - } - case wh@ExWhileWithInvariant(cond, body, inv) => { - val condTree = rec(cond) - val bodyTree = rec(body) - val invTree = rec(inv) - val w = While(condTree, bodyTree).setPosInfo(wh.pos.line,wh.pos.column) - w.invariant = Some(invTree) - w - } - case ExInt32Literal(v) => IntLiteral(v).setType(Int32Type) - case ExBooleanLiteral(v) => BooleanLiteral(v).setType(BooleanType) - case ExUnitLiteral() => UnitLiteral + case ExInt32Literal(v) => IntLiteral(v).setType(Int32Type) + case ExBooleanLiteral(v) => BooleanLiteral(v).setType(BooleanType) + case ExUnitLiteral() => UnitLiteral - case ExTyped(e,tpt) => rec(e) - case ExIdentifier(sym,tpt) => varSubsts.get(sym) match { - case Some(fun) => fun() - case None => mutableVarSubsts.get(sym) match { + case ExTyped(e,tpt) => rec(e) + case ExIdentifier(sym,tpt) => varSubsts.get(sym) match { case Some(fun) => fun() - case None => { - unit.error(tr.pos, "Unidentified variable.") - throw ImpureCodeEncounteredException(tr) + case None => mutableVarSubsts.get(sym) match { + case Some(fun) => fun() + case None => { + unit.error(tr.pos, "Unidentified variable.") + throw ImpureCodeEncounteredException(tr) + } } } - } - case epsi@ExEpsilonExpression(tpe, varSym, predBody) => { - val pstpe = scalaType2PureScala(unit, silent)(tpe) - val previousVarSubst: Option[Function0[Expr]] = varSubsts.get(varSym) //save the previous in case of nested epsilon - varSubsts(varSym) = (() => EpsilonVariable((epsi.pos.line, epsi.pos.column)).setType(pstpe)) - val c1 = rec(predBody) - previousVarSubst match { - case Some(f) => varSubsts(varSym) = f - case None => varSubsts.remove(varSym) - } - if(containsEpsilon(c1)) { - unit.error(epsi.pos, "Usage of nested epsilon is not allowed.") - throw ImpureCodeEncounteredException(epsi) - } - Epsilon(c1).setType(pstpe).setPosInfo(epsi.pos.line, epsi.pos.column) - } - case ExSomeConstruction(tpe, arg) => { - // println("Got Some !" + tpe + ":" + arg) - val underlying = scalaType2PureScala(unit, silent)(tpe) - OptionSome(rec(arg)).setType(OptionType(underlying)) - } - case ExCaseClassConstruction(tpt, args) => { - val cctype = scalaType2PureScala(unit, silent)(tpt.tpe) - if(!cctype.isInstanceOf[CaseClassType]) { - if(!silent) { - unit.error(tr.pos, "Construction of a non-case class.") + case epsi@ExEpsilonExpression(tpe, varSym, predBody) => { + val pstpe = scalaType2PureScala(unit, silent)(tpe) + val previousVarSubst: Option[Function0[Expr]] = varSubsts.get(varSym) //save the previous in case of nested epsilon + varSubsts(varSym) = (() => EpsilonVariable((epsi.pos.line, epsi.pos.column)).setType(pstpe)) + val c1 = rec(predBody) + previousVarSubst match { + case Some(f) => varSubsts(varSym) = f + case None => varSubsts.remove(varSym) } - throw ImpureCodeEncounteredException(tree) + if(containsEpsilon(c1)) { + unit.error(epsi.pos, "Usage of nested epsilon is not allowed.") + throw ImpureCodeEncounteredException(epsi) + } + Epsilon(c1).setType(pstpe).setPosInfo(epsi.pos.line, epsi.pos.column) } - val nargs = args.map(rec(_)) - val cct = cctype.asInstanceOf[CaseClassType] - CaseClass(cct.classDef, nargs).setType(cct) - } - case ExAnd(l, r) => And(rec(l), rec(r)).setType(BooleanType) - case ExOr(l, r) => Or(rec(l), rec(r)).setType(BooleanType) - case ExNot(e) => Not(rec(e)).setType(BooleanType) - case ExUMinus(e) => UMinus(rec(e)).setType(Int32Type) - case ExPlus(l, r) => Plus(rec(l), rec(r)).setType(Int32Type) - case ExMinus(l, r) => Minus(rec(l), rec(r)).setType(Int32Type) - case ExTimes(l, r) => Times(rec(l), rec(r)).setType(Int32Type) - case ExDiv(l, r) => Division(rec(l), rec(r)).setType(Int32Type) - case ExMod(l, r) => Modulo(rec(l), rec(r)).setType(Int32Type) - case ExEquals(l, r) => { - val rl = rec(l) - val rr = rec(r) - ((rl.getType,rr.getType) match { - case (SetType(_), SetType(_)) => SetEquals(rl, rr) - case (BooleanType, BooleanType) => Iff(rl, rr) - case (_, _) => Equals(rl, rr) - }).setType(BooleanType) - } - case ExNotEquals(l, r) => Not(Equals(rec(l), rec(r)).setType(BooleanType)).setType(BooleanType) - case ExGreaterThan(l, r) => GreaterThan(rec(l), rec(r)).setType(BooleanType) - case ExGreaterEqThan(l, r) => GreaterEquals(rec(l), rec(r)).setType(BooleanType) - case ExLessThan(l, r) => LessThan(rec(l), rec(r)).setType(BooleanType) - case ExLessEqThan(l, r) => LessEquals(rec(l), rec(r)).setType(BooleanType) - case ExFiniteSet(tt, args) => { - val underlying = scalaType2PureScala(unit, silent)(tt.tpe) - FiniteSet(args.map(rec(_))).setType(SetType(underlying)) - } - case ExFiniteMultiset(tt, args) => { - val underlying = scalaType2PureScala(unit, silent)(tt.tpe) - FiniteMultiset(args.map(rec(_))).setType(MultisetType(underlying)) - } - case ExEmptySet(tt) => { - val underlying = scalaType2PureScala(unit, silent)(tt.tpe) - EmptySet(underlying).setType(SetType(underlying)) - } - case ExEmptyMultiset(tt) => { - val underlying = scalaType2PureScala(unit, silent)(tt.tpe) - EmptyMultiset(underlying).setType(MultisetType(underlying)) - } - case ExEmptyMap(ft, tt) => { - val fromUnderlying = scalaType2PureScala(unit, silent)(ft.tpe) - val toUnderlying = scalaType2PureScala(unit, silent)(tt.tpe) - EmptyMap(fromUnderlying, toUnderlying).setType(MapType(fromUnderlying, toUnderlying)) - } - case ExSetMin(t) => { - val set = rec(t) - if(!set.getType.isInstanceOf[SetType]) { - if(!silent) unit.error(t.pos, "Min should be computed on a set.") - throw ImpureCodeEncounteredException(tree) + case ExWaypointExpression(tpe, i, tree) => { + val pstpe = scalaType2PureScala(unit, silent)(tpe) + val IntLiteral(ri) = rec(i) + Waypoint(ri, rec(tree)).setType(pstpe) } - SetMin(set).setType(set.getType.asInstanceOf[SetType].base) - } - case ExSetMax(t) => { - val set = rec(t) - if(!set.getType.isInstanceOf[SetType]) { - if(!silent) unit.error(t.pos, "Max should be computed on a set.") - throw ImpureCodeEncounteredException(tree) + case ExSomeConstruction(tpe, arg) => { + // println("Got Some !" + tpe + ":" + arg) + val underlying = scalaType2PureScala(unit, silent)(tpe) + OptionSome(rec(arg)).setType(OptionType(underlying)) } - SetMax(set).setType(set.getType.asInstanceOf[SetType].base) - } - case ExUnion(t1,t2) => { - val rl = rec(t1) - val rr = rec(t2) - rl.getType match { - case s @ SetType(_) => SetUnion(rl, rr).setType(s) - case m @ MultisetType(_) => MultisetUnion(rl, rr).setType(m) - case _ => { - if(!silent) unit.error(tree.pos, "Union of non set/multiset expressions.") + case ExCaseClassConstruction(tpt, args) => { + val cctype = scalaType2PureScala(unit, silent)(tpt.tpe) + if(!cctype.isInstanceOf[CaseClassType]) { + if(!silent) { + unit.error(tr.pos, "Construction of a non-case class.") + } throw ImpureCodeEncounteredException(tree) } + val nargs = args.map(rec(_)) + val cct = cctype.asInstanceOf[CaseClassType] + CaseClass(cct.classDef, nargs).setType(cct) } - } - case ExIntersection(t1,t2) => { - val rl = rec(t1) - val rr = rec(t2) - rl.getType match { - case s @ SetType(_) => SetIntersection(rl, rr).setType(s) - case m @ MultisetType(_) => MultisetIntersection(rl, rr).setType(m) - case _ => { - if(!silent) unit.error(tree.pos, "Intersection of non set/multiset expressions.") + case ExAnd(l, r) => And(rec(l), rec(r)).setType(BooleanType) + case ExOr(l, r) => Or(rec(l), rec(r)).setType(BooleanType) + case ExNot(e) => Not(rec(e)).setType(BooleanType) + case ExUMinus(e) => UMinus(rec(e)).setType(Int32Type) + case ExPlus(l, r) => Plus(rec(l), rec(r)).setType(Int32Type) + case ExMinus(l, r) => Minus(rec(l), rec(r)).setType(Int32Type) + case ExTimes(l, r) => Times(rec(l), rec(r)).setType(Int32Type) + case ExDiv(l, r) => Division(rec(l), rec(r)).setType(Int32Type) + case ExMod(l, r) => Modulo(rec(l), rec(r)).setType(Int32Type) + case ExEquals(l, r) => { + val rl = rec(l) + val rr = rec(r) + ((rl.getType,rr.getType) match { + case (SetType(_), SetType(_)) => SetEquals(rl, rr) + case (BooleanType, BooleanType) => Iff(rl, rr) + case (_, _) => Equals(rl, rr) + }).setType(BooleanType) + } + case ExNotEquals(l, r) => Not(Equals(rec(l), rec(r)).setType(BooleanType)).setType(BooleanType) + case ExGreaterThan(l, r) => GreaterThan(rec(l), rec(r)).setType(BooleanType) + case ExGreaterEqThan(l, r) => GreaterEquals(rec(l), rec(r)).setType(BooleanType) + case ExLessThan(l, r) => LessThan(rec(l), rec(r)).setType(BooleanType) + case ExLessEqThan(l, r) => LessEquals(rec(l), rec(r)).setType(BooleanType) + case ExFiniteSet(tt, args) => { + val underlying = scalaType2PureScala(unit, silent)(tt.tpe) + FiniteSet(args.map(rec(_))).setType(SetType(underlying)) + } + case ExFiniteMultiset(tt, args) => { + val underlying = scalaType2PureScala(unit, silent)(tt.tpe) + FiniteMultiset(args.map(rec(_))).setType(MultisetType(underlying)) + } + case ExEmptySet(tt) => { + val underlying = scalaType2PureScala(unit, silent)(tt.tpe) + EmptySet(underlying).setType(SetType(underlying)) + } + case ExEmptyMultiset(tt) => { + val underlying = scalaType2PureScala(unit, silent)(tt.tpe) + EmptyMultiset(underlying).setType(MultisetType(underlying)) + } + case ExEmptyMap(ft, tt) => { + val fromUnderlying = scalaType2PureScala(unit, silent)(ft.tpe) + val toUnderlying = scalaType2PureScala(unit, silent)(tt.tpe) + EmptyMap(fromUnderlying, toUnderlying).setType(MapType(fromUnderlying, toUnderlying)) + } + case ExSetMin(t) => { + val set = rec(t) + if(!set.getType.isInstanceOf[SetType]) { + if(!silent) unit.error(t.pos, "Min should be computed on a set.") throw ImpureCodeEncounteredException(tree) } + SetMin(set).setType(set.getType.asInstanceOf[SetType].base) } - } - case ExSetContains(t1,t2) => { - val rl = rec(t1) - val rr = rec(t2) - rl.getType match { - case s @ SetType(_) => ElementOfSet(rr, rl) - case _ => { - if(!silent) unit.error(tree.pos, ".contains on non set expression.") + case ExSetMax(t) => { + val set = rec(t) + if(!set.getType.isInstanceOf[SetType]) { + if(!silent) unit.error(t.pos, "Max should be computed on a set.") throw ImpureCodeEncounteredException(tree) } + SetMax(set).setType(set.getType.asInstanceOf[SetType].base) } - } - case ExSetSubset(t1,t2) => { - val rl = rec(t1) - val rr = rec(t2) - rl.getType match { - case s @ SetType(_) => SubsetOf(rl, rr) - case _ => { - if(!silent) unit.error(tree.pos, "Subset on non set expression.") - throw ImpureCodeEncounteredException(tree) + case ExUnion(t1,t2) => { + val rl = rec(t1) + val rr = rec(t2) + rl.getType match { + case s @ SetType(_) => SetUnion(rl, rr).setType(s) + case m @ MultisetType(_) => MultisetUnion(rl, rr).setType(m) + case _ => { + if(!silent) unit.error(tree.pos, "Union of non set/multiset expressions.") + throw ImpureCodeEncounteredException(tree) + } } } - } - case ExSetMinus(t1,t2) => { - val rl = rec(t1) - val rr = rec(t2) - rl.getType match { - case s @ SetType(_) => SetDifference(rl, rr).setType(s) - case m @ MultisetType(_) => MultisetDifference(rl, rr).setType(m) - case _ => { - if(!silent) unit.error(tree.pos, "Difference of non set/multiset expressions.") - throw ImpureCodeEncounteredException(tree) + case ExIntersection(t1,t2) => { + val rl = rec(t1) + val rr = rec(t2) + rl.getType match { + case s @ SetType(_) => SetIntersection(rl, rr).setType(s) + case m @ MultisetType(_) => MultisetIntersection(rl, rr).setType(m) + case _ => { + if(!silent) unit.error(tree.pos, "Intersection of non set/multiset expressions.") + throw ImpureCodeEncounteredException(tree) + } } } - } - case ExSetCard(t) => { - val rt = rec(t) - rt.getType match { - case s @ SetType(_) => SetCardinality(rt) - case m @ MultisetType(_) => MultisetCardinality(rt) - case _ => { - if(!silent) unit.error(tree.pos, "Cardinality of non set/multiset expressions.") - throw ImpureCodeEncounteredException(tree) + case ExSetContains(t1,t2) => { + val rl = rec(t1) + val rr = rec(t2) + rl.getType match { + case s @ SetType(_) => ElementOfSet(rr, rl) + case _ => { + if(!silent) unit.error(tree.pos, ".contains on non set expression.") + throw ImpureCodeEncounteredException(tree) + } } } - } - case ExMultisetToSet(t) => { - val rt = rec(t) - rt.getType match { - case m @ MultisetType(u) => MultisetToSet(rt).setType(SetType(u)) - case _ => { - if(!silent) unit.error(tree.pos, "toSet can only be applied to multisets.") - throw ImpureCodeEncounteredException(tree) + case ExSetSubset(t1,t2) => { + val rl = rec(t1) + val rr = rec(t2) + rl.getType match { + case s @ SetType(_) => SubsetOf(rl, rr) + case _ => { + if(!silent) unit.error(tree.pos, "Subset on non set expression.") + throw ImpureCodeEncounteredException(tree) + } } } - } - case ExMapUpdated(m,f,t) => { - val rm = rec(m) - val rf = rec(f) - val rt = rec(t) - val newSingleton = SingletonMap(rf, rt).setType(rm.getType) - rm.getType match { - case MapType(ft, tt) => - MapUnion(rm, FiniteMap(Seq(newSingleton)).setType(rm.getType)).setType(rm.getType) - case _ => { - if (!silent) unit.error(tree.pos, "updated can only be applied to maps.") - throw ImpureCodeEncounteredException(tree) + case ExSetMinus(t1,t2) => { + val rl = rec(t1) + val rr = rec(t2) + rl.getType match { + case s @ SetType(_) => SetDifference(rl, rr).setType(s) + case m @ MultisetType(_) => MultisetDifference(rl, rr).setType(m) + case _ => { + if(!silent) unit.error(tree.pos, "Difference of non set/multiset expressions.") + throw ImpureCodeEncounteredException(tree) + } + } + } + case ExSetCard(t) => { + val rt = rec(t) + rt.getType match { + case s @ SetType(_) => SetCardinality(rt) + case m @ MultisetType(_) => MultisetCardinality(rt) + case _ => { + if(!silent) unit.error(tree.pos, "Cardinality of non set/multiset expressions.") + throw ImpureCodeEncounteredException(tree) + } } } - } - case ExMapIsDefinedAt(m,k) => { - val rm = rec(m) - val rk = rec(k) - MapIsDefinedAt(rm, rk) - } - - case ExPlusPlusPlus(t1,t2) => { - val rl = rec(t1) - val rr = rec(t2) - MultisetPlus(rl, rr).setType(rl.getType) - } - case app@ExApply(lhs,args) => { - val rlhs = rec(lhs) - val rargs = args map rec - rlhs.getType match { - case MapType(_,tt) => - assert(rargs.size == 1) - MapGet(rlhs, rargs.head).setType(tt).setPosInfo(app.pos.line, app.pos.column) - case FunctionType(fts, tt) => { - rlhs match { - case Variable(id) => - AnonymousFunctionInvocation(id, rargs).setType(tt) - case _ => { - if (!silent) unit.error(tree.pos, "apply on non-variable or non-map expression") - throw ImpureCodeEncounteredException(tree) - } + case ExMultisetToSet(t) => { + val rt = rec(t) + rt.getType match { + case m @ MultisetType(u) => MultisetToSet(rt).setType(SetType(u)) + case _ => { + if(!silent) unit.error(tree.pos, "toSet can only be applied to multisets.") + throw ImpureCodeEncounteredException(tree) } } - case _ => { - if (!silent) unit.error(tree.pos, "apply on unexpected type") - throw ImpureCodeEncounteredException(tree) + } + case up@ExUpdated(m,f,t) => { + val rm = rec(m) + val rf = rec(f) + val rt = rec(t) + rm.getType match { + case MapType(ft, tt) => { + val newSingleton = SingletonMap(rf, rt).setType(rm.getType) + MapUnion(rm, FiniteMap(Seq(newSingleton)).setType(rm.getType)).setType(rm.getType) + } + case ArrayType(bt) => { + ArrayUpdated(rm, rf, rt).setType(rm.getType).setPosInfo(up.pos.line, up.pos.column) + } + case _ => { + if (!silent) unit.error(tree.pos, "updated can only be applied to maps.") + throw ImpureCodeEncounteredException(tree) + } } } - } - case ExIfThenElse(t1,t2,t3) => { - val r1 = rec(t1) - val r2 = rec(t2) - val r3 = rec(t3) - IfExpr(r1, r2, r3).setType(leastUpperBound(r2.getType, r3.getType)) - } - - case ExIsInstanceOf(tt, cc) => { - val ccRec = rec(cc) - val checkType = scalaType2PureScala(unit, silent)(tt.tpe) - checkType match { - case CaseClassType(ccd) => { - val rootType: ClassTypeDef = if(ccd.parent != None) ccd.parent.get else ccd - if(!ccRec.getType.isInstanceOf[ClassType]) { - unit.error(tr.pos, "isInstanceOf can only be used with a case class") - throw ImpureCodeEncounteredException(tr) - } else { - val testedExprType = ccRec.getType.asInstanceOf[ClassType].classDef - val testedExprRootType: ClassTypeDef = if(testedExprType.parent != None) testedExprType.parent.get else testedExprType + case ExMapIsDefinedAt(m,k) => { + val rm = rec(m) + val rk = rec(k) + MapIsDefinedAt(rm, rk) + } - if(rootType != testedExprRootType) { - unit.error(tr.pos, "isInstanceOf can only be used with compatible case classes") - throw ImpureCodeEncounteredException(tr) - } else { - CaseClassInstanceOf(ccd, ccRec) + case ExPlusPlusPlus(t1,t2) => { + val rl = rec(t1) + val rr = rec(t2) + MultisetPlus(rl, rr).setType(rl.getType) + } + case app@ExApply(lhs,args) => { + val rlhs = rec(lhs) + val rargs = args map rec + rlhs.getType match { + case MapType(_,tt) => + assert(rargs.size == 1) + MapGet(rlhs, rargs.head).setType(tt).setPosInfo(app.pos.line, app.pos.column) + case FunctionType(fts, tt) => { + rlhs match { + case Variable(id) => + AnonymousFunctionInvocation(id, rargs).setType(tt) + case _ => { + if (!silent) unit.error(tree.pos, "apply on non-variable or non-map expression") + throw ImpureCodeEncounteredException(tree) + } } } + case ArrayType(bt) => { + assert(rargs.size == 1) + ArraySelect(rlhs, rargs.head).setType(bt).setPosInfo(app.pos.line, app.pos.column) + } + case _ => { + if (!silent) unit.error(tree.pos, "apply on unexpected type") + throw ImpureCodeEncounteredException(tree) + } } - case _ => { - unit.error(tr.pos, "isInstanceOf can only be used with a case class") - throw ImpureCodeEncounteredException(tr) + } + // for now update only occurs on Array. later we might have to distinguished depending on the type of the lhs + case update@ExUpdate(lhs, index, newValue) => { + val lhsRec = rec(lhs) + lhsRec match { + case Variable(_) => + case _ => { + unit.error(tree.pos, "array update only works on variables") + throw ImpureCodeEncounteredException(tree) + } } + getOwner(lhsRec) match { + case Some(Some(fd)) if fd != currentFunDef => + unit.error(nextExpr.pos, "cannot update an array that is not defined locally") + throw ImpureCodeEncounteredException(nextExpr) + case Some(None) => + unit.error(nextExpr.pos, "cannot update an array that is not defined locally") + throw ImpureCodeEncounteredException(nextExpr) + case Some(_) => + case None => sys.error("This array: " + lhsRec + " should have had an owner") + } + val indexRec = rec(index) + val newValueRec = rec(newValue) + ArrayUpdate(lhsRec, indexRec, newValueRec).setPosInfo(update.pos.line, update.pos.column) } - } - - case lc @ ExLocalCall(sy,nm,ar) => { - if(defsToDefs.keysIterator.find(_ == sy).isEmpty) { - if(!silent) - unit.error(tr.pos, "Invoking an invalid function.") - throw ImpureCodeEncounteredException(tr) + case ExArrayLength(t) => { + val rt = rec(t) + ArrayLength(rt) } - val fd = defsToDefs(sy) - FunctionInvocation(fd, ar.map(rec(_))).setType(fd.returnType).setPosInfo(lc.pos.line,lc.pos.column) - } - case pm @ ExPatternMatching(sel, cses) => { - val rs = rec(sel) - val rc = cses.map(rewriteCaseDef(_)) - val rt: purescala.TypeTrees.TypeTree = rc.map(_.rhs.getType).reduceLeft(leastUpperBound(_,_)) - MatchExpr(rs, rc).setType(rt).setPosInfo(pm.pos.line,pm.pos.column) - } - - // this one should stay after all others, cause it also catches UMinus - // and Not, for instance. - case ExParameterlessMethodCall(t,n) => { - val selector = rec(t) - val selType = selector.getType - - if(!selType.isInstanceOf[CaseClassType]) { - if(!silent) - unit.error(tr.pos, "Invalid method or field invocation (not purescala?)") - throw ImpureCodeEncounteredException(tr) + case ExArrayClone(t) => { + val rt = rec(t) + ArrayClone(rt) + } + case ExArrayFill(baseType, length, defaultValue) => { + val underlying = scalaType2PureScala(unit, silent)(baseType.tpe) + val lengthRec = rec(length) + val defaultValueRec = rec(defaultValue) + ArrayFill(lengthRec, defaultValueRec).setType(ArrayType(underlying)) + } + case ExIfThenElse(t1,t2,t3) => { + val r1 = rec(t1) + if(containsLetDef(r1)) { + unit.error(t1.pos, "Condition of if-then-else expression should not contain nested function definition") + throw ImpureCodeEncounteredException(t1) + } + val r2 = rec(t2) + val r3 = rec(t3) + val lub = leastUpperBound(r2.getType, r3.getType) + lub match { + case Some(lub) => IfExpr(r1, r2, r3).setType(lub) + case None => + unit.error(nextExpr.pos, "Both branches of ifthenelse have incompatible types") + throw ImpureCodeEncounteredException(t1) + } } - val selDef: CaseClassDef = selType.asInstanceOf[CaseClassType].classDef + case ExIsInstanceOf(tt, cc) => { + val ccRec = rec(cc) + val checkType = scalaType2PureScala(unit, silent)(tt.tpe) + checkType match { + case CaseClassType(ccd) => { + val rootType: ClassTypeDef = if(ccd.parent != None) ccd.parent.get else ccd + if(!ccRec.getType.isInstanceOf[ClassType]) { + unit.error(tr.pos, "isInstanceOf can only be used with a case class") + throw ImpureCodeEncounteredException(tr) + } else { + val testedExprType = ccRec.getType.asInstanceOf[ClassType].classDef + val testedExprRootType: ClassTypeDef = if(testedExprType.parent != None) testedExprType.parent.get else testedExprType + + if(rootType != testedExprRootType) { + unit.error(tr.pos, "isInstanceOf can only be used with compatible case classes") + throw ImpureCodeEncounteredException(tr) + } else { + CaseClassInstanceOf(ccd, ccRec) + } + } + } + case _ => { + unit.error(tr.pos, "isInstanceOf can only be used with a case class") + throw ImpureCodeEncounteredException(tr) + } + } + } - val fieldID = selDef.fields.find(_.id.name == n.toString) match { - case None => { + case lc @ ExLocalCall(sy,nm,ar) => { + if(defsToDefs.keysIterator.find(_ == sy).isEmpty) { if(!silent) - unit.error(tr.pos, "Invalid method or field invocation (not a case class arg?)") + unit.error(tr.pos, "Invoking an invalid function.") throw ImpureCodeEncounteredException(tr) } - case Some(vd) => vd.id + val fd = defsToDefs(sy) + FunctionInvocation(fd, ar.map(rec(_))).setType(fd.returnType).setPosInfo(lc.pos.line,lc.pos.column) + } + case pm @ ExPatternMatching(sel, cses) => { + val rs = rec(sel) + val rc = cses.map(rewriteCaseDef(_)) + val rt: purescala.TypeTrees.TypeTree = rc.map(_.rhs.getType).reduceLeft(leastUpperBound(_,_).get) + MatchExpr(rs, rc).setType(rt).setPosInfo(pm.pos.line,pm.pos.column) } - CaseClassSelector(selDef, selector, fieldID).setType(fieldID.getType) - } - - // default behaviour is to complain :) - case _ => { - if(!silent) { - println(tr) - reporter.info(tr.pos, "Could not extract as PureScala.", true) + + // default behaviour is to complain :) + case _ => { + if(!silent) { + println(tr) + reporter.info(tr.pos, "Could not extract as PureScala.", true) + } + throw ImpureCodeEncounteredException(tree) } - throw ImpureCodeEncounteredException(tree) } } - if(handleRest) { + val res = if(handleRest) { rest match { case Some(rst) => { val recRst = rec(rst) - PBlock(Seq(psExpr), recRst).setType(recRst.getType) + val block = PBlock(Seq(psExpr), recRst).setType(recRst.getType) + block } case None => psExpr } } else { psExpr } + + res } rec(tree) } @@ -897,6 +1032,7 @@ trait CodeExtraction extends Extractors { def rec(tr: Type): purescala.TypeTrees.TypeTree = tr match { case tpe if tpe == IntClass.tpe => Int32Type case tpe if tpe == BooleanClass.tpe => BooleanType + case tpe if tpe == UnitClass.tpe => UnitType case TypeRef(_, sym, btt :: Nil) if isSetTraitSym(sym) => SetType(rec(btt)) case TypeRef(_, sym, btt :: Nil) if isMultisetTraitSym(sym) => MultisetType(rec(btt)) case TypeRef(_, sym, btt :: Nil) if isOptionClassSym(sym) => OptionType(rec(btt)) @@ -906,6 +1042,7 @@ trait CodeExtraction extends Extractors { case TypeRef(_, sym, List(t1,t2,t3,t4)) if isTuple4(sym) => TupleType(Seq(rec(t1),rec(t2),rec(t3),rec(t4))) case TypeRef(_, sym, List(t1,t2,t3,t4,t5)) if isTuple5(sym) => TupleType(Seq(rec(t1),rec(t2),rec(t3),rec(t4),rec(t5))) case TypeRef(_, sym, ftt :: ttt :: Nil) if isFunction1TraitSym(sym) => FunctionType(List(rec(ftt)), rec(ttt)) + case TypeRef(_, sym, btt :: Nil) if isArrayClassSym(sym) => ArrayType(rec(btt)) case TypeRef(_, sym, Nil) if classesToClasses.keySet.contains(sym) => classDefToClassType(classesToClasses(sym)) case _ => { if(!silent) { @@ -928,4 +1065,31 @@ trait CodeExtraction extends Extractors { def mkPosString(pos: scala.tools.nsc.util.Position) : String = { pos.line + "," + pos.column } + + def getReturnedExpr(expr: Expr): Seq[Expr] = expr match { + case Let(_, _, rest) => getReturnedExpr(rest) + case LetVar(_, _, rest) => getReturnedExpr(rest) + case PBlock(_, rest) => getReturnedExpr(rest) + case IfExpr(_, then, elze) => getReturnedExpr(then) ++ getReturnedExpr(elze) + case MatchExpr(_, cses) => cses.flatMap{ + case SimpleCase(_, rhs) => getReturnedExpr(rhs) + case GuardedCase(_, _, rhs) => getReturnedExpr(rhs) + } + case _ => Seq(expr) + } + + def getOwner(exprs: Seq[Expr]): Option[Option[FunDef]] = { + val exprOwners: Seq[Option[Option[FunDef]]] = exprs.map(owners.get(_)) + if(exprOwners.exists(_ == None)) + None + else if(exprOwners.exists(_ == Some(None))) + Some(None) + else if(exprOwners.exists(o1 => exprOwners.exists(o2 => o1 != o2))) + Some(None) + else + exprOwners(0) + } + + def getOwner(expr: Expr): Option[Option[FunDef]] = getOwner(getReturnedExpr(expr)) + } diff --git a/src/main/scala/leon/plugin/Extractors.scala b/src/main/scala/leon/plugin/Extractors.scala index 04cece3d96670c4723fc09e13f0deaf2bf62dd09..eae46482780a0362946b5ddacce1eae632ffc1e9 100644 --- a/src/main/scala/leon/plugin/Extractors.scala +++ b/src/main/scala/leon/plugin/Extractors.scala @@ -174,6 +174,19 @@ trait Extractors { case _ => None } } + object ExWaypointExpression { + def unapply(tree: Apply) : Option[(Type, Tree, Tree)] = tree match { + case Apply( + TypeApply(Select(Select(funcheckIdent, utilsName), waypoint), typeTree :: Nil), + List(i, expr)) => { + if (utilsName.toString == "Utils" && waypoint.toString == "waypoint") + Some((typeTree.tpe, i, expr)) + else + None + } + case _ => None + } + } object ExValDef { @@ -632,10 +645,13 @@ trait Extractors { } } - object ExMapUpdated { + object ExUpdated { def unapply(tree: Apply): Option[(Tree,Tree,Tree)] = tree match { case Apply(TypeApply(Select(lhs, n), typeTreeList), List(from, to)) if (n.toString == "updated") => Some((lhs, from, to)) + case Apply( + Apply(TypeApply(Select(Apply(_, Seq(lhs)), n), _), Seq(index, value)), + List(Apply(_, _))) if (n.toString == "updated") => Some((lhs, index, value)) case _ => None } } @@ -646,6 +662,14 @@ trait Extractors { case _ => None } } + object ExUpdate { + def unapply(tree: Apply): Option[(Tree, Tree, Tree)] = tree match { + case Apply( + Select(lhs, update), + index :: newValue :: Nil) if(update.toString == "update") => Some((lhs, index, newValue)) + case _ => None + } + } object ExMapIsDefinedAt { def unapply(tree: Apply): Option[(Tree,Tree)] = tree match { @@ -653,5 +677,42 @@ trait Extractors { case _ => None } } + + object ExArrayLength { + def unapply(tree: Select): Option[Tree] = tree match { + case Select(t, n) if (n.toString == "length") => Some(t) + case _ => None + } + } + + object ExArrayClone { + def unapply(tree: Apply): Option[Tree] = tree match { + case Apply(Select(t, n), List()) if (n.toString == "clone") => Some(t) + case _ => None + } + } + + + object ExArrayFill { + def unapply(tree: Apply): Option[(Tree, Tree, Tree)] = tree match { + case Apply( + Apply( + Apply( + TypeApply( + Select(Select(Ident(scala), arrayObject), fillMethod), + baseType :: Nil + ), + length :: Nil + ), + defaultValue :: Nil + ), + manifest + ) if(scala.toString == "scala" && + arrayObject.toString == "Array" && + fillMethod.toString == "fill") => Some((baseType, length, defaultValue)) + case _ => None + } + } + } } diff --git a/src/main/scala/leon/purescala/Definitions.scala b/src/main/scala/leon/purescala/Definitions.scala index f2da8a7cef1f1f3b0290a35a260f40ceefbf6705..3c18e858fcfeba8fa7d02421536e1080c5999847 100644 --- a/src/main/scala/leon/purescala/Definitions.scala +++ b/src/main/scala/leon/purescala/Definitions.scala @@ -53,6 +53,16 @@ object Definitions { def isCatamorphism(f1: FunDef) = mainObject.isCatamorphism(f1) def caseClassDef(name: String) = mainObject.caseClassDef(name) def allIdentifiers : Set[Identifier] = mainObject.allIdentifiers + id + def isPure: Boolean = definedFunctions.forall(fd => fd.body.forall(Trees.isPure) && fd.precondition.forall(Trees.isPure) && fd.postcondition.forall(Trees.isPure)) + + def writeScalaFile(filename: String) { + import java.io.FileWriter + import java.io.BufferedWriter + val fstream = new FileWriter(filename) + val out = new BufferedWriter(fstream) + out.write(ScalaPrinter(this)) + out.close + } } /** Objects work as containers for class definitions, functions (def's) and diff --git a/src/main/scala/leon/purescala/PrettyPrinter.scala b/src/main/scala/leon/purescala/PrettyPrinter.scala index d1a39f7172fafb612c2201b2c4322615a0aabdef..b7b5bd343c2adc3bf973440776951a124a0ddd08 100644 --- a/src/main/scala/leon/purescala/PrettyPrinter.scala +++ b/src/main/scala/leon/purescala/PrettyPrinter.scala @@ -139,8 +139,8 @@ object PrettyPrinter { sb.append("\n") } - case Tuple(exprs) => ppNary(sb, exprs, "(", ", ", ")", lvl) - case TupleSelect(t, i) => { + case t@Tuple(exprs) => ppNary(sb, exprs, "(", ", ", ")", lvl) + case s@TupleSelect(t, i) => { pp(t, sb, lvl) sb.append("._" + i) sb @@ -154,6 +154,12 @@ object PrettyPrinter { nsb } + case Waypoint(i, expr) => { + sb.append("waypoint_" + i + "(") + pp(expr, sb, lvl) + sb.append(")") + } + case OptionSome(a) => { var nsb = sb nsb.append("Some(") @@ -249,6 +255,50 @@ object PrettyPrinter { nsb = ppNary(nsb, Seq(k), "(", ",", ")", lvl) nsb } + case ArrayLength(a) => { + pp(a, sb, lvl) + sb.append(".length") + } + case ArrayClone(a) => { + pp(a, sb, lvl) + sb.append(".clone") + } + case fill@ArrayFill(size, v) => { + sb.append("Array.fill(") + pp(size, sb, lvl) + sb.append(")(") + pp(v, sb, lvl) + sb.append(")") + } + case am@ArrayMake(v) => { + sb.append("Array.make(") + pp(v, sb, lvl) + sb.append(")") + } + case sel@ArraySelect(ar, i) => { + pp(ar, sb, lvl) + sb.append("(") + pp(i, sb, lvl) + sb.append(")") + } + case up@ArrayUpdate(ar, i, v) => { + pp(ar, sb, lvl) + sb.append("(") + pp(i, sb, lvl) + sb.append(") = ") + pp(v, sb, lvl) + } + case up@ArrayUpdated(ar, i, v) => { + pp(ar, sb, lvl) + sb.append(".updated(") + pp(i, sb, lvl) + sb.append(", ") + pp(v, sb, lvl) + sb.append(")") + } + case FiniteArray(exprs) => { + ppNary(sb, exprs, "Array(", ", ", ")", lvl) + } case Distinct(exprs) => { var nsb = sb @@ -377,6 +427,7 @@ object PrettyPrinter { case UnitType => sb.append("Unit") case Int32Type => sb.append("Int") case BooleanType => sb.append("Boolean") + case ArrayType(bt) => pp(bt, sb.append("Array["), lvl).append("]") case SetType(bt) => pp(bt, sb.append("Set["), lvl).append("]") case MapType(ft,tt) => pp(tt, pp(ft, sb.append("Map["), lvl).append(","), lvl).append("]") case MultisetType(bt) => pp(bt, sb.append("Multiset["), lvl).append("]") diff --git a/src/main/scala/leon/purescala/ScalaPrinter.scala b/src/main/scala/leon/purescala/ScalaPrinter.scala new file mode 100644 index 0000000000000000000000000000000000000000..501096c45db7c82d88c441e53a6564065423944d --- /dev/null +++ b/src/main/scala/leon/purescala/ScalaPrinter.scala @@ -0,0 +1,563 @@ +package leon.purescala + +/** This pretty-printer only print valid scala syntax */ +object ScalaPrinter { + import Common._ + import Trees._ + import TypeTrees._ + import Definitions._ + + import java.lang.StringBuffer + + def apply(tree: Expr): String = { + val retSB = pp(tree, new StringBuffer, 0) + retSB.toString + } + + def apply(tpe: TypeTree): String = { + val retSB = pp(tpe, new StringBuffer, 0) + retSB.toString + } + + def apply(defn: Definition): String = { + val retSB = pp(defn, new StringBuffer, 0) + retSB.toString + } + + private def ind(sb: StringBuffer, lvl: Int) : StringBuffer = { + sb.append(" " * lvl) + sb + } + + // EXPRESSIONS + // all expressions are printed in-line + private def ppUnary(sb: StringBuffer, expr: Expr, op1: String, op2: String, lvl: Int): StringBuffer = { + var nsb: StringBuffer = sb + nsb.append(op1) + nsb = pp(expr, nsb, lvl) + nsb.append(op2) + nsb + } + + private def ppBinary(sb: StringBuffer, left: Expr, right: Expr, op: String, lvl: Int): StringBuffer = { + var nsb: StringBuffer = sb + nsb.append("(") + nsb = pp(left, nsb, lvl) + nsb.append(op) + nsb = pp(right, nsb, lvl) + nsb.append(")") + nsb + } + + private def ppNary(sb: StringBuffer, exprs: Seq[Expr], pre: String, op: String, post: String, lvl: Int): StringBuffer = { + var nsb = sb + nsb.append(pre) + val sz = exprs.size + var c = 0 + + exprs.foreach(ex => { + nsb = pp(ex, nsb, lvl) ; c += 1 ; if(c < sz) nsb.append(op) + }) + + nsb.append(post) + nsb + } + + private def pp(tree: Expr, sb: StringBuffer, lvl: Int): StringBuffer = tree match { + case Variable(id) => sb.append(id) + case DeBruijnIndex(idx) => sys.error("Not Valid Scala") + case Let(b,d,e) => { + sb.append("locally {\n") + ind(sb, lvl+1) + sb.append("val " + b + " = ") + pp(d, sb, lvl+1) + sb.append("\n") + ind(sb, lvl+1) + pp(e, sb, lvl+1) + sb.append("\n") + ind(sb, lvl) + sb.append("}\n") + ind(sb, lvl) + sb + } + case LetVar(b,d,e) => { + sb.append("locally {\n") + ind(sb, lvl+1) + sb.append("var " + b + " = ") + pp(d, sb, lvl+1) + sb.append("\n") + ind(sb, lvl+1) + pp(e, sb, lvl+1) + sb.append("\n") + ind(sb, lvl) + sb.append("}\n") + ind(sb, lvl) + sb + } + case LetDef(fd,e) => { + sb.append("\n") + pp(fd, sb, lvl+1) + sb.append("\n") + sb.append("\n") + ind(sb, lvl) + pp(e, sb, lvl) + sb + } + case And(exprs) => ppNary(sb, exprs, "(", " && ", ")", lvl) // \land + case Or(exprs) => ppNary(sb, exprs, "(", " || ", ")", lvl) // \lor + case Not(Equals(l, r)) => ppBinary(sb, l, r, " != ", lvl) // \neq + case Iff(l,r) => sys.error("Not Scala Code") + case Implies(l,r) => sys.error("Not Scala Code") + case UMinus(expr) => ppUnary(sb, expr, "-(", ")", lvl) + case Equals(l,r) => ppBinary(sb, l, r, " == ", lvl) + case IntLiteral(v) => sb.append(v) + case BooleanLiteral(v) => sb.append(v) + case StringLiteral(s) => sb.append("\"" + s + "\"") + case UnitLiteral => sb.append("()") + case Block(exprs, last) => { + sb.append("{\n") + (exprs :+ last).foreach(e => { + ind(sb, lvl+1) + pp(e, sb, lvl+1) + sb.append("\n") + }) + ind(sb, lvl) + sb.append("}\n") + sb + } + case Assignment(lhs, rhs) => ppBinary(sb, lhs.toVariable, rhs, " = ", lvl) + case wh@While(cond, body) => { + wh.invariant match { + case Some(inv) => { + sb.append("\n") + ind(sb, lvl) + sb.append("@invariant: ") + pp(inv, sb, lvl) + sb.append("\n") + ind(sb, lvl) + } + case None => + } + sb.append("while(") + pp(cond, sb, lvl) + sb.append(")\n") + ind(sb, lvl+1) + pp(body, sb, lvl+1) + sb.append("\n") + } + + case t@Tuple(exprs) => ppNary(sb, exprs, "(", ", ", ")", lvl) + case s@TupleSelect(t, i) => { + pp(t, sb, lvl) + sb.append("._" + i) + sb + } + + case e@Epsilon(pred) => sys.error("Not Scala Code") + case Waypoint(i, expr) => pp(expr, sb, lvl) + + case OptionSome(a) => { + var nsb = sb + nsb.append("Some(") + nsb = pp(a, nsb, lvl) + nsb.append(")") + nsb + } + + case OptionNone(_) => sb.append("None") + + case CaseClass(cd, args) => { + var nsb = sb + nsb.append(cd.id) + nsb = ppNary(nsb, args, "(", ", ", ")", lvl) + nsb + } + case CaseClassInstanceOf(cd, e) => { + var nsb = sb + nsb = pp(e, nsb, lvl) + nsb.append(".isInstanceOf[" + cd.id + "]") + nsb + } + case CaseClassSelector(_, cc, id) => pp(cc, sb, lvl).append("." + id) + case FunctionInvocation(fd, args) => { + var nsb = sb + nsb.append(fd.id) + nsb = ppNary(nsb, args, "(", ", ", ")", lvl) + nsb + } + case AnonymousFunction(es, ev) => { + var nsb = sb + nsb.append("{") + es.foreach { + case (as, res) => + nsb = ppNary(nsb, as, "", " ", "", lvl) + nsb.append(" -> ") + nsb = pp(res, nsb, lvl) + nsb.append(", ") + } + nsb.append("else -> ") + nsb = pp(ev, nsb, lvl) + nsb.append("}") + } + case AnonymousFunctionInvocation(id, args) => { + var nsb = sb + nsb.append(id) + nsb = ppNary(nsb, args, "(", ", ", ")", lvl) + nsb + } + case Plus(l,r) => ppBinary(sb, l, r, " + ", lvl) + case Minus(l,r) => ppBinary(sb, l, r, " - ", lvl) + case Times(l,r) => ppBinary(sb, l, r, " * ", lvl) + case Division(l,r) => ppBinary(sb, l, r, " / ", lvl) + case Modulo(l,r) => ppBinary(sb, l, r, " % ", lvl) + case LessThan(l,r) => ppBinary(sb, l, r, " < ", lvl) + case GreaterThan(l,r) => ppBinary(sb, l, r, " > ", lvl) + case LessEquals(l,r) => ppBinary(sb, l, r, " <= ", lvl) // \leq + case GreaterEquals(l,r) => ppBinary(sb, l, r, " >= ", lvl) // \geq + case FiniteSet(rs) => ppNary(sb, rs, "{", ", ", "}", lvl) + case FiniteMultiset(rs) => ppNary(sb, rs, "{|", ", ", "|}", lvl) + case EmptySet(bt) => sb.append("Set()") // Ø + case EmptyMultiset(_) => sys.error("Not Valid Scala") + case Not(ElementOfSet(s,e)) => sys.error("TODO") + //case ElementOfSet(s,e) => ppBinary(sb, s, e, " \u2208 ", lvl) // \in + //case SubsetOf(l,r) => ppBinary(sb, l, r, " \u2286 ", lvl) // \subseteq + //case Not(SubsetOf(l,r)) => ppBinary(sb, l, r, " \u2288 ", lvl) // \notsubseteq + case SetMin(s) => pp(s, sb, lvl).append(".min") + case SetMax(s) => pp(s, sb, lvl).append(".max") + // case SetUnion(l,r) => ppBinary(sb, l, r, " \u222A ", lvl) // \cup + // case MultisetUnion(l,r) => ppBinary(sb, l, r, " \u222A ", lvl) // \cup + // case MapUnion(l,r) => ppBinary(sb, l, r, " \u222A ", lvl) // \cup + // case SetDifference(l,r) => ppBinary(sb, l, r, " \\ ", lvl) + // case MultisetDifference(l,r) => ppBinary(sb, l, r, " \\ ", lvl) + // case SetIntersection(l,r) => ppBinary(sb, l, r, " \u2229 ", lvl) // \cap + // case MultisetIntersection(l,r) => ppBinary(sb, l, r, " \u2229 ", lvl) // \cap + // case SetCardinality(t) => ppUnary(sb, t, "|", "|", lvl) + // case MultisetCardinality(t) => ppUnary(sb, t, "|", "|", lvl) + // case MultisetPlus(l,r) => ppBinary(sb, l, r, " \u228E ", lvl) // U+ + // case MultisetToSet(e) => pp(e, sb, lvl).append(".toSet") + case EmptyMap(_,_) => sb.append("Map()") + case SingletonMap(f,t) => ppBinary(sb, f, t, " -> ", lvl) + case FiniteMap(rs) => ppNary(sb, rs, "Map(", ", ", ")", lvl) + case MapGet(m,k) => { + var nsb = sb + pp(m, nsb, lvl) + nsb = ppNary(nsb, Seq(k), "(", ",", ")", lvl) + nsb + } + case MapIsDefinedAt(m,k) => { + var nsb = sb + pp(m, nsb, lvl) + nsb.append(".isDefinedAt") + nsb = ppNary(nsb, Seq(k), "(", ",", ")", lvl) + nsb + } + case ArrayLength(a) => { + pp(a, sb, lvl) + sb.append(".length") + } + case ArrayClone(a) => { + pp(a, sb, lvl) + sb.append(".clone") + } + case fill@ArrayFill(size, v) => { + sb.append("Array.fill(") + pp(size, sb, lvl) + sb.append(")(") + pp(v, sb, lvl) + sb.append(")") + } + case am@ArrayMake(v) => sys.error("Not Scala Code") + case sel@ArraySelect(ar, i) => { + pp(ar, sb, lvl) + sb.append("(") + pp(i, sb, lvl) + sb.append(")") + } + case up@ArrayUpdate(ar, i, v) => { + pp(ar, sb, lvl) + sb.append("(") + pp(i, sb, lvl) + sb.append(") = ") + pp(v, sb, lvl) + } + case up@ArrayUpdated(ar, i, v) => { + pp(ar, sb, lvl) + sb.append(".updated(") + pp(i, sb, lvl) + sb.append(", ") + pp(v, sb, lvl) + sb.append(")") + } + case FiniteArray(exprs) => { + ppNary(sb, exprs, "Array(", ", ", ")", lvl) + } + + case Distinct(exprs) => { + var nsb = sb + nsb.append("distinct") + nsb = ppNary(nsb, exprs, "(", ", ", ")", lvl) + nsb + } + + case IfExpr(c, t, e) => { + var nsb = sb + nsb.append("(if (") + nsb = pp(c, nsb, lvl) + nsb.append(")\n") + ind(nsb, lvl+1) + nsb = pp(t, nsb, lvl+1) + nsb.append("\n") + ind(nsb, lvl) + nsb.append("else\n") + ind(nsb, lvl+1) + nsb = pp(e, nsb, lvl+1) + nsb.append(")") + nsb + } + + case mex @ MatchExpr(s, csc) => { + def ppc(sb: StringBuffer, p: Pattern): StringBuffer = p match { + //case InstanceOfPattern(None, ctd) => + //case InstanceOfPattern(Some(id), ctd) => + case CaseClassPattern(bndr, ccd, subps) => { + var nsb = sb + bndr.foreach(b => nsb.append(b + " @ ")) + nsb.append(ccd.id).append("(") + var c = 0 + val sz = subps.size + subps.foreach(sp => { + nsb = ppc(nsb, sp) + if(c < sz - 1) + nsb.append(", ") + c = c + 1 + }) + nsb.append(")") + } + case WildcardPattern(None) => sb.append("_") + case WildcardPattern(Some(id)) => sb.append(id) + case TuplePattern(bndr, subPatterns) => { + bndr.foreach(b => sb.append(b + " @ ")) + sb.append("(") + subPatterns.init.foreach(p => { + ppc(sb, p) + sb.append(", ") + }) + ppc(sb, subPatterns.last) + sb.append(")") + } + case _ => sb.append("Pattern?") + } + + var nsb = sb + nsb.append("(") + nsb == pp(s, nsb, lvl) + // if(mex.posInfo != "") { + // nsb.append(" match@(" + mex.posInfo + ") {\n") + // } else { + nsb.append(" match {\n") + // } + + csc.foreach(cs => { + ind(nsb, lvl+1) + nsb.append("case ") + nsb = ppc(nsb, cs.pattern) + cs.theGuard.foreach(g => { + nsb.append(" if ") + nsb = pp(g, nsb, lvl+1) + }) + nsb.append(" => ") + nsb = pp(cs.rhs, nsb, lvl+1) + nsb.append("\n") + }) + ind(nsb, lvl).append("}") + nsb.append(")") + nsb + } + + case ResultVariable() => sb.append("res") + case EpsilonVariable((row, col)) => sb.append("x" + row + "_" + col) + case Not(expr) => ppUnary(sb, expr, "\u00AC(", ")", lvl) // \neg + + case e @ Error(desc) => { + var nsb = sb + nsb.append("error(\"" + desc + "\")[") + nsb = pp(e.getType, nsb, lvl) + nsb.append("]") + nsb + } + + case _ => sb.append("Expr?") + } + + // TYPE TREES + // all type trees are printed in-line + private def ppNaryType(sb: StringBuffer, tpes: Seq[TypeTree], pre: String, op: String, post: String, lvl: Int): StringBuffer = { + var nsb = sb + nsb.append(pre) + val sz = tpes.size + var c = 0 + + tpes.foreach(t => { + nsb = pp(t, nsb, lvl) ; c += 1 ; if(c < sz) nsb.append(op) + }) + + nsb.append(post) + nsb + } + + private def pp(tpe: TypeTree, sb: StringBuffer, lvl: Int): StringBuffer = tpe match { + case Untyped => sb.append("???") + case UnitType => sb.append("Unit") + case Int32Type => sb.append("Int") + case BooleanType => sb.append("Boolean") + case ArrayType(bt) => pp(bt, sb.append("Array["), lvl).append("]") + case SetType(bt) => pp(bt, sb.append("Set["), lvl).append("]") + case MapType(ft,tt) => pp(tt, pp(ft, sb.append("Map["), lvl).append(","), lvl).append("]") + case MultisetType(bt) => pp(bt, sb.append("Multiset["), lvl).append("]") + case OptionType(bt) => pp(bt, sb.append("Option["), lvl).append("]") + case FunctionType(fts, tt) => { + var nsb = sb + if (fts.size > 1) + nsb = ppNaryType(nsb, fts, "(", ", ", ")", lvl) + else if (fts.size == 1) + nsb = pp(fts.head, nsb, lvl) + nsb.append(" => ") + pp(tt, nsb, lvl) + } + case TupleType(tpes) => ppNaryType(sb, tpes, "(", ", ", ")", lvl) + case c: ClassType => sb.append(c.classDef.id) + case _ => sb.append("Type?") + } + + // DEFINITIONS + // all definitions are printed with an end-of-line + private def pp(defn: Definition, sb: StringBuffer, lvl: Int): StringBuffer = { + + defn match { + case Program(id, mainObj) => { + assert(lvl == 0) + pp(mainObj, sb, lvl) + } + + case ObjectDef(id, defs, invs) => { + var nsb = sb + ind(nsb, lvl) + nsb.append("object ") + nsb.append(id) + nsb.append(" {\n") + + var c = 0 + val sz = defs.size + + defs.foreach(df => { + nsb = pp(df, nsb, lvl+1) + if(c < sz - 1) { + nsb.append("\n\n") + } + c = c + 1 + }) + + nsb.append("\n") + ind(nsb, lvl).append("}\n") + } + + case AbstractClassDef(id, parent) => { + var nsb = sb + ind(nsb, lvl) + nsb.append("sealed abstract class ") + nsb.append(id) + parent.foreach(p => nsb.append(" extends " + p.id)) + nsb + } + + case CaseClassDef(id, parent, varDecls) => { + var nsb = sb + ind(nsb, lvl) + nsb.append("case class ") + nsb.append(id) + nsb.append("(") + var c = 0 + val sz = varDecls.size + + varDecls.foreach(vd => { + nsb.append(vd.id) + nsb.append(": ") + nsb = pp(vd.tpe, nsb, lvl) + if(c < sz - 1) { + nsb.append(", ") + } + c = c + 1 + }) + nsb.append(")") + parent.foreach(p => nsb.append(" extends " + p.id)) + nsb + } + + case fd @ FunDef(id, rt, args, body, pre, post) => { + var nsb = sb + + //for(a <- fd.annotations) { + // ind(nsb, lvl) + // nsb.append("@" + a + "\n") + //} + + ind(nsb, lvl) + nsb.append("def ") + nsb.append(id) + nsb.append("(") + + val sz = args.size + var c = 0 + + args.foreach(arg => { + nsb.append(arg.id) + nsb.append(" : ") + nsb = pp(arg.tpe, nsb, lvl) + + if(c < sz - 1) { + nsb.append(", ") + } + c = c + 1 + }) + + nsb.append(") : ") + nsb = pp(rt, nsb, lvl) + nsb.append(" = (") + if(body.isDefined) { + pre match { + case None => pp(body.get, nsb, lvl) + case Some(prec) => { + nsb.append("{\n") + ind(nsb, lvl+1) + nsb.append("require(") + nsb = pp(prec, nsb, lvl+1) + nsb.append(")\n") + pp(body.get, nsb, lvl+1) + nsb.append("\n") + ind(nsb, lvl) + nsb.append("}") + } + } + } else + nsb.append("[unknown function implementation]") + + post match { + case None => { + nsb.append(")") + } + case Some(postc) => { + nsb.append(" ensuring(res => ") //TODO, not very general solution... + nsb = pp(postc, nsb, lvl) + nsb.append("))") + } + } + + nsb + } + + case _ => sb.append("Defn?") + } + } +} + +// vim: set ts=4 sw=4 et: diff --git a/src/main/scala/leon/purescala/Trees.scala b/src/main/scala/leon/purescala/Trees.scala index 638db0c2024152c46b9f735263b11e9ce01b5ca2..bfad43aae71ffb22c33c09cf8667d7920acca9ab 100644 --- a/src/main/scala/leon/purescala/Trees.scala +++ b/src/main/scala/leon/purescala/Trees.scala @@ -76,9 +76,17 @@ object Trees { } case class IfExpr(cond: Expr, then: Expr, elze: Expr) extends Expr - case class Tuple(exprs: Seq[Expr]) extends Expr + case class Tuple(exprs: Seq[Expr]) extends Expr { + val subTpes = exprs.map(_.getType).map(bestRealType) + if(!subTpes.exists(_ == Untyped)) { + setType(TupleType(subTpes)) + } + + } case class TupleSelect(tuple: Expr, index: Int) extends Expr + case class Waypoint(i: Int, expr: Expr) extends Expr + object MatchExpr { def apply(scrutinee: Expr, cases: Seq[MatchCase]) : MatchExpr = { scrutinee.getType match { @@ -148,6 +156,7 @@ object Trees { case class TuplePattern(binder: Option[Identifier], subPatterns: Seq[Pattern]) extends Pattern + /* Propositional logic */ object And { def apply(l: Expr, r: Expr) : Expr = (l,r) match { @@ -379,6 +388,25 @@ object Trees { val fixedType = BooleanType } + /* Array operations */ + case class ArrayFill(length: Expr, defaultValue: Expr) extends Expr + case class ArrayMake(defaultValue: Expr) extends Expr + case class ArraySelect(array: Expr, index: Expr) extends Expr with ScalacPositional + //the difference between ArrayUpdate and ArrayUpdated is that the former has a side effect while the latter is the function variant + //ArrayUpdate should be eliminated soon in the analysis while ArrayUpdated is keep all the way to the backend + case class ArrayUpdate(array: Expr, index: Expr, newValue: Expr) extends Expr with ScalacPositional with FixedType { + val fixedType = UnitType + } + case class ArrayUpdated(array: Expr, index: Expr, newValue: Expr) extends Expr with ScalacPositional + case class ArrayLength(array: Expr) extends Expr with FixedType { + val fixedType = Int32Type + } + case class FiniteArray(exprs: Seq[Expr]) extends Expr + case class ArrayClone(array: Expr) extends Expr { + if(array.getType != Untyped) + setType(array.getType) + } + /* List operations */ case class NilList(baseType: TypeTree) extends Expr with Terminal case class Cons(head: Expr, tail: Expr) extends Expr @@ -411,6 +439,10 @@ object Trees { case CaseClassInstanceOf(cd, e) => Some((e, CaseClassInstanceOf(cd, _))) case Assignment(id, e) => Some((e, Assignment(id, _))) case TupleSelect(t, i) => Some((t, TupleSelect(_, i))) + case ArrayLength(a) => Some((a, ArrayLength)) + case ArrayClone(a) => Some((a, ArrayClone)) + case ArrayMake(t) => Some((t, ArrayMake)) + case Waypoint(i, t) => Some((t, (expr: Expr) => Waypoint(i, expr))) case e@Epsilon(t) => Some((t, (expr: Expr) => Epsilon(expr).setType(e.getType).setPosInfo(e))) case _ => None } @@ -446,6 +478,8 @@ object Trees { case MapUnion(t1,t2) => Some((t1,t2,MapUnion)) case MapDifference(t1,t2) => Some((t1,t2,MapDifference)) case MapIsDefinedAt(t1,t2) => Some((t1,t2, MapIsDefinedAt)) + case ArrayFill(t1, t2) => Some((t1, t2, ArrayFill)) + case ArraySelect(t1, t2) => Some((t1, t2, ArraySelect)) case Concat(t1,t2) => Some((t1,t2,Concat)) case ListAt(t1,t2) => Some((t1,t2,ListAt)) case wh@While(t1, t2) => Some((t1,t2, (t1, t2) => While(t1, t2).setInvariant(wh.invariant).setPosInfo(wh))) @@ -463,6 +497,9 @@ object Trees { case FiniteSet(args) => Some((args, FiniteSet)) case FiniteMap(args) => Some((args, (as : Seq[Expr]) => FiniteMap(as.asInstanceOf[Seq[SingletonMap]]))) case FiniteMultiset(args) => Some((args, FiniteMultiset)) + case ArrayUpdate(t1, t2, t3) => Some((Seq(t1,t2,t3), (as: Seq[Expr]) => ArrayUpdate(as(0), as(1), as(2)))) + case ArrayUpdated(t1, t2, t3) => Some((Seq(t1,t2,t3), (as: Seq[Expr]) => ArrayUpdated(as(0), as(1), as(2)))) + case FiniteArray(args) => Some((args, FiniteArray)) case Distinct(args) => Some((args, Distinct)) case Block(args, rest) => Some((args :+ rest, exprs => Block(exprs.init, exprs.last))) case Tuple(args) => Some((args, Tuple)) @@ -598,7 +635,7 @@ object Trees { case Some(newEx) => { somethingChanged = true if(newEx.getType == Untyped) { - Settings.reporter.warning("REPLACING WITH AN UNTYPED EXPRESSION !") + Settings.reporter.warning("REPLACING [" + ex + "] WITH AN UNTYPED EXPRESSION !") Settings.reporter.warning("Here's the new expression: " + newEx) } newEx @@ -798,13 +835,19 @@ object Trees { searchAndReplaceDFS(applyToTree)(expr) } - //checking whether the expr is pure, that is do not contains any non-pure construct: assign, while and blocks + //checking whether the expr is pure, that is do not contains any non-pure construct: assign, while, blocks, array, ... + //this is expected to be true when entering the "backend" of Leon def isPure(expr: Expr): Boolean = { def convert(t: Expr) : Boolean = t match { case Block(_, _) => false case Assignment(_, _) => false case While(_, _) => false case LetVar(_, _, _) => false + case LetDef(_, _) => false + case ArrayUpdate(_, _, _) => false + case ArrayMake(_) => false + case ArrayClone(_) => false + case Epsilon(_) => false case _ => true } def combine(b1: Boolean, b2: Boolean) = b1 && b2 @@ -813,6 +856,11 @@ object Trees { case Assignment(_, _) => false case While(_, _) => false case LetVar(_, _, _) => false + case LetDef(_, _) => false + case ArrayUpdate(_, _, _) => false + case ArrayMake(_) => false + case ArrayClone(_) => false + case Epsilon(_) => false case _ => b } treeCatamorphism(convert, combine, compute, expr) @@ -842,6 +890,18 @@ object Trees { } treeCatamorphism(convert, combine, compute, expr) } + def containsIfExpr(expr: Expr): Boolean = { + def convert(t : Expr) : Boolean = t match { + case (i: IfExpr) => true + case _ => false + } + def combine(c1 : Boolean, c2 : Boolean) : Boolean = c1 || c2 + def compute(t : Expr, c : Boolean) = t match { + case (i: IfExpr) => true + case _ => c + } + treeCatamorphism(convert, combine, compute, expr) + } def variablesOf(expr: Expr) : Set[Identifier] = { def convert(t: Expr) : Set[Identifier] = t match { @@ -1229,6 +1289,24 @@ object Trees { toRet } + def conditionForPattern(in: Expr, pattern: Pattern) : Expr = pattern match { + case WildcardPattern(_) => BooleanLiteral(true) + case InstanceOfPattern(_,_) => scala.sys.error("InstanceOfPattern not yet supported.") + case CaseClassPattern(_, ccd, subps) => { + assert(ccd.fields.size == subps.size) + val pairs = ccd.fields.map(_.id).toList zip subps.toList + val subTests = pairs.map(p => conditionForPattern(CaseClassSelector(ccd, in, p._1), p._2)) + val together = And(subTests) + And(CaseClassInstanceOf(ccd, in), together) + } + case TuplePattern(_, subps) => { + val TupleType(tpes) = in.getType + assert(tpes.size == subps.size) + val subTests = subps.zipWithIndex.map{case (p, i) => conditionForPattern(TupleSelect(in, i+1).setType(tpes(i)), p)} + And(subTests) + } + } + private def convertMatchToIfThenElse(expr: Expr) : Expr = { def mapForPattern(in: Expr, pattern: Pattern) : Map[Identifier,Expr] = pattern match { case WildcardPattern(None) => Map.empty @@ -1258,24 +1336,6 @@ object Trees { } } - def conditionForPattern(in: Expr, pattern: Pattern) : Expr = pattern match { - case WildcardPattern(_) => BooleanLiteral(true) - case InstanceOfPattern(_,_) => scala.sys.error("InstanceOfPattern not yet supported.") - case CaseClassPattern(_, ccd, subps) => { - assert(ccd.fields.size == subps.size) - val pairs = ccd.fields.map(_.id).toList zip subps.toList - val subTests = pairs.map(p => conditionForPattern(CaseClassSelector(ccd, in, p._1), p._2)) - val together = And(subTests) - And(CaseClassInstanceOf(ccd, in), together) - } - case TuplePattern(_, subps) => { - val TupleType(tpes) = in.getType - assert(tpes.size == subps.size) - val subTests = subps.zipWithIndex.map{case (p, i) => conditionForPattern(TupleSelect(in, i+1).setType(tpes(i)), p)} - And(subTests) - } - } - def rewritePM(e: Expr) : Option[Expr] = e match { case m @ MatchExpr(scrut, cases) => { // println("Rewriting the following PM: " + e) @@ -1406,4 +1466,34 @@ object Trees { case FunctionType(fromTypes, toType) => AnonymousFunction(Seq.empty, simplestValue(toType)).setType(tpe) case _ => throw new Exception("I can't choose simplest value for type " + tpe) } + + //guarentee that all IfExpr will be at the top level and as soon as you encounter a non-IfExpr, then no more IfExpr can be find in the sub-expressions + //require no-match, no-ets and only pure code + def hoistIte(expr: Expr): Expr = { + def transform(expr: Expr): Option[Expr] = expr match { + case uop@UnaryOperator(IfExpr(c, t, e), op) => Some(IfExpr(c, op(t).setType(uop.getType), op(e).setType(uop.getType)).setType(uop.getType)) + case bop@BinaryOperator(IfExpr(c, t, e), t2, op) => Some(IfExpr(c, op(t, t2).setType(bop.getType), op(e, t2).setType(bop.getType)).setType(bop.getType)) + case bop@BinaryOperator(t1, IfExpr(c, t, e), op) => Some(IfExpr(c, op(t1, t).setType(bop.getType), op(t1, e).setType(bop.getType)).setType(bop.getType)) + case nop@NAryOperator(ts, op) => { + val iteIndex = ts.indexWhere{ case IfExpr(_, _, _) => true case _ => false } + if(iteIndex == -1) None else { + val (beforeIte, startIte) = ts.splitAt(iteIndex) + val afterIte = startIte.tail + val IfExpr(c, t, e) = startIte.head + Some(IfExpr(c, + op(beforeIte ++ Seq(t) ++ afterIte).setType(nop.getType), + op(beforeIte ++ Seq(e) ++ afterIte).setType(nop.getType) + ).setType(nop.getType)) + } + } + case _ => None + } + + def fix[A](f: (A) => A, a: A): A = { + val na = f(a) + if(a == na) a else fix(f, na) + } + fix(searchAndReplaceDFS(transform), expr) + } + } diff --git a/src/main/scala/leon/purescala/TypeTrees.scala b/src/main/scala/leon/purescala/TypeTrees.scala index 31947bf28177682e1376f0fd013944ef9dc37c05..01aea89f986c7263cb312763864006714581057c 100644 --- a/src/main/scala/leon/purescala/TypeTrees.scala +++ b/src/main/scala/leon/purescala/TypeTrees.scala @@ -18,7 +18,7 @@ object TypeTrees { def setType(tt: TypeTree): self.type = _type match { case None => _type = Some(tt); this - case Some(o) if o != tt => scala.sys.error("Resetting type information.") + case Some(o) if o != tt => scala.sys.error("Resetting type information! Type [" + o + "] is modified to [" + tt) case _ => this } } @@ -47,7 +47,7 @@ object TypeTrees { case other => other } - def leastUpperBound(t1: TypeTree, t2: TypeTree): TypeTree = (t1,t2) match { + def leastUpperBound(t1: TypeTree, t2: TypeTree): Option[TypeTree] = (t1,t2) match { case (c1: ClassType, c2: ClassType) => { import scala.collection.immutable.Set var c: ClassTypeDef = c1.classDef @@ -72,19 +72,19 @@ object TypeTrees { } if(found.isEmpty) { - scala.sys.error("Asking for lub of unrelated class types : " + t1 + " and " + t2) + None } else { - classDefToClassType(found.get) + Some(classDefToClassType(found.get)) } } - case (o1, o2) if (o1 == o2) => o1 - case (o1,BottomType) => o1 - case (BottomType,o2) => o2 - case (o1,AnyType) => AnyType - case (AnyType,o2) => AnyType + case (o1, o2) if (o1 == o2) => Some(o1) + case (o1,BottomType) => Some(o1) + case (BottomType,o2) => Some(o2) + case (o1,AnyType) => Some(AnyType) + case (AnyType,o2) => Some(AnyType) - case _ => scala.sys.error("Asking for lub of unrelated types: " + t1 + " and " + t2) + case _ => None } // returns the number of distinct values that inhabit a type @@ -100,6 +100,7 @@ object TypeTrees { case UnitType => FiniteSize(1) case Int32Type => InfiniteSize case ListType(_) => InfiniteSize + case ArrayType(_) => InfiniteSize case TupleType(bases) => { val baseSizes = bases.map(domainSize(_)) baseSizes.find(_ == InfiniteSize) match { @@ -148,6 +149,7 @@ object TypeTrees { case class MapType(from: TypeTree, to: TypeTree) extends TypeTree case class OptionType(base: TypeTree) extends TypeTree case class FunctionType(from: List[TypeTree], to: TypeTree) extends TypeTree + case class ArrayType(base: TypeTree) extends TypeTree sealed abstract class ClassType extends TypeTree { val classDef: ClassTypeDef diff --git a/src/main/scala/leon/testgen/CallGraph.scala b/src/main/scala/leon/testgen/CallGraph.scala new file mode 100644 index 0000000000000000000000000000000000000000..16e31057390e2cea0fd1714d7631c819593ecc04 --- /dev/null +++ b/src/main/scala/leon/testgen/CallGraph.scala @@ -0,0 +1,388 @@ +package leon.testgen + +import leon.purescala.Definitions._ +import leon.purescala.Trees._ +import leon.purescala.TypeTrees._ +import leon.purescala.Common._ +import leon.FairZ3Solver + +class CallGraph(val program: Program) { + + sealed abstract class ProgramPoint + case class FunctionStart(fd: FunDef) extends ProgramPoint + case class ExpressionPoint(wp: Expr, id: Int) extends ProgramPoint + private var epid = -1 + private def freshExpressionPoint(wp: Expr) = {epid += 1; ExpressionPoint(wp, epid)} + + case class TransitionLabel(cond: Expr, assignment: Map[Variable, Expr]) + + private lazy val graph: Map[ProgramPoint, Set[(ProgramPoint, TransitionLabel)]] = buildGraph + private lazy val programPoints: Set[ProgramPoint] = { + graph.flatMap(pair => pair._2.map(edge => edge._1).toSet + pair._1).toSet + } + + private def buildGraph: Map[ProgramPoint, Set[(ProgramPoint, TransitionLabel)]] = { + var callGraph: Map[ProgramPoint, Set[(ProgramPoint, TransitionLabel)]] = Map() + + program.definedFunctions.foreach(fd => { + val body = fd.body.get + //val cleanBody = hoistIte(expandLets(matchToIfThenElse(body))) + val cleanBody = expandLets(matchToIfThenElse(body)) + val subgraph = collectWithPathCondition(cleanBody, FunctionStart(fd)) + callGraph ++= subgraph + }) + + callGraph = addFunctionInvocationsEdges(callGraph) + + callGraph = simplifyGraph(callGraph) + + callGraph + } + + private def simplifyGraph(graph: Map[ProgramPoint, Set[(ProgramPoint, TransitionLabel)]]): Map[ProgramPoint, Set[(ProgramPoint, TransitionLabel)]] = { + def fix[A](f: (A) => A, a: A): A = { + val na = f(a) + if(a == na) a else fix(f, na) + } + fix(compressGraph, graph) + } + + //does a one level compression of the graph + private def compressGraph(graph: Map[ProgramPoint, Set[(ProgramPoint, TransitionLabel)]]): Map[ProgramPoint, Set[(ProgramPoint, TransitionLabel)]] = { + var newGraph = graph + + graph.find{ + case (point, edges) => { + edges.exists{ + case edge@(p2@ExpressionPoint(e, _), TransitionLabel(BooleanLiteral(true), assign)) if assign.isEmpty && !e.isInstanceOf[Waypoint] => { + val edgesOfPoint: Set[(ProgramPoint, TransitionLabel)] = graph.get(p2).getOrElse(Set()) //should be unique entry point and cannot be a FunctionStart + newGraph += (point -> ((edges - edge) ++ edgesOfPoint)) + newGraph -= p2 + true + } + case _ => false + } + } + } + + newGraph + } + + + private def addFunctionInvocationsEdges(graph: Map[ProgramPoint, Set[(ProgramPoint, TransitionLabel)]]): Map[ProgramPoint, Set[(ProgramPoint, TransitionLabel)]] = { + var augmentedGraph = graph + + graph.foreach{ + case (point@ExpressionPoint(FunctionInvocation(fd, args), _), edges) => { + val newPoint = FunctionStart(fd) + val newTransition = TransitionLabel(BooleanLiteral(true), fd.args.zip(args).map{ case (VarDecl(id, _), arg) => (id.toVariable, arg) }.toMap) + augmentedGraph += (point -> (edges + ((newPoint, newTransition)))) + } + case _ => ; + } + + augmentedGraph + } + + private def collectWithPathCondition(expression: Expr, startingPoint: ProgramPoint): Map[ProgramPoint, Set[(ProgramPoint, TransitionLabel)]] = { + var callGraph: Map[ProgramPoint, Set[(ProgramPoint, TransitionLabel)]] = Map() + + def rec(expr: Expr, path: List[Expr], startingPoint: ProgramPoint): Unit = { + val transitions: Set[(ProgramPoint, TransitionLabel)] = callGraph.get(startingPoint) match { + case None => Set() + case Some(s) => s + } + expr match { + //case FunctionInvocation(fd, args) => { + // val newPoint = FunctionStart(fd) + // val newTransition = TransitionLabel(And(path.toSeq), fd.args.zip(args).map{ case (VarDecl(id, _), arg) => (id.toVariable, arg) }.toMap) + // callGraph += (startingPoint -> (transitions + ((newPoint, newTransition)))) + // args.foreach(arg => rec(arg, path, startingPoint)) + //} + //this case is actually now handled in the unaryOp case + //case way@Waypoint(i, e) => { + // val newPoint = ExpressionPoint(way) + // val newTransition = TransitionLabel(And(path.toSeq), Map()) + // callGraph += (startingPoint -> (transitions + ((newPoint, newTransition)))) + // rec(e, List(), newPoint) + //} + case IfExpr(cond, then, elze) => { + rec(cond, path, startingPoint) + rec(then, cond :: path, startingPoint) + rec(elze, Not(cond) :: path, startingPoint) + } + case n@NAryOperator(args, _) => { + val newPoint = freshExpressionPoint(n) + val newTransition = TransitionLabel(And(path.toSeq), Map()) + callGraph += (startingPoint -> (transitions + ((newPoint, newTransition)))) + args.foreach(rec(_, List(), newPoint)) + } + case b@BinaryOperator(t1, t2, _) => { + val newPoint = freshExpressionPoint(b) + val newTransition = TransitionLabel(And(path.toSeq), Map()) + callGraph += (startingPoint -> (transitions + ((newPoint, newTransition)))) + rec(t1, List(), newPoint) + rec(t2, List(), newPoint) + } + case u@UnaryOperator(t, _) => { + val newPoint = freshExpressionPoint(u) + val newTransition = TransitionLabel(And(path.toSeq), Map()) + callGraph += (startingPoint -> (transitions + ((newPoint, newTransition)))) + rec(t, List(), newPoint) + } + case t : Terminal => { + val newPoint = freshExpressionPoint(t) + val newTransition = TransitionLabel(And(path.toSeq), Map()) + callGraph += (startingPoint -> (transitions + ((newPoint, newTransition)))) + } + case _ => scala.sys.error("Unhandled tree in collectWithPathCondition : " + expr) + } + } + + rec(expression, List(), startingPoint) + callGraph + } + + //given a path, follow the path to build the logical constraint that need to be satisfiable + def pathConstraint(path: Seq[(ProgramPoint, ProgramPoint, TransitionLabel)], assigns: List[Map[Expr, Expr]] = List()): Expr = { + if(path.isEmpty) BooleanLiteral(true) else { + val (_, _, TransitionLabel(cond, assign)) = path.head + val finalCond = assigns.foldRight(cond)((map, acc) => replace(map, acc)) + And(finalCond, + pathConstraint( + path.tail, + if(assign.isEmpty) assigns else assign.asInstanceOf[Map[Expr, Expr]] :: assigns + ) + ) + } + } + + private def isMain(fd: FunDef): Boolean = { + fd.annotations.exists(_ == "main") + } + + def findAllPaths(z3Solver: FairZ3Solver): Set[Seq[(ProgramPoint, ProgramPoint, TransitionLabel)]] = { + val waypoints: Set[ProgramPoint] = programPoints.filter{ case ExpressionPoint(Waypoint(_, _), _) => true case _ => false } + val sortedWaypoints: Seq[ProgramPoint] = waypoints.toSeq.sortWith((p1, p2) => { + val (ExpressionPoint(Waypoint(i1, _), _), ExpressionPoint(Waypoint(i2, _), _)) = (p1, p2) + i1 <= i2 + }) + + val functionPoints: Set[ProgramPoint] = programPoints.flatMap{ case f@FunctionStart(fd) => Set[ProgramPoint](f) case _ => Set[ProgramPoint]() } + val mainPoint: Option[ProgramPoint] = functionPoints.find{ case FunctionStart(fd) => isMain(fd) case p => sys.error("unexpected: " + p) } + + assert(mainPoint != None) + + if(sortedWaypoints.size == 0) { + findSimplePaths(mainPoint.get) + } else { + visitAllWaypoints(mainPoint.get :: sortedWaypoints.toList, z3Solver) match { + case None => Set() + case Some(p) => Set(p) + } + //Set( + // sortedWaypoints.zip(sortedWaypoints.tail).foldLeft(Seq[(ProgramPoint, ProgramPoint, TransitionLabel)]())((path, waypoint) => + // path ++ findPath(waypoint._1, waypoint._2)) + //) + } + } + + def visitAllWaypoints(waypoints: List[ProgramPoint], z3Solver: FairZ3Solver): Option[Seq[(ProgramPoint, ProgramPoint, TransitionLabel)]] = { + def rec(head: ProgramPoint, tail: List[ProgramPoint], path: Seq[(ProgramPoint, ProgramPoint, TransitionLabel)]): + Option[Seq[(ProgramPoint, ProgramPoint, TransitionLabel)]] = { + tail match { + case Nil => Some(path) + case x::xs => { + val allPaths = findSimplePaths(head, Some(x)) + var completePath: Option[Seq[(ProgramPoint, ProgramPoint, TransitionLabel)]] = None + allPaths.find(intermediatePath => { + val pc = pathConstraint(path ++ intermediatePath) + z3Solver.init() + z3Solver.restartZ3 + + var testcase: Option[Map[Identifier, Expr]] = None + + val (solverResult, model) = z3Solver.decideWithModel(pc, false) + solverResult match { + case None => { + false + } + case Some(true) => { + false + } + case Some(false) => { + val recPath = rec(x, xs, path ++ intermediatePath) + recPath match { + case None => false + case Some(path) => { + completePath = Some(path) + true + } + } + } + } + }) + completePath + } + } + } + rec(waypoints.head, waypoints.tail, Seq()) + } + + def findSimplePaths(from: ProgramPoint, to: Option[ProgramPoint] = None): Set[Seq[(ProgramPoint, ProgramPoint, TransitionLabel)]] = { + def dfs(point: ProgramPoint, path: List[(ProgramPoint, ProgramPoint, TransitionLabel)], visitedPoints: Set[ProgramPoint]): + Set[Seq[(ProgramPoint, ProgramPoint, TransitionLabel)]] = graph.get(point) match { + case None => Set(path.reverse) + case Some(edges) => { + if(to != None && to.get == point) + Set(path.reverse) + else if(to == None && edges.forall((edge: (ProgramPoint, TransitionLabel)) => visitedPoints.contains(edge._1) || point == edge._1)) + Set(path.reverse) + else { + edges.flatMap((edge: (ProgramPoint, TransitionLabel)) => { + val (neighbour, transition) = edge + if(visitedPoints.contains(neighbour) || point == neighbour) + Set[Seq[(ProgramPoint, ProgramPoint, TransitionLabel)]]() + else + dfs(neighbour, (point, neighbour, transition) :: path, visitedPoints + point) + }) + } + } + } + + dfs(from, List(), Set()) + } + + //find a path that goes through all waypoint in order + def findPath(from: ProgramPoint, to: ProgramPoint): Seq[(ProgramPoint, ProgramPoint, TransitionLabel)] = { + var visitedPoints: Set[ProgramPoint] = Set() + var history: Map[ProgramPoint, (ProgramPoint, TransitionLabel)] = Map() + var toVisit: List[ProgramPoint] = List(from) + + var currentPoint: ProgramPoint = null + while(!toVisit.isEmpty && currentPoint != to) { + currentPoint = toVisit.head + if(currentPoint != to) { + visitedPoints += currentPoint + toVisit = toVisit.tail + graph.get(currentPoint).foreach(edges => edges.foreach{ + case (neighbour, transition) => + if(!visitedPoints.contains(neighbour) && !toVisit.contains(neighbour)) { + toVisit ::= neighbour + history += (neighbour -> ((currentPoint, transition))) + } + }) + } + } + + def rebuildPath(point: ProgramPoint, path: List[(ProgramPoint, ProgramPoint, TransitionLabel)]): Seq[(ProgramPoint, ProgramPoint, TransitionLabel)] = { + if(point == from) path else { + val (previousPoint, transition) = history(point) + val newPath = (previousPoint, point, transition) :: path + rebuildPath(previousPoint, newPath) + } + } + + //TODO: handle case where the target node is not found + rebuildPath(to, List()) + } + + + lazy val toDotString: String = { + var vertexLabels: Set[(String, String)] = Set() + var vertexId = -1 + var point2vertex: Map[ProgramPoint, Int] = Map() + //return id and label + def getVertex(p: ProgramPoint): (String, String) = point2vertex.get(p) match { + case Some(id) => ("v_" + id, ppPoint(p)) + case None => { + vertexId += 1 + point2vertex += (p -> vertexId) + val pair = ("v_" + vertexId, ppPoint(p)) + vertexLabels += pair + pair + } + } + + def ppPoint(p: ProgramPoint): String = p match { + case FunctionStart(fd) => fd.id.name + case ExpressionPoint(Waypoint(i, e), _) => "WayPoint " + i + case ExpressionPoint(e, _) => e.toString + } + def ppLabel(l: TransitionLabel): String = { + val TransitionLabel(cond, assignments) = l + cond.toString + ", " + assignments.map(p => p._1 + " -> " + p._2).mkString("\n") + } + + val edges: List[(String, String, String)] = graph.flatMap(pair => { + val (startPoint, edges) = pair + val (startId, _) = getVertex(startPoint) + edges.map(pair => { + val (endPoint, label) = pair + val (endId, _) = getVertex(endPoint) + (startId, endId, ppLabel(label)) + }).toList + }).toList + + val res = ( + "digraph " + program.id.name + " {\n" + + vertexLabels.map(p => p._1 + " [label=\"" + p._2 + "\"];").mkString("\n") + "\n" + + edges.map(p => p._1 + " -> " + p._2 + " [label=\"" + p._3 + "\"];").mkString("\n") + "\n" + + "}") + + res + } + + def writeDotFile(filename: String) { + import java.io.FileWriter + import java.io.BufferedWriter + val fstream = new FileWriter(filename) + val out = new BufferedWriter(fstream) + out.write(toDotString) + out.close + } + +} + + //def hoistIte(expr: Expr): (Seq[Expr] => Expr, Seq[Expr]) = expr match { + // case ite@IfExpr(c, t, e) => { + // val (iteThen, valsThen) = hoistIte(t) + // val nbValsThen = valsThen.size + // val (iteElse, valsElse) = hoistIte(e) + // val nbValsElse = valsElse.size + // def ite(es: Seq[Expr]): Expr = { + // val argsThen = es.take(nbValsThen) + // val argsElse = es.drop(nbValsThen) + // IfExpr(c, iteThen(argsThen), iteElse(argsElse), e2) + // } + // (ite, valsThen ++ valsElse) + // } + // case BinaryOperator(t1, t2, op) => { + // val (iteLeft, valsLeft) = hoistIte(t1) + // val (iteRight, valsRight) = hoistIte(t2) + // def ite(e1: Expr, e2: Expr): Expr = { + + // } + // iteLeft( + // iteRight( + // op(thenValRight, thenValLeft), + // op(thenValRight, elseValLeft) + // ), iteRight( + // op(elseValRight, thenValLeft), + // op(elseValRight, elseValLeft) + // ) + // ) + // } + // case NAryOperator(args, op) => { + + // } + // case (t: Terminal) => { + // def ite(es: Seq[Expr]): Expr = { + // require(es.size == 1) + // es.head + // } + // (ite, Seq(t)) + // } + // case _ => scala.sys.error("Unhandled tree in hoistIte : " + expr) + //} + diff --git a/src/main/scala/leon/testgen/TestGeneration.scala b/src/main/scala/leon/testgen/TestGeneration.scala new file mode 100644 index 0000000000000000000000000000000000000000..1b48d72579f0ff620ef7d35f41e9bcfe841f3ac4 --- /dev/null +++ b/src/main/scala/leon/testgen/TestGeneration.scala @@ -0,0 +1,145 @@ +package leon.testgen + +import leon.purescala.Common._ +import leon.purescala.Definitions._ +import leon.purescala.Trees._ +import leon.purescala.TypeTrees._ +import leon.purescala.ScalaPrinter +import leon.Extensions._ +import leon.FairZ3Solver +import leon.Reporter + +import scala.collection.mutable.{Set => MutableSet} + +class TestGeneration(reporter: Reporter) extends Analyser(reporter) { + + def description: String = "Generate random testcases" + override def shortDescription: String = "test" + + private val z3Solver = new FairZ3Solver(reporter) + + def analyse(program: Program) { + z3Solver.setProgram(program) + reporter.info("Running test generation") + + val testcases = generateTestCases(program) + + val topFunDef = program.definedFunctions.find(fd => isMain(fd)).get +//fd.body.exists(body => body match { +// case Waypoint(1, _) => true +// case _ => false +// }) + val testFun = new FunDef(FreshIdentifier("test"), UnitType, Seq()) + val funInvocs = testcases.map(testcase => { + val params = topFunDef.args + val args = topFunDef.args.map{ + case VarDecl(id, tpe) => testcase.get(id) match { + case Some(v) => v + case None => simplestValue(tpe) + } + } + FunctionInvocation(topFunDef, args) + }).toSeq + testFun.body = Some(Block(funInvocs, UnitLiteral)) + + val Program(id, ObjectDef(objId, defs, invariants)) = program + val testProgram = Program(id, ObjectDef(objId, testFun +: defs , invariants)) + testProgram.writeScalaFile("TestGen.scalax") + + reporter.info("Running from waypoint with the following testcases:\n") + reporter.info(testcases.mkString("\n")) + } + + private def isMain(fd: FunDef): Boolean = { + fd.annotations.exists(_ == "main") + } + + def generatePathConditions(program: Program): Set[Expr] = { + + val callGraph = new CallGraph(program) + callGraph.writeDotFile("testgen.dot") + val constraints = callGraph.findAllPaths(z3Solver).map(path => { + println("Path is: " + path) + val cnstr = callGraph.pathConstraint(path) + println("constraint is: " + cnstr) + cnstr + }) + constraints + } + + private def generateTestCases(program: Program): Set[Map[Identifier, Expr]] = { + val allPaths = generatePathConditions(program) + + allPaths.flatMap(pathCond => { + reporter.info("Now considering path condition: " + pathCond) + + var testcase: Option[Map[Identifier, Expr]] = None + //val z3Solver: FairZ3Solver = loadedSolverExtensions.find(se => se.isInstanceOf[FairZ3Solver]).get.asInstanceOf[FairZ3Solver] + + z3Solver.init() + z3Solver.restartZ3 + val (solverResult, model) = z3Solver.decideWithModel(pathCond, false) + + solverResult match { + case None => Seq() + case Some(true) => { + reporter.info("The path is unreachable") + Seq() + } + case Some(false) => { + reporter.info("The model should be used as the testcase") + Seq(model) + } + } + }) + } + + //private def generatePathConditions(funDef: FunDef): Seq[Expr] = if(!funDef.hasImplementation) Seq() else { + // val body = funDef.body.get + // val cleanBody = hoistIte(expandLets(matchToIfThenElse(body))) + // collectWithPathCondition(cleanBody) + //} + + //private def generateTestCases(funDef: FunDef): Seq[Map[Identifier, Expr]] = { + // val allPaths = generatePathConditions(funDef) + + // allPaths.flatMap(pathCond => { + // reporter.info("Now considering path condition: " + pathCond) + + // var testcase: Option[Map[Identifier, Expr]] = None + // //val z3Solver: FairZ3Solver = loadedSolverExtensions.find(se => se.isInstanceOf[FairZ3Solver]).get.asInstanceOf[FairZ3Solver] + // + // z3Solver.init() + // z3Solver.restartZ3 + // val (solverResult, model) = z3Solver.decideWithModel(pathCond, false) + + // solverResult match { + // case None => Seq() + // case Some(true) => { + // reporter.info("The path is unreachable") + // Seq() + // } + // case Some(false) => { + // reporter.info("The model should be used as the testcase") + // Seq(model) + // } + // } + // }) + //} + + //prec: ite are hoisted and no lets nor match occurs + //private def collectWithPathCondition(expression: Expr): Seq[Expr] = { + // var allPaths: Seq[Expr] = Seq() + + // def rec(expr: Expr, path: List[Expr]): Seq[Expr] = expr match { + // case IfExpr(cond, then, elze) => rec(then, cond :: path) ++ rec(elze, Not(cond) :: path) + // case _ => Seq(And(path.toSeq)) + // } + + // rec(expression, List()) + //} + +} + + + diff --git a/testcases/Abs.scala b/testcases/Abs.scala index 5efb41797b5dc4401407c5fea7c3fc6a7591f4f2..52baf710140b76bf5edeeda52ef9e0da5385734f 100644 --- a/testcases/Abs.scala +++ b/testcases/Abs.scala @@ -1,51 +1,5 @@ -import leon.Utils._ - object Abs { - - def abs(tab: Map[Int, Int], size: Int): Map[Int, Int] = ({ - require(size <= 5 && isArray(tab, size)) - var k = 0 - var tabres = Map.empty[Int, Int] - (while(k < size) { - if(tab(k) < 0) - tabres = tabres.updated(k, -tab(k)) - else - tabres = tabres.updated(k, tab(k)) - k = k + 1 - }) invariant(isArray(tabres, k) && k >= 0 && k <= size && isPositive(tabres, k)) - tabres - }) ensuring(res => isArray(res, size) && isPositive(res, size)) - - def isPositive(a: Map[Int, Int], size: Int): Boolean = { - require(size <= 10 && isArray(a, size)) - def rec(i: Int): Boolean = { - require(i >= 0) - if(i >= size) - true - else { - if(a(i) < 0) - false - else - rec(i+1) - } - } - rec(0) - } - - def isArray(a: Map[Int, Int], size: Int): Boolean = { - - def rec(i: Int): Boolean = { - require(i >= 0) - if(i >= size) true else { - if(a.isDefinedAt(i)) rec(i+1) else false - } - } - - if(size < 0) - false - else - rec(0) - } + def abs(x: Int): Int = (if(x < 0) -x else x) ensuring(_ >= 0) } diff --git a/testcases/AbsArray.scala b/testcases/AbsArray.scala new file mode 100644 index 0000000000000000000000000000000000000000..0086891cdafc5a62987d5563695b38983de24fb4 --- /dev/null +++ b/testcases/AbsArray.scala @@ -0,0 +1,51 @@ +import leon.Utils._ + +object AbsArray { + + + def abs(tab: Map[Int, Int], size: Int): Map[Int, Int] = ({ + require(size <= 5 && isArray(tab, size)) + var k = 0 + var tabres = Map.empty[Int, Int] + (while(k < size) { + if(tab(k) < 0) + tabres = tabres.updated(k, -tab(k)) + else + tabres = tabres.updated(k, tab(k)) + k = k + 1 + }) invariant(isArray(tabres, k) && k >= 0 && k <= size && isPositive(tabres, k)) + tabres + }) ensuring(res => isArray(res, size) && isPositive(res, size)) + + def isPositive(a: Map[Int, Int], size: Int): Boolean = { + require(size <= 10 && isArray(a, size)) + def rec(i: Int): Boolean = { + require(i >= 0) + if(i >= size) + true + else { + if(a(i) < 0) + false + else + rec(i+1) + } + } + rec(0) + } + + def isArray(a: Map[Int, Int], size: Int): Boolean = { + + def rec(i: Int): Boolean = { + require(i >= 0) + if(i >= size) true else { + if(a.isDefinedAt(i)) rec(i+1) else false + } + } + + if(size < 0) + false + else + rec(0) + } + +} diff --git a/testcases/AbsFun.scala b/testcases/AbsFun.scala new file mode 100644 index 0000000000000000000000000000000000000000..8186824b03bca673a899a8505347c09c86e43360 --- /dev/null +++ b/testcases/AbsFun.scala @@ -0,0 +1,65 @@ +import leon.Utils._ + +object AbsFun { + + + def isPositive(a : Array[Int], size : Int) : Boolean = { + require(a.length >= 0 && size <= a.length) + rec(0, a, size) + } + + def rec(i: Int, a: Array[Int], size: Int) : Boolean = { + require(a.length >= 0 && size <= a.length && i >= 0) + + if(i >= size) true + else { + if (a(i) < 0) + false + else + rec(i + 1, a, size) + } + } + + def abs(tab: Array[Int]): Array[Int] = { + require(tab.length >= 0) + val t = while0(Array.fill(tab.length)(0), 0, tab) + t._1 + } ensuring(res => isPositive(res, res.length)) + + + def while0(t: Array[Int], k: Int, tab: Array[Int]): (Array[Int], Int) = { + require(tab.length >= 0 && + t.length == tab.length && + k >= 0 && + k <= tab.length && + isPositive(t, k)) + + if(k < tab.length) { + val nt = if(tab(k) < 0) { + t.updated(k, -tab(k)) + } else { + t.updated(k, tab(k)) + } + while0(nt, k+1, tab) + } else { + (t, k) + } + } ensuring(res => + res._2 >= tab.length && + res._1.length == tab.length && + res._2 >= 0 && + res._2 <= tab.length && + isPositive(res._1, res._2)) + + def property(t: Array[Int], k: Int): Boolean = { + require(isPositive(t, k) && t.length >= 0 && k >= 0) + if(k < t.length) { + val nt = if(t(k) < 0) { + t.updated(k, -t(k)) + } else { + t.updated(k, t(k)) + } + isPositive(nt, k+1) + } else true + } holds +} diff --git a/testcases/BinarySearch.scala b/testcases/BinarySearch.scala index 92d4cf0667fe0175b80ae0da34679e991efcc633..986ee41ae6a057065bc8520c5af010092b67b2b2 100644 --- a/testcases/BinarySearch.scala +++ b/testcases/BinarySearch.scala @@ -4,10 +4,10 @@ import leon.Utils._ object BinarySearch { - def binarySearch(a: Map[Int, Int], size: Int, key: Int): Int = ({ - require(isArray(a, size) && size < 5 && sorted(a, size, 0, size - 1)) + def binarySearch(a: Array[Int], key: Int): Int = ({ + require(a.length > 0 && sorted(a, 0, a.length - 1)) var low = 0 - var high = size - 1 + var high = a.length - 1 var res = -1 (while(low <= high && res == -1) { @@ -21,13 +21,27 @@ object BinarySearch { high = i - 1 else if(v < key) low = i + 1 - }) invariant(0 <= low && low <= high + 1 && high < size && (if(res >= 0) a(res) == key else (!occurs(a, 0, low, key) && !occurs(a, high + 1, size, key)))) + }) invariant( + res >= -1 && + res < a.length && + 0 <= low && + low <= high + 1 && + high >= -1 && + high < a.length && + (if (res >= 0) + a(res) == key else + (!occurs(a, 0, low, key) && !occurs(a, high + 1, a.length, key)) + ) + ) res - }) ensuring(res => res >= -1 && res < size && (if(res >= 0) a(res) == key else !occurs(a, 0, size, key))) + }) ensuring(res => + res >= -1 && + res < a.length && + (if(res >= 0) a(res) == key else !occurs(a, 0, a.length, key))) - def occurs(a: Map[Int, Int], from: Int, to: Int, key: Int): Boolean = { - require(isArray(a, to) && to < 5 && from >= 0) + def occurs(a: Array[Int], from: Int, to: Int, key: Int): Boolean = { + require(a.length >= 0 && to <= a.length && from >= 0) def rec(i: Int): Boolean = { require(i >= 0) if(i >= to) @@ -43,31 +57,16 @@ object BinarySearch { } - def isArray(a: Map[Int, Int], size: Int): Boolean = { - - def rec(i: Int): Boolean = { - require(i >= 0) - if(i >= size) true else { - if(a.isDefinedAt(i)) rec(i+1) else false - } - } - - if(size < 0) - false - else - rec(0) - } - - def sorted(a: Map[Int,Int], size: Int, l: Int, u: Int) : Boolean = { - require(isArray(a, size) && size < 5 && l >= 0 && l <= u && u < size) - val t = sortedWhile(true, l, l, u, a, size) + def sorted(a: Array[Int], l: Int, u: Int) : Boolean = { + require(a.length >= 0 && l >= 0 && l <= u && u < a.length) + val t = sortedWhile(true, l, l, u, a) t._1 } - def sortedWhile(isSorted: Boolean, k: Int, l: Int, u: Int, a: Map[Int,Int], size: Int) : (Boolean, Int) = { - require(isArray(a, size) && size < 5 && l >= 0 && l <= u && u < size && k >= l && k <= u) + def sortedWhile(isSorted: Boolean, k: Int, l: Int, u: Int, a: Array[Int]): (Boolean, Int) = { + require(a.length >= 0 && l >= 0 && l <= u && u < a.length && k >= l && k <= u) if(k < u) { - sortedWhile(if(a(k) > a(k + 1)) false else isSorted, k + 1, l, u, a, size) + sortedWhile(if(a(k) > a(k + 1)) false else isSorted, k + 1, l, u, a) } else (isSorted, k) } } diff --git a/testcases/BubbleSort.scala b/testcases/BubbleSort.scala index 4e274e2707b0cff262e712ab4b78b53eee0db990..3551ebf16416f68bd932adc512177edc6f2611d8 100644 --- a/testcases/BubbleSort.scala +++ b/testcases/BubbleSort.scala @@ -2,44 +2,44 @@ import leon.Utils._ /* The calculus of Computation textbook */ -object Bubble { +object BubbleSort { - def sort(a: Map[Int, Int], size: Int): Map[Int, Int] = ({ - require(size < 5 && isArray(a, size)) - var i = size - 1 + def sort(a: Array[Int]): Array[Int] = ({ + require(a.length >= 1) + var i = a.length - 1 var j = 0 - var sortedArray = a + val sa = a.clone (while(i > 0) { j = 0 (while(j < i) { - if(sortedArray(j) > sortedArray(j+1)) { - val tmp = sortedArray(j) - sortedArray = sortedArray.updated(j, sortedArray(j+1)) - sortedArray = sortedArray.updated(j+1, tmp) + if(sa(j) > sa(j+1)) { + val tmp = sa(j) + sa(j) = sa(j+1) + sa(j+1) = tmp } j = j + 1 }) invariant( j >= 0 && j <= i && - i < size && - isArray(sortedArray, size) && - partitioned(sortedArray, size, 0, i, i+1, size-1) && - sorted(sortedArray, size, i, size-1) && - partitioned(sortedArray, size, 0, j-1, j, j) + i < sa.length && + sa.length >= 0 && + partitioned(sa, 0, i, i+1, sa.length-1) && + sorted(sa, i, sa.length-1) && + partitioned(sa, 0, j-1, j, j) ) i = i - 1 }) invariant( i >= 0 && - i < size && - isArray(sortedArray, size) && - partitioned(sortedArray, size, 0, i, i+1, size-1) && - sorted(sortedArray, size, i, size-1) + i < sa.length && + sa.length >= 0 && + partitioned(sa, 0, i, i+1, sa.length-1) && + sorted(sa, i, sa.length-1) ) - sortedArray - }) ensuring(res => sorted(res, size, 0, size-1)) + sa + }) ensuring(res => sorted(res, 0, a.length-1)) - def sorted(a: Map[Int, Int], size: Int, l: Int, u: Int): Boolean = { - require(isArray(a, size) && size < 5 && l >= 0 && u < size && l <= u) + def sorted(a: Array[Int], l: Int, u: Int): Boolean = { + require(a.length >= 0 && l >= 0 && u < a.length && l <= u) var k = l var isSorted = true (while(k < u) { @@ -49,57 +49,9 @@ object Bubble { }) invariant(k <= u && k >= l) isSorted } - /* - // --------------------- sorted -------------------- - def sorted(a: Map[Int,Int], size: Int, l: Int, u: Int) : Boolean = { - require(isArray(a, size) && size < 5 && l >= 0 && l <= u && u < size) - val t = sortedWhile(true, l, l, u, a, size) - t._1 - } - - def sortedWhile(isSorted: Boolean, k: Int, l: Int, u: Int, a: Map[Int,Int], size: Int) : (Boolean, Int) = { - require(isArray(a, size) && size < 5 && l >= 0 && l <= u && u < size && k >= l && k <= u) - if(k < u) { - sortedWhile(if(a(k) > a(k + 1)) false else isSorted, k + 1, l, u, a, size) - } else (isSorted, k) - } - */ - - /* - // ------------- partitioned ------------------ - def partitioned(a: Map[Int,Int], size: Int, l1: Int, u1: Int, l2: Int, u2: Int) : Boolean = { - require(isArray(a, size) && size < 5 && l1 >= 0 && u1 < l2 && u2 < size) - if(l2 > u2 || l1 > u1) - true - else { - val t = partitionedWhile(l2, true, l1, l1, size, u2, l2, u1, a) - t._2 - } - } - def partitionedWhile(j: Int, isPartitionned: Boolean, i: Int, l1: Int, size: Int, u2: Int, l2: Int, u1: Int, a: Map[Int,Int]) : (Int, Boolean, Int) = { - require(isArray(a, size) && size < 5 && l1 >= 0 && l1 <= u1 && u1 < l2 && l2 <= u2 && u2 < size && i >= l1) - - if(i <= u1) { - val t = partitionedNestedWhile(isPartitionned, l2, i, l1, u1, size, u2, a, l2) - partitionedWhile(t._2, t._1, i + 1, l1, size, u2, l2, u1, a) - } else (j, isPartitionned, i) - } - def partitionedNestedWhile(isPartitionned: Boolean, j: Int, i: Int, l1: Int, u1: Int, size: Int, u2: Int, a: Map[Int,Int], l2: Int): (Boolean, Int) = { - require(isArray(a, size) && size < 5 && l1 >= 0 && l1 <= u1 && u1 < l2 && l2 <= u2 && u2 < size && j >= l2 && i >= l1 && i <= u1) - if (j <= u2) { - partitionedNestedWhile( - (if (a(i) > a(j)) - false - else - isPartitionned), - j + 1, i, l1, u1, size, u2, a, l2) - } else (isPartitionned, j) - } - */ - - def partitioned(a: Map[Int, Int], size: Int, l1: Int, u1: Int, l2: Int, u2: Int): Boolean = { - require(l1 >= 0 && u1 < l2 && u2 < size && isArray(a, size) && size < 5) + def partitioned(a: Array[Int], l1: Int, u1: Int, l2: Int, u2: Int): Boolean = { + require(a.length >= 0 && l1 >= 0 && u1 < l2 && u2 < a.length) if(l2 > u2 || l1 > u1) true else { @@ -119,14 +71,4 @@ object Bubble { } } - def isArray(a: Map[Int, Int], size: Int): Boolean = { - def rec(i: Int): Boolean = if(i >= size) true else { - if(a.isDefinedAt(i)) rec(i+1) else false - } - if(size <= 0) - false - else - rec(0) - } - } diff --git a/testcases/LinearSearch.scala b/testcases/LinearSearch.scala index e91a3f2679b33b8b35e7d64989db1b7ade451d14..e18c4039c7d7f7418399ff16483d1b18cc6895a9 100644 --- a/testcases/LinearSearch.scala +++ b/testcases/LinearSearch.scala @@ -4,29 +4,29 @@ import leon.Utils._ object LinearSearch { - def linearSearch(a: Map[Int, Int], size: Int, c: Int): Boolean = ({ - require(size <= 5 && isArray(a, size)) + def linearSearch(a: Array[Int], c: Int): Boolean = ({ + require(a.length >= 0) var i = 0 var found = false - (while(i < size) { + (while(i < a.length) { if(a(i) == c) found = true i = i + 1 - }) invariant(i <= size && - i >= 0 && - (if(found) contains(a, i, c) else !contains(a, i, c)) - ) + }) invariant( + i <= a.length && + i >= 0 && + (if(found) contains(a, i, c) else !contains(a, i, c)) + ) found - }) ensuring(res => if(res) contains(a, size, c) else !contains(a, size, c)) + }) ensuring(res => if(res) contains(a, a.length, c) else !contains(a, a.length, c)) - def contains(a: Map[Int, Int], size: Int, c: Int): Boolean = { - require(isArray(a, size) && size <= 5) + def contains(a: Array[Int], size: Int, c: Int): Boolean = { + require(a.length >= 0 && size >= 0 && size <= a.length) content(a, size).contains(c) } - - def content(a: Map[Int, Int], size: Int): Set[Int] = { - require(isArray(a, size) && size <= 5) + def content(a: Array[Int], size: Int): Set[Int] = { + require(a.length >= 0 && size >= 0 && size <= a.length) var set = Set.empty[Int] var i = 0 (while(i < size) { @@ -36,18 +36,4 @@ object LinearSearch { set } - def isArray(a: Map[Int, Int], size: Int): Boolean = { - - def rec(i: Int): Boolean = { - require(i >= 0) - if(i >= size) true else { - if(a.isDefinedAt(i)) rec(i+1) else false - } - } - - if(size < 0) - false - else - rec(0) - } } diff --git a/testcases/ListImp.scala b/testcases/ListImp.scala new file mode 100644 index 0000000000000000000000000000000000000000..f3887419ecf59c6241ed530febbac6400538f6bd --- /dev/null +++ b/testcases/ListImp.scala @@ -0,0 +1,49 @@ +import leon.Utils._ + +object ListImp { + + sealed abstract class List + case class Cons(head: Int, tail: List) extends List + case class Nil() extends List + + def content(l: List) : Set[Int] = l match { + case Nil() => Set.empty[Int] + case Cons(x, xs) => Set(x) ++ content(xs) + } + + def size(l: List) : Int = { + var r = 0 + (while(!l.isInstanceOf[Nil]) { + r = r+1 + }) invariant(r >= 0) + r + } ensuring(res => res >= 0) + + def reverse(l: List): List = { + var r: List = Nil() + var l2: List = l + + (while(!l2.isInstanceOf[Nil]) { + val Cons(head, tail) = l2 + l2 = tail + r = Cons(head, r) + }) invariant(content(r) ++ content(l2) == content(l)) + + r + } ensuring(res => content(res) == content(l)) + + def append(l1 : List, l2 : List) : List = { + var r: List = l2 + var tmp: List = reverse(l1) + + (while(!tmp.isInstanceOf[Nil]) { + val Cons(head, tail) = tmp + tmp = tail + r = Cons(head, r) + }) invariant(content(r) ++ content(tmp) == content(l1) ++ content(l2)) + + r + } ensuring(content(_) == content(l1) ++ content(l2)) + + +} diff --git a/testcases/MaxSum.scala b/testcases/MaxSum.scala index 07c5d12ab2450cc95af964c7078759a13fe8f0cb..ba724d255c23b782e1b4de716ccc8d50043e2059 100644 --- a/testcases/MaxSum.scala +++ b/testcases/MaxSum.scala @@ -4,44 +4,26 @@ import leon.Utils._ object MaxSum { - - def maxSum(a: Map[Int, Int], size: Int): (Int, Int) = ({ - require(isArray(a, size) && size < 5 && isPositive(a, size)) + def maxSum(a: Array[Int]): (Int, Int) = ({ + require(a.length >= 0 && isPositive(a)) var sum = 0 var max = 0 var i = 0 - (while(i < size) { + (while(i < a.length) { if(max < a(i)) max = a(i) sum = sum + a(i) i = i + 1 - }) invariant (sum <= i * max && /*isGreaterEq(a, i, max) &&*/ /*(if(i == 0) max == 0 else true) &&*/ i >= 0 && i <= size) + }) invariant (sum <= i * max && i >= 0 && i <= a.length) (sum, max) - }) ensuring(res => res._1 <= size * res._2) + }) ensuring(res => res._1 <= a.length * res._2) -/* - def isGreaterEq(a: Map[Int, Int], size: Int, n: Int): Boolean = { - require(size <= 5 && isArray(a, size)) - def rec(i: Int): Boolean = { - require(i >= 0) - if(i >= size) - true - else { - if(a(i) > n) - false - else - rec(i+1) - } - } - rec(0) - } - */ - def isPositive(a: Map[Int, Int], size: Int): Boolean = { - require(size <= 5 && isArray(a, size)) + def isPositive(a: Array[Int]): Boolean = { + require(a.length >= 0) def rec(i: Int): Boolean = { require(i >= 0) - if(i >= size) + if(i >= a.length) true else { if(a(i) < 0) @@ -53,18 +35,4 @@ object MaxSum { rec(0) } - def isArray(a: Map[Int, Int], size: Int): Boolean = { - - def rec(i: Int): Boolean = { - require(i >= 0) - if(i >= size) true else { - if(a.isDefinedAt(i)) rec(i+1) else false - } - } - - if(size < 0) - false - else - rec(0) - } } diff --git a/testcases/master-thesis-regis/Arithmetic.scala b/testcases/master-thesis-regis/Arithmetic.scala new file mode 100644 index 0000000000000000000000000000000000000000..9173132e00e9e01d27155d1eec95eacc2aa44f1e --- /dev/null +++ b/testcases/master-thesis-regis/Arithmetic.scala @@ -0,0 +1,73 @@ +import leon.Utils._ + +object Arithmetic { + + /* VSTTE 2008 - Dafny paper */ + def mult(x : Int, y : Int): Int = ({ + var r = 0 + if(y < 0) { + var n = y + (while(n != 0) { + r = r - x + n = n + 1 + }) invariant(r == x * (y - n) && 0 <= -n) + } else { + var n = y + (while(n != 0) { + r = r + x + n = n - 1 + }) invariant(r == x * (y - n) && 0 <= n) + } + r + }) ensuring(_ == x*y) + + /* VSTTE 2008 - Dafny paper */ + def add(x : Int, y : Int): Int = ({ + var r = x + if(y < 0) { + var n = y + (while(n != 0) { + r = r - 1 + n = n + 1 + }) invariant(r == x + y - n && 0 <= -n) + } else { + var n = y + (while(n != 0) { + r = r + 1 + n = n - 1 + }) invariant(r == x + y - n && 0 <= n) + } + r + }) ensuring(_ == x+y) + + /* VSTTE 2008 - Dafny paper */ + def addBuggy(x : Int, y : Int): Int = ({ + var r = x + if(y < 0) { + var n = y + (while(n != 0) { + r = r + 1 + n = n + 1 + }) invariant(r == x + y - n && 0 <= -n) + } else { + var n = y + (while(n != 0) { + r = r + 1 + n = n - 1 + }) invariant(r == x + y - n && 0 <= n) + } + r + }) ensuring(_ == x+y) + + def sum(n: Int): Int = { + require(n >= 0) + var r = 0 + var i = 0 + (while(i < n) { + i = i + 1 + r = r + i + }) invariant(r >= i && i >= 0 && r >= 0) + r + } ensuring(_ >= n) + +} diff --git a/testcases/master-thesis-regis/ArrayOperations.scala b/testcases/master-thesis-regis/ArrayOperations.scala new file mode 100644 index 0000000000000000000000000000000000000000..91eb371faa8e550f3d85dfa17d13ba2ef3d6e101 --- /dev/null +++ b/testcases/master-thesis-regis/ArrayOperations.scala @@ -0,0 +1,207 @@ +import leon.Utils._ + +object ArrayOperations { + + /* VSTTE 2008 - Dafny paper */ + def binarySearch(a: Array[Int], key: Int): Int = ({ + require(a.length > 0 && sorted(a, 0, a.length - 1)) + var low = 0 + var high = a.length - 1 + var res = -1 + + (while(low <= high && res == -1) { + val i = (high + low) / 2 + val v = a(i) + + if(v == key) + res = i + + if(v > key) + high = i - 1 + else if(v < key) + low = i + 1 + }) invariant( + res >= -1 && + res < a.length && + 0 <= low && + low <= high + 1 && + high >= -1 && + high < a.length && + (if (res >= 0) + a(res) == key else + (!occurs(a, 0, low, key) && !occurs(a, high + 1, a.length, key)) + ) + ) + res + }) ensuring(res => + res >= -1 && + res < a.length && + (if(res >= 0) a(res) == key else !occurs(a, 0, a.length, key))) + + + def occurs(a: Array[Int], from: Int, to: Int, key: Int): Boolean = { + require(a.length >= 0 && to <= a.length && from >= 0) + def rec(i: Int): Boolean = { + require(i >= 0) + if(i >= to) + false + else { + if(a(i) == key) true else rec(i+1) + } + } + if(from >= to) + false + else + rec(from) + } + + def sorted(a: Array[Int], l: Int, u: Int): Boolean = { + require(a.length >= 0 && l >= 0 && u < a.length && l <= u) + var k = l + var isSorted = true + (while(k < u) { + if(a(k) > a(k+1)) + isSorted = false + k = k + 1 + }) invariant(k <= u && k >= l) + isSorted + } + + /* The calculus of Computation textbook */ + def bubbleSort(a: Array[Int]): Array[Int] = ({ + require(a.length >= 1) + var i = a.length - 1 + var j = 0 + val sa = a.clone + (while(i > 0) { + j = 0 + (while(j < i) { + if(sa(j) > sa(j+1)) { + val tmp = sa(j) + sa(j) = sa(j+1) + sa(j+1) = tmp + } + j = j + 1 + }) invariant( + j >= 0 && + j <= i && + i < sa.length && + sa.length >= 0 && + partitioned(sa, 0, i, i+1, sa.length-1) && + sorted(sa, i, sa.length-1) && + partitioned(sa, 0, j-1, j, j) + ) + i = i - 1 + }) invariant( + i >= 0 && + i < sa.length && + sa.length >= 0 && + partitioned(sa, 0, i, i+1, sa.length-1) && + sorted(sa, i, sa.length-1) + ) + sa + }) ensuring(res => sorted(res, 0, a.length-1)) + + def partitioned(a: Array[Int], l1: Int, u1: Int, l2: Int, u2: Int): Boolean = { + require(a.length >= 0 && l1 >= 0 && u1 < l2 && u2 < a.length) + if(l2 > u2 || l1 > u1) + true + else { + var i = l1 + var j = l2 + var isPartitionned = true + (while(i <= u1) { + j = l2 + (while(j <= u2) { + if(a(i) > a(j)) + isPartitionned = false + j = j + 1 + }) invariant(j >= l2 && i <= u1) + i = i + 1 + }) invariant(i >= l1) + isPartitionned + } + } + + /* The calculus of Computation textbook */ + def linearSearch(a: Array[Int], c: Int): Boolean = ({ + require(a.length >= 0) + var i = 0 + var found = false + (while(i < a.length) { + if(a(i) == c) + found = true + i = i + 1 + }) invariant( + i <= a.length && + i >= 0 && + (if(found) contains(a, i, c) else !contains(a, i, c)) + ) + found + }) ensuring(res => if(res) contains(a, a.length, c) else !contains(a, a.length, c)) + + def contains(a: Array[Int], size: Int, c: Int): Boolean = { + require(a.length >= 0 && size >= 0 && size <= a.length) + content(a, size).contains(c) + } + + def content(a: Array[Int], size: Int): Set[Int] = { + require(a.length >= 0 && size >= 0 && size <= a.length) + var set = Set.empty[Int] + var i = 0 + (while(i < size) { + set = set ++ Set(a(i)) + i = i + 1 + }) invariant(i >= 0 && i <= size) + set + } + + def abs(tab: Array[Int]): Array[Int] = ({ + require(tab.length >= 0) + var k = 0 + val tabres = Array.fill(tab.length)(0) + (while(k < tab.length) { + if(tab(k) < 0) + tabres(k) = -tab(k) + else + tabres(k) = tab(k) + k = k + 1 + }) invariant( + tabres.length == tab.length && + k >= 0 && k <= tab.length && + isPositive(tabres, k)) + tabres + }) ensuring(res => isPositive(res, res.length)) + + def isPositive(a: Array[Int], size: Int): Boolean = { + require(a.length >= 0 && size <= a.length) + def rec(i: Int): Boolean = { + require(i >= 0) + if(i >= size) + true + else { + if(a(i) < 0) + false + else + rec(i+1) + } + } + rec(0) + } + + /* VSTTE 2010 challenge 1 */ + def maxSum(a: Array[Int]): (Int, Int) = ({ + require(a.length >= 0 && isPositive(a, a.length)) + var sum = 0 + var max = 0 + var i = 0 + (while(i < a.length) { + if(max < a(i)) + max = a(i) + sum = sum + a(i) + i = i + 1 + }) invariant (sum <= i * max && i >= 0 && i <= a.length) + (sum, max) + }) ensuring(res => res._1 <= a.length * res._2) + +} diff --git a/testcases/master-thesis-regis/Constraints.scala b/testcases/master-thesis-regis/Constraints.scala new file mode 100644 index 0000000000000000000000000000000000000000..4b7c7133ddcbd5018cc6bf6b896d326e439deaf2 --- /dev/null +++ b/testcases/master-thesis-regis/Constraints.scala @@ -0,0 +1,76 @@ +import leon.Utils._ + +object Epsilon4 { + + sealed abstract class MyList + case class MyCons(head: Int, tail: MyList) extends MyList + case class MyNil() extends MyList + + def size(lst: MyList): Int = lst match { + case MyNil() => 0 + case MyCons(_, xs) => 1 + size(xs) + } + + def setSize(set: Set[Int]): Int = { + if(set == Set.empty[Int]) + 0 + else { + val elem = epsilon((x : Int) => set contains x) + 1 + setSize(set -- Set[Int](elem)) + } + } ensuring(_ >= 0) + + + def toSet(lst: MyList): Set[Int] = lst match { + case MyCons(x, xs) => toSet(xs) ++ Set(x) + case MyNil() => Set[Int]() + } + + def toList(set: Set[Int]): MyList = { + if(set == Set.empty[Int]) + MyNil() + else { + val elem = epsilon((x : Int) => set contains x) + MyCons(elem, toList(set -- Set[Int](elem))) + } + } + + def sizeToListEq(lst: MyList): Boolean = { + size(toList(toSet(lst))) == size(lst) + } holds + + def sizeToListLessEq(lst: MyList): Boolean = { + size(toList(toSet(lst))) <= size(lst) + } holds + + def toListEq(lst: MyList): Boolean = { + toList(toSet(lst)) == lst + } holds + + def positiveNum(): Int = { + epsilon((x: Int) => x > 0) + } ensuring(_ > 0) + + def negativeNum(): Int = { + epsilon((x: Int) => x < 0) + } ensuring(_ < 0) + + def linearEquation(): (Int, Int) = { + val sol = epsilon((t: (Int, Int)) => + 2*t._1 + 3*t._2 == 10 && t._1 >= 0 && t._2 >= 0) + sol + } ensuring(res => res == (2, 2) || res == (5, 0)) + + + def nonDeterministicExecution(): Int = { + var i = 0 + var b = epsilon((x: Boolean) => true) + while(b) { + i = i + 1 + b = epsilon((x: Boolean) => true) + } + i + } ensuring(_ <= 10) + + +} diff --git a/testcases/master-thesis-regis/ListOperations.scala b/testcases/master-thesis-regis/ListOperations.scala new file mode 100644 index 0000000000000000000000000000000000000000..4bb59c26472cfb9f07a09b6571db86cb62b18db7 --- /dev/null +++ b/testcases/master-thesis-regis/ListOperations.scala @@ -0,0 +1,146 @@ +import leon.Utils._ +import leon.Annotations._ + +object ListOperations { + + sealed abstract class List + case class Cons(head: Int, tail: List) extends List + case class Nil() extends List + + sealed abstract class IPList + case class IPCons(head: (Int, Int), tail: IPList) extends IPList + case class IPNil() extends IPList + + def content(l: List) : Set[Int] = l match { + case Nil() => Set.empty[Int] + case Cons(x, xs) => Set(x) ++ content(xs) + } + + def iplContent(l: IPList) : Set[(Int, Int)] = l match { + case IPNil() => Set.empty[(Int, Int)] + case IPCons(x, xs) => Set(x) ++ iplContent(xs) + } + + def size(l: List) : Int = { + var r = 0 + var l2 = l + (while(!l2.isInstanceOf[Nil]) { + val Cons(_, tail) = l2 + l2 = tail + r = r+1 + }) invariant(r >= 0) + r + } ensuring(res => res >= 0) + + def sizeBuggy(l: List) : Int = { + var r = -1 + var l2 = l + (while(!l2.isInstanceOf[Nil]) { + val Cons(_, tail) = l2 + l2 = tail + r = r+1 + }) invariant(r >= 0) + r + } ensuring(res => res >= 0) + + def sizeFun(l: List) : Int = l match { + case Nil() => 0 + case Cons(_, t) => 1 + sizeFun(t) + } + + def iplSizeFun(l: IPList) : Int = l match { + case IPNil() => 0 + case IPCons(_, t) => 1 + iplSizeFun(t) + } + + def iplSize(l: IPList) : Int = { + var r = 0 + var l2 = l + (while(!l2.isInstanceOf[IPNil]) { + val IPCons(_, tail) = l2 + l2 = tail + r = r+1 + }) invariant(r >= 0) + r + } ensuring(_ >= 0) + + def sizeImpEqFun(l: List): Boolean = (size(l) == sizeFun(l)) ensuring(_ == true) + + def zip(l1: List, l2: List) : IPList = ({ + require(sizeFun(l1) == sizeFun(l2)) + var r: IPList = IPNil() + var ll1: List = l1 + var ll2 = l2 + + (while(!ll1.isInstanceOf[Nil]) { + val Cons(l1head, l1tail) = ll1 + val Cons(l2head, l2tail) = if(!ll2.isInstanceOf[Nil]) ll2 else Cons(0, Nil()) + r = IPCons((l1head, l2head), r) + ll1 = l1tail + ll2 = l2tail + }) invariant(iplSizeFun(r) + sizeFun(ll1) == sizeFun(l1)) + + iplReverse(r) + }) ensuring(res => iplSizeFun(res) == sizeFun(l1)) + + def iplReverse(l: IPList): IPList = { + var r: IPList = IPNil() + var l2: IPList = l + + (while(!l2.isInstanceOf[IPNil]) { + val IPCons(head, tail) = l2 + l2 = tail + r = IPCons(head, r) + }) invariant(iplContent(r) ++ iplContent(l2) == iplContent(l) && iplSizeFun(r) + iplSizeFun(l2) == iplSizeFun(l)) + + r + } ensuring(res => iplContent(res) == iplContent(l) && iplSizeFun(res) == iplSizeFun(l)) + + def reverse(l: List): List = { + var r: List = Nil() + var l2: List = l + + (while(!l2.isInstanceOf[Nil]) { + val Cons(head, tail) = l2 + l2 = tail + r = Cons(head, r) + }) invariant(content(r) ++ content(l2) == content(l) && sizeFun(r) + sizeFun(l2) == sizeFun(l)) + + r + } ensuring(res => content(res) == content(l) && sizeFun(res) == sizeFun(l)) + + def listEqReverse(l: List): Boolean = { + l == reverse(l) + } ensuring(_ == true) + + def append(l1 : List, l2 : List) : List = { + var r: List = l2 + var tmp: List = reverse(l1) + + (while(!tmp.isInstanceOf[Nil]) { + val Cons(head, tail) = tmp + tmp = tail + r = Cons(head, r) + }) invariant(content(r) ++ content(tmp) == content(l1) ++ content(l2)) + + r + } ensuring(content(_) == content(l1) ++ content(l2)) + + def appendBuggy(l1 : List, l2 : List) : List = { + var r: List = l2 + var tmp: List = l1 + + while(!tmp.isInstanceOf[Nil]) { + val Cons(head, tail) = tmp + tmp = tail + r = Cons(head, r) + } + + r + } + + def appendEqAppendBuggy(l1: List, l2: List): Boolean = { + append(l1, l2) == appendBuggy(l1, l2) + } ensuring(_ == true) + +} diff --git a/testcases/regression/InfiniteLoop.scala b/testcases/regression/InfiniteLoop.scala new file mode 100644 index 0000000000000000000000000000000000000000..e9e5d0d5c7e258a77fdb418df3b51194fd6a9594 --- /dev/null +++ b/testcases/regression/InfiniteLoop.scala @@ -0,0 +1,12 @@ +object InfiniteLoop { + + def infinite(): Int = { + var i = 0 + var sum = 0 + while (i < 10) { + sum = sum + i + } + sum + } + +} diff --git a/testcases/regression/error/Array1.scala b/testcases/regression/error/Array1.scala new file mode 100644 index 0000000000000000000000000000000000000000..d0056f18864ba168abb5735daf0db1e0bb76eda5 --- /dev/null +++ b/testcases/regression/error/Array1.scala @@ -0,0 +1,8 @@ +object Array1 { + + def foo(): Int = { + (Array.fill(5)(5))(2) = 3 + 0 + } + +} diff --git a/testcases/regression/error/Array10.scala b/testcases/regression/error/Array10.scala new file mode 100644 index 0000000000000000000000000000000000000000..9973626b83a01c7a8a944bcf808274d7989b3a66 --- /dev/null +++ b/testcases/regression/error/Array10.scala @@ -0,0 +1,12 @@ +object Array10 { + + def foo(): Int = { + val a = Array.fill(5)(0) + def rec(): Array[Int] = { + a + } + val b = rec() + b(0) + } + +} diff --git a/testcases/regression/error/Array2.scala b/testcases/regression/error/Array2.scala new file mode 100644 index 0000000000000000000000000000000000000000..9154460c37237761b6c8f2d0ecb0b34fb3ec0bf6 --- /dev/null +++ b/testcases/regression/error/Array2.scala @@ -0,0 +1,9 @@ +object Array2 { + + def foo(): Int = { + val a = Array.fill(5)(5) + val b = a + b(3) + } + +} diff --git a/testcases/regression/error/Array3.scala b/testcases/regression/error/Array3.scala new file mode 100644 index 0000000000000000000000000000000000000000..a2c2fbd6ddfde2b3cca2dcc08fb61a79f0ae471c --- /dev/null +++ b/testcases/regression/error/Array3.scala @@ -0,0 +1,12 @@ +object Array3 { + + def foo(): Int = { + val a = Array.fill(5)(5) + if(a.length > 2) + a(1) = 2 + else + 0 + 0 + } + +} diff --git a/testcases/regression/error/Array4.scala b/testcases/regression/error/Array4.scala new file mode 100644 index 0000000000000000000000000000000000000000..5ab0115af5e8737c6baab851822643490e79334f --- /dev/null +++ b/testcases/regression/error/Array4.scala @@ -0,0 +1,8 @@ +object Array4 { + + def foo(a: Array[Int]): Int = { + val b = a + b(3) + } + +} diff --git a/testcases/regression/error/Array5.scala b/testcases/regression/error/Array5.scala new file mode 100644 index 0000000000000000000000000000000000000000..005d3d389dd1e748d139283a6fbdcb93aa8d525c --- /dev/null +++ b/testcases/regression/error/Array5.scala @@ -0,0 +1,10 @@ +object Array5 { + + def foo(a: Array[Int]): Int = { + a(2) = 4 + a(2) + } + +} + +// vim: set ts=4 sw=4 et: diff --git a/testcases/regression/error/Array6.scala b/testcases/regression/error/Array6.scala new file mode 100644 index 0000000000000000000000000000000000000000..15c87a08554c7e9563cbad9b47a881376e45a4c5 --- /dev/null +++ b/testcases/regression/error/Array6.scala @@ -0,0 +1,10 @@ + +object Array6 { + + def foo(): Int = { + val a = Array.fill(5)(5) + var b = a + b(0) + } + +} diff --git a/testcases/regression/error/Array7.scala b/testcases/regression/error/Array7.scala new file mode 100644 index 0000000000000000000000000000000000000000..abb83e1c0bd931bf1d41a132fef6fb74ea25318b --- /dev/null +++ b/testcases/regression/error/Array7.scala @@ -0,0 +1,9 @@ +object Array7 { + + def foo(): Int = { + val a = Array.fill(5)(5) + var b = a + b(0) + } + +} diff --git a/testcases/regression/error/Array8.scala b/testcases/regression/error/Array8.scala new file mode 100644 index 0000000000000000000000000000000000000000..89af32efdb89273ecf6b275b5a8f2f787bb73a1f --- /dev/null +++ b/testcases/regression/error/Array8.scala @@ -0,0 +1,7 @@ +object Array8 { + + def foo(a: Array[Int]): Array[Int] = { + a + } + +} diff --git a/testcases/regression/error/Array9.scala b/testcases/regression/error/Array9.scala new file mode 100644 index 0000000000000000000000000000000000000000..5dc9d3aea24f7c38b8a374659058c9d0ca7fb336 --- /dev/null +++ b/testcases/regression/error/Array9.scala @@ -0,0 +1,11 @@ +object Array9 { + + def foo(a: Array[Int]): Int = { + def rec(): Array[Int] = { + a + } + val b = rec() + b(0) + } + +} diff --git a/testcases/regression/invalid/Array1.scala b/testcases/regression/invalid/Array1.scala new file mode 100644 index 0000000000000000000000000000000000000000..a8451250c8f9e9c95684b4ea10490aced2a23f6e --- /dev/null +++ b/testcases/regression/invalid/Array1.scala @@ -0,0 +1,9 @@ +object Array1 { + + def foo(): Int = { + val a = Array.fill(5)(0) + a(2) = 3 + a(2) + } ensuring(_ == 0) + +} diff --git a/testcases/regression/invalid/Array2.scala b/testcases/regression/invalid/Array2.scala new file mode 100644 index 0000000000000000000000000000000000000000..54ab5e3c27918596af58c427fabedd2016a33db9 --- /dev/null +++ b/testcases/regression/invalid/Array2.scala @@ -0,0 +1,9 @@ +object Array2 { + + def foo(): Int = { + val a = Array.fill(5)(0) + a(2) = 3 + a.length + } ensuring(_ == 4) + +} diff --git a/testcases/regression/invalid/Array3.scala b/testcases/regression/invalid/Array3.scala new file mode 100644 index 0000000000000000000000000000000000000000..0c1bb76fdce099bd095c59d8c03fe8e6508a9776 --- /dev/null +++ b/testcases/regression/invalid/Array3.scala @@ -0,0 +1,16 @@ +import leon.Utils._ + +object Array3 { + + def foo(): Int = { + val a = Array.fill(5)(3) + var i = 0 + var sum = 0 + (while(i <= a.length) { + sum = sum + a(i) + i = i + 1 + }) invariant(i >= 0) + sum + } ensuring(_ == 15) + +} diff --git a/testcases/regression/invalid/Array4.scala b/testcases/regression/invalid/Array4.scala new file mode 100644 index 0000000000000000000000000000000000000000..5b5e7406165ad214a7d5d5fffd85d2d4fce226e0 --- /dev/null +++ b/testcases/regression/invalid/Array4.scala @@ -0,0 +1,9 @@ +import leon.Utils._ + +object Array4 { + + def foo(a: Array[Int]): Int = { + a(2) + } + +} diff --git a/testcases/regression/invalid/Array5.scala b/testcases/regression/invalid/Array5.scala new file mode 100644 index 0000000000000000000000000000000000000000..ecb00334971f3ecf19f3b6e6f9c25a5082f2d2d7 --- /dev/null +++ b/testcases/regression/invalid/Array5.scala @@ -0,0 +1,10 @@ +import leon.Utils._ + +object Array4 { + + def foo(a: Array[Int]): Int = { + require(a.length > 2) + a(2) + } ensuring(_ == 0) + +} diff --git a/testcases/regression/invalid/Epsilon1.scala b/testcases/regression/invalid/Epsilon1.scala index 859ced1c19b0c3688ad8eef433fbc290c672fbe9..c3c0a48be96f594fe725518a7289333bb587ef40 100644 --- a/testcases/regression/invalid/Epsilon1.scala +++ b/testcases/regression/invalid/Epsilon1.scala @@ -2,8 +2,11 @@ import leon.Utils._ object Epsilon1 { - def greaterWrong(x: Int): Int = { - epsilon((y: Int) => y >= x) - } ensuring(_ > x) + def rand2(x: Int): Int = epsilon((y: Int) => true) + + //this should not hold + def property2(x: Int): Boolean = { + rand2(x) == rand2(x+1) + } holds } diff --git a/testcases/regression/invalid/Epsilon4.scala b/testcases/regression/invalid/Epsilon4.scala index 52ee6dbe66c2d5b3517d5d84f90f35f8f10aa38d..6619e59d6f9d9721c4bc7587b47dac79f48bf437 100644 --- a/testcases/regression/invalid/Epsilon4.scala +++ b/testcases/regression/invalid/Epsilon4.scala @@ -23,5 +23,5 @@ object Epsilon4 { def wrongProperty0(lst: MyList): Boolean = (size(toList(toSet(lst))) == size(lst)) holds - def wrongProperty1(lst: MyList): Boolean = (toList(toSet(lst)) == lst) holds + //def wrongProperty1(lst: MyList): Boolean = (toList(toSet(lst)) == lst) holds } diff --git a/testcases/regression/invalid/Epsilon6.scala b/testcases/regression/invalid/Epsilon6.scala new file mode 100644 index 0000000000000000000000000000000000000000..bc5aca78c3818a94df43146aba16077ffab50a71 --- /dev/null +++ b/testcases/regression/invalid/Epsilon6.scala @@ -0,0 +1,9 @@ +import leon.Utils._ + +object Epsilon6 { + + def greaterWrong(x: Int): Int = { + epsilon((y: Int) => y >= x) + } ensuring(_ > x) + +} diff --git a/testcases/regression/invalid/MyTuple3.scala b/testcases/regression/invalid/MyTuple3.scala new file mode 100644 index 0000000000000000000000000000000000000000..da9f85b16a48f0676d1f15741ad47536e14b57ab --- /dev/null +++ b/testcases/regression/invalid/MyTuple3.scala @@ -0,0 +1,7 @@ +object MyTuple3 { + + def foo(t: (Int, Int)): (Int, Int) = { + t + } ensuring(res => res._1 > 0 && res._2 > 1) + +} diff --git a/testcases/regression/invalid/Unit1.scala b/testcases/regression/invalid/Unit1.scala new file mode 100644 index 0000000000000000000000000000000000000000..789a8f058cd8145a0dd3b7bb0d72d44fb92ee94e --- /dev/null +++ b/testcases/regression/invalid/Unit1.scala @@ -0,0 +1,7 @@ +object Unit1 { + + def foo(u: Unit): Unit = ({ + u + }) ensuring(_ != ()) + +} diff --git a/testcases/regression/valid/Array1.scala b/testcases/regression/valid/Array1.scala new file mode 100644 index 0000000000000000000000000000000000000000..3815f03443448a28fb60475b49c09c981a1377f9 --- /dev/null +++ b/testcases/regression/valid/Array1.scala @@ -0,0 +1,9 @@ +object Array1 { + + def foo(): Int = { + val a = Array.fill(5)(0) + a(2) = 3 + a(2) + } ensuring(_ == 3) + +} diff --git a/testcases/regression/valid/Array10.scala b/testcases/regression/valid/Array10.scala new file mode 100644 index 0000000000000000000000000000000000000000..ebb60ad6e0ba7556c55c74e1b1b624e6c34b4311 --- /dev/null +++ b/testcases/regression/valid/Array10.scala @@ -0,0 +1,9 @@ +object Array10 { + + def foo(a: Array[Int]): Int = { + require(a.length > 0) + val b = a.clone + b(0) + } ensuring(res => res == a(0)) + +} diff --git a/testcases/regression/valid/Array2.scala b/testcases/regression/valid/Array2.scala new file mode 100644 index 0000000000000000000000000000000000000000..4f149a9305cb4b53a4a9353b82e25468c898bdc8 --- /dev/null +++ b/testcases/regression/valid/Array2.scala @@ -0,0 +1,9 @@ +object Array2 { + + def foo(): Int = { + val a = Array.fill(5)(0) + a(2) = 3 + a.length + } ensuring(_ == 5) + +} diff --git a/testcases/regression/valid/Array3.scala b/testcases/regression/valid/Array3.scala new file mode 100644 index 0000000000000000000000000000000000000000..bbb1845b80c0ca747cb0c830828bacdc9668943f --- /dev/null +++ b/testcases/regression/valid/Array3.scala @@ -0,0 +1,16 @@ +import leon.Utils._ + +object Array3 { + + def foo(): Int = { + val a = Array.fill(5)(3) + var i = 0 + var sum = 0 + (while(i < a.length) { + sum = sum + a(i) + i = i + 1 + }) invariant(i >= 0) + sum + } ensuring(_ == 15) + +} diff --git a/testcases/regression/valid/Array4.scala b/testcases/regression/valid/Array4.scala new file mode 100644 index 0000000000000000000000000000000000000000..fd76e02faa83f8fd1bda5fef7c91e7df7fc29864 --- /dev/null +++ b/testcases/regression/valid/Array4.scala @@ -0,0 +1,15 @@ +import leon.Utils._ + +object Array4 { + + def foo(a: Array[Int]): Int = { + var i = 0 + var sum = 0 + (while(i < a.length) { + sum = sum + a(i) + i = i + 1 + }) invariant(i >= 0) + sum + } + +} diff --git a/testcases/regression/valid/Array5.scala b/testcases/regression/valid/Array5.scala new file mode 100644 index 0000000000000000000000000000000000000000..14bed6eff332db69600353001488c46a3e56aa5d --- /dev/null +++ b/testcases/regression/valid/Array5.scala @@ -0,0 +1,20 @@ +import leon.Utils._ + +object Array5 { + + def foo(a: Array[Int]): Int = { + var i = 0 + var sum = 0 + (while(i < a.length) { + sum = sum + a(i) + i = i + 1 + }) invariant(i >= 0) + sum + } + + def bar(): Int = { + val a = Array.fill(5)(5) + foo(a) + } + +} diff --git a/testcases/regression/valid/Array6.scala b/testcases/regression/valid/Array6.scala new file mode 100644 index 0000000000000000000000000000000000000000..bdd6b0c5d9bee06eec14b9f5917762c67af4751b --- /dev/null +++ b/testcases/regression/valid/Array6.scala @@ -0,0 +1,15 @@ +import leon.Utils._ + +object Array6 { + + def foo(a: Array[Int]): Int = { + require(a.length > 2 && a(2) == 5) + a(2) + } ensuring(_ == 5) + + def bar(): Int = { + val a = Array.fill(5)(5) + foo(a) + } + +} diff --git a/testcases/regression/valid/Array7.scala b/testcases/regression/valid/Array7.scala new file mode 100644 index 0000000000000000000000000000000000000000..55bbbb72908e2b192152162d82d2ed9fea67923b --- /dev/null +++ b/testcases/regression/valid/Array7.scala @@ -0,0 +1,17 @@ +import leon.Utils._ + +object Array7 { + + def foo(a: Array[Int]): Array[Int] = { + require(a.length > 0) + val a2 = Array.fill(a.length)(0) + var i = 0 + (while(i < a.length) { + a2(i) = a(i) + i = i + 1 + }) invariant(a.length == a2.length && i >= 0 && (if(i > 0) a2(0) == a(0) else true)) + a2 + } ensuring(_(0) == a(0)) + + +} diff --git a/testcases/regression/valid/Array8.scala b/testcases/regression/valid/Array8.scala new file mode 100644 index 0000000000000000000000000000000000000000..270b181220b55af1588f63e7953221ae71f9bde3 --- /dev/null +++ b/testcases/regression/valid/Array8.scala @@ -0,0 +1,8 @@ +object Array8 { + + def foo(a: Array[Int]): Array[Int] = { + require(a.length >= 2) + a.updated(1, 3) + } ensuring(res => res.length == a.length && res(1) == 3) + +} diff --git a/testcases/regression/valid/Array9.scala b/testcases/regression/valid/Array9.scala new file mode 100644 index 0000000000000000000000000000000000000000..f49333236abfaf63caa278405a89670b1c9c115d --- /dev/null +++ b/testcases/regression/valid/Array9.scala @@ -0,0 +1,15 @@ +object Array9 { + + def foo(i: Int): Array[Int] = { + require(i > 0) + val a = Array.fill(i)(0) + a + } ensuring(res => res.length == i) + + def bar(i: Int): Int = { + require(i > 0) + val b = foo(i) + b(0) + } + +} diff --git a/testcases/regression/valid/Epsilon2.scala b/testcases/regression/valid/Epsilon2.scala index b4f53f36bf3b5c76a7c2fad5a4f1cdfed12587a9..36e5268e2b1ccdbc710dc8d6410ce6968231c7d1 100644 --- a/testcases/regression/valid/Epsilon2.scala +++ b/testcases/regression/valid/Epsilon2.scala @@ -3,16 +3,11 @@ import leon.Utils._ object Epsilon1 { def rand(): Int = epsilon((x: Int) => true) - def rand2(x: Int): Int = epsilon((y: Int) => true) //this should hold, that is the expected semantic of our epsilon def property1(): Boolean = { rand() == rand() } holds - //this should hold, that is the expected semantic of our epsilon - def property2(x: Int): Boolean = { - rand2(x) == rand2(x+1) - } holds } diff --git a/testcases/regression/valid/Field1.scala b/testcases/regression/valid/Field1.scala new file mode 100644 index 0000000000000000000000000000000000000000..116557ab7b883d01a10168aeaf529d5300ee5f19 --- /dev/null +++ b/testcases/regression/valid/Field1.scala @@ -0,0 +1,11 @@ +object Field1 { + + abstract sealed class A + case class B(size: Int) extends A + + def foo(): Int = { + val b = B(3) + b.size + } ensuring(_ == 3) + +} diff --git a/testcases/regression/valid/Field2.scala b/testcases/regression/valid/Field2.scala new file mode 100644 index 0000000000000000000000000000000000000000..9a96610235a68754b84e58f73f5e435ce642ebc9 --- /dev/null +++ b/testcases/regression/valid/Field2.scala @@ -0,0 +1,11 @@ +object Field2 { + + abstract sealed class A + case class B(length: Int) extends A + + def foo(): Int = { + val b = B(3) + b.length + } ensuring(_ == 3) + +} diff --git a/testcases/regression/valid/IfExpr3.scala b/testcases/regression/valid/IfExpr3.scala new file mode 100644 index 0000000000000000000000000000000000000000..86d4e494a7ce8fd22024020bb9580b393af8377a --- /dev/null +++ b/testcases/regression/valid/IfExpr3.scala @@ -0,0 +1,19 @@ +object IfExpr1 { + + def foo(a: Int): Int = { + + if(a > 0) { + var a = 1 + var b = 2 + a = 3 + a + b + } else { + 5 + //var a = 3 + //var b = 1 + //b = b + 1 + //a + b + } + } ensuring(_ == 5) + +} diff --git a/testcases/regression/valid/IfExpr4.scala b/testcases/regression/valid/IfExpr4.scala new file mode 100644 index 0000000000000000000000000000000000000000..94b99fde39e6f7e10b1c898548d60592023ffbd6 --- /dev/null +++ b/testcases/regression/valid/IfExpr4.scala @@ -0,0 +1,18 @@ +object IfExpr4 { + + def foo(a: Int): Int = { + + if(a > 0) { + var a = 1 + var b = 2 + a = 3 + a + b + } else { + var a = 3 + var b = 1 + b = b + 1 + a + b + } + } ensuring(_ == 5) + +} diff --git a/testcases/regression/valid/MyTuple4.scala b/testcases/regression/valid/MyTuple4.scala index 4b87272e23f7d9f2a3c33c29b5d455b88f234ddb..6fcfed661beca0487da4b02db71677e2dbaac60e 100644 --- a/testcases/regression/valid/MyTuple4.scala +++ b/testcases/regression/valid/MyTuple4.scala @@ -1,5 +1,5 @@ -object MyTuple1 { +object MyTuple4 { abstract class A case class B(i: Int) extends A diff --git a/testcases/regression/valid/MyTuple6.scala b/testcases/regression/valid/MyTuple6.scala new file mode 100644 index 0000000000000000000000000000000000000000..a54710fbbe54d9532a5ab4d2ba9906a48ad37acc --- /dev/null +++ b/testcases/regression/valid/MyTuple6.scala @@ -0,0 +1,8 @@ +object MyTuple6 { + + def foo(t: (Int, Int)): (Int, Int) = { + require(t._1 > 0 && t._2 > 1) + t + } ensuring(res => res._1 > 0 && res._2 > 1) + +} diff --git a/testcases/regression/valid/Nested10.scala b/testcases/regression/valid/Nested10.scala new file mode 100644 index 0000000000000000000000000000000000000000..b3c4f865367ca84a52091e89b655ac6438dc65a2 --- /dev/null +++ b/testcases/regression/valid/Nested10.scala @@ -0,0 +1,14 @@ +object Nested10 { + + def foo(i: Int): Int = { + val n = 2 + def rec1(j: Int) = { + i + j + n + } + def rec2(j: Int) = { + rec1(j) + } + rec2(2) + } ensuring(i + 4 == _) + +} diff --git a/testcases/regression/valid/Nested11.scala b/testcases/regression/valid/Nested11.scala new file mode 100644 index 0000000000000000000000000000000000000000..0316fc5f2ba3497691bc48b1d573933ba2cea027 --- /dev/null +++ b/testcases/regression/valid/Nested11.scala @@ -0,0 +1,14 @@ +object Nested11 { + + abstract class A + case class B(b: Int) extends A + + def foo(i: Int): Int = { + val b: A = B(3) + def rec1(j: Int) = b match { + case B(b) => i + j + b + } + rec1(2) + } ensuring(i + 5 == _) + +} diff --git a/testcases/regression/valid/Nested12.scala b/testcases/regression/valid/Nested12.scala new file mode 100644 index 0000000000000000000000000000000000000000..05ac1569f630f1fc905a84f0550cbbaa42047906 --- /dev/null +++ b/testcases/regression/valid/Nested12.scala @@ -0,0 +1,17 @@ +object Nested12 { + + abstract class A + case class B(b: Int) extends A + + def foo(i: Int): Int = { + val b: A = B(3) + def rec1(a: A, j: Int, j2: Int) = a match { + case B(b) => i + j + j2 + b + } + def rec2(a: A, k: Int) = a match { + case B(b) => rec1(a, b, k) + } + rec2(b, 2) + } ensuring(i + 8 == _) + +} diff --git a/testcases/regression/valid/Nested13.scala b/testcases/regression/valid/Nested13.scala new file mode 100644 index 0000000000000000000000000000000000000000..ccb742494564433dffe096210fac07b37a663dfe --- /dev/null +++ b/testcases/regression/valid/Nested13.scala @@ -0,0 +1,21 @@ +object Nested13 { + + abstract class D + case class E(e: Int) extends D + case class F(d: D, f: Int) extends D + + def foo(a : Int): Int = { + + def rec1(d: D, j: Int): Int = d match { + case E(e) => j + e + a + case F(dNext, f) => f + rec1(dNext, j) + } + + def rec2(d: D, i : Int) : Int = d match { + case E(e) => e + case F(dNext, f) => rec1(d, i) + } + + rec2(F(E(2), 3), 0) + } ensuring(a + 5 == _) +} diff --git a/testcases/regression/valid/Nested14.scala b/testcases/regression/valid/Nested14.scala new file mode 100644 index 0000000000000000000000000000000000000000..9a79a4f6e4e7c28aa860d0636487a60a06e9e2ba --- /dev/null +++ b/testcases/regression/valid/Nested14.scala @@ -0,0 +1,11 @@ +object Nested14 { + + def foo(i: Int): Int = { + def rec1(j: Int): Int = { + def rec2(k: Int): Int = if(k == 0) 0 else rec1(j-1) + rec2(j) + } + rec1(3) + } ensuring(0 == _) + +} diff --git a/testcases/regression/valid/Nested2.scala b/testcases/regression/valid/Nested2.scala new file mode 100644 index 0000000000000000000000000000000000000000..cc7220b02109c394e71c127e4cd3f53d6714ed7f --- /dev/null +++ b/testcases/regression/valid/Nested2.scala @@ -0,0 +1,13 @@ +object Nested2 { + + def foo(a: Int): Int = { + require(a >= 0) + val b = a + 2 + def rec1(c: Int): Int = { + require(c >= 0) + b + c + } + rec1(2) + } ensuring(_ > 0) + +} diff --git a/testcases/regression/valid/Nested3.scala b/testcases/regression/valid/Nested3.scala new file mode 100644 index 0000000000000000000000000000000000000000..be6e6d04a335d12d079a9e3dc1a4b14ec0164014 --- /dev/null +++ b/testcases/regression/valid/Nested3.scala @@ -0,0 +1,15 @@ +object Nested3 { + + def foo(a: Int): Int = { + require(a >= 0 && a <= 50) + val b = a + 2 + val c = a + b + def rec1(d: Int): Int = { + require(d >= 0 && d <= 50) + val e = d + b + c + e + } + rec1(2) + } ensuring(_ > 0) + +} diff --git a/testcases/regression/valid/Nested4.scala b/testcases/regression/valid/Nested4.scala new file mode 100644 index 0000000000000000000000000000000000000000..ea1e6066c6f4b7aaf6df23065e3011b86471b7f4 --- /dev/null +++ b/testcases/regression/valid/Nested4.scala @@ -0,0 +1,19 @@ +object Nested4 { + + def foo(a: Int, a2: Int): Int = { + require(a >= 0 && a <= 50) + val b = a + 2 + val c = a + b + if(a2 > a) { + def rec1(d: Int): Int = { + require(d >= 0 && d <= 50) + val e = d + b + c + a2 + e + } ensuring(_ > 0) + rec1(2) + } else { + 5 + } + } ensuring(_ > 0) + +} diff --git a/testcases/regression/valid/Nested5.scala b/testcases/regression/valid/Nested5.scala new file mode 100644 index 0000000000000000000000000000000000000000..6ba128f415ecc3c61ebcccb42b65100d15373982 --- /dev/null +++ b/testcases/regression/valid/Nested5.scala @@ -0,0 +1,12 @@ +object Nested5 { + + def foo(a: Int): Int = { + require(a >= 0) + def bar(b: Int): Int = { + require(b > 0) + b + 3 + } ensuring(a >= 0 && _ == b + 3) + bar(2) + } ensuring(a >= 0 && _ == 5) + +} diff --git a/testcases/regression/valid/Nested6.scala b/testcases/regression/valid/Nested6.scala new file mode 100644 index 0000000000000000000000000000000000000000..1c0220c0447dba10ccd710d60e5f19b1c1ec6e18 --- /dev/null +++ b/testcases/regression/valid/Nested6.scala @@ -0,0 +1,14 @@ +object Nested5 { + + def foo(a: Int): Int = { + require(a >= 0) + def bar(b: Int): Int = { + require(b > 0) + b + 3 + } ensuring(prop(a, b) && a >= 0 && _ == b + 3) + bar(2) + } ensuring(a >= 0 && _ == 5) + + def prop(x: Int, y: Int): Boolean = x + y > 0 + +} diff --git a/testcases/regression/valid/Nested7.scala b/testcases/regression/valid/Nested7.scala new file mode 100644 index 0000000000000000000000000000000000000000..62f5a567f49be174873a24711e630f69e8469a22 --- /dev/null +++ b/testcases/regression/valid/Nested7.scala @@ -0,0 +1,19 @@ +object Nested2 { + + def foo(a: Int): Int = { + require(a >= 0) + val b = a + 2 + def rec1(c: Int): Int = { + require(c >= 0) + b + c + bar(a) + bar(b) + bar(c) + } + rec1(2) + bar(a) + } ensuring(_ > 0) + + + def bar(x: Int): Int = { + require(x >= 0) + x + } ensuring(_ >= 0) + +} diff --git a/testcases/regression/valid/Nested8.scala b/testcases/regression/valid/Nested8.scala new file mode 100644 index 0000000000000000000000000000000000000000..e8a05e40e6cd1b2428e03bcf1a9f96c67797c698 --- /dev/null +++ b/testcases/regression/valid/Nested8.scala @@ -0,0 +1,43 @@ +import leon.Utils._ + +object Nested8 { + + def foo(a: Int): Int = { + require(a > 0) + + def bar(b: Int): Int = { + if(a < b) { + def rec(c: Int): Int = { + require(c > 0) + c + b + } ensuring(_ > 0) + rec(2) + } else 3 + } + bar(1) + } ensuring(_ > 0) + + /* + def partitioned(a: Map[Int, Int], size: Int, l1: Int, u1: Int, l2: Int, u2: Int): Boolean = { + require(l1 >= 0 && u1 < l2 && u2 < size) + if(l2 > u2 || l1 > u1) + true + else { + var i = l1 + var j = l2 + var isPartitionned = true + (while(i <= u1) { + j = l2 + (while(j <= u2) { + if(a(i) > a(j)) + isPartitionned = false + j = j + 1 + }) invariant(j >= l2 && i <= u1) + i = i + 1 + }) invariant(i >= l1) + isPartitionned + } + } + */ + +} diff --git a/testcases/regression/valid/Nested9.scala b/testcases/regression/valid/Nested9.scala new file mode 100644 index 0000000000000000000000000000000000000000..3344a2c7973bed9da2c42208742c34c184b458ac --- /dev/null +++ b/testcases/regression/valid/Nested9.scala @@ -0,0 +1,23 @@ +object Nested4 { + + def foo(a: Int, a2: Int): Int = { + require(a >= 0 && a <= 50) + val b = a + 2 + val c = a + b + if(a2 > a) { + def rec1(d: Int): Int = { + require(d >= 0 && d <= 50) + val e = d + b + c + a2 + def rec2(f: Int): Int = { + require(f >= c) + f + e + } ensuring(_ > 0) + rec2(c+1) + } ensuring(_ > 0) + rec1(2) + } else { + 5 + } + } ensuring(_ > 0) + +} diff --git a/testcases/regression/valid/NestedVar.scala b/testcases/regression/valid/NestedVar.scala new file mode 100644 index 0000000000000000000000000000000000000000..a26b7312b2f5f85509c6e55cc706fb29845202b1 --- /dev/null +++ b/testcases/regression/valid/NestedVar.scala @@ -0,0 +1,17 @@ +object NestedVar { + + def foo(): Int = { + val a = 3 + def rec(x: Int): Int = { + var b = 3 + var c = 3 + if(x > 0) + b = 2 + else + c = 2 + c+b + } + rec(a) + } ensuring(_ == 5) + +} diff --git a/testcases/regression/valid/Unit1.scala b/testcases/regression/valid/Unit1.scala new file mode 100644 index 0000000000000000000000000000000000000000..a7b890d762648cba480c817506c7eee22259860d --- /dev/null +++ b/testcases/regression/valid/Unit1.scala @@ -0,0 +1,7 @@ +object Unit1 { + + def foo(): Unit = ({ + () + }) ensuring(_ == ()) + +} diff --git a/testcases/regression/valid/Unit2.scala b/testcases/regression/valid/Unit2.scala new file mode 100644 index 0000000000000000000000000000000000000000..ac659589af503a8a79f261f9eb05be095e2e0943 --- /dev/null +++ b/testcases/regression/valid/Unit2.scala @@ -0,0 +1,7 @@ +object Unit2 { + + def foo(u: Unit): Unit = { + u + } ensuring(_ == ()) + +} diff --git a/testcases/testgen/Abs.scala b/testcases/testgen/Abs.scala new file mode 100644 index 0000000000000000000000000000000000000000..4aa8307c8ec3da1d9ff941994b084d8590ce3afc --- /dev/null +++ b/testcases/testgen/Abs.scala @@ -0,0 +1,11 @@ +import leon.Utils._ +import leon.Annotations._ + +object Abs { + + @main + def abs(x: Int): Int = { + if(x < 0) -x else x + } ensuring(_ >= 0) + +} diff --git a/testcases/testgen/Abs2.scala b/testcases/testgen/Abs2.scala new file mode 100644 index 0000000000000000000000000000000000000000..d640f941bf299ce06e0e84e40e2a2f98c39a4c4c --- /dev/null +++ b/testcases/testgen/Abs2.scala @@ -0,0 +1,11 @@ +import leon.Utils._ +import leon.Annotations._ + +object Abs2 { + + @main + def f(x: Int): Int = if(x < 0) g(-x) else g(x) + + def g(y: Int): Int = if(y < 0) -y else y + +} diff --git a/testcases/testgen/Diamond.scala b/testcases/testgen/Diamond.scala new file mode 100644 index 0000000000000000000000000000000000000000..dbd354e94ffe82de38d7a83997fa1553189f7c0b --- /dev/null +++ b/testcases/testgen/Diamond.scala @@ -0,0 +1,9 @@ +import leon.Utils._ + +object Diamond { + + def foo(x: Int): Int = waypoint(1, if(x < 0) bar(x) else bar(x)) + + def bar(y: Int): Int = if(y > 5) y else -y + +} diff --git a/testcases/testgen/Imp.scala b/testcases/testgen/Imp.scala new file mode 100644 index 0000000000000000000000000000000000000000..8257e6619ca299a8e56fd3d78076c54f8b731882 --- /dev/null +++ b/testcases/testgen/Imp.scala @@ -0,0 +1,17 @@ +import leon.Utils._ +import leon.Annotations._ + +object Imp { + + @main + def foo(i: Int): Int = { + var a = 0 + a = a + 3 + if(i < a) + a = a + 1 + else + a = a - 1 + a + } ensuring(_ >= 0) + +} diff --git a/testcases/testgen/ImpWaypoint.scala b/testcases/testgen/ImpWaypoint.scala new file mode 100644 index 0000000000000000000000000000000000000000..932d362b6faada0d8acf684d96d6bcaada0cca21 --- /dev/null +++ b/testcases/testgen/ImpWaypoint.scala @@ -0,0 +1,20 @@ + +import leon.Utils._ +import leon.Annotations._ + +object Imp { + + @main + def foo(i: Int): Int = { + var a = 0 + a = a + 3 + if(i < a) + waypoint(1, a = a + 1) + else + a = a - 1 + a + } ensuring(_ >= 0) + +} + +// vim: set ts=4 sw=4 et: diff --git a/testcases/testgen/List.scala b/testcases/testgen/List.scala new file mode 100644 index 0000000000000000000000000000000000000000..b21928d924998d5dcd89ea2c278928c77aecf46a --- /dev/null +++ b/testcases/testgen/List.scala @@ -0,0 +1,20 @@ +import leon.Utils._ +import leon.Annotations._ + +object List { + + abstract class List + case class Cons(head: Int, tail: List) extends List + case class Nil() extends List + + @main + def size(l: List): Int = (l match { + case Cons(_, tail) => sizeTail(tail, 1) + case Nil() => 0 + }) ensuring(_ >= 0) + + def sizeTail(l2: List, acc: Int): Int = l2 match { + case Cons(_, tail) => sizeTail(tail, acc+1) + case Nil() => acc + } +} diff --git a/testcases/testgen/MultiCall.scala b/testcases/testgen/MultiCall.scala new file mode 100644 index 0000000000000000000000000000000000000000..6742cc4ca224b8bef044745dc6cd12d60683ac80 --- /dev/null +++ b/testcases/testgen/MultiCall.scala @@ -0,0 +1,15 @@ +import leon.Utils._ +import leon.Annotations._ + +object MultiCall { + + @main + def a(i: Int): Int = if(i < 0) b(i) else c(i) + + def b(j: Int): Int = if(j == -5) d(j) else e(j) + def c(k: Int): Int = if(k == 5) d(k) else e(k) + + def d(l: Int): Int = l + def e(m: Int): Int = m + +} diff --git a/testcases/testgen/Sum.scala b/testcases/testgen/Sum.scala new file mode 100644 index 0000000000000000000000000000000000000000..5f1c6c481f36d195f1fe6d58c591a5d91918871b --- /dev/null +++ b/testcases/testgen/Sum.scala @@ -0,0 +1,11 @@ +import leon.Utils._ +import leon.Annotations._ + +object Sum { + + @main + def sum(n: Int): Int = { + if(n <= 0) waypoint(4, 0) else waypoint(3, waypoint(2, n + sum(n-1))) + } ensuring(_ >= 0) + +}