From 31640cd6642e31dd80cff815336a5c4cb79eeefa Mon Sep 17 00:00:00 2001
From: Philippe Suter <philippe.suter@gmail.com>
Date: Thu, 18 Nov 2010 15:32:44 +0000
Subject: [PATCH] new solver that checks intermediate counter examples

---
 src/purescala/Definitions.scala               | 18 ++++
 src/purescala/Extensions.scala                |  6 +-
 src/purescala/IterativeZ3Solver.scala         |  7 ++
 src/purescala/Trees.scala                     | 48 ++++------
 src/purescala/Z3ModelReconstruction.scala     | 11 +++
 src/purescala/Z3Solver.scala                  | 93 ++++++++++++++++++-
 .../z3plugins/instantiator/Instantiator.scala |  7 +-
 7 files changed, 150 insertions(+), 40 deletions(-)

diff --git a/src/purescala/Definitions.scala b/src/purescala/Definitions.scala
index 297d8d025..11e6d35ef 100644
--- a/src/purescala/Definitions.scala
+++ b/src/purescala/Definitions.scala
@@ -201,6 +201,24 @@ object Definitions {
       this
     }
     def parent = parent_
+
+    def selectorID2Index(id: Identifier) : Int = {
+      var i : Int = 0
+      var found = false
+      val fs = fields.size
+      while(!found && i < fs) {
+        if(fields(i).id == id) {
+          found = true
+        } else {
+          i += 1
+        }
+      }
+
+      if(found)
+        i
+      else
+        scala.Predef.error("Asking for index of field that does not belong to the case class.")
+    }
   }
 
   /** "Regular" classes */
diff --git a/src/purescala/Extensions.scala b/src/purescala/Extensions.scala
index 7b1331748..f97c25d00 100644
--- a/src/purescala/Extensions.scala
+++ b/src/purescala/Extensions.scala
@@ -85,11 +85,7 @@ object Extensions {
     }
     // these extensions are always loaded, unless specified otherwise
     val defaultExtensions: Seq[Extension] = if(Settings.runDefaultExtensions) {
-      if(false && Settings.useInstantiator) {
-        (new IterativeZ3Solver(extensionsReporter)) :: Nil
-      } else {
-        (new Z3Solver(extensionsReporter)) :: Nil
-      }
+      (new Z3Solver(extensionsReporter)) :: Nil
     } else {
       Nil
     }
diff --git a/src/purescala/IterativeZ3Solver.scala b/src/purescala/IterativeZ3Solver.scala
index 479ee402e..e142b4fcc 100644
--- a/src/purescala/IterativeZ3Solver.scala
+++ b/src/purescala/IterativeZ3Solver.scala
@@ -1,3 +1,8 @@
+/*
+
+This file is rubbish, I'm sticking with Z3Solver.scala...
+
+
 package purescala
 
 import z3.scala._
@@ -15,3 +20,5 @@ class IterativeZ3Solver(reporter: Reporter) extends Z3Solver(reporter) {
     Some(false)
   }
 }
+
+*/
diff --git a/src/purescala/Trees.scala b/src/purescala/Trees.scala
index 4945b0fda..8be7aaeb8 100644
--- a/src/purescala/Trees.scala
+++ b/src/purescala/Trees.scala
@@ -92,22 +92,14 @@ object Trees {
 
   /* Propositional logic */
   object And {
-    def apply(exprs: Seq[Expr]) : Expr = {
-      val newExprs = exprs.filter(_ != BooleanLiteral(true))
-      if(newExprs.contains(BooleanLiteral(false))) {
-        BooleanLiteral(false)
-      } else newExprs.size match {
-        case 0 => BooleanLiteral(true)
-        case 1 => newExprs.head
-        case _ => new And(newExprs)
-      }
+    def apply(l: Expr, r: Expr) : Expr = (l,r) match {
+      case (BooleanLiteral(true),_) => r
+      case (_,BooleanLiteral(true)) => l
+      case _ => new And(Seq(l,r))
     }
-
-    def apply(l: Expr, r: Expr): Expr = (l,r) match {
-      case (And(exs1), And(exs2)) => And(exs1 ++ exs2)
-      case (And(exs1), ex2) => And(exs1 :+ ex2)
-      case (ex1, And(exs2)) => And(exs2 :+ ex1)
-      case (ex1, ex2) => And(List(ex1, ex2))
+    def apply(exprs: Seq[Expr]) : Expr = {
+      val simpler = exprs.filter(_ != BooleanLiteral(true))
+      if(simpler.isEmpty) BooleanLiteral(true) else simpler.reduceRight(And(_,_))
     }
 
     def unapply(and: And) : Option[Seq[Expr]] = 
@@ -119,22 +111,14 @@ object Trees {
   }
 
   object Or {
-    def apply(exprs: Seq[Expr]) : Expr = {
-      val newExprs = exprs.filter(_ != BooleanLiteral(false))
-      if(newExprs.contains(BooleanLiteral(true))) {
-        BooleanLiteral(true)
-      } else newExprs.size match {
-        case 0 => BooleanLiteral(false)
-        case 1 => newExprs.head
-        case _ => new Or(newExprs)
-      }
+    def apply(l: Expr, r: Expr) : Expr = (l,r) match {
+      case (BooleanLiteral(false),_) => r
+      case (_,BooleanLiteral(false)) => l
+      case _ => new Or(Seq(l,r))
     }
-
-    def apply(l: Expr, r: Expr): Expr = (l,r) match {
-      case (Or(exs1), Or(exs2)) => Or(exs1 ++ exs2)
-      case (Or(exs1), ex2) => Or(exs1 :+ ex2)
-      case (ex1, Or(exs2)) => Or(exs2 :+ ex1)
-      case (ex1, ex2) => Or(List(ex1, ex2))
+    def apply(exprs: Seq[Expr]) : Expr = {
+      val simpler = exprs.filter(_ != BooleanLiteral(false))
+      if(simpler.isEmpty) BooleanLiteral(false) else simpler.reduceRight(Or(_,_))
     }
 
     def unapply(or: Or) : Option[Seq[Expr]] = 
@@ -273,8 +257,8 @@ object Trees {
   }
 
   /* Option expressions */
-  case class OptionSome(value: Expr) extends Expr 
-  case class OptionNone(baseType: TypeTree) extends Expr with Terminal
+  // case class OptionSome(value: Expr) extends Expr 
+  // case class OptionNone(baseType: TypeTree) extends Expr with Terminal
 
   /* Set expressions */
   case class EmptySet(baseType: TypeTree) extends Expr with Terminal
diff --git a/src/purescala/Z3ModelReconstruction.scala b/src/purescala/Z3ModelReconstruction.scala
index 6565549cd..c7d94612a 100644
--- a/src/purescala/Z3ModelReconstruction.scala
+++ b/src/purescala/Z3ModelReconstruction.scala
@@ -27,6 +27,17 @@ trait Z3ModelReconstruction {
     }
   }
 
+  def modelToMap(model: Z3Model, ids: Iterable[Identifier]) : Map[Identifier,Expr] = {
+    var asMap = Map.empty[Identifier,Expr]
+    for(id <- ids) {
+      modelValue(model, id) match {
+        case None => ; // can't do much here
+        case Some(ex) => asMap = asMap + ((id -> ex))
+      }
+    }
+    asMap
+  }
+
   def printExtractedModel(model: Z3Model, ids : Iterable[Identifier]) : Unit = {
     reporter.info("Tentative extracted model")
     reporter.info("*************************")
diff --git a/src/purescala/Z3Solver.scala b/src/purescala/Z3Solver.scala
index 24af2daf3..a00212809 100644
--- a/src/purescala/Z3Solver.scala
+++ b/src/purescala/Z3Solver.scala
@@ -336,7 +336,13 @@ class Z3Solver(val reporter: Reporter) extends Solver(reporter) with Z3ModelReco
 
   def solve(vc: Expr) = decide(vc, true)
 
-  def decide(vc: Expr, forValidity: Boolean): Option[Boolean] = {
+  def decide(vc: Expr, fv: Boolean) : Option[Boolean] = if(Settings.useInstantiator) {
+    decideIterative(vc, fv)
+  } else {
+    decideStandard(vc, fv)
+  }
+
+  def decideStandard(vc: Expr, forValidity: Boolean): Option[Boolean] = {
     restartZ3
     abstractedFormula = false
 
@@ -403,6 +409,91 @@ class Z3Solver(val reporter: Reporter) extends Solver(reporter) with Z3ModelReco
     result
   }
 
+  def decideIterative(vc: Expr, forValidity: Boolean) : Option[Boolean] = {
+    restartZ3
+    assert(instantiator != null)
+    assert(!useBAPA)
+
+    lazy val varsInVC = variablesOf(vc) 
+
+    if (neverInitialized) {
+      reporter.error("Z3 Solver was not initialized with a PureScala Program.")
+      None
+    }
+    val toConvert = if (forValidity) negate(vc) else vc
+    val toCheckAgainstModels = toConvert
+
+    val result = toZ3Formula(z3, toConvert) match {
+      case None => None // means it could not be translated
+      case Some(z3f) => {
+        z3.assertCnstr(z3f)
+
+        // THE LOOP STARTS HERE.
+        var foundDefinitiveSolution : Boolean = false
+        var finalResult : Option[Boolean] = None
+
+        while(!foundDefinitiveSolution && instantiator.possibleContinuation) {
+          instantiator.increaseSearchDepth()
+          z3.checkAndGetModel() match {
+            case (Some(true), m) => {
+              import Evaluator._
+
+              // WE HAVE TO CHECK THE COUNTER-EXAMPLE.
+              println("A candidate counter-example was found... Examining...")
+              val asMap = modelToMap(m, varsInVC)
+
+              lazy val modelAsString = asMap.toList.map(p => p._1 + " -> " + p._2).mkString("\n")
+
+              //println("(I'm going to pretend this never happened...)")
+
+              val evalResult = eval(asMap, toCheckAgainstModels)
+
+              
+              evalResult match {
+                case OK(BooleanLiteral(true)) => {
+                  reporter.error("Counter-example found and confirmed:")
+                  reporter.error(modelAsString)
+                  foundDefinitiveSolution = true
+                  finalResult = Some(false)
+                }
+                case InfiniteComputation() => {
+                  reporter.info("Model seems to lead to divergent computation.")
+                  reporter.error(modelAsString)
+                  foundDefinitiveSolution = true
+                  finalResult = None
+                }
+                case RuntimeError(msg) => {
+                  reporter.info("Model leads to runtime error: " + msg)
+                  foundDefinitiveSolution = true
+                  finalResult = Some(false)
+                }
+                case t @ TypeError(_,_) => {
+                  scala.Predef.error("Type error in model evaluation.\n" + t.msg)
+                }
+                case _ => {
+                  // false positive. keep searching
+                }
+              }
+            }
+
+            case (Some(false), _) => {
+              // This means a definitive proof of unsatisfiability has been found.
+              foundDefinitiveSolution = true
+              finalResult = Some(true)
+            }
+
+            case (None, m) => {
+              reporter.warning("Iterative Z3 gave up because: " + z3.getSearchFailure.message)
+              foundDefinitiveSolution = true
+              finalResult = None
+            }
+          }
+        }
+        finalResult
+      }
+    }
+    result
+  }
 
   protected[purescala] var exprToZ3Id : Map[Expr,Z3AST] = Map.empty
   protected[purescala] var z3IdToExpr : Map[Z3AST,Expr] = Map.empty
diff --git a/src/purescala/z3plugins/instantiator/Instantiator.scala b/src/purescala/z3plugins/instantiator/Instantiator.scala
index b5834f139..f77859c34 100644
--- a/src/purescala/z3plugins/instantiator/Instantiator.scala
+++ b/src/purescala/z3plugins/instantiator/Instantiator.scala
@@ -134,8 +134,7 @@ class Instantiator(val z3Solver: Z3Solver) extends Z3Theory(z3Solver.z3, "Instan
 
   override def finalCheck : Boolean = safeBlockToAssertAxioms {
     if(!queue.isEmpty) {
-      val smallest = queue.head.depth
-      while(!queue.isEmpty) { // && queue.head.depth == smallest) {
+      while(!queue.isEmpty && queue.head.depth <= deepestAuthorized) {
         val highest : Unrolling = queue.dequeue()
         val toConvertAndAssert = if(highest.isContract) {
           highest.body
@@ -249,6 +248,10 @@ class Instantiator(val z3Solver: Z3Solver) extends Z3Theory(z3Solver.z3, "Instan
     }
   }
 
+  private var deepestAuthorized : Int = -1
+  def possibleContinuation : Boolean = !queue.isEmpty || deepestAuthorized <= 0
+  def increaseSearchDepth() : Unit = { deepestAuthorized += 1; bodyInlined = 0 }
+
   reinit
   private def reinit : Unit = {
     pushLevel = 0
-- 
GitLab