diff --git a/src/main/scala/leon/evaluators/AbstractEvaluator.scala b/src/main/scala/leon/evaluators/AbstractEvaluator.scala index 2b8baa99424dbd7de9d4d2c4d87c82293e4de2b1..0befea52604aa86c60a2c3820a9abce0bdc9f513 100644 --- a/src/main/scala/leon/evaluators/AbstractEvaluator.scala +++ b/src/main/scala/leon/evaluators/AbstractEvaluator.scala @@ -59,7 +59,7 @@ class AbstractEvaluator(ctx: LeonContext, prog: Program) extends ContextualEvalu protected def e(expr: Expr)(implicit rctx: RC, gctx: GC): (Expr, Expr) = { - implicit def aToC: AbstractEvaluator.this.underlying.RC = DefaultRecContext(rctx.mappings) + implicit def aToC: AbstractEvaluator.this.underlying.RC = DefaultRecContext(rctx.mappings.filter{ case (k, v) => ExprOps.isValue(v) }) expr match { case Variable(id) => (rctx.mappings.get(id), rctx.mappingsAbstract.get(id)) match { diff --git a/src/main/scala/leon/evaluators/Evaluator.scala b/src/main/scala/leon/evaluators/Evaluator.scala index a15febb65c31129db2d5ee37da88cd148dce3806..41cff9a053040d9ce4254a679f711d95760ccb0f 100644 --- a/src/main/scala/leon/evaluators/Evaluator.scala +++ b/src/main/scala/leon/evaluators/Evaluator.scala @@ -46,6 +46,48 @@ abstract class Evaluator(val context: LeonContext, val program: Program) extends trait DeterministicEvaluator extends Evaluator { type Value = Expr + + /**Evaluates the environment first, resolving non-cyclic dependencies, and then evaluate the expression */ + final def evalEnvExpr(expr: Expr, mapping: Iterable[(Identifier, Expr)]) : EvaluationResult = { + if(mapping.forall{ case (key, value) => purescala.ExprOps.isValue(value) }) { + eval(expr, mapping.toMap) + } else (evalEnv(mapping) match { + case Left(m) => eval(expr, m) + case Right(errorMessage) => + val m = mapping.filter{ case (k, v) => purescala.ExprOps.isValue(v) }.toMap + eval(expr, m) match { + case res @ evaluators.EvaluationResults.Successful(result) => res + case _ => evaluators.EvaluationResults.EvaluatorError(errorMessage) + } + }) + } + + /** From a not yet well evaluated context with dependencies between variables, returns a head where all exprs are values (as a Left()) + * If this does not succeed, it provides an error message as Right()*/ + private def evalEnv(mapping: Iterable[(Identifier, Expr)]): Either[Map[Identifier, Value], String] = { + var f= mapping.toSet + var mBuilder = collection.mutable.ListBuffer[(Identifier, Value)]() + var changed = true + while(f.nonEmpty && changed) { + changed = false + for((i, v) <- f) { + eval(v, mBuilder.toMap).result match { + case None => + case Some(e) => + changed = true + mBuilder += ((i -> e)) + f -= ((i, v)) + } + } + } + if(!changed) { + val str = "In the context " + mapping + ",\n" + + (for((i, v) <- f) yield { + s"eval(${v}) returned the error: " + eval(v, mBuilder.toMap) + }).mkString("\n") + Right(str) + } else Left(mBuilder.toMap) + } } trait NDEvaluator extends Evaluator { diff --git a/src/main/scala/leon/evaluators/EvaluatorContexts.scala b/src/main/scala/leon/evaluators/EvaluatorContexts.scala index 859e960d5d7aadbc63de656fd3c340fbc60b30f1..b5d3b4b5fd04db0e9118a653c058d04304c5d63e 100644 --- a/src/main/scala/leon/evaluators/EvaluatorContexts.scala +++ b/src/main/scala/leon/evaluators/EvaluatorContexts.scala @@ -15,16 +15,34 @@ trait RecContext[RC <: RecContext[RC]] { def newVars(news: Map[Identifier, Expr]): RC def withNewVar(id: Identifier, v: Expr): RC = { + if(!purescala.ExprOps.isValue(v)) { + println(s"Trying to add $id -> $v in context butnot a value") + println(Thread.currentThread().getStackTrace.map(_.toString).mkString("\n")) + System.exit(0) + } newVars(mappings + (id -> v)) } def withNewVars(news: Map[Identifier, Expr]): RC = { + if(news.exists{ case (k, v) => !purescala.ExprOps.isValue(v) }) { + println(s"Trying to add $news in context but not a value") + println(Thread.currentThread().getStackTrace.map(_.toString).mkString("\n")) + System.exit(0) + } newVars(mappings ++ news) } } case class DefaultRecContext(mappings: Map[Identifier, Expr]) extends RecContext[DefaultRecContext] { def newVars(news: Map[Identifier, Expr]) = copy(news) + + mappings.find{ case (k, v) => !purescala.ExprOps.isValue(v) } match { + case None => + case Some(a) => + println(s"Trying to add $mappings in context but $a has not a value as second argument (of type ${a._2.getType}) and class ${a._2.getClass} because ${purescala.ExprOps.msgs}") + println(Thread.currentThread().getStackTrace.map(_.toString).mkString("\n")) + System.exit(0) + } } class GlobalContext(val model: Model, val maxSteps: Int, val check: Boolean) { diff --git a/src/main/scala/leon/evaluators/RecursiveEvaluator.scala b/src/main/scala/leon/evaluators/RecursiveEvaluator.scala index cfb45f73350de39cf208441d324a85f0b9b363b9..10351213546db205fc52e1fc83d817315de81119 100644 --- a/src/main/scala/leon/evaluators/RecursiveEvaluator.scala +++ b/src/main/scala/leon/evaluators/RecursiveEvaluator.scala @@ -39,8 +39,6 @@ abstract class RecursiveEvaluator(ctx: LeonContext, prog: Program, maxSteps: Int protected[evaluators] def e(expr: Expr)(implicit rctx: RC, gctx: GC): Expr = expr match { case Variable(id) => rctx.mappings.get(id) match { - case Some(v) if v != expr => - e(v) case Some(v) => v case None => @@ -213,7 +211,8 @@ abstract class RecursiveEvaluator(ctx: LeonContext, prog: Program, maxSteps: Int case CaseClass(cct, args) => val cc = CaseClass(cct, args.map(e)) if (cct.classDef.hasInvariant) { - e(FunctionInvocation(cct.invariant.get, Seq(cc))) match { + val i = FreshIdentifier("i", cct) + e(FunctionInvocation(cct.invariant.get, Seq(Variable(i))))(rctx.withNewVar(i, cc), gctx) match { case BooleanLiteral(true) => case BooleanLiteral(false) => throw RuntimeError("ADT invariant violation for " + cct.classDef.id.asString + " reached in evaluation.: " + cct.invariant.get.asString) diff --git a/src/main/scala/leon/purescala/ExprOps.scala b/src/main/scala/leon/purescala/ExprOps.scala index f715bf109d598abcbb860021b1c73b131a23e58a..6cc0a39bd8b354c0aa263c9031875fafddc28aab 100644 --- a/src/main/scala/leon/purescala/ExprOps.scala +++ b/src/main/scala/leon/purescala/ExprOps.scala @@ -2136,9 +2136,17 @@ object ExprOps extends GenTreeOps[Expr] { case _ => fun } + var msgs: String = "" + implicit class BooleanAdder(b: Boolean) { + def <(msg: String) = {if(!b) msgs += msg; b} + } /** Returns true if expr is a value of type t */ def isValueOfType(e: Expr, t: TypeTree): Boolean = { + def unWrapSome(s: Expr) = s match { + case CaseClass(_, Seq(a)) => a + case _ => s + } (e, t) match { case (StringLiteral(_), StringType) => true case (IntLiteral(_), Int32Type) => true @@ -2154,25 +2162,29 @@ object ExprOps extends GenTreeOps[Expr] { tbase == base && (elems forall isValue) case (FiniteMap(elems, tk, tv), MapType(from, to)) => - tk == from && tv == to && - (elems forall (kv => isValueOfType(kv._1, from) && isValueOfType(kv._2, to) )) + (tk == from) < s"$tk not equal to $from" && (tv == to) < s"$tv not equal to $to" && + (elems forall (kv => isValueOfType(kv._1, from) < s"${kv._1} not a value of type ${from}" && isValueOfType(unWrapSome(kv._2), to) < s"${unWrapSome(kv._2)} not a value of type ${to}" )) case (NonemptyArray(elems, defaultValues), ArrayType(base)) => elems.values forall (x => isValueOfType(x, base)) case (EmptyArray(tpe), ArrayType(base)) => tpe == base case (CaseClass(ct, args), ct2@AbstractClassType(classDef, tps)) => - TypeOps.isSubtypeOf(ct, ct2) && - ((args zip ct.fieldsTypes) forall (argstyped => isValueOfType(argstyped._1, argstyped._2))) + TypeOps.isSubtypeOf(ct, ct2) < s"$ct not a subtype of $ct2" && + ((args zip ct.fieldsTypes) forall (argstyped => isValueOfType(argstyped._1, argstyped._2) < s"${argstyped._1} not a value of type ${argstyped._2}" )) case (CaseClass(ct, args), ct2@CaseClassType(classDef, tps)) => - ct == ct2 && + (ct == ct2) < s"$ct not equal to $ct2" && ((args zip ct.fieldsTypes) forall (argstyped => isValueOfType(argstyped._1, argstyped._2))) + case (FiniteLambda(mapping, default, tpe), exTpe@FunctionType(ins, out)) => + tpe == exTpe case (Lambda(valdefs, body), FunctionType(ins, out)) => - (valdefs zip ins forall (vdin => vdin._1.getType == vdin._2)) && - body.getType == out + (valdefs zip ins forall (vdin => (vdin._1.getType == vdin._2) < s"${vdin._1.getType} is not equal to ${vdin._2}")) && + (body.getType == out) < s"${body.getType} is not equal to ${out}" + case (FiniteBag(elements, fbtpe), BagType(tpe)) => + fbtpe == tpe && elements.forall{ case (key, value) => isValueOfType(key, tpe) && isValueOfType(value, IntegerType) } case _ => false } } - + /** Returns true if expr is a value. Stronger than isGround */ val isValue = (e: Expr) => isValueOfType(e, e.getType) } diff --git a/src/main/scala/leon/purescala/SelfPrettyPrinter.scala b/src/main/scala/leon/purescala/SelfPrettyPrinter.scala index c9f86a404bac39d3cd5bfec8b16e6d48eba2d968..2488aec8cebdd6de10d070800a4991df2e9894ba 100644 --- a/src/main/scala/leon/purescala/SelfPrettyPrinter.scala +++ b/src/main/scala/leon/purescala/SelfPrettyPrinter.scala @@ -8,8 +8,7 @@ import leon.LeonContext import leon.evaluators import leon.utils.StreamUtils import leon.purescala.Quantification._ -import leon.utils.DebugSectionSynthesis -import leon.utils.DebugSectionVerification +import leon.utils.DebugSectionEvaluation import purescala.Definitions.Program import purescala.Expressions._ import purescala.Types.StringType @@ -121,7 +120,7 @@ class SelfPrettyPrinter { case Some(StringLiteral(res)) if res != "" => res case res => - ctx.reporter.debug("not a string literal " + res) + ctx.reporter.debug("not a string literal " + result) orElse } } catch { diff --git a/src/main/scala/leon/repair/rules/Focus.scala b/src/main/scala/leon/repair/rules/Focus.scala index 512fb0afdbfa4fe99c517ca18d822e33b6293f19..cd0da333bcfd1882d3360fd769e6f7f49584eef0 100644 --- a/src/main/scala/leon/repair/rules/Focus.scala +++ b/src/main/scala/leon/repair/rules/Focus.scala @@ -64,9 +64,9 @@ case object Focus extends PreprocessingRule("Focus") { soFar } - def existsFailing(e: Expr, env: Map[Identifier, Expr], evaluator: Evaluator): Boolean = { + def existsFailing(e: Expr, env: Map[Identifier, Expr], evaluator: DeterministicEvaluator): Boolean = { p.eb.invalids.exists { ex => - evaluator.eval(e, (p.as zip ex.ins).toMap ++ env).result match { + evaluator.evalEnvExpr(e, env).result match { case Some(BooleanLiteral(b)) => b case _ => true } diff --git a/src/main/scala/leon/synthesis/ExamplesBank.scala b/src/main/scala/leon/synthesis/ExamplesBank.scala index 35a6986864dde1a97fdca214536dac6a21b52c57..92664547e33e73ccce08544a235249b5c0856d0a 100644 --- a/src/main/scala/leon/synthesis/ExamplesBank.scala +++ b/src/main/scala/leon/synthesis/ExamplesBank.scala @@ -181,9 +181,11 @@ case class QualifiedExamplesBank(as: List[Identifier], xs: List[Identifier], eb: QualifiedExamplesBank(nas, xs, eb mapIns { (in: Seq[Expr]) => List(toKeep.map(in)) }) } - /** Filter inputs throught expr which is an expression evaluating to a boolean */ + /** Filter inputs through expr which is an expression evaluating to a boolean */ def filterIns(expr: Expr): QualifiedExamplesBank = { - filterIns(m => hctx.defaultEvaluator.eval(expr, m).result == Some(BooleanLiteral(true))) + filterIns(m => + hctx.defaultEvaluator.evalEnvExpr(expr, m) + .result == Some(BooleanLiteral(true))) } /** Filters inputs through the predicate pred, with an assignment of input variables to expressions. */ diff --git a/src/main/scala/leon/synthesis/ExamplesFinder.scala b/src/main/scala/leon/synthesis/ExamplesFinder.scala index 0ffdbc7692a0e0766345cbc9c105ad92bdc2b0bb..31731dca30d02281fae6883f139b29dbd96f18d9 100644 --- a/src/main/scala/leon/synthesis/ExamplesFinder.scala +++ b/src/main/scala/leon/synthesis/ExamplesFinder.scala @@ -107,7 +107,7 @@ class ExamplesFinder(ctx0: LeonContext, program: Program) { ((p.as zip i.ins).toMap, p.pc.toClause) } - evaluator.eval(cond, mapping) match { + evaluator.evalEnvExpr(cond, mapping) match { case EvaluationResults.Successful(BooleanLiteral(true)) => true case _ => false } @@ -143,8 +143,8 @@ class ExamplesFinder(ctx0: LeonContext, program: Program) { val ids = variablesOf(test) // Test could contain expressions, we evaluate - evaluator.eval(test, ids.map { (i: Identifier) => i -> i.toVariable }.toMap) match { - case EvaluationResults.Successful(res) => res + abstractEvaluator.eval(test, Model.empty) match { + case EvaluationResults.Successful((res, _)) => res case _ => test } } diff --git a/src/main/scala/leon/synthesis/rules/CEGISLike.scala b/src/main/scala/leon/synthesis/rules/CEGISLike.scala index 933ae752b19eff0ff816718ef7e6c4ee79560f22..e4e5192bc96b99bcfb2f28272665329ffc37a612 100644 --- a/src/main/scala/leon/synthesis/rules/CEGISLike.scala +++ b/src/main/scala/leon/synthesis/rules/CEGISLike.scala @@ -453,11 +453,11 @@ abstract class CEGISLike(name: String) extends Rule(name) { timers.testForProgram.start() val res = ex match { case InExample(ins) => - evaluator.eval(cnstr, p.as.zip(ins).toMap ++ p.pc.bindings) + evaluator.evalEnvExpr(cnstr, p.as.zip(ins).toMap ++ p.pc.bindings) case InOutExample(ins, outs) => val eq = equality(innerSol, tupleWrap(outs)) - evaluator.eval(eq, p.as.zip(ins).toMap ++ p.pc.bindings) + evaluator.evalEnvExpr(eq, p.as.zip(ins).toMap ++ p.pc.bindings) } timers.testForProgram.stop() diff --git a/src/main/scala/leon/synthesis/rules/IntroduceRecCalls.scala b/src/main/scala/leon/synthesis/rules/IntroduceRecCalls.scala index 4463e6818b2b7b102cf404bd80ca794d9b96c3c7..ba3cb368510f2fd79c811766c57f55d71d1c4d71 100644 --- a/src/main/scala/leon/synthesis/rules/IntroduceRecCalls.scala +++ b/src/main/scala/leon/synthesis/rules/IntroduceRecCalls.scala @@ -67,7 +67,7 @@ case object IntroduceRecCalls extends NormalizingRule("Introduce rec. calls") { val evaluator = new NoChooseEvaluator(hctx, hctx.program) def mapExample(ex: Example): List[Example] = { - val results = calls map (evaluator.eval(_, p.as.zip(ex.ins).toMap).result) + val results = calls map (evaluator.evalEnvExpr(_, p.as.zip(ex.ins)).result) if (results forall (_.isDefined)) List({ val extra = results map (_.get) ex match { diff --git a/src/test/scala/leon/test/helpers/WithLikelyEq.scala b/src/test/scala/leon/test/helpers/WithLikelyEq.scala index 9836fc151c7218e61f53cf7e3cff74da5ffc719a..21a97ec48ff9a5926d3e2ebfe50eeac044ab8716 100644 --- a/src/test/scala/leon/test/helpers/WithLikelyEq.scala +++ b/src/test/scala/leon/test/helpers/WithLikelyEq.scala @@ -45,9 +45,8 @@ trait WithLikelyEq { cartesianProduct(allValues).foreach { vs => val m = (freeVars zip vs).toMap - val doTest = pre.map { p => - evaluator.eval(p, m).result match { + evaluator.evalEnvExpr(p, m).result match { case Some(BooleanLiteral(b)) => b case _ => fail("Precondition is not a boolean expression") }