Skip to content

Commit 0239420

Browse files
committed
Check for multiple init/next blocks
Remove code that picks only one init block when combining modules this was previously misleading because the order the init block statements were combined in isn't obvious to the user.
1 parent 20fcff9 commit 0239420

File tree

5 files changed

+96
-15
lines changed

5 files changed

+96
-15
lines changed

src/main/scala/uclid/UclidMain.scala

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -389,21 +389,10 @@ object UclidMain {
389389
(acc, module) => {
390390
val declsP = {
391391
val nonInitAccDecls = acc.decls.filter(p => !p.isInstanceOf[InitDecl])
392-
val initAccDecls = acc.decls.filter(p => p.isInstanceOf[InitDecl]).headOption
392+
val initAccDecls = acc.decls.filter(p => p.isInstanceOf[InitDecl])
393393
val nonInitModuleDecls = module.decls.filter(p => !p.isInstanceOf[InitDecl])
394-
val initModuleDecls = module.decls.filter(p => p.isInstanceOf[InitDecl]).headOption
395-
val newInitDecl = initAccDecls match {
396-
case Some(initAcc) => initModuleDecls match {
397-
case Some(initMod) => List(InitDecl(BlockStmt(List[BlockVarsDecl](),
398-
List(initAcc.asInstanceOf[InitDecl].body, initMod.asInstanceOf[InitDecl].body))))
399-
case None => List(initAcc)
400-
}
401-
case None => initModuleDecls match {
402-
case Some(initMod) => List(initMod)
403-
case None => List[Decl]()
404-
}
405-
}
406-
newInitDecl ++ nonInitAccDecls ++ nonInitModuleDecls
394+
val initModuleDecls = module.decls.filter(p => p.isInstanceOf[InitDecl])
395+
initAccDecls ++ initModuleDecls ++ nonInitAccDecls ++ nonInitModuleDecls
407396
}
408397
val cmdsP = (acc.cmds ++ module.cmds)
409398
Utils.assert(module.notes.size == 1 && module.notes.head.asInstanceOf[InstanceVarMapAnnotation].iMap.size == 0, "Expected module to initially have empty annotations.")

src/main/scala/uclid/lang/ModuleCanonicalizer.scala

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,28 @@ package lang
4242

4343
class ModuleCanonicalizerPass extends RewritePass {
4444
override def rewriteModule(moduleIn : Module, ctx : Scope) : Option[Module] = {
45+
// Currently we only allow one init and one next block in a module.
46+
// we could alternatively, combine multiple init (or next) blocks into a single init (or next) block.
47+
var errors: List[(String, lang.ASTPosition)] = List.empty
48+
val initdecls = moduleIn.decls.filter(_.isInstanceOf[InitDecl]).map(_.asInstanceOf[InitDecl])
49+
if(initdecls.size > 1) {
50+
val msg = "Module has multiple init blocks. This is not allowed."
51+
val position = initdecls(1).position
52+
errors = errors :+ (msg, position)
53+
}
54+
val nextdecls = moduleIn.decls.filter(_.isInstanceOf[NextDecl]).map(_.asInstanceOf[NextDecl])
55+
if(nextdecls.size > 1) {
56+
val msg = "Module has multiple next blocks. This is not allowed."
57+
val position = nextdecls(1).position
58+
errors = errors :+ (msg, position)
59+
}
60+
if(errors.nonEmpty) {
61+
throw throw new Utils.ParserErrorList(errors)
62+
}
63+
4564
val initP = moduleIn.init match {
4665
case None => Some(InitDecl(SkipStmt()))
47-
case Some(_) => None
66+
case Some(_) => None
4867
}
4968
val nextP = moduleIn.next match {
5069
case None => Some(NextDecl(SkipStmt()))

src/test/scala/ParserSpec.scala

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,28 @@ class ParserSpec extends AnyFlatSpec {
258258
assert (p.errors.size == 5)
259259
}
260260
}
261+
"test-multiple-init-blocks.ucl" should "not parse successfully." in {
262+
try {
263+
val fileModules = UclidMain.compile(ConfigCons.createConfig("test/test-multiple-init-blocks.ucl"), lang.Identifier("main"))
264+
// should never get here.
265+
assert (false);
266+
}
267+
catch {
268+
case p : Utils.ParserErrorList =>
269+
assert (p.errors.size == 1)
270+
}
271+
}
272+
"test-multiple-next-blocks.ucl" should "not parse successfully." in {
273+
try {
274+
val fileModules = UclidMain.compile(ConfigCons.createConfig("test/test-multiple-next-blocks.ucl"), lang.Identifier("main"))
275+
// should never get here.
276+
assert (false);
277+
}
278+
catch {
279+
case p : Utils.ParserErrorList =>
280+
assert (p.errors.size == 1)
281+
}
282+
}
261283
"test-typechecker-6.ucl" should "not parse successfully." in {
262284
try {
263285
val fileModules = UclidMain.compile(ConfigCons.createConfig("test/test-typechecker-6.ucl"), lang.Identifier("main"))

test/test-multiple-init-blocks.ucl

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// should throw an error because there are multiple init blocks in the module
2+
module main {
3+
var x : integer;
4+
5+
init {
6+
x = 0;
7+
}
8+
9+
init {
10+
x = 1;
11+
}
12+
13+
next{
14+
15+
}
16+
17+
invariant prop_eq: x == 0;
18+
19+
control {
20+
v = bmc(5);
21+
check;
22+
print_results;
23+
}
24+
25+
}

test/test-multiple-next-blocks.ucl

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// should throw an error because there are multiple next blocks in the module
2+
module main {
3+
var x : integer;
4+
var y : integer;
5+
6+
init {
7+
x = 0;
8+
y = 0;
9+
}
10+
11+
next {
12+
x' = x + 1;
13+
}
14+
15+
next {
16+
y' = y + 2;
17+
}
18+
invariant prop_eq: x != 0;
19+
20+
control {
21+
v = bmc(5);
22+
check;
23+
print_results;
24+
v.print_cex(x);
25+
}
26+
}

0 commit comments

Comments
 (0)