Skip to content

Commit 54070da

Browse files
authored
Issue 903 (#904)
* adds testcase demonstrating the issue * fixes the encoding * adds more testcases to showcase the behavior of branching or looping on a SIF expression * fixes the encoding of loops to support SIF expressions in loop condition * fixes an issue where appearing as a subexpression would not get encoded by the SIF plugin
1 parent ac67f4f commit 54070da

4 files changed

Lines changed: 137 additions & 33 deletions

File tree

src/main/scala/viper/silver/plugin/sif/SIFExtendedTransformer.scala

Lines changed: 42 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -761,44 +761,44 @@ trait SIFExtendedTransformer {
761761
*
762762
* <br> var bypass1 := !(p1 && !ret1 && !break1 && !cont1 && !except1 && !L1...)
763763
* <br> var bypass2 := !(p2 && !ret2 && !break2 && !cont2 && !except2 && !L2...)
764-
* <br> if bypass1 { tmp1_1 := Target_1; ... tmp1_N := Target_N } // to derive that modified vars of non-active execs are unchanged
765-
* <br> if bypass2 { tmp2_1 := P[Target_1]; ... tmp2_N := P[Target_N] }
764+
* <br> if bypass1 { tmp1_1 := Normal[Target_1]; ... tmp1_N := Normal[Target_N] } // to derive that modified vars of non-active execs are unchanged
765+
* <br> if bypass2 { tmp2_1 := Prime[Target_1]; ... tmp2_N := Prime[Target_N] }
766766
* <br> var old_ret1 := ret1; var old_ret2 := ret2; ... // forall control variables (includes lables)
767767
* <br> var idle1 := false; var idl2 := false // if invariant has InhaleExhaleExp
768768
* <br> // if there is a termination measure
769769
* <br> assert Assertion[!Termination ==> low_event]
770770
* <br> assert Assertion[low(Termination)]
771771
* <br> var term_cond1, term_cond2
772-
* <br> if p1 { term_cond1 := Termination }
773-
* <br> if p2 { term_cond2 := P[Termination] }
772+
* <br> if p1 { term_cond1 := Normal[Termination] }
773+
* <br> if p2 { term_cond2 := Prime[Termination] }
774774
* <br> var p1_, p2_
775775
* <br>
776776
* <br> while (
777-
* <br> (p1 && !ret1 && !break1 && !except1 && !L1... && !bypass1 && cond) ||
778-
* <br> (p2 && !ret2 && !break2 && !except2 && !L2... && !bypass2 && P[cond])
777+
* <br> (p1 && !ret1 && !break1 && !except1 && !L1... && !bypass1 && Normal[cond]) ||
778+
* <br> (p2 && !ret2 && !break2 && !except2 && !L2... && !bypass2 && Prime[cond])
779779
* <br> )
780780
* <br> invariant Assertion[ I[ InEx(A,B) -> InEx(A, !idle1 ==> B ) ] ]< p1 && !bypass1, p2 && !bypass2 >
781781
* <br> invariant Assertion[ !term_cond1 ==> cond ]< p1 && !bypass1, p2 && !bypass2 >
782-
* <br> invariant (bypass1 ==> tmp1_1 == Target_1) && ... (bypass1 ==> tmp1_N == Target_N)
783-
* <br> invariant (bypass2 ==> tmp2_1 == P[Target_1]) && ... (bypass2 ==> tmp2_N == P[Target_N]) // TODO REM: maybe group them
782+
* <br> invariant (bypass1 ==> tmp1_1 == Normal[Target_1]) && ... (bypass1 ==> tmp1_N == Normal[Target_N])
783+
* <br> invariant (bypass2 ==> tmp2_1 == Prime[Target_1]) && ... (bypass2 ==> tmp2_N == Prime[Target_N]) // TODO REM: maybe group them
784784
* <br> {
785785
* <br> // preamble
786786
* <br> cont1 := false; cont2 := false
787-
* <br> p1_ := a1 && cond; p2_ := a2 && P[cond]
788-
* <br> idle1 := a1 && !cond; idle2 := a2 && !P[cond] // if invariant has InhaleExhaleExp
787+
* <br> p1_ := a1 && Normal[cond]; p2_ := a2 && Prime[cond]
788+
* <br> idle1 := a1 && !Normal[cond]; idle2 := a2 && !Prime[cond] // if invariant has InhaleExhaleExp
789789
* <br> // body
790790
* <br> Statement[body]< p1_, p2_ >
791791
* <br> }
792792
* <br>
793793
* <br> if (!bypass1 && (ret1 || break1 || except1)) || (!bypass2 && (ret2 || break2 || except2)) {
794794
* <br> ret1 := old_ret1; var ret2 := old_ret2;... // forall control variables (includes lables)
795-
* <br> inhale p1 && !ret1 && !break1 && !except1 && !L1... ==> cond
795+
* <br> inhale p1 && !ret1 && !break1 && !except1 && !L1... ==> Normal[cond]
796796
* <br> inhale p2 && !ret2 && !break2 && !except2 && !L2... ==> Prime[cond]
797797
* <br>
798798
* <br> // preamble
799799
* <br> cont1 := false; cont2 := false
800-
* <br> p1_ := a1 && cond; p2_ := a2 && P[cond]
801-
* <br> idle1 := a1 && !cond; idle2 := a2 && !P[cond] // if invariant has InhaleExhaleExp
800+
* <br> p1_ := a1 && Normal[cond]; p2_ := a2 && Prime[cond]
801+
* <br> idle1 := a1 && !Normal[cond]; idle2 := a2 && !Prime[cond] // if invariant has InhaleExhaleExp
802802
* <br> // body
803803
* <br> Statement[body]< p1_, p2_ >
804804
* <br>
@@ -860,36 +860,36 @@ trait SIFExtendedTransformer {
860860

861861
var tmpAssigns1 = Seq[LocalVarAssign]()
862862
var tmpAssigns2 = Seq[LocalVarAssign]()
863-
// tmpAssigns1 += tmp1_1 := t1...; tmpAssigns2 += tmp1_2 := P[t1]...,
864-
// targetValEqualities1 += tmp1_1 == t1...; targetValEqualities2 += tmp1_2 == P[t2])
863+
// tmpAssigns1 += tmp1_1 := Normal[t1]...; tmpAssigns2 += tmp1_2 := Prime[t1]...,
864+
// targetValEqualities1 += tmp1_1 == Normal[t1]...; targetValEqualities2 += tmp1_2 == Prime[t2])
865865
for (t <- targets) {
866866
// make sure the variable is defined outside the loop
867867
if (primedNames.contains(t.name)){ // TODO REM: should be an assert, but currently fails due to cont2 break2
868868
val (tmp1d, tmp1r) = getNewVar("tmp1", t.typ)
869869
val (tmp2d, tmp2r) = getNewVar("tmp2", t.typ)
870870
newVarDecls ++= Seq(tmp1d, tmp2d)
871871
//targetValRefs ++= Seq(tmp1r, tmp2r)
872-
tmpAssigns1 :+= LocalVarAssign(tmp1r, t)()
872+
tmpAssigns1 :+= LocalVarAssign(tmp1r, translateNormal(t, p1, p2))()
873873
tmpAssigns2 :+= LocalVarAssign(tmp2r, translatePrime(t, p1, p2))()
874-
val eq1 = EqCmp(tmp1r, t)()
874+
val eq1 = EqCmp(tmp1r, translateNormal(t, p1, p2))()
875875
val eq2 = EqCmp(tmp2r, translatePrime(t, p1, p2))()
876876
targetValEqualities1 ++= Seq(eq1)
877877
targetValEqualities2 ++= Seq(eq2)
878878
}
879879
}
880-
// if bypass1 { tmp1_1 := Target_1; ... tmp1_N := Target_N }
881-
// if bypass2 { tmp2_1 := P[Target_1]; ... tmp2_N := P[Target_N] }
880+
// if bypass1 { tmp1_1 := Normal[Target_1]; ... tmp1_N := Normal[Target_N] }
881+
// if bypass2 { tmp2_1 := Prime[Target_1]; ... tmp2_N := Prime[Target_N] }
882882
if (targets.nonEmpty) targetValAssigns :+= If(bypass1r, Seqn(tmpAssigns1, Seq())(), skip)()
883883
if (targets.nonEmpty) targetValAssigns :+= If(bypass2r, Seqn(tmpAssigns2, Seq())(), skip)()
884884

885885
/*if (timing){
886886
val (tmp1d, tmp1r) = getNewVar("tmp1", Int)
887887
val (tmp2d, tmp2r) = getNewVar("tmp2", Int)
888888
newVarDecls ++= Seq(tmp1d, tmp2d)
889-
val assign1 = LocalVarAssign(tmp1r, time.get)()
889+
val assign1 = LocalVarAssign(tmp1r, translateNormal(time.get, p1, p2))()
890890
val assign2 = LocalVarAssign(tmp2r, translatePrime(time.get, p1, p2))()
891891
targetValAssigns ++= Seq(assign1, assign2)
892-
val eq1 = EqCmp(tmp1r, time.get)()
892+
val eq1 = EqCmp(tmp1r, translateNormal(time.get, p1, p2))()
893893
val eq2 = EqCmp(tmp2r, translatePrime(time.get, p1, p2))()
894894
targetValEqualities1 ++= Seq(eq1)
895895
targetValEqualities2 ++= Seq(eq2)
@@ -908,22 +908,22 @@ trait SIFExtendedTransformer {
908908
}
909909
}
910910

911-
// (p1 && !ret1 && !break1 && !except1 && !L1... && !bypass1 && cond) ||
912-
// (p2 && !ret2 && !break2 && !except2 && !L2... && !bypass2 && P[cond])
911+
// (p1 && !ret1 && !break1 && !except1 && !L1... && !bypass1 && Normal[cond]) ||
912+
// (p2 && !ret2 && !break2 && !except2 && !L2... && !bypass2 && Prime[cond])
913913
val newCond = Or(
914-
And(And(ctrlVars.activeExecNoContNormal(Some(p1)), Not(bypass1r)())(), w.cond)(),
914+
And(And(ctrlVars.activeExecNoContNormal(Some(p1)), Not(bypass1r)())(), translateNormal(w.cond, p1, p2))(),
915915
And(And(ctrlVars.activeExecNoContPrime(Some(p2)), Not(bypass2r)())(), translatePrime(w.cond, p1, p2))()
916916
)()
917917
var bodyPreamble: Seq[Stmt] = Seq()
918918
// cont1 := false; cont2 := false
919919
if (ctrlVars.cont1r.isDefined) bodyPreamble = bodyPreamble :+
920920
LocalVarAssign(ctrlVars.cont1r.get, FalseLit()())() :+
921921
LocalVarAssign(ctrlVars.cont2r.get, FalseLit()())()
922-
// var p1 := a1 && cond; var p2 := a2 && P[cond]
922+
// var p1 := a1 && Normal[cond]; var p2 := a2 && Prime[cond]
923923
val (p1d, p1r) = getNewBool("p1")
924924
val (p2d, p2r) = getNewBool("p2")
925925
newVarDecls ++= Seq(p1d, p2d)
926-
val p1Assign = LocalVarAssign(p1r, And(ctrlVars.activeExecNormal(Some(p1)), w.cond)())()
926+
val p1Assign = LocalVarAssign(p1r, And(ctrlVars.activeExecNormal(Some(p1)), translateNormal(w.cond, p1, p2))())()
927927
val p2Assign = LocalVarAssign(p2r, And(ctrlVars.activeExecPrime(Some(p2)), translatePrime(w.cond, p1, p2))())()
928928
bodyPreamble ++= Seq(p1Assign, p2Assign)
929929

@@ -939,7 +939,7 @@ trait SIFExtendedTransformer {
939939
LocalVarAssign(idle2r, FalseLit()())())
940940
// assign inside loop body that the execution is idling
941941
val idle1Assign = LocalVarAssign(idle1r,
942-
And(ctrlVars.activeExecNormal(Some(p1)), Not(w.cond)())())()
942+
And(ctrlVars.activeExecNormal(Some(p1)), Not(translateNormal(w.cond, p1, p2))())())()
943943
val idle2Assign = LocalVarAssign(idle2r,
944944
And(ctrlVars.activeExecPrime(Some(p2)), Not(translatePrime(w.cond, p1, p2))())())()
945945
bodyPreamble ++= Seq(idle1Assign, idle2Assign)
@@ -963,7 +963,7 @@ trait SIFExtendedTransformer {
963963
primedNames.update(cond1r.name, cond2r.name)
964964
newVarDecls ++= Seq(cond1d, cond2d)
965965
stmts ++= Seq(
966-
If(p1, Seqn(Seq(LocalVarAssign(cond1r, terminates.get.cond)()), Seq())(), skip)(),
966+
If(p1, Seqn(Seq(LocalVarAssign(cond1r, translateNormal(terminates.get.cond, p1, p2))()), Seq())(), skip)(),
967967
If(p2, Seqn(Seq(LocalVarAssign(cond2r, translatePrime(terminates.get.cond, p1, p2))()), Seq())(), skip)()
968968
)
969969
newStdInvs :+= Implies(Not(cond1r)(), w.cond)(
@@ -1010,10 +1010,10 @@ trait SIFExtendedTransformer {
10101010
val ctrlVarAssigns: Seq[Stmt] = ctrlVars.declarations()
10111011
.map(d => d.localVar)
10121012
.map(v => LocalVarAssign(v, ctrlVarToOldMap(v))())
1013-
// inhale p1 && !ret1 && !break1 && !except1 && !L1... ==> cond
1013+
// inhale p1 && !ret1 && !break1 && !except1 && !L1... ==> Normal[cond]
10141014
// inhale p2 && !ret2 && !break2 && !except2 && !L2... ==> Prime[cond]
10151015
val recInhales: Seq[Stmt] = Seq() :+ //newStdInvs.map(i => Inhale(i)()) :+
1016-
Inhale(Implies(ctrlVars.activeExecNoContNormal(Some(p1)), w.cond)())() :+
1016+
Inhale(Implies(ctrlVars.activeExecNoContNormal(Some(p1)), translateNormal(w.cond, p1, p2))())() :+
10171017
Inhale(Implies(ctrlVars.activeExecNoContNormal(Some(p2)), translatePrime(w.cond, p1, p2))())()
10181018
// inhale !p1_ || !(p1 && !ret1 && !break1 && !except1 && !L1..)
10191019
// inhale !p2_ || !(p2 && !ret2 && !break2 && !except2 && !L2..)
@@ -1370,7 +1370,7 @@ trait SIFExtendedTransformer {
13701370
Seqn(Seq(newNew) ++ /*allFieldAssigns ++*/ Seq(assign1, assign2, incrementTime(p1, p2)), Seq(tmpd))()
13711371

13721372
// var p1_, p2_, p3_, p4_;
1373-
// p1_ := a1 && cond; p2_ := a2 && P[cond]; p3_ := a1 && !cond; p4 := a2 && !P[cond]
1373+
// p1_ := a1 && Normal[cond]; p2_ := a2 && Prime[cond]; p3_ := a1 && !Normal[cond]; p4 := a2 && !Prime[cond]
13741374
// if p1 { time1 += 1 }; if p2 { time2 += 1 }
13751375
// Statement[thn]<p1_, p2_>
13761376
// Statement[els]<p3_, p4_>
@@ -1380,9 +1380,9 @@ trait SIFExtendedTransformer {
13801380
val (p3d, p3r) = getNewBool("p3")
13811381
val (p4d, p4r) = getNewBool("p4")
13821382

1383-
val p1Assign = LocalVarAssign(p1r, And(act1, cond)())(i.pos, i.info, i.errT)
1383+
val p1Assign = LocalVarAssign(p1r, And(act1, translateNormal(cond, p1, p2))())(i.pos, i.info, i.errT)
13841384
val p2Assign = LocalVarAssign(p2r, And(act2, translatePrime(cond, p1, p2))())(i.pos, i.info, i.errT)
1385-
val p3Assign = LocalVarAssign(p3r, And(act1, Not(cond)())())(i.pos, i.info, i.errT)
1385+
val p3Assign = LocalVarAssign(p3r, And(act1, Not(translateNormal(cond, p1, p2))())())(i.pos, i.info, i.errT)
13861386
val p4Assign = LocalVarAssign(p4r, And(act2, Not(translatePrime(cond, p1, p2))())())(i.pos, i.info, i.errT)
13871387

13881388
val thnCtx = ctx.copy(p1 = p1r, p2 = p2r, ctrlVars = ctrlVars)
@@ -1965,15 +1965,21 @@ trait SIFExtendedTransformer {
19651965
*
19661966
* N[Low(e)] -> p1 && p2 ==> Eq[e]
19671967
* N[low(e)] -> p1 && p2 ==> Eq[e]
1968+
* N[LowEvent] -> p1 && p2
1969+
* N[lowEvent] -> p1 && p2
1970+
* N[Rel(e, 1)] -> P[e]
1971+
* N[Rel(e, i)] && i != 1 -> N[e]
19681972
*/
19691973
def translateNormal[T <: Exp](e: T, p1: Exp, p2: Exp): T = {
19701974
e.transform{
19711975
case l: SIFLowExp => Implies(And(p1, p2)(), translateSIFLowExpComparison(l, p1, p2))(errT = fwTs(l, l))
1976+
case _: SIFLowEventExp => And(p1, p2)()
19721977
case r@SIFRelExp(e, i) =>
19731978
if (!Config.enableExperimentalFeatures)
19741979
reportError(Internal(r, FeatureUnsupported(r, "rel-expressions are an experimental feature and must be explicitly enabled.")))
19751980
if (i.i == BigInt.int2bigInt(1)) translatePrime(e, p1, p2) else translateNormal(e, p1, p2)
19761981
case DomainFuncApp("Low", args, _) if Config.enableNaginiSpecificFeatures => Implies(And(p1, p2)(), translateSIFLowExpComparison(SIFLowExp(args.head)(), p1, p2))()
1982+
case DomainFuncApp("LowEvent", Seq(), _) if Config.enableNaginiSpecificFeatures => And(p1, p2)()
19771983
}
19781984
}
19791985

@@ -1984,11 +1990,14 @@ trait SIFExtendedTransformer {
19841990
*
19851991
* Unary[low(e)] -> true
19861992
* Unary[Low(e)] -> true
1993+
* Unary[lowEvent] -> true
1994+
* Unary[LowEvent] -> true
19871995
*
19881996
* */
19891997
def translateToUnary(e: Exp): Exp = {
19901998
val transformed = e.transform{
19911999
case _: SIFLowExp => TrueLit()()
2000+
case _: SIFLowEventExp => TrueLit()()
19922001
case DomainFuncApp("Low", _, _) if Config.enableNaginiSpecificFeatures => TrueLit()()
19932002
case DomainFuncApp("LowEvent", Seq(), _) if Config.enableNaginiSpecificFeatures => TrueLit()()
19942003
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Any copyright is dedicated to the Public Domain.
2+
// http://creativecommons.org/publicdomain/zero/1.0/
3+
4+
// this testcase demonstrates how branching on a SIF expression behaves semantically
5+
6+
method InLowEventContexts(t: Int)
7+
requires lowEvent
8+
{
9+
if (low(t)) {
10+
assert low(t)
11+
} else {
12+
// this case is reachable as `t` might be the same in both executions or it might not
13+
//:: ExpectedOutput(assert.failed:assertion.false)
14+
assert false
15+
}
16+
}
17+
18+
method InLowEventContexts2(t: Int)
19+
requires lowEvent
20+
{
21+
// since `low(t)` evaluates to the same value in both executions, both executions are guaranteed
22+
// to enter the same branch
23+
if (low(t)) {
24+
assert lowEvent
25+
} else {
26+
assert lowEvent
27+
}
28+
}
29+
30+
method InNonLowEventContexts(t: Int)
31+
requires !lowEvent
32+
{
33+
if (low(t)) {
34+
assert low(t)
35+
} else {
36+
// this is a dead branch as `low(t)` trivially evaluates to `true` as there is no second execution
37+
assert false
38+
}
39+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Any copyright is dedicated to the Public Domain.
2+
// http://creativecommons.org/publicdomain/zero/1.0/
3+
4+
// this testcase demonstrates how looping on a SIF expression behaves semantically
5+
6+
method InLowEventContexts(t: Int)
7+
requires lowEvent
8+
{
9+
while (low(t))
10+
{
11+
assert low(t)
12+
}
13+
14+
// this case is reachable as `t` might be the same in both executions or it might not
15+
//:: ExpectedOutput(assert.failed:assertion.false)
16+
assert false
17+
}
18+
19+
method InLowEventContexts2(t: Int)
20+
requires lowEvent
21+
{
22+
// since `low(t)` evaluates to the same value in both executions, both executions are guaranteed
23+
// to execute the loop bodies in sync
24+
25+
while (low(t))
26+
invariant lowEvent
27+
{
28+
assert lowEvent
29+
}
30+
31+
assert lowEvent
32+
}
33+
34+
method InNonLowEventContexts(t: Int)
35+
requires !lowEvent
36+
{
37+
while (low(t))
38+
{
39+
assert low(t)
40+
}
41+
42+
// the loop will never terminate as `low(t)` trivially evaluates to `true` as there is no second execution
43+
assert false
44+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Any copyright is dedicated to the Public Domain.
2+
// http://creativecommons.org/publicdomain/zero/1.0/
3+
4+
// this testcase checks that the branch condition is encoded to a non-SIF expression
5+
6+
method condWithLow(t: Int)
7+
{
8+
if (low(t)) {
9+
//:: ExpectedOutput(assert.failed:assertion.false)
10+
assert false
11+
}
12+
}

0 commit comments

Comments
 (0)