Skip to content

Commit 6e08d3b

Browse files
committed
Fix model checking of inlined adt invariant
1 parent 088ab0c commit 6e08d3b

File tree

5 files changed

+40
-12
lines changed

5 files changed

+40
-12
lines changed

core/src/main/scala/stainless/verification/DefaultTactic.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ trait DefaultTactic extends Tactic {
126126
case (a @ ADT(aid, tps, args), path) if a.getConstructor.sort.hasInvariant =>
127127
val invId = a.getConstructor.sort.invariant.get.id
128128
val condition = path implies FunctionInvocation(invId, tps, Seq(a))
129-
VC(condition, id, VCKind.AdtInvariant(invId), false).setPos(a)
129+
VC(condition, id, VCKind.AdtInvariant(invId, condition), false).setPos(a)
130130
}(getFunction(id).fullBody)
131131
}
132132
}

core/src/main/scala/stainless/verification/TypeChecker.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -797,7 +797,7 @@ trait TypeChecker {
797797
val trInv =
798798
if (sort.hasInvariant) {
799799
val inv = sort.typed(tps).invariant.get
800-
val invKind = VCKind.AdtInvariant(id)
800+
val invKind = VCKind.AdtInvariant(id, inv.applied(Seq(e)))
801801
val tc2 = tc.withVCKind(invKind).setPos(e)
802802
if (inv.flags.contains(InlineInvariant)) {
803803
val (tc3, freshener) = tc.freshBindWithValues(inv.params, Seq(e))
@@ -836,10 +836,10 @@ trait TypeChecker {
836836
val trInv =
837837
if (sort.hasInvariant) {
838838
val inv = sort.typed(tps).invariant.get
839-
val invKind = VCKind.AdtInvariant(inv.id)
839+
val invKind = VCKind.AdtInvariant(inv.id, inv.applied(Seq(e)))
840840
val tc2 = tc.withVCKind(invKind).setPos(e)
841841
if (inv.flags.contains(InlineInvariant)) {
842-
val (tc3, freshener) = tc.freshBindWithValues(inv.params, Seq(e))
842+
val (tc3, freshener) = tc2.freshBindWithValues(inv.params, Seq(e))
843843
buildVC(tc3, freshener.transform(inv.fullBody))
844844
} else {
845845
buildVC(tc2, inv.applied(Seq(e)))

core/src/main/scala/stainless/verification/VerificationChecker.scala

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -144,12 +144,12 @@ trait VerificationChecker { self =>
144144
* - rewrite the invariant's invocation to be applied to this new variable instead.
145145
* - evaluate the resulting condition under the new model.
146146
*/
147-
protected def checkAdtInvariantModel(vc: VC, invId: Identifier, model: Model): VCStatus = {
147+
protected def checkAdtInvariantModel(vc: VC, invId: Identifier, originalCondition: Expr, model: Model): VCStatus = {
148148
import inox.evaluators.EvaluationResults._
149149

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

154154
def success: VCStatus = {
155155
reporter.debug("- Model validated.")
@@ -161,6 +161,10 @@ trait VerificationChecker { self =>
161161
VCStatus.Unknown
162162
}
163163

164+
val Seq((inv, adt, path)) = collectWithPC(condition) {
165+
case (inv @ FunctionInvocation(`invId`, _, Seq(adt: ADT)), path) => (inv, adt, path)
166+
}
167+
164168
evaluator.eval(path.toClause, model) match {
165169
case Successful(BooleanLiteral(true)) => // path condition was true, we must evaluate invariant
166170
case Successful(BooleanLiteral(false)) => return success
@@ -184,7 +188,7 @@ trait VerificationChecker { self =>
184188
val adtVar = Variable(FreshIdentifier("adt"), adt.getType(symbols), Seq())
185189
val newInv = FunctionInvocation(invId, inv.tps, Seq(adtVar))
186190
val newModel = inox.Model(program)(model.vars + (adtVar.toVal -> newAdt), model.chooses)
187-
val newCondition = exprOps.replace(Map(inv -> newInv), vc.condition)
191+
val newCondition = exprOps.replace(Map(inv -> newInv), condition)
188192

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

254258
case SatWithModel(model) if checkModels && vc.kind.isInstanceOf[VCKind.AdtInvariant] =>
255-
val VCKind.AdtInvariant(invId) = vc.kind
256-
val status = checkAdtInvariantModel(vc, invId, model)
259+
val VCKind.AdtInvariant(invId, expr: Expr) = vc.kind
260+
val status = checkAdtInvariantModel(vc, invId, expr, model)
257261
VCResult(status, s.getResultSolver, Some(time))
258262

259263
case SatWithModel(model) if !vc.satisfiability =>

core/src/main/scala/stainless/verification/VerificationConditions.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ object VCKind {
4141
case object InvariantSat extends VCKind("invariant satisfiability", "inv. sat")
4242
case class AssertErr(err: String) extends VCKind("body assertion: " + err, "assert.")
4343
case class Error(err: String) extends VCKind(err, "error")
44-
case class AdtInvariant(inv: Identifier) extends VCKind("adt invariant", "adt inv.")
44+
45+
case class AdtInvariant(inv: Identifier, expr: ast.Trees#Expr) extends VCKind("adt invariant", "adt inv.")
4546

4647
def fromErr(optErr: Option[String]) = {
4748
optErr.map { err =>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
2+
import stainless.lang._
3+
import stainless.annotation._
4+
import stainless.collection._
5+
6+
object inlineInv {
7+
8+
@inlineInvariant
9+
sealed abstract class Toto
10+
11+
case class Foo(x: BigInt) extends Toto {
12+
require(x > 10)
13+
}
14+
15+
def bad: Toto = {
16+
Foo(5)
17+
}
18+
19+
def ok: Toto = {
20+
Foo(15)
21+
}
22+
23+
}

0 commit comments

Comments
 (0)