From dd92f7b12c1294e08ad176c89be93457b070e03f Mon Sep 17 00:00:00 2001 From: Shuvendu Lahiri Date: Sun, 19 Jul 2015 00:26:45 -0700 Subject: fix for /deterministicExtractLoops sed in SymDiff --- Source/Core/Absy.cs | 49 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/Source/Core/Absy.cs b/Source/Core/Absy.cs index 36c99b7b..c2e68002 100644 --- a/Source/Core/Absy.cs +++ b/Source/Core/Absy.cs @@ -733,6 +733,32 @@ namespace Microsoft.Boogie { } } + + /// + /// Finds blocks that break out of a loop in NaturalLoops(header, backEdgeNode) + /// + /// + /// + /// + private HashSet GetBreakBlocksOfLoop(Block header, Block backEdgeNode, Graph/*!*/ g) + { + Contract.Assert(CommandLineOptions.Clo.DeterministicExtractLoops, "Can only be called with /deterministicExtractLoops option"); + var immSuccBlks = new HashSet(); + var loopBlocks = g.NaturalLoops(header, backEdgeNode); + foreach (Block/*!*/ block in loopBlocks) + { + Contract.Assert(block != null); + var auxCmd = block.TransferCmd as GotoCmd; + if (auxCmd == null) continue; + foreach(var bl in auxCmd.labelTargets) + { + if (loopBlocks.Contains(bl)) continue; + immSuccBlks.Add(bl); + } + } + return immSuccBlks; + } + void CreateProceduresForLoops(Implementation impl, Graph/*!*/ g, List/*!*/ loopImpls, Dictionary> fullMap) { @@ -788,7 +814,13 @@ namespace Microsoft.Boogie { foreach (Block/*!*/ b in g.BackEdgeNodes(header)) { Contract.Assert(b != null); - foreach (Block/*!*/ block in g.NaturalLoops(header, b)) + HashSet immSuccBlks = new HashSet(); + if (detLoopExtract) + { + //Need to get the blocks that exit the loop, as we need to add them to targets and footprint + immSuccBlks = GetBreakBlocksOfLoop(header, b, g); + } + foreach (Block/*!*/ block in g.NaturalLoops(header, b).Union(immSuccBlks)) { Contract.Assert(block != null); foreach (Cmd/*!*/ cmd in block.Cmds) @@ -943,18 +975,15 @@ namespace Microsoft.Boogie { GotoCmd auxGotoCmd = block.TransferCmd as GotoCmd; Contract.Assert(auxGotoCmd != null && auxGotoCmd.labelNames != null && auxGotoCmd.labelTargets != null && auxGotoCmd.labelTargets.Count >= 1); + var blksThatBreakOut = GetBreakBlocksOfLoop(header, source, g); + var loopNodes = g.NaturalLoops(header, source); foreach(var bl in auxGotoCmd.labelTargets) { - bool found = false; - foreach(var n in g.NaturalLoops(header, source)) { //very expensive, can we do a contains? - if (bl == n) { //clarify: is this the right comparison? - found = true; - break; - } - } - if (!found) { + if (!loopNodes.Contains(bl)) { Block auxNewBlock = new Block(); auxNewBlock.Label = ((Block)bl).Label; - auxNewBlock.Cmds = codeCopier.CopyCmdSeq(((Block)bl).Cmds); + //these blocks may have read/write locals that are not present in naturalLoops + //we need to capture these variables + auxNewBlock.Cmds = codeCopier.CopyCmdSeq(((Block)bl).Cmds); //add restoration code for such blocks if (loopHeaderToAssignCmd.ContainsKey(header)) { -- cgit v1.2.3 From a030a9cd291e5d17d09a8d1921ae5a9c9c01c2dd Mon Sep 17 00:00:00 2001 From: Valentin Wüstholz Date: Mon, 20 Jul 2015 07:12:43 +0200 Subject: Added more tests. --- Test/snapshots/Snapshots38.v0.bpl | 13 ++++++++ Test/snapshots/Snapshots38.v1.bpl | 14 ++++++++ Test/snapshots/Snapshots38.v2.bpl | 14 ++++++++ Test/snapshots/Snapshots39.v0.bpl | 13 ++++++++ Test/snapshots/Snapshots39.v1.bpl | 14 ++++++++ Test/snapshots/Snapshots39.v2.bpl | 14 ++++++++ Test/snapshots/runtest.snapshot | 2 +- Test/snapshots/runtest.snapshot.expect | 60 ++++++++++++++++++++++++++++++++++ 8 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 Test/snapshots/Snapshots38.v0.bpl create mode 100644 Test/snapshots/Snapshots38.v1.bpl create mode 100644 Test/snapshots/Snapshots38.v2.bpl create mode 100644 Test/snapshots/Snapshots39.v0.bpl create mode 100644 Test/snapshots/Snapshots39.v1.bpl create mode 100644 Test/snapshots/Snapshots39.v2.bpl diff --git a/Test/snapshots/Snapshots38.v0.bpl b/Test/snapshots/Snapshots38.v0.bpl new file mode 100644 index 00000000..496a75a9 --- /dev/null +++ b/Test/snapshots/Snapshots38.v0.bpl @@ -0,0 +1,13 @@ +procedure {:checksum "-1"} Callee(); + +implementation {:id "Callee"} {:checksum "0"} Callee() +{ + var r: int; + + call r := Sum(42); + assert r != 0; +} + +procedure {:checksum "1"} Sum(n: int) returns (r: int); + requires 0 <= n; + ensures n != 0 ==> 1 <= r; diff --git a/Test/snapshots/Snapshots38.v1.bpl b/Test/snapshots/Snapshots38.v1.bpl new file mode 100644 index 00000000..062b22ea --- /dev/null +++ b/Test/snapshots/Snapshots38.v1.bpl @@ -0,0 +1,14 @@ +procedure {:checksum "-1"} Callee(); + +implementation {:id "Callee"} {:checksum "2"} Callee() +{ + var r: int; + + call r := Sum(42); + assert r != 0; + assert 42 <= r; +} + +procedure {:checksum "1"} Sum(n: int) returns (r: int); + requires 0 <= n; + ensures n != 0 ==> 1 <= r; diff --git a/Test/snapshots/Snapshots38.v2.bpl b/Test/snapshots/Snapshots38.v2.bpl new file mode 100644 index 00000000..5c4b69d6 --- /dev/null +++ b/Test/snapshots/Snapshots38.v2.bpl @@ -0,0 +1,14 @@ +procedure {:checksum "-1"} Callee(); + +implementation {:id "Callee"} {:checksum "2"} Callee() +{ + var r: int; + + call r := Sum(42); + assert r != 0; + assert 42 <= r; +} + +procedure {:checksum "3"} Sum(n: int) returns (r: int); + requires 0 <= n; + ensures n != 0 ==> n <= r; diff --git a/Test/snapshots/Snapshots39.v0.bpl b/Test/snapshots/Snapshots39.v0.bpl new file mode 100644 index 00000000..083d497e --- /dev/null +++ b/Test/snapshots/Snapshots39.v0.bpl @@ -0,0 +1,13 @@ +procedure {:checksum "-1"} Callee(); + +implementation {:id "Callee"} {:checksum "0"} Callee() +{ + var r: int; + + call r := Sum(42); + assert r != 0; +} + +procedure {:checksum "1"} Sum(n: int) returns (r: int); + requires 0 <= n; + ensures n <= r; diff --git a/Test/snapshots/Snapshots39.v1.bpl b/Test/snapshots/Snapshots39.v1.bpl new file mode 100644 index 00000000..09850bfc --- /dev/null +++ b/Test/snapshots/Snapshots39.v1.bpl @@ -0,0 +1,14 @@ +procedure {:checksum "-1"} Callee(); + +implementation {:id "Callee"} {:checksum "2"} Callee() +{ + var r: int; + + call r := Sum(42); + assert r != 0; + assert r == (42 * 43) div 2; +} + +procedure {:checksum "1"} Sum(n: int) returns (r: int); + requires 0 <= n; + ensures n <= r; diff --git a/Test/snapshots/Snapshots39.v2.bpl b/Test/snapshots/Snapshots39.v2.bpl new file mode 100644 index 00000000..4bdc4b6e --- /dev/null +++ b/Test/snapshots/Snapshots39.v2.bpl @@ -0,0 +1,14 @@ +procedure {:checksum "-1"} Callee(); + +implementation {:id "Callee"} {:checksum "2"} Callee() +{ + var r: int; + + call r := Sum(42); + assert r != 0; + assert r == (42 * 43) div 2; +} + +procedure {:checksum "3"} Sum(n: int) returns (r: int); + requires 0 <= n; + ensures r == (n * (n + 1)) div 2; diff --git a/Test/snapshots/runtest.snapshot b/Test/snapshots/runtest.snapshot index d4e18910..4baa5edb 100644 --- a/Test/snapshots/runtest.snapshot +++ b/Test/snapshots/runtest.snapshot @@ -1,2 +1,2 @@ -// RUN: %boogie -errorTrace:0 -traceCaching:1 -verifySnapshots:2 -verifySeparately -noinfer Snapshots0.bpl Snapshots1.bpl Snapshots2.bpl Snapshots3.bpl Snapshots4.bpl Snapshots5.bpl Snapshots6.bpl Snapshots7.bpl Snapshots8.bpl Snapshots9.bpl Snapshots10.bpl Snapshots11.bpl Snapshots12.bpl Snapshots13.bpl Snapshots14.bpl Snapshots15.bpl Snapshots16.bpl Snapshots17.bpl Snapshots18.bpl Snapshots19.bpl Snapshots20.bpl Snapshots21.bpl Snapshots22.bpl Snapshots23.bpl Snapshots24.bpl Snapshots25.bpl Snapshots26.bpl Snapshots27.bpl Snapshots28.bpl Snapshots30.bpl Snapshots31.bpl Snapshots32.bpl Snapshots33.bpl Snapshots34.bpl Snapshots35.bpl Snapshots36.bpl Snapshots37.bpl > "%t" +// RUN: %boogie -errorTrace:0 -traceCaching:1 -verifySnapshots:2 -verifySeparately -noinfer Snapshots0.bpl Snapshots1.bpl Snapshots2.bpl Snapshots3.bpl Snapshots4.bpl Snapshots5.bpl Snapshots6.bpl Snapshots7.bpl Snapshots8.bpl Snapshots9.bpl Snapshots10.bpl Snapshots11.bpl Snapshots12.bpl Snapshots13.bpl Snapshots14.bpl Snapshots15.bpl Snapshots16.bpl Snapshots17.bpl Snapshots18.bpl Snapshots19.bpl Snapshots20.bpl Snapshots21.bpl Snapshots22.bpl Snapshots23.bpl Snapshots24.bpl Snapshots25.bpl Snapshots26.bpl Snapshots27.bpl Snapshots28.bpl Snapshots30.bpl Snapshots31.bpl Snapshots32.bpl Snapshots33.bpl Snapshots34.bpl Snapshots35.bpl Snapshots36.bpl Snapshots37.bpl Snapshots38.bpl Snapshots39.bpl > "%t" // RUN: %diff "%s.expect" "%t" diff --git a/Test/snapshots/runtest.snapshot.expect b/Test/snapshots/runtest.snapshot.expect index 4ef6bd20..b3c08d3b 100644 --- a/Test/snapshots/runtest.snapshot.expect +++ b/Test/snapshots/runtest.snapshot.expect @@ -666,3 +666,63 @@ Processing command (at Snapshots37.v1.bpl(8,5)) assert l[0]; Snapshots37.v1.bpl(8,5): Error BP5001: This assertion might not hold. Boogie program verifier finished with 0 verified, 1 error +Processing command (at Snapshots38.v0.bpl(7,5)) assert 0 <= call0formal#AT#n; + >>> DoNothingToAssert +Processing command (at Snapshots38.v0.bpl(8,5)) assert r != 0; + >>> DoNothingToAssert + +Boogie program verifier finished with 1 verified, 0 errors +Processing command (at Snapshots38.v1.bpl(7,5)) assert 0 <= call0formal#AT#n; + >>> MarkAsFullyVerified +Processing command (at Snapshots38.v1.bpl(8,5)) assert r != 0; + >>> MarkAsFullyVerified +Processing command (at Snapshots38.v1.bpl(9,5)) assert 42 <= r; + >>> DoNothingToAssert +Snapshots38.v1.bpl(9,5): Error BP5001: This assertion might not hold. + +Boogie program verifier finished with 0 verified, 1 error +Processing call to procedure Sum in implementation Callee (at Snapshots38.v2.bpl(7,5)): + >>> added axiom: (forall call0formal#AT#n: int :: {:weight 30} { ##extracted_function##1(call0formal#AT#n) } ##extracted_function##1(call0formal#AT#n) == (0 <= call0formal#AT#n)) + >>> added axiom: (forall call0formal#AT#n: int, call1formal#AT#r: int :: {:weight 30} { ##extracted_function##2(call0formal#AT#n, call1formal#AT#r) } ##extracted_function##2(call0formal#AT#n, call1formal#AT#r) == (call0formal#AT#n != 0 ==> 1 <= call1formal#AT#r)) + >>> added before precondition check: assume {:precondition_previous_snapshot} ##extracted_function##1(call0formal#AT#n); + >>> added after: a##cached##0 := a##cached##0 && ##extracted_function##2(call0formal#AT#n, call1formal#AT#r); +Processing command (at Snapshots38.v2.bpl(7,5)) assume {:precondition_previous_snapshot} ##extracted_function##1(call0formal#AT#n); + >>> MarkAsFullyVerified +Processing command (at Snapshots38.v2.bpl(7,5)) assert 0 <= call0formal#AT#n; + >>> MarkAsFullyVerified +Processing command (at Snapshots38.v2.bpl(8,5)) assert r != 0; + >>> MarkAsPartiallyVerified +Processing command (at Snapshots38.v2.bpl(9,5)) assert 42 <= r; + >>> DoNothingToAssert + +Boogie program verifier finished with 1 verified, 0 errors +Processing command (at Snapshots39.v0.bpl(7,5)) assert 0 <= call0formal#AT#n; + >>> DoNothingToAssert +Processing command (at Snapshots39.v0.bpl(8,5)) assert r != 0; + >>> DoNothingToAssert + +Boogie program verifier finished with 1 verified, 0 errors +Processing command (at Snapshots39.v1.bpl(7,5)) assert 0 <= call0formal#AT#n; + >>> MarkAsFullyVerified +Processing command (at Snapshots39.v1.bpl(8,5)) assert r != 0; + >>> MarkAsFullyVerified +Processing command (at Snapshots39.v1.bpl(9,5)) assert r == 42 * 43 div 2; + >>> DoNothingToAssert +Snapshots39.v1.bpl(9,5): Error BP5001: This assertion might not hold. + +Boogie program verifier finished with 0 verified, 1 error +Processing call to procedure Sum in implementation Callee (at Snapshots39.v2.bpl(7,5)): + >>> added axiom: (forall call0formal#AT#n: int :: {:weight 30} { ##extracted_function##1(call0formal#AT#n) } ##extracted_function##1(call0formal#AT#n) == (0 <= call0formal#AT#n)) + >>> added axiom: (forall call0formal#AT#n: int, call1formal#AT#r: int :: {:weight 30} { ##extracted_function##2(call0formal#AT#n, call1formal#AT#r) } ##extracted_function##2(call0formal#AT#n, call1formal#AT#r) == (call0formal#AT#n <= call1formal#AT#r)) + >>> added before precondition check: assume {:precondition_previous_snapshot} ##extracted_function##1(call0formal#AT#n); + >>> added after: a##cached##0 := a##cached##0 && ##extracted_function##2(call0formal#AT#n, call1formal#AT#r); +Processing command (at Snapshots39.v2.bpl(7,5)) assume {:precondition_previous_snapshot} ##extracted_function##1(call0formal#AT#n); + >>> MarkAsFullyVerified +Processing command (at Snapshots39.v2.bpl(7,5)) assert 0 <= call0formal#AT#n; + >>> MarkAsFullyVerified +Processing command (at Snapshots39.v2.bpl(8,5)) assert r != 0; + >>> MarkAsPartiallyVerified +Processing command (at Snapshots39.v2.bpl(9,5)) assert r == 42 * 43 div 2; + >>> DoNothingToAssert + +Boogie program verifier finished with 1 verified, 0 errors -- cgit v1.2.3 From e73f2c6d74853664b935096ec832cab4cb474ca8 Mon Sep 17 00:00:00 2001 From: Akash Lal Date: Mon, 20 Jul 2015 11:00:48 +0530 Subject: Minor additions to VC gen --- Source/VCGeneration/StratifiedVC.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Source/VCGeneration/StratifiedVC.cs b/Source/VCGeneration/StratifiedVC.cs index 0e598267..789f86f5 100644 --- a/Source/VCGeneration/StratifiedVC.cs +++ b/Source/VCGeneration/StratifiedVC.cs @@ -584,9 +584,6 @@ namespace VC { vcgen.ConvertCFG2DAG(impl); vcgen.PassifyImpl(impl, out mvInfo); - if (PassiveImplInstrumentation != null) - PassiveImplInstrumentation(impl); - VCExpressionGenerator gen = proverInterface.VCExprGen; var exprGen = proverInterface.Context.ExprGen; var translator = proverInterface.Context.BoogieExprTranslator; @@ -599,6 +596,9 @@ namespace VC { vcgen.InstrumentCallSites(impl); + if (PassiveImplInstrumentation != null) + PassiveImplInstrumentation(impl); + label2absy = new Dictionary(); VCGen.CodeExprConversionClosure cc = new VCGen.CodeExprConversionClosure(label2absy, proverInterface.Context); translator.SetCodeExprConverter(cc.CodeExprToVerificationCondition); @@ -639,6 +639,7 @@ namespace VC { public abstract class StratifiedVCGenBase : VCGen { public readonly static string recordProcName = "boogie_si_record"; + public readonly static string callSiteVarAttr = "callSiteVar"; public Dictionary implName2StratifiedInliningInfo; public ProverInterface prover; @@ -699,7 +700,9 @@ namespace VC { if (!implName2StratifiedInliningInfo.ContainsKey(naryExpr.Fun.FunctionName)) continue; Variable callSiteVar = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, "SICS" + callSiteId, Microsoft.Boogie.Type.Bool)); implementation.LocVars.Add(callSiteVar); - newCmds.Add(new AssumeCmd(Token.NoToken, new IdentifierExpr(Token.NoToken, callSiteVar))); + var toInsert = new AssumeCmd(Token.NoToken, new IdentifierExpr(Token.NoToken, callSiteVar), + new QKeyValue(Token.NoToken, callSiteVarAttr, new List(), null)); + newCmds.Add(toInsert); callSiteId++; } block.Cmds = newCmds; -- cgit v1.2.3 From 740f004792b49e59f3980150cb8d543737adbc4b Mon Sep 17 00:00:00 2001 From: Valentin Wüstholz Date: Mon, 20 Jul 2015 18:43:27 +0200 Subject: Added another test. --- Test/snapshots/Snapshots40.v0.bpl | 14 ++++++++++++ Test/snapshots/Snapshots40.v1.bpl | 15 +++++++++++++ Test/snapshots/Snapshots40.v2.bpl | 15 +++++++++++++ Test/snapshots/runtest.snapshot | 2 +- Test/snapshots/runtest.snapshot.expect | 39 ++++++++++++++++++++++++++++++++++ 5 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 Test/snapshots/Snapshots40.v0.bpl create mode 100644 Test/snapshots/Snapshots40.v1.bpl create mode 100644 Test/snapshots/Snapshots40.v2.bpl diff --git a/Test/snapshots/Snapshots40.v0.bpl b/Test/snapshots/Snapshots40.v0.bpl new file mode 100644 index 00000000..27839752 --- /dev/null +++ b/Test/snapshots/Snapshots40.v0.bpl @@ -0,0 +1,14 @@ +procedure {:checksum "-1"} Foo(b: bool); + +implementation {:id "Foo"} {:checksum "0"} Foo(b: bool) +{ + var r: int; + + assert b; + call r := Sum(42); + assert r != 0; +} + +procedure {:checksum "1"} Sum(n: int) returns (r: int); + requires 0 <= n; + ensures n <= r; diff --git a/Test/snapshots/Snapshots40.v1.bpl b/Test/snapshots/Snapshots40.v1.bpl new file mode 100644 index 00000000..e1c505f8 --- /dev/null +++ b/Test/snapshots/Snapshots40.v1.bpl @@ -0,0 +1,15 @@ +procedure {:checksum "-1"} Foo(b: bool); + +implementation {:id "Foo"} {:checksum "2"} Foo(b: bool) +{ + var r: int; + + assert b; + call r := Sum(42); + assert r != 0; + assert r == (42 * 43) div 2; +} + +procedure {:checksum "1"} Sum(n: int) returns (r: int); + requires 0 <= n; + ensures n <= r; diff --git a/Test/snapshots/Snapshots40.v2.bpl b/Test/snapshots/Snapshots40.v2.bpl new file mode 100644 index 00000000..842d33f5 --- /dev/null +++ b/Test/snapshots/Snapshots40.v2.bpl @@ -0,0 +1,15 @@ +procedure {:checksum "-1"} Foo(b: bool); + +implementation {:id "Foo"} {:checksum "2"} Foo(b: bool) +{ + var r: int; + + assert b; + call r := Sum(42); + assert r != 0; + assert r == (42 * 43) div 2; +} + +procedure {:checksum "3"} Sum(n: int) returns (r: int); + requires 0 <= n; + ensures r == (n * (n + 1)) div 2; diff --git a/Test/snapshots/runtest.snapshot b/Test/snapshots/runtest.snapshot index 4baa5edb..f410a885 100644 --- a/Test/snapshots/runtest.snapshot +++ b/Test/snapshots/runtest.snapshot @@ -1,2 +1,2 @@ -// RUN: %boogie -errorTrace:0 -traceCaching:1 -verifySnapshots:2 -verifySeparately -noinfer Snapshots0.bpl Snapshots1.bpl Snapshots2.bpl Snapshots3.bpl Snapshots4.bpl Snapshots5.bpl Snapshots6.bpl Snapshots7.bpl Snapshots8.bpl Snapshots9.bpl Snapshots10.bpl Snapshots11.bpl Snapshots12.bpl Snapshots13.bpl Snapshots14.bpl Snapshots15.bpl Snapshots16.bpl Snapshots17.bpl Snapshots18.bpl Snapshots19.bpl Snapshots20.bpl Snapshots21.bpl Snapshots22.bpl Snapshots23.bpl Snapshots24.bpl Snapshots25.bpl Snapshots26.bpl Snapshots27.bpl Snapshots28.bpl Snapshots30.bpl Snapshots31.bpl Snapshots32.bpl Snapshots33.bpl Snapshots34.bpl Snapshots35.bpl Snapshots36.bpl Snapshots37.bpl Snapshots38.bpl Snapshots39.bpl > "%t" +// RUN: %boogie -errorTrace:0 -traceCaching:1 -verifySnapshots:2 -verifySeparately -noinfer Snapshots0.bpl Snapshots1.bpl Snapshots2.bpl Snapshots3.bpl Snapshots4.bpl Snapshots5.bpl Snapshots6.bpl Snapshots7.bpl Snapshots8.bpl Snapshots9.bpl Snapshots10.bpl Snapshots11.bpl Snapshots12.bpl Snapshots13.bpl Snapshots14.bpl Snapshots15.bpl Snapshots16.bpl Snapshots17.bpl Snapshots18.bpl Snapshots19.bpl Snapshots20.bpl Snapshots21.bpl Snapshots22.bpl Snapshots23.bpl Snapshots24.bpl Snapshots25.bpl Snapshots26.bpl Snapshots27.bpl Snapshots28.bpl Snapshots30.bpl Snapshots31.bpl Snapshots32.bpl Snapshots33.bpl Snapshots34.bpl Snapshots35.bpl Snapshots36.bpl Snapshots37.bpl Snapshots38.bpl Snapshots39.bpl Snapshots40.bpl > "%t" // RUN: %diff "%s.expect" "%t" diff --git a/Test/snapshots/runtest.snapshot.expect b/Test/snapshots/runtest.snapshot.expect index b3c08d3b..c398de78 100644 --- a/Test/snapshots/runtest.snapshot.expect +++ b/Test/snapshots/runtest.snapshot.expect @@ -726,3 +726,42 @@ Processing command (at Snapshots39.v2.bpl(9,5)) assert r == 42 * 43 div 2; >>> DoNothingToAssert Boogie program verifier finished with 1 verified, 0 errors +Processing command (at Snapshots40.v0.bpl(7,5)) assert b; + >>> DoNothingToAssert +Processing command (at Snapshots40.v0.bpl(8,5)) assert 0 <= call0formal#AT#n; + >>> DoNothingToAssert +Processing command (at Snapshots40.v0.bpl(9,5)) assert r != 0; + >>> DoNothingToAssert +Snapshots40.v0.bpl(7,5): Error BP5001: This assertion might not hold. + +Boogie program verifier finished with 0 verified, 1 error +Processing command (at Snapshots40.v1.bpl(7,5)) assert b; + >>> RecycleError +Processing command (at Snapshots40.v1.bpl(8,5)) assert 0 <= call0formal#AT#n; + >>> MarkAsFullyVerified +Processing command (at Snapshots40.v1.bpl(9,5)) assert r != 0; + >>> MarkAsFullyVerified +Processing command (at Snapshots40.v1.bpl(10,5)) assert r == 42 * 43 div 2; + >>> DoNothingToAssert +Snapshots40.v0.bpl(7,5): Error BP5001: This assertion might not hold. +Snapshots40.v1.bpl(10,5): Error BP5001: This assertion might not hold. + +Boogie program verifier finished with 0 verified, 2 errors +Processing call to procedure Sum in implementation Foo (at Snapshots40.v2.bpl(8,5)): + >>> added axiom: (forall call0formal#AT#n: int :: {:weight 30} { ##extracted_function##1(call0formal#AT#n) } ##extracted_function##1(call0formal#AT#n) == (0 <= call0formal#AT#n)) + >>> added axiom: (forall call0formal#AT#n: int, call1formal#AT#r: int :: {:weight 30} { ##extracted_function##2(call0formal#AT#n, call1formal#AT#r) } ##extracted_function##2(call0formal#AT#n, call1formal#AT#r) == (call0formal#AT#n <= call1formal#AT#r)) + >>> added before precondition check: assume {:precondition_previous_snapshot} ##extracted_function##1(call0formal#AT#n); + >>> added after: a##cached##0 := a##cached##0 && ##extracted_function##2(call0formal#AT#n, call1formal#AT#r); +Processing command (at Snapshots40.v2.bpl(7,5)) assert b; + >>> RecycleError +Processing command (at Snapshots40.v2.bpl(8,5)) assume {:precondition_previous_snapshot} ##extracted_function##1(call0formal#AT#n); + >>> MarkAsFullyVerified +Processing command (at Snapshots40.v2.bpl(8,5)) assert 0 <= call0formal#AT#n; + >>> MarkAsFullyVerified +Processing command (at Snapshots40.v2.bpl(9,5)) assert r != 0; + >>> MarkAsPartiallyVerified +Processing command (at Snapshots40.v2.bpl(10,5)) assert r == 42 * 43 div 2; + >>> DoNothingToAssert +Snapshots40.v0.bpl(7,5): Error BP5001: This assertion might not hold. + +Boogie program verifier finished with 0 verified, 1 error -- cgit v1.2.3 From 21f1cfff139759d9b9f91ed800da2158daca8ed4 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 27 Aug 2015 20:10:41 -0700 Subject: Added /verifySnapshots:3, which prints recycled errors messages with the source locations of the new code. --- Source/Core/CommandLineOptions.cs | 6 ++- Source/ExecutionEngine/ExecutionEngine.cs | 45 ++++++++++------------- Source/ExecutionEngine/VerificationResultCache.cs | 44 ++++++---------------- Source/VCGeneration/VC.cs | 40 ++++++++++++++++++-- Test/snapshots/Snapshots41.v0.bpl | 35 ++++++++++++++++++ Test/snapshots/Snapshots41.v1.bpl | 39 ++++++++++++++++++++ Test/snapshots/runtest.snapshot | 1 + Test/snapshots/runtest.snapshot.expect | 43 ++++++++++++++++++++++ 8 files changed, 191 insertions(+), 62 deletions(-) create mode 100644 Test/snapshots/Snapshots41.v0.bpl create mode 100644 Test/snapshots/Snapshots41.v1.bpl diff --git a/Source/Core/CommandLineOptions.cs b/Source/Core/CommandLineOptions.cs index be371fcb..a17ff9c7 100644 --- a/Source/Core/CommandLineOptions.cs +++ b/Source/Core/CommandLineOptions.cs @@ -1492,7 +1492,7 @@ namespace Microsoft.Boogie { return true; case "verifySnapshots": - ps.GetNumericArgument(ref VerifySnapshots, 3); + ps.GetNumericArgument(ref VerifySnapshots, 4); return true; case "traceCaching": @@ -1944,6 +1944,10 @@ namespace Microsoft.Boogie { 0 - do not use any verification result caching (default) 1 - use the basic verification result caching 2 - use the more advanced verification result caching + 3 - use the more advanced caching and report errors according + to the new source locations for errors and their + related locations (but not /errorTrace and CaptureState + locations) /verifySeparately verify each input program separately /removeEmptyBlocks: diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index 67ce49a5..a408642a 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -151,25 +151,18 @@ namespace Microsoft.Boogie { Contract.Requires(message != null); - if (category != null) - { + if (category != null) { message = string.Format("{0}: {1}", category, message); } string s; - if (tok != null) - { + if (tok != null) { s = string.Format("{0}({1},{2}): {3}", ExecutionEngine.GetFileNameForConsole(tok.filename), tok.line, tok.col, message); - } - else - { + } else { s = message; } - if (error) - { + if (error) { ErrorWriteLine(tw, s); - } - else - { + } else { tw.WriteLine(s); } } @@ -1114,23 +1107,23 @@ namespace Microsoft.Boogie printer.Inform(string.Format("Verifying {0} ...", impl.Name), output); int priority = 0; - if (0 < CommandLineOptions.Clo.VerifySnapshots) - { - verificationResult = Cache.Lookup(impl, out priority); - } - var wasCached = false; - if (verificationResult != null && priority == Priority.SKIP) - { - if (CommandLineOptions.Clo.XmlSink != null) - { - CommandLineOptions.Clo.XmlSink.WriteStartMethod(impl.Name, verificationResult.Start); - } + if (0 < CommandLineOptions.Clo.VerifySnapshots) { + var cachedResults = Cache.Lookup(impl, out priority); + if (cachedResults != null && priority == Priority.SKIP) { + if (CommandLineOptions.Clo.XmlSink != null) { + CommandLineOptions.Clo.XmlSink.WriteStartMethod(impl.Name, cachedResults.Start); + } - printer.Inform(string.Format("Retrieving cached verification result for implementation {0}...", impl.Name), output); - wasCached = true; + printer.Inform(string.Format("Retrieving cached verification result for implementation {0}...", impl.Name), output); + if (CommandLineOptions.Clo.VerifySnapshots < 3 || cachedResults.Outcome == ConditionGeneration.Outcome.Correct) { + verificationResult = cachedResults; + wasCached = true; + } + } } - else + + if (!wasCached) { #region Verify the implementation diff --git a/Source/ExecutionEngine/VerificationResultCache.cs b/Source/ExecutionEngine/VerificationResultCache.cs index a18cee05..f8aca0e9 100644 --- a/Source/ExecutionEngine/VerificationResultCache.cs +++ b/Source/ExecutionEngine/VerificationResultCache.cs @@ -172,48 +172,28 @@ namespace Microsoft.Boogie var vr = ExecutionEngine.Cache.Lookup(impl, out priority); if (vr != null && vr.ProgramId == programId) { - if (priority == Priority.LOW) - { + if (priority == Priority.LOW) { run.LowPriorityImplementationCount++; - if (TimeThreshold < vr.End.Subtract(vr.Start).TotalMilliseconds) - { - SetErrorAndAssertionChecksumsInCachedSnapshot(impl, vr); - if (vr.ProgramId != null) - { - var p = ExecutionEngine.CachedProgram(vr.ProgramId); - if (p != null) - { - eai.Inject(impl, p); - run.TransformedImplementationCount++; - } - } - } - } - else if (priority == Priority.MEDIUM) - { + } else if (priority == Priority.MEDIUM) { run.MediumPriorityImplementationCount++; - if (TimeThreshold < vr.End.Subtract(vr.Start).TotalMilliseconds) - { + } else if (priority == Priority.HIGH) { + run.HighPriorityImplementationCount++; + } else if (priority == Priority.SKIP) { + run.SkippedImplementationCount++; + } + + if (priority == Priority.LOW || priority == Priority.MEDIUM || 3 <= CommandLineOptions.Clo.VerifySnapshots) { + if (TimeThreshold < vr.End.Subtract(vr.Start).TotalMilliseconds) { SetErrorAndAssertionChecksumsInCachedSnapshot(impl, vr); - if (vr.ProgramId != null) - { + if (vr.ProgramId != null) { var p = ExecutionEngine.CachedProgram(vr.ProgramId); - if (p != null) - { + if (p != null) { eai.Inject(impl, p); run.TransformedImplementationCount++; } } } } - else if (priority == Priority.HIGH) - { - run.HighPriorityImplementationCount++; - } - else if (priority == Priority.SKIP) - { - run.SkippedImplementationCount++; - } } } run.End = DateTime.UtcNow; diff --git a/Source/VCGeneration/VC.cs b/Source/VCGeneration/VC.cs index 3a483a58..8e5c853c 100644 --- a/Source/VCGeneration/VC.cs +++ b/Source/VCGeneration/VC.cs @@ -1763,9 +1763,18 @@ namespace VC { { var checksum = a.Checksum; var oldCex = impl.ErrorChecksumToCachedError[checksum] as Counterexample; - if (oldCex != null) - { - callback.OnCounterexample(oldCex, null); + if (oldCex != null) { + if (CommandLineOptions.Clo.VerifySnapshots < 3) { + callback.OnCounterexample(oldCex, null); + } else { + // If possible, we use the old counterexample, but with the location information of "a" + var cex = AssertCmdToCloneCounterexample(a, oldCex); + callback.OnCounterexample(cex, null); + // OnCounterexample may have had side effects on the RequestId and OriginalRequestId fields. We make + // any such updates available in oldCex. (Is this really a good design? --KRML) + oldCex.RequestId = cex.RequestId; + oldCex.OriginalRequestId = cex.OriginalRequestId; + } } } } @@ -3137,6 +3146,31 @@ namespace VC { } } + /// + /// Returns a clone of "cex", but with the location stored in "cex" replaced by those from "assrt". + /// + public static Counterexample AssertCmdToCloneCounterexample(AssertCmd assrt, Counterexample cex) { + Contract.Requires(assrt != null); + Contract.Requires(cex != null); + Contract.Ensures(Contract.Result() != null); + + List relatedInformation = new List(); + + Counterexample cc; + if (assrt is AssertRequiresCmd) { + var aa = (AssertRequiresCmd)assrt; + cc = new CallCounterexample(cex.Trace, aa.Call, aa.Requires, cex.Model, cex.MvInfo, cex.Context, aa.Checksum); + } else if (assrt is AssertEnsuresCmd && cex is ReturnCounterexample) { + var aa = (AssertEnsuresCmd)assrt; + var oldCex = (ReturnCounterexample)cex; + cc = new ReturnCounterexample(cex.Trace, oldCex.FailingReturn, aa.Ensures, cex.Model, cex.MvInfo, cex.Context, aa.Checksum); + } else { + cc = new AssertCounterexample(cex.Trace, assrt, cex.Model, cex.MvInfo, cex.Context); + } + cc.relatedInformation = relatedInformation; + return cc; + } + static VCExpr LetVC(Block startBlock, VCExpr controlFlowVariableExpr, Dictionary label2absy, diff --git a/Test/snapshots/Snapshots41.v0.bpl b/Test/snapshots/Snapshots41.v0.bpl new file mode 100644 index 00000000..631fe544 --- /dev/null +++ b/Test/snapshots/Snapshots41.v0.bpl @@ -0,0 +1,35 @@ +procedure {:checksum "0"} M(x: int); +implementation {:id "M"} {:checksum "1"} M(x: int) +{ assert x < 20 || 10 <= x; // always true + assert x < 10; // error + call Other(x); // error: precondition violation +} + +procedure {:checksum "10"} Other(y: int); + requires 0 <= y; +implementation {:id "Other"} {:checksum "11"} Other(y: int) +{ +} + +procedure {:checksum "20"} Posty() returns (z: int); + ensures 2 <= z; // error: postcondition violation +implementation {:id "Posty"} {:checksum "21"} Posty() returns (z: int) +{ + var t: int; + t := 20; + if (t < z) { + } else { // the postcondition violation occurs on this 'else' branch + } +} + +procedure {:checksum "30"} NoChangeWhazzoeva(u: int); +implementation {:id "NoChangeWhazzoeva"} {:checksum "3"} NoChangeWhazzoeva(u: int) +{ + assert u != 53; // error +} + +procedure {:checksum "40"} NoChangeAndCorrect(); +implementation {:id "NoChangeAndCorrect"} {:checksum "41"} NoChangeAndCorrect() +{ + assert true; +} diff --git a/Test/snapshots/Snapshots41.v1.bpl b/Test/snapshots/Snapshots41.v1.bpl new file mode 100644 index 00000000..0cd9fbf9 --- /dev/null +++ b/Test/snapshots/Snapshots41.v1.bpl @@ -0,0 +1,39 @@ +procedure {:checksum "0"} M(x: int); +implementation {:id "M"} {:checksum "1"} M(x: int) +{ +assert x < 20 || 10 <= x; // always true + + assert x < 10; // error + call Other(x); // error: precondition violation + assert x == 7; // error: this is a new error in v1 +} + + + procedure {:checksum "10"} Other(y: int); + requires 0 <= y; + implementation {:id "Other"} {:checksum "11"} Other(y: int) + { + } + + + +procedure {:checksum "20"} Posty() returns (z: int); + ensures 2 <= z; // error: postcondition violation +implementation {:id "Posty"} {:checksum "21"} Posty() returns (z: int) +{ + var t: int; + t := 20; + if (t < z) { + assert true; // this is a new assert + } else { // the postcondition violation occurs on this 'else' branch + } +} + + procedure {:checksum "30"} NoChangeWhazzoeva(u: int); + implementation {:id "NoChangeWhazzoeva"} {:checksum "3"} NoChangeWhazzoeva(u: int) + { + assert u != 53; // error + } + +procedure {:checksum "40"} NoChangeAndCorrect(); +implementation {:id "NoChangeAndCorrect"} {:checksum "41"} NoChangeAndCorrect() { assert true; } diff --git a/Test/snapshots/runtest.snapshot b/Test/snapshots/runtest.snapshot index 01a231fe..908fc2fd 100644 --- a/Test/snapshots/runtest.snapshot +++ b/Test/snapshots/runtest.snapshot @@ -1,2 +1,3 @@ // RUN: %boogie -errorTrace:0 -traceCaching:1 -verifySnapshots:2 -verifySeparately -noinfer Snapshots0.bpl Snapshots1.bpl Snapshots2.bpl Snapshots3.bpl Snapshots4.bpl Snapshots5.bpl Snapshots6.bpl Snapshots7.bpl Snapshots8.bpl Snapshots9.bpl Snapshots10.bpl Snapshots11.bpl Snapshots12.bpl Snapshots13.bpl Snapshots14.bpl Snapshots15.bpl Snapshots16.bpl Snapshots17.bpl Snapshots18.bpl Snapshots19.bpl Snapshots20.bpl Snapshots21.bpl Snapshots22.bpl Snapshots23.bpl Snapshots24.bpl Snapshots25.bpl Snapshots26.bpl Snapshots27.bpl Snapshots28.bpl Snapshots30.bpl Snapshots31.bpl Snapshots32.bpl Snapshots33.bpl Snapshots34.bpl Snapshots35.bpl > "%t" +// RUN: %boogie -errorTrace:0 -traceCaching:1 -verifySnapshots:3 -verifySeparately -noinfer Snapshots41.bpl >> "%t" // RUN: %diff "%s.expect" "%t" diff --git a/Test/snapshots/runtest.snapshot.expect b/Test/snapshots/runtest.snapshot.expect index 637dd088..9dd3d0ec 100644 --- a/Test/snapshots/runtest.snapshot.expect +++ b/Test/snapshots/runtest.snapshot.expect @@ -648,3 +648,46 @@ Processing command (at Snapshots35.v1.bpl(5,5)) assert p; Snapshots35.v1.bpl(5,5): Error BP5001: This assertion might not hold. Boogie program verifier finished with 0 verified, 1 error +Processing command (at Snapshots41.v0.bpl(3,23)) assert x < 20 || 10 <= x; + >>> DoNothingToAssert +Processing command (at Snapshots41.v0.bpl(4,3)) assert x < 10; + >>> DoNothingToAssert +Processing command (at Snapshots41.v0.bpl(5,3)) assert 0 <= call0formal#AT#y; + >>> DoNothingToAssert +Snapshots41.v0.bpl(4,3): Error BP5001: This assertion might not hold. +Snapshots41.v0.bpl(5,3): Error BP5002: A precondition for this call might not hold. +Snapshots41.v0.bpl(9,3): Related location: This is the precondition that might not hold. +Processing command (at Snapshots41.v0.bpl(15,3)) assert 2 <= z; + >>> DoNothingToAssert +Snapshots41.v0.bpl(22,3): Error BP5003: A postcondition might not hold on this return path. +Snapshots41.v0.bpl(15,3): Related location: This is the postcondition that might not hold. +Processing command (at Snapshots41.v0.bpl(28,3)) assert u != 53; + >>> DoNothingToAssert +Snapshots41.v0.bpl(28,3): Error BP5001: This assertion might not hold. +Processing command (at Snapshots41.v0.bpl(34,3)) assert true; + >>> DoNothingToAssert + +Boogie program verifier finished with 2 verified, 4 errors +Processing command (at Snapshots41.v1.bpl(4,1)) assert x < 20 || 10 <= x; + >>> MarkAsFullyVerified +Processing command (at Snapshots41.v1.bpl(6,8)) assert x < 10; + >>> RecycleError +Processing command (at Snapshots41.v1.bpl(7,3)) assert 0 <= call0formal#AT#y; + >>> RecycleError +Processing command (at Snapshots41.v1.bpl(8,3)) assert x == 7; + >>> DoNothingToAssert +Snapshots41.v1.bpl(6,8): Error BP5001: This assertion might not hold. +Snapshots41.v1.bpl(7,3): Error BP5002: A precondition for this call might not hold. +Snapshots41.v1.bpl(13,10): Related location: This is the precondition that might not hold. +Snapshots41.v1.bpl(8,3): Error BP5001: This assertion might not hold. +Processing command (at Snapshots41.v1.bpl(27,5)) assert true; + >>> DoNothingToAssert +Processing command (at Snapshots41.v1.bpl(21,3)) assert 2 <= z; + >>> DoNothingToAssert +Snapshots41.v1.bpl(29,3): Error BP5003: A postcondition might not hold on this return path. +Snapshots41.v1.bpl(21,3): Related location: This is the postcondition that might not hold. +Processing command (at Snapshots41.v1.bpl(35,8)) assert u != 53; + >>> RecycleError +Snapshots41.v1.bpl(35,8): Error BP5001: This assertion might not hold. + +Boogie program verifier finished with 2 verified, 5 errors -- cgit v1.2.3 From ff02bddd8982e92d623b0342e7b079edfaa9b366 Mon Sep 17 00:00:00 2001 From: Shaz Qadeer Date: Wed, 2 Sep 2015 20:14:00 -0700 Subject: fixed a crash when there is no collector --- Source/Concurrency/LinearSets.cs | 2 ++ Test/civl/nocollector.bpl | 8 ++++++++ Test/civl/nocollector.bpl.expect | 2 ++ 3 files changed, 12 insertions(+) create mode 100644 Test/civl/nocollector.bpl create mode 100644 Test/civl/nocollector.bpl.expect diff --git a/Source/Concurrency/LinearSets.cs b/Source/Concurrency/LinearSets.cs index 0fa04b1b..f086dd84 100644 --- a/Source/Concurrency/LinearSets.cs +++ b/Source/Concurrency/LinearSets.cs @@ -624,12 +624,14 @@ namespace Microsoft.Boogie foreach (Variable v in AvailableLinearVars(absy)) { var domainName = FindDomainName(v); + if (!linearDomains.ContainsKey(domainName)) continue; domainNameToScope[domainName].Add(v); } foreach (Variable v in program.GlobalVariables) { var domainName = FindDomainName(v); if (domainName == null) continue; + if (!linearDomains.ContainsKey(domainName)) continue; domainNameToScope[domainName].Add(v); } foreach (string domainName in linearDomains.Keys) diff --git a/Test/civl/nocollector.bpl b/Test/civl/nocollector.bpl new file mode 100644 index 00000000..5a6f1e5d --- /dev/null +++ b/Test/civl/nocollector.bpl @@ -0,0 +1,8 @@ +// RUN: %boogie -noinfer -useArrayTheory "%s" > "%t" +// RUN: %diff "%s.expect" "%t" +var {:linear "L"} x:int; + +procedure{:yields}{:layer 1} P() +{ + yield; +} diff --git a/Test/civl/nocollector.bpl.expect b/Test/civl/nocollector.bpl.expect new file mode 100644 index 00000000..37fad75c --- /dev/null +++ b/Test/civl/nocollector.bpl.expect @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 1 verified, 0 errors -- cgit v1.2.3 From 437bde8d029afab9631e5ce539600dcfcaeace42 Mon Sep 17 00:00:00 2001 From: Shaz Qadeer Date: Thu, 3 Sep 2015 09:07:57 -0700 Subject: fixed my earlier to make it nicer --- Source/Concurrency/LinearSets.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Source/Concurrency/LinearSets.cs b/Source/Concurrency/LinearSets.cs index f086dd84..f654b688 100644 --- a/Source/Concurrency/LinearSets.cs +++ b/Source/Concurrency/LinearSets.cs @@ -624,14 +624,11 @@ namespace Microsoft.Boogie foreach (Variable v in AvailableLinearVars(absy)) { var domainName = FindDomainName(v); - if (!linearDomains.ContainsKey(domainName)) continue; domainNameToScope[domainName].Add(v); } - foreach (Variable v in program.GlobalVariables) + foreach (Variable v in globalVarToDomainName.Keys) { var domainName = FindDomainName(v); - if (domainName == null) continue; - if (!linearDomains.ContainsKey(domainName)) continue; domainNameToScope[domainName].Add(v); } foreach (string domainName in linearDomains.Keys) -- cgit v1.2.3 From 5927744da636063556118f469cc8f9354b1cabe6 Mon Sep 17 00:00:00 2001 From: Shaz Qadeer Date: Fri, 25 Sep 2015 12:05:19 -0700 Subject: added introduced and ghost local variables --- Source/Concurrency/OwickiGries.cs | 54 ++- Source/Concurrency/TypeCheck.cs | 660 ++++++++++++++++++++++++++------- Source/Concurrency/YieldTypeChecker.cs | 2 +- Test/civl/alloc.bpl | 158 ++++++++ Test/civl/alloc.bpl.expect | 2 + Test/civl/ghost.bpl | 11 +- Test/civl/lock-introduced.bpl | 10 + Test/civl/wsq.bpl | 106 +++--- 8 files changed, 786 insertions(+), 217 deletions(-) create mode 100644 Test/civl/alloc.bpl create mode 100644 Test/civl/alloc.bpl.expect diff --git a/Source/Concurrency/OwickiGries.cs b/Source/Concurrency/OwickiGries.cs index 2ad08024..d861e2f3 100644 --- a/Source/Concurrency/OwickiGries.cs +++ b/Source/Concurrency/OwickiGries.cs @@ -40,7 +40,15 @@ namespace Microsoft.Boogie { int enclosingProcLayerNum = moverTypeChecker.procToActionInfo[enclosingImpl.Proc].createdAtLayerNum; Procedure originalProc = originalCallCmd.Proc; - if (moverTypeChecker.procToActionInfo.ContainsKey(originalProc)) + + if (moverTypeChecker.procToAtomicProcedureInfo.ContainsKey(originalProc)) + { + if (moverTypeChecker.CallExists(originalCallCmd, enclosingProcLayerNum, layerNum)) + { + newCmds.Add(callCmd); + } + } + else if (moverTypeChecker.procToActionInfo.ContainsKey(originalProc)) { AtomicActionInfo atomicActionInfo = moverTypeChecker.procToActionInfo[originalProc] as AtomicActionInfo; if (atomicActionInfo != null && atomicActionInfo.gate.Count > 0 && layerNum == enclosingProcLayerNum) @@ -57,8 +65,12 @@ namespace Microsoft.Boogie newCmds.Add(Substituter.Apply(subst, assertCmd)); } } + newCmds.Add(callCmd); + } + else + { + Debug.Assert(false); } - newCmds.Add(callCmd); } private void ProcessParCallCmd(ParCallCmd originalParCallCmd, ParCallCmd parCallCmd, List newCmds) @@ -291,7 +303,34 @@ namespace Microsoft.Boogie private IEnumerable AvailableLinearVars(Absy absy) { - return linearTypeChecker.AvailableLinearVars(absyMap[absy]); + HashSet availableVars = new HashSet(linearTypeChecker.AvailableLinearVars(absyMap[absy])); + foreach (var g in moverTypeChecker.globalVarToSharedVarInfo.Keys) + { + SharedVariableInfo info = moverTypeChecker.globalVarToSharedVarInfo[g]; + if (!(info.introLayerNum <= layerNum && layerNum <= info.hideLayerNum)) + { + availableVars.Remove(g); + } + } + foreach (var v in moverTypeChecker.localVarToLocalVariableInfo.Keys) + { + LocalVariableInfo info = moverTypeChecker.localVarToLocalVariableInfo[v]; + if (info.isGhost) + { + if (info.layer != layerNum) + { + availableVars.Remove(v); + } + } + else + { + if (layerNum < info.layer) + { + availableVars.Remove(v); + } + } + } + return availableVars; } private CallCmd CallToYieldProc(IToken tok, Dictionary ogOldGlobalMap, Dictionary domainNameToLocalVar) @@ -739,7 +778,6 @@ namespace Microsoft.Boogie } Substitution forold = Substituter.SubstitutionFromHashtable(foroldMap); frame = new HashSet(moverTypeChecker.SharedVariables); - HashSet introducedVars = new HashSet(); foreach (Variable v in moverTypeChecker.SharedVariables) { if (moverTypeChecker.globalVarToSharedVarInfo[v].hideLayerNum <= actionInfo.createdAtLayerNum || @@ -747,10 +785,6 @@ namespace Microsoft.Boogie { frame.Remove(v); } - if (moverTypeChecker.globalVarToSharedVarInfo[v].introLayerNum == actionInfo.createdAtLayerNum) - { - introducedVars.Add(v); - } } AtomicActionInfo atomicActionInfo = actionInfo as AtomicActionInfo; if (atomicActionInfo == null) @@ -764,7 +798,7 @@ namespace Microsoft.Boogie } else { - Expr betaExpr = (new MoverCheck.TransitionRelationComputation(moverTypeChecker.program, atomicActionInfo, frame, introducedVars)).TransitionRelationCompute(true); + Expr betaExpr = (new MoverCheck.TransitionRelationComputation(moverTypeChecker.program, atomicActionInfo, frame, new HashSet())).TransitionRelationCompute(true); beta = Substituter.ApplyReplacingOldExprs(always, forold, betaExpr); Expr alphaExpr = Expr.True; foreach (AssertCmd assertCmd in atomicActionInfo.gate) @@ -1178,7 +1212,7 @@ namespace Microsoft.Boogie public static void AddCheckers(LinearTypeChecker linearTypeChecker, MoverTypeChecker moverTypeChecker, List decls) { Program program = linearTypeChecker.program; - foreach (int layerNum in moverTypeChecker.AllCreatedLayerNums.Except(new int[] { moverTypeChecker.leastUnimplementedLayerNum })) + foreach (int layerNum in moverTypeChecker.AllImplementedLayerNums) { if (CommandLineOptions.Clo.TrustLayersDownto <= layerNum || layerNum <= CommandLineOptions.Clo.TrustLayersUpto) continue; diff --git a/Source/Concurrency/TypeCheck.cs b/Source/Concurrency/TypeCheck.cs index c821117a..10127d33 100644 --- a/Source/Concurrency/TypeCheck.cs +++ b/Source/Concurrency/TypeCheck.cs @@ -280,22 +280,129 @@ namespace Microsoft.Boogie } } + public class LayerRange + { + public int lowerLayerNum; + public int upperLayerNum; + public LayerRange(int layer) + { + this.lowerLayerNum = layer; + this.upperLayerNum = layer; + } + public LayerRange(int lower, int upper) + { + this.lowerLayerNum = lower; + this.upperLayerNum = upper; + } + public LayerRange(IEnumerable layerNums) + { + int min = int.MaxValue; + int max = int.MinValue; + foreach (var layerNum in layerNums) + { + if (layerNum < min) + { + min = layerNum; + } + if (max < layerNum) + { + max = layerNum; + } + } + this.lowerLayerNum = min; + this.upperLayerNum = max; + } + public bool Contains(int layerNum) + { + return lowerLayerNum <= layerNum && layerNum <= upperLayerNum; + } + public bool Subset(int lower, int upper) + { + return lower <= lowerLayerNum && upperLayerNum <= upper; + } + public bool Equal(int lower, int upper) + { + return lower == lowerLayerNum && upperLayerNum == upper; + } + public bool Subset(LayerRange info) + { + return info.lowerLayerNum <= lowerLayerNum && upperLayerNum <= info.upperLayerNum; + } + } + + public class AtomicProcedureInfo + { + public bool isPure; + public HashSet layers; + public AtomicProcedureInfo() + { + this.isPure = true; + this.layers = null; + } + public AtomicProcedureInfo(HashSet layers) + { + this.isPure = false; + this.layers = layers; + } + } + + public class LocalVariableInfo + { + public bool isGhost; + public int layer; + public LocalVariableInfo(bool isGhost, int layer) + { + this.isGhost = isGhost; + this.layer = layer; + } + } + public class MoverTypeChecker : ReadOnlyVisitor { CheckingContext checkingContext; - public int errorCount; - public Dictionary globalVarToSharedVarInfo; Procedure enclosingProc; Implementation enclosingImpl; - public Dictionary procToActionInfo; + HashSet sharedVarsAccessed; + int introducedLocalVarsUpperBound; + int ghostVarIntroLayerAllowed; + public Program program; - bool canAccessSharedVars; - bool canAccessGhostVars; - int minLayerNum; - int maxLayerNum; + public int errorCount; + public Dictionary globalVarToSharedVarInfo; + public Dictionary procToActionInfo; + public Dictionary procToAtomicProcedureInfo; public Dictionary> absyToLayerNums; - HashSet ghostVars; - public int leastUnimplementedLayerNum; + public Dictionary localVarToLocalVariableInfo; + + public bool CallExists(CallCmd callCmd, int enclosingProcLayerNum, int layerNum) + { + if (!procToAtomicProcedureInfo.ContainsKey(callCmd.Proc)) + return true; + var atomicProcedureInfo = procToAtomicProcedureInfo[callCmd.Proc]; + if (atomicProcedureInfo.isPure) + { + return true; + } + else if (callCmd.Proc.Modifies.Count == 0) + { + if (callCmd.Outs.Count == 0) + return false; + var outputVar = callCmd.Outs[0].Decl; + Debug.Assert(localVarToLocalVariableInfo.ContainsKey(outputVar)); + if (localVarToLocalVariableInfo[outputVar].isGhost) + { + return localVarToLocalVariableInfo[outputVar].layer == layerNum; + } + else + { + return enclosingProcLayerNum == layerNum; + } + } + else + { + return enclosingProcLayerNum == layerNum; + } + } private static List FindLayers(QKeyValue kv) { @@ -316,6 +423,19 @@ namespace Microsoft.Boogie return layers; } + private static int Least(IEnumerable layerNums) + { + int least = int.MaxValue; + foreach (var layer in layerNums) + { + if (layer < least) + { + least = layer; + } + } + return least; + } + private static MoverType GetMoverType(Ensures e) { if (QKeyValue.FindBoolAttribute(e.Attributes, "atomic")) @@ -331,26 +451,27 @@ namespace Microsoft.Boogie public MoverTypeChecker(Program program) { - this.ghostVars = new HashSet(); - this.absyToLayerNums = new Dictionary>(); - this.globalVarToSharedVarInfo = new Dictionary(); - this.procToActionInfo = new Dictionary(); this.errorCount = 0; this.checkingContext = new CheckingContext(null); this.program = program; this.enclosingProc = null; this.enclosingImpl = null; - this.canAccessSharedVars = false; - this.canAccessGhostVars = false; - this.minLayerNum = int.MaxValue; - this.maxLayerNum = -1; - this.leastUnimplementedLayerNum = int.MaxValue; + this.sharedVarsAccessed = null; + this.introducedLocalVarsUpperBound = int.MinValue; + this.ghostVarIntroLayerAllowed = int.MinValue; + + this.localVarToLocalVariableInfo = new Dictionary(); + this.absyToLayerNums = new Dictionary>(); + this.globalVarToSharedVarInfo = new Dictionary(); + this.procToActionInfo = new Dictionary(); + this.procToAtomicProcedureInfo = new Dictionary(); + foreach (var g in program.GlobalVariables) { List layerNums = FindLayers(g.Attributes); if (layerNums.Count == 0) { - // Cannot access atomic actions + // Inaccessible from yielding and atomic procedures } else if (layerNums.Count == 1) { @@ -366,7 +487,27 @@ namespace Microsoft.Boogie } } } - + + private HashSet allImplementedLayerNums; + public IEnumerable AllImplementedLayerNums + { + get + { + if (allImplementedLayerNums == null) + { + allImplementedLayerNums = new HashSet(); + foreach (ActionInfo actionInfo in procToActionInfo.Values) + { + if (actionInfo.hasImplementation) + { + allImplementedLayerNums.Add(actionInfo.createdAtLayerNum); + } + } + } + return allImplementedLayerNums; + } + } + private HashSet allCreatedLayerNums; public IEnumerable AllCreatedLayerNums { @@ -384,8 +525,98 @@ namespace Microsoft.Boogie } } + private LayerRange FindLayerRange() + { + int maxIntroLayerNum = int.MinValue; + int minHideLayerNum = int.MaxValue; + foreach (var g in sharedVarsAccessed) + { + if (globalVarToSharedVarInfo[g].introLayerNum > maxIntroLayerNum) + { + maxIntroLayerNum = globalVarToSharedVarInfo[g].introLayerNum; + } + if (globalVarToSharedVarInfo[g].hideLayerNum < minHideLayerNum) + { + minHideLayerNum = globalVarToSharedVarInfo[g].hideLayerNum; + } + } + return new LayerRange(maxIntroLayerNum, minHideLayerNum); + } + public void TypeCheck() { + foreach (var proc in program.Procedures) + { + if (!QKeyValue.FindBoolAttribute(proc.Attributes, "pure")) continue; + if (QKeyValue.FindBoolAttribute(proc.Attributes, "yields")) + { + Error(proc, "Pure procedure must not yield"); + continue; + } + if (QKeyValue.FindBoolAttribute(proc.Attributes, "layer")) + { + Error(proc, "Pure procedure must not have layers"); + continue; + } + if (proc.Modifies.Count > 0) + { + Error(proc, "Pure procedure must not modify a global variable"); + continue; + } + procToAtomicProcedureInfo[proc] = new AtomicProcedureInfo(); + } + foreach (var proc in program.Procedures) + { + if (QKeyValue.FindBoolAttribute(proc.Attributes, "yields")) continue; + var procLayerNums = RemoveDuplicatesAndSort(FindLayers(proc.Attributes)); + if (procLayerNums.Count == 0) continue; + foreach (IdentifierExpr ie in proc.Modifies) + { + if (!globalVarToSharedVarInfo.ContainsKey(ie.Decl)) + { + Error(proc, "Atomic procedure cannot modify a global variable without layer numbers"); + } + else if (globalVarToSharedVarInfo[ie.Decl].introLayerNum != procLayerNums[0]) + { + Error(proc, "The introduction layer of a modified global variable must be identical to the layer of the atomic procedure"); + } + } + if (proc.Modifies.Count == 0 || procLayerNums.Count == 1) + { + procToAtomicProcedureInfo[proc] = new AtomicProcedureInfo(new HashSet(procLayerNums)); + } + else + { + Error(proc, "An atomic procedure with more than one layer must not modify a global variable"); + } + } + if (errorCount > 0) return; + + foreach (Implementation impl in program.Implementations) + { + if (!procToAtomicProcedureInfo.ContainsKey(impl.Proc)) continue; + var atomicProcedureInfo = procToAtomicProcedureInfo[impl.Proc]; + if (atomicProcedureInfo.isPure) + { + this.enclosingImpl = impl; + (new PurityChecker(this)).VisitImplementation(impl); + } + else + { + this.enclosingImpl = impl; + this.sharedVarsAccessed = new HashSet(); + (new PurityChecker(this)).VisitImplementation(impl); + LayerRange upperBound = FindLayerRange(); + LayerRange lowerBound = new LayerRange(atomicProcedureInfo.layers); + if (!lowerBound.Subset(upperBound)) + { + Error(impl, "Atomic procedure cannot access global variable"); + } + this.sharedVarsAccessed = null; + } + } + if (errorCount > 0) return; + foreach (var proc in program.Procedures) { if (!QKeyValue.FindBoolAttribute(proc.Attributes, "yields")) continue; @@ -428,25 +659,21 @@ namespace Microsoft.Boogie continue; } - minLayerNum = int.MaxValue; - maxLayerNum = -1; - canAccessSharedVars = true; + sharedVarsAccessed = new HashSet(); enclosingProc = proc; enclosingImpl = null; base.VisitEnsures(e); - canAccessSharedVars = false; - if (maxLayerNum > createdAtLayerNum) - { - Error(e, "A variable being accessed is introduced after this action is created"); - } - else if (availableUptoLayerNum > minLayerNum) + LayerRange upperBound = FindLayerRange(); + LayerRange lowerBound = new LayerRange(createdAtLayerNum, availableUptoLayerNum); + if (lowerBound.Subset(upperBound)) { - Error(e, "A variable being accessed is hidden before this action becomes unavailable"); + procToActionInfo[proc] = new AtomicActionInfo(proc, e, moverType, createdAtLayerNum, availableUptoLayerNum); } else { - procToActionInfo[proc] = new AtomicActionInfo(proc, e, moverType, createdAtLayerNum, availableUptoLayerNum); + Error(e, "A variable being accessed in this action is unavailable"); } + sharedVarsAccessed = null; } if (errorCount > 0) continue; if (!procToActionInfo.ContainsKey(proc)) @@ -463,27 +690,41 @@ namespace Microsoft.Boogie } } if (errorCount > 0) return; - foreach (var impl in program.Implementations) + foreach (Implementation node in program.Implementations) { - if (!procToActionInfo.ContainsKey(impl.Proc)) continue; - procToActionInfo[impl.Proc].hasImplementation = true; - } - foreach (var proc in procToActionInfo.Keys) - { - ActionInfo actionInfo = procToActionInfo[proc]; - if (actionInfo.isExtern && actionInfo.hasImplementation) + if (!procToActionInfo.ContainsKey(node.Proc)) continue; + foreach (Variable v in node.LocVars) { - Error(proc, "Extern procedure cannot have an implementation"); - continue; + var layer = FindLocalVariableLayer(node, v, procToActionInfo[node.Proc].createdAtLayerNum); + if (layer == int.MinValue) continue; + localVarToLocalVariableInfo[v] = new LocalVariableInfo(QKeyValue.FindBoolAttribute(node.Attributes, "ghost"), layer); } - if (actionInfo.isExtern || actionInfo.hasImplementation) continue; - if (leastUnimplementedLayerNum == int.MaxValue) + for (int i = 0; i < node.Proc.InParams.Count; i++) { - leastUnimplementedLayerNum = actionInfo.createdAtLayerNum; + Variable v = node.Proc.InParams[i]; + var layer = FindLocalVariableLayer(node.Proc, v, procToActionInfo[node.Proc].createdAtLayerNum); + if (layer == int.MinValue) continue; + localVarToLocalVariableInfo[v] = new LocalVariableInfo(false, layer); + localVarToLocalVariableInfo[node.InParams[i]] = new LocalVariableInfo(false, layer); } - else if (leastUnimplementedLayerNum != actionInfo.createdAtLayerNum) + for (int i = 0; i < node.Proc.OutParams.Count; i++) + { + Variable v = node.Proc.OutParams[i]; + var layer = FindLocalVariableLayer(node.Proc, v, procToActionInfo[node.Proc].createdAtLayerNum); + if (layer == int.MinValue) continue; + localVarToLocalVariableInfo[v] = new LocalVariableInfo(false, layer); + localVarToLocalVariableInfo[node.OutParams[i]] = new LocalVariableInfo(false, layer); + } + } + if (errorCount > 0) return; + foreach (var impl in program.Implementations) + { + if (!procToActionInfo.ContainsKey(impl.Proc)) continue; + ActionInfo actionInfo = procToActionInfo[impl.Proc]; + procToActionInfo[impl.Proc].hasImplementation = true; + if (actionInfo.isExtern) { - Error(proc, "All unimplemented atomic actions must be created at the same layer"); + Error(impl.Proc, "Extern procedure cannot have an implementation"); } } foreach (var g in this.globalVarToSharedVarInfo.Keys) @@ -500,15 +741,6 @@ namespace Microsoft.Boogie } if (errorCount > 0) return; this.VisitProgram(program); - foreach (Procedure proc in program.Procedures) - { - if (procToActionInfo.ContainsKey(proc)) continue; - foreach (var ie in proc.Modifies) - { - if (!SharedVariables.Contains(ie.Decl)) continue; - Error(proc, "A ghost procedure must not modify a global variable with layer annotation"); - } - } if (errorCount > 0) return; YieldTypeChecker.PerformYieldSafeCheck(this); new LayerEraser().VisitProgram(program); @@ -516,9 +748,26 @@ namespace Microsoft.Boogie public IEnumerable SharedVariables { - get { return this.globalVarToSharedVarInfo.Keys; } + get { return this.globalVarToSharedVarInfo.Keys; } + } + + private int FindLocalVariableLayer(Declaration decl, Variable v, int enclosingProcLayerNum) + { + var layers = FindLayers(v.Attributes); + if (layers.Count == 0) return int.MinValue; + if (layers.Count > 1) + { + Error(decl, "Incorrect number of layers"); + return int.MinValue; + } + if (layers[0] > enclosingProcLayerNum) + { + Error(decl, "Layer of local variable cannot be greater than the creation layer of enclosing procedure"); + return int.MinValue; + } + return layers[0]; } - + public override Implementation VisitImplementation(Implementation node) { if (!procToActionInfo.ContainsKey(node.Proc)) @@ -527,17 +776,9 @@ namespace Microsoft.Boogie } this.enclosingImpl = node; this.enclosingProc = null; - ghostVars = new HashSet(); - foreach (Variable v in node.LocVars) - { - if (QKeyValue.FindBoolAttribute(v.Attributes, "ghost")) - { - ghostVars.Add(v); - } - } return base.VisitImplementation(node); } - + public override Procedure VisitProcedure(Procedure node) { if (!procToActionInfo.ContainsKey(node)) @@ -584,23 +825,106 @@ namespace Microsoft.Boogie { Error(node, "The callee is not available in the caller procedure"); } - return base.VisitCallCmd(node); + for (int i = 0; i < node.Ins.Count; i++) + { + var formal = node.Proc.InParams[i]; + if (localVarToLocalVariableInfo.ContainsKey(formal)) + { + introducedLocalVarsUpperBound = localVarToLocalVariableInfo[formal].layer; + } + Visit(node.Ins[i]); + introducedLocalVarsUpperBound = int.MinValue; + } + for (int i = 0; i < node.Outs.Count; i++) + { + var formal = node.Proc.OutParams[i]; + if (!localVarToLocalVariableInfo.ContainsKey(formal)) continue; + var actual = node.Outs[i].Decl; + if (localVarToLocalVariableInfo.ContainsKey(actual) && + localVarToLocalVariableInfo[formal].layer <= localVarToLocalVariableInfo[actual].layer) + continue; + Error(node, "Formal parameter of call must be introduced no later than the actual parameter"); + } + return node; } - else + else if (procToAtomicProcedureInfo.ContainsKey(node.Proc)) { + // 1. Outputs are either all ghost or all introduced. + // 2. All outputs have the same layer; call it output layer. + // 3. If callee is impure and has outputs, output layer is a member of layer set of callee. + // 4. If callee is impure and has introduced outputs, then creation number of caller belongs to layer set of callee. + // 5. If callee is impure and modifies globals, then creation number of caller belongs to layer set of callee. + + Debug.Assert(introducedLocalVarsUpperBound == int.MinValue); + bool isGhost = false; // This assignment stops the compiler from complaining. + // In the absence of errors, isGhost is initialized by loop below. foreach (var ie in node.Outs) { - if (ghostVars.Contains(ie.Decl)) continue; - Error(node, "The output of a ghost procedure must be assigned to a ghost variable"); + if (localVarToLocalVariableInfo.ContainsKey(ie.Decl)) + { + var localVariableInfo = localVarToLocalVariableInfo[ie.Decl]; + if (introducedLocalVarsUpperBound == int.MinValue) + { + introducedLocalVarsUpperBound = localVariableInfo.layer; + isGhost = localVariableInfo.isGhost; + var atomicProcedureInfo = procToAtomicProcedureInfo[node.Proc]; + if (!atomicProcedureInfo.isPure) + { + if (!atomicProcedureInfo.layers.Contains(introducedLocalVarsUpperBound)) + { + Error(node, "Layer of output variable must be a layer of the callee"); + } + if (!isGhost && !atomicProcedureInfo.layers.Contains(enclosingProcLayerNum)) + { + Error(node, "The creation layer of caller must be a layer of the callee"); + } + } + } + else + { + if (localVariableInfo.layer != introducedLocalVarsUpperBound) + { + Error(node, "All outputs must have the same layer"); + } + if (localVariableInfo.isGhost != isGhost) + { + Error(node, "Outputs are either all ghost or all introduced"); + } + } + } + else + { + Error(node, "Output variable must be a ghost or introduced local variable"); + } + } + + if (node.Proc.Modifies.Count > 0) + { + var atomicProcedureInfo = procToAtomicProcedureInfo[node.Proc]; + if (procToActionInfo[enclosingImpl.Proc] is AtomicActionInfo) + { + if (!atomicProcedureInfo.layers.Contains(enclosingProcLayerNum)) + { + Error(node, "The layer of called atomic procedure must be identical to the creation layer of callee"); + } + } + else + { + Error(node, "Enclosing implementation must refine an atomic action"); + } + introducedLocalVarsUpperBound = enclosingProcLayerNum; + } + foreach (var e in node.Ins) + { + Visit(e); } - bool savedCanAccessSharedVars = canAccessSharedVars; - bool savedCanAccessAuxVars = canAccessGhostVars; - canAccessSharedVars = true; - canAccessGhostVars = true; - var retVal = base.VisitCallCmd(node); - canAccessSharedVars = savedCanAccessSharedVars; - canAccessGhostVars = savedCanAccessAuxVars; - return retVal; + introducedLocalVarsUpperBound = int.MinValue; + return node; + } + else + { + Error(node, "A yielding procedure can call only atomic or yielding procedures"); + return node; } } @@ -618,8 +942,8 @@ namespace Microsoft.Boogie isLeftMover = isLeftMover && actionInfo.IsLeftMover; isRightMover = isRightMover && actionInfo.IsRightMover; if (actionInfo.createdAtLayerNum > maxCalleeLayerNum) - { - maxCalleeLayerNum = actionInfo.createdAtLayerNum; + { + maxCalleeLayerNum = actionInfo.createdAtLayerNum; } if (actionInfo is AtomicActionInfo) { @@ -645,66 +969,46 @@ namespace Microsoft.Boogie return base.VisitParCallCmd(node); } - public override Cmd VisitAssignCmd(AssignCmd node) - { - Contract.Ensures(Contract.Result() == node); - for (int i = 0; i < node.Lhss.Count; ++i) - { - bool savedCanAccessSharedVars = canAccessSharedVars; - bool savedCanAccessAuxVars = canAccessGhostVars; - Variable v = node.Lhss[i].DeepAssignedVariable; - if (v is LocalVariable && ghostVars.Contains(v)) - { - canAccessSharedVars = true; - canAccessGhostVars = true; - } - this.Visit(node.Lhss[i]); - this.Visit(node.Rhss[i]); - canAccessSharedVars = savedCanAccessSharedVars; - canAccessGhostVars = savedCanAccessAuxVars; - } - return node; - } - public override Expr VisitIdentifierExpr(IdentifierExpr node) { if (node.Decl is GlobalVariable) { - if (!canAccessSharedVars) + if (sharedVarsAccessed == null) { Error(node, "Shared variable can be accessed only in atomic actions or specifications"); } else if (this.globalVarToSharedVarInfo.ContainsKey(node.Decl)) { - if (this.globalVarToSharedVarInfo[node.Decl].hideLayerNum < minLayerNum) - { - minLayerNum = this.globalVarToSharedVarInfo[node.Decl].hideLayerNum; - } - if (this.globalVarToSharedVarInfo[node.Decl].introLayerNum > maxLayerNum) - { - maxLayerNum = this.globalVarToSharedVarInfo[node.Decl].introLayerNum; - } + sharedVarsAccessed.Add(node.Decl); } else { Error(node, "Accessed shared variable must have layer annotation"); } } - else if (node.Decl is LocalVariable && ghostVars.Contains(node.Decl) && !canAccessGhostVars) + else if ((node.Decl is Formal || node.Decl is Variable) && localVarToLocalVariableInfo.ContainsKey(node.Decl)) { - Error(node, "Ghost variable can be accessed only in assertions"); - } - + var localVariableInfo = localVarToLocalVariableInfo[node.Decl]; + if (localVariableInfo.isGhost) + { + if (ghostVarIntroLayerAllowed != localVariableInfo.layer) + { + Error(node, "Ghost variable inaccessible"); + } + } + else + { + if (introducedLocalVarsUpperBound < localVariableInfo.layer) + { + Error(node, "Introduced variable inaccessible"); + } + } + } return base.VisitIdentifierExpr(node); } - + public override Ensures VisitEnsures(Ensures ensures) { - minLayerNum = int.MaxValue; - maxLayerNum = -1; - canAccessSharedVars = true; - Ensures ret = base.VisitEnsures(ensures); - canAccessSharedVars = false; ActionInfo actionInfo = procToActionInfo[enclosingProc]; AtomicActionInfo atomicActionInfo = actionInfo as AtomicActionInfo; if (atomicActionInfo != null && atomicActionInfo.ensures == ensures) @@ -713,35 +1017,50 @@ namespace Microsoft.Boogie } else { + sharedVarsAccessed = new HashSet(); + Debug.Assert(introducedLocalVarsUpperBound == int.MinValue); + introducedLocalVarsUpperBound = Least(FindLayers(ensures.Attributes)); + base.VisitEnsures(ensures); CheckAndAddLayers(ensures, ensures.Attributes, actionInfo.createdAtLayerNum); + introducedLocalVarsUpperBound = int.MinValue; + sharedVarsAccessed = null; } - return ret; + return ensures; } - + public override Requires VisitRequires(Requires requires) { - minLayerNum = int.MaxValue; - maxLayerNum = -1; - canAccessSharedVars = true; - Requires ret = base.VisitRequires(requires); - canAccessSharedVars = false; + sharedVarsAccessed = new HashSet(); + Debug.Assert(introducedLocalVarsUpperBound == int.MinValue); + introducedLocalVarsUpperBound = Least(FindLayers(requires.Attributes)); + base.VisitRequires(requires); CheckAndAddLayers(requires, requires.Attributes, procToActionInfo[enclosingProc].createdAtLayerNum); - return ret; + introducedLocalVarsUpperBound = int.MinValue; + sharedVarsAccessed = null; + return requires; } public override Cmd VisitAssertCmd(AssertCmd node) { if (enclosingImpl == null) + { + // in this case, we are visiting an assert inside a CodeExpr return base.VisitAssertCmd(node); - minLayerNum = int.MaxValue; - maxLayerNum = -1; - canAccessSharedVars = true; - canAccessGhostVars = true; - Cmd ret = base.VisitAssertCmd(node); - canAccessGhostVars = false; - canAccessSharedVars = false; + } + sharedVarsAccessed = new HashSet(); + Debug.Assert(introducedLocalVarsUpperBound == int.MinValue); + var layerNums = FindLayers(node.Attributes); + introducedLocalVarsUpperBound = Least(layerNums); + if (layerNums.Count == 1) + { + ghostVarIntroLayerAllowed = layerNums[0]; + } + base.VisitAssertCmd(node); CheckAndAddLayers(node, node.Attributes, procToActionInfo[enclosingImpl.Proc].createdAtLayerNum); - return ret; + introducedLocalVarsUpperBound = int.MinValue; + ghostVarIntroLayerAllowed = int.MinValue; + sharedVarsAccessed = null; + return node; } private List RemoveDuplicatesAndSort(List attrs) @@ -760,10 +1079,11 @@ namespace Microsoft.Boogie Error(node, "layer not present"); return; } + LayerRange upperBound = FindLayerRange(); absyToLayerNums[node] = new HashSet(); foreach (int layerNum in attrs) { - if (layerNum == leastUnimplementedLayerNum || !AllCreatedLayerNums.Contains(layerNum)) + if (!AllImplementedLayerNums.Contains(layerNum)) { Error(node, "Illegal layer number"); } @@ -771,7 +1091,7 @@ namespace Microsoft.Boogie { Error(node, "The layer cannot be greater than the layer of enclosing procedure"); } - else if (maxLayerNum < layerNum && layerNum <= minLayerNum) + else if (upperBound.Contains(layerNum)) { absyToLayerNums[node].Add(layerNum); } @@ -787,5 +1107,67 @@ namespace Microsoft.Boogie checkingContext.Error(node, message); errorCount++; } + + private class PurityChecker : StandardVisitor + { + private MoverTypeChecker moverTypeChecker; + + public PurityChecker(MoverTypeChecker moverTypeChecker) + { + this.moverTypeChecker = moverTypeChecker; + } + + public override Cmd VisitCallCmd(CallCmd node) + { + Procedure enclosingProc = moverTypeChecker.enclosingImpl.Proc; + if (!moverTypeChecker.procToAtomicProcedureInfo.ContainsKey(node.Proc)) + { + moverTypeChecker.Error(node, "Atomic procedure can only call an atomic procedure"); + return base.VisitCallCmd(node); + } + var callerInfo = moverTypeChecker.procToAtomicProcedureInfo[enclosingProc]; + var calleeInfo = moverTypeChecker.procToAtomicProcedureInfo[node.Proc]; + if (calleeInfo.isPure) + { + // do nothing + } + else if (callerInfo.isPure) + { + moverTypeChecker.Error(node, "Pure procedure can only call pure procedures"); + } + else if (!callerInfo.layers.IsSubsetOf(calleeInfo.layers)) + { + moverTypeChecker.Error(node, "Caller layers must be subset of callee layers"); + } + return base.VisitCallCmd(node); + } + + public override Cmd VisitParCallCmd(ParCallCmd node) + { + moverTypeChecker.Error(node, "Atomic procedures cannot make parallel calls"); + return node; + } + + public override Expr VisitIdentifierExpr(IdentifierExpr node) + { + Procedure enclosingProc = moverTypeChecker.enclosingImpl.Proc; + if (node.Decl is GlobalVariable) + { + if (moverTypeChecker.procToAtomicProcedureInfo[enclosingProc].isPure) + { + moverTypeChecker.Error(node, "Pure procedure cannot access global variables"); + } + else if (!moverTypeChecker.globalVarToSharedVarInfo.ContainsKey(node.Decl)) + { + moverTypeChecker.Error(node, "Atomic procedure cannot access a global variable without layer numbers"); + } + else + { + moverTypeChecker.sharedVarsAccessed.Add(node.Decl); + } + } + return node; + } + } } -} \ No newline at end of file +} diff --git a/Source/Concurrency/YieldTypeChecker.cs b/Source/Concurrency/YieldTypeChecker.cs index a698c8fd..5b479ed5 100644 --- a/Source/Concurrency/YieldTypeChecker.cs +++ b/Source/Concurrency/YieldTypeChecker.cs @@ -141,7 +141,7 @@ namespace Microsoft.Boogie Graph implGraph = Program.GraphFromImpl(impl); implGraph.ComputeLoops(); int specLayerNum = moverTypeChecker.procToActionInfo[impl.Proc].createdAtLayerNum; - foreach (int layerNum in moverTypeChecker.AllCreatedLayerNums.Except(new int[] { moverTypeChecker.leastUnimplementedLayerNum })) + foreach (int layerNum in moverTypeChecker.AllImplementedLayerNums) { if (layerNum > specLayerNum) continue; YieldTypeChecker executor = new YieldTypeChecker(moverTypeChecker, impl, layerNum, implGraph.Headers); diff --git a/Test/civl/alloc.bpl b/Test/civl/alloc.bpl new file mode 100644 index 00000000..33535b00 --- /dev/null +++ b/Test/civl/alloc.bpl @@ -0,0 +1,158 @@ +// RUN: %boogie -noinfer -useArrayTheory "%s" > "%t" +// RUN: %diff "%s.expect" "%t" +function {:builtin "MapConst"} MapConstBool(bool) : [int]bool; + +type lmap; +function {:linear "mem"} dom(lmap): [int]bool; +function map(lmap): [int]int; +function cons([int]bool, [int]int) : lmap; +axiom (forall x: [int]bool, y: [int]int :: {cons(x,y)} dom(cons(x, y)) == x && map(cons(x,y)) == y); + +function EmptyLmap(): (lmap); +axiom (dom(EmptyLmap()) == MapConstBool(false)); + +function Add(x: lmap, i: int, v: int): (lmap); +axiom (forall x: lmap, i: int, v: int :: dom(Add(x, i, v)) == dom(x)[i:=true] && map(Add(x, i, v)) == map(x)[i := v]); + +function Remove(x: lmap, i: int): (lmap); +axiom (forall x: lmap, i: int :: dom(Remove(x, i)) == dom(x)[i:=false] && map(Remove(x, i)) == map(x)); + +procedure {:yields} {:layer 2} Main() +requires {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); +ensures {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); +{ + var {:layer 1} {:linear "mem"} l: lmap; + var i: int; + par Yield() | Dummy(); + while (*) + invariant {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); + { + call l, i := Alloc(); + async call Thread(l, i); + par Yield() | Dummy(); + } + par Yield() | Dummy(); +} + +procedure {:yields} {:layer 2} Thread({:layer 1} {:linear_in "mem"} local_in: lmap, i: int) +requires {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); +ensures {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); +requires {:layer 1} dom(local_in)[i] && map(local_in)[i] == mem[i]; +requires {:layer 2} dom(local_in)[i]; +{ + var y, o: int; + var {:layer 1} {:linear "mem"} local: lmap; + var {:layer 1} {:linear "mem"} l: lmap; + + par YieldMem(local_in, i) | Dummy(); + call local := Copy(local_in); + call local := Write(local, i, 42); + call o := Read(local, i); + assert {:layer 2} o == 42; + while (*) + invariant {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); + { + call l, y := Alloc(); + call l := Write(l, y, 42); + call o := Read(l, y); + assert {:layer 2} o == 42; + call Free(l); + par Yield() | Dummy(); + } + par Yield() | Dummy(); +} + +procedure {:pure} {:inline 1} Copy({:linear_in "mem"} l: lmap) returns ({:linear "mem"} l': lmap) +{ + l' := l; +} + +procedure {:yields} {:layer 1,2} Alloc() returns ({:layer 1} {:linear "mem"} l: lmap, i: int) +requires {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); +ensures {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); +ensures {:layer 1} dom(l)[i] && map(l)[i] == mem[i]; +ensures {:right} |{ A: assume dom(l)[i]; return true; }|; +{ + call Yield(); + call i := PickAddr(); + call l := AllocLinear(i); + call YieldMem(l, i); +} + +procedure {:yields} {:layer 1,2} Free({:layer 1} {:linear_in "mem"} l: lmap) +requires {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); +ensures {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); +ensures {:both} |{ A: return true; }|; +{ + call Yield(); +} + +procedure {:yields} {:layer 1,2} Read({:layer 1} {:linear "mem"} l: lmap, i: int) returns (o: int) +requires {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); +ensures {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); +requires {:layer 1} dom(l)[i] && map(l)[i] == mem[i]; +ensures {:both} |{ A: assert dom(l)[i]; o := map(l)[i]; return true; }|; +{ + call YieldMem(l, i); + call o := ReadLow(i); + call YieldMem(l, i); +} + +procedure {:yields} {:layer 1,2} Write({:layer 1} {:linear_in "mem"} l: lmap, i: int, o: int) returns ({:layer 1} {:linear "mem"} l': lmap) +requires {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); +ensures {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); +requires {:layer 1} dom(l)[i] && map(l)[i] == mem[i]; +ensures {:layer 1} dom(l')[i] && map(l')[i] == mem[i]; +ensures {:both} |{ A: assert dom(l)[i]; l' := cons(dom(l), map(l)[i := o]); return true; }|; +{ + call YieldMem(l, i); + call WriteLow(i, o); + call l' := WriteLinear(l, i, o); + call YieldMem(l', i); +} + +procedure {:layer 1} AllocLinear(i: int) returns ({:linear "mem"} l: lmap); +modifies pool; +requires dom(pool)[i]; +ensures pool == Remove(old(pool), i) && dom(l)[i] && map(l)[i] == mem[i]; + +procedure {:layer 1} WriteLinear({:layer 1} {:linear_in "mem"} l: lmap, i: int, o: int) returns ({:layer 1} {:linear "mem"} l': lmap); +requires dom(l)[i]; +ensures l' == cons(dom(l), map(l)[i := o]); + +procedure {:yields} {:layer 1} Yield() +requires {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); +ensures {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); +{ + yield; + assert {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); +} + +procedure {:yields} {:layer 1} YieldMem({:layer 1} {:linear "mem"} l: lmap, i: int) +requires {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); +ensures {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); +requires {:layer 1} dom(l)[i] && map(l)[i] == mem[i]; +ensures {:layer 1} dom(l)[i] && map(l)[i] == mem[i]; +{ + yield; + assert {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); + assert {:layer 1} dom(l)[i] && map(l)[i] == mem[i]; +} + +procedure {:yields} {:layer 2} Dummy() +{ + yield; +} + +var {:layer 1, 1} pool: lmap; +var {:layer 0, 1} mem:[int]int; +var {:layer 0, 1} unallocated:[int]bool; + +procedure {:yields} {:layer 0, 1} ReadLow(i: int) returns (o: int); +ensures {:atomic} |{ A: o := mem[i]; return true; }|; + +procedure {:yields} {:layer 0, 1} WriteLow(i: int, o: int); +ensures {:atomic} |{ A: mem[i] := o; return true; }|; + +procedure {:yields} {:layer 0, 1} PickAddr() returns (i: int); +ensures {:atomic} |{ A: assume unallocated[i]; unallocated[i] := false; return true; }|; \ No newline at end of file diff --git a/Test/civl/alloc.bpl.expect b/Test/civl/alloc.bpl.expect new file mode 100644 index 00000000..f08c6e00 --- /dev/null +++ b/Test/civl/alloc.bpl.expect @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 12 verified, 0 errors diff --git a/Test/civl/ghost.bpl b/Test/civl/ghost.bpl index 74d16acf..1468fa56 100644 --- a/Test/civl/ghost.bpl +++ b/Test/civl/ghost.bpl @@ -5,7 +5,7 @@ var {:layer 0} x: int; procedure {:yields} {:layer 0,1} Incr(); ensures {:right} |{ A: x := x + 1; return true; }|; -procedure ghost(y: int) returns (z: int) +procedure {:pure} ghost(y: int) returns (z: int) requires y == 1; ensures z == 2; { @@ -15,8 +15,7 @@ ensures z == 2; procedure {:yields} {:layer 1,2} Incr2() ensures {:right} |{ A: x := x + 2; return true; }|; { - var {:ghost} a: int; - var {:ghost} b: int; + var {:layer 1} a: int; yield; call a := ghost(1); @@ -25,7 +24,7 @@ ensures {:right} |{ A: x := x + 2; return true; }|; yield; } -procedure ghost_0() returns (z: int) +procedure {:layer 1} ghost_0() returns (z: int) ensures z == x; { z := x; @@ -34,8 +33,8 @@ ensures z == x; procedure {:yields} {:layer 1,2} Incr2_0() ensures {:right} |{ A: x := x + 2; return true; }|; { - var {:ghost} a: int; - var {:ghost} b: int; + var {:layer 1} a: int; + var {:layer 1} b: int; yield; call a := ghost_0(); diff --git a/Test/civl/lock-introduced.bpl b/Test/civl/lock-introduced.bpl index fa0a3977..5403e5d4 100644 --- a/Test/civl/lock-introduced.bpl +++ b/Test/civl/lock-introduced.bpl @@ -67,6 +67,9 @@ ensures {:atomic} |{ A: assume !b; b := true; lock := tid; return true; }|; yield; L: call status := CAS(false, true); + if (status) { + call SetLock(tid); + } yield; goto A, B; @@ -85,9 +88,16 @@ ensures {:atomic} |{ A: b := false; lock := nil; return true; }|; { yield; call SET(false); + call SetLock(nil); yield; } +procedure {:layer 1} {:inline 1} SetLock(v: X) +modifies lock; +{ + lock := v; +} + procedure {:yields} {:layer 0,1} CAS(prev: bool, next: bool) returns (status: bool); ensures {:atomic} |{ A: goto B, C; diff --git a/Test/civl/wsq.bpl b/Test/civl/wsq.bpl index 17f53401..39dad919 100644 --- a/Test/civl/wsq.bpl +++ b/Test/civl/wsq.bpl @@ -89,13 +89,11 @@ ensures {:layer 3} {:expand} emptyInv(put_in_cs, take_in_cs, items,status,T); ensures {:atomic} |{ var i: int; A: assume status[i] == NOT_IN_Q; status[i] := IN_Q; return true; }|; { var t: int; - var {:ghost} oldH:int; - var {:ghost} oldT:int; - var {:ghost} oldStatusT:bool; + var {:ghost} {:layer 3} oldH:int; + var {:ghost} {:layer 3} oldT:int; + var {:ghost} {:layer 3} oldStatusT:bool; - - oldH := H; - oldT := T; + call oldH, oldT := GhostRead(); yield; assert {:layer 3} {:expand} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && !take_in_cs && !put_in_cs; assert {:layer 3} {:expand} {:expand} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); @@ -104,8 +102,7 @@ ensures {:atomic} |{ var i: int; A: assume status[i] == NOT_IN_Q; status[i] := I call t := readT_put(tid); - oldH := H; - oldT := T; + call oldH, oldT := GhostRead(); yield; assert {:layer 3} {:expand} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && !take_in_cs && put_in_cs; assert {:layer 3} {:expand} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); @@ -115,9 +112,8 @@ ensures {:atomic} |{ var i: int; A: assume status[i] == NOT_IN_Q; status[i] := I call writeItems_put(tid,t, task); - oldH := H; - oldT := T; - oldStatusT := status[T]; + call oldH, oldT := GhostRead(); + call oldStatusT := GhostReadStatus(); yield; assert {:layer 3} {:expand} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T) && t == T && tid == ptTid && !take_in_cs && put_in_cs; assert {:layer 3} {:expand} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); @@ -128,8 +124,7 @@ ensures {:atomic} |{ var i: int; A: assume status[i] == NOT_IN_Q; status[i] := I call writeT_put(tid, t+1); - oldH := H; - oldT := T; + call oldH, oldT := GhostRead(); yield; assert {:layer 3} {:expand} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && !take_in_cs && !put_in_cs; assert {:layer 3} {:expand} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); @@ -147,11 +142,10 @@ ensures {:atomic} |{ var i: int; A: goto B,C; B: assume status[i] == IN_Q; statu { var h, t: int; var chk: bool; - var {:ghost} oldH:int; - var {:ghost} oldT:int; + var {:ghost} {:layer 3} oldH:int; + var {:ghost} {:layer 3} oldT:int; - oldH := H; - oldT := T; + call oldH, oldT := GhostRead(); yield; assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && !take_in_cs && !put_in_cs; assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); @@ -164,8 +158,7 @@ ensures {:atomic} |{ var i: int; A: goto B,C; B: assume status[i] == IN_Q; statu invariant {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); { - oldH := H; - oldT := T; + call oldH, oldT := GhostRead(); yield; assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && !take_in_cs && !put_in_cs; assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); @@ -173,8 +166,7 @@ ensures {:atomic} |{ var i: int; A: goto B,C; B: assume status[i] == IN_Q; statu call t := readT_take_init(tid); - oldH := H; - oldT := T; + call oldH, oldT := GhostRead(); yield; assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && !take_in_cs && !put_in_cs; assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); @@ -185,8 +177,7 @@ ensures {:atomic} |{ var i: int; A: goto B,C; B: assume status[i] == IN_Q; statu t := t-1; call writeT_take(tid, t); - oldH := H; - oldT := T; + call oldH, oldT := GhostRead(); yield; assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T) && tid == ptTid && !take_in_cs && !put_in_cs && t_ss[tid] == t; assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); @@ -196,8 +187,7 @@ ensures {:atomic} |{ var i: int; A: goto B,C; B: assume status[i] == IN_Q; statu call h := readH_take(tid); - oldH := H; - oldT := T; + call oldH, oldT := GhostRead(); yield; assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T) && tid == ptTid && take_in_cs && !put_in_cs && h_ss[tid] == h && t_ss[tid] == t; assert {:layer 3} {:expand} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); @@ -214,8 +204,7 @@ ensures {:atomic} |{ var i: int; A: goto B,C; B: assume status[i] == IN_Q; statu call writeT_take_abort(tid, h); task := EMPTY; - oldH := H; - oldT := T; + call oldH, oldT := GhostRead(); yield; assert {:layer 3} h <= H; assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && !take_in_cs && !put_in_cs; @@ -227,8 +216,7 @@ ensures {:atomic} |{ var i: int; A: goto B,C; B: assume status[i] == IN_Q; statu call task, taskstatus := readItems(tid, t); - oldH := H; - oldT := T; + call oldH, oldT := GhostRead(); yield; assert {:layer 3} H >= h; assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T) && tid == ptTid && take_in_cs && h_ss[tid] == h && t_ss[tid] == t; @@ -240,8 +228,7 @@ ensures {:atomic} |{ var i: int; A: goto B,C; B: assume status[i] == IN_Q; statu if(t>h) { call takeExitCS(tid); - oldH := H; - oldT := T; + call oldH, oldT := GhostRead(); yield; assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && !take_in_cs && h_ss[tid] == h && t_ss[tid] == t; assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); @@ -250,8 +237,7 @@ ensures {:atomic} |{ var i: int; A: goto B,C; B: assume status[i] == IN_Q; statu return; } call writeT_take_eq(tid, h+1); - oldH := H; - oldT := T; + call oldH, oldT := GhostRead(); yield; assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && h_ss[tid] == h && t_ss[tid] == t; @@ -265,8 +251,7 @@ ensures {:atomic} |{ var i: int; A: goto B,C; B: assume status[i] == IN_Q; statu call chk := CAS_H_take(tid, h,h+1); - oldH := H; - oldT := T; + call oldH, oldT := GhostRead(); yield; assert {:layer 3} chk ==> (h+1 == oldH && h_ss[tid] == oldH -1 && task != EMPTY); assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && h_ss[tid] == h && t_ss[tid] == t; @@ -279,8 +264,7 @@ ensures {:atomic} |{ var i: int; A: goto B,C; B: assume status[i] == IN_Q; statu if(!chk) { - oldH := H; - oldT := T; + call oldH, oldT := GhostRead(); yield; assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && h_ss[tid] == h && t_ss[tid] == t; assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); @@ -290,8 +274,7 @@ ensures {:atomic} |{ var i: int; A: goto B,C; B: assume status[i] == IN_Q; statu goto LOOP_ENTRY_1; } - oldH := H; - oldT := T; + call oldH, oldT := GhostRead(); yield; assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && h_ss[tid] == h && t_ss[tid] == t; assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); @@ -301,8 +284,7 @@ ensures {:atomic} |{ var i: int; A: goto B,C; B: assume status[i] == IN_Q; statu return; } - oldH := H; - oldT := T; + call oldH, oldT := GhostRead(); yield; assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T) && tid == ptTid && !put_in_cs; assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); @@ -322,11 +304,10 @@ ensures {:atomic} |{ var i: int; A: goto B,C; B: assume status[i] == IN_Q; statu { var h, t: int; var chk: bool; - var {:ghost} oldH:int; - var {:ghost} oldT:int; + var {:ghost} {:layer 3} oldH:int; + var {:ghost} {:layer 3} oldT:int; - oldH := H; - oldT := T; + call oldH, oldT := GhostRead(); yield; assert {:layer 3} stealerTid(tid); assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1); @@ -341,8 +322,7 @@ ensures {:atomic} |{ var i: int; A: goto B,C; B: assume status[i] == IN_Q; statu invariant {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); invariant {:layer 3} !steal_in_cs[tid]; { - oldH := H; - oldT := T; + call oldH, oldT := GhostRead(); yield; assert {:layer 3} stealerTid(tid); assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1); @@ -352,8 +332,7 @@ ensures {:atomic} |{ var i: int; A: goto B,C; B: assume status[i] == IN_Q; statu call h := readH_steal(tid); - oldH := H; - oldT := T; + call oldH, oldT := GhostRead(); yield; assert {:layer 3} H >= h; assert {:layer 3} !steal_in_cs[tid]; @@ -365,8 +344,7 @@ ensures {:atomic} |{ var i: int; A: goto B,C; B: assume status[i] == IN_Q; statu call t := readT_steal(tid); - oldH := H; - oldT := T; + call oldH, oldT := GhostRead(); yield; assert {:layer 3} steal_in_cs[tid]; assert {:layer 3} stealerTid(tid) && H >= h && steal_in_cs[tid] && h_ss[tid] == h; @@ -381,8 +359,7 @@ ensures {:atomic} |{ var i: int; A: goto B,C; B: assume status[i] == IN_Q; statu task := EMPTY; call stealExitCS(tid); - oldH := H; - oldT := T; + call oldH, oldT := GhostRead(); yield; assert {:layer 3} !steal_in_cs[tid]; assert {:layer 3} stealerTid(tid) && !steal_in_cs[tid] && h_ss[tid] == h; @@ -395,8 +372,7 @@ ensures {:atomic} |{ var i: int; A: goto B,C; B: assume status[i] == IN_Q; statu call task, taskstatus := readItems(tid, h); - oldH := H; - oldT := T; + call oldH, oldT := GhostRead(); yield; assert {:layer 3} stealerTid(tid) && steal_in_cs[tid] && h_ss[tid] == h; assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1); @@ -408,8 +384,7 @@ ensures {:atomic} |{ var i: int; A: goto B,C; B: assume status[i] == IN_Q; statu call chk := CAS_H_steal(tid, h,h+1); - oldH := H; - oldT := T; + call oldH, oldT := GhostRead(); yield; assert {:layer 3} h_ss[tid] == h; assert {:layer 3} chk ==> (h+1 == oldH && h_ss[tid] == h && task != EMPTY && taskstatus == IN_Q); @@ -423,8 +398,7 @@ ensures {:atomic} |{ var i: int; A: goto B,C; B: assume status[i] == IN_Q; statu goto LOOP_ENTRY_2; } - oldH := H; - oldT := T; + call oldH, oldT := GhostRead(); yield; assert {:layer 3} stealerTid(tid) && !steal_in_cs[tid]; assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1); @@ -433,14 +407,24 @@ ensures {:atomic} |{ var i: int; A: goto B,C; B: assume status[i] == IN_Q; statu return; } - oldH := H; - oldT := T; + call oldH, oldT := GhostRead(); yield; assert {:layer 3} chk && task != EMPTY; assert {:layer 3} stealerTid(tid) && !steal_in_cs[tid]; assert {:layer 3} oldH <= H; } +procedure {:layer 3} {:inline 1} GhostRead() returns (oldH: int, oldT: int) +{ + oldH := H; + oldT := T; +} + +procedure {:layer 3} {:inline 1} GhostReadStatus() returns (oldStatus: bool) +{ + oldStatus := status[T]; +} + procedure {:yields}{:layer 0,3} readH_take({:linear "tid"} tid:Tid) returns (y: int); ensures {:atomic} |{A: assert tid == ptTid; y := H; -- cgit v1.2.3 From 67ae2df042134ab49c5d9e055213b2c052033962 Mon Sep 17 00:00:00 2001 From: Shaz Qadeer Date: Sat, 26 Sep 2015 21:19:33 -0700 Subject: removed a warning --- Source/Concurrency/TypeCheck.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Source/Concurrency/TypeCheck.cs b/Source/Concurrency/TypeCheck.cs index 10127d33..e5c5999d 100644 --- a/Source/Concurrency/TypeCheck.cs +++ b/Source/Concurrency/TypeCheck.cs @@ -1083,11 +1083,7 @@ namespace Microsoft.Boogie absyToLayerNums[node] = new HashSet(); foreach (int layerNum in attrs) { - if (!AllImplementedLayerNums.Contains(layerNum)) - { - Error(node, "Illegal layer number"); - } - else if (layerNum > enclosingProcLayerNum) + if (layerNum > enclosingProcLayerNum) { Error(node, "The layer cannot be greater than the layer of enclosing procedure"); } -- cgit v1.2.3 From e7a15ef3363e69b24b70f0e5de5cecc56d501a2b Mon Sep 17 00:00:00 2001 From: Shaz Qadeer Date: Sun, 27 Sep 2015 22:46:45 -0700 Subject: fixed a small bug --- Source/Concurrency/TypeCheck.cs | 2 +- Test/civl/chris5.bpl | 19 +++++++++++++++++++ Test/civl/chris5.bpl.expect | 7 +++++++ 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 Test/civl/chris5.bpl create mode 100644 Test/civl/chris5.bpl.expect diff --git a/Source/Concurrency/TypeCheck.cs b/Source/Concurrency/TypeCheck.cs index e5c5999d..79424303 100644 --- a/Source/Concurrency/TypeCheck.cs +++ b/Source/Concurrency/TypeCheck.cs @@ -386,7 +386,7 @@ namespace Microsoft.Boogie else if (callCmd.Proc.Modifies.Count == 0) { if (callCmd.Outs.Count == 0) - return false; + return true; var outputVar = callCmd.Outs[0].Decl; Debug.Assert(localVarToLocalVariableInfo.ContainsKey(outputVar)); if (localVarToLocalVariableInfo[outputVar].isGhost) diff --git a/Test/civl/chris5.bpl b/Test/civl/chris5.bpl new file mode 100644 index 00000000..23ebe424 --- /dev/null +++ b/Test/civl/chris5.bpl @@ -0,0 +1,19 @@ +// RUN: %boogie -noinfer -useArrayTheory "%s" > "%t" +// RUN: %diff "%s.expect" "%t" +var{:layer 1,1} g:int; + +procedure{:layer 1} P(x:int) + requires {:layer 1} x == 0; +{ +} + +procedure{:yields}{:layer 1,2} Y(x:int) + ensures{:atomic} |{ A: return true; }|; +{ + yield; + + call P(x); + assert{:layer 1} x == 0; + + yield; +} diff --git a/Test/civl/chris5.bpl.expect b/Test/civl/chris5.bpl.expect new file mode 100644 index 00000000..32b474f5 --- /dev/null +++ b/Test/civl/chris5.bpl.expect @@ -0,0 +1,7 @@ +chris5.bpl(15,3): Error BP5002: A precondition for this call might not hold. +chris5.bpl(6,3): Related location: This is the precondition that might not hold. +Execution trace: + chris5.bpl(13,3): anon0 + (0,0): anon00 + +Boogie program verifier finished with 1 verified, 1 error -- cgit v1.2.3 From 661de7cd7c192b40d01823f52fb91f23b06e6e58 Mon Sep 17 00:00:00 2001 From: Shaz Qadeer Date: Sun, 27 Sep 2015 23:22:01 -0700 Subject: a bug fix --- Source/Concurrency/TypeCheck.cs | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/Source/Concurrency/TypeCheck.cs b/Source/Concurrency/TypeCheck.cs index 79424303..0e257f30 100644 --- a/Source/Concurrency/TypeCheck.cs +++ b/Source/Concurrency/TypeCheck.cs @@ -379,24 +379,23 @@ namespace Microsoft.Boogie if (!procToAtomicProcedureInfo.ContainsKey(callCmd.Proc)) return true; var atomicProcedureInfo = procToAtomicProcedureInfo[callCmd.Proc]; - if (atomicProcedureInfo.isPure) + if (callCmd.Proc.Modifies.Count > 0) + { + return enclosingProcLayerNum == layerNum; + } + if (callCmd.Outs.Count == 0) { return true; } - else if (callCmd.Proc.Modifies.Count == 0) + var outputVar = callCmd.Outs[0].Decl; + var localVariableInfo = localVarToLocalVariableInfo[outputVar]; + if (localVariableInfo.isGhost) { - if (callCmd.Outs.Count == 0) - return true; - var outputVar = callCmd.Outs[0].Decl; - Debug.Assert(localVarToLocalVariableInfo.ContainsKey(outputVar)); - if (localVarToLocalVariableInfo[outputVar].isGhost) - { - return localVarToLocalVariableInfo[outputVar].layer == layerNum; - } - else - { - return enclosingProcLayerNum == layerNum; - } + return localVariableInfo.layer == layerNum; + } + if (atomicProcedureInfo.isPure) + { + return localVariableInfo.layer <= layerNum; } else { -- cgit v1.2.3 From 80e6c54ccfeac0a2ae07c18f3c8f21970e483185 Mon Sep 17 00:00:00 2001 From: Shaz Qadeer Date: Mon, 28 Sep 2015 09:42:55 -0700 Subject: cleaned up some names --- Source/Concurrency/CivlRefinement.cs | 1240 +++++++++++++++++++++++++++++ Source/Concurrency/CivlTypeChecker.cs | 1167 +++++++++++++++++++++++++++ Source/Concurrency/Concurrency.csproj | 8 +- Source/Concurrency/MoverCheck.cs | 26 +- Source/Concurrency/OwickiGries.cs | 1240 ----------------------------- Source/Concurrency/Program.cs | 12 +- Source/Concurrency/TypeCheck.cs | 1168 --------------------------- Source/Concurrency/YieldTypeChecker.cs | 36 +- Source/ExecutionEngine/ExecutionEngine.cs | 22 +- 9 files changed, 2459 insertions(+), 2460 deletions(-) create mode 100644 Source/Concurrency/CivlRefinement.cs create mode 100644 Source/Concurrency/CivlTypeChecker.cs delete mode 100644 Source/Concurrency/OwickiGries.cs delete mode 100644 Source/Concurrency/TypeCheck.cs diff --git a/Source/Concurrency/CivlRefinement.cs b/Source/Concurrency/CivlRefinement.cs new file mode 100644 index 00000000..43d0f60c --- /dev/null +++ b/Source/Concurrency/CivlRefinement.cs @@ -0,0 +1,1240 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Boogie; +using System.Diagnostics; +using System.Diagnostics.Contracts; +using Microsoft.Boogie.GraphUtil; + +namespace Microsoft.Boogie +{ + public class MyDuplicator : Duplicator + { + CivlTypeChecker civlTypeChecker; + public int layerNum; + Procedure enclosingProc; + Implementation enclosingImpl; + public Dictionary procMap; /* Original -> Duplicate */ + public Dictionary absyMap; /* Duplicate -> Original */ + public Dictionary implMap; /* Duplicate -> Original */ + public HashSet yieldingProcs; + public List impls; + + public MyDuplicator(CivlTypeChecker civlTypeChecker, int layerNum) + { + this.civlTypeChecker = civlTypeChecker; + this.layerNum = layerNum; + this.enclosingProc = null; + this.enclosingImpl = null; + this.procMap = new Dictionary(); + this.absyMap = new Dictionary(); + this.implMap = new Dictionary(); + this.yieldingProcs = new HashSet(); + this.impls = new List(); + } + + private void ProcessCallCmd(CallCmd originalCallCmd, CallCmd callCmd, List newCmds) + { + int enclosingProcLayerNum = civlTypeChecker.procToActionInfo[enclosingImpl.Proc].createdAtLayerNum; + Procedure originalProc = originalCallCmd.Proc; + + if (civlTypeChecker.procToAtomicProcedureInfo.ContainsKey(originalProc)) + { + if (civlTypeChecker.CallExists(originalCallCmd, enclosingProcLayerNum, layerNum)) + { + newCmds.Add(callCmd); + } + } + else if (civlTypeChecker.procToActionInfo.ContainsKey(originalProc)) + { + AtomicActionInfo atomicActionInfo = civlTypeChecker.procToActionInfo[originalProc] as AtomicActionInfo; + if (atomicActionInfo != null && atomicActionInfo.gate.Count > 0 && layerNum == enclosingProcLayerNum) + { + newCmds.Add(new HavocCmd(Token.NoToken, new List(new IdentifierExpr[] { Expr.Ident(dummyLocalVar) }))); + Dictionary map = new Dictionary(); + for (int i = 0; i < originalProc.InParams.Count; i++) + { + map[originalProc.InParams[i]] = callCmd.Ins[i]; + } + Substitution subst = Substituter.SubstitutionFromHashtable(map); + foreach (AssertCmd assertCmd in atomicActionInfo.gate) + { + newCmds.Add(Substituter.Apply(subst, assertCmd)); + } + } + newCmds.Add(callCmd); + } + else + { + Debug.Assert(false); + } + } + + private void ProcessParCallCmd(ParCallCmd originalParCallCmd, ParCallCmd parCallCmd, List newCmds) + { + int maxCalleeLayerNum = 0; + foreach (CallCmd iter in originalParCallCmd.CallCmds) + { + int calleeLayerNum = civlTypeChecker.procToActionInfo[iter.Proc].createdAtLayerNum; + if (calleeLayerNum > maxCalleeLayerNum) + maxCalleeLayerNum = calleeLayerNum; + } + if (layerNum > maxCalleeLayerNum) + { + for (int i = 0; i < parCallCmd.CallCmds.Count; i++) + { + ProcessCallCmd(originalParCallCmd.CallCmds[i], parCallCmd.CallCmds[i], newCmds); + absyMap[parCallCmd.CallCmds[i]] = originalParCallCmd; + } + } + else + { + newCmds.Add(parCallCmd); + } + } + + public override List VisitCmdSeq(List cmdSeq) + { + List cmds = base.VisitCmdSeq(cmdSeq); + List newCmds = new List(); + for (int i = 0; i < cmds.Count; i++) + { + Cmd originalCmd = cmdSeq[i]; + Cmd cmd = cmds[i]; + + CallCmd originalCallCmd = originalCmd as CallCmd; + if (originalCallCmd != null) + { + ProcessCallCmd(originalCallCmd, cmd as CallCmd, newCmds); + continue; + } + + ParCallCmd originalParCallCmd = originalCmd as ParCallCmd; + if (originalParCallCmd != null) + { + ProcessParCallCmd(originalParCallCmd, cmd as ParCallCmd, newCmds); + continue; + } + + newCmds.Add(cmd); + } + return newCmds; + } + + public override YieldCmd VisitYieldCmd(YieldCmd node) + { + YieldCmd yieldCmd = base.VisitYieldCmd(node); + absyMap[yieldCmd] = node; + return yieldCmd; + } + + public override Block VisitBlock(Block node) + { + Block block = base.VisitBlock(node); + absyMap[block] = node; + return block; + } + + public override Cmd VisitCallCmd(CallCmd node) + { + CallCmd callCmd = (CallCmd) base.VisitCallCmd(node); + callCmd.Proc = VisitProcedure(callCmd.Proc); + callCmd.callee = callCmd.Proc.Name; + absyMap[callCmd] = node; + return callCmd; + } + + public override Cmd VisitParCallCmd(ParCallCmd node) + { + ParCallCmd parCallCmd = (ParCallCmd) base.VisitParCallCmd(node); + absyMap[parCallCmd] = node; + return parCallCmd; + } + + public override Procedure VisitProcedure(Procedure node) + { + if (!civlTypeChecker.procToActionInfo.ContainsKey(node)) + return node; + if (!procMap.ContainsKey(node)) + { + enclosingProc = node; + Procedure proc = (Procedure)node.Clone(); + proc.Name = string.Format("{0}_{1}", node.Name, layerNum); + proc.InParams = this.VisitVariableSeq(node.InParams); + proc.Modifies = this.VisitIdentifierExprSeq(node.Modifies); + proc.OutParams = this.VisitVariableSeq(node.OutParams); + + ActionInfo actionInfo = civlTypeChecker.procToActionInfo[node]; + if (actionInfo.createdAtLayerNum < layerNum) + { + proc.Requires = new List(); + proc.Ensures = new List(); + Implementation impl; + AtomicActionInfo atomicActionInfo = actionInfo as AtomicActionInfo; + if (atomicActionInfo != null) + { + CodeExpr action = (CodeExpr)VisitCodeExpr(atomicActionInfo.action); + List cmds = new List(); + foreach (AssertCmd assertCmd in atomicActionInfo.gate) + { + cmds.Add(new AssumeCmd(Token.NoToken, (Expr)Visit(assertCmd.Expr))); + } + Block newInitBlock = new Block(Token.NoToken, "_init", cmds, + new GotoCmd(Token.NoToken, new List(new string[] { action.Blocks[0].Label }), + new List(new Block[] { action.Blocks[0] }))); + List newBlocks = new List(); + newBlocks.Add(newInitBlock); + newBlocks.AddRange(action.Blocks); + impl = new Implementation(Token.NoToken, proc.Name, node.TypeParameters, node.InParams, node.OutParams, action.LocVars, newBlocks); + } + else + { + Block newInitBlock = new Block(Token.NoToken, "_init", new List(), new ReturnCmd(Token.NoToken)); + List newBlocks = new List(); + newBlocks.Add(newInitBlock); + impl = new Implementation(Token.NoToken, proc.Name, node.TypeParameters, node.InParams, node.OutParams, new List(), newBlocks); + } + impl.Proc = proc; + impl.Proc.AddAttribute("inline", new LiteralExpr(Token.NoToken, Microsoft.Basetypes.BigNum.FromInt(1))); + impl.AddAttribute("inline", new LiteralExpr(Token.NoToken, Microsoft.Basetypes.BigNum.FromInt(1))); + impls.Add(impl); + } + else + { + yieldingProcs.Add(proc); + proc.Requires = this.VisitRequiresSeq(node.Requires); + proc.Ensures = this.VisitEnsuresSeq(node.Ensures); + } + procMap[node] = proc; + proc.Modifies = new List(); + civlTypeChecker.SharedVariables.Iter(x => proc.Modifies.Add(Expr.Ident(x))); + } + return procMap[node]; + } + + private Variable dummyLocalVar; + public override Implementation VisitImplementation(Implementation node) + { + enclosingImpl = node; + dummyLocalVar = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, "og_dummy", Type.Bool)); + Implementation impl = base.VisitImplementation(node); + implMap[impl] = node; + impl.LocVars.Add(dummyLocalVar); + impl.Name = impl.Proc.Name; + return impl; + } + + public override Requires VisitRequires(Requires node) + { + Requires requires = base.VisitRequires(node); + if (node.Free) + return requires; + if (!civlTypeChecker.absyToLayerNums[node].Contains(layerNum)) + requires.Condition = Expr.True; + return requires; + } + + public override Ensures VisitEnsures(Ensures node) + { + Ensures ensures = base.VisitEnsures(node); + if (node.Free) + return ensures; + AtomicActionInfo atomicActionInfo = civlTypeChecker.procToActionInfo[enclosingProc] as AtomicActionInfo; + bool isAtomicSpecification = atomicActionInfo != null && atomicActionInfo.ensures == node; + if (isAtomicSpecification || !civlTypeChecker.absyToLayerNums[node].Contains(layerNum)) + { + ensures.Condition = Expr.True; + ensures.Attributes = CivlRefinement.RemoveMoverAttribute(ensures.Attributes); + } + return ensures; + } + + public override Cmd VisitAssertCmd(AssertCmd node) + { + AssertCmd assertCmd = (AssertCmd) base.VisitAssertCmd(node); + if (!civlTypeChecker.absyToLayerNums[node].Contains(layerNum)) + assertCmd.Expr = Expr.True; + return assertCmd; + } + } + + public class CivlRefinement + { + LinearTypeChecker linearTypeChecker; + CivlTypeChecker civlTypeChecker; + Dictionary absyMap; + Dictionary implMap; + HashSet yieldingProcs; + int layerNum; + List globalMods; + Dictionary asyncAndParallelCallDesugarings; + List yieldCheckerProcs; + List yieldCheckerImpls; + Procedure yieldProc; + + Variable pc; + Variable ok; + Expr alpha; + Expr beta; + HashSet frame; + + public CivlRefinement(LinearTypeChecker linearTypeChecker, CivlTypeChecker civlTypeChecker, MyDuplicator duplicator) + { + this.linearTypeChecker = linearTypeChecker; + this.civlTypeChecker = civlTypeChecker; + this.absyMap = duplicator.absyMap; + this.layerNum = duplicator.layerNum; + this.implMap = duplicator.implMap; + this.yieldingProcs = duplicator.yieldingProcs; + Program program = linearTypeChecker.program; + globalMods = new List(); + foreach (Variable g in civlTypeChecker.SharedVariables) + { + globalMods.Add(Expr.Ident(g)); + } + asyncAndParallelCallDesugarings = new Dictionary(); + yieldCheckerProcs = new List(); + yieldCheckerImpls = new List(); + yieldProc = null; + } + + private IEnumerable AvailableLinearVars(Absy absy) + { + HashSet availableVars = new HashSet(linearTypeChecker.AvailableLinearVars(absyMap[absy])); + foreach (var g in civlTypeChecker.globalVarToSharedVarInfo.Keys) + { + SharedVariableInfo info = civlTypeChecker.globalVarToSharedVarInfo[g]; + if (!(info.introLayerNum <= layerNum && layerNum <= info.hideLayerNum)) + { + availableVars.Remove(g); + } + } + foreach (var v in civlTypeChecker.localVarToLocalVariableInfo.Keys) + { + LocalVariableInfo info = civlTypeChecker.localVarToLocalVariableInfo[v]; + if (info.isGhost) + { + if (info.layer != layerNum) + { + availableVars.Remove(v); + } + } + else + { + if (layerNum < info.layer) + { + availableVars.Remove(v); + } + } + } + return availableVars; + } + + private CallCmd CallToYieldProc(IToken tok, Dictionary ogOldGlobalMap, Dictionary domainNameToLocalVar) + { + List exprSeq = new List(); + foreach (string domainName in linearTypeChecker.linearDomains.Keys) + { + exprSeq.Add(Expr.Ident(domainNameToLocalVar[domainName])); + } + foreach (IdentifierExpr ie in globalMods) + { + exprSeq.Add(Expr.Ident(ogOldGlobalMap[ie.Decl])); + } + if (yieldProc == null) + { + List inputs = new List(); + foreach (string domainName in linearTypeChecker.linearDomains.Keys) + { + var domain = linearTypeChecker.linearDomains[domainName]; + Formal f = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "linear_" + domainName + "_in", new MapType(Token.NoToken, new List(), new List { domain.elementType }, Type.Bool)), true); + inputs.Add(f); + } + foreach (IdentifierExpr ie in globalMods) + { + Formal f = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, string.Format("og_global_old_{0}", ie.Decl.Name), ie.Decl.TypedIdent.Type), true); + inputs.Add(f); + } + yieldProc = new Procedure(Token.NoToken, string.Format("og_yield_{0}", layerNum), new List(), inputs, new List(), new List(), new List(), new List()); + yieldProc.AddAttribute("inline", new LiteralExpr(Token.NoToken, Microsoft.Basetypes.BigNum.FromInt(1))); + } + CallCmd yieldCallCmd = new CallCmd(Token.NoToken, yieldProc.Name, exprSeq, new List()); + yieldCallCmd.Proc = yieldProc; + return yieldCallCmd; + } + + private void AddCallToYieldProc(IToken tok, List newCmds, Dictionary ogOldGlobalMap, Dictionary domainNameToLocalVar) + { + if (!CommandLineOptions.Clo.TrustNonInterference) + { + CallCmd yieldCallCmd = CallToYieldProc(tok, ogOldGlobalMap, domainNameToLocalVar); + newCmds.Add(yieldCallCmd); + } + + if (pc != null) + { + Expr aa = OldEqualityExprForGlobals(ogOldGlobalMap); + Expr bb = OldEqualityExpr(ogOldGlobalMap); + + // assert pc || g_old == g || beta(i, g_old, o, g); + Expr assertExpr = Expr.Or(Expr.Ident(pc), Expr.Or(aa, beta)); + assertExpr.Typecheck(new TypecheckingContext(null)); + AssertCmd skipOrBetaAssertCmd = new AssertCmd(tok, assertExpr); + skipOrBetaAssertCmd.ErrorData = "Transition invariant in initial state violated"; + newCmds.Add(skipOrBetaAssertCmd); + + // assert pc ==> o_old == o && g_old == g; + assertExpr = Expr.Imp(Expr.Ident(pc), bb); + assertExpr.Typecheck(new TypecheckingContext(null)); + AssertCmd skipAssertCmd = new AssertCmd(tok, assertExpr); + skipAssertCmd.ErrorData = "Transition invariant in final state violated"; ; + newCmds.Add(skipAssertCmd); + + // pc, ok := g_old == g ==> pc, ok || beta(i, g_old, o, g); + List pcUpdateLHS = new List( + new AssignLhs[] { + new SimpleAssignLhs(Token.NoToken, Expr.Ident(pc)), + new SimpleAssignLhs(Token.NoToken, Expr.Ident(ok)) + }); + List pcUpdateRHS = new List( + new Expr[] { + Expr.Imp(aa, Expr.Ident(pc)), + Expr.Or(Expr.Ident(ok), beta) + }); + foreach (Expr e in pcUpdateRHS) + { + e.Typecheck(new TypecheckingContext(null)); + } + newCmds.Add(new AssignCmd(Token.NoToken, pcUpdateLHS, pcUpdateRHS)); + } + } + + private Dictionary ComputeAvailableExprs(IEnumerable availableLinearVars, Dictionary domainNameToInputVar) + { + Dictionary domainNameToExpr = new Dictionary(); + foreach (var domainName in linearTypeChecker.linearDomains.Keys) + { + var expr = Expr.Ident(domainNameToInputVar[domainName]); + expr.Resolve(new ResolutionContext(null)); + expr.Typecheck(new TypecheckingContext(null)); + domainNameToExpr[domainName] = expr; + } + foreach (Variable v in availableLinearVars) + { + var domainName = linearTypeChecker.FindDomainName(v); + if (!linearTypeChecker.linearDomains.ContainsKey(domainName)) continue; + var domain = linearTypeChecker.linearDomains[domainName]; + if (!domain.collectors.ContainsKey(v.TypedIdent.Type)) continue; + Expr ie = new NAryExpr(Token.NoToken, new FunctionCall(domain.collectors[v.TypedIdent.Type]), new List { Expr.Ident(v) }); + var expr = new NAryExpr(Token.NoToken, new FunctionCall(domain.mapOrBool), new List { ie, domainNameToExpr[domainName] }); + expr.Resolve(new ResolutionContext(null)); + expr.Typecheck(new TypecheckingContext(null)); + domainNameToExpr[domainName] = expr; + } + return domainNameToExpr; + } + + private void AddUpdatesToOldGlobalVars(List newCmds, Dictionary ogOldGlobalMap, Dictionary domainNameToLocalVar, Dictionary domainNameToExpr) + { + List lhss = new List(); + List rhss = new List(); + foreach (var domainName in linearTypeChecker.linearDomains.Keys) + { + lhss.Add(new SimpleAssignLhs(Token.NoToken, Expr.Ident(domainNameToLocalVar[domainName]))); + rhss.Add(domainNameToExpr[domainName]); + } + foreach (Variable g in ogOldGlobalMap.Keys) + { + lhss.Add(new SimpleAssignLhs(Token.NoToken, Expr.Ident(ogOldGlobalMap[g]))); + rhss.Add(Expr.Ident(g)); + } + if (lhss.Count > 0) + { + newCmds.Add(new AssignCmd(Token.NoToken, lhss, rhss)); + } + } + + private Expr OldEqualityExpr(Dictionary ogOldGlobalMap) + { + Expr bb = Expr.True; + foreach (Variable o in ogOldGlobalMap.Keys) + { + if (o is GlobalVariable && !frame.Contains(o)) continue; + bb = Expr.And(bb, Expr.Eq(Expr.Ident(o), Expr.Ident(ogOldGlobalMap[o]))); + bb.Type = Type.Bool; + } + return bb; + } + + private Expr OldEqualityExprForGlobals(Dictionary ogOldGlobalMap) + { + Expr bb = Expr.True; + foreach (Variable o in ogOldGlobalMap.Keys) + { + if (o is GlobalVariable && frame.Contains(o)) + { + bb = Expr.And(bb, Expr.Eq(Expr.Ident(o), Expr.Ident(ogOldGlobalMap[o]))); + bb.Type = Type.Bool; + } + } + return bb; + } + + private void DesugarYield(YieldCmd yieldCmd, List cmds, List newCmds, Dictionary ogOldGlobalMap, Dictionary domainNameToInputVar, Dictionary domainNameToLocalVar) + { + AddCallToYieldProc(yieldCmd.tok, newCmds, ogOldGlobalMap, domainNameToLocalVar); + + if (globalMods.Count > 0) + { + newCmds.Add(new HavocCmd(Token.NoToken, globalMods)); + if (pc != null) + { + // assume pc || alpha(i, g); + Expr assumeExpr = Expr.Or(Expr.Ident(pc), alpha); + assumeExpr.Type = Type.Bool; + newCmds.Add(new AssumeCmd(Token.NoToken, assumeExpr)); + } + } + + Dictionary domainNameToExpr = ComputeAvailableExprs(AvailableLinearVars(yieldCmd), domainNameToInputVar); + AddUpdatesToOldGlobalVars(newCmds, ogOldGlobalMap, domainNameToLocalVar, domainNameToExpr); + + for (int j = 0; j < cmds.Count; j++) + { + PredicateCmd predCmd = (PredicateCmd)cmds[j]; + newCmds.Add(new AssumeCmd(Token.NoToken, predCmd.Expr)); + } + } + + public void DesugarParallelCallCmd(List newCmds, ParCallCmd parCallCmd) + { + List parallelCalleeNames = new List(); + List ins = new List(); + List outs = new List(); + string procName = "og"; + foreach (CallCmd callCmd in parCallCmd.CallCmds) + { + procName = procName + "_" + callCmd.Proc.Name; + ins.AddRange(callCmd.Ins); + outs.AddRange(callCmd.Outs); + } + Procedure proc; + if (asyncAndParallelCallDesugarings.ContainsKey(procName)) + { + proc = asyncAndParallelCallDesugarings[procName]; + } + else + { + List inParams = new List(); + List outParams = new List(); + List requiresSeq = new List(); + List ensuresSeq = new List(); + int count = 0; + foreach (CallCmd callCmd in parCallCmd.CallCmds) + { + Dictionary map = new Dictionary(); + foreach (Variable x in callCmd.Proc.InParams) + { + Variable y = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, string.Format("og_{0}_{1}", count, x.Name), x.TypedIdent.Type), true); + inParams.Add(y); + map[x] = Expr.Ident(y); + } + foreach (Variable x in callCmd.Proc.OutParams) + { + Variable y = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, string.Format("og_{0}_{1}", count, x.Name), x.TypedIdent.Type), false); + outParams.Add(y); + map[x] = Expr.Ident(y); + } + Contract.Assume(callCmd.Proc.TypeParameters.Count == 0); + Substitution subst = Substituter.SubstitutionFromHashtable(map); + foreach (Requires req in callCmd.Proc.Requires) + { + requiresSeq.Add(new Requires(req.tok, req.Free, Substituter.Apply(subst, req.Condition), null, req.Attributes)); + } + foreach (Ensures ens in callCmd.Proc.Ensures) + { + ensuresSeq.Add(new Ensures(ens.tok, ens.Free, Substituter.Apply(subst, ens.Condition), null, ens.Attributes)); + } + count++; + } + proc = new Procedure(Token.NoToken, procName, new List(), inParams, outParams, requiresSeq, globalMods, ensuresSeq); + asyncAndParallelCallDesugarings[procName] = proc; + } + CallCmd dummyCallCmd = new CallCmd(parCallCmd.tok, proc.Name, ins, outs, parCallCmd.Attributes); + dummyCallCmd.Proc = proc; + newCmds.Add(dummyCallCmd); + } + + private void CreateYieldCheckerImpl(Implementation impl, List> yields) + { + if (yields.Count == 0) return; + + Dictionary map = new Dictionary(); + foreach (Variable local in impl.LocVars) + { + var copy = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, local.Name, local.TypedIdent.Type)); + map[local] = Expr.Ident(copy); + } + + Program program = linearTypeChecker.program; + List locals = new List(); + List inputs = new List(); + foreach (IdentifierExpr ie in map.Values) + { + locals.Add(ie.Decl); + } + for (int i = 0; i < impl.InParams.Count - linearTypeChecker.linearDomains.Count; i++) + { + Variable inParam = impl.InParams[i]; + Variable copy = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, inParam.Name, inParam.TypedIdent.Type)); + locals.Add(copy); + map[impl.InParams[i]] = Expr.Ident(copy); + } + { + int i = impl.InParams.Count - linearTypeChecker.linearDomains.Count; + foreach (string domainName in linearTypeChecker.linearDomains.Keys) + { + Variable inParam = impl.InParams[i]; + Variable copy = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, inParam.Name, inParam.TypedIdent.Type), true); + inputs.Add(copy); + map[impl.InParams[i]] = Expr.Ident(copy); + i++; + } + } + for (int i = 0; i < impl.OutParams.Count; i++) + { + Variable outParam = impl.OutParams[i]; + var copy = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, outParam.Name, outParam.TypedIdent.Type)); + locals.Add(copy); + map[impl.OutParams[i]] = Expr.Ident(copy); + } + Dictionary ogOldLocalMap = new Dictionary(); + Dictionary assumeMap = new Dictionary(map); + foreach (IdentifierExpr ie in globalMods) + { + Variable g = ie.Decl; + var copy = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, string.Format("og_local_old_{0}", g.Name), g.TypedIdent.Type)); + locals.Add(copy); + ogOldLocalMap[g] = Expr.Ident(copy); + Formal f = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, string.Format("og_global_old_{0}", g.Name), g.TypedIdent.Type), true); + inputs.Add(f); + assumeMap[g] = Expr.Ident(f); + } + + Substitution assumeSubst = Substituter.SubstitutionFromHashtable(assumeMap); + Substitution oldSubst = Substituter.SubstitutionFromHashtable(ogOldLocalMap); + Substitution subst = Substituter.SubstitutionFromHashtable(map); + List yieldCheckerBlocks = new List(); + List labels = new List(); + List labelTargets = new List(); + Block yieldCheckerBlock = new Block(Token.NoToken, "exit", new List(), new ReturnCmd(Token.NoToken)); + labels.Add(yieldCheckerBlock.Label); + labelTargets.Add(yieldCheckerBlock); + yieldCheckerBlocks.Add(yieldCheckerBlock); + int yieldCount = 0; + foreach (List cs in yields) + { + List newCmds = new List(); + foreach (Cmd cmd in cs) + { + PredicateCmd predCmd = (PredicateCmd)cmd; + newCmds.Add(new AssumeCmd(Token.NoToken, Substituter.ApplyReplacingOldExprs(assumeSubst, oldSubst, predCmd.Expr))); + } + foreach (Cmd cmd in cs) + { + PredicateCmd predCmd = (PredicateCmd)cmd; + var newExpr = Substituter.ApplyReplacingOldExprs(subst, oldSubst, predCmd.Expr); + if (predCmd is AssertCmd) + { + AssertCmd assertCmd = new AssertCmd(predCmd.tok, newExpr, predCmd.Attributes); + assertCmd.ErrorData = "Non-interference check failed"; + newCmds.Add(assertCmd); + } + else + { + newCmds.Add(new AssumeCmd(Token.NoToken, newExpr)); + } + } + newCmds.Add(new AssumeCmd(Token.NoToken, Expr.False)); + yieldCheckerBlock = new Block(Token.NoToken, "L" + yieldCount++, newCmds, new ReturnCmd(Token.NoToken)); + labels.Add(yieldCheckerBlock.Label); + labelTargets.Add(yieldCheckerBlock); + yieldCheckerBlocks.Add(yieldCheckerBlock); + } + yieldCheckerBlocks.Insert(0, new Block(Token.NoToken, "enter", new List(), new GotoCmd(Token.NoToken, labels, labelTargets))); + + // Create the yield checker procedure + var yieldCheckerName = string.Format("{0}_YieldChecker_{1}", "Impl", impl.Name); + var yieldCheckerProc = new Procedure(Token.NoToken, yieldCheckerName, impl.TypeParameters, inputs, new List(), new List(), new List(), new List()); + yieldCheckerProc.AddAttribute("inline", new LiteralExpr(Token.NoToken, Microsoft.Basetypes.BigNum.FromInt(1))); + yieldCheckerProcs.Add(yieldCheckerProc); + + // Create the yield checker implementation + var yieldCheckerImpl = new Implementation(Token.NoToken, yieldCheckerName, impl.TypeParameters, inputs, new List(), locals, yieldCheckerBlocks); + yieldCheckerImpl.Proc = yieldCheckerProc; + yieldCheckerImpl.AddAttribute("inline", new LiteralExpr(Token.NoToken, Microsoft.Basetypes.BigNum.FromInt(1))); + yieldCheckerImpls.Add(yieldCheckerImpl); + } + + private bool IsYieldingHeader(Graph graph, Block header) + { + foreach (Block backEdgeNode in graph.BackEdgeNodes(header)) + { + foreach (Block x in graph.NaturalLoops(header, backEdgeNode)) + { + foreach (Cmd cmd in x.Cmds) + { + if (cmd is YieldCmd) + return true; + if (cmd is ParCallCmd) + return true; + CallCmd callCmd = cmd as CallCmd; + if (callCmd == null) continue; + if (yieldingProcs.Contains(callCmd.Proc)) + return true; + } + } + } + return false; + } + + private Graph ComputeYieldingLoopHeaders(Implementation impl, out HashSet yieldingHeaders) + { + Graph graph; + impl.PruneUnreachableBlocks(); + impl.ComputePredecessorsForBlocks(); + graph = Program.GraphFromImpl(impl); + graph.ComputeLoops(); + if (!graph.Reducible) + { + throw new Exception("Irreducible flow graphs are unsupported."); + } + yieldingHeaders = new HashSet(); + IEnumerable sortedHeaders = graph.SortHeadersByDominance(); + foreach (Block header in sortedHeaders) + { + if (yieldingHeaders.Any(x => graph.DominatorMap.DominatedBy(x, header))) + { + yieldingHeaders.Add(header); + } + else if (IsYieldingHeader(graph, header)) + { + yieldingHeaders.Add(header); + } + else + { + continue; + } + } + return graph; + } + + private void SetupRefinementCheck(Implementation impl, + out List newLocalVars, + out Dictionary domainNameToInputVar, out Dictionary domainNameToLocalVar, out Dictionary ogOldGlobalMap) + { + pc = null; + ok = null; + alpha = null; + beta = null; + frame = null; + + newLocalVars = new List(); + Program program = linearTypeChecker.program; + ogOldGlobalMap = new Dictionary(); + foreach (IdentifierExpr ie in globalMods) + { + Variable g = ie.Decl; + LocalVariable l = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, string.Format("og_global_old_{0}", g.Name), g.TypedIdent.Type)); + ogOldGlobalMap[g] = l; + newLocalVars.Add(l); + } + + Procedure originalProc = implMap[impl].Proc; + ActionInfo actionInfo = civlTypeChecker.procToActionInfo[originalProc]; + if (actionInfo.createdAtLayerNum == this.layerNum) + { + pc = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, "og_pc", Type.Bool)); + newLocalVars.Add(pc); + ok = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, "og_ok", Type.Bool)); + newLocalVars.Add(ok); + Dictionary alwaysMap = new Dictionary(); + for (int i = 0; i < originalProc.InParams.Count; i++) + { + alwaysMap[originalProc.InParams[i]] = Expr.Ident(impl.InParams[i]); + } + for (int i = 0; i < originalProc.OutParams.Count; i++) + { + alwaysMap[originalProc.OutParams[i]] = Expr.Ident(impl.OutParams[i]); + } + Substitution always = Substituter.SubstitutionFromHashtable(alwaysMap); + Dictionary foroldMap = new Dictionary(); + foreach (IdentifierExpr ie in globalMods) + { + foroldMap[ie.Decl] = Expr.Ident(ogOldGlobalMap[ie.Decl]); + } + Substitution forold = Substituter.SubstitutionFromHashtable(foroldMap); + frame = new HashSet(civlTypeChecker.SharedVariables); + foreach (Variable v in civlTypeChecker.SharedVariables) + { + if (civlTypeChecker.globalVarToSharedVarInfo[v].hideLayerNum <= actionInfo.createdAtLayerNum || + civlTypeChecker.globalVarToSharedVarInfo[v].introLayerNum > actionInfo.createdAtLayerNum) + { + frame.Remove(v); + } + } + AtomicActionInfo atomicActionInfo = actionInfo as AtomicActionInfo; + if (atomicActionInfo == null) + { + beta = Expr.True; + foreach (var v in frame) + { + beta = Expr.And(beta, Expr.Eq(Expr.Ident(v), foroldMap[v])); + } + alpha = Expr.True; + } + else + { + Expr betaExpr = (new MoverCheck.TransitionRelationComputation(civlTypeChecker.program, atomicActionInfo, frame, new HashSet())).TransitionRelationCompute(true); + beta = Substituter.ApplyReplacingOldExprs(always, forold, betaExpr); + Expr alphaExpr = Expr.True; + foreach (AssertCmd assertCmd in atomicActionInfo.gate) + { + alphaExpr = Expr.And(alphaExpr, assertCmd.Expr); + alphaExpr.Type = Type.Bool; + } + alpha = Substituter.Apply(always, alphaExpr); + } + foreach (Variable f in impl.OutParams) + { + LocalVariable copy = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, string.Format("og_old_{0}", f.Name), f.TypedIdent.Type)); + newLocalVars.Add(copy); + ogOldGlobalMap[f] = copy; + } + } + + domainNameToInputVar = new Dictionary(); + domainNameToLocalVar = new Dictionary(); + { + int i = impl.InParams.Count - linearTypeChecker.linearDomains.Count; + foreach (string domainName in linearTypeChecker.linearDomains.Keys) + { + Variable inParam = impl.InParams[i]; + domainNameToInputVar[domainName] = inParam; + Variable l = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, inParam.Name + "_local", inParam.TypedIdent.Type)); + domainNameToLocalVar[domainName] = l; + newLocalVars.Add(l); + i++; + } + } + } + + private void TransformImpl(Implementation impl) + { + HashSet yieldingHeaders; + Graph graph = ComputeYieldingLoopHeaders(impl, out yieldingHeaders); + + List newLocalVars; + Dictionary domainNameToInputVar, domainNameToLocalVar; + Dictionary ogOldGlobalMap; + SetupRefinementCheck(impl, out newLocalVars, out domainNameToInputVar, out domainNameToLocalVar, out ogOldGlobalMap); + + List> yields = CollectAndDesugarYields(impl, domainNameToInputVar, domainNameToLocalVar, ogOldGlobalMap); + + List oldPcs, oldOks; + ProcessLoopHeaders(impl, graph, yieldingHeaders, domainNameToInputVar, domainNameToLocalVar, ogOldGlobalMap, out oldPcs, out oldOks); + + AddInitialBlock(impl, oldPcs, oldOks, domainNameToInputVar, domainNameToLocalVar, ogOldGlobalMap); + + CreateYieldCheckerImpl(impl, yields); + + impl.LocVars.AddRange(newLocalVars); + impl.LocVars.AddRange(oldPcs); + impl.LocVars.AddRange(oldOks); + + UnifyCallsToYieldProc(impl, ogOldGlobalMap, domainNameToLocalVar); + } + + private void UnifyCallsToYieldProc(Implementation impl, Dictionary ogOldGlobalMap, Dictionary domainNameToLocalVar) + { + CallCmd yieldCallCmd = CallToYieldProc(Token.NoToken, ogOldGlobalMap, domainNameToLocalVar); + Block yieldCheckBlock = new Block(Token.NoToken, "CallToYieldProc", new List(new Cmd[] { yieldCallCmd, new AssumeCmd(Token.NoToken, Expr.False) }), new ReturnCmd(Token.NoToken)); + List newBlocks = new List(); + foreach (Block b in impl.Blocks) + { + TransferCmd transferCmd = b.TransferCmd; + List newCmds = new List(); + for (int i = b.Cmds.Count-1; i >= 0; i--) + { + CallCmd callCmd = b.Cmds[i] as CallCmd; + if (callCmd == null || callCmd.Proc != yieldProc) + { + newCmds.Insert(0, b.Cmds[i]); + } + else + { + Block newBlock = new Block(Token.NoToken, b.Label + i, newCmds, transferCmd); + newCmds = new List(); + transferCmd = new GotoCmd(Token.NoToken, new List(new string[] { newBlock.Label, yieldCheckBlock.Label }), + new List(new Block[] { newBlock, yieldCheckBlock })); + newBlocks.Add(newBlock); + } + } + b.Cmds = newCmds; + b.TransferCmd = transferCmd; + } + impl.Blocks.AddRange(newBlocks); + impl.Blocks.Add(yieldCheckBlock); + } + + private List> CollectAndDesugarYields(Implementation impl, + Dictionary domainNameToInputVar, Dictionary domainNameToLocalVar, Dictionary ogOldGlobalMap) + { + // Collect the yield predicates and desugar yields + List> yields = new List>(); + List cmds = new List(); + foreach (Block b in impl.Blocks) + { + YieldCmd yieldCmd = null; + List newCmds = new List(); + for (int i = 0; i < b.Cmds.Count; i++) + { + Cmd cmd = b.Cmds[i]; + if (cmd is YieldCmd) + { + yieldCmd = (YieldCmd)cmd; + continue; + } + if (yieldCmd != null) + { + PredicateCmd pcmd = cmd as PredicateCmd; + if (pcmd == null) + { + DesugarYield(yieldCmd, cmds, newCmds, ogOldGlobalMap, domainNameToInputVar, domainNameToLocalVar); + if (cmds.Count > 0) + { + yields.Add(cmds); + cmds = new List(); + } + yieldCmd = null; + } + else + { + cmds.Add(pcmd); + } + } + + if (cmd is CallCmd) + { + CallCmd callCmd = cmd as CallCmd; + if (yieldingProcs.Contains(callCmd.Proc)) + { + AddCallToYieldProc(callCmd.tok, newCmds, ogOldGlobalMap, domainNameToLocalVar); + } + if (callCmd.IsAsync) + { + if (!asyncAndParallelCallDesugarings.ContainsKey(callCmd.Proc.Name)) + { + asyncAndParallelCallDesugarings[callCmd.Proc.Name] = new Procedure(Token.NoToken, string.Format("DummyAsyncTarget_{0}", callCmd.Proc.Name), callCmd.Proc.TypeParameters, callCmd.Proc.InParams, callCmd.Proc.OutParams, callCmd.Proc.Requires, new List(), new List()); + } + var dummyAsyncTargetProc = asyncAndParallelCallDesugarings[callCmd.Proc.Name]; + CallCmd dummyCallCmd = new CallCmd(callCmd.tok, dummyAsyncTargetProc.Name, callCmd.Ins, callCmd.Outs, callCmd.Attributes); + dummyCallCmd.Proc = dummyAsyncTargetProc; + newCmds.Add(dummyCallCmd); + } + else + { + newCmds.Add(callCmd); + } + if (yieldingProcs.Contains(callCmd.Proc)) + { + HashSet availableLinearVars = new HashSet(AvailableLinearVars(callCmd)); + linearTypeChecker.AddAvailableVars(callCmd, availableLinearVars); + + if (!callCmd.IsAsync && globalMods.Count > 0 && pc != null) + { + // assume pc || alpha(i, g); + Expr assumeExpr = Expr.Or(Expr.Ident(pc), alpha); + assumeExpr.Type = Type.Bool; + newCmds.Add(new AssumeCmd(Token.NoToken, assumeExpr)); + } + + Dictionary domainNameToExpr = ComputeAvailableExprs(availableLinearVars, domainNameToInputVar); + AddUpdatesToOldGlobalVars(newCmds, ogOldGlobalMap, domainNameToLocalVar, domainNameToExpr); + } + } + else if (cmd is ParCallCmd) + { + ParCallCmd parCallCmd = cmd as ParCallCmd; + AddCallToYieldProc(parCallCmd.tok, newCmds, ogOldGlobalMap, domainNameToLocalVar); + DesugarParallelCallCmd(newCmds, parCallCmd); + HashSet availableLinearVars = new HashSet(AvailableLinearVars(parCallCmd)); + linearTypeChecker.AddAvailableVars(parCallCmd, availableLinearVars); + + if (globalMods.Count > 0 && pc != null) + { + // assume pc || alpha(i, g); + Expr assumeExpr = Expr.Or(Expr.Ident(pc), alpha); + assumeExpr.Type = Type.Bool; + newCmds.Add(new AssumeCmd(Token.NoToken, assumeExpr)); + } + + Dictionary domainNameToExpr = ComputeAvailableExprs(availableLinearVars, domainNameToInputVar); + AddUpdatesToOldGlobalVars(newCmds, ogOldGlobalMap, domainNameToLocalVar, domainNameToExpr); + } + else + { + newCmds.Add(cmd); + } + } + if (yieldCmd != null) + { + DesugarYield(yieldCmd, cmds, newCmds, ogOldGlobalMap, domainNameToInputVar, domainNameToLocalVar); + if (cmds.Count > 0) + { + yields.Add(cmds); + cmds = new List(); + } + } + if (b.TransferCmd is ReturnCmd) + { + AddCallToYieldProc(b.TransferCmd.tok, newCmds, ogOldGlobalMap, domainNameToLocalVar); + if (pc != null) + { + AssertCmd assertCmd = new AssertCmd(b.TransferCmd.tok, Expr.Ident(ok)); + assertCmd.ErrorData = "Failed to execute atomic action before procedure return"; + newCmds.Add(assertCmd); + } + } + b.Cmds = newCmds; + } + return yields; + } + + private void ProcessLoopHeaders(Implementation impl, Graph graph, HashSet yieldingHeaders, + Dictionary domainNameToInputVar, Dictionary domainNameToLocalVar, Dictionary ogOldGlobalMap, + out List oldPcs, out List oldOks) + { + oldPcs = new List(); + oldOks = new List(); + foreach (Block header in yieldingHeaders) + { + LocalVariable oldPc = null; + LocalVariable oldOk = null; + if (pc != null) + { + oldPc = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, string.Format("{0}_{1}", pc.Name, header.Label), Type.Bool)); + oldPcs.Add(oldPc); + oldOk = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, string.Format("{0}_{1}", ok.Name, header.Label), Type.Bool)); + oldOks.Add(oldOk); + } + Dictionary domainNameToExpr = ComputeAvailableExprs(AvailableLinearVars(header), domainNameToInputVar); + foreach (Block pred in header.Predecessors) + { + AddCallToYieldProc(header.tok, pred.Cmds, ogOldGlobalMap, domainNameToLocalVar); + if (pc != null && !graph.BackEdgeNodes(header).Contains(pred)) + { + pred.Cmds.Add(new AssignCmd(Token.NoToken, new List( + new AssignLhs[] { new SimpleAssignLhs(Token.NoToken, Expr.Ident(oldPc)), new SimpleAssignLhs(Token.NoToken, Expr.Ident(oldOk)) }), + new List(new Expr[] { Expr.Ident(pc), Expr.Ident(ok) }))); + } + AddUpdatesToOldGlobalVars(pred.Cmds, ogOldGlobalMap, domainNameToLocalVar, domainNameToExpr); + } + List newCmds = new List(); + if (pc != null) + { + AssertCmd assertCmd; + assertCmd = new AssertCmd(header.tok, Expr.Eq(Expr.Ident(oldPc), Expr.Ident(pc))); + assertCmd.ErrorData = "Specification state must not change for transitions ending in loop headers"; + newCmds.Add(assertCmd); + assertCmd = new AssertCmd(header.tok, Expr.Imp(Expr.Ident(oldOk), Expr.Ident(ok))); + assertCmd.ErrorData = "Specification state must not change for transitions ending in loop headers"; + newCmds.Add(assertCmd); + } + foreach (string domainName in linearTypeChecker.linearDomains.Keys) + { + newCmds.Add(new AssumeCmd(Token.NoToken, Expr.Eq(Expr.Ident(domainNameToLocalVar[domainName]), domainNameToExpr[domainName]))); + } + foreach (Variable v in ogOldGlobalMap.Keys) + { + newCmds.Add(new AssumeCmd(Token.NoToken, Expr.Eq(Expr.Ident(v), Expr.Ident(ogOldGlobalMap[v])))); + } + newCmds.AddRange(header.Cmds); + header.Cmds = newCmds; + } + } + + private void AddInitialBlock(Implementation impl, List oldPcs, List oldOks, + Dictionary domainNameToInputVar, Dictionary domainNameToLocalVar, Dictionary ogOldGlobalMap) + { + // Add initial block + List lhss = new List(); + List rhss = new List(); + if (pc != null) + { + lhss.Add(new SimpleAssignLhs(Token.NoToken, Expr.Ident(pc))); + rhss.Add(Expr.False); + foreach (Variable oldPc in oldPcs) + { + lhss.Add(new SimpleAssignLhs(Token.NoToken, Expr.Ident(oldPc))); + rhss.Add(Expr.False); + } + lhss.Add(new SimpleAssignLhs(Token.NoToken, Expr.Ident(ok))); + rhss.Add(Expr.False); + foreach (Variable oldOk in oldOks) + { + lhss.Add(new SimpleAssignLhs(Token.NoToken, Expr.Ident(oldOk))); + rhss.Add(Expr.False); + } + } + Dictionary domainNameToExpr = new Dictionary(); + foreach (var domainName in linearTypeChecker.linearDomains.Keys) + { + domainNameToExpr[domainName] = Expr.Ident(domainNameToInputVar[domainName]); + } + for (int i = 0; i < impl.InParams.Count - linearTypeChecker.linearDomains.Count; i++) + { + Variable v = impl.InParams[i]; + var domainName = linearTypeChecker.FindDomainName(v); + if (domainName == null) continue; + if (!linearTypeChecker.linearDomains.ContainsKey(domainName)) continue; + var domain = linearTypeChecker.linearDomains[domainName]; + if (!domain.collectors.ContainsKey(v.TypedIdent.Type)) continue; + Expr ie = new NAryExpr(Token.NoToken, new FunctionCall(domain.collectors[v.TypedIdent.Type]), new List { Expr.Ident(v) }); + domainNameToExpr[domainName] = new NAryExpr(Token.NoToken, new FunctionCall(domain.mapOrBool), new List { ie, domainNameToExpr[domainName] }); + } + foreach (string domainName in linearTypeChecker.linearDomains.Keys) + { + lhss.Add(new SimpleAssignLhs(Token.NoToken, Expr.Ident(domainNameToLocalVar[domainName]))); + rhss.Add(domainNameToExpr[domainName]); + } + foreach (Variable g in ogOldGlobalMap.Keys) + { + lhss.Add(new SimpleAssignLhs(Token.NoToken, Expr.Ident(ogOldGlobalMap[g]))); + rhss.Add(Expr.Ident(g)); + } + if (lhss.Count > 0) + { + Block initBlock = new Block(Token.NoToken, "og_init", new List { new AssignCmd(Token.NoToken, lhss, rhss) }, new GotoCmd(Token.NoToken, new List { impl.Blocks[0].Label }, new List { impl.Blocks[0] })); + impl.Blocks.Insert(0, initBlock); + } + } + + private void AddYieldProcAndImpl(List decls) + { + if (yieldProc == null) return; + + Program program = linearTypeChecker.program; + List inputs = new List(); + foreach (string domainName in linearTypeChecker.linearDomains.Keys) + { + var domain = linearTypeChecker.linearDomains[domainName]; + Formal f = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "linear_" + domainName + "_in", new MapType(Token.NoToken, new List(), new List { domain.elementType }, Type.Bool)), true); + inputs.Add(f); + } + foreach (IdentifierExpr ie in globalMods) + { + Formal f = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, string.Format("og_global_old_{0}", ie.Decl.Name), ie.Decl.TypedIdent.Type), true); + inputs.Add(f); + } + List blocks = new List(); + TransferCmd transferCmd = new ReturnCmd(Token.NoToken); + if (yieldCheckerProcs.Count > 0) + { + List blockTargets = new List(); + List labelTargets = new List(); + int labelCount = 0; + foreach (Procedure proc in yieldCheckerProcs) + { + List exprSeq = new List(); + foreach (Variable v in inputs) + { + exprSeq.Add(Expr.Ident(v)); + } + CallCmd callCmd = new CallCmd(Token.NoToken, proc.Name, exprSeq, new List()); + callCmd.Proc = proc; + string label = string.Format("L_{0}", labelCount++); + Block block = new Block(Token.NoToken, label, new List { callCmd }, new ReturnCmd(Token.NoToken)); + labelTargets.Add(label); + blockTargets.Add(block); + blocks.Add(block); + } + transferCmd = new GotoCmd(Token.NoToken, labelTargets, blockTargets); + } + blocks.Insert(0, new Block(Token.NoToken, "enter", new List(), transferCmd)); + + var yieldImpl = new Implementation(Token.NoToken, yieldProc.Name, new List(), inputs, new List(), new List(), blocks); + yieldImpl.Proc = yieldProc; + yieldImpl.AddAttribute("inline", new LiteralExpr(Token.NoToken, Microsoft.Basetypes.BigNum.FromInt(1))); + decls.Add(yieldProc); + decls.Add(yieldImpl); + } + + public static QKeyValue RemoveYieldsAttribute(QKeyValue iter) + { + if (iter == null) return null; + iter.Next = RemoveYieldsAttribute(iter.Next); + return (iter.Key == "yields") ? iter.Next : iter; + } + + public static QKeyValue RemoveMoverAttribute(QKeyValue iter) + { + if (iter == null) return null; + iter.Next = RemoveMoverAttribute(iter.Next); + if (iter.Key == "atomic" || iter.Key == "right" || iter.Key == "left" || iter.Key == "both") + return iter.Next; + else + return iter; + } + + private List Collect() + { + List decls = new List(); + foreach (Procedure proc in yieldCheckerProcs) + { + decls.Add(proc); + } + foreach (Implementation impl in yieldCheckerImpls) + { + decls.Add(impl); + } + foreach (Procedure proc in asyncAndParallelCallDesugarings.Values) + { + decls.Add(proc); + } + AddYieldProcAndImpl(decls); + return decls; + } + + public static void AddCheckers(LinearTypeChecker linearTypeChecker, CivlTypeChecker civlTypeChecker, List decls) + { + Program program = linearTypeChecker.program; + foreach (int layerNum in civlTypeChecker.AllImplementedLayerNums) + { + if (CommandLineOptions.Clo.TrustLayersDownto <= layerNum || layerNum <= CommandLineOptions.Clo.TrustLayersUpto) continue; + + MyDuplicator duplicator = new MyDuplicator(civlTypeChecker, layerNum); + foreach (var proc in program.Procedures) + { + if (!civlTypeChecker.procToActionInfo.ContainsKey(proc)) continue; + Procedure duplicateProc = duplicator.VisitProcedure(proc); + decls.Add(duplicateProc); + } + decls.AddRange(duplicator.impls); + CivlRefinement civlTransform = new CivlRefinement(linearTypeChecker, civlTypeChecker, duplicator); + foreach (var impl in program.Implementations) + { + if (!civlTypeChecker.procToActionInfo.ContainsKey(impl.Proc) || civlTypeChecker.procToActionInfo[impl.Proc].createdAtLayerNum < layerNum) + continue; + Implementation duplicateImpl = duplicator.VisitImplementation(impl); + civlTransform.TransformImpl(duplicateImpl); + decls.Add(duplicateImpl); + } + decls.AddRange(civlTransform.Collect()); + } + } + } +} diff --git a/Source/Concurrency/CivlTypeChecker.cs b/Source/Concurrency/CivlTypeChecker.cs new file mode 100644 index 00000000..dba7ab4b --- /dev/null +++ b/Source/Concurrency/CivlTypeChecker.cs @@ -0,0 +1,1167 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.Boogie; +using System.Diagnostics.Contracts; +using System.Diagnostics; + +namespace Microsoft.Boogie +{ + public enum MoverType + { + Top, + Atomic, + Right, + Left, + Both + } + + public class ActionInfo + { + public Procedure proc; + public int createdAtLayerNum; + public int availableUptoLayerNum; + public bool hasImplementation; + public bool isExtern; + + public ActionInfo(Procedure proc, int createdAtLayerNum, int availableUptoLayerNum) + { + this.proc = proc; + this.createdAtLayerNum = createdAtLayerNum; + this.availableUptoLayerNum = availableUptoLayerNum; + this.hasImplementation = false; + this.isExtern = QKeyValue.FindBoolAttribute(proc.Attributes, "extern"); + } + + public virtual bool IsRightMover + { + get { return true; } + } + + public virtual bool IsLeftMover + { + get { return true; } + } + } + + public class AtomicActionInfo : ActionInfo + { + public Ensures ensures; + public MoverType moverType; + public List gate; + public CodeExpr action; + public List thisGate; + public CodeExpr thisAction; + public List thisInParams; + public List thisOutParams; + public List thatGate; + public CodeExpr thatAction; + public List thatInParams; + public List thatOutParams; + public HashSet actionUsedGlobalVars; + public HashSet modifiedGlobalVars; + public HashSet gateUsedGlobalVars; + public bool hasAssumeCmd; + public Dictionary thisMap; + public Dictionary thatMap; + + public bool CommutesWith(AtomicActionInfo actionInfo) + { + if (this.modifiedGlobalVars.Intersect(actionInfo.actionUsedGlobalVars).Count() > 0) + return false; + if (this.actionUsedGlobalVars.Intersect(actionInfo.modifiedGlobalVars).Count() > 0) + return false; + return true; + } + + public override bool IsRightMover + { + get { return moverType == MoverType.Right || moverType == MoverType.Both; } + } + + public override bool IsLeftMover + { + get { return moverType == MoverType.Left || moverType == MoverType.Both; } + } + + public AtomicActionInfo(Procedure proc, Ensures ensures, MoverType moverType, int layerNum, int availableUptoLayerNum) + : base(proc, layerNum, availableUptoLayerNum) + { + this.ensures = ensures; + this.moverType = moverType; + this.gate = new List(); + this.action = ensures.Condition as CodeExpr; + this.thisGate = new List(); + this.thisInParams = new List(); + this.thisOutParams = new List(); + this.thatGate = new List(); + this.thatInParams = new List(); + this.thatOutParams = new List(); + this.hasAssumeCmd = false; + this.thisMap = new Dictionary(); + this.thatMap = new Dictionary(); + + foreach (Block block in this.action.Blocks) + { + block.Cmds.ForEach(x => this.hasAssumeCmd = this.hasAssumeCmd || x is AssumeCmd); + } + + foreach (Block block in this.action.Blocks) + { + if (block.TransferCmd is ReturnExprCmd) + { + block.TransferCmd = new ReturnCmd(block.TransferCmd.tok); + } + } + + var cmds = this.action.Blocks[0].Cmds; + for (int i = 0; i < cmds.Count; i++) + { + AssertCmd assertCmd = cmds[i] as AssertCmd; + if (assertCmd == null) break; + this.gate.Add(assertCmd); + cmds[i] = new AssumeCmd(assertCmd.tok, Expr.True); + } + + foreach (Variable x in proc.InParams) + { + Variable thisx = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "this_" + x.Name, x.TypedIdent.Type), true, x.Attributes); + this.thisInParams.Add(thisx); + this.thisMap[x] = Expr.Ident(thisx); + Variable thatx = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "that_" + x.Name, x.TypedIdent.Type), true, x.Attributes); + this.thatInParams.Add(thatx); + this.thatMap[x] = Expr.Ident(thatx); + } + foreach (Variable x in proc.OutParams) + { + Variable thisx = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "this_" + x.Name, x.TypedIdent.Type), false, x.Attributes); + this.thisOutParams.Add(thisx); + this.thisMap[x] = Expr.Ident(thisx); + Variable thatx = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "that_" + x.Name, x.TypedIdent.Type), false, x.Attributes); + this.thatOutParams.Add(thatx); + this.thatMap[x] = Expr.Ident(thatx); + } + List thisLocVars = new List(); + List thatLocVars = new List(); + foreach (Variable x in this.action.LocVars) + { + Variable thisx = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "this_" + x.Name, x.TypedIdent.Type), false); + thisMap[x] = Expr.Ident(thisx); + thisLocVars.Add(thisx); + Variable thatx = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "that_" + x.Name, x.TypedIdent.Type), false); + thatMap[x] = Expr.Ident(thatx); + thatLocVars.Add(thatx); + } + Contract.Assume(proc.TypeParameters.Count == 0); + Substitution thisSubst = Substituter.SubstitutionFromHashtable(this.thisMap); + Substitution thatSubst = Substituter.SubstitutionFromHashtable(this.thatMap); + foreach (AssertCmd assertCmd in this.gate) + { + this.thisGate.Add((AssertCmd)Substituter.Apply(thisSubst, assertCmd)); + this.thatGate.Add((AssertCmd)Substituter.Apply(thatSubst, assertCmd)); + } + this.thisAction = new CodeExpr(thisLocVars, SubstituteBlocks(this.action.Blocks, thisSubst, "this_")); + this.thatAction = new CodeExpr(thatLocVars, SubstituteBlocks(this.action.Blocks, thatSubst, "that_")); + + { + VariableCollector collector = new VariableCollector(); + collector.Visit(this.action); + this.actionUsedGlobalVars = new HashSet(collector.usedVars.Where(x => x is GlobalVariable)); + } + + List modifiedVars = new List(); + foreach (Block block in this.action.Blocks) + { + block.Cmds.ForEach(cmd => cmd.AddAssignedVariables(modifiedVars)); + } + this.modifiedGlobalVars = new HashSet(modifiedVars.Where(x => x is GlobalVariable)); + + { + VariableCollector collector = new VariableCollector(); + this.gate.ForEach(assertCmd => collector.Visit(assertCmd)); + this.gateUsedGlobalVars = new HashSet(collector.usedVars.Where(x => x is GlobalVariable)); + } + } + + private List SubstituteBlocks(List blocks, Substitution subst, string blockLabelPrefix) + { + Dictionary blockMap = new Dictionary(); + List otherBlocks = new List(); + foreach (Block block in blocks) + { + List otherCmds = new List(); + foreach (Cmd cmd in block.Cmds) + { + otherCmds.Add(Substituter.Apply(subst, cmd)); + } + Block otherBlock = new Block(); + otherBlock.Cmds = otherCmds; + otherBlock.Label = blockLabelPrefix + block.Label; + otherBlocks.Add(otherBlock); + blockMap[block] = otherBlock; + } + foreach (Block block in blocks) + { + if (block.TransferCmd is ReturnCmd) + { + blockMap[block].TransferCmd = new ReturnCmd(block.TransferCmd.tok); + continue; + } + List otherGotoCmdLabelTargets = new List(); + List otherGotoCmdLabelNames = new List(); + GotoCmd gotoCmd = block.TransferCmd as GotoCmd; + foreach (Block target in gotoCmd.labelTargets) + { + otherGotoCmdLabelTargets.Add(blockMap[target]); + otherGotoCmdLabelNames.Add(blockMap[target].Label); + } + blockMap[block].TransferCmd = new GotoCmd(block.TransferCmd.tok, otherGotoCmdLabelNames, otherGotoCmdLabelTargets); + } + return otherBlocks; + } + } + + public class SharedVariableInfo + { + public int introLayerNum; + public int hideLayerNum; + + public SharedVariableInfo(int introLayerNum, int hideLayerNum) + { + this.introLayerNum = introLayerNum; + this.hideLayerNum = hideLayerNum; + } + } + + public class LayerEraser : ReadOnlyVisitor + { + private QKeyValue RemoveLayerAttribute(QKeyValue iter) + { + if (iter == null) return null; + iter.Next = RemoveLayerAttribute(iter.Next); + return (iter.Key == "layer") ? iter.Next : iter; + } + + public override Variable VisitVariable(Variable node) + { + node.Attributes = RemoveLayerAttribute(node.Attributes); + return base.VisitVariable(node); + } + + public override Procedure VisitProcedure(Procedure node) + { + node.Attributes = RemoveLayerAttribute(node.Attributes); + return base.VisitProcedure(node); + } + + public override Implementation VisitImplementation(Implementation node) + { + node.Attributes = RemoveLayerAttribute(node.Attributes); + return base.VisitImplementation(node); + } + + public override Requires VisitRequires(Requires node) + { + node.Attributes = RemoveLayerAttribute(node.Attributes); + return base.VisitRequires(node); + } + + public override Ensures VisitEnsures(Ensures node) + { + node.Attributes = RemoveLayerAttribute(node.Attributes); + return base.VisitEnsures(node); + } + + public override Cmd VisitAssertCmd(AssertCmd node) + { + node.Attributes = RemoveLayerAttribute(node.Attributes); + return base.VisitAssertCmd(node); + } + } + + public class LayerRange + { + public int lowerLayerNum; + public int upperLayerNum; + public LayerRange(int layer) + { + this.lowerLayerNum = layer; + this.upperLayerNum = layer; + } + public LayerRange(int lower, int upper) + { + this.lowerLayerNum = lower; + this.upperLayerNum = upper; + } + public LayerRange(IEnumerable layerNums) + { + int min = int.MaxValue; + int max = int.MinValue; + foreach (var layerNum in layerNums) + { + if (layerNum < min) + { + min = layerNum; + } + if (max < layerNum) + { + max = layerNum; + } + } + this.lowerLayerNum = min; + this.upperLayerNum = max; + } + public bool Contains(int layerNum) + { + return lowerLayerNum <= layerNum && layerNum <= upperLayerNum; + } + public bool Subset(int lower, int upper) + { + return lower <= lowerLayerNum && upperLayerNum <= upper; + } + public bool Equal(int lower, int upper) + { + return lower == lowerLayerNum && upperLayerNum == upper; + } + public bool Subset(LayerRange info) + { + return info.lowerLayerNum <= lowerLayerNum && upperLayerNum <= info.upperLayerNum; + } + } + + public class AtomicProcedureInfo + { + public bool isPure; + public HashSet layers; + public AtomicProcedureInfo() + { + this.isPure = true; + this.layers = null; + } + public AtomicProcedureInfo(HashSet layers) + { + this.isPure = false; + this.layers = layers; + } + } + + public class LocalVariableInfo + { + public bool isGhost; + public int layer; + public LocalVariableInfo(bool isGhost, int layer) + { + this.isGhost = isGhost; + this.layer = layer; + } + } + + public class CivlTypeChecker : ReadOnlyVisitor + { + CheckingContext checkingContext; + Procedure enclosingProc; + Implementation enclosingImpl; + HashSet sharedVarsAccessed; + int introducedLocalVarsUpperBound; + int ghostVarIntroLayerAllowed; + + public Program program; + public int errorCount; + public Dictionary globalVarToSharedVarInfo; + public Dictionary procToActionInfo; + public Dictionary procToAtomicProcedureInfo; + public Dictionary> absyToLayerNums; + public Dictionary localVarToLocalVariableInfo; + + public bool CallExists(CallCmd callCmd, int enclosingProcLayerNum, int layerNum) + { + Debug.Assert(procToAtomicProcedureInfo.ContainsKey(callCmd.Proc)); + var atomicProcedureInfo = procToAtomicProcedureInfo[callCmd.Proc]; + if (callCmd.Proc.Modifies.Count > 0) + { + return enclosingProcLayerNum == layerNum; + } + if (callCmd.Outs.Count == 0) + { + return true; + } + var outputVar = callCmd.Outs[0].Decl; + var localVariableInfo = localVarToLocalVariableInfo[outputVar]; + if (localVariableInfo.isGhost) + { + return localVariableInfo.layer == layerNum; + } + if (atomicProcedureInfo.isPure) + { + return localVariableInfo.layer <= layerNum; + } + else + { + return enclosingProcLayerNum == layerNum; + } + } + + private static List FindLayers(QKeyValue kv) + { + List layers = new List(); + for (; kv != null; kv = kv.Next) + { + if (kv.Key != "layer") continue; + foreach (var o in kv.Params) + { + Expr e = o as Expr; + if (e == null) return null; + LiteralExpr l = e as LiteralExpr; + if (l == null) return null; + if (!l.isBigNum) return null; + layers.Add(l.asBigNum.ToIntSafe); + } + } + return layers; + } + + private static int Least(IEnumerable layerNums) + { + int least = int.MaxValue; + foreach (var layer in layerNums) + { + if (layer < least) + { + least = layer; + } + } + return least; + } + + private static MoverType GetMoverType(Ensures e) + { + if (QKeyValue.FindBoolAttribute(e.Attributes, "atomic")) + return MoverType.Atomic; + if (QKeyValue.FindBoolAttribute(e.Attributes, "right")) + return MoverType.Right; + if (QKeyValue.FindBoolAttribute(e.Attributes, "left")) + return MoverType.Left; + if (QKeyValue.FindBoolAttribute(e.Attributes, "both")) + return MoverType.Both; + return MoverType.Top; + } + + public CivlTypeChecker(Program program) + { + this.errorCount = 0; + this.checkingContext = new CheckingContext(null); + this.program = program; + this.enclosingProc = null; + this.enclosingImpl = null; + this.sharedVarsAccessed = null; + this.introducedLocalVarsUpperBound = int.MinValue; + this.ghostVarIntroLayerAllowed = int.MinValue; + + this.localVarToLocalVariableInfo = new Dictionary(); + this.absyToLayerNums = new Dictionary>(); + this.globalVarToSharedVarInfo = new Dictionary(); + this.procToActionInfo = new Dictionary(); + this.procToAtomicProcedureInfo = new Dictionary(); + + foreach (var g in program.GlobalVariables) + { + List layerNums = FindLayers(g.Attributes); + if (layerNums.Count == 0) + { + // Inaccessible from yielding and atomic procedures + } + else if (layerNums.Count == 1) + { + this.globalVarToSharedVarInfo[g] = new SharedVariableInfo(layerNums[0], int.MaxValue); + } + else if (layerNums.Count == 2) + { + this.globalVarToSharedVarInfo[g] = new SharedVariableInfo(layerNums[0], layerNums[1]); + } + else + { + Error(g, "Too many layer numbers"); + } + } + } + + private HashSet allImplementedLayerNums; + public IEnumerable AllImplementedLayerNums + { + get + { + if (allImplementedLayerNums == null) + { + allImplementedLayerNums = new HashSet(); + foreach (ActionInfo actionInfo in procToActionInfo.Values) + { + if (actionInfo.hasImplementation) + { + allImplementedLayerNums.Add(actionInfo.createdAtLayerNum); + } + } + } + return allImplementedLayerNums; + } + } + + private HashSet allCreatedLayerNums; + public IEnumerable AllCreatedLayerNums + { + get + { + if (allCreatedLayerNums == null) + { + allCreatedLayerNums = new HashSet(); + foreach (ActionInfo actionInfo in procToActionInfo.Values) + { + allCreatedLayerNums.Add(actionInfo.createdAtLayerNum); + } + } + return allCreatedLayerNums; + } + } + + private LayerRange FindLayerRange() + { + int maxIntroLayerNum = int.MinValue; + int minHideLayerNum = int.MaxValue; + foreach (var g in sharedVarsAccessed) + { + if (globalVarToSharedVarInfo[g].introLayerNum > maxIntroLayerNum) + { + maxIntroLayerNum = globalVarToSharedVarInfo[g].introLayerNum; + } + if (globalVarToSharedVarInfo[g].hideLayerNum < minHideLayerNum) + { + minHideLayerNum = globalVarToSharedVarInfo[g].hideLayerNum; + } + } + return new LayerRange(maxIntroLayerNum, minHideLayerNum); + } + + public void TypeCheck() + { + foreach (var proc in program.Procedures) + { + if (!QKeyValue.FindBoolAttribute(proc.Attributes, "pure")) continue; + if (QKeyValue.FindBoolAttribute(proc.Attributes, "yields")) + { + Error(proc, "Pure procedure must not yield"); + continue; + } + if (QKeyValue.FindBoolAttribute(proc.Attributes, "layer")) + { + Error(proc, "Pure procedure must not have layers"); + continue; + } + if (proc.Modifies.Count > 0) + { + Error(proc, "Pure procedure must not modify a global variable"); + continue; + } + procToAtomicProcedureInfo[proc] = new AtomicProcedureInfo(); + } + foreach (var proc in program.Procedures) + { + if (QKeyValue.FindBoolAttribute(proc.Attributes, "yields")) continue; + var procLayerNums = RemoveDuplicatesAndSort(FindLayers(proc.Attributes)); + if (procLayerNums.Count == 0) continue; + foreach (IdentifierExpr ie in proc.Modifies) + { + if (!globalVarToSharedVarInfo.ContainsKey(ie.Decl)) + { + Error(proc, "Atomic procedure cannot modify a global variable without layer numbers"); + } + else if (globalVarToSharedVarInfo[ie.Decl].introLayerNum != procLayerNums[0]) + { + Error(proc, "The introduction layer of a modified global variable must be identical to the layer of the atomic procedure"); + } + } + if (proc.Modifies.Count == 0 || procLayerNums.Count == 1) + { + procToAtomicProcedureInfo[proc] = new AtomicProcedureInfo(new HashSet(procLayerNums)); + } + else + { + Error(proc, "An atomic procedure with more than one layer must not modify a global variable"); + } + } + if (errorCount > 0) return; + + foreach (Implementation impl in program.Implementations) + { + if (!procToAtomicProcedureInfo.ContainsKey(impl.Proc)) continue; + var atomicProcedureInfo = procToAtomicProcedureInfo[impl.Proc]; + if (atomicProcedureInfo.isPure) + { + this.enclosingImpl = impl; + (new PurityChecker(this)).VisitImplementation(impl); + } + else + { + this.enclosingImpl = impl; + this.sharedVarsAccessed = new HashSet(); + (new PurityChecker(this)).VisitImplementation(impl); + LayerRange upperBound = FindLayerRange(); + LayerRange lowerBound = new LayerRange(atomicProcedureInfo.layers); + if (!lowerBound.Subset(upperBound)) + { + Error(impl, "Atomic procedure cannot access global variable"); + } + this.sharedVarsAccessed = null; + } + } + if (errorCount > 0) return; + + foreach (var proc in program.Procedures) + { + if (!QKeyValue.FindBoolAttribute(proc.Attributes, "yields")) continue; + + int createdAtLayerNum; // must be initialized by the following code, otherwise it is an error + int availableUptoLayerNum = int.MaxValue; + List attrs = FindLayers(proc.Attributes); + if (attrs.Count == 1) + { + createdAtLayerNum = attrs[0]; + } + else if (attrs.Count == 2) + { + createdAtLayerNum = attrs[0]; + availableUptoLayerNum = attrs[1]; + } + else + { + Error(proc, "Incorrect number of layers"); + continue; + } + foreach (Ensures e in proc.Ensures) + { + MoverType moverType = GetMoverType(e); + if (moverType == MoverType.Top) continue; + CodeExpr codeExpr = e.Condition as CodeExpr; + if (codeExpr == null) + { + Error(e, "An atomic action must be a CodeExpr"); + continue; + } + if (procToActionInfo.ContainsKey(proc)) + { + Error(proc, "A procedure can have at most one atomic action"); + continue; + } + if (availableUptoLayerNum <= createdAtLayerNum) + { + Error(proc, "Creation layer number must be less than the available upto layer number"); + continue; + } + + sharedVarsAccessed = new HashSet(); + enclosingProc = proc; + enclosingImpl = null; + base.VisitEnsures(e); + LayerRange upperBound = FindLayerRange(); + LayerRange lowerBound = new LayerRange(createdAtLayerNum, availableUptoLayerNum); + if (lowerBound.Subset(upperBound)) + { + procToActionInfo[proc] = new AtomicActionInfo(proc, e, moverType, createdAtLayerNum, availableUptoLayerNum); + } + else + { + Error(e, "A variable being accessed in this action is unavailable"); + } + sharedVarsAccessed = null; + } + if (errorCount > 0) continue; + if (!procToActionInfo.ContainsKey(proc)) + { + if (availableUptoLayerNum < createdAtLayerNum) + { + Error(proc, "Creation layer number must be no more than the available upto layer number"); + continue; + } + else + { + procToActionInfo[proc] = new ActionInfo(proc, createdAtLayerNum, availableUptoLayerNum); + } + } + } + if (errorCount > 0) return; + foreach (Implementation node in program.Implementations) + { + if (!procToActionInfo.ContainsKey(node.Proc)) continue; + foreach (Variable v in node.LocVars) + { + var layer = FindLocalVariableLayer(node, v, procToActionInfo[node.Proc].createdAtLayerNum); + if (layer == int.MinValue) continue; + localVarToLocalVariableInfo[v] = new LocalVariableInfo(QKeyValue.FindBoolAttribute(node.Attributes, "ghost"), layer); + } + for (int i = 0; i < node.Proc.InParams.Count; i++) + { + Variable v = node.Proc.InParams[i]; + var layer = FindLocalVariableLayer(node.Proc, v, procToActionInfo[node.Proc].createdAtLayerNum); + if (layer == int.MinValue) continue; + localVarToLocalVariableInfo[v] = new LocalVariableInfo(false, layer); + localVarToLocalVariableInfo[node.InParams[i]] = new LocalVariableInfo(false, layer); + } + for (int i = 0; i < node.Proc.OutParams.Count; i++) + { + Variable v = node.Proc.OutParams[i]; + var layer = FindLocalVariableLayer(node.Proc, v, procToActionInfo[node.Proc].createdAtLayerNum); + if (layer == int.MinValue) continue; + localVarToLocalVariableInfo[v] = new LocalVariableInfo(false, layer); + localVarToLocalVariableInfo[node.OutParams[i]] = new LocalVariableInfo(false, layer); + } + } + if (errorCount > 0) return; + foreach (var impl in program.Implementations) + { + if (!procToActionInfo.ContainsKey(impl.Proc)) continue; + ActionInfo actionInfo = procToActionInfo[impl.Proc]; + procToActionInfo[impl.Proc].hasImplementation = true; + if (actionInfo.isExtern) + { + Error(impl.Proc, "Extern procedure cannot have an implementation"); + } + } + foreach (var g in this.globalVarToSharedVarInfo.Keys) + { + var info = globalVarToSharedVarInfo[g]; + if (!this.AllCreatedLayerNums.Contains(info.introLayerNum)) + { + Error(g, "Variable must be introduced with creation of some atomic action"); + } + if (info.hideLayerNum != int.MaxValue && !this.AllCreatedLayerNums.Contains(info.hideLayerNum)) + { + Error(g, "Variable must be hidden with creation of some atomic action"); + } + } + if (errorCount > 0) return; + this.VisitProgram(program); + if (errorCount > 0) return; + YieldTypeChecker.PerformYieldSafeCheck(this); + new LayerEraser().VisitProgram(program); + } + + public IEnumerable SharedVariables + { + get { return this.globalVarToSharedVarInfo.Keys; } + } + + private int FindLocalVariableLayer(Declaration decl, Variable v, int enclosingProcLayerNum) + { + var layers = FindLayers(v.Attributes); + if (layers.Count == 0) return int.MinValue; + if (layers.Count > 1) + { + Error(decl, "Incorrect number of layers"); + return int.MinValue; + } + if (layers[0] > enclosingProcLayerNum) + { + Error(decl, "Layer of local variable cannot be greater than the creation layer of enclosing procedure"); + return int.MinValue; + } + return layers[0]; + } + + public override Implementation VisitImplementation(Implementation node) + { + if (!procToActionInfo.ContainsKey(node.Proc)) + { + return node; + } + this.enclosingImpl = node; + this.enclosingProc = null; + return base.VisitImplementation(node); + } + + public override Procedure VisitProcedure(Procedure node) + { + if (!procToActionInfo.ContainsKey(node)) + { + return node; + } + this.enclosingProc = node; + this.enclosingImpl = null; + return base.VisitProcedure(node); + } + + public override Cmd VisitCallCmd(CallCmd node) + { + int enclosingProcLayerNum = procToActionInfo[enclosingImpl.Proc].createdAtLayerNum; + if (procToActionInfo.ContainsKey(node.Proc)) + { + ActionInfo actionInfo = procToActionInfo[node.Proc]; + if (node.IsAsync && actionInfo is AtomicActionInfo) + { + Error(node, "Target of async call cannot be an atomic action"); + } + int calleeLayerNum = procToActionInfo[node.Proc].createdAtLayerNum; + if (enclosingProcLayerNum < calleeLayerNum || + (enclosingProcLayerNum == calleeLayerNum && actionInfo is AtomicActionInfo)) + { + Error(node, "The layer of the caller must be greater than the layer of the callee"); + } + else if (enclosingProcLayerNum == calleeLayerNum && enclosingImpl.OutParams.Count > 0) + { + HashSet outParams = new HashSet(enclosingImpl.OutParams); + foreach (var x in node.Outs) + { + if (x.Decl is GlobalVariable) + { + Error(node, "A global variable cannot be used as output argument for this call"); + } + else if (outParams.Contains(x.Decl)) + { + Error(node, "An output variable of the enclosing implementation cannot be used as output argument for this call"); + } + } + } + if (actionInfo.availableUptoLayerNum < enclosingProcLayerNum) + { + Error(node, "The callee is not available in the caller procedure"); + } + for (int i = 0; i < node.Ins.Count; i++) + { + var formal = node.Proc.InParams[i]; + if (localVarToLocalVariableInfo.ContainsKey(formal)) + { + introducedLocalVarsUpperBound = localVarToLocalVariableInfo[formal].layer; + } + Visit(node.Ins[i]); + introducedLocalVarsUpperBound = int.MinValue; + } + for (int i = 0; i < node.Outs.Count; i++) + { + var formal = node.Proc.OutParams[i]; + if (!localVarToLocalVariableInfo.ContainsKey(formal)) continue; + var actual = node.Outs[i].Decl; + if (localVarToLocalVariableInfo.ContainsKey(actual) && + localVarToLocalVariableInfo[formal].layer <= localVarToLocalVariableInfo[actual].layer) + continue; + Error(node, "Formal parameter of call must be introduced no later than the actual parameter"); + } + return node; + } + else if (procToAtomicProcedureInfo.ContainsKey(node.Proc)) + { + // 1. Outputs are either all ghost or all introduced. + // 2. All outputs have the same layer; call it output layer. + // 3. If callee is impure and has outputs, output layer is a member of layer set of callee. + // 4. If callee is impure and has introduced outputs, then creation number of caller belongs to layer set of callee. + // 5. If callee is impure and modifies globals, then creation number of caller belongs to layer set of callee. + + Debug.Assert(introducedLocalVarsUpperBound == int.MinValue); + bool isGhost = false; // This assignment stops the compiler from complaining. + // In the absence of errors, isGhost is initialized by loop below. + foreach (var ie in node.Outs) + { + if (localVarToLocalVariableInfo.ContainsKey(ie.Decl)) + { + var localVariableInfo = localVarToLocalVariableInfo[ie.Decl]; + if (introducedLocalVarsUpperBound == int.MinValue) + { + introducedLocalVarsUpperBound = localVariableInfo.layer; + isGhost = localVariableInfo.isGhost; + var atomicProcedureInfo = procToAtomicProcedureInfo[node.Proc]; + if (!atomicProcedureInfo.isPure) + { + if (!atomicProcedureInfo.layers.Contains(introducedLocalVarsUpperBound)) + { + Error(node, "Layer of output variable must be a layer of the callee"); + } + if (!isGhost && !atomicProcedureInfo.layers.Contains(enclosingProcLayerNum)) + { + Error(node, "The creation layer of caller must be a layer of the callee"); + } + } + } + else + { + if (localVariableInfo.layer != introducedLocalVarsUpperBound) + { + Error(node, "All outputs must have the same layer"); + } + if (localVariableInfo.isGhost != isGhost) + { + Error(node, "Outputs are either all ghost or all introduced"); + } + } + } + else + { + Error(node, "Output variable must be a ghost or introduced local variable"); + } + } + + if (node.Proc.Modifies.Count > 0) + { + var atomicProcedureInfo = procToAtomicProcedureInfo[node.Proc]; + if (procToActionInfo[enclosingImpl.Proc] is AtomicActionInfo) + { + if (!atomicProcedureInfo.layers.Contains(enclosingProcLayerNum)) + { + Error(node, "The layer of called atomic procedure must be identical to the creation layer of callee"); + } + } + else + { + Error(node, "Enclosing implementation must refine an atomic action"); + } + introducedLocalVarsUpperBound = enclosingProcLayerNum; + } + foreach (var e in node.Ins) + { + Visit(e); + } + introducedLocalVarsUpperBound = int.MinValue; + return node; + } + else + { + Error(node, "A yielding procedure can call only atomic or yielding procedures"); + return node; + } + } + + public override Cmd VisitParCallCmd(ParCallCmd node) + { + int enclosingProcLayerNum = procToActionInfo[enclosingImpl.Proc].createdAtLayerNum; + bool isLeftMover = true; + bool isRightMover = true; + int maxCalleeLayerNum = 0; + int atomicActionCalleeLayerNum = 0; + int numAtomicActions = 0; + foreach (CallCmd iter in node.CallCmds) + { + ActionInfo actionInfo = procToActionInfo[iter.Proc]; + isLeftMover = isLeftMover && actionInfo.IsLeftMover; + isRightMover = isRightMover && actionInfo.IsRightMover; + if (actionInfo.createdAtLayerNum > maxCalleeLayerNum) + { + maxCalleeLayerNum = actionInfo.createdAtLayerNum; + } + if (actionInfo is AtomicActionInfo) + { + numAtomicActions++; + if (atomicActionCalleeLayerNum == 0) + { + atomicActionCalleeLayerNum = actionInfo.createdAtLayerNum; + } + else if (atomicActionCalleeLayerNum != actionInfo.createdAtLayerNum) + { + Error(node, "All atomic actions must be introduced at the same layer"); + } + } + } + if (numAtomicActions > 1 && !isLeftMover && !isRightMover) + { + Error(node, "The atomic actions in the parallel call must be all right movers or all left movers"); + } + if (0 < atomicActionCalleeLayerNum && atomicActionCalleeLayerNum < maxCalleeLayerNum) + { + Error(node, "Atomic actions must be introduced at the highest layer"); + } + return base.VisitParCallCmd(node); + } + + public override Expr VisitIdentifierExpr(IdentifierExpr node) + { + if (node.Decl is GlobalVariable) + { + if (sharedVarsAccessed == null) + { + Error(node, "Shared variable can be accessed only in atomic actions or specifications"); + } + else if (this.globalVarToSharedVarInfo.ContainsKey(node.Decl)) + { + sharedVarsAccessed.Add(node.Decl); + } + else + { + Error(node, "Accessed shared variable must have layer annotation"); + } + } + else if ((node.Decl is Formal || node.Decl is Variable) && localVarToLocalVariableInfo.ContainsKey(node.Decl)) + { + var localVariableInfo = localVarToLocalVariableInfo[node.Decl]; + if (localVariableInfo.isGhost) + { + if (ghostVarIntroLayerAllowed != localVariableInfo.layer) + { + Error(node, "Ghost variable inaccessible"); + } + } + else + { + if (introducedLocalVarsUpperBound < localVariableInfo.layer) + { + Error(node, "Introduced variable inaccessible"); + } + } + } + return base.VisitIdentifierExpr(node); + } + + public override Ensures VisitEnsures(Ensures ensures) + { + ActionInfo actionInfo = procToActionInfo[enclosingProc]; + AtomicActionInfo atomicActionInfo = actionInfo as AtomicActionInfo; + if (atomicActionInfo != null && atomicActionInfo.ensures == ensures) + { + // This case has already been checked + } + else + { + sharedVarsAccessed = new HashSet(); + Debug.Assert(introducedLocalVarsUpperBound == int.MinValue); + introducedLocalVarsUpperBound = Least(FindLayers(ensures.Attributes)); + base.VisitEnsures(ensures); + CheckAndAddLayers(ensures, ensures.Attributes, actionInfo.createdAtLayerNum); + introducedLocalVarsUpperBound = int.MinValue; + sharedVarsAccessed = null; + } + return ensures; + } + + public override Requires VisitRequires(Requires requires) + { + sharedVarsAccessed = new HashSet(); + Debug.Assert(introducedLocalVarsUpperBound == int.MinValue); + introducedLocalVarsUpperBound = Least(FindLayers(requires.Attributes)); + base.VisitRequires(requires); + CheckAndAddLayers(requires, requires.Attributes, procToActionInfo[enclosingProc].createdAtLayerNum); + introducedLocalVarsUpperBound = int.MinValue; + sharedVarsAccessed = null; + return requires; + } + + public override Cmd VisitAssertCmd(AssertCmd node) + { + if (enclosingImpl == null) + { + // in this case, we are visiting an assert inside a CodeExpr + return base.VisitAssertCmd(node); + } + sharedVarsAccessed = new HashSet(); + Debug.Assert(introducedLocalVarsUpperBound == int.MinValue); + var layerNums = FindLayers(node.Attributes); + introducedLocalVarsUpperBound = Least(layerNums); + if (layerNums.Count == 1) + { + ghostVarIntroLayerAllowed = layerNums[0]; + } + base.VisitAssertCmd(node); + CheckAndAddLayers(node, node.Attributes, procToActionInfo[enclosingImpl.Proc].createdAtLayerNum); + introducedLocalVarsUpperBound = int.MinValue; + ghostVarIntroLayerAllowed = int.MinValue; + sharedVarsAccessed = null; + return node; + } + + private List RemoveDuplicatesAndSort(List attrs) + { + HashSet layerSet = new HashSet(attrs); + List layers = new List(layerSet); + layers.Sort(); + return layers; + } + + private void CheckAndAddLayers(Absy node, QKeyValue attributes, int enclosingProcLayerNum) + { + List attrs = RemoveDuplicatesAndSort(FindLayers(attributes)); + if (attrs.Count == 0) + { + Error(node, "layer not present"); + return; + } + LayerRange upperBound = FindLayerRange(); + absyToLayerNums[node] = new HashSet(); + foreach (int layerNum in attrs) + { + if (layerNum > enclosingProcLayerNum) + { + Error(node, "The layer cannot be greater than the layer of enclosing procedure"); + } + else if (upperBound.Contains(layerNum)) + { + absyToLayerNums[node].Add(layerNum); + } + else + { + Error(node, string.Format("A variable being accessed in this specification is unavailable at layer {0}", layerNum)); + } + } + } + + public void Error(Absy node, string message) + { + checkingContext.Error(node, message); + errorCount++; + } + + private class PurityChecker : StandardVisitor + { + private CivlTypeChecker civlTypeChecker; + + public PurityChecker(CivlTypeChecker civlTypeChecker) + { + this.civlTypeChecker = civlTypeChecker; + } + + public override Cmd VisitCallCmd(CallCmd node) + { + Procedure enclosingProc = civlTypeChecker.enclosingImpl.Proc; + if (!civlTypeChecker.procToAtomicProcedureInfo.ContainsKey(node.Proc)) + { + civlTypeChecker.Error(node, "Atomic procedure can only call an atomic procedure"); + return base.VisitCallCmd(node); + } + var callerInfo = civlTypeChecker.procToAtomicProcedureInfo[enclosingProc]; + var calleeInfo = civlTypeChecker.procToAtomicProcedureInfo[node.Proc]; + if (calleeInfo.isPure) + { + // do nothing + } + else if (callerInfo.isPure) + { + civlTypeChecker.Error(node, "Pure procedure can only call pure procedures"); + } + else if (!callerInfo.layers.IsSubsetOf(calleeInfo.layers)) + { + civlTypeChecker.Error(node, "Caller layers must be subset of callee layers"); + } + return base.VisitCallCmd(node); + } + + public override Cmd VisitParCallCmd(ParCallCmd node) + { + civlTypeChecker.Error(node, "Atomic procedures cannot make parallel calls"); + return node; + } + + public override Expr VisitIdentifierExpr(IdentifierExpr node) + { + Procedure enclosingProc = civlTypeChecker.enclosingImpl.Proc; + if (node.Decl is GlobalVariable) + { + if (civlTypeChecker.procToAtomicProcedureInfo[enclosingProc].isPure) + { + civlTypeChecker.Error(node, "Pure procedure cannot access global variables"); + } + else if (!civlTypeChecker.globalVarToSharedVarInfo.ContainsKey(node.Decl)) + { + civlTypeChecker.Error(node, "Atomic procedure cannot access a global variable without layer numbers"); + } + else + { + civlTypeChecker.sharedVarsAccessed.Add(node.Decl); + } + } + return node; + } + } + } +} diff --git a/Source/Concurrency/Concurrency.csproj b/Source/Concurrency/Concurrency.csproj index f15ebca3..113019fd 100644 --- a/Source/Concurrency/Concurrency.csproj +++ b/Source/Concurrency/Concurrency.csproj @@ -1,4 +1,4 @@ - + @@ -73,11 +73,11 @@ - + - + @@ -112,4 +112,4 @@ --> - \ No newline at end of file + diff --git a/Source/Concurrency/MoverCheck.cs b/Source/Concurrency/MoverCheck.cs index 7c6d4ac4..732bcaa4 100644 --- a/Source/Concurrency/MoverCheck.cs +++ b/Source/Concurrency/MoverCheck.cs @@ -10,29 +10,29 @@ namespace Microsoft.Boogie public class MoverCheck { LinearTypeChecker linearTypeChecker; - MoverTypeChecker moverTypeChecker; + CivlTypeChecker civlTypeChecker; List decls; HashSet> commutativityCheckerCache; HashSet> gatePreservationCheckerCache; HashSet> failurePreservationCheckerCache; - private MoverCheck(LinearTypeChecker linearTypeChecker, MoverTypeChecker moverTypeChecker, List decls) + private MoverCheck(LinearTypeChecker linearTypeChecker, CivlTypeChecker civlTypeChecker, List decls) { this.linearTypeChecker = linearTypeChecker; - this.moverTypeChecker = moverTypeChecker; + this.civlTypeChecker = civlTypeChecker; this.decls = decls; this.commutativityCheckerCache = new HashSet>(); this.gatePreservationCheckerCache = new HashSet>(); this.failurePreservationCheckerCache = new HashSet>(); } - public static void AddCheckers(LinearTypeChecker linearTypeChecker, MoverTypeChecker moverTypeChecker, List decls) + public static void AddCheckers(LinearTypeChecker linearTypeChecker, CivlTypeChecker civlTypeChecker, List decls) { - if (moverTypeChecker.procToActionInfo.Count == 0) + if (civlTypeChecker.procToActionInfo.Count == 0) return; - List sortedByCreatedLayerNum = new List(moverTypeChecker.procToActionInfo.Values.Where(x => x is AtomicActionInfo && !x.isExtern)); + List sortedByCreatedLayerNum = new List(civlTypeChecker.procToActionInfo.Values.Where(x => x is AtomicActionInfo && !x.isExtern)); sortedByCreatedLayerNum.Sort((x, y) => { return (x.createdAtLayerNum == y.createdAtLayerNum) ? 0 : (x.createdAtLayerNum < y.createdAtLayerNum) ? -1 : 1; }); - List sortedByAvailableUptoLayerNum = new List(moverTypeChecker.procToActionInfo.Values.Where(x => x is AtomicActionInfo && !x.isExtern)); + List sortedByAvailableUptoLayerNum = new List(civlTypeChecker.procToActionInfo.Values.Where(x => x is AtomicActionInfo && !x.isExtern)); sortedByAvailableUptoLayerNum.Sort((x, y) => { return (x.availableUptoLayerNum == y.availableUptoLayerNum) ? 0 : (x.availableUptoLayerNum < y.availableUptoLayerNum) ? -1 : 1; }); Dictionary> pools = new Dictionary>(); @@ -60,8 +60,8 @@ namespace Microsoft.Boogie currPool = pools[currLayerNum]; } - Program program = moverTypeChecker.program; - MoverCheck moverChecking = new MoverCheck(linearTypeChecker, moverTypeChecker, decls); + Program program = civlTypeChecker.program; + MoverCheck moverChecking = new MoverCheck(linearTypeChecker, civlTypeChecker, decls); foreach (int layerNum in pools.Keys) { foreach (AtomicActionInfo first in pools[layerNum]) @@ -537,7 +537,7 @@ namespace Microsoft.Boogie ensures.Add(ensureCheck); string checkerName = string.Format("CommutativityChecker_{0}_{1}", first.proc.Name, second.proc.Name); List globalVars = new List(); - moverTypeChecker.SharedVariables.Iter(x => globalVars.Add(Expr.Ident(x))); + civlTypeChecker.SharedVariables.Iter(x => globalVars.Add(Expr.Ident(x))); Procedure proc = new Procedure(Token.NoToken, checkerName, new List(), inputs, outputs, requires, globalVars, ensures); Implementation impl = new Implementation(Token.NoToken, checkerName, new List(), inputs, outputs, locals, blocks); impl.Proc = proc; @@ -580,7 +580,7 @@ namespace Microsoft.Boogie requires.Add(new Requires(false, assertCmd.Expr)); string checkerName = string.Format("GatePreservationChecker_{0}_{1}", first.proc.Name, second.proc.Name); List globalVars = new List(); - moverTypeChecker.SharedVariables.Iter(x => globalVars.Add(Expr.Ident(x))); + civlTypeChecker.SharedVariables.Iter(x => globalVars.Add(Expr.Ident(x))); Procedure proc = new Procedure(Token.NoToken, checkerName, new List(), inputs, outputs, requires, globalVars, ensures); Implementation impl = new Implementation(Token.NoToken, checkerName, new List(), inputs, outputs, locals, secondBlocks); impl.Proc = proc; @@ -628,7 +628,7 @@ namespace Microsoft.Boogie requires.Add(new Requires(false, assertCmd.Expr)); string checkerName = string.Format("FailurePreservationChecker_{0}_{1}", first.proc.Name, second.proc.Name); List globalVars = new List(); - moverTypeChecker.SharedVariables.Iter(x => globalVars.Add(Expr.Ident(x))); + civlTypeChecker.SharedVariables.Iter(x => globalVars.Add(Expr.Ident(x))); Procedure proc = new Procedure(Token.NoToken, checkerName, new List(), inputs, outputs, requires, globalVars, ensures); Implementation impl = new Implementation(Token.NoToken, checkerName, new List(), inputs, outputs, locals, secondBlocks); impl.Proc = proc; @@ -662,7 +662,7 @@ namespace Microsoft.Boogie blocks.Add(new Block(Token.NoToken, "L", new List(), new ReturnCmd(Token.NoToken))); string checkerName = string.Format("NonBlockingChecker_{0}", second.proc.Name); List globalVars = new List(); - moverTypeChecker.SharedVariables.Iter(x => globalVars.Add(Expr.Ident(x))); + civlTypeChecker.SharedVariables.Iter(x => globalVars.Add(Expr.Ident(x))); Procedure proc = new Procedure(Token.NoToken, checkerName, new List(), inputs, new List(), requires, globalVars, ensures); Implementation impl = new Implementation(Token.NoToken, checkerName, new List(), inputs, new List(), new List(), blocks); impl.Proc = proc; diff --git a/Source/Concurrency/OwickiGries.cs b/Source/Concurrency/OwickiGries.cs deleted file mode 100644 index d861e2f3..00000000 --- a/Source/Concurrency/OwickiGries.cs +++ /dev/null @@ -1,1240 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Microsoft.Boogie; -using System.Diagnostics; -using System.Diagnostics.Contracts; -using Microsoft.Boogie.GraphUtil; - -namespace Microsoft.Boogie -{ - public class MyDuplicator : Duplicator - { - MoverTypeChecker moverTypeChecker; - public int layerNum; - Procedure enclosingProc; - Implementation enclosingImpl; - public Dictionary procMap; /* Original -> Duplicate */ - public Dictionary absyMap; /* Duplicate -> Original */ - public Dictionary implMap; /* Duplicate -> Original */ - public HashSet yieldingProcs; - public List impls; - - public MyDuplicator(MoverTypeChecker moverTypeChecker, int layerNum) - { - this.moverTypeChecker = moverTypeChecker; - this.layerNum = layerNum; - this.enclosingProc = null; - this.enclosingImpl = null; - this.procMap = new Dictionary(); - this.absyMap = new Dictionary(); - this.implMap = new Dictionary(); - this.yieldingProcs = new HashSet(); - this.impls = new List(); - } - - private void ProcessCallCmd(CallCmd originalCallCmd, CallCmd callCmd, List newCmds) - { - int enclosingProcLayerNum = moverTypeChecker.procToActionInfo[enclosingImpl.Proc].createdAtLayerNum; - Procedure originalProc = originalCallCmd.Proc; - - if (moverTypeChecker.procToAtomicProcedureInfo.ContainsKey(originalProc)) - { - if (moverTypeChecker.CallExists(originalCallCmd, enclosingProcLayerNum, layerNum)) - { - newCmds.Add(callCmd); - } - } - else if (moverTypeChecker.procToActionInfo.ContainsKey(originalProc)) - { - AtomicActionInfo atomicActionInfo = moverTypeChecker.procToActionInfo[originalProc] as AtomicActionInfo; - if (atomicActionInfo != null && atomicActionInfo.gate.Count > 0 && layerNum == enclosingProcLayerNum) - { - newCmds.Add(new HavocCmd(Token.NoToken, new List(new IdentifierExpr[] { Expr.Ident(dummyLocalVar) }))); - Dictionary map = new Dictionary(); - for (int i = 0; i < originalProc.InParams.Count; i++) - { - map[originalProc.InParams[i]] = callCmd.Ins[i]; - } - Substitution subst = Substituter.SubstitutionFromHashtable(map); - foreach (AssertCmd assertCmd in atomicActionInfo.gate) - { - newCmds.Add(Substituter.Apply(subst, assertCmd)); - } - } - newCmds.Add(callCmd); - } - else - { - Debug.Assert(false); - } - } - - private void ProcessParCallCmd(ParCallCmd originalParCallCmd, ParCallCmd parCallCmd, List newCmds) - { - int maxCalleeLayerNum = 0; - foreach (CallCmd iter in originalParCallCmd.CallCmds) - { - int calleeLayerNum = moverTypeChecker.procToActionInfo[iter.Proc].createdAtLayerNum; - if (calleeLayerNum > maxCalleeLayerNum) - maxCalleeLayerNum = calleeLayerNum; - } - if (layerNum > maxCalleeLayerNum) - { - for (int i = 0; i < parCallCmd.CallCmds.Count; i++) - { - ProcessCallCmd(originalParCallCmd.CallCmds[i], parCallCmd.CallCmds[i], newCmds); - absyMap[parCallCmd.CallCmds[i]] = originalParCallCmd; - } - } - else - { - newCmds.Add(parCallCmd); - } - } - - public override List VisitCmdSeq(List cmdSeq) - { - List cmds = base.VisitCmdSeq(cmdSeq); - List newCmds = new List(); - for (int i = 0; i < cmds.Count; i++) - { - Cmd originalCmd = cmdSeq[i]; - Cmd cmd = cmds[i]; - - CallCmd originalCallCmd = originalCmd as CallCmd; - if (originalCallCmd != null) - { - ProcessCallCmd(originalCallCmd, cmd as CallCmd, newCmds); - continue; - } - - ParCallCmd originalParCallCmd = originalCmd as ParCallCmd; - if (originalParCallCmd != null) - { - ProcessParCallCmd(originalParCallCmd, cmd as ParCallCmd, newCmds); - continue; - } - - newCmds.Add(cmd); - } - return newCmds; - } - - public override YieldCmd VisitYieldCmd(YieldCmd node) - { - YieldCmd yieldCmd = base.VisitYieldCmd(node); - absyMap[yieldCmd] = node; - return yieldCmd; - } - - public override Block VisitBlock(Block node) - { - Block block = base.VisitBlock(node); - absyMap[block] = node; - return block; - } - - public override Cmd VisitCallCmd(CallCmd node) - { - CallCmd callCmd = (CallCmd) base.VisitCallCmd(node); - callCmd.Proc = VisitProcedure(callCmd.Proc); - callCmd.callee = callCmd.Proc.Name; - absyMap[callCmd] = node; - return callCmd; - } - - public override Cmd VisitParCallCmd(ParCallCmd node) - { - ParCallCmd parCallCmd = (ParCallCmd) base.VisitParCallCmd(node); - absyMap[parCallCmd] = node; - return parCallCmd; - } - - public override Procedure VisitProcedure(Procedure node) - { - if (!moverTypeChecker.procToActionInfo.ContainsKey(node)) - return node; - if (!procMap.ContainsKey(node)) - { - enclosingProc = node; - Procedure proc = (Procedure)node.Clone(); - proc.Name = string.Format("{0}_{1}", node.Name, layerNum); - proc.InParams = this.VisitVariableSeq(node.InParams); - proc.Modifies = this.VisitIdentifierExprSeq(node.Modifies); - proc.OutParams = this.VisitVariableSeq(node.OutParams); - - ActionInfo actionInfo = moverTypeChecker.procToActionInfo[node]; - if (actionInfo.createdAtLayerNum < layerNum) - { - proc.Requires = new List(); - proc.Ensures = new List(); - Implementation impl; - AtomicActionInfo atomicActionInfo = actionInfo as AtomicActionInfo; - if (atomicActionInfo != null) - { - CodeExpr action = (CodeExpr)VisitCodeExpr(atomicActionInfo.action); - List cmds = new List(); - foreach (AssertCmd assertCmd in atomicActionInfo.gate) - { - cmds.Add(new AssumeCmd(Token.NoToken, (Expr)Visit(assertCmd.Expr))); - } - Block newInitBlock = new Block(Token.NoToken, "_init", cmds, - new GotoCmd(Token.NoToken, new List(new string[] { action.Blocks[0].Label }), - new List(new Block[] { action.Blocks[0] }))); - List newBlocks = new List(); - newBlocks.Add(newInitBlock); - newBlocks.AddRange(action.Blocks); - impl = new Implementation(Token.NoToken, proc.Name, node.TypeParameters, node.InParams, node.OutParams, action.LocVars, newBlocks); - } - else - { - Block newInitBlock = new Block(Token.NoToken, "_init", new List(), new ReturnCmd(Token.NoToken)); - List newBlocks = new List(); - newBlocks.Add(newInitBlock); - impl = new Implementation(Token.NoToken, proc.Name, node.TypeParameters, node.InParams, node.OutParams, new List(), newBlocks); - } - impl.Proc = proc; - impl.Proc.AddAttribute("inline", new LiteralExpr(Token.NoToken, Microsoft.Basetypes.BigNum.FromInt(1))); - impl.AddAttribute("inline", new LiteralExpr(Token.NoToken, Microsoft.Basetypes.BigNum.FromInt(1))); - impls.Add(impl); - } - else - { - yieldingProcs.Add(proc); - proc.Requires = this.VisitRequiresSeq(node.Requires); - proc.Ensures = this.VisitEnsuresSeq(node.Ensures); - } - procMap[node] = proc; - proc.Modifies = new List(); - moverTypeChecker.SharedVariables.Iter(x => proc.Modifies.Add(Expr.Ident(x))); - } - return procMap[node]; - } - - private Variable dummyLocalVar; - public override Implementation VisitImplementation(Implementation node) - { - enclosingImpl = node; - dummyLocalVar = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, "og_dummy", Type.Bool)); - Implementation impl = base.VisitImplementation(node); - implMap[impl] = node; - impl.LocVars.Add(dummyLocalVar); - impl.Name = impl.Proc.Name; - return impl; - } - - public override Requires VisitRequires(Requires node) - { - Requires requires = base.VisitRequires(node); - if (node.Free) - return requires; - if (!moverTypeChecker.absyToLayerNums[node].Contains(layerNum)) - requires.Condition = Expr.True; - return requires; - } - - public override Ensures VisitEnsures(Ensures node) - { - Ensures ensures = base.VisitEnsures(node); - if (node.Free) - return ensures; - AtomicActionInfo atomicActionInfo = moverTypeChecker.procToActionInfo[enclosingProc] as AtomicActionInfo; - bool isAtomicSpecification = atomicActionInfo != null && atomicActionInfo.ensures == node; - if (isAtomicSpecification || !moverTypeChecker.absyToLayerNums[node].Contains(layerNum)) - { - ensures.Condition = Expr.True; - ensures.Attributes = OwickiGries.RemoveMoverAttribute(ensures.Attributes); - } - return ensures; - } - - public override Cmd VisitAssertCmd(AssertCmd node) - { - AssertCmd assertCmd = (AssertCmd) base.VisitAssertCmd(node); - if (!moverTypeChecker.absyToLayerNums[node].Contains(layerNum)) - assertCmd.Expr = Expr.True; - return assertCmd; - } - } - - public class OwickiGries - { - LinearTypeChecker linearTypeChecker; - MoverTypeChecker moverTypeChecker; - Dictionary absyMap; - Dictionary implMap; - HashSet yieldingProcs; - int layerNum; - List globalMods; - Dictionary asyncAndParallelCallDesugarings; - List yieldCheckerProcs; - List yieldCheckerImpls; - Procedure yieldProc; - - Variable pc; - Variable ok; - Expr alpha; - Expr beta; - HashSet frame; - - public OwickiGries(LinearTypeChecker linearTypeChecker, MoverTypeChecker moverTypeChecker, MyDuplicator duplicator) - { - this.linearTypeChecker = linearTypeChecker; - this.moverTypeChecker = moverTypeChecker; - this.absyMap = duplicator.absyMap; - this.layerNum = duplicator.layerNum; - this.implMap = duplicator.implMap; - this.yieldingProcs = duplicator.yieldingProcs; - Program program = linearTypeChecker.program; - globalMods = new List(); - foreach (Variable g in moverTypeChecker.SharedVariables) - { - globalMods.Add(Expr.Ident(g)); - } - asyncAndParallelCallDesugarings = new Dictionary(); - yieldCheckerProcs = new List(); - yieldCheckerImpls = new List(); - yieldProc = null; - } - - private IEnumerable AvailableLinearVars(Absy absy) - { - HashSet availableVars = new HashSet(linearTypeChecker.AvailableLinearVars(absyMap[absy])); - foreach (var g in moverTypeChecker.globalVarToSharedVarInfo.Keys) - { - SharedVariableInfo info = moverTypeChecker.globalVarToSharedVarInfo[g]; - if (!(info.introLayerNum <= layerNum && layerNum <= info.hideLayerNum)) - { - availableVars.Remove(g); - } - } - foreach (var v in moverTypeChecker.localVarToLocalVariableInfo.Keys) - { - LocalVariableInfo info = moverTypeChecker.localVarToLocalVariableInfo[v]; - if (info.isGhost) - { - if (info.layer != layerNum) - { - availableVars.Remove(v); - } - } - else - { - if (layerNum < info.layer) - { - availableVars.Remove(v); - } - } - } - return availableVars; - } - - private CallCmd CallToYieldProc(IToken tok, Dictionary ogOldGlobalMap, Dictionary domainNameToLocalVar) - { - List exprSeq = new List(); - foreach (string domainName in linearTypeChecker.linearDomains.Keys) - { - exprSeq.Add(Expr.Ident(domainNameToLocalVar[domainName])); - } - foreach (IdentifierExpr ie in globalMods) - { - exprSeq.Add(Expr.Ident(ogOldGlobalMap[ie.Decl])); - } - if (yieldProc == null) - { - List inputs = new List(); - foreach (string domainName in linearTypeChecker.linearDomains.Keys) - { - var domain = linearTypeChecker.linearDomains[domainName]; - Formal f = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "linear_" + domainName + "_in", new MapType(Token.NoToken, new List(), new List { domain.elementType }, Type.Bool)), true); - inputs.Add(f); - } - foreach (IdentifierExpr ie in globalMods) - { - Formal f = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, string.Format("og_global_old_{0}", ie.Decl.Name), ie.Decl.TypedIdent.Type), true); - inputs.Add(f); - } - yieldProc = new Procedure(Token.NoToken, string.Format("og_yield_{0}", layerNum), new List(), inputs, new List(), new List(), new List(), new List()); - yieldProc.AddAttribute("inline", new LiteralExpr(Token.NoToken, Microsoft.Basetypes.BigNum.FromInt(1))); - } - CallCmd yieldCallCmd = new CallCmd(Token.NoToken, yieldProc.Name, exprSeq, new List()); - yieldCallCmd.Proc = yieldProc; - return yieldCallCmd; - } - - private void AddCallToYieldProc(IToken tok, List newCmds, Dictionary ogOldGlobalMap, Dictionary domainNameToLocalVar) - { - if (!CommandLineOptions.Clo.TrustNonInterference) - { - CallCmd yieldCallCmd = CallToYieldProc(tok, ogOldGlobalMap, domainNameToLocalVar); - newCmds.Add(yieldCallCmd); - } - - if (pc != null) - { - Expr aa = OldEqualityExprForGlobals(ogOldGlobalMap); - Expr bb = OldEqualityExpr(ogOldGlobalMap); - - // assert pc || g_old == g || beta(i, g_old, o, g); - Expr assertExpr = Expr.Or(Expr.Ident(pc), Expr.Or(aa, beta)); - assertExpr.Typecheck(new TypecheckingContext(null)); - AssertCmd skipOrBetaAssertCmd = new AssertCmd(tok, assertExpr); - skipOrBetaAssertCmd.ErrorData = "Transition invariant in initial state violated"; - newCmds.Add(skipOrBetaAssertCmd); - - // assert pc ==> o_old == o && g_old == g; - assertExpr = Expr.Imp(Expr.Ident(pc), bb); - assertExpr.Typecheck(new TypecheckingContext(null)); - AssertCmd skipAssertCmd = new AssertCmd(tok, assertExpr); - skipAssertCmd.ErrorData = "Transition invariant in final state violated"; ; - newCmds.Add(skipAssertCmd); - - // pc, ok := g_old == g ==> pc, ok || beta(i, g_old, o, g); - List pcUpdateLHS = new List( - new AssignLhs[] { - new SimpleAssignLhs(Token.NoToken, Expr.Ident(pc)), - new SimpleAssignLhs(Token.NoToken, Expr.Ident(ok)) - }); - List pcUpdateRHS = new List( - new Expr[] { - Expr.Imp(aa, Expr.Ident(pc)), - Expr.Or(Expr.Ident(ok), beta) - }); - foreach (Expr e in pcUpdateRHS) - { - e.Typecheck(new TypecheckingContext(null)); - } - newCmds.Add(new AssignCmd(Token.NoToken, pcUpdateLHS, pcUpdateRHS)); - } - } - - private Dictionary ComputeAvailableExprs(IEnumerable availableLinearVars, Dictionary domainNameToInputVar) - { - Dictionary domainNameToExpr = new Dictionary(); - foreach (var domainName in linearTypeChecker.linearDomains.Keys) - { - var expr = Expr.Ident(domainNameToInputVar[domainName]); - expr.Resolve(new ResolutionContext(null)); - expr.Typecheck(new TypecheckingContext(null)); - domainNameToExpr[domainName] = expr; - } - foreach (Variable v in availableLinearVars) - { - var domainName = linearTypeChecker.FindDomainName(v); - if (!linearTypeChecker.linearDomains.ContainsKey(domainName)) continue; - var domain = linearTypeChecker.linearDomains[domainName]; - if (!domain.collectors.ContainsKey(v.TypedIdent.Type)) continue; - Expr ie = new NAryExpr(Token.NoToken, new FunctionCall(domain.collectors[v.TypedIdent.Type]), new List { Expr.Ident(v) }); - var expr = new NAryExpr(Token.NoToken, new FunctionCall(domain.mapOrBool), new List { ie, domainNameToExpr[domainName] }); - expr.Resolve(new ResolutionContext(null)); - expr.Typecheck(new TypecheckingContext(null)); - domainNameToExpr[domainName] = expr; - } - return domainNameToExpr; - } - - private void AddUpdatesToOldGlobalVars(List newCmds, Dictionary ogOldGlobalMap, Dictionary domainNameToLocalVar, Dictionary domainNameToExpr) - { - List lhss = new List(); - List rhss = new List(); - foreach (var domainName in linearTypeChecker.linearDomains.Keys) - { - lhss.Add(new SimpleAssignLhs(Token.NoToken, Expr.Ident(domainNameToLocalVar[domainName]))); - rhss.Add(domainNameToExpr[domainName]); - } - foreach (Variable g in ogOldGlobalMap.Keys) - { - lhss.Add(new SimpleAssignLhs(Token.NoToken, Expr.Ident(ogOldGlobalMap[g]))); - rhss.Add(Expr.Ident(g)); - } - if (lhss.Count > 0) - { - newCmds.Add(new AssignCmd(Token.NoToken, lhss, rhss)); - } - } - - private Expr OldEqualityExpr(Dictionary ogOldGlobalMap) - { - Expr bb = Expr.True; - foreach (Variable o in ogOldGlobalMap.Keys) - { - if (o is GlobalVariable && !frame.Contains(o)) continue; - bb = Expr.And(bb, Expr.Eq(Expr.Ident(o), Expr.Ident(ogOldGlobalMap[o]))); - bb.Type = Type.Bool; - } - return bb; - } - - private Expr OldEqualityExprForGlobals(Dictionary ogOldGlobalMap) - { - Expr bb = Expr.True; - foreach (Variable o in ogOldGlobalMap.Keys) - { - if (o is GlobalVariable && frame.Contains(o)) - { - bb = Expr.And(bb, Expr.Eq(Expr.Ident(o), Expr.Ident(ogOldGlobalMap[o]))); - bb.Type = Type.Bool; - } - } - return bb; - } - - private void DesugarYield(YieldCmd yieldCmd, List cmds, List newCmds, Dictionary ogOldGlobalMap, Dictionary domainNameToInputVar, Dictionary domainNameToLocalVar) - { - AddCallToYieldProc(yieldCmd.tok, newCmds, ogOldGlobalMap, domainNameToLocalVar); - - if (globalMods.Count > 0) - { - newCmds.Add(new HavocCmd(Token.NoToken, globalMods)); - if (pc != null) - { - // assume pc || alpha(i, g); - Expr assumeExpr = Expr.Or(Expr.Ident(pc), alpha); - assumeExpr.Type = Type.Bool; - newCmds.Add(new AssumeCmd(Token.NoToken, assumeExpr)); - } - } - - Dictionary domainNameToExpr = ComputeAvailableExprs(AvailableLinearVars(yieldCmd), domainNameToInputVar); - AddUpdatesToOldGlobalVars(newCmds, ogOldGlobalMap, domainNameToLocalVar, domainNameToExpr); - - for (int j = 0; j < cmds.Count; j++) - { - PredicateCmd predCmd = (PredicateCmd)cmds[j]; - newCmds.Add(new AssumeCmd(Token.NoToken, predCmd.Expr)); - } - } - - public void DesugarParallelCallCmd(List newCmds, ParCallCmd parCallCmd) - { - List parallelCalleeNames = new List(); - List ins = new List(); - List outs = new List(); - string procName = "og"; - foreach (CallCmd callCmd in parCallCmd.CallCmds) - { - procName = procName + "_" + callCmd.Proc.Name; - ins.AddRange(callCmd.Ins); - outs.AddRange(callCmd.Outs); - } - Procedure proc; - if (asyncAndParallelCallDesugarings.ContainsKey(procName)) - { - proc = asyncAndParallelCallDesugarings[procName]; - } - else - { - List inParams = new List(); - List outParams = new List(); - List requiresSeq = new List(); - List ensuresSeq = new List(); - int count = 0; - foreach (CallCmd callCmd in parCallCmd.CallCmds) - { - Dictionary map = new Dictionary(); - foreach (Variable x in callCmd.Proc.InParams) - { - Variable y = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, string.Format("og_{0}_{1}", count, x.Name), x.TypedIdent.Type), true); - inParams.Add(y); - map[x] = Expr.Ident(y); - } - foreach (Variable x in callCmd.Proc.OutParams) - { - Variable y = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, string.Format("og_{0}_{1}", count, x.Name), x.TypedIdent.Type), false); - outParams.Add(y); - map[x] = Expr.Ident(y); - } - Contract.Assume(callCmd.Proc.TypeParameters.Count == 0); - Substitution subst = Substituter.SubstitutionFromHashtable(map); - foreach (Requires req in callCmd.Proc.Requires) - { - requiresSeq.Add(new Requires(req.tok, req.Free, Substituter.Apply(subst, req.Condition), null, req.Attributes)); - } - foreach (Ensures ens in callCmd.Proc.Ensures) - { - ensuresSeq.Add(new Ensures(ens.tok, ens.Free, Substituter.Apply(subst, ens.Condition), null, ens.Attributes)); - } - count++; - } - proc = new Procedure(Token.NoToken, procName, new List(), inParams, outParams, requiresSeq, globalMods, ensuresSeq); - asyncAndParallelCallDesugarings[procName] = proc; - } - CallCmd dummyCallCmd = new CallCmd(parCallCmd.tok, proc.Name, ins, outs, parCallCmd.Attributes); - dummyCallCmd.Proc = proc; - newCmds.Add(dummyCallCmd); - } - - private void CreateYieldCheckerImpl(Implementation impl, List> yields) - { - if (yields.Count == 0) return; - - Dictionary map = new Dictionary(); - foreach (Variable local in impl.LocVars) - { - var copy = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, local.Name, local.TypedIdent.Type)); - map[local] = Expr.Ident(copy); - } - - Program program = linearTypeChecker.program; - List locals = new List(); - List inputs = new List(); - foreach (IdentifierExpr ie in map.Values) - { - locals.Add(ie.Decl); - } - for (int i = 0; i < impl.InParams.Count - linearTypeChecker.linearDomains.Count; i++) - { - Variable inParam = impl.InParams[i]; - Variable copy = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, inParam.Name, inParam.TypedIdent.Type)); - locals.Add(copy); - map[impl.InParams[i]] = Expr.Ident(copy); - } - { - int i = impl.InParams.Count - linearTypeChecker.linearDomains.Count; - foreach (string domainName in linearTypeChecker.linearDomains.Keys) - { - Variable inParam = impl.InParams[i]; - Variable copy = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, inParam.Name, inParam.TypedIdent.Type), true); - inputs.Add(copy); - map[impl.InParams[i]] = Expr.Ident(copy); - i++; - } - } - for (int i = 0; i < impl.OutParams.Count; i++) - { - Variable outParam = impl.OutParams[i]; - var copy = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, outParam.Name, outParam.TypedIdent.Type)); - locals.Add(copy); - map[impl.OutParams[i]] = Expr.Ident(copy); - } - Dictionary ogOldLocalMap = new Dictionary(); - Dictionary assumeMap = new Dictionary(map); - foreach (IdentifierExpr ie in globalMods) - { - Variable g = ie.Decl; - var copy = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, string.Format("og_local_old_{0}", g.Name), g.TypedIdent.Type)); - locals.Add(copy); - ogOldLocalMap[g] = Expr.Ident(copy); - Formal f = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, string.Format("og_global_old_{0}", g.Name), g.TypedIdent.Type), true); - inputs.Add(f); - assumeMap[g] = Expr.Ident(f); - } - - Substitution assumeSubst = Substituter.SubstitutionFromHashtable(assumeMap); - Substitution oldSubst = Substituter.SubstitutionFromHashtable(ogOldLocalMap); - Substitution subst = Substituter.SubstitutionFromHashtable(map); - List yieldCheckerBlocks = new List(); - List labels = new List(); - List labelTargets = new List(); - Block yieldCheckerBlock = new Block(Token.NoToken, "exit", new List(), new ReturnCmd(Token.NoToken)); - labels.Add(yieldCheckerBlock.Label); - labelTargets.Add(yieldCheckerBlock); - yieldCheckerBlocks.Add(yieldCheckerBlock); - int yieldCount = 0; - foreach (List cs in yields) - { - List newCmds = new List(); - foreach (Cmd cmd in cs) - { - PredicateCmd predCmd = (PredicateCmd)cmd; - newCmds.Add(new AssumeCmd(Token.NoToken, Substituter.ApplyReplacingOldExprs(assumeSubst, oldSubst, predCmd.Expr))); - } - foreach (Cmd cmd in cs) - { - PredicateCmd predCmd = (PredicateCmd)cmd; - var newExpr = Substituter.ApplyReplacingOldExprs(subst, oldSubst, predCmd.Expr); - if (predCmd is AssertCmd) - { - AssertCmd assertCmd = new AssertCmd(predCmd.tok, newExpr, predCmd.Attributes); - assertCmd.ErrorData = "Non-interference check failed"; - newCmds.Add(assertCmd); - } - else - { - newCmds.Add(new AssumeCmd(Token.NoToken, newExpr)); - } - } - newCmds.Add(new AssumeCmd(Token.NoToken, Expr.False)); - yieldCheckerBlock = new Block(Token.NoToken, "L" + yieldCount++, newCmds, new ReturnCmd(Token.NoToken)); - labels.Add(yieldCheckerBlock.Label); - labelTargets.Add(yieldCheckerBlock); - yieldCheckerBlocks.Add(yieldCheckerBlock); - } - yieldCheckerBlocks.Insert(0, new Block(Token.NoToken, "enter", new List(), new GotoCmd(Token.NoToken, labels, labelTargets))); - - // Create the yield checker procedure - var yieldCheckerName = string.Format("{0}_YieldChecker_{1}", "Impl", impl.Name); - var yieldCheckerProc = new Procedure(Token.NoToken, yieldCheckerName, impl.TypeParameters, inputs, new List(), new List(), new List(), new List()); - yieldCheckerProc.AddAttribute("inline", new LiteralExpr(Token.NoToken, Microsoft.Basetypes.BigNum.FromInt(1))); - yieldCheckerProcs.Add(yieldCheckerProc); - - // Create the yield checker implementation - var yieldCheckerImpl = new Implementation(Token.NoToken, yieldCheckerName, impl.TypeParameters, inputs, new List(), locals, yieldCheckerBlocks); - yieldCheckerImpl.Proc = yieldCheckerProc; - yieldCheckerImpl.AddAttribute("inline", new LiteralExpr(Token.NoToken, Microsoft.Basetypes.BigNum.FromInt(1))); - yieldCheckerImpls.Add(yieldCheckerImpl); - } - - private bool IsYieldingHeader(Graph graph, Block header) - { - foreach (Block backEdgeNode in graph.BackEdgeNodes(header)) - { - foreach (Block x in graph.NaturalLoops(header, backEdgeNode)) - { - foreach (Cmd cmd in x.Cmds) - { - if (cmd is YieldCmd) - return true; - if (cmd is ParCallCmd) - return true; - CallCmd callCmd = cmd as CallCmd; - if (callCmd == null) continue; - if (yieldingProcs.Contains(callCmd.Proc)) - return true; - } - } - } - return false; - } - - private Graph ComputeYieldingLoopHeaders(Implementation impl, out HashSet yieldingHeaders) - { - Graph graph; - impl.PruneUnreachableBlocks(); - impl.ComputePredecessorsForBlocks(); - graph = Program.GraphFromImpl(impl); - graph.ComputeLoops(); - if (!graph.Reducible) - { - throw new Exception("Irreducible flow graphs are unsupported."); - } - yieldingHeaders = new HashSet(); - IEnumerable sortedHeaders = graph.SortHeadersByDominance(); - foreach (Block header in sortedHeaders) - { - if (yieldingHeaders.Any(x => graph.DominatorMap.DominatedBy(x, header))) - { - yieldingHeaders.Add(header); - } - else if (IsYieldingHeader(graph, header)) - { - yieldingHeaders.Add(header); - } - else - { - continue; - } - } - return graph; - } - - private void SetupRefinementCheck(Implementation impl, - out List newLocalVars, - out Dictionary domainNameToInputVar, out Dictionary domainNameToLocalVar, out Dictionary ogOldGlobalMap) - { - pc = null; - ok = null; - alpha = null; - beta = null; - frame = null; - - newLocalVars = new List(); - Program program = linearTypeChecker.program; - ogOldGlobalMap = new Dictionary(); - foreach (IdentifierExpr ie in globalMods) - { - Variable g = ie.Decl; - LocalVariable l = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, string.Format("og_global_old_{0}", g.Name), g.TypedIdent.Type)); - ogOldGlobalMap[g] = l; - newLocalVars.Add(l); - } - - Procedure originalProc = implMap[impl].Proc; - ActionInfo actionInfo = moverTypeChecker.procToActionInfo[originalProc]; - if (actionInfo.createdAtLayerNum == this.layerNum) - { - pc = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, "og_pc", Type.Bool)); - newLocalVars.Add(pc); - ok = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, "og_ok", Type.Bool)); - newLocalVars.Add(ok); - Dictionary alwaysMap = new Dictionary(); - for (int i = 0; i < originalProc.InParams.Count; i++) - { - alwaysMap[originalProc.InParams[i]] = Expr.Ident(impl.InParams[i]); - } - for (int i = 0; i < originalProc.OutParams.Count; i++) - { - alwaysMap[originalProc.OutParams[i]] = Expr.Ident(impl.OutParams[i]); - } - Substitution always = Substituter.SubstitutionFromHashtable(alwaysMap); - Dictionary foroldMap = new Dictionary(); - foreach (IdentifierExpr ie in globalMods) - { - foroldMap[ie.Decl] = Expr.Ident(ogOldGlobalMap[ie.Decl]); - } - Substitution forold = Substituter.SubstitutionFromHashtable(foroldMap); - frame = new HashSet(moverTypeChecker.SharedVariables); - foreach (Variable v in moverTypeChecker.SharedVariables) - { - if (moverTypeChecker.globalVarToSharedVarInfo[v].hideLayerNum <= actionInfo.createdAtLayerNum || - moverTypeChecker.globalVarToSharedVarInfo[v].introLayerNum > actionInfo.createdAtLayerNum) - { - frame.Remove(v); - } - } - AtomicActionInfo atomicActionInfo = actionInfo as AtomicActionInfo; - if (atomicActionInfo == null) - { - beta = Expr.True; - foreach (var v in frame) - { - beta = Expr.And(beta, Expr.Eq(Expr.Ident(v), foroldMap[v])); - } - alpha = Expr.True; - } - else - { - Expr betaExpr = (new MoverCheck.TransitionRelationComputation(moverTypeChecker.program, atomicActionInfo, frame, new HashSet())).TransitionRelationCompute(true); - beta = Substituter.ApplyReplacingOldExprs(always, forold, betaExpr); - Expr alphaExpr = Expr.True; - foreach (AssertCmd assertCmd in atomicActionInfo.gate) - { - alphaExpr = Expr.And(alphaExpr, assertCmd.Expr); - alphaExpr.Type = Type.Bool; - } - alpha = Substituter.Apply(always, alphaExpr); - } - foreach (Variable f in impl.OutParams) - { - LocalVariable copy = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, string.Format("og_old_{0}", f.Name), f.TypedIdent.Type)); - newLocalVars.Add(copy); - ogOldGlobalMap[f] = copy; - } - } - - domainNameToInputVar = new Dictionary(); - domainNameToLocalVar = new Dictionary(); - { - int i = impl.InParams.Count - linearTypeChecker.linearDomains.Count; - foreach (string domainName in linearTypeChecker.linearDomains.Keys) - { - Variable inParam = impl.InParams[i]; - domainNameToInputVar[domainName] = inParam; - Variable l = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, inParam.Name + "_local", inParam.TypedIdent.Type)); - domainNameToLocalVar[domainName] = l; - newLocalVars.Add(l); - i++; - } - } - } - - private void TransformImpl(Implementation impl) - { - HashSet yieldingHeaders; - Graph graph = ComputeYieldingLoopHeaders(impl, out yieldingHeaders); - - List newLocalVars; - Dictionary domainNameToInputVar, domainNameToLocalVar; - Dictionary ogOldGlobalMap; - SetupRefinementCheck(impl, out newLocalVars, out domainNameToInputVar, out domainNameToLocalVar, out ogOldGlobalMap); - - List> yields = CollectAndDesugarYields(impl, domainNameToInputVar, domainNameToLocalVar, ogOldGlobalMap); - - List oldPcs, oldOks; - ProcessLoopHeaders(impl, graph, yieldingHeaders, domainNameToInputVar, domainNameToLocalVar, ogOldGlobalMap, out oldPcs, out oldOks); - - AddInitialBlock(impl, oldPcs, oldOks, domainNameToInputVar, domainNameToLocalVar, ogOldGlobalMap); - - CreateYieldCheckerImpl(impl, yields); - - impl.LocVars.AddRange(newLocalVars); - impl.LocVars.AddRange(oldPcs); - impl.LocVars.AddRange(oldOks); - - UnifyCallsToYieldProc(impl, ogOldGlobalMap, domainNameToLocalVar); - } - - private void UnifyCallsToYieldProc(Implementation impl, Dictionary ogOldGlobalMap, Dictionary domainNameToLocalVar) - { - CallCmd yieldCallCmd = CallToYieldProc(Token.NoToken, ogOldGlobalMap, domainNameToLocalVar); - Block yieldCheckBlock = new Block(Token.NoToken, "CallToYieldProc", new List(new Cmd[] { yieldCallCmd, new AssumeCmd(Token.NoToken, Expr.False) }), new ReturnCmd(Token.NoToken)); - List newBlocks = new List(); - foreach (Block b in impl.Blocks) - { - TransferCmd transferCmd = b.TransferCmd; - List newCmds = new List(); - for (int i = b.Cmds.Count-1; i >= 0; i--) - { - CallCmd callCmd = b.Cmds[i] as CallCmd; - if (callCmd == null || callCmd.Proc != yieldProc) - { - newCmds.Insert(0, b.Cmds[i]); - } - else - { - Block newBlock = new Block(Token.NoToken, b.Label + i, newCmds, transferCmd); - newCmds = new List(); - transferCmd = new GotoCmd(Token.NoToken, new List(new string[] { newBlock.Label, yieldCheckBlock.Label }), - new List(new Block[] { newBlock, yieldCheckBlock })); - newBlocks.Add(newBlock); - } - } - b.Cmds = newCmds; - b.TransferCmd = transferCmd; - } - impl.Blocks.AddRange(newBlocks); - impl.Blocks.Add(yieldCheckBlock); - } - - private List> CollectAndDesugarYields(Implementation impl, - Dictionary domainNameToInputVar, Dictionary domainNameToLocalVar, Dictionary ogOldGlobalMap) - { - // Collect the yield predicates and desugar yields - List> yields = new List>(); - List cmds = new List(); - foreach (Block b in impl.Blocks) - { - YieldCmd yieldCmd = null; - List newCmds = new List(); - for (int i = 0; i < b.Cmds.Count; i++) - { - Cmd cmd = b.Cmds[i]; - if (cmd is YieldCmd) - { - yieldCmd = (YieldCmd)cmd; - continue; - } - if (yieldCmd != null) - { - PredicateCmd pcmd = cmd as PredicateCmd; - if (pcmd == null) - { - DesugarYield(yieldCmd, cmds, newCmds, ogOldGlobalMap, domainNameToInputVar, domainNameToLocalVar); - if (cmds.Count > 0) - { - yields.Add(cmds); - cmds = new List(); - } - yieldCmd = null; - } - else - { - cmds.Add(pcmd); - } - } - - if (cmd is CallCmd) - { - CallCmd callCmd = cmd as CallCmd; - if (yieldingProcs.Contains(callCmd.Proc)) - { - AddCallToYieldProc(callCmd.tok, newCmds, ogOldGlobalMap, domainNameToLocalVar); - } - if (callCmd.IsAsync) - { - if (!asyncAndParallelCallDesugarings.ContainsKey(callCmd.Proc.Name)) - { - asyncAndParallelCallDesugarings[callCmd.Proc.Name] = new Procedure(Token.NoToken, string.Format("DummyAsyncTarget_{0}", callCmd.Proc.Name), callCmd.Proc.TypeParameters, callCmd.Proc.InParams, callCmd.Proc.OutParams, callCmd.Proc.Requires, new List(), new List()); - } - var dummyAsyncTargetProc = asyncAndParallelCallDesugarings[callCmd.Proc.Name]; - CallCmd dummyCallCmd = new CallCmd(callCmd.tok, dummyAsyncTargetProc.Name, callCmd.Ins, callCmd.Outs, callCmd.Attributes); - dummyCallCmd.Proc = dummyAsyncTargetProc; - newCmds.Add(dummyCallCmd); - } - else - { - newCmds.Add(callCmd); - } - if (yieldingProcs.Contains(callCmd.Proc)) - { - HashSet availableLinearVars = new HashSet(AvailableLinearVars(callCmd)); - linearTypeChecker.AddAvailableVars(callCmd, availableLinearVars); - - if (!callCmd.IsAsync && globalMods.Count > 0 && pc != null) - { - // assume pc || alpha(i, g); - Expr assumeExpr = Expr.Or(Expr.Ident(pc), alpha); - assumeExpr.Type = Type.Bool; - newCmds.Add(new AssumeCmd(Token.NoToken, assumeExpr)); - } - - Dictionary domainNameToExpr = ComputeAvailableExprs(availableLinearVars, domainNameToInputVar); - AddUpdatesToOldGlobalVars(newCmds, ogOldGlobalMap, domainNameToLocalVar, domainNameToExpr); - } - } - else if (cmd is ParCallCmd) - { - ParCallCmd parCallCmd = cmd as ParCallCmd; - AddCallToYieldProc(parCallCmd.tok, newCmds, ogOldGlobalMap, domainNameToLocalVar); - DesugarParallelCallCmd(newCmds, parCallCmd); - HashSet availableLinearVars = new HashSet(AvailableLinearVars(parCallCmd)); - linearTypeChecker.AddAvailableVars(parCallCmd, availableLinearVars); - - if (globalMods.Count > 0 && pc != null) - { - // assume pc || alpha(i, g); - Expr assumeExpr = Expr.Or(Expr.Ident(pc), alpha); - assumeExpr.Type = Type.Bool; - newCmds.Add(new AssumeCmd(Token.NoToken, assumeExpr)); - } - - Dictionary domainNameToExpr = ComputeAvailableExprs(availableLinearVars, domainNameToInputVar); - AddUpdatesToOldGlobalVars(newCmds, ogOldGlobalMap, domainNameToLocalVar, domainNameToExpr); - } - else - { - newCmds.Add(cmd); - } - } - if (yieldCmd != null) - { - DesugarYield(yieldCmd, cmds, newCmds, ogOldGlobalMap, domainNameToInputVar, domainNameToLocalVar); - if (cmds.Count > 0) - { - yields.Add(cmds); - cmds = new List(); - } - } - if (b.TransferCmd is ReturnCmd) - { - AddCallToYieldProc(b.TransferCmd.tok, newCmds, ogOldGlobalMap, domainNameToLocalVar); - if (pc != null) - { - AssertCmd assertCmd = new AssertCmd(b.TransferCmd.tok, Expr.Ident(ok)); - assertCmd.ErrorData = "Failed to execute atomic action before procedure return"; - newCmds.Add(assertCmd); - } - } - b.Cmds = newCmds; - } - return yields; - } - - private void ProcessLoopHeaders(Implementation impl, Graph graph, HashSet yieldingHeaders, - Dictionary domainNameToInputVar, Dictionary domainNameToLocalVar, Dictionary ogOldGlobalMap, - out List oldPcs, out List oldOks) - { - oldPcs = new List(); - oldOks = new List(); - foreach (Block header in yieldingHeaders) - { - LocalVariable oldPc = null; - LocalVariable oldOk = null; - if (pc != null) - { - oldPc = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, string.Format("{0}_{1}", pc.Name, header.Label), Type.Bool)); - oldPcs.Add(oldPc); - oldOk = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, string.Format("{0}_{1}", ok.Name, header.Label), Type.Bool)); - oldOks.Add(oldOk); - } - Dictionary domainNameToExpr = ComputeAvailableExprs(AvailableLinearVars(header), domainNameToInputVar); - foreach (Block pred in header.Predecessors) - { - AddCallToYieldProc(header.tok, pred.Cmds, ogOldGlobalMap, domainNameToLocalVar); - if (pc != null && !graph.BackEdgeNodes(header).Contains(pred)) - { - pred.Cmds.Add(new AssignCmd(Token.NoToken, new List( - new AssignLhs[] { new SimpleAssignLhs(Token.NoToken, Expr.Ident(oldPc)), new SimpleAssignLhs(Token.NoToken, Expr.Ident(oldOk)) }), - new List(new Expr[] { Expr.Ident(pc), Expr.Ident(ok) }))); - } - AddUpdatesToOldGlobalVars(pred.Cmds, ogOldGlobalMap, domainNameToLocalVar, domainNameToExpr); - } - List newCmds = new List(); - if (pc != null) - { - AssertCmd assertCmd; - assertCmd = new AssertCmd(header.tok, Expr.Eq(Expr.Ident(oldPc), Expr.Ident(pc))); - assertCmd.ErrorData = "Specification state must not change for transitions ending in loop headers"; - newCmds.Add(assertCmd); - assertCmd = new AssertCmd(header.tok, Expr.Imp(Expr.Ident(oldOk), Expr.Ident(ok))); - assertCmd.ErrorData = "Specification state must not change for transitions ending in loop headers"; - newCmds.Add(assertCmd); - } - foreach (string domainName in linearTypeChecker.linearDomains.Keys) - { - newCmds.Add(new AssumeCmd(Token.NoToken, Expr.Eq(Expr.Ident(domainNameToLocalVar[domainName]), domainNameToExpr[domainName]))); - } - foreach (Variable v in ogOldGlobalMap.Keys) - { - newCmds.Add(new AssumeCmd(Token.NoToken, Expr.Eq(Expr.Ident(v), Expr.Ident(ogOldGlobalMap[v])))); - } - newCmds.AddRange(header.Cmds); - header.Cmds = newCmds; - } - } - - private void AddInitialBlock(Implementation impl, List oldPcs, List oldOks, - Dictionary domainNameToInputVar, Dictionary domainNameToLocalVar, Dictionary ogOldGlobalMap) - { - // Add initial block - List lhss = new List(); - List rhss = new List(); - if (pc != null) - { - lhss.Add(new SimpleAssignLhs(Token.NoToken, Expr.Ident(pc))); - rhss.Add(Expr.False); - foreach (Variable oldPc in oldPcs) - { - lhss.Add(new SimpleAssignLhs(Token.NoToken, Expr.Ident(oldPc))); - rhss.Add(Expr.False); - } - lhss.Add(new SimpleAssignLhs(Token.NoToken, Expr.Ident(ok))); - rhss.Add(Expr.False); - foreach (Variable oldOk in oldOks) - { - lhss.Add(new SimpleAssignLhs(Token.NoToken, Expr.Ident(oldOk))); - rhss.Add(Expr.False); - } - } - Dictionary domainNameToExpr = new Dictionary(); - foreach (var domainName in linearTypeChecker.linearDomains.Keys) - { - domainNameToExpr[domainName] = Expr.Ident(domainNameToInputVar[domainName]); - } - for (int i = 0; i < impl.InParams.Count - linearTypeChecker.linearDomains.Count; i++) - { - Variable v = impl.InParams[i]; - var domainName = linearTypeChecker.FindDomainName(v); - if (domainName == null) continue; - if (!linearTypeChecker.linearDomains.ContainsKey(domainName)) continue; - var domain = linearTypeChecker.linearDomains[domainName]; - if (!domain.collectors.ContainsKey(v.TypedIdent.Type)) continue; - Expr ie = new NAryExpr(Token.NoToken, new FunctionCall(domain.collectors[v.TypedIdent.Type]), new List { Expr.Ident(v) }); - domainNameToExpr[domainName] = new NAryExpr(Token.NoToken, new FunctionCall(domain.mapOrBool), new List { ie, domainNameToExpr[domainName] }); - } - foreach (string domainName in linearTypeChecker.linearDomains.Keys) - { - lhss.Add(new SimpleAssignLhs(Token.NoToken, Expr.Ident(domainNameToLocalVar[domainName]))); - rhss.Add(domainNameToExpr[domainName]); - } - foreach (Variable g in ogOldGlobalMap.Keys) - { - lhss.Add(new SimpleAssignLhs(Token.NoToken, Expr.Ident(ogOldGlobalMap[g]))); - rhss.Add(Expr.Ident(g)); - } - if (lhss.Count > 0) - { - Block initBlock = new Block(Token.NoToken, "og_init", new List { new AssignCmd(Token.NoToken, lhss, rhss) }, new GotoCmd(Token.NoToken, new List { impl.Blocks[0].Label }, new List { impl.Blocks[0] })); - impl.Blocks.Insert(0, initBlock); - } - } - - private void AddYieldProcAndImpl(List decls) - { - if (yieldProc == null) return; - - Program program = linearTypeChecker.program; - List inputs = new List(); - foreach (string domainName in linearTypeChecker.linearDomains.Keys) - { - var domain = linearTypeChecker.linearDomains[domainName]; - Formal f = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "linear_" + domainName + "_in", new MapType(Token.NoToken, new List(), new List { domain.elementType }, Type.Bool)), true); - inputs.Add(f); - } - foreach (IdentifierExpr ie in globalMods) - { - Formal f = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, string.Format("og_global_old_{0}", ie.Decl.Name), ie.Decl.TypedIdent.Type), true); - inputs.Add(f); - } - List blocks = new List(); - TransferCmd transferCmd = new ReturnCmd(Token.NoToken); - if (yieldCheckerProcs.Count > 0) - { - List blockTargets = new List(); - List labelTargets = new List(); - int labelCount = 0; - foreach (Procedure proc in yieldCheckerProcs) - { - List exprSeq = new List(); - foreach (Variable v in inputs) - { - exprSeq.Add(Expr.Ident(v)); - } - CallCmd callCmd = new CallCmd(Token.NoToken, proc.Name, exprSeq, new List()); - callCmd.Proc = proc; - string label = string.Format("L_{0}", labelCount++); - Block block = new Block(Token.NoToken, label, new List { callCmd }, new ReturnCmd(Token.NoToken)); - labelTargets.Add(label); - blockTargets.Add(block); - blocks.Add(block); - } - transferCmd = new GotoCmd(Token.NoToken, labelTargets, blockTargets); - } - blocks.Insert(0, new Block(Token.NoToken, "enter", new List(), transferCmd)); - - var yieldImpl = new Implementation(Token.NoToken, yieldProc.Name, new List(), inputs, new List(), new List(), blocks); - yieldImpl.Proc = yieldProc; - yieldImpl.AddAttribute("inline", new LiteralExpr(Token.NoToken, Microsoft.Basetypes.BigNum.FromInt(1))); - decls.Add(yieldProc); - decls.Add(yieldImpl); - } - - public static QKeyValue RemoveYieldsAttribute(QKeyValue iter) - { - if (iter == null) return null; - iter.Next = RemoveYieldsAttribute(iter.Next); - return (iter.Key == "yields") ? iter.Next : iter; - } - - public static QKeyValue RemoveMoverAttribute(QKeyValue iter) - { - if (iter == null) return null; - iter.Next = RemoveMoverAttribute(iter.Next); - if (iter.Key == "atomic" || iter.Key == "right" || iter.Key == "left" || iter.Key == "both") - return iter.Next; - else - return iter; - } - - private List Collect() - { - List decls = new List(); - foreach (Procedure proc in yieldCheckerProcs) - { - decls.Add(proc); - } - foreach (Implementation impl in yieldCheckerImpls) - { - decls.Add(impl); - } - foreach (Procedure proc in asyncAndParallelCallDesugarings.Values) - { - decls.Add(proc); - } - AddYieldProcAndImpl(decls); - return decls; - } - - public static void AddCheckers(LinearTypeChecker linearTypeChecker, MoverTypeChecker moverTypeChecker, List decls) - { - Program program = linearTypeChecker.program; - foreach (int layerNum in moverTypeChecker.AllImplementedLayerNums) - { - if (CommandLineOptions.Clo.TrustLayersDownto <= layerNum || layerNum <= CommandLineOptions.Clo.TrustLayersUpto) continue; - - MyDuplicator duplicator = new MyDuplicator(moverTypeChecker, layerNum); - foreach (var proc in program.Procedures) - { - if (!moverTypeChecker.procToActionInfo.ContainsKey(proc)) continue; - Procedure duplicateProc = duplicator.VisitProcedure(proc); - decls.Add(duplicateProc); - } - decls.AddRange(duplicator.impls); - OwickiGries ogTransform = new OwickiGries(linearTypeChecker, moverTypeChecker, duplicator); - foreach (var impl in program.Implementations) - { - if (!moverTypeChecker.procToActionInfo.ContainsKey(impl.Proc) || moverTypeChecker.procToActionInfo[impl.Proc].createdAtLayerNum < layerNum) - continue; - Implementation duplicateImpl = duplicator.VisitImplementation(impl); - ogTransform.TransformImpl(duplicateImpl); - decls.Add(duplicateImpl); - } - decls.AddRange(ogTransform.Collect()); - } - } - } -} diff --git a/Source/Concurrency/Program.cs b/Source/Concurrency/Program.cs index b56e1cf3..1be7cc07 100644 --- a/Source/Concurrency/Program.cs +++ b/Source/Concurrency/Program.cs @@ -7,20 +7,20 @@ namespace Microsoft.Boogie { public class Concurrency { - public static void Transform(LinearTypeChecker linearTypeChecker, MoverTypeChecker moverTypeChecker) + public static void Transform(LinearTypeChecker linearTypeChecker, CivlTypeChecker civlTypeChecker) { List originalDecls = new List(); Program program = linearTypeChecker.program; foreach (var decl in program.TopLevelDeclarations) { Procedure proc = decl as Procedure; - if (proc != null && moverTypeChecker.procToActionInfo.ContainsKey(proc)) + if (proc != null && civlTypeChecker.procToActionInfo.ContainsKey(proc)) { originalDecls.Add(proc); continue; } Implementation impl = decl as Implementation; - if (impl != null && moverTypeChecker.procToActionInfo.ContainsKey(impl.Proc)) + if (impl != null && civlTypeChecker.procToActionInfo.ContainsKey(impl.Proc)) { originalDecls.Add(impl); } @@ -29,12 +29,12 @@ namespace Microsoft.Boogie List decls = new List(); if (!CommandLineOptions.Clo.TrustAtomicityTypes) { - MoverCheck.AddCheckers(linearTypeChecker, moverTypeChecker, decls); + MoverCheck.AddCheckers(linearTypeChecker, civlTypeChecker, decls); } - OwickiGries.AddCheckers(linearTypeChecker, moverTypeChecker, decls); + CivlRefinement.AddCheckers(linearTypeChecker, civlTypeChecker, decls); foreach (Declaration decl in decls) { - decl.Attributes = OwickiGries.RemoveYieldsAttribute(decl.Attributes); + decl.Attributes = CivlRefinement.RemoveYieldsAttribute(decl.Attributes); } program.RemoveTopLevelDeclarations(x => originalDecls.Contains(x)); program.AddTopLevelDeclarations(decls); diff --git a/Source/Concurrency/TypeCheck.cs b/Source/Concurrency/TypeCheck.cs deleted file mode 100644 index 0e257f30..00000000 --- a/Source/Concurrency/TypeCheck.cs +++ /dev/null @@ -1,1168 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Microsoft.Boogie; -using System.Diagnostics.Contracts; -using System.Diagnostics; - -namespace Microsoft.Boogie -{ - public enum MoverType - { - Top, - Atomic, - Right, - Left, - Both - } - - public class ActionInfo - { - public Procedure proc; - public int createdAtLayerNum; - public int availableUptoLayerNum; - public bool hasImplementation; - public bool isExtern; - - public ActionInfo(Procedure proc, int createdAtLayerNum, int availableUptoLayerNum) - { - this.proc = proc; - this.createdAtLayerNum = createdAtLayerNum; - this.availableUptoLayerNum = availableUptoLayerNum; - this.hasImplementation = false; - this.isExtern = QKeyValue.FindBoolAttribute(proc.Attributes, "extern"); - } - - public virtual bool IsRightMover - { - get { return true; } - } - - public virtual bool IsLeftMover - { - get { return true; } - } - } - - public class AtomicActionInfo : ActionInfo - { - public Ensures ensures; - public MoverType moverType; - public List gate; - public CodeExpr action; - public List thisGate; - public CodeExpr thisAction; - public List thisInParams; - public List thisOutParams; - public List thatGate; - public CodeExpr thatAction; - public List thatInParams; - public List thatOutParams; - public HashSet actionUsedGlobalVars; - public HashSet modifiedGlobalVars; - public HashSet gateUsedGlobalVars; - public bool hasAssumeCmd; - public Dictionary thisMap; - public Dictionary thatMap; - - public bool CommutesWith(AtomicActionInfo actionInfo) - { - if (this.modifiedGlobalVars.Intersect(actionInfo.actionUsedGlobalVars).Count() > 0) - return false; - if (this.actionUsedGlobalVars.Intersect(actionInfo.modifiedGlobalVars).Count() > 0) - return false; - return true; - } - - public override bool IsRightMover - { - get { return moverType == MoverType.Right || moverType == MoverType.Both; } - } - - public override bool IsLeftMover - { - get { return moverType == MoverType.Left || moverType == MoverType.Both; } - } - - public AtomicActionInfo(Procedure proc, Ensures ensures, MoverType moverType, int layerNum, int availableUptoLayerNum) - : base(proc, layerNum, availableUptoLayerNum) - { - this.ensures = ensures; - this.moverType = moverType; - this.gate = new List(); - this.action = ensures.Condition as CodeExpr; - this.thisGate = new List(); - this.thisInParams = new List(); - this.thisOutParams = new List(); - this.thatGate = new List(); - this.thatInParams = new List(); - this.thatOutParams = new List(); - this.hasAssumeCmd = false; - this.thisMap = new Dictionary(); - this.thatMap = new Dictionary(); - - foreach (Block block in this.action.Blocks) - { - block.Cmds.ForEach(x => this.hasAssumeCmd = this.hasAssumeCmd || x is AssumeCmd); - } - - foreach (Block block in this.action.Blocks) - { - if (block.TransferCmd is ReturnExprCmd) - { - block.TransferCmd = new ReturnCmd(block.TransferCmd.tok); - } - } - - var cmds = this.action.Blocks[0].Cmds; - for (int i = 0; i < cmds.Count; i++) - { - AssertCmd assertCmd = cmds[i] as AssertCmd; - if (assertCmd == null) break; - this.gate.Add(assertCmd); - cmds[i] = new AssumeCmd(assertCmd.tok, Expr.True); - } - - foreach (Variable x in proc.InParams) - { - Variable thisx = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "this_" + x.Name, x.TypedIdent.Type), true, x.Attributes); - this.thisInParams.Add(thisx); - this.thisMap[x] = Expr.Ident(thisx); - Variable thatx = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "that_" + x.Name, x.TypedIdent.Type), true, x.Attributes); - this.thatInParams.Add(thatx); - this.thatMap[x] = Expr.Ident(thatx); - } - foreach (Variable x in proc.OutParams) - { - Variable thisx = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "this_" + x.Name, x.TypedIdent.Type), false, x.Attributes); - this.thisOutParams.Add(thisx); - this.thisMap[x] = Expr.Ident(thisx); - Variable thatx = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "that_" + x.Name, x.TypedIdent.Type), false, x.Attributes); - this.thatOutParams.Add(thatx); - this.thatMap[x] = Expr.Ident(thatx); - } - List thisLocVars = new List(); - List thatLocVars = new List(); - foreach (Variable x in this.action.LocVars) - { - Variable thisx = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "this_" + x.Name, x.TypedIdent.Type), false); - thisMap[x] = Expr.Ident(thisx); - thisLocVars.Add(thisx); - Variable thatx = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "that_" + x.Name, x.TypedIdent.Type), false); - thatMap[x] = Expr.Ident(thatx); - thatLocVars.Add(thatx); - } - Contract.Assume(proc.TypeParameters.Count == 0); - Substitution thisSubst = Substituter.SubstitutionFromHashtable(this.thisMap); - Substitution thatSubst = Substituter.SubstitutionFromHashtable(this.thatMap); - foreach (AssertCmd assertCmd in this.gate) - { - this.thisGate.Add((AssertCmd)Substituter.Apply(thisSubst, assertCmd)); - this.thatGate.Add((AssertCmd)Substituter.Apply(thatSubst, assertCmd)); - } - this.thisAction = new CodeExpr(thisLocVars, SubstituteBlocks(this.action.Blocks, thisSubst, "this_")); - this.thatAction = new CodeExpr(thatLocVars, SubstituteBlocks(this.action.Blocks, thatSubst, "that_")); - - { - VariableCollector collector = new VariableCollector(); - collector.Visit(this.action); - this.actionUsedGlobalVars = new HashSet(collector.usedVars.Where(x => x is GlobalVariable)); - } - - List modifiedVars = new List(); - foreach (Block block in this.action.Blocks) - { - block.Cmds.ForEach(cmd => cmd.AddAssignedVariables(modifiedVars)); - } - this.modifiedGlobalVars = new HashSet(modifiedVars.Where(x => x is GlobalVariable)); - - { - VariableCollector collector = new VariableCollector(); - this.gate.ForEach(assertCmd => collector.Visit(assertCmd)); - this.gateUsedGlobalVars = new HashSet(collector.usedVars.Where(x => x is GlobalVariable)); - } - } - - private List SubstituteBlocks(List blocks, Substitution subst, string blockLabelPrefix) - { - Dictionary blockMap = new Dictionary(); - List otherBlocks = new List(); - foreach (Block block in blocks) - { - List otherCmds = new List(); - foreach (Cmd cmd in block.Cmds) - { - otherCmds.Add(Substituter.Apply(subst, cmd)); - } - Block otherBlock = new Block(); - otherBlock.Cmds = otherCmds; - otherBlock.Label = blockLabelPrefix + block.Label; - otherBlocks.Add(otherBlock); - blockMap[block] = otherBlock; - } - foreach (Block block in blocks) - { - if (block.TransferCmd is ReturnCmd) - { - blockMap[block].TransferCmd = new ReturnCmd(block.TransferCmd.tok); - continue; - } - List otherGotoCmdLabelTargets = new List(); - List otherGotoCmdLabelNames = new List(); - GotoCmd gotoCmd = block.TransferCmd as GotoCmd; - foreach (Block target in gotoCmd.labelTargets) - { - otherGotoCmdLabelTargets.Add(blockMap[target]); - otherGotoCmdLabelNames.Add(blockMap[target].Label); - } - blockMap[block].TransferCmd = new GotoCmd(block.TransferCmd.tok, otherGotoCmdLabelNames, otherGotoCmdLabelTargets); - } - return otherBlocks; - } - } - - public class SharedVariableInfo - { - public int introLayerNum; - public int hideLayerNum; - - public SharedVariableInfo(int introLayerNum, int hideLayerNum) - { - this.introLayerNum = introLayerNum; - this.hideLayerNum = hideLayerNum; - } - } - - public class LayerEraser : ReadOnlyVisitor - { - private QKeyValue RemoveLayerAttribute(QKeyValue iter) - { - if (iter == null) return null; - iter.Next = RemoveLayerAttribute(iter.Next); - return (iter.Key == "layer") ? iter.Next : iter; - } - - public override Variable VisitVariable(Variable node) - { - node.Attributes = RemoveLayerAttribute(node.Attributes); - return base.VisitVariable(node); - } - - public override Procedure VisitProcedure(Procedure node) - { - node.Attributes = RemoveLayerAttribute(node.Attributes); - return base.VisitProcedure(node); - } - - public override Implementation VisitImplementation(Implementation node) - { - node.Attributes = RemoveLayerAttribute(node.Attributes); - return base.VisitImplementation(node); - } - - public override Requires VisitRequires(Requires node) - { - node.Attributes = RemoveLayerAttribute(node.Attributes); - return base.VisitRequires(node); - } - - public override Ensures VisitEnsures(Ensures node) - { - node.Attributes = RemoveLayerAttribute(node.Attributes); - return base.VisitEnsures(node); - } - - public override Cmd VisitAssertCmd(AssertCmd node) - { - node.Attributes = RemoveLayerAttribute(node.Attributes); - return base.VisitAssertCmd(node); - } - } - - public class LayerRange - { - public int lowerLayerNum; - public int upperLayerNum; - public LayerRange(int layer) - { - this.lowerLayerNum = layer; - this.upperLayerNum = layer; - } - public LayerRange(int lower, int upper) - { - this.lowerLayerNum = lower; - this.upperLayerNum = upper; - } - public LayerRange(IEnumerable layerNums) - { - int min = int.MaxValue; - int max = int.MinValue; - foreach (var layerNum in layerNums) - { - if (layerNum < min) - { - min = layerNum; - } - if (max < layerNum) - { - max = layerNum; - } - } - this.lowerLayerNum = min; - this.upperLayerNum = max; - } - public bool Contains(int layerNum) - { - return lowerLayerNum <= layerNum && layerNum <= upperLayerNum; - } - public bool Subset(int lower, int upper) - { - return lower <= lowerLayerNum && upperLayerNum <= upper; - } - public bool Equal(int lower, int upper) - { - return lower == lowerLayerNum && upperLayerNum == upper; - } - public bool Subset(LayerRange info) - { - return info.lowerLayerNum <= lowerLayerNum && upperLayerNum <= info.upperLayerNum; - } - } - - public class AtomicProcedureInfo - { - public bool isPure; - public HashSet layers; - public AtomicProcedureInfo() - { - this.isPure = true; - this.layers = null; - } - public AtomicProcedureInfo(HashSet layers) - { - this.isPure = false; - this.layers = layers; - } - } - - public class LocalVariableInfo - { - public bool isGhost; - public int layer; - public LocalVariableInfo(bool isGhost, int layer) - { - this.isGhost = isGhost; - this.layer = layer; - } - } - - public class MoverTypeChecker : ReadOnlyVisitor - { - CheckingContext checkingContext; - Procedure enclosingProc; - Implementation enclosingImpl; - HashSet sharedVarsAccessed; - int introducedLocalVarsUpperBound; - int ghostVarIntroLayerAllowed; - - public Program program; - public int errorCount; - public Dictionary globalVarToSharedVarInfo; - public Dictionary procToActionInfo; - public Dictionary procToAtomicProcedureInfo; - public Dictionary> absyToLayerNums; - public Dictionary localVarToLocalVariableInfo; - - public bool CallExists(CallCmd callCmd, int enclosingProcLayerNum, int layerNum) - { - if (!procToAtomicProcedureInfo.ContainsKey(callCmd.Proc)) - return true; - var atomicProcedureInfo = procToAtomicProcedureInfo[callCmd.Proc]; - if (callCmd.Proc.Modifies.Count > 0) - { - return enclosingProcLayerNum == layerNum; - } - if (callCmd.Outs.Count == 0) - { - return true; - } - var outputVar = callCmd.Outs[0].Decl; - var localVariableInfo = localVarToLocalVariableInfo[outputVar]; - if (localVariableInfo.isGhost) - { - return localVariableInfo.layer == layerNum; - } - if (atomicProcedureInfo.isPure) - { - return localVariableInfo.layer <= layerNum; - } - else - { - return enclosingProcLayerNum == layerNum; - } - } - - private static List FindLayers(QKeyValue kv) - { - List layers = new List(); - for (; kv != null; kv = kv.Next) - { - if (kv.Key != "layer") continue; - foreach (var o in kv.Params) - { - Expr e = o as Expr; - if (e == null) return null; - LiteralExpr l = e as LiteralExpr; - if (l == null) return null; - if (!l.isBigNum) return null; - layers.Add(l.asBigNum.ToIntSafe); - } - } - return layers; - } - - private static int Least(IEnumerable layerNums) - { - int least = int.MaxValue; - foreach (var layer in layerNums) - { - if (layer < least) - { - least = layer; - } - } - return least; - } - - private static MoverType GetMoverType(Ensures e) - { - if (QKeyValue.FindBoolAttribute(e.Attributes, "atomic")) - return MoverType.Atomic; - if (QKeyValue.FindBoolAttribute(e.Attributes, "right")) - return MoverType.Right; - if (QKeyValue.FindBoolAttribute(e.Attributes, "left")) - return MoverType.Left; - if (QKeyValue.FindBoolAttribute(e.Attributes, "both")) - return MoverType.Both; - return MoverType.Top; - } - - public MoverTypeChecker(Program program) - { - this.errorCount = 0; - this.checkingContext = new CheckingContext(null); - this.program = program; - this.enclosingProc = null; - this.enclosingImpl = null; - this.sharedVarsAccessed = null; - this.introducedLocalVarsUpperBound = int.MinValue; - this.ghostVarIntroLayerAllowed = int.MinValue; - - this.localVarToLocalVariableInfo = new Dictionary(); - this.absyToLayerNums = new Dictionary>(); - this.globalVarToSharedVarInfo = new Dictionary(); - this.procToActionInfo = new Dictionary(); - this.procToAtomicProcedureInfo = new Dictionary(); - - foreach (var g in program.GlobalVariables) - { - List layerNums = FindLayers(g.Attributes); - if (layerNums.Count == 0) - { - // Inaccessible from yielding and atomic procedures - } - else if (layerNums.Count == 1) - { - this.globalVarToSharedVarInfo[g] = new SharedVariableInfo(layerNums[0], int.MaxValue); - } - else if (layerNums.Count == 2) - { - this.globalVarToSharedVarInfo[g] = new SharedVariableInfo(layerNums[0], layerNums[1]); - } - else - { - Error(g, "Too many layer numbers"); - } - } - } - - private HashSet allImplementedLayerNums; - public IEnumerable AllImplementedLayerNums - { - get - { - if (allImplementedLayerNums == null) - { - allImplementedLayerNums = new HashSet(); - foreach (ActionInfo actionInfo in procToActionInfo.Values) - { - if (actionInfo.hasImplementation) - { - allImplementedLayerNums.Add(actionInfo.createdAtLayerNum); - } - } - } - return allImplementedLayerNums; - } - } - - private HashSet allCreatedLayerNums; - public IEnumerable AllCreatedLayerNums - { - get - { - if (allCreatedLayerNums == null) - { - allCreatedLayerNums = new HashSet(); - foreach (ActionInfo actionInfo in procToActionInfo.Values) - { - allCreatedLayerNums.Add(actionInfo.createdAtLayerNum); - } - } - return allCreatedLayerNums; - } - } - - private LayerRange FindLayerRange() - { - int maxIntroLayerNum = int.MinValue; - int minHideLayerNum = int.MaxValue; - foreach (var g in sharedVarsAccessed) - { - if (globalVarToSharedVarInfo[g].introLayerNum > maxIntroLayerNum) - { - maxIntroLayerNum = globalVarToSharedVarInfo[g].introLayerNum; - } - if (globalVarToSharedVarInfo[g].hideLayerNum < minHideLayerNum) - { - minHideLayerNum = globalVarToSharedVarInfo[g].hideLayerNum; - } - } - return new LayerRange(maxIntroLayerNum, minHideLayerNum); - } - - public void TypeCheck() - { - foreach (var proc in program.Procedures) - { - if (!QKeyValue.FindBoolAttribute(proc.Attributes, "pure")) continue; - if (QKeyValue.FindBoolAttribute(proc.Attributes, "yields")) - { - Error(proc, "Pure procedure must not yield"); - continue; - } - if (QKeyValue.FindBoolAttribute(proc.Attributes, "layer")) - { - Error(proc, "Pure procedure must not have layers"); - continue; - } - if (proc.Modifies.Count > 0) - { - Error(proc, "Pure procedure must not modify a global variable"); - continue; - } - procToAtomicProcedureInfo[proc] = new AtomicProcedureInfo(); - } - foreach (var proc in program.Procedures) - { - if (QKeyValue.FindBoolAttribute(proc.Attributes, "yields")) continue; - var procLayerNums = RemoveDuplicatesAndSort(FindLayers(proc.Attributes)); - if (procLayerNums.Count == 0) continue; - foreach (IdentifierExpr ie in proc.Modifies) - { - if (!globalVarToSharedVarInfo.ContainsKey(ie.Decl)) - { - Error(proc, "Atomic procedure cannot modify a global variable without layer numbers"); - } - else if (globalVarToSharedVarInfo[ie.Decl].introLayerNum != procLayerNums[0]) - { - Error(proc, "The introduction layer of a modified global variable must be identical to the layer of the atomic procedure"); - } - } - if (proc.Modifies.Count == 0 || procLayerNums.Count == 1) - { - procToAtomicProcedureInfo[proc] = new AtomicProcedureInfo(new HashSet(procLayerNums)); - } - else - { - Error(proc, "An atomic procedure with more than one layer must not modify a global variable"); - } - } - if (errorCount > 0) return; - - foreach (Implementation impl in program.Implementations) - { - if (!procToAtomicProcedureInfo.ContainsKey(impl.Proc)) continue; - var atomicProcedureInfo = procToAtomicProcedureInfo[impl.Proc]; - if (atomicProcedureInfo.isPure) - { - this.enclosingImpl = impl; - (new PurityChecker(this)).VisitImplementation(impl); - } - else - { - this.enclosingImpl = impl; - this.sharedVarsAccessed = new HashSet(); - (new PurityChecker(this)).VisitImplementation(impl); - LayerRange upperBound = FindLayerRange(); - LayerRange lowerBound = new LayerRange(atomicProcedureInfo.layers); - if (!lowerBound.Subset(upperBound)) - { - Error(impl, "Atomic procedure cannot access global variable"); - } - this.sharedVarsAccessed = null; - } - } - if (errorCount > 0) return; - - foreach (var proc in program.Procedures) - { - if (!QKeyValue.FindBoolAttribute(proc.Attributes, "yields")) continue; - - int createdAtLayerNum; // must be initialized by the following code, otherwise it is an error - int availableUptoLayerNum = int.MaxValue; - List attrs = FindLayers(proc.Attributes); - if (attrs.Count == 1) - { - createdAtLayerNum = attrs[0]; - } - else if (attrs.Count == 2) - { - createdAtLayerNum = attrs[0]; - availableUptoLayerNum = attrs[1]; - } - else - { - Error(proc, "Incorrect number of layers"); - continue; - } - foreach (Ensures e in proc.Ensures) - { - MoverType moverType = GetMoverType(e); - if (moverType == MoverType.Top) continue; - CodeExpr codeExpr = e.Condition as CodeExpr; - if (codeExpr == null) - { - Error(e, "An atomic action must be a CodeExpr"); - continue; - } - if (procToActionInfo.ContainsKey(proc)) - { - Error(proc, "A procedure can have at most one atomic action"); - continue; - } - if (availableUptoLayerNum <= createdAtLayerNum) - { - Error(proc, "Creation layer number must be less than the available upto layer number"); - continue; - } - - sharedVarsAccessed = new HashSet(); - enclosingProc = proc; - enclosingImpl = null; - base.VisitEnsures(e); - LayerRange upperBound = FindLayerRange(); - LayerRange lowerBound = new LayerRange(createdAtLayerNum, availableUptoLayerNum); - if (lowerBound.Subset(upperBound)) - { - procToActionInfo[proc] = new AtomicActionInfo(proc, e, moverType, createdAtLayerNum, availableUptoLayerNum); - } - else - { - Error(e, "A variable being accessed in this action is unavailable"); - } - sharedVarsAccessed = null; - } - if (errorCount > 0) continue; - if (!procToActionInfo.ContainsKey(proc)) - { - if (availableUptoLayerNum < createdAtLayerNum) - { - Error(proc, "Creation layer number must be no more than the available upto layer number"); - continue; - } - else - { - procToActionInfo[proc] = new ActionInfo(proc, createdAtLayerNum, availableUptoLayerNum); - } - } - } - if (errorCount > 0) return; - foreach (Implementation node in program.Implementations) - { - if (!procToActionInfo.ContainsKey(node.Proc)) continue; - foreach (Variable v in node.LocVars) - { - var layer = FindLocalVariableLayer(node, v, procToActionInfo[node.Proc].createdAtLayerNum); - if (layer == int.MinValue) continue; - localVarToLocalVariableInfo[v] = new LocalVariableInfo(QKeyValue.FindBoolAttribute(node.Attributes, "ghost"), layer); - } - for (int i = 0; i < node.Proc.InParams.Count; i++) - { - Variable v = node.Proc.InParams[i]; - var layer = FindLocalVariableLayer(node.Proc, v, procToActionInfo[node.Proc].createdAtLayerNum); - if (layer == int.MinValue) continue; - localVarToLocalVariableInfo[v] = new LocalVariableInfo(false, layer); - localVarToLocalVariableInfo[node.InParams[i]] = new LocalVariableInfo(false, layer); - } - for (int i = 0; i < node.Proc.OutParams.Count; i++) - { - Variable v = node.Proc.OutParams[i]; - var layer = FindLocalVariableLayer(node.Proc, v, procToActionInfo[node.Proc].createdAtLayerNum); - if (layer == int.MinValue) continue; - localVarToLocalVariableInfo[v] = new LocalVariableInfo(false, layer); - localVarToLocalVariableInfo[node.OutParams[i]] = new LocalVariableInfo(false, layer); - } - } - if (errorCount > 0) return; - foreach (var impl in program.Implementations) - { - if (!procToActionInfo.ContainsKey(impl.Proc)) continue; - ActionInfo actionInfo = procToActionInfo[impl.Proc]; - procToActionInfo[impl.Proc].hasImplementation = true; - if (actionInfo.isExtern) - { - Error(impl.Proc, "Extern procedure cannot have an implementation"); - } - } - foreach (var g in this.globalVarToSharedVarInfo.Keys) - { - var info = globalVarToSharedVarInfo[g]; - if (!this.AllCreatedLayerNums.Contains(info.introLayerNum)) - { - Error(g, "Variable must be introduced with creation of some atomic action"); - } - if (info.hideLayerNum != int.MaxValue && !this.AllCreatedLayerNums.Contains(info.hideLayerNum)) - { - Error(g, "Variable must be hidden with creation of some atomic action"); - } - } - if (errorCount > 0) return; - this.VisitProgram(program); - if (errorCount > 0) return; - YieldTypeChecker.PerformYieldSafeCheck(this); - new LayerEraser().VisitProgram(program); - } - - public IEnumerable SharedVariables - { - get { return this.globalVarToSharedVarInfo.Keys; } - } - - private int FindLocalVariableLayer(Declaration decl, Variable v, int enclosingProcLayerNum) - { - var layers = FindLayers(v.Attributes); - if (layers.Count == 0) return int.MinValue; - if (layers.Count > 1) - { - Error(decl, "Incorrect number of layers"); - return int.MinValue; - } - if (layers[0] > enclosingProcLayerNum) - { - Error(decl, "Layer of local variable cannot be greater than the creation layer of enclosing procedure"); - return int.MinValue; - } - return layers[0]; - } - - public override Implementation VisitImplementation(Implementation node) - { - if (!procToActionInfo.ContainsKey(node.Proc)) - { - return node; - } - this.enclosingImpl = node; - this.enclosingProc = null; - return base.VisitImplementation(node); - } - - public override Procedure VisitProcedure(Procedure node) - { - if (!procToActionInfo.ContainsKey(node)) - { - return node; - } - this.enclosingProc = node; - this.enclosingImpl = null; - return base.VisitProcedure(node); - } - - public override Cmd VisitCallCmd(CallCmd node) - { - int enclosingProcLayerNum = procToActionInfo[enclosingImpl.Proc].createdAtLayerNum; - if (procToActionInfo.ContainsKey(node.Proc)) - { - ActionInfo actionInfo = procToActionInfo[node.Proc]; - if (node.IsAsync && actionInfo is AtomicActionInfo) - { - Error(node, "Target of async call cannot be an atomic action"); - } - int calleeLayerNum = procToActionInfo[node.Proc].createdAtLayerNum; - if (enclosingProcLayerNum < calleeLayerNum || - (enclosingProcLayerNum == calleeLayerNum && actionInfo is AtomicActionInfo)) - { - Error(node, "The layer of the caller must be greater than the layer of the callee"); - } - else if (enclosingProcLayerNum == calleeLayerNum && enclosingImpl.OutParams.Count > 0) - { - HashSet outParams = new HashSet(enclosingImpl.OutParams); - foreach (var x in node.Outs) - { - if (x.Decl is GlobalVariable) - { - Error(node, "A global variable cannot be used as output argument for this call"); - } - else if (outParams.Contains(x.Decl)) - { - Error(node, "An output variable of the enclosing implementation cannot be used as output argument for this call"); - } - } - } - if (actionInfo.availableUptoLayerNum < enclosingProcLayerNum) - { - Error(node, "The callee is not available in the caller procedure"); - } - for (int i = 0; i < node.Ins.Count; i++) - { - var formal = node.Proc.InParams[i]; - if (localVarToLocalVariableInfo.ContainsKey(formal)) - { - introducedLocalVarsUpperBound = localVarToLocalVariableInfo[formal].layer; - } - Visit(node.Ins[i]); - introducedLocalVarsUpperBound = int.MinValue; - } - for (int i = 0; i < node.Outs.Count; i++) - { - var formal = node.Proc.OutParams[i]; - if (!localVarToLocalVariableInfo.ContainsKey(formal)) continue; - var actual = node.Outs[i].Decl; - if (localVarToLocalVariableInfo.ContainsKey(actual) && - localVarToLocalVariableInfo[formal].layer <= localVarToLocalVariableInfo[actual].layer) - continue; - Error(node, "Formal parameter of call must be introduced no later than the actual parameter"); - } - return node; - } - else if (procToAtomicProcedureInfo.ContainsKey(node.Proc)) - { - // 1. Outputs are either all ghost or all introduced. - // 2. All outputs have the same layer; call it output layer. - // 3. If callee is impure and has outputs, output layer is a member of layer set of callee. - // 4. If callee is impure and has introduced outputs, then creation number of caller belongs to layer set of callee. - // 5. If callee is impure and modifies globals, then creation number of caller belongs to layer set of callee. - - Debug.Assert(introducedLocalVarsUpperBound == int.MinValue); - bool isGhost = false; // This assignment stops the compiler from complaining. - // In the absence of errors, isGhost is initialized by loop below. - foreach (var ie in node.Outs) - { - if (localVarToLocalVariableInfo.ContainsKey(ie.Decl)) - { - var localVariableInfo = localVarToLocalVariableInfo[ie.Decl]; - if (introducedLocalVarsUpperBound == int.MinValue) - { - introducedLocalVarsUpperBound = localVariableInfo.layer; - isGhost = localVariableInfo.isGhost; - var atomicProcedureInfo = procToAtomicProcedureInfo[node.Proc]; - if (!atomicProcedureInfo.isPure) - { - if (!atomicProcedureInfo.layers.Contains(introducedLocalVarsUpperBound)) - { - Error(node, "Layer of output variable must be a layer of the callee"); - } - if (!isGhost && !atomicProcedureInfo.layers.Contains(enclosingProcLayerNum)) - { - Error(node, "The creation layer of caller must be a layer of the callee"); - } - } - } - else - { - if (localVariableInfo.layer != introducedLocalVarsUpperBound) - { - Error(node, "All outputs must have the same layer"); - } - if (localVariableInfo.isGhost != isGhost) - { - Error(node, "Outputs are either all ghost or all introduced"); - } - } - } - else - { - Error(node, "Output variable must be a ghost or introduced local variable"); - } - } - - if (node.Proc.Modifies.Count > 0) - { - var atomicProcedureInfo = procToAtomicProcedureInfo[node.Proc]; - if (procToActionInfo[enclosingImpl.Proc] is AtomicActionInfo) - { - if (!atomicProcedureInfo.layers.Contains(enclosingProcLayerNum)) - { - Error(node, "The layer of called atomic procedure must be identical to the creation layer of callee"); - } - } - else - { - Error(node, "Enclosing implementation must refine an atomic action"); - } - introducedLocalVarsUpperBound = enclosingProcLayerNum; - } - foreach (var e in node.Ins) - { - Visit(e); - } - introducedLocalVarsUpperBound = int.MinValue; - return node; - } - else - { - Error(node, "A yielding procedure can call only atomic or yielding procedures"); - return node; - } - } - - public override Cmd VisitParCallCmd(ParCallCmd node) - { - int enclosingProcLayerNum = procToActionInfo[enclosingImpl.Proc].createdAtLayerNum; - bool isLeftMover = true; - bool isRightMover = true; - int maxCalleeLayerNum = 0; - int atomicActionCalleeLayerNum = 0; - int numAtomicActions = 0; - foreach (CallCmd iter in node.CallCmds) - { - ActionInfo actionInfo = procToActionInfo[iter.Proc]; - isLeftMover = isLeftMover && actionInfo.IsLeftMover; - isRightMover = isRightMover && actionInfo.IsRightMover; - if (actionInfo.createdAtLayerNum > maxCalleeLayerNum) - { - maxCalleeLayerNum = actionInfo.createdAtLayerNum; - } - if (actionInfo is AtomicActionInfo) - { - numAtomicActions++; - if (atomicActionCalleeLayerNum == 0) - { - atomicActionCalleeLayerNum = actionInfo.createdAtLayerNum; - } - else if (atomicActionCalleeLayerNum != actionInfo.createdAtLayerNum) - { - Error(node, "All atomic actions must be introduced at the same layer"); - } - } - } - if (numAtomicActions > 1 && !isLeftMover && !isRightMover) - { - Error(node, "The atomic actions in the parallel call must be all right movers or all left movers"); - } - if (0 < atomicActionCalleeLayerNum && atomicActionCalleeLayerNum < maxCalleeLayerNum) - { - Error(node, "Atomic actions must be introduced at the highest layer"); - } - return base.VisitParCallCmd(node); - } - - public override Expr VisitIdentifierExpr(IdentifierExpr node) - { - if (node.Decl is GlobalVariable) - { - if (sharedVarsAccessed == null) - { - Error(node, "Shared variable can be accessed only in atomic actions or specifications"); - } - else if (this.globalVarToSharedVarInfo.ContainsKey(node.Decl)) - { - sharedVarsAccessed.Add(node.Decl); - } - else - { - Error(node, "Accessed shared variable must have layer annotation"); - } - } - else if ((node.Decl is Formal || node.Decl is Variable) && localVarToLocalVariableInfo.ContainsKey(node.Decl)) - { - var localVariableInfo = localVarToLocalVariableInfo[node.Decl]; - if (localVariableInfo.isGhost) - { - if (ghostVarIntroLayerAllowed != localVariableInfo.layer) - { - Error(node, "Ghost variable inaccessible"); - } - } - else - { - if (introducedLocalVarsUpperBound < localVariableInfo.layer) - { - Error(node, "Introduced variable inaccessible"); - } - } - } - return base.VisitIdentifierExpr(node); - } - - public override Ensures VisitEnsures(Ensures ensures) - { - ActionInfo actionInfo = procToActionInfo[enclosingProc]; - AtomicActionInfo atomicActionInfo = actionInfo as AtomicActionInfo; - if (atomicActionInfo != null && atomicActionInfo.ensures == ensures) - { - // This case has already been checked - } - else - { - sharedVarsAccessed = new HashSet(); - Debug.Assert(introducedLocalVarsUpperBound == int.MinValue); - introducedLocalVarsUpperBound = Least(FindLayers(ensures.Attributes)); - base.VisitEnsures(ensures); - CheckAndAddLayers(ensures, ensures.Attributes, actionInfo.createdAtLayerNum); - introducedLocalVarsUpperBound = int.MinValue; - sharedVarsAccessed = null; - } - return ensures; - } - - public override Requires VisitRequires(Requires requires) - { - sharedVarsAccessed = new HashSet(); - Debug.Assert(introducedLocalVarsUpperBound == int.MinValue); - introducedLocalVarsUpperBound = Least(FindLayers(requires.Attributes)); - base.VisitRequires(requires); - CheckAndAddLayers(requires, requires.Attributes, procToActionInfo[enclosingProc].createdAtLayerNum); - introducedLocalVarsUpperBound = int.MinValue; - sharedVarsAccessed = null; - return requires; - } - - public override Cmd VisitAssertCmd(AssertCmd node) - { - if (enclosingImpl == null) - { - // in this case, we are visiting an assert inside a CodeExpr - return base.VisitAssertCmd(node); - } - sharedVarsAccessed = new HashSet(); - Debug.Assert(introducedLocalVarsUpperBound == int.MinValue); - var layerNums = FindLayers(node.Attributes); - introducedLocalVarsUpperBound = Least(layerNums); - if (layerNums.Count == 1) - { - ghostVarIntroLayerAllowed = layerNums[0]; - } - base.VisitAssertCmd(node); - CheckAndAddLayers(node, node.Attributes, procToActionInfo[enclosingImpl.Proc].createdAtLayerNum); - introducedLocalVarsUpperBound = int.MinValue; - ghostVarIntroLayerAllowed = int.MinValue; - sharedVarsAccessed = null; - return node; - } - - private List RemoveDuplicatesAndSort(List attrs) - { - HashSet layerSet = new HashSet(attrs); - List layers = new List(layerSet); - layers.Sort(); - return layers; - } - - private void CheckAndAddLayers(Absy node, QKeyValue attributes, int enclosingProcLayerNum) - { - List attrs = RemoveDuplicatesAndSort(FindLayers(attributes)); - if (attrs.Count == 0) - { - Error(node, "layer not present"); - return; - } - LayerRange upperBound = FindLayerRange(); - absyToLayerNums[node] = new HashSet(); - foreach (int layerNum in attrs) - { - if (layerNum > enclosingProcLayerNum) - { - Error(node, "The layer cannot be greater than the layer of enclosing procedure"); - } - else if (upperBound.Contains(layerNum)) - { - absyToLayerNums[node].Add(layerNum); - } - else - { - Error(node, string.Format("A variable being accessed in this specification is unavailable at layer {0}", layerNum)); - } - } - } - - public void Error(Absy node, string message) - { - checkingContext.Error(node, message); - errorCount++; - } - - private class PurityChecker : StandardVisitor - { - private MoverTypeChecker moverTypeChecker; - - public PurityChecker(MoverTypeChecker moverTypeChecker) - { - this.moverTypeChecker = moverTypeChecker; - } - - public override Cmd VisitCallCmd(CallCmd node) - { - Procedure enclosingProc = moverTypeChecker.enclosingImpl.Proc; - if (!moverTypeChecker.procToAtomicProcedureInfo.ContainsKey(node.Proc)) - { - moverTypeChecker.Error(node, "Atomic procedure can only call an atomic procedure"); - return base.VisitCallCmd(node); - } - var callerInfo = moverTypeChecker.procToAtomicProcedureInfo[enclosingProc]; - var calleeInfo = moverTypeChecker.procToAtomicProcedureInfo[node.Proc]; - if (calleeInfo.isPure) - { - // do nothing - } - else if (callerInfo.isPure) - { - moverTypeChecker.Error(node, "Pure procedure can only call pure procedures"); - } - else if (!callerInfo.layers.IsSubsetOf(calleeInfo.layers)) - { - moverTypeChecker.Error(node, "Caller layers must be subset of callee layers"); - } - return base.VisitCallCmd(node); - } - - public override Cmd VisitParCallCmd(ParCallCmd node) - { - moverTypeChecker.Error(node, "Atomic procedures cannot make parallel calls"); - return node; - } - - public override Expr VisitIdentifierExpr(IdentifierExpr node) - { - Procedure enclosingProc = moverTypeChecker.enclosingImpl.Proc; - if (node.Decl is GlobalVariable) - { - if (moverTypeChecker.procToAtomicProcedureInfo[enclosingProc].isPure) - { - moverTypeChecker.Error(node, "Pure procedure cannot access global variables"); - } - else if (!moverTypeChecker.globalVarToSharedVarInfo.ContainsKey(node.Decl)) - { - moverTypeChecker.Error(node, "Atomic procedure cannot access a global variable without layer numbers"); - } - else - { - moverTypeChecker.sharedVarsAccessed.Add(node.Decl); - } - } - return node; - } - } - } -} diff --git a/Source/Concurrency/YieldTypeChecker.cs b/Source/Concurrency/YieldTypeChecker.cs index 5b479ed5..a69f066d 100644 --- a/Source/Concurrency/YieldTypeChecker.cs +++ b/Source/Concurrency/YieldTypeChecker.cs @@ -81,7 +81,7 @@ namespace Microsoft.Boogie Dictionary> simulationRelation = x.ComputeSimulationRelation(); if (simulationRelation[initialState].Count == 0) { - moverTypeChecker.Error(impl, string.Format("Implementation {0} fails simulation check A at layer {1}. An action must be preceded by a yield.\n", impl.Name, currLayerNum)); + civlTypeChecker.Error(impl, string.Format("Implementation {0} fails simulation check A at layer {1}. An action must be preceded by a yield.\n", impl.Name, currLayerNum)); } } @@ -97,7 +97,7 @@ namespace Microsoft.Boogie Dictionary> simulationRelation = x.ComputeSimulationRelation(); if (simulationRelation[initialState].Count == 0) { - moverTypeChecker.Error(impl, string.Format("Implementation {0} fails simulation check B at layer {1}. An action must be succeeded by a yield.\n", impl.Name, currLayerNum)); + civlTypeChecker.Error(impl, string.Format("Implementation {0} fails simulation check B at layer {1}. An action must be succeeded by a yield.\n", impl.Name, currLayerNum)); } } @@ -115,7 +115,7 @@ namespace Microsoft.Boogie Dictionary> simulationRelation = x.ComputeSimulationRelation(); if (simulationRelation[initialState].Count == 0) { - moverTypeChecker.Error(impl, string.Format("Implementation {0} fails simulation check C at layer {1}. Transactions must be separated by a yield.\n", impl.Name, currLayerNum)); + civlTypeChecker.Error(impl, string.Format("Implementation {0} fails simulation check C at layer {1}. Transactions must be separated by a yield.\n", impl.Name, currLayerNum)); } } @@ -124,7 +124,7 @@ namespace Microsoft.Boogie foreach (Cmd cmd in block.Cmds) { AssertCmd assertCmd = cmd as AssertCmd; - if (assertCmd != null && QKeyValue.FindBoolAttribute(assertCmd.Attributes, "terminates") && moverTypeChecker.absyToLayerNums[assertCmd].Contains(currLayerNum)) + if (assertCmd != null && QKeyValue.FindBoolAttribute(assertCmd.Attributes, "terminates") && civlTypeChecker.absyToLayerNums[assertCmd].Contains(currLayerNum)) { return true; } @@ -132,25 +132,25 @@ namespace Microsoft.Boogie return false; } - public static void PerformYieldSafeCheck(MoverTypeChecker moverTypeChecker) + public static void PerformYieldSafeCheck(CivlTypeChecker civlTypeChecker) { - foreach (var impl in moverTypeChecker.program.Implementations) + foreach (var impl in civlTypeChecker.program.Implementations) { - if (!moverTypeChecker.procToActionInfo.ContainsKey(impl.Proc)) continue; + if (!civlTypeChecker.procToActionInfo.ContainsKey(impl.Proc)) continue; impl.PruneUnreachableBlocks(); Graph implGraph = Program.GraphFromImpl(impl); implGraph.ComputeLoops(); - int specLayerNum = moverTypeChecker.procToActionInfo[impl.Proc].createdAtLayerNum; - foreach (int layerNum in moverTypeChecker.AllImplementedLayerNums) + int specLayerNum = civlTypeChecker.procToActionInfo[impl.Proc].createdAtLayerNum; + foreach (int layerNum in civlTypeChecker.AllImplementedLayerNums) { if (layerNum > specLayerNum) continue; - YieldTypeChecker executor = new YieldTypeChecker(moverTypeChecker, impl, layerNum, implGraph.Headers); + YieldTypeChecker executor = new YieldTypeChecker(civlTypeChecker, impl, layerNum, implGraph.Headers); } } } int stateCounter; - MoverTypeChecker moverTypeChecker; + CivlTypeChecker civlTypeChecker; Implementation impl; int currLayerNum; Dictionary absyToNode; @@ -160,9 +160,9 @@ namespace Microsoft.Boogie Dictionary, int> edgeLabels; IEnumerable loopHeaders; - private YieldTypeChecker(MoverTypeChecker moverTypeChecker, Implementation impl, int currLayerNum, IEnumerable loopHeaders) + private YieldTypeChecker(CivlTypeChecker civlTypeChecker, Implementation impl, int currLayerNum, IEnumerable loopHeaders) { - this.moverTypeChecker = moverTypeChecker; + this.civlTypeChecker = civlTypeChecker; this.impl = impl; this.currLayerNum = currLayerNum; this.loopHeaders = loopHeaders; @@ -226,20 +226,20 @@ namespace Microsoft.Boogie CallCmd callCmd = cmd as CallCmd; if (callCmd.IsAsync) { - ActionInfo actionInfo = moverTypeChecker.procToActionInfo[callCmd.Proc]; + ActionInfo actionInfo = civlTypeChecker.procToActionInfo[callCmd.Proc]; if (currLayerNum <= actionInfo.createdAtLayerNum) edgeLabels[edge] = 'L'; else edgeLabels[edge] = 'B'; } - else if (!moverTypeChecker.procToActionInfo.ContainsKey(callCmd.Proc)) + else if (!civlTypeChecker.procToActionInfo.ContainsKey(callCmd.Proc)) { edgeLabels[edge] = 'P'; } else { MoverType moverType; - ActionInfo actionInfo = moverTypeChecker.procToActionInfo[callCmd.Proc]; + ActionInfo actionInfo = civlTypeChecker.procToActionInfo[callCmd.Proc]; if (actionInfo.createdAtLayerNum >= currLayerNum) { moverType = MoverType.Top; @@ -280,7 +280,7 @@ namespace Microsoft.Boogie bool isLeftMover = true; foreach (CallCmd callCmd in parCallCmd.CallCmds) { - if (moverTypeChecker.procToActionInfo[callCmd.Proc].createdAtLayerNum >= currLayerNum) + if (civlTypeChecker.procToActionInfo[callCmd.Proc].createdAtLayerNum >= currLayerNum) { isYield = true; } @@ -294,7 +294,7 @@ namespace Microsoft.Boogie int numAtomicActions = 0; foreach (CallCmd callCmd in parCallCmd.CallCmds) { - ActionInfo actionInfo = moverTypeChecker.procToActionInfo[callCmd.Proc]; + ActionInfo actionInfo = civlTypeChecker.procToActionInfo[callCmd.Proc]; isRightMover = isRightMover && actionInfo.IsRightMover; isLeftMover = isLeftMover && actionInfo.IsLeftMover; if (actionInfo is AtomicActionInfo) diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index 353ac94f..cf44a77f 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -482,8 +482,8 @@ namespace Microsoft.Boogie } LinearTypeChecker linearTypeChecker; - MoverTypeChecker moverTypeChecker; - PipelineOutcome oc = ResolveAndTypecheck(program, fileNames[fileNames.Count - 1], out linearTypeChecker, out moverTypeChecker); + CivlTypeChecker civlTypeChecker; + PipelineOutcome oc = ResolveAndTypecheck(program, fileNames[fileNames.Count - 1], out linearTypeChecker, out civlTypeChecker); if (oc != PipelineOutcome.ResolvedAndTypeChecked) return; @@ -500,7 +500,7 @@ namespace Microsoft.Boogie if (CommandLineOptions.Clo.StratifiedInlining == 0) { - Concurrency.Transform(linearTypeChecker, moverTypeChecker); + Concurrency.Transform(linearTypeChecker, civlTypeChecker); (new LinearEraser()).VisitProgram(program); if (CommandLineOptions.Clo.OwickiGriesDesugaredOutputFile != null) { @@ -692,13 +692,13 @@ namespace Microsoft.Boogie /// - TypeCheckingError if a type checking error occurred /// - ResolvedAndTypeChecked if both resolution and type checking succeeded /// - public static PipelineOutcome ResolveAndTypecheck(Program program, string bplFileName, out LinearTypeChecker linearTypeChecker, out MoverTypeChecker moverTypeChecker) + public static PipelineOutcome ResolveAndTypecheck(Program program, string bplFileName, out LinearTypeChecker linearTypeChecker, out CivlTypeChecker civlTypeChecker) { Contract.Requires(program != null); Contract.Requires(bplFileName != null); linearTypeChecker = null; - moverTypeChecker = null; + civlTypeChecker = null; // ---------- Resolve ------------------------------------------------------------ @@ -735,11 +735,11 @@ namespace Microsoft.Boogie CollectModSets(program); - moverTypeChecker = new MoverTypeChecker(program); - moverTypeChecker.TypeCheck(); - if (moverTypeChecker.errorCount != 0) + civlTypeChecker = new CivlTypeChecker(program); + civlTypeChecker.TypeCheck(); + if (civlTypeChecker.errorCount != 0) { - Console.WriteLine("{0} type checking errors detected in {1}", moverTypeChecker.errorCount, GetFileNameForConsole(bplFileName)); + Console.WriteLine("{0} type checking errors detected in {1}", civlTypeChecker.errorCount, GetFileNameForConsole(bplFileName)); return PipelineOutcome.TypeCheckingError; } @@ -1358,8 +1358,8 @@ namespace Microsoft.Boogie Program p = ParseBoogieProgram(new List { filename }, false); System.Diagnostics.Debug.Assert(p != null); LinearTypeChecker linearTypeChecker; - MoverTypeChecker moverTypeChecker; - PipelineOutcome oc = ExecutionEngine.ResolveAndTypecheck(p, filename, out linearTypeChecker, out moverTypeChecker); + CivlTypeChecker civlTypeChecker; + PipelineOutcome oc = ExecutionEngine.ResolveAndTypecheck(p, filename, out linearTypeChecker, out civlTypeChecker); System.Diagnostics.Debug.Assert(oc == PipelineOutcome.ResolvedAndTypeChecked); return p; } -- cgit v1.2.3 From 7a2aec84f1d924086b6f8e0f3dcbde036e12345c Mon Sep 17 00:00:00 2001 From: Shaz Qadeer Date: Mon, 28 Sep 2015 09:47:27 -0700 Subject: update --- Source/Core/CommandLineOptions.cs | 6 +++--- Source/ExecutionEngine/ExecutionEngine.cs | 4 ++-- Source/VCGeneration/VC.cs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/Core/CommandLineOptions.cs b/Source/Core/CommandLineOptions.cs index 2dda1dd9..f4cba1dc 100644 --- a/Source/Core/CommandLineOptions.cs +++ b/Source/Core/CommandLineOptions.cs @@ -588,7 +588,7 @@ namespace Microsoft.Boogie { } } - public string OwickiGriesDesugaredOutputFile = null; + public string CivlDesugaredFile = null; public bool TrustAtomicityTypes = false; public bool TrustNonInterference = false; public int TrustLayersUpto = -1; @@ -915,9 +915,9 @@ namespace Microsoft.Boogie { } return true; - case "OwickiGries": + case "CivlDesugaredFile": if (ps.ConfirmArgumentCount(1)) { - OwickiGriesDesugaredOutputFile = args[ps.i]; + CivlDesugaredFile = args[ps.i]; } return true; diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index cf44a77f..15fdc081 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -502,11 +502,11 @@ namespace Microsoft.Boogie { Concurrency.Transform(linearTypeChecker, civlTypeChecker); (new LinearEraser()).VisitProgram(program); - if (CommandLineOptions.Clo.OwickiGriesDesugaredOutputFile != null) + if (CommandLineOptions.Clo.CivlDesugaredFile != null) { int oldPrintUnstructured = CommandLineOptions.Clo.PrintUnstructured; CommandLineOptions.Clo.PrintUnstructured = 1; - PrintBplFile(CommandLineOptions.Clo.OwickiGriesDesugaredOutputFile, program, false, false, CommandLineOptions.Clo.PrettyPrint); + PrintBplFile(CommandLineOptions.Clo.CivlDesugaredFile, program, false, false, CommandLineOptions.Clo.PrettyPrint); CommandLineOptions.Clo.PrintUnstructured = oldPrintUnstructured; } } diff --git a/Source/VCGeneration/VC.cs b/Source/VCGeneration/VC.cs index b457b383..94584027 100644 --- a/Source/VCGeneration/VC.cs +++ b/Source/VCGeneration/VC.cs @@ -1632,7 +1632,7 @@ namespace VC { //use Duplicator and Substituter rather than new //nested IToken? //document expand attribute (search for {:ignore}, for example) - //fix up new CallCmd, new Requires, new Ensures in OwickiGries.cs + //fix up new CallCmd, new Requires, new Ensures in CivlRefinement.cs Func withType = (Expr from, Expr to) => { NAryExpr nFrom = from as NAryExpr; -- cgit v1.2.3 From 597a558b2fde558b7f5c581481fd51258aa37c46 Mon Sep 17 00:00:00 2001 From: Valentin Wüstholz Date: Wed, 30 Sep 2015 21:53:31 +0200 Subject: Improve output for diagnosing timeouts. --- Source/ExecutionEngine/ExecutionEngine.cs | 37 ++++++++++++++++++++++++++++-- Source/VCGeneration/ConditionGeneration.cs | 13 ++++++++++- Source/VCGeneration/VC.cs | 1 + 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index 15fdc081..4ab0a9c5 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -1459,11 +1459,11 @@ namespace Microsoft.Boogie printer.Inform(timeIndication + OutcomeIndication(outcome, errors), tw); - ReportOutcome(outcome, er, implName, implTok, requestId, tw, timeLimit); + ReportOutcome(outcome, er, implName, implTok, requestId, tw, timeLimit, errors); } - private static void ReportOutcome(VC.VCGen.Outcome outcome, ErrorReporterDelegate er, string implName, IToken implTok, string requestId, TextWriter tw, int timeLimit) + private static void ReportOutcome(VC.VCGen.Outcome outcome, ErrorReporterDelegate er, string implName, IToken implTok, string requestId, TextWriter tw, int timeLimit, List errors) { ErrorInformation errorInfo = null; @@ -1476,6 +1476,35 @@ namespace Microsoft.Boogie if (implName != null && implTok != null) { errorInfo = errorInformationFactory.CreateErrorInformation(implTok, string.Format("Verification timed out after {0} seconds ({1})", timeLimit, implName), requestId); + + // Report timed out assertions as auxiliary info. + if (errors != null) + { + var cmpr = new CounterexampleComparer(); + var timedOutAssertions = errors.Where(e => e.IsAuxiliaryCexForDiagnosingTimeouts).Distinct(cmpr).ToList(); + timedOutAssertions.Sort(cmpr); + int idx = 1; + foreach (Counterexample error in timedOutAssertions) + { + var callError = error as CallCounterexample; + var returnError = error as ReturnCounterexample; + var assertError = error as AssertCounterexample; + IToken tok = null; + if (callError != null) + { + tok = callError.FailingCall.tok; + } + else if (returnError != null) + { + tok = returnError.FailingReturn.tok; + } + else + { + tok = assertError.FailingAssert.tok; + } + errorInfo.AddAuxInfo(tok, string.Format("unverified assertion due to timeout ({0} of {1})", idx++, timedOutAssertions.Count)); + } + } } break; case VCGen.Outcome.OutOfMemory: @@ -1592,6 +1621,10 @@ namespace Microsoft.Boogie errors.Sort(new CounterexampleComparer()); foreach (Counterexample error in errors) { + if (error.IsAuxiliaryCexForDiagnosingTimeouts) + { + continue; + } var errorInfo = CreateErrorInformation(error, outcome); errorInfo.ImplementationName = implName; diff --git a/Source/VCGeneration/ConditionGeneration.cs b/Source/VCGeneration/ConditionGeneration.cs index 1f010757..ae0a1147 100644 --- a/Source/VCGeneration/ConditionGeneration.cs +++ b/Source/VCGeneration/ConditionGeneration.cs @@ -85,6 +85,7 @@ namespace Microsoft.Boogie { public string RequestId; public abstract byte[] Checksum { get; } public byte[] SugaredCmdChecksum; + public bool IsAuxiliaryCexForDiagnosingTimeouts; public Dictionary calleeCounterexamples; @@ -313,7 +314,7 @@ namespace Microsoft.Boogie { public abstract int GetLocation(); } - public class CounterexampleComparer : IComparer { + public class CounterexampleComparer : IComparer, IEqualityComparer { private int Compare(List bs1, List bs2) { @@ -375,6 +376,16 @@ namespace Microsoft.Boogie { } return -1; } + + public bool Equals(Counterexample x, Counterexample y) + { + return Compare(x, y) == 0; + } + + public int GetHashCode(Counterexample obj) + { + return 0; + } } public class AssertCounterexample : Counterexample { diff --git a/Source/VCGeneration/VC.cs b/Source/VCGeneration/VC.cs index 94584027..33e2f928 100644 --- a/Source/VCGeneration/VC.cs +++ b/Source/VCGeneration/VC.cs @@ -2113,6 +2113,7 @@ namespace VC { foreach (var cmd in assertCmds) { Counterexample cex = AssertCmdToCounterexample(cmd.Item1, cmd.Item2 , new List(), null, null, context); + cex.IsAuxiliaryCexForDiagnosingTimeouts = true; callback.OnCounterexample(cex, msg); } } -- cgit v1.2.3 From c1b06a908ab1ed746672dd42eaf9417916a297cc Mon Sep 17 00:00:00 2001 From: Shaz Qadeer Date: Thu, 1 Oct 2015 09:40:42 -0700 Subject: another fix requested by Chris verification is performed now for all created layers --- Source/Concurrency/CivlRefinement.cs | 2 +- Source/Concurrency/CivlTypeChecker.cs | 20 -------------------- Source/Concurrency/YieldTypeChecker.cs | 2 +- Test/civl/DeviceCache.bpl.expect | 2 +- Test/civl/FlanaganQadeer.bpl.expect | 2 +- Test/civl/Program1.bpl.expect | 2 +- Test/civl/Program2.bpl.expect | 2 +- Test/civl/Program3.bpl.expect | 2 +- Test/civl/Program4.bpl.expect | 2 +- Test/civl/Program5.bpl.expect | 2 +- Test/civl/StoreBuffer.bpl.expect | 2 +- Test/civl/akash.bpl.expect | 2 +- Test/civl/alloc.bpl.expect | 2 +- Test/civl/bar.bpl.expect | 2 +- Test/civl/chris2.bpl.expect | 2 +- Test/civl/chris6.bpl | 14 ++++++++++++++ Test/civl/chris6.bpl.expect | 5 +++++ Test/civl/civl-paper.bpl.expect | 2 +- Test/civl/foo.bpl.expect | 2 +- Test/civl/ghost.bpl.expect | 2 +- Test/civl/linear-set.bpl.expect | 2 +- Test/civl/linear-set2.bpl.expect | 2 +- Test/civl/lock-introduced.bpl.expect | 2 +- Test/civl/lock.bpl.expect | 2 +- Test/civl/lock2.bpl.expect | 2 +- Test/civl/multiset.bpl.expect | 2 +- Test/civl/new1.bpl.expect | 2 +- Test/civl/one.bpl.expect | 2 +- Test/civl/par-incr.bpl.expect | 2 +- Test/civl/parallel1.bpl.expect | 2 +- Test/civl/parallel2.bpl.expect | 2 +- Test/civl/parallel4.bpl.expect | 2 +- Test/civl/parallel5.bpl.expect | 2 +- Test/civl/perm.bpl.expect | 2 +- Test/civl/t1.bpl.expect | 2 +- Test/civl/termination2.bpl.expect | 2 +- Test/civl/ticket.bpl.expect | 2 +- Test/civl/treiber-stack.bpl.expect | 2 +- Test/civl/wsq.bpl.expect | 2 +- 39 files changed, 55 insertions(+), 56 deletions(-) create mode 100644 Test/civl/chris6.bpl create mode 100644 Test/civl/chris6.bpl.expect diff --git a/Source/Concurrency/CivlRefinement.cs b/Source/Concurrency/CivlRefinement.cs index 43d0f60c..8fc27be9 100644 --- a/Source/Concurrency/CivlRefinement.cs +++ b/Source/Concurrency/CivlRefinement.cs @@ -1212,7 +1212,7 @@ namespace Microsoft.Boogie public static void AddCheckers(LinearTypeChecker linearTypeChecker, CivlTypeChecker civlTypeChecker, List decls) { Program program = linearTypeChecker.program; - foreach (int layerNum in civlTypeChecker.AllImplementedLayerNums) + foreach (int layerNum in civlTypeChecker.AllCreatedLayerNums) { if (CommandLineOptions.Clo.TrustLayersDownto <= layerNum || layerNum <= CommandLineOptions.Clo.TrustLayersUpto) continue; diff --git a/Source/Concurrency/CivlTypeChecker.cs b/Source/Concurrency/CivlTypeChecker.cs index dba7ab4b..5215bc52 100644 --- a/Source/Concurrency/CivlTypeChecker.cs +++ b/Source/Concurrency/CivlTypeChecker.cs @@ -486,26 +486,6 @@ namespace Microsoft.Boogie } } - private HashSet allImplementedLayerNums; - public IEnumerable AllImplementedLayerNums - { - get - { - if (allImplementedLayerNums == null) - { - allImplementedLayerNums = new HashSet(); - foreach (ActionInfo actionInfo in procToActionInfo.Values) - { - if (actionInfo.hasImplementation) - { - allImplementedLayerNums.Add(actionInfo.createdAtLayerNum); - } - } - } - return allImplementedLayerNums; - } - } - private HashSet allCreatedLayerNums; public IEnumerable AllCreatedLayerNums { diff --git a/Source/Concurrency/YieldTypeChecker.cs b/Source/Concurrency/YieldTypeChecker.cs index a69f066d..027e7e83 100644 --- a/Source/Concurrency/YieldTypeChecker.cs +++ b/Source/Concurrency/YieldTypeChecker.cs @@ -141,7 +141,7 @@ namespace Microsoft.Boogie Graph implGraph = Program.GraphFromImpl(impl); implGraph.ComputeLoops(); int specLayerNum = civlTypeChecker.procToActionInfo[impl.Proc].createdAtLayerNum; - foreach (int layerNum in civlTypeChecker.AllImplementedLayerNums) + foreach (int layerNum in civlTypeChecker.AllCreatedLayerNums) { if (layerNum > specLayerNum) continue; YieldTypeChecker executor = new YieldTypeChecker(civlTypeChecker, impl, layerNum, implGraph.Headers); diff --git a/Test/civl/DeviceCache.bpl.expect b/Test/civl/DeviceCache.bpl.expect index c4cf5ccf..129e60e2 100644 --- a/Test/civl/DeviceCache.bpl.expect +++ b/Test/civl/DeviceCache.bpl.expect @@ -1,2 +1,2 @@ -Boogie program verifier finished with 30 verified, 0 errors +Boogie program verifier finished with 39 verified, 0 errors diff --git a/Test/civl/FlanaganQadeer.bpl.expect b/Test/civl/FlanaganQadeer.bpl.expect index 00ddb38b..76a9a2bf 100644 --- a/Test/civl/FlanaganQadeer.bpl.expect +++ b/Test/civl/FlanaganQadeer.bpl.expect @@ -1,2 +1,2 @@ -Boogie program verifier finished with 4 verified, 0 errors +Boogie program verifier finished with 8 verified, 0 errors diff --git a/Test/civl/Program1.bpl.expect b/Test/civl/Program1.bpl.expect index 41374b00..00ddb38b 100644 --- a/Test/civl/Program1.bpl.expect +++ b/Test/civl/Program1.bpl.expect @@ -1,2 +1,2 @@ -Boogie program verifier finished with 2 verified, 0 errors +Boogie program verifier finished with 4 verified, 0 errors diff --git a/Test/civl/Program2.bpl.expect b/Test/civl/Program2.bpl.expect index a9949f2e..9823d44a 100644 --- a/Test/civl/Program2.bpl.expect +++ b/Test/civl/Program2.bpl.expect @@ -1,2 +1,2 @@ -Boogie program verifier finished with 3 verified, 0 errors +Boogie program verifier finished with 6 verified, 0 errors diff --git a/Test/civl/Program3.bpl.expect b/Test/civl/Program3.bpl.expect index a9949f2e..9823d44a 100644 --- a/Test/civl/Program3.bpl.expect +++ b/Test/civl/Program3.bpl.expect @@ -1,2 +1,2 @@ -Boogie program verifier finished with 3 verified, 0 errors +Boogie program verifier finished with 6 verified, 0 errors diff --git a/Test/civl/Program4.bpl.expect b/Test/civl/Program4.bpl.expect index a9949f2e..9823d44a 100644 --- a/Test/civl/Program4.bpl.expect +++ b/Test/civl/Program4.bpl.expect @@ -1,2 +1,2 @@ -Boogie program verifier finished with 3 verified, 0 errors +Boogie program verifier finished with 6 verified, 0 errors diff --git a/Test/civl/Program5.bpl.expect b/Test/civl/Program5.bpl.expect index fde7e712..4bcb1071 100644 --- a/Test/civl/Program5.bpl.expect +++ b/Test/civl/Program5.bpl.expect @@ -1,2 +1,2 @@ -Boogie program verifier finished with 18 verified, 0 errors +Boogie program verifier finished with 21 verified, 0 errors diff --git a/Test/civl/StoreBuffer.bpl.expect b/Test/civl/StoreBuffer.bpl.expect index 8c74fe2e..1931ffd2 100644 --- a/Test/civl/StoreBuffer.bpl.expect +++ b/Test/civl/StoreBuffer.bpl.expect @@ -1,2 +1,2 @@ -Boogie program verifier finished with 17 verified, 0 errors +Boogie program verifier finished with 27 verified, 0 errors diff --git a/Test/civl/akash.bpl.expect b/Test/civl/akash.bpl.expect index 00ddb38b..76a9a2bf 100644 --- a/Test/civl/akash.bpl.expect +++ b/Test/civl/akash.bpl.expect @@ -1,2 +1,2 @@ -Boogie program verifier finished with 4 verified, 0 errors +Boogie program verifier finished with 8 verified, 0 errors diff --git a/Test/civl/alloc.bpl.expect b/Test/civl/alloc.bpl.expect index f08c6e00..4bcb1071 100644 --- a/Test/civl/alloc.bpl.expect +++ b/Test/civl/alloc.bpl.expect @@ -1,2 +1,2 @@ -Boogie program verifier finished with 12 verified, 0 errors +Boogie program verifier finished with 21 verified, 0 errors diff --git a/Test/civl/bar.bpl.expect b/Test/civl/bar.bpl.expect index 810c93bf..be6722fe 100644 --- a/Test/civl/bar.bpl.expect +++ b/Test/civl/bar.bpl.expect @@ -10,4 +10,4 @@ Execution trace: (0,0): anon00 (0,0): inline$Impl_YieldChecker_PC_1$0$L0 -Boogie program verifier finished with 3 verified, 2 errors +Boogie program verifier finished with 8 verified, 2 errors diff --git a/Test/civl/chris2.bpl.expect b/Test/civl/chris2.bpl.expect index ddb8537e..f3b66f4a 100644 --- a/Test/civl/chris2.bpl.expect +++ b/Test/civl/chris2.bpl.expect @@ -15,4 +15,4 @@ Execution trace: Execution trace: (0,0): this_A -Boogie program verifier finished with 1 verified, 4 errors +Boogie program verifier finished with 2 verified, 4 errors diff --git a/Test/civl/chris6.bpl b/Test/civl/chris6.bpl new file mode 100644 index 00000000..a0aecf1e --- /dev/null +++ b/Test/civl/chris6.bpl @@ -0,0 +1,14 @@ +// RUN: %boogie -noinfer -useArrayTheory "%s" > "%t" +// RUN: %diff "%s.expect" "%t" +procedure{:extern}{:yields}{:layer 1,2} P1(); + requires{:layer 1} false; + ensures{:atomic} |{ A: return true; }|; + +procedure{:yields}{:layer 2,3} P2() + ensures{:atomic} |{ A: return true; }|; +{ + assert{:layer 1} false; + yield; + call P1(); + yield; +} diff --git a/Test/civl/chris6.bpl.expect b/Test/civl/chris6.bpl.expect new file mode 100644 index 00000000..229e4e10 --- /dev/null +++ b/Test/civl/chris6.bpl.expect @@ -0,0 +1,5 @@ +chris6.bpl(10,3): Error BP5001: This assertion might not hold. +Execution trace: + chris6.bpl(10,3): anon0 + +Boogie program verifier finished with 1 verified, 1 error diff --git a/Test/civl/civl-paper.bpl.expect b/Test/civl/civl-paper.bpl.expect index 11d204a8..bd1df2f9 100644 --- a/Test/civl/civl-paper.bpl.expect +++ b/Test/civl/civl-paper.bpl.expect @@ -1,2 +1,2 @@ -Boogie program verifier finished with 35 verified, 0 errors +Boogie program verifier finished with 45 verified, 0 errors diff --git a/Test/civl/foo.bpl.expect b/Test/civl/foo.bpl.expect index 41e30691..44a93860 100644 --- a/Test/civl/foo.bpl.expect +++ b/Test/civl/foo.bpl.expect @@ -5,4 +5,4 @@ Execution trace: foo.bpl(14,3): inline$Incr_1$0$A (0,0): inline$Impl_YieldChecker_PC_1$0$L0 -Boogie program verifier finished with 4 verified, 1 error +Boogie program verifier finished with 9 verified, 1 error diff --git a/Test/civl/ghost.bpl.expect b/Test/civl/ghost.bpl.expect index 9823d44a..76a9a2bf 100644 --- a/Test/civl/ghost.bpl.expect +++ b/Test/civl/ghost.bpl.expect @@ -1,2 +1,2 @@ -Boogie program verifier finished with 6 verified, 0 errors +Boogie program verifier finished with 8 verified, 0 errors diff --git a/Test/civl/linear-set.bpl.expect b/Test/civl/linear-set.bpl.expect index 00ddb38b..76a9a2bf 100644 --- a/Test/civl/linear-set.bpl.expect +++ b/Test/civl/linear-set.bpl.expect @@ -1,2 +1,2 @@ -Boogie program verifier finished with 4 verified, 0 errors +Boogie program verifier finished with 8 verified, 0 errors diff --git a/Test/civl/linear-set2.bpl.expect b/Test/civl/linear-set2.bpl.expect index 00ddb38b..76a9a2bf 100644 --- a/Test/civl/linear-set2.bpl.expect +++ b/Test/civl/linear-set2.bpl.expect @@ -1,2 +1,2 @@ -Boogie program verifier finished with 4 verified, 0 errors +Boogie program verifier finished with 8 verified, 0 errors diff --git a/Test/civl/lock-introduced.bpl.expect b/Test/civl/lock-introduced.bpl.expect index f08c6e00..8c74fe2e 100644 --- a/Test/civl/lock-introduced.bpl.expect +++ b/Test/civl/lock-introduced.bpl.expect @@ -1,2 +1,2 @@ -Boogie program verifier finished with 12 verified, 0 errors +Boogie program verifier finished with 17 verified, 0 errors diff --git a/Test/civl/lock.bpl.expect b/Test/civl/lock.bpl.expect index 3e6d423a..76a9a2bf 100644 --- a/Test/civl/lock.bpl.expect +++ b/Test/civl/lock.bpl.expect @@ -1,2 +1,2 @@ -Boogie program verifier finished with 5 verified, 0 errors +Boogie program verifier finished with 8 verified, 0 errors diff --git a/Test/civl/lock2.bpl.expect b/Test/civl/lock2.bpl.expect index 3e6d423a..76a9a2bf 100644 --- a/Test/civl/lock2.bpl.expect +++ b/Test/civl/lock2.bpl.expect @@ -1,2 +1,2 @@ -Boogie program verifier finished with 5 verified, 0 errors +Boogie program verifier finished with 8 verified, 0 errors diff --git a/Test/civl/multiset.bpl.expect b/Test/civl/multiset.bpl.expect index 0a77c517..63682bb4 100644 --- a/Test/civl/multiset.bpl.expect +++ b/Test/civl/multiset.bpl.expect @@ -1,2 +1,2 @@ -Boogie program verifier finished with 78 verified, 0 errors +Boogie program verifier finished with 85 verified, 0 errors diff --git a/Test/civl/new1.bpl.expect b/Test/civl/new1.bpl.expect index 41374b00..00ddb38b 100644 --- a/Test/civl/new1.bpl.expect +++ b/Test/civl/new1.bpl.expect @@ -1,2 +1,2 @@ -Boogie program verifier finished with 2 verified, 0 errors +Boogie program verifier finished with 4 verified, 0 errors diff --git a/Test/civl/one.bpl.expect b/Test/civl/one.bpl.expect index 37fad75c..41374b00 100644 --- a/Test/civl/one.bpl.expect +++ b/Test/civl/one.bpl.expect @@ -1,2 +1,2 @@ -Boogie program verifier finished with 1 verified, 0 errors +Boogie program verifier finished with 2 verified, 0 errors diff --git a/Test/civl/par-incr.bpl.expect b/Test/civl/par-incr.bpl.expect index 00ddb38b..3e3dc54b 100644 --- a/Test/civl/par-incr.bpl.expect +++ b/Test/civl/par-incr.bpl.expect @@ -1,2 +1,2 @@ -Boogie program verifier finished with 4 verified, 0 errors +Boogie program verifier finished with 7 verified, 0 errors diff --git a/Test/civl/parallel1.bpl.expect b/Test/civl/parallel1.bpl.expect index 889ee4f2..fa974099 100644 --- a/Test/civl/parallel1.bpl.expect +++ b/Test/civl/parallel1.bpl.expect @@ -5,4 +5,4 @@ Execution trace: parallel1.bpl(14,3): inline$Incr_1$0$A (0,0): inline$Impl_YieldChecker_PC_1$0$L0 -Boogie program verifier finished with 3 verified, 1 error +Boogie program verifier finished with 7 verified, 1 error diff --git a/Test/civl/parallel2.bpl.expect b/Test/civl/parallel2.bpl.expect index 3e6d423a..12041afe 100644 --- a/Test/civl/parallel2.bpl.expect +++ b/Test/civl/parallel2.bpl.expect @@ -1,2 +1,2 @@ -Boogie program verifier finished with 5 verified, 0 errors +Boogie program verifier finished with 10 verified, 0 errors diff --git a/Test/civl/parallel4.bpl.expect b/Test/civl/parallel4.bpl.expect index 2d4c8148..baf228c8 100644 --- a/Test/civl/parallel4.bpl.expect +++ b/Test/civl/parallel4.bpl.expect @@ -3,4 +3,4 @@ Execution trace: parallel4.bpl(29,5): anon0 (0,0): anon01 -Boogie program verifier finished with 3 verified, 1 error +Boogie program verifier finished with 7 verified, 1 error diff --git a/Test/civl/parallel5.bpl.expect b/Test/civl/parallel5.bpl.expect index 3e6d423a..12041afe 100644 --- a/Test/civl/parallel5.bpl.expect +++ b/Test/civl/parallel5.bpl.expect @@ -1,2 +1,2 @@ -Boogie program verifier finished with 5 verified, 0 errors +Boogie program verifier finished with 10 verified, 0 errors diff --git a/Test/civl/perm.bpl.expect b/Test/civl/perm.bpl.expect index 41374b00..00ddb38b 100644 --- a/Test/civl/perm.bpl.expect +++ b/Test/civl/perm.bpl.expect @@ -1,2 +1,2 @@ -Boogie program verifier finished with 2 verified, 0 errors +Boogie program verifier finished with 4 verified, 0 errors diff --git a/Test/civl/t1.bpl.expect b/Test/civl/t1.bpl.expect index fcef7e58..27a208d4 100644 --- a/Test/civl/t1.bpl.expect +++ b/Test/civl/t1.bpl.expect @@ -6,4 +6,4 @@ Execution trace: t1.bpl(25,21): inline$SetG_1$0$A (0,0): inline$Impl_YieldChecker_A_1$0$L1 -Boogie program verifier finished with 4 verified, 1 error +Boogie program verifier finished with 9 verified, 1 error diff --git a/Test/civl/termination2.bpl.expect b/Test/civl/termination2.bpl.expect index 37fad75c..41374b00 100644 --- a/Test/civl/termination2.bpl.expect +++ b/Test/civl/termination2.bpl.expect @@ -1,2 +1,2 @@ -Boogie program verifier finished with 1 verified, 0 errors +Boogie program verifier finished with 2 verified, 0 errors diff --git a/Test/civl/ticket.bpl.expect b/Test/civl/ticket.bpl.expect index b072912b..dc45a0ee 100644 --- a/Test/civl/ticket.bpl.expect +++ b/Test/civl/ticket.bpl.expect @@ -1,2 +1,2 @@ -Boogie program verifier finished with 16 verified, 0 errors +Boogie program verifier finished with 24 verified, 0 errors diff --git a/Test/civl/treiber-stack.bpl.expect b/Test/civl/treiber-stack.bpl.expect index 9823d44a..76a9a2bf 100644 --- a/Test/civl/treiber-stack.bpl.expect +++ b/Test/civl/treiber-stack.bpl.expect @@ -1,2 +1,2 @@ -Boogie program verifier finished with 6 verified, 0 errors +Boogie program verifier finished with 8 verified, 0 errors diff --git a/Test/civl/wsq.bpl.expect b/Test/civl/wsq.bpl.expect index a9949f2e..9823d44a 100644 --- a/Test/civl/wsq.bpl.expect +++ b/Test/civl/wsq.bpl.expect @@ -1,2 +1,2 @@ -Boogie program verifier finished with 3 verified, 0 errors +Boogie program verifier finished with 6 verified, 0 errors -- cgit v1.2.3 From f1d7e9e90e69854f8f33c474f544855abb8508c2 Mon Sep 17 00:00:00 2001 From: Valentin Wüstholz Date: Thu, 1 Oct 2015 22:57:55 +0200 Subject: Improve output for diagnosing timeouts. --- Source/ExecutionEngine/ExecutionEngine.cs | 49 ++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index 4ab0a9c5..9623139a 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -135,7 +135,7 @@ namespace Microsoft.Boogie foreach (var e in errorInfo.Aux) { - if (!(skipExecutionTrace && e.Category.Contains("Execution trace"))) + if (!(skipExecutionTrace && e.Category != null && e.Category.Contains("Execution trace"))) { ReportBplError(e.Tok, e.FullMsg, false, tw); } @@ -1472,37 +1472,67 @@ namespace Microsoft.Boogie case VCGen.Outcome.ReachedBound: tw.WriteLine(string.Format("Stratified Inlining: Reached recursion bound of {0}", CommandLineOptions.Clo.RecursionBound)); break; + case VCGen.Outcome.Errors: case VCGen.Outcome.TimedOut: if (implName != null && implTok != null) { - errorInfo = errorInformationFactory.CreateErrorInformation(implTok, string.Format("Verification timed out after {0} seconds ({1})", timeLimit, implName), requestId); - + if (outcome == ConditionGeneration.Outcome.TimedOut || (errors != null && errors.Any(e => e.IsAuxiliaryCexForDiagnosingTimeouts))) + { + errorInfo = errorInformationFactory.CreateErrorInformation(implTok, string.Format("Verification of '{1}' timed out after {0} seconds", timeLimit, implName), requestId); + } + // Report timed out assertions as auxiliary info. if (errors != null) { var cmpr = new CounterexampleComparer(); var timedOutAssertions = errors.Where(e => e.IsAuxiliaryCexForDiagnosingTimeouts).Distinct(cmpr).ToList(); timedOutAssertions.Sort(cmpr); - int idx = 1; + if (0 < timedOutAssertions.Count) + { + errorInfo.Msg += string.Format(" with {0} check(s) that timed out individually", timedOutAssertions.Count); + } foreach (Counterexample error in timedOutAssertions) { var callError = error as CallCounterexample; var returnError = error as ReturnCounterexample; var assertError = error as AssertCounterexample; IToken tok = null; + string msg = null; if (callError != null) { tok = callError.FailingCall.tok; + msg = callError.FailingCall.ErrorData as string ?? "A precondition for this call might not hold."; } else if (returnError != null) { tok = returnError.FailingReturn.tok; + msg = "A postcondition might not hold on this return path."; } else { tok = assertError.FailingAssert.tok; + if (assertError.FailingAssert is LoopInitAssertCmd) + { + msg = "This loop invariant might not hold on entry."; + } + else if (assertError.FailingAssert is LoopInvMaintainedAssertCmd) + { + msg = "This loop invariant might not be maintained by the loop."; + } + else + { + msg = assertError.FailingAssert.ErrorData as string; + if (!CommandLineOptions.Clo.ForceBplErrors && assertError.FailingAssert.ErrorMessage != null) + { + msg = assertError.FailingAssert.ErrorMessage; + } + if (msg == null) + { + msg = "This assertion might not hold."; + } + } } - errorInfo.AddAuxInfo(tok, string.Format("unverified assertion due to timeout ({0} of {1})", idx++, timedOutAssertions.Count)); + errorInfo.AddAuxInfo(tok, msg, "Unverified check due to timeout"); } } } @@ -1531,6 +1561,10 @@ namespace Microsoft.Boogie er(errorInfo); } } + else + { + printer.WriteErrorInformation(errorInfo, tw); + } } } @@ -1604,8 +1638,9 @@ namespace Microsoft.Boogie } else { - Interlocked.Add(ref stats.ErrorCount, errors.Count); - if (wasCached) { Interlocked.Add(ref stats.CachedErrorCount, errors.Count); } + int cnt = errors.Where(e => !e.IsAuxiliaryCexForDiagnosingTimeouts).Count(); + Interlocked.Add(ref stats.ErrorCount, cnt); + if (wasCached) { Interlocked.Add(ref stats.CachedErrorCount, cnt); } } break; } -- cgit v1.2.3 From 0569be4268fe9c6174ff14cd0e9ab1a8170cfd21 Mon Sep 17 00:00:00 2001 From: Shaz Qadeer Date: Thu, 1 Oct 2015 17:34:53 -0700 Subject: added a fix to check all layers: created layer of actions or layers in requires, ensures, or asserts --- Source/Concurrency/CivlRefinement.cs | 2 +- Source/Concurrency/CivlTypeChecker.cs | 31 +++++++++++++------------------ Source/Concurrency/YieldTypeChecker.cs | 2 +- 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/Source/Concurrency/CivlRefinement.cs b/Source/Concurrency/CivlRefinement.cs index 8fc27be9..dfe50ef9 100644 --- a/Source/Concurrency/CivlRefinement.cs +++ b/Source/Concurrency/CivlRefinement.cs @@ -1212,7 +1212,7 @@ namespace Microsoft.Boogie public static void AddCheckers(LinearTypeChecker linearTypeChecker, CivlTypeChecker civlTypeChecker, List decls) { Program program = linearTypeChecker.program; - foreach (int layerNum in civlTypeChecker.AllCreatedLayerNums) + foreach (int layerNum in civlTypeChecker.AllLayerNums) { if (CommandLineOptions.Clo.TrustLayersDownto <= layerNum || layerNum <= CommandLineOptions.Clo.TrustLayersUpto) continue; diff --git a/Source/Concurrency/CivlTypeChecker.cs b/Source/Concurrency/CivlTypeChecker.cs index 5215bc52..430c0d2c 100644 --- a/Source/Concurrency/CivlTypeChecker.cs +++ b/Source/Concurrency/CivlTypeChecker.cs @@ -486,20 +486,27 @@ namespace Microsoft.Boogie } } - private HashSet allCreatedLayerNums; - public IEnumerable AllCreatedLayerNums + private HashSet allLayerNums; + public IEnumerable AllLayerNums { get { - if (allCreatedLayerNums == null) + if (allLayerNums == null) { - allCreatedLayerNums = new HashSet(); + allLayerNums = new HashSet(); foreach (ActionInfo actionInfo in procToActionInfo.Values) { - allCreatedLayerNums.Add(actionInfo.createdAtLayerNum); + allLayerNums.Add(actionInfo.createdAtLayerNum); + } + foreach (var layerNums in absyToLayerNums.Values) + { + foreach (var layer in layerNums) + { + allLayerNums.Add(layer); + } } } - return allCreatedLayerNums; + return allLayerNums; } } @@ -705,18 +712,6 @@ namespace Microsoft.Boogie Error(impl.Proc, "Extern procedure cannot have an implementation"); } } - foreach (var g in this.globalVarToSharedVarInfo.Keys) - { - var info = globalVarToSharedVarInfo[g]; - if (!this.AllCreatedLayerNums.Contains(info.introLayerNum)) - { - Error(g, "Variable must be introduced with creation of some atomic action"); - } - if (info.hideLayerNum != int.MaxValue && !this.AllCreatedLayerNums.Contains(info.hideLayerNum)) - { - Error(g, "Variable must be hidden with creation of some atomic action"); - } - } if (errorCount > 0) return; this.VisitProgram(program); if (errorCount > 0) return; diff --git a/Source/Concurrency/YieldTypeChecker.cs b/Source/Concurrency/YieldTypeChecker.cs index 027e7e83..ed59d3ad 100644 --- a/Source/Concurrency/YieldTypeChecker.cs +++ b/Source/Concurrency/YieldTypeChecker.cs @@ -141,7 +141,7 @@ namespace Microsoft.Boogie Graph implGraph = Program.GraphFromImpl(impl); implGraph.ComputeLoops(); int specLayerNum = civlTypeChecker.procToActionInfo[impl.Proc].createdAtLayerNum; - foreach (int layerNum in civlTypeChecker.AllCreatedLayerNums) + foreach (int layerNum in civlTypeChecker.AllLayerNums) { if (layerNum > specLayerNum) continue; YieldTypeChecker executor = new YieldTypeChecker(civlTypeChecker, impl, layerNum, implGraph.Headers); -- cgit v1.2.3 From cbbced423ed4887ea78628066bc57c4aa36997d1 Mon Sep 17 00:00:00 2001 From: Shaz Qadeer Date: Thu, 8 Oct 2015 20:53:09 -0700 Subject: removed an extraneous warning --- Source/Concurrency/CivlTypeChecker.cs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/Source/Concurrency/CivlTypeChecker.cs b/Source/Concurrency/CivlTypeChecker.cs index 430c0d2c..b0ea678c 100644 --- a/Source/Concurrency/CivlTypeChecker.cs +++ b/Source/Concurrency/CivlTypeChecker.cs @@ -874,16 +874,9 @@ namespace Microsoft.Boogie if (node.Proc.Modifies.Count > 0) { var atomicProcedureInfo = procToAtomicProcedureInfo[node.Proc]; - if (procToActionInfo[enclosingImpl.Proc] is AtomicActionInfo) + if (!atomicProcedureInfo.layers.Contains(enclosingProcLayerNum)) { - if (!atomicProcedureInfo.layers.Contains(enclosingProcLayerNum)) - { - Error(node, "The layer of called atomic procedure must be identical to the creation layer of callee"); - } - } - else - { - Error(node, "Enclosing implementation must refine an atomic action"); + Error(node, "The layer of called atomic procedure must be identical to the creation layer of callee"); } introducedLocalVarsUpperBound = enclosingProcLayerNum; } -- cgit v1.2.3 From bad6c014fdf57c5674a840b32047c7db54cd7aba Mon Sep 17 00:00:00 2001 From: Shaz Qadeer Date: Fri, 9 Oct 2015 14:58:15 -0700 Subject: bug fix --- Source/Concurrency/CivlTypeChecker.cs | 54 +++++++++++++++++++++++------------ Test/civl/chris7.bpl | 14 +++++++++ Test/civl/chris7.bpl.expect | 2 ++ 3 files changed, 52 insertions(+), 18 deletions(-) create mode 100644 Test/civl/chris7.bpl create mode 100644 Test/civl/chris7.bpl.expect diff --git a/Source/Concurrency/CivlTypeChecker.cs b/Source/Concurrency/CivlTypeChecker.cs index b0ea678c..71feb927 100644 --- a/Source/Concurrency/CivlTypeChecker.cs +++ b/Source/Concurrency/CivlTypeChecker.cs @@ -675,6 +675,36 @@ namespace Microsoft.Boogie } } if (errorCount > 0) return; + + foreach (var impl in program.Implementations) + { + if (!procToActionInfo.ContainsKey(impl.Proc)) continue; + ActionInfo actionInfo = procToActionInfo[impl.Proc]; + procToActionInfo[impl.Proc].hasImplementation = true; + if (actionInfo.isExtern) + { + Error(impl.Proc, "Extern procedure cannot have an implementation"); + } + } + if (errorCount > 0) return; + + foreach (Procedure proc in procToActionInfo.Keys) + { + for (int i = 0; i < proc.InParams.Count; i++) + { + Variable v = proc.InParams[i]; + var layer = FindLocalVariableLayer(proc, v, procToActionInfo[proc].createdAtLayerNum); + if (layer == int.MinValue) continue; + localVarToLocalVariableInfo[v] = new LocalVariableInfo(false, layer); + } + for (int i = 0; i < proc.OutParams.Count; i++) + { + Variable v = proc.OutParams[i]; + var layer = FindLocalVariableLayer(proc, v, procToActionInfo[proc].createdAtLayerNum); + if (layer == int.MinValue) continue; + localVarToLocalVariableInfo[v] = new LocalVariableInfo(false, layer); + } + } foreach (Implementation node in program.Implementations) { if (!procToActionInfo.ContainsKey(node.Proc)) continue; @@ -687,32 +717,20 @@ namespace Microsoft.Boogie for (int i = 0; i < node.Proc.InParams.Count; i++) { Variable v = node.Proc.InParams[i]; - var layer = FindLocalVariableLayer(node.Proc, v, procToActionInfo[node.Proc].createdAtLayerNum); - if (layer == int.MinValue) continue; - localVarToLocalVariableInfo[v] = new LocalVariableInfo(false, layer); + if (!localVarToLocalVariableInfo.ContainsKey(v)) continue; + var layer = localVarToLocalVariableInfo[v].layer; localVarToLocalVariableInfo[node.InParams[i]] = new LocalVariableInfo(false, layer); } for (int i = 0; i < node.Proc.OutParams.Count; i++) { Variable v = node.Proc.OutParams[i]; - var layer = FindLocalVariableLayer(node.Proc, v, procToActionInfo[node.Proc].createdAtLayerNum); - if (layer == int.MinValue) continue; - localVarToLocalVariableInfo[v] = new LocalVariableInfo(false, layer); + if (!localVarToLocalVariableInfo.ContainsKey(v)) continue; + var layer = localVarToLocalVariableInfo[v].layer; localVarToLocalVariableInfo[node.OutParams[i]] = new LocalVariableInfo(false, layer); } } - if (errorCount > 0) return; - foreach (var impl in program.Implementations) - { - if (!procToActionInfo.ContainsKey(impl.Proc)) continue; - ActionInfo actionInfo = procToActionInfo[impl.Proc]; - procToActionInfo[impl.Proc].hasImplementation = true; - if (actionInfo.isExtern) - { - Error(impl.Proc, "Extern procedure cannot have an implementation"); - } - } - if (errorCount > 0) return; + if (errorCount > 0) return; + this.VisitProgram(program); if (errorCount > 0) return; YieldTypeChecker.PerformYieldSafeCheck(this); diff --git a/Test/civl/chris7.bpl b/Test/civl/chris7.bpl new file mode 100644 index 00000000..a8fd25d3 --- /dev/null +++ b/Test/civl/chris7.bpl @@ -0,0 +1,14 @@ +// RUN: %boogie -noinfer -useArrayTheory "%s" > "%t" +// RUN: %diff "%s.expect" "%t" +procedure{:layer 1}{:extern} P() returns(i:int); + +procedure{:yields}{:layer 1,1}{:extern} Y({:layer 1}x:int); + +procedure{:yields}{:layer 1,2} A({:layer 1}y:int) + ensures {:atomic} |{ A: return true; }|; +{ + var{:layer 1} tmp:int; + call Y(y); + call tmp := P(); + call Y(tmp); +} diff --git a/Test/civl/chris7.bpl.expect b/Test/civl/chris7.bpl.expect new file mode 100644 index 00000000..37fad75c --- /dev/null +++ b/Test/civl/chris7.bpl.expect @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 1 verified, 0 errors -- cgit v1.2.3 From a6b78b0ea28c22744fa846d7729b5c50247f9987 Mon Sep 17 00:00:00 2001 From: Shaz Qadeer Date: Fri, 16 Oct 2015 14:08:21 -0700 Subject: bug fix in the type checking of calls to atomic procedures --- Source/Concurrency/CivlRefinement.cs | 14 +-- Source/Concurrency/CivlTypeChecker.cs | 219 ++++++++++++++++++---------------- Test/civl/chris8.bpl | 15 +++ Test/civl/chris8.bpl.expect | 2 + Test/civl/wsq.bpl | 14 +-- 5 files changed, 139 insertions(+), 125 deletions(-) create mode 100644 Test/civl/chris8.bpl create mode 100644 Test/civl/chris8.bpl.expect diff --git a/Source/Concurrency/CivlRefinement.cs b/Source/Concurrency/CivlRefinement.cs index dfe50ef9..75ff2358 100644 --- a/Source/Concurrency/CivlRefinement.cs +++ b/Source/Concurrency/CivlRefinement.cs @@ -315,19 +315,9 @@ namespace Microsoft.Boogie foreach (var v in civlTypeChecker.localVarToLocalVariableInfo.Keys) { LocalVariableInfo info = civlTypeChecker.localVarToLocalVariableInfo[v]; - if (info.isGhost) + if (layerNum < info.layer) { - if (info.layer != layerNum) - { - availableVars.Remove(v); - } - } - else - { - if (layerNum < info.layer) - { - availableVars.Remove(v); - } + availableVars.Remove(v); } } return availableVars; diff --git a/Source/Concurrency/CivlTypeChecker.cs b/Source/Concurrency/CivlTypeChecker.cs index 71feb927..b426d9ed 100644 --- a/Source/Concurrency/CivlTypeChecker.cs +++ b/Source/Concurrency/CivlTypeChecker.cs @@ -333,26 +333,24 @@ namespace Microsoft.Boogie public class AtomicProcedureInfo { public bool isPure; - public HashSet layers; + public LayerRange layerRange; public AtomicProcedureInfo() { this.isPure = true; - this.layers = null; + this.layerRange = null; } - public AtomicProcedureInfo(HashSet layers) + public AtomicProcedureInfo(LayerRange layerRange) { this.isPure = false; - this.layers = layers; + this.layerRange = layerRange; } } public class LocalVariableInfo { - public bool isGhost; public int layer; - public LocalVariableInfo(bool isGhost, int layer) + public LocalVariableInfo(int layer) { - this.isGhost = isGhost; this.layer = layer; } } @@ -364,7 +362,6 @@ namespace Microsoft.Boogie Implementation enclosingImpl; HashSet sharedVarsAccessed; int introducedLocalVarsUpperBound; - int ghostVarIntroLayerAllowed; public Program program; public int errorCount; @@ -373,28 +370,15 @@ namespace Microsoft.Boogie public Dictionary procToAtomicProcedureInfo; public Dictionary> absyToLayerNums; public Dictionary localVarToLocalVariableInfo; + Dictionary pureCallLayer; public bool CallExists(CallCmd callCmd, int enclosingProcLayerNum, int layerNum) { Debug.Assert(procToAtomicProcedureInfo.ContainsKey(callCmd.Proc)); var atomicProcedureInfo = procToAtomicProcedureInfo[callCmd.Proc]; - if (callCmd.Proc.Modifies.Count > 0) - { - return enclosingProcLayerNum == layerNum; - } - if (callCmd.Outs.Count == 0) - { - return true; - } - var outputVar = callCmd.Outs[0].Decl; - var localVariableInfo = localVarToLocalVariableInfo[outputVar]; - if (localVariableInfo.isGhost) - { - return localVariableInfo.layer == layerNum; - } if (atomicProcedureInfo.isPure) { - return localVariableInfo.layer <= layerNum; + return pureCallLayer[callCmd] <= layerNum; } else { @@ -456,13 +440,13 @@ namespace Microsoft.Boogie this.enclosingImpl = null; this.sharedVarsAccessed = null; this.introducedLocalVarsUpperBound = int.MinValue; - this.ghostVarIntroLayerAllowed = int.MinValue; this.localVarToLocalVariableInfo = new Dictionary(); this.absyToLayerNums = new Dictionary>(); this.globalVarToSharedVarInfo = new Dictionary(); this.procToActionInfo = new Dictionary(); - this.procToAtomicProcedureInfo = new Dictionary(); + this.procToAtomicProcedureInfo = new Dictionary(); + this.pureCallLayer = new Dictionary(); foreach (var g in program.GlobalVariables) { @@ -553,27 +537,39 @@ namespace Microsoft.Boogie foreach (var proc in program.Procedures) { if (QKeyValue.FindBoolAttribute(proc.Attributes, "yields")) continue; - var procLayerNums = RemoveDuplicatesAndSort(FindLayers(proc.Attributes)); + var procLayerNums = FindLayers(proc.Attributes); if (procLayerNums.Count == 0) continue; foreach (IdentifierExpr ie in proc.Modifies) { if (!globalVarToSharedVarInfo.ContainsKey(ie.Decl)) { Error(proc, "Atomic procedure cannot modify a global variable without layer numbers"); + continue; } - else if (globalVarToSharedVarInfo[ie.Decl].introLayerNum != procLayerNums[0]) - { - Error(proc, "The introduction layer of a modified global variable must be identical to the layer of the atomic procedure"); - } } - if (proc.Modifies.Count == 0 || procLayerNums.Count == 1) + int lower, upper; + if (procLayerNums.Count == 1) + { + lower = procLayerNums[0]; + upper = procLayerNums[0]; + } + else if (procLayerNums.Count == 2) { - procToAtomicProcedureInfo[proc] = new AtomicProcedureInfo(new HashSet(procLayerNums)); + lower = procLayerNums[0]; + upper = procLayerNums[1]; + if (lower >= upper) + { + Error(proc, "Lower layer must be less than upper layer"); + continue; + } } else { - Error(proc, "An atomic procedure with more than one layer must not modify a global variable"); + Error(proc, "Atomic procedure must specify a layer range"); + continue; } + LayerRange layerRange = new LayerRange(lower, upper); + procToAtomicProcedureInfo[proc] = new AtomicProcedureInfo(layerRange); } if (errorCount > 0) return; @@ -592,7 +588,7 @@ namespace Microsoft.Boogie this.sharedVarsAccessed = new HashSet(); (new PurityChecker(this)).VisitImplementation(impl); LayerRange upperBound = FindLayerRange(); - LayerRange lowerBound = new LayerRange(atomicProcedureInfo.layers); + LayerRange lowerBound = atomicProcedureInfo.layerRange; if (!lowerBound.Subset(upperBound)) { Error(impl, "Atomic procedure cannot access global variable"); @@ -695,14 +691,14 @@ namespace Microsoft.Boogie Variable v = proc.InParams[i]; var layer = FindLocalVariableLayer(proc, v, procToActionInfo[proc].createdAtLayerNum); if (layer == int.MinValue) continue; - localVarToLocalVariableInfo[v] = new LocalVariableInfo(false, layer); + localVarToLocalVariableInfo[v] = new LocalVariableInfo(layer); } for (int i = 0; i < proc.OutParams.Count; i++) { Variable v = proc.OutParams[i]; var layer = FindLocalVariableLayer(proc, v, procToActionInfo[proc].createdAtLayerNum); if (layer == int.MinValue) continue; - localVarToLocalVariableInfo[v] = new LocalVariableInfo(false, layer); + localVarToLocalVariableInfo[v] = new LocalVariableInfo(layer); } } foreach (Implementation node in program.Implementations) @@ -712,21 +708,21 @@ namespace Microsoft.Boogie { var layer = FindLocalVariableLayer(node, v, procToActionInfo[node.Proc].createdAtLayerNum); if (layer == int.MinValue) continue; - localVarToLocalVariableInfo[v] = new LocalVariableInfo(QKeyValue.FindBoolAttribute(node.Attributes, "ghost"), layer); + localVarToLocalVariableInfo[v] = new LocalVariableInfo(layer); } for (int i = 0; i < node.Proc.InParams.Count; i++) { Variable v = node.Proc.InParams[i]; if (!localVarToLocalVariableInfo.ContainsKey(v)) continue; var layer = localVarToLocalVariableInfo[v].layer; - localVarToLocalVariableInfo[node.InParams[i]] = new LocalVariableInfo(false, layer); + localVarToLocalVariableInfo[node.InParams[i]] = new LocalVariableInfo(layer); } for (int i = 0; i < node.Proc.OutParams.Count; i++) { Variable v = node.Proc.OutParams[i]; if (!localVarToLocalVariableInfo.ContainsKey(v)) continue; var layer = localVarToLocalVariableInfo[v].layer; - localVarToLocalVariableInfo[node.OutParams[i]] = new LocalVariableInfo(false, layer); + localVarToLocalVariableInfo[node.OutParams[i]] = new LocalVariableInfo(layer); } } if (errorCount > 0) return; @@ -818,13 +814,17 @@ namespace Microsoft.Boogie } for (int i = 0; i < node.Ins.Count; i++) { - var formal = node.Proc.InParams[i]; - if (localVarToLocalVariableInfo.ContainsKey(formal)) + Visit(node.Ins[i]); + if (introducedLocalVarsUpperBound != int.MinValue) { - introducedLocalVarsUpperBound = localVarToLocalVariableInfo[formal].layer; + var formal = node.Proc.InParams[i]; + if (!localVarToLocalVariableInfo.ContainsKey(formal) || + introducedLocalVarsUpperBound > localVarToLocalVariableInfo[formal].layer) + { + Error(node, "An introduced local variable is accessed but not available"); + } + introducedLocalVarsUpperBound = int.MinValue; } - Visit(node.Ins[i]); - introducedLocalVarsUpperBound = int.MinValue; } for (int i = 0; i < node.Outs.Count; i++) { @@ -840,69 +840,83 @@ namespace Microsoft.Boogie } else if (procToAtomicProcedureInfo.ContainsKey(node.Proc)) { - // 1. Outputs are either all ghost or all introduced. - // 2. All outputs have the same layer; call it output layer. - // 3. If callee is impure and has outputs, output layer is a member of layer set of callee. - // 4. If callee is impure and has introduced outputs, then creation number of caller belongs to layer set of callee. - // 5. If callee is impure and modifies globals, then creation number of caller belongs to layer set of callee. - - Debug.Assert(introducedLocalVarsUpperBound == int.MinValue); - bool isGhost = false; // This assignment stops the compiler from complaining. - // In the absence of errors, isGhost is initialized by loop below. - foreach (var ie in node.Outs) + var atomicProcedureInfo = procToAtomicProcedureInfo[node.Proc]; + if (atomicProcedureInfo.isPure) { - if (localVarToLocalVariableInfo.ContainsKey(ie.Decl)) + if (node.Outs.Count > 0) { - var localVariableInfo = localVarToLocalVariableInfo[ie.Decl]; - if (introducedLocalVarsUpperBound == int.MinValue) + int inferredLayer = int.MinValue; + foreach (var ie in node.Outs) { - introducedLocalVarsUpperBound = localVariableInfo.layer; - isGhost = localVariableInfo.isGhost; - var atomicProcedureInfo = procToAtomicProcedureInfo[node.Proc]; - if (!atomicProcedureInfo.isPure) + if (!localVarToLocalVariableInfo.ContainsKey(ie.Decl)) continue; + if (inferredLayer < localVarToLocalVariableInfo[ie.Decl].layer) { - if (!atomicProcedureInfo.layers.Contains(introducedLocalVarsUpperBound)) + inferredLayer = localVarToLocalVariableInfo[ie.Decl].layer; + } + } + pureCallLayer[node] = inferredLayer; + if (inferredLayer != int.MinValue) + { + foreach (var ie in node.Outs) + { + if (!localVarToLocalVariableInfo.ContainsKey(ie.Decl)) { - Error(node, "Layer of output variable must be a layer of the callee"); + Error(node, "Output variable must be introduced"); } - if (!isGhost && !atomicProcedureInfo.layers.Contains(enclosingProcLayerNum)) + else if (inferredLayer != localVarToLocalVariableInfo[ie.Decl].layer) { - Error(node, "The creation layer of caller must be a layer of the callee"); + Error(node, "All output variables must be introduced at the same layer"); } } } - else + Debug.Assert(introducedLocalVarsUpperBound == int.MinValue); + foreach (var e in node.Ins) { - if (localVariableInfo.layer != introducedLocalVarsUpperBound) + Visit(e); + if (inferredLayer < introducedLocalVarsUpperBound) { - Error(node, "All outputs must have the same layer"); - } - if (localVariableInfo.isGhost != isGhost) - { - Error(node, "Outputs are either all ghost or all introduced"); + Error(node, "An introduced local variable is not accessible"); } + introducedLocalVarsUpperBound = int.MinValue; } } else { - Error(node, "Output variable must be a ghost or introduced local variable"); + Debug.Assert(introducedLocalVarsUpperBound == int.MinValue); + int inferredLayer = int.MinValue; + foreach (var e in node.Ins) + { + Visit(e); + if (inferredLayer < introducedLocalVarsUpperBound) + { + inferredLayer = introducedLocalVarsUpperBound; + } + introducedLocalVarsUpperBound = int.MinValue; + } + pureCallLayer[node] = inferredLayer; } } - - if (node.Proc.Modifies.Count > 0) + else { - var atomicProcedureInfo = procToAtomicProcedureInfo[node.Proc]; - if (!atomicProcedureInfo.layers.Contains(enclosingProcLayerNum)) + if (enclosingProcLayerNum != atomicProcedureInfo.layerRange.upperLayerNum) { - Error(node, "The layer of called atomic procedure must be identical to the creation layer of callee"); + Error(node, "Creation layer of caller must be the upper bound of the layer range of callee"); + } + foreach (var ie in node.Proc.Modifies) + { + if (enclosingProcLayerNum != globalVarToSharedVarInfo[ie.Decl].introLayerNum) + { + Error(node, "Creation layer of caller must be identical to the introduction layer of modified variable"); + } + } + foreach (var ie in node.Outs) + { + if (localVarToLocalVariableInfo.ContainsKey(ie.Decl) && + enclosingProcLayerNum == localVarToLocalVariableInfo[ie.Decl].layer) + continue; + Error(node, "Output variable must be introduced at the creation layer of caller"); } - introducedLocalVarsUpperBound = enclosingProcLayerNum; - } - foreach (var e in node.Ins) - { - Visit(e); } - introducedLocalVarsUpperBound = int.MinValue; return node; } else @@ -973,19 +987,9 @@ namespace Microsoft.Boogie else if ((node.Decl is Formal || node.Decl is Variable) && localVarToLocalVariableInfo.ContainsKey(node.Decl)) { var localVariableInfo = localVarToLocalVariableInfo[node.Decl]; - if (localVariableInfo.isGhost) + if (introducedLocalVarsUpperBound < localVariableInfo.layer) { - if (ghostVarIntroLayerAllowed != localVariableInfo.layer) - { - Error(node, "Ghost variable inaccessible"); - } - } - else - { - if (introducedLocalVarsUpperBound < localVariableInfo.layer) - { - Error(node, "Introduced variable inaccessible"); - } + introducedLocalVarsUpperBound = localVariableInfo.layer; } } return base.VisitIdentifierExpr(node); @@ -1003,9 +1007,12 @@ namespace Microsoft.Boogie { sharedVarsAccessed = new HashSet(); Debug.Assert(introducedLocalVarsUpperBound == int.MinValue); - introducedLocalVarsUpperBound = Least(FindLayers(ensures.Attributes)); base.VisitEnsures(ensures); CheckAndAddLayers(ensures, ensures.Attributes, actionInfo.createdAtLayerNum); + if (introducedLocalVarsUpperBound > Least(FindLayers(ensures.Attributes))) + { + Error(ensures, "An introduced local variable is accessed but not available"); + } introducedLocalVarsUpperBound = int.MinValue; sharedVarsAccessed = null; } @@ -1016,9 +1023,12 @@ namespace Microsoft.Boogie { sharedVarsAccessed = new HashSet(); Debug.Assert(introducedLocalVarsUpperBound == int.MinValue); - introducedLocalVarsUpperBound = Least(FindLayers(requires.Attributes)); base.VisitRequires(requires); CheckAndAddLayers(requires, requires.Attributes, procToActionInfo[enclosingProc].createdAtLayerNum); + if (introducedLocalVarsUpperBound > Least(FindLayers(requires.Attributes))) + { + Error(requires, "An introduced local variable is accessed but not available"); + } introducedLocalVarsUpperBound = int.MinValue; sharedVarsAccessed = null; return requires; @@ -1033,16 +1043,13 @@ namespace Microsoft.Boogie } sharedVarsAccessed = new HashSet(); Debug.Assert(introducedLocalVarsUpperBound == int.MinValue); - var layerNums = FindLayers(node.Attributes); - introducedLocalVarsUpperBound = Least(layerNums); - if (layerNums.Count == 1) - { - ghostVarIntroLayerAllowed = layerNums[0]; - } base.VisitAssertCmd(node); CheckAndAddLayers(node, node.Attributes, procToActionInfo[enclosingImpl.Proc].createdAtLayerNum); + if (introducedLocalVarsUpperBound > Least(FindLayers(node.Attributes))) + { + Error(node, "An introduced local variable is accessed but not available"); + } introducedLocalVarsUpperBound = int.MinValue; - ghostVarIntroLayerAllowed = int.MinValue; sharedVarsAccessed = null; return node; } @@ -1115,7 +1122,7 @@ namespace Microsoft.Boogie { civlTypeChecker.Error(node, "Pure procedure can only call pure procedures"); } - else if (!callerInfo.layers.IsSubsetOf(calleeInfo.layers)) + else if (!callerInfo.layerRange.Subset(calleeInfo.layerRange)) { civlTypeChecker.Error(node, "Caller layers must be subset of callee layers"); } diff --git a/Test/civl/chris8.bpl b/Test/civl/chris8.bpl new file mode 100644 index 00000000..070cfec4 --- /dev/null +++ b/Test/civl/chris8.bpl @@ -0,0 +1,15 @@ +// RUN: %boogie -noinfer -useArrayTheory "%s" > "%t" +// RUN: %diff "%s.expect" "%t" +var{:layer 1,1} x:int; + +procedure{:layer 1}{:extern} P1(i:int); +procedure{:pure}{:extern} P2(j:int); + +procedure{:yields}{:layer 1,2} A1({:layer 1}i:int) + ensures {:atomic} |{ A: return true; }|; +{ + yield; + call P1(i); + call P2(i); + yield; +} diff --git a/Test/civl/chris8.bpl.expect b/Test/civl/chris8.bpl.expect new file mode 100644 index 00000000..37fad75c --- /dev/null +++ b/Test/civl/chris8.bpl.expect @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 1 verified, 0 errors diff --git a/Test/civl/wsq.bpl b/Test/civl/wsq.bpl index 39dad919..0a2227b6 100644 --- a/Test/civl/wsq.bpl +++ b/Test/civl/wsq.bpl @@ -89,9 +89,9 @@ ensures {:layer 3} {:expand} emptyInv(put_in_cs, take_in_cs, items,status,T); ensures {:atomic} |{ var i: int; A: assume status[i] == NOT_IN_Q; status[i] := IN_Q; return true; }|; { var t: int; - var {:ghost} {:layer 3} oldH:int; - var {:ghost} {:layer 3} oldT:int; - var {:ghost} {:layer 3} oldStatusT:bool; + var {:layer 3} oldH:int; + var {:layer 3} oldT:int; + var {:layer 3} oldStatusT:bool; call oldH, oldT := GhostRead(); yield; @@ -142,8 +142,8 @@ ensures {:atomic} |{ var i: int; A: goto B,C; B: assume status[i] == IN_Q; statu { var h, t: int; var chk: bool; - var {:ghost} {:layer 3} oldH:int; - var {:ghost} {:layer 3} oldT:int; + var {:layer 3} oldH:int; + var {:layer 3} oldT:int; call oldH, oldT := GhostRead(); yield; @@ -304,8 +304,8 @@ ensures {:atomic} |{ var i: int; A: goto B,C; B: assume status[i] == IN_Q; statu { var h, t: int; var chk: bool; - var {:ghost} {:layer 3} oldH:int; - var {:ghost} {:layer 3} oldT:int; + var {:layer 3} oldH:int; + var {:layer 3} oldT:int; call oldH, oldT := GhostRead(); yield; -- cgit v1.2.3 From deef37064f673be0391a7224ed8551b1e68be829 Mon Sep 17 00:00:00 2001 From: Shuvendu Lahiri Date: Mon, 26 Oct 2015 17:27:52 -0700 Subject: Bug fix for deterministExtractLoops for Shaobo's example --- Source/Core/Absy.cs | 21 ++++++-- Test/extractloops/detLoopExtract2.bpl | 27 ++++++++++ Test/extractloops/detLoopExtract2.bpl.expect | 2 + Test/snapshots/Snapshots41.v0.bpl | 70 ++++++++++++------------- Test/snapshots/Snapshots41.v1.bpl | 78 ++++++++++++++-------------- 5 files changed, 120 insertions(+), 78 deletions(-) create mode 100644 Test/extractloops/detLoopExtract2.bpl create mode 100644 Test/extractloops/detLoopExtract2.bpl.expect diff --git a/Source/Core/Absy.cs b/Source/Core/Absy.cs index c2e68002..8c04007b 100644 --- a/Source/Core/Absy.cs +++ b/Source/Core/Absy.cs @@ -750,15 +750,28 @@ namespace Microsoft.Boogie { Contract.Assert(block != null); var auxCmd = block.TransferCmd as GotoCmd; if (auxCmd == null) continue; - foreach(var bl in auxCmd.labelTargets) + foreach (var bl in auxCmd.labelTargets) { if (loopBlocks.Contains(bl)) continue; - immSuccBlks.Add(bl); + immSuccBlks.Add(bl); } } return immSuccBlks; } + private HashSet GetBlocksInAllNaturalLoops(Block header, Graph/*!*/ g) + { + Contract.Assert(CommandLineOptions.Clo.DeterministicExtractLoops, "Can only be called with /deterministicExtractLoops option"); + var allBlocksInNaturalLoops = new HashSet(); + foreach (Block/*!*/ source in g.BackEdgeNodes(header)) + { + Contract.Assert(source != null); + g.NaturalLoops(header, source).Iter(b => allBlocksInNaturalLoops.Add(b)); + } + return allBlocksInNaturalLoops; + } + + void CreateProceduresForLoops(Implementation impl, Graph/*!*/ g, List/*!*/ loopImpls, Dictionary> fullMap) { @@ -975,8 +988,8 @@ namespace Microsoft.Boogie { GotoCmd auxGotoCmd = block.TransferCmd as GotoCmd; Contract.Assert(auxGotoCmd != null && auxGotoCmd.labelNames != null && auxGotoCmd.labelTargets != null && auxGotoCmd.labelTargets.Count >= 1); - var blksThatBreakOut = GetBreakBlocksOfLoop(header, source, g); - var loopNodes = g.NaturalLoops(header, source); + //BUGFIX on 10/26/15: this contains nodes present in NaturalLoops for a different backedgenode + var loopNodes = GetBlocksInAllNaturalLoops(header, g); //var loopNodes = g.NaturalLoops(header, source); foreach(var bl in auxGotoCmd.labelTargets) { if (!loopNodes.Contains(bl)) { Block auxNewBlock = new Block(); diff --git a/Test/extractloops/detLoopExtract2.bpl b/Test/extractloops/detLoopExtract2.bpl new file mode 100644 index 00000000..f2befc53 --- /dev/null +++ b/Test/extractloops/detLoopExtract2.bpl @@ -0,0 +1,27 @@ +// RUN: %boogie -nologo -nologo -stratifiedInline:1 -extractLoops -deterministicExtractLoops -recursionBound:6 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +//This example checks the bug fix in the loop extract for http://symdiff.codeplex.com/workitem/4 +procedure {:entrypoint} Main() returns(r:int) +{ + var i, j : int; + var Flag : bool; + var b : bool; + i := 0; + j := 0; + Flag := false; + while(i<3) + { + havoc b; + if (b || Flag) { + i := i + 1; + j := j + 1; + } + else { + Flag := true; + j := j + 1; + } + } + assume !(i == j || i == j - 1); + return; +} diff --git a/Test/extractloops/detLoopExtract2.bpl.expect b/Test/extractloops/detLoopExtract2.bpl.expect new file mode 100644 index 00000000..37fad75c --- /dev/null +++ b/Test/extractloops/detLoopExtract2.bpl.expect @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 1 verified, 0 errors diff --git a/Test/snapshots/Snapshots41.v0.bpl b/Test/snapshots/Snapshots41.v0.bpl index 631fe544..dbfe3e2d 100644 --- a/Test/snapshots/Snapshots41.v0.bpl +++ b/Test/snapshots/Snapshots41.v0.bpl @@ -1,35 +1,35 @@ -procedure {:checksum "0"} M(x: int); -implementation {:id "M"} {:checksum "1"} M(x: int) -{ assert x < 20 || 10 <= x; // always true - assert x < 10; // error - call Other(x); // error: precondition violation -} - -procedure {:checksum "10"} Other(y: int); - requires 0 <= y; -implementation {:id "Other"} {:checksum "11"} Other(y: int) -{ -} - -procedure {:checksum "20"} Posty() returns (z: int); - ensures 2 <= z; // error: postcondition violation -implementation {:id "Posty"} {:checksum "21"} Posty() returns (z: int) -{ - var t: int; - t := 20; - if (t < z) { - } else { // the postcondition violation occurs on this 'else' branch - } -} - -procedure {:checksum "30"} NoChangeWhazzoeva(u: int); -implementation {:id "NoChangeWhazzoeva"} {:checksum "3"} NoChangeWhazzoeva(u: int) -{ - assert u != 53; // error -} - -procedure {:checksum "40"} NoChangeAndCorrect(); -implementation {:id "NoChangeAndCorrect"} {:checksum "41"} NoChangeAndCorrect() -{ - assert true; -} +procedure {:checksum "0"} M(x: int); +implementation {:id "M"} {:checksum "1"} M(x: int) +{ assert x < 20 || 10 <= x; // always true + assert x < 10; // error + call Other(x); // error: precondition violation +} + +procedure {:checksum "10"} Other(y: int); + requires 0 <= y; +implementation {:id "Other"} {:checksum "11"} Other(y: int) +{ +} + +procedure {:checksum "20"} Posty() returns (z: int); + ensures 2 <= z; // error: postcondition violation +implementation {:id "Posty"} {:checksum "21"} Posty() returns (z: int) +{ + var t: int; + t := 20; + if (t < z) { + } else { // the postcondition violation occurs on this 'else' branch + } +} + +procedure {:checksum "30"} NoChangeWhazzoeva(u: int); +implementation {:id "NoChangeWhazzoeva"} {:checksum "3"} NoChangeWhazzoeva(u: int) +{ + assert u != 53; // error +} + +procedure {:checksum "40"} NoChangeAndCorrect(); +implementation {:id "NoChangeAndCorrect"} {:checksum "41"} NoChangeAndCorrect() +{ + assert true; +} diff --git a/Test/snapshots/Snapshots41.v1.bpl b/Test/snapshots/Snapshots41.v1.bpl index 0cd9fbf9..9864e0e4 100644 --- a/Test/snapshots/Snapshots41.v1.bpl +++ b/Test/snapshots/Snapshots41.v1.bpl @@ -1,39 +1,39 @@ -procedure {:checksum "0"} M(x: int); -implementation {:id "M"} {:checksum "1"} M(x: int) -{ -assert x < 20 || 10 <= x; // always true - - assert x < 10; // error - call Other(x); // error: precondition violation - assert x == 7; // error: this is a new error in v1 -} - - - procedure {:checksum "10"} Other(y: int); - requires 0 <= y; - implementation {:id "Other"} {:checksum "11"} Other(y: int) - { - } - - - -procedure {:checksum "20"} Posty() returns (z: int); - ensures 2 <= z; // error: postcondition violation -implementation {:id "Posty"} {:checksum "21"} Posty() returns (z: int) -{ - var t: int; - t := 20; - if (t < z) { - assert true; // this is a new assert - } else { // the postcondition violation occurs on this 'else' branch - } -} - - procedure {:checksum "30"} NoChangeWhazzoeva(u: int); - implementation {:id "NoChangeWhazzoeva"} {:checksum "3"} NoChangeWhazzoeva(u: int) - { - assert u != 53; // error - } - -procedure {:checksum "40"} NoChangeAndCorrect(); -implementation {:id "NoChangeAndCorrect"} {:checksum "41"} NoChangeAndCorrect() { assert true; } +procedure {:checksum "0"} M(x: int); +implementation {:id "M"} {:checksum "1"} M(x: int) +{ +assert x < 20 || 10 <= x; // always true + + assert x < 10; // error + call Other(x); // error: precondition violation + assert x == 7; // error: this is a new error in v1 +} + + + procedure {:checksum "10"} Other(y: int); + requires 0 <= y; + implementation {:id "Other"} {:checksum "11"} Other(y: int) + { + } + + + +procedure {:checksum "20"} Posty() returns (z: int); + ensures 2 <= z; // error: postcondition violation +implementation {:id "Posty"} {:checksum "21"} Posty() returns (z: int) +{ + var t: int; + t := 20; + if (t < z) { + assert true; // this is a new assert + } else { // the postcondition violation occurs on this 'else' branch + } +} + + procedure {:checksum "30"} NoChangeWhazzoeva(u: int); + implementation {:id "NoChangeWhazzoeva"} {:checksum "3"} NoChangeWhazzoeva(u: int) + { + assert u != 53; // error + } + +procedure {:checksum "40"} NoChangeAndCorrect(); +implementation {:id "NoChangeAndCorrect"} {:checksum "41"} NoChangeAndCorrect() { assert true; } -- cgit v1.2.3 From e037ca99cdc163ca43690aaef3c380ebc1ce1962 Mon Sep 17 00:00:00 2001 From: Shaz Qadeer Date: Mon, 26 Oct 2015 21:12:35 -0700 Subject: fixed --- Test/linear/typecheck.bpl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Test/linear/typecheck.bpl b/Test/linear/typecheck.bpl index b4f784d3..c3c294c9 100644 --- a/Test/linear/typecheck.bpl +++ b/Test/linear/typecheck.bpl @@ -89,11 +89,14 @@ modifies g; procedure {:yields} {:layer 0} I({:linear_in ""} x:int) returns({:linear ""} x':int) { + yield; x' := x; + yield; } procedure {:yields} {:layer 0} J() { + yield; } procedure {:yields} {:layer 1} P1({:linear_in ""} x:int) returns({:linear ""} x':int) -- cgit v1.2.3 From 02f5c060ca5ce6bff003034ed634c114d5592398 Mon Sep 17 00:00:00 2001 From: Shuvendu Lahiri Date: Tue, 27 Oct 2015 17:40:33 -0700 Subject: fix for deterministicExtractLoops for nested loops --- Source/Core/Absy.cs | 3 ++- Test/extractloops/detLoopExtractNested.bpl | 23 +++++++++++++++++++++++ Test/extractloops/detLoopExtractNested.bpl.expect | 19 +++++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 Test/extractloops/detLoopExtractNested.bpl create mode 100644 Test/extractloops/detLoopExtractNested.bpl.expect diff --git a/Source/Core/Absy.cs b/Source/Core/Absy.cs index 8c04007b..d2243085 100644 --- a/Source/Core/Absy.cs +++ b/Source/Core/Absy.cs @@ -991,7 +991,8 @@ namespace Microsoft.Boogie { //BUGFIX on 10/26/15: this contains nodes present in NaturalLoops for a different backedgenode var loopNodes = GetBlocksInAllNaturalLoops(header, g); //var loopNodes = g.NaturalLoops(header, source); foreach(var bl in auxGotoCmd.labelTargets) { - if (!loopNodes.Contains(bl)) { + if (g.Nodes.Contains(bl) && //newly created blocks are not present in NaturalLoop(header, xx, g) + !loopNodes.Contains(bl)) { Block auxNewBlock = new Block(); auxNewBlock.Label = ((Block)bl).Label; //these blocks may have read/write locals that are not present in naturalLoops diff --git a/Test/extractloops/detLoopExtractNested.bpl b/Test/extractloops/detLoopExtractNested.bpl new file mode 100644 index 00000000..65de20c1 --- /dev/null +++ b/Test/extractloops/detLoopExtractNested.bpl @@ -0,0 +1,23 @@ +// RUN: %boogie -nologo -stratifiedInline:1 -extractLoops -deterministicExtractLoops -recursionBound:100 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +//This example checks the bug fix in the loop extract for http://symdiff.codeplex.com/workitem/1 + +var t: int; +procedure {:entrypoint} NestedLoops() +modifies t; +//ensures t == 6; +{ + var i:int, j:int; + i, j, t := 0, 0, 0; + while(i < 2) { + j := 0; + while (j < 3) { + t := t + 1; + j := j + 1; + } + i := i + 1; + } + assume true; //would be provable (!true) wihtout the fix +} + diff --git a/Test/extractloops/detLoopExtractNested.bpl.expect b/Test/extractloops/detLoopExtractNested.bpl.expect new file mode 100644 index 00000000..f4932ede --- /dev/null +++ b/Test/extractloops/detLoopExtractNested.bpl.expect @@ -0,0 +1,19 @@ +(0,0): Error BP5001: This assertion might not hold. +Execution trace: + detLoopExtractNested.bpl(12,12): anon0 + detLoopExtractNested.bpl(14,8): anon5_LoopBody + detLoopExtractNested.bpl(16,10): anon6_LoopBody + detLoopExtractNested.bpl(16,10): anon6_LoopBody + detLoopExtractNested.bpl(16,10): anon6_LoopBody + detLoopExtractNested.bpl(15,6): anon6_LoopDone + detLoopExtractNested.bpl(15,6): anon6_LoopDone + detLoopExtractNested.bpl(14,8): anon5_LoopBody + detLoopExtractNested.bpl(16,10): anon6_LoopBody + detLoopExtractNested.bpl(16,10): anon6_LoopBody + detLoopExtractNested.bpl(16,10): anon6_LoopBody + detLoopExtractNested.bpl(15,6): anon6_LoopDone + detLoopExtractNested.bpl(15,6): anon6_LoopDone + detLoopExtractNested.bpl(13,4): anon5_LoopDone + detLoopExtractNested.bpl(13,4): anon5_LoopDone + +Boogie program verifier finished with 0 verified, 1 error -- cgit v1.2.3 From 0732077773c80e86f8fbbc0be94ae9c034ad1924 Mon Sep 17 00:00:00 2001 From: Valentin Wüstholz Date: Thu, 29 Oct 2015 18:40:23 -0500 Subject: Add support for annotating implementations with k-ind. depth. --- Source/VCGeneration/VC.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Source/VCGeneration/VC.cs b/Source/VCGeneration/VC.cs index 33e2f928..79f56934 100644 --- a/Source/VCGeneration/VC.cs +++ b/Source/VCGeneration/VC.cs @@ -2229,10 +2229,11 @@ namespace VC { impl.Blocks.Insert(0, new Block(new Token(-17, -4), "0", new List(), new GotoCmd(Token.NoToken, new List { impl.Blocks[0].Label }, new List { impl.Blocks[0] }))); ResetPredecessors(impl.Blocks); - if(CommandLineOptions.Clo.KInductionDepth < 0) { + var k = Math.Max(CommandLineOptions.Clo.KInductionDepth, QKeyValue.FindIntAttribute(impl.Attributes, "kInductionDepth", -1)); + if(k < 0) { ConvertCFG2DAGStandard(impl, edgesCut, taskID); } else { - ConvertCFG2DAGKInduction(impl, edgesCut, taskID); + ConvertCFG2DAGKInduction(impl, edgesCut, taskID, k); } #region Debug Tracing @@ -2497,14 +2498,12 @@ namespace VC { return referencedVars; } - private void ConvertCFG2DAGKInduction(Implementation impl, Dictionary> edgesCut, int taskID) { + private void ConvertCFG2DAGKInduction(Implementation impl, Dictionary> edgesCut, int taskID, int inductionK) { // K-induction has not been adapted to be aware of these parameters which standard CFG to DAG transformation uses Contract.Requires(edgesCut == null); Contract.Requires(taskID == -1); - - int inductionK = CommandLineOptions.Clo.KInductionDepth; - Contract.Assume(inductionK >= 0); + Contract.Requires(0 <= inductionK); bool contRuleApplication = true; while (contRuleApplication) { -- cgit v1.2.3 From 90f2ae09d29b841ff42cdd8f441bda684c3421e2 Mon Sep 17 00:00:00 2001 From: Shaobo Date: Fri, 30 Oct 2015 14:53:07 -0600 Subject: Added wild card matching for /proc flag --- Source/Core/CommandLineOptions.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Core/CommandLineOptions.cs b/Source/Core/CommandLineOptions.cs index f4cba1dc..d7644915 100644 --- a/Source/Core/CommandLineOptions.cs +++ b/Source/Core/CommandLineOptions.cs @@ -11,6 +11,7 @@ using System.IO; using System.Linq; using System.Diagnostics; using System.Diagnostics.Contracts; +using System.Text.RegularExpressions; namespace Microsoft.Boogie { public class CommandLineOptionEngine @@ -1700,7 +1701,7 @@ namespace Microsoft.Boogie { // no preference return true; } - return ProcsToCheck.Contains(methodFullname); + return ProcsToCheck.Any(s => Regex.IsMatch(methodFullname, "^" + Regex.Escape(s).Replace(@"\*", ".*") + "$")); } public virtual StringCollection ParseNamedArgumentList(string argList) { -- cgit v1.2.3 From 12c5ff0211e844156706f8e617c94ab221c1b456 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Sat, 31 Oct 2015 08:19:49 +0000 Subject: Added test cases for the new asterisk wildcard behaviour of the ``-proc`` command line argument. --- .../multiple_procs_unusual_identifiers.bpl | 75 ++++++++++++++++++++++ ...ultiple_procs_verify_four_asterisk_wildcard.bpl | 28 ++++++++ ...le_procs_verify_two_asterisk_wildcard_begin.bpl | 17 +++++ ...iple_procs_verify_two_asterisk_wildcard_end.bpl | 17 +++++ ...rocs_verify_two_asterisk_wildcard_inbetween.bpl | 23 +++++++ 5 files changed, 160 insertions(+) create mode 100644 Test/commandline/multiple_procs_unusual_identifiers.bpl create mode 100644 Test/commandline/multiple_procs_verify_four_asterisk_wildcard.bpl create mode 100644 Test/commandline/multiple_procs_verify_two_asterisk_wildcard_begin.bpl create mode 100644 Test/commandline/multiple_procs_verify_two_asterisk_wildcard_end.bpl create mode 100644 Test/commandline/multiple_procs_verify_two_asterisk_wildcard_inbetween.bpl diff --git a/Test/commandline/multiple_procs_unusual_identifiers.bpl b/Test/commandline/multiple_procs_unusual_identifiers.bpl new file mode 100644 index 00000000..a3a4a4c1 --- /dev/null +++ b/Test/commandline/multiple_procs_unusual_identifiers.bpl @@ -0,0 +1,75 @@ +// RUN: %boogie "-proc:*Bar*" "%s" > "%t" +// RUN: %OutputCheck --file-to-check "%t" "%s" +// CHECK-L: Boogie program verifier finished with 10 verified, 0 errors + +procedure foo() +{ + assert false; +} + +procedure bar() +{ + assert false; +} + +/* Start should be matched */ + +procedure _Bar() +{ + assert true; +} + +procedure .Bar() +{ + assert true; +} + +procedure ..Bar..() +{ + assert true; +} + +procedure $Bar() +{ + assert true; +} + +procedure #Bar() +{ + assert true; +} + +procedure 'Bar''() +{ + assert true; +} + +procedure ``Bar``() +{ + assert true; +} + +procedure ~Bar() +{ + assert true; +} + +procedure Bar^^() +{ + assert true; +} + +/* This is Boogie2 claims backslash is a valid identifier + but the parser rejects this. +procedure Bar\\() +{ + assert true; +} +*/ + +procedure ??Bar() +{ + assert true; +} + +/* End should be matched */ diff --git a/Test/commandline/multiple_procs_verify_four_asterisk_wildcard.bpl b/Test/commandline/multiple_procs_verify_four_asterisk_wildcard.bpl new file mode 100644 index 00000000..e0f8eef3 --- /dev/null +++ b/Test/commandline/multiple_procs_verify_four_asterisk_wildcard.bpl @@ -0,0 +1,28 @@ +// RUN: %boogie "-proc:*Bar" "-proc:*Foo" "%s" > "%t" +// RUN: %OutputCheck --file-to-check "%t" "%s" +// CHECK-L: Boogie program verifier finished with 4 verified, 0 errors + +procedure foo() +{ + assert false; +} + +procedure helpfulFoo() +{ + assert true; +} + +procedure Foo() +{ + assert true; +} + +procedure translucentBar() +{ + assert true; +} + +procedure opaqueBar() +{ + assert true; +} diff --git a/Test/commandline/multiple_procs_verify_two_asterisk_wildcard_begin.bpl b/Test/commandline/multiple_procs_verify_two_asterisk_wildcard_begin.bpl new file mode 100644 index 00000000..0f6571ba --- /dev/null +++ b/Test/commandline/multiple_procs_verify_two_asterisk_wildcard_begin.bpl @@ -0,0 +1,17 @@ +// RUN: %boogie "-proc:*Bar" "%s" > "%t" +// RUN: %OutputCheck --file-to-check "%t" "%s" +// CHECK-L: Boogie program verifier finished with 2 verified, 0 errors +procedure foo() +{ + assert false; +} + +procedure translucentBar() +{ + assert true; +} + +procedure opaqueBar() +{ + assert true; +} diff --git a/Test/commandline/multiple_procs_verify_two_asterisk_wildcard_end.bpl b/Test/commandline/multiple_procs_verify_two_asterisk_wildcard_end.bpl new file mode 100644 index 00000000..5cb102e2 --- /dev/null +++ b/Test/commandline/multiple_procs_verify_two_asterisk_wildcard_end.bpl @@ -0,0 +1,17 @@ +// RUN: %boogie "-proc:bar*" "%s" > "%t" +// RUN: %OutputCheck --file-to-check "%t" "%s" +// CHECK-L: Boogie program verifier finished with 2 verified, 0 errors +procedure foo() +{ + assert false; +} + +procedure bar() +{ + assert true; +} + +procedure barzzz() +{ + assert true; +} diff --git a/Test/commandline/multiple_procs_verify_two_asterisk_wildcard_inbetween.bpl b/Test/commandline/multiple_procs_verify_two_asterisk_wildcard_inbetween.bpl new file mode 100644 index 00000000..7e19fe79 --- /dev/null +++ b/Test/commandline/multiple_procs_verify_two_asterisk_wildcard_inbetween.bpl @@ -0,0 +1,23 @@ +// RUN: %boogie "-proc:trivial*ZZZ" "%s" > "%t" +// RUN: %OutputCheck --file-to-check "%t" "%s" +// CHECK-L: Boogie program verifier finished with 2 verified, 0 errors +procedure foo() +{ + assert false; +} + +// should not be matched +procedure trivialFooZZX() +{ + assert false; +} + +procedure trivialFooZZZ() +{ + assert true; +} + +procedure trivialBarZZZ() +{ + assert true; +} -- cgit v1.2.3 From 8d1864f189552068d22f174b6eeaee202568de36 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Sat, 31 Oct 2015 08:52:02 +0000 Subject: Document the new behaviour of the ``-proc:`` command line option in the output of Boogie's help. --- Source/Core/CommandLineOptions.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Source/Core/CommandLineOptions.cs b/Source/Core/CommandLineOptions.cs index d7644915..3892bbc0 100644 --- a/Source/Core/CommandLineOptions.cs +++ b/Source/Core/CommandLineOptions.cs @@ -1858,7 +1858,15 @@ namespace Microsoft.Boogie { Multiple .bpl files supplied on the command line are concatenated into one Boogie program. - /proc:

: limits which procedures to check + /proc:

: Only check procedures matched by pattern

. This option + may be specified multiple times to match multiple patterns. + The pattern

matches the whole procedure name (i.e. + pattern ""foo"" will only match a procedure called foo and + not fooBar). The pattern

may contain * wildcards which + match any character zero or more times. For example the + pattern ""ab*d"" would match abd, abcd and abccd but not + Aabd nor abdD. The pattern ""*ab*d*"" would match abd, + abcd, abccd, Abd and abdD. /noResolve : parse only /noTypecheck : parse and resolve only -- cgit v1.2.3 From f049d2ec646244bc40964b36d961966fe2a3e4dc Mon Sep 17 00:00:00 2001 From: Valentin Wüstholz Date: Mon, 16 Nov 2015 12:04:37 -0600 Subject: Add support for identifying unnecessary assumes. --- Source/Core/Absy.cs | 2 ++ Source/Core/AbsyCmd.cs | 12 +++++++++ Source/Core/CommandLineOptions.cs | 3 +++ Source/Core/ResolutionContext.cs | 12 +++++++++ Source/ExecutionEngine/ExecutionEngine.cs | 5 ++++ Source/Provers/SMTLib/ProverInterface.cs | 30 ++++++++++++++++++++-- Source/VCGeneration/Check.cs | 16 ++++++++++-- Source/VCGeneration/VC.cs | 28 ++++++++++++++------ Source/VCGeneration/Wlp.cs | 17 +++++++++--- Test/unnecessaryassumes/unnecessaryassumes0.bpl | 13 ++++++++++ .../unnecessaryassumes0.bpl.expect | 3 +++ Test/unnecessaryassumes/unnecessaryassumes1.bpl | 23 +++++++++++++++++ .../unnecessaryassumes1.bpl.expect | 3 +++ 13 files changed, 151 insertions(+), 16 deletions(-) create mode 100644 Test/unnecessaryassumes/unnecessaryassumes0.bpl create mode 100644 Test/unnecessaryassumes/unnecessaryassumes0.bpl.expect create mode 100644 Test/unnecessaryassumes/unnecessaryassumes1.bpl create mode 100644 Test/unnecessaryassumes/unnecessaryassumes1.bpl.expect diff --git a/Source/Core/Absy.cs b/Source/Core/Absy.cs index d2243085..8be4f24e 100644 --- a/Source/Core/Absy.cs +++ b/Source/Core/Absy.cs @@ -696,6 +696,8 @@ namespace Microsoft.Boogie { } } + public readonly ISet NecessaryAssumes = new HashSet(); + public IEnumerable Blocks() { return Implementations.Select(Item => Item.Blocks).SelectMany(Item => Item); diff --git a/Source/Core/AbsyCmd.cs b/Source/Core/AbsyCmd.cs index 404945a9..c33f0743 100644 --- a/Source/Core/AbsyCmd.cs +++ b/Source/Core/AbsyCmd.cs @@ -2463,6 +2463,12 @@ namespace Microsoft.Boogie { } finally { rc.TypeBinderState = previousTypeBinderState; } + + var id = QKeyValue.FindStringAttribute(Attributes, "id"); + if (id != null) + { + rc.AddStatementId(tok, id); + } } public override void AddAssignedVariables(List vars) { @@ -2890,6 +2896,12 @@ namespace Microsoft.Boogie { public override void Resolve(ResolutionContext rc) { //Contract.Requires(rc != null); Expr.Resolve(rc); + + var id = QKeyValue.FindStringAttribute(Attributes, "id"); + if (id != null) + { + rc.AddStatementId(tok, id); + } } public override void AddAssignedVariables(List vars) { //Contract.Requires(vars != null); diff --git a/Source/Core/CommandLineOptions.cs b/Source/Core/CommandLineOptions.cs index 3892bbc0..e9aa3ceb 100644 --- a/Source/Core/CommandLineOptions.cs +++ b/Source/Core/CommandLineOptions.cs @@ -479,6 +479,8 @@ namespace Microsoft.Boogie { public string AbstractHoudini = null; public bool UseUnsatCoreForContractInfer = false; public bool PrintAssignment = false; + // TODO(wuestholz): Add documentation for this flag. + public bool PrintNecessaryAssumes = false; public int InlineDepth = -1; public bool UseProverEvaluate = false; // Use ProverInterface's Evaluate method, instead of model to get variable values public bool UseUncheckedContracts = false; @@ -1619,6 +1621,7 @@ namespace Microsoft.Boogie { ps.CheckBooleanFlag("crossDependencies", ref HoudiniUseCrossDependencies) || ps.CheckBooleanFlag("useUnsatCoreForContractInfer", ref UseUnsatCoreForContractInfer) || ps.CheckBooleanFlag("printAssignment", ref PrintAssignment) || + ps.CheckBooleanFlag("printNecessaryAssumes", ref PrintNecessaryAssumes) || ps.CheckBooleanFlag("useProverEvaluate", ref UseProverEvaluate) || ps.CheckBooleanFlag("nonUniformUnfolding", ref NonUniformUnfolding) || ps.CheckBooleanFlag("deterministicExtractLoops", ref DeterministicExtractLoops) || diff --git a/Source/Core/ResolutionContext.cs b/Source/Core/ResolutionContext.cs index 474a91dd..279e00bf 100644 --- a/Source/Core/ResolutionContext.cs +++ b/Source/Core/ResolutionContext.cs @@ -339,6 +339,18 @@ namespace Microsoft.Boogie { varContext = varContext.ParentContext; } + public readonly ISet StatementIds = new HashSet(); + + public void AddStatementId(IToken tok, string name) + { + if (StatementIds.Contains(name)) + { + Error(tok, "more than one statement with same id: " + name); + return; + } + StatementIds.Add(name); + } + public void AddVariable(Variable var, bool global) { Contract.Requires(var != null); var previous = FindVariable(cce.NonNull(var.Name), !global); diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index 9623139a..9bc855be 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -1008,6 +1008,11 @@ namespace Microsoft.Boogie CleanupCheckers(requestId); } + if (CommandLineOptions.Clo.PrintNecessaryAssumes && program.NecessaryAssumes.Any()) + { + Console.WriteLine("Necessary assume command(s): {0}", string.Join(", ", program.NecessaryAssumes)); + } + cce.NonNull(CommandLineOptions.Clo.TheProverFactory).Close(); outputCollector.WriteMoreOutput(); diff --git a/Source/Provers/SMTLib/ProverInterface.cs b/Source/Provers/SMTLib/ProverInterface.cs index e93ecee9..cb8442e5 100644 --- a/Source/Provers/SMTLib/ProverInterface.cs +++ b/Source/Provers/SMTLib/ProverInterface.cs @@ -246,9 +246,9 @@ namespace Microsoft.Boogie.SMTLib // Set produce-unsat-cores last. It seems there's a bug in Z3 where if we set it earlier its value // gets reset by other set-option commands ( https://z3.codeplex.com/workitem/188 ) - if (CommandLineOptions.Clo.ContractInfer && (CommandLineOptions.Clo.UseUnsatCoreForContractInfer || CommandLineOptions.Clo.ExplainHoudini)) + if (CommandLineOptions.Clo.PrintNecessaryAssumes || (CommandLineOptions.Clo.ContractInfer && (CommandLineOptions.Clo.UseUnsatCoreForContractInfer || CommandLineOptions.Clo.ExplainHoudini))) { - SendThisVC("(set-option :produce-unsat-cores true)"); + SendCommon("(set-option :produce-unsat-cores true)"); this.usingUnsatCore = true; } @@ -408,6 +408,15 @@ namespace Microsoft.Boogie.SMTLib SendThisVC("(push 1)"); SendThisVC("(set-info :boogie-vc-id " + SMTLibNamer.QuoteId(descriptiveName) + ")"); + + if (NamedAssumeVars != null) + { + foreach (var v in NamedAssumeVars) + { + SendThisVC(string.Format("(assert (! {0} :named {1}))", v, "aux$$" + v.Name)); + } + } + SendThisVC(vcString); FlushLogFile(); @@ -446,6 +455,7 @@ namespace Microsoft.Boogie.SMTLib if (options.Solver == SolverKind.Z3) { this.gen = gen; + SendThisVC("(reset)"); Namer.Reset(); common.Clear(); SetupAxiomBuilder(gen); @@ -1264,6 +1274,22 @@ namespace Microsoft.Boogie.SMTLib result = GetResponse(); + var reporter = handler as VC.VCGen.ErrorReporter; + // TODO(wuestholz): Is the reporter ever null? + if (NamedAssumeVars != null && NamedAssumeVars.Any() && result == Outcome.Valid && reporter != null) + { + SendThisVC("(get-unsat-core)"); + var resp = Process.GetProverResponse(); + if (resp.Name != "") + { + reporter.AddNecessaryAssume(resp.Name.Substring("aux$$assume$$".Length)); + } + foreach (var arg in resp.Arguments) + { + reporter.AddNecessaryAssume(arg.Name.Substring("aux$$assume$$".Length)); + } + } + if (CommandLineOptions.Clo.RunDiagnosticsOnTimeout && result == Outcome.TimeOut) { #region Run timeout diagnostics diff --git a/Source/VCGeneration/Check.cs b/Source/VCGeneration/Check.cs index 3c3b5cae..da445a00 100644 --- a/Source/VCGeneration/Check.cs +++ b/Source/VCGeneration/Check.cs @@ -346,7 +346,7 @@ namespace Microsoft.Boogie { } } - public void BeginCheck(string descriptiveName, VCExpr vc, ProverInterface.ErrorHandler handler) { + public void BeginCheck(string descriptiveName, VCExpr vc, ProverInterface.ErrorHandler handler, IList namedAssumeVars = null) { Contract.Requires(descriptiveName != null); Contract.Requires(vc != null); Contract.Requires(handler != null); @@ -357,9 +357,18 @@ namespace Microsoft.Boogie { outputExn = null; this.handler = handler; - thmProver.Reset(gen); + if (namedAssumeVars != null && namedAssumeVars.Any()) + { + // TODO(wuestholz): Avoid doing a full reset. This is currently necessary for old versions of Z3 due to a bug. + thmProver.FullReset(gen); + } + else + { + thmProver.Reset(gen); + } SetTimeout(); proverStart = DateTime.UtcNow; + thmProver.NamedAssumeVars = namedAssumeVars; thmProver.BeginCheck(descriptiveName, vc, handler); // gen.ClearSharedFormulas(); PR: don't know yet what to do with this guy @@ -386,6 +395,9 @@ namespace Microsoft.Boogie { // ----------------------------------------------------------------------------------------------- public abstract class ProverInterface { + + public IList NamedAssumeVars; + public static ProverInterface CreateProver(Program prog, string/*?*/ logFilePath, bool appendLogFile, int timeout, int taskID = -1) { Contract.Requires(prog != null); diff --git a/Source/VCGeneration/VC.cs b/Source/VCGeneration/VC.cs index 79f56934..2762fc72 100644 --- a/Source/VCGeneration/VC.cs +++ b/Source/VCGeneration/VC.cs @@ -1386,7 +1386,8 @@ namespace VC { var exprGen = ctx.ExprGen; VCExpr controlFlowVariableExpr = CommandLineOptions.Clo.UseLabels ? null : exprGen.Integer(BigNum.ZERO); - VCExpr vc = parent.GenerateVCAux(impl, controlFlowVariableExpr, label2absy, checker.TheoremProver.Context); + var namedAssumeVars = new List(); + VCExpr vc = parent.GenerateVCAux(impl, controlFlowVariableExpr, label2absy, checker.TheoremProver.Context, namedAssumeVars: namedAssumeVars); Contract.Assert(vc != null); if (!CommandLineOptions.Clo.UseLabels) @@ -1414,7 +1415,7 @@ namespace VC { string desc = cce.NonNull(impl.Name); if (no >= 0) desc += "_split" + no; - checker.BeginCheck(desc, vc, reporter); + checker.BeginCheck(desc, vc, reporter, namedAssumeVars); } private void SoundnessCheck(HashSet/*!*/>/*!*/ cache, Block/*!*/ orig, List/*!*/ copies) { @@ -1519,7 +1520,7 @@ namespace VC { } } - public VCExpr GenerateVC(Implementation/*!*/ impl, VCExpr controlFlowVariableExpr, out Dictionary/*!*/ label2absy, ProverContext proverContext) + public VCExpr GenerateVC(Implementation/*!*/ impl, VCExpr controlFlowVariableExpr, out Dictionary/*!*/ label2absy, ProverContext proverContext, IList namedAssumeVars = null) { Contract.Requires(impl != null); Contract.Requires(proverContext != null); @@ -1527,10 +1528,10 @@ namespace VC { Contract.Ensures(Contract.Result() != null); label2absy = new Dictionary(); - return GenerateVCAux(impl, controlFlowVariableExpr, label2absy, proverContext); + return GenerateVCAux(impl, controlFlowVariableExpr, label2absy, proverContext, namedAssumeVars: namedAssumeVars); } - public VCExpr GenerateVCAux(Implementation/*!*/ impl, VCExpr controlFlowVariableExpr, Dictionary/*!*/ label2absy, ProverContext proverContext) { + public VCExpr GenerateVCAux(Implementation/*!*/ impl, VCExpr controlFlowVariableExpr, Dictionary/*!*/ label2absy, ProverContext proverContext, IList namedAssumeVars = null) { Contract.Requires(impl != null); Contract.Requires(proverContext != null); Contract.Ensures(Contract.Result() != null); @@ -1567,7 +1568,8 @@ namespace VC { } break; case CommandLineOptions.VCVariety.DagIterative: - vc = LetVCIterative(impl.Blocks, controlFlowVariableExpr, label2absy, proverContext, out assertionCount); + // TODO(wuestholz): Support named assume statements not just for this encoding. + vc = LetVCIterative(impl.Blocks, controlFlowVariableExpr, label2absy, proverContext, out assertionCount, namedAssumeVars: namedAssumeVars); break; case CommandLineOptions.VCVariety.Doomed: vc = FlatBlockVC(impl, label2absy, false, false, true, proverContext, out assertionCount); @@ -2021,6 +2023,16 @@ namespace VC { protected ProverContext/*!*/ context; Program/*!*/ program; + public IEnumerable NecessaryAssumes + { + get { return program.NecessaryAssumes; } + } + + public void AddNecessaryAssume(string id) + { + program.NecessaryAssumes.Add(id); + } + public ErrorReporter(Dictionary/*!*/ gotoCmdOrigins, Dictionary/*!*/ label2absy, List/*!*/ blocks, @@ -3192,7 +3204,7 @@ namespace VC { Dictionary label2absy, ProverContext proverCtxt, out int assertionCount, - bool isPositiveContext = true) + bool isPositiveContext = true, IList namedAssumeVars = null) { Contract.Requires(blocks != null); Contract.Requires(proverCtxt != null); @@ -3252,7 +3264,7 @@ namespace VC { } VCContext context = new VCContext(label2absy, proverCtxt, controlFlowVariableExpr, isPositiveContext); - VCExpr vc = Wlp.Block(block, SuccCorrect, context); + VCExpr vc = Wlp.Block(block, SuccCorrect, context, namedAssumeVars); assertionCount += context.AssertionCount; VCExprVar v = gen.Variable(block.Label + "_correct", Bpl.Type.Bool); diff --git a/Source/VCGeneration/Wlp.cs b/Source/VCGeneration/Wlp.cs index b4ee4c09..74b77188 100644 --- a/Source/VCGeneration/Wlp.cs +++ b/Source/VCGeneration/Wlp.cs @@ -48,7 +48,7 @@ namespace VC { public class Wlp { - public static VCExpr Block(Block b, VCExpr N, VCContext ctxt) + public static VCExpr Block(Block b, VCExpr N, VCContext ctxt, IList namedAssumeVars = null) //modifies ctxt.*; { Contract.Requires(b != null); @@ -63,7 +63,7 @@ namespace VC { for (int i = b.Cmds.Count; --i >= 0; ) { - res = Cmd(b, cce.NonNull( b.Cmds[i]), res, ctxt); + res = Cmd(b, cce.NonNull( b.Cmds[i]), res, ctxt, namedAssumeVars); } int id = b.UniqueId; @@ -87,7 +87,7 @@ namespace VC { ///

/// Computes the wlp for an assert or assume command "cmd". /// - public static VCExpr Cmd(Block b, Cmd cmd, VCExpr N, VCContext ctxt) { + internal static VCExpr Cmd(Block b, Cmd cmd, VCExpr N, VCContext ctxt, IList namedAssumeVars = null) { Contract.Requires(cmd != null); Contract.Requires(N != null); Contract.Requires(ctxt != null); @@ -190,7 +190,16 @@ namespace VC { } } } - return gen.ImpliesSimp(ctxt.Ctxt.BoogieExprTranslator.Translate(ac.Expr), N); + var expr = ctxt.Ctxt.BoogieExprTranslator.Translate(ac.Expr); + + var aid = QKeyValue.FindStringAttribute(ac.Attributes, "id"); + if (aid != null && namedAssumeVars != null) + { + var v = gen.Variable("assume$$" + aid, Microsoft.Boogie.Type.Bool); + namedAssumeVars.Add(v); + expr = gen.ImpliesSimp(v, expr); + } + return gen.ImpliesSimp(expr, N); } else { Console.WriteLine(cmd.ToString()); Contract.Assert(false); throw new cce.UnreachableException(); // unexpected command diff --git a/Test/unnecessaryassumes/unnecessaryassumes0.bpl b/Test/unnecessaryassumes/unnecessaryassumes0.bpl new file mode 100644 index 00000000..a955495a --- /dev/null +++ b/Test/unnecessaryassumes/unnecessaryassumes0.bpl @@ -0,0 +1,13 @@ +// RUN: %boogie /printNecessaryAssumes "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +procedure test0(n: int) +{ + assume {:id "s0"} 0 < n; + assume {:id "s0"} 0 < n; +} + +procedure test1(n: int) +{ + assume {:id "s0"} 0 < n; +} diff --git a/Test/unnecessaryassumes/unnecessaryassumes0.bpl.expect b/Test/unnecessaryassumes/unnecessaryassumes0.bpl.expect new file mode 100644 index 00000000..9e420fa7 --- /dev/null +++ b/Test/unnecessaryassumes/unnecessaryassumes0.bpl.expect @@ -0,0 +1,3 @@ +unnecessaryassumes0.bpl(7,4): Error: more than one statement with same id: s0 +unnecessaryassumes0.bpl(12,4): Error: more than one statement with same id: s0 +2 name resolution errors detected in unnecessaryassumes0.bpl diff --git a/Test/unnecessaryassumes/unnecessaryassumes1.bpl b/Test/unnecessaryassumes/unnecessaryassumes1.bpl new file mode 100644 index 00000000..04226dfd --- /dev/null +++ b/Test/unnecessaryassumes/unnecessaryassumes1.bpl @@ -0,0 +1,23 @@ +// RUN: %boogie /printNecessaryAssumes "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +procedure test0(n: int) +{ + assume {:id "s0"} 0 < n; + assert 0 <= n; // verified under s0 +} + +procedure test1(n: int) +{ + assume 0 < n; + assume {:id "s1"} n == 3; + assert 0 <= n; // verified under true +} + +procedure test2(n: int) +{ + assume 0 < n; + assume {:id "s2"} n <= 42; + assume {:id "s3"} 42 <= n; + assert n == 42; // verified under s2 and s3 +} diff --git a/Test/unnecessaryassumes/unnecessaryassumes1.bpl.expect b/Test/unnecessaryassumes/unnecessaryassumes1.bpl.expect new file mode 100644 index 00000000..dd04bb46 --- /dev/null +++ b/Test/unnecessaryassumes/unnecessaryassumes1.bpl.expect @@ -0,0 +1,3 @@ +Necessary assume command(s): s0, s3, s2 + +Boogie program verifier finished with 3 verified, 0 errors -- cgit v1.2.3 From 75a179354ea7b78682904575d5692efa0068fcf0 Mon Sep 17 00:00:00 2001 From: Valentin Wüstholz Date: Mon, 16 Nov 2015 12:13:45 -0600 Subject: Add a test. --- Test/test1/StatementIds0.bpl | 15 +++++++++++++++ Test/test1/StatementIds0.bpl.expect | 3 +++ 2 files changed, 18 insertions(+) create mode 100644 Test/test1/StatementIds0.bpl create mode 100644 Test/test1/StatementIds0.bpl.expect diff --git a/Test/test1/StatementIds0.bpl b/Test/test1/StatementIds0.bpl new file mode 100644 index 00000000..85b9cd36 --- /dev/null +++ b/Test/test1/StatementIds0.bpl @@ -0,0 +1,15 @@ +// RUN: %boogie -noVerify "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +procedure test0(n: int) +{ + assume {:id "s0"} 0 < n; + assert {:id "s0"} 0 < n; +} + +procedure test1(n: int) +{ + call {:id "s0"} P(); +} + +procedure P(); diff --git a/Test/test1/StatementIds0.bpl.expect b/Test/test1/StatementIds0.bpl.expect new file mode 100644 index 00000000..944c3257 --- /dev/null +++ b/Test/test1/StatementIds0.bpl.expect @@ -0,0 +1,3 @@ +StatementIds0.bpl(7,4): Error: more than one statement with same id: s0 +StatementIds0.bpl(12,4): Error: more than one statement with same id: s0 +2 name resolution errors detected in StatementIds0.bpl -- cgit v1.2.3 From 67a0a458aad9ece669a49cca085b695a56003d0e Mon Sep 17 00:00:00 2001 From: Valentin Wüstholz Date: Mon, 16 Nov 2015 12:50:24 -0600 Subject: Add a test. --- Test/test1/StatementIds0.bpl | 15 ++++++++++++--- Test/test1/StatementIds0.bpl.expect | 4 +++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Test/test1/StatementIds0.bpl b/Test/test1/StatementIds0.bpl index 85b9cd36..abf26159 100644 --- a/Test/test1/StatementIds0.bpl +++ b/Test/test1/StatementIds0.bpl @@ -3,13 +3,22 @@ procedure test0(n: int) { - assume {:id "s0"} 0 < n; - assert {:id "s0"} 0 < n; + assume {:id "s0"} true; + assert {:id "s0"} true; } -procedure test1(n: int) +procedure test1() { call {:id "s0"} P(); } +procedure test2(n: int) +{ + while (*) + invariant {:id "s0"} true; + invariant {:id "s0"} true; + { + } +} + procedure P(); diff --git a/Test/test1/StatementIds0.bpl.expect b/Test/test1/StatementIds0.bpl.expect index 944c3257..4783d912 100644 --- a/Test/test1/StatementIds0.bpl.expect +++ b/Test/test1/StatementIds0.bpl.expect @@ -1,3 +1,5 @@ StatementIds0.bpl(7,4): Error: more than one statement with same id: s0 StatementIds0.bpl(12,4): Error: more than one statement with same id: s0 -2 name resolution errors detected in StatementIds0.bpl +StatementIds0.bpl(18,6): Error: more than one statement with same id: s0 +StatementIds0.bpl(19,6): Error: more than one statement with same id: s0 +4 name resolution errors detected in StatementIds0.bpl -- cgit v1.2.3 From 3f417a0223b4e5d0139c5b035e573895ac2ad84c Mon Sep 17 00:00:00 2001 From: qunyanm Date: Mon, 16 Nov 2015 11:03:28 -0800 Subject: Use EndCurly token for the ReturnCmd when creating unifiedExit Instead of using NoToken for the ReturnCmd, use the EndCurly when creating unifiedExit. --- Source/VCGeneration/ConditionGeneration.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VCGeneration/ConditionGeneration.cs b/Source/VCGeneration/ConditionGeneration.cs index ae0a1147..b26750ab 100644 --- a/Source/VCGeneration/ConditionGeneration.cs +++ b/Source/VCGeneration/ConditionGeneration.cs @@ -1133,7 +1133,7 @@ namespace VC { } if (returnBlocks > 1) { string unifiedExitLabel = "GeneratedUnifiedExit"; - Block unifiedExit = new Block(new Token(-17, -4), unifiedExitLabel, new List(), new ReturnCmd(Token.NoToken)); + Block unifiedExit = new Block(new Token(-17, -4), unifiedExitLabel, new List(), new ReturnCmd(impl.StructuredStmts.EndCurly)); Contract.Assert(unifiedExit != null); foreach (Block b in impl.Blocks) { if (b.TransferCmd is ReturnCmd) { -- cgit v1.2.3 From a799e128af68911228d081202ba0bd294ced4a4f Mon Sep 17 00:00:00 2001 From: qunyanm Date: Mon, 16 Nov 2015 12:27:44 -0800 Subject: Use EndCurly token for the ReturnCmd when creating unifiedExit Instead of using NoToken for the ReturnCmd, use the EndCurly, if it exists, when creating unifiedExit --- Source/VCGeneration/ConditionGeneration.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Source/VCGeneration/ConditionGeneration.cs b/Source/VCGeneration/ConditionGeneration.cs index b26750ab..0e5fce82 100644 --- a/Source/VCGeneration/ConditionGeneration.cs +++ b/Source/VCGeneration/ConditionGeneration.cs @@ -1133,7 +1133,12 @@ namespace VC { } if (returnBlocks > 1) { string unifiedExitLabel = "GeneratedUnifiedExit"; - Block unifiedExit = new Block(new Token(-17, -4), unifiedExitLabel, new List(), new ReturnCmd(impl.StructuredStmts.EndCurly)); + Block unifiedExit; + if (impl.StructuredStmts != null) { + unifiedExit = new Block(new Token(-17, -4), unifiedExitLabel, new List(), new ReturnCmd(impl.StructuredStmts.EndCurly)); + } else { + unifiedExit = new Block(new Token(-17, -4), unifiedExitLabel, new List(), new ReturnCmd(Token.NoToken)); + } Contract.Assert(unifiedExit != null); foreach (Block b in impl.Blocks) { if (b.TransferCmd is ReturnCmd) { -- cgit v1.2.3 From e25dac8edab3e3f23db7c83b393ed388840d239f Mon Sep 17 00:00:00 2001 From: Valentin Wüstholz Date: Tue, 17 Nov 2015 14:03:06 -0600 Subject: Fix issue #25. --- Source/AbsInt/IntervalDomain.cs | 4 ++++ Test/aitest0/Issue25.bpl | 14 ++++++++++++++ Test/aitest0/Issue25.bpl.expect | 8 ++++++++ 3 files changed, 26 insertions(+) create mode 100644 Test/aitest0/Issue25.bpl create mode 100644 Test/aitest0/Issue25.bpl.expect diff --git a/Source/AbsInt/IntervalDomain.cs b/Source/AbsInt/IntervalDomain.cs index 0d2676f2..ee9d632b 100644 --- a/Source/AbsInt/IntervalDomain.cs +++ b/Source/AbsInt/IntervalDomain.cs @@ -698,9 +698,13 @@ namespace Microsoft.Boogie.AbstractInterpretation lo = Lo; hi = Hi; if (hi != null) { Lo = node.Type.IsReal ? -hi : 1 - hi; + } else { + Lo = null; } if (lo != null) { Hi = node.Type.IsReal ? -lo : 1 - lo; + } else { + Hi = null; } } else if (op.Op == UnaryOperator.Opcode.Not) { diff --git a/Test/aitest0/Issue25.bpl b/Test/aitest0/Issue25.bpl new file mode 100644 index 00000000..6ffcd113 --- /dev/null +++ b/Test/aitest0/Issue25.bpl @@ -0,0 +1,14 @@ +// RUN: %boogie -infer:j "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +const N: int; +axiom 0 <= N; + +procedure vacuous_post() +ensures (forall k, l: int :: 0 <= k && k <= l && l < N ==> N < N); // Used to verify at some point (see https://github.com/boogie-org/boogie/issues/25). +{ +var x: int; +x := -N; +while (x != x) { +} +} diff --git a/Test/aitest0/Issue25.bpl.expect b/Test/aitest0/Issue25.bpl.expect new file mode 100644 index 00000000..80eeb1a7 --- /dev/null +++ b/Test/aitest0/Issue25.bpl.expect @@ -0,0 +1,8 @@ +Bug25.bpl(12,1): Error BP5003: A postcondition might not hold on this return path. +Bug25.bpl(8,1): Related location: This is the postcondition that might not hold. +Execution trace: + Bug25.bpl(11,3): anon0 + Bug25.bpl(12,1): anon2_LoopHead + Bug25.bpl(12,1): anon2_LoopDone + +Boogie program verifier finished with 0 verified, 1 error -- cgit v1.2.3 From 9a4448732895ffe451642b2bebd95dcf1ed371d4 Mon Sep 17 00:00:00 2001 From: Valentin Wüstholz Date: Tue, 17 Nov 2015 14:13:58 -0600 Subject: Fix test output. --- Test/aitest0/Issue25.bpl.expect | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Test/aitest0/Issue25.bpl.expect b/Test/aitest0/Issue25.bpl.expect index 80eeb1a7..f56502e2 100644 --- a/Test/aitest0/Issue25.bpl.expect +++ b/Test/aitest0/Issue25.bpl.expect @@ -1,8 +1,8 @@ -Bug25.bpl(12,1): Error BP5003: A postcondition might not hold on this return path. -Bug25.bpl(8,1): Related location: This is the postcondition that might not hold. +Issue25.bpl(12,1): Error BP5003: A postcondition might not hold on this return path. +Issue25.bpl(8,1): Related location: This is the postcondition that might not hold. Execution trace: - Bug25.bpl(11,3): anon0 - Bug25.bpl(12,1): anon2_LoopHead - Bug25.bpl(12,1): anon2_LoopDone + Issue25.bpl(11,3): anon0 + Issue25.bpl(12,1): anon2_LoopHead + Issue25.bpl(12,1): anon2_LoopDone Boogie program verifier finished with 0 verified, 1 error -- cgit v1.2.3 From a51f4e6cba57b6711e36ef482f4e320c9cf0542f Mon Sep 17 00:00:00 2001 From: Valentin Wüstholz Date: Wed, 18 Nov 2015 15:46:24 -0600 Subject: Add experimental support for optimization (requires Z3 build after changeset 9cba63c31f6f1466dd4ef442bb840d1ab84539c7). --- Source/Core/AbsyCmd.cs | 6 + Source/Core/AbsyQuant.cs | 17 ++- Source/Provers/SMTLib/ProverInterface.cs | 21 +++- Source/Provers/SMTLib/SMTLibLineariser.cs | 17 ++- Source/Provers/SMTLib/SMTLibNamer.cs | 2 +- Source/VCExpr/VCExprAST.cs | 3 + Source/VCGeneration/ConditionGeneration.cs | 12 ++ Source/VCGeneration/Wlp.cs | 21 +++- Test/optimization/Optimization0.bpl | 86 ++++++++++++++ Test/optimization/Optimization0.bpl.expect | 182 +++++++++++++++++++++++++++++ Test/optimization/Optimization1.bpl | 32 +++++ Test/optimization/Optimization1.bpl.expect | 5 + Test/optimization/Optimization2.bpl | 12 ++ Test/optimization/Optimization2.bpl.expect | 3 + Test/optimization/lit.local.cfg | 3 + 15 files changed, 410 insertions(+), 12 deletions(-) create mode 100644 Test/optimization/Optimization0.bpl create mode 100644 Test/optimization/Optimization0.bpl.expect create mode 100644 Test/optimization/Optimization1.bpl create mode 100644 Test/optimization/Optimization1.bpl.expect create mode 100644 Test/optimization/Optimization2.bpl create mode 100644 Test/optimization/Optimization2.bpl.expect create mode 100644 Test/optimization/lit.local.cfg diff --git a/Source/Core/AbsyCmd.cs b/Source/Core/AbsyCmd.cs index c33f0743..2e33e1dd 100644 --- a/Source/Core/AbsyCmd.cs +++ b/Source/Core/AbsyCmd.cs @@ -3218,8 +3218,14 @@ namespace Microsoft.Boogie { this.Expr.Emit(stream); stream.WriteLine(";"); } + public override void Resolve(ResolutionContext rc) { + //Contract.Requires(rc != null); + ResolveAttributes(Attributes, rc); + base.Resolve(rc); + } public override void Typecheck(TypecheckingContext tc) { //Contract.Requires(tc != null); + TypecheckAttributes(Attributes, tc); Expr.Typecheck(tc); Contract.Assert(Expr.Type != null); // follows from Expr.Typecheck postcondition if (!Expr.Type.Unify(Type.Bool)) { diff --git a/Source/Core/AbsyQuant.cs b/Source/Core/AbsyQuant.cs index 9f94490b..39f18657 100644 --- a/Source/Core/AbsyQuant.cs +++ b/Source/Core/AbsyQuant.cs @@ -338,6 +338,12 @@ namespace Microsoft.Boogie { public override void Resolve(ResolutionContext rc) { //Contract.Requires(rc != null); + + if ((Key == "minimize" || Key == "maximize") && Params.Count != 1) + { + rc.Error(this, "attributes :minimize and :maximize accept only one argument"); + } + foreach (object p in Params) { if (p is Expr) { ((Expr)p).Resolve(rc); @@ -348,8 +354,15 @@ namespace Microsoft.Boogie { public override void Typecheck(TypecheckingContext tc) { //Contract.Requires(tc != null); foreach (object p in Params) { - if (p is Expr) { - ((Expr)p).Typecheck(tc); + var expr = p as Expr; + if (expr != null) { + expr.Typecheck(tc); + } + if ((Key == "minimize" || Key == "maximize") + && (expr == null || !(expr.Type.IsInt || expr.Type.IsReal || expr.Type.IsBv))) + { + tc.Error(this, "attributes :minimize and :maximize accept only one argument of type int, real or bv"); + break; } } } diff --git a/Source/Provers/SMTLib/ProverInterface.cs b/Source/Provers/SMTLib/ProverInterface.cs index cb8442e5..ca530da2 100644 --- a/Source/Provers/SMTLib/ProverInterface.cs +++ b/Source/Provers/SMTLib/ProverInterface.cs @@ -401,6 +401,8 @@ namespace Microsoft.Boogie.SMTLib PrepareCommon(); + OptimizationRequests.Clear(); + string vcString = "(assert (not\n" + VCExpr2String(vc, 1) + "\n))"; FlushAxioms(); @@ -418,6 +420,15 @@ namespace Microsoft.Boogie.SMTLib } SendThisVC(vcString); + + if (options.Solver == SolverKind.Z3 && 0 < OptimizationRequests.Count) + { + foreach (var r in OptimizationRequests) + { + SendThisVC(r); + } + } + FlushLogFile(); if (Process != null) { @@ -1838,6 +1849,7 @@ namespace Microsoft.Boogie.SMTLib private Model GetErrorModel() { if (!options.ExpectingModel()) return null; + SendThisVC("(get-model)"); Process.Ping(); Model theModel = null; @@ -1932,6 +1944,9 @@ namespace Microsoft.Boogie.SMTLib result = Outcome.Invalid; wasUnknown = true; break; + case "objectives": + // We ignore this. + break; default: HandleProverError("Unexpected prover response: " + resp.ToString()); break; @@ -1970,6 +1985,8 @@ namespace Microsoft.Boogie.SMTLib return result; } + readonly IList OptimizationRequests = new List(); + protected string VCExpr2String(VCExpr expr, int polarity) { Contract.Requires(expr != null); @@ -2009,10 +2026,8 @@ namespace Microsoft.Boogie.SMTLib DeclCollector.Collect(sortedExpr); FeedTypeDeclsToProver(); - - AddAxiom(SMTLibExprLineariser.ToString(sortedAxioms, Namer, options)); - string res = SMTLibExprLineariser.ToString(sortedExpr, Namer, options); + string res = SMTLibExprLineariser.ToString(sortedExpr, Namer, options, OptimizationRequests); Contract.Assert(res != null); if (CommandLineOptions.Clo.Trace) diff --git a/Source/Provers/SMTLib/SMTLibLineariser.cs b/Source/Provers/SMTLib/SMTLibLineariser.cs index dcf95bd2..de8798b8 100644 --- a/Source/Provers/SMTLib/SMTLibLineariser.cs +++ b/Source/Provers/SMTLib/SMTLibLineariser.cs @@ -34,14 +34,14 @@ namespace Microsoft.Boogie.SMTLib public class SMTLibExprLineariser : IVCExprVisitor { - public static string ToString(VCExpr e, UniqueNamer namer, SMTLibProverOptions opts) + public static string ToString(VCExpr e, UniqueNamer namer, SMTLibProverOptions opts, IList optReqs = null) { Contract.Requires(e != null); Contract.Requires(namer != null); Contract.Ensures(Contract.Result() != null); StringWriter sw = new StringWriter(); - SMTLibExprLineariser lin = new SMTLibExprLineariser(sw, namer, opts); + SMTLibExprLineariser lin = new SMTLibExprLineariser(sw, namer, opts, optReqs); Contract.Assert(lin != null); lin.Linearise(e, LineariserOptions.Default); return cce.NonNull(sw.ToString()); @@ -74,12 +74,15 @@ namespace Microsoft.Boogie.SMTLib internal int UnderQuantifier = 0; internal readonly SMTLibProverOptions ProverOptions; - public SMTLibExprLineariser(TextWriter wr, UniqueNamer namer, SMTLibProverOptions opts) + readonly IList OptimizationRequests; + + public SMTLibExprLineariser(TextWriter wr, UniqueNamer namer, SMTLibProverOptions opts, IList optReqs = null) { Contract.Requires(wr != null); Contract.Requires(namer != null); this.wr = wr; this.Namer = namer; this.ProverOptions = opts; + this.OptimizationRequests = optReqs; } public void Linearise(VCExpr expr, LineariserOptions options) @@ -263,6 +266,14 @@ namespace Microsoft.Boogie.SMTLib } return true; } + if (OptimizationRequests != null + && (node.Op.Equals(VCExpressionGenerator.MinimizeOp) || node.Op.Equals(VCExpressionGenerator.MaximizeOp))) + { + string optOp = node.Op.Equals(VCExpressionGenerator.MinimizeOp) ? "minimize" : "maximize"; + OptimizationRequests.Add(string.Format("({0} {1})", optOp, ToString(node[0], Namer, ProverOptions))); + Linearise(node[1], options); + return true; + } return node.Accept(OpLineariser, options); } diff --git a/Source/Provers/SMTLib/SMTLibNamer.cs b/Source/Provers/SMTLib/SMTLibNamer.cs index 900bdbcc..f1179159 100644 --- a/Source/Provers/SMTLib/SMTLibNamer.cs +++ b/Source/Provers/SMTLib/SMTLibNamer.cs @@ -47,7 +47,7 @@ namespace Microsoft.Boogie.SMTLib "flet", "implies", "!=", "if_then_else", // Z3 extensions "lblneg", "lblpos", "lbl-lit", - "if", "&&", "||", "equals", "equiv", "bool", + "if", "&&", "||", "equals", "equiv", "bool", "minimize", "maximize", // Boogie-defined "real_pow", "UOrdering2", "UOrdering3", // Floating point (final draft SMTLIB-v2.5) diff --git a/Source/VCExpr/VCExprAST.cs b/Source/VCExpr/VCExprAST.cs index 0a9ba6b3..2a06447e 100644 --- a/Source/VCExpr/VCExprAST.cs +++ b/Source/VCExpr/VCExprAST.cs @@ -341,6 +341,9 @@ namespace Microsoft.Boogie { public static readonly VCExprOp TimeoutDiagnosticsOp = new VCExprCustomOp("timeoutDiagnostics", 1, Type.Bool); + public static readonly VCExprOp MinimizeOp = new VCExprNAryOp(2, Type.Bool); + public static readonly VCExprOp MaximizeOp = new VCExprNAryOp(2, Type.Bool); + public VCExprOp BoogieFunctionOp(Function func) { Contract.Requires(func != null); Contract.Ensures(Contract.Result() != null); diff --git a/Source/VCGeneration/ConditionGeneration.cs b/Source/VCGeneration/ConditionGeneration.cs index ae0a1147..5971d6f8 100644 --- a/Source/VCGeneration/ConditionGeneration.cs +++ b/Source/VCGeneration/ConditionGeneration.cs @@ -1539,6 +1539,18 @@ namespace VC { PredicateCmd pc = (PredicateCmd)c.Clone(); Contract.Assert(pc != null); + QKeyValue current = pc.Attributes; + while (current != null) + { + if (current.Key == "minimize" || current.Key == "maximize") { + Contract.Assume(current.Params.Count == 1); + var param = current.Params[0] as Expr; + Contract.Assume(param != null && (param.Type.IsInt || param.Type.IsReal || param.Type.IsBv)); + current.ClearParams(); + current.AddParam(Substituter.ApplyReplacingOldExprs(incarnationSubst, oldFrameSubst, param)); + } + current = current.Next; + } Expr copy = Substituter.ApplyReplacingOldExprs(incarnationSubst, oldFrameSubst, pc.Expr); if (CommandLineOptions.Clo.ModelViewFile != null && pc is AssumeCmd) { string description = QKeyValue.FindStringAttribute(pc.Attributes, "captureState"); diff --git a/Source/VCGeneration/Wlp.cs b/Source/VCGeneration/Wlp.cs index 74b77188..741ed723 100644 --- a/Source/VCGeneration/Wlp.cs +++ b/Source/VCGeneration/Wlp.cs @@ -186,7 +186,7 @@ namespace VC { if (naryExpr.Fun is FunctionCall) { int id = ac.UniqueId; ctxt.Label2absy[id] = ac; - return gen.ImpliesSimp(gen.LabelPos(cce.NonNull("si_fcall_" + id.ToString()), ctxt.Ctxt.BoogieExprTranslator.Translate(ac.Expr)), N); + return MaybeWrapWithOptimization(ctxt, gen, ac.Attributes, gen.ImpliesSimp(gen.LabelPos(cce.NonNull("si_fcall_" + id.ToString()), ctxt.Ctxt.BoogieExprTranslator.Translate(ac.Expr)), N)); } } } @@ -199,13 +199,28 @@ namespace VC { namedAssumeVars.Add(v); expr = gen.ImpliesSimp(v, expr); } - return gen.ImpliesSimp(expr, N); + return MaybeWrapWithOptimization(ctxt, gen, ac.Attributes, gen.ImpliesSimp(expr, N)); } else { Console.WriteLine(cmd.ToString()); Contract.Assert(false); throw new cce.UnreachableException(); // unexpected command } } - + + private static VCExpr MaybeWrapWithOptimization(VCContext ctxt, VCExpressionGenerator gen, QKeyValue attrs, VCExpr expr) + { + var min = QKeyValue.FindExprAttribute(attrs, "minimize"); + if (min != null) + { + expr = gen.Function(VCExpressionGenerator.MinimizeOp, ctxt.Ctxt.BoogieExprTranslator.Translate(min), expr); + } + var max = QKeyValue.FindExprAttribute(attrs, "maximize"); + if (max != null) + { + expr = gen.Function(VCExpressionGenerator.MaximizeOp, ctxt.Ctxt.BoogieExprTranslator.Translate(max), expr); + } + return expr; + } + public static CommandLineOptions.SubsumptionOption Subsumption(AssertCmd ac) { Contract.Requires(ac != null); int n = QKeyValue.FindIntAttribute(ac.Attributes, "subsumption", -1); diff --git a/Test/optimization/Optimization0.bpl b/Test/optimization/Optimization0.bpl new file mode 100644 index 00000000..c704c855 --- /dev/null +++ b/Test/optimization/Optimization0.bpl @@ -0,0 +1,86 @@ +// RUN: %boogie /doNotUseLabels /printModel:4 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +function may_fail(f: int) : bool; + +procedure test0() +{ + var x: int; + + havoc x; + assume 42 < x; + assume {:minimize x} true; + assert may_fail(x); +} + +procedure test1() +{ + var x: int; + + x := 24; + if (*) { + x := 42; + } + assume {:minimize x} true; + assert may_fail(x); +} + +procedure test2() +{ + var x: int; + + x := 1; + while (*) { + x := x + 1; + } + assume {:minimize x} true; + assert x < 10; +} + +procedure test3() +{ + var x: int; + + havoc x; + assume x < 42; + assume {:maximize x} true; + assert may_fail(x); +} + +procedure test4() +{ + var x: int; + + x := 24; + if (*) { + x := 42; + } + assume {:maximize x} true; + assert may_fail(x); +} + +procedure test5() +{ + var x: int; + + x := 1; + while (*) { + x := x - 1; + } + assume {:maximize x} true; + assert x < 1; +} + +procedure test6() +{ + var x: int; + + x := 1; + if (*) { + x := 2; + } + assume {:maximize x} true; + assert may_fail(x); +} + +// TODO(wuestholz): Make this work without the /doNotUseLabels flag. diff --git a/Test/optimization/Optimization0.bpl.expect b/Test/optimization/Optimization0.bpl.expect new file mode 100644 index 00000000..d2f9b606 --- /dev/null +++ b/Test/optimization/Optimization0.bpl.expect @@ -0,0 +1,182 @@ +*** MODEL +%lbl%@280 -> false +%lbl%+260 -> true +%lbl%+39 -> true +x@0 -> 43 +ControlFlow -> { + 0 0 -> 260 + 0 260 -> 39 + 0 39 -> (- 280) + else -> 260 +} +tickleBool -> { + true -> true + false -> true + else -> true +} +may_fail -> { + 43 -> false + else -> false +} +*** END_MODEL +Optimization0.bpl(13,5): Error BP5001: This assertion might not hold. +Execution trace: + Optimization0.bpl(10,5): anon0 +*** MODEL +%lbl%@328 -> false +%lbl%+306 -> true +%lbl%+66 -> true +%lbl%+68 -> true +x@0@@0 -> 24 +ControlFlow -> { + 0 0 -> 306 + 0 306 -> 66 + 0 68 -> (- 328) + 0 66 -> 68 + else -> 306 +} +tickleBool -> { + true -> true + false -> true + else -> true +} +may_fail -> { + 24 -> false + else -> false +} +*** END_MODEL +Optimization0.bpl(25,5): Error BP5001: This assertion might not hold. +Execution trace: + Optimization0.bpl(20,7): anon0 + Optimization0.bpl(21,5): anon3_Else + Optimization0.bpl(24,5): anon2 +*** MODEL +%lbl%@360 -> false +%lbl%+342 -> true +%lbl%+95 -> true +%lbl%+99 -> true +x@0@@1 -> 10 +ControlFlow -> { + 0 0 -> 342 + 0 342 -> 95 + 0 95 -> 99 + 0 99 -> (- 360) + else -> 342 +} +tickleBool -> { + true -> true + false -> true + else -> true +} +*** END_MODEL +Optimization0.bpl(37,5): Error BP5001: This assertion might not hold. +Execution trace: + Optimization0.bpl(32,7): anon0 + Optimization0.bpl(33,5): anon3_LoopHead + Optimization0.bpl(33,5): anon3_LoopDone +*** MODEL +%lbl%@393 -> false +%lbl%+122 -> true +%lbl%+382 -> true +x@0@@2 -> 41 +ControlFlow -> { + 0 0 -> 382 + 0 382 -> 122 + 0 122 -> (- 393) + else -> 382 +} +tickleBool -> { + true -> true + false -> true + else -> true +} +may_fail -> { + 41 -> false + else -> false +} +*** END_MODEL +Optimization0.bpl(47,5): Error BP5001: This assertion might not hold. +Execution trace: + Optimization0.bpl(44,5): anon0 +*** MODEL +%lbl%@421 -> false +%lbl%+147 -> true +%lbl%+151 -> true +%lbl%+399 -> true +x@0@@3 -> 42 +ControlFlow -> { + 0 0 -> 399 + 0 399 -> 147 + 0 147 -> 151 + 0 151 -> (- 421) + else -> 399 +} +tickleBool -> { + true -> true + false -> true + else -> true +} +may_fail -> { + 42 -> false + else -> false +} +*** END_MODEL +Optimization0.bpl(59,5): Error BP5001: This assertion might not hold. +Execution trace: + Optimization0.bpl(54,7): anon0 + Optimization0.bpl(56,11): anon3_Then + Optimization0.bpl(58,5): anon2 +*** MODEL +%lbl%@453 -> false +%lbl%+178 -> true +%lbl%+182 -> true +%lbl%+435 -> true +x@0@@4 -> 1 +ControlFlow -> { + 0 0 -> 435 + 0 435 -> 178 + 0 178 -> 182 + 0 182 -> (- 453) + else -> 435 +} +tickleBool -> { + true -> true + false -> true + else -> true +} +*** END_MODEL +Optimization0.bpl(71,5): Error BP5001: This assertion might not hold. +Execution trace: + Optimization0.bpl(66,7): anon0 + Optimization0.bpl(67,5): anon3_LoopHead + Optimization0.bpl(67,5): anon3_LoopDone +*** MODEL +%lbl%@497 -> false +%lbl%+209 -> true +%lbl%+213 -> true +%lbl%+475 -> true +x@0@@5 -> 2 +ControlFlow -> { + 0 0 -> 475 + 0 475 -> 209 + 0 209 -> 213 + 0 213 -> (- 497) + else -> 475 +} +tickleBool -> { + true -> true + false -> true + else -> true +} +may_fail -> { + 2 -> false + else -> false +} +*** END_MODEL +Optimization0.bpl(83,5): Error BP5001: This assertion might not hold. +Execution trace: + Optimization0.bpl(78,7): anon0 + Optimization0.bpl(80,11): anon3_Then + Optimization0.bpl(82,5): anon2 + +Boogie program verifier finished with 0 verified, 7 errors diff --git a/Test/optimization/Optimization1.bpl b/Test/optimization/Optimization1.bpl new file mode 100644 index 00000000..60df1edd --- /dev/null +++ b/Test/optimization/Optimization1.bpl @@ -0,0 +1,32 @@ +// RUN: %boogie /noVerify /printModel:4 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +procedure test0(n: int) +{ + assume {:minimize} true; +} + +procedure test1(n: int) +{ + assume {:maximize} true; +} + +procedure test2(n: int) +{ + assume {:minimize n, n} true; +} + +procedure test3(n: int) +{ + assume {:maximize n, n} true; +} + +procedure test4(n: int) +{ + assume {:minimize true} true; +} + +procedure test5(n: int) +{ + assume {:maximize true} true; +} diff --git a/Test/optimization/Optimization1.bpl.expect b/Test/optimization/Optimization1.bpl.expect new file mode 100644 index 00000000..d8508807 --- /dev/null +++ b/Test/optimization/Optimization1.bpl.expect @@ -0,0 +1,5 @@ +Optimization1.bpl(6,11): Error: attributes :minimize and :maximize accept only one argument +Optimization1.bpl(11,11): Error: attributes :minimize and :maximize accept only one argument +Optimization1.bpl(16,11): Error: attributes :minimize and :maximize accept only one argument +Optimization1.bpl(21,11): Error: attributes :minimize and :maximize accept only one argument +4 name resolution errors detected in Optimization1.bpl diff --git a/Test/optimization/Optimization2.bpl b/Test/optimization/Optimization2.bpl new file mode 100644 index 00000000..7d80d735 --- /dev/null +++ b/Test/optimization/Optimization2.bpl @@ -0,0 +1,12 @@ +// RUN: %boogie /noVerify /printModel:4 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +procedure test0(n: int) +{ + assume {:minimize true} true; +} + +procedure test1(n: int) +{ + assume {:maximize true} true; +} diff --git a/Test/optimization/Optimization2.bpl.expect b/Test/optimization/Optimization2.bpl.expect new file mode 100644 index 00000000..cab2fd3d --- /dev/null +++ b/Test/optimization/Optimization2.bpl.expect @@ -0,0 +1,3 @@ +Optimization2.bpl(6,11): Error: attributes :minimize and :maximize accept only one argument of type int, real or bv +Optimization2.bpl(11,11): Error: attributes :minimize and :maximize accept only one argument of type int, real or bv +2 type checking errors detected in Optimization2.bpl diff --git a/Test/optimization/lit.local.cfg b/Test/optimization/lit.local.cfg new file mode 100644 index 00000000..158a1e4d --- /dev/null +++ b/Test/optimization/lit.local.cfg @@ -0,0 +1,3 @@ +# Do not run tests in this directory and below +config.unsupported = True +# TODO(wuestholz): Enable these tests once we can rely on Z3 4.4.2 or higher. -- cgit v1.2.3 From c8d15354d033713295a6c55802c6dfe55a95f2b5 Mon Sep 17 00:00:00 2001 From: Valentin Wüstholz Date: Wed, 18 Nov 2015 22:21:47 -0600 Subject: Improve experimental support for optimization (requires Z3 changeset 5948013b1b04d8529bce366c0c7b87e1d88a1827 or later). --- Test/optimization/Optimization0.bpl | 2 +- Test/optimization/Optimization0.bpl.expect | 71 +++++------------------------- 2 files changed, 13 insertions(+), 60 deletions(-) diff --git a/Test/optimization/Optimization0.bpl b/Test/optimization/Optimization0.bpl index c704c855..d000fd79 100644 --- a/Test/optimization/Optimization0.bpl +++ b/Test/optimization/Optimization0.bpl @@ -1,4 +1,4 @@ -// RUN: %boogie /doNotUseLabels /printModel:4 "%s" > "%t" +// RUN: %boogie /printModel:4 "%s" > "%t" // RUN: %diff "%s.expect" "%t" function may_fail(f: int) : bool; diff --git a/Test/optimization/Optimization0.bpl.expect b/Test/optimization/Optimization0.bpl.expect index d2f9b606..f5a51848 100644 --- a/Test/optimization/Optimization0.bpl.expect +++ b/Test/optimization/Optimization0.bpl.expect @@ -3,12 +3,6 @@ %lbl%+260 -> true %lbl%+39 -> true x@0 -> 43 -ControlFlow -> { - 0 0 -> 260 - 0 260 -> 39 - 0 39 -> (- 280) - else -> 260 -} tickleBool -> { true -> true false -> true @@ -23,18 +17,11 @@ Optimization0.bpl(13,5): Error BP5001: This assertion might not hold. Execution trace: Optimization0.bpl(10,5): anon0 *** MODEL -%lbl%@328 -> false -%lbl%+306 -> true +%lbl%@321 -> false +%lbl%+299 -> true %lbl%+66 -> true %lbl%+68 -> true x@0@@0 -> 24 -ControlFlow -> { - 0 0 -> 306 - 0 306 -> 66 - 0 68 -> (- 328) - 0 66 -> 68 - else -> 306 -} tickleBool -> { true -> true false -> true @@ -51,18 +38,11 @@ Execution trace: Optimization0.bpl(21,5): anon3_Else Optimization0.bpl(24,5): anon2 *** MODEL -%lbl%@360 -> false -%lbl%+342 -> true +%lbl%@353 -> false +%lbl%+335 -> true %lbl%+95 -> true %lbl%+99 -> true x@0@@1 -> 10 -ControlFlow -> { - 0 0 -> 342 - 0 342 -> 95 - 0 95 -> 99 - 0 99 -> (- 360) - else -> 342 -} tickleBool -> { true -> true false -> true @@ -75,16 +55,10 @@ Execution trace: Optimization0.bpl(33,5): anon3_LoopHead Optimization0.bpl(33,5): anon3_LoopDone *** MODEL -%lbl%@393 -> false +%lbl%@386 -> false %lbl%+122 -> true -%lbl%+382 -> true +%lbl%+375 -> true x@0@@2 -> 41 -ControlFlow -> { - 0 0 -> 382 - 0 382 -> 122 - 0 122 -> (- 393) - else -> 382 -} tickleBool -> { true -> true false -> true @@ -99,18 +73,11 @@ Optimization0.bpl(47,5): Error BP5001: This assertion might not hold. Execution trace: Optimization0.bpl(44,5): anon0 *** MODEL -%lbl%@421 -> false +%lbl%@414 -> false %lbl%+147 -> true %lbl%+151 -> true -%lbl%+399 -> true +%lbl%+392 -> true x@0@@3 -> 42 -ControlFlow -> { - 0 0 -> 399 - 0 399 -> 147 - 0 147 -> 151 - 0 151 -> (- 421) - else -> 399 -} tickleBool -> { true -> true false -> true @@ -127,18 +94,11 @@ Execution trace: Optimization0.bpl(56,11): anon3_Then Optimization0.bpl(58,5): anon2 *** MODEL -%lbl%@453 -> false +%lbl%@446 -> false %lbl%+178 -> true %lbl%+182 -> true -%lbl%+435 -> true +%lbl%+428 -> true x@0@@4 -> 1 -ControlFlow -> { - 0 0 -> 435 - 0 435 -> 178 - 0 178 -> 182 - 0 182 -> (- 453) - else -> 435 -} tickleBool -> { true -> true false -> true @@ -151,18 +111,11 @@ Execution trace: Optimization0.bpl(67,5): anon3_LoopHead Optimization0.bpl(67,5): anon3_LoopDone *** MODEL -%lbl%@497 -> false +%lbl%@490 -> false %lbl%+209 -> true %lbl%+213 -> true -%lbl%+475 -> true +%lbl%+468 -> true x@0@@5 -> 2 -ControlFlow -> { - 0 0 -> 475 - 0 475 -> 209 - 0 209 -> 213 - 0 213 -> (- 497) - else -> 475 -} tickleBool -> { true -> true false -> true -- cgit v1.2.3 From 51d0b71fa4fd0795c56f1cddbd85b49625c6c91e Mon Sep 17 00:00:00 2001 From: Valentin Wüstholz Date: Thu, 19 Nov 2015 11:09:58 -0600 Subject: Minor changes --- Source/VCExpr/VCExprAST.cs | 4 ++-- Test/optimization/Optimization0.bpl | 2 -- Test/optimization/lit.local.cfg | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Source/VCExpr/VCExprAST.cs b/Source/VCExpr/VCExprAST.cs index 2a06447e..2fbb102c 100644 --- a/Source/VCExpr/VCExprAST.cs +++ b/Source/VCExpr/VCExprAST.cs @@ -341,8 +341,8 @@ namespace Microsoft.Boogie { public static readonly VCExprOp TimeoutDiagnosticsOp = new VCExprCustomOp("timeoutDiagnostics", 1, Type.Bool); - public static readonly VCExprOp MinimizeOp = new VCExprNAryOp(2, Type.Bool); - public static readonly VCExprOp MaximizeOp = new VCExprNAryOp(2, Type.Bool); + public static readonly VCExprOp MinimizeOp = new VCExprCustomOp("minimize##dummy", 2, Type.Bool); + public static readonly VCExprOp MaximizeOp = new VCExprCustomOp("maximize##dummy", 2, Type.Bool); public VCExprOp BoogieFunctionOp(Function func) { Contract.Requires(func != null); diff --git a/Test/optimization/Optimization0.bpl b/Test/optimization/Optimization0.bpl index d000fd79..24424e53 100644 --- a/Test/optimization/Optimization0.bpl +++ b/Test/optimization/Optimization0.bpl @@ -82,5 +82,3 @@ procedure test6() assume {:maximize x} true; assert may_fail(x); } - -// TODO(wuestholz): Make this work without the /doNotUseLabels flag. diff --git a/Test/optimization/lit.local.cfg b/Test/optimization/lit.local.cfg index 158a1e4d..35c7e558 100644 --- a/Test/optimization/lit.local.cfg +++ b/Test/optimization/lit.local.cfg @@ -1,3 +1,3 @@ # Do not run tests in this directory and below config.unsupported = True -# TODO(wuestholz): Enable these tests once we can rely on Z3 4.4.2 or higher. +# TODO(wuestholz): Enable these tests once we can rely on a version of Z3 that includes changeset 5948013b1b04d8529bce366c0c7b87e1d88a1827. -- cgit v1.2.3 From 261c125776d911fda9b545094cb182f3ceb0cdd2 Mon Sep 17 00:00:00 2001 From: Valentin Wüstholz Date: Thu, 19 Nov 2015 11:18:42 -0600 Subject: Add a test. --- Test/optimization/Optimization3.bpl | 20 +++++++++++++++++++ Test/optimization/Optimization3.bpl.expect | 31 ++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 Test/optimization/Optimization3.bpl create mode 100644 Test/optimization/Optimization3.bpl.expect diff --git a/Test/optimization/Optimization3.bpl b/Test/optimization/Optimization3.bpl new file mode 100644 index 00000000..c1dba49c --- /dev/null +++ b/Test/optimization/Optimization3.bpl @@ -0,0 +1,20 @@ +// RUN: %boogie /printModel:4 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +procedure test0() +{ + var x: int; + + assume x < 42; + assume {:maximize x} true; // TODO(wuestholz): Currently, Z3 does not produce a model with "x == 41" here. It seems like this is due to the fact that it returns "unknown". + assert (exists y: int :: y < x); +} + +procedure test1() +{ + var x: int; + + assume x < 42; + assume {:maximize x} true; + assert (forall y: int :: y < x); +} diff --git a/Test/optimization/Optimization3.bpl.expect b/Test/optimization/Optimization3.bpl.expect new file mode 100644 index 00000000..6f308d7d --- /dev/null +++ b/Test/optimization/Optimization3.bpl.expect @@ -0,0 +1,31 @@ +*** MODEL +%lbl%@80 -> false +%lbl%+33 -> true +%lbl%+61 -> true +x -> (- 962) +tickleBool -> { + true -> true + false -> true + else -> true +} +*** END_MODEL +Optimization3.bpl(10,5): Error BP5001: This assertion might not hold. +Execution trace: + Optimization3.bpl(8,5): anon0 +*** MODEL +%lbl%@115 -> false +%lbl%+105 -> true +%lbl%+56 -> true +x@@0 -> 41 +y@@0!1!1 -> 719 +tickleBool -> { + true -> true + false -> true + else -> true +} +*** END_MODEL +Optimization3.bpl(19,5): Error BP5001: This assertion might not hold. +Execution trace: + Optimization3.bpl(17,5): anon0 + +Boogie program verifier finished with 0 verified, 2 errors -- cgit v1.2.3 From 3e37476e15593fe7889b2644bcf389f90f985e35 Mon Sep 17 00:00:00 2001 From: Valentin Wüstholz Date: Thu, 19 Nov 2015 14:19:25 -0600 Subject: Update test output after Nikolaj's enhancement in Z3. --- Test/optimization/Optimization3.bpl | 2 +- Test/optimization/Optimization3.bpl.expect | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Test/optimization/Optimization3.bpl b/Test/optimization/Optimization3.bpl index c1dba49c..02499c31 100644 --- a/Test/optimization/Optimization3.bpl +++ b/Test/optimization/Optimization3.bpl @@ -6,7 +6,7 @@ procedure test0() var x: int; assume x < 42; - assume {:maximize x} true; // TODO(wuestholz): Currently, Z3 does not produce a model with "x == 41" here. It seems like this is due to the fact that it returns "unknown". + assume {:maximize x} true; assert (exists y: int :: y < x); } diff --git a/Test/optimization/Optimization3.bpl.expect b/Test/optimization/Optimization3.bpl.expect index 6f308d7d..6a0066fc 100644 --- a/Test/optimization/Optimization3.bpl.expect +++ b/Test/optimization/Optimization3.bpl.expect @@ -2,7 +2,7 @@ %lbl%@80 -> false %lbl%+33 -> true %lbl%+61 -> true -x -> (- 962) +x -> 41 tickleBool -> { true -> true false -> true -- cgit v1.2.3 From 5bb7ad4c69ed1c782e50272d37bf116007a52f4c Mon Sep 17 00:00:00 2001 From: Valentin Wüstholz Date: Thu, 19 Nov 2015 14:39:36 -0600 Subject: Fix issue with partially-verified assertions. --- Source/Core/AbsyQuant.cs | 10 ++++++++++ Source/VCGeneration/ConditionGeneration.cs | 8 ++++++++ Test/test0/AssertVerifiedUnder0.bpl | 8 ++++++++ Test/test0/AssertVerifiedUnder0.bpl.expect | 3 +++ Test/test1/AssertVerifiedUnder0.bpl | 8 ++++++++ Test/test1/AssertVerifiedUnder0.bpl.expect | 3 +++ 6 files changed, 40 insertions(+) create mode 100644 Test/test0/AssertVerifiedUnder0.bpl create mode 100644 Test/test0/AssertVerifiedUnder0.bpl.expect create mode 100644 Test/test1/AssertVerifiedUnder0.bpl create mode 100644 Test/test1/AssertVerifiedUnder0.bpl.expect diff --git a/Source/Core/AbsyQuant.cs b/Source/Core/AbsyQuant.cs index 39f18657..3a27eddf 100644 --- a/Source/Core/AbsyQuant.cs +++ b/Source/Core/AbsyQuant.cs @@ -344,6 +344,11 @@ namespace Microsoft.Boogie { rc.Error(this, "attributes :minimize and :maximize accept only one argument"); } + if (Key == "verified_under" && Params.Count != 1) + { + rc.Error(this, "attribute :verified_under accepts only one argument"); + } + foreach (object p in Params) { if (p is Expr) { ((Expr)p).Resolve(rc); @@ -364,6 +369,11 @@ namespace Microsoft.Boogie { tc.Error(this, "attributes :minimize and :maximize accept only one argument of type int, real or bv"); break; } + if (Key == "verified_under" && (expr == null || !expr.Type.IsBool)) + { + tc.Error(this, "attribute :verified_under accepts only one argument of type bool"); + break; + } } } public void AddLast(QKeyValue other) { diff --git a/Source/VCGeneration/ConditionGeneration.cs b/Source/VCGeneration/ConditionGeneration.cs index 5971d6f8..6a2eec29 100644 --- a/Source/VCGeneration/ConditionGeneration.cs +++ b/Source/VCGeneration/ConditionGeneration.cs @@ -1549,8 +1549,16 @@ namespace VC { current.ClearParams(); current.AddParam(Substituter.ApplyReplacingOldExprs(incarnationSubst, oldFrameSubst, param)); } + if (current.Key == "verified_under") { + Contract.Assume(current.Params.Count == 1); + var param = current.Params[0] as Expr; + Contract.Assume(param != null && param.Type.IsBool); + current.ClearParams(); + current.AddParam(Substituter.ApplyReplacingOldExprs(incarnationSubst, oldFrameSubst, param)); + } current = current.Next; } + Expr copy = Substituter.ApplyReplacingOldExprs(incarnationSubst, oldFrameSubst, pc.Expr); if (CommandLineOptions.Clo.ModelViewFile != null && pc is AssumeCmd) { string description = QKeyValue.FindStringAttribute(pc.Attributes, "captureState"); diff --git a/Test/test0/AssertVerifiedUnder0.bpl b/Test/test0/AssertVerifiedUnder0.bpl new file mode 100644 index 00000000..1b054f68 --- /dev/null +++ b/Test/test0/AssertVerifiedUnder0.bpl @@ -0,0 +1,8 @@ +// RUN: %boogie -noVerify "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +procedure test0() +{ + assert {:verified_under} true; + assert {:verified_under true, false} true; +} diff --git a/Test/test0/AssertVerifiedUnder0.bpl.expect b/Test/test0/AssertVerifiedUnder0.bpl.expect new file mode 100644 index 00000000..b3d8177d --- /dev/null +++ b/Test/test0/AssertVerifiedUnder0.bpl.expect @@ -0,0 +1,3 @@ +AssertVerifiedUnder0.bpl(6,11): Error: attribute :verified_under accepts only one argument +AssertVerifiedUnder0.bpl(7,11): Error: attribute :verified_under accepts only one argument +2 name resolution errors detected in AssertVerifiedUnder0.bpl diff --git a/Test/test1/AssertVerifiedUnder0.bpl b/Test/test1/AssertVerifiedUnder0.bpl new file mode 100644 index 00000000..e419a5ef --- /dev/null +++ b/Test/test1/AssertVerifiedUnder0.bpl @@ -0,0 +1,8 @@ +// RUN: %boogie -noVerify "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +procedure test0() +{ + assert {:verified_under 4} true; + assert {:verified_under 3.0} true; +} diff --git a/Test/test1/AssertVerifiedUnder0.bpl.expect b/Test/test1/AssertVerifiedUnder0.bpl.expect new file mode 100644 index 00000000..6d3c04cd --- /dev/null +++ b/Test/test1/AssertVerifiedUnder0.bpl.expect @@ -0,0 +1,3 @@ +AssertVerifiedUnder0.bpl(6,11): Error: attribute :verified_under accepts only one argument of type bool +AssertVerifiedUnder0.bpl(7,11): Error: attribute :verified_under accepts only one argument of type bool +2 type checking errors detected in AssertVerifiedUnder0.bpl -- cgit v1.2.3 From f19110ec25d4ee962558627150d22e4c8a26a2a0 Mon Sep 17 00:00:00 2001 From: Valentin Wüstholz Date: Fri, 20 Nov 2015 11:24:17 -0600 Subject: Add simplified may-unverified analysis and instrumentation. --- Source/Core/Absy.cs | 2 + Source/VCGeneration/VC.cs | 189 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 191 insertions(+) diff --git a/Source/Core/Absy.cs b/Source/Core/Absy.cs index 8be4f24e..8a8558bf 100644 --- a/Source/Core/Absy.cs +++ b/Source/Core/Absy.cs @@ -2241,6 +2241,7 @@ namespace Microsoft.Boogie { } public class Incarnation : LocalVariable { public int incarnationNumber; + public readonly Variable OriginalVariable; public Incarnation(Variable/*!*/ var, int i) : base( var.tok, @@ -2248,6 +2249,7 @@ namespace Microsoft.Boogie { ) { Contract.Requires(var != null); incarnationNumber = i; + OriginalVariable = var; } } diff --git a/Source/VCGeneration/VC.cs b/Source/VCGeneration/VC.cs index 2762fc72..ad067c04 100644 --- a/Source/VCGeneration/VC.cs +++ b/Source/VCGeneration/VC.cs @@ -2830,6 +2830,11 @@ namespace VC { mvInfo = new ModelViewInfo(program, impl); Convert2PassiveCmd(impl, mvInfo); + if (QKeyValue.FindBoolAttribute(impl.Attributes, "may_unverified_instrumentation")) + { + InstrumentWithMayUnverifiedConditions(impl, exitBlock); + } + #region Peep-hole optimizations if (CommandLineOptions.Clo.RemoveEmptyBlocks){ #region Get rid of empty blocks @@ -2865,6 +2870,190 @@ namespace VC { return gotoCmdOrigins; } + #region Simplified May-Unverified Analysis and Instrumentation + + static void InstrumentWithMayUnverifiedConditions(Implementation impl, Block unifiedExitBlock) + { + var q = new Queue(); + q.Enqueue(unifiedExitBlock); + var conditionOnBlockEntry = new Dictionary>(); + while (q.Any()) + { + var block = q.Dequeue(); + + if (conditionOnBlockEntry.ContainsKey(block)) + { + continue; + } + + var gotoCmd = block.TransferCmd as GotoCmd; + if (gotoCmd != null && gotoCmd.labelTargets.Any(b => !conditionOnBlockEntry.ContainsKey(b))) + { + q.Enqueue(block); + continue; + } + + HashSet cond = new HashSet(); + if (gotoCmd != null) + { + var mayInstrs = new List(); + bool noInstr = true; + foreach (var succ in gotoCmd.labelTargets) + { + var c = conditionOnBlockEntry[succ]; + if (c != null) + { + mayInstrs.Add(succ); + } + else + { + noInstr = false; + } + cond = JoinVariableSets(cond, c); + } + if (!noInstr) + { + foreach (var instr in mayInstrs) + { + InstrumentWithCondition(instr, 0, conditionOnBlockEntry[instr]); + } + } + } + + for (int i = block.Cmds.Count - 1; 0 <= i; i--) + { + var cmd = block.Cmds[i]; + if (cond == null) { break; } + + var assertCmd = cmd as AssertCmd; + if (assertCmd != null) + { + var litExpr = assertCmd.Expr as LiteralExpr; + if (litExpr != null && litExpr.IsTrue) + { + continue; + } + + HashSet vu = null; + if (assertCmd.VerifiedUnder == null) + { + vu = null; + } + else + { + HashSet vars; + if (IsConjunctionOfAssumptionVariables(assertCmd.VerifiedUnder, out vars)) + { + vu = vars; + // TODO(wuestholz): Maybe drop the :verified_under attribute. + } + else + { + vu = null; + } + } + + if (vu == null) + { + InstrumentWithCondition(block, i + 1, cond); + } + + cond = JoinVariableSets(cond, vu); + } + } + + if (cond != null && block.Predecessors.Count == 0) + { + // TODO(wuestholz): Should we rather instrument each block? + InstrumentWithCondition(block, 0, cond); + } + + foreach (var pred in block.Predecessors) + { + q.Enqueue(pred); + } + + conditionOnBlockEntry[block] = cond; + } + } + + private static void InstrumentWithCondition(Block block, int idx, HashSet condition) + { + var conj = Expr.BinaryTreeAnd(condition.Select(v => (Expr)new IdentifierExpr(Token.NoToken, v)).ToList()); + block.Cmds.Insert(idx, new AssumeCmd(Token.NoToken, Expr.Not(conj))); + } + + static HashSet JoinVariableSets(HashSet c0, HashSet c1) + { + // We use the following lattice: + // - Top: null (i.e., true) + // - Bottom: new HashSet() (i.e., false) + // - Other Elements: new HashSet(...) (i.e., conjunctions of assumption variables) + + if (c0 == null || c1 == null) + { + return null; + } + var result = new HashSet(c0); + result.UnionWith(c1); + return result; + } + + static bool IsAssumptionVariableOrIncarnation(Variable v) + { + if (QKeyValue.FindBoolAttribute(v.Attributes, "assumption")) { return true; } + var incar = v as Incarnation; + return incar == null || QKeyValue.FindBoolAttribute(incar.OriginalVariable.Attributes, "assumption"); + } + + static bool IsConjunctionOfAssumptionVariables(Expr expr, out HashSet variables) + { + Contract.Requires(expr != null); + + variables = null; + var litExpr = expr as LiteralExpr; + if (litExpr != null && (litExpr.IsFalse || litExpr.IsTrue)) + { + if (litExpr.IsTrue) + { + variables = new HashSet(); + } + return true; + } + + var idExpr = expr as IdentifierExpr; + if (idExpr != null && IsAssumptionVariableOrIncarnation(idExpr.Decl)) + { + variables = new HashSet(); + variables.Add(idExpr.Decl); + return true; + } + + var andExpr = expr as NAryExpr; + if (andExpr != null) + { + var fun = andExpr.Fun as BinaryOperator; + if (fun != null && fun.Op == BinaryOperator.Opcode.And && andExpr.Args != null) + { + bool res = true; + variables = new HashSet(); + foreach (var op in andExpr.Args) + { + HashSet vars; + var r = IsConjunctionOfAssumptionVariables(op, out vars); + res &= r; + variables = JoinVariableSets(variables, vars); + if (!res) { break; } + } + return res; + } + } + + return false; + } + + #endregion + private static void HandleSelectiveChecking(Implementation impl) { if (QKeyValue.FindBoolAttribute(impl.Attributes, "selective_checking") || -- cgit v1.2.3 From fe8de9444fb6ce90216a9b8268d8a13daa9a9f2a Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Sat, 21 Nov 2015 18:27:06 +0000 Subject: Teach the TravisCI build to use the ``travis_retry`` command when using NuGet. The motivation here is that the ``nuget`` command is a little flakey (sometimes it times out) in the legacy TravisCI infrastructure. This command will retry the command passed to it a few times. Hopefully this will stop the random build failures we've been having. --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index c5d0c21d..11ed4d64 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,12 +10,13 @@ install: # FIXME: We should not be using GPUVerify's repo for Z3 - sudo sh -c 'echo "deb http://ppa.launchpad.net/delcypher/gpuverify-smt/ubuntu precise main" > /etc/apt/sources.list.d/smt.list' - sudo apt-get update - - nuget restore ${TRAVIS_SOLUTION} + # NuGet is a little flakey in legacy TravisCI, use travis_retry command to retry the command if it fails + - travis_retry nuget restore ${TRAVIS_SOLUTION} # Install Z3 - sudo apt-get -y install z3=4.3.2-0~precise2 # Install needed python tools - sudo pip install lit OutputCheck pyyaml - - mkdir -p Source/packages && cd Source/packages && nuget install NUnit.Runners -Version 2.6.3 + - mkdir -p Source/packages && cd Source/packages && travis_retry nuget install NUnit.Runners -Version 2.6.3 - cd ../../ script: - xbuild /p:Configuration=${BOOGIE_CONFIG} ${TRAVIS_SOLUTION} -- cgit v1.2.3 From 280cb724b7499ed4f09f9a54e5ae457b1eb254ae Mon Sep 17 00:00:00 2001 From: qunyanm Date: Wed, 25 Nov 2015 11:50:43 -0800 Subject: Use the EndCurly token when creating the ReturnCmd for unifiedExit When there are more than one exit blocks, an unified exit block is created which includes a ReturnCmd. However, the ReturnCmd is created with NoToken. This causes the line/column number reported for a failed postcondition to be (0,0). The right token should be the EndCurly since the ReturnCmd is in the exit block. --- Source/VCGeneration/ConditionGeneration.cs | 6 +----- Test/test2/BadLineNumber.bpl | 15 +++++++++++++++ Test/test2/BadLineNumber.bpl.expect | 7 +++++++ 3 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 Test/test2/BadLineNumber.bpl create mode 100644 Test/test2/BadLineNumber.bpl.expect diff --git a/Source/VCGeneration/ConditionGeneration.cs b/Source/VCGeneration/ConditionGeneration.cs index 0e5fce82..043d0985 100644 --- a/Source/VCGeneration/ConditionGeneration.cs +++ b/Source/VCGeneration/ConditionGeneration.cs @@ -1134,11 +1134,7 @@ namespace VC { if (returnBlocks > 1) { string unifiedExitLabel = "GeneratedUnifiedExit"; Block unifiedExit; - if (impl.StructuredStmts != null) { - unifiedExit = new Block(new Token(-17, -4), unifiedExitLabel, new List(), new ReturnCmd(impl.StructuredStmts.EndCurly)); - } else { - unifiedExit = new Block(new Token(-17, -4), unifiedExitLabel, new List(), new ReturnCmd(Token.NoToken)); - } + unifiedExit = new Block(new Token(-17, -4), unifiedExitLabel, new List(), new ReturnCmd(impl.StructuredStmts != null ? impl.StructuredStmts.EndCurly : Token.NoToken)); Contract.Assert(unifiedExit != null); foreach (Block b in impl.Blocks) { if (b.TransferCmd is ReturnCmd) { diff --git a/Test/test2/BadLineNumber.bpl b/Test/test2/BadLineNumber.bpl new file mode 100644 index 00000000..b8776a4e --- /dev/null +++ b/Test/test2/BadLineNumber.bpl @@ -0,0 +1,15 @@ +// RUN: %boogie "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +procedure p(); + ensures false; + +implementation p() +{ + if (*) + { + } + else + { + } +} \ No newline at end of file diff --git a/Test/test2/BadLineNumber.bpl.expect b/Test/test2/BadLineNumber.bpl.expect new file mode 100644 index 00000000..bc5d1984 --- /dev/null +++ b/Test/test2/BadLineNumber.bpl.expect @@ -0,0 +1,7 @@ +BadLineNumber.bpl(15,1): Error BP5003: A postcondition might not hold on this return path. +BadLineNumber.bpl(5,3): Related location: This is the postcondition that might not hold. +Execution trace: + BadLineNumber.bpl(9,5): anon0 + BadLineNumber.bpl(14,5): anon3_Else + +Boogie program verifier finished with 0 verified, 1 error -- cgit v1.2.3 From c03c5ba246565fcb3b16630051c6235f06c4bef8 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Tue, 1 Dec 2015 17:01:22 +0000 Subject: Teach TravisCI to use Z3 4.4.1 from repositories that I'm currently maintaining on the OpenSUSE build service. See https://build.opensuse.org/package/show/home:delcypher:z3/z3 --- .travis.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 11ed4d64..41ee569f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,14 +6,15 @@ env: - BOOGIE_CONFIG=Debug - BOOGIE_CONFIG=Release install: - - sudo apt-key adv --recv-keys --keyserver keyserver.ubuntu.com C504E590 - # FIXME: We should not be using GPUVerify's repo for Z3 - - sudo sh -c 'echo "deb http://ppa.launchpad.net/delcypher/gpuverify-smt/ubuntu precise main" > /etc/apt/sources.list.d/smt.list' + - wget http://download.opensuse.org/repositories/home:delcypher:z3/xUbuntu_12.04/Release.key + - sudo apt-key add - < Release.key + # Use Z3 package built by the OpenSUSE build service https://build.opensuse.org/package/show/home:delcypher:z3/z3 + - sudo sh -c "echo 'deb http://download.opensuse.org/repositories/home:/delcypher:/z3/xUbuntu_12.04/ /' >> /etc/apt/sources.list.d/z3.list" - sudo apt-get update # NuGet is a little flakey in legacy TravisCI, use travis_retry command to retry the command if it fails - travis_retry nuget restore ${TRAVIS_SOLUTION} - # Install Z3 - - sudo apt-get -y install z3=4.3.2-0~precise2 + # Install Z3 executable + - sudo apt-get -y install 'z3=4.4.1-*' # Install needed python tools - sudo pip install lit OutputCheck pyyaml - mkdir -p Source/packages && cd Source/packages && travis_retry nuget install NUnit.Runners -Version 2.6.3 -- cgit v1.2.3 From 75b5befa9f82f6fd54817d9cec20522af1a797a6 Mon Sep 17 00:00:00 2001 From: Valentin Wüstholz Date: Thu, 19 Nov 2015 18:23:03 -0600 Subject: Update test output for Z3 4.4.1. --- README.md | 2 +- Test/livevars/daytona_bug2_ioctl_example_2.bpl.expect | 3 ++- Test/test15/CaptureState.bpl.expect | 18 +++++++++--------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index a976b249..224d7196 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ You can also report issues on our [issue tracker](https://github.com/boogie-org/ ### Requirements - [NuGet](https://www.nuget.org/) -- [Z3](https://github.com/Z3Prover/z3) 4.3.2 or [CVC4](http://cvc4.cs.nyu.edu/web/) **FIXME_VERSION** (note +- [Z3](https://github.com/Z3Prover/z3) 4.4.1 or [CVC4](http://cvc4.cs.nyu.edu/web/) **FIXME_VERSION** (note CVC4 support is experimental) #### Windows specific diff --git a/Test/livevars/daytona_bug2_ioctl_example_2.bpl.expect b/Test/livevars/daytona_bug2_ioctl_example_2.bpl.expect index 9f960f26..3c0d0b20 100644 --- a/Test/livevars/daytona_bug2_ioctl_example_2.bpl.expect +++ b/Test/livevars/daytona_bug2_ioctl_example_2.bpl.expect @@ -127,7 +127,8 @@ Execution trace: daytona_bug2_ioctl_example_2.bpl(3832,3): inline$storm_IoCancelIrp$0$anon10#2 daytona_bug2_ioctl_example_2.bpl(3892,3): inline$storm_IoAcquireCancelSpinLock$0$label_11_true#2 daytona_bug2_ioctl_example_2.bpl(3902,3): inline$storm_IoAcquireCancelSpinLock$0$anon8_Else#2 - daytona_bug2_ioctl_example_2.bpl(3928,3): inline$storm_IoAcquireCancelSpinLock$0$anon9_Then#2 + daytona_bug2_ioctl_example_2.bpl(3915,3): inline$storm_IoAcquireCancelSpinLock$0$anon9_Else#2 + daytona_bug2_ioctl_example_2.bpl(3923,3): inline$storm_IoAcquireCancelSpinLock$0$anon10_Then#2 daytona_bug2_ioctl_example_2.bpl(3933,3): inline$storm_IoAcquireCancelSpinLock$0$anon5#2 daytona_bug2_ioctl_example_2.bpl(3944,3): inline$storm_IoAcquireCancelSpinLock$0$anon11_Then#2 daytona_bug2_ioctl_example_2.bpl(3949,3): inline$storm_IoAcquireCancelSpinLock$0$anon7#2 diff --git a/Test/test15/CaptureState.bpl.expect b/Test/test15/CaptureState.bpl.expect index 5d9d41c5..6939fee4 100644 --- a/Test/test15/CaptureState.bpl.expect +++ b/Test/test15/CaptureState.bpl.expect @@ -14,17 +14,17 @@ $mv_state_const -> 3 F -> T@FieldName!val!0 Heap -> |T@[Ref,FieldName]Int!val!0| m -> **m -m@0 -> (- 276) -m@1 -> (- 275) -m@3 -> (- 275) +m@0 -> (- 2) +m@1 -> (- 1) +m@3 -> (- 1) r -> **r -r@0 -> (- 550) +r@0 -> (- 2) this -> T@Ref!val!0 x -> 719 y -> **y Select_[Ref,FieldName]$int -> { - |T@[Ref,FieldName]Int!val!0| T@Ref!val!0 T@FieldName!val!0 -> (- 276) - else -> (- 276) + |T@[Ref,FieldName]Int!val!0| T@Ref!val!0 T@FieldName!val!0 -> (- 2) + else -> (- 2) } $mv_state -> { 3 0 -> true @@ -49,13 +49,13 @@ tickleBool -> { *** STATE top *** END_STATE *** STATE then - m -> (- 276) + m -> (- 2) *** END_STATE *** STATE postUpdate0 - m -> (- 275) + m -> (- 1) *** END_STATE *** STATE end - r -> (- 550) + r -> (- 2) m -> 7 *** END_STATE *** END_MODEL -- cgit v1.2.3 From f97656456a74564cdf96633c3f46223d39e2c6f2 Mon Sep 17 00:00:00 2001 From: qunyanm Date: Mon, 16 Nov 2015 11:03:28 -0800 Subject: Use the EndCurly token when creating the ReturnCmd for unifiedExit When there are more than one exit blocks, an unified exit block is created which includes a ReturnCmd. However, the ReturnCmd is created with NoToken. This causes the line/column number reported for a failed postcondition to be (0,0). The right token should be the EndCurly since the ReturnCmd is in the exit block. --- Source/VCGeneration/ConditionGeneration.cs | 3 ++- Test/test2/BadLineNumber.bpl | 15 +++++++++++++++ Test/test2/BadLineNumber.bpl.expect | 7 +++++++ 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 Test/test2/BadLineNumber.bpl create mode 100644 Test/test2/BadLineNumber.bpl.expect diff --git a/Source/VCGeneration/ConditionGeneration.cs b/Source/VCGeneration/ConditionGeneration.cs index 6a2eec29..19438924 100644 --- a/Source/VCGeneration/ConditionGeneration.cs +++ b/Source/VCGeneration/ConditionGeneration.cs @@ -1133,7 +1133,8 @@ namespace VC { } if (returnBlocks > 1) { string unifiedExitLabel = "GeneratedUnifiedExit"; - Block unifiedExit = new Block(new Token(-17, -4), unifiedExitLabel, new List(), new ReturnCmd(Token.NoToken)); + Block unifiedExit; + unifiedExit = new Block(new Token(-17, -4), unifiedExitLabel, new List(), new ReturnCmd(impl.StructuredStmts != null ? impl.StructuredStmts.EndCurly : Token.NoToken)); Contract.Assert(unifiedExit != null); foreach (Block b in impl.Blocks) { if (b.TransferCmd is ReturnCmd) { diff --git a/Test/test2/BadLineNumber.bpl b/Test/test2/BadLineNumber.bpl new file mode 100644 index 00000000..b8776a4e --- /dev/null +++ b/Test/test2/BadLineNumber.bpl @@ -0,0 +1,15 @@ +// RUN: %boogie "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +procedure p(); + ensures false; + +implementation p() +{ + if (*) + { + } + else + { + } +} \ No newline at end of file diff --git a/Test/test2/BadLineNumber.bpl.expect b/Test/test2/BadLineNumber.bpl.expect new file mode 100644 index 00000000..bc5d1984 --- /dev/null +++ b/Test/test2/BadLineNumber.bpl.expect @@ -0,0 +1,7 @@ +BadLineNumber.bpl(15,1): Error BP5003: A postcondition might not hold on this return path. +BadLineNumber.bpl(5,3): Related location: This is the postcondition that might not hold. +Execution trace: + BadLineNumber.bpl(9,5): anon0 + BadLineNumber.bpl(14,5): anon3_Else + +Boogie program verifier finished with 0 verified, 1 error -- cgit v1.2.3 From 3e4a58021e7f9b2bd31dd195f9a7b2f02d5b4c02 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Wed, 2 Dec 2015 09:27:06 +0000 Subject: TravisCI build icon should only show build status for master branch, not all branches. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 224d7196..1dd0ba5a 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ |-------------------------------|---------------------------------| | [![linux build status][1]][2] | [![windows_build_status][3]][4] | -[1]: https://travis-ci.org/boogie-org/boogie.svg +[1]: https://travis-ci.org/boogie-org/boogie.svg?branch=master [2]: https://travis-ci.org/boogie-org/boogie [3]: https://pmbuilds.inf.ethz.ch/buildStatus/icon?job=boogie [4]: #FIXME -- cgit v1.2.3 From 9b355c4e494bc6427e3d165288c09e113a86000a Mon Sep 17 00:00:00 2001 From: Valentin Wüstholz Date: Wed, 2 Dec 2015 12:02:43 -0600 Subject: Remove workaround for older versions of Z3. --- Source/VCGeneration/Check.cs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/Source/VCGeneration/Check.cs b/Source/VCGeneration/Check.cs index da445a00..8c1ae407 100644 --- a/Source/VCGeneration/Check.cs +++ b/Source/VCGeneration/Check.cs @@ -356,16 +356,8 @@ namespace Microsoft.Boogie { hasOutput = false; outputExn = null; this.handler = handler; - - if (namedAssumeVars != null && namedAssumeVars.Any()) - { - // TODO(wuestholz): Avoid doing a full reset. This is currently necessary for old versions of Z3 due to a bug. - thmProver.FullReset(gen); - } - else - { - thmProver.Reset(gen); - } + + thmProver.Reset(gen); SetTimeout(); proverStart = DateTime.UtcNow; thmProver.NamedAssumeVars = namedAssumeVars; -- cgit v1.2.3 From 9f50f449bef2aeed660a8e3c0b5eb73ad7fe1e78 Mon Sep 17 00:00:00 2001 From: Valentin Wüstholz Date: Thu, 3 Dec 2015 09:37:45 -0600 Subject: Remove unused directory. --- Build/CodePlex.Tools.MsBuild.dll | Bin 131072 -> 0 bytes Build/CodePlex.Tools.Wiki.dll | Bin 45056 -> 0 bytes Build/updateVersionFile.xml | 19 ------------------- 3 files changed, 19 deletions(-) delete mode 100644 Build/CodePlex.Tools.MsBuild.dll delete mode 100644 Build/CodePlex.Tools.Wiki.dll delete mode 100644 Build/updateVersionFile.xml diff --git a/Build/CodePlex.Tools.MsBuild.dll b/Build/CodePlex.Tools.MsBuild.dll deleted file mode 100644 index 2e400e8e..00000000 Binary files a/Build/CodePlex.Tools.MsBuild.dll and /dev/null differ diff --git a/Build/CodePlex.Tools.Wiki.dll b/Build/CodePlex.Tools.Wiki.dll deleted file mode 100644 index 9ea2bea8..00000000 Binary files a/Build/CodePlex.Tools.Wiki.dll and /dev/null differ diff --git a/Build/updateVersionFile.xml b/Build/updateVersionFile.xml deleted file mode 100644 index cb083c7a..00000000 --- a/Build/updateVersionFile.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - -- cgit v1.2.3 From 8938d00be93e780d54917c1448bc534702766fdf Mon Sep 17 00:00:00 2001 From: Valentin Wüstholz Date: Thu, 3 Dec 2015 09:39:44 -0600 Subject: Remove unused file. --- Source/version.ssc | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 Source/version.ssc diff --git a/Source/version.ssc b/Source/version.ssc deleted file mode 100644 index 5bdcf170..00000000 --- a/Source/version.ssc +++ /dev/null @@ -1,12 +0,0 @@ -// ==++== -// -// -// -// ==--== -// Warning: Automatically generated file. DO NOT EDIT -// Generated at Dienstag, 5. Juli 2011 11:26:45 - -using System.Reflection; -[assembly: AssemblyVersion("2.2.30705.1126")] -[assembly: AssemblyFileVersion("2.2.30705.1126")] - -- cgit v1.2.3 From 1c16dc829c81426e17427c0d103ed831a48f1f81 Mon Sep 17 00:00:00 2001 From: Valentin Wüstholz Date: Sun, 27 Dec 2015 15:08:38 -0600 Subject: Enable optimization for more prover queries. --- Source/Provers/SMTLib/ProverInterface.cs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/Source/Provers/SMTLib/ProverInterface.cs b/Source/Provers/SMTLib/ProverInterface.cs index ca530da2..b8fd2f50 100644 --- a/Source/Provers/SMTLib/ProverInterface.cs +++ b/Source/Provers/SMTLib/ProverInterface.cs @@ -421,13 +421,7 @@ namespace Microsoft.Boogie.SMTLib SendThisVC(vcString); - if (options.Solver == SolverKind.Z3 && 0 < OptimizationRequests.Count) - { - foreach (var r in OptimizationRequests) - { - SendThisVC(r); - } - } + SendOptimizationRequests(); FlushLogFile(); @@ -442,6 +436,17 @@ namespace Microsoft.Boogie.SMTLib FlushLogFile(); } + private void SendOptimizationRequests() + { + if (options.Solver == SolverKind.Z3 && 0 < OptimizationRequests.Count) + { + foreach (var r in OptimizationRequests) + { + SendThisVC(r); + } + } + } + public override void Reset(VCExpressionGenerator gen) { if (options.Solver == SolverKind.Z3) @@ -2137,6 +2142,7 @@ namespace Microsoft.Boogie.SMTLib public override void Assert(VCExpr vc, bool polarity) { + OptimizationRequests.Clear(); string a = ""; if (polarity) { @@ -2148,6 +2154,7 @@ namespace Microsoft.Boogie.SMTLib } AssertAxioms(); SendThisVC(a); + SendOptimizationRequests(); } public override void DefineMacro(Macro f, VCExpr vc) { -- cgit v1.2.3 From dd8e69b7b71a9375b7206a70633deae234175ef8 Mon Sep 17 00:00:00 2001 From: Valentin Wüstholz Date: Mon, 28 Dec 2015 20:22:03 -0600 Subject: Improve precision of abstract interpreter for modulo operations. --- Source/AbsInt/IntervalDomain.cs | 4 ++++ Test/aitest0/Intervals.bpl | 15 +++++++++++++++ Test/aitest0/Intervals.bpl.expect | 2 +- 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/Source/AbsInt/IntervalDomain.cs b/Source/AbsInt/IntervalDomain.cs index ee9d632b..2fd37463 100644 --- a/Source/AbsInt/IntervalDomain.cs +++ b/Source/AbsInt/IntervalDomain.cs @@ -884,6 +884,10 @@ namespace Microsoft.Boogie.AbstractInterpretation if (lo0 != null && lo1 != null && 0 <= (BigInteger)lo0 && 0 <= (BigInteger)lo1) { Lo = BigInteger.Zero; Hi = hi1; + if (lo0 < lo1 && hi0 != null && hi0 < lo1) { + Lo = lo0; + Hi = hi0; + } } break; case BinaryOperator.Opcode.RealDiv: diff --git a/Test/aitest0/Intervals.bpl b/Test/aitest0/Intervals.bpl index fddce05a..8d40b81d 100644 --- a/Test/aitest0/Intervals.bpl +++ b/Test/aitest0/Intervals.bpl @@ -332,3 +332,18 @@ procedure W0(N: real) assert N - i <= bf0 - 1.0; } } + +// mod + +procedure Mod0(n: int) + requires 10 < n; +{ + var i: int; + + i := 0; + while (i < 10) + { + i := (i mod n) + 1; + } + assert i == 10; +} diff --git a/Test/aitest0/Intervals.bpl.expect b/Test/aitest0/Intervals.bpl.expect index a0769ec5..980593a9 100644 --- a/Test/aitest0/Intervals.bpl.expect +++ b/Test/aitest0/Intervals.bpl.expect @@ -54,4 +54,4 @@ Execution trace: Intervals.bpl(303,3): anon3_LoopHead Intervals.bpl(303,3): anon3_LoopDone -Boogie program verifier finished with 16 verified, 11 errors +Boogie program verifier finished with 17 verified, 11 errors -- cgit v1.2.3 From 5d23ab3bf5bc80ee1bf5bbc6194a6de67264c61f Mon Sep 17 00:00:00 2001 From: Valentin Wüstholz Date: Mon, 28 Dec 2015 20:28:04 -0600 Subject: Fix issue with ids for assume-statements. --- Source/Provers/SMTLib/ProverInterface.cs | 5 +++-- Source/Provers/SMTLib/TypeDeclCollector.cs | 5 ++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Source/Provers/SMTLib/ProverInterface.cs b/Source/Provers/SMTLib/ProverInterface.cs index b8fd2f50..7e98e8f8 100644 --- a/Source/Provers/SMTLib/ProverInterface.cs +++ b/Source/Provers/SMTLib/ProverInterface.cs @@ -411,10 +411,11 @@ namespace Microsoft.Boogie.SMTLib SendThisVC("(push 1)"); SendThisVC("(set-info :boogie-vc-id " + SMTLibNamer.QuoteId(descriptiveName) + ")"); - if (NamedAssumeVars != null) + if (CommandLineOptions.Clo.PrintNecessaryAssumes && NamedAssumeVars != null) { foreach (var v in NamedAssumeVars) { + SendThisVC(string.Format("(declare-fun {0} () Bool)", v)); SendThisVC(string.Format("(assert (! {0} :named {1}))", v, "aux$$" + v.Name)); } } @@ -1292,7 +1293,7 @@ namespace Microsoft.Boogie.SMTLib var reporter = handler as VC.VCGen.ErrorReporter; // TODO(wuestholz): Is the reporter ever null? - if (NamedAssumeVars != null && NamedAssumeVars.Any() && result == Outcome.Valid && reporter != null) + if (CommandLineOptions.Clo.PrintNecessaryAssumes && NamedAssumeVars != null && NamedAssumeVars.Any() && result == Outcome.Valid && reporter != null) { SendThisVC("(get-unsat-core)"); var resp = Process.GetProverResponse(); diff --git a/Source/Provers/SMTLib/TypeDeclCollector.cs b/Source/Provers/SMTLib/TypeDeclCollector.cs index 32e28560..1c23c22f 100644 --- a/Source/Provers/SMTLib/TypeDeclCollector.cs +++ b/Source/Provers/SMTLib/TypeDeclCollector.cs @@ -255,7 +255,10 @@ void ObjectInvariant() RegisterType(node.Type); string decl = "(declare-fun " + printedName + " () " + TypeToString(node.Type) + ")"; - AddDeclaration(decl); + if (!printedName.StartsWith("assume$$")) + { + AddDeclaration(decl); + } KnownVariables.Add(node); if(declHandler != null) declHandler.VarDecl(node); -- cgit v1.2.3 From 2dafee57c84cd8d3bdccdba1bc348936a9548b94 Mon Sep 17 00:00:00 2001 From: Valentin Wüstholz Date: Tue, 29 Dec 2015 12:15:46 -0600 Subject: Minor change --- Source/VCGeneration/Wlp.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VCGeneration/Wlp.cs b/Source/VCGeneration/Wlp.cs index 741ed723..508a1400 100644 --- a/Source/VCGeneration/Wlp.cs +++ b/Source/VCGeneration/Wlp.cs @@ -193,7 +193,7 @@ namespace VC { var expr = ctxt.Ctxt.BoogieExprTranslator.Translate(ac.Expr); var aid = QKeyValue.FindStringAttribute(ac.Attributes, "id"); - if (aid != null && namedAssumeVars != null) + if (CommandLineOptions.Clo.PrintNecessaryAssumes && aid != null && namedAssumeVars != null) { var v = gen.Variable("assume$$" + aid, Microsoft.Boogie.Type.Bool); namedAssumeVars.Add(v); -- cgit v1.2.3 From 24a49931e1c7d456008296e1255d8506d28cb18b Mon Sep 17 00:00:00 2001 From: Shaz Qadeer Date: Fri, 8 Jan 2016 11:36:11 -0800 Subject: added Free code --- Test/civl/alloc.bpl | 69 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 26 deletions(-) diff --git a/Test/civl/alloc.bpl b/Test/civl/alloc.bpl index 33535b00..56515ba8 100644 --- a/Test/civl/alloc.bpl +++ b/Test/civl/alloc.bpl @@ -11,21 +11,26 @@ axiom (forall x: [int]bool, y: [int]int :: {cons(x,y)} dom(cons(x, y)) == x && m function EmptyLmap(): (lmap); axiom (dom(EmptyLmap()) == MapConstBool(false)); -function Add(x: lmap, i: int, v: int): (lmap); -axiom (forall x: lmap, i: int, v: int :: dom(Add(x, i, v)) == dom(x)[i:=true] && map(Add(x, i, v)) == map(x)[i := v]); +function Add(x: lmap, i: int): (lmap); +axiom (forall x: lmap, i: int :: dom(Add(x, i)) == dom(x)[i:=true] && map(Add(x, i)) == map(x)); function Remove(x: lmap, i: int): (lmap); axiom (forall x: lmap, i: int :: dom(Remove(x, i)) == dom(x)[i:=false] && map(Remove(x, i)) == map(x)); +function {:inline} PoolInv(unallocated:[int]bool, pool: lmap) : (bool) +{ + (forall x: int :: unallocated[x] ==> dom(pool)[x]) +} + procedure {:yields} {:layer 2} Main() -requires {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); -ensures {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); +requires {:layer 1} PoolInv(unallocated, pool); +ensures {:layer 1} PoolInv(unallocated, pool); { var {:layer 1} {:linear "mem"} l: lmap; var i: int; par Yield() | Dummy(); while (*) - invariant {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); + invariant {:layer 1} PoolInv(unallocated, pool); { call l, i := Alloc(); async call Thread(l, i); @@ -35,8 +40,8 @@ ensures {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); } procedure {:yields} {:layer 2} Thread({:layer 1} {:linear_in "mem"} local_in: lmap, i: int) -requires {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); -ensures {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); +requires {:layer 1} PoolInv(unallocated, pool); +ensures {:layer 1} PoolInv(unallocated, pool); requires {:layer 1} dom(local_in)[i] && map(local_in)[i] == mem[i]; requires {:layer 2} dom(local_in)[i]; { @@ -50,13 +55,13 @@ requires {:layer 2} dom(local_in)[i]; call o := Read(local, i); assert {:layer 2} o == 42; while (*) - invariant {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); + invariant {:layer 1} PoolInv(unallocated, pool); { call l, y := Alloc(); call l := Write(l, y, 42); call o := Read(l, y); assert {:layer 2} o == 42; - call Free(l); + call Free(l, y); par Yield() | Dummy(); } par Yield() | Dummy(); @@ -68,8 +73,8 @@ procedure {:pure} {:inline 1} Copy({:linear_in "mem"} l: lmap) returns ({:linear } procedure {:yields} {:layer 1,2} Alloc() returns ({:layer 1} {:linear "mem"} l: lmap, i: int) -requires {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); -ensures {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); +requires {:layer 1} PoolInv(unallocated, pool); +ensures {:layer 1} PoolInv(unallocated, pool); ensures {:layer 1} dom(l)[i] && map(l)[i] == mem[i]; ensures {:right} |{ A: assume dom(l)[i]; return true; }|; { @@ -79,17 +84,21 @@ ensures {:right} |{ A: assume dom(l)[i]; return true; }|; call YieldMem(l, i); } -procedure {:yields} {:layer 1,2} Free({:layer 1} {:linear_in "mem"} l: lmap) -requires {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); -ensures {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); +procedure {:yields} {:layer 1,2} Free({:layer 1} {:linear_in "mem"} l: lmap, i: int) +requires {:layer 1} PoolInv(unallocated, pool); +ensures {:layer 1} PoolInv(unallocated, pool); +requires {:layer 1} dom(l)[i]; ensures {:both} |{ A: return true; }|; { call Yield(); + call FreeLinear(l, i); + call ReturnAddr(i); + call Yield(); } procedure {:yields} {:layer 1,2} Read({:layer 1} {:linear "mem"} l: lmap, i: int) returns (o: int) -requires {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); -ensures {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); +requires {:layer 1} PoolInv(unallocated, pool); +ensures {:layer 1} PoolInv(unallocated, pool); requires {:layer 1} dom(l)[i] && map(l)[i] == mem[i]; ensures {:both} |{ A: assert dom(l)[i]; o := map(l)[i]; return true; }|; { @@ -99,8 +108,8 @@ ensures {:both} |{ A: assert dom(l)[i]; o := map(l)[i]; return true; }|; } procedure {:yields} {:layer 1,2} Write({:layer 1} {:linear_in "mem"} l: lmap, i: int, o: int) returns ({:layer 1} {:linear "mem"} l': lmap) -requires {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); -ensures {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); +requires {:layer 1} PoolInv(unallocated, pool); +ensures {:layer 1} PoolInv(unallocated, pool); requires {:layer 1} dom(l)[i] && map(l)[i] == mem[i]; ensures {:layer 1} dom(l')[i] && map(l')[i] == mem[i]; ensures {:both} |{ A: assert dom(l)[i]; l' := cons(dom(l), map(l)[i := o]); return true; }|; @@ -116,26 +125,31 @@ modifies pool; requires dom(pool)[i]; ensures pool == Remove(old(pool), i) && dom(l)[i] && map(l)[i] == mem[i]; +procedure {:layer 1} FreeLinear({:linear_in "mem"} l: lmap, i: int); +modifies pool; +requires !dom(pool)[i]; +ensures pool == Add(old(pool), i); + procedure {:layer 1} WriteLinear({:layer 1} {:linear_in "mem"} l: lmap, i: int, o: int) returns ({:layer 1} {:linear "mem"} l': lmap); requires dom(l)[i]; ensures l' == cons(dom(l), map(l)[i := o]); procedure {:yields} {:layer 1} Yield() -requires {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); -ensures {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); +requires {:layer 1} PoolInv(unallocated, pool); +ensures {:layer 1} PoolInv(unallocated, pool); { yield; - assert {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); + assert {:layer 1} PoolInv(unallocated, pool); } procedure {:yields} {:layer 1} YieldMem({:layer 1} {:linear "mem"} l: lmap, i: int) -requires {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); -ensures {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); +requires {:layer 1} PoolInv(unallocated, pool); +ensures {:layer 1} PoolInv(unallocated, pool); requires {:layer 1} dom(l)[i] && map(l)[i] == mem[i]; ensures {:layer 1} dom(l)[i] && map(l)[i] == mem[i]; { yield; - assert {:layer 1} (forall x: int :: unallocated[x] ==> dom(pool)[x]); + assert {:layer 1} PoolInv(unallocated, pool); assert {:layer 1} dom(l)[i] && map(l)[i] == mem[i]; } @@ -144,7 +158,7 @@ procedure {:yields} {:layer 2} Dummy() yield; } -var {:layer 1, 1} pool: lmap; +var {:layer 1, 1} {:linear "mem"} pool: lmap; var {:layer 0, 1} mem:[int]int; var {:layer 0, 1} unallocated:[int]bool; @@ -155,4 +169,7 @@ procedure {:yields} {:layer 0, 1} WriteLow(i: int, o: int); ensures {:atomic} |{ A: mem[i] := o; return true; }|; procedure {:yields} {:layer 0, 1} PickAddr() returns (i: int); -ensures {:atomic} |{ A: assume unallocated[i]; unallocated[i] := false; return true; }|; \ No newline at end of file +ensures {:atomic} |{ A: assume unallocated[i]; unallocated[i] := false; return true; }|; + +procedure {:yields} {:layer 0, 1} ReturnAddr(i: int); +ensures {:atomic} |{ A: unallocated[i] := true; return true; }|; \ No newline at end of file -- cgit v1.2.3 From 107dae2093c2049c674cb4a32cd01143765893b9 Mon Sep 17 00:00:00 2001 From: Shaz Qadeer Date: Sun, 10 Jan 2016 09:19:12 -0800 Subject: fixed a small problem in the precondition for FreeLinear --- Test/civl/alloc.bpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Test/civl/alloc.bpl b/Test/civl/alloc.bpl index 56515ba8..68b7e6c6 100644 --- a/Test/civl/alloc.bpl +++ b/Test/civl/alloc.bpl @@ -127,7 +127,7 @@ ensures pool == Remove(old(pool), i) && dom(l)[i] && map(l)[i] == mem[i]; procedure {:layer 1} FreeLinear({:linear_in "mem"} l: lmap, i: int); modifies pool; -requires !dom(pool)[i]; +requires dom(l)[i]; ensures pool == Add(old(pool), i); procedure {:layer 1} WriteLinear({:layer 1} {:linear_in "mem"} l: lmap, i: int, o: int) returns ({:layer 1} {:linear "mem"} l': lmap); -- cgit v1.2.3 From f75e5ba707885666ead81d7c5ec1653e7a09f3ff Mon Sep 17 00:00:00 2001 From: Shaz Qadeer Date: Sun, 17 Jan 2016 19:11:04 -0800 Subject: updated the example to implement the allocation of thread identifiers; this example provides another illustration of abstracting ordinary variables by linear variables --- Test/civl/Program4.bpl | 119 +++++++++++++++++++++++++++++++++--------- Test/civl/Program4.bpl.expect | 2 +- 2 files changed, 96 insertions(+), 25 deletions(-) diff --git a/Test/civl/Program4.bpl b/Test/civl/Program4.bpl index 68c2a5f3..a6dfcb2a 100644 --- a/Test/civl/Program4.bpl +++ b/Test/civl/Program4.bpl @@ -1,67 +1,138 @@ // RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory "%s" > "%t" // RUN: %diff "%s.expect" "%t" -type Tid; -var {:layer 0,1} a:[Tid]int; +var {:layer 0,2} a:[int]int; +var {:layer 0,1} count: int; +var {:layer 1,1} {:linear "tid"} unallocated:[int]bool; -procedure {:yields} {:layer 1} main() { - var {:linear "tid"} tid:Tid; +procedure {:yields} {:layer 2} main() +requires {:layer 1} AllocInv(count, unallocated); +{ + var {:layer 1} {:linear "tid"} tid:int; + var i: int; yield; - while (true) { - call tid := Allocate(); - async call P(tid); + assert {:layer 1} AllocInv(count, unallocated); + while (true) + invariant {:layer 1} AllocInv(count, unallocated); + { + call tid, i := Allocate(); + async call P(tid, i); yield; + assert {:layer 1} AllocInv(count, unallocated); } yield; } -procedure {:yields} {:layer 1} P({:linear "tid"} tid: Tid) -ensures {:layer 1} a[tid] == old(a)[tid] + 1; +procedure {:yields} {:layer 2} P({:layer 1} {:linear "tid"} tid: int, i: int) +requires {:layer 1} tid == i; +requires {:layer 1} AllocInv(count, unallocated); +ensures {:layer 1} AllocInv(count, unallocated); +ensures {:layer 2} a[tid] == old(a)[tid] + 1; { var t:int; yield; - assert {:layer 1} a[tid] == old(a)[tid]; - call t := Read(tid); + assert {:layer 1} AllocInv(count, unallocated); + assert {:layer 2} a[tid] == old(a)[tid]; + call t := Read(tid, i); yield; - assert {:layer 1} a[tid] == t; - call Write(tid, t + 1); + assert {:layer 1} AllocInv(count, unallocated); + assert {:layer 2} a[tid] == t; + call Write(tid, i, t + 1); yield; - assert {:layer 1} a[tid] == t + 1; + assert {:layer 1} AllocInv(count, unallocated); + assert {:layer 2} a[tid] == t + 1; } -procedure {:yields} {:layer 1} Allocate() returns ({:linear "tid"} tid: Tid) +procedure {:yields} {:layer 1,2} Allocate() returns ({:layer 1} {:linear "tid"} tid: int, i: int) +requires {:layer 1} AllocInv(count, unallocated); +ensures {:layer 1} AllocInv(count, unallocated); +ensures {:layer 1} tid == i; +ensures {:atomic} +|{A: + return true; +}|; { yield; - call tid := AllocateLow(); + assert {:layer 1} AllocInv(count, unallocated); + call i := AllocateLow(); + call tid := MakeLinear(i); yield; + assert {:layer 1} AllocInv(count, unallocated); } -procedure {:yields} {:layer 0,1} Read({:linear "tid"} tid: Tid) returns (val: int); +procedure {:yields} {:layer 1,2} Read({:layer 1} {:linear "tid"} tid: int, i: int) returns (val: int) +requires {:layer 1} tid == i; +requires {:layer 1} AllocInv(count, unallocated); +ensures {:layer 1} AllocInv(count, unallocated); ensures {:atomic} |{A: val := a[tid]; return true; }|; +{ + yield; + assert {:layer 1} AllocInv(count, unallocated); + call val := ReadLow(i); + yield; + assert {:layer 1} AllocInv(count, unallocated); +} -procedure {:yields} {:layer 0,1} Write({:linear "tid"} tid: Tid, val: int); +procedure {:yields} {:layer 1,2} Write({:layer 1} {:linear "tid"} tid: int, i: int, val: int) +requires {:layer 1} tid == i; +requires {:layer 1} AllocInv(count, unallocated); +ensures {:layer 1} AllocInv(count, unallocated); ensures {:atomic} |{A: a[tid] := val; return true; }|; +{ + yield; + assert {:layer 1} AllocInv(count, unallocated); + call WriteLow(i, val); + yield; + assert {:layer 1} AllocInv(count, unallocated); +} -procedure {:yields} {:layer 0,1} AllocateLow() returns ({:linear "tid"} tid: Tid); -ensures {:atomic} |{ A: return true; }|; +function {:inline} AllocInv(count: int, unallocated:[int]bool): (bool) +{ + (forall x: int :: count <= x ==> unallocated[x]) +} + +procedure {:yields} {:layer 0,1} ReadLow(i: int) returns (val: int); +ensures {:atomic} +|{A: + val := a[i]; return true; +}|; + +procedure {:yields} {:layer 0,1} WriteLow(i: int, val: int); +ensures {:atomic} +|{A: + a[i] := val; return true; +}|; +procedure {:yields} {:layer 0,1} AllocateLow() returns (i: int); +ensures {:atomic} +|{A: + i := count; + count := i + 1; + return true; +}|; +// We can prove that this primitive procedure preserves the permission invariant locally. +// We only need to using its specification and the definitions of TidCollector and TidSetCollector. +procedure {:layer 1} MakeLinear(i: int) returns ({:linear "tid"} tid: int); +requires unallocated[i]; +modifies unallocated; +ensures tid == i && unallocated == old(unallocated)[i := false]; -function {:builtin "MapConst"} MapConstBool(bool): [Tid]bool; -function {:builtin "MapOr"} MapOr([Tid]bool, [Tid]bool) : [Tid]bool; +function {:builtin "MapConst"} MapConstBool(bool): [int]bool; +function {:builtin "MapOr"} MapOr([int]bool, [int]bool) : [int]bool; -function {:inline} {:linear "tid"} TidCollector(x: Tid) : [Tid]bool +function {:inline} {:linear "tid"} TidCollector(x: int) : [int]bool { MapConstBool(false)[x := true] } -function {:inline} {:linear "tid"} TidSetCollector(x: [Tid]bool) : [Tid]bool +function {:inline} {:linear "tid"} TidSetCollector(x: [int]bool) : [int]bool { x } diff --git a/Test/civl/Program4.bpl.expect b/Test/civl/Program4.bpl.expect index 9823d44a..f08c6e00 100644 --- a/Test/civl/Program4.bpl.expect +++ b/Test/civl/Program4.bpl.expect @@ -1,2 +1,2 @@ -Boogie program verifier finished with 6 verified, 0 errors +Boogie program verifier finished with 12 verified, 0 errors -- cgit v1.2.3 From 83f675b0b0124872cc343ba0f14984803ee6eb18 Mon Sep 17 00:00:00 2001 From: qunyanm Date: Tue, 19 Jan 2016 11:08:28 -0800 Subject: Bug fix in determining whether a type parameter is bounded When checking whether a type parameter could be determined from the bound variable types, we mistakenly compare equality between a TypeVarable and a VCExprVar, instead of between two VCExprVars. --- Source/VCExpr/TypeErasurePremisses.cs | 2 +- Test/test2/BoundedTypeParameterQuantifier.bpl | 14 ++++++++++++++ Test/test2/BoundedTypeParameterQuantifier.bpl.expect | 2 ++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 Test/test2/BoundedTypeParameterQuantifier.bpl create mode 100644 Test/test2/BoundedTypeParameterQuantifier.bpl.expect diff --git a/Source/VCExpr/TypeErasurePremisses.cs b/Source/VCExpr/TypeErasurePremisses.cs index 6077f327..dc9ad10f 100644 --- a/Source/VCExpr/TypeErasurePremisses.cs +++ b/Source/VCExpr/TypeErasurePremisses.cs @@ -1115,7 +1115,7 @@ namespace Microsoft.Boogie.TypeErasure if (typeVarBindings.Count < node.TypeParameters.Count) { foreach (TypeVariable/*!*/ var in node.TypeParameters) { Contract.Assert(var != null); - if (typeVarBindings.All(b => !b.V.Equals(var))) + if (typeVarBindings.All(b => !b.V.Equals(bindings.TypeVariableBindings[var]))) newBoundVars.Add((VCExprVar)bindings.TypeVariableBindings[var]); } } diff --git a/Test/test2/BoundedTypeParameterQuantifier.bpl b/Test/test2/BoundedTypeParameterQuantifier.bpl new file mode 100644 index 00000000..146ba445 --- /dev/null +++ b/Test/test2/BoundedTypeParameterQuantifier.bpl @@ -0,0 +1,14 @@ +// RUN: %boogie /proverWarnings:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +function Map#Domain(Map QUN YAN): [QUN] bool; +function Map#Empty(): Map QUN YAN; +type Map QUN YAN; + +axiom (forall u: QUN :: + { Map#Domain(Map#Empty(): Map QUN YAN)[u] } + !Map#Domain(Map#Empty(): Map QUN YAN)[u]); + +procedure P() +{ +} diff --git a/Test/test2/BoundedTypeParameterQuantifier.bpl.expect b/Test/test2/BoundedTypeParameterQuantifier.bpl.expect new file mode 100644 index 00000000..37fad75c --- /dev/null +++ b/Test/test2/BoundedTypeParameterQuantifier.bpl.expect @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 1 verified, 0 errors -- cgit v1.2.3 From c36b3d93a9c55dcb1d37d8b6ca09ae0b5114ec0b Mon Sep 17 00:00:00 2001 From: Shaz Qadeer Date: Tue, 19 Jan 2016 15:07:06 -0800 Subject: some fixes --- Test/civl/treiber-stack.bpl | 59 ++++++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/Test/civl/treiber-stack.bpl b/Test/civl/treiber-stack.bpl index 751ce861..bb0201a9 100644 --- a/Test/civl/treiber-stack.bpl +++ b/Test/civl/treiber-stack.bpl @@ -1,62 +1,71 @@ // RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory "%s" > "%t" // RUN: %diff "%s.expect" "%t" -type Node = int; -const unique null: Node; + +const unique null: int; type lmap; -function {:linear "Node"} dom(lmap): [Node]bool; -function map(lmap): [Node]Node; -function {:builtin "MapConst"} MapConstBool(bool) : [Node]bool; +function {:linear "Node"} dom(lmap): [int]bool; +function map(lmap): [int]int; +function {:builtin "MapConst"} MapConstBool(bool) : [int]bool; function EmptyLmap(): (lmap); axiom (dom(EmptyLmap()) == MapConstBool(false)); -function Add(x: lmap, i: Node, v: Node): (lmap); -axiom (forall x: lmap, i: Node, v: Node :: dom(Add(x, i, v)) == dom(x)[i:=true] && map(Add(x, i, v)) == map(x)[i := v]); +function Add(x: lmap, i: int, v: int): (lmap); +axiom (forall x: lmap, i: int, v: int :: dom(Add(x, i, v)) == dom(x)[i:=true] && map(Add(x, i, v)) == map(x)[i := v]); -function Remove(x: lmap, i: Node): (lmap); -axiom (forall x: lmap, i: Node :: dom(Remove(x, i)) == dom(x)[i:=false] && map(Remove(x, i)) == map(x)); +function Remove(x: lmap, i: int): (lmap); +axiom (forall x: lmap, i: int :: dom(Remove(x, i)) == dom(x)[i:=false] && map(Remove(x, i)) == map(x)); -procedure {:yields} {:layer 0,1} ReadTopOfStack() returns (v:Node); -ensures {:right} |{ A: assume dom(Stack)[v] || dom(Used)[v]; return true; }|; +procedure {:yields} {:layer 0,1} ReadTopOfStack() returns (v:int); +ensures {:right} |{ A: assume dom(Stack)[v] || Used[v]; return true; }|; -procedure {:yields} {:layer 0,1} Load(i:Node) returns (v:Node); -ensures {:right} |{ A: assert dom(Stack)[i] || dom(Used)[i]; goto B,C; +procedure {:yields} {:layer 0,1} Load(i:int) returns (v:int); +ensures {:right} |{ A: assert dom(Stack)[i] || Used[i]; goto B,C; B: assume dom(Stack)[i]; v := map(Stack)[i]; return true; C: assume !dom(Stack)[i]; return true; }|; -procedure {:yields} {:layer 0,1} Store({:linear_in "Node"} l_in:lmap, i:Node, v:Node) returns ({:linear "Node"} l_out:lmap); +procedure {:yields} {:layer 0,1} Store({:linear_in "Node"} l_in:lmap, i:int, v:int) returns ({:linear "Node"} l_out:lmap); ensures {:both} |{ A: assert dom(l_in)[i]; l_out := Add(l_in, i, v); return true; }|; -procedure {:yields} {:layer 0,1} TransferToStack(oldVal: Node, newVal: Node, {:linear_in "Node"} l_in:lmap) returns (r: bool, {:linear "Node"} l_out:lmap); +procedure {:yields} {:layer 0,1} TransferToStack(oldVal: int, newVal: int, {:linear_in "Node"} l_in:lmap) returns (r: bool, {:linear "Node"} l_out:lmap); ensures {:atomic} |{ A: assert dom(l_in)[newVal]; goto B,C; B: assume oldVal == TopOfStack; TopOfStack := newVal; l_out := EmptyLmap(); Stack := Add(Stack, newVal, map(l_in)[newVal]); r := true; return true; C: assume oldVal != TopOfStack; l_out := l_in; r := false; return true; }|; -procedure {:yields} {:layer 0,1} TransferFromStack(oldVal: Node, newVal: Node) returns (r: bool); +procedure {:yields} {:layer 0,1} TransferFromStack(oldVal: int, newVal: int) returns (r: bool); ensures {:atomic} |{ A: goto B,C; - B: assume oldVal == TopOfStack; TopOfStack := newVal; Used := Add(Used, oldVal, map(Stack)[oldVal]); Stack := Remove(Stack, oldVal); r := true; return true; + B: assume oldVal == TopOfStack; TopOfStack := newVal; Used[oldVal] := true; Stack := Remove(Stack, oldVal); r := true; return true; C: assume oldVal != TopOfStack; r := false; return true; }|; -var {:layer 0} TopOfStack: Node; +var {:layer 0} TopOfStack: int; var {:linear "Node"} {:layer 0} Stack: lmap; -function {:inline} Inv(TopOfStack: Node, Stack: lmap) : (bool) +function {:inline} Inv(TopOfStack: int, Stack: lmap) : (bool) { BetweenSet(map(Stack), TopOfStack, null)[TopOfStack] && Subset(BetweenSet(map(Stack), TopOfStack, null), Union(Singleton(null), dom(Stack))) } -var {:linear "Node"} {:layer 0} Used: lmap; +var {:linear "Node"} {:layer 0} Used: [int]bool; + +function {:inline} {:linear "Node"} NodeCollector(x: int) : [int]bool +{ + MapConstBool(false)[x := true] +} +function {:inline} {:linear "Node"} NodeSetCollector(x: [int]bool) : [int]bool +{ + x +} -procedure {:yields} {:layer 1} push(x: Node, {:linear_in "Node"} x_lmap: lmap) +procedure {:yields} {:layer 1} push(x: int, {:linear_in "Node"} x_lmap: lmap) requires {:layer 1} dom(x_lmap)[x]; requires {:layer 1} Inv(TopOfStack, Stack); ensures {:layer 1} Inv(TopOfStack, Stack); ensures {:atomic} |{ A: Stack := Add(Stack, x, TopOfStack); TopOfStack := x; return true; }|; { - var t: Node; + var t: int; var g: bool; var {:linear "Node"} t_lmap: lmap; @@ -82,13 +91,13 @@ ensures {:atomic} |{ A: Stack := Add(Stack, x, TopOfStack); TopOfStack := x; ret assert {:expand} {:layer 1} Inv(TopOfStack, Stack); } -procedure {:yields} {:layer 1} pop() returns (t: Node) +procedure {:yields} {:layer 1} pop() returns (t: int) requires {:layer 1} Inv(TopOfStack, Stack); ensures {:layer 1} Inv(TopOfStack, Stack); -ensures {:atomic} |{ A: assume TopOfStack != null; t := TopOfStack; Used := Add(Used, t, map(Stack)[t]); TopOfStack := map(Stack)[t]; Stack := Remove(Stack, t); return true; }|; +ensures {:atomic} |{ A: assume TopOfStack != null; t := TopOfStack; Used[t] := true; TopOfStack := map(Stack)[t]; Stack := Remove(Stack, t); return true; }|; { var g: bool; - var x: Node; + var x: int; yield; assert {:layer 1} Inv(TopOfStack, Stack); -- cgit v1.2.3 From 4d9ec68b4b038ff2e4fe91eec2e82b1d613ee3b0 Mon Sep 17 00:00:00 2001 From: Shaz Qadeer Date: Thu, 21 Jan 2016 21:28:00 -0800 Subject: improved some of the annotations --- Test/civl/Program4.bpl | 54 ++++++++++++++++++++++----------------------- Test/civl/treiber-stack.bpl | 4 ++-- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/Test/civl/Program4.bpl b/Test/civl/Program4.bpl index a6dfcb2a..11ba8afa 100644 --- a/Test/civl/Program4.bpl +++ b/Test/civl/Program4.bpl @@ -2,51 +2,51 @@ // RUN: %diff "%s.expect" "%t" var {:layer 0,2} a:[int]int; var {:layer 0,1} count: int; -var {:layer 1,1} {:linear "tid"} unallocated:[int]bool; +var {:layer 1,1} {:linear "tid"} allocated:[int]bool; procedure {:yields} {:layer 2} main() -requires {:layer 1} AllocInv(count, unallocated); +requires {:layer 1} allocated == MapConstBool(false); { var {:layer 1} {:linear "tid"} tid:int; var i: int; yield; - assert {:layer 1} AllocInv(count, unallocated); + assert {:layer 1} AllocInv(count, allocated); while (true) - invariant {:layer 1} AllocInv(count, unallocated); + invariant {:layer 1} AllocInv(count, allocated); { call tid, i := Allocate(); async call P(tid, i); yield; - assert {:layer 1} AllocInv(count, unallocated); + assert {:layer 1} AllocInv(count, allocated); } yield; } procedure {:yields} {:layer 2} P({:layer 1} {:linear "tid"} tid: int, i: int) requires {:layer 1} tid == i; -requires {:layer 1} AllocInv(count, unallocated); -ensures {:layer 1} AllocInv(count, unallocated); +requires {:layer 1} AllocInv(count, allocated); +ensures {:layer 1} AllocInv(count, allocated); ensures {:layer 2} a[tid] == old(a)[tid] + 1; { var t:int; yield; - assert {:layer 1} AllocInv(count, unallocated); + assert {:layer 1} AllocInv(count, allocated); assert {:layer 2} a[tid] == old(a)[tid]; call t := Read(tid, i); yield; - assert {:layer 1} AllocInv(count, unallocated); + assert {:layer 1} AllocInv(count, allocated); assert {:layer 2} a[tid] == t; call Write(tid, i, t + 1); yield; - assert {:layer 1} AllocInv(count, unallocated); + assert {:layer 1} AllocInv(count, allocated); assert {:layer 2} a[tid] == t + 1; } procedure {:yields} {:layer 1,2} Allocate() returns ({:layer 1} {:linear "tid"} tid: int, i: int) -requires {:layer 1} AllocInv(count, unallocated); -ensures {:layer 1} AllocInv(count, unallocated); +requires {:layer 1} AllocInv(count, allocated); +ensures {:layer 1} AllocInv(count, allocated); ensures {:layer 1} tid == i; ensures {:atomic} |{A: @@ -54,48 +54,48 @@ ensures {:atomic} }|; { yield; - assert {:layer 1} AllocInv(count, unallocated); + assert {:layer 1} AllocInv(count, allocated); call i := AllocateLow(); call tid := MakeLinear(i); yield; - assert {:layer 1} AllocInv(count, unallocated); + assert {:layer 1} AllocInv(count, allocated); } procedure {:yields} {:layer 1,2} Read({:layer 1} {:linear "tid"} tid: int, i: int) returns (val: int) requires {:layer 1} tid == i; -requires {:layer 1} AllocInv(count, unallocated); -ensures {:layer 1} AllocInv(count, unallocated); +requires {:layer 1} AllocInv(count, allocated); +ensures {:layer 1} AllocInv(count, allocated); ensures {:atomic} |{A: val := a[tid]; return true; }|; { yield; - assert {:layer 1} AllocInv(count, unallocated); + assert {:layer 1} AllocInv(count, allocated); call val := ReadLow(i); yield; - assert {:layer 1} AllocInv(count, unallocated); + assert {:layer 1} AllocInv(count, allocated); } procedure {:yields} {:layer 1,2} Write({:layer 1} {:linear "tid"} tid: int, i: int, val: int) requires {:layer 1} tid == i; -requires {:layer 1} AllocInv(count, unallocated); -ensures {:layer 1} AllocInv(count, unallocated); +requires {:layer 1} AllocInv(count, allocated); +ensures {:layer 1} AllocInv(count, allocated); ensures {:atomic} |{A: a[tid] := val; return true; }|; { yield; - assert {:layer 1} AllocInv(count, unallocated); + assert {:layer 1} AllocInv(count, allocated); call WriteLow(i, val); yield; - assert {:layer 1} AllocInv(count, unallocated); + assert {:layer 1} AllocInv(count, allocated); } -function {:inline} AllocInv(count: int, unallocated:[int]bool): (bool) +function {:inline} AllocInv(count: int, allocated:[int]bool): (bool) { - (forall x: int :: count <= x ==> unallocated[x]) + (forall x: int :: allocated[x] ==> x < count) } procedure {:yields} {:layer 0,1} ReadLow(i: int) returns (val: int); @@ -121,9 +121,9 @@ ensures {:atomic} // We can prove that this primitive procedure preserves the permission invariant locally. // We only need to using its specification and the definitions of TidCollector and TidSetCollector. procedure {:layer 1} MakeLinear(i: int) returns ({:linear "tid"} tid: int); -requires unallocated[i]; -modifies unallocated; -ensures tid == i && unallocated == old(unallocated)[i := false]; +requires !allocated[i]; +modifies allocated; +ensures tid == i && allocated == old(allocated)[i := true]; function {:builtin "MapConst"} MapConstBool(bool): [int]bool; function {:builtin "MapOr"} MapOr([int]bool, [int]bool) : [int]bool; diff --git a/Test/civl/treiber-stack.bpl b/Test/civl/treiber-stack.bpl index bb0201a9..64e01c99 100644 --- a/Test/civl/treiber-stack.bpl +++ b/Test/civl/treiber-stack.bpl @@ -1,7 +1,7 @@ // RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory "%s" > "%t" // RUN: %diff "%s.expect" "%t" -const unique null: int; +const null: int; type lmap; function {:linear "Node"} dom(lmap): [int]bool; function map(lmap): [int]int; @@ -17,7 +17,7 @@ function Remove(x: lmap, i: int): (lmap); axiom (forall x: lmap, i: int :: dom(Remove(x, i)) == dom(x)[i:=false] && map(Remove(x, i)) == map(x)); procedure {:yields} {:layer 0,1} ReadTopOfStack() returns (v:int); -ensures {:right} |{ A: assume dom(Stack)[v] || Used[v]; return true; }|; +ensures {:right} |{ A: assume v == null || dom(Stack)[v] || Used[v]; return true; }|; procedure {:yields} {:layer 0,1} Load(i:int) returns (v:int); ensures {:right} |{ A: assert dom(Stack)[i] || Used[i]; goto B,C; -- cgit v1.2.3 From 1e0f2b1ea7e9cbfd1c7923674c0ed4601263d09a Mon Sep 17 00:00:00 2001 From: Shaz Qadeer Date: Tue, 26 Jan 2016 10:11:24 -0800 Subject: another fix --- Test/civl/treiber-stack.bpl | 2 -- 1 file changed, 2 deletions(-) diff --git a/Test/civl/treiber-stack.bpl b/Test/civl/treiber-stack.bpl index 64e01c99..a184886d 100644 --- a/Test/civl/treiber-stack.bpl +++ b/Test/civl/treiber-stack.bpl @@ -80,7 +80,6 @@ ensures {:atomic} |{ A: Stack := Add(Stack, x, TopOfStack); TopOfStack := x; ret call t_lmap := Store(t_lmap, x, t); call g, t_lmap := TransferToStack(t, x, t_lmap); if (g) { - assert {:layer 1} map(Stack)[x] == t; break; } yield; @@ -124,7 +123,6 @@ function Subset([int]bool, [int]bool) returns (bool); function Empty() returns ([int]bool); function Singleton(int) returns ([int]bool); -function Reachable([int,int]bool, int) returns ([int]bool); function Union([int]bool, [int]bool) returns ([int]bool); axiom(forall x:int :: !Empty()[x]); -- cgit v1.2.3 From 5fb565e439255ede7dc3653708af41678b6c1062 Mon Sep 17 00:00:00 2001 From: Shaz Qadeer Date: Tue, 9 Feb 2016 21:35:24 -0800 Subject: added an example --- Test/civl/funky.bpl | 133 +++++++++++++++++++++++++++++++++++++++++++++ Test/civl/funky.bpl.expect | 2 + Test/civl/ticket.bpl | 5 +- 3 files changed, 137 insertions(+), 3 deletions(-) create mode 100644 Test/civl/funky.bpl create mode 100644 Test/civl/funky.bpl.expect diff --git a/Test/civl/funky.bpl b/Test/civl/funky.bpl new file mode 100644 index 00000000..ad5bf271 --- /dev/null +++ b/Test/civl/funky.bpl @@ -0,0 +1,133 @@ +// RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory "%s" > "%t" +// RUN: %diff "%s.expect" "%t" +type X; +const nil: X; +function {:builtin "MapConst"} MapConstBool(bool) : [X]bool; +function {:inline} {:linear "tid"} TidCollector(x: X) : [X]bool +{ + MapConstBool(false)[x := true] +} + +var {:layer 0, 3} A: X; +var {:layer 0, 3} B: X; +var {:layer 0, 3} counter: int; + +procedure {:yields} {:layer 0, 3} LockA({:linear "tid"} tid: X); +ensures {:right} |{ A: assert tid != nil; assume A == nil; A := tid; return true; }|; + +procedure {:yields} {:layer 0, 1} IncrA({:linear "tid"} tid: X); +ensures {:right} |{ A: assert tid != nil && A == tid; counter := counter + 1; return true; }|; + +procedure {:yields} {:layer 0, 1} DecrA({:linear "tid"} tid: X); +ensures {:right} |{ A: assert tid != nil && A == tid; counter := counter - 1; return true; }|; + +procedure {:yields} {:layer 0, 3} UnlockA({:linear "tid"} tid: X); +ensures {:left} |{ A: assert tid != nil && A == tid; A := nil; return true; }|; + +procedure {:yields} {:layer 0, 3} LockB({:linear "tid"} tid: X); +ensures {:right} |{ A: assert tid != nil; assume B == nil; B := tid; return true; }|; + +procedure {:yields} {:layer 0, 2} IncrB({:linear "tid"} tid: X); +ensures {:atomic} |{ A: assert tid != nil && B == tid; counter := counter + 1; return true; }|; + +procedure {:yields} {:layer 0, 1} DecrB({:linear "tid"} tid: X); +ensures {:atomic} |{ A: assert tid != nil && B == tid; counter := counter - 1; return true; }|; + +procedure {:yields} {:layer 0, 3} UnlockB({:linear "tid"} tid: X); +ensures {:left} |{ A: assert tid != nil && B == tid; B := nil; return true; }|; + +procedure {:yields} {:layer 0, 3} AssertA({:linear "tid"} tid: X); +ensures {:atomic} |{ A: assert tid != nil && A == tid; assert counter >= -1; return true; }|; + +procedure {:yields} {:layer 0, 3} AssertB({:linear "tid"} tid: X); +ensures {:atomic} |{ A: assert tid != nil && A == tid && B == tid; assert counter == 0; return true; }|; + +procedure {:pure} AllocTid() returns ({:linear "tid"} tid: X); +ensures tid != nil; + +procedure {:yields} {:layer 1, 2} AbsDecrB({:linear "tid"} tid: X) +ensures {:right} |{ A: assert tid != nil && B == tid && counter == 0; counter := counter - 1; return true; }|; +{ + yield; + call DecrB(tid); + yield; +} + +procedure {:yields} {:layer 2, 3} AbsAssertA({:linear "tid"} tid: X) +ensures {:both} |{ A: assert tid != nil && A == tid; assert counter >= -1; return true; }|; +{ + yield; + call AssertA(tid); + yield; +} + +procedure {:yields} {:layer 2, 3} AbsAssertB({:linear "tid"} tid: X) +ensures {:both} |{ A: assert tid != nil && A == tid && B == tid; assert counter == 0; return true; }|; +{ + yield; + call AssertB(tid); + yield; +} + +procedure {:yields} {:layer 1} TA({:linear "tid"} tid: X) +requires {:layer 1} tid != nil; +{ + yield; + call LockA(tid); + call IncrA(tid); + call DecrA(tid); + call UnlockA(tid); + yield; +} + +procedure {:yields} {:layer 2, 3} TB({:linear "tid"} tid: X) +ensures {:both} |{ A: assert tid != nil && counter == 0; return true; }|; +{ + yield; + call LockB(tid); + call AbsDecrB(tid); + call IncrB(tid); + call UnlockB(tid); + yield; +} + +procedure {:yields} {:layer 3} AbsTB({:linear "tid"} tid: X) +requires {:layer 3} tid != nil && counter == 0; +{ + yield; + assert {:layer 3} counter == 0; + call TB(tid); + yield; +} + +procedure {:yields} {:layer 3} main({:linear "tid"} tid: X) +requires {:layer 3} tid != nil && counter == 0; +{ + var {:linear "tid"} cid: X; + + yield; + assert {:layer 3} counter == 0; + while (*) + invariant {:layer 3} counter == 0; + { + if (*) { + call cid := AllocTid(); + async call TA(cid); + } + if (*) { + call cid := AllocTid(); + async call AbsTB(cid); + } + yield; + assert {:layer 3} counter == 0; + call LockA(tid); + call AbsAssertA(tid); + call LockB(tid); + call AbsAssertB(tid); + call UnlockB(tid); + call UnlockA(tid); + yield; + assert {:layer 3} counter == 0; + } + yield; +} \ No newline at end of file diff --git a/Test/civl/funky.bpl.expect b/Test/civl/funky.bpl.expect new file mode 100644 index 00000000..0a114594 --- /dev/null +++ b/Test/civl/funky.bpl.expect @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 75 verified, 0 errors diff --git a/Test/civl/ticket.bpl b/Test/civl/ticket.bpl index 9fc55646..df19aae4 100644 --- a/Test/civl/ticket.bpl +++ b/Test/civl/ticket.bpl @@ -6,7 +6,6 @@ axiom (forall x: int, y: int :: RightOpen(x)[y] <==> y < x); axiom (forall x: int, y: int :: RightClosed(x)[y] <==> y <= x); type X; -function {:builtin "MapConst"} mapconstbool(bool): [X]bool; const nil: X; var {:layer 0,2} t: int; var {:layer 0,2} s: int; @@ -42,7 +41,7 @@ ensures {:layer 1} {:layer 2} xl != nil; } procedure {:yields} {:layer 2} main({:linear_in "tid"} xls':[X]bool) -requires {:layer 2} xls' == mapconstbool(true); +requires {:layer 2} xls' == MapConstBool(true); { var {:linear "tid"} tid: X; var {:linear "tid"} xls: [X]bool; @@ -132,7 +131,7 @@ ensures {:layer 1} Inv1(T,t); } procedure {:yields} {:layer 0,2} Init({:linear "tid"} xls:[X]bool); -ensures {:atomic} |{ A: assert xls == mapconstbool(true); cs := nil; t := 0; s := 0; T := RightOpen(0); return true; }|; +ensures {:atomic} |{ A: assert xls == MapConstBool(true); cs := nil; t := 0; s := 0; T := RightOpen(0); return true; }|; procedure {:yields} {:layer 0,1} GetTicket({:linear "tid"} tid: X) returns (m: int); ensures {:atomic} |{ A: m := t; t := t + 1; T[m] := true; return true; }|; -- cgit v1.2.3 From abee810ceedbf551194788164fdf723edc511c0c Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Fri, 12 Feb 2016 16:47:14 -0800 Subject: (Honestly, I don't know what I'm doing. I'm trying to revert these changes, but Git doesn't actually say what sort of commit I'm doing. Well, here goes.) --- Test/snapshots/Snapshots41.v0.bpl | 70 +++++++++++++++++------------------ Test/snapshots/Snapshots41.v1.bpl | 78 +++++++++++++++++++-------------------- 2 files changed, 74 insertions(+), 74 deletions(-) diff --git a/Test/snapshots/Snapshots41.v0.bpl b/Test/snapshots/Snapshots41.v0.bpl index 631fe544..dbfe3e2d 100644 --- a/Test/snapshots/Snapshots41.v0.bpl +++ b/Test/snapshots/Snapshots41.v0.bpl @@ -1,35 +1,35 @@ -procedure {:checksum "0"} M(x: int); -implementation {:id "M"} {:checksum "1"} M(x: int) -{ assert x < 20 || 10 <= x; // always true - assert x < 10; // error - call Other(x); // error: precondition violation -} - -procedure {:checksum "10"} Other(y: int); - requires 0 <= y; -implementation {:id "Other"} {:checksum "11"} Other(y: int) -{ -} - -procedure {:checksum "20"} Posty() returns (z: int); - ensures 2 <= z; // error: postcondition violation -implementation {:id "Posty"} {:checksum "21"} Posty() returns (z: int) -{ - var t: int; - t := 20; - if (t < z) { - } else { // the postcondition violation occurs on this 'else' branch - } -} - -procedure {:checksum "30"} NoChangeWhazzoeva(u: int); -implementation {:id "NoChangeWhazzoeva"} {:checksum "3"} NoChangeWhazzoeva(u: int) -{ - assert u != 53; // error -} - -procedure {:checksum "40"} NoChangeAndCorrect(); -implementation {:id "NoChangeAndCorrect"} {:checksum "41"} NoChangeAndCorrect() -{ - assert true; -} +procedure {:checksum "0"} M(x: int); +implementation {:id "M"} {:checksum "1"} M(x: int) +{ assert x < 20 || 10 <= x; // always true + assert x < 10; // error + call Other(x); // error: precondition violation +} + +procedure {:checksum "10"} Other(y: int); + requires 0 <= y; +implementation {:id "Other"} {:checksum "11"} Other(y: int) +{ +} + +procedure {:checksum "20"} Posty() returns (z: int); + ensures 2 <= z; // error: postcondition violation +implementation {:id "Posty"} {:checksum "21"} Posty() returns (z: int) +{ + var t: int; + t := 20; + if (t < z) { + } else { // the postcondition violation occurs on this 'else' branch + } +} + +procedure {:checksum "30"} NoChangeWhazzoeva(u: int); +implementation {:id "NoChangeWhazzoeva"} {:checksum "3"} NoChangeWhazzoeva(u: int) +{ + assert u != 53; // error +} + +procedure {:checksum "40"} NoChangeAndCorrect(); +implementation {:id "NoChangeAndCorrect"} {:checksum "41"} NoChangeAndCorrect() +{ + assert true; +} diff --git a/Test/snapshots/Snapshots41.v1.bpl b/Test/snapshots/Snapshots41.v1.bpl index 0cd9fbf9..9864e0e4 100644 --- a/Test/snapshots/Snapshots41.v1.bpl +++ b/Test/snapshots/Snapshots41.v1.bpl @@ -1,39 +1,39 @@ -procedure {:checksum "0"} M(x: int); -implementation {:id "M"} {:checksum "1"} M(x: int) -{ -assert x < 20 || 10 <= x; // always true - - assert x < 10; // error - call Other(x); // error: precondition violation - assert x == 7; // error: this is a new error in v1 -} - - - procedure {:checksum "10"} Other(y: int); - requires 0 <= y; - implementation {:id "Other"} {:checksum "11"} Other(y: int) - { - } - - - -procedure {:checksum "20"} Posty() returns (z: int); - ensures 2 <= z; // error: postcondition violation -implementation {:id "Posty"} {:checksum "21"} Posty() returns (z: int) -{ - var t: int; - t := 20; - if (t < z) { - assert true; // this is a new assert - } else { // the postcondition violation occurs on this 'else' branch - } -} - - procedure {:checksum "30"} NoChangeWhazzoeva(u: int); - implementation {:id "NoChangeWhazzoeva"} {:checksum "3"} NoChangeWhazzoeva(u: int) - { - assert u != 53; // error - } - -procedure {:checksum "40"} NoChangeAndCorrect(); -implementation {:id "NoChangeAndCorrect"} {:checksum "41"} NoChangeAndCorrect() { assert true; } +procedure {:checksum "0"} M(x: int); +implementation {:id "M"} {:checksum "1"} M(x: int) +{ +assert x < 20 || 10 <= x; // always true + + assert x < 10; // error + call Other(x); // error: precondition violation + assert x == 7; // error: this is a new error in v1 +} + + + procedure {:checksum "10"} Other(y: int); + requires 0 <= y; + implementation {:id "Other"} {:checksum "11"} Other(y: int) + { + } + + + +procedure {:checksum "20"} Posty() returns (z: int); + ensures 2 <= z; // error: postcondition violation +implementation {:id "Posty"} {:checksum "21"} Posty() returns (z: int) +{ + var t: int; + t := 20; + if (t < z) { + assert true; // this is a new assert + } else { // the postcondition violation occurs on this 'else' branch + } +} + + procedure {:checksum "30"} NoChangeWhazzoeva(u: int); + implementation {:id "NoChangeWhazzoeva"} {:checksum "3"} NoChangeWhazzoeva(u: int) + { + assert u != 53; // error + } + +procedure {:checksum "40"} NoChangeAndCorrect(); +implementation {:id "NoChangeAndCorrect"} {:checksum "41"} NoChangeAndCorrect() { assert true; } -- cgit v1.2.3 From 502942a53a6db2b3a900d7570807216372d49ad0 Mon Sep 17 00:00:00 2001 From: Valentin Wüstholz Date: Thu, 3 Mar 2016 17:32:31 -0600 Subject: Improve support for optimization and identifying unnecessary assumes. --- Source/Provers/SMTLib/ProverInterface.cs | 34 +++++++++------------- Source/Provers/SMTLib/SMTLibLineariser.cs | 22 ++++++++++++-- Source/Provers/SMTLib/TypeDeclCollector.cs | 12 ++++++-- Source/VCExpr/VCExprAST.cs | 2 ++ Source/VCGeneration/Check.cs | 7 ++--- Source/VCGeneration/VC.cs | 12 ++++---- Source/VCGeneration/Wlp.cs | 17 +++++++---- .../unnecessaryassumes1.bpl.expect | 2 +- 8 files changed, 64 insertions(+), 44 deletions(-) diff --git a/Source/Provers/SMTLib/ProverInterface.cs b/Source/Provers/SMTLib/ProverInterface.cs index 7e98e8f8..432d7f3e 100644 --- a/Source/Provers/SMTLib/ProverInterface.cs +++ b/Source/Provers/SMTLib/ProverInterface.cs @@ -411,15 +411,6 @@ namespace Microsoft.Boogie.SMTLib SendThisVC("(push 1)"); SendThisVC("(set-info :boogie-vc-id " + SMTLibNamer.QuoteId(descriptiveName) + ")"); - if (CommandLineOptions.Clo.PrintNecessaryAssumes && NamedAssumeVars != null) - { - foreach (var v in NamedAssumeVars) - { - SendThisVC(string.Format("(declare-fun {0} () Bool)", v)); - SendThisVC(string.Format("(assert (! {0} :named {1}))", v, "aux$$" + v.Name)); - } - } - SendThisVC(vcString); SendOptimizationRequests(); @@ -1293,7 +1284,7 @@ namespace Microsoft.Boogie.SMTLib var reporter = handler as VC.VCGen.ErrorReporter; // TODO(wuestholz): Is the reporter ever null? - if (CommandLineOptions.Clo.PrintNecessaryAssumes && NamedAssumeVars != null && NamedAssumeVars.Any() && result == Outcome.Valid && reporter != null) + if (CommandLineOptions.Clo.PrintNecessaryAssumes && ContainsNamedAssumes && result == Outcome.Valid && reporter != null) { SendThisVC("(get-unsat-core)"); var resp = Process.GetProverResponse(); @@ -1993,6 +1984,8 @@ namespace Microsoft.Boogie.SMTLib readonly IList OptimizationRequests = new List(); + bool ContainsNamedAssumes; + protected string VCExpr2String(VCExpr expr, int polarity) { Contract.Requires(expr != null); @@ -2032,8 +2025,8 @@ namespace Microsoft.Boogie.SMTLib DeclCollector.Collect(sortedExpr); FeedTypeDeclsToProver(); - AddAxiom(SMTLibExprLineariser.ToString(sortedAxioms, Namer, options)); - string res = SMTLibExprLineariser.ToString(sortedExpr, Namer, options, OptimizationRequests); + AddAxiom(SMTLibExprLineariser.ToString(sortedAxioms, Namer, options, ref ContainsNamedAssumes)); + string res = SMTLibExprLineariser.ToString(sortedExpr, Namer, options, ref ContainsNamedAssumes, OptimizationRequests); Contract.Assert(res != null); if (CommandLineOptions.Clo.Trace) @@ -2141,20 +2134,19 @@ namespace Microsoft.Boogie.SMTLib throw new NotImplementedException(); } - public override void Assert(VCExpr vc, bool polarity) + public override void Assert(VCExpr vc, bool polarity, bool isSoft = false, int weight = 1) { OptimizationRequests.Clear(); - string a = ""; - if (polarity) - { - a = "(assert " + VCExpr2String(vc, 1) + ")"; + string assert = "assert"; + if (options.Solver == SolverKind.Z3 && isSoft) { + assert += "-soft"; } - else - { - a = "(assert (not\n" + VCExpr2String(vc, 1) + "\n))"; + var expr = polarity ? VCExpr2String(vc, 1) : "(not\n" + VCExpr2String(vc, 1) + "\n)"; + if (options.Solver == SolverKind.Z3 && isSoft) { + expr += " :weight " + weight; } AssertAxioms(); - SendThisVC(a); + SendThisVC("(" + assert + " " + expr + ")"); SendOptimizationRequests(); } diff --git a/Source/Provers/SMTLib/SMTLibLineariser.cs b/Source/Provers/SMTLib/SMTLibLineariser.cs index de8798b8..6df44c2f 100644 --- a/Source/Provers/SMTLib/SMTLibLineariser.cs +++ b/Source/Provers/SMTLib/SMTLibLineariser.cs @@ -34,7 +34,7 @@ namespace Microsoft.Boogie.SMTLib public class SMTLibExprLineariser : IVCExprVisitor { - public static string ToString(VCExpr e, UniqueNamer namer, SMTLibProverOptions opts, IList optReqs = null) + public static string ToString(VCExpr e, UniqueNamer namer, SMTLibProverOptions opts, ref bool containsNamedAssumes, IList optReqs = null) { Contract.Requires(e != null); Contract.Requires(namer != null); @@ -44,6 +44,7 @@ namespace Microsoft.Boogie.SMTLib SMTLibExprLineariser lin = new SMTLibExprLineariser(sw, namer, opts, optReqs); Contract.Assert(lin != null); lin.Linearise(e, LineariserOptions.Default); + containsNamedAssumes |= lin.ContainsNamedAssumes; return cce.NonNull(sw.ToString()); } @@ -76,6 +77,12 @@ namespace Microsoft.Boogie.SMTLib readonly IList OptimizationRequests; + bool containsNamedAssumes; + public bool ContainsNamedAssumes + { + get { return containsNamedAssumes; } + } + public SMTLibExprLineariser(TextWriter wr, UniqueNamer namer, SMTLibProverOptions opts, IList optReqs = null) { Contract.Requires(wr != null); Contract.Requires(namer != null); @@ -270,7 +277,18 @@ namespace Microsoft.Boogie.SMTLib && (node.Op.Equals(VCExpressionGenerator.MinimizeOp) || node.Op.Equals(VCExpressionGenerator.MaximizeOp))) { string optOp = node.Op.Equals(VCExpressionGenerator.MinimizeOp) ? "minimize" : "maximize"; - OptimizationRequests.Add(string.Format("({0} {1})", optOp, ToString(node[0], Namer, ProverOptions))); + OptimizationRequests.Add(string.Format("({0} {1})", optOp, ToString(node[0], Namer, ProverOptions, ref containsNamedAssumes))); + Linearise(node[1], options); + return true; + } + if (node.Op.Equals(VCExpressionGenerator.SoftOp)) + { + Linearise(node[1], options); + return true; + } + if (node.Op.Equals(VCExpressionGenerator.NamedAssumeOp)) + { + containsNamedAssumes = true; Linearise(node[1], options); return true; } diff --git a/Source/Provers/SMTLib/TypeDeclCollector.cs b/Source/Provers/SMTLib/TypeDeclCollector.cs index 1c23c22f..72540c9c 100644 --- a/Source/Provers/SMTLib/TypeDeclCollector.cs +++ b/Source/Provers/SMTLib/TypeDeclCollector.cs @@ -210,7 +210,15 @@ void ObjectInvariant() if (node.Op is VCExprStoreOp) RegisterStore(node); else if (node.Op is VCExprSelectOp) RegisterSelect(node); - else { + else if (node.Op.Equals(VCExpressionGenerator.SoftOp)) { + var exprVar = node[0] as VCExprVar; + AddDeclaration(string.Format("(declare-fun {0} () Bool)", exprVar.Name)); + AddDeclaration(string.Format("(assert-soft {0} :weight 1)", exprVar.Name)); + } else if (node.Op.Equals(VCExpressionGenerator.NamedAssumeOp)) { + var exprVar = node[0] as VCExprVar; + AddDeclaration(string.Format("(declare-fun {0} () Bool)", exprVar.Name)); + AddDeclaration(string.Format("(assert (! {0} :named {1}))", exprVar.Name, "aux$$" + exprVar.Name)); + } else { VCExprBoogieFunctionOp op = node.Op as VCExprBoogieFunctionOp; if (op != null && !(op.Func is DatatypeConstructor) && !(op.Func is DatatypeMembership) && !(op.Func is DatatypeSelector) && @@ -255,7 +263,7 @@ void ObjectInvariant() RegisterType(node.Type); string decl = "(declare-fun " + printedName + " () " + TypeToString(node.Type) + ")"; - if (!printedName.StartsWith("assume$$")) + if (!(printedName.StartsWith("assume$$") || printedName.StartsWith("soft$$"))) { AddDeclaration(decl); } diff --git a/Source/VCExpr/VCExprAST.cs b/Source/VCExpr/VCExprAST.cs index 2fbb102c..a58cfb7f 100644 --- a/Source/VCExpr/VCExprAST.cs +++ b/Source/VCExpr/VCExprAST.cs @@ -343,6 +343,8 @@ namespace Microsoft.Boogie { public static readonly VCExprOp MinimizeOp = new VCExprCustomOp("minimize##dummy", 2, Type.Bool); public static readonly VCExprOp MaximizeOp = new VCExprCustomOp("maximize##dummy", 2, Type.Bool); + public static readonly VCExprOp NamedAssumeOp = new VCExprCustomOp("named_assume##dummy", 2, Type.Bool); + public static readonly VCExprOp SoftOp = new VCExprCustomOp("soft##dummy", 2, Type.Bool); public VCExprOp BoogieFunctionOp(Function func) { Contract.Requires(func != null); diff --git a/Source/VCGeneration/Check.cs b/Source/VCGeneration/Check.cs index 8c1ae407..ae4d158a 100644 --- a/Source/VCGeneration/Check.cs +++ b/Source/VCGeneration/Check.cs @@ -346,7 +346,7 @@ namespace Microsoft.Boogie { } } - public void BeginCheck(string descriptiveName, VCExpr vc, ProverInterface.ErrorHandler handler, IList namedAssumeVars = null) { + public void BeginCheck(string descriptiveName, VCExpr vc, ProverInterface.ErrorHandler handler) { Contract.Requires(descriptiveName != null); Contract.Requires(vc != null); Contract.Requires(handler != null); @@ -360,7 +360,6 @@ namespace Microsoft.Boogie { thmProver.Reset(gen); SetTimeout(); proverStart = DateTime.UtcNow; - thmProver.NamedAssumeVars = namedAssumeVars; thmProver.BeginCheck(descriptiveName, vc, handler); // gen.ClearSharedFormulas(); PR: don't know yet what to do with this guy @@ -388,8 +387,6 @@ namespace Microsoft.Boogie { public abstract class ProverInterface { - public IList NamedAssumeVars; - public static ProverInterface CreateProver(Program prog, string/*?*/ logFilePath, bool appendLogFile, int timeout, int taskID = -1) { Contract.Requires(prog != null); @@ -546,7 +543,7 @@ namespace Microsoft.Boogie { } // (assert vc) - public virtual void Assert(VCExpr vc, bool polarity) + public virtual void Assert(VCExpr vc, bool polarity, bool isSoft = false, int weight = 1) { throw new NotImplementedException(); } diff --git a/Source/VCGeneration/VC.cs b/Source/VCGeneration/VC.cs index ad067c04..6e43e917 100644 --- a/Source/VCGeneration/VC.cs +++ b/Source/VCGeneration/VC.cs @@ -1386,8 +1386,7 @@ namespace VC { var exprGen = ctx.ExprGen; VCExpr controlFlowVariableExpr = CommandLineOptions.Clo.UseLabels ? null : exprGen.Integer(BigNum.ZERO); - var namedAssumeVars = new List(); - VCExpr vc = parent.GenerateVCAux(impl, controlFlowVariableExpr, label2absy, checker.TheoremProver.Context, namedAssumeVars: namedAssumeVars); + VCExpr vc = parent.GenerateVCAux(impl, controlFlowVariableExpr, label2absy, checker.TheoremProver.Context); Contract.Assert(vc != null); if (!CommandLineOptions.Clo.UseLabels) @@ -1415,7 +1414,7 @@ namespace VC { string desc = cce.NonNull(impl.Name); if (no >= 0) desc += "_split" + no; - checker.BeginCheck(desc, vc, reporter, namedAssumeVars); + checker.BeginCheck(desc, vc, reporter); } private void SoundnessCheck(HashSet/*!*/>/*!*/ cache, Block/*!*/ orig, List/*!*/ copies) { @@ -1568,8 +1567,7 @@ namespace VC { } break; case CommandLineOptions.VCVariety.DagIterative: - // TODO(wuestholz): Support named assume statements not just for this encoding. - vc = LetVCIterative(impl.Blocks, controlFlowVariableExpr, label2absy, proverContext, out assertionCount, namedAssumeVars: namedAssumeVars); + vc = LetVCIterative(impl.Blocks, controlFlowVariableExpr, label2absy, proverContext, out assertionCount); break; case CommandLineOptions.VCVariety.Doomed: vc = FlatBlockVC(impl, label2absy, false, false, true, proverContext, out assertionCount); @@ -3393,7 +3391,7 @@ namespace VC { Dictionary label2absy, ProverContext proverCtxt, out int assertionCount, - bool isPositiveContext = true, IList namedAssumeVars = null) + bool isPositiveContext = true) { Contract.Requires(blocks != null); Contract.Requires(proverCtxt != null); @@ -3453,7 +3451,7 @@ namespace VC { } VCContext context = new VCContext(label2absy, proverCtxt, controlFlowVariableExpr, isPositiveContext); - VCExpr vc = Wlp.Block(block, SuccCorrect, context, namedAssumeVars); + VCExpr vc = Wlp.Block(block, SuccCorrect, context); assertionCount += context.AssertionCount; VCExprVar v = gen.Variable(block.Label + "_correct", Bpl.Type.Bool); diff --git a/Source/VCGeneration/Wlp.cs b/Source/VCGeneration/Wlp.cs index 508a1400..07db709d 100644 --- a/Source/VCGeneration/Wlp.cs +++ b/Source/VCGeneration/Wlp.cs @@ -48,7 +48,7 @@ namespace VC { public class Wlp { - public static VCExpr Block(Block b, VCExpr N, VCContext ctxt, IList namedAssumeVars = null) + public static VCExpr Block(Block b, VCExpr N, VCContext ctxt) //modifies ctxt.*; { Contract.Requires(b != null); @@ -63,7 +63,7 @@ namespace VC { for (int i = b.Cmds.Count; --i >= 0; ) { - res = Cmd(b, cce.NonNull( b.Cmds[i]), res, ctxt, namedAssumeVars); + res = Cmd(b, cce.NonNull( b.Cmds[i]), res, ctxt); } int id = b.UniqueId; @@ -87,7 +87,7 @@ namespace VC { /// /// Computes the wlp for an assert or assume command "cmd". /// - internal static VCExpr Cmd(Block b, Cmd cmd, VCExpr N, VCContext ctxt, IList namedAssumeVars = null) { + internal static VCExpr Cmd(Block b, Cmd cmd, VCExpr N, VCContext ctxt) { Contract.Requires(cmd != null); Contract.Requires(N != null); Contract.Requires(ctxt != null); @@ -193,11 +193,16 @@ namespace VC { var expr = ctxt.Ctxt.BoogieExprTranslator.Translate(ac.Expr); var aid = QKeyValue.FindStringAttribute(ac.Attributes, "id"); - if (CommandLineOptions.Clo.PrintNecessaryAssumes && aid != null && namedAssumeVars != null) + if (CommandLineOptions.Clo.PrintNecessaryAssumes && aid != null) { var v = gen.Variable("assume$$" + aid, Microsoft.Boogie.Type.Bool); - namedAssumeVars.Add(v); - expr = gen.ImpliesSimp(v, expr); + expr = gen.Function(VCExpressionGenerator.NamedAssumeOp, v, gen.ImpliesSimp(v, expr)); + } + var soft = QKeyValue.FindBoolAttribute(ac.Attributes, "soft"); + if (soft && aid != null) + { + var v = gen.Variable("soft$$" + aid, Microsoft.Boogie.Type.Bool); + expr = gen.Function(VCExpressionGenerator.SoftOp, v, gen.ImpliesSimp(v, expr)); } return MaybeWrapWithOptimization(ctxt, gen, ac.Attributes, gen.ImpliesSimp(expr, N)); } else { diff --git a/Test/unnecessaryassumes/unnecessaryassumes1.bpl.expect b/Test/unnecessaryassumes/unnecessaryassumes1.bpl.expect index dd04bb46..0d3aeca2 100644 --- a/Test/unnecessaryassumes/unnecessaryassumes1.bpl.expect +++ b/Test/unnecessaryassumes/unnecessaryassumes1.bpl.expect @@ -1,3 +1,3 @@ -Necessary assume command(s): s0, s3, s2 +Necessary assume command(s): s0, s2, s3 Boogie program verifier finished with 3 verified, 0 errors -- cgit v1.2.3 From 63c4b7642cd4566a39906b2d73c47b4ebe9f7c1c Mon Sep 17 00:00:00 2001 From: Valentin Wüstholz Date: Mon, 7 Mar 2016 18:25:13 -0600 Subject: Add support for weights on soft assumes. --- Source/Provers/SMTLib/SMTLibLineariser.cs | 2 +- Source/Provers/SMTLib/TypeDeclCollector.cs | 4 ++-- Source/VCExpr/VCExprAST.cs | 11 ++++++++++- Source/VCGeneration/Wlp.cs | 5 +++-- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/Source/Provers/SMTLib/SMTLibLineariser.cs b/Source/Provers/SMTLib/SMTLibLineariser.cs index 6df44c2f..06554fcb 100644 --- a/Source/Provers/SMTLib/SMTLibLineariser.cs +++ b/Source/Provers/SMTLib/SMTLibLineariser.cs @@ -281,7 +281,7 @@ namespace Microsoft.Boogie.SMTLib Linearise(node[1], options); return true; } - if (node.Op.Equals(VCExpressionGenerator.SoftOp)) + if (node.Op is VCExprSoftOp) { Linearise(node[1], options); return true; diff --git a/Source/Provers/SMTLib/TypeDeclCollector.cs b/Source/Provers/SMTLib/TypeDeclCollector.cs index 72540c9c..d911ce58 100644 --- a/Source/Provers/SMTLib/TypeDeclCollector.cs +++ b/Source/Provers/SMTLib/TypeDeclCollector.cs @@ -210,10 +210,10 @@ void ObjectInvariant() if (node.Op is VCExprStoreOp) RegisterStore(node); else if (node.Op is VCExprSelectOp) RegisterSelect(node); - else if (node.Op.Equals(VCExpressionGenerator.SoftOp)) { + else if (node.Op is VCExprSoftOp) { var exprVar = node[0] as VCExprVar; AddDeclaration(string.Format("(declare-fun {0} () Bool)", exprVar.Name)); - AddDeclaration(string.Format("(assert-soft {0} :weight 1)", exprVar.Name)); + AddDeclaration(string.Format("(assert-soft {0} :weight {1})", exprVar.Name, ((VCExprSoftOp)node.Op).Weight)); } else if (node.Op.Equals(VCExpressionGenerator.NamedAssumeOp)) { var exprVar = node[0] as VCExprVar; AddDeclaration(string.Format("(declare-fun {0} () Bool)", exprVar.Name)); diff --git a/Source/VCExpr/VCExprAST.cs b/Source/VCExpr/VCExprAST.cs index a58cfb7f..2c77a252 100644 --- a/Source/VCExpr/VCExprAST.cs +++ b/Source/VCExpr/VCExprAST.cs @@ -344,7 +344,6 @@ namespace Microsoft.Boogie { public static readonly VCExprOp MinimizeOp = new VCExprCustomOp("minimize##dummy", 2, Type.Bool); public static readonly VCExprOp MaximizeOp = new VCExprCustomOp("maximize##dummy", 2, Type.Bool); public static readonly VCExprOp NamedAssumeOp = new VCExprCustomOp("named_assume##dummy", 2, Type.Bool); - public static readonly VCExprOp SoftOp = new VCExprCustomOp("soft##dummy", 2, Type.Bool); public VCExprOp BoogieFunctionOp(Function func) { Contract.Requires(func != null); @@ -1570,6 +1569,16 @@ namespace Microsoft.Boogie.VCExprAST { } } + public class VCExprSoftOp : VCExprCustomOp + { + public readonly int Weight; + + public VCExprSoftOp(int weight) : base("soft##dummy", 2, Microsoft.Boogie.Type.Bool) + { + Weight = weight; + } + } + public class VCExprCustomOp : VCExprOp { public readonly string/*!*/ Name; int arity; diff --git a/Source/VCGeneration/Wlp.cs b/Source/VCGeneration/Wlp.cs index 07db709d..d18c544a 100644 --- a/Source/VCGeneration/Wlp.cs +++ b/Source/VCGeneration/Wlp.cs @@ -199,10 +199,11 @@ namespace VC { expr = gen.Function(VCExpressionGenerator.NamedAssumeOp, v, gen.ImpliesSimp(v, expr)); } var soft = QKeyValue.FindBoolAttribute(ac.Attributes, "soft"); - if (soft && aid != null) + var softWeight = QKeyValue.FindIntAttribute(ac.Attributes, "soft", 0); + if ((soft || 0 < softWeight) && aid != null) { var v = gen.Variable("soft$$" + aid, Microsoft.Boogie.Type.Bool); - expr = gen.Function(VCExpressionGenerator.SoftOp, v, gen.ImpliesSimp(v, expr)); + expr = gen.Function(new VCExprSoftOp(Math.Max(softWeight, 1)), v, gen.ImpliesSimp(v, expr)); } return MaybeWrapWithOptimization(ctxt, gen, ac.Attributes, gen.ImpliesSimp(expr, N)); } else { -- cgit v1.2.3 From 8ed5dab22d8377924ee6282b83c1b1f8aa8f3573 Mon Sep 17 00:00:00 2001 From: Valentin Wüstholz Date: Wed, 9 Mar 2016 19:18:15 -0600 Subject: Improve support for identifying unnecessary assumes. --- Source/Provers/SMTLib/ProverInterface.cs | 84 +++++++++++++++++++----------- Source/Provers/SMTLib/SMTLibLineariser.cs | 20 +++---- Source/Provers/SMTLib/TypeDeclCollector.cs | 7 ++- Source/VCGeneration/Check.cs | 4 ++ Source/VCGeneration/Wlp.cs | 5 +- 5 files changed, 73 insertions(+), 47 deletions(-) diff --git a/Source/Provers/SMTLib/ProverInterface.cs b/Source/Provers/SMTLib/ProverInterface.cs index 432d7f3e..300fbc10 100644 --- a/Source/Provers/SMTLib/ProverInterface.cs +++ b/Source/Provers/SMTLib/ProverInterface.cs @@ -86,6 +86,21 @@ namespace Microsoft.Boogie.SMTLib } } + public override void AssertNamed(VCExpr vc, bool polarity, string name) + { + string vcString; + if (polarity) + { + vcString = VCExpr2String(vc, 1); + } + else + { + vcString = "(not " + VCExpr2String(vc, 1) + ")"; + } + AssertAxioms(); + SendThisVC(string.Format("(assert (! {0} :named {1}))", vcString, name)); + } + private void SetupAxiomBuilder(VCExpressionGenerator gen) { switch (CommandLineOptions.Clo.TypeEncodingMethod) @@ -424,7 +439,7 @@ namespace Microsoft.Boogie.SMTLib Process.Inspector.NewProblem(descriptiveName, vc, handler); } - SendThisVC("(check-sat)"); + SendCheckSat(); FlushLogFile(); } @@ -474,6 +489,8 @@ namespace Microsoft.Boogie.SMTLib ctx.KnownDatatypeConstructors.Clear(); ctx.parent = this; DeclCollector.Reset(); + NamedAssumes.Clear(); + UsedNamedAssumes = null; SendThisVC("; did a full reset"); } } @@ -1284,17 +1301,33 @@ namespace Microsoft.Boogie.SMTLib var reporter = handler as VC.VCGen.ErrorReporter; // TODO(wuestholz): Is the reporter ever null? - if (CommandLineOptions.Clo.PrintNecessaryAssumes && ContainsNamedAssumes && result == Outcome.Valid && reporter != null) + if (usingUnsatCore && result == Outcome.Valid && reporter != null && 0 < NamedAssumes.Count) { - SendThisVC("(get-unsat-core)"); - var resp = Process.GetProverResponse(); - if (resp.Name != "") + if (usingUnsatCore) { - reporter.AddNecessaryAssume(resp.Name.Substring("aux$$assume$$".Length)); + UsedNamedAssumes = new HashSet(); + SendThisVC("(get-unsat-core)"); + var resp = Process.GetProverResponse(); + if (resp.Name != "") + { + UsedNamedAssumes.Add(resp.Name); + if (CommandLineOptions.Clo.PrintNecessaryAssumes) + { + reporter.AddNecessaryAssume(resp.Name.Substring("aux$$assume$$".Length)); + } + } + foreach (var arg in resp.Arguments) + { + UsedNamedAssumes.Add(arg.Name); + if (CommandLineOptions.Clo.PrintNecessaryAssumes) + { + reporter.AddNecessaryAssume(arg.Name.Substring("aux$$assume$$".Length)); + } + } } - foreach (var arg in resp.Arguments) + else { - reporter.AddNecessaryAssume(arg.Name.Substring("aux$$assume$$".Length)); + UsedNamedAssumes = null; } } @@ -1457,13 +1490,13 @@ namespace Microsoft.Boogie.SMTLib expr = "false"; } SendThisVC("(assert " + expr + ")"); - SendThisVC("(check-sat)"); + SendCheckSat(); } else { string source = labels[labels.Length - 2]; string target = labels[labels.Length - 1]; SendThisVC("(assert (not (= (ControlFlow 0 " + source + ") (- " + target + "))))"); - SendThisVC("(check-sat)"); + SendCheckSat(); } } @@ -1508,7 +1541,7 @@ namespace Microsoft.Boogie.SMTLib SendThisVC("(apply (then (using-params propagate-values :max_rounds 1) simplify) :print false)"); } FlushLogFile(); - SendThisVC("(check-sat)"); + SendCheckSat(); queries++; return GetResponse(); } @@ -1984,8 +2017,6 @@ namespace Microsoft.Boogie.SMTLib readonly IList OptimizationRequests = new List(); - bool ContainsNamedAssumes; - protected string VCExpr2String(VCExpr expr, int polarity) { Contract.Requires(expr != null); @@ -2025,8 +2056,8 @@ namespace Microsoft.Boogie.SMTLib DeclCollector.Collect(sortedExpr); FeedTypeDeclsToProver(); - AddAxiom(SMTLibExprLineariser.ToString(sortedAxioms, Namer, options, ref ContainsNamedAssumes)); - string res = SMTLibExprLineariser.ToString(sortedExpr, Namer, options, ref ContainsNamedAssumes, OptimizationRequests); + AddAxiom(SMTLibExprLineariser.ToString(sortedAxioms, Namer, options, namedAssumes: NamedAssumes)); + string res = SMTLibExprLineariser.ToString(sortedExpr, Namer, options, NamedAssumes, OptimizationRequests); Contract.Assert(res != null); if (CommandLineOptions.Clo.Trace) @@ -2167,9 +2198,15 @@ namespace Microsoft.Boogie.SMTLib public override void Check() { PrepareCommon(); - SendThisVC("(check-sat)"); + SendCheckSat(); FlushLogFile(); } + + public void SendCheckSat() + { + UsedNamedAssumes = null; + SendThisVC("(check-sat)"); + } public override void SetTimeOut(int ms) { @@ -2366,21 +2403,6 @@ namespace Microsoft.Boogie.SMTLib return opts; } - public override void AssertNamed(VCExpr vc, bool polarity, string name) - { - string vcString; - if (polarity) - { - vcString = VCExpr2String(vc, 1); - } - else - { - vcString = "(not " + VCExpr2String(vc, 1) + ")"; - } - AssertAxioms(); - SendThisVC(string.Format("(assert (! {0} :named {1}))", vcString, name)); - } - public override VCExpr ComputeInterpolant(VCExpr A, VCExpr B) { string A_str = VCExpr2String(A, 1); diff --git a/Source/Provers/SMTLib/SMTLibLineariser.cs b/Source/Provers/SMTLib/SMTLibLineariser.cs index 06554fcb..06aa5bbe 100644 --- a/Source/Provers/SMTLib/SMTLibLineariser.cs +++ b/Source/Provers/SMTLib/SMTLibLineariser.cs @@ -34,17 +34,16 @@ namespace Microsoft.Boogie.SMTLib public class SMTLibExprLineariser : IVCExprVisitor { - public static string ToString(VCExpr e, UniqueNamer namer, SMTLibProverOptions opts, ref bool containsNamedAssumes, IList optReqs = null) + public static string ToString(VCExpr e, UniqueNamer namer, SMTLibProverOptions opts, ISet namedAssumes = null, IList optReqs = null, ISet tryAssumes = null) { Contract.Requires(e != null); Contract.Requires(namer != null); Contract.Ensures(Contract.Result() != null); StringWriter sw = new StringWriter(); - SMTLibExprLineariser lin = new SMTLibExprLineariser(sw, namer, opts, optReqs); + SMTLibExprLineariser lin = new SMTLibExprLineariser(sw, namer, opts, namedAssumes, optReqs); Contract.Assert(lin != null); lin.Linearise(e, LineariserOptions.Default); - containsNamedAssumes |= lin.ContainsNamedAssumes; return cce.NonNull(sw.ToString()); } @@ -76,20 +75,16 @@ namespace Microsoft.Boogie.SMTLib internal readonly SMTLibProverOptions ProverOptions; readonly IList OptimizationRequests; + readonly ISet NamedAssumes; - bool containsNamedAssumes; - public bool ContainsNamedAssumes - { - get { return containsNamedAssumes; } - } - - public SMTLibExprLineariser(TextWriter wr, UniqueNamer namer, SMTLibProverOptions opts, IList optReqs = null) + public SMTLibExprLineariser(TextWriter wr, UniqueNamer namer, SMTLibProverOptions opts, ISet namedAssumes = null, IList optReqs = null) { Contract.Requires(wr != null); Contract.Requires(namer != null); this.wr = wr; this.Namer = namer; this.ProverOptions = opts; this.OptimizationRequests = optReqs; + this.NamedAssumes = namedAssumes; } public void Linearise(VCExpr expr, LineariserOptions options) @@ -277,7 +272,7 @@ namespace Microsoft.Boogie.SMTLib && (node.Op.Equals(VCExpressionGenerator.MinimizeOp) || node.Op.Equals(VCExpressionGenerator.MaximizeOp))) { string optOp = node.Op.Equals(VCExpressionGenerator.MinimizeOp) ? "minimize" : "maximize"; - OptimizationRequests.Add(string.Format("({0} {1})", optOp, ToString(node[0], Namer, ProverOptions, ref containsNamedAssumes))); + OptimizationRequests.Add(string.Format("({0} {1})", optOp, ToString(node[0], Namer, ProverOptions, NamedAssumes))); Linearise(node[1], options); return true; } @@ -288,7 +283,8 @@ namespace Microsoft.Boogie.SMTLib } if (node.Op.Equals(VCExpressionGenerator.NamedAssumeOp)) { - containsNamedAssumes = true; + var exprVar = node[0] as VCExprVar; + NamedAssumes.Add(exprVar); Linearise(node[1], options); return true; } diff --git a/Source/Provers/SMTLib/TypeDeclCollector.cs b/Source/Provers/SMTLib/TypeDeclCollector.cs index d911ce58..eaed83e9 100644 --- a/Source/Provers/SMTLib/TypeDeclCollector.cs +++ b/Source/Provers/SMTLib/TypeDeclCollector.cs @@ -217,7 +217,10 @@ void ObjectInvariant() } else if (node.Op.Equals(VCExpressionGenerator.NamedAssumeOp)) { var exprVar = node[0] as VCExprVar; AddDeclaration(string.Format("(declare-fun {0} () Bool)", exprVar.Name)); - AddDeclaration(string.Format("(assert (! {0} :named {1}))", exprVar.Name, "aux$$" + exprVar.Name)); + if (CommandLineOptions.Clo.PrintNecessaryAssumes) + { + AddDeclaration(string.Format("(assert (! {0} :named {1}))", exprVar.Name, "aux$$" + exprVar.Name)); + } } else { VCExprBoogieFunctionOp op = node.Op as VCExprBoogieFunctionOp; if (op != null && @@ -263,7 +266,7 @@ void ObjectInvariant() RegisterType(node.Type); string decl = "(declare-fun " + printedName + " () " + TypeToString(node.Type) + ")"; - if (!(printedName.StartsWith("assume$$") || printedName.StartsWith("soft$$"))) + if (!(printedName.StartsWith("assume$$") || printedName.StartsWith("soft$$") || printedName.StartsWith("try$$"))) { AddDeclaration(decl); } diff --git a/Source/VCGeneration/Check.cs b/Source/VCGeneration/Check.cs index ae4d158a..7bda0022 100644 --- a/Source/VCGeneration/Check.cs +++ b/Source/VCGeneration/Check.cs @@ -453,6 +453,10 @@ namespace Microsoft.Boogie { Undetermined, Bounded } + + public readonly ISet NamedAssumes = new HashSet(); + public ISet UsedNamedAssumes { get; protected set; } + public class ErrorHandler { // Used in CheckOutcomeCore public virtual int StartingProcId() diff --git a/Source/VCGeneration/Wlp.cs b/Source/VCGeneration/Wlp.cs index d18c544a..cad5914b 100644 --- a/Source/VCGeneration/Wlp.cs +++ b/Source/VCGeneration/Wlp.cs @@ -193,9 +193,10 @@ namespace VC { var expr = ctxt.Ctxt.BoogieExprTranslator.Translate(ac.Expr); var aid = QKeyValue.FindStringAttribute(ac.Attributes, "id"); - if (CommandLineOptions.Clo.PrintNecessaryAssumes && aid != null) + if (aid != null) { - var v = gen.Variable("assume$$" + aid, Microsoft.Boogie.Type.Bool); + var isTry = QKeyValue.FindBoolAttribute(ac.Attributes, "try"); + var v = gen.Variable((isTry ? "try$$" : "assume$$") + aid, Microsoft.Boogie.Type.Bool); expr = gen.Function(VCExpressionGenerator.NamedAssumeOp, v, gen.ImpliesSimp(v, expr)); } var soft = QKeyValue.FindBoolAttribute(ac.Attributes, "soft"); -- cgit v1.2.3