Skip to content

Commit 0c4c72c

Browse files
authored
Merge pull request #753 from viperproject/meilers_fix_751
Properly treating let-expressions when generating triggers
2 parents dfe7d76 + c7ed3b9 commit 0c4c72c

3 files changed

Lines changed: 55 additions & 4 deletions

File tree

src/main/scala/viper/silver/ast/utility/GenericTriggerGenerator.scala

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,15 @@ abstract class GenericTriggerGenerator[Node <: AnyRef,
165165
val nestedBoundVars: Seq[Var] =
166166
deepCollect(toSearch){ case qe: Quantification => Quantification_vars(qe)}.flatten
167167

168+
val additionalRelevantVars: Seq[Var] = {
169+
val additionalVarFinder = additionalRelevantVariables(vs, nestedBoundVars)
170+
deepCollect(toSearch){
171+
case n if additionalVarFinder.isDefinedAt(n) => additionalVarFinder(n)
172+
}.flatten
173+
}
174+
val allRelevantVars = (vs ++ additionalRelevantVars).distinct
175+
val modifyTriggers = modifyPossibleTriggers(allRelevantVars)
176+
168177
/* Get all function applications */
169178
reduceTree(toSearch)((node: Node, results: Seq[Seq[(PossibleTrigger, Seq[Var], Seq[Var])]]) => node match {
170179
case possibleTrigger: PossibleTrigger if isPossibleTrigger(possibleTrigger) =>
@@ -187,15 +196,15 @@ abstract class GenericTriggerGenerator[Node <: AnyRef,
187196
processedArgs foreach (arg => visit(arg) {
188197
case v: Var =>
189198
if (nestedBoundVars.contains(v)) containsNestedBoundVars = true
190-
if (vs.contains(v)) containedVars +:= v
199+
if (allRelevantVars.contains(v)) containedVars +:= v
191200
})
192201

193202
if (!containsNestedBoundVars && containedVars.nonEmpty)
194203
results.flatten ++ Seq((withArgs(possibleTrigger, processedArgs), containedVars, extraVars))
195204
else
196205
results.flatten
197206

198-
case e if modifyPossibleTriggers.isDefinedAt(e) => modifyPossibleTriggers.apply(e)(results)
207+
case e if modifyTriggers.isDefinedAt(e) => modifyTriggers.apply(e)(results)
199208

200209
case _ => results.flatten
201210
})
@@ -205,9 +214,14 @@ abstract class GenericTriggerGenerator[Node <: AnyRef,
205214
* Hook for clients to add more cases to getFunctionAppsContaining to modify the found possible triggers.
206215
* Used e.g. to wrap trigger expressions inferred from inside old-expression into old()
207216
*/
208-
def modifyPossibleTriggers: PartialFunction[Node, Seq[Seq[(PossibleTrigger, Seq[Var], Seq[Var])]] =>
217+
def modifyPossibleTriggers(relevantVars: Seq[Var]): PartialFunction[Node, Seq[Seq[(PossibleTrigger, Seq[Var], Seq[Var])]] =>
209218
Seq[(PossibleTrigger, Seq[Var], Seq[Var])]] = PartialFunction.empty
210219

220+
/*
221+
* Hook for clients to identify additional variables which can be covered by triggers.
222+
*/
223+
def additionalRelevantVariables(relevantVars: Seq[Var], varsToAvoid: Seq[Var]): PartialFunction[Node, Seq[Var]] = PartialFunction.empty
224+
211225
/* Precondition: if vars is non-empty then every (f,vs) pair in functs
212226
* satisfies the property that vars and vs are not disjoint.
213227
*

src/main/scala/viper/silver/ast/utility/Triggers.scala

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ object Triggers {
5858
case _ => sys.error(s"Unexpected expression $e")
5959
}
6060

61-
override def modifyPossibleTriggers = {
61+
override def modifyPossibleTriggers(relevantVars: Seq[LocalVar]) = {
6262
case ast.Old(_) => results =>
6363
results.flatten.map(t => {
6464
val exp = t._1
@@ -69,6 +69,17 @@ object Triggers {
6969
val exp = t._1
7070
(ast.LabelledOld(exp, l)(exp.pos, exp.info, exp.errT), t._2, t._3)
7171
})
72+
case ast.Let(v, e, _) => results =>
73+
results.flatten.map(t => {
74+
val exp = t._1
75+
val coveredVars = t._2.filter(cv => cv != v.localVar) ++ relevantVars.filter(rv => e.contains(rv))
76+
(exp.replace(v.localVar, e), coveredVars, t._3)
77+
})
78+
}
79+
80+
override def additionalRelevantVariables(relevantVars: Seq[LocalVar], varsToAvoid: Seq[LocalVar]): PartialFunction[Node, Seq[LocalVar]] = {
81+
case ast.Let(v, e, _) if relevantVars.exists(v => e.contains(v)) && varsToAvoid.forall(v => !e.contains(v)) =>
82+
Seq(v.localVar)
7283
}
7384
}
7485

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Any copyright is dedicated to the Public Domain.
2+
// http://creativecommons.org/publicdomain/zero/1.0/
3+
4+
method main() {
5+
assume forall qvar: Int ::
6+
let alias == (qvar) in
7+
f_get(alias) == f_clamp()
8+
assert f_get(10) == 0
9+
}
10+
11+
function f_get(idx: Int): Int
12+
13+
14+
method main2() {
15+
assume forall qvar: Int :: qvar > 0 ==>
16+
let alias == (qvar) in
17+
f_get2(alias) == f_clamp()
18+
assert f_get2(10) == 0
19+
}
20+
21+
function f_get2(idx: Int): Int
22+
requires idx > -5
23+
24+
25+
function f_clamp(): Int
26+
ensures result == 0

0 commit comments

Comments
 (0)