Skip to content
Snippets Groups Projects
AnalysisPhase.scala 4.87 KiB
package leon
package verification

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

import solvers.{Solver,TrivialSolver}
import solvers.z3.FairZ3Solver

import scala.collection.mutable.{Set => MutableSet}

object AnalysisPhase extends LeonPhase[Program,VerificationReport] {
  val name = "Analysis"
  val description = "Leon Verification"

  override def definedOptions = Set(
    LeonFlagOptionDef("no-luck", "--no-luck", "Disable early model detection in solving loop.")
  )

  def run(ctx: LeonContext)(program: Program) : VerificationReport = {
    val reporter = ctx.reporter

    val trivialSolver = new TrivialSolver(ctx)
    val fairZ3 = new FairZ3Solver(ctx)

    val solvers : Seq[Solver] = trivialSolver :: fairZ3 :: Nil

    solvers.foreach(_.setProgram(program))

    val defaultTactic = new DefaultTactic(reporter)
    defaultTactic.setProgram(program)
    val inductionTactic = new InductionTactic(reporter)
    inductionTactic.setProgram(program)

    def generateVerificationConditions : List[VerificationCondition] = {
      var allVCs: Seq[VerificationCondition] = Seq.empty
      val analysedFunctions: MutableSet[String] = MutableSet.empty

      for(funDef <- program.definedFunctions.toList.sortWith((fd1, fd2) => fd1 < fd2) if (Settings.functionsToAnalyse.isEmpty || Settings.functionsToAnalyse.contains(funDef.id.name))) {
        analysedFunctions += funDef.id.name

        val tactic: Tactic =
          if(funDef.annotations.contains("induct")) {
            inductionTactic
          } else {
            defaultTactic
          }

        if(funDef.body.isDefined) {
          allVCs ++= tactic.generatePreconditions(funDef)
          allVCs ++= tactic.generatePatternMatchingExhaustivenessChecks(funDef)
          allVCs ++= tactic.generatePostconditions(funDef)
          allVCs ++= tactic.generateMiscCorrectnessConditions(funDef)
          allVCs ++= tactic.generateArrayAccessChecks(funDef)
        }
        allVCs = allVCs.sortWith((vc1, vc2) => {
          val id1 = vc1.funDef.id.name
          val id2 = vc2.funDef.id.name
          if(id1 != id2) id1 < id2 else vc1 < vc2
        })
      }

      val notFound: Set[String] = Settings.functionsToAnalyse -- analysedFunctions
      notFound.foreach(fn => reporter.error("Did not find function \"" + fn + "\" though it was marked for analysis."))

      allVCs.toList
    }

    def checkVerificationConditions(vcs: Seq[VerificationCondition]) : VerificationReport = {
      for(vcInfo <- vcs) {
        val funDef = vcInfo.funDef
        val vc = vcInfo.condition

        reporter.info("Now considering '" + vcInfo.kind + "' VC for " + funDef.id + "...")
        reporter.info("Verification condition (" + vcInfo.kind + ") for ==== " + funDef.id + " ====")
        reporter.info(simplifyLets(vc))

        // try all solvers until one returns a meaningful answer
        var superseeded : Set[String] = Set.empty[String]
        solvers.find(se => {
          reporter.info("Trying with solver: " + se.shortDescription)
          if(superseeded(se.shortDescription) || superseeded(se.description)) {
            reporter.info("Solver was superseeded. Skipping.")
            false
          } else {
            superseeded = superseeded ++ Set(se.superseeds: _*)

            val t1 = System.nanoTime
            se.init()
            val solverResult = se.solve(vc)
            val t2 = System.nanoTime
            val dt = ((t2 - t1) / 1000000) / 1000.0

            solverResult match {
              case None => false
              case Some(true) => {
                reporter.info("==== VALID ====")

                vcInfo.value = Some(true)
                vcInfo.solvedWith = Some(se)
                vcInfo.time = Some(dt)

                true
              }
              case Some(false) => {
                reporter.error("==== INVALID ====")

                vcInfo.value = Some(false)
                vcInfo.solvedWith = Some(se)
                vcInfo.time = Some(dt)

                true
              }
            }
          }
        }) match {
          case None => {
            reporter.warning("No solver could prove or disprove the verification condition.")
          }
          case _ => 
        } 
      
      } 

      val report = new VerificationReport(vcs)
      reporter.info(report.summaryString)
      report
    }

    // We generate all function templates in advance.
    for(funDef <- program.definedFunctions if funDef.hasImplementation) {
      // this gets cached :D
      FunctionTemplate.mkTemplate(funDef)
    }

    val report = if(solvers.size > 1) {
      reporter.info("Running verification condition generation...")
      checkVerificationConditions(generateVerificationConditions)
    } else {
      reporter.warning("No solver specified. Cannot test verification conditions.")
      VerificationReport.emptyReport
    }

    report
  }
}