diff --git a/src/main/scala/leon/FunctionClosure.scala b/src/main/scala/leon/FunctionClosure.scala index d9426510ee76f9703514df4219cff8761db26995..bb0fe741cefc67ff699cd53fa3e79d904a44c2b0 100644 --- a/src/main/scala/leon/FunctionClosure.scala +++ b/src/main/scala/leon/FunctionClosure.scala @@ -9,16 +9,14 @@ 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() 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)) @@ -28,58 +26,66 @@ object FunctionClosure extends Pass { private def functionClosure(expr: Expr, bindedVars: Set[Identifier]): 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 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 capturedLets = enclosingLets.filter(let => capturedVars.contains(let._1)) + //val (constraints, allCapturedVars) = filterConstraints(capturedVars) //all relevant path constraints + //val capturedVarsWithConstraints = allCapturedVars.toSeq + + + /* let's just take everything for now */ + val capturedVars = bindedVars.toSeq + val capturedLets = enclosingLets + val capturedConstraints = pathConstraints + + val freshIds: Map[Identifier, Identifier] = capturedVars.map(v => (v, FreshIdentifier(v.name).setType(v.getType))).toMap + val freshVars: Map[Expr, Expr] = freshIds.map(p => (p._1.toVariable, p._2.toVariable)) + + val extraVarDeclIds = capturedVars.toSet.diff(capturedLets.map(p => p._1).toSet).toSeq + val extraVarDecls = extraVarDeclIds.map(id => VarDecl(freshIds(id), id.getType)) + val newVarDecls = fd.args ++ extraVarDecls + val newFunId = FreshIdentifier(fd.id.name) + val newFunDef = new FunDef(newFunId, fd.returnType, newVarDecls).setPosInfo(fd) 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)) + val freshPrecondition = fd.precondition.map(expr => replace(freshVars, expr)) + val freshPostcondition = fd.postcondition.map(expr => replace(freshVars, expr)) + val freshBody = fd.body.map(expr => replace(freshVars, expr)) + val freshConstraints = capturedConstraints.map(expr => replace(freshVars, expr)) + val freshLets = capturedLets.map{ case (i, v) => (freshIds(i), replace(freshVars, v)) } 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 fi@FunctionInvocation(fd2, args) if fd2.id == fd.id => Some(FunctionInvocation(newFunDef, args ++ extraVarDeclIds.map(_.toVariable)).setPosInfo(fi)) case _ => None } 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 + val oldEnclosingLets = enclosingLets + pathConstraints = (fd.precondition.getOrElse(BooleanLiteral(true)) :: pathConstraints).map(e => replace(freshVars, e)) + enclosingLets = enclosingLets.map{ case (i, v) => (freshIds(i), replace(freshVars, v)) } + + val recPrecondition = freshConstraints match { 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)) + val finalBody = recBody.map(b => freshLets.foldLeft(b){ case (bacc, (i, v)) => Let(i, v, bacc) }) + pathConstraints = oldPathConstraints + enclosingLets = oldEnclosingLets newFunDef.precondition = recPrecondition - newFunDef.body = recBody + newFunDef.body = finalBody newFunDef.postcondition = freshPostcondition 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 fi@FunctionInvocation(fd2, args) if fd2.id == fd.id => Some(FunctionInvocation(newFunDef, args ++ extraVarDeclIds.map(_.toVariable)).setPosInfo(fi)) case _ => None } val recRest = searchAndReplaceDFS(substFunInvocInRest)(functionClosure(rest, bindedVars)) @@ -87,13 +93,22 @@ object FunctionClosure extends Pass { } case l @ Let(i,e,b) => { val re = functionClosure(e, bindedVars) - pathConstraints ::= Equals(Variable(i), re) + enclosingLets ::= (i, re) val rb = functionClosure(b, bindedVars + i) - pathConstraints = pathConstraints.tail + enclosingLets = enclosingLets.tail Let(i, re, rb).setType(l.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 n @ NAryOperator(args, recons) => { - var change = false val rargs = args.map(a => functionClosure(a, bindedVars)) recons(rargs).setType(n.getType) } @@ -106,16 +121,6 @@ object FunctionClosure extends Pass { val r = functionClosure(t, bindedVars) 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) => { //TODO: will not work if there are actual nested function in cases //val rScrut = functionClosure(scrut, bindedVars) m diff --git a/testcases/BubbleSort.scala b/testcases/BubbleSort.scala index 4e274e2707b0cff262e712ab4b78b53eee0db990..46ae7c5e61320a59672c8893d7483d4d20452e97 100644 --- a/testcases/BubbleSort.scala +++ b/testcases/BubbleSort.scala @@ -2,44 +2,42 @@ 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 >= 5) + var i = a.length - 1 var j = 0 - var sortedArray = a + val sa = a (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 + } else 0 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 && + 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 && + 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 +47,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 +69,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/InfiniteLoop.scala b/testcases/regression/InfiniteLoop.scala similarity index 100% rename from testcases/InfiniteLoop.scala rename to testcases/regression/InfiniteLoop.scala 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) + +}