Skip to content
Snippets Groups Projects
InlineHoles.scala 2.82 KiB
/* Copyright 2009-2014 EPFL, Lausanne */

package leon
package synthesis
package rules

import scala.annotation.tailrec

import leon.utils._

import solvers._

import purescala.Common._
import purescala.Trees._
import purescala.TreeOps._
import purescala.TypeTrees._
import purescala.Extractors._

case object InlineHoles extends Rule("Inline-Holes") {
  override val priority = RulePriorityHoles

  def instantiateOn(sctx: SynthesisContext, p: Problem): Traversable[RuleInstantiation] = {

    @tailrec
    def inlineUntilHoles(e: Expr): Expr = {
      if (containsHoles(e)) {
        // Holes are exposed, no need to inline (yet)
        e
      } else {
        // Inline all functions "once" that contain holes
        val newE = postMap {
          case fi @ FunctionInvocation(tfd, args) if usesHoles(fi) && tfd.hasBody =>
            val inlined = replaceFromIDs((tfd.params.map(_.id) zip args).toMap, tfd.body.get)
            Some(inlined)

          case _ => None
        }(e)

        inlineUntilHoles(newE)
      }
    }

    def inlineHoles(phi: Expr): (List[Identifier], Expr) = {
      var newXs = List[Identifier]()

      val res = preMap {
        case h @ Hole(o) =>
          val tpe = h.getType
          val x = FreshIdentifier("h", true).setType(tpe)
          newXs ::= x

          Some(x.toVariable)

        case _ => None
      }(phi)

      (newXs.reverse, res)
    }

    if (usesHoles(p.phi)) {
      val pathsToCalls = CollectorWithPaths({
          case fi: FunctionInvocation if usesHoles(fi) => fi
      }).traverse(p.phi).map(_._2)

      val pc = if (pathsToCalls.nonEmpty) {
        Not(Or(pathsToCalls))
      } else {
        BooleanLiteral(false)
      }
      // Creates two alternative branches:
      // 1) a version with holes unreachable, on which this rule won't re-apply
      val sfact  = new TimeoutSolverFactory(sctx.solverFactory, 500L)
      val solver = SimpleSolverAPI(new TimeoutSolverFactory(sctx.solverFactory, 2000L))

      val(holesAvoidable, _) = solver.solveSAT(And(p.pc, pc))

      val avoid = if (holesAvoidable != Some(false)) {
        val newPhi = simplifyPaths(sfact)(And(pc, p.phi))
        val newProblem1 = p.copy(phi = newPhi)

        Some(RuleInstantiation.immediateDecomp(p, this, List(newProblem1), {
          case List(s) if (s.pre != BooleanLiteral(false)) => Some(s)
          case _ => None
        }, "Avoid Holes"))
      } else {
        None
      }


      // 2) a version with holes reachable to continue applying itself
      val newPhi                 = inlineUntilHoles(p.phi)
      val (newXs, newPhiInlined) = inlineHoles(newPhi)

      val newProblem2 = p.copy(phi = newPhiInlined, xs = p.xs ::: newXs)
      val rec = Some(RuleInstantiation.immediateDecomp(p, this, List(newProblem2), project(p.xs.size), "Inline Holes"))

      List(rec, avoid).flatten
    } else {
      Nil
    }
  }
}