-
Etienne Kneuss authoredEtienne Kneuss authored
DefaultTactic.scala 9.33 KiB
package leon
import purescala.Common._
import purescala.Trees._
import purescala.TreeOps._
import purescala.Extractors._
import purescala.Definitions._
import Extensions.Tactic
import scala.collection.mutable.{Map => MutableMap}
class DefaultTactic(reporter: Reporter) extends Tactic(reporter) {
val description = "Default verification condition generation approach"
override val shortDescription = "default"
var _prog : Option[Program] = None
def program : Program = _prog match {
case None => throw new Exception("Program never set in DefaultTactic.")
case Some(p) => p
}
override def setProgram(program: Program) : Unit = {
_prog = Some(program)
}
def generatePostconditions(functionDefinition: FunDef) : Seq[VerificationCondition] = {
assert(functionDefinition.body.isDefined)
val prec = functionDefinition.precondition
val post = functionDefinition.postcondition
val body = matchToIfThenElse(functionDefinition.body.get)
if(post.isEmpty) {
Seq.empty
} else {
val theExpr = {
val resFresh = FreshIdentifier("result", true).setType(body.getType)
val bodyAndPost = Let(resFresh, body, replace(Map(ResultVariable() -> Variable(resFresh)), matchToIfThenElse(post.get)))
val withPrec = if(prec.isEmpty) {
bodyAndPost
} else {
Implies(matchToIfThenElse(prec.get), bodyAndPost)
}
import Analysis._
if(Settings.zeroInlining) {
withPrec
} else {
if(Settings.experimental) {
reporter.info("Raw:")
reporter.info(withPrec)
reporter.info("Raw, expanded:")
reporter.info(expandLets(withPrec))
}
reporter.info(" - inlining...")
val expr0 = inlineNonRecursiveFunctions(program, withPrec)
if(Settings.experimental) {
reporter.info("Inlined:")
reporter.info(expr0)
reporter.info("Inlined, expanded:")
reporter.info(expandLets(expr0))
}
reporter.info(" - unrolling...")
val expr1 = unrollRecursiveFunctions(program, expr0, Settings.unrollingLevel)
if(Settings.experimental) {
reporter.info("Unrolled:")
reporter.info(expr1)
reporter.info("Unrolled, expanded:")
reporter.info(expandLets(expr1))
}
reporter.info(" - inlining contracts...")
val expr2 = inlineContracts(expr1)
if(Settings.experimental) {
reporter.info("Contract'ed:")
reporter.info(expr2)
reporter.info("Contract'ed, expanded:")
reporter.info(expandLets(expr2))
}
expr2
}
}
if(functionDefinition.fromLoop)
Seq(new VerificationCondition(theExpr, functionDefinition.parent.get, VCKind.InvariantPost, this.asInstanceOf[DefaultTactic]).setPosInfo(functionDefinition))
else
Seq(new VerificationCondition(theExpr, functionDefinition, VCKind.Postcondition, this.asInstanceOf[DefaultTactic]))
}
}
def generatePreconditions(function: FunDef) : Seq[VerificationCondition] = {
val toRet = if(function.hasBody) {
val cleanBody = expandLets(matchToIfThenElse(function.body.get))
val allPathConds = collectWithPathCondition((t => t match {
case FunctionInvocation(fd, _) if(fd.hasPrecondition) => true
case _ => false
}), cleanBody)
def withPrecIfDefined(path: Seq[Expr], shouldHold: Expr) : Expr = if(function.hasPrecondition) {
Not(And(And(matchToIfThenElse(function.precondition.get) +: path), Not(shouldHold)))
} else {
Not(And(And(path), Not(shouldHold)))
}
allPathConds.map(pc => {
val path : Seq[Expr] = pc._1
val fi = pc._2.asInstanceOf[FunctionInvocation]
val FunctionInvocation(fd, args) = fi
val prec : Expr = freshenLocals(matchToIfThenElse(fd.precondition.get))
val newLetIDs = fd.args.map(a => FreshIdentifier("arg_" + a.id.name, true).setType(a.tpe))
val substMap = Map[Expr,Expr]((fd.args.map(_.toVariable) zip newLetIDs.map(Variable(_))) : _*)
val newBody : Expr = replace(substMap, prec)
val newCall : Expr = (newLetIDs zip args).foldRight(newBody)((iap, e) => Let(iap._1, iap._2, e))
if(fd.fromLoop)
new VerificationCondition(
withPrecIfDefined(path, newCall),
fd.parent.get,
if(fd == function) VCKind.InvariantInd else VCKind.InvariantInit,
this.asInstanceOf[DefaultTactic]).setPosInfo(fd)
else
new VerificationCondition(
withPrecIfDefined(path, newCall),
function,
VCKind.Precondition,
this.asInstanceOf[DefaultTactic]).setPosInfo(fi)
}).toSeq
} else {
Seq.empty
}
// println("PRECS VCs FOR " + function.id.name)
// println(toRet.toList.map(vc => vc.posInfo + " -- " + vc.condition).mkString("\n\n"))
toRet
}
def generatePatternMatchingExhaustivenessChecks(function: FunDef) : Seq[VerificationCondition] = {
val toRet = if(function.hasBody) {
val cleanBody = matchToIfThenElse(function.body.get)
val allPathConds = collectWithPathCondition((t => t match {
case Error("non-exhaustive match") => true
case _ => false
}), cleanBody)
def withPrecIfDefined(conds: Seq[Expr]) : Expr = if(function.hasPrecondition) {
Not(And(matchToIfThenElse(function.precondition.get), And(conds)))
} else {
Not(And(conds))
}
allPathConds.map(pc =>
new VerificationCondition(
withPrecIfDefined(pc._1),
if(function.fromLoop) function.parent.get else function,
VCKind.ExhaustiveMatch,
this.asInstanceOf[DefaultTactic]).setPosInfo(pc._2.asInstanceOf[Error])
).toSeq
} else {
Seq.empty
}
// println("MATCHING VCs FOR " + function.id.name)
// println(toRet.toList.map(vc => vc.posInfo + " -- " + vc.condition).mkString("\n\n"))
toRet
}
def generateMapAccessChecks(function: FunDef) : Seq[VerificationCondition] = {
val toRet = if (function.hasBody) {
val cleanBody = mapGetWithChecks(matchToIfThenElse(function.body.get))
val allPathConds = collectWithPathCondition((t => t match {
case Error("key not found for map access") => true
case _ => false
}), cleanBody)
def withPrecIfDefined(conds: Seq[Expr]) : Expr = if (function.hasPrecondition) {
Not(And(mapGetWithChecks(matchToIfThenElse(function.precondition.get)), And(conds)))
} else {
Not(And(conds))
}
allPathConds.map(pc =>
new VerificationCondition(
withPrecIfDefined(pc._1),
if(function.fromLoop) function.parent.get else function,
VCKind.MapAccess,
this.asInstanceOf[DefaultTactic]).setPosInfo(pc._2.asInstanceOf[Error])
).toSeq
} else {
Seq.empty
}
toRet
}
def generateArrayAccessChecks(function: FunDef) : Seq[VerificationCondition] = {
val toRet = if (function.hasBody) {
val cleanBody = matchToIfThenElse(function.body.get)
val allPathConds = collectWithPathCondition((t => t match {
case Error("Index out of bound") => true
case _ => false
}), cleanBody)
def withPrecIfDefined(conds: Seq[Expr]) : Expr = if (function.hasPrecondition) {
Not(And(mapGetWithChecks(matchToIfThenElse(function.precondition.get)), And(conds)))
} else {
Not(And(conds))
}
allPathConds.map(pc =>
new VerificationCondition(
withPrecIfDefined(pc._1),
if(function.fromLoop) function.parent.get else function,
VCKind.ArrayAccess,
this.asInstanceOf[DefaultTactic]).setPosInfo(pc._2.asInstanceOf[Error])
).toSeq
} else {
Seq.empty
}
toRet
}
def generateMiscCorrectnessConditions(function: FunDef) : Seq[VerificationCondition] = {
generateMapAccessChecks(function)
}
// prec: there should be no lets and no pattern-matching in this expression
def collectWithPathCondition(matcher: Expr=>Boolean, expression: Expr) : Set[(Seq[Expr],Expr)] = {
var collected : Set[(Seq[Expr],Expr)] = Set.empty
def rec(expr: Expr, path: List[Expr]) : Unit = {
if(matcher(expr)) {
collected = collected + ((path.reverse, expr))
}
expr match {
case Let(i,e,b) => {
rec(e, path)
rec(b, Equals(Variable(i), e) :: path)
}
case IfExpr(cond, then, elze) => {
rec(cond, path)
rec(then, cond :: path)
rec(elze, Not(cond) :: path)
}
case NAryOperator(args, _) => args.foreach(rec(_, path))
case BinaryOperator(t1, t2, _) => rec(t1, path); rec(t2, path)
case UnaryOperator(t, _) => rec(t, path)
case t : Terminal => ;
case _ => scala.sys.error("Unhandled tree in collectWithPathCondition : " + expr)
}
}
rec(expression, Nil)
collected
}
}