Skip to content

Commit 9d401d8

Browse files
authored
Merge pull request #757 from viperproject/meilers_fix_termination_pre_inhale
Fix earlier termination plugin fix
2 parents adcfce0 + 3f64dcf commit 9d401d8

3 files changed

Lines changed: 56 additions & 30 deletions

File tree

src/main/scala/viper/silver/plugin/standard/termination/transformation/ExpTransformer.scala

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,17 @@ import viper.silver.verifier.ConsistencyError
1919
trait ExpTransformer extends ProgramManager with ErrorReporter {
2020

2121
/**
22-
* Transforms an expression into a statement.
22+
* Transforms an expression into a statement that checks the termination conditions for that expression.
23+
* If inhaleExp is true, the expression is inhaled during the check.
2324
*
2425
* @return a statement representing the expression.
2526
*/
26-
def transformExp(e: Exp, c: ExpressionContext): Stmt = e match {
27-
case And(fst, snd) =>
28-
val fstStmt = transformExp(fst, c)
27+
def transformExp(e: Exp, c: ExpressionContext, inhaleExp: Boolean): Stmt = e match {
28+
case And(fst, snd) if inhaleExp =>
29+
// Special case conjunctions s.t. we properly inhale the first expression before checking the second.
30+
// This is necessary if fst contains e.g. a quantified permission, and snd is well-defined only if that quantified
31+
// permission is available.
32+
val fstStmt = transformExp(fst, c, inhaleExp)
2933
val nonDetVarDecl = LocalVarDecl(uniqueName("b"), Bool)(e.pos, e.info, e.errT)
3034
val assumeFalse = Inhale(FalseLit()())()
3135
val inhaleFst = Inhale(fst)()
@@ -39,20 +43,20 @@ trait ExpTransformer extends ProgramManager with ErrorReporter {
3943
}
4044
val fstIf = If(nonDetVarDecl.localVar, Seqn(Seq(fstStmt, assumeFalse), Nil)(), Seqn(Seq(inhaleFstStmt), Nil)())(e.pos, e.info, e.errT)
4145
val fstBlock = Seqn(Seq(fstIf), Seq(nonDetVarDecl))()
42-
val sndStmt = transformExp(snd, c)
46+
val sndStmt = transformExp(snd, c, inhaleExp)
4347
Seqn(Seq(fstBlock, sndStmt), Nil)()
4448
case CondExp(cond, thn, els) =>
45-
val condStmt = transformExp(cond, c)
46-
val thnStmt = transformExp(thn, c)
47-
val elsStmt = transformExp(els, c)
49+
val condStmt = transformExp(cond, c, false)
50+
val thnStmt = transformExp(thn, c, inhaleExp)
51+
val elsStmt = transformExp(els, c, inhaleExp)
4852

4953
val ifStmt = If(cond, Seqn(Seq(thnStmt), Nil)(), Seqn(Seq(elsStmt), Nil)())()
5054

5155
val stmts = Seq(condStmt, ifStmt)
5256
Seqn(stmts, Nil)()
5357
case Unfolding(acc, unfBody) =>
54-
val permCheck = transformExp(acc.perm, c)
55-
val unfoldBody = transformExp(unfBody, c)
58+
val permCheck = transformExp(acc.perm, c, false)
59+
val unfoldBody = transformExp(unfBody, c, inhaleExp)
5660
// only unfold and fold if body contains something
5761
val (unfold, fold) = (Unfold(acc)(), Fold(acc)())
5862

@@ -61,32 +65,32 @@ trait ExpTransformer extends ProgramManager with ErrorReporter {
6165
case Applying(wand, body) =>
6266
// note that this case is untested -- it's not possible to write a function with an `applying` expression
6367
val nonDetVarDecl = LocalVarDecl(uniqueName("b"), Bool)(e.pos, e.info, e.errT)
64-
val bodyStmt = transformExp(body, c)
68+
val bodyStmt = transformExp(body, c, inhaleExp)
6569
val killBranchStmt = Inhale(FalseLit()(e.pos, e.info, e.errT))(e.pos, e.info, e.errT)
6670
val thnStmt = Seqn(Seq(Apply(wand)(e.pos, e.info, e.errT), bodyStmt, killBranchStmt), Nil)()
6771
val ifStmt = If(nonDetVarDecl.localVar, thnStmt, EmptyStmt)(e.pos, e.info, e.errT)
6872
Seqn(Seq(ifStmt), Seq(nonDetVarDecl))(e.pos, e.info, e.errT)
6973
case inex: InhaleExhaleExp =>
70-
val inhaleStmt = transformExp(inex.in, c)
71-
val exhaleStmt = transformExp(inex.ex, c)
74+
val inhaleStmt = transformExp(inex.in, c, inhaleExp)
75+
val exhaleStmt = transformExp(inex.ex, c, inhaleExp)
7276

7377
c.conditionInEx match {
7478
case Some(conditionVar) => If(conditionVar.localVar, Seqn(Seq(inhaleStmt), Nil)(), Seqn(Seq(exhaleStmt), Nil)())()
7579
case None => Seqn(Seq(inhaleStmt, exhaleStmt), Nil)()
7680
}
7781
case letExp: Let =>
78-
val expressionStmt = transformExp(letExp.exp, c)
82+
val expressionStmt = transformExp(letExp.exp, c, false)
7983
val localVarDecl = letExp.variable
8084

8185
val inhaleEq = Inhale(EqCmp(localVarDecl.localVar, letExp.exp)())()
8286

83-
val bodyStmt = transformExp(letExp.body, c)
87+
val bodyStmt = transformExp(letExp.body, c, inhaleExp)
8488

8589
Seqn(Seq(expressionStmt, inhaleEq, bodyStmt), Seq(localVarDecl))()
8690

8791
case b: BinExp =>
88-
val left = transformExp(b.left, c)
89-
val right = transformExp(b.right, c)
92+
val left = transformExp(b.left, c, inhaleExp)
93+
val right = transformExp(b.right, c, inhaleExp)
9094

9195
// Short circuit evaluation
9296
val pureLeft: Exp = toPureBooleanExp(c).execute(b.left)
@@ -111,21 +115,21 @@ trait ExpTransformer extends ProgramManager with ErrorReporter {
111115
case _: LocationAccess => EmptyStmt
112116

113117
case ap: AccessPredicate =>
114-
val check = transformExp(ap.perm, c)
118+
val check = transformExp(ap.perm, c, false)
115119
val inhale = Inhale(ap)(ap.pos)
116120
Seqn(Seq(check, inhale), Nil)()
117121
case fa: Forall =>
118122
// we turn the quantified variables into local variables with arbitrary value and show that the expression holds
119123
// for arbitrary values, which is similar to a forall introduction
120124
val (localDeclMapping, transformedExp) = substituteWithFreshVars(fa.variables, fa.exp)
121-
val expressionStmt = transformExp(transformedExp, c)
125+
val expressionStmt = transformExp(transformedExp, c, inhaleExp)
122126
Seqn(Seq(expressionStmt), localDeclMapping.map(_._2))(fa.pos, fa.info, fa.errT)
123127
case fp: ForPerm =>
124128
// let's pick arbitrary values for the quantified variables and check the body given that the current heap has
125129
// sufficient permissions
126130
val (localDeclMapping, transformedExp) = substituteWithFreshVars(fp.variables, fp.exp)
127131
val transformedRes = applySubstitution(localDeclMapping, fp.resource)
128-
val expressionStmt = transformExp(transformedExp, c)
132+
val expressionStmt = transformExp(transformedExp, c, inhaleExp)
129133
val killBranchStmt = Inhale(FalseLit()(e.pos, e.info, e.errT))(e.pos, e.info, e.errT)
130134
val thnStmt = Seqn(Seq(expressionStmt, killBranchStmt), Nil)(e.pos, e.info, e.errT)
131135
val ifCond = GtCmp(CurrentPerm(transformedRes)(e.pos, e.info, e.errT), NoPerm()(e.pos, e.info, e.errT))(e.pos, e.info, e.errT)
@@ -137,15 +141,15 @@ trait ExpTransformer extends ProgramManager with ErrorReporter {
137141
// we can't use an assume statement at this point because the `assume`s have already been rewritten
138142
// furthermore, Viper only allows pure existentially quantified expressions
139143
val inhaleWitnesses = Inhale(transformedExp)(ex.pos, ex.info, ex.errT)
140-
val expressionStmt = transformExp(transformedExp, c)
144+
val expressionStmt = transformExp(transformedExp, c, inhaleExp)
141145
Seqn(Seq(inhaleWitnesses, expressionStmt), localDeclMapping.map(_._2))(ex.pos, ex.info, ex.errT)
142146
case fa: FuncLikeApp =>
143-
val argStmts = fa.args.map(transformExp(_, c))
147+
val argStmts = fa.args.map(transformExp(_, c, false))
144148
Seqn(argStmts, Nil)()
145149
case e: ExtensionExp => reportUnsupportedExp(e)
146150

147151
case _ =>
148-
val sub = e.subExps.map(transformExp(_, c))
152+
val sub = e.subExps.map(transformExp(_, c, false))
149153
Seqn(sub, Nil)()
150154
}
151155

src/main/scala/viper/silver/plugin/standard/termination/transformation/FunctionCheck.scala

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ trait FunctionCheck extends ProgramManager with DecreasesCheck with ExpTransform
5353
val context = FContext(f)
5454

5555
val proofMethodBody: Stmt = {
56-
val stmt: Stmt = Simplifier.simplify(transformExp(f.body.get, context))
56+
val stmt: Stmt = Simplifier.simplify(transformExp(f.body.get, context, false))
5757
if (requireNestedInfo) {
5858
addNestedPredicateInformation.execute(stmt)
5959
} else {
@@ -88,7 +88,7 @@ trait FunctionCheck extends ProgramManager with DecreasesCheck with ExpTransform
8888
.reduce((e, p) => And(e, p)(e.pos))
8989

9090
val proofMethodBody: Stmt = {
91-
val stmt: Stmt = Simplifier.simplify(transformExp(posts, context))
91+
val stmt: Stmt = Simplifier.simplify(transformExp(posts, context, false))
9292
if (requireNestedInfo) {
9393
addNestedPredicateInformation.execute(stmt)
9494
} else {
@@ -113,7 +113,7 @@ trait FunctionCheck extends ProgramManager with DecreasesCheck with ExpTransform
113113
// concatenate all pres
114114
val pres = f.pres.reduce((e, p) => And(e, p)(e.pos))
115115

116-
val proofMethodBody: Stmt = Simplifier.simplify(transformExp(pres, context))
116+
val proofMethodBody: Stmt = Simplifier.simplify(transformExp(pres, context, true))
117117

118118
if (proofMethodBody != EmptyStmt) {
119119
val proofMethod = Method(proofMethodName, f.formalArgs, Nil, Nil, Nil,
@@ -136,12 +136,12 @@ trait FunctionCheck extends ProgramManager with DecreasesCheck with ExpTransform
136136
*
137137
* @return a statement representing the expression
138138
*/
139-
override def transformExp(e: Exp, c: ExpressionContext): Stmt = (e, c) match {
139+
override def transformExp(e: Exp, c: ExpressionContext, inhaleExp: Boolean): Stmt = (e, c) match {
140140
case (functionCall: FuncApp, context: FunctionContext @unchecked) =>
141141
val stmts = collection.mutable.ArrayBuffer[Stmt]()
142142

143143
// check the arguments
144-
val termChecksOfArgs: Seq[Stmt] = functionCall.getArgs map (a => transformExp(a, context))
144+
val termChecksOfArgs: Seq[Stmt] = functionCall.getArgs map (a => transformExp(a, context, false))
145145
stmts.appendAll(termChecksOfArgs)
146146

147147
getFunctionDecreasesSpecification(context.functionName).tuple match {
@@ -218,7 +218,7 @@ trait FunctionCheck extends ProgramManager with DecreasesCheck with ExpTransform
218218
// should not happen
219219
}
220220
Seqn(stmts.toSeq, Nil)()
221-
case _ => super.transformExp(e, c)
221+
case _ => super.transformExp(e, c, inhaleExp)
222222
}
223223

224224
// context creator

src/test/resources/all/issues/silicon/0768.vpr

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,26 @@ function inp_to_seq_2(inp: pointer, n: Int): Seq[Int]
8686
decreases
8787
{
8888
Seq[Int]()
89-
}
89+
}
90+
91+
// Regression test for a bug in the original fix:
92+
93+
adt tree {
94+
leaf(value: Int)
95+
node(left: tree, right: tree)
96+
}
97+
98+
function leafCount(t: tree): Int
99+
decreases 1
100+
{
101+
id(t.isleaf && t.isleaf ?
102+
(1) :
103+
//:: ExpectedOutput(termination.failed:tuple.false)
104+
(leafCount(t.left) + leafCount(t.right)))
105+
}
106+
107+
function id(i: Int): Int
108+
decreases 0
109+
{
110+
i
111+
}

0 commit comments

Comments
 (0)