diff --git a/library/lang/package.scala b/library/lang/package.scala index 44351d02ca9328bbbf86063745d233f2db17cd7c..bb7ec69122f4814bd61b1ce79f94c2eab9054c74 100644 --- a/library/lang/package.scala +++ b/library/lang/package.scala @@ -47,6 +47,9 @@ package object lang { @ignore def error[T](reason: java.lang.String): T = sys.error(reason) + @ignore + def old[T](value: T): T = value + @ignore implicit class Passes[A,B](io : (A,B)) { val (in, out) = io diff --git a/src/main/scala/leon/frontends/scalac/ASTExtractors.scala b/src/main/scala/leon/frontends/scalac/ASTExtractors.scala index db477ec494742b0dc2b0e42e098676c4ab55282d..fb64b3311dfdd2af5b06df85ab2cb26088d3233e 100644 --- a/src/main/scala/leon/frontends/scalac/ASTExtractors.scala +++ b/src/main/scala/leon/frontends/scalac/ASTExtractors.scala @@ -574,6 +574,15 @@ trait ASTExtractors { } } + object ExOldExpression { + def unapply(tree: Apply) : Option[Symbol] = tree match { + case a @ Apply(TypeApply(ExSymbol("leon", "lang", "old"), List(tpe)), List(arg)) => + Some(arg.symbol) + case _ => + None + } + } + object ExHoleExpression { def unapply(tree: Tree) : Option[(Tree, List[Tree])] = tree match { case a @ Apply(TypeApply(s @ ExSymbol("leon", "lang", "synthesis", "$qmark"), List(tpt)), args1) => diff --git a/src/main/scala/leon/frontends/scalac/CodeExtraction.scala b/src/main/scala/leon/frontends/scalac/CodeExtraction.scala index 77a6c719f8f4d0c06766982456b6a094545512ce..4bca0e76794e64a40f32b56679d4a502d0b1590c 100644 --- a/src/main/scala/leon/frontends/scalac/CodeExtraction.scala +++ b/src/main/scala/leon/frontends/scalac/CodeExtraction.scala @@ -1054,6 +1054,15 @@ trait CodeExtraction extends ASTExtractors { val tupleExprs = exprs.map(e => extractTree(e)) Tuple(tupleExprs) + case ex@ExOldExpression(sym) if dctx.isVariable(sym) => + dctx.vars.get(sym).orElse(dctx.mutableVars.get(sym)) match { + case Some(builder) => + val Variable(id) = builder() + Old(id).setPos(ex.pos) + case None => + outOfSubsetError(current, "old can only be used with variables") + } + case ExErrorExpression(str, tpt) => Error(extractType(tpt), str) @@ -1105,7 +1114,7 @@ trait CodeExtraction extends ASTExtractors { val oldCurrentFunDef = currentFunDef - val funDefWithBody = extractFunBody(fd, params, b)(newDctx.copy(mutableVars = Map())) + val funDefWithBody = extractFunBody(fd, params, b)(newDctx) currentFunDef = oldCurrentFunDef @@ -1200,11 +1209,11 @@ trait CodeExtraction extends ASTExtractors { } getOwner(lhsRec) match { - case Some(Some(fd)) if fd != currentFunDef => - outOfSubsetError(tr, "cannot update an array that is not defined locally") + // case Some(Some(fd)) if fd != currentFunDef => + // outOfSubsetError(tr, "cannot update an array that is not defined locally") - case Some(None) => - outOfSubsetError(tr, "cannot update an array that is not defined locally") + // case Some(None) => + // outOfSubsetError(tr, "cannot update an array that is not defined locally") case Some(_) => diff --git a/src/main/scala/leon/purescala/ExprOps.scala b/src/main/scala/leon/purescala/ExprOps.scala index c106d1393bcf793a7fe5d6944ecd533d84a42e36..c80d777480f66d795f2bc15eef0a3010452394fb 100644 --- a/src/main/scala/leon/purescala/ExprOps.scala +++ b/src/main/scala/leon/purescala/ExprOps.scala @@ -345,6 +345,7 @@ object ExprOps { val subvs = subs.flatten.toSet e match { case Variable(i) => subvs + i + case Old(i) => subvs + i case LetDef(fd, _) => subvs -- fd.params.map(_.id) case Let(i, _, _) => subvs - i case LetVar(i, _, _) => subvs - i diff --git a/src/main/scala/leon/purescala/Expressions.scala b/src/main/scala/leon/purescala/Expressions.scala index 6e9adae5f6ee3548b1fa70af41b127e6a46a76b4..1569ebfb8ab3d8c0ab7f0e09bbb31f82870fc8f9 100644 --- a/src/main/scala/leon/purescala/Expressions.scala +++ b/src/main/scala/leon/purescala/Expressions.scala @@ -76,6 +76,10 @@ object Expressions { val getType = tpe } + case class Old(id: Identifier) extends Expr with Terminal { + val getType = id.getType + } + /** Precondition of an [[Expressions.Expr]]. Corresponds to the Leon keyword *require* * * @param pred The precondition formula inside ``require(...)`` diff --git a/src/main/scala/leon/purescala/PrettyPrinter.scala b/src/main/scala/leon/purescala/PrettyPrinter.scala index 6b22810e542bdb9af65251da1c9a4d4ab1ab7142..3b96fff138dd7bc6a91c66acf3e3928fac0e44d0 100644 --- a/src/main/scala/leon/purescala/PrettyPrinter.scala +++ b/src/main/scala/leon/purescala/PrettyPrinter.scala @@ -77,6 +77,9 @@ class PrettyPrinter(opts: PrinterOptions, } p"$name" + case Old(id) => + p"old($id)" + case Variable(id) => p"$id" diff --git a/src/main/scala/leon/xlang/ImperativeCodeElimination.scala b/src/main/scala/leon/xlang/ImperativeCodeElimination.scala index fe9582d548deb679e0de7a082d5e4ba178582a1f..e9802d500208b96b5af448391d69c6660a05f843 100644 --- a/src/main/scala/leon/xlang/ImperativeCodeElimination.scala +++ b/src/main/scala/leon/xlang/ImperativeCodeElimination.scala @@ -10,6 +10,7 @@ import leon.purescala.Extractors._ import leon.purescala.Constructors._ import leon.purescala.ExprOps._ import leon.purescala.TypeOps._ +import leon.purescala.Types._ import leon.xlang.Expressions._ object ImperativeCodeElimination extends UnitPhase[Program] { @@ -22,13 +23,22 @@ object ImperativeCodeElimination extends UnitPhase[Program] { fd <- pgm.definedFunctions body <- fd.body } { - val (res, scope, _) = toFunction(body)(State(fd, Set())) + val (res, scope, _) = toFunction(body)(State(fd, Set(), Map())) fd.body = Some(scope(res)) } } - case class State(parent: FunDef, varsInScope: Set[Identifier]) { + /* varsInScope refers to variable declared in the same level scope. + Typically, when entering a nested function body, the scope should be + reset to empty */ + private case class State( + parent: FunDef, + varsInScope: Set[Identifier], + funDefsMapping: Map[FunDef, (FunDef, List[Identifier])] + ) { def withVar(i: Identifier) = copy(varsInScope = varsInScope + i) + def withFunDef(fd: FunDef, nfd: FunDef, ids: List[Identifier]) = + copy(funDefsMapping = funDefsMapping + (fd -> (nfd, ids))) } //return a "scope" consisting of purely functional code that defines potentially needed @@ -119,6 +129,7 @@ object ImperativeCodeElimination extends UnitPhase[Program] { (resId.toVariable, scope, scrutFun ++ modifiedVars.zip(freshIds).toMap) case wh@While(cond, body) => + //TODO: rewrite by re-using the nested function transformation code val (condRes, condScope, condFun) = toFunction(cond) val (_, bodyScope, bodyFun) = toFunction(body) val condBodyFun = condFun ++ bodyFun @@ -218,14 +229,115 @@ object ImperativeCodeElimination extends UnitPhase[Program] { bindFun ++ bodyFun ) + //a function invocation can update variables in scope. + case fi@FunctionInvocation(tfd, args) => + + + val (recArgs, argScope, argFun) = args.foldRight((Seq[Expr](), (body: Expr) => body, Map[Identifier, Identifier]()))((arg, acc) => { + val (accArgs, accScope, accFun) = acc + val (argVal, argScope, argFun) = toFunction(arg) + val newScope = (body: Expr) => argScope(replaceNames(argFun, accScope(body))) + (argVal +: accArgs, newScope, argFun ++ accFun) + }) + + val fd = tfd.fd + state.funDefsMapping.get(fd) match { + case Some((newFd, modifiedVars)) => { + val newInvoc = FunctionInvocation(newFd.typed, recArgs ++ modifiedVars.map(id => id.toVariable)).setPos(fi) + val freshNames = modifiedVars.map(id => id.freshen) + val tmpTuple = FreshIdentifier("t", newFd.returnType) + + val scope = (body: Expr) => { + argScope(Let(tmpTuple, newInvoc, + freshNames.zipWithIndex.foldRight(body)((p, b) => + Let(p._1, TupleSelect(tmpTuple.toVariable, p._2 + 2), b)) + )) + } + val newMap = argFun ++ modifiedVars.zip(freshNames).toMap + + (TupleSelect(tmpTuple.toVariable, 1), scope, newMap) + } + case None => + (FunctionInvocation(tfd, recArgs).copiedFrom(fi), argScope, argFun) + } + + case LetDef(fd, b) => - //Recall that here the nested function should not access mutable variables from an outside scope - fd.body.foreach { bd => - val (fdRes, fdScope, _) = toFunction(bd) - fd.body = Some(fdScope(fdRes)) + + def fdWithoutSideEffects = { + fd.body.foreach { bd => + val (fdRes, fdScope, _) = toFunction(bd) + fd.body = Some(fdScope(fdRes)) + } + val (bodyRes, bodyScope, bodyFun) = toFunction(b) + (bodyRes, (b2: Expr) => LetDef(fd, bodyScope(b2)).setPos(fd).copiedFrom(expr), bodyFun) + } + + fd.body match { + case Some(bd) => { + + val modifiedVars: List[Identifier] = + collect[Identifier]({ + case Assignment(v, _) => Set(v) + case _ => Set() + })(bd).intersect(state.varsInScope).toList + + if(modifiedVars.isEmpty) fdWithoutSideEffects else { + + val freshNames: List[Identifier] = modifiedVars.map(id => id.freshen) + + val newParams: Seq[ValDef] = fd.params ++ freshNames.map(n => ValDef(n)) + val freshVarDecls: List[Identifier] = freshNames.map(id => id.freshen) + + val rewritingMap: Map[Identifier, Identifier] = + modifiedVars.zip(freshVarDecls).toMap + val freshBody = + preMap({ + case Assignment(v, e) => rewritingMap.get(v).map(nv => Assignment(nv, e)) + case Variable(id) => rewritingMap.get(id).map(nid => Variable(nid)) + case _ => None + })(bd) + val wrappedBody = freshNames.zip(freshVarDecls).foldLeft(freshBody)((body, p) => { + LetVar(p._2, Variable(p._1), body) + }) + + val newReturnType = TupleType(fd.returnType :: modifiedVars.map(_.getType)) + + val newFd = new FunDef(fd.id.freshen, fd.tparams, newParams, newReturnType).setPos(fd) + + val (fdRes, fdScope, fdFun) = + toFunction(wrappedBody)( + State(state.parent, Set(), + state.funDefsMapping + (fd -> ((newFd, freshVarDecls)))) + ) + val newRes = Tuple(fdRes :: freshVarDecls.map(vd => fdFun(vd).toVariable)) + val newBody = fdScope(newRes) + + newFd.body = Some(newBody) + newFd.precondition = fd.precondition.map(prec => { + replace(modifiedVars.zip(freshNames).map(p => (p._1.toVariable, p._2.toVariable)).toMap, prec) + }) + newFd.postcondition = fd.postcondition.map(post => { + val Lambda(Seq(res), postBody) = post + val newRes = ValDef(FreshIdentifier(res.id.name, newFd.returnType)) + + val newBody = + replace( + modifiedVars.zipWithIndex.map{ case (v, i) => + (v.toVariable, TupleSelect(newRes.toVariable, i+2)): (Expr, Expr)}.toMap ++ + modifiedVars.zip(freshNames).map{ case (ov, nv) => + (Old(ov), nv.toVariable)}.toMap + + (res.toVariable -> TupleSelect(newRes.toVariable, 1)), + postBody) + Lambda(Seq(newRes), newBody).setPos(post) + }) + + val (bodyRes, bodyScope, bodyFun) = toFunction(b)(state.withFunDef(fd, newFd, modifiedVars)) + (bodyRes, (b2: Expr) => LetDef(newFd, bodyScope(b2)).copiedFrom(expr), bodyFun) + } + } + case None => fdWithoutSideEffects } - val (bodyRes, bodyScope, bodyFun) = toFunction(b) - (bodyRes, (b2: Expr) => LetDef(fd, bodyScope(b2)).copiedFrom(expr), bodyFun) case c @ Choose(b) => //Recall that Choose cannot mutate variables from the scope diff --git a/src/test/resources/regression/verification/xlang/invalid/NestedFunState1.scala b/src/test/resources/regression/verification/xlang/invalid/NestedFunState1.scala new file mode 100644 index 0000000000000000000000000000000000000000..9372ea742a278a3da3d476dca57e52c55c09b978 --- /dev/null +++ b/src/test/resources/regression/verification/xlang/invalid/NestedFunState1.scala @@ -0,0 +1,20 @@ +object NestedFunState1 { + + def simpleSideEffect(n: BigInt): BigInt = { + require(n > 0) + + var a = BigInt(0) + + def incA(prevA: BigInt): Unit = { + require(prevA == a) + a += 1 + } ensuring(_ => a == prevA + 1) + + incA(a) + incA(a) + incA(a) + incA(a) + a + } ensuring(_ == 5) + +} diff --git a/src/test/resources/regression/verification/xlang/invalid/NestedFunState2.scala b/src/test/resources/regression/verification/xlang/invalid/NestedFunState2.scala new file mode 100644 index 0000000000000000000000000000000000000000..68769df28353c9defa6d33000bb8ae4de21c7ace --- /dev/null +++ b/src/test/resources/regression/verification/xlang/invalid/NestedFunState2.scala @@ -0,0 +1,23 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +object NestedFunState2 { + + def sum(n: BigInt): BigInt = { + require(n > 0) + var i = BigInt(0) + var res = BigInt(0) + + def iter(): Unit = { + require(res >= i && i >= 0) + if(i < n) { + i += 1 + res += i + iter() + } + } + + iter() + res + } ensuring(_ < 0) + +} diff --git a/src/test/resources/regression/verification/xlang/valid/ArrayNested1.scala b/src/test/resources/regression/verification/xlang/valid/ArrayNested1.scala new file mode 100644 index 0000000000000000000000000000000000000000..196a6442bd544a3981e6a7de7e923e040cadd32d --- /dev/null +++ b/src/test/resources/regression/verification/xlang/valid/ArrayNested1.scala @@ -0,0 +1,19 @@ +import leon.lang._ + +object ArrayNested1 { + + def test(): Int = { + + var a = Array(1, 2, 0) + + def nested(): Unit = { + require(a.length == 3) + a = a.updated(1, 5) + } + + nested() + a(1) + + } ensuring(_ == 5) + +} diff --git a/src/test/resources/regression/verification/xlang/valid/ArrayNested2.scala b/src/test/resources/regression/verification/xlang/valid/ArrayNested2.scala new file mode 100644 index 0000000000000000000000000000000000000000..a8935ab8c1d4eec6980b85f171e695071f2a9442 --- /dev/null +++ b/src/test/resources/regression/verification/xlang/valid/ArrayNested2.scala @@ -0,0 +1,19 @@ +import leon.lang._ + +object ArrayNested2 { + + def test(): Int = { + + val a = Array(1, 2, 0) + + def nested(): Unit = { + require(a.length == 3) + a(2) = 5 + } + + nested() + a(2) + + } ensuring(_ == 5) + +} diff --git a/src/test/resources/regression/verification/xlang/valid/FunInvocEvaluationOrder1.scala b/src/test/resources/regression/verification/xlang/valid/FunInvocEvaluationOrder1.scala new file mode 100644 index 0000000000000000000000000000000000000000..8ef668a2afeb61a6b305831358a65e675bf670ed --- /dev/null +++ b/src/test/resources/regression/verification/xlang/valid/FunInvocEvaluationOrder1.scala @@ -0,0 +1,22 @@ +object FunInvocEvaluationOrder1 { + + def test(): Int = { + + var res = 10 + justAddingStuff({ + res += 1 + res + }, { + res *= 2 + res + }, { + res += 10 + res + }) + + res + } ensuring(_ == 32) + + def justAddingStuff(x: Int, y: Int, z: Int): Int = x + y + z + +} diff --git a/src/test/resources/regression/verification/xlang/valid/FunInvocEvaluationOrder2.scala b/src/test/resources/regression/verification/xlang/valid/FunInvocEvaluationOrder2.scala new file mode 100644 index 0000000000000000000000000000000000000000..12090f0f3fad0a5fcf2451e30fdb6f25bb09c590 --- /dev/null +++ b/src/test/resources/regression/verification/xlang/valid/FunInvocEvaluationOrder2.scala @@ -0,0 +1,17 @@ +object FunInvocEvaluationOrder2 { + + def leftToRight(n: BigInt): BigInt = { + require(n > 0) + + var a = BigInt(0) + + def nested(x: BigInt, y: BigInt): BigInt = { + require(y >= x) + x + y + } + + nested({a += 10; a}, {a *= 2; a}) + + } ensuring(_ == 30) + +} diff --git a/src/test/resources/regression/verification/xlang/valid/FunInvocEvaluationOrder3.scala b/src/test/resources/regression/verification/xlang/valid/FunInvocEvaluationOrder3.scala new file mode 100644 index 0000000000000000000000000000000000000000..f263529653486ee5477b9fd1934760bcdc737d15 --- /dev/null +++ b/src/test/resources/regression/verification/xlang/valid/FunInvocEvaluationOrder3.scala @@ -0,0 +1,17 @@ +object FunInvocEvaluationOrder3 { + + def leftToRight(n: BigInt): BigInt = { + require(n > 0) + + var a = BigInt(0) + + def nested(x: BigInt, y: BigInt): Unit = { + a = x + y + } + + nested({a += 10; a}, {a *= 2; a}) + a + + } ensuring(_ == 30) + +} diff --git a/src/test/resources/regression/verification/xlang/valid/NestedFunState1.scala b/src/test/resources/regression/verification/xlang/valid/NestedFunState1.scala new file mode 100644 index 0000000000000000000000000000000000000000..1ec0266f846dd55cfa1a6274d5c6dc7dc67dd125 --- /dev/null +++ b/src/test/resources/regression/verification/xlang/valid/NestedFunState1.scala @@ -0,0 +1,23 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +object NestedFunState1 { + + def sum(n: BigInt): BigInt = { + require(n > 0) + var i = BigInt(0) + var res = BigInt(0) + + def iter(): Unit = { + require(res >= i && i >= 0) + if(i < n) { + i += 1 + res += i + iter() + } + } ensuring(_ => res >= n) + + iter() + res + } ensuring(_ >= n) + +} diff --git a/src/test/resources/regression/verification/xlang/valid/NestedFunState2.scala b/src/test/resources/regression/verification/xlang/valid/NestedFunState2.scala new file mode 100644 index 0000000000000000000000000000000000000000..e48b1b959b3a83262329cf48c63370a2b591cd41 --- /dev/null +++ b/src/test/resources/regression/verification/xlang/valid/NestedFunState2.scala @@ -0,0 +1,19 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +object NestedFunState2 { + + def countConst(): Int = { + + var counter = 0 + + def inc(): Unit = { + counter += 1 + } + + inc() + inc() + inc() + counter + } ensuring(_ == 3) + +} diff --git a/src/test/resources/regression/verification/xlang/valid/NestedFunState3.scala b/src/test/resources/regression/verification/xlang/valid/NestedFunState3.scala new file mode 100644 index 0000000000000000000000000000000000000000..650f5987dcd352a3f5c47d41c684e94d4b9be20c --- /dev/null +++ b/src/test/resources/regression/verification/xlang/valid/NestedFunState3.scala @@ -0,0 +1,25 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ +import leon.lang._ + +object NestedFunState3 { + + + def counterN(n: Int): Int = { + require(n > 0) + + var counter = 0 + + def inc(): Unit = { + counter += 1 + } + + var i = 0 + (while(i < n) { + inc() + i += 1 + }) invariant(i >= 0 && counter == i && i <= n) + + counter + } ensuring(_ == n) + +} diff --git a/src/test/resources/regression/verification/xlang/valid/NestedFunState4.scala b/src/test/resources/regression/verification/xlang/valid/NestedFunState4.scala new file mode 100644 index 0000000000000000000000000000000000000000..9f4fb2621f60c60da3a677dc732e6c93f4d8ae72 --- /dev/null +++ b/src/test/resources/regression/verification/xlang/valid/NestedFunState4.scala @@ -0,0 +1,38 @@ +import leon.lang._ + +object NestedFunState4 { + + def deep(n: BigInt): BigInt = { + require(n > 0) + + var a = BigInt(0) + + def iter(): Unit = { + require(a >= 0) + + var b = BigInt(0) + + def nestedIter(): Unit = { + b += 1 + } + + var i = BigInt(0) + (while(i < n) { + i += 1 + nestedIter() + }) invariant(i >= 0 && i <= n && b == i) + + a += b + + } ensuring(_ => a >= n) + + var i = BigInt(0) + (while(i < n) { + i += 1 + iter() + }) invariant(i >= 0 && i <= n && a >= i) + + a + } ensuring(_ >= n) + +} diff --git a/src/test/resources/regression/verification/xlang/valid/NestedFunState5.scala b/src/test/resources/regression/verification/xlang/valid/NestedFunState5.scala new file mode 100644 index 0000000000000000000000000000000000000000..13f3cf47cd0ee6c831f12877493cbfe6c7edea5a --- /dev/null +++ b/src/test/resources/regression/verification/xlang/valid/NestedFunState5.scala @@ -0,0 +1,29 @@ +import leon.lang._ + +object NestedFunState5 { + + def deep(n: BigInt): BigInt = { + require(n > 0) + + var a = BigInt(0) + + def iter(prevA: BigInt): Unit = { + require(prevA == a) + def nestedIter(): Unit = { + a += 1 + } + + nestedIter() + nestedIter() + + } ensuring(_ => a == prevA + 2) + + var i = BigInt(0) + (while(i < n) { + i += 1 + iter(a) + }) invariant(i >= 0 && i <= n && a >= 2*i) + + a + } ensuring(_ >= 2*n) +} diff --git a/src/test/resources/regression/verification/xlang/valid/NestedFunState6.scala b/src/test/resources/regression/verification/xlang/valid/NestedFunState6.scala new file mode 100644 index 0000000000000000000000000000000000000000..cea0f2e1900a00bc803df4107b46dd62cf082c68 --- /dev/null +++ b/src/test/resources/regression/verification/xlang/valid/NestedFunState6.scala @@ -0,0 +1,20 @@ +object NestedFunState6 { + + def simpleSideEffect(n: BigInt): BigInt = { + require(n > 0) + + var a = BigInt(0) + + def incA(prevA: BigInt): Unit = { + require(prevA == a) + a += 1 + } ensuring(_ => a == prevA + 1) + + incA(a) + incA(a) + incA(a) + incA(a) + a + } ensuring(_ == 4) + +} diff --git a/src/test/resources/regression/verification/xlang/valid/NestedFunState7.scala b/src/test/resources/regression/verification/xlang/valid/NestedFunState7.scala new file mode 100644 index 0000000000000000000000000000000000000000..ff2418a0c972add2d48c08f6319905f35d0493c2 --- /dev/null +++ b/src/test/resources/regression/verification/xlang/valid/NestedFunState7.scala @@ -0,0 +1,27 @@ +import leon.lang._ + +object NestedFunState7 { + + def test(x: BigInt): BigInt = { + + var a = BigInt(0) + + def defCase(): Unit = { + a = 100 + } + + if(x >= 0) { + a = 2*x + if(a < 100) { + a = 100 - a + } else { + defCase() + } + } else { + defCase() + } + + a + } ensuring(res => res >= 0 && res <= 100) + +} diff --git a/src/test/resources/regression/verification/xlang/valid/NestedOld1.scala b/src/test/resources/regression/verification/xlang/valid/NestedOld1.scala new file mode 100644 index 0000000000000000000000000000000000000000..903dfd63aca5105c9366c459552865646139033e --- /dev/null +++ b/src/test/resources/regression/verification/xlang/valid/NestedOld1.scala @@ -0,0 +1,16 @@ +import leon.lang._ + +object NestedOld1 { + + def test(): Int = { + var counter = 0 + + def inc(): Unit = { + counter += 1 + } ensuring(_ => counter == old(counter) + 1) + + inc() + counter + } ensuring(_ == 1) + +} diff --git a/src/test/resources/regression/verification/xlang/valid/NestedOld2.scala b/src/test/resources/regression/verification/xlang/valid/NestedOld2.scala new file mode 100644 index 0000000000000000000000000000000000000000..fe6143ecf623c295251ca2b9d75d6474d422fd4f --- /dev/null +++ b/src/test/resources/regression/verification/xlang/valid/NestedOld2.scala @@ -0,0 +1,24 @@ +import leon.lang._ + +object NestedOld2 { + + def test(): Int = { + var counterPrev = 0 + var counterNext = 1 + + def step(): Unit = { + require(counterNext == counterPrev + 1) + counterPrev = counterNext + counterNext = counterNext+1 + } ensuring(_ => { + counterPrev == old(counterNext) && + counterNext == old(counterNext) + 1 && + counterPrev == old(counterPrev) + 1 + }) + + step() + step() + counterNext + } ensuring(_ == 3) + +}