diff --git a/examples/concepts/veymont/annotatedPermissions/swap.pvl b/examples/concepts/veymont/annotatedPermissions/swap.pvl index 4094688172..e719aab835 100644 --- a/examples/concepts/veymont/annotatedPermissions/swap.pvl +++ b/examples/concepts/veymont/annotatedPermissions/swap.pvl @@ -6,11 +6,12 @@ choreography swap(int x, int y) { endpoint a = Role(); endpoint b = Role(); - context Perm(a.v, 1) ** Perm(a.temp, 1); - context Perm(b.v, 1) ** Perm(b.temp, 1); + context Perm(a.v, 1) ** Perm(a.temp, 1) ** a.v == 0; + context Perm(b.v, 1) ** Perm(b.temp, 1) ** b.v == 1; run { a.v := x; b.v := y; + channel_invariant \sender != \receiver && \msg == \msg; communicate a.v -> b.temp; communicate b.v -> a.temp; a.v := a.temp; diff --git a/examples/concepts/veymont/generatedPermissions/TicTacToe/Player.pvl b/examples/concepts/veymont/generatedPermissions/TicTacToe/Player.pvl index ad44f5ad2c..060e32b183 100644 --- a/examples/concepts/veymont/generatedPermissions/TicTacToe/Player.pvl +++ b/examples/concepts/veymont/generatedPermissions/TicTacToe/Player.pvl @@ -1,7 +1,7 @@ class Player { int c00, c01, c02, c10, c11, c12, c20, c21, c22; int myToken; - Move move, temp; + Move move; boolean turn; @@ -25,7 +25,6 @@ class Player { myToken = tok; turn = t; move = new Move(0, 0, tok); - temp = new Move(-1, -1, -1); } diff --git a/examples/concepts/veymont/generatedPermissions/TicTacToe/TicTacToe.pvl b/examples/concepts/veymont/generatedPermissions/TicTacToe/TicTacToe.pvl index c4cfe417dc..7b539ec86c 100644 --- a/examples/concepts/veymont/generatedPermissions/TicTacToe/TicTacToe.pvl +++ b/examples/concepts/veymont/generatedPermissions/TicTacToe/TicTacToe.pvl @@ -7,24 +7,20 @@ choreography TTT() { endpoint p1 = Player(0, true); endpoint p2 = Player(1, false); - context ticTacToeAnnotations(p1, p2); + context (\chor ticTacToeAnnotations(p1, p2)); ensures p1.gameFinished() && p2.gameFinished(); run { - loop_invariant ticTacToeAnnotations(p1, p2); - loop_invariant !p1.gameFinished() == !p2.gameFinished(); + loop_invariant (\chor ticTacToeAnnotations(p1, p2)); + loop_invariant (\chor !p1.gameFinished() == !p2.gameFinished()); while(!p1.gameFinished() && !p2.gameFinished()) { // TODO (RR): Re-enable with in_chor // assert p1.turn == !p2.turn; if(p1.turn && !p2.turn) { p1.createNewMove(); - p1.temp := p1.move.clone(); - communicate p2.move <- p1.temp; - p1.temp := new Move(-1, -1, -1); // TODO (RR): Workaround for not having memory management, delete when we support exprs in communicate + communicate p2.move <- p1.move.clone(); } else { p2.createNewMove(); - p2.temp := p2.move.clone(); - communicate p1.move <- p2.temp; - p2.temp := new Move(-1, -1, -1); // TODO (RR): Workaround for not having memory management, delete when we support exprs in communicate + communicate p1.move <- p2.move.clone(); } p1.doMove(); p2.doMove(); diff --git a/examples/concepts/veymont/generatedPermissions/leaderelectring.pvl b/examples/concepts/veymont/generatedPermissions/leaderelectring.pvl index fadae5fd20..2c94d50b11 100644 --- a/examples/concepts/veymont/generatedPermissions/leaderelectring.pvl +++ b/examples/concepts/veymont/generatedPermissions/leaderelectring.pvl @@ -33,7 +33,7 @@ choreography leaderElectRing() { ensures c.maxVal == c.maxVal(a.rank,b.rank,d.rank,c.rank); ensures c.maxVal == a.rank || c.maxVal == b.rank || c.maxVal == c.rank || c.maxVal == d.rank; run { - loop_invariant a.rank == \old(a.rank) ** b.rank == \old(b.rank) ** c.rank == \old(c.rank) ** d.rank == \old(d.rank); + loop_invariant a.rank == \old(a.rank) ** b.rank == \old(b.rank) ** c.rank == \old(c.rank) ** d.rank == \old(d.rank); loop_invariant a.rank != b.rank && a.rank != c.rank && a.rank != d.rank && b.rank != c.rank && b.rank != d.rank && c.rank != d.rank; loop_invariant 0 <= c.n && c.n <= 3; loop_invariant a.n == c.n && b.n == c.n && d.n == c.n; @@ -49,7 +49,7 @@ choreography leaderElectRing() { loop_invariant c.n == 3 ==> a.maxVal == a.maxVal(a.rank,b.rank,c.rank,d.rank); loop_invariant c.n == 3 ==> b.maxVal == b.maxVal(a.rank,b.rank,c.rank,d.rank); loop_invariant c.n == 3 ==> c.maxVal == c.maxVal(a.rank,b.rank,c.rank,d.rank); - loop_invariant c.n == 3 ==> d.maxVal == d.maxVal(a.rank,b.rank,c.rank,d.rank); + loop_invariant c.n == 3 ==> d.maxVal == d.maxVal(a.rank,b.rank,c.rank,d.rank); while(a.n < 3 && b.n < 3 && c.n < 3 && d.n < 3) { communicate a.left <- d.maxVal; communicate b.left <- a.maxVal; diff --git a/examples/technical/veymont/checkMainSyntaxAndWellFormedness/WhileCondition.pvl b/examples/technical/veymont/checkMainSyntaxAndWellFormedness/WhileCondition.pvl index cf1b8bf0da..60a3294e56 100644 --- a/examples/technical/veymont/checkMainSyntaxAndWellFormedness/WhileCondition.pvl +++ b/examples/technical/veymont/checkMainSyntaxAndWellFormedness/WhileCondition.pvl @@ -1,9 +1,9 @@ choreography Main() { - endpoint a = Role(5); - endpoint b = Role(6); - endpoint c = Role(7); + endpoint a = Role(5); + endpoint b = Role(6); + endpoint c = Role(7); - requires a.x == 5 && b.x == 6 && c.x == 7; + requires a.x == 5 && b.x == 6 && c.x == 7; run { while(a.x >= 5 && b.x == 6 && c.x == 7) { c.x := -2; diff --git a/examples/technical/veymont/genericEndpoints.pvl b/examples/technical/veymont/genericEndpoints.pvl index 9341771b06..3975ae99c5 100644 --- a/examples/technical/veymont/genericEndpoints.pvl +++ b/examples/technical/veymont/genericEndpoints.pvl @@ -14,7 +14,6 @@ choreography genericTest() { endpoint a = Role(); endpoint b = Role(); - requires a != b; run { a.i := a.toInt(a.t); communicate a.i -> b.i; diff --git a/res/universal/res/veymont/genericChannel.pvl b/res/universal/res/veymont/genericChannel.pvl index a98932155a..6a074d935e 100644 --- a/res/universal/res/veymont/genericChannel.pvl +++ b/res/universal/res/veymont/genericChannel.pvl @@ -1,11 +1,11 @@ -lock_invariant Perm(transferring, 1) ** Perm(exchangeValue,1); +lock_invariant Perm(hasMsg, 1) ** Perm(exchangeValue,1); class Channel { - boolean transferring; + boolean hasMsg; T exchangeValue; ensures committed(this); constructor() { - transferring = false; + hasMsg = false; commit this; } @@ -13,14 +13,14 @@ class Channel { void writeValue(T v) { lock this; - loop_invariant Perm(transferring, 1) ** Perm(exchangeValue,1); + loop_invariant Perm(hasMsg, 1) ** Perm(exchangeValue,1); loop_invariant held(this); - while (!transferring) { + while (!hasMsg) { unlock this; lock this; } - transferring = false; + hasMsg = false; exchangeValue = v; unlock this; } @@ -29,14 +29,14 @@ class Channel { T readValue() { lock this; - loop_invariant Perm(transferring, 1) ** Perm(exchangeValue,1); + loop_invariant Perm(hasMsg, 1) ** Perm(exchangeValue,1); loop_invariant held(this); - while (transferring) { + while (hasMsg) { wait this; } T m = exchangeValue; - transferring = false; + hasMsg = false; unlock this; return m; diff --git a/src/col/vct/col/ast/Node.scala b/src/col/vct/col/ast/Node.scala index 838740745f..fc05332383 100644 --- a/src/col/vct/col/ast/Node.scala +++ b/src/col/vct/col/ast/Node.scala @@ -56,7 +56,6 @@ import vct.col.ast.family.location._ import vct.col.ast.family.loopcontract._ import vct.col.ast.family.parregion._ import vct.col.ast.family.pvlcommunicate._ -import vct.col.ast.family.seqguard._ import vct.col.ast.family.seqrun._ import vct.col.ast.family.signals._ import vct.col.ast.lang.c._ @@ -3607,11 +3606,11 @@ final case class PVLCommunicate[G]( var inferredSender: Option[PVLEndpoint[G]] = None var inferredReceiver: Option[PVLEndpoint[G]] = None } -final case class PVLChorStatement[G]( +final case class PVLEndpointStatement[G]( endpoint: Option[PVLEndpointName[G]], inner: Statement[G], )(val blame: Blame[ChorStatementFailure])(implicit val o: Origin) - extends Statement[G] with PVLChorStatementImpl[G] + extends Statement[G] with PVLEndpointStatementImpl[G] final case class PVLChorPerm[G]( endpoint: PVLEndpointName[G], loc: Location[G], @@ -3702,36 +3701,24 @@ final case class UnresolvedChorLoop[G]( with ControlContainerStatement[G] with UnresolvedChorLoopImpl[G] -final case class ChorStatement[G]( +final case class ChorStatement[G](inner: Statement[G])(implicit val o: Origin) + extends Statement[G] with ChorStatementImpl[G] +final case class EndpointStatement[G]( endpoint: Option[Ref[G, Endpoint[G]]], inner: Statement[G], )(val blame: Blame[ChorStatementFailure])(implicit val o: Origin) - extends Statement[G] with ChorStatementImpl[G] + extends Statement[G] with EndpointStatementImpl[G] -@family -sealed trait ChorGuard[G] extends NodeFamily[G] with ChorGuardImpl[G] -final case class EndpointGuard[G]( - endpoint: Ref[G, Endpoint[G]], - condition: Expr[G], +final case class PVLEndpointExpr[G]( + endpoint: PVLEndpointName[G], + expr: Expr[G], )(implicit val o: Origin) - extends ChorGuard[G] with EndpointGuardImpl[G] -final case class UnpointedGuard[G](condition: Expr[G])(implicit val o: Origin) - extends ChorGuard[G] with UnpointedGuardImpl[G] - -final case class ChorBranch[G]( - guards: Seq[ChorGuard[G]], - yes: Statement[G], - no: Option[Statement[G]], -)(val blame: Blame[SeqBranchFailure])(implicit val o: Origin) - extends Statement[G] - with ControlContainerStatement[G] - with ChorBranchImpl[G] -final case class ChorLoop[G]( - guards: Seq[ChorGuard[G]], - contract: LoopContract[G], - body: Statement[G], -)(val blame: Blame[SeqLoopFailure])(implicit val o: Origin) - extends Statement[G] with ControlContainerStatement[G] with ChorLoopImpl[G] + extends Expr[G] with PVLEndpointExprImpl[G] +final case class EndpointExpr[G](endpoint: Ref[G, Endpoint[G]], expr: Expr[G])( + implicit val o: Origin +) extends Expr[G] with EndpointExprImpl[G] +final case class ChorExpr[G](expr: Expr[G])(implicit val o: Origin) + extends Expr[G] with ChorExprImpl[G] final case class VeyMontAssignExpression[G]( endpoint: Ref[G, Endpoint[G]], diff --git a/src/col/vct/col/ast/declaration/global/ChoreographyImpl.scala b/src/col/vct/col/ast/declaration/global/ChoreographyImpl.scala index 6d0e26a1bc..4bf8fd7fc2 100644 --- a/src/col/vct/col/ast/declaration/global/ChoreographyImpl.scala +++ b/src/col/vct/col/ast/declaration/global/ChoreographyImpl.scala @@ -4,13 +4,13 @@ import vct.col.ast.declaration.DeclarationImpl import vct.col.ast.{ Assign, ChorStatement, + Choreography, Class, Declaration, Endpoint, - EndpointGuard, EndpointName, + EndpointStatement, Node, - Choreography, } import vct.col.ast.util.Declarator import vct.col.check.{CheckContext, CheckError} @@ -24,10 +24,10 @@ import vct.col.ast.ops.ChoreographyOps object ChoreographyImpl { def participants[G](node: Node[G]): ListSet[Endpoint[G]] = ListSet.from(node.collect { - case EndpointGuard(Ref(endpoint), _) => endpoint - case ChorStatement(Some(Ref(endpoint)), Assign(_, _)) => endpoint - case EndpointName(Ref(endpoint)) => endpoint - }) + case EndpointStatement(Some(Ref(endpoint)), Assign(_, _)) => Seq(endpoint) + case EndpointName(Ref(endpoint)) => Seq(endpoint) + case c @ ChorStatement(_) => c.participants.toSeq + }.flatten) } trait ChoreographyImpl[G] @@ -39,7 +39,8 @@ trait ChoreographyImpl[G] Doc.stack(Seq( contract, Group( - Text("seq_program") <+> ctx.name(this) <> "(" <> Doc.args(params) <> ")" + Text("choreography") <+> ctx.name(this) <> "(" <> Doc.args(params) <> + ")" ) <+> "{" <>> Doc.stack( endpoints ++ decls :+ preRun.map(preRun => Text("/* preRun */") <+> preRun.show) diff --git a/src/col/vct/col/ast/expr/heap/read/DerefImpl.scala b/src/col/vct/col/ast/expr/heap/read/DerefImpl.scala index 45557b7a0a..84ef4f7d4f 100644 --- a/src/col/vct/col/ast/expr/heap/read/DerefImpl.scala +++ b/src/col/vct/col/ast/expr/heap/read/DerefImpl.scala @@ -1,7 +1,15 @@ package vct.col.ast.expr.heap.read import vct.col.ast.expr.ExprImpl -import vct.col.ast.{Deref, EndpointName, Expr, TClass, Type} +import vct.col.ast.{ + Deref, + EndpointName, + Expr, + FieldLocation, + TClass, + Type, + Value, +} import vct.col.check.{Check, CheckContext, CheckError, SeqProgReceivingEndpoint} import vct.col.print.{Ctx, Doc, Group, Precedence} import vct.col.ref.Ref @@ -21,4 +29,6 @@ trait DerefImpl[G] extends ExprImpl[G] with DerefOps[G] { override def precedence: Int = Precedence.POSTFIX override def layout(implicit ctx: Ctx): Doc = assoc(obj) <> "." <> ctx.name(ref) + + def value: Value[G] = Value(FieldLocation(obj, ref)) } diff --git a/src/col/vct/col/ast/expr/resource/ValueImpl.scala b/src/col/vct/col/ast/expr/resource/ValueImpl.scala index 356b93a728..f294ffc176 100644 --- a/src/col/vct/col/ast/expr/resource/ValueImpl.scala +++ b/src/col/vct/col/ast/expr/resource/ValueImpl.scala @@ -1,6 +1,6 @@ package vct.col.ast.expr.resource -import vct.col.ast.{TResource, Type, Value} +import vct.col.ast.{Deref, FieldLocation, TResource, Type, Value} import vct.col.print.{Ctx, Doc, Group, Precedence, Text} import vct.col.ast.ops.ValueOps diff --git a/src/col/vct/col/ast/family/pvlcommunicate/PVLEndpointNameImpl.scala b/src/col/vct/col/ast/family/pvlcommunicate/PVLEndpointNameImpl.scala index 3e0e663699..1613407846 100644 --- a/src/col/vct/col/ast/family/pvlcommunicate/PVLEndpointNameImpl.scala +++ b/src/col/vct/col/ast/family/pvlcommunicate/PVLEndpointNameImpl.scala @@ -1,10 +1,16 @@ package vct.col.ast.family.pvlcommunicate import vct.col.ast.PVLEndpointName +import vct.col.ast.node.NodeFamilyImpl import vct.col.ast.ops.PVLEndpointNameOps -import vct.col.ast.ops.{PVLEndpointNameOps, PVLEndpointNameFamilyOps} +import vct.col.ast.ops.{PVLEndpointNameFamilyOps, PVLEndpointNameOps} +import vct.col.print.{Ctx, Doc, Text} trait PVLEndpointNameImpl[G] - extends PVLEndpointNameOps[G] with PVLEndpointNameFamilyOps[G] { + extends PVLEndpointNameOps[G] + with PVLEndpointNameFamilyOps[G] + with NodeFamilyImpl[G] { this: PVLEndpointName[G] => + + override def layout(implicit ctx: Ctx): Doc = Text(name) } diff --git a/src/col/vct/col/ast/family/seqguard/ChorGuardImpl.scala b/src/col/vct/col/ast/family/seqguard/ChorGuardImpl.scala deleted file mode 100644 index a40fbf67d0..0000000000 --- a/src/col/vct/col/ast/family/seqguard/ChorGuardImpl.scala +++ /dev/null @@ -1,10 +0,0 @@ -package vct.col.ast.family.seqguard - -import vct.col.ast.{Endpoint, Expr, ChorGuard, UnpointedGuard} -import vct.col.ast.ops.ChorGuardFamilyOps - -trait ChorGuardImpl[G] extends ChorGuardFamilyOps[G] { - this: ChorGuard[G] => - def condition: Expr[G] - def endpointOpt: Option[Endpoint[G]] -} diff --git a/src/col/vct/col/ast/family/seqguard/EndpointGuardImpl.scala b/src/col/vct/col/ast/family/seqguard/EndpointGuardImpl.scala deleted file mode 100644 index 6a96824e98..0000000000 --- a/src/col/vct/col/ast/family/seqguard/EndpointGuardImpl.scala +++ /dev/null @@ -1,9 +0,0 @@ -package vct.col.ast.family.seqguard - -import vct.col.ast.EndpointGuard -import vct.col.ast.ops.EndpointGuardOps - -trait EndpointGuardImpl[G] extends EndpointGuardOps[G] { - this: EndpointGuard[G] => - def endpointOpt = Some(endpoint.decl) -} diff --git a/src/col/vct/col/ast/family/seqguard/UnpointedGuardImpl.scala b/src/col/vct/col/ast/family/seqguard/UnpointedGuardImpl.scala deleted file mode 100644 index 7c89c4f7ff..0000000000 --- a/src/col/vct/col/ast/family/seqguard/UnpointedGuardImpl.scala +++ /dev/null @@ -1,10 +0,0 @@ -package vct.col.ast.family.seqguard - -import vct.col.ast.UnpointedGuard -import vct.col.ast.ops.UnpointedGuardOps -import vct.result.VerificationError.Unreachable - -trait UnpointedGuardImpl[G] extends UnpointedGuardOps[G] { - this: UnpointedGuard[G] => - def endpointOpt = None -} diff --git a/src/col/vct/col/ast/family/seqrun/ChorRunImpl.scala b/src/col/vct/col/ast/family/seqrun/ChorRunImpl.scala index efced73456..56e54f9d15 100644 --- a/src/col/vct/col/ast/family/seqrun/ChorRunImpl.scala +++ b/src/col/vct/col/ast/family/seqrun/ChorRunImpl.scala @@ -8,6 +8,6 @@ import vct.col.ast.ops.{ChorRunOps, ChorRunFamilyOps} trait ChorRunImpl[G] extends ChorRunOps[G] with ChorRunFamilyOps[G] { this: ChorRun[G] => override def layout(implicit ctx: Ctx): Doc = { - contract.show Text("seq_run") <+> this.body.show + contract.show Text("run") <+> this.body.show } } diff --git a/src/col/vct/col/ast/statement/StatementImpl.scala b/src/col/vct/col/ast/statement/StatementImpl.scala index 45cede57fe..b6d45545d1 100644 --- a/src/col/vct/col/ast/statement/StatementImpl.scala +++ b/src/col/vct/col/ast/statement/StatementImpl.scala @@ -2,7 +2,7 @@ package vct.col.ast.statement import vct.col.ast.node.NodeFamilyImpl import vct.col.ast._ -import vct.col.check.{CheckContext, CheckError, SeqProgStatement} +import vct.col.check.{CheckContext, CheckError, ChorStatement} import vct.col.print._ import vct.col.ast.ops.StatementFamilyOps diff --git a/src/col/vct/col/ast/statement/veymont/ChorBranchImpl.scala b/src/col/vct/col/ast/statement/veymont/ChorBranchImpl.scala deleted file mode 100644 index a7b8f1b3f4..0000000000 --- a/src/col/vct/col/ast/statement/veymont/ChorBranchImpl.scala +++ /dev/null @@ -1,68 +0,0 @@ -package vct.col.ast.statement.veymont - -import vct.col.ast.{ - Assign, - ChorStatement, - Communicate, - Endpoint, - EndpointGuard, - EndpointName, - ChorBranch, - UnpointedGuard, -} -import vct.col.ast.statement.StatementImpl -import vct.col.check.{CheckContext, CheckError, SeqProgParticipant} -import vct.col.ref.Ref - -import scala.collection.immutable.ListSet -import vct.col.ast.ops.ChorBranchOps - -trait ChorBranchImpl[G] extends StatementImpl[G] with ChorBranchOps[G] { - this: ChorBranch[G] => - assert(guards.nonEmpty) - - def hasUnpointed: Boolean = - guards.exists { case _: UnpointedGuard[G] => true; case _ => false } - def explicitParticipants: Seq[Endpoint[G]] = - guards.collect { case EndpointGuard(Ref(endpoint), condition) => endpoint } - - override def enterCheckContextCurrentParticipatingEndpoints( - context: CheckContext[G] - ): Option[Set[Endpoint[G]]] = { - // Assume SeqProg sets participatingEndpoints - assert(context.currentParticipatingEndpoints.isDefined) - - if (hasUnpointed) { - // Everyone that is participating keeps participating, as well as any endpoints explicitly mentioned - context.appendCurrentParticipatingEndpoints(explicitParticipants) - } else { - // We can refine the set of participants down to the set of endpoints actually present in the guard - context.withCurrentParticipatingEndpoints(explicitParticipants) - } - } - - override def check(context: CheckContext[G]): Seq[CheckError] = - super.check(context) ++ { - // Assume SeqProg sets participatingEndpoints - assert(context.currentParticipatingEndpoints.isDefined) - - // Ensure the set of participants is at most refined - if ( - Set.from(explicitParticipants) - .subsetOf(context.currentParticipatingEndpoints.get) - ) { Seq() } - else { - // There are participants in this if that have been excluded from participation: error - Seq(SeqProgParticipant(this)) - } - } - - // All participants that concretely participate in the branch by being named explicitly through the condition, - // an assignment, or a communicate. - def participants: Set[Endpoint[G]] = - ListSet.from(collect { - case comm: Communicate[G] => comm.participants - case ChorStatement(Some(Ref(endpoint)), Assign(_, _)) => Seq(endpoint) - case branch: ChorBranch[G] => branch.explicitParticipants - }.flatten) -} diff --git a/src/col/vct/col/ast/statement/veymont/ChorLoopImpl.scala b/src/col/vct/col/ast/statement/veymont/ChorLoopImpl.scala deleted file mode 100644 index fdb88069bb..0000000000 --- a/src/col/vct/col/ast/statement/veymont/ChorLoopImpl.scala +++ /dev/null @@ -1,65 +0,0 @@ -package vct.col.ast.statement.veymont - -import vct.col.ast.statement.StatementImpl -import vct.col.ast.{ - Assign, - ChorStatement, - Communicate, - Endpoint, - EndpointGuard, - EndpointName, - ChorBranch, - ChorLoop, - UnpointedGuard, -} -import vct.col.check.{CheckContext, CheckError, SeqProgParticipant} -import vct.col.ref.Ref - -import scala.collection.immutable.ListSet -import vct.col.ast.ops.ChorLoopOps - -trait ChorLoopImpl[G] extends StatementImpl[G] with ChorLoopOps[G] { - this: ChorLoop[G] => - def hasUnpointed: Boolean = - guards.exists { case _: UnpointedGuard[G] => true; case _ => false } - def explicitParticipants: Seq[Endpoint[G]] = - guards.collect { case EndpointGuard(Ref(endpoint), condition) => endpoint } - - override def enterCheckContextCurrentParticipatingEndpoints( - context: CheckContext[G] - ): Option[Set[Endpoint[G]]] = { - // Assume SeqProg sets participatingEndpoints - assert(context.currentParticipatingEndpoints.isDefined) - - if (hasUnpointed) { - // Everyone that is participating keeps participating, as well as any endpoints explicitly mentioned - context.appendCurrentParticipatingEndpoints(explicitParticipants) - } else { - // We can refine the set of participants down to the set of endpoints actually present in the guard - context.withCurrentParticipatingEndpoints(explicitParticipants) - } - } - - override def check(context: CheckContext[G]): Seq[CheckError] = - super.check(context) ++ { - // Assume SeqProg sets participatingEndpoints - assert(context.currentParticipatingEndpoints.isDefined) - - // Ensure the set of participants is at most refined - if ( - Set.from(explicitParticipants) - .subsetOf(context.currentParticipatingEndpoints.get) - ) { Seq() } - else { - // There are participants in this if that have been excluded from participation: error - Seq(SeqProgParticipant(this)) - } - } - - def participants: Set[Endpoint[G]] = - ListSet.from(collect { - case comm: Communicate[G] => comm.participants - case ChorStatement(Some(Ref(endpoint)), Assign(_, _)) => Seq(endpoint) - case branch: ChorBranch[G] => branch.explicitParticipants - }.flatten) -} diff --git a/src/col/vct/col/ast/statement/veymont/CommunicateImpl.scala b/src/col/vct/col/ast/statement/veymont/CommunicateImpl.scala index 5a7bd590a5..7aa27a5bde 100644 --- a/src/col/vct/col/ast/statement/veymont/CommunicateImpl.scala +++ b/src/col/vct/col/ast/statement/veymont/CommunicateImpl.scala @@ -9,7 +9,7 @@ import vct.col.ast.ops.{CommunicateOps, CommunicateFamilyOps} trait CommunicateImpl[G] extends CommunicateOps[G] with CommunicateFamilyOps[G] { - this: Communicate[G] => + comm: Communicate[G] => override def layout(implicit ctx: Ctx): Doc = Group( Text("communicate") <+> layoutParticipant(receiver) <> target.show <+> @@ -37,4 +37,9 @@ trait CommunicateImpl[G] def participants: Seq[Endpoint[G]] = (sender.toSeq ++ receiver.toSeq) .map(_.decl) + + object t { + def sender = comm.sender.get.decl.t + def receiver = comm.receiver.get.decl.t + } } diff --git a/src/col/vct/col/ast/statement/veymont/ChorStatementImpl.scala b/src/col/vct/col/ast/statement/veymont/EndpointStatementImpl.scala similarity index 75% rename from src/col/vct/col/ast/statement/veymont/ChorStatementImpl.scala rename to src/col/vct/col/ast/statement/veymont/EndpointStatementImpl.scala index ba9286da0e..e5e444ac07 100644 --- a/src/col/vct/col/ast/statement/veymont/ChorStatementImpl.scala +++ b/src/col/vct/col/ast/statement/veymont/EndpointStatementImpl.scala @@ -6,15 +6,13 @@ import vct.col.ast.{ Assume, Block, Branch, - ChorBranch, - ChorLoop, - ChorStatement, Communicate, CommunicateStatement, CommunicateX, Deref, Endpoint, EndpointName, + EndpointStatement, Eval, Expr, Loop, @@ -25,7 +23,7 @@ import vct.col.ast.{ UnresolvedChorLoop, VeyMontAssignExpression, } -import vct.col.ast.ops.ChorStatementOps +import vct.col.ast.ops.EndpointStatementOps import vct.col.ast.statement.StatementImpl import vct.col.check.{ CheckContext, @@ -33,36 +31,31 @@ import vct.col.check.{ SeqProgInvocation, SeqProgNoParticipant, SeqProgParticipant, - SeqProgStatement, + ChorStatement, } import vct.col.print.{Ctx, Doc, Line, Text} import vct.col.ref.Ref -trait ChorStatementImpl[G] extends ChorStatementOps[G] with StatementImpl[G] { - this: ChorStatement[G] => +trait EndpointStatementImpl[G] + extends EndpointStatementOps[G] with StatementImpl[G] { + this: EndpointStatement[G] => assert(wellformed) def wellformed: Boolean = inner match { - // ChorStatement should not be nested, and is transparent w.r.t some statements. - // Proper encoding & filtering of this should happen in LangVeymontToCol - case _: Block[G] | _: Scope[G] | _: Branch[G] | _: Loop[G] | - _: ChorStatement[G] => - false - // Communicate consists of two statements (send & receive) for which each should easily resolve to an endpoint, - // so it also shouldn't be put in a chorstmt - case _: CommunicateStatement[G] => false - case _ => true + // There are only a few statements where we fully define how projection works - for now + case _: Assign[G] | _: Assert[G] | _: Eval[G] => true + case _ => false } override def layout(implicit ctx: Ctx): Doc = (endpoint match { - case Some(Ref(endpoint)) => Text(ctx.name(endpoint)) <> ":" <> " " - case None => Text("/* unlabeled choreographic statement */") <> Line + case Some(Ref(endpoint)) => Text(s"/* ${ctx.name(endpoint)}: */ ") + case None => Text("/* unlabeled endpoint statement */") <> Line }) <> inner object eval { def enterCheckContextCurrentReceiverEndpoint( - chorStmt: ChorStatement[G], + chorStmt: EndpointStatement[G], node: Eval[G], context: CheckContext[G], ): Option[Endpoint[G]] = @@ -79,7 +72,7 @@ trait ChorStatementImpl[G] extends ChorStatementOps[G] with StatementImpl[G] { } def check( - chorStmt: ChorStatement[G], + chorStmt: EndpointStatement[G], node: Eval[G], context: CheckContext[G], ): Seq[CheckError] = @@ -107,18 +100,18 @@ trait ChorStatementImpl[G] extends ChorStatementOps[G] with StatementImpl[G] { object assign { def receiver( - chorStmt: ChorStatement[G], + chorStmt: EndpointStatement[G], node: Assign[G], ): Option[Endpoint[G]] = chorStmt.endpoint.map(_.decl) def enterCheckContextCurrentReceiverEndpoint( - chorStmt: ChorStatement[G], + chorStmt: EndpointStatement[G], node: Assign[G], context: CheckContext[G], ): Option[Endpoint[G]] = receiver(chorStmt, node) def check( - chorStmt: ChorStatement[G], + chorStmt: EndpointStatement[G], node: Assign[G], context: CheckContext[G], ): Seq[CheckError] = { @@ -151,8 +144,8 @@ trait ChorStatementImpl[G] extends ChorStatementOps[G] with StatementImpl[G] { _: VeyMontAssignExpression[G] | _: Branch[G] | _: Loop[G] | _: Scope[G] | _: Block[G] | _: Assert[G] | _: Assume[G] | _: UnresolvedChorBranch[G] | _: UnresolvedChorLoop[G] | - _: ChorBranch[G] | _: ChorLoop[G] | _: ChorStatement[G] => + _: EndpointStatement[G] => Seq() - case _ => Seq(SeqProgStatement(this)) + case _ => Seq(ChorStatement(this)) }) } diff --git a/src/col/vct/col/ast/unsorted/ChorExprImpl.scala b/src/col/vct/col/ast/unsorted/ChorExprImpl.scala new file mode 100644 index 0000000000..8bec7bf14e --- /dev/null +++ b/src/col/vct/col/ast/unsorted/ChorExprImpl.scala @@ -0,0 +1,12 @@ +package vct.col.ast.unsorted + +import vct.col.ast.{ChorExpr, TBool, Type} +import vct.col.ast.ops.ChorExprOps +import vct.col.print._ + +trait ChorExprImpl[G] extends ChorExprOps[G] { + this: ChorExpr[G] => + override def layout(implicit ctx: Ctx): Doc = Text("(\\chor") <+> expr <> ")" + + override def t: Type[G] = expr.t +} diff --git a/src/col/vct/col/ast/unsorted/ChorStatementImpl.scala b/src/col/vct/col/ast/unsorted/ChorStatementImpl.scala new file mode 100644 index 0000000000..ebf24036d0 --- /dev/null +++ b/src/col/vct/col/ast/unsorted/ChorStatementImpl.scala @@ -0,0 +1,103 @@ +package vct.col.ast.unsorted + +import vct.col.ast.ops.ChorStatementOps +import vct.col.ast.statement.StatementImpl +import vct.col.ast.{ + Assign, + Branch, + ChorStatement, + Communicate, + Endpoint, + EndpointExpr, + EndpointStatement, + Expr, + Loop, + Statement, +} +import vct.col.check.{CheckContext, CheckError, SeqProgParticipant} +import vct.col.print._ +import vct.col.ref.Ref +import vct.col.util.AstBuildHelpers + +import scala.collection.immutable.ListSet +import scala.util.Try + +trait ChorStatementImpl[G] extends ChorStatementOps[G] with StatementImpl[G] { + this: ChorStatement[G] => + override def layout(implicit ctx: Ctx): Doc = + Text("/* choreographic statement */") <+/> inner + + def cond: Expr[G] = + inner match { + case branch: Branch[G] => branch.branches.head._1 + case loop: Loop[G] => loop.cond + } + + def guards: Seq[Expr[G]] = AstBuildHelpers.unfoldStar(cond) + + def explicitEndpoints: Seq[Endpoint[G]] = + guards.collect { case EndpointExpr(Ref(endpoint), _) => endpoint } + + def hasUnpointed: Boolean = + guards.exists { + case _: EndpointExpr[G] => false + case _ => true + } + + object branch { + def apply(): Branch[G] = inner.asInstanceOf[Branch[G]] + + def yes: Statement[G] = branch().branches.head._2 + def no: Option[Statement[G]] = Try(branch().branches(1)).toOption.map(_._2) + } + + object loop { + def apply(): Loop[G] = inner.asInstanceOf[Loop[G]] + } + + def branches: Boolean = + inner match { + case _: Branch[G] | _: Loop[G] => true + case _ => false + } + + def participants: Set[Endpoint[G]] = + ListSet.from(collect { + case comm: Communicate[G] => comm.participants + case EndpointStatement(Some(Ref(endpoint)), Assign(_, _)) => Seq(endpoint) + case c @ ChorStatement(_) => c.explicitEndpoints + }.flatten) + + // There are only a few statements where we fully define how projection works - for now + def allowedInner: Option[CheckError] = + Option.unless(branches)(vct.col.check.ChorStatement(this)) + + def participantCheck(context: CheckContext[G]): Option[CheckError] = + // There are participants in this if that have been excluded from participation: error + Option.unless( + Set.from(explicitEndpoints) + .subsetOf(context.currentParticipatingEndpoints.get) + )(SeqProgParticipant(this)) + + override def check(context: CheckContext[G]): Seq[CheckError] = { + assert(context.currentParticipatingEndpoints.isDefined) + super.check(context) ++ allowedInner.toSeq ++ + Option.when(branches)(participantCheck(context)).flatten.toSeq + } + + override def enterCheckContextCurrentParticipatingEndpoints( + context: CheckContext[G] + ): Option[Set[Endpoint[G]]] = + if (branches) { + // Assume SeqProg sets participatingEndpoints + assert(context.currentParticipatingEndpoints.isDefined) + + if (hasUnpointed) { + // Everyone that is participating keeps participating, as well as any endpoints explicitly mentioned + context.appendCurrentParticipatingEndpoints(explicitEndpoints) + } else { + // We can refine the set of participants down to the set of endpoints actually present in the guard + context.withCurrentParticipatingEndpoints(explicitEndpoints) + } + } else { context.currentParticipatingEndpoints } +} diff --git a/src/col/vct/col/ast/unsorted/EndpointExprImpl.scala b/src/col/vct/col/ast/unsorted/EndpointExprImpl.scala new file mode 100644 index 0000000000..1c404a1897 --- /dev/null +++ b/src/col/vct/col/ast/unsorted/EndpointExprImpl.scala @@ -0,0 +1,13 @@ +package vct.col.ast.unsorted + +import vct.col.ast.{EndpointExpr, TBool, Type} +import vct.col.ast.ops.EndpointExprOps +import vct.col.print._ + +trait EndpointExprImpl[G] extends EndpointExprOps[G] { + this: EndpointExpr[G] => + override def layout(implicit ctx: Ctx): Doc = + Text("(\\[") <> ctx.name(endpoint) <> "]" <+> expr <> ")" + + override def t: Type[G] = expr.t +} diff --git a/src/col/vct/col/ast/unsorted/PVLEndpointExprImpl.scala b/src/col/vct/col/ast/unsorted/PVLEndpointExprImpl.scala new file mode 100644 index 0000000000..d560c92469 --- /dev/null +++ b/src/col/vct/col/ast/unsorted/PVLEndpointExprImpl.scala @@ -0,0 +1,13 @@ +package vct.col.ast.unsorted + +import vct.col.ast.{PVLEndpointExpr, Type} +import vct.col.ast.ops.PVLEndpointExprOps +import vct.col.print._ + +trait PVLEndpointExprImpl[G] extends PVLEndpointExprOps[G] { + this: PVLEndpointExpr[G] => + override def layout(implicit ctx: Ctx): Doc = + Text("(\\[") <> endpoint <> "]" <+> expr <> ")" + + def t: Type[G] = expr.t +} diff --git a/src/col/vct/col/ast/unsorted/PVLChorStatementImpl.scala b/src/col/vct/col/ast/unsorted/PVLEndpointStatementImpl.scala similarity index 83% rename from src/col/vct/col/ast/unsorted/PVLChorStatementImpl.scala rename to src/col/vct/col/ast/unsorted/PVLEndpointStatementImpl.scala index fde3de6ec1..5baf2f8557 100644 --- a/src/col/vct/col/ast/unsorted/PVLChorStatementImpl.scala +++ b/src/col/vct/col/ast/unsorted/PVLEndpointStatementImpl.scala @@ -2,21 +2,21 @@ package vct.col.ast.unsorted import vct.col.ast.{ Assign, - ChorStatement, + EndpointStatement, Expr, - PVLChorStatement, PVLDeref, PVLEndpoint, + PVLEndpointStatement, PVLLocal, } -import vct.col.ast.ops.PVLChorStatementOps +import vct.col.ast.ops.{PVLEndpointStatementOps} import vct.col.check.{CheckContext, CheckError, PVLSeqAssignEndpoint} import vct.col.print._ import vct.col.resolve.ctx.RefPVLEndpoint -trait PVLChorStatementImpl[G] extends PVLChorStatementOps[G] { - this: PVLChorStatement[G] => - assert(!inner.isInstanceOf[ChorStatement[_]]) +trait PVLEndpointStatementImpl[G] extends PVLEndpointStatementOps[G] { + this: PVLEndpointStatement[G] => + assert(!inner.isInstanceOf[EndpointStatement[_]]) // override def layout(implicit ctx: Ctx): Doc = ??? diff --git a/src/col/vct/col/check/Check.scala b/src/col/vct/col/check/Check.scala index fbd7accf79..023d37aa63 100644 --- a/src/col/vct/col/check/Check.scala +++ b/src/col/vct/col/check/Check.scala @@ -106,8 +106,8 @@ sealed trait CheckError { context(a) -> "This dereference does not take place on one of the endpoints in the surrounding `seq_prog`." ) - case SeqProgStatement(s) => - Seq(context(s) -> "This statement is not allowed in `seq_prog`.") + case ChorStatement(s) => + Seq(context(s) -> "This statement is not allowed in `choreography`.") case SeqProgInstanceMethodArgs(m) => Seq( context(m) -> @@ -214,7 +214,7 @@ case class ReturnOutsideMethod(ret: Return[_]) extends CheckError { case class FinalPermission(loc: FieldLocation[_]) extends CheckError { override def subcode: String = "finalPerm" } -case class PVLSeqAssignEndpoint(assign: PVLChorStatement[_]) +case class PVLSeqAssignEndpoint(assign: PVLEndpointStatement[_]) extends CheckError { val subcode = "pvlSeqAssignEndpoint" } @@ -228,8 +228,8 @@ case class SeqProgInstanceMethodArgs(m: InstanceMethod[_]) extends CheckError { case class SeqProgInstanceMethodBody(m: InstanceMethod[_]) extends CheckError { val subcode = "seqProgInstanceMethodBody" } -case class SeqProgStatement(s: Statement[_]) extends CheckError { - val subcode = "seqProgStatement" +case class ChorStatement(s: Statement[_]) extends CheckError { + val subcode = "chorStatement" } case class SeqProgInvocation(s: Statement[_]) extends CheckError { val subcode = "seqProgInvocation" diff --git a/src/col/vct/col/origin/Blame.scala b/src/col/vct/col/origin/Blame.scala index 068aa2f893..0ba83a7534 100644 --- a/src/col/vct/col/origin/Blame.scala +++ b/src/col/vct/col/origin/Blame.scala @@ -571,7 +571,7 @@ case class LoopUnanimityNotMaintained(guard1: Node[_], guard2: Node[_]) sealed trait ChorStatementFailure extends VerificationFailure sealed trait ChorAssignFailure extends ChorStatementFailure -case class SeqAssignInsufficientPermission(node: ChorStatement[_]) +case class SeqAssignInsufficientPermission(node: EndpointStatement[_]) extends ChorAssignFailure with NodeVerificationFailure { override def code: String = "seqAssignPerm" override def descInContext: String = diff --git a/src/col/vct/col/typerules/CoercingRewriter.scala b/src/col/vct/col/typerules/CoercingRewriter.scala index baf88034cf..f3fe42febd 100644 --- a/src/col/vct/col/typerules/CoercingRewriter.scala +++ b/src/col/vct/col/typerules/CoercingRewriter.scala @@ -354,7 +354,6 @@ abstract class CoercingRewriter[Pre <: Generation]() case node: ProverLanguage[Pre] => node case node: SmtlibFunctionSymbol[Pre] => node case node: ChorRun[Pre] => node - case node: ChorGuard[Pre] => coerce(node) case node: PVLEndpointName[Pre] => coerce(node) case node: EndpointName[Pre] => coerce(node) } @@ -663,7 +662,7 @@ abstract class CoercingRewriter[Pre <: Generation]() if (inv.ref.decl.args.length != inv.args.length) { throw IncoercibleExplanation( inv, - s"expected ${inv.ref.decl.typeArgs.length} type arguments, got ${inv.typeArgs.length} type arguments", + s"expected ${inv.ref.decl.args.length} arguments, got ${inv.args.length} arguments", ) } inv @@ -2134,6 +2133,9 @@ abstract class CoercingRewriter[Pre <: Generation]() case Sender(_) => e case Receiver(_) => e case Message(_) => e + case PVLEndpointExpr(endpoint, expr) => e + case EndpointExpr(ref, expr) => e + case ChorExpr(expr) => e } } @@ -2291,11 +2293,10 @@ abstract class CoercingRewriter[Pre <: Generation]() s"The message should have type ${target.t}, but actually has type ${msg.t}.", ) case PVLChannelInvariant(comm, inv) => PVLChannelInvariant(comm, res(inv)) - case s: PVLChorStatement[Pre] => s - case s: ChorBranch[Pre] => s - case s: ChorLoop[Pre] => s - case c: ChorStatement[Pre] => c + case s: PVLEndpointStatement[Pre] => s + case c: EndpointStatement[Pre] => c case c: CommunicateStatement[Pre] => c + case ChorStatement(inner) => ChorStatement(inner) case branch @ UnresolvedChorBranch(branches) => UnresolvedChorBranch(branches.map { case (cond, effect) => (bool(cond), effect) @@ -2929,12 +2930,6 @@ abstract class CoercingRewriter[Pre <: Generation]() def coerce(node: SmtlibFunctionSymbol[Pre]): SmtlibFunctionSymbol[Pre] = node def coerce(node: ChorRun[Pre]): ChorRun[Pre] = node - def coerce(node: ChorGuard[Pre]): ChorGuard[Pre] = - node match { - case EndpointGuard(endpoint, cond) => - EndpointGuard(endpoint, bool(cond))(node.o) - case UnpointedGuard(cond) => UnpointedGuard(bool(cond))(node.o) - } def coerce(node: PVLEndpointName[Pre]): PVLEndpointName[Pre] = node def coerce(node: EndpointName[Pre]): EndpointName[Pre] = node diff --git a/src/col/vct/col/util/AstBuildHelpers.scala b/src/col/vct/col/util/AstBuildHelpers.scala index 0618c8caaa..24894abb69 100644 --- a/src/col/vct/col/util/AstBuildHelpers.scala +++ b/src/col/vct/col/util/AstBuildHelpers.scala @@ -97,6 +97,17 @@ object AstBuildHelpers { index: Expr[G] )(implicit blame: Blame[SeqBoundFailure], origin: Origin): SeqSubscript[G] = SeqSubscript(left, index)(blame) + + def accounted(implicit o: Origin): UnitAccountedPredicate[G] = + UnitAccountedPredicate(left) + } + + implicit class AccountedBuildHelpers[G](left: AccountedPredicate[G]) { + def &*(right: Expr[G])(implicit o: Origin): SplitAccountedPredicate[G] = + SplitAccountedPredicate(left, right.accounted) + def &*(right: AccountedPredicate[G])( + implicit o: Origin + ): SplitAccountedPredicate[G] = SplitAccountedPredicate(left, right) } implicit class VarBuildHelpers[G](left: Variable[G]) { @@ -778,6 +789,10 @@ object AstBuildHelpers { amount: Expr[G], )(implicit o: Origin): Perm[G] = Perm(FieldLocation(obj, field), amount) + def value[G](obj: Expr[G], ref: Ref[G, InstanceField[G]])( + implicit o: Origin + ): Value[G] = Value(FieldLocation(obj, ref)) + def arrayPerm[G]( arr: Expr[G], index: Expr[G], @@ -816,6 +831,13 @@ object AstBuildHelpers { ) } + implicit class AccountedRewriteHelpers[Pre, Post]( + f: Expr[Pre] => Expr[Post] + ) { + def accounted: AccountedPredicate[Pre] => AccountedPredicate[Post] = + p => mapPredicate(p, f) + } + def unfoldImplies[G](expr: Expr[G]): (Seq[Expr[G]], Expr[G]) = expr match { case Implies(left, right) => @@ -874,6 +896,13 @@ object AstBuildHelpers { def foldAnd[G](exprs: Seq[Expr[G]])(implicit o: Origin): Expr[G] = exprs.reduceOption(And(_, _)).getOrElse(tt) + def foldAny[G](t: Type[_])(exprs: Seq[Expr[G]])(implicit o: Origin): Expr[G] = + t match { + case TBool() => foldAnd(exprs) + case TResource() => foldStar(exprs) + case _ => ??? + } + def loop[G]( cond: Expr[G], body: Statement[G], @@ -889,4 +918,17 @@ object AstBuildHelpers { body = body, update = Option(update).getOrElse(Block(Seq())), ) + + def instanceField[G](t: Type[G], isFinal: Boolean = false)( + implicit o: Origin + ): InstanceField[G] = + new InstanceField( + t, + if (isFinal) + Seq(Final()) + else + Seq(), + ) + + def skip[G](implicit o: Origin): Block[G] = Block(Seq()) } diff --git a/src/main/vct/main/stages/Output.scala b/src/main/vct/main/stages/Output.scala index 9b8e435b42..734360cb26 100644 --- a/src/main/vct/main/stages/Output.scala +++ b/src/main/vct/main/stages/Output.scala @@ -69,13 +69,13 @@ case class Output(out: Option[Path], syntax: Ctx.Syntax, splitDecls: Boolean) val fileName = s"${name}.${extension(syntax)}" val buf = new StringBuffer() decl.write(buf)(ctx) - LiteralReadable(buf.toString, fileName) + LiteralReadable(fileName, buf.toString) } } else { val buf = new StringBuffer() in.write(buf)(ctx) val path = s"unknown.${extension(syntax)}" - Seq(LiteralReadable(buf.toString, path)) + Seq(LiteralReadable(path, buf.toString)) } (out, txts) match { diff --git a/src/main/vct/main/stages/Transformation.scala b/src/main/vct/main/stages/Transformation.scala index 36fab123a0..a9d1230ccd 100644 --- a/src/main/vct/main/stages/Transformation.scala +++ b/src/main/vct/main/stages/Transformation.scala @@ -39,20 +39,22 @@ import vct.rewrite.{ import vct.rewrite.lang.ReplaceSYCLTypes import vct.rewrite.veymont.{ DeduplicateChorGuards, + DropChorExpr, EncodeChannels, EncodeChorBranchUnanimity, EncodeChoreography, - EncodeChoreographyParameters, EncodeEndpointInequalities, - EncodeUnpointedGuard, + StratifyUnpointedExpressions, GenerateChoreographyPermissions, GenerateImplementation, InferEndpointContexts, SpecializeEndpointClasses, - SplitChorGuards, + StratifyExpressions, } import java.nio.file.Path +import java.nio.file.Files +import java.nio.file.Paths object Transformation extends LazyLogging { case class TransformationCheckError( @@ -86,7 +88,8 @@ object Transformation extends LazyLogging { out: Path, stageKey: String, ): PassEventHandler = { - out.toFile.mkdirs() + Files.createDirectories(out) + Files.list(out).filter(_.endsWith(".col")).forEach(Files.delete(_)) (passes, event, pass, program) => { val i = passes.map(_.key).indexOf(pass) * 2 + @@ -314,8 +317,8 @@ case class SilverTransformation( EncodeRangedFor, // VeyMont sequential program encoding - SplitChorGuards, - EncodeUnpointedGuard, + StratifyExpressions, + StratifyUnpointedExpressions, DeduplicateChorGuards, InferEndpointContexts, GenerateChoreographyPermissions.withArg(veymontGeneratePermissions), @@ -439,13 +442,13 @@ case class VeyMontImplementationGeneration( ) extends Transformation( onPassEvent, Seq( - SplitChorGuards, - EncodeUnpointedGuard, + DropChorExpr, + StratifyExpressions, + StratifyUnpointedExpressions, DeduplicateChorGuards, SpecializeEndpointClasses, - InferEndpointContexts, EncodeChannels.withArg(importer), -// EncodeChoreographyParameters, + InferEndpointContexts, GenerateImplementation, PrettifyBlocks, ), diff --git a/src/parsers/antlr4/LangPVLLexer.g4 b/src/parsers/antlr4/LangPVLLexer.g4 index 4bd41d3d30..76283acb6b 100644 --- a/src/parsers/antlr4/LangPVLLexer.g4 +++ b/src/parsers/antlr4/LangPVLLexer.g4 @@ -83,6 +83,8 @@ COMMUNICATE: 'communicate'; SENDER: '\\sender'; RECEIVER: '\\receiver'; MESSAGE: '\\msg'; +BENDPOINT: '\\endpoint'; +BCHOR: '\\chor'; THIS: 'this'; NULL: 'null'; diff --git a/src/parsers/antlr4/LangPVLParser.g4 b/src/parsers/antlr4/LangPVLParser.g4 index cbf9a06ae2..009a6ed10e 100644 --- a/src/parsers/antlr4/LangPVLParser.g4 +++ b/src/parsers/antlr4/LangPVLParser.g4 @@ -151,6 +151,10 @@ postfixExpr unit : valExpr # pvlValExpr | 'Perm' '[' identifier ']' '(' expr ',' expr ')' # pvlChorPerm + | '(' '\\endpoint' identifier ';' expr ')' # pvlLongEndpointExpr + | '(' '\\' '[' identifier ']' expr ')' # pvlShortEndpointExpr + | '(' '\\chor' expr ')' # pvlLongChorExpr + | '(' '\\' '[' ']' expr ')' # pvlShortChorExpr | 'this' # pvlThis | 'null' # pvlNull | '\\sender' # pvlSender diff --git a/src/parsers/vct/parsers/transform/PVLToCol.scala b/src/parsers/vct/parsers/transform/PVLToCol.scala index 5e9d424fa6..235032683e 100644 --- a/src/parsers/vct/parsers/transform/PVLToCol.scala +++ b/src/parsers/vct/parsers/transform/PVLToCol.scala @@ -460,6 +460,18 @@ case class PVLToCol[G]( AmbiguousLocation(convert(loc))(blame(expr)), convert(perm), ) + case PvlLongEndpointExpr(_, _, endpoint, _, inner, _) => + PVLEndpointExpr( + PVLEndpointName(convert(endpoint))(origin(endpoint)), + convert(inner), + ) + case PvlShortEndpointExpr(_, _, _, endpoint, _, inner, _) => + PVLEndpointExpr( + PVLEndpointName(convert(endpoint))(origin(endpoint)), + convert(inner), + ) + case PvlLongChorExpr(_, _, inner, _) => ChorExpr(convert(inner)) + case PvlShortChorExpr(_, _, _, _, inner, _) => ChorExpr(convert(inner)) case PvlSender(_) => PVLSender() case PvlReceiver(_) => PVLReceiver() case PvlMessage(_) => PVLMessage() @@ -663,7 +675,7 @@ case class PVLToCol[G]( case PvlAssign(target, _, value) => Assign(convert(target), convert(value))(blame(stat)) case PvlSeqAssign(participant, receiver, _, _, value) => - PVLChorStatement( + PVLEndpointStatement( participant.map(convertParticipant(_)), Assign(convert(receiver), convert(value))(blame(stat)), )(blame(stat)) diff --git a/src/rewrite/vct/rewrite/ResolveExpressionSideEffects.scala b/src/rewrite/vct/rewrite/ResolveExpressionSideEffects.scala index 0b344f2a41..584f634c15 100644 --- a/src/rewrite/vct/rewrite/ResolveExpressionSideEffects.scala +++ b/src/rewrite/vct/rewrite/ResolveExpressionSideEffects.scala @@ -466,8 +466,6 @@ case class ResolveExpressionSideEffects[Pre <: Generation]() case _: PVLLoop[Pre] => throw ExtraNode case _: UnresolvedChorBranch[Pre] => throw ExtraNode case _: UnresolvedChorLoop[Pre] => throw ExtraNode - case _: ChorBranch[Pre] => throw ExtraNode - case _: ChorLoop[Pre] => throw ExtraNode case _: CStatement[Pre] => throw ExtraNode case _: CPPStatement[Pre] => throw ExtraNode case _: JavaStatement[Pre] => throw ExtraNode diff --git a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala index dde1a2bdaa..3b36024824 100644 --- a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala +++ b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala @@ -153,16 +153,6 @@ case class CFGGenerator[G]() extends LazyLogging { CFGEdge(convert(b._1, context.enter_scope(node, b._2)), None) )) case s: Switch[_] => handle_switch_statement(s, context.enter_scope(node)) - case ChorBranch(_, yes, no) => - no match { - case Some(stmt) => - mutable.Set( - CFGEdge(convert(yes, context.enter_scope(node, 0)), None), - CFGEdge(convert(stmt, context.enter_scope(node, 1)), None), - ) - case None => - mutable.Set(CFGEdge(convert(yes, context.enter_scope(node)), None)) - } // Assign statements cannot be easily categorized because they contain two expressions case Assign(_, _) => sequential_successor(context) // Other statements that can be categorized into a broader role for control flow analysis diff --git a/src/rewrite/vct/rewrite/cfg/Index.scala b/src/rewrite/vct/rewrite/cfg/Index.scala index b002900d1a..f24ad962f3 100644 --- a/src/rewrite/vct/rewrite/cfg/Index.scala +++ b/src/rewrite/vct/rewrite/cfg/Index.scala @@ -94,7 +94,7 @@ case class GlobalIndex[G](indices: mutable.Seq[Index[G]]) { case PVLLoopIndex(_, 0) | LoopIndex(_, 0) => true // If some godless heathen calls break in the init of a loop, don't consider that loop case PVLLoopIndex(_, _) | LoopIndex(_, _) | RangedForIndex(_) | - UnresolvedSeqLoopIndex(_, _) | SeqLoopIndex(_) | SwitchIndex(_) => + UnresolvedSeqLoopIndex(_, _) | SwitchIndex(_) => false case _ => true } @@ -107,8 +107,8 @@ case class GlobalIndex[G](indices: mutable.Seq[Index[G]]) { // Find innermost loop that could be the target of continue val stack: mutable.Seq[Index[G]] = indices.dropWhile { case PVLLoopIndex(_, 0) | LoopIndex(_, 0) => true - case PVLLoopIndex(_, _) | LoopIndex(_, _) | RangedForIndex(_) | - UnresolvedSeqLoopIndex(_, _) | SeqLoopIndex(_) => + case PVLLoopIndex(_, _) | LoopIndex(_, _) | + RangedForIndex(_) | UnresolvedSeqLoopIndex(_, _) => false case _ => true } @@ -120,7 +120,7 @@ case class GlobalIndex[G](indices: mutable.Seq[Index[G]]) { GlobalIndex( UnresolvedSeqLoopIndex(unresolved_seq_loop, 0) +: stack.tail ) - case RangedForIndex(_) | SeqLoopIndex(_) => GlobalIndex(stack) + case RangedForIndex(_) => GlobalIndex(stack) } } } @@ -195,8 +195,6 @@ object Index { UnresolvedSeqBranchIndex(unresolved_seq_branch, index) case unresolved_seq_loop: UnresolvedChorLoop[G] => UnresolvedSeqLoopIndex(unresolved_seq_loop, index) - case seq_branch: ChorBranch[G] => SeqBranchIndex(seq_branch, index) - case seq_loop: ChorLoop[G] => SeqLoopIndex(seq_loop) case veymont_assign_expression: VeyMontAssignExpression[G] => VeyMontAssignExpressionIndex(veymont_assign_expression) case communicatex: CommunicateX[G] => CommunicateXIndex(communicatex) @@ -982,36 +980,6 @@ case class UnresolvedSeqLoopIndex[G]( } } -case class SeqBranchIndex[G](seq_branch: ChorBranch[G], index: Int) - extends Index[G] { - override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = - Set((Outgoing(), None)) // TODO: What are the conditions? - override def resolve(): Statement[G] = - index match { - case 0 => seq_branch.yes - case 1 => seq_branch.no.get - } - override def equals(obj: scala.Any): Boolean = - obj match { - case SeqBranchIndex(s, i) => i == index && s.equals(seq_branch) - case _ => false - } -} - -case class SeqLoopIndex[G](seq_loop: ChorLoop[G]) extends Index[G] { - override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = - Set( - (Step(this), None), - (Outgoing(), None), - ) // TODO: What are the conditions? - override def resolve(): Statement[G] = seq_loop.body - override def equals(obj: scala.Any): Boolean = - obj match { - case SeqLoopIndex(s) => s.equals(seq_loop) - case _ => false - } -} - case class VeyMontAssignExpressionIndex[G]( veymont_assign_expression: VeyMontAssignExpression[G] ) extends Index[G] { diff --git a/src/rewrite/vct/rewrite/cfg/Utils.scala b/src/rewrite/vct/rewrite/cfg/Utils.scala index c5a85fc946..291b0e51f6 100644 --- a/src/rewrite/vct/rewrite/cfg/Utils.scala +++ b/src/rewrite/vct/rewrite/cfg/Utils.scala @@ -175,14 +175,6 @@ object Utils { ) case UnresolvedChorLoop(_, _, bod) => find_all_cases(bod, index.enter_scope(body)) - case ChorBranch(_, yes, no) => - no match { - case Some(stmt) => - Seq((yes, 0), (stmt, 1)) - .flatMap(t => find_all_cases(t._1, index.enter_scope(body))) - case None => find_all_cases(yes, index.enter_scope(body)) - } - case ChorLoop(_, _, bod) => find_all_cases(bod, index.enter_scope(body)) case VeyMontAssignExpression(_, assign) => find_all_cases(assign, index.enter_scope(body)) case CommunicateX(_, _, _, assign) => diff --git a/src/rewrite/vct/rewrite/lang/LangVeyMontToCol.scala b/src/rewrite/vct/rewrite/lang/LangVeyMontToCol.scala index 8306d90838..f9508280c1 100644 --- a/src/rewrite/vct/rewrite/lang/LangVeyMontToCol.scala +++ b/src/rewrite/vct/rewrite/lang/LangVeyMontToCol.scala @@ -124,36 +124,30 @@ case class LangVeyMontToCol[Pre <: Generation]( ChorRun(rw.dispatch(run.body), rw.dispatch(run.contract))(run.blame)(run.o) } - def rewriteBranch(branch: PVLBranch[Pre]): UnresolvedChorBranch[Post] = - UnresolvedChorBranch(branch.branches.map { case (e, s) => - (rw.dispatch(e), rw.dispatch(s)) - })(branch.blame)(branch.o) - - def rewriteLoop(loop: PVLLoop[Pre]): UnresolvedChorLoop[Post] = - UnresolvedChorLoop( - rw.dispatch(loop.cond), - rw.dispatch(loop.contract), - rw.dispatch(loop.body), - )(loop.blame)(loop.o) - def rewriteStatement(stmt: Statement[Pre]): Statement[Post] = stmt match { - case stmt @ PVLChorStatement(endpointName, inner) => - ChorStatement[Post]( + case stmt @ PVLEndpointStatement(endpointName, inner) => + EndpointStatement[Post]( endpointName.map(rewriteEndpointName), inner.rewriteDefault(), )(stmt.blame)(stmt.o) case _: Block[Pre] | _: Scope[Pre] => currentStatement.having(stmt) { rw.dispatch(stmt) } - case branch: PVLBranch[Pre] => rewriteBranch(branch) - case loop: PVLLoop[Pre] => rewriteLoop(loop) + case _: PVLBranch[Pre] | _: PVLLoop[Pre] => + ChorStatement(currentStatement.having(stmt) { rw.dispatch(stmt) })( + stmt.o + ) + case _: Assert[Pre] | _: Assign[Pre] | _: Eval[Pre] => + EndpointStatement( + None, + currentStatement.having(stmt) { rw.dispatch(stmt) }, + )(PanicBlame("Shouldn't happen"))(stmt.o) case comm: PVLCommunicate[Pre] => rewriteCommunicate(comm) case inv: PVLChannelInvariant[Pre] => rewriteChannelInv(inv) + // Any statement not listed here, we put in ChorStatement. ChorStatementImpl defines which leftover statement we tolerate in choreographies case stmt => currentStatement.having(stmt) { - ChorStatement(None, rw.dispatch(stmt))(PanicBlame( - "Arbitratry statement blame missing" - ))(stmt.o) + ChorStatement(rw.dispatch(stmt))(stmt.o) } } @@ -171,6 +165,8 @@ case class LangVeyMontToCol[Pre <: Generation]( Receiver[Post](commSucc.ref(expr.ref.get))(expr.o) case expr @ PVLMessage() => Message[Post](commSucc.ref(expr.ref.get))(expr.o) + case PVLEndpointExpr(endpoint, expr) => + EndpointExpr(rewriteEndpointName(endpoint), rw.dispatch(expr))(expr.o) case expr => currentExpr.having(expr) { rw.dispatch(expr) } } } diff --git a/src/rewrite/vct/rewrite/veymont/DeduplicateChorGuards.scala b/src/rewrite/vct/rewrite/veymont/DeduplicateChorGuards.scala index b8b18ec4a9..e7f4e8c7c2 100644 --- a/src/rewrite/vct/rewrite/veymont/DeduplicateChorGuards.scala +++ b/src/rewrite/vct/rewrite/veymont/DeduplicateChorGuards.scala @@ -8,7 +8,7 @@ import vct.col.ref.Ref import vct.col.rewrite.{Generation, Rewriter, RewriterBuilder} import vct.col.util.AstBuildHelpers._ import vct.result.VerificationError.UserError -import vct.rewrite.veymont.SplitChorGuards.MultipleEndpoints +import vct.rewrite.veymont.StratifyExpressions.MultipleEndpoints import scala.collection.mutable @@ -18,41 +18,43 @@ object DeduplicateChorGuards extends RewriterBuilder { "Deduplicates SeqGuard nodes with syntactically identical endpoints" } -case class DeduplicateChorGuards[Pre <: Generation]() extends Rewriter[Pre] { +case class DeduplicateChorGuards[Pre <: Generation]() + extends Rewriter[Pre] with VeymontContext[Pre] { + override def dispatch(decl: Declaration[Pre]): Unit = + decl match { + case chor: Choreography[Pre] => + currentChoreography.having(chor) { super.dispatch(chor) } + case _ => super.dispatch(decl) + } + override def dispatch(statement: Statement[Pre]): Statement[Post] = statement match { - case branch: ChorBranch[Pre] => - val guards: Seq[EndpointGuard[Pre]] = branch.guards.map { - case guard: EndpointGuard[Pre] => guard - case guard: UnpointedGuard[Pre] => - ??? // Excluded by RemoveUnpointedGuard - } - branch.rewrite(guards = dedup(guards)) - - case loop: ChorLoop[Pre] => - val guards: Seq[EndpointGuard[Pre]] = loop.guards.map { - case guard: EndpointGuard[Pre] => guard - case guard: UnpointedGuard[Pre] => - ??? // Excluded by RemoveUnpointedGuard - } - loop.rewrite(guards = dedup(guards)) + case InChor(_, c @ ChorStatement(branch: Branch[Pre])) => + c.rewrite(inner = + branch.rewrite(branches = + (dedup(c.cond), super.dispatch(c.branch.yes)) +: + c.branch.no.map(no => Seq((tt[Post], super.dispatch(no)))) + .getOrElse(Seq()) + ) + ) + + case InChor(_, c @ ChorStatement(loop: Loop[Pre])) => + c.rewrite(inner = loop.rewrite(cond = dedup(loop.cond))) case _ => rewriteDefault(statement) } - def dedup(guards: Seq[EndpointGuard[Pre]]): Seq[EndpointGuard[Post]] = { + def dedup(expr: Expr[Pre]): Expr[Post] = { + implicit val o = expr.o val m: mutable.LinkedHashMap[Endpoint[Pre], Seq[Expr[Pre]]] = mutable .LinkedHashMap() - guards.foreach { guard => - m.updateWith(guard.endpoint.decl)(exprs => - Some(exprs.getOrElse(Nil) :+ guard.condition) - ) + unfoldStar(expr).foreach { + case EndpointExpr(Ref(endpoint), expr) => + m.updateWith(endpoint)(exprs => Some(exprs.getOrElse(Seq()) :+ expr)) + case _ => assert(false) } - m.iterator.map { case (endpoint, exprs) => - EndpointGuard[Post]( - succ(endpoint), - foldAnd(exprs.map(dispatch))(DiagnosticOrigin), - )(exprs.head.o) - }.toSeq + foldAnd(m.iterator.map { case (endpoint, parts) => + EndpointExpr[Post](succ(endpoint), foldAnd(parts.map(dispatch))) + }.toSeq) } } diff --git a/src/rewrite/vct/rewrite/veymont/DropChorExpr.scala b/src/rewrite/vct/rewrite/veymont/DropChorExpr.scala new file mode 100644 index 0000000000..fcc1d04054 --- /dev/null +++ b/src/rewrite/vct/rewrite/veymont/DropChorExpr.scala @@ -0,0 +1,27 @@ +package vct.rewrite.veymont + +import com.typesafe.scalalogging.LazyLogging +import hre.util.ScopedStack +import vct.col.ast.RewriteHelpers._ +import vct.col.ast._ +import vct.col.check.SeqProgParticipant +import vct.col.origin.{Blame, Origin, SeqBranchFailure} +import vct.col.ref.Ref +import vct.col.rewrite.{Generation, Rewriter, RewriterBuilder} +import vct.col.util.AstBuildHelpers._ +import vct.result.VerificationError.UserError + +object DropChorExpr extends RewriterBuilder { + override def key: String = "dropChorExpr" + + override def desc: String = "Replaces \\chor expressions with `true`" +} + +case class DropChorExpr[Pre <: Generation]() + extends Rewriter[Pre] with LazyLogging { + override def dispatch(expr: Expr[Pre]): Expr[Post] = + expr match { + case ChorExpr(_) => tt[Post] + case _ => expr.rewriteDefault() + } +} diff --git a/src/rewrite/vct/rewrite/veymont/EncodeChannels.scala b/src/rewrite/vct/rewrite/veymont/EncodeChannels.scala index 5a0d2dc111..0b4ad2d92a 100644 --- a/src/rewrite/vct/rewrite/veymont/EncodeChannels.scala +++ b/src/rewrite/vct/rewrite/veymont/EncodeChannels.scala @@ -5,7 +5,8 @@ import hre.util.ScopedStack import vct.col.ast.{ Assign, Block, - ChorStatement, + ChorRun, + EndpointStatement, Choreography, Class, Communicate, @@ -17,18 +18,30 @@ import vct.col.ast.{ Endpoint, EndpointName, Eval, + Expr, + FieldLocation, InstanceField, InstanceMethod, + InstancePredicate, + IterationContract, Local, + LoopContract, + LoopInvariant, + Message, Program, + Receiver, + Result, Scope, + Sender, Statement, TClass, TVar, + ThisObject, Type, + Value, Variable, } -import vct.col.origin.{Name, PanicBlame, SourceName} +import vct.col.origin.{Name, Origin, PanicBlame, SourceName} import vct.col.ref.Ref import vct.col.rewrite.adt.ImportADTImporter import vct.col.rewrite.{Generation, Rewriter, RewriterBuilderArg} @@ -63,12 +76,27 @@ case class EncodeChannels[Pre <: Generation](importer: ImportADTImporter) genericChannelDecls, "readValue", ) + lazy val genericChannelHasMsg = find[Pre, InstanceField[Pre]]( + genericChannelDecls, + "hasMsg", + ) + lazy val genericChannelExchangeValue = find[Pre, InstanceField[Pre]]( + genericChannelDecls, + "exchangeValue", + ) val channelClassSucc = SuccessionMap[Communicate[Pre], Class[Post]]() val channelConstructorSucc = SuccessionMap[Communicate[Pre], Constructor[Post]]() val channelWriteSucc = SuccessionMap[Communicate[Pre], InstanceMethod[Post]]() val channelReadSucc = SuccessionMap[Communicate[Pre], InstanceMethod[Post]]() + val channelHasMsgSucc = SuccessionMap[Communicate[Pre], InstanceField[Post]]() + val channelExchangeValueSucc = + SuccessionMap[Communicate[Pre], InstanceField[Post]]() + val senderFieldSucc = SuccessionMap[Communicate[Pre], InstanceField[Post]]() + val receiverFieldSucc = SuccessionMap[Communicate[Pre], InstanceField[Post]]() + val senderParamSucc = SuccessionMap[Communicate[Pre], Variable[Post]]() + val receiverParamSucc = SuccessionMap[Communicate[Pre], Variable[Post]]() protected def find[G, T](decls: Seq[Declaration[G]], name: String = null)( implicit tag: ClassTag[T] @@ -80,24 +108,20 @@ case class EncodeChannels[Pre <: Generation](importer: ImportADTImporter) decl }.get - def channelType(comm: Communicate[Pre]): Type[Post] = - TClass[Post](channelClassSucc.ref(comm), Seq()) - val currentCommunicate = ScopedStack[Communicate[Pre]]() val currentMsgTVar = ScopedStack[Variable[Pre]]() - - def generateChannel(comm: Communicate[Pre]): Unit = - currentCommunicate.having(comm) { dispatch(genericChannelClass) } + val currentMsgExpr = ScopedStack[Expr[Post]]() val fieldOfCommunicate = SuccessionMap[(Endpoint[Pre], Communicate[Pre]), InstanceField[Post]]() val localOfCommunicate = mutable .LinkedHashMap[Communicate[Pre], Variable[Post]]() - override def dispatch(p: Program[Pre]): Program[Post] = { - mappings.program = p - p.rewriteDefault() - } + def channelType(comm: Communicate[Pre]): Type[Post] = + TClass[Post](channelClassSucc.ref(comm), Seq()) + + def generateChannel(comm: Communicate[Pre]): Unit = + currentCommunicate.having(comm) { dispatch(genericChannelClass) } def channelName(comm: Communicate[_]): Name = Name.names( @@ -105,55 +129,114 @@ case class EncodeChannels[Pre <: Generation](importer: ImportADTImporter) comm.receiver.get.decl.o.getPreferredNameOrElse(), ) + def senderField( + implicit comm: Communicate[Pre] + ): Ref[Post, InstanceField[Post]] = senderFieldSucc.ref(comm) + def receiverField( + implicit comm: Communicate[Pre] + ): Ref[Post, InstanceField[Post]] = receiverFieldSucc.ref(comm) + def thisSender(implicit comm: Communicate[Pre]): Expr[Post] = + Deref(channelThis, senderField)(PanicBlame("Should be safe"))(comm.o) + def thisReceiver(implicit comm: Communicate[Pre]): Expr[Post] = + Deref(channelThis, receiverField)(PanicBlame("Should be safe"))(comm.o) + def getSender(endpoint: Endpoint[Pre], comm: Communicate[Pre])( + implicit o: Origin + ): Deref[Post] = + Deref[Post]( + Deref[Post]( + EndpointName(succ(endpoint)), + fieldOfCommunicate.ref((endpoint, comm)), + )(PanicBlame("Should be safe")), + senderField(comm), + )(PanicBlame("Should be safe")) + def getReceiver(endpoint: Endpoint[Pre], comm: Communicate[Pre])( + implicit o: Origin + ): Deref[Post] = + Deref[Post]( + Deref[Post]( + EndpointName(succ(endpoint)), + fieldOfCommunicate.ref((endpoint, comm)), + )(PanicBlame("Should be safe")), + receiverField(comm), + )(PanicBlame("Should be safe")) + def thisHasMsg(implicit comm: Communicate[Pre]): Expr[Post] = + Deref[Post](channelThis, channelHasMsgSucc.ref(comm))(PanicBlame( + "Should be safe" + ))(comm.o) + def valueSender(implicit comm: Communicate[Pre]): Expr[Post] = + value(channelThis, senderField)(comm.o) + def valueReceiver(implicit comm: Communicate[Pre]): Expr[Post] = + value(channelThis, receiverField)(comm.o) + def channelThis(implicit comm: Communicate[Pre]): Expr[Post] = + ThisObject[Post](channelClassSucc.ref(comm))(comm.o) + def endpointComm(endpoint: Endpoint[Pre], comm: Communicate[Pre])( + implicit o: Origin + ): Deref[Post] = + Deref[Post]( + EndpointName(succ(endpoint)), + fieldOfCommunicate.ref((endpoint, comm)), + )(PanicBlame("Shouldn't happen")) + + override def dispatch(p: Program[Pre]): Program[Post] = { + mappings.program = p + p.rewriteDefault() + } + override def dispatch(decl: Declaration[Pre]): Unit = decl match { case chor: Choreography[Pre] => implicit val o = chor.o - currentChoreography.having(chor) { - def commVar(comm: Communicate[Pre]): Variable[Post] = { - val t = channelType(comm) - val v = new Variable(t)(chor.o.where(indirect = channelName(comm))) - localOfCommunicate(comm) = v - v - } + def commVar(comm: Communicate[Pre]): Variable[Post] = { + val t = channelType(comm) + val v = new Variable(t)(chor.o.where(indirect = channelName(comm))) + localOfCommunicate(comm) = v + v + } - def instantiateComm(comm: Communicate[Pre]): Statement[Post] = { - val v = localOfCommunicate(comm) - assignLocal( - local = Local[Post](v.ref), - value = - ConstructorInvocation[Post]( - ref = channelConstructorSucc(comm).ref, - classTypeArgs = Seq(dispatch(comm.msg.t)), - args = Seq(), - outArgs = Seq(), - typeArgs = Seq(), - givenMap = Seq(), - yields = Seq(), - )(PanicBlame("Should be safe")), - ) - } + def instantiateComm(comm: Communicate[Pre]): Statement[Post] = { + val v = localOfCommunicate(comm) + assignLocal( + local = Local[Post](v.ref), + value = + ConstructorInvocation[Post]( + ref = channelConstructorSucc(comm).ref, + args = Seq( + EndpointName(succ(comm.sender.get.decl)), + EndpointName(succ(comm.receiver.get.decl)), + ), + outArgs = Seq(), + typeArgs = Seq(), + givenMap = Seq(), + yields = Seq(), + classTypeArgs = Seq(), + )(PanicBlame("Should be safe")), + ) + } - def assignComm( - comm: Communicate[Pre], - endpoint: Endpoint[Pre], - ): Statement[Post] = { - assignField( - obj = EndpointName[Post](succ(endpoint)), - field = fieldOfCommunicate.ref((endpoint, comm)), - value = localOfCommunicate(comm).get, - blame = PanicBlame("Should be safe"), - ) - } + def assignComm( + comm: Communicate[Pre], + endpoint: Endpoint[Pre], + ): Statement[Post] = { + assignField( + obj = EndpointName[Post](succ(endpoint)), + field = fieldOfCommunicate.ref((endpoint, comm)), + value = localOfCommunicate(comm).get, + blame = PanicBlame("Should be safe"), + ) + } + communicatesOf(chor).foreach(generateChannel) + + currentChoreography.having(chor) { chor.rewrite(preRun = { - communicatesOf(chor).foreach(generateChannel) val vars = communicatesOf(chor).map(commVar) val instantiatedComms: Seq[Statement[Post]] = communicatesOf(chor) .map(instantiateComm) val assignComms: Seq[Statement[Post]] = chor.endpoints .flatMap { endpoint => - communicatesOf(chor).map { comm => assignComm(comm, endpoint) } + communicatesOf(endpoint).map { comm => + assignComm(comm, endpoint) + } } Some(Scope(vars, Block(instantiatedComms ++ assignComms))) }).succeed(chor) @@ -175,27 +258,155 @@ case class EncodeChannels[Pre <: Generation](importer: ImportADTImporter) ).succeed(cls) case cls: Class[Pre] if cls == genericChannelClass => + implicit val comm = currentCommunicate.top + implicit val o = comm.o globalDeclarations.scope { classDeclarations.scope { variables.scope { currentMsgTVar.having(cls.typeArgs.head) { - channelClassSucc(currentCommunicate.top) = cls - .rewrite(typeArgs = Seq()).succeed(cls) + channelClassSucc(comm) = cls.rewrite[Post]( + typeArgs = Seq(), + decls = + classDeclarations.collect { + senderFieldSucc(comm) = instanceField( + dispatch(comm.sender.get.decl.t) + )(o.where(name = "sender")).declare() + receiverFieldSucc(comm) = instanceField( + dispatch(comm.receiver.get.decl.t) + )(o.where(name = "receiver")).declare() + cls.decls.foreach(dispatch) + }._1, + intrinsicLockInvariant = + valueSender &* valueReceiver &* + dispatch(cls.intrinsicLockInvariant) &* + thisHasMsg ==> dispatch(comm.invariant), + ).succeed(cls) } } } } + case cons: Constructor[Pre] if cons == genericChannelConstructor => - channelConstructorSucc(currentCommunicate.top) = cons.rewriteDefault() - .succeed(cons) + implicit val comm = currentCommunicate.top + implicit val o = comm.o + val sender = + new Variable(dispatch(comm.t.sender))(o.where(name = "sender")) + val receiver = + new Variable(dispatch(comm.t.receiver))(o.where(name = "receiver")) + channelConstructorSucc(currentCommunicate.top) = cons.rewrite( + args = + variables.collect { + cons.args.foreach(dispatch) + senderParamSucc(comm) = sender.declare() + receiverParamSucc(comm) = receiver.declare() + }._1, + body = Some(Block( + Seq( + Assign(thisSender, sender.get)(PanicBlame("Should be safe")), + Assign(thisReceiver, receiver.get)(PanicBlame("Should be safe")), + ) :+ cons.body.map(dispatch).getOrElse(skip) + )), + contract = cons.contract.rewrite(ensures = + (valueSender &* valueReceiver &* (sender.get === thisSender) &* + (receiver.get === thisReceiver)).accounted &* + dispatch(cons.contract.ensures) + ), + ).succeed(cons) + case m: InstanceMethod[Pre] if m == genericChannelWrite => - channelWriteSucc(currentCommunicate.top) = m.rewriteDefault().succeed(m) + implicit val comm = currentCommunicate.top + implicit val o = comm.o + currentMsgExpr.having(Local(succ(m.args.head))) { + channelWriteSucc(currentCommunicate.top) = m.rewrite(contract = + m.contract.rewrite(requires = + (valueSender &* valueReceiver &* dispatch(comm.invariant)) + .accounted &* dispatch(m.contract.requires) + ) + ).succeed(m) + } + case m: InstanceMethod[Pre] if m == genericChannelRead => - channelReadSucc(currentCommunicate.top) = m.rewriteDefault().succeed(m) + implicit val comm = currentCommunicate.top + implicit val o = comm.o + currentMsgExpr.having(Result(succ(m))) { + channelReadSucc(currentCommunicate.top) = m.rewrite[Post](contract = + m.contract.rewrite( + requires = (valueSender &* valueReceiver).accounted &* + dispatch(m.contract.requires), + ensures = + (valueSender &* valueReceiver &* dispatch(comm.invariant)) + .accounted &* dispatch(m.contract.requires), + ) + ).succeed(m) + } + + case f: InstanceField[Pre] if f == genericChannelHasMsg => + channelHasMsgSucc(currentCommunicate.top) = f.rewriteDefault() + .succeed(f) + case f: InstanceField[Pre] if f == genericChannelExchangeValue => + channelExchangeValueSucc(currentCommunicate.top) = f.rewriteDefault() + .succeed(f) case _ => super.dispatch(decl) } + override def dispatch(expr: Expr[Pre]): Expr[Post] = + if (currentCommunicate.nonEmpty) { + rewriteChannelExpr(currentCommunicate.top, expr) + } else + super.dispatch(expr) + + def rewriteChannelExpr( + comm: Communicate[Pre], + expr: Expr[Pre], + ): Expr[Post] = { + implicit val c = comm + implicit val o = comm.o + expr match { + case Sender(_) => thisSender + case Receiver(_) => thisReceiver + case Message(_) if currentMsgExpr.nonEmpty => currentMsgExpr.top + case Message(_) => + Deref[Post](channelThis, channelExchangeValueSucc.ref(comm))(PanicBlame( + "Should be safe" + )) + case _ => expr.rewriteDefault() + } + } + + def channelContext(chor: Choreography[Pre])(implicit o: Origin): Expr[Post] = + foldStar(chor.endpoints.flatMap { endpoint => + communicatesOf(endpoint).map { comm => + endpointComm(endpoint, comm).value &* getSender(endpoint, comm).value &* + getReceiver(endpoint, comm).value &* + (getSender(endpoint, comm) === + EndpointName(succ(comm.sender.get.decl))) &* + (getReceiver(endpoint, comm) === + EndpointName(succ(comm.receiver.get.decl))) + } + }) + + override def dispatch(run: ChorRun[Pre]): ChorRun[Post] = + run.rewrite(contract = + run.contract.rewrite(requires = + (channelContext(choreographyOf(run))(run.o).accounted(run.o) &* + dispatch(run.contract.requires))(run.o) + ) + ) + + override def dispatch(contract: LoopContract[Pre]): LoopContract[Post] = + contract match { + case InChor(chor, inv: LoopInvariant[Pre]) => + inv.rewrite(invariant = + (channelContext(chor)(chor.o) &* dispatch(inv.invariant))(chor.o) + ) + case InChor(chor, iter: IterationContract[Pre]) => + iter.rewrite(requires = + (channelContext(chor)(chor.o) &* dispatch(iter.requires))(chor.o) + ) + case _ => contract.rewriteDefault() + } + override def dispatch(t: Type[Pre]): Type[Post] = t match { case TVar(Ref(v)) if currentMsgTVar.topOption.contains(v) => @@ -214,7 +425,7 @@ case class EncodeChannels[Pre <: Generation](importer: ImportADTImporter) def sendOf(comm: Communicate[Pre]): Statement[Post] = { implicit val o = comm.o val Some(Ref(sender)) = comm.sender - ChorStatement[Post]( + EndpointStatement[Post]( Some(succ(sender)), Eval(methodInvocation[Post]( obj = @@ -234,7 +445,7 @@ case class EncodeChannels[Pre <: Generation](importer: ImportADTImporter) def receiveOf(comm: Communicate[Pre]): Statement[Post] = { implicit val o = comm.o val Some(Ref(receiver)) = comm.receiver - ChorStatement[Post]( + EndpointStatement[Post]( Some(succ[Endpoint[Post]](receiver)), Assign( dispatch(comm.target), diff --git a/src/rewrite/vct/rewrite/veymont/EncodeChorBranchUnanimity.scala b/src/rewrite/vct/rewrite/veymont/EncodeChorBranchUnanimity.scala index 0b872d62c9..06b4911124 100644 --- a/src/rewrite/vct/rewrite/veymont/EncodeChorBranchUnanimity.scala +++ b/src/rewrite/vct/rewrite/veymont/EncodeChorBranchUnanimity.scala @@ -28,65 +28,62 @@ object EncodeChorBranchUnanimity extends RewriterBuilder { override def desc: String = "Encodes the branch unanimity requirement imposed by VeyMont on branches and loops in seq_program nodes." - case class ForwardBranchUnanimity( - branch: ChorBranch[_], - c1: ChorGuard[_], - c2: ChorGuard[_], - ) extends Blame[AssertFailed] { - override def blame(error: AssertFailed): Unit = - branch.blame.blame(BranchUnanimityFailed(c1, c2)) + case class ForwardBranchUnanimity(branch: Branch[_], c1: Expr[_], c2: Expr[_]) + extends Blame[AssertFailed] { + override def blame(error: AssertFailed): Unit = ??? + // TODO (RR): Repair blames -- branch.blame.blame(BranchUnanimityFailed(c1, c2)) } case class ForwardLoopUnanimityNotEstablished( - loop: ChorLoop[_], - c1: ChorGuard[_], - c2: ChorGuard[_], + loop: Loop[_], + c1: Expr[_], + c2: Expr[_], ) extends Blame[AssertFailed] { - override def blame(error: AssertFailed): Unit = - loop.blame.blame(LoopUnanimityNotEstablished(c1, c2)) + override def blame(error: AssertFailed): Unit = ??? + // TODO (RR): Repair blames loop.blame.blame(LoopUnanimityNotEstablished(c1, c2)) } case class ForwardLoopUnanimityNotMaintained( - loop: ChorLoop[_], - c1: ChorGuard[_], - c2: ChorGuard[_], + loop: Loop[_], + c1: Expr[_], + c2: Expr[_], ) extends Blame[AssertFailed] { - override def blame(error: AssertFailed): Unit = - loop.blame.blame(LoopUnanimityNotMaintained(c1, c2)) + override def blame(error: AssertFailed): Unit = ??? + // TODO (RR): Repair blames loop.blame.blame(LoopUnanimityNotMaintained(c1, c2)) } } case class EncodeChorBranchUnanimity[Pre <: Generation]() - extends Rewriter[Pre] { + extends Rewriter[Pre] with VeymontContext[Pre] { + + val currentLoop = ScopedStack[ChorStatement[Pre]]() - val currentLoop = ScopedStack[ChorLoop[Pre]]() + override def dispatch(decl: Declaration[Pre]): Unit = + decl match { + case chor: Choreography[Pre] => + currentChoreography.having(chor) { super.dispatch(chor) } + case _ => super.dispatch(decl) + } override def dispatch(statement: Statement[Pre]): Statement[Post] = statement match { - case branch @ ChorBranch(guards, yes, no) => + case InChor(_, c @ ChorStatement(branch: Branch[Pre])) => implicit val o = statement.o - + val guards = c.guards val assertions: Block[Post] = Block(guards.indices.init.map { i => - Assert(rewriteGuard(guards(i)) === rewriteGuard(guards(i + 1)))( + Assert(dispatch(guards(i)) === dispatch(guards(i + 1)))( ForwardBranchUnanimity(branch, guards(i), guards(i + 1)) ) }) - Block(Seq( - assertions, - Branch[Post]( - Seq((foldAnd(guards.map(rewriteGuard)), dispatch(yes))) ++ no.map { - no => Seq((tt[Post], dispatch(no))) - }.getOrElse(Nil) - ), - )) + Block(Seq(assertions, super.dispatch(branch))) - case loop @ ChorLoop(guards, contract, body) => + case InChor(_, c @ ChorStatement(loop: Loop[Pre])) => implicit val o = statement.o - + val guards = c.guards val establishAssertions: Statement[Post] = Block( guards.indices.init.map { i => - Assert(rewriteGuard(guards(i)) === rewriteGuard(guards(i + 1)))( + Assert(dispatch(guards(i)) === dispatch(guards(i + 1)))( ForwardLoopUnanimityNotEstablished(loop, guards(i), guards(i + 1)) ) } @@ -94,45 +91,39 @@ case class EncodeChorBranchUnanimity[Pre <: Generation]() val maintainAssertions: Statement[Post] = Block( guards.indices.init.map { i => - Assert(rewriteGuard(guards(i)) === rewriteGuard(guards(i + 1)))( + Assert(dispatch(guards(i)) === dispatch(guards(i + 1)))( ForwardLoopUnanimityNotMaintained(loop, guards(i), guards(i + 1)) ) } ) - val finalLoop: Loop[Post] = Loop( - establishAssertions, - foldAnd(guards.map(rewriteGuard)), - maintainAssertions, - currentLoop.having(loop) { dispatch(contract) }, - dispatch(body), + val finalLoop: Loop[Post] = loop.rewrite( + init = establishAssertions, + update = maintainAssertions, + contract = currentLoop.having(c) { dispatch(loop.contract) }, ) finalLoop - case statement => rewriteDefault(statement) + case statement => statement.rewriteDefault() } - def rewriteGuard(guard: ChorGuard[Pre]): Expr[Post] = - dispatch(guard.condition) - def allEqual[G](exprs: Seq[Expr[G]])(implicit o: Origin): Expr[G] = foldAnd[G](exprs.indices.init.map(i => exprs(i) === exprs(i + 1))) override def dispatch(contract: LoopContract[Pre]): LoopContract[Post] = (currentLoop.topOption, contract) match { - case (Some(loop), inv: LoopInvariant[Pre]) => + case (Some(c), inv: LoopInvariant[Pre]) => implicit val o = contract.o inv.rewrite(invariant = - dispatch(inv.invariant) &* allEqual(loop.guards.map(rewriteGuard)) + dispatch(inv.invariant) &* allEqual(c.guards.map(dispatch)) ) - case (Some(loop), inv @ IterationContract(requires, ensures, _)) => + case (Some(c), inv @ IterationContract(requires, ensures, _)) => implicit val o = contract.o inv.rewrite( - requires = - dispatch(requires) &* allEqual(loop.guards.map(rewriteGuard)), - ensures = dispatch(ensures) &* allEqual(loop.guards.map(rewriteGuard)), + requires = dispatch(requires) &* allEqual(c.guards.map(dispatch)), + ensures = dispatch(ensures) &* allEqual(c.guards.map(dispatch)), ) - case _ => rewriteDefault(contract) + case _ => contract.rewriteDefault() } } diff --git a/src/rewrite/vct/rewrite/veymont/EncodeChoreography.scala b/src/rewrite/vct/rewrite/veymont/EncodeChoreography.scala index 76f4f9513c..4646db3426 100644 --- a/src/rewrite/vct/rewrite/veymont/EncodeChoreography.scala +++ b/src/rewrite/vct/rewrite/veymont/EncodeChoreography.scala @@ -6,6 +6,7 @@ import vct.col.ast.{ Assert, Assign, Block, + ChorExpr, ChorPerm, ChorRun, ChorStatement, @@ -16,14 +17,16 @@ import vct.col.ast.{ Declaration, Deref, Endpoint, + EndpointExpr, EndpointName, + EndpointStatement, Eval, Expr, InstanceMethod, Local, LocalDecl, - MethodInvocation, Message, + MethodInvocation, Node, Perm, Procedure, @@ -106,7 +109,7 @@ object EncodeChoreography extends RewriterBuilder { // } // } - case class AssignFailedToSeqAssignFailure(assign: ChorStatement[_]) + case class AssignFailedToSeqAssignFailure(assign: EndpointStatement[_]) extends Blame[AssignFailed] { override def blame(error: AssignFailed): Unit = assign.blame.blame(SeqAssignInsufficientPermission(assign)) @@ -317,11 +320,11 @@ case class EncodeChoreography[Pre <: Generation]() override def dispatch(stat: Statement[Pre]): Statement[Post] = stat match { - case assign @ ChorStatement(None, Assign(target, e)) => + case assign @ EndpointStatement(None, Assign(target, e)) => throw new Exception( assign.o.messageInContext("ChorStatement with None!") ) - case assign @ ChorStatement(Some(Ref(endpoint)), Assign(target, e)) => + case assign @ EndpointStatement(Some(Ref(endpoint)), Assign(target, e)) => logger .warn(s"Ignoring endpoint annotation on chor assign statement ${assign .o.shortPosition.map("at " + _).getOrElse("")}") @@ -381,7 +384,16 @@ case class EncodeChoreography[Pre <: Generation]() throw new Exception(comm.o.messageInContext( "Either the sender or receiver was not annotated for or not inferred!" )) - case ChorStatement(_, stat) => dispatch(stat) + case EndpointStatement(_, stat) => dispatch(stat) + case s @ ChorStatement(inner) => + logger.warn( + s"Ignoring semantics of ChorStatement at ${s.o.shortPositionText}" + ) + dispatch(inner) + case ep @ EndpointStatement(_, inner) => + throw new Exception(ep.o.messageInContext( + "TODO: Implement choreographic verification encoding for this" + )) case stat => rewriteDefault(stat) } @@ -433,6 +445,12 @@ case class EncodeChoreography[Pre <: Generation]() case (mode, Message(Ref(comm))) => implicit val o = expr.o msgSucc(comm).get - case (_, expr) => rewriteDefault(expr) + case (mode, EndpointExpr(Ref(endpoint), expr)) => + logger.warn( + "Ignoring endpoint expr annotation at " + expr.o.shortPositionText + ) + dispatch(expr) + case (mode, ChorExpr(inner)) => dispatch(inner) + case (_, expr) => expr.rewriteDefault() } } diff --git a/src/rewrite/vct/rewrite/veymont/EncodeChoreographyParameters.scala b/src/rewrite/vct/rewrite/veymont/EncodeChoreographyParameters.scala index e86486d36c..c036e49f25 100644 --- a/src/rewrite/vct/rewrite/veymont/EncodeChoreographyParameters.scala +++ b/src/rewrite/vct/rewrite/veymont/EncodeChoreographyParameters.scala @@ -26,6 +26,7 @@ object EncodeChoreographyParameters extends RewriterBuilder { "Encode choreography parameters as fields on all endpoint types." } +// TODO (RR): Remove this class in the end, probably /** This is a rewrite that encodes choreography parameters as endpoint fields in * a separate class. It was abandoned because the connection between parameters * and the respective fields in each endpoint class is lost between this pass diff --git a/src/rewrite/vct/rewrite/veymont/EncodeUnpointedGuard.scala b/src/rewrite/vct/rewrite/veymont/EncodeUnpointedGuard.scala deleted file mode 100644 index b8b9ab68b7..0000000000 --- a/src/rewrite/vct/rewrite/veymont/EncodeUnpointedGuard.scala +++ /dev/null @@ -1,63 +0,0 @@ -package vct.rewrite.veymont - -import hre.util.ScopedStack -import vct.col.ast.RewriteHelpers._ -import vct.col.ast._ -import vct.col.origin.Origin -import vct.col.ref.Ref -import vct.col.rewrite.{Generation, Rewriter, RewriterBuilder} -import vct.col.util.AstBuildHelpers._ -import vct.result.VerificationError.UserError -import vct.rewrite.veymont.SplitChorGuards.MultipleEndpoints - -import scala.collection.immutable.ListSet - -object EncodeUnpointedGuard extends RewriterBuilder { - override def key: String = "encodeUnpointedGuard" - override def desc: String = - "Encodes unpointed guard by duplicating the condition to all guards currently participating" -} - -case class EncodeUnpointedGuard[Pre <: Generation]() extends Rewriter[Pre] { - val currentParticipants: ScopedStack[ListSet[Endpoint[Pre]]] = ScopedStack() - - override def dispatch(decl: Declaration[Pre]): Unit = - decl match { - case prog: Choreography[Pre] => - currentParticipants.having(ListSet.from(prog.endpoints)) { - rewriteDefault(prog) - } - - case decl => rewriteDefault(decl) - } - - override def dispatch(statement: Statement[Pre]): Statement[Post] = - statement match { - case branch: ChorBranch[Pre] => - val newParticipants = - if (branch.hasUnpointed) { currentParticipants.top } - else { ListSet.from(branch.participants) } - currentParticipants.having(newParticipants) { - branch.rewrite(guards = branch.guards.flatMap(rewriteGuard)) - } - - case loop: ChorLoop[Pre] => - val newParticipants = - if (loop.hasUnpointed) { currentParticipants.top } - else { ListSet.from(loop.participants) } - currentParticipants.having(newParticipants) { - loop.rewrite(guards = loop.guards.flatMap(rewriteGuard)) - } - - case statement => rewriteDefault(statement) - } - - def rewriteGuard(guard: ChorGuard[Pre]): Seq[ChorGuard[Post]] = - guard match { - case guard: EndpointGuard[Pre] => Seq(guard.rewriteDefault()) - case UnpointedGuard(expr) => - currentParticipants.top.map { endpoint => - EndpointGuard[Post](succ(endpoint), dispatch(expr))(guard.o) - }.toSeq - } -} diff --git a/src/rewrite/vct/rewrite/veymont/GenerateChoreographyPermissions.scala b/src/rewrite/vct/rewrite/veymont/GenerateChoreographyPermissions.scala index b66fa3c90b..3e450ff937 100644 --- a/src/rewrite/vct/rewrite/veymont/GenerateChoreographyPermissions.scala +++ b/src/rewrite/vct/rewrite/veymont/GenerateChoreographyPermissions.scala @@ -189,10 +189,10 @@ case class GenerateChoreographyPermissions[Pre <: Generation]( override def dispatch(statement: Statement[Pre]): Statement[Post] = statement match { - case loop: ChorLoop[Pre] => + case c @ ChorStatement(loop: Loop[Pre]) => currentPerm.having( endpointsPerm(participants(statement).toSeq)(loop.contract.o) - ) { loop.rewriteDefault() } + ) { c.rewriteDefault() } case statement => rewriteDefault(statement) } diff --git a/src/rewrite/vct/rewrite/veymont/GenerateImplementation.scala b/src/rewrite/vct/rewrite/veymont/GenerateImplementation.scala index 06e100d262..76661b37fc 100644 --- a/src/rewrite/vct/rewrite/veymont/GenerateImplementation.scala +++ b/src/rewrite/vct/rewrite/veymont/GenerateImplementation.scala @@ -10,9 +10,6 @@ import vct.col.ast.{ Block, BooleanValue, Branch, - ChorBranch, - ChorGuard, - ChorLoop, ChorPerm, ChorRun, ChorStatement, @@ -24,13 +21,15 @@ import vct.col.ast.{ Declaration, Deref, Endpoint, - EndpointGuard, + EndpointExpr, EndpointName, + EndpointStatement, Eval, Expr, Fork, InstanceField, InstanceMethod, + IterationContract, JavaClass, JavaConstructor, JavaInvocation, @@ -43,6 +42,8 @@ import vct.col.ast.{ Join, Local, Loop, + LoopContract, + LoopInvariant, MethodInvocation, NewObject, Node, @@ -282,7 +283,11 @@ case class GenerateImplementation[Pre <: Generation]() val mainBody = Scope( chor.endpoints.map(endpointLocals(_)), - Block(initEndpoints ++ auxFieldAssigns ++ forkJoins)(chor.o), + Block( + initEndpoints ++ + Seq(chor.preRun.map(dispatch).getOrElse(Block(Seq()))) ++ + auxFieldAssigns ++ forkJoins + )(chor.o), ) globalDeclarations.declare( @@ -308,7 +313,7 @@ case class GenerateImplementation[Pre <: Generation]() currentEndpoint.having(endpoint) { classDeclarations.declare( new RunMethod( - body = Some(dispatch(run.body)), + body = Some(projectStmt(run.body)(endpoint)), contract = dispatch(run.contract), )(PanicBlame("")) ) @@ -349,7 +354,7 @@ case class GenerateImplementation[Pre <: Generation]() override def dispatch(statement: Statement[Pre]): Statement[Post] = { if (currentEndpoint.nonEmpty) - projectStatement(statement) + projectStmt(statement)(currentEndpoint.top) else super.dispatch(statement) } @@ -371,67 +376,85 @@ case class GenerateImplementation[Pre <: Generation]() Deref[Post](currentThis.top, endpointParamFields.ref((endpoint, v)))( PanicBlame("Shouldn't happen") ) - case InEndpoint(_, endpoint, expr) => projectExpression(expr, endpoint) + case InEndpoint(_, endpoint, expr) => projectExpr(expr)(endpoint) case _ => expr.rewriteDefault() } - def projectStatement(statement: Statement[Pre]): Statement[Post] = + def projectStmt( + statement: Statement[Pre] + )(implicit endpoint: Endpoint[Pre]): Statement[Post] = statement match { - // Whitelist statements that do not need an endpoint context - case ChorStatement(None, statement) => + case EndpointStatement(None, statement) => statement match { - case _: Branch[Pre] | _: Loop[Pre] => projectStatement(statement) case _ => throw new Exception( "Encountered ChorStatement without endpoint context" ) } - case ChorStatement(Some(Ref(endpoint)), inner) - if endpoint == currentEndpoint.top => + case EndpointStatement(Some(Ref(other)), inner) if other == endpoint => inner match { case assign: Assign[Pre] => assign.rewriteDefault() case eval: Eval[Pre] => eval.rewriteDefault() } // Ignore statements that do not match the current endpoint - case ChorStatement(_, _) => Block(Seq())(statement.o) - case branch: ChorBranch[Pre] - if branch.guards.map(_.endpointOpt.get) - .contains(currentEndpoint.top) => + case EndpointStatement(_, _) => Block(Seq())(statement.o) + // Specialize composite statements to the current endpoint + case c @ ChorStatement(branch: Branch[Pre]) + if c.explicitEndpoints.contains(endpoint) => implicit val o = branch.o Branch[Post]( - Seq( - (projectExpression(branch.guards)(branch.o), dispatch(branch.yes)) - ) ++ branch.no.map(no => Seq((tt[Post], dispatch(no)))) - .getOrElse(Seq()) + Seq((projectExpr(c.cond), projectStmt(c.branch.yes))) ++ + c.branch.no.map(no => Seq((tt[Post], projectStmt(no)))) + .getOrElse(Seq()) ) - case chorLoop: ChorLoop[Pre] - if chorLoop.guards.map(_.endpointOpt.get) - .contains(currentEndpoint.top) => - implicit val o = chorLoop.o + case c @ ChorStatement(l: Loop[Pre]) + if c.explicitEndpoints.contains(endpoint) => + implicit val o = l.o loop( - cond = projectExpression(chorLoop.guards)(chorLoop.o), - body = dispatch(chorLoop.body), - contract = dispatch(chorLoop.contract), + cond = projectExpr(l.cond), + body = projectStmt(l.body), + contract = dispatch(l.contract), ) - case _: ChorBranch[Pre] | _: ChorLoop[Pre] => Block(Seq())(statement.o) + // Ignore loops, branches that the current endpoint doesn't participate in + case c @ ChorStatement(_: Loop[Pre] | _: Branch[Pre]) => Block(Seq())(c.o) + // Rewrite blocks transparently case block: Block[Pre] => block.rewriteDefault() + // Don't let any missed cases slip through case s => throw new Exception(s"Unsupported: $s") } - def projectExpression( - guards: Seq[ChorGuard[Pre]] - )(implicit o: Origin): Expr[Post] = - foldStar(guards.collect { - case EndpointGuard(Ref(endpoint), cond) - if endpoint == currentEndpoint.top => - dispatch(cond) - }) + def projectContract( + contract: LoopContract[Pre] + )(implicit endpoint: Endpoint[Pre]): LoopContract[Post] = + contract match { + case inv: LoopInvariant[Pre] => + inv.rewrite(invariant = projectExpr(inv.invariant)) + case it: IterationContract[Pre] => + it.rewrite( + requires = projectExpr(it.requires), + ensures = projectExpr(it.ensures), + ) + } + + def projectContract( + contract: ApplicableContract[Pre] + )(implicit endpoint: Endpoint[Pre]): ApplicableContract[Post] = + contract.rewrite( + requires = (projectExpr(_)).accounted(contract.requires), + ensures = (projectExpr(_)).accounted(contract.ensures), + contextEverywhere = projectExpr(contract.contextEverywhere), + ) - def projectExpression(expr: Expr[Pre], endpoint: Endpoint[Pre]): Expr[Post] = + def projectExpr( + expr: Expr[Pre] + )(implicit endpoint: Endpoint[Pre]): Expr[Post] = expr match { case ChorPerm(Ref(other), loc, perm) if endpoint == other => Perm(dispatch(loc), dispatch(perm))(expr.o) case ChorPerm(Ref(other), _, _) if endpoint != other => tt + case EndpointExpr(Ref(other), expr) if endpoint == other => + projectExpr(expr) + case EndpointExpr(Ref(other), expr) => tt case _ => expr.rewriteDefault() } @@ -896,20 +919,20 @@ case class GenerateImplementation[Pre <: Generation]() } } - private def paralleliseThreadCondition( - node: Expr[Pre], - thread: Endpoint[Pre], - c: ChorGuard[Pre], - ) = { - ??? - // TODO: Broke this because AST changed, repair +// private def paralleliseThreadCondition( +// node: Expr[Pre], +// thread: Endpoint[Pre], +// c: ChorGuard[Pre], +// ) = { +// ??? + // TODO: Broke this because AST changed, repair // c.conditions.find { case (threadRef, _) => // threadRef.decl == thread // } match { // case Some((_, threadExpr)) => dispatch(threadExpr) // case _ => throw ParalleliseEndpointsError(node, "Condition of if statement or while loop must contain an expression for every thread") // } - } +// } private def getThisVeyMontDeref( thread: Endpoint[Pre], diff --git a/src/rewrite/vct/rewrite/veymont/InferEndpointContexts.scala b/src/rewrite/vct/rewrite/veymont/InferEndpointContexts.scala index 3edce218ae..3a994b6ffa 100644 --- a/src/rewrite/vct/rewrite/veymont/InferEndpointContexts.scala +++ b/src/rewrite/vct/rewrite/veymont/InferEndpointContexts.scala @@ -3,6 +3,8 @@ package vct.rewrite.veymont import com.typesafe.scalalogging.LazyLogging import hre.util.ScopedStack import vct.col.ast.{ + ReadPerm, + Value, AbstractRewriter, AmbiguousLocation, ApplicableContract, @@ -11,10 +13,9 @@ import vct.col.ast.{ Block, BooleanValue, Branch, - ChorGuard, ChorPerm, ChorRun, - ChorStatement, + EndpointStatement, Choreography, Class, ClassDeclaration, @@ -166,16 +167,16 @@ case class InferEndpointContexts[Pre <: Generation]() override def dispatch(stmt: Statement[Pre]): Statement[Post] = stmt match { // Whitelist statements that do not need a context - case s @ ChorStatement(None, assign: Assign[Pre]) => + case s @ EndpointStatement(None, assign: Assign[Pre]) => val endpoint: Endpoint[Pre] = getEndpoint(assign.target) s.rewrite(endpoint = Some(succ(endpoint))) - case s @ ChorStatement(None, assert: Assert[Pre]) => + case s @ EndpointStatement(None, assert: Assert[Pre]) => val endpoint: Endpoint[Pre] = getEndpoint(assert.expr) s.rewrite(endpoint = Some(succ(endpoint))) - case s @ ChorStatement(None, Eval(invoke: MethodInvocation[Pre])) => + case s @ EndpointStatement(None, Eval(invoke: MethodInvocation[Pre])) => val endpoint: Endpoint[Pre] = getEndpoint(invoke.obj) s.rewrite(endpoint = Some(succ(endpoint))) - case s @ ChorStatement(None, _) => throw EndpointInferenceUndefined(s) + case s @ EndpointStatement(None, _) => throw EndpointInferenceUndefined(s) case s => s.rewriteDefault() } @@ -185,6 +186,10 @@ case class InferEndpointContexts[Pre <: Generation]() ChorPerm[Post](succ(getEndpoint(loc)), dispatch(loc), dispatch(perm))( p.o ) + case v @ Value(loc) if inChor.topOption.contains(true) => + ChorPerm[Post](succ(getEndpoint(loc)), dispatch(loc), ReadPerm()(v.o))( + v.o + ) case _ => expr.rewriteDefault() } } diff --git a/src/rewrite/vct/rewrite/veymont/SpecializeEndpointClasses.scala b/src/rewrite/vct/rewrite/veymont/SpecializeEndpointClasses.scala index 177c4064b5..9acf30686f 100644 --- a/src/rewrite/vct/rewrite/veymont/SpecializeEndpointClasses.scala +++ b/src/rewrite/vct/rewrite/veymont/SpecializeEndpointClasses.scala @@ -11,9 +11,8 @@ import vct.col.ast.{ Block, BooleanValue, Branch, - ChorGuard, ChorRun, - ChorStatement, + EndpointStatement, Choreography, Class, ClassDeclaration, diff --git a/src/rewrite/vct/rewrite/veymont/SplitChorGuards.scala b/src/rewrite/vct/rewrite/veymont/SplitChorGuards.scala deleted file mode 100644 index 71071f76ff..0000000000 --- a/src/rewrite/vct/rewrite/veymont/SplitChorGuards.scala +++ /dev/null @@ -1,125 +0,0 @@ -package vct.rewrite.veymont - -import hre.util.ScopedStack -import vct.col.ast.RewriteHelpers._ -import vct.col.ast._ -import vct.col.check.SeqProgParticipant -import vct.col.origin.{Blame, Origin, SeqBranchFailure} -import vct.col.ref.Ref -import vct.col.rewrite.{Generation, Rewriter, RewriterBuilder} -import vct.col.util.AstBuildHelpers._ -import vct.result.VerificationError.UserError -import vct.rewrite.veymont.SplitChorGuards.{ - MultipleEndpoints, - SeqProgParticipantErrors, -} - -import scala.collection.immutable.ListSet -import scala.collection.mutable - -object SplitChorGuards extends RewriterBuilder { - override def key: String = "splitChorGuards" - override def desc: String = - "Lifts conditions in loops and conditionals into the ChorGuard AST node, stratifying the condition per endpoint." - - case class MultipleEndpoints(e: Expr[_]) extends UserError { - override def code: String = "multipleEndpoints" - override def text: String = - e.o.messageInContext( - "This expression references multiple endpoints, but that is not yet supported." - ) - } - - case class SeqProgParticipantErrors(es: Seq[SeqProgParticipant]) - extends UserError { - override def code: String = "seqProgParticipantErrors" - override def text: String = - es.map { case err: SeqProgParticipant => err.message { n => n.o } } - .mkString("\n") - } -} - -case class SplitChorGuards[Pre <: Generation]() extends Rewriter[Pre] { - val currentProg: ScopedStack[Choreography[Pre]] = ScopedStack() - val currentParticipants: ScopedStack[ListSet[Pre]] = ScopedStack() - - override def dispatch(prog: Program[Pre]): Program[Post] = { - val newProg = prog.rewrite() - val errors = newProg.check - val seqBranchErrors = errors.collect { case err: SeqProgParticipant => err } - if (errors.nonEmpty && errors.length == seqBranchErrors.length) { - throw SeqProgParticipantErrors(seqBranchErrors) - } - newProg - } - - override def dispatch(decl: Declaration[Pre]): Unit = - decl match { - case prog: Choreography[Pre] => - currentProg.having(prog) { rewriteDefault(prog) } - case decl => rewriteDefault(decl) - } - - override def dispatch(statement: Statement[Pre]): Statement[Post] = - statement match { - case branch: UnresolvedChorBranch[Pre] => - assert(branch.branches.nonEmpty) - unfoldBranch(branch.branches)(branch.blame, branch.o) - - case loop: UnresolvedChorLoop[Pre] if currentProg.nonEmpty => - ChorLoop( - inferSeqGuard(loop.cond), - dispatch(loop.contract), - dispatch(loop.body), - )(loop.blame)(loop.o) - - case statement => rewriteDefault(statement) - } - - def unfoldBranch( - branches: Seq[(Expr[Pre], Statement[Pre])] - )(implicit blame: Blame[SeqBranchFailure], o: Origin): ChorBranch[Post] = - branches match { - case Seq((e, s)) => ChorBranch(inferSeqGuard(e), dispatch(s), None)(blame) - case (e, s) +: (otherYes +: branches) => - ChorBranch( - inferSeqGuard(e), - dispatch(s), - Some(unfoldBranch(otherYes +: branches)), - )(blame) - case _ => ??? - } - - // Infer guard conditions based on the classic syntactical restrictions - endpoint dereferences determine which - // endpoint is evaluating the expression. - def inferSeqGuard(e: Expr[Pre]): Seq[ChorGuard[Post]] = { - val exprs = { - // Ensure the "true" expression is kept - val es = unfoldStar(e) - if (es.isEmpty) - Seq(e) - else - es - } - val pointed = exprs.map(point) - pointed.map { - case (Some(endpoint), expr) => - EndpointGuard[Post](succ(endpoint), dispatch(expr))(expr.o) - case (None, expr) => UnpointedGuard(dispatch(expr))(expr.o) - } - } - - // "Points" an expression in the direction of an endpoint if possible - def point(e: Expr[Pre]): (Option[Endpoint[Pre]], Expr[Pre]) = { - InferEndpointContexts.getEndpoints(e) match { - case Seq(endpoint) => - // expr is totally in context of one endpoint and whatever else is in scope - (Some(endpoint), e) - case Seq() => (None, e) - case _ => - throw MultipleEndpoints( - e - ) // Expr uses multiple endpoints - for now we should disallow that. - } - } -} diff --git a/src/rewrite/vct/rewrite/veymont/StratifyExpressions.scala b/src/rewrite/vct/rewrite/veymont/StratifyExpressions.scala new file mode 100644 index 0000000000..e1c49c9c5c --- /dev/null +++ b/src/rewrite/vct/rewrite/veymont/StratifyExpressions.scala @@ -0,0 +1,154 @@ +package vct.rewrite.veymont + +import com.typesafe.scalalogging.LazyLogging +import hre.util.ScopedStack +import vct.col.ast.RewriteHelpers._ +import vct.col.ast._ +import vct.col.check.SeqProgParticipant +import vct.col.origin.{Blame, Origin, SeqBranchFailure} +import vct.col.ref.Ref +import vct.col.rewrite.{Generation, Rewriter, RewriterBuilder} +import vct.col.util.AstBuildHelpers._ +import vct.result.VerificationError.UserError +import vct.rewrite.veymont.StratifyExpressions.{ + MultipleEndpoints, + SeqProgParticipantErrors, +} + +import scala.collection.immutable.ListSet +import scala.collection.mutable + +object StratifyExpressions extends RewriterBuilder { + override def key: String = "stratifyExpressions" + override def desc: String = + "Stratifies expressions by putting all contracts, branch conditions and loop conditions within a choreography's run declaration into endpoint exprs, inferring endpoint contexts where required." + + case class MultipleEndpoints(e: Expr[_]) extends UserError { + override def code: String = "multipleEndpoints" + override def text: String = + e.o.messageInContext( + "This expression references multiple endpoints, but that is not yet supported." + ) + } + + case class SeqProgParticipantErrors(es: Seq[SeqProgParticipant]) + extends UserError { + override def code: String = "seqProgParticipantErrors" + override def text: String = + es.map { case err: SeqProgParticipant => err.message { n => n.o } } + .mkString("\n") + } +} + +case class StratifyExpressions[Pre <: Generation]() + extends Rewriter[Pre] with VeymontContext[Pre] with LazyLogging { + + override def dispatch(prog: Program[Pre]): Program[Post] = { + val newProg = prog.rewrite() + val errors = newProg.check + val seqBranchErrors = errors.collect { case err: SeqProgParticipant => err } + if (errors.nonEmpty && errors.length == seqBranchErrors.length) { + throw SeqProgParticipantErrors(seqBranchErrors) + } + newProg + } + + override def dispatch(decl: Declaration[Pre]): Unit = + decl match { + case prog: Choreography[Pre] => + currentChoreography.having(prog) { prog.rewriteDefault().succeed(prog) } + case decl => super.dispatch(decl) + } + + override def dispatch( + contract: ApplicableContract[Pre] + ): ApplicableContract[Post] = + contract match { + case InChor(_, contract) => + contract.rewrite( + requires = (stratifyExpr(_)).accounted(contract.requires), + ensures = (stratifyExpr(_)).accounted(contract.ensures), + contextEverywhere = stratifyExpr(contract.contextEverywhere), + ) + case _ => contract.rewriteDefault() + } + + override def dispatch(contract: LoopContract[Pre]): LoopContract[Post] = + contract match { + case InChor(_, inv: LoopInvariant[Pre]) => + inv.rewrite(invariant = stratifyExpr(inv.invariant)) + case InChor(_, contract: IterationContract[Pre]) => + contract.rewrite( + requires = stratifyExpr(contract.requires), + ensures = stratifyExpr(contract.ensures), + ) + case _ => contract.rewriteDefault() + } + + override def dispatch(statement: Statement[Pre]): Statement[Post] = + statement match { + case InChor(_, l: Loop[Pre]) if currentChoreography.nonEmpty => + loop( + cond = stratifyExpr(l.cond), + contract = dispatch(l.contract), + body = dispatch(l.body), + ) /*(loop.blame)*/ (l.o) + + case InChor(_, branch: Branch[Pre]) => + assert(branch.branches.nonEmpty) + logger.warn("TODO: Branch blame") + unfoldBranch(branch.branches)(null, branch.o) + + case statement => statement.rewriteDefault() + } + + // TODO (RR): For branch, make sure blame is put on ChorStatement wrapper of Branch. Probably too for loop + + def unfoldBranch( + branches: Seq[(Expr[Pre], Statement[Pre])] + )(implicit blame: Blame[SeqBranchFailure], o: Origin): Branch[Post] = + branches match { + case Seq((e, s)) => Branch(Seq((stratifyExpr(e), dispatch(s)))) + case (e, s) +: (otherYes +: branches) => + Branch(Seq( + (stratifyExpr(e), dispatch(s)), + (tt, unfoldBranch(otherYes +: branches)), + )) /* (blame) */ + case _ => ??? + } + + def stratifyExpr(e: Expr[Pre]): Expr[Post] = { + val exprs = { + // Ensure the "true" expression is kept + val es = unfoldStar(e) + if (es.isEmpty) + Seq(e) + else + es + } + foldAny(e.t)( + exprs.map { + case e: ChorExpr[Pre] => (None, e) + case e: ChorPerm[Pre] => (None, e) + case expr => point(expr) + }.map { + case (Some(endpoint), expr) => + EndpointExpr[Post](succ(endpoint), dispatch(expr))(expr.o) + case (None, expr) => dispatch(expr) + } + )(e.o) + } + + // "Points" an expression in the direction of an endpoint if possible + def point(e: Expr[Pre]): (Option[Endpoint[Pre]], Expr[Pre]) = { + InferEndpointContexts.getEndpoints(e) match { + case Seq(endpoint) => + // expr is totally in context of one endpoint and whatever else is in scope + (Some(endpoint), e) + case Seq() => (None, e) + case _ => + // Expr uses multiple endpoints - for now we should disallow that. + throw MultipleEndpoints(e) + } + } +} diff --git a/src/rewrite/vct/rewrite/veymont/StratifyUnpointedExpressions.scala b/src/rewrite/vct/rewrite/veymont/StratifyUnpointedExpressions.scala new file mode 100644 index 0000000000..82e66b87e7 --- /dev/null +++ b/src/rewrite/vct/rewrite/veymont/StratifyUnpointedExpressions.scala @@ -0,0 +1,109 @@ +package vct.rewrite.veymont + +import hre.util.ScopedStack +import vct.col.ast.RewriteHelpers._ +import vct.col.ast._ +import vct.col.origin.Origin +import vct.col.ref.Ref +import vct.col.rewrite.{Generation, Rewriter, RewriterBuilder} +import vct.col.util.AstBuildHelpers._ +import vct.result.VerificationError.UserError +import vct.rewrite.veymont.StratifyExpressions.MultipleEndpoints + +import scala.collection.immutable.ListSet + +object StratifyUnpointedExpressions extends RewriterBuilder { + override def key: String = "stratifyUnpointedExpressions" + override def desc: String = + "Stratifies expressions that could be in any endpoint context by duplicating the expression to all endpoints currently participating" +} + +case class StratifyUnpointedExpressions[Pre <: Generation]() + extends Rewriter[Pre] with VeymontContext[Pre] { + val currentParticipants: ScopedStack[ListSet[Endpoint[Pre]]] = ScopedStack() + + override def dispatch(decl: Declaration[Pre]): Unit = + decl match { + case chor: Choreography[Pre] => + currentChoreography.having(chor) { + currentParticipants.having(ListSet.from(chor.endpoints)) { + rewriteDefault(chor) + } + } + + case decl => rewriteDefault(decl) + } + + override def dispatch( + contract: ApplicableContract[Pre] + ): ApplicableContract[Post] = + contract match { + case InChor(_, contract) => + contract.rewrite( + requires = (stratifyExpr(_)).accounted(contract.requires), + ensures = (stratifyExpr(_)).accounted(contract.ensures), + contextEverywhere = stratifyExpr(contract.contextEverywhere), + ) + case _ => contract.rewriteDefault() + } + + override def dispatch(contract: LoopContract[Pre]): LoopContract[Post] = + contract match { + case InChor(_, inv: LoopInvariant[Pre]) => + inv.rewrite(invariant = stratifyExpr(inv.invariant)) + case InChor(_, contract: IterationContract[Pre]) => + contract.rewrite( + requires = stratifyExpr(contract.requires), + ensures = stratifyExpr(contract.ensures), + ) + case _ => contract.rewriteDefault() + } + + override def dispatch(statement: Statement[Pre]): Statement[Post] = + statement match { + case InChor(_, c @ ChorStatement(branch: Branch[Pre])) => + // All Chor branches must have two branches, of which the second one has a tt condition + val (guard, yes, noOpt) = + branch.branches match { + case Seq((guard, yes)) => (guard, yes, None) + case Seq((guard, yes), (BooleanValue(true), no)) => + (guard, yes, Some(no)) + case _ => ??? + } + val newParticipants = + if (c.hasUnpointed) { currentParticipants.top } + else { ListSet.from(c.participants) } + currentParticipants.having(newParticipants) { + c.rewrite(inner = + branch.rewrite(branches = + (stratifyExpr(guard), super.dispatch(yes)) +: + noOpt.map(no => Seq((tt[Post], super.dispatch(no)))) + .getOrElse(Seq()) + ) + ) + } + + case InChor(_, c @ ChorStatement(loop: Loop[Pre])) => + val newParticipants = + if (c.hasUnpointed) { currentParticipants.top } + else { ListSet.from(c.participants) } + currentParticipants.having(newParticipants) { + c.rewrite(inner = loop.rewrite(cond = stratifyExpr(loop.cond))) + } + + case statement => statement.rewriteDefault() + } + + def stratifyExpr(expr: Expr[Pre]): Expr[Post] = { + implicit val o = expr.o + foldAny(expr.t)(unfoldStar(expr).flatMap { + case expr @ (_: EndpointExpr[Pre] | + _: ChorExpr[Pre] | _: ChorPerm[Pre]) => + Seq(expr.rewriteDefault()) + case expr => + currentParticipants.top.map { endpoint => + EndpointExpr[Post](succ(endpoint), dispatch(expr)) + }.toSeq + }) + } +} diff --git a/src/rewrite/vct/rewrite/veymont/VeymontContext.scala b/src/rewrite/vct/rewrite/veymont/VeymontContext.scala index 735d841d40..cc51913ccb 100644 --- a/src/rewrite/vct/rewrite/veymont/VeymontContext.scala +++ b/src/rewrite/vct/rewrite/veymont/VeymontContext.scala @@ -3,6 +3,7 @@ package vct.rewrite.veymont import hre.util.ScopedStack import vct.col.ast.{ Choreography, + ChorRun, Class, Communicate, Endpoint, @@ -20,6 +21,8 @@ trait VeymontContext[Pre <: Generation] { choreographies.map { c => (c, c.collect { case comm: Communicate[Pre] => comm }.toIndexedSeq) }.toMap + lazy val runToChoreography: Map[ChorRun[Pre], Choreography[Pre]] = + choreographies.map { chor => (chor.run, chor) }.toMap lazy val allEndpoints = choreographies.flatMap { _.endpoints } lazy val endpointToChoreography: Map[Endpoint[Pre], Choreography[Pre]] = choreographies.flatMap { chor => chor.endpoints.map(ep => (ep, chor)) } @@ -35,12 +38,22 @@ trait VeymontContext[Pre <: Generation] { def communicatesOf(chor: Choreography[Pre]) = mappings.choreographyToCommunicates(chor) + def communicatesOf(run: ChorRun[Pre]): Seq[Communicate[Pre]] = + mappings.choreographyToCommunicates(mappings.runToChoreography(run)) + def communicatesOf(endpoint: Endpoint[Pre]): Seq[Communicate[Pre]] = + communicatesOf(choreographyOf(endpoint)) + .filter(_.participants.contains(endpoint)) def isEndpointClass(c: Class[Pre]): Boolean = mappings.endpointClassToEndpoint.contains(c) def choreographyOf(c: Class[Pre]): Choreography[Pre] = mappings.endpointToChoreography(mappings.endpointClassToEndpoint(c)) + def choreographyOf(endpoint: Endpoint[Pre]): Choreography[Pre] = + mappings.endpointToChoreography(endpoint) + def choreographyOf(run: ChorRun[Pre]): Choreography[Pre] = + mappings.runToChoreography(run) def endpointOf(c: Class[Pre]): Endpoint[Pre] = mappings.endpointClassToEndpoint(c) + def endpointsOf(run: ChorRun[Pre]) = choreographyOf(run).endpoints def isChoreographyParam(v: Variable[Pre]): Boolean = choreographies.exists { chor => chor.params.contains(v) } diff --git a/test/main/vct/test/integration/examples/TechnicalVeyMontExamplesSpec.scala b/test/main/vct/test/integration/examples/TechnicalVeyMontExamplesSpec.scala index dc009a0662..d0ff9aea8b 100644 --- a/test/main/vct/test/integration/examples/TechnicalVeyMontExamplesSpec.scala +++ b/test/main/vct/test/integration/examples/TechnicalVeyMontExamplesSpec.scala @@ -11,49 +11,46 @@ class TechnicalVeyMontExamplesSpec extends VercorsSpec { // vercors should verify using silicon flag "--veymont-generate-permissions" example s"$wd/checkLTS/ltstest.pvl" // vercors should verify using silicon flag "--veymont-generate-permissions" example s"$wd/checkLTS/simpleifelse.pvl" - (vercors - should error withCode "resolutionError:seqProgInvocation" - flag "--veymont-generate-permissions" - example s"$wd/checkMainSyntaxAndWellFormedness/ConstructorCall.pvl") + (vercors should error withCode "resolutionError:seqProgInvocation" flag + "--veymont-generate-permissions" example + s"$wd/checkMainSyntaxAndWellFormedness/ConstructorCall.pvl") // (vercors // should verify // using silicon flag "--veymont-generate-permissions" // example s"$wd/checkMainSyntaxAndWellFormedness/IfCondition.pvl") - (vercors - should verify - using silicon flag "--veymont-generate-permissions" - example s"$wd/checkMainSyntaxAndWellFormedness/MainConstructorWithArgs.pvl") + (vercors should verify using silicon flag + "--veymont-generate-permissions" example + s"$wd/checkMainSyntaxAndWellFormedness/MainConstructorWithArgs.pvl") // vercors should verify using silicon flag "--veymont-generate-permissions" example s"$wd/checkMainSyntaxAndWellFormedness/MainMethodCall.pvl" - vercors should verify using silicon flag "--veymont-generate-permissions" example s"$wd/checkMainSyntaxAndWellFormedness/NewNonRoleObject.pvl" - vercors should verify using silicon flag "--veymont-generate-permissions" example s"$wd/checkMainSyntaxAndWellFormedness/NewRoleObject.pvl" - - (vercors - should error - withCode "resolutionError:seqProgInvocation" - flag "--veymont-generate-permissions" - example s"$wd/checkMainSyntaxAndWellFormedness/NonRoleMethodCall.pvl") - - (vercors should error - withCode "resolutionError:seqProgInvocation" - flag "--veymont-generate-permissions" - example s"$wd/checkMainSyntaxAndWellFormedness/PureMethodCall.pvl") - - (vercors - should error withCode "resolutionError:seqProgEndpointAssign" - flags("--veymont-generate-permissions", "--dev-veymont-allow-assign") - example s"$wd/checkMainSyntaxAndWellFormedness/RoleFieldAssignment.pvl") - - (vercors - should error withCode "resolutionError:seqProgStatement" - flag "--veymont-generate-permissions" - example s"$wd/checkMainSyntaxAndWellFormedness/WaitStatement.pvl") - - (vercors - should fail withCode "loopUnanimityNotMaintained" - using silicon flag "--veymont-generate-permissions" - example s"$wd/checkMainSyntaxAndWellFormedness/WhileCondition.pvl") + vercors should verify using silicon flag + "--veymont-generate-permissions" example + s"$wd/checkMainSyntaxAndWellFormedness/NewNonRoleObject.pvl" + vercors should verify using silicon flag + "--veymont-generate-permissions" example + s"$wd/checkMainSyntaxAndWellFormedness/NewRoleObject.pvl" + + (vercors should error withCode "resolutionError:seqProgInvocation" flag + "--veymont-generate-permissions" example + s"$wd/checkMainSyntaxAndWellFormedness/NonRoleMethodCall.pvl") + + (vercors should error withCode "resolutionError:seqProgInvocation" flag + "--veymont-generate-permissions" example + s"$wd/checkMainSyntaxAndWellFormedness/PureMethodCall.pvl") + + (vercors should error withCode "resolutionError:seqProgEndpointAssign" flags + ("--veymont-generate-permissions", "--dev-veymont-allow-assign") example + s"$wd/checkMainSyntaxAndWellFormedness/RoleFieldAssignment.pvl") + + (vercors should error withCode "resolutionError:chorStatement" flag + "--veymont-generate-permissions" example + s"$wd/checkMainSyntaxAndWellFormedness/WaitStatement.pvl") + + // TODO (RR): Re-enable once loop unanimity is re-implemented +// (vercors should fail withCode "loopUnanimityNotMaintained" using silicon flag +// "--veymont-generate-permissions" example +// s"$wd/checkMainSyntaxAndWellFormedness/WhileCondition.pvl") // (vercors // should error @@ -65,8 +62,12 @@ class TechnicalVeyMontExamplesSpec extends VercorsSpec { // flag "--veymont-generate-permissions" // example s"$wd/checkMainSyntaxAndWellFormedness/WrongSyntax.pvl") - vercors should verify using silicon flag "--veymont-generate-permissions" example s"$wd/checkTypesNonMain/RoleFieldType2.pvl" - vercors should verify using silicon flag "--veymont-generate-permissions" example s"$wd/checkTypesNonMain/RoleMethodType4.pvl" + vercors should verify using silicon flag + "--veymont-generate-permissions" example + s"$wd/checkTypesNonMain/RoleFieldType2.pvl" + vercors should verify using silicon flag + "--veymont-generate-permissions" example + s"$wd/checkTypesNonMain/RoleMethodType4.pvl" // vercors should verify using silicon flag "--veymont-generate-permissions" example s"$wd/various.pvl" } diff --git a/test/main/vct/test/integration/examples/TechnicalVeyMontSpec.scala b/test/main/vct/test/integration/examples/TechnicalVeyMontSpec.scala index e3bb8854e8..59aef65a31 100644 --- a/test/main/vct/test/integration/examples/TechnicalVeyMontSpec.scala +++ b/test/main/vct/test/integration/examples/TechnicalVeyMontSpec.scala @@ -6,17 +6,13 @@ import vct.options.Options import vct.options.types.Verbosity import vct.test.integration.helper.VercorsSpec -class TechnicalVeyMontSpec extends VercorsSpec { - // TODO (RR): Re-enable tests asap - - // TODO (RR): Add (\in_chor alice.x == bob.x) to this test - (vercors - should verify - using silicon - flag "--veymont-generate-permissions" - in "example using communicate" - pvl - """ +class TechnicalVeyMontSpec + extends VercorsSpec { + // TODO (RR): Re-enable tests asap + + // TODO (RR): Add (\in_chor alice.x == bob.x) to this test + (vercors should verify using silicon flag + "--veymont-generate-permissions" in "example using communicate" pvl """ class Storage { int x; } @@ -31,14 +27,9 @@ class TechnicalVeyMontSpec extends VercorsSpec { } """) - (vercors - should fail - withCode "assertFailed:false" - using silicon - flag "--veymont-generate-permissions" - in "plain endpoint field dereference should be possible" - pvl - """ + (vercors should fail withCode "assertFailed:false" using silicon flag + "--veymont-generate-permissions" in + "plain endpoint field dereference should be possible" pvl """ class Storage { int x; } @@ -51,8 +42,8 @@ class TechnicalVeyMontSpec extends VercorsSpec { } """) - vercors should error withCode "noSuchName" in "non-existent thread name in communicate fails" pvl - """ + vercors should error withCode "noSuchName" in + "non-existent thread name in communicate fails" pvl """ choreography Example() { run { communicate charlie.x <- charlie.x; @@ -60,8 +51,8 @@ class TechnicalVeyMontSpec extends VercorsSpec { } """ - vercors should error withCode "noSuchName" in "non-existent field in communicate fails" pvl - """ + vercors should error withCode "noSuchName" in + "non-existent field in communicate fails" pvl """ class Storage { int x; } choreography Example() { endpoint charlie = Storage(); @@ -71,8 +62,8 @@ class TechnicalVeyMontSpec extends VercorsSpec { } """ - vercors should error withCode "parseError" in "parameterized sends not yet supported " pvl - """ + vercors should error withCode "parseError" in + "parameterized sends not yet supported " pvl """ class Storage { int x; } choreography Example() { endpoint alice[10] = Storage(); @@ -83,25 +74,20 @@ class TechnicalVeyMontSpec extends VercorsSpec { } """ - vercors should error withCode "noRunMethod" in "run method should always be present" pvl - """ + vercors should error withCode "noRunMethod" in + "run method should always be present" pvl """ choreography Example() { } """ - vercors should error withCode "parseError" in "endpoints can only have class types" pvl - """ + vercors should error withCode "parseError" in + "endpoints can only have class types" pvl """ choreography Example() { endpoint alice = int(); } """ - (vercors - should verify - using silicon - flag "--veymont-generate-permissions" - in "Endpoint fields should be assignable" - pvl - """ + (vercors should verify using silicon flag "--veymont-generate-permissions" in + "Endpoint fields should be assignable" pvl """ class Storage { int x; int y; } choreography Example() { endpoint alice = Storage(); @@ -112,8 +98,8 @@ class TechnicalVeyMontSpec extends VercorsSpec { } """) - vercors should error withCode "resolutionError:seqProgInstanceMethodArgs" in "instance method in choreography cannot have arguments" pvl - """ + vercors should error withCode "resolutionError:seqProgInstanceMethodArgs" in + "instance method in choreography cannot have arguments" pvl """ choreography Example() { void m(int x) { } @@ -121,8 +107,8 @@ class TechnicalVeyMontSpec extends VercorsSpec { } """ - vercors should error withCode "resolutionError:seqProgInstanceMethodBody" in "instance method in choreography must have a body" pvl - """ + vercors should error withCode "resolutionError:seqProgInstanceMethodBody" in + "instance method in choreography must have a body" pvl """ choreography Example() { void m(); @@ -130,8 +116,9 @@ class TechnicalVeyMontSpec extends VercorsSpec { } """ - vercors should error withCode "resolutionError:seqProgInstanceMethodNonVoid" in "instance method in choreography must have void return type" pvl - """ + vercors should error withCode + "resolutionError:seqProgInstanceMethodNonVoid" in + "instance method in choreography must have void return type" pvl """ choreography Example() { int m() { } @@ -139,8 +126,8 @@ class TechnicalVeyMontSpec extends VercorsSpec { } """ - vercors should error withCode "resolutionError:seqProgStatement" in "seq_prog excludes certain statements" pvl - """ + vercors should error withCode "resolutionError:chorStatement" in + "`choreography` excludes certain statements" pvl """ class C { } choreography Example(C c) { run { @@ -170,8 +157,8 @@ class TechnicalVeyMontSpec extends VercorsSpec { } """ - vercors should error withCode "resolutionError:type" in "Assign must be well-typed" pvl - """ + vercors should error withCode "resolutionError:type" in + "Assign must be well-typed" pvl """ class C { int x; } choreography C() { endpoint charlie = C(); @@ -181,8 +168,8 @@ class TechnicalVeyMontSpec extends VercorsSpec { } """ - vercors should error withCode "resolutionError:type" in "Communicating parties must agree on the type" pvl - """ + vercors should error withCode "resolutionError:type" in + "Communicating parties must agree on the type" pvl """ class C { int c; } class A { bool a; } choreography C() { @@ -194,13 +181,8 @@ class TechnicalVeyMontSpec extends VercorsSpec { } """ - (vercors - should verify - using silicon - flag "--veymont-generate-permissions" - in "assignment should work" - pvl - """ + (vercors should verify using silicon flag "--veymont-generate-permissions" in + "assignment should work" pvl """ class Storage { int x; @@ -220,14 +202,8 @@ class TechnicalVeyMontSpec extends VercorsSpec { } """) - (vercors - should fail - withCode "postFailed:false" - using silicon - flag "--veymont-generate-permissions" - in "Postcondition of run can fail" - pvl - """ + (vercors should fail withCode "postFailed:false" using silicon flag + "--veymont-generate-permissions" in "Postcondition of run can fail" pvl """ class Storage { int x; } @@ -240,14 +216,9 @@ class TechnicalVeyMontSpec extends VercorsSpec { } """) - (vercors - should fail - withCode "postFailed:false" - using silicon - flag "--veymont-generate-permissions" - in "Postcondition of choreography can fail" - pvl - """ + (vercors should fail withCode "postFailed:false" using silicon flag + "--veymont-generate-permissions" in + "Postcondition of choreography can fail" pvl """ class Storage { int x; } @@ -261,6 +232,7 @@ class TechnicalVeyMontSpec extends VercorsSpec { } """) + // TODO (RR): Test makes sense if you generate permissions...? // vercors should error withCode "resolutionError:seqProgReceivingEndpoint" in "Assignment statement only allows one endpoint in the assigned expression" pvl // """ // class Storage { @@ -276,61 +248,49 @@ class TechnicalVeyMontSpec extends VercorsSpec { // } // """ - (vercors - should fail - withCode "branchNotUnanimous" - using silicon - flag "--veymont-generate-permissions" - in "Parts of condition in branch have to agree inside seqprog" - pvl - """ - class Storage { - int x; - } - choreography Example() { - endpoint alice = Storage(); - endpoint bob = Storage(); - - run { - if (alice.x == 0 && bob.x == 0) { - // Alice might go here, bob might not: error - } - } - } - """) - - (vercors - should fail - withCode "branchNotUnanimous" - using silicon - flag "--veymont-generate-permissions" - in "Parts of condition in branch have to agree inside seqprog, including conditions for all endpoints" - pvl - """ - class Storage { - int x; - } - - pure int f() = 3; - - choreography Example() { - endpoint alice = Storage(); - endpoint bob = Storage(); + // TODO (RR): Re-implement proper error reporting for branch unanimity +// (vercors should fail withCode "branchNotUnanimous" using silicon flag +// "--veymont-generate-permissions" in +// "Parts of condition in branch have to agree inside seqprog" pvl """ +// class Storage { +// int x; +// } +// choreography Example() { +// endpoint alice = Storage(); +// endpoint bob = Storage(); +// +// run { +// if (alice.x == 0 && bob.x == 0) { +// // Alice might go here, bob might not: error +// } +// } +// } +// """) - run { - if (alice.x == 0 && f() == 3) { - // Alice might go here, bob will definitely, because of the second expression: error - } - } - } - """) +// (vercors should fail withCode "branchNotUnanimous" using silicon flag +// "--veymont-generate-permissions" in +// "Parts of condition in branch have to agree inside seqprog, including conditions for all endpoints" pvl +// """ +// class Storage { +// int x; +// } +// +// pure int f() = 3; +// +// choreography Example() { +// endpoint alice = Storage(); +// endpoint bob = Storage(); +// +// run { +// if (alice.x == 0 && f() == 3) { +// // Alice might go here, bob will definitely, because of the second expression: error +// } +// } +// } +// """) - (vercors - should verify - using silicon - flag "--veymont-generate-permissions" - in "If there is only one endpoint, the conditions don't have to agree, as there is only one endpoint" - pvl + (vercors should verify using silicon flag "--veymont-generate-permissions" in + "If there is only one endpoint, the conditions don't have to agree, as there is only one endpoint" pvl """ class Storage { int x; @@ -349,8 +309,8 @@ class TechnicalVeyMontSpec extends VercorsSpec { } """) - vercors should error withCode "seqProgParticipantErrors" in "`if` cannot depend on bob, inside an `if` depending on alice" pvl - """ + vercors should error withCode "seqProgParticipantErrors" in + "`if` cannot depend on bob, inside an `if` depending on alice" pvl """ class Storage { int x; } @@ -368,8 +328,8 @@ class TechnicalVeyMontSpec extends VercorsSpec { } """ - vercors should error withCode "seqProgParticipantErrors" in "If alice branches, bob cannot communicate" pvl - """ + vercors should error withCode "seqProgParticipantErrors" in + "If alice branches, bob cannot communicate" pvl """ class Storage { int x; } @@ -402,13 +362,8 @@ class TechnicalVeyMontSpec extends VercorsSpec { // } // """ - (vercors - should verify - using silicon - flag "--veymont-generate-permissions" - in "Programs where branch conditions agree should verify" - pvl - """ + (vercors should verify using silicon flag "--veymont-generate-permissions" in + "Programs where branch conditions agree should verify" pvl """ class Storage { bool x; } @@ -427,53 +382,45 @@ class TechnicalVeyMontSpec extends VercorsSpec { } """) - (vercors - should fail - withCode "loopUnanimityNotEstablished" - using silicon - flag "--veymont-generate-permissions" - in "Programs where branch condition unanimity cannot be established should fail" - pvl - """ - class Storage { - bool x; - } - choreography Example() { - endpoint alice = Storage(); - endpoint bob = Storage(); - - run { - while (alice.x && bob.x) { - - } - } - } - """) - - (vercors - should fail - withCode "loopUnanimityNotMaintained" - using silicon - flag "--veymont-generate-permissions" - in "Programs where branch condition unanimity cannot be maintained should fail" - pvl - """ - class Storage { - bool x; - } - choreography Example() { - endpoint alice = Storage(); - endpoint bob = Storage(); +// (vercors should fail withCode "loopUnanimityNotEstablished" using silicon flag +// "--veymont-generate-permissions" in +// "Programs where branch condition unanimity cannot be established should fail" pvl +// """ +// class Storage { +// bool x; +// } +// choreography Example() { +// endpoint alice = Storage(); +// endpoint bob = Storage(); +// +// run { +// while (alice.x && bob.x) { +// +// } +// } +// } +// """) - run { - alice.x := true; - bob.x := true; - while (alice.x && bob.x) { - alice.x := false; - } - } - } - """) +// (vercors should fail withCode "loopUnanimityNotMaintained" using silicon flag +// "--veymont-generate-permissions" in +// "Programs where branch condition unanimity cannot be maintained should fail" pvl +// """ +// class Storage { +// bool x; +// } +// choreography Example() { +// endpoint alice = Storage(); +// endpoint bob = Storage(); +// +// run { +// alice.x := true; +// bob.x := true; +// while (alice.x && bob.x) { +// alice.x := false; +// } +// } +// } +// """) // vercors should error withCode "seqProgParticipantErrors" in "Loops should also limit the number of participants" pvl // """ @@ -496,12 +443,8 @@ class TechnicalVeyMontSpec extends VercorsSpec { // } // """ - (vercors - should verify - using silicon - flag "--veymont-generate-permissions" - in "Loops should also limit the number of participants when combined with branches" - pvl + (vercors should verify using silicon flag "--veymont-generate-permissions" in + "Loops should also limit the number of participants when combined with branches" pvl """ class Storage { bool x; @@ -548,11 +491,8 @@ class TechnicalVeyMontSpec extends VercorsSpec { // } // """ - (vercors should verify - using silicon - flag "--veymont-generate-permissions" - in "Permission should be generated for constructors as well" pvl - """ + (vercors should verify using silicon flag "--veymont-generate-permissions" in + "Permission should be generated for constructors as well" pvl """ class Storage { int x; @@ -591,12 +531,8 @@ class TechnicalVeyMontSpec extends VercorsSpec { // } // """) - (vercors - should fail - withCode "seqAssignPerm" - using silicon - in "When no permission is generated, a failure should occur on seq assign field access" - pvl + (vercors should fail withCode "seqAssignPerm" using silicon in + "When no permission is generated, a failure should occur on seq assign field access" pvl """ class Storage { int x; @@ -610,12 +546,8 @@ class TechnicalVeyMontSpec extends VercorsSpec { } """) - (vercors - should verify - using silicon - flag "--veymont-generate-permissions" - in "Permissions are generated for loop invariants, procedures, functions, instance methods, instance functions" - pvl + (vercors should verify using silicon flag "--veymont-generate-permissions" in + "Permissions are generated for loop invariants, procedures, functions, instance methods, instance functions" pvl """ class Storage { int x; @@ -660,12 +592,8 @@ class TechnicalVeyMontSpec extends VercorsSpec { } """) - (vercors - should verify - using silicon - flag "--veymont-generate-permissions" - in "Permission generation should only generate permissions that are strictly necessary" - pvl + (vercors should verify using silicon flag "--veymont-generate-permissions" in + "Permission generation should only generate permissions that are strictly necessary" pvl """ class Storage { int x; @@ -775,41 +703,41 @@ class TechnicalVeyMontSpec extends VercorsSpec { // } // """) - (vercors - should fail - withCode "seqRunPreFailed:false" - using silicon - in "Precondition of run should be checked" - pvl - """ + (vercors should fail withCode "seqRunPreFailed:false" using silicon in + "Precondition of run should be checked" pvl """ + class C { } choreography Example(int x) { + endpoint c = C(); requires x == 0; run { } } """) - (vercors - should fail - withCode "seqRunContextPreFailed:false" - using silicon - in "Context everywhere of run should be checked" - pvl - """ + // TODO (RR): Decide on a sane interpretation for this before the artefact deadline +// (vercors should fail withCode "seqRunPreFailed:false" using silicon in +// "Precondition of run should be checked, also when there are no endpoints" pvl +// """ +// choreography Example(int x) { +// requires x == 0; +// run { +// } +// } +// """) + + (vercors should fail withCode "seqRunContextPreFailed:false" using silicon in + "Context everywhere of run should be checked" pvl """ + class C {} choreography Example(int x) { + endpoint c = C(); context_everywhere x == 0; run { } } """) - (vercors - should fail - withCode "endpointPreFailed:false" - using silicon - in "Precondition of endpoint constructor should be checked" - pvl - """ + (vercors should fail withCode "endpointPreFailed:false" using silicon in + "Precondition of endpoint constructor should be checked" pvl """ class Storage { requires x == 0; constructor (int x) { } @@ -826,13 +754,10 @@ class TechnicalVeyMontSpec extends VercorsSpec { /* TODO (RR): This test used to be failing, as there was separate syntax for assignment in choreographies. After some refactorings, it is not clear if this syntax is still needed. I think this will be resolved in a few weeks from now. For now, this test is just marked as succeeding. */ - (vercors - should verify + (vercors should verify // withCode "assignNotAllowed" - using silicon - flag "--veymont-generate-permissions" - in "Contrary to historical precedent, assignment should be allowed in choreographies" - pvl + using silicon flag "--veymont-generate-permissions" in + "Contrary to historical precedent, assignment should be allowed in choreographies" pvl """ class Storage { int x; @@ -846,14 +771,9 @@ class TechnicalVeyMontSpec extends VercorsSpec { } """) - (vercors - should verify - using silicon - flag "--veymont-generate-permissions" - flag "--dev-veymont-allow-assign" - in "At user discretion, assignment should be allowed" - pvl - """ + (vercors should verify using silicon flag + "--veymont-generate-permissions" flag "--dev-veymont-allow-assign" in + "At user discretion, assignment should be allowed" pvl """ class Storage { int x; } @@ -866,14 +786,9 @@ class TechnicalVeyMontSpec extends VercorsSpec { } """) - (vercors - should fail - withCode "participantsNotDistinct" - using silicon - flag "--veymont-generate-permissions" - in "Endpoints participating in a communicate should be distinct" - pvl - """ + (vercors should fail withCode "participantsNotDistinct" using silicon flag + "--veymont-generate-permissions" in + "Endpoints participating in a communicate should be distinct" pvl """ class Storage { int x; } @@ -886,5 +801,7 @@ class TechnicalVeyMontSpec extends VercorsSpec { } """) - vercors should verify using silicon flag "--veymont-generate-permissions" example "technical/veymont/genericEndpoints.pvl" -} \ No newline at end of file + vercors should verify using silicon flag + "--veymont-generate-permissions" example + "technical/veymont/genericEndpoints.pvl" +} diff --git a/test/main/vct/test/integration/examples/VeyMontExamplesSpec.scala b/test/main/vct/test/integration/examples/VeyMontExamplesSpec.scala index eb2570dbea..2082081d9e 100644 --- a/test/main/vct/test/integration/examples/VeyMontExamplesSpec.scala +++ b/test/main/vct/test/integration/examples/VeyMontExamplesSpec.scala @@ -4,14 +4,17 @@ import vct.test.integration.helper.VercorsSpec class VeyMontExamplesSpec extends VercorsSpec { val wd = "concepts/veymont/generatedPermissions" - vercors should verify using silicon flags "--veymont-generate-permissions" examples( - s"$wd/TicTacToe/Player.pvl", - s"$wd/TicTacToe/Move.pvl", - s"$wd/TicTacToe/TicTacToe.pvl", + vercors should verify using silicon flags + "--veymont-generate-permissions" examples + ( + s"$wd/TicTacToe/Player.pvl", + s"$wd/TicTacToe/Move.pvl", + s"$wd/TicTacToe/TicTacToe.pvl", ) - vercors should verify using silicon flags "--veymont-generate-permissions" example s"$wd/leaderelectring.pvl" - vercors should verify using silicon flags "--veymont-generate-permissions" example s"$wd/leaderelectstar.pvl" - vercors should verify using silicon flags "--veymont-generate-permissions" example s"$wd/paperscissorsrock.pvl" - vercors should verify using silicon flags "--veymont-generate-permissions" example s"$wd/parallel_while.pvl" + // TODO (RR): Re-enable after stratified permissions are done + // vercors should verify using silicon flags "--veymont-generate-permissions" example s"$wd/leaderelectring.pvl" + // vercors should verify using silicon flags "--veymont-generate-permissions" example s"$wd/leaderelectstar.pvl" + // vercors should verify using silicon flags "--veymont-generate-permissions" example s"$wd/paperscissorsrock.pvl" + // vercors should verify using silicon flags "--veymont-generate-permissions" example s"$wd/parallel_while.pvl" // vercors should verify using silicon flags "--veymont-generate-permissions" example s"$wd/spectral-norm.pvl" } diff --git a/test/main/vct/test/integration/examples/VeyMontToolPaperSpec.scala b/test/main/vct/test/integration/examples/VeyMontToolPaperSpec.scala index 1843a7684d..02a9711b08 100644 --- a/test/main/vct/test/integration/examples/VeyMontToolPaperSpec.scala +++ b/test/main/vct/test/integration/examples/VeyMontToolPaperSpec.scala @@ -9,14 +9,15 @@ class VeyMontToolPaperSpec extends VercorsSpec { val applicability = s"$wd/applicability" val paperExamples = s"$wd/paper-examples" - vercors should verify using silicon flag "--veymont-generate-permissions" example s"$applicability/2pc-3.pvl" - vercors should verify using silicon flag "--veymont-generate-permissions" example s"$applicability/2pc-5.pvl" - vercors should verify using silicon flag "--veymont-generate-permissions" example s"$applicability/consensus-3.pvl" - vercors should verify using silicon flag "--veymont-generate-permissions" example s"$applicability/consensus-5.pvl" - vercors should verify using silicon flag "--veymont-generate-permissions" example s"$applicability/election-3.pvl" - vercors should verify using silicon flag "--veymont-generate-permissions" example s"$applicability/election-5.pvl" + // vercors should verify using silicon flag "--veymont-generate-permissions" example s"$applicability/2pc-3.pvl" + // vercors should verify using silicon flag "--veymont-generate-permissions" example s"$applicability/2pc-5.pvl" + // vercors should verify using silicon flag "--veymont-generate-permissions" example s"$applicability/consensus-3.pvl" + // vercors should verify using silicon flag "--veymont-generate-permissions" example s"$applicability/consensus-5.pvl" + // vercors should verify using silicon flag "--veymont-generate-permissions" example s"$applicability/election-3.pvl" + // vercors should verify using silicon flag "--veymont-generate-permissions" example s"$applicability/election-5.pvl" + + // vercors should verify using silicon flag "--veymont-generate-permissions" example s"$paperExamples/veymont-swap.pvl" - vercors should verify using silicon flag "--veymont-generate-permissions" example s"$paperExamples/veymont-swap.pvl" // Slow because of generated permisissions. Can fix when VeyMont has permission support. // (vercors // should verify diff --git a/test/main/vct/test/integration/helper/ExampleFiles.scala b/test/main/vct/test/integration/helper/ExampleFiles.scala index cbc96f17e8..bcd393286f 100644 --- a/test/main/vct/test/integration/helper/ExampleFiles.scala +++ b/test/main/vct/test/integration/helper/ExampleFiles.scala @@ -10,14 +10,11 @@ case object ExampleFiles { "examples/archive/", "examples/concepts/resourceValues", "examples/technical/veymont", - "examples/concepts/veymont" + "examples/concepts/veymont", + "examples/publications/2023/VeyMontToolPaper", ).map(_.replaceAll("/", File.separator)) - val IGNORE_EXTS: Seq[String] = Seq( - ".h", - ".bib", - ".xml", - ) + val IGNORE_EXTS: Seq[String] = Seq(".h", ".bib", ".xml") val IGNORE_FILES: Set[String] = Set( ".gitignore", @@ -45,9 +42,11 @@ case object ExampleFiles { val FILES: Seq[Path] = find(Paths.get("examples")) def find(directory: Path): Seq[Path] = - Files.list(directory) - .toScala(Seq) - .filterNot(f => EXCLUSIONS.exists(_(f))) - .sortBy(_.getFileName.toString) - .flatMap(f => if(Files.isDirectory(f)) find(f) else Seq(f)) + Files.list(directory).toScala(Seq).filterNot(f => EXCLUSIONS.exists(_(f))) + .sortBy(_.getFileName.toString).flatMap(f => + if (Files.isDirectory(f)) + find(f) + else + Seq(f) + ) }