Skip to content

Commit

Permalink
Merge pull request #814 from viperproject/meilers_asserting
Browse files Browse the repository at this point in the history
New "asserting" expression
  • Loading branch information
marcoeilers authored Nov 13, 2024
2 parents 5611fcb + 5f5c022 commit 08c001b
Show file tree
Hide file tree
Showing 14 changed files with 242 additions and 3 deletions.
5 changes: 5 additions & 0 deletions src/main/scala/viper/silver/ast/Expression.scala
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,11 @@ case class Applying(wand: MagicWand, body: Exp)(val pos: Position = NoPosition,
lazy val typ = body.typ
}

case class Asserting(a: Exp, body: Exp)(val pos: Position = NoPosition, val info: Info = NoInfo, val errT: ErrorTrafo = NoTrafos) extends Exp {
override lazy val check: Seq[ConsistencyError] = Consistency.checkPure(body)
lazy val typ = body.typ
}

// --- Old expressions

sealed trait OldExp extends UnExp {
Expand Down
2 changes: 2 additions & 0 deletions src/main/scala/viper/silver/ast/pretty/PrettyPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,8 @@ object FastPrettyPrinter extends FastPrettyPrinterBase with BracketPrettyPrinter
group(parens(text("unfolding") <+> nest(defaultIndent, show(acc)) <+> "in" <> nest(defaultIndent, line <> show(exp))))
case Applying(wand, exp) =>
parens(text("applying") <+> nest(defaultIndent, show(wand)) <+> "in" <> nest(defaultIndent, line <> show(exp)))
case Asserting(ass, exp) =>
parens(text("asserting") <+> nest(defaultIndent, parens(show(ass))) <+> "in" <> nest(defaultIndent, line <> show(exp)))
case Old(exp) =>
text("old") <> parens(show(exp))
case LabelledOld(exp,label) =>
Expand Down
5 changes: 4 additions & 1 deletion src/main/scala/viper/silver/ast/utility/Expressions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ object Expressions {
case CondExp(cnd, thn, els) => isPure(cnd) && isPure(thn) && isPure(els)
case unf: Unfolding => isPure(unf.body)
case app: Applying => isPure(app.body)
case Asserting(a, e) => isPure(e)
case QuantifiedExp(_, e0) => isPure(e0)
case Let(_, _, body) => isPure(body)
case e: ExtensionExp => e.extensionIsPure
Expand Down Expand Up @@ -96,9 +97,10 @@ object Expressions {
def asBooleanExp(e: Exp): Exp = {
e.transform({
case _: AccessPredicate | _: MagicWand => TrueLit()()
case fa@Forall(vs,ts,body) => Forall(vs,ts,asBooleanExp(body))(fa.pos,fa.info)
case fa@Forall(vs,ts,body) => Forall(vs, ts, asBooleanExp(body))(fa.pos, fa.info, fa.errT)
case Unfolding(_, exp) => asBooleanExp(exp)
case Applying(_, exp) => asBooleanExp(exp)
case ass@Asserting(a, exp) => Asserting(asBooleanExp(a), asBooleanExp(exp))(ass.pos, ass.info, ass.errT)
})
}

Expand Down Expand Up @@ -199,6 +201,7 @@ object Expressions {
case MapLookup(m, k) => List(MapContains(k, m)(p))
case Unfolding(pred, _) => List(pred)
case Applying(wand, _) => List(wand)
case Asserting(a, _) => List(a)
case _ => Nil
}
// Only use non-trivial conditions for the subnodes.
Expand Down
1 change: 1 addition & 0 deletions src/main/scala/viper/silver/ast/utility/Nodes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ object Nodes {
case PredicateAccessPredicate(pred_acc, perm) => Seq(pred_acc, perm)
case Unfolding(acc, body) => Seq(acc, body)
case Applying(wand, body) => Seq(wand, body)
case Asserting(ass, body) => Seq(ass, body)
case Old(exp) => Seq(exp)
case CondExp(cond, thn, els) => Seq(cond, thn, els)
case Let(v, exp, body) => Seq(v, exp, body)
Expand Down
9 changes: 7 additions & 2 deletions src/main/scala/viper/silver/parser/FastParser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ object FastParserCompanion {
// maps
PKw.Map, PKwOp.Range, PKwOp.Domain,
// prover hint expressions
PKwOp.Unfolding, PKwOp.In, PKwOp.Applying,
PKwOp.Unfolding, PKwOp.In, PKwOp.Applying, PKwOp.Asserting,
// old expression
PKwOp.Old, PKw.Lhs,
// other expressions
Expand Down Expand Up @@ -365,7 +365,7 @@ class FastParser {
def atomReservedKw[$: P]: P[PExp] = {
reservedKwMany(
StringIn("true", "false", "null", "old", "result", "acc", "none", "wildcard", "write", "epsilon", "perm", "let", "forall", "exists", "forperm",
"unfolding", "applying", "Set", "Seq", "Multiset", "Map", "range", "domain", "new"),
"unfolding", "applying", "asserting", "Set", "Seq", "Multiset", "Map", "range", "domain", "new"),
str => pos => str match {
case "true" => Pass.map(_ => PBoolLit(PReserved(PKw.True)(pos))(_))
case "false" => Pass.map(_ => PBoolLit(PReserved(PKw.False)(pos))(_))
Expand All @@ -384,6 +384,7 @@ class FastParser {
case "forperm" => forperm.map(_(PReserved(PKw.Forperm)(pos)))
case "unfolding" => unfolding.map(_(PReserved(PKwOp.Unfolding)(pos)))
case "applying" => applying.map(_(PReserved(PKwOp.Applying)(pos)))
case "asserting" => asserting.map(_(PReserved(PKwOp.Asserting)(pos)))
case "Set" => setConstructor.map(_(PReserved(PKwOp.Set)(pos)))
case "Seq" => seqConstructor.map(_(PReserved(PKwOp.Seq)(pos)))
case "Multiset" => multisetConstructor.map(_(PReserved(PKwOp.Multiset)(pos)))
Expand Down Expand Up @@ -652,6 +653,10 @@ class FastParser {
PApplying(_, wand.inner, op, b)
}

def asserting[$: P]: P[PKwOp.Asserting => Pos => PExp] = P(exp.parens ~ PKwOp.In ~ exp).map {
case (a, in, b) => PAsserting(_, a.inner, in, b)
}

def predicateAccessAssertion[$: P]: P[PAccAssertion] = P(accessPred | predAcc)

def setConstructor[$: P]: P[PKwOp.Set => Pos => PExp] =
Expand Down
6 changes: 6 additions & 0 deletions src/main/scala/viper/silver/parser/ParseAst.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1080,6 +1080,12 @@ case class PApplying(applying: PKwOp.Applying, wand: PExp, in: PKwOp.In, exp: PE
List(Map(POpApp.pArgS(0) -> Wand, POpApp.pResS -> POpApp.pArg(1)))
}

case class PAsserting(asserting: PKwOp.Asserting, a: PExp, in: PKwOp.In, exp: PExp)(val pos: (Position, Position)) extends PHeapOpApp {
override val args = Seq(a, exp)
override val signatures: List[PTypeSubstitution] =
List(Map(POpApp.pArgS(0) -> Impure, POpApp.pResS -> POpApp.pArg(1)))
}

sealed trait PBinder extends PExp with PScope {
def boundVars: Seq[PLogicalVarDecl]

Expand Down
2 changes: 2 additions & 0 deletions src/main/scala/viper/silver/parser/ParseAstKeyword.scala
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,8 @@ object PKwOp {
type Unfolding = PReserved[Unfolding.type]
case object Applying extends PKwOp("applying") with PKeywordAtom with RightSpace
type Applying = PReserved[Applying.type]
case object Asserting extends PKwOp("asserting") with PKeywordAtom with RightSpace
type Asserting = PReserved[Asserting.type]
case object Let extends PKwOp("let") with PKeywordAtom with RightSpace
type Let = PReserved[Let.type]

Expand Down
2 changes: 2 additions & 0 deletions src/main/scala/viper/silver/parser/Translator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,8 @@ case class Translator(program: PProgram) {
Unfolding(exp(loc).asInstanceOf[PredicateAccessPredicate], exp(e))(pos, info)
case PApplying(_, wand, _, e) =>
Applying(exp(wand).asInstanceOf[MagicWand], exp(e))(pos, info)
case PAsserting(_, a, _, e) =>
Asserting(exp(a), exp(e))(pos, info)
case pl@PLet(_, _, _, exp1, _, PLetNestedScope(body)) =>
Let(liftLogicalDecl(pl.decl), exp(exp1.inner), exp(body))(pos, info)
case _: PLetNestedScope =>
Expand Down
38 changes: 38 additions & 0 deletions src/test/resources/all/asserting/function.vpr
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/publicdomain/zero/1.0/

field f: Int

function fplusone(x: Ref, y: Ref): Int
requires acc(x.f)
{
asserting (x != null) in (x.f + 1)
}

//:: ExpectedOutput(function.not.wellformed:assertion.false)
function fplusone2(x: Ref, y: Ref): Int
requires acc(x.f)
{
asserting (x != y) in (x.f + 1)
}

method main()
{
var x: Ref
x := new(f)

var hmm: Int
hmm := fplusone(x, x)
hmm := asserting (hmm == x.f + 1) in hmm
}

method main2()
{
var x: Ref
x := new(f)

var hmm: Int
hmm := fplusone(x, x)
//:: ExpectedOutput(assignment.failed:assertion.false)
hmm := asserting (hmm == x.f) in hmm
}
19 changes: 19 additions & 0 deletions src/test/resources/all/asserting/other-fail.vpr
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/publicdomain/zero/1.0/

field x: Int

method test1() {
//:: ExpectedOutput(assert.failed:division.by.zero)
assert asserting (1 / 0 == 0) in true
}

method test2() {
//:: ExpectedOutput(assert.failed:division.by.zero)
assert asserting (true) in (1 / 0 == 0)
}

method test3(r: Ref) {
//:: ExpectedOutput(assert.failed:insufficient.permission)
assert asserting (true && acc(r.x)) in true
}
44 changes: 44 additions & 0 deletions src/test/resources/all/asserting/qp.vpr
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/publicdomain/zero/1.0/

field f: Int

method m1(s: Set[Ref])
requires |s| > 0
{
inhale forall x: Ref :: x in (asserting(|s| > 0) in s) ==> acc(x.f)
}

method m1Fail(s: Set[Ref])
{
//:: ExpectedOutput(inhale.failed:assertion.false)
inhale forall x: Ref :: x in (asserting(|s| > 0) in s) ==> acc(x.f)
}

method m2(s: Set[Ref])
requires |s| > 0
requires !(null in s)
{
inhale forall x: Ref :: x in s ==> acc((asserting (x != null) in x).f)
}

method m2Fail(s: Set[Ref])
requires |s| > 0
{
//:: ExpectedOutput(inhale.failed:assertion.false)
inhale forall x: Ref :: x in s ==> acc((asserting (x != null) in x).f)
}

method m3(s: Set[Ref])
requires |s| > 0
requires !(null in s)
{
inhale forall x: Ref :: x in s ==> acc(x.f, asserting (x != null) in write)
}

method m3Fail(s: Set[Ref])
requires |s| > 0
{
//:: ExpectedOutput(inhale.failed:assertion.false)
inhale forall x: Ref :: x in s ==> acc(x.f, asserting (x != null) in write)
}
73 changes: 73 additions & 0 deletions src/test/resources/all/asserting/simple-fail.vpr
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/publicdomain/zero/1.0/

field f: Int

method assign() {
//:: ExpectedOutput(assignment.failed:assertion.false)
var x: Int := asserting (false) in 0
}

method assign2(i: Int) {
//:: ExpectedOutput(assignment.failed:assertion.false)
var x: Int := asserting (i > 0) in 0
}

method assign3(i: Int)
requires i > 5
{
var x: Int := asserting (i > 0) in 0
}

method pres()
//:: ExpectedOutput(not.wellformed:assertion.false)
requires asserting (false) in false
{
assert false
}

method pres2(x: Ref)
//:: ExpectedOutput(not.wellformed:insufficient.permission)
requires asserting (acc(x.f)) in false
{
assert false
}

method pres3(x: Ref)
requires acc(x.f)
requires asserting (acc(x.f)) in false
{
assert false
}

//:: ExpectedOutput(function.not.wellformed:assertion.false)
function fun(): Int
{
asserting (false) in 0
}

//:: ExpectedOutput(function.not.wellformed:insufficient.permission)
function fun2(x: Ref): Int
{
asserting (acc(x.f) && x.f > 0) in 0
}

//:: ExpectedOutput(function.not.wellformed:assertion.false)
function fun3(x: Ref): Int
requires acc(x.f)
{
asserting (acc(x.f) && x.f > 0) in 0
}

function fun4(x: Ref): Int
requires acc(x.f) && x.f > 8
{
asserting (acc(x.f) && x.f > 0) in 0
}

method stateUnchanged(x: Ref)
requires acc(x.f)
{
var y: Int := asserting (acc(x.f)) in x.f
assert acc(x.f)
}
22 changes: 22 additions & 0 deletions src/test/resources/all/asserting/trigger.vpr
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/publicdomain/zero/1.0/

function trigger(i: Int): Bool
{
true
}

method triggerUse(s: Seq[Int])
requires |s| > 0
requires forall i: Int :: {trigger(i)} 0 <= i < |s| ==> s[i] > 0
{
assert asserting (trigger(0)) in s[0] > 0
}

method triggerUse2(s: Seq[Int])
requires |s| > 0
requires forall i: Int :: {trigger(i)} 0 <= i < |s| ==> s[i] > 0
{
//:: ExpectedOutput(assert.failed:assertion.false)
assert s[0] > 0
}
17 changes: 17 additions & 0 deletions src/test/resources/all/asserting/wand.vpr
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/publicdomain/zero/1.0/

field f: Int
field g: Int

method test0(x: Ref)
{
package acc(x.f) && (asserting (acc(x.f)) in (x.f == 0)) --* acc(x.f) {}
}

method test1(x: Ref)
{
//:: ExpectedOutput(package.failed:insufficient.permission)
package acc(x.f) && (asserting (acc(x.g)) in (x.f == 0)) --* acc(x.f) {}
}

0 comments on commit 08c001b

Please sign in to comment.