diff --git a/src/main/scala/leon/evaluators/AbstractEvaluator.scala b/src/main/scala/leon/evaluators/AbstractEvaluator.scala index 5caec3ee8a3f585ba8a61981c1cd783e01f52e92..2e0a7a2bb1959f720e62ab47f4c6783436e4d28b 100644 --- a/src/main/scala/leon/evaluators/AbstractEvaluator.scala +++ b/src/main/scala/leon/evaluators/AbstractEvaluator.scala @@ -61,7 +61,7 @@ class AbstractEvaluator(ctx: LeonContext, prog: Program) extends ContextualEvalu var evaluateCaseClassSelector = true 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..c308c5e347e859bc192833bf015e4c2013dc26b9 100644 --- a/src/main/scala/leon/evaluators/Evaluator.scala +++ b/src/main/scala/leon/evaluators/Evaluator.scala @@ -25,7 +25,7 @@ abstract class Evaluator(val context: LeonContext, val program: Program) extends /** Evaluates an expression given a simple model (assumes expr is quantifier-free). * Mainly useful for compatibility reasons. */ - final def eval(expr: Expr, mapping: Map[Identifier, Expr]) : EvaluationResult = eval(expr, new Model(mapping)) + def eval(expr: Expr, mapping: Map[Identifier, Expr]) : EvaluationResult = eval(expr, new Model(mapping)) /** Evaluates a ground expression. */ final def eval(expr: Expr) : EvaluationResult = eval(expr, Model.empty) @@ -46,6 +46,59 @@ 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 */ + override def eval(expr: Expr, mapping: Map[Identifier, Expr]) : EvaluationResult = { + if(mapping.forall{ case (key, value) => purescala.ExprOps.isValue(value) }) { + super.eval(expr, mapping.toMap) + } else (_evalEnv(mapping) match { + case Left(m) => super.eval(expr, m) + case Right(errorMessage) => + val m = mapping.filter{ case (k, v) => purescala.ExprOps.isValue(v) }.toMap + super.eval(expr, m) match { + case res @ evaluators.EvaluationResults.Successful(result) => res + case _ => evaluators.EvaluationResults.EvaluatorError(errorMessage) + } + }) + } + + /** Returns an evaluated environment. If it fails, removes all non-values from the environment. */ + def evalEnv(mapping: Iterable[(Identifier, Expr)]): Map[Identifier, Value] = { + if(mapping.forall{ case (key, value) => purescala.ExprOps.isValue(value) }) { + mapping.toMap + } else (_evalEnv(mapping) match { + case Left(m) => m + case Right(msg) => mapping.filter(x => purescala.ExprOps.isValue(x._2)).toMap + }) + } + + /** 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] = { + val (evaled, nonevaled) = mapping.partition{ case (id, expr) => purescala.ExprOps.isValue(expr)} + var f= nonevaled.toSet + var mBuilder = collection.mutable.ListBuffer[(Identifier, Value)]() ++= evaled + 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/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 fb7cb0742ed74430e628c70b3f0a5ff5b7fa9f19..f3a9a94caaabf7e5d2c104439f041de680c54f53 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,30 @@ 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 + variablesOf(e).isEmpty && + (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..a881953bb9354472703660cae8bacbb542e67596 100644 --- a/src/main/scala/leon/repair/rules/Focus.scala +++ b/src/main/scala/leon/repair/rules/Focus.scala @@ -64,7 +64,7 @@ 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 { case Some(BooleanLiteral(b)) => b diff --git a/src/main/scala/leon/solvers/unrolling/UnrollingSolver.scala b/src/main/scala/leon/solvers/unrolling/UnrollingSolver.scala index 403fb08fd20c9297b91cd6294cfc4a707e7ad40a..b7aa7dc664c80941d8fcd23e7e5f807aead28174 100644 --- a/src/main/scala/leon/solvers/unrolling/UnrollingSolver.scala +++ b/src/main/scala/leon/solvers/unrolling/UnrollingSolver.scala @@ -210,7 +210,11 @@ trait AbstractUnrollingSolver[T] private def validateModel(model: Model, assumptions: Seq[Expr], silenceErrors: Boolean): Boolean = { val expr = andJoin(assumptions ++ constraints) - evaluator.eval(expr, model) match { + val newExpr = model.toSeq.foldLeft(expr){ + case (e, (k, v)) => let(k, v, e) + } + + evaluator.eval(newExpr) match { case EvaluationResults.Successful(BooleanLiteral(true)) => reporter.debug("- Model validated.") true diff --git a/src/main/scala/leon/synthesis/ExamplesBank.scala b/src/main/scala/leon/synthesis/ExamplesBank.scala index 35a6986864dde1a97fdca214536dac6a21b52c57..6cababe398c6159ec4e5cce721b795a751825bda 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.eval(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 a20b37a100caf2ca0bfed6c8bcf7813c01149e88..52756108c029ff7fcad5d0f2a0e6aaceb3d6770c 100644 --- a/src/main/scala/leon/synthesis/ExamplesFinder.scala +++ b/src/main/scala/leon/synthesis/ExamplesFinder.scala @@ -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/test/scala/leon/test/helpers/WithLikelyEq.scala b/src/test/scala/leon/test/helpers/WithLikelyEq.scala index 9836fc151c7218e61f53cf7e3cff74da5ffc719a..f121f4553809fd7ae5b9e93d15bb1b5b7d3b2de0 100644 --- a/src/test/scala/leon/test/helpers/WithLikelyEq.scala +++ b/src/test/scala/leon/test/helpers/WithLikelyEq.scala @@ -44,8 +44,7 @@ trait WithLikelyEq { val allValues = freeVars.map(id => values.get(id).map(Seq(_)).getOrElse(typesValues(id.getType))) cartesianProduct(allValues).foreach { vs => - val m = (freeVars zip vs).toMap - + val m = evaluator.evalEnv(freeVars zip vs) val doTest = pre.map { p => evaluator.eval(p, m).result match { case Some(BooleanLiteral(b)) => b