diff --git a/src/main/scala/leon/solvers/z3/FairZ3Solver.scala b/src/main/scala/leon/solvers/z3/FairZ3Solver.scala
index 5a19411bd0bc166cbeaf5c0494d9fe8039e08a73..22c0bf885b32a3d53447b58291dddfd8850f527a 100644
--- a/src/main/scala/leon/solvers/z3/FairZ3Solver.scala
+++ b/src/main/scala/leon/solvers/z3/FairZ3Solver.scala
@@ -162,29 +162,27 @@ class FairZ3Solver(val context : LeonContext, val program: Program)
 
     def blockersInfo = blockersInfoStack.head
 
-    // Note that this computes cumulated sets.
-    private var unlockedStack : List[MutableSet[Z3AST]] = List(MutableSet())
-    def unlockedSet : MutableSet[Z3AST] = unlockedStack.head
-    def wasUnlocked(id : Z3AST) : Boolean = unlockedStack.head(id)
-
     def push() {
       blockersInfoStack = (MutableMap() ++ blockersInfo) :: blockersInfoStack
-      unlockedStack     = (MutableSet() ++ unlockedStack.head) :: unlockedStack
     }
 
     def pop(lvl: Int) {
       blockersInfoStack = blockersInfoStack.drop(lvl)
-      unlockedStack     = unlockedStack.drop(lvl)
     }
 
     def z3CurrentZ3Blockers = blockersInfo.map(_._2._3)
 
+    def finfo(fi: Z3FunctionInvocation) = {
+      fi.tfd.id.uniqueName+fi.args.mkString("(", ", ", ")")
+    }
+
     def dumpBlockers = {
       blockersInfo.groupBy(_._2._1).toSeq.sortBy(_._1).foreach { case (gen, entries) =>
         reporter.debug("--- "+gen)
 
+
         for (((bast), (gen, origGen, ast, fis)) <- entries) {
-          reporter.debug(".     "+bast +" ~> "+fis.map(_.tfd.signature))
+          reporter.debug(f".     $bast%15s ~> "+fis.map(finfo).mkString(", "))
         }
       }
     }
@@ -202,20 +200,18 @@ class FairZ3Solver(val context : LeonContext, val program: Program)
     }
 
     private def registerBlocker(gen: Int, id: Z3AST, fis: Set[Z3FunctionInvocation]) {
-      if (!wasUnlocked(id)) {
-        val z3ast = z3.mkNot(id)
-        blockersInfo.get(id) match {
-          case Some((exGen, origGen, _, exFis)) =>
-            // PS: when recycling `b`s, this assertion becomes dangerous.
-            // It's better to simply take the max of the generations.
-            // assert(exGen == gen, "Mixing the same id "+id+" with various generations "+ exGen+" and "+gen)
+      val z3ast = z3.mkNot(id)
+      blockersInfo.get(id) match {
+        case Some((exGen, origGen, _, exFis)) =>
+          // PS: when recycling `b`s, this assertion becomes dangerous.
+          // It's better to simply take the max of the generations.
+          // assert(exGen == gen, "Mixing the same id "+id+" with various generations "+ exGen+" and "+gen)
 
-            val maxGen = gen max exGen
+          val minGen = gen min exGen
 
-            blockersInfo(id) = ((maxGen, origGen, z3ast, fis++exFis))
-          case None =>
-            blockersInfo(id) = ((gen, gen, z3ast, fis))
-        }
+          blockersInfo(id) = ((minGen, origGen, z3ast, fis++exFis))
+        case None =>
+          blockersInfo(id) = ((gen, gen, z3ast, fis))
       }
     }
 
@@ -267,35 +263,61 @@ class FairZ3Solver(val context : LeonContext, val program: Program)
       }
     }
 
-    def unlock(id: Z3AST) : Seq[Z3AST] = {
-      assert(blockersInfo contains id)
-      assert(!wasUnlocked(id))
+    private var defBlockers = Map[Z3FunctionInvocation, Z3AST]()
 
-      val (gen, origGen, _, fis) = blockersInfo(id)
-
-      blockersInfo -= id
+    def unlock(ids: Seq[Z3AST]) : Seq[Z3AST] = {
+      assert(ids.forall(id => blockersInfo contains id))
 
       var newClauses : Seq[Z3AST] = Seq.empty
 
-      var reintroducedSelf : Boolean = false
+      for (id <- ids) {
+        val (gen, _, _, fis) = blockersInfo(id)
+
+        blockersInfo -= id
 
-      for(fi <- fis) {
-        val template              = getTemplate(fi.tfd)
-        val (newExprs, newBlocks) = template.instantiate(id, fi.args)
+        var reintroducedSelf = false
 
-        for((i, fis2) <- newBlocks) {
-          registerBlocker(nextGeneration(gen), i, fis2)
-          if(i == id) {
-            reintroducedSelf = true
+        for (fi <- fis) {
+          val defBlocker = defBlockers.get(fi) match {
+            case Some(defBlocker) =>
+              // we already have defBlocker => f(args) = body
+              defBlocker
+            case None =>
+              // we need to define this defBlocker and link it to definition
+              val defBlocker = z3.mkFreshConst("d", z3.mkBoolSort)
+              defBlockers += fi -> defBlocker
+
+              val template              = getTemplate(fi.tfd)
+              val (newExprs, newBlocks) = template.instantiate(defBlocker, fi.args)
+
+              for((i, fis2) <- newBlocks) {
+                registerBlocker(nextGeneration(gen), i, fis2)
+              }
+
+              newClauses ++= newExprs
+              defBlocker
+          }
+
+          // We connect it to the defBlocker:   blocker => defBlocker
+          if (defBlocker != id) {
+            newClauses ++= List(z3.mkImplies(id, defBlocker))
           }
         }
 
-        newClauses ++= newExprs
       }
 
-      if(!reintroducedSelf) {
-        unlockedSet += id
-      }
+      context.reporter.debug(s"   - ${newClauses.size} new clauses")
+      //context.reporter.ifDebug { debug =>
+      //  debug(s" - new clauses:")
+      //  debug("@@@@")
+      //  for (cl <- newClauses) {
+      //    debug(""+cl)
+      //  }
+      //  debug("////")
+      //}
+
+      //dumpBlockers
+      //readLine()
 
       newClauses
     }
@@ -387,9 +409,9 @@ class FairZ3Solver(val context : LeonContext, val program: Program)
 
       reporter.debug(" - Running Z3 search...")
 
-      // reporter.debug("Searching in:\n"+solver.getAssertions.toSeq.mkString("\nAND\n"))
-      // reporter.debug("Unroll.  Assumptions:\n"+unrollingBank.z3CurrentZ3Blockers.mkString("  &&  "))
-      // reporter.debug("Userland Assumptions:\n"+assumptionsAsZ3.mkString("  &&  "))
+      //reporter.debug("Searching in:\n"+solver.getAssertions.toSeq.mkString("\nAND\n"))
+      //reporter.debug("Unroll.  Assumptions:\n"+unrollingBank.z3CurrentZ3Blockers.mkString("  &&  "))
+      //reporter.debug("Userland Assumptions:\n"+assumptionsAsZ3.mkString("  &&  "))
 
       solver.push() // FIXME: remove when z3 bug is fixed
       val res = solver.checkAssumptions((assumptionsAsZ3 ++ unrollingBank.z3CurrentZ3Blockers) :_*)
@@ -515,12 +537,10 @@ class FairZ3Solver(val context : LeonContext, val program: Program)
 
             reporter.debug(" - more unrollings")
 
-            for(id <- toRelease) {
-              val newClauses = unrollingBank.unlock(id)
+            val newClauses = unrollingBank.unlock(toRelease)
 
-              for(ncl <- newClauses) {
-                solver.assertCnstr(ncl)
-              }
+            for(ncl <- newClauses) {
+              solver.assertCnstr(ncl)
             }
 
             reporter.debug(" - finished unrolling")
diff --git a/src/main/scala/leon/solvers/z3/FunctionTemplate.scala b/src/main/scala/leon/solvers/z3/FunctionTemplate.scala
index 686025374aa00f2abecaeb4f4c6b076d52ac4485..ad173d712c8ae9e711450a84c8ba01b5b0e90729 100644
--- a/src/main/scala/leon/solvers/z3/FunctionTemplate.scala
+++ b/src/main/scala/leon/solvers/z3/FunctionTemplate.scala
@@ -16,7 +16,9 @@ import z3.scala._
 
 import scala.collection.mutable.{Set=>MutableSet,Map=>MutableMap}
 
-case class Z3FunctionInvocation(tfd: TypedFunDef, args: Seq[Z3AST])
+case class Z3FunctionInvocation(tfd: TypedFunDef, args: Seq[Z3AST]) {
+  override def toString = tfd.signature + args.mkString("(", ",", ")")
+}
 
 class FunctionTemplate private(
   solver: FairZ3Solver,