From 8ca74bdc27d7aa9f4c77897086b0e1ead3579392 Mon Sep 17 00:00:00 2001
From: Etienne Kneuss <ekneuss@gmail.com>
Date: Mon, 6 Jul 2015 15:54:39 +0200
Subject: [PATCH] Fix handling of holes, and multiple-variables chooses

---
 .../scala/leon/purescala/Constructors.scala   |  4 ++-
 .../solvers/templates/TemplateGenerator.scala | 18 ++++++++----
 .../scala/leon/synthesis/ConvertHoles.scala   | 29 ++++++++++++-------
 .../regression/synthesis/Misc/HolesBug.scala  | 16 ++++++++++
 .../synthesis/SynthesisRegressionSuite.scala  |  4 +++
 5 files changed, 54 insertions(+), 17 deletions(-)
 create mode 100644 src/test/resources/regression/synthesis/Misc/HolesBug.scala

diff --git a/src/main/scala/leon/purescala/Constructors.scala b/src/main/scala/leon/purescala/Constructors.scala
index aceebf495..148f8d051 100644
--- a/src/main/scala/leon/purescala/Constructors.scala
+++ b/src/main/scala/leon/purescala/Constructors.scala
@@ -68,7 +68,7 @@ object Constructors {
   /** Will instantiate the type parameters of the function according to argument types */
   def functionInvocation(fd : FunDef, args : Seq[Expr]) = {
     
-    require(fd.params.length == args.length)
+    require(fd.params.length == args.length, "Invoking function with incorrect number of arguments")
     
     val formalType = tupleTypeWrap(fd.params map { _.getType })
     val actualType = tupleTypeWrap(args map { _.getType })
@@ -234,6 +234,8 @@ object Constructors {
 
   def application(fn: Expr, realArgs: Seq[Expr]) = fn match {
      case Lambda(formalArgs, body) =>
+      assert(realArgs.size == formalArgs.size, "Invoking lambda with incorrect number of arguments")
+
       var defs: Seq[(Identifier, Expr)] = Seq()
 
       val subst = formalArgs.zip(realArgs).map {
diff --git a/src/main/scala/leon/solvers/templates/TemplateGenerator.scala b/src/main/scala/leon/solvers/templates/TemplateGenerator.scala
index fc0826dbb..0ee3997c6 100644
--- a/src/main/scala/leon/solvers/templates/TemplateGenerator.scala
+++ b/src/main/scala/leon/solvers/templates/TemplateGenerator.scala
@@ -244,11 +244,19 @@ class TemplateGenerator[T](val encoder: TemplateEncoder[T],
           }
         }
 
-        case c @ Choose(cond) =>
-          val cid = FreshIdentifier("choose", c.getType, true)
-          storeExpr(cid)
-          storeGuarded(pathVar, application(cond, Seq(Variable(cid))))
-          Variable(cid)
+        case c @ Choose(Lambda(params, cond)) =>
+
+          val cs = params.map(_.id.freshen.toVariable)
+
+          for (c <- cs) {
+            storeExpr(c.id)
+          }
+
+          val freshMap = (params.map(_.id) zip cs).toMap
+
+          storeGuarded(pathVar, replaceFromIDs(freshMap, cond))
+
+          tupleWrap(cs)
 
         case l @ Lambda(args, body) =>
           val idArgs : Seq[Identifier] = lambdaArgs(l)
diff --git a/src/main/scala/leon/synthesis/ConvertHoles.scala b/src/main/scala/leon/synthesis/ConvertHoles.scala
index 3d9183a7b..4c27e9b96 100644
--- a/src/main/scala/leon/synthesis/ConvertHoles.scala
+++ b/src/main/scala/leon/synthesis/ConvertHoles.scala
@@ -64,26 +64,33 @@ object ConvertHoles extends LeonPhase[Program, Program] {
             None
         }(body)
 
-        val asChoose = if (holes.nonEmpty) {
+        if (holes.nonEmpty) {
           val cids: List[Identifier] = holes.map(_.freshen)
-          val pred = post match {
-            case Some(post) =>
-              replaceFromIDs((holes zip cids.map(_.toVariable)).toMap, post)
-            case None =>
+          val hToFresh = (holes zip cids.map(_.toVariable)).toMap
+
+          val spec = post match {
+            case Some(post: Lambda) =>
+              val asLet = letTuple(post.args.map(_.id), withoutHoles, post.body)
+
+              Lambda(cids.map(ValDef(_)), replaceFromIDs(hToFresh, asLet))
+
+            case _ =>
               Lambda(cids.map(ValDef(_)), BooleanLiteral(true))
           }
 
-          letTuple(holes, Choose(pred), withoutHoles)
+          val choose = Choose(spec)
+
 
+          val valuations = letTuple(holes, choose, withoutHoles)
+
+          withPostcondition(withPrecondition(valuations, pre), post)
+
+        } else {
+          e
         }
-        else withoutHoles
 
-        withPostcondition(withPrecondition(asChoose, pre), post)
-      
       case None => e
     }
-
-
   }
 
    
diff --git a/src/test/resources/regression/synthesis/Misc/HolesBug.scala b/src/test/resources/regression/synthesis/Misc/HolesBug.scala
new file mode 100644
index 000000000..47db91b99
--- /dev/null
+++ b/src/test/resources/regression/synthesis/Misc/HolesBug.scala
@@ -0,0 +1,16 @@
+import leon.lang._
+import leon.lang.synthesis._
+import leon.annotation._
+
+object HolesBug {
+  def plop(a: BigInt, b: BigInt): BigInt = {
+    if (a < b) {
+      ???[BigInt]
+    } else {
+      ???[BigInt]
+    }
+  } ensuring { res =>
+    res > 0
+  }
+}
+
diff --git a/src/test/scala/leon/test/synthesis/SynthesisRegressionSuite.scala b/src/test/scala/leon/test/synthesis/SynthesisRegressionSuite.scala
index 35513481a..255781935 100644
--- a/src/test/scala/leon/test/synthesis/SynthesisRegressionSuite.scala
+++ b/src/test/scala/leon/test/synthesis/SynthesisRegressionSuite.scala
@@ -64,6 +64,10 @@ class SynthesisRegressionSuite extends LeonTestSuite {
     testSynthesis("List", f, 200)
   }
 
+  forEachFileIn("regression/synthesis/Misc/") { f =>
+    testSynthesis("Miscellaneous", f, 1000)
+  }
+
   forEachFileIn("regression/synthesis/Holes/") { f =>
     testSynthesis("Holes", f, 1000)
   }
-- 
GitLab