diff --git a/PERMISSIONS b/PERMISSIONS index ace330f0c1edc8c871c960eeab6d0319836f2aae..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 +WRITERS kuncak psuter rblanc edarulov hojjat READERS @lara mazimmer hardy amin diff --git a/run-tests.sh b/run-tests.sh index cedaea2d3ec3e75c79161e5a33ac3225368a372d..951862c3e850e1ecb859d2385ed3726a22fddb0d 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 --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 --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 --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/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 004d03d6159e07d78dc0b508af3196a5970b66d6..c3e0ca1a6a40fdbf59da5508d076399346ceffe7 100644 --- a/src/main/scala/leon/Evaluator.scala +++ b/src/main/scala/leon/Evaluator.scala @@ -85,7 +85,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)) } } @@ -255,6 +257,37 @@ object Evaluator { 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 7d8469998f53c9abb14c9c84dfa123fb462900c2..89cfe4492b914c630e5122993700f4945f1662b5 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 diff --git a/src/main/scala/leon/FairZ3Solver.scala b/src/main/scala/leon/FairZ3Solver.scala index e56e82ac9d294a0f7ecf6e07504a402aa4614f60..8ec0984ee1994371ad95a95b7367887c923d2ff5 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,11 @@ 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 @@ -192,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) @@ -361,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) { @@ -389,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 => { @@ -406,7 +452,7 @@ 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) @@ -518,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 @@ -566,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) @@ -745,16 +789,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") @@ -970,6 +1011,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)) @@ -1053,6 +1095,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) @@ -1110,8 +1184,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)) { @@ -1148,7 +1242,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.") diff --git a/src/main/scala/leon/FunctionClosure.scala b/src/main/scala/leon/FunctionClosure.scala index d9426510ee76f9703514df4219cff8761db26995..56239d319dca67c9614f0e7d9dd97a7640c8da49 100644 --- a/src/main/scala/leon/FunctionClosure.scala +++ b/src/main/scala/leon/FunctionClosure.scala @@ -9,121 +9,150 @@ 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)))))) + 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))))) + 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).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) => { //still needs to handle the new ids introduced by the patterns + 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..3cc81b703e96179337090ec69b29a1effbcf738b 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), 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/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..4d3dab3d0f84fa215a588712bfe82e57f63abb51 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 @@ -245,6 +250,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 +261,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 +293,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 +351,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 +423,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 +435,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 +466,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 +498,525 @@ 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 - case ExAssign(sym, rhs) => mutableVarSubsts.get(sym) match { - case Some(fun) => { - val Variable(id) = fun() - val rhsTree = rec(rhs) - Assignment(id, rhsTree) - } - case None => { - unit.error(tr.pos, "Undeclared variable.") - throw ImpureCodeEncounteredException(tr) + 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 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 _ => None + } + var handleRest = true + val psExpr = e2 match { + case Some(e3) => e3 + case None => 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) + 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 ExTyped(e,tpt) => rec(e) - case ExIdentifier(sym,tpt) => varSubsts.get(sym) match { - case Some(fun) => fun() - case None => mutableVarSubsts.get(sym) match { - case Some(fun) => fun() + 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, "Unidentified variable.") + unit.error(tr.pos, "Undeclared 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 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 ExTyped(e,tpt) => rec(e) + case ExIdentifier(sym,tpt) => varSubsts.get(sym) match { + case Some(fun) => fun() + case None => mutableVarSubsts.get(sym) match { + case Some(fun) => fun() + case None => { + unit.error(tr.pos, "Unidentified variable.") + throw ImpureCodeEncounteredException(tr) + } } - throw ImpureCodeEncounteredException(tree) } - 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 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) } - 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 +1026,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 +1036,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 +1059,27 @@ 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 _ => 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..a3313663baea11ad915e0229511f0a4426a27b5c 100644 --- a/src/main/scala/leon/plugin/Extractors.scala +++ b/src/main/scala/leon/plugin/Extractors.scala @@ -632,10 +632,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 +649,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 +664,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..bc497dcdd318cdbd7809c357b811002f986908da 100644 --- a/src/main/scala/leon/purescala/Definitions.scala +++ b/src/main/scala/leon/purescala/Definitions.scala @@ -53,6 +53,7 @@ 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)) } /** 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..21a9393b142b177d49ba985988512c5dbe885c5f 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 @@ -249,6 +249,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 +421,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/Trees.scala b/src/main/scala/leon/purescala/Trees.scala index ee3daffb735a3fa97adb1600be050e3b3f258180..3716a41265b6399fc3ea06de189607a7415e2b8b 100644 --- a/src/main/scala/leon/purescala/Trees.scala +++ b/src/main/scala/leon/purescala/Trees.scala @@ -76,7 +76,13 @@ 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) + if(!subTpes.exists(_ == Untyped)) { + setType(TupleType(subTpes)) + } + + } case class TupleSelect(tuple: Expr, index: Int) extends Expr object MatchExpr { @@ -379,6 +385,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 +436,9 @@ 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 e@Epsilon(t) => Some((t, (expr: Expr) => Epsilon(expr).setType(e.getType).setPosInfo(e))) case _ => None } @@ -446,6 +474,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 +493,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 +631,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 +831,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 +852,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) @@ -1241,6 +1285,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 @@ -1270,24 +1332,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) 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/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/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/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/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/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/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(_ == ()) + +}