From ce03249fa06a7442df96352bb55e1bfba3e30365 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Mon, 21 Feb 2022 23:21:11 +0100 Subject: [PATCH 01/32] Reduce references to `CoreOptions.Clo` from 204 to 92 (#508) - CheckerPool is now a field of ExecutionEngine and ExecutionEngine inherits from `IDisposable`, to give more control over the lifetime of the `CheckerPool` to Boogie consumers and to ensure `CheckerPool` does not hold a different `ExecutionEngineOptions` object then is used by the rest of the code. - Snapshots turn out not to work well when reusing the checkerPool, so when snapshots are used the checker pool is not reused for each snapshot. - Introduce interfaces `ExecutionEngineOptions`, `HoudiniOptions`, `VCGenOptions` and `ConcurrencyOptions` for holding options related to their respective assemblies. - Reduce the references to `CoreOptions.Clo` from 204 to 92. --- Source/AbsInt/IntervalDomain.cs | 4 +- Source/AbsInt/NativeLattice.cs | 22 +- Source/BoogieDriver/BoogieDriver.cs | 14 +- Source/Concurrency/ConcurrencyOptions.cs | 2 +- Source/Core/Absy.cs | 48 +-- Source/Core/AbsyCmd.cs | 20 +- Source/Core/AbsyExpr.cs | 2 +- Source/Core/CoreOptions.cs | 4 +- Source/Core/DeadVarElim.cs | 4 +- Source/Core/Helpers.cs | 2 +- Source/Core/Inline.cs | 10 +- .../Core/InterProceduralReachabilityGraph.cs | 4 +- Source/Core/LambdaHelper.cs | 10 +- Source/Core/MaxHolesLambdaLifter.cs | 6 +- Source/Core/Monomorphization.cs | 2 +- Source/Core/VariableDependenceAnalyser.cs | 20 +- Source/Core/Xml.cs | 2 +- Source/ExecutionEngine/CommandLineOptions.cs | 58 ++-- Source/ExecutionEngine/ExecutionEngine.cs | 328 +++++++++--------- .../VerificationResultCache.cs | 39 ++- .../Houdini/AnnotationDependenceAnalyser.cs | 12 +- Source/Houdini/Checker.cs | 26 +- Source/Houdini/Houdini.cs | 48 +-- Source/Houdini/StagedHoudini.cs | 10 +- Source/Provers/SMTLib/ProverContext.cs | 2 +- Source/Provers/SMTLib/ProverInterface.cs | 19 +- Source/Provers/SMTLib/ProverUtil.cs | 2 +- Source/Provers/SMTLib/SMTLibOptions.cs | 2 +- .../SMTLib/SMTLibProcessTheoremProver.cs | 10 +- Source/Provers/SMTLib/TypeDeclCollector.cs | 6 +- Source/UnitTests/CoreTests/Duplicator.cs | 4 +- .../ExecutionEngineTests/CancellationTests.cs | 31 +- .../ExecutionEngineTests/GetProverLogs.cs | 13 +- .../ExecutionEngineTests/RandomSeedTest.cs | 10 +- .../SolverLogStabilityTest.cs | 10 +- Source/VCExpr/TypeErasureArguments.cs | 2 +- Source/VCExpr/TypeErasurePremisses.cs | 6 +- Source/VCExpr/VCExprASTPrinter.cs | 2 +- Source/VCGeneration/Checker.cs | 11 +- Source/VCGeneration/ConditionGeneration.cs | 88 +++-- Source/VCGeneration/Counterexample.cs | 45 ++- Source/VCGeneration/Prune/Prune.cs | 4 +- Source/VCGeneration/Split.cs | 98 +++--- Source/VCGeneration/SplitAndVerifyWorker.cs | 2 +- Source/VCGeneration/VCGen.cs | 78 +++-- Source/VCGeneration/Wlp.cs | 29 +- 46 files changed, 617 insertions(+), 554 deletions(-) diff --git a/Source/AbsInt/IntervalDomain.cs b/Source/AbsInt/IntervalDomain.cs index 011f28a31..53b670bc4 100644 --- a/Source/AbsInt/IntervalDomain.cs +++ b/Source/AbsInt/IntervalDomain.cs @@ -214,8 +214,8 @@ public static IEnumerable> Merge(Node a, Node b) public Expr ToExpr() { - if (!V.IsMutable && CommandLineOptions.Clo.InstrumentInfer != - CommandLineOptions.InstrumentationPlaces.Everywhere) + if (!V.IsMutable && CoreOptions.Clo.InstrumentInfer != + CoreOptions.InstrumentationPlaces.Everywhere) { // omit invariants about readonly variables return Expr.True; diff --git a/Source/AbsInt/NativeLattice.cs b/Source/AbsInt/NativeLattice.cs index 3544713ce..eeb728428 100644 --- a/Source/AbsInt/NativeLattice.cs +++ b/Source/AbsInt/NativeLattice.cs @@ -77,7 +77,7 @@ public static void RunAbstractInterpretation(Program program) Helpers.ExtraTraceInformation("Starting abstract interpretation"); DateTime start = new DateTime(); // to please compiler's definite assignment rules - if (CommandLineOptions.Clo.Trace) + if (CoreOptions.Clo.Trace) { Console.WriteLine(); Console.WriteLine("Running abstract interpretation..."); @@ -87,11 +87,11 @@ public static void RunAbstractInterpretation(Program program) WidenPoints.Compute(program); NativeLattice lattice = null; - if (CommandLineOptions.Clo.Ai.J_Trivial) + if (CoreOptions.Clo.Ai.J_Trivial) { lattice = new TrivialDomain(); } - else if (CommandLineOptions.Clo.Ai.J_Intervals) + else if (CoreOptions.Clo.Ai.J_Intervals) { lattice = new NativeIntervalDomain(); } @@ -100,13 +100,13 @@ public static void RunAbstractInterpretation(Program program) { Dictionary procedureImplementations = ComputeProcImplMap(program); ComputeProgramInvariants(program, procedureImplementations, lattice); - if (CommandLineOptions.Clo.Ai.DebugStatistics) + if (CoreOptions.Clo.Ai.DebugStatistics) { Console.Error.WriteLine(lattice.DebugStatistics); } } - if (CommandLineOptions.Clo.Trace) + if (CoreOptions.Clo.Trace) { DateTime end = DateTime.UtcNow; TimeSpan elapsed = end - start; @@ -180,7 +180,7 @@ public static void Analyze(Implementation impl, NativeLattice lattice, NativeLat // the additional information. var pre = new NativeLattice.Element[impl.Blocks .Count]; // set to null if we never compute a join/widen at this block - var post = CommandLineOptions.Clo.InstrumentInfer == CommandLineOptions.InstrumentationPlaces.Everywhere + var post = CoreOptions.Clo.InstrumentInfer == CoreOptions.InstrumentationPlaces.Everywhere ? new NativeLattice.Element[impl.Blocks.Count] : null; var iterations = new int[impl.Blocks.Count]; @@ -223,7 +223,7 @@ public static void Analyze(Implementation impl, NativeLattice lattice, NativeLat // no change continue; } - else if (b.widenBlock && CommandLineOptions.Clo.Ai.StepsBeforeWidening <= iterations[id]) + else if (b.widenBlock && CoreOptions.Clo.Ai.StepsBeforeWidening <= iterations[id]) { e = lattice.Widen(pre[id], e); pre[id] = e; @@ -269,14 +269,14 @@ static void Instrument(Implementation impl, NativeLattice.Element[] pre, NativeL foreach (var b in impl.Blocks) { var element = pre[b.aiId]; - if (element != null && (b.widenBlock || CommandLineOptions.Clo.InstrumentInfer == - CommandLineOptions.InstrumentationPlaces.Everywhere)) + if (element != null && (b.widenBlock || CoreOptions.Clo.InstrumentInfer == + CoreOptions.InstrumentationPlaces.Everywhere)) { List newCommands = new List(); Expr inv = element.ToExpr(); PredicateCmd cmd; var kv = new QKeyValue(Token.NoToken, "inferred", new List(), null); - if (CommandLineOptions.Clo.InstrumentWithAsserts) + if (CoreOptions.Clo.InstrumentWithAsserts) { cmd = new AssertCmd(Token.NoToken, inv, kv); } @@ -291,7 +291,7 @@ static void Instrument(Implementation impl, NativeLattice.Element[] pre, NativeL { inv = post[b.aiId].ToExpr(); kv = new QKeyValue(Token.NoToken, "inferred", new List(), null); - if (CommandLineOptions.Clo.InstrumentWithAsserts) + if (CoreOptions.Clo.InstrumentWithAsserts) { cmd = new AssertCmd(Token.NoToken, inv, kv); } diff --git a/Source/BoogieDriver/BoogieDriver.cs b/Source/BoogieDriver/BoogieDriver.cs index a5b7cde86..802478285 100644 --- a/Source/BoogieDriver/BoogieDriver.cs +++ b/Source/BoogieDriver/BoogieDriver.cs @@ -11,18 +11,18 @@ public static int Main(string[] args) { Contract.Requires(cce.NonNullElements(args)); - - var options = new CommandLineOptionsImpl + var options = new CommandLineOptions { RunningBoogieFromCommandLine = true }; ExecutionEngine.printer = new ConsolePrinter(options); - CommandLineOptionsImpl.Install(options); + CommandLineOptions.Install(options); if (!options.Parse(args)) { return 1; } + using var executionEngine = new ExecutionEngine(options); if (options.ProcessInfoFlags()) { @@ -65,11 +65,11 @@ public static int Main(string[] args) Helpers.ExtraTraceInformation("Becoming sentient"); - var success = ExecutionEngine.ProcessFiles(options, fileList); + var success = executionEngine.ProcessFiles(fileList); - if (CommandLineOptions.Clo.XmlSink != null) + if (CoreOptions.Clo.XmlSink != null) { - CommandLineOptions.Clo.XmlSink.Close(); + CoreOptions.Clo.XmlSink.Close(); } if (options.Wait) @@ -81,7 +81,7 @@ public static int Main(string[] args) return success ? 0 : 1; } - private static List GetFileList(CommandLineOptionsImpl options) + private static List GetFileList(CommandLineOptions options) { List fileList = new List(); foreach (string file in options.Files) diff --git a/Source/Concurrency/ConcurrencyOptions.cs b/Source/Concurrency/ConcurrencyOptions.cs index 679501ea9..bd48cefb7 100644 --- a/Source/Concurrency/ConcurrencyOptions.cs +++ b/Source/Concurrency/ConcurrencyOptions.cs @@ -1,6 +1,6 @@ namespace Microsoft.Boogie; -public interface ConcurrencyOptions : CommandLineOptions +public interface ConcurrencyOptions : CoreOptions { bool TrustMoverTypes { get; } bool TrustInductiveSequentialization { get; } diff --git a/Source/Core/Absy.cs b/Source/Core/Absy.cs index 539a145b3..2f6c47a78 100644 --- a/Source/Core/Absy.cs +++ b/Source/Core/Absy.cs @@ -436,7 +436,7 @@ public override void Resolve(ResolutionContext rc) { int e = rc.ErrorCount; d.Resolve(rc); - if (CommandLineOptions.Clo.OverlookBoogieTypeErrors && rc.ErrorCount != e && d is Implementation) + if (CoreOptions.Clo.OverlookBoogieTypeErrors && rc.ErrorCount != e && d is Implementation) { // ignore this implementation System.Console.WriteLine("Warning: Ignoring implementation {0} because of translation resolution errors", @@ -813,7 +813,7 @@ public void UnrollLoops(int n, bool uc) /// private HashSet GetBreakBlocksOfLoop(Block header, Block backEdgeNode, Graph /*!*/ g) { - Contract.Assert(CommandLineOptions.Clo.DeterministicExtractLoops, + Contract.Assert(CoreOptions.Clo.DeterministicExtractLoops, "Can only be called with /deterministicExtractLoops option"); var immSuccBlks = new HashSet(); var loopBlocks = g.NaturalLoops(header, backEdgeNode); @@ -842,7 +842,7 @@ private HashSet GetBreakBlocksOfLoop(Block header, Block backEdgeNode, Gr private HashSet GetBlocksInAllNaturalLoops(Block header, Graph /*!*/ g) { - Contract.Assert(CommandLineOptions.Clo.DeterministicExtractLoops, + Contract.Assert(CoreOptions.Clo.DeterministicExtractLoops, "Can only be called with /deterministicExtractLoops option"); var allBlocksInNaturalLoops = new HashSet(); foreach (Block /*!*/ source in g.BackEdgeNodes(header)) @@ -882,7 +882,7 @@ void CreateProceduresForLoops(Implementation impl, Graph /*!*/ g, AddToFullMap(fullMap, impl.Name, block.Label, block); } - bool detLoopExtract = CommandLineOptions.Clo.DeterministicExtractLoops; + bool detLoopExtract = CoreOptions.Clo.DeterministicExtractLoops; Dictionary /*!*/> /*!*/ loopHeaderToInputs = new Dictionary /*!*/>(); @@ -1467,7 +1467,7 @@ public Dictionary> ExtractLoops(out HashSet> ExtractLoops(out HashSet original block @@ -1936,11 +1936,11 @@ public uint TimeLimit { get { - uint tl = CommandLineOptions.Clo.TimeLimit; + uint tl = CoreOptions.Clo.TimeLimit; CheckUIntAttribute("timeLimit", ref tl); if (tl < 0) { - tl = CommandLineOptions.Clo.TimeLimit; + tl = CoreOptions.Clo.TimeLimit; } return tl; } @@ -1950,11 +1950,11 @@ public uint ResourceLimit { get { - uint rl = CommandLineOptions.Clo.ResourceLimit; + uint rl = CoreOptions.Clo.ResourceLimit; CheckUIntAttribute("rlimit", ref rl); if (rl < 0) { - rl = CommandLineOptions.Clo.ResourceLimit; + rl = CoreOptions.Clo.ResourceLimit; } return rl; } @@ -2388,7 +2388,7 @@ public void EmitVitals(TokenTextWriter stream, int level, bool emitAttributes, b EmitAttributes(stream); } - if (CommandLineOptions.Clo.PrintWithUniqueASTIds && this.TypedIdent.HasName) + if (CoreOptions.Clo.PrintWithUniqueASTIds && this.TypedIdent.HasName) { stream.Write("h{0}^^", this.GetHashCode()); // the idea is that this will prepend the name printed by TypedIdent.Emit @@ -3399,7 +3399,7 @@ public override void Emit(TokenTextWriter stream, int level) stream.Write("{:define} "); } - if (CommandLineOptions.Clo.PrintWithUniqueASTIds) + if (CoreOptions.Clo.PrintWithUniqueASTIds) { stream.Write("h{0}^^{1}", this.GetHashCode(), TokenTextWriter.SanitizeIdentifier(this.Name)); } @@ -4228,8 +4228,8 @@ public bool SkipVerification return true; } - if (CommandLineOptions.Clo.ProcedureInlining == CommandLineOptions.Inlining.Assert || - CommandLineOptions.Clo.ProcedureInlining == CommandLineOptions.Inlining.Assume) + if (CoreOptions.Clo.ProcedureInlining == CoreOptions.Inlining.Assert || + CoreOptions.Clo.ProcedureInlining == CoreOptions.Inlining.Assume) { Expr inl = this.FindExprAttribute("inline"); if (inl == null) @@ -4243,7 +4243,7 @@ public bool SkipVerification } } - if (CommandLineOptions.Clo.StratifiedInlining > 0) + if (CoreOptions.Clo.StratifiedInlining > 0) { return !QKeyValue.FindBoolAttribute(Attributes, "entrypoint"); } @@ -4527,31 +4527,31 @@ public override void Emit(TokenTextWriter stream, int level) v.Emit(stream, level + 1); } - if (this.StructuredStmts != null && !CommandLineOptions.Clo.PrintInstrumented && - !CommandLineOptions.Clo.PrintInlined) + if (this.StructuredStmts != null && !CoreOptions.Clo.PrintInstrumented && + !CoreOptions.Clo.PrintInlined) { if (this.LocVars.Count > 0) { stream.WriteLine(); } - if (CommandLineOptions.Clo.PrintUnstructured < 2) + if (CoreOptions.Clo.PrintUnstructured < 2) { - if (CommandLineOptions.Clo.PrintUnstructured == 1) + if (CoreOptions.Clo.PrintUnstructured == 1) { stream.WriteLine(this, level + 1, "/*** structured program:"); } this.StructuredStmts.Emit(stream, level + 1); - if (CommandLineOptions.Clo.PrintUnstructured == 1) + if (CoreOptions.Clo.PrintUnstructured == 1) { stream.WriteLine(level + 1, "**** end structured program */"); } } } - if (this.StructuredStmts == null || 1 <= CommandLineOptions.Clo.PrintUnstructured || - CommandLineOptions.Clo.PrintInstrumented || CommandLineOptions.Clo.PrintInlined) + if (this.StructuredStmts == null || 1 <= CoreOptions.Clo.PrintUnstructured || + CoreOptions.Clo.PrintInstrumented || CoreOptions.Clo.PrintInlined) { foreach (Block b in this.Blocks) { @@ -4795,7 +4795,7 @@ public void ResetImplFormalMap() this.formalMap = map; - if (CommandLineOptions.Clo.PrintWithUniqueASTIds) + if (CoreOptions.Clo.PrintWithUniqueASTIds) { Console.WriteLine("Implementation.GetImplFormalMap on {0}:", this.Name); using TokenTextWriter stream = @@ -4974,7 +4974,7 @@ public void PruneUnreachableBlocks() reachable.Add(b); if (b.TransferCmd is GotoCmd) { - if (CommandLineOptions.Clo.PruneInfeasibleEdges) + if (CoreOptions.Clo.PruneInfeasibleEdges) { foreach (Cmd /*!*/ s in b.Cmds) { diff --git a/Source/Core/AbsyCmd.cs b/Source/Core/AbsyCmd.cs index 14534af60..16a954526 100644 --- a/Source/Core/AbsyCmd.cs +++ b/Source/Core/AbsyCmd.cs @@ -103,7 +103,7 @@ public void Emit(TokenTextWriter stream, int level) if (!Anonymous) { stream.WriteLine(level, "{0}:", - CommandLineOptions.Clo.PrintWithUniqueASTIds + CoreOptions.Clo.PrintWithUniqueASTIds ? String.Format("h{0}^^{1}", this.GetHashCode(), this.LabelName) : this.LabelName); } @@ -1331,7 +1331,7 @@ public void Emit(TokenTextWriter stream, int level) this, level, "{0}:{1}", - CommandLineOptions.Clo.PrintWithUniqueASTIds + CoreOptions.Clo.PrintWithUniqueASTIds ? String.Format("h{0}^^{1}", this.GetHashCode(), this.Label) : this.Label, this.widenBlock ? " // cut point" : ""); @@ -1427,7 +1427,7 @@ public static class ChecksumHelper public static void ComputeChecksums(Cmd cmd, Implementation impl, ISet usedVariables, byte[] currentChecksum = null) { - if (CommandLineOptions.Clo.VerifySnapshots < 2) + if (CoreOptions.Clo.VerifySnapshots < 2) { return; } @@ -1568,7 +1568,7 @@ public void CheckAssignments(TypecheckingContext tc) { tc.Error(this, "command assigns to an immutable variable: {0}", v.Name); } - else if (!CommandLineOptions.Clo.DoModSetAnalysis && v is GlobalVariable) + else if (!CoreOptions.Clo.DoModSetAnalysis && v is GlobalVariable) { if (!tc.Yields && !tc.InFrame(v)) { @@ -1730,7 +1730,7 @@ public override void Resolve(ResolutionContext rc) public override void Typecheck(TypecheckingContext tc) { - if (!CommandLineOptions.Clo.DoModSetAnalysis && !tc.Yields) + if (!CoreOptions.Clo.DoModSetAnalysis && !tc.Yields) { tc.Error(this, "enclosing procedure of a yield command must yield"); } @@ -2553,7 +2553,7 @@ public void ExtendDesugaring(IEnumerable before, IEnumerable beforePre public override void Emit(TokenTextWriter stream, int level) { //Contract.Requires(stream != null); - if (CommandLineOptions.Clo.PrintDesugarings && !stream.UseForComputingChecksums) + if (CoreOptions.Clo.PrintDesugarings && !stream.UseForComputingChecksums) { stream.WriteLine(this, level, "/*** desugaring:"); Desugaring.Emit(stream, level); @@ -2733,7 +2733,7 @@ public override void Resolve(ResolutionContext rc) public override void Typecheck(TypecheckingContext tc) { TypecheckAttributes(Attributes, tc); - if (!CommandLineOptions.Clo.DoModSetAnalysis) + if (!CoreOptions.Clo.DoModSetAnalysis) { if (!tc.Yields) { @@ -3139,7 +3139,7 @@ public override void Typecheck(TypecheckingContext tc) TypeParameters = SimpleTypeParamInstantiation.From(Proc.TypeParameters, actualTypeParams); - if (!CommandLineOptions.Clo.DoModSetAnalysis && IsAsync) + if (!CoreOptions.Clo.DoModSetAnalysis && IsAsync) { if (!tc.Yields) { @@ -3310,7 +3310,7 @@ protected override Cmd ComputeDesugaring() } } else if (req.CanAlwaysAssume() - || CommandLineOptions.Clo.StratifiedInlining > 0) + || CoreOptions.Clo.StratifiedInlining > 0) { // inject free requires as assume statements at the call site AssumeCmd /*!*/ @@ -4312,7 +4312,7 @@ public override void Emit(TokenTextWriter stream, int level) //Contract.Requires(stream != null); Contract.Assume(this.labelNames != null); stream.Write(this, level, "goto "); - if (CommandLineOptions.Clo.PrintWithUniqueASTIds) + if (CoreOptions.Clo.PrintWithUniqueASTIds) { if (labelTargets == null) { diff --git a/Source/Core/AbsyExpr.cs b/Source/Core/AbsyExpr.cs index 189690c96..fb21cdf85 100644 --- a/Source/Core/AbsyExpr.cs +++ b/Source/Core/AbsyExpr.cs @@ -1294,7 +1294,7 @@ public override int ComputeHashCode() public override void Emit(TokenTextWriter stream, int contextBindingStrength, bool fragileContext) { //Contract.Requires(stream != null); - if (CommandLineOptions.Clo.PrintWithUniqueASTIds && !stream.UseForComputingChecksums) + if (CoreOptions.Clo.PrintWithUniqueASTIds && !stream.UseForComputingChecksums) { stream.Write("{0}^^", this.Decl == null ? "NoDecl" : "h" + this.Decl.GetHashCode()); } diff --git a/Source/Core/CoreOptions.cs b/Source/Core/CoreOptions.cs index 725a8b8e1..4643d5319 100644 --- a/Source/Core/CoreOptions.cs +++ b/Source/Core/CoreOptions.cs @@ -6,10 +6,10 @@ namespace Microsoft.Boogie /// Boogie command-line options (other tools can subclass this class in order to support a /// superset of Boogie's options). /// - public interface CommandLineOptions + public interface CoreOptions { - public static CommandLineOptions /*!*/ Clo + public static CoreOptions /*!*/ Clo { get; set; diff --git a/Source/Core/DeadVarElim.cs b/Source/Core/DeadVarElim.cs index 11a542ef6..0249e1eaf 100644 --- a/Source/Core/DeadVarElim.cs +++ b/Source/Core/DeadVarElim.cs @@ -76,7 +76,7 @@ public void DoModSetAnalysis(Program program) { Contract.Requires(program != null); - if (CommandLineOptions.Clo.Trace) + if (CoreOptions.Clo.Trace) { // Console.WriteLine(); // Console.WriteLine("Running modset analysis ..."); @@ -602,7 +602,7 @@ public static void ComputeLiveVariables(Implementation impl) Microsoft.Boogie.Helpers.ExtraTraceInformation("Starting live variable analysis"); Graph dag = Program.GraphFromBlocks(impl.Blocks, false); IEnumerable sortedNodes; - if (CommandLineOptions.Clo.ModifyTopologicalSorting) + if (CoreOptions.Clo.ModifyTopologicalSorting) { sortedNodes = dag.TopologicalSort(true); } diff --git a/Source/Core/Helpers.cs b/Source/Core/Helpers.cs index d3d110cd8..c0377a6a8 100644 --- a/Source/Core/Helpers.cs +++ b/Source/Core/Helpers.cs @@ -255,7 +255,7 @@ public static string PrettyPrintBplExpr(Expr e) public static void ExtraTraceInformation(string point) { Contract.Requires(point != null); - if (CommandLineOptions.Clo.TraceTimes) + if (CoreOptions.Clo.TraceTimes) { DateTime now = DateTime.UtcNow; TimeSpan timeSinceStartUp = now - StartUp; diff --git a/Source/Core/Inline.cs b/Source/Core/Inline.cs index 42b18071e..c0b256373 100644 --- a/Source/Core/Inline.cs +++ b/Source/Core/Inline.cs @@ -43,7 +43,7 @@ void ObjectInvariant() public override Expr VisitCodeExpr(CodeExpr node) { - Inliner codeExprInliner = new Inliner(program, inlineCallback, CommandLineOptions.Clo.InlineDepth); + Inliner codeExprInliner = new Inliner(program, inlineCallback, CoreOptions.Clo.InlineDepth); codeExprInliner.newLocalVars.AddRange(node.LocVars); codeExprInliner.inlinedProcLblMap = this.inlinedProcLblMap; List newCodeExprBlocks = codeExprInliner.DoInlineBlocks(node.Blocks, ref inlinedSomething); @@ -171,7 +171,7 @@ protected static void ProcessImplementation(Program program, Implementation impl // we need to resolve the new code inliner.ResolveImpl(impl); - if (CommandLineOptions.Clo.PrintInlined) + if (CoreOptions.Clo.PrintInlined) { inliner.EmitImpl(impl); } @@ -182,7 +182,7 @@ public static void ProcessImplementationForHoudini(Program program, Implementati Contract.Requires(impl != null); Contract.Requires(program != null); Contract.Requires(impl.Proc != null); - ProcessImplementation(program, impl, new Inliner(program, null, CommandLineOptions.Clo.InlineDepth)); + ProcessImplementation(program, impl, new Inliner(program, null, CoreOptions.Clo.InlineDepth)); } public static void ProcessImplementation(Program program, Implementation impl) @@ -388,12 +388,12 @@ private int InlineCallCmd(Block block, CallCmd callCmd, Implementation impl, Lis else if (inline == 0) { inlinedSomething = true; - if (CommandLineOptions.Clo.ProcedureInlining == CommandLineOptions.Inlining.Assert) + if (CoreOptions.Clo.ProcedureInlining == CoreOptions.Inlining.Assert) { // add assert newCmds.Add(new AssertCmd(callCmd.tok, Expr.False)); } - else if (CommandLineOptions.Clo.ProcedureInlining == CommandLineOptions.Inlining.Assume) + else if (CoreOptions.Clo.ProcedureInlining == CoreOptions.Inlining.Assume) { // add assume newCmds.Add(new AssumeCmd(callCmd.tok, Expr.False)); diff --git a/Source/Core/InterProceduralReachabilityGraph.cs b/Source/Core/InterProceduralReachabilityGraph.cs index aaacf1067..4b79c31c0 100644 --- a/Source/Core/InterProceduralReachabilityGraph.cs +++ b/Source/Core/InterProceduralReachabilityGraph.cs @@ -257,7 +257,7 @@ public bool MayReach(Block src, Block dst) { if (ReachabilityGraphSCCsDAG == null) { - if (CommandLineOptions.Clo.Trace) + if (CoreOptions.Clo.Trace) { Console.WriteLine("Interprocedural reachability: computing SCCs"); } @@ -292,7 +292,7 @@ public bool MayReach(Block src, Block dst) ReachabilityGraphSCCsDAG.AddEdge(BlockToSCC[n], dummy); } - if (CommandLineOptions.Clo.Trace) + if (CoreOptions.Clo.Trace) { Console.WriteLine("Interprocedural reachability: SCCs computed!"); } diff --git a/Source/Core/LambdaHelper.cs b/Source/Core/LambdaHelper.cs index b5b182aef..2f62ad3b0 100644 --- a/Source/Core/LambdaHelper.cs +++ b/Source/Core/LambdaHelper.cs @@ -19,7 +19,7 @@ public static Program Desugar(Program program, out List /*!*/ axiom program = v.VisitProgram(program); axioms = v.lambdaAxioms; functions = v.lambdaFunctions; - if (CommandLineOptions.Clo.TraceVerify) + if (CoreOptions.Clo.TraceVerify) { Console.WriteLine("Desugaring of lambda expressions produced {0} functions and {1} axioms:", functions.Count, axioms.Count); @@ -121,7 +121,7 @@ public override Expr VisitLambdaExpr(LambdaExpr lambda) return baseResult; // apparently, the base visitor already turned the lambda into something else } - return CommandLineOptions.Clo.FreeVarLambdaLifting ? LiftLambdaFreeVars(lambda) : LiftLambdaMaxHoles(lambda); + return CoreOptions.Clo.FreeVarLambdaLifting ? LiftLambdaFreeVars(lambda) : LiftLambdaMaxHoles(lambda); } /// @@ -174,7 +174,7 @@ private Expr LiftLambdaFreeVars(LambdaExpr lambda) Substituter.SubstitutionFromDictionary(oldSubst), lambda.Attributes); - if (0 < CommandLineOptions.Clo.VerifySnapshots && + if (0 < CoreOptions.Clo.VerifySnapshots && QKeyValue.FindStringAttribute(lambdaAttrs, "checksum") == null) { // Attach a dummy checksum to avoid issues in the dependency analysis. @@ -250,14 +250,14 @@ private Expr LiftLambdaFreeVars(LambdaExpr lambda) if (liftedLambdas.TryGetValue(lambda, out var fcall)) { - if (CommandLineOptions.Clo.TraceVerify) + if (CoreOptions.Clo.TraceVerify) { Console.WriteLine("Old lambda: {0}", lam_str); } } else { - if (CommandLineOptions.Clo.TraceVerify) + if (CoreOptions.Clo.TraceVerify) { Console.WriteLine("New lambda: {0}", lam_str); } diff --git a/Source/Core/MaxHolesLambdaLifter.cs b/Source/Core/MaxHolesLambdaLifter.cs index 736f0ed79..230d43017 100644 --- a/Source/Core/MaxHolesLambdaLifter.cs +++ b/Source/Core/MaxHolesLambdaLifter.cs @@ -346,7 +346,7 @@ public override Expr VisitLambdaExpr(LambdaExpr node) var lambdaAttrs = _lambda.Attributes; - if (0 < CommandLineOptions.Clo.VerifySnapshots && QKeyValue.FindStringAttribute(lambdaAttrs, "checksum") == null) + if (0 < CoreOptions.Clo.VerifySnapshots && QKeyValue.FindStringAttribute(lambdaAttrs, "checksum") == null) { // Attach a dummy checksum to avoid issues in the dependency analysis. var checksumAttr = new QKeyValue(_lambda.tok, "checksum", new List {"lambda expression"}, null); @@ -381,14 +381,14 @@ public override Expr VisitLambdaExpr(LambdaExpr node) if (_liftedLambdas.TryGetValue(liftedLambda, out var fcall)) { - if (CommandLineOptions.Clo.TraceVerify) + if (CoreOptions.Clo.TraceVerify) { Console.WriteLine("Old lambda: {0}", lam_str); } } else { - if (CommandLineOptions.Clo.TraceVerify) + if (CoreOptions.Clo.TraceVerify) { Console.WriteLine("New lambda: {0}", lam_str); } diff --git a/Source/Core/Monomorphization.cs b/Source/Core/Monomorphization.cs index 358404829..14c23f2d5 100644 --- a/Source/Core/Monomorphization.cs +++ b/Source/Core/Monomorphization.cs @@ -628,7 +628,7 @@ public override Expr VisitNAryExpr(NAryExpr node) private static bool IsInlined(Implementation impl) { - if (CommandLineOptions.Clo.ProcedureInlining == CommandLineOptions.Inlining.None) + if (CoreOptions.Clo.ProcedureInlining == CoreOptions.Inlining.None) { return false; } diff --git a/Source/Core/VariableDependenceAnalyser.cs b/Source/Core/VariableDependenceAnalyser.cs index 841a7f3cd..c91cb60ae 100644 --- a/Source/Core/VariableDependenceAnalyser.cs +++ b/Source/Core/VariableDependenceAnalyser.cs @@ -265,21 +265,21 @@ public void Analyse() * */ - if (CommandLineOptions.Clo.Trace) + if (CoreOptions.Clo.Trace) { Console.WriteLine("Variable dependence analysis: Initialising"); } Initialise(); - if (CommandLineOptions.Clo.Trace) + if (CoreOptions.Clo.Trace) { Console.WriteLine("Variable dependence analysis: Computing control dependence info"); } BlockToControllingBlocks = ComputeGlobalControlDependences(); - if (CommandLineOptions.Clo.Trace) + if (CoreOptions.Clo.Trace) { Console.WriteLine("Variable dependence analysis: Computing control dependence variables"); } @@ -287,7 +287,7 @@ public void Analyse() ControllingBlockToVariables = ComputeControllingVariables(BlockToControllingBlocks); foreach (var Impl in prog.NonInlinedImplementations()) { - if (CommandLineOptions.Clo.Trace) + if (CoreOptions.Clo.Trace) { Console.WriteLine("Variable dependence analysis: Analysing " + Impl.Name); } @@ -384,7 +384,7 @@ void AddDependences(VariableDescriptor v, IEnumerable vs, st { foreach (var n in vs) { - if (CommandLineOptions.Clo.DebugStagedHoudini) + if (CoreOptions.Clo.DebugStagedHoudini) { Console.WriteLine("Adding dependence " + v + " -> " + n + ", reason: " + reason + "(" + tok.line + ":" + tok.col + ")"); @@ -471,14 +471,14 @@ public bool VariableRelevantToAnalysis(Variable v, string proc) private void MakeIgnoreList() { IgnoredVariables = new HashSet(); - if (CommandLineOptions.Clo.VariableDependenceIgnore == null) + if (CoreOptions.Clo.VariableDependenceIgnore == null) { return; } try { - var file = System.IO.File.OpenText(CommandLineOptions.Clo.VariableDependenceIgnore); + var file = System.IO.File.OpenText(CoreOptions.Clo.VariableDependenceIgnore); while (!file.EndOfStream) { string line = file.ReadLine(); @@ -507,7 +507,7 @@ private void MakeIgnoreList() catch (System.IO.IOException e) { Console.Error.WriteLine("Error reading from ignored variables file " + - CommandLineOptions.Clo.VariableDependenceIgnore + ": " + e); + CoreOptions.Clo.VariableDependenceIgnore + ": " + e); } } @@ -654,7 +654,7 @@ public HashSet DependsOn(VariableDescriptor v) { if (DependsOnSCCsDAG == null) { - if (CommandLineOptions.Clo.Trace) + if (CoreOptions.Clo.Trace) { Console.WriteLine("Variable dependence: computing SCCs"); } @@ -690,7 +690,7 @@ public HashSet DependsOn(VariableDescriptor v) DependsOnSCCsDAG.AddEdge(VariableDescriptorToSCC[n], dummy); } - if (CommandLineOptions.Clo.Trace) + if (CoreOptions.Clo.Trace) { Console.WriteLine("Variable dependence: SCCs computed!"); } diff --git a/Source/Core/Xml.cs b/Source/Core/Xml.cs index 2b152f8c9..6268c3019 100644 --- a/Source/Core/Xml.cs +++ b/Source/Core/Xml.cs @@ -49,7 +49,7 @@ public string Open() wr = XmlWriter.Create(filename, settings); wr.WriteStartDocument(); wr.WriteStartElement("boogie"); - wr.WriteAttributeString("version", CommandLineOptions.Clo.VersionNumber); + wr.WriteAttributeString("version", CoreOptions.Clo.VersionNumber); wr.WriteAttributeString("commandLine", Environment.CommandLine); } cce.EndExpose(); diff --git a/Source/ExecutionEngine/CommandLineOptions.cs b/Source/ExecutionEngine/CommandLineOptions.cs index 15036a091..756d52aa3 100644 --- a/Source/ExecutionEngine/CommandLineOptions.cs +++ b/Source/ExecutionEngine/CommandLineOptions.cs @@ -627,31 +627,31 @@ public bool Parse([Captured] string[] /*!*/ args) /// Boogie command-line options (other tools can subclass this class in order to support a /// superset of Boogie's options). /// - public class CommandLineOptionsImpl : CommandLineOptionEngine, ExecutionEngineOptions + public class CommandLineOptions : CommandLineOptionEngine, ExecutionEngineOptions { - public static CommandLineOptionsImpl FromArguments(params string[] arguments) + public static CommandLineOptions FromArguments(params string[] arguments) { - var result = new CommandLineOptionsImpl(); + var result = new CommandLineOptions(); result.Parse(arguments); return result; } - public CommandLineOptionsImpl() + public CommandLineOptions() : base("Boogie", "Boogie program verifier") { } - protected CommandLineOptionsImpl(string toolName, string descriptiveName) + protected CommandLineOptions(string toolName, string descriptiveName) : base(toolName, descriptiveName) { Contract.Requires(toolName != null); Contract.Requires(descriptiveName != null); } - public static void Install(CommandLineOptions options) + public static void Install(CoreOptions options) { Contract.Requires(options != null); - CommandLineOptions.Clo = options; + CoreOptions.Clo = options; } // Flags and arguments @@ -771,7 +771,7 @@ public bool InstrumentWithAsserts */ public bool Prune { get; set; } - public CommandLineOptions.InstrumentationPlaces InstrumentInfer { get; set; } = CommandLineOptions.InstrumentationPlaces.LoopHeaders; + public CoreOptions.InstrumentationPlaces InstrumentInfer { get; set; } = CoreOptions.InstrumentationPlaces.LoopHeaders; public int? RandomSeed { get; set; } @@ -914,10 +914,10 @@ public string LogPrefix public bool PrettyPrint { get; set; } = true; - public CommandLineOptions.ProverWarnings PrintProverWarnings { get; set; } = CommandLineOptions.ProverWarnings.None; + public CoreOptions.ProverWarnings PrintProverWarnings { get; set; } = CoreOptions.ProverWarnings.None; - public CommandLineOptions.SubsumptionOption UseSubsumption { get; set; } = CommandLineOptions.SubsumptionOption.Always; + public CoreOptions.SubsumptionOption UseSubsumption { get; set; } = CoreOptions.SubsumptionOption.Always; public bool AlwaysAssumeFreeLoopInvariants { get; set; } @@ -1079,7 +1079,7 @@ public XmlSink XmlRefuted } } - public CommandLineOptions.Inlining ProcedureInlining { get; set; } = CommandLineOptions.Inlining.Assume; + public CoreOptions.Inlining ProcedureInlining { get; set; } = CoreOptions.Inlining.Assume; public bool PrintInlined { get => printInlined; @@ -1102,7 +1102,7 @@ public bool PrintInlined { public bool ExtractLoopsUnrollIrreducible { get; set; } = true; // unroll irreducible loops? (set programmatically) - public CommandLineOptions.TypeEncoding TypeEncodingMethod { get; set; } = CommandLineOptions.TypeEncoding.Predicates; + public CoreOptions.TypeEncoding TypeEncodingMethod { get; set; } = CoreOptions.TypeEncoding.Predicates; public bool Monomorphize { get; set; } = false; @@ -1126,7 +1126,7 @@ void ObjectInvariant5() Contract.Invariant(Ai != null); } - public CommandLineOptions.AiFlags /*!*/ Ai { get; private set; } = new(); + public CoreOptions.AiFlags /*!*/ Ai { get; private set; } = new(); private bool proverHelpRequested = false; private bool restartProverPerVc = false; @@ -1159,7 +1159,7 @@ void ObjectInvariant5() private bool normalizeNames; private bool normalizeDeclarationOrder = true; - public List Cho { get; set; } = new(); + public List Cho { get; set; } = new(); protected override bool ParseOption(string name, CommandLineOptionEngine.CommandLineParseState ps) { @@ -1336,13 +1336,13 @@ protected override bool ParseOption(string name, CommandLineOptionEngine.Command switch (pw) { case 0: - PrintProverWarnings = CommandLineOptions.ProverWarnings.None; + PrintProverWarnings = CoreOptions.ProverWarnings.None; break; case 1: - PrintProverWarnings = CommandLineOptions.ProverWarnings.Stdout; + PrintProverWarnings = CoreOptions.ProverWarnings.Stdout; break; case 2: - PrintProverWarnings = CommandLineOptions.ProverWarnings.Stderr; + PrintProverWarnings = CoreOptions.ProverWarnings.Stderr; break; default: { @@ -1456,13 +1456,13 @@ protected override bool ParseOption(string name, CommandLineOptionEngine.Command switch (s) { case 0: - UseSubsumption = CommandLineOptions.SubsumptionOption.Never; + UseSubsumption = CoreOptions.SubsumptionOption.Never; break; case 1: - UseSubsumption = CommandLineOptions.SubsumptionOption.NotForQuantifiers; + UseSubsumption = CoreOptions.SubsumptionOption.NotForQuantifiers; break; case 2: - UseSubsumption = CommandLineOptions.SubsumptionOption.Always; + UseSubsumption = CoreOptions.SubsumptionOption.Always; break; default: { @@ -1623,16 +1623,16 @@ protected override bool ParseOption(string name, CommandLineOptionEngine.Command switch (args[ps.i]) { case "none": - ProcedureInlining = CommandLineOptions.Inlining.None; + ProcedureInlining = CoreOptions.Inlining.None; break; case "assert": - ProcedureInlining = CommandLineOptions.Inlining.Assert; + ProcedureInlining = CoreOptions.Inlining.Assert; break; case "assume": - ProcedureInlining = CommandLineOptions.Inlining.Assume; + ProcedureInlining = CoreOptions.Inlining.Assume; break; case "spec": - ProcedureInlining = CommandLineOptions.Inlining.Spec; + ProcedureInlining = CoreOptions.Inlining.Spec; break; default: ps.Error("Invalid argument \"{0}\" to option {1}", args[ps.i], ps.s); @@ -1663,11 +1663,11 @@ protected override bool ParseOption(string name, CommandLineOptionEngine.Command { case "p": case "predicates": - TypeEncodingMethod = CommandLineOptions.TypeEncoding.Predicates; + TypeEncodingMethod = CoreOptions.TypeEncoding.Predicates; break; case "a": case "arguments": - TypeEncodingMethod = CommandLineOptions.TypeEncoding.Arguments; + TypeEncodingMethod = CoreOptions.TypeEncoding.Arguments; break; default: ps.Error("Invalid argument \"{0}\" to option {1}", args[ps.i], ps.s); @@ -1691,10 +1691,10 @@ protected override bool ParseOption(string name, CommandLineOptionEngine.Command switch (args[ps.i]) { case "e": - InstrumentInfer = CommandLineOptions.InstrumentationPlaces.Everywhere; + InstrumentInfer = CoreOptions.InstrumentationPlaces.Everywhere; break; case "h": - InstrumentInfer = CommandLineOptions.InstrumentationPlaces.LoopHeaders; + InstrumentInfer = CoreOptions.InstrumentationPlaces.LoopHeaders; break; default: ps.Error("Invalid argument \"{0}\" to option {1}", args[ps.i], ps.s); @@ -1938,7 +1938,7 @@ public override void ApplyDefaultOptions() if (StratifiedInlining > 0) { - TypeEncodingMethod = CommandLineOptions.TypeEncoding.Monomorphic; + TypeEncodingMethod = CoreOptions.TypeEncoding.Monomorphic; UseArrayTheory = true; UseAbstractInterpretation = false; if (ProverDllName == "SMTLib") diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index d5cba097b..7f1454d3c 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -214,7 +214,7 @@ public VerificationResult(string requestId, Implementation implementation, strin } } - public class ExecutionEngine + public class ExecutionEngine : IDisposable { public static OutputPrinter printer; @@ -243,7 +243,7 @@ public static int AutoRequestId(string id) return -1; } - public readonly static VerificationResultCache Cache = new VerificationResultCache(); + public static readonly VerificationResultCache Cache = new VerificationResultCache(); static readonly MemoryCache programCache = new MemoryCache("ProgramCache"); @@ -256,7 +256,14 @@ public static Program CachedProgram(string programId) return result; } - private static CheckerPool checkerPool; + public ExecutionEngine(ExecutionEngineOptions options) + { + this.Options = options; + checkerPool = new CheckerPool(options); + } + + public ExecutionEngineOptions Options { get; } + private readonly CheckerPool checkerPool; static DateTime FirstRequestStart; @@ -276,71 +283,76 @@ static readonly ConcurrentDictionary static TextWriter ModelWriter = null; - public static bool ProcessFiles(ExecutionEngineOptions options, IList fileNames, bool lookForSnapshots = true, string programId = null) + public bool ProcessFiles(IList fileNames, bool lookForSnapshots = true, string programId = null) { Contract.Requires(cce.NonNullElements(fileNames)); - if (options.VerifySeparately && 1 < fileNames.Count) + if (Options.VerifySeparately && 1 < fileNames.Count) { - return fileNames.All(f => ProcessFiles(options, new List {f}, lookForSnapshots, f)); + return fileNames.All(f => ProcessFiles( new List {f}, lookForSnapshots, f)); } - if (0 <= options.VerifySnapshots && lookForSnapshots) + if (0 <= Options.VerifySnapshots && lookForSnapshots) { var snapshotsByVersion = LookForSnapshots(fileNames); - return snapshotsByVersion.All(s => ProcessFiles(options, new List(s), false, programId)); + return snapshotsByVersion.All(s => + { + // BUG: Reusing checkers during snapshots doesn't work, even though it should. We create a new engine (and thus checker pool) to workaround this. + using var engine = new ExecutionEngine(Options); + return engine.ProcessFiles(new List(s), false, programId); + }); } - using XmlFileScope xf = new XmlFileScope(options.XmlSink, fileNames[^1]); - Program program = ParseBoogieProgram(options, fileNames, false); + using XmlFileScope xf = new XmlFileScope(Options.XmlSink, fileNames[^1]); + Program program = ParseBoogieProgram(fileNames, false); var bplFileName = fileNames[^1]; if (program == null) { return true; } - return ProcessProgram(options, program, bplFileName, programId); + return ProcessProgram(program, bplFileName, programId); } - public static bool ProcessProgram(ExecutionEngineOptions options, Program program, string bplFileName, string programId = null) + public bool ProcessProgram(Program program, string bplFileName, string programId = null) { if (programId == null) { programId = "main_program_id"; } - if (options.PrintFile != null) { - PrintBplFile(options, options.PrintFile, program, false, true, options.PrettyPrint); + if (Options.PrintFile != null) { + PrintBplFile(Options.PrintFile, program, false, true, Options.PrettyPrint); } - PipelineOutcome oc = ResolveAndTypecheck(options, program, bplFileName, out var civlTypeChecker); + PipelineOutcome oc = ResolveAndTypecheck(program, bplFileName, out var civlTypeChecker); if (oc != PipelineOutcome.ResolvedAndTypeChecked) { return true; } - if (options.PrintCFGPrefix != null) { + if (Options.PrintCFGPrefix != null) { foreach (var impl in program.Implementations) { - using StreamWriter sw = new StreamWriter(options.PrintCFGPrefix + "." + impl.Name + ".dot"); + using StreamWriter sw = new StreamWriter(Options.PrintCFGPrefix + "." + impl.Name + ".dot"); sw.Write(program.ProcessLoops(impl).ToDot()); } } - CivlVCGeneration.Transform(options, civlTypeChecker); - if (options.CivlDesugaredFile != null) { - int oldPrintUnstructured = options.PrintUnstructured; - options.PrintUnstructured = 1; - PrintBplFile(options, options.CivlDesugaredFile, program, false, false, - options.PrettyPrint); - options.PrintUnstructured = oldPrintUnstructured; + CivlVCGeneration.Transform(Options, civlTypeChecker); + if (Options.CivlDesugaredFile != null) { + int oldPrintUnstructured = Options.PrintUnstructured; + Options.PrintUnstructured = 1; + PrintBplFile(Options.CivlDesugaredFile, program, false, false, + Options.PrettyPrint); + Options.PrintUnstructured = oldPrintUnstructured; } EliminateDeadVariables(program); - CoalesceBlocks(options, program); + CoalesceBlocks(program); - Inline(options, program); + Inline(program); var stats = new PipelineStatistics(); - oc = InferAndVerify(options, program, stats, 1 < options.VerifySnapshots ? programId : null); + oc = InferAndVerify(program, stats, 1 < Options.VerifySnapshots ? programId : null); switch (oc) { case PipelineOutcome.Done: case PipelineOutcome.VerificationCompleted: @@ -385,11 +397,11 @@ public static IList> LookForSnapshots(IList fileNames) } - public static void CoalesceBlocks(ExecutionEngineOptions options, Program program) + public void CoalesceBlocks(Program program) { - if (options.CoalesceBlocks) + if (Options.CoalesceBlocks) { - if (options.Trace) + if (Options.Trace) { Console.WriteLine("Coalescing blocks..."); } @@ -399,23 +411,30 @@ public static void CoalesceBlocks(ExecutionEngineOptions options, Program progra } - public static void CollectModSets(ExecutionEngineOptions options, Program program) + public void CollectModSets(Program program) { - if (options.DoModSetAnalysis) + if (Options.DoModSetAnalysis) { new ModSetCollector().DoModSetAnalysis(program); } } - public static void EliminateDeadVariables(Program program) + public void EliminateDeadVariables(Program program) { Microsoft.Boogie.UnusedVarEliminator.Eliminate(program); } + public void PrintBplFile(string filename, Program program, bool allowPrintDesugaring, bool setTokens = true, + bool pretty = false) + { + PrintBplFile(Options, filename, program, allowPrintDesugaring, setTokens, pretty); + } + public static void PrintBplFile(ExecutionEngineOptions options, string filename, Program program, bool allowPrintDesugaring, bool setTokens = true, bool pretty = false) + { Contract.Requires(program != null); Contract.Requires(filename != null); @@ -447,7 +466,7 @@ public static void PrintBplFile(ExecutionEngineOptions options, string filename, /// Parse the given files into one Boogie program. If an I/O or parse error occurs, an error will be printed /// and null will be returned. On success, a non-null program is returned. /// - public static Program ParseBoogieProgram(ExecutionEngineOptions options, IList fileNames, bool suppressTraceOutput) + public Program ParseBoogieProgram(IList fileNames, bool suppressTraceOutput) { Contract.Requires(cce.NonNullElements(fileNames)); @@ -459,14 +478,14 @@ public static Program ParseBoogieProgram(ExecutionEngineOptions options, IList() {"FILE_" + fileId}; int errorCount = Parser.Parse(bplFileName, defines, out Program programSnippet, - options.UseBaseNameForFileName); + Options.UseBaseNameForFileName); if (programSnippet == null || errorCount != 0) { - Console.WriteLine("{0} parse errors detected in {1}", errorCount, GetFileNameForConsole(options, bplFileName)); + Console.WriteLine("{0} parse errors detected in {1}", errorCount, GetFileNameForConsole(Options, bplFileName)); okay = false; } else @@ -487,8 +506,8 @@ public static Program ParseBoogieProgram(ExecutionEngineOptions options, IList d.HasCivlAttribute())) { - options.UseLibrary = true; + Options.UseLibrary = true; } - if (options.UseLibrary) + if (Options.UseLibrary) { - options.UseArrayTheory = true; - options.Monomorphize = true; + Options.UseArrayTheory = true; + Options.Monomorphize = true; var library = Parser.ParseLibraryDefinitions(); program.AddTopLevelDeclarations(library.TopLevelDeclarations); } @@ -533,7 +552,7 @@ internal static string GetFileNameForConsole(ExecutionEngineOptions options, str /// - TypeCheckingError if a type checking error occurred /// - ResolvedAndTypeChecked if both resolution and type checking succeeded /// - public static PipelineOutcome ResolveAndTypecheck(ExecutionEngineOptions options, Program program, string bplFileName, + public PipelineOutcome ResolveAndTypecheck(Program program, string bplFileName, out CivlTypeChecker civlTypeChecker) { Contract.Requires(program != null); @@ -543,7 +562,7 @@ public static PipelineOutcome ResolveAndTypecheck(ExecutionEngineOptions options // ---------- Resolve ------------------------------------------------------------ - if (options.NoResolve) + if (Options.NoResolve) { return PipelineOutcome.Done; } @@ -551,13 +570,13 @@ public static PipelineOutcome ResolveAndTypecheck(ExecutionEngineOptions options int errorCount = program.Resolve(); if (errorCount != 0) { - Console.WriteLine("{0} name resolution errors detected in {1}", errorCount, GetFileNameForConsole(options, bplFileName)); + Console.WriteLine("{0} name resolution errors detected in {1}", errorCount, GetFileNameForConsole(Options, bplFileName)); return PipelineOutcome.ResolutionError; } // ---------- Type check ------------------------------------------------------------ - if (options.NoTypecheck) + if (Options.NoTypecheck) { return PipelineOutcome.Done; } @@ -570,20 +589,20 @@ public static PipelineOutcome ResolveAndTypecheck(ExecutionEngineOptions options errorCount = program.Typecheck(); if (errorCount != 0) { - Console.WriteLine("{0} type checking errors detected in {1}", errorCount, GetFileNameForConsole(options, bplFileName)); + Console.WriteLine("{0} type checking errors detected in {1}", errorCount, GetFileNameForConsole(Options, bplFileName)); return PipelineOutcome.TypeCheckingError; } if (MonomorphismChecker.IsMonomorphic(program)) { - options.TypeEncodingMethod = CommandLineOptions.TypeEncoding.Monomorphic; + Options.TypeEncodingMethod = CoreOptions.TypeEncoding.Monomorphic; } - else if (options.Monomorphize) + else if (Options.Monomorphize) { var monomorphizableStatus = Monomorphizer.Monomorphize(program); if (monomorphizableStatus == MonomorphizableStatus.Monomorphizable) { - options.TypeEncodingMethod = CommandLineOptions.TypeEncoding.Monomorphic; + Options.TypeEncodingMethod = CoreOptions.TypeEncoding.Monomorphic; } else if (monomorphizableStatus == MonomorphizableStatus.UnhandledPolymorphism) { @@ -596,7 +615,7 @@ public static PipelineOutcome ResolveAndTypecheck(ExecutionEngineOptions options return PipelineOutcome.FatalError; } } - else if (options.UseArrayTheory) + else if (Options.UseArrayTheory) { Console.WriteLine( "Option /useArrayTheory only supported for monomorphic programs, polymorphism is detected in input program, try using -monomorphize"); @@ -609,32 +628,32 @@ public static PipelineOutcome ResolveAndTypecheck(ExecutionEngineOptions options return PipelineOutcome.FatalError; } - CollectModSets(options, program); + CollectModSets(program); - civlTypeChecker = new CivlTypeChecker(options, program); + civlTypeChecker = new CivlTypeChecker(Options, program); civlTypeChecker.TypeCheck(); if (civlTypeChecker.checkingContext.ErrorCount != 0) { Console.WriteLine("{0} type checking errors detected in {1}", civlTypeChecker.checkingContext.ErrorCount, - GetFileNameForConsole(options, bplFileName)); + GetFileNameForConsole(Options, bplFileName)); return PipelineOutcome.TypeCheckingError; } - if (options.PrintFile != null && options.PrintDesugarings) + if (Options.PrintFile != null && Options.PrintDesugarings) { // if PrintDesugaring option is engaged, print the file here, after resolution and type checking - PrintBplFile(options, options.PrintFile, program, true, true, options.PrettyPrint); + PrintBplFile(Options.PrintFile, program, true, true, Options.PrettyPrint); } return PipelineOutcome.ResolvedAndTypeChecked; } - public static void Inline(ExecutionEngineOptions options, Program program) + public void Inline(Program program) { Contract.Requires(program != null); - if (options.Trace) + if (Options.Trace) { Console.WriteLine("Inlining..."); } @@ -642,7 +661,7 @@ public static void Inline(ExecutionEngineOptions options, Program program) // Inline var TopLevelDeclarations = cce.NonNull(program.TopLevelDeclarations); - if (options.ProcedureInlining != CommandLineOptions.Inlining.None) + if (Options.ProcedureInlining != CoreOptions.Inlining.None) { bool inline = false; foreach (var d in TopLevelDeclarations) @@ -663,7 +682,7 @@ public static void Inline(ExecutionEngineOptions options, Program program) foreach (var impl in TopLevelDeclarations.OfType()) { - if (options.UserWantsToCheckRoutine(impl.Name) && !impl.SkipVerification) + if (Options.UserWantsToCheckRoutine(impl.Name) && !impl.SkipVerification) { Inliner.ProcessImplementation(program, impl); } @@ -687,7 +706,7 @@ public static void Inline(ExecutionEngineOptions options, Program program) /// - VerificationCompleted if inference and verification completed, in which the out /// parameters contain meaningful values /// - public static PipelineOutcome InferAndVerify(ExecutionEngineOptions options, + public PipelineOutcome InferAndVerify( Program program, PipelineStatistics stats, string programId = null, @@ -698,18 +717,14 @@ public static PipelineOutcome InferAndVerify(ExecutionEngineOptions options, Contract.Ensures(0 <= Contract.ValueAtReturn(out stats.InconclusiveCount) && 0 <= Contract.ValueAtReturn(out stats.TimeoutCount)); - if (checkerPool == null) { - checkerPool = new CheckerPool(options); - } - if (requestId == null) { requestId = FreshRequestId(); } - if (options.PrintErrorModelFile != null) + if (Options.PrintErrorModelFile != null) { - ExecutionEngine.ModelWriter = new StreamWriter(options.PrintErrorModelFile, false); + ExecutionEngine.ModelWriter = new StreamWriter(Options.PrintErrorModelFile, false); } var start = DateTime.UtcNow; @@ -719,12 +734,12 @@ public static PipelineOutcome InferAndVerify(ExecutionEngineOptions options, // Doing lambda expansion before abstract interpretation means that the abstract interpreter // never needs to see any lambda expressions. (On the other hand, if it were useful for it // to see lambdas, then it would be better to more lambda expansion until after inference.) - if (options.ExpandLambdas) + if (Options.ExpandLambdas) { LambdaHelper.ExpandLambdas(program); - if (options.PrintFile != null && options.PrintLambdaLifting) + if (Options.PrintFile != null && Options.PrintLambdaLifting) { - PrintBplFile(options, options.PrintFile, program, false, true, options.PrettyPrint); + PrintBplFile(Options.PrintFile, program, false, true, Options.PrettyPrint); } } @@ -732,7 +747,7 @@ public static PipelineOutcome InferAndVerify(ExecutionEngineOptions options, #region Infer invariants using Abstract Interpretation - if (options.UseAbstractInterpretation) + if (Options.UseAbstractInterpretation) { AbstractInterpretation.NativeAbstractInterpretation.RunAbstractInterpretation(program); } @@ -741,34 +756,34 @@ public static PipelineOutcome InferAndVerify(ExecutionEngineOptions options, #region Do some post-abstract-interpretation preprocessing on the program (e.g., loop unrolling) - if (options.LoopUnrollCount != -1) + if (Options.LoopUnrollCount != -1) { - program.UnrollLoops(options.LoopUnrollCount, options.SoundLoopUnrolling); + program.UnrollLoops(Options.LoopUnrollCount, Options.SoundLoopUnrolling); } Dictionary> extractLoopMappingInfo = null; - if (options.ExtractLoops) + if (Options.ExtractLoops) { extractLoopMappingInfo = program.ExtractLoops(); } - if (options.PrintInstrumented) + if (Options.PrintInstrumented) { - program.Emit(new TokenTextWriter(Console.Out, options.PrettyPrint)); + program.Emit(new TokenTextWriter(Console.Out, Options.PrettyPrint)); } #endregion - if (!options.Verify) + if (!Options.Verify) { return PipelineOutcome.Done; } #region Run Houdini and verify - if (options.ContractInfer) + if (Options.ContractInfer) { - return RunHoudini(options, program, stats, er); + return RunHoudini(program, stats, er); } #endregion @@ -776,15 +791,15 @@ public static PipelineOutcome InferAndVerify(ExecutionEngineOptions options, #region Select and prioritize implementations that should be verified var impls = program.Implementations.Where( - impl => impl != null && options.UserWantsToCheckRoutine(cce.NonNull(impl.Name)) && + impl => impl != null && Options.UserWantsToCheckRoutine(cce.NonNull(impl.Name)) && !impl.SkipVerification); // operate on a stable copy, in case it gets updated while we're running Implementation[] stablePrioritizedImpls = null; - if (0 < options.VerifySnapshots) + if (0 < Options.VerifySnapshots) { - OtherDefinitionAxiomsCollector.Collect(program.Axioms); - DependencyCollector.Collect(program); + OtherDefinitionAxiomsCollector.Collect(Options, program.Axioms); + DependencyCollector.Collect(Options, program); stablePrioritizedImpls = impls.OrderByDescending( impl => impl.Priority != 1 ? impl.Priority : Cache.VerificationPriority(impl)).ToArray(); } @@ -795,9 +810,9 @@ public static PipelineOutcome InferAndVerify(ExecutionEngineOptions options, #endregion - if (1 < options.VerifySnapshots) + if (1 < Options.VerifySnapshots) { - CachedVerificationResultInjector.Inject(program, stablePrioritizedImpls, requestId, programId, + CachedVerificationResultInjector.Inject(Options, program, stablePrioritizedImpls, requestId, programId, out stats.CachingActionCounts); } @@ -813,7 +828,7 @@ public static PipelineOutcome InferAndVerify(ExecutionEngineOptions options, var tasks = new Task[stablePrioritizedImpls.Length]; // We use this semaphore to limit the number of tasks that are currently executing. - var semaphore = new SemaphoreSlim(options.VcsCores); + var semaphore = new SemaphoreSlim(Options.VcsCores); // Create a task per implementation. for (int i = 0; i < stablePrioritizedImpls.Length; i++) @@ -842,7 +857,7 @@ public static PipelineOutcome InferAndVerify(ExecutionEngineOptions options, cts.Token.ThrowIfCancellationRequested(); } - VerifyImplementation(options, program, stats, er, requestId, extractLoopMappingInfo, stablePrioritizedImpls, + VerifyImplementation(program, stats, er, requestId, extractLoopMappingInfo, stablePrioritizedImpls, taskIndex, outputCollector, checkerPool, programId); ImplIdToCancellationTokenSource.TryRemove(id, out old); } @@ -896,25 +911,25 @@ public static PipelineOutcome InferAndVerify(ExecutionEngineOptions options, } finally { - CleanupCheckers(requestId); + CleanupRequest(requestId); } - if (options.PrintNecessaryAssumes && program.NecessaryAssumes.Any()) + if (Options.PrintNecessaryAssumes && program.NecessaryAssumes.Any()) { Console.WriteLine("Necessary assume command(s): {0}", string.Join(", ", program.NecessaryAssumes)); } - cce.NonNull(options.TheProverFactory).Close(); + cce.NonNull(Options.TheProverFactory).Close(); outputCollector.WriteMoreOutput(); - if (1 < options.VerifySnapshots && programId != null) + if (1 < Options.VerifySnapshots && programId != null) { program.FreezeTopLevelDeclarations(); programCache.Set(programId, program, policy); } - if (0 <= options.VerifySnapshots && options.TraceCachingForBenchmarking) + if (0 <= Options.VerifySnapshots && Options.TraceCachingForBenchmarking) { var end = DateTime.UtcNow; if (TimePerRequest.Count == 0) @@ -967,30 +982,20 @@ public static void CancelRequest(string requestId) { cts.Cancel(); - CleanupCheckers(requestId); + CleanupRequest(requestId); } } - - private static void CleanupCheckers(string requestId) + private static void CleanupRequest(string requestId) { if (requestId != null) { RequestIdToCancellationTokenSource.TryRemove(requestId, out var old); } - - lock (RequestIdToCancellationTokenSource) - { - if (RequestIdToCancellationTokenSource.IsEmpty) - { - checkerPool?.Dispose(); - checkerPool = null; - } - } } - private static void VerifyImplementation(ExecutionEngineOptions options, Program program, PipelineStatistics stats, ErrorReporterDelegate er, + private void VerifyImplementation(Program program, PipelineStatistics stats, ErrorReporterDelegate er, string requestId, Dictionary> extractLoopMappingInfo, Implementation[] stablePrioritizedImpls, int index, OutputCollector outputCollector, CheckerPool checkerPool, string programId) @@ -1004,19 +1009,19 @@ private static void VerifyImplementation(ExecutionEngineOptions options, Program int priority = 0; var wasCached = false; - if (0 < options.VerifySnapshots) + if (0 < Options.VerifySnapshots) { var cachedResults = Cache.Lookup(impl, out priority); if (cachedResults != null && priority == Priority.SKIP) { - if (options.XmlSink != null) + if (Options.XmlSink != null) { - options.XmlSink.WriteStartMethod(impl.Name, cachedResults.Start); + Options.XmlSink.WriteStartMethod(impl.Name, cachedResults.Start); } printer.Inform(string.Format("Retrieving cached verification result for implementation {0}...", impl.Name), output); - if (options.VerifySnapshots < 3 || + if (Options.VerifySnapshots < 3 || cachedResults.Outcome == ConditionGeneration.Outcome.Correct) { verificationResult = cachedResults; @@ -1037,16 +1042,16 @@ private static void VerifyImplementation(ExecutionEngineOptions options, Program verificationResult.ProofObligationCountBefore = vcgen.CumulativeAssertionCount; verificationResult.Start = DateTime.UtcNow; - if (options.XmlSink != null) + if (Options.XmlSink != null) { - options.XmlSink.WriteStartMethod(impl.Name, verificationResult.Start); + Options.XmlSink.WriteStartMethod(impl.Name, verificationResult.Start); } try { var cancellationToken = RequestIdToCancellationTokenSource[requestId].Token; verificationResult.Outcome = vcgen.VerifyImplementation(impl, out verificationResult.Errors, requestId, cancellationToken); - if (options.ExtractLoops && verificationResult.Errors != null) { + if (Options.ExtractLoops && verificationResult.Errors != null) { var vcg = vcgen as VCGen; if (vcg != null) { for (int i = 0; i < verificationResult.Errors.Count; i++) { @@ -1110,7 +1115,7 @@ private static void VerifyImplementation(ExecutionEngineOptions options, Program #region Cache the verification result - if (0 < options.VerifySnapshots && !string.IsNullOrEmpty(impl.Checksum)) + if (0 < Options.VerifySnapshots && !string.IsNullOrEmpty(impl.Checksum)) { Cache.Insert(impl, verificationResult); } @@ -1120,15 +1125,15 @@ private static void VerifyImplementation(ExecutionEngineOptions options, Program #region Process the verification results and statistics - ProcessOutcome(options, verificationResult.Outcome, verificationResult.Errors, TimeIndication(options, verificationResult), stats, + ProcessOutcome(verificationResult.Outcome, verificationResult.Errors, TimeIndication(verificationResult), stats, output, impl.TimeLimit, er, verificationResult.ImplementationName, verificationResult.ImplementationToken, verificationResult.RequestId, verificationResult.MessageIfVerifies, wasCached); - ProcessErrors(options, verificationResult.Errors, verificationResult.Outcome, output, er, impl); + ProcessErrors(verificationResult.Errors, verificationResult.Outcome, output, er, impl); - if (options.XmlSink != null) + if (Options.XmlSink != null) { - options.XmlSink.WriteEndMethod(verificationResult.Outcome.ToString().ToLowerInvariant(), + Options.XmlSink.WriteEndMethod(verificationResult.Outcome.ToString().ToLowerInvariant(), verificationResult.End, verificationResult.End - verificationResult.Start, verificationResult.ResourceCount); } @@ -1137,7 +1142,7 @@ private static void VerifyImplementation(ExecutionEngineOptions options, Program outputCollector.WriteMoreOutput(); - if (verificationResult.Outcome == VCGen.Outcome.Errors || options.Trace) + if (verificationResult.Outcome == VCGen.Outcome.Errors || Options.Trace) { Console.Out.Flush(); } @@ -1189,21 +1194,21 @@ private static ConditionGeneration CreateVCGen(Program program, CheckerPool chec #region Houdini - private static PipelineOutcome RunHoudini(ExecutionEngineOptions options, Program program, PipelineStatistics stats, ErrorReporterDelegate er) + private PipelineOutcome RunHoudini(Program program, PipelineStatistics stats, ErrorReporterDelegate er) { Contract.Requires(stats != null); - if (options.StagedHoudini != null) + if (Options.StagedHoudini != null) { - return RunStagedHoudini(options, program, stats, er); + return RunStagedHoudini(program, stats, er); } Houdini.HoudiniSession.HoudiniStatistics houdiniStats = new Houdini.HoudiniSession.HoudiniStatistics(); - Houdini.Houdini houdini = new Houdini.Houdini(options, program, houdiniStats); + Houdini.Houdini houdini = new Houdini.Houdini(Options, program, houdiniStats); Houdini.HoudiniOutcome outcome = houdini.PerformHoudiniInference(); houdini.Close(); - if (options.PrintAssignment) + if (Options.PrintAssignment) { Console.WriteLine("Assignment computed by Houdini:"); foreach (var x in outcome.assignment) @@ -1212,7 +1217,7 @@ private static PipelineOutcome RunHoudini(ExecutionEngineOptions options, Progra } } - if (options.Trace) + if (Options.Trace) { int numTrueAssigns = 0; foreach (var x in outcome.assignment) @@ -1234,29 +1239,29 @@ private static PipelineOutcome RunHoudini(ExecutionEngineOptions options, Progra foreach (Houdini.VCGenOutcome x in outcome.implementationOutcomes.Values) { - ProcessOutcome(options, x.outcome, x.errors, "", stats, Console.Out, options.TimeLimit, er); - ProcessErrors(options, x.errors, x.outcome, Console.Out, er); + ProcessOutcome(x.outcome, x.errors, "", stats, Console.Out, Options.TimeLimit, er); + ProcessErrors(x.errors, x.outcome, Console.Out, er); } return PipelineOutcome.Done; } - public static Program ProgramFromFile(ExecutionEngineOptions options, string filename) + public Program ProgramFromFile(string filename) { - Program p = ParseBoogieProgram(options, new List {filename}, false); + Program p = ParseBoogieProgram( new List {filename}, false); Debug.Assert(p != null); - PipelineOutcome oc = ResolveAndTypecheck(options, p, filename, out var civlTypeChecker); + PipelineOutcome oc = ResolveAndTypecheck(p, filename, out var civlTypeChecker); Debug.Assert(oc == PipelineOutcome.ResolvedAndTypeChecked); return p; } - private static PipelineOutcome RunStagedHoudini(ExecutionEngineOptions options, Program program, PipelineStatistics stats, ErrorReporterDelegate er) + private PipelineOutcome RunStagedHoudini(Program program, PipelineStatistics stats, ErrorReporterDelegate er) { Houdini.HoudiniSession.HoudiniStatistics houdiniStats = new Houdini.HoudiniSession.HoudiniStatistics(); - Houdini.StagedHoudini stagedHoudini = new Houdini.StagedHoudini(options, program, houdiniStats, f => ProgramFromFile(options, f)); + var stagedHoudini = new Houdini.StagedHoudini(Options, program, houdiniStats, ProgramFromFile); Houdini.HoudiniOutcome outcome = stagedHoudini.PerformStagedHoudiniInference(); - if (options.PrintAssignment) + if (Options.PrintAssignment) { Console.WriteLine("Assignment computed by Houdini:"); foreach (var x in outcome.assignment) @@ -1265,7 +1270,7 @@ private static PipelineOutcome RunStagedHoudini(ExecutionEngineOptions options, } } - if (options.Trace) + if (Options.Trace) { int numTrueAssigns = 0; foreach (var x in outcome.assignment) @@ -1287,8 +1292,8 @@ private static PipelineOutcome RunStagedHoudini(ExecutionEngineOptions options, foreach (Houdini.VCGenOutcome x in outcome.implementationOutcomes.Values) { - ProcessOutcome(options, x.outcome, x.errors, "", stats, Console.Out, options.TimeLimit, er); - ProcessErrors(options, x.errors, x.outcome, Console.Out, er); + ProcessOutcome(x.outcome, x.errors, "", stats, Console.Out, Options.TimeLimit, er); + ProcessErrors(x.errors, x.outcome, Console.Out, er); } return PipelineOutcome.Done; @@ -1297,10 +1302,10 @@ private static PipelineOutcome RunStagedHoudini(ExecutionEngineOptions options, #endregion - private static string TimeIndication(ExecutionEngineOptions options, VerificationResult verificationResult) + private string TimeIndication(VerificationResult verificationResult) { var result = ""; - if (options.Trace) + if (Options.Trace) { result = string.Format(" [{0:F3} s, solver resource count: {1}, {2} proof obligation{3}] ", (verificationResult.End - verificationResult.Start).TotalSeconds, @@ -1308,7 +1313,7 @@ private static string TimeIndication(ExecutionEngineOptions options, Verificatio verificationResult.ProofObligationCount, verificationResult.ProofObligationCount == 1 ? "" : "s"); } - else if (options.TraceProofObligations) + else if (Options.TraceProofObligations) { result = string.Format(" [{0} proof obligation{1}] ", verificationResult.ProofObligationCount, verificationResult.ProofObligationCount == 1 ? "" : "s"); @@ -1318,7 +1323,7 @@ private static string TimeIndication(ExecutionEngineOptions options, Verificatio } - private static void ProcessOutcome(ExecutionEngineOptions options, VC.VCGen.Outcome outcome, List errors, string timeIndication, + private void ProcessOutcome(VC.VCGen.Outcome outcome, List errors, string timeIndication, PipelineStatistics stats, TextWriter tw, uint timeLimit, ErrorReporterDelegate er = null, string implName = null, IToken implTok = null, string requestId = null, string msgIfVerifies = null, bool wasCached = false) { @@ -1328,11 +1333,11 @@ private static void ProcessOutcome(ExecutionEngineOptions options, VC.VCGen.Outc printer.Inform(timeIndication + OutcomeIndication(outcome, errors), tw); - ReportOutcome(options, outcome, er, implName, implTok, requestId, msgIfVerifies, tw, timeLimit, errors); + ReportOutcome(outcome, er, implName, implTok, requestId, msgIfVerifies, tw, timeLimit, errors); } - private static void ReportOutcome(ExecutionEngineOptions options, VC.VCGen.Outcome outcome, ErrorReporterDelegate er, string implName, + private void ReportOutcome(VC.VCGen.Outcome outcome, ErrorReporterDelegate er, string implName, IToken implTok, string requestId, string msgIfVerifies, TextWriter tw, uint timeLimit, List errors) { ErrorInformation errorInfo = null; @@ -1347,7 +1352,7 @@ private static void ReportOutcome(ExecutionEngineOptions options, VC.VCGen.Outco break; case VCGen.Outcome.ReachedBound: tw.WriteLine(string.Format("Stratified Inlining: Reached recursion bound of {0}", - options.RecursionBound)); + Options.RecursionBound)); break; case VCGen.Outcome.Errors: case VCGen.Outcome.TimedOut: @@ -1402,7 +1407,7 @@ private static void ReportOutcome(ExecutionEngineOptions options, VC.VCGen.Outco } else { - if (assertError.FailingAssert.ErrorMessage == null || options.ForceBplErrors) + if (assertError.FailingAssert.ErrorMessage == null || Options.ForceBplErrors) { msg = assertError.FailingAssert.ErrorData as string; } @@ -1594,7 +1599,7 @@ private static void UpdateStatistics(PipelineStatistics stats, VC.VCGen.Outcome } - private static void ProcessErrors(ExecutionEngineOptions options, List errors, VC.VCGen.Outcome outcome, TextWriter tw, + private void ProcessErrors(List errors, VC.VCGen.Outcome outcome, TextWriter tw, ErrorReporterDelegate er, Implementation impl = null) { var implName = impl != null ? impl.Name : null; @@ -1609,30 +1614,30 @@ private static void ProcessErrors(ExecutionEngineOptions options, List 0) + if (Options.ErrorTrace > 0) { errorInfo.Out.WriteLine("Execution trace:"); error.Print(4, errorInfo.Out, b => { errorInfo.AddAuxInfo(b.tok, b.Label, "Execution trace"); }); - if (options.EnhancedErrorMessages == 1 && error.AugmentedTrace != null && error.AugmentedTrace.Count > 0) + if (Options.EnhancedErrorMessages == 1 && error.AugmentedTrace != null && error.AugmentedTrace.Count > 0) { errorInfo.Out.WriteLine("Augmented execution trace:"); error.AugmentedTrace.Iter(elem => errorInfo.Out.Write(elem)); } - if (options.PrintErrorModel >= 1 && error.Model != null) + if (Options.PrintErrorModel >= 1 && error.Model != null) { error.Model.Write(ExecutionEngine.ModelWriter == null ? errorInfo.Out : ExecutionEngine.ModelWriter); } } - if (options.ModelViewFile != null) { + if (Options.ModelViewFile != null) { error.PrintModel(errorInfo.Model, error); } @@ -1650,7 +1655,7 @@ private static void ProcessErrors(ExecutionEngineOptions options, List", Console.Out, false, false); var loc = currentImplementation.tok != null && currentImplementation.tok != Token.NoToken ? string.Format("{0}({1},{2})", currentImplementation.tok.filename, currentImplementation.tok.line, @@ -169,10 +171,10 @@ public Implementation Inject(Implementation implementation, Program programInCac return result; } - public static void Inject(Program program, IEnumerable implementations, string requestId, + public static void Inject(ExecutionEngineOptions options, Program program, IEnumerable implementations, string requestId, string programId, out long[] cachingActionCounts) { - var eai = new CachedVerificationResultInjector(program); + var eai = new CachedVerificationResultInjector(options, program); cachingActionCounts = new long[Enum.GetNames(typeof(VC.ConditionGeneration.CachingAction)).Length]; var run = new CachedVerificationResultInjectorRun @@ -202,11 +204,11 @@ public static void Inject(Program program, IEnumerable implement run.SkippedImplementationCount++; } - if (priority == Priority.LOW || priority == Priority.MEDIUM || CommandLineOptions.Clo.VerifySnapshots >= 3) + if (priority == Priority.LOW || priority == Priority.MEDIUM || options.VerifySnapshots >= 3) { if (TimeThreshold < vr.End.Subtract(vr.Start).TotalMilliseconds) { - SetErrorAndAssertionChecksumsInCachedSnapshot(impl, vr); + eai.SetErrorAndAssertionChecksumsInCachedSnapshot(impl, vr); if (vr.ProgramId != null) { var p = ExecutionEngine.CachedProgram(vr.ProgramId); @@ -225,11 +227,11 @@ public static void Inject(Program program, IEnumerable implement Statistics.AddRun(requestId, run); } - private static void SetErrorAndAssertionChecksumsInCachedSnapshot(Implementation implementation, + private void SetErrorAndAssertionChecksumsInCachedSnapshot(Implementation implementation, VerificationResult result) { if (result.Outcome == ConditionGeneration.Outcome.Errors && result.Errors != null && - result.Errors.Count < CommandLineOptions.Clo.ErrorLimit) + result.Errors.Count < options.ErrorLimit) { implementation.SetErrorChecksumToCachedError(result.Errors.Select(cex => new Tuple(cex.Checksum, cex.SugaredCmdChecksum, cex))); @@ -328,7 +330,7 @@ public override Cmd VisitCallCmd(CallCmd node) } node.ExtendDesugaring(before, beforePreconditionCheck, after); - if (CommandLineOptions.Clo.TraceCachingForTesting || CommandLineOptions.Clo.TraceCachingForBenchmarking) { + if (options.TraceCachingForTesting || options.TraceCachingForBenchmarking) { using var tokTxtWr = new TokenTextWriter("", Console.Out, false, false); var loc = node.tok != null && node.tok != Token.NoToken ? string.Format("{0}({1},{2})", node.tok.filename, node.tok.line, node.tok.col) @@ -450,14 +452,20 @@ public static Expr Extract(Expr expr, Program program, List axioms) sealed class OtherDefinitionAxiomsCollector : ReadOnlyVisitor { + private ExecutionEngineOptions options; Axiom currentAxiom; Trigger currentTrigger; - public static void Collect(IEnumerable axioms) + public OtherDefinitionAxiomsCollector(ExecutionEngineOptions options) + { + this.options = options; + } + + public static void Collect(ExecutionEngineOptions options, IEnumerable axioms) { var start = DateTime.UtcNow; - var v = new OtherDefinitionAxiomsCollector(); + var v = new OtherDefinitionAxiomsCollector(options); foreach (var a in axioms) { v.currentAxiom = a; @@ -466,7 +474,7 @@ public static void Collect(IEnumerable axioms) } var end = DateTime.UtcNow; - if (CommandLineOptions.Clo.TraceCachingForDebugging) + if (options.TraceCachingForDebugging) { Console.Out.WriteLine("Collected other definition axioms within {0:F0} ms.", end.Subtract(start).TotalMilliseconds); @@ -512,7 +520,7 @@ sealed class DependencyCollector : ReadOnlyVisitor private DeclWithFormals currentDeclaration; private Axiom currentAxiom; - public static void Collect(Program program) + public static void Collect(ExecutionEngineOptions options, Program program) { var start = DateTime.UtcNow; @@ -520,7 +528,7 @@ public static void Collect(Program program) dc.VisitProgram(program); var end = DateTime.UtcNow; - if (CommandLineOptions.Clo.TraceCachingForDebugging) + if (options.TraceCachingForDebugging) { Console.Out.WriteLine("Collected dependencies within {0:F0} ms.", end.Subtract(start).TotalMilliseconds); } @@ -674,7 +682,6 @@ public sealed class VerificationResultCache private readonly CacheItemPolicy Policy = new CacheItemPolicy {SlidingExpiration = new TimeSpan(0, 10, 0), Priority = CacheItemPriority.Default}; - public void Insert(Implementation impl, VerificationResult result) { Contract.Requires(impl != null); @@ -701,7 +708,7 @@ public VerificationResult Lookup(Implementation impl, out int priority) { priority = Priority.LOW; } - else if (result.Outcome == ConditionGeneration.Outcome.TimedOut && CommandLineOptions.Clo.RunDiagnosticsOnTimeout) + else if (result.Outcome == ConditionGeneration.Outcome.TimedOut && CoreOptions.Clo.RunDiagnosticsOnTimeout) { priority = Priority.MEDIUM; } diff --git a/Source/Houdini/AnnotationDependenceAnalyser.cs b/Source/Houdini/AnnotationDependenceAnalyser.cs index 1f903b569..4d9419eb8 100644 --- a/Source/Houdini/AnnotationDependenceAnalyser.cs +++ b/Source/Houdini/AnnotationDependenceAnalyser.cs @@ -35,7 +35,7 @@ public AnnotationDependenceAnalyser(HoudiniOptions options, Program prog) public void Analyse() { - if (CommandLineOptions.Clo.Trace) + if (options.Trace) { Console.WriteLine("Annotation dependence analysis: Getting annotations"); } @@ -68,7 +68,7 @@ private IEnumerable AllAnnotationIdentifiers() private void ConstructStagesDAG() { - if (CommandLineOptions.Clo.Trace) + if (options.Trace) { Console.WriteLine("Annotation dependence analysis: Computing SCCs"); } @@ -79,7 +79,7 @@ private void ConstructStagesDAG() AnnotationDependences.Nodes, next, prev); SCCs.Compute(); - if (CommandLineOptions.Clo.Trace) + if (options.Trace) { Console.WriteLine("Annotation dependence analysis: Building stages DAG"); } @@ -112,7 +112,7 @@ private void ConstructStagesDAG() private void ConstructAnnotationDependenceGraph() { - if (CommandLineOptions.Clo.Trace) + if (options.Trace) { Console.WriteLine("Annotation dependence analysis: Building dependence graph"); } @@ -204,7 +204,7 @@ private void MergeIgnoredAnnotations() private void DetermineAnnotationVariableDependences() { - if (CommandLineOptions.Clo.Trace) + if (options.Trace) { Console.WriteLine("Annotation dependence analysis: Working out what annotations depend on"); } @@ -345,7 +345,7 @@ private bool IsStageDependence(SCC Src, SCC Dst) public void dump() { - if (CommandLineOptions.Clo.DebugStagedHoudini) + if (options.DebugStagedHoudini) { varDepAnalyser.dump(); diff --git a/Source/Houdini/Checker.cs b/Source/Houdini/Checker.cs index 7db65d656..c73d82c09 100644 --- a/Source/Houdini/Checker.cs +++ b/Source/Houdini/Checker.cs @@ -158,7 +158,7 @@ public HoudiniSession(Houdini houdini, VCGen vcgen, ProverInterface proverInterf this.descriptiveName = impl.Name; this.houdini = houdini; this.stats = stats; - collector = new ConditionGeneration.CounterexampleCollector(); + collector = new ConditionGeneration.CounterexampleCollector(houdini.Options); collector.OnProgress?.Invoke("HdnVCGen", 0, 0, 0.0); vcgen.ConvertCFG2DAG(impl, taskID: taskID); @@ -190,7 +190,7 @@ public HoudiniSession(Houdini houdini, VCGen vcgen, ProverInterface proverInterf new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "", Type.Bool), false)); proverInterface.DefineMacro(macro, conjecture); conjecture = exprGen.Function(macro); - handler = new VCGen.ErrorReporter(gotoCmdOrigins, absyIds, impl.Blocks, vcgen.debugInfos, collector, + handler = new VCGen.ErrorReporter(this.houdini.Options, gotoCmdOrigins, absyIds, impl.Blocks, vcgen.debugInfos, collector, mvInfo, proverInterface.Context, program); } @@ -237,7 +237,7 @@ private VCExpr BuildAxiom(ProverInterface proverInterface, Dictionary houdini.Options; + public ProverInterface.Outcome Verify(ProverInterface proverInterface, Dictionary assignment, out List errors, int errorLimit) { collector.examples.Clear(); - if (CommandLineOptions.Clo.Trace) + if (Options.Trace) { Console.WriteLine("Verifying " + descriptiveName); } @@ -264,7 +266,7 @@ public ProverInterface.Outcome Verify(ProverInterface proverInterface, Dictionar double queryTime = (DateTime.UtcNow - now).TotalSeconds; stats.proverTime += queryTime; stats.numProverQueries++; - if (CommandLineOptions.Clo.Trace) + if (Options.Trace) { Console.WriteLine("Outcome = " + proverOutcome); Console.WriteLine("Time taken = " + queryTime); @@ -376,15 +378,15 @@ public void Explain(ProverInterface proverInterface, var controlExprFalse = exprGen.And(controlExpr, exprGen.And(exprGen.Not(exprTranslator.LookupVariable(pc)), exprGen.Not(exprTranslator.LookupVariable(nc)))); - if (CommandLineOptions.Clo.Trace) + if (Options.Trace) { Console.WriteLine("Verifying (MaxSat) " + descriptiveName); } DateTime now = DateTime.UtcNow; - var el = CommandLineOptions.Clo.ErrorLimit; - CommandLineOptions.Clo.ErrorLimit = 1; + var el = Options.ErrorLimit; + Options.ErrorLimit = 1; var outcome = ProverInterface.Outcome.Undetermined; @@ -403,7 +405,7 @@ public void Explain(ProverInterface proverInterface, var reason = new HashSet(); unsatisfiedSoftAssumptions.Iter(i => reason.Add(softAssumptions[i].ToString())); - if (CommandLineOptions.Clo.Trace) + if (Options.Trace) { Console.Write("Reason for removal of {0}: ", refutedConstant.Name); reason.Iter(r => Console.Write("{0} ", r)); @@ -439,7 +441,7 @@ public void Explain(ProverInterface proverInterface, var reason1 = new HashSet(); //these are the reasons for inconsistency unsatisfiedSoftAssumptions2.Iter(i => reason1.Add(softAssumptions2[i].ToString())); - if (CommandLineOptions.Clo.Trace) + if (Options.Trace) { Console.Write("Revised reason for removal of {0}: ", refutedConstant.Name); reason.Iter(r => Console.Write("{0} ", r)); @@ -467,12 +469,12 @@ public void Explain(ProverInterface proverInterface, "TimeOut", descriptiveName); } - CommandLineOptions.Clo.ErrorLimit = el; + Options.ErrorLimit = el; double queryTime = (DateTime.UtcNow - now).TotalSeconds; stats.proverTime += queryTime; stats.numProverQueries++; - if (CommandLineOptions.Clo.Trace) + if (Options.Trace) { Console.WriteLine("Time taken = " + queryTime); } diff --git a/Source/Houdini/Houdini.cs b/Source/Houdini/Houdini.cs index de7506310..c0b4406c6 100644 --- a/Source/Houdini/Houdini.cs +++ b/Source/Houdini/Houdini.cs @@ -426,27 +426,27 @@ public Houdini(HoudiniOptions options, Program program, HoudiniSession.HoudiniSt protected void Initialize(Program program, HoudiniSession.HoudiniStatistics stats) { - if (CommandLineOptions.Clo.Trace) + if (Options.Trace) { Console.WriteLine("Collecting existential constants..."); } this.houdiniConstants = CollectExistentialConstants(); - if (CommandLineOptions.Clo.Trace) + if (Options.Trace) { Console.WriteLine("Building call graph..."); } this.callGraph = Program.BuildCallGraph(program); - if (CommandLineOptions.Clo.Trace) + if (Options.Trace) { Console.WriteLine("Number of implementations = {0}", callGraph.Nodes.Count); } if (Options.HoudiniUseCrossDependencies) { - if (CommandLineOptions.Clo.Trace) + if (Options.Trace) { Console.WriteLine("Computing procedure cross dependencies ..."); } @@ -458,24 +458,24 @@ protected void Initialize(Program program, HoudiniSession.HoudiniStatistics stat Inline(); /* { - int oldPrintUnstructured = CommandLineOptions.Clo.PrintUnstructured; - CommandLineOptions.Clo.PrintUnstructured = 1; + int oldPrintUnstructured = Options.PrintUnstructured; + Options.PrintUnstructured = 1; using (TokenTextWriter stream = new TokenTextWriter("houdini_inline.bpl")) { program.Emit(stream); } - CommandLineOptions.Clo.PrintUnstructured = oldPrintUnstructured; + Options.PrintUnstructured = oldPrintUnstructured; } */ var checkerPool = new CheckerPool(Options); this.vcgen = new VCGen(program, checkerPool); - this.proverInterface = ProverInterface.CreateProver(Options, program, CommandLineOptions.Clo.ProverLogFilePath, - CommandLineOptions.Clo.ProverLogFileAppend, CommandLineOptions.Clo.TimeLimit, taskID: GetTaskID()); + this.proverInterface = ProverInterface.CreateProver(Options, program, Options.ProverLogFilePath, + Options.ProverLogFileAppend, Options.TimeLimit, taskID: GetTaskID()); vcgenFailures = new HashSet(); Dictionary houdiniSessions = new Dictionary(); - if (CommandLineOptions.Clo.Trace) + if (Options.Trace) { Console.WriteLine("Beginning VC generation for Houdini..."); } @@ -484,7 +484,7 @@ protected void Initialize(Program program, HoudiniSession.HoudiniStatistics stat { try { - if (CommandLineOptions.Clo.Trace) + if (Options.Trace) { Console.WriteLine("Generating VC for {0}", impl.Name); } @@ -495,7 +495,7 @@ protected void Initialize(Program program, HoudiniSession.HoudiniStatistics stat } catch (VCGenException) { - if (CommandLineOptions.Clo.Trace) + if (Options.Trace) { Console.WriteLine("VC generation failed"); } @@ -522,7 +522,7 @@ protected void Initialize(Program program, HoudiniSession.HoudiniStatistics stat protected void Inline() { - if (CommandLineOptions.Clo.InlineDepth <= 0) + if (Options.InlineDepth <= 0) { return; } @@ -541,10 +541,10 @@ protected void Inline() foreach (Implementation impl in callGraph.Nodes) { - CommandLineOptions.Inlining savedOption = CommandLineOptions.Clo.ProcedureInlining; - CommandLineOptions.Clo.ProcedureInlining = CommandLineOptions.Inlining.Spec; + CoreOptions.Inlining savedOption = Options.ProcedureInlining; + Options.ProcedureInlining = CoreOptions.Inlining.Spec; Inliner.ProcessImplementationForHoudini(program, impl); - CommandLineOptions.Clo.ProcedureInlining = savedOption; + Options.ProcedureInlining = savedOption; } foreach (Implementation impl in callGraph.Nodes) @@ -565,7 +565,7 @@ protected void Inline() callGraph.AddEdge(edge.Item1, edge.Item2); } - int count = CommandLineOptions.Clo.InlineDepth; + int count = Options.InlineDepth; while (count > 0) { foreach (Implementation impl in oldCallGraph.Nodes) @@ -908,7 +908,7 @@ protected void FlushWorkList(int stage, IEnumerable completedStages) protected void UpdateAssignment(RefutedAnnotation refAnnot) { - if (CommandLineOptions.Clo.Trace) + if (Options.Trace) { Console.WriteLine("Removing " + refAnnot.Constant); using var cexWriter = new System.IO.StreamWriter(cexTraceFile, true); @@ -964,7 +964,7 @@ protected bool UpdateAssignmentWorkList(ProverInterface.Outcome outcome, #region Extra debugging output - if (CommandLineOptions.Clo.Trace) { + if (Options.Trace) { using var cexWriter = new System.IO.StreamWriter(cexTraceFile, true); cexWriter.WriteLine("Counter example for " + refutedAnnotation.Constant); cexWriter.Write(error.ToString()); @@ -988,7 +988,7 @@ protected bool UpdateAssignmentWorkList(ProverInterface.Outcome outcome, break; default: - if (CommandLineOptions.Clo.Trace) + if (Options.Trace) { Console.WriteLine("Timeout/Spaceout while verifying " + currentHoudiniState.Implementation.Name); } @@ -996,7 +996,7 @@ protected bool UpdateAssignmentWorkList(ProverInterface.Outcome outcome, houdiniSessions.TryGetValue(currentHoudiniState.Implementation, out var houdiniSession); foreach (Variable v in houdiniSession.houdiniAssertConstants) { - if (CommandLineOptions.Clo.Trace) + if (Options.Trace) { Console.WriteLine("Removing " + v); } @@ -1509,11 +1509,11 @@ private int GetErrorLimit() { var taskID = GetTaskID(); int errorLimit; - if (CommandLineOptions.Clo.ConcurrentHoudini) { + if (Options.ConcurrentHoudini) { Contract.Assert(taskID >= 0); - errorLimit = CommandLineOptions.Clo.Cho[taskID].ErrorLimit; + errorLimit = Options.Cho[taskID].ErrorLimit; } else { - errorLimit = CommandLineOptions.Clo.ErrorLimit; + errorLimit = Options.ErrorLimit; } return errorLimit; diff --git a/Source/Houdini/StagedHoudini.cs b/Source/Houdini/StagedHoudini.cs index b10911edf..a85b633b4 100644 --- a/Source/Houdini/StagedHoudini.cs +++ b/Source/Houdini/StagedHoudini.cs @@ -39,11 +39,11 @@ public StagedHoudini(HoudiniOptions options, Program program, HoudiniSession.Hou var annotationDependenceAnalyser = new AnnotationDependenceAnalyser(options, program); annotationDependenceAnalyser.Analyse(); this.plan = annotationDependenceAnalyser.ApplyStages(); - if (CommandLineOptions.Clo.Trace) + if (options.Trace) { annotationDependenceAnalyser.dump(); - if (CommandLineOptions.Clo.DebugStagedHoudini) + if (options.DebugStagedHoudini) { Console.WriteLine("Plan\n====\n"); if (plan == null) @@ -297,10 +297,10 @@ private List AcquireHoudiniInstance() private void EmitProgram(string filename) { using TokenTextWriter writer = new TokenTextWriter(filename, true); - int oldPrintUnstructured = CommandLineOptions.Clo.PrintUnstructured; - CommandLineOptions.Clo.PrintUnstructured = 2; + int oldPrintUnstructured = options.PrintUnstructured; + options.PrintUnstructured = 2; program.Emit(writer); - CommandLineOptions.Clo.PrintUnstructured = oldPrintUnstructured; + options.PrintUnstructured = oldPrintUnstructured; } diff --git a/Source/Provers/SMTLib/ProverContext.cs b/Source/Provers/SMTLib/ProverContext.cs index 4b99d4827..f63908d7f 100644 --- a/Source/Provers/SMTLib/ProverContext.cs +++ b/Source/Provers/SMTLib/ProverContext.cs @@ -265,7 +265,7 @@ public VCExpr Axioms } axioms = gen.AndSimp(gen.Distinct(distinctVars), axioms); - if (CommandLineOptions.Clo.TypeEncodingMethod != CommandLineOptions.TypeEncoding.Monomorphic) + if (CoreOptions.Clo.TypeEncodingMethod != CoreOptions.TypeEncoding.Monomorphic) { axioms = gen.AndSimp(orderingAxiomBuilder.Axioms, axioms); } diff --git a/Source/Provers/SMTLib/ProverInterface.cs b/Source/Provers/SMTLib/ProverInterface.cs index 4c3cda923..f7a634d35 100644 --- a/Source/Provers/SMTLib/ProverInterface.cs +++ b/Source/Provers/SMTLib/ProverInterface.cs @@ -32,11 +32,11 @@ public static ProverInterface CreateProver(SMTLibOptions libOptions, Program pro if (taskID >= 0) { - options.Parse(CommandLineOptions.Clo.Cho[taskID].ProverOptions); + options.Parse(libOptions.Cho[taskID].ProverOptions); } else { - options.Parse(CommandLineOptions.Clo.ProverOptions); + options.Parse(libOptions.ProverOptions); } ProverContext ctx = libOptions.TheProverFactory.NewProverContext(options); @@ -104,6 +104,13 @@ public enum Outcome public class ErrorHandler { + private SMTLibOptions options; + + public ErrorHandler(SMTLibOptions options) + { + this.options = options; + } + public virtual void AddNecessaryAssume(string id) { throw new System.NotImplementedException(); @@ -129,14 +136,14 @@ public virtual void OnResourceExceeded(string message, public virtual void OnProverWarning(string message) { Contract.Requires(message != null); - switch (CommandLineOptions.Clo.PrintProverWarnings) + switch (options.PrintProverWarnings) { - case CommandLineOptions.ProverWarnings.None: + case CoreOptions.ProverWarnings.None: break; - case CommandLineOptions.ProverWarnings.Stdout: + case CoreOptions.ProverWarnings.Stdout: Console.WriteLine("Prover warning: " + message); break; - case CommandLineOptions.ProverWarnings.Stderr: + case CoreOptions.ProverWarnings.Stderr: Console.Error.WriteLine("Prover warning: " + message); break; default: diff --git a/Source/Provers/SMTLib/ProverUtil.cs b/Source/Provers/SMTLib/ProverUtil.cs index 34f2a3a8c..ca558181f 100644 --- a/Source/Provers/SMTLib/ProverUtil.cs +++ b/Source/Provers/SMTLib/ProverUtil.cs @@ -172,7 +172,7 @@ private string ConfirmProverPath(string proverPath) Contract.Requires(proverPath != null); Contract.Ensures(confirmedProverPath != null); confirmedProverPath = proverPath; - if (CommandLineOptions.Clo.Trace) + if (CoreOptions.Clo.Trace) { Console.WriteLine("[TRACE] Using prover: " + confirmedProverPath); } diff --git a/Source/Provers/SMTLib/SMTLibOptions.cs b/Source/Provers/SMTLib/SMTLibOptions.cs index 9648c388f..d7981cd8e 100644 --- a/Source/Provers/SMTLib/SMTLibOptions.cs +++ b/Source/Provers/SMTLib/SMTLibOptions.cs @@ -4,7 +4,7 @@ namespace Microsoft.Boogie { // TODO move to SMTLib - public interface SMTLibOptions : CommandLineOptions + public interface SMTLibOptions : CoreOptions { ProverFactory TheProverFactory { get; } diff --git a/Source/Provers/SMTLib/SMTLibProcessTheoremProver.cs b/Source/Provers/SMTLib/SMTLibProcessTheoremProver.cs index 6e85b8152..83896518e 100644 --- a/Source/Provers/SMTLib/SMTLibProcessTheoremProver.cs +++ b/Source/Provers/SMTLib/SMTLibProcessTheoremProver.cs @@ -108,11 +108,11 @@ private void SetupAxiomBuilder(VCExpressionGenerator gen) { switch (libOptions.TypeEncodingMethod) { - case CommandLineOptions.TypeEncoding.Arguments: + case CoreOptions.TypeEncoding.Arguments: AxBuilder = new TypeAxiomBuilderArguments(gen); AxBuilder.Setup(); break; - case CommandLineOptions.TypeEncoding.Monomorphic: + case CoreOptions.TypeEncoding.Monomorphic: AxBuilder = null; break; default: @@ -1733,13 +1733,13 @@ protected string VCExpr2String(VCExpr expr, int polarity) VCExpr exprWithoutTypes; switch (libOptions.TypeEncodingMethod) { - case CommandLineOptions.TypeEncoding.Arguments: + case CoreOptions.TypeEncoding.Arguments: { TypeEraser eraser = new TypeEraserArguments((TypeAxiomBuilderArguments) AxBuilder, gen); exprWithoutTypes = AxBuilder.Cast(eraser.Erase(expr, polarity), Type.Bool); break; } - case CommandLineOptions.TypeEncoding.Monomorphic: + case CoreOptions.TypeEncoding.Monomorphic: { exprWithoutTypes = expr; break; @@ -1827,7 +1827,7 @@ void InitializeGlobalInformation() //throws ProverException, System.IO.FileNotFoundException; if (_backgroundPredicates == null) { - if (libOptions.TypeEncodingMethod == CommandLineOptions.TypeEncoding.Monomorphic) + if (libOptions.TypeEncodingMethod == CoreOptions.TypeEncoding.Monomorphic) { _backgroundPredicates = ""; } diff --git a/Source/Provers/SMTLib/TypeDeclCollector.cs b/Source/Provers/SMTLib/TypeDeclCollector.cs index 65dadce43..cb37ba6db 100644 --- a/Source/Provers/SMTLib/TypeDeclCollector.cs +++ b/Source/Provers/SMTLib/TypeDeclCollector.cs @@ -307,7 +307,7 @@ private void RegisterType(Type type) return; } - if (type.IsMap && options.TypeEncodingMethod == CommandLineOptions.TypeEncoding.Monomorphic) + if (type.IsMap && options.TypeEncodingMethod == CoreOptions.TypeEncoding.Monomorphic) { KnownTypes.Add(type); MapType mapType = type.AsMap; @@ -352,7 +352,7 @@ private void RegisterType(Type type) } } - if (options.TypeEncodingMethod == CommandLineOptions.TypeEncoding.Monomorphic) + if (options.TypeEncodingMethod == CoreOptions.TypeEncoding.Monomorphic) { AddDeclaration("(declare-sort " + TypeToString(type) + " 0)"); KnownTypes.Add(type); @@ -399,7 +399,7 @@ private void RegisterStore(VCExprNAry node) TypeToString(node.Type) + ")"; AddDeclaration(decl); - if (options.TypeEncodingMethod == CommandLineOptions.TypeEncoding.Monomorphic) + if (options.TypeEncodingMethod == CoreOptions.TypeEncoding.Monomorphic) { var sel = new SMTLibExprLineariser(options).SelectOpName(node); sel = Namer.GetQuotedName(sel, sel); diff --git a/Source/UnitTests/CoreTests/Duplicator.cs b/Source/UnitTests/CoreTests/Duplicator.cs index 81fac3fb3..44d6ccb7a 100644 --- a/Source/UnitTests/CoreTests/Duplicator.cs +++ b/Source/UnitTests/CoreTests/Duplicator.cs @@ -119,7 +119,7 @@ public void WholeProgram() [Test()] public void GotoTargets() { - CommandLineOptions.Clo = new CommandLineOptionsImpl(); + CoreOptions.Clo = new CommandLineOptions(); Program p = TestUtil.ProgramLoader.LoadProgramFrom(@" procedure main() { @@ -218,7 +218,7 @@ implementation main(a:int) returns (r:int) [Test()] public void CallCmdResolving() { - CommandLineOptions.Clo = new CommandLineOptionsImpl(); + CoreOptions.Clo = new CommandLineOptions(); Program p = TestUtil.ProgramLoader.LoadProgramFrom(@" procedure main() { diff --git a/Source/UnitTests/ExecutionEngineTests/CancellationTests.cs b/Source/UnitTests/ExecutionEngineTests/CancellationTests.cs index fc5ad01bb..8007b83d4 100644 --- a/Source/UnitTests/ExecutionEngineTests/CancellationTests.cs +++ b/Source/UnitTests/ExecutionEngineTests/CancellationTests.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; -using System.IO; using System.Threading.Tasks; using Microsoft.Boogie; using NUnit.Framework; @@ -9,39 +7,40 @@ namespace ExecutionEngineTests [TestFixture] public class CancellationTests { - public Program GetProgram(ExecutionEngineOptions options, string code) { + public Program GetProgram(ExecutionEngine engine, string code) { var bplFileName = "1"; int errorCount = Parser.Parse(code, bplFileName, out Program program, - options.UseBaseNameForFileName); + engine.Options.UseBaseNameForFileName); Assert.AreEqual(0, errorCount); - ExecutionEngine.printer = new ConsolePrinter(options); - ExecutionEngine.ResolveAndTypecheck(options, program, bplFileName, out _); - ExecutionEngine.EliminateDeadVariables(program); - ExecutionEngine.CollectModSets(options, program); - ExecutionEngine.CoalesceBlocks(options, program); - ExecutionEngine.Inline(options, program); + ExecutionEngine.printer = new ConsolePrinter(engine.Options); + engine.ResolveAndTypecheck(program, bplFileName, out _); + engine.EliminateDeadVariables(program); + engine.CollectModSets(program); + engine.CoalesceBlocks(program); + engine.Inline(program); return program; } [Test] public async Task InferAndVerifyCanBeCancelledWhileWaitingForProver() { - var options = CommandLineOptionsImpl.FromArguments(); - CommandLineOptionsImpl.Install(options); - var infiniteProgram = GetProgram(options, slow); - var terminatingProgram = GetProgram(options, fast); + var options = CommandLineOptions.FromArguments(); + CommandLineOptions.Install(options); + using var executionEngine = new ExecutionEngine(options); + var infiniteProgram = GetProgram(executionEngine, slow); + var terminatingProgram = GetProgram(executionEngine, fast); // We limit the number of checkers to 1. options.VcsCores = 1; var requestId = ExecutionEngine.FreshRequestId(); - var outcomeTask = Task.Run(() => ExecutionEngine.InferAndVerify(options, infiniteProgram, new PipelineStatistics(), requestId, null, requestId)); + var outcomeTask = Task.Run(() => executionEngine.InferAndVerify(infiniteProgram, new PipelineStatistics(), requestId, null, requestId)); await Task.Delay(1000); ExecutionEngine.CancelRequest(requestId); var outcome = await outcomeTask; Assert.AreEqual(PipelineOutcome.Cancelled, outcome); var requestId2 = ExecutionEngine.FreshRequestId(); - var outcome2 = ExecutionEngine.InferAndVerify(options, terminatingProgram, new PipelineStatistics(), requestId2, null, requestId2); + var outcome2 = executionEngine.InferAndVerify(terminatingProgram, new PipelineStatistics(), requestId2, null, requestId2); Assert.AreEqual(PipelineOutcome.VerificationCompleted, outcome2); } diff --git a/Source/UnitTests/ExecutionEngineTests/GetProverLogs.cs b/Source/UnitTests/ExecutionEngineTests/GetProverLogs.cs index f8830c34b..17946ff28 100644 --- a/Source/UnitTests/ExecutionEngineTests/GetProverLogs.cs +++ b/Source/UnitTests/ExecutionEngineTests/GetProverLogs.cs @@ -10,7 +10,7 @@ public static class GetProverLogs { public static string GetProverLogForProgram(ExecutionEngineOptions options, string procedure) { - CommandLineOptionsImpl.Install(options); + CommandLineOptions.Install(options); var logs = GetProverLogsForProgram(options, procedure).ToList(); Assert.AreEqual(1, logs.Count); return logs[0]; @@ -18,19 +18,20 @@ public static string GetProverLogForProgram(ExecutionEngineOptions options, stri public static IEnumerable GetProverLogsForProgram(ExecutionEngineOptions options, string procedure1) { - ExecutionEngine.printer = new ConsolePrinter(options); + using var engine = new ExecutionEngine(options); + ExecutionEngine.printer = new ConsolePrinter(engine.Options); var defines = new List() { "FILE_0" }; // Parse error are printed to StdOut :/ int errorCount = Parser.Parse(new StringReader(procedure1), "1", defines, out Program program1, - options.UseBaseNameForFileName); + engine.Options.UseBaseNameForFileName); Assert.AreEqual(0, errorCount); string directory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); Directory.CreateDirectory(directory); var temp1 = directory + "/proverLog"; - options.ProverLogFilePath = temp1; - options.ProverOptions.Add("SOLVER=noop"); - var success1 = ExecutionEngine.ProcessProgram(options, program1, "1"); + engine.Options.ProverLogFilePath = temp1; + engine.Options.ProverOptions.Add("SOLVER=noop"); + var success1 = engine.ProcessProgram(program1, "1"); foreach (var proverFile in Directory.GetFiles(directory)) { yield return File.ReadAllText(proverFile); } diff --git a/Source/UnitTests/ExecutionEngineTests/RandomSeedTest.cs b/Source/UnitTests/ExecutionEngineTests/RandomSeedTest.cs index 41aefca81..201bfcb2c 100644 --- a/Source/UnitTests/ExecutionEngineTests/RandomSeedTest.cs +++ b/Source/UnitTests/ExecutionEngineTests/RandomSeedTest.cs @@ -27,18 +27,18 @@ private static string GetProgramWithAttribute(int randomSeed) [Test] public void AttributeAndCommandLineOptionProduceSameResult() { - var options = CommandLineOptionsImpl.FromArguments(); + var options = CommandLineOptions.FromArguments(); options.RandomSeed = randomSeed; var randomOptionsLogs = GetProverLogs.GetProverLogForProgram(options, program); var randomAttributeLogs = - GetProverLogs.GetProverLogForProgram(CommandLineOptionsImpl.FromArguments(), GetProgramWithAttribute(randomSeed)); + GetProverLogs.GetProverLogForProgram(CommandLineOptions.FromArguments(), GetProgramWithAttribute(randomSeed)); Assert.AreEqual(randomOptionsLogs, randomAttributeLogs); } [Test] public void Z3RandomisationOptionsAreSet() { - var options = CommandLineOptionsImpl.FromArguments(); + var options = CommandLineOptions.FromArguments(); options.RandomSeed = randomSeed; var randomOptionsLogs = GetProverLogs.GetProverLogForProgram(options, program); Assert.IsTrue(randomOptionsLogs.Contains("(set-option :smt.random_seed 12312314)")); @@ -48,7 +48,7 @@ public void Z3RandomisationOptionsAreSet() [Test] public void DeclarationOrderIsRandomised() { - var options = CommandLineOptionsImpl.FromArguments(); + var options = CommandLineOptions.FromArguments(); options.NormalizeDeclarationOrder = false; var noRandomLogs = GetProverLogs.GetProverLogForProgram(options, program); options.RandomSeed = 10000; @@ -68,7 +68,7 @@ public void DeclarationOrderIsRandomised() [Test] public void SomeVariablesAreRenamed() { - var options = CommandLineOptionsImpl.FromArguments(); + var options = CommandLineOptions.FromArguments(); options.RandomSeed = randomSeed; options.NormalizeNames = false; var randomOptionsLogs = GetProverLogs.GetProverLogForProgram(options, program); diff --git a/Source/UnitTests/ExecutionEngineTests/SolverLogStabilityTest.cs b/Source/UnitTests/ExecutionEngineTests/SolverLogStabilityTest.cs index f84840ca7..577b69dec 100644 --- a/Source/UnitTests/ExecutionEngineTests/SolverLogStabilityTest.cs +++ b/Source/UnitTests/ExecutionEngineTests/SolverLogStabilityTest.cs @@ -41,7 +41,7 @@ procedure M(p: Person) { }"; - var options = CommandLineOptionsImpl.FromArguments(); + var options = CommandLineOptions.FromArguments(); options.NormalizeNames = true; options.EmitDebugInformation = false; @@ -64,7 +64,7 @@ procedure M(x: int) assert (forall y:int :: x + y + x - y == 4); }"; - var options = CommandLineOptionsImpl.FromArguments(); + var options = CommandLineOptions.FromArguments(); var proverLog1 = GetProverLogs.GetProverLogForProgram(options, procedure); Assert.True(proverLog1.Contains("skolemid")); @@ -146,7 +146,7 @@ procedure M(x2: int, coloredBarrel: Barrel2 RGBColor2) } "; - var options = CommandLineOptionsImpl.FromArguments(); + var options = CommandLineOptions.FromArguments(); options.NormalizeNames = true; var proverLog1 = GetProverLogs.GetProverLogForProgram(options, procedure1); @@ -176,7 +176,7 @@ procedure N(x: int) var procedure2And1 = $@" {procedure1} {procedure2}"; - var options = CommandLineOptionsImpl.FromArguments(); + var options = CommandLineOptions.FromArguments(); var proverLog1 = GetProverLogs.GetProverLogForProgram(options, procedure1); options.ProcsToCheck.Add("M"); @@ -260,7 +260,7 @@ procedure M2(x: int, coloredBarrel: Barrel2 RGBColor2) var procedure2And1 = $@" {procedure1} {procedure2}"; - var options = CommandLineOptionsImpl.FromArguments(); + var options = CommandLineOptions.FromArguments(); options.Prune = true; var proverLog1 = GetProverLogs.GetProverLogForProgram(options, procedure1); diff --git a/Source/VCExpr/TypeErasureArguments.cs b/Source/VCExpr/TypeErasureArguments.cs index 48ec34c4e..a6ba78bd5 100644 --- a/Source/VCExpr/TypeErasureArguments.cs +++ b/Source/VCExpr/TypeErasureArguments.cs @@ -243,7 +243,7 @@ protected override void GenSelectStoreFunctions(MapType abstractedType, TypeCtor select = HelperFuns.BoogieFunction(baseName + "Select", selectTypes); store = HelperFuns.BoogieFunction(baseName + "Store", storeTypes); - if (CommandLineOptions.Clo.UseArrayTheory) + if (CoreOptions.Clo.UseArrayTheory) { select.AddAttribute("builtin", "select"); store.AddAttribute("builtin", "store"); diff --git a/Source/VCExpr/TypeErasurePremisses.cs b/Source/VCExpr/TypeErasurePremisses.cs index b467327fa..f0b963133 100644 --- a/Source/VCExpr/TypeErasurePremisses.cs +++ b/Source/VCExpr/TypeErasurePremisses.cs @@ -729,7 +729,7 @@ protected override void GenSelectStoreFunctions(MapType abstractedType, TypeCtor // the store function does not have any explicit type parameters Contract.Assert(explicitStoreParams.Count == 0); - if (CommandLineOptions.Clo.UseArrayTheory) + if (CoreOptions.Clo.UseArrayTheory) { select.AddAttribute("builtin", "select"); store.AddAttribute("builtin", "store"); @@ -1343,8 +1343,8 @@ private VCExpr HandleQuantifier(VCExprQuantifier node, List /*! List /*!*/ newVarsWithTypeSpecs = new List(); if (!IsUniversalQuantifier(node) || - CommandLineOptions.Clo.TypeEncodingMethod - == CommandLineOptions.TypeEncoding.Predicates) + CoreOptions.Clo.TypeEncodingMethod + == CoreOptions.TypeEncoding.Predicates) { foreach (VCExprVar /*!*/ oldVar in occurringVars) { diff --git a/Source/VCExpr/VCExprASTPrinter.cs b/Source/VCExpr/VCExprASTPrinter.cs index 2b9888dbc..6d6ff0372 100644 --- a/Source/VCExpr/VCExprASTPrinter.cs +++ b/Source/VCExpr/VCExprASTPrinter.cs @@ -416,7 +416,7 @@ public bool VisitAddOp(VCExprNAry node, TextWriter wr) { //Contract.Requires(wr != null); //Contract.Requires(node != null); - if (CommandLineOptions.Clo.ReflectAdd) + if (CoreOptions.Clo.ReflectAdd) { return PrintNAry("Reflect$Add", node, wr); } diff --git a/Source/VCGeneration/Checker.cs b/Source/VCGeneration/Checker.cs index cd9a64f6d..7a784567d 100644 --- a/Source/VCGeneration/Checker.cs +++ b/Source/VCGeneration/Checker.cs @@ -48,6 +48,7 @@ void ObjectInvariant() public volatile Program Program; public readonly ProverOptions SolverOptions; + public VCGenOptions Options => Pool.Options; public CheckerPool Pool { get; } public void GetReady() @@ -111,7 +112,7 @@ public Checker(CheckerPool pool, string /*?*/ logFilePath, bool appendLogFile) } } - SolverOptions.Parse(CommandLineOptions.Clo.ProverOptions); + SolverOptions.Parse(Options.ProverOptions); var ctx = Pool.Options.TheProverFactory.NewProverContext(SolverOptions); @@ -150,7 +151,7 @@ private void SetRlimit(uint rlimit) /// private void Setup(Program prog, ProverContext ctx, Split split = null) { - SolverOptions.RandomSeed = split?.RandomSeed ?? CommandLineOptions.Clo.RandomSeed; + SolverOptions.RandomSeed = split?.RandomSeed ?? Options.RandomSeed; var random = SolverOptions.RandomSeed == null ? null : new Random(SolverOptions.RandomSeed.Value); Program = prog; @@ -185,11 +186,11 @@ private void Setup(Program prog, ProverContext ctx, Split split = null) } } - private static IEnumerable GetReorderedDeclarations(IEnumerable declarations, Random random) + private IEnumerable GetReorderedDeclarations(IEnumerable declarations, Random random) { if (random == null) { // By ordering the declarations based on their content and naming them based on order, the solver input stays constant under reordering and renaming. - return CommandLineOptions.Clo.NormalizeDeclarationOrder + return Options.NormalizeDeclarationOrder ? declarations.OrderBy(d => d.ContentHash) : declarations; } @@ -257,7 +258,7 @@ public Task GetProverResourceCount() private async Task WaitForOutput(object dummy, CancellationToken cancellationToken) { try { - outcome = await thmProver.CheckOutcome(cce.NonNull(handler), CommandLineOptions.Clo.ErrorLimit, + outcome = await thmProver.CheckOutcome(cce.NonNull(handler), Options.ErrorLimit, cancellationToken); } catch (OperationCanceledException) { diff --git a/Source/VCGeneration/ConditionGeneration.cs b/Source/VCGeneration/ConditionGeneration.cs index 489b7055d..e2c5f4bad 100644 --- a/Source/VCGeneration/ConditionGeneration.cs +++ b/Source/VCGeneration/ConditionGeneration.cs @@ -7,6 +7,7 @@ using Microsoft.Boogie; using Microsoft.Boogie.GraphUtil; using System.Diagnostics.Contracts; +using System.Runtime.CompilerServices; using Set = Microsoft.Boogie.GSet; namespace VC @@ -22,7 +23,8 @@ public VCGenException(string s) [ContractClassFor(typeof(ConditionGeneration))] public abstract class ConditionGenerationContracts : ConditionGeneration { - public override Outcome VerifyImplementation(Implementation impl, VerifierCallback callback, CancellationToken cancellationToken) + public override Outcome VerifyImplementation(Implementation impl, VerifierCallback callback, + CancellationToken cancellationToken) { Contract.Requires(impl != null); Contract.Requires(callback != null); @@ -123,7 +125,7 @@ public Outcome VerifyImplementation(Implementation impl, out List(true); Helpers.ExtraTraceInformation("Starting implementation verification"); - CounterexampleCollector collector = new CounterexampleCollector(); + CounterexampleCollector collector = new CounterexampleCollector(Options); collector.RequestId = requestId; Outcome outcome = VerifyImplementation(impl, collector, cancellationToken); if (outcome == Outcome.Errors || outcome == Outcome.TimedOut || outcome == Outcome.OutOfMemory || @@ -140,7 +142,10 @@ public Outcome VerifyImplementation(Implementation impl, out List CheckerPool.Options; + + public abstract Outcome VerifyImplementation(Implementation impl, VerifierCallback callback, + CancellationToken cancellationToken); /////////////////////////////////// Common Methods and Classes ////////////////////////////////////////// @@ -216,14 +221,14 @@ private static void AddAsPrefix(Block b, List cs) /// /// /// - protected static void InjectPreconditions(Implementation impl, [Captured] List startCmds) + protected static void InjectPreconditions(VCGenOptions options, Implementation impl, [Captured] List startCmds) { Contract.Requires(impl != null); Contract.Requires(startCmds != null); Contract.Requires(impl.Proc != null); TokenTextWriter debugWriter = null; - if (CommandLineOptions.Clo.PrintWithUniqueASTIds) + if (options.PrintWithUniqueASTIds) { debugWriter = new TokenTextWriter("", Console.Out, /*setTokens=*/ false, /*pretty=*/ false); debugWriter.WriteLine("Effective precondition:"); @@ -278,7 +283,7 @@ protected static void InjectPreconditions(Implementation impl, [Captured] List - protected static void InjectPostConditions(Implementation impl, Block unifiedExitBlock, + protected static void InjectPostConditions(VCGenOptions options, Implementation impl, Block unifiedExitBlock, Dictionary gotoCmdOrigins) { Contract.Requires(impl != null); @@ -288,7 +293,7 @@ protected static void InjectPostConditions(Implementation impl, Block unifiedExi Contract.Requires(unifiedExitBlock.TransferCmd is ReturnCmd); TokenTextWriter debugWriter = null; - if (CommandLineOptions.Clo.PrintWithUniqueASTIds) + if (options.PrintWithUniqueASTIds) { debugWriter = new TokenTextWriter("", Console.Out, /*setTokens=*/ false, /*pretty=*/ false); debugWriter.WriteLine("Effective postcondition:"); @@ -336,7 +341,7 @@ protected static void InjectPostConditions(Implementation impl, Block unifiedExi /// Get the pre-condition of an implementation, including the where clauses from the in-parameters. /// /// - protected static List GetPre(Implementation impl) + protected static List GetPre(VCGenOptions options, Implementation impl) { Contract.Requires(impl != null); Contract.Requires(impl.Proc != null); @@ -344,7 +349,7 @@ protected static List GetPre(Implementation impl) TokenTextWriter debugWriter = null; - if (CommandLineOptions.Clo.PrintWithUniqueASTIds) + if (options.PrintWithUniqueASTIds) { debugWriter = new TokenTextWriter("", Console.Out, /*setTokens=*/ false, /*pretty=*/ false); debugWriter.WriteLine("Effective precondition:"); @@ -381,12 +386,12 @@ protected static List GetPre(Implementation impl) /// Get the post-condition of an implementation. /// /// - protected static List GetPost(Implementation impl) + protected static List GetPost(VCGenOptions options, Implementation impl) { Contract.Requires(impl != null); Contract.Requires(impl.Proc != null); Contract.Ensures(Contract.Result>() != null); - if (CommandLineOptions.Clo.PrintWithUniqueASTIds) + if (options.PrintWithUniqueASTIds) { Console.WriteLine("Effective postcondition:"); } @@ -407,14 +412,14 @@ protected static List GetPost(Implementation impl) ((AssertEnsuresCmd) c).ErrorDataEnhanced = ensCopy.ErrorDataEnhanced; post.Add(c); - if (CommandLineOptions.Clo.PrintWithUniqueASTIds) + if (options.PrintWithUniqueASTIds) { c.Emit(new TokenTextWriter("", Console.Out, /*setTokens=*/ false, /*pretty=*/ false), 1); } } } - if (CommandLineOptions.Clo.PrintWithUniqueASTIds) + if (options.PrintWithUniqueASTIds) { Console.WriteLine(); } @@ -428,13 +433,13 @@ protected static List GetPost(Implementation impl) /// As a side effect, this method adds these where clauses to the out parameters. /// /// - protected static List GetParamWhereClauses(Implementation impl) + protected static List GetParamWhereClauses(VCGenOptions options, Implementation impl) { Contract.Requires(impl != null); Contract.Requires(impl.Proc != null); Contract.Ensures(Contract.Result>() != null); TokenTextWriter debugWriter = null; - if (CommandLineOptions.Clo.PrintWithUniqueASTIds) + if (options.PrintWithUniqueASTIds) { debugWriter = new TokenTextWriter("", Console.Out, /*setTokens=*/ false, /*pretty=*/ false); debugWriter.WriteLine("Effective precondition from where-clauses:"); @@ -510,6 +515,13 @@ public virtual void Close() public class CounterexampleCollector : VerifierCallback { + private readonly VCGenOptions options; + + public CounterexampleCollector(VCGenOptions options) : base(options.PrintProverWarnings) + { + this.options = options; + } + [ContractInvariantMethod] void ObjectInvariant() { @@ -529,7 +541,7 @@ public override void OnCounterexample(Counterexample ce, string /*?*/ reason) ce.RequestId = RequestId; } - if (ce.OriginalRequestId == null && 1 < CommandLineOptions.Clo.VerifySnapshots) + if (ce.OriginalRequestId == null && 1 < options.VerifySnapshots) { ce.OriginalRequestId = RequestId; } @@ -541,21 +553,21 @@ public override void OnUnreachableCode(Implementation impl) { //Contract.Requires(impl != null); System.Console.WriteLine("found unreachable code:"); - EmitImpl(impl, false); + EmitImpl(options, impl, false); // TODO report error about next to last in seq } } - public static void EmitImpl(Implementation impl, bool printDesugarings) + public static void EmitImpl(VCGenOptions options, Implementation impl, bool printDesugarings) { Contract.Requires(impl != null); - int oldPrintUnstructured = CommandLineOptions.Clo.PrintUnstructured; - CommandLineOptions.Clo.PrintUnstructured = 2; // print only the unstructured program - bool oldPrintDesugaringSetting = CommandLineOptions.Clo.PrintDesugarings; - CommandLineOptions.Clo.PrintDesugarings = printDesugarings; + int oldPrintUnstructured = options.PrintUnstructured; + options.PrintUnstructured = 2; // print only the unstructured program + bool oldPrintDesugaringSetting = options.PrintDesugarings; + options.PrintDesugarings = printDesugarings; impl.Emit(new TokenTextWriter("", Console.Out, /*setTokens=*/ false, /*pretty=*/ false), 0); - CommandLineOptions.Clo.PrintDesugarings = oldPrintDesugaringSetting; - CommandLineOptions.Clo.PrintUnstructured = oldPrintUnstructured; + options.PrintDesugarings = oldPrintDesugaringSetting; + options.PrintUnstructured = oldPrintUnstructured; } @@ -854,21 +866,21 @@ protected Dictionary Convert2PassiveCmd(Implementation impl, Mod var end = DateTime.UtcNow; - if (CommandLineOptions.Clo.TraceCachingForDebugging) + if (Options.TraceCachingForDebugging) { Console.Out.WriteLine("Turned implementation into passive commands within {0:F0} ms.\n", end.Subtract(start).TotalMilliseconds); } - if (CommandLineOptions.Clo.TraceCachingForDebugging) { + if (Options.TraceCachingForDebugging) { using var tokTxtWr = new TokenTextWriter("", Console.Out, false, false); - var pd = CommandLineOptions.Clo.PrintDesugarings; - var pu = CommandLineOptions.Clo.PrintUnstructured; - CommandLineOptions.Clo.PrintDesugarings = true; - CommandLineOptions.Clo.PrintUnstructured = 1; + var pd = Options.PrintDesugarings; + var pu = Options.PrintUnstructured; + Options.PrintDesugarings = true; + Options.PrintUnstructured = 1; impl.Emit(tokTxtWr, 0); - CommandLineOptions.Clo.PrintDesugarings = pd; - CommandLineOptions.Clo.PrintUnstructured = pu; + Options.PrintDesugarings = pd; + Options.PrintUnstructured = pu; } currentImplementation = null; @@ -877,10 +889,10 @@ protected Dictionary Convert2PassiveCmd(Implementation impl, Mod #region Debug Tracing - if (CommandLineOptions.Clo.TraceVerify) + if (Options.TraceVerify) { Console.WriteLine("after conversion to passive commands"); - EmitImpl(impl, true); + EmitImpl(Options, impl, true); } #endregion @@ -901,7 +913,7 @@ protected Dictionary ConvertBlocks2PassiveCmd(List blocks Graph dag = Program.GraphFromBlocks(blocks); IEnumerable sortedNodes; - if (CommandLineOptions.Clo.ModifyTopologicalSorting) + if (Options.ModifyTopologicalSorting) { sortedNodes = dag.TopologicalSort(true); } @@ -1017,7 +1029,7 @@ public enum CachingAction : byte void TraceCachingAction(Cmd cmd, CachingAction action) { - if (CommandLineOptions.Clo.TraceCachingForTesting) { + if (Options.TraceCachingForTesting) { using var tokTxtWr = new TokenTextWriter("", Console.Out, false, false); var loc = cmd.tok != null && cmd.tok != Token.NoToken ? string.Format("{0}({1},{2})", cmd.tok.filename, cmd.tok.line, cmd.tok.col) @@ -1027,7 +1039,7 @@ void TraceCachingAction(Cmd cmd, CachingAction action) Console.Out.WriteLine(" >>> {0}", action); } - if (CommandLineOptions.Clo.TraceCachingForBenchmarking && CachingActionCounts != null) + if (Options.TraceCachingForBenchmarking && CachingActionCounts != null) { Interlocked.Increment(ref CachingActionCounts[(int) action]); } @@ -1129,7 +1141,7 @@ protected void TurnIntoPassiveCmd(Cmd c, Block enclosingBlock, Dictionary Trace; public List AugmentedTrace; public Model Model; @@ -87,10 +88,11 @@ void ObjectInvariant() public Dictionary calleeCounterexamples; - internal Counterexample(List trace, List augmentedTrace, Model model, VC.ModelViewInfo mvInfo, ProverContext context) + internal Counterexample(VCGenOptions options, List trace, List augmentedTrace, Model model, VC.ModelViewInfo mvInfo, ProverContext context) { Contract.Requires(trace != null); Contract.Requires(context != null); + this.options = options; this.Trace = trace; this.Model = model; this.MvInfo = mvInfo; @@ -175,7 +177,7 @@ public void Print(int indent, TextWriter tw, Action blockAction = null) // for ErrorTrace == 1 restrict the output; // do not print tokens with -17:-4 as their location because they have been // introduced in the translation and do not give any useful feedback to the user - if (!(CommandLineOptions.Clo.ErrorTrace == 1 && b.tok.line == -17 && b.tok.col == -4)) + if (!(options.ErrorTrace == 1 && b.tok.line == -17 && b.tok.col == -4)) { if (blockAction != null) { @@ -192,7 +194,7 @@ public void Print(int indent, TextWriter tw, Action blockAction = null) var cmd = getTraceCmd(loc); var calleeName = getCalledProcName(cmd); if (calleeName.StartsWith(VC.StratifiedVCGenBase.recordProcName) && - CommandLineOptions.Clo.StratifiedInlining > 0) + options.StratifiedInlining > 0) { Contract.Assert(calleeCounterexamples[loc].args.Count == 1); var arg = calleeCounterexamples[loc].args[0]; @@ -217,8 +219,8 @@ public void PrintModel(TextWriter tw, Counterexample counterexample) { Contract.Requires(counterexample != null); - var filename = CommandLineOptions.Clo.ModelViewFile; - if (Model == null || filename == null || CommandLineOptions.Clo.StratifiedInlining > 0) + var filename = options.ModelViewFile; + if (Model == null || filename == null || options.StratifiedInlining > 0) { return; } @@ -473,9 +475,9 @@ void ObjectInvariant() } - public AssertCounterexample(List trace, List augmentedTrace, AssertCmd failingAssert, Model model, VC.ModelViewInfo mvInfo, + public AssertCounterexample(VCGenOptions options, List trace, List augmentedTrace, AssertCmd failingAssert, Model model, VC.ModelViewInfo mvInfo, ProverContext context) - : base(trace, augmentedTrace, model, mvInfo, context) + : base(options, trace, augmentedTrace, model, mvInfo, context) { Contract.Requires(trace != null); Contract.Requires(failingAssert != null); @@ -495,7 +497,7 @@ public override byte[] Checksum public override Counterexample Clone() { - var ret = new AssertCounterexample(Trace, AugmentedTrace, FailingAssert, Model, MvInfo, Context); + var ret = new AssertCounterexample(options, Trace, AugmentedTrace, FailingAssert, Model, MvInfo, Context); ret.calleeCounterexamples = calleeCounterexamples; return ret; } @@ -514,9 +516,9 @@ void ObjectInvariant() } - public CallCounterexample(List trace, List augmentedTrace, CallCmd failingCall, Requires failingRequires, Model model, + public CallCounterexample(VCGenOptions options, List trace, List augmentedTrace, CallCmd failingCall, Requires failingRequires, Model model, VC.ModelViewInfo mvInfo, ProverContext context, byte[] checksum = null) - : base(trace, augmentedTrace, model, mvInfo, context) + : base(options, trace, augmentedTrace, model, mvInfo, context) { Contract.Requires(!failingRequires.Free); Contract.Requires(trace != null); @@ -543,7 +545,7 @@ public override byte[] Checksum public override Counterexample Clone() { - var ret = new CallCounterexample(Trace, AugmentedTrace, FailingCall, FailingRequires, Model, MvInfo, Context, Checksum); + var ret = new CallCounterexample(options, Trace, AugmentedTrace, FailingCall, FailingRequires, Model, MvInfo, Context, Checksum); ret.calleeCounterexamples = calleeCounterexamples; return ret; } @@ -562,9 +564,9 @@ void ObjectInvariant() } - public ReturnCounterexample(List trace, List augmentedTrace, TransferCmd failingReturn, Ensures failingEnsures, Model model, + public ReturnCounterexample(VCGenOptions options, List trace, List augmentedTrace, TransferCmd failingReturn, Ensures failingEnsures, Model model, VC.ModelViewInfo mvInfo, ProverContext context, byte[] checksum) - : base(trace, augmentedTrace, model, mvInfo, context) + : base(options, trace, augmentedTrace, model, mvInfo, context) { Contract.Requires(trace != null); Contract.Requires(context != null); @@ -593,7 +595,7 @@ public override byte[] Checksum public override Counterexample Clone() { - var ret = new ReturnCounterexample(Trace, AugmentedTrace, FailingReturn, FailingEnsures, Model, MvInfo, Context, checksum); + var ret = new ReturnCounterexample(options, Trace, AugmentedTrace, FailingReturn, FailingEnsures, Model, MvInfo, Context, checksum); ret.calleeCounterexamples = calleeCounterexamples; return ret; } @@ -601,6 +603,13 @@ public override Counterexample Clone() public class VerifierCallback { + private CoreOptions.ProverWarnings printProverWarnings; + + public VerifierCallback(CoreOptions.ProverWarnings printProverWarnings) + { + this.printProverWarnings = printProverWarnings; + } + // reason == null means this is genuine counterexample returned by the prover // other reason means it's time out/memory out/crash public virtual void OnCounterexample(Counterexample ce, string /*?*/ reason) @@ -636,14 +645,14 @@ public virtual void OnUnreachableCode(Implementation impl) public virtual void OnWarning(string msg) { Contract.Requires(msg != null); - switch (CommandLineOptions.Clo.PrintProverWarnings) + switch (printProverWarnings) { - case CommandLineOptions.ProverWarnings.None: + case CoreOptions.ProverWarnings.None: break; - case CommandLineOptions.ProverWarnings.Stdout: + case CoreOptions.ProverWarnings.Stdout: Console.WriteLine("Prover warning: " + msg); break; - case CommandLineOptions.ProverWarnings.Stderr: + case CoreOptions.ProverWarnings.Stderr: Console.Error.WriteLine("Prover warning: " + msg); break; default: diff --git a/Source/VCGeneration/Prune/Prune.cs b/Source/VCGeneration/Prune/Prune.cs index 5d3df53dd..6360c2787 100644 --- a/Source/VCGeneration/Prune/Prune.cs +++ b/Source/VCGeneration/Prune/Prune.cs @@ -9,7 +9,7 @@ public class Prune { public static Dictionary> ComputeDeclarationDependencies(Program program) { - if (!CommandLineOptions.Clo.Prune) + if (!CoreOptions.Clo.Prune) { return null; } @@ -56,7 +56,7 @@ public static Dictionary> ComputeDeclarationDependencies(Pr */ public static IEnumerable GetLiveDeclarations(Program program, List blocks) { - if (program.DeclarationDependencies == null || blocks == null || !CommandLineOptions.Clo.Prune) + if (program.DeclarationDependencies == null || blocks == null || !CoreOptions.Clo.Prune) { return program.TopLevelDeclarations; } diff --git a/Source/VCGeneration/Split.cs b/Source/VCGeneration/Split.cs index 0839c91d7..8b13b2248 100644 --- a/Source/VCGeneration/Split.cs +++ b/Source/VCGeneration/Split.cs @@ -17,7 +17,9 @@ namespace VC public class Split { - public int? RandomSeed => Implementation.RandomSeed ?? CommandLineOptions.Clo.RandomSeed; + private VCGenOptions options; + + public int? RandomSeed => Implementation.RandomSeed ?? options.RandomSeed; class BlockStats { @@ -117,7 +119,8 @@ readonly public VCGen /*!*/ private int splitNum; internal VCGen.ErrorReporter reporter; - public Split(List /*!*/ blocks, Dictionary /*!*/ gotoCmdOrigins, + public Split(VCGenOptions options, List /*!*/ blocks, + Dictionary /*!*/ gotoCmdOrigins, VCGen /*!*/ par, Implementation /*!*/ implementation) { Contract.Requires(cce.NonNullElements(blocks)); @@ -128,6 +131,7 @@ public Split(List /*!*/ blocks, Dictionary this.gotoCmdOrigins = gotoCmdOrigins; this.parent = par; this.Implementation = implementation; + this.options = options; Interlocked.Increment(ref currentId); TopLevelDeclarations = par.program.TopLevelDeclarations; @@ -138,14 +142,14 @@ public Split(List /*!*/ blocks, Dictionary private void PrintTopLevelDeclarationsForPruning(Program program, Implementation implementation, string suffix) { - if (!CommandLineOptions.Clo.Prune || CommandLineOptions.Clo.PrintPrunedFile == null) + if (!options.Prune || options.PrintPrunedFile == null) { return; } using var writer = new TokenTextWriter( - $"{CommandLineOptions.Clo.PrintPrunedFile}-{suffix}-{Util.EscapeFilename(implementation.Name)}", false, - CommandLineOptions.Clo.PrettyPrint); + $"{options.PrintPrunedFile}-{suffix}-{Util.EscapeFilename(implementation.Name)}", false, + options.PrettyPrint); foreach (var declaration in TopLevelDeclarations ?? program.TopLevelDeclarations) { declaration.Emit(writer, 0); } @@ -216,17 +220,17 @@ public void DumpDot(int splitNum) string filename = string.Format("{0}.split.{1}.bpl", Implementation.Name, splitNum); using (System.IO.StreamWriter sw = System.IO.File.CreateText(filename)) { - int oldPrintUnstructured = CommandLineOptions.Clo.PrintUnstructured; - CommandLineOptions.Clo.PrintUnstructured = 2; // print only the unstructured program - bool oldPrintDesugaringSetting = CommandLineOptions.Clo.PrintDesugarings; - CommandLineOptions.Clo.PrintDesugarings = false; + int oldPrintUnstructured = options.PrintUnstructured; + options.PrintUnstructured = 2; // print only the unstructured program + bool oldPrintDesugaringSetting = options.PrintDesugarings; + options.PrintDesugarings = false; List backup = Implementation.Blocks; Contract.Assert(backup != null); Implementation.Blocks = blocks; Implementation.Emit(new TokenTextWriter(filename, sw, /*setTokens=*/ false, /*pretty=*/ false), 0); Implementation.Blocks = backup; - CommandLineOptions.Clo.PrintDesugarings = oldPrintDesugaringSetting; - CommandLineOptions.Clo.PrintUnstructured = oldPrintUnstructured; + options.PrintDesugarings = oldPrintDesugaringSetting; + options.PrintUnstructured = oldPrintUnstructured; } } @@ -432,7 +436,7 @@ void ComputeBestSplit() } } - if (CommandLineOptions.Clo.VcsPathSplitMult * score > totalCost) + if (options.VcsPathSplitMult * score > totalCost) { splitBlock = null; score = -1; @@ -471,7 +475,7 @@ void UpdateIncommingPaths(BlockStats s) if (count > 1) { - s.incomingPaths *= CommandLineOptions.Clo.VcsPathJoinMult; + s.incomingPaths *= options.VcsPathJoinMult; } } } @@ -583,14 +587,14 @@ double DoComputeScore(bool aa) double local = s.assertionCost; if (ShouldAssumize(b)) { - local = (s.assertionCost + s.assumptionCost) * CommandLineOptions.Clo.VcsAssumeMult; + local = (s.assertionCost + s.assumptionCost) * options.VcsAssumeMult; } else { - local = s.assumptionCost * CommandLineOptions.Clo.VcsAssumeMult + s.assertionCost; + local = s.assumptionCost * options.VcsAssumeMult + s.assertionCost; } - local = local + local * s.incomingPaths * CommandLineOptions.Clo.VcsPathCostMult; + local = local + local * s.incomingPaths * options.VcsPathCostMult; cost += local; } } @@ -715,7 +719,7 @@ Split DoSplit() } } - return new Split(newBlocks, newGotoCmdOrigins, parent, Implementation); + return new Split(options, newBlocks, newGotoCmdOrigins, parent, Implementation); } Split SplitAt(int idx) @@ -754,7 +758,7 @@ void Print() List tmp = Implementation.Blocks; Contract.Assert(tmp != null); Implementation.Blocks = blocks; - ConditionGeneration.EmitImpl(Implementation, false); + ConditionGeneration.EmitImpl(options, Implementation, false); Implementation.Blocks = tmp; } @@ -778,7 +782,7 @@ public Counterexample ToCounterexample(ProverContext context) Contract.Assert(c != null); if (c is AssertCmd) { - return VCGen.AssertCmdToCounterexample((AssertCmd) c, cce.NonNull(b.TransferCmd), trace, null, null, null, context); + return VCGen.AssertCmdToCounterexample(options, (AssertCmd) c, cce.NonNull(b.TransferCmd), trace, null, null, null, context); } } } @@ -949,13 +953,13 @@ private static bool ShouldSplitHere(Cmd c, bool splitOnEveryAssert) { || c is AssertCmd && splitOnEveryAssert; } - public static List FindManualSplits(Split s, bool splitOnEveryAssert) + public static List FindManualSplits(Split initialSplit, bool splitOnEveryAssert) { - Contract.Requires(s.Implementation != null); + Contract.Requires(initialSplit.Implementation != null); Contract.Ensures(Contract.Result>() == null || cce.NonNullElements(Contract.Result>())); var splitPoints = new Dictionary(); - foreach (var b in s.blocks) + foreach (var b in initialSplit.blocks) { foreach (Cmd c in b.Cmds) { @@ -967,39 +971,39 @@ private static bool ShouldSplitHere(Cmd c, bool splitOnEveryAssert) { } } } - List splits = new List(); - if (splitPoints.Count() == 0) + var splits = new List(); + if (!splitPoints.Any()) { - splits.Add(s); + splits.Add(initialSplit); } else { - Block entryPoint = s.blocks[0]; - var blockAssignments = PickBlocksToVerify(s.blocks, splitPoints); + Block entryPoint = initialSplit.blocks[0]; + var blockAssignments = PickBlocksToVerify(initialSplit.blocks, splitPoints); var entryBlockHasSplit = splitPoints.Keys.Contains(entryPoint); - var baseSplitBlocks = PostProcess(DoPreAssignedManualSplit(s.blocks, blockAssignments, -1, entryPoint, !entryBlockHasSplit, splitOnEveryAssert)); - splits.Add(new Split(baseSplitBlocks, s.gotoCmdOrigins, s.parent, s.Implementation)); + var baseSplitBlocks = PostProcess(DoPreAssignedManualSplit(initialSplit.blocks, blockAssignments, -1, entryPoint, !entryBlockHasSplit, splitOnEveryAssert)); + splits.Add(new Split(initialSplit.options, baseSplitBlocks, initialSplit.gotoCmdOrigins, initialSplit.parent, initialSplit.Implementation)); foreach (KeyValuePair pair in splitPoints) { for (int i = 0; i < pair.Value; i++) { bool lastSplitInBlock = i == pair.Value - 1; - var newBlocks = DoPreAssignedManualSplit(s.blocks, blockAssignments, i, pair.Key, lastSplitInBlock, splitOnEveryAssert); - splits.Add(new Split(PostProcess(newBlocks), s.gotoCmdOrigins, s.parent, s.Implementation)); // REVIEW: Does gotoCmdOrigins need to be changed at all? + var newBlocks = DoPreAssignedManualSplit(initialSplit.blocks, blockAssignments, i, pair.Key, lastSplitInBlock, splitOnEveryAssert); + splits.Add(new Split(initialSplit.options, PostProcess(newBlocks), initialSplit.gotoCmdOrigins, initialSplit.parent, initialSplit.Implementation)); // REVIEW: Does gotoCmdOrigins need to be changed at all? } } } return splits; } - public static List FocusImpl(Implementation impl, Dictionary gotoCmdOrigins, VCGen par) + public static List FocusImpl(VCGenOptions options, Implementation impl, Dictionary gotoCmdOrigins, VCGen par) { bool IsFocusCmd(Cmd c) { return c is PredicateCmd p && QKeyValue.FindBoolAttribute(p.Attributes, "focus"); } List GetFocusBlocks(List blocks) { - return blocks.Where(blk => blk.Cmds.Where(c => IsFocusCmd(c)).Any()).ToList(); + return blocks.Where(blk => blk.Cmds.Any(c => IsFocusCmd(c))).ToList(); } var dag = Program.GraphFromImpl(impl); @@ -1007,12 +1011,12 @@ List GetFocusBlocks(List blocks) { // By default, we process the foci in a top-down fashion, i.e., in the topological order. // If the user sets the RelaxFocus flag, we use the reverse (topological) order. var focusBlocks = GetFocusBlocks(topoSorted); - if (CommandLineOptions.Clo.RelaxFocus) { + if (par.CheckerPool.Options.RelaxFocus) { focusBlocks.Reverse(); } if (!focusBlocks.Any()) { var f = new List(); - f.Add(new Split(impl.Blocks, gotoCmdOrigins, par, impl)); + f.Add(new Split(options, impl.Blocks, gotoCmdOrigins, par, impl)); return f; } // finds all the blocks dominated by focusBlock in the subgraph @@ -1088,7 +1092,7 @@ void FocusRec(int focusIdx, IEnumerable blocks, IEnumerable freeBl } newBlocks.Reverse(); Contract.Assert(newBlocks[0] == oldToNewBlockMap[impl.Blocks[0]]); - s.Add(new Split(PostProcess(newBlocks), gotoCmdOrigins, par, impl)); + s.Add(new Split(options, PostProcess(newBlocks), gotoCmdOrigins, par, impl)); } else if (!blocks.Contains(focusBlocks[focusIdx]) || freeBlocks.Contains(focusBlocks[focusIdx])) @@ -1113,9 +1117,9 @@ void FocusRec(int focusIdx, IEnumerable blocks, IEnumerable freeBl return s; } - public static List FocusAndSplit(Implementation impl, Dictionary gotoCmdOrigins, VCGen par, bool splitOnEveryAssert) + public static List FocusAndSplit(VCGenOptions options, Implementation impl, Dictionary gotoCmdOrigins, VCGen par, bool splitOnEveryAssert) { - List focussedImpl = FocusImpl(impl, gotoCmdOrigins, par); + List focussedImpl = FocusImpl(options, impl, gotoCmdOrigins, par); var splits = focussedImpl.Select(s => FindManualSplits(s, splitOnEveryAssert)).SelectMany(x => x).ToList(); return splits; } @@ -1150,7 +1154,7 @@ public static List FocusAndSplit(Implementation impl, Dictionary FocusAndSplit(Implementation impl, Dictionary {0}", s1.Stats); } - if (CommandLineOptions.Clo.TraceVerify) + if (initial.options.TraceVerify) { best.Print(); } @@ -1283,18 +1287,18 @@ public void ReadOutcome(ref ConditionGeneration.Outcome curOutcome, out bool pro Contract.EnsuresOnThrow(true); ProverInterface.Outcome outcome = cce.NonNull(checker).ReadOutcome(); - if (CommandLineOptions.Clo.Trace && splitNum >= 0) + if (options.Trace && splitNum >= 0) { System.Console.WriteLine(" --> split #{0} done, [{1} s] {2}", splitNum + 1, checker.ProverRunTime.TotalSeconds, outcome); } - if (CommandLineOptions.Clo.XmlSink != null && splitNum >= 0) { - CommandLineOptions.Clo.XmlSink.WriteEndSplit(outcome.ToString().ToLowerInvariant(), + if (options.XmlSink != null && splitNum >= 0) { + options.XmlSink.WriteEndSplit(outcome.ToString().ToLowerInvariant(), TimeSpan.FromSeconds(checker.ProverRunTime.TotalSeconds)); } - if (CommandLineOptions.Clo.VcsDumpSplits) + if (options.VcsDumpSplits) { DumpDot(splitNum); } @@ -1381,10 +1385,10 @@ public void BeginCheck(Checker checker, VerifierCallback callback, ModelViewInfo exprGen.ControlFlowFunctionApplication(exprGen.Integer(BigNum.ZERO), exprGen.Integer(BigNum.ZERO)); VCExpr eqExpr = exprGen.Eq(controlFlowFunctionAppl, exprGen.Integer(BigNum.FromInt(absyIds.GetId(Implementation.Blocks[0])))); vc = exprGen.Implies(eqExpr, vc); - reporter = new VCGen.ErrorReporter(gotoCmdOrigins, absyIds, Implementation.Blocks, parent.debugInfos, callback, - mvInfo, this.Checker.TheoremProver.Context, parent.program); + reporter = new VCGen.ErrorReporter(options, gotoCmdOrigins, absyIds, Implementation.Blocks, parent.debugInfos, callback, + mvInfo, Checker.TheoremProver.Context, parent.program); - if (CommandLineOptions.Clo.TraceVerify && no >= 0) + if (options.TraceVerify && no >= 0) { Console.WriteLine("-- after split #{0}", no); Print(); diff --git a/Source/VCGeneration/SplitAndVerifyWorker.cs b/Source/VCGeneration/SplitAndVerifyWorker.cs index de1f73d1d..c6d4caddb 100644 --- a/Source/VCGeneration/SplitAndVerifyWorker.cs +++ b/Source/VCGeneration/SplitAndVerifyWorker.cs @@ -61,7 +61,7 @@ public SplitAndVerifyWorker(VCGenOptions options, VCGen vcGen, Implementation im implementation.CheckBooleanAttribute("vcs_split_on_every_assert", ref splitOnEveryAssert); ResetPredecessors(implementation.Blocks); - manualSplits = Split.FocusAndSplit(implementation, gotoCmdOrigins, vcGen, splitOnEveryAssert); + manualSplits = Split.FocusAndSplit(options, implementation, gotoCmdOrigins, vcGen, splitOnEveryAssert); if (manualSplits.Count == 1 && maxSplits > 1) { manualSplits = Split.DoSplit(manualSplits[0], maxVcCost, maxSplits); diff --git a/Source/VCGeneration/VCGen.cs b/Source/VCGeneration/VCGen.cs index 27071a68e..bb99708b7 100644 --- a/Source/VCGeneration/VCGen.cs +++ b/Source/VCGeneration/VCGen.cs @@ -6,6 +6,7 @@ using Microsoft.Boogie; using Microsoft.Boogie.GraphUtil; using System.Diagnostics.Contracts; +using System.Runtime.CompilerServices; using System.Threading; using Microsoft.BaseTypes; using Microsoft.Boogie.VCExprAST; @@ -36,12 +37,12 @@ public static AssumeCmd AssertTurnedIntoAssume(AssertCmd assrt) Contract.Assert(expr != null); switch (Wlp.Subsumption(assrt)) { - case CommandLineOptions.SubsumptionOption.Never: + case CoreOptions.SubsumptionOption.Never: expr = Expr.True; break; - case CommandLineOptions.SubsumptionOption.Always: + case CoreOptions.SubsumptionOption.Always: break; - case CommandLineOptions.SubsumptionOption.NotForQuantifiers: + case CoreOptions.SubsumptionOption.NotForQuantifiers: if (expr is QuantifierExpr) { expr = Expr.True; @@ -119,7 +120,7 @@ void TopologicalSortImpl() void Emit() { TopologicalSortImpl(); - EmitImpl(impl, false); + EmitImpl(Options, impl, false); } // this one copies forward @@ -352,7 +353,7 @@ bool CheckUnreachable(Block cur, List seq) Emit(); } - ch.BeginCheck(cce.NonNull(impl.Name + "_smoke" + id++), vc, new ErrorHandler(absyIds, this.callback), + ch.BeginCheck(cce.NonNull(impl.Name + "_smoke" + id++), vc, new ErrorHandler(Options, absyIds, callback), Options.SmokeTimeout, Options.ResourceLimit, CancellationToken.None); } @@ -515,7 +516,7 @@ void ObjectInvariant() } - public ErrorHandler(ControlFlowIdMap absyIds, VerifierCallback callback) + public ErrorHandler(VCGenOptions options, ControlFlowIdMap absyIds, VerifierCallback callback) : base(options) { Contract.Requires(absyIds != null); Contract.Requires(callback != null); @@ -566,7 +567,7 @@ public VCExpr CodeExprToVerificationCondition(CodeExpr codeExpr, List gotoCmdOrigins = vcgen.ConvertBlocks2PassiveCmd(codeExpr.Blocks, new List(), new ModelViewInfo(codeExpr)); - VCExpr startCorrect = LetVC(codeExpr.Blocks, null, absyIds, ctx, out var ac, isPositiveContext); + VCExpr startCorrect = vcgen.LetVC(codeExpr.Blocks, null, absyIds, ctx, out var ac, isPositiveContext); VCExpr vce = ctx.ExprGen.Let(bindings, startCorrect); if (vcgen.CurrentLocalVariables.Count != 0) { @@ -811,7 +812,8 @@ void ExpandAsserts(Implementation impl) private VCGenOptions Options => CheckerPool.Options; - public override Outcome VerifyImplementation(Implementation impl, VerifierCallback callback, CancellationToken cancellationToken) + public override Outcome VerifyImplementation(Implementation impl, VerifierCallback callback, + CancellationToken cancellationToken) { Contract.EnsuresOnThrow(true); @@ -861,7 +863,7 @@ public override Outcome VerifyImplementation(Implementation impl, VerifierCallba else { // If possible, we use the old counterexample, but with the location information of "a" - var cex = AssertCmdToCloneCounterexample(a, oldCex, impl.Blocks[0], gotoCmdOrigins); + var cex = AssertCmdToCloneCounterexample(CheckerPool.Options, a, oldCex, impl.Blocks[0], gotoCmdOrigins); 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) @@ -893,6 +895,7 @@ public override Outcome VerifyImplementation(Implementation impl, VerifierCallba public class ErrorReporter : ProverInterface.ErrorHandler { + private VCGenOptions options; Dictionary gotoCmdOrigins; ControlFlowIdMap absyIds; @@ -931,14 +934,15 @@ public override void AddNecessaryAssume(string id) program.NecessaryAssumes.Add(id); } - public ErrorReporter(Dictionary /*!*/ gotoCmdOrigins, + public ErrorReporter(VCGenOptions options, + Dictionary /*!*/ gotoCmdOrigins, ControlFlowIdMap /*!*/ absyIds, List /*!*/ blocks, Dictionary> debugInfos, VerifierCallback /*!*/ callback, ModelViewInfo mvInfo, ProverContext /*!*/ context, - Program /*!*/ program) + Program /*!*/ program) : base(options) { Contract.Requires(gotoCmdOrigins != null); Contract.Requires(absyIds != null); @@ -955,6 +959,7 @@ public ErrorReporter(Dictionary /*!*/ gotoCmdOrigins, this.context = context; this.program = program; + this.options = options; } public override void OnModel(IList /*!*/ labels, Model model, ProverInterface.Outcome proverOutcome) @@ -986,7 +991,7 @@ public override void OnModel(IList /*!*/ labels, Model model, Prov Contract.Assert(traceNodes.Contains(entryBlock)); trace.Add(entryBlock); - Counterexample newCounterexample = TraceCounterexample(entryBlock, traceNodes, trace, model, MvInfo, + Counterexample newCounterexample = TraceCounterexample(options, entryBlock, traceNodes, trace, model, MvInfo, debugInfos, context, new Dictionary()); if (newCounterexample == null) @@ -1035,7 +1040,7 @@ public override void OnResourceExceeded(string msg, IEnumerable(), new List(), null, null, context); + AssertCmdToCounterexample(options, cmd.Item1, cmd.Item2, new List(), new List(), null, null, context); cex.IsAuxiliaryCexForDiagnosingTimeouts = true; callback.OnCounterexample(cex, msg); } @@ -1076,7 +1081,7 @@ public void ConvertCFG2DAG(Implementation impl, Dictionary> e if (Options.TraceVerify) { Console.WriteLine("original implementation"); - EmitImpl(impl, false); + EmitImpl(Options, impl, false); } #endregion @@ -1086,7 +1091,7 @@ public void ConvertCFG2DAG(Implementation impl, Dictionary> e if (Options.TraceVerify) { Console.WriteLine("after desugaring sugared commands like procedure calls"); - EmitImpl(impl, true); + EmitImpl(Options, impl, true); } #endregion @@ -1114,7 +1119,7 @@ public void ConvertCFG2DAG(Implementation impl, Dictionary> e if (Options.TraceVerify) { Console.WriteLine("after conversion into a DAG"); - EmitImpl(impl, true); + EmitImpl(Options, impl, true); } #endregion @@ -1700,7 +1705,7 @@ public Dictionary PassifyImpl(Implementation impl, out M if (Options.TraceVerify) { Console.WriteLine("after creating a unified exit block"); - EmitImpl(impl, true); + EmitImpl(Options, impl, true); } #endregion @@ -1723,7 +1728,7 @@ public Dictionary PassifyImpl(Implementation impl, out M } // where clauses of in- and out-parameters - cc.AddRange(GetParamWhereClauses(impl)); + cc.AddRange(GetParamWhereClauses(Options, impl)); // where clauses of local variables foreach (Variable lvar in impl.LocVars) { @@ -1744,10 +1749,10 @@ public Dictionary PassifyImpl(Implementation impl, out M } // add cc and the preconditions to new blocks preceding impl.Blocks[0] - InjectPreconditions(impl, cc); + InjectPreconditions(Options, impl, cc); // append postconditions, starting in exitBlock and continuing into other blocks, if needed - InjectPostConditions(impl, exitBlock, gotoCmdOrigins); + InjectPostConditions(Options, impl, exitBlock, gotoCmdOrigins); } #endregion @@ -1764,7 +1769,7 @@ public Dictionary PassifyImpl(Implementation impl, out M if (Options.TraceVerify) { Console.WriteLine("after inserting pre- and post-conditions"); - EmitImpl(impl, true); + EmitImpl(Options, impl, true); } #endregion @@ -1776,7 +1781,7 @@ public Dictionary PassifyImpl(Implementation impl, out M if (Options.TraceVerify) { Console.WriteLine("after adding empty blocks as needed to catch join assumptions"); - EmitImpl(impl, true); + EmitImpl(Options, impl, true); } #endregion @@ -1812,7 +1817,7 @@ public Dictionary PassifyImpl(Implementation impl, out M if (Options.TraceVerify) { Console.WriteLine("after peep-hole optimizations"); - EmitImpl(impl, true); + EmitImpl(Options, impl, true); } #endregion @@ -2293,6 +2298,7 @@ private Block elGetBlock(string procname, Block block, } static Counterexample TraceCounterexample( + VCGenOptions options, Block b, HashSet traceNodes, List trace, Model errModel, ModelViewInfo mvInfo, Dictionary> debugInfos, ProverContext context, @@ -2325,7 +2331,7 @@ static Counterexample TraceCounterexample( if (cmd is AssertCmd && traceNodes.Contains(cmd)) { Counterexample newCounterexample = - AssertCmdToCounterexample((AssertCmd) cmd, transferCmd, trace, augmentedTrace, errModel, mvInfo, context); + AssertCmdToCounterexample(options, (AssertCmd) cmd, transferCmd, trace, augmentedTrace, errModel, mvInfo, context); Contract.Assert(newCounterexample != null); newCounterexample.AddCalleeCounterexample(calleeCounterexamples); return newCounterexample; @@ -2359,7 +2365,7 @@ static Counterexample TraceCounterexample( } } - public static Counterexample AssertCmdToCounterexample(AssertCmd cmd, TransferCmd transferCmd, List trace, List augmentedTrace, + public static Counterexample AssertCmdToCounterexample(VCGenOptions options, AssertCmd cmd, TransferCmd transferCmd, List trace, List augmentedTrace, Model errModel, ModelViewInfo mvInfo, ProverContext context) { Contract.Requires(cmd != null); @@ -2373,7 +2379,7 @@ public static Counterexample AssertCmdToCounterexample(AssertCmd cmd, TransferCm { AssertRequiresCmd assertCmd = (AssertRequiresCmd) cmd; Contract.Assert(assertCmd != null); - CallCounterexample cc = new CallCounterexample(trace, augmentedTrace, assertCmd.Call, assertCmd.Requires, errModel, mvInfo, + CallCounterexample cc = new CallCounterexample(options, trace, augmentedTrace, assertCmd.Call, assertCmd.Requires, errModel, mvInfo, context, assertCmd.Checksum); return cc; } @@ -2381,13 +2387,13 @@ public static Counterexample AssertCmdToCounterexample(AssertCmd cmd, TransferCm { AssertEnsuresCmd assertCmd = (AssertEnsuresCmd) cmd; Contract.Assert(assertCmd != null); - ReturnCounterexample rc = new ReturnCounterexample(trace, augmentedTrace, transferCmd, assertCmd.Ensures, errModel, mvInfo, + ReturnCounterexample rc = new ReturnCounterexample(options, trace, augmentedTrace, transferCmd, assertCmd.Ensures, errModel, mvInfo, context, cmd.Checksum); return rc; } else { - AssertCounterexample ac = new AssertCounterexample(trace, augmentedTrace, (AssertCmd) cmd, errModel, mvInfo, context); + AssertCounterexample ac = new AssertCounterexample(options, trace, augmentedTrace, (AssertCmd) cmd, errModel, mvInfo, context); return ac; } } @@ -2395,7 +2401,7 @@ public static Counterexample AssertCmdToCounterexample(AssertCmd cmd, TransferCm /// /// 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, + public static Counterexample AssertCmdToCloneCounterexample(VCGenOptions options, AssertCmd assrt, Counterexample cex, Block implEntryBlock, Dictionary gotoCmdOrigins) { Contract.Requires(assrt != null); @@ -2408,7 +2414,7 @@ public static Counterexample AssertCmdToCloneCounterexample(AssertCmd assrt, Cou if (assrt is AssertRequiresCmd) { var aa = (AssertRequiresCmd) assrt; - cc = new CallCounterexample(cex.Trace, cex.AugmentedTrace, aa.Call, aa.Requires, cex.Model, cex.MvInfo, cex.Context, aa.Checksum); + cc = new CallCounterexample(options, cex.Trace, cex.AugmentedTrace, aa.Call, aa.Requires, cex.Model, cex.MvInfo, cex.Context, aa.Checksum); } else if (assrt is AssertEnsuresCmd && cex is ReturnCounterexample) { @@ -2490,12 +2496,12 @@ public static Counterexample AssertCmdToCloneCounterexample(AssertCmd assrt, Cou } } - cc = new ReturnCounterexample(reconstructedTrace ?? cex.Trace, cex.AugmentedTrace, returnCmd ?? oldCex.FailingReturn, aa.Ensures, + cc = new ReturnCounterexample(options, reconstructedTrace ?? cex.Trace, cex.AugmentedTrace, returnCmd ?? oldCex.FailingReturn, aa.Ensures, cex.Model, cex.MvInfo, cex.Context, aa.Checksum); } else { - cc = new AssertCounterexample(cex.Trace, cex.AugmentedTrace, assrt, cex.Model, cex.MvInfo, cex.Context); + cc = new AssertCounterexample(options, cex.Trace, cex.AugmentedTrace, assrt, cex.Model, cex.MvInfo, cex.Context); } return cc; @@ -2523,7 +2529,7 @@ public static Counterexample AssertCmdToCloneCounterexample(AssertCmd assrt, Cou * */ - static VCExpr LetVC(List blocks, + VCExpr LetVC(List blocks, VCExpr controlFlowVariableExpr, ControlFlowIdMap absyIds, ProverContext proverCtxt, @@ -2588,7 +2594,7 @@ static VCExpr LetVC(List blocks, SuccCorrect = gen.NAry(VCExpressionGenerator.AndOp, SuccCorrectVars); } - VCContext context = new VCContext(absyIds, proverCtxt, controlFlowVariableExpr, isPositiveContext); + VCContext context = new VCContext(Options, absyIds, proverCtxt, controlFlowVariableExpr, isPositiveContext); VCExpr vc = Wlp.Block(block, SuccCorrect, context); assertionCount += context.AssertionCount; @@ -2600,7 +2606,7 @@ static VCExpr LetVC(List blocks, return proverCtxt.ExprGen.Let(bindings, blockVariables[blocks[0]]); } - static VCExpr DagVC(Block block, + VCExpr DagVC(Block block, VCExpr controlFlowVariableExpr, ControlFlowIdMap absyIds, Dictionary blockEquations, @@ -2653,7 +2659,7 @@ static VCExpr DagVC(Block block, SuccCorrect = VCExpressionGenerator.True; } - VCContext context = new VCContext(absyIds, proverCtxt, controlFlowVariableExpr); + VCContext context = new VCContext(Options, absyIds, proverCtxt, controlFlowVariableExpr); vc = Wlp.Block(block, SuccCorrect, context); assertionCount += context.AssertionCount; diff --git a/Source/VCGeneration/Wlp.cs b/Source/VCGeneration/Wlp.cs index 6580c96f8..8f9550727 100644 --- a/Source/VCGeneration/Wlp.cs +++ b/Source/VCGeneration/Wlp.cs @@ -3,12 +3,15 @@ using Microsoft.Boogie.VCExprAST; using System.Diagnostics.Contracts; using System.Collections.Generic; +using System.Runtime.CompilerServices; using Microsoft.BaseTypes; namespace VC { public class VCContext { + public VCGenOptions Options { get; } + [ContractInvariantMethod] void ObjectInvariant() { @@ -21,18 +24,20 @@ void ObjectInvariant() public int AssertionCount; // counts the number of assertions for which Wlp has been computed public bool isPositiveContext; - public VCContext(ControlFlowIdMap absyIds, ProverContext ctxt, bool isPositiveContext = true) + public VCContext(VCGenOptions options, ControlFlowIdMap absyIds, ProverContext ctxt, bool isPositiveContext = true) { Contract.Requires(ctxt != null); + Options = options; this.absyIds = absyIds; this.Ctxt = ctxt; this.isPositiveContext = isPositiveContext; } - public VCContext(ControlFlowIdMap absyIds, ProverContext ctxt, VCExpr controlFlowVariableExpr, + public VCContext(VCGenOptions options, ControlFlowIdMap absyIds, ProverContext ctxt, VCExpr controlFlowVariableExpr, bool isPositiveContext = true) { Contract.Requires(ctxt != null); + Options = options; this.absyIds = absyIds; this.Ctxt = ctxt; this.ControlFlowVariableExpr = controlFlowVariableExpr; @@ -112,7 +117,7 @@ internal static VCExpr Cmd(Block b, Cmd cmd, VCExpr N, VCContext ctxt) { VU = ctxt.Ctxt.BoogieExprTranslator.Translate(ac.VerifiedUnder); - if (CommandLineOptions.Clo.RunDiagnosticsOnTimeout) + if (ctxt.Options.RunDiagnosticsOnTimeout) { ctxt.Ctxt.TimeoutDiagnosticIDToAssertion[ctxt.Ctxt.TimeoutDiagnosticsCount] = new Tuple(ac, b.TransferCmd); @@ -121,7 +126,7 @@ internal static VCExpr Cmd(Block b, Cmd cmd, VCExpr N, VCContext ctxt) gen.Integer(BigNum.FromInt(ctxt.Ctxt.TimeoutDiagnosticsCount++)))); } } - else if (CommandLineOptions.Clo.RunDiagnosticsOnTimeout) + else if (ctxt.Options.RunDiagnosticsOnTimeout) { ctxt.Ctxt.TimeoutDiagnosticIDToAssertion[ctxt.Ctxt.TimeoutDiagnosticsCount] = new Tuple(ac, b.TransferCmd); @@ -134,8 +139,8 @@ internal static VCExpr Cmd(Block b, Cmd cmd, VCExpr N, VCContext ctxt) { var subsumption = Subsumption(ac); - if (subsumption == CommandLineOptions.SubsumptionOption.Always - || (subsumption == CommandLineOptions.SubsumptionOption.NotForQuantifiers && !(C is VCExprQuantifier))) + if (subsumption == CoreOptions.SubsumptionOption.Always + || (subsumption == CoreOptions.SubsumptionOption.NotForQuantifiers && !(C is VCExprQuantifier))) { // Translate ac.Expr again so that we create separate VC expressions for the two different // occurrences of the translation of ac.Expr. Pool-based quantifier instantiation assumes @@ -175,7 +180,7 @@ internal static VCExpr Cmd(Block b, Cmd cmd, VCExpr N, VCContext ctxt) { AssumeCmd ac = (AssumeCmd) cmd; - if (CommandLineOptions.Clo.StratifiedInlining > 0) + if (ctxt.Options.StratifiedInlining > 0) { // Label the assume if it is a procedure call NAryExpr naryExpr = ac.Expr as NAryExpr; @@ -236,16 +241,16 @@ private static VCExpr MaybeWrapWithOptimization(VCContext ctxt, VCExpressionGene return expr; } - public static CommandLineOptions.SubsumptionOption Subsumption(AssertCmd ac) + public static CoreOptions.SubsumptionOption Subsumption(AssertCmd ac) { Contract.Requires(ac != null); int n = QKeyValue.FindIntAttribute(ac.Attributes, "subsumption", -1); switch (n) { - case 0: return CommandLineOptions.SubsumptionOption.Never; - case 1: return CommandLineOptions.SubsumptionOption.NotForQuantifiers; - case 2: return CommandLineOptions.SubsumptionOption.Always; - default: return CommandLineOptions.Clo.UseSubsumption; + case 0: return CoreOptions.SubsumptionOption.Never; + case 1: return CoreOptions.SubsumptionOption.NotForQuantifiers; + case 2: return CoreOptions.SubsumptionOption.Always; + default: return CoreOptions.Clo.UseSubsumption; } } From dfbaab1abd700dd89ba27babd5492cc81b2e4869 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Sat, 26 Feb 2022 10:58:28 +0100 Subject: [PATCH 02/32] Remove all references to CoreOptions.Clo (#510) - Add a fix to `Source/VCGeneration/Checker.GoBackToIdle` for when a model view file is used. - ExecutionEngine takes a `VerificationResultCache` as an argument instead of storing it in a static variable, so consumers have control over the lifetime of the cache. Consumers who want to migrate cheaply should create their own static `VerificationResultCache`. - Remove all references to `CoreOptions.Clo` --- Source/AbsInt/IntervalDomain.cs | 10 +- Source/AbsInt/NativeLattice.cs | 49 ++--- Source/AbsInt/TrivialDomain.cs | 2 +- Source/BoogieDriver/BoogieDriver.cs | 9 +- Source/Concurrency/CivlCoreTypes.cs | 7 +- Source/Concurrency/CivlTypeChecker.cs | 10 +- Source/Concurrency/CivlUtil.cs | 22 +-- Source/Concurrency/CivlVCGeneration.cs | 2 +- .../Concurrency/InductiveSequentialization.cs | 19 +- .../LinearPermissionInstrumentation.cs | 4 +- Source/Concurrency/LinearTypeChecker.cs | 94 +++++----- Source/Concurrency/MoverCheck.cs | 4 +- Source/Concurrency/PendingAsyncChecker.cs | 7 +- .../Concurrency/RefinementInstrumentation.cs | 10 +- .../TransitionRelationComputation.cs | 11 +- .../YieldSufficiencyTypeChecker.cs | 2 +- Source/Concurrency/YieldingProcDuplicator.cs | 10 +- .../YieldingProcInstrumentation.cs | 2 +- Source/Core/Absy.cs | 177 ++++++++---------- Source/Core/AbsyCmd.cs | 67 +++---- Source/Core/AbsyExpr.cs | 5 +- Source/Core/AbsyType.cs | 2 +- Source/Core/CoreOptions.cs | 17 +- Source/Core/DeadVarElim.cs | 66 ++++--- Source/Core/Helpers.cs | 4 +- Source/Core/Inline.cs | 44 +++-- .../Core/InterProceduralReachabilityGraph.cs | 8 +- Source/Core/LambdaHelper.cs | 30 +-- Source/Core/MaxHolesLambdaLifter.cs | 13 +- Source/Core/Monomorphization.cs | 22 ++- Source/Core/PrintOptions.cs | 23 +++ Source/Core/ResolutionContext.cs | 9 +- Source/Core/TokenTextWriter.cs | 30 +-- Source/Core/VariableDependenceAnalyser.cs | 28 +-- Source/Core/Xml.cs | 9 +- Source/ExecutionEngine/CommandLineOptions.cs | 14 +- Source/ExecutionEngine/ExecutionEngine.cs | 53 +++--- .../VerificationResultCache.cs | 30 +-- .../Houdini/AnnotationDependenceAnalyser.cs | 8 +- Source/Houdini/Houdini.cs | 6 +- Source/Houdini/StagedHoudini.cs | 2 +- Source/Predication/SmartBlockPredicator.cs | 16 +- Source/Predication/UniformityAnalyser.cs | 10 +- Source/Provers/SMTLib/ProverContext.cs | 7 +- Source/Provers/SMTLib/ProverInterface.cs | 5 +- Source/Provers/SMTLib/ProverUtil.cs | 15 +- Source/Provers/SMTLib/SMTLibLineariser.cs | 2 +- .../SMTLib/SMTLibProcessTheoremProver.cs | 14 +- Source/Provers/SMTLib/SMTLibProverOptions.cs | 4 + Source/UnitTests/CoreTests/Duplicator.cs | 13 +- .../UnitTests/CoreTests/ExprImmutability.cs | 9 +- .../UnitTests/CoreTests/ExprTypeChecking.cs | 6 +- .../ExecutionEngineTests/CancellationTests.cs | 3 +- .../ExecutionEngineTests/GetProverLogs.cs | 3 +- Source/UnitTests/TestUtil/ProgramLoader.cs | 6 +- Source/VCExpr/TypeErasureArguments.cs | 18 +- Source/VCExpr/TypeErasurePremisses.cs | 19 +- Source/VCExpr/VCExprAST.cs | 2 +- Source/VCExpr/VCExprASTPrinter.cs | 11 +- Source/VCGeneration/Checker.cs | 8 +- Source/VCGeneration/ConditionGeneration.cs | 36 ++-- Source/VCGeneration/Prune/Prune.cs | 8 +- Source/VCGeneration/Split.cs | 28 +-- Source/VCGeneration/SplitAndVerifyWorker.cs | 4 +- Source/VCGeneration/StratifiedVC.cs | 2 +- Source/VCGeneration/VCGen.cs | 24 +-- Source/VCGeneration/Wlp.cs | 6 +- 67 files changed, 658 insertions(+), 562 deletions(-) create mode 100644 Source/Core/PrintOptions.cs diff --git a/Source/AbsInt/IntervalDomain.cs b/Source/AbsInt/IntervalDomain.cs index 53b670bc4..fe6276dda 100644 --- a/Source/AbsInt/IntervalDomain.cs +++ b/Source/AbsInt/IntervalDomain.cs @@ -14,7 +14,7 @@ abstract class E_Common : NativeLattice.Element class E_Bottom : E_Common { - public override Expr ToExpr() + public override Expr ToExpr(CoreOptions options) { return Expr.False; } @@ -33,12 +33,12 @@ public E(Node n) N = n; } - public override Expr ToExpr() + public override Expr ToExpr(CoreOptions options) { Expr expr = Expr.True; for (var n = N; n != null; n = n.Next) { - expr = BplAnd(expr, n.ToExpr()); + expr = BplAnd(expr, n.ToExpr(options)); } return expr; @@ -212,9 +212,9 @@ public static IEnumerable> Merge(Node a, Node b) } } - public Expr ToExpr() + public Expr ToExpr(CoreOptions options) { - if (!V.IsMutable && CoreOptions.Clo.InstrumentInfer != + if (!V.IsMutable && options.InstrumentInfer != CoreOptions.InstrumentationPlaces.Everywhere) { // omit invariants about readonly variables diff --git a/Source/AbsInt/NativeLattice.cs b/Source/AbsInt/NativeLattice.cs index eeb728428..3e977b049 100644 --- a/Source/AbsInt/NativeLattice.cs +++ b/Source/AbsInt/NativeLattice.cs @@ -17,7 +17,7 @@ public abstract class NativeLattice /// public abstract class Element { - public abstract Expr ToExpr(); + public abstract Expr ToExpr(CoreOptions options); } public abstract Element Top { get; } @@ -70,14 +70,21 @@ public virtual void Validate() public class NativeAbstractInterpretation { - public static void RunAbstractInterpretation(Program program) + private CoreOptions options; + + public NativeAbstractInterpretation(CoreOptions options) + { + this.options = options; + } + + public void RunAbstractInterpretation(Program program) { Contract.Requires(program != null); - Helpers.ExtraTraceInformation("Starting abstract interpretation"); + Helpers.ExtraTraceInformation(options, "Starting abstract interpretation"); DateTime start = new DateTime(); // to please compiler's definite assignment rules - if (CoreOptions.Clo.Trace) + if (options.Trace) { Console.WriteLine(); Console.WriteLine("Running abstract interpretation..."); @@ -87,11 +94,11 @@ public static void RunAbstractInterpretation(Program program) WidenPoints.Compute(program); NativeLattice lattice = null; - if (CoreOptions.Clo.Ai.J_Trivial) + if (options.Ai.J_Trivial) { lattice = new TrivialDomain(); } - else if (CoreOptions.Clo.Ai.J_Intervals) + else if (options.Ai.J_Intervals) { lattice = new NativeIntervalDomain(); } @@ -100,13 +107,13 @@ public static void RunAbstractInterpretation(Program program) { Dictionary procedureImplementations = ComputeProcImplMap(program); ComputeProgramInvariants(program, procedureImplementations, lattice); - if (CoreOptions.Clo.Ai.DebugStatistics) + if (options.Ai.DebugStatistics) { Console.Error.WriteLine(lattice.DebugStatistics); } } - if (CoreOptions.Clo.Trace) + if (options.Trace) { DateTime end = DateTime.UtcNow; TimeSpan elapsed = end - start; @@ -132,7 +139,7 @@ private static Dictionary ComputeProcImplMap(Progra /// /// Compute and apply the invariants for the program using the underlying abstract domain. /// - public static void ComputeProgramInvariants(Program program, + public void ComputeProgramInvariants(Program program, Dictionary procedureImplementations, NativeLattice lattice) { Contract.Requires(program != null); @@ -157,7 +164,7 @@ public static void ComputeProgramInvariants(Program program, foreach (var impl in procedureImplementations[proc]) { // add the precondition to the axioms - Substitution formalProcImplSubst = Substituter.SubstitutionFromDictionary(impl.GetImplFormalMap()); + Substitution formalProcImplSubst = Substituter.SubstitutionFromDictionary(impl.GetImplFormalMap(options)); var start = initialElement; foreach (Requires pre in proc.Requires) { @@ -173,14 +180,14 @@ public static void ComputeProgramInvariants(Program program, } } - public static void Analyze(Implementation impl, NativeLattice lattice, NativeLattice.Element start) + public void Analyze(Implementation impl, NativeLattice lattice, NativeLattice.Element start) { // We need to keep track of some information for each(some) block(s). To do that efficiently, // we number the implementation's blocks sequentially, and then we can use arrays to store // the additional information. var pre = new NativeLattice.Element[impl.Blocks .Count]; // set to null if we never compute a join/widen at this block - var post = CoreOptions.Clo.InstrumentInfer == CoreOptions.InstrumentationPlaces.Everywhere + var post = options.InstrumentInfer == CoreOptions.InstrumentationPlaces.Everywhere ? new NativeLattice.Element[impl.Blocks.Count] : null; var iterations = new int[impl.Blocks.Count]; @@ -223,7 +230,7 @@ public static void Analyze(Implementation impl, NativeLattice lattice, NativeLat // no change continue; } - else if (b.widenBlock && CoreOptions.Clo.Ai.StepsBeforeWidening <= iterations[id]) + else if (b.widenBlock && options.Ai.StepsBeforeWidening <= iterations[id]) { e = lattice.Widen(pre[id], e); pre[id] = e; @@ -261,7 +268,7 @@ public static void Analyze(Implementation impl, NativeLattice lattice, NativeLat Instrument(impl, pre, post); } - static void Instrument(Implementation impl, NativeLattice.Element[] pre, NativeLattice.Element[] post) + void Instrument(Implementation impl, NativeLattice.Element[] pre, NativeLattice.Element[] post) { Contract.Requires(impl != null); Contract.Requires(pre != null); @@ -269,14 +276,14 @@ static void Instrument(Implementation impl, NativeLattice.Element[] pre, NativeL foreach (var b in impl.Blocks) { var element = pre[b.aiId]; - if (element != null && (b.widenBlock || CoreOptions.Clo.InstrumentInfer == + if (element != null && (b.widenBlock || options.InstrumentInfer == CoreOptions.InstrumentationPlaces.Everywhere)) { List newCommands = new List(); - Expr inv = element.ToExpr(); + Expr inv = element.ToExpr(options); PredicateCmd cmd; var kv = new QKeyValue(Token.NoToken, "inferred", new List(), null); - if (CoreOptions.Clo.InstrumentWithAsserts) + if (options.InstrumentWithAsserts) { cmd = new AssertCmd(Token.NoToken, inv, kv); } @@ -289,9 +296,9 @@ static void Instrument(Implementation impl, NativeLattice.Element[] pre, NativeL newCommands.AddRange(b.Cmds); if (post != null && post[b.aiId] != null) { - inv = post[b.aiId].ToExpr(); + inv = post[b.aiId].ToExpr(options); kv = new QKeyValue(Token.NoToken, "inferred", new List(), null); - if (CoreOptions.Clo.InstrumentWithAsserts) + if (options.InstrumentWithAsserts) { cmd = new AssertCmd(Token.NoToken, inv, kv); } @@ -312,7 +319,7 @@ static void Instrument(Implementation impl, NativeLattice.Element[] pre, NativeL /// The abstract transition relation. /// 'cmd' is allowed to be a StateCmd. /// - static NativeLattice.Element Step(NativeLattice lattice, Cmd cmd, NativeLattice.Element elmt) + NativeLattice.Element Step(NativeLattice lattice, Cmd cmd, NativeLattice.Element elmt) { Contract.Requires(lattice != null); Contract.Requires(cmd != null); @@ -364,7 +371,7 @@ static NativeLattice.Element Step(NativeLattice lattice, Cmd cmd, NativeLattice. else if (cmd is SugaredCmd) { var c = (SugaredCmd) cmd; - elmt = Step(lattice, c.Desugaring, elmt); + elmt = Step(lattice, c.GetDesugaring(options), elmt); } else if (cmd is CommentCmd) { diff --git a/Source/AbsInt/TrivialDomain.cs b/Source/AbsInt/TrivialDomain.cs index f01bca2ac..a2cd14153 100644 --- a/Source/AbsInt/TrivialDomain.cs +++ b/Source/AbsInt/TrivialDomain.cs @@ -11,7 +11,7 @@ public E(bool isTop) IsTop = isTop; } - public override Expr ToExpr() + public override Expr ToExpr(CoreOptions options) { return Expr.Literal(IsTop); } diff --git a/Source/BoogieDriver/BoogieDriver.cs b/Source/BoogieDriver/BoogieDriver.cs index 802478285..1f88cd2be 100644 --- a/Source/BoogieDriver/BoogieDriver.cs +++ b/Source/BoogieDriver/BoogieDriver.cs @@ -16,13 +16,12 @@ public static int Main(string[] args) RunningBoogieFromCommandLine = true }; ExecutionEngine.printer = new ConsolePrinter(options); - CommandLineOptions.Install(options); if (!options.Parse(args)) { return 1; } - using var executionEngine = new ExecutionEngine(options); + using var executionEngine = ExecutionEngine.CreateWithoutSharedCache(options); if (options.ProcessInfoFlags()) { @@ -63,13 +62,13 @@ public static int Main(string[] args) Console.WriteLine("--------------------"); } - Helpers.ExtraTraceInformation("Becoming sentient"); + Helpers.ExtraTraceInformation(options, "Becoming sentient"); var success = executionEngine.ProcessFiles(fileList); - if (CoreOptions.Clo.XmlSink != null) + if (options.XmlSink != null) { - CoreOptions.Clo.XmlSink.Close(); + options.XmlSink.Close(); } if (options.Wait) diff --git a/Source/Concurrency/CivlCoreTypes.cs b/Source/Concurrency/CivlCoreTypes.cs index 9de59fb75..0bd7cf149 100644 --- a/Source/Concurrency/CivlCoreTypes.cs +++ b/Source/Concurrency/CivlCoreTypes.cs @@ -137,6 +137,7 @@ public IntroductionAction(Procedure proc, Implementation impl, LayerRange layerR public class AtomicAction : Action { + private ConcurrencyOptions options; public MoverType moverType; public AtomicAction refinedAction; @@ -151,10 +152,12 @@ public class AtomicAction : Action public Dictionary triggerFunctions; - public AtomicAction(Procedure proc, Implementation impl, LayerRange layerRange, MoverType moverType) : + public AtomicAction(Procedure proc, Implementation impl, LayerRange layerRange, + MoverType moverType, ConcurrencyOptions options) : base(proc, impl, layerRange) { this.moverType = moverType; + this.options = options; AtomicActionDuplicator.SetupCopy(this, ref firstGate, ref firstImpl, "first_"); AtomicActionDuplicator.SetupCopy(this, ref secondGate, ref secondImpl, "second_"); DeclareTriggerFunctions(); @@ -201,7 +204,7 @@ public void AddTriggerAssumes(Program program) { var f = triggerFunctions[v]; program.AddTopLevelDeclaration(f); - var assume = CmdHelper.AssumeCmd(ExprHelper.FunctionCall(f, Expr.Ident(v))); + var assume = CmdHelper.AssumeCmd(ExprHelper.FunctionCall(options, f, Expr.Ident(v))); impl.Blocks[0].Cmds.Insert(0, assume); } } diff --git a/Source/Concurrency/CivlTypeChecker.cs b/Source/Concurrency/CivlTypeChecker.cs index 009cee158..be050bcc8 100644 --- a/Source/Concurrency/CivlTypeChecker.cs +++ b/Source/Concurrency/CivlTypeChecker.cs @@ -98,7 +98,7 @@ public CivlTypeChecker(ConcurrencyOptions options, Program program) new List(), new List(), new List {BlockHelper.Block("init", new List())}); - SkipAtomicAction = new AtomicAction(skipProcedure, skipImplementation, LayerRange.MinMax, MoverType.Both); + SkipAtomicAction = new AtomicAction(skipProcedure, skipImplementation, LayerRange.MinMax, MoverType.Both, Options); } public string AddNamePrefix(string name) @@ -278,7 +278,7 @@ private void TypeCheckActionDecls() } Implementation impl = actionImpls[0]; - impl.PruneUnreachableBlocks(); + impl.PruneUnreachableBlocks(Options); Graph cfg = Program.GraphFromImpl(impl); if (!Graph.Acyclic(cfg, impl.Blocks[0])) { @@ -325,7 +325,7 @@ private void TypeCheckActionDecls() } else { - var action = new AtomicAction(proc, impl, layerRange, GetActionMoverType(proc)); + var action = new AtomicAction(proc, impl, layerRange, GetActionMoverType(proc), Options); if (proc.HasAttribute(CivlAttributes.IS_INVARIANT)) { procToIsInvariant[proc] = action; @@ -1512,6 +1512,8 @@ class YieldInvariantCallChecker : ReadOnlyVisitor static List EnsuresAvailable = new List { LinearKind.LINEAR, LinearKind.LINEAR_OUT }; static List PreservesAvailable = new List { LinearKind.LINEAR }; + private ConcurrencyOptions Options => civlTypeChecker.Options; + public static CallCmd CheckRequires(CivlTypeChecker civlTypeChecker, QKeyValue attr, Procedure caller) { var v = new YieldInvariantCallChecker(civlTypeChecker, attr, caller, RequiresAvailable); @@ -1617,7 +1619,7 @@ private void ParseAttribute() callCmd = new CallCmd(attr.tok, yieldingProc.Name, exprs, new List()) { Proc = yieldingProc }; - if (CivlUtil.ResolveAndTypecheck(callCmd) != 0) + if (CivlUtil.ResolveAndTypecheck(Options, callCmd) != 0) { callCmd = null; } diff --git a/Source/Concurrency/CivlUtil.cs b/Source/Concurrency/CivlUtil.cs index 05decc802..522723a25 100644 --- a/Source/Concurrency/CivlUtil.cs +++ b/Source/Concurrency/CivlUtil.cs @@ -11,23 +11,23 @@ public static void AddInlineAttribute(Declaration decl) decl.AddAttribute("inline", Expr.Literal(1)); } - public static int ResolveAndTypecheck(Absy absy) + public static int ResolveAndTypecheck(CoreOptions options, Absy absy) { - var rc = new ResolutionContext(null); + var rc = new ResolutionContext(null, options); absy.Resolve(rc); if (rc.ErrorCount != 0) { return rc.ErrorCount; } - var tc = new TypecheckingContext(null); + var tc = new TypecheckingContext(null, options); absy.Typecheck(tc); return tc.ErrorCount; } - public static int ResolveAndTypecheck(Absy absy, ResolutionContext.State state) + public static int ResolveAndTypecheck(CoreOptions options, Absy absy, ResolutionContext.State state) { - var rc = new ResolutionContext(null); + var rc = new ResolutionContext(null, options); rc.StateMode = state; absy.Resolve(rc); if (rc.ErrorCount != 0) @@ -35,17 +35,17 @@ public static int ResolveAndTypecheck(Absy absy, ResolutionContext.State state) return rc.ErrorCount; } - var tc = new TypecheckingContext(null); + var tc = new TypecheckingContext(null, options); absy.Typecheck(tc); return tc.ErrorCount; } - public static int ResolveAndTypecheck(IEnumerable absys) + public static int ResolveAndTypecheck(CoreOptions options, IEnumerable absys) { int errorCount = 0; foreach (var absy in absys) { - errorCount += ResolveAndTypecheck(absy); + errorCount += ResolveAndTypecheck(options, absy); } return errorCount; @@ -55,13 +55,13 @@ public static int ResolveAndTypecheck(IEnumerable absys) // Handy syntactic sugar missing in Expr public static class ExprHelper { - public static NAryExpr FunctionCall(Function f, params Expr[] args) + public static NAryExpr FunctionCall(ConcurrencyOptions options, Function f, params Expr[] args) { var expr = new NAryExpr(Token.NoToken, new FunctionCall(f), args); - var rc = new ResolutionContext(null); + var rc = new ResolutionContext(null, options); rc.StateMode = ResolutionContext.State.Two; expr.Resolve(rc); - expr.Typecheck(new TypecheckingContext(null)); + expr.Typecheck(new TypecheckingContext(null, options)); return expr; } diff --git a/Source/Concurrency/CivlVCGeneration.cs b/Source/Concurrency/CivlVCGeneration.cs index f4b0c6fae..57aef209f 100644 --- a/Source/Concurrency/CivlVCGeneration.cs +++ b/Source/Concurrency/CivlVCGeneration.cs @@ -28,7 +28,7 @@ public static void Transform(ConcurrencyOptions options, CivlTypeChecker civlTyp YieldingProcChecker.AddCheckers(civlTypeChecker, decls); // Linear type checks - LinearTypeChecker.AddCheckers(civlTypeChecker, decls); + civlTypeChecker.linearTypeChecker.AddCheckers(decls); if (!options.TrustInductiveSequentialization) { diff --git a/Source/Concurrency/InductiveSequentialization.cs b/Source/Concurrency/InductiveSequentialization.cs index cf9f90bcc..aebe54114 100644 --- a/Source/Concurrency/InductiveSequentialization.cs +++ b/Source/Concurrency/InductiveSequentialization.cs @@ -18,6 +18,8 @@ public class InductiveSequentialization private IdentifierExpr newPAs; private string checkName; + private ConcurrencyOptions Options => civlTypeChecker.Options; + public InductiveSequentialization(CivlTypeChecker civlTypeChecker, AtomicAction inputAction, AtomicAction outputAction, AtomicAction invariantAction, Dictionary elim) { @@ -110,7 +112,7 @@ public Tuple GenerateStepChecker(AtomicAction pending List cmds = new List(); cmds.Add(GetCallCmd(invariantAction)); - cmds.Add(CmdHelper.AssumeCmd(ExprHelper.FunctionCall(pendingAsync.pendingAsyncCtor.membership, choice))); + cmds.Add(CmdHelper.AssumeCmd(ExprHelper.FunctionCall(Options, pendingAsync.pendingAsyncCtor.membership, choice))); cmds.Add(CmdHelper.AssumeCmd(Expr.Gt(Expr.Select(PAs, choice), Expr.Literal(0)))); cmds.Add(RemoveChoice); @@ -119,7 +121,7 @@ public Tuple GenerateStepChecker(AtomicAction pending List inputExprs = new List(); for (int i = 0; i < abs.impl.InParams.Count; i++) { - var pendingAsyncParam = ExprHelper.FunctionCall(pendingAsync.pendingAsyncCtor.selectors[i], choice); + var pendingAsyncParam = ExprHelper.FunctionCall(Options, pendingAsync.pendingAsyncCtor.selectors[i], choice); map[abs.impl.InParams[i]] = pendingAsyncParam; inputExprs.Add(pendingAsyncParam); } @@ -154,7 +156,7 @@ private CallCmd GetCallCmd(AtomicAction callee) private AssertCmd GetCheck(Expr expr) { - expr.Typecheck(new TypecheckingContext(null)); + expr.Typecheck(new TypecheckingContext(null, civlTypeChecker.Options)); return CmdHelper.AssertCmd( inputAction.proc.tok, expr, @@ -212,7 +214,7 @@ private Expr NoPendingAsyncs var paBound = civlTypeChecker.BoundVariable("pa", civlTypeChecker.pendingAsyncType); var pa = Expr.Ident(paBound); var expr = Expr.Eq(Expr.Select(PAs, pa), Expr.Literal(0)); - expr.Typecheck(new TypecheckingContext(null)); + expr.Typecheck(new TypecheckingContext(null, civlTypeChecker.Options)); return ExprHelper.ForallExpr(new List { paBound }, expr); } } @@ -225,8 +227,9 @@ private Expr PendingAsyncsEliminatedExpr var pa = Expr.Ident(paBound); var expr = Expr.Imp( Expr.Gt(Expr.Select(PAs, pa), Expr.Literal(0)), - Expr.And(elim.Keys.Select(a => Expr.Not(ExprHelper.FunctionCall(a.pendingAsyncCtor.membership, pa))))); - expr.Typecheck(new TypecheckingContext(null)); + Expr.And(elim.Keys.Select(a => + Expr.Not(ExprHelper.FunctionCall(civlTypeChecker.Options, a.pendingAsyncCtor.membership, pa))))); + expr.Typecheck(new TypecheckingContext(null, civlTypeChecker.Options)); return ExprHelper.ForallExpr(new List { paBound }, expr); } } @@ -244,7 +247,7 @@ private Expr ExistsElimPendingAsyncExpr private Expr ElimPendingAsyncExpr(IdentifierExpr pa) { return Expr.And( - Expr.Or(elim.Keys.Select(a => ExprHelper.FunctionCall(a.pendingAsyncCtor.membership, pa))), + Expr.Or(elim.Keys.Select(a => ExprHelper.FunctionCall(Options, a.pendingAsyncCtor.membership, pa))), Expr.Gt(Expr.Select(PAs, pa), Expr.Literal(0)) ); } @@ -262,7 +265,7 @@ private AssignCmd AddNewPAs(Function pendingAsyncAdd) { return AssignCmd.SimpleAssign(Token.NoToken, PAs, - ExprHelper.FunctionCall(pendingAsyncAdd, PAs, newPAs)); + ExprHelper.FunctionCall(Options, pendingAsyncAdd, PAs, newPAs)); } diff --git a/Source/Concurrency/LinearPermissionInstrumentation.cs b/Source/Concurrency/LinearPermissionInstrumentation.cs index a504eeb82..00f34bba6 100644 --- a/Source/Concurrency/LinearPermissionInstrumentation.cs +++ b/Source/Concurrency/LinearPermissionInstrumentation.cs @@ -12,6 +12,8 @@ public class LinearPermissionInstrumentation private Dictionary domainNameToHoleVar; private Dictionary localVarMap; + private ConcurrencyOptions Options => civlTypeChecker.Options; + public LinearPermissionInstrumentation( CivlTypeChecker civlTypeChecker, int layerNum, @@ -104,7 +106,7 @@ public void AddDisjointnessAssumptions(Implementation impl, HashSet y } // loop headers - impl.PruneUnreachableBlocks(); + impl.PruneUnreachableBlocks(Options); impl.ComputePredecessorsForBlocks(); var graph = Program.GraphFromImpl(impl); graph.ComputeLoops(); diff --git a/Source/Concurrency/LinearTypeChecker.cs b/Source/Concurrency/LinearTypeChecker.cs index de4f9bdb2..a7a61afb8 100644 --- a/Source/Concurrency/LinearTypeChecker.cs +++ b/Source/Concurrency/LinearTypeChecker.cs @@ -25,6 +25,7 @@ public LinearQualifier(string domainName, LinearKind kind) public class LinearDomain { + private ConcurrencyOptions options; public string domainName; public Type permissionType; public Dictionary collectors; @@ -39,11 +40,13 @@ public class LinearDomain public Function mapIteInt; public Function mapLe; - public LinearDomain(Program program, string domainName, Type permissionType, Dictionary collectors) + public LinearDomain(Program program, string domainName, Type permissionType, Dictionary collectors, + ConcurrencyOptions options) { this.domainName = domainName; this.permissionType = permissionType; this.collectors = collectors; + this.options = options; this.mapTypeBool = new MapType(Token.NoToken, new List(), new List {this.permissionType}, Type.Bool); @@ -70,12 +73,12 @@ public LinearDomain(Program program, string domainName, Type permissionType, Dic public Expr MapConstInt(int value) { - return ExprHelper.FunctionCall(mapConstInt, Expr.Literal(value)); + return ExprHelper.FunctionCall(options, mapConstInt, Expr.Literal(value)); } public Expr MapEqTrue(Expr expr) { - return Expr.Eq(expr, ExprHelper.FunctionCall(mapConstBool, Expr.True)); + return Expr.Eq(expr, ExprHelper.FunctionCall(options, mapConstBool, Expr.True)); } } @@ -241,7 +244,7 @@ public void TypeCheck() if (collectors.Count != 0) { this.linearDomains[domainName] = - new LinearDomain(program, domainName, permissionTypes[domainName], collectors); + new LinearDomain(program, domainName, permissionTypes[domainName], collectors, Options); } } foreach (Absy absy in this.availableLinearVars.Keys) @@ -381,7 +384,7 @@ public override Implementation VisitImplementation(Implementation node) return node; } - node.PruneUnreachableBlocks(); + node.PruneUnreachableBlocks(civlTypeChecker.Options); node.ComputePredecessorsForBlocks(); GraphUtil.Graph graph = Program.GraphFromImpl(node); graph.ComputeLoops(); @@ -944,13 +947,15 @@ public IEnumerable PermissionExprForEachVariable(string domainName, IEnume var domain = linearDomains[domainName]; foreach (Variable v in scope) { - Expr expr = ExprHelper.FunctionCall(domain.collectors[v.TypedIdent.Type], Expr.Ident(v)); - expr.Resolve(new ResolutionContext(null)); - expr.Typecheck(new TypecheckingContext(null)); + Expr expr = ExprHelper.FunctionCall(Options, domain.collectors[v.TypedIdent.Type], Expr.Ident(v)); + expr.Resolve(new ResolutionContext(null, Options)); + expr.Typecheck(new TypecheckingContext(null, Options)); yield return expr; } } + private ConcurrencyOptions Options => civlTypeChecker.Options; + public Expr DisjointnessExprForPermissions(string domainName, IEnumerable permissionsExprs) { Expr expr = Expr.True; @@ -969,31 +974,31 @@ public Expr DisjointnessExprForPermissions(string domainName, IEnumerable expr = ExprHelper.ExistsExpr(new List {partition}, Expr.And(subsetExprs)); } - expr.Resolve(new ResolutionContext(null)); - expr.Typecheck(new TypecheckingContext(null)); + expr.Resolve(new ResolutionContext(null, Options)); + expr.Typecheck(new TypecheckingContext(null, Options)); return expr; } public Expr UnionExprForPermissions(string domainName, IEnumerable permissionExprs) { var domain = linearDomains[domainName]; - var expr = ExprHelper.FunctionCall(domain.mapConstBool, Expr.False); + var expr = ExprHelper.FunctionCall(Options, domain.mapConstBool, Expr.False); foreach (Expr e in permissionExprs) { - expr = ExprHelper.FunctionCall(domain.mapOr, e, expr); + expr = ExprHelper.FunctionCall(Options, domain.mapOr, e, expr); } - expr.Resolve(new ResolutionContext(null)); - expr.Typecheck(new TypecheckingContext(null)); + expr.Resolve(new ResolutionContext(null, Options)); + expr.Typecheck(new TypecheckingContext(null, Options)); return expr; } private Expr SubsetExpr(LinearDomain domain, Expr ie, Variable partition, int partitionCount) { - Expr e = ExprHelper.FunctionCall(domain.mapConstInt, Expr.Literal(partitionCount)); - e = ExprHelper.FunctionCall(domain.mapEqInt, Expr.Ident(partition), e); - e = ExprHelper.FunctionCall(domain.mapImp, ie, e); - e = Expr.Eq(e, ExprHelper.FunctionCall(domain.mapConstBool, Expr.True)); + Expr e = ExprHelper.FunctionCall(Options, domain.mapConstInt, Expr.Literal(partitionCount)); + e = ExprHelper.FunctionCall(Options, domain.mapEqInt, Expr.Ident(partition), e); + e = ExprHelper.FunctionCall(Options, domain.mapImp, ie, e); + e = Expr.Eq(e, ExprHelper.FunctionCall(Options, domain.mapConstBool, Expr.True)); return e; } @@ -1001,12 +1006,12 @@ private Expr SubsetExpr(LinearDomain domain, Expr ie, Variable partition, int pa #region Linearity Invariant Checker - public static void AddCheckers(CivlTypeChecker civlTypeChecker, List decls) + public void AddCheckers(List decls) { foreach (var action in Enumerable.Concat(civlTypeChecker.procToAtomicAction.Values, civlTypeChecker.procToIntroductionAction.Values)) { - AddChecker(civlTypeChecker, action, decls); + AddChecker(action, decls); } } @@ -1031,9 +1036,8 @@ public LinearityCheck(string domainName, Expr assume, Expr assert, string messag } } - private static void AddChecker(CivlTypeChecker civlTypeChecker, Action action, List decls) + private void AddChecker(Action action, List decls) { - var linearTypeChecker = civlTypeChecker.linearTypeChecker; // Note: The implementation should be used as the variables in the // gate are bound to implementation and not to the procedure. Implementation impl = action.impl; @@ -1055,19 +1059,19 @@ private static void AddChecker(CivlTypeChecker civlTypeChecker, Action action, L List requires = action.gate.Select(a => new Requires(false, a.Expr)).ToList(); List linearityChecks = new List(); - foreach (var domain in linearTypeChecker.linearDomains.Values) + foreach (var domain in linearDomains.Values) { // Linear in vars var inVars = inputs.Union(action.modifiedGlobalVars) - .Where(x => linearTypeChecker.FindDomainName(x) == domain.domainName) - .Where(x => InKinds.Contains(linearTypeChecker.FindLinearKind(x))) + .Where(x => FindDomainName(x) == domain.domainName) + .Where(x => InKinds.Contains(FindLinearKind(x))) .Select(Expr.Ident) .ToList(); // Linear out vars var outVars = inputs.Union(outputs).Union(action.modifiedGlobalVars) - .Where(x => linearTypeChecker.FindDomainName(x) == domain.domainName) - .Where(x => OutKinds.Contains(linearTypeChecker.FindLinearKind(x))) + .Where(x => FindDomainName(x) == domain.domainName) + .Where(x => OutKinds.Contains(FindLinearKind(x))) .Select(Expr.Ident) .ToList(); @@ -1089,7 +1093,7 @@ private static void AddChecker(CivlTypeChecker civlTypeChecker, Action action, L foreach (var pendingAsync in atomicAction.pendingAsyncs) { - var pendingAsyncLinearParams = PendingAsyncLinearParams(linearTypeChecker, domain, pendingAsync, pa1); + var pendingAsyncLinearParams = PendingAsyncLinearParams(domain, pendingAsync, pa1); if (pendingAsyncLinearParams.Count == 0) { @@ -1100,7 +1104,7 @@ private static void AddChecker(CivlTypeChecker civlTypeChecker, Action action, L // Permissions in linear output variables + linear inputs of a single pending async // are a subset of permissions in linear input variables. var exactlyOnePA = Expr.And( - ExprHelper.FunctionCall(pendingAsync.pendingAsyncCtor.membership, pa1), + ExprHelper.FunctionCall(Options, pendingAsync.pendingAsyncCtor.membership, pa1), Expr.Eq(Expr.Select(PAs, pa1), Expr.Literal(1))); var outSubsetInExpr = OutPermsSubsetInPerms(domain, inVars, pendingAsyncLinearParams.Union(outVars)); linearityChecks.Add(new LinearityCheck( @@ -1113,7 +1117,7 @@ private static void AddChecker(CivlTypeChecker civlTypeChecker, Action action, L // Third kind // If there are two identical pending asyncs, then their input permissions mut be empty. var twoIdenticalPAs = Expr.And( - ExprHelper.FunctionCall(pendingAsync.pendingAsyncCtor.membership, pa1), + ExprHelper.FunctionCall(Options, pendingAsync.pendingAsyncCtor.membership, pa1), Expr.Ge(Expr.Select(PAs, pa1), Expr.Literal(2))); var emptyPerms = OutPermsSubsetInPerms(domain, Enumerable.Empty(), pendingAsyncLinearParams); linearityChecks.Add(new LinearityCheck( @@ -1132,8 +1136,8 @@ private static void AddChecker(CivlTypeChecker civlTypeChecker, Action action, L { var pendingAsync2 = pendingAsyncs[j]; - var pendingAsyncLinearParams1 = PendingAsyncLinearParams(linearTypeChecker, domain, pendingAsync1, pa1); - var pendingAsyncLinearParams2 = PendingAsyncLinearParams(linearTypeChecker, domain, pendingAsync2, pa2); + var pendingAsyncLinearParams1 = PendingAsyncLinearParams(domain, pendingAsync1, pa1); + var pendingAsyncLinearParams2 = PendingAsyncLinearParams(domain, pendingAsync2, pa2); if (pendingAsyncLinearParams1.Count == 0 || pendingAsyncLinearParams2.Count == 0) { @@ -1146,8 +1150,8 @@ private static void AddChecker(CivlTypeChecker civlTypeChecker, Action action, L var membership = Expr.And( Expr.Neq(pa1, pa2), Expr.And( - ExprHelper.FunctionCall(pendingAsync1.pendingAsyncCtor.membership, pa1), - ExprHelper.FunctionCall(pendingAsync2.pendingAsyncCtor.membership, pa2))); + ExprHelper.FunctionCall(Options, pendingAsync1.pendingAsyncCtor.membership, pa1), + ExprHelper.FunctionCall(Options, pendingAsync2.pendingAsyncCtor.membership, pa2))); var existing = Expr.And( Expr.Ge(Expr.Select(PAs, pa1), Expr.Literal(1)), @@ -1182,7 +1186,7 @@ private static void AddChecker(CivlTypeChecker civlTypeChecker, Action action, L } cmds.Add(CmdHelper.AssertCmd(action.proc.tok, lc.assert, lc.message)); var block = BlockHelper.Block($"{lc.domainName}_{lc.checkName}", cmds); - CivlUtil.ResolveAndTypecheck(block, ResolutionContext.State.Two); + CivlUtil.ResolveAndTypecheck(Options, block, ResolutionContext.State.Two); checkerBlocks.Add(block); } @@ -1205,16 +1209,16 @@ private static void AddChecker(CivlTypeChecker civlTypeChecker, Action action, L decls.Add(linCheckerProc); } - private static List PendingAsyncLinearParams(LinearTypeChecker linearTypeChecker, LinearDomain domain, AtomicAction pendingAsync, IdentifierExpr pa) + private List PendingAsyncLinearParams(LinearDomain domain, AtomicAction pendingAsync, IdentifierExpr pa) { var pendingAsyncLinearParams = new List(); for (int i = 0; i < pendingAsync.proc.InParams.Count; i++) { var inParam = pendingAsync.proc.InParams[i]; - if (linearTypeChecker.FindDomainName(inParam) == domain.domainName && InKinds.Contains(linearTypeChecker.FindLinearKind(inParam))) + if (FindDomainName(inParam) == domain.domainName && InKinds.Contains(FindLinearKind(inParam))) { - var pendingAsyncParam = ExprHelper.FunctionCall(pendingAsync.pendingAsyncCtor.selectors[i], pa); + var pendingAsyncParam = ExprHelper.FunctionCall(Options, pendingAsync.pendingAsyncCtor.selectors[i], pa); pendingAsyncLinearParams.Add(pendingAsyncParam); } } @@ -1222,19 +1226,19 @@ private static List PendingAsyncLinearParams(LinearTypeChecker linearTypeC return pendingAsyncLinearParams; } - private static Expr OutPermsSubsetInPerms(LinearDomain domain, IEnumerable ins, IEnumerable outs) + private Expr OutPermsSubsetInPerms(LinearDomain domain, IEnumerable ins, IEnumerable outs) { Expr inMultiset = ExprHelper.Old(PermissionMultiset(domain, ins)); Expr outMultiset = PermissionMultiset(domain, outs); - Expr subsetExpr = ExprHelper.FunctionCall(domain.mapLe, outMultiset, inMultiset); - return Expr.Eq(subsetExpr, ExprHelper.FunctionCall(domain.mapConstBool, Expr.True)); + Expr subsetExpr = ExprHelper.FunctionCall(Options, domain.mapLe, outMultiset, inMultiset); + return Expr.Eq(subsetExpr, ExprHelper.FunctionCall(Options, domain.mapConstBool, Expr.True)); } - private static Expr PermissionMultiset(LinearDomain domain, IEnumerable exprs) + private Expr PermissionMultiset(LinearDomain domain, IEnumerable exprs) { var terms = exprs.Select(x => - ExprHelper.FunctionCall(domain.mapIteInt, - ExprHelper.FunctionCall(domain.collectors[x.Type], x), + ExprHelper.FunctionCall(Options, domain.mapIteInt, + ExprHelper.FunctionCall(Options, domain.collectors[x.Type], x), domain.MapConstInt(1), domain.MapConstInt(0))).ToList(); @@ -1243,7 +1247,7 @@ private static Expr PermissionMultiset(LinearDomain domain, IEnumerable ex return domain.MapConstInt(0); } - return terms.Aggregate((x, y) => ExprHelper.FunctionCall(domain.mapAdd, x, y)); + return terms.Aggregate((x, y) => ExprHelper.FunctionCall(Options, domain.mapAdd, x, y)); } #endregion diff --git a/Source/Concurrency/MoverCheck.cs b/Source/Concurrency/MoverCheck.cs index c019ae2e9..39eafa8a9 100644 --- a/Source/Concurrency/MoverCheck.cs +++ b/Source/Concurrency/MoverCheck.cs @@ -22,6 +22,8 @@ private MoverCheck(CivlTypeChecker civlTypeChecker, List decls) this.failurePreservationCheckerCache = new HashSet>(); } + private ConcurrencyOptions Options => civlTypeChecker.Options; + public static void AddCheckers(CivlTypeChecker civlTypeChecker, List decls) { MoverCheck moverChecking = new MoverCheck(civlTypeChecker, decls); @@ -176,7 +178,7 @@ private void CreateCommutativityChecker(AtomicAction first, AtomicAction second) }; foreach (var lemma in civlTypeChecker.commutativityHints.GetLemmas(first, second)) { - cmds.Add(CmdHelper.AssumeCmd(ExprHelper.FunctionCall(lemma.function, lemma.args.ToArray()))); + cmds.Add(CmdHelper.AssumeCmd(ExprHelper.FunctionCall(Options, lemma.function, lemma.args.ToArray()))); } cmds.Add(commutativityCheck); diff --git a/Source/Concurrency/PendingAsyncChecker.cs b/Source/Concurrency/PendingAsyncChecker.cs index 1bd42ba37..89b4c358c 100644 --- a/Source/Concurrency/PendingAsyncChecker.cs +++ b/Source/Concurrency/PendingAsyncChecker.cs @@ -21,10 +21,11 @@ public static void AddCheckers(CivlTypeChecker civlTypeChecker) var correctTypeExpr = ExprHelper.ForallExpr(new List {paBound}, Expr.Imp( Expr.Gt(Expr.Select(PAs, pa), Expr.Literal(0)), - Expr.Or(action.pendingAsyncs.Select(a => ExprHelper.FunctionCall(a.pendingAsyncCtor.membership, pa))))); + Expr.Or(action.pendingAsyncs.Select(a => + ExprHelper.FunctionCall(civlTypeChecker.Options, a.pendingAsyncCtor.membership, pa))))); - CivlUtil.ResolveAndTypecheck(nonnegativeExpr); - CivlUtil.ResolveAndTypecheck(correctTypeExpr); + CivlUtil.ResolveAndTypecheck(civlTypeChecker.Options, nonnegativeExpr); + CivlUtil.ResolveAndTypecheck(civlTypeChecker.Options, correctTypeExpr); var cmds = new List { diff --git a/Source/Concurrency/RefinementInstrumentation.cs b/Source/Concurrency/RefinementInstrumentation.cs index 4a9817513..bb5388aad 100644 --- a/Source/Concurrency/RefinementInstrumentation.cs +++ b/Source/Concurrency/RefinementInstrumentation.cs @@ -170,14 +170,14 @@ public override List CreateAssertCmds() tok, Expr.Or(Expr.Ident(pc), Expr.Or(OldEqualityExprForGlobals(), transitionRelation)), $"A yield-to-yield fragment modifies layer-{layerNum + 1} state in a way that does not match the refined atomic action"); - CivlUtil.ResolveAndTypecheck(skipOrTransitionRelationAssertCmd); + CivlUtil.ResolveAndTypecheck(civlTypeChecker.Options, skipOrTransitionRelationAssertCmd); // assert pc ==> g_old == g && o_old == o; AssertCmd skipAssertCmd = CmdHelper.AssertCmd( tok, Expr.Imp(Expr.Ident(pc), Expr.And(OldEqualityExprForGlobals(), OldEqualityExprForOutputs())), $"A yield-to-yield fragment modifies layer-{layerNum + 1} state subsequent to a yield-to-yield fragment that already modified layer-{layerNum + 1} state"); - CivlUtil.ResolveAndTypecheck(skipAssertCmd); + CivlUtil.ResolveAndTypecheck(civlTypeChecker.Options, skipAssertCmd); return new List {skipOrTransitionRelationAssertCmd, skipAssertCmd}; } @@ -197,14 +197,14 @@ public override List CreateUnchangedAssertCmds() tok, Expr.And(this.oldGlobalMap.Select(kvPair => Expr.Eq(Expr.Ident(kvPair.Key), Expr.Ident(kvPair.Value)))), $"A yield-to-yield fragment illegally modifies layer-{layerNum + 1} globals"); - CivlUtil.ResolveAndTypecheck(globalsAssertCmd); + CivlUtil.ResolveAndTypecheck(civlTypeChecker.Options, globalsAssertCmd); // assert pc ==> o_old == o; AssertCmd outputsAssertCmd = CmdHelper.AssertCmd( tok, Expr.Imp(Expr.Ident(pc), OldEqualityExprForOutputs()), $"A yield-to-yield fragment illegally modifies layer-{layerNum + 1} outputs"); - CivlUtil.ResolveAndTypecheck(outputsAssertCmd); + CivlUtil.ResolveAndTypecheck(civlTypeChecker.Options, outputsAssertCmd); return new List {globalsAssertCmd, outputsAssertCmd}; } @@ -231,7 +231,7 @@ public override List CreateUpdatesToRefinementVars(bool isMarkedCall) cmds.Add(CmdHelper.AssignCmd(pcOkUpdateLHS, pcOkUpdateRHS)); } - CivlUtil.ResolveAndTypecheck(cmds); + CivlUtil.ResolveAndTypecheck(civlTypeChecker.Options, cmds); return cmds; } diff --git a/Source/Concurrency/TransitionRelationComputation.cs b/Source/Concurrency/TransitionRelationComputation.cs index 17f31caf4..1ea756d02 100644 --- a/Source/Concurrency/TransitionRelationComputation.cs +++ b/Source/Concurrency/TransitionRelationComputation.cs @@ -24,6 +24,7 @@ public class TransitionRelationComputation private List pathTranslations; private bool IsJoint => second != null; + private ConcurrencyOptions Options => civlTypeChecker.Options; private IEnumerable AllVariables => frame.Union(allInParams).Union(allOutParams).Union(allLocVars).Distinct(); @@ -89,8 +90,8 @@ private static Expr ComputeTransitionRelation( messagePrefix); trc.EnumeratePaths(); var transitionRelation = Expr.Or(trc.pathTranslations); - transitionRelation.Resolve(new ResolutionContext(null) {StateMode = ResolutionContext.State.Two}); - transitionRelation.Typecheck(new TypecheckingContext(null)); + transitionRelation.Resolve(new ResolutionContext(null, civlTypeChecker.Options) {StateMode = ResolutionContext.State.Two}); + transitionRelation.Typecheck(new TypecheckingContext(null, civlTypeChecker.Options)); return transitionRelation; } @@ -208,6 +209,8 @@ private class PathTranslation private IEnumerable IntermediateFrameWithWitnesses => trc.FrameWithWitnesses.Select(v => frameIntermediateCopy[v]); + private ConcurrencyOptions Options => trc.Options; + class Assignment { public Variable Var { get; set; } @@ -433,7 +436,7 @@ private void ComputeTransitionRelationExpr() if (v == varCopies[orig].First() && trc.triggers.ContainsKey(orig)) { var f = trc.triggers[orig]; - exprs.Add(ExprHelper.FunctionCall(f, Expr.Ident(existsVarMap[v]))); + exprs.Add(ExprHelper.FunctionCall(Options, f, Expr.Ident(existsVarMap[v]))); } } @@ -531,7 +534,7 @@ private void ComputeWitnessedTransitionRelationExprs() Enumerable.Zip(varToWitnesses.Keys, witnessSet, Tuple.Create)) { CommutativityWitness witness = pair.Item2; - witnessSubst[pair.Item1] = ExprHelper.FunctionCall( + witnessSubst[pair.Item1] = ExprHelper.FunctionCall(Options, witness.function, witness.args.ToArray() ); } diff --git a/Source/Concurrency/YieldSufficiencyTypeChecker.cs b/Source/Concurrency/YieldSufficiencyTypeChecker.cs index c0498639e..1ba1b2850 100644 --- a/Source/Concurrency/YieldSufficiencyTypeChecker.cs +++ b/Source/Concurrency/YieldSufficiencyTypeChecker.cs @@ -85,7 +85,7 @@ public static void TypeCheck(CivlTypeChecker civlTypeChecker) { var yieldingProc = civlTypeChecker.procToYieldingProc[impl.Proc]; - impl.PruneUnreachableBlocks(); + impl.PruneUnreachableBlocks(civlTypeChecker.Options); Graph implGraph = Program.GraphFromImpl(impl); implGraph.ComputeLoops(); diff --git a/Source/Concurrency/YieldingProcDuplicator.cs b/Source/Concurrency/YieldingProcDuplicator.cs index 333d48d35..cb294f923 100644 --- a/Source/Concurrency/YieldingProcDuplicator.cs +++ b/Source/Concurrency/YieldingProcDuplicator.cs @@ -24,6 +24,8 @@ public class YieldingProcDuplicator : Duplicator private Dictionary refinementCallCmds; // rewritten -> original private Dictionary refinementBlocks; // rewritten -> block + private ConcurrencyOptions Options => civlTypeChecker.Options; + public YieldingProcDuplicator(CivlTypeChecker civlTypeChecker, int layerNum) { this.civlTypeChecker = civlTypeChecker; @@ -196,7 +198,7 @@ public override Implementation VisitImplementation(Implementation impl) var pa = Expr.Ident(paBound); var expr = Expr.Eq(Expr.Select(Expr.Ident(CollectedPAs), pa), Expr.Literal(0)); var forallExpr = ExprHelper.ForallExpr(new List {paBound}, expr); - forallExpr.Typecheck(new TypecheckingContext(null)); + forallExpr.Typecheck(new TypecheckingContext(null, Options)); newImpl.Blocks.First().Cmds.Insert(0, CmdHelper.AssumeCmd(forallExpr)); if (!impl.LocVars.Contains(CollectedPAs)) @@ -533,7 +535,7 @@ private void CollectReturnedPendingAsyncs(CallCmd newCall) if (SummaryHasPendingAsyncParam) { - var collectedUnionReturned = ExprHelper.FunctionCall(civlTypeChecker.pendingAsyncAdd, + var collectedUnionReturned = ExprHelper.FunctionCall(Options, civlTypeChecker.pendingAsyncAdd, Expr.Ident(CollectedPAs), Expr.Ident(ReturnedPAs)); newCmdSeq.Add(CmdHelper.AssignCmd(CollectedPAs, collectedUnionReturned)); } @@ -545,7 +547,7 @@ private void CollectReturnedPendingAsyncs(CallCmd newCall) var pa = Expr.Ident(paBound); var expr = Expr.Eq(Expr.Select(Expr.Ident(ReturnedPAs), pa), Expr.Literal(0)); var forallExpr = ExprHelper.ForallExpr(new List {paBound}, expr); - forallExpr.Typecheck(new TypecheckingContext(null)); + forallExpr.Typecheck(new TypecheckingContext(null, civlTypeChecker.Options)); newCmdSeq.Add(CmdHelper.AssertCmd(newCall.tok, forallExpr, "Pending asyncs created by this call are not summarized")); } @@ -614,7 +616,7 @@ private void AddPendingAsync(CallCmd newCall, ActionProc actionProc) } } - var pa = ExprHelper.FunctionCall(paAction.pendingAsyncCtor, newIns); + var pa = ExprHelper.FunctionCall(Options, paAction.pendingAsyncCtor, newIns); var inc = Expr.Add(Expr.Select(Expr.Ident(CollectedPAs), pa), Expr.Literal(1)); var add = CmdHelper.AssignCmd(CollectedPAs, Expr.Store(Expr.Ident(CollectedPAs), pa, inc)); newCmdSeq.Add(add); diff --git a/Source/Concurrency/YieldingProcInstrumentation.cs b/Source/Concurrency/YieldingProcInstrumentation.cs index db801908d..c2da81f9a 100644 --- a/Source/Concurrency/YieldingProcInstrumentation.cs +++ b/Source/Concurrency/YieldingProcInstrumentation.cs @@ -402,7 +402,7 @@ private void ComputeYieldingLoops( { yieldingLoopHeaders = new HashSet(impl.Blocks.Where(IsYieldingLoopHeader)); - impl.PruneUnreachableBlocks(); + impl.PruneUnreachableBlocks(civlTypeChecker.Options); impl.ComputePredecessorsForBlocks(); var graph = Program.GraphFromImpl(impl); graph.ComputeLoops(); diff --git a/Source/Core/Absy.cs b/Source/Core/Absy.cs index 2f6c47a78..9a0230ed7 100644 --- a/Source/Core/Absy.cs +++ b/Source/Core/Absy.cs @@ -375,14 +375,14 @@ public void ProcessDatatypeConstructors(Errors errors) /// Returns the number of name resolution errors. /// /// - public int Resolve() + public int Resolve(CoreOptions options) { - return Resolve((IErrorSink) null); + return Resolve(options, null); } - public int Resolve(IErrorSink errorSink) + public int Resolve(CoreOptions options, IErrorSink errorSink) { - ResolutionContext rc = new ResolutionContext(errorSink); + ResolutionContext rc = new ResolutionContext(errorSink, options); Resolve(rc); return rc.ErrorCount; } @@ -390,7 +390,7 @@ public int Resolve(IErrorSink errorSink) public override void Resolve(ResolutionContext rc) { //Contract.Requires(rc != null); - Helpers.ExtraTraceInformation("Starting resolution"); + Helpers.ExtraTraceInformation(rc.Options, "Starting resolution"); foreach (var d in TopLevelDeclarations) { @@ -436,7 +436,7 @@ public override void Resolve(ResolutionContext rc) { int e = rc.ErrorCount; d.Resolve(rc); - if (CoreOptions.Clo.OverlookBoogieTypeErrors && rc.ErrorCount != e && d is Implementation) + if (rc.Options.OverlookBoogieTypeErrors && rc.ErrorCount != e && d is Implementation) { // ignore this implementation System.Console.WriteLine("Warning: Ignoring implementation {0} because of translation resolution errors", @@ -486,14 +486,14 @@ private void ResolveTypes(ResolutionContext rc) TypeSynonymDecl.ResolveTypeSynonyms(synonymDecls, rc); } - public int Typecheck() + public int Typecheck(CoreOptions options) { - return this.Typecheck((IErrorSink) null); + return this.Typecheck(options, (IErrorSink) null); } - public int Typecheck(IErrorSink errorSink) + public int Typecheck(CoreOptions options, IErrorSink errorSink) { - TypecheckingContext tc = new TypecheckingContext(errorSink); + TypecheckingContext tc = new TypecheckingContext(errorSink, options); Typecheck(tc); return tc.ErrorCount; } @@ -501,7 +501,7 @@ public int Typecheck(IErrorSink errorSink) public override void Typecheck(TypecheckingContext tc) { //Contract.Requires(tc != null); - Helpers.ExtraTraceInformation("Starting typechecking"); + Helpers.ExtraTraceInformation(tc.Options, "Starting typechecking"); int oldErrorCount = tc.ErrorCount; foreach (var d in TopLevelDeclarations) @@ -811,9 +811,9 @@ public void UnrollLoops(int n, bool uc) /// /// /// - private HashSet GetBreakBlocksOfLoop(Block header, Block backEdgeNode, Graph /*!*/ g) + private HashSet GetBreakBlocksOfLoop(CoreOptions options, Block header, Block backEdgeNode, Graph /*!*/ g) { - Contract.Assert(CoreOptions.Clo.DeterministicExtractLoops, + Contract.Assert(options.DeterministicExtractLoops, "Can only be called with /deterministicExtractLoops option"); var immSuccBlks = new HashSet(); var loopBlocks = g.NaturalLoops(header, backEdgeNode); @@ -840,9 +840,9 @@ private HashSet GetBreakBlocksOfLoop(Block header, Block backEdgeNode, Gr return immSuccBlks; } - private HashSet GetBlocksInAllNaturalLoops(Block header, Graph /*!*/ g) + private HashSet GetBlocksInAllNaturalLoops(CoreOptions options, Block header, Graph /*!*/ g) { - Contract.Assert(CoreOptions.Clo.DeterministicExtractLoops, + Contract.Assert(options.DeterministicExtractLoops, "Can only be called with /deterministicExtractLoops option"); var allBlocksInNaturalLoops = new HashSet(); foreach (Block /*!*/ source in g.BackEdgeNodes(header)) @@ -855,7 +855,7 @@ private HashSet GetBlocksInAllNaturalLoops(Block header, Graph /*!*/ g, + void CreateProceduresForLoops(CoreOptions options, Implementation impl, Graph /*!*/ g, List /*!*/ loopImpls, Dictionary> fullMap) { @@ -882,7 +882,7 @@ void CreateProceduresForLoops(Implementation impl, Graph /*!*/ g, AddToFullMap(fullMap, impl.Name, block.Label, block); } - bool detLoopExtract = CoreOptions.Clo.DeterministicExtractLoops; + bool detLoopExtract = options.DeterministicExtractLoops; Dictionary /*!*/> /*!*/ loopHeaderToInputs = new Dictionary /*!*/>(); @@ -922,7 +922,7 @@ void CreateProceduresForLoops(Implementation impl, Graph /*!*/ g, 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); + immSuccBlks = GetBreakBlocksOfLoop(options, header, b, g); } foreach (Block /*!*/ block in g.NaturalLoops(header, b).Union(immSuccBlks)) @@ -1116,7 +1116,7 @@ void CreateProceduresForLoops(Implementation impl, Graph /*!*/ g, Contract.Assert(auxGotoCmd != null && auxGotoCmd.labelNames != null && auxGotoCmd.labelTargets != null && auxGotoCmd.labelTargets.Count >= 1); //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); + var loopNodes = GetBlocksInAllNaturalLoops(options, header, g); //var loopNodes = g.NaturalLoops(header, source); foreach (var bl in auxGotoCmd.labelTargets) { if (g.Nodes.Contains(bl) && //newly created blocks are not present in NaturalLoop(header, xx, g) @@ -1342,7 +1342,7 @@ private void AddToFullMap(Dictionary> fullMap, fullMap[procName][blockName] = block; } - public static Graph BuildCallGraph(Program program) + public static Graph BuildCallGraph(CoreOptions options, Program program) { Graph callGraph = new Graph(); Dictionary> procToImpls = new Dictionary>(); @@ -1353,7 +1353,7 @@ public static Graph BuildCallGraph(Program program) foreach (var impl in program.Implementations) { - if (impl.SkipVerification) + if (impl.IsSkipVerification(options)) { continue; } @@ -1364,7 +1364,7 @@ public static Graph BuildCallGraph(Program program) foreach (var impl in program.Implementations) { - if (impl.SkipVerification) + if (impl.IsSkipVerification(options)) { continue; } @@ -1427,9 +1427,9 @@ public class IrreducibleLoopException : Exception { } - public Graph ProcessLoops(Implementation impl) + public Graph ProcessLoops(CoreOptions options, Implementation impl) { - impl.PruneUnreachableBlocks(); + impl.PruneUnreachableBlocks(options); impl.ComputePredecessorsForBlocks(); Graph g = GraphFromImpl(impl); g.ComputeLoops(); @@ -1440,13 +1440,14 @@ public Graph ProcessLoops(Implementation impl) throw new IrreducibleLoopException(); } - public Dictionary> ExtractLoops() + public Dictionary> ExtractLoops(CoreOptions options) { HashSet procsWithIrreducibleLoops = null; - return ExtractLoops(out procsWithIrreducibleLoops); + return ExtractLoops(options, out procsWithIrreducibleLoops); } - public Dictionary> ExtractLoops(out HashSet procsWithIrreducibleLoops) + public Dictionary> ExtractLoops(CoreOptions options, + out HashSet procsWithIrreducibleLoops) { procsWithIrreducibleLoops = new HashSet(); List /*!*/ @@ -1458,8 +1459,8 @@ public Dictionary> ExtractLoops(out HashSet g = ProcessLoops(impl); - CreateProceduresForLoops(impl, g, loopImpls, fullMap); + Graph g = ProcessLoops(options, impl); + CreateProceduresForLoops(options, impl, g, loopImpls, fullMap); } catch (IrreducibleLoopException) { @@ -1467,7 +1468,7 @@ public Dictionary> ExtractLoops(out HashSet> ExtractLoops(out HashSet original block @@ -1932,32 +1933,26 @@ public string /*!*/ Name } } - public uint TimeLimit + public uint GetTimeLimit(CoreOptions options) { - get - { - uint tl = CoreOptions.Clo.TimeLimit; - CheckUIntAttribute("timeLimit", ref tl); - if (tl < 0) - { - tl = CoreOptions.Clo.TimeLimit; - } - return tl; + uint tl = options.TimeLimit; + CheckUIntAttribute("timeLimit", ref tl); + if (tl < 0) { + tl = options.TimeLimit; } + + return tl; } - public uint ResourceLimit + public uint GetResourceLimit(CoreOptions options) { - get - { - uint rl = CoreOptions.Clo.ResourceLimit; - CheckUIntAttribute("rlimit", ref rl); - if (rl < 0) - { - rl = CoreOptions.Clo.ResourceLimit; - } - return rl; + uint rl = options.ResourceLimit; + CheckUIntAttribute("rlimit", ref rl); + if (rl < 0) { + rl = options.ResourceLimit; } + + return rl; } public int? RandomSeed @@ -2388,7 +2383,7 @@ public void EmitVitals(TokenTextWriter stream, int level, bool emitAttributes, b EmitAttributes(stream); } - if (CoreOptions.Clo.PrintWithUniqueASTIds && this.TypedIdent.HasName) + if (stream.Options.PrintWithUniqueASTIds && this.TypedIdent.HasName) { stream.Write("h{0}^^", this.GetHashCode()); // the idea is that this will prepend the name printed by TypedIdent.Emit @@ -3043,21 +3038,21 @@ public void AddFunctionDependency(Function function) functionDependencies.Add(function); } - public bool SignatureEquals(DeclWithFormals other) + public bool SignatureEquals(CoreOptions options, DeclWithFormals other) { Contract.Requires(other != null); string sig = null; string otherSig = null; using (var strWr = new System.IO.StringWriter()) - using (var tokTxtWr = new TokenTextWriter("", strWr, false, false)) + using (var tokTxtWr = new TokenTextWriter("", strWr, false, false, options)) { EmitSignature(tokTxtWr, this is Function); sig = strWr.ToString(); } using (var otherStrWr = new System.IO.StringWriter()) - using (var otherTokTxtWr = new TokenTextWriter("", otherStrWr, false, false)) + using (var otherTokTxtWr = new TokenTextWriter("", otherStrWr, false, false, options)) { EmitSignature(otherTokTxtWr, other is Function); otherSig = otherStrWr.ToString(); @@ -3399,7 +3394,7 @@ public override void Emit(TokenTextWriter stream, int level) stream.Write("{:define} "); } - if (CoreOptions.Clo.PrintWithUniqueASTIds) + if (stream.Options.PrintWithUniqueASTIds) { stream.Write("h{0}^^{1}", this.GetHashCode(), TokenTextWriter.SanitizeIdentifier(this.Name)); } @@ -4216,40 +4211,32 @@ public bool StronglyConnectedComponentsComputed get { return this.scc != null; } } - public bool SkipVerification + public bool IsSkipVerification(CoreOptions options) { - get - { - bool verify = true; - cce.NonNull(this.Proc).CheckBooleanAttribute("verify", ref verify); - this.CheckBooleanAttribute("verify", ref verify); - if (!verify) - { - return true; - } - - if (CoreOptions.Clo.ProcedureInlining == CoreOptions.Inlining.Assert || - CoreOptions.Clo.ProcedureInlining == CoreOptions.Inlining.Assume) - { - Expr inl = this.FindExprAttribute("inline"); - if (inl == null) - { - inl = this.Proc.FindExprAttribute("inline"); - } + bool verify = true; + cce.NonNull(this.Proc).CheckBooleanAttribute("verify", ref verify); + this.CheckBooleanAttribute("verify", ref verify); + if (!verify) { + return true; + } - if (inl != null) - { - return true; - } + if (options.ProcedureInlining == CoreOptions.Inlining.Assert || + options.ProcedureInlining == CoreOptions.Inlining.Assume) { + Expr inl = this.FindExprAttribute("inline"); + if (inl == null) { + inl = this.Proc.FindExprAttribute("inline"); } - if (CoreOptions.Clo.StratifiedInlining > 0) - { - return !QKeyValue.FindBoolAttribute(Attributes, "entrypoint"); + if (inl != null) { + return true; } + } - return false; + if (options.StratifiedInlining > 0) { + return !QKeyValue.FindBoolAttribute(Attributes, "entrypoint"); } + + return false; } public string Id @@ -4527,31 +4514,31 @@ public override void Emit(TokenTextWriter stream, int level) v.Emit(stream, level + 1); } - if (this.StructuredStmts != null && !CoreOptions.Clo.PrintInstrumented && - !CoreOptions.Clo.PrintInlined) + if (this.StructuredStmts != null && !stream.Options.PrintInstrumented && + !stream.Options.PrintInlined) { if (this.LocVars.Count > 0) { stream.WriteLine(); } - if (CoreOptions.Clo.PrintUnstructured < 2) + if (stream.Options.PrintUnstructured < 2) { - if (CoreOptions.Clo.PrintUnstructured == 1) + if (stream.Options.PrintUnstructured == 1) { stream.WriteLine(this, level + 1, "/*** structured program:"); } this.StructuredStmts.Emit(stream, level + 1); - if (CoreOptions.Clo.PrintUnstructured == 1) + if (stream.Options.PrintUnstructured == 1) { stream.WriteLine(level + 1, "**** end structured program */"); } } } - if (this.StructuredStmts == null || 1 <= CoreOptions.Clo.PrintUnstructured || - CoreOptions.Clo.PrintInstrumented || CoreOptions.Clo.PrintInlined) + if (this.StructuredStmts == null || 1 <= stream.Options.PrintUnstructured || + stream.Options.PrintInstrumented || stream.Options.PrintInlined) { foreach (Block b in this.Blocks) { @@ -4756,7 +4743,7 @@ public void ResetImplFormalMap() this.formalMap = null; } - public Dictionary /*!*/ GetImplFormalMap() + public Dictionary /*!*/ GetImplFormalMap(CoreOptions options) { Contract.Ensures(Contract.Result>() != null); @@ -4795,11 +4782,11 @@ public void ResetImplFormalMap() this.formalMap = map; - if (CoreOptions.Clo.PrintWithUniqueASTIds) + if (options.PrintWithUniqueASTIds) { Console.WriteLine("Implementation.GetImplFormalMap on {0}:", this.Name); using TokenTextWriter stream = - new TokenTextWriter("", Console.Out, /*setTokens=*/false, /*pretty=*/ false); + new TokenTextWriter("", Console.Out, /*setTokens=*/false, /*pretty=*/ false, options); foreach (var e in map) { Console.Write(" "); @@ -4956,7 +4943,7 @@ public void ComputePredecessorsForBlocks() this.BlockPredecessorsComputed = true; } - public void PruneUnreachableBlocks() + public void PruneUnreachableBlocks(CoreOptions options) { ArrayList /*Block!*/ visitNext = new ArrayList /*Block!*/(); @@ -4974,7 +4961,7 @@ public void PruneUnreachableBlocks() reachable.Add(b); if (b.TransferCmd is GotoCmd) { - if (CoreOptions.Clo.PruneInfeasibleEdges) + if (options.PruneInfeasibleEdges) { foreach (Cmd /*!*/ s in b.Cmds) { diff --git a/Source/Core/AbsyCmd.cs b/Source/Core/AbsyCmd.cs index 16a954526..3616ae578 100644 --- a/Source/Core/AbsyCmd.cs +++ b/Source/Core/AbsyCmd.cs @@ -103,7 +103,7 @@ public void Emit(TokenTextWriter stream, int level) if (!Anonymous) { stream.WriteLine(level, "{0}:", - CoreOptions.Clo.PrintWithUniqueASTIds + stream.Options.PrintWithUniqueASTIds ? String.Format("h{0}^^{1}", this.GetHashCode(), this.LabelName) : this.LabelName); } @@ -1331,7 +1331,7 @@ public void Emit(TokenTextWriter stream, int level) this, level, "{0}:{1}", - CoreOptions.Clo.PrintWithUniqueASTIds + stream.Options.PrintWithUniqueASTIds ? String.Format("h{0}^^{1}", this.GetHashCode(), this.Label) : this.Label, this.widenBlock ? " // cut point" : ""); @@ -1424,10 +1424,10 @@ public override void AddAssignedVariables(List vars) public static class ChecksumHelper { - public static void ComputeChecksums(Cmd cmd, Implementation impl, ISet usedVariables, + public static void ComputeChecksums(CoreOptions options, Cmd cmd, Implementation impl, ISet usedVariables, byte[] currentChecksum = null) { - if (CoreOptions.Clo.VerifySnapshots < 2) + if (options.VerifySnapshots < 2) { return; } @@ -1448,7 +1448,7 @@ public static void ComputeChecksums(Cmd cmd, Implementation impl, ISet } using (var strWr = new System.IO.StringWriter()) - using (var tokTxtWr = new TokenTextWriter("", strWr, false, false)) + using (var tokTxtWr = new TokenTextWriter("", strWr, false, false, options)) { tokTxtWr.UseForComputingChecksums = true; var havocCmd = cmd as HavocCmd; @@ -1498,12 +1498,12 @@ public static void ComputeChecksums(Cmd cmd, Implementation impl, ISet if (sugaredCmd != null) { // The checksum of a sugared command should not depend on the desugaring itself. - var stateCmd = sugaredCmd.Desugaring as StateCmd; + var stateCmd = sugaredCmd.GetDesugaring(options) as StateCmd; if (stateCmd != null) { foreach (var c in stateCmd.Cmds) { - ComputeChecksums(c, impl, usedVariables, currentChecksum); + ComputeChecksums(options, c, impl, usedVariables, currentChecksum); currentChecksum = c.Checksum; if (c.SugaredCmdChecksum == null) { @@ -1513,7 +1513,7 @@ public static void ComputeChecksums(Cmd cmd, Implementation impl, ISet } else { - ComputeChecksums(sugaredCmd.Desugaring, impl, usedVariables, currentChecksum); + ComputeChecksums(options, sugaredCmd.GetDesugaring(options), impl, usedVariables, currentChecksum); } } } @@ -1568,7 +1568,7 @@ public void CheckAssignments(TypecheckingContext tc) { tc.Error(this, "command assigns to an immutable variable: {0}", v.Name); } - else if (!CoreOptions.Clo.DoModSetAnalysis && v is GlobalVariable) + else if (!tc.Options.DoModSetAnalysis && v is GlobalVariable) { if (!tc.Yields && !tc.InFrame(v)) { @@ -1698,11 +1698,10 @@ public static void TypecheckAttributes(QKeyValue attributes, TypecheckingContext } [Pure] - public override string ToString() - { + public override string ToString() { Contract.Ensures(Contract.Result() != null); System.IO.StringWriter buffer = new System.IO.StringWriter(); - using TokenTextWriter stream = new TokenTextWriter("", buffer, /*setTokens=*/ false, /*pretty=*/ false); + using TokenTextWriter stream = new TokenTextWriter("", buffer, false, false, PrintOptions.Default); this.Emit(stream, 0); return buffer.ToString(); @@ -1730,7 +1729,7 @@ public override void Resolve(ResolutionContext rc) public override void Typecheck(TypecheckingContext tc) { - if (!CoreOptions.Clo.DoModSetAnalysis && !tc.Yields) + if (!tc.Options.DoModSetAnalysis && !tc.Yields) { tc.Error(this, "enclosing procedure of a yield command must yield"); } @@ -2490,19 +2489,15 @@ public SugaredCmd(IToken /*!*/ tok) Contract.Requires(tok != null); } - public Cmd /*!*/ Desugaring + public Cmd GetDesugaring(PrintOptions options) { - get - { - Contract.Ensures(Contract.Result() != null); - - if (desugaring == null) - { - desugaring = ComputeDesugaring(); - } + Contract.Ensures(Contract.Result() != null); - return desugaring; + if (desugaring == null) { + desugaring = ComputeDesugaring(options); } + + return desugaring; } /// @@ -2522,12 +2517,12 @@ public void VisitDesugaring(StandardVisitor visitor) } } - protected abstract Cmd /*!*/ ComputeDesugaring(); + protected abstract Cmd /*!*/ ComputeDesugaring(PrintOptions options); - public void ExtendDesugaring(IEnumerable before, IEnumerable beforePreconditionCheck, + public void ExtendDesugaring(CoreOptions options, IEnumerable before, IEnumerable beforePreconditionCheck, IEnumerable after) { - var desug = Desugaring; + var desug = GetDesugaring(options); var stCmd = desug as StateCmd; if (stCmd != null) { @@ -2553,10 +2548,10 @@ public void ExtendDesugaring(IEnumerable before, IEnumerable beforePre public override void Emit(TokenTextWriter stream, int level) { //Contract.Requires(stream != null); - if (CoreOptions.Clo.PrintDesugarings && !stream.UseForComputingChecksums) + if (stream.Options.PrintDesugarings && !stream.UseForComputingChecksums) { stream.WriteLine(this, level, "/*** desugaring:"); - Desugaring.Emit(stream, level); + GetDesugaring(stream.Options).Emit(stream, level); stream.WriteLine(level, "**** end desugaring */"); } } @@ -2569,7 +2564,7 @@ public SugaredCmdContracts() : base(null) { } - protected override Cmd ComputeDesugaring() + protected override Cmd ComputeDesugaring(PrintOptions options) { Contract.Ensures(Contract.Result() != null); @@ -2672,7 +2667,7 @@ public ParCallCmd(IToken tok, List callCmds, QKeyValue kv) this.CallCmds = callCmds; } - protected override Cmd ComputeDesugaring() + protected override Cmd ComputeDesugaring(PrintOptions options) { throw new NotImplementedException(); } @@ -2733,7 +2728,7 @@ public override void Resolve(ResolutionContext rc) public override void Typecheck(TypecheckingContext tc) { TypecheckAttributes(Attributes, tc); - if (!CoreOptions.Clo.DoModSetAnalysis) + if (!tc.Options.DoModSetAnalysis) { if (!tc.Yields) { @@ -3139,7 +3134,7 @@ public override void Typecheck(TypecheckingContext tc) TypeParameters = SimpleTypeParamInstantiation.From(Proc.TypeParameters, actualTypeParams); - if (!CoreOptions.Clo.DoModSetAnalysis && IsAsync) + if (!tc.Options.DoModSetAnalysis && IsAsync) { if (!tc.Yields) { @@ -3168,7 +3163,7 @@ public override void Typecheck(TypecheckingContext tc) return res; } - protected override Cmd ComputeDesugaring() + protected override Cmd ComputeDesugaring(PrintOptions options) { Contract.Ensures(Contract.Result() != null); @@ -3310,7 +3305,7 @@ protected override Cmd ComputeDesugaring() } } else if (req.CanAlwaysAssume() - || CoreOptions.Clo.StratifiedInlining > 0) + || options.StratifiedInlining > 0) { // inject free requires as assume statements at the call site AssumeCmd /*!*/ @@ -4184,7 +4179,7 @@ public override string ToString() { Contract.Ensures(Contract.Result() != null); System.IO.StringWriter buffer = new System.IO.StringWriter(); - using TokenTextWriter stream = new TokenTextWriter("", buffer, /*setTokens=*/ false, /*pretty=*/ false); + using TokenTextWriter stream = new TokenTextWriter("", buffer, false, false, PrintOptions.Default); this.Emit(stream, 0); return buffer.ToString(); @@ -4312,7 +4307,7 @@ public override void Emit(TokenTextWriter stream, int level) //Contract.Requires(stream != null); Contract.Assume(this.labelNames != null); stream.Write(this, level, "goto "); - if (CoreOptions.Clo.PrintWithUniqueASTIds) + if (stream.Options.PrintWithUniqueASTIds) { if (labelTargets == null) { diff --git a/Source/Core/AbsyExpr.cs b/Source/Core/AbsyExpr.cs index fb21cdf85..7421da658 100644 --- a/Source/Core/AbsyExpr.cs +++ b/Source/Core/AbsyExpr.cs @@ -81,7 +81,8 @@ public override string ToString() { Contract.Ensures(Contract.Result() != null); System.IO.StringWriter buffer = new System.IO.StringWriter(); - using TokenTextWriter stream = new TokenTextWriter("", buffer, /*setTokens=*/ false, /*pretty=*/ false); + using TokenTextWriter stream = new TokenTextWriter("", buffer, + /*setTokens=*/ false, /*pretty=*/ false, PrintOptions.Default); this.Emit(stream, 0, false); return buffer.ToString(); @@ -1294,7 +1295,7 @@ public override int ComputeHashCode() public override void Emit(TokenTextWriter stream, int contextBindingStrength, bool fragileContext) { //Contract.Requires(stream != null); - if (CoreOptions.Clo.PrintWithUniqueASTIds && !stream.UseForComputingChecksums) + if (stream.Options.PrintWithUniqueASTIds && !stream.UseForComputingChecksums) { stream.Write("{0}^^", this.Decl == null ? "NoDecl" : "h" + this.Decl.GetHashCode()); } diff --git a/Source/Core/AbsyType.cs b/Source/Core/AbsyType.cs index a65c6a0b3..eba42e7ab 100644 --- a/Source/Core/AbsyType.cs +++ b/Source/Core/AbsyType.cs @@ -54,7 +54,7 @@ public override string ToString() { Contract.Ensures(Contract.Result() != null); System.IO.StringWriter buffer = new System.IO.StringWriter(); - using TokenTextWriter stream = new TokenTextWriter("", buffer, /*setTokens=*/false, /*pretty=*/ false); + using TokenTextWriter stream = new TokenTextWriter("", buffer, false, false, PrintOptions.Default); this.Emit(stream); return buffer.ToString(); diff --git a/Source/Core/CoreOptions.cs b/Source/Core/CoreOptions.cs index 4643d5319..762ea4fff 100644 --- a/Source/Core/CoreOptions.cs +++ b/Source/Core/CoreOptions.cs @@ -2,18 +2,12 @@ namespace Microsoft.Boogie { + /// /// Boogie command-line options (other tools can subclass this class in order to support a /// superset of Boogie's options). /// - public interface CoreOptions - { - - public static CoreOptions /*!*/ Clo - { - get; - set; - } + public interface CoreOptions : PrintOptions { public enum TypeEncoding { @@ -48,13 +42,7 @@ public enum Inlining bool OverlookBoogieTypeErrors { get; } uint TimeLimit { get; } uint ResourceLimit { get; } - bool PrintWithUniqueASTIds { get; } - int StratifiedInlining { get; } bool DoModSetAnalysis { get; } - bool PrintInstrumented { get; } - bool PrintInlined { get; } - int PrintUnstructured { get; set; } - bool PrintDesugarings { get; set; } bool DebugStagedHoudini { get; } bool DeterministicExtractLoops { get; } string VariableDependenceIgnore { get; } @@ -70,7 +58,6 @@ public enum Inlining bool InstrumentWithAsserts { get; } bool UseArrayTheory { get; set; } TypeEncoding TypeEncodingMethod { get; set; } - bool ReflectAdd { get; } SubsumptionOption UseSubsumption { get; } int VcsCores { get; } List ProverOptions { get; } diff --git a/Source/Core/DeadVarElim.cs b/Source/Core/DeadVarElim.cs index 0249e1eaf..0c9ba1d00 100644 --- a/Source/Core/DeadVarElim.cs +++ b/Source/Core/DeadVarElim.cs @@ -50,6 +50,7 @@ public override Implementation VisitImplementation(Implementation node) public class ModSetCollector : ReadOnlyVisitor { + private CoreOptions options; private Procedure enclosingProc; private Dictionary /*!*/> /*!*/ @@ -64,8 +65,9 @@ void ObjectInvariant() Contract.Invariant(Contract.ForAll(modSets.Values, v => cce.NonNullElements(v))); } - public ModSetCollector() + public ModSetCollector(CoreOptions options) { + this.options = options; modSets = new Dictionary /*!*/>(); yieldingProcs = new HashSet(); } @@ -76,7 +78,7 @@ public void DoModSetAnalysis(Program program) { Contract.Requires(program != null); - if (CoreOptions.Clo.Trace) + if (options.Trace) { // Console.WriteLine(); // Console.WriteLine("Running modset analysis ..."); @@ -586,6 +588,13 @@ public override Implementation VisitImplementation(Implementation impl) public class LiveVariableAnalysis { + private CoreOptions options; + + public LiveVariableAnalysis(CoreOptions options) + { + this.options = options; + } + public static void ClearLiveVariables(Implementation impl) { Contract.Requires(impl != null); @@ -596,13 +605,13 @@ public static void ClearLiveVariables(Implementation impl) } } - public static void ComputeLiveVariables(Implementation impl) + public void ComputeLiveVariables(Implementation impl) { Contract.Requires(impl != null); - Microsoft.Boogie.Helpers.ExtraTraceInformation("Starting live variable analysis"); + Microsoft.Boogie.Helpers.ExtraTraceInformation(options, "Starting live variable analysis"); Graph dag = Program.GraphFromBlocks(impl.Blocks, false); IEnumerable sortedNodes; - if (CoreOptions.Clo.ModifyTopologicalSorting) + if (options.ModifyTopologicalSorting) { sortedNodes = dag.TopologicalSort(true); } @@ -648,7 +657,7 @@ public static void ComputeLiveVariables(Implementation impl) if (InterProcGenKill.HasSummary(proc.Name)) { liveVarsAfter = - InterProcGenKill.PropagateLiveVarsAcrossCall(cce.NonNull((CallCmd /*!*/) cmds[i]), liveVarsAfter); + InterProcGenKill.PropagateLiveVarsAcrossCall(options, cce.NonNull((CallCmd /*!*/) cmds[i]), liveVarsAfter); continue; } } @@ -661,7 +670,7 @@ public static void ComputeLiveVariables(Implementation impl) } // perform in place update of liveSet - public static void Propagate(Cmd cmd, HashSet /*!*/ liveSet) + public void Propagate(Cmd cmd, HashSet /*!*/ liveSet) { Contract.Requires(cmd != null); Contract.Requires(cce.NonNullElements(liveSet)); @@ -749,7 +758,7 @@ public static void Propagate(Cmd cmd, HashSet /*!*/ liveSet) { SugaredCmd /*!*/ sugCmd = (SugaredCmd) cce.NonNull(cmd); - Propagate(sugCmd.Desugaring, liveSet); + Propagate(sugCmd.GetDesugaring(options), liveSet); } else if (cmd is StateCmd) { @@ -1178,8 +1187,8 @@ private void addEdge(Block src, Block tgt) // Interprocedural Gen/Kill Analysis public class InterProcGenKill { - Program /*!*/ - program; + private CoreOptions options; + Program /*!*/ program; Dictionary /*!*/ procICFG; @@ -1235,11 +1244,12 @@ void ObjectInvariant() [NotDelayed] - public InterProcGenKill(Implementation impl, Program program) + public InterProcGenKill(Implementation impl, Program program, CoreOptions options) { Contract.Requires(program != null); Contract.Requires(impl != null); this.program = program; + this.options = options; procICFG = new Dictionary(); name2Proc = new Dictionary(); workList = new WorkList(); @@ -1409,7 +1419,7 @@ public static bool HasSummary(string name) } public static HashSet /*!*/ - PropagateLiveVarsAcrossCall(CallCmd cmd, HashSet /*!*/ lvAfter) + PropagateLiveVarsAcrossCall(CoreOptions options, CallCmd cmd, HashSet /*!*/ lvAfter) { Contract.Requires(cmd != null); Contract.Requires(cce.NonNullElements(lvAfter)); @@ -1436,7 +1446,7 @@ public static HashSet /*!*/ HashSet /*!*/ ret = new HashSet(); ret.UnionWith(lvAfter); - LiveVariableAnalysis.Propagate(cmd, ret); + new LiveVariableAnalysis(options).Propagate(cmd, ret); return ret; } @@ -1634,12 +1644,12 @@ private GenKillWeight getSummary(CallCmd cmd) } } - public static void ComputeLiveVars(Implementation impl, Program /*!*/ prog) + public void ComputeLiveVars(Implementation impl, Program /*!*/ prog) { Contract.Requires(prog != null); Contract.Requires(impl != null); InterProcGenKill /*!*/ - ipgk = new InterProcGenKill(impl, prog); + ipgk = new InterProcGenKill(impl, prog, options); Contract.Assert(ipgk != null); ipgk.Compute(); } @@ -1698,7 +1708,7 @@ public void Compute() WorkItem /*!*/ wi = workList.Get(); Contract.Assert(wi != null); - processLV(wi); + ProcessLv(wi); } // Set live variable info @@ -1740,7 +1750,7 @@ public void Compute() } // Called when summaries have already been computed - private void processLV(WorkItem wi) + private void ProcessLv(WorkItem wi) { Contract.Requires(wi != null); ICFG /*!*/ @@ -1800,17 +1810,17 @@ private void processLV(WorkItem wi) // Continue with intra propagation GenKillWeight /*!*/ - summary = getWeightCall(cce.NonNull((CallCmd /*!*/) cmd)); + summary = GetWeightCall(cce.NonNull((CallCmd /*!*/) cmd)); prop = summary.getLiveVars(prop); } else { - LiveVariableAnalysis.Propagate(cmd, prop); + new LiveVariableAnalysis(options).Propagate(cmd, prop); } } else { - LiveVariableAnalysis.Propagate(cmd, prop); + new LiveVariableAnalysis(options).Propagate(cmd, prop); } } @@ -1848,12 +1858,12 @@ private void process(WorkItem wi) Contract.Assert(c != null); if (c is CallCmd && procICFG.ContainsKey(cce.NonNull(cce.NonNull((CallCmd) c).Proc).Name)) { - w = GenKillWeight.extend(getWeightCall(cce.NonNull((CallCmd) c)), w); + w = GenKillWeight.extend(GetWeightCall(cce.NonNull((CallCmd) c)), w); } else { GenKillWeight /*!*/ - cweight = getWeight(c, wi.cfg.impl, program); + cweight = GetWeight(c, wi.cfg.impl, program); Contract.Assert(cweight != null); w = GenKillWeight.extend(cweight, w); } @@ -1904,14 +1914,14 @@ private void process(WorkItem wi) static Dictionary /*!*/ weightCache = new Dictionary(); - private static GenKillWeight getWeight(Cmd cmd) + private GenKillWeight GetWeight(Cmd cmd) { Contract.Requires(cmd != null); Contract.Ensures(Contract.Result() != null); - return getWeight(cmd, null, null); + return GetWeight(cmd, null, null); } - private GenKillWeight getWeightCall(CallCmd cmd) + private GenKillWeight GetWeightCall(CallCmd cmd) { Contract.Requires(cmd != null); Contract.Ensures(Contract.Result() != null); @@ -1927,7 +1937,7 @@ private GenKillWeight getWeightCall(CallCmd cmd) return GenKillWeight.extend(w1, GenKillWeight.extend(w2, w3)); } - private static GenKillWeight getWeight(Cmd cmd, Implementation impl, Program prog) + private GenKillWeight GetWeight(Cmd cmd, Implementation impl, Program prog) { Contract.Requires(cmd != null); Contract.Ensures(Contract.Result() != null); @@ -2060,7 +2070,7 @@ private static GenKillWeight getWeight(Cmd cmd, Implementation impl, Program pro SugaredCmd /*!*/ sugCmd = (SugaredCmd) cmd; Contract.Assert(sugCmd != null); - ret = getWeight(sugCmd.Desugaring, impl, prog); + ret = GetWeight(sugCmd.GetDesugaring(options), impl, prog); } else if (cmd is StateCmd) { @@ -2075,7 +2085,7 @@ private static GenKillWeight getWeight(Cmd cmd, Implementation impl, Program pro for (int i = len - 1; i >= 0; i--) { GenKillWeight /*!*/ - w = getWeight(cmds[i], impl, prog); + w = GetWeight(cmds[i], impl, prog); Contract.Assert(w != null); ret = GenKillWeight.extend(w, ret); } diff --git a/Source/Core/Helpers.cs b/Source/Core/Helpers.cs index c0377a6a8..bb9024247 100644 --- a/Source/Core/Helpers.cs +++ b/Source/Core/Helpers.cs @@ -252,10 +252,10 @@ public static string PrettyPrintBplExpr(Expr e) private static readonly DateTime StartUp = DateTime.UtcNow; - public static void ExtraTraceInformation(string point) + public static void ExtraTraceInformation(CoreOptions options, string point) { Contract.Requires(point != null); - if (CoreOptions.Clo.TraceTimes) + if (options.TraceTimes) { DateTime now = DateTime.UtcNow; TimeSpan timeSinceStartUp = now - StartUp; diff --git a/Source/Core/Inline.cs b/Source/Core/Inline.cs index c0b256373..59453f7eb 100644 --- a/Source/Core/Inline.cs +++ b/Source/Core/Inline.cs @@ -10,6 +10,7 @@ namespace Microsoft.Boogie public class Inliner : Duplicator { + private CoreOptions options; protected bool inlinedSomething; protected Program program; @@ -43,7 +44,7 @@ void ObjectInvariant() public override Expr VisitCodeExpr(CodeExpr node) { - Inliner codeExprInliner = new Inliner(program, inlineCallback, CoreOptions.Clo.InlineDepth); + Inliner codeExprInliner = new Inliner(program, inlineCallback, options.InlineDepth, options); codeExprInliner.newLocalVars.AddRange(node.LocVars); codeExprInliner.inlinedProcLblMap = this.inlinedProcLblMap; List newCodeExprBlocks = codeExprInliner.DoInlineBlocks(node.Blocks, ref inlinedSomething); @@ -78,12 +79,13 @@ protected string GetProcVarName(string procName, string formalName) return GetInlinedProcLabel(procName) + "$" + formalName; } - public Inliner(Program program, InlineCallback cb, int inlineDepth) + public Inliner(Program program, InlineCallback cb, int inlineDepth, CoreOptions options) { this.program = program; this.inlinedProcLblMap = new Dictionary(); this.recursiveProcUnrollMap = new Dictionary(); this.inlineDepth = inlineDepth; + this.options = options; this.codeCopier = new CodeCopier(); this.inlineCallback = cb; this.newLocalVars = new List(); @@ -143,17 +145,17 @@ private void DistinguishPrefix(string s) } } - protected static void ProcessImplementation(Program program, Implementation impl, Inliner inliner) + protected void ProcessImplementation(Program program, Implementation impl) { Contract.Requires(impl != null); Contract.Requires(impl.Proc != null); - inliner.ComputePrefix(program, impl); + ComputePrefix(program, impl); - inliner.newLocalVars.AddRange(impl.LocVars); + newLocalVars.AddRange(impl.LocVars); bool inlined = false; - List newBlocks = inliner.DoInlineBlocks(impl.Blocks, ref inlined); + List newBlocks = DoInlineBlocks(impl.Blocks, ref inlined); Contract.Assert(cce.NonNullElements(newBlocks)); if (!inlined) @@ -163,34 +165,36 @@ protected static void ProcessImplementation(Program program, Implementation impl impl.InParams = new List(impl.InParams); impl.OutParams = new List(impl.OutParams); - impl.LocVars = inliner.newLocalVars; + impl.LocVars = newLocalVars; impl.Blocks = newBlocks; impl.ResetImplFormalMap(); // we need to resolve the new code - inliner.ResolveImpl(impl); + ResolveImpl(impl); - if (CoreOptions.Clo.PrintInlined) + if (options.PrintInlined) { - inliner.EmitImpl(impl); + EmitImpl(impl); } } - public static void ProcessImplementationForHoudini(Program program, Implementation impl) + public static void ProcessImplementationForHoudini(CoreOptions options, Program program, Implementation impl) { Contract.Requires(impl != null); Contract.Requires(program != null); Contract.Requires(impl.Proc != null); - ProcessImplementation(program, impl, new Inliner(program, null, CoreOptions.Clo.InlineDepth)); + var inliner = new Inliner(program, null, options.InlineDepth, options); + inliner.ProcessImplementation(program, impl); } - public static void ProcessImplementation(Program program, Implementation impl) + public static void ProcessImplementation(CoreOptions options, Program program, Implementation impl) { Contract.Requires(impl != null); Contract.Requires(program != null); Contract.Requires(impl.Proc != null); - ProcessImplementation(program, impl, new Inliner(program, null, -1)); + var inliner = new Inliner(program, null, -1, options); + inliner.ProcessImplementation(program, impl); } protected void EmitImpl(Implementation impl) @@ -198,8 +202,8 @@ protected void EmitImpl(Implementation impl) Contract.Requires(impl != null); Contract.Requires(impl.Proc != null); Console.WriteLine("after inlining procedure calls"); - impl.Proc.Emit(new TokenTextWriter("", Console.Out, /*pretty=*/ false), 0); - impl.Emit(new TokenTextWriter("", Console.Out, /*pretty=*/ false), 0); + impl.Proc.Emit(new TokenTextWriter("", Console.Out, /*pretty=*/ false, options), 0); + impl.Emit(new TokenTextWriter("", Console.Out, /*pretty=*/ false, options), 0); } private sealed class DummyErrorSink : IErrorSink @@ -219,7 +223,7 @@ protected void ResolveImpl(Implementation impl) { Contract.Requires(impl != null); Contract.Ensures(impl.Proc != null); - ResolutionContext rc = new ResolutionContext(new DummyErrorSink()); + ResolutionContext rc = new ResolutionContext(new DummyErrorSink(), options); foreach (var decl in program.TopLevelDeclarations) { decl.Register(rc); @@ -228,7 +232,7 @@ protected void ResolveImpl(Implementation impl) impl.Resolve(rc); Debug.Assert(rc.ErrorCount == 0); - TypecheckingContext tc = new TypecheckingContext(new DummyErrorSink()); + TypecheckingContext tc = new TypecheckingContext(new DummyErrorSink(), options); impl.Typecheck(tc); Debug.Assert(tc.ErrorCount == 0); } @@ -388,12 +392,12 @@ private int InlineCallCmd(Block block, CallCmd callCmd, Implementation impl, Lis else if (inline == 0) { inlinedSomething = true; - if (CoreOptions.Clo.ProcedureInlining == CoreOptions.Inlining.Assert) + if (options.ProcedureInlining == CoreOptions.Inlining.Assert) { // add assert newCmds.Add(new AssertCmd(callCmd.tok, Expr.False)); } - else if (CoreOptions.Clo.ProcedureInlining == CoreOptions.Inlining.Assume) + else if (options.ProcedureInlining == CoreOptions.Inlining.Assume) { // add assume newCmds.Add(new AssumeCmd(callCmd.tok, Expr.False)); diff --git a/Source/Core/InterProceduralReachabilityGraph.cs b/Source/Core/InterProceduralReachabilityGraph.cs index 4b79c31c0..0b3555384 100644 --- a/Source/Core/InterProceduralReachabilityGraph.cs +++ b/Source/Core/InterProceduralReachabilityGraph.cs @@ -21,6 +21,7 @@ public interface IInterproceduralReachabilityGraph public class InterproceduralReachabilityGraph : IInterproceduralReachabilityGraph { + private CoreOptions options; private Program prog; private HashSet nodes; private Dictionary originalToNew; @@ -29,9 +30,10 @@ public class InterproceduralReachabilityGraph : IInterproceduralReachabilityGrap private Graph reachabilityGraph; - public InterproceduralReachabilityGraph(Program prog) + public InterproceduralReachabilityGraph(Program prog, CoreOptions options) { this.prog = prog; + this.options = options; originalToNew = new Dictionary(); newProcedureEntryNodes = new Dictionary(); newProcedureExitNodes = new Dictionary(); @@ -257,7 +259,7 @@ public bool MayReach(Block src, Block dst) { if (ReachabilityGraphSCCsDAG == null) { - if (CoreOptions.Clo.Trace) + if (options.Trace) { Console.WriteLine("Interprocedural reachability: computing SCCs"); } @@ -292,7 +294,7 @@ public bool MayReach(Block src, Block dst) ReachabilityGraphSCCsDAG.AddEdge(BlockToSCC[n], dummy); } - if (CoreOptions.Clo.Trace) + if (options.Trace) { Console.WriteLine("Interprocedural reachability: SCCs computed!"); } diff --git a/Source/Core/LambdaHelper.cs b/Source/Core/LambdaHelper.cs index 2f62ad3b0..9405e6aac 100644 --- a/Source/Core/LambdaHelper.cs +++ b/Source/Core/LambdaHelper.cs @@ -8,22 +8,22 @@ namespace Microsoft.Boogie { public static class LambdaHelper { - public static Program Desugar(Program program, out List /*!*/ axioms, + public static Program Desugar(CoreOptions options, Program program, out List /*!*/ axioms, out List /*!*/ functions) { Contract.Requires(program != null); Contract.Ensures(cce.NonNullElements(Contract.ValueAtReturn(out functions))); Contract.Ensures(cce.NonNullElements(Contract.ValueAtReturn(out axioms))); Contract.Ensures(Contract.Result() != null); - LambdaVisitor v = new LambdaVisitor(); + LambdaVisitor v = new LambdaVisitor(options); program = v.VisitProgram(program); axioms = v.lambdaAxioms; functions = v.lambdaFunctions; - if (CoreOptions.Clo.TraceVerify) + if (options.TraceVerify) { Console.WriteLine("Desugaring of lambda expressions produced {0} functions and {1} axioms:", functions.Count, axioms.Count); - TokenTextWriter wr = new TokenTextWriter("", Console.Out, /*pretty=*/ false); + TokenTextWriter wr = new TokenTextWriter("", Console.Out, /*pretty=*/ false, options); foreach (Function f in functions) { f.Emit(wr, 0); @@ -71,11 +71,11 @@ public static Program Desugar(Program program, out List /*!*/ axiom /// is used by default whereas LambdaLiftingFreeVars /// is used with the command-line option /freeVarLambdaLifting. /// - public static void ExpandLambdas(Program prog) + public static void ExpandLambdas(CoreOptions options, Program prog) { Contract.Requires(prog != null); - Desugar(prog, out var axioms, out var functions); + Desugar(options, prog, out var axioms, out var functions); foreach (var f in functions) { prog.AddTopLevelDeclaration(f); @@ -89,6 +89,7 @@ public static void ExpandLambdas(Program prog) private class LambdaVisitor : VarDeclOnceStandardVisitor { + private CoreOptions options; private readonly Dictionary liftedLambdas = new Dictionary(new AlphaEquality()); @@ -107,6 +108,11 @@ void ObjectInvariant() int lambdaid = 0; + public LambdaVisitor(CoreOptions options) + { + this.options = options; + } + string FreshLambdaFunctionName() { return $"lambda#{lambdaid++}"; @@ -121,7 +127,7 @@ public override Expr VisitLambdaExpr(LambdaExpr lambda) return baseResult; // apparently, the base visitor already turned the lambda into something else } - return CoreOptions.Clo.FreeVarLambdaLifting ? LiftLambdaFreeVars(lambda) : LiftLambdaMaxHoles(lambda); + return options.FreeVarLambdaLifting ? LiftLambdaFreeVars(lambda) : LiftLambdaMaxHoles(lambda); } /// @@ -174,7 +180,7 @@ private Expr LiftLambdaFreeVars(LambdaExpr lambda) Substituter.SubstitutionFromDictionary(oldSubst), lambda.Attributes); - if (0 < CoreOptions.Clo.VerifySnapshots && + if (0 < options.VerifySnapshots && QKeyValue.FindStringAttribute(lambdaAttrs, "checksum") == null) { // Attach a dummy checksum to avoid issues in the dependency analysis. @@ -241,7 +247,7 @@ private Expr LiftLambdaFreeVars(LambdaExpr lambda) dummies.AddRange(lambda.Dummies); var sw = new System.IO.StringWriter(); - var wr = new TokenTextWriter(sw, true); + var wr = new TokenTextWriter(sw, true, options); lambda.Emit(wr); string lam_str = sw.ToString(); @@ -250,14 +256,14 @@ private Expr LiftLambdaFreeVars(LambdaExpr lambda) if (liftedLambdas.TryGetValue(lambda, out var fcall)) { - if (CoreOptions.Clo.TraceVerify) + if (options.TraceVerify) { Console.WriteLine("Old lambda: {0}", lam_str); } } else { - if (CoreOptions.Clo.TraceVerify) + if (options.TraceVerify) { Console.WriteLine("New lambda: {0}", lam_str); } @@ -369,7 +375,7 @@ private Expr LiftLambdaMaxHoles(LambdaExpr lambda) // We perform lambda lifting on the resulting lambda which now contains only `old` expressions of the form // `old(x)` where `x` is a variable that is free in the lambda. return new MaxHolesLambdaLifter( - newLambda, liftedLambdas, FreshLambdaFunctionName(), lambdaFunctions, lambdaAxioms) + newLambda, liftedLambdas, FreshLambdaFunctionName(), lambdaFunctions, lambdaAxioms, options) .VisitLambdaExpr(newLambda); } diff --git a/Source/Core/MaxHolesLambdaLifter.cs b/Source/Core/MaxHolesLambdaLifter.cs index 230d43017..9fb0b51af 100644 --- a/Source/Core/MaxHolesLambdaLifter.cs +++ b/Source/Core/MaxHolesLambdaLifter.cs @@ -31,6 +31,7 @@ namespace Core /// class MaxHolesLambdaLifter : StandardVisitor { + private CoreOptions options; private readonly List _nestedBoundVariables = new List(); private readonly LambdaExpr _lambda; @@ -47,8 +48,7 @@ public MaxHolesLambdaLifter( Dictionary liftedLambdas, string freshFnName, List lambdaFunctions, - List lambdaAxioms, - int freshVarCount = 0 + List lambdaAxioms, CoreOptions options, int freshVarCount = 0 ) { _lambda = lambda; @@ -56,6 +56,7 @@ public MaxHolesLambdaLifter( _freshFnName = freshFnName; _lambdaFunctions = lambdaFunctions; _lambdaAxioms = lambdaAxioms; + this.options = options; _freshVarCount = freshVarCount; } @@ -346,7 +347,7 @@ public override Expr VisitLambdaExpr(LambdaExpr node) var lambdaAttrs = _lambda.Attributes; - if (0 < CoreOptions.Clo.VerifySnapshots && QKeyValue.FindStringAttribute(lambdaAttrs, "checksum") == null) + if (0 < options.VerifySnapshots && QKeyValue.FindStringAttribute(lambdaAttrs, "checksum") == null) { // Attach a dummy checksum to avoid issues in the dependency analysis. var checksumAttr = new QKeyValue(_lambda.tok, "checksum", new List {"lambda expression"}, null); @@ -367,7 +368,7 @@ public override Expr VisitLambdaExpr(LambdaExpr node) var freeVarActuals = freeVars.OfType().ToList(); var sw = new StringWriter(); - var wr = new TokenTextWriter(sw, true); + var wr = new TokenTextWriter(sw, true, options); _lambda.Emit(wr); string lam_str = sw.ToString(); @@ -381,14 +382,14 @@ public override Expr VisitLambdaExpr(LambdaExpr node) if (_liftedLambdas.TryGetValue(liftedLambda, out var fcall)) { - if (CoreOptions.Clo.TraceVerify) + if (options.TraceVerify) { Console.WriteLine("Old lambda: {0}", lam_str); } } else { - if (CoreOptions.Clo.TraceVerify) + if (options.TraceVerify) { Console.WriteLine("New lambda: {0}", lam_str); } diff --git a/Source/Core/Monomorphization.cs b/Source/Core/Monomorphization.cs index 14c23f2d5..1d22c5543 100644 --- a/Source/Core/Monomorphization.cs +++ b/Source/Core/Monomorphization.cs @@ -304,6 +304,7 @@ public override Type VisitTypeProxy(TypeProxy node) class MonomorphizationVisitor : StandardVisitor { + public CoreOptions Options { get; } /* * This class monomorphizes a Boogie program. * Monomorphization starts from a traversal of monomorphic procedures. @@ -626,9 +627,9 @@ public override Expr VisitNAryExpr(NAryExpr node) return returnExpr; } - private static bool IsInlined(Implementation impl) + private bool IsInlined(Implementation impl) { - if (CoreOptions.Clo.ProcedureInlining == CoreOptions.Inlining.None) + if (monomorphizationVisitor.Options.ProcedureInlining == CoreOptions.Inlining.None) { return false; } @@ -649,7 +650,7 @@ public override Cmd VisitCallCmd(CallCmd node) var actualTypeParams = returnCallCmd.TypeParameters.FormalTypeParams.Select(x => TypeProxy.FollowProxy(returnCallCmd.TypeParameters[x]).Substitute(typeParamInstantiation)) - .Select(x => LookupType(x)).ToList(); + .Select(LookupType).ToList(); returnCallCmd.Proc = InstantiateProcedure(node.Proc, actualTypeParams); returnCallCmd.callee = returnCallCmd.Proc.Name; returnCallCmd.TypeParameters = SimpleTypeParamInstantiation.EMPTY; @@ -839,15 +840,16 @@ public override Cmd VisitAssignCmd(AssignCmd node) } } - public static MonomorphizationVisitor Initialize(Program program, Dictionary axiomsToBeInstantiated, + public static MonomorphizationVisitor Initialize(CoreOptions options, Program program, + Dictionary axiomsToBeInstantiated, HashSet polymorphicFunctionAxioms) { - var monomorphizationVisitor = new MonomorphizationVisitor(program, axiomsToBeInstantiated, polymorphicFunctionAxioms); + var monomorphizationVisitor = new MonomorphizationVisitor(options, program, axiomsToBeInstantiated, polymorphicFunctionAxioms); // ctorTypes contains all the uninterpreted types created for monomorphizing top-level polymorphic implementations // that must be verified. The types in ctorTypes are reused across different implementations. var ctorTypes = new List(); var typeCtorDecls = new HashSet(); - monomorphizationVisitor.implInstantiations.Keys.Where(impl => !impl.SkipVerification).Iter(impl => + monomorphizationVisitor.implInstantiations.Keys.Where(impl => !impl.IsSkipVerification(options)).Iter(impl => { for (int i = ctorTypes.Count; i < impl.TypeParameters.Count; i++) { @@ -897,8 +899,10 @@ public Function Monomorphize(string functionName, Dictionary typeP private MonomorphizationDuplicator monomorphizationDuplicator; private Dictionary procToImpl; - private MonomorphizationVisitor(Program program, Dictionary axiomsToBeInstantiated, HashSet polymorphicFunctionAxioms) + private MonomorphizationVisitor(CoreOptions options, Program program, + Dictionary axiomsToBeInstantiated, HashSet polymorphicFunctionAxioms) { + Options = options; this.program = program; this.axiomsToBeInstantiated = axiomsToBeInstantiated; implInstantiations = new Dictionary, Implementation>>(); @@ -1076,12 +1080,12 @@ public override Function VisitFunction(Function node) public class Monomorphizer { - public static MonomorphizableStatus Monomorphize(Program program) + public static MonomorphizableStatus Monomorphize(CoreOptions options, Program program) { var monomorphizableStatus = MonomorphizableChecker.IsMonomorphizable(program, out var axiomsToBeInstantiated, out var polymorphicFunctionAxioms); if (monomorphizableStatus == MonomorphizableStatus.Monomorphizable) { - var monomorphizationVisitor = MonomorphizationVisitor.Initialize(program, axiomsToBeInstantiated, polymorphicFunctionAxioms); + var monomorphizationVisitor = MonomorphizationVisitor.Initialize(options, program, axiomsToBeInstantiated, polymorphicFunctionAxioms); program.monomorphizer = new Monomorphizer(monomorphizationVisitor); } return monomorphizableStatus; diff --git a/Source/Core/PrintOptions.cs b/Source/Core/PrintOptions.cs new file mode 100644 index 000000000..eb99b2c4f --- /dev/null +++ b/Source/Core/PrintOptions.cs @@ -0,0 +1,23 @@ +namespace Microsoft.Boogie; + +public interface PrintOptions { + public static readonly PrintOptions Default = new PrintOptionsRec( + false, + false, + false, + 0, + false); + + bool PrintWithUniqueASTIds { get; } + bool PrintInstrumented { get; } + bool PrintInlined { get; } + int StratifiedInlining { get; } + bool PrintDesugarings { get; set; } + int PrintUnstructured { get; set; } + bool ReflectAdd { get; } +} + +record PrintOptionsRec(bool PrintWithUniqueASTIds, bool PrintInstrumented, bool PrintInlined, int StratifiedInlining, bool ReflectAdd) : PrintOptions { + public bool PrintDesugarings { get; set; } + public int PrintUnstructured { get; set; } +} \ No newline at end of file diff --git a/Source/Core/ResolutionContext.cs b/Source/Core/ResolutionContext.cs index de7c0ab9a..5631c5aa8 100644 --- a/Source/Core/ResolutionContext.cs +++ b/Source/Core/ResolutionContext.cs @@ -118,9 +118,12 @@ public void Warning(IToken tok, string msg, params object[] args) public class ResolutionContext : CheckingContext { - public ResolutionContext(IErrorSink errorSink) + public CoreOptions Options { get; } + + public ResolutionContext(IErrorSink errorSink, CoreOptions options) : base(errorSink) { + this.Options = options; } // user-defined types, which can be either TypeCtorDecl or TypeSynonymDecl @@ -740,12 +743,14 @@ public bool TriggerMode public class TypecheckingContext : CheckingContext { + public CoreOptions Options { get; } public List Frame; // used in checking the assignment targets of implementation bodies public bool Yields; - public TypecheckingContext(IErrorSink errorSink) + public TypecheckingContext(IErrorSink errorSink, CoreOptions options) : base(errorSink) { + this.Options = options; } public bool InFrame(Variable v) diff --git a/Source/Core/TokenTextWriter.cs b/Source/Core/TokenTextWriter.cs index 0ba274c8f..d507ee648 100644 --- a/Source/Core/TokenTextWriter.cs +++ b/Source/Core/TokenTextWriter.cs @@ -8,11 +8,10 @@ namespace Microsoft.Boogie { public class TokenTextWriter : IDisposable { - string /*!*/ - filename; + public PrintOptions Options { get; } + string /*!*/ filename; - TextWriter /*!*/ - writer; + TextWriter /*!*/ writer; [ContractInvariantMethod] void ObjectInvariant() @@ -262,61 +261,66 @@ public static string SanitizeIdentifier(string name) } } - public TokenTextWriter(string filename) - : this(filename, false) + public TokenTextWriter(string filename, CoreOptions options) + : this(filename, false, options) { } - public TokenTextWriter(string filename, bool pretty) + public TokenTextWriter(string filename, bool pretty, CoreOptions options) : base() { Contract.Requires(filename != null); this.pretty = pretty; + this.Options = options; this.filename = filename; this.writer = new StreamWriter(filename); } - public TokenTextWriter(string filename, bool setTokens, bool pretty) + public TokenTextWriter(string filename, bool setTokens, bool pretty, CoreOptions options) : base() { Contract.Requires(filename != null); this.pretty = pretty; + this.Options = options; this.filename = filename; this.writer = new StreamWriter(filename); this.setTokens = setTokens; } - public TokenTextWriter(string filename, TextWriter writer, bool setTokens, bool pretty) + public TokenTextWriter(string filename, TextWriter writer, bool setTokens, bool pretty, PrintOptions options) : base() { Contract.Requires(writer != null); Contract.Requires(filename != null); this.pretty = pretty; + this.Options = options; this.filename = filename; this.writer = writer; this.setTokens = setTokens; } - public TokenTextWriter(string filename, TextWriter writer, bool pretty) + public TokenTextWriter(string filename, TextWriter writer, bool pretty, CoreOptions options) : base() { Contract.Requires(writer != null); Contract.Requires(filename != null); this.pretty = pretty; + this.Options = options; this.filename = filename; this.writer = writer; } - public TokenTextWriter(TextWriter writer) - : this(writer, false) + public TokenTextWriter(TextWriter writer, CoreOptions options) + : this(writer, false, options) { } - public TokenTextWriter(TextWriter writer, bool pretty) + public TokenTextWriter(TextWriter writer, bool pretty, CoreOptions options) : base() { Contract.Requires(writer != null); this.pretty = pretty; + this.Options = options; this.filename = ""; this.writer = writer; } diff --git a/Source/Core/VariableDependenceAnalyser.cs b/Source/Core/VariableDependenceAnalyser.cs index c91cb60ae..133fb066b 100644 --- a/Source/Core/VariableDependenceAnalyser.cs +++ b/Source/Core/VariableDependenceAnalyser.cs @@ -134,14 +134,16 @@ public override int GetHashCode() /// public class VariableDependenceAnalyser : IVariableDependenceAnalyser { + private CoreOptions options; private Graph dependsOnNonTransitive; private Program prog; private Dictionary> BlockToControllingBlocks; private Dictionary> ControllingBlockToVariables; - public VariableDependenceAnalyser(Program prog) + public VariableDependenceAnalyser(Program prog, CoreOptions options) { this.prog = prog; + this.options = options; dependsOnNonTransitive = new Graph(); } @@ -265,21 +267,21 @@ public void Analyse() * */ - if (CoreOptions.Clo.Trace) + if (options.Trace) { Console.WriteLine("Variable dependence analysis: Initialising"); } Initialise(); - if (CoreOptions.Clo.Trace) + if (options.Trace) { Console.WriteLine("Variable dependence analysis: Computing control dependence info"); } BlockToControllingBlocks = ComputeGlobalControlDependences(); - if (CoreOptions.Clo.Trace) + if (options.Trace) { Console.WriteLine("Variable dependence analysis: Computing control dependence variables"); } @@ -287,7 +289,7 @@ public void Analyse() ControllingBlockToVariables = ComputeControllingVariables(BlockToControllingBlocks); foreach (var Impl in prog.NonInlinedImplementations()) { - if (CoreOptions.Clo.Trace) + if (options.Trace) { Console.WriteLine("Variable dependence analysis: Analysing " + Impl.Name); } @@ -384,7 +386,7 @@ void AddDependences(VariableDescriptor v, IEnumerable vs, st { foreach (var n in vs) { - if (CoreOptions.Clo.DebugStagedHoudini) + if (options.DebugStagedHoudini) { Console.WriteLine("Adding dependence " + v + " -> " + n + ", reason: " + reason + "(" + tok.line + ":" + tok.col + ")"); @@ -471,14 +473,14 @@ public bool VariableRelevantToAnalysis(Variable v, string proc) private void MakeIgnoreList() { IgnoredVariables = new HashSet(); - if (CoreOptions.Clo.VariableDependenceIgnore == null) + if (options.VariableDependenceIgnore == null) { return; } try { - var file = System.IO.File.OpenText(CoreOptions.Clo.VariableDependenceIgnore); + var file = System.IO.File.OpenText(options.VariableDependenceIgnore); while (!file.EndOfStream) { string line = file.ReadLine(); @@ -507,7 +509,7 @@ private void MakeIgnoreList() catch (System.IO.IOException e) { Console.Error.WriteLine("Error reading from ignored variables file " + - CoreOptions.Clo.VariableDependenceIgnore + ": " + e); + options.VariableDependenceIgnore + ": " + e); } } @@ -520,7 +522,7 @@ private Dictionary> ComputeGlobalControlDependences() // Work out and union together local control dependences foreach (var Impl in prog.NonInlinedImplementations()) { - Graph blockGraph = prog.ProcessLoops(Impl); + Graph blockGraph = prog.ProcessLoops(options, Impl); LocalCtrlDeps[Impl] = blockGraph.ControlDependence(); foreach (var KeyValue in LocalCtrlDeps[Impl]) { @@ -528,7 +530,7 @@ private Dictionary> ComputeGlobalControlDependences() } } - Graph callGraph = Program.BuildCallGraph(prog); + Graph callGraph = Program.BuildCallGraph(options, prog); // Add inter-procedural control dependence nodes based on calls foreach (var Impl in prog.NonInlinedImplementations()) @@ -654,7 +656,7 @@ public HashSet DependsOn(VariableDescriptor v) { if (DependsOnSCCsDAG == null) { - if (CoreOptions.Clo.Trace) + if (options.Trace) { Console.WriteLine("Variable dependence: computing SCCs"); } @@ -690,7 +692,7 @@ public HashSet DependsOn(VariableDescriptor v) DependsOnSCCsDAG.AddEdge(VariableDescriptorToSCC[n], dummy); } - if (CoreOptions.Clo.Trace) + if (options.Trace) { Console.WriteLine("Variable dependence: SCCs computed!"); } diff --git a/Source/Core/Xml.cs b/Source/Core/Xml.cs index 6268c3019..be5618616 100644 --- a/Source/Core/Xml.cs +++ b/Source/Core/Xml.cs @@ -7,8 +7,8 @@ namespace Microsoft.Boogie { public class XmlSink { - string /*!*/ - filename; + string /*!*/ filename; + private CoreOptions options; [ContractInvariantMethod] void ObjectInvariant() @@ -23,10 +23,11 @@ public bool IsOpen get { return wr != null; } } - public XmlSink(string filename) + public XmlSink(CoreOptions options, string filename) { Contract.Requires(filename != null); this.filename = filename; + this.options = options; } /// @@ -49,7 +50,7 @@ public string Open() wr = XmlWriter.Create(filename, settings); wr.WriteStartDocument(); wr.WriteStartElement("boogie"); - wr.WriteAttributeString("version", CoreOptions.Clo.VersionNumber); + wr.WriteAttributeString("version", options.VersionNumber); wr.WriteAttributeString("commandLine", Environment.CommandLine); } cce.EndExpose(); diff --git a/Source/ExecutionEngine/CommandLineOptions.cs b/Source/ExecutionEngine/CommandLineOptions.cs index 756d52aa3..c371daeda 100644 --- a/Source/ExecutionEngine/CommandLineOptions.cs +++ b/Source/ExecutionEngine/CommandLineOptions.cs @@ -627,8 +627,8 @@ public bool Parse([Captured] string[] /*!*/ args) /// Boogie command-line options (other tools can subclass this class in order to support a /// superset of Boogie's options). /// - public class CommandLineOptions : CommandLineOptionEngine, ExecutionEngineOptions - { + public class CommandLineOptions : CommandLineOptionEngine, ExecutionEngineOptions { + public static CommandLineOptions FromArguments(params string[] arguments) { var result = new CommandLineOptions(); @@ -648,12 +648,6 @@ protected CommandLineOptions(string toolName, string descriptiveName) Contract.Requires(descriptiveName != null); } - public static void Install(CoreOptions options) - { - Contract.Requires(options != null); - CoreOptions.Clo = options; - } - // Flags and arguments public bool ExpectingModel => PrintErrorModel >= 1 || @@ -1927,7 +1921,7 @@ public override void ApplyDefaultOptions() Contract.Assume(XmlSink == null); // XmlSink is to be set here if (XmlSinkFilename != null) { - XmlSink = new XmlSink(XmlSinkFilename); + XmlSink = new XmlSink(this, XmlSinkFilename); } if (TheProverFactory == null) @@ -2030,7 +2024,7 @@ public int GetArgumentSeparatorIndex(string argList, int startIndex) return semicolonIndex; } - public string ProverHelp => TheProverFactory.BlankProverOptions().Help; + public string ProverHelp => TheProverFactory.BlankProverOptions(this).Help; public override string AttributeHelp => @"Boogie: The following attributes are supported by this version. diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index 7f1454d3c..fe13ae425 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -28,7 +28,6 @@ public interface OutputPrinter #endregion - public enum PipelineOutcome { Done, @@ -243,7 +242,7 @@ public static int AutoRequestId(string id) return -1; } - public static readonly VerificationResultCache Cache = new VerificationResultCache(); + public readonly VerificationResultCache Cache; static readonly MemoryCache programCache = new MemoryCache("ProgramCache"); @@ -256,12 +255,17 @@ public static Program CachedProgram(string programId) return result; } - public ExecutionEngine(ExecutionEngineOptions options) + public ExecutionEngine(ExecutionEngineOptions options, VerificationResultCache cache) { - this.Options = options; + Options = options; + Cache = cache; checkerPool = new CheckerPool(options); } + public static ExecutionEngine CreateWithoutSharedCache(ExecutionEngineOptions options) { + return new ExecutionEngine(options, new VerificationResultCache()); + } + public ExecutionEngineOptions Options { get; } private readonly CheckerPool checkerPool; @@ -298,7 +302,7 @@ public bool ProcessFiles(IList fileNames, bool lookForSnapshots = true, return snapshotsByVersion.All(s => { // BUG: Reusing checkers during snapshots doesn't work, even though it should. We create a new engine (and thus checker pool) to workaround this. - using var engine = new ExecutionEngine(Options); + using var engine = new ExecutionEngine(Options, Cache); return engine.ProcessFiles(new List(s), false, programId); }); } @@ -332,7 +336,7 @@ public bool ProcessProgram(Program program, string bplFileName, string programId if (Options.PrintCFGPrefix != null) { foreach (var impl in program.Implementations) { using StreamWriter sw = new StreamWriter(Options.PrintCFGPrefix + "." + impl.Name + ".dot"); - sw.Write(program.ProcessLoops(impl).ToDot()); + sw.Write(program.ProcessLoops(Options, impl).ToDot()); } } @@ -396,7 +400,6 @@ public static IList> LookForSnapshots(IList fileNames) return result; } - public void CoalesceBlocks(Program program) { if (Options.CoalesceBlocks) @@ -415,7 +418,7 @@ public void CollectModSets(Program program) { if (Options.DoModSetAnalysis) { - new ModSetCollector().DoModSetAnalysis(program); + new ModSetCollector(Options).DoModSetAnalysis(program); } } @@ -445,8 +448,8 @@ public static void PrintBplFile(ExecutionEngineOptions options, string filename, } using (TokenTextWriter writer = filename == "-" - ? new TokenTextWriter("", Console.Out, setTokens, pretty) - : new TokenTextWriter(filename, setTokens, pretty)) + ? new TokenTextWriter("", Console.Out, setTokens, pretty, options) + : new TokenTextWriter(filename, setTokens, pretty, options)) { if (options.ShowEnv != ExecutionEngineOptions.ShowEnvironment.Never) { @@ -567,7 +570,7 @@ public PipelineOutcome ResolveAndTypecheck(Program program, string bplFileName, return PipelineOutcome.Done; } - int errorCount = program.Resolve(); + int errorCount = program.Resolve(Options); if (errorCount != 0) { Console.WriteLine("{0} name resolution errors detected in {1}", errorCount, GetFileNameForConsole(Options, bplFileName)); @@ -586,7 +589,7 @@ public PipelineOutcome ResolveAndTypecheck(Program program, string bplFileName, return PipelineOutcome.TypeCheckingError; } - errorCount = program.Typecheck(); + errorCount = program.Typecheck(Options); if (errorCount != 0) { Console.WriteLine("{0} type checking errors detected in {1}", errorCount, GetFileNameForConsole(Options, bplFileName)); @@ -599,7 +602,7 @@ public PipelineOutcome ResolveAndTypecheck(Program program, string bplFileName, } else if (Options.Monomorphize) { - var monomorphizableStatus = Monomorphizer.Monomorphize(program); + var monomorphizableStatus = Monomorphizer.Monomorphize(Options, program); if (monomorphizableStatus == MonomorphizableStatus.Monomorphizable) { Options.TypeEncodingMethod = CoreOptions.TypeEncoding.Monomorphic; @@ -682,9 +685,9 @@ public void Inline(Program program) foreach (var impl in TopLevelDeclarations.OfType()) { - if (Options.UserWantsToCheckRoutine(impl.Name) && !impl.SkipVerification) + if (Options.UserWantsToCheckRoutine(impl.Name) && !impl.IsSkipVerification(Options)) { - Inliner.ProcessImplementation(program, impl); + Inliner.ProcessImplementation(Options, program, impl); } } @@ -736,7 +739,7 @@ public PipelineOutcome InferAndVerify( // to see lambdas, then it would be better to more lambda expansion until after inference.) if (Options.ExpandLambdas) { - LambdaHelper.ExpandLambdas(program); + LambdaHelper.ExpandLambdas(Options, program); if (Options.PrintFile != null && Options.PrintLambdaLifting) { PrintBplFile(Options.PrintFile, program, false, true, Options.PrettyPrint); @@ -749,7 +752,7 @@ public PipelineOutcome InferAndVerify( if (Options.UseAbstractInterpretation) { - AbstractInterpretation.NativeAbstractInterpretation.RunAbstractInterpretation(program); + new AbstractInterpretation.NativeAbstractInterpretation(Options).RunAbstractInterpretation(program); } #endregion @@ -764,12 +767,12 @@ public PipelineOutcome InferAndVerify( Dictionary> extractLoopMappingInfo = null; if (Options.ExtractLoops) { - extractLoopMappingInfo = program.ExtractLoops(); + extractLoopMappingInfo = program.ExtractLoops(Options); } if (Options.PrintInstrumented) { - program.Emit(new TokenTextWriter(Console.Out, Options.PrettyPrint)); + program.Emit(new TokenTextWriter(Console.Out, Options.PrettyPrint, Options)); } #endregion @@ -792,7 +795,7 @@ public PipelineOutcome InferAndVerify( var impls = program.Implementations.Where( impl => impl != null && Options.UserWantsToCheckRoutine(cce.NonNull(impl.Name)) && - !impl.SkipVerification); + !impl.IsSkipVerification(Options)); // operate on a stable copy, in case it gets updated while we're running Implementation[] stablePrioritizedImpls = null; @@ -801,7 +804,7 @@ public PipelineOutcome InferAndVerify( OtherDefinitionAxiomsCollector.Collect(Options, program.Axioms); DependencyCollector.Collect(Options, program); stablePrioritizedImpls = impls.OrderByDescending( - impl => impl.Priority != 1 ? impl.Priority : Cache.VerificationPriority(impl)).ToArray(); + impl => impl.Priority != 1 ? impl.Priority : Cache.VerificationPriority(impl, Options.RunDiagnosticsOnTimeout)).ToArray(); } else { @@ -812,12 +815,12 @@ public PipelineOutcome InferAndVerify( if (1 < Options.VerifySnapshots) { - CachedVerificationResultInjector.Inject(Options, program, stablePrioritizedImpls, requestId, programId, + CachedVerificationResultInjector.Inject(this, program, stablePrioritizedImpls, requestId, programId, out stats.CachingActionCounts); } #region Verify each implementation - program.DeclarationDependencies = Prune.ComputeDeclarationDependencies(program); + program.DeclarationDependencies = Prune.ComputeDeclarationDependencies(Options, program); var outputCollector = new OutputCollector(stablePrioritizedImpls); var outcome = PipelineOutcome.VerificationCompleted; @@ -1011,7 +1014,7 @@ private void VerifyImplementation(Program program, PipelineStatistics stats, Err var wasCached = false; if (0 < Options.VerifySnapshots) { - var cachedResults = Cache.Lookup(impl, out priority); + var cachedResults = Cache.Lookup(impl, Options.RunDiagnosticsOnTimeout, out priority); if (cachedResults != null && priority == Priority.SKIP) { if (Options.XmlSink != null) @@ -1126,7 +1129,7 @@ private void VerifyImplementation(Program program, PipelineStatistics stats, Err #region Process the verification results and statistics ProcessOutcome(verificationResult.Outcome, verificationResult.Errors, TimeIndication(verificationResult), stats, - output, impl.TimeLimit, er, verificationResult.ImplementationName, verificationResult.ImplementationToken, + output, impl.GetTimeLimit(Options), er, verificationResult.ImplementationName, verificationResult.ImplementationToken, verificationResult.RequestId, verificationResult.MessageIfVerifies, wasCached); ProcessErrors(verificationResult.Errors, verificationResult.Outcome, output, er, impl); diff --git a/Source/ExecutionEngine/VerificationResultCache.cs b/Source/ExecutionEngine/VerificationResultCache.cs index 022444728..78383e591 100644 --- a/Source/ExecutionEngine/VerificationResultCache.cs +++ b/Source/ExecutionEngine/VerificationResultCache.cs @@ -114,9 +114,9 @@ public Implementation Inject(Implementation implementation, Program programInCac var after = new List(); Expr assumedExpr = new LiteralExpr(Token.NoToken, false); var canUseSpecs = DependencyCollector.CanExpressOldSpecs(oldProc, Program, true); - if (canUseSpecs && oldProc.SignatureEquals(currentImplementation.Proc)) + if (canUseSpecs && oldProc.SignatureEquals(options, currentImplementation.Proc)) { - var always = Substituter.SubstitutionFromDictionary(currentImplementation.GetImplFormalMap(), true, + var always = Substituter.SubstitutionFromDictionary(currentImplementation.GetImplFormalMap(options), true, currentImplementation.Proc); var forOld = Substituter.SubstitutionFromDictionary(new Dictionary()); var clauses = oldProc.Requires.Select(r => @@ -142,7 +142,7 @@ public Implementation Inject(Implementation implementation, Program programInCac } if (options.TraceCachingForTesting || options.TraceCachingForBenchmarking) { - using var tokTxtWr = new TokenTextWriter("", Console.Out, false, false); + using var tokTxtWr = new TokenTextWriter("", Console.Out, false, false, options); var loc = currentImplementation.tok != null && currentImplementation.tok != Token.NoToken ? string.Format("{0}({1},{2})", currentImplementation.tok.filename, currentImplementation.tok.line, currentImplementation.tok.col) @@ -171,10 +171,10 @@ public Implementation Inject(Implementation implementation, Program programInCac return result; } - public static void Inject(ExecutionEngineOptions options, Program program, IEnumerable implementations, string requestId, + public static void Inject(ExecutionEngine engine, Program program, IEnumerable implementations, string requestId, string programId, out long[] cachingActionCounts) { - var eai = new CachedVerificationResultInjector(options, program); + var eai = new CachedVerificationResultInjector(engine.Options, program); cachingActionCounts = new long[Enum.GetNames(typeof(VC.ConditionGeneration.CachingAction)).Length]; var run = new CachedVerificationResultInjectorRun @@ -184,7 +184,7 @@ public static void Inject(ExecutionEngineOptions options, Program program, IEnum }; foreach (var impl in implementations) { - var vr = ExecutionEngine.Cache.Lookup(impl, out var priority); + var vr = engine.Cache.Lookup(impl, engine.Options.RunDiagnosticsOnTimeout, out var priority); if (vr != null && vr.ProgramId == programId) { if (priority == Priority.LOW) @@ -204,7 +204,7 @@ public static void Inject(ExecutionEngineOptions options, Program program, IEnum run.SkippedImplementationCount++; } - if (priority == Priority.LOW || priority == Priority.MEDIUM || options.VerifySnapshots >= 3) + if (priority == Priority.LOW || priority == Priority.MEDIUM || engine.Options.VerifySnapshots >= 3) { if (TimeThreshold < vr.End.Subtract(vr.Start).TotalMilliseconds) { @@ -260,9 +260,9 @@ public override Cmd VisitCallCmd(CallCmd node) Expr assumedExpr = new LiteralExpr(Token.NoToken, false); // TODO(wuestholz): Try out two alternatives: only do this for low priority implementations or not at all. var canUseSpecs = DependencyCollector.CanExpressOldSpecs(oldProc, Program); - if (canUseSpecs && oldProc.SignatureEquals(node.Proc)) + if (canUseSpecs && oldProc.SignatureEquals(options, node.Proc)) { - var desugaring = node.Desugaring; + var desugaring = node.GetDesugaring(options); Contract.Assert(desugaring != null); var precond = node.CheckedPrecondition(oldProc, Program, e => FunctionExtractor.Extract(e, Program, axioms)); if (precond != null) @@ -329,9 +329,9 @@ public override Cmd VisitCallCmd(CallCmd node) after.Add(assumed); } - node.ExtendDesugaring(before, beforePreconditionCheck, after); + node.ExtendDesugaring(options, before, beforePreconditionCheck, after); if (options.TraceCachingForTesting || options.TraceCachingForBenchmarking) { - using var tokTxtWr = new TokenTextWriter("", Console.Out, false, false); + using var tokTxtWr = new TokenTextWriter("", Console.Out, false, false, options); var loc = node.tok != null && node.tok != Token.NoToken ? string.Format("{0}({1},{2})", node.tok.filename, node.tok.line, node.tok.col) : ""; @@ -691,7 +691,7 @@ public void Insert(Implementation impl, VerificationResult result) } - public VerificationResult Lookup(Implementation impl, out int priority) + public VerificationResult Lookup(Implementation impl, bool runDiagnosticsOnTimeout, out int priority) { Contract.Requires(impl != null); @@ -708,7 +708,7 @@ public VerificationResult Lookup(Implementation impl, out int priority) { priority = Priority.LOW; } - else if (result.Outcome == ConditionGeneration.Outcome.TimedOut && CoreOptions.Clo.RunDiagnosticsOnTimeout) + else if (result.Outcome == ConditionGeneration.Outcome.TimedOut && runDiagnosticsOnTimeout) { priority = Priority.MEDIUM; } @@ -741,11 +741,11 @@ public void RemoveMatchingKeys(Regex keyRegexp) } - public int VerificationPriority(Implementation impl) + public int VerificationPriority(Implementation impl, bool runDiagnosticsOnTimeout) { Contract.Requires(impl != null); - Lookup(impl, out var priority); + Lookup(impl, runDiagnosticsOnTimeout, out var priority); return priority; } } diff --git a/Source/Houdini/AnnotationDependenceAnalyser.cs b/Source/Houdini/AnnotationDependenceAnalyser.cs index 4d9419eb8..efca404ff 100644 --- a/Source/Houdini/AnnotationDependenceAnalyser.cs +++ b/Source/Houdini/AnnotationDependenceAnalyser.cs @@ -29,7 +29,7 @@ public AnnotationDependenceAnalyser(HoudiniOptions options, Program prog) { this.options = options; this.prog = prog; - this.varDepAnalyser = new VariableDependenceAnalyser(prog); + this.varDepAnalyser = new VariableDependenceAnalyser(prog, options); varDepAnalyser.Analyse(); } @@ -121,7 +121,7 @@ private void ConstructAnnotationDependenceGraph() if (options.StagedHoudiniReachabilityAnalysis) { - reachabilityChecker = new AnnotationReachabilityChecker(prog, AllAnnotationIdentifiers()); + reachabilityChecker = new AnnotationReachabilityChecker(options, prog, AllAnnotationIdentifiers()); } else { @@ -900,11 +900,11 @@ private enum PrePost private IInterproceduralReachabilityGraph reachabilityGraph; private Dictionary> annotationToOccurences; - internal AnnotationReachabilityChecker(Program prog, IEnumerable AnnotationIdentifiers) + internal AnnotationReachabilityChecker(CoreOptions options, Program prog, IEnumerable AnnotationIdentifiers) { this.prog = prog; this.AnnotationIdentifiers = AnnotationIdentifiers; - this.reachabilityGraph = new InterproceduralReachabilityGraph(prog); + this.reachabilityGraph = new InterproceduralReachabilityGraph(prog, options); this.annotationToOccurences = new Dictionary>(); // Add all annotation occurrences in blocks diff --git a/Source/Houdini/Houdini.cs b/Source/Houdini/Houdini.cs index c0b4406c6..02b53c24e 100644 --- a/Source/Houdini/Houdini.cs +++ b/Source/Houdini/Houdini.cs @@ -438,7 +438,7 @@ protected void Initialize(Program program, HoudiniSession.HoudiniStatistics stat Console.WriteLine("Building call graph..."); } - this.callGraph = Program.BuildCallGraph(program); + this.callGraph = Program.BuildCallGraph(Options, program); if (Options.Trace) { Console.WriteLine("Number of implementations = {0}", callGraph.Nodes.Count); @@ -543,7 +543,7 @@ protected void Inline() { CoreOptions.Inlining savedOption = Options.ProcedureInlining; Options.ProcedureInlining = CoreOptions.Inlining.Spec; - Inliner.ProcessImplementationForHoudini(program, impl); + Inliner.ProcessImplementationForHoudini(Options, program, impl); Options.ProcedureInlining = savedOption; } @@ -969,7 +969,7 @@ protected bool UpdateAssignmentWorkList(ProverInterface.Outcome outcome, cexWriter.WriteLine("Counter example for " + refutedAnnotation.Constant); cexWriter.Write(error.ToString()); cexWriter.WriteLine(); - using var writer = new Microsoft.Boogie.TokenTextWriter(cexWriter, /*pretty=*/ false); + using var writer = new TokenTextWriter(cexWriter, false, Options); foreach (Microsoft.Boogie.Block blk in error.Trace) { blk.Emit(writer, 15); diff --git a/Source/Houdini/StagedHoudini.cs b/Source/Houdini/StagedHoudini.cs index a85b633b4..1661d5571 100644 --- a/Source/Houdini/StagedHoudini.cs +++ b/Source/Houdini/StagedHoudini.cs @@ -296,7 +296,7 @@ private List AcquireHoudiniInstance() private void EmitProgram(string filename) { - using TokenTextWriter writer = new TokenTextWriter(filename, true); + using TokenTextWriter writer = new TokenTextWriter(filename, true, options); int oldPrintUnstructured = options.PrintUnstructured; options.PrintUnstructured = 2; program.Emit(writer); diff --git a/Source/Predication/SmartBlockPredicator.cs b/Source/Predication/SmartBlockPredicator.cs index 838f08bd1..bc0c870d2 100644 --- a/Source/Predication/SmartBlockPredicator.cs +++ b/Source/Predication/SmartBlockPredicator.cs @@ -9,6 +9,7 @@ namespace Microsoft.Boogie { public class SmartBlockPredicator { + private CoreOptions options; Program prog; Implementation impl; Graph blockGraph; @@ -30,13 +31,14 @@ public class SmartBlockPredicator bool myUseProcedurePredicates; UniformityAnalyser uni; - SmartBlockPredicator(Program p, Implementation i, Func upp, UniformityAnalyser u) + SmartBlockPredicator(Program p, Implementation i, Func upp, UniformityAnalyser u, CoreOptions options) { prog = p; impl = i; useProcedurePredicates = upp; myUseProcedurePredicates = useProcedurePredicates(i.Proc); uni = u; + this.options = options; } void PredicateCmd(Expr p, Expr pDom, List blocks, Block block, Cmd cmd, out Block nextBlock) @@ -543,7 +545,7 @@ Block FindImmediateDominator(Block block) void PredicateImplementation() { - blockGraph = prog.ProcessLoops(impl); + blockGraph = prog.ProcessLoops(options, impl); sortedBlocks = blockGraph.LoopyTopSort(); AssignPredicates(); @@ -720,7 +722,7 @@ private Expr CreateIfFPThenElse(Expr then, Expr eElse) } } - public static void Predicate(Program p, + public void Predicate(Program p, Func useProcedurePredicates = null, UniformityAnalyser uni = null) { @@ -815,7 +817,7 @@ public static void Predicate(Program p, { try { - new SmartBlockPredicator(p, impl, useProcedurePredicates, uni).PredicateImplementation(); + new SmartBlockPredicator(p, impl, useProcedurePredicates, uni, options).PredicateImplementation(); foreach (AssignCmd c in newAssignCmds) { impl.Blocks.First().Cmds.Insert(0, c); @@ -844,7 +846,7 @@ public static void Predicate(Program p, { try { - new SmartBlockPredicator(p, impl, useProcedurePredicates, uni).PredicateImplementation(); + new SmartBlockPredicator(p, impl, useProcedurePredicates, uni, options).PredicateImplementation(); } catch (Program.IrreducibleLoopException) { @@ -855,11 +857,11 @@ public static void Predicate(Program p, } } - public static void Predicate(Program p, Implementation impl) + public static void Predicate(CoreOptions options, Program p, Implementation impl) { try { - new SmartBlockPredicator(p, impl, proc => false, null).PredicateImplementation(); + new SmartBlockPredicator(p, impl, proc => false, null, options).PredicateImplementation(); } catch (Program.IrreducibleLoopException) { diff --git a/Source/Predication/UniformityAnalyser.cs b/Source/Predication/UniformityAnalyser.cs index 23bbca2fa..8dcee3051 100644 --- a/Source/Predication/UniformityAnalyser.cs +++ b/Source/Predication/UniformityAnalyser.cs @@ -8,6 +8,7 @@ namespace Microsoft.Boogie { public class UniformityAnalyser { + private CoreOptions options; private Program prog; private bool doAnalysis; @@ -37,9 +38,9 @@ public class UniformityAnalyser /// being used then blocks will only be merged if they are both uniform /// or both non-uniform /// - public static void MergeBlocksIntoPredecessors(Program prog, Implementation impl, UniformityAnalyser uni) + public void MergeBlocksIntoPredecessors(Program prog, Implementation impl, UniformityAnalyser uni) { - var blockGraph = prog.ProcessLoops(impl); + var blockGraph = prog.ProcessLoops(options, impl); var predMap = new Dictionary(); foreach (var block in blockGraph.Nodes) { @@ -71,12 +72,13 @@ public static void MergeBlocksIntoPredecessors(Program prog, Implementation impl } public UniformityAnalyser(Program prog, bool doAnalysis, ISet entryPoints, - IEnumerable nonUniformVars) + IEnumerable nonUniformVars, CoreOptions options) { this.prog = prog; this.doAnalysis = doAnalysis; this.entryPoints = entryPoints; this.nonUniformVars = nonUniformVars; + this.options = options; uniformityInfo = new Dictionary>>(); nonUniformLoops = new Dictionary>(); nonUniformBlocks = new Dictionary>(); @@ -263,7 +265,7 @@ private void Analyse(Implementation Impl, bool ControlFlowIsUniform) return; } - Graph blockGraph = prog.ProcessLoops(Impl); + Graph blockGraph = prog.ProcessLoops(options, Impl); var ctrlDep = blockGraph.ControlDependence(); // Compute transitive closure of control dependence info. diff --git a/Source/Provers/SMTLib/ProverContext.cs b/Source/Provers/SMTLib/ProverContext.cs index f63908d7f..fdc3f18b6 100644 --- a/Source/Provers/SMTLib/ProverContext.cs +++ b/Source/Provers/SMTLib/ProverContext.cs @@ -122,6 +122,7 @@ public override object Clone() /// public class DeclFreeProverContext : ProverContext { + private SMTLibOptions options; protected VCExpressionGenerator gen; protected VCGenerationOptions genOptions; protected Boogie2VCExprTranslator translator; @@ -146,12 +147,13 @@ public VCExprTranslator /*?*/ exprTranslator; public DeclFreeProverContext(VCExpressionGenerator gen, - VCGenerationOptions genOptions) + VCGenerationOptions genOptions, SMTLibOptions options) { Contract.Requires(gen != null); Contract.Requires(genOptions != null); this.gen = gen; this.genOptions = genOptions; + this.options = options; Boogie2VCExprTranslator t = new Boogie2VCExprTranslator(gen, genOptions); this.translator = t; @@ -187,6 +189,7 @@ public override void Clear() protected DeclFreeProverContext(DeclFreeProverContext ctxt) { Contract.Requires(ctxt != null); + this.options = ctxt.options; this.gen = ctxt.gen; this.genOptions = ctxt.genOptions; Boogie2VCExprTranslator t = (Boogie2VCExprTranslator) ctxt.translator.Clone(); @@ -265,7 +268,7 @@ public VCExpr Axioms } axioms = gen.AndSimp(gen.Distinct(distinctVars), axioms); - if (CoreOptions.Clo.TypeEncodingMethod != CoreOptions.TypeEncoding.Monomorphic) + if (options.TypeEncodingMethod != CoreOptions.TypeEncoding.Monomorphic) { axioms = gen.AndSimp(orderingAxiomBuilder.Axioms, axioms); } diff --git a/Source/Provers/SMTLib/ProverInterface.cs b/Source/Provers/SMTLib/ProverInterface.cs index f7a634d35..a7a02c817 100644 --- a/Source/Provers/SMTLib/ProverInterface.cs +++ b/Source/Provers/SMTLib/ProverInterface.cs @@ -9,12 +9,13 @@ namespace Microsoft.Boogie; public abstract class ProverInterface { - public static ProverInterface CreateProver(SMTLibOptions libOptions, Program prog, string /*?*/ logFilePath, bool appendLogFile, uint timeout, + public static ProverInterface CreateProver(SMTLibOptions libOptions, Program prog, + string /*?*/ logFilePath, bool appendLogFile, uint timeout, int taskID = -1) { Contract.Requires(prog != null); - ProverOptions options = cce.NonNull(libOptions.TheProverFactory).BlankProverOptions(); + ProverOptions options = cce.NonNull(libOptions.TheProverFactory).BlankProverOptions(libOptions); if (logFilePath != null) { diff --git a/Source/Provers/SMTLib/ProverUtil.cs b/Source/Provers/SMTLib/ProverUtil.cs index ca558181f..3d0e6f992 100644 --- a/Source/Provers/SMTLib/ProverUtil.cs +++ b/Source/Provers/SMTLib/ProverUtil.cs @@ -9,8 +9,8 @@ namespace Microsoft.Boogie { public class ProverOptions { - public string /*?*/ - LogFilename = null; + public SMTLibOptions LibOptions { get; } + public string /*?*/ LogFilename = null; public bool AppendLogFile = false; @@ -31,6 +31,11 @@ public string /*?*/ private string /*!*/ stringRepr = ""; + public ProverOptions(SMTLibOptions libOptions) + { + this.LibOptions = libOptions; + } + [ContractInvariantMethod] void ObjectInvariant() { @@ -172,7 +177,7 @@ private string ConfirmProverPath(string proverPath) Contract.Requires(proverPath != null); Contract.Ensures(confirmedProverPath != null); confirmedProverPath = proverPath; - if (CoreOptions.Clo.Trace) + if (LibOptions.Trace) { Console.WriteLine("[TRACE] Using prover: " + confirmedProverPath); } @@ -306,10 +311,10 @@ public abstract class ProverFactory // Really returns ProverContext public abstract ProverContext /*!*/ NewProverContext(ProverOptions /*!*/ options); - public virtual ProverOptions BlankProverOptions() + public virtual ProverOptions BlankProverOptions(SMTLibOptions libOptions) { Contract.Ensures(Contract.Result() != null); - return new ProverOptions(); + return new ProverOptions(libOptions); } // return true if the prover supports DAG AST as opposed to LET AST diff --git a/Source/Provers/SMTLib/SMTLibLineariser.cs b/Source/Provers/SMTLib/SMTLibLineariser.cs index c3e202d15..17f091aae 100644 --- a/Source/Provers/SMTLib/SMTLibLineariser.cs +++ b/Source/Provers/SMTLib/SMTLibLineariser.cs @@ -166,7 +166,7 @@ private void TypeToStringHelper(Type t, StringBuilder sb) else { System.IO.StringWriter buffer = new System.IO.StringWriter(); - using (TokenTextWriter stream = new TokenTextWriter("", buffer, /*setTokens=*/false, /*pretty=*/false) + using (TokenTextWriter stream = new TokenTextWriter("", buffer, false, false, LibOptions) ) { t.Emit(stream); diff --git a/Source/Provers/SMTLib/SMTLibProcessTheoremProver.cs b/Source/Provers/SMTLib/SMTLibProcessTheoremProver.cs index 83896518e..cf235f2e1 100644 --- a/Source/Provers/SMTLib/SMTLibProcessTheoremProver.cs +++ b/Source/Provers/SMTLib/SMTLibProcessTheoremProver.cs @@ -109,14 +109,14 @@ private void SetupAxiomBuilder(VCExpressionGenerator gen) switch (libOptions.TypeEncodingMethod) { case CoreOptions.TypeEncoding.Arguments: - AxBuilder = new TypeAxiomBuilderArguments(gen); + AxBuilder = new TypeAxiomBuilderArguments(gen, libOptions); AxBuilder.Setup(); break; case CoreOptions.TypeEncoding.Monomorphic: AxBuilder = null; break; default: - AxBuilder = new TypeAxiomBuilderPremisses(gen); + AxBuilder = new TypeAxiomBuilderPremisses(gen, libOptions); AxBuilder.Setup(); break; } @@ -2422,8 +2422,8 @@ public class SMTLibProverContext : DeclFreeProverContext public readonly Dictionary DefinedFunctions = new Dictionary(); public SMTLibProverContext(VCExpressionGenerator gen, - VCGenerationOptions genOptions) - : base(gen, genOptions) + VCGenerationOptions genOptions, SMTLibOptions options) + : base(gen, genOptions, options) { } @@ -2519,12 +2519,12 @@ public override ProverContext NewProverContext(ProverOptions options) } VCGenerationOptions genOptions = new VCGenerationOptions(proverCommands); - return new SMTLibProverContext(gen, genOptions); + return new SMTLibProverContext(gen, genOptions, options.LibOptions); } - public override ProverOptions BlankProverOptions() + public override ProverOptions BlankProverOptions(SMTLibOptions libOptions) { - return new SMTLibProverOptions(); + return new SMTLibProverOptions(libOptions); } protected virtual SMTLibProcessTheoremProver SpawnProver(SMTLibOptions libOptions, ProverOptions options, diff --git a/Source/Provers/SMTLib/SMTLibProverOptions.cs b/Source/Provers/SMTLib/SMTLibProverOptions.cs index e308d97cc..cbf8f6e13 100644 --- a/Source/Provers/SMTLib/SMTLibProverOptions.cs +++ b/Source/Provers/SMTLib/SMTLibProverOptions.cs @@ -45,6 +45,10 @@ public class SMTLibProverOptions : ProverOptions // Z3 specific (at the moment; some of them make sense also for other provers) public string Inspector = null; + public SMTLibProverOptions(SMTLibOptions libOptions) : base(libOptions) + { + } + public void AddSolverArgument(string s) { SolverArguments.Add(s); diff --git a/Source/UnitTests/CoreTests/Duplicator.cs b/Source/UnitTests/CoreTests/Duplicator.cs index 44d6ccb7a..ac9cc137c 100644 --- a/Source/UnitTests/CoreTests/Duplicator.cs +++ b/Source/UnitTests/CoreTests/Duplicator.cs @@ -119,8 +119,8 @@ public void WholeProgram() [Test()] public void GotoTargets() { - CoreOptions.Clo = new CommandLineOptions(); - Program p = TestUtil.ProgramLoader.LoadProgramFrom(@" + var options = CommandLineOptions.FromArguments(); + Program p = TestUtil.ProgramLoader.LoadProgramFrom(options, @" procedure main() { entry: @@ -186,7 +186,8 @@ procedure main() [Test()] public void ImplementationProcedureResolving() { - Program p = TestUtil.ProgramLoader.LoadProgramFrom(@" + var options = CommandLineOptions.FromArguments(); + Program p = TestUtil.ProgramLoader.LoadProgramFrom(options, @" procedure main(a:int) returns (r:int); requires a > 0; ensures r > a; @@ -206,7 +207,7 @@ implementation main(a:int) returns (r:int) var newProgram = (Program) d.Visit(p); // Resolving doesn't seem to fix this. - var rc = new ResolutionContext(this); + var rc = new ResolutionContext(this, options); newProgram.Resolve(rc); // Check resolved @@ -218,8 +219,8 @@ implementation main(a:int) returns (r:int) [Test()] public void CallCmdResolving() { - CoreOptions.Clo = new CommandLineOptions(); - Program p = TestUtil.ProgramLoader.LoadProgramFrom(@" + var options = CommandLineOptions.FromArguments(); + Program p = TestUtil.ProgramLoader.LoadProgramFrom(options, @" procedure main() { var x:int; diff --git a/Source/UnitTests/CoreTests/ExprImmutability.cs b/Source/UnitTests/CoreTests/ExprImmutability.cs index bd64db0c1..b22fa61e8 100644 --- a/Source/UnitTests/CoreTests/ExprImmutability.cs +++ b/Source/UnitTests/CoreTests/ExprImmutability.cs @@ -150,11 +150,12 @@ private NAryExpr GetUnTypedImmutableNAry() [Test()] public void ProtectedExprType() { + var options = CommandLineOptions.FromArguments(); var e = GetUnTypedImmutableNAry(); // Now Typecheck // Even though it's immutable we allow the TypeCheck field to be set if the Expr has never been type checked - var TC = new TypecheckingContext(this); + var TC = new TypecheckingContext(this, options); e.Typecheck(TC); Assert.IsNotNull(e.Type); Assert.IsTrue(e.Type.IsBool); @@ -163,11 +164,12 @@ public void ProtectedExprType() [Test()] public void ProtectedExprChangeTypeFail() { + var options = CommandLineOptions.FromArguments(); var e = GetUnTypedImmutableNAry(); // Now Typecheck // Even though it's immutable we allow the TypeCheck field to be set if the Expr has never been type checked - var TC = new TypecheckingContext(this); + var TC = new TypecheckingContext(this, options); e.Typecheck(TC); Assert.IsNotNull(e.Type); Assert.IsTrue(e.Type.IsBool); @@ -179,11 +181,12 @@ public void ProtectedExprChangeTypeFail() [Test()] public void ProtectedExprTypeChangeTypeSucceed() { + var options = CommandLineOptions.FromArguments(); var e = GetUnTypedImmutableNAry(); // Now Typecheck // Even though it's immutable we allow the TypeCheck field to be set if the Expr has never been type checked - var TC = new TypecheckingContext(this); + var TC = new TypecheckingContext(this, options); e.Typecheck(TC); Assert.IsNotNull(e.Type); Assert.IsTrue(e.Type.IsBool); diff --git a/Source/UnitTests/CoreTests/ExprTypeChecking.cs b/Source/UnitTests/CoreTests/ExprTypeChecking.cs index fe86fd22f..664d64eb1 100644 --- a/Source/UnitTests/CoreTests/ExprTypeChecking.cs +++ b/Source/UnitTests/CoreTests/ExprTypeChecking.cs @@ -12,6 +12,7 @@ public class ExprTypeChecking : IErrorSink [Test()] public void FunctionCall() { + var options = CommandLineOptions.FromArguments(); var fc = CreateFunctionCall("bv8slt", Microsoft.Boogie.Type.Bool, new List() { BasicType.GetBvType(8), @@ -27,7 +28,7 @@ public void FunctionCall() // Deep type check (this was not broken before writing this test) Assert.IsNull(nary.Type); - var tc = new TypecheckingContext(this); + var tc = new TypecheckingContext(this, options); nary.Typecheck(tc); Assert.AreEqual(BasicType.Bool, nary.Type); @@ -36,11 +37,12 @@ public void FunctionCall() [Test()] public void FunctionCallTypeResolved() { + var options = CommandLineOptions.FromArguments(); // This test case requires that function calls have been resolved // correctly and that the ShallowType is correct. // It's simpler to let the parser and resolver do all the work here // than try to build all the objects manually. - var program = TestUtil.ProgramLoader.LoadProgramFrom(@" + var program = TestUtil.ProgramLoader.LoadProgramFrom(options, @" function {:bvbuiltin ""bvugt""} bv8ugt(bv8,bv8) returns(bool); procedure main(a:bv8) diff --git a/Source/UnitTests/ExecutionEngineTests/CancellationTests.cs b/Source/UnitTests/ExecutionEngineTests/CancellationTests.cs index 8007b83d4..9e9b09796 100644 --- a/Source/UnitTests/ExecutionEngineTests/CancellationTests.cs +++ b/Source/UnitTests/ExecutionEngineTests/CancellationTests.cs @@ -25,8 +25,7 @@ public Program GetProgram(ExecutionEngine engine, string code) { [Test] public async Task InferAndVerifyCanBeCancelledWhileWaitingForProver() { var options = CommandLineOptions.FromArguments(); - CommandLineOptions.Install(options); - using var executionEngine = new ExecutionEngine(options); + using var executionEngine = ExecutionEngine.CreateWithoutSharedCache(options); var infiniteProgram = GetProgram(executionEngine, slow); var terminatingProgram = GetProgram(executionEngine, fast); diff --git a/Source/UnitTests/ExecutionEngineTests/GetProverLogs.cs b/Source/UnitTests/ExecutionEngineTests/GetProverLogs.cs index 17946ff28..4b352360c 100644 --- a/Source/UnitTests/ExecutionEngineTests/GetProverLogs.cs +++ b/Source/UnitTests/ExecutionEngineTests/GetProverLogs.cs @@ -10,7 +10,6 @@ public static class GetProverLogs { public static string GetProverLogForProgram(ExecutionEngineOptions options, string procedure) { - CommandLineOptions.Install(options); var logs = GetProverLogsForProgram(options, procedure).ToList(); Assert.AreEqual(1, logs.Count); return logs[0]; @@ -18,7 +17,7 @@ public static string GetProverLogForProgram(ExecutionEngineOptions options, stri public static IEnumerable GetProverLogsForProgram(ExecutionEngineOptions options, string procedure1) { - using var engine = new ExecutionEngine(options); + using var engine = ExecutionEngine.CreateWithoutSharedCache(options); ExecutionEngine.printer = new ConsolePrinter(engine.Options); var defines = new List() { "FILE_0" }; diff --git a/Source/UnitTests/TestUtil/ProgramLoader.cs b/Source/UnitTests/TestUtil/ProgramLoader.cs index 6ce39b1d4..734ff8f7e 100644 --- a/Source/UnitTests/TestUtil/ProgramLoader.cs +++ b/Source/UnitTests/TestUtil/ProgramLoader.cs @@ -5,7 +5,7 @@ namespace TestUtil { public class ProgramLoader { - public static Program LoadProgramFrom(string programText, string fileName = "file.bpl") + public static Program LoadProgramFrom(CoreOptions options, string programText, string fileName = "file.bpl") { Assert.That(programText, Is.Not.Null.And.Not.Empty); Assert.That(fileName, Is.Not.Null.And.Not.Empty); @@ -18,11 +18,11 @@ public static Program LoadProgramFrom(string programText, string fileName = "fil Assert.IsNotNull(p); // Resolve - errors = p.Resolve(); + errors = p.Resolve(options); Assert.AreEqual(0, errors); // Type check - errors = p.Typecheck(); + errors = p.Typecheck(options); Assert.AreEqual(0, errors); return p; diff --git a/Source/VCExpr/TypeErasureArguments.cs b/Source/VCExpr/TypeErasureArguments.cs index a6ba78bd5..bc4615d20 100644 --- a/Source/VCExpr/TypeErasureArguments.cs +++ b/Source/VCExpr/TypeErasureArguments.cs @@ -11,10 +11,12 @@ namespace Microsoft.Boogie.TypeErasure { public class TypeAxiomBuilderArguments : TypeAxiomBuilderIntBoolU { - public TypeAxiomBuilderArguments(VCExpressionGenerator gen) + private CoreOptions options; + public TypeAxiomBuilderArguments(VCExpressionGenerator gen, CoreOptions options) : base(gen) { Contract.Requires(gen != null); + this.options = options; Typed2UntypedFunctions = new Dictionary(); } @@ -25,6 +27,7 @@ internal TypeAxiomBuilderArguments(TypeAxiomBuilderArguments builder) : base(builder) { Contract.Requires(builder != null); + this.options = builder.options; Typed2UntypedFunctions = new Dictionary(builder.Typed2UntypedFunctions); @@ -33,7 +36,7 @@ internal TypeAxiomBuilderArguments(TypeAxiomBuilderArguments builder) builder.MapTypeAbstracterAttr == null ? null : new MapTypeAbstractionBuilderArguments(this, builder.Gen, - builder.MapTypeAbstracterAttr); + builder.MapTypeAbstracterAttr, options); } public override Object Clone() @@ -77,7 +80,7 @@ internal override MapTypeAbstractionBuilder /*!*/ MapTypeAbstracter if (MapTypeAbstracterAttr == null) { - MapTypeAbstracterAttr = new MapTypeAbstractionBuilderArguments(this, Gen); + MapTypeAbstracterAttr = new MapTypeAbstractionBuilderArguments(this, Gen, options); } return MapTypeAbstracterAttr; @@ -157,6 +160,7 @@ public Function Typed2Untyped(Function fun) internal class MapTypeAbstractionBuilderArguments : MapTypeAbstractionBuilder { + private CoreOptions options; private readonly TypeAxiomBuilderArguments /*!*/ AxBuilderArguments; @@ -167,24 +171,26 @@ void ObjectInvariant() } - internal MapTypeAbstractionBuilderArguments(TypeAxiomBuilderArguments axBuilder, VCExpressionGenerator gen) + internal MapTypeAbstractionBuilderArguments(TypeAxiomBuilderArguments axBuilder, VCExpressionGenerator gen, CoreOptions options) : base(axBuilder, gen) { Contract.Requires(gen != null); Contract.Requires(axBuilder != null); this.AxBuilderArguments = axBuilder; + this.options = options; } // constructor for cloning internal MapTypeAbstractionBuilderArguments(TypeAxiomBuilderArguments axBuilder, VCExpressionGenerator gen, - MapTypeAbstractionBuilderArguments builder) + MapTypeAbstractionBuilderArguments builder, CoreOptions options) : base(axBuilder, gen, builder) { Contract.Requires(builder != null); Contract.Requires(gen != null); Contract.Requires(axBuilder != null); this.AxBuilderArguments = axBuilder; + this.options = options; } //////////////////////////////////////////////////////////////////////////// @@ -243,7 +249,7 @@ protected override void GenSelectStoreFunctions(MapType abstractedType, TypeCtor select = HelperFuns.BoogieFunction(baseName + "Select", selectTypes); store = HelperFuns.BoogieFunction(baseName + "Store", storeTypes); - if (CoreOptions.Clo.UseArrayTheory) + if (options.UseArrayTheory) { select.AddAttribute("builtin", "select"); store.AddAttribute("builtin", "store"); diff --git a/Source/VCExpr/TypeErasurePremisses.cs b/Source/VCExpr/TypeErasurePremisses.cs index f0b963133..9338f37e1 100644 --- a/Source/VCExpr/TypeErasurePremisses.cs +++ b/Source/VCExpr/TypeErasurePremisses.cs @@ -58,10 +58,13 @@ public UntypedFunction(Function /*!*/ fun, public class TypeAxiomBuilderPremisses : TypeAxiomBuilderIntBoolU { - public TypeAxiomBuilderPremisses(VCExpressionGenerator gen) + public CoreOptions Options { get; } + + public TypeAxiomBuilderPremisses(VCExpressionGenerator gen, CoreOptions options) : base(gen) { Contract.Requires(gen != null); + this.Options = options; TypeFunction = HelperFuns.BoogieFunction("dummy", Type.Int); Typed2UntypedFunctions = new Dictionary(); @@ -74,6 +77,7 @@ internal TypeAxiomBuilderPremisses(TypeAxiomBuilderPremisses builder) : base(builder) { Contract.Requires(builder != null); + this.Options = builder.Options; TypeFunction = builder.TypeFunction; Typed2UntypedFunctions = new Dictionary(builder.Typed2UntypedFunctions); @@ -81,8 +85,7 @@ internal TypeAxiomBuilderPremisses(TypeAxiomBuilderPremisses builder) MapTypeAbstracterAttr = builder.MapTypeAbstracterAttr == null ? null - : new MapTypeAbstractionBuilderPremisses(this, builder.Gen, - builder.MapTypeAbstracterAttr); + : new MapTypeAbstractionBuilderPremisses(this, builder.Gen, builder.MapTypeAbstracterAttr); } public override Object Clone() @@ -628,8 +631,7 @@ public VCExpr GenVarTypeAxiom(VCExprVar var, Type originalType, internal class MapTypeAbstractionBuilderPremisses : MapTypeAbstractionBuilder { - private readonly TypeAxiomBuilderPremisses /*!*/ - AxBuilderPremisses; + private readonly TypeAxiomBuilderPremisses /*!*/ AxBuilderPremisses; [ContractInvariantMethod] void ObjectInvariant() @@ -729,7 +731,7 @@ protected override void GenSelectStoreFunctions(MapType abstractedType, TypeCtor // the store function does not have any explicit type parameters Contract.Assert(explicitStoreParams.Count == 0); - if (CoreOptions.Clo.UseArrayTheory) + if (AxBuilderPremisses.Options.UseArrayTheory) { select.AddAttribute("builtin", "select"); store.AddAttribute("builtin", "store"); @@ -1120,8 +1122,7 @@ private VCExpr GenMapAxiom1(Function select, Function store, Type mapResult, public class TypeEraserPremisses : TypeEraser { - private readonly TypeAxiomBuilderPremisses /*!*/ - AxBuilderPremisses; + private readonly TypeAxiomBuilderPremisses /*!*/ AxBuilderPremisses; [ContractInvariantMethod] void ObjectInvariant() @@ -1343,7 +1344,7 @@ private VCExpr HandleQuantifier(VCExprQuantifier node, List /*! List /*!*/ newVarsWithTypeSpecs = new List(); if (!IsUniversalQuantifier(node) || - CoreOptions.Clo.TypeEncodingMethod + AxBuilderPremisses.Options.TypeEncodingMethod == CoreOptions.TypeEncoding.Predicates) { foreach (VCExprVar /*!*/ oldVar in occurringVars) diff --git a/Source/VCExpr/VCExprAST.cs b/Source/VCExpr/VCExprAST.cs index 463074bdd..3e2136721 100644 --- a/Source/VCExpr/VCExprAST.cs +++ b/Source/VCExpr/VCExprAST.cs @@ -141,7 +141,7 @@ public override string ToString() { Contract.Ensures(Contract.Result() != null); StringWriter sw = new StringWriter(); - VCExprPrinter printer = new VCExprPrinter(); + VCExprPrinter printer = new VCExprPrinter(PrintOptions.Default); printer.Print(this, sw); return cce.NonNull(sw.ToString()); } diff --git a/Source/VCExpr/VCExprASTPrinter.cs b/Source/VCExpr/VCExprASTPrinter.cs index 6d6ff0372..339fd43ca 100644 --- a/Source/VCExpr/VCExprASTPrinter.cs +++ b/Source/VCExpr/VCExprASTPrinter.cs @@ -10,6 +10,12 @@ namespace Microsoft.Boogie.VCExprAST public class VCExprPrinter : IVCExprVisitor { private VCExprOpPrinter OpPrinterVar = null; + public PrintOptions Options { get; } + + public VCExprPrinter(PrintOptions options) + { + Options = options; + } private VCExprOpPrinter /*!*/ OpPrinter { @@ -206,8 +212,7 @@ public bool Visit(VCExprLet node, TextWriter wr) public class VCExprOpPrinter : IVCExprOpVisitor { - private VCExprPrinter /*!*/ - ExprPrinter; + private VCExprPrinter /*!*/ ExprPrinter; [ContractInvariantMethod] void ObjectInvariant() @@ -416,7 +421,7 @@ public bool VisitAddOp(VCExprNAry node, TextWriter wr) { //Contract.Requires(wr != null); //Contract.Requires(node != null); - if (CoreOptions.Clo.ReflectAdd) + if (ExprPrinter.Options.ReflectAdd) { return PrintNAry("Reflect$Add", node, wr); } diff --git a/Source/VCGeneration/Checker.cs b/Source/VCGeneration/Checker.cs index 7a784567d..7db69df1d 100644 --- a/Source/VCGeneration/Checker.cs +++ b/Source/VCGeneration/Checker.cs @@ -61,6 +61,12 @@ public void GetReady() public void GoBackToIdle() { Contract.Requires(IsBusy); + if (Options.ModelViewFile != null) { + // Don't re-use theorem provers whose ProverContext still needs to be queried to extract model data. + Pool.CheckerDied(); + Close(); + return; + } status = CheckerStatus.Idle; var becameIdle = thmProver.GoBackToIdle().Wait(TimeSpan.FromMilliseconds(100)); @@ -101,7 +107,7 @@ public Checker(CheckerPool pool, string /*?*/ logFilePath, bool appendLogFile) { Pool = pool; - SolverOptions = cce.NonNull(Pool.Options.TheProverFactory).BlankProverOptions(); + SolverOptions = cce.NonNull(Pool.Options.TheProverFactory).BlankProverOptions(pool.Options); if (logFilePath != null) { diff --git a/Source/VCGeneration/ConditionGeneration.cs b/Source/VCGeneration/ConditionGeneration.cs index e2c5f4bad..1a27a9efb 100644 --- a/Source/VCGeneration/ConditionGeneration.cs +++ b/Source/VCGeneration/ConditionGeneration.cs @@ -123,7 +123,7 @@ public Outcome VerifyImplementation(Implementation impl, out List i != null)); Contract.Ensures(Contract.Result() != Outcome.Errors || errors != null); Contract.EnsuresOnThrow(true); - Helpers.ExtraTraceInformation("Starting implementation verification"); + Helpers.ExtraTraceInformation(Options, "Starting implementation verification"); CounterexampleCollector collector = new CounterexampleCollector(Options); collector.RequestId = requestId; @@ -138,7 +138,7 @@ public Outcome VerifyImplementation(Implementation impl, out List", Console.Out, /*setTokens=*/ false, /*pretty=*/ false); + debugWriter = new TokenTextWriter("", Console.Out, /*setTokens=*/ false, /*pretty=*/ false, options); debugWriter.WriteLine("Effective precondition:"); } - Substitution formalProcImplSubst = Substituter.SubstitutionFromDictionary(impl.GetImplFormalMap()); + Substitution formalProcImplSubst = Substituter.SubstitutionFromDictionary(impl.GetImplFormalMap(options)); string blockLabel = "PreconditionGeneratedEntry"; Block origStartBlock = impl.Blocks[0]; @@ -295,11 +295,11 @@ protected static void InjectPostConditions(VCGenOptions options, Implementation TokenTextWriter debugWriter = null; if (options.PrintWithUniqueASTIds) { - debugWriter = new TokenTextWriter("", Console.Out, /*setTokens=*/ false, /*pretty=*/ false); + debugWriter = new TokenTextWriter("", Console.Out, /*setTokens=*/ false, /*pretty=*/ false, options); debugWriter.WriteLine("Effective postcondition:"); } - Substitution formalProcImplSubst = Substituter.SubstitutionFromDictionary(impl.GetImplFormalMap()); + Substitution formalProcImplSubst = Substituter.SubstitutionFromDictionary(impl.GetImplFormalMap(options)); // (free and checked) ensures clauses foreach (Ensures ens in impl.Proc.Ensures) @@ -351,11 +351,11 @@ protected static List GetPre(VCGenOptions options, Implementation impl) TokenTextWriter debugWriter = null; if (options.PrintWithUniqueASTIds) { - debugWriter = new TokenTextWriter("", Console.Out, /*setTokens=*/ false, /*pretty=*/ false); + debugWriter = new TokenTextWriter("", Console.Out, /*setTokens=*/ false, /*pretty=*/ false, options); debugWriter.WriteLine("Effective precondition:"); } - Substitution formalProcImplSubst = Substituter.SubstitutionFromDictionary(impl.GetImplFormalMap()); + Substitution formalProcImplSubst = Substituter.SubstitutionFromDictionary(impl.GetImplFormalMap(options)); List pre = new List(); // (free and checked) requires clauses @@ -397,7 +397,7 @@ protected static List GetPost(VCGenOptions options, Implementation impl) } // Construct an Expr for the post-condition - Substitution formalProcImplSubst = Substituter.SubstitutionFromDictionary(impl.GetImplFormalMap()); + Substitution formalProcImplSubst = Substituter.SubstitutionFromDictionary(impl.GetImplFormalMap(options)); List post = new List(); foreach (Ensures ens in impl.Proc.Ensures) { @@ -414,7 +414,7 @@ protected static List GetPost(VCGenOptions options, Implementation impl) if (options.PrintWithUniqueASTIds) { - c.Emit(new TokenTextWriter("", Console.Out, /*setTokens=*/ false, /*pretty=*/ false), 1); + c.Emit(new TokenTextWriter("", Console.Out, /*setTokens=*/ false, /*pretty=*/ false, options), 1); } } } @@ -441,11 +441,11 @@ protected static List GetParamWhereClauses(VCGenOptions options, Implementa TokenTextWriter debugWriter = null; if (options.PrintWithUniqueASTIds) { - debugWriter = new TokenTextWriter("", Console.Out, /*setTokens=*/ false, /*pretty=*/ false); + debugWriter = new TokenTextWriter("", Console.Out, /*setTokens=*/ false, /*pretty=*/ false, options); debugWriter.WriteLine("Effective precondition from where-clauses:"); } - Substitution formalProcImplSubst = Substituter.SubstitutionFromDictionary(impl.GetImplFormalMap()); + Substitution formalProcImplSubst = Substituter.SubstitutionFromDictionary(impl.GetImplFormalMap(options)); List whereClauses = new List(); // where clauses of in-parameters @@ -565,7 +565,7 @@ public static void EmitImpl(VCGenOptions options, Implementation impl, bool prin options.PrintUnstructured = 2; // print only the unstructured program bool oldPrintDesugaringSetting = options.PrintDesugarings; options.PrintDesugarings = printDesugarings; - impl.Emit(new TokenTextWriter("", Console.Out, /*setTokens=*/ false, /*pretty=*/ false), 0); + impl.Emit(new TokenTextWriter("", Console.Out, /*setTokens=*/ false, /*pretty=*/ false, options), 0); options.PrintDesugarings = oldPrintDesugaringSetting; options.PrintUnstructured = oldPrintUnstructured; } @@ -833,7 +833,7 @@ protected void TurnIntoPassiveBlock(Block b, Dictionary incarnat { Contract.Assert( c != null); // walk forward over the commands because the map gets modified in a forward direction - ChecksumHelper.ComputeChecksums(c, currentImplementation, variableCollector.UsedVariables, currentChecksum); + ChecksumHelper.ComputeChecksums(Options, c, currentImplementation, variableCollector.UsedVariables, currentChecksum); variableCollector.Visit(c); currentChecksum = c.Checksum; TurnIntoPassiveCmd(c, b, incarnationMap, oldFrameSubst, passiveCmds, mvInfo); @@ -873,7 +873,7 @@ protected Dictionary Convert2PassiveCmd(Implementation impl, Mod } if (Options.TraceCachingForDebugging) { - using var tokTxtWr = new TokenTextWriter("", Console.Out, false, false); + using var tokTxtWr = new TokenTextWriter("", Console.Out, false, false, Options); var pd = Options.PrintDesugarings; var pu = Options.PrintUnstructured; Options.PrintDesugarings = true; @@ -1030,7 +1030,7 @@ public enum CachingAction : byte void TraceCachingAction(Cmd cmd, CachingAction action) { if (Options.TraceCachingForTesting) { - using var tokTxtWr = new TokenTextWriter("", Console.Out, false, false); + using var tokTxtWr = new TokenTextWriter("", Console.Out, false, false, Options); var loc = cmd.tok != null && cmd.tok != Token.NoToken ? string.Format("{0}({1},{2})", cmd.tok.filename, cmd.tok.line, cmd.tok.col) : ""; @@ -1171,7 +1171,7 @@ protected void TurnIntoPassiveCmd(Cmd c, Block enclosingBlock, Dictionary) cce.NonNull(new Dictionary(incarnationMap)); - var subsumption = Wlp.Subsumption(ac); + var subsumption = Wlp.Subsumption(Options, ac); if (relevantDoomedAssumpVars.Any()) { TraceCachingAction(pc, CachingAction.DoNothingToAssert); @@ -1472,7 +1472,7 @@ protected void TurnIntoPassiveCmd(Cmd c, Block enclosingBlock, Dictionary> ComputeDeclarationDependencies(Program program) + public static Dictionary> ComputeDeclarationDependencies(VCGenOptions options, Program program) { - if (!CoreOptions.Clo.Prune) + if (!options.Prune) { return null; } @@ -54,9 +54,9 @@ public static Dictionary> ComputeDeclarationDependencies(Pr * See Checker.Setup for more information. * Data type constructor declarations are not pruned and they do affect VC generation. */ - public static IEnumerable GetLiveDeclarations(Program program, List blocks) + public static IEnumerable GetLiveDeclarations(VCGenOptions options, Program program, List blocks) { - if (program.DeclarationDependencies == null || blocks == null || !CoreOptions.Clo.Prune) + if (program.DeclarationDependencies == null || blocks == null || !options.Prune) { return program.TopLevelDeclarations; } diff --git a/Source/VCGeneration/Split.cs b/Source/VCGeneration/Split.cs index 8b13b2248..2c638f3e3 100644 --- a/Source/VCGeneration/Split.cs +++ b/Source/VCGeneration/Split.cs @@ -136,7 +136,7 @@ public Split(VCGenOptions options, List /*!*/ blocks, TopLevelDeclarations = par.program.TopLevelDeclarations; PrintTopLevelDeclarationsForPruning(par.program, implementation, "before"); - TopLevelDeclarations = Prune.GetLiveDeclarations(par.program, blocks).ToList(); + TopLevelDeclarations = Prune.GetLiveDeclarations(options, par.program, blocks).ToList(); PrintTopLevelDeclarationsForPruning(par.program, implementation, "after"); } @@ -149,7 +149,7 @@ private void PrintTopLevelDeclarationsForPruning(Program program, Implementation using var writer = new TokenTextWriter( $"{options.PrintPrunedFile}-{suffix}-{Util.EscapeFilename(implementation.Name)}", false, - options.PrettyPrint); + options.PrettyPrint, options); foreach (var declaration in TopLevelDeclarations ?? program.TopLevelDeclarations) { declaration.Emit(writer, 0); } @@ -227,7 +227,7 @@ public void DumpDot(int splitNum) List backup = Implementation.Blocks; Contract.Assert(backup != null); Implementation.Blocks = blocks; - Implementation.Emit(new TokenTextWriter(filename, sw, /*setTokens=*/ false, /*pretty=*/ false), 0); + Implementation.Emit(new TokenTextWriter(filename, sw, /*setTokens=*/ false, /*pretty=*/ false, options), 0); Implementation.Blocks = backup; options.PrintDesugarings = oldPrintDesugaringSetting; options.PrintUnstructured = oldPrintUnstructured; @@ -642,7 +642,7 @@ List SliceCmds(Block b) if (swap) { - theNewCmd = VCGen.AssertTurnedIntoAssume(a); + theNewCmd = VCGen.AssertTurnedIntoAssume(options, a); } } @@ -832,7 +832,7 @@ private static Dictionary PickBlocksToVerify (List blocks, } return blockAssignments; } - private static List DoPreAssignedManualSplit(List blocks, Dictionary blockAssignments, int splitNumberWithinBlock, + private static List DoPreAssignedManualSplit(VCGenOptions options, List blocks, Dictionary blockAssignments, int splitNumberWithinBlock, Block containingBlock, bool lastSplitInBlock, bool splitOnEveryAssert) { var newBlocks = new List(blocks.Count()); // Copies of the original blocks @@ -855,7 +855,7 @@ private static List DoPreAssignedManualSplit(List blocks, Dictiona splitCount++; verify = splitCount == splitNumberWithinBlock; } - newCmds.Add(verify ? c : Split.AssertIntoAssume(c)); + newCmds.Add(verify ? c : AssertIntoAssume(options, c)); } newBlock.Cmds = newCmds; } @@ -864,14 +864,14 @@ private static List DoPreAssignedManualSplit(List blocks, Dictiona var verify = true; var newCmds = new List(); foreach(Cmd c in currentBlock.Cmds) { - verify = ShouldSplitHere(c, splitOnEveryAssert) ? false : verify; - newCmds.Add(verify ? c : Split.AssertIntoAssume(c)); + verify = !ShouldSplitHere(c, splitOnEveryAssert) && verify; + newCmds.Add(verify ? c : AssertIntoAssume(options, c)); } newBlock.Cmds = newCmds; } else { - newBlock.Cmds = currentBlock.Cmds.Select(c => Split.AssertIntoAssume(c)).ToList(); + newBlock.Cmds = currentBlock.Cmds.Select(x => AssertIntoAssume(options, x)).ToList(); } } // Patch the edges between the new blocks @@ -981,14 +981,14 @@ private static bool ShouldSplitHere(Cmd c, bool splitOnEveryAssert) { Block entryPoint = initialSplit.blocks[0]; var blockAssignments = PickBlocksToVerify(initialSplit.blocks, splitPoints); var entryBlockHasSplit = splitPoints.Keys.Contains(entryPoint); - var baseSplitBlocks = PostProcess(DoPreAssignedManualSplit(initialSplit.blocks, blockAssignments, -1, entryPoint, !entryBlockHasSplit, splitOnEveryAssert)); + var baseSplitBlocks = PostProcess(DoPreAssignedManualSplit(initialSplit.options, initialSplit.blocks, blockAssignments, -1, entryPoint, !entryBlockHasSplit, splitOnEveryAssert)); splits.Add(new Split(initialSplit.options, baseSplitBlocks, initialSplit.gotoCmdOrigins, initialSplit.parent, initialSplit.Implementation)); foreach (KeyValuePair pair in splitPoints) { for (int i = 0; i < pair.Value; i++) { bool lastSplitInBlock = i == pair.Value - 1; - var newBlocks = DoPreAssignedManualSplit(initialSplit.blocks, blockAssignments, i, pair.Key, lastSplitInBlock, splitOnEveryAssert); + var newBlocks = DoPreAssignedManualSplit(initialSplit.options, initialSplit.blocks, blockAssignments, i, pair.Key, lastSplitInBlock, splitOnEveryAssert); splits.Add(new Split(initialSplit.options, PostProcess(newBlocks), initialSplit.gotoCmdOrigins, initialSplit.parent, initialSplit.Implementation)); // REVIEW: Does gotoCmdOrigins need to be changed at all? } } @@ -1080,7 +1080,7 @@ void FocusRec(int focusIdx, IEnumerable blocks, IEnumerable freeBl // Their assertions turn into assumes and any splits inside them are disabled. if(freeBlocks.Contains(b)) { - newBlock.Cmds = b.Cmds.Select(c => Split.AssertIntoAssume(c)).Select(c => DisableSplits(c)).ToList(); + newBlock.Cmds = b.Cmds.Select(c => Split.AssertIntoAssume(options, c)).Select(c => DisableSplits(c)).ToList(); } if (b.TransferCmd is GotoCmd gtc) { @@ -1404,11 +1404,11 @@ public void BeginCheck(Checker checker, VerifierCallback callback, ModelViewInfo } } - private static Cmd AssertIntoAssume(Cmd c) + private static Cmd AssertIntoAssume(VCGenOptions options, Cmd c) { if (c is AssertCmd assrt) { - return VCGen.AssertTurnedIntoAssume(assrt); + return VCGen.AssertTurnedIntoAssume(options, assrt); } return c; diff --git a/Source/VCGeneration/SplitAndVerifyWorker.cs b/Source/VCGeneration/SplitAndVerifyWorker.cs index c6d4caddb..562f1d7c4 100644 --- a/Source/VCGeneration/SplitAndVerifyWorker.cs +++ b/Source/VCGeneration/SplitAndVerifyWorker.cs @@ -126,8 +126,8 @@ private void StartCheck(Split split, Checker checker, CancellationToken cancella var timeout = KeepGoing && split.LastChance ? options.VcsFinalAssertTimeout : KeepGoing ? options.VcsKeepGoingTimeout : - implementation.TimeLimit; - split.BeginCheck(checker, callback, mvInfo, currentSplitNumber, timeout, implementation.ResourceLimit, cancellationToken); + implementation.GetTimeLimit(options); + split.BeginCheck(checker, callback, mvInfo, currentSplitNumber, timeout, implementation.GetResourceLimit(options), cancellationToken); } private async Task ProcessResult(Split split, CancellationToken cancellationToken) diff --git a/Source/VCGeneration/StratifiedVC.cs b/Source/VCGeneration/StratifiedVC.cs index c6421fd25..e0cb00bb1 100644 --- a/Source/VCGeneration/StratifiedVC.cs +++ b/Source/VCGeneration/StratifiedVC.cs @@ -486,7 +486,7 @@ public void GenerateVCBoolControl() vcgen.InstrumentCallSites(impl); // typecheck - var tc = new TypecheckingContext(null); + var tc = new TypecheckingContext(null, options); impl.Typecheck(tc); /////////////////// diff --git a/Source/VCGeneration/VCGen.cs b/Source/VCGeneration/VCGen.cs index bb99708b7..fcf665f7b 100644 --- a/Source/VCGeneration/VCGen.cs +++ b/Source/VCGeneration/VCGen.cs @@ -28,14 +28,14 @@ public VCGen(Program program, CheckerPool checkerPool) Contract.Requires(program != null); } - public static AssumeCmd AssertTurnedIntoAssume(AssertCmd assrt) + public static AssumeCmd AssertTurnedIntoAssume(VCGenOptions options, AssertCmd assrt) { Contract.Requires(assrt != null); Contract.Ensures(Contract.Result() != null); Expr expr = assrt.Expr; Contract.Assert(expr != null); - switch (Wlp.Subsumption(assrt)) + switch (Wlp.Subsumption(options, assrt)) { case CoreOptions.SubsumptionOption.Never: expr = Expr.True; @@ -178,7 +178,7 @@ Block CopyBlock(Block b) } else { - seq.Add(AssertTurnedIntoAssume(turn)); + seq.Add(AssertTurnedIntoAssume(Options, turn)); } } @@ -616,7 +616,7 @@ public VCExpr GenerateVCAux(Implementation /*!*/ impl, VCExpr controlFlowVariabl Contract.Requires(proverContext != null); Contract.Ensures(Contract.Result() != null); - TypecheckingContext tc = new TypecheckingContext(null); + TypecheckingContext tc = new TypecheckingContext(null, Options); impl.Typecheck(tc); VCExpr vc; @@ -817,7 +817,7 @@ public override Outcome VerifyImplementation(Implementation impl, VerifierCallba { Contract.EnsuresOnThrow(true); - if (impl.SkipVerification) + if (impl.IsSkipVerification(Options)) { return Outcome.Inconclusive; // not sure about this one } @@ -1070,7 +1070,7 @@ private void RecordCutEdge(Dictionary> edgesCut, Block from, public void ConvertCFG2DAG(Implementation impl, Dictionary> edgesCut = null, int taskID = -1) { Contract.Requires(impl != null); - impl.PruneUnreachableBlocks(); // This is needed for VCVariety.BlockNested, and is otherwise just an optimization + impl.PruneUnreachableBlocks(Options); // This is needed for VCVariety.BlockNested, and is otherwise just an optimization CurrentLocalVariables = impl.LocVars; variable2SequenceNumber = new Dictionary(); @@ -1543,7 +1543,7 @@ private void ConvertCFG2DAGKInduction(Implementation impl, Dictionary PassifyImpl(Implementation impl, out M if (Options.LiveVariableAnalysis > 0) { - Microsoft.Boogie.LiveVariableAnalysis.ComputeLiveVariables(impl); + new LiveVariableAnalysis(Options).ComputeLiveVariables(impl); } mvInfo = new ModelViewInfo(program, impl); @@ -1807,7 +1807,7 @@ public Dictionary PassifyImpl(Implementation impl, out M { RemoveEmptyBlocks(impl.Blocks); - impl.PruneUnreachableBlocks(); + impl.PruneUnreachableBlocks(Options); } #endregion Get rid of empty blocks @@ -2038,7 +2038,7 @@ static bool IsConjunctionOfAssumptionVariables(Expr expr, out HashSet #endregion - private static void HandleSelectiveChecking(Implementation impl) + private void HandleSelectiveChecking(Implementation impl) { if (QKeyValue.FindBoolAttribute(impl.Attributes, "selective_checking") || QKeyValue.FindBoolAttribute(impl.Proc.Attributes, "selective_checking")) @@ -2114,7 +2114,7 @@ private static void HandleSelectiveChecking(Implementation impl) } else { - newCmds.Add(AssertTurnedIntoAssume(asrt)); + newCmds.Add(AssertTurnedIntoAssume(Options, asrt)); } } diff --git a/Source/VCGeneration/Wlp.cs b/Source/VCGeneration/Wlp.cs index 8f9550727..44fe312bb 100644 --- a/Source/VCGeneration/Wlp.cs +++ b/Source/VCGeneration/Wlp.cs @@ -138,7 +138,7 @@ internal static VCExpr Cmd(Block b, Cmd cmd, VCExpr N, VCContext ctxt) } { - var subsumption = Subsumption(ac); + var subsumption = Subsumption(ctxt.Options, ac); if (subsumption == CoreOptions.SubsumptionOption.Always || (subsumption == CoreOptions.SubsumptionOption.NotForQuantifiers && !(C is VCExprQuantifier))) { @@ -241,7 +241,7 @@ private static VCExpr MaybeWrapWithOptimization(VCContext ctxt, VCExpressionGene return expr; } - public static CoreOptions.SubsumptionOption Subsumption(AssertCmd ac) + public static CoreOptions.SubsumptionOption Subsumption(VCGenOptions options, AssertCmd ac) { Contract.Requires(ac != null); int n = QKeyValue.FindIntAttribute(ac.Attributes, "subsumption", -1); @@ -250,7 +250,7 @@ public static CoreOptions.SubsumptionOption Subsumption(AssertCmd ac) case 0: return CoreOptions.SubsumptionOption.Never; case 1: return CoreOptions.SubsumptionOption.NotForQuantifiers; case 2: return CoreOptions.SubsumptionOption.Always; - default: return CoreOptions.Clo.UseSubsumption; + default: return options.UseSubsumption; } } From cd260fcced783d83d5c09819c25075c66c8b4782 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Pit-Claudel?= Date: Mon, 28 Feb 2022 05:31:26 -0500 Subject: [PATCH 03/32] Clean up calls to ToList() to reduce memory pressure (#516) It's a minor part of Boogie's full memory footprint, but it adds up over many calls to Boogie. On Test/havoc0 I observe a 4-5% reduction of the footprint of the whole program. --- Source/Core/Absy.cs | 16 ++++++++-------- Source/Core/AbsyExpr.cs | 4 ++-- Source/Core/AbsyQuant.cs | 4 ++-- Source/Core/InterProceduralReachabilityGraph.cs | 4 ++-- Source/Predication/SmartBlockPredicator.cs | 5 ++--- 5 files changed, 16 insertions(+), 17 deletions(-) diff --git a/Source/Core/Absy.cs b/Source/Core/Absy.cs index 9a0230ed7..cdb047290 100644 --- a/Source/Core/Absy.cs +++ b/Source/Core/Absy.cs @@ -3101,9 +3101,9 @@ protected void RegisterTypeParameters(ResolutionContext rc) protected void SortTypeParams() { List /*!*/ - allTypes = new List(InParams.Select(Item => Item.TypedIdent.Type).ToArray()); + allTypes = InParams.Select(Item => Item.TypedIdent.Type).ToList(); Contract.Assert(allTypes != null); - allTypes.AddRange(new List(OutParams.Select(Item => Item.TypedIdent.Type).ToArray())); + allTypes.AddRange(OutParams.Select(Item => Item.TypedIdent.Type)); TypeParameters = Type.SortTypeParams(TypeParameters, allTypes, null); } @@ -3462,8 +3462,8 @@ public override void Resolve(ResolutionContext rc) rc.PopVarContext(); Type.CheckBoundVariableOccurrences(TypeParameters, - new List(InParams.Select(Item => Item.TypedIdent.Type).ToArray()), - new List(OutParams.Select(Item => Item.TypedIdent.Type).ToArray()), + InParams.Select(Item => Item.TypedIdent.Type).ToList(), + OutParams.Select(Item => Item.TypedIdent.Type).ToList(), this.tok, "function arguments", rc); } @@ -4003,8 +4003,8 @@ public override void Resolve(ResolutionContext rc) ResolveAttributes(rc); Type.CheckBoundVariableOccurrences(TypeParameters, - new List(InParams.Select(Item => Item.TypedIdent.Type).ToArray()), - new List(OutParams.Select(Item => Item.TypedIdent.Type).ToArray()), + InParams.Select(Item => Item.TypedIdent.Type).ToList(), + OutParams.Select(Item => Item.TypedIdent.Type).ToList(), this.tok, "procedure arguments", rc); } @@ -4615,8 +4615,8 @@ public override void Resolve(ResolutionContext rc) rc.PopVarContext(); Type.CheckBoundVariableOccurrences(TypeParameters, - new List(InParams.Select(Item => Item.TypedIdent.Type).ToArray()), - new List(OutParams.Select(Item => Item.TypedIdent.Type).ToArray()), + InParams.Select(Item => Item.TypedIdent.Type).ToList(), + OutParams.Select(Item => Item.TypedIdent.Type).ToList(), this.tok, "implementation arguments", rc); } diff --git a/Source/Core/AbsyExpr.cs b/Source/Core/AbsyExpr.cs index 7421da658..b1d394c22 100644 --- a/Source/Core/AbsyExpr.cs +++ b/Source/Core/AbsyExpr.cs @@ -2817,9 +2817,9 @@ public virtual Type Typecheck(IList actuals, out TypeParamInstantiation tp List actualResultType = Type.CheckArgumentTypes(Func.TypeParameters, out var resultingTypeArgs, - new List(Func.InParams.Select(Item => Item.TypedIdent.Type).ToArray()), + Func.InParams.Select(Item => Item.TypedIdent.Type).ToList(), actuals, - new List(Func.OutParams.Select(Item => Item.TypedIdent.Type).ToArray()), + Func.OutParams.Select(Item => Item.TypedIdent.Type).ToList(), null, // we need some token to report a possibly wrong number of // arguments diff --git a/Source/Core/AbsyQuant.cs b/Source/Core/AbsyQuant.cs index 9ef7ecf03..7337c9d91 100644 --- a/Source/Core/AbsyQuant.cs +++ b/Source/Core/AbsyQuant.cs @@ -252,7 +252,7 @@ public override void Resolve(ResolutionContext rc) // establish a canonical order of the type parameters this.TypeParameters = Type.SortTypeParams(TypeParameters, - new List(Dummies.Select(Item => Item.TypedIdent.Type).ToArray()), null); + Dummies.Select(Item => Item.TypedIdent.Type).ToList(), null); } finally { @@ -312,7 +312,7 @@ protected List GetUnmentionedTypeParameters() { Contract.Ensures(Contract.Result>() != null); List /*!*/ - dummyParameters = Type.FreeVariablesIn(new List(Dummies.Select(Item => Item.TypedIdent.Type).ToArray())); + dummyParameters = Type.FreeVariablesIn(Dummies.Select(Item => Item.TypedIdent.Type).ToList()); Contract.Assert(dummyParameters != null); List /*!*/ unmentionedParameters = new List(); diff --git a/Source/Core/InterProceduralReachabilityGraph.cs b/Source/Core/InterProceduralReachabilityGraph.cs index 0b3555384..cf19b8f16 100644 --- a/Source/Core/InterProceduralReachabilityGraph.cs +++ b/Source/Core/InterProceduralReachabilityGraph.cs @@ -172,7 +172,7 @@ private void ProcessImplementations() Block newBlock; if (prev == null) { - newBlock = new Block(b.tok, "__" + impl.Name + "_" + b.Label, new List(cmds.ToArray()), null); + newBlock = new Block(b.tok, "__" + impl.Name + "_" + b.Label, cmds.ToList(), null); nodes.Add(newBlock); originalToNew[b] = newBlock; if (impl.Blocks[0] == b) @@ -183,7 +183,7 @@ private void ProcessImplementations() else { string label = "__" + impl.Name + "_" + b.Label + "_call_" + i; - newBlock = new Block(b.tok, label, new List(cmds.ToArray()), null); + newBlock = new Block(b.tok, label, cmds.ToList(), null); nodes.Add(newBlock); originalToNew[newBlock] = newBlock; prev.TransferCmd = new GotoCmd(Token.NoToken, new List {label}, new List {newBlock}); diff --git a/Source/Predication/SmartBlockPredicator.cs b/Source/Predication/SmartBlockPredicator.cs index bc0c870d2..b199b05f4 100644 --- a/Source/Predication/SmartBlockPredicator.cs +++ b/Source/Predication/SmartBlockPredicator.cs @@ -490,7 +490,7 @@ Dictionary BuildPartitionInfo() continue; } - var parts = block.Cmds.Cast().TakeWhile( + var parts = block.Cmds.TakeWhile( c => c is AssumeCmd && QKeyValue.FindBoolAttribute(((AssumeCmd) c).Attributes, "partition")); @@ -498,8 +498,7 @@ Dictionary BuildPartitionInfo() if (parts.Count() > 0) { pred = parts.Select(a => ((AssumeCmd) a).Expr).Aggregate(Expr.And); - block.Cmds = - new List(block.Cmds.Cast().Skip(parts.Count()).ToArray()); + block.Cmds = block.Cmds.Skip(parts.Count()).ToList(); } else { From 3a4f371becd65ce3f9f6ebddae7ed69c40c9134d Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Mon, 28 Feb 2022 17:10:41 +0100 Subject: [PATCH 04/32] Bump minor version to 2.12.1 (#517) --- Source/Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Directory.Build.props b/Source/Directory.Build.props index 13eea29ad..29446c953 100644 --- a/Source/Directory.Build.props +++ b/Source/Directory.Build.props @@ -2,7 +2,7 @@ - 2.11.5 + 2.12.1 net6.0 false Boogie From ed98b8eb76920a81e3baac38bbd55f57bc3001bb Mon Sep 17 00:00:00 2001 From: Aaron Tomb Date: Tue, 1 Mar 2022 01:37:41 -0800 Subject: [PATCH 05/32] Support XML generation with /vcsCores (#515) Updates the process of collecting verification results and generating XML from them so that it should be safe to use `/xml` and `/vcsCores` together without getting mangled, interleaved output. In the process, make the results of checking individual splits more readily available at the higher levels of the verification call tree. This should be a step toward allowing clients such as Dafny to access fine-grained verification results without needing to read the XML output. The `VerificationResult` type now includes a list of `SplitResult` objects. If there's any more information a client is likely to need from verification, it should probably wind up in either `VerificationResult` or `SplitResult`. --- Source/Core/Xml.cs | 22 +++++----------- Source/ExecutionEngine/ExecutionEngine.cs | 29 +++++++++++---------- Source/Houdini/Checker.cs | 4 +-- Source/VCGeneration/Checker.cs | 6 ++--- Source/VCGeneration/ConditionGeneration.cs | 16 +++++++++--- Source/VCGeneration/Counterexample.cs | 7 ++++- Source/VCGeneration/Split.cs | 16 ++++++++---- Source/VCGeneration/SplitAndVerifyWorker.cs | 6 +---- Test/commandline/xml.bpl | 17 +++++------- 9 files changed, 63 insertions(+), 60 deletions(-) diff --git a/Source/Core/Xml.cs b/Source/Core/Xml.cs index be5618616..1afe577a6 100644 --- a/Source/Core/Xml.cs +++ b/Source/Core/Xml.cs @@ -115,36 +115,26 @@ public void WriteEndMethod(string outcome, DateTime endTime, TimeSpan elapsed, i cce.EndExpose(); } - public void WriteStartSplit(int splitNum, DateTime startTime) + public void WriteSplit(int splitNum, DateTime startTime, string outcome, TimeSpan elapsed) { Contract.Requires(splitNum > 0); + Contract.Requires(outcome != null); Contract.Requires(IsOpen); //modifies this.*; Contract.Ensures(IsOpen); Contract.Assert(wr != null); + cce.BeginExpose(this); { wr.WriteStartElement("split"); wr.WriteAttributeString("number", splitNum.ToString()); wr.WriteAttributeString("startTime", startTime.ToString(DateTimeFormatString)); - } - cce.EndExpose(); - } - - public void WriteEndSplit(string outcome, TimeSpan elapsed) - { - Contract.Requires(outcome != null); - Contract.Requires(IsOpen); - //modifies this.*; - Contract.Ensures(IsOpen); - Contract.Assert(wr != null); - cce.BeginExpose(this); - { + wr.WriteStartElement("conclusion"); wr.WriteAttributeString("duration", elapsed.TotalSeconds.ToString()); wr.WriteAttributeString("outcome", outcome); - wr.WriteEndElement(); // outcome + wr.WriteEndElement(); // split } cce.EndExpose(); @@ -378,4 +368,4 @@ public void Dispose() } } } -} \ No newline at end of file +} diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index fe13ae425..f10a9fdec 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -197,6 +197,7 @@ public int ProofObligationCount public ConditionGeneration.Outcome Outcome { get; set; } public List Errors; + public List VCResults; public ISet AssertionChecksums { get; private set; } @@ -1017,11 +1018,6 @@ private void VerifyImplementation(Program program, PipelineStatistics stats, Err var cachedResults = Cache.Lookup(impl, Options.RunDiagnosticsOnTimeout, out priority); if (cachedResults != null && priority == Priority.SKIP) { - if (Options.XmlSink != null) - { - Options.XmlSink.WriteStartMethod(impl.Name, cachedResults.Start); - } - printer.Inform(string.Format("Retrieving cached verification result for implementation {0}...", impl.Name), output); if (Options.VerifySnapshots < 3 || @@ -1045,15 +1041,11 @@ private void VerifyImplementation(Program program, PipelineStatistics stats, Err verificationResult.ProofObligationCountBefore = vcgen.CumulativeAssertionCount; verificationResult.Start = DateTime.UtcNow; - if (Options.XmlSink != null) - { - Options.XmlSink.WriteStartMethod(impl.Name, verificationResult.Start); - } - try { var cancellationToken = RequestIdToCancellationTokenSource[requestId].Token; verificationResult.Outcome = - vcgen.VerifyImplementation(impl, out verificationResult.Errors, requestId, cancellationToken); + vcgen.VerifyImplementation(impl, out verificationResult.Errors, + out verificationResult.VCResults, requestId, cancellationToken); if (Options.ExtractLoops && verificationResult.Errors != null) { var vcg = vcgen as VCGen; if (vcg != null) { @@ -1136,9 +1128,18 @@ private void VerifyImplementation(Program program, PipelineStatistics stats, Err if (Options.XmlSink != null) { - Options.XmlSink.WriteEndMethod(verificationResult.Outcome.ToString().ToLowerInvariant(), - verificationResult.End, verificationResult.End - verificationResult.Start, - verificationResult.ResourceCount); + lock (Options.XmlSink) { + Options.XmlSink.WriteStartMethod(impl.Name, verificationResult.Start); + + foreach (var vcResult in verificationResult.VCResults.OrderBy(s => s.vcNum)) { + Options.XmlSink.WriteSplit(vcResult.vcNum, vcResult.startTime, + vcResult.outcome.ToString().ToLowerInvariant(), vcResult.runTime); + } + + Options.XmlSink.WriteEndMethod(verificationResult.Outcome.ToString().ToLowerInvariant(), + verificationResult.End, verificationResult.End - verificationResult.Start, + verificationResult.ResourceCount); + } } outputCollector.Add(index, output); diff --git a/Source/Houdini/Checker.cs b/Source/Houdini/Checker.cs index c73d82c09..40780e97a 100644 --- a/Source/Houdini/Checker.cs +++ b/Source/Houdini/Checker.cs @@ -125,7 +125,7 @@ public class HoudiniStatistics public HoudiniStatistics stats; private VCExpr conjecture; private ProverInterface.ErrorHandler handler; - ConditionGeneration.CounterexampleCollector collector; + ConditionGeneration.VerificationResultCollector collector; HashSet unsatCoreSet; HashSet houdiniConstants; public HashSet houdiniAssertConstants; @@ -158,7 +158,7 @@ public HoudiniSession(Houdini houdini, VCGen vcgen, ProverInterface proverInterf this.descriptiveName = impl.Name; this.houdini = houdini; this.stats = stats; - collector = new ConditionGeneration.CounterexampleCollector(houdini.Options); + collector = new ConditionGeneration.VerificationResultCollector(houdini.Options); collector.OnProgress?.Invoke("HdnVCGen", 0, 0, 0.0); vcgen.ConvertCFG2DAG(impl, taskID: taskID); diff --git a/Source/VCGeneration/Checker.cs b/Source/VCGeneration/Checker.cs index 7db69df1d..7fefcfa41 100644 --- a/Source/VCGeneration/Checker.cs +++ b/Source/VCGeneration/Checker.cs @@ -41,7 +41,7 @@ void ObjectInvariant() private volatile ProverInterface.Outcome outcome; private volatile bool hasOutput; private volatile UnexpectedProverOutputException outputExn; - private DateTime proverStart; + public DateTime ProverStart { get; private set; } private TimeSpan proverRunTime; private volatile ProverInterface.ErrorHandler handler; private volatile CheckerStatus status; @@ -302,7 +302,7 @@ private async Task WaitForOutput(object dummy, CancellationToken cancellationTok } hasOutput = true; - proverRunTime = DateTime.UtcNow - proverStart; + proverRunTime = DateTime.UtcNow - ProverStart; } public void BeginCheck(string descriptiveName, VCExpr vc, ProverInterface.ErrorHandler handler, uint timeout, uint rlimit, CancellationToken cancellationToken) @@ -324,7 +324,7 @@ public void BeginCheck(string descriptiveName, VCExpr vc, ProverInterface.ErrorH } SetTimeout(timeout); SetRlimit(rlimit); - proverStart = DateTime.UtcNow; + ProverStart = DateTime.UtcNow; thmProver.BeginCheck(descriptiveName, vc, handler); // gen.ClearSharedFormulas(); PR: don't know yet what to do with this guy diff --git a/Source/VCGeneration/ConditionGeneration.cs b/Source/VCGeneration/ConditionGeneration.cs index 1a27a9efb..0e4b249f8 100644 --- a/Source/VCGeneration/ConditionGeneration.cs +++ b/Source/VCGeneration/ConditionGeneration.cs @@ -115,6 +115,7 @@ public ConditionGeneration(Program p, CheckerPool checkerPool) /// /// public Outcome VerifyImplementation(Implementation impl, out List /*?*/ errors, + out List vcResults, string requestId, CancellationToken cancellationToken) { Contract.Requires(impl != null); @@ -125,7 +126,7 @@ public Outcome VerifyImplementation(Implementation impl, out List(true); Helpers.ExtraTraceInformation(Options, "Starting implementation verification"); - CounterexampleCollector collector = new CounterexampleCollector(Options); + VerificationResultCollector collector = new VerificationResultCollector(Options); collector.RequestId = requestId; Outcome outcome = VerifyImplementation(impl, collector, cancellationToken); if (outcome == Outcome.Errors || outcome == Outcome.TimedOut || outcome == Outcome.OutOfMemory || @@ -137,6 +138,7 @@ public Outcome VerifyImplementation(Implementation impl, out List /*!>!*/ examples = new List(); + public readonly List /*!>!*/ + vcResults = new List(); public override void OnCounterexample(Counterexample ce, string /*?*/ reason) { @@ -556,6 +561,11 @@ public override void OnUnreachableCode(Implementation impl) EmitImpl(options, impl, false); // TODO report error about next to last in seq } + + public override void OnVCResult(VCResult result) + { + vcResults.Add(result); + } } public static void EmitImpl(VCGenOptions options, Implementation impl, bool printDesugarings) diff --git a/Source/VCGeneration/Counterexample.cs b/Source/VCGeneration/Counterexample.cs index fa34ab1a6..030dc8db1 100644 --- a/Source/VCGeneration/Counterexample.cs +++ b/Source/VCGeneration/Counterexample.cs @@ -1,11 +1,11 @@ using System; using System.Linq; using System.Collections.Generic; -using System.Collections.Specialized; using System.Diagnostics; using System.IO; using System.Diagnostics.Contracts; using Microsoft.Boogie.VCExprAST; +using VC; using Set = Microsoft.Boogie.GSet; namespace Microsoft.Boogie @@ -660,5 +660,10 @@ public virtual void OnWarning(string msg) throw new cce.UnreachableException(); // unexpected case } } + + public virtual void OnVCResult(VCResult result) + { + Contract.Requires(result != null); + } } } diff --git a/Source/VCGeneration/Split.cs b/Source/VCGeneration/Split.cs index 2c638f3e3..0084b0cc9 100644 --- a/Source/VCGeneration/Split.cs +++ b/Source/VCGeneration/Split.cs @@ -15,6 +15,14 @@ namespace VC using Bpl = Microsoft.Boogie; using System.Threading.Tasks; + public record VCResult + ( + int vcNum, + DateTime startTime, + ProverInterface.Outcome outcome, + TimeSpan runTime + ); + public class Split { private VCGenOptions options; @@ -1282,7 +1290,7 @@ public Task ProverTask } } - public void ReadOutcome(ref ConditionGeneration.Outcome curOutcome, out bool proverFailed, ref int totalResourceCount) + public void ReadOutcome(VerifierCallback callback, ref ConditionGeneration.Outcome curOutcome, out bool proverFailed, ref int totalResourceCount) { Contract.EnsuresOnThrow(true); ProverInterface.Outcome outcome = cce.NonNull(checker).ReadOutcome(); @@ -1293,10 +1301,8 @@ public void ReadOutcome(ref ConditionGeneration.Outcome curOutcome, out bool pro checker.ProverRunTime.TotalSeconds, outcome); } - if (options.XmlSink != null && splitNum >= 0) { - options.XmlSink.WriteEndSplit(outcome.ToString().ToLowerInvariant(), - TimeSpan.FromSeconds(checker.ProverRunTime.TotalSeconds)); - } + var result = new VCResult(splitNum + 1, checker.ProverStart, outcome, checker.ProverRunTime); + callback.OnVCResult(result); if (options.VcsDumpSplits) { diff --git a/Source/VCGeneration/SplitAndVerifyWorker.cs b/Source/VCGeneration/SplitAndVerifyWorker.cs index 562f1d7c4..32d8ffd84 100644 --- a/Source/VCGeneration/SplitAndVerifyWorker.cs +++ b/Source/VCGeneration/SplitAndVerifyWorker.cs @@ -117,10 +117,6 @@ private void StartCheck(Split split, Checker checker, CancellationToken cancella split.Stats, currentSplitNumber + 1, total, 100 * provenCost / (provenCost + remainingCost)); } - if (options.XmlSink != null && DoSplitting) { - options.XmlSink.WriteStartSplit(currentSplitNumber + 1, DateTime.UtcNow); - } - callback.OnProgress?.Invoke("VCprove", currentSplitNumber, total, provenCost / (remainingCost + provenCost)); @@ -138,7 +134,7 @@ private async Task ProcessResult(Split split, CancellationToken cancellationToke } } - split.ReadOutcome(ref outcome, out var proverFailed, ref totalResourceCount); + split.ReadOutcome(callback, ref outcome, out var proverFailed, ref totalResourceCount); if (TrackingProgress) { lock (this) { diff --git a/Test/commandline/xml.bpl b/Test/commandline/xml.bpl index 61623373b..1f56bf028 100644 --- a/Test/commandline/xml.bpl +++ b/Test/commandline/xml.bpl @@ -1,12 +1,13 @@ -// Can't use %parallel-boogie here yet - see https://github.com/boogie-org/boogie/issues/460 -// RUN: %boogie -randomSeed:0 -xml:"%t-1.xml" "%s" -// RUN: %boogie -randomSeed:0 -xml:"%t-2.xml" "%s" -// RUN: grep -Eo "resourceCount=\"[0-9]+\"" "%t-1.xml" > "%t-res1" -// RUN: grep -Eo "resourceCount=\"[0-9]+\"" "%t-2.xml" > "%t-res2" +// RUN: %parallel-boogie -randomSeed:0 -xml:"%t-1.xml" "%s" +// RUN: %parallel-boogie -randomSeed:0 -xml:"%t-2.xml" "%s" +// RUN: grep -Eo "resourceCount=\"[0-9]+\"" "%t-1.xml" | sort -g > "%t-res1" +// RUN: grep -Eo "resourceCount=\"[0-9]+\"" "%t-2.xml" | sort -g > "%t-res2" // RUN: diff "%t-res1" "%t-res2" // Chop off the first line, since OutputCheck expects ASCII and can't handle the byte-order mark // RUN: tail -n +2 "%t-1.xml" > "%t.trimmed.xml" // RUN: %OutputCheck "%s" --file-to-check="%t.trimmed.xml" +// We only check for one of the methods in the XML because there's no +// guarantee about what order they'll appear in. // CHECK: \ // CHECK: \ // CHECK: \ @@ -16,12 +17,6 @@ // CHECK: \ // CHECK: \ // CHECK: \ -// CHECK: \ -// CHECK: \ -// CHECK: \ -// CHECK: \ -// CHECK: \ -// CHECK: \ procedure ExampleWithSplits() { From 1de13ecbb816185aeeca21770b9431989ca67d8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Pit-Claudel?= Date: Thu, 3 Mar 2022 10:07:34 -0500 Subject: [PATCH 06/32] Separate help header from help body in CommandLineOptions.cs (#498) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is for Dafny: it makes it easier to keep the few "general" settings at the top while putting the rest of the help after Dafny's settings, like this: Usage: … General settings Boogie general settings Dafny settings Boogie settings --- Source/ExecutionEngine/CommandLineOptions.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Source/ExecutionEngine/CommandLineOptions.cs b/Source/ExecutionEngine/CommandLineOptions.cs index c371daeda..3af91971f 100644 --- a/Source/ExecutionEngine/CommandLineOptions.cs +++ b/Source/ExecutionEngine/CommandLineOptions.cs @@ -513,7 +513,7 @@ public void Error(string message, params string[] args) } } - public virtual string Help => + protected virtual string HelpHeader => $"Usage: {ToolName} [ option ... ] [ filename ... ]" + @" ---- General options ------------------------------------------------------- @@ -522,6 +522,11 @@ public void Error(string message, params string[] args) /help print this message /attrHelp print a message about supported declaration attributes"; + protected virtual string HelpBody => ""; + + public virtual string Help => + HelpHeader + HelpBody; + public virtual string AttributeHelp => ""; /// @@ -2288,8 +2293,8 @@ inductive sequentialization. These are exempt from the overall pool of {:backward} Backward assignment in atomic action."; - public override string Help => - base.Help + @" + protected override string HelpHeader => + base.HelpHeader + @" /env: print command line arguments 0 - never, 1 (default) - during BPL print and prover log, 2 - like 1 and also to standard output @@ -2298,7 +2303,10 @@ inductive sequentialization. These are exempt from the overall pool of 1 (default) - yes /wait await Enter from keyboard before terminating program /xml: also produce output in XML format to +"; + protected override string HelpBody => + @" ---- Boogie options -------------------------------------------------------- Multiple .bpl files supplied on the command line are concatenated into one From d1917bcc34f655c25449df802ee8dff9b8555858 Mon Sep 17 00:00:00 2001 From: Aaron Tomb Date: Mon, 7 Mar 2022 15:32:11 -0800 Subject: [PATCH 07/32] Refactor solver process handling to allow non-interactive mode (#512) This PR moves portions of the SMTLibProcessTheoremProver class into a subclass called SMTLibInteractiveTheoremProver. The goal is that everything in SMTLibProcessTheoremProver should be useful for either interactive or batch mode solvers. It then adds another subclass, SMTLibBatchTheoremProver, to address issue #511. --- .github/workflows/test.yml | 3 +- Source/ExecutionEngine/CommandLineOptions.cs | 2 + Source/Provers/SMTLib/NoopSolver.cs | 39 +- Source/Provers/SMTLib/ProverInterface.cs | 2 +- Source/Provers/SMTLib/ProverUtil.cs | 19 +- .../SMTLib/SMTLibBatchTheoremProver.cs | 280 ++++ .../SMTLib/SMTLibInteractiveTheoremProver.cs | 887 +++++++++++ Source/Provers/SMTLib/SMTLibProcess.cs | 16 +- .../SMTLib/SMTLibProcessTheoremProver.cs | 1415 ++++------------- Source/Provers/SMTLib/SMTLibProverOptions.cs | 11 +- Source/Provers/SMTLib/SMTLibSolver.cs | 2 + Test/aitest9/TestIntervals.bpl | 1 + Test/aitest9/TestIntervals.bpl.expect | 4 +- Test/civl/r2.bpl | 1 + Test/civl/r2.bpl.expect | 14 +- Test/civl/refinement.bpl | 1 + Test/civl/refinement.bpl.expect | 42 +- Test/houdini/deterministic.bpl | 3 + Test/houdini/houd1.bpl | 3 + Test/houdini/houd10.bpl | 3 + Test/houdini/houd10.bpl.expect | 6 +- Test/houdini/houd11.bpl | 3 + Test/houdini/houd11.bpl.expect | 4 +- Test/houdini/houd12.bpl | 3 + Test/houdini/houd2.bpl | 3 + Test/houdini/houd2.bpl.expect | 6 +- Test/houdini/houd3.bpl | 3 + Test/houdini/houd4.bpl | 3 + Test/houdini/houd5.bpl | 3 + Test/houdini/houd6.bpl | 3 + Test/houdini/houd7.bpl | 3 + Test/houdini/houd8.bpl | 3 + Test/houdini/houd9.bpl | 3 + Test/houdini/houd9.bpl.expect | 4 +- Test/houdini/mergedProgSingle_dac.bpl | 3 + Test/houdini/mergedProgSingle_res_ex1.bpl | 3 + Test/houdini/mergedProgSingle_res_ex2.bpl | 3 + Test/houdini/test1.bpl | 3 + Test/houdini/test10.bpl | 3 + Test/houdini/test2.bpl | 3 + Test/houdini/test7.bpl | 3 + Test/houdini/test8.bpl | 3 + Test/houdini/test9.bpl | 3 + Test/houdini/testUnsatCore.bpl | 5 +- Test/inline/test4.bpl | 3 +- Test/inline/test7.bpl | 1 + Test/lit.site.cfg | 7 + Test/livevars/bla1.bpl | 1 + Test/livevars/bla1.bpl.expect | 146 +- .../livevars/daytona_bug2_ioctl_example_2.bpl | 1 + .../daytona_bug2_ioctl_example_2.bpl.expect | 2 +- Test/livevars/stack_overflow.bpl | 3 + Test/livevars/stack_overflow.bpl.expect | 2 +- Test/prover/batch-solver.bpl | 8 + Test/prover/mocksolver.sh | 5 +- Test/pruning/UsesClauses.bpl | 3 +- Test/pruning/UsesClauses.bpl.expect | 6 +- Test/snapshots/runtest.snapshot | 1 + Test/test13/ManyErrors.bpl | 1 + Test/test13/ManyErrors.bpl.expect | 46 +- Test/test15/MoreCapturedStates.bpl | 1 + Test/test15/MoreCapturedStates.bpl.expect | 26 +- Test/test2/Implies.bpl | 1 + Test/test2/Implies.bpl.expect | 30 +- Test/test2/IncompleteArithmetic-RealTypes.bpl | 1 + .../IncompleteArithmetic-RealTypes.bpl.expect | 32 +- Test/test2/InvariantVerifiedUnder0.bpl | 1 + Test/test2/InvariantVerifiedUnder0.bpl.expect | 26 +- Test/test2/Lambda.bpl | 1 + Test/test2/Lambda.bpl.expect | 8 +- Test/test2/LambdaExt.bpl | 1 + Test/test2/LambdaExt.bpl.expect | 134 +- Test/test2/LambdaPoly.bpl | 1 + Test/test2/LambdaPoly.bpl.expect | 18 +- Test/test2/NullaryMaps.bpl | 1 + Test/test2/NullaryMaps.bpl.expect | 12 +- Test/test2/RandomSeed.bpl | 5 + Test/test2/Rlimitouts0.bpl | 4 + Test/test2/SelectiveChecking.bpl | 1 + Test/test2/SelectiveChecking.bpl.expect | 20 +- Test/test2/Timeouts0.bpl | 1 + Test/test2/Timeouts0.bpl.expect | 18 +- Test/test21/InterestingExamples4.bpl | 1 + Test/test21/InterestingExamples4.bpl.a.expect | 8 +- Test/test21/InterestingExamples4.bpl.p.expect | 4 +- Test/test21/Orderings3.bpl | 1 + Test/test21/Orderings3.bpl.a.expect | 12 +- Test/test21/Orderings3.bpl.p.expect | 12 +- Test/test21/ParallelAssignment.bpl | 1 + Test/test21/ParallelAssignment.bpl.a.expect | 12 +- Test/test21/ParallelAssignment.bpl.p.expect | 12 +- Test/test21/Triggers0.bpl | 1 + Test/test21/Triggers0.bpl.a.expect | 8 +- Test/test21/Triggers0.bpl.p.expect | 8 +- .../unnecessaryassumes1.bpl | 1 + 95 files changed, 1994 insertions(+), 1496 deletions(-) create mode 100644 Source/Provers/SMTLib/SMTLibBatchTheoremProver.cs create mode 100644 Source/Provers/SMTLib/SMTLibInteractiveTheoremProver.cs create mode 100644 Test/prover/batch-solver.bpl diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 526889ee3..e51e4e1c1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,6 +20,7 @@ jobs: strategy: matrix: configuration: [Debug, Release] + lit_param: ["batch_mode=True", "batch_mode=False"] steps: - name: Setup dotnet uses: actions/setup-dotnet@v1 @@ -53,7 +54,7 @@ jobs: # Run unit tests dotnet test --no-build -c ${{ matrix.configuration }} ${SOLUTION} # Run lit test suite - lit -v --timeout=120 -D configuration=${{ matrix.configuration }} Test + lit --param ${{ matrix.lit_param }} -v --timeout=120 -D configuration=${{ matrix.configuration }} Test - name: Deploy to nuget if: matrix.configuration == 'Release' && startsWith(github.ref, 'refs/tags/v') run: dotnet nuget push "Source/**/bin/${{ matrix.configuration }}/Boogie*.nupkg" -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json diff --git a/Source/ExecutionEngine/CommandLineOptions.cs b/Source/ExecutionEngine/CommandLineOptions.cs index 3af91971f..1c47208bd 100644 --- a/Source/ExecutionEngine/CommandLineOptions.cs +++ b/Source/ExecutionEngine/CommandLineOptions.cs @@ -805,6 +805,8 @@ public bool NormalizeDeclarationOrder public bool ProduceUnsatCores => PrintNecessaryAssumes || EnableUnSatCoreExtract == 1 || ContractInfer && (UseUnsatCoreForContractInfer || ExplainHoudini); + public bool BatchModeSolver { get; set; } + public bool TraceTimes { get; set; } public bool TraceProofObligations { get; set; } diff --git a/Source/Provers/SMTLib/NoopSolver.cs b/Source/Provers/SMTLib/NoopSolver.cs index c588ad382..532eea99c 100644 --- a/Source/Provers/SMTLib/NoopSolver.cs +++ b/Source/Provers/SMTLib/NoopSolver.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -14,33 +15,35 @@ public override void Close() { } - private SExpr response; + private Queue responses = new(); public override void Send(string cmd) { if (cmd.StartsWith("(get-value")) { - response = null; - } - else - { - response = cmd switch - { - "(get-model)" => null, - "(get-info :name)" => new SExpr(":name"), - "(get-info :rlimit)" => new SExpr(":rlimit", new SExpr("0")), - "(check-sat)" => new SExpr("unknown"), - "(get-info :reason-unknown)" => new SExpr("incomplete"), - "(get-unsat-core)" => new SExpr(""), - _ => null - }; + return; } + + var response = cmd switch + { + "(get-model)" => new SExpr("error", new SExpr("model is not available")), + "(get-info :name)" => new SExpr(":name"), + "(get-info :rlimit)" => new SExpr(":rlimit", new SExpr("0")), + "(check-sat)" => new SExpr("unknown"), + "(get-info :reason-unknown)" => new SExpr(":reason-unknown", new SExpr("incomplete")), + "(get-unsat-core)" => new SExpr(""), + _ => null + }; + + if (response is not null) { responses.Enqueue(response); } } public override Task GetProverResponse() { - var result = response; - response = null; - return Task.FromResult(result); + return Task.FromResult(responses.Count > 0 ? responses.Dequeue() : null); + } + + public override void IndicateEndOfInput() + { } public override void NewProblem(string descriptiveName) diff --git a/Source/Provers/SMTLib/ProverInterface.cs b/Source/Provers/SMTLib/ProverInterface.cs index a7a02c817..0f684f752 100644 --- a/Source/Provers/SMTLib/ProverInterface.cs +++ b/Source/Provers/SMTLib/ProverInterface.cs @@ -259,7 +259,7 @@ public virtual void Check() } public virtual Task CheckOutcomeCore(ErrorHandler handler, - CancellationToken cancellationToken, int taskID = -1) + CancellationToken cancellationToken, int errorLimit) { throw new NotImplementedException(); } diff --git a/Source/Provers/SMTLib/ProverUtil.cs b/Source/Provers/SMTLib/ProverUtil.cs index 3d0e6f992..d0ab62c0a 100644 --- a/Source/Provers/SMTLib/ProverUtil.cs +++ b/Source/Provers/SMTLib/ProverUtil.cs @@ -26,6 +26,7 @@ public class ProverOptions public string ProverName; public string ProverPath; private string confirmedProverPath; + public bool BatchMode; private string /*!*/ @@ -61,7 +62,8 @@ protected virtual bool Parse(string opt) ParseBool(opt, "FORCE_LOG_STATUS", ref ForceLogStatus) || ParseInt(opt, "MEMORY_LIMIT", ref MemoryLimit) || ParseInt(opt, "VERBOSITY", ref Verbosity) || - ParseUInt(opt, "TIME_LIMIT", ref TimeLimit); + ParseUInt(opt, "TIME_LIMIT", ref TimeLimit) || + ParseBool(opt, "BATCH_MODE", ref BatchMode); } public virtual string Help @@ -74,14 +76,18 @@ public virtual string Help ~~~~~~~~~~~~~~~~~~~~~~~ PROVER_PATH= Path to the prover to use. PROVER_NAME= Name of the prover executable. -LOG_FILE= Log input for the theorem prover. The string @PROC@ in the filename - causes there to be one prover log file per verification condition, - and is expanded to the name of the procedure that the verification +LOG_FILE= Log input for the theorem prover. The string @PROC@ + in the filename causes there to be one prover log + file per verification condition, and is expanded to + the name of the procedure that the verification condition is for. APPEND_LOG_FILE= Append, rather than overwrite the log file. MEMORY_LIMIT= Memory limit of the prover in megabytes. VERBOSITY= The higher, the more verbose. -TIME_LIMIT= Time limit per verification condition in milliseconds. +TIME_LIMIT= Time limit per verification condition in + milliseconds. +BATCH_MODE= If true, send all solver input in one batch, + rather than incrementally. The generic options may or may not be used by the prover plugin. "; @@ -180,6 +186,9 @@ private string ConfirmProverPath(string proverPath) if (LibOptions.Trace) { Console.WriteLine("[TRACE] Using prover: " + confirmedProverPath); + if (BatchMode) { + Console.WriteLine("[TRACE] Running in batch mode."); + } } return confirmedProverPath; diff --git a/Source/Provers/SMTLib/SMTLibBatchTheoremProver.cs b/Source/Provers/SMTLib/SMTLibBatchTheoremProver.cs new file mode 100644 index 000000000..0695e065a --- /dev/null +++ b/Source/Provers/SMTLib/SMTLibBatchTheoremProver.cs @@ -0,0 +1,280 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Diagnostics; +using Microsoft.Boogie.VCExprAST; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.Boogie.SMTLib +{ + /// + /// This class provides a "batch" interface to an SMT-Lib prover that + /// prepares all input up-front, sends it to the prover, and then reads + /// all output from the prover. + /// + /// Some SMT-Lib provers don't support the interactive (a.k.a. + /// incremental) mode provided by SMTLibInteractiveTheoremProver. This + /// class allows Boogie to work with such provers. It's known to work + /// with Z3, at least. To work correctly in batch mode, a solver must + /// be able to handle the following commands without crashing: + /// + /// * `(get-model)` after returning `unsat` + /// * `(get-info :reason-unknown)` after returning `sat` or `unsat` + /// + /// Working non-interactively precludes certain features, including the + /// ability to return multiple errors. The current implementation also + /// does not support evaluating expressions in the result context, + /// generating unsat cores, or checking assumptions. + /// + public class SMTLibBatchTheoremProver : SMTLibProcessTheoremProver + { + private bool CheckSatSent; + private int resourceCount; + private Model errorModel; + + [NotDelayed] + public SMTLibBatchTheoremProver(SMTLibOptions libOptions, ProverOptions options, VCExpressionGenerator gen, + SMTLibProverContext ctx) : base(libOptions, options, gen, ctx) + { + if (usingUnsatCore) { + throw new NotSupportedException("Batch mode solver interface does not support unsat cores."); + } + } + + public override Task GoBackToIdle() + { + return Task.CompletedTask; + } + + public override int FlushAxiomsToTheoremProver() + { + // we feed the axioms when BeginCheck is called. + return 0; + } + + public override void BeginCheck(string descriptiveName, VCExpr vc, ErrorHandler handler) + { + SetupProcess(); + CheckSatSent = false; + FullReset(gen); + + if (options.LogFilename != null && currentLogFile == null) { + currentLogFile = OpenOutputFile(descriptiveName); + currentLogFile.Write(common.ToString()); + } + + PrepareCommon(); + FlushAxioms(); + + OptimizationRequests.Clear(); + + string vcString = "(assert (not\n" + VCExpr2String(vc, 1) + "\n))"; + FlushAxioms(); + + Push(); + SendVCAndOptions(descriptiveName, vcString); + SendOptimizationRequests(); + + FlushLogFile(); + + Process.NewProblem(descriptiveName); + + SendCheckSat(); + Pop(); + + FlushLogFile(); + } + + public override void Reset(VCExpressionGenerator generator) + { + } + + public override void FullReset(VCExpressionGenerator generator) + { + if (options.Solver == SolverKind.Z3 || options.Solver == SolverKind.NoOpWithZ3Options) + { + this.gen = generator; + common.Clear(); + SetupAxiomBuilder(gen); + Axioms.Clear(); + TypeDecls.Clear(); + AxiomsAreSetup = false; + DeclCollector.Reset(); + NamedAssumes.Clear(); + UsedNamedAssumes = null; + } + } + + // TODO: move to base? + [NoDefaultContract] + public override async Task CheckOutcome(ErrorHandler handler, int errorLimit, CancellationToken cancellationToken) + { + var result = await CheckOutcomeCore(handler, cancellationToken, errorLimit); + + FlushLogFile(); + + return result; + } + + [NoDefaultContract] + public override async Task CheckOutcomeCore(ErrorHandler handler, CancellationToken cancellationToken, + int errorLimit) + { + if (Process == null || proverErrors.Count > 0) { + return Outcome.Undetermined; + } + + try { + currentErrorHandler = handler; + FlushProverWarnings(); + + var result = await GetResponse(cancellationToken); + + if (result == Outcome.Invalid) { + var labels = CalculatePath(handler.StartingProcId(), errorModel); + if (labels.Length == 0) { + // Without a path to an error, we don't know what to report + result = Outcome.Undetermined; + } else { + handler.OnModel(labels, errorModel, result); + } + } + + // Note: batch mode never tries to discover more errors + + FlushLogFile(); + + Close(); + + return result; + } finally { + Close(); + currentErrorHandler = null; + } + } + + private async Task GetResponse(CancellationToken cancellationToken) + { + var outcomeSExp = await Process.GetProverResponse().WaitAsync(cancellationToken); + var result = ParseOutcome(outcomeSExp, out var wasUnknown); + + var unknownSExp = await Process.GetProverResponse().WaitAsync(cancellationToken); + if (wasUnknown) { + result = ParseReasonUnknown(unknownSExp, result); + } + + var rlimitSExp = await Process.GetProverResponse().WaitAsync(cancellationToken); + resourceCount = ParseRCount(rlimitSExp); + + var modelSExp = await Process.GetProverResponse().WaitAsync(cancellationToken); + errorModel = ParseErrorModel(modelSExp); + + return result; + } + + // Note: This could probably be made to work with the result of + // `(get-value ControlFlow)` rather than the full result of `(get-model)`. + // At some point we should do experiments to see whether that's at all + // faster. + private string[] CalculatePath(int controlFlowConstant, Model model) + { + var path = new List(); + + if (model is null) { + return path.ToArray(); + } + + var function = model.TryGetFunc("ControlFlow"); + var controlFlowElement = model.TryMkElement(controlFlowConstant.ToString()); + var zeroElement = model.TryMkElement("0"); + var v = zeroElement; + + while (true) { + var result = function?.TryEval(controlFlowElement, v) ?? function?.Else; + if (result is null) { + break; + } + var resultData = result as Model.DatatypeValue; + + if (resultData is not null && resultData.Arguments.Length >= 1) { + path.Add(resultData.Arguments[0].ToString()); + break; + } else if (result is Model.Integer) { + path.Add(result.ToString()); + } else { + HandleProverError($"Invalid control flow model received from solver."); + break; + } + + v = result; + } + + return path.ToArray(); + } + + public override Task Evaluate(VCExpr expr) + { + throw new NotSupportedException("Batch mode solver interface does not support evaluating SMT expressions."); + } + + public override void Check() + { + throw new NotSupportedException("Batch mode solver interface does not support the Check call."); + } + + private void SendCheckSat() + { + UsedNamedAssumes = null; + SendThisVC("(check-sat)"); + SendThisVC("(get-info :reason-unknown)"); + SendThisVC("(get-info :rlimit)"); + SendThisVC("(get-model)"); + CheckSatSent = true; + Process.IndicateEndOfInput(); + } + + protected override void Send(string s, bool isCommon) + { + s = Sanitize(s); + + if (isCommon) { + common.Append(s).Append("\r\n"); + } + + // Boogie emits comments after the solver has responded. In batch + // mode, sending these to the solver is problematic. But they'll + // still get sent to the log below. + if (Process != null && !CheckSatSent) { + Process.Send(s); + } + + if (currentLogFile != null) { + currentLogFile.WriteLine(s); + currentLogFile.Flush(); + } + } + + public override Task GetRCount() + { + return Task.FromResult(resourceCount); + } + + public override List UnsatCore() + { + throw new NotSupportedException("Batch mode solver interface does not support unsat cores."); + } + + public override Task<(Outcome, List)> CheckAssumptions(List assumptions, + ErrorHandler handler, CancellationToken cancellationToken) + { + throw new NotSupportedException("Batch mode solver interface does not support checking assumptions."); + } + + public override Task<(Outcome, List)> CheckAssumptions(List hardAssumptions, List softAssumptions, + ErrorHandler handler, CancellationToken cancellationToken) + { + throw new NotSupportedException("Batch mode solver interface does not support checking assumptions."); + } + } +} diff --git a/Source/Provers/SMTLib/SMTLibInteractiveTheoremProver.cs b/Source/Provers/SMTLib/SMTLibInteractiveTheoremProver.cs new file mode 100644 index 000000000..8d8fd375b --- /dev/null +++ b/Source/Provers/SMTLib/SMTLibInteractiveTheoremProver.cs @@ -0,0 +1,887 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Diagnostics; +using System.Diagnostics.Contracts; +using System.Numerics; +using Microsoft.Boogie.VCExprAST; +using Microsoft.Boogie.TypeErasure; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.Boogie.SMTLib +{ + public class SMTLibInteractiveTheoremProver : SMTLibProcessTheoremProver + { + private bool ProcessNeedsRestart; + + [NotDelayed] + public SMTLibInteractiveTheoremProver(SMTLibOptions libOptions, ProverOptions options, VCExpressionGenerator gen, + SMTLibProverContext ctx) : base(libOptions, options, gen, ctx) + { + SetupProcess(); + if (libOptions.ImmediatelyAcceptCommands) { + PrepareCommon(); + } + } + + public override Task GoBackToIdle() + { + return Process.PingPong(); + } + + private void PossiblyRestart() + { + if (Process != null && ProcessNeedsRestart) { + ProcessNeedsRestart = false; + SetupProcess(); + Process.Send(common.ToString()); + } + } + + private void FlushAndCacheCommons() + { + FlushAxioms(); + CachedCommon ??= common.ToString(); + } + + public override int FlushAxiomsToTheoremProver() + { + // we feed the axioms when BeginCheck is called. + return 0; + } + + private bool hasReset; + public override void BeginCheck(string descriptiveName, VCExpr vc, ErrorHandler handler) + { + if (options.SeparateLogFiles) + { + CloseLogFile(); // shouldn't really happen + } + + if (options.LogFilename != null && currentLogFile == null) + { + currentLogFile = OpenOutputFile(descriptiveName); + currentLogFile.Write(common.ToString()); + } + + PrepareCommon(); + FlushAndCacheCommons(); + + if (hasReset) + { + AxBuilder = (TypeAxiomBuilder) CachedAxBuilder?.Clone(); + Namer = ResetNamer(CachedNamer); + DeclCollector.SetNamer(Namer); + DeclCollector.Push(); + } + + OptimizationRequests.Clear(); + + string vcString = "(assert (not\n" + VCExpr2String(vc, 1) + "\n))"; + + FlushAxioms(); + + PossiblyRestart(); + + SendThisVC("(push 1)"); + SendVCAndOptions(descriptiveName, vcString); + + SendOptimizationRequests(); + + FlushLogFile(); + + if (Process != null) + { + Process.PingPong().Wait(); // flush any errors + Process.NewProblem(descriptiveName); + } + + if (hasReset) + { + DeclCollector.Pop(); + common = new StringBuilder(CachedCommon); + hasReset = false; + } + + SendCheckSat(); + FlushLogFile(); + } + + public override void Reset(VCExpressionGenerator generator) + { + if (options.Solver == SolverKind.Z3 || options.Solver == SolverKind.NoOpWithZ3Options) + { + this.gen = generator; + SendThisVC("(reset)"); + RecoverIfProverCrashedAfterReset(); + SendThisVC("(set-option :" + Z3.RlimitOption + " 0)"); + + if (0 < common.Length) + { + var c = common.ToString(); + Process.Send(c); + if (currentLogFile != null) + { + currentLogFile.WriteLine(c); + } + } + + hasReset = true; + } + } + + private void RecoverIfProverCrashedAfterReset() + { + if (Process.GetExceptionIfProverDied().Result is not null) + { + // We recover the process but don't issue the `(reset)` command that fails. + SetupProcess(); + } + } + + public override void FullReset(VCExpressionGenerator generator) + { + if (options.Solver == SolverKind.Z3 || options.Solver == SolverKind.NoOpWithZ3Options) + { + this.gen = generator; + SendThisVC("(reset)"); + SendThisVC("(set-option :" + Z3.RlimitOption + " 0)"); + hasReset = true; + common.Clear(); + SetupAxiomBuilder(gen); + Axioms.Clear(); + TypeDecls.Clear(); + AxiomsAreSetup = false; + ctx.Reset(); + ctx.KnownDatatypes.Clear(); + ctx.parent = this; + DeclCollector.Reset(); + NamedAssumes.Clear(); + UsedNamedAssumes = null; + SendThisVC("; did a full reset"); + } + } + + [NoDefaultContract] + public override async Task CheckOutcome(ErrorHandler handler, int errorLimit, CancellationToken cancellationToken) + { + Contract.EnsuresOnThrow(true); + + var result = await CheckOutcomeCore(handler, cancellationToken, errorLimit); + SendThisVC("(pop 1)"); + FlushLogFile(); + + return result; + } + + [NoDefaultContract] + public override async Task CheckOutcomeCore(ErrorHandler handler, CancellationToken cancellationToken, + int errorLimit) + { + Contract.EnsuresOnThrow(true); + + var result = Outcome.Undetermined; + + if (Process == null || proverErrors.Count > 0) + { + return result; + } + + try + { + currentErrorHandler = handler; + FlushProverWarnings(); + + var errorsDiscovered = 0; + + var globalResult = Outcome.Undetermined; + + while (true) + { + string[] labels = null; + var popLater = false; + + try + { + errorsDiscovered++; + + result = await GetResponse(cancellationToken); + + var reporter = handler; + // TODO(wuestholz): Is the reporter ever null? + if (usingUnsatCore && result == Outcome.Valid && reporter != null && 0 < NamedAssumes.Count) + { + if (usingUnsatCore) + { + UsedNamedAssumes = new HashSet(); + SendThisVC("(get-unsat-core)"); + var resp = await Process.GetProverResponse().WaitAsync(cancellationToken); + if (resp.Name != "") + { + UsedNamedAssumes.Add(resp.Name); + if (libOptions.PrintNecessaryAssumes) + { + reporter.AddNecessaryAssume(resp.Name.Substring("aux$$assume$$".Length)); + } + } + + foreach (var arg in resp.Arguments) + { + UsedNamedAssumes.Add(arg.Name); + if (libOptions.PrintNecessaryAssumes) + { + reporter.AddNecessaryAssume(arg.Name.Substring("aux$$assume$$".Length)); + } + } + } + else + { + UsedNamedAssumes = null; + } + } + + if (libOptions.RunDiagnosticsOnTimeout && result == Outcome.TimeOut) { + (result, popLater) = await RunTimeoutDiagnostics(handler, result, cancellationToken); + } + + if (globalResult == Outcome.Undetermined) + { + globalResult = result; + } + + if (result == Outcome.Invalid) + { + Model model = await GetErrorModel(cancellationToken); + if (libOptions.SIBoolControlVC) + { + labels = Array.Empty(); + } + else + { + labels = await CalculatePath(handler.StartingProcId(), cancellationToken); + if (labels.Length == 0) + { + // Without a path to an error, we don't know what to report + globalResult = Outcome.Undetermined; + break; + } + } + + handler.OnModel(labels, model, result); + } + + Debug.Assert(errorsDiscovered > 0); + // if errorLimit is 0, loop will break only if there are no more + // counterexamples to be discovered. + if (labels == null || !labels.Any() || errorsDiscovered == errorLimit) + { + break; + } + } + finally + { + if (popLater) + { + SendThisVC("(pop 1)"); + } + } + + var source = labels[^2]; + var target = labels[^1]; + // block the assert which was falsified by this counterexample + SendThisVC($"(assert (not (= (ControlFlow 0 {source}) (- {target}))))"); + SendCheckSat(); + } + + FlushLogFile(); + + if (libOptions.RestartProverPerVC && Process != null) + { + ProcessNeedsRestart = true; + } + + return globalResult; + } + finally + { + currentErrorHandler = null; + } + } + + private T WrapInPushPop(ref bool popLater, Func action) + { + if (popLater) + { + SendThisVC("(pop 1)"); + } + SendThisVC("(push 1)"); + var result = action(); + popLater = true; + return result; + } + + private async Task<(Outcome, bool)> RunTimeoutDiagnostics(ErrorHandler handler, Outcome result, CancellationToken cancellationToken) + { + var popLater = false; + if (libOptions.TraceDiagnosticsOnTimeout) { + Console.Out.WriteLine("Starting timeout diagnostics with initial time limit {0}.", options.TimeLimit); + } + + SendThisVC("; begin timeout diagnostics"); + + var start = DateTime.UtcNow; + var unverified = new SortedSet(ctx.TimeoutDiagnosticIDToAssertion.Keys); + var timedOut = new SortedSet(); + var frac = 2; + var queries = 0; + var timeLimitPerAssertion = 0 < options.TimeLimit + ? (options.TimeLimit / 100) * libOptions.TimeLimitPerAssertionInPercent + : 1000; + while (true) { + var rem = unverified.Count; + if (rem == 0) { + if (0 < timedOut.Count) { + + result = await WrapInPushPop(ref popLater, () => CheckSplit(timedOut, options.TimeLimit, timeLimitPerAssertion, ref queries, cancellationToken)); + if (result == Outcome.Valid) { + timedOut.Clear(); + } else if (result == Outcome.TimeOut) { + // Give up and report which assertions were not verified. + var cmds = timedOut.Select(id => ctx.TimeoutDiagnosticIDToAssertion[id]); + + if (cmds.Any()) { + handler.OnResourceExceeded("timeout after running diagnostics", cmds); + } + } + } else { + result = Outcome.Valid; + } + + break; + } + + // TODO(wuestholz): Try out different ways for splitting up the work (e.g., randomly). + var cnt = Math.Max(1, rem / frac); + // It seems like assertions later in the control flow have smaller indexes. + var split = new SortedSet(unverified.Where((val, idx) => (rem - idx - 1) < cnt)); + Contract.Assert(0 < split.Count); + + var splitRes = await WrapInPushPop(ref popLater, () => CheckSplit(split, timeLimitPerAssertion, timeLimitPerAssertion, + ref queries, cancellationToken)); + if (splitRes == Outcome.Valid) { + unverified.ExceptWith(split); + frac = 1; + } else if (splitRes == Outcome.Invalid) { + result = splitRes; + break; + } else if (splitRes == Outcome.TimeOut) { + if (2 <= frac && (4 <= (rem / frac))) { + frac *= 4; + } else if (2 <= (rem / frac)) { + frac *= 2; + } else { + timedOut.UnionWith(split); + unverified.ExceptWith(split); + frac = 1; + } + } else { + break; + } + } + + unverified.UnionWith(timedOut); + + var end = DateTime.UtcNow; + + SendThisVC("; end timeout diagnostics"); + + if (libOptions.TraceDiagnosticsOnTimeout) { + Console.Out.WriteLine("Terminated timeout diagnostics after {0:F0} ms and {1} prover queries.", + end.Subtract(start).TotalMilliseconds, queries); + Console.Out.WriteLine("Outcome: {0}", result); + Console.Out.WriteLine("Unverified assertions: {0} (of {1})", unverified.Count, + ctx.TimeoutDiagnosticIDToAssertion.Keys.Count); + + var filename = "unknown"; + var assertion = ctx.TimeoutDiagnosticIDToAssertion.Values.Select(t => t.Item1) + .FirstOrDefault(a => a.tok != null && a.tok != Token.NoToken && a.tok.filename != null); + if (assertion != null) { + filename = assertion.tok.filename; + } + + File.AppendAllText("timeouts.csv", + string.Format(";{0};{1};{2:F0};{3};{4};{5};{6}\n", filename, options.TimeLimit, + end.Subtract(start).TotalMilliseconds, queries, result, unverified.Count, + ctx.TimeoutDiagnosticIDToAssertion.Keys.Count)); + } + + return (result, popLater); + } + + private Task CheckSplit(SortedSet split, uint timeLimit, uint timeLimitPerAssertion, + ref int queries, CancellationToken cancellationToken) + { + var tla = (uint)(timeLimitPerAssertion * split.Count); + + // FIXME: Gross. Timeout should be set in one place! This is also Z3 specific! + var newTimeout = (0 < tla && tla < timeLimit) ? tla : timeLimit; + if (newTimeout > 0) + { + SendThisVC(string.Format("(set-option :{0} {1})", Z3.TimeoutOption, newTimeout)); + } + + SendThisVC(string.Format("; checking split VC with {0} unverified assertions", split.Count)); + var expr = VCExpressionGenerator.True; + foreach (var i in ctx.TimeoutDiagnosticIDToAssertion.Keys) + { + var lit = VCExprGen.Function(VCExpressionGenerator.TimeoutDiagnosticsOp, + VCExprGen.Integer(BaseTypes.BigNum.FromInt(i))); + if (split.Contains(i)) + { + lit = VCExprGen.Not(lit); + } + + expr = VCExprGen.AndSimp(expr, lit); + } + + SendThisVC("(assert " + VCExpr2String(expr, 1) + ")"); + if (options.Solver == SolverKind.Z3) + { + SendThisVC("(apply (then (using-params propagate-values :max_rounds 1) simplify) :print false)"); + } + + FlushLogFile(); + SendCheckSat(); + queries++; + return GetResponse(cancellationToken); + } + + private async Task CalculatePath(int controlFlowConstant, CancellationToken cancellationToken) + { + SendThisVC("(get-value ((ControlFlow " + controlFlowConstant + " 0)))"); + var path = new List(); + while (true) + { + var response = await Process.GetProverResponse().WaitAsync(cancellationToken); + if (response == null) + { + break; + } + + if (!(response.Name == "" && response.ArgCount == 1)) + { + break; + } + + response = response.Arguments[0]; + if (!(response.Name == "" && response.ArgCount == 2)) + { + break; + } + + response = response.Arguments[1]; + var v = response.Name; + if (v == "-" && response.ArgCount == 1) + { + v = response.Arguments[0].Name; + path.Add(v); + break; + } + else if (response.ArgCount != 0) + { + break; + } + + path.Add(v); + SendThisVC("(get-value ((ControlFlow " + controlFlowConstant + " " + v + ")))"); + } + + return path.ToArray(); + } + + private async Task GetErrorModel(CancellationToken cancellationToken) + { + if (!libOptions.ExpectingModel) + { + return null; + } + + SendThisVC("(get-model)"); + Process.Ping(); + Model theModel = null; + while (true) + { + var resp = await Process.GetProverResponse().WaitAsync(cancellationToken); + if (resp == null || Process.IsPong(resp)) + { + break; + } + + if (theModel != null) + { + HandleProverError("Expecting only one model but got many"); + } + + theModel = ParseErrorModel(resp); + } + + return theModel; + } + + private async Task GetResponse(CancellationToken cancellationToken) + { + var result = Outcome.Undetermined; + var wasUnknown = false; + + Process.Ping(); + + while (true) + { + var resp = await Process.GetProverResponse().WaitAsync(cancellationToken); + if (resp == null || Process.IsPong(resp)) + { + break; + } + + result = ParseOutcome(resp, out wasUnknown); + } + + if (wasUnknown) + { + SendThisVC("(get-info :reason-unknown)"); + Process.Ping(); + while (true) + { + var resp = await Process.GetProverResponse().WaitAsync(cancellationToken); + if (resp == null || Process.IsPong(resp)) + { + break; + } + + result = ParseReasonUnknown(resp, result); + if (result == Outcome.OutOfMemory) { + ProcessNeedsRestart = true; + } + } + } + + return result; + } + + public override async Task Evaluate(VCExpr expr) + { + string vcString = VCExpr2String(expr, 1); + SendThisVC("(get-value (" + vcString + "))"); + var resp = await Process.GetProverResponse(); + if (resp == null) + { + throw new VCExprEvaluationException(); + } + + if (!(resp.Name == "" && resp.ArgCount == 1)) + { + throw new VCExprEvaluationException(); + } + + resp = resp.Arguments[0]; + if (resp.Name == "") + { + // evaluating an expression + if (resp.ArgCount == 2) + { + resp = resp.Arguments[1]; + } + else + { + throw new VCExprEvaluationException(); + } + } + else + { + // evaluating a variable + if (resp.ArgCount == 1) + { + resp = resp.Arguments[0]; + } + else + { + throw new VCExprEvaluationException(); + } + } + + if (resp.Name == "-" && resp.ArgCount == 1) // negative int + { + return Microsoft.BaseTypes.BigNum.FromString("-" + resp.Arguments[0].Name); + } + + if (resp.Name == "_" && resp.ArgCount == 2 && resp.Arguments[0].Name.StartsWith("bv")) // bitvector + { + return new BvConst(Microsoft.BaseTypes.BigNum.FromString(resp.Arguments[0].Name.Substring("bv".Length)), + int.Parse(resp.Arguments[1].Name)); + } + + if (resp.Name == "fp" && resp.ArgCount == 3) + { + Func getBvVal = e => BigInteger.Parse(e.Arguments[0].Name.Substring("bv".Length)); + Func getBvSize = e => int.Parse(e.Arguments[1].ToString()); + bool isNeg = getBvVal(resp.Arguments[0]).IsOne; + var expExpr = resp.Arguments[1]; + var sigExpr = resp.Arguments[2]; + BigInteger exp = getBvVal(expExpr); + int expSize = getBvSize(expExpr); + BigInteger sig = getBvVal(sigExpr); + int sigSize = getBvSize(sigExpr) + 1; + return new BaseTypes.BigFloat(isNeg, sig, exp, sigSize, expSize); + } + + if (resp.Name == "_" && resp.ArgCount == 3) + { + String specialValue = resp.Arguments[0].ToString(); + int expSize = int.Parse(resp.Arguments[1].ToString()); + int sigSize = int.Parse(resp.Arguments[2].ToString()); + return new BaseTypes.BigFloat(specialValue, sigSize, expSize); + } + + var ary = ParseArrayFromProverResponse(resp); + if (ary != null) + { + return ary; + } + + if (resp.ArgCount != 0) + { + throw new VCExprEvaluationException(); + } + + if (expr.Type.Equals(Boogie.Type.Bool)) + { + return bool.Parse(resp.Name); + } + else if (expr.Type.Equals(Boogie.Type.Int)) + { + return Microsoft.BaseTypes.BigNum.FromString(resp.Name); + } + else + { + return resp.Name; + } + } + + public override List UnsatCore() + { + SendThisVC("(get-unsat-core)"); + var resp = Process.GetProverResponse().ToString(); + return ParseUnsatCore(resp); + } + + public override void Check() + { + PrepareCommon(); + SendCheckSat(); + FlushLogFile(); + } + + private void SendCheckSat() + { + UsedNamedAssumes = null; + SendThisVC("(check-sat)"); + } + + protected override void Send(string s, bool isCommon) + { + s = Sanitize(s); + + if (isCommon) + { + common.Append(s).Append("\r\n"); + } + + if (Process != null) + { + Process.Send(s); + } + + if (currentLogFile != null) + { + currentLogFile.WriteLine(s); + currentLogFile.Flush(); + } + } + + public override async Task GetRCount() + { + SendThisVC("(get-info :rlimit)"); + return ParseRCount(await Process.GetProverResponse()); + } + + /// + /// Extra state for ApiChecker (used by stratifiedInlining) + /// + static int nameCounter; + + public override async Task<(Outcome, List)> CheckAssumptions(List assumptions, + ErrorHandler handler, CancellationToken cancellationToken) + { + Push(); + // Name the assumptions + var nameToAssumption = new Dictionary(); + int i = 0; + foreach (var vc in assumptions) + { + var name = "a" + nameCounter.ToString(); + nameCounter++; + nameToAssumption.Add(name, i); + + string vcString = VCExpr2String(vc, 1); + AssertAxioms(); + SendThisVC(string.Format("(assert (! {0} :named {1}))", vcString, name)); + i++; + } + + Check(); + + var outcome = await CheckOutcomeCore(handler, cancellationToken, libOptions.ErrorLimit); + + if (outcome != Outcome.Valid) + { + Pop(); + return (outcome, new List()); + } + + Contract.Assert(usingUnsatCore, "SMTLib prover not setup for computing unsat cores"); + SendThisVC("(get-unsat-core)"); + var resp = await Process.GetProverResponse().WaitAsync(cancellationToken); + var unsatCore = new List(); + if (resp is not null && resp.Name != "") + { + unsatCore.Add(nameToAssumption[resp.Name]); + } + + if (resp is not null) + { + foreach (var s in resp.Arguments) + { + unsatCore.Add(nameToAssumption[s.Name]); + } + } + + FlushLogFile(); + Pop(); + return (outcome, unsatCore); + } + + public override async Task<(Outcome, List)> CheckAssumptions(List hardAssumptions, List softAssumptions, + ErrorHandler handler, CancellationToken cancellationToken) + { + + // First, convert both hard and soft assumptions to SMTLIB strings + var hardAssumptionStrings = new List(); + foreach (var a in hardAssumptions) + { + hardAssumptionStrings.Add(VCExpr2String(a, 1)); + } + + var currAssumptionStrings = new List(); + foreach (var a in softAssumptions) + { + currAssumptionStrings.Add(VCExpr2String(a, 1)); + } + + Push(); + AssertAxioms(); + foreach (var a in hardAssumptionStrings) + { + SendThisVC("(assert " + a + ")"); + } + + Check(); + var outcome = await GetResponse(cancellationToken); + if (outcome != Outcome.Invalid) + { + Pop(); + return (outcome, new List()); + } + + var k = 0; + var relaxVars = new List(); + var unsatisfiedSoftAssumptions = new List(); + while (true) + { + Push(); + foreach (var a in currAssumptionStrings) + { + SendThisVC("(assert " + a + ")"); + } + + Check(); + outcome = await CheckOutcomeCore(handler, cancellationToken, libOptions.ErrorLimit); + if (outcome != Outcome.Valid) + { + break; + } + + Pop(); + var relaxVar = "relax_" + k; + relaxVars.Add(relaxVar); + SendThisVC("(declare-fun " + relaxVar + " () Int)"); + var nextAssumptionStrings = new List(); + for (var i = 0; i < currAssumptionStrings.Count; i++) + { + var constraint = "(= " + relaxVar + " " + i + ")"; + nextAssumptionStrings.Add("(or " + currAssumptionStrings[i] + " " + constraint + ")"); + } + + currAssumptionStrings = nextAssumptionStrings; + k++; + } + + if (outcome == Outcome.Invalid) + { + foreach (var relaxVar in relaxVars) + { + SendThisVC("(get-value (" + relaxVar + "))"); + FlushLogFile(); + var resp = await Process.GetProverResponse().WaitAsync(cancellationToken); + if (resp == null) + { + break; + } + + if (!(resp.Name == "" && resp.ArgCount == 1)) + { + break; + } + + resp = resp.Arguments[0]; + if (!(resp.Name != "" && resp.ArgCount == 1)) + { + break; + } + + resp = resp.Arguments[0]; + if (resp.ArgCount != 0) + { + break; + } + + if (int.TryParse(resp.Name, out var v)) + { + unsatisfiedSoftAssumptions.Add(v); + } + else + { + break; + } + } + + Pop(); + } + + Pop(); + return (outcome, unsatisfiedSoftAssumptions); + } + } +} diff --git a/Source/Provers/SMTLib/SMTLibProcess.cs b/Source/Provers/SMTLib/SMTLibProcess.cs index f6c55515f..7fb2dd7ee 100644 --- a/Source/Provers/SMTLib/SMTLibProcess.cs +++ b/Source/Provers/SMTLib/SMTLibProcess.cs @@ -19,7 +19,7 @@ public class SMTLibProcess : SMTLibSolver readonly SMTLibProverOptions options; readonly Queue proverOutput = new(); readonly Queue proverErrors = new(); - readonly TextWriter toProver; + private TextWriter toProver; readonly int smtProcessId; static int smtProcessIdSeq = 0; ConsoleCancelEventHandler cancelEvent; @@ -92,12 +92,18 @@ void ControlCHandler(object o, ConsoleCancelEventArgs a) } } + public override void IndicateEndOfInput() + { + prover.StandardInput.Close(); + toProver = null; + } + private void TerminateProver(Int32 timeout = 2000) { try { // Let the prover know that we're done sending input. - prover.StandardInput.Close(); + IndicateEndOfInput(); // Give it a chance to exit cleanly (e.g. to flush buffers) if (!prover.WaitForExit(timeout)) @@ -132,7 +138,9 @@ public override void Send(string cmd) public override async Task GetProverResponse() { - await toProver.FlushAsync(); + if (toProver != null) { + await toProver.FlushAsync(); + } while (true) { var exprs = await ParseSExprs(true).ToListAsync(); @@ -146,6 +154,8 @@ public override async Task GetProverResponse() if (resp.Arguments.Length == 1 && resp.Arguments[0].IsId) { if (resp.Arguments[0].Name.Contains("max. resource limit exceeded")) { return resp; + } else if (resp.Arguments[0].Name.Contains("model is not available")) { + return null; } else { HandleError(resp.Arguments[0].Name); return null; diff --git a/Source/Provers/SMTLib/SMTLibProcessTheoremProver.cs b/Source/Provers/SMTLib/SMTLibProcessTheoremProver.cs index cf235f2e1..f7fbb0230 100644 --- a/Source/Provers/SMTLib/SMTLibProcessTheoremProver.cs +++ b/Source/Provers/SMTLib/SMTLibProcessTheoremProver.cs @@ -13,16 +13,17 @@ namespace Microsoft.Boogie.SMTLib { - public class SMTLibProcessTheoremProver : ProverInterface + public abstract class SMTLibProcessTheoremProver : ProverInterface { - private readonly SMTLibOptions libOptions; - private readonly SMTLibProverContext ctx; - private VCExpressionGenerator gen; - protected readonly SMTLibProverOptions options; - private bool usingUnsatCore; + protected SMTLibSolver Process; + protected SMTLibOptions libOptions; + protected SMTLibProverContext ctx; + protected VCExpressionGenerator gen; + protected SMTLibProverOptions options; + protected bool usingUnsatCore; [ContractInvariantMethod] - void ObjectInvariant() + private void ObjectInvariant() { Contract.Invariant(ctx != null); Contract.Invariant(Namer != null); @@ -55,8 +56,6 @@ public SMTLibProcessTheoremProver(SMTLibOptions libOptions, ProverOptions option ctx.parent = this; DeclCollector = new TypeDeclCollector(libOptions, Namer); - SetupProcess(); - if (libOptions.ImmediatelyAcceptCommands) { // Prepare for ApiChecker usage @@ -64,12 +63,9 @@ public SMTLibProcessTheoremProver(SMTLibOptions libOptions, ProverOptions option { currentLogFile = OpenOutputFile(""); } - - PrepareCommon(); } } - - private static ScopedNamer GetNamer(SMTLibOptions libOptions, ProverOptions options, ScopedNamer namer = null) + protected static ScopedNamer GetNamer(SMTLibOptions libOptions, ProverOptions options, ScopedNamer namer = null) { return (RandomSeed: options.RandomSeed, libOptions.NormalizeNames) switch { @@ -79,7 +75,7 @@ private static ScopedNamer GetNamer(SMTLibOptions libOptions, ProverOptions opti }; } - private ScopedNamer ResetNamer(ScopedNamer namer) { + protected ScopedNamer ResetNamer(ScopedNamer namer) { return GetNamer(libOptions, options, namer); } @@ -99,12 +95,7 @@ public override void AssertNamed(VCExpr vc, bool polarity, string name) SendThisVC(string.Format("(assert (! {0} :named {1}))", vcString, name)); } - public override Task GoBackToIdle() - { - return Process.PingPong(); - } - - private void SetupAxiomBuilder(VCExpressionGenerator gen) + protected void SetupAxiomBuilder(VCExpressionGenerator gen) { switch (libOptions.TypeEncodingMethod) { @@ -122,22 +113,6 @@ private void SetupAxiomBuilder(VCExpressionGenerator gen) } } - void SetupProcess() - { - Process?.Close(); - Process = options.Solver == SolverKind.NoOpWithZ3Options ? new NoopSolver() : new SMTLibProcess(libOptions, options); - Process.ErrorHandler += HandleProverError; - } - - void PossiblyRestart() - { - if (Process != null && ProcessNeedsRestart) { - ProcessNeedsRestart = false; - SetupProcess(); - Process.Send(common.ToString()); - } - } - public override ProverContext Context { get @@ -147,17 +122,15 @@ public override ProverContext Context } } - internal TypeAxiomBuilder AxBuilder { get; private set; } - private TypeAxiomBuilder CachedAxBuilder; - private ScopedNamer CachedNamer; - internal ScopedNamer Namer { get; private set; } - readonly TypeDeclCollector DeclCollector; - protected SMTLibSolver Process; - private bool ProcessNeedsRestart; - readonly List proverErrors = new List(); - readonly List proverWarnings = new List(); - StringBuilder common = new StringBuilder(); - private string CachedCommon = null; + internal TypeAxiomBuilder AxBuilder { get; set; } + protected TypeAxiomBuilder CachedAxBuilder; + protected ScopedNamer CachedNamer; + internal ScopedNamer Namer { get; set; } + protected TypeDeclCollector DeclCollector; + protected readonly List proverErrors = new List(); + protected readonly List proverWarnings = new List(); + protected StringBuilder common = new StringBuilder(); + protected string CachedCommon = null; protected TextWriter currentLogFile; protected volatile ErrorHandler currentErrorHandler; @@ -170,7 +143,7 @@ private void FeedTypeDeclsToProver() } } - private string Sanitize(string msg) + protected static string Sanitize(string msg) { var idx = msg.IndexOf('\n'); if (idx > 0) @@ -181,6 +154,23 @@ private string Sanitize(string msg) return msg; } + protected void SetupProcess() + { + Process?.Close(); + Process = options.Solver == SolverKind.NoOpWithZ3Options + ? new NoopSolver() + : new SMTLibProcess(libOptions, options); + Process.ErrorHandler += HandleProverError; + } + + public override void Close() + { + base.Close(); + Process?.Close(); + Process = null; + CloseLogFile(); + } + public override void LogComment(string comment) { SendCommon("; " + comment); @@ -196,26 +186,7 @@ protected void SendThisVC(string s) Send(s, false); } - private void Send(string s, bool isCommon) - { - s = Sanitize(s); - - if (isCommon) - { - common.Append(s).Append("\r\n"); - } - - if (Process != null) - { - Process.Send(s); - } - - if (currentLogFile != null) - { - currentLogFile.WriteLine(s); - currentLogFile.Flush(); - } - } + protected abstract void Send(string s, bool isCommon); private void FindDependentTypes(Type type, List dependentTypes) { @@ -385,7 +356,7 @@ private void PrepareFunctionDefinitions() generatedFuncDefs.Iter(SendCommon); // Flush function definitions } - private void PrepareCommon() + protected void PrepareCommon() { if (common.Length == 0) { @@ -464,22 +435,7 @@ private void PrepareCommon() } } - public override int FlushAxiomsToTheoremProver() - { - // we feed the axioms when BeginCheck is called. - return 0; - } - - private void FlushAndCacheCommons() - { - FlushAxioms(); - if (CachedCommon == null) - { - CachedCommon = common.ToString(); - } - } - - private void FlushAxioms() + protected void FlushAxioms() { TypeDecls.Iter(SendCommon); TypeDecls.Clear(); @@ -496,69 +452,8 @@ private void FlushAxioms() //FlushPushedAssertions(); } - private void CloseLogFile() - { - if (currentLogFile != null) - { - currentLogFile.Close(); - currentLogFile = null; - } - } - - private void FlushLogFile() - { - if (currentLogFile != null) - { - currentLogFile.Flush(); - } - } - - public override void Close() - { - base.Close(); - CloseLogFile(); - if (Process != null) - { - Process.Close(); - } - } - - public override void BeginCheck(string descriptiveName, VCExpr vc, ErrorHandler handler) + protected void SendVCAndOptions(string descriptiveName, String vcString) { - //Contract.Requires(descriptiveName != null); - //Contract.Requires(vc != null); - //Contract.Requires(handler != null); - - if (options.SeparateLogFiles) - { - CloseLogFile(); // shouldn't really happen - } - - if (options.LogFilename != null && currentLogFile == null) - { - currentLogFile = OpenOutputFile(descriptiveName); - currentLogFile.Write(common.ToString()); - } - - PrepareCommon(); - FlushAndCacheCommons(); - - if (HasReset) - { - AxBuilder = (TypeAxiomBuilder) CachedAxBuilder?.Clone(); - Namer = ResetNamer(CachedNamer); - DeclCollector.SetNamer(Namer); - DeclCollector.Push(); - } - - OptimizationRequests.Clear(); - - string vcString = "(assert (not\n" + VCExpr2String(vc, 1) + "\n))"; - FlushAxioms(); - - PossiblyRestart(); - - SendThisVC("(push 1)"); if (this.libOptions.EmitDebugInformation) { SendThisVC("(set-info :boogie-vc-id " + SmtLibNameUtils.QuoteId(descriptiveName) + ")"); } @@ -572,29 +467,26 @@ public override void BeginCheck(string descriptiveName, VCExpr vc, ErrorHandler } } SendThisVC(vcString); + } - SendOptimizationRequests(); - - FlushLogFile(); - - if (Process != null) + protected void CloseLogFile() + { + if (currentLogFile != null) { - Process.PingPong().Wait(); // flush any errors - Process.NewProblem(descriptiveName); + currentLogFile.Close(); + currentLogFile = null; } + } - if (HasReset) + protected void FlushLogFile() + { + if (currentLogFile != null) { - DeclCollector.Pop(); - common = new StringBuilder(CachedCommon); - HasReset = false; + currentLogFile.Flush(); } - - SendCheckSat(); - FlushLogFile(); } - private void SendOptimizationRequests() + protected void SendOptimizationRequests() { if (options.Solver == SolverKind.Z3 && 0 < OptimizationRequests.Count) { @@ -605,68 +497,13 @@ private void SendOptimizationRequests() } } - public override void Reset(VCExpressionGenerator gen) - { - if (options.Solver == SolverKind.Z3 || options.Solver == SolverKind.NoOpWithZ3Options) - { - this.gen = gen; - SendThisVC("(reset)"); - RecoverIfProverCrashedAfterReset(); - SendThisVC("(set-option :" + Z3.RlimitOption + " 0)"); - - if (0 < common.Length) - { - var c = common.ToString(); - Process.Send(c); - if (currentLogFile != null) - { - currentLogFile.WriteLine(c); - } - } - - HasReset = true; - } - } - - private void RecoverIfProverCrashedAfterReset() - { - if (Process.GetExceptionIfProverDied().Result is Exception e) - { - // We recover the process but don't issue the `(reset)` command that fails. - SetupProcess(); - } - } - - public override void FullReset(VCExpressionGenerator gen) - { - if (options.Solver == SolverKind.Z3 || options.Solver == SolverKind.NoOpWithZ3Options) - { - this.gen = gen; - SendThisVC("(reset)"); - SendThisVC("(set-option :" + Z3.RlimitOption + " 0)"); - HasReset = true; - common.Clear(); - SetupAxiomBuilder(gen); - Axioms.Clear(); - TypeDecls.Clear(); - AxiomsAreSetup = false; - ctx.Reset(); - ctx.KnownDatatypes.Clear(); - ctx.parent = this; - DeclCollector.Reset(); - NamedAssumes.Clear(); - UsedNamedAssumes = null; - SendThisVC("; did a full reset"); - } - } - private class BadExprFromProver : Exception { } - class MyFileParser : SExpr.Parser + private class MyFileParser : SExpr.Parser { - SMTLibProcessTheoremProver parent; + private SMTLibProcessTheoremProver parent; public MyFileParser(System.IO.StreamReader _sr, SMTLibProcessTheoremProver _parent) : base(_sr) @@ -682,7 +519,7 @@ public override void ParseError(string msg) private static HashSet usedLogNames = new HashSet(); - private TextWriter OpenOutputFile(string descriptiveName) + protected TextWriter OpenOutputFile(string descriptiveName) { Contract.Requires(descriptiveName != null); Contract.Ensures(Contract.Result() != null); @@ -705,7 +542,7 @@ private TextWriter OpenOutputFile(string descriptiveName) return new StreamWriter(curFilename, false); } - private void FlushProverWarnings() + protected void FlushProverWarnings() { var handler = currentErrorHandler; if (handler != null) @@ -771,426 +608,88 @@ protected void HandleProverError(string s) ReportProverError(errors); } - [NoDefaultContract] - public override async Task CheckOutcome(ErrorHandler handler, int errorLimit, CancellationToken cancellationToken) + protected class SMTErrorModelConverter { - Contract.EnsuresOnThrow(true); - - var result = await CheckOutcomeCore(handler, cancellationToken, errorLimit); - SendThisVC("(pop 1)"); - FlushLogFile(); - - return result; - } + private List ErrorModelTodo; + private SMTLibProcessTheoremProver Parent; + private StringBuilder ErrorModel = new StringBuilder(); + private HashSet TopLevelProcessed = new HashSet(); + private int NumNewArrays = 0; + private Dictionary SortSet = new Dictionary(); - [NoDefaultContract] - public override async Task CheckOutcomeCore(ErrorHandler handler, CancellationToken cancellationToken, - int errorLimit) - { - Contract.EnsuresOnThrow(true); + private Dictionary>> DataTypes = + new Dictionary>>(); - var result = Outcome.Undetermined; + private Dictionary Functions = new Dictionary(); - if (Process == null || proverErrors.Count > 0) + public SMTErrorModelConverter(SExpr _ErrorModel, SMTLibProcessTheoremProver _Parent) { - return result; + ErrorModelTodo = _ErrorModel.Arguments.ToList(); + Parent = _Parent; } - try + public string Convert() { - currentErrorHandler = handler; - FlushProverWarnings(); - - int errorsDiscovered = 0; - - var globalResult = Outcome.Undetermined; + ConvertErrorModel(ErrorModel); + return ErrorModel.ToString(); + } - while (true) + private bool IsConstArray(SExpr element, SExpr type) + { + if (type.Name != "Array") { - string[] labels = null; - bool popLater = false; - - try - { - errorsDiscovered++; - - result = await GetResponse(cancellationToken); - - var reporter = handler; - // TODO(wuestholz): Is the reporter ever null? - if (usingUnsatCore && result == Outcome.Valid && reporter != null && 0 < NamedAssumes.Count) - { - if (usingUnsatCore) - { - UsedNamedAssumes = new HashSet(); - SendThisVC("(get-unsat-core)"); - var resp = await Process.GetProverResponse().WaitAsync(cancellationToken); - if (resp.Name != "") - { - UsedNamedAssumes.Add(resp.Name); - if (libOptions.PrintNecessaryAssumes) - { - reporter.AddNecessaryAssume(resp.Name.Substring("aux$$assume$$".Length)); - } - } - - foreach (var arg in resp.Arguments) - { - UsedNamedAssumes.Add(arg.Name); - if (libOptions.PrintNecessaryAssumes) - { - reporter.AddNecessaryAssume(arg.Name.Substring("aux$$assume$$".Length)); - } - } - } - else - { - UsedNamedAssumes = null; - } - } - - if (libOptions.RunDiagnosticsOnTimeout && result == Outcome.TimeOut) { - (result, popLater) = await RunTimeoutDiagnostics(handler, result, cancellationToken); - } - - if (globalResult == Outcome.Undetermined) - { - globalResult = result; - } - - if (result == Outcome.Invalid) - { - Model model = await GetErrorModel(cancellationToken); - if (libOptions.SIBoolControlVC) - { - labels = new string[0]; - } - else - { - labels = await CalculatePath(handler.StartingProcId(), cancellationToken); - if (labels.Length == 0) - { - // Without a path to an error, we don't know what to report - globalResult = Outcome.Undetermined; - break; - } - } - - handler.OnModel(labels, model, result); - } - - Debug.Assert(errorsDiscovered > 0); - // if errorLimit is 0, loop will break only if there are no more - // counterexamples to be discovered. - if (labels == null || !labels.Any() || errorsDiscovered == errorLimit) - { - break; - } - } - finally - { - if (popLater) - { - SendThisVC("(pop 1)"); - } - } - - string source = labels[^2]; - string target = labels[^1]; - // block the assert which was falsified by this counterexample - SendThisVC($"(assert (not (= (ControlFlow 0 {source}) (- {target}))))"); - SendCheckSat(); + return false; } - FlushLogFile(); - - if (libOptions.RestartProverPerVC && Process != null) + if (element.Name == "__array_store_all__") { - ProcessNeedsRestart = true; + return true; + } + else if (element.Name == "" && element[0].Name == "as" && + element[0][0].Name == "const") + { + return true; } - return globalResult; - } - finally - { - currentErrorHandler = null; + return false; } - } - T WrapInPushPop(ref bool popLater, Func action) - { - if (popLater) + private SExpr GetConstArrayElement(SExpr element) { - SendThisVC("(pop 1)"); - } - SendThisVC("(push 1)"); - var result = action(); - popLater = true; - return result; - } - - private async Task<(Outcome, bool)> RunTimeoutDiagnostics(ErrorHandler handler, Outcome result, CancellationToken cancellationToken) - { - bool popLater = false; - if (libOptions.TraceDiagnosticsOnTimeout) { - Console.Out.WriteLine("Starting timeout diagnostics with initial time limit {0}.", options.TimeLimit); - } - - SendThisVC("; begin timeout diagnostics"); - - var start = DateTime.UtcNow; - var unverified = new SortedSet(ctx.TimeoutDiagnosticIDToAssertion.Keys); - var timedOut = new SortedSet(); - int frac = 2; - int queries = 0; - uint timeLimitPerAssertion = 0 < options.TimeLimit - ? (options.TimeLimit / 100) * libOptions.TimeLimitPerAssertionInPercent - : 1000; - while (true) { - int rem = unverified.Count; - if (rem == 0) { - if (0 < timedOut.Count) { - - result = await WrapInPushPop(ref popLater, () => CheckSplit(timedOut, options.TimeLimit, timeLimitPerAssertion, ref queries, cancellationToken)); - if (result == Outcome.Valid) { - timedOut.Clear(); - } else if (result == Outcome.TimeOut) { - // Give up and report which assertions were not verified. - var cmds = timedOut.Select(id => ctx.TimeoutDiagnosticIDToAssertion[id]); - - if (cmds.Any()) { - handler.OnResourceExceeded("timeout after running diagnostics", cmds); - } - } - } else { - result = Outcome.Valid; - } - - break; - } - - // TODO(wuestholz): Try out different ways for splitting up the work (e.g., randomly). - var cnt = Math.Max(1, rem / frac); - // It seems like assertions later in the control flow have smaller indexes. - var split = new SortedSet(unverified.Where((val, idx) => (rem - idx - 1) < cnt)); - Contract.Assert(0 < split.Count); - - var splitRes = await WrapInPushPop(ref popLater, () => CheckSplit(split, timeLimitPerAssertion, timeLimitPerAssertion, - ref queries, cancellationToken)); - if (splitRes == Outcome.Valid) { - unverified.ExceptWith(split); - frac = 1; - } else if (splitRes == Outcome.Invalid) { - result = splitRes; - break; - } else if (splitRes == Outcome.TimeOut) { - if (2 <= frac && (4 <= (rem / frac))) { - frac *= 4; - } else if (2 <= (rem / frac)) { - frac *= 2; - } else { - timedOut.UnionWith(split); - unverified.ExceptWith(split); - frac = 1; - } - } else { - break; + if (element.Name == "__array_store_all__") + { + return element[1]; } - } - - unverified.UnionWith(timedOut); - - var end = DateTime.UtcNow; - - SendThisVC("; end timeout diagnostics"); - - if (libOptions.TraceDiagnosticsOnTimeout) { - Console.Out.WriteLine("Terminated timeout diagnostics after {0:F0} ms and {1} prover queries.", - end.Subtract(start).TotalMilliseconds, queries); - Console.Out.WriteLine("Outcome: {0}", result); - Console.Out.WriteLine("Unverified assertions: {0} (of {1})", unverified.Count, - ctx.TimeoutDiagnosticIDToAssertion.Keys.Count); - - string filename = "unknown"; - var assertion = ctx.TimeoutDiagnosticIDToAssertion.Values.Select(t => t.Item1) - .FirstOrDefault(a => a.tok != null && a.tok != Token.NoToken && a.tok.filename != null); - if (assertion != null) { - filename = assertion.tok.filename; + else if (element.Name == "" && element[0].Name == "as" && + element[0][0].Name == "const") + { + return element[1]; } - File.AppendAllText("timeouts.csv", - string.Format(";{0};{1};{2:F0};{3};{4};{5};{6}\n", filename, options.TimeLimit, - end.Subtract(start).TotalMilliseconds, queries, result, unverified.Count, - ctx.TimeoutDiagnosticIDToAssertion.Keys.Count)); - } - - return (result, popLater); - } - - private Task CheckSplit(SortedSet split, uint timeLimit, uint timeLimitPerAssertion, - ref int queries, CancellationToken cancellationToken) - { - uint tla = (uint)(timeLimitPerAssertion * split.Count); - - // FIXME: Gross. Timeout should be set in one place! This is also Z3 specific! - uint newTimeout = (0 < tla && tla < timeLimit) ? tla : timeLimit; - if (newTimeout > 0) - { - SendThisVC(string.Format("(set-option :{0} {1})", Z3.TimeoutOption, newTimeout)); + Parent.HandleProverError("Unexpected value: " + element); + throw new BadExprFromProver(); } - SendThisVC(string.Format("; checking split VC with {0} unverified assertions", split.Count)); - var expr = VCExpressionGenerator.True; - foreach (var i in ctx.TimeoutDiagnosticIDToAssertion.Keys) + private void ConstructComplexValue(SExpr element, SExpr type, StringBuilder m) { - var lit = VCExprGen.Function(VCExpressionGenerator.TimeoutDiagnosticsOp, - VCExprGen.Integer(Microsoft.BaseTypes.BigNum.FromInt(i))); - if (split.Contains(i)) + if (type.Name == "Array") { - lit = VCExprGen.Not(lit); + if (element.Name == "store" || IsConstArray(element, type)) + { + NumNewArrays++; + m.Append("as-array[k!" + NumNewArrays + ']'); + SExpr[] args = {new SExpr("k!" + NumNewArrays), new SExpr(""), type, element}; + var newElement = new SExpr("define-fun", args); + TopLevelProcessed.Add(newElement); + ErrorModelTodo.Add(newElement); + return; + } } - expr = VCExprGen.AndSimp(expr, lit); + ConstructSimpleValue(element, type, m); } - SendThisVC("(assert " + VCExpr2String(expr, 1) + ")"); - if (options.Solver == SolverKind.Z3) - { - SendThisVC("(apply (then (using-params propagate-values :max_rounds 1) simplify) :print false)"); - } - - FlushLogFile(); - SendCheckSat(); - queries++; - return GetResponse(cancellationToken); - } - - public async Task CalculatePath(int controlFlowConstant, CancellationToken cancellationToken) - { - SendThisVC("(get-value ((ControlFlow " + controlFlowConstant + " 0)))"); - var path = new List(); - while (true) - { - var response = await Process.GetProverResponse().WaitAsync(cancellationToken); - if (response == null) - { - break; - } - - if (!(response.Name == "" && response.ArgCount == 1)) - { - break; - } - - response = response.Arguments[0]; - if (!(response.Name == "" && response.ArgCount == 2)) - { - break; - } - - response = response.Arguments[1]; - var v = response.Name; - if (v == "-" && response.ArgCount == 1) - { - v = response.Arguments[0].Name; - path.Add(v); - break; - } - else if (response.ArgCount != 0) - { - break; - } - - path.Add(v); - SendThisVC("(get-value ((ControlFlow " + controlFlowConstant + " " + v + ")))"); - } - - return path.ToArray(); - } - - - private class SMTErrorModelConverter - { - private List ErrorModelTodo; - private SMTLibProcessTheoremProver Parent; - private StringBuilder ErrorModel = new StringBuilder(); - private HashSet TopLevelProcessed = new HashSet(); - private int NumNewArrays = 0; - private Dictionary SortSet = new Dictionary(); - - private Dictionary>> DataTypes = - new Dictionary>>(); - - private Dictionary Functions = new Dictionary(); - - public SMTErrorModelConverter(SExpr _ErrorModel, SMTLibProcessTheoremProver _Parent) - { - ErrorModelTodo = _ErrorModel.Arguments.ToList(); - Parent = _Parent; - } - - public string Convert() - { - ConvertErrorModel(ErrorModel); - return ErrorModel.ToString(); - } - - bool IsConstArray(SExpr element, SExpr type) - { - if (type.Name != "Array") - { - return false; - } - - if (element.Name == "__array_store_all__") - { - return true; - } - else if (element.Name == "" && element[0].Name == "as" && - element[0][0].Name == "const") - { - return true; - } - - return false; - } - - SExpr GetConstArrayElement(SExpr element) - { - if (element.Name == "__array_store_all__") - { - return element[1]; - } - else if (element.Name == "" && element[0].Name == "as" && - element[0][0].Name == "const") - { - return element[1]; - } - - Parent.HandleProverError("Unexpected value: " + element); - throw new BadExprFromProver(); - } - - void ConstructComplexValue(SExpr element, SExpr type, StringBuilder m) - { - if (type.Name == "Array") - { - if (element.Name == "store" || IsConstArray(element, type)) - { - NumNewArrays++; - m.Append("as-array[k!" + NumNewArrays + ']'); - SExpr[] args = {new SExpr("k!" + NumNewArrays), new SExpr(""), type, element}; - var newElement = new SExpr("define-fun", args); - TopLevelProcessed.Add(newElement); - ErrorModelTodo.Add(newElement); - return; - } - } - - ConstructSimpleValue(element, type, m); - } - - void ConstructSimpleValue(SExpr element, SExpr type, StringBuilder m) + private void ConstructSimpleValue(SExpr element, SExpr type, StringBuilder m) { if (type.Name == "Bool" && element.ArgCount == 0) { @@ -1302,7 +801,7 @@ void ConstructSimpleValue(SExpr element, SExpr type, StringBuilder m) throw new BadExprFromProver(); } - void ConstructFunctionArguments(SExpr arguments, List argTypes, StringBuilder[] argValues) + private void ConstructFunctionArguments(SExpr arguments, List argTypes, StringBuilder[] argValues) { if (arguments.Name == "and") { @@ -1349,7 +848,7 @@ void ConstructFunctionArguments(SExpr arguments, List argTypes, StringBui } } - void ConstructFunctionElements(SExpr element, List argTypes, SExpr outType, StringBuilder m) + private void ConstructFunctionElements(SExpr element, List argTypes, SExpr outType, StringBuilder m) { while (element.Name == "ite") { @@ -1374,7 +873,7 @@ void ConstructFunctionElements(SExpr element, List argTypes, SExpr outTyp ConstructComplexValue(element, outType, m); } - void ConstructFunction(SExpr element, SExpr inType, SExpr outType, StringBuilder m) + private void ConstructFunction(SExpr element, SExpr inType, SExpr outType, StringBuilder m) { List argTypes = new List(); @@ -1393,7 +892,7 @@ void ConstructFunction(SExpr element, SExpr inType, SExpr outType, StringBuilder ConstructFunctionElements(element, argTypes, outType, m); } - void ConstructDefine(SExpr element, StringBuilder m) + private void ConstructDefine(SExpr element, StringBuilder m) { Debug.Assert(element.Name == "define-fun"); @@ -1429,7 +928,7 @@ void ConstructDefine(SExpr element, StringBuilder m) m.Append("\n"); } - void ExtractDataType(SExpr datatypes) + private void ExtractDataType(SExpr datatypes) { Debug.Assert(datatypes.Name == "declare-datatypes"); @@ -1522,203 +1021,7 @@ private void ConvertErrorModel(StringBuilder m) } } - private async Task GetErrorModel(CancellationToken cancellationToken) - { - if (!libOptions.ExpectingModel) - { - return null; - } - - SendThisVC("(get-model)"); - Process.Ping(); - Model theModel = null; - while (true) - { - var resp = await Process.GetProverResponse().WaitAsync(cancellationToken); - if (resp == null || Process.IsPong(resp)) - { - break; - } - - if (theModel != null) - { - HandleProverError("Expecting only one model but got many"); - } - - string modelStr = null; - if (resp.ArgCount >= 1) - { - var converter = new SMTErrorModelConverter(resp, this); - modelStr = converter.Convert(); - } - else if (resp.ArgCount == 0 && resp.Name.Contains("->")) - { - modelStr = resp.Name; - } - else - { - HandleProverError("Unexpected prover response getting model: " + resp.ToString()); - } - - List models = null; - try - { - switch (options.Solver) - { - case SolverKind.Z3: - case SolverKind.CVC5: - models = Model.ParseModels(new StringReader("Error model: \n" + modelStr), Namer.GetOriginalName); - break; - default: - Debug.Assert(false); - return null; - } - } - catch (ArgumentException exn) - { - HandleProverError("Model parsing error: " + exn.Message); - } - - if (models == null) - { - HandleProverError("Could not parse any models"); - } - else if (models.Count == 0) - { - HandleProverError("Could not parse any models"); - } - else if (models.Count > 1) - { - HandleProverError("Expecting only one model but got many"); - } - else - { - theModel = models[0]; - } - } - - return theModel; - } - - private async Task GetResponse(CancellationToken cancellationToken) - { - var result = Outcome.Undetermined; - var wasUnknown = false; - - Process.Ping(); - - while (true) - { - var resp = await Process.GetProverResponse().WaitAsync(cancellationToken); - if (resp == null || Process.IsPong(resp)) - { - break; - } - - switch (resp.Name) - { - case "unsat": - result = Outcome.Valid; - break; - case "sat": - result = Outcome.Invalid; - break; - case "unknown": - result = Outcome.Invalid; - wasUnknown = true; - break; - case "objectives": - // We ignore this. - break; - case "error": - if (resp.Arguments.Length == 1 && resp.Arguments[0].IsId && - (resp.Arguments[0].Name.Contains("max. resource limit exceeded") - || resp.Arguments[0].Name.Contains("resource limits reached"))) - { - currentErrorHandler.OnResourceExceeded("max resource limit"); - result = Outcome.OutOfResource; - } - else - { - HandleProverError("Unexpected prover response: " + resp.ToString()); - } - - break; - default: - HandleProverError("Unexpected prover response: " + resp.ToString()); - break; - } - } - - if (wasUnknown) - { - SendThisVC("(get-info :reason-unknown)"); - Process.Ping(); - while (true) - { - var resp = await Process.GetProverResponse().WaitAsync(cancellationToken); - if (resp == null || Process.IsPong(resp)) - { - break; - } - - if (resp.ArgCount == 1 && resp.Name == ":reason-unknown") - { - switch (resp[0].Name) - { - case "incomplete": - case "(incomplete quantifiers)": - case "(incomplete (theory arithmetic))": - case "smt tactic failed to show goal to be sat/unsat (incomplete (theory arithmetic))": - break; - case "memout": - currentErrorHandler.OnResourceExceeded("memory"); - result = Outcome.OutOfMemory; - ProcessNeedsRestart = true; - break; - case "timeout": - currentErrorHandler.OnResourceExceeded("timeout"); - result = Outcome.TimeOut; - break; - case "canceled": - // both timeout and max resource limit are reported as - // canceled in version 4.4.1 - if (this.options.TimeLimit > 0) - { - currentErrorHandler.OnResourceExceeded("timeout"); - result = Outcome.TimeOut; - } - else - { - currentErrorHandler.OnResourceExceeded("max resource limit"); - result = Outcome.OutOfResource; - } - - break; - case "max. resource limit exceeded": - case "resource limits reached": - case "(resource limits reached)": - currentErrorHandler.OnResourceExceeded("max resource limit"); - result = Outcome.OutOfResource; - break; - default: - result = Outcome.Undetermined; - HandleProverError("Unexpected prover response (getting info about 'unknown' response): " + resp.ToString()); - break; - } - } - else - { - result = Outcome.Undetermined; - HandleProverError("Unexpected prover response (getting info about 'unknown' response): " + resp.ToString()); - } - } - } - - return result; - } - - readonly IList OptimizationRequests = new List(); + protected readonly IList OptimizationRequests = new List(); protected string VCExpr2String(VCExpr expr, int polarity) { @@ -1789,13 +1092,11 @@ protected string VCExpr2String(VCExpr expr, int polarity) // the list of all known axioms, where have to be included in each // verification condition - private readonly List!*/> Axioms = new List(); - private bool AxiomsAreSetup = false; - private bool HasReset = false; - + protected readonly List!*/> Axioms = new List(); + protected bool AxiomsAreSetup = false; // similarly, a list of function/predicate declarations - private readonly List!*/> TypeDecls = new List(); + protected readonly List!*/> TypeDecls = new List(); protected void AddAxiom(string axiom) { @@ -1821,7 +1122,7 @@ protected void AddTypeDecl(string decl) private static string _backgroundPredicates; - void InitializeGlobalInformation() + private void InitializeGlobalInformation() { Contract.Ensures(_backgroundPredicates != null); //throws ProverException, System.IO.FileNotFoundException; @@ -1905,24 +1206,6 @@ public override void Assert(VCExpr vc, bool polarity, bool isSoft = false, int w SendOptimizationRequests(); } - public override List UnsatCore() - { - SendThisVC("(get-unsat-core)"); - var resp = Process.GetProverResponse().ToString(); - if (resp == "" || resp == "()") - { - return null; - } - - if (resp[0] == '(') - { - resp = resp.Substring(1, resp.Length - 2); - } - - var ucore = resp.Split(' ').ToList(); - return ucore; - } - public override void DefineMacro(Macro f, VCExpr vc) { DeclCollector.AddKnownFunction(f); @@ -1939,19 +1222,6 @@ public override void AssertAxioms() FlushAxioms(); } - public override void Check() - { - PrepareCommon(); - SendCheckSat(); - FlushLogFile(); - } - - public void SendCheckSat() - { - UsedNamedAssumes = null; - SendThisVC("(check-sat)"); - } - public override void SetTimeout(uint ms) { options.TimeLimit = ms; @@ -1962,32 +1232,167 @@ public override void SetRlimit(uint limit) options.ResourceLimit = limit; } - public override async Task GetRCount() + protected Outcome ParseOutcome(SExpr resp, out bool wasUnknown) { - SendThisVC("(get-info :rlimit)"); - var resp = await Process.GetProverResponse(); - try + var result = Outcome.Undetermined; + wasUnknown = false; + + if (resp is null) { + wasUnknown = true; + return result; + } + + switch (resp.Name) { - return int.Parse(resp[0].Name); + case "unsat": + result = Outcome.Valid; + break; + case "sat": + result = Outcome.Invalid; + break; + case "unknown": + result = Outcome.Invalid; + wasUnknown = true; + break; + case "objectives": + // We ignore this. + break; + case "error": + if (resp.Arguments.Length == 1 && resp.Arguments[0].IsId && + (resp.Arguments[0].Name.Contains("max. resource limit exceeded") + || resp.Arguments[0].Name.Contains("resource limits reached"))) + { + currentErrorHandler.OnResourceExceeded("max resource limit"); + result = Outcome.OutOfResource; + } + else + { + HandleProverError("Unexpected prover response: " + resp); + } + break; + default: + HandleProverError("Unexpected prover response: " + resp); + break; } - catch + + return result; + } + + protected Outcome ParseReasonUnknown(SExpr resp, Outcome initialOutcome) + { + Outcome result; + if (resp is null || resp.Name == "") { + result = initialOutcome; + } + else if (resp.ArgCount == 1 && resp.Name == ":reason-unknown") { - // If anything goes wrong with parsing the response from the solver, - // it's better to be able to continue, even with uninformative data. - lock (proverWarnings) + switch (resp[0].Name) { - proverWarnings.Add($"Failed to parse resource count from solver. Got: {resp.ToString()}"); + case "": + result = initialOutcome; + break; + case "incomplete": + case "(incomplete quantifiers)": + case "(incomplete (theory arithmetic))": + case "smt tactic failed to show goal to be sat/unsat (incomplete (theory arithmetic))": + result = Outcome.Invalid; + break; + case "memout": + currentErrorHandler.OnResourceExceeded("memory"); + result = Outcome.OutOfMemory; + break; + case "timeout": + currentErrorHandler.OnResourceExceeded("timeout"); + result = Outcome.TimeOut; + break; + case "canceled": + // both timeout and max resource limit are reported as + // canceled in version 4.4.1 + if (this.options.TimeLimit > 0) + { + currentErrorHandler.OnResourceExceeded("timeout"); + result = Outcome.TimeOut; + } + else + { + currentErrorHandler.OnResourceExceeded("max resource limit"); + result = Outcome.OutOfResource; + } + + break; + case "max. resource limit exceeded": + case "resource limits reached": + case "(resource limits reached)": + currentErrorHandler.OnResourceExceeded("max resource limit"); + result = Outcome.OutOfResource; + break; + default: + result = Outcome.Undetermined; + HandleProverError("Unexpected prover response (getting info about 'unknown' response): " + resp); + break; } - return -1; } + else + { + result = Outcome.Undetermined; + HandleProverError("Unexpected prover response (getting info about 'unknown' response): " + resp); + } + + return result; + } + + protected Model ParseErrorModel(SExpr resp) + { + if (resp is null || resp.Name.Contains("error")) { + return null; + } + + Model theModel = null; + string modelStr = null; + + if (resp.ArgCount >= 1) { + var converter = new SMTErrorModelConverter(resp, this); + modelStr = converter.Convert(); + } else if (resp.ArgCount == 0 && resp.Name.Contains("->")) { + modelStr = resp.Name; + } else { + HandleProverError("Unexpected prover response getting model: " + resp); + } + + List models = null; + try { + switch (options.Solver) { + case SolverKind.Z3: + case SolverKind.CVC5: + models = Model.ParseModels(new StringReader("Error model: \n" + modelStr), Namer.GetOriginalName); + break; + default: + Debug.Assert(false); + return null; + } + } catch (ArgumentException exn) { + HandleProverError("Model parsing error: " + exn.Message); + } + + if (models == null) { + HandleProverError("Could not parse any models"); + } else if (models.Count == 0) { + HandleProverError("Could not parse any models"); + } else if (models.Count > 1) { + HandleProverError("Expecting only one model but got many"); + } else { + theModel = models[0]; + } + + return theModel; } - object ParseValueFromProver(SExpr expr) + private static object ParseValueFromProver(SExpr expr) { return expr.ToString().Replace(" ", "").Replace("(", "").Replace(")", ""); } - SExpr ReduceLet(SExpr expr) + private static SExpr ReduceLet(SExpr expr) { if (expr.Name != "let") { @@ -2028,13 +1433,13 @@ SExpr ReduceLet(SExpr expr) return subexpr; } - object GetArrayFromProverResponse(SExpr resp) + protected static object ParseArrayFromProverResponse(SExpr resp) { resp = ReduceLet(resp); - var dict = GetArrayFromArrayExpr(resp); + var dict = ParseArrayFromArrayExpr(resp); if (dict == null) { - dict = GetArrayFromProverLambdaExpr(resp); + dict = ParseArrayFromProverLambdaExpr(resp); } if (dict == null) @@ -2061,7 +1466,7 @@ object GetArrayFromProverResponse(SExpr resp) return str.ToString(); } - Dictionary GetArrayFromProverLambdaExpr(SExpr resp) + private static Dictionary ParseArrayFromProverLambdaExpr(SExpr resp) { var dict = new Dictionary(); if (resp.Name == "lambda" && resp.ArgCount == 2) @@ -2114,7 +1519,7 @@ Dictionary GetArrayFromProverLambdaExpr(SExpr resp) return dict.Count > 0 ? dict : null; } - Dictionary GetArrayFromArrayExpr(SExpr resp) + private static Dictionary ParseArrayFromArrayExpr(SExpr resp) { var dict = new Dictionary(); var curr = resp; @@ -2143,156 +1548,37 @@ Dictionary GetArrayFromArrayExpr(SExpr resp) return dict.Count > 0 ? dict : null; } - public override async Task Evaluate(VCExpr expr) + protected List ParseUnsatCore(string resp) { - string vcString = VCExpr2String(expr, 1); - SendThisVC("(get-value (" + vcString + "))"); - var resp = await Process.GetProverResponse(); - if (resp == null) - { - throw new VCExprEvaluationException(); - } - - if (!(resp.Name == "" && resp.ArgCount == 1)) - { - throw new VCExprEvaluationException(); - } - - resp = resp.Arguments[0]; - if (resp.Name == "") - { - // evaluating an expression - if (resp.ArgCount == 2) - { - resp = resp.Arguments[1]; - } - else - { - throw new VCExprEvaluationException(); - } - } - else - { - // evaluating a variable - if (resp.ArgCount == 1) - { - resp = resp.Arguments[0]; - } - else - { - throw new VCExprEvaluationException(); - } - } - - if (resp.Name == "-" && resp.ArgCount == 1) // negative int - { - return Microsoft.BaseTypes.BigNum.FromString("-" + resp.Arguments[0].Name); - } - - if (resp.Name == "_" && resp.ArgCount == 2 && resp.Arguments[0].Name.StartsWith("bv")) // bitvector - { - return new BvConst(Microsoft.BaseTypes.BigNum.FromString(resp.Arguments[0].Name.Substring("bv".Length)), - int.Parse(resp.Arguments[1].Name)); - } - - if (resp.Name == "fp" && resp.ArgCount == 3) - { - Func getBvVal = e => BigInteger.Parse(e.Arguments[0].Name.Substring("bv".Length)); - Func getBvSize = e => int.Parse(e.Arguments[1].ToString()); - bool isNeg = getBvVal(resp.Arguments[0]).IsOne; - var expExpr = resp.Arguments[1]; - var sigExpr = resp.Arguments[2]; - BigInteger exp = getBvVal(expExpr); - int expSize = getBvSize(expExpr); - BigInteger sig = getBvVal(sigExpr); - int sigSize = getBvSize(sigExpr) + 1; - return new BaseTypes.BigFloat(isNeg, sig, exp, sigSize, expSize); - } - - if (resp.Name == "_" && resp.ArgCount == 3) - { - String specialValue = resp.Arguments[0].ToString(); - int expSize = int.Parse(resp.Arguments[1].ToString()); - int sigSize = int.Parse(resp.Arguments[2].ToString()); - return new BaseTypes.BigFloat(specialValue, sigSize, expSize); - } - - var ary = GetArrayFromProverResponse(resp); - if (ary != null) + if (resp == "" || resp == "()") { - return ary; + return null; } - if (resp.ArgCount != 0) + if (resp[0] == '(') { - throw new VCExprEvaluationException(); + resp = resp.Substring(1, resp.Length - 2); } - if (expr.Type.Equals(Boogie.Type.Bool)) - { - return bool.Parse(resp.Name); - } - else if (expr.Type.Equals(Boogie.Type.Int)) - { - return Microsoft.BaseTypes.BigNum.FromString(resp.Name); - } - else - { - return resp.Name; - } + return resp.Split(' ').ToList(); } - /// - /// Extra state for ApiChecker (used by stratifiedInlining) - /// - static int nameCounter = 0; - - public override async Task<(Outcome, List)> CheckAssumptions(List assumptions, - ErrorHandler handler, CancellationToken cancellationToken) + protected int ParseRCount(SExpr resp) { - Push(); - // Name the assumptions - var nameToAssumption = new Dictionary(); - int i = 0; - foreach (var vc in assumptions) - { - var name = "a" + nameCounter.ToString(); - nameCounter++; - nameToAssumption.Add(name, i); - - string vcString = VCExpr2String(vc, 1); - AssertAxioms(); - SendThisVC(string.Format("(assert (! {0} :named {1}))", vcString, name)); - i++; - } - - Check(); - - var outcome = await CheckOutcomeCore(handler, cancellationToken, libOptions.ErrorLimit); - - if (outcome != Outcome.Valid) - { - Pop(); - return (outcome, new List()); - } - - Contract.Assert(usingUnsatCore, "SMTLib prover not setup for computing unsat cores"); - SendThisVC("(get-unsat-core)"); - var resp = await Process.GetProverResponse().WaitAsync(cancellationToken); - var unsatCore = new List(); - if (resp.Name != "") + try { - unsatCore.Add(nameToAssumption[resp.Name]); + return int.Parse(resp[0].Name); } - - foreach (var s in resp.Arguments) + catch { - unsatCore.Add(nameToAssumption[s.Name]); + // If anything goes wrong with parsing the response from the solver, + // it's better to be able to continue, even with uninformative data. + lock (proverWarnings) + { + proverWarnings.Add($"Failed to parse resource count from solver. Got: {resp}"); + } + return -1; } - - FlushLogFile(); - Pop(); - return (outcome, unsatCore); } public override void Push() @@ -2300,119 +1586,8 @@ public override void Push() SendThisVC("(push 1)"); DeclCollector.Push(); } - - public override async Task<(Outcome, List)> CheckAssumptions(List hardAssumptions, List softAssumptions, - ErrorHandler handler, CancellationToken cancellationToken) - { - - // First, convert both hard and soft assumptions to SMTLIB strings - List hardAssumptionStrings = new List(); - foreach (var a in hardAssumptions) - { - hardAssumptionStrings.Add(VCExpr2String(a, 1)); - } - - List currAssumptionStrings = new List(); - foreach (var a in softAssumptions) - { - currAssumptionStrings.Add(VCExpr2String(a, 1)); - } - - Push(); - AssertAxioms(); - foreach (var a in hardAssumptionStrings) - { - SendThisVC("(assert " + a + ")"); - } - - Check(); - Outcome outcome = await GetResponse(cancellationToken); - if (outcome != Outcome.Invalid) - { - Pop(); - return (outcome, new List()); - } - - int k = 0; - List relaxVars = new List(); - var unsatisfiedSoftAssumptions = new List(); - while (true) - { - Push(); - foreach (var a in currAssumptionStrings) - { - SendThisVC("(assert " + a + ")"); - } - - Check(); - outcome = await CheckOutcomeCore(handler, cancellationToken, libOptions.ErrorLimit); - if (outcome != Outcome.Valid) - { - break; - } - - Pop(); - string relaxVar = "relax_" + k; - relaxVars.Add(relaxVar); - SendThisVC("(declare-fun " + relaxVar + " () Int)"); - List nextAssumptionStrings = new List(); - for (int i = 0; i < currAssumptionStrings.Count; i++) - { - string constraint = "(= " + relaxVar + " " + i + ")"; - nextAssumptionStrings.Add("(or " + currAssumptionStrings[i] + " " + constraint + ")"); - } - - currAssumptionStrings = nextAssumptionStrings; - k++; - } - - if (outcome == Outcome.Invalid) - { - foreach (var relaxVar in relaxVars) - { - SendThisVC("(get-value (" + relaxVar + "))"); - FlushLogFile(); - var resp = await Process.GetProverResponse().WaitAsync(cancellationToken); - if (resp == null) - { - break; - } - - if (!(resp.Name == "" && resp.ArgCount == 1)) - { - break; - } - - resp = resp.Arguments[0]; - if (!(resp.Name != "" && resp.ArgCount == 1)) - { - break; - } - - resp = resp.Arguments[0]; - if (resp.ArgCount != 0) - { - break; - } - - if (int.TryParse(resp.Name, out var v)) - { - unsatisfiedSoftAssumptions.Add(v); - } - else - { - break; - } - } - - Pop(); - } - - Pop(); - return (outcome, unsatisfiedSoftAssumptions); - } } - + public class SMTLibProverContext : DeclFreeProverContext { internal SMTLibProcessTheoremProver parent; @@ -2535,7 +1710,11 @@ protected virtual SMTLibProcessTheoremProver SpawnProver(SMTLibOptions libOption Contract.Requires(gen != null); Contract.Requires(ctx != null); Contract.Ensures(Contract.Result() != null); - return new SMTLibProcessTheoremProver(libOptions, options, gen, ctx); + if (options.BatchMode) { + return new SMTLibBatchTheoremProver(libOptions, options, gen, ctx); + } else { + return new SMTLibInteractiveTheoremProver(libOptions, options, gen, ctx); + } } } } diff --git a/Source/Provers/SMTLib/SMTLibProverOptions.cs b/Source/Provers/SMTLib/SMTLibProverOptions.cs index cbf8f6e13..f6bc82cc4 100644 --- a/Source/Provers/SMTLib/SMTLibProverOptions.cs +++ b/Source/Provers/SMTLib/SMTLibProverOptions.cs @@ -177,10 +177,13 @@ public override string Help @" SMT-specific options: ~~~~~~~~~~~~~~~~~~~~~ -SOLVER= Use the given SMT solver (z3, cvc5, yices2, noop; default: z3) - The noop solver never does any solving and always returns unknown. -LOGIC= Pass (set-logic ) to the prover (default: empty, 'ALL' for CVC5) -USE_WEIGHTS= Pass :weight annotations on quantified formulas (default: true) +SOLVER= Use the given SMT solver (z3, cvc5, yices2, noop; + default: z3). The noop solver never does any solving + and always returns unknown. +LOGIC= Pass (set-logic ) to the prover + (default: empty, 'ALL' for CVC5) +USE_WEIGHTS= Pass :weight annotations on quantified formulas + (default: true) VERBOSITY= 1 - print prover output (default: 0) O:= Pass (set-option : ) to the SMT solver. C: Pass to the SMT solver on the command line. diff --git a/Source/Provers/SMTLib/SMTLibSolver.cs b/Source/Provers/SMTLib/SMTLibSolver.cs index 61de9de85..778ba8048 100644 --- a/Source/Provers/SMTLib/SMTLibSolver.cs +++ b/Source/Provers/SMTLib/SMTLibSolver.cs @@ -12,6 +12,8 @@ public abstract class SMTLibSolver public abstract Task GetProverResponse(); public abstract void NewProblem(string descriptiveName); + public abstract void IndicateEndOfInput(); + protected abstract void HandleError(string msg); public void Ping() diff --git a/Test/aitest9/TestIntervals.bpl b/Test/aitest9/TestIntervals.bpl index b5bf0e751..e1fc1dcc0 100644 --- a/Test/aitest9/TestIntervals.bpl +++ b/Test/aitest9/TestIntervals.bpl @@ -1,5 +1,6 @@ // RUN: %parallel-boogie "%s" -infer:j /errorTrace:0 > "%t" // RUN: %diff "%s.expect" "%t" +// UNSUPPORTED: batch_mode procedure P() { var a: int, b: int, c: int; diff --git a/Test/aitest9/TestIntervals.bpl.expect b/Test/aitest9/TestIntervals.bpl.expect index 8d799fa4b..c192354ff 100644 --- a/Test/aitest9/TestIntervals.bpl.expect +++ b/Test/aitest9/TestIntervals.bpl.expect @@ -1,5 +1,5 @@ -TestIntervals.bpl(25,3): Error: This assertion might not hold. -TestIntervals.bpl(70,3): Error: This assertion might not hold. +TestIntervals.bpl(26,3): Error: This assertion might not hold. TestIntervals.bpl(71,3): Error: This assertion might not hold. +TestIntervals.bpl(72,3): Error: This assertion might not hold. Boogie program verifier finished with 2 verified, 3 errors diff --git a/Test/civl/r2.bpl b/Test/civl/r2.bpl index 81e792794..4beb128a4 100644 --- a/Test/civl/r2.bpl +++ b/Test/civl/r2.bpl @@ -1,5 +1,6 @@ // RUN: %parallel-boogie "%s" > "%t" // RUN: %diff "%s.expect" "%t" +// UNSUPPORTED: batch_mode var {:layer 0,2} x: int; diff --git a/Test/civl/r2.bpl.expect b/Test/civl/r2.bpl.expect index d93e09746..910928d55 100644 --- a/Test/civl/r2.bpl.expect +++ b/Test/civl/r2.bpl.expect @@ -1,15 +1,15 @@ -r2.bpl(10,57): Error: On some path no yield-to-yield fragment matched the refined atomic action +r2.bpl(11,57): Error: On some path no yield-to-yield fragment matched the refined atomic action Execution trace: - r2.bpl(11,5): anon0 + r2.bpl(12,5): anon0 (0,0): Civl_call_refinement_4 - r2.bpl(21,34): inline$atomic_nop$0$Return + r2.bpl(22,34): inline$atomic_nop$0$Return (0,0): Civl_ReturnChecker -r2.bpl(10,57): Error: A yield-to-yield fragment illegally modifies layer-2 globals +r2.bpl(11,57): Error: A yield-to-yield fragment illegally modifies layer-2 globals Execution trace: - r2.bpl(11,5): anon0 + r2.bpl(12,5): anon0 (0,0): Civl_call_refinement_3 - r2.bpl(17,7): inline$atomic_incr$0$anon0 - r2.bpl(14,34): inline$atomic_incr$0$Return + r2.bpl(18,7): inline$atomic_incr$0$anon0 + r2.bpl(15,34): inline$atomic_incr$0$Return (0,0): Civl_UnchangedChecker Boogie program verifier finished with 1 verified, 2 errors diff --git a/Test/civl/refinement.bpl b/Test/civl/refinement.bpl index eb019f643..050c2a7b4 100644 --- a/Test/civl/refinement.bpl +++ b/Test/civl/refinement.bpl @@ -1,5 +1,6 @@ // RUN: %parallel-boogie "%s" > "%t" // RUN: %diff "%s.expect" "%t" +// UNSUPPORTED: batch_mode var {:layer 0,2} x:int; diff --git a/Test/civl/refinement.bpl.expect b/Test/civl/refinement.bpl.expect index 4b2b291b5..ff894057b 100644 --- a/Test/civl/refinement.bpl.expect +++ b/Test/civl/refinement.bpl.expect @@ -1,33 +1,33 @@ -refinement.bpl(6,48): Error: A yield-to-yield fragment modifies layer-2 state subsequent to a yield-to-yield fragment that already modified layer-2 state +refinement.bpl(7,48): Error: A yield-to-yield fragment modifies layer-2 state subsequent to a yield-to-yield fragment that already modified layer-2 state Execution trace: - refinement.bpl(8,3): anon0 - refinement.bpl(52,5): inline$INCR$0$anon0 - refinement.bpl(8,3): anon0_0 - refinement.bpl(52,5): inline$INCR$1$anon0 + refinement.bpl(9,3): anon0 + refinement.bpl(53,5): inline$INCR$0$anon0 + refinement.bpl(9,3): anon0_0 + refinement.bpl(53,5): inline$INCR$1$anon0 (0,0): Civl_ReturnChecker -refinement.bpl(13,48): Error: A yield-to-yield fragment modifies layer-2 state in a way that does not match the refined atomic action +refinement.bpl(14,48): Error: A yield-to-yield fragment modifies layer-2 state in a way that does not match the refined atomic action Execution trace: - refinement.bpl(15,3): anon0 - refinement.bpl(58,5): inline$DECR$0$anon0 + refinement.bpl(16,3): anon0 + refinement.bpl(59,5): inline$DECR$0$anon0 (0,0): Civl_RefinementChecker -refinement.bpl(13,48): Error: A yield-to-yield fragment modifies layer-2 state subsequent to a yield-to-yield fragment that already modified layer-2 state +refinement.bpl(14,48): Error: A yield-to-yield fragment modifies layer-2 state subsequent to a yield-to-yield fragment that already modified layer-2 state Execution trace: - refinement.bpl(15,3): anon0 - refinement.bpl(58,5): inline$DECR$0$anon0 - refinement.bpl(15,3): anon0_0 - refinement.bpl(52,5): inline$INCR$0$anon0 + refinement.bpl(16,3): anon0 + refinement.bpl(59,5): inline$DECR$0$anon0 + refinement.bpl(16,3): anon0_0 + refinement.bpl(53,5): inline$INCR$0$anon0 (0,0): Civl_ReturnChecker -refinement.bpl(33,48): Error: On some path no yield-to-yield fragment matched the refined atomic action +refinement.bpl(34,48): Error: On some path no yield-to-yield fragment matched the refined atomic action Execution trace: - refinement.bpl(36,1): anon0 + refinement.bpl(37,1): anon0 (0,0): Civl_ReturnChecker -refinement.bpl(38,48): Error: A yield-to-yield fragment illegally modifies layer-2 globals +refinement.bpl(39,48): Error: A yield-to-yield fragment illegally modifies layer-2 globals Execution trace: - refinement.bpl(40,3): anon0 - refinement.bpl(52,5): inline$INCR$0$anon0 - refinement.bpl(40,3): anon0_0 - refinement.bpl(42,3): anon2_LoopHead - refinement.bpl(52,5): inline$INCR$1$anon0 + refinement.bpl(41,3): anon0 + refinement.bpl(53,5): inline$INCR$0$anon0 + refinement.bpl(41,3): anon0_0 + refinement.bpl(43,3): anon2_LoopHead + refinement.bpl(53,5): inline$INCR$1$anon0 (0,0): Civl_UnchangedChecker Boogie program verifier finished with 3 verified, 5 errors diff --git a/Test/houdini/deterministic.bpl b/Test/houdini/deterministic.bpl index 3513c3731..d89b75d89 100644 --- a/Test/houdini/deterministic.bpl +++ b/Test/houdini/deterministic.bpl @@ -1,6 +1,9 @@ // RUN: %parallel-boogie /contractInfer /inlineDepth:1 /printAssignment "%s" > "%t" // RUN: %diff "%s.expect" "%t" +// Houdini is very interactive and doesn't work with batch mode +// UNSUPPORTED: batch_mode + function f(a:int):int; diff --git a/Test/houdini/houd1.bpl b/Test/houdini/houd1.bpl index 35e900230..0b53832e5 100644 --- a/Test/houdini/houd1.bpl +++ b/Test/houdini/houd1.bpl @@ -1,5 +1,8 @@ // RUN: %parallel-boogie -contractInfer -printAssignment "%s" > "%t" // RUN: %diff "%s.expect" "%t" + +// Houdini is very interactive and doesn't work with batch mode +// UNSUPPORTED: batch_mode const {:existential true} b1:bool; var myVar: int; diff --git a/Test/houdini/houd10.bpl b/Test/houdini/houd10.bpl index db38b553b..6948686a5 100644 --- a/Test/houdini/houd10.bpl +++ b/Test/houdini/houd10.bpl @@ -1,5 +1,8 @@ // RUN: %parallel-boogie -contractInfer -printAssignment "%s" > "%t" // RUN: %diff "%s.expect" "%t" + +// Houdini is very interactive and doesn't work with batch mode +// UNSUPPORTED: batch_mode const {:existential true} b1:bool; const {:existential true} b2:bool; const {:existential true} b3:bool; diff --git a/Test/houdini/houd10.bpl.expect b/Test/houdini/houd10.bpl.expect index bf2522e43..73675f1a0 100644 --- a/Test/houdini/houd10.bpl.expect +++ b/Test/houdini/houd10.bpl.expect @@ -2,9 +2,9 @@ Assignment computed by Houdini: b1 = True b2 = True b3 = True -houd10.bpl(17,3): Error: A precondition for this call might not hold. -houd10.bpl(22,1): Related location: This is the precondition that might not hold. +houd10.bpl(20,3): Error: A precondition for this call might not hold. +houd10.bpl(25,1): Related location: This is the precondition that might not hold. Execution trace: - houd10.bpl(16,9): anon0 + houd10.bpl(19,9): anon0 Boogie program verifier finished with 0 verified, 1 error diff --git a/Test/houdini/houd11.bpl b/Test/houdini/houd11.bpl index c0f68a009..bc49f0e95 100644 --- a/Test/houdini/houd11.bpl +++ b/Test/houdini/houd11.bpl @@ -1,6 +1,9 @@ // RUN: %parallel-boogie -contractInfer -printAssignment "%s" > "%t" // RUN: %diff "%s.expect" "%t" +// Houdini is very interactive and doesn't work with batch mode +// UNSUPPORTED: batch_mode + var fooVar: int; procedure foo() diff --git a/Test/houdini/houd11.bpl.expect b/Test/houdini/houd11.bpl.expect index f21bab9a9..65f778597 100644 --- a/Test/houdini/houd11.bpl.expect +++ b/Test/houdini/houd11.bpl.expect @@ -1,6 +1,6 @@ Assignment computed by Houdini: -houd11.bpl(10,3): Error: This assertion might not hold. +houd11.bpl(13,3): Error: This assertion might not hold. Execution trace: - houd11.bpl(9,9): anon0 + houd11.bpl(12,9): anon0 Boogie program verifier finished with 0 verified, 1 error diff --git a/Test/houdini/houd12.bpl b/Test/houdini/houd12.bpl index 18a2a7736..9f811d980 100644 --- a/Test/houdini/houd12.bpl +++ b/Test/houdini/houd12.bpl @@ -1,4 +1,7 @@ // RUN: %parallel-boogie -contractInfer -printAssignment "%s" | %OutputCheck -d "%s" + +// Houdini is very interactive and doesn't work with batch mode +// UNSUPPORTED: batch_mode // Example to test candidate annotations on loops // CHECK-L: Assignment computed by Houdini: diff --git a/Test/houdini/houd2.bpl b/Test/houdini/houd2.bpl index 324019887..3d5cf9b58 100644 --- a/Test/houdini/houd2.bpl +++ b/Test/houdini/houd2.bpl @@ -1,5 +1,8 @@ // RUN: %parallel-boogie -contractInfer -printAssignment "%s" > "%t" // RUN: %diff "%s.expect" "%t" + +// Houdini is very interactive and doesn't work with batch mode +// UNSUPPORTED: batch_mode const {:existential true} b1:bool; const {:existential true} b2:bool; diff --git a/Test/houdini/houd2.bpl.expect b/Test/houdini/houd2.bpl.expect index 7ee80bfd6..efaeb2d7b 100644 --- a/Test/houdini/houd2.bpl.expect +++ b/Test/houdini/houd2.bpl.expect @@ -1,9 +1,9 @@ Assignment computed by Houdini: b1 = False b2 = True -houd2.bpl(14,1): Error: A postcondition might not hold on this return path. -houd2.bpl(11,1): Related location: This is the postcondition that might not hold. +houd2.bpl(17,1): Error: A postcondition might not hold on this return path. +houd2.bpl(14,1): Related location: This is the postcondition that might not hold. Execution trace: - houd2.bpl(13,3): anon0 + houd2.bpl(16,3): anon0 Boogie program verifier finished with 1 verified, 1 error diff --git a/Test/houdini/houd3.bpl b/Test/houdini/houd3.bpl index a32fd5add..25bce7085 100644 --- a/Test/houdini/houd3.bpl +++ b/Test/houdini/houd3.bpl @@ -1,5 +1,8 @@ // RUN: %parallel-boogie -contractInfer -printAssignment "%s" > "%t" // RUN: %diff "%s.expect" "%t" + +// Houdini is very interactive and doesn't work with batch mode +// UNSUPPORTED: batch_mode const {:existential true} b1:bool; const {:existential true} b2:bool; diff --git a/Test/houdini/houd4.bpl b/Test/houdini/houd4.bpl index bc66f02bf..2773ba272 100644 --- a/Test/houdini/houd4.bpl +++ b/Test/houdini/houd4.bpl @@ -1,5 +1,8 @@ // RUN: %parallel-boogie -contractInfer -printAssignment "%s" > "%t" // RUN: %diff "%s.expect" "%t" + +// Houdini is very interactive and doesn't work with batch mode +// UNSUPPORTED: batch_mode const {:existential true} b1:bool; const {:existential true} b2:bool; const {:existential true} b3:bool; diff --git a/Test/houdini/houd5.bpl b/Test/houdini/houd5.bpl index 4001eb3e2..4b23ced73 100644 --- a/Test/houdini/houd5.bpl +++ b/Test/houdini/houd5.bpl @@ -1,5 +1,8 @@ // RUN: %parallel-boogie -contractInfer -printAssignment "%s" > "%t" // RUN: %diff "%s.expect" "%t" + +// Houdini is very interactive and doesn't work with batch mode +// UNSUPPORTED: batch_mode const {:existential true} b1:bool; const {:existential true} b2:bool; const {:existential true} b3:bool; diff --git a/Test/houdini/houd6.bpl b/Test/houdini/houd6.bpl index 5ac12e5b7..d471e63db 100644 --- a/Test/houdini/houd6.bpl +++ b/Test/houdini/houd6.bpl @@ -1,5 +1,8 @@ // RUN: %parallel-boogie -contractInfer -printAssignment "%s" > "%t" // RUN: %diff "%s.expect" "%t" + +// Houdini is very interactive and doesn't work with batch mode +// UNSUPPORTED: batch_mode const {:existential true} b1:bool; const {:existential true} b2:bool; const {:existential true} b3:bool; diff --git a/Test/houdini/houd7.bpl b/Test/houdini/houd7.bpl index b5a908ffe..8b557ec3b 100644 --- a/Test/houdini/houd7.bpl +++ b/Test/houdini/houd7.bpl @@ -1,5 +1,8 @@ // RUN: %parallel-boogie -contractInfer -printAssignment "%s" > "%t" // RUN: %diff "%s.expect" "%t" + +// Houdini is very interactive and doesn't work with batch mode +// UNSUPPORTED: batch_mode const {:existential true} b1:bool; const {:existential true} b2:bool; const {:existential true} b3:bool; diff --git a/Test/houdini/houd8.bpl b/Test/houdini/houd8.bpl index 601f468b5..37da9713d 100644 --- a/Test/houdini/houd8.bpl +++ b/Test/houdini/houd8.bpl @@ -1,5 +1,8 @@ // RUN: %parallel-boogie -contractInfer -printAssignment "%s" > "%t" // RUN: %diff "%s.expect" "%t" + +// Houdini is very interactive and doesn't work with batch mode +// UNSUPPORTED: batch_mode const {:existential true} b1:bool; const {:existential true} b2:bool; const {:existential true} b3:bool; diff --git a/Test/houdini/houd9.bpl b/Test/houdini/houd9.bpl index 53a78d9b5..c2674ae34 100644 --- a/Test/houdini/houd9.bpl +++ b/Test/houdini/houd9.bpl @@ -1,5 +1,8 @@ // RUN: %parallel-boogie -contractInfer -printAssignment "%s" > "%t" // RUN: %diff "%s.expect" "%t" + +// Houdini is very interactive and doesn't work with batch mode +// UNSUPPORTED: batch_mode const {:existential true} b1:bool; const {:existential true} b2:bool; const {:existential true} b3:bool; diff --git a/Test/houdini/houd9.bpl.expect b/Test/houdini/houd9.bpl.expect index f9c77e08c..ffd4608c3 100644 --- a/Test/houdini/houd9.bpl.expect +++ b/Test/houdini/houd9.bpl.expect @@ -2,8 +2,8 @@ Assignment computed by Houdini: b1 = True b2 = True b3 = True -houd9.bpl(21,3): Error: This assertion might not hold. +houd9.bpl(24,3): Error: This assertion might not hold. Execution trace: - houd9.bpl(20,9): anon0 + houd9.bpl(23,9): anon0 Boogie program verifier finished with 0 verified, 1 error diff --git a/Test/houdini/mergedProgSingle_dac.bpl b/Test/houdini/mergedProgSingle_dac.bpl index 97499ce11..e357383fa 100644 --- a/Test/houdini/mergedProgSingle_dac.bpl +++ b/Test/houdini/mergedProgSingle_dac.bpl @@ -1,6 +1,9 @@ // RUN: %parallel-boogie /contractInfer /inlineDepth:1 /printAssignment "%s" > "%t" // RUN: %diff "%s.expect" "%t" +// Houdini is very interactive and doesn't work with batch mode +// UNSUPPORTED: batch_mode + function _v2.ite(b: bool, x: int, y: int) : int; var _v2.OK: bool; diff --git a/Test/houdini/mergedProgSingle_res_ex1.bpl b/Test/houdini/mergedProgSingle_res_ex1.bpl index b1d6a2b7e..143bbe75a 100644 --- a/Test/houdini/mergedProgSingle_res_ex1.bpl +++ b/Test/houdini/mergedProgSingle_res_ex1.bpl @@ -1,6 +1,9 @@ // RUN: %parallel-boogie /contractInfer /inlineDepth:1 /printAssignment "%s" > "%t" // RUN: %diff "%s.expect" "%t" +// Houdini is very interactive and doesn't work with batch mode +// UNSUPPORTED: batch_mode + var _v2.control_flag: int; function _v2.control_UIF(arg_0: int, arg_1: int) : int; diff --git a/Test/houdini/mergedProgSingle_res_ex2.bpl b/Test/houdini/mergedProgSingle_res_ex2.bpl index a7b6bafab..fc636891a 100644 --- a/Test/houdini/mergedProgSingle_res_ex2.bpl +++ b/Test/houdini/mergedProgSingle_res_ex2.bpl @@ -1,6 +1,9 @@ // RUN: %parallel-boogie /contractInfer /inlineDepth:1 /printAssignment "%s" > "%t" // RUN: %diff "%s.expect" "%t" +// Houdini is very interactive and doesn't work with batch mode +// UNSUPPORTED: batch_mode + var _v2.control_flag: int; function _v2.control_UIF(arg_0: int, arg_1: int) : int; diff --git a/Test/houdini/test1.bpl b/Test/houdini/test1.bpl index ab7555fe2..39c797473 100644 --- a/Test/houdini/test1.bpl +++ b/Test/houdini/test1.bpl @@ -1,5 +1,8 @@ // RUN: %parallel-boogie -contractInfer -printAssignment -inlineDepth:1 "%s" > "%t" // RUN: %diff "%s.expect" "%t" + +// Houdini is very interactive and doesn't work with batch mode +// UNSUPPORTED: batch_mode var g: bool; procedure foo() diff --git a/Test/houdini/test10.bpl b/Test/houdini/test10.bpl index 36f0a1482..8ebdd5f44 100644 --- a/Test/houdini/test10.bpl +++ b/Test/houdini/test10.bpl @@ -1,5 +1,8 @@ // RUN: %parallel-boogie -contractInfer -printAssignment -inlineDepth:1 "%s" > "%t" // RUN: %diff "%s.expect" "%t" + +// Houdini is very interactive and doesn't work with batch mode +// UNSUPPORTED: batch_mode var sdv_7: int; var sdv_21: int; const {:existential true} b1: bool; diff --git a/Test/houdini/test2.bpl b/Test/houdini/test2.bpl index 5fe3ba30c..4e0915282 100644 --- a/Test/houdini/test2.bpl +++ b/Test/houdini/test2.bpl @@ -1,5 +1,8 @@ // RUN: %parallel-boogie -contractInfer -printAssignment -inlineDepth:1 "%s" > "%t" // RUN: %diff "%s.expect" "%t" + +// Houdini is very interactive and doesn't work with batch mode +// UNSUPPORTED: batch_mode var g: int; var h: int; diff --git a/Test/houdini/test7.bpl b/Test/houdini/test7.bpl index d701652cf..8d2eb556b 100644 --- a/Test/houdini/test7.bpl +++ b/Test/houdini/test7.bpl @@ -1,5 +1,8 @@ // RUN: %parallel-boogie -contractInfer -printAssignment -inlineDepth:1 "%s" > "%t" // RUN: %diff "%s.expect" "%t" + +// Houdini is very interactive and doesn't work with batch mode +// UNSUPPORTED: batch_mode var g: int; procedure main() diff --git a/Test/houdini/test8.bpl b/Test/houdini/test8.bpl index 8b5c887db..46d8e9356 100644 --- a/Test/houdini/test8.bpl +++ b/Test/houdini/test8.bpl @@ -1,5 +1,8 @@ // RUN: %parallel-boogie -contractInfer -printAssignment -inlineDepth:1 "%s" > "%t" // RUN: %diff "%s.expect" "%t" + +// Houdini is very interactive and doesn't work with batch mode +// UNSUPPORTED: batch_mode var g: int; procedure main() diff --git a/Test/houdini/test9.bpl b/Test/houdini/test9.bpl index a3fe2f8c5..871844677 100644 --- a/Test/houdini/test9.bpl +++ b/Test/houdini/test9.bpl @@ -1,5 +1,8 @@ // RUN: %parallel-boogie -contractInfer -printAssignment -inlineDepth:1 "%s" > "%t" // RUN: %diff "%s.expect" "%t" + +// Houdini is very interactive and doesn't work with batch mode +// UNSUPPORTED: batch_mode var v1: int; var v2: int; var v3: int; diff --git a/Test/houdini/testUnsatCore.bpl b/Test/houdini/testUnsatCore.bpl index 832bb4d9d..4355c9b27 100644 --- a/Test/houdini/testUnsatCore.bpl +++ b/Test/houdini/testUnsatCore.bpl @@ -1,6 +1,9 @@ // RUN: %parallel-boogie -contractInfer -printAssignment -useUnsatCoreForContractInfer "%s" > "%t" // RUN: %diff "%s.expect" "%t" +// Houdini is very interactive and doesn't work with batch mode +// UNSUPPORTED: batch_mode + // Example to exercise the unsatcore to optimize houdini procedure foo(x:int, y:int, z:int) @@ -34,4 +37,4 @@ const {:existential true} be0: bool; // and lookout for the following lines // // Number of unsat core prover queries = 2 -// Number of unsat core prunings = 2 \ No newline at end of file +// Number of unsat core prunings = 2 diff --git a/Test/inline/test4.bpl b/Test/inline/test4.bpl index 04ec36639..adeb6a9d8 100644 --- a/Test/inline/test4.bpl +++ b/Test/inline/test4.bpl @@ -1,5 +1,6 @@ // RUN: %parallel-boogie -inline:spec -print:- -env:0 -printInlined "%s" > "%t" // RUN: %diff "%s.expect" "%t" +// UNSUPPORTED: batch_mode procedure main(x:int) { @@ -52,4 +53,4 @@ procedure {:inline 3} check (A:[int]int, i:int, c:int) returns (ret:bool) ret := false; } return; -} \ No newline at end of file +} diff --git a/Test/inline/test7.bpl b/Test/inline/test7.bpl index 7fc718318..3d89a94d4 100644 --- a/Test/inline/test7.bpl +++ b/Test/inline/test7.bpl @@ -1,5 +1,6 @@ // RUN: %parallel-boogie -inline:spec -print:- -env:0 -printInlined "%s" > "%t" // RUN: %diff "%s.expect" "%t" +// UNSUPPORTED: batch_mode type ref; var arr:[int]int; diff --git a/Test/lit.site.cfg b/Test/lit.site.cfg index 026663105..fdb6a8681 100644 --- a/Test/lit.site.cfg +++ b/Test/lit.site.cfg @@ -100,6 +100,13 @@ boogieParams = lit_config.params.get('boogie_params', '') if len(boogieParams) > 0: boogieExecutable = boogieExecutable + ' ' + boogieParams +# Allow user to turn on batch mode in a way that allows skipping +# tests that don't support it. +batchMode = lit_config.params.get('batch_mode', '') == 'True' +if batchMode: + config.available_features = ['batch_mode'] + boogieExecutable += ' -proverOpt:BATCH_MODE=true' + # Inform user what executable is being used lit_config.note('Using Boogie: {}'.format(boogieExecutable)) diff --git a/Test/livevars/bla1.bpl b/Test/livevars/bla1.bpl index 441fc5cb4..f675921ff 100644 --- a/Test/livevars/bla1.bpl +++ b/Test/livevars/bla1.bpl @@ -1,5 +1,6 @@ // RUN: %parallel-boogie -useArrayTheory "%s" > "%t" // RUN: %diff "%s.expect" "%t" +// UNSUPPORTED: batch_mode var __storm_thread_done_0 : bool; var __storm_thread_done_1 : bool; var __storm_thread_done_2 : bool; diff --git a/Test/livevars/bla1.bpl.expect b/Test/livevars/bla1.bpl.expect index 7d4af8366..4204f6e8d 100644 --- a/Test/livevars/bla1.bpl.expect +++ b/Test/livevars/bla1.bpl.expect @@ -1,76 +1,76 @@ -bla1.bpl(2097,5): Error: This assertion might not hold. +bla1.bpl(2098,5): Error: This assertion might not hold. Execution trace: - bla1.bpl(751,3): start#1 - bla1.bpl(791,3): anon10_Then#1 - bla1.bpl(796,3): anon2#1 - bla1.bpl(824,3): inline$storm_IoAllocateIrp$0$label_8_case_1#1 - bla1.bpl(856,3): inline$storm_IoAllocateIrp$0$anon14_Then#1 - bla1.bpl(861,3): inline$storm_IoAllocateIrp$0$anon2#1 - bla1.bpl(881,3): inline$storm_IoAllocateIrp$0$anon16_Then#1 - bla1.bpl(886,3): inline$storm_IoAllocateIrp$0$anon5#1 - bla1.bpl(906,3): inline$storm_IoAllocateIrp$0$anon18_Then#1 - bla1.bpl(911,3): inline$storm_IoAllocateIrp$0$anon8#1 - bla1.bpl(947,3): inline$storm_IoAllocateIrp$0$anon20_Then#1 - bla1.bpl(952,3): inline$storm_IoAllocateIrp$0$anon11#1 - bla1.bpl(983,3): inline$IoGetNextIrpStackLocation$0$anon3_Then#1 - bla1.bpl(988,3): inline$IoGetNextIrpStackLocation$0$anon2#1 - bla1.bpl(1020,3): inline$storm_IoAllocateIrp$0$anon22_Then#1 - bla1.bpl(1025,3): inline$storm_IoAllocateIrp$0$anon13#1 - bla1.bpl(1037,3): inline$storm_IoAllocateIrp$0$label_36#1 - bla1.bpl(1091,3): inline$IoSetNextIrpStackLocation$0$anon6_Then#1 - bla1.bpl(1096,3): inline$IoSetNextIrpStackLocation$0$anon2#1 - bla1.bpl(1114,3): inline$IoSetNextIrpStackLocation$0$anon8_Then#1 - bla1.bpl(1119,3): inline$IoSetNextIrpStackLocation$0$anon5#1 - bla1.bpl(1159,3): inline$IoGetCurrentIrpStackLocation$0$anon3_Then#1 - bla1.bpl(1164,3): inline$IoGetCurrentIrpStackLocation$0$anon2#1 - bla1.bpl(1199,3): anon12_Then#1 - bla1.bpl(1204,3): anon5#1 - bla1.bpl(1257,3): inline$IoGetCurrentIrpStackLocation$1$anon3_Then#1 - bla1.bpl(1261,3): inline$IoGetCurrentIrpStackLocation$1$anon2#1 - bla1.bpl(1310,3): inline$I8xDeviceControl$0$anon3_Then#1 - bla1.bpl(1314,3): inline$I8xDeviceControl$0$anon2#1 - bla1.bpl(1350,3): inline$I8xKeyboardGetSysButtonEvent$0$label_9_false#1 - bla1.bpl(1378,3): inline$storm_IoSetCancelRoutine$0$label_7_false#1 - bla1.bpl(1417,3): inline$storm_IoSetCancelRoutine$0$anon9_Else#1 - bla1.bpl(1425,3): inline$storm_IoSetCancelRoutine$0$anon10_Then#1 - bla1.bpl(1433,3): inline$storm_IoSetCancelRoutine$0$anon3#1 - bla1.bpl(1440,3): inline$storm_IoSetCancelRoutine$0$anon11_Else#1 - bla1.bpl(1448,3): inline$storm_IoSetCancelRoutine$0$anon12_Then#1 - bla1.bpl(1458,3): inline$storm_IoSetCancelRoutine$0$anon6#1 - bla1.bpl(1469,3): inline$storm_IoSetCancelRoutine$0$anon13_Then#1 - bla1.bpl(1474,3): inline$storm_IoSetCancelRoutine$0$anon8#1 - bla1.bpl(1526,3): inline$I8xKeyboardGetSysButtonEvent$0$anon6_Else#1 - bla1.bpl(1534,3): inline$I8xKeyboardGetSysButtonEvent$0$anon7_Then#1 - bla1.bpl(1544,3): inline$I8xKeyboardGetSysButtonEvent$0$anon2#1 - bla1.bpl(1556,3): inline$I8xKeyboardGetSysButtonEvent$0$label_23_false#1 - bla1.bpl(1588,3): inline$I8xKeyboardGetSysButtonEvent$0$label_13_true#1 - bla1.bpl(1621,3): inline$storm_IoCompleteRequest$0$label_6_false#1 - bla1.bpl(1656,3): inline$storm_IoCompleteRequest$0$anon4_Else#1 - bla1.bpl(1664,3): inline$storm_IoCompleteRequest$0$anon5_Then#1 - bla1.bpl(1674,3): inline$storm_IoCompleteRequest$0$anon2#1 - bla1.bpl(1678,3): inline$storm_IoCompleteRequest$0$label_1#1 - bla1.bpl(1736,3): anon14_Then#1 - bla1.bpl(1741,3): anon7#1 - bla1.bpl(1798,3): inline$storm_IoCancelIrp$0$anon12_Then#1 - bla1.bpl(1803,3): inline$storm_IoCancelIrp$0$anon2#1 - bla1.bpl(1814,3): inline$storm_IoCancelIrp$0$anon14_Else#1 - bla1.bpl(1822,3): inline$storm_IoCancelIrp$0$anon15_Then#1 - bla1.bpl(1832,3): inline$storm_IoCancelIrp$0$anon5#1 - bla1.bpl(1840,3): inline$storm_IoCancelIrp$0$anon16_Else#1 - bla1.bpl(1848,3): inline$storm_IoCancelIrp$0$anon17_Then#1 - bla1.bpl(1858,3): inline$storm_IoCancelIrp$0$anon8#1 - bla1.bpl(1869,3): inline$storm_IoCancelIrp$0$anon18_Then#1 - bla1.bpl(1874,3): inline$storm_IoCancelIrp$0$anon10#1 - bla1.bpl(1934,3): inline$storm_IoAcquireCancelSpinLock$0$label_11_true#1 - bla1.bpl(1949,3): inline$storm_IoAcquireCancelSpinLock$0$anon6_Else#1 - bla1.bpl(1957,3): inline$storm_IoAcquireCancelSpinLock$0$anon7_Then#1 - bla1.bpl(1967,3): inline$storm_IoAcquireCancelSpinLock$0$anon3#1 - bla1.bpl(1978,3): inline$storm_IoAcquireCancelSpinLock$0$anon8_Then#1 - bla1.bpl(1983,3): inline$storm_IoAcquireCancelSpinLock$0$anon5#1 - bla1.bpl(2007,3): inline$storm_IoCancelIrp$0$label_16_true#1 - bla1.bpl(2025,3): inline$storm_IoCancelIrp$0$label_22_true#1 - bla1.bpl(2038,3): inline$storm_IoCancelIrp$0$label_25_false#1 - bla1.bpl(2078,3): anon15_Then#1 - bla1.bpl(2083,3): anon9#1 + bla1.bpl(752,3): start#1 + bla1.bpl(792,3): anon10_Then#1 + bla1.bpl(797,3): anon2#1 + bla1.bpl(825,3): inline$storm_IoAllocateIrp$0$label_8_case_1#1 + bla1.bpl(857,3): inline$storm_IoAllocateIrp$0$anon14_Then#1 + bla1.bpl(862,3): inline$storm_IoAllocateIrp$0$anon2#1 + bla1.bpl(882,3): inline$storm_IoAllocateIrp$0$anon16_Then#1 + bla1.bpl(887,3): inline$storm_IoAllocateIrp$0$anon5#1 + bla1.bpl(907,3): inline$storm_IoAllocateIrp$0$anon18_Then#1 + bla1.bpl(912,3): inline$storm_IoAllocateIrp$0$anon8#1 + bla1.bpl(948,3): inline$storm_IoAllocateIrp$0$anon20_Then#1 + bla1.bpl(953,3): inline$storm_IoAllocateIrp$0$anon11#1 + bla1.bpl(984,3): inline$IoGetNextIrpStackLocation$0$anon3_Then#1 + bla1.bpl(989,3): inline$IoGetNextIrpStackLocation$0$anon2#1 + bla1.bpl(1021,3): inline$storm_IoAllocateIrp$0$anon22_Then#1 + bla1.bpl(1026,3): inline$storm_IoAllocateIrp$0$anon13#1 + bla1.bpl(1038,3): inline$storm_IoAllocateIrp$0$label_36#1 + bla1.bpl(1092,3): inline$IoSetNextIrpStackLocation$0$anon6_Then#1 + bla1.bpl(1097,3): inline$IoSetNextIrpStackLocation$0$anon2#1 + bla1.bpl(1115,3): inline$IoSetNextIrpStackLocation$0$anon8_Then#1 + bla1.bpl(1120,3): inline$IoSetNextIrpStackLocation$0$anon5#1 + bla1.bpl(1160,3): inline$IoGetCurrentIrpStackLocation$0$anon3_Then#1 + bla1.bpl(1165,3): inline$IoGetCurrentIrpStackLocation$0$anon2#1 + bla1.bpl(1200,3): anon12_Then#1 + bla1.bpl(1205,3): anon5#1 + bla1.bpl(1258,3): inline$IoGetCurrentIrpStackLocation$1$anon3_Then#1 + bla1.bpl(1262,3): inline$IoGetCurrentIrpStackLocation$1$anon2#1 + bla1.bpl(1311,3): inline$I8xDeviceControl$0$anon3_Then#1 + bla1.bpl(1315,3): inline$I8xDeviceControl$0$anon2#1 + bla1.bpl(1351,3): inline$I8xKeyboardGetSysButtonEvent$0$label_9_false#1 + bla1.bpl(1379,3): inline$storm_IoSetCancelRoutine$0$label_7_false#1 + bla1.bpl(1418,3): inline$storm_IoSetCancelRoutine$0$anon9_Else#1 + bla1.bpl(1426,3): inline$storm_IoSetCancelRoutine$0$anon10_Then#1 + bla1.bpl(1434,3): inline$storm_IoSetCancelRoutine$0$anon3#1 + bla1.bpl(1441,3): inline$storm_IoSetCancelRoutine$0$anon11_Else#1 + bla1.bpl(1449,3): inline$storm_IoSetCancelRoutine$0$anon12_Then#1 + bla1.bpl(1459,3): inline$storm_IoSetCancelRoutine$0$anon6#1 + bla1.bpl(1470,3): inline$storm_IoSetCancelRoutine$0$anon13_Then#1 + bla1.bpl(1475,3): inline$storm_IoSetCancelRoutine$0$anon8#1 + bla1.bpl(1527,3): inline$I8xKeyboardGetSysButtonEvent$0$anon6_Else#1 + bla1.bpl(1535,3): inline$I8xKeyboardGetSysButtonEvent$0$anon7_Then#1 + bla1.bpl(1545,3): inline$I8xKeyboardGetSysButtonEvent$0$anon2#1 + bla1.bpl(1557,3): inline$I8xKeyboardGetSysButtonEvent$0$label_23_false#1 + bla1.bpl(1589,3): inline$I8xKeyboardGetSysButtonEvent$0$label_13_true#1 + bla1.bpl(1622,3): inline$storm_IoCompleteRequest$0$label_6_false#1 + bla1.bpl(1657,3): inline$storm_IoCompleteRequest$0$anon4_Else#1 + bla1.bpl(1665,3): inline$storm_IoCompleteRequest$0$anon5_Then#1 + bla1.bpl(1675,3): inline$storm_IoCompleteRequest$0$anon2#1 + bla1.bpl(1679,3): inline$storm_IoCompleteRequest$0$label_1#1 + bla1.bpl(1737,3): anon14_Then#1 + bla1.bpl(1742,3): anon7#1 + bla1.bpl(1799,3): inline$storm_IoCancelIrp$0$anon12_Then#1 + bla1.bpl(1804,3): inline$storm_IoCancelIrp$0$anon2#1 + bla1.bpl(1815,3): inline$storm_IoCancelIrp$0$anon14_Else#1 + bla1.bpl(1823,3): inline$storm_IoCancelIrp$0$anon15_Then#1 + bla1.bpl(1833,3): inline$storm_IoCancelIrp$0$anon5#1 + bla1.bpl(1841,3): inline$storm_IoCancelIrp$0$anon16_Else#1 + bla1.bpl(1849,3): inline$storm_IoCancelIrp$0$anon17_Then#1 + bla1.bpl(1859,3): inline$storm_IoCancelIrp$0$anon8#1 + bla1.bpl(1870,3): inline$storm_IoCancelIrp$0$anon18_Then#1 + bla1.bpl(1875,3): inline$storm_IoCancelIrp$0$anon10#1 + bla1.bpl(1935,3): inline$storm_IoAcquireCancelSpinLock$0$label_11_true#1 + bla1.bpl(1950,3): inline$storm_IoAcquireCancelSpinLock$0$anon6_Else#1 + bla1.bpl(1958,3): inline$storm_IoAcquireCancelSpinLock$0$anon7_Then#1 + bla1.bpl(1968,3): inline$storm_IoAcquireCancelSpinLock$0$anon3#1 + bla1.bpl(1979,3): inline$storm_IoAcquireCancelSpinLock$0$anon8_Then#1 + bla1.bpl(1984,3): inline$storm_IoAcquireCancelSpinLock$0$anon5#1 + bla1.bpl(2008,3): inline$storm_IoCancelIrp$0$label_16_true#1 + bla1.bpl(2026,3): inline$storm_IoCancelIrp$0$label_22_true#1 + bla1.bpl(2039,3): inline$storm_IoCancelIrp$0$label_25_false#1 + bla1.bpl(2079,3): anon15_Then#1 + bla1.bpl(2084,3): anon9#1 Boogie program verifier finished with 0 verified, 1 error diff --git a/Test/livevars/daytona_bug2_ioctl_example_2.bpl b/Test/livevars/daytona_bug2_ioctl_example_2.bpl index f223187f6..ab6db35a1 100644 --- a/Test/livevars/daytona_bug2_ioctl_example_2.bpl +++ b/Test/livevars/daytona_bug2_ioctl_example_2.bpl @@ -1,5 +1,6 @@ // RUN: %parallel-boogie -useArrayTheory -errorTrace:0 "%s" > "%t" // RUN: %diff "%s.expect" "%t" +// UNSUPPORTED: batch_mode var __storm_thread_done_0 : bool; var __storm_thread_done_1 : bool; var __storm_thread_done_2 : bool; diff --git a/Test/livevars/daytona_bug2_ioctl_example_2.bpl.expect b/Test/livevars/daytona_bug2_ioctl_example_2.bpl.expect index 1beec9445..7b47f9bd6 100644 --- a/Test/livevars/daytona_bug2_ioctl_example_2.bpl.expect +++ b/Test/livevars/daytona_bug2_ioctl_example_2.bpl.expect @@ -1,3 +1,3 @@ -daytona_bug2_ioctl_example_2.bpl(4835,5): Error: This assertion might not hold. +daytona_bug2_ioctl_example_2.bpl(4836,5): Error: This assertion might not hold. Boogie program verifier finished with 0 verified, 1 error diff --git a/Test/livevars/stack_overflow.bpl b/Test/livevars/stack_overflow.bpl index aa6532656..50d915347 100644 --- a/Test/livevars/stack_overflow.bpl +++ b/Test/livevars/stack_overflow.bpl @@ -1,6 +1,9 @@ // RUN: %parallel-boogie -useArrayTheory /errorTrace:0 "%s" > "%t" // RUN: %diff "%s.expect" "%t" +// Stress test that we really don't need ot run multiple times +// UNSUPPORTED: batch_mode + var raiseException : bool; var errorReached : bool; var k : int; diff --git a/Test/livevars/stack_overflow.bpl.expect b/Test/livevars/stack_overflow.bpl.expect index 95cc3a316..092cd6939 100644 --- a/Test/livevars/stack_overflow.bpl.expect +++ b/Test/livevars/stack_overflow.bpl.expect @@ -1,3 +1,3 @@ -stack_overflow.bpl(97944,5): Error: This assertion might not hold. +stack_overflow.bpl(97947,5): Error: This assertion might not hold. Boogie program verifier finished with 0 verified, 1 error diff --git a/Test/prover/batch-solver.bpl b/Test/prover/batch-solver.bpl new file mode 100644 index 000000000..45f421bf7 --- /dev/null +++ b/Test/prover/batch-solver.bpl @@ -0,0 +1,8 @@ +// RUN: %boogie /trace /proverOpt:BATCH_MODE=true "%s" > "%t" +// RUN: %OutputCheck --file-to-check "%t" "%s" +// CHECK: \[TRACE\] Running in batch mode. +// CHECK: Boogie program verifier finished with 1 verified, 0 errors + +procedure P(x: int, y: int) { + assert x*y == y*x; +} diff --git a/Test/prover/mocksolver.sh b/Test/prover/mocksolver.sh index 1dfc3a6cf..9d0f9c603 100755 --- a/Test/prover/mocksolver.sh +++ b/Test/prover/mocksolver.sh @@ -1,10 +1,11 @@ #!/bin/bash -while true ; do - read cmd +while read cmd ; do case $cmd in *get-info*name* ) echo "(:name \"Z3\")" ;; *get-info*rlimit* ) echo "(:rlimit bad)" ;; + *get-info*reason-unknown* ) echo "incomplete" ;; *check-sat* ) echo "unsat" ;; + *get-model* ) echo "(error \"model is not available\")" ;; * ) continue ;; esac done diff --git a/Test/pruning/UsesClauses.bpl b/Test/pruning/UsesClauses.bpl index 7aea958cc..0ec5f06c5 100644 --- a/Test/pruning/UsesClauses.bpl +++ b/Test/pruning/UsesClauses.bpl @@ -1,5 +1,6 @@ // RUN: %parallel-boogie /prune /printPruned:"%tpruned" /errorTrace:0 "%s" > "%t" // RUN: %diff "%s.expect" "%t" +// UNSUPPORTED: batch_mode const unique four: int; const unique ProducerConst: bool uses { @@ -26,4 +27,4 @@ procedure doesNotHaveAxioms() ensures four == 4; // The ProducerConstant axiom is pruned away, so this fails to verify { -} \ No newline at end of file +} diff --git a/Test/pruning/UsesClauses.bpl.expect b/Test/pruning/UsesClauses.bpl.expect index 6b51bf017..96d455665 100644 --- a/Test/pruning/UsesClauses.bpl.expect +++ b/Test/pruning/UsesClauses.bpl.expect @@ -1,6 +1,6 @@ -UsesClauses.bpl(29,1): Error: A postcondition might not hold on this return path. -UsesClauses.bpl(25,3): Related location: This is the postcondition that might not hold. -UsesClauses.bpl(29,1): Error: A postcondition might not hold on this return path. +UsesClauses.bpl(30,1): Error: A postcondition might not hold on this return path. UsesClauses.bpl(26,3): Related location: This is the postcondition that might not hold. +UsesClauses.bpl(30,1): Error: A postcondition might not hold on this return path. +UsesClauses.bpl(27,3): Related location: This is the postcondition that might not hold. Boogie program verifier finished with 1 verified, 2 errors diff --git a/Test/snapshots/runtest.snapshot b/Test/snapshots/runtest.snapshot index 02f5d84a0..ac62914f9 100644 --- a/Test/snapshots/runtest.snapshot +++ b/Test/snapshots/runtest.snapshot @@ -2,3 +2,4 @@ // RUN: %boogie -errorTrace:0 -traceCaching:1 -verifySnapshots:2 -verifySeparately 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: %boogie -errorTrace:0 -traceCaching:1 -verifySnapshots:3 -verifySeparately Snapshots41.bpl >> "%t" // RUN: %diff "%s.expect" "%t" +// UNSUPPORTED: batch_mode diff --git a/Test/test13/ManyErrors.bpl b/Test/test13/ManyErrors.bpl index 4e1b9707b..f3c353f6f 100644 --- a/Test/test13/ManyErrors.bpl +++ b/Test/test13/ManyErrors.bpl @@ -1,5 +1,6 @@ // RUN: %parallel-boogie -errorLimit:0 "%s" > "%t" // RUN: %diff "%s.expect" "%t" +// UNSUPPORTED: batch_mode procedure ManyErrors() { L0: goto L1, L2, L3, L4, L5, L6, L7, L8, L9, L10; diff --git a/Test/test13/ManyErrors.bpl.expect b/Test/test13/ManyErrors.bpl.expect index 4bf4f781b..c84534f63 100644 --- a/Test/test13/ManyErrors.bpl.expect +++ b/Test/test13/ManyErrors.bpl.expect @@ -1,42 +1,42 @@ -ManyErrors.bpl(6,9): Error: This assertion might not hold. -Execution trace: - ManyErrors.bpl(5,5): L0 - ManyErrors.bpl(6,5): L1 ManyErrors.bpl(7,9): Error: This assertion might not hold. Execution trace: - ManyErrors.bpl(5,5): L0 - ManyErrors.bpl(7,5): L2 + ManyErrors.bpl(6,5): L0 + ManyErrors.bpl(7,5): L1 ManyErrors.bpl(8,9): Error: This assertion might not hold. Execution trace: - ManyErrors.bpl(5,5): L0 - ManyErrors.bpl(8,5): L3 + ManyErrors.bpl(6,5): L0 + ManyErrors.bpl(8,5): L2 ManyErrors.bpl(9,9): Error: This assertion might not hold. Execution trace: - ManyErrors.bpl(5,5): L0 - ManyErrors.bpl(9,5): L4 + ManyErrors.bpl(6,5): L0 + ManyErrors.bpl(9,5): L3 ManyErrors.bpl(10,9): Error: This assertion might not hold. Execution trace: - ManyErrors.bpl(5,5): L0 - ManyErrors.bpl(10,5): L5 + ManyErrors.bpl(6,5): L0 + ManyErrors.bpl(10,5): L4 ManyErrors.bpl(11,9): Error: This assertion might not hold. Execution trace: - ManyErrors.bpl(5,5): L0 - ManyErrors.bpl(11,5): L6 + ManyErrors.bpl(6,5): L0 + ManyErrors.bpl(11,5): L5 ManyErrors.bpl(12,9): Error: This assertion might not hold. Execution trace: - ManyErrors.bpl(5,5): L0 - ManyErrors.bpl(12,5): L7 + ManyErrors.bpl(6,5): L0 + ManyErrors.bpl(12,5): L6 ManyErrors.bpl(13,9): Error: This assertion might not hold. Execution trace: - ManyErrors.bpl(5,5): L0 - ManyErrors.bpl(13,5): L8 + ManyErrors.bpl(6,5): L0 + ManyErrors.bpl(13,5): L7 ManyErrors.bpl(14,9): Error: This assertion might not hold. Execution trace: - ManyErrors.bpl(5,5): L0 - ManyErrors.bpl(14,5): L9 -ManyErrors.bpl(15,10): Error: This assertion might not hold. + ManyErrors.bpl(6,5): L0 + ManyErrors.bpl(14,5): L8 +ManyErrors.bpl(15,9): Error: This assertion might not hold. +Execution trace: + ManyErrors.bpl(6,5): L0 + ManyErrors.bpl(15,5): L9 +ManyErrors.bpl(16,10): Error: This assertion might not hold. Execution trace: - ManyErrors.bpl(5,5): L0 - ManyErrors.bpl(15,5): L10 + ManyErrors.bpl(6,5): L0 + ManyErrors.bpl(16,5): L10 Boogie program verifier finished with 0 verified, 10 errors diff --git a/Test/test15/MoreCapturedStates.bpl b/Test/test15/MoreCapturedStates.bpl index 5e8e7d204..6cc79f6d5 100644 --- a/Test/test15/MoreCapturedStates.bpl +++ b/Test/test15/MoreCapturedStates.bpl @@ -1,6 +1,7 @@ // RUN: %boogie "%s" -normalizeNames:1 -mv:"%t".model > "%t" // RUN: grep STATE "%t".model >> "%t" // RUN: %diff "%s.expect" "%t" +// UNSUPPORTED: batch_mode procedure Abs(x: int) returns (y: int) ensures y >= 0; diff --git a/Test/test15/MoreCapturedStates.bpl.expect b/Test/test15/MoreCapturedStates.bpl.expect index 93b648da9..67c748708 100644 --- a/Test/test15/MoreCapturedStates.bpl.expect +++ b/Test/test15/MoreCapturedStates.bpl.expect @@ -1,20 +1,20 @@ -MoreCapturedStates.bpl(34,1): Error: A postcondition might not hold on this return path. -MoreCapturedStates.bpl(6,3): Related location: This is the postcondition that might not hold. +MoreCapturedStates.bpl(35,1): Error: A postcondition might not hold on this return path. +MoreCapturedStates.bpl(7,3): Related location: This is the postcondition that might not hold. Execution trace: - MoreCapturedStates.bpl(8,3): anon0 - MoreCapturedStates.bpl(17,3): LabelB - MoreCapturedStates.bpl(32,3): Done -MoreCapturedStates.bpl(53,3): Error: A precondition for this call might not hold. -MoreCapturedStates.bpl(62,3): Related location: This is the precondition that might not hold. + MoreCapturedStates.bpl(9,3): anon0 + MoreCapturedStates.bpl(18,3): LabelB + MoreCapturedStates.bpl(33,3): Done +MoreCapturedStates.bpl(54,3): Error: A precondition for this call might not hold. +MoreCapturedStates.bpl(63,3): Related location: This is the precondition that might not hold. Execution trace: - MoreCapturedStates.bpl(40,3): anon0 - MoreCapturedStates.bpl(49,3): LabelB -MoreCapturedStates.bpl(79,3): Error: This assertion might not hold. + MoreCapturedStates.bpl(41,3): anon0 + MoreCapturedStates.bpl(50,3): LabelB +MoreCapturedStates.bpl(80,3): Error: This assertion might not hold. Execution trace: - MoreCapturedStates.bpl(67,3): anon0 -MoreCapturedStates.bpl(85,3): Error: This assertion might not hold. + MoreCapturedStates.bpl(68,3): anon0 +MoreCapturedStates.bpl(86,3): Error: This assertion might not hold. Execution trace: - MoreCapturedStates.bpl(67,3): anon0 + MoreCapturedStates.bpl(68,3): anon0 Boogie program verifier finished with 0 verified, 4 errors *** STATE diff --git a/Test/test2/Implies.bpl b/Test/test2/Implies.bpl index f2cdc97c2..f13df55e2 100644 --- a/Test/test2/Implies.bpl +++ b/Test/test2/Implies.bpl @@ -1,5 +1,6 @@ // RUN: %parallel-boogie "%s" > "%t" // RUN: %diff "%s.expect" "%t" +// UNSUPPORTED: batch_mode const a:bool; const b:bool; diff --git a/Test/test2/Implies.bpl.expect b/Test/test2/Implies.bpl.expect index ae7aa9d3c..2f98669b0 100644 --- a/Test/test2/Implies.bpl.expect +++ b/Test/test2/Implies.bpl.expect @@ -1,26 +1,26 @@ -Implies.bpl(14,3): Error: This assertion might not hold. +Implies.bpl(15,3): Error: This assertion might not hold. Execution trace: - Implies.bpl(13,3): anon0 -Implies.bpl(17,3): Error: This assertion might not hold. + Implies.bpl(14,3): anon0 +Implies.bpl(18,3): Error: This assertion might not hold. Execution trace: - Implies.bpl(13,3): anon0 -Implies.bpl(21,3): Error: This assertion might not hold. + Implies.bpl(14,3): anon0 +Implies.bpl(22,3): Error: This assertion might not hold. Execution trace: - Implies.bpl(21,3): anon0 -Implies.bpl(26,3): Error: This assertion might not hold. -Execution trace: - Implies.bpl(26,3): anon0 + Implies.bpl(22,3): anon0 Implies.bpl(27,3): Error: This assertion might not hold. Execution trace: - Implies.bpl(26,3): anon0 -Implies.bpl(31,3): Error: This assertion might not hold. + Implies.bpl(27,3): anon0 +Implies.bpl(28,3): Error: This assertion might not hold. Execution trace: - Implies.bpl(31,3): anon0 -Implies.bpl(36,3): Error: This assertion might not hold. + Implies.bpl(27,3): anon0 +Implies.bpl(32,3): Error: This assertion might not hold. Execution trace: - Implies.bpl(36,3): anon0 + Implies.bpl(32,3): anon0 Implies.bpl(37,3): Error: This assertion might not hold. Execution trace: - Implies.bpl(36,3): anon0 + Implies.bpl(37,3): anon0 +Implies.bpl(38,3): Error: This assertion might not hold. +Execution trace: + Implies.bpl(37,3): anon0 Boogie program verifier finished with 0 verified, 8 errors diff --git a/Test/test2/IncompleteArithmetic-RealTypes.bpl b/Test/test2/IncompleteArithmetic-RealTypes.bpl index 66014cb30..dbe89df39 100644 --- a/Test/test2/IncompleteArithmetic-RealTypes.bpl +++ b/Test/test2/IncompleteArithmetic-RealTypes.bpl @@ -1,5 +1,6 @@ // RUN: %parallel-boogie "-proverOpt:O:smt.arith.solver=2" "%s" > "%t" // RUN: %diff "%s.expect" "%t" +// UNSUPPORTED: batch_mode // This file is generated from $DAFNY/Test/dafny0/RealTypes.dfy using Dafny. // Note that Dafny currently uses smt.arith.solver=2, which is part of this test diff --git a/Test/test2/IncompleteArithmetic-RealTypes.bpl.expect b/Test/test2/IncompleteArithmetic-RealTypes.bpl.expect index 8144da7b9..14f19f45a 100644 --- a/Test/test2/IncompleteArithmetic-RealTypes.bpl.expect +++ b/Test/test2/IncompleteArithmetic-RealTypes.bpl.expect @@ -1,23 +1,23 @@ -IncompleteArithmetic-RealTypes.bpl(3138,9): Error: This assertion might not hold. +IncompleteArithmetic-RealTypes.bpl(3139,9): Error: This assertion might not hold. Execution trace: - IncompleteArithmetic-RealTypes.bpl(3112,13): anon0 - IncompleteArithmetic-RealTypes.bpl(3134,9): anon6_Then -IncompleteArithmetic-RealTypes.bpl(3147,9): Error: This assertion might not hold. + IncompleteArithmetic-RealTypes.bpl(3113,13): anon0 + IncompleteArithmetic-RealTypes.bpl(3135,9): anon6_Then +IncompleteArithmetic-RealTypes.bpl(3148,9): Error: This assertion might not hold. Execution trace: - IncompleteArithmetic-RealTypes.bpl(3112,13): anon0 - IncompleteArithmetic-RealTypes.bpl(3144,9): anon7_Then -IncompleteArithmetic-RealTypes.bpl(3208,5): Error: This assertion might not hold. + IncompleteArithmetic-RealTypes.bpl(3113,13): anon0 + IncompleteArithmetic-RealTypes.bpl(3145,9): anon7_Then +IncompleteArithmetic-RealTypes.bpl(3209,5): Error: This assertion might not hold. Execution trace: - IncompleteArithmetic-RealTypes.bpl(3195,13): anon0 - IncompleteArithmetic-RealTypes.bpl(3200,5): anon3_Else - IncompleteArithmetic-RealTypes.bpl(3205,5): anon2 -IncompleteArithmetic-RealTypes.bpl(3210,5): Error: This assertion might not hold. + IncompleteArithmetic-RealTypes.bpl(3196,13): anon0 + IncompleteArithmetic-RealTypes.bpl(3201,5): anon3_Else + IncompleteArithmetic-RealTypes.bpl(3206,5): anon2 +IncompleteArithmetic-RealTypes.bpl(3211,5): Error: This assertion might not hold. Execution trace: - IncompleteArithmetic-RealTypes.bpl(3195,13): anon0 - IncompleteArithmetic-RealTypes.bpl(3200,5): anon3_Else - IncompleteArithmetic-RealTypes.bpl(3205,5): anon2 -IncompleteArithmetic-RealTypes.bpl(3265,5): Error: This assertion might not hold. + IncompleteArithmetic-RealTypes.bpl(3196,13): anon0 + IncompleteArithmetic-RealTypes.bpl(3201,5): anon3_Else + IncompleteArithmetic-RealTypes.bpl(3206,5): anon2 +IncompleteArithmetic-RealTypes.bpl(3266,5): Error: This assertion might not hold. Execution trace: - IncompleteArithmetic-RealTypes.bpl(3251,13): anon0 + IncompleteArithmetic-RealTypes.bpl(3252,13): anon0 Boogie program verifier finished with 2 verified, 5 errors diff --git a/Test/test2/InvariantVerifiedUnder0.bpl b/Test/test2/InvariantVerifiedUnder0.bpl index 6d2ae9433..5c2062188 100644 --- a/Test/test2/InvariantVerifiedUnder0.bpl +++ b/Test/test2/InvariantVerifiedUnder0.bpl @@ -1,5 +1,6 @@ // RUN: %parallel-boogie "%s" > "%t" // RUN: %diff "%s.expect" "%t" +// UNSUPPORTED: batch_mode procedure Test0() { diff --git a/Test/test2/InvariantVerifiedUnder0.bpl.expect b/Test/test2/InvariantVerifiedUnder0.bpl.expect index ff516e922..bfd5835d0 100644 --- a/Test/test2/InvariantVerifiedUnder0.bpl.expect +++ b/Test/test2/InvariantVerifiedUnder0.bpl.expect @@ -1,23 +1,23 @@ -InvariantVerifiedUnder0.bpl(7,7): Error: This assertion might not hold. +InvariantVerifiedUnder0.bpl(8,7): Error: This assertion might not hold. Execution trace: - InvariantVerifiedUnder0.bpl(6,5): anon0 -InvariantVerifiedUnder0.bpl(23,7): Error: This loop invariant might not hold on entry. -Execution trace: - InvariantVerifiedUnder0.bpl(22,5): anon0 + InvariantVerifiedUnder0.bpl(7,5): anon0 InvariantVerifiedUnder0.bpl(24,7): Error: This loop invariant might not hold on entry. Execution trace: - InvariantVerifiedUnder0.bpl(22,5): anon0 -InvariantVerifiedUnder0.bpl(34,7): Error: This loop invariant might not hold on entry. + InvariantVerifiedUnder0.bpl(23,5): anon0 +InvariantVerifiedUnder0.bpl(25,7): Error: This loop invariant might not hold on entry. Execution trace: - InvariantVerifiedUnder0.bpl(32,5): anon0 -InvariantVerifiedUnder0.bpl(41,7): Error: This loop invariant might not hold on entry. + InvariantVerifiedUnder0.bpl(23,5): anon0 +InvariantVerifiedUnder0.bpl(35,7): Error: This loop invariant might not hold on entry. Execution trace: - InvariantVerifiedUnder0.bpl(40,5): anon0 + InvariantVerifiedUnder0.bpl(33,5): anon0 InvariantVerifiedUnder0.bpl(42,7): Error: This loop invariant might not hold on entry. Execution trace: - InvariantVerifiedUnder0.bpl(40,5): anon0 -InvariantVerifiedUnder0.bpl(51,7): Error: This loop invariant might not hold on entry. + InvariantVerifiedUnder0.bpl(41,5): anon0 +InvariantVerifiedUnder0.bpl(43,7): Error: This loop invariant might not hold on entry. +Execution trace: + InvariantVerifiedUnder0.bpl(41,5): anon0 +InvariantVerifiedUnder0.bpl(52,7): Error: This loop invariant might not hold on entry. Execution trace: - InvariantVerifiedUnder0.bpl(50,5): anon0 + InvariantVerifiedUnder0.bpl(51,5): anon0 Boogie program verifier finished with 1 verified, 7 errors diff --git a/Test/test2/Lambda.bpl b/Test/test2/Lambda.bpl index 67f2e612a..2202fffc0 100644 --- a/Test/test2/Lambda.bpl +++ b/Test/test2/Lambda.bpl @@ -2,6 +2,7 @@ // RUN: %diff "%s.expect" "%t" // RUN: %parallel-boogie "%s" > "%t" // RUN: %diff "%s.expect" "%t" +// UNSUPPORTED: batch_mode procedure foo() { var a: [int]int; diff --git a/Test/test2/Lambda.bpl.expect b/Test/test2/Lambda.bpl.expect index a11d3731f..908e8a821 100644 --- a/Test/test2/Lambda.bpl.expect +++ b/Test/test2/Lambda.bpl.expect @@ -1,8 +1,8 @@ -Lambda.bpl(41,3): Error: This assertion might not hold. -Execution trace: - Lambda.bpl(40,5): anon0 Lambda.bpl(42,3): Error: This assertion might not hold. Execution trace: - Lambda.bpl(40,5): anon0 + Lambda.bpl(41,5): anon0 +Lambda.bpl(43,3): Error: This assertion might not hold. +Execution trace: + Lambda.bpl(41,5): anon0 Boogie program verifier finished with 6 verified, 2 errors diff --git a/Test/test2/LambdaExt.bpl b/Test/test2/LambdaExt.bpl index 881e6dc76..1b7e49725 100644 --- a/Test/test2/LambdaExt.bpl +++ b/Test/test2/LambdaExt.bpl @@ -1,6 +1,7 @@ // RUN: %parallel-boogie "%s" > "%t" // RUN: %parallel-boogie -freeVarLambdaLifting "%s" >> "%t" // RUN: %diff "%s.expect" "%t" +// UNSUPPORTED: batch_mode procedure Simplest() { var id1, id2 : [int]int; diff --git a/Test/test2/LambdaExt.bpl.expect b/Test/test2/LambdaExt.bpl.expect index f499a8e1e..275bd5e9d 100644 --- a/Test/test2/LambdaExt.bpl.expect +++ b/Test/test2/LambdaExt.bpl.expect @@ -1,100 +1,100 @@ -LambdaExt.bpl(11,3): Error: This assertion might not hold. +LambdaExt.bpl(12,3): Error: This assertion might not hold. Execution trace: - LambdaExt.bpl(7,7): anon0 -LambdaExt.bpl(27,3): Error: This assertion might not hold. + LambdaExt.bpl(8,7): anon0 +LambdaExt.bpl(28,3): Error: This assertion might not hold. Execution trace: - LambdaExt.bpl(18,5): anon0 -LambdaExt.bpl(31,3): Error: This assertion might not hold. + LambdaExt.bpl(19,5): anon0 +LambdaExt.bpl(32,3): Error: This assertion might not hold. Execution trace: - LambdaExt.bpl(18,5): anon0 -LambdaExt.bpl(35,3): Error: This assertion might not hold. + LambdaExt.bpl(19,5): anon0 +LambdaExt.bpl(36,3): Error: This assertion might not hold. Execution trace: - LambdaExt.bpl(35,3): anon0 -LambdaExt.bpl(82,5): Error: This assertion might not hold. + LambdaExt.bpl(36,3): anon0 +LambdaExt.bpl(83,5): Error: This assertion might not hold. Execution trace: - LambdaExt.bpl(77,5): anon0 - LambdaExt.bpl(82,5): anon3_Else -LambdaExt.bpl(100,5): Error: This assertion might not hold. + LambdaExt.bpl(78,5): anon0 + LambdaExt.bpl(83,5): anon3_Else +LambdaExt.bpl(101,5): Error: This assertion might not hold. Execution trace: - LambdaExt.bpl(95,5): anon0 - LambdaExt.bpl(100,5): anon3_Else -LambdaExt.bpl(118,5): Error: This assertion might not hold. + LambdaExt.bpl(96,5): anon0 + LambdaExt.bpl(101,5): anon3_Else +LambdaExt.bpl(119,5): Error: This assertion might not hold. Execution trace: - LambdaExt.bpl(113,5): anon0 - LambdaExt.bpl(118,5): anon3_Else -LambdaExt.bpl(130,3): Error: This assertion might not hold. + LambdaExt.bpl(114,5): anon0 + LambdaExt.bpl(119,5): anon3_Else +LambdaExt.bpl(131,3): Error: This assertion might not hold. Execution trace: - LambdaExt.bpl(126,5): anon0 -LambdaExt.bpl(137,3): Error: This assertion might not hold. + LambdaExt.bpl(127,5): anon0 +LambdaExt.bpl(138,3): Error: This assertion might not hold. Execution trace: - LambdaExt.bpl(135,5): anon0 -LambdaExt.bpl(139,3): Error: This assertion might not hold. + LambdaExt.bpl(136,5): anon0 +LambdaExt.bpl(140,3): Error: This assertion might not hold. Execution trace: - LambdaExt.bpl(135,5): anon0 -LambdaExt.bpl(141,3): Error: This assertion might not hold. + LambdaExt.bpl(136,5): anon0 +LambdaExt.bpl(142,3): Error: This assertion might not hold. Execution trace: - LambdaExt.bpl(135,5): anon0 -LambdaExt.bpl(152,5): Error: This assertion might not hold. + LambdaExt.bpl(136,5): anon0 +LambdaExt.bpl(153,5): Error: This assertion might not hold. Execution trace: - LambdaExt.bpl(147,5): anon0 - LambdaExt.bpl(152,5): anon3_Else -LambdaExt.bpl(171,3): Error: This assertion might not hold. + LambdaExt.bpl(148,5): anon0 + LambdaExt.bpl(153,5): anon3_Else +LambdaExt.bpl(172,3): Error: This assertion might not hold. Execution trace: - LambdaExt.bpl(171,3): anon0 -LambdaExt.bpl(181,3): Error: This assertion might not hold. + LambdaExt.bpl(172,3): anon0 +LambdaExt.bpl(182,3): Error: This assertion might not hold. Execution trace: - LambdaExt.bpl(181,3): anon0 + LambdaExt.bpl(182,3): anon0 Boogie program verifier finished with 5 verified, 14 errors -LambdaExt.bpl(11,3): Error: This assertion might not hold. +LambdaExt.bpl(12,3): Error: This assertion might not hold. Execution trace: - LambdaExt.bpl(7,7): anon0 -LambdaExt.bpl(27,3): Error: This assertion might not hold. + LambdaExt.bpl(8,7): anon0 +LambdaExt.bpl(28,3): Error: This assertion might not hold. Execution trace: - LambdaExt.bpl(18,5): anon0 -LambdaExt.bpl(31,3): Error: This assertion might not hold. + LambdaExt.bpl(19,5): anon0 +LambdaExt.bpl(32,3): Error: This assertion might not hold. Execution trace: - LambdaExt.bpl(18,5): anon0 -LambdaExt.bpl(35,3): Error: This assertion might not hold. + LambdaExt.bpl(19,5): anon0 +LambdaExt.bpl(36,3): Error: This assertion might not hold. Execution trace: - LambdaExt.bpl(35,3): anon0 -LambdaExt.bpl(82,5): Error: This assertion might not hold. + LambdaExt.bpl(36,3): anon0 +LambdaExt.bpl(83,5): Error: This assertion might not hold. Execution trace: - LambdaExt.bpl(77,5): anon0 - LambdaExt.bpl(82,5): anon3_Else -LambdaExt.bpl(100,5): Error: This assertion might not hold. + LambdaExt.bpl(78,5): anon0 + LambdaExt.bpl(83,5): anon3_Else +LambdaExt.bpl(101,5): Error: This assertion might not hold. Execution trace: - LambdaExt.bpl(95,5): anon0 - LambdaExt.bpl(100,5): anon3_Else -LambdaExt.bpl(116,5): Error: This assertion might not hold. + LambdaExt.bpl(96,5): anon0 + LambdaExt.bpl(101,5): anon3_Else +LambdaExt.bpl(117,5): Error: This assertion might not hold. Execution trace: - LambdaExt.bpl(113,5): anon0 - LambdaExt.bpl(116,5): anon3_Then -LambdaExt.bpl(118,5): Error: This assertion might not hold. + LambdaExt.bpl(114,5): anon0 + LambdaExt.bpl(117,5): anon3_Then +LambdaExt.bpl(119,5): Error: This assertion might not hold. Execution trace: - LambdaExt.bpl(113,5): anon0 - LambdaExt.bpl(118,5): anon3_Else -LambdaExt.bpl(130,3): Error: This assertion might not hold. + LambdaExt.bpl(114,5): anon0 + LambdaExt.bpl(119,5): anon3_Else +LambdaExt.bpl(131,3): Error: This assertion might not hold. Execution trace: - LambdaExt.bpl(126,5): anon0 -LambdaExt.bpl(137,3): Error: This assertion might not hold. + LambdaExt.bpl(127,5): anon0 +LambdaExt.bpl(138,3): Error: This assertion might not hold. Execution trace: - LambdaExt.bpl(135,5): anon0 -LambdaExt.bpl(139,3): Error: This assertion might not hold. + LambdaExt.bpl(136,5): anon0 +LambdaExt.bpl(140,3): Error: This assertion might not hold. Execution trace: - LambdaExt.bpl(135,5): anon0 -LambdaExt.bpl(141,3): Error: This assertion might not hold. + LambdaExt.bpl(136,5): anon0 +LambdaExt.bpl(142,3): Error: This assertion might not hold. Execution trace: - LambdaExt.bpl(135,5): anon0 -LambdaExt.bpl(152,5): Error: This assertion might not hold. + LambdaExt.bpl(136,5): anon0 +LambdaExt.bpl(153,5): Error: This assertion might not hold. Execution trace: - LambdaExt.bpl(147,5): anon0 - LambdaExt.bpl(152,5): anon3_Else -LambdaExt.bpl(171,3): Error: This assertion might not hold. + LambdaExt.bpl(148,5): anon0 + LambdaExt.bpl(153,5): anon3_Else +LambdaExt.bpl(172,3): Error: This assertion might not hold. Execution trace: - LambdaExt.bpl(171,3): anon0 -LambdaExt.bpl(181,3): Error: This assertion might not hold. + LambdaExt.bpl(172,3): anon0 +LambdaExt.bpl(182,3): Error: This assertion might not hold. Execution trace: - LambdaExt.bpl(181,3): anon0 + LambdaExt.bpl(182,3): anon0 Boogie program verifier finished with 5 verified, 15 errors diff --git a/Test/test2/LambdaPoly.bpl b/Test/test2/LambdaPoly.bpl index cc77fe7f2..df9ac9aab 100644 --- a/Test/test2/LambdaPoly.bpl +++ b/Test/test2/LambdaPoly.bpl @@ -1,5 +1,6 @@ // RUN: %parallel-boogie "%s" > "%t" // RUN: %diff "%s.expect" "%t" +// UNSUPPORTED: batch_mode type set a = [a]bool; function union(a:set T, b:set T) : set T; axiom (forall a,b:set T :: union(a,b) == (lambda x:T :: a[x] || b[x])); diff --git a/Test/test2/LambdaPoly.bpl.expect b/Test/test2/LambdaPoly.bpl.expect index 3bb5bcdb9..815870595 100644 --- a/Test/test2/LambdaPoly.bpl.expect +++ b/Test/test2/LambdaPoly.bpl.expect @@ -1,14 +1,14 @@ -LambdaPoly.bpl(30,5): Error: This assertion might not hold. +LambdaPoly.bpl(31,5): Error: This assertion might not hold. Execution trace: - LambdaPoly.bpl(26,5): anon0 - LambdaPoly.bpl(29,5): anon4_Then -LambdaPoly.bpl(33,5): Error: This assertion might not hold. + LambdaPoly.bpl(27,5): anon0 + LambdaPoly.bpl(30,5): anon4_Then +LambdaPoly.bpl(34,5): Error: This assertion might not hold. Execution trace: - LambdaPoly.bpl(26,5): anon0 - LambdaPoly.bpl(32,5): anon5_Then -LambdaPoly.bpl(38,5): Error: This assertion might not hold. + LambdaPoly.bpl(27,5): anon0 + LambdaPoly.bpl(33,5): anon5_Then +LambdaPoly.bpl(39,5): Error: This assertion might not hold. Execution trace: - LambdaPoly.bpl(26,5): anon0 - LambdaPoly.bpl(36,5): anon5_Else + LambdaPoly.bpl(27,5): anon0 + LambdaPoly.bpl(37,5): anon5_Else Boogie program verifier finished with 1 verified, 3 errors diff --git a/Test/test2/NullaryMaps.bpl b/Test/test2/NullaryMaps.bpl index c209d8af9..9786ef4bc 100644 --- a/Test/test2/NullaryMaps.bpl +++ b/Test/test2/NullaryMaps.bpl @@ -1,5 +1,6 @@ // RUN: %parallel-boogie "%s" > "%t" // RUN: %diff "%s.expect" "%t" +// UNSUPPORTED: batch_mode // aren't these cool! var m: []int; diff --git a/Test/test2/NullaryMaps.bpl.expect b/Test/test2/NullaryMaps.bpl.expect index 058ca37af..d0661a4c7 100644 --- a/Test/test2/NullaryMaps.bpl.expect +++ b/Test/test2/NullaryMaps.bpl.expect @@ -1,11 +1,11 @@ -NullaryMaps.bpl(30,3): Error: This assertion might not hold. +NullaryMaps.bpl(31,3): Error: This assertion might not hold. Execution trace: - NullaryMaps.bpl(30,3): anon0 -NullaryMaps.bpl(32,3): Error: This assertion might not hold. + NullaryMaps.bpl(31,3): anon0 +NullaryMaps.bpl(33,3): Error: This assertion might not hold. Execution trace: - NullaryMaps.bpl(30,3): anon0 -NullaryMaps.bpl(38,3): Error: This assertion might not hold. + NullaryMaps.bpl(31,3): anon0 +NullaryMaps.bpl(39,3): Error: This assertion might not hold. Execution trace: - NullaryMaps.bpl(38,3): anon0 + NullaryMaps.bpl(39,3): anon0 Boogie program verifier finished with 2 verified, 3 errors diff --git a/Test/test2/RandomSeed.bpl b/Test/test2/RandomSeed.bpl index e6e7ac72e..f638628de 100644 --- a/Test/test2/RandomSeed.bpl +++ b/Test/test2/RandomSeed.bpl @@ -9,6 +9,11 @@ // CHECK-L: (set-option :smt.random_seed 99) // CHECK-L: (set-option :smt.random_seed 55) // CHECK-L: (set-info :boogie-vc-id WithoutRandomSeed1) + +// Depends on all output going to a single file, so incompatible with +// batch mode. +// UNSUPPORTED: batch_mode + procedure {:random_seed 100} WithRandomSeed0() { } diff --git a/Test/test2/Rlimitouts0.bpl b/Test/test2/Rlimitouts0.bpl index 39b2aaf62..5762b9370 100644 --- a/Test/test2/Rlimitouts0.bpl +++ b/Test/test2/Rlimitouts0.bpl @@ -7,6 +7,10 @@ // CHECK-L: (set-option :rlimit 900000) // CHECK-L: (set-option :timeout 0) // CHECK-L: (set-option :rlimit 1000000) + +// Depends on all output going to a single file, so incompatible with +// batch mode. +// UNSUPPORTED: batch_mode procedure {:timeLimit 4} /* timeLimit overridden by rlimit */ TestTimeouts0(in: [int]int, len: int) returns (out: [int]int) requires in[0] == 0 && (forall i: int :: 0 <= i ==> in[i + 1] == in[i] + 1); requires 0 < len; diff --git a/Test/test2/SelectiveChecking.bpl b/Test/test2/SelectiveChecking.bpl index 1561a5362..231e2f4da 100644 --- a/Test/test2/SelectiveChecking.bpl +++ b/Test/test2/SelectiveChecking.bpl @@ -1,5 +1,6 @@ // RUN: %parallel-boogie "%s" > "%t" // RUN: %diff "%s.expect" "%t" +// UNSUPPORTED: batch_mode procedure {:selective_checking} foo() { var x, y, z : int; diff --git a/Test/test2/SelectiveChecking.bpl.expect b/Test/test2/SelectiveChecking.bpl.expect index d3b64985b..842c4c22d 100644 --- a/Test/test2/SelectiveChecking.bpl.expect +++ b/Test/test2/SelectiveChecking.bpl.expect @@ -1,16 +1,16 @@ -SelectiveChecking.bpl(19,3): Error: This assertion might not hold. +SelectiveChecking.bpl(20,3): Error: This assertion might not hold. Execution trace: - SelectiveChecking.bpl(17,3): anon0 -SelectiveChecking.bpl(32,3): Error: This assertion might not hold. + SelectiveChecking.bpl(18,3): anon0 +SelectiveChecking.bpl(33,3): Error: This assertion might not hold. Execution trace: - SelectiveChecking.bpl(26,3): anon0 - SelectiveChecking.bpl(29,5): anon3_Then - SelectiveChecking.bpl(32,3): anon2 -SelectiveChecking.bpl(39,3): Error: This assertion might not hold. + SelectiveChecking.bpl(27,3): anon0 + SelectiveChecking.bpl(30,5): anon3_Then + SelectiveChecking.bpl(33,3): anon2 +SelectiveChecking.bpl(40,3): Error: This assertion might not hold. Execution trace: - SelectiveChecking.bpl(39,3): anon0 -SelectiveChecking.bpl(41,3): Error: This assertion might not hold. + SelectiveChecking.bpl(40,3): anon0 +SelectiveChecking.bpl(42,3): Error: This assertion might not hold. Execution trace: - SelectiveChecking.bpl(39,3): anon0 + SelectiveChecking.bpl(40,3): anon0 Boogie program verifier finished with 1 verified, 4 errors diff --git a/Test/test2/Timeouts0.bpl b/Test/test2/Timeouts0.bpl index 33ea32f91..7ca86452a 100644 --- a/Test/test2/Timeouts0.bpl +++ b/Test/test2/Timeouts0.bpl @@ -3,6 +3,7 @@ // We use boogie here because parallel-boogie doesn't work well with -proverLog // RUN: %boogie -timeLimit:4 /errorTrace:0 -proverLog:"%t.smt2" "%s" // RUN: %OutputCheck --file-to-check "%t.smt2" "%s" +// UNSUPPORTED: batch_mode // CHECK-L: (set-option :timeout 4000) // CHECK-L: (set-option :timeout 8000) // CHECK-L: (set-option :timeout 2000) diff --git a/Test/test2/Timeouts0.bpl.expect b/Test/test2/Timeouts0.bpl.expect index 22814c49c..e62fe47ad 100644 --- a/Test/test2/Timeouts0.bpl.expect +++ b/Test/test2/Timeouts0.bpl.expect @@ -1,11 +1,11 @@ -Timeouts0.bpl(27,5): Error: A postcondition might not hold on this return path. -Timeouts0.bpl(12,3): Related location: This is the postcondition that might not hold. -Timeouts0.bpl(29,7): Error: This loop invariant might not be maintained by the loop. -Timeouts0.bpl(56,5): Error: A postcondition might not hold on this return path. -Timeouts0.bpl(39,3): Related location: This is the postcondition that might not hold. -Timeouts0.bpl(58,7): Error: This loop invariant might not be maintained by the loop. -Timeouts0.bpl(85,5): Error: A postcondition might not hold on this return path. -Timeouts0.bpl(68,3): Related location: This is the postcondition that might not hold. -Timeouts0.bpl(87,7): Error: This loop invariant might not be maintained by the loop. +Timeouts0.bpl(28,5): Error: A postcondition might not hold on this return path. +Timeouts0.bpl(13,3): Related location: This is the postcondition that might not hold. +Timeouts0.bpl(30,7): Error: This loop invariant might not be maintained by the loop. +Timeouts0.bpl(57,5): Error: A postcondition might not hold on this return path. +Timeouts0.bpl(40,3): Related location: This is the postcondition that might not hold. +Timeouts0.bpl(59,7): Error: This loop invariant might not be maintained by the loop. +Timeouts0.bpl(86,5): Error: A postcondition might not hold on this return path. +Timeouts0.bpl(69,3): Related location: This is the postcondition that might not hold. +Timeouts0.bpl(88,7): Error: This loop invariant might not be maintained by the loop. Boogie program verifier finished with 0 verified, 6 errors diff --git a/Test/test21/InterestingExamples4.bpl b/Test/test21/InterestingExamples4.bpl index d26a2e653..790811468 100644 --- a/Test/test21/InterestingExamples4.bpl +++ b/Test/test21/InterestingExamples4.bpl @@ -2,6 +2,7 @@ // RUN: %diff "%s.p.expect" "%t" // RUN: %parallel-boogie -typeEncoding:a -logPrefix:0a "%s" > "%t" // RUN: %diff "%s.a.expect" "%t" +// UNSUPPORTED: batch_mode // a property that should hold according to the Boogie semantics // (but no automatic theorem prover will be able to prove it) diff --git a/Test/test21/InterestingExamples4.bpl.a.expect b/Test/test21/InterestingExamples4.bpl.a.expect index 0cbb47dc5..ef962850b 100644 --- a/Test/test21/InterestingExamples4.bpl.a.expect +++ b/Test/test21/InterestingExamples4.bpl.a.expect @@ -1,8 +1,8 @@ -InterestingExamples4.bpl(40,3): Error: This assertion might not hold. +InterestingExamples4.bpl(41,3): Error: This assertion might not hold. Execution trace: - InterestingExamples4.bpl(40,3): anon0 -InterestingExamples4.bpl(43,3): Error: This assertion might not hold. + InterestingExamples4.bpl(41,3): anon0 +InterestingExamples4.bpl(44,3): Error: This assertion might not hold. Execution trace: - InterestingExamples4.bpl(40,3): anon0 + InterestingExamples4.bpl(41,3): anon0 Boogie program verifier finished with 0 verified, 2 errors diff --git a/Test/test21/InterestingExamples4.bpl.p.expect b/Test/test21/InterestingExamples4.bpl.p.expect index 97d782fcf..2bfcdd502 100644 --- a/Test/test21/InterestingExamples4.bpl.p.expect +++ b/Test/test21/InterestingExamples4.bpl.p.expect @@ -1,5 +1,5 @@ -InterestingExamples4.bpl(43,3): Error: This assertion might not hold. +InterestingExamples4.bpl(44,3): Error: This assertion might not hold. Execution trace: - InterestingExamples4.bpl(40,3): anon0 + InterestingExamples4.bpl(41,3): anon0 Boogie program verifier finished with 0 verified, 1 error diff --git a/Test/test21/Orderings3.bpl b/Test/test21/Orderings3.bpl index ec342ade3..e7ff8f5c4 100644 --- a/Test/test21/Orderings3.bpl +++ b/Test/test21/Orderings3.bpl @@ -2,6 +2,7 @@ // RUN: %diff "%s.p.expect" "%t" // RUN: %parallel-boogie -typeEncoding:a -logPrefix:0a "%s" > "%t" // RUN: %diff "%s.a.expect" "%t" +// UNSUPPORTED: batch_mode // Example from the Boogie 2 language report diff --git a/Test/test21/Orderings3.bpl.a.expect b/Test/test21/Orderings3.bpl.a.expect index 09a8b065a..55f5afa4d 100644 --- a/Test/test21/Orderings3.bpl.a.expect +++ b/Test/test21/Orderings3.bpl.a.expect @@ -1,11 +1,11 @@ -Orderings3.bpl(33,3): Error: This assertion might not hold. +Orderings3.bpl(34,3): Error: This assertion might not hold. Execution trace: - Orderings3.bpl(19,3): anon0 -Orderings3.bpl(38,3): Error: This assertion might not hold. + Orderings3.bpl(20,3): anon0 +Orderings3.bpl(39,3): Error: This assertion might not hold. Execution trace: - Orderings3.bpl(38,3): anon0 -Orderings3.bpl(40,3): Error: This assertion might not hold. + Orderings3.bpl(39,3): anon0 +Orderings3.bpl(41,3): Error: This assertion might not hold. Execution trace: - Orderings3.bpl(38,3): anon0 + Orderings3.bpl(39,3): anon0 Boogie program verifier finished with 0 verified, 3 errors diff --git a/Test/test21/Orderings3.bpl.p.expect b/Test/test21/Orderings3.bpl.p.expect index 09a8b065a..55f5afa4d 100644 --- a/Test/test21/Orderings3.bpl.p.expect +++ b/Test/test21/Orderings3.bpl.p.expect @@ -1,11 +1,11 @@ -Orderings3.bpl(33,3): Error: This assertion might not hold. +Orderings3.bpl(34,3): Error: This assertion might not hold. Execution trace: - Orderings3.bpl(19,3): anon0 -Orderings3.bpl(38,3): Error: This assertion might not hold. + Orderings3.bpl(20,3): anon0 +Orderings3.bpl(39,3): Error: This assertion might not hold. Execution trace: - Orderings3.bpl(38,3): anon0 -Orderings3.bpl(40,3): Error: This assertion might not hold. + Orderings3.bpl(39,3): anon0 +Orderings3.bpl(41,3): Error: This assertion might not hold. Execution trace: - Orderings3.bpl(38,3): anon0 + Orderings3.bpl(39,3): anon0 Boogie program verifier finished with 0 verified, 3 errors diff --git a/Test/test21/ParallelAssignment.bpl b/Test/test21/ParallelAssignment.bpl index fb4ddf6d0..1de0fb7e0 100644 --- a/Test/test21/ParallelAssignment.bpl +++ b/Test/test21/ParallelAssignment.bpl @@ -2,6 +2,7 @@ // RUN: %diff "%s.p.expect" "%t" // RUN: %parallel-boogie -typeEncoding:a -logPrefix:0a "%s" > "%t" // RUN: %diff "%s.a.expect" "%t" +// UNSUPPORTED: batch_mode // Examples from the Boogie2 language report type C, D; diff --git a/Test/test21/ParallelAssignment.bpl.a.expect b/Test/test21/ParallelAssignment.bpl.a.expect index 3c370602a..e83890787 100644 --- a/Test/test21/ParallelAssignment.bpl.a.expect +++ b/Test/test21/ParallelAssignment.bpl.a.expect @@ -1,11 +1,11 @@ -ParallelAssignment.bpl(30,3): Error: This assertion might not hold. +ParallelAssignment.bpl(31,3): Error: This assertion might not hold. Execution trace: - ParallelAssignment.bpl(19,5): anon0 -ParallelAssignment.bpl(39,3): Error: This assertion might not hold. + ParallelAssignment.bpl(20,5): anon0 +ParallelAssignment.bpl(40,3): Error: This assertion might not hold. Execution trace: - ParallelAssignment.bpl(19,5): anon0 -ParallelAssignment.bpl(58,3): Error: This assertion might not hold. + ParallelAssignment.bpl(20,5): anon0 +ParallelAssignment.bpl(59,3): Error: This assertion might not hold. Execution trace: - ParallelAssignment.bpl(44,11): anon0 + ParallelAssignment.bpl(45,11): anon0 Boogie program verifier finished with 0 verified, 3 errors diff --git a/Test/test21/ParallelAssignment.bpl.p.expect b/Test/test21/ParallelAssignment.bpl.p.expect index 3c370602a..e83890787 100644 --- a/Test/test21/ParallelAssignment.bpl.p.expect +++ b/Test/test21/ParallelAssignment.bpl.p.expect @@ -1,11 +1,11 @@ -ParallelAssignment.bpl(30,3): Error: This assertion might not hold. +ParallelAssignment.bpl(31,3): Error: This assertion might not hold. Execution trace: - ParallelAssignment.bpl(19,5): anon0 -ParallelAssignment.bpl(39,3): Error: This assertion might not hold. + ParallelAssignment.bpl(20,5): anon0 +ParallelAssignment.bpl(40,3): Error: This assertion might not hold. Execution trace: - ParallelAssignment.bpl(19,5): anon0 -ParallelAssignment.bpl(58,3): Error: This assertion might not hold. + ParallelAssignment.bpl(20,5): anon0 +ParallelAssignment.bpl(59,3): Error: This assertion might not hold. Execution trace: - ParallelAssignment.bpl(44,11): anon0 + ParallelAssignment.bpl(45,11): anon0 Boogie program verifier finished with 0 verified, 3 errors diff --git a/Test/test21/Triggers0.bpl b/Test/test21/Triggers0.bpl index f9e9e977a..23f66b127 100644 --- a/Test/test21/Triggers0.bpl +++ b/Test/test21/Triggers0.bpl @@ -2,6 +2,7 @@ // RUN: %diff "%s.p.expect" "%t" // RUN: %parallel-boogie -typeEncoding:a -logPrefix:0a "%s" > "%t" // RUN: %diff "%s.a.expect" "%t" +// UNSUPPORTED: batch_mode const ar : [int]bool; diff --git a/Test/test21/Triggers0.bpl.a.expect b/Test/test21/Triggers0.bpl.a.expect index d327f946b..8574b1397 100644 --- a/Test/test21/Triggers0.bpl.a.expect +++ b/Test/test21/Triggers0.bpl.a.expect @@ -1,8 +1,8 @@ -Triggers0.bpl(43,3): Error: This assertion might not hold. +Triggers0.bpl(44,3): Error: This assertion might not hold. Execution trace: - Triggers0.bpl(43,3): anon0 -Triggers0.bpl(47,3): Error: This assertion might not hold. + Triggers0.bpl(44,3): anon0 +Triggers0.bpl(48,3): Error: This assertion might not hold. Execution trace: - Triggers0.bpl(43,3): anon0 + Triggers0.bpl(44,3): anon0 Boogie program verifier finished with 1 verified, 2 errors diff --git a/Test/test21/Triggers0.bpl.p.expect b/Test/test21/Triggers0.bpl.p.expect index d327f946b..8574b1397 100644 --- a/Test/test21/Triggers0.bpl.p.expect +++ b/Test/test21/Triggers0.bpl.p.expect @@ -1,8 +1,8 @@ -Triggers0.bpl(43,3): Error: This assertion might not hold. +Triggers0.bpl(44,3): Error: This assertion might not hold. Execution trace: - Triggers0.bpl(43,3): anon0 -Triggers0.bpl(47,3): Error: This assertion might not hold. + Triggers0.bpl(44,3): anon0 +Triggers0.bpl(48,3): Error: This assertion might not hold. Execution trace: - Triggers0.bpl(43,3): anon0 + Triggers0.bpl(44,3): anon0 Boogie program verifier finished with 1 verified, 2 errors diff --git a/Test/unnecessaryassumes/unnecessaryassumes1.bpl b/Test/unnecessaryassumes/unnecessaryassumes1.bpl index ffb96e8ac..cb9f18bb4 100644 --- a/Test/unnecessaryassumes/unnecessaryassumes1.bpl +++ b/Test/unnecessaryassumes/unnecessaryassumes1.bpl @@ -1,6 +1,7 @@ // We use boogie instead of parallel-boogie here to fix the order of the output from /printNecessaryAssumes // RUN: %boogie /printNecessaryAssumes "%s" > "%t" // RUN: %diff "%s.expect" "%t" +// UNSUPPORTED: batch_mode procedure test0(n: int) { From e3ca84cbaa20ef82087096ccde2cc56b88879d41 Mon Sep 17 00:00:00 2001 From: Aaron Tomb Date: Tue, 8 Mar 2022 09:48:46 -0800 Subject: [PATCH 08/32] Include split locations in XML output (#480) This includes a location in addition to an outcome for each split entry in the XML result report. --- Source/Core/Xml.cs | 23 +++++++++++++++++---- Source/ExecutionEngine/ExecutionEngine.cs | 6 +++--- Source/VCGeneration/Split.cs | 14 ++++++++----- Source/VCGeneration/SplitAndVerifyWorker.cs | 2 +- Test/commandline/xml.bpl | 16 +++++++------- 5 files changed, 41 insertions(+), 20 deletions(-) diff --git a/Source/Core/Xml.cs b/Source/Core/Xml.cs index 1afe577a6..2a8909554 100644 --- a/Source/Core/Xml.cs +++ b/Source/Core/Xml.cs @@ -115,7 +115,8 @@ public void WriteEndMethod(string outcome, DateTime endTime, TimeSpan elapsed, i cce.EndExpose(); } - public void WriteSplit(int splitNum, DateTime startTime, string outcome, TimeSpan elapsed) + public void WriteSplit(int splitNum, IEnumerable asserts, DateTime startTime, + string outcome, TimeSpan elapsed, int? resourceCount) { Contract.Requires(splitNum > 0); Contract.Requires(outcome != null); @@ -126,16 +127,30 @@ public void WriteSplit(int splitNum, DateTime startTime, string outcome, TimeSpa cce.BeginExpose(this); { - wr.WriteStartElement("split"); + wr.WriteStartElement("assertionBatch"); wr.WriteAttributeString("number", splitNum.ToString()); wr.WriteAttributeString("startTime", startTime.ToString(DateTimeFormatString)); + foreach(var assert in asserts) + { + var token = assert.tok; + wr.WriteStartElement("assertion"); + wr.WriteAttributeString("file", token.filename); + wr.WriteAttributeString("line", token.line.ToString()); + wr.WriteAttributeString("column", token.col.ToString()); + wr.WriteEndElement(); // assertion + } + wr.WriteStartElement("conclusion"); wr.WriteAttributeString("duration", elapsed.TotalSeconds.ToString()); wr.WriteAttributeString("outcome", outcome); - wr.WriteEndElement(); // outcome + if (resourceCount is not null) + { + wr.WriteAttributeString("resourceCount", resourceCount.ToString()); + } + wr.WriteEndElement(); // conclusion - wr.WriteEndElement(); // split + wr.WriteEndElement(); // assertionBatch } cce.EndExpose(); } diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index f10a9fdec..2a2fcab21 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -1132,8 +1132,8 @@ private void VerifyImplementation(Program program, PipelineStatistics stats, Err Options.XmlSink.WriteStartMethod(impl.Name, verificationResult.Start); foreach (var vcResult in verificationResult.VCResults.OrderBy(s => s.vcNum)) { - Options.XmlSink.WriteSplit(vcResult.vcNum, vcResult.startTime, - vcResult.outcome.ToString().ToLowerInvariant(), vcResult.runTime); + Options.XmlSink.WriteSplit(vcResult.vcNum, vcResult.asserts, vcResult.startTime, + vcResult.outcome.ToString().ToLowerInvariant(), vcResult.runTime, vcResult.resourceCount); } Options.XmlSink.WriteEndMethod(verificationResult.Outcome.ToString().ToLowerInvariant(), @@ -1798,4 +1798,4 @@ public void Dispose() checkerPool.Dispose(); } } -} \ No newline at end of file +} diff --git a/Source/VCGeneration/Split.cs b/Source/VCGeneration/Split.cs index 0084b0cc9..466de8250 100644 --- a/Source/VCGeneration/Split.cs +++ b/Source/VCGeneration/Split.cs @@ -20,7 +20,9 @@ public record VCResult int vcNum, DateTime startTime, ProverInterface.Outcome outcome, - TimeSpan runTime + TimeSpan runTime, + IEnumerable asserts, + int resourceCount ); public class Split @@ -81,6 +83,7 @@ void ObjectInvariant() private readonly List blocks; + public IEnumerable Asserts => blocks.SelectMany(block => block.cmds.OfType()); public readonly IReadOnlyList TopLevelDeclarations; readonly List bigBlocks = new(); @@ -1301,7 +1304,10 @@ public void ReadOutcome(VerifierCallback callback, ref ConditionGeneration.Outco checker.ProverRunTime.TotalSeconds, outcome); } - var result = new VCResult(splitNum + 1, checker.ProverStart, outcome, checker.ProverRunTime); + var resourceCount = checker.GetProverResourceCount().Result; + totalResourceCount += resourceCount; + + var result = new VCResult(splitNum + 1, checker.ProverStart, outcome, checker.ProverRunTime, Asserts, resourceCount); callback.OnVCResult(result); if (options.VcsDumpSplits) @@ -1309,8 +1315,6 @@ public void ReadOutcome(VerifierCallback callback, ref ConditionGeneration.Outco DumpDot(splitNum); } - totalResourceCount += checker.GetProverResourceCount().Result; - proverFailed = false; switch (outcome) @@ -1495,4 +1499,4 @@ public void ReleaseChecker() checker = null; } } -} \ No newline at end of file +} diff --git a/Source/VCGeneration/SplitAndVerifyWorker.cs b/Source/VCGeneration/SplitAndVerifyWorker.cs index 32d8ffd84..8da3aea39 100644 --- a/Source/VCGeneration/SplitAndVerifyWorker.cs +++ b/Source/VCGeneration/SplitAndVerifyWorker.cs @@ -208,4 +208,4 @@ private async Task HandleProverFailure(Split split, CancellationToken cancellati } } } -} \ No newline at end of file +} diff --git a/Test/commandline/xml.bpl b/Test/commandline/xml.bpl index 1f56bf028..fdec06762 100644 --- a/Test/commandline/xml.bpl +++ b/Test/commandline/xml.bpl @@ -9,13 +9,15 @@ // We only check for one of the methods in the XML because there's no // guarantee about what order they'll appear in. // CHECK: \ -// CHECK: \ -// CHECK: \ -// CHECK: \ -// CHECK: \ -// CHECK: \ -// CHECK: \ -// CHECK: \ +// CHECK: \ +// CHECK: \ +// CHECK: \ +// CHECK: \ +// CHECK: \ +// CHECK: \ +// CHECK: \ +// CHECK: \ +// CHECK: \ // CHECK: \ procedure ExampleWithSplits() From c4fc91a145595a6622d8cedb14494c6dc9d8ef00 Mon Sep 17 00:00:00 2001 From: Aaron Tomb Date: Tue, 8 Mar 2022 12:32:04 -0800 Subject: [PATCH 09/32] Fix compatibility issues with solvers other than Z3 (#521) Ensures that Boogie's solver interface, including batch mode, work smoothly with all supported solvers. --- .github/workflows/test.yml | 5 +++ .../SMTLib/SMTLibBatchTheoremProver.cs | 38 ++++++++++--------- .../SMTLib/SMTLibInteractiveTheoremProver.cs | 10 ++++- Source/Provers/SMTLib/SMTLibProcess.cs | 6 +++ Test/prover/cvc5.bpl | 8 ++++ 5 files changed, 47 insertions(+), 20 deletions(-) create mode 100644 Test/prover/cvc5.bpl diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e51e4e1c1..1c2ed7704 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,6 +12,7 @@ on: env: SOLUTION: Source/Boogie.sln Z3URL: https://github.com/Z3Prover/z3/releases/download/z3-4.8.8/z3-4.8.8-x64-ubuntu-16.04.zip + CVC5URL: https://github.com/cvc5/cvc5/releases/latest/download/cvc5-Linux jobs: job0: @@ -36,6 +37,10 @@ jobs: wget ${Z3URL} unzip z3*.zip export PATH="$(find $PWD/z3* -name bin -type d):$PATH" + # Download a CVC5 release + mkdir -p bin + (cd bin && wget ${CVC5URL} && mv cvc5-Linux cvc5 && chmod +x cvc5) + export PATH="$PWD/bin:$PATH" # Install python tools sudo pip3 install setuptools sudo pip3 install lit OutputCheck pyyaml psutil diff --git a/Source/Provers/SMTLib/SMTLibBatchTheoremProver.cs b/Source/Provers/SMTLib/SMTLibBatchTheoremProver.cs index 0695e065a..d76976ffc 100644 --- a/Source/Provers/SMTLib/SMTLibBatchTheoremProver.cs +++ b/Source/Provers/SMTLib/SMTLibBatchTheoremProver.cs @@ -15,9 +15,10 @@ namespace Microsoft.Boogie.SMTLib /// /// Some SMT-Lib provers don't support the interactive (a.k.a. /// incremental) mode provided by SMTLibInteractiveTheoremProver. This - /// class allows Boogie to work with such provers. It's known to work - /// with Z3, at least. To work correctly in batch mode, a solver must - /// be able to handle the following commands without crashing: + /// class allows Boogie to work with such provers, and also works with + /// provers that support interactive modes (including CVC5, Yices2, and + /// Z3). To work correctly in batch mode, a solver must be able to + /// handle the following commands without crashing: /// /// * `(get-model)` after returning `unsat` /// * `(get-info :reason-unknown)` after returning `sat` or `unsat` @@ -92,18 +93,15 @@ public override void Reset(VCExpressionGenerator generator) public override void FullReset(VCExpressionGenerator generator) { - if (options.Solver == SolverKind.Z3 || options.Solver == SolverKind.NoOpWithZ3Options) - { - this.gen = generator; - common.Clear(); - SetupAxiomBuilder(gen); - Axioms.Clear(); - TypeDecls.Clear(); - AxiomsAreSetup = false; - DeclCollector.Reset(); - NamedAssumes.Clear(); - UsedNamedAssumes = null; - } + this.gen = generator; + common.Clear(); + SetupAxiomBuilder(gen); + Axioms.Clear(); + TypeDecls.Clear(); + AxiomsAreSetup = false; + DeclCollector.Reset(); + NamedAssumes.Clear(); + UsedNamedAssumes = null; } // TODO: move to base? @@ -164,8 +162,10 @@ private async Task GetResponse(CancellationToken cancellationToken) result = ParseReasonUnknown(unknownSExp, result); } - var rlimitSExp = await Process.GetProverResponse().WaitAsync(cancellationToken); - resourceCount = ParseRCount(rlimitSExp); + if (options.Solver == SolverKind.Z3) { + var rlimitSExp = await Process.GetProverResponse().WaitAsync(cancellationToken); + resourceCount = ParseRCount(rlimitSExp); + } var modelSExp = await Process.GetProverResponse().WaitAsync(cancellationToken); errorModel = ParseErrorModel(modelSExp); @@ -228,7 +228,9 @@ private void SendCheckSat() UsedNamedAssumes = null; SendThisVC("(check-sat)"); SendThisVC("(get-info :reason-unknown)"); - SendThisVC("(get-info :rlimit)"); + if (options.Solver == SolverKind.Z3) { + SendThisVC($"(get-info :{Z3.RlimitOption})"); + } SendThisVC("(get-model)"); CheckSatSent = true; Process.IndicateEndOfInput(); diff --git a/Source/Provers/SMTLib/SMTLibInteractiveTheoremProver.cs b/Source/Provers/SMTLib/SMTLibInteractiveTheoremProver.cs index 8d8fd375b..739ee492d 100644 --- a/Source/Provers/SMTLib/SMTLibInteractiveTheoremProver.cs +++ b/Source/Provers/SMTLib/SMTLibInteractiveTheoremProver.cs @@ -117,7 +117,6 @@ public override void Reset(VCExpressionGenerator generator) this.gen = generator; SendThisVC("(reset)"); RecoverIfProverCrashedAfterReset(); - SendThisVC("(set-option :" + Z3.RlimitOption + " 0)"); if (0 < common.Length) { @@ -713,7 +712,14 @@ protected override void Send(string s, bool isCommon) public override async Task GetRCount() { - SendThisVC("(get-info :rlimit)"); + if (options.Solver != SolverKind.Z3) { + // Only Z3 currently supports retrieving this value. CVC5 + // supports setting a limit, but does not appear to support + // reporting how much it took to complete a query. + return 0; + } + + SendThisVC($"(get-info :{Z3.RlimitOption})"); return ParseRCount(await Process.GetProverResponse()); } diff --git a/Source/Provers/SMTLib/SMTLibProcess.cs b/Source/Provers/SMTLib/SMTLibProcess.cs index 7fb2dd7ee..d0bebf00e 100644 --- a/Source/Provers/SMTLib/SMTLibProcess.cs +++ b/Source/Provers/SMTLib/SMTLibProcess.cs @@ -156,6 +156,12 @@ public override async Task GetProverResponse() return resp; } else if (resp.Arguments[0].Name.Contains("model is not available")) { return null; + } else if (resp.Arguments[0].Name.Contains("context is unsatisfiable")) { + return null; + } else if (resp.Arguments[0].Name.Contains("Cannot get model")) { + return null; + } else if (resp.Arguments[0].Name.Contains("last result wasn't unknown")) { + return null; } else { HandleError(resp.Arguments[0].Name); return null; diff --git a/Test/prover/cvc5.bpl b/Test/prover/cvc5.bpl new file mode 100644 index 000000000..d1982b814 --- /dev/null +++ b/Test/prover/cvc5.bpl @@ -0,0 +1,8 @@ +// RUN: %boogie /trace /proverOpt:SOLVER=cvc5 "%s" > "%t" +// RUN: %OutputCheck --file-to-check "%t" "%s" +// CHECK: \[TRACE\] Using prover:.*cvc5 +// CHECK: Boogie program verifier finished with 1 verified, 0 errors + +procedure P(x: int, y: int) { + assert x*y == y*x; +} From 71d6ca56a677968b6a9948b3353118f581316acb Mon Sep 17 00:00:00 2001 From: Robin Salkeld Date: Tue, 8 Mar 2022 13:57:43 -0800 Subject: [PATCH 10/32] Bump version to 2.13.0 (#525) --- Source/Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Directory.Build.props b/Source/Directory.Build.props index 29446c953..9e92ec889 100644 --- a/Source/Directory.Build.props +++ b/Source/Directory.Build.props @@ -2,7 +2,7 @@ - 2.12.1 + 2.13.0 net6.0 false Boogie From 980d6a744eb45d74d542c113b204eefeeae6c54d Mon Sep 17 00:00:00 2001 From: Shaz Qadeer Date: Wed, 9 Mar 2022 04:15:45 -0800 Subject: [PATCH 11/32] Visiting attributes in StandardVisitor (#523) * added code to StandardVisitor so that it visits attributes for all commands and expressions consequently, inlining and monomorphization will get simpler additionally, any pass that depends on inlining will also not have to worry about attribute processing * added comment * removed whitespace --- Source/Core/Inline.cs | 8 +--- Source/Core/Monomorphization.cs | 69 --------------------------------- Source/Core/StandardVisitor.cs | 29 ++++++++++++-- 3 files changed, 26 insertions(+), 80 deletions(-) diff --git a/Source/Core/Inline.cs b/Source/Core/Inline.cs index 59453f7eb..e18bc47f5 100644 --- a/Source/Core/Inline.cs +++ b/Source/Core/Inline.cs @@ -888,13 +888,7 @@ public Cmd CopyCmd(Cmd cmd) { return cmd; } - var newCmd = BoundVarAndReplacingOldSubstituter.Apply(substMap, oldSubstMap, prefix, cmd); - if (cmd is ICarriesAttributes attrCmd && attrCmd.Attributes != null) - { - var attrCopy = (QKeyValue) attrCmd.Attributes.Clone(); - ((ICarriesAttributes) newCmd).Attributes = Substituter.ApplyReplacingOldExprs(PartialSubst, PartialOldSubst, attrCopy); - } - return newCmd; + return BoundVarAndReplacingOldSubstituter.Apply(substMap, oldSubstMap, prefix, cmd); } public Expr CopyExpr(Expr expr) diff --git a/Source/Core/Monomorphization.cs b/Source/Core/Monomorphization.cs index 1d22c5543..f3ac9109d 100644 --- a/Source/Core/Monomorphization.cs +++ b/Source/Core/Monomorphization.cs @@ -808,36 +808,6 @@ public override Expr VisitLetExpr(LetExpr node) return expr; } - - public override Cmd VisitAssumeCmd(AssumeCmd node) - { - var returnCmd = (AssumeCmd) base.VisitAssumeCmd(node); - if (node.Attributes != null) - { - returnCmd.Attributes = VisitQKeyValue(node.Attributes); - } - return returnCmd; - } - - public override Cmd VisitAssertCmd(AssertCmd node) - { - var returnCmd = (AssertCmd) base.VisitAssertCmd(node); - if (node.Attributes != null) - { - returnCmd.Attributes = VisitQKeyValue(node.Attributes); - } - return returnCmd; - } - - public override Cmd VisitAssignCmd(AssignCmd node) - { - var returnCmd = (AssignCmd) base.VisitAssignCmd(node); - if (node.Attributes != null) - { - returnCmd.Attributes = VisitQKeyValue(node.Attributes); - } - return returnCmd; - } } public static MonomorphizationVisitor Initialize(CoreOptions options, Program program, @@ -976,36 +946,6 @@ public override Cmd VisitCallCmd(CallCmd node) } } - public override Cmd VisitAssumeCmd(AssumeCmd node) - { - var returnCmd = (AssumeCmd) base.VisitAssumeCmd(node); - if (node.Attributes != null) - { - returnCmd.Attributes = VisitQKeyValue(node.Attributes); - } - return returnCmd; - } - - public override Cmd VisitAssertCmd(AssertCmd node) - { - var returnCmd = (AssertCmd) base.VisitAssertCmd(node); - if (node.Attributes != null) - { - returnCmd.Attributes = VisitQKeyValue(node.Attributes); - } - return returnCmd; - } - - public override Cmd VisitAssignCmd(AssignCmd node) - { - var returnCmd = (AssignCmd) base.VisitAssignCmd(node); - if (node.Attributes != null) - { - returnCmd.Attributes = VisitQKeyValue(node.Attributes); - } - return returnCmd; - } - public override CtorType VisitCtorType(CtorType node) { return (CtorType) monomorphizationDuplicator.VisitType(node); @@ -1056,15 +996,6 @@ private void VisitConstructor(DatatypeConstructor constructor) constructor.selectors.Iter(selector => base.VisitFunction(selector)); } - public override Absy Visit(Absy node) - { - if (node is ICarriesAttributes attrNode && attrNode.Attributes != null) - { - VisitQKeyValue(attrNode.Attributes); - } - return base.Visit(node); - } - // this function may be called directly by monomorphizationDuplicator // if a non-generic function call is discovered in an expression public override Function VisitFunction(Function node) diff --git a/Source/Core/StandardVisitor.cs b/Source/Core/StandardVisitor.cs index 58cbffe44..a3312c5eb 100644 --- a/Source/Core/StandardVisitor.cs +++ b/Source/Core/StandardVisitor.cs @@ -78,6 +78,7 @@ public virtual Cmd VisitAssertCmd(AssertCmd node) Contract.Requires(node != null); Contract.Ensures(Contract.Result() != null); node.Expr = this.VisitExpr(node.Expr); + VisitAttributes(node); return node; } @@ -90,7 +91,7 @@ public virtual Cmd VisitAssignCmd(AssignCmd node) node.SetLhs(i, cce.NonNull((AssignLhs) this.Visit(node.Lhss[i]))); node.SetRhs(i, cce.NonNull((Expr /*!*/) this.VisitExpr(node.Rhss[i]))); } - + VisitAttributes(node); return node; } @@ -99,6 +100,7 @@ public virtual Cmd VisitAssumeCmd(AssumeCmd node) Contract.Requires(node != null); Contract.Ensures(Contract.Result() != null); node.Expr = this.VisitExpr(node.Expr); + VisitAttributes(node); return node; } @@ -234,7 +236,7 @@ public virtual Cmd VisitCallCmd(CallCmd node) node.Outs[i] = (IdentifierExpr) this.VisitIdentifierExpr(cce.NonNull(node.Outs[i])); } } - + VisitAttributes(node); return node; } @@ -249,7 +251,7 @@ public virtual Cmd VisitParCallCmd(ParCallCmd node) node.CallCmds[i] = (CallCmd) this.VisitCallCmd(node.CallCmds[i]); } } - + VisitAttributes(node); return node; } @@ -433,6 +435,7 @@ public virtual Expr VisitLetExpr(LetExpr node) node.Rhss = this.VisitExprSeq(node.Rhss); node.Dummies = this.VisitVariableSeq(node.Dummies); node.Body = this.VisitExpr(node.Body); + VisitAttributes(node); return node; } @@ -457,7 +460,6 @@ public virtual Function VisitFunction(Function node) { node.DefinitionBody = (NAryExpr) this.VisitExpr(node.DefinitionBody); } - return node; } @@ -651,6 +653,7 @@ public virtual BinderExpr VisitBinderExpr(BinderExpr node) node.Body = this.VisitExpr(node.Body); node.Dummies = this.VisitVariableSeq(node.Dummies); //node.Type = this.VisitType(node.Type); + VisitAttributes(node); return node; } @@ -864,6 +867,7 @@ public virtual Cmd VisitAssertEnsuresCmd(AssertEnsuresCmd node) Contract.Ensures(Contract.Result() != null); node.Ensures = this.VisitEnsures(node.Ensures); node.Expr = this.VisitExpr(node.Expr); + VisitAttributes(node); return node; } @@ -873,8 +877,25 @@ public virtual Cmd VisitAssertRequiresCmd(AssertRequiresCmd node) Contract.Ensures(Contract.Result() != null); node.Requires = this.VisitRequires(node.Requires); node.Expr = this.VisitExpr(node.Expr); + VisitAttributes(node); return node; } + + /* + * VisitAttributes is being called in the visitor of those subtypes of Cmd + * and Expr that implement ICarriesAttributes. This behavior is introduced so + * that hints for pool-based quantifier instantiation present in attributes + * are processed naturally during monomorphization and inlining. There + * are other subtypes of Absy that implement ICarriesAttributes; if + * necessary, this method could be in the visitor for those types also. + */ + private void VisitAttributes(ICarriesAttributes node) + { + if (node.Attributes != null) + { + node.Attributes = VisitQKeyValue(node.Attributes); + } + } } /// From c4550aea3b56f83358ddd807ddcfd1f50a643036 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Wed, 9 Mar 2022 19:21:10 +0100 Subject: [PATCH 12/32] Refactoring for return verification tasks (#527) * Move VerificationResult and CommandLineParseState to separate files * Extract methods in InferAndVerify * Remove region directive --- Source/ExecutionEngine/CommandLineOptions.cs | 376 +---------------- .../ExecutionEngine/CommandLineParseState.cs | 378 ++++++++++++++++++ Source/ExecutionEngine/ExecutionEngine.cs | 160 +++----- Source/ExecutionEngine/VerificationResult.cs | 47 +++ 4 files changed, 481 insertions(+), 480 deletions(-) create mode 100644 Source/ExecutionEngine/CommandLineParseState.cs create mode 100644 Source/ExecutionEngine/VerificationResult.cs diff --git a/Source/ExecutionEngine/CommandLineOptions.cs b/Source/ExecutionEngine/CommandLineOptions.cs index 1c47208bd..e3c05aaab 100644 --- a/Source/ExecutionEngine/CommandLineOptions.cs +++ b/Source/ExecutionEngine/CommandLineOptions.cs @@ -139,380 +139,6 @@ protected virtual bool ParseOption(string name, CommandLineParseState ps) return false; // unrecognized option } - protected class CommandLineParseState - { - public string s; - public bool hasColonArgument; - - public readonly string[] /*!*/ - args; - - public int i; - public int nextIndex; - public bool EncounteredErrors; - public readonly string ToolName; - - [ContractInvariantMethod] - void ObjectInvariant() - { - Contract.Invariant(args != null); - Contract.Invariant(0 <= i && i <= args.Length); - Contract.Invariant(0 <= nextIndex && nextIndex <= args.Length); - } - - - public CommandLineParseState(string[] args, string toolName) - { - Contract.Requires(args != null); - Contract.Requires(Contract.ForAll(0, args.Length, i => args[i] != null)); - Contract.Requires(toolName != null); - Contract.Ensures(this.args == args); - this.ToolName = toolName; - this.s = null; // set later by client - this.hasColonArgument = false; // set later by client - this.args = args; - this.i = 0; - this.nextIndex = 0; // set later by client - this.EncounteredErrors = false; - } - - public bool CheckBooleanFlag(string flagName, Action setFlag, bool valueWhenPresent = true) - { - Contract.Requires(flagName != null); - //modifies nextIndex, encounteredErrors, Console.Error.*; - bool flagPresent = false; - - if ((s == "/" + flagName || s == "-" + flagName) && ConfirmArgumentCount(0)) - { - setFlag(valueWhenPresent); - flagPresent = true; - } - - return flagPresent; - } - - public bool CheckBooleanFlag(string flagName, ref bool flag, bool valueWhenPresent) - { - Contract.Requires(flagName != null); - //modifies nextIndex, encounteredErrors, Console.Error.*; - bool flagPresent = false; - - if ((s == "/" + flagName || s == "-" + flagName) && ConfirmArgumentCount(0)) - { - flag = valueWhenPresent; - flagPresent = true; - } - - return flagPresent; - } - - public bool CheckBooleanFlag(string flagName, ref bool flag) - { - Contract.Requires(flagName != null); - //modifies nextIndex, encounteredErrors, Console.Error.*; - return CheckBooleanFlag(flagName, ref flag, true); - } - - /// - /// If there is one argument and it is a non-negative integer, - /// then call "setArg" with that number mapped to a boolean and return "true". - /// Otherwise, emit error message, leave "arg" unchanged, and return "false". - /// - public bool GetNumericArgument(Action setArg) - { - int intArg = 0; - var result = GetNumericArgument(ref intArg, x => x < 2); - if (result) { - setArg(intArg != 0); - } - return result; - } - - /// - /// If there is one argument and it is a non-negative integer, then set "arg" to that number and return "true". - /// Otherwise, emit error message, leave "arg" unchanged, and return "false". - /// - public bool GetNumericArgument(ref bool arg) - { - int intArg = 0; - var result = GetNumericArgument(ref intArg, x => x < 2); - if (result) { - arg = intArg != 0; - } - return result; - } - - /// - /// If there is one argument and it is a non-negative integer, then call "setArg" with that number and return "true". - /// Otherwise, emit error message, leave "arg" unchanged, and return "false". - /// - public bool GetNumericArgument(Action setArg, Predicate filter = null) - { - filter ??= a => 0 <= a; - - Contract.Requires(filter != null); - - if (this.ConfirmArgumentCount(1)) - { - try - { - Contract.Assume(args[i] != null); - Contract.Assert(args[i] is string); // needed to prove args[i].IsPeerConsistent - int d = Convert.ToInt32(this.args[this.i]); - if (filter == null || filter(d)) - { - setArg(d); - return true; - } - } - catch (FormatException) - { - } - catch (OverflowException) - { - } - } - else - { - return false; - } - - Error("Invalid argument \"{0}\" to option {1}", args[this.i], this.s); - return false; - } - - /// - /// If there is one argument and it is a non-negative integer, then set "arg" to that number and return "true". - /// Otherwise, emit error message, leave "arg" unchanged, and return "false". - /// - public bool GetNumericArgument(ref int arg) - { - //modifies nextIndex, encounteredErrors, Console.Error.*; - return GetNumericArgument(ref arg, a => 0 <= a); - } - - public bool GetUnsignedNumericArgument(Action setArg, Predicate filter = null) - { - if (ConfirmArgumentCount(1)) - { - try - { - Contract.Assume(args[i] != null); - Contract.Assert(args[i] is string); // needed to prove args[i].IsPeerConsistent - uint d = Convert.ToUInt32(this.args[this.i]); - if (filter == null || filter(d)) - { - setArg(d); - return true; - } - } - catch (FormatException) - { - } - catch (OverflowException) - { - } - } - else - { - return false; - } - - Error("Invalid argument \"{0}\" to option {1}", args[this.i], this.s); - return false; - } - - public bool GetUnsignedNumericArgument(ref uint arg, Predicate filter) - { - if (this.ConfirmArgumentCount(1)) - { - try - { - Contract.Assume(args[i] != null); - Contract.Assert(args[i] is string); // needed to prove args[i].IsPeerConsistent - uint d = Convert.ToUInt32(this.args[this.i]); - if (filter == null || filter(d)) - { - arg = d; - return true; - } - } - catch (System.FormatException) - { - } - catch (System.OverflowException) - { - } - } - else - { - return false; - } - - Error("Invalid argument \"{0}\" to option {1}", args[this.i], this.s); - return false; - } - - /// - /// If there is one argument and the filtering predicate holds, then set "arg" to that number and return "true". - /// Otherwise, emit error message, leave "arg" unchanged, and return "false". - /// - public bool GetNumericArgument(ref int arg, Predicate filter) - { - Contract.Requires(filter != null); - - if (this.ConfirmArgumentCount(1)) - { - try - { - Contract.Assume(args[i] != null); - Contract.Assert(args[i] is string); // needed to prove args[i].IsPeerConsistent - int d = Convert.ToInt32(this.args[this.i]); - if (filter == null || filter(d)) - { - arg = d; - return true; - } - } - catch (System.FormatException) - { - } - catch (System.OverflowException) - { - } - } - else - { - return false; - } - - Error("Invalid argument \"{0}\" to option {1}", args[this.i], this.s); - return false; - } - - /// - /// If there is one argument and it is a non-negative integer less than "limit", - /// then call "setArg" with that number and return "true". - /// Otherwise, emit error message, leave "arg" unchanged, and return "false". - /// - public bool GetNumericArgument(Action setArg, int limit) - { - Contract.Requires(this.i < args.Length); - int a = 0; - if (!GetNumericArgument(x => a = x)) - { - return false; - } - - if (a < limit) { - setArg(a); - return true; - } - - Error("Invalid argument \"{0}\" to option {1}", args[this.i], this.s); - return false; - } - - /// - /// If there is one argument and it is a non-negative integer less than "limit", - /// then set "arg" to that number and return "true". - /// Otherwise, emit error message, leave "arg" unchanged, and return "false". - /// - public bool GetNumericArgument(ref int arg, int limit) - { - Contract.Requires(this.i < args.Length); - Contract.Ensures(Math.Min(arg, 0) <= Contract.ValueAtReturn(out arg) && - Contract.ValueAtReturn(out arg) < limit); - //modifies nextIndex, encounteredErrors, Console.Error.*; - int a = arg; - if (!GetNumericArgument(ref a)) - { - return false; - } - else if (a < limit) - { - arg = a; - return true; - } - else - { - Error("Invalid argument \"{0}\" to option {1}", args[this.i], this.s); - return false; - } - } - - /// - /// If there is one argument and it is a non-negative real, then set "arg" to that number and return "true". - /// Otherwise, emit an error message, leave "arg" unchanged, and return "false". - /// - public bool GetNumericArgument(ref double arg) - { - Contract.Ensures(Contract.ValueAtReturn(out arg) >= 0); - //modifies nextIndex, encounteredErrors, Console.Error.*; - if (this.ConfirmArgumentCount(1)) - { - try - { - Contract.Assume(args[i] != null); - Contract.Assert(args[i] is string); // needed to prove args[i].IsPeerConsistent - double d = Convert.ToDouble(this.args[this.i]); - if (0 <= d) - { - arg = d; - return true; - } - } - catch (System.FormatException) - { - } - catch (System.OverflowException) - { - } - } - else - { - return false; - } - - Error("Invalid argument \"{0}\" to option {1}", args[this.i], this.s); - return false; - } - - public bool ConfirmArgumentCount(int argCount) - { - Contract.Requires(0 <= argCount); - //modifies nextIndex, encounteredErrors, Console.Error.*; - Contract.Ensures(Contract.Result() == - (!(hasColonArgument && argCount != 1) && !(args.Length < i + argCount))); - if (hasColonArgument && argCount != 1) - { - Error("\"{0}\" cannot take a colon argument", s); - nextIndex = args.Length; - return false; - } - else if (args.Length < i + argCount) - { - Error("\"{0}\" expects {1} argument{2}", s, argCount.ToString(), (string) (argCount == 1 ? "" : "s")); - nextIndex = args.Length; - return false; - } - else - { - nextIndex = i + argCount; - return true; - } - } - - public void Error(string message, params string[] args) - { - Contract.Requires(args != null); - Contract.Requires(message != null); - //modifies encounteredErrors, Console.Error.*; - Console.Error.WriteLine("{0}: Error: {1}", ToolName, String.Format(message, args)); - EncounteredErrors = true; - } - } - protected virtual string HelpHeader => $"Usage: {ToolName} [ option ... ] [ filename ... ]" + @" @@ -1162,7 +788,7 @@ void ObjectInvariant5() public List Cho { get; set; } = new(); - protected override bool ParseOption(string name, CommandLineOptionEngine.CommandLineParseState ps) + protected override bool ParseOption(string name, CommandLineParseState ps) { var args = ps.args; // convenient synonym switch (name) diff --git a/Source/ExecutionEngine/CommandLineParseState.cs b/Source/ExecutionEngine/CommandLineParseState.cs new file mode 100644 index 000000000..7792d159f --- /dev/null +++ b/Source/ExecutionEngine/CommandLineParseState.cs @@ -0,0 +1,378 @@ +using System; +using System.Diagnostics.Contracts; + +namespace Microsoft.Boogie; + +public class CommandLineParseState +{ + public string s; + public bool hasColonArgument; + + public readonly string[] /*!*/ + args; + + public int i; + public int nextIndex; + public bool EncounteredErrors; + public readonly string ToolName; + + [ContractInvariantMethod] + void ObjectInvariant() + { + Contract.Invariant(args != null); + Contract.Invariant(0 <= i && i <= args.Length); + Contract.Invariant(0 <= nextIndex && nextIndex <= args.Length); + } + + + public CommandLineParseState(string[] args, string toolName) + { + Contract.Requires(args != null); + Contract.Requires(Contract.ForAll(0, args.Length, i => args[i] != null)); + Contract.Requires(toolName != null); + Contract.Ensures(this.args == args); + this.ToolName = toolName; + this.s = null; // set later by client + this.hasColonArgument = false; // set later by client + this.args = args; + this.i = 0; + this.nextIndex = 0; // set later by client + this.EncounteredErrors = false; + } + + public bool CheckBooleanFlag(string flagName, Action setFlag, bool valueWhenPresent = true) + { + Contract.Requires(flagName != null); + //modifies nextIndex, encounteredErrors, Console.Error.*; + bool flagPresent = false; + + if ((s == "/" + flagName || s == "-" + flagName) && ConfirmArgumentCount(0)) + { + setFlag(valueWhenPresent); + flagPresent = true; + } + + return flagPresent; + } + + public bool CheckBooleanFlag(string flagName, ref bool flag, bool valueWhenPresent) + { + Contract.Requires(flagName != null); + //modifies nextIndex, encounteredErrors, Console.Error.*; + bool flagPresent = false; + + if ((s == "/" + flagName || s == "-" + flagName) && ConfirmArgumentCount(0)) + { + flag = valueWhenPresent; + flagPresent = true; + } + + return flagPresent; + } + + public bool CheckBooleanFlag(string flagName, ref bool flag) + { + Contract.Requires(flagName != null); + //modifies nextIndex, encounteredErrors, Console.Error.*; + return CheckBooleanFlag(flagName, ref flag, true); + } + + /// + /// If there is one argument and it is a non-negative integer, + /// then call "setArg" with that number mapped to a boolean and return "true". + /// Otherwise, emit error message, leave "arg" unchanged, and return "false". + /// + public bool GetNumericArgument(Action setArg) + { + int intArg = 0; + var result = GetNumericArgument(ref intArg, x => x < 2); + if (result) { + setArg(intArg != 0); + } + return result; + } + + /// + /// If there is one argument and it is a non-negative integer, then set "arg" to that number and return "true". + /// Otherwise, emit error message, leave "arg" unchanged, and return "false". + /// + public bool GetNumericArgument(ref bool arg) + { + int intArg = 0; + var result = GetNumericArgument(ref intArg, x => x < 2); + if (result) { + arg = intArg != 0; + } + return result; + } + + /// + /// If there is one argument and it is a non-negative integer, then call "setArg" with that number and return "true". + /// Otherwise, emit error message, leave "arg" unchanged, and return "false". + /// + public bool GetNumericArgument(Action setArg, Predicate filter = null) + { + filter ??= a => 0 <= a; + + Contract.Requires(filter != null); + + if (this.ConfirmArgumentCount(1)) + { + try + { + Contract.Assume(args[i] != null); + Contract.Assert(args[i] is string); // needed to prove args[i].IsPeerConsistent + int d = Convert.ToInt32(this.args[this.i]); + if (filter == null || filter(d)) + { + setArg(d); + return true; + } + } + catch (FormatException) + { + } + catch (OverflowException) + { + } + } + else + { + return false; + } + + Error("Invalid argument \"{0}\" to option {1}", args[this.i], this.s); + return false; + } + + /// + /// If there is one argument and it is a non-negative integer, then set "arg" to that number and return "true". + /// Otherwise, emit error message, leave "arg" unchanged, and return "false". + /// + public bool GetNumericArgument(ref int arg) + { + //modifies nextIndex, encounteredErrors, Console.Error.*; + return GetNumericArgument(ref arg, a => 0 <= a); + } + + public bool GetUnsignedNumericArgument(Action setArg, Predicate filter = null) + { + if (ConfirmArgumentCount(1)) + { + try + { + Contract.Assume(args[i] != null); + Contract.Assert(args[i] is string); // needed to prove args[i].IsPeerConsistent + uint d = Convert.ToUInt32(this.args[this.i]); + if (filter == null || filter(d)) + { + setArg(d); + return true; + } + } + catch (FormatException) + { + } + catch (OverflowException) + { + } + } + else + { + return false; + } + + Error("Invalid argument \"{0}\" to option {1}", args[this.i], this.s); + return false; + } + + public bool GetUnsignedNumericArgument(ref uint arg, Predicate filter) + { + if (this.ConfirmArgumentCount(1)) + { + try + { + Contract.Assume(args[i] != null); + Contract.Assert(args[i] is string); // needed to prove args[i].IsPeerConsistent + uint d = Convert.ToUInt32(this.args[this.i]); + if (filter == null || filter(d)) + { + arg = d; + return true; + } + } + catch (System.FormatException) + { + } + catch (System.OverflowException) + { + } + } + else + { + return false; + } + + Error("Invalid argument \"{0}\" to option {1}", args[this.i], this.s); + return false; + } + + /// + /// If there is one argument and the filtering predicate holds, then set "arg" to that number and return "true". + /// Otherwise, emit error message, leave "arg" unchanged, and return "false". + /// + public bool GetNumericArgument(ref int arg, Predicate filter) + { + Contract.Requires(filter != null); + + if (this.ConfirmArgumentCount(1)) + { + try + { + Contract.Assume(args[i] != null); + Contract.Assert(args[i] is string); // needed to prove args[i].IsPeerConsistent + int d = Convert.ToInt32(this.args[this.i]); + if (filter == null || filter(d)) + { + arg = d; + return true; + } + } + catch (System.FormatException) + { + } + catch (System.OverflowException) + { + } + } + else + { + return false; + } + + Error("Invalid argument \"{0}\" to option {1}", args[this.i], this.s); + return false; + } + + /// + /// If there is one argument and it is a non-negative integer less than "limit", + /// then call "setArg" with that number and return "true". + /// Otherwise, emit error message, leave "arg" unchanged, and return "false". + /// + public bool GetNumericArgument(Action setArg, int limit) + { + Contract.Requires(this.i < args.Length); + int a = 0; + if (!GetNumericArgument(x => a = x)) + { + return false; + } + + if (a < limit) { + setArg(a); + return true; + } + + Error("Invalid argument \"{0}\" to option {1}", args[this.i], this.s); + return false; + } + + /// + /// If there is one argument and it is a non-negative integer less than "limit", + /// then set "arg" to that number and return "true". + /// Otherwise, emit error message, leave "arg" unchanged, and return "false". + /// + public bool GetNumericArgument(ref int arg, int limit) + { + Contract.Requires(this.i < args.Length); + Contract.Ensures(Math.Min(arg, 0) <= Contract.ValueAtReturn(out arg) && + Contract.ValueAtReturn(out arg) < limit); + //modifies nextIndex, encounteredErrors, Console.Error.*; + int a = arg; + if (!GetNumericArgument(ref a)) + { + return false; + } + else if (a < limit) + { + arg = a; + return true; + } + else + { + Error("Invalid argument \"{0}\" to option {1}", args[this.i], this.s); + return false; + } + } + + /// + /// If there is one argument and it is a non-negative real, then set "arg" to that number and return "true". + /// Otherwise, emit an error message, leave "arg" unchanged, and return "false". + /// + public bool GetNumericArgument(ref double arg) + { + Contract.Ensures(Contract.ValueAtReturn(out arg) >= 0); + //modifies nextIndex, encounteredErrors, Console.Error.*; + if (this.ConfirmArgumentCount(1)) + { + try + { + Contract.Assume(args[i] != null); + Contract.Assert(args[i] is string); // needed to prove args[i].IsPeerConsistent + double d = Convert.ToDouble(this.args[this.i]); + if (0 <= d) + { + arg = d; + return true; + } + } + catch (System.FormatException) + { + } + catch (System.OverflowException) + { + } + } + else + { + return false; + } + + Error("Invalid argument \"{0}\" to option {1}", args[this.i], this.s); + return false; + } + + public bool ConfirmArgumentCount(int argCount) + { + Contract.Requires(0 <= argCount); + //modifies nextIndex, encounteredErrors, Console.Error.*; + Contract.Ensures(Contract.Result() == + (!(hasColonArgument && argCount != 1) && !(args.Length < i + argCount))); + if (hasColonArgument && argCount != 1) + { + Error("\"{0}\" cannot take a colon argument", s); + nextIndex = args.Length; + return false; + } + else if (args.Length < i + argCount) + { + Error("\"{0}\" expects {1} argument{2}", s, argCount.ToString(), (string) (argCount == 1 ? "" : "s")); + nextIndex = args.Length; + return false; + } + else + { + nextIndex = i + argCount; + return true; + } + } + + public void Error(string message, params string[] args) + { + Contract.Requires(args != null); + Contract.Requires(message != null); + //modifies encounteredErrors, Console.Error.*; + Console.Error.WriteLine("{0}: Error: {1}", ToolName, String.Format(message, args)); + EncounteredErrors = true; + } +} \ No newline at end of file diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index 2a2fcab21..9f5da8765 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -172,48 +172,6 @@ protected static string CleanUp(string msg) #endregion - public sealed class VerificationResult - { - public readonly string RequestId; - public readonly string Checksum; - public readonly string DependeciesChecksum; - public readonly string ImplementationName; - public readonly IToken ImplementationToken; - public readonly string ProgramId; - public readonly string MessageIfVerifies; - - public DateTime Start { get; set; } - public DateTime End { get; set; } - - public int ResourceCount { get; set; } - - public int ProofObligationCount - { - get { return ProofObligationCountAfter - ProofObligationCountBefore; } - } - - public int ProofObligationCountBefore { get; set; } - public int ProofObligationCountAfter { get; set; } - - public ConditionGeneration.Outcome Outcome { get; set; } - public List Errors; - public List VCResults; - - public ISet AssertionChecksums { get; private set; } - - public VerificationResult(string requestId, Implementation implementation, string programId = null) - { - Checksum = implementation.Checksum; - DependeciesChecksum = implementation.DependencyChecksum; - RequestId = requestId; - ImplementationName = implementation.Name; - ImplementationToken = implementation.tok; - ProgramId = programId; - AssertionChecksums = implementation.AssertionChecksums; - MessageIfVerifies = implementation.FindStringAttribute("msg_if_verifies"); - } - } - public class ExecutionEngine : IDisposable { public static OutputPrinter printer; @@ -792,41 +750,57 @@ public PipelineOutcome InferAndVerify( #endregion - #region Select and prioritize implementations that should be verified + var stablePrioritizedImpls = GetPrioritizedImplementations(program); + + if (1 < Options.VerifySnapshots) + { + CachedVerificationResultInjector.Inject(this, program, stablePrioritizedImpls, requestId, programId, + out stats.CachingActionCounts); + } + + var outcome = VerifyEachImplementation(program, stats, programId, er, requestId, stablePrioritizedImpls, extractLoopMappingInfo); + + if (1 < Options.VerifySnapshots && programId != null) + { + program.FreezeTopLevelDeclarations(); + programCache.Set(programId, program, policy); + } + + TraceCachingForBenchmarking(stats, requestId, start); + + return outcome; + } + private Implementation[] GetPrioritizedImplementations(Program program) + { var impls = program.Implementations.Where( impl => impl != null && Options.UserWantsToCheckRoutine(cce.NonNull(impl.Name)) && !impl.IsSkipVerification(Options)); // operate on a stable copy, in case it gets updated while we're running Implementation[] stablePrioritizedImpls = null; - if (0 < Options.VerifySnapshots) - { + if (0 < Options.VerifySnapshots) { OtherDefinitionAxiomsCollector.Collect(Options, program.Axioms); DependencyCollector.Collect(Options, program); stablePrioritizedImpls = impls.OrderByDescending( - impl => impl.Priority != 1 ? impl.Priority : Cache.VerificationPriority(impl, Options.RunDiagnosticsOnTimeout)).ToArray(); - } - else - { + impl => impl.Priority != 1 ? impl.Priority : Cache.VerificationPriority(impl, Options.RunDiagnosticsOnTimeout)) + .ToArray(); + } else { stablePrioritizedImpls = impls.OrderByDescending(impl => impl.Priority).ToArray(); } - #endregion - - if (1 < Options.VerifySnapshots) - { - CachedVerificationResultInjector.Inject(this, program, stablePrioritizedImpls, requestId, programId, - out stats.CachingActionCounts); - } + return stablePrioritizedImpls; + } - #region Verify each implementation + private PipelineOutcome VerifyEachImplementation(Program program, PipelineStatistics stats, string programId, + ErrorReporterDelegate er, string requestId, Implementation[] stablePrioritizedImpls, + Dictionary> extractLoopMappingInfo) + { program.DeclarationDependencies = Prune.ComputeDeclarationDependencies(Options, program); var outputCollector = new OutputCollector(stablePrioritizedImpls); var outcome = PipelineOutcome.VerificationCompleted; - try - { + try { var cts = new CancellationTokenSource(); RequestIdToCancellationTokenSource.AddOrUpdate(requestId, cts, (k, ov) => cts); @@ -835,13 +809,11 @@ public PipelineOutcome InferAndVerify( var semaphore = new SemaphoreSlim(Options.VcsCores); // Create a task per implementation. - for (int i = 0; i < stablePrioritizedImpls.Length; i++) - { + for (int i = 0; i < stablePrioritizedImpls.Length; i++) { var taskIndex = i; var id = stablePrioritizedImpls[taskIndex].Id; - if (ImplIdToCancellationTokenSource.TryGetValue(id, out var old)) - { + if (ImplIdToCancellationTokenSource.TryGetValue(id, out var old)) { old.Cancel(); } @@ -849,15 +821,12 @@ public PipelineOutcome InferAndVerify( var t = new Task((dummy) => { - try - { - if (outcome == PipelineOutcome.FatalError) - { + try { + if (outcome == PipelineOutcome.FatalError) { return; } - if (cts.Token.IsCancellationRequested) - { + if (cts.Token.IsCancellationRequested) { cts.Token.ThrowIfCancellationRequested(); } @@ -865,8 +834,7 @@ public PipelineOutcome InferAndVerify( taskIndex, outputCollector, checkerPool, programId); ImplIdToCancellationTokenSource.TryRemove(id, out old); } - finally - { + finally { semaphore.Release(); } }, cts.Token, TaskCreationOptions.None); @@ -875,14 +843,11 @@ public PipelineOutcome InferAndVerify( // Execute the tasks. int j = 0; - for (; j < stablePrioritizedImpls.Length && outcome != PipelineOutcome.FatalError; j++) - { - try - { + for (; j < stablePrioritizedImpls.Length && outcome != PipelineOutcome.FatalError; j++) { + try { semaphore.Wait(cts.Token); } - catch (OperationCanceledException) - { + catch (OperationCanceledException) { break; } @@ -893,19 +858,16 @@ public PipelineOutcome InferAndVerify( tasks = tasks.Take(j).ToArray(); Task.WaitAll(tasks); } - catch (AggregateException ae) - { + catch (AggregateException ae) { ae.Flatten().Handle(e => { - if (e is ProverException) - { + if (e is ProverException) { printer.ErrorWriteLine(Console.Out, "Fatal Error: ProverException: {0}", e.Message); outcome = PipelineOutcome.FatalError; return true; } - if (e is OperationCanceledException) - { + if (e is OperationCanceledException) { outcome = PipelineOutcome.Cancelled; return true; } @@ -913,31 +875,25 @@ public PipelineOutcome InferAndVerify( return false; }); } - finally - { + finally { CleanupRequest(requestId); } - if (Options.PrintNecessaryAssumes && program.NecessaryAssumes.Any()) - { + if (Options.PrintNecessaryAssumes && program.NecessaryAssumes.Any()) { Console.WriteLine("Necessary assume command(s): {0}", string.Join(", ", program.NecessaryAssumes)); } cce.NonNull(Options.TheProverFactory).Close(); outputCollector.WriteMoreOutput(); + return outcome; + } - if (1 < Options.VerifySnapshots && programId != null) - { - program.FreezeTopLevelDeclarations(); - programCache.Set(programId, program, policy); - } - - if (0 <= Options.VerifySnapshots && Options.TraceCachingForBenchmarking) - { + private void TraceCachingForBenchmarking(PipelineStatistics stats, string requestId, DateTime start) + { + if (0 <= Options.VerifySnapshots && Options.TraceCachingForBenchmarking) { var end = DateTime.UtcNow; - if (TimePerRequest.Count == 0) - { + if (TimePerRequest.Count == 0) { FirstRequestStart = start; } @@ -953,8 +909,7 @@ public PipelineOutcome InferAndVerify( Console.Out.WriteLine( "Request ID{0}, Error, E (C), Inconclusive, I (C), Out of Memory, OoM (C), Timeout, T (C), Verified, V (C), {1}", printTimes ? ", Time (ms)" : "", actions); - foreach (var kv in TimePerRequest.OrderBy(kv => ExecutionEngine.AutoRequestId(kv.Key))) - { + foreach (var kv in TimePerRequest.OrderBy(kv => ExecutionEngine.AutoRequestId(kv.Key))) { var s = StatisticsPerRequest[kv.Key]; var cacs = s.CachingActionCounts; var c = cacs != null ? ", " + cacs.Select(ac => string.Format("{0,3}", ac)).Concat(", ") : ""; @@ -965,17 +920,12 @@ public PipelineOutcome InferAndVerify( s.CachedOutOfMemoryCount, s.TimeoutCount, s.CachedTimeoutCount, s.VerifiedCount, s.CachedVerifiedCount, c); } - if (printTimes) - { + if (printTimes) { Console.Out.WriteLine(); Console.Out.WriteLine("Total time (ms) since first request: {0:F0}", end.Subtract(FirstRequestStart).TotalMilliseconds); } } - - #endregion - - return outcome; } public static void CancelRequest(string requestId) diff --git a/Source/ExecutionEngine/VerificationResult.cs b/Source/ExecutionEngine/VerificationResult.cs new file mode 100644 index 000000000..2edfb9218 --- /dev/null +++ b/Source/ExecutionEngine/VerificationResult.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using VC; + +namespace Microsoft.Boogie; + +public sealed class VerificationResult +{ + public readonly string RequestId; + public readonly string Checksum; + public readonly string DependeciesChecksum; + public readonly string ImplementationName; + public readonly IToken ImplementationToken; + public readonly string ProgramId; + public readonly string MessageIfVerifies; + + public DateTime Start { get; set; } + public DateTime End { get; set; } + + public int ResourceCount { get; set; } + + public int ProofObligationCount + { + get { return ProofObligationCountAfter - ProofObligationCountBefore; } + } + + public int ProofObligationCountBefore { get; set; } + public int ProofObligationCountAfter { get; set; } + + public ConditionGeneration.Outcome Outcome { get; set; } + public List Errors; + public List VCResults; + + public ISet AssertionChecksums { get; private set; } + + public VerificationResult(string requestId, Implementation implementation, string programId = null) + { + Checksum = implementation.Checksum; + DependeciesChecksum = implementation.DependencyChecksum; + RequestId = requestId; + ImplementationName = implementation.Name; + ImplementationToken = implementation.tok; + ProgramId = programId; + AssertionChecksums = implementation.AssertionChecksums; + MessageIfVerifies = implementation.FindStringAttribute("msg_if_verifies"); + } +} \ No newline at end of file From b1ff4af3b7c42badaf8e0b1009996a84523b4e3e Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Wed, 9 Mar 2022 22:54:36 +0100 Subject: [PATCH 13/32] Extract methods within ExecutionEngine.VerifyImplementation (#528) * Extract methods * Rename Process to Emit --- Source/ExecutionEngine/ExecutionEngine.cs | 250 +++++++------------ Source/ExecutionEngine/OutputCollector.cs | 41 +++ Source/ExecutionEngine/VerificationResult.cs | 36 +++ 3 files changed, 164 insertions(+), 163 deletions(-) create mode 100644 Source/ExecutionEngine/OutputCollector.cs diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index 9f5da8765..0d824985f 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -831,7 +831,7 @@ private PipelineOutcome VerifyEachImplementation(Program program, PipelineStatis } VerifyImplementation(program, stats, er, requestId, extractLoopMappingInfo, stablePrioritizedImpls, - taskIndex, outputCollector, checkerPool, programId); + taskIndex, outputCollector, programId); ImplIdToCancellationTokenSource.TryRemove(id, out old); } finally { @@ -951,194 +951,118 @@ private static void CleanupRequest(string requestId) private void VerifyImplementation(Program program, PipelineStatistics stats, ErrorReporterDelegate er, string requestId, Dictionary> extractLoopMappingInfo, - Implementation[] stablePrioritizedImpls, int index, OutputCollector outputCollector, CheckerPool checkerPool, - string programId) + Implementation[] stablePrioritizedImpls, int index, OutputCollector outputCollector, string programId) { - Implementation impl = stablePrioritizedImpls[index]; - VerificationResult verificationResult = null; var output = new StringWriter(); + Implementation impl = stablePrioritizedImpls[index]; printer.Inform("", output); // newline - printer.Inform(string.Format("Verifying {0} ...", impl.Name), output); + printer.Inform($"Verifying {impl.Name} ...", output); + var verificationResult = GetCachedVerificationResult(impl, output); + + var wasCached = true; + if (verificationResult == null) { + wasCached = false; + + verificationResult = VerifyImplementationWithoutCaching(program, stats, er, requestId, extractLoopMappingInfo, programId, impl, output); + + if (0 < Options.VerifySnapshots && !string.IsNullOrEmpty(impl.Checksum)) + { + Cache.Insert(impl, verificationResult); + } + } + verificationResult.Emit(this, stats, er, index, outputCollector, output, impl, wasCached); + } + private VerificationResult GetCachedVerificationResult(Implementation impl, TextWriter output) + { + VerificationResult verificationResult = null; int priority = 0; - var wasCached = false; - if (0 < Options.VerifySnapshots) - { + if (0 < Options.VerifySnapshots) { var cachedResults = Cache.Lookup(impl, Options.RunDiagnosticsOnTimeout, out priority); - if (cachedResults != null && priority == Priority.SKIP) - { + if (cachedResults != null && priority == Priority.SKIP) { printer.Inform(string.Format("Retrieving cached verification result for implementation {0}...", impl.Name), output); if (Options.VerifySnapshots < 3 || - cachedResults.Outcome == ConditionGeneration.Outcome.Correct) - { + cachedResults.Outcome == ConditionGeneration.Outcome.Correct) { verificationResult = cachedResults; - wasCached = true; } } } - if (!wasCached) - { - #region Verify the implementation - - verificationResult = new VerificationResult(requestId, impl, programId); - - using (var vcgen = CreateVCGen(program, checkerPool)) - { - vcgen.CachingActionCounts = stats.CachingActionCounts; - verificationResult.ProofObligationCountBefore = vcgen.CumulativeAssertionCount; - verificationResult.Start = DateTime.UtcNow; + return verificationResult; + } - try { - var cancellationToken = RequestIdToCancellationTokenSource[requestId].Token; - verificationResult.Outcome = - vcgen.VerifyImplementation(impl, out verificationResult.Errors, - out verificationResult.VCResults, requestId, cancellationToken); - if (Options.ExtractLoops && verificationResult.Errors != null) { - var vcg = vcgen as VCGen; - if (vcg != null) { - for (int i = 0; i < verificationResult.Errors.Count; i++) { - verificationResult.Errors[i] = vcg.extractLoopTrace(verificationResult.Errors[i], impl.Name, - program, extractLoopMappingInfo); - } + private VerificationResult VerifyImplementationWithoutCaching(Program program, PipelineStatistics stats, + ErrorReporterDelegate er, string requestId, Dictionary> extractLoopMappingInfo, + string programId, Implementation impl, TextWriter output) + { + VerificationResult verificationResult = new VerificationResult(requestId, impl, programId); + + using (var vcgen = CreateVCGen(program, checkerPool)) { + vcgen.CachingActionCounts = stats.CachingActionCounts; + verificationResult.ProofObligationCountBefore = vcgen.CumulativeAssertionCount; + verificationResult.Start = DateTime.UtcNow; + + try { + var cancellationToken = RequestIdToCancellationTokenSource[requestId].Token; + verificationResult.Outcome = + vcgen.VerifyImplementation(impl, out verificationResult.Errors, + out verificationResult.VCResults, requestId, cancellationToken); + if (Options.ExtractLoops && verificationResult.Errors != null) { + var vcg = vcgen as VCGen; + if (vcg != null) { + for (int i = 0; i < verificationResult.Errors.Count; i++) { + verificationResult.Errors[i] = vcg.extractLoopTrace(verificationResult.Errors[i], impl.Name, + program, extractLoopMappingInfo); } } } - catch (VCGenException e) - { - var errorInfo = errorInformationFactory.CreateErrorInformation(impl.tok, - String.Format("{0} (encountered in implementation {1}).", e.Message, impl.Name), requestId, "Error"); - errorInfo.ImplementationName = impl.Name; - printer.WriteErrorInformation(errorInfo, output); - if (er != null) - { - lock (er) - { - er(errorInfo); - } + } + catch (VCGenException e) { + var errorInfo = errorInformationFactory.CreateErrorInformation(impl.tok, + String.Format("{0} (encountered in implementation {1}).", e.Message, impl.Name), requestId, "Error"); + errorInfo.ImplementationName = impl.Name; + printer.WriteErrorInformation(errorInfo, output); + if (er != null) { + lock (er) { + er(errorInfo); } - - verificationResult.Errors = null; - verificationResult.Outcome = VCGen.Outcome.Inconclusive; } - catch (ProverDiedException) - { - throw; - } - catch (UnexpectedProverOutputException upo) - { - printer.AdvisoryWriteLine("Advisory: {0} SKIPPED because of internal error: unexpected prover output: {1}", - impl.Name, upo.Message); - verificationResult.Errors = null; - verificationResult.Outcome = VCGen.Outcome.Inconclusive; - } - catch(AggregateException ae) - { - ae.Flatten().Handle(e => - { - if (e is IOException) - { - printer.AdvisoryWriteLine("Advisory: {0} SKIPPED due to I/O exception: {1}", - impl.Name, e.Message); - verificationResult.Errors = null; - verificationResult.Outcome = VCGen.Outcome.SolverException; - return true; - } - return false; - }); - } - - verificationResult.ProofObligationCountAfter = vcgen.CumulativeAssertionCount; - verificationResult.End = DateTime.UtcNow; - verificationResult.ResourceCount = vcgen.ResourceCount; + verificationResult.Errors = null; + verificationResult.Outcome = VCGen.Outcome.Inconclusive; } - - #endregion - - #region Cache the verification result - - if (0 < Options.VerifySnapshots && !string.IsNullOrEmpty(impl.Checksum)) - { - Cache.Insert(impl, verificationResult); + catch (ProverDiedException) { + throw; } - - #endregion - } - - #region Process the verification results and statistics - - ProcessOutcome(verificationResult.Outcome, verificationResult.Errors, TimeIndication(verificationResult), stats, - output, impl.GetTimeLimit(Options), er, verificationResult.ImplementationName, verificationResult.ImplementationToken, - verificationResult.RequestId, verificationResult.MessageIfVerifies, wasCached); - - ProcessErrors(verificationResult.Errors, verificationResult.Outcome, output, er, impl); - - if (Options.XmlSink != null) - { - lock (Options.XmlSink) { - Options.XmlSink.WriteStartMethod(impl.Name, verificationResult.Start); - - foreach (var vcResult in verificationResult.VCResults.OrderBy(s => s.vcNum)) { - Options.XmlSink.WriteSplit(vcResult.vcNum, vcResult.asserts, vcResult.startTime, - vcResult.outcome.ToString().ToLowerInvariant(), vcResult.runTime, vcResult.resourceCount); - } - - Options.XmlSink.WriteEndMethod(verificationResult.Outcome.ToString().ToLowerInvariant(), - verificationResult.End, verificationResult.End - verificationResult.Start, - verificationResult.ResourceCount); + catch (UnexpectedProverOutputException upo) { + printer.AdvisoryWriteLine("Advisory: {0} SKIPPED because of internal error: unexpected prover output: {1}", + impl.Name, upo.Message); + verificationResult.Errors = null; + verificationResult.Outcome = VCGen.Outcome.Inconclusive; } - } - - outputCollector.Add(index, output); - - outputCollector.WriteMoreOutput(); - - if (verificationResult.Outcome == VCGen.Outcome.Errors || Options.Trace) - { - Console.Out.Flush(); - } - - #endregion - } - - - class OutputCollector - { - StringWriter[] outputs; - - int nextPrintableIndex = 0; - - public OutputCollector(Implementation[] implementations) - { - outputs = new StringWriter[implementations.Length]; - } - - public void WriteMoreOutput() - { - lock (outputs) - { - for (; nextPrintableIndex < outputs.Length && outputs[nextPrintableIndex] != null; nextPrintableIndex++) + catch (AggregateException ae) { + ae.Flatten().Handle(e => { - Console.Write(outputs[nextPrintableIndex].ToString()); - outputs[nextPrintableIndex] = null; - Console.Out.Flush(); - } - } - } - - public void Add(int index, StringWriter output) - { - Contract.Requires(0 <= index && index < outputs.Length); - Contract.Requires(output != null); + if (e is IOException) { + printer.AdvisoryWriteLine("Advisory: {0} SKIPPED due to I/O exception: {1}", + impl.Name, e.Message); + verificationResult.Errors = null; + verificationResult.Outcome = VCGen.Outcome.SolverException; + return true; + } - lock (this) - { - outputs[index] = output; + return false; + }); } + + verificationResult.ProofObligationCountAfter = vcgen.CumulativeAssertionCount; + verificationResult.End = DateTime.UtcNow; + verificationResult.ResourceCount = vcgen.ResourceCount; } + + return verificationResult; } private static ConditionGeneration CreateVCGen(Program program, CheckerPool checkerPool) @@ -1256,7 +1180,7 @@ private PipelineOutcome RunStagedHoudini(Program program, PipelineStatistics sta #endregion - private string TimeIndication(VerificationResult verificationResult) + public string TimeIndication(VerificationResult verificationResult) { var result = ""; if (Options.Trace) @@ -1277,7 +1201,7 @@ private string TimeIndication(VerificationResult verificationResult) } - private void ProcessOutcome(VC.VCGen.Outcome outcome, List errors, string timeIndication, + public void ProcessOutcome(VC.VCGen.Outcome outcome, List errors, string timeIndication, PipelineStatistics stats, TextWriter tw, uint timeLimit, ErrorReporterDelegate er = null, string implName = null, IToken implTok = null, string requestId = null, string msgIfVerifies = null, bool wasCached = false) { @@ -1553,7 +1477,7 @@ private static void UpdateStatistics(PipelineStatistics stats, VC.VCGen.Outcome } - private void ProcessErrors(List errors, VC.VCGen.Outcome outcome, TextWriter tw, + public void ProcessErrors(List errors, VC.VCGen.Outcome outcome, TextWriter tw, ErrorReporterDelegate er, Implementation impl = null) { var implName = impl != null ? impl.Name : null; diff --git a/Source/ExecutionEngine/OutputCollector.cs b/Source/ExecutionEngine/OutputCollector.cs new file mode 100644 index 000000000..32b794717 --- /dev/null +++ b/Source/ExecutionEngine/OutputCollector.cs @@ -0,0 +1,41 @@ +using System; +using System.Diagnostics.Contracts; +using System.IO; + +namespace Microsoft.Boogie; + +public class OutputCollector +{ + StringWriter[] outputs; + + int nextPrintableIndex = 0; + + public OutputCollector(Implementation[] implementations) + { + outputs = new StringWriter[implementations.Length]; + } + + public void WriteMoreOutput() + { + lock (outputs) + { + for (; nextPrintableIndex < outputs.Length && outputs[nextPrintableIndex] != null; nextPrintableIndex++) + { + Console.Write(outputs[nextPrintableIndex].ToString()); + outputs[nextPrintableIndex] = null; + Console.Out.Flush(); + } + } + } + + public void Add(int index, StringWriter output) + { + Contract.Requires(0 <= index && index < outputs.Length); + Contract.Requires(output != null); + + lock (this) + { + outputs[index] = output; + } + } +} \ No newline at end of file diff --git a/Source/ExecutionEngine/VerificationResult.cs b/Source/ExecutionEngine/VerificationResult.cs index 2edfb9218..ef165ec78 100644 --- a/Source/ExecutionEngine/VerificationResult.cs +++ b/Source/ExecutionEngine/VerificationResult.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.IO; +using System.Linq; using VC; namespace Microsoft.Boogie; @@ -44,4 +46,38 @@ public VerificationResult(string requestId, Implementation implementation, strin AssertionChecksums = implementation.AssertionChecksums; MessageIfVerifies = implementation.FindStringAttribute("msg_if_verifies"); } + + public void Emit(ExecutionEngine engine, PipelineStatistics stats, ErrorReporterDelegate er, int index, + OutputCollector outputCollector, StringWriter output, Implementation impl, bool wasCached) + { + engine.ProcessOutcome(Outcome, Errors, engine.TimeIndication(this), stats, + output, impl.GetTimeLimit(engine.Options), er, ImplementationName, + ImplementationToken, + RequestId, MessageIfVerifies, wasCached); + + engine.ProcessErrors(Errors, Outcome, output, er, impl); + + if (engine.Options.XmlSink != null) { + lock (engine.Options.XmlSink) { + engine.Options.XmlSink.WriteStartMethod(impl.Name, Start); + + foreach (var vcResult in VCResults.OrderBy(s => s.vcNum)) { + engine.Options.XmlSink.WriteSplit(vcResult.vcNum, vcResult.asserts, vcResult.startTime, + vcResult.outcome.ToString().ToLowerInvariant(), vcResult.runTime, vcResult.resourceCount); + } + + engine.Options.XmlSink.WriteEndMethod(Outcome.ToString().ToLowerInvariant(), + End, End - Start, + ResourceCount); + } + } + + outputCollector.Add(index, output); + + outputCollector.WriteMoreOutput(); + + if (Outcome == VCGen.Outcome.Errors || engine.Options.Trace) { + Console.Out.Flush(); + } + } } \ No newline at end of file From c0ea6a18ab763acffef7eb3362e39019d10ecab2 Mon Sep 17 00:00:00 2001 From: Aaron Tomb Date: Wed, 9 Mar 2022 14:04:23 -0800 Subject: [PATCH 14/32] Add more structured descriptions of proof obligations (#505) Adds a structured type for describing proof obligations with potentially different text for success and failure forms. As a result, describing successful proofs becomes less awkward. This is more valuable for Boogie clients (such as Dafny) than it is for Boogie itself, though it has the benefit of grouping error messages in one place. At the moment, Boogie only uses the failure text, but I could imagine it using the success text at some point. Dafny plans to use the success text right away, and to provide a large number of additional subclasses to attach to the instances of AssertCmd it generates. I also hope that it could serve as a more general replacement for ErrorData, though I don't have a very thorough idea of how the various Boogie clients currently use that property. --- Source/Core/Absy.cs | 10 ++ Source/Core/AbsyCmd.cs | 39 +++++--- Source/Core/ProofObligationDescription.cs | 106 ++++++++++++++++++++++ Source/ExecutionEngine/ExecutionEngine.cs | 61 ++++--------- Source/VCGeneration/VCGen.cs | 1 + 5 files changed, 161 insertions(+), 56 deletions(-) create mode 100644 Source/Core/ProofObligationDescription.cs diff --git a/Source/Core/Absy.cs b/Source/Core/Absy.cs index cdb047290..8daacddcb 100644 --- a/Source/Core/Absy.cs +++ b/Source/Core/Absy.cs @@ -3617,6 +3617,8 @@ public class Requires : Absy, ICarriesAttributes, IPotentialErrorNode ins, List outs) : base(tok, null) { @@ -3751,12 +3761,17 @@ public void MarkAsVerifiedUnder(Expr expr) // TODO: convert to use generics private object errorData; + // Note: the `Description` property should cover all the use cases + // of `ErrorData` and be used instead. Ideally, `ErrorData` will + // eventually go away. public object ErrorData { get { return errorData; } set { errorData = value; } } + public ProofObligationDescription Description { get; set; } + public string ErrorMessage { get { return QKeyValue.FindStringAttribute(Attributes, "msg"); } @@ -3770,22 +3785,18 @@ public MiningStrategy ErrorDataEnhanced set { errorDataEnhanced = value; } } - public AssertCmd(IToken /*!*/ tok, Expr /*!*/ expr) - : base(tok, expr) - { - Contract.Requires(tok != null); - Contract.Requires(expr != null); - errorDataEnhanced = GenerateBoundVarMiningStrategy(expr); - } - - public AssertCmd(IToken /*!*/ tok, Expr /*!*/ expr, QKeyValue kv) + public AssertCmd(IToken /*!*/ tok, Expr /*!*/ expr, ProofObligationDescription description, QKeyValue kv = null) : base(tok, expr, kv) { Contract.Requires(tok != null); Contract.Requires(expr != null); errorDataEnhanced = GenerateBoundVarMiningStrategy(expr); + Description = description; } + public AssertCmd(IToken /*!*/ tok, Expr /*!*/ expr, QKeyValue kv = null) + : this(tok, expr, new AssertionDescription(), kv) { } + public override void Emit(TokenTextWriter stream, int level) { //Contract.Requires(stream != null); @@ -3888,7 +3899,7 @@ public override Absy StdDispatch(StandardVisitor visitor) public class LoopInitAssertCmd : AssertCmd { public LoopInitAssertCmd(IToken /*!*/ tok, Expr /*!*/ expr) - : base(tok, expr) + : base(tok, expr, new InvariantEstablishedDescription()) { Contract.Requires(tok != null); Contract.Requires(expr != null); @@ -3899,7 +3910,7 @@ public LoopInitAssertCmd(IToken /*!*/ tok, Expr /*!*/ expr) public class LoopInvMaintainedAssertCmd : AssertCmd { public LoopInvMaintainedAssertCmd(IToken /*!*/ tok, Expr /*!*/ expr) - : base(tok, expr) + : base(tok, expr, new InvariantMaintainedDescription()) { Contract.Requires(tok != null); Contract.Requires(expr != null); @@ -3926,7 +3937,7 @@ void ObjectInvariant() public AssertRequiresCmd(CallCmd /*!*/ call, Requires /*!*/ requires) - : base(call.tok, requires.Condition) + : base(call.tok, requires.Condition, requires.Description) { Contract.Requires(call != null); Contract.Requires(requires != null); @@ -3958,7 +3969,7 @@ void ObjectInvariant() } public AssertEnsuresCmd(Ensures /*!*/ ens) - : base(ens.tok, ens.Condition) + : base(ens.tok, ens.Condition, ens.Description) { Contract.Requires(ens != null); this.Ensures = ens; @@ -4161,6 +4172,8 @@ public override Absy StdDispatch(StandardVisitor visitor) [ContractClass(typeof(TransferCmdContracts))] public abstract class TransferCmd : Absy { + public ProofObligationDescription Description { get; set; } = new PostconditionDescription(); + internal TransferCmd(IToken /*!*/ tok) : base(tok) { diff --git a/Source/Core/ProofObligationDescription.cs b/Source/Core/ProofObligationDescription.cs new file mode 100644 index 000000000..b6bef2ab7 --- /dev/null +++ b/Source/Core/ProofObligationDescription.cs @@ -0,0 +1,106 @@ +namespace Microsoft.Boogie; + +/// +/// A multi-faceted description of a proof obligation. This class is intended +/// to provide several forms of human-readable text summarizing the meaning of +/// a proof obligation: one for the case where the proof has succeeded, one +/// for the case where the proof has failed, and one for the case of quickly +/// identifying the type of proof obligation (for inclusion in long lists of +/// proof obligations, for example). +/// +public abstract class ProofObligationDescription { + /// + /// A description of what this proof obligation means when it has been + /// successfully proven. + /// + public abstract string SuccessDescription { get; } + + /// + /// A description of what this proof obligation means (or might mean) + /// when the prover has failed to establish its validity. + /// + public virtual string FailureDescription => + $"Failed to prove: {SuccessDescription}"; + + /// + /// A short description of the general type of this proof obligation. + /// Should be a unique identifier appropriate for programmatic comparison + /// but also comprehensible to humans. + /// + public abstract string ShortDescription { get; } +} + +public class AssertionDescription : ProofObligationDescription +{ + public override string SuccessDescription => "This assertion holds."; + + public override string FailureDescription => "This assertion might not hold."; + + public override string ShortDescription => "assert"; +} + +public class PreconditionDescription : ProofObligationDescription +{ + public override string SuccessDescription => + "All preconditions hold for this call."; + + public override string FailureDescription => + "A precondition for this call might not hold."; + + public override string ShortDescription => "precondition"; +} + +public class RequiresDescription : ProofObligationDescription +{ + public override string SuccessDescription => + "This precondition holds."; + + public override string FailureDescription => + "This is the precondition that might not hold."; + + public override string ShortDescription => "requires"; +} + +public class PostconditionDescription : ProofObligationDescription +{ + public override string SuccessDescription => + "All postconditions hold for this return path."; + + public override string FailureDescription => + "A postcondition might not hold on this return path."; + + public override string ShortDescription => "postcondition"; +} + +public class EnsuresDescription : ProofObligationDescription +{ + public override string SuccessDescription => + "This postcondition holds."; + + public override string FailureDescription => + "This is the postcondition that might not hold."; + + public override string ShortDescription => "ensures"; +} + +public class InvariantEstablishedDescription : AssertionDescription +{ + public override string SuccessDescription => + "This loop invariant holds on entry."; + + public override string FailureDescription => + "This loop invariant might not hold on entry."; + + public override string ShortDescription => "invariant established"; +} + +public class InvariantMaintainedDescription : AssertionDescription +{ + public override string SuccessDescription => + "This loop invariant is maintained by the loop."; + + public override string FailureDescription => + "This loop invariant might not be maintained by the loop."; + + public override string ShortDescription => "invariant maintained"; +} diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index 0d824985f..a274d514d 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -1265,39 +1265,24 @@ private void ReportOutcome(VC.VCGen.Outcome outcome, ErrorReporterDelegate er, s if (callError != null) { tok = callError.FailingCall.tok; - msg = callError.FailingCall.ErrorData as string ?? "A precondition for this call might not hold."; + msg = callError.FailingCall.ErrorData as string ?? + callError.FailingCall.Description.FailureDescription; } else if (returnError != null) { tok = returnError.FailingReturn.tok; - msg = "A postcondition might not hold on this return path."; + msg = returnError.FailingReturn.Description.FailureDescription; } 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 - { - if (assertError.FailingAssert.ErrorMessage == null || Options.ForceBplErrors) - { + if (assertError.FailingAssert.ErrorMessage == null || Options.ForceBplErrors) { msg = assertError.FailingAssert.ErrorData as string; - } - else - { - msg = assertError.FailingAssert.ErrorMessage; - } - if (msg == null) - { - msg = "This assertion might not hold."; - } } + else { + msg = assertError.FailingAssert.ErrorMessage; + } + msg ??= assertError.FailingAssert.Description.FailureDescription; } errorInfo.AddAuxInfo(tok, msg, "Unverified check due to timeout"); @@ -1559,11 +1544,11 @@ private ErrorInformation CreateErrorInformation(Counterexample error, VC.VCGen.O if (callError.FailingRequires.ErrorMessage == null || Options.ForceBplErrors) { errorInfo = errorInformationFactory.CreateErrorInformation(callError.FailingCall.tok, - callError.FailingCall.ErrorData as string ?? "A precondition for this call might not hold.", + callError.FailingCall.ErrorData as string ?? callError.FailingCall.Description.FailureDescription, callError.RequestId, callError.OriginalRequestId, cause); errorInfo.Kind = ErrorKind.Precondition; errorInfo.AddAuxInfo(callError.FailingRequires.tok, - callError.FailingRequires.ErrorData as string ?? "This is the precondition that might not hold.", + callError.FailingRequires.ErrorData as string ?? callError.FailingRequires.Description.FailureDescription, "Related location"); } else @@ -1578,11 +1563,11 @@ private ErrorInformation CreateErrorInformation(Counterexample error, VC.VCGen.O if (returnError.FailingEnsures.ErrorMessage == null || Options.ForceBplErrors) { errorInfo = errorInformationFactory.CreateErrorInformation(returnError.FailingReturn.tok, - "A postcondition might not hold on this return path.", + returnError.FailingReturn.Description.FailureDescription, returnError.RequestId, returnError.OriginalRequestId, cause); errorInfo.Kind = ErrorKind.Postcondition; errorInfo.AddAuxInfo(returnError.FailingEnsures.tok, - returnError.FailingEnsures.ErrorData as string ?? "This is the postcondition that might not hold.", + returnError.FailingEnsures.ErrorData as string ?? returnError.FailingEnsures.Description.FailureDescription, "Related location"); } else @@ -1596,24 +1581,13 @@ private ErrorInformation CreateErrorInformation(Counterexample error, VC.VCGen.O { Debug.Assert(error is AssertCounterexample); var assertError = (AssertCounterexample)error; - if (assertError.FailingAssert is LoopInitAssertCmd) - { - errorInfo = errorInformationFactory.CreateErrorInformation(assertError.FailingAssert.tok, - "This loop invariant might not hold on entry.", - assertError.RequestId, assertError.OriginalRequestId, cause); - errorInfo.Kind = ErrorKind.InvariantEntry; - if ((assertError.FailingAssert.ErrorData as string) != null) - { - errorInfo.AddAuxInfo(assertError.FailingAssert.tok, assertError.FailingAssert.ErrorData as string, - "Related message"); - } - } - else if (assertError.FailingAssert is LoopInvMaintainedAssertCmd) + if (assertError.FailingAssert is LoopInitAssertCmd or LoopInvMaintainedAssertCmd) { errorInfo = errorInformationFactory.CreateErrorInformation(assertError.FailingAssert.tok, - "This loop invariant might not be maintained by the loop.", + assertError.FailingAssert.Description.FailureDescription, assertError.RequestId, assertError.OriginalRequestId, cause); - errorInfo.Kind = ErrorKind.InvariantMaintainance; + errorInfo.Kind = assertError.FailingAssert is LoopInitAssertCmd ? + ErrorKind.InvariantEntry : ErrorKind.InvariantMaintainance; if ((assertError.FailingAssert.ErrorData as string) != null) { errorInfo.AddAuxInfo(assertError.FailingAssert.tok, assertError.FailingAssert.ErrorData as string, @@ -1624,7 +1598,8 @@ private ErrorInformation CreateErrorInformation(Counterexample error, VC.VCGen.O { if (assertError.FailingAssert.ErrorMessage == null || Options.ForceBplErrors) { - string msg = assertError.FailingAssert.ErrorData as string ?? "This assertion might not hold."; + string msg = assertError.FailingAssert.ErrorData as string ?? + assertError.FailingAssert.Description.FailureDescription; errorInfo = errorInformationFactory.CreateErrorInformation(assertError.FailingAssert.tok, msg, assertError.RequestId, assertError.OriginalRequestId, cause); errorInfo.Kind = ErrorKind.Assertion; diff --git a/Source/VCGeneration/VCGen.cs b/Source/VCGeneration/VCGen.cs index fcf665f7b..d32312d7e 100644 --- a/Source/VCGeneration/VCGen.cs +++ b/Source/VCGeneration/VCGen.cs @@ -788,6 +788,7 @@ void ExpandAsserts(Implementation impl) (ai != null) ? new LoopInitAssertCmd(e.tok, fe(e)) : (am != null) ? new LoopInvMaintainedAssertCmd(e.tok, fe(e)) : new AssertCmd(e.tok, fe(e)); + new_c.Description = a.Description; new_c.Attributes = new QKeyValue(e.tok, "subsumption", new List() {new LiteralExpr(e.tok, BigNum.FromInt(0))}, a.Attributes); newCmds.Add(new_c); From 34b946ee90ba84f62627fab3799d7d0b8a4d25ca Mon Sep 17 00:00:00 2001 From: Shaz Qadeer Date: Wed, 9 Mar 2022 14:24:01 -0800 Subject: [PATCH 15/32] adding support for allowing calls to atomic actions from atomic actions (#509) --- Source/Concurrency/CivlCoreTypes.cs | 110 ++++++++-- Source/Concurrency/CivlTypeChecker.cs | 192 +++++++++++------- .../TransitionRelationComputation.cs | 1 + Source/Graph/Graph.cs | 6 +- Test/civl/action-no-call.bpl | 25 --- Test/civl/action-no-call.bpl.expect | 5 - Test/civl/assert-not-first-1.bpl | 22 ++ Test/civl/assert-not-first-1.bpl.expect | 2 + Test/civl/assert-not-first-2.bpl | 15 ++ Test/civl/assert-not-first-2.bpl.expect | 2 + Test/civl/assert-not-first-3.bpl | 17 ++ Test/civl/assert-not-first-3.bpl.expect | 2 + Test/civl/assert-not-first-4.bpl | 18 ++ Test/civl/assert-not-first-4.bpl.expect | 5 + .../inductive-sequentialization/NBuyer.bpl | 7 +- .../paxos/PaxosImpl.bpl | 2 +- .../paxos/PaxosSeq.bpl | 2 +- Test/civl/intro-inline-check.bpl | 16 ++ Test/civl/intro-inline-check.bpl.expect | 2 + 19 files changed, 327 insertions(+), 124 deletions(-) delete mode 100644 Test/civl/action-no-call.bpl delete mode 100644 Test/civl/action-no-call.bpl.expect create mode 100644 Test/civl/assert-not-first-1.bpl create mode 100644 Test/civl/assert-not-first-1.bpl.expect create mode 100644 Test/civl/assert-not-first-2.bpl create mode 100644 Test/civl/assert-not-first-2.bpl.expect create mode 100644 Test/civl/assert-not-first-3.bpl create mode 100644 Test/civl/assert-not-first-3.bpl.expect create mode 100644 Test/civl/assert-not-first-4.bpl create mode 100644 Test/civl/assert-not-first-4.bpl.expect create mode 100644 Test/civl/intro-inline-check.bpl create mode 100644 Test/civl/intro-inline-check.bpl.expect diff --git a/Source/Concurrency/CivlCoreTypes.cs b/Source/Concurrency/CivlCoreTypes.cs index 0bd7cf149..9f26070fa 100644 --- a/Source/Concurrency/CivlCoreTypes.cs +++ b/Source/Concurrency/CivlCoreTypes.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using Microsoft.Boogie.GraphUtil; namespace Microsoft.Boogie { @@ -85,18 +86,6 @@ protected Action(Procedure proc, Implementation impl, LayerRange layerRange) this.impl = impl; this.layerRange = layerRange; - CivlUtil.AddInlineAttribute(proc); - CivlUtil.AddInlineAttribute(impl); - - // The gate of an action is represented as asserts at the beginning of the procedure body. - gate = impl.Blocks[0].cmds.TakeWhile((c, i) => c is AssertCmd).Cast().ToList(); - // We separate the gate from the action - impl.Blocks[0].cmds.RemoveRange(0, gate.Count); - - gateUsedGlobalVars = new HashSet(VariableCollector.Collect(gate).Where(x => x is GlobalVariable)); - actionUsedGlobalVars = new HashSet(VariableCollector.Collect(impl).Where(x => x is GlobalVariable)); - modifiedGlobalVars = new HashSet(AssignedVariables().Where(x => x is GlobalVariable)); - // We usually declare the Boogie procedure and implementation of an atomic action together. // Since Boogie only stores the supplied attributes (in particular linearity) in the procedure parameters, // we copy them into the implementation parameters here. @@ -109,6 +98,11 @@ protected Action(Procedure proc, Implementation impl, LayerRange layerRange) { impl.OutParams[i].Attributes = proc.OutParams[i].Attributes; } + + gate = HoistAsserts(impl); + gateUsedGlobalVars = new HashSet(VariableCollector.Collect(gate).Where(x => x is GlobalVariable)); + actionUsedGlobalVars = new HashSet(VariableCollector.Collect(impl).Where(x => x is GlobalVariable)); + modifiedGlobalVars = new HashSet(AssignedVariables().Where(x => x is GlobalVariable)); } public bool HasAssumeCmd => impl.Blocks.Any(b => b.Cmds.Any(c => c is AssumeCmd)); @@ -120,9 +114,99 @@ protected List AssignedVariables() { cmd.AddAssignedVariables(modifiedVars); } - return modifiedVars; } + + /* + * HoistAsserts computes the weakest liberal precondition (wlp) of the body + * of impl as a collection of AssertCmd's. As a side effect, all AssertCmd's + * in any block of impl are removed. + * + * HoistAsserts assumes that the body of impl does not contain any loops or + * calls. The blocks in impl are sorted from entry to exit and processed in + * reverse order, thus ensuring that a block is processed only once the wlp + * of all its successors has been computed. + */ + private static List HoistAsserts(Implementation impl) + { + Dictionary> wlps = new Dictionary>(); + Graph dag = Program.GraphFromBlocks(impl.Blocks, false); + foreach (var block in dag.TopologicalSort()) + { + if (block.TransferCmd is ReturnCmd) + { + var wlp = HoistAsserts(block, new List()); + wlps.Add(block, wlp); + } + else if (block.TransferCmd is GotoCmd gotoCmd) + { + var wlp = + HoistAsserts(block, gotoCmd.labelTargets.SelectMany(b => wlps[b]).ToList()); + wlps.Add(block, wlp); + } + else + { + throw new cce.UnreachableException(); + } + } + return wlps[impl.Blocks[0]].Select(assertCmd => Forall(impl.LocVars.Union(impl.OutParams), assertCmd)).ToList(); + } + + private static List HoistAsserts(Block block, List postconditions) + { + for (int i = block.Cmds.Count - 1; i >= 0; i--) + { + var cmd = block.Cmds[i]; + if (cmd is AssertCmd assertCmd) + { + postconditions.Add(assertCmd); + } + else if (cmd is AssumeCmd assumeCmd) + { + postconditions = postconditions + .Select(assertCmd => new AssertCmd(assertCmd.tok, Expr.Imp(assumeCmd.Expr, assertCmd.Expr))).ToList(); + } + else if (cmd is AssignCmd assignCmd) + { + var varToExpr = new Dictionary(); + var simpleAssignCmd = assignCmd.AsSimpleAssignCmd; + for (var j = 0; j < simpleAssignCmd.Lhss.Count; j++) + { + var lhs = simpleAssignCmd.Lhss[j]; + var rhs = simpleAssignCmd.Rhss[j]; + varToExpr.Add(lhs.DeepAssignedVariable, rhs); + } + postconditions = postconditions.Select(assertCmd => + new AssertCmd(assertCmd.tok, SubstitutionHelper.Apply(varToExpr, assertCmd.Expr))).ToList(); + } + else if (cmd is HavocCmd havocCmd) + { + postconditions = postconditions.Select(assertCmd => Forall(havocCmd.Vars.Select(ie => ie.Decl), assertCmd)) + .ToList(); + } + else + { + throw new cce.UnreachableException(); + } + } + block.Cmds.RemoveAll(cmd => cmd is AssertCmd); + return postconditions; + } + + private static AssertCmd Forall(IEnumerable vars, AssertCmd assertCmd) + { + var freeObjects = new GSet(); + assertCmd.Expr.ComputeFreeVariables(freeObjects); + var quantifiedVars = freeObjects.OfType().Intersect(vars); + if (quantifiedVars.Count() == 0) + { + return assertCmd; + } + var varMapping = quantifiedVars.ToDictionary(v => v, + v => (Variable) VarHelper.BoundVariable(v.Name, v.TypedIdent.Type)); + return new AssertCmd(assertCmd.tok, + ExprHelper.ForallExpr(varMapping.Values.ToList(), SubstitutionHelper.Apply(varMapping, assertCmd.Expr))); + } } public class IntroductionAction : Action diff --git a/Source/Concurrency/CivlTypeChecker.cs b/Source/Concurrency/CivlTypeChecker.cs index be050bcc8..769d55bb9 100644 --- a/Source/Concurrency/CivlTypeChecker.cs +++ b/Source/Concurrency/CivlTypeChecker.cs @@ -144,8 +144,13 @@ public void TypeCheck() TypeCheckGlobalVariables(); TypeCheckLemmaProcedures(); TypeCheckYieldInvariants(); - - TypeCheckActionDecls(); + TypeCheckActions(); + + if (checkingContext.ErrorCount > 0) + { + return; + } + TypeCheckPendingAsyncMachinery(); if (checkingContext.ErrorCount > 0) @@ -154,6 +159,7 @@ public void TypeCheck() } linearTypeChecker.TypeCheck(); + if (checkingContext.ErrorCount > 0) { return; @@ -167,8 +173,7 @@ public void TypeCheck() { return; } - - TypeCheckActionImpls(); + TypeCheckYieldingProcedureImpls(); if (checkingContext.ErrorCount > 0) @@ -250,60 +255,49 @@ private void TypeCheckGlobalVariables() } } - private void TypeCheckActionDecls() + private void TypeCheckActions() { // Atomic action: // * no {:yield} // * {:right}, {:left}, {:both}, {:atomic}, {:intro}, {:IS_invariant}, {:IS_abstraction} // * layer range - foreach (var proc in program.Procedures.Where(IsAction)) + var actionProcs = program.Procedures.Where(IsAction).ToHashSet(); + var actionProcToImpl = new Dictionary(); + foreach (var impl in program.Implementations) + { + if (actionProcToImpl.ContainsKey(impl.Proc)) + { + Error(impl.Proc, "More then one action specification provided"); + continue; + } + if (actionProcs.Contains(impl.Proc)) + { + actionProcToImpl.Add(impl.Proc, impl); + } + } + var actionProcToLayerRange = new Dictionary(); + foreach (var proc in actionProcs) { - LayerRange layerRange = ToLayerRange(FindLayers(proc.Attributes), proc); if (proc.Requires.Count + proc.Ensures.Count > 0) { Error(proc, "Action cannot have preconditions or postconditions"); } - - var actionImpls = program.Implementations.Where(i => i.Name == proc.Name).ToList(); - if (actionImpls.Count == 0) + if (!actionProcToImpl.ContainsKey(proc)) { Error(proc, "Action specification missing"); continue; } - if (actionImpls.Count > 1) - { - Error(proc, "More then one action specification provided"); - continue; - } - - Implementation impl = actionImpls[0]; + Implementation impl = actionProcToImpl[proc]; impl.PruneUnreachableBlocks(Options); Graph cfg = Program.GraphFromImpl(impl); - if (!Graph.Acyclic(cfg, impl.Blocks[0])) + if (!Graph.Acyclic(cfg)) { Error(proc, "Action specification cannot have loops"); continue; } - - bool inGate = true; - foreach (var cmd in impl.Blocks[0].cmds) - { - if (inGate && !(cmd is AssertCmd)) - { - inGate = false; - } - else if (!inGate && cmd is AssertCmd) - { - Error(cmd, "Assert is only allowed in the gate of an action"); - } - } - - foreach (var cmd in impl.Blocks.Skip(1).SelectMany(b => b.cmds).OfType()) - { - Error(cmd, "Assert is only allowed in the gate of an action"); - } - + LayerRange layerRange = ToLayerRange(FindLayers(proc.Attributes), proc); + actionProcToLayerRange.Add(proc, layerRange); if (proc.HasAttribute(CivlAttributes.INTRO)) { if (GetMoverType(proc) != null) @@ -318,10 +312,55 @@ private void TypeCheckActionDecls() { Error(proc, "Introduction actions can modify a global variable only on its introduction layer"); } - else - { - procToIntroductionAction[proc] = new IntroductionAction(proc, impl, layerRange); - } + } + } + + var actionImplVisitor = new ActionImplVisitor(this, actionProcToLayerRange); + foreach (var impl in actionProcToImpl.Values) + { + actionImplVisitor.VisitImplementation(impl); + } + if (!actionImplVisitor.IsCallGraphAcyclic()) + { + Error(program, "Call graph over atomic actions must be acyclic"); + } + + if (checkingContext.ErrorCount > 0) + { + return; + } + + CivlUtil.AddInlineAttribute(SkipAtomicAction.proc); + CivlUtil.AddInlineAttribute(SkipAtomicAction.impl); + actionProcs.Iter(proc => + { + CivlUtil.AddInlineAttribute(proc); + CivlUtil.AddInlineAttribute(actionProcToImpl[proc]); + }); + actionProcs.Iter(proc => + { + var impl = actionProcToImpl[proc]; + impl.OriginalBlocks = impl.Blocks; + impl.OriginalLocVars = impl.LocVars; + }); + actionProcs.Iter(proc => + { + Inliner.ProcessImplementation(Options, program, actionProcToImpl[proc]); + }); + actionProcs.Iter(proc => + { + var impl = actionProcToImpl[proc]; + impl.OriginalBlocks = null; + impl.OriginalLocVars = null; + }); + + foreach (var proc in actionProcs) + { + Implementation impl = actionProcToImpl[proc]; + LayerRange layerRange = actionProcToLayerRange[proc]; + if (proc.HasAttribute(CivlAttributes.INTRO)) + { + procToIntroductionAction[proc] = new IntroductionAction(proc, impl, layerRange); } else { @@ -341,16 +380,7 @@ private void TypeCheckActionDecls() } } } - - private void TypeCheckActionImpls() - { - ActionVisitor actionVisitor = new ActionVisitor(this); - foreach (var action in Enumerable.Concat(AllAtomicActions, procToIntroductionAction.Values)) - { - actionVisitor.VisitAction(action); - } - } - + private void TypeCheckInductiveSequentializations() { foreach (var action in procToAtomicAction.Values) @@ -1359,25 +1389,18 @@ public bool Require(bool condition, Absy node, string message) #endregion - private class ActionVisitor : ReadOnlyVisitor + private class ActionImplVisitor : ReadOnlyVisitor { private CivlTypeChecker civlTypeChecker; - private Action action; + private Dictionary actionProcToLayerRange; + private Graph callGraph; + private Procedure proc; - public ActionVisitor(CivlTypeChecker civlTypeChecker) + public ActionImplVisitor(CivlTypeChecker civlTypeChecker, Dictionary actionProcToLayerRange) { this.civlTypeChecker = civlTypeChecker; - } - - internal void VisitAction(Action action) - { - this.action = action; - foreach (var g in action.gate) - { - VisitAssertCmd(g); - } - - VisitImplementation(action.impl); + this.actionProcToLayerRange = actionProcToLayerRange; + this.callGraph = new Graph(); } public override Procedure VisitProcedure(Procedure node) @@ -1386,14 +1409,20 @@ public override Procedure VisitProcedure(Procedure node) return node; } + public override Implementation VisitImplementation(Implementation node) + { + this.proc = node.Proc; + return base.VisitImplementation(node); + } + public override Expr VisitIdentifierExpr(IdentifierExpr node) { + var layerRange = actionProcToLayerRange[proc]; if (node.Decl is GlobalVariable) { var globalVarLayerRange = civlTypeChecker.GlobalVariableLayerRange(node.Decl); - if (!action.layerRange.Subset(globalVarLayerRange) || - (globalVarLayerRange.lowerLayerNum == action.layerRange.lowerLayerNum && - action is AtomicAction)) + if (!layerRange.Subset(globalVarLayerRange) || + globalVarLayerRange.lowerLayerNum == layerRange.lowerLayerNum && !proc.HasAttribute(CivlAttributes.INTRO)) // a global variable introduced at layer n is visible to an atomic action only at layer n+1 or higher // thus, a global variable with layer range [n,n] is not accessible by an atomic action // however, an introduction action may access the global variable at layer n @@ -1402,17 +1431,38 @@ public override Expr VisitIdentifierExpr(IdentifierExpr node) node.Decl.Name); } } - return base.VisitIdentifierExpr(node); } public override Cmd VisitCallCmd(CallCmd node) { - civlTypeChecker.Error(node, "Call command not allowed inside an atomic action"); + if (!actionProcToLayerRange.ContainsKey(node.Proc)) + { + civlTypeChecker.Error(node, "An atomic action can only call other atomic actions"); + } + else if (!actionProcToLayerRange[proc].Subset(actionProcToLayerRange[node.Proc])) + { + civlTypeChecker.Error(node, "Caller layer range must be subset of callee layer range"); + } + else if (actionProcToLayerRange[proc].lowerLayerNum == actionProcToLayerRange[node.Proc].lowerLayerNum && + node.Proc.HasAttribute(CivlAttributes.INTRO) && + !proc.HasAttribute(CivlAttributes.INTRO)) + { + civlTypeChecker.Error(node, "Lower layer of caller must be greater that lower layer of callee"); + } + else + { + callGraph.AddEdge(proc, node.Proc); + } return base.VisitCallCmd(node); } - } + public bool IsCallGraphAcyclic() + { + return Graph.Acyclic(callGraph); + } + } + private class LemmaProcedureVisitor : ReadOnlyVisitor { private CivlTypeChecker civlTypeChecker; diff --git a/Source/Concurrency/TransitionRelationComputation.cs b/Source/Concurrency/TransitionRelationComputation.cs index 1ea756d02..c1ade36b6 100644 --- a/Source/Concurrency/TransitionRelationComputation.cs +++ b/Source/Concurrency/TransitionRelationComputation.cs @@ -362,6 +362,7 @@ private void EliminateIntermediateVariables() { TryElimination(Enumerable.Empty()); TryElimination(trc.allLocVars.Select(v => varCopies[v][0])); + TryElimination(trc.allLocVars.Where(v => varCopies[v].Count > 1).Select(v => varCopies[v][1])); if (trc.ignorePostState) { diff --git a/Source/Graph/Graph.cs b/Source/Graph/Graph.cs index b58a05ee6..c22a646f6 100644 --- a/Source/Graph/Graph.cs +++ b/Source/Graph/Graph.cs @@ -842,9 +842,9 @@ private Tuple> TopSort() return new Tuple>(true, S); } - public static bool Acyclic(Graph g, Node source) + public static bool Acyclic(Graph g) { - g.TarjanTopSort(out var acyclic, out var sortedList); + g.TarjanTopSort(out var acyclic, out var _); return acyclic; } @@ -997,7 +997,7 @@ static ReducibleResult ComputeReducible(Graph g, } Graph withoutBackEdges = new Graph(nonBackEdges); - if (!Acyclic(withoutBackEdges, source)) + if (!Acyclic(withoutBackEdges)) { return new ReducibleResult(false, new HashSet(), diff --git a/Test/civl/action-no-call.bpl b/Test/civl/action-no-call.bpl deleted file mode 100644 index 7908217ba..000000000 --- a/Test/civl/action-no-call.bpl +++ /dev/null @@ -1,25 +0,0 @@ -// RUN: %parallel-boogie "%s" > "%t" -// RUN: %diff "%s.expect" "%t" - -procedure {:atomic}{:layer 2} foo () {} - -type {:pending_async}{:datatype} PA; - -procedure {:atomic}{:layer 2} atomic () -{ - call foo(); -} - -procedure {:intro}{:layer 2} intro () -{ - call foo(); -} - -procedure {:IS_invariant}{:layer 2} IS_invariant () returns ({:pending_async} PAs:[PA]int) -{ - call foo(); -} -procedure {:IS_abstraction}{:layer 2} IS_abstraction () -{ - call foo(); -} diff --git a/Test/civl/action-no-call.bpl.expect b/Test/civl/action-no-call.bpl.expect deleted file mode 100644 index f9019f2c1..000000000 --- a/Test/civl/action-no-call.bpl.expect +++ /dev/null @@ -1,5 +0,0 @@ -action-no-call.bpl(10,2): Error: Call command not allowed inside an atomic action -action-no-call.bpl(20,2): Error: Call command not allowed inside an atomic action -action-no-call.bpl(24,2): Error: Call command not allowed inside an atomic action -action-no-call.bpl(15,2): Error: Call command not allowed inside an atomic action -4 type checking errors detected in action-no-call.bpl diff --git a/Test/civl/assert-not-first-1.bpl b/Test/civl/assert-not-first-1.bpl new file mode 100644 index 000000000..faa7e7794 --- /dev/null +++ b/Test/civl/assert-not-first-1.bpl @@ -0,0 +1,22 @@ +// RUN: %parallel-boogie "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +procedure {:yields} {:layer 1} P(b: bool, i: int) returns (o: int) +requires {:layer 1} b ==> i >= 0; +{ + call o := bar(b, i); +} + +procedure {:atomic} {:layer 1} BAR(b: bool, i: int) returns (o: int) { + call o := FOO(b, i); +} + +procedure {:atomic} {:layer 1} FOO(b: bool, i: int) returns (o: int) { + if (b) { + o := i + 1; + assert o > 0; + } +} + +procedure {:yields} {:layer 0} {:refines "BAR"} bar(b: bool, i: int) returns (o: int); +procedure {:yields} {:layer 0} {:refines "FOO"} foo(b: bool, i: int) returns (o: int); diff --git a/Test/civl/assert-not-first-1.bpl.expect b/Test/civl/assert-not-first-1.bpl.expect new file mode 100644 index 000000000..37fad75c9 --- /dev/null +++ b/Test/civl/assert-not-first-1.bpl.expect @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 1 verified, 0 errors diff --git a/Test/civl/assert-not-first-2.bpl b/Test/civl/assert-not-first-2.bpl new file mode 100644 index 000000000..a42330905 --- /dev/null +++ b/Test/civl/assert-not-first-2.bpl @@ -0,0 +1,15 @@ +// RUN: %parallel-boogie "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +procedure {:yields} {:layer 1} P(arr: [int]int) +requires {:layer 1} (forall x: int :: arr[x] == 1); +{ + call foo(arr); +} + +procedure {:atomic} {:layer 1} FOO(arr: [int]int) { + var x: int; + assert arr[x] == 1; +} + +procedure {:yields} {:layer 0} {:refines "FOO"} foo(arr: [int]int); diff --git a/Test/civl/assert-not-first-2.bpl.expect b/Test/civl/assert-not-first-2.bpl.expect new file mode 100644 index 000000000..37fad75c9 --- /dev/null +++ b/Test/civl/assert-not-first-2.bpl.expect @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 1 verified, 0 errors diff --git a/Test/civl/assert-not-first-3.bpl b/Test/civl/assert-not-first-3.bpl new file mode 100644 index 000000000..cec28dafb --- /dev/null +++ b/Test/civl/assert-not-first-3.bpl @@ -0,0 +1,17 @@ +// RUN: %parallel-boogie "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +procedure {:yields} {:layer 1} P(arr: [int]int, i: int) +requires {:layer 1} (forall x: int :: arr[x] == 1); +{ + call foo(arr, i); +} + +procedure {:atomic} {:layer 1} FOO(arr: [int]int, i: int) { + var x: int; + x := i; + havoc x; + assert arr[x] == 1; +} + +procedure {:yields} {:layer 0} {:refines "FOO"} foo(arr: [int]int, i: int); diff --git a/Test/civl/assert-not-first-3.bpl.expect b/Test/civl/assert-not-first-3.bpl.expect new file mode 100644 index 000000000..37fad75c9 --- /dev/null +++ b/Test/civl/assert-not-first-3.bpl.expect @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 1 verified, 0 errors diff --git a/Test/civl/assert-not-first-4.bpl b/Test/civl/assert-not-first-4.bpl new file mode 100644 index 000000000..6fa8ecc90 --- /dev/null +++ b/Test/civl/assert-not-first-4.bpl @@ -0,0 +1,18 @@ +// RUN: %parallel-boogie "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// Verification of P is expected to fail +procedure {:yields} {:layer 1} P(arr: [int]int, i: int) +requires {:layer 1} arr[i] == 1; +{ + call foo(arr, i); +} + +procedure {:atomic} {:layer 1} FOO(arr: [int]int, i: int) { + var x: int; + x := i; + havoc x; + assert arr[x] == 1; +} + +procedure {:yields} {:layer 0} {:refines "FOO"} foo(arr: [int]int, i: int); diff --git a/Test/civl/assert-not-first-4.bpl.expect b/Test/civl/assert-not-first-4.bpl.expect new file mode 100644 index 000000000..a4dfb4b1c --- /dev/null +++ b/Test/civl/assert-not-first-4.bpl.expect @@ -0,0 +1,5 @@ +assert-not-first-4.bpl(15,5): Error: This gate of FOO might not hold. +Execution trace: + assert-not-first-4.bpl(8,5): anon0 + +Boogie program verifier finished with 0 verified, 1 error diff --git a/Test/civl/inductive-sequentialization/NBuyer.bpl b/Test/civl/inductive-sequentialization/NBuyer.bpl index 70e9d311b..749a8d356 100644 --- a/Test/civl/inductive-sequentialization/NBuyer.bpl +++ b/Test/civl/inductive-sequentialization/NBuyer.bpl @@ -131,6 +131,7 @@ modifies QuoteCH, RemCH, DecCH, contribution; havoc contribution; + assume {:add_to_pool "INV3", 0, 1, k, k+1, n} true; if (*) { QuoteCH := (lambda i:int :: (lambda q:int :: if buyerID(i) && q == price then 1 else 0)); @@ -139,9 +140,7 @@ modifies QuoteCH, RemCH, DecCH, contribution; } else if (*) { - assume - {:add_to_pool "INV3", 1, k, k+1} - 1 <= k && k < n && 0 <= sum(contribution, 1, k) && sum(contribution, 1, k) <= price; + assume 1 <= k && k < n && 0 <= sum(contribution, 1, k) && sum(contribution, 1, k) <= price; QuoteCH := (lambda i:int :: (lambda q:int :: if buyerID(i) && i > k && q == price then 1 else 0)); RemCH := (lambda i:int :: (lambda r:int :: if i == k+1 && r == price - sum(contribution, 1, k) then 1 else 0)); PAs := MapAddPA3(SellerFinish(0), LastBuyer(n), (lambda pa:PA :: if is#MiddleBuyer(pa) && middleBuyerID(pid#MiddleBuyer(pa)) && pid#MiddleBuyer(pa) > k then 1 else 0)); @@ -197,7 +196,6 @@ modifies QuoteCH, RemCH, contribution; assert (forall r:int :: RemCH[pid][r] > 0 ==> 0 <= r && r <= price); assert (forall r:int :: RemCH[pid][r] > 0 ==> r == price - sum(contribution, 1, pid - 1)); assert RemCH[pid][price - sum(contribution, 1, pid - 1)] > 0; - assert (exists r:int :: RemCH[pid][r] > 0); assert DecCH == (lambda b:bool :: 0); assert (forall i:int :: i < pid ==> QuoteCH[i] == (lambda r:int :: 0)); assert (forall i:int :: i != pid ==> RemCH[i] == (lambda r:int :: 0)); @@ -226,7 +224,6 @@ modifies QuoteCH, RemCH, DecCH, contribution; assert (forall r:int :: RemCH[pid][r] > 0 ==> 0 <= r && r <= price); assert (forall r:int :: RemCH[pid][r] > 0 ==> r == price - sum(contribution, 1, pid - 1)); assert RemCH[n][price - sum(contribution, 1, n-1)] > 0; - assert (exists r:int :: RemCH[pid][r] > 0); assert DecCH == (lambda b:bool :: 0); assert (forall i:int :: i < pid ==> QuoteCH[i] == (lambda r:int :: 0)); diff --git a/Test/civl/inductive-sequentialization/paxos/PaxosImpl.bpl b/Test/civl/inductive-sequentialization/paxos/PaxosImpl.bpl index 32f3a0fa8..efaec5932 100644 --- a/Test/civl/inductive-sequentialization/paxos/PaxosImpl.bpl +++ b/Test/civl/inductive-sequentialization/paxos/PaxosImpl.bpl @@ -4,9 +4,9 @@ modifies pendingAsyncs; { var {:pool "NumRounds"} numRounds: int; assert - {:add_to_pool "Round", 0, numRounds} Init(rs, joinedNodes, voteInfo, decision, pendingAsyncs); assume + {:add_to_pool "Round", 0, numRounds} {:add_to_pool "NumRounds", numRounds} 0 <= numRounds; PAs := (lambda pa: PA :: if is#A_StartRound(pa) && round#A_StartRound(pa) == round_lin#A_StartRound(pa) && Round(round#A_StartRound(pa)) && round#A_StartRound(pa) <= numRounds then 1 else 0); diff --git a/Test/civl/inductive-sequentialization/paxos/PaxosSeq.bpl b/Test/civl/inductive-sequentialization/paxos/PaxosSeq.bpl index 172607b08..a7dd67711 100644 --- a/Test/civl/inductive-sequentialization/paxos/PaxosSeq.bpl +++ b/Test/civl/inductive-sequentialization/paxos/PaxosSeq.bpl @@ -20,9 +20,9 @@ modifies pendingAsyncs; { var {:pool "NumRounds"} numRounds: int; assert - {:add_to_pool "Round", 0, numRounds} Init(rs, joinedNodes, voteInfo, decision, pendingAsyncs); assume + {:add_to_pool "Round", 0, numRounds} {:add_to_pool "NumRounds", numRounds} 0 <= numRounds; PAs := (lambda pa: PA :: if is#A_StartRound(pa) && round#A_StartRound(pa) == round_lin#A_StartRound(pa) && Round(round#A_StartRound(pa)) && round#A_StartRound(pa) <= numRounds then 1 else 0); diff --git a/Test/civl/intro-inline-check.bpl b/Test/civl/intro-inline-check.bpl new file mode 100644 index 000000000..cab1b7a1d --- /dev/null +++ b/Test/civl/intro-inline-check.bpl @@ -0,0 +1,16 @@ +// RUN: %parallel-boogie "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +var {:layer 1} g: int; + +procedure {:atomic}{:layer 1} A() +modifies g; +{ + call I(); +} + +procedure {:intro}{:layer 1} I() +modifies g; +{ + g := g + 1; +} diff --git a/Test/civl/intro-inline-check.bpl.expect b/Test/civl/intro-inline-check.bpl.expect new file mode 100644 index 000000000..16a342449 --- /dev/null +++ b/Test/civl/intro-inline-check.bpl.expect @@ -0,0 +1,2 @@ +intro-inline-check.bpl(9,4): Error: Lower layer of caller must be greater that lower layer of callee +1 type checking errors detected in intro-inline-check.bpl From b947db5b32f5077357d1015f1351f8e06421adb9 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Thu, 10 Mar 2022 11:59:50 +0100 Subject: [PATCH 16/32] Turn on parallel-boogie in related tests --- Test/snapshots/runtest.snapshot | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Test/snapshots/runtest.snapshot b/Test/snapshots/runtest.snapshot index ac62914f9..6e78db0f8 100644 --- a/Test/snapshots/runtest.snapshot +++ b/Test/snapshots/runtest.snapshot @@ -1,5 +1,4 @@ -// Using boogie instead of parallel-boogie here since parallel Boogie doesn't work well with -traceCaching -// RUN: %boogie -errorTrace:0 -traceCaching:1 -verifySnapshots:2 -verifySeparately 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: %boogie -errorTrace:0 -traceCaching:1 -verifySnapshots:3 -verifySeparately Snapshots41.bpl >> "%t" +// RUN: %parallel-boogie -errorTrace:0 -traceCaching:1 -verifySnapshots:2 -verifySeparately 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: %parallel-boogie -errorTrace:0 -traceCaching:1 -verifySnapshots:3 -verifySeparately Snapshots41.bpl >> "%t" // RUN: %diff "%s.expect" "%t" // UNSUPPORTED: batch_mode From a76edf5ca7e4ebc37bedb86037a58cfd68eed669 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Thu, 10 Mar 2022 12:22:55 +0100 Subject: [PATCH 17/32] Ordered the output of -traceCaching when using parallel Boogie --- Source/ExecutionEngine/ExecutionEngine.cs | 19 ++-- Source/ExecutionEngine/VerificationResult.cs | 19 ++-- Source/Houdini/Checker.cs | 5 +- Source/Houdini/ConcurrentHoudini.cs | 5 +- Source/Houdini/Houdini.cs | 8 +- Source/Houdini/StagedHoudini.cs | 13 ++- Source/VCGeneration/ConditionGeneration.cs | 80 ++++++++------- Source/VCGeneration/Split.cs | 4 +- Source/VCGeneration/SplitAndVerifyWorker.cs | 26 ++--- Source/VCGeneration/StratifiedVC.cs | 100 +++++++++---------- Source/VCGeneration/VCGen.cs | 96 ++++++++++-------- 11 files changed, 199 insertions(+), 176 deletions(-) diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index a274d514d..3a50a7350 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -971,7 +971,13 @@ private void VerifyImplementation(Program program, PipelineStatistics stats, Err Cache.Insert(impl, verificationResult); } } - verificationResult.Emit(this, stats, er, index, outputCollector, output, impl, wasCached); + verificationResult.Emit(this, stats, er, output, impl, wasCached); + + outputCollector.Add(index, output); + outputCollector.WriteMoreOutput(); + if (verificationResult.Outcome == VCGen.Outcome.Errors || Options.Trace) { + Console.Out.Flush(); + } } private VerificationResult GetCachedVerificationResult(Implementation impl, TextWriter output) @@ -1007,11 +1013,10 @@ private VerificationResult VerifyImplementationWithoutCaching(Program program, P try { var cancellationToken = RequestIdToCancellationTokenSource[requestId].Token; verificationResult.Outcome = - vcgen.VerifyImplementation(impl, out verificationResult.Errors, + vcgen.VerifyImplementation(new ImplementationRun(impl, output), out verificationResult.Errors, out verificationResult.VCResults, requestId, cancellationToken); if (Options.ExtractLoops && verificationResult.Errors != null) { - var vcg = vcgen as VCGen; - if (vcg != null) { + if (vcgen is VCGen vcg) { for (int i = 0; i < verificationResult.Errors.Count; i++) { verificationResult.Errors[i] = vcg.extractLoopTrace(verificationResult.Errors[i], impl.Name, program, extractLoopMappingInfo); @@ -1023,13 +1028,13 @@ private VerificationResult VerifyImplementationWithoutCaching(Program program, P var errorInfo = errorInformationFactory.CreateErrorInformation(impl.tok, String.Format("{0} (encountered in implementation {1}).", e.Message, impl.Name), requestId, "Error"); errorInfo.ImplementationName = impl.Name; - printer.WriteErrorInformation(errorInfo, output); if (er != null) { lock (er) { er(errorInfo); } } + verificationResult.ErrorBeforeVerification = errorInfo; verificationResult.Errors = null; verificationResult.Outcome = VCGen.Outcome.Inconclusive; } @@ -1082,7 +1087,7 @@ private PipelineOutcome RunHoudini(Program program, PipelineStatistics stats, Er } Houdini.HoudiniSession.HoudiniStatistics houdiniStats = new Houdini.HoudiniSession.HoudiniStatistics(); - Houdini.Houdini houdini = new Houdini.Houdini(Options, program, houdiniStats); + Houdini.Houdini houdini = new Houdini.Houdini(Console.Out, Options, program, houdiniStats); Houdini.HoudiniOutcome outcome = houdini.PerformHoudiniInference(); houdini.Close(); @@ -1136,7 +1141,7 @@ public Program ProgramFromFile(string filename) private PipelineOutcome RunStagedHoudini(Program program, PipelineStatistics stats, ErrorReporterDelegate er) { Houdini.HoudiniSession.HoudiniStatistics houdiniStats = new Houdini.HoudiniSession.HoudiniStatistics(); - var stagedHoudini = new Houdini.StagedHoudini(Options, program, houdiniStats, ProgramFromFile); + var stagedHoudini = new Houdini.StagedHoudini(Console.Out, Options, program, houdiniStats, ProgramFromFile); Houdini.HoudiniOutcome outcome = stagedHoudini.PerformStagedHoudiniInference(); if (Options.PrintAssignment) diff --git a/Source/ExecutionEngine/VerificationResult.cs b/Source/ExecutionEngine/VerificationResult.cs index ef165ec78..6cfe57e3c 100644 --- a/Source/ExecutionEngine/VerificationResult.cs +++ b/Source/ExecutionEngine/VerificationResult.cs @@ -33,7 +33,8 @@ public int ProofObligationCount public List Errors; public List VCResults; - public ISet AssertionChecksums { get; private set; } + public ISet AssertionChecksums { get; } + public ErrorInformation ErrorBeforeVerification { get; set; } public VerificationResult(string requestId, Implementation implementation, string programId = null) { @@ -47,9 +48,13 @@ public VerificationResult(string requestId, Implementation implementation, strin MessageIfVerifies = implementation.FindStringAttribute("msg_if_verifies"); } - public void Emit(ExecutionEngine engine, PipelineStatistics stats, ErrorReporterDelegate er, int index, - OutputCollector outputCollector, StringWriter output, Implementation impl, bool wasCached) + public void Emit(ExecutionEngine engine, PipelineStatistics stats, ErrorReporterDelegate er, StringWriter output, + Implementation impl, bool wasCached) { + if (ErrorBeforeVerification != null) { + ExecutionEngine.printer.WriteErrorInformation(ErrorBeforeVerification, output); + } + engine.ProcessOutcome(Outcome, Errors, engine.TimeIndication(this), stats, output, impl.GetTimeLimit(engine.Options), er, ImplementationName, ImplementationToken, @@ -71,13 +76,5 @@ public void Emit(ExecutionEngine engine, PipelineStatistics stats, ErrorReporter ResourceCount); } } - - outputCollector.Add(index, output); - - outputCollector.WriteMoreOutput(); - - if (Outcome == VCGen.Outcome.Errors || engine.Options.Trace) { - Console.Out.Flush(); - } } } \ No newline at end of file diff --git a/Source/Houdini/Checker.cs b/Source/Houdini/Checker.cs index 40780e97a..3795790cd 100644 --- a/Source/Houdini/Checker.cs +++ b/Source/Houdini/Checker.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics.Contracts; using System.Collections.Generic; +using System.IO; using Microsoft.Boogie.VCExprAST; using Microsoft.BaseTypes; using VC; @@ -152,7 +153,7 @@ public bool InUnsatCore(Variable constant) return false; } - public HoudiniSession(Houdini houdini, VCGen vcgen, ProverInterface proverInterface, Program program, + public HoudiniSession(TextWriter traceWriter, Houdini houdini, VCGen vcgen, ProverInterface proverInterface, Program program, Implementation impl, HoudiniStatistics stats, int taskID = -1) { this.descriptiveName = impl.Name; @@ -162,7 +163,7 @@ public HoudiniSession(Houdini houdini, VCGen vcgen, ProverInterface proverInterf collector.OnProgress?.Invoke("HdnVCGen", 0, 0, 0.0); vcgen.ConvertCFG2DAG(impl, taskID: taskID); - var gotoCmdOrigins = vcgen.PassifyImpl(impl, out var mvInfo); + var gotoCmdOrigins = vcgen.PassifyImpl(new ImplementationRun(impl, traceWriter), out var mvInfo); ExistentialConstantCollector.CollectHoudiniConstants(houdini, impl, out var ecollector); this.houdiniAssertConstants = ecollector.houdiniAssertConstants; diff --git a/Source/Houdini/ConcurrentHoudini.cs b/Source/Houdini/ConcurrentHoudini.cs index 6184eedc0..c4db35284 100644 --- a/Source/Houdini/ConcurrentHoudini.cs +++ b/Source/Houdini/ConcurrentHoudini.cs @@ -3,6 +3,7 @@ using System.Collections.Concurrent; using System.Diagnostics; using System.Diagnostics.Contracts; +using System.IO; using System.Linq; namespace Microsoft.Boogie.Houdini @@ -18,14 +19,14 @@ public static ConcurrentDictionary RefutedSharedAnnot get { return refutedSharedAnnotations; } } - public ConcurrentHoudini(HoudiniOptions options, int taskId, Program program, HoudiniSession.HoudiniStatistics stats, + public ConcurrentHoudini(TextWriter traceWriter, HoudiniOptions options, int taskId, Program program, HoudiniSession.HoudiniStatistics stats, string cexTraceFile = "houdiniCexTrace.txt") : base(options) { Contract.Assert(taskId >= 0); this.program = program; this.cexTraceFile = cexTraceFile; this.taskID = taskId; - Initialize(program, stats); + Initialize(traceWriter, program, stats); } static ConcurrentHoudini() diff --git a/Source/Houdini/Houdini.cs b/Source/Houdini/Houdini.cs index 02b53c24e..d06e9e3bc 100644 --- a/Source/Houdini/Houdini.cs +++ b/Source/Houdini/Houdini.cs @@ -416,15 +416,15 @@ protected Houdini(HoudiniOptions options) this.Options = options; } - public Houdini(HoudiniOptions options, Program program, HoudiniSession.HoudiniStatistics stats, string cexTraceFile = "houdiniCexTrace.txt") + public Houdini(TextWriter traceWriter, HoudiniOptions options, Program program, HoudiniSession.HoudiniStatistics stats, string cexTraceFile = "houdiniCexTrace.txt") { this.Options = options; this.program = program; this.cexTraceFile = cexTraceFile; - Initialize(program, stats); + Initialize(traceWriter, program, stats); } - protected void Initialize(Program program, HoudiniSession.HoudiniStatistics stats) + protected void Initialize(TextWriter traceWriter, Program program, HoudiniSession.HoudiniStatistics stats) { if (Options.Trace) { @@ -490,7 +490,7 @@ protected void Initialize(Program program, HoudiniSession.HoudiniStatistics stat } HoudiniSession session = - new HoudiniSession(this, vcgen, proverInterface, program, impl, stats, taskID: GetTaskID()); + new HoudiniSession(traceWriter, this, vcgen, proverInterface, program, impl, stats, taskID: GetTaskID()); houdiniSessions.Add(impl, session); } catch (VCGenException) diff --git a/Source/Houdini/StagedHoudini.cs b/Source/Houdini/StagedHoudini.cs index 1661d5571..e72e6e383 100644 --- a/Source/Houdini/StagedHoudini.cs +++ b/Source/Houdini/StagedHoudini.cs @@ -2,14 +2,15 @@ using System.Collections.Generic; using System.Linq; using System.Diagnostics; +using System.IO; using System.Threading.Tasks; using System.Threading; using Microsoft.Boogie.GraphUtil; namespace Microsoft.Boogie.Houdini { - public class StagedHoudini - { + public class StagedHoudini { + private TextWriter traceWriter; private readonly HoudiniOptions options; private Program program; private HoudiniSession.HoudiniStatistics houdiniStats; @@ -21,13 +22,15 @@ public class StagedHoudini private const string tempFilename = "__stagedHoudiniTemp.bpl"; - public StagedHoudini(HoudiniOptions options, Program program, HoudiniSession.HoudiniStatistics houdiniStats, + public StagedHoudini(TextWriter traceWriter, HoudiniOptions options, Program program, + HoudiniSession.HoudiniStatistics houdiniStats, Func ProgramFromFile) { this.options = options; this.program = program; this.houdiniStats = houdiniStats; this.ProgramFromFile = ProgramFromFile; + this.traceWriter = traceWriter; this.houdiniInstances = new List[options.StagedHoudiniThreads]; for (int i = 0; i < options.StagedHoudiniThreads; i++) { @@ -149,7 +152,7 @@ public HoudiniOutcome PerformStagedHoudiniInference() { if (NoStages()) { - Houdini houdini = new Houdini(options, program, houdiniStats); + Houdini houdini = new Houdini(traceWriter, options, program, houdiniStats); return houdini.PerformHoudiniInference(); } @@ -233,7 +236,7 @@ private void ExecuteStage(ScheduledStage s) if (!h.Any()) { - h.Add(new Houdini(options, ProgramFromFile(tempFilename), new HoudiniSession.HoudiniStatistics(), + h.Add(new Houdini(traceWriter, options, ProgramFromFile(tempFilename), new HoudiniSession.HoudiniStatistics(), "houdiniCexTrace_" + s.GetId() + ".txt")); } diff --git a/Source/VCGeneration/ConditionGeneration.cs b/Source/VCGeneration/ConditionGeneration.cs index 0e4b249f8..90461ee4f 100644 --- a/Source/VCGeneration/ConditionGeneration.cs +++ b/Source/VCGeneration/ConditionGeneration.cs @@ -7,7 +7,10 @@ using Microsoft.Boogie; using Microsoft.Boogie.GraphUtil; using System.Diagnostics.Contracts; +using System.IO; using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using VC; using Set = Microsoft.Boogie.GSet; namespace VC @@ -23,10 +26,10 @@ public VCGenException(string s) [ContractClassFor(typeof(ConditionGeneration))] public abstract class ConditionGenerationContracts : ConditionGeneration { - public override Outcome VerifyImplementation(Implementation impl, VerifierCallback callback, + public override Outcome VerifyImplementation(ImplementationRun run, VerifierCallback callback, CancellationToken cancellationToken) { - Contract.Requires(impl != null); + Contract.Requires(run != null); Contract.Requires(callback != null); Contract.EnsuresOnThrow(true); throw new NotImplementedException(); @@ -114,11 +117,11 @@ public ConditionGeneration(Program p, CheckerPool checkerPool) /// each counterexample consisting of an array of labels. /// /// - public Outcome VerifyImplementation(Implementation impl, out List /*?*/ errors, + public Outcome VerifyImplementation(ImplementationRun run, out List /*?*/ errors, out List vcResults, string requestId, CancellationToken cancellationToken) { - Contract.Requires(impl != null); + Contract.Requires(run != null); Contract.Ensures(Contract.ValueAtReturn(out errors) == null || Contract.ForAll(Contract.ValueAtReturn(out errors), i => i != null)); @@ -128,7 +131,7 @@ public Outcome VerifyImplementation(Implementation impl, out List CheckerPool.Options; - public abstract Outcome VerifyImplementation(Implementation impl, VerifierCallback callback, + public abstract Outcome VerifyImplementation(ImplementationRun run, VerifierCallback callback, CancellationToken cancellationToken); /////////////////////////////////// Common Methods and Classes ////////////////////////////////////////// @@ -828,7 +831,7 @@ protected Dictionary ComputeIncarnationMap(Block b, preHavocIncarnationMap = null; // null = the previous command was not an HashCmd. Otherwise, a *copy* of the map before the havoc statement - protected void TurnIntoPassiveBlock(Block b, Dictionary incarnationMap, ModelViewInfo mvInfo, + protected void TurnIntoPassiveBlock(TextWriter traceWriter, Block b, Dictionary incarnationMap, ModelViewInfo mvInfo, Substitution oldFrameSubst, MutableVariableCollector variableCollector, byte[] currentChecksum = null) { Contract.Requires(b != null); @@ -846,7 +849,7 @@ protected void TurnIntoPassiveBlock(Block b, Dictionary incarnat ChecksumHelper.ComputeChecksums(Options, c, currentImplementation, variableCollector.UsedVariables, currentChecksum); variableCollector.Visit(c); currentChecksum = c.Checksum; - TurnIntoPassiveCmd(c, b, incarnationMap, oldFrameSubst, passiveCmds, mvInfo); + TurnIntoPassiveCmd(traceWriter, c, b, incarnationMap, oldFrameSubst, passiveCmds, mvInfo); } b.Checksum = currentChecksum; @@ -863,46 +866,45 @@ protected void TurnIntoPassiveBlock(Block b, Dictionary incarnat #endregion } - protected Dictionary Convert2PassiveCmd(Implementation impl, ModelViewInfo mvInfo) + protected Dictionary Convert2PassiveCmd(ImplementationRun run, ModelViewInfo mvInfo) { - Contract.Requires(impl != null); + Contract.Requires(run != null); Contract.Requires(mvInfo != null); - currentImplementation = impl; + var implementation = run.Implementation; + currentImplementation = run.Implementation; var start = DateTime.UtcNow; - Dictionary r = ConvertBlocks2PassiveCmd(impl.Blocks, impl.Proc.Modifies, mvInfo); + Dictionary r = ConvertBlocks2PassiveCmd(run.TraceWriter, implementation.Blocks, implementation.Proc.Modifies, mvInfo); var end = DateTime.UtcNow; if (Options.TraceCachingForDebugging) { - Console.Out.WriteLine("Turned implementation into passive commands within {0:F0} ms.\n", + run.TraceWriter.WriteLine("Turned implementation into passive commands within {0:F0} ms.\n", end.Subtract(start).TotalMilliseconds); - } - if (Options.TraceCachingForDebugging) { - using var tokTxtWr = new TokenTextWriter("", Console.Out, false, false, Options); + var tokTxtWr = new TokenTextWriter("", run.TraceWriter, false, false, Options); var pd = Options.PrintDesugarings; var pu = Options.PrintUnstructured; Options.PrintDesugarings = true; Options.PrintUnstructured = 1; - impl.Emit(tokTxtWr, 0); + implementation.Emit(tokTxtWr, 0); Options.PrintDesugarings = pd; Options.PrintUnstructured = pu; } currentImplementation = null; - RestoreParamWhereClauses(impl); + RestoreParamWhereClauses(implementation); #region Debug Tracing if (Options.TraceVerify) { Console.WriteLine("after conversion to passive commands"); - EmitImpl(Options, impl, true); + EmitImpl(Options, implementation, true); } #endregion @@ -910,7 +912,7 @@ protected Dictionary Convert2PassiveCmd(Implementation impl, Mod return r; } - protected Dictionary ConvertBlocks2PassiveCmd(List blocks, List modifies, + protected Dictionary ConvertBlocks2PassiveCmd(TextWriter traceWriter, List blocks, List modifies, ModelViewInfo mvInfo) { Contract.Requires(blocks != null); @@ -991,7 +993,7 @@ protected Dictionary ConvertBlocks2PassiveCmd(List blocks #endregion Each block's map needs to be available to successor blocks - TurnIntoPassiveBlock(b, incarnationMap, mvInfo, oldFrameSubst, mvc, currentChecksum); + TurnIntoPassiveBlock(traceWriter, b, incarnationMap, mvInfo, oldFrameSubst, mvc, currentChecksum); exitBlock = b; exitIncarnationMap = incarnationMap; } @@ -1037,16 +1039,16 @@ public enum CachingAction : byte public long[] CachingActionCounts; - void TraceCachingAction(Cmd cmd, CachingAction action) + void TraceCachingAction(TextWriter traceWriter, Cmd cmd, CachingAction action) { if (Options.TraceCachingForTesting) { - using var tokTxtWr = new TokenTextWriter("", Console.Out, false, false, Options); + var tokTxtWr = new TokenTextWriter("", traceWriter, false, false, Options); var loc = cmd.tok != null && cmd.tok != Token.NoToken ? string.Format("{0}({1},{2})", cmd.tok.filename, cmd.tok.line, cmd.tok.col) : ""; - Console.Write("Processing command (at {0}) ", loc); + traceWriter.Write("Processing command (at {0}) ", loc); cmd.Emit(tokTxtWr, 0); - Console.Out.WriteLine(" >>> {0}", action); + traceWriter.WriteLine(" >>> {0}", action); } if (Options.TraceCachingForBenchmarking && CachingActionCounts != null) @@ -1102,7 +1104,7 @@ private void AddDebugInfo(Cmd c, Dictionary incarnationMap, List /// In that case, it remembers the incarnation map BEFORE the havoc. /// Meanwhile, record any information needed to later reconstruct a model view. /// - protected void TurnIntoPassiveCmd(Cmd c, Block enclosingBlock, Dictionary incarnationMap, Substitution oldFrameSubst, + protected void TurnIntoPassiveCmd(TextWriter traceWriter, Cmd c, Block enclosingBlock, Dictionary incarnationMap, Substitution oldFrameSubst, List passiveCmds, ModelViewInfo mvInfo) { Contract.Requires(c != null); @@ -1184,7 +1186,7 @@ protected void TurnIntoPassiveCmd(Cmd c, Block enclosingBlock, Dictionary(), @@ -1229,7 +1231,7 @@ protected void TurnIntoPassiveCmd(Cmd c, Block enclosingBlock, Dictionary /// As a side effect, updates "this.parent.CumulativeAssertionCount". /// - public void BeginCheck(Checker checker, VerifierCallback callback, ModelViewInfo mvInfo, int no, uint timeout, uint rlimit, CancellationToken cancellationToken) + public void BeginCheck(TextWriter traceWriter, Checker checker, VerifierCallback callback, ModelViewInfo mvInfo, int no, uint timeout, uint rlimit, CancellationToken cancellationToken) { Contract.Requires(checker != null); Contract.Requires(callback != null); @@ -1381,7 +1381,7 @@ public void BeginCheck(Checker checker, VerifierCallback callback, ModelViewInfo ProverContext ctx = checker.TheoremProver.Context; Boogie2VCExprTranslator bet = ctx.BoogieExprTranslator; - var cc = new VCGen.CodeExprConversionClosure(checker.Pool.Options, absyIds, ctx); + var cc = new VCGen.CodeExprConversionClosure(traceWriter, checker.Pool.Options, absyIds, ctx); bet.SetCodeExprConverter(cc.CodeExprToVerificationCondition); var exprGen = ctx.ExprGen; diff --git a/Source/VCGeneration/SplitAndVerifyWorker.cs b/Source/VCGeneration/SplitAndVerifyWorker.cs index 8da3aea39..0513de9ac 100644 --- a/Source/VCGeneration/SplitAndVerifyWorker.cs +++ b/Source/VCGeneration/SplitAndVerifyWorker.cs @@ -14,7 +14,7 @@ class SplitAndVerifyWorker private readonly VCGenOptions options; private readonly VerifierCallback callback; private readonly ModelViewInfo mvInfo; - private readonly Implementation implementation; + private readonly ImplementationRun run; private readonly int maxKeepGoingSplits; private readonly List manualSplits; @@ -33,35 +33,35 @@ class SplitAndVerifyWorker private int totalResourceCount; - public SplitAndVerifyWorker(VCGenOptions options, VCGen vcGen, Implementation implementation, + public SplitAndVerifyWorker(VCGenOptions options, VCGen vcGen, ImplementationRun run, Dictionary gotoCmdOrigins, VerifierCallback callback, ModelViewInfo mvInfo, Outcome outcome) { this.options = options; this.callback = callback; this.mvInfo = mvInfo; - this.implementation = implementation; + this.run = run; this.outcome = outcome; - + var maxSplits = options.VcsMaxSplits; - VCGen.CheckIntAttributeOnImpl(implementation, "vcs_max_splits", ref maxSplits); + VCGen.CheckIntAttributeOnImpl(Implementation, "vcs_max_splits", ref maxSplits); maxKeepGoingSplits = options.VcsMaxKeepGoingSplits; - VCGen.CheckIntAttributeOnImpl(implementation, "vcs_max_keep_going_splits", ref maxKeepGoingSplits); + VCGen.CheckIntAttributeOnImpl(Implementation, "vcs_max_keep_going_splits", ref maxKeepGoingSplits); maxVcCost = options.VcsMaxCost; var tmpMaxVcCost = -1; - VCGen.CheckIntAttributeOnImpl(implementation, "vcs_max_cost", ref tmpMaxVcCost); + VCGen.CheckIntAttributeOnImpl(Implementation, "vcs_max_cost", ref tmpMaxVcCost); if (tmpMaxVcCost >= 0) { maxVcCost = tmpMaxVcCost; } splitOnEveryAssert = options.VcsSplitOnEveryAssert; - implementation.CheckBooleanAttribute("vcs_split_on_every_assert", ref splitOnEveryAssert); + Implementation.CheckBooleanAttribute("vcs_split_on_every_assert", ref splitOnEveryAssert); - ResetPredecessors(implementation.Blocks); - manualSplits = Split.FocusAndSplit(options, implementation, gotoCmdOrigins, vcGen, splitOnEveryAssert); + ResetPredecessors(Implementation.Blocks); + manualSplits = Split.FocusAndSplit(options, Implementation, gotoCmdOrigins, vcGen, splitOnEveryAssert); if (manualSplits.Count == 1 && maxSplits > 1) { manualSplits = Split.DoSplit(manualSplits[0], maxVcCost, maxSplits); @@ -122,10 +122,12 @@ private void StartCheck(Split split, Checker checker, CancellationToken cancella var timeout = KeepGoing && split.LastChance ? options.VcsFinalAssertTimeout : KeepGoing ? options.VcsKeepGoingTimeout : - implementation.GetTimeLimit(options); - split.BeginCheck(checker, callback, mvInfo, currentSplitNumber, timeout, implementation.GetResourceLimit(options), cancellationToken); + run.Implementation.GetTimeLimit(options); + split.BeginCheck(run.TraceWriter, checker, callback, mvInfo, currentSplitNumber, timeout, Implementation.GetResourceLimit(options), cancellationToken); } + private Implementation Implementation => run.Implementation; + private async Task ProcessResult(Split split, CancellationToken cancellationToken) { if (TrackingProgress) { diff --git a/Source/VCGeneration/StratifiedVC.cs b/Source/VCGeneration/StratifiedVC.cs index e0cb00bb1..5aa5e2306 100644 --- a/Source/VCGeneration/StratifiedVC.cs +++ b/Source/VCGeneration/StratifiedVC.cs @@ -109,7 +109,7 @@ public VCExpr MustReach(Block block, ControlFlowIdMap absyIds) { var vcgen = info.vcgen; var gen = vcgen.prover.VCExprGen; - var impl = info.impl; + var impl = info.Implementation; mustReachVar = new Dictionary(); mustReachBindings = new List(); foreach (Block b in impl.Blocks) @@ -182,7 +182,7 @@ public List RecordProcCallSites public override string ToString() { - return info.impl.Name; + return info.Implementation.Name; } } @@ -339,7 +339,7 @@ public class StratifiedInliningInfo { private VCGenOptions options; public StratifiedVCGenBase vcgen; - public Implementation impl; + public ImplementationRun run; public Function function; public Variable controlFlowVariable; public Cmd exitAssertCmd; @@ -358,12 +358,14 @@ public class StratifiedInliningInfo // boolControlVC (block -> its Bool variable) public Dictionary blockToControlVar; - public StratifiedInliningInfo(VCGenOptions options, Implementation implementation, StratifiedVCGenBase stratifiedVcGen, + public Implementation Implementation => run.Implementation; + + public StratifiedInliningInfo(VCGenOptions options, ImplementationRun run, StratifiedVCGenBase stratifiedVcGen, Action PassiveImplInstrumentation) { vcgen = stratifiedVcGen; - impl = implementation; this.PassiveImplInstrumentation = PassiveImplInstrumentation; + this.run = run; this.options = options; List functionInterfaceVars = new List(); @@ -373,19 +375,19 @@ public StratifiedInliningInfo(VCGenOptions options, Implementation implementatio true)); } - foreach (Variable v in impl.InParams) + foreach (Variable v in Implementation.InParams) { functionInterfaceVars.Add(new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "", v.TypedIdent.Type), true)); } - foreach (Variable v in impl.OutParams) + foreach (Variable v in Implementation.OutParams) { functionInterfaceVars.Add(new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "", v.TypedIdent.Type), true)); } - foreach (IdentifierExpr e in impl.Proc.Modifies) + foreach (IdentifierExpr e in Implementation.Proc.Modifies) { if (e.Decl == null) { @@ -397,7 +399,7 @@ public StratifiedInliningInfo(VCGenOptions options, Implementation implementatio } Formal returnVar = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "", Bpl.Type.Bool), false); - function = new Function(Token.NoToken, impl.Name, functionInterfaceVars, returnVar); + function = new Function(Token.NoToken, Implementation.Name, functionInterfaceVars, returnVar); vcgen.prover.Context.DeclareFunction(function, ""); List exprs = new List(); @@ -407,19 +409,19 @@ public StratifiedInliningInfo(VCGenOptions options, Implementation implementatio exprs.Add(new OldExpr(Token.NoToken, new IdentifierExpr(Token.NoToken, v))); } - foreach (Variable v in impl.Proc.InParams) + foreach (Variable v in Implementation.Proc.InParams) { Contract.Assert(v != null); exprs.Add(new IdentifierExpr(Token.NoToken, v)); } - foreach (Variable v in impl.Proc.OutParams) + foreach (Variable v in Implementation.Proc.OutParams) { Contract.Assert(v != null); exprs.Add(new IdentifierExpr(Token.NoToken, v)); } - foreach (IdentifierExpr ie in impl.Proc.Modifies) + foreach (IdentifierExpr ie in Implementation.Proc.Modifies) { Contract.Assert(ie != null); if (ie.Decl == null) @@ -431,7 +433,7 @@ public StratifiedInliningInfo(VCGenOptions options, Implementation implementatio } Expr freePostExpr = new NAryExpr(Token.NoToken, new FunctionCall(function), exprs); - impl.Proc.Ensures.Add(new Ensures(Token.NoToken, true, freePostExpr, "", + Implementation.Proc.Ensures.Add(new Ensures(Token.NoToken, true, freePostExpr, "", new QKeyValue(Token.NoToken, "si_fcall", new List(), null))); initialized = false; @@ -445,16 +447,16 @@ public void GenerateVCBoolControl() // fix names for exit variables var outputVariables = new List(); var assertConjuncts = new List(); - foreach (Variable v in impl.OutParams) + foreach (Variable v in Implementation.OutParams) { Constant c = new Constant(Token.NoToken, - new TypedIdent(Token.NoToken, impl.Name + "_" + v.Name, v.TypedIdent.Type)); + new TypedIdent(Token.NoToken, Implementation.Name + "_" + v.Name, v.TypedIdent.Type)); outputVariables.Add(c); Expr eqExpr = Expr.Eq(new IdentifierExpr(Token.NoToken, c), new IdentifierExpr(Token.NoToken, v)); assertConjuncts.Add(eqExpr); } - foreach (IdentifierExpr e in impl.Proc.Modifies) + foreach (IdentifierExpr e in Implementation.Proc.Modifies) { if (e.Decl == null) { @@ -463,7 +465,7 @@ public void GenerateVCBoolControl() Variable v = e.Decl; Constant c = new Constant(Token.NoToken, - new TypedIdent(Token.NoToken, impl.Name + "_" + v.Name, v.TypedIdent.Type)); + new TypedIdent(Token.NoToken, Implementation.Name + "_" + v.Name, v.TypedIdent.Type)); outputVariables.Add(c); Expr eqExpr = Expr.Eq(new IdentifierExpr(Token.NoToken, c), new IdentifierExpr(Token.NoToken, v)); assertConjuncts.Add(eqExpr); @@ -475,19 +477,19 @@ public void GenerateVCBoolControl() // Passify Program program = vcgen.program; ProverInterface proverInterface = vcgen.prover; - vcgen.ConvertCFG2DAG(impl); - vcgen.PassifyImpl(impl, out mvInfo); + vcgen.ConvertCFG2DAG(Implementation); + vcgen.PassifyImpl(run, out mvInfo); VCExpressionGenerator gen = proverInterface.VCExprGen; var exprGen = proverInterface.Context.ExprGen; var translator = proverInterface.Context.BoogieExprTranslator; // add a boolean variable at each call site - vcgen.InstrumentCallSites(impl); + vcgen.InstrumentCallSites(Implementation); // typecheck var tc = new TypecheckingContext(null, options); - impl.Typecheck(tc); + Implementation.Typecheck(tc); /////////////////// // Generate the VC @@ -495,13 +497,13 @@ public void GenerateVCBoolControl() // block -> bool variable blockToControlVar = new Dictionary(); - foreach (var b in impl.Blocks) + foreach (var b in Implementation.Blocks) { blockToControlVar.Add(b, gen.Variable(b.Label + "_holds", Bpl.Type.Bool)); } vcexpr = VCExpressionGenerator.True; - foreach (var b in impl.Blocks) + foreach (var b in Implementation.Blocks) { // conjoin all assume cmds VCExpr c = VCExpressionGenerator.True; @@ -541,21 +543,21 @@ public void GenerateVCBoolControl() } // assert start block - vcexpr = gen.AndSimp(vcexpr, blockToControlVar[impl.Blocks[0]]); + vcexpr = gen.AndSimp(vcexpr, blockToControlVar[Implementation.Blocks[0]]); //Console.WriteLine("VC of {0}: {1}", impl.Name, vcexpr); // Collect other information - callSites = vcgen.CollectCallSites(impl); - recordProcCallSites = vcgen.CollectRecordProcedureCallSites(impl); + callSites = vcgen.CollectCallSites(Implementation); + recordProcCallSites = vcgen.CollectRecordProcedureCallSites(Implementation); // record interface variables privateExprVars = new List(); - foreach (Variable v in impl.LocVars) + foreach (Variable v in Implementation.LocVars) { privateExprVars.Add(translator.LookupVariable(v)); } - foreach (Variable v in impl.OutParams) + foreach (Variable v in Implementation.OutParams) { privateExprVars.Add(translator.LookupVariable(v)); } @@ -568,7 +570,7 @@ public void GenerateVCBoolControl() interfaceExprVars.Add(translator.LookupVariable(v)); } - foreach (Variable v in impl.InParams) + foreach (Variable v in Implementation.InParams) { interfaceExprVars.Add(translator.LookupVariable(v)); } @@ -595,16 +597,16 @@ public void GenerateVC() List outputVariables = new List(); List assertConjuncts = new List(); - foreach (Variable v in impl.OutParams) + foreach (Variable v in Implementation.OutParams) { Constant c = new Constant(Token.NoToken, - new TypedIdent(Token.NoToken, impl.Name + "_" + v.Name, v.TypedIdent.Type)); + new TypedIdent(Token.NoToken, Implementation.Name + "_" + v.Name, v.TypedIdent.Type)); outputVariables.Add(c); Expr eqExpr = Expr.Eq(new IdentifierExpr(Token.NoToken, c), new IdentifierExpr(Token.NoToken, v)); assertConjuncts.Add(eqExpr); } - foreach (IdentifierExpr e in impl.Proc.Modifies) + foreach (IdentifierExpr e in Implementation.Proc.Modifies) { if (e.Decl == null) { @@ -613,7 +615,7 @@ public void GenerateVC() Variable v = e.Decl; Constant c = new Constant(Token.NoToken, - new TypedIdent(Token.NoToken, impl.Name + "_" + v.Name, v.TypedIdent.Type)); + new TypedIdent(Token.NoToken, Implementation.Name + "_" + v.Name, v.TypedIdent.Type)); outputVariables.Add(c); Expr eqExpr = Expr.Eq(new IdentifierExpr(Token.NoToken, c), new IdentifierExpr(Token.NoToken, v)); assertConjuncts.Add(eqExpr); @@ -623,8 +625,8 @@ public void GenerateVC() Program program = vcgen.program; ProverInterface proverInterface = vcgen.prover; - vcgen.ConvertCFG2DAG(impl); - vcgen.PassifyImpl(impl, out mvInfo); + vcgen.ConvertCFG2DAG(Implementation); + vcgen.PassifyImpl(run, out mvInfo); VCExpressionGenerator gen = proverInterface.VCExprGen; var exprGen = proverInterface.Context.ExprGen; @@ -634,37 +636,37 @@ public void GenerateVC() new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, "@cfc", Microsoft.Boogie.Type.Int)); VCExpr controlFlowVariableExpr = translator.LookupVariable(controlFlowVariable); - vcgen.InstrumentCallSites(impl); + vcgen.InstrumentCallSites(Implementation); if (PassiveImplInstrumentation != null) { - PassiveImplInstrumentation(impl); + PassiveImplInstrumentation(Implementation); } var absyIds = new ControlFlowIdMap(); - VCGen.CodeExprConversionClosure cc = new VCGen.CodeExprConversionClosure(options, absyIds, proverInterface.Context); + VCGen.CodeExprConversionClosure cc = new VCGen.CodeExprConversionClosure(run.TraceWriter, options, absyIds, proverInterface.Context); translator.SetCodeExprConverter(cc.CodeExprToVerificationCondition); - vcexpr = gen.Not(vcgen.GenerateVCAux(impl, controlFlowVariableExpr, absyIds, proverInterface.Context)); + vcexpr = gen.Not(vcgen.GenerateVCAux(Implementation, controlFlowVariableExpr, absyIds, proverInterface.Context)); if (controlFlowVariableExpr != null) { VCExpr controlFlowFunctionAppl = exprGen.ControlFlowFunctionApplication(controlFlowVariableExpr, exprGen.Integer(BigNum.ZERO)); - VCExpr eqExpr = exprGen.Eq(controlFlowFunctionAppl, exprGen.Integer(BigNum.FromInt(absyIds.GetId(impl.Blocks[0])))); + VCExpr eqExpr = exprGen.Eq(controlFlowFunctionAppl, exprGen.Integer(BigNum.FromInt(absyIds.GetId(Implementation.Blocks[0])))); vcexpr = exprGen.And(eqExpr, vcexpr); } - callSites = vcgen.CollectCallSites(impl); - recordProcCallSites = vcgen.CollectRecordProcedureCallSites(impl); + callSites = vcgen.CollectCallSites(Implementation); + recordProcCallSites = vcgen.CollectRecordProcedureCallSites(Implementation); privateExprVars = new List(); - foreach (Variable v in impl.LocVars) + foreach (Variable v in Implementation.LocVars) { privateExprVars.Add(translator.LookupVariable(v)); } - foreach (Variable v in impl.OutParams) + foreach (Variable v in Implementation.OutParams) { privateExprVars.Add(translator.LookupVariable(v)); } @@ -675,7 +677,7 @@ public void GenerateVC() interfaceExprVars.Add(translator.LookupVariable(v)); } - foreach (Variable v in impl.InParams) + foreach (Variable v in Implementation.InParams) { interfaceExprVars.Add(translator.LookupVariable(v)); } @@ -697,7 +699,7 @@ public abstract class StratifiedVCGenBase : VCGen public Dictionary implName2StratifiedInliningInfo; public ProverInterface prover; - public StratifiedVCGenBase(VCGenOptions options, Program program, string logFilePath /*?*/, bool appendLogFile, CheckerPool checkerPool, + public StratifiedVCGenBase(TextWriter traceWriter, VCGenOptions options, Program program, string logFilePath /*?*/, bool appendLogFile, CheckerPool checkerPool, Action PassiveImplInstrumentation) : base(program, checkerPool) { @@ -705,7 +707,7 @@ public StratifiedVCGenBase(VCGenOptions options, Program program, string logFile prover = ProverInterface.CreateProver(options, program, logFilePath, appendLogFile, options.TimeLimit); foreach (var impl in program.Implementations) { - implName2StratifiedInliningInfo[impl.Name] = new StratifiedInliningInfo(options, impl, this, PassiveImplInstrumentation); + implName2StratifiedInliningInfo[impl.Name] = new StratifiedInliningInfo(options, new ImplementationRun(impl, traceWriter), this, PassiveImplInstrumentation); } GenerateRecordFunctions(); @@ -971,9 +973,7 @@ protected override bool elIsLoop(string procname) return false; } - var lp = info.impl.Proc as LoopProcedure; - - if (lp == null) + if (info.Implementation.Proc is not LoopProcedure) { return false; } diff --git a/Source/VCGeneration/VCGen.cs b/Source/VCGeneration/VCGen.cs index d32312d7e..69477bccc 100644 --- a/Source/VCGeneration/VCGen.cs +++ b/Source/VCGeneration/VCGen.cs @@ -6,6 +6,7 @@ using Microsoft.Boogie; using Microsoft.Boogie.GraphUtil; using System.Diagnostics.Contracts; +using System.IO; using System.Runtime.CompilerServices; using System.Threading; using Microsoft.BaseTypes; @@ -65,7 +66,7 @@ class SmokeTester void ObjectInvariant() { Contract.Invariant(parent != null); - Contract.Invariant(impl != null); + Contract.Invariant(run != null); Contract.Invariant(initial != null); Contract.Invariant(cce.NonNullDictionaryAndValues(copies)); Contract.Invariant(cce.NonNull(visited)); @@ -73,21 +74,21 @@ void ObjectInvariant() } VCGen parent; - Implementation impl; + ImplementationRun run; Block initial; int id; Dictionary copies = new Dictionary(); HashSet visited = new HashSet(); VerifierCallback callback; - internal SmokeTester(VCGen par, Implementation i, VerifierCallback callback) + internal SmokeTester(VCGen par, ImplementationRun run, VerifierCallback callback) { Contract.Requires(par != null); - Contract.Requires(i != null); + Contract.Requires(run != null); Contract.Requires(callback != null); parent = par; - impl = i; - initial = i.Blocks[0]; + this.run = run; + initial = run.Implementation.Blocks[0]; this.callback = callback; } @@ -95,32 +96,32 @@ internal SmokeTester(VCGen par, Implementation i, VerifierCallback callback) internal void Copy() { - CloneBlock(impl.Blocks[0]); + CloneBlock(run.Implementation.Blocks[0]); initial = GetCopiedBlocks()[0]; } - internal void Test() + internal void Test(TextWriter traceWriter) { Contract.EnsuresOnThrow(true); - DFS(initial); + DFS(traceWriter, initial); } void TopologicalSortImpl() { - Graph dag = Program.GraphFromImpl(impl); - impl.Blocks = new List(); + Graph dag = Program.GraphFromImpl(run.Implementation); + run.Implementation.Blocks = new List(); foreach (Block b in dag.TopologicalSort()) { Contract.Assert(b != null); - impl.Blocks.Add(b); + run.Implementation.Blocks.Add(b); } } void Emit() { TopologicalSortImpl(); - EmitImpl(Options, impl, false); + EmitImpl(Options, run.Implementation, false); } // this one copies forward @@ -282,7 +283,7 @@ bool IsFalse(Expr e) return BooleanEval(e, ref val) && !val; } - bool CheckUnreachable(Block cur, List seq) + bool CheckUnreachable(TextWriter traceWriter, Block cur, List seq) { Contract.Requires(cur != null); Contract.Requires(seq != null); @@ -310,9 +311,9 @@ bool CheckUnreachable(Block cur, List seq) Block copy = CopyBlock(cur); Contract.Assert(copy != null); copy.Cmds = seq; - List backup = impl.Blocks; + List backup = run.Implementation.Blocks; Contract.Assert(backup != null); - impl.Blocks = GetCopiedBlocks(); + run.Implementation.Blocks = GetCopiedBlocks(); copy.TransferCmd = new ReturnCmd(Token.NoToken); if (Options.TraceVerify) { @@ -321,8 +322,8 @@ bool CheckUnreachable(Block cur, List seq) Emit(); } - parent.CurrentLocalVariables = impl.LocVars; - parent.PassifyImpl(impl, out var mvInfo); + parent.CurrentLocalVariables = run.Implementation.LocVars; + parent.PassifyImpl(run, out var mvInfo); Checker ch = parent.CheckerPool.FindCheckerFor(parent).Result; Contract.Assert(ch != null); @@ -336,16 +337,16 @@ bool CheckUnreachable(Block cur, List seq) var absyIds = new ControlFlowIdMap(); - VCExpr vc = parent.GenerateVC(impl, controlFlowVariableExpr, absyIds, ch.TheoremProver.Context); + VCExpr vc = parent.GenerateVC(run.Implementation, controlFlowVariableExpr, absyIds, ch.TheoremProver.Context); Contract.Assert(vc != null); VCExpr controlFlowFunctionAppl = exprGen.ControlFlowFunctionApplication(exprGen.Integer(BigNum.ZERO), exprGen.Integer(BigNum.ZERO)); VCExpr eqExpr = exprGen.Eq(controlFlowFunctionAppl, - exprGen.Integer(BigNum.FromInt(absyIds.GetId(impl.Blocks[0])))); + exprGen.Integer(BigNum.FromInt(absyIds.GetId(run.Implementation.Blocks[0])))); vc = exprGen.Implies(eqExpr, vc); - impl.Blocks = backup; + run.Implementation.Blocks = backup; if (Options.TraceVerify) { @@ -353,7 +354,7 @@ bool CheckUnreachable(Block cur, List seq) Emit(); } - ch.BeginCheck(cce.NonNull(impl.Name + "_smoke" + id++), vc, new ErrorHandler(Options, absyIds, callback), + ch.BeginCheck(cce.NonNull(Implementation.Name + "_smoke" + id++), vc, new ErrorHandler(Options, absyIds, callback), Options.SmokeTimeout, Options.ResourceLimit, CancellationToken.None); } @@ -386,19 +387,21 @@ bool CheckUnreachable(Block cur, List seq) // copy it again, so we get the version with calls, assignments and such copy = CopyBlock(cur); copy.Cmds = seq; - impl.Blocks = GetCopiedBlocks(); + Implementation.Blocks = GetCopiedBlocks(); TopologicalSortImpl(); - callback.OnUnreachableCode(impl); - impl.Blocks = backup; + callback.OnUnreachableCode(Implementation); + Implementation.Blocks = backup; return true; } return false; } + private Implementation Implementation => run.Implementation; + const bool turnAssertIntoAssumes = false; - void DFS(Block cur) + void DFS(TextWriter traceWriter, Block cur) { Contract.Requires(cur != null); Contract.EnsuresOnThrow(true); @@ -458,7 +461,7 @@ void DFS(Block cur) if (assumeFalse) { - CheckUnreachable(cur, seq); + CheckUnreachable(traceWriter, cur, seq); return; } @@ -474,7 +477,7 @@ void DFS(Block cur) if (ret != null || (go != null && cce.NonNull(go.labelTargets).Count == 0)) { // we end in return, so there will be no more places to check - CheckUnreachable(cur, seq); + CheckUnreachable(traceWriter, cur, seq); } else if (go != null) { @@ -492,13 +495,13 @@ void DFS(Block cur) if (needToCheck) { - CheckUnreachable(cur, seq); + CheckUnreachable(traceWriter, cur, seq); } foreach (Block target in go.labelTargets) { Contract.Assert(target != null); - DFS(target); + DFS(traceWriter, target); } } } @@ -545,12 +548,15 @@ public override void OnProverWarning(string msg) public class CodeExprConversionClosure { + private readonly TextWriter traceWriter; private readonly VCGenOptions options; ControlFlowIdMap absyIds; ProverContext ctx; - public CodeExprConversionClosure(VCGenOptions options, ControlFlowIdMap absyIds, ProverContext ctx) + public CodeExprConversionClosure(TextWriter traceWriter, VCGenOptions options, ControlFlowIdMap absyIds, + ProverContext ctx) { + this.traceWriter = traceWriter; this.options = options; this.absyIds = absyIds; this.ctx = ctx; @@ -565,7 +571,7 @@ public VCExpr CodeExprToVerificationCondition(CodeExpr codeExpr, List gotoCmdOrigins = vcgen.ConvertBlocks2PassiveCmd(codeExpr.Blocks, + Dictionary gotoCmdOrigins = vcgen.ConvertBlocks2PassiveCmd(traceWriter, codeExpr.Blocks, new List(), new ModelViewInfo(codeExpr)); VCExpr startCorrect = vcgen.LetVC(codeExpr.Blocks, null, absyIds, ctx, out var ac, isPositiveContext); VCExpr vce = ctx.ExprGen.Let(bindings, startCorrect); @@ -813,12 +819,14 @@ void ExpandAsserts(Implementation impl) private VCGenOptions Options => CheckerPool.Options; - public override Outcome VerifyImplementation(Implementation impl, VerifierCallback callback, + public override Outcome VerifyImplementation(ImplementationRun run, VerifierCallback callback, CancellationToken cancellationToken) { Contract.EnsuresOnThrow(true); - if (impl.IsSkipVerification(Options)) + var impl = run.Implementation; + + if (run.Implementation.IsSkipVerification(Options)) { return Outcome.Inconclusive; // not sure about this one } @@ -832,16 +840,16 @@ public override Outcome VerifyImplementation(Implementation impl, VerifierCallba watch.Start(); #endif - ConvertCFG2DAG(impl); + ConvertCFG2DAG(run.Implementation); SmokeTester smoke_tester = null; if (Options.SoundnessSmokeTest) { - smoke_tester = new SmokeTester(this, impl, callback); + smoke_tester = new SmokeTester(this, run, callback); smoke_tester.Copy(); } - var gotoCmdOrigins = PassifyImpl(impl, out var mvInfo); + var gotoCmdOrigins = PassifyImpl(run, out var mvInfo); ExpandAsserts(impl); @@ -854,8 +862,7 @@ public override Outcome VerifyImplementation(Implementation impl, VerifierCallba foreach (var a in impl.RecycledFailingAssertions) { var checksum = a.Checksum; - var oldCex = impl.ErrorChecksumToCachedError[checksum] as Counterexample; - if (oldCex != null) + if (impl.ErrorChecksumToCachedError[checksum] is Counterexample oldCex) { if (Options.VerifySnapshots < 3) { @@ -875,13 +882,13 @@ public override Outcome VerifyImplementation(Implementation impl, VerifierCallba } } - var worker = new SplitAndVerifyWorker(Options, this, impl, gotoCmdOrigins, callback, mvInfo, outcome); + var worker = new SplitAndVerifyWorker(Options, this, run, gotoCmdOrigins, callback, mvInfo, outcome); outcome = worker.WorkUntilDone(cancellationToken).Result; ResourceCount = worker.ResourceCount; if (outcome == Outcome.Correct && smoke_tester != null) { - smoke_tester.Test(); + smoke_tester.Test(run.TraceWriter); } callback.OnProgress?.Invoke("done", 0, 0, 1.0); @@ -1692,12 +1699,13 @@ public void DesugarCalls(Implementation impl) } } - public Dictionary PassifyImpl(Implementation impl, out ModelViewInfo mvInfo) + public Dictionary PassifyImpl(ImplementationRun run, out ModelViewInfo mvInfo) { - Contract.Requires(impl != null); + Contract.Requires(run != null); Contract.Requires(program != null); Contract.Ensures(Contract.Result>() != null); + var impl = run.Implementation; Dictionary gotoCmdOrigins = new Dictionary(); Block exitBlock = GenerateUnifiedExit(impl, gotoCmdOrigins); @@ -1793,7 +1801,7 @@ public Dictionary PassifyImpl(Implementation impl, out M } mvInfo = new ModelViewInfo(program, impl); - Convert2PassiveCmd(impl, mvInfo); + Convert2PassiveCmd(run, mvInfo); if (QKeyValue.FindBoolAttribute(impl.Attributes, "may_unverified_instrumentation")) { From 2a69bc17b4233b72ca48307ad3f3ddfbdb723943 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Wed, 9 Mar 2022 12:04:51 +0100 Subject: [PATCH 18/32] Allow trace output to be printed immediately, before verification has completed --- .../ConcurrentToSequentialWriteManager.cs | 60 +++++++++++++ Source/ExecutionEngine/ExecutionEngine.cs | 14 ++- Source/ExecutionEngine/OutputCollector.cs | 41 --------- Source/ExecutionEngine/VerificationResult.cs | 2 +- Source/ExecutionEngine/WriterWrapper.cs | 88 +++++++++++++++++++ .../ConcurrentToSequentialWriteManagerTest.cs | 31 +++++++ Test/commandline/SplitOnEveryAssert.bpl | 2 +- Test/test0/SplitOnEveryAssert.bpl | 2 +- 8 files changed, 188 insertions(+), 52 deletions(-) create mode 100644 Source/ExecutionEngine/ConcurrentToSequentialWriteManager.cs delete mode 100644 Source/ExecutionEngine/OutputCollector.cs create mode 100644 Source/ExecutionEngine/WriterWrapper.cs create mode 100644 Source/UnitTests/ExecutionEngineTests/ConcurrentToSequentialWriteManagerTest.cs diff --git a/Source/ExecutionEngine/ConcurrentToSequentialWriteManager.cs b/Source/ExecutionEngine/ConcurrentToSequentialWriteManager.cs new file mode 100644 index 000000000..eca4f6a4e --- /dev/null +++ b/Source/ExecutionEngine/ConcurrentToSequentialWriteManager.cs @@ -0,0 +1,60 @@ +using System.Collections.Generic; +using System.IO; + +namespace Microsoft.Boogie; + +public class ConcurrentToSequentialWriteManager +{ + public TextWriter Writer { get; } + private readonly Queue writers = new(); + + public ConcurrentToSequentialWriteManager(TextWriter writer) { + Writer = writer; + } + + private readonly object myLock = new(); + + private void Disposed() { + lock (myLock) { + while (writers.Count > 0 && writers.Peek().Disposed) { + var disposedWriter = writers.Dequeue(); + Writer.Write(disposedWriter.SetTargetAndGetBuffer(null)); + } + if (writers.Count > 0) { + Writer.Write(writers.Peek().SetTargetAndGetBuffer(Writer)); + } + } + } + + public TextWriter AppendWriter() { + lock (myLock) { + var target = writers.Count == 0 ? Writer : null; + var result = new SubWriter(this, target); + writers.Enqueue(result); + return result; + } + } + + class SubWriter : WriterWrapper { + private readonly ConcurrentToSequentialWriteManager collector; + private bool buffering; + public bool Disposed { get; private set; } + + public SubWriter(ConcurrentToSequentialWriteManager collector, TextWriter target) : base(target ?? new StringWriter()) { + this.collector = collector; + buffering = target == null; + } + + public string SetTargetAndGetBuffer(TextWriter newTarget) { + var result = buffering ? ((StringWriter)target).ToString() : ""; + buffering = false; + target = newTarget; + return result; + } + + protected override void Dispose(bool disposing) { + Disposed = true; + collector.Disposed(); + } + } +} \ No newline at end of file diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index 3a50a7350..8e7a787d4 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -10,6 +10,7 @@ using BoogiePL = Microsoft.Boogie; using System.Runtime.Caching; using System.Diagnostics; +using System.Net.Mime; namespace Microsoft.Boogie { @@ -797,7 +798,7 @@ private PipelineOutcome VerifyEachImplementation(Program program, PipelineStatis Dictionary> extractLoopMappingInfo) { program.DeclarationDependencies = Prune.ComputeDeclarationDependencies(Options, program); - var outputCollector = new OutputCollector(stablePrioritizedImpls); + var outputCollector = new ConcurrentToSequentialWriteManager(Console.Out); var outcome = PipelineOutcome.VerificationCompleted; try { @@ -830,8 +831,9 @@ private PipelineOutcome VerifyEachImplementation(Program program, PipelineStatis cts.Token.ThrowIfCancellationRequested(); } + using var implementationWriter = outputCollector.AppendWriter(); VerifyImplementation(program, stats, er, requestId, extractLoopMappingInfo, stablePrioritizedImpls, - taskIndex, outputCollector, programId); + taskIndex, implementationWriter, programId); ImplIdToCancellationTokenSource.TryRemove(id, out old); } finally { @@ -885,7 +887,6 @@ private PipelineOutcome VerifyEachImplementation(Program program, PipelineStatis cce.NonNull(Options.TheProverFactory).Close(); - outputCollector.WriteMoreOutput(); return outcome; } @@ -951,9 +952,8 @@ private static void CleanupRequest(string requestId) private void VerifyImplementation(Program program, PipelineStatistics stats, ErrorReporterDelegate er, string requestId, Dictionary> extractLoopMappingInfo, - Implementation[] stablePrioritizedImpls, int index, OutputCollector outputCollector, string programId) + Implementation[] stablePrioritizedImpls, int index, TextWriter output, string programId) { - var output = new StringWriter(); Implementation impl = stablePrioritizedImpls[index]; printer.Inform("", output); // newline @@ -973,10 +973,8 @@ private void VerifyImplementation(Program program, PipelineStatistics stats, Err } verificationResult.Emit(this, stats, er, output, impl, wasCached); - outputCollector.Add(index, output); - outputCollector.WriteMoreOutput(); if (verificationResult.Outcome == VCGen.Outcome.Errors || Options.Trace) { - Console.Out.Flush(); + output.Flush(); } } diff --git a/Source/ExecutionEngine/OutputCollector.cs b/Source/ExecutionEngine/OutputCollector.cs deleted file mode 100644 index 32b794717..000000000 --- a/Source/ExecutionEngine/OutputCollector.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Diagnostics.Contracts; -using System.IO; - -namespace Microsoft.Boogie; - -public class OutputCollector -{ - StringWriter[] outputs; - - int nextPrintableIndex = 0; - - public OutputCollector(Implementation[] implementations) - { - outputs = new StringWriter[implementations.Length]; - } - - public void WriteMoreOutput() - { - lock (outputs) - { - for (; nextPrintableIndex < outputs.Length && outputs[nextPrintableIndex] != null; nextPrintableIndex++) - { - Console.Write(outputs[nextPrintableIndex].ToString()); - outputs[nextPrintableIndex] = null; - Console.Out.Flush(); - } - } - } - - public void Add(int index, StringWriter output) - { - Contract.Requires(0 <= index && index < outputs.Length); - Contract.Requires(output != null); - - lock (this) - { - outputs[index] = output; - } - } -} \ No newline at end of file diff --git a/Source/ExecutionEngine/VerificationResult.cs b/Source/ExecutionEngine/VerificationResult.cs index 6cfe57e3c..b95c40342 100644 --- a/Source/ExecutionEngine/VerificationResult.cs +++ b/Source/ExecutionEngine/VerificationResult.cs @@ -48,7 +48,7 @@ public VerificationResult(string requestId, Implementation implementation, strin MessageIfVerifies = implementation.FindStringAttribute("msg_if_verifies"); } - public void Emit(ExecutionEngine engine, PipelineStatistics stats, ErrorReporterDelegate er, StringWriter output, + public void Emit(ExecutionEngine engine, PipelineStatistics stats, ErrorReporterDelegate er, TextWriter output, Implementation impl, bool wasCached) { if (ErrorBeforeVerification != null) { diff --git a/Source/ExecutionEngine/WriterWrapper.cs b/Source/ExecutionEngine/WriterWrapper.cs new file mode 100644 index 000000000..ce5776bd6 --- /dev/null +++ b/Source/ExecutionEngine/WriterWrapper.cs @@ -0,0 +1,88 @@ +using System; +using System.IO; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.Boogie; + +class WriterWrapper : TextWriter { + protected TextWriter target; + public override Encoding Encoding => target.Encoding; + + protected WriterWrapper(TextWriter target) { + this.target = target; + } + + public override void Write(char value) { + target.Write(value); + } + + public override void Write(char[] buffer, int index, int count) { + target.Write(buffer, index, count); + } + + public override void Write(ReadOnlySpan buffer) { + target.Write(buffer); + } + + public override void Write(string value) { + target.Write(value); + } + + public override void WriteLine(char[] buffer, int index, int count) { + target.WriteLine(buffer, index, count); + } + + public override void WriteLine(ReadOnlySpan buffer) { + target.WriteLine(buffer); + } + + public override void WriteLine(StringBuilder value) { + target.WriteLine(value); + } + + public override void Write(StringBuilder value) { + target.Write(value); + } + + public override Task WriteAsync(char value) { + return target.WriteAsync(value); + } + + public override Task WriteAsync(char[] buffer, int index, int count) { + return target.WriteAsync(buffer, index, count); + } + + public override Task WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = new()) { + return target.WriteAsync(buffer, cancellationToken); + } + + public override Task WriteAsync(string value) { + return target.WriteAsync(value); + } + + public override Task WriteAsync(StringBuilder value, CancellationToken cancellationToken = new()) { + return target.WriteAsync(value, cancellationToken); + } + + public override Task WriteLineAsync(char value) { + return target.WriteLineAsync(value); + } + + public override Task WriteLineAsync(char[] buffer, int index, int count) { + return target.WriteLineAsync(buffer, index, count); + } + + public override Task WriteLineAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = new()) { + return target.WriteLineAsync(buffer, cancellationToken); + } + + public override Task WriteLineAsync(string value) { + return target.WriteLineAsync(value); + } + + public override Task WriteLineAsync(StringBuilder value, CancellationToken cancellationToken = new()) { + return target.WriteLineAsync(value, cancellationToken); + } +} \ No newline at end of file diff --git a/Source/UnitTests/ExecutionEngineTests/ConcurrentToSequentialWriteManagerTest.cs b/Source/UnitTests/ExecutionEngineTests/ConcurrentToSequentialWriteManagerTest.cs new file mode 100644 index 000000000..9ece8917c --- /dev/null +++ b/Source/UnitTests/ExecutionEngineTests/ConcurrentToSequentialWriteManagerTest.cs @@ -0,0 +1,31 @@ +using System.IO; +using System.Threading.Tasks; +using Microsoft.Boogie; +using NUnit.Framework; + +namespace ExecutionEngineTests; + +[TestFixture] +public class ConcurrentToSequentialWriteManagerTest { + + [Test] + public async Task ThreeConcurrentWriters() { + var writer = new StringWriter(); + var manager = new ConcurrentToSequentialWriteManager(writer); + + var first = manager.AppendWriter(); + var second = manager.AppendWriter(); + var third = manager.AppendWriter(); + + await second.WriteLineAsync("secondLine1"); + await first.WriteLineAsync("firstLine1"); + Assert.AreEqual("firstLine1\n", writer.ToString()); + + await first.DisposeAsync(); + await second.WriteLineAsync("secondLine2"); + await third.WriteLineAsync("thirdLine1"); + Assert.AreEqual("firstLine1\nsecondLine1\nsecondLine2\n", writer.ToString()); + await second.DisposeAsync(); + Assert.AreEqual("firstLine1\nsecondLine1\nsecondLine2\nthirdLine1\n", writer.ToString()); + } +} \ No newline at end of file diff --git a/Test/commandline/SplitOnEveryAssert.bpl b/Test/commandline/SplitOnEveryAssert.bpl index 77e936d5d..640606ff1 100644 --- a/Test/commandline/SplitOnEveryAssert.bpl +++ b/Test/commandline/SplitOnEveryAssert.bpl @@ -2,6 +2,7 @@ // RUN: %boogie /vcsSplitOnEveryAssert /errorTrace:0 /trace "%s" > "%t" // RUN: %OutputCheck --file-to-check "%t" "%s" +// CHECK: Verifying Ex ... // CHECK: checking split 1/12, .* // CHECK: checking split 2/12, .* // CHECK: checking split 3/12, .* @@ -15,7 +16,6 @@ // CHECK: checking split 10/12, .* // CHECK: checking split 11/12, .* // CHECK: checking split 12/12, .* -// CHECK: Verifying Ex ... // CHECK-L: SplitOnEveryAssert.bpl(32,5): Error: This assertion might not hold. procedure Ex() returns (y: int) diff --git a/Test/test0/SplitOnEveryAssert.bpl b/Test/test0/SplitOnEveryAssert.bpl index d6d731264..67a75fb21 100644 --- a/Test/test0/SplitOnEveryAssert.bpl +++ b/Test/test0/SplitOnEveryAssert.bpl @@ -2,6 +2,7 @@ // RUN: %boogie /errorTrace:0 /trace "%s" > "%t" // RUN: %OutputCheck --file-to-check "%t" "%s" +// CHECK: Verifying DoTheSplitting ... // CHECK: checking split 1/12, .* // CHECK: checking split 2/12, .* // CHECK: checking split 3/12, .* @@ -15,7 +16,6 @@ // CHECK: checking split 10/12, .* // CHECK: checking split 11/12, .* // CHECK: checking split 12/12, .* -// CHECK: Verifying DoTheSplitting ... // CHECK-L: SplitOnEveryAssert.bpl(37,5): Error: This assertion might not hold. // Verify the second procedure is NOT split. .* is necessary to match the blank line in-between. From 4811d87bf5f77551ca58672de4affdd04122d3f3 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Thu, 10 Mar 2022 15:35:47 +0100 Subject: [PATCH 19/32] Let InferAndVerify return a Task, and related changes --- Source/BoogieDriver/BoogieDriver.cs | 9 +- Source/Core/Helpers.cs | 4 +- Source/Core/Xml.cs | 24 +- Source/ExecutionEngine/CommandLineOptions.cs | 68 +- Source/ExecutionEngine/ConsolePrinter.cs | 17 +- Source/ExecutionEngine/ExecutionEngine.cs | 596 +++++++++--------- .../ExecutionEngine/ExecutionEngineOptions.cs | 4 +- Source/ExecutionEngine/VerificationResult.cs | 67 +- Source/Houdini/Checker.cs | 2 +- .../ExecutionEngineTests/CancellationTests.cs | 6 +- .../ExecutionEngineTests/GetProverLogs.cs | 1 - Source/VCGeneration/ConditionGeneration.cs | 29 +- Source/VCGeneration/Split.cs | 7 +- Source/VCGeneration/SplitAndVerifyWorker.cs | 3 +- Source/VCGeneration/VCGen.cs | 5 +- 15 files changed, 421 insertions(+), 421 deletions(-) diff --git a/Source/BoogieDriver/BoogieDriver.cs b/Source/BoogieDriver/BoogieDriver.cs index 1f88cd2be..63c84dcf7 100644 --- a/Source/BoogieDriver/BoogieDriver.cs +++ b/Source/BoogieDriver/BoogieDriver.cs @@ -11,11 +11,10 @@ public static int Main(string[] args) { Contract.Requires(cce.NonNullElements(args)); - var options = new CommandLineOptions + var options = new CommandLineOptions(new ConsolePrinter()) { RunningBoogieFromCommandLine = true }; - ExecutionEngine.printer = new ConsolePrinter(options); if (!options.Parse(args)) { @@ -30,7 +29,7 @@ public static int Main(string[] args) if (options.Files.Count == 0) { - ExecutionEngine.printer.ErrorWriteLine(Console.Out, "*** Error: No input files were specified."); + options.Printer.ErrorWriteLine(Console.Out, "*** Error: No input files were specified."); return 1; } @@ -45,7 +44,7 @@ public static int Main(string[] args) string errMsg = options.XmlSink.Open(); if (errMsg != null) { - ExecutionEngine.printer.ErrorWriteLine(Console.Out, "*** Error: " + errMsg); + options.Printer.ErrorWriteLine(Console.Out, "*** Error: " + errMsg); return 1; } } @@ -114,7 +113,7 @@ private static List GetFileList(CommandLineOptions options) if (extension != ".bpl") { - ExecutionEngine.printer.ErrorWriteLine( + options.Printer.ErrorWriteLine( Console.Out, "*** Error: '{0}': Filename extension '{1}' is not supported. Input files must be BoogiePL programs (.bpl).", file, diff --git a/Source/Core/Helpers.cs b/Source/Core/Helpers.cs index bb9024247..6de9f31d2 100644 --- a/Source/Core/Helpers.cs +++ b/Source/Core/Helpers.cs @@ -269,9 +269,7 @@ public static string SubstituteAtPROC(string descName, string fileName) Contract.Requires(fileName != null); Contract.Requires(descName != null); Contract.Ensures(Contract.Result() != null); - System.Text.StringBuilder /*!*/ - sb = - new System.Text.StringBuilder(descName.Length); + var /*!*/ sb = new System.Text.StringBuilder(descName.Length); // quote the name, characters like ^ cause trouble in CMD // while $ could cause trouble in SH foreach (char c in descName) diff --git a/Source/Core/Xml.cs b/Source/Core/Xml.cs index 2a8909554..2b12dcaa1 100644 --- a/Source/Core/Xml.cs +++ b/Source/Core/Xml.cs @@ -44,15 +44,13 @@ public string Open() } cce.BeginExpose(this); - { - XmlWriterSettings settings = new XmlWriterSettings(); - settings.Indent = true; - wr = XmlWriter.Create(filename, settings); - wr.WriteStartDocument(); - wr.WriteStartElement("boogie"); - wr.WriteAttributeString("version", options.VersionNumber); - wr.WriteAttributeString("commandLine", Environment.CommandLine); - } + XmlWriterSettings settings = new XmlWriterSettings(); + settings.Indent = true; + wr = XmlWriter.Create(filename, settings); + wr.WriteStartDocument(); + wr.WriteStartElement("boogie"); + wr.WriteAttributeString("version", options.VersionNumber); + wr.WriteAttributeString("commandLine", Environment.CommandLine); cce.EndExpose(); return null; // success } @@ -82,11 +80,9 @@ public void WriteStartMethod(string methodName, DateTime startTime) Contract.Ensures(IsOpen); Contract.Assert(wr != null); cce.BeginExpose(this); - { - wr.WriteStartElement("method"); - wr.WriteAttributeString("name", methodName); - wr.WriteAttributeString("startTime", startTime.ToString(DateTimeFormatString)); - } + wr.WriteStartElement("method"); + wr.WriteAttributeString("name", methodName); + wr.WriteAttributeString("startTime", startTime.ToString(DateTimeFormatString)); cce.EndExpose(); } diff --git a/Source/ExecutionEngine/CommandLineOptions.cs b/Source/ExecutionEngine/CommandLineOptions.cs index e3c05aaab..f76f165d0 100644 --- a/Source/ExecutionEngine/CommandLineOptions.cs +++ b/Source/ExecutionEngine/CommandLineOptions.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Collections.Specialized; +using System.Diagnostics; using System.Diagnostics.Contracts; using System.IO; @@ -142,11 +143,11 @@ protected virtual bool ParseOption(string name, CommandLineParseState ps) protected virtual string HelpHeader => $"Usage: {ToolName} [ option ... ] [ filename ... ]" + @" - ---- General options ------------------------------------------------------- + ---- General options ------------------------------------------------------- - /version print the " + ToolName + @" version number - /help print this message - /attrHelp print a message about supported declaration attributes"; + /version print the " + ToolName + @" version number + /help print this message + /attrHelp print a message about supported declaration attributes"; protected virtual string HelpBody => ""; @@ -258,25 +259,32 @@ public bool Parse([Captured] string[] /*!*/ args) /// Boogie command-line options (other tools can subclass this class in order to support a /// superset of Boogie's options). /// - public class CommandLineOptions : CommandLineOptionEngine, ExecutionEngineOptions { - + public class CommandLineOptions : CommandLineOptionEngine, ExecutionEngineOptions + { public static CommandLineOptions FromArguments(params string[] arguments) { - var result = new CommandLineOptions(); + return FromArguments(new ConsolePrinter(), arguments); + } + + public static CommandLineOptions FromArguments(OutputPrinter printer, params string[] arguments) + { + var result = new CommandLineOptions(printer); result.Parse(arguments); return result; } - - public CommandLineOptions() - : base("Boogie", "Boogie program verifier") - { + + public CommandLineOptions(OutputPrinter printer) + : this("Boogie", "Boogie program verifier", printer) { } - protected CommandLineOptions(string toolName, string descriptiveName) + protected CommandLineOptions(string toolName, string descriptiveName, OutputPrinter printer) : base(toolName, descriptiveName) { Contract.Requires(toolName != null); Contract.Requires(descriptiveName != null); + Contract.Requires(printer.Options == null); + Printer = printer; + printer.Options = this; } // Flags and arguments @@ -287,7 +295,7 @@ protected CommandLineOptions(string toolName, string descriptiveName) StratifiedInlining > 0 && !StratifiedInliningWithoutModels; public bool ProduceModel => ExplainHoudini || UseProverEvaluate || ExpectingModel; - + public bool RunningBoogieFromCommandLine { get; set; } [ContractInvariantMethod] @@ -312,7 +320,7 @@ public bool EmitDebugInformation get => emitDebugInformation; set => emitDebugInformation = value; } - + public int PrintUnstructured { get => printUnstructured; set => printUnstructured = value; @@ -342,20 +350,20 @@ public bool InstrumentWithAsserts } public string ProverPreamble { get; set; } public bool WarnNotEliminatedVars { get; set; } - + /** * Pruning will remove any top-level Boogie declarations that are not accessible by the implementation that is about to be verified. * * # Why pruning? * Without pruning, a change to any part of a Boogie program has the potential to affect the verification of any other part of the program. - * + * * When pruning is used, a declaration of a Boogie program can be changed with the guarantee that the verification of * implementations that do not depend on the modified declaration, remains unchanged. * * # How to use pruning * Pruning depends on the dependency graph of Boogie declarations. * This graph must contain both incoming and outgoing edges for axioms. - * + * * Outgoing edges for axioms are detected automatically: * an axiom has an outgoing edge to each declaration that it references. * @@ -374,12 +382,12 @@ public bool InstrumentWithAsserts * ensures F(x) - x == x * { } * ``` - * + * * When verifying FMultipliedByTwo, pruning will remove G and its axiom, but not F and its axiom. * * Axioms defined in a uses clause have an incoming edge from the clause's declaration. * Uses clauses can be placed on functions and constants. - * + * * Adding the {:include_dep} attribute to an axiom will give it an incoming edge from each declaration that it references. * The {:include_dep} attribute is useful in a migration scenario. * When turning on pruning in a Boogie program with many axioms, @@ -399,7 +407,7 @@ public bool InstrumentWithAsserts public CoreOptions.InstrumentationPlaces InstrumentInfer { get; set; } = CoreOptions.InstrumentationPlaces.LoopHeaders; public int? RandomSeed { get; set; } - + public bool PrintWithUniqueASTIds { get => printWithUniqueAstIds; set => printWithUniqueAstIds = value; @@ -419,7 +427,7 @@ public bool NormalizeNames get => normalizeNames; set => normalizeNames = value; } - + public bool NormalizeDeclarationOrder { get => normalizeDeclarationOrder; @@ -460,7 +468,7 @@ public bool TraceCachingForDebugging public int /*(0:3)*/ ErrorTrace { get; set; } = 1; - + public bool IntraproceduralInfer { get; set; }= true; public bool ContractInfer { @@ -549,6 +557,9 @@ public string LogPrefix public bool AlwaysAssumeFreeLoopInvariants { get; set; } public ExecutionEngineOptions.ShowEnvironment ShowEnv { get; set; } = ExecutionEngineOptions.ShowEnvironment.DuringPrint; + + public OutputPrinter Printer { get; set; } + public bool ShowVerifiedProcedureCount { get; set; } = true; [ContractInvariantMethod] @@ -564,7 +575,7 @@ void ObjectInvariant3() public int LoopUnrollCount { get; set; } = -1; // -1 means don't unroll loops public bool SoundLoopUnrolling { get; set; } public int PrintErrorModel { get; set; } - public string PrintErrorModelFile { get; set; } + private string printErrorModelFile; public string /*?*/ ModelViewFile { get; set; } @@ -603,6 +614,8 @@ public bool SIBoolControlVC { set => siBoolControlVc = value; } + public TextWriter ModelWriter { get; private set; } + public bool ExpandLambdas { get; set; } = true; // not useful from command line, only to be set to false programatically public bool DoModSetAnalysis { @@ -1054,7 +1067,7 @@ protected override bool ParseOption(string name, CommandLineParseState ps) case "printModelToFile": if (ps.ConfirmArgumentCount(1)) { - PrintErrorModelFile = args[ps.i]; + printErrorModelFile = args[ps.i]; } return true; @@ -1545,11 +1558,12 @@ public override void ApplyDefaultOptions() base.ApplyDefaultOptions(); + // expand macros in filenames, now that LogPrefix is fully determined ExpandFilename(XmlSinkFilename, x => XmlSinkFilename = x, LogPrefix, FileTimestamp); ExpandFilename(PrintFile, x => PrintFile = x, LogPrefix, FileTimestamp); ExpandFilename(ProverLogFilePath, x => ProverLogFilePath = x, LogPrefix, FileTimestamp); - ExpandFilename(PrintErrorModelFile, x => PrintErrorModelFile = x, LogPrefix, FileTimestamp); + ExpandFilename(printErrorModelFile, x => printErrorModelFile = x, LogPrefix, FileTimestamp); Contract.Assume(XmlSink == null); // XmlSink is to be set here if (XmlSinkFilename != null) @@ -1557,6 +1571,10 @@ public override void ApplyDefaultOptions() XmlSink = new XmlSink(this, XmlSinkFilename); } + if (printErrorModelFile != null) { + ModelWriter = new StreamWriter(printErrorModelFile, false); + } + if (TheProverFactory == null) { ProverDllName = "SMTLib"; diff --git a/Source/ExecutionEngine/ConsolePrinter.cs b/Source/ExecutionEngine/ConsolePrinter.cs index 98fb984c0..e91b46fe1 100644 --- a/Source/ExecutionEngine/ConsolePrinter.cs +++ b/Source/ExecutionEngine/ConsolePrinter.cs @@ -6,12 +6,7 @@ namespace Microsoft.Boogie; public class ConsolePrinter : OutputPrinter { - private ExecutionEngineOptions options; - - public ConsolePrinter(ExecutionEngineOptions options) - { - this.options = options; - } + public ExecutionEngineOptions Options { get; set; } public void ErrorWriteLine(TextWriter tw, string s) { @@ -72,7 +67,7 @@ public void AdvisoryWriteLine(string format, params object[] args) /// public void Inform(string s, TextWriter tw) { - if (options.Trace || options.TraceProofObligations) + if (Options.Trace || Options.TraceProofObligations) { tw.WriteLine(s); } @@ -86,14 +81,14 @@ public void WriteTrailer(PipelineStatistics stats) 0 <= stats.TimeoutCount && 0 <= stats.OutOfMemoryCount); Console.WriteLine(); - if (options.ShowVerifiedProcedureCount) + if (Options.ShowVerifiedProcedureCount) { - Console.Write("{0} finished with {1} verified, {2} error{3}", options.DescriptiveToolName, + Console.Write("{0} finished with {1} verified, {2} error{3}", Options.DescriptiveToolName, stats.VerifiedCount, stats.ErrorCount, stats.ErrorCount == 1 ? "" : "s"); } else { - Console.Write("{0} finished with {1} error{2}", options.DescriptiveToolName, stats.ErrorCount, + Console.Write("{0} finished with {1} error{2}", Options.DescriptiveToolName, stats.ErrorCount, stats.ErrorCount == 1 ? "" : "s"); } @@ -159,7 +154,7 @@ public virtual void ReportBplError(IToken tok, string message, bool error, TextW string s; if (tok != null) { - s = string.Format("{0}({1},{2}): {3}", ExecutionEngine.GetFileNameForConsole(options, tok.filename), tok.line, tok.col, + s = string.Format("{0}({1},{2}): {3}", ExecutionEngine.GetFileNameForConsole(Options, tok.filename), tok.line, tok.col, message); } else diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index 8e7a787d4..6a8bde25a 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -18,6 +18,7 @@ namespace Microsoft.Boogie public interface OutputPrinter { + ExecutionEngineOptions Options { get; set; } void ErrorWriteLine(TextWriter tw, string s); void ErrorWriteLine(TextWriter tw, string format, params object[] args); void AdvisoryWriteLine(string format, params object[] args); @@ -172,12 +173,9 @@ protected static string CleanUp(string msg) #endregion - public class ExecutionEngine : IDisposable { - public static OutputPrinter printer; - - public static ErrorInformationFactory errorInformationFactory = new ErrorInformationFactory(); + public static ErrorInformationFactory ErrorInformationFactory { get; } = new(); static int autoRequestIdCount; @@ -220,6 +218,7 @@ public ExecutionEngine(ExecutionEngineOptions options, VerificationResultCache c Options = options; Cache = cache; checkerPool = new CheckerPool(options); + verifyImplementationSemaphore = new SemaphoreSlim(Options.VcsCores); } public static ExecutionEngine CreateWithoutSharedCache(ExecutionEngineOptions options) { @@ -228,6 +227,7 @@ public static ExecutionEngine CreateWithoutSharedCache(ExecutionEngineOptions op public ExecutionEngineOptions Options { get; } private readonly CheckerPool checkerPool; + private readonly SemaphoreSlim verifyImplementationSemaphore; static DateTime FirstRequestStart; @@ -243,9 +243,7 @@ static readonly ConcurrentDictionary static readonly ConcurrentDictionary RequestIdToCancellationTokenSource = new ConcurrentDictionary(); - static ThreadTaskScheduler Scheduler = new ThreadTaskScheduler(16 * 1024 * 1024); - - static TextWriter ModelWriter = null; + static ThreadTaskScheduler LargeStackScheduler = new ThreadTaskScheduler(16 * 1024 * 1024); public bool ProcessFiles(IList fileNames, bool lookForSnapshots = true, string programId = null) { @@ -288,8 +286,8 @@ public bool ProcessProgram(Program program, string bplFileName, string programId PrintBplFile(Options.PrintFile, program, false, true, Options.PrettyPrint); } - PipelineOutcome oc = ResolveAndTypecheck(program, bplFileName, out var civlTypeChecker); - if (oc != PipelineOutcome.ResolvedAndTypeChecked) { + PipelineOutcome outcome = ResolveAndTypecheck(program, bplFileName, out var civlTypeChecker); + if (outcome != PipelineOutcome.ResolvedAndTypeChecked) { return true; } @@ -316,11 +314,11 @@ public bool ProcessProgram(Program program, string bplFileName, string programId Inline(program); var stats = new PipelineStatistics(); - oc = InferAndVerify(program, stats, 1 < Options.VerifySnapshots ? programId : null); - switch (oc) { + outcome = InferAndVerify(program, stats, 1 < Options.VerifySnapshots ? programId : null).Result; + switch (outcome) { case PipelineOutcome.Done: case PipelineOutcome.VerificationCompleted: - printer.WriteTrailer(stats); + Options.Printer.WriteTrailer(stats); return true; case PipelineOutcome.FatalError: return false; @@ -469,7 +467,7 @@ public Program ParseBoogieProgram(IList fileNames, bool suppressTraceOut } catch (IOException e) { - printer.ErrorWriteLine(Console.Out, "Error opening file \"{0}\": {1}", + Options.Printer.ErrorWriteLine(Console.Out, "Error opening file \"{0}\": {1}", GetFileNameForConsole(Options, bplFileName), e.Message); okay = false; } @@ -669,7 +667,7 @@ public void Inline(Program program) /// - VerificationCompleted if inference and verification completed, in which the out /// parameters contain meaningful values /// - public PipelineOutcome InferAndVerify( + public async Task InferAndVerify( Program program, PipelineStatistics stats, string programId = null, @@ -680,15 +678,8 @@ public PipelineOutcome InferAndVerify( Contract.Ensures(0 <= Contract.ValueAtReturn(out stats.InconclusiveCount) && 0 <= Contract.ValueAtReturn(out stats.TimeoutCount)); - if (requestId == null) - { - requestId = FreshRequestId(); - } + requestId ??= FreshRequestId(); - if (Options.PrintErrorModelFile != null) - { - ExecutionEngine.ModelWriter = new StreamWriter(Options.PrintErrorModelFile, false); - } var start = DateTime.UtcNow; @@ -708,15 +699,11 @@ public PipelineOutcome InferAndVerify( #endregion - #region Infer invariants using Abstract Interpretation - if (Options.UseAbstractInterpretation) { new AbstractInterpretation.NativeAbstractInterpretation(Options).RunAbstractInterpretation(program); } - #endregion - #region Do some post-abstract-interpretation preprocessing on the program (e.g., loop unrolling) if (Options.LoopUnrollCount != -1) @@ -742,15 +729,11 @@ public PipelineOutcome InferAndVerify( return PipelineOutcome.Done; } - #region Run Houdini and verify - if (Options.ContractInfer) { return RunHoudini(program, stats, er); } - #endregion - var stablePrioritizedImpls = GetPrioritizedImplementations(program); if (1 < Options.VerifySnapshots) @@ -759,7 +742,7 @@ public PipelineOutcome InferAndVerify( out stats.CachingActionCounts); } - var outcome = VerifyEachImplementation(program, stats, programId, er, requestId, stablePrioritizedImpls, extractLoopMappingInfo); + var outcome = await VerifyEachImplementation(program, stats, programId, er, requestId, stablePrioritizedImpls, extractLoopMappingInfo); if (1 < Options.VerifySnapshots && programId != null) { @@ -784,8 +767,7 @@ private Implementation[] GetPrioritizedImplementations(Program program) OtherDefinitionAxiomsCollector.Collect(Options, program.Axioms); DependencyCollector.Collect(Options, program); stablePrioritizedImpls = impls.OrderByDescending( - impl => impl.Priority != 1 ? impl.Priority : Cache.VerificationPriority(impl, Options.RunDiagnosticsOnTimeout)) - .ToArray(); + impl => impl.Priority != 1 ? impl.Priority : Cache.VerificationPriority(impl, Options.RunDiagnosticsOnTimeout)).ToArray(); } else { stablePrioritizedImpls = impls.OrderByDescending(impl => impl.Priority).ToArray(); } @@ -793,78 +775,36 @@ private Implementation[] GetPrioritizedImplementations(Program program) return stablePrioritizedImpls; } - private PipelineOutcome VerifyEachImplementation(Program program, PipelineStatistics stats, string programId, - ErrorReporterDelegate er, string requestId, Implementation[] stablePrioritizedImpls, + private async Task VerifyEachImplementation( + Program program, PipelineStatistics stats, + string programId, ErrorReporterDelegate er, string requestId, Implementation[] stablePrioritizedImpls, Dictionary> extractLoopMappingInfo) { + var consoleCollector = new ConcurrentToSequentialWriteManager(Console.Out); program.DeclarationDependencies = Prune.ComputeDeclarationDependencies(Options, program); - var outputCollector = new ConcurrentToSequentialWriteManager(Console.Out); - var outcome = PipelineOutcome.VerificationCompleted; - - try { - var cts = new CancellationTokenSource(); - RequestIdToCancellationTokenSource.AddOrUpdate(requestId, cts, (k, ov) => cts); - - var tasks = new Task[stablePrioritizedImpls.Length]; - // We use this semaphore to limit the number of tasks that are currently executing. - var semaphore = new SemaphoreSlim(Options.VcsCores); - - // Create a task per implementation. - for (int i = 0; i < stablePrioritizedImpls.Length; i++) { - var taskIndex = i; - var id = stablePrioritizedImpls[taskIndex].Id; - - if (ImplIdToCancellationTokenSource.TryGetValue(id, out var old)) { - old.Cancel(); - } - - ImplIdToCancellationTokenSource.AddOrUpdate(id, cts, (k, ov) => cts); - - var t = new Task((dummy) => - { - try { - if (outcome == PipelineOutcome.FatalError) { - return; - } - if (cts.Token.IsCancellationRequested) { - cts.Token.ThrowIfCancellationRequested(); - } + var cts = new CancellationTokenSource(); + RequestIdToCancellationTokenSource.AddOrUpdate(requestId, cts, (k, ov) => cts); - using var implementationWriter = outputCollector.AppendWriter(); - VerifyImplementation(program, stats, er, requestId, extractLoopMappingInfo, stablePrioritizedImpls, - taskIndex, implementationWriter, programId); - ImplIdToCancellationTokenSource.TryRemove(id, out old); - } - finally { - semaphore.Release(); - } - }, cts.Token, TaskCreationOptions.None); - tasks[taskIndex] = t; - } - - // Execute the tasks. - int j = 0; - for (; j < stablePrioritizedImpls.Length && outcome != PipelineOutcome.FatalError; j++) { - try { - semaphore.Wait(cts.Token); - } - catch (OperationCanceledException) { - break; - } + var tasks = stablePrioritizedImpls.Select(async (impl, index) => { + await using var taskWriter = consoleCollector.AppendWriter(); + var result = await VerifyImplementationWithLargeStackScheduler(program, stats, programId, er, requestId, + stablePrioritizedImpls, extractLoopMappingInfo, cts, index, taskWriter); + return result; + }).ToList(); + var outcome = PipelineOutcome.VerificationCompleted; - tasks[j].Start(Scheduler); + try { + await Task.WhenAll(tasks); + foreach (var task in tasks) { + task.Result.ProcessXml(this); } - - // Don't wait for tasks that haven't been started yet. - tasks = tasks.Take(j).ToArray(); - Task.WaitAll(tasks); } catch (AggregateException ae) { ae.Flatten().Handle(e => { if (e is ProverException) { - printer.ErrorWriteLine(Console.Out, "Fatal Error: ProverException: {0}", e.Message); + Options.Printer.ErrorWriteLine(Console.Out, "Fatal Error: ProverException: {0}", e.Message); outcome = PipelineOutcome.FatalError; return true; } @@ -882,15 +822,53 @@ private PipelineOutcome VerifyEachImplementation(Program program, PipelineStatis } if (Options.PrintNecessaryAssumes && program.NecessaryAssumes.Any()) { - Console.WriteLine("Necessary assume command(s): {0}", string.Join(", ", program.NecessaryAssumes)); + Console.WriteLine("Necessary assume command(s): {0}", string.Join(", ", program.NecessaryAssumes.OrderBy(s => s))); } cce.NonNull(Options.TheProverFactory).Close(); return outcome; + + } + + async Task VerifyImplementationWithLargeStackScheduler( + Program program, PipelineStatistics stats, + string programId, ErrorReporterDelegate er, string requestId, Implementation[] stablePrioritizedImpls, + Dictionary> extractLoopMappingInfo, + CancellationTokenSource cts, + int index, TextWriter taskWriter) + { + var implementation = stablePrioritizedImpls[index]; + var id = implementation.Id; + if (ImplIdToCancellationTokenSource.TryGetValue(id, out var old)) { + old.Cancel(); + } + + await verifyImplementationSemaphore.WaitAsync(cts.Token); + try { + + ImplIdToCancellationTokenSource.AddOrUpdate(id, cts, (k, ov) => cts); + + var coreTask = new Task(() => VerifyImplementation(program, stats, er, requestId, + extractLoopMappingInfo, implementation, + programId, taskWriter).Result, cts.Token, TaskCreationOptions.None); + + coreTask.Start(LargeStackScheduler); + var verificationResult = await coreTask; + var output = verificationResult.GetOutput(Options.Printer, this, stats, er, implementation); + + await taskWriter.WriteAsync(output); + return verificationResult; + } + finally { + taskWriter.Close(); + verifyImplementationSemaphore.Release(); + ImplIdToCancellationTokenSource.TryRemove(id, out old); + } } - private void TraceCachingForBenchmarking(PipelineStatistics stats, string requestId, DateTime start) + private void TraceCachingForBenchmarking(PipelineStatistics stats, + string requestId, DateTime start) { if (0 <= Options.VerifySnapshots && Options.TraceCachingForBenchmarking) { var end = DateTime.UtcNow; @@ -949,126 +927,132 @@ private static void CleanupRequest(string requestId) } } - - private void VerifyImplementation(Program program, PipelineStatistics stats, ErrorReporterDelegate er, + private async Task VerifyImplementation( + Program program, + PipelineStatistics stats, + ErrorReporterDelegate er, string requestId, Dictionary> extractLoopMappingInfo, - Implementation[] stablePrioritizedImpls, int index, TextWriter output, string programId) + Implementation implementation, + string programId, + TextWriter traceWriter) { - Implementation impl = stablePrioritizedImpls[index]; - - printer.Inform("", output); // newline - printer.Inform($"Verifying {impl.Name} ...", output); - var verificationResult = GetCachedVerificationResult(impl, output); + VerificationResult verificationResult = GetCachedVerificationResult(implementation, traceWriter); + if (verificationResult != null) { + UpdateCachedStatistics(stats, verificationResult.Outcome, verificationResult.Errors); + return verificationResult; + } - var wasCached = true; - if (verificationResult == null) { - wasCached = false; + Options.Printer.Inform("", traceWriter); // newline + Options.Printer.Inform($"Verifying {implementation.Name} ...", traceWriter); - verificationResult = VerifyImplementationWithoutCaching(program, stats, er, requestId, extractLoopMappingInfo, programId, impl, output); + verificationResult = await VerifyImplementationWithoutCaching(program, stats, er, requestId, + extractLoopMappingInfo, programId, implementation, traceWriter); - if (0 < Options.VerifySnapshots && !string.IsNullOrEmpty(impl.Checksum)) - { - Cache.Insert(impl, verificationResult); - } + if (0 < Options.VerifySnapshots && !string.IsNullOrEmpty(implementation.Checksum)) + { + Cache.Insert(implementation, verificationResult); } - verificationResult.Emit(this, stats, er, output, impl, wasCached); - if (verificationResult.Outcome == VCGen.Outcome.Errors || Options.Trace) { - output.Flush(); - } + return verificationResult; } - private VerificationResult GetCachedVerificationResult(Implementation impl, TextWriter output) + private async Task VerifyImplementationWithoutCaching(Program program, + PipelineStatistics stats, ErrorReporterDelegate er, string requestId, + Dictionary> extractLoopMappingInfo, + string programId, Implementation impl, TextWriter traceWriter) { - VerificationResult verificationResult = null; - int priority = 0; - if (0 < Options.VerifySnapshots) { - var cachedResults = Cache.Lookup(impl, Options.RunDiagnosticsOnTimeout, out priority); - if (cachedResults != null && priority == Priority.SKIP) { - printer.Inform(string.Format("Retrieving cached verification result for implementation {0}...", impl.Name), - output); - if (Options.VerifySnapshots < 3 || - cachedResults.Outcome == ConditionGeneration.Outcome.Correct) { - verificationResult = cachedResults; - } - } - } + var verificationResult = new VerificationResult(requestId, impl, programId); - return verificationResult; - } + using var vcgen = new VCGen(program, checkerPool); - private VerificationResult VerifyImplementationWithoutCaching(Program program, PipelineStatistics stats, - ErrorReporterDelegate er, string requestId, Dictionary> extractLoopMappingInfo, - string programId, Implementation impl, TextWriter output) - { - VerificationResult verificationResult = new VerificationResult(requestId, impl, programId); - - using (var vcgen = CreateVCGen(program, checkerPool)) { - vcgen.CachingActionCounts = stats.CachingActionCounts; - verificationResult.ProofObligationCountBefore = vcgen.CumulativeAssertionCount; - verificationResult.Start = DateTime.UtcNow; - - try { - var cancellationToken = RequestIdToCancellationTokenSource[requestId].Token; - verificationResult.Outcome = - vcgen.VerifyImplementation(new ImplementationRun(impl, output), out verificationResult.Errors, - out verificationResult.VCResults, requestId, cancellationToken); - if (Options.ExtractLoops && verificationResult.Errors != null) { - if (vcgen is VCGen vcg) { - for (int i = 0; i < verificationResult.Errors.Count; i++) { - verificationResult.Errors[i] = vcg.extractLoopTrace(verificationResult.Errors[i], impl.Name, - program, extractLoopMappingInfo); - } + vcgen.CachingActionCounts = stats.CachingActionCounts; + verificationResult.ProofObligationCountBefore = vcgen.CumulativeAssertionCount; + verificationResult.Start = DateTime.UtcNow; + + try { + var cancellationToken = RequestIdToCancellationTokenSource[requestId].Token; + (verificationResult.Outcome, verificationResult.Errors, verificationResult.VCResults) = + await vcgen.VerifyImplementation(new ImplementationRun(impl, traceWriter), requestId, cancellationToken); + if (Options.ExtractLoops && verificationResult.Errors != null) { + if (vcgen is VCGen vcg) { + for (int i = 0; i < verificationResult.Errors.Count; i++) { + verificationResult.Errors[i] = vcg.extractLoopTrace(verificationResult.Errors[i], impl.Name, + program, extractLoopMappingInfo); } } } - catch (VCGenException e) { - var errorInfo = errorInformationFactory.CreateErrorInformation(impl.tok, - String.Format("{0} (encountered in implementation {1}).", e.Message, impl.Name), requestId, "Error"); - errorInfo.ImplementationName = impl.Name; - if (er != null) { - lock (er) { - er(errorInfo); - } + } + catch (VCGenException e) { + var errorInfo = ErrorInformationFactory.CreateErrorInformation(impl.tok, + $"{e.Message} (encountered in implementation {impl.Name}).", requestId, "Error"); + errorInfo.ImplementationName = impl.Name; + verificationResult.ErrorBeforeVerification = errorInfo; + if (er != null) { + lock (er) { + er(errorInfo); } - - verificationResult.ErrorBeforeVerification = errorInfo; - verificationResult.Errors = null; - verificationResult.Outcome = VCGen.Outcome.Inconclusive; - } - catch (ProverDiedException) { - throw; } - catch (UnexpectedProverOutputException upo) { - printer.AdvisoryWriteLine("Advisory: {0} SKIPPED because of internal error: unexpected prover output: {1}", - impl.Name, upo.Message); - verificationResult.Errors = null; - verificationResult.Outcome = VCGen.Outcome.Inconclusive; - } - catch (AggregateException ae) { - ae.Flatten().Handle(e => - { - if (e is IOException) { - printer.AdvisoryWriteLine("Advisory: {0} SKIPPED due to I/O exception: {1}", - impl.Name, e.Message); - verificationResult.Errors = null; - verificationResult.Outcome = VCGen.Outcome.SolverException; - return true; - } - return false; - }); - } + verificationResult.Errors = null; + verificationResult.Outcome = VCGen.Outcome.Inconclusive; + } + catch (ProverDiedException) { + throw; + } + catch (UnexpectedProverOutputException upo) { + Options.Printer.AdvisoryWriteLine( + "Advisory: {0} SKIPPED because of internal error: unexpected prover output: {1}", + impl.Name, upo.Message); + verificationResult.Errors = null; + verificationResult.Outcome = VCGen.Outcome.Inconclusive; + } + catch (AggregateException ae) { + ae.Flatten().Handle(e => + { + // TODO Do we need to move this into a catch? + if (e is IOException) { + Options.Printer.AdvisoryWriteLine("Advisory: {0} SKIPPED due to I/O exception: {1}", + impl.Name, e.Message); + verificationResult.Errors = null; + verificationResult.Outcome = VCGen.Outcome.SolverException; + return true; + } - verificationResult.ProofObligationCountAfter = vcgen.CumulativeAssertionCount; - verificationResult.End = DateTime.UtcNow; - verificationResult.ResourceCount = vcgen.ResourceCount; + return false; + }); } + verificationResult.ProofObligationCountAfter = vcgen.CumulativeAssertionCount; + verificationResult.End = DateTime.UtcNow; + verificationResult.ResourceCount = vcgen.ResourceCount; + return verificationResult; } - private static ConditionGeneration CreateVCGen(Program program, CheckerPool checkerPool) + private VerificationResult GetCachedVerificationResult(Implementation impl, TextWriter output) + { + if (0 >= Options.VerifySnapshots) + { + return null; + } + + var cachedResults = Cache.Lookup(impl, Options.RunDiagnosticsOnTimeout, out var priority); + if (cachedResults == null || priority != Priority.SKIP) + { + return null; + } + + Options.Printer.Inform($"Retrieving cached verification result for implementation {impl.Name}...", output); + if (Options.VerifySnapshots < 3 || + cachedResults.Outcome == ConditionGeneration.Outcome.Correct) { + return cachedResults; + } + + return null; + } + + + private ConditionGeneration CreateVCGen(Program program) { return new VCGen(program, checkerPool); } @@ -1120,8 +1104,8 @@ private PipelineOutcome RunHoudini(Program program, PipelineStatistics stats, Er foreach (Houdini.VCGenOutcome x in outcome.implementationOutcomes.Values) { - ProcessOutcome(x.outcome, x.errors, "", stats, Console.Out, Options.TimeLimit, er); - ProcessErrors(x.errors, x.outcome, Console.Out, er); + ProcessOutcome(Options.Printer, x.outcome, x.errors, "", stats, Console.Out, Options.TimeLimit, er); + ProcessErrors(Options.Printer, x.errors, x.outcome, Console.Out, er); } return PipelineOutcome.Done; @@ -1173,8 +1157,8 @@ private PipelineOutcome RunStagedHoudini(Program program, PipelineStatistics sta foreach (Houdini.VCGenOutcome x in outcome.implementationOutcomes.Values) { - ProcessOutcome(x.outcome, x.errors, "", stats, Console.Out, Options.TimeLimit, er); - ProcessErrors(x.errors, x.outcome, Console.Out, er); + ProcessOutcome(Options.Printer, x.outcome, x.errors, "", stats, Console.Out, Options.TimeLimit, er); + ProcessErrors(Options.Printer, x.errors, x.outcome, Console.Out, er); } return PipelineOutcome.Done; @@ -1182,43 +1166,22 @@ private PipelineOutcome RunStagedHoudini(Program program, PipelineStatistics sta #endregion - - public string TimeIndication(VerificationResult verificationResult) - { - var result = ""; - if (Options.Trace) - { - result = string.Format(" [{0:F3} s, solver resource count: {1}, {2} proof obligation{3}] ", - (verificationResult.End - verificationResult.Start).TotalSeconds, - verificationResult.ResourceCount, - verificationResult.ProofObligationCount, - verificationResult.ProofObligationCount == 1 ? "" : "s"); - } - else if (Options.TraceProofObligations) - { - result = string.Format(" [{0} proof obligation{1}] ", verificationResult.ProofObligationCount, - verificationResult.ProofObligationCount == 1 ? "" : "s"); - } - - return result; - } - - - public void ProcessOutcome(VC.VCGen.Outcome outcome, List errors, string timeIndication, + public void ProcessOutcome(OutputPrinter printer, ConditionGeneration.Outcome outcome, List errors, string timeIndication, PipelineStatistics stats, TextWriter tw, uint timeLimit, ErrorReporterDelegate er = null, string implName = null, - IToken implTok = null, string requestId = null, string msgIfVerifies = null, bool wasCached = false) + IToken implTok = null, string requestId = null, string msgIfVerifies = null) { Contract.Requires(stats != null); - UpdateStatistics(stats, outcome, errors, wasCached); + UpdateStatistics(stats, outcome, errors); printer.Inform(timeIndication + OutcomeIndication(outcome, errors), tw); - ReportOutcome(outcome, er, implName, implTok, requestId, msgIfVerifies, tw, timeLimit, errors); - } + ReportOutcome(printer, outcome, er, implName, implTok, requestId, msgIfVerifies, tw, timeLimit, errors); + } - private void ReportOutcome(VC.VCGen.Outcome outcome, ErrorReporterDelegate er, string implName, + public void ReportOutcome(OutputPrinter printer, + ConditionGeneration.Outcome outcome, ErrorReporterDelegate er, string implName, IToken implTok, string requestId, string msgIfVerifies, TextWriter tw, uint timeLimit, List errors) { ErrorInformation errorInfo = null; @@ -1228,12 +1191,11 @@ private void ReportOutcome(VC.VCGen.Outcome outcome, ErrorReporterDelegate er, s case VCGen.Outcome.Correct: if (msgIfVerifies != null) { - tw.WriteLine(msgIfVerifies); + tw.WriteLine(msgIfVerifies); } break; case VCGen.Outcome.ReachedBound: - tw.WriteLine(string.Format("Stratified Inlining: Reached recursion bound of {0}", - Options.RecursionBound)); + tw.WriteLine($"Stratified Inlining: Reached recursion bound of {Options.RecursionBound}"); break; case VCGen.Outcome.Errors: case VCGen.Outcome.TimedOut: @@ -1242,7 +1204,7 @@ private void ReportOutcome(VC.VCGen.Outcome outcome, ErrorReporterDelegate er, s if (outcome == ConditionGeneration.Outcome.TimedOut || (errors != null && errors.Any(e => e.IsAuxiliaryCexForDiagnosingTimeouts))) { - errorInfo = errorInformationFactory.CreateErrorInformation(implTok, + errorInfo = ExecutionEngine.ErrorInformationFactory.CreateErrorInformation(implTok, string.Format("Verification of '{1}' timed out after {0} seconds", timeLimit, implName), requestId); } @@ -1254,8 +1216,7 @@ private void ReportOutcome(VC.VCGen.Outcome outcome, ErrorReporterDelegate er, s timedOutAssertions.Sort(cmpr); if (0 < timedOutAssertions.Count) { - errorInfo.Msg += string.Format(" with {0} check(s) that timed out individually", - timedOutAssertions.Count); + errorInfo.Msg += $" with {timedOutAssertions.Count} check(s) that timed out individually"; } foreach (Counterexample error in timedOutAssertions) @@ -1297,7 +1258,7 @@ private void ReportOutcome(VC.VCGen.Outcome outcome, ErrorReporterDelegate er, s case VCGen.Outcome.OutOfResource: if (implName != null && implTok != null) { - errorInfo = errorInformationFactory.CreateErrorInformation(implTok, + errorInfo = ExecutionEngine.ErrorInformationFactory.CreateErrorInformation(implTok, "Verification out of resource (" + implName + ")", requestId); } @@ -1305,7 +1266,7 @@ private void ReportOutcome(VC.VCGen.Outcome outcome, ErrorReporterDelegate er, s case VCGen.Outcome.OutOfMemory: if (implName != null && implTok != null) { - errorInfo = errorInformationFactory.CreateErrorInformation(implTok, + errorInfo = ExecutionEngine.ErrorInformationFactory.CreateErrorInformation(implTok, "Verification out of memory (" + implName + ")", requestId); } @@ -1313,7 +1274,7 @@ private void ReportOutcome(VC.VCGen.Outcome outcome, ErrorReporterDelegate er, s case VCGen.Outcome.SolverException: if (implName != null && implTok != null) { - errorInfo = errorInformationFactory.CreateErrorInformation(implTok, + errorInfo = ExecutionEngine.ErrorInformationFactory.CreateErrorInformation(implTok, "Verification encountered solver exception (" + implName + ")", requestId); } @@ -1322,7 +1283,7 @@ private void ReportOutcome(VC.VCGen.Outcome outcome, ErrorReporterDelegate er, s case VCGen.Outcome.Inconclusive: if (implName != null && implTok != null) { - errorInfo = errorInformationFactory.CreateErrorInformation(implTok, + errorInfo = ExecutionEngine.ErrorInformationFactory.CreateErrorInformation(implTok, "Verification inconclusive (" + implName + ")", requestId); } @@ -1386,8 +1347,7 @@ private static string OutcomeIndication(VC.VCGen.Outcome outcome, List errors, bool wasCached) + private static void UpdateStatistics(PipelineStatistics stats, VC.VCGen.Outcome outcome, List errors) { Contract.Requires(stats != null); @@ -1398,129 +1358,143 @@ private static void UpdateStatistics(PipelineStatistics stats, VC.VCGen.Outcome throw new cce.UnreachableException(); case VCGen.Outcome.ReachedBound: Interlocked.Increment(ref stats.VerifiedCount); - if (wasCached) - { - Interlocked.Increment(ref stats.CachedVerifiedCount); - } break; case VCGen.Outcome.Correct: Interlocked.Increment(ref stats.VerifiedCount); - if (wasCached) - { - Interlocked.Increment(ref stats.CachedVerifiedCount); - } break; case VCGen.Outcome.TimedOut: Interlocked.Increment(ref stats.TimeoutCount); - if (wasCached) - { - Interlocked.Increment(ref stats.CachedTimeoutCount); - } break; case VCGen.Outcome.OutOfResource: Interlocked.Increment(ref stats.OutOfResourceCount); - if (wasCached) - { - Interlocked.Increment(ref stats.CachedOutOfResourceCount); - } break; case VCGen.Outcome.OutOfMemory: Interlocked.Increment(ref stats.OutOfMemoryCount); - if (wasCached) - { - Interlocked.Increment(ref stats.CachedOutOfMemoryCount); - } break; case VCGen.Outcome.SolverException: Interlocked.Increment(ref stats.SolverExceptionCount); - if (wasCached) - { - Interlocked.Increment(ref stats.CachedSolverExceptionCount); - } break; case VCGen.Outcome.Inconclusive: Interlocked.Increment(ref stats.InconclusiveCount); - if (wasCached) - { - Interlocked.Increment(ref stats.CachedInconclusiveCount); - } break; case VCGen.Outcome.Errors: - int cnt = errors.Where(e => !e.IsAuxiliaryCexForDiagnosingTimeouts).Count(); + int cnt = errors.Count(e => !e.IsAuxiliaryCexForDiagnosingTimeouts); Interlocked.Add(ref stats.ErrorCount, cnt); - if (wasCached) - { - Interlocked.Add(ref stats.CachedErrorCount, cnt); - } break; } } + private static void UpdateCachedStatistics(PipelineStatistics stats, VC.VCGen.Outcome outcome, List errors) { + Contract.Requires(stats != null); + + switch (outcome) + { + default: + Contract.Assert(false); // unexpected outcome + throw new cce.UnreachableException(); + case VCGen.Outcome.ReachedBound: + Interlocked.Increment(ref stats.CachedVerifiedCount); + + break; + case VCGen.Outcome.Correct: + Interlocked.Increment(ref stats.CachedVerifiedCount); - public void ProcessErrors(List errors, VC.VCGen.Outcome outcome, TextWriter tw, + break; + case VCGen.Outcome.TimedOut: + Interlocked.Increment(ref stats.CachedTimeoutCount); + + break; + case VCGen.Outcome.OutOfResource: + Interlocked.Increment(ref stats.CachedOutOfResourceCount); + + break; + case VCGen.Outcome.OutOfMemory: + Interlocked.Increment(ref stats.CachedOutOfMemoryCount); + + break; + case VCGen.Outcome.SolverException: + Interlocked.Increment(ref stats.CachedSolverExceptionCount); + + break; + case VCGen.Outcome.Inconclusive: + Interlocked.Increment(ref stats.CachedInconclusiveCount); + + break; + case VCGen.Outcome.Errors: + int cnt = errors.Count(e => !e.IsAuxiliaryCexForDiagnosingTimeouts); + Interlocked.Add(ref stats.CachedErrorCount, cnt); + + break; + } + } + + public void ProcessErrors(OutputPrinter printer, + List errors, + ConditionGeneration.Outcome outcome, TextWriter tw, ErrorReporterDelegate er, Implementation impl = null) { - var implName = impl != null ? impl.Name : null; + var implName = impl?.Name; + + if (errors == null) + { + return; + } - if (errors != null) + errors.Sort(new CounterexampleComparer()); + foreach (Counterexample error in errors) { - errors.Sort(new CounterexampleComparer()); - foreach (Counterexample error in errors) + if (error.IsAuxiliaryCexForDiagnosingTimeouts) { - if (error.IsAuxiliaryCexForDiagnosingTimeouts) - { - continue; - } + continue; + } - var errorInfo = CreateErrorInformation(error, outcome); - errorInfo.ImplementationName = implName; + var errorInfo = CreateErrorInformation(error, outcome); + errorInfo.ImplementationName = implName; - if (Options.XmlSink != null) + if (Options.XmlSink != null) + { + WriteErrorInformationToXmlSink(Options.XmlSink, errorInfo, error.Trace); + } + + if (Options.ErrorTrace > 0) + { + errorInfo.Out.WriteLine("Execution trace:"); + error.Print(4, errorInfo.Out, b => { errorInfo.AddAuxInfo(b.tok, b.Label, "Execution trace"); }); + if (Options.EnhancedErrorMessages == 1 && error.AugmentedTrace != null && error.AugmentedTrace.Count > 0) { - WriteErrorInformationToXmlSink(Options.XmlSink, errorInfo, error.Trace); + errorInfo.Out.WriteLine("Augmented execution trace:"); + error.AugmentedTrace.Iter(elem => errorInfo.Out.Write(elem)); } - - if (Options.ErrorTrace > 0) + if (Options.PrintErrorModel >= 1 && error.Model != null) { - errorInfo.Out.WriteLine("Execution trace:"); - error.Print(4, errorInfo.Out, b => { errorInfo.AddAuxInfo(b.tok, b.Label, "Execution trace"); }); - if (Options.EnhancedErrorMessages == 1 && error.AugmentedTrace != null && error.AugmentedTrace.Count > 0) - { - errorInfo.Out.WriteLine("Augmented execution trace:"); - error.AugmentedTrace.Iter(elem => errorInfo.Out.Write(elem)); - } - if (Options.PrintErrorModel >= 1 && error.Model != null) - { - error.Model.Write(ExecutionEngine.ModelWriter == null ? errorInfo.Out : ExecutionEngine.ModelWriter); - } + error.Model.Write(Options.ModelWriter ?? errorInfo.Out); } + } - if (Options.ModelViewFile != null) { - error.PrintModel(errorInfo.Model, error); - } + if (Options.ModelViewFile != null) { + error.PrintModel(errorInfo.Model, error); + } - printer.WriteErrorInformation(errorInfo, tw); + printer.WriteErrorInformation(errorInfo, tw); - if (er != null) + if (er != null) + { + lock (er) { - lock (er) - { - er(errorInfo); - } + er(errorInfo); } } } } - private ErrorInformation CreateErrorInformation(Counterexample error, VC.VCGen.Outcome outcome) { ErrorInformation errorInfo; @@ -1546,7 +1520,7 @@ private ErrorInformation CreateErrorInformation(Counterexample error, VC.VCGen.O { if (callError.FailingRequires.ErrorMessage == null || Options.ForceBplErrors) { - errorInfo = errorInformationFactory.CreateErrorInformation(callError.FailingCall.tok, + errorInfo = ErrorInformationFactory.CreateErrorInformation(callError.FailingCall.tok, callError.FailingCall.ErrorData as string ?? callError.FailingCall.Description.FailureDescription, callError.RequestId, callError.OriginalRequestId, cause); errorInfo.Kind = ErrorKind.Precondition; @@ -1556,7 +1530,7 @@ private ErrorInformation CreateErrorInformation(Counterexample error, VC.VCGen.O } else { - errorInfo = errorInformationFactory.CreateErrorInformation(null, + errorInfo = ExecutionEngine.ErrorInformationFactory.CreateErrorInformation(null, callError.FailingRequires.ErrorMessage, callError.RequestId, callError.OriginalRequestId); } @@ -1565,7 +1539,7 @@ private ErrorInformation CreateErrorInformation(Counterexample error, VC.VCGen.O { if (returnError.FailingEnsures.ErrorMessage == null || Options.ForceBplErrors) { - errorInfo = errorInformationFactory.CreateErrorInformation(returnError.FailingReturn.tok, + errorInfo = ErrorInformationFactory.CreateErrorInformation(returnError.FailingReturn.tok, returnError.FailingReturn.Description.FailureDescription, returnError.RequestId, returnError.OriginalRequestId, cause); errorInfo.Kind = ErrorKind.Postcondition; @@ -1575,7 +1549,7 @@ private ErrorInformation CreateErrorInformation(Counterexample error, VC.VCGen.O } else { - errorInfo = errorInformationFactory.CreateErrorInformation(null, + errorInfo = ExecutionEngine.ErrorInformationFactory.CreateErrorInformation(null, returnError.FailingEnsures.ErrorMessage, returnError.RequestId, returnError.OriginalRequestId); } @@ -1586,7 +1560,7 @@ private ErrorInformation CreateErrorInformation(Counterexample error, VC.VCGen.O var assertError = (AssertCounterexample)error; if (assertError.FailingAssert is LoopInitAssertCmd or LoopInvMaintainedAssertCmd) { - errorInfo = errorInformationFactory.CreateErrorInformation(assertError.FailingAssert.tok, + errorInfo = ErrorInformationFactory.CreateErrorInformation(assertError.FailingAssert.tok, assertError.FailingAssert.Description.FailureDescription, assertError.RequestId, assertError.OriginalRequestId, cause); errorInfo.Kind = assertError.FailingAssert is LoopInitAssertCmd ? @@ -1603,13 +1577,13 @@ private ErrorInformation CreateErrorInformation(Counterexample error, VC.VCGen.O { string msg = assertError.FailingAssert.ErrorData as string ?? assertError.FailingAssert.Description.FailureDescription; - errorInfo = errorInformationFactory.CreateErrorInformation(assertError.FailingAssert.tok, msg, + errorInfo = ErrorInformationFactory.CreateErrorInformation(assertError.FailingAssert.tok, msg, assertError.RequestId, assertError.OriginalRequestId, cause); errorInfo.Kind = ErrorKind.Assertion; } else { - errorInfo = errorInformationFactory.CreateErrorInformation(null, + errorInfo = ExecutionEngine.ErrorInformationFactory.CreateErrorInformation(null, assertError.FailingAssert.ErrorMessage, assertError.RequestId, assertError.OriginalRequestId); } diff --git a/Source/ExecutionEngine/ExecutionEngineOptions.cs b/Source/ExecutionEngine/ExecutionEngineOptions.cs index b8d693122..d35ab0b68 100644 --- a/Source/ExecutionEngine/ExecutionEngineOptions.cs +++ b/Source/ExecutionEngine/ExecutionEngineOptions.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; +using System.IO; using System.Linq; using System.Text.RegularExpressions; @@ -8,6 +9,7 @@ namespace Microsoft.Boogie; public interface ExecutionEngineOptions : HoudiniOptions, ConcurrencyOptions { + public OutputPrinter Printer { get; } bool ShowVerifiedProcedureCount { get; } string DescriptiveToolName { get; } bool TraceProofObligations { get; } @@ -31,7 +33,7 @@ public interface ExecutionEngineOptions : HoudiniOptions, ConcurrencyOptions bool ForceBplErrors { get; } bool PrintAssignment { get; } bool ExtractLoops { get; } - string PrintErrorModelFile { get; } + TextWriter ModelWriter { get; } bool ExpandLambdas { get; } bool PrintLambdaLifting { get; } bool UseAbstractInterpretation { get; } diff --git a/Source/ExecutionEngine/VerificationResult.cs b/Source/ExecutionEngine/VerificationResult.cs index b95c40342..97d290287 100644 --- a/Source/ExecutionEngine/VerificationResult.cs +++ b/Source/ExecutionEngine/VerificationResult.cs @@ -30,7 +30,7 @@ public int ProofObligationCount public int ProofObligationCountAfter { get; set; } public ConditionGeneration.Outcome Outcome { get; set; } - public List Errors; + public List Errors = new(); public List VCResults; public ISet AssertionChecksums { get; } @@ -48,33 +48,60 @@ public VerificationResult(string requestId, Implementation implementation, strin MessageIfVerifies = implementation.FindStringAttribute("msg_if_verifies"); } - public void Emit(ExecutionEngine engine, PipelineStatistics stats, ErrorReporterDelegate er, TextWriter output, - Implementation impl, bool wasCached) - { + public String GetOutput(OutputPrinter printer, + ExecutionEngine engine, + PipelineStatistics stats, ErrorReporterDelegate er, + Implementation implementation) { + var result = new StringWriter(); if (ErrorBeforeVerification != null) { - ExecutionEngine.printer.WriteErrorInformation(ErrorBeforeVerification, output); + printer.WriteErrorInformation(ErrorBeforeVerification, result); } - engine.ProcessOutcome(Outcome, Errors, engine.TimeIndication(this), stats, - output, impl.GetTimeLimit(engine.Options), er, ImplementationName, - ImplementationToken, - RequestId, MessageIfVerifies, wasCached); + engine.ProcessOutcome(printer, Outcome, Errors, TimeIndication(engine.Options), stats, + result, implementation.GetTimeLimit(engine.Options), er, ImplementationName, ImplementationToken, + RequestId, MessageIfVerifies); + + engine.ProcessErrors(printer, Errors, Outcome, result, er, implementation); - engine.ProcessErrors(Errors, Outcome, output, er, impl); + return result.ToString(); + } - if (engine.Options.XmlSink != null) { - lock (engine.Options.XmlSink) { - engine.Options.XmlSink.WriteStartMethod(impl.Name, Start); + public void ProcessXml(ExecutionEngine engine) { + if (engine.Options.XmlSink == null) { + return; + } - foreach (var vcResult in VCResults.OrderBy(s => s.vcNum)) { - engine.Options.XmlSink.WriteSplit(vcResult.vcNum, vcResult.asserts, vcResult.startTime, - vcResult.outcome.ToString().ToLowerInvariant(), vcResult.runTime, vcResult.resourceCount); - } + lock (engine.Options.XmlSink) { + engine.Options.XmlSink.WriteStartMethod(ImplementationName, Start); - engine.Options.XmlSink.WriteEndMethod(Outcome.ToString().ToLowerInvariant(), - End, End - Start, - ResourceCount); + foreach (var vcResult in VCResults.OrderBy(s => s.vcNum)) { + engine.Options.XmlSink.WriteSplit(vcResult.vcNum, vcResult.asserts, vcResult.startTime, + vcResult.outcome.ToString().ToLowerInvariant(), vcResult.runTime, vcResult.resourceCount); } + + engine.Options.XmlSink.WriteEndMethod(Outcome.ToString().ToLowerInvariant(), + End, End - Start, + ResourceCount); } } + + private string TimeIndication(ExecutionEngineOptions options) + { + var result = ""; + if (options.Trace) + { + result = string.Format(" [{0:F3} s, solver resource count: {1}, {2} proof obligation{3}] ", + (End - Start).TotalSeconds, + ResourceCount, + ProofObligationCount, + ProofObligationCount == 1 ? "" : "s"); + } + else if (options.TraceProofObligations) + { + result = string.Format(" [{0} proof obligation{1}] ", ProofObligationCount, + ProofObligationCount == 1 ? "" : "s"); + } + + return result; + } } \ No newline at end of file diff --git a/Source/Houdini/Checker.cs b/Source/Houdini/Checker.cs index 3795790cd..a5e4c4f0a 100644 --- a/Source/Houdini/Checker.cs +++ b/Source/Houdini/Checker.cs @@ -394,7 +394,7 @@ public void Explain(ProverInterface proverInterface, do { hardAssumptions.Add(controlExprNoop); - (outcome, var unsatisfiedSoftAssumptions) = proverInterface.CheckAssumptions(hardAssumptions, softAssumptions, + (outcome, var unsatisfiedSoftAssumptions) = proverInterface.CheckAssumptions(hardAssumptions, softAssumptions, handler, CancellationToken.None).Result; hardAssumptions.RemoveAt(hardAssumptions.Count - 1); diff --git a/Source/UnitTests/ExecutionEngineTests/CancellationTests.cs b/Source/UnitTests/ExecutionEngineTests/CancellationTests.cs index 9e9b09796..c93507f82 100644 --- a/Source/UnitTests/ExecutionEngineTests/CancellationTests.cs +++ b/Source/UnitTests/ExecutionEngineTests/CancellationTests.cs @@ -13,7 +13,6 @@ public Program GetProgram(ExecutionEngine engine, string code) { engine.Options.UseBaseNameForFileName); Assert.AreEqual(0, errorCount); - ExecutionEngine.printer = new ConsolePrinter(engine.Options); engine.ResolveAndTypecheck(program, bplFileName, out _); engine.EliminateDeadVariables(program); engine.CollectModSets(program); @@ -33,13 +32,14 @@ public async Task InferAndVerifyCanBeCancelledWhileWaitingForProver() { options.VcsCores = 1; var requestId = ExecutionEngine.FreshRequestId(); - var outcomeTask = Task.Run(() => executionEngine.InferAndVerify(infiniteProgram, new PipelineStatistics(), requestId, null, requestId)); + var outcomeTask = + executionEngine.InferAndVerify(infiniteProgram, new PipelineStatistics(), requestId, null, requestId); await Task.Delay(1000); ExecutionEngine.CancelRequest(requestId); var outcome = await outcomeTask; Assert.AreEqual(PipelineOutcome.Cancelled, outcome); var requestId2 = ExecutionEngine.FreshRequestId(); - var outcome2 = executionEngine.InferAndVerify(terminatingProgram, new PipelineStatistics(), requestId2, null, requestId2); + var outcome2 = await executionEngine.InferAndVerify(terminatingProgram, new PipelineStatistics(), requestId2, null, requestId2); Assert.AreEqual(PipelineOutcome.VerificationCompleted, outcome2); } diff --git a/Source/UnitTests/ExecutionEngineTests/GetProverLogs.cs b/Source/UnitTests/ExecutionEngineTests/GetProverLogs.cs index 4b352360c..078505af3 100644 --- a/Source/UnitTests/ExecutionEngineTests/GetProverLogs.cs +++ b/Source/UnitTests/ExecutionEngineTests/GetProverLogs.cs @@ -18,7 +18,6 @@ public static string GetProverLogForProgram(ExecutionEngineOptions options, stri public static IEnumerable GetProverLogsForProgram(ExecutionEngineOptions options, string procedure1) { using var engine = ExecutionEngine.CreateWithoutSharedCache(options); - ExecutionEngine.printer = new ConsolePrinter(engine.Options); var defines = new List() { "FILE_0" }; // Parse error are printed to StdOut :/ diff --git a/Source/VCGeneration/ConditionGeneration.cs b/Source/VCGeneration/ConditionGeneration.cs index 90461ee4f..ec1bd0ec3 100644 --- a/Source/VCGeneration/ConditionGeneration.cs +++ b/Source/VCGeneration/ConditionGeneration.cs @@ -10,7 +10,6 @@ using System.IO; using System.Runtime.CompilerServices; using System.Threading.Tasks; -using VC; using Set = Microsoft.Boogie.GSet; namespace VC @@ -26,7 +25,7 @@ public VCGenException(string s) [ContractClassFor(typeof(ConditionGeneration))] public abstract class ConditionGenerationContracts : ConditionGeneration { - public override Outcome VerifyImplementation(ImplementationRun run, VerifierCallback callback, + public override Task VerifyImplementation(ImplementationRun run, VerifierCallback callback, CancellationToken cancellationToken) { Contract.Requires(run != null); @@ -117,39 +116,31 @@ public ConditionGeneration(Program p, CheckerPool checkerPool) /// each counterexample consisting of an array of labels. /// /// - public Outcome VerifyImplementation(ImplementationRun run, out List /*?*/ errors, - out List vcResults, - string requestId, CancellationToken cancellationToken) + public async Task<(Outcome, List /*?*/ errors, List vcResults)> + VerifyImplementation(ImplementationRun run, string requestId, CancellationToken cancellationToken) { Contract.Requires(run != null); - Contract.Ensures(Contract.ValueAtReturn(out errors) == null || - Contract.ForAll(Contract.ValueAtReturn(out errors), i => i != null)); - Contract.Ensures(Contract.Result() != Outcome.Errors || errors != null); Contract.EnsuresOnThrow(true); Helpers.ExtraTraceInformation(Options, "Starting implementation verification"); - VerificationResultCollector collector = new VerificationResultCollector(Options); + var collector = new VerificationResultCollector(Options); collector.RequestId = requestId; - Outcome outcome = VerifyImplementation(run, collector, cancellationToken); + Outcome outcome = await VerifyImplementation(run, collector, cancellationToken); + List /*?*/ errors = null; if (outcome == Outcome.Errors || outcome == Outcome.TimedOut || outcome == Outcome.OutOfMemory || - outcome == Outcome.OutOfResource) - { + outcome == Outcome.OutOfResource) { errors = collector.examples; } - else - { - errors = null; - } - vcResults = collector.vcResults; Helpers.ExtraTraceInformation(Options, "Finished implementation verification"); - return outcome; + return (outcome, errors, collector.vcResults); } private VCGenOptions Options => CheckerPool.Options; - public abstract Outcome VerifyImplementation(ImplementationRun run, VerifierCallback callback, + public abstract Task VerifyImplementation(ImplementationRun run, + VerifierCallback callback, CancellationToken cancellationToken); /////////////////////////////////// Common Methods and Classes ////////////////////////////////////////// diff --git a/Source/VCGeneration/Split.cs b/Source/VCGeneration/Split.cs index 3e3f9a29f..a55eeb98b 100644 --- a/Source/VCGeneration/Split.cs +++ b/Source/VCGeneration/Split.cs @@ -1306,7 +1306,6 @@ public void ReadOutcome(VerifierCallback callback, ref ConditionGeneration.Outco var resourceCount = checker.GetProverResourceCount().Result; totalResourceCount += resourceCount; - var result = new VCResult(splitNum + 1, checker.ProverStart, outcome, checker.ProverRunTime, Asserts, resourceCount); callback.OnVCResult(result); @@ -1364,7 +1363,8 @@ public void ReadOutcome(VerifierCallback callback, ref ConditionGeneration.Outco /// /// As a side effect, updates "this.parent.CumulativeAssertionCount". /// - public void BeginCheck(TextWriter traceWriter, Checker checker, VerifierCallback callback, ModelViewInfo mvInfo, int no, uint timeout, uint rlimit, CancellationToken cancellationToken) + public void BeginCheck(TextWriter traceWriter, Checker checker, VerifierCallback callback, ModelViewInfo mvInfo, int no, uint timeout, + uint rlimit, CancellationToken cancellationToken) { Contract.Requires(checker != null); Contract.Requires(callback != null); @@ -1493,9 +1493,8 @@ private void SoundnessCheck(HashSet /*!*/> /*!*/ cache, Block /*!*/ } } - public void ReleaseChecker() + public void ResetChecker() { - Checker.GoBackToIdle(); checker = null; } } diff --git a/Source/VCGeneration/SplitAndVerifyWorker.cs b/Source/VCGeneration/SplitAndVerifyWorker.cs index 0513de9ac..be9cae275 100644 --- a/Source/VCGeneration/SplitAndVerifyWorker.cs +++ b/Source/VCGeneration/SplitAndVerifyWorker.cs @@ -105,7 +105,8 @@ async Task DoWork(Split split, CancellationToken cancellationToken) await ProcessResult(split, cancellationToken); } finally { - split.ReleaseChecker(); + checker.GoBackToIdle(); + split.ResetChecker(); } } diff --git a/Source/VCGeneration/VCGen.cs b/Source/VCGeneration/VCGen.cs index 69477bccc..588c32025 100644 --- a/Source/VCGeneration/VCGen.cs +++ b/Source/VCGeneration/VCGen.cs @@ -819,7 +819,8 @@ void ExpandAsserts(Implementation impl) private VCGenOptions Options => CheckerPool.Options; - public override Outcome VerifyImplementation(ImplementationRun run, VerifierCallback callback, + public override async Task VerifyImplementation(ImplementationRun run, + VerifierCallback callback, CancellationToken cancellationToken) { Contract.EnsuresOnThrow(true); @@ -883,7 +884,7 @@ public override Outcome VerifyImplementation(ImplementationRun run, VerifierCall } var worker = new SplitAndVerifyWorker(Options, this, run, gotoCmdOrigins, callback, mvInfo, outcome); - outcome = worker.WorkUntilDone(cancellationToken).Result; + outcome = await worker.WorkUntilDone(cancellationToken); ResourceCount = worker.ResourceCount; if (outcome == Outcome.Correct && smoke_tester != null) From 50837d6946af3baee7adccc76f06f8bef8f820ce Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Thu, 10 Mar 2022 16:42:38 +0100 Subject: [PATCH 20/32] Add test for running multiple Boogie program in parallel --- Source/BoogieDriver/BoogieDriver.cs | 2 +- Source/ExecutionEngine/ConsolePrinter.cs | 22 ++++---- Source/ExecutionEngine/ExecutionEngine.cs | 25 ++++----- .../ExecutionEngineTests/CancellationTests.cs | 5 +- .../ExecutionEngineTest.cs | 54 +++++++++++++++++++ .../ExecutionEngineTests/GetProverLogs.cs | 3 +- 6 files changed, 84 insertions(+), 27 deletions(-) create mode 100644 Source/UnitTests/ExecutionEngineTests/ExecutionEngineTest.cs diff --git a/Source/BoogieDriver/BoogieDriver.cs b/Source/BoogieDriver/BoogieDriver.cs index 63c84dcf7..e99ffd397 100644 --- a/Source/BoogieDriver/BoogieDriver.cs +++ b/Source/BoogieDriver/BoogieDriver.cs @@ -63,7 +63,7 @@ public static int Main(string[] args) Helpers.ExtraTraceInformation(options, "Becoming sentient"); - var success = executionEngine.ProcessFiles(fileList); + var success = executionEngine.ProcessFiles(Console.Out, fileList); if (options.XmlSink != null) { diff --git a/Source/ExecutionEngine/ConsolePrinter.cs b/Source/ExecutionEngine/ConsolePrinter.cs index e91b46fe1..ce61ce1dd 100644 --- a/Source/ExecutionEngine/ConsolePrinter.cs +++ b/Source/ExecutionEngine/ConsolePrinter.cs @@ -74,51 +74,51 @@ public void Inform(string s, TextWriter tw) } - public void WriteTrailer(PipelineStatistics stats) + public void WriteTrailer(TextWriter textWriter, PipelineStatistics stats) { Contract.Requires(stats != null); Contract.Requires(0 <= stats.VerifiedCount && 0 <= stats.ErrorCount && 0 <= stats.InconclusiveCount && 0 <= stats.TimeoutCount && 0 <= stats.OutOfMemoryCount); - Console.WriteLine(); + textWriter.WriteLine(); if (Options.ShowVerifiedProcedureCount) { - Console.Write("{0} finished with {1} verified, {2} error{3}", Options.DescriptiveToolName, + textWriter.Write("{0} finished with {1} verified, {2} error{3}", Options.DescriptiveToolName, stats.VerifiedCount, stats.ErrorCount, stats.ErrorCount == 1 ? "" : "s"); } else { - Console.Write("{0} finished with {1} error{2}", Options.DescriptiveToolName, stats.ErrorCount, + textWriter.Write("{0} finished with {1} error{2}", Options.DescriptiveToolName, stats.ErrorCount, stats.ErrorCount == 1 ? "" : "s"); } if (stats.InconclusiveCount != 0) { - Console.Write(", {0} inconclusive{1}", stats.InconclusiveCount, stats.InconclusiveCount == 1 ? "" : "s"); + textWriter.Write(", {0} inconclusive{1}", stats.InconclusiveCount, stats.InconclusiveCount == 1 ? "" : "s"); } if (stats.TimeoutCount != 0) { - Console.Write(", {0} time out{1}", stats.TimeoutCount, stats.TimeoutCount == 1 ? "" : "s"); + textWriter.Write(", {0} time out{1}", stats.TimeoutCount, stats.TimeoutCount == 1 ? "" : "s"); } if (stats.OutOfMemoryCount != 0) { - Console.Write(", {0} out of memory", stats.OutOfMemoryCount); + textWriter.Write(", {0} out of memory", stats.OutOfMemoryCount); } if (stats.OutOfResourceCount != 0) { - Console.Write(", {0} out of resource", stats.OutOfResourceCount); + textWriter.Write(", {0} out of resource", stats.OutOfResourceCount); } if (stats.SolverExceptionCount != 0) { - Console.Write(", {0} solver exceptions", stats.SolverExceptionCount); + textWriter.Write(", {0} solver exceptions", stats.SolverExceptionCount); } - Console.WriteLine(); - Console.Out.Flush(); + textWriter.WriteLine(); + textWriter.Flush(); } diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index 6a8bde25a..72552a1c0 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -23,7 +23,7 @@ public interface OutputPrinter void ErrorWriteLine(TextWriter tw, string format, params object[] args); void AdvisoryWriteLine(string format, params object[] args); void Inform(string s, TextWriter tw); - void WriteTrailer(PipelineStatistics stats); + void WriteTrailer(TextWriter textWriter, PipelineStatistics stats); void WriteErrorInformation(ErrorInformation errorInfo, TextWriter tw, bool skipExecutionTrace = true); void ReportBplError(IToken tok, string message, bool error, TextWriter tw, string category = null); } @@ -245,13 +245,13 @@ static readonly ConcurrentDictionary static ThreadTaskScheduler LargeStackScheduler = new ThreadTaskScheduler(16 * 1024 * 1024); - public bool ProcessFiles(IList fileNames, bool lookForSnapshots = true, string programId = null) + public bool ProcessFiles(TextWriter output, IList fileNames, bool lookForSnapshots = true, string programId = null) { Contract.Requires(cce.NonNullElements(fileNames)); if (Options.VerifySeparately && 1 < fileNames.Count) { - return fileNames.All(f => ProcessFiles( new List {f}, lookForSnapshots, f)); + return fileNames.All(f => ProcessFiles( output, new List {f}, lookForSnapshots, f)); } if (0 <= Options.VerifySnapshots && lookForSnapshots) @@ -261,7 +261,7 @@ public bool ProcessFiles(IList fileNames, bool lookForSnapshots = true, { // BUG: Reusing checkers during snapshots doesn't work, even though it should. We create a new engine (and thus checker pool) to workaround this. using var engine = new ExecutionEngine(Options, Cache); - return engine.ProcessFiles(new List(s), false, programId); + return engine.ProcessFiles(output, new List(s), false, programId); }); } @@ -272,10 +272,10 @@ public bool ProcessFiles(IList fileNames, bool lookForSnapshots = true, { return true; } - return ProcessProgram(program, bplFileName, programId); + return ProcessProgram(output, program, bplFileName, programId).Result; } - public bool ProcessProgram(Program program, string bplFileName, string programId = null) + public async Task ProcessProgram(TextWriter output, Program program, string bplFileName, string programId = null) { if (programId == null) { @@ -314,11 +314,11 @@ public bool ProcessProgram(Program program, string bplFileName, string programId Inline(program); var stats = new PipelineStatistics(); - outcome = InferAndVerify(program, stats, 1 < Options.VerifySnapshots ? programId : null).Result; + outcome = await InferAndVerify(output, program, stats, 1 < Options.VerifySnapshots ? programId : null); switch (outcome) { case PipelineOutcome.Done: case PipelineOutcome.VerificationCompleted: - Options.Printer.WriteTrailer(stats); + Options.Printer.WriteTrailer(output, stats); return true; case PipelineOutcome.FatalError: return false; @@ -668,6 +668,7 @@ public void Inline(Program program) /// parameters contain meaningful values /// public async Task InferAndVerify( + TextWriter output, Program program, PipelineStatistics stats, string programId = null, @@ -742,7 +743,7 @@ public async Task InferAndVerify( out stats.CachingActionCounts); } - var outcome = await VerifyEachImplementation(program, stats, programId, er, requestId, stablePrioritizedImpls, extractLoopMappingInfo); + var outcome = await VerifyEachImplementation(output, program, stats, programId, er, requestId, stablePrioritizedImpls, extractLoopMappingInfo); if (1 < Options.VerifySnapshots && programId != null) { @@ -775,12 +776,12 @@ private Implementation[] GetPrioritizedImplementations(Program program) return stablePrioritizedImpls; } - private async Task VerifyEachImplementation( - Program program, PipelineStatistics stats, + private async Task VerifyEachImplementation(TextWriter output, Program program, + PipelineStatistics stats, string programId, ErrorReporterDelegate er, string requestId, Implementation[] stablePrioritizedImpls, Dictionary> extractLoopMappingInfo) { - var consoleCollector = new ConcurrentToSequentialWriteManager(Console.Out); + var consoleCollector = new ConcurrentToSequentialWriteManager(output); program.DeclarationDependencies = Prune.ComputeDeclarationDependencies(Options, program); var cts = new CancellationTokenSource(); diff --git a/Source/UnitTests/ExecutionEngineTests/CancellationTests.cs b/Source/UnitTests/ExecutionEngineTests/CancellationTests.cs index c93507f82..d5422d805 100644 --- a/Source/UnitTests/ExecutionEngineTests/CancellationTests.cs +++ b/Source/UnitTests/ExecutionEngineTests/CancellationTests.cs @@ -1,3 +1,4 @@ +using System; using System.Threading.Tasks; using Microsoft.Boogie; using NUnit.Framework; @@ -33,13 +34,13 @@ public async Task InferAndVerifyCanBeCancelledWhileWaitingForProver() { var requestId = ExecutionEngine.FreshRequestId(); var outcomeTask = - executionEngine.InferAndVerify(infiniteProgram, new PipelineStatistics(), requestId, null, requestId); + executionEngine.InferAndVerify(Console.Out, infiniteProgram, new PipelineStatistics(), requestId, null, requestId); await Task.Delay(1000); ExecutionEngine.CancelRequest(requestId); var outcome = await outcomeTask; Assert.AreEqual(PipelineOutcome.Cancelled, outcome); var requestId2 = ExecutionEngine.FreshRequestId(); - var outcome2 = await executionEngine.InferAndVerify(terminatingProgram, new PipelineStatistics(), requestId2, null, requestId2); + var outcome2 = await executionEngine.InferAndVerify(Console.Out, terminatingProgram, new PipelineStatistics(), requestId2, null, requestId2); Assert.AreEqual(PipelineOutcome.VerificationCompleted, outcome2); } diff --git a/Source/UnitTests/ExecutionEngineTests/ExecutionEngineTest.cs b/Source/UnitTests/ExecutionEngineTests/ExecutionEngineTest.cs new file mode 100644 index 000000000..c7794cb9b --- /dev/null +++ b/Source/UnitTests/ExecutionEngineTests/ExecutionEngineTest.cs @@ -0,0 +1,54 @@ +using System.IO; +using System.Threading.Tasks; +using Microsoft.Boogie; +using NUnit.Framework; + +namespace ExecutionEngineTests; + +[TestFixture] +public class ExecutionEngineTest { + + [Test] + public async Task ConcurrentInferAndVerifyCalls() { + var options = CommandLineOptions.FromArguments(); + options.VcsCores = 4; + var engine = ExecutionEngine.CreateWithoutSharedCache(options); + + var writer = new StringWriter(); + var concurrentWriterManager = new ConcurrentToSequentialWriteManager(writer); + + var programString = @"procedure Bad(y: int) +{ + assert 2 == 1; +} + +procedure Good(y: int) +{ + assert 2 == 2; +} +"; + Parser.Parse(programString, "fakeFilename1", out var program1); + Parser.Parse(programString, "fakeFilename2", out var program2); + var task1Writer = concurrentWriterManager.AppendWriter(); + var task1 = engine.ProcessProgram(task1Writer, program1, "fakeFilename"); + var task2Writer = concurrentWriterManager.AppendWriter(); + var task2 = engine.ProcessProgram(task2Writer, program2, "fakeFilename"); + await Task.WhenAll(task1, task2); + + await task1Writer.DisposeAsync(); + await task2Writer.DisposeAsync(); + var output = writer.ToString(); + var expected = @"fakeFilename1(3,3): Error: This assertion might not hold. +Execution trace: + fakeFilename1(3,3): anon0 + +Boogie program verifier finished with 1 verified, 1 error +fakeFilename2(3,3): Error: This assertion might not hold. +Execution trace: + fakeFilename2(3,3): anon0 + +Boogie program verifier finished with 1 verified, 1 error +"; + Assert.AreEqual(expected, output); + } +} \ No newline at end of file diff --git a/Source/UnitTests/ExecutionEngineTests/GetProverLogs.cs b/Source/UnitTests/ExecutionEngineTests/GetProverLogs.cs index 078505af3..6ad14d66a 100644 --- a/Source/UnitTests/ExecutionEngineTests/GetProverLogs.cs +++ b/Source/UnitTests/ExecutionEngineTests/GetProverLogs.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -29,7 +30,7 @@ public static IEnumerable GetProverLogsForProgram(ExecutionEngineOptions var temp1 = directory + "/proverLog"; engine.Options.ProverLogFilePath = temp1; engine.Options.ProverOptions.Add("SOLVER=noop"); - var success1 = engine.ProcessProgram(program1, "1"); + var success1 = engine.ProcessProgram(Console.Out, program1, "1"); foreach (var proverFile in Directory.GetFiles(directory)) { yield return File.ReadAllText(proverFile); } From 658603abc6450be4602074af29d1d34d76bbc02c Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Thu, 10 Mar 2022 17:00:15 +0100 Subject: [PATCH 21/32] Let AdvisoryWriteLine take a TextWriter --- Source/ExecutionEngine/ConsolePrinter.cs | 2 +- Source/ExecutionEngine/ExecutionEngine.cs | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Source/ExecutionEngine/ConsolePrinter.cs b/Source/ExecutionEngine/ConsolePrinter.cs index ce61ce1dd..36d35a23e 100644 --- a/Source/ExecutionEngine/ConsolePrinter.cs +++ b/Source/ExecutionEngine/ConsolePrinter.cs @@ -51,7 +51,7 @@ public void ErrorWriteLine(TextWriter tw, string format, params object[] args) } - public void AdvisoryWriteLine(string format, params object[] args) + public void AdvisoryWriteLine(TextWriter output, string format, params object[] args) { Contract.Requires(format != null); ConsoleColor col = Console.ForegroundColor; diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index 72552a1c0..8f48e67b8 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -21,7 +21,7 @@ public interface OutputPrinter ExecutionEngineOptions Options { get; set; } void ErrorWriteLine(TextWriter tw, string s); void ErrorWriteLine(TextWriter tw, string format, params object[] args); - void AdvisoryWriteLine(string format, params object[] args); + void AdvisoryWriteLine(TextWriter output, string format, params object[] args); void Inform(string s, TextWriter tw); void WriteTrailer(TextWriter textWriter, PipelineStatistics stats); void WriteErrorInformation(ErrorInformation errorInfo, TextWriter tw, bool skipExecutionTrace = true); @@ -1001,8 +1001,7 @@ private async Task VerifyImplementationWithoutCaching(Progra throw; } catch (UnexpectedProverOutputException upo) { - Options.Printer.AdvisoryWriteLine( - "Advisory: {0} SKIPPED because of internal error: unexpected prover output: {1}", + Options.Printer.AdvisoryWriteLine(traceWriter, "Advisory: {0} SKIPPED because of internal error: unexpected prover output: {1}", impl.Name, upo.Message); verificationResult.Errors = null; verificationResult.Outcome = VCGen.Outcome.Inconclusive; @@ -1012,7 +1011,7 @@ private async Task VerifyImplementationWithoutCaching(Progra { // TODO Do we need to move this into a catch? if (e is IOException) { - Options.Printer.AdvisoryWriteLine("Advisory: {0} SKIPPED due to I/O exception: {1}", + Options.Printer.AdvisoryWriteLine(traceWriter, "Advisory: {0} SKIPPED due to I/O exception: {1}", impl.Name, e.Message); verificationResult.Errors = null; verificationResult.Outcome = VCGen.Outcome.SolverException; From 6ae3f6f36dc1841b9488991d4b8a25e6a0943ac6 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Thu, 10 Mar 2022 17:05:11 +0100 Subject: [PATCH 22/32] Fix tests that call engine.ProcessProgram so they wait for it to finish --- .../ExecutionEngineTests/GetProverLogs.cs | 11 +++--- .../ExecutionEngineTests/RandomSeedTest.cs | 21 +++++----- .../SolverLogStabilityTest.cs | 39 ++++++++++--------- 3 files changed, 37 insertions(+), 34 deletions(-) diff --git a/Source/UnitTests/ExecutionEngineTests/GetProverLogs.cs b/Source/UnitTests/ExecutionEngineTests/GetProverLogs.cs index 6ad14d66a..0c4dddeda 100644 --- a/Source/UnitTests/ExecutionEngineTests/GetProverLogs.cs +++ b/Source/UnitTests/ExecutionEngineTests/GetProverLogs.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading.Tasks; using Microsoft.Boogie; using NUnit.Framework; @@ -9,14 +10,14 @@ namespace ExecutionEngineTests; public static class GetProverLogs { - public static string GetProverLogForProgram(ExecutionEngineOptions options, string procedure) + public static async Task GetProverLogForProgram(ExecutionEngineOptions options, string procedure) { - var logs = GetProverLogsForProgram(options, procedure).ToList(); + var logs = await GetProverLogsForProgram(options, procedure).ToListAsync(); Assert.AreEqual(1, logs.Count); return logs[0]; } - public static IEnumerable GetProverLogsForProgram(ExecutionEngineOptions options, string procedure1) + public static async IAsyncEnumerable GetProverLogsForProgram(ExecutionEngineOptions options, string procedure1) { using var engine = ExecutionEngine.CreateWithoutSharedCache(options); var defines = new List() { "FILE_0" }; @@ -30,9 +31,9 @@ public static IEnumerable GetProverLogsForProgram(ExecutionEngineOptions var temp1 = directory + "/proverLog"; engine.Options.ProverLogFilePath = temp1; engine.Options.ProverOptions.Add("SOLVER=noop"); - var success1 = engine.ProcessProgram(Console.Out, program1, "1"); + var success1 = await engine.ProcessProgram(Console.Out, program1, "1"); foreach (var proverFile in Directory.GetFiles(directory)) { - yield return File.ReadAllText(proverFile); + yield return await File.ReadAllTextAsync(proverFile); } } } \ No newline at end of file diff --git a/Source/UnitTests/ExecutionEngineTests/RandomSeedTest.cs b/Source/UnitTests/ExecutionEngineTests/RandomSeedTest.cs index 201bfcb2c..c6e8802bb 100644 --- a/Source/UnitTests/ExecutionEngineTests/RandomSeedTest.cs +++ b/Source/UnitTests/ExecutionEngineTests/RandomSeedTest.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using Microsoft.Boogie; using NUnit.Framework; @@ -25,34 +26,34 @@ private static string GetProgramWithAttribute(int randomSeed) } [Test] - public void AttributeAndCommandLineOptionProduceSameResult() + public async Task AttributeAndCommandLineOptionProduceSameResult() { var options = CommandLineOptions.FromArguments(); options.RandomSeed = randomSeed; - var randomOptionsLogs = GetProverLogs.GetProverLogForProgram(options, program); - var randomAttributeLogs = + var randomOptionsLogs = await GetProverLogs.GetProverLogForProgram(options, program); + var randomAttributeLogs = await GetProverLogs.GetProverLogForProgram(CommandLineOptions.FromArguments(), GetProgramWithAttribute(randomSeed)); Assert.AreEqual(randomOptionsLogs, randomAttributeLogs); } [Test] - public void Z3RandomisationOptionsAreSet() + public async Task Z3RandomisationOptionsAreSet() { var options = CommandLineOptions.FromArguments(); options.RandomSeed = randomSeed; - var randomOptionsLogs = GetProverLogs.GetProverLogForProgram(options, program); + var randomOptionsLogs = await GetProverLogs.GetProverLogForProgram(options, program); Assert.IsTrue(randomOptionsLogs.Contains("(set-option :smt.random_seed 12312314)")); Assert.IsTrue(randomOptionsLogs.Contains("(set-option :sat.random_seed 12312314)")); } [Test] - public void DeclarationOrderIsRandomised() + public async Task DeclarationOrderIsRandomised() { var options = CommandLineOptions.FromArguments(); options.NormalizeDeclarationOrder = false; - var noRandomLogs = GetProverLogs.GetProverLogForProgram(options, program); + var noRandomLogs = await GetProverLogs.GetProverLogForProgram(options, program); options.RandomSeed = 10000; - var randomOptionsLogs = GetProverLogs.GetProverLogForProgram(options, program); + var randomOptionsLogs = await GetProverLogs.GetProverLogForProgram(options, program); var assertN3 = "(assert (<= N 3)"; var randomN3Index = randomOptionsLogs.IndexOf(assertN3, StringComparison.Ordinal)!; var noRandomN3Index = noRandomLogs.IndexOf(assertN3, StringComparison.Ordinal)!; @@ -66,12 +67,12 @@ public void DeclarationOrderIsRandomised() } [Test] - public void SomeVariablesAreRenamed() + public async Task SomeVariablesAreRenamed() { var options = CommandLineOptions.FromArguments(); options.RandomSeed = randomSeed; options.NormalizeNames = false; - var randomOptionsLogs = GetProverLogs.GetProverLogForProgram(options, program); + var randomOptionsLogs = await GetProverLogs.GetProverLogForProgram(options, program); Assert.IsTrue(randomOptionsLogs.Contains("random2084218992")); } } \ No newline at end of file diff --git a/Source/UnitTests/ExecutionEngineTests/SolverLogStabilityTest.cs b/Source/UnitTests/ExecutionEngineTests/SolverLogStabilityTest.cs index 577b69dec..c88d42bf4 100644 --- a/Source/UnitTests/ExecutionEngineTests/SolverLogStabilityTest.cs +++ b/Source/UnitTests/ExecutionEngineTests/SolverLogStabilityTest.cs @@ -1,4 +1,5 @@ -using Microsoft.Boogie; +using System.Threading.Tasks; +using Microsoft.Boogie; using NUnit.Framework; namespace ExecutionEngineTests @@ -7,7 +8,7 @@ namespace ExecutionEngineTests public class SolverLogStabilityTest { [Test()] - public void OrderIsNormalisedBasedOnContent() + public async Task OrderIsNormalisedBasedOnContent() { var procedure1 = @" type Person; @@ -45,17 +46,17 @@ procedure M(p: Person) options.NormalizeNames = true; options.EmitDebugInformation = false; - var proverLog1 = GetProverLogs.GetProverLogForProgram(options, procedure1); - var proverLog2 = GetProverLogs.GetProverLogForProgram(options, procedure2); + var proverLog1 = await GetProverLogs.GetProverLogForProgram(options, procedure1); + var proverLog2 = await GetProverLogs.GetProverLogForProgram(options, procedure2); Assert.AreEqual(proverLog1, proverLog2); options.NormalizeDeclarationOrder = false; - var proverLog3 = GetProverLogs.GetProverLogForProgram(options, procedure2); + var proverLog3 = await GetProverLogs.GetProverLogForProgram(options, procedure2); Assert.AreNotEqual(proverLog1, proverLog3); } [Test()] - public void TurnOffEmitDebugInformation() + public async Task TurnOffEmitDebugInformation() { var procedure = @" procedure M(x: int) @@ -66,21 +67,21 @@ procedure M(x: int) var options = CommandLineOptions.FromArguments(); - var proverLog1 = GetProverLogs.GetProverLogForProgram(options, procedure); + var proverLog1 = await GetProverLogs.GetProverLogForProgram(options, procedure); Assert.True(proverLog1.Contains("skolemid")); Assert.True(proverLog1.Contains("qid")); Assert.True(proverLog1.Contains(":boogie-vc-id")); options.EmitDebugInformation = false; - var proverLog2 = GetProverLogs.GetProverLogForProgram(options, procedure); + var proverLog2 = await GetProverLogs.GetProverLogForProgram(options, procedure); Assert.True(!proverLog2.Contains("skolemid")); Assert.True(!proverLog2.Contains("qid")); Assert.True(!proverLog2.Contains(":boogie-vc-id")); } [Test()] - public void TestNameDiscarding() + public async Task TestNameDiscarding() { var procedure1 = @" type Wicket; @@ -149,13 +150,13 @@ procedure M(x2: int, coloredBarrel: Barrel2 RGBColor2) var options = CommandLineOptions.FromArguments(); options.NormalizeNames = true; - var proverLog1 = GetProverLogs.GetProverLogForProgram(options, procedure1); - var proverLog2 = GetProverLogs.GetProverLogForProgram(options, procedure2); + var proverLog1 = await GetProverLogs.GetProverLogForProgram(options, procedure1); + var proverLog2 = await GetProverLogs.GetProverLogForProgram(options, procedure2); Assert.AreEqual(proverLog1, proverLog2); } [Test()] - public void ControlFlowIsIsolated() + public async Task ControlFlowIsIsolated() { var procedure1 = @" procedure M(x: int) @@ -178,16 +179,16 @@ procedure N(x: int) {procedure2}"; var options = CommandLineOptions.FromArguments(); - var proverLog1 = GetProverLogs.GetProverLogForProgram(options, procedure1); + var proverLog1 = await GetProverLogs.GetProverLogForProgram(options, procedure1); options.ProcsToCheck.Add("M"); - var proverLog2 = GetProverLogs.GetProverLogForProgram(options, procedure1And2); + var proverLog2 = await GetProverLogs.GetProverLogForProgram(options, procedure1And2); Assert.AreEqual(proverLog1, proverLog2); - var proverLog3 = GetProverLogs.GetProverLogForProgram(options, procedure2And1); + var proverLog3 = await GetProverLogs.GetProverLogForProgram(options, procedure2And1); Assert.AreEqual(proverLog3, proverLog2); } [Test()] - public void ConstantsFunctionsAxiomsAndTypesAreIsolated() + public async Task ConstantsFunctionsAxiomsAndTypesAreIsolated() { var procedure1 = @" type Wicket; @@ -263,11 +264,11 @@ procedure M2(x: int, coloredBarrel: Barrel2 RGBColor2) var options = CommandLineOptions.FromArguments(); options.Prune = true; - var proverLog1 = GetProverLogs.GetProverLogForProgram(options, procedure1); + var proverLog1 = await GetProverLogs.GetProverLogForProgram(options, procedure1); options.ProcsToCheck.Add("M"); - var proverLog2 = GetProverLogs.GetProverLogForProgram(options, procedure1And2); + var proverLog2 = await GetProverLogs.GetProverLogForProgram(options, procedure1And2); Assert.AreEqual(proverLog1, proverLog2); - var proverLog3 = GetProverLogs.GetProverLogForProgram(options, procedure2And1); + var proverLog3 = await GetProverLogs.GetProverLogForProgram(options, procedure2And1); Assert.AreEqual(proverLog3, proverLog2); } } From 6f320c8681f916afeb044b8ef8293821380c6dfe Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Thu, 10 Mar 2022 17:14:31 +0100 Subject: [PATCH 23/32] Fix line endings in test --- Source/UnitTests/ExecutionEngineTests/ExecutionEngineTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/UnitTests/ExecutionEngineTests/ExecutionEngineTest.cs b/Source/UnitTests/ExecutionEngineTests/ExecutionEngineTest.cs index c7794cb9b..f43735cc7 100644 --- a/Source/UnitTests/ExecutionEngineTests/ExecutionEngineTest.cs +++ b/Source/UnitTests/ExecutionEngineTests/ExecutionEngineTest.cs @@ -48,7 +48,7 @@ procedure Good(y: int) fakeFilename2(3,3): anon0 Boogie program verifier finished with 1 verified, 1 error -"; +".ReplaceLineEndings(); Assert.AreEqual(expected, output); } } \ No newline at end of file From a1faa6957abb5b55f0533b1708ed8410b3f7bf40 Mon Sep 17 00:00:00 2001 From: Shaz Qadeer Date: Thu, 10 Mar 2022 11:09:09 -0800 Subject: [PATCH 24/32] bug fix (#529) --- Source/Concurrency/LinearTypeChecker.cs | 2 ++ .../inductive-sequentialization/PingPong.bpl | 35 ++----------------- .../ProducerConsumer.bpl | 18 ++-------- 3 files changed, 6 insertions(+), 49 deletions(-) diff --git a/Source/Concurrency/LinearTypeChecker.cs b/Source/Concurrency/LinearTypeChecker.cs index a7a61afb8..c5f8f61a1 100644 --- a/Source/Concurrency/LinearTypeChecker.cs +++ b/Source/Concurrency/LinearTypeChecker.cs @@ -379,6 +379,8 @@ public override Implementation VisitImplementation(Implementation node) { if (civlTypeChecker.procToAtomicAction.ContainsKey(node.Proc) || civlTypeChecker.procToIntroductionAction.ContainsKey(node.Proc) || + civlTypeChecker.procToIsAbstraction.ContainsKey(node.Proc) || + civlTypeChecker.procToIsInvariant.ContainsKey(node.Proc) || civlTypeChecker.procToLemmaProc.ContainsKey(node.Proc)) { return node; diff --git a/Test/civl/inductive-sequentialization/PingPong.bpl b/Test/civl/inductive-sequentialization/PingPong.bpl index 1b49dc092..d476c1900 100644 --- a/Test/civl/inductive-sequentialization/PingPong.bpl +++ b/Test/civl/inductive-sequentialization/PingPong.bpl @@ -73,24 +73,9 @@ PING' (x:int, {:linear_in "pid"} pid:int) returns ({:pending_async "PING"} PAs:[PA]int) modifies ping_channel, pong_channel; { - assert x > 0; - assert pid == ping_id; - assert (forall m:int :: ping_channel[m] > 0 ==> m == x); assert (exists {:pool "INV"} m:int :: ping_channel[m] > 0) && (forall m:int :: pong_channel[m] == 0); + call PAs := PING(x, pid); - assume ping_channel[x] > 0; - ping_channel[x] := ping_channel[x] - 1; - - if (*) - { - pong_channel[x+1] := pong_channel[x+1] + 1; - PAs := NoPAs()[PING(x+1, pid) := 1]; - } - else - { - pong_channel[0] := pong_channel[0] + 1; - PAs := NoPAs(); - } } procedure {:IS_abstraction}{:layer 2} @@ -98,24 +83,8 @@ PONG' (y:int, {:linear_in "pid"} pid:int) returns ({:pending_async "PONG"} PAs:[PA]int) modifies ping_channel, pong_channel; { - assert y > 0; - assert pid == pong_id; - assert (forall m:int :: pong_channel[m] > 0 ==> m == y || m == 0); assert (exists {:pool "INV"} m:int :: pong_channel[m] > 0) && (forall m:int :: ping_channel[m] == 0); - - if (*) - { - assume pong_channel[y] > 0; - pong_channel[y] := pong_channel[y] - 1; - ping_channel[y] := ping_channel[y] + 1; - PAs := NoPAs()[PONG(y+1, pid) := 1]; - } - else - { - assume pong_channel[0] > 0; - pong_channel[0] := pong_channel[0] - 1; - PAs := NoPAs(); - } + call PAs := PONG(y, pid); } //////////////////////////////////////////////////////////////////////////////// diff --git a/Test/civl/inductive-sequentialization/ProducerConsumer.bpl b/Test/civl/inductive-sequentialization/ProducerConsumer.bpl index a77c5858a..df8b8d11c 100644 --- a/Test/civl/inductive-sequentialization/ProducerConsumer.bpl +++ b/Test/civl/inductive-sequentialization/ProducerConsumer.bpl @@ -125,22 +125,8 @@ CONSUMER' (x:int, {:linear_in "pid"} pid:int) returns ({:pending_async "CONSUMER"} PAs:[PA]int) modifies head; { - var x':int; - - assert pid == cons_id; - assert head < tail && (C[head] == x || C[head] == 0); - - assume head < tail; - x' := C[head]; - head := head + 1; - if (x' != 0) - { - PAs := NoPAs()[CONSUMER(x'+1, pid) := 1]; - } - else - { - PAs := NoPAs(); - } + assert head < tail; + call PAs := CONSUMER(x, pid); } //////////////////////////////////////////////////////////////////////////////// From ee3c6d6f76f107fce4874c192288f8d8fe4d9498 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 11 Mar 2022 00:20:00 +0100 Subject: [PATCH 25/32] Support @PROC@ for the -mv option (#530) --- Source/Core/Helpers.cs | 36 +++++++++++----- Source/ExecutionEngine/ConsolePrinter.cs | 2 +- Source/ExecutionEngine/ExecutionEngine.cs | 4 +- .../Houdini/{Checker.cs => HoudiniSession.cs} | 28 ++++++------- Source/Provers/SMTLib/ProverInterface.cs | 1 + Source/Provers/SMTLib/ProverUtil.cs | 2 +- .../SMTLib/SMTLibProcessTheoremProver.cs | 18 ++------ Source/VCGeneration/Checker.cs | 5 ++- Source/VCGeneration/Counterexample.cs | 35 ++++++++-------- Source/VCGeneration/ProofRun.cs | 5 +++ Source/VCGeneration/Split.cs | 42 +++++++++++-------- Source/VCGeneration/VCGen.cs | 32 +++++++------- Test/test15/MoreCapturedStates.bpl | 5 ++- Test/test15/MoreCapturedStates.bpl.expect | 26 ++++++------ 14 files changed, 133 insertions(+), 108 deletions(-) rename Source/Houdini/{Checker.cs => HoudiniSession.cs} (96%) create mode 100644 Source/VCGeneration/ProofRun.cs diff --git a/Source/Core/Helpers.cs b/Source/Core/Helpers.cs index bb9024247..b03ef237e 100644 --- a/Source/Core/Helpers.cs +++ b/Source/Core/Helpers.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Concurrent; +using System.Collections.Generic; using System.Diagnostics.Contracts; namespace Microsoft.Boogie @@ -263,18 +265,30 @@ public static void ExtraTraceInformation(CoreOptions options, string point) } } - // Substitute @PROC@ in a filename with the given descName - public static string SubstituteAtPROC(string descName, string fileName) + private static readonly ConcurrentDictionary UsedLogNames = new(); + public static (string fileName, bool reused) GetLogFilename(string descriptiveName, string filename, bool allowReuse) { - Contract.Requires(fileName != null); - Contract.Requires(descName != null); + filename = SubstituteAtPROC(descriptiveName, cce.NonNull(filename)); + + var reused = false; + var index = UsedLogNames.AddOrUpdate(filename, 0, (_, i) => { + reused = allowReuse; + return allowReuse ? i : i + 1; + }); + var filenameWithIndex = index > 0 ? filename + "." + index : filename; + + return (filenameWithIndex, reused); + } + + private static string SubstituteAtPROC(string descriptiveName, string filename) + { + Contract.Requires(filename != null); + Contract.Requires(descriptiveName != null); Contract.Ensures(Contract.Result() != null); - System.Text.StringBuilder /*!*/ - sb = - new System.Text.StringBuilder(descName.Length); + var /*!*/ sb = new System.Text.StringBuilder(descriptiveName.Length); // quote the name, characters like ^ cause trouble in CMD // while $ could cause trouble in SH - foreach (char c in descName) + foreach (char c in descriptiveName) { if (Char.IsLetterOrDigit(c) || c == '.') { @@ -291,13 +305,13 @@ public static string SubstituteAtPROC(string descName, string fileName) // do it by truncating the @PROC@ replacement, which leaves unchanged // any filename extension specified by the user. We base our // calculations on that there is at most one occurrence of @PROC@. - if (180 <= fileName.Length - 6 + pn.Length) + if (180 <= filename.Length - 6 + pn.Length) { - pn = pn.Substring(0, Math.Max(180 - (fileName.Length - 6), 0)) + "-n" + + pn = pn.Substring(0, Math.Max(180 - (filename.Length - 6), 0)) + "-n" + System.Threading.Interlocked.Increment(ref sequenceId); } - return fileName.Replace("@PROC@", pn); + return filename.Replace("@PROC@", pn); } private static int sequenceId = -1; diff --git a/Source/ExecutionEngine/ConsolePrinter.cs b/Source/ExecutionEngine/ConsolePrinter.cs index 98fb984c0..3465ae503 100644 --- a/Source/ExecutionEngine/ConsolePrinter.cs +++ b/Source/ExecutionEngine/ConsolePrinter.cs @@ -142,7 +142,7 @@ public void WriteErrorInformation(ErrorInformation errorInfo, TextWriter tw, boo } tw.Write(errorInfo.Out.ToString()); - tw.Write(errorInfo.Model.ToString()); + tw.Write(errorInfo.ModelWriter.ToString()); tw.Flush(); } diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index a274d514d..252ff81d9 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -99,7 +99,7 @@ public class ErrorInformation public ErrorKind Kind { get; set; } public string ImplementationName { get; set; } public TextWriter Out = new StringWriter(); - public TextWriter Model = new StringWriter(); + public TextWriter ModelWriter = new StringWriter(); public string FullMsg { @@ -1501,7 +1501,7 @@ public void ProcessErrors(List errors, VC.VCGen.Outcome outcome, } if (Options.ModelViewFile != null) { - error.PrintModel(errorInfo.Model, error); + error.PrintModel(errorInfo.ModelWriter, error); } printer.WriteErrorInformation(errorInfo, tw); diff --git a/Source/Houdini/Checker.cs b/Source/Houdini/HoudiniSession.cs similarity index 96% rename from Source/Houdini/Checker.cs rename to Source/Houdini/HoudiniSession.cs index 40780e97a..08603e521 100644 --- a/Source/Houdini/Checker.cs +++ b/Source/Houdini/HoudiniSession.cs @@ -109,7 +109,7 @@ private Tuple createNewExplainConstants(Variable v) } - public class HoudiniSession + public class HoudiniSession : ProofRun { public class HoudiniStatistics { @@ -120,7 +120,7 @@ public class HoudiniStatistics public int numUnsatCorePrunings = 0; } - public string descriptiveName; + public string Description { get; } private readonly Houdini houdini; public HoudiniStatistics stats; private VCExpr conjecture; @@ -155,7 +155,7 @@ public bool InUnsatCore(Variable constant) public HoudiniSession(Houdini houdini, VCGen vcgen, ProverInterface proverInterface, Program program, Implementation impl, HoudiniStatistics stats, int taskID = -1) { - this.descriptiveName = impl.Name; + this.Description = impl.Name; this.houdini = houdini; this.stats = stats; collector = new ConditionGeneration.VerificationResultCollector(houdini.Options); @@ -186,12 +186,12 @@ public HoudiniSession(Houdini houdini, VCGen vcgen, ProverInterface proverInterf VCExpr eqExpr = exprGen.Eq(controlFlowFunctionAppl, exprGen.Integer(BigNum.FromInt(absyIds.GetId(impl.Blocks[0])))); conjecture = exprGen.Implies(eqExpr, conjecture); - Macro macro = new Macro(Token.NoToken, descriptiveName, new List(), + Macro macro = new Macro(Token.NoToken, Description, new List(), new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "", Type.Bool), false)); proverInterface.DefineMacro(macro, conjecture); conjecture = exprGen.Function(macro); handler = new VCGen.ErrorReporter(this.houdini.Options, gotoCmdOrigins, absyIds, impl.Blocks, vcgen.debugInfos, collector, - mvInfo, proverInterface.Context, program); + mvInfo, proverInterface.Context, program, this); } private VCExpr BuildAxiom(ProverInterface proverInterface, Dictionary currentAssignment) @@ -254,13 +254,13 @@ public ProverInterface.Outcome Verify(ProverInterface proverInterface, Dictionar if (Options.Trace) { - Console.WriteLine("Verifying " + descriptiveName); + Console.WriteLine("Verifying " + Description); } DateTime now = DateTime.UtcNow; VCExpr vc = proverInterface.VCExprGen.Implies(BuildAxiom(proverInterface, assignment), conjecture); - proverInterface.BeginCheck(descriptiveName, vc, handler); + proverInterface.BeginCheck(Description, vc, handler); ProverInterface.Outcome proverOutcome = proverInterface.CheckOutcome(handler, errorLimit, CancellationToken.None).Result; double queryTime = (DateTime.UtcNow - now).TotalSeconds; @@ -380,7 +380,7 @@ public void Explain(ProverInterface proverInterface, if (Options.Trace) { - Console.WriteLine("Verifying (MaxSat) " + descriptiveName); + Console.WriteLine("Verifying (MaxSat) " + Description); } DateTime now = DateTime.UtcNow; @@ -393,8 +393,8 @@ public void Explain(ProverInterface proverInterface, do { hardAssumptions.Add(controlExprNoop); - (outcome, var unsatisfiedSoftAssumptions) = proverInterface.CheckAssumptions(hardAssumptions, softAssumptions, - handler, CancellationToken.None).Result; + (outcome, var unsatisfiedSoftAssumptions) = proverInterface.CheckAssumptions(hardAssumptions, + softAssumptions, handler, CancellationToken.None).Result; hardAssumptions.RemoveAt(hardAssumptions.Count - 1); if (outcome == ProverInterface.Outcome.TimeOut || outcome == ProverInterface.Outcome.OutOfMemory || @@ -428,7 +428,7 @@ public void Explain(ProverInterface proverInterface, hardAssumptions.Add(softAssumptions[i]); } - (outcome, var unsatisfiedSoftAssumptions2) = proverInterface.CheckAssumptions(hardAssumptions, softAssumptions2, + (outcome, var unsatisfiedSoftAssumptions2) = proverInterface.CheckAssumptions(hardAssumptions, softAssumptions2, handler, CancellationToken.None).Result; if (outcome == ProverInterface.Outcome.TimeOut || outcome == ProverInterface.Outcome.OutOfMemory || @@ -451,14 +451,14 @@ public void Explain(ProverInterface proverInterface, foreach (var r in reason) { Houdini.explainHoudiniDottyFile.WriteLine("{0} -> {1} [ label = \"{2}\" color=red ];", refutedConstant.Name, - r, descriptiveName); + r, Description); } //also add the removed reasons using dotted edges (requires- x != 0, requires- x == 0 ==> assert x != 0) foreach (var r in reason1) { Houdini.explainHoudiniDottyFile.WriteLine("{0} -> {1} [ label = \"{2}\" color=blue style=dotted ];", - refutedConstant.Name, r, descriptiveName); + refutedConstant.Name, r, Description); } } while (false); @@ -466,7 +466,7 @@ public void Explain(ProverInterface proverInterface, outcome == ProverInterface.Outcome.OutOfResource || outcome == ProverInterface.Outcome.Undetermined) { Houdini.explainHoudiniDottyFile.WriteLine("{0} -> {1} [ label = \"{2}\" color=red ];", refutedConstant.Name, - "TimeOut", descriptiveName); + "TimeOut", Description); } Options.ErrorLimit = el; diff --git a/Source/Provers/SMTLib/ProverInterface.cs b/Source/Provers/SMTLib/ProverInterface.cs index 0f684f752..a3cab0495 100644 --- a/Source/Provers/SMTLib/ProverInterface.cs +++ b/Source/Provers/SMTLib/ProverInterface.cs @@ -3,6 +3,7 @@ using System.Diagnostics.Contracts; using System.Threading; using System.Threading.Tasks; +using Microsoft.Boogie.SMTLib; using Microsoft.Boogie.VCExprAST; namespace Microsoft.Boogie; diff --git a/Source/Provers/SMTLib/ProverUtil.cs b/Source/Provers/SMTLib/ProverUtil.cs index d0ab62c0a..42135f3cc 100644 --- a/Source/Provers/SMTLib/ProverUtil.cs +++ b/Source/Provers/SMTLib/ProverUtil.cs @@ -298,7 +298,7 @@ public virtual TextWriter OpenLog(string /*?*/ descName) Contract.Assert(filename != null); if (descName != null) { - filename = Helpers.SubstituteAtPROC(descName, filename); + filename = Helpers.GetLogFilename(descName, filename, true).fileName; } return new StreamWriter(filename, AppendLogFile); diff --git a/Source/Provers/SMTLib/SMTLibProcessTheoremProver.cs b/Source/Provers/SMTLib/SMTLibProcessTheoremProver.cs index f7fbb0230..5a2a04758 100644 --- a/Source/Provers/SMTLib/SMTLibProcessTheoremProver.cs +++ b/Source/Provers/SMTLib/SMTLibProcessTheoremProver.cs @@ -524,22 +524,10 @@ protected TextWriter OpenOutputFile(string descriptiveName) Contract.Requires(descriptiveName != null); Contract.Ensures(Contract.Result() != null); - string filename = options.LogFilename; - filename = Helpers.SubstituteAtPROC(descriptiveName, cce.NonNull(filename)); - var curFilename = filename; + string filenameTemplate = options.LogFilename; + var (filename, reused) = Helpers.GetLogFilename(descriptiveName, filenameTemplate, false); - lock (usedLogNames) - { - int n = 1; - while (usedLogNames.Contains(curFilename)) - { - curFilename = filename + "." + n++; - } - - usedLogNames.Add(curFilename); - } - - return new StreamWriter(curFilename, false); + return new StreamWriter(filename, reused); } protected void FlushProverWarnings() diff --git a/Source/VCGeneration/Checker.cs b/Source/VCGeneration/Checker.cs index 7fefcfa41..c300ee53f 100644 --- a/Source/VCGeneration/Checker.cs +++ b/Source/VCGeneration/Checker.cs @@ -5,6 +5,7 @@ using System.Threading; using Microsoft.Boogie.VCExprAST; using System.Threading.Tasks; +using Microsoft.Boogie.SMTLib; using VC; namespace Microsoft.Boogie @@ -261,7 +262,7 @@ public Task GetProverResourceCount() return thmProver.GetRCount(); } - private async Task WaitForOutput(object dummy, CancellationToken cancellationToken) + private async Task WaitForOutput(CancellationToken cancellationToken) { try { outcome = await thmProver.CheckOutcome(cce.NonNull(handler), Options.ErrorLimit, @@ -328,7 +329,7 @@ public void BeginCheck(string descriptiveName, VCExpr vc, ProverInterface.ErrorH thmProver.BeginCheck(descriptiveName, vc, handler); // gen.ClearSharedFormulas(); PR: don't know yet what to do with this guy - ProverTask = WaitForOutput(null, cancellationToken); + ProverTask = WaitForOutput(cancellationToken); } public ProverInterface.Outcome ReadOutcome() diff --git a/Source/VCGeneration/Counterexample.cs b/Source/VCGeneration/Counterexample.cs index 030dc8db1..81b89f7e2 100644 --- a/Source/VCGeneration/Counterexample.cs +++ b/Source/VCGeneration/Counterexample.cs @@ -74,6 +74,7 @@ void ObjectInvariant() Contract.Invariant(cce.NonNullDictionaryAndValues(calleeCounterexamples)); } + public ProofRun ProofRun { get; } protected readonly VCGenOptions options; [Peer] public List Trace; public List AugmentedTrace; @@ -88,7 +89,8 @@ void ObjectInvariant() public Dictionary calleeCounterexamples; - internal Counterexample(VCGenOptions options, List trace, List augmentedTrace, Model model, VC.ModelViewInfo mvInfo, ProverContext context) + internal Counterexample(VCGenOptions options, List trace, List augmentedTrace, Model model, + VC.ModelViewInfo mvInfo, ProverContext context, ProofRun proofRun) { Contract.Requires(trace != null); Contract.Requires(context != null); @@ -97,6 +99,7 @@ internal Counterexample(VCGenOptions options, List trace, List au this.Model = model; this.MvInfo = mvInfo; this.Context = context; + this.ProofRun = proofRun; this.calleeCounterexamples = new Dictionary(); // the call to instance method GetModelValue in the following code requires the fields Model and Context to be initialized if (augmentedTrace != null) @@ -213,14 +216,12 @@ public void Print(int indent, TextWriter tw, Action blockAction = null) } } - public static bool firstModelFile = true; - public void PrintModel(TextWriter tw, Counterexample counterexample) { Contract.Requires(counterexample != null); - var filename = options.ModelViewFile; - if (Model == null || filename == null || options.StratifiedInlining > 0) + var filenameTemplate = options.ModelViewFile; + if (Model == null || filenameTemplate == null || options.StratifiedInlining > 0) { return; } @@ -238,14 +239,14 @@ public void PrintModel(TextWriter tw, Counterexample counterexample) Model.ModelHasStatesAlready = true; } - if (filename == "-") + if (filenameTemplate == "-") { Model.Write(tw); tw.Flush(); } else { - using var wr = new StreamWriter(filename, !firstModelFile); - firstModelFile = false; + var (filename, reused) = Helpers.GetLogFilename(ProofRun.Description, filenameTemplate, true); + using var wr = new StreamWriter(filename, reused); Model.Write(wr); } } @@ -476,8 +477,8 @@ void ObjectInvariant() public AssertCounterexample(VCGenOptions options, List trace, List augmentedTrace, AssertCmd failingAssert, Model model, VC.ModelViewInfo mvInfo, - ProverContext context) - : base(options, trace, augmentedTrace, model, mvInfo, context) + ProverContext context, ProofRun proofRun) + : base(options, trace, augmentedTrace, model, mvInfo, context, proofRun) { Contract.Requires(trace != null); Contract.Requires(failingAssert != null); @@ -497,7 +498,7 @@ public override byte[] Checksum public override Counterexample Clone() { - var ret = new AssertCounterexample(options, Trace, AugmentedTrace, FailingAssert, Model, MvInfo, Context); + var ret = new AssertCounterexample(options, Trace, AugmentedTrace, FailingAssert, Model, MvInfo, Context, ProofRun); ret.calleeCounterexamples = calleeCounterexamples; return ret; } @@ -517,8 +518,8 @@ void ObjectInvariant() public CallCounterexample(VCGenOptions options, List trace, List augmentedTrace, CallCmd failingCall, Requires failingRequires, Model model, - VC.ModelViewInfo mvInfo, ProverContext context, byte[] checksum = null) - : base(options, trace, augmentedTrace, model, mvInfo, context) + VC.ModelViewInfo mvInfo, ProverContext context, ProofRun proofRun, byte[] checksum = null) + : base(options, trace, augmentedTrace, model, mvInfo, context, proofRun) { Contract.Requires(!failingRequires.Free); Contract.Requires(trace != null); @@ -545,7 +546,7 @@ public override byte[] Checksum public override Counterexample Clone() { - var ret = new CallCounterexample(options, Trace, AugmentedTrace, FailingCall, FailingRequires, Model, MvInfo, Context, Checksum); + var ret = new CallCounterexample(options, Trace, AugmentedTrace, FailingCall, FailingRequires, Model, MvInfo, Context, ProofRun, Checksum); ret.calleeCounterexamples = calleeCounterexamples; return ret; } @@ -565,8 +566,8 @@ void ObjectInvariant() public ReturnCounterexample(VCGenOptions options, List trace, List augmentedTrace, TransferCmd failingReturn, Ensures failingEnsures, Model model, - VC.ModelViewInfo mvInfo, ProverContext context, byte[] checksum) - : base(options, trace, augmentedTrace, model, mvInfo, context) + VC.ModelViewInfo mvInfo, ProverContext context, ProofRun proofRun, byte[] checksum) + : base(options, trace, augmentedTrace, model, mvInfo, context, proofRun) { Contract.Requires(trace != null); Contract.Requires(context != null); @@ -595,7 +596,7 @@ public override byte[] Checksum public override Counterexample Clone() { - var ret = new ReturnCounterexample(options, Trace, AugmentedTrace, FailingReturn, FailingEnsures, Model, MvInfo, Context, checksum); + var ret = new ReturnCounterexample(options, Trace, AugmentedTrace, FailingReturn, FailingEnsures, Model, MvInfo, Context, ProofRun, checksum); ret.calleeCounterexamples = calleeCounterexamples; return ret; } diff --git a/Source/VCGeneration/ProofRun.cs b/Source/VCGeneration/ProofRun.cs new file mode 100644 index 000000000..00e5cfffd --- /dev/null +++ b/Source/VCGeneration/ProofRun.cs @@ -0,0 +1,5 @@ +namespace VC; + +public interface ProofRun { + string Description { get; } +} \ No newline at end of file diff --git a/Source/VCGeneration/Split.cs b/Source/VCGeneration/Split.cs index 466de8250..bdc804a71 100644 --- a/Source/VCGeneration/Split.cs +++ b/Source/VCGeneration/Split.cs @@ -25,7 +25,7 @@ public record VCResult int resourceCount ); - public class Split + public class Split : ProofRun { private VCGenOptions options; @@ -127,7 +127,7 @@ readonly public VCGen /*!*/ // async interface private Checker checker; - private int splitNum; + private int splitIndex; internal VCGen.ErrorReporter reporter; public Split(VCGenOptions options, List /*!*/ blocks, @@ -793,7 +793,7 @@ public Counterexample ToCounterexample(ProverContext context) Contract.Assert(c != null); if (c is AssertCmd) { - return VCGen.AssertCmdToCounterexample(options, (AssertCmd) c, cce.NonNull(b.TransferCmd), trace, null, null, null, context); + return VCGen.AssertCmdToCounterexample(options, (AssertCmd) c, cce.NonNull(b.TransferCmd), trace, null, null, null, context, this); } } } @@ -1298,21 +1298,21 @@ public void ReadOutcome(VerifierCallback callback, ref ConditionGeneration.Outco Contract.EnsuresOnThrow(true); ProverInterface.Outcome outcome = cce.NonNull(checker).ReadOutcome(); - if (options.Trace && splitNum >= 0) + if (options.Trace && splitIndex >= 0) { - System.Console.WriteLine(" --> split #{0} done, [{1} s] {2}", splitNum + 1, + System.Console.WriteLine(" --> split #{0} done, [{1} s] {2}", splitIndex + 1, checker.ProverRunTime.TotalSeconds, outcome); } var resourceCount = checker.GetProverResourceCount().Result; totalResourceCount += resourceCount; - var result = new VCResult(splitNum + 1, checker.ProverStart, outcome, checker.ProverRunTime, Asserts, resourceCount); + var result = new VCResult(splitIndex + 1, checker.ProverStart, outcome, checker.ProverRunTime, Asserts, resourceCount); callback.OnVCResult(result); if (options.VcsDumpSplits) { - DumpDot(splitNum); + DumpDot(splitIndex); } proverFailed = false; @@ -1364,12 +1364,13 @@ public void ReadOutcome(VerifierCallback callback, ref ConditionGeneration.Outco /// /// As a side effect, updates "this.parent.CumulativeAssertionCount". /// - public void BeginCheck(Checker checker, VerifierCallback callback, ModelViewInfo mvInfo, int no, uint timeout, uint rlimit, CancellationToken cancellationToken) + public void BeginCheck(Checker checker, VerifierCallback callback, ModelViewInfo mvInfo, int splitIndex, uint timeout, + uint rlimit, CancellationToken cancellationToken) { Contract.Requires(checker != null); Contract.Requires(callback != null); - splitNum = no; + this.splitIndex = splitIndex; // Lock impl since we're setting impl.Blocks that is used to generate the VC. lock (Implementation) { @@ -1396,21 +1397,28 @@ public void BeginCheck(Checker checker, VerifierCallback callback, ModelViewInfo VCExpr eqExpr = exprGen.Eq(controlFlowFunctionAppl, exprGen.Integer(BigNum.FromInt(absyIds.GetId(Implementation.Blocks[0])))); vc = exprGen.Implies(eqExpr, vc); reporter = new VCGen.ErrorReporter(options, gotoCmdOrigins, absyIds, Implementation.Blocks, parent.debugInfos, callback, - mvInfo, Checker.TheoremProver.Context, parent.program); + mvInfo, Checker.TheoremProver.Context, parent.program, this); - if (options.TraceVerify && no >= 0) + if (options.TraceVerify && splitIndex >= 0) { - Console.WriteLine("-- after split #{0}", no); + Console.WriteLine("-- after split #{0}", splitIndex); Print(); } - string desc = cce.NonNull(Implementation.Name); - if (no >= 0) - { - desc += "_split" + no; + checker.BeginCheck(Description, vc, reporter, timeout, rlimit, cancellationToken); + } + } + + public string Description + { + get + { + string description = cce.NonNull(Implementation.Name); + if (splitIndex >= 0) { + description += "_split" + splitIndex; } - checker.BeginCheck(desc, vc, reporter, timeout, rlimit, cancellationToken); + return description; } } diff --git a/Source/VCGeneration/VCGen.cs b/Source/VCGeneration/VCGen.cs index d32312d7e..c78db04a0 100644 --- a/Source/VCGeneration/VCGen.cs +++ b/Source/VCGeneration/VCGen.cs @@ -894,8 +894,8 @@ public override Outcome VerifyImplementation(Implementation impl, VerifierCallba return outcome; } - public class ErrorReporter : ProverInterface.ErrorHandler - { + public class ErrorReporter : ProverInterface.ErrorHandler { + private ProofRun split; private VCGenOptions options; Dictionary gotoCmdOrigins; @@ -943,7 +943,7 @@ public ErrorReporter(VCGenOptions options, VerifierCallback /*!*/ callback, ModelViewInfo mvInfo, ProverContext /*!*/ context, - Program /*!*/ program) : base(options) + Program /*!*/ program, ProofRun split) : base(options) { Contract.Requires(gotoCmdOrigins != null); Contract.Requires(absyIds != null); @@ -960,10 +960,12 @@ public ErrorReporter(VCGenOptions options, this.context = context; this.program = program; + this.split = split; this.options = options; } - public override void OnModel(IList /*!*/ labels, Model model, ProverInterface.Outcome proverOutcome) + public override void OnModel(IList labels /*!*/ /*!*/, Model model, + ProverInterface.Outcome proverOutcome) { // no counter examples reported. if (labels.Count == 0) @@ -993,7 +995,7 @@ public override void OnModel(IList /*!*/ labels, Model model, Prov trace.Add(entryBlock); Counterexample newCounterexample = TraceCounterexample(options, entryBlock, traceNodes, trace, model, MvInfo, - debugInfos, context, new Dictionary()); + debugInfos, context, split, new Dictionary()); if (newCounterexample == null) { @@ -1041,7 +1043,8 @@ public override void OnResourceExceeded(string msg, IEnumerable(), new List(), null, null, context); + AssertCmdToCounterexample(options, cmd.Item1, cmd.Item2, new List(), + new List(), null, null, context, null); cex.IsAuxiliaryCexForDiagnosingTimeouts = true; callback.OnCounterexample(cex, msg); } @@ -2303,6 +2306,7 @@ static Counterexample TraceCounterexample( Block b, HashSet traceNodes, List trace, Model errModel, ModelViewInfo mvInfo, Dictionary> debugInfos, ProverContext context, + ProofRun split, Dictionary calleeCounterexamples) { Contract.Requires(b != null); @@ -2332,7 +2336,7 @@ static Counterexample TraceCounterexample( if (cmd is AssertCmd && traceNodes.Contains(cmd)) { Counterexample newCounterexample = - AssertCmdToCounterexample(options, (AssertCmd) cmd, transferCmd, trace, augmentedTrace, errModel, mvInfo, context); + AssertCmdToCounterexample(options, (AssertCmd) cmd, transferCmd, trace, augmentedTrace, errModel, mvInfo, context, split); Contract.Assert(newCounterexample != null); newCounterexample.AddCalleeCounterexample(calleeCounterexamples); return newCounterexample; @@ -2367,7 +2371,7 @@ static Counterexample TraceCounterexample( } public static Counterexample AssertCmdToCounterexample(VCGenOptions options, AssertCmd cmd, TransferCmd transferCmd, List trace, List augmentedTrace, - Model errModel, ModelViewInfo mvInfo, ProverContext context) + Model errModel, ModelViewInfo mvInfo, ProverContext context, ProofRun split) { Contract.Requires(cmd != null); Contract.Requires(transferCmd != null); @@ -2381,7 +2385,7 @@ public static Counterexample AssertCmdToCounterexample(VCGenOptions options, Ass AssertRequiresCmd assertCmd = (AssertRequiresCmd) cmd; Contract.Assert(assertCmd != null); CallCounterexample cc = new CallCounterexample(options, trace, augmentedTrace, assertCmd.Call, assertCmd.Requires, errModel, mvInfo, - context, assertCmd.Checksum); + context, split, assertCmd.Checksum); return cc; } else if (cmd is AssertEnsuresCmd) @@ -2389,12 +2393,12 @@ public static Counterexample AssertCmdToCounterexample(VCGenOptions options, Ass AssertEnsuresCmd assertCmd = (AssertEnsuresCmd) cmd; Contract.Assert(assertCmd != null); ReturnCounterexample rc = new ReturnCounterexample(options, trace, augmentedTrace, transferCmd, assertCmd.Ensures, errModel, mvInfo, - context, cmd.Checksum); + context, split, cmd.Checksum); return rc; } else { - AssertCounterexample ac = new AssertCounterexample(options, trace, augmentedTrace, (AssertCmd) cmd, errModel, mvInfo, context); + AssertCounterexample ac = new AssertCounterexample(options, trace, augmentedTrace, (AssertCmd) cmd, errModel, mvInfo, context, split); return ac; } } @@ -2415,7 +2419,7 @@ public static Counterexample AssertCmdToCloneCounterexample(VCGenOptions options if (assrt is AssertRequiresCmd) { var aa = (AssertRequiresCmd) assrt; - cc = new CallCounterexample(options, cex.Trace, cex.AugmentedTrace, aa.Call, aa.Requires, cex.Model, cex.MvInfo, cex.Context, aa.Checksum); + cc = new CallCounterexample(options, cex.Trace, cex.AugmentedTrace, aa.Call, aa.Requires, cex.Model, cex.MvInfo, cex.Context, cex.ProofRun, aa.Checksum); } else if (assrt is AssertEnsuresCmd && cex is ReturnCounterexample) { @@ -2498,11 +2502,11 @@ public static Counterexample AssertCmdToCloneCounterexample(VCGenOptions options } cc = new ReturnCounterexample(options, reconstructedTrace ?? cex.Trace, cex.AugmentedTrace, returnCmd ?? oldCex.FailingReturn, aa.Ensures, - cex.Model, cex.MvInfo, cex.Context, aa.Checksum); + cex.Model, cex.MvInfo, cex.Context, cex.ProofRun, aa.Checksum); } else { - cc = new AssertCounterexample(options, cex.Trace, cex.AugmentedTrace, assrt, cex.Model, cex.MvInfo, cex.Context); + cc = new AssertCounterexample(options, cex.Trace, cex.AugmentedTrace, assrt, cex.Model, cex.MvInfo, cex.Context, cex.ProofRun); } return cc; diff --git a/Test/test15/MoreCapturedStates.bpl b/Test/test15/MoreCapturedStates.bpl index 6cc79f6d5..c149aabb1 100644 --- a/Test/test15/MoreCapturedStates.bpl +++ b/Test/test15/MoreCapturedStates.bpl @@ -1,4 +1,7 @@ -// RUN: %boogie "%s" -normalizeNames:1 -mv:"%t".model > "%t" +// RUN: %parallel-boogie "%s" -normalizeNames:1 -mv:"%t@PROC@".model > "%t" +// RUN: cat %tAbs.model > "%t.model" +// RUN: cat %tBadCall.model >> "%t.model" +// RUN: cat %tBadAssert.model >> "%t.model" // RUN: grep STATE "%t".model >> "%t" // RUN: %diff "%s.expect" "%t" // UNSUPPORTED: batch_mode diff --git a/Test/test15/MoreCapturedStates.bpl.expect b/Test/test15/MoreCapturedStates.bpl.expect index 67c748708..8cb8cca27 100644 --- a/Test/test15/MoreCapturedStates.bpl.expect +++ b/Test/test15/MoreCapturedStates.bpl.expect @@ -1,20 +1,20 @@ -MoreCapturedStates.bpl(35,1): Error: A postcondition might not hold on this return path. -MoreCapturedStates.bpl(7,3): Related location: This is the postcondition that might not hold. +MoreCapturedStates.bpl(38,1): Error: A postcondition might not hold on this return path. +MoreCapturedStates.bpl(10,3): Related location: This is the postcondition that might not hold. Execution trace: - MoreCapturedStates.bpl(9,3): anon0 - MoreCapturedStates.bpl(18,3): LabelB - MoreCapturedStates.bpl(33,3): Done -MoreCapturedStates.bpl(54,3): Error: A precondition for this call might not hold. -MoreCapturedStates.bpl(63,3): Related location: This is the precondition that might not hold. + MoreCapturedStates.bpl(12,3): anon0 + MoreCapturedStates.bpl(21,3): LabelB + MoreCapturedStates.bpl(36,3): Done +MoreCapturedStates.bpl(57,3): Error: A precondition for this call might not hold. +MoreCapturedStates.bpl(66,3): Related location: This is the precondition that might not hold. Execution trace: - MoreCapturedStates.bpl(41,3): anon0 - MoreCapturedStates.bpl(50,3): LabelB -MoreCapturedStates.bpl(80,3): Error: This assertion might not hold. + MoreCapturedStates.bpl(44,3): anon0 + MoreCapturedStates.bpl(53,3): LabelB +MoreCapturedStates.bpl(83,3): Error: This assertion might not hold. Execution trace: - MoreCapturedStates.bpl(68,3): anon0 -MoreCapturedStates.bpl(86,3): Error: This assertion might not hold. + MoreCapturedStates.bpl(71,3): anon0 +MoreCapturedStates.bpl(89,3): Error: This assertion might not hold. Execution trace: - MoreCapturedStates.bpl(68,3): anon0 + MoreCapturedStates.bpl(71,3): anon0 Boogie program verifier finished with 0 verified, 4 errors *** STATE From dbb602a05ffa6c25aa2dc614e5230907b6b143e5 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 11 Mar 2022 14:19:39 +0100 Subject: [PATCH 26/32] Rename writer --- Source/ExecutionEngine/ConsolePrinter.cs | 2 +- Source/ExecutionEngine/ExecutionEngine.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/ExecutionEngine/ConsolePrinter.cs b/Source/ExecutionEngine/ConsolePrinter.cs index 36d35a23e..bacaafd79 100644 --- a/Source/ExecutionEngine/ConsolePrinter.cs +++ b/Source/ExecutionEngine/ConsolePrinter.cs @@ -137,7 +137,7 @@ public void WriteErrorInformation(ErrorInformation errorInfo, TextWriter tw, boo } tw.Write(errorInfo.Out.ToString()); - tw.Write(errorInfo.Model.ToString()); + tw.Write(errorInfo.ModelWriter.ToString()); tw.Flush(); } diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index 8f48e67b8..3bd0ee5a3 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -101,7 +101,7 @@ public class ErrorInformation public ErrorKind Kind { get; set; } public string ImplementationName { get; set; } public TextWriter Out = new StringWriter(); - public TextWriter Model = new StringWriter(); + public TextWriter ModelWriter = new StringWriter(); public string FullMsg { @@ -1480,7 +1480,7 @@ public void ProcessErrors(OutputPrinter printer, } if (Options.ModelViewFile != null) { - error.PrintModel(errorInfo.Model, error); + error.PrintModel(errorInfo.ModelWriter, error); } printer.WriteErrorInformation(errorInfo, tw); From 0297589db513c0c4d72c81e9bb159bcea9863b59 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 11 Mar 2022 15:19:12 +0100 Subject: [PATCH 27/32] Do not say we're retrieving cached results when we're not --- Source/ExecutionEngine/ExecutionEngine.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index 3bd0ee5a3..1371cc417 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -1042,16 +1042,15 @@ private VerificationResult GetCachedVerificationResult(Implementation impl, Text return null; } - Options.Printer.Inform($"Retrieving cached verification result for implementation {impl.Name}...", output); if (Options.VerifySnapshots < 3 || cachedResults.Outcome == ConditionGeneration.Outcome.Correct) { + Options.Printer.Inform($"Retrieving cached verification result for implementation {impl.Name}...", output); return cachedResults; } return null; } - private ConditionGeneration CreateVCGen(Program program) { return new VCGen(program, checkerPool); From 8d9afad98d52b1f335d073c99a9bb33554b61e25 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 11 Mar 2022 18:48:29 +0100 Subject: [PATCH 28/32] Maintain order of logging that results from use the -traceCaching option when using parallel Boogie (#531) ### Changes - Maintain order of logging that results from use the `-traceCaching` option when using parallel Boogie - Replace `OutputCollector` with `ConcurrentToSequentialWriteManager` that allows writing to the final TextWriter immediately when child TextWriters are written to, instead of only after calls to `WriteMoreOutput`. ### Testing - Use `%parallel-boogie` in `runtest.snapshot` which already uses `-traceCaching` - Add a test for `ConcurrentToSequentialWriteManager` - Tweak `test0/SplitOnEveryAssert.bpl` and `commandline/SplitOnEveryAssert.bpl` because the output order has improved slightly --- .../ConcurrentToSequentialWriteManager.cs | 64 +++++++++++ Source/ExecutionEngine/ExecutionEngine.cs | 27 ++--- Source/ExecutionEngine/OutputCollector.cs | 41 ------- Source/ExecutionEngine/VerificationResult.cs | 19 ++-- Source/ExecutionEngine/WriterWrapper.cs | 88 +++++++++++++++ Source/Houdini/ConcurrentHoudini.cs | 5 +- Source/Houdini/Houdini.cs | 8 +- Source/Houdini/HoudiniSession.cs | 5 +- Source/Houdini/StagedHoudini.cs | 13 ++- .../ConcurrentToSequentialWriteManagerTest.cs | 31 ++++++ Source/VCGeneration/ConditionGeneration.cs | 80 +++++++------- Source/VCGeneration/Split.cs | 4 +- Source/VCGeneration/SplitAndVerifyWorker.cs | 26 ++--- Source/VCGeneration/StratifiedVC.cs | 100 +++++++++--------- Source/VCGeneration/VCGen.cs | 96 +++++++++-------- Test/commandline/SplitOnEveryAssert.bpl | 2 +- Test/snapshots/runtest.snapshot | 5 +- Test/test0/SplitOnEveryAssert.bpl | 2 +- 18 files changed, 389 insertions(+), 227 deletions(-) create mode 100644 Source/ExecutionEngine/ConcurrentToSequentialWriteManager.cs delete mode 100644 Source/ExecutionEngine/OutputCollector.cs create mode 100644 Source/ExecutionEngine/WriterWrapper.cs create mode 100644 Source/UnitTests/ExecutionEngineTests/ConcurrentToSequentialWriteManagerTest.cs diff --git a/Source/ExecutionEngine/ConcurrentToSequentialWriteManager.cs b/Source/ExecutionEngine/ConcurrentToSequentialWriteManager.cs new file mode 100644 index 000000000..bfd7c45d8 --- /dev/null +++ b/Source/ExecutionEngine/ConcurrentToSequentialWriteManager.cs @@ -0,0 +1,64 @@ +using System.Collections.Generic; +using System.IO; + +namespace Microsoft.Boogie; + +/// +/// Allows grouping writes from concurrent sources before sending them to a single TextWriter. +/// For each concurrent source, a TextWriter can be spawned using AppendWriter. +/// +public class ConcurrentToSequentialWriteManager +{ + public TextWriter Writer { get; } + private readonly Queue writers = new(); + + public ConcurrentToSequentialWriteManager(TextWriter writer) { + Writer = writer; + } + + private readonly object myLock = new(); + + private void Disposed() { + lock (myLock) { + while (writers.Count > 0 && writers.Peek().Disposed) { + var disposedWriter = writers.Dequeue(); + Writer.Write(disposedWriter.SetTargetAndGetBuffer(null)); + } + if (writers.Count > 0) { + Writer.Write(writers.Peek().SetTargetAndGetBuffer(Writer)); + } + } + } + + public TextWriter AppendWriter() { + lock (myLock) { + var target = writers.Count == 0 ? Writer : null; + var result = new SubWriter(this, target); + writers.Enqueue(result); + return result; + } + } + + class SubWriter : WriterWrapper { + private readonly ConcurrentToSequentialWriteManager collector; + private bool buffering; + public bool Disposed { get; private set; } + + public SubWriter(ConcurrentToSequentialWriteManager collector, TextWriter target) : base(target ?? new StringWriter()) { + this.collector = collector; + buffering = target == null; + } + + public string SetTargetAndGetBuffer(TextWriter newTarget) { + var result = buffering ? ((StringWriter)target).ToString() : ""; + buffering = false; + target = newTarget; + return result; + } + + protected override void Dispose(bool disposing) { + Disposed = true; + collector.Disposed(); + } + } +} \ No newline at end of file diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index 252ff81d9..422d4fa85 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -10,6 +10,7 @@ using BoogiePL = Microsoft.Boogie; using System.Runtime.Caching; using System.Diagnostics; +using System.Net.Mime; namespace Microsoft.Boogie { @@ -797,7 +798,7 @@ private PipelineOutcome VerifyEachImplementation(Program program, PipelineStatis Dictionary> extractLoopMappingInfo) { program.DeclarationDependencies = Prune.ComputeDeclarationDependencies(Options, program); - var outputCollector = new OutputCollector(stablePrioritizedImpls); + var outputCollector = new ConcurrentToSequentialWriteManager(Console.Out); var outcome = PipelineOutcome.VerificationCompleted; try { @@ -830,8 +831,9 @@ private PipelineOutcome VerifyEachImplementation(Program program, PipelineStatis cts.Token.ThrowIfCancellationRequested(); } + using var implementationWriter = outputCollector.AppendWriter(); VerifyImplementation(program, stats, er, requestId, extractLoopMappingInfo, stablePrioritizedImpls, - taskIndex, outputCollector, programId); + taskIndex, implementationWriter, programId); ImplIdToCancellationTokenSource.TryRemove(id, out old); } finally { @@ -885,7 +887,6 @@ private PipelineOutcome VerifyEachImplementation(Program program, PipelineStatis cce.NonNull(Options.TheProverFactory).Close(); - outputCollector.WriteMoreOutput(); return outcome; } @@ -951,9 +952,8 @@ private static void CleanupRequest(string requestId) private void VerifyImplementation(Program program, PipelineStatistics stats, ErrorReporterDelegate er, string requestId, Dictionary> extractLoopMappingInfo, - Implementation[] stablePrioritizedImpls, int index, OutputCollector outputCollector, string programId) + Implementation[] stablePrioritizedImpls, int index, TextWriter output, string programId) { - var output = new StringWriter(); Implementation impl = stablePrioritizedImpls[index]; printer.Inform("", output); // newline @@ -971,7 +971,11 @@ private void VerifyImplementation(Program program, PipelineStatistics stats, Err Cache.Insert(impl, verificationResult); } } - verificationResult.Emit(this, stats, er, index, outputCollector, output, impl, wasCached); + verificationResult.Emit(this, stats, er, output, impl, wasCached); + + if (verificationResult.Outcome == VCGen.Outcome.Errors || Options.Trace) { + output.Flush(); + } } private VerificationResult GetCachedVerificationResult(Implementation impl, TextWriter output) @@ -1007,11 +1011,10 @@ private VerificationResult VerifyImplementationWithoutCaching(Program program, P try { var cancellationToken = RequestIdToCancellationTokenSource[requestId].Token; verificationResult.Outcome = - vcgen.VerifyImplementation(impl, out verificationResult.Errors, + vcgen.VerifyImplementation(new ImplementationRun(impl, output), out verificationResult.Errors, out verificationResult.VCResults, requestId, cancellationToken); if (Options.ExtractLoops && verificationResult.Errors != null) { - var vcg = vcgen as VCGen; - if (vcg != null) { + if (vcgen is VCGen vcg) { for (int i = 0; i < verificationResult.Errors.Count; i++) { verificationResult.Errors[i] = vcg.extractLoopTrace(verificationResult.Errors[i], impl.Name, program, extractLoopMappingInfo); @@ -1023,13 +1026,13 @@ private VerificationResult VerifyImplementationWithoutCaching(Program program, P var errorInfo = errorInformationFactory.CreateErrorInformation(impl.tok, String.Format("{0} (encountered in implementation {1}).", e.Message, impl.Name), requestId, "Error"); errorInfo.ImplementationName = impl.Name; - printer.WriteErrorInformation(errorInfo, output); if (er != null) { lock (er) { er(errorInfo); } } + verificationResult.ErrorBeforeVerification = errorInfo; verificationResult.Errors = null; verificationResult.Outcome = VCGen.Outcome.Inconclusive; } @@ -1082,7 +1085,7 @@ private PipelineOutcome RunHoudini(Program program, PipelineStatistics stats, Er } Houdini.HoudiniSession.HoudiniStatistics houdiniStats = new Houdini.HoudiniSession.HoudiniStatistics(); - Houdini.Houdini houdini = new Houdini.Houdini(Options, program, houdiniStats); + Houdini.Houdini houdini = new Houdini.Houdini(Console.Out, Options, program, houdiniStats); Houdini.HoudiniOutcome outcome = houdini.PerformHoudiniInference(); houdini.Close(); @@ -1136,7 +1139,7 @@ public Program ProgramFromFile(string filename) private PipelineOutcome RunStagedHoudini(Program program, PipelineStatistics stats, ErrorReporterDelegate er) { Houdini.HoudiniSession.HoudiniStatistics houdiniStats = new Houdini.HoudiniSession.HoudiniStatistics(); - var stagedHoudini = new Houdini.StagedHoudini(Options, program, houdiniStats, ProgramFromFile); + var stagedHoudini = new Houdini.StagedHoudini(Console.Out, Options, program, houdiniStats, ProgramFromFile); Houdini.HoudiniOutcome outcome = stagedHoudini.PerformStagedHoudiniInference(); if (Options.PrintAssignment) diff --git a/Source/ExecutionEngine/OutputCollector.cs b/Source/ExecutionEngine/OutputCollector.cs deleted file mode 100644 index 32b794717..000000000 --- a/Source/ExecutionEngine/OutputCollector.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Diagnostics.Contracts; -using System.IO; - -namespace Microsoft.Boogie; - -public class OutputCollector -{ - StringWriter[] outputs; - - int nextPrintableIndex = 0; - - public OutputCollector(Implementation[] implementations) - { - outputs = new StringWriter[implementations.Length]; - } - - public void WriteMoreOutput() - { - lock (outputs) - { - for (; nextPrintableIndex < outputs.Length && outputs[nextPrintableIndex] != null; nextPrintableIndex++) - { - Console.Write(outputs[nextPrintableIndex].ToString()); - outputs[nextPrintableIndex] = null; - Console.Out.Flush(); - } - } - } - - public void Add(int index, StringWriter output) - { - Contract.Requires(0 <= index && index < outputs.Length); - Contract.Requires(output != null); - - lock (this) - { - outputs[index] = output; - } - } -} \ No newline at end of file diff --git a/Source/ExecutionEngine/VerificationResult.cs b/Source/ExecutionEngine/VerificationResult.cs index ef165ec78..b95c40342 100644 --- a/Source/ExecutionEngine/VerificationResult.cs +++ b/Source/ExecutionEngine/VerificationResult.cs @@ -33,7 +33,8 @@ public int ProofObligationCount public List Errors; public List VCResults; - public ISet AssertionChecksums { get; private set; } + public ISet AssertionChecksums { get; } + public ErrorInformation ErrorBeforeVerification { get; set; } public VerificationResult(string requestId, Implementation implementation, string programId = null) { @@ -47,9 +48,13 @@ public VerificationResult(string requestId, Implementation implementation, strin MessageIfVerifies = implementation.FindStringAttribute("msg_if_verifies"); } - public void Emit(ExecutionEngine engine, PipelineStatistics stats, ErrorReporterDelegate er, int index, - OutputCollector outputCollector, StringWriter output, Implementation impl, bool wasCached) + public void Emit(ExecutionEngine engine, PipelineStatistics stats, ErrorReporterDelegate er, TextWriter output, + Implementation impl, bool wasCached) { + if (ErrorBeforeVerification != null) { + ExecutionEngine.printer.WriteErrorInformation(ErrorBeforeVerification, output); + } + engine.ProcessOutcome(Outcome, Errors, engine.TimeIndication(this), stats, output, impl.GetTimeLimit(engine.Options), er, ImplementationName, ImplementationToken, @@ -71,13 +76,5 @@ public void Emit(ExecutionEngine engine, PipelineStatistics stats, ErrorReporter ResourceCount); } } - - outputCollector.Add(index, output); - - outputCollector.WriteMoreOutput(); - - if (Outcome == VCGen.Outcome.Errors || engine.Options.Trace) { - Console.Out.Flush(); - } } } \ No newline at end of file diff --git a/Source/ExecutionEngine/WriterWrapper.cs b/Source/ExecutionEngine/WriterWrapper.cs new file mode 100644 index 000000000..ce5776bd6 --- /dev/null +++ b/Source/ExecutionEngine/WriterWrapper.cs @@ -0,0 +1,88 @@ +using System; +using System.IO; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.Boogie; + +class WriterWrapper : TextWriter { + protected TextWriter target; + public override Encoding Encoding => target.Encoding; + + protected WriterWrapper(TextWriter target) { + this.target = target; + } + + public override void Write(char value) { + target.Write(value); + } + + public override void Write(char[] buffer, int index, int count) { + target.Write(buffer, index, count); + } + + public override void Write(ReadOnlySpan buffer) { + target.Write(buffer); + } + + public override void Write(string value) { + target.Write(value); + } + + public override void WriteLine(char[] buffer, int index, int count) { + target.WriteLine(buffer, index, count); + } + + public override void WriteLine(ReadOnlySpan buffer) { + target.WriteLine(buffer); + } + + public override void WriteLine(StringBuilder value) { + target.WriteLine(value); + } + + public override void Write(StringBuilder value) { + target.Write(value); + } + + public override Task WriteAsync(char value) { + return target.WriteAsync(value); + } + + public override Task WriteAsync(char[] buffer, int index, int count) { + return target.WriteAsync(buffer, index, count); + } + + public override Task WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = new()) { + return target.WriteAsync(buffer, cancellationToken); + } + + public override Task WriteAsync(string value) { + return target.WriteAsync(value); + } + + public override Task WriteAsync(StringBuilder value, CancellationToken cancellationToken = new()) { + return target.WriteAsync(value, cancellationToken); + } + + public override Task WriteLineAsync(char value) { + return target.WriteLineAsync(value); + } + + public override Task WriteLineAsync(char[] buffer, int index, int count) { + return target.WriteLineAsync(buffer, index, count); + } + + public override Task WriteLineAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = new()) { + return target.WriteLineAsync(buffer, cancellationToken); + } + + public override Task WriteLineAsync(string value) { + return target.WriteLineAsync(value); + } + + public override Task WriteLineAsync(StringBuilder value, CancellationToken cancellationToken = new()) { + return target.WriteLineAsync(value, cancellationToken); + } +} \ No newline at end of file diff --git a/Source/Houdini/ConcurrentHoudini.cs b/Source/Houdini/ConcurrentHoudini.cs index 6184eedc0..c4db35284 100644 --- a/Source/Houdini/ConcurrentHoudini.cs +++ b/Source/Houdini/ConcurrentHoudini.cs @@ -3,6 +3,7 @@ using System.Collections.Concurrent; using System.Diagnostics; using System.Diagnostics.Contracts; +using System.IO; using System.Linq; namespace Microsoft.Boogie.Houdini @@ -18,14 +19,14 @@ public static ConcurrentDictionary RefutedSharedAnnot get { return refutedSharedAnnotations; } } - public ConcurrentHoudini(HoudiniOptions options, int taskId, Program program, HoudiniSession.HoudiniStatistics stats, + public ConcurrentHoudini(TextWriter traceWriter, HoudiniOptions options, int taskId, Program program, HoudiniSession.HoudiniStatistics stats, string cexTraceFile = "houdiniCexTrace.txt") : base(options) { Contract.Assert(taskId >= 0); this.program = program; this.cexTraceFile = cexTraceFile; this.taskID = taskId; - Initialize(program, stats); + Initialize(traceWriter, program, stats); } static ConcurrentHoudini() diff --git a/Source/Houdini/Houdini.cs b/Source/Houdini/Houdini.cs index 02b53c24e..d06e9e3bc 100644 --- a/Source/Houdini/Houdini.cs +++ b/Source/Houdini/Houdini.cs @@ -416,15 +416,15 @@ protected Houdini(HoudiniOptions options) this.Options = options; } - public Houdini(HoudiniOptions options, Program program, HoudiniSession.HoudiniStatistics stats, string cexTraceFile = "houdiniCexTrace.txt") + public Houdini(TextWriter traceWriter, HoudiniOptions options, Program program, HoudiniSession.HoudiniStatistics stats, string cexTraceFile = "houdiniCexTrace.txt") { this.Options = options; this.program = program; this.cexTraceFile = cexTraceFile; - Initialize(program, stats); + Initialize(traceWriter, program, stats); } - protected void Initialize(Program program, HoudiniSession.HoudiniStatistics stats) + protected void Initialize(TextWriter traceWriter, Program program, HoudiniSession.HoudiniStatistics stats) { if (Options.Trace) { @@ -490,7 +490,7 @@ protected void Initialize(Program program, HoudiniSession.HoudiniStatistics stat } HoudiniSession session = - new HoudiniSession(this, vcgen, proverInterface, program, impl, stats, taskID: GetTaskID()); + new HoudiniSession(traceWriter, this, vcgen, proverInterface, program, impl, stats, taskID: GetTaskID()); houdiniSessions.Add(impl, session); } catch (VCGenException) diff --git a/Source/Houdini/HoudiniSession.cs b/Source/Houdini/HoudiniSession.cs index 08603e521..dc8a55751 100644 --- a/Source/Houdini/HoudiniSession.cs +++ b/Source/Houdini/HoudiniSession.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics.Contracts; using System.Collections.Generic; +using System.IO; using Microsoft.Boogie.VCExprAST; using Microsoft.BaseTypes; using VC; @@ -152,7 +153,7 @@ public bool InUnsatCore(Variable constant) return false; } - public HoudiniSession(Houdini houdini, VCGen vcgen, ProverInterface proverInterface, Program program, + public HoudiniSession(TextWriter traceWriter, Houdini houdini, VCGen vcgen, ProverInterface proverInterface, Program program, Implementation impl, HoudiniStatistics stats, int taskID = -1) { this.Description = impl.Name; @@ -162,7 +163,7 @@ public HoudiniSession(Houdini houdini, VCGen vcgen, ProverInterface proverInterf collector.OnProgress?.Invoke("HdnVCGen", 0, 0, 0.0); vcgen.ConvertCFG2DAG(impl, taskID: taskID); - var gotoCmdOrigins = vcgen.PassifyImpl(impl, out var mvInfo); + var gotoCmdOrigins = vcgen.PassifyImpl(new ImplementationRun(impl, traceWriter), out var mvInfo); ExistentialConstantCollector.CollectHoudiniConstants(houdini, impl, out var ecollector); this.houdiniAssertConstants = ecollector.houdiniAssertConstants; diff --git a/Source/Houdini/StagedHoudini.cs b/Source/Houdini/StagedHoudini.cs index 1661d5571..e72e6e383 100644 --- a/Source/Houdini/StagedHoudini.cs +++ b/Source/Houdini/StagedHoudini.cs @@ -2,14 +2,15 @@ using System.Collections.Generic; using System.Linq; using System.Diagnostics; +using System.IO; using System.Threading.Tasks; using System.Threading; using Microsoft.Boogie.GraphUtil; namespace Microsoft.Boogie.Houdini { - public class StagedHoudini - { + public class StagedHoudini { + private TextWriter traceWriter; private readonly HoudiniOptions options; private Program program; private HoudiniSession.HoudiniStatistics houdiniStats; @@ -21,13 +22,15 @@ public class StagedHoudini private const string tempFilename = "__stagedHoudiniTemp.bpl"; - public StagedHoudini(HoudiniOptions options, Program program, HoudiniSession.HoudiniStatistics houdiniStats, + public StagedHoudini(TextWriter traceWriter, HoudiniOptions options, Program program, + HoudiniSession.HoudiniStatistics houdiniStats, Func ProgramFromFile) { this.options = options; this.program = program; this.houdiniStats = houdiniStats; this.ProgramFromFile = ProgramFromFile; + this.traceWriter = traceWriter; this.houdiniInstances = new List[options.StagedHoudiniThreads]; for (int i = 0; i < options.StagedHoudiniThreads; i++) { @@ -149,7 +152,7 @@ public HoudiniOutcome PerformStagedHoudiniInference() { if (NoStages()) { - Houdini houdini = new Houdini(options, program, houdiniStats); + Houdini houdini = new Houdini(traceWriter, options, program, houdiniStats); return houdini.PerformHoudiniInference(); } @@ -233,7 +236,7 @@ private void ExecuteStage(ScheduledStage s) if (!h.Any()) { - h.Add(new Houdini(options, ProgramFromFile(tempFilename), new HoudiniSession.HoudiniStatistics(), + h.Add(new Houdini(traceWriter, options, ProgramFromFile(tempFilename), new HoudiniSession.HoudiniStatistics(), "houdiniCexTrace_" + s.GetId() + ".txt")); } diff --git a/Source/UnitTests/ExecutionEngineTests/ConcurrentToSequentialWriteManagerTest.cs b/Source/UnitTests/ExecutionEngineTests/ConcurrentToSequentialWriteManagerTest.cs new file mode 100644 index 000000000..9ece8917c --- /dev/null +++ b/Source/UnitTests/ExecutionEngineTests/ConcurrentToSequentialWriteManagerTest.cs @@ -0,0 +1,31 @@ +using System.IO; +using System.Threading.Tasks; +using Microsoft.Boogie; +using NUnit.Framework; + +namespace ExecutionEngineTests; + +[TestFixture] +public class ConcurrentToSequentialWriteManagerTest { + + [Test] + public async Task ThreeConcurrentWriters() { + var writer = new StringWriter(); + var manager = new ConcurrentToSequentialWriteManager(writer); + + var first = manager.AppendWriter(); + var second = manager.AppendWriter(); + var third = manager.AppendWriter(); + + await second.WriteLineAsync("secondLine1"); + await first.WriteLineAsync("firstLine1"); + Assert.AreEqual("firstLine1\n", writer.ToString()); + + await first.DisposeAsync(); + await second.WriteLineAsync("secondLine2"); + await third.WriteLineAsync("thirdLine1"); + Assert.AreEqual("firstLine1\nsecondLine1\nsecondLine2\n", writer.ToString()); + await second.DisposeAsync(); + Assert.AreEqual("firstLine1\nsecondLine1\nsecondLine2\nthirdLine1\n", writer.ToString()); + } +} \ No newline at end of file diff --git a/Source/VCGeneration/ConditionGeneration.cs b/Source/VCGeneration/ConditionGeneration.cs index 0e4b249f8..90461ee4f 100644 --- a/Source/VCGeneration/ConditionGeneration.cs +++ b/Source/VCGeneration/ConditionGeneration.cs @@ -7,7 +7,10 @@ using Microsoft.Boogie; using Microsoft.Boogie.GraphUtil; using System.Diagnostics.Contracts; +using System.IO; using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using VC; using Set = Microsoft.Boogie.GSet; namespace VC @@ -23,10 +26,10 @@ public VCGenException(string s) [ContractClassFor(typeof(ConditionGeneration))] public abstract class ConditionGenerationContracts : ConditionGeneration { - public override Outcome VerifyImplementation(Implementation impl, VerifierCallback callback, + public override Outcome VerifyImplementation(ImplementationRun run, VerifierCallback callback, CancellationToken cancellationToken) { - Contract.Requires(impl != null); + Contract.Requires(run != null); Contract.Requires(callback != null); Contract.EnsuresOnThrow(true); throw new NotImplementedException(); @@ -114,11 +117,11 @@ public ConditionGeneration(Program p, CheckerPool checkerPool) /// each counterexample consisting of an array of labels. /// /// - public Outcome VerifyImplementation(Implementation impl, out List /*?*/ errors, + public Outcome VerifyImplementation(ImplementationRun run, out List /*?*/ errors, out List vcResults, string requestId, CancellationToken cancellationToken) { - Contract.Requires(impl != null); + Contract.Requires(run != null); Contract.Ensures(Contract.ValueAtReturn(out errors) == null || Contract.ForAll(Contract.ValueAtReturn(out errors), i => i != null)); @@ -128,7 +131,7 @@ public Outcome VerifyImplementation(Implementation impl, out List CheckerPool.Options; - public abstract Outcome VerifyImplementation(Implementation impl, VerifierCallback callback, + public abstract Outcome VerifyImplementation(ImplementationRun run, VerifierCallback callback, CancellationToken cancellationToken); /////////////////////////////////// Common Methods and Classes ////////////////////////////////////////// @@ -828,7 +831,7 @@ protected Dictionary ComputeIncarnationMap(Block b, preHavocIncarnationMap = null; // null = the previous command was not an HashCmd. Otherwise, a *copy* of the map before the havoc statement - protected void TurnIntoPassiveBlock(Block b, Dictionary incarnationMap, ModelViewInfo mvInfo, + protected void TurnIntoPassiveBlock(TextWriter traceWriter, Block b, Dictionary incarnationMap, ModelViewInfo mvInfo, Substitution oldFrameSubst, MutableVariableCollector variableCollector, byte[] currentChecksum = null) { Contract.Requires(b != null); @@ -846,7 +849,7 @@ protected void TurnIntoPassiveBlock(Block b, Dictionary incarnat ChecksumHelper.ComputeChecksums(Options, c, currentImplementation, variableCollector.UsedVariables, currentChecksum); variableCollector.Visit(c); currentChecksum = c.Checksum; - TurnIntoPassiveCmd(c, b, incarnationMap, oldFrameSubst, passiveCmds, mvInfo); + TurnIntoPassiveCmd(traceWriter, c, b, incarnationMap, oldFrameSubst, passiveCmds, mvInfo); } b.Checksum = currentChecksum; @@ -863,46 +866,45 @@ protected void TurnIntoPassiveBlock(Block b, Dictionary incarnat #endregion } - protected Dictionary Convert2PassiveCmd(Implementation impl, ModelViewInfo mvInfo) + protected Dictionary Convert2PassiveCmd(ImplementationRun run, ModelViewInfo mvInfo) { - Contract.Requires(impl != null); + Contract.Requires(run != null); Contract.Requires(mvInfo != null); - currentImplementation = impl; + var implementation = run.Implementation; + currentImplementation = run.Implementation; var start = DateTime.UtcNow; - Dictionary r = ConvertBlocks2PassiveCmd(impl.Blocks, impl.Proc.Modifies, mvInfo); + Dictionary r = ConvertBlocks2PassiveCmd(run.TraceWriter, implementation.Blocks, implementation.Proc.Modifies, mvInfo); var end = DateTime.UtcNow; if (Options.TraceCachingForDebugging) { - Console.Out.WriteLine("Turned implementation into passive commands within {0:F0} ms.\n", + run.TraceWriter.WriteLine("Turned implementation into passive commands within {0:F0} ms.\n", end.Subtract(start).TotalMilliseconds); - } - if (Options.TraceCachingForDebugging) { - using var tokTxtWr = new TokenTextWriter("", Console.Out, false, false, Options); + var tokTxtWr = new TokenTextWriter("", run.TraceWriter, false, false, Options); var pd = Options.PrintDesugarings; var pu = Options.PrintUnstructured; Options.PrintDesugarings = true; Options.PrintUnstructured = 1; - impl.Emit(tokTxtWr, 0); + implementation.Emit(tokTxtWr, 0); Options.PrintDesugarings = pd; Options.PrintUnstructured = pu; } currentImplementation = null; - RestoreParamWhereClauses(impl); + RestoreParamWhereClauses(implementation); #region Debug Tracing if (Options.TraceVerify) { Console.WriteLine("after conversion to passive commands"); - EmitImpl(Options, impl, true); + EmitImpl(Options, implementation, true); } #endregion @@ -910,7 +912,7 @@ protected Dictionary Convert2PassiveCmd(Implementation impl, Mod return r; } - protected Dictionary ConvertBlocks2PassiveCmd(List blocks, List modifies, + protected Dictionary ConvertBlocks2PassiveCmd(TextWriter traceWriter, List blocks, List modifies, ModelViewInfo mvInfo) { Contract.Requires(blocks != null); @@ -991,7 +993,7 @@ protected Dictionary ConvertBlocks2PassiveCmd(List blocks #endregion Each block's map needs to be available to successor blocks - TurnIntoPassiveBlock(b, incarnationMap, mvInfo, oldFrameSubst, mvc, currentChecksum); + TurnIntoPassiveBlock(traceWriter, b, incarnationMap, mvInfo, oldFrameSubst, mvc, currentChecksum); exitBlock = b; exitIncarnationMap = incarnationMap; } @@ -1037,16 +1039,16 @@ public enum CachingAction : byte public long[] CachingActionCounts; - void TraceCachingAction(Cmd cmd, CachingAction action) + void TraceCachingAction(TextWriter traceWriter, Cmd cmd, CachingAction action) { if (Options.TraceCachingForTesting) { - using var tokTxtWr = new TokenTextWriter("", Console.Out, false, false, Options); + var tokTxtWr = new TokenTextWriter("", traceWriter, false, false, Options); var loc = cmd.tok != null && cmd.tok != Token.NoToken ? string.Format("{0}({1},{2})", cmd.tok.filename, cmd.tok.line, cmd.tok.col) : ""; - Console.Write("Processing command (at {0}) ", loc); + traceWriter.Write("Processing command (at {0}) ", loc); cmd.Emit(tokTxtWr, 0); - Console.Out.WriteLine(" >>> {0}", action); + traceWriter.WriteLine(" >>> {0}", action); } if (Options.TraceCachingForBenchmarking && CachingActionCounts != null) @@ -1102,7 +1104,7 @@ private void AddDebugInfo(Cmd c, Dictionary incarnationMap, List /// In that case, it remembers the incarnation map BEFORE the havoc. /// Meanwhile, record any information needed to later reconstruct a model view. /// - protected void TurnIntoPassiveCmd(Cmd c, Block enclosingBlock, Dictionary incarnationMap, Substitution oldFrameSubst, + protected void TurnIntoPassiveCmd(TextWriter traceWriter, Cmd c, Block enclosingBlock, Dictionary incarnationMap, Substitution oldFrameSubst, List passiveCmds, ModelViewInfo mvInfo) { Contract.Requires(c != null); @@ -1184,7 +1186,7 @@ protected void TurnIntoPassiveCmd(Cmd c, Block enclosingBlock, Dictionary(), @@ -1229,7 +1231,7 @@ protected void TurnIntoPassiveCmd(Cmd c, Block enclosingBlock, Dictionary /// As a side effect, updates "this.parent.CumulativeAssertionCount". /// - public void BeginCheck(Checker checker, VerifierCallback callback, ModelViewInfo mvInfo, int splitIndex, uint timeout, + public void BeginCheck(TextWriter traceWriter, Checker checker, VerifierCallback callback, ModelViewInfo mvInfo, int splitIndex, uint timeout, uint rlimit, CancellationToken cancellationToken) { Contract.Requires(checker != null); @@ -1382,7 +1382,7 @@ public void BeginCheck(Checker checker, VerifierCallback callback, ModelViewInfo ProverContext ctx = checker.TheoremProver.Context; Boogie2VCExprTranslator bet = ctx.BoogieExprTranslator; - var cc = new VCGen.CodeExprConversionClosure(checker.Pool.Options, absyIds, ctx); + var cc = new VCGen.CodeExprConversionClosure(traceWriter, checker.Pool.Options, absyIds, ctx); bet.SetCodeExprConverter(cc.CodeExprToVerificationCondition); var exprGen = ctx.ExprGen; diff --git a/Source/VCGeneration/SplitAndVerifyWorker.cs b/Source/VCGeneration/SplitAndVerifyWorker.cs index 8da3aea39..0513de9ac 100644 --- a/Source/VCGeneration/SplitAndVerifyWorker.cs +++ b/Source/VCGeneration/SplitAndVerifyWorker.cs @@ -14,7 +14,7 @@ class SplitAndVerifyWorker private readonly VCGenOptions options; private readonly VerifierCallback callback; private readonly ModelViewInfo mvInfo; - private readonly Implementation implementation; + private readonly ImplementationRun run; private readonly int maxKeepGoingSplits; private readonly List manualSplits; @@ -33,35 +33,35 @@ class SplitAndVerifyWorker private int totalResourceCount; - public SplitAndVerifyWorker(VCGenOptions options, VCGen vcGen, Implementation implementation, + public SplitAndVerifyWorker(VCGenOptions options, VCGen vcGen, ImplementationRun run, Dictionary gotoCmdOrigins, VerifierCallback callback, ModelViewInfo mvInfo, Outcome outcome) { this.options = options; this.callback = callback; this.mvInfo = mvInfo; - this.implementation = implementation; + this.run = run; this.outcome = outcome; - + var maxSplits = options.VcsMaxSplits; - VCGen.CheckIntAttributeOnImpl(implementation, "vcs_max_splits", ref maxSplits); + VCGen.CheckIntAttributeOnImpl(Implementation, "vcs_max_splits", ref maxSplits); maxKeepGoingSplits = options.VcsMaxKeepGoingSplits; - VCGen.CheckIntAttributeOnImpl(implementation, "vcs_max_keep_going_splits", ref maxKeepGoingSplits); + VCGen.CheckIntAttributeOnImpl(Implementation, "vcs_max_keep_going_splits", ref maxKeepGoingSplits); maxVcCost = options.VcsMaxCost; var tmpMaxVcCost = -1; - VCGen.CheckIntAttributeOnImpl(implementation, "vcs_max_cost", ref tmpMaxVcCost); + VCGen.CheckIntAttributeOnImpl(Implementation, "vcs_max_cost", ref tmpMaxVcCost); if (tmpMaxVcCost >= 0) { maxVcCost = tmpMaxVcCost; } splitOnEveryAssert = options.VcsSplitOnEveryAssert; - implementation.CheckBooleanAttribute("vcs_split_on_every_assert", ref splitOnEveryAssert); + Implementation.CheckBooleanAttribute("vcs_split_on_every_assert", ref splitOnEveryAssert); - ResetPredecessors(implementation.Blocks); - manualSplits = Split.FocusAndSplit(options, implementation, gotoCmdOrigins, vcGen, splitOnEveryAssert); + ResetPredecessors(Implementation.Blocks); + manualSplits = Split.FocusAndSplit(options, Implementation, gotoCmdOrigins, vcGen, splitOnEveryAssert); if (manualSplits.Count == 1 && maxSplits > 1) { manualSplits = Split.DoSplit(manualSplits[0], maxVcCost, maxSplits); @@ -122,10 +122,12 @@ private void StartCheck(Split split, Checker checker, CancellationToken cancella var timeout = KeepGoing && split.LastChance ? options.VcsFinalAssertTimeout : KeepGoing ? options.VcsKeepGoingTimeout : - implementation.GetTimeLimit(options); - split.BeginCheck(checker, callback, mvInfo, currentSplitNumber, timeout, implementation.GetResourceLimit(options), cancellationToken); + run.Implementation.GetTimeLimit(options); + split.BeginCheck(run.TraceWriter, checker, callback, mvInfo, currentSplitNumber, timeout, Implementation.GetResourceLimit(options), cancellationToken); } + private Implementation Implementation => run.Implementation; + private async Task ProcessResult(Split split, CancellationToken cancellationToken) { if (TrackingProgress) { diff --git a/Source/VCGeneration/StratifiedVC.cs b/Source/VCGeneration/StratifiedVC.cs index e0cb00bb1..5aa5e2306 100644 --- a/Source/VCGeneration/StratifiedVC.cs +++ b/Source/VCGeneration/StratifiedVC.cs @@ -109,7 +109,7 @@ public VCExpr MustReach(Block block, ControlFlowIdMap absyIds) { var vcgen = info.vcgen; var gen = vcgen.prover.VCExprGen; - var impl = info.impl; + var impl = info.Implementation; mustReachVar = new Dictionary(); mustReachBindings = new List(); foreach (Block b in impl.Blocks) @@ -182,7 +182,7 @@ public List RecordProcCallSites public override string ToString() { - return info.impl.Name; + return info.Implementation.Name; } } @@ -339,7 +339,7 @@ public class StratifiedInliningInfo { private VCGenOptions options; public StratifiedVCGenBase vcgen; - public Implementation impl; + public ImplementationRun run; public Function function; public Variable controlFlowVariable; public Cmd exitAssertCmd; @@ -358,12 +358,14 @@ public class StratifiedInliningInfo // boolControlVC (block -> its Bool variable) public Dictionary blockToControlVar; - public StratifiedInliningInfo(VCGenOptions options, Implementation implementation, StratifiedVCGenBase stratifiedVcGen, + public Implementation Implementation => run.Implementation; + + public StratifiedInliningInfo(VCGenOptions options, ImplementationRun run, StratifiedVCGenBase stratifiedVcGen, Action PassiveImplInstrumentation) { vcgen = stratifiedVcGen; - impl = implementation; this.PassiveImplInstrumentation = PassiveImplInstrumentation; + this.run = run; this.options = options; List functionInterfaceVars = new List(); @@ -373,19 +375,19 @@ public StratifiedInliningInfo(VCGenOptions options, Implementation implementatio true)); } - foreach (Variable v in impl.InParams) + foreach (Variable v in Implementation.InParams) { functionInterfaceVars.Add(new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "", v.TypedIdent.Type), true)); } - foreach (Variable v in impl.OutParams) + foreach (Variable v in Implementation.OutParams) { functionInterfaceVars.Add(new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "", v.TypedIdent.Type), true)); } - foreach (IdentifierExpr e in impl.Proc.Modifies) + foreach (IdentifierExpr e in Implementation.Proc.Modifies) { if (e.Decl == null) { @@ -397,7 +399,7 @@ public StratifiedInliningInfo(VCGenOptions options, Implementation implementatio } Formal returnVar = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "", Bpl.Type.Bool), false); - function = new Function(Token.NoToken, impl.Name, functionInterfaceVars, returnVar); + function = new Function(Token.NoToken, Implementation.Name, functionInterfaceVars, returnVar); vcgen.prover.Context.DeclareFunction(function, ""); List exprs = new List(); @@ -407,19 +409,19 @@ public StratifiedInliningInfo(VCGenOptions options, Implementation implementatio exprs.Add(new OldExpr(Token.NoToken, new IdentifierExpr(Token.NoToken, v))); } - foreach (Variable v in impl.Proc.InParams) + foreach (Variable v in Implementation.Proc.InParams) { Contract.Assert(v != null); exprs.Add(new IdentifierExpr(Token.NoToken, v)); } - foreach (Variable v in impl.Proc.OutParams) + foreach (Variable v in Implementation.Proc.OutParams) { Contract.Assert(v != null); exprs.Add(new IdentifierExpr(Token.NoToken, v)); } - foreach (IdentifierExpr ie in impl.Proc.Modifies) + foreach (IdentifierExpr ie in Implementation.Proc.Modifies) { Contract.Assert(ie != null); if (ie.Decl == null) @@ -431,7 +433,7 @@ public StratifiedInliningInfo(VCGenOptions options, Implementation implementatio } Expr freePostExpr = new NAryExpr(Token.NoToken, new FunctionCall(function), exprs); - impl.Proc.Ensures.Add(new Ensures(Token.NoToken, true, freePostExpr, "", + Implementation.Proc.Ensures.Add(new Ensures(Token.NoToken, true, freePostExpr, "", new QKeyValue(Token.NoToken, "si_fcall", new List(), null))); initialized = false; @@ -445,16 +447,16 @@ public void GenerateVCBoolControl() // fix names for exit variables var outputVariables = new List(); var assertConjuncts = new List(); - foreach (Variable v in impl.OutParams) + foreach (Variable v in Implementation.OutParams) { Constant c = new Constant(Token.NoToken, - new TypedIdent(Token.NoToken, impl.Name + "_" + v.Name, v.TypedIdent.Type)); + new TypedIdent(Token.NoToken, Implementation.Name + "_" + v.Name, v.TypedIdent.Type)); outputVariables.Add(c); Expr eqExpr = Expr.Eq(new IdentifierExpr(Token.NoToken, c), new IdentifierExpr(Token.NoToken, v)); assertConjuncts.Add(eqExpr); } - foreach (IdentifierExpr e in impl.Proc.Modifies) + foreach (IdentifierExpr e in Implementation.Proc.Modifies) { if (e.Decl == null) { @@ -463,7 +465,7 @@ public void GenerateVCBoolControl() Variable v = e.Decl; Constant c = new Constant(Token.NoToken, - new TypedIdent(Token.NoToken, impl.Name + "_" + v.Name, v.TypedIdent.Type)); + new TypedIdent(Token.NoToken, Implementation.Name + "_" + v.Name, v.TypedIdent.Type)); outputVariables.Add(c); Expr eqExpr = Expr.Eq(new IdentifierExpr(Token.NoToken, c), new IdentifierExpr(Token.NoToken, v)); assertConjuncts.Add(eqExpr); @@ -475,19 +477,19 @@ public void GenerateVCBoolControl() // Passify Program program = vcgen.program; ProverInterface proverInterface = vcgen.prover; - vcgen.ConvertCFG2DAG(impl); - vcgen.PassifyImpl(impl, out mvInfo); + vcgen.ConvertCFG2DAG(Implementation); + vcgen.PassifyImpl(run, out mvInfo); VCExpressionGenerator gen = proverInterface.VCExprGen; var exprGen = proverInterface.Context.ExprGen; var translator = proverInterface.Context.BoogieExprTranslator; // add a boolean variable at each call site - vcgen.InstrumentCallSites(impl); + vcgen.InstrumentCallSites(Implementation); // typecheck var tc = new TypecheckingContext(null, options); - impl.Typecheck(tc); + Implementation.Typecheck(tc); /////////////////// // Generate the VC @@ -495,13 +497,13 @@ public void GenerateVCBoolControl() // block -> bool variable blockToControlVar = new Dictionary(); - foreach (var b in impl.Blocks) + foreach (var b in Implementation.Blocks) { blockToControlVar.Add(b, gen.Variable(b.Label + "_holds", Bpl.Type.Bool)); } vcexpr = VCExpressionGenerator.True; - foreach (var b in impl.Blocks) + foreach (var b in Implementation.Blocks) { // conjoin all assume cmds VCExpr c = VCExpressionGenerator.True; @@ -541,21 +543,21 @@ public void GenerateVCBoolControl() } // assert start block - vcexpr = gen.AndSimp(vcexpr, blockToControlVar[impl.Blocks[0]]); + vcexpr = gen.AndSimp(vcexpr, blockToControlVar[Implementation.Blocks[0]]); //Console.WriteLine("VC of {0}: {1}", impl.Name, vcexpr); // Collect other information - callSites = vcgen.CollectCallSites(impl); - recordProcCallSites = vcgen.CollectRecordProcedureCallSites(impl); + callSites = vcgen.CollectCallSites(Implementation); + recordProcCallSites = vcgen.CollectRecordProcedureCallSites(Implementation); // record interface variables privateExprVars = new List(); - foreach (Variable v in impl.LocVars) + foreach (Variable v in Implementation.LocVars) { privateExprVars.Add(translator.LookupVariable(v)); } - foreach (Variable v in impl.OutParams) + foreach (Variable v in Implementation.OutParams) { privateExprVars.Add(translator.LookupVariable(v)); } @@ -568,7 +570,7 @@ public void GenerateVCBoolControl() interfaceExprVars.Add(translator.LookupVariable(v)); } - foreach (Variable v in impl.InParams) + foreach (Variable v in Implementation.InParams) { interfaceExprVars.Add(translator.LookupVariable(v)); } @@ -595,16 +597,16 @@ public void GenerateVC() List outputVariables = new List(); List assertConjuncts = new List(); - foreach (Variable v in impl.OutParams) + foreach (Variable v in Implementation.OutParams) { Constant c = new Constant(Token.NoToken, - new TypedIdent(Token.NoToken, impl.Name + "_" + v.Name, v.TypedIdent.Type)); + new TypedIdent(Token.NoToken, Implementation.Name + "_" + v.Name, v.TypedIdent.Type)); outputVariables.Add(c); Expr eqExpr = Expr.Eq(new IdentifierExpr(Token.NoToken, c), new IdentifierExpr(Token.NoToken, v)); assertConjuncts.Add(eqExpr); } - foreach (IdentifierExpr e in impl.Proc.Modifies) + foreach (IdentifierExpr e in Implementation.Proc.Modifies) { if (e.Decl == null) { @@ -613,7 +615,7 @@ public void GenerateVC() Variable v = e.Decl; Constant c = new Constant(Token.NoToken, - new TypedIdent(Token.NoToken, impl.Name + "_" + v.Name, v.TypedIdent.Type)); + new TypedIdent(Token.NoToken, Implementation.Name + "_" + v.Name, v.TypedIdent.Type)); outputVariables.Add(c); Expr eqExpr = Expr.Eq(new IdentifierExpr(Token.NoToken, c), new IdentifierExpr(Token.NoToken, v)); assertConjuncts.Add(eqExpr); @@ -623,8 +625,8 @@ public void GenerateVC() Program program = vcgen.program; ProverInterface proverInterface = vcgen.prover; - vcgen.ConvertCFG2DAG(impl); - vcgen.PassifyImpl(impl, out mvInfo); + vcgen.ConvertCFG2DAG(Implementation); + vcgen.PassifyImpl(run, out mvInfo); VCExpressionGenerator gen = proverInterface.VCExprGen; var exprGen = proverInterface.Context.ExprGen; @@ -634,37 +636,37 @@ public void GenerateVC() new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, "@cfc", Microsoft.Boogie.Type.Int)); VCExpr controlFlowVariableExpr = translator.LookupVariable(controlFlowVariable); - vcgen.InstrumentCallSites(impl); + vcgen.InstrumentCallSites(Implementation); if (PassiveImplInstrumentation != null) { - PassiveImplInstrumentation(impl); + PassiveImplInstrumentation(Implementation); } var absyIds = new ControlFlowIdMap(); - VCGen.CodeExprConversionClosure cc = new VCGen.CodeExprConversionClosure(options, absyIds, proverInterface.Context); + VCGen.CodeExprConversionClosure cc = new VCGen.CodeExprConversionClosure(run.TraceWriter, options, absyIds, proverInterface.Context); translator.SetCodeExprConverter(cc.CodeExprToVerificationCondition); - vcexpr = gen.Not(vcgen.GenerateVCAux(impl, controlFlowVariableExpr, absyIds, proverInterface.Context)); + vcexpr = gen.Not(vcgen.GenerateVCAux(Implementation, controlFlowVariableExpr, absyIds, proverInterface.Context)); if (controlFlowVariableExpr != null) { VCExpr controlFlowFunctionAppl = exprGen.ControlFlowFunctionApplication(controlFlowVariableExpr, exprGen.Integer(BigNum.ZERO)); - VCExpr eqExpr = exprGen.Eq(controlFlowFunctionAppl, exprGen.Integer(BigNum.FromInt(absyIds.GetId(impl.Blocks[0])))); + VCExpr eqExpr = exprGen.Eq(controlFlowFunctionAppl, exprGen.Integer(BigNum.FromInt(absyIds.GetId(Implementation.Blocks[0])))); vcexpr = exprGen.And(eqExpr, vcexpr); } - callSites = vcgen.CollectCallSites(impl); - recordProcCallSites = vcgen.CollectRecordProcedureCallSites(impl); + callSites = vcgen.CollectCallSites(Implementation); + recordProcCallSites = vcgen.CollectRecordProcedureCallSites(Implementation); privateExprVars = new List(); - foreach (Variable v in impl.LocVars) + foreach (Variable v in Implementation.LocVars) { privateExprVars.Add(translator.LookupVariable(v)); } - foreach (Variable v in impl.OutParams) + foreach (Variable v in Implementation.OutParams) { privateExprVars.Add(translator.LookupVariable(v)); } @@ -675,7 +677,7 @@ public void GenerateVC() interfaceExprVars.Add(translator.LookupVariable(v)); } - foreach (Variable v in impl.InParams) + foreach (Variable v in Implementation.InParams) { interfaceExprVars.Add(translator.LookupVariable(v)); } @@ -697,7 +699,7 @@ public abstract class StratifiedVCGenBase : VCGen public Dictionary implName2StratifiedInliningInfo; public ProverInterface prover; - public StratifiedVCGenBase(VCGenOptions options, Program program, string logFilePath /*?*/, bool appendLogFile, CheckerPool checkerPool, + public StratifiedVCGenBase(TextWriter traceWriter, VCGenOptions options, Program program, string logFilePath /*?*/, bool appendLogFile, CheckerPool checkerPool, Action PassiveImplInstrumentation) : base(program, checkerPool) { @@ -705,7 +707,7 @@ public StratifiedVCGenBase(VCGenOptions options, Program program, string logFile prover = ProverInterface.CreateProver(options, program, logFilePath, appendLogFile, options.TimeLimit); foreach (var impl in program.Implementations) { - implName2StratifiedInliningInfo[impl.Name] = new StratifiedInliningInfo(options, impl, this, PassiveImplInstrumentation); + implName2StratifiedInliningInfo[impl.Name] = new StratifiedInliningInfo(options, new ImplementationRun(impl, traceWriter), this, PassiveImplInstrumentation); } GenerateRecordFunctions(); @@ -971,9 +973,7 @@ protected override bool elIsLoop(string procname) return false; } - var lp = info.impl.Proc as LoopProcedure; - - if (lp == null) + if (info.Implementation.Proc is not LoopProcedure) { return false; } diff --git a/Source/VCGeneration/VCGen.cs b/Source/VCGeneration/VCGen.cs index c78db04a0..be9830954 100644 --- a/Source/VCGeneration/VCGen.cs +++ b/Source/VCGeneration/VCGen.cs @@ -6,6 +6,7 @@ using Microsoft.Boogie; using Microsoft.Boogie.GraphUtil; using System.Diagnostics.Contracts; +using System.IO; using System.Runtime.CompilerServices; using System.Threading; using Microsoft.BaseTypes; @@ -65,7 +66,7 @@ class SmokeTester void ObjectInvariant() { Contract.Invariant(parent != null); - Contract.Invariant(impl != null); + Contract.Invariant(run != null); Contract.Invariant(initial != null); Contract.Invariant(cce.NonNullDictionaryAndValues(copies)); Contract.Invariant(cce.NonNull(visited)); @@ -73,21 +74,21 @@ void ObjectInvariant() } VCGen parent; - Implementation impl; + ImplementationRun run; Block initial; int id; Dictionary copies = new Dictionary(); HashSet visited = new HashSet(); VerifierCallback callback; - internal SmokeTester(VCGen par, Implementation i, VerifierCallback callback) + internal SmokeTester(VCGen par, ImplementationRun run, VerifierCallback callback) { Contract.Requires(par != null); - Contract.Requires(i != null); + Contract.Requires(run != null); Contract.Requires(callback != null); parent = par; - impl = i; - initial = i.Blocks[0]; + this.run = run; + initial = run.Implementation.Blocks[0]; this.callback = callback; } @@ -95,32 +96,32 @@ internal SmokeTester(VCGen par, Implementation i, VerifierCallback callback) internal void Copy() { - CloneBlock(impl.Blocks[0]); + CloneBlock(run.Implementation.Blocks[0]); initial = GetCopiedBlocks()[0]; } - internal void Test() + internal void Test(TextWriter traceWriter) { Contract.EnsuresOnThrow(true); - DFS(initial); + DFS(traceWriter, initial); } void TopologicalSortImpl() { - Graph dag = Program.GraphFromImpl(impl); - impl.Blocks = new List(); + Graph dag = Program.GraphFromImpl(run.Implementation); + run.Implementation.Blocks = new List(); foreach (Block b in dag.TopologicalSort()) { Contract.Assert(b != null); - impl.Blocks.Add(b); + run.Implementation.Blocks.Add(b); } } void Emit() { TopologicalSortImpl(); - EmitImpl(Options, impl, false); + EmitImpl(Options, run.Implementation, false); } // this one copies forward @@ -282,7 +283,7 @@ bool IsFalse(Expr e) return BooleanEval(e, ref val) && !val; } - bool CheckUnreachable(Block cur, List seq) + bool CheckUnreachable(TextWriter traceWriter, Block cur, List seq) { Contract.Requires(cur != null); Contract.Requires(seq != null); @@ -310,9 +311,9 @@ bool CheckUnreachable(Block cur, List seq) Block copy = CopyBlock(cur); Contract.Assert(copy != null); copy.Cmds = seq; - List backup = impl.Blocks; + List backup = run.Implementation.Blocks; Contract.Assert(backup != null); - impl.Blocks = GetCopiedBlocks(); + run.Implementation.Blocks = GetCopiedBlocks(); copy.TransferCmd = new ReturnCmd(Token.NoToken); if (Options.TraceVerify) { @@ -321,8 +322,8 @@ bool CheckUnreachable(Block cur, List seq) Emit(); } - parent.CurrentLocalVariables = impl.LocVars; - parent.PassifyImpl(impl, out var mvInfo); + parent.CurrentLocalVariables = run.Implementation.LocVars; + parent.PassifyImpl(run, out var mvInfo); Checker ch = parent.CheckerPool.FindCheckerFor(parent).Result; Contract.Assert(ch != null); @@ -336,16 +337,16 @@ bool CheckUnreachable(Block cur, List seq) var absyIds = new ControlFlowIdMap(); - VCExpr vc = parent.GenerateVC(impl, controlFlowVariableExpr, absyIds, ch.TheoremProver.Context); + VCExpr vc = parent.GenerateVC(run.Implementation, controlFlowVariableExpr, absyIds, ch.TheoremProver.Context); Contract.Assert(vc != null); VCExpr controlFlowFunctionAppl = exprGen.ControlFlowFunctionApplication(exprGen.Integer(BigNum.ZERO), exprGen.Integer(BigNum.ZERO)); VCExpr eqExpr = exprGen.Eq(controlFlowFunctionAppl, - exprGen.Integer(BigNum.FromInt(absyIds.GetId(impl.Blocks[0])))); + exprGen.Integer(BigNum.FromInt(absyIds.GetId(run.Implementation.Blocks[0])))); vc = exprGen.Implies(eqExpr, vc); - impl.Blocks = backup; + run.Implementation.Blocks = backup; if (Options.TraceVerify) { @@ -353,7 +354,7 @@ bool CheckUnreachable(Block cur, List seq) Emit(); } - ch.BeginCheck(cce.NonNull(impl.Name + "_smoke" + id++), vc, new ErrorHandler(Options, absyIds, callback), + ch.BeginCheck(cce.NonNull(Implementation.Name + "_smoke" + id++), vc, new ErrorHandler(Options, absyIds, callback), Options.SmokeTimeout, Options.ResourceLimit, CancellationToken.None); } @@ -386,19 +387,21 @@ bool CheckUnreachable(Block cur, List seq) // copy it again, so we get the version with calls, assignments and such copy = CopyBlock(cur); copy.Cmds = seq; - impl.Blocks = GetCopiedBlocks(); + Implementation.Blocks = GetCopiedBlocks(); TopologicalSortImpl(); - callback.OnUnreachableCode(impl); - impl.Blocks = backup; + callback.OnUnreachableCode(Implementation); + Implementation.Blocks = backup; return true; } return false; } + private Implementation Implementation => run.Implementation; + const bool turnAssertIntoAssumes = false; - void DFS(Block cur) + void DFS(TextWriter traceWriter, Block cur) { Contract.Requires(cur != null); Contract.EnsuresOnThrow(true); @@ -458,7 +461,7 @@ void DFS(Block cur) if (assumeFalse) { - CheckUnreachable(cur, seq); + CheckUnreachable(traceWriter, cur, seq); return; } @@ -474,7 +477,7 @@ void DFS(Block cur) if (ret != null || (go != null && cce.NonNull(go.labelTargets).Count == 0)) { // we end in return, so there will be no more places to check - CheckUnreachable(cur, seq); + CheckUnreachable(traceWriter, cur, seq); } else if (go != null) { @@ -492,13 +495,13 @@ void DFS(Block cur) if (needToCheck) { - CheckUnreachable(cur, seq); + CheckUnreachable(traceWriter, cur, seq); } foreach (Block target in go.labelTargets) { Contract.Assert(target != null); - DFS(target); + DFS(traceWriter, target); } } } @@ -545,12 +548,15 @@ public override void OnProverWarning(string msg) public class CodeExprConversionClosure { + private readonly TextWriter traceWriter; private readonly VCGenOptions options; ControlFlowIdMap absyIds; ProverContext ctx; - public CodeExprConversionClosure(VCGenOptions options, ControlFlowIdMap absyIds, ProverContext ctx) + public CodeExprConversionClosure(TextWriter traceWriter, VCGenOptions options, ControlFlowIdMap absyIds, + ProverContext ctx) { + this.traceWriter = traceWriter; this.options = options; this.absyIds = absyIds; this.ctx = ctx; @@ -565,7 +571,7 @@ public VCExpr CodeExprToVerificationCondition(CodeExpr codeExpr, List gotoCmdOrigins = vcgen.ConvertBlocks2PassiveCmd(codeExpr.Blocks, + Dictionary gotoCmdOrigins = vcgen.ConvertBlocks2PassiveCmd(traceWriter, codeExpr.Blocks, new List(), new ModelViewInfo(codeExpr)); VCExpr startCorrect = vcgen.LetVC(codeExpr.Blocks, null, absyIds, ctx, out var ac, isPositiveContext); VCExpr vce = ctx.ExprGen.Let(bindings, startCorrect); @@ -813,12 +819,14 @@ void ExpandAsserts(Implementation impl) private VCGenOptions Options => CheckerPool.Options; - public override Outcome VerifyImplementation(Implementation impl, VerifierCallback callback, + public override Outcome VerifyImplementation(ImplementationRun run, VerifierCallback callback, CancellationToken cancellationToken) { Contract.EnsuresOnThrow(true); - if (impl.IsSkipVerification(Options)) + var impl = run.Implementation; + + if (run.Implementation.IsSkipVerification(Options)) { return Outcome.Inconclusive; // not sure about this one } @@ -832,16 +840,16 @@ public override Outcome VerifyImplementation(Implementation impl, VerifierCallba watch.Start(); #endif - ConvertCFG2DAG(impl); + ConvertCFG2DAG(run.Implementation); SmokeTester smoke_tester = null; if (Options.SoundnessSmokeTest) { - smoke_tester = new SmokeTester(this, impl, callback); + smoke_tester = new SmokeTester(this, run, callback); smoke_tester.Copy(); } - var gotoCmdOrigins = PassifyImpl(impl, out var mvInfo); + var gotoCmdOrigins = PassifyImpl(run, out var mvInfo); ExpandAsserts(impl); @@ -854,8 +862,7 @@ public override Outcome VerifyImplementation(Implementation impl, VerifierCallba foreach (var a in impl.RecycledFailingAssertions) { var checksum = a.Checksum; - var oldCex = impl.ErrorChecksumToCachedError[checksum] as Counterexample; - if (oldCex != null) + if (impl.ErrorChecksumToCachedError[checksum] is Counterexample oldCex) { if (Options.VerifySnapshots < 3) { @@ -875,13 +882,13 @@ public override Outcome VerifyImplementation(Implementation impl, VerifierCallba } } - var worker = new SplitAndVerifyWorker(Options, this, impl, gotoCmdOrigins, callback, mvInfo, outcome); + var worker = new SplitAndVerifyWorker(Options, this, run, gotoCmdOrigins, callback, mvInfo, outcome); outcome = worker.WorkUntilDone(cancellationToken).Result; ResourceCount = worker.ResourceCount; if (outcome == Outcome.Correct && smoke_tester != null) { - smoke_tester.Test(); + smoke_tester.Test(run.TraceWriter); } callback.OnProgress?.Invoke("done", 0, 0, 1.0); @@ -1695,12 +1702,13 @@ public void DesugarCalls(Implementation impl) } } - public Dictionary PassifyImpl(Implementation impl, out ModelViewInfo mvInfo) + public Dictionary PassifyImpl(ImplementationRun run, out ModelViewInfo mvInfo) { - Contract.Requires(impl != null); + Contract.Requires(run != null); Contract.Requires(program != null); Contract.Ensures(Contract.Result>() != null); + var impl = run.Implementation; Dictionary gotoCmdOrigins = new Dictionary(); Block exitBlock = GenerateUnifiedExit(impl, gotoCmdOrigins); @@ -1796,7 +1804,7 @@ public Dictionary PassifyImpl(Implementation impl, out M } mvInfo = new ModelViewInfo(program, impl); - Convert2PassiveCmd(impl, mvInfo); + Convert2PassiveCmd(run, mvInfo); if (QKeyValue.FindBoolAttribute(impl.Attributes, "may_unverified_instrumentation")) { diff --git a/Test/commandline/SplitOnEveryAssert.bpl b/Test/commandline/SplitOnEveryAssert.bpl index 77e936d5d..640606ff1 100644 --- a/Test/commandline/SplitOnEveryAssert.bpl +++ b/Test/commandline/SplitOnEveryAssert.bpl @@ -2,6 +2,7 @@ // RUN: %boogie /vcsSplitOnEveryAssert /errorTrace:0 /trace "%s" > "%t" // RUN: %OutputCheck --file-to-check "%t" "%s" +// CHECK: Verifying Ex ... // CHECK: checking split 1/12, .* // CHECK: checking split 2/12, .* // CHECK: checking split 3/12, .* @@ -15,7 +16,6 @@ // CHECK: checking split 10/12, .* // CHECK: checking split 11/12, .* // CHECK: checking split 12/12, .* -// CHECK: Verifying Ex ... // CHECK-L: SplitOnEveryAssert.bpl(32,5): Error: This assertion might not hold. procedure Ex() returns (y: int) diff --git a/Test/snapshots/runtest.snapshot b/Test/snapshots/runtest.snapshot index ac62914f9..6e78db0f8 100644 --- a/Test/snapshots/runtest.snapshot +++ b/Test/snapshots/runtest.snapshot @@ -1,5 +1,4 @@ -// Using boogie instead of parallel-boogie here since parallel Boogie doesn't work well with -traceCaching -// RUN: %boogie -errorTrace:0 -traceCaching:1 -verifySnapshots:2 -verifySeparately 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: %boogie -errorTrace:0 -traceCaching:1 -verifySnapshots:3 -verifySeparately Snapshots41.bpl >> "%t" +// RUN: %parallel-boogie -errorTrace:0 -traceCaching:1 -verifySnapshots:2 -verifySeparately 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: %parallel-boogie -errorTrace:0 -traceCaching:1 -verifySnapshots:3 -verifySeparately Snapshots41.bpl >> "%t" // RUN: %diff "%s.expect" "%t" // UNSUPPORTED: batch_mode diff --git a/Test/test0/SplitOnEveryAssert.bpl b/Test/test0/SplitOnEveryAssert.bpl index d6d731264..67a75fb21 100644 --- a/Test/test0/SplitOnEveryAssert.bpl +++ b/Test/test0/SplitOnEveryAssert.bpl @@ -2,6 +2,7 @@ // RUN: %boogie /errorTrace:0 /trace "%s" > "%t" // RUN: %OutputCheck --file-to-check "%t" "%s" +// CHECK: Verifying DoTheSplitting ... // CHECK: checking split 1/12, .* // CHECK: checking split 2/12, .* // CHECK: checking split 3/12, .* @@ -15,7 +16,6 @@ // CHECK: checking split 10/12, .* // CHECK: checking split 11/12, .* // CHECK: checking split 12/12, .* -// CHECK: Verifying DoTheSplitting ... // CHECK-L: SplitOnEveryAssert.bpl(37,5): Error: This assertion might not hold. // Verify the second procedure is NOT split. .* is necessary to match the blank line in-between. From ce5ecbf95a1e86235f9142e5c85d9a1660da0f78 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 11 Mar 2022 19:02:10 +0100 Subject: [PATCH 29/32] Move method --- Source/ExecutionEngine/ExecutionEngine.cs | 56 +++++++++++------------ 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index 1371cc417..33518dd69 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -957,6 +957,33 @@ private async Task VerifyImplementation( return verificationResult; } + private VerificationResult GetCachedVerificationResult(Implementation impl, TextWriter output) + { + if (0 >= Options.VerifySnapshots) + { + return null; + } + + var cachedResults = Cache.Lookup(impl, Options.RunDiagnosticsOnTimeout, out var priority); + if (cachedResults == null || priority != Priority.SKIP) + { + return null; + } + + if (Options.VerifySnapshots < 3 || + cachedResults.Outcome == ConditionGeneration.Outcome.Correct) { + Options.Printer.Inform($"Retrieving cached verification result for implementation {impl.Name}...", output); + return cachedResults; + } + + return null; + } + + private ConditionGeneration CreateVCGen(Program program) + { + return new VCGen(program, checkerPool); + } + private async Task VerifyImplementationWithoutCaching(Program program, PipelineStatistics stats, ErrorReporterDelegate er, string requestId, Dictionary> extractLoopMappingInfo, @@ -1029,32 +1056,6 @@ private async Task VerifyImplementationWithoutCaching(Progra return verificationResult; } - private VerificationResult GetCachedVerificationResult(Implementation impl, TextWriter output) - { - if (0 >= Options.VerifySnapshots) - { - return null; - } - - var cachedResults = Cache.Lookup(impl, Options.RunDiagnosticsOnTimeout, out var priority); - if (cachedResults == null || priority != Priority.SKIP) - { - return null; - } - - if (Options.VerifySnapshots < 3 || - cachedResults.Outcome == ConditionGeneration.Outcome.Correct) { - Options.Printer.Inform($"Retrieving cached verification result for implementation {impl.Name}...", output); - return cachedResults; - } - - return null; - } - - private ConditionGeneration CreateVCGen(Program program) - { - return new VCGen(program, checkerPool); - } #region Houdini @@ -1176,8 +1177,7 @@ public void ProcessOutcome(OutputPrinter printer, ConditionGeneration.Outcome ou printer.Inform(timeIndication + OutcomeIndication(outcome, errors), tw); ReportOutcome(printer, outcome, er, implName, implTok, requestId, msgIfVerifies, tw, timeLimit, errors); - } - + } public void ReportOutcome(OutputPrinter printer, ConditionGeneration.Outcome outcome, ErrorReporterDelegate er, string implName, From f3ed07d9aaa591d539b86f3ddd5204e25ef9b4c2 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 11 Mar 2022 19:05:53 +0100 Subject: [PATCH 30/32] Move exception handling --- Source/ExecutionEngine/ExecutionEngine.cs | 32 +++++++---------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index 33518dd69..a073084ec 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -1009,8 +1009,7 @@ private async Task VerifyImplementationWithoutCaching(Progra } } } - } - catch (VCGenException e) { + } catch (VCGenException e) { var errorInfo = ErrorInformationFactory.CreateErrorInformation(impl.tok, $"{e.Message} (encountered in implementation {impl.Name}).", requestId, "Error"); errorInfo.ImplementationName = impl.Name; @@ -1023,30 +1022,19 @@ private async Task VerifyImplementationWithoutCaching(Progra verificationResult.Errors = null; verificationResult.Outcome = VCGen.Outcome.Inconclusive; - } - catch (ProverDiedException) { + } catch (ProverDiedException) { throw; - } - catch (UnexpectedProverOutputException upo) { - Options.Printer.AdvisoryWriteLine(traceWriter, "Advisory: {0} SKIPPED because of internal error: unexpected prover output: {1}", + } catch (UnexpectedProverOutputException upo) { + Options.Printer.AdvisoryWriteLine(traceWriter, + "Advisory: {0} SKIPPED because of internal error: unexpected prover output: {1}", impl.Name, upo.Message); verificationResult.Errors = null; verificationResult.Outcome = VCGen.Outcome.Inconclusive; - } - catch (AggregateException ae) { - ae.Flatten().Handle(e => - { - // TODO Do we need to move this into a catch? - if (e is IOException) { - Options.Printer.AdvisoryWriteLine(traceWriter, "Advisory: {0} SKIPPED due to I/O exception: {1}", - impl.Name, e.Message); - verificationResult.Errors = null; - verificationResult.Outcome = VCGen.Outcome.SolverException; - return true; - } - - return false; - }); + } catch (IOException e) { + Options.Printer.AdvisoryWriteLine(traceWriter, "Advisory: {0} SKIPPED due to I/O exception: {1}", + impl.Name, e.Message); + verificationResult.Errors = null; + verificationResult.Outcome = VCGen.Outcome.SolverException; } verificationResult.ProofObligationCountAfter = vcgen.CumulativeAssertionCount; From 4353489fa1394be6ac14161cd16974f48219cff4 Mon Sep 17 00:00:00 2001 From: Shaz Qadeer Date: Sun, 13 Mar 2022 13:06:10 -0700 Subject: [PATCH 31/32] fixed bug in the injection of preconditions for yielding procedures instrumentation (#532) --- .../YieldingProcInstrumentation.cs | 37 ++++++++++--------- Test/civl/Program4-fail.bpl | 3 +- Test/civl/Program4-fail.bpl.expect | 10 ++--- Test/civl/linear-test.bpl | 9 +++++ Test/civl/linear-test.bpl.expect | 2 + 5 files changed, 38 insertions(+), 23 deletions(-) create mode 100644 Test/civl/linear-test.bpl create mode 100644 Test/civl/linear-test.bpl.expect diff --git a/Source/Concurrency/YieldingProcInstrumentation.cs b/Source/Concurrency/YieldingProcInstrumentation.cs index c2da81f9a..ddf6d3a50 100644 --- a/Source/Concurrency/YieldingProcInstrumentation.cs +++ b/Source/Concurrency/YieldingProcInstrumentation.cs @@ -250,31 +250,34 @@ private Dictionary> CreatePreconditions( foreach (var impl in absyMap.Keys.OfType()) { var initCmds = new List(); + + // Global variables must be havoced to model the yield upon entering a yielding procedure if (civlTypeChecker.GlobalVariables.Count() > 0) { initCmds.Add(CmdHelper.HavocCmd( civlTypeChecker.GlobalVariables.Select(v => Expr.Ident(v)).ToList())); - linearPermissionInstrumentation.DisjointnessExprs(impl, true).ForEach( - expr => initCmds.Add(CmdHelper.AssumeCmd(expr))); + } - Substitution procToImplInParams = Substituter.SubstitutionFromDictionary(impl.Proc.InParams - .Zip(impl.InParams).ToDictionary(x => x.Item1, x => (Expr) Expr.Ident(x.Item2))); + linearPermissionInstrumentation.DisjointnessExprs(impl, true).ForEach( + expr => initCmds.Add(CmdHelper.AssumeCmd(expr))); - impl.Proc.Requires.ForEach(req => - initCmds.Add(new AssumeCmd(req.tok, Substituter.Apply(procToImplInParams, req.Condition)))); + Substitution procToImplInParams = Substituter.SubstitutionFromDictionary(impl.Proc.InParams + .Zip(impl.InParams).ToDictionary(x => x.Item1, x => (Expr) Expr.Ident(x.Item2))); - foreach (var callCmd in GetYieldingProc(impl).yieldRequires) + impl.Proc.Requires.ForEach(req => + initCmds.Add(new AssumeCmd(req.tok, Substituter.Apply(procToImplInParams, req.Condition)))); + + foreach (var callCmd in GetYieldingProc(impl).yieldRequires) + { + var yieldInvariant = civlTypeChecker.procToYieldInvariant[callCmd.Proc]; + if (layerNum == yieldInvariant.LayerNum) { - var yieldInvariant = civlTypeChecker.procToYieldInvariant[callCmd.Proc]; - if (layerNum == yieldInvariant.LayerNum) - { - Substitution callFormalsToActuals = Substituter.SubstitutionFromDictionary(callCmd.Proc.InParams - .Zip(callCmd.Ins) - .ToDictionary(x => x.Item1, x => (Expr) ExprHelper.Old(x.Item2))); - callCmd.Proc.Requires.ForEach(req => initCmds.Add(new AssumeCmd(req.tok, - Substituter.Apply(procToImplInParams, - Substituter.Apply(callFormalsToActuals, req.Condition))))); - } + Substitution callFormalsToActuals = Substituter.SubstitutionFromDictionary(callCmd.Proc.InParams + .Zip(callCmd.Ins) + .ToDictionary(x => x.Item1, x => (Expr) ExprHelper.Old(x.Item2))); + callCmd.Proc.Requires.ForEach(req => initCmds.Add(new AssumeCmd(req.tok, + Substituter.Apply(procToImplInParams, + Substituter.Apply(callFormalsToActuals, req.Condition))))); } } diff --git a/Test/civl/Program4-fail.bpl b/Test/civl/Program4-fail.bpl index a7ec631f1..8128c2fd2 100644 --- a/Test/civl/Program4-fail.bpl +++ b/Test/civl/Program4-fail.bpl @@ -16,7 +16,8 @@ p1() } procedure {:yields} {:layer 1} -// {:yield_requires "yield_x", x} // Without this precondition, the usage of old(.) in the postcondition is not sound. +// The following commented precondition is required to verify the postcondition. +// {:yield_requires "yield_x", x} {:yield_ensures "yield_x", old(x) + 3} p2() { diff --git a/Test/civl/Program4-fail.bpl.expect b/Test/civl/Program4-fail.bpl.expect index 615fe2f22..8aa7adf90 100644 --- a/Test/civl/Program4-fail.bpl.expect +++ b/Test/civl/Program4-fail.bpl.expect @@ -1,9 +1,9 @@ -Program4-fail.bpl(26,1): Error: A postcondition might not hold on this return path. +Program4-fail.bpl(27,1): Error: A postcondition might not hold on this return path. Program4-fail.bpl(6,1): Related location: This is the postcondition that might not hold. Execution trace: - Program4-fail.bpl(23,3): anon0 - Program4-fail.bpl(36,5): inline$AtomicIncr$0$anon0 - Program4-fail.bpl(36,5): inline$AtomicIncr$1$anon0 - Program4-fail.bpl(36,5): inline$AtomicIncr$2$anon0 + Program4-fail.bpl(24,3): anon0 + Program4-fail.bpl(37,5): inline$AtomicIncr$0$anon0 + Program4-fail.bpl(37,5): inline$AtomicIncr$1$anon0 + Program4-fail.bpl(37,5): inline$AtomicIncr$2$anon0 Boogie program verifier finished with 3 verified, 1 error diff --git a/Test/civl/linear-test.bpl b/Test/civl/linear-test.bpl new file mode 100644 index 000000000..52f98bdf9 --- /dev/null +++ b/Test/civl/linear-test.bpl @@ -0,0 +1,9 @@ +// RUN: %parallel-boogie "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +type {:linear "A"} A = int; + +procedure {:yields}{:layer 1} foo({:linear "A"} x: int, {:linear "A"} y: int) +{ + assert {:layer 1} x != y; +} diff --git a/Test/civl/linear-test.bpl.expect b/Test/civl/linear-test.bpl.expect new file mode 100644 index 000000000..37fad75c9 --- /dev/null +++ b/Test/civl/linear-test.bpl.expect @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 1 verified, 0 errors From b0214fc8dd58f62c3acf88c8eb914fc05c26be87 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Mon, 14 Mar 2022 12:29:03 +0100 Subject: [PATCH 32/32] Add backwards compatibility methods --- Source/ExecutionEngine/ExecutionEngine.cs | 56 ++++++++++++----------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index a073084ec..060151672 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -173,26 +173,21 @@ protected static string CleanUp(string msg) #endregion - public class ExecutionEngine : IDisposable - { + public class ExecutionEngine : IDisposable { public static ErrorInformationFactory ErrorInformationFactory { get; } = new(); static int autoRequestIdCount; static readonly string AutoRequestIdPrefix = "auto_request_id_"; - public static string FreshRequestId() - { + public static string FreshRequestId() { var id = Interlocked.Increment(ref autoRequestIdCount); return AutoRequestIdPrefix + id; } - public static int AutoRequestId(string id) - { - if (id.StartsWith(AutoRequestIdPrefix)) - { - if (int.TryParse(id.Substring(AutoRequestIdPrefix.Length), out var result)) - { + public static int AutoRequestId(string id) { + if (id.StartsWith(AutoRequestIdPrefix)) { + if (int.TryParse(id.Substring(AutoRequestIdPrefix.Length), out var result)) { return result; } } @@ -205,16 +200,14 @@ public static int AutoRequestId(string id) static readonly MemoryCache programCache = new MemoryCache("ProgramCache"); static readonly CacheItemPolicy policy = new CacheItemPolicy - {SlidingExpiration = new TimeSpan(0, 10, 0), Priority = CacheItemPriority.Default}; + { SlidingExpiration = new TimeSpan(0, 10, 0), Priority = CacheItemPriority.Default }; - public static Program CachedProgram(string programId) - { + public static Program CachedProgram(string programId) { var result = programCache.Get(programId) as Program; return result; } - public ExecutionEngine(ExecutionEngineOptions options, VerificationResultCache cache) - { + public ExecutionEngine(ExecutionEngineOptions options, VerificationResultCache cache) { Options = options; Cache = cache; checkerPool = new CheckerPool(options); @@ -245,20 +238,17 @@ static readonly ConcurrentDictionary static ThreadTaskScheduler LargeStackScheduler = new ThreadTaskScheduler(16 * 1024 * 1024); - public bool ProcessFiles(TextWriter output, IList fileNames, bool lookForSnapshots = true, string programId = null) - { + public bool ProcessFiles(TextWriter output, IList fileNames, bool lookForSnapshots = true, + string programId = null) { Contract.Requires(cce.NonNullElements(fileNames)); - if (Options.VerifySeparately && 1 < fileNames.Count) - { - return fileNames.All(f => ProcessFiles( output, new List {f}, lookForSnapshots, f)); + if (Options.VerifySeparately && 1 < fileNames.Count) { + return fileNames.All(f => ProcessFiles(output, new List { f }, lookForSnapshots, f)); } - if (0 <= Options.VerifySnapshots && lookForSnapshots) - { + if (0 <= Options.VerifySnapshots && lookForSnapshots) { var snapshotsByVersion = LookForSnapshots(fileNames); - return snapshotsByVersion.All(s => - { + return snapshotsByVersion.All(s => { // BUG: Reusing checkers during snapshots doesn't work, even though it should. We create a new engine (and thus checker pool) to workaround this. using var engine = new ExecutionEngine(Options, Cache); return engine.ProcessFiles(output, new List(s), false, programId); @@ -268,13 +258,19 @@ public bool ProcessFiles(TextWriter output, IList fileNames, bool lookFo using XmlFileScope xf = new XmlFileScope(Options.XmlSink, fileNames[^1]); Program program = ParseBoogieProgram(fileNames, false); var bplFileName = fileNames[^1]; - if (program == null) - { + if (program == null) { return true; } + return ProcessProgram(output, program, bplFileName, programId).Result; } + [Obsolete("Please inline this method call")] + public bool ProcessProgram(Program program, string bplFileName, + string programId = null) { + return ProcessProgram(Console.Out, program, bplFileName, programId).Result; + } + public async Task ProcessProgram(TextWriter output, Program program, string bplFileName, string programId = null) { if (programId == null) @@ -658,6 +654,14 @@ public void Inline(Program program) } } + [Obsolete("Please inline this method call")] + public PipelineOutcome InferAndVerify( + Program program, + PipelineStatistics stats, + string programId = null, + ErrorReporterDelegate er = null, string requestId = null) { + return InferAndVerify(Console.Out, program, stats, programId, er, requestId).Result; + } /// /// Given a resolved and type checked Boogie program, infers invariants for the program