From 57710fe219d542d2f1d2fa259df5303c5cadd744 Mon Sep 17 00:00:00 2001 From: Philippe Suter <philippe.suter@gmail.com> Date: Tue, 8 Jan 2013 21:50:32 +0100 Subject: [PATCH] "Support" for `choose` in evaluators. What this commit really introduces is a graceful failure when asked to evaluate a `choose` expression. In particular, it makes it possible for codegen evaluator to compile functions that contain `choose` expressions, and even evaluate them, as long as the execution path doesn't meet a `choose` expressions. --- .../LeonCodeGenEvaluationException.java | 11 +++++++ .../runtime/LeonCodeGenRuntimeException.java | 2 ++ .../scala/leon/codegen/CodeGeneration.scala | 7 +++++ .../leon/evaluators/CodeGenEvaluator.scala | 3 ++ .../leon/evaluators/DefaultEvaluator.scala | 3 ++ .../test/evaluators/EvaluatorsTests.scala | 30 +++++++++++++++++++ 6 files changed, 56 insertions(+) create mode 100644 src/main/java/leon/codegen/runtime/LeonCodeGenEvaluationException.java diff --git a/src/main/java/leon/codegen/runtime/LeonCodeGenEvaluationException.java b/src/main/java/leon/codegen/runtime/LeonCodeGenEvaluationException.java new file mode 100644 index 000000000..f4ba02da8 --- /dev/null +++ b/src/main/java/leon/codegen/runtime/LeonCodeGenEvaluationException.java @@ -0,0 +1,11 @@ +package leon.codegen.runtime; + +/** Such exceptions are thrown when the evaluator is asked to do something it + * cannot do, for instance evaluating a `choose` expression. It should be + * distinguished from `LeonCodeGenRuntimeException`s, which are thrown when + * the evaluator runs into a runtime error (.get on an undefined map, etc.). */ +public class LeonCodeGenEvaluationException extends Exception { + public LeonCodeGenEvaluationException(String msg) { + super(msg); + } +} diff --git a/src/main/java/leon/codegen/runtime/LeonCodeGenRuntimeException.java b/src/main/java/leon/codegen/runtime/LeonCodeGenRuntimeException.java index 39cb3bfc1..8a3e6b86a 100644 --- a/src/main/java/leon/codegen/runtime/LeonCodeGenRuntimeException.java +++ b/src/main/java/leon/codegen/runtime/LeonCodeGenRuntimeException.java @@ -1,5 +1,7 @@ package leon.codegen.runtime; +/** Such exceptions are thrown when the evaluator encounters a runtime error, + * for instance `.get` with an undefined key on a map. */ public class LeonCodeGenRuntimeException extends Exception { public LeonCodeGenRuntimeException(String msg) { super(msg); diff --git a/src/main/scala/leon/codegen/CodeGeneration.scala b/src/main/scala/leon/codegen/CodeGeneration.scala index 65eb47562..11e57f36e 100644 --- a/src/main/scala/leon/codegen/CodeGeneration.scala +++ b/src/main/scala/leon/codegen/CodeGeneration.scala @@ -22,6 +22,7 @@ object CodeGeneration { private val MapClass = "leon/codegen/runtime/Map" private val CaseClassClass = "leon/codegen/runtime/CaseClass" private val ErrorClass = "leon/codegen/runtime/LeonCodeGenRuntimeException" + private val ImpossibleEvaluationClass = "leon/codegen/runtime/LeonCodeGenEvaluationException" def defToJVMName(p : Program, d : Definition) : String = "Leon$CodeGen$" + d.id.uniqueName @@ -288,6 +289,12 @@ object CodeGeneration { ch << InvokeSpecial(ErrorClass, constructorName, "(Ljava/lang/String;)V") ch << ATHROW + case Choose(_, _) => + ch << New(ImpossibleEvaluationClass) << DUP + ch << Ldc("Cannot execute choose.") + ch << InvokeSpecial(ImpossibleEvaluationClass, constructorName, "(Ljava/lang/String;)V") + ch << ATHROW + case b if b.getType == BooleanType && canDelegateToMkBranch => val fl = ch.getFreshLabel("boolfalse") val al = ch.getFreshLabel("boolafter") diff --git a/src/main/scala/leon/evaluators/CodeGenEvaluator.scala b/src/main/scala/leon/evaluators/CodeGenEvaluator.scala index 9b9702f31..c9f6bed91 100644 --- a/src/main/scala/leon/evaluators/CodeGenEvaluator.scala +++ b/src/main/scala/leon/evaluators/CodeGenEvaluator.scala @@ -45,6 +45,9 @@ class CodeGenEvaluator(ctx : LeonContext, val unit : CompilationUnit) extends Ev // and the check above would fail. case t : Throwable if t.getClass.toString.endsWith("LeonCodeGenRuntimeException") => EvaluationFailure(t.getMessage) + + case t : Throwable if t.getClass.toString.endsWith("LeonCodeGenEvaluationException") => + EvaluationError(t.getMessage) } }) } diff --git a/src/main/scala/leon/evaluators/DefaultEvaluator.scala b/src/main/scala/leon/evaluators/DefaultEvaluator.scala index a6998f10b..9b0bbdc2e 100644 --- a/src/main/scala/leon/evaluators/DefaultEvaluator.scala +++ b/src/main/scala/leon/evaluators/DefaultEvaluator.scala @@ -284,6 +284,9 @@ class DefaultEvaluator(ctx : LeonContext, prog : Program) extends Evaluator(ctx, BooleanLiteral(newArgs.distinct.size == newArgs.size) } + case Choose(_, _) => + throw EvalError("Cannot evaluate choose.") + case other => { context.reporter.error("Error: don't know how to handle " + other + " in Evaluator.") throw EvalError("Unhandled case in Evaluator : " + other) diff --git a/src/test/scala/leon/test/evaluators/EvaluatorsTests.scala b/src/test/scala/leon/test/evaluators/EvaluatorsTests.scala index 2633a1b9c..d832950cc 100644 --- a/src/test/scala/leon/test/evaluators/EvaluatorsTests.scala +++ b/src/test/scala/leon/test/evaluators/EvaluatorsTests.scala @@ -141,6 +141,19 @@ class EvaluatorsTests extends FunSuite { } } + private def checkEvaluatorError(evaluator : Evaluator, in : Expr) { + evaluator.eval(in) match { + case EvaluationFailure(msg) => + throw new AssertionError("Evaluation of '%s' with evaluator '%s' should have produced an internal error, but it failed instead (%s).".format(in, evaluator.name, msg)) + + case EvaluationSuccessful(result) => + throw new AssertionError("Evaluation of '%s' with evaluator '%s' should have produced an internal error, but it produced the result '%s' instead.".format(in, evaluator.name, result)) + + case EvaluationError(_) => + // that's the desired outcome + } + } + private def T = BooleanLiteral(true) private def F = BooleanLiteral(false) private def IL(i : Int) = IntLiteral(i) @@ -357,4 +370,21 @@ class EvaluatorsTests extends FunSuite { checkComp(e, MapIsDefinedAt(mkCall("finite2"), IL(3)), F) } } + + test("Misc") { + val p = """|object Program { + | import leon.Utils._ + | + | def c(i : Int) : Int = choose { (j : Int) => j > i } + |} + |""".stripMargin + + implicit val prog = parseString(p) + + val evaluators = prepareEvaluators + + for(e <- evaluators) { + checkEvaluatorError(e, mkCall("c", IL(42))) + } + } } -- GitLab