Skip to content

Commit 530d850

Browse files
committed
WIP: Fix model checking of inlined adt invariant
1 parent b210d6b commit 530d850

File tree

5 files changed

+47
-13
lines changed

5 files changed

+47
-13
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
@@ -795,7 +795,7 @@ trait TypeChecker {
795795
val trInv =
796796
if (sort.hasInvariant) {
797797
val inv = sort.typed(tps).invariant.get
798-
val invKind = VCKind.AdtInvariant(id)
798+
val invKind = VCKind.AdtInvariant(id, inv.applied(Seq(e)))
799799
val tc2 = tc.withVCKind(invKind).setPos(e)
800800
if (inv.flags.contains(InlineInvariant)) {
801801
val (tc3, freshener) = tc.freshBindWithValues(inv.params, Seq(e))
@@ -834,10 +834,10 @@ trait TypeChecker {
834834
val trInv =
835835
if (sort.hasInvariant) {
836836
val inv = sort.typed(tps).invariant.get
837-
val invKind = VCKind.AdtInvariant(inv.id)
837+
val invKind = VCKind.AdtInvariant(inv.id, inv.applied(Seq(e)))
838838
val tc2 = tc.withVCKind(invKind).setPos(e)
839839
if (inv.flags.contains(InlineInvariant)) {
840-
val (tc3, freshener) = tc.freshBindWithValues(inv.params, Seq(e))
840+
val (tc3, freshener) = tc2.freshBindWithValues(inv.params, Seq(e))
841841
buildVC(tc3, freshener.transform(inv.fullBody))
842842
} else {
843843
buildVC(tc2, inv.applied(Seq(e)))

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

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -144,13 +144,9 @@ 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, expr: 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-
}
153-
154150
def success: VCStatus = {
155151
reporter.debug("- Model validated.")
156152
VCStatus.Invalid(VCStatus.CounterExample(model))
@@ -161,6 +157,21 @@ trait VerificationChecker { self =>
161157
VCStatus.Unknown
162158
}
163159

160+
val pcCond = collectWithPC(expr) {
161+
case (inv @ FunctionInvocation(`invId`, _, Seq(adt: ADT)), path) => (inv, adt, path)
162+
}
163+
164+
if (pcCond.isEmpty) {
165+
return evaluator.eval(expr, model) match {
166+
case Successful(BooleanLiteral(false)) => success
167+
case Successful(_) => failure("- Invalid model.")
168+
case RuntimeError(msg) => failure(s"- Model leads to runtime error: $msg")
169+
case EvaluatorError(msg) => failure(s"- Model leads to evaluation error: $msg")
170+
}
171+
}
172+
173+
val Seq((inv, adt, path)) = pcCond
174+
164175
evaluator.eval(path.toClause, model) match {
165176
case Successful(BooleanLiteral(true)) => // path condition was true, we must evaluate invariant
166177
case Successful(BooleanLiteral(false)) => return success
@@ -184,7 +195,7 @@ trait VerificationChecker { self =>
184195
val adtVar = Variable(FreshIdentifier("adt"), adt.getType(symbols), Seq())
185196
val newInv = FunctionInvocation(invId, inv.tps, Seq(adtVar))
186197
val newModel = inox.Model(program)(model.vars + (adtVar.toVal -> newAdt), model.chooses)
187-
val newCondition = exprOps.replace(Map(inv -> newInv), vc.condition)
198+
val newCondition = exprOps.replace(Map(inv -> newInv), expr)
188199

189200
evaluator.eval(newCondition, newModel) match {
190201
case Successful(BooleanLiteral(false)) => success
@@ -252,8 +263,8 @@ trait VerificationChecker { self =>
252263
VCResult(VCStatus.Valid, s.getResultSolver, Some(time))
253264

254265
case SatWithModel(model) if checkModels && vc.kind.isInstanceOf[VCKind.AdtInvariant] =>
255-
val VCKind.AdtInvariant(invId) = vc.kind
256-
val status = checkAdtInvariantModel(vc, invId, model)
266+
val VCKind.AdtInvariant(invId, expr) = vc.kind
267+
val status = checkAdtInvariantModel(vc, invId, expr.asInstanceOf[Expr], model)
257268
VCResult(status, s.getResultSolver, Some(time))
258269

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

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ object VCKind {
4040
case object InvariantSat extends VCKind("invariant satisfiability", "inv. sat")
4141
case class AssertErr(err: String) extends VCKind("body assertion: " + err, "assert.")
4242
case class Error(err: String) extends VCKind(err, "error")
43-
case class AdtInvariant(inv: Identifier) extends VCKind("adt invariant", "adt inv.")
43+
case class AdtInvariant(inv: Identifier, expr: Any) extends VCKind("adt invariant", "adt inv.")
4444

4545
def fromErr(optErr: Option[String]) = {
4646
optErr.map { err =>

inline-invariant.scala

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)