Skip to content

Commit 47abf04

Browse files
committed
Improve language flexibility
1 parent ae4a123 commit 47abf04

21 files changed

Lines changed: 246 additions & 304 deletions

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import viper.silver.verifier.{ConsistencyError, VerificationResult}
1616
/** Expressions. */
1717
sealed trait Exp extends Hashable with Typed with Positioned with Infoed with TransformableErrors with PrettyExpression {
1818
lazy val isPure = Expressions.isPure(this)
19+
lazy val noAccessPredicates = Expressions.noAccessPredicates(this)
1920
def isHeapDependent(p: Program) = Expressions.isHeapDependent(this, p)
2021
def isTopLevelHeapDependent(p: Program) = Expressions.isTopLevelHeapDependent(this, p)
2122

@@ -446,6 +447,8 @@ case class FieldAccess(rcv: Exp, field: Field)
446447
def getArgs: Seq[Exp] = Seq(rcv)
447448
def withArgs(args: Seq[Exp]): FieldAccess = copy(rcv = args.head, field)(pos, info, errT)
448449
// def asManifestation: Exp = this
450+
451+
override def name: String = field.name
449452
}
450453

451454
/** A predicate access expression. See also companion object below for an alternative creation signature */
@@ -533,6 +536,14 @@ case class Let(variable: LocalVarDecl, exp: Exp, body: Exp)(val pos: Position =
533536
val scopedDecls: Seq[Declaration] = Seq(variable)
534537
}
535538

539+
/** An expression that creates a new object. */
540+
case class NewExp(fields: Seq[Field])(val pos: Position = NoPosition, val info: Info = NoInfo, val errT: ErrorTrafo = NoTrafos) extends Exp {
541+
override lazy val check : Seq[ConsistencyError] =
542+
(if(!Consistency.noResult(this)) Seq(ConsistencyError("Result variables are only allowed in postconditions of functions.", pos)) else Seq())
543+
544+
override def typ: Type = Ref
545+
}
546+
536547
// --- Quantifications
537548

538549
/** A common trait for quantified expressions. */
@@ -1257,7 +1268,9 @@ sealed abstract class DomainUnExp(val funct: UnOp) extends PrettyUnaryExpression
12571268
}
12581269

12591270
/** Expressions which can appear on the left hand side of an assignment */
1260-
sealed trait Lhs extends Exp
1271+
sealed trait Lhs extends Exp {
1272+
def name: String
1273+
}
12611274

12621275
/** Generic Expression to use to extend the AST.
12631276
* New expression-typed AST nodes can be defined by creating new case classes extending this trait.

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

Lines changed: 5 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -37,49 +37,16 @@ sealed trait Stmt extends Hashable with Infoed with Positioned with Transformabl
3737
}
3838
}
3939

40-
/** A statement that creates a new object and assigns it to a local variable. */
41-
case class NewStmt(lhs: LocalVar, fields: Seq[Field])(val pos: Position = NoPosition, val info: Info = NoInfo, val errT: ErrorTrafo = NoTrafos) extends Stmt {
40+
/** An assignment to a field or a local variable. */
41+
case class Assign[T <: Lhs](lhs: T, rhs: Exp)(val pos: Position = NoPosition, val info: Info = NoInfo, val errT: ErrorTrafo = NoTrafos) extends Stmt {
4242
override lazy val check : Seq[ConsistencyError] =
4343
(if(!Consistency.noResult(this)) Seq(ConsistencyError("Result variables are only allowed in postconditions of functions.", pos)) else Seq()) ++
44-
(if(!(Ref isSubtype lhs)) Seq(ConsistencyError(s"Left-hand side of New statement must be Ref type, but found ${lhs.typ}", lhs.pos)) else Seq())
45-
46-
}
47-
48-
/** An assignment to a field or a local variable */
49-
sealed trait AbstractAssign extends Stmt {
50-
51-
def lhs: Lhs
52-
53-
def rhs: Exp
54-
}
55-
56-
object AbstractAssign {
57-
def apply(lhs: Lhs, rhs: Exp)(pos: Position = NoPosition, info: Info = NoInfo, errT: ErrorTrafo = NoTrafos) = lhs match {
58-
case l: LocalVar => LocalVarAssign(l, rhs)(pos, info, errT)
59-
case l: FieldAccess => FieldAssign(l, rhs)(pos, info, errT)
60-
}
61-
62-
def unapply(a: AbstractAssign) = Some((a.lhs, a.rhs))
63-
}
64-
65-
/** An assignment to a local variable. */
66-
case class LocalVarAssign(lhs: LocalVar, rhs: Exp)(val pos: Position = NoPosition, val info: Info = NoInfo, val errT: ErrorTrafo = NoTrafos) extends AbstractAssign{
67-
override lazy val check : Seq[ConsistencyError] =
68-
(if(!Consistency.noResult(this)) Seq(ConsistencyError("Result variables are only allowed in postconditions of functions.", pos)) else Seq()) ++
69-
Consistency.checkPure(rhs) ++
70-
(if(!Consistency.isAssignable(rhs, lhs)) Seq(ConsistencyError(s"Right-hand side $rhs is not assignable to left-hand side $lhs.", lhs.pos)) else Seq())
71-
}
72-
73-
/** An assignment to a field variable. */
74-
case class FieldAssign(lhs: FieldAccess, rhs: Exp)(val pos: Position = NoPosition, val info: Info = NoInfo, val errT: ErrorTrafo = NoTrafos) extends AbstractAssign{
75-
override lazy val check : Seq[ConsistencyError] =
76-
(if(!Consistency.noResult(this)) Seq(ConsistencyError("Result variables are only allowed in postconditions of functions.", pos)) else Seq()) ++
77-
Consistency.checkPure(rhs) ++
44+
Consistency.checkCanAssignFrom(rhs) ++
7845
(if(!Consistency.isAssignable(rhs, lhs)) Seq(ConsistencyError(s"Right-hand side $rhs is not assignable to left-hand side $lhs.", lhs.pos)) else Seq())
7946
}
8047

8148
/** A method call. */
82-
case class MethodCall(methodName: String, args: Seq[Exp], targets: Seq[LocalVar])(val pos: Position, val info: Info, val errT: ErrorTrafo) extends Stmt {
49+
case class MethodCall(methodName: String, args: Seq[Exp], targets: Seq[Lhs])(val pos: Position, val info: Info, val errT: ErrorTrafo) extends Stmt {
8350
override lazy val check : Seq[ConsistencyError] = {
8451
var s = Seq.empty[ConsistencyError]
8552
if(!Consistency.noResult(this))
@@ -92,7 +59,7 @@ case class MethodCall(methodName: String, args: Seq[Exp], targets: Seq[LocalVar]
9259
}
9360

9461
object MethodCall {
95-
def apply(method: Method, args: Seq[Exp], targets: Seq[LocalVar])(pos: Position = NoPosition, info: Info = NoInfo, errT: ErrorTrafo = NoTrafos): MethodCall = {
62+
def apply(method: Method, args: Seq[Exp], targets: Seq[Lhs])(pos: Position = NoPosition, info: Info = NoInfo, errT: ErrorTrafo = NoTrafos): MethodCall = {
9663
MethodCall(method.name, args, targets)(pos, info, errT)
9764
}
9865
}

src/main/scala/viper/silver/ast/pretty/PrettyPrinter.scala

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -644,10 +644,7 @@ object FastPrettyPrinter extends FastPrettyPrinterBase with BracketPrettyPrinter
644644
/** Show a statement. */
645645
def showStmt(stmt: Stmt): Cont = {
646646
val stmtDoc = stmt match {
647-
case NewStmt(target, fields) =>
648-
show(target) <+> ":=" <+> "new(" <> ssep(fields map (f => value(f.name)), char(',') <> space) <> ")"
649-
case LocalVarAssign(lhs, rhs) => show(lhs) <+> ":=" <+> nest(defaultIndent, show(rhs))
650-
case FieldAssign(lhs, rhs) => show(lhs) <+> ":=" <+> nest(defaultIndent, show(rhs))
647+
case Assign(lhs, rhs) => show(lhs) <+> ":=" <+> nest(defaultIndent, show(rhs))
651648
case Fold(e) => text("fold") <+> nest(defaultIndent, show(e))
652649
case Unfold(e) => text("unfold") <+> nest(defaultIndent, show(e))
653650
case Package(e, proofScript) => text("package") <+> show(e) <+> showBlock(proofScript)
@@ -748,6 +745,8 @@ object FastPrettyPrinter extends FastPrettyPrinterBase with BracketPrettyPrinter
748745
case Let(v, exp, body) =>
749746
parens(text("let") <+> text(v.name) <+> "==" <> nest(defaultIndent, line <> parens(show(exp))) <+>
750747
"in" <> nest(defaultIndent, line <> show(body)))
748+
case NewExp(fields) =>
749+
text("new") <> parens(ssep(fields map (f => value(f.name)), char(',') <> space))
751750
case CondExp(cond, thn, els) =>
752751
group(parens(show(cond) <+> "?" <> nest(defaultIndent, line <> show(thn) <+> ":" <@> show(els))))
753752
case Exists(v, triggers, exp) =>

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

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ object Consistency {
127127
def nullValue[T](a: T, b: T) = if (a != null) a else b
128128

129129
/**
130-
* Checks that this boolean expression is pure i.e. it contains no magic wands, access predicates or ghost operations.
130+
* Checks that this boolean expression is pure i.e. it contains no magic wands, access predicates or `new` operations.
131131
*/
132132
def checkPure(e: Exp): Seq[ConsistencyError] = {
133133
if(!e.isPure){
@@ -137,6 +137,17 @@ object Consistency {
137137
}
138138
}
139139

140+
/**
141+
* Checks that this expression can be used to assign to a variable i.e. it contains no magic wands or access predicates.
142+
*/
143+
def checkCanAssignFrom(e: Exp): Seq[ConsistencyError] = {
144+
if(!e.noAccessPredicates){
145+
Seq(ConsistencyError( s"$e is non pure and appears where only pure expressions are allowed.", e.pos))
146+
}else{
147+
Seq()
148+
}
149+
}
150+
140151
/** Checks that no perm or forperm expression occurs in a node, except inside inhale/exhale statements */
141152
def checkNoPermForpermExceptInhaleExhale(e: Exp) : Seq[ConsistencyError] = {
142153
val permsAndForperms: Seq[Node] = e.deepCollect({case p: CurrentPerm => p; case fp: ForPerm => fp})
@@ -165,15 +176,12 @@ object Consistency {
165176
val argVars = args.map(_.localVar).toSet
166177
var s = Seq.empty[ConsistencyError]
167178

168-
for (a@LocalVarAssign(l, _) <- b if argVars.contains(l)) {
179+
for (a@Assign(l: LocalVar, _) <- b if argVars.contains(l)) {
169180
s :+= ConsistencyError(s"$a is a reassignment of formal argument $l.", a.pos)
170181
}
171-
for (c@MethodCall(_, _, targets) <- b; t <- targets if argVars.contains(t)) {
182+
for (c@MethodCall(_, _, targets) <- b; t@LocalVar(_, _) <- targets if argVars.contains(t)) {
172183
s :+= ConsistencyError(s"$c is a reassignment of formal argument $t.", c.pos)
173184
}
174-
for (n@NewStmt(l, _) <- b if argVars.contains(l)){
175-
s :+= ConsistencyError(s"$n is a reassignment of formal argument $l.", n.pos)
176-
}
177185
s
178186
}
179187
/** Check all properties required for a precondition. */
@@ -387,13 +395,13 @@ object Consistency {
387395

388396
private def checkMagicWandProofScript(script: Stmt, locals: Seq[LocalVarDecl]): Seq[ConsistencyError] =
389397
script.shallowCollect({
390-
case fa: FieldAssign =>
398+
case Assign(fa: FieldAccess, _) =>
391399
Some(ConsistencyError("Field assignments are not allowed in magic wand proof scripts.", fa.pos))
392-
case ne: NewStmt =>
393-
Some(ConsistencyError("New statements statements are not allowed in magic wand proof scripts.", ne.pos))
400+
case ne: NewExp =>
401+
Some(ConsistencyError("New expressions are not allowed in magic wand proof scripts.", ne.pos))
394402
case wh: While =>
395403
Some(ConsistencyError("While statements are not allowed in magic wand proof scripts.", wh.pos))
396-
case loc @ LocalVarAssign(LocalVar(varName, _), _) if !locals.exists(_.name == varName) =>
404+
case loc @ Assign(LocalVar(varName, _), _) if !locals.exists(_.name == varName) =>
397405
Some(ConsistencyError("Can only assign to local variables that were declared inside the proof script.", loc.pos))
398406
case _: Package => None
399407
}).flatten

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

Lines changed: 3 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -14,35 +14,9 @@ import viper.silver.utility.Sanitizer
1414

1515
/** Utility methods for expressions. */
1616
object Expressions {
17-
def isPure(e: Exp): Boolean = e match {
18-
case _: AccessPredicate
19-
| _: MagicWand
20-
=> false
21-
22-
case UnExp(e0) => isPure(e0)
23-
case InhaleExhaleExp(in, ex) => isPure(in) && isPure(ex)
24-
case BinExp(e0, e1) => isPure(e0) && isPure(e1)
25-
case CondExp(cnd, thn, els) => isPure(cnd) && isPure(thn) && isPure(els)
26-
case unf: Unfolding => isPure(unf.body)
27-
case app: Applying => isPure(app.body)
28-
case QuantifiedExp(_, e0) => isPure(e0)
29-
case Let(_, _, body) => isPure(body)
30-
case e: ExtensionExp => e.extensionIsPure
31-
32-
case _: Literal
33-
| _: PermExp
34-
| _: FuncApp
35-
| _: DomainFuncApp
36-
| _: BackendFuncApp
37-
| _: LocationAccess
38-
| _: AbstractLocalVar
39-
| _: SeqExp
40-
| _: SetExp
41-
| _: MultisetExp
42-
| _: MapExp
43-
| _: ForPerm
44-
=> true
45-
}
17+
def isPure(e: Exp): Boolean = noAccessPredicates(e) && !e.contains[NewExp]
18+
// This includes `acc(..)`, `Pred(..)` and `.. --* ...`
19+
def noAccessPredicates(e: Exp): Boolean = !e.contains[AccessPredicate]
4620

4721
def isTopLevelHeapDependent(e: Exp, p:Program) : Boolean = e match {
4822
case _: AccessPredicate

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,7 @@ object Nodes {
4848
}
4949
case s: Stmt =>
5050
s match {
51-
case NewStmt(target, fields) => Seq(target) ++ fields
52-
case LocalVarAssign(lhs, rhs) => Seq(lhs, rhs)
53-
case FieldAssign(lhs, rhs) => Seq(lhs, rhs)
51+
case Assign(lhs, rhs) => Seq(lhs, rhs)
5452
case Fold(e) => Seq(e)
5553
case Unfold(e) => Seq(e)
5654
case Package(e, proofScript) => Seq(e, proofScript)
@@ -85,6 +83,7 @@ object Nodes {
8583
case Applying(wand, body) => Seq(wand, body)
8684
case Old(exp) => Seq(exp)
8785
case CondExp(cond, thn, els) => Seq(cond, thn, els)
86+
case NewExp(fields) => fields
8887
case Let(v, exp, body) => Seq(v, exp, body)
8988
case Exists(v, triggers, exp) => v ++ triggers ++ Seq(exp)
9089
case Forall(v, triggers, exp) => v ++ triggers ++ Seq(exp)

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,8 @@ object Statements {
7979
var writtenTo = Seq[LocalVar]()
8080

8181
s visit {
82-
case LocalVarAssign(lhs, _) => writtenTo = lhs +: writtenTo
83-
case MethodCall(_, _, targets) => writtenTo = writtenTo ++ targets
84-
case NewStmt(target, _) => writtenTo = target +: writtenTo
82+
case Assign(lhs: LocalVar, _) => writtenTo = lhs +: writtenTo
83+
case MethodCall(_, _, targets) => writtenTo = writtenTo ++ targets collect { case t: LocalVar => t }
8584
case While(_, _, body) => writtenTo = writtenTo ++ (writtenVars(body) intersect s.undeclLocalVars)
8685
}
8786

src/main/scala/viper/silver/cfg/silver/CfgGenerator.scala

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -215,16 +215,14 @@ object CfgGenerator {
215215
val label = TmpLabel(name)
216216
addLabel(label)
217217
addStatement(WrappedStmt(stmt))
218-
case _: LocalVarAssign |
219-
_: FieldAssign |
218+
case _: Assign[_] |
220219
_: Inhale |
221220
_: Exhale |
222221
_: Fold |
223222
_: Unfold |
224223
_: Package |
225224
_: Apply |
226225
_: MethodCall |
227-
_: NewStmt |
228226
_: Assert |
229227
_: LocalVarDeclStmt |
230228
_: Assume |

0 commit comments

Comments
 (0)