Skip to content

Commit b4ed514

Browse files
committed
Improve language flexibility
1 parent ff39eef commit b4ed514

20 files changed

Lines changed: 205 additions & 268 deletions

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,8 @@ case class FieldAccess(rcv: Exp, field: Field)
446446
def getArgs: Seq[Exp] = Seq(rcv)
447447
def withArgs(args: Seq[Exp]): FieldAccess = copy(rcv = args.head, field)(pos, info, errT)
448448
// def asManifestation: Exp = this
449+
450+
override def name: String = field.name
449451
}
450452

451453
/** A predicate access expression. See also companion object below for an alternative creation signature */
@@ -1257,7 +1259,9 @@ sealed abstract class DomainUnExp(val funct: UnOp) extends PrettyUnaryExpression
12571259
}
12581260

12591261
/** Expressions which can appear on the left hand side of an assignment */
1260-
sealed trait Lhs extends Exp
1262+
sealed trait Lhs extends Exp {
1263+
def name: String
1264+
}
12611265

12621266
/** Generic Expression to use to extend the AST.
12631267
* 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 & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -38,48 +38,22 @@ sealed trait Stmt extends Hashable with Infoed with Positioned with Transformabl
3838
}
3939

4040
/** 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 {
41+
case class NewStmt[T <: Lhs](lhs: T, fields: Seq[Field])(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()) ++
4444
(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())
7145
}
7246

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{
47+
/** An assignment to a field or a local variable. */
48+
case class Assign[T <: Lhs](lhs: T, rhs: Exp)(val pos: Position = NoPosition, val info: Info = NoInfo, val errT: ErrorTrafo = NoTrafos) extends Stmt {
7549
override lazy val check : Seq[ConsistencyError] =
7650
(if(!Consistency.noResult(this)) Seq(ConsistencyError("Result variables are only allowed in postconditions of functions.", pos)) else Seq()) ++
7751
Consistency.checkPure(rhs) ++
7852
(if(!Consistency.isAssignable(rhs, lhs)) Seq(ConsistencyError(s"Right-hand side $rhs is not assignable to left-hand side $lhs.", lhs.pos)) else Seq())
7953
}
8054

8155
/** 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 {
56+
case class MethodCall(methodName: String, args: Seq[Exp], targets: Seq[Lhs])(val pos: Position, val info: Info, val errT: ErrorTrafo) extends Stmt {
8357
override lazy val check : Seq[ConsistencyError] = {
8458
var s = Seq.empty[ConsistencyError]
8559
if(!Consistency.noResult(this))
@@ -92,7 +66,7 @@ case class MethodCall(methodName: String, args: Seq[Exp], targets: Seq[LocalVar]
9266
}
9367

9468
object MethodCall {
95-
def apply(method: Method, args: Seq[Exp], targets: Seq[LocalVar])(pos: Position = NoPosition, info: Info = NoInfo, errT: ErrorTrafo = NoTrafos): MethodCall = {
69+
def apply(method: Method, args: Seq[Exp], targets: Seq[Lhs])(pos: Position = NoPosition, info: Info = NoInfo, errT: ErrorTrafo = NoTrafos): MethodCall = {
9670
MethodCall(method.name, args, targets)(pos, info, errT)
9771
}
9872
}

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -645,9 +645,8 @@ object FastPrettyPrinter extends FastPrettyPrinterBase with BracketPrettyPrinter
645645
def showStmt(stmt: Stmt): Cont = {
646646
val stmtDoc = stmt match {
647647
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))
648+
show(target) <+> ":=" <+> text("new") <> parens(ssep(fields map (f => value(f.name)), char(',') <> space))
649+
case Assign(lhs, rhs) => show(lhs) <+> ":=" <+> nest(defaultIndent, show(rhs))
651650
case Fold(e) => text("fold") <+> nest(defaultIndent, show(e))
652651
case Unfold(e) => text("unfold") <+> nest(defaultIndent, show(e))
653652
case Package(e, proofScript) => text("package") <+> show(e) <+> showBlock(proofScript)

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

Lines changed: 8 additions & 8 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 or access predicates.
131131
*/
132132
def checkPure(e: Exp): Seq[ConsistencyError] = {
133133
if(!e.isPure){
@@ -165,13 +165,13 @@ object Consistency {
165165
val argVars = args.map(_.localVar).toSet
166166
var s = Seq.empty[ConsistencyError]
167167

168-
for (a@LocalVarAssign(l, _) <- b if argVars.contains(l)) {
168+
for (a@Assign(l: LocalVar, _) <- b if argVars.contains(l)) {
169169
s :+= ConsistencyError(s"$a is a reassignment of formal argument $l.", a.pos)
170170
}
171-
for (c@MethodCall(_, _, targets) <- b; t <- targets if argVars.contains(t)) {
171+
for (c@MethodCall(_, _, targets) <- b; t@LocalVar(_, _) <- targets if argVars.contains(t)) {
172172
s :+= ConsistencyError(s"$c is a reassignment of formal argument $t.", c.pos)
173173
}
174-
for (n@NewStmt(l, _) <- b if argVars.contains(l)){
174+
for (n@NewStmt(l: LocalVar, _) <- b if argVars.contains(l)){
175175
s :+= ConsistencyError(s"$n is a reassignment of formal argument $l.", n.pos)
176176
}
177177
s
@@ -387,13 +387,13 @@ object Consistency {
387387

388388
private def checkMagicWandProofScript(script: Stmt, locals: Seq[LocalVarDecl]): Seq[ConsistencyError] =
389389
script.shallowCollect({
390-
case fa: FieldAssign =>
390+
case Assign(fa: FieldAccess, _) =>
391391
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))
392+
case ne: NewStmt[_] =>
393+
Some(ConsistencyError("New statements are not allowed in magic wand proof scripts.", ne.pos))
394394
case wh: While =>
395395
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) =>
396+
case loc @ Assign(LocalVar(varName, _), _) if !locals.exists(_.name == varName) =>
397397
Some(ConsistencyError("Can only assign to local variables that were declared inside the proof script.", loc.pos))
398398
case _: Package => None
399399
}).flatten

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,7 @@ object Nodes {
4949
case s: Stmt =>
5050
s match {
5151
case NewStmt(target, fields) => Seq(target) ++ fields
52-
case LocalVarAssign(lhs, rhs) => Seq(lhs, rhs)
53-
case FieldAssign(lhs, rhs) => Seq(lhs, rhs)
52+
case Assign(lhs, rhs) => Seq(lhs, rhs)
5453
case Fold(e) => Seq(e)
5554
case Unfold(e) => Seq(e)
5655
case Package(e, proofScript) => Seq(e, proofScript)

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,9 @@ 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 }
84+
case NewStmt(target: LocalVar, _) => writtenTo = target +: writtenTo
8585
case While(_, _, body) => writtenTo = writtenTo ++ (writtenVars(body) intersect s.undeclLocalVars)
8686
}
8787

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -215,16 +215,15 @@ 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 |
226+
_: NewStmt[_] |
228227
_: Assert |
229228
_: LocalVarDeclStmt |
230229
_: Assume |

src/main/scala/viper/silver/parser/FastParser.scala

Lines changed: 32 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -750,20 +750,23 @@ class FastParser {
750750
val expander = StrategyBuilder.Ancestor[PNode] {
751751

752752
// Handles macros on the left hand-side of assignments
753-
case (PMacroAssign(call, exp), ctx) =>
754-
if (!isMacro(call.opName))
755-
throw ParseException("The only calls that can be on the left-hand side of an assignment statement are calls to macros", call.pos._1)
756-
757-
val body = ExpandMacroIfValid(call, ctx)
758-
759-
// Check if macro's body can be the left-hand side of an assignment and,
760-
// if that's the case, add it in a corresponding assignment statement
761-
body match {
762-
case fa: PFieldAccess =>
763-
val node = PFieldAssign(fa, exp)(fa.pos)
764-
(node, ctx)
765-
case _ => throw ParseException("The body of this macro is not a suitable left-hand side for an assignment statement", call.pos._1)
753+
case (assign@PAssign(targets, rhs), ctx) =>
754+
val expandedTargets = targets map {
755+
case call: PCall => {
756+
if (!isMacro(call.opName))
757+
throw ParseException("The only calls that can be on the left-hand side of an assignment statement are calls to macros", call.pos._1)
758+
val body = ExpandMacroIfValid(call, ctx)
759+
760+
// Check if macro's body can be the left-hand side of an assignment and,
761+
// if that's the case, add it in a corresponding assignment statement
762+
body match {
763+
case target: PAssignTarget => target
764+
case _ => throw ParseException("The body of this macro is not a suitable left-hand side for an assignment statement", call.pos._1)
765+
}
766+
}
767+
case target => target
766768
}
769+
(PAssign(expandedTargets, rhs)(assign.pos), ctx)
767770

768771
// Handles all other calls to macros
769772
case (node, ctx) => (ExpandMacroIfValid(node, ctx), ctx)
@@ -800,7 +803,7 @@ class FastParser {
800803
| setTypedEmpty | explicitSetNonEmpty | multiSetTypedEmpty | explicitMultisetNonEmpty | seqTypedEmpty
801804
| size | explicitSeqNonEmpty | seqRange
802805
| mapTypedEmpty | explicitMapNonEmpty | mapDomain | mapRange
803-
| fapp | idnuse | ParserExtension.newExpAtEnd(ctx))
806+
| newExp | fapp | idnuse | ParserExtension.newExpAtEnd(ctx))
804807

805808
def result[$: P]: P[PResultLit] = FP(keyword("result")).map { case (pos, _) => PResultLit()(pos) }
806809

@@ -1085,6 +1088,10 @@ class FastParser {
10851088
case (pos, e) => PMapRange(e)(pos)
10861089
})
10871090

1091+
def newExp[$: P]: P[PNewExp] = FP("new" ~ "(" ~ newExpFields ~ ")").map { case (pos, fields) => PNewExp(fields)(pos) }
1092+
1093+
def newExpFields[$: P]: P[Option[Seq[PIdnUse]]] = P(idnuse.rep(sep = ",")).map(Some(_)) | P("*").map(_ => None)
1094+
10881095
def fapp[$: P]: P[PCall] = FP(idnuse ~ parens(actualArgList)).map {
10891096
case (pos, (func, args)) =>
10901097
PCall(func, args, None)(pos)
@@ -1094,23 +1101,21 @@ class FastParser {
10941101
case (pos, (func, args, typeGiven)) => PCall(func, args, Some(typeGiven))(pos)
10951102
}
10961103

1097-
def stmt(implicit ctx : P[_]) : P[PStmt] = P(ParserExtension.newStmtAtStart(ctx) | macroassign | fieldassign | localassign | fold | unfold | exhale | assertP |
1098-
inhale | assume | ifthnels | whle | varDecl | defineDecl | newstmt |
1099-
methodCall | goto | lbl | packageWand | applyWand | macroref | block |
1104+
def stmt(implicit ctx : P[_]) : P[PStmt] = P(ParserExtension.newStmtAtStart(ctx) | fold | unfold | exhale | assertP |
1105+
inhale | assume | ifthnels | whle | varDecl | defineDecl |
1106+
goto | lbl | packageWand | applyWand | assign | macroref | block |
11001107
quasihavoc | quasihavocall | ParserExtension.newStmtAtEnd(ctx))
11011108

1102-
def nodefinestmt(implicit ctx : P[_]) : P[PStmt] = P(ParserExtension.newStmtAtStart(ctx) | fieldassign | localassign | fold | unfold | exhale | assertP |
1103-
inhale | assume | ifthnels | whle | varDecl | newstmt |
1104-
methodCall | goto | lbl | packageWand | applyWand | macroref | block |
1109+
def nodefinestmt(implicit ctx : P[_]) : P[PStmt] = P(ParserExtension.newStmtAtStart(ctx) | fold | unfold | exhale | assertP |
1110+
inhale | assume | ifthnels | whle | varDecl |
1111+
goto | lbl | packageWand | applyWand | assign | macroref | block |
11051112
quasihavoc | quasihavocall | ParserExtension.newStmtAtEnd(ctx))
11061113

11071114
def macroref[$: P]: P[PMacroRef] = FP(idnuse).map { case (pos, a) => PMacroRef(a)(pos) }
11081115

1109-
def fieldassign[$: P]: P[PFieldAssign] = FP(fieldAcc ~ ":=" ~ exp).map { case (pos, (a, b)) => PFieldAssign(a, b)(pos) }
1110-
1111-
def macroassign[$: P]: P[PMacroAssign] = FP(NoCut(fapp) ~ ":=" ~ exp).map { case (pos, (call, exp)) => PMacroAssign(call, exp)(pos) }
1116+
def assignTarget[$: P]: P[PAssignTarget] = fieldAcc | NoCut(fapp) | idnuse
11121117

1113-
def localassign[$: P]: P[PVarAssign] = FP(idnuse ~ ":=" ~ exp).map { case (pos, (a, b)) => PVarAssign(a, b)(pos) }
1118+
def assign[$: P]: P[PAssign] = FP((assignTarget.rep(min = 1, sep = ",") ~ ":=").? ~ exp).map { case (pos, (targets, rhs)) => PAssign(targets.getOrElse(Seq()), rhs)(pos) }
11141119

11151120
def fold[$: P]: P[PFold] = FP("fold" ~ predicateAccessPred).map{ case (pos, e) => PFold(e)(pos)}
11161121

@@ -1170,7 +1175,9 @@ class FastParser {
11701175

11711176
def inv(implicit ctx : P[_]) : P[PExp] = P((keyword("invariant") ~ exp ~~~ ";".lw.?) | ParserExtension.invSpecification(ctx))
11721177

1173-
def varDecl[$: P]: P[PLocalVarDecl] = FP(keyword("var") ~/ idndef ~ ":" ~ typ ~~~ (":=" ~ exp).lw.?).map { case (pos, (a, b, c)) => PLocalVarDecl(a, b, c)(pos) }
1178+
def varDecl[$: P]: P[PLocalVarDecl] = FP(keyword("var") ~/ nonEmptyFormalArgList ~~~ (":=" ~ exp).lw.?).map {
1179+
case (pos, (a, b)) => PLocalVarDecl(a, b.map(i => PAssign(a.map(v => PIdnUse(v.idndef.name)(v.idndef.pos)), i)(pos)))(pos)
1180+
}
11741181

11751182
def defineDecl[$: P]: P[PDefine] = FP(keyword("define") ~/ idndef ~ ("(" ~ idndef.rep(sep = ",") ~ ")").? ~ (exp | "{" ~ (nodefinestmt ~ ";".?).rep ~ "}")).map {
11761183
case (pos, (a, b, c)) => c match {
@@ -1179,19 +1186,6 @@ class FastParser {
11791186
}
11801187
}
11811188

1182-
def newstmt[$: P]: P[PNewStmt] = starredNewstmt | regularNewstmt
1183-
1184-
def regularNewstmt[$: P]: P[PRegularNewStmt] = FP(idnuse ~ ":=" ~ "new" ~ "(" ~ idnuse.rep(sep = ",") ~ ")").map { case (pos, (a, b)) => PRegularNewStmt(a, b)(pos) }
1185-
1186-
def starredNewstmt[$: P]: P[PStarredNewStmt] = FP(idnuse ~ ":=" ~ "new" ~ "(" ~ "*" ~ ")").map{ case (pos, e) => PStarredNewStmt(e)(pos) }
1187-
1188-
def methodCall[$: P]: P[PMethodCall] = FP((idnuse.rep(sep = ",") ~ ":=").? ~ idnuse ~ parens(exp.rep(sep = ","))).map {
1189-
case (pos, (None, method, args)) =>
1190-
PMethodCall(Nil, method, args)(pos)
1191-
case (pos, (Some(targets), method, args)) =>
1192-
PMethodCall(targets, method, args)(pos)
1193-
}
1194-
11951189
def goto[$: P]: P[PGoto] = FP("goto" ~/ idnuse).map{ case (pos, e) => PGoto(e)(pos) }
11961190

11971191
def lbl[$: P]: P[PLabel] = FP(keyword("label") ~/ idndef ~~~ (keyword("invariant") ~/ exp).lw.rep).map {
@@ -1264,8 +1258,6 @@ class FastParser {
12641258

12651259
def unnamedFormalArg[$: P] = FP(typ).map{ case (pos, t) => PUnnamedFormalArgDecl(t)(pos) }
12661260

1267-
def functionSignature[$: P] = P("function" ~ idndef ~ "(" ~ formalArgList ~ ")" ~ ":" ~ typ)
1268-
12691261
def formalArgList[$: P]: P[Seq[PFormalArgDecl]] = P(formalArg.rep(sep = ","))
12701262

12711263
def axiomDecl[$: P]: P[PAxiom1] = FP(keyword("axiom") ~ idndef.? ~ "{" ~ exp ~ "}" ~~~ ";".lw.?).map { case (pos, (a, b)) => PAxiom1(a, b)(pos) }

0 commit comments

Comments
 (0)