Skip to content

Commit

Permalink
Fix model checking of inlined adt invariant
Browse files Browse the repository at this point in the history
  • Loading branch information
romac committed Jun 11, 2020
1 parent 088ab0c commit 6e08d3b
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ trait DefaultTactic extends Tactic {
case (a @ ADT(aid, tps, args), path) if a.getConstructor.sort.hasInvariant =>
val invId = a.getConstructor.sort.invariant.get.id
val condition = path implies FunctionInvocation(invId, tps, Seq(a))
VC(condition, id, VCKind.AdtInvariant(invId), false).setPos(a)
VC(condition, id, VCKind.AdtInvariant(invId, condition), false).setPos(a)
}(getFunction(id).fullBody)
}
}
Expand Down
6 changes: 3 additions & 3 deletions core/src/main/scala/stainless/verification/TypeChecker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -797,7 +797,7 @@ trait TypeChecker {
val trInv =
if (sort.hasInvariant) {
val inv = sort.typed(tps).invariant.get
val invKind = VCKind.AdtInvariant(id)
val invKind = VCKind.AdtInvariant(id, inv.applied(Seq(e)))
val tc2 = tc.withVCKind(invKind).setPos(e)
if (inv.flags.contains(InlineInvariant)) {
val (tc3, freshener) = tc.freshBindWithValues(inv.params, Seq(e))
Expand Down Expand Up @@ -836,10 +836,10 @@ trait TypeChecker {
val trInv =
if (sort.hasInvariant) {
val inv = sort.typed(tps).invariant.get
val invKind = VCKind.AdtInvariant(inv.id)
val invKind = VCKind.AdtInvariant(inv.id, inv.applied(Seq(e)))
val tc2 = tc.withVCKind(invKind).setPos(e)
if (inv.flags.contains(InlineInvariant)) {
val (tc3, freshener) = tc.freshBindWithValues(inv.params, Seq(e))
val (tc3, freshener) = tc2.freshBindWithValues(inv.params, Seq(e))
buildVC(tc3, freshener.transform(inv.fullBody))
} else {
buildVC(tc2, inv.applied(Seq(e)))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,12 +144,12 @@ trait VerificationChecker { self =>
* - rewrite the invariant's invocation to be applied to this new variable instead.
* - evaluate the resulting condition under the new model.
*/
protected def checkAdtInvariantModel(vc: VC, invId: Identifier, model: Model): VCStatus = {
protected def checkAdtInvariantModel(vc: VC, invId: Identifier, originalCondition: Expr, model: Model): VCStatus = {
import inox.evaluators.EvaluationResults._

val Seq((inv, adt, path)) = collectWithPC(vc.condition) {
case (inv @ FunctionInvocation(`invId`, _, Seq(adt: ADT)), path) => (inv, adt, path)
}
val condition = simplifyExpr(
simplifyLets(simplifyAssertions(originalCondition))
)(PurityOptions.assumeChecked)

def success: VCStatus = {
reporter.debug("- Model validated.")
Expand All @@ -161,6 +161,10 @@ trait VerificationChecker { self =>
VCStatus.Unknown
}

val Seq((inv, adt, path)) = collectWithPC(condition) {
case (inv @ FunctionInvocation(`invId`, _, Seq(adt: ADT)), path) => (inv, adt, path)
}

evaluator.eval(path.toClause, model) match {
case Successful(BooleanLiteral(true)) => // path condition was true, we must evaluate invariant
case Successful(BooleanLiteral(false)) => return success
Expand All @@ -184,7 +188,7 @@ trait VerificationChecker { self =>
val adtVar = Variable(FreshIdentifier("adt"), adt.getType(symbols), Seq())
val newInv = FunctionInvocation(invId, inv.tps, Seq(adtVar))
val newModel = inox.Model(program)(model.vars + (adtVar.toVal -> newAdt), model.chooses)
val newCondition = exprOps.replace(Map(inv -> newInv), vc.condition)
val newCondition = exprOps.replace(Map(inv -> newInv), condition)

evaluator.eval(newCondition, newModel) match {
case Successful(BooleanLiteral(false)) => success
Expand Down Expand Up @@ -252,8 +256,8 @@ trait VerificationChecker { self =>
VCResult(VCStatus.Valid, s.getResultSolver, Some(time))

case SatWithModel(model) if checkModels && vc.kind.isInstanceOf[VCKind.AdtInvariant] =>
val VCKind.AdtInvariant(invId) = vc.kind
val status = checkAdtInvariantModel(vc, invId, model)
val VCKind.AdtInvariant(invId, expr: Expr) = vc.kind
val status = checkAdtInvariantModel(vc, invId, expr, model)
VCResult(status, s.getResultSolver, Some(time))

case SatWithModel(model) if !vc.satisfiability =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ object VCKind {
case object InvariantSat extends VCKind("invariant satisfiability", "inv. sat")
case class AssertErr(err: String) extends VCKind("body assertion: " + err, "assert.")
case class Error(err: String) extends VCKind(err, "error")
case class AdtInvariant(inv: Identifier) extends VCKind("adt invariant", "adt inv.")

case class AdtInvariant(inv: Identifier, expr: ast.Trees#Expr) extends VCKind("adt invariant", "adt inv.")

def fromErr(optErr: Option[String]) = {
optErr.map { err =>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@

import stainless.lang._
import stainless.annotation._
import stainless.collection._

object inlineInv {

@inlineInvariant
sealed abstract class Toto

case class Foo(x: BigInt) extends Toto {
require(x > 10)
}

def bad: Toto = {
Foo(5)
}

def ok: Toto = {
Foo(15)
}

}

0 comments on commit 6e08d3b

Please sign in to comment.