Skip to content
Snippets Groups Projects
Commit 56678650 authored by Etienne Kneuss's avatar Etienne Kneuss Committed by Philippe Suter
Browse files

Decouple the validation, group VCs per functions

Decoupling the validation phase allows the web-interface to interract in
a much easier way.

Grouping VCs per functions makes the verification overview easier.
parent e8532c5b
No related branches found
No related tags found
No related merge requests found
...@@ -78,34 +78,40 @@ class Synthesizer(val context : LeonContext, ...@@ -78,34 +78,40 @@ class Synthesizer(val context : LeonContext,
res match { res match {
case Some((solution, true)) => case Some((solution, true)) =>
val ssol = solution.toSimplifiedExpr(context, program)
(solution, true) (solution, true)
case Some((sol, false)) => case Some((sol, false)) =>
val ssol = sol.toSimplifiedExpr(context, program) validateSolution(search, sol, 5000L)
reporter.info("Solution requires validation")
val (npr, fds) = solutionToProgram(sol)
val tsolver = new TimeoutSolver(new FairZ3Solver(silentContext), 10000L)
tsolver.setProgram(npr)
import verification.AnalysisPhase._
val vcs = generateVerificationConditions(reporter, npr, fds.map(_.id.name))
val vcreport = checkVerificationConditions(silentReporter, Seq(tsolver), vcs)
if (vcreport.totalValid == vcreport.totalConditions) {
(sol, true)
} else {
reporter.warning("Solution was invalid:")
reporter.warning(fds.map(ScalaPrinter(_)).mkString("\n\n"))
reporter.warning(vcreport.summaryString)
(new AndOrGraphPartialSolution(search.g, (task: TaskRunRule) => Solution.choose(task.problem), false).getSolution, false)
}
case None => case None =>
(new AndOrGraphPartialSolution(search.g, (task: TaskRunRule) => Solution.choose(task.problem), true).getSolution, false) (new AndOrGraphPartialSolution(search.g, (task: TaskRunRule) => Solution.choose(task.problem), true).getSolution, false)
} }
} }
def validateSolution(search: AndOrGraphSearch[TaskRunRule, TaskTryRules, Solution], sol: Solution, timeoutMs: Long): (Solution, Boolean) = {
import verification.AnalysisPhase._
import verification.VerificationContext
val ssol = sol.toSimplifiedExpr(context, program)
reporter.info("Solution requires validation")
val (npr, fds) = solutionToProgram(sol)
val tsolver = new TimeoutSolver(new FairZ3Solver(silentContext), timeoutMs)
tsolver.setProgram(npr)
val vcs = generateVerificationConditions(reporter, npr, fds.map(_.id.name))
val vctx = VerificationContext(context, Seq(tsolver), silentReporter)
val vcreport = checkVerificationConditions(vctx, vcs)
if (vcreport.totalValid == vcreport.totalConditions) {
(sol, true)
} else {
reporter.warning("Solution was invalid:")
reporter.warning(fds.map(ScalaPrinter(_)).mkString("\n\n"))
reporter.warning(vcreport.summaryString)
(new AndOrGraphPartialSolution(search.g, (task: TaskRunRule) => Solution.choose(task.problem), false).getSolution, false)
}
}
// Returns the new program and the new functions generated for this // Returns the new program and the new functions generated for this
def solutionToProgram(sol: Solution): (Program, Set[FunDef]) = { def solutionToProgram(sol: Solution): (Program, Set[FunDef]) = {
import purescala.TypeTrees.TupleType import purescala.TypeTrees.TupleType
......
...@@ -21,17 +21,15 @@ object AnalysisPhase extends LeonPhase[Program,VerificationReport] { ...@@ -21,17 +21,15 @@ object AnalysisPhase extends LeonPhase[Program,VerificationReport] {
LeonValueOptionDef("timeout", "--timeout=T", "Timeout after T seconds when trying to prove a verification condition.") LeonValueOptionDef("timeout", "--timeout=T", "Timeout after T seconds when trying to prove a verification condition.")
) )
def generateVerificationConditions(reporter: Reporter, program: Program, functionsToAnalyse: Set[String]) : List[VerificationCondition] = { def generateVerificationConditions(reporter: Reporter, program: Program, functionsToAnalyse: Set[String]): Map[FunDef, List[VerificationCondition]] = {
val defaultTactic = new DefaultTactic(reporter) val defaultTactic = new DefaultTactic(reporter)
defaultTactic.setProgram(program) defaultTactic.setProgram(program)
val inductionTactic = new InductionTactic(reporter) val inductionTactic = new InductionTactic(reporter)
inductionTactic.setProgram(program) inductionTactic.setProgram(program)
var allVCs: Seq[VerificationCondition] = Seq.empty var allVCs = Map[FunDef, List[VerificationCondition]]()
val analysedFunctions: MutableSet[String] = MutableSet.empty
for(funDef <- program.definedFunctions.toList.sortWith((fd1, fd2) => fd1 < fd2) if (functionsToAnalyse.isEmpty || functionsToAnalyse.contains(funDef.id.name))) { for(funDef <- program.definedFunctions.toList.sortWith((fd1, fd2) => fd1 < fd2) if (functionsToAnalyse.isEmpty || functionsToAnalyse.contains(funDef.id.name))) {
analysedFunctions += funDef.id.name
val tactic: Tactic = val tactic: Tactic =
if(funDef.annotations.contains("induct")) { if(funDef.annotations.contains("induct")) {
...@@ -41,27 +39,27 @@ object AnalysisPhase extends LeonPhase[Program,VerificationReport] { ...@@ -41,27 +39,27 @@ object AnalysisPhase extends LeonPhase[Program,VerificationReport] {
} }
if(funDef.body.isDefined) { if(funDef.body.isDefined) {
allVCs ++= tactic.generatePreconditions(funDef) val funVCs = tactic.generatePreconditions(funDef) ++
allVCs ++= tactic.generatePatternMatchingExhaustivenessChecks(funDef) tactic.generatePatternMatchingExhaustivenessChecks(funDef) ++
allVCs ++= tactic.generatePostconditions(funDef) tactic.generatePostconditions(funDef) ++
allVCs ++= tactic.generateMiscCorrectnessConditions(funDef) tactic.generateMiscCorrectnessConditions(funDef) ++
allVCs ++= tactic.generateArrayAccessChecks(funDef) tactic.generateArrayAccessChecks(funDef)
allVCs += funDef -> funVCs.toList
} }
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 = functionsToAnalyse -- analysedFunctions val notFound = functionsToAnalyse -- allVCs.keys.map(_.id.name)
notFound.foreach(fn => reporter.error("Did not find function \"" + fn + "\" though it was marked for analysis.")) notFound.foreach(fn => reporter.error("Did not find function \"" + fn + "\" though it was marked for analysis."))
allVCs.toList allVCs
} }
def checkVerificationConditions(reporter: Reporter, solvers: Seq[Solver], vcs: Seq[VerificationCondition]) : VerificationReport = { def checkVerificationConditions(vctx: VerificationContext, vcs: Map[FunDef, List[VerificationCondition]]) : VerificationReport = {
for(vcInfo <- vcs) { val reporter = vctx.reporter
val solvers = vctx.solvers
for((funDef, vcs) <- vcs.toSeq.sortWith((a,b) => a._1 < b._1); vcInfo <- vcs if !vctx.shouldStop.get()) {
val funDef = vcInfo.funDef val funDef = vcInfo.funDef
val vc = vcInfo.condition val vc = vcInfo.condition
...@@ -88,37 +86,43 @@ object AnalysisPhase extends LeonPhase[Program,VerificationReport] { ...@@ -88,37 +86,43 @@ object AnalysisPhase extends LeonPhase[Program,VerificationReport] {
val dt = ((t2 - t1) / 1000000) / 1000.0 val dt = ((t2 - t1) / 1000000) / 1000.0
solverResult match { solverResult match {
case None => false case _ if vctx.shouldStop.get() =>
case Some(true) => { reporter.info("=== CANCELLED ===")
false
case None =>
false
case Some(true) =>
reporter.info("==== VALID ====") reporter.info("==== VALID ====")
vcInfo.hasValue = true
vcInfo.value = Some(true) vcInfo.value = Some(true)
vcInfo.solvedWith = Some(se) vcInfo.solvedWith = Some(se)
vcInfo.time = Some(dt) vcInfo.time = Some(dt)
true true
}
case Some(false) => { case Some(false) =>
reporter.error("Found counter-example : ") reporter.error("Found counter-example : ")
reporter.error(counterexample.toSeq.sortBy(_._1.name).map(p => p._1 + " -> " + p._2).mkString("\n")) reporter.error(counterexample.toSeq.sortBy(_._1.name).map(p => p._1 + " -> " + p._2).mkString("\n"))
reporter.error("==== INVALID ====") reporter.error("==== INVALID ====")
vcInfo.hasValue = true
vcInfo.value = Some(false) vcInfo.value = Some(false)
vcInfo.solvedWith = Some(se) vcInfo.solvedWith = Some(se)
vcInfo.counterExample = Some(counterexample) vcInfo.counterExample = Some(counterexample)
vcInfo.time = Some(dt) vcInfo.time = Some(dt)
true true
}
} }
} }
}) match { }) match {
case None => { case None => {
vcInfo.hasValue = true
reporter.warning("No solver could prove or disprove the verification condition.") reporter.warning("No solver could prove or disprove the verification condition.")
} }
case _ => case _ =>
} }
}
}
val report = new VerificationReport(vcs) val report = new VerificationReport(vcs)
report report
...@@ -151,11 +155,12 @@ object AnalysisPhase extends LeonPhase[Program,VerificationReport] { ...@@ -151,11 +155,12 @@ object AnalysisPhase extends LeonPhase[Program,VerificationReport] {
solvers.foreach(_.setProgram(program)) solvers.foreach(_.setProgram(program))
val vctx = VerificationContext(ctx, solvers, reporter)
val report = if(solvers.size > 1) { val report = if(solvers.size > 1) {
reporter.info("Running verification condition generation...") reporter.info("Running verification condition generation...")
val vcs = generateVerificationConditions(reporter, program, functionsToAnalyse) val vcs = generateVerificationConditions(reporter, program, functionsToAnalyse)
checkVerificationConditions(reporter, solvers, vcs) checkVerificationConditions(vctx, vcs)
} else { } else {
reporter.warning("No solver specified. Cannot test verification conditions.") reporter.warning("No solver specified. Cannot test verification conditions.")
VerificationReport.emptyReport VerificationReport.emptyReport
......
...@@ -11,6 +11,7 @@ class VerificationCondition(val condition: Expr, val funDef: FunDef, val kind: V ...@@ -11,6 +11,7 @@ class VerificationCondition(val condition: Expr, val funDef: FunDef, val kind: V
// None = still unknown // None = still unknown
// Some(true) = valid // Some(true) = valid
// Some(false) = valid // Some(false) = valid
var hasValue = false
var value : Option[Boolean] = None var value : Option[Boolean] = None
var solvedWith : Option[Solver] = None var solvedWith : Option[Solver] = None
var time : Option[Double] = None var time : Option[Double] = None
......
package leon
package verification
import solvers.Solver
import java.util.concurrent.atomic.AtomicBoolean
case class VerificationContext (
context: LeonContext,
solvers: Seq[Solver],
reporter: Reporter,
shouldStop: AtomicBoolean = new AtomicBoolean(false)
)
package leon.verification package leon
package verification
import purescala.Definitions.FunDef
class VerificationReport(val fvcs: Map[FunDef, List[VerificationCondition]]) {
val conditions : Seq[VerificationCondition] = fvcs.flatMap(_._2).toSeq.sortWith {
(vc1,vc2) =>
val id1 = vc1.funDef.id.name
val id2 = vc2.funDef.id.name
if(id1 != id2) id1 < id2 else vc1 < vc2
}
class VerificationReport(val conditions : Seq[VerificationCondition]) {
lazy val totalConditions : Int = conditions.size lazy val totalConditions : Int = conditions.size
lazy val totalTime : Double = conditions.foldLeft(0.0d)((t,c) => t + c.time.getOrElse(0.0d)) lazy val totalTime : Double = conditions.foldLeft(0.0d)((t,c) => t + c.time.getOrElse(0.0d))
...@@ -25,7 +35,7 @@ class VerificationReport(val conditions : Seq[VerificationCondition]) { ...@@ -25,7 +35,7 @@ class VerificationReport(val conditions : Seq[VerificationCondition]) {
} }
object VerificationReport { object VerificationReport {
def emptyReport : VerificationReport = new VerificationReport(Nil) def emptyReport : VerificationReport = new VerificationReport(Map())
private def fit(str : String, maxLength : Int) : String = { private def fit(str : String, maxLength : Int) : String = {
if(str.length <= maxLength) { if(str.length <= maxLength) {
......
...@@ -44,7 +44,12 @@ object XlangAnalysisPhase extends LeonPhase[Program, VerificationReport] { ...@@ -44,7 +44,12 @@ object XlangAnalysisPhase extends LeonPhase[Program, VerificationReport] {
} }
case opt => opt case opt => opt
} }
val vr = AnalysisPhase.run(ctx.copy(options = newOptions))(pgm4) val vr = AnalysisPhase.run(ctx.copy(options = newOptions))(pgm4)
completeVerificationReport(vr, parents, functionWasLoop _)
}
def completeVerificationReport(vr: VerificationReport, parents: Map[FunDef, FunDef], functionWasLoop: FunDef => Boolean): VerificationReport = {
val vcs = vr.conditions val vcs = vr.conditions
//this is enough to convert invariant postcondition and inductive conditions. However the initial validity //this is enough to convert invariant postcondition and inductive conditions. However the initial validity
...@@ -52,7 +57,9 @@ object XlangAnalysisPhase extends LeonPhase[Program, VerificationReport] { ...@@ -52,7 +57,9 @@ object XlangAnalysisPhase extends LeonPhase[Program, VerificationReport] {
//To fix this, we need some information in the VCs about which function the precondtion refers to //To fix this, we need some information in the VCs about which function the precondtion refers to
//although we could arguably say that the term precondition is general enough to refer both to a loop invariant //although we could arguably say that the term precondition is general enough to refer both to a loop invariant
//precondition and a function invocation precondition //precondition and a function invocation precondition
val freshVcs = vcs.map(vc => { var freshFtoVcs = Map[FunDef, List[VerificationCondition]]()
for (vc <- vcs) {
val funDef = vc.funDef val funDef = vc.funDef
if(functionWasLoop(funDef)) { if(functionWasLoop(funDef)) {
val freshVc = new VerificationCondition( val freshVc = new VerificationCondition(
...@@ -64,17 +71,13 @@ object XlangAnalysisPhase extends LeonPhase[Program, VerificationReport] { ...@@ -64,17 +71,13 @@ object XlangAnalysisPhase extends LeonPhase[Program, VerificationReport] {
freshVc.value = vc.value freshVc.value = vc.value
freshVc.solvedWith = vc.solvedWith freshVc.solvedWith = vc.solvedWith
freshVc.time = vc.time freshVc.time = vc.time
freshVc freshFtoVcs += freshVc.funDef -> (freshVc :: freshFtoVcs.getOrElse(freshVc.funDef, List()))
} else vc } else {
}) freshFtoVcs += vc.funDef -> (vc :: freshFtoVcs.getOrElse(vc.funDef, List()))
}
val sortedFreshVcs = freshVcs.sortWith((vc1, vc2) => { }
val id1 = vc1.funDef.id.name
val id2 = vc2.funDef.id.name
if(id1 != id2) id1 < id2 else vc1 < vc2
})
new VerificationReport(sortedFreshVcs) new VerificationReport(freshFtoVcs)
} }
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment