Skip to content

Commit 94bd536

Browse files
authored
Fixing issue #849 by letting extension expressions declare whether they are valid triggers
1 parent 7807892 commit 94bd536

File tree

7 files changed

+115
-5
lines changed

7 files changed

+115
-5
lines changed

src/main/scala/viper/silver/ast/Expression.scala

+11-1
Original file line numberDiff line numberDiff line change
@@ -1290,10 +1290,20 @@ sealed trait Lhs extends Exp
12901290
* reach the backend verifiers. */
12911291
trait ExtensionExp extends Exp {
12921292
def extensionIsPure: Boolean
1293+
12931294
def extensionSubnodes: Seq[Node]
1295+
12941296
def typ: Type
1297+
12951298
def verifyExtExp(): VerificationResult
1299+
12961300
/** Pretty printing functionality as defined for other nodes in class FastPrettyPrinter.
1297-
* Sample implementation would be text("old") <> parens(show(e)) for pretty-printing an old-expression.*/
1301+
* Sample implementation would be text("old") <> parens(show(e)) for pretty-printing an old-expression. */
12981302
def prettyPrint: PrettyPrintPrimitives#Cont
1303+
1304+
/** Defines whether this expression can be used as a trigger. Defaults to false. */
1305+
def extensionIsValidTrigger(): Boolean = false
1306+
1307+
/** Defines whether this expression is forbidden from occurring *anywhere* in a trigger. Defaults to true. */
1308+
def extensionIsForbiddenInTrigger(): Boolean = true
12991309
}

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

+3-2
Original file line numberDiff line numberDiff line change
@@ -269,8 +269,9 @@ object Consistency {
269269
e match {
270270
case Old(nested) => validTrigger(nested, program) // case corresponds to OldTrigger node
271271
case LabelledOld(nested, _) => validTrigger(nested, program)
272-
case wand: MagicWand => wand.subexpressionsToEvaluate(program).forall(e => !e.existsDefined {case _: ForbiddenInTrigger => })
273-
case _ : PossibleTrigger | _: FieldAccess | _: PredicateAccess => !e.existsDefined { case _: ForbiddenInTrigger => }
272+
case ee: ExtensionExp => ee.extensionIsValidTrigger()
273+
case wand: MagicWand => wand.subexpressionsToEvaluate(program).forall(e => !e.exists(Expressions.isForbiddenInTrigger))
274+
case _ : PossibleTrigger | _: FieldAccess | _: PredicateAccess => !e.exists(Expressions.isForbiddenInTrigger)
274275
case _ => false
275276
}
276277
}

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

+6
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,12 @@ object Expressions {
295295
leftConds ++ guardedRightConds
296296
}
297297

298+
def isForbiddenInTrigger(n: Node): Boolean = n match {
299+
case _: ForbiddenInTrigger => true
300+
case ee: ExtensionExp => ee.extensionIsForbiddenInTrigger()
301+
case _ => false
302+
}
303+
298304
/** See [[viper.silver.ast.utility.Triggers.TriggerGeneration.generateTriggerSetGroups]] */
299305
def generateTriggerGroups(exp: QuantifiedExp, tg: TriggerGeneration = DefaultTriggerGeneration): Seq[(Seq[tg.TriggerSet], Seq[LocalVarDecl])] = {
300306
tg.generateTriggerSetGroups(exp.variables map (_.localVar), exp.exp)

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ object Triggers {
3030
/* True iff the given node is a possible trigger */
3131
protected def isPossibleTrigger(e: Exp): Boolean = (customIsPossibleTrigger orElse {
3232
case _: PossibleTrigger => true
33+
case ee: ExtensionExp => ee.extensionIsValidTrigger()
3334
case Old(_: PossibleTrigger) => true
3435
case LabelledOld(_: PossibleTrigger, _) => true
3536
case _ => false
@@ -38,7 +39,7 @@ object Triggers {
3839
/* Note: If Add and Sub were type arguments of GenericTriggerGenerator, the latter
3940
* could implement isForbiddenInTrigger already */
4041
def isForbiddenInTrigger(e: Exp) = (customIsForbiddenInTrigger orElse {
41-
case _: ForbiddenInTrigger => true
42+
case e if Expressions.isForbiddenInTrigger(e) => true
4243
case _ => false
4344
}: PartialFunction[Exp, Boolean])(e)
4445

src/main/scala/viper/silver/plugin/standard/adt/AdtASTExtension.scala

+14-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ package viper.silver.plugin.standard.adt
99
import viper.silver.ast._
1010
import viper.silver.ast.pretty.FastPrettyPrinter.{ContOps, braces, brackets, char, defaultIndent, line, nest, nil, parens, show, showType, showVars, space, ssep, text}
1111
import viper.silver.ast.pretty.PrettyPrintPrimitives
12-
import viper.silver.ast.utility.Consistency
12+
import viper.silver.ast.utility.{Consistency, Expressions}
1313
import viper.silver.verifier.{ConsistencyError, Failure, VerificationResult}
1414

1515
/**
@@ -206,6 +206,10 @@ case class AdtConstructorApp(name: String, args: Seq[Exp], typVarMap: Map[TypeVa
206206
AdtConstructorApp(first, second, third)(this.pos, this.info, this.typ, this.adtName, this.errT).asInstanceOf[this.type]
207207
}
208208
}
209+
210+
override def extensionIsValidTrigger(): Boolean = args.forall(a => !a.exists(Expressions.isForbiddenInTrigger))
211+
212+
override def extensionIsForbiddenInTrigger(): Boolean = false
209213
}
210214

211215
object AdtConstructorApp {
@@ -253,6 +257,10 @@ case class AdtDestructorApp(name: String, rcv: Exp, typVarMap: Map[TypeVar, Type
253257
AdtDestructorApp(first, second, third)(this.pos, this.info, this.typ, this.adtName, this.errT).asInstanceOf[this.type]
254258
}
255259
}
260+
261+
override def extensionIsValidTrigger(): Boolean = !rcv.exists(Expressions.isForbiddenInTrigger)
262+
263+
override def extensionIsForbiddenInTrigger(): Boolean = false
256264
}
257265

258266
object AdtDestructorApp {
@@ -306,6 +314,11 @@ case class AdtDiscriminatorApp(name: String, rcv: Exp, typVarMap: Map[TypeVar, T
306314
}
307315
}
308316

317+
// Since a discriminator desugars to ADT_tag(rcv) == name_tag, which contains an equality,
318+
// it cannot be used anywhere inside a trigger.
319+
override def extensionIsValidTrigger(): Boolean = false
320+
321+
override def extensionIsForbiddenInTrigger(): Boolean = true
309322
}
310323

311324
object AdtDiscriminatorApp {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Any copyright is dedicated to the Public Domain.
2+
// http://creativecommons.org/publicdomain/zero/1.0/
3+
4+
5+
adt Term {
6+
Const(n: Int)
7+
}
8+
9+
domain theory {
10+
function foo(t: Term): Bool
11+
12+
axiom ax1 { forall n: Int ::
13+
{Const(n)} // Type checker error
14+
{foo(Const(n))} // OK
15+
foo(Const(n))
16+
}
17+
}
18+
19+
method testConstr(s: Set[Int], j: Int) {
20+
assume j in s
21+
assume forall i: Int :: {Const(i)} i in s ==> i > 0
22+
var t: Term := Const(j)
23+
assert j > 0
24+
}
25+
26+
method testConstrF(s: Set[Int], j: Int) {
27+
assume j in s
28+
assume forall i: Int :: {Const(i)} i in s ==> i > 0
29+
//:: ExpectedOutput(assert.failed:assertion.false)
30+
assert j > 0
31+
}
32+
33+
method testDestr(s: Set[Term], c: Term) {
34+
assume c in s
35+
assume forall i: Term :: {i.n} i in s ==> foo(i)
36+
var t: Int := c.n
37+
assert foo(c)
38+
}
39+
40+
method testDestr2(s: Set[Term], c: Term) {
41+
assume c in s
42+
assume forall i: Term :: {i.n} i in s ==> foo(i)
43+
//:: ExpectedOutput(assert.failed:assertion.false)
44+
assert foo(c)
45+
}
46+
47+
method testDiscr(s: Set[Term], c: Term) {
48+
assume c in s
49+
// i.isConst must not be picked as a trigger, otherwise the backends may crash
50+
assume forall i: Term :: i.isConst ==> foo(i)
51+
var t: Bool := c.isConst
52+
assert foo(c)
53+
}
54+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Any copyright is dedicated to the Public Domain.
2+
// http://creativecommons.org/publicdomain/zero/1.0/
3+
4+
5+
adt Term {
6+
Const(n: Int)
7+
}
8+
9+
domain theory {
10+
function foo(t: Term): Bool
11+
12+
axiom ax1 { forall n: Int ::
13+
{Const(n)} // Type checker error
14+
{foo(Const(n))} // OK
15+
foo(Const(n))
16+
}
17+
}
18+
19+
20+
method testDiscr(s: Set[Term], c: Term) {
21+
assume c in s
22+
//:: ExpectedOutput(typechecker.error)
23+
assume forall i: Term :: {i.isConst} i in s ==> foo(i)
24+
assert foo(c)
25+
}

0 commit comments

Comments
 (0)