diff --git a/src/main/scala/leon/frontends/scalac/CodeExtraction.scala b/src/main/scala/leon/frontends/scalac/CodeExtraction.scala index 96215b68b268752f7534a2b9fe0dc23db1d38cbf..c5877e1e4d3705c67ebe51eb97c57daf8da58b33 100644 --- a/src/main/scala/leon/frontends/scalac/CodeExtraction.scala +++ b/src/main/scala/leon/frontends/scalac/CodeExtraction.scala @@ -137,10 +137,6 @@ trait CodeExtraction extends ASTExtractors { private var currentFunDef: FunDef = null - //This is a bit misleading, 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[Identifier, Option[FunDef]] = Map() - // This one never fails, on error, it returns Untyped def leonType(tpt: Type)(implicit dctx: DefContext, pos: Position): LeonType = { try { @@ -639,7 +635,6 @@ trait CodeExtraction extends ASTExtractors { val ptpe = leonType(sym.tpe)(nctx, sym.pos) val tpe = if (sym.isByNameParam) FunctionType(Seq(), ptpe) else ptpe val newID = FreshIdentifier(sym.name.toString, tpe).setPos(sym.pos) - owners += (newID -> None) val vd = LeonValDef(newID).setPos(sym.pos) if (sym.isByNameParam) { @@ -798,21 +793,7 @@ trait CodeExtraction extends ASTExtractors { }} else body0 val finalBody = try { - flattenBlocks(extractTreeOrNoTree(body)(fctx)) match { - case e if e.getType.isInstanceOf[ArrayType] => - getOwner(e) match { - case Some(Some(fd)) if fd == funDef => - e - - case None => - e - - case _ => - outOfSubsetError(body, "Function cannot return an array that is not locally defined") - } - case e => - e - } + flattenBlocks(extractTreeOrNoTree(body)(fctx)) } catch { case e: ImpureCodeEncounteredException => e.emit() @@ -1090,15 +1071,6 @@ trait CodeExtraction extends ASTExtractors { val newID = FreshIdentifier(vs.name.toString, binderTpe) val valTree = extractTree(bdy) - if(valTree.getType.isInstanceOf[ArrayType]) { - getOwner(valTree) match { - case None => - owners += (newID -> Some(currentFunDef)) - case _ => - outOfSubsetError(tr, "Cannot alias array") - } - } - val restTree = rest match { case Some(rst) => val nctx = dctx.withNewVar(vs -> (() => Variable(newID))) @@ -1151,15 +1123,6 @@ trait CodeExtraction extends ASTExtractors { val newID = FreshIdentifier(vs.name.toString, binderTpe) val valTree = extractTree(bdy) - if(valTree.getType.isInstanceOf[ArrayType]) { - getOwner(valTree) match { - case None => - owners += (newID -> Some(currentFunDef)) - case Some(_) => - outOfSubsetError(tr, "Cannot alias array") - } - } - val restTree = rest match { case Some(rst) => { val nv = vs -> (() => Variable(newID)) @@ -1178,9 +1141,6 @@ trait CodeExtraction extends ASTExtractors { case Some(fun) => val Variable(id) = fun() val rhsTree = extractTree(rhs) - if(rhsTree.getType.isInstanceOf[ArrayType] && getOwner(rhsTree).isDefined) { - outOfSubsetError(tr, "Cannot alias array") - } Assignment(id, rhsTree) case None => @@ -1223,18 +1183,6 @@ trait CodeExtraction extends ASTExtractors { outOfSubsetError(tr, "Array update only works on variables") } - getOwner(lhsRec) match { - // 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(_) => - - case None => sys.error("This array: " + lhsRec + " should have had an owner") - } - val indexRec = extractTree(index) val newValueRec = extractTree(newValue) ArrayUpdate(lhsRec, indexRec, newValueRec) @@ -1309,7 +1257,6 @@ trait CodeExtraction extends ASTExtractors { val aTpe = extractType(tpt) val oTpe = oracleType(ops.pos, aTpe) val newID = FreshIdentifier(sym.name.toString, oTpe) - owners += (newID -> None) newID } @@ -1331,7 +1278,6 @@ trait CodeExtraction extends ASTExtractors { val vds = args map { vd => val aTpe = extractType(vd.tpt) val newID = FreshIdentifier(vd.symbol.name.toString, aTpe) - owners += (newID -> None) LeonValDef(newID) } @@ -1347,7 +1293,6 @@ trait CodeExtraction extends ASTExtractors { val vds = args map { case (tpt, sym) => val aTpe = extractType(tpt) val newID = FreshIdentifier(sym.name.toString, aTpe) - owners += (newID -> None) LeonValDef(newID) } @@ -1908,34 +1853,6 @@ trait CodeExtraction extends ASTExtractors { } } - private def getReturnedExpr(expr: LeonExpr): Seq[LeonExpr] = expr match { - case Let(_, _, rest) => getReturnedExpr(rest) - case LetVar(_, _, rest) => getReturnedExpr(rest) - case LeonBlock(_, rest) => getReturnedExpr(rest) - case IfExpr(_, thenn, elze) => getReturnedExpr(thenn) ++ getReturnedExpr(elze) - case MatchExpr(_, cses) => cses.flatMap{ cse => getReturnedExpr(cse.rhs) } - case _ => Seq(expr) - } - - def getOwner(exprs: Seq[LeonExpr]): Option[Option[FunDef]] = { - val exprOwners: Seq[Option[Option[FunDef]]] = exprs.map { - case Variable(id) => - owners.get(id) - case _ => - None - } - - if(exprOwners.contains(None)) - None - else if(exprOwners.contains(Some(None))) - Some(None) - else if(exprOwners.exists(o1 => exprOwners.exists(o2 => o1 != o2))) - Some(None) - else - exprOwners.head - } - - def getOwner(expr: LeonExpr): Option[Option[FunDef]] = getOwner(getReturnedExpr(expr)) } def containsLetDef(expr: LeonExpr): Boolean = { diff --git a/src/main/scala/leon/purescala/ExprOps.scala b/src/main/scala/leon/purescala/ExprOps.scala index 1cee46865717b0e214e66ff41fac0c07039031fa..724f6a0fca6e4b2c91c1d288013ef8fb9a817b41 100644 --- a/src/main/scala/leon/purescala/ExprOps.scala +++ b/src/main/scala/leon/purescala/ExprOps.scala @@ -213,6 +213,49 @@ object ExprOps { } + /** Pre-transformation of the tree, with a context value from "top-down". + * + * Takes a partial function of replacements. + * Substitutes '''before''' recursing down the trees. The function returns + * an option of the new value, as well as the new context to be used for + * the recursion in its children. The context is "lost" when going back up, + * so changes made by one node will not be see by its siblings. + */ + def preMapWithContext[C](f: (Expr, C) => (Option[Expr], C), applyRec: Boolean = false) + (e: Expr, c: C): Expr = { + + def rec(expr: Expr, context: C): Expr = { + + val (newV, newCtx) = { + if(applyRec) { + var ctx = context + val finalV = fixpoint{ e: Expr => { + val res = f(e, ctx) + ctx = res._2 + res._1.getOrElse(e) + }} (expr) + (finalV, ctx) + } else { + val res = f(expr, context) + (res._1.getOrElse(expr), res._2) + } + } + + val Operator(es, builder) = newV + val newEs = es.map(e => rec(e, newCtx)) + + if ((newEs zip es).exists { case (bef, aft) => aft ne bef }) { + builder(newEs).copiedFrom(newV) + } else { + newV + } + + } + + rec(e, c) + } + + /** Applies functions and combines results in a generic way * * Start with an initial value, and apply functions to nodes before @@ -376,6 +419,14 @@ object ExprOps { }(expr) } + /** Returns all Function calls found in the expression */ + def nestedFunDefsOf(expr: Expr): Set[FunDef] = { + collect[FunDef] { + case LetDef(fds, _) => fds.toSet + case _ => Set() + }(expr) + } + /** Returns functions in directly nested LetDefs */ def directlyNestedFunDefs(e: Expr): Set[FunDef] = { fold[Set[FunDef]]{ diff --git a/src/main/scala/leon/xlang/AntiAliasingPhase.scala b/src/main/scala/leon/xlang/AntiAliasingPhase.scala new file mode 100644 index 0000000000000000000000000000000000000000..7eb391088ea64c276aa0bbf7836e4a9511aa978c --- /dev/null +++ b/src/main/scala/leon/xlang/AntiAliasingPhase.scala @@ -0,0 +1,383 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ +package leon.xlang + +import leon.TransformationPhase +import leon.LeonContext +import leon.purescala.Common._ +import leon.purescala.Definitions._ +import leon.purescala.Expressions._ +import leon.purescala.ExprOps._ +import leon.purescala.DefOps._ +import leon.purescala.Types._ +import leon.purescala.Constructors._ +import leon.purescala.Extractors._ +import leon.xlang.Expressions._ + +object AntiAliasingPhase extends TransformationPhase { + + val name = "Anti-Aliasing" + val description = "Make aliasing explicit" + + override def apply(ctx: LeonContext, pgm: Program): Program = { + val fds = allFunDefs(pgm) + fds.foreach(fd => checkAliasing(fd)(ctx)) + + var updatedFunctions: Map[FunDef, FunDef] = Map() + + val effects = effectsAnalysis(pgm) + + //for each fun def, all the vars the the body captures. Only + //mutable types. + val varsInScope: Map[FunDef, Set[Identifier]] = (for { + fd <- fds + } yield { + val allFreeVars = fd.body.map(bd => variablesOf(bd)).getOrElse(Set()) + val freeVars = allFreeVars -- fd.params.map(_.id) + val mutableFreeVars = freeVars.filter(id => id.getType.isInstanceOf[ArrayType]) + (fd, mutableFreeVars) + }).toMap + + /* + * The first pass will introduce all new function definitions, + * so that in the next pass we can update function invocations + */ + for { + fd <- fds + } { + updatedFunctions += (fd -> updateFunDef(fd, effects)(ctx)) + } + + for { + fd <- fds + } { + updateBody(fd, effects, updatedFunctions, varsInScope)(ctx) + } + + val res = replaceFunDefs(pgm)(fd => updatedFunctions.get(fd), (fi, fd) => None) + //println(res._1) + res._1 + } + + /* + * Create a new FunDef for a given FunDef in the program. + * Adapt the signature to express its effects. In case the + * function has no effect, this will still introduce a fresh + * FunDef as the body might have to be updated anyway. + */ + private def updateFunDef(fd: FunDef, effects: Effects)(ctx: LeonContext): FunDef = { + + val ownEffects = effects(fd) + val aliasedParams: Seq[Identifier] = fd.params.zipWithIndex.flatMap{ + case (vd, i) if ownEffects.contains(i) => Some(vd) + case _ => None + }.map(_.id) + + fd.body.foreach(body => getReturnedExpr(body).foreach{ + case v@Variable(id) if aliasedParams.contains(id) => + ctx.reporter.fatalError(v.getPos, "Cannot return a shared reference to a mutable object") + case _ => () + }) + //val allBodies: Set[Expr] = + // fd.body.toSet.flatMap((bd: Expr) => nestedFunDefsOf(bd).flatMap(_.body)) ++ fd.body + //allBodies.foreach(body => getReturnedExpr(body).foreach{ + // case v@Variable(id) if aliasedParams.contains(id) => + // ctx.reporter.fatalError(v.getPos, "Cannot return a shared reference to a mutable object: "k+ v) + // case _ => () + //}) + + val newReturnType: TypeTree = if(aliasedParams.isEmpty) fd.returnType else { + tupleTypeWrap(fd.returnType +: aliasedParams.map(_.getType)) + } + val newFunDef = new FunDef(fd.id.freshen, fd.tparams, fd.params, newReturnType) + newFunDef.addFlags(fd.flags) + newFunDef.setPos(fd) + newFunDef + } + + private def updateBody(fd: FunDef, effects: Effects, updatedFunDefs: Map[FunDef, FunDef], varsInScope: Map[FunDef, Set[Identifier]]) + (ctx: LeonContext): Unit = { + + val ownEffects = effects(fd) + val aliasedParams: Seq[Identifier] = fd.params.zipWithIndex.flatMap{ + case (vd, i) if ownEffects.contains(i) => Some(vd) + case _ => None + }.map(_.id) + + val newFunDef = updatedFunDefs(fd) + + if(aliasedParams.isEmpty) { + val newBody = fd.body.map(body => { + makeSideEffectsExplicit(body, Seq(), effects, updatedFunDefs, varsInScope)(ctx) + }) + newFunDef.body = newBody + newFunDef.precondition = fd.precondition + newFunDef.postcondition = fd.postcondition + } else { + val freshLocalVars: Seq[Identifier] = aliasedParams.map(v => v.freshen) + val rewritingMap: Map[Identifier, Identifier] = aliasedParams.zip(freshLocalVars).toMap + + val newBody = fd.body.map(body => { + + val freshBody = replaceFromIDs(rewritingMap.map(p => (p._1, p._2.toVariable)), body) + val explicitBody = makeSideEffectsExplicit(freshBody, freshLocalVars, effects, updatedFunDefs, varsInScope)(ctx) + + //WARNING: only works if side effects in Tuples are extracted from left to right, + // in the ImperativeTransformation phase. + val finalBody: Expr = Tuple(explicitBody +: freshLocalVars.map(_.toVariable)) + + freshLocalVars.zip(aliasedParams).foldLeft(finalBody)((bd, vp) => { + LetVar(vp._1, Variable(vp._2), bd) + }) + + }) + + val newPostcondition = fd.postcondition.map(post => { + val Lambda(Seq(res), postBody) = post + val newRes = ValDef(FreshIdentifier(res.id.name, newFunDef.returnType)) + val newBody = + replace( + aliasedParams.zipWithIndex.map{ case (id, i) => + (id.toVariable, TupleSelect(newRes.toVariable, i+2)): (Expr, Expr)}.toMap ++ + aliasedParams.map(id => + (Old(id), id.toVariable): (Expr, Expr)).toMap + + (res.toVariable -> TupleSelect(newRes.toVariable, 1)), + postBody) + Lambda(Seq(newRes), newBody).setPos(post) + }) + + newFunDef.body = newBody + newFunDef.precondition = fd.precondition + newFunDef.postcondition = newPostcondition + } + } + + //We turn all local val of mutable objects into vars and explicit side effects + //using assignments. We also make sure that no aliasing is being done. + private def makeSideEffectsExplicit + (body: Expr, aliasedParams: Seq[Identifier], effects: Effects, updatedFunDefs: Map[FunDef, FunDef], varsInScope: Map[FunDef, Set[Identifier]]) + (ctx: LeonContext): Expr = { + preMapWithContext[Set[Identifier]]((expr, bindings) => expr match { + + case up@ArrayUpdate(a, i, v) => { + val ra@Variable(id) = a + if(bindings.contains(id)) + (Some(Assignment(id, ArrayUpdated(ra, i, v).setPos(up)).setPos(up)), bindings) + else + (None, bindings) + } + + case l@Let(id, IsTyped(v, ArrayType(_)), b) => { + val varDecl = LetVar(id, v, b).setPos(l) + (Some(varDecl), bindings + id) + } + + case l@LetVar(id, IsTyped(v, ArrayType(_)), b) => { + (None, bindings + id) + } + + //we need to replace local fundef by the new updated fun defs. + case l@LetDef(fds, body) => { + //this might be traversed several time in case of doubly nested fundef, + //so we need to ignore the second times by checking if updatedFunDefs + //contains a mapping or not + val nfds = fds.map(fd => updatedFunDefs.get(fd).getOrElse(fd)) + (Some(LetDef(nfds, body)), bindings) + } + + case fi@FunctionInvocation(fd, args) => { + + val vis: Set[Identifier] = varsInScope.get(fd.fd).getOrElse(Set()) + args.find({ + case Variable(id) => vis.contains(id) + case _ => false + }).foreach(aliasedArg => + ctx.reporter.fatalError(aliasedArg.getPos, "Illegal passing of aliased parameter: " + aliasedArg)) + + updatedFunDefs.get(fd.fd) match { + case None => (None, bindings) + case Some(nfd) => { + val nfi = FunctionInvocation(nfd.typed(fd.tps), args).setPos(fi) + val fiEffects = effects.getOrElse(fd.fd, Set()) + if(fiEffects.nonEmpty) { + val modifiedArgs: Seq[Variable] = + args.zipWithIndex.filter{ case (arg, i) => fiEffects.contains(i) } + .map(_._1.asInstanceOf[Variable]) + + val duplicatedParams = modifiedArgs.diff(modifiedArgs.distinct).distinct + if(duplicatedParams.nonEmpty) + ctx.reporter.fatalError(fi.getPos, "Illegal passing of aliased parameter: " + duplicatedParams.head) + + val freshRes = FreshIdentifier("res", nfd.returnType) + + val extractResults = Block( + modifiedArgs.zipWithIndex.map(p => Assignment(p._1.id, TupleSelect(freshRes.toVariable, p._2 + 2))), + TupleSelect(freshRes.toVariable, 1)) + + + val newExpr = Let(freshRes, nfi, extractResults) + (Some(newExpr), bindings) + } else { + (Some(nfi), bindings) + } + } + } + } + + case _ => (None, bindings) + + })(body, aliasedParams.toSet) + } + + //TODO: in the future, any object with vars could be aliased and so + // we will need a general property + + private type Effects = Map[FunDef, Set[Int]] + + /* + * compute effects for each function in the program, including any nested + * functions (LetDef). + */ + private def effectsAnalysis(pgm: Program): Effects = { + + //currently computed effects (incremental) + var effects: Map[FunDef, Set[Int]] = Map() + //missing dependencies for a function for its effects to be complete + var missingEffects: Map[FunDef, Set[FunctionInvocation]] = Map() + + def effectsFullyComputed(fd: FunDef): Boolean = !missingEffects.isDefinedAt(fd) + + for { + fd <- allFunDefs(pgm) + } { + fd.body match { + case None => + effects += (fd -> Set()) + case Some(body) => { + val mutableParams = fd.params.filter(vd => vd.getType match { + case ArrayType(_) => true + case _ => false + }) + val mutatedParams = mutableParams.filter(vd => exists { + case ArrayUpdate(Variable(a), _, _) => a == vd.id + case _ => false + }(body)) + val mutatedParamsIndices = fd.params.zipWithIndex.flatMap{ + case (vd, i) if mutatedParams.contains(vd) => Some(i) + case _ => None + }.toSet + effects = effects + (fd -> mutatedParamsIndices) + + val missingCalls: Set[FunctionInvocation] = functionCallsOf(body).filterNot(fi => fi.tfd.fd == fd) + if(missingCalls.nonEmpty) + missingEffects += (fd -> missingCalls) + } + } + } + + def rec(): Unit = { + val previousMissingEffects = missingEffects + + for{ (fd, calls) <- missingEffects } { + var newMissingCalls: Set[FunctionInvocation] = calls + for(fi <- calls) { + val mutatedArgs = invocEffects(fi) + val mutatedFunParams: Set[Int] = fd.params.zipWithIndex.flatMap{ + case (vd, i) if mutatedArgs.contains(vd.id) => Some(i) + case _ => None + }.toSet + effects += (fd -> (effects(fd) ++ mutatedFunParams)) + + if(effectsFullyComputed(fi.tfd.fd)) { + newMissingCalls -= fi + } + } + if(newMissingCalls.isEmpty) + missingEffects = missingEffects - fd + else + missingEffects += (fd -> newMissingCalls) + } + + if(missingEffects != previousMissingEffects) { + rec() + } + } + + def invocEffects(fi: FunctionInvocation): Set[Identifier] = { + //TODO: the require should be fine once we consider nested functions as well + //require(effects.isDefinedAt(fi.tfd.fd) + val mutatedParams: Set[Int] = effects.get(fi.tfd.fd).getOrElse(Set()) + fi.args.zipWithIndex.flatMap{ + case (Variable(id), i) if mutatedParams.contains(i) => Some(id) + case _ => None + }.toSet + } + + rec() + effects + } + + + def checkAliasing(fd: FunDef)(ctx: LeonContext): Unit = { + def checkReturnValue(body: Expr, bindings: Set[Identifier]): Unit = { + getReturnedExpr(body).foreach{ + case IsTyped(v@Variable(id), ArrayType(_)) if bindings.contains(id) => + ctx.reporter.fatalError(v.getPos, "Cannot return a shared reference to a mutable object: " + v) + case _ => () + } + } + + fd.body.foreach(bd => { + val params = fd.params.map(_.id).toSet + checkReturnValue(bd, params) + preMapWithContext[Set[Identifier]]((expr, bindings) => expr match { + case l@Let(id, IsTyped(v, ArrayType(_)), b) => { + v match { + case FiniteArray(_, _, _) => () + case FunctionInvocation(_, _) => () + case ArrayUpdated(_, _, _) => () + case _ => ctx.reporter.fatalError(l.getPos, "Cannot alias array: " + l) + } + (None, bindings + id) + } + case l@LetVar(id, IsTyped(v, ArrayType(_)), b) => { + v match { + case FiniteArray(_, _, _) => () + case FunctionInvocation(_, _) => () + case ArrayUpdated(_, _, _) => () + case _ => ctx.reporter.fatalError(l.getPos, "Cannot alias array: " + l) + } + (None, bindings + id) + } + case l@LetDef(fds, body) => { + fds.foreach(fd => fd.body.foreach(bd => checkReturnValue(bd, bindings))) + (None, bindings) + } + + case _ => (None, bindings) + })(bd, params) + }) + } + + /* + * A bit hacky, but not sure of the best way to do something like that + * currently. + */ + private def getReturnedExpr(expr: Expr): Seq[Expr] = expr match { + case Let(_, _, rest) => getReturnedExpr(rest) + case LetVar(_, _, rest) => getReturnedExpr(rest) + case Block(_, rest) => getReturnedExpr(rest) + case IfExpr(_, thenn, elze) => getReturnedExpr(thenn) ++ getReturnedExpr(elze) + case MatchExpr(_, cses) => cses.flatMap{ cse => getReturnedExpr(cse.rhs) } + case e => Seq(expr) + } + + + /* + * returns all fun def in the program, including local definitions inside + * other functions (LetDef). + */ + private def allFunDefs(pgm: Program): Seq[FunDef] = + pgm.definedFunctions.flatMap(fd => + fd.body.toSet.flatMap((bd: Expr) => + nestedFunDefsOf(bd)) + fd) +} diff --git a/src/main/scala/leon/xlang/ImperativeCodeElimination.scala b/src/main/scala/leon/xlang/ImperativeCodeElimination.scala index 20048c0ba60b0d968877c713c1ddfe9f9d25f98d..a298e90b16f405a19856c1a38a276a04153fd500 100644 --- a/src/main/scala/leon/xlang/ImperativeCodeElimination.scala +++ b/src/main/scala/leon/xlang/ImperativeCodeElimination.scala @@ -67,7 +67,7 @@ object ImperativeCodeElimination extends UnitPhase[Program] { val (tRes, tScope, tFun) = toFunction(tExpr) val (eRes, eScope, eFun) = toFunction(eExpr) - val iteRType = leastUpperBound(tRes.getType, eRes.getType).get + val iteRType = leastUpperBound(tRes.getType, eRes.getType).getOrElse(Untyped) val modifiedVars: Seq[Identifier] = (tFun.keys ++ eFun.keys).toSet.intersect(varsInScope).toSeq val resId = FreshIdentifier("res", iteRType) diff --git a/src/main/scala/leon/xlang/XLangDesugaringPhase.scala b/src/main/scala/leon/xlang/XLangDesugaringPhase.scala index 3a7f8be381cfa5fdb87dc6870cf35141f8d8cf33..59dd3217714f964be5f9ff6a1428617c6e54e17a 100644 --- a/src/main/scala/leon/xlang/XLangDesugaringPhase.scala +++ b/src/main/scala/leon/xlang/XLangDesugaringPhase.scala @@ -12,7 +12,8 @@ object XLangDesugaringPhase extends LeonPhase[Program, Program] { override def run(ctx: LeonContext, pgm: Program): (LeonContext, Program) = { val phases = - ArrayTransformation andThen + //ArrayTransformation andThen + AntiAliasingPhase andThen EpsilonElimination andThen ImperativeCodeElimination diff --git a/src/test/resources/regression/frontends/error/xlang/Array2.scala b/src/test/resources/regression/frontends/error/xlang/Array2.scala deleted file mode 100644 index b1b370395d7e0b648e0b88875b3678eaf4668eb5..0000000000000000000000000000000000000000 --- a/src/test/resources/regression/frontends/error/xlang/Array2.scala +++ /dev/null @@ -1,11 +0,0 @@ -/* Copyright 2009-2015 EPFL, Lausanne */ - -object Array2 { - - def foo(): Int = { - val a = Array.fill(5)(5) - val b = a - b(3) - } - -} diff --git a/src/test/resources/regression/frontends/error/xlang/Array3.scala b/src/test/resources/regression/frontends/error/xlang/Array3.scala deleted file mode 100644 index 14a8512015102bd235a9efe41782ba7d5a46fd44..0000000000000000000000000000000000000000 --- a/src/test/resources/regression/frontends/error/xlang/Array3.scala +++ /dev/null @@ -1,14 +0,0 @@ -/* Copyright 2009-2015 EPFL, Lausanne */ - -object Array3 { - - def foo(): Int = { - val a = Array.fill(5)(5) - if(a.length > 2) - a(1) = 2 - else - 0 - 0 - } - -} diff --git a/src/test/resources/regression/frontends/error/xlang/Array4.scala b/src/test/resources/regression/frontends/error/xlang/Array4.scala deleted file mode 100644 index e41535d6d267986ba7764a6f457970c6ab33b733..0000000000000000000000000000000000000000 --- a/src/test/resources/regression/frontends/error/xlang/Array4.scala +++ /dev/null @@ -1,10 +0,0 @@ -/* Copyright 2009-2015 EPFL, Lausanne */ - -object Array4 { - - def foo(a: Array[Int]): Int = { - val b = a - b(3) - } - -} diff --git a/src/test/resources/regression/frontends/error/xlang/Array5.scala b/src/test/resources/regression/frontends/error/xlang/Array5.scala deleted file mode 100644 index 8b7254e9482ddc7df1196c03b1a191c54e86ea0f..0000000000000000000000000000000000000000 --- a/src/test/resources/regression/frontends/error/xlang/Array5.scala +++ /dev/null @@ -1,12 +0,0 @@ -/* Copyright 2009-2015 EPFL, Lausanne */ - -object Array5 { - - def foo(a: Array[Int]): Int = { - a(2) = 4 - a(2) - } - -} - -// vim: set ts=4 sw=4 et: diff --git a/src/test/resources/regression/frontends/error/xlang/Array6.scala b/src/test/resources/regression/frontends/error/xlang/Array6.scala deleted file mode 100644 index c4d0c09541d3c7fe4ea4be1527cd704e96d54bb1..0000000000000000000000000000000000000000 --- a/src/test/resources/regression/frontends/error/xlang/Array6.scala +++ /dev/null @@ -1,12 +0,0 @@ -/* Copyright 2009-2015 EPFL, Lausanne */ - - -object Array6 { - - def foo(): Int = { - val a = Array.fill(5)(5) - var b = a - b(0) - } - -} diff --git a/src/test/resources/regression/frontends/error/xlang/Array7.scala b/src/test/resources/regression/frontends/error/xlang/Array7.scala deleted file mode 100644 index ab6f4c20da5a84adfd8509a60174d78c0c423654..0000000000000000000000000000000000000000 --- a/src/test/resources/regression/frontends/error/xlang/Array7.scala +++ /dev/null @@ -1,11 +0,0 @@ -/* Copyright 2009-2015 EPFL, Lausanne */ - -object Array7 { - - def foo(): Int = { - val a = Array.fill(5)(5) - var b = a - b(0) - } - -} diff --git a/src/test/resources/regression/verification/xlang/valid/ArrayParamMutation1.scala b/src/test/resources/regression/verification/xlang/valid/ArrayParamMutation1.scala new file mode 100644 index 0000000000000000000000000000000000000000..f0e622c66c31e4fe01ff8be5c1b08e17d4d330eb --- /dev/null +++ b/src/test/resources/regression/verification/xlang/valid/ArrayParamMutation1.scala @@ -0,0 +1,16 @@ +import leon.lang._ + +object ArrayParamMutation1 { + + def update(a: Array[BigInt]): Unit = { + require(a.length > 0) + a(0) = 10 + } + + def f(): BigInt = { + val a = Array.fill(10)(BigInt(0)) + update(a) + a(0) + } ensuring(res => res == 10) + +} diff --git a/src/test/resources/regression/verification/xlang/valid/ArrayParamMutation2.scala b/src/test/resources/regression/verification/xlang/valid/ArrayParamMutation2.scala new file mode 100644 index 0000000000000000000000000000000000000000..801b35e0cc545f160fc8061e34fd0ee06b7c3f73 --- /dev/null +++ b/src/test/resources/regression/verification/xlang/valid/ArrayParamMutation2.scala @@ -0,0 +1,16 @@ +import leon.lang._ + +object ArrayParamMutation2 { + + def rec(a: Array[BigInt]): BigInt = { + require(a.length > 1 && a(0) >= 0) + if(a(0) == 0) + a(1) + else { + a(0) = a(0) - 1 + a(1) = a(1) + a(0) + rec(a) + } + } ensuring(res => a(0) == 0) + +} diff --git a/src/test/resources/regression/verification/xlang/valid/ArrayParamMutation3.scala b/src/test/resources/regression/verification/xlang/valid/ArrayParamMutation3.scala new file mode 100644 index 0000000000000000000000000000000000000000..f575167444f839c0ee900a35de5e4e822624dc21 --- /dev/null +++ b/src/test/resources/regression/verification/xlang/valid/ArrayParamMutation3.scala @@ -0,0 +1,23 @@ +import leon.lang._ + +object ArrayParamMutation3 { + + def odd(a: Array[BigInt]): Boolean = { + require(a.length > 0 && a(0) >= 0) + if(a(0) == 0) false + else { + a(0) = a(0) - 1 + even(a) + } + } ensuring(res => a(0) == 0) + + def even(a: Array[BigInt]): Boolean = { + require(a.length > 0 && a(0) >= 0) + if(a(0) == 0) true + else { + a(0) = a(0) - 1 + odd(a) + } + } ensuring(res => a(0) == 0) + +} diff --git a/src/test/resources/regression/verification/xlang/valid/ArrayParamMutation4.scala b/src/test/resources/regression/verification/xlang/valid/ArrayParamMutation4.scala new file mode 100644 index 0000000000000000000000000000000000000000..31af4cd5885ea66a2d11e9387ba7e306423ec4d7 --- /dev/null +++ b/src/test/resources/regression/verification/xlang/valid/ArrayParamMutation4.scala @@ -0,0 +1,23 @@ +import leon.lang._ + +object ArrayParamMutation4 { + + def multipleArgs(a1: Array[BigInt], a2: Array[BigInt]): Unit = { + require(a1.length > 0 && a2.length > 0) + if(a1(0) == 10) + a2(0) = 13 + else + a2(0) = a1(0) + 1 + } + + def transitiveEffects(a1: Array[BigInt], a2: Array[BigInt]): Unit = { + require(a1.length > 0 && a2.length > 0) + multipleArgs(a1, a2) + } ensuring(_ => a2(0) >= a1(0)) + + def transitiveReverseEffects(a1: Array[BigInt], a2: Array[BigInt]): Unit = { + require(a1.length > 0 && a2.length > 0) + multipleArgs(a2, a1) + } ensuring(_ => a1(0) >= a2(0)) + +} diff --git a/src/test/resources/regression/verification/xlang/valid/ArrayParamMutation5.scala b/src/test/resources/regression/verification/xlang/valid/ArrayParamMutation5.scala new file mode 100644 index 0000000000000000000000000000000000000000..249a79d1f3b7d8df8c941ab3121c4eafed149e03 --- /dev/null +++ b/src/test/resources/regression/verification/xlang/valid/ArrayParamMutation5.scala @@ -0,0 +1,21 @@ + +import leon.lang._ + +object ArrayParamMutation5 { + + def mutuallyRec1(a1: Array[BigInt], a2: Array[BigInt]): Unit = { + require(a1.length > 0 && a1(0) > 0 && a2.length > 0) + if(a1(0) == 10) { + () + } else { + mutuallyRec2(a1, a2) + } + } ensuring(res => a1(0) == 10) + + def mutuallyRec2(a1: Array[BigInt], a2: Array[BigInt]): Unit = { + require(a1.length > 0 && a2.length > 0 && a1(0) > 0) + a1(0) = 10 + mutuallyRec1(a1, a2) + } + +} diff --git a/src/test/resources/regression/verification/xlang/valid/ArrayParamMutation6.scala b/src/test/resources/regression/verification/xlang/valid/ArrayParamMutation6.scala new file mode 100644 index 0000000000000000000000000000000000000000..29ded427fa6546a103d8da6f98cefc1415f389a6 --- /dev/null +++ b/src/test/resources/regression/verification/xlang/valid/ArrayParamMutation6.scala @@ -0,0 +1,16 @@ +import leon.lang._ + +object ArrayParamMutation6 { + + def multipleEffects(a1: Array[BigInt], a2: Array[BigInt]): Unit = { + require(a1.length > 0 && a2.length > 0) + a1(0) = 11 + a2(0) = 12 + } ensuring(_ => a1(0) != a2(0)) + + def f(a1: Array[BigInt], a2: Array[BigInt]): Unit = { + require(a1.length > 0 && a2.length > 0) + multipleEffects(a1, a2) + } ensuring(_ => a1(0) == 11 && a2(0) == 12) + +} diff --git a/src/test/resources/regression/verification/xlang/valid/ArrayParamMutation7.scala b/src/test/resources/regression/verification/xlang/valid/ArrayParamMutation7.scala new file mode 100644 index 0000000000000000000000000000000000000000..53d67729fd57723d1693c564e99cd3d66ee095ef --- /dev/null +++ b/src/test/resources/regression/verification/xlang/valid/ArrayParamMutation7.scala @@ -0,0 +1,29 @@ +import leon.lang._ + +object ArrayParamMutation7 { + + def f(i: BigInt)(implicit world: Array[BigInt]): BigInt = { + require(world.length == 3) + + world(1) += 1 //global counter of f + + val res = i*i + world(0) = res + res + } + + def mainProgram(): Unit = { + + implicit val world: Array[BigInt] = Array(0,0,0) + + f(1) + assert(world(0) == 1) + f(2) + assert(world(0) == 4) + f(4) + assert(world(0) == 16) + + assert(world(1) == 3) + } + +} diff --git a/src/test/resources/regression/verification/xlang/valid/ArrayParamMutation8.scala b/src/test/resources/regression/verification/xlang/valid/ArrayParamMutation8.scala new file mode 100644 index 0000000000000000000000000000000000000000..68aa737eb42e6e51073dac27b799e06bde928400 --- /dev/null +++ b/src/test/resources/regression/verification/xlang/valid/ArrayParamMutation8.scala @@ -0,0 +1,25 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +import leon.lang._ + +object ArrayParamMutation8 { + + def odd(a: Array[BigInt]): Boolean = { + require(a.length > 0 && a(0) >= 0) + if(a(0) == 0) false + else { + a(0) = a(0) - 1 + even(a) + } + } ensuring(res => if(old(a)(0) % 2 == 1) res else !res) + + def even(a: Array[BigInt]): Boolean = { + require(a.length > 0 && a(0) >= 0) + if(a(0) == 0) true + else { + a(0) = a(0) - 1 + odd(a) + } + } ensuring(res => if(old(a)(0) % 2 == 0) res else !res) + +} diff --git a/src/test/resources/regression/verification/xlang/valid/ArrayParamMutation9.scala b/src/test/resources/regression/verification/xlang/valid/ArrayParamMutation9.scala new file mode 100644 index 0000000000000000000000000000000000000000..f5046b6cf3b40382ccc5d989d81d73bc577da9f7 --- /dev/null +++ b/src/test/resources/regression/verification/xlang/valid/ArrayParamMutation9.scala @@ -0,0 +1,22 @@ +import leon.lang._ + +object ArrayParamMutation9 { + def abs(a: Array[Int]) { + require(a.length > 0) + var i = 0; + (while (i < a.length) { + a(i) = if (a(i) < 0) -a(i) else a(i) // <-- this makes Leon crash + i = i + 1 + }) invariant(i >= 0) + } + + + def main = { + val a = Array(0, -1, 2, -3) + + abs(a) + + a(0) + a(1) - 1 + a(2) - 2 + a(3) - 3 // == 0 + } + +} diff --git a/src/test/resources/regression/verification/xlang/valid/NestedFunParamsMutation1.scala b/src/test/resources/regression/verification/xlang/valid/NestedFunParamsMutation1.scala new file mode 100644 index 0000000000000000000000000000000000000000..a7250a7bcfd572c49584110d213f9e9991a10c9f --- /dev/null +++ b/src/test/resources/regression/verification/xlang/valid/NestedFunParamsMutation1.scala @@ -0,0 +1,16 @@ +import leon.lang._ + +object NestedFunParamsMutation1 { + + def f(): Int = { + def g(a: Array[Int]): Unit = { + require(a.length > 0) + a(0) = 10 + } + + val a = Array(1,2,3,4) + g(a) + a(0) + } ensuring(_ == 10) + +} diff --git a/src/test/resources/regression/verification/xlang/valid/NestedFunParamsMutation2.scala b/src/test/resources/regression/verification/xlang/valid/NestedFunParamsMutation2.scala new file mode 100644 index 0000000000000000000000000000000000000000..799a87c6e9e70bf6ef89bfc1fb7a6e116adb7feb --- /dev/null +++ b/src/test/resources/regression/verification/xlang/valid/NestedFunParamsMutation2.scala @@ -0,0 +1,21 @@ +import leon.lang._ + +object NestedFunParamsMutation2 { + + def f(): Int = { + def g(a: Array[Int]): Unit = { + require(a.length > 0) + a(0) = 10 + } + + def h(a: Array[Int]): Unit = { + require(a.length > 0) + g(a) + } + + val a = Array(1,2,3,4) + h(a) + a(0) + } ensuring(_ == 10) + +} diff --git a/src/test/resources/regression/frontends/error/xlang/Array1.scala b/src/test/resources/regression/xlang/error/Array1.scala similarity index 100% rename from src/test/resources/regression/frontends/error/xlang/Array1.scala rename to src/test/resources/regression/xlang/error/Array1.scala diff --git a/src/test/resources/regression/xlang/error/ArrayAliasing1.scala b/src/test/resources/regression/xlang/error/ArrayAliasing1.scala new file mode 100644 index 0000000000000000000000000000000000000000..30b1652dac16f1f8a9c7b36d83d9a0a52811e3c6 --- /dev/null +++ b/src/test/resources/regression/xlang/error/ArrayAliasing1.scala @@ -0,0 +1,13 @@ +import leon.lang._ + +object ArrayAliasing1 { + + def f1(): BigInt = { + val a = Array.fill(10)(BigInt(0)) + val b = a + b(0) = 10 + a(0) + } ensuring(_ == 10) + +} + diff --git a/src/test/resources/regression/xlang/error/ArrayAliasing10.scala b/src/test/resources/regression/xlang/error/ArrayAliasing10.scala new file mode 100644 index 0000000000000000000000000000000000000000..05737b03d9816be72cf52f4913f57a482abf76dd --- /dev/null +++ b/src/test/resources/regression/xlang/error/ArrayAliasing10.scala @@ -0,0 +1,19 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +object ArrayAliasing10 { + + def foo(): Int = { + val a = Array.fill(5)(0) + + def rec(): Array[Int] = { + + def nestedRec(): Array[Int] = { + a + } + nestedRec() + } + val b = rec() + b(0) + } + +} diff --git a/src/test/resources/regression/xlang/error/ArrayAliasing2.scala b/src/test/resources/regression/xlang/error/ArrayAliasing2.scala new file mode 100644 index 0000000000000000000000000000000000000000..4e906865a8848aaa00150c67de16f6b32136c64a --- /dev/null +++ b/src/test/resources/regression/xlang/error/ArrayAliasing2.scala @@ -0,0 +1,11 @@ +import leon.lang._ + +object ArrayAliasing2 { + + def f1(a: Array[BigInt]): BigInt = { + val b = a + b(0) = 10 + a(0) + } ensuring(_ == 10) + +} diff --git a/src/test/resources/regression/xlang/error/ArrayAliasing3.scala b/src/test/resources/regression/xlang/error/ArrayAliasing3.scala new file mode 100644 index 0000000000000000000000000000000000000000..0398fc37b9dc2028e1535878ec377bef1620dd88 --- /dev/null +++ b/src/test/resources/regression/xlang/error/ArrayAliasing3.scala @@ -0,0 +1,11 @@ +import leon.lang._ + +object ArrayAliasing3 { + + def f1(a: Array[BigInt], b: Boolean): BigInt = { + val c = if(b) a else Array[BigInt](1,2,3,4,5) + c(0) = 10 + a(0) + } ensuring(_ == 10) + +} diff --git a/src/test/resources/regression/xlang/error/ArrayAliasing4.scala b/src/test/resources/regression/xlang/error/ArrayAliasing4.scala new file mode 100644 index 0000000000000000000000000000000000000000..2632782c39e853744744b66309ef10342bee386b --- /dev/null +++ b/src/test/resources/regression/xlang/error/ArrayAliasing4.scala @@ -0,0 +1,11 @@ +import leon.lang._ + +object ArrayAliasing4 { + + def f1(a: Array[BigInt]): Array[BigInt] = { + require(a.length > 0) + a(0) = 10 + a + } ensuring(res => res(0) == 10) + +} diff --git a/src/test/resources/regression/xlang/error/ArrayAliasing5.scala b/src/test/resources/regression/xlang/error/ArrayAliasing5.scala new file mode 100644 index 0000000000000000000000000000000000000000..b9363d1ab5a627df29e6a0f0018c73850dcbb529 --- /dev/null +++ b/src/test/resources/regression/xlang/error/ArrayAliasing5.scala @@ -0,0 +1,18 @@ +import leon.lang._ + +object ArrayAliasing5 { + + + def f1(a: Array[BigInt], b: Array[BigInt]): Unit = { + require(a.length > 0 && b.length > 0) + a(0) = 10 + b(0) = 20 + } ensuring(_ => a(0) == 10 && b(0) == 20) + + + def callWithAliases(): Unit = { + val a = Array[BigInt](0,0,0,0) + f1(a, a) + } + +} diff --git a/src/test/resources/regression/frontends/error/xlang/Array8.scala b/src/test/resources/regression/xlang/error/ArrayAliasing6.scala similarity index 80% rename from src/test/resources/regression/frontends/error/xlang/Array8.scala rename to src/test/resources/regression/xlang/error/ArrayAliasing6.scala index bbe5bd5fd92b0f4f9662379693d06924bdaf5461..963a134bf71da7a625252411854d73272f56d574 100644 --- a/src/test/resources/regression/frontends/error/xlang/Array8.scala +++ b/src/test/resources/regression/xlang/error/ArrayAliasing6.scala @@ -1,6 +1,6 @@ /* Copyright 2009-2015 EPFL, Lausanne */ -object Array8 { +object ArrayAliasing6 { def foo(a: Array[Int]): Array[Int] = { a diff --git a/src/test/resources/regression/xlang/error/ArrayAliasing7.scala b/src/test/resources/regression/xlang/error/ArrayAliasing7.scala new file mode 100644 index 0000000000000000000000000000000000000000..21bc94502327b334f2e4e5887d4a7286731c78b7 --- /dev/null +++ b/src/test/resources/regression/xlang/error/ArrayAliasing7.scala @@ -0,0 +1,10 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +object ArrayAliasing7 { + + def foo(a: Array[Int]): Array[Int] = { + val b = a + b + } + +} diff --git a/src/test/resources/regression/frontends/error/xlang/Array9.scala b/src/test/resources/regression/xlang/error/ArrayAliasing8.scala similarity index 86% rename from src/test/resources/regression/frontends/error/xlang/Array9.scala rename to src/test/resources/regression/xlang/error/ArrayAliasing8.scala index fbc7dd7376e0966df5ed6eb93bafa7427aeab9e8..e7c27cc9cebf657033da4c07936ce16a510075a0 100644 --- a/src/test/resources/regression/frontends/error/xlang/Array9.scala +++ b/src/test/resources/regression/xlang/error/ArrayAliasing8.scala @@ -1,6 +1,6 @@ /* Copyright 2009-2015 EPFL, Lausanne */ -object Array9 { +object ArrayAliasing8 { def foo(a: Array[Int]): Int = { def rec(): Array[Int] = { diff --git a/src/test/resources/regression/frontends/error/xlang/Array10.scala b/src/test/resources/regression/xlang/error/ArrayAliasing9.scala similarity index 87% rename from src/test/resources/regression/frontends/error/xlang/Array10.scala rename to src/test/resources/regression/xlang/error/ArrayAliasing9.scala index 563cdacdf7e0e66ff56eec571fae4d3e3bbe10be..c84d29c3fbb4866100173b7ac0e7b4d0da9a1e57 100644 --- a/src/test/resources/regression/frontends/error/xlang/Array10.scala +++ b/src/test/resources/regression/xlang/error/ArrayAliasing9.scala @@ -1,6 +1,6 @@ /* Copyright 2009-2015 EPFL, Lausanne */ -object Array10 { +object ArrayAliasing9 { def foo(): Int = { val a = Array.fill(5)(0) diff --git a/src/test/resources/regression/xlang/error/NestedFunctionAliasing1.scala b/src/test/resources/regression/xlang/error/NestedFunctionAliasing1.scala new file mode 100644 index 0000000000000000000000000000000000000000..12feace5413c23e688ac194192482120793b4e24 --- /dev/null +++ b/src/test/resources/regression/xlang/error/NestedFunctionAliasing1.scala @@ -0,0 +1,17 @@ +import leon.lang._ + +object NestedFunctinAliasing1 { + + def f(): Int = { + val a = Array(1,2,3,4) + + def g(b: Array[Int]): Unit = { + require(b.length > 0 && a.length > 0) + b(0) = 10 + a(0) = 17 + } ensuring(_ => b(0) == 10) + + g(a) + a(0) + } ensuring(_ == 10) +} diff --git a/src/test/resources/regression/xlang/error/NestedFunctionAliasing2.scala b/src/test/resources/regression/xlang/error/NestedFunctionAliasing2.scala new file mode 100644 index 0000000000000000000000000000000000000000..81a9b82b39fb47af105ef2e3122fe86a8b10dbb6 --- /dev/null +++ b/src/test/resources/regression/xlang/error/NestedFunctionAliasing2.scala @@ -0,0 +1,17 @@ +import leon.lang._ + +object NestedFunctinAliasing1 { + + def f(a: Array(1,2,3,4)): Int = { + + def g(b: Array[Int]): Unit = { + require(b.length > 0 && a.length > 0) + b(0) = 10 + a(0) = 17 + } ensuring(_ => b(0) == 10) + + g(a) + a(0) + } ensuring(_ == 10) + +} diff --git a/src/test/scala/leon/regression/frontends/FrontEndsSuite.scala b/src/test/scala/leon/regression/frontends/FrontEndsSuite.scala index b9bfccd36bc2abcc3479c9d3a703df0cdfe16ab7..a0e52aa0b083c04a5a3e1141d2f3287a1995611e 100644 --- a/src/test/scala/leon/regression/frontends/FrontEndsSuite.scala +++ b/src/test/scala/leon/regression/frontends/FrontEndsSuite.scala @@ -36,7 +36,6 @@ class FrontEndsSuite extends LeonRegressionSuite { } val pipeNormal = xlang.NoXLangFeaturesChecking andThen NoopPhase() // redundant NoopPhase to trigger throwing error between phases - val pipeX = NoopPhase[Program]() val baseDir = "regression/frontends/" forEachFileIn(baseDir+"passing/") { f => @@ -45,8 +44,5 @@ class FrontEndsSuite extends LeonRegressionSuite { forEachFileIn(baseDir+"error/simple/") { f => testFrontend(f, pipeNormal, true) } - forEachFileIn(baseDir+"error/xlang/") { f => - testFrontend(f, pipeX, true) - } } diff --git a/src/test/scala/leon/regression/verification/VerificationSuite.scala b/src/test/scala/leon/regression/verification/VerificationSuite.scala index f2ae97880694baac498b94570f81beb1c21c422f..446d06675cdb9b3eb1c7095137ab95e8a928c399 100644 --- a/src/test/scala/leon/regression/verification/VerificationSuite.scala +++ b/src/test/scala/leon/regression/verification/VerificationSuite.scala @@ -41,7 +41,7 @@ trait VerificationSuite extends LeonRegressionSuite { VerificationPhase andThen (if (desugarXLang) FixReportLabels else NoopPhase[VerificationReport]) - val ctx = createLeonContext(files:_*) + val ctx = createLeonContext(files:_*).copy(reporter = new TestErrorReporter) try { val (_, ast) = extraction.run(ctx, files) diff --git a/src/test/scala/leon/regression/xlang/XLangDesugaringSuite.scala b/src/test/scala/leon/regression/xlang/XLangDesugaringSuite.scala new file mode 100644 index 0000000000000000000000000000000000000000..41260ec2df6f9d9f32827c39e7f700da15ccca9e --- /dev/null +++ b/src/test/scala/leon/regression/xlang/XLangDesugaringSuite.scala @@ -0,0 +1,46 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +package leon.regression.xlang + +import leon._ +import leon.test._ +import purescala.Definitions.Program +import java.io.File + +class XLangDesugaringSuite extends LeonRegressionSuite { + // Hard-code output directory, for Eclipse purposes + + val pipeline = frontends.scalac.ExtractionPhase andThen new utils.PreprocessingPhase(true) + + def testFrontend(f: File, forError: Boolean) = { + test ("Testing " + f.getName) { + val ctx = createLeonContext() + if (forError) { + intercept[LeonFatalError]{ + pipeline.run(ctx, List(f.getAbsolutePath)) + } + } else { + pipeline.run(ctx, List(f.getAbsolutePath)) + } + } + + } + + private def forEachFileIn(path : String)(block : File => Unit) { + val fs = filesInResourceDir(path, _.endsWith(".scala")) + + for(f <- fs) { + block(f) + } + } + + val baseDir = "regression/xlang/" + + forEachFileIn(baseDir+"passing/") { f => + testFrontend(f, false) + } + forEachFileIn(baseDir+"error/") { f => + testFrontend(f, true) + } + +} diff --git a/src/test/scala/leon/test/TestSilentReporter.scala b/src/test/scala/leon/test/TestSilentReporter.scala index 2cf9ea4f7f6c78d4f07002a01f434a150e0d9034..2a8761584222f02c1e4bf85b6fd031c603771079 100644 --- a/src/test/scala/leon/test/TestSilentReporter.scala +++ b/src/test/scala/leon/test/TestSilentReporter.scala @@ -13,3 +13,10 @@ class TestSilentReporter extends DefaultReporter(Set()) { case _ => } } + +class TestErrorReporter extends DefaultReporter(Set()) { + override def emit(msg: Message): Unit = msg match { + case Message(this.ERROR | this.FATAL, _, _) => super.emit(msg) + case _ => + } +} diff --git a/src/test/scala/leon/unit/purescala/ExprOpsSuite.scala b/src/test/scala/leon/unit/purescala/ExprOpsSuite.scala index b448953acf1856cf1df12289880000226f78f8bf..827596fc6cd116aefc752a37aeb30647e3ebdf99 100644 --- a/src/test/scala/leon/unit/purescala/ExprOpsSuite.scala +++ b/src/test/scala/leon/unit/purescala/ExprOpsSuite.scala @@ -279,4 +279,44 @@ class ExprOpsSuite extends LeonTestSuite with helpers.WithLikelyEq with helpers. } } + + test("preMapWithContext") { ctx => + val expr = Plus(bi(1), Minus(bi(2), bi(3))) + def op(e : Expr, set: Set[Int]): (Option[Expr], Set[Int]) = e match { + case Minus(InfiniteIntegerLiteral(two), e2) if two == BigInt(2) => (Some(bi(2)), set) + case InfiniteIntegerLiteral(one) if one == BigInt(1) => (Some(bi(2)), set) + case InfiniteIntegerLiteral(two) if two == BigInt(2) => (Some(bi(42)), set) + case _ => (None, set) + } + + assert(preMapWithContext(op, false)(expr, Set()) === Plus(bi(2), bi(2))) + assert(preMapWithContext(op, true)(expr, Set()) === Plus(bi(42), bi(42))) + + val expr2 = Let(x.id, bi(1), Let(y.id, bi(2), Plus(x, y))) + def op2(e: Expr, bindings: Map[Identifier, BigInt]): (Option[Expr], Map[Identifier, BigInt]) = e match { + case Let(id, InfiniteIntegerLiteral(v), body) => (None, bindings + (id -> v)) + case Variable(id) => (bindings.get(id).map(v => InfiniteIntegerLiteral(v)), bindings) + case _ => (None, bindings) + } + + assert(preMapWithContext(op2, false)(expr2, Map()) === Let(x.id, bi(1), Let(y.id, bi(2), Plus(bi(1), bi(2))))) + + def op3(e: Expr, bindings: Map[Identifier, BigInt]): (Option[Expr], Map[Identifier, BigInt]) = e match { + case Let(id, InfiniteIntegerLiteral(v), body) => (Some(body), bindings + (id -> v)) + case Variable(id) => (bindings.get(id).map(v => InfiniteIntegerLiteral(v)), bindings) + case _ => (None, bindings) + } + assert(preMapWithContext(op3, true)(expr2, Map()) === Plus(bi(1), bi(2))) + + + val expr4 = Plus(Let(y.id, bi(2), y), + Let(y.id, bi(4), y)) + def op4(e: Expr, bindings: Map[Identifier, BigInt]): (Option[Expr], Map[Identifier, BigInt]) = e match { + case Let(id, InfiniteIntegerLiteral(v), body) => (Some(body), if(bindings.contains(id)) bindings else (bindings + (id -> v))) + case Variable(id) => (bindings.get(id).map(v => InfiniteIntegerLiteral(v)), bindings) + case _ => (None, bindings) + } + assert(preMapWithContext(op4, true)(expr4, Map()) === Plus(bi(2), bi(4))) + } + } diff --git a/testcases/verification/xlang/AbsFun.scala b/testcases/verification/xlang/AbsFun.scala index fe37632df68bc4ae1c164bb034902e45b18365c4..a6ff9679e27fc32ff4dd8b62a1cf2170386083d8 100644 --- a/testcases/verification/xlang/AbsFun.scala +++ b/testcases/verification/xlang/AbsFun.scala @@ -35,11 +35,7 @@ object AbsFun { isPositive(t, k)) if(k < tab.length) { - val nt = if(tab(k) < 0) { - t.updated(k, -tab(k)) - } else { - t.updated(k, tab(k)) - } + val nt = t.updated(k, if(tab(k) < 0) -tab(k) else tab(k)) while0(nt, k+1, tab) } else { (t, k) @@ -54,11 +50,7 @@ object AbsFun { 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)) - } + val nt = t.updated(k, if(t(k) < 0) -t(k) else t(k)) isPositive(nt, k+1) } else true } holds