Skip to content
Snippets Groups Projects
Commit 55246deb authored by Régis Blanc's avatar Régis Blanc
Browse files

Introduce array access VC in DefaultTactic

Previously, array access were generated via a code transformation in
ArrayTransformation in xlang, where Array Select and Update were
wrapped in if-then-else with an Error in the else branch. They are
now natively supported in VC generation.  Some testcases --- that would
trigger bugs when --xlang is not used with functional array ---- are
included in this commit

The commit introduces a new abstraction to traverse Leon trees and collect
path conditions. This traverser is used for the implementation of the
VC generation for arrays.
parent 3e223b3d
No related branches found
No related tags found
No related merge requests found
...@@ -1086,6 +1086,27 @@ object TreeOps { ...@@ -1086,6 +1086,27 @@ object TreeOps {
def traverse(e: Expr): T def traverse(e: Expr): T
} }
class CollectorWithPaths[T](matcher: PartialFunction[Expr, T]) extends TransformerWithPC {
type C = Seq[Expr]
val initC = Nil
def register(e: Expr, path: C) = path :+ e
var results: Seq[(T, Expr)] = Nil
override def rec(e: Expr, path: C) = {
if(matcher.isDefinedAt(e)) {
val res = matcher(e)
results = results :+ (res, And(path))
e
} else super.rec(e, path)
}
def traverse(e: Expr) = {
results = Nil
rec(e, initC)
results
}
}
class ChooseCollectorWithPaths extends TransformerWithPC with Traverser[Seq[(Choose, Expr)]] { class ChooseCollectorWithPaths extends TransformerWithPC with Traverser[Seq[(Choose, Expr)]] {
type C = Seq[Expr] type C = Seq[Expr]
val initC = Nil val initC = Nil
......
...@@ -165,18 +165,28 @@ class DefaultTactic(reporter: Reporter) extends Tactic(reporter) { ...@@ -165,18 +165,28 @@ class DefaultTactic(reporter: Reporter) extends Tactic(reporter) {
val toRet = if (function.hasBody) { val toRet = if (function.hasBody) {
val cleanBody = matchToIfThenElse(function.body.get) val cleanBody = matchToIfThenElse(function.body.get)
val allPathConds = collectWithPathCondition((t => t match { val allPathConds = new CollectorWithPaths({
case Error("Index out of bound") => true case expr@ArraySelect(a, i) => (expr, a, i)
case _ => false case expr@ArrayUpdated(a, i, _) => (expr, a, i)
}), cleanBody) }).traverse(cleanBody)
val arrayAccessConditions = allPathConds.map{
case ((expr, array, index), pathCond) => {
val length = ArrayLength(array)
val negative = LessThan(index, IntLiteral(0))
val tooBig = GreaterEquals(index, length)
(And(pathCond, Or(negative, tooBig)), expr)
}
}
def withPrecIfDefined(conds: Seq[Expr]) : Expr = if (function.hasPrecondition) { def withPrecIfDefined(conds: Expr) : Expr = if (function.hasPrecondition) {
Not(And(mapGetWithChecks(matchToIfThenElse(function.precondition.get)), And(conds))) Not(And(mapGetWithChecks(matchToIfThenElse(function.precondition.get)), conds))
} else { } else {
Not(And(conds)) Not(conds)
} }
allPathConds.map(pc =>
arrayAccessConditions.map(pc =>
new VerificationCondition( new VerificationCondition(
withPrecIfDefined(pc._1), withPrecIfDefined(pc._1),
function, //if(function.fromLoop) function.parent.get else function, function, //if(function.fromLoop) function.parent.get else function,
...@@ -194,34 +204,42 @@ class DefaultTactic(reporter: Reporter) extends Tactic(reporter) { ...@@ -194,34 +204,42 @@ class DefaultTactic(reporter: Reporter) extends Tactic(reporter) {
generateMapAccessChecks(function) 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)] = { def collectWithPathCondition(matcher: Expr=>Boolean, expression: Expr) : Set[(Seq[Expr],Expr)] = {
var collected : Set[(Seq[Expr],Expr)] = Set.empty new CollectorWithPaths({
case e if matcher(e) => e
def rec(expr: Expr, path: List[Expr]) : Unit = { }).traverse(expression).map{
if(matcher(expr)) { case (e, And(es)) => (es, e)
collected = collected + ((path.reverse, expr)) case (e1, e2) => (Seq(e2), e1)
} }.toSet
expr match {
case Let(i,e,b) => {
rec(e, path)
rec(b, Equals(Variable(i), e) :: path)
}
case IfExpr(cond, thenn, elze) => {
rec(cond, path)
rec(thenn, 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
} }
// 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, thenn, elze) => {
// rec(cond, path)
// rec(thenn, 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
//}
} }
...@@ -33,41 +33,12 @@ object ArrayTransformation extends TransformationPhase { ...@@ -33,41 +33,12 @@ object ArrayTransformation extends TransformationPhase {
def transform(expr: Expr): Expr = (expr match { def transform(expr: Expr): Expr = (expr match {
case sel@ArraySelect(a, i) => {
val ra = transform(a)
val ri = transform(i)
val length = ArrayLength(ra)
val res = IfExpr(
And(LessEquals(IntLiteral(0), ri), LessThan(ri, length)),
ArraySelect(ra, ri).setType(sel.getType).setPos(sel),
Error("Index out of bound").setType(sel.getType).setPos(sel)
).setType(sel.getType)
res
}
case up@ArrayUpdate(a, i, v) => { case up@ArrayUpdate(a, i, v) => {
val ra = transform(a) val ra = transform(a)
val ri = transform(i) val ri = transform(i)
val rv = transform(v) val rv = transform(v)
val Variable(id) = ra val Variable(id) = ra
val length = ArrayLength(ra) Assignment(id, ArrayUpdated(ra, ri, rv).setType(ra.getType).setPos(up))
val res = IfExpr(
And(LessEquals(IntLiteral(0), ri), LessThan(ri, length)),
Assignment(id, ArrayUpdated(ra, ri, rv).setType(ra.getType).setPos(up)),
Error("Index out of bound").setType(UnitType).setPos(up)
).setType(UnitType)
res
}
case up@ArrayUpdated(a, i, v) => {
val ra = transform(a)
val ri = transform(i)
val rv = transform(v)
val length = ArrayLength(ra)
val res = IfExpr(
And(LessEquals(IntLiteral(0), ri), LessThan(ri, length)),
ArrayUpdated(ra, ri, rv).setType(ra.getType).setPos(up),
Error("Index out of bound").setType(ra.getType).setPos(up)
).setType(ra.getType)
res
} }
case ArrayClone(a) => { case ArrayClone(a) => {
val ra = transform(a) val ra = transform(a)
......
/* Copyright 2009-2013 EPFL, Lausanne */
import leon.Utils._
object Array4 {
def foo(a: Array[Int]): Int = {
a(2)
}
}
/* Copyright 2009-2013 EPFL, Lausanne */
import leon.Utils._
object Array4 {
def foo(a: Array[Int]): Int = {
require(a.length > 2)
a(2)
} ensuring(_ == 0)
}
import leon.Utils._
object Test {
def find(c: Array[Int], i: Int): Int = {
if(c(i) == i)
42
else
12
} ensuring(_ > 0)
}
/* Copyright 2009-2013 EPFL, Lausanne */
import leon.Utils._
object Array4 {
def foo(a: Array[Int]): Int = {
val tmp = a.updated(0, 0)
42
}
}
/* Copyright 2009-2013 EPFL, Lausanne */
import leon.Utils._
object Array4 {
def foo(a: Array[Int]): Int = {
require(a.length > 0)
val a2 = a.updated(1, 2)
a2(0)
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment