diff --git a/src/main/scala/leon/purescala/Definitions.scala b/src/main/scala/leon/purescala/Definitions.scala index cc8003c5ab9e9baafce014471dd47bd98d772cde..ea08be2f4f3fcd523679e4fe11d396cbe0cd3c13 100644 --- a/src/main/scala/leon/purescala/Definitions.scala +++ b/src/main/scala/leon/purescala/Definitions.scala @@ -112,7 +112,7 @@ object Definitions { val resSet: CallGraph = (for(funDef <- definedFunctions) yield { funDef.precondition.map(treeCatamorphism[CallGraph](convert, combine, compute(funDef)_, _)).getOrElse(Set.empty) ++ funDef.body.map(treeCatamorphism[CallGraph](convert, combine, compute(funDef)_, _)).getOrElse(Set.empty) ++ - funDef.postcondition.map( pc => treeCatamorphism[CallGraph](convert, combine, compute(funDef)_, pc._2)).getOrElse(Set.empty) + funDef.postcondition.map(pc => treeCatamorphism[CallGraph](convert, combine, compute(funDef)_, pc._2)).getOrElse(Set.empty) }).foldLeft(Set[(FunDef, FunDef)]())(_ ++ _) var callers: Map[FunDef,Set[FunDef]] = diff --git a/src/main/scala/leon/termination/ChainBuilder.scala b/src/main/scala/leon/termination/ChainBuilder.scala index 741b7c2b65cbd5084d028dc1480658dd935aa1ad..f6ec38673dd707f0f3a3840065ed61561fe6dadc 100644 --- a/src/main/scala/leon/termination/ChainBuilder.scala +++ b/src/main/scala/leon/termination/ChainBuilder.scala @@ -21,7 +21,7 @@ final case class Chain(chain: List[Relation]) { override def hashCode(): Int = id def funDef : FunDef = chain.head.funDef - def funDefs : Set[FunDef] = chain.map(_.funDef) toSet + def funDefs : Set[FunDef] = chain.map(_.funDef).toSet lazy val size: Int = chain.size @@ -38,7 +38,7 @@ final case class Chain(chain: List[Relation]) { case Relation(_, path, FunctionInvocation(fd, args)) :: xs => val formalArgs = fd.args.map(_.id) val freshFormalArgVars = formalArgs.map(_.freshen.toVariable) - val formalArgsMap: Map[Identifier, Expr] = formalArgs zip freshFormalArgVars toMap + val formalArgsMap: Map[Identifier, Expr] = (formalArgs zip freshFormalArgVars).toMap val (newPath, newArgs) = (path.map(replaceFromIDs(subst, _)), args.map(replaceFromIDs(subst, _))) val constraints = newPath ++ (freshFormalArgVars zip newArgs).map(p => Equals(p._1, p._2)) constraints ++ rec(xs, formalArgsMap) @@ -68,14 +68,14 @@ final case class Chain(chain: List[Relation]) { val mappedArgs = args.map(replaceFromIDs(mapping, _)) val newMapping = fd.args.map(_.id).zip(mappedArgs).toMap // We assume we have a body at this point. It would be weird to have gotten here without one... - val expr = hoistIte(expandLets(matchToIfThenElse(fd.getBody))) + val expr = hoistIte(expandLets(matchToIfThenElse(fd.body.get))) val inlinedExpr = replaceFromIDs(newMapping, expr) inlinedExpr:: rec(xs, newMapping) case Nil => Nil } - val body = hoistIte(expandLets(matchToIfThenElse(funDef.getBody))) - body :: rec(chain, funDef.args.map(arg => arg.id -> arg.toVariable) toMap) + val body = hoistIte(expandLets(matchToIfThenElse(funDef.body.get))) + body :: rec(chain, funDef.args.map(arg => arg.id -> arg.toVariable).toMap) } } diff --git a/src/main/scala/leon/termination/ChainComparator.scala b/src/main/scala/leon/termination/ChainComparator.scala index 9a6352a2a59460f021cb085d93d99df5dbb8aee4..278b4bc50ff46629ab0f8bb4062f182ee11a0421 100644 --- a/src/main/scala/leon/termination/ChainComparator.scala +++ b/src/main/scala/leon/termination/ChainComparator.scala @@ -12,13 +12,6 @@ object ChainComparator { def init : Unit = StructuralSize.init - def sizeDecreasing(e1: TypedExpr, e2s: Seq[(Seq[Expr], Expr)]) = _sizeDecreasing(e1, e2s map { - case (path, e2) => (path, exprToTypedExpr(e2)) - }) - def sizeDecreasing(e1: Expr, e2s: Seq[(Seq[Expr], Expr)]) = _sizeDecreasing(e1, e2s map { - case (path, e2) => (path, exprToTypedExpr(e2)) - }) - private object ContainerType { def unapply(c: ClassType): Option[(CaseClassDef, Seq[(Identifier, TypeTree)])] = c match { case CaseClassType(classDef) => @@ -29,32 +22,32 @@ object ChainComparator { } } - private def _sizeDecreasing(te1: TypedExpr, te2s: Seq[(Seq[Expr], TypedExpr)]) : Expr = te1 match { - case TypedExpr(e1, ContainerType(def1, types1)) => Or(types1.zipWithIndex map { case ((id1, type1), index) => - val newTe1 = TypedExpr(CaseClassSelector(def1, e1, id1), type1) - val newTe2s = te2s.map({ - case (path, TypedExpr(e2, ContainerType(def2, types2))) => - val (id2, type2) = types2(index) - (path, TypedExpr(CaseClassSelector(def2, e2, id2), type2)) - case (_, te2) => scala.sys.error("Unexpected input combinations: " + te1 + " " + te2) + def sizeDecreasing(e1: Expr, e2s: Seq[(Seq[Expr], Expr)]) : Expr = e1.getType match { + case ContainerType(def1, fields1) => Or(fields1.zipWithIndex map { case ((id1, type1), index) => + sizeDecreasing(CaseClassSelector(def1, e1, id1), e2s.map { case (path, e2) => + e2.getType match { + case ContainerType(def2, fields2) => (path, CaseClassSelector(def2, e2, fields2(index)._1)) + case _ => scala.sys.error("Unexpected input combinations: " + e1 + " " + e2) + } }) - _sizeDecreasing(newTe1, newTe2s) }) - case TypedExpr(e1, TupleType(types1)) => Or(types1.zipWithIndex map { case (type1, index) => - val newTe1 = TypedExpr(TupleSelect(e1, index + 1), type1) - val newTe2s = te2s.map({ - case (path, TypedExpr(e2, TupleType(types2))) => (path, TypedExpr(TupleSelect(e2, index + 1), types2(index))) - case (_, te2) => scala.sys.error("Unexpected input combinations: " + te1 + " " + te2) + case TupleType(types1) => Or((0 until types1.length) map { case index => + sizeDecreasing(TupleSelect(e1, index + 1), e2s.map { case (path, e2) => + e2.getType match { + case TupleType(_) => (path, TupleSelect(e2, index + 1)) + case _ => scala.sys.error("Unexpected input combination: " + e1 + " " + e2) + } }) - _sizeDecreasing(newTe1, newTe2s) }) - case TypedExpr(_, _: ClassType) => And(te2s map { - case (path, te2 @ TypedExpr(_, _: ClassType)) => Implies(And(path), GreaterThan(size(te1), size(te2))) - case (_, te2) => scala.sys.error("Unexpected input combinations: " + te1 + " " + te2) + case c: ClassType => And(e2s map { case (path, e2) => + e2.getType match { + case c2: ClassType => Implies(And(path), GreaterThan(size(e1), size(e2))) + case _ => scala.sys.error("Unexpected input combination: " + e1 + " " + e2) + } }) - case TypedExpr(_, BooleanType) => BooleanLiteral(false) - case TypedExpr(_, Int32Type) => BooleanLiteral(false) - case _ => scala.sys.error("Unexpected type " + te1.tpe) + case BooleanType => BooleanLiteral(false) + case Int32Type => BooleanLiteral(false) + case tpe => scala.sys.error("Unexpected type " + tpe) } private sealed abstract class NumericEndpoint { @@ -133,13 +126,13 @@ object ChainComparator { } def endpoint(expr: Expr) : NumericEndpoint = expr match { - case IfExpr(cond, then, elze) => matches(cond) match { + case IfExpr(cond, thenn, elze) => matches(cond) match { case NoEndpoint => - endpoint(then) min endpoint(elze) + endpoint(thenn) min endpoint(elze) case ep => - val terminatingThen = functionCallsOf(then).forall(fi => checker.terminates(fi.funDef).isGuaranteed) + val terminatingThen = functionCallsOf(thenn).forall(fi => checker.terminates(fi.funDef).isGuaranteed) val terminatingElze = functionCallsOf(elze).forall(fi => checker.terminates(fi.funDef).isGuaranteed) - val thenEndpoint = if (terminatingThen) ep max endpoint(then) else endpoint(then) + val thenEndpoint = if (terminatingThen) ep max endpoint(thenn) else endpoint(thenn) val elzeEndpoint = if (terminatingElze) ep.inverse max endpoint(elze) else endpoint(elze) thenEndpoint max elzeEndpoint } @@ -151,54 +144,45 @@ object ChainComparator { }) } - def numericConverging(e1: TypedExpr, e2s: Seq[(Seq[Expr], Expr)], cluster: Set[Chain], checker: TerminationChecker) = _numericConverging(e1, e2s map { - case (path, e2) => (path, exprToTypedExpr(e2)) - }, cluster, checker) - def numericConverging(e1: Expr, e2s: Seq[(Seq[Expr], Expr)], cluster: Set[Chain], checker: TerminationChecker) = _numericConverging(e1, e2s map { - case (path, e2) => (path, exprToTypedExpr(e2)) - }, cluster, checker) - - private def _numericConverging(te1: TypedExpr, te2s: Seq[(Seq[Expr], TypedExpr)], cluster: Set[Chain], checker: TerminationChecker) : Expr = te1 match { - case TypedExpr(e1, ContainerType(def1, types1)) => Or(types1.zipWithIndex map { case ((id1, type1), index) => - val newTe1 = TypedExpr(CaseClassSelector(def1, e1, id1), type1) - val newTe2s = te2s.map({ - case (path, TypedExpr(e2, ContainerType(def2, types2))) => - val (id2, type2) = types2(index) - (path, TypedExpr(CaseClassSelector(def2, e2, id2), type2)) - case (_, te2) => scala.sys.error("Unexpected input combinations: " + te1 + " " + te2) - }) - _numericConverging(newTe1, newTe2s, cluster, checker) + def numericConverging(e1: Expr, e2s: Seq[(Seq[Expr], Expr)], cluster: Set[Chain], checker: TerminationChecker) : Expr = e1.getType match { + case ContainerType(def1, fields1) => Or(fields1.zipWithIndex map { case ((id1, type1), index) => + numericConverging(CaseClassSelector(def1, e1, id1), e2s.map { case (path, e2) => + e2.getType match { + case ContainerType(def2, fields2) => (path, CaseClassSelector(def2, e2, fields2(index)._1)) + case _ => scala.sys.error("Unexpected input combination: " + e1 + " " + e2) + } + }, cluster, checker) }) - case TypedExpr(e1, TupleType(types1)) => Or(types1.zipWithIndex map { case (type1, index) => - val newTe1 = TypedExpr(TupleSelect(e1, index + 1), type1) - val newTe2s = te2s.map({ - case (path, TypedExpr(e2, TupleType(types2))) => (path, TypedExpr(TupleSelect(e2, index + 1), types2(index))) - case (_, te2) => scala.sys.error("Unexpected input combinations: " + te1 + " " + te2) - }) - _numericConverging(newTe1, newTe2s, cluster, checker) + case TupleType(types) => Or((0 until types.length) map { case index => + numericConverging(TupleSelect(e1, index + 1), e2s.map { case (path, e2) => + e2.getType match { + case TupleType(_) => (path, TupleSelect(e2, index + 1)) + case _ => scala.sys.error("Unexpected input combination: " + e1 + " " + e2) + } + }, cluster, checker) }) - case TypedExpr(e1, Int32Type) => numericEndpoint(e1, cluster, checker) match { - case UpperBoundEndpoint => And(te2s map { - case (path, TypedExpr(e2, Int32Type)) => Implies(And(path), GreaterThan(e1, e2)) - case (_, te2) => scala.sys.error("Unexpected input combinations: " + te1 + " " + te2) + case Int32Type => numericEndpoint(e1, cluster, checker) match { + case UpperBoundEndpoint => And(e2s map { + case (path, e2) if e2.getType == Int32Type => Implies(And(path), GreaterThan(e1, e2)) + case (_, e2) => scala.sys.error("Unexpected input combinations: " + e1 + " " + e2) }) - case LowerBoundEndpoint => And(te2s map { - case (path, TypedExpr(e2, Int32Type)) => Implies(And(path), LessThan(e1, e2)) - case (_, te2) => scala.sys.error("Unexpected input combinations: " + te1 + " " + te2) + case LowerBoundEndpoint => And(e2s map { + case (path, e2) if e2.getType == Int32Type => Implies(And(path), LessThan(e1, e2)) + case (_, e2) => scala.sys.error("Unexpected input combinations: " + e1 + " " + e2) }) - case AnyEndpoint => Or(And(te2s map { - case (path, TypedExpr(e2, Int32Type)) => Implies(And(path), GreaterThan(e1, e2)) - case (_, te2) => scala.sys.error("Unexpected input combinations: " + te1 + " " + te2) - }), And(te2s map { - case (path, TypedExpr(e2, Int32Type)) => Implies(And(path), LessThan(e1, e2)) - case (_, te2) => scala.sys.error("Unexpected input combinations: " + te1 + " " + te2) + case AnyEndpoint => Or(And(e2s map { + case (path, e2) if e2.getType == Int32Type => Implies(And(path), GreaterThan(e1, e2)) + case (_, e2) => scala.sys.error("Unexpected input combinations: " + e1 + " " + e2) + }), And(e2s map { + case (path, e2) if e2.getType == Int32Type => Implies(And(path), LessThan(e1, e2)) + case (_, e2) => scala.sys.error("Unexpected input combinations: " + e1 + " " + e2) })) case InnerEndpoint => BooleanLiteral(false) case NoEndpoint => BooleanLiteral(false) } - case TypedExpr(_, _: ClassType) => BooleanLiteral(false) - case TypedExpr(_, BooleanType) => BooleanLiteral(false) - case _ => scala.sys.error("Unexpected type " + te1.tpe) + case _: ClassType => BooleanLiteral(false) + case BooleanType => BooleanLiteral(false) + case tpe => scala.sys.error("Unexpected type " + tpe) } } diff --git a/src/main/scala/leon/termination/ChainProcessor.scala b/src/main/scala/leon/termination/ChainProcessor.scala index 06c98ed31e9bff826aa4f36906c6f34b1e9fe14c..2d6ad5b412451f60e8de81552c8e743457329101 100644 --- a/src/main/scala/leon/termination/ChainProcessor.scala +++ b/src/main/scala/leon/termination/ChainProcessor.scala @@ -56,7 +56,7 @@ class ChainProcessor(checker: TerminationChecker) extends Processor(checker) wit all.map(cluster => cluster.toSeq.sortBy(_.size).foldLeft(Set[Chain]())({ case (acc, chain) => val chainElements : Set[Relation] = chain.chain.toSet val seenElements : Set[Relation] = acc.map(_.chain).flatten.toSet - if (chainElements -- seenElements nonEmpty) acc + chain else acc + if ((chainElements -- seenElements).nonEmpty) acc + chain else acc })).filter(_.nonEmpty) } @@ -119,10 +119,10 @@ class ChainProcessor(checker: TerminationChecker) extends Processor(checker) wit val (okPairs, nokPairs) = numericCleared.partition(_._2.isEmpty) val nok = nokPairs.map(_._1).toSet val (ok, transitiveNok) = okPairs.map(_._1).partition({ fd => - checker.program.transitiveCallees(fd) intersect nok isEmpty + (checker.program.transitiveCallees(fd) intersect nok).isEmpty }) val allNok = nok ++ transitiveNok - val newProblems = if (allNok nonEmpty) List(Problem(allNok)) else Nil + val newProblems = if (allNok.nonEmpty) List(Problem(allNok)) else Nil (ok.map(Cleared(_)), newProblems) } } diff --git a/src/main/scala/leon/termination/ComponentProcessor.scala b/src/main/scala/leon/termination/ComponentProcessor.scala index 58d1da4a538ffdcf932377969840ddcae7a4d4e0..1e980370b31e4a3808ce7a9110cbadd1112629b8 100644 --- a/src/main/scala/leon/termination/ComponentProcessor.scala +++ b/src/main/scala/leon/termination/ComponentProcessor.scala @@ -26,8 +26,8 @@ class ComponentProcessor(checker: TerminationChecker) extends Processor(checker) }) val terminating = problem.funDefs.filter(terminates(_)) - assert(components.forall(scc => (scc subsetOf terminating) || (scc intersect terminating isEmpty))) - val newProblems = components.filter(scc => scc intersect terminating isEmpty).map(Problem(_)) + assert(components.forall(scc => (scc subsetOf terminating) || (scc intersect terminating).isEmpty)) + val newProblems = components.filter(scc => (scc intersect terminating).isEmpty).map(Problem(_)) (terminating.map(Cleared(_)), newProblems) } } diff --git a/src/main/scala/leon/termination/Processor.scala b/src/main/scala/leon/termination/Processor.scala index 299d5b338c7ea976df17da2527cac72fa11db795..e230c870170d9dcf6986c629d598295ecad73565 100644 --- a/src/main/scala/leon/termination/Processor.scala +++ b/src/main/scala/leon/termination/Processor.scala @@ -23,31 +23,36 @@ abstract class Processor(val checker: TerminationChecker) { val reporter = checker.context.reporter - def run(problem: Problem): (TraversableOnce[Result], TraversableOnce[Problem]) + protected def run(problem: Problem): (TraversableOnce[Result], TraversableOnce[Problem]) + + def process(problem: Problem): (TraversableOnce[Result], TraversableOnce[Problem]) = run(problem) } object Solvable { import scala.collection.mutable.{Set => MutableSet} private val strengthened : MutableSet[FunDef] = MutableSet() - private def strengthenPostcondition(funDef: FunDef, cmp: (Expr, TypedExpr) => Expr) + private def strengthenPostcondition(funDef: FunDef, cmp: (Expr, Expr) => Expr) (implicit solver: Processor with Solvable) : Boolean = if (!funDef.hasBody) false else { assert(solver.checker.terminates(funDef).isGuaranteed) - val postcondition = funDef.postcondition - val args = funDef.args.map(_.toVariable) - val typedResult = TypedExpr(ResultVariable(), funDef.returnType) - val sizePost = cmp(Tuple(args), typedResult) - funDef.postcondition = Some(And(postcondition.toSeq :+ sizePost)) + val old = funDef.postcondition + val (res, postcondition) = { + val (res, post) = old.getOrElse(FreshIdentifier("res").setType(funDef.returnType) -> BooleanLiteral(true)) + val args = funDef.args.map(_.toVariable) + val sizePost = cmp(Tuple(funDef.args.map(_.toVariable)), res.toVariable) + (res, And(post, sizePost)) + } + + funDef.postcondition = Some(res -> postcondition) val prec = matchToIfThenElse(funDef.precondition.getOrElse(BooleanLiteral(true))) - val post = matchToIfThenElse(funDef.postcondition.get) val body = matchToIfThenElse(funDef.body.get) - val resFresh = FreshIdentifier("result", true).setType(body.getType) - val formula = Implies(prec, Let(resFresh, body, replace(Map(ResultVariable() -> Variable(resFresh)), post))) + val post = matchToIfThenElse(postcondition) + val formula = Implies(prec, Let(res, body, post)) if (!solver.isAlwaysSAT(formula)) { - funDef.postcondition = postcondition + funDef.postcondition = old strengthened.add(funDef) false } else { @@ -74,19 +79,36 @@ object Solvable { trait Solvable { self: Processor => - private var solvers: List[Solver] = null + override def process(problem: Problem): (TraversableOnce[Result], TraversableOnce[Problem]) = { + try { + self.run(problem) + } finally { + destroySolvers + } + } + + private var solvers: List[SolverFactory[Solver]] = null + private var lastDefs: Set[FunDef] = Set() def strengthenPostconditions(funDefs: Set[FunDef]) = Solvable.strengthenPostconditions(funDefs)(this) private def initSolvers { - val program : Program = self.checker.program - val allDefs : Seq[Definition] = program.mainObject.defs ++ StructuralSize.defs - val newProgram : Program = program.copy(mainObject = program.mainObject.copy(defs = allDefs)) - val context : LeonContext = self.checker.context.copy(reporter = new QuietReporter()) - - val solvers0 = new TrivialSolver(context) :: new FairZ3Solver(context) :: Nil - solvers = solvers0.map(new TimeoutSolver(_, 500)) - solvers.foreach(_.setProgram(newProgram)) + val structDefs = StructuralSize.defs + if (structDefs != lastDefs || solvers == null) { + destroySolvers + + val program : Program = self.checker.program + val allDefs : Seq[Definition] = program.mainObject.defs ++ structDefs + val newProgram : Program = program.copy(mainObject = program.mainObject.copy(defs = allDefs)) + val context : LeonContext = self.checker.context + + val solvers0 = new FairZ3SolverFactory(context, newProgram) :: Nil + solvers = solvers0.map(_.withTimeout(500)) + } + } + + protected def destroySolvers { + if (solvers != null) solvers.foreach(_.free()) } type Solution = (Option[Boolean], Map[Identifier, Expr]) @@ -104,19 +126,11 @@ trait Solvable { self: Processor => val expr = searchAndReplace(dangerousCallsMap.get, recursive=false)(problem) object Solved { - var superseeded : Set[String] = Set.empty[String] - def unapply(se: Solver): Option[Solution] = { - if(superseeded(se.name) || superseeded(se.description)) { - None - } else { - superseeded = superseeded ++ Set(se.superseeds: _*) - - se.init() - val (satResult, model) = se.solveSAT(expr) - - if (!satResult.isDefined) None - else Some(satResult, model) - } + def unapply(se: SolverFactory[Solver]): Option[Solution] = { + val (satResult, model) = SimpleSolverAPI(se).solveSAT(expr) + + if (!satResult.isDefined) None + else Some(satResult -> model) } } @@ -176,7 +190,7 @@ class ProcessingPipeline(program: Program, context: LeonContext, _processors: Pr def clear(fd: FunDef) : Boolean = { lazy val unsolvedDefs = unsolved.map(_.funDefs).flatten.toSet lazy val problemDefs = problems.map({ case (problem, _) => problem.funDefs }).flatten.toSet - def issue(defs: Set[FunDef]) : Boolean = defs(fd) || (defs intersect program.transitiveCallees(fd) nonEmpty) + def issue(defs: Set[FunDef]) : Boolean = defs(fd) || (defs intersect program.transitiveCallees(fd)).nonEmpty ! (issue(unsolvedDefs) || issue(problemDefs)) } @@ -192,7 +206,7 @@ class ProcessingPipeline(program: Program, context: LeonContext, _processors: Pr printQueue val (problem, index) = problems.head val processor : Processor = processors(index) - val (_results, nextProblems) = processor.run(problem) + val (_results, nextProblems) = processor.process(problem) val results = _results.toList printResult(results) diff --git a/src/main/scala/leon/termination/RelationBuilder.scala b/src/main/scala/leon/termination/RelationBuilder.scala index 8e50fcecbf530dd4c58585d0fe5c5a82476a2098..11b411b0a88aaeff961ca7ff40611543fd877742 100644 --- a/src/main/scala/leon/termination/RelationBuilder.scala +++ b/src/main/scala/leon/termination/RelationBuilder.scala @@ -33,9 +33,9 @@ object RelationBuilder { val vb = visit(b, Equals(Variable(i), e) :: path) ve ++ vb - case IfExpr(cond, then, elze) => + case IfExpr(cond, thenn, elze) => val vc = visit(cond, path) - val vt = visit(then, cond :: path) + val vt = visit(thenn, cond :: path) val ve = visit(elze, Not(cond) :: path) vc ++ vt ++ ve @@ -44,14 +44,14 @@ object RelationBuilder { case x :: xs => visit(x, p ++ path) ++ resolveAnds(xs, x :: p) case Nil => Set() } - resolveAnds(es toList, Nil) + resolveAnds(es.toList, Nil) case Or(es) => def resolveOrs(ors: List[Expr], p: List[Expr]): Set[Relation] = ors match { case x :: xs => visit(x, p ++ path) ++ resolveOrs(xs, Not(x) :: p) case Nil => Set() } - resolveOrs(es toList, Nil) + resolveOrs(es.toList, Nil) case UnaryOperator(e, _) => visit(e, path) @@ -65,9 +65,9 @@ object RelationBuilder { } val precondition = funDef.precondition getOrElse BooleanLiteral(true) - val precRelations = funDef.precondition.map(e => visit(simplifyLets(matchToIfThenElse(e)), Nil)).flatten.toSet - val bodyRelations = funDef.body.map(e => visit(simplifyLets(matchToIfThenElse(e)), List(precondition))).flatten.toSet - val postRelations = funDef.postcondition.map(e => visit(simplifyLets(matchToIfThenElse(e)), Nil)).flatten.toSet + val precRelations = funDef.precondition.map(e => visit(simplifyLets(matchToIfThenElse(e)), Nil)) getOrElse Set() + val bodyRelations = funDef.body.map(e => visit(simplifyLets(matchToIfThenElse(e)), List(precondition))) getOrElse Set() + val postRelations = funDef.postcondition.map(e => visit(simplifyLets(matchToIfThenElse(e._2)), Nil)) getOrElse Set() val relations = precRelations ++ bodyRelations ++ postRelations relationCache(funDef) = relations relations diff --git a/src/main/scala/leon/termination/RelationComparator.scala b/src/main/scala/leon/termination/RelationComparator.scala index e6a6c9721683d4b7636f931752e1b7974dbcd4ab..9f3985b2c74d771e15cb88e253b6c1b6f9bb2586 100644 --- a/src/main/scala/leon/termination/RelationComparator.scala +++ b/src/main/scala/leon/termination/RelationComparator.scala @@ -12,15 +12,9 @@ object RelationComparator { def init : Unit = StructuralSize.init - def sizeDecreasing(e1: TypedExpr, e2: TypedExpr) = GreaterThan(size(e1), size(e2)) - def sizeDecreasing(e1: Expr, e2: TypedExpr) = GreaterThan(size(e1), size(e2)) - def sizeDecreasing(e1: TypedExpr, e2: Expr) = GreaterThan(size(e1), size(e2)) - def sizeDecreasing(e1: Expr, e2: Expr) = GreaterThan(size(e1), size(e2)) + def sizeDecreasing(e1: Expr, e2: Expr) = GreaterThan(size(e1), size(e2)) - def softDecreasing(e1: TypedExpr, e2: TypedExpr) = GreaterEquals(size(e1), size(e2)) - def softDecreasing(e1: Expr, e2: TypedExpr) = GreaterEquals(size(e1), size(e2)) - def softDecreasing(e1: TypedExpr, e2: Expr) = GreaterEquals(size(e1), size(e2)) - def softDecreasing(e1: Expr, e2: Expr) = GreaterEquals(size(e1), size(e2)) + def softDecreasing(e1: Expr, e2: Expr) = GreaterEquals(size(e1), size(e2)) } // vim: set ts=4 sw=4 et: diff --git a/src/main/scala/leon/termination/RelationProcessor.scala b/src/main/scala/leon/termination/RelationProcessor.scala index 4f2786528b45daa95f8be11dda4c3fd60942746a..24527eb92aea5713b26602343a4ebf949a06be7e 100644 --- a/src/main/scala/leon/termination/RelationProcessor.scala +++ b/src/main/scala/leon/termination/RelationProcessor.scala @@ -70,7 +70,7 @@ class RelationProcessor(checker: TerminationChecker) extends Processor(checker) assert(terminating ++ nonTerminating == problem.funDefs) val results = terminating.map(Cleared(_)).toList - val newProblems = if (problem.funDefs intersect nonTerminating nonEmpty) List(Problem(nonTerminating)) else Nil + val newProblems = if ((problem.funDefs intersect nonTerminating).nonEmpty) List(Problem(nonTerminating)) else Nil (results, newProblems) } } diff --git a/src/main/scala/leon/termination/StructuralSize.scala b/src/main/scala/leon/termination/StructuralSize.scala index 412c486604fd848ab2328a809c6b86bd799fe1ff..c85f2408564a0ca513f2b3b4361e48c80cc975fd 100644 --- a/src/main/scala/leon/termination/StructuralSize.scala +++ b/src/main/scala/leon/termination/StructuralSize.scala @@ -7,18 +7,11 @@ import purescala.TypeTrees._ import purescala.Definitions._ import purescala.Common._ -case class TypedExpr(expr: Expr, tpe: TypeTree) - object StructuralSize { import scala.collection.mutable.{Map => MutableMap} - implicit def exprToTypedExpr(expr: Expr): TypedExpr = { - assert(expr.getType != Untyped) - TypedExpr(expr, expr.getType) - } - private val sizeFunctionCache : MutableMap[TypeTree, FunDef] = MutableMap() - def size(typedExpr: TypedExpr) : Expr = { + def size(expr: Expr) : Expr = { def funDef(tpe: TypeTree, cases: => Seq[MatchCase]) = { // we want to reuse generic size functions for sub-types val argumentType = tpe match { @@ -34,12 +27,13 @@ object StructuralSize { sizeFunctionCache(argumentType) = fd val body = simplifyLets(matchToIfThenElse(MatchExpr(argument.toVariable, cases))) + val postId = FreshIdentifier("res", false).setType(Int32Type) val postSubcalls = functionCallsOf(body).map(GreaterThan(_, IntLiteral(0))).toSeq - val postRecursive = GreaterThan(ResultVariable(), IntLiteral(0)) + val postRecursive = GreaterThan(Variable(postId), IntLiteral(0)) val postcondition = And(postSubcalls :+ postRecursive) fd.body = Some(body) - fd.postcondition = Some(postcondition) + fd.postcondition = Some(postId, postcondition) fd } } @@ -48,20 +42,20 @@ object StructuralSize { val c = _c.asInstanceOf[CaseClassDef] // required by leon framework val arguments = c.fields.map(f => f -> f.id.freshen) val argumentPatterns = arguments.map(p => WildcardPattern(Some(p._2))) - val sizes = arguments.map(p => size(TypedExpr(Variable(p._2), p._1.tpe))) + val sizes = arguments.map(p => size(Variable(p._2))) val result = sizes.foldLeft[Expr](IntLiteral(1))(Plus(_,_)) SimpleCase(CaseClassPattern(None, c, argumentPatterns), result) } - typedExpr match { - case TypedExpr(expr, a: AbstractClassType) => + expr.getType match { + case a: AbstractClassType => val sizeFd = funDef(a, a.classDef.knownChildren map caseClassType2MatchCase) FunctionInvocation(sizeFd, Seq(expr)) - case TypedExpr(expr, c: CaseClassType) => + case c: CaseClassType => val sizeFd = funDef(c, Seq(caseClassType2MatchCase(c.classDef))) FunctionInvocation(sizeFd, Seq(expr)) - case TypedExpr(expr, TupleType(argTypes)) => argTypes.zipWithIndex.map({ - case (tpe, index) => size(TypedExpr(TupleSelect(expr, index + 1), tpe)) + case TupleType(argTypes) => argTypes.zipWithIndex.map({ + case (_, index) => size(TupleSelect(expr, index + 1)) }).foldLeft[Expr](IntLiteral(0))(Plus(_,_)) case _ => IntLiteral(0) } diff --git a/src/test/resources/regression/termination/looping/Termination_failling1.scala b/src/test/resources/regression/termination/looping/Termination_failling1.scala index 4904c9fc57b2357a32bafe672f977c5732c24e48..17b6be8645527074b5f95b0d0012d5b10c4691c7 100644 --- a/src/test/resources/regression/termination/looping/Termination_failling1.scala +++ b/src/test/resources/regression/termination/looping/Termination_failling1.scala @@ -3,7 +3,7 @@ import leon.Utils._ object Termination { abstract class List case class Cons(head: Int, tail: List) extends List - case class Nil extends List + case class Nil() extends List def looping1(list: List) : Int = looping2(list) diff --git a/src/test/resources/regression/termination/valid/ComplexChains.scala b/src/test/resources/regression/termination/valid/ComplexChains.scala index 568e09bb1215d7a5db11c3c87870e1867834fcd6..68fad00ff27e5db7529c861a59f662af6e43a62f 100644 --- a/src/test/resources/regression/termination/valid/ComplexChains.scala +++ b/src/test/resources/regression/termination/valid/ComplexChains.scala @@ -4,7 +4,7 @@ object ComplexChains { abstract class List case class Cons(head: Int, tail: List) extends List - case class Nil extends List + case class Nil() extends List def f1(list: List): List = list match { case Cons(head, tail) if head > 0 => f2(Cons(1, list)) diff --git a/src/test/resources/regression/termination/valid/ListWithSize.scala b/src/test/resources/regression/termination/valid/ListWithSize.scala index 868cb45561e95552e10b8310c1a17f321515f89b..4e236ae1f7ffac822c932a46168ee31a752b7831 100644 --- a/src/test/resources/regression/termination/valid/ListWithSize.scala +++ b/src/test/resources/regression/termination/valid/ListWithSize.scala @@ -49,7 +49,7 @@ object ListWithSize { def sizesAreEquiv(l: List) : Boolean = { size(l) == sizeTailRec(l) - } holds + }.holds def content(l: List) : Set[Int] = l match { case Nil() => Set.empty[Int] @@ -58,7 +58,7 @@ object ListWithSize { def sizeAndContent(l: List) : Boolean = { size(l) == 0 || content(l) != Set.empty[Int] - } holds + }.holds def drunk(l : List) : List = (l match { case Nil() => Nil() @@ -84,7 +84,7 @@ object ListWithSize { }) ensuring(content(_) == content(l1) ++ content(l2)) @induct - def nilAppend(l : List) : Boolean = (append(l, Nil()) == l) holds + def nilAppend(l : List) : Boolean = (append(l, Nil()) == l).holds // unclear if we needed this--it was meant to force folding //def appendFold(x : Int, xs : List, ys : List) : Boolean = { @@ -93,7 +93,7 @@ object ListWithSize { @induct def appendAssoc(xs : List, ys : List, zs : List) : Boolean = - (append(append(xs, ys), zs) == append(xs, append(ys, zs))) holds + (append(append(xs, ys), zs) == append(xs, append(ys, zs))).holds def revAuxBroken(l1 : List, e : Int, l2 : List) : Boolean = { (append(reverse(l1), Cons(e,l2)) == reverse0(l1, l2)) @@ -106,7 +106,7 @@ object ListWithSize { @induct def sizeAppend(l1 : List, l2 : List) : Boolean = - (size(append(l1, l2)) == size(l1) + size(l2)) holds + (size(append(l1, l2)) == size(l1) + size(l2)).holds // proved with unrolling=4 @induct diff --git a/src/test/resources/regression/termination/valid/SimpInterpret.scala b/src/test/resources/regression/termination/valid/SimpInterpret.scala index bcb7bafc8648364441b380f06933ecf602b1d6f4..fd56989f4a4597e2b9e39fb19acdabd0d2cd6d93 100644 --- a/src/test/resources/regression/termination/valid/SimpInterpret.scala +++ b/src/test/resources/regression/termination/valid/SimpInterpret.scala @@ -55,11 +55,11 @@ object Interpret { def treeBad(t : IntTree) : Boolean = { !(repOk(t) && computesPositive(t) && identityForPositive(t)) - } holds + }.holds def thereIsGoodTree() : Boolean = { !treeBad(If(Less(Const(0),Var()), Var(), Minus(Const(0),Var()))) - } holds + }.holds def main(args : Array[String]) { thereIsGoodTree() diff --git a/src/test/resources/regression/termination/valid/Termination_passing1.scala b/src/test/resources/regression/termination/valid/Termination_passing1.scala index fffb8bca90176257ab730d6a8329eafc73d625e4..ea86b67694431ef9efa7eadb30b0a08cac30b4ea 100644 --- a/src/test/resources/regression/termination/valid/Termination_passing1.scala +++ b/src/test/resources/regression/termination/valid/Termination_passing1.scala @@ -3,7 +3,7 @@ import leon.Utils._ object Termination { abstract class List case class Cons(head: Int, tail: List) extends List - case class Nil extends List + case class Nil() extends List def f1(list: List) : Int = f2(list) diff --git a/src/test/scala/leon/test/termination/TerminationRegression.scala b/src/test/scala/leon/test/termination/TerminationRegression.scala index 0ccc6c8645741f6adfb369dfc2658e29afc6fa93..977da326df20af53b965adb61512c66c25754cce 100644 --- a/src/test/scala/leon/test/termination/TerminationRegression.scala +++ b/src/test/scala/leon/test/termination/TerminationRegression.scala @@ -6,13 +6,9 @@ package termination import leon.termination._ -import org.scalatest.FunSuite - import java.io.File -import TestUtils._ - -class TerminationRegression extends FunSuite { +class TerminationRegression extends LeonTestSuite { private var counter : Int = 0 private def nextInt() : Int = { counter += 1 @@ -21,7 +17,7 @@ class TerminationRegression extends FunSuite { private case class Output(report : TerminationReport, reporter : Reporter) private def mkPipeline : Pipeline[List[String],TerminationReport] = - leon.plugin.ExtractionPhase andThen leon.SubtypingPhase andThen leon.termination.TerminationPhase + leon.plugin.ExtractionPhase andThen leon.utils.SubtypingPhase andThen leon.termination.TerminationPhase private def mkTest(file : File, leonOptions: Seq[LeonOption], forError: Boolean)(block: Output=>Unit) = { val fullName = file.getPath() @@ -37,16 +33,15 @@ class TerminationRegression extends FunSuite { assert(file.exists && file.isFile && file.canRead, "Benchmark %s is not a readable file".format(displayName)) - val ctx = LeonContext( + val ctx = testContext.copy( settings = Settings( synthesis = false, xlang = false, verify = false, termination = true ), - options = leonOptions, - files = List(file), - reporter = new SilentReporter + options = leonOptions.toList, + files = List(file) ) val pipeline = mkPipeline