From 962f8d5252b3f5ec4d19e0cd2a430934bd55cc6d Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Sun, 28 Jun 2015 01:44:30 +0100 Subject: Normalise line endings using a .gitattributes file. Unfortunately this required that this commit globally modify most files. If you want to use git blame to see the real author of a line use the ``-w`` flag so that whitespace changes are ignored. --- Source/VCGeneration/Check.cs | 1378 ++--- Source/VCGeneration/ConditionGeneration.cs | 4018 +++++++------- Source/VCGeneration/Context.cs | 512 +- Source/VCGeneration/ExprExtensions.cs | 706 +-- Source/VCGeneration/FixedpointVC.cs | 4490 ++++++++-------- Source/VCGeneration/OrderingAxioms.cs | 676 +-- Source/VCGeneration/RPFP.cs | 1218 ++--- Source/VCGeneration/StratifiedVC.cs | 5806 ++++++++++----------- Source/VCGeneration/VC.cs | 7802 ++++++++++++++-------------- Source/VCGeneration/VCGeneration.csproj | 446 +- Source/VCGeneration/Wlp.cs | 510 +- Source/VCGeneration/cce.cs | 210 +- 12 files changed, 13886 insertions(+), 13886 deletions(-) (limited to 'Source/VCGeneration') diff --git a/Source/VCGeneration/Check.cs b/Source/VCGeneration/Check.cs index 71ae21c6..3c3b5cae 100644 --- a/Source/VCGeneration/Check.cs +++ b/Source/VCGeneration/Check.cs @@ -1,689 +1,689 @@ -//----------------------------------------------------------------------------- -// -// Copyright (C) Microsoft Corporation. All Rights Reserved. -// -//----------------------------------------------------------------------------- -using System; -using System.Linq; -using System.Collections; -using System.Collections.Generic; -using System.Threading; -using System.IO; -using System.Text.RegularExpressions; -using System.Diagnostics; -using System.Diagnostics.Contracts; -using Microsoft.Boogie.AbstractInterpretation; -using Microsoft.Boogie.VCExprAST; -using Microsoft.Basetypes; -using System.Threading.Tasks; - -namespace Microsoft.Boogie { - - enum CheckerStatus - { - Idle, - Ready, - Busy, - Closed - } - - /// - /// Interface to the theorem prover specialized to Boogie. - /// - /// This class creates the appropriate background axioms. There - /// should be one instance per BoogiePL program. - /// - public sealed class Checker { - [ContractInvariantMethod] - void ObjectInvariant() { - Contract.Invariant(gen != null); - Contract.Invariant(thmProver != null); - } - - private readonly VCExpressionGenerator gen; - - private ProverInterface thmProver; - private int timeout; - - // state for the async interface - private volatile ProverInterface.Outcome outcome; - private volatile bool hasOutput; - private volatile UnexpectedProverOutputException outputExn; - private DateTime proverStart; - private TimeSpan proverRunTime; - private volatile ProverInterface.ErrorHandler handler; - private volatile CheckerStatus status; - public volatile Program Program; - - public void GetReady() - { - Contract.Requires(IsIdle); - - status = CheckerStatus.Ready; - } - - public void GoBackToIdle() - { - Contract.Requires(IsBusy); - - status = CheckerStatus.Idle; - } - - public Task ProverTask { get; set; } - - public bool WillingToHandle(int timeout, Program prog) { - return status == CheckerStatus.Idle && timeout == this.timeout && (prog == null || Program == prog); - } - - public VCExpressionGenerator VCExprGen { - get { - Contract.Ensures(Contract.Result() != null); - return this.gen; - } - } - public ProverInterface TheoremProver { - get { - Contract.Ensures(Contract.Result() != null); - return this.thmProver; - } - } - - ///////////////////////////////////////////////////////////////////////////////// - // We share context information for the same program between different Checkers - - private struct ContextCacheKey { - [ContractInvariantMethod] - void ObjectInvariant() { - Contract.Invariant(program != null); - } - - public readonly Program program; - - public ContextCacheKey(Program prog) { - Contract.Requires(prog != null); - this.program = prog; - } - - [Pure] - [Reads(ReadsAttribute.Reads.Nothing)] - public override bool Equals(object that) { - if (that is ContextCacheKey) { - ContextCacheKey thatKey = (ContextCacheKey)that; - return this.program.Equals(thatKey.program); - } - return false; - } - - [Pure] - public override int GetHashCode() { - return this.program.GetHashCode(); - } - } - - ///////////////////////////////////////////////////////////////////////////////// - - /// - /// Constructor. Initialize a checker with the program and log file. - /// Optionally, use prover context provided by parameter "ctx". - /// - public Checker(VC.ConditionGeneration vcgen, Program prog, string/*?*/ logFilePath, bool appendLogFile, int timeout, ProverContext ctx = null) { - Contract.Requires(vcgen != null); - Contract.Requires(prog != null); - this.timeout = timeout; - this.Program = prog; - - ProverOptions options = cce.NonNull(CommandLineOptions.Clo.TheProverFactory).BlankProverOptions(); - - if (logFilePath != null) { - options.LogFilename = logFilePath; - if (appendLogFile) - options.AppendLogFile = appendLogFile; - } - - if (timeout > 0) { - options.TimeLimit = timeout * 1000; - } - - options.Parse(CommandLineOptions.Clo.ProverOptions); - - ContextCacheKey key = new ContextCacheKey(prog); - ProverInterface prover; - - if (vcgen.CheckerCommonState == null) { - vcgen.CheckerCommonState = new Dictionary(); - } - IDictionary/*!>!*/ cachedContexts = (IDictionary)vcgen.CheckerCommonState; - - if (ctx == null && cachedContexts.TryGetValue(key, out ctx)) - { - ctx = (ProverContext)cce.NonNull(ctx).Clone(); - prover = (ProverInterface) - CommandLineOptions.Clo.TheProverFactory.SpawnProver(options, ctx); - } else { - if (ctx == null) ctx = (ProverContext)CommandLineOptions.Clo.TheProverFactory.NewProverContext(options); - - Setup(prog, ctx); - - // we first generate the prover and then store a clone of the - // context in the cache, so that the prover can setup stuff in - // the context to be cached - prover = (ProverInterface) - CommandLineOptions.Clo.TheProverFactory.SpawnProver(options, ctx); - cachedContexts.Add(key, cce.NonNull((ProverContext)ctx.Clone())); - } - - this.thmProver = prover; - this.gen = prover.VCExprGen; - } - - public void Retarget(Program prog, ProverContext ctx, int timeout = 0) - { - lock (this) - { - hasOutput = default(bool); - outcome = default(ProverInterface.Outcome); - outputExn = default(UnexpectedProverOutputException); - handler = default(ProverInterface.ErrorHandler); - TheoremProver.FullReset(gen); - ctx.Reset(); - Setup(prog, ctx); - this.timeout = timeout; - SetTimeout(); - } - } - - public void RetargetWithoutReset(Program prog, ProverContext ctx) - { - ctx.Clear(); - Setup(prog, ctx); - } - - - public void SetTimeout() - { - if (0 < timeout) - { - TheoremProver.SetTimeOut(timeout * 1000); - } - else - { - TheoremProver.SetTimeOut(0); - } - } - - /// - /// Set up the context. - /// - private void Setup(Program prog, ProverContext ctx) - { - Program = prog; - // TODO(wuestholz): Is this lock necessary? - lock (Program.TopLevelDeclarations) - { - foreach (Declaration decl in Program.TopLevelDeclarations) - { - Contract.Assert(decl != null); - var typeDecl = decl as TypeCtorDecl; - var constDecl = decl as Constant; - var funDecl = decl as Function; - var axiomDecl = decl as Axiom; - var glVarDecl = decl as GlobalVariable; - if (typeDecl != null) - { - ctx.DeclareType(typeDecl, null); - } - else if (constDecl != null) - { - ctx.DeclareConstant(constDecl, constDecl.Unique, null); - } - else if (funDecl != null) - { - ctx.DeclareFunction(funDecl, null); - } - else if (axiomDecl != null) - { - ctx.AddAxiom(axiomDecl, null); - } - else if (glVarDecl != null) - { - ctx.DeclareGlobalVariable(glVarDecl, null); - } - } - } - } - - /// - /// Clean-up. - /// - public void Close() { - thmProver.Close(); - status = CheckerStatus.Closed; - } - - /// - /// Push a Verification Condition as an Axiom - /// (Required for Doomed Program Point detection) - /// - public void PushVCExpr(VCExpr vc) { - Contract.Requires(vc != null); - //thmProver.Context.AddAxiom(vc); - thmProver.PushVCExpression(vc); - } - - public bool IsBusy { - get { - return status == CheckerStatus.Busy; - } - } - - public bool IsReady - { - get - { - return status == CheckerStatus.Ready; - } - } - - public bool IsClosed { - get { - return status == CheckerStatus.Closed; - } - } - - public bool IsIdle - { - get - { - return status == CheckerStatus.Idle; - } - } - - public bool HasOutput { - get { - return hasOutput; - } - } - - public TimeSpan ProverRunTime { - get { - return proverRunTime; - } - } - - private void WaitForOutput(object dummy) { - lock (this) - { - try - { - outcome = thmProver.CheckOutcome(cce.NonNull(handler)); - } - catch (UnexpectedProverOutputException e) - { - outputExn = e; - } - - switch (outcome) - { - case ProverInterface.Outcome.Valid: - thmProver.LogComment("Valid"); - break; - case ProverInterface.Outcome.Invalid: - thmProver.LogComment("Invalid"); - break; - case ProverInterface.Outcome.TimeOut: - thmProver.LogComment("Timed out"); - break; - case ProverInterface.Outcome.OutOfMemory: - thmProver.LogComment("Out of memory"); - break; - case ProverInterface.Outcome.Undetermined: - thmProver.LogComment("Undetermined"); - break; - } - - hasOutput = true; - proverRunTime = DateTime.UtcNow - proverStart; - } - } - - public void BeginCheck(string descriptiveName, VCExpr vc, ProverInterface.ErrorHandler handler) { - Contract.Requires(descriptiveName != null); - Contract.Requires(vc != null); - Contract.Requires(handler != null); - Contract.Requires(IsReady); - - status = CheckerStatus.Busy; - hasOutput = false; - outputExn = null; - this.handler = handler; - - thmProver.Reset(gen); - SetTimeout(); - proverStart = DateTime.UtcNow; - thmProver.BeginCheck(descriptiveName, vc, handler); - // gen.ClearSharedFormulas(); PR: don't know yet what to do with this guy - - ProverTask = Task.Factory.StartNew(() => { WaitForOutput(null); }, TaskCreationOptions.LongRunning); - } - - public ProverInterface.Outcome ReadOutcome() { - Contract.Requires(IsBusy); - Contract.Requires(HasOutput); - Contract.EnsuresOnThrow(true); - - hasOutput = false; - - if (outputExn != null) { - throw outputExn; - } - - return outcome; - } - } - - // ----------------------------------------------------------------------------------------------- - // ----------------------------------------------------------------------------------------------- - // ----------------------------------------------------------------------------------------------- - - public abstract class ProverInterface { - public static ProverInterface CreateProver(Program prog, string/*?*/ logFilePath, bool appendLogFile, int timeout, int taskID = -1) { - Contract.Requires(prog != null); - - ProverOptions options = cce.NonNull(CommandLineOptions.Clo.TheProverFactory).BlankProverOptions(); - - if (logFilePath != null) { - options.LogFilename = logFilePath; - if (appendLogFile) - options.AppendLogFile = appendLogFile; - } - - if (timeout > 0) { - options.TimeLimit = timeout * 1000; - } - - if (taskID >= 0) { - options.Parse(CommandLineOptions.Clo.Cho[taskID].ProverOptions); - } else { - options.Parse(CommandLineOptions.Clo.ProverOptions); - } - - ProverContext ctx = (ProverContext)CommandLineOptions.Clo.TheProverFactory.NewProverContext(options); - - // set up the context - foreach (Declaration decl in prog.TopLevelDeclarations) { - Contract.Assert(decl != null); - TypeCtorDecl t = decl as TypeCtorDecl; - if (t != null) { - ctx.DeclareType(t, null); - } - } - foreach (Declaration decl in prog.TopLevelDeclarations) { - Contract.Assert(decl != null); - Constant c = decl as Constant; - if (c != null) { - ctx.DeclareConstant(c, c.Unique, null); - } - else { - Function f = decl as Function; - if (f != null) { - ctx.DeclareFunction(f, null); - } - } - } - foreach (var ax in prog.Axioms) { - ctx.AddAxiom(ax, null); - } - foreach (Declaration decl in prog.TopLevelDeclarations) { - Contract.Assert(decl != null); - GlobalVariable v = decl as GlobalVariable; - if (v != null) { - ctx.DeclareGlobalVariable(v, null); - } - } - - return (ProverInterface)CommandLineOptions.Clo.TheProverFactory.SpawnProver(options, ctx); - } - - public enum Outcome { - Valid, - Invalid, - TimeOut, - OutOfMemory, - Undetermined, - Bounded - } - public class ErrorHandler { - // Used in CheckOutcomeCore - public virtual int StartingProcId() - { - return 0; - } - - public virtual void OnModel(IList labels, Model model, Outcome proverOutcome) { - Contract.Requires(cce.NonNullElements(labels)); - } - - public virtual void OnResourceExceeded(string message, IEnumerable> assertCmds = null) { - Contract.Requires(message != null); - } - - public virtual void OnProverWarning(string message) - { - Contract.Requires(message != null); - switch (CommandLineOptions.Clo.PrintProverWarnings) { - case CommandLineOptions.ProverWarnings.None: - break; - case CommandLineOptions.ProverWarnings.Stdout: - Console.WriteLine("Prover warning: " + message); - break; - case CommandLineOptions.ProverWarnings.Stderr: - Console.Error.WriteLine("Prover warning: " + message); - break; - default: - Contract.Assume(false); - throw new cce.UnreachableException(); // unexpected case - } - } - - public virtual Absy Label2Absy(string label) { - Contract.Requires(label != null); - Contract.Ensures(Contract.Result() != null); - - throw new System.NotImplementedException(); - } - } - public abstract void BeginCheck(string descriptiveName, VCExpr vc, ErrorHandler handler); - - public virtual Outcome CheckRPFP(string descriptiveName, RPFP vc, ErrorHandler handler, - out RPFP.Node cex, - Dictionary> varSubst, Dictionary extra_bound = null) - { - throw new System.NotImplementedException(); - } - [NoDefaultContract] - public abstract Outcome CheckOutcome(ErrorHandler handler, int taskID = -1); - public virtual string[] CalculatePath(int controlFlowConstant) { - throw new System.NotImplementedException(); - } - public virtual void LogComment(string comment) { - Contract.Requires(comment != null); - } - public virtual void Close() { - } - - public abstract void Reset(VCExpressionGenerator gen); - - public abstract void FullReset(VCExpressionGenerator gen); - - /// - /// MSchaef: Allows to Push a VCExpression as Axiom on the prover stack (beta) - /// for now it is only implemented by ProcessTheoremProver and still requires some - /// testing - /// - public virtual void PushVCExpression(VCExpr vc) { - Contract.Requires(vc != null); - throw new NotImplementedException(); - } - public virtual string VCExpressionToString(VCExpr vc) { - Contract.Requires(vc != null); - Contract.Ensures(Contract.Result() != null); - throw new NotImplementedException(); - } - public virtual void Pop() { - Contract.EnsuresOnThrow(true); - throw new NotImplementedException(); - } - public virtual int NumAxiomsPushed() { - throw new NotImplementedException(); - } - public virtual int FlushAxiomsToTheoremProver() { - Contract.EnsuresOnThrow(true); - throw new NotImplementedException(); - } - - // (assert vc) - public virtual void Assert(VCExpr vc, bool polarity) - { - throw new NotImplementedException(); - } - - // (assert implicit-axioms) - public virtual void AssertAxioms() - { - throw new NotImplementedException(); - } - - // (check-sat) - public virtual void Check() - { - throw new NotImplementedException(); - } - - // (check-sat + get-unsat-core + checkOutcome) - public virtual Outcome CheckAssumptions(List assumptions, out List unsatCore, ErrorHandler handler) - { - throw new NotImplementedException(); - } - - public virtual Outcome CheckAssumptions(List hardAssumptions, List softAssumptions, out List unsatisfiedSoftAssumptions, ErrorHandler handler) { - throw new NotImplementedException(); - } - - public virtual Outcome CheckOutcomeCore(ErrorHandler handler, int taskID = -1) - { - throw new NotImplementedException(); - } - - // (push 1) - public virtual void Push() - { - throw new NotImplementedException(); - } - - // Set theorem prover timeout for the next "check-sat" - public virtual void SetTimeOut(int ms) - { } - - public abstract ProverContext Context { - get; - } - - public abstract VCExpressionGenerator VCExprGen { - get; - } - - public virtual void DefineMacro(Macro fun, VCExpr vc) { - throw new NotImplementedException(); - } - - public class VCExprEvaluationException : Exception - { - - } - - public virtual object Evaluate(VCExpr expr) - { - throw new NotImplementedException(); - } - - ////////////////////// - // For interpolation queries - ////////////////////// - - // Assert vc tagged with a name - public virtual void AssertNamed(VCExpr vc, bool polarity, string name) - { - throw new NotImplementedException(); - } - - // Returns Interpolant(A,B) - public virtual VCExpr ComputeInterpolant(VCExpr A, VCExpr B) - { - throw new NotImplementedException(); - } - - // Returns for each l, Interpolant(root + (leaves - l), l) - // Preconditions: - // leaves cannot have subformulas with same variable names - // Both root and leaves should have been previously named via AssertNamed - public virtual List GetTreeInterpolant(List root, List leaves) - { - throw new NotImplementedException(); - } - - } - - public class ProverInterfaceContracts : ProverInterface { - public override ProverContext Context { - get { - Contract.Ensures(Contract.Result() != null); - - throw new NotImplementedException(); - } - } - public override VCExpressionGenerator VCExprGen { - get { - Contract.Ensures(Contract.Result() != null); - - throw new NotImplementedException(); - } - } - public override void BeginCheck(string descriptiveName, VCExpr vc, ErrorHandler handler) {/*Contract.Requires(descriptiveName != null);*/ - //Contract.Requires(vc != null); - //Contract.Requires(handler != null); - throw new NotImplementedException(); - } - [NoDefaultContract] - public override Outcome CheckOutcome(ErrorHandler handler, int taskID = -1) { - //Contract.Requires(handler != null); - Contract.EnsuresOnThrow(true); - throw new NotImplementedException(); - } - - public override void Reset(VCExpressionGenerator gen) - { - throw new NotImplementedException(); - } - - public override void FullReset(VCExpressionGenerator gen) - { - throw new NotImplementedException(); - } - } - - public class ProverException : Exception { - public ProverException(string s) - : base(s) { - } - } - public class UnexpectedProverOutputException : ProverException { - public UnexpectedProverOutputException(string s) - : base(s) { - } - } - public class ProverDiedException : UnexpectedProverOutputException { - public ProverDiedException() - : base("Prover died with no further output, perhaps it ran out of memory or was killed.") { - } - } -} +//----------------------------------------------------------------------------- +// +// Copyright (C) Microsoft Corporation. All Rights Reserved. +// +//----------------------------------------------------------------------------- +using System; +using System.Linq; +using System.Collections; +using System.Collections.Generic; +using System.Threading; +using System.IO; +using System.Text.RegularExpressions; +using System.Diagnostics; +using System.Diagnostics.Contracts; +using Microsoft.Boogie.AbstractInterpretation; +using Microsoft.Boogie.VCExprAST; +using Microsoft.Basetypes; +using System.Threading.Tasks; + +namespace Microsoft.Boogie { + + enum CheckerStatus + { + Idle, + Ready, + Busy, + Closed + } + + /// + /// Interface to the theorem prover specialized to Boogie. + /// + /// This class creates the appropriate background axioms. There + /// should be one instance per BoogiePL program. + /// + public sealed class Checker { + [ContractInvariantMethod] + void ObjectInvariant() { + Contract.Invariant(gen != null); + Contract.Invariant(thmProver != null); + } + + private readonly VCExpressionGenerator gen; + + private ProverInterface thmProver; + private int timeout; + + // state for the async interface + private volatile ProverInterface.Outcome outcome; + private volatile bool hasOutput; + private volatile UnexpectedProverOutputException outputExn; + private DateTime proverStart; + private TimeSpan proverRunTime; + private volatile ProverInterface.ErrorHandler handler; + private volatile CheckerStatus status; + public volatile Program Program; + + public void GetReady() + { + Contract.Requires(IsIdle); + + status = CheckerStatus.Ready; + } + + public void GoBackToIdle() + { + Contract.Requires(IsBusy); + + status = CheckerStatus.Idle; + } + + public Task ProverTask { get; set; } + + public bool WillingToHandle(int timeout, Program prog) { + return status == CheckerStatus.Idle && timeout == this.timeout && (prog == null || Program == prog); + } + + public VCExpressionGenerator VCExprGen { + get { + Contract.Ensures(Contract.Result() != null); + return this.gen; + } + } + public ProverInterface TheoremProver { + get { + Contract.Ensures(Contract.Result() != null); + return this.thmProver; + } + } + + ///////////////////////////////////////////////////////////////////////////////// + // We share context information for the same program between different Checkers + + private struct ContextCacheKey { + [ContractInvariantMethod] + void ObjectInvariant() { + Contract.Invariant(program != null); + } + + public readonly Program program; + + public ContextCacheKey(Program prog) { + Contract.Requires(prog != null); + this.program = prog; + } + + [Pure] + [Reads(ReadsAttribute.Reads.Nothing)] + public override bool Equals(object that) { + if (that is ContextCacheKey) { + ContextCacheKey thatKey = (ContextCacheKey)that; + return this.program.Equals(thatKey.program); + } + return false; + } + + [Pure] + public override int GetHashCode() { + return this.program.GetHashCode(); + } + } + + ///////////////////////////////////////////////////////////////////////////////// + + /// + /// Constructor. Initialize a checker with the program and log file. + /// Optionally, use prover context provided by parameter "ctx". + /// + public Checker(VC.ConditionGeneration vcgen, Program prog, string/*?*/ logFilePath, bool appendLogFile, int timeout, ProverContext ctx = null) { + Contract.Requires(vcgen != null); + Contract.Requires(prog != null); + this.timeout = timeout; + this.Program = prog; + + ProverOptions options = cce.NonNull(CommandLineOptions.Clo.TheProverFactory).BlankProverOptions(); + + if (logFilePath != null) { + options.LogFilename = logFilePath; + if (appendLogFile) + options.AppendLogFile = appendLogFile; + } + + if (timeout > 0) { + options.TimeLimit = timeout * 1000; + } + + options.Parse(CommandLineOptions.Clo.ProverOptions); + + ContextCacheKey key = new ContextCacheKey(prog); + ProverInterface prover; + + if (vcgen.CheckerCommonState == null) { + vcgen.CheckerCommonState = new Dictionary(); + } + IDictionary/*!>!*/ cachedContexts = (IDictionary)vcgen.CheckerCommonState; + + if (ctx == null && cachedContexts.TryGetValue(key, out ctx)) + { + ctx = (ProverContext)cce.NonNull(ctx).Clone(); + prover = (ProverInterface) + CommandLineOptions.Clo.TheProverFactory.SpawnProver(options, ctx); + } else { + if (ctx == null) ctx = (ProverContext)CommandLineOptions.Clo.TheProverFactory.NewProverContext(options); + + Setup(prog, ctx); + + // we first generate the prover and then store a clone of the + // context in the cache, so that the prover can setup stuff in + // the context to be cached + prover = (ProverInterface) + CommandLineOptions.Clo.TheProverFactory.SpawnProver(options, ctx); + cachedContexts.Add(key, cce.NonNull((ProverContext)ctx.Clone())); + } + + this.thmProver = prover; + this.gen = prover.VCExprGen; + } + + public void Retarget(Program prog, ProverContext ctx, int timeout = 0) + { + lock (this) + { + hasOutput = default(bool); + outcome = default(ProverInterface.Outcome); + outputExn = default(UnexpectedProverOutputException); + handler = default(ProverInterface.ErrorHandler); + TheoremProver.FullReset(gen); + ctx.Reset(); + Setup(prog, ctx); + this.timeout = timeout; + SetTimeout(); + } + } + + public void RetargetWithoutReset(Program prog, ProverContext ctx) + { + ctx.Clear(); + Setup(prog, ctx); + } + + + public void SetTimeout() + { + if (0 < timeout) + { + TheoremProver.SetTimeOut(timeout * 1000); + } + else + { + TheoremProver.SetTimeOut(0); + } + } + + /// + /// Set up the context. + /// + private void Setup(Program prog, ProverContext ctx) + { + Program = prog; + // TODO(wuestholz): Is this lock necessary? + lock (Program.TopLevelDeclarations) + { + foreach (Declaration decl in Program.TopLevelDeclarations) + { + Contract.Assert(decl != null); + var typeDecl = decl as TypeCtorDecl; + var constDecl = decl as Constant; + var funDecl = decl as Function; + var axiomDecl = decl as Axiom; + var glVarDecl = decl as GlobalVariable; + if (typeDecl != null) + { + ctx.DeclareType(typeDecl, null); + } + else if (constDecl != null) + { + ctx.DeclareConstant(constDecl, constDecl.Unique, null); + } + else if (funDecl != null) + { + ctx.DeclareFunction(funDecl, null); + } + else if (axiomDecl != null) + { + ctx.AddAxiom(axiomDecl, null); + } + else if (glVarDecl != null) + { + ctx.DeclareGlobalVariable(glVarDecl, null); + } + } + } + } + + /// + /// Clean-up. + /// + public void Close() { + thmProver.Close(); + status = CheckerStatus.Closed; + } + + /// + /// Push a Verification Condition as an Axiom + /// (Required for Doomed Program Point detection) + /// + public void PushVCExpr(VCExpr vc) { + Contract.Requires(vc != null); + //thmProver.Context.AddAxiom(vc); + thmProver.PushVCExpression(vc); + } + + public bool IsBusy { + get { + return status == CheckerStatus.Busy; + } + } + + public bool IsReady + { + get + { + return status == CheckerStatus.Ready; + } + } + + public bool IsClosed { + get { + return status == CheckerStatus.Closed; + } + } + + public bool IsIdle + { + get + { + return status == CheckerStatus.Idle; + } + } + + public bool HasOutput { + get { + return hasOutput; + } + } + + public TimeSpan ProverRunTime { + get { + return proverRunTime; + } + } + + private void WaitForOutput(object dummy) { + lock (this) + { + try + { + outcome = thmProver.CheckOutcome(cce.NonNull(handler)); + } + catch (UnexpectedProverOutputException e) + { + outputExn = e; + } + + switch (outcome) + { + case ProverInterface.Outcome.Valid: + thmProver.LogComment("Valid"); + break; + case ProverInterface.Outcome.Invalid: + thmProver.LogComment("Invalid"); + break; + case ProverInterface.Outcome.TimeOut: + thmProver.LogComment("Timed out"); + break; + case ProverInterface.Outcome.OutOfMemory: + thmProver.LogComment("Out of memory"); + break; + case ProverInterface.Outcome.Undetermined: + thmProver.LogComment("Undetermined"); + break; + } + + hasOutput = true; + proverRunTime = DateTime.UtcNow - proverStart; + } + } + + public void BeginCheck(string descriptiveName, VCExpr vc, ProverInterface.ErrorHandler handler) { + Contract.Requires(descriptiveName != null); + Contract.Requires(vc != null); + Contract.Requires(handler != null); + Contract.Requires(IsReady); + + status = CheckerStatus.Busy; + hasOutput = false; + outputExn = null; + this.handler = handler; + + thmProver.Reset(gen); + SetTimeout(); + proverStart = DateTime.UtcNow; + thmProver.BeginCheck(descriptiveName, vc, handler); + // gen.ClearSharedFormulas(); PR: don't know yet what to do with this guy + + ProverTask = Task.Factory.StartNew(() => { WaitForOutput(null); }, TaskCreationOptions.LongRunning); + } + + public ProverInterface.Outcome ReadOutcome() { + Contract.Requires(IsBusy); + Contract.Requires(HasOutput); + Contract.EnsuresOnThrow(true); + + hasOutput = false; + + if (outputExn != null) { + throw outputExn; + } + + return outcome; + } + } + + // ----------------------------------------------------------------------------------------------- + // ----------------------------------------------------------------------------------------------- + // ----------------------------------------------------------------------------------------------- + + public abstract class ProverInterface { + public static ProverInterface CreateProver(Program prog, string/*?*/ logFilePath, bool appendLogFile, int timeout, int taskID = -1) { + Contract.Requires(prog != null); + + ProverOptions options = cce.NonNull(CommandLineOptions.Clo.TheProverFactory).BlankProverOptions(); + + if (logFilePath != null) { + options.LogFilename = logFilePath; + if (appendLogFile) + options.AppendLogFile = appendLogFile; + } + + if (timeout > 0) { + options.TimeLimit = timeout * 1000; + } + + if (taskID >= 0) { + options.Parse(CommandLineOptions.Clo.Cho[taskID].ProverOptions); + } else { + options.Parse(CommandLineOptions.Clo.ProverOptions); + } + + ProverContext ctx = (ProverContext)CommandLineOptions.Clo.TheProverFactory.NewProverContext(options); + + // set up the context + foreach (Declaration decl in prog.TopLevelDeclarations) { + Contract.Assert(decl != null); + TypeCtorDecl t = decl as TypeCtorDecl; + if (t != null) { + ctx.DeclareType(t, null); + } + } + foreach (Declaration decl in prog.TopLevelDeclarations) { + Contract.Assert(decl != null); + Constant c = decl as Constant; + if (c != null) { + ctx.DeclareConstant(c, c.Unique, null); + } + else { + Function f = decl as Function; + if (f != null) { + ctx.DeclareFunction(f, null); + } + } + } + foreach (var ax in prog.Axioms) { + ctx.AddAxiom(ax, null); + } + foreach (Declaration decl in prog.TopLevelDeclarations) { + Contract.Assert(decl != null); + GlobalVariable v = decl as GlobalVariable; + if (v != null) { + ctx.DeclareGlobalVariable(v, null); + } + } + + return (ProverInterface)CommandLineOptions.Clo.TheProverFactory.SpawnProver(options, ctx); + } + + public enum Outcome { + Valid, + Invalid, + TimeOut, + OutOfMemory, + Undetermined, + Bounded + } + public class ErrorHandler { + // Used in CheckOutcomeCore + public virtual int StartingProcId() + { + return 0; + } + + public virtual void OnModel(IList labels, Model model, Outcome proverOutcome) { + Contract.Requires(cce.NonNullElements(labels)); + } + + public virtual void OnResourceExceeded(string message, IEnumerable> assertCmds = null) { + Contract.Requires(message != null); + } + + public virtual void OnProverWarning(string message) + { + Contract.Requires(message != null); + switch (CommandLineOptions.Clo.PrintProverWarnings) { + case CommandLineOptions.ProverWarnings.None: + break; + case CommandLineOptions.ProverWarnings.Stdout: + Console.WriteLine("Prover warning: " + message); + break; + case CommandLineOptions.ProverWarnings.Stderr: + Console.Error.WriteLine("Prover warning: " + message); + break; + default: + Contract.Assume(false); + throw new cce.UnreachableException(); // unexpected case + } + } + + public virtual Absy Label2Absy(string label) { + Contract.Requires(label != null); + Contract.Ensures(Contract.Result() != null); + + throw new System.NotImplementedException(); + } + } + public abstract void BeginCheck(string descriptiveName, VCExpr vc, ErrorHandler handler); + + public virtual Outcome CheckRPFP(string descriptiveName, RPFP vc, ErrorHandler handler, + out RPFP.Node cex, + Dictionary> varSubst, Dictionary extra_bound = null) + { + throw new System.NotImplementedException(); + } + [NoDefaultContract] + public abstract Outcome CheckOutcome(ErrorHandler handler, int taskID = -1); + public virtual string[] CalculatePath(int controlFlowConstant) { + throw new System.NotImplementedException(); + } + public virtual void LogComment(string comment) { + Contract.Requires(comment != null); + } + public virtual void Close() { + } + + public abstract void Reset(VCExpressionGenerator gen); + + public abstract void FullReset(VCExpressionGenerator gen); + + /// + /// MSchaef: Allows to Push a VCExpression as Axiom on the prover stack (beta) + /// for now it is only implemented by ProcessTheoremProver and still requires some + /// testing + /// + public virtual void PushVCExpression(VCExpr vc) { + Contract.Requires(vc != null); + throw new NotImplementedException(); + } + public virtual string VCExpressionToString(VCExpr vc) { + Contract.Requires(vc != null); + Contract.Ensures(Contract.Result() != null); + throw new NotImplementedException(); + } + public virtual void Pop() { + Contract.EnsuresOnThrow(true); + throw new NotImplementedException(); + } + public virtual int NumAxiomsPushed() { + throw new NotImplementedException(); + } + public virtual int FlushAxiomsToTheoremProver() { + Contract.EnsuresOnThrow(true); + throw new NotImplementedException(); + } + + // (assert vc) + public virtual void Assert(VCExpr vc, bool polarity) + { + throw new NotImplementedException(); + } + + // (assert implicit-axioms) + public virtual void AssertAxioms() + { + throw new NotImplementedException(); + } + + // (check-sat) + public virtual void Check() + { + throw new NotImplementedException(); + } + + // (check-sat + get-unsat-core + checkOutcome) + public virtual Outcome CheckAssumptions(List assumptions, out List unsatCore, ErrorHandler handler) + { + throw new NotImplementedException(); + } + + public virtual Outcome CheckAssumptions(List hardAssumptions, List softAssumptions, out List unsatisfiedSoftAssumptions, ErrorHandler handler) { + throw new NotImplementedException(); + } + + public virtual Outcome CheckOutcomeCore(ErrorHandler handler, int taskID = -1) + { + throw new NotImplementedException(); + } + + // (push 1) + public virtual void Push() + { + throw new NotImplementedException(); + } + + // Set theorem prover timeout for the next "check-sat" + public virtual void SetTimeOut(int ms) + { } + + public abstract ProverContext Context { + get; + } + + public abstract VCExpressionGenerator VCExprGen { + get; + } + + public virtual void DefineMacro(Macro fun, VCExpr vc) { + throw new NotImplementedException(); + } + + public class VCExprEvaluationException : Exception + { + + } + + public virtual object Evaluate(VCExpr expr) + { + throw new NotImplementedException(); + } + + ////////////////////// + // For interpolation queries + ////////////////////// + + // Assert vc tagged with a name + public virtual void AssertNamed(VCExpr vc, bool polarity, string name) + { + throw new NotImplementedException(); + } + + // Returns Interpolant(A,B) + public virtual VCExpr ComputeInterpolant(VCExpr A, VCExpr B) + { + throw new NotImplementedException(); + } + + // Returns for each l, Interpolant(root + (leaves - l), l) + // Preconditions: + // leaves cannot have subformulas with same variable names + // Both root and leaves should have been previously named via AssertNamed + public virtual List GetTreeInterpolant(List root, List leaves) + { + throw new NotImplementedException(); + } + + } + + public class ProverInterfaceContracts : ProverInterface { + public override ProverContext Context { + get { + Contract.Ensures(Contract.Result() != null); + + throw new NotImplementedException(); + } + } + public override VCExpressionGenerator VCExprGen { + get { + Contract.Ensures(Contract.Result() != null); + + throw new NotImplementedException(); + } + } + public override void BeginCheck(string descriptiveName, VCExpr vc, ErrorHandler handler) {/*Contract.Requires(descriptiveName != null);*/ + //Contract.Requires(vc != null); + //Contract.Requires(handler != null); + throw new NotImplementedException(); + } + [NoDefaultContract] + public override Outcome CheckOutcome(ErrorHandler handler, int taskID = -1) { + //Contract.Requires(handler != null); + Contract.EnsuresOnThrow(true); + throw new NotImplementedException(); + } + + public override void Reset(VCExpressionGenerator gen) + { + throw new NotImplementedException(); + } + + public override void FullReset(VCExpressionGenerator gen) + { + throw new NotImplementedException(); + } + } + + public class ProverException : Exception { + public ProverException(string s) + : base(s) { + } + } + public class UnexpectedProverOutputException : ProverException { + public UnexpectedProverOutputException(string s) + : base(s) { + } + } + public class ProverDiedException : UnexpectedProverOutputException { + public ProverDiedException() + : base("Prover died with no further output, perhaps it ran out of memory or was killed.") { + } + } +} diff --git a/Source/VCGeneration/ConditionGeneration.cs b/Source/VCGeneration/ConditionGeneration.cs index 8d6606b6..1f010757 100644 --- a/Source/VCGeneration/ConditionGeneration.cs +++ b/Source/VCGeneration/ConditionGeneration.cs @@ -1,2009 +1,2009 @@ -//----------------------------------------------------------------------------- -// -// Copyright (C) Microsoft Corporation. All Rights Reserved. -// -//----------------------------------------------------------------------------- -using System; -using System.Linq; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.Threading; -using System.IO; -using Microsoft.Boogie; -using Microsoft.Boogie.GraphUtil; -using System.Diagnostics.Contracts; -using Microsoft.Basetypes; -using Microsoft.Boogie.VCExprAST; -using Set = Microsoft.Boogie.GSet; - -namespace Microsoft.Boogie { - - public class CalleeCounterexampleInfo { - public Counterexample counterexample; - public List/*!>!*/ args; - - [ContractInvariantMethod] - void ObjectInvariant() { - Contract.Invariant(cce.NonNullElements(args)); - } - - public CalleeCounterexampleInfo(Counterexample cex, List!*/> x) - { - Contract.Requires(cce.NonNullElements(x)); - counterexample = cex; - args = x; - } - } - - public class TraceLocation : IEquatable - { - public int numBlock; - public int numInstr; - - public TraceLocation(int numBlock, int numInstr) - { - this.numBlock = numBlock; - this.numInstr = numInstr; - } - - public override bool Equals(object obj) - { - TraceLocation that = obj as TraceLocation; - if (that == null) return false; - return (numBlock == that.numBlock && numInstr == that.numInstr); - } - - public bool Equals(TraceLocation that) - { - return (numBlock == that.numBlock && numInstr == that.numInstr); - } - - public override int GetHashCode() - { - return numBlock.GetHashCode() ^ 131 * numInstr.GetHashCode(); - } - } - - public abstract class Counterexample { - [ContractInvariantMethod] - void ObjectInvariant() { - Contract.Invariant(Trace != null); - Contract.Invariant(Context != null); - Contract.Invariant(cce.NonNullElements(relatedInformation)); - Contract.Invariant(cce.NonNullDictionaryAndValues(calleeCounterexamples)); - } - - [Peer] - public List Trace; - public Model Model; - public VC.ModelViewInfo MvInfo; - public ProverContext Context; - [Peer] - public List/*!>!*/ relatedInformation; - public string OriginalRequestId; - public string RequestId; - public abstract byte[] Checksum { get; } - public byte[] SugaredCmdChecksum; - - public Dictionary calleeCounterexamples; - - internal Counterexample(List trace, Model model, VC.ModelViewInfo mvInfo, ProverContext context) { - Contract.Requires(trace != null); - Contract.Requires(context != null); - this.Trace = trace; - this.Model = model; - this.MvInfo = mvInfo; - this.Context = context; - this.relatedInformation = new List(); - this.calleeCounterexamples = new Dictionary(); - } - - // Create a shallow copy of the counterexample - public abstract Counterexample Clone(); - - public void AddCalleeCounterexample(TraceLocation loc, CalleeCounterexampleInfo cex) - { - Contract.Requires(cex != null); - calleeCounterexamples[loc] = cex; - } - - public void AddCalleeCounterexample(int numBlock, int numInstr, CalleeCounterexampleInfo cex) - { - Contract.Requires(cex != null); - calleeCounterexamples[new TraceLocation(numBlock, numInstr)] = cex; - } - - public void AddCalleeCounterexample(Dictionary cs) - { - Contract.Requires(cce.NonNullDictionaryAndValues(cs)); - foreach (TraceLocation loc in cs.Keys) - { - AddCalleeCounterexample(loc, cs[loc]); - } - } - - // Looks up the Cmd at a given index into the trace - public Cmd getTraceCmd(TraceLocation loc) - { - Debug.Assert(loc.numBlock < Trace.Count); - Block b = Trace[loc.numBlock]; - Debug.Assert(loc.numInstr < b.Cmds.Count); - return b.Cmds[loc.numInstr]; - } - - // Looks up the name of the called procedure. - // Asserts that the name exists - public string getCalledProcName(Cmd cmd) - { - // There are two options: - // 1. cmd is a CallCmd - // 2. cmd is an AssumeCmd (passified version of a CallCmd) - if(cmd is CallCmd) { - return (cmd as CallCmd).Proc.Name; - } - AssumeCmd assumeCmd = cmd as AssumeCmd; - Debug.Assert(assumeCmd != null); - - NAryExpr naryExpr = assumeCmd.Expr as NAryExpr; - Debug.Assert(naryExpr != null); - - return naryExpr.Fun.FunctionName; - } - - public void Print(int indent, TextWriter tw, Action blockAction = null) { - int numBlock = -1; - string ind = new string(' ', indent); - foreach (Block b in Trace) { - Contract.Assert(b != null); - numBlock++; - if (b.tok == null) { - tw.WriteLine("{0}", ind); - } else { - // 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 (blockAction != null) - { - blockAction(b); - } - - tw.WriteLine("{4}{0}({1},{2}): {3}", b.tok.filename, b.tok.line, b.tok.col, b.Label, ind); - - for (int numInstr = 0; numInstr < b.Cmds.Count; numInstr++) - { - var loc = new TraceLocation(numBlock, numInstr); - if (calleeCounterexamples.ContainsKey(loc)) - { - var cmd = getTraceCmd(loc); - var calleeName = getCalledProcName(cmd); - if (calleeName.StartsWith(VC.StratifiedVCGen.recordProcName) && CommandLineOptions.Clo.StratifiedInlining > 0) - { - Contract.Assert(calleeCounterexamples[loc].args.Count == 1); - var arg = calleeCounterexamples[loc].args[0]; - tw.WriteLine("{0}value = {1}", ind, arg.ToString()); - } - else - { - tw.WriteLine("{1}Inlined call to procedure {0} begins", calleeName, ind); - calleeCounterexamples[loc].counterexample.Print(indent + 4, tw); - tw.WriteLine("{1}Inlined call to procedure {0} ends", calleeName, ind); - } - } - } - } - } - } - } - - public static bool firstModelFile = true; - - public bool ModelHasStatesAlready = false; - - public void PrintModel(TextWriter tw) - { - var filename = CommandLineOptions.Clo.ModelViewFile; - if (Model == null || filename == null || CommandLineOptions.Clo.StratifiedInlining > 0) return; - - if (!ModelHasStatesAlready) { - PopulateModelWithStates(); - ModelHasStatesAlready = true; - } - - if (filename == "-") { - Model.Write(tw); - tw.Flush(); - } else { - using (var wr = new StreamWriter(filename, !firstModelFile)) { - firstModelFile = false; - Model.Write(wr); - } - } - } - - void ApplyRedirections(Model m) { - var mapping = new Dictionary(); - foreach (var name in new string[] { "U_2_bool", "U_2_int" }) { - Model.Func f = m.TryGetFunc(name); - if (f != null && f.Arity == 1) { - foreach (var ft in f.Apps) mapping[ft.Args[0]] = ft.Result; - } - } - m.Substitute(mapping); - } - - public void PopulateModelWithStates() - { - Contract.Requires(Model != null); - - Model m = Model; - ApplyRedirections(m); - - var mvstates = m.TryGetFunc("$mv_state"); - if (MvInfo == null || mvstates == null || (mvstates.Arity == 1 && mvstates.Apps.Count() == 0)) - return; - - Contract.Assert(mvstates.Arity == 2); - - foreach (Variable v in MvInfo.AllVariables) { - m.InitialState.AddBinding(v.Name, GetModelValue(m, v)); - } - - var states = new List(); - foreach (var t in mvstates.Apps) - states.Add(t.Args[1].AsInt()); - - states.Sort(); - - for (int i = 0; i < states.Count; ++i) { - var s = states[i]; - if (0 <= s && s < MvInfo.CapturePoints.Count) { - VC.ModelViewInfo.Mapping map = MvInfo.CapturePoints[s]; - var prevInc = i > 0 ? MvInfo.CapturePoints[states[i - 1]].IncarnationMap : new Dictionary(); - var cs = m.MkState(map.Description); - - foreach (Variable v in MvInfo.AllVariables) { - Expr e = map.IncarnationMap.ContainsKey(v) ? map.IncarnationMap[v] : null; - if (e == null) continue; - - Expr prevIncV = prevInc.ContainsKey(v) ? prevInc[v] : null; - if (prevIncV == e) continue; // skip unchanged variables - - Model.Element elt; - - if (e is IdentifierExpr) { - IdentifierExpr ide = (IdentifierExpr)e; - elt = GetModelValue(m, ide.Decl); - } else if (e is LiteralExpr) { - LiteralExpr lit = (LiteralExpr)e; - elt = m.MkElement(lit.Val.ToString()); - } else { - elt = m.MkFunc(e.ToString(), 0).GetConstant(); - } - - cs.AddBinding(v.Name, elt); - } - - } else { - Contract.Assume(false); - } - } - } - - private Model.Element GetModelValue(Model m, Variable v) { - Model.Element elt; - // first, get the unique name - string uniqueName; - VCExprVar vvar = Context.BoogieExprTranslator.TryLookupVariable(v); - if (vvar == null) { - uniqueName = v.Name; - } else { - uniqueName = Context.Lookup(vvar); - } - - var f = m.TryGetFunc(uniqueName); - if (f == null) { - f = m.MkFunc(uniqueName, 0); - } - elt = f.GetConstant(); - return elt; - } - - public abstract int GetLocation(); - } - - public class CounterexampleComparer : IComparer { - - private int Compare(List bs1, List bs2) - { - if (bs1.Count < bs2.Count) - { - return -1; - } - else if (bs2.Count < bs1.Count) - { - return 1; - } - - for (int i = 0; i < bs1.Count; i++) - { - var b1 = bs1[i]; - var b2 = bs2[i]; - if (b1.tok.pos < b2.tok.pos) - { - return -1; - } - else if (b2.tok.pos < b1.tok.pos) - { - return 1; - } - } - - return 0; - } - - public int Compare(Counterexample c1, Counterexample c2) - { - //Contract.Requires(c1 != null); - //Contract.Requires(c2 != null); - if (c1.GetLocation() == c2.GetLocation()) - { - var c = Compare(c1.Trace, c2.Trace); - if (c != 0) - { - return c; - } - // TODO(wuestholz): Generalize this to compare all IPotentialErrorNodes of the counterexample. - var a1 = c1 as AssertCounterexample; - var a2 = c2 as AssertCounterexample; - if (a1 != null && a2 != null) - { - var s1 = a1.FailingAssert.ErrorData as string; - var s2 = a2.FailingAssert.ErrorData as string; - if (s1 != null && s2 != null) - { - return s1.CompareTo(s2); - } - } - - return 0; - } - if (c1.GetLocation() > c2.GetLocation()) - { - return 1; - } - return -1; - } - } - - public class AssertCounterexample : Counterexample { - [Peer] - public AssertCmd FailingAssert; - [ContractInvariantMethod] - void ObjectInvariant() { - Contract.Invariant(FailingAssert != null); - } - - - public AssertCounterexample(List trace, AssertCmd failingAssert, Model model, VC.ModelViewInfo mvInfo, ProverContext context) - : base(trace, model, mvInfo, context) { - Contract.Requires(trace != null); - Contract.Requires(failingAssert != null); - Contract.Requires(context != null); - this.FailingAssert = failingAssert; - } - - public override int GetLocation() { - return FailingAssert.tok.line * 1000 + FailingAssert.tok.col; - } - - public override byte[] Checksum - { - get { return FailingAssert.Checksum; } - } - - public override Counterexample Clone() - { - var ret = new AssertCounterexample(Trace, FailingAssert, Model, MvInfo, Context); - ret.calleeCounterexamples = calleeCounterexamples; - return ret; - } - } - - public class CallCounterexample : Counterexample { - public CallCmd FailingCall; - public Requires FailingRequires; - [ContractInvariantMethod] - void ObjectInvariant() { - Contract.Invariant(FailingCall != null); - Contract.Invariant(FailingRequires != null); - } - - - public CallCounterexample(List trace, CallCmd failingCall, Requires failingRequires, Model model, VC.ModelViewInfo mvInfo, ProverContext context, byte[] checksum = null) - : base(trace, model, mvInfo, context) { - Contract.Requires(!failingRequires.Free); - Contract.Requires(trace != null); - Contract.Requires(context != null); - Contract.Requires(failingCall != null); - Contract.Requires(failingRequires != null); - this.FailingCall = failingCall; - this.FailingRequires = failingRequires; - this.checksum = checksum; - this.SugaredCmdChecksum = failingCall.Checksum; - } - - public override int GetLocation() { - return FailingCall.tok.line * 1000 + FailingCall.tok.col; - } - - byte[] checksum; - public override byte[] Checksum - { - get { return checksum; } - } - - public override Counterexample Clone() - { - var ret = new CallCounterexample(Trace, FailingCall, FailingRequires, Model, MvInfo, Context, Checksum); - ret.calleeCounterexamples = calleeCounterexamples; - return ret; - } - } - - public class ReturnCounterexample : Counterexample { - public TransferCmd FailingReturn; - public Ensures FailingEnsures; - [ContractInvariantMethod] - void ObjectInvariant() { - Contract.Invariant(FailingEnsures != null); - Contract.Invariant(FailingReturn != null); - } - - - public ReturnCounterexample(List trace, TransferCmd failingReturn, Ensures failingEnsures, Model model, VC.ModelViewInfo mvInfo, ProverContext context, byte[] checksum) - : base(trace, model, mvInfo, context) { - Contract.Requires(trace != null); - Contract.Requires(context != null); - Contract.Requires(failingReturn != null); - Contract.Requires(failingEnsures != null); - Contract.Requires(!failingEnsures.Free); - this.FailingReturn = failingReturn; - this.FailingEnsures = failingEnsures; - this.checksum = checksum; - } - - public override int GetLocation() { - return FailingReturn.tok.line * 1000 + FailingReturn.tok.col; - } - - byte[] checksum; - - /// - /// Returns the checksum of the corresponding assertion. - /// - public override byte[] Checksum - { - get - { - return checksum; - } - } - - public override Counterexample Clone() - { - var ret = new ReturnCounterexample(Trace, FailingReturn, FailingEnsures, Model, MvInfo, Context, checksum); - ret.calleeCounterexamples = calleeCounterexamples; - return ret; - } - } - - public class VerifierCallback { - // 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) { - Contract.Requires(ce != null); - } - - // called in case resource is exceeded and we don't have counterexample - public virtual void OnTimeout(string reason) { - Contract.Requires(reason != null); - } - - public virtual void OnOutOfMemory(string reason) { - Contract.Requires(reason != null); - } - - public virtual void OnProgress(string phase, int step, int totalSteps, double progressEstimate) { - } - - public virtual void OnUnreachableCode(Implementation impl) { - Contract.Requires(impl != null); - } - - public virtual void OnWarning(string msg) { - Contract.Requires(msg != null); - switch (CommandLineOptions.Clo.PrintProverWarnings) { - case CommandLineOptions.ProverWarnings.None: - break; - case CommandLineOptions.ProverWarnings.Stdout: - Console.WriteLine("Prover warning: " + msg); - break; - case CommandLineOptions.ProverWarnings.Stderr: - Console.Error.WriteLine("Prover warning: " + msg); - break; - default: - Contract.Assume(false); - throw new cce.UnreachableException(); // unexpected case - } - } - } -} - -//////////////////////////////////////////// - -namespace VC { - using Bpl = Microsoft.Boogie; - - public class VCGenException : Exception { - public VCGenException(string s) - : base(s) { - } - } - [ContractClassFor(typeof(ConditionGeneration))] - public abstract class ConditionGenerationContracts : ConditionGeneration { - public override Outcome VerifyImplementation(Implementation impl, VerifierCallback callback) { - Contract.Requires(impl != null); - Contract.Requires(callback != null); - Contract.EnsuresOnThrow(true); - throw new NotImplementedException(); - } - public ConditionGenerationContracts(Program p, List checkers) - : base(p, checkers) { - } - } - - [ContractClass(typeof(ConditionGenerationContracts))] - public abstract class ConditionGeneration : IDisposable { - protected internal object CheckerCommonState; - - public enum Outcome { - Correct, - Errors, - TimedOut, - OutOfMemory, - Inconclusive, - ReachedBound - } - - public static Outcome ProverInterfaceOutcomeToConditionGenerationOutcome(ProverInterface.Outcome outcome) { - switch (outcome) { - case ProverInterface.Outcome.Invalid: - return Outcome.Errors; - case ProverInterface.Outcome.OutOfMemory: - return Outcome.OutOfMemory; - case ProverInterface.Outcome.TimeOut: - return Outcome.TimedOut; - case ProverInterface.Outcome.Undetermined: - return Outcome.Inconclusive; - case ProverInterface.Outcome.Valid: - return Outcome.Correct; - } - return Outcome.Inconclusive; // unreachable but the stupid compiler does not understand - } - - [ContractInvariantMethod] - void ObjectInvariant() { - Contract.Invariant(cce.NonNullElements(checkers)); - Contract.Invariant(cce.NonNullDictionaryAndValues(incarnationOriginMap)); - Contract.Invariant(program != null); - } - - public int CumulativeAssertionCount; // for statistics - - protected readonly List/*!>!*/ checkers; - - private bool _disposed; - - protected Implementation currentImplementation; - - protected List CurrentLocalVariables = null; - - // shared across each implementation; created anew for each implementation - protected Dictionary variable2SequenceNumber; - public Dictionary/*!>!*/ incarnationOriginMap = new Dictionary(); - - public Program program; - protected string/*?*/ logFilePath; - protected bool appendLogFile; - - public static List errorModelList; - - public ConditionGeneration(Program p, List checkers) { - Contract.Requires(p != null && checkers != null && cce.NonNullElements(checkers)); - program = p; - this.checkers = checkers; - Cores = 1; - } - - /// - /// Takes an implementation and constructs a verification condition and sends - /// it to the theorem prover. - /// Returns null if "impl" is correct. Otherwise, returns a list of counterexamples, - /// each counterexample consisting of an array of labels. - /// - /// - public Outcome VerifyImplementation(Implementation impl, out List/*?*/ errors, string requestId = null) { - Contract.Requires(impl != 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("Starting implementation verification"); - - CounterexampleCollector collector = new CounterexampleCollector(); - collector.RequestId = requestId; - Outcome outcome = VerifyImplementation(impl, collector); - if (outcome == Outcome.Errors || outcome == Outcome.TimedOut || outcome == Outcome.OutOfMemory) { - errors = collector.examples; - } else { - errors = null; - } - - Helpers.ExtraTraceInformation("Finished implementation verification"); - return outcome; - } - - /// - /// Takes an implementation and constructs a verification condition and sends - /// it to the theorem prover. - /// Returns null if "impl" is correct. Otherwise, returns a list of counterexamples, - /// each counterexample consisting of an array of labels. - /// - /// - public Outcome VerifyImplementation(Implementation impl, out List errors, out List errorsModel) - { - Contract.Ensures(Contract.Result() != Outcome.Errors || Contract.ValueAtReturn(out errors) != null); - Contract.EnsuresOnThrow(true); - List errorsOut; - - Outcome outcome; - errorModelList = new List(); - outcome = VerifyImplementation(impl, out errorsOut); - errors = errorsOut; - errorsModel = errorModelList; - - return outcome; - } - - public abstract Outcome VerifyImplementation(Implementation impl, VerifierCallback callback); - - /////////////////////////////////// Common Methods and Classes ////////////////////////////////////////// - - #region Methods for injecting pre- and postconditions - private static void - ThreadInCodeExpr(Implementation impl, - Block targetBlock, - CodeExpr codeExpr, - bool replaceWithAssert, - TokenTextWriter debugWriter) { - Contract.Requires(impl != null); - Contract.Requires(codeExpr != null); - Contract.Requires(targetBlock != null); - // Go through codeExpr and for all blocks that have a "return e" - // as their transfer command: - // Replace all "return e" with "assert/assume e" - // Change the transfer command to "goto targetBlock" - // Then add all of the blocks in codeExpr to the implementation (at the end) - foreach (Block b in codeExpr.Blocks) { - Contract.Assert(b != null); - ReturnExprCmd rec = b.TransferCmd as ReturnExprCmd; - if (rec != null) { // otherwise it is a goto command - if (replaceWithAssert) { - Ensures ens = new Ensures(rec.tok, false, rec.Expr, null); - Contract.Assert(ens != null); - Cmd c = new AssertEnsuresCmd(ens); - Contract.Assert(c != null); - b.Cmds.Add(c); - } else { - b.Cmds.Add(new AssumeCmd(rec.tok, rec.Expr)); - } - b.TransferCmd = new GotoCmd(Token.NoToken, - new List { targetBlock.Label }, - new List { targetBlock }); - targetBlock.Predecessors.Add(b); - } - impl.Blocks.Add(b); - } - if (debugWriter != null) { - codeExpr.Emit(debugWriter, 1, false); - } - return; - } - - private static void AddAsPrefix(Block b, List cs) { - Contract.Requires(b != null); - Contract.Requires(cs != null); - List newCommands = new List(); - newCommands.AddRange(cs); - newCommands.AddRange(b.Cmds); - b.Cmds = newCommands; - } - - - /// - /// Modifies an implementation by prepending it with startCmds and then, as assume - /// statements, all preconditions. Insert new blocks as needed, and adjust impl.Blocks[0] - /// accordingly to make it the new implementation entry block. - /// - /// - /// - protected static void InjectPreconditions(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) { - debugWriter = new TokenTextWriter("", Console.Out, /*setTokens=*/ false, /*pretty=*/ false); - debugWriter.WriteLine("Effective precondition:"); - } - - Substitution formalProcImplSubst = Substituter.SubstitutionFromHashtable(impl.GetImplFormalMap()); - string blockLabel = "PreconditionGeneratedEntry"; - - Block origStartBlock = impl.Blocks[0]; - Block insertionPoint = new Block( - new Token(-17, -4), blockLabel, startCmds, - new GotoCmd(Token.NoToken, new List { origStartBlock.Label }, new List { origStartBlock })); - - impl.Blocks[0] = insertionPoint; // make insertionPoint the start block - impl.Blocks.Add(origStartBlock); // and put the previous start block at the end of the list - - // (free and checked) requires clauses - foreach (Requires req in impl.Proc.Requires) - // invariant: insertionPoint.TransferCmd is "goto origStartBlock;", but origStartBlock.Predecessors has not yet been updated - { - Contract.Assert(req != null); - Expr e = Substituter.Apply(formalProcImplSubst, req.Condition); - Cmd c = new AssumeCmd(req.tok, e); - c.IrrelevantForChecksumComputation = true; - insertionPoint.Cmds.Add(c); - if (debugWriter != null) { - c.Emit(debugWriter, 1); - } - } - origStartBlock.Predecessors.Add(insertionPoint); - - if (impl.ExplicitAssumptionAboutCachedPrecondition != null) - { - insertionPoint.Cmds.Add(impl.ExplicitAssumptionAboutCachedPrecondition); - } - - if (debugWriter != null) { - debugWriter.WriteLine(); - } - } - /// - /// Modifies an implementation by inserting all postconditions - /// as assert statements at the end of the implementation - /// Returns the possibly-new unified exit block of the implementation - /// - /// - /// The unified exit block that has - /// already been constructed for the implementation (and so - /// is already an element of impl.Blocks) - /// - protected static void InjectPostConditions(Implementation impl, Block unifiedExitBlock, Dictionary gotoCmdOrigins) { - Contract.Requires(impl != null); - Contract.Requires(unifiedExitBlock != null); - Contract.Requires(gotoCmdOrigins != null); - Contract.Requires(impl.Proc != null); - Contract.Requires(unifiedExitBlock.TransferCmd is ReturnCmd); - - TokenTextWriter debugWriter = null; - if (CommandLineOptions.Clo.PrintWithUniqueASTIds) { - debugWriter = new TokenTextWriter("", Console.Out, /*setTokens=*/ false, /*pretty=*/ false); - debugWriter.WriteLine("Effective postcondition:"); - } - - Substitution formalProcImplSubst = Substituter.SubstitutionFromHashtable(impl.GetImplFormalMap()); - - // (free and checked) ensures clauses - foreach (Ensures ens in impl.Proc.Ensures) { - Contract.Assert(ens != null); - if (!ens.Free) { // skip free ensures clauses - Expr e = Substituter.Apply(formalProcImplSubst, ens.Condition); - Ensures ensCopy = (Ensures)cce.NonNull(ens.Clone()); - ensCopy.Condition = e; - AssertEnsuresCmd c = new AssertEnsuresCmd(ensCopy); - c.ErrorDataEnhanced = ensCopy.ErrorDataEnhanced; - unifiedExitBlock.Cmds.Add(c); - if (debugWriter != null) { - c.Emit(debugWriter, 1); - } - } - } - - if (debugWriter != null) { - debugWriter.WriteLine(); - } - } - - - /// - /// Get the pre-condition of an implementation, including the where clauses from the in-parameters. - /// - /// - protected static List GetPre(Implementation impl) { - Contract.Requires(impl != null); - Contract.Requires(impl.Proc != null); - Contract.Ensures(Contract.Result>() != null); - - - TokenTextWriter debugWriter = null; - if (CommandLineOptions.Clo.PrintWithUniqueASTIds) { - debugWriter = new TokenTextWriter("", Console.Out, /*setTokens=*/ false, /*pretty=*/ false); - debugWriter.WriteLine("Effective precondition:"); - } - - Substitution formalProcImplSubst = Substituter.SubstitutionFromHashtable(impl.GetImplFormalMap()); - List pre = new List(); - - // (free and checked) requires clauses - foreach (Requires req in impl.Proc.Requires) { - Contract.Assert(req != null); - Expr e = Substituter.Apply(formalProcImplSubst, req.Condition); - Contract.Assert(e != null); - Cmd c = new AssumeCmd(req.tok, e); - Contract.Assert(c != null); - pre.Add(c); - - if (debugWriter != null) { - c.Emit(debugWriter, 1); - } - } - - if (debugWriter != null) { - debugWriter.WriteLine(); - } - - return pre; - } - - /// - /// Get the post-condition of an implementation. - /// - /// - protected static List GetPost(Implementation impl) { - - - Contract.Requires(impl != null); - Contract.Requires(impl.Proc != null); - Contract.Ensures(Contract.Result>() != null); - if (CommandLineOptions.Clo.PrintWithUniqueASTIds) { - Console.WriteLine("Effective postcondition:"); - } - - // Construct an Expr for the post-condition - Substitution formalProcImplSubst = Substituter.SubstitutionFromHashtable(impl.GetImplFormalMap()); - List post = new List(); - foreach (Ensures ens in impl.Proc.Ensures) { - Contract.Assert(ens != null); - if (!ens.Free) { - Expr e = Substituter.Apply(formalProcImplSubst, ens.Condition); - Contract.Assert(e != null); - Ensures ensCopy = cce.NonNull((Ensures)ens.Clone()); - ensCopy.Condition = e; - Cmd c = new AssertEnsuresCmd(ensCopy); - ((AssertEnsuresCmd)c).ErrorDataEnhanced = ensCopy.ErrorDataEnhanced; - post.Add(c); - - if (CommandLineOptions.Clo.PrintWithUniqueASTIds) { - c.Emit(new TokenTextWriter("", Console.Out, /*setTokens=*/ false, /*pretty=*/ false), 1); - } - } - } - - if (CommandLineOptions.Clo.PrintWithUniqueASTIds) { - Console.WriteLine(); - } - - return post; - } - - /// - /// Get the where clauses from the in- and out-parameters as - /// a sequence of assume commands. - /// As a side effect, this method adds these where clauses to the out parameters. - /// - /// - protected static List GetParamWhereClauses(Implementation impl) { - Contract.Requires(impl != null); - Contract.Requires(impl.Proc != null); - Contract.Ensures(Contract.Result>() != null); - TokenTextWriter debugWriter = null; - if (CommandLineOptions.Clo.PrintWithUniqueASTIds) { - debugWriter = new TokenTextWriter("", Console.Out, /*setTokens=*/ false, /*pretty=*/ false); - debugWriter.WriteLine("Effective precondition from where-clauses:"); - } - - Substitution formalProcImplSubst = Substituter.SubstitutionFromHashtable(impl.GetImplFormalMap()); - List whereClauses = new List(); - - // where clauses of in-parameters - foreach (Formal f in impl.Proc.InParams) { - Contract.Assert(f != null); - if (f.TypedIdent.WhereExpr != null) { - Expr e = Substituter.Apply(formalProcImplSubst, f.TypedIdent.WhereExpr); - Cmd c = new AssumeCmd(f.tok, e); - whereClauses.Add(c); - - if (debugWriter != null) { - c.Emit(debugWriter, 1); - } - } - } - - // where clauses of out-parameters - Contract.Assert(impl.OutParams.Count == impl.Proc.OutParams.Count); - for (int i = 0; i < impl.OutParams.Count; i++) { - Variable f = cce.NonNull(impl.Proc.OutParams[i]); - if (f.TypedIdent.WhereExpr != null) { - Expr e = Substituter.Apply(formalProcImplSubst, f.TypedIdent.WhereExpr); - Cmd c = new AssumeCmd(f.tok, e); - whereClauses.Add(c); - - Variable fi = cce.NonNull(impl.OutParams[i]); - Contract.Assume(fi.TypedIdent.WhereExpr == null); - fi.TypedIdent.WhereExpr = e; - - if (debugWriter != null) { - c.Emit(debugWriter, 1); - } - } - } - - if (debugWriter != null) { - debugWriter.WriteLine(); - } - - return whereClauses; - } - - protected static void RestoreParamWhereClauses(Implementation impl) { - Contract.Requires(impl != null); - // We no longer need the where clauses on the out parameters, so we remove them to restore the situation from before VC generation - foreach (Formal f in impl.OutParams) { - Contract.Assert(f != null); - f.TypedIdent.WhereExpr = null; - } - } - #endregion - - - protected Checker FindCheckerFor(int timeout, bool isBlocking = true, int waitTimeinMs = 50, int maxRetries = 3) - { - Contract.Requires(0 <= waitTimeinMs && 0 <= maxRetries); - Contract.Ensures(!isBlocking || Contract.Result() != null); - - lock (checkers) - { - retry: - // Look for existing checker. - for (int i = 0; i < checkers.Count; i++) - { - var c = checkers[i]; - if (Monitor.TryEnter(c)) - { - try - { - if (c.WillingToHandle(timeout, program)) - { - c.GetReady(); - return c; - } - else if (c.IsIdle || c.IsClosed) - { - if (c.IsIdle) - { - c.Retarget(program, c.TheoremProver.Context, timeout); - c.GetReady(); - return c; - } - else - { - checkers.RemoveAt(i); - i--; - continue; - } - } - } - finally - { - Monitor.Exit(c); - } - } - } - - if (Cores <= checkers.Count) - { - if (isBlocking || 0 < maxRetries) - { - if (0 < waitTimeinMs) - { - Monitor.Wait(checkers, waitTimeinMs); - } - maxRetries--; - goto retry; - } - else - { - return null; - } - } - - // Create a new checker. - string log = logFilePath; - if (log != null && !log.Contains("@PROC@") && checkers.Count > 0) - { - log = log + "." + checkers.Count; - } - Checker ch = new Checker(this, program, log, appendLogFile, timeout); - ch.GetReady(); - checkers.Add(ch); - return ch; - } - } - - - virtual public void Close() { - } - - - public class CounterexampleCollector : VerifierCallback { - [ContractInvariantMethod] - void ObjectInvariant() { - Contract.Invariant(cce.NonNullElements(examples)); - } - - public string RequestId; - - public readonly List/*!>!*/ examples = new List(); - public override void OnCounterexample(Counterexample ce, string/*?*/ reason) { - //Contract.Requires(ce != null); - if (RequestId != null) - { - ce.RequestId = RequestId; - } - if (ce.OriginalRequestId == null && 1 < CommandLineOptions.Clo.VerifySnapshots) - { - ce.OriginalRequestId = RequestId; - } - examples.Add(ce); - } - - public override void OnUnreachableCode(Implementation impl) { - //Contract.Requires(impl != null); - System.Console.WriteLine("found unreachable code:"); - EmitImpl(impl, false); - // TODO report error about next to last in seq - } - } - - protected static void EmitImpl(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; - impl.Emit(new TokenTextWriter("", Console.Out, /*setTokens=*/ false, /*pretty=*/ false), 0); - CommandLineOptions.Clo.PrintDesugarings = oldPrintDesugaringSetting; - CommandLineOptions.Clo.PrintUnstructured = oldPrintUnstructured; - } - - - protected Block GenerateUnifiedExit(Implementation impl, Dictionary gotoCmdOrigins) { - Contract.Requires(impl != null); - Contract.Requires(gotoCmdOrigins != null); - Contract.Ensures(Contract.Result() != null); - - Contract.Ensures(Contract.Result().TransferCmd is ReturnCmd); - Block/*?*/ exitBlock = null; - #region Create a unified exit block, if there's more than one - { - int returnBlocks = 0; - foreach (Block b in impl.Blocks) { - if (b.TransferCmd is ReturnCmd) { - exitBlock = b; - returnBlocks++; - } - } - if (returnBlocks > 1) { - string unifiedExitLabel = "GeneratedUnifiedExit"; - Block unifiedExit = new Block(new Token(-17, -4), unifiedExitLabel, new List(), new ReturnCmd(Token.NoToken)); - Contract.Assert(unifiedExit != null); - foreach (Block b in impl.Blocks) { - if (b.TransferCmd is ReturnCmd) { - List labels = new List(); - labels.Add(unifiedExitLabel); - List bs = new List(); - bs.Add(unifiedExit); - GotoCmd go = new GotoCmd(Token.NoToken, labels, bs); - gotoCmdOrigins[go] = (ReturnCmd)b.TransferCmd; - b.TransferCmd = go; - unifiedExit.Predecessors.Add(b); - } - } - - exitBlock = unifiedExit; - impl.Blocks.Add(unifiedExit); - } - Contract.Assert(exitBlock != null); - } - return exitBlock; - #endregion - } - - protected static void ResetPredecessors(List blocks) { - Contract.Requires(blocks != null); - foreach (Block b in blocks) { - Contract.Assert(b != null); - b.Predecessors = new List(); - } - foreach (Block b in blocks) { - Contract.Assert(b != null); - foreach (Block ch in Exits(b)) { - Contract.Assert(ch != null); - ch.Predecessors.Add(b); - } - } - } - - protected static IEnumerable Exits(Block b) { - Contract.Requires(b != null); - GotoCmd g = b.TransferCmd as GotoCmd; - if (g != null) { - return cce.NonNull(g.labelTargets); - } - return new List(); - } - - protected Variable CreateIncarnation(Variable x, Absy a) { - Contract.Requires(this.variable2SequenceNumber != null); - Contract.Requires(this.CurrentLocalVariables != null); - Contract.Requires(a is Block || a is AssignCmd || a is HavocCmd); - - Contract.Requires(x != null); - Contract.Ensures(Contract.Result() != null); - - int currentIncarnationNumber = - variable2SequenceNumber.ContainsKey(x) - ? - variable2SequenceNumber[x] - : - -1; - Variable v = new Incarnation(x, currentIncarnationNumber + 1); - variable2SequenceNumber[x] = currentIncarnationNumber + 1; - CurrentLocalVariables.Add(v); - incarnationOriginMap.Add((Incarnation)v, a); - return v; - } - - /// - /// Compute the incarnation map at the beginning of block "b" from the incarnation blocks of the - /// predecessors of "b". - /// - /// The predecessor map b.map for block "b" is defined as follows: - /// b.map.Domain == Union{Block p in b.predecessors; p.map.Domain} - /// Forall{Variable v in b.map.Domain; - /// b.map[v] == (v in Intersection{Block p in b.predecessors; p.map}.Domain - /// ? b.predecessors[0].map[v] - /// : new Variable())} - /// Every variable that b.map maps to a fresh variable requires a fixup in all predecessor blocks. - /// - /// - /// Gives incarnation maps for b's predecessors. - /// - protected Dictionary ComputeIncarnationMap(Block b, Dictionary> block2Incarnation) { - Contract.Requires(b != null); - Contract.Requires(block2Incarnation != null); - Contract.Ensures(Contract.Result>() != null); - - if (b.Predecessors.Count == 0) { - return new Dictionary(); - } - - Dictionary incarnationMap = null; - Set /*Variable*/ fixUps = new Set /*Variable*/ (); - foreach (Block pred in b.Predecessors) { - Contract.Assert(pred != null); - Contract.Assert(block2Incarnation.ContainsKey(pred)); // otherwise, Passive Transformation found a block whose predecessors have not been processed yet - Dictionary predMap = (Dictionary)block2Incarnation[pred]; - Contract.Assert(predMap != null); - if (incarnationMap == null) { - incarnationMap = new Dictionary(predMap); - continue; - } - - ArrayList /*Variable*/ conflicts = new ArrayList /*Variable*/ (); - foreach (Variable v in incarnationMap.Keys) { - Contract.Assert(v != null); - if (!predMap.ContainsKey(v)) { - // conflict!! - conflicts.Add(v); - fixUps.Add(v); - } - } - // Now that we're done with enumeration, we'll do all the removes - foreach (Variable v in conflicts) { - Contract.Assert(v != null); - incarnationMap.Remove(v); - } - foreach (Variable v in predMap.Keys) { - Contract.Assert(v != null); - if (!incarnationMap.ContainsKey(v)) { - // v was not in the domain of the predecessors seen so far, so it needs to be fixed up - fixUps.Add(v); - } else { - // v in incarnationMap ==> all pred blocks (up to now) all agree on its incarnation - if (predMap[v] != incarnationMap[v]) { - // conflict!! - incarnationMap.Remove(v); - fixUps.Add(v); - } - } - } - } - - #region Second, for all variables in the fixups list, introduce a new incarnation and push it back into the preds. - foreach (Variable v in fixUps) { - Contract.Assert(v != null); - if (!b.IsLive(v)) - continue; - Variable v_prime = CreateIncarnation(v, b); - IdentifierExpr ie = new IdentifierExpr(v_prime.tok, v_prime); - Contract.Assert(incarnationMap != null); - incarnationMap[v] = ie; - foreach (Block pred in b.Predecessors) { - Contract.Assert(pred != null); - #region Create an assume command equating v_prime with its last incarnation in pred - #region Create an identifier expression for the last incarnation in pred - Dictionary predMap = (Dictionary)cce.NonNull(block2Incarnation[pred]); - - Expr pred_incarnation_exp; - Expr o = predMap.ContainsKey(v) ? predMap[v] : null; - if (o == null) { - Variable predIncarnation = v; - IdentifierExpr ie2 = new IdentifierExpr(predIncarnation.tok, predIncarnation); - pred_incarnation_exp = ie2; - } else { - pred_incarnation_exp = o; - } - #endregion - #region Create an identifier expression for the new incarnation - IdentifierExpr v_prime_exp = new IdentifierExpr(v_prime.tok, v_prime); - #endregion - #region Create the assume command itself - AssumeCmd ac = new AssumeCmd(v.tok, TypedExprEq(v_prime_exp, pred_incarnation_exp, v_prime.Name.Contains("a##cached##"))); - pred.Cmds.Add(ac); - #endregion - #endregion - } - } - #endregion - - Contract.Assert(incarnationMap != null); - return incarnationMap; - } - - Dictionary 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, Substitution oldFrameSubst, MutableVariableCollector variableCollector, byte[] currentChecksum = null) { - Contract.Requires(b != null); - Contract.Requires(incarnationMap != null); - Contract.Requires(mvInfo != null); - Contract.Requires(oldFrameSubst != null); - #region Walk forward over the commands in this block and convert them to passive commands - - List passiveCmds = new List(); - foreach (Cmd c in b.Cmds) { - 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); - variableCollector.Visit(c); - currentChecksum = c.Checksum; - TurnIntoPassiveCmd(c, incarnationMap, oldFrameSubst, passiveCmds, mvInfo, b); - } - b.Checksum = currentChecksum; - b.Cmds = passiveCmds; - - if (b.TransferCmd is ReturnExprCmd) { - ReturnExprCmd rec = (ReturnExprCmd)b.TransferCmd.Clone(); - Substitution incarnationSubst = Substituter.SubstitutionFromHashtable(incarnationMap); - rec.Expr = Substituter.ApplyReplacingOldExprs(incarnationSubst, oldFrameSubst, rec.Expr); - b.TransferCmd = rec; - } - #endregion - } - - protected Dictionary Convert2PassiveCmd(Implementation impl, ModelViewInfo mvInfo) { - Contract.Requires(impl != null); - Contract.Requires(mvInfo != null); - - currentImplementation = impl; - - var start = DateTime.UtcNow; - - Dictionary r = ConvertBlocks2PassiveCmd(impl.Blocks, impl.Proc.Modifies, mvInfo); - - var end = DateTime.UtcNow; - - if (CommandLineOptions.Clo.TraceCachingForDebugging) - { - Console.Out.WriteLine("Turned implementation into passive commands within {0:F0} ms.\n", end.Subtract(start).TotalMilliseconds); - } - - if (CommandLineOptions.Clo.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; - impl.Emit(tokTxtWr, 0); - CommandLineOptions.Clo.PrintDesugarings = pd; - CommandLineOptions.Clo.PrintUnstructured = pu; - } - } - - currentImplementation = null; - - RestoreParamWhereClauses(impl); - - #region Debug Tracing - if (CommandLineOptions.Clo.TraceVerify) { - Console.WriteLine("after conversion to passive commands"); - EmitImpl(impl, true); - } - #endregion - - return r; - } - - protected Dictionary ConvertBlocks2PassiveCmd(List blocks, List modifies, ModelViewInfo mvInfo) { - Contract.Requires(blocks != null); - Contract.Requires(modifies != null); - Contract.Requires(mvInfo != null); - #region Convert to Passive Commands - - #region Topological sort -- need to process in a linearization of the partial order - Graph dag = new Graph(); - dag.AddSource(cce.NonNull(blocks[0])); // there is always at least one node in the graph - foreach (Block b in blocks) { - GotoCmd gtc = b.TransferCmd as GotoCmd; - if (gtc != null) { - Contract.Assume(gtc.labelTargets != null); - foreach (Block dest in gtc.labelTargets) { - Contract.Assert(dest != null); - dag.AddEdge(b, dest); - } - } - } - - IEnumerable sortedNodes; - if (CommandLineOptions.Clo.ModifyTopologicalSorting) { - sortedNodes = dag.TopologicalSort(true); - } else { - sortedNodes = dag.TopologicalSort(); - } - - Contract.Assert(sortedNodes != null); - #endregion - - Substitution oldFrameSubst = ComputeOldExpressionSubstitution(modifies); - - // Now we can process the nodes in an order so that we're guaranteed to have - // processed all of a node's predecessors before we process the node. - Dictionary> block2Incarnation = new Dictionary>(); - Block exitBlock = null; - Dictionary exitIncarnationMap = null; - var variableCollectors = new Dictionary(); - foreach (Block b in sortedNodes) { - Contract.Assert(b != null); - Contract.Assert(!block2Incarnation.ContainsKey(b)); - Dictionary incarnationMap = ComputeIncarnationMap(b, block2Incarnation); - - // b.liveVarsBefore has served its purpose in the just-finished call to ComputeIncarnationMap; null it out. - b.liveVarsBefore = null; - - // Decrement the succCount field in each predecessor. Once the field reaches zero in any block, - // all its successors have been passified. Consequently, its entry in block2Incarnation can be removed. - byte[] currentChecksum = null; - var mvc = new MutableVariableCollector(); - variableCollectors[b] = mvc; - foreach (Block p in b.Predecessors) { - p.succCount--; - if (p.Checksum != null) - { - // Compute the checksum based on the checksums of the predecessor. The order should not matter. - currentChecksum = ChecksumHelper.CombineChecksums(p.Checksum, currentChecksum, true); - } - mvc.AddUsedVariables(variableCollectors[p].UsedVariables); - if (p.succCount == 0) - block2Incarnation.Remove(p); - } - - #region Each block's map needs to be available to successor blocks - GotoCmd gotoCmd = b.TransferCmd as GotoCmd; - if (gotoCmd == null) { - b.succCount = 0; - } - else { - // incarnationMap needs to be added only if there is some successor of b - b.succCount = gotoCmd.labelNames.Count; - block2Incarnation.Add(b, incarnationMap); - } - #endregion Each block's map needs to be available to successor blocks - - TurnIntoPassiveBlock(b, incarnationMap, mvInfo, oldFrameSubst, mvc, currentChecksum); - exitBlock = b; - exitIncarnationMap = incarnationMap; - } - - variableCollectors.Clear(); - - // Verify that exitBlock is indeed the unique exit block - Contract.Assert(exitBlock != null); - Contract.Assert(exitBlock.TransferCmd is ReturnCmd); - #endregion Convert to Passive Commands - - return exitIncarnationMap; - } - - /// - /// Compute the substitution for old expressions. - /// - protected static Substitution ComputeOldExpressionSubstitution(List modifies) - { - Dictionary oldFrameMap = new Dictionary(); - foreach (IdentifierExpr ie in modifies) - { - Contract.Assert(ie != null); - if (!oldFrameMap.ContainsKey(cce.NonNull(ie.Decl))) - oldFrameMap.Add(ie.Decl, ie); - } - return Substituter.SubstitutionFromHashtable(oldFrameMap); - } - - public enum CachingAction : byte - { - DoNothingToAssert, - MarkAsPartiallyVerified, - MarkAsFullyVerified, - RecycleError, - AssumeNegationOfAssumptionVariable, - DropAssume - } - - public long[] CachingActionCounts; - - void TraceCachingAction(Cmd cmd, CachingAction action) - { - if (CommandLineOptions.Clo.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) : ""; - Console.Write("Processing command (at {0}) ", loc); - cmd.Emit(tokTxtWr, 0); - Console.Out.WriteLine(" >>> {0}", action); - } - } - - if (CommandLineOptions.Clo.TraceCachingForBenchmarking && CachingActionCounts != null) - { - Interlocked.Increment(ref CachingActionCounts[(int)action]); - } - } - - /// - /// Turn a command into a passive command, and it remembers the previous step, to see if it is a havoc or not. In the 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, Dictionary incarnationMap, Substitution oldFrameSubst, List passiveCmds, ModelViewInfo mvInfo, Block containingBlock) { - Contract.Requires(c != null); - Contract.Requires(incarnationMap != null); - Contract.Requires(oldFrameSubst != null); - Contract.Requires(passiveCmds != null); - Contract.Requires(mvInfo != null); - Contract.Requires(containingBlock != null); - - Substitution incarnationSubst = Substituter.SubstitutionFromHashtable(incarnationMap); - #region assert/assume P |--> assert/assume P[x := in(x)], out := in - if (c is PredicateCmd) { - Contract.Assert(c is AssertCmd || c is AssumeCmd); // otherwise, unexpected PredicateCmd type - - PredicateCmd pc = (PredicateCmd)c.Clone(); - Contract.Assert(pc != null); - - Expr copy = Substituter.ApplyReplacingOldExprs(incarnationSubst, oldFrameSubst, pc.Expr); - if (CommandLineOptions.Clo.ModelViewFile != null && pc is AssumeCmd) { - string description = QKeyValue.FindStringAttribute(pc.Attributes, "captureState"); - if (description != null) { - Expr mv = new NAryExpr(pc.tok, new FunctionCall(ModelViewInfo.MVState_FunctionDef), new List { Bpl.Expr.Ident(ModelViewInfo.MVState_ConstantDef), Bpl.Expr.Literal(mvInfo.CapturePoints.Count) }); - copy = Bpl.Expr.And(mv, copy); - mvInfo.CapturePoints.Add(new ModelViewInfo.Mapping(description, new Dictionary(incarnationMap))); - } - } - Contract.Assert(copy != null); - var dropCmd = false; - var relevantAssumpVars = currentImplementation != null ? currentImplementation.RelevantInjectedAssumptionVariables(incarnationMap) : new List(); - var relevantDoomedAssumpVars = currentImplementation != null ? currentImplementation.RelevantDoomedInjectedAssumptionVariables(incarnationMap) : new List(); - var checksum = pc.Checksum; - if (pc is AssertCmd) { - var ac = (AssertCmd)pc; - ac.OrigExpr = ac.Expr; - Contract.Assert(ac.IncarnationMap == null); - ac.IncarnationMap = (Dictionary)cce.NonNull(new Dictionary(incarnationMap)); - - var subsumption = Wlp.Subsumption(ac); - if (relevantDoomedAssumpVars.Any()) - { - TraceCachingAction(pc, CachingAction.DoNothingToAssert); - } - else if (currentImplementation != null - && currentImplementation.HasCachedSnapshot - && checksum != null - && currentImplementation.IsAssertionChecksumInCachedSnapshot(checksum) - && !currentImplementation.IsErrorChecksumInCachedSnapshot(checksum)) - { - if (!currentImplementation.AnyErrorsInCachedSnapshot - && currentImplementation.InjectedAssumptionVariables.Count == 1 - && relevantAssumpVars.Count == 1) - { - TraceCachingAction(pc, CachingAction.MarkAsPartiallyVerified); - } - else - { - bool isTrue; - var assmVars = currentImplementation.ConjunctionOfInjectedAssumptionVariables(incarnationMap, out isTrue); - TraceCachingAction(pc, !isTrue ? CachingAction.MarkAsPartiallyVerified : CachingAction.MarkAsFullyVerified); - var litExpr = ac.Expr as LiteralExpr; - if (litExpr == null || !litExpr.IsTrue) - { - ac.MarkAsVerifiedUnder(assmVars); - } - else - { - dropCmd = true; - } - } - } - else if (currentImplementation != null - && currentImplementation.HasCachedSnapshot - && relevantAssumpVars.Count == 0 - && checksum != null - && currentImplementation.IsAssertionChecksumInCachedSnapshot(checksum) - && currentImplementation.IsErrorChecksumInCachedSnapshot(checksum)) - { - TraceCachingAction(pc, CachingAction.RecycleError); - ac.MarkAsVerifiedUnder(Expr.True); - currentImplementation.AddRecycledFailingAssertion(ac); - pc.Attributes = new QKeyValue(Token.NoToken, "recycled_failing_assertion", new List(), pc.Attributes); - } - else - { - TraceCachingAction(pc, CachingAction.DoNothingToAssert); - } - } - else if (pc is AssumeCmd - && QKeyValue.FindBoolAttribute(pc.Attributes, "precondition_previous_snapshot") - && pc.SugaredCmdChecksum != null) - { - if (!relevantDoomedAssumpVars.Any() - && currentImplementation.HasCachedSnapshot - && currentImplementation.IsAssertionChecksumInCachedSnapshot(pc.SugaredCmdChecksum) - && !currentImplementation.IsErrorChecksumInCachedSnapshot(pc.SugaredCmdChecksum)) - { - bool isTrue; - var assmVars = currentImplementation.ConjunctionOfInjectedAssumptionVariables(incarnationMap, out isTrue); - if (!isTrue) - { - copy = LiteralExpr.Imp(assmVars, copy); - TraceCachingAction(pc, CachingAction.MarkAsPartiallyVerified); - } - else - { - TraceCachingAction(pc, CachingAction.MarkAsFullyVerified); - } - } - else - { - TraceCachingAction(pc, CachingAction.DropAssume); - dropCmd = true; - } - } - else if (pc is AssumeCmd && QKeyValue.FindBoolAttribute(pc.Attributes, "assumption_variable_initialization")) - { - var identExpr = pc.Expr as IdentifierExpr; - if (identExpr != null && identExpr.Decl != null && !incarnationMap.ContainsKey(identExpr.Decl)) - { - incarnationMap[identExpr.Decl] = LiteralExpr.True; - dropCmd = true; - } - } - pc.Expr = copy; - if (!dropCmd) - { - passiveCmds.Add(pc); - } - } - #endregion - #region x1 := E1, x2 := E2, ... |--> assume x1' = E1[in] & x2' = E2[in], out := in( x |-> x' ) [except as noted below] - else if (c is AssignCmd) { - AssignCmd assign = ((AssignCmd)c).AsSimpleAssignCmd; // first remove map assignments - Contract.Assert(assign != null); - #region Substitute all variables in E with the current map - List copies = new List(); - foreach (Expr e in assign.Rhss) { - Contract.Assert(e != null); - copies.Add(Substituter.ApplyReplacingOldExprs(incarnationSubst, - oldFrameSubst, - e)); - } - #endregion - - List!*/> assumptions = new List(); - // it might be too slow to create a new dictionary each time ... - IDictionary newIncarnationMappings = - new Dictionary(); - - for (int i = 0; i < assign.Lhss.Count; ++i) { - IdentifierExpr lhsIdExpr = - cce.NonNull((SimpleAssignLhs)assign.Lhss[i]).AssignedVariable; - Variable lhs = cce.NonNull(lhsIdExpr.Decl); - Contract.Assert(lhs != null); - Expr rhs = assign.Rhss[i]; - Contract.Assert(rhs != null); - - // don't create incarnations for assignments of literals or single variables. - if (rhs is LiteralExpr) { - incarnationMap[lhs] = rhs; - } else if (rhs is IdentifierExpr) { - IdentifierExpr ie = (IdentifierExpr)rhs; - if (incarnationMap.ContainsKey(cce.NonNull(ie.Decl))) - newIncarnationMappings[lhs] = cce.NonNull((Expr)incarnationMap[ie.Decl]); - else - newIncarnationMappings[lhs] = ie; - } else { - IdentifierExpr x_prime_exp = null; - #region Make a new incarnation, x', for variable x, but only if x is *not* already an incarnation - if (lhs is Incarnation) { - // incarnations are already written only once, no need to make an incarnation of an incarnation - x_prime_exp = lhsIdExpr; - } else { - Variable v = CreateIncarnation(lhs, c); - x_prime_exp = new IdentifierExpr(lhsIdExpr.tok, v); - newIncarnationMappings[lhs] = x_prime_exp; - } - #endregion - - var nAryExpr = copies[i] as NAryExpr; - if (nAryExpr != null) - { - var binOp = nAryExpr.Fun as BinaryOperator; - if (binOp != null - && binOp.Op == BinaryOperator.Opcode.And) - { - var arg0 = nAryExpr.Args[0] as LiteralExpr; - var arg1 = nAryExpr.Args[1] as LiteralExpr; - if ((arg0 != null && arg0.IsTrue) || (arg1 != null && arg1.IsFalse)) - { - // Replace the expressions "true && arg1" or "arg0 && false" by "arg1". - copies[i] = nAryExpr.Args[1]; - } - } - } - - #region Create an assume command with the new variable - assumptions.Add(TypedExprEq(x_prime_exp, copies[i], x_prime_exp.Decl != null && x_prime_exp.Decl.Name.Contains("a##cached##"))); - #endregion - } - } - - foreach (KeyValuePair pair in newIncarnationMappings) { - Contract.Assert(pair.Key != null && pair.Value != null); - incarnationMap[pair.Key] = pair.Value; - } - - if (assumptions.Count > 0) { - Expr assumption = assumptions[0]; - - for (int i = 1; i < assumptions.Count; ++i) { - Contract.Assert(assumption != null); - assumption = Expr.And(assumption, assumptions[i]); - } - passiveCmds.Add(new AssumeCmd(c.tok, assumption)); - } - - if (currentImplementation != null - && currentImplementation.HasCachedSnapshot - && !currentImplementation.AnyErrorsInCachedSnapshot - && currentImplementation.DoomedInjectedAssumptionVariables.Count == 0 - && currentImplementation.InjectedAssumptionVariables.Count == 1 - && assign.Lhss.Count == 1) - { - var identExpr = assign.Lhss[0].AsExpr as IdentifierExpr; - Expr incarnation; - if (identExpr != null && identExpr.Decl != null && QKeyValue.FindBoolAttribute(identExpr.Decl.Attributes, "assumption") && incarnationMap.TryGetValue(identExpr.Decl, out incarnation)) - { - TraceCachingAction(assign, CachingAction.AssumeNegationOfAssumptionVariable); - passiveCmds.Add(new AssumeCmd(c.tok, Expr.Not(incarnation))); - } - } - } - #endregion - #region havoc w |--> assume whereClauses, out := in( w |-> w' ) - else if (c is HavocCmd) { - if (this.preHavocIncarnationMap == null) // Save a copy of the incarnation map (at the top of a sequence of havoc statements) - this.preHavocIncarnationMap = new Dictionary(incarnationMap); - - HavocCmd hc = (HavocCmd)c; - Contract.Assert(c != null); - // If an assumption variable for postconditions is included here, it must have been assigned within a loop. - // We do not need to havoc it if we have performed a modular proof of the loop (i.e., using only the loop - // invariant) in the previous snapshot and, consequently, the corresponding assumption did not affect the - // anything after the loop. We can achieve this by simply not updating/adding it in the incarnation map. - List havocVars = hc.Vars.Where(v => !(QKeyValue.FindBoolAttribute(v.Decl.Attributes, "assumption") && v.Decl.Name.StartsWith("a##cached##"))).ToList(); - // First, compute the new incarnations - foreach (IdentifierExpr ie in havocVars) { - Contract.Assert(ie != null); - if (!(ie.Decl is Incarnation)) { - Variable x = cce.NonNull(ie.Decl); - Variable x_prime = CreateIncarnation(x, c); - incarnationMap[x] = new IdentifierExpr(x_prime.tok, x_prime); - } - } - // Then, perform the assume of the where clauses, using the updated incarnations - Substitution updatedIncarnationSubst = Substituter.SubstitutionFromHashtable(incarnationMap); - foreach (IdentifierExpr ie in havocVars) { - Contract.Assert(ie != null); - if (!(ie.Decl is Incarnation)) { - Variable x = cce.NonNull(ie.Decl); - Bpl.Expr w = x.TypedIdent.WhereExpr; - if (w != null) { - Expr copy = Substituter.ApplyReplacingOldExprs(updatedIncarnationSubst, oldFrameSubst, w); - passiveCmds.Add(new AssumeCmd(c.tok, copy)); - } - } - } - - // Add the following assume-statement for each assumption variable 'v', where 'v_post' is the new incarnation and 'v_pre' is the old one: - // assume v_post ==> v_pre; - foreach (IdentifierExpr ie in havocVars) - { - if (QKeyValue.FindBoolAttribute(ie.Decl.Attributes, "assumption")) - { - var preInc = (Expr)(preHavocIncarnationMap[ie.Decl].Clone()); - var postInc = (Expr)(incarnationMap[ie.Decl].Clone()); - passiveCmds.Add(new AssumeCmd(c.tok, Expr.Imp(postInc, preInc))); - } - } - } - #endregion - else if (c is CommentCmd) { - // comments are just for debugging and don't affect verification - } else if (c is SugaredCmd) { - SugaredCmd sug = (SugaredCmd)c; - Contract.Assert(sug != null); - Cmd cmd = sug.Desugaring; - Contract.Assert(cmd != null); - TurnIntoPassiveCmd(cmd, incarnationMap, oldFrameSubst, passiveCmds, mvInfo, containingBlock); - } else if (c is StateCmd) { - this.preHavocIncarnationMap = null; // we do not need to remeber the previous incarnations - StateCmd st = (StateCmd)c; - Contract.Assert(st != null); - // account for any where clauses among the local variables - foreach (Variable v in st.Locals) { - Contract.Assert(v != null); - Expr w = v.TypedIdent.WhereExpr; - if (w != null) { - passiveCmds.Add(new AssumeCmd(v.tok, w)); - } - } - // do the sub-commands - foreach (Cmd s in st.Cmds) { - Contract.Assert(s != null); - TurnIntoPassiveCmd(s, incarnationMap, oldFrameSubst, passiveCmds, mvInfo, containingBlock); - } - // remove the local variables from the incarnation map - foreach (Variable v in st.Locals) { - Contract.Assert(v != null); - incarnationMap.Remove(v); - } - } - #region There shouldn't be any other types of commands at this point - else { - Debug.Fail("Internal Error: Passive transformation handed a command that is not one of assert,assume,havoc,assign."); - } - #endregion - - - #region We remember if we have put an havoc statement into a passive form - - if (!(c is HavocCmd)) - this.preHavocIncarnationMap = null; - // else: it has already been set by the case for the HavocCmd - #endregion - } - - NAryExpr TypedExprEq(Expr e0, Expr e1, bool doNotResolveOverloading = false) { - Contract.Requires(e0 != null); - Contract.Requires(e1 != null); - NAryExpr e = Expr.Eq(e0, e1); - var fun = e.Fun as IOverloadedAppliable; - if (fun != null) - { - fun.DoNotResolveOverloading = doNotResolveOverloading; - } - e.Type = Bpl.Type.Bool; - e.TypeParameters = SimpleTypeParamInstantiation.EMPTY; - return e; - } - - /// - /// Creates a new block to add to impl.Blocks, where impl is the implementation that contains - /// succ. Caller must do the add to impl.Blocks. - /// - protected Block CreateBlockBetween(int predIndex, Block succ) { - Contract.Requires(0 <= predIndex && predIndex < succ.Predecessors.Count); - - - Contract.Requires(succ != null); - Contract.Ensures(Contract.Result() != null); - - Block pred = cce.NonNull(succ.Predecessors[predIndex]); - - string newBlockLabel = pred.Label + "_@2_" + succ.Label; - - // successor of newBlock list - List ls = new List(); - ls.Add(succ.Label); - List bs = new List(); - bs.Add(succ); - - Block newBlock = new Block( - new Token(-17, -4), - newBlockLabel, - new List(), - new GotoCmd(Token.NoToken, ls, bs) - ); - - // predecessors of newBlock - List ps = new List(); - ps.Add(pred); - newBlock.Predecessors = ps; - - // fix successors of pred - #region Change the edge "pred->succ" to "pred->newBlock" - GotoCmd gtc = (GotoCmd)cce.NonNull(pred.TransferCmd); - Contract.Assume(gtc.labelTargets != null); - Contract.Assume(gtc.labelNames != null); - for (int i = 0, n = gtc.labelTargets.Count; i < n; i++) { - if (gtc.labelTargets[i] == succ) { - gtc.labelTargets[i] = newBlock; - gtc.labelNames[i] = newBlockLabel; - break; - } - } - #endregion Change the edge "pred->succ" to "pred->newBlock" - - // fix predecessors of succ - succ.Predecessors[predIndex] = newBlock; - - return newBlock; - } - - protected void AddBlocksBetween(List blocks) { - Contract.Requires(blocks != null); - #region Introduce empty blocks between join points and their multi-successor predecessors - List tweens = new List(); - foreach (Block b in blocks) { - int nPreds = b.Predecessors.Count; - if (nPreds > 1) { - // b is a join point (i.e., it has more than one predecessor) - for (int i = 0; i < nPreds; i++) { - GotoCmd gotocmd = (GotoCmd)(cce.NonNull(b.Predecessors[i]).TransferCmd); - if (gotocmd.labelNames != null && gotocmd.labelNames.Count > 1) { - tweens.Add(CreateBlockBetween(i, b)); - } - } - } - } - blocks.AddRange(tweens); // must wait until iteration is done before changing the list - #endregion - } - - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - if (!_disposed) - { - if (disposing) - { - Close(); - } - _disposed = true; - } - } - - public int Cores { get; set; } - } - - public class ModelViewInfo - { - public readonly List AllVariables = new List(); - public readonly List CapturePoints = new List(); - public static readonly Function MVState_FunctionDef = new Function(Token.NoToken, "$mv_state", - new List { new Formal(Token.NoToken, new TypedIdent(Token.NoToken, TypedIdent.NoName, Bpl.Type.Int), true), - new Formal(Token.NoToken, new TypedIdent(Token.NoToken, TypedIdent.NoName, Bpl.Type.Int), true) }, - new Formal(Token.NoToken, new TypedIdent(Token.NoToken, TypedIdent.NoName, Bpl.Type.Bool), false)); - public static readonly Constant MVState_ConstantDef = new Constant(Token.NoToken, new TypedIdent(Token.NoToken, "$mv_state_const", Bpl.Type.Int)); - - public ModelViewInfo(Program program, Implementation impl) { - Contract.Requires(program != null); - Contract.Requires(impl != null); - - // global variables - lock (program.TopLevelDeclarations) - { - foreach (var v in program.Variables) - { - if (!(v is Constant)) - { - AllVariables.Add(v); - } - } - } - // implementation parameters - foreach (Variable p in impl.InParams) { - AllVariables.Add(p); - } - foreach (Variable p in impl.OutParams) { - AllVariables.Add(p); - } - // implementation locals - foreach (Variable v in impl.LocVars) { - AllVariables.Add(v); - } - } - - public ModelViewInfo(CodeExpr codeExpr) { - Contract.Requires(codeExpr != null); - // TODO: also need all variables of enclosing scopes (the global variables of the program, the parameters - // and perhaps locals of the implementation (if any), any enclosing code expressions). - - foreach (Variable v in codeExpr.LocVars) { - AllVariables.Add(v); - } - } - - public class Mapping - { - public readonly string Description; - public readonly Dictionary IncarnationMap; - public Mapping(string description, Dictionary incarnationMap) { - Description = description; - IncarnationMap = incarnationMap; - } - } - } -} +//----------------------------------------------------------------------------- +// +// Copyright (C) Microsoft Corporation. All Rights Reserved. +// +//----------------------------------------------------------------------------- +using System; +using System.Linq; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; +using System.IO; +using Microsoft.Boogie; +using Microsoft.Boogie.GraphUtil; +using System.Diagnostics.Contracts; +using Microsoft.Basetypes; +using Microsoft.Boogie.VCExprAST; +using Set = Microsoft.Boogie.GSet; + +namespace Microsoft.Boogie { + + public class CalleeCounterexampleInfo { + public Counterexample counterexample; + public List/*!>!*/ args; + + [ContractInvariantMethod] + void ObjectInvariant() { + Contract.Invariant(cce.NonNullElements(args)); + } + + public CalleeCounterexampleInfo(Counterexample cex, List!*/> x) + { + Contract.Requires(cce.NonNullElements(x)); + counterexample = cex; + args = x; + } + } + + public class TraceLocation : IEquatable + { + public int numBlock; + public int numInstr; + + public TraceLocation(int numBlock, int numInstr) + { + this.numBlock = numBlock; + this.numInstr = numInstr; + } + + public override bool Equals(object obj) + { + TraceLocation that = obj as TraceLocation; + if (that == null) return false; + return (numBlock == that.numBlock && numInstr == that.numInstr); + } + + public bool Equals(TraceLocation that) + { + return (numBlock == that.numBlock && numInstr == that.numInstr); + } + + public override int GetHashCode() + { + return numBlock.GetHashCode() ^ 131 * numInstr.GetHashCode(); + } + } + + public abstract class Counterexample { + [ContractInvariantMethod] + void ObjectInvariant() { + Contract.Invariant(Trace != null); + Contract.Invariant(Context != null); + Contract.Invariant(cce.NonNullElements(relatedInformation)); + Contract.Invariant(cce.NonNullDictionaryAndValues(calleeCounterexamples)); + } + + [Peer] + public List Trace; + public Model Model; + public VC.ModelViewInfo MvInfo; + public ProverContext Context; + [Peer] + public List/*!>!*/ relatedInformation; + public string OriginalRequestId; + public string RequestId; + public abstract byte[] Checksum { get; } + public byte[] SugaredCmdChecksum; + + public Dictionary calleeCounterexamples; + + internal Counterexample(List trace, Model model, VC.ModelViewInfo mvInfo, ProverContext context) { + Contract.Requires(trace != null); + Contract.Requires(context != null); + this.Trace = trace; + this.Model = model; + this.MvInfo = mvInfo; + this.Context = context; + this.relatedInformation = new List(); + this.calleeCounterexamples = new Dictionary(); + } + + // Create a shallow copy of the counterexample + public abstract Counterexample Clone(); + + public void AddCalleeCounterexample(TraceLocation loc, CalleeCounterexampleInfo cex) + { + Contract.Requires(cex != null); + calleeCounterexamples[loc] = cex; + } + + public void AddCalleeCounterexample(int numBlock, int numInstr, CalleeCounterexampleInfo cex) + { + Contract.Requires(cex != null); + calleeCounterexamples[new TraceLocation(numBlock, numInstr)] = cex; + } + + public void AddCalleeCounterexample(Dictionary cs) + { + Contract.Requires(cce.NonNullDictionaryAndValues(cs)); + foreach (TraceLocation loc in cs.Keys) + { + AddCalleeCounterexample(loc, cs[loc]); + } + } + + // Looks up the Cmd at a given index into the trace + public Cmd getTraceCmd(TraceLocation loc) + { + Debug.Assert(loc.numBlock < Trace.Count); + Block b = Trace[loc.numBlock]; + Debug.Assert(loc.numInstr < b.Cmds.Count); + return b.Cmds[loc.numInstr]; + } + + // Looks up the name of the called procedure. + // Asserts that the name exists + public string getCalledProcName(Cmd cmd) + { + // There are two options: + // 1. cmd is a CallCmd + // 2. cmd is an AssumeCmd (passified version of a CallCmd) + if(cmd is CallCmd) { + return (cmd as CallCmd).Proc.Name; + } + AssumeCmd assumeCmd = cmd as AssumeCmd; + Debug.Assert(assumeCmd != null); + + NAryExpr naryExpr = assumeCmd.Expr as NAryExpr; + Debug.Assert(naryExpr != null); + + return naryExpr.Fun.FunctionName; + } + + public void Print(int indent, TextWriter tw, Action blockAction = null) { + int numBlock = -1; + string ind = new string(' ', indent); + foreach (Block b in Trace) { + Contract.Assert(b != null); + numBlock++; + if (b.tok == null) { + tw.WriteLine("{0}", ind); + } else { + // 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 (blockAction != null) + { + blockAction(b); + } + + tw.WriteLine("{4}{0}({1},{2}): {3}", b.tok.filename, b.tok.line, b.tok.col, b.Label, ind); + + for (int numInstr = 0; numInstr < b.Cmds.Count; numInstr++) + { + var loc = new TraceLocation(numBlock, numInstr); + if (calleeCounterexamples.ContainsKey(loc)) + { + var cmd = getTraceCmd(loc); + var calleeName = getCalledProcName(cmd); + if (calleeName.StartsWith(VC.StratifiedVCGen.recordProcName) && CommandLineOptions.Clo.StratifiedInlining > 0) + { + Contract.Assert(calleeCounterexamples[loc].args.Count == 1); + var arg = calleeCounterexamples[loc].args[0]; + tw.WriteLine("{0}value = {1}", ind, arg.ToString()); + } + else + { + tw.WriteLine("{1}Inlined call to procedure {0} begins", calleeName, ind); + calleeCounterexamples[loc].counterexample.Print(indent + 4, tw); + tw.WriteLine("{1}Inlined call to procedure {0} ends", calleeName, ind); + } + } + } + } + } + } + } + + public static bool firstModelFile = true; + + public bool ModelHasStatesAlready = false; + + public void PrintModel(TextWriter tw) + { + var filename = CommandLineOptions.Clo.ModelViewFile; + if (Model == null || filename == null || CommandLineOptions.Clo.StratifiedInlining > 0) return; + + if (!ModelHasStatesAlready) { + PopulateModelWithStates(); + ModelHasStatesAlready = true; + } + + if (filename == "-") { + Model.Write(tw); + tw.Flush(); + } else { + using (var wr = new StreamWriter(filename, !firstModelFile)) { + firstModelFile = false; + Model.Write(wr); + } + } + } + + void ApplyRedirections(Model m) { + var mapping = new Dictionary(); + foreach (var name in new string[] { "U_2_bool", "U_2_int" }) { + Model.Func f = m.TryGetFunc(name); + if (f != null && f.Arity == 1) { + foreach (var ft in f.Apps) mapping[ft.Args[0]] = ft.Result; + } + } + m.Substitute(mapping); + } + + public void PopulateModelWithStates() + { + Contract.Requires(Model != null); + + Model m = Model; + ApplyRedirections(m); + + var mvstates = m.TryGetFunc("$mv_state"); + if (MvInfo == null || mvstates == null || (mvstates.Arity == 1 && mvstates.Apps.Count() == 0)) + return; + + Contract.Assert(mvstates.Arity == 2); + + foreach (Variable v in MvInfo.AllVariables) { + m.InitialState.AddBinding(v.Name, GetModelValue(m, v)); + } + + var states = new List(); + foreach (var t in mvstates.Apps) + states.Add(t.Args[1].AsInt()); + + states.Sort(); + + for (int i = 0; i < states.Count; ++i) { + var s = states[i]; + if (0 <= s && s < MvInfo.CapturePoints.Count) { + VC.ModelViewInfo.Mapping map = MvInfo.CapturePoints[s]; + var prevInc = i > 0 ? MvInfo.CapturePoints[states[i - 1]].IncarnationMap : new Dictionary(); + var cs = m.MkState(map.Description); + + foreach (Variable v in MvInfo.AllVariables) { + Expr e = map.IncarnationMap.ContainsKey(v) ? map.IncarnationMap[v] : null; + if (e == null) continue; + + Expr prevIncV = prevInc.ContainsKey(v) ? prevInc[v] : null; + if (prevIncV == e) continue; // skip unchanged variables + + Model.Element elt; + + if (e is IdentifierExpr) { + IdentifierExpr ide = (IdentifierExpr)e; + elt = GetModelValue(m, ide.Decl); + } else if (e is LiteralExpr) { + LiteralExpr lit = (LiteralExpr)e; + elt = m.MkElement(lit.Val.ToString()); + } else { + elt = m.MkFunc(e.ToString(), 0).GetConstant(); + } + + cs.AddBinding(v.Name, elt); + } + + } else { + Contract.Assume(false); + } + } + } + + private Model.Element GetModelValue(Model m, Variable v) { + Model.Element elt; + // first, get the unique name + string uniqueName; + VCExprVar vvar = Context.BoogieExprTranslator.TryLookupVariable(v); + if (vvar == null) { + uniqueName = v.Name; + } else { + uniqueName = Context.Lookup(vvar); + } + + var f = m.TryGetFunc(uniqueName); + if (f == null) { + f = m.MkFunc(uniqueName, 0); + } + elt = f.GetConstant(); + return elt; + } + + public abstract int GetLocation(); + } + + public class CounterexampleComparer : IComparer { + + private int Compare(List bs1, List bs2) + { + if (bs1.Count < bs2.Count) + { + return -1; + } + else if (bs2.Count < bs1.Count) + { + return 1; + } + + for (int i = 0; i < bs1.Count; i++) + { + var b1 = bs1[i]; + var b2 = bs2[i]; + if (b1.tok.pos < b2.tok.pos) + { + return -1; + } + else if (b2.tok.pos < b1.tok.pos) + { + return 1; + } + } + + return 0; + } + + public int Compare(Counterexample c1, Counterexample c2) + { + //Contract.Requires(c1 != null); + //Contract.Requires(c2 != null); + if (c1.GetLocation() == c2.GetLocation()) + { + var c = Compare(c1.Trace, c2.Trace); + if (c != 0) + { + return c; + } + // TODO(wuestholz): Generalize this to compare all IPotentialErrorNodes of the counterexample. + var a1 = c1 as AssertCounterexample; + var a2 = c2 as AssertCounterexample; + if (a1 != null && a2 != null) + { + var s1 = a1.FailingAssert.ErrorData as string; + var s2 = a2.FailingAssert.ErrorData as string; + if (s1 != null && s2 != null) + { + return s1.CompareTo(s2); + } + } + + return 0; + } + if (c1.GetLocation() > c2.GetLocation()) + { + return 1; + } + return -1; + } + } + + public class AssertCounterexample : Counterexample { + [Peer] + public AssertCmd FailingAssert; + [ContractInvariantMethod] + void ObjectInvariant() { + Contract.Invariant(FailingAssert != null); + } + + + public AssertCounterexample(List trace, AssertCmd failingAssert, Model model, VC.ModelViewInfo mvInfo, ProverContext context) + : base(trace, model, mvInfo, context) { + Contract.Requires(trace != null); + Contract.Requires(failingAssert != null); + Contract.Requires(context != null); + this.FailingAssert = failingAssert; + } + + public override int GetLocation() { + return FailingAssert.tok.line * 1000 + FailingAssert.tok.col; + } + + public override byte[] Checksum + { + get { return FailingAssert.Checksum; } + } + + public override Counterexample Clone() + { + var ret = new AssertCounterexample(Trace, FailingAssert, Model, MvInfo, Context); + ret.calleeCounterexamples = calleeCounterexamples; + return ret; + } + } + + public class CallCounterexample : Counterexample { + public CallCmd FailingCall; + public Requires FailingRequires; + [ContractInvariantMethod] + void ObjectInvariant() { + Contract.Invariant(FailingCall != null); + Contract.Invariant(FailingRequires != null); + } + + + public CallCounterexample(List trace, CallCmd failingCall, Requires failingRequires, Model model, VC.ModelViewInfo mvInfo, ProverContext context, byte[] checksum = null) + : base(trace, model, mvInfo, context) { + Contract.Requires(!failingRequires.Free); + Contract.Requires(trace != null); + Contract.Requires(context != null); + Contract.Requires(failingCall != null); + Contract.Requires(failingRequires != null); + this.FailingCall = failingCall; + this.FailingRequires = failingRequires; + this.checksum = checksum; + this.SugaredCmdChecksum = failingCall.Checksum; + } + + public override int GetLocation() { + return FailingCall.tok.line * 1000 + FailingCall.tok.col; + } + + byte[] checksum; + public override byte[] Checksum + { + get { return checksum; } + } + + public override Counterexample Clone() + { + var ret = new CallCounterexample(Trace, FailingCall, FailingRequires, Model, MvInfo, Context, Checksum); + ret.calleeCounterexamples = calleeCounterexamples; + return ret; + } + } + + public class ReturnCounterexample : Counterexample { + public TransferCmd FailingReturn; + public Ensures FailingEnsures; + [ContractInvariantMethod] + void ObjectInvariant() { + Contract.Invariant(FailingEnsures != null); + Contract.Invariant(FailingReturn != null); + } + + + public ReturnCounterexample(List trace, TransferCmd failingReturn, Ensures failingEnsures, Model model, VC.ModelViewInfo mvInfo, ProverContext context, byte[] checksum) + : base(trace, model, mvInfo, context) { + Contract.Requires(trace != null); + Contract.Requires(context != null); + Contract.Requires(failingReturn != null); + Contract.Requires(failingEnsures != null); + Contract.Requires(!failingEnsures.Free); + this.FailingReturn = failingReturn; + this.FailingEnsures = failingEnsures; + this.checksum = checksum; + } + + public override int GetLocation() { + return FailingReturn.tok.line * 1000 + FailingReturn.tok.col; + } + + byte[] checksum; + + /// + /// Returns the checksum of the corresponding assertion. + /// + public override byte[] Checksum + { + get + { + return checksum; + } + } + + public override Counterexample Clone() + { + var ret = new ReturnCounterexample(Trace, FailingReturn, FailingEnsures, Model, MvInfo, Context, checksum); + ret.calleeCounterexamples = calleeCounterexamples; + return ret; + } + } + + public class VerifierCallback { + // 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) { + Contract.Requires(ce != null); + } + + // called in case resource is exceeded and we don't have counterexample + public virtual void OnTimeout(string reason) { + Contract.Requires(reason != null); + } + + public virtual void OnOutOfMemory(string reason) { + Contract.Requires(reason != null); + } + + public virtual void OnProgress(string phase, int step, int totalSteps, double progressEstimate) { + } + + public virtual void OnUnreachableCode(Implementation impl) { + Contract.Requires(impl != null); + } + + public virtual void OnWarning(string msg) { + Contract.Requires(msg != null); + switch (CommandLineOptions.Clo.PrintProverWarnings) { + case CommandLineOptions.ProverWarnings.None: + break; + case CommandLineOptions.ProverWarnings.Stdout: + Console.WriteLine("Prover warning: " + msg); + break; + case CommandLineOptions.ProverWarnings.Stderr: + Console.Error.WriteLine("Prover warning: " + msg); + break; + default: + Contract.Assume(false); + throw new cce.UnreachableException(); // unexpected case + } + } + } +} + +//////////////////////////////////////////// + +namespace VC { + using Bpl = Microsoft.Boogie; + + public class VCGenException : Exception { + public VCGenException(string s) + : base(s) { + } + } + [ContractClassFor(typeof(ConditionGeneration))] + public abstract class ConditionGenerationContracts : ConditionGeneration { + public override Outcome VerifyImplementation(Implementation impl, VerifierCallback callback) { + Contract.Requires(impl != null); + Contract.Requires(callback != null); + Contract.EnsuresOnThrow(true); + throw new NotImplementedException(); + } + public ConditionGenerationContracts(Program p, List checkers) + : base(p, checkers) { + } + } + + [ContractClass(typeof(ConditionGenerationContracts))] + public abstract class ConditionGeneration : IDisposable { + protected internal object CheckerCommonState; + + public enum Outcome { + Correct, + Errors, + TimedOut, + OutOfMemory, + Inconclusive, + ReachedBound + } + + public static Outcome ProverInterfaceOutcomeToConditionGenerationOutcome(ProverInterface.Outcome outcome) { + switch (outcome) { + case ProverInterface.Outcome.Invalid: + return Outcome.Errors; + case ProverInterface.Outcome.OutOfMemory: + return Outcome.OutOfMemory; + case ProverInterface.Outcome.TimeOut: + return Outcome.TimedOut; + case ProverInterface.Outcome.Undetermined: + return Outcome.Inconclusive; + case ProverInterface.Outcome.Valid: + return Outcome.Correct; + } + return Outcome.Inconclusive; // unreachable but the stupid compiler does not understand + } + + [ContractInvariantMethod] + void ObjectInvariant() { + Contract.Invariant(cce.NonNullElements(checkers)); + Contract.Invariant(cce.NonNullDictionaryAndValues(incarnationOriginMap)); + Contract.Invariant(program != null); + } + + public int CumulativeAssertionCount; // for statistics + + protected readonly List/*!>!*/ checkers; + + private bool _disposed; + + protected Implementation currentImplementation; + + protected List CurrentLocalVariables = null; + + // shared across each implementation; created anew for each implementation + protected Dictionary variable2SequenceNumber; + public Dictionary/*!>!*/ incarnationOriginMap = new Dictionary(); + + public Program program; + protected string/*?*/ logFilePath; + protected bool appendLogFile; + + public static List errorModelList; + + public ConditionGeneration(Program p, List checkers) { + Contract.Requires(p != null && checkers != null && cce.NonNullElements(checkers)); + program = p; + this.checkers = checkers; + Cores = 1; + } + + /// + /// Takes an implementation and constructs a verification condition and sends + /// it to the theorem prover. + /// Returns null if "impl" is correct. Otherwise, returns a list of counterexamples, + /// each counterexample consisting of an array of labels. + /// + /// + public Outcome VerifyImplementation(Implementation impl, out List/*?*/ errors, string requestId = null) { + Contract.Requires(impl != 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("Starting implementation verification"); + + CounterexampleCollector collector = new CounterexampleCollector(); + collector.RequestId = requestId; + Outcome outcome = VerifyImplementation(impl, collector); + if (outcome == Outcome.Errors || outcome == Outcome.TimedOut || outcome == Outcome.OutOfMemory) { + errors = collector.examples; + } else { + errors = null; + } + + Helpers.ExtraTraceInformation("Finished implementation verification"); + return outcome; + } + + /// + /// Takes an implementation and constructs a verification condition and sends + /// it to the theorem prover. + /// Returns null if "impl" is correct. Otherwise, returns a list of counterexamples, + /// each counterexample consisting of an array of labels. + /// + /// + public Outcome VerifyImplementation(Implementation impl, out List errors, out List errorsModel) + { + Contract.Ensures(Contract.Result() != Outcome.Errors || Contract.ValueAtReturn(out errors) != null); + Contract.EnsuresOnThrow(true); + List errorsOut; + + Outcome outcome; + errorModelList = new List(); + outcome = VerifyImplementation(impl, out errorsOut); + errors = errorsOut; + errorsModel = errorModelList; + + return outcome; + } + + public abstract Outcome VerifyImplementation(Implementation impl, VerifierCallback callback); + + /////////////////////////////////// Common Methods and Classes ////////////////////////////////////////// + + #region Methods for injecting pre- and postconditions + private static void + ThreadInCodeExpr(Implementation impl, + Block targetBlock, + CodeExpr codeExpr, + bool replaceWithAssert, + TokenTextWriter debugWriter) { + Contract.Requires(impl != null); + Contract.Requires(codeExpr != null); + Contract.Requires(targetBlock != null); + // Go through codeExpr and for all blocks that have a "return e" + // as their transfer command: + // Replace all "return e" with "assert/assume e" + // Change the transfer command to "goto targetBlock" + // Then add all of the blocks in codeExpr to the implementation (at the end) + foreach (Block b in codeExpr.Blocks) { + Contract.Assert(b != null); + ReturnExprCmd rec = b.TransferCmd as ReturnExprCmd; + if (rec != null) { // otherwise it is a goto command + if (replaceWithAssert) { + Ensures ens = new Ensures(rec.tok, false, rec.Expr, null); + Contract.Assert(ens != null); + Cmd c = new AssertEnsuresCmd(ens); + Contract.Assert(c != null); + b.Cmds.Add(c); + } else { + b.Cmds.Add(new AssumeCmd(rec.tok, rec.Expr)); + } + b.TransferCmd = new GotoCmd(Token.NoToken, + new List { targetBlock.Label }, + new List { targetBlock }); + targetBlock.Predecessors.Add(b); + } + impl.Blocks.Add(b); + } + if (debugWriter != null) { + codeExpr.Emit(debugWriter, 1, false); + } + return; + } + + private static void AddAsPrefix(Block b, List cs) { + Contract.Requires(b != null); + Contract.Requires(cs != null); + List newCommands = new List(); + newCommands.AddRange(cs); + newCommands.AddRange(b.Cmds); + b.Cmds = newCommands; + } + + + /// + /// Modifies an implementation by prepending it with startCmds and then, as assume + /// statements, all preconditions. Insert new blocks as needed, and adjust impl.Blocks[0] + /// accordingly to make it the new implementation entry block. + /// + /// + /// + protected static void InjectPreconditions(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) { + debugWriter = new TokenTextWriter("", Console.Out, /*setTokens=*/ false, /*pretty=*/ false); + debugWriter.WriteLine("Effective precondition:"); + } + + Substitution formalProcImplSubst = Substituter.SubstitutionFromHashtable(impl.GetImplFormalMap()); + string blockLabel = "PreconditionGeneratedEntry"; + + Block origStartBlock = impl.Blocks[0]; + Block insertionPoint = new Block( + new Token(-17, -4), blockLabel, startCmds, + new GotoCmd(Token.NoToken, new List { origStartBlock.Label }, new List { origStartBlock })); + + impl.Blocks[0] = insertionPoint; // make insertionPoint the start block + impl.Blocks.Add(origStartBlock); // and put the previous start block at the end of the list + + // (free and checked) requires clauses + foreach (Requires req in impl.Proc.Requires) + // invariant: insertionPoint.TransferCmd is "goto origStartBlock;", but origStartBlock.Predecessors has not yet been updated + { + Contract.Assert(req != null); + Expr e = Substituter.Apply(formalProcImplSubst, req.Condition); + Cmd c = new AssumeCmd(req.tok, e); + c.IrrelevantForChecksumComputation = true; + insertionPoint.Cmds.Add(c); + if (debugWriter != null) { + c.Emit(debugWriter, 1); + } + } + origStartBlock.Predecessors.Add(insertionPoint); + + if (impl.ExplicitAssumptionAboutCachedPrecondition != null) + { + insertionPoint.Cmds.Add(impl.ExplicitAssumptionAboutCachedPrecondition); + } + + if (debugWriter != null) { + debugWriter.WriteLine(); + } + } + /// + /// Modifies an implementation by inserting all postconditions + /// as assert statements at the end of the implementation + /// Returns the possibly-new unified exit block of the implementation + /// + /// + /// The unified exit block that has + /// already been constructed for the implementation (and so + /// is already an element of impl.Blocks) + /// + protected static void InjectPostConditions(Implementation impl, Block unifiedExitBlock, Dictionary gotoCmdOrigins) { + Contract.Requires(impl != null); + Contract.Requires(unifiedExitBlock != null); + Contract.Requires(gotoCmdOrigins != null); + Contract.Requires(impl.Proc != null); + Contract.Requires(unifiedExitBlock.TransferCmd is ReturnCmd); + + TokenTextWriter debugWriter = null; + if (CommandLineOptions.Clo.PrintWithUniqueASTIds) { + debugWriter = new TokenTextWriter("", Console.Out, /*setTokens=*/ false, /*pretty=*/ false); + debugWriter.WriteLine("Effective postcondition:"); + } + + Substitution formalProcImplSubst = Substituter.SubstitutionFromHashtable(impl.GetImplFormalMap()); + + // (free and checked) ensures clauses + foreach (Ensures ens in impl.Proc.Ensures) { + Contract.Assert(ens != null); + if (!ens.Free) { // skip free ensures clauses + Expr e = Substituter.Apply(formalProcImplSubst, ens.Condition); + Ensures ensCopy = (Ensures)cce.NonNull(ens.Clone()); + ensCopy.Condition = e; + AssertEnsuresCmd c = new AssertEnsuresCmd(ensCopy); + c.ErrorDataEnhanced = ensCopy.ErrorDataEnhanced; + unifiedExitBlock.Cmds.Add(c); + if (debugWriter != null) { + c.Emit(debugWriter, 1); + } + } + } + + if (debugWriter != null) { + debugWriter.WriteLine(); + } + } + + + /// + /// Get the pre-condition of an implementation, including the where clauses from the in-parameters. + /// + /// + protected static List GetPre(Implementation impl) { + Contract.Requires(impl != null); + Contract.Requires(impl.Proc != null); + Contract.Ensures(Contract.Result>() != null); + + + TokenTextWriter debugWriter = null; + if (CommandLineOptions.Clo.PrintWithUniqueASTIds) { + debugWriter = new TokenTextWriter("", Console.Out, /*setTokens=*/ false, /*pretty=*/ false); + debugWriter.WriteLine("Effective precondition:"); + } + + Substitution formalProcImplSubst = Substituter.SubstitutionFromHashtable(impl.GetImplFormalMap()); + List pre = new List(); + + // (free and checked) requires clauses + foreach (Requires req in impl.Proc.Requires) { + Contract.Assert(req != null); + Expr e = Substituter.Apply(formalProcImplSubst, req.Condition); + Contract.Assert(e != null); + Cmd c = new AssumeCmd(req.tok, e); + Contract.Assert(c != null); + pre.Add(c); + + if (debugWriter != null) { + c.Emit(debugWriter, 1); + } + } + + if (debugWriter != null) { + debugWriter.WriteLine(); + } + + return pre; + } + + /// + /// Get the post-condition of an implementation. + /// + /// + protected static List GetPost(Implementation impl) { + + + Contract.Requires(impl != null); + Contract.Requires(impl.Proc != null); + Contract.Ensures(Contract.Result>() != null); + if (CommandLineOptions.Clo.PrintWithUniqueASTIds) { + Console.WriteLine("Effective postcondition:"); + } + + // Construct an Expr for the post-condition + Substitution formalProcImplSubst = Substituter.SubstitutionFromHashtable(impl.GetImplFormalMap()); + List post = new List(); + foreach (Ensures ens in impl.Proc.Ensures) { + Contract.Assert(ens != null); + if (!ens.Free) { + Expr e = Substituter.Apply(formalProcImplSubst, ens.Condition); + Contract.Assert(e != null); + Ensures ensCopy = cce.NonNull((Ensures)ens.Clone()); + ensCopy.Condition = e; + Cmd c = new AssertEnsuresCmd(ensCopy); + ((AssertEnsuresCmd)c).ErrorDataEnhanced = ensCopy.ErrorDataEnhanced; + post.Add(c); + + if (CommandLineOptions.Clo.PrintWithUniqueASTIds) { + c.Emit(new TokenTextWriter("", Console.Out, /*setTokens=*/ false, /*pretty=*/ false), 1); + } + } + } + + if (CommandLineOptions.Clo.PrintWithUniqueASTIds) { + Console.WriteLine(); + } + + return post; + } + + /// + /// Get the where clauses from the in- and out-parameters as + /// a sequence of assume commands. + /// As a side effect, this method adds these where clauses to the out parameters. + /// + /// + protected static List GetParamWhereClauses(Implementation impl) { + Contract.Requires(impl != null); + Contract.Requires(impl.Proc != null); + Contract.Ensures(Contract.Result>() != null); + TokenTextWriter debugWriter = null; + if (CommandLineOptions.Clo.PrintWithUniqueASTIds) { + debugWriter = new TokenTextWriter("", Console.Out, /*setTokens=*/ false, /*pretty=*/ false); + debugWriter.WriteLine("Effective precondition from where-clauses:"); + } + + Substitution formalProcImplSubst = Substituter.SubstitutionFromHashtable(impl.GetImplFormalMap()); + List whereClauses = new List(); + + // where clauses of in-parameters + foreach (Formal f in impl.Proc.InParams) { + Contract.Assert(f != null); + if (f.TypedIdent.WhereExpr != null) { + Expr e = Substituter.Apply(formalProcImplSubst, f.TypedIdent.WhereExpr); + Cmd c = new AssumeCmd(f.tok, e); + whereClauses.Add(c); + + if (debugWriter != null) { + c.Emit(debugWriter, 1); + } + } + } + + // where clauses of out-parameters + Contract.Assert(impl.OutParams.Count == impl.Proc.OutParams.Count); + for (int i = 0; i < impl.OutParams.Count; i++) { + Variable f = cce.NonNull(impl.Proc.OutParams[i]); + if (f.TypedIdent.WhereExpr != null) { + Expr e = Substituter.Apply(formalProcImplSubst, f.TypedIdent.WhereExpr); + Cmd c = new AssumeCmd(f.tok, e); + whereClauses.Add(c); + + Variable fi = cce.NonNull(impl.OutParams[i]); + Contract.Assume(fi.TypedIdent.WhereExpr == null); + fi.TypedIdent.WhereExpr = e; + + if (debugWriter != null) { + c.Emit(debugWriter, 1); + } + } + } + + if (debugWriter != null) { + debugWriter.WriteLine(); + } + + return whereClauses; + } + + protected static void RestoreParamWhereClauses(Implementation impl) { + Contract.Requires(impl != null); + // We no longer need the where clauses on the out parameters, so we remove them to restore the situation from before VC generation + foreach (Formal f in impl.OutParams) { + Contract.Assert(f != null); + f.TypedIdent.WhereExpr = null; + } + } + #endregion + + + protected Checker FindCheckerFor(int timeout, bool isBlocking = true, int waitTimeinMs = 50, int maxRetries = 3) + { + Contract.Requires(0 <= waitTimeinMs && 0 <= maxRetries); + Contract.Ensures(!isBlocking || Contract.Result() != null); + + lock (checkers) + { + retry: + // Look for existing checker. + for (int i = 0; i < checkers.Count; i++) + { + var c = checkers[i]; + if (Monitor.TryEnter(c)) + { + try + { + if (c.WillingToHandle(timeout, program)) + { + c.GetReady(); + return c; + } + else if (c.IsIdle || c.IsClosed) + { + if (c.IsIdle) + { + c.Retarget(program, c.TheoremProver.Context, timeout); + c.GetReady(); + return c; + } + else + { + checkers.RemoveAt(i); + i--; + continue; + } + } + } + finally + { + Monitor.Exit(c); + } + } + } + + if (Cores <= checkers.Count) + { + if (isBlocking || 0 < maxRetries) + { + if (0 < waitTimeinMs) + { + Monitor.Wait(checkers, waitTimeinMs); + } + maxRetries--; + goto retry; + } + else + { + return null; + } + } + + // Create a new checker. + string log = logFilePath; + if (log != null && !log.Contains("@PROC@") && checkers.Count > 0) + { + log = log + "." + checkers.Count; + } + Checker ch = new Checker(this, program, log, appendLogFile, timeout); + ch.GetReady(); + checkers.Add(ch); + return ch; + } + } + + + virtual public void Close() { + } + + + public class CounterexampleCollector : VerifierCallback { + [ContractInvariantMethod] + void ObjectInvariant() { + Contract.Invariant(cce.NonNullElements(examples)); + } + + public string RequestId; + + public readonly List/*!>!*/ examples = new List(); + public override void OnCounterexample(Counterexample ce, string/*?*/ reason) { + //Contract.Requires(ce != null); + if (RequestId != null) + { + ce.RequestId = RequestId; + } + if (ce.OriginalRequestId == null && 1 < CommandLineOptions.Clo.VerifySnapshots) + { + ce.OriginalRequestId = RequestId; + } + examples.Add(ce); + } + + public override void OnUnreachableCode(Implementation impl) { + //Contract.Requires(impl != null); + System.Console.WriteLine("found unreachable code:"); + EmitImpl(impl, false); + // TODO report error about next to last in seq + } + } + + protected static void EmitImpl(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; + impl.Emit(new TokenTextWriter("", Console.Out, /*setTokens=*/ false, /*pretty=*/ false), 0); + CommandLineOptions.Clo.PrintDesugarings = oldPrintDesugaringSetting; + CommandLineOptions.Clo.PrintUnstructured = oldPrintUnstructured; + } + + + protected Block GenerateUnifiedExit(Implementation impl, Dictionary gotoCmdOrigins) { + Contract.Requires(impl != null); + Contract.Requires(gotoCmdOrigins != null); + Contract.Ensures(Contract.Result() != null); + + Contract.Ensures(Contract.Result().TransferCmd is ReturnCmd); + Block/*?*/ exitBlock = null; + #region Create a unified exit block, if there's more than one + { + int returnBlocks = 0; + foreach (Block b in impl.Blocks) { + if (b.TransferCmd is ReturnCmd) { + exitBlock = b; + returnBlocks++; + } + } + if (returnBlocks > 1) { + string unifiedExitLabel = "GeneratedUnifiedExit"; + Block unifiedExit = new Block(new Token(-17, -4), unifiedExitLabel, new List(), new ReturnCmd(Token.NoToken)); + Contract.Assert(unifiedExit != null); + foreach (Block b in impl.Blocks) { + if (b.TransferCmd is ReturnCmd) { + List labels = new List(); + labels.Add(unifiedExitLabel); + List bs = new List(); + bs.Add(unifiedExit); + GotoCmd go = new GotoCmd(Token.NoToken, labels, bs); + gotoCmdOrigins[go] = (ReturnCmd)b.TransferCmd; + b.TransferCmd = go; + unifiedExit.Predecessors.Add(b); + } + } + + exitBlock = unifiedExit; + impl.Blocks.Add(unifiedExit); + } + Contract.Assert(exitBlock != null); + } + return exitBlock; + #endregion + } + + protected static void ResetPredecessors(List blocks) { + Contract.Requires(blocks != null); + foreach (Block b in blocks) { + Contract.Assert(b != null); + b.Predecessors = new List(); + } + foreach (Block b in blocks) { + Contract.Assert(b != null); + foreach (Block ch in Exits(b)) { + Contract.Assert(ch != null); + ch.Predecessors.Add(b); + } + } + } + + protected static IEnumerable Exits(Block b) { + Contract.Requires(b != null); + GotoCmd g = b.TransferCmd as GotoCmd; + if (g != null) { + return cce.NonNull(g.labelTargets); + } + return new List(); + } + + protected Variable CreateIncarnation(Variable x, Absy a) { + Contract.Requires(this.variable2SequenceNumber != null); + Contract.Requires(this.CurrentLocalVariables != null); + Contract.Requires(a is Block || a is AssignCmd || a is HavocCmd); + + Contract.Requires(x != null); + Contract.Ensures(Contract.Result() != null); + + int currentIncarnationNumber = + variable2SequenceNumber.ContainsKey(x) + ? + variable2SequenceNumber[x] + : + -1; + Variable v = new Incarnation(x, currentIncarnationNumber + 1); + variable2SequenceNumber[x] = currentIncarnationNumber + 1; + CurrentLocalVariables.Add(v); + incarnationOriginMap.Add((Incarnation)v, a); + return v; + } + + /// + /// Compute the incarnation map at the beginning of block "b" from the incarnation blocks of the + /// predecessors of "b". + /// + /// The predecessor map b.map for block "b" is defined as follows: + /// b.map.Domain == Union{Block p in b.predecessors; p.map.Domain} + /// Forall{Variable v in b.map.Domain; + /// b.map[v] == (v in Intersection{Block p in b.predecessors; p.map}.Domain + /// ? b.predecessors[0].map[v] + /// : new Variable())} + /// Every variable that b.map maps to a fresh variable requires a fixup in all predecessor blocks. + /// + /// + /// Gives incarnation maps for b's predecessors. + /// + protected Dictionary ComputeIncarnationMap(Block b, Dictionary> block2Incarnation) { + Contract.Requires(b != null); + Contract.Requires(block2Incarnation != null); + Contract.Ensures(Contract.Result>() != null); + + if (b.Predecessors.Count == 0) { + return new Dictionary(); + } + + Dictionary incarnationMap = null; + Set /*Variable*/ fixUps = new Set /*Variable*/ (); + foreach (Block pred in b.Predecessors) { + Contract.Assert(pred != null); + Contract.Assert(block2Incarnation.ContainsKey(pred)); // otherwise, Passive Transformation found a block whose predecessors have not been processed yet + Dictionary predMap = (Dictionary)block2Incarnation[pred]; + Contract.Assert(predMap != null); + if (incarnationMap == null) { + incarnationMap = new Dictionary(predMap); + continue; + } + + ArrayList /*Variable*/ conflicts = new ArrayList /*Variable*/ (); + foreach (Variable v in incarnationMap.Keys) { + Contract.Assert(v != null); + if (!predMap.ContainsKey(v)) { + // conflict!! + conflicts.Add(v); + fixUps.Add(v); + } + } + // Now that we're done with enumeration, we'll do all the removes + foreach (Variable v in conflicts) { + Contract.Assert(v != null); + incarnationMap.Remove(v); + } + foreach (Variable v in predMap.Keys) { + Contract.Assert(v != null); + if (!incarnationMap.ContainsKey(v)) { + // v was not in the domain of the predecessors seen so far, so it needs to be fixed up + fixUps.Add(v); + } else { + // v in incarnationMap ==> all pred blocks (up to now) all agree on its incarnation + if (predMap[v] != incarnationMap[v]) { + // conflict!! + incarnationMap.Remove(v); + fixUps.Add(v); + } + } + } + } + + #region Second, for all variables in the fixups list, introduce a new incarnation and push it back into the preds. + foreach (Variable v in fixUps) { + Contract.Assert(v != null); + if (!b.IsLive(v)) + continue; + Variable v_prime = CreateIncarnation(v, b); + IdentifierExpr ie = new IdentifierExpr(v_prime.tok, v_prime); + Contract.Assert(incarnationMap != null); + incarnationMap[v] = ie; + foreach (Block pred in b.Predecessors) { + Contract.Assert(pred != null); + #region Create an assume command equating v_prime with its last incarnation in pred + #region Create an identifier expression for the last incarnation in pred + Dictionary predMap = (Dictionary)cce.NonNull(block2Incarnation[pred]); + + Expr pred_incarnation_exp; + Expr o = predMap.ContainsKey(v) ? predMap[v] : null; + if (o == null) { + Variable predIncarnation = v; + IdentifierExpr ie2 = new IdentifierExpr(predIncarnation.tok, predIncarnation); + pred_incarnation_exp = ie2; + } else { + pred_incarnation_exp = o; + } + #endregion + #region Create an identifier expression for the new incarnation + IdentifierExpr v_prime_exp = new IdentifierExpr(v_prime.tok, v_prime); + #endregion + #region Create the assume command itself + AssumeCmd ac = new AssumeCmd(v.tok, TypedExprEq(v_prime_exp, pred_incarnation_exp, v_prime.Name.Contains("a##cached##"))); + pred.Cmds.Add(ac); + #endregion + #endregion + } + } + #endregion + + Contract.Assert(incarnationMap != null); + return incarnationMap; + } + + Dictionary 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, Substitution oldFrameSubst, MutableVariableCollector variableCollector, byte[] currentChecksum = null) { + Contract.Requires(b != null); + Contract.Requires(incarnationMap != null); + Contract.Requires(mvInfo != null); + Contract.Requires(oldFrameSubst != null); + #region Walk forward over the commands in this block and convert them to passive commands + + List passiveCmds = new List(); + foreach (Cmd c in b.Cmds) { + 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); + variableCollector.Visit(c); + currentChecksum = c.Checksum; + TurnIntoPassiveCmd(c, incarnationMap, oldFrameSubst, passiveCmds, mvInfo, b); + } + b.Checksum = currentChecksum; + b.Cmds = passiveCmds; + + if (b.TransferCmd is ReturnExprCmd) { + ReturnExprCmd rec = (ReturnExprCmd)b.TransferCmd.Clone(); + Substitution incarnationSubst = Substituter.SubstitutionFromHashtable(incarnationMap); + rec.Expr = Substituter.ApplyReplacingOldExprs(incarnationSubst, oldFrameSubst, rec.Expr); + b.TransferCmd = rec; + } + #endregion + } + + protected Dictionary Convert2PassiveCmd(Implementation impl, ModelViewInfo mvInfo) { + Contract.Requires(impl != null); + Contract.Requires(mvInfo != null); + + currentImplementation = impl; + + var start = DateTime.UtcNow; + + Dictionary r = ConvertBlocks2PassiveCmd(impl.Blocks, impl.Proc.Modifies, mvInfo); + + var end = DateTime.UtcNow; + + if (CommandLineOptions.Clo.TraceCachingForDebugging) + { + Console.Out.WriteLine("Turned implementation into passive commands within {0:F0} ms.\n", end.Subtract(start).TotalMilliseconds); + } + + if (CommandLineOptions.Clo.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; + impl.Emit(tokTxtWr, 0); + CommandLineOptions.Clo.PrintDesugarings = pd; + CommandLineOptions.Clo.PrintUnstructured = pu; + } + } + + currentImplementation = null; + + RestoreParamWhereClauses(impl); + + #region Debug Tracing + if (CommandLineOptions.Clo.TraceVerify) { + Console.WriteLine("after conversion to passive commands"); + EmitImpl(impl, true); + } + #endregion + + return r; + } + + protected Dictionary ConvertBlocks2PassiveCmd(List blocks, List modifies, ModelViewInfo mvInfo) { + Contract.Requires(blocks != null); + Contract.Requires(modifies != null); + Contract.Requires(mvInfo != null); + #region Convert to Passive Commands + + #region Topological sort -- need to process in a linearization of the partial order + Graph dag = new Graph(); + dag.AddSource(cce.NonNull(blocks[0])); // there is always at least one node in the graph + foreach (Block b in blocks) { + GotoCmd gtc = b.TransferCmd as GotoCmd; + if (gtc != null) { + Contract.Assume(gtc.labelTargets != null); + foreach (Block dest in gtc.labelTargets) { + Contract.Assert(dest != null); + dag.AddEdge(b, dest); + } + } + } + + IEnumerable sortedNodes; + if (CommandLineOptions.Clo.ModifyTopologicalSorting) { + sortedNodes = dag.TopologicalSort(true); + } else { + sortedNodes = dag.TopologicalSort(); + } + + Contract.Assert(sortedNodes != null); + #endregion + + Substitution oldFrameSubst = ComputeOldExpressionSubstitution(modifies); + + // Now we can process the nodes in an order so that we're guaranteed to have + // processed all of a node's predecessors before we process the node. + Dictionary> block2Incarnation = new Dictionary>(); + Block exitBlock = null; + Dictionary exitIncarnationMap = null; + var variableCollectors = new Dictionary(); + foreach (Block b in sortedNodes) { + Contract.Assert(b != null); + Contract.Assert(!block2Incarnation.ContainsKey(b)); + Dictionary incarnationMap = ComputeIncarnationMap(b, block2Incarnation); + + // b.liveVarsBefore has served its purpose in the just-finished call to ComputeIncarnationMap; null it out. + b.liveVarsBefore = null; + + // Decrement the succCount field in each predecessor. Once the field reaches zero in any block, + // all its successors have been passified. Consequently, its entry in block2Incarnation can be removed. + byte[] currentChecksum = null; + var mvc = new MutableVariableCollector(); + variableCollectors[b] = mvc; + foreach (Block p in b.Predecessors) { + p.succCount--; + if (p.Checksum != null) + { + // Compute the checksum based on the checksums of the predecessor. The order should not matter. + currentChecksum = ChecksumHelper.CombineChecksums(p.Checksum, currentChecksum, true); + } + mvc.AddUsedVariables(variableCollectors[p].UsedVariables); + if (p.succCount == 0) + block2Incarnation.Remove(p); + } + + #region Each block's map needs to be available to successor blocks + GotoCmd gotoCmd = b.TransferCmd as GotoCmd; + if (gotoCmd == null) { + b.succCount = 0; + } + else { + // incarnationMap needs to be added only if there is some successor of b + b.succCount = gotoCmd.labelNames.Count; + block2Incarnation.Add(b, incarnationMap); + } + #endregion Each block's map needs to be available to successor blocks + + TurnIntoPassiveBlock(b, incarnationMap, mvInfo, oldFrameSubst, mvc, currentChecksum); + exitBlock = b; + exitIncarnationMap = incarnationMap; + } + + variableCollectors.Clear(); + + // Verify that exitBlock is indeed the unique exit block + Contract.Assert(exitBlock != null); + Contract.Assert(exitBlock.TransferCmd is ReturnCmd); + #endregion Convert to Passive Commands + + return exitIncarnationMap; + } + + /// + /// Compute the substitution for old expressions. + /// + protected static Substitution ComputeOldExpressionSubstitution(List modifies) + { + Dictionary oldFrameMap = new Dictionary(); + foreach (IdentifierExpr ie in modifies) + { + Contract.Assert(ie != null); + if (!oldFrameMap.ContainsKey(cce.NonNull(ie.Decl))) + oldFrameMap.Add(ie.Decl, ie); + } + return Substituter.SubstitutionFromHashtable(oldFrameMap); + } + + public enum CachingAction : byte + { + DoNothingToAssert, + MarkAsPartiallyVerified, + MarkAsFullyVerified, + RecycleError, + AssumeNegationOfAssumptionVariable, + DropAssume + } + + public long[] CachingActionCounts; + + void TraceCachingAction(Cmd cmd, CachingAction action) + { + if (CommandLineOptions.Clo.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) : ""; + Console.Write("Processing command (at {0}) ", loc); + cmd.Emit(tokTxtWr, 0); + Console.Out.WriteLine(" >>> {0}", action); + } + } + + if (CommandLineOptions.Clo.TraceCachingForBenchmarking && CachingActionCounts != null) + { + Interlocked.Increment(ref CachingActionCounts[(int)action]); + } + } + + /// + /// Turn a command into a passive command, and it remembers the previous step, to see if it is a havoc or not. In the 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, Dictionary incarnationMap, Substitution oldFrameSubst, List passiveCmds, ModelViewInfo mvInfo, Block containingBlock) { + Contract.Requires(c != null); + Contract.Requires(incarnationMap != null); + Contract.Requires(oldFrameSubst != null); + Contract.Requires(passiveCmds != null); + Contract.Requires(mvInfo != null); + Contract.Requires(containingBlock != null); + + Substitution incarnationSubst = Substituter.SubstitutionFromHashtable(incarnationMap); + #region assert/assume P |--> assert/assume P[x := in(x)], out := in + if (c is PredicateCmd) { + Contract.Assert(c is AssertCmd || c is AssumeCmd); // otherwise, unexpected PredicateCmd type + + PredicateCmd pc = (PredicateCmd)c.Clone(); + Contract.Assert(pc != null); + + Expr copy = Substituter.ApplyReplacingOldExprs(incarnationSubst, oldFrameSubst, pc.Expr); + if (CommandLineOptions.Clo.ModelViewFile != null && pc is AssumeCmd) { + string description = QKeyValue.FindStringAttribute(pc.Attributes, "captureState"); + if (description != null) { + Expr mv = new NAryExpr(pc.tok, new FunctionCall(ModelViewInfo.MVState_FunctionDef), new List { Bpl.Expr.Ident(ModelViewInfo.MVState_ConstantDef), Bpl.Expr.Literal(mvInfo.CapturePoints.Count) }); + copy = Bpl.Expr.And(mv, copy); + mvInfo.CapturePoints.Add(new ModelViewInfo.Mapping(description, new Dictionary(incarnationMap))); + } + } + Contract.Assert(copy != null); + var dropCmd = false; + var relevantAssumpVars = currentImplementation != null ? currentImplementation.RelevantInjectedAssumptionVariables(incarnationMap) : new List(); + var relevantDoomedAssumpVars = currentImplementation != null ? currentImplementation.RelevantDoomedInjectedAssumptionVariables(incarnationMap) : new List(); + var checksum = pc.Checksum; + if (pc is AssertCmd) { + var ac = (AssertCmd)pc; + ac.OrigExpr = ac.Expr; + Contract.Assert(ac.IncarnationMap == null); + ac.IncarnationMap = (Dictionary)cce.NonNull(new Dictionary(incarnationMap)); + + var subsumption = Wlp.Subsumption(ac); + if (relevantDoomedAssumpVars.Any()) + { + TraceCachingAction(pc, CachingAction.DoNothingToAssert); + } + else if (currentImplementation != null + && currentImplementation.HasCachedSnapshot + && checksum != null + && currentImplementation.IsAssertionChecksumInCachedSnapshot(checksum) + && !currentImplementation.IsErrorChecksumInCachedSnapshot(checksum)) + { + if (!currentImplementation.AnyErrorsInCachedSnapshot + && currentImplementation.InjectedAssumptionVariables.Count == 1 + && relevantAssumpVars.Count == 1) + { + TraceCachingAction(pc, CachingAction.MarkAsPartiallyVerified); + } + else + { + bool isTrue; + var assmVars = currentImplementation.ConjunctionOfInjectedAssumptionVariables(incarnationMap, out isTrue); + TraceCachingAction(pc, !isTrue ? CachingAction.MarkAsPartiallyVerified : CachingAction.MarkAsFullyVerified); + var litExpr = ac.Expr as LiteralExpr; + if (litExpr == null || !litExpr.IsTrue) + { + ac.MarkAsVerifiedUnder(assmVars); + } + else + { + dropCmd = true; + } + } + } + else if (currentImplementation != null + && currentImplementation.HasCachedSnapshot + && relevantAssumpVars.Count == 0 + && checksum != null + && currentImplementation.IsAssertionChecksumInCachedSnapshot(checksum) + && currentImplementation.IsErrorChecksumInCachedSnapshot(checksum)) + { + TraceCachingAction(pc, CachingAction.RecycleError); + ac.MarkAsVerifiedUnder(Expr.True); + currentImplementation.AddRecycledFailingAssertion(ac); + pc.Attributes = new QKeyValue(Token.NoToken, "recycled_failing_assertion", new List(), pc.Attributes); + } + else + { + TraceCachingAction(pc, CachingAction.DoNothingToAssert); + } + } + else if (pc is AssumeCmd + && QKeyValue.FindBoolAttribute(pc.Attributes, "precondition_previous_snapshot") + && pc.SugaredCmdChecksum != null) + { + if (!relevantDoomedAssumpVars.Any() + && currentImplementation.HasCachedSnapshot + && currentImplementation.IsAssertionChecksumInCachedSnapshot(pc.SugaredCmdChecksum) + && !currentImplementation.IsErrorChecksumInCachedSnapshot(pc.SugaredCmdChecksum)) + { + bool isTrue; + var assmVars = currentImplementation.ConjunctionOfInjectedAssumptionVariables(incarnationMap, out isTrue); + if (!isTrue) + { + copy = LiteralExpr.Imp(assmVars, copy); + TraceCachingAction(pc, CachingAction.MarkAsPartiallyVerified); + } + else + { + TraceCachingAction(pc, CachingAction.MarkAsFullyVerified); + } + } + else + { + TraceCachingAction(pc, CachingAction.DropAssume); + dropCmd = true; + } + } + else if (pc is AssumeCmd && QKeyValue.FindBoolAttribute(pc.Attributes, "assumption_variable_initialization")) + { + var identExpr = pc.Expr as IdentifierExpr; + if (identExpr != null && identExpr.Decl != null && !incarnationMap.ContainsKey(identExpr.Decl)) + { + incarnationMap[identExpr.Decl] = LiteralExpr.True; + dropCmd = true; + } + } + pc.Expr = copy; + if (!dropCmd) + { + passiveCmds.Add(pc); + } + } + #endregion + #region x1 := E1, x2 := E2, ... |--> assume x1' = E1[in] & x2' = E2[in], out := in( x |-> x' ) [except as noted below] + else if (c is AssignCmd) { + AssignCmd assign = ((AssignCmd)c).AsSimpleAssignCmd; // first remove map assignments + Contract.Assert(assign != null); + #region Substitute all variables in E with the current map + List copies = new List(); + foreach (Expr e in assign.Rhss) { + Contract.Assert(e != null); + copies.Add(Substituter.ApplyReplacingOldExprs(incarnationSubst, + oldFrameSubst, + e)); + } + #endregion + + List!*/> assumptions = new List(); + // it might be too slow to create a new dictionary each time ... + IDictionary newIncarnationMappings = + new Dictionary(); + + for (int i = 0; i < assign.Lhss.Count; ++i) { + IdentifierExpr lhsIdExpr = + cce.NonNull((SimpleAssignLhs)assign.Lhss[i]).AssignedVariable; + Variable lhs = cce.NonNull(lhsIdExpr.Decl); + Contract.Assert(lhs != null); + Expr rhs = assign.Rhss[i]; + Contract.Assert(rhs != null); + + // don't create incarnations for assignments of literals or single variables. + if (rhs is LiteralExpr) { + incarnationMap[lhs] = rhs; + } else if (rhs is IdentifierExpr) { + IdentifierExpr ie = (IdentifierExpr)rhs; + if (incarnationMap.ContainsKey(cce.NonNull(ie.Decl))) + newIncarnationMappings[lhs] = cce.NonNull((Expr)incarnationMap[ie.Decl]); + else + newIncarnationMappings[lhs] = ie; + } else { + IdentifierExpr x_prime_exp = null; + #region Make a new incarnation, x', for variable x, but only if x is *not* already an incarnation + if (lhs is Incarnation) { + // incarnations are already written only once, no need to make an incarnation of an incarnation + x_prime_exp = lhsIdExpr; + } else { + Variable v = CreateIncarnation(lhs, c); + x_prime_exp = new IdentifierExpr(lhsIdExpr.tok, v); + newIncarnationMappings[lhs] = x_prime_exp; + } + #endregion + + var nAryExpr = copies[i] as NAryExpr; + if (nAryExpr != null) + { + var binOp = nAryExpr.Fun as BinaryOperator; + if (binOp != null + && binOp.Op == BinaryOperator.Opcode.And) + { + var arg0 = nAryExpr.Args[0] as LiteralExpr; + var arg1 = nAryExpr.Args[1] as LiteralExpr; + if ((arg0 != null && arg0.IsTrue) || (arg1 != null && arg1.IsFalse)) + { + // Replace the expressions "true && arg1" or "arg0 && false" by "arg1". + copies[i] = nAryExpr.Args[1]; + } + } + } + + #region Create an assume command with the new variable + assumptions.Add(TypedExprEq(x_prime_exp, copies[i], x_prime_exp.Decl != null && x_prime_exp.Decl.Name.Contains("a##cached##"))); + #endregion + } + } + + foreach (KeyValuePair pair in newIncarnationMappings) { + Contract.Assert(pair.Key != null && pair.Value != null); + incarnationMap[pair.Key] = pair.Value; + } + + if (assumptions.Count > 0) { + Expr assumption = assumptions[0]; + + for (int i = 1; i < assumptions.Count; ++i) { + Contract.Assert(assumption != null); + assumption = Expr.And(assumption, assumptions[i]); + } + passiveCmds.Add(new AssumeCmd(c.tok, assumption)); + } + + if (currentImplementation != null + && currentImplementation.HasCachedSnapshot + && !currentImplementation.AnyErrorsInCachedSnapshot + && currentImplementation.DoomedInjectedAssumptionVariables.Count == 0 + && currentImplementation.InjectedAssumptionVariables.Count == 1 + && assign.Lhss.Count == 1) + { + var identExpr = assign.Lhss[0].AsExpr as IdentifierExpr; + Expr incarnation; + if (identExpr != null && identExpr.Decl != null && QKeyValue.FindBoolAttribute(identExpr.Decl.Attributes, "assumption") && incarnationMap.TryGetValue(identExpr.Decl, out incarnation)) + { + TraceCachingAction(assign, CachingAction.AssumeNegationOfAssumptionVariable); + passiveCmds.Add(new AssumeCmd(c.tok, Expr.Not(incarnation))); + } + } + } + #endregion + #region havoc w |--> assume whereClauses, out := in( w |-> w' ) + else if (c is HavocCmd) { + if (this.preHavocIncarnationMap == null) // Save a copy of the incarnation map (at the top of a sequence of havoc statements) + this.preHavocIncarnationMap = new Dictionary(incarnationMap); + + HavocCmd hc = (HavocCmd)c; + Contract.Assert(c != null); + // If an assumption variable for postconditions is included here, it must have been assigned within a loop. + // We do not need to havoc it if we have performed a modular proof of the loop (i.e., using only the loop + // invariant) in the previous snapshot and, consequently, the corresponding assumption did not affect the + // anything after the loop. We can achieve this by simply not updating/adding it in the incarnation map. + List havocVars = hc.Vars.Where(v => !(QKeyValue.FindBoolAttribute(v.Decl.Attributes, "assumption") && v.Decl.Name.StartsWith("a##cached##"))).ToList(); + // First, compute the new incarnations + foreach (IdentifierExpr ie in havocVars) { + Contract.Assert(ie != null); + if (!(ie.Decl is Incarnation)) { + Variable x = cce.NonNull(ie.Decl); + Variable x_prime = CreateIncarnation(x, c); + incarnationMap[x] = new IdentifierExpr(x_prime.tok, x_prime); + } + } + // Then, perform the assume of the where clauses, using the updated incarnations + Substitution updatedIncarnationSubst = Substituter.SubstitutionFromHashtable(incarnationMap); + foreach (IdentifierExpr ie in havocVars) { + Contract.Assert(ie != null); + if (!(ie.Decl is Incarnation)) { + Variable x = cce.NonNull(ie.Decl); + Bpl.Expr w = x.TypedIdent.WhereExpr; + if (w != null) { + Expr copy = Substituter.ApplyReplacingOldExprs(updatedIncarnationSubst, oldFrameSubst, w); + passiveCmds.Add(new AssumeCmd(c.tok, copy)); + } + } + } + + // Add the following assume-statement for each assumption variable 'v', where 'v_post' is the new incarnation and 'v_pre' is the old one: + // assume v_post ==> v_pre; + foreach (IdentifierExpr ie in havocVars) + { + if (QKeyValue.FindBoolAttribute(ie.Decl.Attributes, "assumption")) + { + var preInc = (Expr)(preHavocIncarnationMap[ie.Decl].Clone()); + var postInc = (Expr)(incarnationMap[ie.Decl].Clone()); + passiveCmds.Add(new AssumeCmd(c.tok, Expr.Imp(postInc, preInc))); + } + } + } + #endregion + else if (c is CommentCmd) { + // comments are just for debugging and don't affect verification + } else if (c is SugaredCmd) { + SugaredCmd sug = (SugaredCmd)c; + Contract.Assert(sug != null); + Cmd cmd = sug.Desugaring; + Contract.Assert(cmd != null); + TurnIntoPassiveCmd(cmd, incarnationMap, oldFrameSubst, passiveCmds, mvInfo, containingBlock); + } else if (c is StateCmd) { + this.preHavocIncarnationMap = null; // we do not need to remeber the previous incarnations + StateCmd st = (StateCmd)c; + Contract.Assert(st != null); + // account for any where clauses among the local variables + foreach (Variable v in st.Locals) { + Contract.Assert(v != null); + Expr w = v.TypedIdent.WhereExpr; + if (w != null) { + passiveCmds.Add(new AssumeCmd(v.tok, w)); + } + } + // do the sub-commands + foreach (Cmd s in st.Cmds) { + Contract.Assert(s != null); + TurnIntoPassiveCmd(s, incarnationMap, oldFrameSubst, passiveCmds, mvInfo, containingBlock); + } + // remove the local variables from the incarnation map + foreach (Variable v in st.Locals) { + Contract.Assert(v != null); + incarnationMap.Remove(v); + } + } + #region There shouldn't be any other types of commands at this point + else { + Debug.Fail("Internal Error: Passive transformation handed a command that is not one of assert,assume,havoc,assign."); + } + #endregion + + + #region We remember if we have put an havoc statement into a passive form + + if (!(c is HavocCmd)) + this.preHavocIncarnationMap = null; + // else: it has already been set by the case for the HavocCmd + #endregion + } + + NAryExpr TypedExprEq(Expr e0, Expr e1, bool doNotResolveOverloading = false) { + Contract.Requires(e0 != null); + Contract.Requires(e1 != null); + NAryExpr e = Expr.Eq(e0, e1); + var fun = e.Fun as IOverloadedAppliable; + if (fun != null) + { + fun.DoNotResolveOverloading = doNotResolveOverloading; + } + e.Type = Bpl.Type.Bool; + e.TypeParameters = SimpleTypeParamInstantiation.EMPTY; + return e; + } + + /// + /// Creates a new block to add to impl.Blocks, where impl is the implementation that contains + /// succ. Caller must do the add to impl.Blocks. + /// + protected Block CreateBlockBetween(int predIndex, Block succ) { + Contract.Requires(0 <= predIndex && predIndex < succ.Predecessors.Count); + + + Contract.Requires(succ != null); + Contract.Ensures(Contract.Result() != null); + + Block pred = cce.NonNull(succ.Predecessors[predIndex]); + + string newBlockLabel = pred.Label + "_@2_" + succ.Label; + + // successor of newBlock list + List ls = new List(); + ls.Add(succ.Label); + List bs = new List(); + bs.Add(succ); + + Block newBlock = new Block( + new Token(-17, -4), + newBlockLabel, + new List(), + new GotoCmd(Token.NoToken, ls, bs) + ); + + // predecessors of newBlock + List ps = new List(); + ps.Add(pred); + newBlock.Predecessors = ps; + + // fix successors of pred + #region Change the edge "pred->succ" to "pred->newBlock" + GotoCmd gtc = (GotoCmd)cce.NonNull(pred.TransferCmd); + Contract.Assume(gtc.labelTargets != null); + Contract.Assume(gtc.labelNames != null); + for (int i = 0, n = gtc.labelTargets.Count; i < n; i++) { + if (gtc.labelTargets[i] == succ) { + gtc.labelTargets[i] = newBlock; + gtc.labelNames[i] = newBlockLabel; + break; + } + } + #endregion Change the edge "pred->succ" to "pred->newBlock" + + // fix predecessors of succ + succ.Predecessors[predIndex] = newBlock; + + return newBlock; + } + + protected void AddBlocksBetween(List blocks) { + Contract.Requires(blocks != null); + #region Introduce empty blocks between join points and their multi-successor predecessors + List tweens = new List(); + foreach (Block b in blocks) { + int nPreds = b.Predecessors.Count; + if (nPreds > 1) { + // b is a join point (i.e., it has more than one predecessor) + for (int i = 0; i < nPreds; i++) { + GotoCmd gotocmd = (GotoCmd)(cce.NonNull(b.Predecessors[i]).TransferCmd); + if (gotocmd.labelNames != null && gotocmd.labelNames.Count > 1) { + tweens.Add(CreateBlockBetween(i, b)); + } + } + } + } + blocks.AddRange(tweens); // must wait until iteration is done before changing the list + #endregion + } + + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + Close(); + } + _disposed = true; + } + } + + public int Cores { get; set; } + } + + public class ModelViewInfo + { + public readonly List AllVariables = new List(); + public readonly List CapturePoints = new List(); + public static readonly Function MVState_FunctionDef = new Function(Token.NoToken, "$mv_state", + new List { new Formal(Token.NoToken, new TypedIdent(Token.NoToken, TypedIdent.NoName, Bpl.Type.Int), true), + new Formal(Token.NoToken, new TypedIdent(Token.NoToken, TypedIdent.NoName, Bpl.Type.Int), true) }, + new Formal(Token.NoToken, new TypedIdent(Token.NoToken, TypedIdent.NoName, Bpl.Type.Bool), false)); + public static readonly Constant MVState_ConstantDef = new Constant(Token.NoToken, new TypedIdent(Token.NoToken, "$mv_state_const", Bpl.Type.Int)); + + public ModelViewInfo(Program program, Implementation impl) { + Contract.Requires(program != null); + Contract.Requires(impl != null); + + // global variables + lock (program.TopLevelDeclarations) + { + foreach (var v in program.Variables) + { + if (!(v is Constant)) + { + AllVariables.Add(v); + } + } + } + // implementation parameters + foreach (Variable p in impl.InParams) { + AllVariables.Add(p); + } + foreach (Variable p in impl.OutParams) { + AllVariables.Add(p); + } + // implementation locals + foreach (Variable v in impl.LocVars) { + AllVariables.Add(v); + } + } + + public ModelViewInfo(CodeExpr codeExpr) { + Contract.Requires(codeExpr != null); + // TODO: also need all variables of enclosing scopes (the global variables of the program, the parameters + // and perhaps locals of the implementation (if any), any enclosing code expressions). + + foreach (Variable v in codeExpr.LocVars) { + AllVariables.Add(v); + } + } + + public class Mapping + { + public readonly string Description; + public readonly Dictionary IncarnationMap; + public Mapping(string description, Dictionary incarnationMap) { + Description = description; + IncarnationMap = incarnationMap; + } + } + } +} diff --git a/Source/VCGeneration/Context.cs b/Source/VCGeneration/Context.cs index ddc34976..3bd14e6b 100644 --- a/Source/VCGeneration/Context.cs +++ b/Source/VCGeneration/Context.cs @@ -1,256 +1,256 @@ -//----------------------------------------------------------------------------- -// -// Copyright (C) Microsoft Corporation. All Rights Reserved. -// -//----------------------------------------------------------------------------- -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -using System.Diagnostics.Contracts; -using Microsoft.Boogie.VCExprAST; - -namespace Microsoft.Boogie -{ - /// - /// The methods of this class are called in the following order: - /// DeclareType* - /// (DeclareConstant DeclareFunction)* - /// AddAxiom* - /// DeclareGlobalVariable* - /// At this time, all "attributes" are passed in as null. - /// - [ContractClass(typeof(ProverContextContracts))] - public abstract class ProverContext : ICloneable { - public int TimoutDiagnosticsCount { get; set; } - public readonly Dictionary> TimeoutDiagnosticIDToAssertion = new Dictionary>(); - protected virtual void ProcessDeclaration(Declaration decl) {Contract.Requires(decl != null);} - public virtual void DeclareType(TypeCtorDecl t, string attributes) {Contract.Requires(t != null); ProcessDeclaration(t); } - public virtual void DeclareConstant(Constant c, bool uniq, string attributes) {Contract.Requires(c != null); ProcessDeclaration(c); } - public virtual void DeclareFunction(Function f, string attributes) {Contract.Requires(f != null); ProcessDeclaration(f); } - public virtual void AddAxiom(Axiom a, string attributes) {Contract.Requires(a != null); ProcessDeclaration(a); } - public virtual void DeclareGlobalVariable(GlobalVariable v, string attributes) {Contract.Requires(v != null); ProcessDeclaration(v); } - public abstract void AddAxiom(VCExpr vc); - public abstract string Lookup(VCExprVar var); - public abstract VCExpressionGenerator ExprGen { get; } - public abstract Boogie2VCExprTranslator BoogieExprTranslator { get; } - public abstract VCGenerationOptions VCGenOptions { get; } - public abstract object Clone(); - public abstract void Reset(); - public abstract void Clear(); - } - -[ContractClassFor(typeof(ProverContext))] -public abstract class ProverContextContracts:ProverContext{ - public override void AddAxiom(VCExpr vc) { - } - public override void AddAxiom(Axiom a, string attributes) -{ -} - public override VCExpressionGenerator ExprGen -{ - get { Contract.Ensures(Contract.Result() != null); - throw new NotImplementedException(); } -} - public override Boogie2VCExprTranslator BoogieExprTranslator -{ - get { Contract.Ensures(Contract.Result() != null); - throw new NotImplementedException(); } -} - public override VCGenerationOptions VCGenOptions -{ - get {Contract.Ensures(Contract.Result() != null); - throw new NotImplementedException(); } -} - public override object Clone() -{ - Contract.Ensures(Contract.Result() != null); - throw new NotImplementedException(); -} -} - - // ----------------------------------------------------------------------------------------------- - // ----------------------------------------------------------------------------------------------- - // ----------------------------------------------------------------------------------------------- - - /// - /// This ProverContext subclass is intended for use with untyped provers that do not require names - /// to be declared before use. It constructs its context from unique constants and given axioms. - /// - public class DeclFreeProverContext : ProverContext { - protected VCExpressionGenerator gen; - protected VCGenerationOptions genOptions; - protected Boogie2VCExprTranslator translator; - - protected OrderingAxiomBuilder orderingAxiomBuilder; - - protected List distincts; - protected List axiomConjuncts; - - [ContractInvariantMethod] - void ObjectInvariant() { - Contract.Invariant(gen != null); - Contract.Invariant(genOptions != null); - Contract.Invariant(translator != null); - Contract.Invariant(orderingAxiomBuilder != null); - Contract.Invariant(cce.NonNullElements(distincts)); - Contract.Invariant(cce.NonNullElements(axiomConjuncts)); - } - - public VCExprTranslator/*?*/ exprTranslator; - - public DeclFreeProverContext(VCExpressionGenerator gen, - VCGenerationOptions genOptions) { - Contract.Requires(gen != null); - Contract.Requires(genOptions != null); - this.gen = gen; - this.genOptions = genOptions; - Boogie2VCExprTranslator t = new Boogie2VCExprTranslator (gen, genOptions); - this.translator = t; - - SetupOrderingAxiomBuilder(gen, t); - - distincts = new List(); - axiomConjuncts = new List(); - - exprTranslator = null; - } - - private void SetupOrderingAxiomBuilder(VCExpressionGenerator gen, Boogie2VCExprTranslator t) - { - OrderingAxiomBuilder oab = new OrderingAxiomBuilder(gen, t); - Contract.Assert(oab != null); - oab.Setup(); - this.orderingAxiomBuilder = oab; - } - - public override void Reset() - { - SetupOrderingAxiomBuilder(gen, translator); - distincts = new List(); - axiomConjuncts = new List(); - } - - public override void Clear() - { - distincts = new List(); - axiomConjuncts = new List(); - } - - protected DeclFreeProverContext(DeclFreeProverContext ctxt) { - Contract.Requires(ctxt != null); - this.gen = ctxt.gen; - this.genOptions = ctxt.genOptions; - Boogie2VCExprTranslator t = (Boogie2VCExprTranslator)ctxt.translator.Clone(); - Contract.Assert(t != null); - this.translator = t; - this.orderingAxiomBuilder = new OrderingAxiomBuilder(ctxt.gen, t, ctxt.orderingAxiomBuilder); - - StringBuilder cmds = new StringBuilder (); - - distincts = new List(ctxt.distincts); - axiomConjuncts = new List(ctxt.axiomConjuncts); - - if (ctxt.exprTranslator == null) - exprTranslator = null; - else - exprTranslator = (VCExprTranslator)cce.NonNull(ctxt.exprTranslator.Clone()); - } - - public override object Clone() { - Contract.Ensures(Contract.Result() != null); - - return new DeclFreeProverContext(this); - } - - public override void DeclareFunction(Function f, string attributes) {//Contract.Requires(f != null); - base.ProcessDeclaration(f); - } - - public override void DeclareConstant(Constant c, bool uniq, string attributes) {//Contract.Requires(c != null); - base.DeclareConstant(c, uniq, attributes); - orderingAxiomBuilder.AddConstant(c); - - // TODO: make separate distinct lists for names coming from different types - // e.g., one for strings, one for ints, one for program types. - if (uniq){ - distincts.Add(c); - } - } - - public override void AddAxiom(Axiom ax, string attributes) {//Contract.Requires(ax != null); - base.AddAxiom(ax, attributes); - - axiomConjuncts.Add(translator.Translate(ax.Expr)); - } - - public override void AddAxiom(VCExpr vc) - {//Contract.Requires(vc != null); - axiomConjuncts.Add(vc); - } - - public VCExpr Axioms { - get {Contract.Ensures(Contract.Result() != null); - VCExpr axioms = gen.NAry(VCExpressionGenerator.AndOp, axiomConjuncts); - List/*!>!*/ distinctVars = new List (); - foreach (Variable v in distincts){ - Contract.Assert(v != null); - distinctVars.Add(translator.LookupVariable(v));} - axioms = gen.AndSimp(gen.Distinct(distinctVars), axioms); - if (CommandLineOptions.Clo.TypeEncodingMethod != CommandLineOptions.TypeEncoding.Monomorphic) - axioms = gen.AndSimp(orderingAxiomBuilder.Axioms, axioms); - return axioms; - } - } - - public override string Lookup(VCExprVar var) - { - return exprTranslator.Lookup(var); - } - - public override VCExpressionGenerator ExprGen { get {Contract.Ensures(Contract.Result() != null); - - return gen; - } } - public override Boogie2VCExprTranslator BoogieExprTranslator { get {Contract.Ensures(Contract.Result() != null); - - return translator; - } } - public override VCGenerationOptions VCGenOptions { get {Contract.Ensures(Contract.Result() != null); - - return genOptions; - } } - } - - // Translator from VCExpressions to strings, which are implemented - // by the various provers - [ContractClass(typeof(VCExprTranslatorContracts))] - public abstract class VCExprTranslator : ICloneable { - public abstract string translate(VCExpr expr, int polarity); - public abstract string Lookup(VCExprVar var); - public abstract Object Clone(); - } - - [ContractClassFor(typeof(VCExprTranslator))] - - public abstract class VCExprTranslatorContracts : VCExprTranslator { - public override object Clone() { - Contract.Ensures(Contract.Result() != null); - throw new NotImplementedException(); - } - public override string Lookup(VCExprVar var) { - Contract.Requires(var != null); - Contract.Ensures(Contract.Result() != null); - - throw new NotImplementedException(); - } - public override string translate(VCExpr expr, int polarity) { - - Contract.Requires(expr != null); - - Contract.Ensures(Contract.Result() != null); - - throw new NotImplementedException(); - } - } -} +//----------------------------------------------------------------------------- +// +// Copyright (C) Microsoft Corporation. All Rights Reserved. +// +//----------------------------------------------------------------------------- +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Diagnostics.Contracts; +using Microsoft.Boogie.VCExprAST; + +namespace Microsoft.Boogie +{ + /// + /// The methods of this class are called in the following order: + /// DeclareType* + /// (DeclareConstant DeclareFunction)* + /// AddAxiom* + /// DeclareGlobalVariable* + /// At this time, all "attributes" are passed in as null. + /// + [ContractClass(typeof(ProverContextContracts))] + public abstract class ProverContext : ICloneable { + public int TimoutDiagnosticsCount { get; set; } + public readonly Dictionary> TimeoutDiagnosticIDToAssertion = new Dictionary>(); + protected virtual void ProcessDeclaration(Declaration decl) {Contract.Requires(decl != null);} + public virtual void DeclareType(TypeCtorDecl t, string attributes) {Contract.Requires(t != null); ProcessDeclaration(t); } + public virtual void DeclareConstant(Constant c, bool uniq, string attributes) {Contract.Requires(c != null); ProcessDeclaration(c); } + public virtual void DeclareFunction(Function f, string attributes) {Contract.Requires(f != null); ProcessDeclaration(f); } + public virtual void AddAxiom(Axiom a, string attributes) {Contract.Requires(a != null); ProcessDeclaration(a); } + public virtual void DeclareGlobalVariable(GlobalVariable v, string attributes) {Contract.Requires(v != null); ProcessDeclaration(v); } + public abstract void AddAxiom(VCExpr vc); + public abstract string Lookup(VCExprVar var); + public abstract VCExpressionGenerator ExprGen { get; } + public abstract Boogie2VCExprTranslator BoogieExprTranslator { get; } + public abstract VCGenerationOptions VCGenOptions { get; } + public abstract object Clone(); + public abstract void Reset(); + public abstract void Clear(); + } + +[ContractClassFor(typeof(ProverContext))] +public abstract class ProverContextContracts:ProverContext{ + public override void AddAxiom(VCExpr vc) { + } + public override void AddAxiom(Axiom a, string attributes) +{ +} + public override VCExpressionGenerator ExprGen +{ + get { Contract.Ensures(Contract.Result() != null); + throw new NotImplementedException(); } +} + public override Boogie2VCExprTranslator BoogieExprTranslator +{ + get { Contract.Ensures(Contract.Result() != null); + throw new NotImplementedException(); } +} + public override VCGenerationOptions VCGenOptions +{ + get {Contract.Ensures(Contract.Result() != null); + throw new NotImplementedException(); } +} + public override object Clone() +{ + Contract.Ensures(Contract.Result() != null); + throw new NotImplementedException(); +} +} + + // ----------------------------------------------------------------------------------------------- + // ----------------------------------------------------------------------------------------------- + // ----------------------------------------------------------------------------------------------- + + /// + /// This ProverContext subclass is intended for use with untyped provers that do not require names + /// to be declared before use. It constructs its context from unique constants and given axioms. + /// + public class DeclFreeProverContext : ProverContext { + protected VCExpressionGenerator gen; + protected VCGenerationOptions genOptions; + protected Boogie2VCExprTranslator translator; + + protected OrderingAxiomBuilder orderingAxiomBuilder; + + protected List distincts; + protected List axiomConjuncts; + + [ContractInvariantMethod] + void ObjectInvariant() { + Contract.Invariant(gen != null); + Contract.Invariant(genOptions != null); + Contract.Invariant(translator != null); + Contract.Invariant(orderingAxiomBuilder != null); + Contract.Invariant(cce.NonNullElements(distincts)); + Contract.Invariant(cce.NonNullElements(axiomConjuncts)); + } + + public VCExprTranslator/*?*/ exprTranslator; + + public DeclFreeProverContext(VCExpressionGenerator gen, + VCGenerationOptions genOptions) { + Contract.Requires(gen != null); + Contract.Requires(genOptions != null); + this.gen = gen; + this.genOptions = genOptions; + Boogie2VCExprTranslator t = new Boogie2VCExprTranslator (gen, genOptions); + this.translator = t; + + SetupOrderingAxiomBuilder(gen, t); + + distincts = new List(); + axiomConjuncts = new List(); + + exprTranslator = null; + } + + private void SetupOrderingAxiomBuilder(VCExpressionGenerator gen, Boogie2VCExprTranslator t) + { + OrderingAxiomBuilder oab = new OrderingAxiomBuilder(gen, t); + Contract.Assert(oab != null); + oab.Setup(); + this.orderingAxiomBuilder = oab; + } + + public override void Reset() + { + SetupOrderingAxiomBuilder(gen, translator); + distincts = new List(); + axiomConjuncts = new List(); + } + + public override void Clear() + { + distincts = new List(); + axiomConjuncts = new List(); + } + + protected DeclFreeProverContext(DeclFreeProverContext ctxt) { + Contract.Requires(ctxt != null); + this.gen = ctxt.gen; + this.genOptions = ctxt.genOptions; + Boogie2VCExprTranslator t = (Boogie2VCExprTranslator)ctxt.translator.Clone(); + Contract.Assert(t != null); + this.translator = t; + this.orderingAxiomBuilder = new OrderingAxiomBuilder(ctxt.gen, t, ctxt.orderingAxiomBuilder); + + StringBuilder cmds = new StringBuilder (); + + distincts = new List(ctxt.distincts); + axiomConjuncts = new List(ctxt.axiomConjuncts); + + if (ctxt.exprTranslator == null) + exprTranslator = null; + else + exprTranslator = (VCExprTranslator)cce.NonNull(ctxt.exprTranslator.Clone()); + } + + public override object Clone() { + Contract.Ensures(Contract.Result() != null); + + return new DeclFreeProverContext(this); + } + + public override void DeclareFunction(Function f, string attributes) {//Contract.Requires(f != null); + base.ProcessDeclaration(f); + } + + public override void DeclareConstant(Constant c, bool uniq, string attributes) {//Contract.Requires(c != null); + base.DeclareConstant(c, uniq, attributes); + orderingAxiomBuilder.AddConstant(c); + + // TODO: make separate distinct lists for names coming from different types + // e.g., one for strings, one for ints, one for program types. + if (uniq){ + distincts.Add(c); + } + } + + public override void AddAxiom(Axiom ax, string attributes) {//Contract.Requires(ax != null); + base.AddAxiom(ax, attributes); + + axiomConjuncts.Add(translator.Translate(ax.Expr)); + } + + public override void AddAxiom(VCExpr vc) + {//Contract.Requires(vc != null); + axiomConjuncts.Add(vc); + } + + public VCExpr Axioms { + get {Contract.Ensures(Contract.Result() != null); + VCExpr axioms = gen.NAry(VCExpressionGenerator.AndOp, axiomConjuncts); + List/*!>!*/ distinctVars = new List (); + foreach (Variable v in distincts){ + Contract.Assert(v != null); + distinctVars.Add(translator.LookupVariable(v));} + axioms = gen.AndSimp(gen.Distinct(distinctVars), axioms); + if (CommandLineOptions.Clo.TypeEncodingMethod != CommandLineOptions.TypeEncoding.Monomorphic) + axioms = gen.AndSimp(orderingAxiomBuilder.Axioms, axioms); + return axioms; + } + } + + public override string Lookup(VCExprVar var) + { + return exprTranslator.Lookup(var); + } + + public override VCExpressionGenerator ExprGen { get {Contract.Ensures(Contract.Result() != null); + + return gen; + } } + public override Boogie2VCExprTranslator BoogieExprTranslator { get {Contract.Ensures(Contract.Result() != null); + + return translator; + } } + public override VCGenerationOptions VCGenOptions { get {Contract.Ensures(Contract.Result() != null); + + return genOptions; + } } + } + + // Translator from VCExpressions to strings, which are implemented + // by the various provers + [ContractClass(typeof(VCExprTranslatorContracts))] + public abstract class VCExprTranslator : ICloneable { + public abstract string translate(VCExpr expr, int polarity); + public abstract string Lookup(VCExprVar var); + public abstract Object Clone(); + } + + [ContractClassFor(typeof(VCExprTranslator))] + + public abstract class VCExprTranslatorContracts : VCExprTranslator { + public override object Clone() { + Contract.Ensures(Contract.Result() != null); + throw new NotImplementedException(); + } + public override string Lookup(VCExprVar var) { + Contract.Requires(var != null); + Contract.Ensures(Contract.Result() != null); + + throw new NotImplementedException(); + } + public override string translate(VCExpr expr, int polarity) { + + Contract.Requires(expr != null); + + Contract.Ensures(Contract.Result() != null); + + throw new NotImplementedException(); + } + } +} diff --git a/Source/VCGeneration/ExprExtensions.cs b/Source/VCGeneration/ExprExtensions.cs index 2bdb4af7..5e0dcf8e 100644 --- a/Source/VCGeneration/ExprExtensions.cs +++ b/Source/VCGeneration/ExprExtensions.cs @@ -1,353 +1,353 @@ -//----------------------------------------------------------------------------- -// -// Copyright (C) 2012 Microsoft Corporation. All Rights Reserved. -// -//----------------------------------------------------------------------------- - - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using Term = Microsoft.Boogie.VCExprAST.VCExpr; -using FuncDecl = Microsoft.Boogie.VCExprAST.VCExprOp; -using Sort = Microsoft.Boogie.Type; -using Microsoft.Boogie.VCExprAST; - - -/** This namespace contains some extensions to allow VCExpr to provide the - * interface needed by RPFP and FixedpointVC. */ - -namespace Microsoft.Boogie.ExprExtensions -{ - class ReferenceComparer : IEqualityComparer where T : class - { - private static ReferenceComparer m_instance; - - public static ReferenceComparer Instance - { - get - { - return m_instance ?? (m_instance = new ReferenceComparer()); - } - } - - public bool Equals(T x, T y) - { - return ReferenceEquals(x, y); - } - - public int GetHashCode(T obj) - { - return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(obj); - } - } - - public class TermDict : Dictionary - { - public TermDict() : base(ReferenceComparer.Instance) { } - } - - - - public enum TermKind { App, Other }; - - public enum DeclKind { Uninterpreted, And, Implies, Label, Other }; - - public static class MyExtensions - { - public static Term[] GetAppArgs(this Term t) - { - Microsoft.Boogie.VCExprAST.VCExprNAry tn = t as Microsoft.Boogie.VCExprAST.VCExprNAry; - return tn.ToArray(); - } - - public static FuncDecl GetAppDecl(this Term t) - { - Microsoft.Boogie.VCExprAST.VCExprNAry tn = t as Microsoft.Boogie.VCExprAST.VCExprNAry; - return tn.Op; - } - - public static string GetDeclName(this FuncDecl f) - { - return (f as VCExprBoogieFunctionOp).Func.Name; //TODO - } - - public static DeclKind GetKind(this FuncDecl f) - { - if (f is VCExprBoogieFunctionOp) - return DeclKind.Uninterpreted; - if (f == VCExpressionGenerator.AndOp) - return DeclKind.And; - if (f == VCExpressionGenerator.ImpliesOp) - return DeclKind.Implies; - if (f is VCExprLabelOp) - return DeclKind.Label; - return DeclKind.Other; - } - - public static bool IsLabel(this Term t) - { - return (t is VCExprNAry) && (GetAppDecl(t) is VCExprLabelOp); - } - - public static string LabelName(this Term t) - { - return (GetAppDecl(t) as VCExprLabelOp).label; - } - - public static Sort GetSort(this Term t) - { - return t.Type; - } - - public static TermKind GetKind(this Term t) - { - if (t is Microsoft.Boogie.VCExprAST.VCExprNAry) - return TermKind.App; - return TermKind.Other; - } - - public static bool IsFunctionApp(this Term t) - { - return t.GetKind() == TermKind.App && t.GetAppDecl().GetKind() == DeclKind.Uninterpreted; - } - - public static bool IsFalse(this Term t) - { - return t == VCExpressionGenerator.False; - } - - public static Term VCExprToTerm(this Microsoft.Boogie.ProverContext ctx, VCExpr e, LineariserOptions lin){ - return e; - } - - } - - public class Context : Microsoft.Boogie.VCExpressionGenerator - { - public Term MkTrue() - { - return VCExpressionGenerator.True; - } - - public Term MkFalse() - { - return VCExpressionGenerator.False; - } - - - public List axioms = new List(); - - public void AddAxiom(Term ax) - { - axioms.Add(ax); - } - - public void RemoveAxiom(Term ax) - { - axioms.Remove(ax); - } - - public FuncDecl MkFuncDecl(string name, FuncDecl f) - { - Function h = (f as VCExprBoogieFunctionOp).Func; - Function g = new Function(Token.NoToken, name, h.InParams, h.OutParams[0]); - return BoogieFunctionOp(g); - } - - public FuncDecl MkFuncDecl(string name, Sort rng) - { - Function g = new Function(Token.NoToken, name, new List(), new Constant(Token.NoToken, new TypedIdent(Token.NoToken, "dummy",rng))); - return BoogieFunctionOp(g); - } - - public Term MkApp(FuncDecl f, Term[] args) - { - return Function(f, args); - } - - public Term MkApp(FuncDecl f, Term[] args, Type[]/*!*/ typeArguments) - { - return Function(f, args, typeArguments); - } - - public Term MkApp(FuncDecl f, Term arg) - { - return Function(f, arg); - } - - public Term CloneApp(Term t, Term[] args) - { - var f = t.GetAppDecl(); - var typeArgs = (t as VCExprNAry).TypeArguments; - if (typeArgs != null && typeArgs.Count > 0) - { - return MkApp(f, args, typeArgs.ToArray()); - } - else - { - return MkApp(f, args); - } - } - - public Term MkAnd(Term[] args) - { - if (args.Length == 0) return True; - Term res = args[0]; - for (int i = 1; i < args.Length; i++) - res = And(res, args[i]); - return res; - } - - public Term MkAnd(Term arg1, Term arg2) - { - return And(arg1, arg2); - } - - - public Term MkNot(Term arg1) - { - return Not(arg1); - } - - public Term MkImplies(Term arg1, Term arg2) - { - return Implies(arg1, arg2); - } - - public Term MkEq(Term arg1, Term arg2) - { - return Eq(arg1, arg2); - } - - public Sort MkBoolSort() - { - return Type.Bool; - } - - public Term MkConst(string name, Sort sort) - { - return Variable(name, sort); - } - - public Term MkForall(Term[] bounds, Term body) - { - if (bounds.Length == 0) - return body; - List vbs = new List(); - foreach(var v in bounds) - vbs.Add(v as VCExprVar); - return Forall(vbs,new List(), body); - } - - public Term MkExists(Term[] bounds, Term body) - { - if (bounds.Length == 0) - return body; - List vbs = new List(); - foreach (var v in bounds) - vbs.Add(v as VCExprVar); - return Exists(vbs, new List(), body); - } - - private class Letifier - { - private class counter - { - public int cnt = 0; - } - TermDict refcnt = new TermDict(); - List bindings = new List(); - TermDict< VCExprVar> bindingMap = new TermDict< VCExprVar>(); - int letcnt = 0; - Context ctx; - - public Letifier(Context _ctx) { ctx = _ctx; } - - private void RefCnt(Term t) - { - counter cnt; - if (!refcnt.TryGetValue(t, out cnt)) - { - cnt = new counter(); - refcnt.Add(t, cnt); - } - cnt.cnt++; - if (cnt.cnt == 1) - { - var kind = t.GetKind(); - if (kind == TermKind.App) - { - var args = t.GetAppArgs(); - foreach (var arg in args) - RefCnt(arg); - } - else if (t is VCExprQuantifier) - { - RefCnt((t as VCExprQuantifier).Body); - } - } - } - - private Term Doit(Term t) - { - VCExprVar v; - if (bindingMap.TryGetValue(t, out v)) - { - return v; - } - Term res = null; - var kind = t.GetKind(); - bool letok = false; - if (kind == TermKind.App) - { - var f = t.GetAppDecl(); - var args = t.GetAppArgs(); - args = args.Select(x => Doit(x)).ToArray(); - res = ctx.MkApp(f, args); - letok = true; - } - else if (t is VCExprQuantifier) - { - var q = t as VCExprQuantifier; - var newbody = ctx.Letify(q.Body); - if (q.Quan == Quantifier.ALL) - res = ctx.Forall(q.BoundVars, q.Triggers, newbody); - else - res = ctx.Exists(q.BoundVars, q.Triggers, newbody); - letok = true; - } - else res = t; - if (letok && refcnt[t].cnt > 1) - { - VCExprVar lv = ctx.MkConst("fpvc$" + Convert.ToString(letcnt), t.GetSort()) as VCExprVar; - VCExprLetBinding b = ctx.LetBinding(lv, res); - bindings.Add(b); - bindingMap.Add(t, lv); - res = lv; - letcnt++; - } - return res; - } - - public Term Letify(Term t) - { - RefCnt(t); - Term res = Doit(t); - if (bindings.Count > 0) - res = ctx.Let(bindings, res); - return res; - } - - } - - public Term Letify(Term t) - { - var thing = new Letifier(this); - return thing.Letify(t); - } - - }; -} +//----------------------------------------------------------------------------- +// +// Copyright (C) 2012 Microsoft Corporation. All Rights Reserved. +// +//----------------------------------------------------------------------------- + + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Term = Microsoft.Boogie.VCExprAST.VCExpr; +using FuncDecl = Microsoft.Boogie.VCExprAST.VCExprOp; +using Sort = Microsoft.Boogie.Type; +using Microsoft.Boogie.VCExprAST; + + +/** This namespace contains some extensions to allow VCExpr to provide the + * interface needed by RPFP and FixedpointVC. */ + +namespace Microsoft.Boogie.ExprExtensions +{ + class ReferenceComparer : IEqualityComparer where T : class + { + private static ReferenceComparer m_instance; + + public static ReferenceComparer Instance + { + get + { + return m_instance ?? (m_instance = new ReferenceComparer()); + } + } + + public bool Equals(T x, T y) + { + return ReferenceEquals(x, y); + } + + public int GetHashCode(T obj) + { + return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(obj); + } + } + + public class TermDict : Dictionary + { + public TermDict() : base(ReferenceComparer.Instance) { } + } + + + + public enum TermKind { App, Other }; + + public enum DeclKind { Uninterpreted, And, Implies, Label, Other }; + + public static class MyExtensions + { + public static Term[] GetAppArgs(this Term t) + { + Microsoft.Boogie.VCExprAST.VCExprNAry tn = t as Microsoft.Boogie.VCExprAST.VCExprNAry; + return tn.ToArray(); + } + + public static FuncDecl GetAppDecl(this Term t) + { + Microsoft.Boogie.VCExprAST.VCExprNAry tn = t as Microsoft.Boogie.VCExprAST.VCExprNAry; + return tn.Op; + } + + public static string GetDeclName(this FuncDecl f) + { + return (f as VCExprBoogieFunctionOp).Func.Name; //TODO + } + + public static DeclKind GetKind(this FuncDecl f) + { + if (f is VCExprBoogieFunctionOp) + return DeclKind.Uninterpreted; + if (f == VCExpressionGenerator.AndOp) + return DeclKind.And; + if (f == VCExpressionGenerator.ImpliesOp) + return DeclKind.Implies; + if (f is VCExprLabelOp) + return DeclKind.Label; + return DeclKind.Other; + } + + public static bool IsLabel(this Term t) + { + return (t is VCExprNAry) && (GetAppDecl(t) is VCExprLabelOp); + } + + public static string LabelName(this Term t) + { + return (GetAppDecl(t) as VCExprLabelOp).label; + } + + public static Sort GetSort(this Term t) + { + return t.Type; + } + + public static TermKind GetKind(this Term t) + { + if (t is Microsoft.Boogie.VCExprAST.VCExprNAry) + return TermKind.App; + return TermKind.Other; + } + + public static bool IsFunctionApp(this Term t) + { + return t.GetKind() == TermKind.App && t.GetAppDecl().GetKind() == DeclKind.Uninterpreted; + } + + public static bool IsFalse(this Term t) + { + return t == VCExpressionGenerator.False; + } + + public static Term VCExprToTerm(this Microsoft.Boogie.ProverContext ctx, VCExpr e, LineariserOptions lin){ + return e; + } + + } + + public class Context : Microsoft.Boogie.VCExpressionGenerator + { + public Term MkTrue() + { + return VCExpressionGenerator.True; + } + + public Term MkFalse() + { + return VCExpressionGenerator.False; + } + + + public List axioms = new List(); + + public void AddAxiom(Term ax) + { + axioms.Add(ax); + } + + public void RemoveAxiom(Term ax) + { + axioms.Remove(ax); + } + + public FuncDecl MkFuncDecl(string name, FuncDecl f) + { + Function h = (f as VCExprBoogieFunctionOp).Func; + Function g = new Function(Token.NoToken, name, h.InParams, h.OutParams[0]); + return BoogieFunctionOp(g); + } + + public FuncDecl MkFuncDecl(string name, Sort rng) + { + Function g = new Function(Token.NoToken, name, new List(), new Constant(Token.NoToken, new TypedIdent(Token.NoToken, "dummy",rng))); + return BoogieFunctionOp(g); + } + + public Term MkApp(FuncDecl f, Term[] args) + { + return Function(f, args); + } + + public Term MkApp(FuncDecl f, Term[] args, Type[]/*!*/ typeArguments) + { + return Function(f, args, typeArguments); + } + + public Term MkApp(FuncDecl f, Term arg) + { + return Function(f, arg); + } + + public Term CloneApp(Term t, Term[] args) + { + var f = t.GetAppDecl(); + var typeArgs = (t as VCExprNAry).TypeArguments; + if (typeArgs != null && typeArgs.Count > 0) + { + return MkApp(f, args, typeArgs.ToArray()); + } + else + { + return MkApp(f, args); + } + } + + public Term MkAnd(Term[] args) + { + if (args.Length == 0) return True; + Term res = args[0]; + for (int i = 1; i < args.Length; i++) + res = And(res, args[i]); + return res; + } + + public Term MkAnd(Term arg1, Term arg2) + { + return And(arg1, arg2); + } + + + public Term MkNot(Term arg1) + { + return Not(arg1); + } + + public Term MkImplies(Term arg1, Term arg2) + { + return Implies(arg1, arg2); + } + + public Term MkEq(Term arg1, Term arg2) + { + return Eq(arg1, arg2); + } + + public Sort MkBoolSort() + { + return Type.Bool; + } + + public Term MkConst(string name, Sort sort) + { + return Variable(name, sort); + } + + public Term MkForall(Term[] bounds, Term body) + { + if (bounds.Length == 0) + return body; + List vbs = new List(); + foreach(var v in bounds) + vbs.Add(v as VCExprVar); + return Forall(vbs,new List(), body); + } + + public Term MkExists(Term[] bounds, Term body) + { + if (bounds.Length == 0) + return body; + List vbs = new List(); + foreach (var v in bounds) + vbs.Add(v as VCExprVar); + return Exists(vbs, new List(), body); + } + + private class Letifier + { + private class counter + { + public int cnt = 0; + } + TermDict refcnt = new TermDict(); + List bindings = new List(); + TermDict< VCExprVar> bindingMap = new TermDict< VCExprVar>(); + int letcnt = 0; + Context ctx; + + public Letifier(Context _ctx) { ctx = _ctx; } + + private void RefCnt(Term t) + { + counter cnt; + if (!refcnt.TryGetValue(t, out cnt)) + { + cnt = new counter(); + refcnt.Add(t, cnt); + } + cnt.cnt++; + if (cnt.cnt == 1) + { + var kind = t.GetKind(); + if (kind == TermKind.App) + { + var args = t.GetAppArgs(); + foreach (var arg in args) + RefCnt(arg); + } + else if (t is VCExprQuantifier) + { + RefCnt((t as VCExprQuantifier).Body); + } + } + } + + private Term Doit(Term t) + { + VCExprVar v; + if (bindingMap.TryGetValue(t, out v)) + { + return v; + } + Term res = null; + var kind = t.GetKind(); + bool letok = false; + if (kind == TermKind.App) + { + var f = t.GetAppDecl(); + var args = t.GetAppArgs(); + args = args.Select(x => Doit(x)).ToArray(); + res = ctx.MkApp(f, args); + letok = true; + } + else if (t is VCExprQuantifier) + { + var q = t as VCExprQuantifier; + var newbody = ctx.Letify(q.Body); + if (q.Quan == Quantifier.ALL) + res = ctx.Forall(q.BoundVars, q.Triggers, newbody); + else + res = ctx.Exists(q.BoundVars, q.Triggers, newbody); + letok = true; + } + else res = t; + if (letok && refcnt[t].cnt > 1) + { + VCExprVar lv = ctx.MkConst("fpvc$" + Convert.ToString(letcnt), t.GetSort()) as VCExprVar; + VCExprLetBinding b = ctx.LetBinding(lv, res); + bindings.Add(b); + bindingMap.Add(t, lv); + res = lv; + letcnt++; + } + return res; + } + + public Term Letify(Term t) + { + RefCnt(t); + Term res = Doit(t); + if (bindings.Count > 0) + res = ctx.Let(bindings, res); + return res; + } + + } + + public Term Letify(Term t) + { + var thing = new Letifier(this); + return thing.Letify(t); + } + + }; +} diff --git a/Source/VCGeneration/FixedpointVC.cs b/Source/VCGeneration/FixedpointVC.cs index 70ce5a8f..c636ea2b 100644 --- a/Source/VCGeneration/FixedpointVC.cs +++ b/Source/VCGeneration/FixedpointVC.cs @@ -1,2245 +1,2245 @@ -//----------------------------------------------------------------------------- -// -// Copyright (C) 2012 Microsoft Corporation. All Rights Reserved. -// -//----------------------------------------------------------------------------- - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.IO; -using System.Diagnostics.Contracts; -using Microsoft.Boogie; -using Microsoft.Boogie.VCExprAST; - - -using Term = Microsoft.Boogie.VCExprAST.VCExpr; -using FuncDecl = Microsoft.Boogie.VCExprAST.VCExprOp; -using Sort = Microsoft.Boogie.Type; -using Microsoft.Boogie.ExprExtensions; - - -namespace Microsoft.Boogie -{ - public class FixedpointVC : VC.VCGen - { - - public class AnnotationInfo - { - public enum AnnotationType { LoopInvariant, ProcedureSummary }; - public string filename; - public int lineno; - public string[] argnames; - public AnnotationType type; - }; - - static bool NoLabels = false; - - // options - bool largeblock = false; - - public bool SetOption(string option, string value) - { - if (option == "LargeBlock") - { - largeblock = true; - return true; - } - return false; - } - - Context ctx; - RPFP rpfp; - // Program program; - Microsoft.Boogie.ProverContext boogieContext; - Microsoft.Boogie.VCExpressionGenerator gen; - public readonly static string recordProcName = "boogie_si_record"; // TODO: this really needed? - private Dictionary implName2StratifiedInliningInfo - = new Dictionary(); - Checker checker; - // Microsoft.Boogie.Z3.Z3InstanceOptions options = new Microsoft.Boogie.Z3.Z3InstanceOptions(); // TODO: what? - LineariserOptions linOptions; - Dictionary relationToProc = new Dictionary(); - Dictionary labels = new Dictionary (); - List DualityVCs = new List(); - Dictionary summaries = new Dictionary(); - Dictionary> edgesCut = new Dictionary>(); - string main_proc_name = "main"; - Dictionary extraRecBound = null; - - - public enum Mode { Corral, OldCorral, Boogie}; - public enum AnnotationStyle { Flat, Procedure, Call }; - - Mode mode; - AnnotationStyle style; - - private static Checker old_checker = null; - - public static void CleanUp() - { - if (old_checker != null) - { - old_checker.Close(); - old_checker = null; - } - } - - public FixedpointVC( Program _program, string/*?*/ logFilePath, bool appendLogFile, List checkers, Dictionary _extraRecBound = null) - : base(_program, logFilePath, appendLogFile, checkers) - { - switch (CommandLineOptions.Clo.FixedPointMode) - { - case CommandLineOptions.FixedPointInferenceMode.Corral: - mode = Mode.Corral; - style = AnnotationStyle.Procedure; - break; - case CommandLineOptions.FixedPointInferenceMode.OldCorral: - mode = Mode.OldCorral; - style = AnnotationStyle.Procedure; - break; - case CommandLineOptions.FixedPointInferenceMode.Flat: - mode = Mode.Boogie; - style = AnnotationStyle.Flat; - break; - case CommandLineOptions.FixedPointInferenceMode.Procedure: - mode = Mode.Boogie; - style = AnnotationStyle.Procedure; - break; - case CommandLineOptions.FixedPointInferenceMode.Call: - mode = Mode.Boogie; - style = AnnotationStyle.Call; - break; - } - ctx = new Context(); // TODO is this right? - rpfp = new RPFP(RPFP.CreateLogicSolver(ctx)); - program = _program; - gen = ctx; - if(old_checker == null) - checker = new Checker(this, program, logFilePath, appendLogFile, CommandLineOptions.Clo.ProverKillTime, null); - else { - checker = old_checker; - checker.RetargetWithoutReset(program,checker.TheoremProver.Context); - } - old_checker = checker; - boogieContext = checker.TheoremProver.Context; - linOptions = null; // new Microsoft.Boogie.Z3.Z3LineariserOptions(false, options, new List()); - extraRecBound = _extraRecBound; - } - - Dictionary annotationInfo = new Dictionary(); - - public void AnnotateLoops(Implementation impl, ProverContext ctxt) - { - Contract.Requires(impl != null); - - CurrentLocalVariables = impl.LocVars; - variable2SequenceNumber = new Dictionary(); - incarnationOriginMap = new Dictionary(); - - ResetPredecessors(impl.Blocks); - - #region Create the graph by adding the source node and each edge - GraphUtil.Graph g = Program.GraphFromImpl(impl); - #endregion - - //Graph g = program.ProcessLoops(impl); - - g.ComputeLoops(); // this is the call that does all of the processing - if (!g.Reducible) - { - throw new System.Exception("Irreducible flow graphs are unsupported."); - } - - #region add a symbolic annoation to every loop head - foreach (Block header in cce.NonNull(g.Headers)) - AnnotateBlock(impl, ctxt, header); - #endregion - } - - private void AnnotateCallSites(Implementation impl, ProverContext ctxt, Dictionary impls){ - foreach (var b in impl.Blocks) - { - foreach (var cmd in b.Cmds) - { - if (cmd is CallCmd) - { - string name = (cmd as CallCmd).callee; - if(impls.ContainsKey(name)) - goto annotate; - } - } - continue; - annotate: - AnnotateBlock(impl, ctxt, b); - } - } - - - private void AnnotateBlock(Implementation impl, ProverContext ctxt, Block header) - { - Contract.Assert(header != null); - - string name = impl.Name + "_" + header.Label + "_invar"; - if (annotationInfo.ContainsKey(name)) - return; - - // collect the variables needed in the invariant - List exprs = new List(); - List vars = new List(); - List names = new List(); - - if (style == AnnotationStyle.Flat) - { - // in flat mode, all live globals should be in live set -#if false - foreach (Variable v in program.GlobalVariables) - { - vars.Add(v); - names.Add(v.ToString()); - exprs.Add(new IdentifierExpr(Token.NoToken, v)); - } -#endif - foreach (Variable v in /* impl.LocVars */ header.liveVarsBefore) - { - if (!(v is BoundVariable)) - { - vars.Add(v); - names.Add(v.ToString()); - exprs.Add(new IdentifierExpr(Token.NoToken, v)); - } - } - } - else - { - foreach (Variable v in program.GlobalVariables) - { - vars.Add(v); - names.Add("@old_" + v.ToString()); - exprs.Add(new OldExpr(Token.NoToken, new IdentifierExpr(Token.NoToken, v))); - } - foreach (IdentifierExpr ie in impl.Proc.Modifies) - { - if (ie.Decl == null) - continue; - vars.Add(ie.Decl); - names.Add(ie.Decl.ToString()); - exprs.Add(ie); - } - foreach (Variable v in impl.Proc.InParams) - { - Contract.Assert(v != null); - vars.Add(v); - names.Add("@old_" + v.ToString()); - exprs.Add(new OldExpr(Token.NoToken, new IdentifierExpr(Token.NoToken, v))); - } - foreach (Variable v in impl.LocVars) - { - vars.Add(v); - names.Add(v.ToString()); - exprs.Add(new IdentifierExpr(Token.NoToken, v)); - } - } - - TypedIdent ti = new TypedIdent(Token.NoToken, "", Microsoft.Boogie.Type.Bool); - Contract.Assert(ti != null); - Formal returnVar = new Formal(Token.NoToken, ti, false); - Contract.Assert(returnVar != null); - var function = new Function(Token.NoToken, name, vars, returnVar); - ctxt.DeclareFunction(function, ""); - - Expr invarExpr = new NAryExpr(Token.NoToken, new FunctionCall(function), exprs); - var invarAssertion = new AssertCmd(Token.NoToken, invarExpr); - List newCmds = new List(); - newCmds.Add(invarAssertion); - - // make a record in annotationInfo; - var info = new AnnotationInfo(); - info.filename = header.tok.filename; - info.lineno = header.Line; - info.argnames = names.ToArray(); - info.type = AnnotationInfo.AnnotationType.LoopInvariant; - annotationInfo.Add(name, info); - // get file and line info from havoc, if there is... - if (header.Cmds.Count > 0) - { - PredicateCmd bif = header.Cmds[0] as PredicateCmd; - if (bif != null) - { - string foo = QKeyValue.FindStringAttribute(bif.Attributes, "sourcefile"); - if (foo != null) - info.filename = foo; - int bar = QKeyValue.FindIntAttribute(bif.Attributes, "sourceline", -1); - if (bar != -1) - info.lineno = bar; - } - } - var thing = header; - foreach (Cmd c in header.Cmds) - { - newCmds.Add(c); - } - header.Cmds = newCmds; - } - -#if true - public void AnnotateProcRequires(Procedure proc, Implementation impl, ProverContext ctxt) - { - Contract.Requires(impl != null); - - CurrentLocalVariables = impl.LocVars; - - // collect the variables needed in the invariant - List exprs = new List(); - List vars = new List(); - List names = new List(); - - foreach (Variable v in program.GlobalVariables) - { - vars.Add(v); - exprs.Add(new IdentifierExpr(Token.NoToken, v)); - names.Add(v.Name); - } - foreach (Variable v in proc.InParams) - { - Contract.Assert(v != null); - vars.Add(v); - exprs.Add(new IdentifierExpr(Token.NoToken, v)); - names.Add(v.Name); - } - string name = impl.Name + "_precond"; - TypedIdent ti = new TypedIdent(Token.NoToken, "", Microsoft.Boogie.Type.Bool); - Contract.Assert(ti != null); - Formal returnVar = new Formal(Token.NoToken, ti, false); - Contract.Assert(returnVar != null); - var function = new Function(Token.NoToken, name, vars, returnVar); - ctxt.DeclareFunction(function, ""); - - Expr invarExpr = new NAryExpr(Token.NoToken, new FunctionCall(function), exprs); - - proc.Requires.Add(new Requires(Token.NoToken, false, invarExpr, "", null)); - - var info = new AnnotationInfo(); - info.filename = proc.tok.filename; - info.lineno = proc.Line; - info.argnames = names.ToArray(); - info.type = AnnotationInfo.AnnotationType.LoopInvariant; - annotationInfo.Add(name, info); - } - - public void AnnotateProcEnsures(Procedure proc, Implementation impl, ProverContext ctxt) - { - Contract.Requires(impl != null); - - CurrentLocalVariables = impl.LocVars; - - // collect the variables needed in the invariant - List exprs = new List(); - List vars = new List(); - List names = new List(); - - foreach (Variable v in program.GlobalVariables) - { - vars.Add(v); - exprs.Add(new OldExpr(Token.NoToken,new IdentifierExpr(Token.NoToken, v))); - names.Add(v.Name); - } - foreach (Variable v in proc.InParams) - { - Contract.Assert(v != null); - vars.Add(v); - exprs.Add(new OldExpr(Token.NoToken, new IdentifierExpr(Token.NoToken, v))); - names.Add(v.Name); - } - foreach (IdentifierExpr ie in proc.Modifies) - { - if (ie.Decl == null) - continue; - vars.Add(ie.Decl); - exprs.Add(ie); - names.Add(ie.Decl.Name + "_out"); - } - foreach (Variable v in proc.OutParams) - { - Contract.Assert(v != null); - vars.Add(v); - exprs.Add(new IdentifierExpr(Token.NoToken, v)); - names.Add(v.Name); - } - string name = impl.Name + "_summary"; - summaries.Add(name, true); - TypedIdent ti = new TypedIdent(Token.NoToken, "", Microsoft.Boogie.Type.Bool); - Contract.Assert(ti != null); - Formal returnVar = new Formal(Token.NoToken, ti, false); - Contract.Assert(returnVar != null); - var function = new Function(Token.NoToken, name, vars, returnVar); - ctxt.DeclareFunction(function, ""); - - Expr invarExpr = new NAryExpr(Token.NoToken, new FunctionCall(function), exprs); - - proc.Ensures.Add(new Ensures(Token.NoToken, false, invarExpr, "", null)); - - var info = new AnnotationInfo(); - info.filename = proc.tok.filename; - info.lineno = proc.Line; - info.argnames = names.ToArray(); - info.type = AnnotationInfo.AnnotationType.ProcedureSummary; - annotationInfo.Add(name, info); - } -#endif - - void MarkAllFunctionImplementationsInline() - { - foreach (var func in program.Functions) - { - if (func.Body == null && func.DefinitionAxiom != null) - { - var def = func.DefinitionAxiom.Expr as QuantifierExpr; - var bod = def.Body as NAryExpr; - func.Body = bod.Args[1]; - func.DefinitionAxiom = null; - } - if (func.Body != null) - if (func.FindExprAttribute("inline") == null) - func.AddAttribute("inline", Expr.Literal(100)); - } - } - - void InlineAll() - { - foreach (var impl in program.Implementations) - { - impl.OriginalBlocks = impl.Blocks; - impl.OriginalLocVars = impl.LocVars; - if(impl.Name != main_proc_name) - if(impl.FindExprAttribute("inline") == null) - impl.AddAttribute("inline", Expr.Literal(100)); - } - foreach (var impl in program.Implementations) - { - if (!impl.SkipVerification) - { - Inliner.ProcessImplementation(program, impl); - } - } - foreach (var impl in program.Implementations) - { - impl.OriginalBlocks = null; - impl.OriginalLocVars = null; - } - } - - public class LazyInliningInfo - { - [ContractInvariantMethod] - void ObjectInvariant() - { - Contract.Invariant(impl != null); - Contract.Invariant(function != null); - Contract.Invariant(controlFlowVariable != null); - Contract.Invariant(assertExpr != null); - Contract.Invariant(cce.NonNullElements(interfaceVars)); - Contract.Invariant(incarnationOriginMap == null || cce.NonNullDictionaryAndValues(incarnationOriginMap)); - } - - public Implementation impl; - public int uniqueId; - public Function function; - public Variable controlFlowVariable; - public List interfaceVars; - public List> interfaceVarCopies; - public Expr assertExpr; - public VCExpr vcexpr; - public List privateVars; - public Dictionary incarnationOriginMap; - public Hashtable /*Variable->Expr*/ exitIncarnationMap; - public Hashtable /*GotoCmd->returnCmd*/ gotoCmdOrigins; - public Dictionary label2absy; - public VC.ModelViewInfo mvInfo; - - public Dictionary reachVars; - public List reachVarBindings; - public Variable inputErrorVariable; - public Variable outputErrorVariable; - - - - public LazyInliningInfo(Implementation impl, Program program, ProverContext ctxt, int uniqueId, GlobalVariable errorVariable) - { - Contract.Requires(impl != null); - Contract.Requires(program != null); - Procedure proc = cce.NonNull(impl.Proc); - - this.impl = impl; - this.uniqueId = uniqueId; - this.controlFlowVariable = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, "cfc", Microsoft.Boogie.Type.Int)); - impl.LocVars.Add(controlFlowVariable); - - List interfaceVars = new List(); - Expr assertExpr = new LiteralExpr(Token.NoToken, true); - Contract.Assert(assertExpr != null); - // InParams must be obtained from impl and not proc - foreach (Variable v in impl.InParams) - { - Contract.Assert(v != null); - interfaceVars.Add(v); - } - // OutParams must be obtained from impl and not proc - foreach (Variable v in impl.OutParams) - { - Contract.Assert(v != null); - Constant c = new Constant(Token.NoToken, - new TypedIdent(Token.NoToken, impl.Name + "_" + v.Name, v.TypedIdent.Type)); - interfaceVars.Add(c); - Expr eqExpr = Expr.Eq(new IdentifierExpr(Token.NoToken, c), new IdentifierExpr(Token.NoToken, v)); - assertExpr = Expr.And(assertExpr, eqExpr); - } - foreach (Variable v in program.GlobalVariables) - { - Contract.Assert(v != null); - interfaceVars.Add(v); - if (v.Name == "error") - inputErrorVariable = v; - } - if (errorVariable != null) - { - proc.Modifies.Add(new IdentifierExpr(Token.NoToken, errorVariable)); - } - foreach (IdentifierExpr e in proc.Modifies) - { - Contract.Assert(e != null); - if (e.Decl == null) - continue; - Variable v = e.Decl; - Constant c = new Constant(Token.NoToken, new TypedIdent(Token.NoToken, impl.Name + "_" + v.Name, v.TypedIdent.Type)); - interfaceVars.Add(c); - if (v.Name == "error") - { - outputErrorVariable = c; - continue; - } - Expr eqExpr = Expr.Eq(new IdentifierExpr(Token.NoToken, c), new IdentifierExpr(Token.NoToken, v)); - assertExpr = Expr.And(assertExpr, eqExpr); - } - - this.interfaceVars = interfaceVars; - this.assertExpr = Expr.Not(assertExpr); - List functionInterfaceVars = new List(); - foreach (Variable v in interfaceVars) - { - Contract.Assert(v != null); - functionInterfaceVars.Add(new Formal(Token.NoToken, new TypedIdent(Token.NoToken, v.Name, v.TypedIdent.Type), true)); - } - TypedIdent ti = new TypedIdent(Token.NoToken, "", Microsoft.Boogie.Type.Bool); - Contract.Assert(ti != null); - Formal returnVar = new Formal(Token.NoToken, ti, false); - Contract.Assert(returnVar != null); - this.function = new Function(Token.NoToken, proc.Name, functionInterfaceVars, returnVar); - ctxt.DeclareFunction(this.function, ""); - - interfaceVarCopies = new List>(); - int temp = 0; - for (int i = 0; i < /* CommandLineOptions.Clo.ProcedureCopyBound */ 0; i++) - { - interfaceVarCopies.Add(new List()); - foreach (Variable v in interfaceVars) - { - Constant constant = new Constant(Token.NoToken, new TypedIdent(Token.NoToken, v.Name + temp++, v.TypedIdent.Type)); - interfaceVarCopies[i].Add(constant); - //program.AddTopLevelDeclaration(constant); - } - } - } - } - - public class StratifiedInliningInfo : LazyInliningInfo - { - [ContractInvariantMethod] - void ObjectInvariant() - { - Contract.Invariant(cce.NonNullElements(privateVars)); - Contract.Invariant(cce.NonNullElements(interfaceExprVars)); - Contract.Invariant(cce.NonNullElements(interfaceExprVars)); - } - - // public StratifiedVCGenBase vcgen; - //public Implementation impl; - //public Program program; - //public ProverContext ctxt; - //public int uniqueid; - //public Function function; - //public Variable controlFlowVariable; - //public Expr assertExpr; - //public VCExpr vcexpr; - //public List interfaceExprVars; - //public List privateExprVars; - //public Dictionary label2absy; - //public VC.ModelViewInfo mvInfo; - //public Dictionary> callSites; - //public Dictionary> recordProcCallSites; - //public IEnumerable sortedBlocks; - //public bool initialized { get; private set; } - - - public List interfaceExprVars; - // public List privateVars; - public VCExpr funcExpr; - public VCExpr falseExpr; - public RPFP.Transformer F; - public RPFP.Node node; - public RPFP.Edge edge; - public bool isMain = false; - public Dictionary label2absyInv; - public ProverContext ctxt; - public Hashtable/**/ blockVariables = new Hashtable/**/(); - public List bindings = new List(); - - public StratifiedInliningInfo(Implementation _impl, Program _program, ProverContext _ctxt, int _uniqueid) - : base(_impl,_program,_ctxt,_uniqueid,null){ - Contract.Requires(_impl != null); - Contract.Requires(_program != null); - privateVars = new List(); - interfaceExprVars = new List(); - ctxt = _ctxt; - } - - } - - protected override void addExitAssert(string implName, Block exitBlock) - { - if (implName2StratifiedInliningInfo != null - && implName2StratifiedInliningInfo.ContainsKey(implName) - && !implName2StratifiedInliningInfo[implName].isMain) - { - if (mode == Mode.Boogie) return; - Expr assertExpr = implName2StratifiedInliningInfo[implName].assertExpr; - Contract.Assert(assertExpr != null); - exitBlock.Cmds.Add(new AssertCmd(Token.NoToken, assertExpr)); - } - } - -#if false - protected override void storeIncarnationMaps(string implName, Hashtable exitIncarnationMap) - { - if (implName2StratifiedInliningInfo != null && implName2StratifiedInliningInfo.ContainsKey(implName)) - { - StratifiedInliningInfo info = implName2StratifiedInliningInfo[implName]; - Contract.Assert(info != null); - info.exitIncarnationMap = exitIncarnationMap; - info.incarnationOriginMap = this.incarnationOriginMap; - } - } -#endif - - public void GenerateVCsForStratifiedInlining() - { - Contract.Requires(program != null); - foreach (var impl in program.Implementations) - { - Contract.Assert(!impl.Name.StartsWith(recordProcName), "Not allowed to have an implementation for this guy"); - - Procedure proc = cce.NonNull(impl.Proc); - - { - StratifiedInliningInfo info = new StratifiedInliningInfo(impl, program, boogieContext, QuantifierExpr.GetNextSkolemId()); - implName2StratifiedInliningInfo[impl.Name] = info; - // We don't need controlFlowVariable for stratified Inlining - //impl.LocVars.Add(info.controlFlowVariable); - List exprs = new List(); - - if (mode != Mode.Boogie && QKeyValue.FindBoolAttribute(impl.Attributes, "entrypoint")) - { - proc.Ensures.Add(new Ensures(Token.NoToken, true, Microsoft.Boogie.Expr.False, "", null)); - info.assertExpr = Microsoft.Boogie.Expr.False; - // info.isMain = true; - } - else if (mode == Mode.Corral || proc.FindExprAttribute("inline") != null || proc is LoopProcedure) - { - foreach (Variable v in proc.InParams) - { - Contract.Assert(v != null); - exprs.Add(new IdentifierExpr(Token.NoToken, v)); - } - foreach (Variable v in proc.OutParams) - { - Contract.Assert(v != null); - exprs.Add(new IdentifierExpr(Token.NoToken, v)); - } - foreach (Variable v in program.GlobalVariables) - { - Contract.Assert(v != null); - exprs.Add(new OldExpr(Token.NoToken, new IdentifierExpr(Token.NoToken, v))); - } - foreach (IdentifierExpr ie in proc.Modifies) - { - Contract.Assert(ie != null); - if (ie.Decl == null) - continue; - exprs.Add(ie); - } - Expr freePostExpr = new NAryExpr(Token.NoToken, new FunctionCall(info.function), exprs); -#if true - if(mode == Mode.Corral || mode == Mode.OldCorral) - proc.Ensures.Add(new Ensures(Token.NoToken, true, freePostExpr, "", new QKeyValue(Token.NoToken, "si_fcall", new List(), null))); -#endif - } - else // not marked "inline" must be main - { - Expr freePostExpr = new NAryExpr(Token.NoToken, new FunctionCall(info.function), exprs); - info.isMain = true; - } - } - } - - if (mode == Mode.Boogie) return; - - foreach (var proc in program.Procedures) - { - if (!proc.Name.StartsWith(recordProcName)) continue; - Contract.Assert(proc.InParams.Count == 1); - - // Make a new function - TypedIdent ti = new TypedIdent(Token.NoToken, "", Microsoft.Boogie.Type.Bool); - Contract.Assert(ti != null); - Formal returnVar = new Formal(Token.NoToken, ti, false); - Contract.Assert(returnVar != null); - - // Get record type - var argtype = proc.InParams[0].TypedIdent.Type; - - var ins = new List(); - ins.Add(new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "x", argtype), true)); - - var recordFunc = new Function(Token.NoToken, proc.Name, ins, returnVar); - boogieContext.DeclareFunction(recordFunc, ""); - - var exprs = new List(); - exprs.Add(new IdentifierExpr(Token.NoToken, proc.InParams[0])); - - Expr freePostExpr = new NAryExpr(Token.NoToken, new FunctionCall(recordFunc), exprs); - proc.Ensures.Add(new Ensures(true, freePostExpr)); - } - } - - private void FixedPointToSpecs(){ - - if(mode != Mode.Corral || CommandLineOptions.Clo.PrintFixedPoint == null) - return; // not implemented for other annotation modes yet - - var twr = new TokenTextWriter(CommandLineOptions.Clo.PrintFixedPoint, /*pretty=*/ false); - Dictionary pmap = new Dictionary (); - - foreach (var node in rpfp.nodes) - pmap.Add ((node.Name as VCExprBoogieFunctionOp).Func.Name, node); - - foreach (var impl in program.Implementations) - { - Contract.Assert(!impl.Name.StartsWith(recordProcName), "Not allowed to have an implementation for this guy"); - - Procedure proc = cce.NonNull(impl.Proc); - - { - StratifiedInliningInfo info = new StratifiedInliningInfo(impl, program, boogieContext, QuantifierExpr.GetNextSkolemId()); - implName2StratifiedInliningInfo[impl.Name] = info; - // We don't need controlFlowVariable for stratified Inlining - //impl.LocVars.Add(info.controlFlowVariable); - List exprs = new List(); - - { - if (pmap.ContainsKey(impl.Name)) - { - RPFP.Node node = pmap[impl.Name]; - var annot = node.Annotation; - EmitProcSpec(twr, proc, info, annot); - } - } - } - } - twr.Close (); - } - - private void EmitProcSpec(TokenTextWriter twr, Procedure proc, StratifiedInliningInfo info, RPFP.Transformer annot) - { - // last ensures clause will be the symbolic one - if (!info.isMain) - { - var ens = proc.Ensures[proc.Ensures.Count - 1]; - if (ens.Condition != Expr.False) // this is main - { - var postExpr = ens.Condition as NAryExpr; - var args = postExpr.Args; - - var ind = annot.IndParams; - var bound = new Dictionary(); - for (int i = 0; i < args.Count; i++) - { - bound[ind[i]] = args[i]; - } - var new_ens_cond = VCExprToExpr(annot.Formula, bound); - if (new_ens_cond != Expr.True) - { - var new_ens = new Ensures(false, new_ens_cond); - var enslist = new List(); - enslist.Add(new_ens); - var new_proc = new Procedure(proc.tok, proc.Name, proc.TypeParameters, proc.InParams, - proc.OutParams, new List(), new List(), enslist); - new_proc.Emit(twr, 0); - } - } - } - } - - static int ConjectureFileCounter = 0; - - private void ConjecturesToSpecs() - { - - if (mode != Mode.Corral || CommandLineOptions.Clo.PrintConjectures == null) - return; // not implemented for other annotation modes yet - - var twr = new TokenTextWriter(CommandLineOptions.Clo.PrintConjectures + "." + ConjectureFileCounter.ToString(), /*pretty=*/ false); - ConjectureFileCounter++; - - foreach (var c in rpfp.conjectures) - { - var name = c.node.Name.GetDeclName(); - if (implName2StratifiedInliningInfo.ContainsKey(name)) - { - StratifiedInliningInfo info = implName2StratifiedInliningInfo[c.node.Name.GetDeclName()]; - Implementation impl = info.impl; - Procedure proc = impl.Proc; - EmitProcSpec(twr, proc, info, c.bound); - } - } - - twr.Close (); - } - - private Term ExtractSmallerVCsRec(TermDict< Term> memo, Term t, List small, Term lbl = null) - { - Term res; - if (memo.TryGetValue(t, out res)) - return res; - var kind = t.GetKind(); - if (kind == TermKind.App) - { - var f = t.GetAppDecl(); - if (f.GetKind() == DeclKind.Implies){ - var lhs = t.GetAppArgs()[0]; - if(lhs.GetKind() == TermKind.App){ - var r = lhs.GetAppDecl(); - if (r.GetKind() == DeclKind.And) - { - Term q = t.GetAppArgs()[1]; - var lhsargs = lhs.GetAppArgs(); - for (int i = lhsargs.Length-1; i >= 0; --i) - { - q = ctx.MkImplies(lhsargs[i], q); - } - res = ExtractSmallerVCsRec(memo, q, small,lbl); - goto done; - } - if (r.GetKind() == DeclKind.Label) - { - var arg = lhs; - arg = lhs.GetAppArgs()[0]; - if (!(arg.GetKind() == TermKind.App && arg.GetAppDecl().GetKind() == DeclKind.Uninterpreted)) - goto normal; - if (!(annotationInfo.ContainsKey(arg.GetAppDecl().GetDeclName()) && annotationInfo[arg.GetAppDecl().GetDeclName()].type == AnnotationInfo.AnnotationType.LoopInvariant)) - goto normal; - var sm = ctx.MkImplies(lhs, ExtractSmallerVCsRec(memo, t.GetAppArgs()[1], small)); - if (lbl != null) - sm = ctx.MkImplies(lbl, sm); - small.Add(sm); - res = ctx.MkTrue(); - goto done; - } - if (r.GetKind() == DeclKind.Uninterpreted) - { - var arg = lhs; - if (!(annotationInfo.ContainsKey(arg.GetAppDecl().GetDeclName()) && annotationInfo[arg.GetAppDecl().GetDeclName()].type == AnnotationInfo.AnnotationType.LoopInvariant)) - goto normal; - var sm = ctx.MkImplies(lhs,ExtractSmallerVCsRec(memo,t.GetAppArgs()[1],small)); - if (lbl != null) - sm = ctx.MkImplies(lbl, sm); - small.Add(sm); - res = ctx.MkTrue(); - goto done; - } - } - normal: - Term newlbl = null; - if (lhs.IsLabel() && lhs.GetAppArgs()[0] == ctx.MkTrue()) - newlbl = lhs; - res = ctx.MkImplies(lhs,ExtractSmallerVCsRec(memo,t.GetAppArgs()[1],small,newlbl)); - } - else if (f.GetKind() == DeclKind.And) - { - res = ctx.MkApp(f,t.GetAppArgs().Select(x => ExtractSmallerVCsRec(memo, x, small)).ToArray()); - } - else - res = t; - } - else - res = t; - done: - memo.Add(t, res); - return res; - } - - private void ExtractSmallerVCs(Term t, List small){ - TermDict< Term> memo = new TermDict< Term>(); - Term top = ExtractSmallerVCsRec(memo, t, small); - small.Add(top); - } - - private Dictionary goalNumbering = new Dictionary(); - - private Term NormalizeGoal(Term goal, FuncDecl label) - { - var f = goal.GetAppDecl(); - var args = goal.GetAppArgs(); - int number; - if (!goalNumbering.TryGetValue(f, out number)) - { - number = goalNumbering.Count; - goalNumbering.Add(f, number); - } - Term[] tvars = new Term[args.Length]; - Term[] eqns = new Term[args.Length]; - AnnotationInfo info = null; - annotationInfo.TryGetValue(f.GetDeclName(), out info); - for (int i = 0; i < args.Length; i++) - { - string pname = (info == null) ? i.ToString() : info.argnames[i]; - tvars[i] = ctx.MkConst("@a" + number.ToString() + "_" + pname, args[i].GetSort()); - eqns[i] = ctx.MkEq(tvars[i], args[i]); - } - return ctx.MkImplies(ctx.MkAnd(eqns), ctx.MkApp(label, ctx.MkApp(f, tvars))); - } - - private Term MergeGoalsRec(TermDict< Term> memo, Term t) - { - Term res; - if (memo.TryGetValue(t, out res)) - return res; - var kind = t.GetKind(); - if (kind == TermKind.App) - { - var f = t.GetAppDecl(); - var args = t.GetAppArgs(); - if (f.GetKind() == DeclKind.Implies) - { - res = ctx.MkImplies(args[0], MergeGoalsRec(memo, args[1])); - goto done; - } - else if (f.GetKind() == DeclKind.And) - { - args = args.Select(x => MergeGoalsRec(memo, x)).ToArray(); - res = ctx.MkApp(f, args); - goto done; - } - else if (f.GetKind() == DeclKind.Label) - { - var arg = t.GetAppArgs()[0]; - var r = arg.GetAppDecl(); - if (r.GetKind() == DeclKind.Uninterpreted) - { - res = NormalizeGoal(arg, f); - goto done; - } - } - } - res = t; - done: - memo.Add(t, res); - return res; - } - - private Term MergeGoals(Term t) - { - TermDict< Term> memo = new TermDict< Term>(); - return MergeGoalsRec(memo, t); - } - - private Term CollectGoalsRec(TermDict< Term> memo, Term t, List goals, List cruft) - { - Term res; - if (memo.TryGetValue(t, out res)) - return res; - var kind = t.GetKind(); - if (kind == TermKind.App) - { - var f = t.GetAppDecl(); - if (f.GetKind() == DeclKind.Implies) - { - CollectGoalsRec(memo, t.GetAppArgs()[1], goals, cruft); - goto done; - } - else if (f.GetKind() == DeclKind.And) - { - foreach (var arg in t.GetAppArgs()) - { - CollectGoalsRec(memo, arg, goals, cruft); - } - goto done; - } - else if (f.GetKind() == DeclKind.Label) - { - var arg = t.GetAppArgs()[0]; - if (arg.GetKind() == TermKind.App && arg.GetAppDecl().GetKind() == DeclKind.Uninterpreted) - { - var r = arg.GetAppDecl(); - if (memo.TryGetValue(arg, out res)) - goto done; - if (!annotationInfo.ContainsKey(r.GetDeclName()) && !arg.GetAppDecl().GetDeclName().StartsWith("_solve_")) - goto done; - goals.Add(arg); - memo.Add(arg, arg); - goto done; - } - else - return CollectGoalsRec(memo, arg, goals, cruft); - } - else if (f.GetKind() == DeclKind.Uninterpreted) - { - string name = f.GetDeclName(); - if (name.StartsWith("_solve_")) - { - if (memo.TryGetValue(t, out res)) - goto done; - goals.Add(t); - memo.Add(t, t); - return t; - } - } - } - // else the goal must be cruft - cruft.Add(t); - done: - res = t; // just to return something - memo.Add(t, res); - return res; - } - - private void CollectGoals(Term t, List goals, List cruft) - { - TermDict< Term> memo = new TermDict< Term>(); - CollectGoalsRec(memo, t.GetAppArgs()[1], goals, cruft); - } - - private Term SubstRec(TermDict< Term> memo, Term t) - { - Term res; - if (memo.TryGetValue(t, out res)) - return res; - var kind = t.GetKind(); - if (kind == TermKind.App) - { - // var f = t.GetAppDecl(); - var args = t.GetAppArgs().Select(x => SubstRec(memo, x)).ToArray(); - res = ctx.CloneApp(t, args); - } - else res = t; - memo.Add(t, res); - return res; - } - - private Term SubstRecGoals(TermDict< Term> memo, Term t) - { - Term res; - if (memo.TryGetValue(t, out res)) - return res; - var kind = t.GetKind(); - if (kind == TermKind.App) - { - var f = t.GetAppDecl(); - var args = t.GetAppArgs(); - if (f.GetKind() == DeclKind.Implies){ - res = SubstRecGoals(memo, args[1]); - if (res != ctx.MkTrue()) - res = ctx.MkImplies(args[0],res); - goto done; - } - else if (f.GetKind() == DeclKind.And) - { - args = args.Select(x => SubstRecGoals(memo, x)).ToArray(); - args = args.Where(x => x != ctx.MkTrue()).ToArray(); - res = ctx.MkAnd(args); - goto done; - } - else if (f.GetKind() == DeclKind.Label) - { - var arg = t.GetAppArgs()[0]; - if (arg.GetKind() == TermKind.App && arg.GetAppDecl().GetKind() == DeclKind.Uninterpreted) - { - var r = arg.GetAppDecl(); - if (memo.TryGetValue(arg, out res)) - { - if(res != ctx.MkTrue()) - res = ctx.MkApp(f, res); - goto done; - } - } - else - { - res = ctx.MkApp(f, SubstRecGoals(memo, arg)); - goto done; - } - - } - // what's left could be cruft! - if (memo.TryGetValue(t, out res)) - { - goto done; - } - } - res = t; - done: - memo.Add(t, res); - return res; - } - - private void FactorVCs(Term t, List vcs) - { - List small = new List(); - ExtractSmallerVCs(t, small); - foreach (var smm in small) - { - List goals = new List(); - List cruft = new List(); - var sm = largeblock ? MergeGoals(smm) : smm; - CollectGoals(sm, goals,cruft); - foreach (var goal in goals) - { - TermDict< Term> memo = new TermDict< Term>(); - foreach (var othergoal in goals) - memo.Add(othergoal, othergoal.Equals(goal) ? ctx.MkFalse() : ctx.MkTrue()); - foreach (var thing in cruft) - memo.Add(thing, ctx.MkTrue()); - var vc = SubstRecGoals(memo, sm); - vc = ctx.MkImplies(ctx.MkNot(vc), goal); - vcs.Add(vc); - } - { - TermDict< Term> memo = new TermDict< Term>(); - foreach (var othergoal in goals) - memo.Add(othergoal, ctx.MkTrue()); - var vc = SubstRecGoals(memo, sm); - if (vc != ctx.MkTrue()) - { - vc = ctx.MkImplies(ctx.MkNot(vc), ctx.MkFalse()); - vcs.Add(vc); - } - } - } - } - - - - private void GenerateVCForStratifiedInlining(Program program, StratifiedInliningInfo info, Checker checker) - { - Contract.Requires(program != null); - Contract.Requires(info != null); - Contract.Requires(checker != null); - Contract.Requires(info.impl != null); - Contract.Requires(info.impl.Proc != null); - - - - Implementation impl = info.impl; - if (mode == Mode.Boogie && style == AnnotationStyle.Flat && impl.Name != main_proc_name) - return; - Contract.Assert(impl != null); - ConvertCFG2DAG(impl,edgesCut); - VC.ModelViewInfo mvInfo; - PassifyImpl(impl, out mvInfo); - Dictionary label2absy = null; - VCExpressionGenerator gen = checker.VCExprGen; - Contract.Assert(gen != null); - VCExpr vcexpr; - if(NoLabels){ - // int assertionCount = 0; - VCExpr startCorrect = null; /* VC.VCGen.LetVC(cce.NonNull(impl.Blocks[0]), null, null, info.blockVariables, info.bindings, - info.ctxt, out assertionCount); */ - vcexpr = gen.Let(info.bindings, startCorrect); - } - else vcexpr = GenerateVC(impl, null /* info.controlFlowVariable */, out label2absy, info.ctxt); - if(mode != Mode.Boogie) - vcexpr = gen.Not(vcexpr); - Contract.Assert(vcexpr != null); - info.label2absy = label2absy; - info.mvInfo = mvInfo; - List interfaceExprs = new List(); - - if (true /* was: !info.isMain */) - { - Boogie2VCExprTranslator translator = checker.TheoremProver.Context.BoogieExprTranslator; - Contract.Assert(translator != null); - info.privateVars = new List(); - foreach (Variable v in impl.LocVars) - { - Contract.Assert(v != null); - info.privateVars.Add(translator.LookupVariable(v)); - } - foreach (Variable v in impl.OutParams) - { - Contract.Assert(v != null); - info.privateVars.Add(translator.LookupVariable(v)); - } - - info.interfaceExprVars = new List(); - - foreach (Variable v in info.interfaceVars) - { - Contract.Assert(v != null); - VCExprVar ev = translator.LookupVariable(v); - Contract.Assert(ev != null); - info.interfaceExprVars.Add(ev); - interfaceExprs.Add(ev); - } - } - - Function function = cce.NonNull(info.function); - Contract.Assert(function != null); - info.funcExpr = gen.Function(function, interfaceExprs); - info.vcexpr = vcexpr; - - if (mode == Mode.Boogie) - { - Term z3vc = boogieContext.VCExprToTerm(vcexpr, linOptions); - FactorVCs(z3vc, DualityVCs); - } - else - { - // Index the procedures by relational variable - FuncDecl R = boogieContext.VCExprToTerm(info.funcExpr, linOptions).GetAppDecl(); - relationToProc.Add(R, info); - info.node = rpfp.CreateNode(boogieContext.VCExprToTerm(info.funcExpr, linOptions)); - rpfp.nodes.Add(info.node); - if (info.isMain || QKeyValue.FindBoolAttribute(impl.Attributes, "entrypoint")) - info.node.Bound.Formula = ctx.MkFalse(); - } - } - - // This returns a new FuncDel with same sort as top-level function - // of term t, but with numeric suffix appended to name. - - private FuncDecl SuffixFuncDecl(Term t, int n) - { - var name = t.GetAppDecl().GetDeclName() + "_" + n.ToString(); - return ctx.MkFuncDecl(name, t.GetAppDecl()); - } - - // Collect the relational paremeters - - private Term CollectParamsRec(TermDict memo, Term t, List parms, List nodes, Dictionary done) - { - Term res; - if (memo.TryGetValue(t, out res)) - return res; - var kind = t.GetKind(); - if (kind == TermKind.App) - { - var f = t.GetAppDecl(); - var args = t.GetAppArgs(); - args = args.Select(x => CollectParamsRec(memo, x, parms, nodes, done)).ToArray(); - StratifiedInliningInfo info; - if (relationToProc.TryGetValue(f, out info)) - { - if (done.ContainsKey(t)) - res = done[t]; - else - { - f = SuffixFuncDecl(t, parms.Count); - parms.Add(f); - nodes.Add(info.node); - res = ctx.MkApp(f, args); - done.Add(t,res); // don't count same expression twice! - } - } - else - res = ctx.CloneApp(t, args); - } // TODO: handle quantifiers - else res = t; - memo.Add(t, res); - return res; - } - - public void GetTransformer(StratifiedInliningInfo info) - { - Term vcTerm = boogieContext.VCExprToTerm(info.vcexpr, linOptions); - Term[] paramTerms = info.interfaceExprVars.Select(x => boogieContext.VCExprToTerm(x, linOptions)).ToArray(); - var relParams = new List(); - var nodeParams = new List(); - var memo = new TermDict< Term>(); - var done = new Dictionary(); // note this hashes on equality, not reference! - vcTerm = CollectParamsRec(memo, vcTerm, relParams, nodeParams,done); - // var ops = new Util.ContextOps(ctx); - // var foo = ops.simplify_lhs(vcTerm); - // vcTerm = foo.Item1; - info.F = rpfp.CreateTransformer(relParams.ToArray(), paramTerms, vcTerm); - info.edge = rpfp.CreateEdge(info.node, info.F, nodeParams.ToArray()); - rpfp.edges.Add(info.edge); - // TODO labels[info.edge.number] = foo.Item2; - } - - public RPFP.Node GetNodeOfImpl(Implementation/*!*/ impl) - { - return implName2StratifiedInliningInfo[impl.Name].node; - } - - public class CyclicLiveVariableAnalysis : Microsoft.Boogie.LiveVariableAnalysis - { - public new static void ComputeLiveVariables(Implementation impl) - { - - bool some_change = true; - List sortedNodes = new List(); - foreach (var block in impl.Blocks) - { - sortedNodes.Add(block); - } - sortedNodes.Reverse(); - - while (some_change) - { - some_change = false; - foreach (Block/*!*/ block in sortedNodes) - { - Contract.Assert(block != null); - HashSet/*!*/ liveVarsAfter = new HashSet(); - if (block.TransferCmd is GotoCmd) - { - GotoCmd gotoCmd = (GotoCmd)block.TransferCmd; - if (gotoCmd.labelTargets != null) - { - foreach (Block/*!*/ succ in gotoCmd.labelTargets) - { - Contract.Assert(succ != null); - if (succ.liveVarsBefore != null) - liveVarsAfter.UnionWith(succ.liveVarsBefore); - } - } - } - - List cmds = block.Cmds; - int len = cmds.Count; - for (int i = len - 1; i >= 0; i--) - { - if (cmds[i] is CallCmd) - { - Procedure/*!*/ proc = cce.NonNull(cce.NonNull((CallCmd/*!*/)cmds[i]).Proc); - if (InterProcGenKill.HasSummary(proc.Name)) - { - liveVarsAfter = - InterProcGenKill.PropagateLiveVarsAcrossCall(cce.NonNull((CallCmd/*!*/)cmds[i]), liveVarsAfter); - continue; - } - } - Propagate(cmds[i], liveVarsAfter); - } - - if (block.liveVarsBefore == null) - block.liveVarsBefore = new HashSet(); - if (!liveVarsAfter.IsSubsetOf(block.liveVarsBefore)) - { - block.liveVarsBefore = liveVarsAfter; - some_change = true; - } - } - } - } - } - - public void Generate() - { - - var oldDagOption = CommandLineOptions.Clo.vcVariety; - CommandLineOptions.Clo.vcVariety = CommandLineOptions.VCVariety.Dag; - - // MarkAllFunctionImplementationsInline(); // This is for SMACK, which goes crazy with functions - - // Run live variable analysis (TODO: should this be here?) -#if false - if (CommandLineOptions.Clo.LiveVariableAnalysis == 2) - { - Microsoft.Boogie.InterProcGenKill.ComputeLiveVars(impl, program); - } -#endif - - #region In Boogie mode, annotate the program - if (mode == Mode.Boogie) - { - - // find the name of the main procedure - main_proc_name = null; // default in case no entry point defined - foreach (var impl in program.Implementations) - { - if (QKeyValue.FindBoolAttribute(impl.Attributes, "entrypoint")) - main_proc_name = impl.Proc.Name; - } - if (main_proc_name == null) - { - foreach (var impl in program.Implementations) - { - if (impl.Proc.Name == "main" || impl.Proc.Name.EndsWith(".main")) - main_proc_name = impl.Proc.Name; - } - } - if (main_proc_name == null) - main_proc_name = "main"; - - if (style == AnnotationStyle.Flat) - { - InlineAll(); - Microsoft.Boogie.BlockCoalescer.CoalesceBlocks(program); - foreach (var impl in program.Implementations) - { - if (main_proc_name == impl.Proc.Name) - { - Microsoft.Boogie.LiveVariableAnalysis.ClearLiveVariables(impl); - CyclicLiveVariableAnalysis.ComputeLiveVariables(impl); - AnnotateLoops(impl, boogieContext); - } - } - } - else - { - - if (style == AnnotationStyle.Procedure || style == AnnotationStyle.Call) - { - foreach (var impl in program.Implementations) - { - if (!QKeyValue.FindBoolAttribute(impl.Attributes, "entrypoint")) - AnnotateProcRequires(impl.Proc, impl, boogieContext); - AnnotateProcEnsures(impl.Proc, impl, boogieContext); - } - if (style == AnnotationStyle.Call) - { - - } - } - - // must do this after annotating procedures, else calls - // will be prematurely desugared - - foreach (var impl in program.Implementations) - { - Microsoft.Boogie.LiveVariableAnalysis.ClearLiveVariables(impl); - CyclicLiveVariableAnalysis.ComputeLiveVariables(impl); - } - - - if (style == AnnotationStyle.Flat || style == AnnotationStyle.Call) - { - foreach (var impl in program.Implementations) - { - AnnotateLoops(impl, boogieContext); - } - } - if (style == AnnotationStyle.Call) - { - Dictionary impls = new Dictionary(); - foreach (var impl in program.Implementations) - { - impls.Add(impl.Proc.Name, true); - } - foreach (var impl in program.Implementations) - { - AnnotateCallSites(impl, boogieContext, impls); - } - } - if (style == AnnotationStyle.Flat) - InlineAll(); - } - } - #endregion - - /* Generate the VC's */ - GenerateVCsForStratifiedInlining(); - - /* Generate the background axioms */ - Term background = ctx.MkTrue(); // TODO boogieContext.VCExprToTerm(boogieContext.Axioms, linOptions); - rpfp.AssertAxiom(background); - - int save_option = CommandLineOptions.Clo.StratifiedInlining; // need this to get funcall labels - CommandLineOptions.Clo.StratifiedInlining = 1; - - /* Create the nodes, indexing procedures by their relational symbols. */ - foreach (StratifiedInliningInfo info in implName2StratifiedInliningInfo.Values) - GenerateVCForStratifiedInlining(program, info, checker); - - CommandLineOptions.Clo.StratifiedInlining = save_option; - - if (mode == Mode.Boogie) - { - // var ops = new Util.ContextOps(ctx); - var vcs = DualityVCs; - DualityVCs = new List(); - foreach (var vc in vcs) - { - // var foo = ops.simplify_lhs(vc.GetAppArgs()[0]); - var foo = vc.GetAppArgs()[0]; - if (!foo.IsFalse()) - DualityVCs.Add(ctx.MkImplies(foo, vc.GetAppArgs()[1])); - } - - rpfp.FromClauses(DualityVCs.ToArray()); - // TODO rpfp.HornClauses = style == AnnotationStyle.Flat; - } - else - { - /* Generate the edges. */ - foreach (StratifiedInliningInfo info in implName2StratifiedInliningInfo.Values) - GetTransformer(info); - } - - // save some information for debugging purposes - // TODO rpfp.ls.SetAnnotationInfo(annotationInfo); - - CommandLineOptions.Clo.vcVariety = oldDagOption; - } - - - private class ErrorHandler : ProverInterface.ErrorHandler - { - //TODO: anything we need to handle? - } - - Dictionary> varSubst = null; - - /** Check the RPFP, and return a counterexample if there is one. */ - - public VC.ConditionGeneration.Outcome Check(ref RPFP.Node cexroot) - { - var start = DateTime.Now; - - ErrorHandler handler = new ErrorHandler(); - RPFP.Node cex; - varSubst = new Dictionary>(); - -#if false - int origRecursionBound = CommandLineOptions.Clo.RecursionBound; - if (CommandLineOptions.Clo.RecursionBound > 0 && extraRecBound != null) - { - int maxExtra = 0; - foreach (string s in extraRecBound.Keys) - { - int extra = extraRecBound[s]; - if (extra > maxExtra) maxExtra = extra; - } - CommandLineOptions.Clo.RecursionBound += maxExtra; - } -#endif - - ProverInterface.Outcome outcome = - checker.TheoremProver.CheckRPFP("name", rpfp, handler, out cex, varSubst, extraRecBound); - cexroot = cex; - -#if false - CommandLineOptions.Clo.RecursionBound = origRecursionBound; -#endif - - Console.WriteLine("solve: {0}s", (DateTime.Now - start).TotalSeconds); - - switch(outcome) - { - case ProverInterface.Outcome.Valid: - return VC.ConditionGeneration.Outcome.Correct; - case ProverInterface.Outcome.Bounded: - return VC.ConditionGeneration.Outcome.ReachedBound; - case ProverInterface.Outcome.Invalid: - return VC.ConditionGeneration.Outcome.Errors; - case ProverInterface.Outcome.TimeOut: - return VC.ConditionGeneration.Outcome.TimedOut; - default: - return VC.ConditionGeneration.Outcome.Inconclusive; - } - } - - private bool generated = false; - - static private Object thisLock = new Object(); - - public override VC.VCGen.Outcome VerifyImplementation(Implementation impl, VerifierCallback collector) - { - - lock (thisLock) - { - Procedure proc = impl.Proc; - - // we verify all the impls at once, so we need to execute only once - // TODO: make sure needToCheck is true only once - bool needToCheck = false; - if (mode == Mode.OldCorral) - needToCheck = proc.FindExprAttribute("inline") == null && !(proc is LoopProcedure); - else if (mode == Mode.Corral || mode == Mode.Boogie) - needToCheck = QKeyValue.FindBoolAttribute(impl.Attributes, "entrypoint") && !(proc is LoopProcedure); - else - needToCheck = impl.Name == main_proc_name; - - if (needToCheck) - { - - var start = DateTime.Now; - - if (!generated) - { - Generate(); - Console.WriteLine("generate: {0}s", (DateTime.Now - start).TotalSeconds); - generated = true; - } - - - Console.WriteLine("Verifying {0}...", impl.Name); - - RPFP.Node cexroot = null; - // start = DateTime.Now; - var checkres = Check(ref cexroot); - Console.WriteLine("check: {0}s", (DateTime.Now - start).TotalSeconds); - switch (checkres) - { - case Outcome.Errors: - Console.WriteLine("Counterexample found.\n"); - // start = DateTime.Now; - Counterexample cex = CreateBoogieCounterExample(cexroot.owner, cexroot, impl); - // cexroot.owner.DisposeDualModel(); - // cex.Print(0); // just for testing - collector.OnCounterexample(cex, "assertion failure"); - Console.WriteLine("cex: {0}s", (DateTime.Now - start).TotalSeconds); - ConjecturesToSpecs(); - break; - case Outcome.Correct: - Console.WriteLine("Procedure is correct. (fixed point reached)"); - FixedPointToSpecs(); - ConjecturesToSpecs(); - break; - case Outcome.ReachedBound: - Console.WriteLine("Procedure is correct. (recursion bound reached)"); - FixedPointToSpecs(); - ConjecturesToSpecs(); - break; - default: - Console.WriteLine("Inconclusive result."); - ConjecturesToSpecs(); - break; - } - return checkres; - - } - - return Outcome.Inconclusive; - } - } - - public void FindLabelsRec(HashSet memo, Term t, Dictionary res) - { - if (memo.Contains(t)) - return; - if (t.IsLabel()) - { - string l = t.LabelName(); - if (!res.ContainsKey(l)) - res.Add(l, t.GetAppArgs()[0]); - } - if (t.GetKind() == TermKind.App) - { - var args = t.GetAppArgs(); - foreach (var a in args) - FindLabelsRec(memo, a, res); - } // TODO: handle quantifiers - - memo.Add(t); - } - - public void FindLabels() - { - labels = new Dictionary(); - foreach(var e in rpfp.edges){ - int id = e.number; - HashSet memo = new HashSet(ReferenceComparer.Instance); - FindLabelsRec(memo, e.F.Formula, labels); - } - } - - public string CodeLabel(Absy code, StratifiedInliningInfo info, string prefix) - { - if (info.label2absyInv == null) - { - info.label2absyInv = new Dictionary(); - foreach (int foo in info.label2absy.Keys) - { - Absy bar = info.label2absy[foo] as Absy; - string lbl = foo.ToString(); - info.label2absyInv.Add(bar, lbl); - } - } - if (info.label2absyInv.ContainsKey(code)) - { - string label = info.label2absyInv[code]; - return prefix+label; - } - return null; - } - - public Term CodeLabeledExpr(RPFP rpfp, RPFP.Node root, Absy code, StratifiedInliningInfo info, string prefix) - { - string label = CodeLabel(code, info, prefix); - - if (label != null) - { - var res = labels[label]; - return res; - } - else return null; - } - - public class LabelNotFound : Exception { }; - - public bool CodeLabelTrue(RPFP rpfp, RPFP.Node root, Absy code, StratifiedInliningInfo info, string prefix) - { - string label = CodeLabel(code, info, prefix); - - if (label == null) - throw new LabelNotFound(); - return root.Outgoing.labels.Contains(label); - } - - public bool CodeLabelFalse(RPFP rpfp, RPFP.Node root, Absy code, StratifiedInliningInfo info, string prefix) - { - return CodeLabelTrue(rpfp, root, code, info, prefix); - } - - - private class StateId - { - public RPFP.Edge edge; - public int capturePoint; - public StratifiedInliningInfo info; - public StateId(RPFP.Edge e, int c, StratifiedInliningInfo i) - { - edge = e; - capturePoint = c; - info = i; - } - } - - - public Counterexample CreateBoogieCounterExample(RPFP rpfp, RPFP.Node root, Implementation mainImpl) - { - FindLabels(); - var orderedStateIds = new List(); - Counterexample newCounterexample = - GenerateTrace(rpfp, root, orderedStateIds, mainImpl,true); - if (CommandLineOptions.Clo.ModelViewFile != null) - { - Model m = root.owner.GetBackgroundModel(); - GetModelWithStates(m, root, implName2StratifiedInliningInfo[mainImpl.Name], - orderedStateIds, varSubst); - newCounterexample.Model = m; - newCounterexample.ModelHasStatesAlready = true; - } - return newCounterexample; - } - - - - private Counterexample GenerateTrace(RPFP rpfp, RPFP.Node root, - List orderedStateIds, Implementation procImpl, bool toplevel) - { - Contract.Requires(procImpl != null); - - Contract.Assert(!rpfp.Empty(root)); - - - var info = implName2StratifiedInliningInfo[procImpl.Name]; - Block entryBlock = cce.NonNull(procImpl.Blocks[0]); - Contract.Assert(entryBlock != null); - - List trace = new List(); - trace.Add(entryBlock); - - var calleeCounterexamples = new Dictionary(); - - Counterexample newCounterexample = - GenerateTraceRec(rpfp, root, orderedStateIds, entryBlock, trace, calleeCounterexamples, info, toplevel); - - return newCounterexample; - } - - // TODO: this is a bit cheesy. Rather than finding the argument position - // of a relational term in a transformer by linear search, better to index this - // somewhere, but where? - private int TransformerArgPosition(RPFP rpfp, RPFP.Node root, Term expr) - { - FuncDecl rel = expr.GetAppDecl(); - string relname = rel.GetDeclName(); - var rps = root.Outgoing.F.RelParams; - for (int i = 0; i < rps.Length; i++) - { - string thisname = rps[i].GetDeclName(); - if (thisname == relname) - return i; - } - return -1; - } - - private bool EvalToFalse(RPFP rpfp, RPFP.Node root, Term expr,StratifiedInliningInfo info){ - Term res = rpfp.Eval(root.Outgoing,expr); - return res.Equals(ctx.MkTrue()); - } - - private Counterexample GenerateTraceRec( - RPFP rpfp, RPFP.Node root, - List orderedStateIds, - Block/*!*/ b, List/*!*/ trace, - Dictionary/*!*/ calleeCounterexamples, - StratifiedInliningInfo info, - bool toplevel) - { - Contract.Requires(b != null); - Contract.Requires(trace != null); - Contract.Requires(cce.NonNullDictionaryAndValues(calleeCounterexamples)); - - Stack continuation_stack = new Stack(); - - // If our block is not present, try diving into precondition - // and push a continuation. - // TODO: is the precondition always the first child? - while (!CodeLabelFalse(rpfp, root, b, info, "+")) - { - if (root.Outgoing != null && root.Outgoing.Children.Length > 0) - { - continuation_stack.Push(root); - root = root.Outgoing.Children[0]; - } - else - { - // can't find our block - Contract.Assert(false); - return null; - } - } - - // After translation, all potential errors come from asserts. - while (true) - { - - - List cmds = b.Cmds; - TransferCmd transferCmd = cce.NonNull(b.TransferCmd); - for (int i = 0; i < cmds.Count; i++) - { - Cmd cmd = cce.NonNull(cmds[i]); - - // Skip if 'cmd' not contained in the trace or not an assert - if (cmd is AssertCmd) - { - bool is_failed_assertion = false; - if (NoLabels) - is_failed_assertion = true; // we assume only assertions on - else - is_failed_assertion = CodeLabelTrue(rpfp, root, cmd, info, "@"); - - if (is_failed_assertion) - { - if (continuation_stack.Count == 0) - { - Counterexample newCounterexample = - AssertCmdToCounterexample((AssertCmd)cmd, transferCmd, trace, new Microsoft.Boogie.Model(), info.mvInfo, - boogieContext); - newCounterexample.AddCalleeCounterexample(calleeCounterexamples); - return newCounterexample; - } - root = continuation_stack.Pop(); - } - continue; - } - - // Counterexample generation for inlined procedures - AssumeCmd assumeCmd = cmd as AssumeCmd; - if (assumeCmd == null) - continue; - NAryExpr naryExpr = assumeCmd.Expr as NAryExpr; - if (naryExpr == null) - continue; - string calleeName = naryExpr.Fun.FunctionName; - Contract.Assert(calleeName != null); - - // what is this crap??? - BinaryOperator binOp = naryExpr.Fun as BinaryOperator; - if (binOp != null && binOp.Op == BinaryOperator.Opcode.And) - { - Expr expr = naryExpr.Args[0]; - NAryExpr mvStateExpr = expr as NAryExpr; - if (mvStateExpr != null && mvStateExpr.Fun.FunctionName == VC.ModelViewInfo.MVState_FunctionDef.Name) - { - LiteralExpr x = mvStateExpr.Args[1] as LiteralExpr; - // Debug.Assert(x != null); - int foo = x.asBigNum.ToInt; - orderedStateIds.Add(new StateId(root.Outgoing,foo,info)); - } - } - - if (calleeName.EndsWith("_summary")) - calleeName = calleeName.Substring(0, calleeName.Length - 8); - - if (!implName2StratifiedInliningInfo.ContainsKey(calleeName) && !calleeName.EndsWith("_summary")) - continue; - - { - Term code = CodeLabeledExpr(rpfp, root, cmd, info, "+si_fcall_"); - int pos = TransformerArgPosition(rpfp, root, code); - if (pos >= 0) - { - RPFP.Node callee = root.Outgoing.Children[pos]; - orderedStateIds.Add(new StateId(callee.Outgoing, CALL,info)); - calleeCounterexamples[new TraceLocation(trace.Count - 1, i)] = - new CalleeCounterexampleInfo( - cce.NonNull(GenerateTrace(rpfp, callee, orderedStateIds, - implName2StratifiedInliningInfo[calleeName].impl, false)), - new List()); - orderedStateIds.Add(new StateId(root.Outgoing, RETURN,info)); - } - } - } - - GotoCmd gotoCmd = transferCmd as GotoCmd; - List cuts = null; - if (edgesCut.ContainsKey(b)) - cuts = edgesCut[b]; - b = null; - - if (gotoCmd != null) - { - - foreach (Block bb in cce.NonNull(gotoCmd.labelTargets)) - { - Contract.Assert(bb != null); - if (CodeLabelFalse(rpfp, root, bb, info, "+")) - { - trace.Add(bb); - b = bb; - break; - } - } - if (b != null) continue; - } - // HACK: we have to try edges that were cut in generating the VC - - if (cuts != null) - foreach (var bb in cuts) - { - if (CodeLabelFalse(rpfp, root, bb, info, "+")) - { - trace.Add(bb); - b = bb; - break; - } - } - if (b != null) continue; - - return null; - } - - - } - - public override Counterexample extractLoopTrace(Counterexample cex, string mainProcName, Program program, Dictionary> extractLoopMappingInfo) - { - // Construct the set of inlined procs in the original program - var inlinedProcs = new HashSet(); - foreach (var decl in program.TopLevelDeclarations) - { - // Implementations - if (decl is Implementation) - { - var impl = decl as Implementation; - if (!(impl.Proc is LoopProcedure)) - { - inlinedProcs.Add(impl.Name); - } - } - - // And recording procedures - if (decl is Procedure) - { - var proc = decl as Procedure; - if (proc.Name.StartsWith(recordProcName)) - { - // Debug.Assert(!(decl is LoopProcedure)); - inlinedProcs.Add(proc.Name); - } - } - } - return extractLoopTraceRec( - new CalleeCounterexampleInfo(cex, new List()), - mainProcName, inlinedProcs, extractLoopMappingInfo).counterexample; - } - - protected override bool elIsLoop(string procname) - { - StratifiedInliningInfo info = null; - if (implName2StratifiedInliningInfo.ContainsKey(procname)) - { - info = implName2StratifiedInliningInfo[procname]; - } - - if (info == null) return false; - - var lp = info.impl.Proc as LoopProcedure; - - if (lp == null) return false; - return true; - } - - private void NumberCexEdges(RPFP.Node node, Dictionary map) - { - if (node.Outgoing == null) - return; // shouldn't happen - RPFP.Edge edge = node.Outgoing; - map[edge.number] = edge; - foreach (var c in edge.Children) - NumberCexEdges(c, map); - } - - private void GetModelWithStates(Model m, RPFP.Node cex, StratifiedInliningInfo mainInfo, - List orderedStateIds, - Dictionary> varSubst) - { - if (m == null) return; - var mvInfo = mainInfo.mvInfo; - - - foreach (Variable v in mvInfo.AllVariables) - { - m.InitialState.AddBinding(v.Name, GetModelValue(m, v, varSubst[cex.Outgoing.number])); - } - - Dictionary edgeNumbering = new Dictionary(); - NumberCexEdges(cex, edgeNumbering); - - int lastCandidate = 0; - int lastCapturePoint = CALL; - for (int i = 0; i < orderedStateIds.Count; ++i) - { - var s = orderedStateIds[i]; - RPFP.Edge edge = s.edge; - int candidate = edge.number; - int capturePoint = s.capturePoint; - Dictionary subst = varSubst[candidate]; - - string implName = edge.Parent.Name.GetDeclName(); - var info = s.info.mvInfo; - - if (capturePoint == CALL || capturePoint == RETURN) - { - lastCandidate = candidate; - lastCapturePoint = capturePoint; - continue; - } - - Contract.Assume(0 <= capturePoint && capturePoint < info.CapturePoints.Count); - VC.ModelViewInfo.Mapping map = info.CapturePoints[capturePoint]; - var prevInc = (lastCapturePoint != CALL && lastCapturePoint != RETURN && candidate == lastCandidate) - ? info.CapturePoints[lastCapturePoint].IncarnationMap : new Dictionary(); - var cs = m.MkState(map.Description); - - foreach (Variable v in info.AllVariables) - { - var e = (Expr)map.IncarnationMap[v]; - - if (e == null) - { - if (lastCapturePoint == CALL || lastCapturePoint == RETURN) - { - cs.AddBinding(v.Name, GetModelValue(m, v, subst)); - } - continue; - } - - if (lastCapturePoint != CALL && lastCapturePoint != RETURN && prevInc[v] == e) continue; // skip unchanged variables - - Model.Element elt; - if (e is IdentifierExpr) - { - IdentifierExpr ide = (IdentifierExpr)e; - elt = GetModelValue(m, ide.Decl, subst); - } - else if (e is LiteralExpr) - { - LiteralExpr lit = (LiteralExpr)e; - elt = m.MkElement(lit.Val.ToString()); - } - else - { - Contract.Assume(false); - elt = m.MkFunc(e.ToString(), 0).GetConstant(); - } - cs.AddBinding(v.Name, elt); - } - - lastCandidate = candidate; - lastCapturePoint = capturePoint; - } - - return; - } - - - public readonly static int CALL = -1; - public readonly static int RETURN = -2; - - private Model.Element GetModelValue(Model m, Variable v, Dictionary subst) - { - // first, get the unique name - string uniqueName; - - VCExprVar vvar = boogieContext.BoogieExprTranslator.TryLookupVariable(v); - - uniqueName = v.Name; - - if(subst.ContainsKey(uniqueName)) - return m.MkElement(subst[uniqueName]); - return m.MkFunc("@undefined", 0).GetConstant(); - } - - class InternalError : Exception { - } - - - private BinaryOperator.Opcode VCOpToOp (VCExprOp op) - { - if (op == VCExpressionGenerator.AddIOp) - return BinaryOperator.Opcode.Add; - if (op == VCExpressionGenerator.SubIOp) - return BinaryOperator.Opcode.Sub; - if (op == VCExpressionGenerator.MulIOp) - return BinaryOperator.Opcode.Mul; - if (op == VCExpressionGenerator.DivIOp) - return BinaryOperator.Opcode.Div; - if (op == VCExpressionGenerator.EqOp) - return BinaryOperator.Opcode.Eq; - if (op == VCExpressionGenerator.LeOp) - return BinaryOperator.Opcode.Le; - if (op == VCExpressionGenerator.LtOp) - return BinaryOperator.Opcode.Lt; - if (op == VCExpressionGenerator.GeOp) - return BinaryOperator.Opcode.Ge; - if (op == VCExpressionGenerator.GtOp) - return BinaryOperator.Opcode.Gt; - if (op == VCExpressionGenerator.AndOp) - return BinaryOperator.Opcode.And; - if (op == VCExpressionGenerator.OrOp) - return BinaryOperator.Opcode.Or; - throw new InternalError(); - } - - private Expr MakeBinary (BinaryOperator.Opcode op, List args) - { - if(args.Count == 0){ - // with zero args we need the identity of the op - switch(op){ - case BinaryOperator.Opcode.And: - return Expr.True; - case BinaryOperator.Opcode.Or: - return Expr.False; - case BinaryOperator.Opcode.Add: - return new LiteralExpr(Token.NoToken,Microsoft.Basetypes.BigNum.ZERO); - default: - throw new InternalError(); - } - } - var temp = args[0]; - for(int i = 1; i < args.Count; i++) - temp = Expr.Binary(Token.NoToken,op,temp,args[i]); - return temp; - } - - private Variable MakeVar(VCExprVar v){ - var foo = new TypedIdent(Token.NoToken,v.Name.ToString(),v.Type); - return new BoundVariable(Token.NoToken,foo); - } - - private Expr VCExprToExpr (VCExpr e, Dictionary bound) - { - if (e is VCExprVar) { - if(bound.ContainsKey(e)) - return bound[e]; - return Expr.Ident(MakeVar(e as VCExprVar)); // TODO: this isn't right - } - if (e is VCExprIntLit) { - var n = e as VCExprIntLit; - return new LiteralExpr(Token.NoToken,n.Val); - } - if (e is VCExprNAry) { - var f = e as VCExprNAry; - var args = new List(); - for(int i = 0; i < f.Arity; i++){ - args.Add (VCExprToExpr (f[i],bound)); - } - - if(f.Op == VCExpressionGenerator.NotOp) - return Expr.Unary(Token.NoToken, UnaryOperator.Opcode.Not, args[0]); - - if(f.Op == VCExpressionGenerator.IfThenElseOp) - return new NAryExpr(Token.NoToken,new IfThenElse(Token.NoToken),args); - - if(f.Op is VCExprSelectOp){ - var idx = new List(); - idx.Add(args[1]); - return Expr.Select(args[0],idx); - } - - if(f.Op is VCExprStoreOp){ - var idx = new List(); - idx.Add(args[1]); - return Expr.Store(args[0],idx,args[2]); - } - - if (f.Op is VCExprBoogieFunctionOp) - { - return new NAryExpr(Token.NoToken, - new FunctionCall((f.Op as VCExprBoogieFunctionOp).Func), args); - } - - var op = VCOpToOp (f.Op); - return MakeBinary(op,args); - } - - if(e is VCExprQuantifier) { - var f = e as VCExprQuantifier; - var vs = new List(); - var new_bound = new Dictionary(bound); - foreach(var v in f.BoundVars){ - var ve = MakeVar(v); - vs.Add(ve); - new_bound.Add (v,Expr.Ident (ve)); - } - var bd = VCExprToExpr(f.Body,new_bound); - if(f.Quan == Quantifier.EX) - return new ExistsExpr(Token.NoToken,vs,bd); - else - return new ForallExpr(Token.NoToken,vs,bd); - } - if (e == VCExpressionGenerator.True) { - return Expr.True; - } - if (e == VCExpressionGenerator.False) { - return Expr.False; - } - if (e is VCExprLet) { - - } - - throw new InternalError(); - } - - - } - - -} +//----------------------------------------------------------------------------- +// +// Copyright (C) 2012 Microsoft Corporation. All Rights Reserved. +// +//----------------------------------------------------------------------------- + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using System.Diagnostics.Contracts; +using Microsoft.Boogie; +using Microsoft.Boogie.VCExprAST; + + +using Term = Microsoft.Boogie.VCExprAST.VCExpr; +using FuncDecl = Microsoft.Boogie.VCExprAST.VCExprOp; +using Sort = Microsoft.Boogie.Type; +using Microsoft.Boogie.ExprExtensions; + + +namespace Microsoft.Boogie +{ + public class FixedpointVC : VC.VCGen + { + + public class AnnotationInfo + { + public enum AnnotationType { LoopInvariant, ProcedureSummary }; + public string filename; + public int lineno; + public string[] argnames; + public AnnotationType type; + }; + + static bool NoLabels = false; + + // options + bool largeblock = false; + + public bool SetOption(string option, string value) + { + if (option == "LargeBlock") + { + largeblock = true; + return true; + } + return false; + } + + Context ctx; + RPFP rpfp; + // Program program; + Microsoft.Boogie.ProverContext boogieContext; + Microsoft.Boogie.VCExpressionGenerator gen; + public readonly static string recordProcName = "boogie_si_record"; // TODO: this really needed? + private Dictionary implName2StratifiedInliningInfo + = new Dictionary(); + Checker checker; + // Microsoft.Boogie.Z3.Z3InstanceOptions options = new Microsoft.Boogie.Z3.Z3InstanceOptions(); // TODO: what? + LineariserOptions linOptions; + Dictionary relationToProc = new Dictionary(); + Dictionary labels = new Dictionary (); + List DualityVCs = new List(); + Dictionary summaries = new Dictionary(); + Dictionary> edgesCut = new Dictionary>(); + string main_proc_name = "main"; + Dictionary extraRecBound = null; + + + public enum Mode { Corral, OldCorral, Boogie}; + public enum AnnotationStyle { Flat, Procedure, Call }; + + Mode mode; + AnnotationStyle style; + + private static Checker old_checker = null; + + public static void CleanUp() + { + if (old_checker != null) + { + old_checker.Close(); + old_checker = null; + } + } + + public FixedpointVC( Program _program, string/*?*/ logFilePath, bool appendLogFile, List checkers, Dictionary _extraRecBound = null) + : base(_program, logFilePath, appendLogFile, checkers) + { + switch (CommandLineOptions.Clo.FixedPointMode) + { + case CommandLineOptions.FixedPointInferenceMode.Corral: + mode = Mode.Corral; + style = AnnotationStyle.Procedure; + break; + case CommandLineOptions.FixedPointInferenceMode.OldCorral: + mode = Mode.OldCorral; + style = AnnotationStyle.Procedure; + break; + case CommandLineOptions.FixedPointInferenceMode.Flat: + mode = Mode.Boogie; + style = AnnotationStyle.Flat; + break; + case CommandLineOptions.FixedPointInferenceMode.Procedure: + mode = Mode.Boogie; + style = AnnotationStyle.Procedure; + break; + case CommandLineOptions.FixedPointInferenceMode.Call: + mode = Mode.Boogie; + style = AnnotationStyle.Call; + break; + } + ctx = new Context(); // TODO is this right? + rpfp = new RPFP(RPFP.CreateLogicSolver(ctx)); + program = _program; + gen = ctx; + if(old_checker == null) + checker = new Checker(this, program, logFilePath, appendLogFile, CommandLineOptions.Clo.ProverKillTime, null); + else { + checker = old_checker; + checker.RetargetWithoutReset(program,checker.TheoremProver.Context); + } + old_checker = checker; + boogieContext = checker.TheoremProver.Context; + linOptions = null; // new Microsoft.Boogie.Z3.Z3LineariserOptions(false, options, new List()); + extraRecBound = _extraRecBound; + } + + Dictionary annotationInfo = new Dictionary(); + + public void AnnotateLoops(Implementation impl, ProverContext ctxt) + { + Contract.Requires(impl != null); + + CurrentLocalVariables = impl.LocVars; + variable2SequenceNumber = new Dictionary(); + incarnationOriginMap = new Dictionary(); + + ResetPredecessors(impl.Blocks); + + #region Create the graph by adding the source node and each edge + GraphUtil.Graph g = Program.GraphFromImpl(impl); + #endregion + + //Graph g = program.ProcessLoops(impl); + + g.ComputeLoops(); // this is the call that does all of the processing + if (!g.Reducible) + { + throw new System.Exception("Irreducible flow graphs are unsupported."); + } + + #region add a symbolic annoation to every loop head + foreach (Block header in cce.NonNull(g.Headers)) + AnnotateBlock(impl, ctxt, header); + #endregion + } + + private void AnnotateCallSites(Implementation impl, ProverContext ctxt, Dictionary impls){ + foreach (var b in impl.Blocks) + { + foreach (var cmd in b.Cmds) + { + if (cmd is CallCmd) + { + string name = (cmd as CallCmd).callee; + if(impls.ContainsKey(name)) + goto annotate; + } + } + continue; + annotate: + AnnotateBlock(impl, ctxt, b); + } + } + + + private void AnnotateBlock(Implementation impl, ProverContext ctxt, Block header) + { + Contract.Assert(header != null); + + string name = impl.Name + "_" + header.Label + "_invar"; + if (annotationInfo.ContainsKey(name)) + return; + + // collect the variables needed in the invariant + List exprs = new List(); + List vars = new List(); + List names = new List(); + + if (style == AnnotationStyle.Flat) + { + // in flat mode, all live globals should be in live set +#if false + foreach (Variable v in program.GlobalVariables) + { + vars.Add(v); + names.Add(v.ToString()); + exprs.Add(new IdentifierExpr(Token.NoToken, v)); + } +#endif + foreach (Variable v in /* impl.LocVars */ header.liveVarsBefore) + { + if (!(v is BoundVariable)) + { + vars.Add(v); + names.Add(v.ToString()); + exprs.Add(new IdentifierExpr(Token.NoToken, v)); + } + } + } + else + { + foreach (Variable v in program.GlobalVariables) + { + vars.Add(v); + names.Add("@old_" + v.ToString()); + exprs.Add(new OldExpr(Token.NoToken, new IdentifierExpr(Token.NoToken, v))); + } + foreach (IdentifierExpr ie in impl.Proc.Modifies) + { + if (ie.Decl == null) + continue; + vars.Add(ie.Decl); + names.Add(ie.Decl.ToString()); + exprs.Add(ie); + } + foreach (Variable v in impl.Proc.InParams) + { + Contract.Assert(v != null); + vars.Add(v); + names.Add("@old_" + v.ToString()); + exprs.Add(new OldExpr(Token.NoToken, new IdentifierExpr(Token.NoToken, v))); + } + foreach (Variable v in impl.LocVars) + { + vars.Add(v); + names.Add(v.ToString()); + exprs.Add(new IdentifierExpr(Token.NoToken, v)); + } + } + + TypedIdent ti = new TypedIdent(Token.NoToken, "", Microsoft.Boogie.Type.Bool); + Contract.Assert(ti != null); + Formal returnVar = new Formal(Token.NoToken, ti, false); + Contract.Assert(returnVar != null); + var function = new Function(Token.NoToken, name, vars, returnVar); + ctxt.DeclareFunction(function, ""); + + Expr invarExpr = new NAryExpr(Token.NoToken, new FunctionCall(function), exprs); + var invarAssertion = new AssertCmd(Token.NoToken, invarExpr); + List newCmds = new List(); + newCmds.Add(invarAssertion); + + // make a record in annotationInfo; + var info = new AnnotationInfo(); + info.filename = header.tok.filename; + info.lineno = header.Line; + info.argnames = names.ToArray(); + info.type = AnnotationInfo.AnnotationType.LoopInvariant; + annotationInfo.Add(name, info); + // get file and line info from havoc, if there is... + if (header.Cmds.Count > 0) + { + PredicateCmd bif = header.Cmds[0] as PredicateCmd; + if (bif != null) + { + string foo = QKeyValue.FindStringAttribute(bif.Attributes, "sourcefile"); + if (foo != null) + info.filename = foo; + int bar = QKeyValue.FindIntAttribute(bif.Attributes, "sourceline", -1); + if (bar != -1) + info.lineno = bar; + } + } + var thing = header; + foreach (Cmd c in header.Cmds) + { + newCmds.Add(c); + } + header.Cmds = newCmds; + } + +#if true + public void AnnotateProcRequires(Procedure proc, Implementation impl, ProverContext ctxt) + { + Contract.Requires(impl != null); + + CurrentLocalVariables = impl.LocVars; + + // collect the variables needed in the invariant + List exprs = new List(); + List vars = new List(); + List names = new List(); + + foreach (Variable v in program.GlobalVariables) + { + vars.Add(v); + exprs.Add(new IdentifierExpr(Token.NoToken, v)); + names.Add(v.Name); + } + foreach (Variable v in proc.InParams) + { + Contract.Assert(v != null); + vars.Add(v); + exprs.Add(new IdentifierExpr(Token.NoToken, v)); + names.Add(v.Name); + } + string name = impl.Name + "_precond"; + TypedIdent ti = new TypedIdent(Token.NoToken, "", Microsoft.Boogie.Type.Bool); + Contract.Assert(ti != null); + Formal returnVar = new Formal(Token.NoToken, ti, false); + Contract.Assert(returnVar != null); + var function = new Function(Token.NoToken, name, vars, returnVar); + ctxt.DeclareFunction(function, ""); + + Expr invarExpr = new NAryExpr(Token.NoToken, new FunctionCall(function), exprs); + + proc.Requires.Add(new Requires(Token.NoToken, false, invarExpr, "", null)); + + var info = new AnnotationInfo(); + info.filename = proc.tok.filename; + info.lineno = proc.Line; + info.argnames = names.ToArray(); + info.type = AnnotationInfo.AnnotationType.LoopInvariant; + annotationInfo.Add(name, info); + } + + public void AnnotateProcEnsures(Procedure proc, Implementation impl, ProverContext ctxt) + { + Contract.Requires(impl != null); + + CurrentLocalVariables = impl.LocVars; + + // collect the variables needed in the invariant + List exprs = new List(); + List vars = new List(); + List names = new List(); + + foreach (Variable v in program.GlobalVariables) + { + vars.Add(v); + exprs.Add(new OldExpr(Token.NoToken,new IdentifierExpr(Token.NoToken, v))); + names.Add(v.Name); + } + foreach (Variable v in proc.InParams) + { + Contract.Assert(v != null); + vars.Add(v); + exprs.Add(new OldExpr(Token.NoToken, new IdentifierExpr(Token.NoToken, v))); + names.Add(v.Name); + } + foreach (IdentifierExpr ie in proc.Modifies) + { + if (ie.Decl == null) + continue; + vars.Add(ie.Decl); + exprs.Add(ie); + names.Add(ie.Decl.Name + "_out"); + } + foreach (Variable v in proc.OutParams) + { + Contract.Assert(v != null); + vars.Add(v); + exprs.Add(new IdentifierExpr(Token.NoToken, v)); + names.Add(v.Name); + } + string name = impl.Name + "_summary"; + summaries.Add(name, true); + TypedIdent ti = new TypedIdent(Token.NoToken, "", Microsoft.Boogie.Type.Bool); + Contract.Assert(ti != null); + Formal returnVar = new Formal(Token.NoToken, ti, false); + Contract.Assert(returnVar != null); + var function = new Function(Token.NoToken, name, vars, returnVar); + ctxt.DeclareFunction(function, ""); + + Expr invarExpr = new NAryExpr(Token.NoToken, new FunctionCall(function), exprs); + + proc.Ensures.Add(new Ensures(Token.NoToken, false, invarExpr, "", null)); + + var info = new AnnotationInfo(); + info.filename = proc.tok.filename; + info.lineno = proc.Line; + info.argnames = names.ToArray(); + info.type = AnnotationInfo.AnnotationType.ProcedureSummary; + annotationInfo.Add(name, info); + } +#endif + + void MarkAllFunctionImplementationsInline() + { + foreach (var func in program.Functions) + { + if (func.Body == null && func.DefinitionAxiom != null) + { + var def = func.DefinitionAxiom.Expr as QuantifierExpr; + var bod = def.Body as NAryExpr; + func.Body = bod.Args[1]; + func.DefinitionAxiom = null; + } + if (func.Body != null) + if (func.FindExprAttribute("inline") == null) + func.AddAttribute("inline", Expr.Literal(100)); + } + } + + void InlineAll() + { + foreach (var impl in program.Implementations) + { + impl.OriginalBlocks = impl.Blocks; + impl.OriginalLocVars = impl.LocVars; + if(impl.Name != main_proc_name) + if(impl.FindExprAttribute("inline") == null) + impl.AddAttribute("inline", Expr.Literal(100)); + } + foreach (var impl in program.Implementations) + { + if (!impl.SkipVerification) + { + Inliner.ProcessImplementation(program, impl); + } + } + foreach (var impl in program.Implementations) + { + impl.OriginalBlocks = null; + impl.OriginalLocVars = null; + } + } + + public class LazyInliningInfo + { + [ContractInvariantMethod] + void ObjectInvariant() + { + Contract.Invariant(impl != null); + Contract.Invariant(function != null); + Contract.Invariant(controlFlowVariable != null); + Contract.Invariant(assertExpr != null); + Contract.Invariant(cce.NonNullElements(interfaceVars)); + Contract.Invariant(incarnationOriginMap == null || cce.NonNullDictionaryAndValues(incarnationOriginMap)); + } + + public Implementation impl; + public int uniqueId; + public Function function; + public Variable controlFlowVariable; + public List interfaceVars; + public List> interfaceVarCopies; + public Expr assertExpr; + public VCExpr vcexpr; + public List privateVars; + public Dictionary incarnationOriginMap; + public Hashtable /*Variable->Expr*/ exitIncarnationMap; + public Hashtable /*GotoCmd->returnCmd*/ gotoCmdOrigins; + public Dictionary label2absy; + public VC.ModelViewInfo mvInfo; + + public Dictionary reachVars; + public List reachVarBindings; + public Variable inputErrorVariable; + public Variable outputErrorVariable; + + + + public LazyInliningInfo(Implementation impl, Program program, ProverContext ctxt, int uniqueId, GlobalVariable errorVariable) + { + Contract.Requires(impl != null); + Contract.Requires(program != null); + Procedure proc = cce.NonNull(impl.Proc); + + this.impl = impl; + this.uniqueId = uniqueId; + this.controlFlowVariable = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, "cfc", Microsoft.Boogie.Type.Int)); + impl.LocVars.Add(controlFlowVariable); + + List interfaceVars = new List(); + Expr assertExpr = new LiteralExpr(Token.NoToken, true); + Contract.Assert(assertExpr != null); + // InParams must be obtained from impl and not proc + foreach (Variable v in impl.InParams) + { + Contract.Assert(v != null); + interfaceVars.Add(v); + } + // OutParams must be obtained from impl and not proc + foreach (Variable v in impl.OutParams) + { + Contract.Assert(v != null); + Constant c = new Constant(Token.NoToken, + new TypedIdent(Token.NoToken, impl.Name + "_" + v.Name, v.TypedIdent.Type)); + interfaceVars.Add(c); + Expr eqExpr = Expr.Eq(new IdentifierExpr(Token.NoToken, c), new IdentifierExpr(Token.NoToken, v)); + assertExpr = Expr.And(assertExpr, eqExpr); + } + foreach (Variable v in program.GlobalVariables) + { + Contract.Assert(v != null); + interfaceVars.Add(v); + if (v.Name == "error") + inputErrorVariable = v; + } + if (errorVariable != null) + { + proc.Modifies.Add(new IdentifierExpr(Token.NoToken, errorVariable)); + } + foreach (IdentifierExpr e in proc.Modifies) + { + Contract.Assert(e != null); + if (e.Decl == null) + continue; + Variable v = e.Decl; + Constant c = new Constant(Token.NoToken, new TypedIdent(Token.NoToken, impl.Name + "_" + v.Name, v.TypedIdent.Type)); + interfaceVars.Add(c); + if (v.Name == "error") + { + outputErrorVariable = c; + continue; + } + Expr eqExpr = Expr.Eq(new IdentifierExpr(Token.NoToken, c), new IdentifierExpr(Token.NoToken, v)); + assertExpr = Expr.And(assertExpr, eqExpr); + } + + this.interfaceVars = interfaceVars; + this.assertExpr = Expr.Not(assertExpr); + List functionInterfaceVars = new List(); + foreach (Variable v in interfaceVars) + { + Contract.Assert(v != null); + functionInterfaceVars.Add(new Formal(Token.NoToken, new TypedIdent(Token.NoToken, v.Name, v.TypedIdent.Type), true)); + } + TypedIdent ti = new TypedIdent(Token.NoToken, "", Microsoft.Boogie.Type.Bool); + Contract.Assert(ti != null); + Formal returnVar = new Formal(Token.NoToken, ti, false); + Contract.Assert(returnVar != null); + this.function = new Function(Token.NoToken, proc.Name, functionInterfaceVars, returnVar); + ctxt.DeclareFunction(this.function, ""); + + interfaceVarCopies = new List>(); + int temp = 0; + for (int i = 0; i < /* CommandLineOptions.Clo.ProcedureCopyBound */ 0; i++) + { + interfaceVarCopies.Add(new List()); + foreach (Variable v in interfaceVars) + { + Constant constant = new Constant(Token.NoToken, new TypedIdent(Token.NoToken, v.Name + temp++, v.TypedIdent.Type)); + interfaceVarCopies[i].Add(constant); + //program.AddTopLevelDeclaration(constant); + } + } + } + } + + public class StratifiedInliningInfo : LazyInliningInfo + { + [ContractInvariantMethod] + void ObjectInvariant() + { + Contract.Invariant(cce.NonNullElements(privateVars)); + Contract.Invariant(cce.NonNullElements(interfaceExprVars)); + Contract.Invariant(cce.NonNullElements(interfaceExprVars)); + } + + // public StratifiedVCGenBase vcgen; + //public Implementation impl; + //public Program program; + //public ProverContext ctxt; + //public int uniqueid; + //public Function function; + //public Variable controlFlowVariable; + //public Expr assertExpr; + //public VCExpr vcexpr; + //public List interfaceExprVars; + //public List privateExprVars; + //public Dictionary label2absy; + //public VC.ModelViewInfo mvInfo; + //public Dictionary> callSites; + //public Dictionary> recordProcCallSites; + //public IEnumerable sortedBlocks; + //public bool initialized { get; private set; } + + + public List interfaceExprVars; + // public List privateVars; + public VCExpr funcExpr; + public VCExpr falseExpr; + public RPFP.Transformer F; + public RPFP.Node node; + public RPFP.Edge edge; + public bool isMain = false; + public Dictionary label2absyInv; + public ProverContext ctxt; + public Hashtable/**/ blockVariables = new Hashtable/**/(); + public List bindings = new List(); + + public StratifiedInliningInfo(Implementation _impl, Program _program, ProverContext _ctxt, int _uniqueid) + : base(_impl,_program,_ctxt,_uniqueid,null){ + Contract.Requires(_impl != null); + Contract.Requires(_program != null); + privateVars = new List(); + interfaceExprVars = new List(); + ctxt = _ctxt; + } + + } + + protected override void addExitAssert(string implName, Block exitBlock) + { + if (implName2StratifiedInliningInfo != null + && implName2StratifiedInliningInfo.ContainsKey(implName) + && !implName2StratifiedInliningInfo[implName].isMain) + { + if (mode == Mode.Boogie) return; + Expr assertExpr = implName2StratifiedInliningInfo[implName].assertExpr; + Contract.Assert(assertExpr != null); + exitBlock.Cmds.Add(new AssertCmd(Token.NoToken, assertExpr)); + } + } + +#if false + protected override void storeIncarnationMaps(string implName, Hashtable exitIncarnationMap) + { + if (implName2StratifiedInliningInfo != null && implName2StratifiedInliningInfo.ContainsKey(implName)) + { + StratifiedInliningInfo info = implName2StratifiedInliningInfo[implName]; + Contract.Assert(info != null); + info.exitIncarnationMap = exitIncarnationMap; + info.incarnationOriginMap = this.incarnationOriginMap; + } + } +#endif + + public void GenerateVCsForStratifiedInlining() + { + Contract.Requires(program != null); + foreach (var impl in program.Implementations) + { + Contract.Assert(!impl.Name.StartsWith(recordProcName), "Not allowed to have an implementation for this guy"); + + Procedure proc = cce.NonNull(impl.Proc); + + { + StratifiedInliningInfo info = new StratifiedInliningInfo(impl, program, boogieContext, QuantifierExpr.GetNextSkolemId()); + implName2StratifiedInliningInfo[impl.Name] = info; + // We don't need controlFlowVariable for stratified Inlining + //impl.LocVars.Add(info.controlFlowVariable); + List exprs = new List(); + + if (mode != Mode.Boogie && QKeyValue.FindBoolAttribute(impl.Attributes, "entrypoint")) + { + proc.Ensures.Add(new Ensures(Token.NoToken, true, Microsoft.Boogie.Expr.False, "", null)); + info.assertExpr = Microsoft.Boogie.Expr.False; + // info.isMain = true; + } + else if (mode == Mode.Corral || proc.FindExprAttribute("inline") != null || proc is LoopProcedure) + { + foreach (Variable v in proc.InParams) + { + Contract.Assert(v != null); + exprs.Add(new IdentifierExpr(Token.NoToken, v)); + } + foreach (Variable v in proc.OutParams) + { + Contract.Assert(v != null); + exprs.Add(new IdentifierExpr(Token.NoToken, v)); + } + foreach (Variable v in program.GlobalVariables) + { + Contract.Assert(v != null); + exprs.Add(new OldExpr(Token.NoToken, new IdentifierExpr(Token.NoToken, v))); + } + foreach (IdentifierExpr ie in proc.Modifies) + { + Contract.Assert(ie != null); + if (ie.Decl == null) + continue; + exprs.Add(ie); + } + Expr freePostExpr = new NAryExpr(Token.NoToken, new FunctionCall(info.function), exprs); +#if true + if(mode == Mode.Corral || mode == Mode.OldCorral) + proc.Ensures.Add(new Ensures(Token.NoToken, true, freePostExpr, "", new QKeyValue(Token.NoToken, "si_fcall", new List(), null))); +#endif + } + else // not marked "inline" must be main + { + Expr freePostExpr = new NAryExpr(Token.NoToken, new FunctionCall(info.function), exprs); + info.isMain = true; + } + } + } + + if (mode == Mode.Boogie) return; + + foreach (var proc in program.Procedures) + { + if (!proc.Name.StartsWith(recordProcName)) continue; + Contract.Assert(proc.InParams.Count == 1); + + // Make a new function + TypedIdent ti = new TypedIdent(Token.NoToken, "", Microsoft.Boogie.Type.Bool); + Contract.Assert(ti != null); + Formal returnVar = new Formal(Token.NoToken, ti, false); + Contract.Assert(returnVar != null); + + // Get record type + var argtype = proc.InParams[0].TypedIdent.Type; + + var ins = new List(); + ins.Add(new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "x", argtype), true)); + + var recordFunc = new Function(Token.NoToken, proc.Name, ins, returnVar); + boogieContext.DeclareFunction(recordFunc, ""); + + var exprs = new List(); + exprs.Add(new IdentifierExpr(Token.NoToken, proc.InParams[0])); + + Expr freePostExpr = new NAryExpr(Token.NoToken, new FunctionCall(recordFunc), exprs); + proc.Ensures.Add(new Ensures(true, freePostExpr)); + } + } + + private void FixedPointToSpecs(){ + + if(mode != Mode.Corral || CommandLineOptions.Clo.PrintFixedPoint == null) + return; // not implemented for other annotation modes yet + + var twr = new TokenTextWriter(CommandLineOptions.Clo.PrintFixedPoint, /*pretty=*/ false); + Dictionary pmap = new Dictionary (); + + foreach (var node in rpfp.nodes) + pmap.Add ((node.Name as VCExprBoogieFunctionOp).Func.Name, node); + + foreach (var impl in program.Implementations) + { + Contract.Assert(!impl.Name.StartsWith(recordProcName), "Not allowed to have an implementation for this guy"); + + Procedure proc = cce.NonNull(impl.Proc); + + { + StratifiedInliningInfo info = new StratifiedInliningInfo(impl, program, boogieContext, QuantifierExpr.GetNextSkolemId()); + implName2StratifiedInliningInfo[impl.Name] = info; + // We don't need controlFlowVariable for stratified Inlining + //impl.LocVars.Add(info.controlFlowVariable); + List exprs = new List(); + + { + if (pmap.ContainsKey(impl.Name)) + { + RPFP.Node node = pmap[impl.Name]; + var annot = node.Annotation; + EmitProcSpec(twr, proc, info, annot); + } + } + } + } + twr.Close (); + } + + private void EmitProcSpec(TokenTextWriter twr, Procedure proc, StratifiedInliningInfo info, RPFP.Transformer annot) + { + // last ensures clause will be the symbolic one + if (!info.isMain) + { + var ens = proc.Ensures[proc.Ensures.Count - 1]; + if (ens.Condition != Expr.False) // this is main + { + var postExpr = ens.Condition as NAryExpr; + var args = postExpr.Args; + + var ind = annot.IndParams; + var bound = new Dictionary(); + for (int i = 0; i < args.Count; i++) + { + bound[ind[i]] = args[i]; + } + var new_ens_cond = VCExprToExpr(annot.Formula, bound); + if (new_ens_cond != Expr.True) + { + var new_ens = new Ensures(false, new_ens_cond); + var enslist = new List(); + enslist.Add(new_ens); + var new_proc = new Procedure(proc.tok, proc.Name, proc.TypeParameters, proc.InParams, + proc.OutParams, new List(), new List(), enslist); + new_proc.Emit(twr, 0); + } + } + } + } + + static int ConjectureFileCounter = 0; + + private void ConjecturesToSpecs() + { + + if (mode != Mode.Corral || CommandLineOptions.Clo.PrintConjectures == null) + return; // not implemented for other annotation modes yet + + var twr = new TokenTextWriter(CommandLineOptions.Clo.PrintConjectures + "." + ConjectureFileCounter.ToString(), /*pretty=*/ false); + ConjectureFileCounter++; + + foreach (var c in rpfp.conjectures) + { + var name = c.node.Name.GetDeclName(); + if (implName2StratifiedInliningInfo.ContainsKey(name)) + { + StratifiedInliningInfo info = implName2StratifiedInliningInfo[c.node.Name.GetDeclName()]; + Implementation impl = info.impl; + Procedure proc = impl.Proc; + EmitProcSpec(twr, proc, info, c.bound); + } + } + + twr.Close (); + } + + private Term ExtractSmallerVCsRec(TermDict< Term> memo, Term t, List small, Term lbl = null) + { + Term res; + if (memo.TryGetValue(t, out res)) + return res; + var kind = t.GetKind(); + if (kind == TermKind.App) + { + var f = t.GetAppDecl(); + if (f.GetKind() == DeclKind.Implies){ + var lhs = t.GetAppArgs()[0]; + if(lhs.GetKind() == TermKind.App){ + var r = lhs.GetAppDecl(); + if (r.GetKind() == DeclKind.And) + { + Term q = t.GetAppArgs()[1]; + var lhsargs = lhs.GetAppArgs(); + for (int i = lhsargs.Length-1; i >= 0; --i) + { + q = ctx.MkImplies(lhsargs[i], q); + } + res = ExtractSmallerVCsRec(memo, q, small,lbl); + goto done; + } + if (r.GetKind() == DeclKind.Label) + { + var arg = lhs; + arg = lhs.GetAppArgs()[0]; + if (!(arg.GetKind() == TermKind.App && arg.GetAppDecl().GetKind() == DeclKind.Uninterpreted)) + goto normal; + if (!(annotationInfo.ContainsKey(arg.GetAppDecl().GetDeclName()) && annotationInfo[arg.GetAppDecl().GetDeclName()].type == AnnotationInfo.AnnotationType.LoopInvariant)) + goto normal; + var sm = ctx.MkImplies(lhs, ExtractSmallerVCsRec(memo, t.GetAppArgs()[1], small)); + if (lbl != null) + sm = ctx.MkImplies(lbl, sm); + small.Add(sm); + res = ctx.MkTrue(); + goto done; + } + if (r.GetKind() == DeclKind.Uninterpreted) + { + var arg = lhs; + if (!(annotationInfo.ContainsKey(arg.GetAppDecl().GetDeclName()) && annotationInfo[arg.GetAppDecl().GetDeclName()].type == AnnotationInfo.AnnotationType.LoopInvariant)) + goto normal; + var sm = ctx.MkImplies(lhs,ExtractSmallerVCsRec(memo,t.GetAppArgs()[1],small)); + if (lbl != null) + sm = ctx.MkImplies(lbl, sm); + small.Add(sm); + res = ctx.MkTrue(); + goto done; + } + } + normal: + Term newlbl = null; + if (lhs.IsLabel() && lhs.GetAppArgs()[0] == ctx.MkTrue()) + newlbl = lhs; + res = ctx.MkImplies(lhs,ExtractSmallerVCsRec(memo,t.GetAppArgs()[1],small,newlbl)); + } + else if (f.GetKind() == DeclKind.And) + { + res = ctx.MkApp(f,t.GetAppArgs().Select(x => ExtractSmallerVCsRec(memo, x, small)).ToArray()); + } + else + res = t; + } + else + res = t; + done: + memo.Add(t, res); + return res; + } + + private void ExtractSmallerVCs(Term t, List small){ + TermDict< Term> memo = new TermDict< Term>(); + Term top = ExtractSmallerVCsRec(memo, t, small); + small.Add(top); + } + + private Dictionary goalNumbering = new Dictionary(); + + private Term NormalizeGoal(Term goal, FuncDecl label) + { + var f = goal.GetAppDecl(); + var args = goal.GetAppArgs(); + int number; + if (!goalNumbering.TryGetValue(f, out number)) + { + number = goalNumbering.Count; + goalNumbering.Add(f, number); + } + Term[] tvars = new Term[args.Length]; + Term[] eqns = new Term[args.Length]; + AnnotationInfo info = null; + annotationInfo.TryGetValue(f.GetDeclName(), out info); + for (int i = 0; i < args.Length; i++) + { + string pname = (info == null) ? i.ToString() : info.argnames[i]; + tvars[i] = ctx.MkConst("@a" + number.ToString() + "_" + pname, args[i].GetSort()); + eqns[i] = ctx.MkEq(tvars[i], args[i]); + } + return ctx.MkImplies(ctx.MkAnd(eqns), ctx.MkApp(label, ctx.MkApp(f, tvars))); + } + + private Term MergeGoalsRec(TermDict< Term> memo, Term t) + { + Term res; + if (memo.TryGetValue(t, out res)) + return res; + var kind = t.GetKind(); + if (kind == TermKind.App) + { + var f = t.GetAppDecl(); + var args = t.GetAppArgs(); + if (f.GetKind() == DeclKind.Implies) + { + res = ctx.MkImplies(args[0], MergeGoalsRec(memo, args[1])); + goto done; + } + else if (f.GetKind() == DeclKind.And) + { + args = args.Select(x => MergeGoalsRec(memo, x)).ToArray(); + res = ctx.MkApp(f, args); + goto done; + } + else if (f.GetKind() == DeclKind.Label) + { + var arg = t.GetAppArgs()[0]; + var r = arg.GetAppDecl(); + if (r.GetKind() == DeclKind.Uninterpreted) + { + res = NormalizeGoal(arg, f); + goto done; + } + } + } + res = t; + done: + memo.Add(t, res); + return res; + } + + private Term MergeGoals(Term t) + { + TermDict< Term> memo = new TermDict< Term>(); + return MergeGoalsRec(memo, t); + } + + private Term CollectGoalsRec(TermDict< Term> memo, Term t, List goals, List cruft) + { + Term res; + if (memo.TryGetValue(t, out res)) + return res; + var kind = t.GetKind(); + if (kind == TermKind.App) + { + var f = t.GetAppDecl(); + if (f.GetKind() == DeclKind.Implies) + { + CollectGoalsRec(memo, t.GetAppArgs()[1], goals, cruft); + goto done; + } + else if (f.GetKind() == DeclKind.And) + { + foreach (var arg in t.GetAppArgs()) + { + CollectGoalsRec(memo, arg, goals, cruft); + } + goto done; + } + else if (f.GetKind() == DeclKind.Label) + { + var arg = t.GetAppArgs()[0]; + if (arg.GetKind() == TermKind.App && arg.GetAppDecl().GetKind() == DeclKind.Uninterpreted) + { + var r = arg.GetAppDecl(); + if (memo.TryGetValue(arg, out res)) + goto done; + if (!annotationInfo.ContainsKey(r.GetDeclName()) && !arg.GetAppDecl().GetDeclName().StartsWith("_solve_")) + goto done; + goals.Add(arg); + memo.Add(arg, arg); + goto done; + } + else + return CollectGoalsRec(memo, arg, goals, cruft); + } + else if (f.GetKind() == DeclKind.Uninterpreted) + { + string name = f.GetDeclName(); + if (name.StartsWith("_solve_")) + { + if (memo.TryGetValue(t, out res)) + goto done; + goals.Add(t); + memo.Add(t, t); + return t; + } + } + } + // else the goal must be cruft + cruft.Add(t); + done: + res = t; // just to return something + memo.Add(t, res); + return res; + } + + private void CollectGoals(Term t, List goals, List cruft) + { + TermDict< Term> memo = new TermDict< Term>(); + CollectGoalsRec(memo, t.GetAppArgs()[1], goals, cruft); + } + + private Term SubstRec(TermDict< Term> memo, Term t) + { + Term res; + if (memo.TryGetValue(t, out res)) + return res; + var kind = t.GetKind(); + if (kind == TermKind.App) + { + // var f = t.GetAppDecl(); + var args = t.GetAppArgs().Select(x => SubstRec(memo, x)).ToArray(); + res = ctx.CloneApp(t, args); + } + else res = t; + memo.Add(t, res); + return res; + } + + private Term SubstRecGoals(TermDict< Term> memo, Term t) + { + Term res; + if (memo.TryGetValue(t, out res)) + return res; + var kind = t.GetKind(); + if (kind == TermKind.App) + { + var f = t.GetAppDecl(); + var args = t.GetAppArgs(); + if (f.GetKind() == DeclKind.Implies){ + res = SubstRecGoals(memo, args[1]); + if (res != ctx.MkTrue()) + res = ctx.MkImplies(args[0],res); + goto done; + } + else if (f.GetKind() == DeclKind.And) + { + args = args.Select(x => SubstRecGoals(memo, x)).ToArray(); + args = args.Where(x => x != ctx.MkTrue()).ToArray(); + res = ctx.MkAnd(args); + goto done; + } + else if (f.GetKind() == DeclKind.Label) + { + var arg = t.GetAppArgs()[0]; + if (arg.GetKind() == TermKind.App && arg.GetAppDecl().GetKind() == DeclKind.Uninterpreted) + { + var r = arg.GetAppDecl(); + if (memo.TryGetValue(arg, out res)) + { + if(res != ctx.MkTrue()) + res = ctx.MkApp(f, res); + goto done; + } + } + else + { + res = ctx.MkApp(f, SubstRecGoals(memo, arg)); + goto done; + } + + } + // what's left could be cruft! + if (memo.TryGetValue(t, out res)) + { + goto done; + } + } + res = t; + done: + memo.Add(t, res); + return res; + } + + private void FactorVCs(Term t, List vcs) + { + List small = new List(); + ExtractSmallerVCs(t, small); + foreach (var smm in small) + { + List goals = new List(); + List cruft = new List(); + var sm = largeblock ? MergeGoals(smm) : smm; + CollectGoals(sm, goals,cruft); + foreach (var goal in goals) + { + TermDict< Term> memo = new TermDict< Term>(); + foreach (var othergoal in goals) + memo.Add(othergoal, othergoal.Equals(goal) ? ctx.MkFalse() : ctx.MkTrue()); + foreach (var thing in cruft) + memo.Add(thing, ctx.MkTrue()); + var vc = SubstRecGoals(memo, sm); + vc = ctx.MkImplies(ctx.MkNot(vc), goal); + vcs.Add(vc); + } + { + TermDict< Term> memo = new TermDict< Term>(); + foreach (var othergoal in goals) + memo.Add(othergoal, ctx.MkTrue()); + var vc = SubstRecGoals(memo, sm); + if (vc != ctx.MkTrue()) + { + vc = ctx.MkImplies(ctx.MkNot(vc), ctx.MkFalse()); + vcs.Add(vc); + } + } + } + } + + + + private void GenerateVCForStratifiedInlining(Program program, StratifiedInliningInfo info, Checker checker) + { + Contract.Requires(program != null); + Contract.Requires(info != null); + Contract.Requires(checker != null); + Contract.Requires(info.impl != null); + Contract.Requires(info.impl.Proc != null); + + + + Implementation impl = info.impl; + if (mode == Mode.Boogie && style == AnnotationStyle.Flat && impl.Name != main_proc_name) + return; + Contract.Assert(impl != null); + ConvertCFG2DAG(impl,edgesCut); + VC.ModelViewInfo mvInfo; + PassifyImpl(impl, out mvInfo); + Dictionary label2absy = null; + VCExpressionGenerator gen = checker.VCExprGen; + Contract.Assert(gen != null); + VCExpr vcexpr; + if(NoLabels){ + // int assertionCount = 0; + VCExpr startCorrect = null; /* VC.VCGen.LetVC(cce.NonNull(impl.Blocks[0]), null, null, info.blockVariables, info.bindings, + info.ctxt, out assertionCount); */ + vcexpr = gen.Let(info.bindings, startCorrect); + } + else vcexpr = GenerateVC(impl, null /* info.controlFlowVariable */, out label2absy, info.ctxt); + if(mode != Mode.Boogie) + vcexpr = gen.Not(vcexpr); + Contract.Assert(vcexpr != null); + info.label2absy = label2absy; + info.mvInfo = mvInfo; + List interfaceExprs = new List(); + + if (true /* was: !info.isMain */) + { + Boogie2VCExprTranslator translator = checker.TheoremProver.Context.BoogieExprTranslator; + Contract.Assert(translator != null); + info.privateVars = new List(); + foreach (Variable v in impl.LocVars) + { + Contract.Assert(v != null); + info.privateVars.Add(translator.LookupVariable(v)); + } + foreach (Variable v in impl.OutParams) + { + Contract.Assert(v != null); + info.privateVars.Add(translator.LookupVariable(v)); + } + + info.interfaceExprVars = new List(); + + foreach (Variable v in info.interfaceVars) + { + Contract.Assert(v != null); + VCExprVar ev = translator.LookupVariable(v); + Contract.Assert(ev != null); + info.interfaceExprVars.Add(ev); + interfaceExprs.Add(ev); + } + } + + Function function = cce.NonNull(info.function); + Contract.Assert(function != null); + info.funcExpr = gen.Function(function, interfaceExprs); + info.vcexpr = vcexpr; + + if (mode == Mode.Boogie) + { + Term z3vc = boogieContext.VCExprToTerm(vcexpr, linOptions); + FactorVCs(z3vc, DualityVCs); + } + else + { + // Index the procedures by relational variable + FuncDecl R = boogieContext.VCExprToTerm(info.funcExpr, linOptions).GetAppDecl(); + relationToProc.Add(R, info); + info.node = rpfp.CreateNode(boogieContext.VCExprToTerm(info.funcExpr, linOptions)); + rpfp.nodes.Add(info.node); + if (info.isMain || QKeyValue.FindBoolAttribute(impl.Attributes, "entrypoint")) + info.node.Bound.Formula = ctx.MkFalse(); + } + } + + // This returns a new FuncDel with same sort as top-level function + // of term t, but with numeric suffix appended to name. + + private FuncDecl SuffixFuncDecl(Term t, int n) + { + var name = t.GetAppDecl().GetDeclName() + "_" + n.ToString(); + return ctx.MkFuncDecl(name, t.GetAppDecl()); + } + + // Collect the relational paremeters + + private Term CollectParamsRec(TermDict memo, Term t, List parms, List nodes, Dictionary done) + { + Term res; + if (memo.TryGetValue(t, out res)) + return res; + var kind = t.GetKind(); + if (kind == TermKind.App) + { + var f = t.GetAppDecl(); + var args = t.GetAppArgs(); + args = args.Select(x => CollectParamsRec(memo, x, parms, nodes, done)).ToArray(); + StratifiedInliningInfo info; + if (relationToProc.TryGetValue(f, out info)) + { + if (done.ContainsKey(t)) + res = done[t]; + else + { + f = SuffixFuncDecl(t, parms.Count); + parms.Add(f); + nodes.Add(info.node); + res = ctx.MkApp(f, args); + done.Add(t,res); // don't count same expression twice! + } + } + else + res = ctx.CloneApp(t, args); + } // TODO: handle quantifiers + else res = t; + memo.Add(t, res); + return res; + } + + public void GetTransformer(StratifiedInliningInfo info) + { + Term vcTerm = boogieContext.VCExprToTerm(info.vcexpr, linOptions); + Term[] paramTerms = info.interfaceExprVars.Select(x => boogieContext.VCExprToTerm(x, linOptions)).ToArray(); + var relParams = new List(); + var nodeParams = new List(); + var memo = new TermDict< Term>(); + var done = new Dictionary(); // note this hashes on equality, not reference! + vcTerm = CollectParamsRec(memo, vcTerm, relParams, nodeParams,done); + // var ops = new Util.ContextOps(ctx); + // var foo = ops.simplify_lhs(vcTerm); + // vcTerm = foo.Item1; + info.F = rpfp.CreateTransformer(relParams.ToArray(), paramTerms, vcTerm); + info.edge = rpfp.CreateEdge(info.node, info.F, nodeParams.ToArray()); + rpfp.edges.Add(info.edge); + // TODO labels[info.edge.number] = foo.Item2; + } + + public RPFP.Node GetNodeOfImpl(Implementation/*!*/ impl) + { + return implName2StratifiedInliningInfo[impl.Name].node; + } + + public class CyclicLiveVariableAnalysis : Microsoft.Boogie.LiveVariableAnalysis + { + public new static void ComputeLiveVariables(Implementation impl) + { + + bool some_change = true; + List sortedNodes = new List(); + foreach (var block in impl.Blocks) + { + sortedNodes.Add(block); + } + sortedNodes.Reverse(); + + while (some_change) + { + some_change = false; + foreach (Block/*!*/ block in sortedNodes) + { + Contract.Assert(block != null); + HashSet/*!*/ liveVarsAfter = new HashSet(); + if (block.TransferCmd is GotoCmd) + { + GotoCmd gotoCmd = (GotoCmd)block.TransferCmd; + if (gotoCmd.labelTargets != null) + { + foreach (Block/*!*/ succ in gotoCmd.labelTargets) + { + Contract.Assert(succ != null); + if (succ.liveVarsBefore != null) + liveVarsAfter.UnionWith(succ.liveVarsBefore); + } + } + } + + List cmds = block.Cmds; + int len = cmds.Count; + for (int i = len - 1; i >= 0; i--) + { + if (cmds[i] is CallCmd) + { + Procedure/*!*/ proc = cce.NonNull(cce.NonNull((CallCmd/*!*/)cmds[i]).Proc); + if (InterProcGenKill.HasSummary(proc.Name)) + { + liveVarsAfter = + InterProcGenKill.PropagateLiveVarsAcrossCall(cce.NonNull((CallCmd/*!*/)cmds[i]), liveVarsAfter); + continue; + } + } + Propagate(cmds[i], liveVarsAfter); + } + + if (block.liveVarsBefore == null) + block.liveVarsBefore = new HashSet(); + if (!liveVarsAfter.IsSubsetOf(block.liveVarsBefore)) + { + block.liveVarsBefore = liveVarsAfter; + some_change = true; + } + } + } + } + } + + public void Generate() + { + + var oldDagOption = CommandLineOptions.Clo.vcVariety; + CommandLineOptions.Clo.vcVariety = CommandLineOptions.VCVariety.Dag; + + // MarkAllFunctionImplementationsInline(); // This is for SMACK, which goes crazy with functions + + // Run live variable analysis (TODO: should this be here?) +#if false + if (CommandLineOptions.Clo.LiveVariableAnalysis == 2) + { + Microsoft.Boogie.InterProcGenKill.ComputeLiveVars(impl, program); + } +#endif + + #region In Boogie mode, annotate the program + if (mode == Mode.Boogie) + { + + // find the name of the main procedure + main_proc_name = null; // default in case no entry point defined + foreach (var impl in program.Implementations) + { + if (QKeyValue.FindBoolAttribute(impl.Attributes, "entrypoint")) + main_proc_name = impl.Proc.Name; + } + if (main_proc_name == null) + { + foreach (var impl in program.Implementations) + { + if (impl.Proc.Name == "main" || impl.Proc.Name.EndsWith(".main")) + main_proc_name = impl.Proc.Name; + } + } + if (main_proc_name == null) + main_proc_name = "main"; + + if (style == AnnotationStyle.Flat) + { + InlineAll(); + Microsoft.Boogie.BlockCoalescer.CoalesceBlocks(program); + foreach (var impl in program.Implementations) + { + if (main_proc_name == impl.Proc.Name) + { + Microsoft.Boogie.LiveVariableAnalysis.ClearLiveVariables(impl); + CyclicLiveVariableAnalysis.ComputeLiveVariables(impl); + AnnotateLoops(impl, boogieContext); + } + } + } + else + { + + if (style == AnnotationStyle.Procedure || style == AnnotationStyle.Call) + { + foreach (var impl in program.Implementations) + { + if (!QKeyValue.FindBoolAttribute(impl.Attributes, "entrypoint")) + AnnotateProcRequires(impl.Proc, impl, boogieContext); + AnnotateProcEnsures(impl.Proc, impl, boogieContext); + } + if (style == AnnotationStyle.Call) + { + + } + } + + // must do this after annotating procedures, else calls + // will be prematurely desugared + + foreach (var impl in program.Implementations) + { + Microsoft.Boogie.LiveVariableAnalysis.ClearLiveVariables(impl); + CyclicLiveVariableAnalysis.ComputeLiveVariables(impl); + } + + + if (style == AnnotationStyle.Flat || style == AnnotationStyle.Call) + { + foreach (var impl in program.Implementations) + { + AnnotateLoops(impl, boogieContext); + } + } + if (style == AnnotationStyle.Call) + { + Dictionary impls = new Dictionary(); + foreach (var impl in program.Implementations) + { + impls.Add(impl.Proc.Name, true); + } + foreach (var impl in program.Implementations) + { + AnnotateCallSites(impl, boogieContext, impls); + } + } + if (style == AnnotationStyle.Flat) + InlineAll(); + } + } + #endregion + + /* Generate the VC's */ + GenerateVCsForStratifiedInlining(); + + /* Generate the background axioms */ + Term background = ctx.MkTrue(); // TODO boogieContext.VCExprToTerm(boogieContext.Axioms, linOptions); + rpfp.AssertAxiom(background); + + int save_option = CommandLineOptions.Clo.StratifiedInlining; // need this to get funcall labels + CommandLineOptions.Clo.StratifiedInlining = 1; + + /* Create the nodes, indexing procedures by their relational symbols. */ + foreach (StratifiedInliningInfo info in implName2StratifiedInliningInfo.Values) + GenerateVCForStratifiedInlining(program, info, checker); + + CommandLineOptions.Clo.StratifiedInlining = save_option; + + if (mode == Mode.Boogie) + { + // var ops = new Util.ContextOps(ctx); + var vcs = DualityVCs; + DualityVCs = new List(); + foreach (var vc in vcs) + { + // var foo = ops.simplify_lhs(vc.GetAppArgs()[0]); + var foo = vc.GetAppArgs()[0]; + if (!foo.IsFalse()) + DualityVCs.Add(ctx.MkImplies(foo, vc.GetAppArgs()[1])); + } + + rpfp.FromClauses(DualityVCs.ToArray()); + // TODO rpfp.HornClauses = style == AnnotationStyle.Flat; + } + else + { + /* Generate the edges. */ + foreach (StratifiedInliningInfo info in implName2StratifiedInliningInfo.Values) + GetTransformer(info); + } + + // save some information for debugging purposes + // TODO rpfp.ls.SetAnnotationInfo(annotationInfo); + + CommandLineOptions.Clo.vcVariety = oldDagOption; + } + + + private class ErrorHandler : ProverInterface.ErrorHandler + { + //TODO: anything we need to handle? + } + + Dictionary> varSubst = null; + + /** Check the RPFP, and return a counterexample if there is one. */ + + public VC.ConditionGeneration.Outcome Check(ref RPFP.Node cexroot) + { + var start = DateTime.Now; + + ErrorHandler handler = new ErrorHandler(); + RPFP.Node cex; + varSubst = new Dictionary>(); + +#if false + int origRecursionBound = CommandLineOptions.Clo.RecursionBound; + if (CommandLineOptions.Clo.RecursionBound > 0 && extraRecBound != null) + { + int maxExtra = 0; + foreach (string s in extraRecBound.Keys) + { + int extra = extraRecBound[s]; + if (extra > maxExtra) maxExtra = extra; + } + CommandLineOptions.Clo.RecursionBound += maxExtra; + } +#endif + + ProverInterface.Outcome outcome = + checker.TheoremProver.CheckRPFP("name", rpfp, handler, out cex, varSubst, extraRecBound); + cexroot = cex; + +#if false + CommandLineOptions.Clo.RecursionBound = origRecursionBound; +#endif + + Console.WriteLine("solve: {0}s", (DateTime.Now - start).TotalSeconds); + + switch(outcome) + { + case ProverInterface.Outcome.Valid: + return VC.ConditionGeneration.Outcome.Correct; + case ProverInterface.Outcome.Bounded: + return VC.ConditionGeneration.Outcome.ReachedBound; + case ProverInterface.Outcome.Invalid: + return VC.ConditionGeneration.Outcome.Errors; + case ProverInterface.Outcome.TimeOut: + return VC.ConditionGeneration.Outcome.TimedOut; + default: + return VC.ConditionGeneration.Outcome.Inconclusive; + } + } + + private bool generated = false; + + static private Object thisLock = new Object(); + + public override VC.VCGen.Outcome VerifyImplementation(Implementation impl, VerifierCallback collector) + { + + lock (thisLock) + { + Procedure proc = impl.Proc; + + // we verify all the impls at once, so we need to execute only once + // TODO: make sure needToCheck is true only once + bool needToCheck = false; + if (mode == Mode.OldCorral) + needToCheck = proc.FindExprAttribute("inline") == null && !(proc is LoopProcedure); + else if (mode == Mode.Corral || mode == Mode.Boogie) + needToCheck = QKeyValue.FindBoolAttribute(impl.Attributes, "entrypoint") && !(proc is LoopProcedure); + else + needToCheck = impl.Name == main_proc_name; + + if (needToCheck) + { + + var start = DateTime.Now; + + if (!generated) + { + Generate(); + Console.WriteLine("generate: {0}s", (DateTime.Now - start).TotalSeconds); + generated = true; + } + + + Console.WriteLine("Verifying {0}...", impl.Name); + + RPFP.Node cexroot = null; + // start = DateTime.Now; + var checkres = Check(ref cexroot); + Console.WriteLine("check: {0}s", (DateTime.Now - start).TotalSeconds); + switch (checkres) + { + case Outcome.Errors: + Console.WriteLine("Counterexample found.\n"); + // start = DateTime.Now; + Counterexample cex = CreateBoogieCounterExample(cexroot.owner, cexroot, impl); + // cexroot.owner.DisposeDualModel(); + // cex.Print(0); // just for testing + collector.OnCounterexample(cex, "assertion failure"); + Console.WriteLine("cex: {0}s", (DateTime.Now - start).TotalSeconds); + ConjecturesToSpecs(); + break; + case Outcome.Correct: + Console.WriteLine("Procedure is correct. (fixed point reached)"); + FixedPointToSpecs(); + ConjecturesToSpecs(); + break; + case Outcome.ReachedBound: + Console.WriteLine("Procedure is correct. (recursion bound reached)"); + FixedPointToSpecs(); + ConjecturesToSpecs(); + break; + default: + Console.WriteLine("Inconclusive result."); + ConjecturesToSpecs(); + break; + } + return checkres; + + } + + return Outcome.Inconclusive; + } + } + + public void FindLabelsRec(HashSet memo, Term t, Dictionary res) + { + if (memo.Contains(t)) + return; + if (t.IsLabel()) + { + string l = t.LabelName(); + if (!res.ContainsKey(l)) + res.Add(l, t.GetAppArgs()[0]); + } + if (t.GetKind() == TermKind.App) + { + var args = t.GetAppArgs(); + foreach (var a in args) + FindLabelsRec(memo, a, res); + } // TODO: handle quantifiers + + memo.Add(t); + } + + public void FindLabels() + { + labels = new Dictionary(); + foreach(var e in rpfp.edges){ + int id = e.number; + HashSet memo = new HashSet(ReferenceComparer.Instance); + FindLabelsRec(memo, e.F.Formula, labels); + } + } + + public string CodeLabel(Absy code, StratifiedInliningInfo info, string prefix) + { + if (info.label2absyInv == null) + { + info.label2absyInv = new Dictionary(); + foreach (int foo in info.label2absy.Keys) + { + Absy bar = info.label2absy[foo] as Absy; + string lbl = foo.ToString(); + info.label2absyInv.Add(bar, lbl); + } + } + if (info.label2absyInv.ContainsKey(code)) + { + string label = info.label2absyInv[code]; + return prefix+label; + } + return null; + } + + public Term CodeLabeledExpr(RPFP rpfp, RPFP.Node root, Absy code, StratifiedInliningInfo info, string prefix) + { + string label = CodeLabel(code, info, prefix); + + if (label != null) + { + var res = labels[label]; + return res; + } + else return null; + } + + public class LabelNotFound : Exception { }; + + public bool CodeLabelTrue(RPFP rpfp, RPFP.Node root, Absy code, StratifiedInliningInfo info, string prefix) + { + string label = CodeLabel(code, info, prefix); + + if (label == null) + throw new LabelNotFound(); + return root.Outgoing.labels.Contains(label); + } + + public bool CodeLabelFalse(RPFP rpfp, RPFP.Node root, Absy code, StratifiedInliningInfo info, string prefix) + { + return CodeLabelTrue(rpfp, root, code, info, prefix); + } + + + private class StateId + { + public RPFP.Edge edge; + public int capturePoint; + public StratifiedInliningInfo info; + public StateId(RPFP.Edge e, int c, StratifiedInliningInfo i) + { + edge = e; + capturePoint = c; + info = i; + } + } + + + public Counterexample CreateBoogieCounterExample(RPFP rpfp, RPFP.Node root, Implementation mainImpl) + { + FindLabels(); + var orderedStateIds = new List(); + Counterexample newCounterexample = + GenerateTrace(rpfp, root, orderedStateIds, mainImpl,true); + if (CommandLineOptions.Clo.ModelViewFile != null) + { + Model m = root.owner.GetBackgroundModel(); + GetModelWithStates(m, root, implName2StratifiedInliningInfo[mainImpl.Name], + orderedStateIds, varSubst); + newCounterexample.Model = m; + newCounterexample.ModelHasStatesAlready = true; + } + return newCounterexample; + } + + + + private Counterexample GenerateTrace(RPFP rpfp, RPFP.Node root, + List orderedStateIds, Implementation procImpl, bool toplevel) + { + Contract.Requires(procImpl != null); + + Contract.Assert(!rpfp.Empty(root)); + + + var info = implName2StratifiedInliningInfo[procImpl.Name]; + Block entryBlock = cce.NonNull(procImpl.Blocks[0]); + Contract.Assert(entryBlock != null); + + List trace = new List(); + trace.Add(entryBlock); + + var calleeCounterexamples = new Dictionary(); + + Counterexample newCounterexample = + GenerateTraceRec(rpfp, root, orderedStateIds, entryBlock, trace, calleeCounterexamples, info, toplevel); + + return newCounterexample; + } + + // TODO: this is a bit cheesy. Rather than finding the argument position + // of a relational term in a transformer by linear search, better to index this + // somewhere, but where? + private int TransformerArgPosition(RPFP rpfp, RPFP.Node root, Term expr) + { + FuncDecl rel = expr.GetAppDecl(); + string relname = rel.GetDeclName(); + var rps = root.Outgoing.F.RelParams; + for (int i = 0; i < rps.Length; i++) + { + string thisname = rps[i].GetDeclName(); + if (thisname == relname) + return i; + } + return -1; + } + + private bool EvalToFalse(RPFP rpfp, RPFP.Node root, Term expr,StratifiedInliningInfo info){ + Term res = rpfp.Eval(root.Outgoing,expr); + return res.Equals(ctx.MkTrue()); + } + + private Counterexample GenerateTraceRec( + RPFP rpfp, RPFP.Node root, + List orderedStateIds, + Block/*!*/ b, List/*!*/ trace, + Dictionary/*!*/ calleeCounterexamples, + StratifiedInliningInfo info, + bool toplevel) + { + Contract.Requires(b != null); + Contract.Requires(trace != null); + Contract.Requires(cce.NonNullDictionaryAndValues(calleeCounterexamples)); + + Stack continuation_stack = new Stack(); + + // If our block is not present, try diving into precondition + // and push a continuation. + // TODO: is the precondition always the first child? + while (!CodeLabelFalse(rpfp, root, b, info, "+")) + { + if (root.Outgoing != null && root.Outgoing.Children.Length > 0) + { + continuation_stack.Push(root); + root = root.Outgoing.Children[0]; + } + else + { + // can't find our block + Contract.Assert(false); + return null; + } + } + + // After translation, all potential errors come from asserts. + while (true) + { + + + List cmds = b.Cmds; + TransferCmd transferCmd = cce.NonNull(b.TransferCmd); + for (int i = 0; i < cmds.Count; i++) + { + Cmd cmd = cce.NonNull(cmds[i]); + + // Skip if 'cmd' not contained in the trace or not an assert + if (cmd is AssertCmd) + { + bool is_failed_assertion = false; + if (NoLabels) + is_failed_assertion = true; // we assume only assertions on + else + is_failed_assertion = CodeLabelTrue(rpfp, root, cmd, info, "@"); + + if (is_failed_assertion) + { + if (continuation_stack.Count == 0) + { + Counterexample newCounterexample = + AssertCmdToCounterexample((AssertCmd)cmd, transferCmd, trace, new Microsoft.Boogie.Model(), info.mvInfo, + boogieContext); + newCounterexample.AddCalleeCounterexample(calleeCounterexamples); + return newCounterexample; + } + root = continuation_stack.Pop(); + } + continue; + } + + // Counterexample generation for inlined procedures + AssumeCmd assumeCmd = cmd as AssumeCmd; + if (assumeCmd == null) + continue; + NAryExpr naryExpr = assumeCmd.Expr as NAryExpr; + if (naryExpr == null) + continue; + string calleeName = naryExpr.Fun.FunctionName; + Contract.Assert(calleeName != null); + + // what is this crap??? + BinaryOperator binOp = naryExpr.Fun as BinaryOperator; + if (binOp != null && binOp.Op == BinaryOperator.Opcode.And) + { + Expr expr = naryExpr.Args[0]; + NAryExpr mvStateExpr = expr as NAryExpr; + if (mvStateExpr != null && mvStateExpr.Fun.FunctionName == VC.ModelViewInfo.MVState_FunctionDef.Name) + { + LiteralExpr x = mvStateExpr.Args[1] as LiteralExpr; + // Debug.Assert(x != null); + int foo = x.asBigNum.ToInt; + orderedStateIds.Add(new StateId(root.Outgoing,foo,info)); + } + } + + if (calleeName.EndsWith("_summary")) + calleeName = calleeName.Substring(0, calleeName.Length - 8); + + if (!implName2StratifiedInliningInfo.ContainsKey(calleeName) && !calleeName.EndsWith("_summary")) + continue; + + { + Term code = CodeLabeledExpr(rpfp, root, cmd, info, "+si_fcall_"); + int pos = TransformerArgPosition(rpfp, root, code); + if (pos >= 0) + { + RPFP.Node callee = root.Outgoing.Children[pos]; + orderedStateIds.Add(new StateId(callee.Outgoing, CALL,info)); + calleeCounterexamples[new TraceLocation(trace.Count - 1, i)] = + new CalleeCounterexampleInfo( + cce.NonNull(GenerateTrace(rpfp, callee, orderedStateIds, + implName2StratifiedInliningInfo[calleeName].impl, false)), + new List()); + orderedStateIds.Add(new StateId(root.Outgoing, RETURN,info)); + } + } + } + + GotoCmd gotoCmd = transferCmd as GotoCmd; + List cuts = null; + if (edgesCut.ContainsKey(b)) + cuts = edgesCut[b]; + b = null; + + if (gotoCmd != null) + { + + foreach (Block bb in cce.NonNull(gotoCmd.labelTargets)) + { + Contract.Assert(bb != null); + if (CodeLabelFalse(rpfp, root, bb, info, "+")) + { + trace.Add(bb); + b = bb; + break; + } + } + if (b != null) continue; + } + // HACK: we have to try edges that were cut in generating the VC + + if (cuts != null) + foreach (var bb in cuts) + { + if (CodeLabelFalse(rpfp, root, bb, info, "+")) + { + trace.Add(bb); + b = bb; + break; + } + } + if (b != null) continue; + + return null; + } + + + } + + public override Counterexample extractLoopTrace(Counterexample cex, string mainProcName, Program program, Dictionary> extractLoopMappingInfo) + { + // Construct the set of inlined procs in the original program + var inlinedProcs = new HashSet(); + foreach (var decl in program.TopLevelDeclarations) + { + // Implementations + if (decl is Implementation) + { + var impl = decl as Implementation; + if (!(impl.Proc is LoopProcedure)) + { + inlinedProcs.Add(impl.Name); + } + } + + // And recording procedures + if (decl is Procedure) + { + var proc = decl as Procedure; + if (proc.Name.StartsWith(recordProcName)) + { + // Debug.Assert(!(decl is LoopProcedure)); + inlinedProcs.Add(proc.Name); + } + } + } + return extractLoopTraceRec( + new CalleeCounterexampleInfo(cex, new List()), + mainProcName, inlinedProcs, extractLoopMappingInfo).counterexample; + } + + protected override bool elIsLoop(string procname) + { + StratifiedInliningInfo info = null; + if (implName2StratifiedInliningInfo.ContainsKey(procname)) + { + info = implName2StratifiedInliningInfo[procname]; + } + + if (info == null) return false; + + var lp = info.impl.Proc as LoopProcedure; + + if (lp == null) return false; + return true; + } + + private void NumberCexEdges(RPFP.Node node, Dictionary map) + { + if (node.Outgoing == null) + return; // shouldn't happen + RPFP.Edge edge = node.Outgoing; + map[edge.number] = edge; + foreach (var c in edge.Children) + NumberCexEdges(c, map); + } + + private void GetModelWithStates(Model m, RPFP.Node cex, StratifiedInliningInfo mainInfo, + List orderedStateIds, + Dictionary> varSubst) + { + if (m == null) return; + var mvInfo = mainInfo.mvInfo; + + + foreach (Variable v in mvInfo.AllVariables) + { + m.InitialState.AddBinding(v.Name, GetModelValue(m, v, varSubst[cex.Outgoing.number])); + } + + Dictionary edgeNumbering = new Dictionary(); + NumberCexEdges(cex, edgeNumbering); + + int lastCandidate = 0; + int lastCapturePoint = CALL; + for (int i = 0; i < orderedStateIds.Count; ++i) + { + var s = orderedStateIds[i]; + RPFP.Edge edge = s.edge; + int candidate = edge.number; + int capturePoint = s.capturePoint; + Dictionary subst = varSubst[candidate]; + + string implName = edge.Parent.Name.GetDeclName(); + var info = s.info.mvInfo; + + if (capturePoint == CALL || capturePoint == RETURN) + { + lastCandidate = candidate; + lastCapturePoint = capturePoint; + continue; + } + + Contract.Assume(0 <= capturePoint && capturePoint < info.CapturePoints.Count); + VC.ModelViewInfo.Mapping map = info.CapturePoints[capturePoint]; + var prevInc = (lastCapturePoint != CALL && lastCapturePoint != RETURN && candidate == lastCandidate) + ? info.CapturePoints[lastCapturePoint].IncarnationMap : new Dictionary(); + var cs = m.MkState(map.Description); + + foreach (Variable v in info.AllVariables) + { + var e = (Expr)map.IncarnationMap[v]; + + if (e == null) + { + if (lastCapturePoint == CALL || lastCapturePoint == RETURN) + { + cs.AddBinding(v.Name, GetModelValue(m, v, subst)); + } + continue; + } + + if (lastCapturePoint != CALL && lastCapturePoint != RETURN && prevInc[v] == e) continue; // skip unchanged variables + + Model.Element elt; + if (e is IdentifierExpr) + { + IdentifierExpr ide = (IdentifierExpr)e; + elt = GetModelValue(m, ide.Decl, subst); + } + else if (e is LiteralExpr) + { + LiteralExpr lit = (LiteralExpr)e; + elt = m.MkElement(lit.Val.ToString()); + } + else + { + Contract.Assume(false); + elt = m.MkFunc(e.ToString(), 0).GetConstant(); + } + cs.AddBinding(v.Name, elt); + } + + lastCandidate = candidate; + lastCapturePoint = capturePoint; + } + + return; + } + + + public readonly static int CALL = -1; + public readonly static int RETURN = -2; + + private Model.Element GetModelValue(Model m, Variable v, Dictionary subst) + { + // first, get the unique name + string uniqueName; + + VCExprVar vvar = boogieContext.BoogieExprTranslator.TryLookupVariable(v); + + uniqueName = v.Name; + + if(subst.ContainsKey(uniqueName)) + return m.MkElement(subst[uniqueName]); + return m.MkFunc("@undefined", 0).GetConstant(); + } + + class InternalError : Exception { + } + + + private BinaryOperator.Opcode VCOpToOp (VCExprOp op) + { + if (op == VCExpressionGenerator.AddIOp) + return BinaryOperator.Opcode.Add; + if (op == VCExpressionGenerator.SubIOp) + return BinaryOperator.Opcode.Sub; + if (op == VCExpressionGenerator.MulIOp) + return BinaryOperator.Opcode.Mul; + if (op == VCExpressionGenerator.DivIOp) + return BinaryOperator.Opcode.Div; + if (op == VCExpressionGenerator.EqOp) + return BinaryOperator.Opcode.Eq; + if (op == VCExpressionGenerator.LeOp) + return BinaryOperator.Opcode.Le; + if (op == VCExpressionGenerator.LtOp) + return BinaryOperator.Opcode.Lt; + if (op == VCExpressionGenerator.GeOp) + return BinaryOperator.Opcode.Ge; + if (op == VCExpressionGenerator.GtOp) + return BinaryOperator.Opcode.Gt; + if (op == VCExpressionGenerator.AndOp) + return BinaryOperator.Opcode.And; + if (op == VCExpressionGenerator.OrOp) + return BinaryOperator.Opcode.Or; + throw new InternalError(); + } + + private Expr MakeBinary (BinaryOperator.Opcode op, List args) + { + if(args.Count == 0){ + // with zero args we need the identity of the op + switch(op){ + case BinaryOperator.Opcode.And: + return Expr.True; + case BinaryOperator.Opcode.Or: + return Expr.False; + case BinaryOperator.Opcode.Add: + return new LiteralExpr(Token.NoToken,Microsoft.Basetypes.BigNum.ZERO); + default: + throw new InternalError(); + } + } + var temp = args[0]; + for(int i = 1; i < args.Count; i++) + temp = Expr.Binary(Token.NoToken,op,temp,args[i]); + return temp; + } + + private Variable MakeVar(VCExprVar v){ + var foo = new TypedIdent(Token.NoToken,v.Name.ToString(),v.Type); + return new BoundVariable(Token.NoToken,foo); + } + + private Expr VCExprToExpr (VCExpr e, Dictionary bound) + { + if (e is VCExprVar) { + if(bound.ContainsKey(e)) + return bound[e]; + return Expr.Ident(MakeVar(e as VCExprVar)); // TODO: this isn't right + } + if (e is VCExprIntLit) { + var n = e as VCExprIntLit; + return new LiteralExpr(Token.NoToken,n.Val); + } + if (e is VCExprNAry) { + var f = e as VCExprNAry; + var args = new List(); + for(int i = 0; i < f.Arity; i++){ + args.Add (VCExprToExpr (f[i],bound)); + } + + if(f.Op == VCExpressionGenerator.NotOp) + return Expr.Unary(Token.NoToken, UnaryOperator.Opcode.Not, args[0]); + + if(f.Op == VCExpressionGenerator.IfThenElseOp) + return new NAryExpr(Token.NoToken,new IfThenElse(Token.NoToken),args); + + if(f.Op is VCExprSelectOp){ + var idx = new List(); + idx.Add(args[1]); + return Expr.Select(args[0],idx); + } + + if(f.Op is VCExprStoreOp){ + var idx = new List(); + idx.Add(args[1]); + return Expr.Store(args[0],idx,args[2]); + } + + if (f.Op is VCExprBoogieFunctionOp) + { + return new NAryExpr(Token.NoToken, + new FunctionCall((f.Op as VCExprBoogieFunctionOp).Func), args); + } + + var op = VCOpToOp (f.Op); + return MakeBinary(op,args); + } + + if(e is VCExprQuantifier) { + var f = e as VCExprQuantifier; + var vs = new List(); + var new_bound = new Dictionary(bound); + foreach(var v in f.BoundVars){ + var ve = MakeVar(v); + vs.Add(ve); + new_bound.Add (v,Expr.Ident (ve)); + } + var bd = VCExprToExpr(f.Body,new_bound); + if(f.Quan == Quantifier.EX) + return new ExistsExpr(Token.NoToken,vs,bd); + else + return new ForallExpr(Token.NoToken,vs,bd); + } + if (e == VCExpressionGenerator.True) { + return Expr.True; + } + if (e == VCExpressionGenerator.False) { + return Expr.False; + } + if (e is VCExprLet) { + + } + + throw new InternalError(); + } + + + } + + +} diff --git a/Source/VCGeneration/OrderingAxioms.cs b/Source/VCGeneration/OrderingAxioms.cs index dbb97764..9284601f 100644 --- a/Source/VCGeneration/OrderingAxioms.cs +++ b/Source/VCGeneration/OrderingAxioms.cs @@ -1,338 +1,338 @@ -//----------------------------------------------------------------------------- -// -// Copyright (C) Microsoft Corporation. All Rights Reserved. -// -//----------------------------------------------------------------------------- -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -using System.Linq; -using System.Diagnostics.Contracts; -using Microsoft.Boogie.VCExprAST; - -// Class for constructing and collecting the axioms of the partial -// order <:. The class also manages "unique" attributes of constants -// and generated the necessary assumptions for the theorem prover. - -// TODO: there should be an interface so that different ways to handle -// ordering relations can be accessed uniformly - -namespace Microsoft.Boogie { - - public class OrderingAxiomBuilder { - [ContractInvariantMethod] - void ObjectInvariant() { - Contract.Invariant(Gen != null); - Contract.Invariant(Translator != null); - Contract.Invariant(cce.NonNullDictionaryAndValues(OneStepFuns)); - Contract.Invariant(cce.NonNullElements(Constants)); - Contract.Invariant(cce.NonNullElements(CompleteConstantsOpen)); - Contract.Invariant(cce.NonNullElements(AllAxioms)); - Contract.Invariant(cce.NonNullElements(IncAxioms)); - } - - - private readonly VCExpressionGenerator Gen; - private readonly Boogie2VCExprTranslator Translator; - private readonly IDictionary OneStepFuns; - private readonly List Constants = new List(); - - // A list to handle constants whose direct children are fully - // specified (the "complete" keyword). Constants are removed from - // the list as soon as the corresponding axiom has been generated, - // which means that from this point on no further children can be - // added - private readonly List CompleteConstantsOpen = new List(); - - // list in which all axioms are collected - private readonly List AllAxioms = new List(); - - // list in which axioms are incrementally collected - private readonly List IncAxioms = new List(); - - - public OrderingAxiomBuilder(VCExpressionGenerator gen, - Boogie2VCExprTranslator translator) { - Contract.Requires(gen != null); - Contract.Requires(translator != null); - this.Gen = gen; - this.Translator = translator; - OneStepFuns = new Dictionary(); - Constants = new List(); - CompleteConstantsOpen = new List(); - AllAxioms = new List(); - IncAxioms = new List(); - } - - public OrderingAxiomBuilder(VCExpressionGenerator gen, - Boogie2VCExprTranslator translator, - OrderingAxiomBuilder builder) { - Contract.Requires(gen != null); - Contract.Requires(translator != null); - Contract.Requires(builder != null); - this.Gen = gen; - this.Translator = translator; - OneStepFuns = new Dictionary(builder.OneStepFuns); - Constants = new List(builder.Constants); - CompleteConstantsOpen = new List(builder.CompleteConstantsOpen); - AllAxioms = new List(builder.AllAxioms); - IncAxioms = new List(builder.IncAxioms); - } - - //////////////////////////////////////////////////////////////////////////// - - // Used to axiomatise the disjoint-sub-dag specs that are - // described by parents with the "unique" flag - - - private Function OneStepFunFor(Type t) { - Contract.Requires(t != null); - Contract.Ensures(Contract.Result() != null); - - Function res; - if (!OneStepFuns.TryGetValue(t, out res)) { - List args = new List(); - args.Add(new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "arg0", t), true)); - args.Add(new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "arg1", t), true)); - Formal result = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "res", t), false); - res = new Function(Token.NoToken, "oneStep", new List(), args, result); - OneStepFuns.Add(t, res); - } - return cce.NonNull(res); - } - - //////////////////////////////////////////////////////////////////////////// - - - private void AddAxiom(VCExpr axiom) { - Contract.Requires(axiom != null); - if (axiom.Equals(VCExpressionGenerator.True)) - return; - AllAxioms.Add(axiom); - IncAxioms.Add(axiom); - } - - // Return all axioms that were added since the last time NewAxioms - // was called - public VCExpr GetNewAxioms() { - Contract.Ensures(Contract.Result() != null); - - CloseChildrenCompleteConstants(); - VCExpr res = Gen.NAry(VCExpressionGenerator.AndOp, IncAxioms); - IncAxioms.Clear(); - return res; - } - - // return all axioms - public VCExpr Axioms { - get { - Contract.Ensures(Contract.Result() != null); - - CloseChildrenCompleteConstants(); - return Gen.NAry(VCExpressionGenerator.AndOp, AllAxioms); - } - } - - //////////////////////////////////////////////////////////////////////////// - - // Generate the normal axioms for a partial order relation - public void Setup() { - TypeVariable alpha = new TypeVariable(Token.NoToken, "alpha"); - Contract.Assert(alpha != null); - List typeParams = new List(); - typeParams.Add(alpha); - - List triggers = new List(); - - VCExprVar x = Gen.Variable("x", alpha); - Contract.Assert(x != null); - VCExprVar y = Gen.Variable("y", alpha); - Contract.Assert(y != null); - VCExprVar z = Gen.Variable("z", alpha); - Contract.Assert(z != null); - - List boundVars = new List(); - - // reflexivity - boundVars.Add(x); - AddAxiom(Gen.Forall(typeParams, boundVars, triggers, - new VCQuantifierInfos("bg:subtype-refl", -1, false, null), - Gen.AtMost(x, x))); - - // transitivity - boundVars = new List(); - boundVars.Add(x); - boundVars.Add(y); - boundVars.Add(z); - triggers = new List(); - triggers.Add(Gen.Trigger(true, Gen.AtMost(x, y), Gen.AtMost(y, z))); - VCExpr body = Gen.Implies(Gen.And(Gen.AtMost(x, y), Gen.AtMost(y, z)), - Gen.AtMost(x, z)); - Contract.Assert(body != null); - AddAxiom(Gen.Forall(typeParams, boundVars, triggers, - new VCQuantifierInfos("bg:subtype-trans", -1, false, null), - body)); - - // anti-symmetry - boundVars = new List(); - boundVars.Add(x); - boundVars.Add(y); - triggers = new List(); - triggers.Add(Gen.Trigger(true, Gen.AtMost(x, y), Gen.AtMost(y, x))); - body = Gen.Implies(Gen.And(Gen.AtMost(x, y), Gen.AtMost(y, x)), - Gen.Eq(x, y)); - AddAxiom(Gen.Forall(typeParams, boundVars, triggers, - new VCQuantifierInfos("bg:subtype-antisymm", -1, false, null), - body)); - } - - //////////////////////////////////////////////////////////////////////////// - - public void AddConstant(Constant c) { - Contract.Requires(c != null); - AddAxiom(GenParentConstraints(c)); - Constants.Add(c); - if (c.ChildrenComplete) - CompleteConstantsOpen.Add(c); - - // ensure that no further children are added to closed - // children-complete constants - Contract.Assert(!(c.Parents != null && Contract.Exists(c.Parents, p => cce.NonNull((Constant)p.Parent.Decl).ChildrenComplete && !CompleteConstantsOpen.Contains((Constant)p.Parent.Decl)))); - } - - // Generate the constraints telling that parents of a constant are - // strictly greater than the constant itself, and are the minimal - // elements with this property - private VCExpr GenParentConstraints(Constant c) { - Contract.Requires(c != null); - Contract.Ensures(Contract.Result() != null); - - VCExpr res = VCExpressionGenerator.True; - - if (c.Parents == null) - return res; - - VCExprVar cAsVar = Translator.LookupVariable(c); - VCExprVar w = Gen.Variable("w", c.TypedIdent.Type); - - // Parents of c are proper ancestors of c - foreach (ConstantParent p in c.Parents) { - Contract.Assert(p != null); - VCExprVar par = Translator.LookupVariable(cce.NonNull(p.Parent.Decl)); - res = Gen.AndSimp(res, Gen.Neq(cAsVar, par)); - res = Gen.AndSimp(res, Gen.AtMost(cAsVar, par)); - } - - // Parents are direct ancestors of c (no other elements are in - // between c and a parent) - foreach (ConstantParent p in c.Parents) { - Contract.Assert(p != null); - VCExprVar par = Translator.LookupVariable(cce.NonNull(p.Parent.Decl)); - Contract.Assert(par != null); - VCExpr antecedent1 = Gen.AtMost(cAsVar, w); - Contract.Assert(antecedent1 != null); - VCExpr antecedent2 = Gen.AtMost(w, par); - Contract.Assert(antecedent2 != null); - VCExpr body = Gen.Implies(Gen.And(antecedent1, antecedent2), - Gen.Or(Gen.Eq(cAsVar, w), Gen.Eq(par, w))); - Contract.Assert(body != null); - res = Gen.AndSimp(res, - Gen.Forall(w, - Gen.Trigger(true, antecedent1, antecedent2), - body)); - } - - // Ancestors of c are only c itself and the ancestors of the - // parents of c - VCExpr minAncestors = Gen.Eq(cAsVar, w); - Contract.Assert(minAncestors != null); - foreach (ConstantParent p in c.Parents) { - Contract.Assert(p != null); - minAncestors = - Gen.Or(minAncestors, - Gen.AtMost(Translator.LookupVariable(cce.NonNull(p.Parent.Decl)), w)); - } - VCExpr antecedent = Gen.AtMost(cAsVar, w); - Contract.Assert(antecedent != null); - res = Gen.AndSimp(res, - Gen.Forall(w, - Gen.Trigger(true, antecedent), - Gen.Implies(antecedent, minAncestors))); - - // Constraints for unique child-parent edges - foreach (ConstantParent p in c.Parents) { - Contract.Assert(p != null); - if (p.Unique) - res = - Gen.AndSimp(res, - GenUniqueParentConstraint(c, cce.NonNull((Constant)p.Parent.Decl))); - } - - return res; - } - - // Generate axioms that state that all direct children of c are - // specified; this is the dual of the axiom stating that all direct - // ancestors of a constant are known - private VCExpr GenCompleteChildrenConstraints(Constant c) { - Contract.Requires(c != null); - Contract.Requires(c.ChildrenComplete); - Contract.Ensures(Contract.Result() != null); - - - VCExprVar cAsVar = Translator.LookupVariable(c); - VCExprVar w = Gen.Variable("w", c.TypedIdent.Type); - - VCExpr maxDescendants = Gen.Eq(cAsVar, w); - foreach (Constant d in Constants) { - Contract.Assert(d != null); - if (d.Parents != null && d.Parents.Any(p => c.Equals(p.Parent.Decl))) - maxDescendants = Gen.Or(maxDescendants, - Gen.AtMost(w, Translator.LookupVariable(d))); - } - - VCExpr antecedent = Gen.AtMost(w, cAsVar); - Contract.Assert(antecedent != null); - return Gen.Forall(w, - Gen.Trigger(true, antecedent), - Gen.Implies(antecedent, maxDescendants)); - } - - private void CloseChildrenCompleteConstants() { - foreach (Constant c in CompleteConstantsOpen) { - Contract.Assert(c != null); - AddAxiom(GenCompleteChildrenConstraints(c)); - } - CompleteConstantsOpen.Clear(); - } - - // Generate the axiom ensuring that the sub-dags underneath unique - // child-parent edges are all disjoint - private VCExpr GenUniqueParentConstraint(Constant child, Constant parent) { - Contract.Requires(child != null); - Contract.Requires(parent != null); - Contract.Requires(child.TypedIdent.Type.Equals(parent.TypedIdent.Type)); - Contract.Ensures(Contract.Result() != null); - - - - VCExprVar w = Gen.Variable("w", child.TypedIdent.Type); - Contract.Assert(w != null); - VCExpr antecedent = - Gen.AtMost(w, Translator.LookupVariable(child)); - Contract.Assert(antecedent != null); - VCExpr succedent = - Gen.Eq(Gen.Function(OneStepFunFor(child.TypedIdent.Type), - Translator.LookupVariable(parent), w), - Translator.LookupVariable(child)); - Contract.Assert(succedent != null); - - return Gen.Forall(w, - Gen.Trigger(true, antecedent), - Gen.Implies(antecedent, succedent)); - } - - } - -} +//----------------------------------------------------------------------------- +// +// Copyright (C) Microsoft Corporation. All Rights Reserved. +// +//----------------------------------------------------------------------------- +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Linq; +using System.Diagnostics.Contracts; +using Microsoft.Boogie.VCExprAST; + +// Class for constructing and collecting the axioms of the partial +// order <:. The class also manages "unique" attributes of constants +// and generated the necessary assumptions for the theorem prover. + +// TODO: there should be an interface so that different ways to handle +// ordering relations can be accessed uniformly + +namespace Microsoft.Boogie { + + public class OrderingAxiomBuilder { + [ContractInvariantMethod] + void ObjectInvariant() { + Contract.Invariant(Gen != null); + Contract.Invariant(Translator != null); + Contract.Invariant(cce.NonNullDictionaryAndValues(OneStepFuns)); + Contract.Invariant(cce.NonNullElements(Constants)); + Contract.Invariant(cce.NonNullElements(CompleteConstantsOpen)); + Contract.Invariant(cce.NonNullElements(AllAxioms)); + Contract.Invariant(cce.NonNullElements(IncAxioms)); + } + + + private readonly VCExpressionGenerator Gen; + private readonly Boogie2VCExprTranslator Translator; + private readonly IDictionary OneStepFuns; + private readonly List Constants = new List(); + + // A list to handle constants whose direct children are fully + // specified (the "complete" keyword). Constants are removed from + // the list as soon as the corresponding axiom has been generated, + // which means that from this point on no further children can be + // added + private readonly List CompleteConstantsOpen = new List(); + + // list in which all axioms are collected + private readonly List AllAxioms = new List(); + + // list in which axioms are incrementally collected + private readonly List IncAxioms = new List(); + + + public OrderingAxiomBuilder(VCExpressionGenerator gen, + Boogie2VCExprTranslator translator) { + Contract.Requires(gen != null); + Contract.Requires(translator != null); + this.Gen = gen; + this.Translator = translator; + OneStepFuns = new Dictionary(); + Constants = new List(); + CompleteConstantsOpen = new List(); + AllAxioms = new List(); + IncAxioms = new List(); + } + + public OrderingAxiomBuilder(VCExpressionGenerator gen, + Boogie2VCExprTranslator translator, + OrderingAxiomBuilder builder) { + Contract.Requires(gen != null); + Contract.Requires(translator != null); + Contract.Requires(builder != null); + this.Gen = gen; + this.Translator = translator; + OneStepFuns = new Dictionary(builder.OneStepFuns); + Constants = new List(builder.Constants); + CompleteConstantsOpen = new List(builder.CompleteConstantsOpen); + AllAxioms = new List(builder.AllAxioms); + IncAxioms = new List(builder.IncAxioms); + } + + //////////////////////////////////////////////////////////////////////////// + + // Used to axiomatise the disjoint-sub-dag specs that are + // described by parents with the "unique" flag + + + private Function OneStepFunFor(Type t) { + Contract.Requires(t != null); + Contract.Ensures(Contract.Result() != null); + + Function res; + if (!OneStepFuns.TryGetValue(t, out res)) { + List args = new List(); + args.Add(new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "arg0", t), true)); + args.Add(new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "arg1", t), true)); + Formal result = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "res", t), false); + res = new Function(Token.NoToken, "oneStep", new List(), args, result); + OneStepFuns.Add(t, res); + } + return cce.NonNull(res); + } + + //////////////////////////////////////////////////////////////////////////// + + + private void AddAxiom(VCExpr axiom) { + Contract.Requires(axiom != null); + if (axiom.Equals(VCExpressionGenerator.True)) + return; + AllAxioms.Add(axiom); + IncAxioms.Add(axiom); + } + + // Return all axioms that were added since the last time NewAxioms + // was called + public VCExpr GetNewAxioms() { + Contract.Ensures(Contract.Result() != null); + + CloseChildrenCompleteConstants(); + VCExpr res = Gen.NAry(VCExpressionGenerator.AndOp, IncAxioms); + IncAxioms.Clear(); + return res; + } + + // return all axioms + public VCExpr Axioms { + get { + Contract.Ensures(Contract.Result() != null); + + CloseChildrenCompleteConstants(); + return Gen.NAry(VCExpressionGenerator.AndOp, AllAxioms); + } + } + + //////////////////////////////////////////////////////////////////////////// + + // Generate the normal axioms for a partial order relation + public void Setup() { + TypeVariable alpha = new TypeVariable(Token.NoToken, "alpha"); + Contract.Assert(alpha != null); + List typeParams = new List(); + typeParams.Add(alpha); + + List triggers = new List(); + + VCExprVar x = Gen.Variable("x", alpha); + Contract.Assert(x != null); + VCExprVar y = Gen.Variable("y", alpha); + Contract.Assert(y != null); + VCExprVar z = Gen.Variable("z", alpha); + Contract.Assert(z != null); + + List boundVars = new List(); + + // reflexivity + boundVars.Add(x); + AddAxiom(Gen.Forall(typeParams, boundVars, triggers, + new VCQuantifierInfos("bg:subtype-refl", -1, false, null), + Gen.AtMost(x, x))); + + // transitivity + boundVars = new List(); + boundVars.Add(x); + boundVars.Add(y); + boundVars.Add(z); + triggers = new List(); + triggers.Add(Gen.Trigger(true, Gen.AtMost(x, y), Gen.AtMost(y, z))); + VCExpr body = Gen.Implies(Gen.And(Gen.AtMost(x, y), Gen.AtMost(y, z)), + Gen.AtMost(x, z)); + Contract.Assert(body != null); + AddAxiom(Gen.Forall(typeParams, boundVars, triggers, + new VCQuantifierInfos("bg:subtype-trans", -1, false, null), + body)); + + // anti-symmetry + boundVars = new List(); + boundVars.Add(x); + boundVars.Add(y); + triggers = new List(); + triggers.Add(Gen.Trigger(true, Gen.AtMost(x, y), Gen.AtMost(y, x))); + body = Gen.Implies(Gen.And(Gen.AtMost(x, y), Gen.AtMost(y, x)), + Gen.Eq(x, y)); + AddAxiom(Gen.Forall(typeParams, boundVars, triggers, + new VCQuantifierInfos("bg:subtype-antisymm", -1, false, null), + body)); + } + + //////////////////////////////////////////////////////////////////////////// + + public void AddConstant(Constant c) { + Contract.Requires(c != null); + AddAxiom(GenParentConstraints(c)); + Constants.Add(c); + if (c.ChildrenComplete) + CompleteConstantsOpen.Add(c); + + // ensure that no further children are added to closed + // children-complete constants + Contract.Assert(!(c.Parents != null && Contract.Exists(c.Parents, p => cce.NonNull((Constant)p.Parent.Decl).ChildrenComplete && !CompleteConstantsOpen.Contains((Constant)p.Parent.Decl)))); + } + + // Generate the constraints telling that parents of a constant are + // strictly greater than the constant itself, and are the minimal + // elements with this property + private VCExpr GenParentConstraints(Constant c) { + Contract.Requires(c != null); + Contract.Ensures(Contract.Result() != null); + + VCExpr res = VCExpressionGenerator.True; + + if (c.Parents == null) + return res; + + VCExprVar cAsVar = Translator.LookupVariable(c); + VCExprVar w = Gen.Variable("w", c.TypedIdent.Type); + + // Parents of c are proper ancestors of c + foreach (ConstantParent p in c.Parents) { + Contract.Assert(p != null); + VCExprVar par = Translator.LookupVariable(cce.NonNull(p.Parent.Decl)); + res = Gen.AndSimp(res, Gen.Neq(cAsVar, par)); + res = Gen.AndSimp(res, Gen.AtMost(cAsVar, par)); + } + + // Parents are direct ancestors of c (no other elements are in + // between c and a parent) + foreach (ConstantParent p in c.Parents) { + Contract.Assert(p != null); + VCExprVar par = Translator.LookupVariable(cce.NonNull(p.Parent.Decl)); + Contract.Assert(par != null); + VCExpr antecedent1 = Gen.AtMost(cAsVar, w); + Contract.Assert(antecedent1 != null); + VCExpr antecedent2 = Gen.AtMost(w, par); + Contract.Assert(antecedent2 != null); + VCExpr body = Gen.Implies(Gen.And(antecedent1, antecedent2), + Gen.Or(Gen.Eq(cAsVar, w), Gen.Eq(par, w))); + Contract.Assert(body != null); + res = Gen.AndSimp(res, + Gen.Forall(w, + Gen.Trigger(true, antecedent1, antecedent2), + body)); + } + + // Ancestors of c are only c itself and the ancestors of the + // parents of c + VCExpr minAncestors = Gen.Eq(cAsVar, w); + Contract.Assert(minAncestors != null); + foreach (ConstantParent p in c.Parents) { + Contract.Assert(p != null); + minAncestors = + Gen.Or(minAncestors, + Gen.AtMost(Translator.LookupVariable(cce.NonNull(p.Parent.Decl)), w)); + } + VCExpr antecedent = Gen.AtMost(cAsVar, w); + Contract.Assert(antecedent != null); + res = Gen.AndSimp(res, + Gen.Forall(w, + Gen.Trigger(true, antecedent), + Gen.Implies(antecedent, minAncestors))); + + // Constraints for unique child-parent edges + foreach (ConstantParent p in c.Parents) { + Contract.Assert(p != null); + if (p.Unique) + res = + Gen.AndSimp(res, + GenUniqueParentConstraint(c, cce.NonNull((Constant)p.Parent.Decl))); + } + + return res; + } + + // Generate axioms that state that all direct children of c are + // specified; this is the dual of the axiom stating that all direct + // ancestors of a constant are known + private VCExpr GenCompleteChildrenConstraints(Constant c) { + Contract.Requires(c != null); + Contract.Requires(c.ChildrenComplete); + Contract.Ensures(Contract.Result() != null); + + + VCExprVar cAsVar = Translator.LookupVariable(c); + VCExprVar w = Gen.Variable("w", c.TypedIdent.Type); + + VCExpr maxDescendants = Gen.Eq(cAsVar, w); + foreach (Constant d in Constants) { + Contract.Assert(d != null); + if (d.Parents != null && d.Parents.Any(p => c.Equals(p.Parent.Decl))) + maxDescendants = Gen.Or(maxDescendants, + Gen.AtMost(w, Translator.LookupVariable(d))); + } + + VCExpr antecedent = Gen.AtMost(w, cAsVar); + Contract.Assert(antecedent != null); + return Gen.Forall(w, + Gen.Trigger(true, antecedent), + Gen.Implies(antecedent, maxDescendants)); + } + + private void CloseChildrenCompleteConstants() { + foreach (Constant c in CompleteConstantsOpen) { + Contract.Assert(c != null); + AddAxiom(GenCompleteChildrenConstraints(c)); + } + CompleteConstantsOpen.Clear(); + } + + // Generate the axiom ensuring that the sub-dags underneath unique + // child-parent edges are all disjoint + private VCExpr GenUniqueParentConstraint(Constant child, Constant parent) { + Contract.Requires(child != null); + Contract.Requires(parent != null); + Contract.Requires(child.TypedIdent.Type.Equals(parent.TypedIdent.Type)); + Contract.Ensures(Contract.Result() != null); + + + + VCExprVar w = Gen.Variable("w", child.TypedIdent.Type); + Contract.Assert(w != null); + VCExpr antecedent = + Gen.AtMost(w, Translator.LookupVariable(child)); + Contract.Assert(antecedent != null); + VCExpr succedent = + Gen.Eq(Gen.Function(OneStepFunFor(child.TypedIdent.Type), + Translator.LookupVariable(parent), w), + Translator.LookupVariable(child)); + Contract.Assert(succedent != null); + + return Gen.Forall(w, + Gen.Trigger(true, antecedent), + Gen.Implies(antecedent, succedent)); + } + + } + +} diff --git a/Source/VCGeneration/RPFP.cs b/Source/VCGeneration/RPFP.cs index ed3842d5..9d38eb47 100644 --- a/Source/VCGeneration/RPFP.cs +++ b/Source/VCGeneration/RPFP.cs @@ -1,609 +1,609 @@ -//----------------------------------------------------------------------------- -// -// Copyright (C) 2012 Microsoft Corporation. All Rights Reserved. -// -//----------------------------------------------------------------------------- - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using Term = Microsoft.Boogie.VCExprAST.VCExpr; -using FuncDecl = Microsoft.Boogie.VCExprAST.VCExprOp; -using Sort = Microsoft.Boogie.Type; -using Microsoft.Boogie.VCExprAST; - - -using Microsoft.Boogie.ExprExtensions; - - -namespace Microsoft.Boogie -{ - - - - - /** This class represents a relation post-fixed point (RPFP) problem as - * a "problem graph". The graph consists of Nodes and hyper-edges. - * - * A node consists of - * - Annotation, a symbolic relation - * - Bound, a symbolic relation giving an upper bound on Annotation - * - * - * A hyper-edge consists of: - * - Children, a sequence of children Nodes, - * - F, a symbolic relational transformer, - * - Parent, a single parent Node. - * - * The graph is "solved" when: - * - For every Node n, n.Annotation subseteq n.Bound - * - For every hyperedge e, e.F(e.Children.Annotation) subseteq e.Parent.Annotation - * - * where, if x is a sequence of Nodes, x.Annotation is the sequences - * of Annotations of the nodes in the sequence. - * - * A symbolic Transformer consists of - * - RelParams, a sequence of relational symbols - * - IndParams, a sequence of individual symbols - * - Formula, a formula over RelParams and IndParams - * - * A Transformer t represents a function that takes sequence R of relations - * and yields the relation lambda (t.Indparams). Formula(R/RelParams). - * - * As a special case, a nullary Transformer (where RelParams is the empty sequence) - * represents a fixed relation. - * - * An RPFP consists of - * - Nodes, a set of Nodes - * - Edges, a set of hyper-edges - * - Context, a prover context that contains formula AST's - * - * Multiple RPFP's can use the same Context, but you should be careful - * that only one RPFP asserts constraints in the context at any time. - * - * */ - public class RPFP - { - /** Symbolic representation of a relational transformer */ - public class Transformer - { - public FuncDecl[] RelParams; - public Term[] IndParams; - public Term Formula; - public RPFP owner; - - public Transformer Clone() - { - return (Transformer)this.MemberwiseClone(); - } - } - - /** Create a symbolic transformer. */ - public Transformer CreateTransformer(FuncDecl[] _RelParams, Term[] _IndParams, Term _Formula) - { - Transformer t = new Transformer(); - t.RelParams = _RelParams; - t.IndParams = _IndParams; - t.Formula = _Formula; - t.owner = this; - return t; - } - - /** Create a relation (nullary relational transformer) */ - public Transformer CreateRelation(Term[] _IndParams, Term _Formula) - { - return CreateTransformer(new FuncDecl[0], _IndParams, _Formula); - } - - /** A node in the RPFP graph */ - public class Node - { - public FuncDecl Name; - public Transformer Annotation; - public Transformer Bound; - public RPFP owner; - public int number; - public Edge Outgoing; - public List Incoming; - public Term dual; - public Node map; - } - - /** Create a node in the graph. The input is a term R(v_1...v_n) - * where R is an arbitrary relational symbol and v_1...v_n are - * arbitary distinct variables. The names are only of mnemonic value, - * however, the number and type of arguments determine the type - * of the relation at this node. */ - - public Node CreateNode(Term t) - { - Node n = new Node(); - // Microsoft.Boogie.VCExprAST.VCExprNAry tn = t as Microsoft.Boogie.VCExprAST.VCExprNAry; - // Term[] _IndParams = tn.ToArray(); - Term[] _IndParams = t.GetAppArgs(); - FuncDecl Name = t.GetAppDecl(); - n.Annotation = CreateRelation(_IndParams,ctx.MkTrue()); - n.Bound = CreateRelation(_IndParams, ctx.MkTrue()); - n.owner = this; - n.number = ++nodeCount; - n.Name = Name; // just to have a unique name - n.Incoming = new List(); - return n; - } - - /** Clone a node (can be from another graph). */ - - public Node CloneNode(Node old) - { - Node n = new Node(); - n.Annotation = old.Annotation.Clone(); - n.Bound = old.Bound.Clone(); - n.owner = this; - n.number = ++nodeCount; - n.Name = old.Name; // just to have a unique name - n.Incoming = new List(); - return n; - } - - /** This class represents a hyper-edge in the RPFP graph */ - - public class Edge - { - public Transformer F; - public Node Parent; - public Node[] Children; - public RPFP owner; - public int number; - public Edge map; - public HashSet labels; - internal Term dual; - internal TermDict valuation; - } - - - /** Create a hyper-edge. */ - public Edge CreateEdge(Node _Parent, Transformer _F, Node[] _Children) - { - Edge e = new Edge(); - e.Parent = _Parent; - e.F = _F; - e.Children = _Children; - e.owner = this; - e.number = ++edgeCount; - _Parent.Outgoing = e; - foreach (var c in _Children) - if(c != null) - c.Incoming.Add(e); - return e; - } - - /** Create an edge that lower-bounds its parent. */ - public Edge CreateLowerBoundEdge(Node _Parent) - { - return CreateEdge(_Parent, _Parent.Annotation, new RPFP.Node[0]); - } - - - - - /** Assert a background axiom. Background axioms can be used to provide the - * theory of auxilliary functions or relations. All symbols appearing in - * background axioms are considered global, and may appear in both transformer - * and relational solutions. Semantically, a solution to the RPFP gives - * an interpretation of the unknown relations for each interpretation of the - * auxilliary symbols that is consistent with the axioms. Axioms should be - * asserted before any calls to Push. They cannot be de-asserted by Pop. */ - - public void AssertAxiom(Term t) - { - ctx.AddAxiom(t); - } - - /** Do not call this. */ - - public void RemoveAxiom(Term t) - { - ctx.RemoveAxiom(t); - } - - /** Type of solve results */ - public enum LBool { False, True, Undef }; - - - /** Solve an RPFP graph. This means either strengthen the annotation - * so that the bound at the given root node is satisfied, or - * show that this cannot be done by giving a dual solution - * (i.e., a counterexample). - * - * In the current implementation, this only works for graphs that - * are: - * - tree-like - * - * - closed. - * - * In a tree-like graph, every nod has out most one incoming and one out-going edge, - * and there are no cycles. In a closed graph, every node has exactly one out-going - * edge. This means that the leaves of the tree are all hyper-edges with no - * children. Such an edge represents a relation (nullary transformer) and thus - * a lower bound on its parent. The parameter root must be the root of this tree. - * - * If Solve returns LBool.False, this indicates success. The annotation of the tree - * has been updated to satisfy the upper bound at the root. - * - * If Solve returns LBool.True, this indicates a counterexample. For each edge, - * you can then call Eval to determine the values of symbols in the transformer formula. - * You can also call Empty on a node to determine if its value in the counterexample - * is the empty relation. - * - * \param root The root of the tree - * \param persist Number of context pops through which result should persist - * - * - */ - - public LBool Solve(Node root, int persist) - { - return LBool.False; // TODO - } - - - /** Dispose of the dual model (counterexample) if there is one. */ - - public void DisposeDualModel() - { - // TODO dualModel = null; - } - - - /** Determines the value in the counterexample of a symbol occuring in the transformer formula of - * a given edge. */ - - public Term Eval(Edge e, Term t) - { - if (e.valuation == null) - e.valuation = new TermDict< Term>(); - if (e.valuation.ContainsKey(t)) - return e.valuation[t]; - return null; // TODO - } - - /** Sets the value in the counterexample of a symbol occuring in the transformer formula of - * a given edge. */ - - public void SetValue(Edge e, Term variable, Term value) - { - if (e.valuation == null) - e.valuation = new TermDict< Term>(); - e.valuation.Add(variable, value); - } - - - /** Returns true if the given node is empty in the primal solution. For proecudure summaries, - this means that the procedure is not called in the current counter-model. */ - - public bool Empty(Node p) - { - return false; // TODO - } - - /** Push a scope. Assertions made after Push can be undone by Pop. */ - - public void Push() - { - stack.Push(new stack_entry()); - // TODO: do we need push/pop? - } - - /** Pop a scope (see Push). Note, you cannot pop axioms. */ - - public void Pop(int num_scopes) - { - //TODO ctx.Pop((uint)num_scopes); - for (uint i = 0; i < num_scopes; i++) - { - stack_entry back = stack.Pop(); - foreach (var e in back.edges) - e.dual = null; - foreach (var n in back.nodes) - n.dual = null; - } - } - - public Context ctx; - - public class LogicSolver { - public Context ctx; - }; - - public LogicSolver solver; - - static public LogicSolver CreateLogicSolver(Context _ctx){ - LogicSolver res = new LogicSolver(); - res.ctx = _ctx; - return res; - } - - /** This represents a conjecture that a given node is upper-boudned - by bound. */ - public class Conjecture - { - public Node node; - public Transformer bound; - } - - /** This is a list of conjectures generated during solving. */ - - public List conjectures = new List(); - - /** Construct an RPFP graph with a given interpolating prover context. It is allowed to - have multiple RPFP's use the same context, but you should never have teo RPFP's - with the same conext asserting nodes or edges at the same time. Note, if you create - axioms in one RPFP, them create a second RPFP with the same context, the second will - inherit the axioms. - */ - - public RPFP(LogicSolver slvr) - { - solver = slvr; - ctx = slvr.ctx; - stack = new Stack(); - stack.Push(new stack_entry()); - } - - - /** Convert an array of clauses to an RPFP. - */ - - public void FromClauses(Term[] clauses){ - FuncDecl failName = ctx.MkFuncDecl("@Fail", ctx.MkBoolSort()); - foreach(var clause in clauses){ - Node foo = GetNodeFromClause(clause,failName); - if(foo != null) - nodes.Add(foo); - } - foreach (var clause in clauses) - edges.Add(GetEdgeFromClause(clause,failName)); - } - - - // This returns a new FuncDel with same sort as top-level function - // of term t, but with numeric suffix appended to name. - - private FuncDecl SuffixFuncDecl(Term t, int n) - { - var name = t.GetAppDecl().GetDeclName() + "_" + n.ToString(); - return ctx.MkFuncDecl(name, t.GetAppDecl()); - } - - // Collect the relational paremeters - - Dictionary relationToNode = new Dictionary(); - - private Term CollectParamsRec(TermDict memo, Term t, List parms, List nodes, Dictionary done) - { - Term res; - if (memo.TryGetValue(t, out res)) - return res; - if (t.GetKind() == TermKind.App) - { - var f = t.GetAppDecl(); - Node node; - if (relationToNode.TryGetValue(f, out node)) - { - if (done.ContainsKey(t)) - res = done[t]; - else - { - f = SuffixFuncDecl(t, parms.Count); - parms.Add(f); - nodes.Add(node); - done.Add(t,res); // don't count same expression twice! - } - } - var args = t.GetAppArgs(); - args = args.Select(x => CollectParamsRec(memo, x, parms, nodes, done)).ToArray(); - res = ctx.CloneApp(t, args); - } // TODO: handle quantifiers - else - res = t; - memo.Add(t, res); - return res; - } - - private bool IsVariable(Term t) - { - // TODO: is this right? - // return t.IsFunctionApp() && t.GetAppArgs().Length == 0; - return t is VCExprVar && !(t is VCExprConstant); - } - - private Edge GetEdgeFromClause(Term t, FuncDecl failName) - { - Term[] args = t.GetAppArgs(); - Term body = args[0]; - Term head = args[1]; - Term[] _IndParams; - FuncDecl Name; - if (head.IsFalse()) - { - Name = failName; - _IndParams = new Term[0]; - } - else - { - _IndParams = head.GetAppArgs(); - Name = head.GetAppDecl(); - } - for(int i = 0; i < _IndParams.Length; i++) - if (!IsVariable(_IndParams[i])) - { - Term v = ctx.MkConst("@a" + i.ToString(), _IndParams[i].GetSort()); - body = ctx.MkAnd(body, ctx.MkEq(v, _IndParams[i])); - _IndParams[i] = v; - } - var relParams = new List(); - var nodeParams = new List(); - var memo = new TermDict< Term>(); - var done = new Dictionary(); // note this hashes on equality, not reference! - body = CollectParamsRec(memo, body, relParams, nodeParams,done); - Transformer F = CreateTransformer(relParams.ToArray(), _IndParams, body); - Node parent = relationToNode[Name]; - return CreateEdge(parent, F, nodeParams.ToArray()); - } - - private Node GetNodeFromClause(Term t, FuncDecl failName) - { - Term[] args = t.GetAppArgs(); - Term body = args[0]; - Term head = args[1]; - FuncDecl Name; - Term[] _IndParams; - bool is_query = false; - if (head.Equals(ctx.MkFalse())) - { - Name = failName; - is_query = true; - _IndParams = new Term[0]; - } - else - { - Name = head.GetAppDecl(); - _IndParams = head.GetAppArgs(); - } - if (relationToNode.ContainsKey(Name)) - return null; - for (int i = 0; i < _IndParams.Length; i++) - if (!IsVariable(_IndParams[i])) - { - Term v = ctx.MkConst("@a" + i.ToString(), _IndParams[i].GetSort()); - _IndParams[i] = v; - } - Term foo = ctx.MkApp(Name, _IndParams); - Node node = CreateNode(foo); - relationToNode[Name] = node; - if (is_query) - node.Bound = CreateRelation(new Term[0], ctx.MkFalse()); - return node; - } - - ///////////////////////////////////////////////////////////////////////////////////////// - // Convert RPFP to Z3 rules - ///////////////////////////////////////////////////////////////////////////////////////// - - /** Get the Z3 rule corresponding to an edge */ - - public Term GetRule(Edge edge) - { - Dictionary predSubst = new Dictionary(); - for (int i = 0; i < edge.Children.Length; i++) - predSubst.Add(edge.F.RelParams[i], edge.Children[i].Name); - Term body = SubstPreds(predSubst, edge.F.Formula); - Term head = ctx.MkApp(edge.Parent.Name, edge.F.IndParams); - var rule = BindVariables(ctx.MkImplies(body, head)); - rule = ctx.Letify(rule); // put in let bindings for theorem prover - return rule; - } - - /** Get the Z3 query corresponding to the conjunction of the node bounds. */ - - public Term GetQuery() - { - List conjuncts = new List(); - foreach (var node in nodes) - { - if (node.Bound.Formula != ctx.MkTrue()) - conjuncts.Add(ctx.MkImplies(ctx.MkApp(node.Name, node.Bound.IndParams), node.Bound.Formula)); - } - Term query = ctx.MkNot(ctx.MkAnd(conjuncts.ToArray())); - query = BindVariables(query,false); // bind variables existentially - query = ctx.Letify(query); // put in let bindings for theorem prover - return query; - } - - private void CollectVariables(TermDict< bool> memo, Term t, List vars) - { - if (memo.ContainsKey(t)) - return; - if (IsVariable(t)) - vars.Add(t); - if (t.GetKind() == TermKind.App) - { - foreach (var s in t.GetAppArgs()) - CollectVariables(memo, s, vars); - } - memo.Add(t, true); - } - - private Term BindVariables(Term t, bool universal = true) - { - TermDict< bool> memo = new TermDict(); - List vars = new List(); - CollectVariables(memo,t,vars); - return universal ? ctx.MkForall(vars.ToArray(), t) : ctx.MkExists(vars.ToArray(), t); - } - - private Term SubstPredsRec(TermDict< Term> memo, Dictionary subst, Term t) - { - Term res; - if (memo.TryGetValue(t, out res)) - return res; - if (t.GetKind() == TermKind.App) - { - var args = t.GetAppArgs(); - args = args.Select(x => SubstPredsRec(memo,subst,x)).ToArray(); - FuncDecl nf = null; - var f = t.GetAppDecl(); - if (subst.TryGetValue(f, out nf)) - { - res = ctx.MkApp(nf, args); - } - else - { - res = ctx.CloneApp(t, args); - } - } // TODO: handle quantifiers - else - res = t; - memo.Add(t, res); - return res; - } - - private Term SubstPreds(Dictionary subst, Term t) - { - TermDict< Term> memo = new TermDict< Term>(); - return SubstPredsRec(memo, subst, t); - } - - /* Everything after here is private. */ - - private class stack_entry - { - public List edges = new List(); - public List nodes = new List(); - }; - - /** Set the model of the background theory used in a counterexample. */ - public void SetBackgroundModel(Model m) - { - dualModel = m; - } - - /** Set the model of the background theory used in a counterexample. */ - public Model GetBackgroundModel() - { - return dualModel; - } - - private int nodeCount = 0; - private int edgeCount = 0; - private Model dualModel; - // private LabeledLiterals dualLabels; - private Stack stack; - public List nodes = new List(); - public List edges = new List(); - - - } -} +//----------------------------------------------------------------------------- +// +// Copyright (C) 2012 Microsoft Corporation. All Rights Reserved. +// +//----------------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Term = Microsoft.Boogie.VCExprAST.VCExpr; +using FuncDecl = Microsoft.Boogie.VCExprAST.VCExprOp; +using Sort = Microsoft.Boogie.Type; +using Microsoft.Boogie.VCExprAST; + + +using Microsoft.Boogie.ExprExtensions; + + +namespace Microsoft.Boogie +{ + + + + + /** This class represents a relation post-fixed point (RPFP) problem as + * a "problem graph". The graph consists of Nodes and hyper-edges. + * + * A node consists of + * - Annotation, a symbolic relation + * - Bound, a symbolic relation giving an upper bound on Annotation + * + * + * A hyper-edge consists of: + * - Children, a sequence of children Nodes, + * - F, a symbolic relational transformer, + * - Parent, a single parent Node. + * + * The graph is "solved" when: + * - For every Node n, n.Annotation subseteq n.Bound + * - For every hyperedge e, e.F(e.Children.Annotation) subseteq e.Parent.Annotation + * + * where, if x is a sequence of Nodes, x.Annotation is the sequences + * of Annotations of the nodes in the sequence. + * + * A symbolic Transformer consists of + * - RelParams, a sequence of relational symbols + * - IndParams, a sequence of individual symbols + * - Formula, a formula over RelParams and IndParams + * + * A Transformer t represents a function that takes sequence R of relations + * and yields the relation lambda (t.Indparams). Formula(R/RelParams). + * + * As a special case, a nullary Transformer (where RelParams is the empty sequence) + * represents a fixed relation. + * + * An RPFP consists of + * - Nodes, a set of Nodes + * - Edges, a set of hyper-edges + * - Context, a prover context that contains formula AST's + * + * Multiple RPFP's can use the same Context, but you should be careful + * that only one RPFP asserts constraints in the context at any time. + * + * */ + public class RPFP + { + /** Symbolic representation of a relational transformer */ + public class Transformer + { + public FuncDecl[] RelParams; + public Term[] IndParams; + public Term Formula; + public RPFP owner; + + public Transformer Clone() + { + return (Transformer)this.MemberwiseClone(); + } + } + + /** Create a symbolic transformer. */ + public Transformer CreateTransformer(FuncDecl[] _RelParams, Term[] _IndParams, Term _Formula) + { + Transformer t = new Transformer(); + t.RelParams = _RelParams; + t.IndParams = _IndParams; + t.Formula = _Formula; + t.owner = this; + return t; + } + + /** Create a relation (nullary relational transformer) */ + public Transformer CreateRelation(Term[] _IndParams, Term _Formula) + { + return CreateTransformer(new FuncDecl[0], _IndParams, _Formula); + } + + /** A node in the RPFP graph */ + public class Node + { + public FuncDecl Name; + public Transformer Annotation; + public Transformer Bound; + public RPFP owner; + public int number; + public Edge Outgoing; + public List Incoming; + public Term dual; + public Node map; + } + + /** Create a node in the graph. The input is a term R(v_1...v_n) + * where R is an arbitrary relational symbol and v_1...v_n are + * arbitary distinct variables. The names are only of mnemonic value, + * however, the number and type of arguments determine the type + * of the relation at this node. */ + + public Node CreateNode(Term t) + { + Node n = new Node(); + // Microsoft.Boogie.VCExprAST.VCExprNAry tn = t as Microsoft.Boogie.VCExprAST.VCExprNAry; + // Term[] _IndParams = tn.ToArray(); + Term[] _IndParams = t.GetAppArgs(); + FuncDecl Name = t.GetAppDecl(); + n.Annotation = CreateRelation(_IndParams,ctx.MkTrue()); + n.Bound = CreateRelation(_IndParams, ctx.MkTrue()); + n.owner = this; + n.number = ++nodeCount; + n.Name = Name; // just to have a unique name + n.Incoming = new List(); + return n; + } + + /** Clone a node (can be from another graph). */ + + public Node CloneNode(Node old) + { + Node n = new Node(); + n.Annotation = old.Annotation.Clone(); + n.Bound = old.Bound.Clone(); + n.owner = this; + n.number = ++nodeCount; + n.Name = old.Name; // just to have a unique name + n.Incoming = new List(); + return n; + } + + /** This class represents a hyper-edge in the RPFP graph */ + + public class Edge + { + public Transformer F; + public Node Parent; + public Node[] Children; + public RPFP owner; + public int number; + public Edge map; + public HashSet labels; + internal Term dual; + internal TermDict valuation; + } + + + /** Create a hyper-edge. */ + public Edge CreateEdge(Node _Parent, Transformer _F, Node[] _Children) + { + Edge e = new Edge(); + e.Parent = _Parent; + e.F = _F; + e.Children = _Children; + e.owner = this; + e.number = ++edgeCount; + _Parent.Outgoing = e; + foreach (var c in _Children) + if(c != null) + c.Incoming.Add(e); + return e; + } + + /** Create an edge that lower-bounds its parent. */ + public Edge CreateLowerBoundEdge(Node _Parent) + { + return CreateEdge(_Parent, _Parent.Annotation, new RPFP.Node[0]); + } + + + + + /** Assert a background axiom. Background axioms can be used to provide the + * theory of auxilliary functions or relations. All symbols appearing in + * background axioms are considered global, and may appear in both transformer + * and relational solutions. Semantically, a solution to the RPFP gives + * an interpretation of the unknown relations for each interpretation of the + * auxilliary symbols that is consistent with the axioms. Axioms should be + * asserted before any calls to Push. They cannot be de-asserted by Pop. */ + + public void AssertAxiom(Term t) + { + ctx.AddAxiom(t); + } + + /** Do not call this. */ + + public void RemoveAxiom(Term t) + { + ctx.RemoveAxiom(t); + } + + /** Type of solve results */ + public enum LBool { False, True, Undef }; + + + /** Solve an RPFP graph. This means either strengthen the annotation + * so that the bound at the given root node is satisfied, or + * show that this cannot be done by giving a dual solution + * (i.e., a counterexample). + * + * In the current implementation, this only works for graphs that + * are: + * - tree-like + * + * - closed. + * + * In a tree-like graph, every nod has out most one incoming and one out-going edge, + * and there are no cycles. In a closed graph, every node has exactly one out-going + * edge. This means that the leaves of the tree are all hyper-edges with no + * children. Such an edge represents a relation (nullary transformer) and thus + * a lower bound on its parent. The parameter root must be the root of this tree. + * + * If Solve returns LBool.False, this indicates success. The annotation of the tree + * has been updated to satisfy the upper bound at the root. + * + * If Solve returns LBool.True, this indicates a counterexample. For each edge, + * you can then call Eval to determine the values of symbols in the transformer formula. + * You can also call Empty on a node to determine if its value in the counterexample + * is the empty relation. + * + * \param root The root of the tree + * \param persist Number of context pops through which result should persist + * + * + */ + + public LBool Solve(Node root, int persist) + { + return LBool.False; // TODO + } + + + /** Dispose of the dual model (counterexample) if there is one. */ + + public void DisposeDualModel() + { + // TODO dualModel = null; + } + + + /** Determines the value in the counterexample of a symbol occuring in the transformer formula of + * a given edge. */ + + public Term Eval(Edge e, Term t) + { + if (e.valuation == null) + e.valuation = new TermDict< Term>(); + if (e.valuation.ContainsKey(t)) + return e.valuation[t]; + return null; // TODO + } + + /** Sets the value in the counterexample of a symbol occuring in the transformer formula of + * a given edge. */ + + public void SetValue(Edge e, Term variable, Term value) + { + if (e.valuation == null) + e.valuation = new TermDict< Term>(); + e.valuation.Add(variable, value); + } + + + /** Returns true if the given node is empty in the primal solution. For proecudure summaries, + this means that the procedure is not called in the current counter-model. */ + + public bool Empty(Node p) + { + return false; // TODO + } + + /** Push a scope. Assertions made after Push can be undone by Pop. */ + + public void Push() + { + stack.Push(new stack_entry()); + // TODO: do we need push/pop? + } + + /** Pop a scope (see Push). Note, you cannot pop axioms. */ + + public void Pop(int num_scopes) + { + //TODO ctx.Pop((uint)num_scopes); + for (uint i = 0; i < num_scopes; i++) + { + stack_entry back = stack.Pop(); + foreach (var e in back.edges) + e.dual = null; + foreach (var n in back.nodes) + n.dual = null; + } + } + + public Context ctx; + + public class LogicSolver { + public Context ctx; + }; + + public LogicSolver solver; + + static public LogicSolver CreateLogicSolver(Context _ctx){ + LogicSolver res = new LogicSolver(); + res.ctx = _ctx; + return res; + } + + /** This represents a conjecture that a given node is upper-boudned + by bound. */ + public class Conjecture + { + public Node node; + public Transformer bound; + } + + /** This is a list of conjectures generated during solving. */ + + public List conjectures = new List(); + + /** Construct an RPFP graph with a given interpolating prover context. It is allowed to + have multiple RPFP's use the same context, but you should never have teo RPFP's + with the same conext asserting nodes or edges at the same time. Note, if you create + axioms in one RPFP, them create a second RPFP with the same context, the second will + inherit the axioms. + */ + + public RPFP(LogicSolver slvr) + { + solver = slvr; + ctx = slvr.ctx; + stack = new Stack(); + stack.Push(new stack_entry()); + } + + + /** Convert an array of clauses to an RPFP. + */ + + public void FromClauses(Term[] clauses){ + FuncDecl failName = ctx.MkFuncDecl("@Fail", ctx.MkBoolSort()); + foreach(var clause in clauses){ + Node foo = GetNodeFromClause(clause,failName); + if(foo != null) + nodes.Add(foo); + } + foreach (var clause in clauses) + edges.Add(GetEdgeFromClause(clause,failName)); + } + + + // This returns a new FuncDel with same sort as top-level function + // of term t, but with numeric suffix appended to name. + + private FuncDecl SuffixFuncDecl(Term t, int n) + { + var name = t.GetAppDecl().GetDeclName() + "_" + n.ToString(); + return ctx.MkFuncDecl(name, t.GetAppDecl()); + } + + // Collect the relational paremeters + + Dictionary relationToNode = new Dictionary(); + + private Term CollectParamsRec(TermDict memo, Term t, List parms, List nodes, Dictionary done) + { + Term res; + if (memo.TryGetValue(t, out res)) + return res; + if (t.GetKind() == TermKind.App) + { + var f = t.GetAppDecl(); + Node node; + if (relationToNode.TryGetValue(f, out node)) + { + if (done.ContainsKey(t)) + res = done[t]; + else + { + f = SuffixFuncDecl(t, parms.Count); + parms.Add(f); + nodes.Add(node); + done.Add(t,res); // don't count same expression twice! + } + } + var args = t.GetAppArgs(); + args = args.Select(x => CollectParamsRec(memo, x, parms, nodes, done)).ToArray(); + res = ctx.CloneApp(t, args); + } // TODO: handle quantifiers + else + res = t; + memo.Add(t, res); + return res; + } + + private bool IsVariable(Term t) + { + // TODO: is this right? + // return t.IsFunctionApp() && t.GetAppArgs().Length == 0; + return t is VCExprVar && !(t is VCExprConstant); + } + + private Edge GetEdgeFromClause(Term t, FuncDecl failName) + { + Term[] args = t.GetAppArgs(); + Term body = args[0]; + Term head = args[1]; + Term[] _IndParams; + FuncDecl Name; + if (head.IsFalse()) + { + Name = failName; + _IndParams = new Term[0]; + } + else + { + _IndParams = head.GetAppArgs(); + Name = head.GetAppDecl(); + } + for(int i = 0; i < _IndParams.Length; i++) + if (!IsVariable(_IndParams[i])) + { + Term v = ctx.MkConst("@a" + i.ToString(), _IndParams[i].GetSort()); + body = ctx.MkAnd(body, ctx.MkEq(v, _IndParams[i])); + _IndParams[i] = v; + } + var relParams = new List(); + var nodeParams = new List(); + var memo = new TermDict< Term>(); + var done = new Dictionary(); // note this hashes on equality, not reference! + body = CollectParamsRec(memo, body, relParams, nodeParams,done); + Transformer F = CreateTransformer(relParams.ToArray(), _IndParams, body); + Node parent = relationToNode[Name]; + return CreateEdge(parent, F, nodeParams.ToArray()); + } + + private Node GetNodeFromClause(Term t, FuncDecl failName) + { + Term[] args = t.GetAppArgs(); + Term body = args[0]; + Term head = args[1]; + FuncDecl Name; + Term[] _IndParams; + bool is_query = false; + if (head.Equals(ctx.MkFalse())) + { + Name = failName; + is_query = true; + _IndParams = new Term[0]; + } + else + { + Name = head.GetAppDecl(); + _IndParams = head.GetAppArgs(); + } + if (relationToNode.ContainsKey(Name)) + return null; + for (int i = 0; i < _IndParams.Length; i++) + if (!IsVariable(_IndParams[i])) + { + Term v = ctx.MkConst("@a" + i.ToString(), _IndParams[i].GetSort()); + _IndParams[i] = v; + } + Term foo = ctx.MkApp(Name, _IndParams); + Node node = CreateNode(foo); + relationToNode[Name] = node; + if (is_query) + node.Bound = CreateRelation(new Term[0], ctx.MkFalse()); + return node; + } + + ///////////////////////////////////////////////////////////////////////////////////////// + // Convert RPFP to Z3 rules + ///////////////////////////////////////////////////////////////////////////////////////// + + /** Get the Z3 rule corresponding to an edge */ + + public Term GetRule(Edge edge) + { + Dictionary predSubst = new Dictionary(); + for (int i = 0; i < edge.Children.Length; i++) + predSubst.Add(edge.F.RelParams[i], edge.Children[i].Name); + Term body = SubstPreds(predSubst, edge.F.Formula); + Term head = ctx.MkApp(edge.Parent.Name, edge.F.IndParams); + var rule = BindVariables(ctx.MkImplies(body, head)); + rule = ctx.Letify(rule); // put in let bindings for theorem prover + return rule; + } + + /** Get the Z3 query corresponding to the conjunction of the node bounds. */ + + public Term GetQuery() + { + List conjuncts = new List(); + foreach (var node in nodes) + { + if (node.Bound.Formula != ctx.MkTrue()) + conjuncts.Add(ctx.MkImplies(ctx.MkApp(node.Name, node.Bound.IndParams), node.Bound.Formula)); + } + Term query = ctx.MkNot(ctx.MkAnd(conjuncts.ToArray())); + query = BindVariables(query,false); // bind variables existentially + query = ctx.Letify(query); // put in let bindings for theorem prover + return query; + } + + private void CollectVariables(TermDict< bool> memo, Term t, List vars) + { + if (memo.ContainsKey(t)) + return; + if (IsVariable(t)) + vars.Add(t); + if (t.GetKind() == TermKind.App) + { + foreach (var s in t.GetAppArgs()) + CollectVariables(memo, s, vars); + } + memo.Add(t, true); + } + + private Term BindVariables(Term t, bool universal = true) + { + TermDict< bool> memo = new TermDict(); + List vars = new List(); + CollectVariables(memo,t,vars); + return universal ? ctx.MkForall(vars.ToArray(), t) : ctx.MkExists(vars.ToArray(), t); + } + + private Term SubstPredsRec(TermDict< Term> memo, Dictionary subst, Term t) + { + Term res; + if (memo.TryGetValue(t, out res)) + return res; + if (t.GetKind() == TermKind.App) + { + var args = t.GetAppArgs(); + args = args.Select(x => SubstPredsRec(memo,subst,x)).ToArray(); + FuncDecl nf = null; + var f = t.GetAppDecl(); + if (subst.TryGetValue(f, out nf)) + { + res = ctx.MkApp(nf, args); + } + else + { + res = ctx.CloneApp(t, args); + } + } // TODO: handle quantifiers + else + res = t; + memo.Add(t, res); + return res; + } + + private Term SubstPreds(Dictionary subst, Term t) + { + TermDict< Term> memo = new TermDict< Term>(); + return SubstPredsRec(memo, subst, t); + } + + /* Everything after here is private. */ + + private class stack_entry + { + public List edges = new List(); + public List nodes = new List(); + }; + + /** Set the model of the background theory used in a counterexample. */ + public void SetBackgroundModel(Model m) + { + dualModel = m; + } + + /** Set the model of the background theory used in a counterexample. */ + public Model GetBackgroundModel() + { + return dualModel; + } + + private int nodeCount = 0; + private int edgeCount = 0; + private Model dualModel; + // private LabeledLiterals dualLabels; + private Stack stack; + public List nodes = new List(); + public List edges = new List(); + + + } +} diff --git a/Source/VCGeneration/StratifiedVC.cs b/Source/VCGeneration/StratifiedVC.cs index 037fa2be..0e598267 100644 --- a/Source/VCGeneration/StratifiedVC.cs +++ b/Source/VCGeneration/StratifiedVC.cs @@ -1,2903 +1,2903 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Threading; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.IO; -using Microsoft.Boogie; -using Microsoft.Boogie.GraphUtil; -using System.Diagnostics.Contracts; -using Microsoft.Basetypes; -using Microsoft.Boogie.VCExprAST; - -namespace VC { - using Bpl = Microsoft.Boogie; - - public class StratifiedVC { - public StratifiedInliningInfo info; - public int id; - public List interfaceExprVars; - - // boolControlVC (block -> its bool variable) - public Dictionary blockToControlVar; - // While using labels (block -> its label) - public Dictionary block2label; - - public Dictionary> callSites; - public Dictionary> recordProcCallSites; - public VCExpr vcexpr; - - // Must-Reach Information - Dictionary mustReachVar; - List mustReachBindings; - - public StratifiedVC(StratifiedInliningInfo siInfo, HashSet procCalls) { - info = siInfo; - info.GenerateVC(); - var vcgen = info.vcgen; - var prover = vcgen.prover; - VCExpressionGenerator gen = prover.VCExprGen; - var bet = prover.Context.BoogieExprTranslator; - - vcexpr = info.vcexpr; - id = vcgen.CreateNewId(); - interfaceExprVars = new List(); - Dictionary substDict = new Dictionary(); - foreach (VCExprVar v in info.interfaceExprVars) { - VCExprVar newVar = vcgen.CreateNewVar(v.Type); - interfaceExprVars.Add(newVar); - substDict.Add(v, newVar); - } - foreach (VCExprVar v in info.privateExprVars) { - substDict.Add(v, vcgen.CreateNewVar(v.Type)); - } - if(info.controlFlowVariable != null) - substDict.Add(bet.LookupVariable(info.controlFlowVariable), gen.Integer(BigNum.FromInt(id))); - VCExprSubstitution subst = new VCExprSubstitution(substDict, new Dictionary()); - SubstitutingVCExprVisitor substVisitor = new SubstitutingVCExprVisitor(prover.VCExprGen); - vcexpr = substVisitor.Mutate(vcexpr, subst); - - // For BoolControlVC generation - if (info.blockToControlVar != null) - { - blockToControlVar = new Dictionary(); - foreach (var tup in info.blockToControlVar) - blockToControlVar.Add(tup.Key, substDict[tup.Value]); - } - - // labels - if (info.label2absy != null) - { - block2label = new Dictionary(); - vcexpr = RenameVCExprLabels.Apply(vcexpr, info.vcgen.prover.VCExprGen, info.label2absy, block2label); - } - - if(procCalls != null) - vcexpr = RemoveProcedureCalls.Apply(vcexpr, info.vcgen.prover.VCExprGen, procCalls); - - callSites = new Dictionary>(); - foreach (Block b in info.callSites.Keys) { - callSites[b] = new List(); - foreach (CallSite cs in info.callSites[b]) { - callSites[b].Add(new StratifiedCallSite(cs, substVisitor, subst)); - } - } - - recordProcCallSites = new Dictionary>(); - foreach (Block b in info.recordProcCallSites.Keys) { - recordProcCallSites[b] = new List(); - foreach (CallSite cs in info.recordProcCallSites[b]) { - recordProcCallSites[b].Add(new StratifiedCallSite(cs, substVisitor, subst)); - } - } - } - - public VCExpr MustReach(Block block) - { - Contract.Assert(!CommandLineOptions.Clo.UseLabels); - - // This information is computed lazily - if (mustReachBindings == null) - { - var vcgen = info.vcgen; - var gen = vcgen.prover.VCExprGen; - var impl = info.impl; - mustReachVar = new Dictionary(); - mustReachBindings = new List(); - foreach (Block b in impl.Blocks) - mustReachVar[b] = vcgen.CreateNewVar(Bpl.Type.Bool); - - var dag = new Graph(); - dag.AddSource(impl.Blocks[0]); - foreach (Block b in impl.Blocks) - { - var gtc = b.TransferCmd as GotoCmd; - if (gtc != null) - foreach (Block dest in gtc.labelTargets) - dag.AddEdge(dest, b); - } - IEnumerable sortedNodes = dag.TopologicalSort(); - - foreach (Block currBlock in dag.TopologicalSort()) - { - if (currBlock == impl.Blocks[0]) - { - mustReachBindings.Add(gen.LetBinding(mustReachVar[currBlock], VCExpressionGenerator.True)); - continue; - } - - VCExpr expr = VCExpressionGenerator.False; - foreach (var pred in dag.Successors(currBlock)) - { - VCExpr controlFlowFunctionAppl = gen.ControlFlowFunctionApplication(gen.Integer(BigNum.FromInt(id)), gen.Integer(BigNum.FromInt(pred.UniqueId))); - VCExpr controlTransferExpr = gen.Eq(controlFlowFunctionAppl, gen.Integer(BigNum.FromInt(currBlock.UniqueId))); - expr = gen.Or(expr, gen.And(mustReachVar[pred], controlTransferExpr)); - } - mustReachBindings.Add(gen.LetBinding(mustReachVar[currBlock], expr)); - } - } - - Contract.Assert(mustReachVar.ContainsKey(block)); - return info.vcgen.prover.VCExprGen.Let(mustReachBindings, mustReachVar[block]); - } - - public List CallSites { - get { - var ret = new List(); - foreach (var b in callSites.Keys) { - foreach (var cs in callSites[b]) { - ret.Add(cs); - } - } - return ret; - } - } - - public List RecordProcCallSites { - get { - var ret = new List(); - foreach (var b in recordProcCallSites.Keys) { - foreach (var cs in recordProcCallSites[b]) { - ret.Add(cs); - } - } - return ret; - } - } - - public override string ToString() - { - return info.impl.Name; - } - } - - // Rename all labels in a VC to (globally) fresh labels - class RenameVCExprLabels : MutatingVCExprVisitor - { - Dictionary label2absy; - Dictionary absy2newlabel; - static int counter = 11; - - RenameVCExprLabels(VCExpressionGenerator gen, Dictionary label2absy, Dictionary absy2newlabel) - : base(gen) - { - this.label2absy = label2absy; - this.absy2newlabel = absy2newlabel; - } - - public static VCExpr Apply(VCExpr expr, VCExpressionGenerator gen, Dictionary label2absy, Dictionary absy2newlabel) - { - return (new RenameVCExprLabels(gen, label2absy, absy2newlabel)).Mutate(expr, true); - } - - // Finds labels and changes them to a globally unique label: - protected override VCExpr/*!*/ UpdateModifiedNode(VCExprNAry/*!*/ originalNode, - List/*!*/ newSubExprs, - bool changed, - bool arg) - { - Contract.Ensures(Contract.Result() != null); - - VCExpr ret; - if (changed) - ret = Gen.Function(originalNode.Op, - newSubExprs, originalNode.TypeArguments); - else - ret = originalNode; - - VCExprLabelOp lop = originalNode.Op as VCExprLabelOp; - if (lop == null) return ret; - if (!(ret is VCExprNAry)) return ret; - VCExprNAry retnary = (VCExprNAry)ret; - - // remove the sign - var nosign = 0; - if (!Int32.TryParse(lop.label.Substring(1), out nosign)) - return ret; - - if (!label2absy.ContainsKey(nosign)) - return ret; - - string newLabel = "SI" + counter.ToString(); - counter++; - absy2newlabel[label2absy[nosign]] = newLabel; - - if (lop.pos) - { - return Gen.LabelPos(newLabel, retnary[0]); - } - else - { - return Gen.LabelNeg(newLabel, retnary[0]); - } - - } - } - - // Remove the uninterpreted function calls that substitute procedure calls - class RemoveProcedureCalls : MutatingVCExprVisitor - { - HashSet procNames; - - RemoveProcedureCalls(VCExpressionGenerator gen, HashSet procNames) - : base(gen) - { - this.procNames = procNames; - } - - public static VCExpr Apply(VCExpr expr, VCExpressionGenerator gen, HashSet procNames) - { - return (new RemoveProcedureCalls(gen, procNames)).Mutate(expr, true); - } - - // Finds labels and changes them to a globally unique label: - protected override VCExpr/*!*/ UpdateModifiedNode(VCExprNAry/*!*/ originalNode, - List/*!*/ newSubExprs, - bool changed, - bool arg) - { - //Contract.Ensures(Contract.Result() != null); - - VCExpr ret; - if (changed) - ret = Gen.Function(originalNode.Op, - newSubExprs, originalNode.TypeArguments); - else - ret = originalNode; - - if (!(ret is VCExprNAry)) return ret; - VCExprNAry retnary = (VCExprNAry)ret; - if (!(retnary.Op is VCExprBoogieFunctionOp)) - return ret; - - var fcall = (retnary.Op as VCExprBoogieFunctionOp).Func.Name; - if (procNames.Contains(fcall)) - return VCExpressionGenerator.True; - return ret; - } - } - - - public class CallSite { - public string calleeName; - public List interfaceExprs; - public Block block; - public int numInstr; // for TraceLocation - public VCExprVar callSiteVar; - public QKeyValue Attributes; // attributes on the call cmd - public CallSite(string callee, List interfaceExprs, VCExprVar callSiteVar, Block block, int numInstr, QKeyValue Attributes) - { - this.calleeName = callee; - this.interfaceExprs = interfaceExprs; - this.callSiteVar = callSiteVar; - this.block = block; - this.numInstr = numInstr; - this.Attributes = Attributes; - } - } - - public class StratifiedCallSite { - public CallSite callSite; - public List interfaceExprs; - public VCExpr callSiteExpr; - - public StratifiedCallSite(CallSite cs, SubstitutingVCExprVisitor substVisitor, VCExprSubstitution subst) { - callSite = cs; - interfaceExprs = new List(); - foreach (VCExpr v in cs.interfaceExprs) { - interfaceExprs.Add(substVisitor.Mutate(v, subst)); - } - if (callSite.callSiteVar != null) - callSiteExpr = substVisitor.Mutate(callSite.callSiteVar, subst); - } - - public VCExpr Attach(StratifiedVC svc) { - Contract.Assert(interfaceExprs.Count == svc.interfaceExprVars.Count); - StratifiedInliningInfo info = svc.info; - ProverInterface prover = info.vcgen.prover; - VCExpressionGenerator gen = prover.VCExprGen; - - Dictionary substDict = new Dictionary(); - for (int i = 0; i < svc.interfaceExprVars.Count; i++) { - VCExprVar v = svc.interfaceExprVars[i]; - substDict.Add(v, interfaceExprs[i]); - } - VCExprSubstitution subst = new VCExprSubstitution(substDict, new Dictionary()); - SubstitutingVCExprVisitor substVisitor = new SubstitutingVCExprVisitor(prover.VCExprGen); - svc.vcexpr = substVisitor.Mutate(svc.vcexpr, subst); - foreach (StratifiedCallSite scs in svc.CallSites) { - List newInterfaceExprs = new List(); - foreach (VCExpr expr in scs.interfaceExprs) { - newInterfaceExprs.Add(substVisitor.Mutate(expr, subst)); - } - scs.interfaceExprs = newInterfaceExprs; - } - foreach (StratifiedCallSite scs in svc.RecordProcCallSites) { - List newInterfaceExprs = new List(); - foreach (VCExpr expr in scs.interfaceExprs) { - newInterfaceExprs.Add(substVisitor.Mutate(expr, subst)); - } - scs.interfaceExprs = newInterfaceExprs; - } - //return gen.Implies(callSiteExpr, svc.vcexpr); - return svc.vcexpr; - } - - public override string ToString() - { - return callSite.calleeName; - } - } - - public class StratifiedInliningInfo { - public StratifiedVCGenBase vcgen; - public Implementation impl; - public Function function; - public Variable controlFlowVariable; - public Cmd exitAssertCmd; - public VCExpr vcexpr; - public List interfaceExprVars; - public List privateExprVars; - public Dictionary label2absy; - public ModelViewInfo mvInfo; - public Dictionary> callSites; - public Dictionary> recordProcCallSites; - public bool initialized { get; private set; } - // Instrumentation to apply after PassiveImpl, but before VCGen - Action PassiveImplInstrumentation; - - // boolControlVC (block -> its Bool variable) - public Dictionary blockToControlVar; - - public StratifiedInliningInfo(Implementation implementation, StratifiedVCGenBase stratifiedVcGen, Action PassiveImplInstrumentation) { - vcgen = stratifiedVcGen; - impl = implementation; - this.PassiveImplInstrumentation = PassiveImplInstrumentation; - - List functionInterfaceVars = new List(); - foreach (Variable v in vcgen.program.GlobalVariables) { - functionInterfaceVars.Add(new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "", v.TypedIdent.Type), true)); - } - foreach (Variable v in impl.InParams) { - functionInterfaceVars.Add(new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "", v.TypedIdent.Type), true)); - } - foreach (Variable v in impl.OutParams) { - functionInterfaceVars.Add(new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "", v.TypedIdent.Type), true)); - } - foreach (IdentifierExpr e in impl.Proc.Modifies) { - if (e.Decl == null) continue; - functionInterfaceVars.Add(new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "", e.Decl.TypedIdent.Type), true)); - } - Formal returnVar = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "", Bpl.Type.Bool), false); - function = new Function(Token.NoToken, impl.Name, functionInterfaceVars, returnVar); - vcgen.prover.Context.DeclareFunction(function, ""); - - List exprs = new List(); - foreach (Variable v in vcgen.program.GlobalVariables) { - Contract.Assert(v != null); - exprs.Add(new OldExpr(Token.NoToken, new IdentifierExpr(Token.NoToken, v))); - } - foreach (Variable v in impl.Proc.InParams) { - Contract.Assert(v != null); - exprs.Add(new IdentifierExpr(Token.NoToken, v)); - } - foreach (Variable v in impl.Proc.OutParams) { - Contract.Assert(v != null); - exprs.Add(new IdentifierExpr(Token.NoToken, v)); - } - foreach (IdentifierExpr ie in impl.Proc.Modifies) { - Contract.Assert(ie != null); - if (ie.Decl == null) - continue; - exprs.Add(ie); - } - Expr freePostExpr = new NAryExpr(Token.NoToken, new FunctionCall(function), exprs); - impl.Proc.Ensures.Add(new Ensures(Token.NoToken, true, freePostExpr, "", new QKeyValue(Token.NoToken, "si_fcall", new List(), null))); - - initialized = false; - } - - public void GenerateVCBoolControl() - { - Debug.Assert(!initialized); - Debug.Assert(CommandLineOptions.Clo.SIBoolControlVC); - - // fix names for exit variables - var outputVariables = new List(); - var assertConjuncts = new List(); - foreach (Variable v in impl.OutParams) - { - Constant c = new Constant(Token.NoToken, new TypedIdent(Token.NoToken, impl.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) - { - if (e.Decl == null) continue; - Variable v = e.Decl; - Constant c = new Constant(Token.NoToken, new TypedIdent(Token.NoToken, impl.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); - } - exitAssertCmd = new AssumeCmd(Token.NoToken, Expr.BinaryTreeAnd(assertConjuncts)); - (exitAssertCmd as AssumeCmd).Attributes = new QKeyValue(Token.NoToken, "exitAssert", new List(), null); - - // no need for label2absy - label2absy = new Dictionary(); - - // Passify - Program program = vcgen.program; - ProverInterface proverInterface = vcgen.prover; - vcgen.ConvertCFG2DAG(impl); - vcgen.PassifyImpl(impl, 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); - - // typecheck - var tc = new TypecheckingContext(null); - impl.Typecheck(tc); - - /////////////////// - // Generate the VC - /////////////////// - - // block -> bool variable - blockToControlVar = new Dictionary(); - foreach (var b in impl.Blocks) - blockToControlVar.Add(b, gen.Variable(b.Label + "_holds", Bpl.Type.Bool)); - - vcexpr = VCExpressionGenerator.True; - foreach (var b in impl.Blocks) - { - // conjoin all assume cmds - VCExpr c = VCExpressionGenerator.True; - foreach (var cmd in b.Cmds) - { - var acmd = cmd as AssumeCmd; - if (acmd == null) - { - Debug.Assert(cmd is AssertCmd && (cmd as AssertCmd).Expr is LiteralExpr && - ((cmd as AssertCmd).Expr as LiteralExpr).IsTrue); - continue; - } - var expr = translator.Translate(acmd.Expr); - // Label the assume if it is a procedure call - NAryExpr naryExpr = acmd.Expr as NAryExpr; - if (naryExpr != null && naryExpr.Fun is FunctionCall) - { - var id = acmd.UniqueId; - label2absy[id] = acmd; - expr = gen.LabelPos(cce.NonNull("si_fcall_" + id.ToString()), expr); - } - - c = gen.AndSimp(c, expr); - } - - // block implies a disjunction of successors - Debug.Assert(!(b.TransferCmd is ReturnExprCmd), "Not supported"); - var gc = b.TransferCmd as GotoCmd; - if (gc != null) - { - VCExpr succ = VCExpressionGenerator.False; - foreach (var sb in gc.labelTargets) - succ = gen.OrSimp(succ, blockToControlVar[sb]); - c = gen.AndSimp(c, succ); - } - else - { - // nothing to do - } - vcexpr = gen.AndSimp(vcexpr, gen.Eq(blockToControlVar[b], c)); - } - // assert start block - vcexpr = gen.AndSimp(vcexpr, blockToControlVar[impl.Blocks[0]]); - - //Console.WriteLine("VC of {0}: {1}", impl.Name, vcexpr); - // Collect other information - callSites = vcgen.CollectCallSites(impl); - recordProcCallSites = vcgen.CollectRecordProcedureCallSites(impl); - - // record interface variables - privateExprVars = new List(); - foreach (Variable v in impl.LocVars) - { - privateExprVars.Add(translator.LookupVariable(v)); - } - foreach (Variable v in impl.OutParams) - { - privateExprVars.Add(translator.LookupVariable(v)); - } - privateExprVars.AddRange(blockToControlVar.Values); - - interfaceExprVars = new List(); - foreach (Variable v in program.GlobalVariables) - { - interfaceExprVars.Add(translator.LookupVariable(v)); - } - foreach (Variable v in impl.InParams) - { - interfaceExprVars.Add(translator.LookupVariable(v)); - } - foreach (Variable v in outputVariables) - { - interfaceExprVars.Add(translator.LookupVariable(v)); - } - } - - public void GenerateVC() { - if (initialized) return; - if (CommandLineOptions.Clo.SIBoolControlVC) - { - GenerateVCBoolControl(); - initialized = true; - return; - } - List outputVariables = new List(); - List assertConjuncts = new List(); - foreach (Variable v in impl.OutParams) { - Constant c = new Constant(Token.NoToken, new TypedIdent(Token.NoToken, impl.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) { - if (e.Decl == null) continue; - Variable v = e.Decl; - Constant c = new Constant(Token.NoToken, new TypedIdent(Token.NoToken, impl.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); - } - exitAssertCmd = new AssertCmd(Token.NoToken, Expr.Not(Expr.BinaryTreeAnd(assertConjuncts))); - - Program program = vcgen.program; - ProverInterface proverInterface = vcgen.prover; - vcgen.ConvertCFG2DAG(impl); - vcgen.PassifyImpl(impl, out mvInfo); - - if (PassiveImplInstrumentation != null) - PassiveImplInstrumentation(impl); - - VCExpressionGenerator gen = proverInterface.VCExprGen; - var exprGen = proverInterface.Context.ExprGen; - var translator = proverInterface.Context.BoogieExprTranslator; - - VCExpr controlFlowVariableExpr = null; - if (!CommandLineOptions.Clo.UseLabels) { - controlFlowVariable = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, "@cfc", Microsoft.Boogie.Type.Int)); - controlFlowVariableExpr = translator.LookupVariable(controlFlowVariable); - } - - vcgen.InstrumentCallSites(impl); - - label2absy = new Dictionary(); - VCGen.CodeExprConversionClosure cc = new VCGen.CodeExprConversionClosure(label2absy, proverInterface.Context); - translator.SetCodeExprConverter(cc.CodeExprToVerificationCondition); - vcexpr = gen.Not(vcgen.GenerateVCAux(impl, controlFlowVariableExpr, label2absy, proverInterface.Context)); - - if (controlFlowVariableExpr != null) - { - VCExpr controlFlowFunctionAppl = exprGen.ControlFlowFunctionApplication(controlFlowVariableExpr, exprGen.Integer(BigNum.ZERO)); - VCExpr eqExpr = exprGen.Eq(controlFlowFunctionAppl, exprGen.Integer(BigNum.FromInt(impl.Blocks[0].UniqueId))); - vcexpr = exprGen.And(eqExpr, vcexpr); - } - - callSites = vcgen.CollectCallSites(impl); - recordProcCallSites = vcgen.CollectRecordProcedureCallSites(impl); - - privateExprVars = new List(); - foreach (Variable v in impl.LocVars) { - privateExprVars.Add(translator.LookupVariable(v)); - } - foreach (Variable v in impl.OutParams) { - privateExprVars.Add(translator.LookupVariable(v)); - } - - interfaceExprVars = new List(); - foreach (Variable v in program.GlobalVariables) { - interfaceExprVars.Add(translator.LookupVariable(v)); - } - foreach (Variable v in impl.InParams) { - interfaceExprVars.Add(translator.LookupVariable(v)); - } - foreach (Variable v in outputVariables) { - interfaceExprVars.Add(translator.LookupVariable(v)); - } - - initialized = true; - } - } - - public abstract class StratifiedVCGenBase : VCGen { - public readonly static string recordProcName = "boogie_si_record"; - public Dictionary implName2StratifiedInliningInfo; - public ProverInterface prover; - - public StratifiedVCGenBase(Program program, string/*?*/ logFilePath, bool appendLogFile, List checkers, Action PassiveImplInstrumentation) - : base(program, logFilePath, appendLogFile, checkers) { - implName2StratifiedInliningInfo = new Dictionary(); - prover = ProverInterface.CreateProver(program, logFilePath, appendLogFile, CommandLineOptions.Clo.ProverKillTime); - foreach (var impl in program.Implementations) { - implName2StratifiedInliningInfo[impl.Name] = new StratifiedInliningInfo(impl, this, PassiveImplInstrumentation); - } - GenerateRecordFunctions(); - } - - private void GenerateRecordFunctions() { - foreach (var proc in program.Procedures) { - if (!proc.Name.StartsWith(recordProcName)) continue; - Contract.Assert(proc.InParams.Count == 1); - - // Make a new function - TypedIdent ti = new TypedIdent(Token.NoToken, "", Bpl.Type.Bool); - Contract.Assert(ti != null); - Formal returnVar = new Formal(Token.NoToken, ti, false); - Contract.Assert(returnVar != null); - - // Get record type - var argtype = proc.InParams[0].TypedIdent.Type; - - var ins = new List(); - ins.Add(new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "x", argtype), true)); - - var recordFunc = new Function(Token.NoToken, proc.Name, ins, returnVar); - prover.Context.DeclareFunction(recordFunc, ""); - - var exprs = new List(); - exprs.Add(new IdentifierExpr(Token.NoToken, proc.InParams[0])); - - Expr freePostExpr = new NAryExpr(Token.NoToken, new FunctionCall(recordFunc), exprs); - proc.Ensures.Add(new Ensures(true, freePostExpr)); - } - } - - public override void Close() { - prover.Close(); - base.Close(); - } - - public void InstrumentCallSites(Implementation implementation) { - var callSiteId = 0; - foreach (Block block in implementation.Blocks) { - List newCmds = new List(); - for (int i = 0; i < block.Cmds.Count; i++) { - Cmd cmd = block.Cmds[i]; - newCmds.Add(cmd); - AssumeCmd assumeCmd = cmd as AssumeCmd; - if (assumeCmd == null) continue; - NAryExpr naryExpr = assumeCmd.Expr as NAryExpr; - if (naryExpr == null) continue; - if (!implName2StratifiedInliningInfo.ContainsKey(naryExpr.Fun.FunctionName)) continue; - Variable callSiteVar = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, "SICS" + callSiteId, Microsoft.Boogie.Type.Bool)); - implementation.LocVars.Add(callSiteVar); - newCmds.Add(new AssumeCmd(Token.NoToken, new IdentifierExpr(Token.NoToken, callSiteVar))); - callSiteId++; - } - block.Cmds = newCmds; - } - } - - public Dictionary> CollectCallSites(Implementation implementation) { - var callSites = new Dictionary>(); - foreach (Block block in implementation.Blocks) { - for (int i = 0; i < block.Cmds.Count; i++) { - Cmd cmd = block.Cmds[i]; - AssumeCmd assumeCmd = cmd as AssumeCmd; - if (assumeCmd == null) continue; - NAryExpr naryExpr = assumeCmd.Expr as NAryExpr; - if (naryExpr == null) continue; - if (!implName2StratifiedInliningInfo.ContainsKey(naryExpr.Fun.FunctionName)) continue; - List interfaceExprs = new List(); - foreach (Expr e in naryExpr.Args) { - interfaceExprs.Add(prover.Context.BoogieExprTranslator.Translate(e)); - } - int instr = i; - i++; - AssumeCmd callSiteAssumeCmd = (AssumeCmd)block.Cmds[i]; - IdentifierExpr iexpr = (IdentifierExpr) callSiteAssumeCmd.Expr; - CallSite cs = new CallSite(naryExpr.Fun.FunctionName, interfaceExprs, prover.Context.BoogieExprTranslator.LookupVariable(iexpr.Decl), block, instr, assumeCmd.Attributes); - if (!callSites.ContainsKey(block)) - callSites[block] = new List(); - callSites[block].Add(cs); - } - } - return callSites; - } - - public Dictionary> CollectRecordProcedureCallSites(Implementation implementation) { - var callSites = new Dictionary>(); - foreach (Block block in implementation.Blocks) { - for (int i = 0; i < block.Cmds.Count; i++) { - AssumeCmd assumeCmd = block.Cmds[i] as AssumeCmd; - if (assumeCmd == null) continue; - NAryExpr naryExpr = assumeCmd.Expr as NAryExpr; - if (naryExpr == null) continue; - if (!naryExpr.Fun.FunctionName.StartsWith(recordProcName)) continue; - List interfaceExprs = new List(); - foreach (Expr e in naryExpr.Args) { - interfaceExprs.Add(prover.Context.BoogieExprTranslator.Translate(e)); - } - CallSite cs = new CallSite(naryExpr.Fun.FunctionName, interfaceExprs, null, block, i, assumeCmd.Attributes); - if (!callSites.ContainsKey(block)) - callSites[block] = new List(); - callSites[block].Add(cs); - } - } - return callSites; - } - - private int macroCountForStratifiedInlining = 0; - public Macro CreateNewMacro() { - string newName = "SIMacro@" + macroCountForStratifiedInlining.ToString(); - macroCountForStratifiedInlining++; - return new Macro(Token.NoToken, newName, new List(), new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "", Microsoft.Boogie.Type.Bool), false)); - } - private int varCountForStratifiedInlining = 0; - public VCExprVar CreateNewVar(Microsoft.Boogie.Type type) { - string newName = "SIV@" + varCountForStratifiedInlining.ToString(); - varCountForStratifiedInlining++; - Constant newVar = new Constant(Token.NoToken, new TypedIdent(Token.NoToken, newName, type)); - prover.Context.DeclareConstant(newVar, false, null); - return prover.VCExprGen.Variable(newVar.Name, type); - } - private int idCountForStratifiedInlining = 0; - public int CreateNewId() { - return idCountForStratifiedInlining++; - } - - // Used inside PassifyImpl - protected override void addExitAssert(string implName, Block exitBlock) { - if (implName2StratifiedInliningInfo != null && implName2StratifiedInliningInfo.ContainsKey(implName)) { - var exitAssertCmd = implName2StratifiedInliningInfo[implName].exitAssertCmd; - if(exitAssertCmd != null) exitBlock.Cmds.Add(exitAssertCmd); - } - } - - public override Counterexample extractLoopTrace(Counterexample cex, string mainProcName, Program program, Dictionary> extractLoopMappingInfo) { - // Construct the set of inlined procs in the original program - var inlinedProcs = new HashSet(); - foreach (var decl in program.TopLevelDeclarations) { - // Implementations - if (decl is Implementation) { - var impl = decl as Implementation; - if (!(impl.Proc is LoopProcedure)) { - inlinedProcs.Add(impl.Name); - } - } - - // And recording procedures - if (decl is Procedure) { - var proc = decl as Procedure; - if (proc.Name.StartsWith(recordProcName)) { - Debug.Assert(!(decl is LoopProcedure)); - inlinedProcs.Add(proc.Name); - } - } - } - - return extractLoopTraceRec( - new CalleeCounterexampleInfo(cex, new List()), - mainProcName, inlinedProcs, extractLoopMappingInfo).counterexample; - } - - protected override bool elIsLoop(string procname) { - StratifiedInliningInfo info = null; - if (implName2StratifiedInliningInfo.ContainsKey(procname)) { - info = implName2StratifiedInliningInfo[procname]; - } - - if (info == null) return false; - - var lp = info.impl.Proc as LoopProcedure; - - if (lp == null) return false; - return true; - } - - public abstract Outcome FindLeastToVerify(Implementation impl, ref HashSet allBoolVars); - } - - public class StratifiedVCGen : StratifiedVCGenBase { - public bool PersistCallTree; - public static HashSet callTree = null; - public int numInlined = 0; - public int vcsize = 0; - private HashSet procsThatReachedRecBound; - private Dictionary extraRecBound; - - public StratifiedVCGen(bool usePrevCallTree, HashSet prevCallTree, - Program program, string/*?*/ logFilePath, bool appendLogFile, List checkers) - : this(program, logFilePath, appendLogFile, checkers) - { - if (usePrevCallTree) { - callTree = prevCallTree; - PersistCallTree = true; - } - else { - PersistCallTree = false; - } - } - - public StratifiedVCGen(Program program, string/*?*/ logFilePath, bool appendLogFile, List checkers) - : base(program, logFilePath, appendLogFile, checkers, null) { - PersistCallTree = false; - procsThatReachedRecBound = new HashSet(); - - extraRecBound = new Dictionary(); - program.TopLevelDeclarations.OfType() - .Iter(impl => - { - var b = QKeyValue.FindIntAttribute(impl.Attributes, "SIextraRecBound", -1); - if (b != -1) extraRecBound.Add(impl.Name, b); - }); - } - - // Extra rec bound for procedures - public int GetExtraRecBound(string procName) { - if (!extraRecBound.ContainsKey(procName)) - return 0; - else return extraRecBound[procName]; - } - - public class ApiChecker { - public ProverInterface prover; - public ProverInterface.ErrorHandler reporter; - - public ApiChecker(ProverInterface prover, ProverInterface.ErrorHandler reporter) { - this.reporter = reporter; - this.prover = prover; - } - - private Outcome CheckVC() { - prover.Check(); - ProverInterface.Outcome outcome = prover.CheckOutcomeCore(reporter); - - return ConditionGeneration.ProverInterfaceOutcomeToConditionGenerationOutcome(outcome); - } - - public Outcome CheckAssumptions(List assumptions) { - if (assumptions.Count == 0) { - return CheckVC(); - } - - prover.Push(); - foreach (var a in assumptions) { - prover.Assert(a, true); - } - Outcome ret = CheckVC(); - prover.Pop(); - return ret; - } - - public Outcome CheckAssumptions(List hardAssumptions, List softAssumptions) { - List unsatisfiedSoftAssumptions; - ProverInterface.Outcome outcome = prover.CheckAssumptions(hardAssumptions, softAssumptions, out unsatisfiedSoftAssumptions, reporter); - return ConditionGeneration.ProverInterfaceOutcomeToConditionGenerationOutcome(outcome); - } - - public Outcome CheckAssumptions(List assumptions, out List unsatCore) { - ProverInterface.Outcome outcome = prover.CheckAssumptions(assumptions, out unsatCore, reporter); - return ConditionGeneration.ProverInterfaceOutcomeToConditionGenerationOutcome(outcome); - } - } - - // Store important information related to a single VerifyImplementation query - public class VerificationState { - // The call tree - public FCallHandler calls; - public ApiChecker checker; - // For statistics - public int vcSize; - public int expansionCount; - - public VerificationState(VCExpr vcMain, FCallHandler calls, ProverInterface prover, ProverInterface.ErrorHandler reporter) { - prover.Assert(vcMain, true); - this.calls = calls; - this.checker = new ApiChecker(prover, reporter); - vcSize = 0; - expansionCount = 0; - } - } - - class FindLeastOORException : Exception - { - public Outcome outcome; - - public FindLeastOORException(string msg, Outcome outcome) - : base(msg) - { - this.outcome = outcome; - } - } - - public override Outcome FindLeastToVerify(Implementation impl, ref HashSet allBoolVars) { - Contract.EnsuresOnThrow(true); - - // Record current time - var startTime = DateTime.UtcNow; - - // No Max: avoids theorem prover restarts - CommandLineOptions.Clo.MaxProverMemory = 0; - - // Initialize cache - satQueryCache = new Dictionary>>(); - unsatQueryCache = new Dictionary>>(); - - Contract.Assert(implName2StratifiedInliningInfo != null); - - // Build VCs for all procedures - implName2StratifiedInliningInfo.Values - .Iter(info => info.GenerateVC()); - - // Get the VC of the current procedure - VCExpr vcMain = implName2StratifiedInliningInfo[impl.Name].vcexpr; - Dictionary mainLabel2absy = implName2StratifiedInliningInfo[impl.Name].label2absy; - - // Find all procedure calls in vc and put labels on them - FCallHandler calls = new FCallHandler(prover.VCExprGen, implName2StratifiedInliningInfo, impl.Name, mainLabel2absy); - calls.setCurrProcAsMain(); - vcMain = calls.Mutate(vcMain, true); - - try - { - - // Put all of the necessary state into one object - var vState = new VerificationState(vcMain, calls, prover, new EmptyErrorHandler()); - - // We'll restore the original state of the theorem prover at the end - // of this procedure - vState.checker.prover.Push(); - - // Do eager inlining - while (calls.currCandidates.Count > 0) - { - List toExpand = new List(); - - foreach (int id in calls.currCandidates) - { - Debug.Assert(calls.getRecursionBound(id) <= 1, "Recursion not supported"); - toExpand.Add(id); - } - DoExpansion(toExpand, vState); - } - - // Find all the boolean constants - var allConsts = new HashSet(); - foreach (var constant in program.Constants) - { - if (!allBoolVars.Contains(constant.Name)) continue; - var v = prover.Context.BoogieExprTranslator.LookupVariable(constant); - allConsts.Add(v); - } - - // Now, lets start the algo - var min = refinementLoop(vState.checker, new HashSet(), allConsts, allConsts); - - var ret = new HashSet(); - foreach (var v in min) - { - //Console.WriteLine(v.Name); - ret.Add(v.Name); - } - allBoolVars = ret; - - vState.checker.prover.Pop(); - - return Outcome.Correct; - } - catch (FindLeastOORException e) - { - Console.WriteLine("Exception in FindLeastToVerify: {0}, {1}", e.Message, e.outcome); - return e.outcome; - } - } - - private HashSet refinementLoop(ApiChecker apiChecker, HashSet trackedVars, HashSet trackedVarsUpperBound, HashSet allVars) { - Debug.Assert(trackedVars.IsSubsetOf(trackedVarsUpperBound)); - - // If we already know the fate of all vars, then we're done. - if (trackedVars.Count == trackedVarsUpperBound.Count) - return new HashSet(trackedVars); - - // See if we already have enough variables tracked - var success = refinementLoopCheckPath(apiChecker, trackedVars, allVars); - if (success) { - // We have enough - return new HashSet(trackedVars); - } - - // If all that remains is 1 variable, then we know that we must track it - if (trackedVars.Count + 1 == trackedVarsUpperBound.Count) - return new HashSet(trackedVarsUpperBound); - - // Partition the remaining set of variables - HashSet part1, part2; - var temp = new HashSet(trackedVarsUpperBound); - temp.ExceptWith(trackedVars); - Partition(temp, out part1, out part2); - - // First half - var fh = new HashSet(trackedVars); fh.UnionWith(part2); - var s1 = refinementLoop(apiChecker, fh, trackedVarsUpperBound, allVars); - - var a = new HashSet(part1); a.IntersectWith(s1); - var b = new HashSet(part1); b.ExceptWith(s1); - var c = new HashSet(trackedVarsUpperBound); c.ExceptWith(b); - a.UnionWith(trackedVars); - - // Second half - return refinementLoop(apiChecker, a, c, allVars); - } - - Dictionary>> satQueryCache; - Dictionary>> unsatQueryCache; - - private bool refinementLoopCheckPath(ApiChecker apiChecker, HashSet varsToSet, HashSet allVars) { - var assumptions = new List(); - var prover = apiChecker.prover; - var query = new HashSet(); - varsToSet.Iter(v => query.Add(v.Name)); - - if (checkCache(query, unsatQueryCache)) { - prover.LogComment("FindLeast: Query Cache Hit"); - return true; - } - if (checkCache(query, satQueryCache)) { - prover.LogComment("FindLeast: Query Cache Hit"); - return false; - } - - prover.LogComment("FindLeast: Query Begin"); - - foreach (var c in allVars) { - if (varsToSet.Contains(c)) { - assumptions.Add(c); - } - else { - assumptions.Add(prover.VCExprGen.Not(c)); - } - } - - var o = apiChecker.CheckAssumptions(assumptions); - if (o != Outcome.Correct && o != Outcome.Errors) - { - throw new FindLeastOORException("OOR", o); - } - //Console.WriteLine("Result = " + o.ToString()); - prover.LogComment("FindLeast: Query End"); - - if (o == Outcome.Correct) { - insertCache(query, unsatQueryCache); - return true; - } - - insertCache(query, satQueryCache); - return false; - } - - private bool checkCache(HashSet q, Dictionary>> cache) { - if (!cache.ContainsKey(q.Count)) return false; - foreach (var s in cache[q.Count]) { - if (q.SetEquals(s)) return true; - } - return false; - } - - private void insertCache(HashSet q, Dictionary>> cache) { - if (!cache.ContainsKey(q.Count)) { - cache.Add(q.Count, new List>()); - } - cache[q.Count].Add(q); - } - - public static void Partition(HashSet values, out HashSet part1, out HashSet part2) { - part1 = new HashSet(); - part2 = new HashSet(); - var size = values.Count; - var crossed = false; - var curr = 0; - foreach (var s in values) { - if (crossed) part2.Add(s); - else part1.Add(s); - curr++; - if (!crossed && curr >= size / 2) crossed = true; - } - } - - public override Outcome VerifyImplementation(Implementation/*!*/ impl, VerifierCallback/*!*/ callback) { - Debug.Assert(QKeyValue.FindBoolAttribute(impl.Attributes, "entrypoint")); - Debug.Assert(this.program == program); - - // Record current time - var startTime = DateTime.UtcNow; - - // Flush any axioms that came with the program before we start SI on this implementation - prover.AssertAxioms(); - - // Run live variable analysis - if (CommandLineOptions.Clo.LiveVariableAnalysis == 2) { - Microsoft.Boogie.InterProcGenKill.ComputeLiveVars(impl, program); - } - - // Get the VC of the current procedure - StratifiedInliningInfo info = implName2StratifiedInliningInfo[impl.Name]; - info.GenerateVC(); - VCExpr vc = info.vcexpr; - Dictionary mainLabel2absy = info.label2absy; - var reporter = new StratifiedInliningErrorReporter(implName2StratifiedInliningInfo, prover, callback, info); - - // Find all procedure calls in vc and put labels on them - FCallHandler calls = new FCallHandler(prover.VCExprGen, implName2StratifiedInliningInfo, impl.Name, mainLabel2absy); - calls.setCurrProcAsMain(); - vc = calls.Mutate(vc, true); - reporter.SetCandidateHandler(calls); - calls.id2VC.Add(0, vc); - calls.extraRecursion = extraRecBound; - if (CommandLineOptions.Clo.SIBoolControlVC) - { - calls.candiate2block2controlVar.Add(0, new Dictionary()); - implName2StratifiedInliningInfo[impl.Name].blockToControlVar.Iter(tup => - calls.candiate2block2controlVar[0].Add(tup.Key, tup.Value)); - } - - // We'll restore the original state of the theorem prover at the end - // of this procedure - prover.Push(); - - // Put all of the necessary state into one object - var vState = new VerificationState(vc, calls, prover, reporter); - vState.vcSize += SizeComputingVisitor.ComputeSize(vc); - - Outcome ret = Outcome.ReachedBound; - - #region eager inlining - for (int i = 1; i < CommandLineOptions.Clo.StratifiedInlining && calls.currCandidates.Count > 0; i++) { - List toExpand = new List(); - - foreach (int id in calls.currCandidates) { - if (calls.getRecursionBound(id) <= CommandLineOptions.Clo.RecursionBound) { - toExpand.Add(id); - } - } - DoExpansion(toExpand, vState); - } - #endregion - - #region Repopulate call tree, if there is one - if (PersistCallTree && callTree != null) { - bool expand = true; - while (expand) { - List toExpand = new List(); - foreach (int id in calls.currCandidates) { - if (callTree.Contains(calls.getPersistentId(id))) { - toExpand.Add(id); - } - } - if (toExpand.Count == 0) expand = false; - else { - DoExpansion(toExpand, vState); - } - } - } - #endregion - - if (CommandLineOptions.Clo.StratifiedInliningVerbose > 1) { - Console.WriteLine(">> SI: Size of VC after eager inlining: {0}", vState.vcSize); - } - - // Under-approx query is only needed if something was inlined since - // the last time an under-approx query was made - // TODO: introduce this - // bool underApproxNeeded = true; - - // The recursion bound for stratified search - int bound = CommandLineOptions.Clo.NonUniformUnfolding ? CommandLineOptions.Clo.RecursionBound : 1; - - int done = 0; - - int iters = 0; - - // for blocking candidates (and focusing on a counterexample) - var block = new HashSet(); - - // Process tasks while not done. We're done when: - // case 1: (correct) We didn't find a bug (either an over-approx query was valid - // or we reached the recursion bound) and the task is "step" - // case 2: (bug) We find a bug - // case 3: (internal error) The theorem prover TimesOut of runs OutOfMemory - while (true) - { - // Check timeout - if (CommandLineOptions.Clo.ProverKillTime != -1) - { - if ((DateTime.UtcNow - startTime).TotalSeconds > CommandLineOptions.Clo.ProverKillTime) - { - ret = Outcome.TimedOut; - break; - } - } - - if (done > 0) - { - break; - } - - // Stratified Step - ret = stratifiedStep(bound, vState, block); - iters++; - - // Sorry, out of luck (time/memory) - if (ret == Outcome.Inconclusive || ret == Outcome.OutOfMemory || ret == Outcome.TimedOut) - { - done = 3; - continue; - } - - if (ret == Outcome.Errors && reporter.underapproximationMode) - { - // Found a bug - done = 2; - } - else if (ret == Outcome.Correct) - { - if (block.Count == 0) - { - // Correct - done = 1; - } - else - { - // reset blocked and continue loop - block.Clear(); - } - } - else if (ret == Outcome.ReachedBound) - { - if (block.Count == 0) - { - if (CommandLineOptions.Clo.StratifiedInliningVerbose > 0) - Console.WriteLine(">> SI: Exhausted Bound {0}", bound); - - // Increment bound - bound++; - - if (bound > CommandLineOptions.Clo.RecursionBound) - { - // Correct under bound - done = 1; - } - } - else - { - // reset blocked and continue loop - block.Clear(); - } - } - else - { - // Do inlining - Debug.Assert(ret == Outcome.Errors && !reporter.underapproximationMode); - Contract.Assert(reporter.candidatesToExpand.Count != 0); - - #region expand call tree - if (CommandLineOptions.Clo.StratifiedInliningVerbose > 1) - { - Console.Write(">> SI Inlining: "); - reporter.candidatesToExpand - .Select(c => calls.getProc(c)) - .Iter(c => Console.Write("{0} ", c)); - - Console.WriteLine(); - } - - // Expand and try again - vState.checker.prover.LogComment(";;;;;;;;;;;; Expansion begin ;;;;;;;;;;"); - DoExpansion(reporter.candidatesToExpand, vState); - vState.checker.prover.LogComment(";;;;;;;;;;;; Expansion end ;;;;;;;;;;"); - - #endregion - } - } - - // Pop off everything that we pushed so that there are no side effects from - // this call to VerifyImplementation - vState.checker.prover.Pop(); - - if (CommandLineOptions.Clo.StratifiedInliningVerbose > 1) { - Console.WriteLine(">> SI: Expansions performed: {0}", vState.expansionCount); - Console.WriteLine(">> SI: Candidates left: {0}", calls.currCandidates.Count); - Console.WriteLine(">> SI: VC Size: {0}", vState.vcSize); - } - - vcsize = vState.vcSize; - numInlined = (calls.candidateParent.Keys.Count + 1) - (calls.currCandidates.Count); - - var rbound = "Procs that reached bound: "; - foreach (var s in procsThatReachedRecBound) rbound += " " + s; - if (ret == Outcome.ReachedBound) Helpers.ExtraTraceInformation(rbound); - if (CommandLineOptions.Clo.StackDepthBound > 0 && ret == Outcome.Correct) ret = Outcome.ReachedBound; - - // Store current call tree - if (PersistCallTree && (ret == Outcome.Correct || ret == Outcome.Errors || ret == Outcome.ReachedBound)) { - callTree = new HashSet(); - //var persistentNodes = new HashSet(calls.candidateParent.Values); - var persistentNodes = new HashSet(calls.candidateParent.Keys); - persistentNodes.Add(0); - persistentNodes.ExceptWith(calls.currCandidates); - - foreach (var id in persistentNodes) { - var pid = calls.getPersistentId(id); - Debug.Assert(!callTree.Contains(pid)); - callTree.Add(pid); - } - } - return ret; - } - - // A step of the stratified inlining algorithm: both under-approx and over-approx queries - private Outcome stratifiedStep(int bound, VerificationState vState, HashSet block) { - var calls = vState.calls; - var checker = vState.checker; - var prover = checker.prover; - var reporter = checker.reporter as StratifiedInliningErrorReporter; - - reporter.underapproximationMode = true; - prover.LogComment(";;;;;;;;;;;; Underapprox mode begin ;;;;;;;;;;"); - List assumptions = new List(); - - foreach (int id in calls.currCandidates) { - assumptions.Add(calls.getFalseExpr(id)); - } - Outcome ret = checker.CheckAssumptions(assumptions); - prover.LogComment(";;;;;;;;;;;; Underapprox mode end ;;;;;;;;;;"); - - if (ret != Outcome.Correct) { - // Either the query returned an error or it ran out of memory or time. - // In all cases, we are done. - return ret; - } - - if (calls.currCandidates.Count == 0) { - // If we didn't underapproximate, then we're done - return ret; - } - - prover.LogComment(";;;;;;;;;;;; Overapprox mode begin ;;;;;;;;;;"); - - // Over-approx query - reporter.underapproximationMode = false; - - // Push "true" for all, except: - // push "false" for all candidates that have reached - // the recursion bounds - - bool allTrue = true; - bool allFalse = true; - List softAssumptions = new List(); - - assumptions = new List(); - procsThatReachedRecBound.Clear(); - - foreach (int id in calls.currCandidates) { - - int idBound = calls.getRecursionBound(id); - int sd = calls.getStackDepth(id); - if (idBound <= bound && (CommandLineOptions.Clo.StackDepthBound == 0 || sd <= CommandLineOptions.Clo.StackDepthBound)) { - if (idBound > 1) - softAssumptions.Add(calls.getFalseExpr(id)); - - if (block.Contains(id)) { - assumptions.Add(calls.getFalseExpr(id)); - allTrue = false; - } - else { - allFalse = false; - } - } - else { - procsThatReachedRecBound.Add(calls.getProc(id)); - assumptions.Add(calls.getFalseExpr(id)); - allTrue = false; - } - } - - if (allFalse) { - // If we made all candidates false, then this is the same - // as the underapprox query. We already know the answer. - ret = Outcome.Correct; - } - else { - ret = CommandLineOptions.Clo.NonUniformUnfolding - ? checker.CheckAssumptions(assumptions, softAssumptions) - : checker.CheckAssumptions(assumptions); - } - - if (ret != Outcome.Correct && ret != Outcome.Errors) { - // The query ran out of memory or time, that's it, - // we cannot do better. Give up! - return ret; - } - - if (ret == Outcome.Correct) { - // If nothing was made false, then the program is correct - if (allTrue) { - return ret; - } - - // Nothing more can be done with current recursion bound. - return Outcome.ReachedBound; - } - - Contract.Assert(ret == Outcome.Errors); - - prover.LogComment(";;;;;;;;;;;; Overapprox mode end ;;;;;;;;;;"); - - return ret; - } - - // A counter for adding new variables - static int newVarCnt = 0; - - // Does on-demand inlining -- pushes procedure bodies on the theorem prover stack. - private void DoExpansion(List/*!*/ candidates, VerificationState vState) { - Contract.Requires(candidates != null); - Contract.Requires(vState.calls != null); - Contract.Requires(vState.checker.prover != null); - Contract.EnsuresOnThrow(true); - - vState.expansionCount += candidates.Count; - - var prover = vState.checker.prover; - var calls = vState.calls; - - VCExpr exprToPush = VCExpressionGenerator.True; - Contract.Assert(exprToPush != null); - foreach (int id in candidates) { - VCExprNAry expr = calls.id2Candidate[id]; - Contract.Assert(expr != null); - string procName = cce.NonNull(expr.Op as VCExprBoogieFunctionOp).Func.Name; - if (!implName2StratifiedInliningInfo.ContainsKey(procName)) continue; - - StratifiedInliningInfo info = implName2StratifiedInliningInfo[procName]; - info.GenerateVC(); - //Console.WriteLine("Inlining {0}", procName); - VCExpr expansion = cce.NonNull(info.vcexpr); - - // Instantiate the "forall" variables - Dictionary substForallDict = new Dictionary(); - Contract.Assert(info.interfaceExprVars.Count == expr.Length); - for (int i = 0; i < info.interfaceExprVars.Count; i++) { - substForallDict.Add(info.interfaceExprVars[i], expr[i]); - } - VCExprSubstitution substForall = new VCExprSubstitution(substForallDict, new Dictionary()); - - SubstitutingVCExprVisitor subst = new SubstitutingVCExprVisitor(prover.VCExprGen); - Contract.Assert(subst != null); - expansion = subst.Mutate(expansion, substForall); - - // Instantiate and declare the "exists" variables - Dictionary substExistsDict = new Dictionary(); - foreach (VCExprVar v in info.privateExprVars) { - Contract.Assert(v != null); - string newName = v.Name + "_si_" + newVarCnt.ToString(); - newVarCnt++; - prover.Context.DeclareConstant(new Constant(Token.NoToken, new TypedIdent(Token.NoToken, newName, v.Type)), false, null); - substExistsDict.Add(v, prover.VCExprGen.Variable(newName, v.Type)); - } - if (CommandLineOptions.Clo.SIBoolControlVC) - { - // record the mapping for control booleans (for tracing the path later) - calls.candiate2block2controlVar[id] = new Dictionary(); - foreach (var tup in info.blockToControlVar) - { - calls.candiate2block2controlVar[id].Add(tup.Key, - substExistsDict[tup.Value]); - } - } - if (CommandLineOptions.Clo.ModelViewFile != null) { - SaveSubstitution(vState, id, substForallDict, substExistsDict); - } - VCExprSubstitution substExists = new VCExprSubstitution(substExistsDict, new Dictionary()); - - subst = new SubstitutingVCExprVisitor(prover.VCExprGen); - expansion = subst.Mutate(expansion, substExists); - - if (!calls.currCandidates.Contains(id)) { - Console.WriteLine("Don't know what we just expanded"); - } - - calls.currCandidates.Remove(id); - - // Record the new set of candidates and rename absy labels - calls.currInlineCount = id; - calls.setCurrProc(procName); - expansion = calls.Mutate(expansion, true); - - //expansion = checker.VCExprGen.Eq(calls.id2ControlVar[id], expansion); - expansion = prover.VCExprGen.Implies(calls.id2ControlVar[id], expansion); - calls.id2VC.Add(id, expansion); - - exprToPush = prover.VCExprGen.And(exprToPush, expansion); - } - vState.checker.prover.Assert(exprToPush, true); - vState.vcSize += SizeComputingVisitor.ComputeSize(exprToPush); - } - - private void SaveSubstitution(VerificationState vState, int id, - Dictionary substForallDict, Dictionary substExistsDict) { - var prover = vState.checker.prover; - var calls = vState.calls; - Boogie2VCExprTranslator translator = prover.Context.BoogieExprTranslator; - VCExprVar mvStateConstant = translator.LookupVariable(ModelViewInfo.MVState_ConstantDef); - substExistsDict.Add(mvStateConstant, prover.VCExprGen.Integer(BigNum.FromInt(id))); - Dictionary mapping = new Dictionary(); - foreach (var key in substForallDict.Keys) - mapping[key] = substForallDict[key]; - foreach (var key in substExistsDict.Keys) - mapping[key] = substExistsDict[key]; - calls.id2Vars[id] = mapping; - } - - // Uniquely identifies a procedure call (the call expr, instance) - public class BoogieCallExpr : IEquatable { - public NAryExpr expr; - public int inlineCnt; - - public BoogieCallExpr(NAryExpr expr, int inlineCnt) { - this.expr = expr; - this.inlineCnt = inlineCnt; - } - - public override int GetHashCode() { - return expr.GetHashCode() + 131 * inlineCnt.GetHashCode(); - } - - public override bool Equals(object obj) { - BoogieCallExpr that = obj as BoogieCallExpr; - return (expr == that.expr && inlineCnt == that.inlineCnt); - } - - public bool Equals(BoogieCallExpr that) { - return (expr == that.expr && inlineCnt == that.inlineCnt); - } - } - - // This class is used to traverse VCs and do the following: - // -- collect the set of FunctionCall nodes and label them with a unique string - // -- Rename all other labels (so that calling this on the same VC results in - // VCs with different labels each time) - public class FCallHandler : MutatingVCExprVisitor { - Dictionary/*!*/ implName2StratifiedInliningInfo; - public readonly Dictionary/*!*/ mainLabel2absy; - public Dictionary/*!*/ boogieExpr2Id; - public Dictionary/*!*/ recordExpr2Var; - public Dictionary/*!*/ id2Candidate; - public Dictionary/*!*/ id2ControlVar; - public Dictionary id2VC; - public Dictionary/*!*/ label2Id; - // candidate to block to Bool Control variable - public Dictionary> candiate2block2controlVar; - // Stores the candidate from which this one originated - public Dictionary candidateParent; - // Mapping from candidate Id to the "si_unique_call" id that led to - // this candidate. This is useful for getting persistent names for - // candidates - public Dictionary candidate2callId; - // A cache for candidate id to its persistent name - public Dictionary persistentNameCache; - // Inverse of the above map - public Dictionary persistentNameInv; - // Used to record candidates recently added - public HashSet recentlyAddedCandidates; - // Name of main procedure - private string mainProcName; - // A map from candidate id to the VCExpr that represents its - // first argument (used for obtaining concrete values in error trace) - public Dictionary argExprMap; - - // map from candidate to summary candidates - public Dictionary>> summaryCandidates; - private Dictionary>> summaryTemp; - // set of all boolean guards of summaries - public HashSet allSummaryConst; - - public HashSet forcedCandidates; - - // User info -- to decrease/increase calculation of recursion bound - public Dictionary recursionIncrement; - public Dictionary extraRecursion; - - public HashSet currCandidates; - [ContractInvariantMethod] - void ObjectInvariant() { - Contract.Invariant(cce.NonNullDictionaryAndValues(implName2StratifiedInliningInfo)); - Contract.Invariant(mainLabel2absy != null); - Contract.Invariant(boogieExpr2Id != null); - Contract.Invariant(cce.NonNullDictionaryAndValues(id2Candidate)); - Contract.Invariant(cce.NonNullDictionaryAndValues(id2ControlVar)); - Contract.Invariant(label2Id != null); - } - - // Name of the procedure whose VC we're mutating - string currProc; - - // The 0^th candidate is main - static int candidateCount = 1; - public int currInlineCount; - - public Dictionary> id2Vars; - - public FCallHandler(VCExpressionGenerator/*!*/ gen, - Dictionary/*!*/ implName2StratifiedInliningInfo, - string mainProcName, Dictionary/*!*/ mainLabel2absy) - : base(gen) { - Contract.Requires(gen != null); - Contract.Requires(cce.NonNullDictionaryAndValues(implName2StratifiedInliningInfo)); - Contract.Requires(mainLabel2absy != null); - this.implName2StratifiedInliningInfo = implName2StratifiedInliningInfo; - this.mainProcName = mainProcName; - this.mainLabel2absy = mainLabel2absy; - id2Candidate = new Dictionary(); - id2ControlVar = new Dictionary(); - boogieExpr2Id = new Dictionary(); - label2Id = new Dictionary(); - currCandidates = new HashSet(); - currInlineCount = 0; - currProc = null; - labelRenamer = new Dictionary(); - labelRenamerInv = new Dictionary(); - candidateParent = new Dictionary(); - //callGraphMapping = new Dictionary(); - recursionIncrement = new Dictionary(); - candidate2callId = new Dictionary(); - persistentNameCache = new Dictionary(); - persistentNameInv = new Dictionary(); - persistentNameCache[0] = "0"; - persistentNameInv["0"] = 0; - recentlyAddedCandidates = new HashSet(); - argExprMap = new Dictionary(); - recordExpr2Var = new Dictionary(); - candiate2block2controlVar = new Dictionary>(); - - forcedCandidates = new HashSet(); - extraRecursion = new Dictionary(); - - id2Vars = new Dictionary>(); - summaryCandidates = new Dictionary>>(); - summaryTemp = new Dictionary>>(); - allSummaryConst = new HashSet(); - id2VC = new Dictionary(); - } - - public void Clear() { - currCandidates = new HashSet(); - } - - // Return the set of all candidates - public HashSet getAllCandidates() { - var ret = new HashSet(candidateParent.Keys); - ret.Add(0); - return ret; - } - - // Given a candidate "id", let proc(id) be the - // procedure corresponding to id. This procedure returns - // the number of times proc(id) appears as an ancestor - // of id. This is the same as the number of times we've - // recursed on proc(id) - public int getRecursionBound(int id) { - int ret = 1; - var str = getProc(id); - - while (candidateParent.ContainsKey(id)) { - if (recursionIncrement.ContainsKey(id)) ret += recursionIncrement[id]; - id = candidateParent[id]; - if (getProc(id) == str && !forcedCandidates.Contains(id)) ret++; - } - - // Usual - if (!extraRecursion.ContainsKey(str)) - return ret; - - // Usual - if (ret <= CommandLineOptions.Clo.RecursionBound - 1) - return ret; - - // Special - if (ret >= CommandLineOptions.Clo.RecursionBound && - ret <= CommandLineOptions.Clo.RecursionBound + extraRecursion[str] - 1) - return CommandLineOptions.Clo.RecursionBound - 1; - - // Special - return ret - extraRecursion[str]; - } - - // This procedure returns the stack depth of the candidate - // (distance from main) - public int getStackDepth(int id) - { - int ret = 1; - - while (candidateParent.ContainsKey(id)) - { - ret++; - id = candidateParent[id]; - } - - return ret; - } - - // Set user-define increment/decrement to recursionBound - public void setRecursionIncrement(int id, int incr) { - if (recursionIncrement.ContainsKey(id)) - recursionIncrement[id] = incr; - else - recursionIncrement.Add(id, incr); - } - - // Returns the name of the procedure corresponding to candidate id - public string getProc(int id) { - if (id == 0) return mainProcName; - - return (id2Candidate[id].Op as VCExprBoogieFunctionOp).Func.Name; - } - - // Get a unique id for this candidate (dependent only on the Call - // graph of the program). The persistent id is: - // 0: for main - // a_b_c: where a is the persistent id of parent, and b is the procedure name - // and c is the unique call id (if any) - public string getPersistentId(int top_id) { - if (top_id == 0) return "0"; - Debug.Assert(candidateParent.ContainsKey(top_id)); - if (persistentNameCache.ContainsKey(top_id)) - return persistentNameCache[top_id]; - - var parent_id = getPersistentId(candidateParent[top_id]); - var call_id = candidate2callId.ContainsKey(top_id) ? candidate2callId[top_id] : -1; - var ret = string.Format("{0}_131_{1}_131_{2}", parent_id, getProc(top_id), call_id); - persistentNameCache[top_id] = ret; - persistentNameInv[ret] = top_id; - return ret; - } - - public int getCandidateFromGraphNode(string n) { - if (!persistentNameInv.ContainsKey(n)) { - return -1; - } - return persistentNameInv[n]; - } - - private int GetNewId(VCExprNAry vc) { - Contract.Requires(vc != null); - int id = candidateCount; - - id2Candidate[id] = vc; - id2ControlVar[id] = Gen.Variable("si_control_var_bool_" + id.ToString(), Microsoft.Boogie.Type.Bool); - - candidateCount++; - currCandidates.Add(id); - recentlyAddedCandidates.Add(id); - - return id; - } - - private string GetLabel(int id) { - Contract.Ensures(Contract.Result() != null); - - string ret = "si_fcall_" + id.ToString(); - if (!label2Id.ContainsKey(ret)) - label2Id[ret] = id; - - return ret; - } - - public int GetId(string label) { - Contract.Requires(label != null); - if (!label2Id.ContainsKey(label)) - return -1; - return label2Id[label]; - } - - Dictionary labelRenamer; - Dictionary labelRenamerInv; - - public string RenameAbsyLabel(string label) { - Contract.Requires(label != null); - Contract.Requires(label.Length >= 1); - Contract.Ensures(Contract.Result() != null); - - // Remove the sign from the label - string nosign = label.Substring(1); - var ret = "si_inline_" + currInlineCount.ToString() + "_" + nosign; - - if (!labelRenamer.ContainsKey(ret)) { - var c = labelRenamer.Count + 11; // two digit labels only - labelRenamer.Add(ret, c); - labelRenamerInv.Add(c.ToString(), ret); - } - return labelRenamer[ret].ToString(); - } - - public string ParseRenamedAbsyLabel(string label, int cnt) { - Contract.Requires(label != null); - if (!labelRenamerInv.ContainsKey(label)) { - return null; - } - var str = labelRenamerInv[label]; - var prefix = "si_inline_" + cnt.ToString() + "_"; - if (!str.StartsWith(prefix)) return null; - return str.Substring(prefix.Length); - } - - public void setCurrProc(string name) { - Contract.Requires(name != null); - currProc = name; - Contract.Assert(implName2StratifiedInliningInfo.ContainsKey(name)); - } - - public void setCurrProcAsMain() { - currProc = ""; - } - - // Return the formula (candidate IFF false) - public VCExpr getFalseExpr(int candidateId) { - //return Gen.Eq(VCExpressionGenerator.False, id2ControlVar[candidateId]); - return Gen.Not(id2ControlVar[candidateId]); - } - - // Return the formula (candidate IFF true) - public VCExpr getTrueExpr(int candidateId) { - return Gen.Eq(VCExpressionGenerator.True, id2ControlVar[candidateId]); - } - - public Dictionary getLabel2absy() { - Contract.Ensures(Contract.Result>() != null); - - Contract.Assert(currProc != null); - if (currProc == "") { - return mainLabel2absy; - } - return cce.NonNull(implName2StratifiedInliningInfo[currProc].label2absy); - } - - // Finds labels and changes them: - // si_fcall_id: if "id" corresponds to a tracked procedure call, then - // si_control_var_candidateId - // si_fcall_id: if "id" does not corresponds to a tracked procedure call, then - // delete - // num: si_inline_num - // - protected override VCExpr/*!*/ UpdateModifiedNode(VCExprNAry/*!*/ originalNode, - List/*!*/ newSubExprs, - // has any of the subexpressions changed? - bool changed, - bool arg) { - //Contract.Requires(originalNode != null); - //Contract.Requires(cce.NonNullElements(newSubExprs)); - Contract.Ensures(Contract.Result() != null); - - VCExpr ret; - if (changed) - ret = Gen.Function(originalNode.Op, - newSubExprs, originalNode.TypeArguments); - else - ret = originalNode; - - VCExprLabelOp lop = originalNode.Op as VCExprLabelOp; - if (lop == null) return ret; - if (!(ret is VCExprNAry)) return ret; - - VCExprNAry retnary = (VCExprNAry)ret; - Contract.Assert(retnary != null); - string prefix = "si_fcall_"; // from Wlp.ssc::Cmd(...) - if (lop.label.Substring(1).StartsWith(prefix)) { - int id = Int32.Parse(lop.label.Substring(prefix.Length + 1)); - Dictionary label2absy = getLabel2absy(); - Absy cmd = label2absy[id] as Absy; - //label2absy.Remove(id); - - Contract.Assert(cmd != null); - AssumeCmd acmd = cmd as AssumeCmd; - Contract.Assert(acmd != null); - NAryExpr naryExpr = acmd.Expr as NAryExpr; - Contract.Assert(naryExpr != null); - - string calleeName = naryExpr.Fun.FunctionName; - - VCExprNAry callExpr = retnary[0] as VCExprNAry; - - if (implName2StratifiedInliningInfo.ContainsKey(calleeName)) { - Contract.Assert(callExpr != null); - int candidateId = GetNewId(callExpr); - boogieExpr2Id[new BoogieCallExpr(naryExpr, currInlineCount)] = candidateId; - candidateParent[candidateId] = currInlineCount; - candiate2block2controlVar[candidateId] = new Dictionary(); - - string label = GetLabel(candidateId); - var unique_call_id = QKeyValue.FindIntAttribute(acmd.Attributes, "si_unique_call", -1); - if (unique_call_id != -1) - candidate2callId[candidateId] = unique_call_id; - - //return Gen.LabelPos(label, callExpr); - return Gen.LabelPos(label, id2ControlVar[candidateId]); - } - else if (calleeName.StartsWith(recordProcName)) { - Contract.Assert(callExpr != null); - Debug.Assert(callExpr.Length == 1); - Debug.Assert(callExpr[0] != null); - recordExpr2Var[new BoogieCallExpr(naryExpr, currInlineCount)] = callExpr[0]; - return callExpr; - } - else { - // callExpr can be null; this happens when the FunctionCall was on a - // pure function (not procedure) and the function got inlined - return retnary[0]; - } - } - - // Else, rename label - string newLabel = RenameAbsyLabel(lop.label); - if (lop.pos) { - return Gen.LabelPos(newLabel, retnary[0]); - } - else { - return Gen.LabelNeg(newLabel, retnary[0]); - } - - } - - // Upgrades summaryTemp to summaryCandidates by matching ensure clauses with - // the appropriate candidate they came from - public void matchSummaries() { - var id2Set = new Dictionary>>>(); - foreach (var id in recentlyAddedCandidates) { - var collect = new CollectVCVars(); - var proc = getProc(id); - if (!id2Set.ContainsKey(proc)) id2Set.Add(proc, new List>>()); - id2Set[proc].Add(Tuple.Create(id, collect.Collect(id2Candidate[id], true))); - } - - foreach (var kvp in summaryTemp) { - Contract.Assert(id2Set.ContainsKey(kvp.Key)); - var ls = id2Set[kvp.Key]; - foreach (var tup in kvp.Value) { - var collect = new CollectVCVars(); - var s1 = collect.Collect(tup.Item2, true); - var found = false; - foreach (var t in ls) { - var s2 = t.Item2; - if (s1.IsSubsetOf(s2)) { - if (!summaryCandidates.ContainsKey(t.Item1)) - summaryCandidates.Add(t.Item1, new List>()); - summaryCandidates[t.Item1].Add(tup); - allSummaryConst.Add(tup.Item1); - found = true; - break; - } - } - Contract.Assert(found); - } - } - summaryTemp.Clear(); - } - - public IEnumerable getInlinedCandidates() { - return candidateParent.Keys.Except(currCandidates).Union(new int[] { 0 }); - } - - } // end FCallHandler - - // Collects the set of all VCExprVar in a given VCExpr - class CollectVCVars : CollectingVCExprVisitor, bool> { - public override HashSet Visit(VCExprVar node, bool arg) { - var ret = new HashSet(); - ret.Add(node); - return ret; - } - - protected override HashSet CombineResults(List> results, bool arg) { - var ret = new HashSet(); - results.Iter(s => ret.UnionWith(s)); - return ret; - } - } - - public class FCallInliner : MutatingVCExprVisitor { - public Dictionary/*!*/ subst; - [ContractInvariantMethod] - void ObjectInvariant() { - Contract.Invariant(cce.NonNullDictionaryAndValues(subst)); - } - - - public FCallInliner(VCExpressionGenerator gen) - : base(gen) { - Contract.Requires(gen != null); - subst = new Dictionary(); - } - - public void Clear() { - subst = new Dictionary(); - } - - protected override VCExpr/*!*/ UpdateModifiedNode(VCExprNAry/*!*/ originalNode, - List/*!*/ newSubExprs, - // has any of the subexpressions changed? - bool changed, - bool arg) { - //Contract.Requires(originalNode != null);Contract.Requires(newSubExprs != null); - Contract.Ensures(Contract.Result() != null); - - VCExpr ret; - if (changed) - ret = Gen.Function(originalNode.Op, newSubExprs, originalNode.TypeArguments); - else - ret = originalNode; - - VCExprLabelOp lop = originalNode.Op as VCExprLabelOp; - if (lop == null) return ret; - if (!(ret is VCExprNAry)) return ret; - - string prefix = "si_fcall_"; // from FCallHandler::GetLabel - if (lop.label.Substring(1).StartsWith(prefix)) { - int id = Int32.Parse(lop.label.Substring(prefix.Length + 1)); - if (subst.ContainsKey(id)) { - return subst[id]; - } - } - return ret; - } - - } // end FCallInliner - - - - public class StratifiedInliningErrorReporter : ProverInterface.ErrorHandler { - Dictionary implName2StratifiedInliningInfo; - ProverInterface theoremProver; - VerifierCallback callback; - FCallHandler calls; - StratifiedInliningInfo mainInfo; - StratifiedVC mainVC; - - public bool underapproximationMode; - public List candidatesToExpand; - public List callSitesToExpand; - - [ContractInvariantMethod] - void ObjectInvariant() { - Contract.Invariant(candidatesToExpand != null); - Contract.Invariant(mainInfo != null); - Contract.Invariant(callback != null); - Contract.Invariant(theoremProver != null); - Contract.Invariant(cce.NonNullDictionaryAndValues(implName2StratifiedInliningInfo)); - } - - - public StratifiedInliningErrorReporter(Dictionary implName2StratifiedInliningInfo, - ProverInterface theoremProver, VerifierCallback callback, - StratifiedInliningInfo mainInfo) { - Contract.Requires(cce.NonNullDictionaryAndValues(implName2StratifiedInliningInfo)); - Contract.Requires(theoremProver != null); - Contract.Requires(callback != null); - Contract.Requires(mainInfo != null); - this.implName2StratifiedInliningInfo = implName2StratifiedInliningInfo; - this.theoremProver = theoremProver; - this.callback = callback; - this.mainInfo = mainInfo; - this.underapproximationMode = false; - this.calls = null; - this.candidatesToExpand = new List(); - } - - public StratifiedInliningErrorReporter(Dictionary implName2StratifiedInliningInfo, - ProverInterface theoremProver, VerifierCallback callback, - StratifiedVC mainVC) { - Contract.Requires(cce.NonNullDictionaryAndValues(implName2StratifiedInliningInfo)); - Contract.Requires(theoremProver != null); - Contract.Requires(callback != null); - Contract.Requires(mainVC != null); - this.implName2StratifiedInliningInfo = implName2StratifiedInliningInfo; - this.theoremProver = theoremProver; - this.callback = callback; - this.mainVC = mainVC; - this.underapproximationMode = false; - this.candidatesToExpand = new List(); - } - - public void SetCandidateHandler(FCallHandler calls) { - Contract.Requires(calls != null); - this.calls = calls; - } - - List> orderedStateIds; - - private Model.Element GetModelValue(Model m, Variable v, int candidateId) { - // first, get the unique name - string uniqueName; - - VCExprVar vvar = theoremProver.Context.BoogieExprTranslator.TryLookupVariable(v); - if (vvar == null) { - uniqueName = v.Name; - } - else { - if (candidateId != 0) { - Dictionary mapping = calls.id2Vars[candidateId]; - if (mapping.ContainsKey(vvar)) { - VCExpr e = mapping[vvar]; - if (e is VCExprLiteral) { - VCExprLiteral lit = (VCExprLiteral)e; - return m.MkElement(lit.ToString()); - } - vvar = (VCExprVar)mapping[vvar]; - } - } - uniqueName = theoremProver.Context.Lookup(vvar); - } - - var f = m.TryGetFunc(uniqueName); - if (f == null) - return m.MkFunc("@undefined", 0).GetConstant(); - return f.GetConstant(); - } - - public readonly static int CALL = -1; - public readonly static int RETURN = -2; - - public void PrintModel(Model model) { - var filename = CommandLineOptions.Clo.ModelViewFile; - if (model == null || filename == null) return; - - if (filename == "-") { - model.Write(Console.Out); - Console.Out.Flush(); - } - else { - using (var wr = new StreamWriter(filename, !Counterexample.firstModelFile)) { - Counterexample.firstModelFile = false; - model.Write(wr); - } - } - } - - private void GetModelWithStates(Model m) { - if (m == null) return; - var mvInfo = mainInfo.mvInfo; - var mvstates = m.TryGetFunc("$mv_state"); - if (mvstates == null) - return; - - Contract.Assert(mvstates.Arity == 2); - - foreach (Variable v in mvInfo.AllVariables) { - m.InitialState.AddBinding(v.Name, GetModelValue(m, v, 0)); - } - - int lastCandidate = 0; - int lastCapturePoint = CALL; - for (int i = 0; i < this.orderedStateIds.Count; ++i) { - var s = orderedStateIds[i]; - int candidate = s.Item1; - int capturePoint = s.Item2; - string implName = calls.getProc(candidate); - ModelViewInfo info = candidate == 0 ? mvInfo : implName2StratifiedInliningInfo[implName].mvInfo; - - if (capturePoint == CALL || capturePoint == RETURN) { - lastCandidate = candidate; - lastCapturePoint = capturePoint; - continue; - } - - Contract.Assume(0 <= capturePoint && capturePoint < info.CapturePoints.Count); - VC.ModelViewInfo.Mapping map = info.CapturePoints[capturePoint]; - var prevInc = (lastCapturePoint != CALL && lastCapturePoint != RETURN && candidate == lastCandidate) - ? info.CapturePoints[lastCapturePoint].IncarnationMap : new Dictionary(); - var cs = m.MkState(map.Description); - - foreach (Variable v in info.AllVariables) { - var e = (Expr)map.IncarnationMap[v]; - - if (e == null) { - if (lastCapturePoint == CALL || lastCapturePoint == RETURN) { - cs.AddBinding(v.Name, GetModelValue(m, v, candidate)); - } - continue; - } - - if (lastCapturePoint != CALL && lastCapturePoint != RETURN && prevInc[v] == e) continue; // skip unchanged variables - - Model.Element elt; - if (e is IdentifierExpr) { - IdentifierExpr ide = (IdentifierExpr)e; - elt = GetModelValue(m, ide.Decl, candidate); - } - else if (e is LiteralExpr) { - LiteralExpr lit = (LiteralExpr)e; - elt = m.MkElement(lit.Val.ToString()); - } - else { - Contract.Assume(false); - elt = m.MkFunc(e.ToString(), 0).GetConstant(); - } - cs.AddBinding(v.Name, elt); - } - - lastCandidate = candidate; - lastCapturePoint = capturePoint; - } - - return; - } - - public override void OnResourceExceeded(string message, IEnumerable> assertCmds = null) - { - //Contract.Requires(message != null); - } - - public override void OnModel(IList/*!*/ labels, Model model, ProverInterface.Outcome proverOutcome) { - if (CommandLineOptions.Clo.PrintErrorModel >= 1 && model != null) { - model.Write(ErrorReporter.ModelWriter); - ErrorReporter.ModelWriter.Flush(); - } - - // Timeout? - if (proverOutcome != ProverInterface.Outcome.Invalid) - return; - - candidatesToExpand = new List(); - orderedStateIds = new List>(); - var cex = GenerateTrace(labels, model, 0, mainInfo.impl, mainInfo.mvInfo); - - if (underapproximationMode && cex != null) { - //Debug.Assert(candidatesToExpand.All(calls.isSkipped)); - GetModelWithStates(model); - callback.OnCounterexample(cex, null); - this.PrintModel(model); - } - } - - private Counterexample GenerateTrace(IList/*!*/ labels, Model/*!*/ errModel, - int candidateId, Implementation procImpl, ModelViewInfo mvInfo) { - Contract.Requires(cce.NonNullElements(labels)); - Contract.Requires(procImpl != null); - - Hashtable traceNodes = new Hashtable(); - - if (!CommandLineOptions.Clo.SIBoolControlVC) - { - foreach (string s in labels) - { - Contract.Assert(s != null); - var absylabel = calls.ParseRenamedAbsyLabel(s, candidateId); - - if (absylabel == null) continue; - - Absy absy; - - if (candidateId == 0) - { - absy = Label2Absy(absylabel); - } - else - { - absy = Label2Absy(procImpl.Name, absylabel); - } - - if (traceNodes.ContainsKey(absy)) - System.Console.WriteLine("Warning: duplicate label: " + s + " read while tracing nodes"); - else - traceNodes.Add(absy, null); - } - } - else - { - Debug.Assert(CommandLineOptions.Clo.UseProverEvaluate, "Must use prover evaluate option with boolControlVC"); - var block = procImpl.Blocks[0]; - traceNodes.Add(block, null); - while (true) - { - var gc = block.TransferCmd as GotoCmd; - if (gc == null) break; - Block next = null; - foreach (var succ in gc.labelTargets) - { - var succtaken = (bool) theoremProver.Evaluate(calls.candiate2block2controlVar[candidateId][succ]); - if (succtaken) - { - next = succ; - traceNodes.Add(succ, null); - break; - } - } - Debug.Assert(next != null, "Must find a successor"); - Debug.Assert(traceNodes.ContainsKey(next), "CFG cannot be cyclic"); - block = next; - } - } - - List trace = new List(); - Block entryBlock = cce.NonNull(procImpl.Blocks[0]); - Contract.Assert(entryBlock != null); - Contract.Assert(traceNodes.Contains(entryBlock)); - trace.Add(entryBlock); - - var calleeCounterexamples = new Dictionary(); - Counterexample newCounterexample = GenerateTraceRec(labels, errModel, mvInfo, candidateId, entryBlock, traceNodes, trace, calleeCounterexamples); - - return newCounterexample; - } - - private Counterexample GenerateTraceRec( - IList/*!*/ labels, Model/*!*/ errModel, ModelViewInfo mvInfo, - int candidateId, - Block/*!*/ b, Hashtable/*!*/ traceNodes, List/*!*/ trace, - Dictionary/*!*/ calleeCounterexamples) { - Contract.Requires(cce.NonNullElements(labels)); - Contract.Requires(b != null); - Contract.Requires(traceNodes != null); - Contract.Requires(trace != null); - Contract.Requires(cce.NonNullDictionaryAndValues(calleeCounterexamples)); - // After translation, all potential errors come from asserts. - while (true) { - List cmds = b.Cmds; - TransferCmd transferCmd = cce.NonNull(b.TransferCmd); - for (int i = 0; i < cmds.Count; i++) { - Cmd cmd = cce.NonNull(cmds[i]); - - // Skip if 'cmd' not contained in the trace or not an assert - if ((cmd is AssertCmd && traceNodes.Contains(cmd)) || - (cmd is AssumeCmd && QKeyValue.FindBoolAttribute((cmd as AssumeCmd).Attributes, "exitAssert"))) - { - var acmd = cmd as AssertCmd; - if (acmd == null) { acmd = new AssertCmd(Token.NoToken, Expr.True); } - Counterexample newCounterexample = AssertCmdToCounterexample(acmd, transferCmd, trace, errModel, mvInfo, theoremProver.Context); - newCounterexample.AddCalleeCounterexample(calleeCounterexamples); - return newCounterexample; - } - - // Counterexample generation for inlined procedures - AssumeCmd assumeCmd = cmd as AssumeCmd; - if (assumeCmd == null) - continue; - NAryExpr naryExpr = assumeCmd.Expr as NAryExpr; - if (naryExpr == null) - continue; - string calleeName = naryExpr.Fun.FunctionName; - Contract.Assert(calleeName != null); - - BinaryOperator binOp = naryExpr.Fun as BinaryOperator; - if (binOp != null && binOp.Op == BinaryOperator.Opcode.And) { - Expr expr = naryExpr.Args[0]; - NAryExpr mvStateExpr = expr as NAryExpr; - if (mvStateExpr != null && mvStateExpr.Fun.FunctionName == ModelViewInfo.MVState_FunctionDef.Name) { - LiteralExpr x = mvStateExpr.Args[1] as LiteralExpr; - orderedStateIds.Add(new Tuple(candidateId, x.asBigNum.ToInt)); - } - } - - if (calleeName.StartsWith(recordProcName) && (errModel != null || CommandLineOptions.Clo.UseProverEvaluate)) { - var expr = calls.recordExpr2Var[new BoogieCallExpr(naryExpr, candidateId)]; - - // Record concrete value of the argument to this procedure - var args = new List(); - if (errModel == null && CommandLineOptions.Clo.UseProverEvaluate) - { - object exprv; - try - { - exprv = theoremProver.Evaluate(expr); - } - catch (Exception) - { - exprv = null; - } - args.Add(exprv); - } - else - { - if (expr is VCExprIntLit) - { - args.Add(errModel.MkElement((expr as VCExprIntLit).Val.ToString())); - } - else if (expr == VCExpressionGenerator.True) - { - args.Add(errModel.MkElement("true")); - } - else if (expr == VCExpressionGenerator.False) - { - args.Add(errModel.MkElement("false")); - } - else if (expr is VCExprVar) - { - var idExpr = expr as VCExprVar; - string name = theoremProver.Context.Lookup(idExpr); - Contract.Assert(name != null); - Model.Func f = errModel.TryGetFunc(name); - if (f != null) - { - args.Add(f.GetConstant()); - } - } - else - { - Contract.Assert(false); - } - } - calleeCounterexamples[new TraceLocation(trace.Count - 1, i)] = - new CalleeCounterexampleInfo(null, args); - continue; - } - - if (!implName2StratifiedInliningInfo.ContainsKey(calleeName)) - continue; - - Contract.Assert(calls != null); - - int calleeId = calls.boogieExpr2Id[new BoogieCallExpr(naryExpr, candidateId)]; - - if (calls.currCandidates.Contains(calleeId)) { - candidatesToExpand.Add(calleeId); - } - else { - orderedStateIds.Add(new Tuple(calleeId, StratifiedInliningErrorReporter.CALL)); - var calleeInfo = implName2StratifiedInliningInfo[calleeName]; - calleeCounterexamples[new TraceLocation(trace.Count - 1, i)] = - new CalleeCounterexampleInfo(GenerateTrace(labels, errModel, calleeId, calleeInfo.impl, calleeInfo.mvInfo), new List()); - orderedStateIds.Add(new Tuple(candidateId, StratifiedInliningErrorReporter.RETURN)); - } - } - - GotoCmd gotoCmd = transferCmd as GotoCmd; - if (gotoCmd != null) { - b = null; - foreach (Block bb in cce.NonNull(gotoCmd.labelTargets)) { - Contract.Assert(bb != null); - if (traceNodes.Contains(bb)) { - trace.Add(bb); - b = bb; - break; - } - } - if (b != null) continue; - } - return null; - } - } - - public override Absy Label2Absy(string label) { - //Contract.Requires(label != null); - Contract.Ensures(Contract.Result() != null); - - int id = int.Parse(label); - Contract.Assert(calls != null); - return cce.NonNull((Absy)calls.mainLabel2absy[id]); - } - - public Absy Label2Absy(string procName, string label) { - Contract.Requires(label != null); - Contract.Requires(procName != null); - Contract.Ensures(Contract.Result() != null); - - int id = int.Parse(label); - Dictionary l2a = cce.NonNull(implName2StratifiedInliningInfo[procName]).label2absy; - return cce.NonNull((Absy)l2a[id]); - } - - public override void OnProverWarning(string msg) { - //Contract.Requires(msg != null); - callback.OnWarning(msg); - } - } - - } // class StratifiedVCGen - - public class EmptyErrorHandler : ProverInterface.ErrorHandler - { - public override void OnModel(IList labels, Model model, ProverInterface.Outcome proverOutcome) - { } - } - - public class InvalidProgramForSecureVc : Exception - { - public InvalidProgramForSecureVc(string msg) : - base(msg) { } - } - - public class SecureVCGen : VCGen - { - // Z3 - ProverInterface prover; - // Handler - ErrorReporter handler; - // dump file - public static TokenTextWriter outfile = null; - - - public SecureVCGen(Program program, string/*?*/ logFilePath, bool appendLogFile, List checkers) - : base(program, logFilePath, appendLogFile, checkers) - { - prover = null; - handler = null; - if (CommandLineOptions.Clo.SecureVcGen != "" && outfile == null) - { - outfile = new TokenTextWriter(new StreamWriter(CommandLineOptions.Clo.SecureVcGen)); - CommandLineOptions.Clo.PrintInstrumented = true; - var implsToVerify = new HashSet( - program.TopLevelDeclarations.OfType() - .Where(impl => !impl.SkipVerification) - .Select(impl => impl.Name)); - - foreach (var decl in program.TopLevelDeclarations) - { - if (decl is NamedDeclaration && implsToVerify.Contains((decl as NamedDeclaration).Name)) - continue; - decl.Emit(outfile, 0); - } - } - } - - private Block GetExitBlock(Implementation impl) - { - var exitblocks = impl.Blocks.Where(blk => blk.TransferCmd is ReturnCmd); - if (exitblocks.Count() == 1) - return exitblocks.First(); - // create a new exit block - var eb = new Block(Token.NoToken, "SVCeb", new List(), new ReturnCmd(Token.NoToken)); - foreach (var b in exitblocks) - { - b.TransferCmd = new GotoCmd(Token.NoToken, new List { eb }); - } - impl.Blocks.Add(eb); - return eb; - } - - //static int LocalVarCounter = 0; - private LocalVariable GetNewLocal(Variable v, string suffix) - { - return new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, - string.Format("svc_{0}_{1}", v.Name, suffix), v.TypedIdent.Type)); - } - - private void GenVc(Implementation impl, VerifierCallback collector) - { - if (impl.Proc.Requires.Any()) - throw new InvalidProgramForSecureVc("SecureVc: Requires not supported"); - if(impl.LocVars.Any(v => isVisible(v))) - throw new InvalidProgramForSecureVc("SecureVc: Visible Local variables not allowed"); - - // Desugar procedure calls - DesugarCalls(impl); - - // Gather spec, remove existing ensures - var secureAsserts = new List(); - var logicalAsserts = new List(); - - foreach (var ens in impl.Proc.Ensures) - { - if(ens.Free) - throw new InvalidProgramForSecureVc("SecureVc: Free Ensures not supported"); - var dd = new Duplicator(); - secureAsserts.Add(new AssertCmd(ens.tok, Expr.Not(ens.Condition))); - logicalAsserts.Add(dd.VisitAssertCmd(new AssertCmd(ens.tok, ens.Condition)) as AssertCmd); - } - impl.Proc.Ensures.Clear(); - - // Make a copy of the impl - var dup = new Duplicator(); - var implDup = dup.VisitImplementation(impl); - - // Get exit block - var eb = GetExitBlock(impl); - - // Create two blocks: one for secureAsserts, one for logical asserts - var ebSecure = new Block(Token.NoToken, "svc_secure_asserts", new List(), new ReturnCmd(Token.NoToken)); - var ebLogical = new Block(Token.NoToken, "svc_logical_asserts", new List(), new ReturnCmd(Token.NoToken)); - - eb.TransferCmd = new GotoCmd(eb.TransferCmd.tok, new List { ebSecure, ebLogical }); - impl.Blocks.Add(ebSecure); - impl.Blocks.Add(ebLogical); - - // Rename spec, while create copies of the hidden variables - var substOld = new Dictionary(); - var substVarSpec = new Dictionary(); - var substVarPath = new Dictionary(); - foreach (var g in program.GlobalVariables) - { - if (!isHidden(g)) continue; - var lv = GetNewLocal(g, "In"); - impl.LocVars.Add(lv); - substOld.Add(g, Expr.Ident(lv)); - } - - for(int i = 0; i < impl.InParams.Count; i++) - { - var v = impl.Proc.InParams[i]; - if (!isHidden(v)) - { - substVarSpec.Add(impl.Proc.InParams[i], Expr.Ident(impl.InParams[i])); - continue; - } - - var lv = GetNewLocal(v, "In"); - impl.LocVars.Add(lv); - substVarSpec.Add(v, Expr.Ident(lv)); - substVarPath.Add(impl.InParams[i], Expr.Ident(lv)); - } - - for (int i = 0; i < impl.OutParams.Count; i++) - { - var v = impl.Proc.OutParams[i]; - if (!isHidden(v)) - { - substVarSpec.Add(impl.Proc.OutParams[i], Expr.Ident(impl.OutParams[i])); - continue; - } - - var lv = GetNewLocal(v, "Out"); - impl.LocVars.Add(lv); - substVarSpec.Add(v, Expr.Ident(lv)); - substVarPath.Add(impl.OutParams[i], Expr.Ident(lv)); - } - - foreach (var g in program.GlobalVariables) - { - if (!isHidden(g)) continue; - if (!impl.Proc.Modifies.Any(ie => ie.Name == g.Name)) continue; - - var lv = GetNewLocal(g, "Out"); - impl.LocVars.Add(lv); - substVarSpec.Add(g, Expr.Ident(lv)); - substVarPath.Add(g, Expr.Ident(lv)); - } - - secureAsserts = secureAsserts.ConvertAll(ac => - Substituter.ApplyReplacingOldExprs( - Substituter.SubstitutionFromHashtable(substVarSpec), - Substituter.SubstitutionFromHashtable(substOld), - ac) as AssertCmd); - - var substVarProcToImpl = new Dictionary(); - for (int i = 0; i < impl.InParams.Count; i++) - substVarProcToImpl.Add(impl.Proc.InParams[i], Expr.Ident(impl.InParams[i])); - - for (int i = 0; i < impl.OutParams.Count; i++) - substVarProcToImpl.Add(impl.Proc.OutParams[i], Expr.Ident(impl.OutParams[i])); - - logicalAsserts = logicalAsserts.ConvertAll(ac => - Substituter.Apply(Substituter.SubstitutionFromHashtable(substVarProcToImpl), ac) - as AssertCmd); - - // Paths - foreach (var path in GetAllPaths(implDup)) - { - var wp = ComputeWP(implDup, path); - - // replace hidden variables to match those used in the spec - wp = Substituter.ApplyReplacingOldExprs( - Substituter.SubstitutionFromHashtable(substVarPath), - Substituter.SubstitutionFromHashtable(substOld), - wp); - - ebSecure.Cmds.Add(new AssumeCmd(Token.NoToken, Expr.Not(wp))); - } - - ebSecure.Cmds.AddRange(secureAsserts); - ebLogical.Cmds.AddRange(logicalAsserts); - - if (outfile != null) - { - impl.Proc.Emit(outfile, 0); - impl.Emit(outfile, 0); - } - - ModelViewInfo mvInfo; - ConvertCFG2DAG(impl); - var gotoCmdOrigins = PassifyImpl(impl, out mvInfo); - - var gen = prover.VCExprGen; - var exprGen = prover.Context.ExprGen; - var translator = prover.Context.BoogieExprTranslator; - - var label2absy = new Dictionary(); - VCGen.CodeExprConversionClosure cc = new VCGen.CodeExprConversionClosure(label2absy, prover.Context); - translator.SetCodeExprConverter(cc.CodeExprToVerificationCondition); - var implVc = gen.Not(GenerateVCAux(impl, null, label2absy, prover.Context)); - - handler = new VCGen.ErrorReporter(gotoCmdOrigins, label2absy, impl.Blocks, incarnationOriginMap, collector, mvInfo, prover.Context, program); - - prover.Assert(implVc, true); - } - - Expr ComputeWP(Implementation impl, List path) - { - Expr expr = Expr.True; - - // create constants for out varibles - var subst = new Dictionary(); - foreach (var g in impl.Proc.Modifies) - { - var c = new Constant(Token.NoToken, new TypedIdent(Token.NoToken, - "svc_out_const_" + g.Name, g.Decl.TypedIdent.Type)); - subst.Add(c, g); - expr = Expr.And(expr, Expr.Eq(Expr.Ident(c), g)); - } - - foreach (var v in impl.OutParams) - { - var c = new Constant(Token.NoToken, new TypedIdent(Token.NoToken, - "svc_out_const_" + v.Name, v.TypedIdent.Type)); - subst.Add(c, Expr.Ident(v)); - expr = Expr.And(expr, Expr.Eq(Expr.Ident(c), Expr.Ident(v))); - } - - // we need this technicality - var subst1 = new Dictionary(); - foreach (var g in program.GlobalVariables) - { - subst1.Add(g, new OldExpr(Token.NoToken, Expr.Ident(g))); - } - - // Implicitly close with havoc of all the locals and OutParams - path.Insert(0, new HavocCmd(Token.NoToken, new List( - impl.LocVars.Select(v => Expr.Ident(v)).Concat( - impl.OutParams.Select(v => Expr.Ident(v)))))); - - for (int i = path.Count - 1; i >= 0; i--) - { - var cmd = path[i]; - if (cmd is AssumeCmd) - { - expr = Expr.And(expr, (cmd as AssumeCmd).Expr); - } - else if (cmd is AssignCmd) - { - var h = new Dictionary(); - var acmd = cmd as AssignCmd; - for (int j = 0; j < acmd.Lhss.Count; j++) - { - h.Add(acmd.Lhss[j].DeepAssignedVariable, acmd.Rhss[j]); - } - var s = Substituter.SubstitutionFromHashtable(h); - expr = Substituter.Apply(s, expr); - } - else if (cmd is HavocCmd) - { - var h = new Dictionary(); - var formals = new List(); - - var vc = new VariableCollector(); - vc.VisitExpr(expr); - - foreach (var ie in (cmd as HavocCmd).Vars) - { - if (!vc.usedVars.Contains(ie.Decl)) continue; - var f = new BoundVariable(Token.NoToken, new TypedIdent(Token.NoToken, - ie.Decl.Name + "_formal", ie.Decl.TypedIdent.Type)); - h.Add(ie.Decl, Expr.Ident(f)); - formals.Add(f); - } - if (!formals.Any()) - continue; - var s = Substituter.SubstitutionFromHashtable(h); - expr = Substituter.Apply(s, expr); - expr = new ExistsExpr(Token.NoToken, formals, expr); - } - else - { - throw new InvalidProgramForSecureVc(string.Format("Unhandled cmd: {0}", cmd)); - } - } - - // Implicitly close with havoc of all the locals and OutParams - - - - expr = Substituter.Apply(Substituter.SubstitutionFromHashtable(subst1), expr); - expr = Substituter.Apply(Substituter.SubstitutionFromHashtable(subst), - Substituter.SubstitutionFromHashtable(new Dictionary()), expr); - expr.Typecheck(new TypecheckingContext(null)); - return expr; - } - - // Generate all paths in the impl - IEnumerable> GetAllPaths(Implementation impl) - { - var stk = new Stack>(); - stk.Push(Tuple.Create(impl.Blocks[0], 0)); - - while (stk.Any()) - { - var tup = stk.Peek(); - if (tup.Item1.TransferCmd is ReturnCmd) - { - var ret = new List(); - var ls = stk.ToList(); - ls.Iter(t => ret.AddRange(t.Item1.Cmds)); - yield return ret; - - stk.Pop(); - continue; - } - - stk.Pop(); - - var gc = tup.Item1.TransferCmd as GotoCmd; - if (gc.labelTargets.Count <= tup.Item2) - continue; - - stk.Push(Tuple.Create(tup.Item1, tup.Item2 + 1)); - stk.Push(Tuple.Create(gc.labelTargets[tup.Item2], 0)); - } - yield break; - } - - bool isHidden(Variable v) - { - return QKeyValue.FindBoolAttribute(v.Attributes, "hidden"); - } - - bool isVisible(Variable v) - { - return !isHidden(v); - } - - public override Outcome VerifyImplementation(Implementation/*!*/ impl, VerifierCallback/*!*/ callback) - { - Debug.Assert(this.program == program); - - // Record current time - var startTime = DateTime.UtcNow; - - CommandLineOptions.Clo.ProverCCLimit = 1; - prover = ProverInterface.CreateProver(program, logFilePath, appendLogFile, CommandLineOptions.Clo.ProverKillTime); - - // Flush any axioms that came with the program before we start SI on this implementation - prover.AssertAxioms(); - - GenVc(impl, callback); - - prover.Check(); - var outcome = prover.CheckOutcomeCore(handler); - //var outcome = ProverInterface.Outcome.Valid; - - prover.Close(); - - - - //Console.WriteLine("Answer = {0}", outcome); - - return ProverInterfaceOutcomeToConditionGenerationOutcome(outcome); - } - } - -} // namespace VC +using System; +using System.Collections; +using System.Collections.Generic; +using System.Threading; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.IO; +using Microsoft.Boogie; +using Microsoft.Boogie.GraphUtil; +using System.Diagnostics.Contracts; +using Microsoft.Basetypes; +using Microsoft.Boogie.VCExprAST; + +namespace VC { + using Bpl = Microsoft.Boogie; + + public class StratifiedVC { + public StratifiedInliningInfo info; + public int id; + public List interfaceExprVars; + + // boolControlVC (block -> its bool variable) + public Dictionary blockToControlVar; + // While using labels (block -> its label) + public Dictionary block2label; + + public Dictionary> callSites; + public Dictionary> recordProcCallSites; + public VCExpr vcexpr; + + // Must-Reach Information + Dictionary mustReachVar; + List mustReachBindings; + + public StratifiedVC(StratifiedInliningInfo siInfo, HashSet procCalls) { + info = siInfo; + info.GenerateVC(); + var vcgen = info.vcgen; + var prover = vcgen.prover; + VCExpressionGenerator gen = prover.VCExprGen; + var bet = prover.Context.BoogieExprTranslator; + + vcexpr = info.vcexpr; + id = vcgen.CreateNewId(); + interfaceExprVars = new List(); + Dictionary substDict = new Dictionary(); + foreach (VCExprVar v in info.interfaceExprVars) { + VCExprVar newVar = vcgen.CreateNewVar(v.Type); + interfaceExprVars.Add(newVar); + substDict.Add(v, newVar); + } + foreach (VCExprVar v in info.privateExprVars) { + substDict.Add(v, vcgen.CreateNewVar(v.Type)); + } + if(info.controlFlowVariable != null) + substDict.Add(bet.LookupVariable(info.controlFlowVariable), gen.Integer(BigNum.FromInt(id))); + VCExprSubstitution subst = new VCExprSubstitution(substDict, new Dictionary()); + SubstitutingVCExprVisitor substVisitor = new SubstitutingVCExprVisitor(prover.VCExprGen); + vcexpr = substVisitor.Mutate(vcexpr, subst); + + // For BoolControlVC generation + if (info.blockToControlVar != null) + { + blockToControlVar = new Dictionary(); + foreach (var tup in info.blockToControlVar) + blockToControlVar.Add(tup.Key, substDict[tup.Value]); + } + + // labels + if (info.label2absy != null) + { + block2label = new Dictionary(); + vcexpr = RenameVCExprLabels.Apply(vcexpr, info.vcgen.prover.VCExprGen, info.label2absy, block2label); + } + + if(procCalls != null) + vcexpr = RemoveProcedureCalls.Apply(vcexpr, info.vcgen.prover.VCExprGen, procCalls); + + callSites = new Dictionary>(); + foreach (Block b in info.callSites.Keys) { + callSites[b] = new List(); + foreach (CallSite cs in info.callSites[b]) { + callSites[b].Add(new StratifiedCallSite(cs, substVisitor, subst)); + } + } + + recordProcCallSites = new Dictionary>(); + foreach (Block b in info.recordProcCallSites.Keys) { + recordProcCallSites[b] = new List(); + foreach (CallSite cs in info.recordProcCallSites[b]) { + recordProcCallSites[b].Add(new StratifiedCallSite(cs, substVisitor, subst)); + } + } + } + + public VCExpr MustReach(Block block) + { + Contract.Assert(!CommandLineOptions.Clo.UseLabels); + + // This information is computed lazily + if (mustReachBindings == null) + { + var vcgen = info.vcgen; + var gen = vcgen.prover.VCExprGen; + var impl = info.impl; + mustReachVar = new Dictionary(); + mustReachBindings = new List(); + foreach (Block b in impl.Blocks) + mustReachVar[b] = vcgen.CreateNewVar(Bpl.Type.Bool); + + var dag = new Graph(); + dag.AddSource(impl.Blocks[0]); + foreach (Block b in impl.Blocks) + { + var gtc = b.TransferCmd as GotoCmd; + if (gtc != null) + foreach (Block dest in gtc.labelTargets) + dag.AddEdge(dest, b); + } + IEnumerable sortedNodes = dag.TopologicalSort(); + + foreach (Block currBlock in dag.TopologicalSort()) + { + if (currBlock == impl.Blocks[0]) + { + mustReachBindings.Add(gen.LetBinding(mustReachVar[currBlock], VCExpressionGenerator.True)); + continue; + } + + VCExpr expr = VCExpressionGenerator.False; + foreach (var pred in dag.Successors(currBlock)) + { + VCExpr controlFlowFunctionAppl = gen.ControlFlowFunctionApplication(gen.Integer(BigNum.FromInt(id)), gen.Integer(BigNum.FromInt(pred.UniqueId))); + VCExpr controlTransferExpr = gen.Eq(controlFlowFunctionAppl, gen.Integer(BigNum.FromInt(currBlock.UniqueId))); + expr = gen.Or(expr, gen.And(mustReachVar[pred], controlTransferExpr)); + } + mustReachBindings.Add(gen.LetBinding(mustReachVar[currBlock], expr)); + } + } + + Contract.Assert(mustReachVar.ContainsKey(block)); + return info.vcgen.prover.VCExprGen.Let(mustReachBindings, mustReachVar[block]); + } + + public List CallSites { + get { + var ret = new List(); + foreach (var b in callSites.Keys) { + foreach (var cs in callSites[b]) { + ret.Add(cs); + } + } + return ret; + } + } + + public List RecordProcCallSites { + get { + var ret = new List(); + foreach (var b in recordProcCallSites.Keys) { + foreach (var cs in recordProcCallSites[b]) { + ret.Add(cs); + } + } + return ret; + } + } + + public override string ToString() + { + return info.impl.Name; + } + } + + // Rename all labels in a VC to (globally) fresh labels + class RenameVCExprLabels : MutatingVCExprVisitor + { + Dictionary label2absy; + Dictionary absy2newlabel; + static int counter = 11; + + RenameVCExprLabels(VCExpressionGenerator gen, Dictionary label2absy, Dictionary absy2newlabel) + : base(gen) + { + this.label2absy = label2absy; + this.absy2newlabel = absy2newlabel; + } + + public static VCExpr Apply(VCExpr expr, VCExpressionGenerator gen, Dictionary label2absy, Dictionary absy2newlabel) + { + return (new RenameVCExprLabels(gen, label2absy, absy2newlabel)).Mutate(expr, true); + } + + // Finds labels and changes them to a globally unique label: + protected override VCExpr/*!*/ UpdateModifiedNode(VCExprNAry/*!*/ originalNode, + List/*!*/ newSubExprs, + bool changed, + bool arg) + { + Contract.Ensures(Contract.Result() != null); + + VCExpr ret; + if (changed) + ret = Gen.Function(originalNode.Op, + newSubExprs, originalNode.TypeArguments); + else + ret = originalNode; + + VCExprLabelOp lop = originalNode.Op as VCExprLabelOp; + if (lop == null) return ret; + if (!(ret is VCExprNAry)) return ret; + VCExprNAry retnary = (VCExprNAry)ret; + + // remove the sign + var nosign = 0; + if (!Int32.TryParse(lop.label.Substring(1), out nosign)) + return ret; + + if (!label2absy.ContainsKey(nosign)) + return ret; + + string newLabel = "SI" + counter.ToString(); + counter++; + absy2newlabel[label2absy[nosign]] = newLabel; + + if (lop.pos) + { + return Gen.LabelPos(newLabel, retnary[0]); + } + else + { + return Gen.LabelNeg(newLabel, retnary[0]); + } + + } + } + + // Remove the uninterpreted function calls that substitute procedure calls + class RemoveProcedureCalls : MutatingVCExprVisitor + { + HashSet procNames; + + RemoveProcedureCalls(VCExpressionGenerator gen, HashSet procNames) + : base(gen) + { + this.procNames = procNames; + } + + public static VCExpr Apply(VCExpr expr, VCExpressionGenerator gen, HashSet procNames) + { + return (new RemoveProcedureCalls(gen, procNames)).Mutate(expr, true); + } + + // Finds labels and changes them to a globally unique label: + protected override VCExpr/*!*/ UpdateModifiedNode(VCExprNAry/*!*/ originalNode, + List/*!*/ newSubExprs, + bool changed, + bool arg) + { + //Contract.Ensures(Contract.Result() != null); + + VCExpr ret; + if (changed) + ret = Gen.Function(originalNode.Op, + newSubExprs, originalNode.TypeArguments); + else + ret = originalNode; + + if (!(ret is VCExprNAry)) return ret; + VCExprNAry retnary = (VCExprNAry)ret; + if (!(retnary.Op is VCExprBoogieFunctionOp)) + return ret; + + var fcall = (retnary.Op as VCExprBoogieFunctionOp).Func.Name; + if (procNames.Contains(fcall)) + return VCExpressionGenerator.True; + return ret; + } + } + + + public class CallSite { + public string calleeName; + public List interfaceExprs; + public Block block; + public int numInstr; // for TraceLocation + public VCExprVar callSiteVar; + public QKeyValue Attributes; // attributes on the call cmd + public CallSite(string callee, List interfaceExprs, VCExprVar callSiteVar, Block block, int numInstr, QKeyValue Attributes) + { + this.calleeName = callee; + this.interfaceExprs = interfaceExprs; + this.callSiteVar = callSiteVar; + this.block = block; + this.numInstr = numInstr; + this.Attributes = Attributes; + } + } + + public class StratifiedCallSite { + public CallSite callSite; + public List interfaceExprs; + public VCExpr callSiteExpr; + + public StratifiedCallSite(CallSite cs, SubstitutingVCExprVisitor substVisitor, VCExprSubstitution subst) { + callSite = cs; + interfaceExprs = new List(); + foreach (VCExpr v in cs.interfaceExprs) { + interfaceExprs.Add(substVisitor.Mutate(v, subst)); + } + if (callSite.callSiteVar != null) + callSiteExpr = substVisitor.Mutate(callSite.callSiteVar, subst); + } + + public VCExpr Attach(StratifiedVC svc) { + Contract.Assert(interfaceExprs.Count == svc.interfaceExprVars.Count); + StratifiedInliningInfo info = svc.info; + ProverInterface prover = info.vcgen.prover; + VCExpressionGenerator gen = prover.VCExprGen; + + Dictionary substDict = new Dictionary(); + for (int i = 0; i < svc.interfaceExprVars.Count; i++) { + VCExprVar v = svc.interfaceExprVars[i]; + substDict.Add(v, interfaceExprs[i]); + } + VCExprSubstitution subst = new VCExprSubstitution(substDict, new Dictionary()); + SubstitutingVCExprVisitor substVisitor = new SubstitutingVCExprVisitor(prover.VCExprGen); + svc.vcexpr = substVisitor.Mutate(svc.vcexpr, subst); + foreach (StratifiedCallSite scs in svc.CallSites) { + List newInterfaceExprs = new List(); + foreach (VCExpr expr in scs.interfaceExprs) { + newInterfaceExprs.Add(substVisitor.Mutate(expr, subst)); + } + scs.interfaceExprs = newInterfaceExprs; + } + foreach (StratifiedCallSite scs in svc.RecordProcCallSites) { + List newInterfaceExprs = new List(); + foreach (VCExpr expr in scs.interfaceExprs) { + newInterfaceExprs.Add(substVisitor.Mutate(expr, subst)); + } + scs.interfaceExprs = newInterfaceExprs; + } + //return gen.Implies(callSiteExpr, svc.vcexpr); + return svc.vcexpr; + } + + public override string ToString() + { + return callSite.calleeName; + } + } + + public class StratifiedInliningInfo { + public StratifiedVCGenBase vcgen; + public Implementation impl; + public Function function; + public Variable controlFlowVariable; + public Cmd exitAssertCmd; + public VCExpr vcexpr; + public List interfaceExprVars; + public List privateExprVars; + public Dictionary label2absy; + public ModelViewInfo mvInfo; + public Dictionary> callSites; + public Dictionary> recordProcCallSites; + public bool initialized { get; private set; } + // Instrumentation to apply after PassiveImpl, but before VCGen + Action PassiveImplInstrumentation; + + // boolControlVC (block -> its Bool variable) + public Dictionary blockToControlVar; + + public StratifiedInliningInfo(Implementation implementation, StratifiedVCGenBase stratifiedVcGen, Action PassiveImplInstrumentation) { + vcgen = stratifiedVcGen; + impl = implementation; + this.PassiveImplInstrumentation = PassiveImplInstrumentation; + + List functionInterfaceVars = new List(); + foreach (Variable v in vcgen.program.GlobalVariables) { + functionInterfaceVars.Add(new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "", v.TypedIdent.Type), true)); + } + foreach (Variable v in impl.InParams) { + functionInterfaceVars.Add(new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "", v.TypedIdent.Type), true)); + } + foreach (Variable v in impl.OutParams) { + functionInterfaceVars.Add(new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "", v.TypedIdent.Type), true)); + } + foreach (IdentifierExpr e in impl.Proc.Modifies) { + if (e.Decl == null) continue; + functionInterfaceVars.Add(new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "", e.Decl.TypedIdent.Type), true)); + } + Formal returnVar = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "", Bpl.Type.Bool), false); + function = new Function(Token.NoToken, impl.Name, functionInterfaceVars, returnVar); + vcgen.prover.Context.DeclareFunction(function, ""); + + List exprs = new List(); + foreach (Variable v in vcgen.program.GlobalVariables) { + Contract.Assert(v != null); + exprs.Add(new OldExpr(Token.NoToken, new IdentifierExpr(Token.NoToken, v))); + } + foreach (Variable v in impl.Proc.InParams) { + Contract.Assert(v != null); + exprs.Add(new IdentifierExpr(Token.NoToken, v)); + } + foreach (Variable v in impl.Proc.OutParams) { + Contract.Assert(v != null); + exprs.Add(new IdentifierExpr(Token.NoToken, v)); + } + foreach (IdentifierExpr ie in impl.Proc.Modifies) { + Contract.Assert(ie != null); + if (ie.Decl == null) + continue; + exprs.Add(ie); + } + Expr freePostExpr = new NAryExpr(Token.NoToken, new FunctionCall(function), exprs); + impl.Proc.Ensures.Add(new Ensures(Token.NoToken, true, freePostExpr, "", new QKeyValue(Token.NoToken, "si_fcall", new List(), null))); + + initialized = false; + } + + public void GenerateVCBoolControl() + { + Debug.Assert(!initialized); + Debug.Assert(CommandLineOptions.Clo.SIBoolControlVC); + + // fix names for exit variables + var outputVariables = new List(); + var assertConjuncts = new List(); + foreach (Variable v in impl.OutParams) + { + Constant c = new Constant(Token.NoToken, new TypedIdent(Token.NoToken, impl.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) + { + if (e.Decl == null) continue; + Variable v = e.Decl; + Constant c = new Constant(Token.NoToken, new TypedIdent(Token.NoToken, impl.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); + } + exitAssertCmd = new AssumeCmd(Token.NoToken, Expr.BinaryTreeAnd(assertConjuncts)); + (exitAssertCmd as AssumeCmd).Attributes = new QKeyValue(Token.NoToken, "exitAssert", new List(), null); + + // no need for label2absy + label2absy = new Dictionary(); + + // Passify + Program program = vcgen.program; + ProverInterface proverInterface = vcgen.prover; + vcgen.ConvertCFG2DAG(impl); + vcgen.PassifyImpl(impl, 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); + + // typecheck + var tc = new TypecheckingContext(null); + impl.Typecheck(tc); + + /////////////////// + // Generate the VC + /////////////////// + + // block -> bool variable + blockToControlVar = new Dictionary(); + foreach (var b in impl.Blocks) + blockToControlVar.Add(b, gen.Variable(b.Label + "_holds", Bpl.Type.Bool)); + + vcexpr = VCExpressionGenerator.True; + foreach (var b in impl.Blocks) + { + // conjoin all assume cmds + VCExpr c = VCExpressionGenerator.True; + foreach (var cmd in b.Cmds) + { + var acmd = cmd as AssumeCmd; + if (acmd == null) + { + Debug.Assert(cmd is AssertCmd && (cmd as AssertCmd).Expr is LiteralExpr && + ((cmd as AssertCmd).Expr as LiteralExpr).IsTrue); + continue; + } + var expr = translator.Translate(acmd.Expr); + // Label the assume if it is a procedure call + NAryExpr naryExpr = acmd.Expr as NAryExpr; + if (naryExpr != null && naryExpr.Fun is FunctionCall) + { + var id = acmd.UniqueId; + label2absy[id] = acmd; + expr = gen.LabelPos(cce.NonNull("si_fcall_" + id.ToString()), expr); + } + + c = gen.AndSimp(c, expr); + } + + // block implies a disjunction of successors + Debug.Assert(!(b.TransferCmd is ReturnExprCmd), "Not supported"); + var gc = b.TransferCmd as GotoCmd; + if (gc != null) + { + VCExpr succ = VCExpressionGenerator.False; + foreach (var sb in gc.labelTargets) + succ = gen.OrSimp(succ, blockToControlVar[sb]); + c = gen.AndSimp(c, succ); + } + else + { + // nothing to do + } + vcexpr = gen.AndSimp(vcexpr, gen.Eq(blockToControlVar[b], c)); + } + // assert start block + vcexpr = gen.AndSimp(vcexpr, blockToControlVar[impl.Blocks[0]]); + + //Console.WriteLine("VC of {0}: {1}", impl.Name, vcexpr); + // Collect other information + callSites = vcgen.CollectCallSites(impl); + recordProcCallSites = vcgen.CollectRecordProcedureCallSites(impl); + + // record interface variables + privateExprVars = new List(); + foreach (Variable v in impl.LocVars) + { + privateExprVars.Add(translator.LookupVariable(v)); + } + foreach (Variable v in impl.OutParams) + { + privateExprVars.Add(translator.LookupVariable(v)); + } + privateExprVars.AddRange(blockToControlVar.Values); + + interfaceExprVars = new List(); + foreach (Variable v in program.GlobalVariables) + { + interfaceExprVars.Add(translator.LookupVariable(v)); + } + foreach (Variable v in impl.InParams) + { + interfaceExprVars.Add(translator.LookupVariable(v)); + } + foreach (Variable v in outputVariables) + { + interfaceExprVars.Add(translator.LookupVariable(v)); + } + } + + public void GenerateVC() { + if (initialized) return; + if (CommandLineOptions.Clo.SIBoolControlVC) + { + GenerateVCBoolControl(); + initialized = true; + return; + } + List outputVariables = new List(); + List assertConjuncts = new List(); + foreach (Variable v in impl.OutParams) { + Constant c = new Constant(Token.NoToken, new TypedIdent(Token.NoToken, impl.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) { + if (e.Decl == null) continue; + Variable v = e.Decl; + Constant c = new Constant(Token.NoToken, new TypedIdent(Token.NoToken, impl.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); + } + exitAssertCmd = new AssertCmd(Token.NoToken, Expr.Not(Expr.BinaryTreeAnd(assertConjuncts))); + + Program program = vcgen.program; + ProverInterface proverInterface = vcgen.prover; + vcgen.ConvertCFG2DAG(impl); + vcgen.PassifyImpl(impl, out mvInfo); + + if (PassiveImplInstrumentation != null) + PassiveImplInstrumentation(impl); + + VCExpressionGenerator gen = proverInterface.VCExprGen; + var exprGen = proverInterface.Context.ExprGen; + var translator = proverInterface.Context.BoogieExprTranslator; + + VCExpr controlFlowVariableExpr = null; + if (!CommandLineOptions.Clo.UseLabels) { + controlFlowVariable = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, "@cfc", Microsoft.Boogie.Type.Int)); + controlFlowVariableExpr = translator.LookupVariable(controlFlowVariable); + } + + vcgen.InstrumentCallSites(impl); + + label2absy = new Dictionary(); + VCGen.CodeExprConversionClosure cc = new VCGen.CodeExprConversionClosure(label2absy, proverInterface.Context); + translator.SetCodeExprConverter(cc.CodeExprToVerificationCondition); + vcexpr = gen.Not(vcgen.GenerateVCAux(impl, controlFlowVariableExpr, label2absy, proverInterface.Context)); + + if (controlFlowVariableExpr != null) + { + VCExpr controlFlowFunctionAppl = exprGen.ControlFlowFunctionApplication(controlFlowVariableExpr, exprGen.Integer(BigNum.ZERO)); + VCExpr eqExpr = exprGen.Eq(controlFlowFunctionAppl, exprGen.Integer(BigNum.FromInt(impl.Blocks[0].UniqueId))); + vcexpr = exprGen.And(eqExpr, vcexpr); + } + + callSites = vcgen.CollectCallSites(impl); + recordProcCallSites = vcgen.CollectRecordProcedureCallSites(impl); + + privateExprVars = new List(); + foreach (Variable v in impl.LocVars) { + privateExprVars.Add(translator.LookupVariable(v)); + } + foreach (Variable v in impl.OutParams) { + privateExprVars.Add(translator.LookupVariable(v)); + } + + interfaceExprVars = new List(); + foreach (Variable v in program.GlobalVariables) { + interfaceExprVars.Add(translator.LookupVariable(v)); + } + foreach (Variable v in impl.InParams) { + interfaceExprVars.Add(translator.LookupVariable(v)); + } + foreach (Variable v in outputVariables) { + interfaceExprVars.Add(translator.LookupVariable(v)); + } + + initialized = true; + } + } + + public abstract class StratifiedVCGenBase : VCGen { + public readonly static string recordProcName = "boogie_si_record"; + public Dictionary implName2StratifiedInliningInfo; + public ProverInterface prover; + + public StratifiedVCGenBase(Program program, string/*?*/ logFilePath, bool appendLogFile, List checkers, Action PassiveImplInstrumentation) + : base(program, logFilePath, appendLogFile, checkers) { + implName2StratifiedInliningInfo = new Dictionary(); + prover = ProverInterface.CreateProver(program, logFilePath, appendLogFile, CommandLineOptions.Clo.ProverKillTime); + foreach (var impl in program.Implementations) { + implName2StratifiedInliningInfo[impl.Name] = new StratifiedInliningInfo(impl, this, PassiveImplInstrumentation); + } + GenerateRecordFunctions(); + } + + private void GenerateRecordFunctions() { + foreach (var proc in program.Procedures) { + if (!proc.Name.StartsWith(recordProcName)) continue; + Contract.Assert(proc.InParams.Count == 1); + + // Make a new function + TypedIdent ti = new TypedIdent(Token.NoToken, "", Bpl.Type.Bool); + Contract.Assert(ti != null); + Formal returnVar = new Formal(Token.NoToken, ti, false); + Contract.Assert(returnVar != null); + + // Get record type + var argtype = proc.InParams[0].TypedIdent.Type; + + var ins = new List(); + ins.Add(new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "x", argtype), true)); + + var recordFunc = new Function(Token.NoToken, proc.Name, ins, returnVar); + prover.Context.DeclareFunction(recordFunc, ""); + + var exprs = new List(); + exprs.Add(new IdentifierExpr(Token.NoToken, proc.InParams[0])); + + Expr freePostExpr = new NAryExpr(Token.NoToken, new FunctionCall(recordFunc), exprs); + proc.Ensures.Add(new Ensures(true, freePostExpr)); + } + } + + public override void Close() { + prover.Close(); + base.Close(); + } + + public void InstrumentCallSites(Implementation implementation) { + var callSiteId = 0; + foreach (Block block in implementation.Blocks) { + List newCmds = new List(); + for (int i = 0; i < block.Cmds.Count; i++) { + Cmd cmd = block.Cmds[i]; + newCmds.Add(cmd); + AssumeCmd assumeCmd = cmd as AssumeCmd; + if (assumeCmd == null) continue; + NAryExpr naryExpr = assumeCmd.Expr as NAryExpr; + if (naryExpr == null) continue; + if (!implName2StratifiedInliningInfo.ContainsKey(naryExpr.Fun.FunctionName)) continue; + Variable callSiteVar = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, "SICS" + callSiteId, Microsoft.Boogie.Type.Bool)); + implementation.LocVars.Add(callSiteVar); + newCmds.Add(new AssumeCmd(Token.NoToken, new IdentifierExpr(Token.NoToken, callSiteVar))); + callSiteId++; + } + block.Cmds = newCmds; + } + } + + public Dictionary> CollectCallSites(Implementation implementation) { + var callSites = new Dictionary>(); + foreach (Block block in implementation.Blocks) { + for (int i = 0; i < block.Cmds.Count; i++) { + Cmd cmd = block.Cmds[i]; + AssumeCmd assumeCmd = cmd as AssumeCmd; + if (assumeCmd == null) continue; + NAryExpr naryExpr = assumeCmd.Expr as NAryExpr; + if (naryExpr == null) continue; + if (!implName2StratifiedInliningInfo.ContainsKey(naryExpr.Fun.FunctionName)) continue; + List interfaceExprs = new List(); + foreach (Expr e in naryExpr.Args) { + interfaceExprs.Add(prover.Context.BoogieExprTranslator.Translate(e)); + } + int instr = i; + i++; + AssumeCmd callSiteAssumeCmd = (AssumeCmd)block.Cmds[i]; + IdentifierExpr iexpr = (IdentifierExpr) callSiteAssumeCmd.Expr; + CallSite cs = new CallSite(naryExpr.Fun.FunctionName, interfaceExprs, prover.Context.BoogieExprTranslator.LookupVariable(iexpr.Decl), block, instr, assumeCmd.Attributes); + if (!callSites.ContainsKey(block)) + callSites[block] = new List(); + callSites[block].Add(cs); + } + } + return callSites; + } + + public Dictionary> CollectRecordProcedureCallSites(Implementation implementation) { + var callSites = new Dictionary>(); + foreach (Block block in implementation.Blocks) { + for (int i = 0; i < block.Cmds.Count; i++) { + AssumeCmd assumeCmd = block.Cmds[i] as AssumeCmd; + if (assumeCmd == null) continue; + NAryExpr naryExpr = assumeCmd.Expr as NAryExpr; + if (naryExpr == null) continue; + if (!naryExpr.Fun.FunctionName.StartsWith(recordProcName)) continue; + List interfaceExprs = new List(); + foreach (Expr e in naryExpr.Args) { + interfaceExprs.Add(prover.Context.BoogieExprTranslator.Translate(e)); + } + CallSite cs = new CallSite(naryExpr.Fun.FunctionName, interfaceExprs, null, block, i, assumeCmd.Attributes); + if (!callSites.ContainsKey(block)) + callSites[block] = new List(); + callSites[block].Add(cs); + } + } + return callSites; + } + + private int macroCountForStratifiedInlining = 0; + public Macro CreateNewMacro() { + string newName = "SIMacro@" + macroCountForStratifiedInlining.ToString(); + macroCountForStratifiedInlining++; + return new Macro(Token.NoToken, newName, new List(), new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "", Microsoft.Boogie.Type.Bool), false)); + } + private int varCountForStratifiedInlining = 0; + public VCExprVar CreateNewVar(Microsoft.Boogie.Type type) { + string newName = "SIV@" + varCountForStratifiedInlining.ToString(); + varCountForStratifiedInlining++; + Constant newVar = new Constant(Token.NoToken, new TypedIdent(Token.NoToken, newName, type)); + prover.Context.DeclareConstant(newVar, false, null); + return prover.VCExprGen.Variable(newVar.Name, type); + } + private int idCountForStratifiedInlining = 0; + public int CreateNewId() { + return idCountForStratifiedInlining++; + } + + // Used inside PassifyImpl + protected override void addExitAssert(string implName, Block exitBlock) { + if (implName2StratifiedInliningInfo != null && implName2StratifiedInliningInfo.ContainsKey(implName)) { + var exitAssertCmd = implName2StratifiedInliningInfo[implName].exitAssertCmd; + if(exitAssertCmd != null) exitBlock.Cmds.Add(exitAssertCmd); + } + } + + public override Counterexample extractLoopTrace(Counterexample cex, string mainProcName, Program program, Dictionary> extractLoopMappingInfo) { + // Construct the set of inlined procs in the original program + var inlinedProcs = new HashSet(); + foreach (var decl in program.TopLevelDeclarations) { + // Implementations + if (decl is Implementation) { + var impl = decl as Implementation; + if (!(impl.Proc is LoopProcedure)) { + inlinedProcs.Add(impl.Name); + } + } + + // And recording procedures + if (decl is Procedure) { + var proc = decl as Procedure; + if (proc.Name.StartsWith(recordProcName)) { + Debug.Assert(!(decl is LoopProcedure)); + inlinedProcs.Add(proc.Name); + } + } + } + + return extractLoopTraceRec( + new CalleeCounterexampleInfo(cex, new List()), + mainProcName, inlinedProcs, extractLoopMappingInfo).counterexample; + } + + protected override bool elIsLoop(string procname) { + StratifiedInliningInfo info = null; + if (implName2StratifiedInliningInfo.ContainsKey(procname)) { + info = implName2StratifiedInliningInfo[procname]; + } + + if (info == null) return false; + + var lp = info.impl.Proc as LoopProcedure; + + if (lp == null) return false; + return true; + } + + public abstract Outcome FindLeastToVerify(Implementation impl, ref HashSet allBoolVars); + } + + public class StratifiedVCGen : StratifiedVCGenBase { + public bool PersistCallTree; + public static HashSet callTree = null; + public int numInlined = 0; + public int vcsize = 0; + private HashSet procsThatReachedRecBound; + private Dictionary extraRecBound; + + public StratifiedVCGen(bool usePrevCallTree, HashSet prevCallTree, + Program program, string/*?*/ logFilePath, bool appendLogFile, List checkers) + : this(program, logFilePath, appendLogFile, checkers) + { + if (usePrevCallTree) { + callTree = prevCallTree; + PersistCallTree = true; + } + else { + PersistCallTree = false; + } + } + + public StratifiedVCGen(Program program, string/*?*/ logFilePath, bool appendLogFile, List checkers) + : base(program, logFilePath, appendLogFile, checkers, null) { + PersistCallTree = false; + procsThatReachedRecBound = new HashSet(); + + extraRecBound = new Dictionary(); + program.TopLevelDeclarations.OfType() + .Iter(impl => + { + var b = QKeyValue.FindIntAttribute(impl.Attributes, "SIextraRecBound", -1); + if (b != -1) extraRecBound.Add(impl.Name, b); + }); + } + + // Extra rec bound for procedures + public int GetExtraRecBound(string procName) { + if (!extraRecBound.ContainsKey(procName)) + return 0; + else return extraRecBound[procName]; + } + + public class ApiChecker { + public ProverInterface prover; + public ProverInterface.ErrorHandler reporter; + + public ApiChecker(ProverInterface prover, ProverInterface.ErrorHandler reporter) { + this.reporter = reporter; + this.prover = prover; + } + + private Outcome CheckVC() { + prover.Check(); + ProverInterface.Outcome outcome = prover.CheckOutcomeCore(reporter); + + return ConditionGeneration.ProverInterfaceOutcomeToConditionGenerationOutcome(outcome); + } + + public Outcome CheckAssumptions(List assumptions) { + if (assumptions.Count == 0) { + return CheckVC(); + } + + prover.Push(); + foreach (var a in assumptions) { + prover.Assert(a, true); + } + Outcome ret = CheckVC(); + prover.Pop(); + return ret; + } + + public Outcome CheckAssumptions(List hardAssumptions, List softAssumptions) { + List unsatisfiedSoftAssumptions; + ProverInterface.Outcome outcome = prover.CheckAssumptions(hardAssumptions, softAssumptions, out unsatisfiedSoftAssumptions, reporter); + return ConditionGeneration.ProverInterfaceOutcomeToConditionGenerationOutcome(outcome); + } + + public Outcome CheckAssumptions(List assumptions, out List unsatCore) { + ProverInterface.Outcome outcome = prover.CheckAssumptions(assumptions, out unsatCore, reporter); + return ConditionGeneration.ProverInterfaceOutcomeToConditionGenerationOutcome(outcome); + } + } + + // Store important information related to a single VerifyImplementation query + public class VerificationState { + // The call tree + public FCallHandler calls; + public ApiChecker checker; + // For statistics + public int vcSize; + public int expansionCount; + + public VerificationState(VCExpr vcMain, FCallHandler calls, ProverInterface prover, ProverInterface.ErrorHandler reporter) { + prover.Assert(vcMain, true); + this.calls = calls; + this.checker = new ApiChecker(prover, reporter); + vcSize = 0; + expansionCount = 0; + } + } + + class FindLeastOORException : Exception + { + public Outcome outcome; + + public FindLeastOORException(string msg, Outcome outcome) + : base(msg) + { + this.outcome = outcome; + } + } + + public override Outcome FindLeastToVerify(Implementation impl, ref HashSet allBoolVars) { + Contract.EnsuresOnThrow(true); + + // Record current time + var startTime = DateTime.UtcNow; + + // No Max: avoids theorem prover restarts + CommandLineOptions.Clo.MaxProverMemory = 0; + + // Initialize cache + satQueryCache = new Dictionary>>(); + unsatQueryCache = new Dictionary>>(); + + Contract.Assert(implName2StratifiedInliningInfo != null); + + // Build VCs for all procedures + implName2StratifiedInliningInfo.Values + .Iter(info => info.GenerateVC()); + + // Get the VC of the current procedure + VCExpr vcMain = implName2StratifiedInliningInfo[impl.Name].vcexpr; + Dictionary mainLabel2absy = implName2StratifiedInliningInfo[impl.Name].label2absy; + + // Find all procedure calls in vc and put labels on them + FCallHandler calls = new FCallHandler(prover.VCExprGen, implName2StratifiedInliningInfo, impl.Name, mainLabel2absy); + calls.setCurrProcAsMain(); + vcMain = calls.Mutate(vcMain, true); + + try + { + + // Put all of the necessary state into one object + var vState = new VerificationState(vcMain, calls, prover, new EmptyErrorHandler()); + + // We'll restore the original state of the theorem prover at the end + // of this procedure + vState.checker.prover.Push(); + + // Do eager inlining + while (calls.currCandidates.Count > 0) + { + List toExpand = new List(); + + foreach (int id in calls.currCandidates) + { + Debug.Assert(calls.getRecursionBound(id) <= 1, "Recursion not supported"); + toExpand.Add(id); + } + DoExpansion(toExpand, vState); + } + + // Find all the boolean constants + var allConsts = new HashSet(); + foreach (var constant in program.Constants) + { + if (!allBoolVars.Contains(constant.Name)) continue; + var v = prover.Context.BoogieExprTranslator.LookupVariable(constant); + allConsts.Add(v); + } + + // Now, lets start the algo + var min = refinementLoop(vState.checker, new HashSet(), allConsts, allConsts); + + var ret = new HashSet(); + foreach (var v in min) + { + //Console.WriteLine(v.Name); + ret.Add(v.Name); + } + allBoolVars = ret; + + vState.checker.prover.Pop(); + + return Outcome.Correct; + } + catch (FindLeastOORException e) + { + Console.WriteLine("Exception in FindLeastToVerify: {0}, {1}", e.Message, e.outcome); + return e.outcome; + } + } + + private HashSet refinementLoop(ApiChecker apiChecker, HashSet trackedVars, HashSet trackedVarsUpperBound, HashSet allVars) { + Debug.Assert(trackedVars.IsSubsetOf(trackedVarsUpperBound)); + + // If we already know the fate of all vars, then we're done. + if (trackedVars.Count == trackedVarsUpperBound.Count) + return new HashSet(trackedVars); + + // See if we already have enough variables tracked + var success = refinementLoopCheckPath(apiChecker, trackedVars, allVars); + if (success) { + // We have enough + return new HashSet(trackedVars); + } + + // If all that remains is 1 variable, then we know that we must track it + if (trackedVars.Count + 1 == trackedVarsUpperBound.Count) + return new HashSet(trackedVarsUpperBound); + + // Partition the remaining set of variables + HashSet part1, part2; + var temp = new HashSet(trackedVarsUpperBound); + temp.ExceptWith(trackedVars); + Partition(temp, out part1, out part2); + + // First half + var fh = new HashSet(trackedVars); fh.UnionWith(part2); + var s1 = refinementLoop(apiChecker, fh, trackedVarsUpperBound, allVars); + + var a = new HashSet(part1); a.IntersectWith(s1); + var b = new HashSet(part1); b.ExceptWith(s1); + var c = new HashSet(trackedVarsUpperBound); c.ExceptWith(b); + a.UnionWith(trackedVars); + + // Second half + return refinementLoop(apiChecker, a, c, allVars); + } + + Dictionary>> satQueryCache; + Dictionary>> unsatQueryCache; + + private bool refinementLoopCheckPath(ApiChecker apiChecker, HashSet varsToSet, HashSet allVars) { + var assumptions = new List(); + var prover = apiChecker.prover; + var query = new HashSet(); + varsToSet.Iter(v => query.Add(v.Name)); + + if (checkCache(query, unsatQueryCache)) { + prover.LogComment("FindLeast: Query Cache Hit"); + return true; + } + if (checkCache(query, satQueryCache)) { + prover.LogComment("FindLeast: Query Cache Hit"); + return false; + } + + prover.LogComment("FindLeast: Query Begin"); + + foreach (var c in allVars) { + if (varsToSet.Contains(c)) { + assumptions.Add(c); + } + else { + assumptions.Add(prover.VCExprGen.Not(c)); + } + } + + var o = apiChecker.CheckAssumptions(assumptions); + if (o != Outcome.Correct && o != Outcome.Errors) + { + throw new FindLeastOORException("OOR", o); + } + //Console.WriteLine("Result = " + o.ToString()); + prover.LogComment("FindLeast: Query End"); + + if (o == Outcome.Correct) { + insertCache(query, unsatQueryCache); + return true; + } + + insertCache(query, satQueryCache); + return false; + } + + private bool checkCache(HashSet q, Dictionary>> cache) { + if (!cache.ContainsKey(q.Count)) return false; + foreach (var s in cache[q.Count]) { + if (q.SetEquals(s)) return true; + } + return false; + } + + private void insertCache(HashSet q, Dictionary>> cache) { + if (!cache.ContainsKey(q.Count)) { + cache.Add(q.Count, new List>()); + } + cache[q.Count].Add(q); + } + + public static void Partition(HashSet values, out HashSet part1, out HashSet part2) { + part1 = new HashSet(); + part2 = new HashSet(); + var size = values.Count; + var crossed = false; + var curr = 0; + foreach (var s in values) { + if (crossed) part2.Add(s); + else part1.Add(s); + curr++; + if (!crossed && curr >= size / 2) crossed = true; + } + } + + public override Outcome VerifyImplementation(Implementation/*!*/ impl, VerifierCallback/*!*/ callback) { + Debug.Assert(QKeyValue.FindBoolAttribute(impl.Attributes, "entrypoint")); + Debug.Assert(this.program == program); + + // Record current time + var startTime = DateTime.UtcNow; + + // Flush any axioms that came with the program before we start SI on this implementation + prover.AssertAxioms(); + + // Run live variable analysis + if (CommandLineOptions.Clo.LiveVariableAnalysis == 2) { + Microsoft.Boogie.InterProcGenKill.ComputeLiveVars(impl, program); + } + + // Get the VC of the current procedure + StratifiedInliningInfo info = implName2StratifiedInliningInfo[impl.Name]; + info.GenerateVC(); + VCExpr vc = info.vcexpr; + Dictionary mainLabel2absy = info.label2absy; + var reporter = new StratifiedInliningErrorReporter(implName2StratifiedInliningInfo, prover, callback, info); + + // Find all procedure calls in vc and put labels on them + FCallHandler calls = new FCallHandler(prover.VCExprGen, implName2StratifiedInliningInfo, impl.Name, mainLabel2absy); + calls.setCurrProcAsMain(); + vc = calls.Mutate(vc, true); + reporter.SetCandidateHandler(calls); + calls.id2VC.Add(0, vc); + calls.extraRecursion = extraRecBound; + if (CommandLineOptions.Clo.SIBoolControlVC) + { + calls.candiate2block2controlVar.Add(0, new Dictionary()); + implName2StratifiedInliningInfo[impl.Name].blockToControlVar.Iter(tup => + calls.candiate2block2controlVar[0].Add(tup.Key, tup.Value)); + } + + // We'll restore the original state of the theorem prover at the end + // of this procedure + prover.Push(); + + // Put all of the necessary state into one object + var vState = new VerificationState(vc, calls, prover, reporter); + vState.vcSize += SizeComputingVisitor.ComputeSize(vc); + + Outcome ret = Outcome.ReachedBound; + + #region eager inlining + for (int i = 1; i < CommandLineOptions.Clo.StratifiedInlining && calls.currCandidates.Count > 0; i++) { + List toExpand = new List(); + + foreach (int id in calls.currCandidates) { + if (calls.getRecursionBound(id) <= CommandLineOptions.Clo.RecursionBound) { + toExpand.Add(id); + } + } + DoExpansion(toExpand, vState); + } + #endregion + + #region Repopulate call tree, if there is one + if (PersistCallTree && callTree != null) { + bool expand = true; + while (expand) { + List toExpand = new List(); + foreach (int id in calls.currCandidates) { + if (callTree.Contains(calls.getPersistentId(id))) { + toExpand.Add(id); + } + } + if (toExpand.Count == 0) expand = false; + else { + DoExpansion(toExpand, vState); + } + } + } + #endregion + + if (CommandLineOptions.Clo.StratifiedInliningVerbose > 1) { + Console.WriteLine(">> SI: Size of VC after eager inlining: {0}", vState.vcSize); + } + + // Under-approx query is only needed if something was inlined since + // the last time an under-approx query was made + // TODO: introduce this + // bool underApproxNeeded = true; + + // The recursion bound for stratified search + int bound = CommandLineOptions.Clo.NonUniformUnfolding ? CommandLineOptions.Clo.RecursionBound : 1; + + int done = 0; + + int iters = 0; + + // for blocking candidates (and focusing on a counterexample) + var block = new HashSet(); + + // Process tasks while not done. We're done when: + // case 1: (correct) We didn't find a bug (either an over-approx query was valid + // or we reached the recursion bound) and the task is "step" + // case 2: (bug) We find a bug + // case 3: (internal error) The theorem prover TimesOut of runs OutOfMemory + while (true) + { + // Check timeout + if (CommandLineOptions.Clo.ProverKillTime != -1) + { + if ((DateTime.UtcNow - startTime).TotalSeconds > CommandLineOptions.Clo.ProverKillTime) + { + ret = Outcome.TimedOut; + break; + } + } + + if (done > 0) + { + break; + } + + // Stratified Step + ret = stratifiedStep(bound, vState, block); + iters++; + + // Sorry, out of luck (time/memory) + if (ret == Outcome.Inconclusive || ret == Outcome.OutOfMemory || ret == Outcome.TimedOut) + { + done = 3; + continue; + } + + if (ret == Outcome.Errors && reporter.underapproximationMode) + { + // Found a bug + done = 2; + } + else if (ret == Outcome.Correct) + { + if (block.Count == 0) + { + // Correct + done = 1; + } + else + { + // reset blocked and continue loop + block.Clear(); + } + } + else if (ret == Outcome.ReachedBound) + { + if (block.Count == 0) + { + if (CommandLineOptions.Clo.StratifiedInliningVerbose > 0) + Console.WriteLine(">> SI: Exhausted Bound {0}", bound); + + // Increment bound + bound++; + + if (bound > CommandLineOptions.Clo.RecursionBound) + { + // Correct under bound + done = 1; + } + } + else + { + // reset blocked and continue loop + block.Clear(); + } + } + else + { + // Do inlining + Debug.Assert(ret == Outcome.Errors && !reporter.underapproximationMode); + Contract.Assert(reporter.candidatesToExpand.Count != 0); + + #region expand call tree + if (CommandLineOptions.Clo.StratifiedInliningVerbose > 1) + { + Console.Write(">> SI Inlining: "); + reporter.candidatesToExpand + .Select(c => calls.getProc(c)) + .Iter(c => Console.Write("{0} ", c)); + + Console.WriteLine(); + } + + // Expand and try again + vState.checker.prover.LogComment(";;;;;;;;;;;; Expansion begin ;;;;;;;;;;"); + DoExpansion(reporter.candidatesToExpand, vState); + vState.checker.prover.LogComment(";;;;;;;;;;;; Expansion end ;;;;;;;;;;"); + + #endregion + } + } + + // Pop off everything that we pushed so that there are no side effects from + // this call to VerifyImplementation + vState.checker.prover.Pop(); + + if (CommandLineOptions.Clo.StratifiedInliningVerbose > 1) { + Console.WriteLine(">> SI: Expansions performed: {0}", vState.expansionCount); + Console.WriteLine(">> SI: Candidates left: {0}", calls.currCandidates.Count); + Console.WriteLine(">> SI: VC Size: {0}", vState.vcSize); + } + + vcsize = vState.vcSize; + numInlined = (calls.candidateParent.Keys.Count + 1) - (calls.currCandidates.Count); + + var rbound = "Procs that reached bound: "; + foreach (var s in procsThatReachedRecBound) rbound += " " + s; + if (ret == Outcome.ReachedBound) Helpers.ExtraTraceInformation(rbound); + if (CommandLineOptions.Clo.StackDepthBound > 0 && ret == Outcome.Correct) ret = Outcome.ReachedBound; + + // Store current call tree + if (PersistCallTree && (ret == Outcome.Correct || ret == Outcome.Errors || ret == Outcome.ReachedBound)) { + callTree = new HashSet(); + //var persistentNodes = new HashSet(calls.candidateParent.Values); + var persistentNodes = new HashSet(calls.candidateParent.Keys); + persistentNodes.Add(0); + persistentNodes.ExceptWith(calls.currCandidates); + + foreach (var id in persistentNodes) { + var pid = calls.getPersistentId(id); + Debug.Assert(!callTree.Contains(pid)); + callTree.Add(pid); + } + } + return ret; + } + + // A step of the stratified inlining algorithm: both under-approx and over-approx queries + private Outcome stratifiedStep(int bound, VerificationState vState, HashSet block) { + var calls = vState.calls; + var checker = vState.checker; + var prover = checker.prover; + var reporter = checker.reporter as StratifiedInliningErrorReporter; + + reporter.underapproximationMode = true; + prover.LogComment(";;;;;;;;;;;; Underapprox mode begin ;;;;;;;;;;"); + List assumptions = new List(); + + foreach (int id in calls.currCandidates) { + assumptions.Add(calls.getFalseExpr(id)); + } + Outcome ret = checker.CheckAssumptions(assumptions); + prover.LogComment(";;;;;;;;;;;; Underapprox mode end ;;;;;;;;;;"); + + if (ret != Outcome.Correct) { + // Either the query returned an error or it ran out of memory or time. + // In all cases, we are done. + return ret; + } + + if (calls.currCandidates.Count == 0) { + // If we didn't underapproximate, then we're done + return ret; + } + + prover.LogComment(";;;;;;;;;;;; Overapprox mode begin ;;;;;;;;;;"); + + // Over-approx query + reporter.underapproximationMode = false; + + // Push "true" for all, except: + // push "false" for all candidates that have reached + // the recursion bounds + + bool allTrue = true; + bool allFalse = true; + List softAssumptions = new List(); + + assumptions = new List(); + procsThatReachedRecBound.Clear(); + + foreach (int id in calls.currCandidates) { + + int idBound = calls.getRecursionBound(id); + int sd = calls.getStackDepth(id); + if (idBound <= bound && (CommandLineOptions.Clo.StackDepthBound == 0 || sd <= CommandLineOptions.Clo.StackDepthBound)) { + if (idBound > 1) + softAssumptions.Add(calls.getFalseExpr(id)); + + if (block.Contains(id)) { + assumptions.Add(calls.getFalseExpr(id)); + allTrue = false; + } + else { + allFalse = false; + } + } + else { + procsThatReachedRecBound.Add(calls.getProc(id)); + assumptions.Add(calls.getFalseExpr(id)); + allTrue = false; + } + } + + if (allFalse) { + // If we made all candidates false, then this is the same + // as the underapprox query. We already know the answer. + ret = Outcome.Correct; + } + else { + ret = CommandLineOptions.Clo.NonUniformUnfolding + ? checker.CheckAssumptions(assumptions, softAssumptions) + : checker.CheckAssumptions(assumptions); + } + + if (ret != Outcome.Correct && ret != Outcome.Errors) { + // The query ran out of memory or time, that's it, + // we cannot do better. Give up! + return ret; + } + + if (ret == Outcome.Correct) { + // If nothing was made false, then the program is correct + if (allTrue) { + return ret; + } + + // Nothing more can be done with current recursion bound. + return Outcome.ReachedBound; + } + + Contract.Assert(ret == Outcome.Errors); + + prover.LogComment(";;;;;;;;;;;; Overapprox mode end ;;;;;;;;;;"); + + return ret; + } + + // A counter for adding new variables + static int newVarCnt = 0; + + // Does on-demand inlining -- pushes procedure bodies on the theorem prover stack. + private void DoExpansion(List/*!*/ candidates, VerificationState vState) { + Contract.Requires(candidates != null); + Contract.Requires(vState.calls != null); + Contract.Requires(vState.checker.prover != null); + Contract.EnsuresOnThrow(true); + + vState.expansionCount += candidates.Count; + + var prover = vState.checker.prover; + var calls = vState.calls; + + VCExpr exprToPush = VCExpressionGenerator.True; + Contract.Assert(exprToPush != null); + foreach (int id in candidates) { + VCExprNAry expr = calls.id2Candidate[id]; + Contract.Assert(expr != null); + string procName = cce.NonNull(expr.Op as VCExprBoogieFunctionOp).Func.Name; + if (!implName2StratifiedInliningInfo.ContainsKey(procName)) continue; + + StratifiedInliningInfo info = implName2StratifiedInliningInfo[procName]; + info.GenerateVC(); + //Console.WriteLine("Inlining {0}", procName); + VCExpr expansion = cce.NonNull(info.vcexpr); + + // Instantiate the "forall" variables + Dictionary substForallDict = new Dictionary(); + Contract.Assert(info.interfaceExprVars.Count == expr.Length); + for (int i = 0; i < info.interfaceExprVars.Count; i++) { + substForallDict.Add(info.interfaceExprVars[i], expr[i]); + } + VCExprSubstitution substForall = new VCExprSubstitution(substForallDict, new Dictionary()); + + SubstitutingVCExprVisitor subst = new SubstitutingVCExprVisitor(prover.VCExprGen); + Contract.Assert(subst != null); + expansion = subst.Mutate(expansion, substForall); + + // Instantiate and declare the "exists" variables + Dictionary substExistsDict = new Dictionary(); + foreach (VCExprVar v in info.privateExprVars) { + Contract.Assert(v != null); + string newName = v.Name + "_si_" + newVarCnt.ToString(); + newVarCnt++; + prover.Context.DeclareConstant(new Constant(Token.NoToken, new TypedIdent(Token.NoToken, newName, v.Type)), false, null); + substExistsDict.Add(v, prover.VCExprGen.Variable(newName, v.Type)); + } + if (CommandLineOptions.Clo.SIBoolControlVC) + { + // record the mapping for control booleans (for tracing the path later) + calls.candiate2block2controlVar[id] = new Dictionary(); + foreach (var tup in info.blockToControlVar) + { + calls.candiate2block2controlVar[id].Add(tup.Key, + substExistsDict[tup.Value]); + } + } + if (CommandLineOptions.Clo.ModelViewFile != null) { + SaveSubstitution(vState, id, substForallDict, substExistsDict); + } + VCExprSubstitution substExists = new VCExprSubstitution(substExistsDict, new Dictionary()); + + subst = new SubstitutingVCExprVisitor(prover.VCExprGen); + expansion = subst.Mutate(expansion, substExists); + + if (!calls.currCandidates.Contains(id)) { + Console.WriteLine("Don't know what we just expanded"); + } + + calls.currCandidates.Remove(id); + + // Record the new set of candidates and rename absy labels + calls.currInlineCount = id; + calls.setCurrProc(procName); + expansion = calls.Mutate(expansion, true); + + //expansion = checker.VCExprGen.Eq(calls.id2ControlVar[id], expansion); + expansion = prover.VCExprGen.Implies(calls.id2ControlVar[id], expansion); + calls.id2VC.Add(id, expansion); + + exprToPush = prover.VCExprGen.And(exprToPush, expansion); + } + vState.checker.prover.Assert(exprToPush, true); + vState.vcSize += SizeComputingVisitor.ComputeSize(exprToPush); + } + + private void SaveSubstitution(VerificationState vState, int id, + Dictionary substForallDict, Dictionary substExistsDict) { + var prover = vState.checker.prover; + var calls = vState.calls; + Boogie2VCExprTranslator translator = prover.Context.BoogieExprTranslator; + VCExprVar mvStateConstant = translator.LookupVariable(ModelViewInfo.MVState_ConstantDef); + substExistsDict.Add(mvStateConstant, prover.VCExprGen.Integer(BigNum.FromInt(id))); + Dictionary mapping = new Dictionary(); + foreach (var key in substForallDict.Keys) + mapping[key] = substForallDict[key]; + foreach (var key in substExistsDict.Keys) + mapping[key] = substExistsDict[key]; + calls.id2Vars[id] = mapping; + } + + // Uniquely identifies a procedure call (the call expr, instance) + public class BoogieCallExpr : IEquatable { + public NAryExpr expr; + public int inlineCnt; + + public BoogieCallExpr(NAryExpr expr, int inlineCnt) { + this.expr = expr; + this.inlineCnt = inlineCnt; + } + + public override int GetHashCode() { + return expr.GetHashCode() + 131 * inlineCnt.GetHashCode(); + } + + public override bool Equals(object obj) { + BoogieCallExpr that = obj as BoogieCallExpr; + return (expr == that.expr && inlineCnt == that.inlineCnt); + } + + public bool Equals(BoogieCallExpr that) { + return (expr == that.expr && inlineCnt == that.inlineCnt); + } + } + + // This class is used to traverse VCs and do the following: + // -- collect the set of FunctionCall nodes and label them with a unique string + // -- Rename all other labels (so that calling this on the same VC results in + // VCs with different labels each time) + public class FCallHandler : MutatingVCExprVisitor { + Dictionary/*!*/ implName2StratifiedInliningInfo; + public readonly Dictionary/*!*/ mainLabel2absy; + public Dictionary/*!*/ boogieExpr2Id; + public Dictionary/*!*/ recordExpr2Var; + public Dictionary/*!*/ id2Candidate; + public Dictionary/*!*/ id2ControlVar; + public Dictionary id2VC; + public Dictionary/*!*/ label2Id; + // candidate to block to Bool Control variable + public Dictionary> candiate2block2controlVar; + // Stores the candidate from which this one originated + public Dictionary candidateParent; + // Mapping from candidate Id to the "si_unique_call" id that led to + // this candidate. This is useful for getting persistent names for + // candidates + public Dictionary candidate2callId; + // A cache for candidate id to its persistent name + public Dictionary persistentNameCache; + // Inverse of the above map + public Dictionary persistentNameInv; + // Used to record candidates recently added + public HashSet recentlyAddedCandidates; + // Name of main procedure + private string mainProcName; + // A map from candidate id to the VCExpr that represents its + // first argument (used for obtaining concrete values in error trace) + public Dictionary argExprMap; + + // map from candidate to summary candidates + public Dictionary>> summaryCandidates; + private Dictionary>> summaryTemp; + // set of all boolean guards of summaries + public HashSet allSummaryConst; + + public HashSet forcedCandidates; + + // User info -- to decrease/increase calculation of recursion bound + public Dictionary recursionIncrement; + public Dictionary extraRecursion; + + public HashSet currCandidates; + [ContractInvariantMethod] + void ObjectInvariant() { + Contract.Invariant(cce.NonNullDictionaryAndValues(implName2StratifiedInliningInfo)); + Contract.Invariant(mainLabel2absy != null); + Contract.Invariant(boogieExpr2Id != null); + Contract.Invariant(cce.NonNullDictionaryAndValues(id2Candidate)); + Contract.Invariant(cce.NonNullDictionaryAndValues(id2ControlVar)); + Contract.Invariant(label2Id != null); + } + + // Name of the procedure whose VC we're mutating + string currProc; + + // The 0^th candidate is main + static int candidateCount = 1; + public int currInlineCount; + + public Dictionary> id2Vars; + + public FCallHandler(VCExpressionGenerator/*!*/ gen, + Dictionary/*!*/ implName2StratifiedInliningInfo, + string mainProcName, Dictionary/*!*/ mainLabel2absy) + : base(gen) { + Contract.Requires(gen != null); + Contract.Requires(cce.NonNullDictionaryAndValues(implName2StratifiedInliningInfo)); + Contract.Requires(mainLabel2absy != null); + this.implName2StratifiedInliningInfo = implName2StratifiedInliningInfo; + this.mainProcName = mainProcName; + this.mainLabel2absy = mainLabel2absy; + id2Candidate = new Dictionary(); + id2ControlVar = new Dictionary(); + boogieExpr2Id = new Dictionary(); + label2Id = new Dictionary(); + currCandidates = new HashSet(); + currInlineCount = 0; + currProc = null; + labelRenamer = new Dictionary(); + labelRenamerInv = new Dictionary(); + candidateParent = new Dictionary(); + //callGraphMapping = new Dictionary(); + recursionIncrement = new Dictionary(); + candidate2callId = new Dictionary(); + persistentNameCache = new Dictionary(); + persistentNameInv = new Dictionary(); + persistentNameCache[0] = "0"; + persistentNameInv["0"] = 0; + recentlyAddedCandidates = new HashSet(); + argExprMap = new Dictionary(); + recordExpr2Var = new Dictionary(); + candiate2block2controlVar = new Dictionary>(); + + forcedCandidates = new HashSet(); + extraRecursion = new Dictionary(); + + id2Vars = new Dictionary>(); + summaryCandidates = new Dictionary>>(); + summaryTemp = new Dictionary>>(); + allSummaryConst = new HashSet(); + id2VC = new Dictionary(); + } + + public void Clear() { + currCandidates = new HashSet(); + } + + // Return the set of all candidates + public HashSet getAllCandidates() { + var ret = new HashSet(candidateParent.Keys); + ret.Add(0); + return ret; + } + + // Given a candidate "id", let proc(id) be the + // procedure corresponding to id. This procedure returns + // the number of times proc(id) appears as an ancestor + // of id. This is the same as the number of times we've + // recursed on proc(id) + public int getRecursionBound(int id) { + int ret = 1; + var str = getProc(id); + + while (candidateParent.ContainsKey(id)) { + if (recursionIncrement.ContainsKey(id)) ret += recursionIncrement[id]; + id = candidateParent[id]; + if (getProc(id) == str && !forcedCandidates.Contains(id)) ret++; + } + + // Usual + if (!extraRecursion.ContainsKey(str)) + return ret; + + // Usual + if (ret <= CommandLineOptions.Clo.RecursionBound - 1) + return ret; + + // Special + if (ret >= CommandLineOptions.Clo.RecursionBound && + ret <= CommandLineOptions.Clo.RecursionBound + extraRecursion[str] - 1) + return CommandLineOptions.Clo.RecursionBound - 1; + + // Special + return ret - extraRecursion[str]; + } + + // This procedure returns the stack depth of the candidate + // (distance from main) + public int getStackDepth(int id) + { + int ret = 1; + + while (candidateParent.ContainsKey(id)) + { + ret++; + id = candidateParent[id]; + } + + return ret; + } + + // Set user-define increment/decrement to recursionBound + public void setRecursionIncrement(int id, int incr) { + if (recursionIncrement.ContainsKey(id)) + recursionIncrement[id] = incr; + else + recursionIncrement.Add(id, incr); + } + + // Returns the name of the procedure corresponding to candidate id + public string getProc(int id) { + if (id == 0) return mainProcName; + + return (id2Candidate[id].Op as VCExprBoogieFunctionOp).Func.Name; + } + + // Get a unique id for this candidate (dependent only on the Call + // graph of the program). The persistent id is: + // 0: for main + // a_b_c: where a is the persistent id of parent, and b is the procedure name + // and c is the unique call id (if any) + public string getPersistentId(int top_id) { + if (top_id == 0) return "0"; + Debug.Assert(candidateParent.ContainsKey(top_id)); + if (persistentNameCache.ContainsKey(top_id)) + return persistentNameCache[top_id]; + + var parent_id = getPersistentId(candidateParent[top_id]); + var call_id = candidate2callId.ContainsKey(top_id) ? candidate2callId[top_id] : -1; + var ret = string.Format("{0}_131_{1}_131_{2}", parent_id, getProc(top_id), call_id); + persistentNameCache[top_id] = ret; + persistentNameInv[ret] = top_id; + return ret; + } + + public int getCandidateFromGraphNode(string n) { + if (!persistentNameInv.ContainsKey(n)) { + return -1; + } + return persistentNameInv[n]; + } + + private int GetNewId(VCExprNAry vc) { + Contract.Requires(vc != null); + int id = candidateCount; + + id2Candidate[id] = vc; + id2ControlVar[id] = Gen.Variable("si_control_var_bool_" + id.ToString(), Microsoft.Boogie.Type.Bool); + + candidateCount++; + currCandidates.Add(id); + recentlyAddedCandidates.Add(id); + + return id; + } + + private string GetLabel(int id) { + Contract.Ensures(Contract.Result() != null); + + string ret = "si_fcall_" + id.ToString(); + if (!label2Id.ContainsKey(ret)) + label2Id[ret] = id; + + return ret; + } + + public int GetId(string label) { + Contract.Requires(label != null); + if (!label2Id.ContainsKey(label)) + return -1; + return label2Id[label]; + } + + Dictionary labelRenamer; + Dictionary labelRenamerInv; + + public string RenameAbsyLabel(string label) { + Contract.Requires(label != null); + Contract.Requires(label.Length >= 1); + Contract.Ensures(Contract.Result() != null); + + // Remove the sign from the label + string nosign = label.Substring(1); + var ret = "si_inline_" + currInlineCount.ToString() + "_" + nosign; + + if (!labelRenamer.ContainsKey(ret)) { + var c = labelRenamer.Count + 11; // two digit labels only + labelRenamer.Add(ret, c); + labelRenamerInv.Add(c.ToString(), ret); + } + return labelRenamer[ret].ToString(); + } + + public string ParseRenamedAbsyLabel(string label, int cnt) { + Contract.Requires(label != null); + if (!labelRenamerInv.ContainsKey(label)) { + return null; + } + var str = labelRenamerInv[label]; + var prefix = "si_inline_" + cnt.ToString() + "_"; + if (!str.StartsWith(prefix)) return null; + return str.Substring(prefix.Length); + } + + public void setCurrProc(string name) { + Contract.Requires(name != null); + currProc = name; + Contract.Assert(implName2StratifiedInliningInfo.ContainsKey(name)); + } + + public void setCurrProcAsMain() { + currProc = ""; + } + + // Return the formula (candidate IFF false) + public VCExpr getFalseExpr(int candidateId) { + //return Gen.Eq(VCExpressionGenerator.False, id2ControlVar[candidateId]); + return Gen.Not(id2ControlVar[candidateId]); + } + + // Return the formula (candidate IFF true) + public VCExpr getTrueExpr(int candidateId) { + return Gen.Eq(VCExpressionGenerator.True, id2ControlVar[candidateId]); + } + + public Dictionary getLabel2absy() { + Contract.Ensures(Contract.Result>() != null); + + Contract.Assert(currProc != null); + if (currProc == "") { + return mainLabel2absy; + } + return cce.NonNull(implName2StratifiedInliningInfo[currProc].label2absy); + } + + // Finds labels and changes them: + // si_fcall_id: if "id" corresponds to a tracked procedure call, then + // si_control_var_candidateId + // si_fcall_id: if "id" does not corresponds to a tracked procedure call, then + // delete + // num: si_inline_num + // + protected override VCExpr/*!*/ UpdateModifiedNode(VCExprNAry/*!*/ originalNode, + List/*!*/ newSubExprs, + // has any of the subexpressions changed? + bool changed, + bool arg) { + //Contract.Requires(originalNode != null); + //Contract.Requires(cce.NonNullElements(newSubExprs)); + Contract.Ensures(Contract.Result() != null); + + VCExpr ret; + if (changed) + ret = Gen.Function(originalNode.Op, + newSubExprs, originalNode.TypeArguments); + else + ret = originalNode; + + VCExprLabelOp lop = originalNode.Op as VCExprLabelOp; + if (lop == null) return ret; + if (!(ret is VCExprNAry)) return ret; + + VCExprNAry retnary = (VCExprNAry)ret; + Contract.Assert(retnary != null); + string prefix = "si_fcall_"; // from Wlp.ssc::Cmd(...) + if (lop.label.Substring(1).StartsWith(prefix)) { + int id = Int32.Parse(lop.label.Substring(prefix.Length + 1)); + Dictionary label2absy = getLabel2absy(); + Absy cmd = label2absy[id] as Absy; + //label2absy.Remove(id); + + Contract.Assert(cmd != null); + AssumeCmd acmd = cmd as AssumeCmd; + Contract.Assert(acmd != null); + NAryExpr naryExpr = acmd.Expr as NAryExpr; + Contract.Assert(naryExpr != null); + + string calleeName = naryExpr.Fun.FunctionName; + + VCExprNAry callExpr = retnary[0] as VCExprNAry; + + if (implName2StratifiedInliningInfo.ContainsKey(calleeName)) { + Contract.Assert(callExpr != null); + int candidateId = GetNewId(callExpr); + boogieExpr2Id[new BoogieCallExpr(naryExpr, currInlineCount)] = candidateId; + candidateParent[candidateId] = currInlineCount; + candiate2block2controlVar[candidateId] = new Dictionary(); + + string label = GetLabel(candidateId); + var unique_call_id = QKeyValue.FindIntAttribute(acmd.Attributes, "si_unique_call", -1); + if (unique_call_id != -1) + candidate2callId[candidateId] = unique_call_id; + + //return Gen.LabelPos(label, callExpr); + return Gen.LabelPos(label, id2ControlVar[candidateId]); + } + else if (calleeName.StartsWith(recordProcName)) { + Contract.Assert(callExpr != null); + Debug.Assert(callExpr.Length == 1); + Debug.Assert(callExpr[0] != null); + recordExpr2Var[new BoogieCallExpr(naryExpr, currInlineCount)] = callExpr[0]; + return callExpr; + } + else { + // callExpr can be null; this happens when the FunctionCall was on a + // pure function (not procedure) and the function got inlined + return retnary[0]; + } + } + + // Else, rename label + string newLabel = RenameAbsyLabel(lop.label); + if (lop.pos) { + return Gen.LabelPos(newLabel, retnary[0]); + } + else { + return Gen.LabelNeg(newLabel, retnary[0]); + } + + } + + // Upgrades summaryTemp to summaryCandidates by matching ensure clauses with + // the appropriate candidate they came from + public void matchSummaries() { + var id2Set = new Dictionary>>>(); + foreach (var id in recentlyAddedCandidates) { + var collect = new CollectVCVars(); + var proc = getProc(id); + if (!id2Set.ContainsKey(proc)) id2Set.Add(proc, new List>>()); + id2Set[proc].Add(Tuple.Create(id, collect.Collect(id2Candidate[id], true))); + } + + foreach (var kvp in summaryTemp) { + Contract.Assert(id2Set.ContainsKey(kvp.Key)); + var ls = id2Set[kvp.Key]; + foreach (var tup in kvp.Value) { + var collect = new CollectVCVars(); + var s1 = collect.Collect(tup.Item2, true); + var found = false; + foreach (var t in ls) { + var s2 = t.Item2; + if (s1.IsSubsetOf(s2)) { + if (!summaryCandidates.ContainsKey(t.Item1)) + summaryCandidates.Add(t.Item1, new List>()); + summaryCandidates[t.Item1].Add(tup); + allSummaryConst.Add(tup.Item1); + found = true; + break; + } + } + Contract.Assert(found); + } + } + summaryTemp.Clear(); + } + + public IEnumerable getInlinedCandidates() { + return candidateParent.Keys.Except(currCandidates).Union(new int[] { 0 }); + } + + } // end FCallHandler + + // Collects the set of all VCExprVar in a given VCExpr + class CollectVCVars : CollectingVCExprVisitor, bool> { + public override HashSet Visit(VCExprVar node, bool arg) { + var ret = new HashSet(); + ret.Add(node); + return ret; + } + + protected override HashSet CombineResults(List> results, bool arg) { + var ret = new HashSet(); + results.Iter(s => ret.UnionWith(s)); + return ret; + } + } + + public class FCallInliner : MutatingVCExprVisitor { + public Dictionary/*!*/ subst; + [ContractInvariantMethod] + void ObjectInvariant() { + Contract.Invariant(cce.NonNullDictionaryAndValues(subst)); + } + + + public FCallInliner(VCExpressionGenerator gen) + : base(gen) { + Contract.Requires(gen != null); + subst = new Dictionary(); + } + + public void Clear() { + subst = new Dictionary(); + } + + protected override VCExpr/*!*/ UpdateModifiedNode(VCExprNAry/*!*/ originalNode, + List/*!*/ newSubExprs, + // has any of the subexpressions changed? + bool changed, + bool arg) { + //Contract.Requires(originalNode != null);Contract.Requires(newSubExprs != null); + Contract.Ensures(Contract.Result() != null); + + VCExpr ret; + if (changed) + ret = Gen.Function(originalNode.Op, newSubExprs, originalNode.TypeArguments); + else + ret = originalNode; + + VCExprLabelOp lop = originalNode.Op as VCExprLabelOp; + if (lop == null) return ret; + if (!(ret is VCExprNAry)) return ret; + + string prefix = "si_fcall_"; // from FCallHandler::GetLabel + if (lop.label.Substring(1).StartsWith(prefix)) { + int id = Int32.Parse(lop.label.Substring(prefix.Length + 1)); + if (subst.ContainsKey(id)) { + return subst[id]; + } + } + return ret; + } + + } // end FCallInliner + + + + public class StratifiedInliningErrorReporter : ProverInterface.ErrorHandler { + Dictionary implName2StratifiedInliningInfo; + ProverInterface theoremProver; + VerifierCallback callback; + FCallHandler calls; + StratifiedInliningInfo mainInfo; + StratifiedVC mainVC; + + public bool underapproximationMode; + public List candidatesToExpand; + public List callSitesToExpand; + + [ContractInvariantMethod] + void ObjectInvariant() { + Contract.Invariant(candidatesToExpand != null); + Contract.Invariant(mainInfo != null); + Contract.Invariant(callback != null); + Contract.Invariant(theoremProver != null); + Contract.Invariant(cce.NonNullDictionaryAndValues(implName2StratifiedInliningInfo)); + } + + + public StratifiedInliningErrorReporter(Dictionary implName2StratifiedInliningInfo, + ProverInterface theoremProver, VerifierCallback callback, + StratifiedInliningInfo mainInfo) { + Contract.Requires(cce.NonNullDictionaryAndValues(implName2StratifiedInliningInfo)); + Contract.Requires(theoremProver != null); + Contract.Requires(callback != null); + Contract.Requires(mainInfo != null); + this.implName2StratifiedInliningInfo = implName2StratifiedInliningInfo; + this.theoremProver = theoremProver; + this.callback = callback; + this.mainInfo = mainInfo; + this.underapproximationMode = false; + this.calls = null; + this.candidatesToExpand = new List(); + } + + public StratifiedInliningErrorReporter(Dictionary implName2StratifiedInliningInfo, + ProverInterface theoremProver, VerifierCallback callback, + StratifiedVC mainVC) { + Contract.Requires(cce.NonNullDictionaryAndValues(implName2StratifiedInliningInfo)); + Contract.Requires(theoremProver != null); + Contract.Requires(callback != null); + Contract.Requires(mainVC != null); + this.implName2StratifiedInliningInfo = implName2StratifiedInliningInfo; + this.theoremProver = theoremProver; + this.callback = callback; + this.mainVC = mainVC; + this.underapproximationMode = false; + this.candidatesToExpand = new List(); + } + + public void SetCandidateHandler(FCallHandler calls) { + Contract.Requires(calls != null); + this.calls = calls; + } + + List> orderedStateIds; + + private Model.Element GetModelValue(Model m, Variable v, int candidateId) { + // first, get the unique name + string uniqueName; + + VCExprVar vvar = theoremProver.Context.BoogieExprTranslator.TryLookupVariable(v); + if (vvar == null) { + uniqueName = v.Name; + } + else { + if (candidateId != 0) { + Dictionary mapping = calls.id2Vars[candidateId]; + if (mapping.ContainsKey(vvar)) { + VCExpr e = mapping[vvar]; + if (e is VCExprLiteral) { + VCExprLiteral lit = (VCExprLiteral)e; + return m.MkElement(lit.ToString()); + } + vvar = (VCExprVar)mapping[vvar]; + } + } + uniqueName = theoremProver.Context.Lookup(vvar); + } + + var f = m.TryGetFunc(uniqueName); + if (f == null) + return m.MkFunc("@undefined", 0).GetConstant(); + return f.GetConstant(); + } + + public readonly static int CALL = -1; + public readonly static int RETURN = -2; + + public void PrintModel(Model model) { + var filename = CommandLineOptions.Clo.ModelViewFile; + if (model == null || filename == null) return; + + if (filename == "-") { + model.Write(Console.Out); + Console.Out.Flush(); + } + else { + using (var wr = new StreamWriter(filename, !Counterexample.firstModelFile)) { + Counterexample.firstModelFile = false; + model.Write(wr); + } + } + } + + private void GetModelWithStates(Model m) { + if (m == null) return; + var mvInfo = mainInfo.mvInfo; + var mvstates = m.TryGetFunc("$mv_state"); + if (mvstates == null) + return; + + Contract.Assert(mvstates.Arity == 2); + + foreach (Variable v in mvInfo.AllVariables) { + m.InitialState.AddBinding(v.Name, GetModelValue(m, v, 0)); + } + + int lastCandidate = 0; + int lastCapturePoint = CALL; + for (int i = 0; i < this.orderedStateIds.Count; ++i) { + var s = orderedStateIds[i]; + int candidate = s.Item1; + int capturePoint = s.Item2; + string implName = calls.getProc(candidate); + ModelViewInfo info = candidate == 0 ? mvInfo : implName2StratifiedInliningInfo[implName].mvInfo; + + if (capturePoint == CALL || capturePoint == RETURN) { + lastCandidate = candidate; + lastCapturePoint = capturePoint; + continue; + } + + Contract.Assume(0 <= capturePoint && capturePoint < info.CapturePoints.Count); + VC.ModelViewInfo.Mapping map = info.CapturePoints[capturePoint]; + var prevInc = (lastCapturePoint != CALL && lastCapturePoint != RETURN && candidate == lastCandidate) + ? info.CapturePoints[lastCapturePoint].IncarnationMap : new Dictionary(); + var cs = m.MkState(map.Description); + + foreach (Variable v in info.AllVariables) { + var e = (Expr)map.IncarnationMap[v]; + + if (e == null) { + if (lastCapturePoint == CALL || lastCapturePoint == RETURN) { + cs.AddBinding(v.Name, GetModelValue(m, v, candidate)); + } + continue; + } + + if (lastCapturePoint != CALL && lastCapturePoint != RETURN && prevInc[v] == e) continue; // skip unchanged variables + + Model.Element elt; + if (e is IdentifierExpr) { + IdentifierExpr ide = (IdentifierExpr)e; + elt = GetModelValue(m, ide.Decl, candidate); + } + else if (e is LiteralExpr) { + LiteralExpr lit = (LiteralExpr)e; + elt = m.MkElement(lit.Val.ToString()); + } + else { + Contract.Assume(false); + elt = m.MkFunc(e.ToString(), 0).GetConstant(); + } + cs.AddBinding(v.Name, elt); + } + + lastCandidate = candidate; + lastCapturePoint = capturePoint; + } + + return; + } + + public override void OnResourceExceeded(string message, IEnumerable> assertCmds = null) + { + //Contract.Requires(message != null); + } + + public override void OnModel(IList/*!*/ labels, Model model, ProverInterface.Outcome proverOutcome) { + if (CommandLineOptions.Clo.PrintErrorModel >= 1 && model != null) { + model.Write(ErrorReporter.ModelWriter); + ErrorReporter.ModelWriter.Flush(); + } + + // Timeout? + if (proverOutcome != ProverInterface.Outcome.Invalid) + return; + + candidatesToExpand = new List(); + orderedStateIds = new List>(); + var cex = GenerateTrace(labels, model, 0, mainInfo.impl, mainInfo.mvInfo); + + if (underapproximationMode && cex != null) { + //Debug.Assert(candidatesToExpand.All(calls.isSkipped)); + GetModelWithStates(model); + callback.OnCounterexample(cex, null); + this.PrintModel(model); + } + } + + private Counterexample GenerateTrace(IList/*!*/ labels, Model/*!*/ errModel, + int candidateId, Implementation procImpl, ModelViewInfo mvInfo) { + Contract.Requires(cce.NonNullElements(labels)); + Contract.Requires(procImpl != null); + + Hashtable traceNodes = new Hashtable(); + + if (!CommandLineOptions.Clo.SIBoolControlVC) + { + foreach (string s in labels) + { + Contract.Assert(s != null); + var absylabel = calls.ParseRenamedAbsyLabel(s, candidateId); + + if (absylabel == null) continue; + + Absy absy; + + if (candidateId == 0) + { + absy = Label2Absy(absylabel); + } + else + { + absy = Label2Absy(procImpl.Name, absylabel); + } + + if (traceNodes.ContainsKey(absy)) + System.Console.WriteLine("Warning: duplicate label: " + s + " read while tracing nodes"); + else + traceNodes.Add(absy, null); + } + } + else + { + Debug.Assert(CommandLineOptions.Clo.UseProverEvaluate, "Must use prover evaluate option with boolControlVC"); + var block = procImpl.Blocks[0]; + traceNodes.Add(block, null); + while (true) + { + var gc = block.TransferCmd as GotoCmd; + if (gc == null) break; + Block next = null; + foreach (var succ in gc.labelTargets) + { + var succtaken = (bool) theoremProver.Evaluate(calls.candiate2block2controlVar[candidateId][succ]); + if (succtaken) + { + next = succ; + traceNodes.Add(succ, null); + break; + } + } + Debug.Assert(next != null, "Must find a successor"); + Debug.Assert(traceNodes.ContainsKey(next), "CFG cannot be cyclic"); + block = next; + } + } + + List trace = new List(); + Block entryBlock = cce.NonNull(procImpl.Blocks[0]); + Contract.Assert(entryBlock != null); + Contract.Assert(traceNodes.Contains(entryBlock)); + trace.Add(entryBlock); + + var calleeCounterexamples = new Dictionary(); + Counterexample newCounterexample = GenerateTraceRec(labels, errModel, mvInfo, candidateId, entryBlock, traceNodes, trace, calleeCounterexamples); + + return newCounterexample; + } + + private Counterexample GenerateTraceRec( + IList/*!*/ labels, Model/*!*/ errModel, ModelViewInfo mvInfo, + int candidateId, + Block/*!*/ b, Hashtable/*!*/ traceNodes, List/*!*/ trace, + Dictionary/*!*/ calleeCounterexamples) { + Contract.Requires(cce.NonNullElements(labels)); + Contract.Requires(b != null); + Contract.Requires(traceNodes != null); + Contract.Requires(trace != null); + Contract.Requires(cce.NonNullDictionaryAndValues(calleeCounterexamples)); + // After translation, all potential errors come from asserts. + while (true) { + List cmds = b.Cmds; + TransferCmd transferCmd = cce.NonNull(b.TransferCmd); + for (int i = 0; i < cmds.Count; i++) { + Cmd cmd = cce.NonNull(cmds[i]); + + // Skip if 'cmd' not contained in the trace or not an assert + if ((cmd is AssertCmd && traceNodes.Contains(cmd)) || + (cmd is AssumeCmd && QKeyValue.FindBoolAttribute((cmd as AssumeCmd).Attributes, "exitAssert"))) + { + var acmd = cmd as AssertCmd; + if (acmd == null) { acmd = new AssertCmd(Token.NoToken, Expr.True); } + Counterexample newCounterexample = AssertCmdToCounterexample(acmd, transferCmd, trace, errModel, mvInfo, theoremProver.Context); + newCounterexample.AddCalleeCounterexample(calleeCounterexamples); + return newCounterexample; + } + + // Counterexample generation for inlined procedures + AssumeCmd assumeCmd = cmd as AssumeCmd; + if (assumeCmd == null) + continue; + NAryExpr naryExpr = assumeCmd.Expr as NAryExpr; + if (naryExpr == null) + continue; + string calleeName = naryExpr.Fun.FunctionName; + Contract.Assert(calleeName != null); + + BinaryOperator binOp = naryExpr.Fun as BinaryOperator; + if (binOp != null && binOp.Op == BinaryOperator.Opcode.And) { + Expr expr = naryExpr.Args[0]; + NAryExpr mvStateExpr = expr as NAryExpr; + if (mvStateExpr != null && mvStateExpr.Fun.FunctionName == ModelViewInfo.MVState_FunctionDef.Name) { + LiteralExpr x = mvStateExpr.Args[1] as LiteralExpr; + orderedStateIds.Add(new Tuple(candidateId, x.asBigNum.ToInt)); + } + } + + if (calleeName.StartsWith(recordProcName) && (errModel != null || CommandLineOptions.Clo.UseProverEvaluate)) { + var expr = calls.recordExpr2Var[new BoogieCallExpr(naryExpr, candidateId)]; + + // Record concrete value of the argument to this procedure + var args = new List(); + if (errModel == null && CommandLineOptions.Clo.UseProverEvaluate) + { + object exprv; + try + { + exprv = theoremProver.Evaluate(expr); + } + catch (Exception) + { + exprv = null; + } + args.Add(exprv); + } + else + { + if (expr is VCExprIntLit) + { + args.Add(errModel.MkElement((expr as VCExprIntLit).Val.ToString())); + } + else if (expr == VCExpressionGenerator.True) + { + args.Add(errModel.MkElement("true")); + } + else if (expr == VCExpressionGenerator.False) + { + args.Add(errModel.MkElement("false")); + } + else if (expr is VCExprVar) + { + var idExpr = expr as VCExprVar; + string name = theoremProver.Context.Lookup(idExpr); + Contract.Assert(name != null); + Model.Func f = errModel.TryGetFunc(name); + if (f != null) + { + args.Add(f.GetConstant()); + } + } + else + { + Contract.Assert(false); + } + } + calleeCounterexamples[new TraceLocation(trace.Count - 1, i)] = + new CalleeCounterexampleInfo(null, args); + continue; + } + + if (!implName2StratifiedInliningInfo.ContainsKey(calleeName)) + continue; + + Contract.Assert(calls != null); + + int calleeId = calls.boogieExpr2Id[new BoogieCallExpr(naryExpr, candidateId)]; + + if (calls.currCandidates.Contains(calleeId)) { + candidatesToExpand.Add(calleeId); + } + else { + orderedStateIds.Add(new Tuple(calleeId, StratifiedInliningErrorReporter.CALL)); + var calleeInfo = implName2StratifiedInliningInfo[calleeName]; + calleeCounterexamples[new TraceLocation(trace.Count - 1, i)] = + new CalleeCounterexampleInfo(GenerateTrace(labels, errModel, calleeId, calleeInfo.impl, calleeInfo.mvInfo), new List()); + orderedStateIds.Add(new Tuple(candidateId, StratifiedInliningErrorReporter.RETURN)); + } + } + + GotoCmd gotoCmd = transferCmd as GotoCmd; + if (gotoCmd != null) { + b = null; + foreach (Block bb in cce.NonNull(gotoCmd.labelTargets)) { + Contract.Assert(bb != null); + if (traceNodes.Contains(bb)) { + trace.Add(bb); + b = bb; + break; + } + } + if (b != null) continue; + } + return null; + } + } + + public override Absy Label2Absy(string label) { + //Contract.Requires(label != null); + Contract.Ensures(Contract.Result() != null); + + int id = int.Parse(label); + Contract.Assert(calls != null); + return cce.NonNull((Absy)calls.mainLabel2absy[id]); + } + + public Absy Label2Absy(string procName, string label) { + Contract.Requires(label != null); + Contract.Requires(procName != null); + Contract.Ensures(Contract.Result() != null); + + int id = int.Parse(label); + Dictionary l2a = cce.NonNull(implName2StratifiedInliningInfo[procName]).label2absy; + return cce.NonNull((Absy)l2a[id]); + } + + public override void OnProverWarning(string msg) { + //Contract.Requires(msg != null); + callback.OnWarning(msg); + } + } + + } // class StratifiedVCGen + + public class EmptyErrorHandler : ProverInterface.ErrorHandler + { + public override void OnModel(IList labels, Model model, ProverInterface.Outcome proverOutcome) + { } + } + + public class InvalidProgramForSecureVc : Exception + { + public InvalidProgramForSecureVc(string msg) : + base(msg) { } + } + + public class SecureVCGen : VCGen + { + // Z3 + ProverInterface prover; + // Handler + ErrorReporter handler; + // dump file + public static TokenTextWriter outfile = null; + + + public SecureVCGen(Program program, string/*?*/ logFilePath, bool appendLogFile, List checkers) + : base(program, logFilePath, appendLogFile, checkers) + { + prover = null; + handler = null; + if (CommandLineOptions.Clo.SecureVcGen != "" && outfile == null) + { + outfile = new TokenTextWriter(new StreamWriter(CommandLineOptions.Clo.SecureVcGen)); + CommandLineOptions.Clo.PrintInstrumented = true; + var implsToVerify = new HashSet( + program.TopLevelDeclarations.OfType() + .Where(impl => !impl.SkipVerification) + .Select(impl => impl.Name)); + + foreach (var decl in program.TopLevelDeclarations) + { + if (decl is NamedDeclaration && implsToVerify.Contains((decl as NamedDeclaration).Name)) + continue; + decl.Emit(outfile, 0); + } + } + } + + private Block GetExitBlock(Implementation impl) + { + var exitblocks = impl.Blocks.Where(blk => blk.TransferCmd is ReturnCmd); + if (exitblocks.Count() == 1) + return exitblocks.First(); + // create a new exit block + var eb = new Block(Token.NoToken, "SVCeb", new List(), new ReturnCmd(Token.NoToken)); + foreach (var b in exitblocks) + { + b.TransferCmd = new GotoCmd(Token.NoToken, new List { eb }); + } + impl.Blocks.Add(eb); + return eb; + } + + //static int LocalVarCounter = 0; + private LocalVariable GetNewLocal(Variable v, string suffix) + { + return new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, + string.Format("svc_{0}_{1}", v.Name, suffix), v.TypedIdent.Type)); + } + + private void GenVc(Implementation impl, VerifierCallback collector) + { + if (impl.Proc.Requires.Any()) + throw new InvalidProgramForSecureVc("SecureVc: Requires not supported"); + if(impl.LocVars.Any(v => isVisible(v))) + throw new InvalidProgramForSecureVc("SecureVc: Visible Local variables not allowed"); + + // Desugar procedure calls + DesugarCalls(impl); + + // Gather spec, remove existing ensures + var secureAsserts = new List(); + var logicalAsserts = new List(); + + foreach (var ens in impl.Proc.Ensures) + { + if(ens.Free) + throw new InvalidProgramForSecureVc("SecureVc: Free Ensures not supported"); + var dd = new Duplicator(); + secureAsserts.Add(new AssertCmd(ens.tok, Expr.Not(ens.Condition))); + logicalAsserts.Add(dd.VisitAssertCmd(new AssertCmd(ens.tok, ens.Condition)) as AssertCmd); + } + impl.Proc.Ensures.Clear(); + + // Make a copy of the impl + var dup = new Duplicator(); + var implDup = dup.VisitImplementation(impl); + + // Get exit block + var eb = GetExitBlock(impl); + + // Create two blocks: one for secureAsserts, one for logical asserts + var ebSecure = new Block(Token.NoToken, "svc_secure_asserts", new List(), new ReturnCmd(Token.NoToken)); + var ebLogical = new Block(Token.NoToken, "svc_logical_asserts", new List(), new ReturnCmd(Token.NoToken)); + + eb.TransferCmd = new GotoCmd(eb.TransferCmd.tok, new List { ebSecure, ebLogical }); + impl.Blocks.Add(ebSecure); + impl.Blocks.Add(ebLogical); + + // Rename spec, while create copies of the hidden variables + var substOld = new Dictionary(); + var substVarSpec = new Dictionary(); + var substVarPath = new Dictionary(); + foreach (var g in program.GlobalVariables) + { + if (!isHidden(g)) continue; + var lv = GetNewLocal(g, "In"); + impl.LocVars.Add(lv); + substOld.Add(g, Expr.Ident(lv)); + } + + for(int i = 0; i < impl.InParams.Count; i++) + { + var v = impl.Proc.InParams[i]; + if (!isHidden(v)) + { + substVarSpec.Add(impl.Proc.InParams[i], Expr.Ident(impl.InParams[i])); + continue; + } + + var lv = GetNewLocal(v, "In"); + impl.LocVars.Add(lv); + substVarSpec.Add(v, Expr.Ident(lv)); + substVarPath.Add(impl.InParams[i], Expr.Ident(lv)); + } + + for (int i = 0; i < impl.OutParams.Count; i++) + { + var v = impl.Proc.OutParams[i]; + if (!isHidden(v)) + { + substVarSpec.Add(impl.Proc.OutParams[i], Expr.Ident(impl.OutParams[i])); + continue; + } + + var lv = GetNewLocal(v, "Out"); + impl.LocVars.Add(lv); + substVarSpec.Add(v, Expr.Ident(lv)); + substVarPath.Add(impl.OutParams[i], Expr.Ident(lv)); + } + + foreach (var g in program.GlobalVariables) + { + if (!isHidden(g)) continue; + if (!impl.Proc.Modifies.Any(ie => ie.Name == g.Name)) continue; + + var lv = GetNewLocal(g, "Out"); + impl.LocVars.Add(lv); + substVarSpec.Add(g, Expr.Ident(lv)); + substVarPath.Add(g, Expr.Ident(lv)); + } + + secureAsserts = secureAsserts.ConvertAll(ac => + Substituter.ApplyReplacingOldExprs( + Substituter.SubstitutionFromHashtable(substVarSpec), + Substituter.SubstitutionFromHashtable(substOld), + ac) as AssertCmd); + + var substVarProcToImpl = new Dictionary(); + for (int i = 0; i < impl.InParams.Count; i++) + substVarProcToImpl.Add(impl.Proc.InParams[i], Expr.Ident(impl.InParams[i])); + + for (int i = 0; i < impl.OutParams.Count; i++) + substVarProcToImpl.Add(impl.Proc.OutParams[i], Expr.Ident(impl.OutParams[i])); + + logicalAsserts = logicalAsserts.ConvertAll(ac => + Substituter.Apply(Substituter.SubstitutionFromHashtable(substVarProcToImpl), ac) + as AssertCmd); + + // Paths + foreach (var path in GetAllPaths(implDup)) + { + var wp = ComputeWP(implDup, path); + + // replace hidden variables to match those used in the spec + wp = Substituter.ApplyReplacingOldExprs( + Substituter.SubstitutionFromHashtable(substVarPath), + Substituter.SubstitutionFromHashtable(substOld), + wp); + + ebSecure.Cmds.Add(new AssumeCmd(Token.NoToken, Expr.Not(wp))); + } + + ebSecure.Cmds.AddRange(secureAsserts); + ebLogical.Cmds.AddRange(logicalAsserts); + + if (outfile != null) + { + impl.Proc.Emit(outfile, 0); + impl.Emit(outfile, 0); + } + + ModelViewInfo mvInfo; + ConvertCFG2DAG(impl); + var gotoCmdOrigins = PassifyImpl(impl, out mvInfo); + + var gen = prover.VCExprGen; + var exprGen = prover.Context.ExprGen; + var translator = prover.Context.BoogieExprTranslator; + + var label2absy = new Dictionary(); + VCGen.CodeExprConversionClosure cc = new VCGen.CodeExprConversionClosure(label2absy, prover.Context); + translator.SetCodeExprConverter(cc.CodeExprToVerificationCondition); + var implVc = gen.Not(GenerateVCAux(impl, null, label2absy, prover.Context)); + + handler = new VCGen.ErrorReporter(gotoCmdOrigins, label2absy, impl.Blocks, incarnationOriginMap, collector, mvInfo, prover.Context, program); + + prover.Assert(implVc, true); + } + + Expr ComputeWP(Implementation impl, List path) + { + Expr expr = Expr.True; + + // create constants for out varibles + var subst = new Dictionary(); + foreach (var g in impl.Proc.Modifies) + { + var c = new Constant(Token.NoToken, new TypedIdent(Token.NoToken, + "svc_out_const_" + g.Name, g.Decl.TypedIdent.Type)); + subst.Add(c, g); + expr = Expr.And(expr, Expr.Eq(Expr.Ident(c), g)); + } + + foreach (var v in impl.OutParams) + { + var c = new Constant(Token.NoToken, new TypedIdent(Token.NoToken, + "svc_out_const_" + v.Name, v.TypedIdent.Type)); + subst.Add(c, Expr.Ident(v)); + expr = Expr.And(expr, Expr.Eq(Expr.Ident(c), Expr.Ident(v))); + } + + // we need this technicality + var subst1 = new Dictionary(); + foreach (var g in program.GlobalVariables) + { + subst1.Add(g, new OldExpr(Token.NoToken, Expr.Ident(g))); + } + + // Implicitly close with havoc of all the locals and OutParams + path.Insert(0, new HavocCmd(Token.NoToken, new List( + impl.LocVars.Select(v => Expr.Ident(v)).Concat( + impl.OutParams.Select(v => Expr.Ident(v)))))); + + for (int i = path.Count - 1; i >= 0; i--) + { + var cmd = path[i]; + if (cmd is AssumeCmd) + { + expr = Expr.And(expr, (cmd as AssumeCmd).Expr); + } + else if (cmd is AssignCmd) + { + var h = new Dictionary(); + var acmd = cmd as AssignCmd; + for (int j = 0; j < acmd.Lhss.Count; j++) + { + h.Add(acmd.Lhss[j].DeepAssignedVariable, acmd.Rhss[j]); + } + var s = Substituter.SubstitutionFromHashtable(h); + expr = Substituter.Apply(s, expr); + } + else if (cmd is HavocCmd) + { + var h = new Dictionary(); + var formals = new List(); + + var vc = new VariableCollector(); + vc.VisitExpr(expr); + + foreach (var ie in (cmd as HavocCmd).Vars) + { + if (!vc.usedVars.Contains(ie.Decl)) continue; + var f = new BoundVariable(Token.NoToken, new TypedIdent(Token.NoToken, + ie.Decl.Name + "_formal", ie.Decl.TypedIdent.Type)); + h.Add(ie.Decl, Expr.Ident(f)); + formals.Add(f); + } + if (!formals.Any()) + continue; + var s = Substituter.SubstitutionFromHashtable(h); + expr = Substituter.Apply(s, expr); + expr = new ExistsExpr(Token.NoToken, formals, expr); + } + else + { + throw new InvalidProgramForSecureVc(string.Format("Unhandled cmd: {0}", cmd)); + } + } + + // Implicitly close with havoc of all the locals and OutParams + + + + expr = Substituter.Apply(Substituter.SubstitutionFromHashtable(subst1), expr); + expr = Substituter.Apply(Substituter.SubstitutionFromHashtable(subst), + Substituter.SubstitutionFromHashtable(new Dictionary()), expr); + expr.Typecheck(new TypecheckingContext(null)); + return expr; + } + + // Generate all paths in the impl + IEnumerable> GetAllPaths(Implementation impl) + { + var stk = new Stack>(); + stk.Push(Tuple.Create(impl.Blocks[0], 0)); + + while (stk.Any()) + { + var tup = stk.Peek(); + if (tup.Item1.TransferCmd is ReturnCmd) + { + var ret = new List(); + var ls = stk.ToList(); + ls.Iter(t => ret.AddRange(t.Item1.Cmds)); + yield return ret; + + stk.Pop(); + continue; + } + + stk.Pop(); + + var gc = tup.Item1.TransferCmd as GotoCmd; + if (gc.labelTargets.Count <= tup.Item2) + continue; + + stk.Push(Tuple.Create(tup.Item1, tup.Item2 + 1)); + stk.Push(Tuple.Create(gc.labelTargets[tup.Item2], 0)); + } + yield break; + } + + bool isHidden(Variable v) + { + return QKeyValue.FindBoolAttribute(v.Attributes, "hidden"); + } + + bool isVisible(Variable v) + { + return !isHidden(v); + } + + public override Outcome VerifyImplementation(Implementation/*!*/ impl, VerifierCallback/*!*/ callback) + { + Debug.Assert(this.program == program); + + // Record current time + var startTime = DateTime.UtcNow; + + CommandLineOptions.Clo.ProverCCLimit = 1; + prover = ProverInterface.CreateProver(program, logFilePath, appendLogFile, CommandLineOptions.Clo.ProverKillTime); + + // Flush any axioms that came with the program before we start SI on this implementation + prover.AssertAxioms(); + + GenVc(impl, callback); + + prover.Check(); + var outcome = prover.CheckOutcomeCore(handler); + //var outcome = ProverInterface.Outcome.Valid; + + prover.Close(); + + + + //Console.WriteLine("Answer = {0}", outcome); + + return ProverInterfaceOutcomeToConditionGenerationOutcome(outcome); + } + } + +} // namespace VC diff --git a/Source/VCGeneration/VC.cs b/Source/VCGeneration/VC.cs index 3a483a58..bf25e042 100644 --- a/Source/VCGeneration/VC.cs +++ b/Source/VCGeneration/VC.cs @@ -1,3901 +1,3901 @@ -//----------------------------------------------------------------------------- -// -// Copyright (C) Microsoft Corporation. All Rights Reserved. -// -//----------------------------------------------------------------------------- -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Threading; -using System.IO; -using Microsoft.Boogie; -using Microsoft.Boogie.GraphUtil; -using System.Diagnostics.Contracts; -using Microsoft.Basetypes; -using Microsoft.Boogie.VCExprAST; - -namespace VC { - using Bpl = Microsoft.Boogie; - using System.Threading.Tasks; - - public class VCGen : ConditionGeneration { - private const bool _print_time = false; - /// - /// Constructor. Initializes the theorem prover. - /// - [NotDelayed] - public VCGen(Program program, string/*?*/ logFilePath, bool appendLogFile, List checkers) - : base(program, checkers) - { - Contract.Requires(program != null); - this.appendLogFile = appendLogFile; - this.logFilePath = logFilePath; - } - - private static AssumeCmd AssertTurnedIntoAssume(AssertCmd assrt) { - Contract.Requires(assrt != null); - Contract.Ensures(Contract.Result() != null); - - Expr expr = assrt.Expr; - Contract.Assert(expr != null); - switch (Wlp.Subsumption(assrt)) { - case CommandLineOptions.SubsumptionOption.Never: - expr = Expr.True; - break; - case CommandLineOptions.SubsumptionOption.Always: - break; - case CommandLineOptions.SubsumptionOption.NotForQuantifiers: - if (expr is QuantifierExpr) { - expr = Expr.True; - } - break; - default: - Contract.Assert(false); - throw new cce.UnreachableException(); // unexpected case - } - - return new AssumeCmd(assrt.tok, expr); - } - - #region Soundness smoke tester - class SmokeTester { - [ContractInvariantMethod] - void ObjectInvariant() { - Contract.Invariant(parent != null); - Contract.Invariant(impl != null); - Contract.Invariant(initial != null); - Contract.Invariant(cce.NonNullDictionaryAndValues(copies)); - Contract.Invariant(cce.NonNull(visited)); - Contract.Invariant(callback != null); - } - - VCGen parent; - Implementation impl; - Block initial; - int id; - Dictionary copies = new Dictionary(); - HashSet visited = new HashSet(); - VerifierCallback callback; - - internal SmokeTester(VCGen par, Implementation i, VerifierCallback callback) { - Contract.Requires(par != null); - Contract.Requires(i != null); - Contract.Requires(callback != null); - parent = par; - impl = i; - initial = i.Blocks[0]; - this.callback = callback; - } - - internal void Copy() { - CloneBlock(impl.Blocks[0]); - initial = GetCopiedBlocks()[0]; - } - - internal void Test() { - Contract.EnsuresOnThrow(true); - - DFS(initial); - } - - void TopologicalSortImpl() { - Graph dag = new Graph(); - dag.AddSource(cce.NonNull(impl.Blocks[0])); // there is always at least one node in the graph - foreach (Block b in impl.Blocks) { - GotoCmd gtc = b.TransferCmd as GotoCmd; - if (gtc != null) { - Contract.Assume(gtc.labelTargets != null); - foreach (Block dest in gtc.labelTargets) { - Contract.Assert(dest != null); - dag.AddEdge(b, dest); - } - } - } - impl.Blocks = new List(); - foreach (Block b in dag.TopologicalSort()) { - Contract.Assert(b != null); - impl.Blocks.Add(b); - } - } - - void Emit() { - TopologicalSortImpl(); - EmitImpl(impl, false); - } - - // this one copies forward - Block CloneBlock(Block b) { - Contract.Requires(b != null); - Contract.Ensures(Contract.Result() != null); - - Block fake_res; - if (copies.TryGetValue(b, out fake_res)) { - return cce.NonNull(fake_res); - } - Block res = new Block(b.tok, b.Label, new List(b.Cmds), null); - copies[b] = res; - if (b.TransferCmd is GotoCmd) { - foreach (Block ch in cce.NonNull((GotoCmd)b.TransferCmd).labelTargets) { - Contract.Assert(ch != null); - CloneBlock(ch); - } - } - foreach (Block p in b.Predecessors) { - Contract.Assert(p != null); - res.Predecessors.Add(CloneBlock(p)); - } - return res; - } - - // this one copies backwards - Block CopyBlock(Block b) { - Contract.Requires(b != null); - Contract.Ensures(Contract.Result() != null); - - Block fake_res; - if (copies.TryGetValue(b, out fake_res)) { - // fake_res should be Block! but the compiler fails - return cce.NonNull(fake_res); - } - Block res; - List seq = new List(); - foreach (Cmd c in b.Cmds) { - Contract.Assert(c != null); - AssertCmd turn = c as AssertCmd; - if (!turnAssertIntoAssumes || turn == null) { - seq.Add(c); - } else { - seq.Add(AssertTurnedIntoAssume(turn)); - } - } - res = new Block(b.tok, b.Label, seq, null); - copies[b] = res; - foreach (Block p in b.Predecessors) { - Contract.Assert(p != null); - res.Predecessors.Add(CopyBlock(p)); - } - return res; - } - - List GetCopiedBlocks() { - Contract.Ensures(cce.NonNullElements(Contract.Result>())); - - // the order of nodes in res is random (except for the first one, being the entry) - List res = new List(); - res.Add(copies[initial]); - - foreach (KeyValuePair kv in copies) { - Contract.Assert(kv.Key != null&&kv.Value!=null); - GotoCmd go = kv.Key.TransferCmd as GotoCmd; - ReturnCmd ret = kv.Key.TransferCmd as ReturnCmd; - if (kv.Key != initial) { - res.Add(kv.Value); - } - if (go != null) { - GotoCmd copy = new GotoCmd(go.tok, new List(), new List()); - kv.Value.TransferCmd = copy; - foreach (Block b in cce.NonNull(go.labelTargets)) { - Contract.Assert(b != null); - Block c; - if (copies.TryGetValue(b, out c)) { - copy.AddTarget(cce.NonNull(c)); - } - } - } else if (ret != null) { - kv.Value.TransferCmd = ret; - } else { - Contract.Assume(false); - throw new cce.UnreachableException(); - } - } - - copies.Clear(); - - return res; - } - - // check if e is true, false, !true, !false - // if so return true and the value of the expression in val - bool BooleanEval(Expr e, ref bool val) { - Contract.Requires(e != null); - LiteralExpr lit = e as LiteralExpr; - NAryExpr call = e as NAryExpr; - - if (lit != null && lit.isBool) { - val = lit.asBool; - return true; - } else if (call != null && - call.Fun is UnaryOperator && - ((UnaryOperator)call.Fun).Op == UnaryOperator.Opcode.Not && - BooleanEval(cce.NonNull(call.Args[0]), ref val)) { - val = !val; - return true; - } - // this is for the 0bv32 != 0bv32 generated by vcc - else if (call != null && - call.Fun is BinaryOperator && - ((BinaryOperator)call.Fun).Op == BinaryOperator.Opcode.Neq && - call.Args[0] is LiteralExpr && - cce.NonNull(call.Args[0]).Equals(call.Args[1])) { - val = false; - return true; - } - - return false; - } - - bool IsFalse(Expr e) { - Contract.Requires(e != null); - bool val = false; - return BooleanEval(e, ref val) && !val; - } - - bool CheckUnreachable(Block cur, List seq) - { - Contract.Requires(cur != null); - Contract.Requires(seq != null); - Contract.EnsuresOnThrow(true); - foreach (Cmd cmd in seq) - { - AssertCmd assrt = cmd as AssertCmd; - if (assrt != null && QKeyValue.FindBoolAttribute(assrt.Attributes, "PossiblyUnreachable")) - return false; - } - - DateTime start = DateTime.UtcNow; - if (CommandLineOptions.Clo.Trace) - { - System.Console.Write(" soundness smoke test #{0} ... ", id); - } - callback.OnProgress("smoke", id, id, 0.0); - - Token tok = new Token(); - tok.val = "soundness smoke test assertion"; - seq.Add(new AssertCmd(tok, Expr.False)); - Block copy = CopyBlock(cur); - Contract.Assert(copy != null); - copy.Cmds = seq; - List backup = impl.Blocks; - Contract.Assert(backup != null); - impl.Blocks = GetCopiedBlocks(); - copy.TransferCmd = new ReturnCmd(Token.NoToken); - if (CommandLineOptions.Clo.TraceVerify) - { - System.Console.WriteLine(); - System.Console.WriteLine(" --- smoke #{0}, before passify", id); - Emit(); - } - parent.CurrentLocalVariables = impl.LocVars; - ModelViewInfo mvInfo; - parent.PassifyImpl(impl, out mvInfo); - Dictionary label2Absy; - Checker ch = parent.FindCheckerFor(CommandLineOptions.Clo.SmokeTimeout); - Contract.Assert(ch != null); - - ProverInterface.Outcome outcome = ProverInterface.Outcome.Undetermined; - try - { - lock (ch) - { - var exprGen = ch.TheoremProver.Context.ExprGen; - VCExpr controlFlowVariableExpr = CommandLineOptions.Clo.UseLabels ? null : exprGen.Integer(BigNum.ZERO); - - VCExpr vc = parent.GenerateVC(impl, controlFlowVariableExpr, out label2Absy, ch.TheoremProver.Context); - Contract.Assert(vc != null); - - if (!CommandLineOptions.Clo.UseLabels) - { - VCExpr controlFlowFunctionAppl = exprGen.ControlFlowFunctionApplication(exprGen.Integer(BigNum.ZERO), exprGen.Integer(BigNum.ZERO)); - VCExpr eqExpr = exprGen.Eq(controlFlowFunctionAppl, exprGen.Integer(BigNum.FromInt(impl.Blocks[0].UniqueId))); - vc = exprGen.Implies(eqExpr, vc); - } - - impl.Blocks = backup; - - if (CommandLineOptions.Clo.TraceVerify) - { - System.Console.WriteLine(" --- smoke #{0}, after passify", id); - Emit(); - } - - ch.BeginCheck(cce.NonNull(impl.Name + "_smoke" + id++), vc, new ErrorHandler(label2Absy, this.callback)); - } - - ch.ProverTask.Wait(); - - lock (ch) - { - outcome = ch.ReadOutcome(); - } - } - finally - { - ch.GoBackToIdle(); - } - - parent.CurrentLocalVariables = null; - - DateTime end = DateTime.UtcNow; - TimeSpan elapsed = end - start; - if (CommandLineOptions.Clo.Trace) - { - System.Console.WriteLine(" [{0} s] {1}", elapsed.TotalSeconds, - outcome == ProverInterface.Outcome.Valid ? "OOPS" : - "OK" + (outcome == ProverInterface.Outcome.Invalid ? "" : " (" + outcome + ")")); - } - - if (outcome == ProverInterface.Outcome.Valid) - { - // copy it again, so we get the version with calls, assignments and such - copy = CopyBlock(cur); - copy.Cmds = seq; - impl.Blocks = GetCopiedBlocks(); - TopologicalSortImpl(); - callback.OnUnreachableCode(impl); - impl.Blocks = backup; - return true; - } - return false; - } - - const bool turnAssertIntoAssumes = false; - - void DFS(Block cur) { - Contract.Requires(cur != null); - Contract.EnsuresOnThrow(true); - if (visited.Contains(cur)) - return; - visited.Add(cur); - - List seq = new List(); - foreach (Cmd cmd_ in cur.Cmds) { - Cmd cmd = cmd_; - Contract.Assert(cmd != null); - AssertCmd assrt = cmd as AssertCmd; - AssumeCmd assm = cmd as AssumeCmd; - CallCmd call = cmd as CallCmd; - - bool assumeFalse = false; - - if (assrt != null) { - // we're not going any further - // it's clear the user expected unreachable code here - // it's not clear where did he expect it, maybe it would be right to insert - // a check just one command before - if (IsFalse(assrt.Expr)) - return; - -#if TURN_ASSERT_INFO_ASSUMES - if (turnAssertIntoAssumes) { - cmd = AssertTurnedIntoAssume(assrt); - } -#endif - } else if (assm != null) { - if (IsFalse(assm.Expr)) - assumeFalse = true; - } else if (call != null) { - foreach (Ensures e in (cce.NonNull(call.Proc)).Ensures) { - Contract.Assert(e != null); - if (IsFalse(e.Condition)) - assumeFalse = true; - } - } - - if (assumeFalse) { - CheckUnreachable(cur, seq); - return; - } - - seq.Add(cmd); - } - - - GotoCmd go = cur.TransferCmd as GotoCmd; - ReturnCmd ret = cur.TransferCmd as ReturnCmd; - - Contract.Assume(!(go != null && go.labelTargets == null && go.labelNames != null && go.labelNames.Count > 0)); - - 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); - } else if (go != null) { - bool needToCheck = true; - // if all of our children have more than one parent, then - // we're in the right place to check - foreach (Block target in cce.NonNull(go.labelTargets)) { - Contract.Assert(target != null); - if (target.Predecessors.Count == 1) { - needToCheck = false; - } - } - if (needToCheck) { - CheckUnreachable(cur, seq); - } - foreach (Block target in go.labelTargets) { - Contract.Assert(target != null); - DFS(target); - } - } - } - - class ErrorHandler : ProverInterface.ErrorHandler { - Dictionary label2Absy; - VerifierCallback callback; - [ContractInvariantMethod] - void ObjectInvariant() { - Contract.Invariant(label2Absy != null); - Contract.Invariant(callback != null); - } - - - public ErrorHandler(Dictionary label2Absy, VerifierCallback callback) { - Contract.Requires(label2Absy != null); - Contract.Requires(callback != null); - this.label2Absy = label2Absy; - this.callback = callback; - } - - public override Absy Label2Absy(string label) { - //Contract.Requires(label != null); - Contract.Ensures(Contract.Result() != null); - - int id = int.Parse(label); - return cce.NonNull((Absy)label2Absy[id]); - } - - public override void OnProverWarning(string msg) { - //Contract.Requires(msg != null); - this.callback.OnWarning(msg); - } - } - } - - - #endregion - - #region Splitter - class Split { - class BlockStats { - public bool big_block; - public int id; - public double assertion_cost; - public double assumption_cost; // before multiplier - public double incomming_paths; - public List/*!>!*/ virtual_successors = new List(); - public List/*!>!*/ virtual_predecesors = new List(); - public HashSet reachable_blocks; - public readonly Block block; - [ContractInvariantMethod] - void ObjectInvariant() { - Contract.Invariant(cce.NonNullElements(virtual_successors)); - Contract.Invariant(cce.NonNullElements(virtual_predecesors)); - Contract.Invariant(block != null); - } - - - public BlockStats(Block b, int i) { - Contract.Requires(b != null); - block = b; - assertion_cost = -1; - id = i; - } - } - [ContractInvariantMethod] - void ObjectInvariant() { - Contract.Invariant(cce.NonNullElements(blocks)); - Contract.Invariant(cce.NonNullElements(big_blocks)); - Contract.Invariant(cce.NonNullDictionaryAndValues(stats)); - Contract.Invariant(cce.NonNullElements(assumized_branches)); - Contract.Invariant(gotoCmdOrigins != null); - Contract.Invariant(parent != null); - Contract.Invariant(impl != null); - Contract.Invariant(copies != null); - Contract.Invariant(cce.NonNull(protected_from_assert_to_assume)); - Contract.Invariant(cce.NonNull(keep_at_all)); - } - - - readonly List blocks; - readonly List big_blocks = new List(); - readonly Dictionary/*!*/ stats = new Dictionary(); - readonly int id; - static int current_id = -1; - Block split_block; - bool assert_to_assume; - List/*!*/ assumized_branches = new List(); - - double score; - bool score_computed; - double total_cost; - int assertion_count; - double assertion_cost; // without multiplication by paths - Dictionary/*!*/ gotoCmdOrigins; - readonly public VCGen/*!*/ parent; - Implementation/*!*/ impl; - - Dictionary/*!*/ copies = new Dictionary(); - bool doing_slice; - double slice_initial_limit; - double slice_limit; - bool slice_pos; - HashSet/*!*/ protected_from_assert_to_assume = new HashSet(); - HashSet/*!*/ keep_at_all = new HashSet(); - - // async interface - private Checker checker; - private int splitNo; - internal ErrorReporter reporter; - - public Split(List/*!*/ blocks, Dictionary/*!*/ gotoCmdOrigins, VCGen/*!*/ par, Implementation/*!*/ impl) { - Contract.Requires(cce.NonNullElements(blocks)); - Contract.Requires(gotoCmdOrigins != null); - Contract.Requires(par != null); - Contract.Requires(impl != null); - this.blocks = blocks; - this.gotoCmdOrigins = gotoCmdOrigins; - this.parent = par; - this.impl = impl; - this.id = Interlocked.Increment(ref current_id); - } - - public double Cost { - get { - ComputeBestSplit(); - return total_cost; - } - } - - public bool LastChance { - get { - ComputeBestSplit(); - return assertion_count == 1 && score < 0; - } - } - - public string Stats { - get { - ComputeBestSplit(); - return string.Format("(cost:{0:0}/{1:0}{2})", total_cost, assertion_cost, LastChance ? " last" : ""); - } - } - - public void DumpDot(int no) { - using (System.IO.StreamWriter sw = System.IO.File.CreateText(string.Format("split.{0}.dot", no))) { - sw.WriteLine("digraph G {"); - - ComputeBestSplit(); - List saved = assumized_branches; - Contract.Assert(saved != null); - assumized_branches = new List(); - DoComputeScore(false); - assumized_branches = saved; - - foreach (Block b in big_blocks) { - Contract.Assert(b != null); - BlockStats s = GetBlockStats(b); - foreach (Block t in s.virtual_successors) { - Contract.Assert(t != null); - sw.WriteLine("n{0} -> n{1};", s.id, GetBlockStats(t).id); - } - sw.WriteLine("n{0} [label=\"{1}:\\n({2:0.0}+{3:0.0})*{4:0.0}\"{5}];", - s.id, b.Label, - s.assertion_cost, s.assumption_cost, s.incomming_paths, - s.assertion_cost > 0 ? ",shape=box" : ""); - - } - sw.WriteLine("}"); - sw.Close(); - } - - string filename = string.Format("split.{0}.bpl", no); - 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; - List backup = impl.Blocks; - Contract.Assert(backup != null); - impl.Blocks = blocks; - impl.Emit(new TokenTextWriter(filename, sw, /*setTokens=*/ false, /*pretty=*/ false), 0); - impl.Blocks = backup; - CommandLineOptions.Clo.PrintDesugarings = oldPrintDesugaringSetting; - CommandLineOptions.Clo.PrintUnstructured = oldPrintUnstructured; - } - } - - int bsid; - BlockStats GetBlockStats(Block b) { - Contract.Requires(b != null); - Contract.Ensures(Contract.Result() != null); - - BlockStats s; - if (!stats.TryGetValue(b, out s)) { - s = new BlockStats(b, bsid++); - stats[b] = s; - } - return cce.NonNull(s); - } - - double AssertionCost(PredicateCmd c) { - return 1.0; - } - - void CountAssertions(Block b) { - Contract.Requires(b != null); - BlockStats s = GetBlockStats(b); - if (s.assertion_cost >= 0) - return; // already done - s.big_block = true; - s.assertion_cost = 0; - s.assumption_cost = 0; - foreach (Cmd c in b.Cmds) { - if (c is AssertCmd) { - double cost = AssertionCost((AssertCmd)c); - s.assertion_cost += cost; - assertion_count++; - assertion_cost += cost; - } else if (c is AssumeCmd) { - s.assumption_cost += AssertionCost((AssumeCmd)c); - } - } - foreach (Block c in Exits(b)) { - Contract.Assert(c != null); - s.virtual_successors.Add(c); - } - if (s.virtual_successors.Count == 1) { - Block next = s.virtual_successors[0]; - BlockStats se = GetBlockStats(next); - CountAssertions(next); - if (next.Predecessors.Count > 1 || se.virtual_successors.Count != 1) - return; - s.virtual_successors[0] = se.virtual_successors[0]; - s.assertion_cost += se.assertion_cost; - s.assumption_cost += se.assumption_cost; - se.big_block = false; - } - } - - HashSet/*!*/ ComputeReachableNodes(Block/*!*/ b) { - Contract.Requires(b != null); - Contract.Ensures(cce.NonNull(Contract.Result>())); - BlockStats s = GetBlockStats(b); - if (s.reachable_blocks != null) { - return s.reachable_blocks; - } - HashSet blocks = new HashSet(); - s.reachable_blocks = blocks; - blocks.Add(b); - foreach (Block/*!*/ succ in Exits(b)) { - Contract.Assert(succ != null); - foreach (Block r in ComputeReachableNodes(succ)) { - Contract.Assert(r != null); - blocks.Add(r); - } - } - return blocks; - } - - double ProverCost(double vc_cost) { - return vc_cost * vc_cost; - } - - void ComputeBestSplit() { - if (score_computed) - return; - score_computed = true; - - assertion_count = 0; - - foreach (Block b in blocks) { - Contract.Assert(b != null); - CountAssertions(b); - } - - foreach (Block b in blocks) { - Contract.Assert(b != null); - BlockStats bs = GetBlockStats(b); - if (bs.big_block) { - big_blocks.Add(b); - foreach (Block ch in bs.virtual_successors) { - Contract.Assert(ch != null); - BlockStats chs = GetBlockStats(ch); - if (!chs.big_block) { - Console.WriteLine("non-big {0} accessed from {1}", ch, b); - DumpDot(-1); - Contract.Assert(false); - throw new cce.UnreachableException(); - } - chs.virtual_predecesors.Add(b); - } - } - } - - assumized_branches.Clear(); - total_cost = ProverCost(DoComputeScore(false)); - - score = double.PositiveInfinity; - Block best_split = null; - List saved_branches = new List(); - - foreach (Block b in big_blocks) { - Contract.Assert(b != null); - GotoCmd gt = b.TransferCmd as GotoCmd; - if (gt == null) - continue; - List targ = cce.NonNull(gt.labelTargets); - if (targ.Count < 2) - continue; - // caution, we only consider two first exits - - double left0, right0, left1, right1; - split_block = b; - - assumized_branches.Clear(); - assumized_branches.Add(cce.NonNull(targ[0])); - left0 = DoComputeScore(true); - right0 = DoComputeScore(false); - - assumized_branches.Clear(); - for (int idx = 1; idx < targ.Count; idx++) { - assumized_branches.Add(cce.NonNull(targ[idx])); - } - left1 = DoComputeScore(true); - right1 = DoComputeScore(false); - - double current_score = ProverCost(left1) + ProverCost(right1); - double other_score = ProverCost(left0) + ProverCost(right0); - - if (other_score < current_score) { - current_score = other_score; - assumized_branches.Clear(); - assumized_branches.Add(cce.NonNull(targ[0])); - } - - if (current_score < score) { - score = current_score; - best_split = split_block; - saved_branches.Clear(); - saved_branches.AddRange(assumized_branches); - } - } - - if (CommandLineOptions.Clo.VcsPathSplitMult * score > total_cost) { - split_block = null; - score = -1; - } else { - assumized_branches = saved_branches; - split_block = best_split; - } - } - - void UpdateIncommingPaths(BlockStats s) { - Contract.Requires(s != null); - if (s.incomming_paths < 0.0) { - int count = 0; - s.incomming_paths = 0.0; - if (!keep_at_all.Contains(s.block)) - return; - foreach (Block b in s.virtual_predecesors) { - Contract.Assert(b != null); - BlockStats ch = GetBlockStats(b); - Contract.Assert(ch != null); - UpdateIncommingPaths(ch); - if (ch.incomming_paths > 0.0) { - s.incomming_paths += ch.incomming_paths; - count++; - } - } - if (count > 1) { - s.incomming_paths *= CommandLineOptions.Clo.VcsPathJoinMult; - } - } - } - - void ComputeBlockSetsHelper(Block b, bool allow_small) { - Contract.Requires(b != null); - if (keep_at_all.Contains(b)) - return; - keep_at_all.Add(b); - - if (allow_small) { - foreach (Block ch in Exits(b)) { - Contract.Assert(ch != null); - if (b == split_block && assumized_branches.Contains(ch)) - continue; - ComputeBlockSetsHelper(ch, allow_small); - } - } else { - foreach (Block ch in GetBlockStats(b).virtual_successors) { - Contract.Assert(ch != null); - if (b == split_block && assumized_branches.Contains(ch)) - continue; - ComputeBlockSetsHelper(ch, allow_small); - } - } - } - - void ComputeBlockSets(bool allow_small) { - protected_from_assert_to_assume.Clear(); - keep_at_all.Clear(); - - Debug.Assert(split_block == null || GetBlockStats(split_block).big_block); - Debug.Assert(GetBlockStats(blocks[0]).big_block); - - if (assert_to_assume) { - foreach (Block b in allow_small ? blocks : big_blocks) { - Contract.Assert(b != null); - if (ComputeReachableNodes(b).Contains(cce.NonNull(split_block))) { - keep_at_all.Add(b); - } - } - - foreach (Block b in assumized_branches) { - Contract.Assert(b != null); - foreach (Block r in ComputeReachableNodes(b)) { - Contract.Assert(r != null); - if (allow_small || GetBlockStats(r).big_block) { - keep_at_all.Add(r); - protected_from_assert_to_assume.Add(r); - } - } - } - } else { - ComputeBlockSetsHelper(blocks[0], allow_small); - } - } - - bool ShouldAssumize(Block b) { - Contract.Requires(b != null); - return assert_to_assume && !protected_from_assert_to_assume.Contains(b); - } - - double DoComputeScore(bool aa) { - assert_to_assume = aa; - ComputeBlockSets(false); - - foreach (Block b in big_blocks) { - Contract.Assert(b != null); - GetBlockStats(b).incomming_paths = -1.0; - } - - GetBlockStats(blocks[0]).incomming_paths = 1.0; - - double cost = 0.0; - foreach (Block b in big_blocks) { - Contract.Assert(b != null); - if (keep_at_all.Contains(b)) { - BlockStats s = GetBlockStats(b); - UpdateIncommingPaths(s); - double local = s.assertion_cost; - if (ShouldAssumize(b)) { - local = (s.assertion_cost + s.assumption_cost) * CommandLineOptions.Clo.VcsAssumeMult; - } else { - local = s.assumption_cost * CommandLineOptions.Clo.VcsAssumeMult + s.assertion_cost; - } - local = local + local * s.incomming_paths * CommandLineOptions.Clo.VcsPathCostMult; - cost += local; - } - } - - return cost; - } - - List SliceCmds(Block b) { - Contract.Requires(b != null); - Contract.Ensures(Contract.Result>() != null); - - List seq = b.Cmds; - Contract.Assert(seq != null); - if (!doing_slice && !ShouldAssumize(b)) - return seq; - List res = new List(); - foreach (Cmd c in seq) { - Contract.Assert(c != null); - AssertCmd a = c as AssertCmd; - Cmd the_new = c; - bool swap = false; - if (a != null) { - if (doing_slice) { - double cost = AssertionCost(a); - bool first = (slice_limit - cost) >= 0 || slice_initial_limit == slice_limit; - slice_limit -= cost; - swap = slice_pos == first; - } else if (assert_to_assume) { - swap = true; - } else { - Contract.Assert(false); - throw new cce.UnreachableException(); - } - - if (swap) { - the_new = AssertTurnedIntoAssume(a); - } - } - res.Add(the_new); - } - return res; - } - - Block CloneBlock(Block b) { - Contract.Requires(b != null); - Contract.Ensures(Contract.Result() != null); - - Block res; - if (copies.TryGetValue(b, out res)) { - return cce.NonNull(res); - } - res = new Block(b.tok, b.Label, SliceCmds(b), b.TransferCmd); - GotoCmd gt = b.TransferCmd as GotoCmd; - copies[b] = res; - if (gt != null) { - GotoCmd newGoto = new GotoCmd(gt.tok, new List(), new List()); - res.TransferCmd = newGoto; - int pos = 0; - foreach (Block ch in cce.NonNull(gt.labelTargets)) { - Contract.Assert(ch != null); - Contract.Assert(doing_slice || - (assert_to_assume || (keep_at_all.Contains(ch) || assumized_branches.Contains(ch)))); - if (doing_slice || - ((b != split_block || assumized_branches.Contains(ch) == assert_to_assume) && - keep_at_all.Contains(ch))) { - newGoto.AddTarget(CloneBlock(ch)); - } - pos++; - } - } - return res; - } - - Split DoSplit() { - Contract.Ensures(Contract.Result() != null); - - copies.Clear(); - CloneBlock(blocks[0]); - List newBlocks = new List(); - Dictionary newGotoCmdOrigins = new Dictionary(); - foreach (Block b in blocks) { - Contract.Assert(b != null); - Block tmp; - if (copies.TryGetValue(b, out tmp)) { - newBlocks.Add(cce.NonNull(tmp)); - if (gotoCmdOrigins.ContainsKey(b.TransferCmd)) { - newGotoCmdOrigins[tmp.TransferCmd] = gotoCmdOrigins[b.TransferCmd]; - } - - foreach (Block p in b.Predecessors) { - Contract.Assert(p != null); - Block tmp2; - if (copies.TryGetValue(p, out tmp2)) { - tmp.Predecessors.Add(tmp2); - } - } - } - } - - return new Split(newBlocks, newGotoCmdOrigins, parent, impl); - } - - Split SplitAt(int idx) { - Contract.Ensures(Contract.Result() != null); - - assert_to_assume = idx == 0; - doing_slice = false; - ComputeBlockSets(true); - - return DoSplit(); - } - - Split SliceAsserts(double limit, bool pos) { - Contract.Ensures(Contract.Result() != null); - - slice_pos = pos; - slice_limit = limit; - slice_initial_limit = limit; - doing_slice = true; - Split r = DoSplit(); - /* - Console.WriteLine("split {0} / {1} -->", limit, pos); - List tmp = impl.Blocks; - impl.Blocks = r.blocks; - EmitImpl(impl, false); - impl.Blocks = tmp; - */ - - return r; - } - - void Print() { - List tmp = impl.Blocks; - Contract.Assert(tmp != null); - impl.Blocks = blocks; - EmitImpl(impl, false); - impl.Blocks = tmp; - } - - public Counterexample ToCounterexample(ProverContext context) { - Contract.Requires(context != null); - Contract.Ensures(Contract.Result() != null); - - List trace = new List(); - foreach (Block b in blocks) { - Contract.Assert(b != null); - trace.Add(b); - } - foreach (Block b in blocks) { - Contract.Assert(b != null); - foreach (Cmd c in b.Cmds) { - Contract.Assert(c != null); - if (c is AssertCmd) { - return AssertCmdToCounterexample((AssertCmd)c, cce.NonNull(b.TransferCmd), trace, null, null, context); - } - } - } - Contract.Assume(false); - throw new cce.UnreachableException(); - } - - /// - /// Starting from the 0-index "split_here" annotation in begin, verifies until it reaches a subsequent "split_here" annotation - /// Returns a list of blocks where all code not verified has asserts converted into assumes - /// - /// Implementation's collection of blocks - /// Block containing the first split_here from which to start verifying - /// 0-based ID of the "split_here" annotation within begin at which to start verifying - /// True if the entire split is contained within block begin - /// Set of all blocks containing a "split_here" annotation - /// - // Note: Current implementation may over report errors. - // For example, if the control flow graph is a diamond (e.g., A -> B, C, B->D, C->D), - // and there is a split in B and an error in D, then D will be verified twice and hence report the error twice. - // Best solution may be to memoize blocks that have been fully verified and be sure not to verify them again - private static List DoManualSplit(List blocks, Block begin, int begin_split_id, bool blockInternalSplit, IEnumerable endPoints) { - // Compute the set of blocks reachable from begin but not included in endPoints. These will be verified in their entirety. - var blocksToVerifyEntirely = new HashSet(); - var reachableEndPoints = new HashSet(); // Reachable end points will be verified up to their first split point - var todo = new Stack(); - todo.Push(begin); - while (todo.Count > 0) { - var currentBlock = todo.Pop(); - if (blocksToVerifyEntirely.Contains(currentBlock)) continue; - blocksToVerifyEntirely.Add(currentBlock); - var exit = currentBlock.TransferCmd as GotoCmd; - if (exit != null) - foreach (Block targetBlock in exit.labelTargets) { - if (!endPoints.Contains(targetBlock)) { - todo.Push(targetBlock); - } else { - reachableEndPoints.Add(targetBlock); - } - } - - } - blocksToVerifyEntirely.Remove(begin); - - // Convert assumes to asserts in "unreachable" blocks, including portions of blocks containing "split_here" - var newBlocks = new List(blocks.Count()); // Copies of the original blocks - var duplicator = new Duplicator(); - var oldToNewBlockMap = new Dictionary(blocks.Count()); // Maps original blocks to their new copies in newBlocks - - foreach (var currentBlock in blocks) { - var newBlock = (Block)duplicator.VisitBlock(currentBlock); - oldToNewBlockMap[currentBlock] = newBlock; - newBlocks.Add(newBlock); - - if (!blockInternalSplit && blocksToVerifyEntirely.Contains(currentBlock)) continue; // All reachable blocks must be checked in their entirety, so don't change anything - // Otherwise, we only verify a portion of the current block, so we'll need to look at each of its commands - - // !verify -> convert assert to assume - var verify = (currentBlock == begin && begin_split_id == -1) // -1 tells us to start verifying from the very beginning (i.e., there is no split in the begin block) - || (reachableEndPoints.Contains(currentBlock) // This endpoint is reachable from begin, so we verify until we hit the first split point - && !blockInternalSplit); // Don't bother verifying if all of the splitting is within the begin block - var newCmds = new List(); - var split_here_count = 0; - - foreach (Cmd c in currentBlock.Cmds) { - var p = c as PredicateCmd; - if (p != null && QKeyValue.FindBoolAttribute(p.Attributes, "split_here")) { - if (currentBlock == begin) { // Verify everything between the begin_split_id we were given and the next split - if (split_here_count == begin_split_id) { - verify = true; - } else if (split_here_count == begin_split_id + 1) { - verify = false; - } - } else { // We're in an endPoint so we stop verifying as soon as we hit a "split_here" - verify = false; - } - split_here_count++; - } - - var asrt = c as AssertCmd; - if (verify || asrt == null) - newCmds.Add(c); - else - newCmds.Add(AssertTurnedIntoAssume(asrt)); - } - - newBlock.Cmds = newCmds; - } - - // Patch the edges between the new blocks - foreach (var oldBlock in blocks) { - if (oldBlock.TransferCmd is ReturnCmd) { continue; } - var gotoCmd = (GotoCmd)oldBlock.TransferCmd; - var newLabelTargets = new List(gotoCmd.labelTargets.Count()); - var newLabelNames = new List(gotoCmd.labelTargets.Count()); - foreach (var target in gotoCmd.labelTargets) { - newLabelTargets.Add(oldToNewBlockMap[target]); - newLabelNames.Add(oldToNewBlockMap[target].Label); - } - oldToNewBlockMap[oldBlock].TransferCmd = new GotoCmd(gotoCmd.tok, newLabelNames, newLabelTargets); - } - - return newBlocks; - } - - public static List FindManualSplits(Implementation/*!*/ impl, Dictionary/*!*/ gotoCmdOrigins, VCGen/*!*/ par) { - Contract.Requires(impl != null); - Contract.Ensures(Contract.Result>() == null || cce.NonNullElements(Contract.Result>())); - - var splitPoints = new Dictionary(); - foreach (var b in impl.Blocks) { - foreach (Cmd c in b.Cmds) { - var p = c as PredicateCmd; - if (p != null && QKeyValue.FindBoolAttribute(p.Attributes, "split_here")) { - int count; - splitPoints.TryGetValue(b, out count); - splitPoints[b] = count + 1; - } - } - } - - if (splitPoints.Count() == 0) { // No manual split points here - return null; - } - - List splits = new List(); - Block entryPoint = impl.Blocks[0]; - var newEntryBlocks = DoManualSplit(impl.Blocks, entryPoint, -1, splitPoints.Keys.Contains(entryPoint), splitPoints.Keys); - splits.Add(new Split(newEntryBlocks, gotoCmdOrigins, par, impl)); // REVIEW: Does gotoCmdOrigins need to be changed at all? - - foreach (KeyValuePair pair in splitPoints) { - for (int i = 0; i < pair.Value; i++) { - bool blockInternalSplit = i < pair.Value - 1; // There's at least one more split, after this one, in the current block - var newBlocks = DoManualSplit(impl.Blocks, pair.Key, i, blockInternalSplit, splitPoints.Keys); - Split s = new Split(newBlocks, gotoCmdOrigins, par, impl); // REVIEW: Does gotoCmdOrigins need to be changed at all? - splits.Add(s); - } - } - - return splits; - } - - public static List/*!*/ DoSplit(Split initial, double max_cost, int max) { - Contract.Requires(initial != null); - Contract.Ensures(cce.NonNullElements(Contract.Result>())); - - List res = new List(); - res.Add(initial); - - while (res.Count < max) { - Split best = null; - int best_idx = 0, pos = 0; - foreach (Split s in res) { - Contract.Assert(s != null); - s.ComputeBestSplit(); // TODO check total_cost first - if (s.total_cost > max_cost && - (best == null || best.total_cost < s.total_cost) && - (s.assertion_count > 1 || s.split_block != null)) { - best = s; - best_idx = pos; - } - pos++; - } - - if (best == null) - break; // no split found - - Split s0, s1; - - bool split_stats = CommandLineOptions.Clo.TraceVerify; - - if (split_stats) { - Console.WriteLine("{0} {1} -->", best.split_block == null ? "SLICE" : ("SPLIT@" + best.split_block.Label), best.Stats); - if (best.split_block != null) { - GotoCmd g = best.split_block.TransferCmd as GotoCmd; - if (g != null) { - Console.Write(" exits: "); - foreach (Block b in cce.NonNull(g.labelTargets)) { - Contract.Assert(b != null); - Console.Write("{0} ", b.Label); - } - Console.WriteLine(""); - Console.Write(" assumized: "); - foreach (Block b in best.assumized_branches) { - Contract.Assert(b != null); - Console.Write("{0} ", b.Label); - } - Console.WriteLine(""); - } - } - } - - if (best.split_block != null) { - s0 = best.SplitAt(0); - s1 = best.SplitAt(1); - } else { - best.split_block = null; - s0 = best.SliceAsserts(best.assertion_cost / 2, true); - s1 = best.SliceAsserts(best.assertion_cost / 2, false); - } - - if (true) { - List ss = new List(); - ss.Add(s0.blocks[0]); - ss.Add(s1.blocks[0]); - try { - best.SoundnessCheck(new HashSet>(new BlockListComparer()), best.blocks[0], ss); - } catch (System.Exception e) { - Console.WriteLine(e); - best.DumpDot(-1); - s0.DumpDot(-2); - s1.DumpDot(-3); - Contract.Assert(false); - throw new cce.UnreachableException(); - } - } - - if (split_stats) { - s0.ComputeBestSplit(); - s1.ComputeBestSplit(); - Console.WriteLine(" --> {0}", s0.Stats); - Console.WriteLine(" --> {0}", s1.Stats); - } - - if (CommandLineOptions.Clo.TraceVerify) { - best.Print(); - } - - res[best_idx] = s0; - res.Add(s1); - } - - return res; - } - - class BlockListComparer : IEqualityComparer> - { - public bool Equals(List x, List y) - { - return x == y || x.SequenceEqual(y); - } - - public int GetHashCode(List obj) - { - int h = 0; - Contract.Assume(obj != null); - foreach (var b in obj) - { - if (b != null) - { - h += b.GetHashCode(); - } - } - return h; - } - } - - public Checker Checker { - get { - Contract.Ensures(Contract.Result() != null); - - Contract.Assert(checker != null); - return checker; - } - } - - public Task ProverTask { - get { - Contract.Assert(checker != null); - return checker.ProverTask; - } - } - - public void ReadOutcome(ref Outcome cur_outcome, out bool prover_failed) { - Contract.EnsuresOnThrow(true); - ProverInterface.Outcome outcome = cce.NonNull(checker).ReadOutcome(); - - if (CommandLineOptions.Clo.Trace && splitNo >= 0) { - System.Console.WriteLine(" --> split #{0} done, [{1} s] {2}", splitNo, checker.ProverRunTime.TotalSeconds, outcome); - } - - if (CommandLineOptions.Clo.VcsDumpSplits) { - DumpDot(splitNo); - } - - prover_failed = false; - - switch (outcome) { - case ProverInterface.Outcome.Valid: - return; - case ProverInterface.Outcome.Invalid: - cur_outcome = Outcome.Errors; - return; - case ProverInterface.Outcome.OutOfMemory: - prover_failed = true; - if (cur_outcome != Outcome.Errors && cur_outcome != Outcome.Inconclusive) - cur_outcome = Outcome.OutOfMemory; - return; - case ProverInterface.Outcome.TimeOut: - prover_failed = true; - if (cur_outcome != Outcome.Errors && cur_outcome != Outcome.Inconclusive) - cur_outcome = Outcome.TimedOut; - return; - case ProverInterface.Outcome.Undetermined: - if (cur_outcome != Outcome.Errors) - cur_outcome = Outcome.Inconclusive; - return; - default: - Contract.Assert(false); - throw new cce.UnreachableException(); - } - } - - /// - /// As a side effect, updates "this.parent.CumulativeAssertionCount". - /// - public void BeginCheck(Checker checker, VerifierCallback callback, ModelViewInfo mvInfo, int no, int timeout) - { - Contract.Requires(checker != null); - Contract.Requires(callback != null); - - splitNo = no; - - impl.Blocks = blocks; - - this.checker = checker; - - Dictionary label2absy = new Dictionary(); - - ProverContext ctx = checker.TheoremProver.Context; - Boogie2VCExprTranslator bet = ctx.BoogieExprTranslator; - CodeExprConversionClosure cc = new CodeExprConversionClosure(label2absy, ctx); - bet.SetCodeExprConverter(cc.CodeExprToVerificationCondition); - - var exprGen = ctx.ExprGen; - VCExpr controlFlowVariableExpr = CommandLineOptions.Clo.UseLabels ? null : exprGen.Integer(BigNum.ZERO); - - VCExpr vc = parent.GenerateVCAux(impl, controlFlowVariableExpr, label2absy, checker.TheoremProver.Context); - Contract.Assert(vc != null); - - if (!CommandLineOptions.Clo.UseLabels) - { - VCExpr controlFlowFunctionAppl = exprGen.ControlFlowFunctionApplication(exprGen.Integer(BigNum.ZERO), exprGen.Integer(BigNum.ZERO)); - VCExpr eqExpr = exprGen.Eq(controlFlowFunctionAppl, exprGen.Integer(BigNum.FromInt(impl.Blocks[0].UniqueId))); - vc = exprGen.Implies(eqExpr, vc); - } - - if (CommandLineOptions.Clo.vcVariety == CommandLineOptions.VCVariety.Local) - { - reporter = new ErrorReporterLocal(gotoCmdOrigins, label2absy, impl.Blocks, parent.incarnationOriginMap, callback, mvInfo, cce.NonNull(this.Checker.TheoremProver.Context), parent.program); - } - else - { - reporter = new ErrorReporter(gotoCmdOrigins, label2absy, impl.Blocks, parent.incarnationOriginMap, callback, mvInfo, this.Checker.TheoremProver.Context, parent.program); - } - - if (CommandLineOptions.Clo.TraceVerify && no >= 0) - { - Console.WriteLine("-- after split #{0}", no); - Print(); - } - - string desc = cce.NonNull(impl.Name); - if (no >= 0) - desc += "_split" + no; - checker.BeginCheck(desc, vc, reporter); - } - - private void SoundnessCheck(HashSet/*!*/>/*!*/ cache, Block/*!*/ orig, List/*!*/ copies) { - Contract.Requires(cce.NonNull(cache)); - Contract.Requires(orig != null); - Contract.Requires(copies != null); - { - var t = new List { orig }; - foreach (Block b in copies) { - Contract.Assert(b != null); - t.Add(b); - } - if (cache.Contains(t)) { - return; - } - cache.Add(t); - } - - for (int i = 0; i < orig.Cmds.Count; ++i) { - Cmd cmd = orig.Cmds[i]; - if (cmd is AssertCmd) { - int found = 0; - foreach (Block c in copies) { - Contract.Assert(c != null); - if (c.Cmds[i] == cmd) { - found++; - } - } - if (found == 0) { - throw new System.Exception(string.Format("missing assertion: {0}({1})", cmd.tok.filename, cmd.tok.line)); - } - } - } - - foreach (Block exit in Exits(orig)) { - Contract.Assert(exit != null); - List newcopies = new List(); - foreach (Block c in copies) { - foreach (Block cexit in Exits(c)) { - Contract.Assert(cexit != null); - if (cexit.Label == exit.Label) { - newcopies.Add(cexit); - } - } - } - if (newcopies.Count == 0) { - throw new System.Exception("missing exit " + exit.Label); - } - SoundnessCheck(cache, exit, newcopies); - } - } - } - #endregion - - - public class CodeExprConversionClosure - { - Dictionary label2absy; - ProverContext ctx; - public CodeExprConversionClosure(Dictionary label2absy, ProverContext ctx) - { - this.label2absy = label2absy; - this.ctx = ctx; - } - - public VCExpr CodeExprToVerificationCondition(CodeExpr codeExpr, Hashtable blockVariables, List bindings, bool isPositiveContext) - { - VCGen vcgen = new VCGen(new Program(), null, false, new List()); - vcgen.variable2SequenceNumber = new Dictionary(); - vcgen.incarnationOriginMap = new Dictionary(); - vcgen.CurrentLocalVariables = codeExpr.LocVars; - - ResetPredecessors(codeExpr.Blocks); - vcgen.AddBlocksBetween(codeExpr.Blocks); - Dictionary gotoCmdOrigins = vcgen.ConvertBlocks2PassiveCmd(codeExpr.Blocks, new List(), new ModelViewInfo(codeExpr)); - int ac; // computed, but then ignored for this CodeExpr - VCExpr startCorrect = VCGen.LetVCIterative(codeExpr.Blocks, null, label2absy, ctx, out ac, isPositiveContext); - VCExpr vce = ctx.ExprGen.Let(bindings, startCorrect); - if (vcgen.CurrentLocalVariables.Count != 0) - { - Boogie2VCExprTranslator translator = ctx.BoogieExprTranslator; - List boundVars = new List(); - foreach (Variable v in vcgen.CurrentLocalVariables) - { - Contract.Assert(v != null); - VCExprVar ev = translator.LookupVariable(v); - Contract.Assert(ev != null); - boundVars.Add(ev); - if (v.TypedIdent.Type.Equals(Bpl.Type.Bool)) - { - // add an antecedent (tickleBool ev) to help the prover find a possible trigger - vce = ctx.ExprGen.Implies(ctx.ExprGen.Function(VCExpressionGenerator.TickleBoolOp, ev), vce); - } - } - vce = ctx.ExprGen.Forall(boundVars, new List(), vce); - } - if (isPositiveContext) - { - vce = ctx.ExprGen.Not(vce); - } - return vce; - } - } - - public VCExpr GenerateVC(Implementation/*!*/ impl, VCExpr controlFlowVariableExpr, out Dictionary/*!*/ label2absy, ProverContext proverContext) - { - Contract.Requires(impl != null); - Contract.Requires(proverContext != null); - Contract.Ensures(Contract.ValueAtReturn(out label2absy) != null); - Contract.Ensures(Contract.Result() != null); - - label2absy = new Dictionary(); - return GenerateVCAux(impl, controlFlowVariableExpr, label2absy, proverContext); - } - - public VCExpr GenerateVCAux(Implementation/*!*/ impl, VCExpr controlFlowVariableExpr, Dictionary/*!*/ label2absy, ProverContext proverContext) { - Contract.Requires(impl != null); - Contract.Requires(proverContext != null); - Contract.Ensures(Contract.Result() != null); - - TypecheckingContext tc = new TypecheckingContext(null); - impl.Typecheck(tc); - - VCExpr vc; - int assertionCount; - switch (CommandLineOptions.Clo.vcVariety) { - case CommandLineOptions.VCVariety.Structured: - vc = VCViaStructuredProgram(impl, label2absy, proverContext, out assertionCount); - break; - case CommandLineOptions.VCVariety.Block: - vc = FlatBlockVC(impl, label2absy, false, false, false, proverContext, out assertionCount); - break; - case CommandLineOptions.VCVariety.BlockReach: - vc = FlatBlockVC(impl, label2absy, false, true, false, proverContext, out assertionCount); - break; - case CommandLineOptions.VCVariety.Local: - vc = FlatBlockVC(impl, label2absy, true, false, false, proverContext, out assertionCount); - break; - case CommandLineOptions.VCVariety.BlockNested: - vc = NestedBlockVC(impl, label2absy, false, proverContext, out assertionCount); - break; - case CommandLineOptions.VCVariety.BlockNestedReach: - vc = NestedBlockVC(impl, label2absy, true, proverContext, out assertionCount); - break; - case CommandLineOptions.VCVariety.Dag: - if (cce.NonNull(CommandLineOptions.Clo.TheProverFactory).SupportsDags || CommandLineOptions.Clo.FixedPointEngine != null) { - vc = DagVC(cce.NonNull(impl.Blocks[0]), controlFlowVariableExpr, label2absy, new Hashtable/**/(), proverContext, out assertionCount); - } else { - vc = LetVC(cce.NonNull(impl.Blocks[0]), controlFlowVariableExpr, label2absy, proverContext, out assertionCount); - } - break; - case CommandLineOptions.VCVariety.DagIterative: - vc = LetVCIterative(impl.Blocks, controlFlowVariableExpr, label2absy, proverContext, out assertionCount); - break; - case CommandLineOptions.VCVariety.Doomed: - vc = FlatBlockVC(impl, label2absy, false, false, true, proverContext, out assertionCount); - break; - default: - Contract.Assert(false); - throw new cce.UnreachableException(); // unexpected enumeration value - } - CumulativeAssertionCount += assertionCount; - return vc; - } - - void CheckIntAttributeOnImpl(Implementation impl, string name, ref int val) { - Contract.Requires(impl != null); - Contract.Requires(name != null); - if (!(cce.NonNull(impl.Proc).CheckIntAttribute(name, ref val) || !impl.CheckIntAttribute(name, ref val))) { - Console.WriteLine("ignoring ill-formed {:{0} ...} attribute on {1}, parameter should be an int", name, impl.Name); - } - } - - public override Outcome VerifyImplementation(Implementation/*!*/ impl, VerifierCallback/*!*/ callback) { - Contract.EnsuresOnThrow(true); - - if (impl.SkipVerification) { - return Outcome.Inconclusive; // not sure about this one - } - - callback.OnProgress("VCgen", 0, 0, 0.0); - - Stopwatch watch = new Stopwatch(); - if (_print_time) - { - Console.WriteLine("Checking function {0}", impl.Name); - watch.Reset(); - watch.Start(); - } - - ConvertCFG2DAG(impl); - - SmokeTester smoke_tester = null; - if (CommandLineOptions.Clo.SoundnessSmokeTest) { - smoke_tester = new SmokeTester(this, impl, callback); - smoke_tester.Copy(); - } - - ModelViewInfo mvInfo; - var gotoCmdOrigins = PassifyImpl(impl, out mvInfo); - - // If "expand" attribute is supplied, expand any assertion of conjunctions into multiple assertions, one per conjunct - foreach (var b in impl.Blocks) - { - List newCmds = new List(); - bool changed = false; - foreach (var c in b.Cmds) - { - var a = c as AssertCmd; - var ar = c as AssertRequiresCmd; - var ae = c as AssertEnsuresCmd; - var ai = c as LoopInitAssertCmd; - var am = c as LoopInvMaintainedAssertCmd; - // TODO: - //use Duplicator and Substituter rather than new - //nested IToken? - //document expand attribute (search for {:ignore}, for example) - //fix up new CallCmd, new Requires, new Ensures in OwickiGries.cs - Func withType = (Expr from, Expr to) => - { - NAryExpr nFrom = from as NAryExpr; - NAryExpr nTo = to as NAryExpr; - to.Type = from.Type; - if (nFrom != null && nTo != null) nTo.TypeParameters = nFrom.TypeParameters; - return to; - }; - - Action> traverse = null; - traverse = (depth, e, act) => - { - ForallExpr forall = e as ForallExpr; - NAryExpr nary = e as NAryExpr; - if (forall != null) - { - traverse(depth, forall.Body, e1 => act(withType(forall, - new ForallExpr(e1.tok, forall.TypeParameters, forall.Dummies, forall.Attributes, forall.Triggers, e1)))); - return; - } - if (nary != null) - { - var args = nary.Args; - IAppliable fun = nary.Fun; - BinaryOperator bop = fun as BinaryOperator; - FunctionCall call = fun as FunctionCall; - if (bop != null) - { - switch (bop.Op) - { - case BinaryOperator.Opcode.And: - traverse(depth, args[0], act); - traverse(depth, args[1], act); - return; - case BinaryOperator.Opcode.Imp: - traverse(depth, args[1], e1 => act(withType(nary, - new NAryExpr(e1.tok, fun, new List() { args[0], e1 })))); - return; - } - } - if (depth > 0 && call != null && call.Func != null) - { - Function cf = call.Func; - Expr body = cf.Body; - List ins = cf.InParams; - if (body == null && cf.DefinitionAxiom != null) - { - ForallExpr all = cf.DefinitionAxiom.Expr as ForallExpr; - if (all != null) - { - NAryExpr def = all.Body as NAryExpr; - if (def != null && def.Fun is BinaryOperator && ((BinaryOperator) (def.Fun)).Op == BinaryOperator.Opcode.Iff) - { - body = def.Args[1]; - ins = all.Dummies; - } - } - } - if (body != null) - { - Func new_f = e1 => - { - Function f = new Function(cf.tok, "expand<" + cf.Name + ">", cf.TypeParameters, ins, cf.OutParams[0], cf.Comment); - f.Body = e1; - Token tok = new Token(e1.tok.line, e1.tok.col); - tok.filename = e.tok.filename + "(" + e.tok.line + "," + e.tok.col + ") --> " + e1.tok.filename; - return withType(nary, new NAryExpr(tok, new FunctionCall(f), args)); - }; - traverse(depth - 1, body, e1 => act(new_f(e1))); - return; - } - } - } - act(e); - }; - - if (a != null) - { - var attr = a.Attributes; - if (ar != null && ar.Requires.Attributes != null) attr = ar.Requires.Attributes; - if (ar != null && ar.Call.Attributes != null) attr = ar.Call.Attributes; - if (ae != null && ae.Ensures.Attributes != null) attr = ae.Ensures.Attributes; - if (QKeyValue.FindExprAttribute(attr, "expand") != null || QKeyValue.FindBoolAttribute(attr, "expand")) - { - int depth = QKeyValue.FindIntAttribute(attr, "expand", 100); - Func fe = e => Expr.Or(a.Expr, e); - //traverse(depth, a.Expr, e => System.Console.WriteLine(e.GetType() + " :: " + e + " @ " + e.tok.line + ", " + e.tok.col)); - traverse(depth, a.Expr, e => - { - AssertCmd new_c = - (ar != null) ? new AssertRequiresCmd(ar.Call, new Requires(e.tok, ar.Requires.Free, fe(e), ar.Requires.Comment)) : - (ae != null) ? new AssertEnsuresCmd(new Ensures(e.tok, ae.Ensures.Free, fe(e), ae.Ensures.Comment)) : - (ai != null) ? new LoopInitAssertCmd(e.tok, fe(e)) : - (am != null) ? new LoopInvMaintainedAssertCmd(e.tok, fe(e)) : - new AssertCmd(e.tok, fe(e)); - new_c.Attributes = new QKeyValue(e.tok, "subsumption", new List() { new LiteralExpr(e.tok, BigNum.FromInt(0)) }, a.Attributes); - newCmds.Add(new_c); - }); - } - newCmds.Add(c); - changed = true; - } - else - { - newCmds.Add(c); - } - } - if (changed) b.Cmds = newCmds; - } - - double max_vc_cost = CommandLineOptions.Clo.VcsMaxCost; - int tmp_max_vc_cost = -1, max_splits = CommandLineOptions.Clo.VcsMaxSplits, - max_kg_splits = CommandLineOptions.Clo.VcsMaxKeepGoingSplits; - CheckIntAttributeOnImpl(impl, "vcs_max_cost", ref tmp_max_vc_cost); - CheckIntAttributeOnImpl(impl, "vcs_max_splits", ref max_splits); - CheckIntAttributeOnImpl(impl, "vcs_max_keep_going_splits", ref max_kg_splits); - if (tmp_max_vc_cost >= 0) { - max_vc_cost = tmp_max_vc_cost; - } - - Outcome outcome = Outcome.Correct; - - // Report all recycled failing assertions for this implementation. - if (impl.RecycledFailingAssertions != null && impl.RecycledFailingAssertions.Any()) - { - outcome = Outcome.Errors; - foreach (var a in impl.RecycledFailingAssertions) - { - var checksum = a.Checksum; - var oldCex = impl.ErrorChecksumToCachedError[checksum] as Counterexample; - if (oldCex != null) - { - callback.OnCounterexample(oldCex, null); - } - } - } - - Cores = CommandLineOptions.Clo.VcsCores; - Stack work = new Stack(); - List currently_running = new List(); - ResetPredecessors(impl.Blocks); - List manual_splits = Split.FindManualSplits(impl, gotoCmdOrigins, this); - if (manual_splits != null) { - foreach (var split in manual_splits) { - work.Push(split); - } - } else { - work.Push(new Split(impl.Blocks, gotoCmdOrigins, this, impl)); - } - - bool keep_going = max_kg_splits > 1; - int total = 0; - int no = max_splits == 1 && !keep_going ? -1 : 0; - bool first_round = true; - bool do_splitting = keep_going || max_splits > 1; - double remaining_cost = 0.0, proven_cost = 0.0; - - if (do_splitting) { - remaining_cost = work.Peek().Cost; - } - - while (work.Any() || currently_running.Any()) - { - bool prover_failed = false; - Split s = null; - var isWaiting = !work.Any(); - - if (!isWaiting) - { - s = work.Peek(); - - if (first_round && max_splits > 1) - { - prover_failed = true; - remaining_cost -= s.Cost; - } - else - { - var timeout = (keep_going && s.LastChance) ? CommandLineOptions.Clo.VcsFinalAssertTimeout : - keep_going ? CommandLineOptions.Clo.VcsKeepGoingTimeout : - impl.TimeLimit; - - var checker = s.parent.FindCheckerFor(timeout, false); - try - { - if (checker == null) - { - isWaiting = true; - goto waiting; - } - else - { - s = work.Pop(); - } - - if (CommandLineOptions.Clo.Trace && no >= 0) - { - System.Console.WriteLine(" checking split {1}/{2}, {3:0.00}%, {0} ...", - s.Stats, no + 1, total, 100 * proven_cost / (proven_cost + remaining_cost)); - } - callback.OnProgress("VCprove", no < 0 ? 0 : no, total, proven_cost / (remaining_cost + proven_cost)); - - Contract.Assert(s.parent == this); - lock (checker) - { - s.BeginCheck(checker, callback, mvInfo, no, timeout); - } - - no++; - - currently_running.Add(s); - } - catch (Exception) - { - checker.GoBackToIdle(); - throw; - } - } - } - - waiting: - if (isWaiting) - { - // Wait for one split to terminate. - var tasks = currently_running.Select(splt => splt.ProverTask).ToArray(); - - if (tasks.Any()) - { - try - { - int index = Task.WaitAny(tasks); - s = currently_running[index]; - currently_running.RemoveAt(index); - - if (do_splitting) - { - remaining_cost -= s.Cost; - } - - lock (s.Checker) - { - s.ReadOutcome(ref outcome, out prover_failed); - } - - if (do_splitting) - { - if (prover_failed) - { - // even if the prover fails, we have learned something, i.e., it is - // annoying to watch Boogie say Timeout, 0.00% a couple of times - proven_cost += s.Cost / 100; - } - else - { - proven_cost += s.Cost; - } - } - callback.OnProgress("VCprove", no < 0 ? 0 : no, total, proven_cost / (remaining_cost + proven_cost)); - - if (prover_failed && !first_round && s.LastChance) - { - string msg = "some timeout"; - if (s.reporter != null && s.reporter.resourceExceededMessage != null) - { - msg = s.reporter.resourceExceededMessage; - } - callback.OnCounterexample(s.ToCounterexample(s.Checker.TheoremProver.Context), msg); - outcome = Outcome.Errors; - break; - } - } - finally - { - s.Checker.GoBackToIdle(); - } - - Contract.Assert(prover_failed || outcome == Outcome.Correct || outcome == Outcome.Errors || outcome == Outcome.Inconclusive); - } - } - - if (prover_failed) - { - int splits = first_round && max_splits > 1 ? max_splits : max_kg_splits; - - if (splits > 1) - { - List tmp = Split.DoSplit(s, max_vc_cost, splits); - Contract.Assert(tmp != null); - max_vc_cost = 1.0; // for future - first_round = false; - //tmp.Sort(new Comparison(Split.Compare)); - foreach (Split a in tmp) - { - Contract.Assert(a != null); - work.Push(a); - total++; - remaining_cost += a.Cost; - } - if (outcome != Outcome.Errors) - { - outcome = Outcome.Correct; - } - } - else - { - Contract.Assert(outcome != Outcome.Correct); - if (outcome == Outcome.TimedOut) - { - string msg = "some timeout"; - if (s.reporter != null && s.reporter.resourceExceededMessage != null) - { - msg = s.reporter.resourceExceededMessage; - } - callback.OnTimeout(msg); - } - else if (outcome == Outcome.OutOfMemory) - { - string msg = "out of memory"; - if (s.reporter != null && s.reporter.resourceExceededMessage != null) - { - msg = s.reporter.resourceExceededMessage; - } - callback.OnOutOfMemory(msg); - } - - break; - } - } - } - - if (outcome == Outcome.Correct && smoke_tester != null) { - smoke_tester.Test(); - } - - callback.OnProgress("done", 0, 0, 1.0); - - if (_print_time) - { - watch.Stop(); - Console.WriteLine("Total time for this method: {0}", watch.Elapsed.ToString()); - } - - return outcome; - } - - public class ErrorReporter : ProverInterface.ErrorHandler { - Dictionary/*!*/ gotoCmdOrigins; - Dictionary/*!*/ label2absy; - List/*!*/ blocks; - protected Dictionary/*!*/ incarnationOriginMap; - protected VerifierCallback/*!*/ callback; - protected ModelViewInfo MvInfo; - internal string resourceExceededMessage; - static System.IO.TextWriter modelWriter; - [ContractInvariantMethod] - void ObjectInvariant() { - Contract.Invariant(gotoCmdOrigins != null); - Contract.Invariant(label2absy != null); - Contract.Invariant(cce.NonNullElements(blocks)); - Contract.Invariant(cce.NonNullDictionaryAndValues(incarnationOriginMap)); - Contract.Invariant(callback != null); - Contract.Invariant(context != null); - Contract.Invariant(program != null); - } - - - public static TextWriter ModelWriter { - get { - Contract.Ensures(Contract.Result() != null); - - if (ErrorReporter.modelWriter == null) - ErrorReporter.modelWriter = CommandLineOptions.Clo.PrintErrorModelFile == null ? Console.Out : new StreamWriter(CommandLineOptions.Clo.PrintErrorModelFile, false); - return ErrorReporter.modelWriter; - } - } - - protected ProverContext/*!*/ context; - Program/*!*/ program; - - public ErrorReporter(Dictionary/*!*/ gotoCmdOrigins, - Dictionary/*!*/ label2absy, - List/*!*/ blocks, - Dictionary/*!*/ incarnationOriginMap, - VerifierCallback/*!*/ callback, - ModelViewInfo mvInfo, - ProverContext/*!*/ context, - Program/*!*/ program) { - Contract.Requires(gotoCmdOrigins != null); - Contract.Requires(label2absy != null); - Contract.Requires(cce.NonNullElements(blocks)); - Contract.Requires(cce.NonNullDictionaryAndValues(incarnationOriginMap)); - Contract.Requires(callback != null); - Contract.Requires(context!=null); - Contract.Requires(program!=null); - this.gotoCmdOrigins = gotoCmdOrigins; - this.label2absy = label2absy; - this.blocks = blocks; - this.incarnationOriginMap = incarnationOriginMap; - this.callback = callback; - this.MvInfo = mvInfo; - - this.context = context; - this.program = program; - } - - public override void OnModel(IList/*!*/ labels, Model model, ProverInterface.Outcome proverOutcome) { - //Contract.Requires(cce.NonNullElements(labels)); - if (CommandLineOptions.Clo.PrintErrorModel >= 1 && model != null) { - if (VC.ConditionGeneration.errorModelList != null) - { - VC.ConditionGeneration.errorModelList.Add(model); - } - - model.Write(ErrorReporter.ModelWriter); - ErrorReporter.ModelWriter.Flush(); - } - - Hashtable traceNodes = new Hashtable(); - foreach (string s in labels) { - Contract.Assert(s != null); - Absy absy = Label2Absy(s); - Contract.Assert(absy != null); - if (traceNodes.ContainsKey(absy)) - System.Console.WriteLine("Warning: duplicate label: " + s + " read while tracing nodes"); - else - traceNodes.Add(absy, null); - } - - List trace = new List(); - Block entryBlock = cce.NonNull(this.blocks[0]); - Contract.Assert(traceNodes.Contains(entryBlock)); - trace.Add(entryBlock); - - Counterexample newCounterexample = TraceCounterexample(entryBlock, traceNodes, trace, model, MvInfo, incarnationOriginMap, context, new Dictionary()); - - if (newCounterexample == null) - return; - - #region Map passive program errors back to original program errors - ReturnCounterexample returnExample = newCounterexample as ReturnCounterexample; - if (returnExample != null) { - foreach (Block b in returnExample.Trace) { - Contract.Assert(b != null); - Contract.Assume(b.TransferCmd != null); - ReturnCmd cmd = gotoCmdOrigins.ContainsKey(b.TransferCmd) ? gotoCmdOrigins[b.TransferCmd] : null; - if (cmd != null) { - returnExample.FailingReturn = cmd; - break; - } - } - } - #endregion - callback.OnCounterexample(newCounterexample, null); - } - - public override Absy Label2Absy(string label) { - //Contract.Requires(label != null); - Contract.Ensures(Contract.Result() != null); - - int id = int.Parse(label); - return cce.NonNull((Absy)label2absy[id]); - } - - public override void OnResourceExceeded(string msg, IEnumerable> assertCmds = null) { - //Contract.Requires(msg != null); - resourceExceededMessage = msg; - if (assertCmds != null) - { - foreach (var cmd in assertCmds) - { - Counterexample cex = AssertCmdToCounterexample(cmd.Item1, cmd.Item2 , new List(), null, null, context); - callback.OnCounterexample(cex, msg); - } - } - } - - public override void OnProverWarning(string msg) { - //Contract.Requires(msg != null); - callback.OnWarning(msg); - } - } - - public class ErrorReporterLocal : ErrorReporter { - public ErrorReporterLocal(Dictionary/*!*/ gotoCmdOrigins, - Dictionary/*!*/ label2absy, - List/*!*/ blocks, - Dictionary/*!*/ incarnationOriginMap, - VerifierCallback/*!*/ callback, - ModelViewInfo mvInfo, - ProverContext/*!*/ context, - Program/*!*/ program) - : base(gotoCmdOrigins, label2absy, blocks, incarnationOriginMap, callback, mvInfo, context, program) // here for aesthetic purposes //TODO: Maybe nix? - { - Contract.Requires(gotoCmdOrigins != null); - Contract.Requires(label2absy != null); - Contract.Requires(cce.NonNullElements(blocks)); - Contract.Requires(cce.NonNullDictionaryAndValues(incarnationOriginMap)); - Contract.Requires(callback != null); - Contract.Requires(context != null); - Contract.Requires(program != null); - } - - public override void OnModel(IList/*!*/ labels, Model model, ProverInterface.Outcome proverOutcome) { - //Contract.Requires(cce.NonNullElements(labels)); - // We ignore the error model here for enhanced error message purposes. - // It is only printed to the command line. - if (CommandLineOptions.Clo.PrintErrorModel >= 1 && model != null) { - if (CommandLineOptions.Clo.PrintErrorModelFile != null) { - model.Write(ErrorReporter.ModelWriter); - ErrorReporter.ModelWriter.Flush(); - } - } - List traceNodes = new List(); - List assertNodes = new List(); - foreach (string s in labels) { - Contract.Assert(s != null); - Absy node = Label2Absy(s); - if (node is Block) { - Block b = (Block)node; - traceNodes.Add(b); - } else { - AssertCmd a = (AssertCmd)node; - assertNodes.Add(a); - } - } - Contract.Assert(assertNodes.Count > 0); - Contract.Assert(traceNodes.Count == assertNodes.Count); - - foreach (AssertCmd a in assertNodes) { - // find the corresponding Block (assertNodes.Count is likely to be 1, or small in any case, so just do a linear search here) - foreach (Block b in traceNodes) { - if (b.Cmds.Contains(a)) { - List trace = new List(); - trace.Add(b); - Counterexample newCounterexample = AssertCmdToCounterexample(a, cce.NonNull(b.TransferCmd), trace, model, MvInfo, context); - callback.OnCounterexample(newCounterexample, null); - goto NEXT_ASSERT; - } - } - Contract.Assert(false); - throw new cce.UnreachableException(); // there was no block that contains the assert - NEXT_ASSERT: { - } - } - } - } - - private void RecordCutEdge(Dictionary> edgesCut, Block from, Block to){ - if (edgesCut != null) - { - if (!edgesCut.ContainsKey(from)) - edgesCut.Add(from, new List()); - edgesCut[from].Add(to); - } - } - - 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 - - CurrentLocalVariables = impl.LocVars; - variable2SequenceNumber = new Dictionary(); - incarnationOriginMap = new Dictionary(); - - #region Debug Tracing - if (CommandLineOptions.Clo.TraceVerify) - { - Console.WriteLine("original implementation"); - EmitImpl(impl, false); - } - #endregion - - #region Debug Tracing - if (CommandLineOptions.Clo.TraceVerify) - { - Console.WriteLine("after desugaring sugared commands like procedure calls"); - EmitImpl(impl, true); - } - #endregion - - // Recompute the predecessors, but first insert a dummy start node that is sure not to be the target of any goto (because the cutting of back edges - // below assumes that the start node has no predecessor) - impl.Blocks.Insert(0, new Block(new Token(-17, -4), "0", new List(), new GotoCmd(Token.NoToken, new List { impl.Blocks[0].Label }, new List { impl.Blocks[0] }))); - ResetPredecessors(impl.Blocks); - - if(CommandLineOptions.Clo.KInductionDepth < 0) { - ConvertCFG2DAGStandard(impl, edgesCut, taskID); - } else { - ConvertCFG2DAGKInduction(impl, edgesCut, taskID); - } - - #region Debug Tracing - if (CommandLineOptions.Clo.TraceVerify) - { - Console.WriteLine("after conversion into a DAG"); - EmitImpl(impl, true); - } - #endregion - } - - private void ConvertCFG2DAGStandard(Implementation impl, Dictionary> edgesCut, int taskID) - { - #region Convert program CFG into a DAG - - #region Use the graph library to figure out where the (natural) loops are - - #region Create the graph by adding the source node and each edge - Graph g = Program.GraphFromImpl(impl); - #endregion - - //Graph g = program.ProcessLoops(impl); - - g.ComputeLoops(); // this is the call that does all of the processing - if (!g.Reducible) - { - throw new VCGenException("Irreducible flow graphs are unsupported."); - } - - #endregion - - #region Cut the backedges, push assert/assume statements from loop header into predecessors, change them all into assume statements at top of loop, introduce havoc statements - foreach (Block header in cce.NonNull(g.Headers)) - { - Contract.Assert(header != null); - IDictionary backEdgeNodes = new Dictionary(); - foreach (Block b in cce.NonNull(g.BackEdgeNodes(header))) - { - Contract.Assert(b != null); - backEdgeNodes.Add(b, null); - } - - #region Find the (possibly empty) prefix of assert commands in the header, replace each assert with an assume of the same condition - List prefixOfPredicateCmdsInit = new List(); - List prefixOfPredicateCmdsMaintained = new List(); - for (int i = 0, n = header.Cmds.Count; i < n; i++) - { - PredicateCmd a = header.Cmds[i] as PredicateCmd; - if (a != null) - { - if (a is AssertCmd) - { - AssertCmd c = (AssertCmd)a; - AssertCmd b = null; - - if (CommandLineOptions.Clo.ConcurrentHoudini) - { - Contract.Assert(taskID >= 0); - if (CommandLineOptions.Clo.Cho[taskID].DisableLoopInvEntryAssert) - b = new LoopInitAssertCmd(c.tok, Expr.True); - else - b = new LoopInitAssertCmd(c.tok, c.Expr); - } - else - { - b = new LoopInitAssertCmd(c.tok, c.Expr); - } - - b.Attributes = c.Attributes; - b.ErrorData = c.ErrorData; - prefixOfPredicateCmdsInit.Add(b); - - if (CommandLineOptions.Clo.ConcurrentHoudini) - { - Contract.Assert(taskID >= 0); - if (CommandLineOptions.Clo.Cho[taskID].DisableLoopInvMaintainedAssert) - b = new Bpl.LoopInvMaintainedAssertCmd(c.tok, Expr.True); - else - b = new Bpl.LoopInvMaintainedAssertCmd(c.tok, c.Expr); - } - else - { - b = new Bpl.LoopInvMaintainedAssertCmd(c.tok, c.Expr); - } - - b.Attributes = c.Attributes; - b.ErrorData = c.ErrorData; - prefixOfPredicateCmdsMaintained.Add(b); - header.Cmds[i] = new AssumeCmd(c.tok, c.Expr); - } - else - { - Contract.Assert(a is AssumeCmd); - if (Bpl.CommandLineOptions.Clo.AlwaysAssumeFreeLoopInvariants) - { - // Usually, "free" stuff, like free loop invariants (and the assume statements - // that stand for such loop invariants) are ignored on the checking side. This - // command-line option changes that behavior to always assume the conditions. - prefixOfPredicateCmdsInit.Add(a); - prefixOfPredicateCmdsMaintained.Add(a); - } - } - } - else if (header.Cmds[i] is CommentCmd) - { - // ignore - } - else - { - break; // stop when an assignment statement (or any other non-predicate cmd) is encountered - } - } - #endregion - - #region Copy the prefix of predicate commands into each predecessor. Do this *before* cutting the backedge!! - for (int predIndex = 0, n = header.Predecessors.Count; predIndex < n; predIndex++) - { - Block pred = cce.NonNull(header.Predecessors[predIndex]); - - // Create a block between header and pred for the predicate commands if pred has more than one successor - GotoCmd gotocmd = cce.NonNull((GotoCmd)pred.TransferCmd); - Contract.Assert(gotocmd.labelNames != null); // if "pred" is really a predecessor, it may be a GotoCmd with at least one label - if (gotocmd.labelNames.Count > 1) - { - Block newBlock = CreateBlockBetween(predIndex, header); - impl.Blocks.Add(newBlock); - - // if pred is a back edge node, then now newBlock is the back edge node - if (backEdgeNodes.ContainsKey(pred)) - { - backEdgeNodes.Remove(pred); - backEdgeNodes.Add(newBlock, null); - } - - pred = newBlock; - } - // Add the predicate commands - if (backEdgeNodes.ContainsKey(pred)) - { - pred.Cmds.AddRange(prefixOfPredicateCmdsMaintained); - } - else - { - pred.Cmds.AddRange(prefixOfPredicateCmdsInit); - } - } - #endregion - - #region Cut the back edge - foreach (Block backEdgeNode in cce.NonNull(backEdgeNodes.Keys)) - { - Contract.Assert(backEdgeNode != null); - Debug.Assert(backEdgeNode.TransferCmd is GotoCmd, "An node was identified as the source for a backedge, but it does not have a goto command."); - GotoCmd gtc = backEdgeNode.TransferCmd as GotoCmd; - if (gtc != null && gtc.labelTargets != null && gtc.labelTargets.Count > 1) - { - // then remove the backedge by removing the target block from the list of gotos - List remainingTargets = new List(); - List remainingLabels = new List(); - Contract.Assume(gtc.labelNames != null); - for (int i = 0, n = gtc.labelTargets.Count; i < n; i++) - { - if (gtc.labelTargets[i] != header) - { - remainingTargets.Add(gtc.labelTargets[i]); - remainingLabels.Add(gtc.labelNames[i]); - } - else - RecordCutEdge(edgesCut, backEdgeNode, header); - } - gtc.labelTargets = remainingTargets; - gtc.labelNames = remainingLabels; - } - else - { - // This backedge is the only out-going edge from this node. - // Add an "assume false" statement to the end of the statements - // inside of the block and change the goto command to a return command. - AssumeCmd ac = new AssumeCmd(Token.NoToken, Expr.False); - backEdgeNode.Cmds.Add(ac); - backEdgeNode.TransferCmd = new ReturnCmd(Token.NoToken); - if (gtc != null && gtc.labelTargets != null && gtc.labelTargets.Count == 1) - RecordCutEdge(edgesCut, backEdgeNode, gtc.labelTargets[0]); - } - #region Remove the backedge node from the list of predecessor nodes in the header - List newPreds = new List(); - foreach (Block p in header.Predecessors) - { - if (p != backEdgeNode) - newPreds.Add(p); - } - header.Predecessors = newPreds; - #endregion - } - #endregion - - #region Collect all variables that are assigned to in all of the natural loops for which this is the header - List varsToHavoc = VarsAssignedInLoop(g, header); - List havocExprs = new List(); - foreach (Variable v in varsToHavoc) - { - Contract.Assert(v != null); - IdentifierExpr ie = new IdentifierExpr(Token.NoToken, v); - if (!havocExprs.Contains(ie)) - havocExprs.Add(ie); - } - // pass the token of the enclosing loop header to the HavocCmd so we can reconstruct - // the source location for this later on - HavocCmd hc = new HavocCmd(header.tok, havocExprs); - List newCmds = new List(); - newCmds.Add(hc); - foreach (Cmd c in header.Cmds) - { - newCmds.Add(c); - } - header.Cmds = newCmds; - #endregion - } - #endregion - #endregion Convert program CFG into a DAG - } - - public static List VarsAssignedInLoop(Graph g, Block header) - { - List varsToHavoc = new List(); - foreach (Block backEdgeNode in cce.NonNull(g.BackEdgeNodes(header))) - { - Contract.Assert(backEdgeNode != null); - foreach (Block b in g.NaturalLoops(header, backEdgeNode)) - { - Contract.Assert(b != null); - foreach (Cmd c in b.Cmds) - { - Contract.Assert(c != null); - c.AddAssignedVariables(varsToHavoc); - } - } - } - return varsToHavoc; - } - - public static IEnumerable VarsReferencedInLoop(Graph g, Block header) - { - HashSet referencedVars = new HashSet(); - foreach (Block backEdgeNode in cce.NonNull(g.BackEdgeNodes(header))) - { - Contract.Assert(backEdgeNode != null); - foreach (Block b in g.NaturalLoops(header, backEdgeNode)) - { - Contract.Assert(b != null); - foreach (Cmd c in b.Cmds) - { - Contract.Assert(c != null); - var Collector = new VariableCollector(); - Collector.Visit(c); - foreach(var v in Collector.usedVars) { - referencedVars.Add(v); - } - } - } - } - return referencedVars; - } - - private void ConvertCFG2DAGKInduction(Implementation impl, Dictionary> edgesCut, int taskID) { - - // K-induction has not been adapted to be aware of these parameters which standard CFG to DAG transformation uses - Contract.Requires(edgesCut == null); - Contract.Requires(taskID == -1); - - int inductionK = CommandLineOptions.Clo.KInductionDepth; - Contract.Assume(inductionK >= 0); - - bool contRuleApplication = true; - while (contRuleApplication) { - contRuleApplication = false; - - #region Use the graph library to figure out where the (natural) loops are - - #region Create the graph by adding the source node and each edge - Graph g = Program.GraphFromImpl(impl); - #endregion - - g.ComputeLoops(); // this is the call that does all of the processing - if (!g.Reducible) { - throw new VCGenException("Irreducible flow graphs are unsupported."); - } - - #endregion - - foreach (Block header in cce.NonNull(g.Headers)) { - Contract.Assert(header != null); - - #region Debug Tracing - if (CommandLineOptions.Clo.TraceVerify) - { - Console.WriteLine("Applying k-induction rule with k=" + inductionK); - } - #endregion - - #region generate the step case - Block newHeader = DuplicateLoop(impl, g, header, null, - false, false, "_step_assertion"); - for (int i = 0; i < inductionK; ++i) - { - newHeader = DuplicateLoop(impl, g, header, newHeader, - true, true, - "_step_" + (inductionK - i)); - } - #endregion - - #region havoc variables that can be assigned in the loop - - List varsToHavoc = VarsAssignedInLoop(g, header); - List havocExprs = new List(); - foreach (Variable v in varsToHavoc) - { - Contract.Assert(v != null); - IdentifierExpr ie = new IdentifierExpr(Token.NoToken, v); - if (!havocExprs.Contains(ie)) - havocExprs.Add(ie); - } - // pass the token of the enclosing loop header to the HavocCmd so we can reconstruct - // the source location for this later on - HavocCmd hc = new HavocCmd(newHeader.tok, havocExprs); - List havocCmds = new List(); - havocCmds.Add(hc); - - Block havocBlock = new Block(newHeader.tok, newHeader.Label + "_havoc", havocCmds, - new GotoCmd (newHeader.tok, new List { newHeader })); - - impl.Blocks.Add(havocBlock); - newHeader.Predecessors.Add(havocBlock); - newHeader = havocBlock; - - #endregion - - #region generate the base case loop copies - for (int i = 0; i < inductionK; ++i) - { - newHeader = DuplicateLoop(impl, g, header, newHeader, - false, false, - "_base_" + (inductionK - i)); - } - #endregion - - #region redirect into the new loop copies and remove the original loop (but don't redirect back-edges) - - IDictionary backEdgeNodes = new Dictionary(); - foreach (Block b in cce.NonNull(g.BackEdgeNodes(header))) { Contract.Assert(b != null); backEdgeNodes.Add(b, null); } - - for (int predIndex = 0, n = header.Predecessors.Count(); predIndex < n; predIndex++) - { - Block pred = cce.NonNull(header.Predecessors[predIndex]); - if (!backEdgeNodes.ContainsKey(pred)) - { - GotoCmd gc = pred.TransferCmd as GotoCmd; - Contract.Assert(gc != null); - for (int i = 0; i < gc.labelTargets.Count(); ++i) - { - if (gc.labelTargets[i] == header) - { - gc.labelTargets[i] = newHeader; - gc.labelNames[i] = newHeader.Label; - newHeader.Predecessors.Add(pred); - } - } - } - } - impl.PruneUnreachableBlocks(); - - #endregion - - contRuleApplication = true; - break; - } - - } - - ResetPredecessors(impl.Blocks); - impl.FreshenCaptureStates(); - - } - - private Block DuplicateLoop(Implementation impl, Graph g, - Block header, Block nextHeader, bool cutExits, - bool toAssumptions, string suffix) - { - IDictionary ori2CopiedBlocks = new Dictionary(); - Duplicator duplicator = new Duplicator(); - - #region create copies of all blocks in the loop - foreach (Block backEdgeNode in cce.NonNull(g.BackEdgeNodes(header))) - { - Contract.Assert(backEdgeNode != null); - foreach (Block b in g.NaturalLoops(header, backEdgeNode)) - { - Contract.Assert(b != null); - if (!ori2CopiedBlocks.ContainsKey(b)) - { - Block copy = (Block)duplicator.Visit(b); - copy.Cmds = new List(copy.Cmds); // Philipp Ruemmer commented that this was necessary due to a bug in the Duplicator. That was a long time; worth checking whether this has been fixed - copy.Predecessors = new List(); - copy.Label = copy.Label + suffix; - - #region turn asserts into assumptions - if (toAssumptions) - { - for (int i = 0; i < copy.Cmds.Count(); ++i) - { - AssertCmd ac = copy.Cmds[i] as AssertCmd; - if (ac != null) - { - copy.Cmds[i] = new AssumeCmd(ac.tok, ac.Expr); - } - } - } - #endregion - - impl.Blocks.Add(copy); - ori2CopiedBlocks.Add(b, copy); - } - } - } - #endregion - - #region adjust the transfer commands of the newly created blocks - foreach (KeyValuePair pair in ori2CopiedBlocks) - { - Block copy = pair.Value; - GotoCmd gc = copy.TransferCmd as GotoCmd; - if (gc != null) - { - List newTargets = new List(); - List newLabels = new List(); - - for (int i = 0; i < gc.labelTargets.Count(); ++i) - { - Block newTarget; - if (gc.labelTargets[i] == header) - { - if (nextHeader != null) - { - newTargets.Add(nextHeader); - newLabels.Add(nextHeader.Label); - nextHeader.Predecessors.Add(copy); - } - } - else if (ori2CopiedBlocks.TryGetValue(gc.labelTargets[i], out newTarget)) - { - newTargets.Add(newTarget); - newLabels.Add(newTarget.Label); - newTarget.Predecessors.Add(copy); - } - else if (!cutExits) - { - newTargets.Add(gc.labelTargets[i]); - newLabels.Add(gc.labelNames[i]); - gc.labelTargets[i].Predecessors.Add(copy); - } - } - - if (newTargets.Count() == 0) - { - // if no targets are left, we assume false and return - copy.Cmds.Add(new AssumeCmd(Token.NoToken, Expr.False)); - copy.TransferCmd = new ReturnCmd(Token.NoToken); - } - else - { - copy.TransferCmd = new GotoCmd(gc.tok, newLabels, newTargets); - } - } - else if (cutExits && (copy.TransferCmd is ReturnCmd)) - { - // because return is a kind of exit from the loop, we - // assume false to cut this path - copy.Cmds.Add(new AssumeCmd(Token.NoToken, Expr.False)); - } - } - #endregion - - return ori2CopiedBlocks[header]; - } - - public void DesugarCalls(Implementation impl) { - foreach (Block block in impl.Blocks) { - List newCmds = new List(); - foreach (Cmd cmd in block.Cmds) { - SugaredCmd sugaredCmd = cmd as SugaredCmd; - if (sugaredCmd != null) { - StateCmd stateCmd = sugaredCmd.Desugaring as StateCmd; - foreach (Variable v in stateCmd.Locals) { - impl.LocVars.Add(v); - } - newCmds.AddRange(stateCmd.Cmds); - } - else { - newCmds.Add(cmd); - } - } - block.Cmds = newCmds; - } - } - - public Dictionary PassifyImpl(Implementation impl, out ModelViewInfo mvInfo) - { - Contract.Requires(impl != null); - Contract.Requires(program != null); - Contract.Ensures(Contract.Result>() != null); - - Dictionary gotoCmdOrigins = new Dictionary(); - Block exitBlock = GenerateUnifiedExit(impl, gotoCmdOrigins); - - #region Debug Tracing - if (CommandLineOptions.Clo.TraceVerify) - { - Console.WriteLine("after creating a unified exit block"); - EmitImpl(impl, true); - } - #endregion - - #region Insert pre- and post-conditions and where clauses as assume and assert statements - { - List cc = new List(); - // where clauses of global variables - lock (program.TopLevelDeclarations) - { - foreach (var gvar in program.GlobalVariables) - { - if (gvar != null && gvar.TypedIdent.WhereExpr != null) - { - Cmd c = new AssumeCmd(gvar.tok, gvar.TypedIdent.WhereExpr); - cc.Add(c); - } - } - } - // where clauses of in- and out-parameters - cc.AddRange(GetParamWhereClauses(impl)); - // where clauses of local variables - foreach (Variable lvar in impl.LocVars) {Contract.Assert(lvar != null); - if (lvar.TypedIdent.WhereExpr != null) { - Cmd c = new AssumeCmd(lvar.tok, lvar.TypedIdent.WhereExpr); - cc.Add(c); - } else if (QKeyValue.FindBoolAttribute(lvar.Attributes, "assumption")) { - cc.Add(new AssumeCmd(lvar.tok, new IdentifierExpr(lvar.tok, lvar), new QKeyValue(lvar.tok, "assumption_variable_initialization", new List(), null))); - } - } - // add cc and the preconditions to new blocks preceding impl.Blocks[0] - InjectPreconditions(impl, cc); - - // append postconditions, starting in exitBlock and continuing into other blocks, if needed - InjectPostConditions(impl, exitBlock, gotoCmdOrigins); - } - #endregion - - #region Support for stratified inlining - addExitAssert(impl.Name, exitBlock); - #endregion - - - #region Debug Tracing - if (CommandLineOptions.Clo.TraceVerify) - { - Console.WriteLine("after inserting pre- and post-conditions"); - EmitImpl(impl, true); - } - #endregion - - AddBlocksBetween(impl.Blocks); - - #region Debug Tracing - if (CommandLineOptions.Clo.TraceVerify) - { - Console.WriteLine("after adding empty blocks as needed to catch join assumptions"); - EmitImpl(impl, true); - } - #endregion - - if (CommandLineOptions.Clo.LiveVariableAnalysis > 0) { - Microsoft.Boogie.LiveVariableAnalysis.ComputeLiveVariables(impl); - } - - mvInfo = new ModelViewInfo(program, impl); - Convert2PassiveCmd(impl, mvInfo); - - #region Peep-hole optimizations - if (CommandLineOptions.Clo.RemoveEmptyBlocks){ - #region Get rid of empty blocks - { - RemoveEmptyBlocksIterative(impl.Blocks); - impl.PruneUnreachableBlocks(); - } - #endregion Get rid of empty blocks - - #region Debug Tracing - if (CommandLineOptions.Clo.TraceVerify) - { - Console.WriteLine("after peep-hole optimizations"); - EmitImpl(impl, true); - } - #endregion - } - #endregion Peep-hole optimizations - - HandleSelectiveChecking(impl); - - -// #region Constant Folding -// #endregion -// #region Debug Tracing -// if (CommandLineOptions.Clo.TraceVerify) -// { -// Console.WriteLine("after constant folding"); -// EmitImpl(impl, true); -// } -// #endregion - - return gotoCmdOrigins; - } - - private static void HandleSelectiveChecking(Implementation impl) - { - if (QKeyValue.FindBoolAttribute(impl.Attributes, "selective_checking") || - QKeyValue.FindBoolAttribute(impl.Proc.Attributes, "selective_checking")) { - - var startPoints = new List(); - foreach (var b in impl.Blocks) { - foreach (Cmd c in b.Cmds) { - var p = c as PredicateCmd; - if (p != null && QKeyValue.FindBoolAttribute(p.Attributes, "start_checking_here")) { - startPoints.Add(b); - break; - } - } - } - - // Compute the set of blocks reachable from blocks containing "start_checking_here" - var blocksToCheck = new HashSet(); - foreach (var b in startPoints) { - var todo = new Stack(); - var wasThere = blocksToCheck.Contains(b); - todo.Push(b); - while (todo.Count > 0) { - var x = todo.Pop(); - if (blocksToCheck.Contains(x)) continue; - blocksToCheck.Add(x); - var ex = x.TransferCmd as GotoCmd; - if (ex != null) - foreach (Block e in ex.labelTargets) - todo.Push(e); - } - if (!wasThere) blocksToCheck.Remove(b); - } - - // Convert asserts to assumes in "unreachable" blocks, as well as in portions of blocks before we reach "start_checking_here" - foreach (var b in impl.Blocks) { - if (blocksToCheck.Contains(b)) continue; // All reachable blocks must be checked in their entirety, so don't change anything - var newCmds = new List(); - var copyMode = false; - foreach (Cmd c in b.Cmds) { - var p = c as PredicateCmd; - if (p != null && QKeyValue.FindBoolAttribute(p.Attributes, "start_checking_here")) - copyMode = true; - var asrt = c as AssertCmd; - if (copyMode || asrt == null) - newCmds.Add(c); - else - newCmds.Add(AssertTurnedIntoAssume(asrt)); - } - - b.Cmds = newCmds; - } - } - } - - // Used by stratified inlining - protected virtual void addExitAssert(string implName, Block exitBlock) - { - } - - public virtual Counterexample extractLoopTrace(Counterexample cex, string mainProcName, Program program, Dictionary> extractLoopMappingInfo) - { - // Construct the set of inlined procs in the original program - var inlinedProcs = new HashSet(); - foreach (var proc in program.Procedures) - { - if (!(proc is LoopProcedure)) - { - inlinedProcs.Add(proc.Name); - } - } - - return extractLoopTraceRec( - new CalleeCounterexampleInfo(cex, new List()), - mainProcName, inlinedProcs, extractLoopMappingInfo).counterexample; - } - - protected CalleeCounterexampleInfo extractLoopTraceRec( - CalleeCounterexampleInfo cexInfo, string currProc, - HashSet inlinedProcs, - Dictionary> extractLoopMappingInfo) - { - Contract.Requires(currProc != null); - if (cexInfo.counterexample == null) return cexInfo; - - var cex = cexInfo.counterexample; - // Go through all blocks in the trace, map them back to blocks in the original program (if there is one) - var ret = cex.Clone(); - ret.Trace = new List(); - ret.calleeCounterexamples = new Dictionary(); - - for (int numBlock = 0; numBlock < cex.Trace.Count; numBlock ++ ) - { - Block block = cex.Trace[numBlock]; - var origBlock = elGetBlock(currProc, block, extractLoopMappingInfo); - if (origBlock != null) ret.Trace.Add(origBlock); - var callCnt = 1; - for (int numInstr = 0; numInstr < block.Cmds.Count; numInstr ++) { - Cmd cmd = block.Cmds[numInstr]; - var loc = new TraceLocation(numBlock, numInstr); - if (!cex.calleeCounterexamples.ContainsKey(loc)) - { - if (getCallee(cex.getTraceCmd(loc), inlinedProcs) != null) callCnt++; - continue; - } - string callee = cex.getCalledProcName(cex.getTraceCmd(loc)); - Contract.Assert(callee != null); - var calleeTrace = cex.calleeCounterexamples[loc]; - Debug.Assert(calleeTrace != null); - - var origTrace = extractLoopTraceRec(calleeTrace, callee, inlinedProcs, extractLoopMappingInfo); - - if (elIsLoop(callee)) - { - // Absorb the trace into the current trace - - int currLen = ret.Trace.Count; - ret.Trace.AddRange(origTrace.counterexample.Trace); - - foreach (var kvp in origTrace.counterexample.calleeCounterexamples) - { - var newloc = new TraceLocation(kvp.Key.numBlock + currLen, kvp.Key.numInstr); - ret.calleeCounterexamples.Add(newloc, kvp.Value); - } - - } - else - { - var origLoc = new TraceLocation(ret.Trace.Count - 1, getCallCmdPosition(origBlock, callCnt, inlinedProcs, callee)); - ret.calleeCounterexamples.Add(origLoc, origTrace); - callCnt++; - } - } - } - return new CalleeCounterexampleInfo(ret, cexInfo.args); - } - - // return the position of the i^th CallCmd in the block (count only those Calls that call a procedure in inlinedProcs). - // Assert failure if there isn't any. - // Assert that the CallCmd found calls "callee" - private int getCallCmdPosition(Block block, int i, HashSet inlinedProcs, string callee) - { - Debug.Assert(i >= 1); - for (int pos = 0; pos < block.Cmds.Count; pos++) - { - Cmd cmd = block.Cmds[pos]; - string procCalled = getCallee(cmd, inlinedProcs); - - if (procCalled != null) - { - if (i == 1) - { - Debug.Assert(procCalled == callee); - return pos; - } - i--; - } - } - - Debug.Assert(false, "Didn't find the i^th call cmd"); - return -1; - } - - private string getCallee(Cmd cmd, HashSet inlinedProcs) - { - string procCalled = null; - if (cmd is CallCmd) - { - var cc = (CallCmd)cmd; - if (inlinedProcs.Contains(cc.Proc.Name)) - { - procCalled = cc.Proc.Name; - } - } - - if (cmd is AssumeCmd) - { - var expr = (cmd as AssumeCmd).Expr as NAryExpr; - if (expr != null) - { - if (inlinedProcs.Contains(expr.Fun.FunctionName)) - { - procCalled = expr.Fun.FunctionName; - } - } - } - return procCalled; - } - - protected virtual bool elIsLoop(string procname) - { - return false; - } - - private Block elGetBlock(string procname, Block block, Dictionary> extractLoopMappingInfo) - { - Contract.Requires(procname != null); - - if (!extractLoopMappingInfo.ContainsKey(procname)) - return block; - - if (!extractLoopMappingInfo[procname].ContainsKey(block.Label)) - return null; - - return extractLoopMappingInfo[procname][block.Label]; - } - - static Counterexample TraceCounterexample( - Block/*!*/ b, Hashtable/*!*/ traceNodes, List/*!*/ trace, Model errModel, ModelViewInfo mvInfo, - Dictionary/*!*/ incarnationOriginMap, - ProverContext/*!*/ context, - Dictionary/*!*/ calleeCounterexamples) - { - Contract.Requires(b != null); - Contract.Requires(traceNodes != null); - Contract.Requires(trace != null); - Contract.Requires(cce.NonNullDictionaryAndValues(incarnationOriginMap)); - Contract.Requires(context != null); - Contract.Requires(cce.NonNullDictionaryAndValues(calleeCounterexamples)); - // After translation, all potential errors come from asserts. - - while (true) - { - List cmds = b.Cmds; - Contract.Assert(cmds != null); - TransferCmd transferCmd = cce.NonNull(b.TransferCmd); - for (int i = 0; i < cmds.Count; i++) - { - Cmd cmd = cce.NonNull(cmds[i]); - - // Skip if 'cmd' not contained in the trace or not an assert - if (cmd is AssertCmd && traceNodes.Contains(cmd)) - { - Counterexample newCounterexample = AssertCmdToCounterexample((AssertCmd)cmd, transferCmd, trace, errModel, mvInfo, context); - Contract.Assert(newCounterexample != null); - newCounterexample.AddCalleeCounterexample(calleeCounterexamples); - return newCounterexample; - } - } - - GotoCmd gotoCmd = transferCmd as GotoCmd; - if (gotoCmd == null) return null; - Block foundBlock = null; - foreach (Block bb in cce.NonNull(gotoCmd.labelTargets)) - { - Contract.Assert(bb != null); - if (traceNodes.Contains(bb)) - { - foundBlock = bb; - break; - } - } - if (foundBlock == null) return null; - trace.Add(foundBlock); - b = foundBlock; - } - } - - public static Counterexample AssertCmdToCounterexample(AssertCmd cmd, TransferCmd transferCmd, List trace, Model errModel, ModelViewInfo mvInfo, ProverContext context) - { - Contract.Requires(cmd != null); - Contract.Requires(transferCmd != null); - Contract.Requires(trace != null); - Contract.Requires(context != null); - Contract.Ensures(Contract.Result() != null); - - List relatedInformation = new List(); - - // See if it is a special assert inserted in translation - if (cmd is AssertRequiresCmd) - { - AssertRequiresCmd assertCmd = (AssertRequiresCmd)cmd; - Contract.Assert(assertCmd != null); - CallCounterexample cc = new CallCounterexample(trace, assertCmd.Call, assertCmd.Requires, errModel, mvInfo, context, assertCmd.Checksum); - cc.relatedInformation = relatedInformation; - return cc; - } - else if (cmd is AssertEnsuresCmd) - { - AssertEnsuresCmd assertCmd = (AssertEnsuresCmd)cmd; - Contract.Assert(assertCmd != null); - ReturnCounterexample rc = new ReturnCounterexample(trace, transferCmd, assertCmd.Ensures, errModel, mvInfo, context, cmd.Checksum); - rc.relatedInformation = relatedInformation; - return rc; - } - else - { - AssertCounterexample ac = new AssertCounterexample(trace, (AssertCmd)cmd, errModel, mvInfo, context); - ac.relatedInformation = relatedInformation; - return ac; - } - } - - static VCExpr LetVC(Block startBlock, - VCExpr controlFlowVariableExpr, - Dictionary label2absy, - ProverContext proverCtxt, - out int assertionCount) { - Contract.Requires(startBlock != null); - Contract.Requires(proverCtxt != null); - - Contract.Ensures(Contract.Result() != null); - - Hashtable/**/ blockVariables = new Hashtable/**/(); - List bindings = new List(); - VCExpr startCorrect = LetVC(startBlock, controlFlowVariableExpr, label2absy, blockVariables, bindings, proverCtxt, out assertionCount); - return proverCtxt.ExprGen.Let(bindings, startCorrect); - } - - static VCExpr LetVCIterative(List blocks, - VCExpr controlFlowVariableExpr, - Dictionary label2absy, - ProverContext proverCtxt, - out int assertionCount, - bool isPositiveContext = true) - { - Contract.Requires(blocks != null); - Contract.Requires(proverCtxt != null); - Contract.Ensures(Contract.Result() != null); - - assertionCount = 0; - - Graph dag = new Graph(); - dag.AddSource(blocks[0]); - foreach (Block b in blocks) { - GotoCmd gtc = b.TransferCmd as GotoCmd; - if (gtc != null) { - Contract.Assume(gtc.labelTargets != null); - foreach (Block dest in gtc.labelTargets) { - Contract.Assert(dest != null); - dag.AddEdge(dest, b); - } - } - } - IEnumerable sortedNodes = dag.TopologicalSort(); - Contract.Assert(sortedNodes != null); - - Dictionary blockVariables = new Dictionary(); - List bindings = new List(); - VCExpressionGenerator gen = proverCtxt.ExprGen; - Contract.Assert(gen != null); - foreach (Block block in sortedNodes) { - VCExpr SuccCorrect; - GotoCmd gotocmd = block.TransferCmd as GotoCmd; - if (gotocmd == null) { - ReturnExprCmd re = block.TransferCmd as ReturnExprCmd; - if (re == null) { - SuccCorrect = VCExpressionGenerator.True; - } - else { - SuccCorrect = proverCtxt.BoogieExprTranslator.Translate(re.Expr); - if (isPositiveContext) - { - SuccCorrect = gen.Not(SuccCorrect); - } - } - } - else { - Contract.Assert(gotocmd.labelTargets != null); - List SuccCorrectVars = new List(gotocmd.labelTargets.Count); - foreach (Block successor in gotocmd.labelTargets) { - Contract.Assert(successor != null); - VCExpr s = blockVariables[successor]; - if (controlFlowVariableExpr != null) { - VCExpr controlFlowFunctionAppl = gen.ControlFlowFunctionApplication(controlFlowVariableExpr, gen.Integer(BigNum.FromInt(block.UniqueId))); - VCExpr controlTransferExpr = gen.Eq(controlFlowFunctionAppl, gen.Integer(BigNum.FromInt(successor.UniqueId))); - s = gen.Implies(controlTransferExpr, s); - } - SuccCorrectVars.Add(s); - } - SuccCorrect = gen.NAry(VCExpressionGenerator.AndOp, SuccCorrectVars); - } - - VCContext context = new VCContext(label2absy, proverCtxt, controlFlowVariableExpr, isPositiveContext); - VCExpr vc = Wlp.Block(block, SuccCorrect, context); - assertionCount += context.AssertionCount; - - VCExprVar v = gen.Variable(block.Label + "_correct", Bpl.Type.Bool); - bindings.Add(gen.LetBinding(v, vc)); - blockVariables.Add(block, v); - } - - return proverCtxt.ExprGen.Let(bindings, blockVariables[blocks[0]]); - } - - static VCExpr LetVC(Block block, - VCExpr controlFlowVariableExpr, - Dictionary label2absy, - Hashtable/**/ blockVariables, - List/*!*/ bindings, - ProverContext proverCtxt, - out int assertionCount) - { - Contract.Requires(block != null); - Contract.Requires(blockVariables!= null); - Contract.Requires(cce.NonNullElements(bindings)); - Contract.Requires(proverCtxt != null); - - Contract.Ensures(Contract.Result() != null); - - assertionCount = 0; - - VCExpressionGenerator gen = proverCtxt.ExprGen; - Contract.Assert(gen != null); - VCExprVar v = (VCExprVar)blockVariables[block]; - if (v == null) { - /* - * For block A (= block), generate: - * LET_binding A_correct = wp(A_body, (/\ S \in Successors(A) :: S_correct)) - * with the side effect of adding the let bindings to "bindings" for any - * successor not yet visited. - */ - VCExpr SuccCorrect; - GotoCmd gotocmd = block.TransferCmd as GotoCmd; - if (gotocmd == null) { - ReturnExprCmd re = block.TransferCmd as ReturnExprCmd; - if (re == null) { - SuccCorrect = VCExpressionGenerator.True; - } else { - SuccCorrect = proverCtxt.BoogieExprTranslator.Translate(re.Expr); - } - } else { - Contract.Assert( gotocmd.labelTargets != null); - List SuccCorrectVars = new List(gotocmd.labelTargets.Count); - foreach (Block successor in gotocmd.labelTargets) { - Contract.Assert(successor != null); - int ac; - VCExpr s = LetVC(successor, controlFlowVariableExpr, label2absy, blockVariables, bindings, proverCtxt, out ac); - assertionCount += ac; - if (controlFlowVariableExpr != null) - { - VCExpr controlFlowFunctionAppl = gen.ControlFlowFunctionApplication(controlFlowVariableExpr, gen.Integer(BigNum.FromInt(block.UniqueId))); - VCExpr controlTransferExpr = gen.Eq(controlFlowFunctionAppl, gen.Integer(BigNum.FromInt(successor.UniqueId))); - s = gen.Implies(controlTransferExpr, s); - } - SuccCorrectVars.Add(s); - } - SuccCorrect = gen.NAry(VCExpressionGenerator.AndOp, SuccCorrectVars); - } - - - VCContext context = new VCContext(label2absy, proverCtxt, controlFlowVariableExpr); - VCExpr vc = Wlp.Block(block, SuccCorrect, context); - assertionCount += context.AssertionCount; - - v = gen.Variable(block.Label + "_correct", Bpl.Type.Bool); - bindings.Add(gen.LetBinding(v, vc)); - blockVariables.Add(block, v); - } - return v; - } - - static VCExpr DagVC(Block block, - VCExpr controlFlowVariableExpr, - Dictionary label2absy, - Hashtable/**/ blockEquations, - ProverContext proverCtxt, - out int assertionCount) - { - Contract.Requires(block != null); - Contract.Requires(label2absy != null); - Contract.Requires(blockEquations != null); - Contract.Requires(proverCtxt != null); - Contract.Ensures(Contract.Result() != null); - - assertionCount = 0; - VCExpressionGenerator gen = proverCtxt.ExprGen; - Contract.Assert(gen != null); - VCExpr vc = (VCExpr)blockEquations[block]; - if (vc != null) { - return vc; - } - - /* - * For block A (= block), generate: - * wp(A_body, (/\ S \in Successors(A) :: DagVC(S))) - */ - VCExpr SuccCorrect = null; - GotoCmd gotocmd = block.TransferCmd as GotoCmd; - if (gotocmd != null) - { - foreach (Block successor in cce.NonNull(gotocmd.labelTargets)) { - Contract.Assert(successor != null); - int ac; - VCExpr c = DagVC(successor, controlFlowVariableExpr, label2absy, blockEquations, proverCtxt, out ac); - assertionCount += ac; - if (controlFlowVariableExpr != null) { - VCExpr controlFlowFunctionAppl = gen.ControlFlowFunctionApplication(controlFlowVariableExpr, gen.Integer(BigNum.FromInt(block.UniqueId))); - VCExpr controlTransferExpr = gen.Eq(controlFlowFunctionAppl, gen.Integer(BigNum.FromInt(successor.UniqueId))); - c = gen.Implies(controlTransferExpr, c); - } - SuccCorrect = SuccCorrect == null ? c : gen.And(SuccCorrect, c); - } - } - if (SuccCorrect == null) { - SuccCorrect = VCExpressionGenerator.True; - } - - VCContext context = new VCContext(label2absy, proverCtxt, controlFlowVariableExpr); - vc = Wlp.Block(block, SuccCorrect, context); - assertionCount += context.AssertionCount; - - // gen.MarkAsSharedFormula(vc); PR: don't know yet what to do with this guy - - blockEquations.Add(block, vc); - return vc; - } - - static VCExpr FlatBlockVC(Implementation impl, - Dictionary label2absy, - bool local, bool reach, bool doomed, - ProverContext proverCtxt, - out int assertionCount) - { - Contract.Requires(impl != null); - Contract.Requires(label2absy != null); - Contract.Requires(proverCtxt != null); - Contract.Requires( !local || !reach); // "reach" must be false for local - - VCExpressionGenerator gen = proverCtxt.ExprGen; - Contract.Assert(gen != null); - Hashtable/* Block --> VCExprVar */ BlkCorrect = BlockVariableMap(impl.Blocks, "_correct", gen); - Hashtable/* Block --> VCExprVar */ BlkReached = reach ? BlockVariableMap(impl.Blocks, "_reached", gen) : null; - - List blocks = impl.Blocks; - Contract.Assert(blocks != null); - // block sorting is now done on the VCExpr - // if (!local && (cce.NonNull(CommandLineOptions.Clo.TheProverFactory).NeedsBlockSorting) { - // blocks = SortBlocks(blocks); - // } - - VCExpr proofObligation; - if (!local) { - proofObligation = cce.NonNull((VCExprVar)BlkCorrect[impl.Blocks[0]]); - } else { - List conjuncts = new List(blocks.Count); - foreach (Block b in blocks) {Contract.Assert(b != null); - VCExpr v = cce.NonNull((VCExprVar)BlkCorrect[b]); - conjuncts.Add(v); - } - proofObligation = gen.NAry(VCExpressionGenerator.AndOp, conjuncts); - } - - VCContext context = new VCContext(label2absy, proverCtxt); - Contract.Assert(context != null); - - List programSemantics = new List(blocks.Count); - foreach (Block b in blocks) {Contract.Assert(b != null); - /* - * In block mode, - * For a return block A, generate: - * A_correct <== wp(A_body, true) [post-condition has been translated into an assert] - * For all other blocks, generate: - * A_correct <== wp(A_body, (/\ S \in Successors(A) :: S_correct)) - * - * In doomed mode, proceed as in block mode, except for a return block A, generate: - * A_correct <== wp(A_body, false) [post-condition has been translated into an assert] - * - * In block reach mode, the wp(A_body,...) in the equations above change to: - * A_reached ==> wp(A_body,...) - * and the conjunction above changes to: - * (/\ S \in Successors(A) :: S_correct \/ (\/ T \in Successors(A) && T != S :: T_reached)) - * - * In local mode, generate: - * A_correct <== wp(A_body, true) - */ - VCExpr SuccCorrect; - if (local) { - SuccCorrect = VCExpressionGenerator.True; - } else { - SuccCorrect = SuccessorsCorrect(b, BlkCorrect, BlkReached, doomed, gen); - } - - VCExpr wlp = Wlp.Block(b, SuccCorrect, context); - if (BlkReached != null) { - wlp = gen.Implies(cce.NonNull((VCExprVar)BlkReached[b]), wlp); - } - - VCExprVar okVar = cce.NonNull((VCExprVar)BlkCorrect[b]); - VCExprLetBinding binding = gen.LetBinding(okVar, wlp); - programSemantics.Add(binding); - } - - assertionCount = context.AssertionCount; - return gen.Let(programSemantics, proofObligation); - } - - private static Hashtable/* Block --> VCExprVar */ BlockVariableMap(List/*!*/ blocks, string suffix, - Microsoft.Boogie.VCExpressionGenerator gen) { - Contract.Requires(cce.NonNullElements(blocks)); - Contract.Requires(suffix != null); - Contract.Requires(gen != null); - Contract.Ensures(Contract.Result() != null); - - Hashtable/* Block --> VCExprVar */ map = new Hashtable/* Block --> (Let)Variable */(); - foreach (Block b in blocks) { - Contract.Assert(b != null); - VCExprVar v = gen.Variable(b.Label + suffix, Bpl.Type.Bool); - Contract.Assert(v != null); - map.Add(b, v); - } - return map; - } - - private static VCExpr SuccessorsCorrect( - Block b, - Hashtable/* Block --> VCExprVar */ BlkCorrect, - Hashtable/* Block --> VCExprVar */ BlkReached, - bool doomed, - Microsoft.Boogie.VCExpressionGenerator gen) { - Contract.Requires(b != null); - Contract.Requires(BlkCorrect != null); - Contract.Requires(gen != null); - Contract.Ensures(Contract.Result() != null); - - VCExpr SuccCorrect = null; - GotoCmd gotocmd = b.TransferCmd as GotoCmd; - if (gotocmd != null) { - foreach (Block successor in cce.NonNull(gotocmd.labelTargets)) { - Contract.Assert(successor != null); - // c := S_correct - VCExpr c = (VCExprVar)BlkCorrect[successor]; - Contract.Assert(c != null); - if (BlkReached != null) { - // c := S_correct \/ Sibling0_reached \/ Sibling1_reached \/ ...; - foreach (Block successorSibling in gotocmd.labelTargets) { - Contract.Assert(successorSibling != null); - if (successorSibling != successor) { - c = gen.Or(c, cce.NonNull((VCExprVar)BlkReached[successorSibling])); - } - } - } - SuccCorrect = SuccCorrect == null ? c : gen.And(SuccCorrect, c); - } - } - if (SuccCorrect == null) { - return VCExpressionGenerator.True; - } else if (doomed) { - return VCExpressionGenerator.False; - } else { - return SuccCorrect; - } - } - - static VCExpr NestedBlockVC(Implementation impl, - Dictionary label2absy, - bool reach, - ProverContext proverCtxt, - out int assertionCount){ - Contract.Requires(impl != null); - Contract.Requires(label2absy != null); - Contract.Requires(proverCtxt != null); - Contract.Requires( impl.Blocks.Count != 0); - Contract.Ensures(Contract.Result() != null); - - VCExpressionGenerator gen = proverCtxt.ExprGen; - Contract.Assert(gen != null); - Graph g = Program.GraphFromImpl(impl); - - Hashtable/* Block --> VCExprVar */ BlkCorrect = BlockVariableMap(impl.Blocks, "_correct", gen); - Hashtable/* Block --> VCExprVar */ BlkReached = reach ? BlockVariableMap(impl.Blocks, "_reached", gen) : null; - - Block startBlock = cce.NonNull( impl.Blocks[0]); - VCExpr proofObligation = (VCExprVar)BlkCorrect[startBlock]; - Contract.Assert(proofObligation != null); - VCContext context = new VCContext(label2absy, proverCtxt); - - Hashtable/*Block->int*/ totalOrder = new Hashtable/*Block->int*/(); - { - List blocks = impl.Blocks; - - // block sorting is now done on the VCExpr - // if (((!)CommandLineOptions.Clo.TheProverFactory).NeedsBlockSorting) { - // blocks = SortBlocks(blocks); - // } - int i = 0; - foreach (Block b in blocks) { - Contract.Assert(b != null); - totalOrder[b] = i; - i++; - } - } - - VCExprLetBinding programSemantics = NestedBlockEquation(cce.NonNull(impl.Blocks[0]), BlkCorrect, BlkReached, totalOrder, context, g, gen); - List ps = new List(1); - ps.Add(programSemantics); - - assertionCount = context.AssertionCount; - return gen.Let(ps, proofObligation); - } - - private static VCExprLetBinding NestedBlockEquation(Block b, - Hashtable/*Block-->VCExprVar*/ BlkCorrect, - Hashtable/*Block-->VCExprVar*/ BlkReached, - Hashtable/*Block->int*/ totalOrder, - VCContext context, - Graph g, - Microsoft.Boogie.VCExpressionGenerator gen) { - Contract.Requires(b != null); - Contract.Requires(BlkCorrect != null); - Contract.Requires(totalOrder != null); - Contract.Requires(g != null); - Contract.Requires(context != null); - - Contract.Ensures(Contract.Result() != null); - - /* - * For a block b, return: - * LET_BINDING b_correct = wp(b_body, X) - * where X is: - * LET (THOSE d \in DirectDominates(b) :: BlockEquation(d)) - * IN (/\ s \in Successors(b) :: s_correct) - * - * When the VC-expression generator does not support LET expresions, this - * will eventually turn into: - * b_correct <== wp(b_body, X) - * where X is: - * (/\ s \in Successors(b) :: s_correct) - * <== - * (/\ d \in DirectDominatees(b) :: BlockEquation(d)) - * - * In both cases above, if BlkReached is non-null, then the wp expression - * is instead: - * b_reached ==> wp(b_body, X) - */ - - VCExpr SuccCorrect = SuccessorsCorrect(b, BlkCorrect, null, false, gen); - Contract.Assert(SuccCorrect != null); - - List bindings = new List(); - foreach (Block dominee in GetSortedBlocksImmediatelyDominatedBy(g, b, totalOrder)) { - Contract.Assert(dominee != null); - VCExprLetBinding c = NestedBlockEquation(dominee, BlkCorrect, BlkReached, totalOrder, context, g, gen); - bindings.Add(c); - } - - VCExpr X = gen.Let(bindings, SuccCorrect); - VCExpr wlp = Wlp.Block(b, X, context); - if (BlkReached != null) { - wlp = gen.Implies((VCExprVar)BlkReached[b], wlp); - Contract.Assert(wlp != null); - } - VCExprVar okVar = cce.NonNull((VCExprVar)BlkCorrect[b]); - return gen.LetBinding(okVar, wlp); - } - - /// - /// Returns a list of g.ImmediatelyDominatedBy(b), but in a sorted order, hoping to steer around - /// the nondeterminism problems we've been seeing by using just this call. - /// - static List/*!*/ GetSortedBlocksImmediatelyDominatedBy(Graph/*!*/ g, Block/*!*/ b, Hashtable/*Block->int*//*!*/ totalOrder) { - Contract.Requires(g != null); - Contract.Requires(b != null); - Contract.Requires(totalOrder != null); - Contract.Ensures(Contract.Result>() != null); - - List list = new List(); - foreach (Block dominee in g.ImmediatelyDominatedBy(b)) { - Contract.Assert(dominee != null); - list.Add(dominee); - } - list.Sort(new Comparison(delegate(Block x, Block y) { - return (int)cce.NonNull(totalOrder[x]) - (int)cce.NonNull(totalOrder[y]); - })); - return list; - } - - static VCExpr VCViaStructuredProgram - (Implementation impl, Dictionary label2absy, - ProverContext proverCtxt, - out int assertionCount) - { - Contract.Requires(impl != null); - Contract.Requires(label2absy != null); - Contract.Requires(proverCtxt != null); - Contract.Ensures(Contract.Result() != null); - - #region Convert block structure back to a "regular expression" - RE r = DAG2RE.Transform(cce.NonNull(impl.Blocks[0])); - Contract.Assert(r != null); - #endregion - - VCContext ctxt = new VCContext(label2absy, proverCtxt); - Contract.Assert(ctxt != null); - #region Send wlp(program,true) to Simplify - var vcexp = Wlp.RegExpr(r, VCExpressionGenerator.True, ctxt); - assertionCount = ctxt.AssertionCount; - return vcexp; - #endregion - } - - /// - /// Remove empty blocks reachable from the startBlock of the CFG - /// - static void RemoveEmptyBlocksIterative(List blocks) { - // postorder traversal of cfg - // noting loop heads in [keep] and - // generating token information in [renameInfo] - Block startBlock = blocks[0]; - var postorder = new List(); - var keep = new HashSet(); - var visited = new HashSet(); - var grey = new HashSet(); - var stack = new Stack(); - Dictionary renameInfo = new Dictionary(); - - stack.Push(startBlock); - visited.Add(startBlock); - while (stack.Count != 0) { - var curr = stack.Pop(); - if (grey.Contains(curr)) { - postorder.Add(curr); - - // generate renameInfoForStartBlock - GotoCmd gtc = curr.TransferCmd as GotoCmd; - renameInfo[curr] = null; - if (gtc == null || gtc.labelTargets == null || gtc.labelTargets.Count == 0) { - if (curr.Cmds.Count == 0 && curr.tok.IsValid) { - renameInfo[curr] = curr; - } - } else { - if (curr.Cmds.Count == 0 || curr == startBlock) { - if (curr.tok.IsValid) { - renameInfo[curr] = curr; - } else { - HashSet successorRenameInfo = new HashSet(); - foreach (Block s in gtc.labelTargets) { - if (keep.Contains(s)) { - successorRenameInfo.Add(null); - } else { - successorRenameInfo.Add(renameInfo[s]); - } - } - if (successorRenameInfo.Count == 1) { - renameInfo[curr] = successorRenameInfo.Single(); - } - } - } - } - // end generate renameInfoForStartBlock - - } else { - grey.Add(curr); - stack.Push(curr); - GotoCmd gtc = curr.TransferCmd as GotoCmd; - if (gtc == null || gtc.labelTargets == null || gtc.labelTargets.Count == 0) continue; - foreach (Block s in gtc.labelTargets) { - if (!visited.Contains(s)) { - visited.Add(s); - stack.Push(s); - } else if (grey.Contains(s) && !postorder.Contains(s)) { // s is a loop head - keep.Add(s); - } - } - } - } - keep.Add(startBlock); - - foreach (Block b in postorder) { - if (!keep.Contains(b) && b.Cmds.Count == 0) { - GotoCmd bGtc = b.TransferCmd as GotoCmd; - foreach (Block p in b.Predecessors) { - GotoCmd pGtc = p.TransferCmd as GotoCmd; - Contract.Assert(pGtc != null); - pGtc.labelTargets.Remove(b); - pGtc.labelNames.Remove(b.Label); - } - if (bGtc == null || bGtc.labelTargets == null || bGtc.labelTargets.Count == 0) { - continue; - } - - List successors = bGtc.labelTargets; - - // Try to push token information if possible - if (b.tok.IsValid && successors.Count == 1 && b != renameInfo[startBlock]) { - var s = successors.Single(); - if (!s.tok.IsValid) { - foreach (Block p in s.Predecessors) { - if (p != b) { - GotoCmd pGtc = p.TransferCmd as GotoCmd; - Contract.Assert(pGtc != null); - pGtc.labelTargets.Remove(s); - pGtc.labelNames.Remove(s.Label); - pGtc.labelTargets.Add(s); - pGtc.labelNames.Add(b.Label); - } - } - s.tok = b.tok; - s.Label = b.Label; - } - } - - foreach (Block p in b.Predecessors) { - GotoCmd pGtc = p.TransferCmd as GotoCmd; - Contract.Assert(pGtc != null); - foreach (Block s in successors) { - if (!pGtc.labelTargets.Contains(s)) { - pGtc.labelTargets.Add(s); - pGtc.labelNames.Add(s.Label); - } - } - } - } - } - - if (!startBlock.tok.IsValid && startBlock.Cmds.All(c => c is AssumeCmd)) { - if (renameInfo[startBlock] != null) { - startBlock.tok = renameInfo[startBlock].tok; - startBlock.Label = renameInfo[startBlock].Label; - } - } - - } - - /// - /// Remove the empty blocks reachable from the block. - /// It changes the visiting state of the blocks, so that if you want to visit again the blocks, you have to reset them... - /// - static List RemoveEmptyBlocks(Block b) { - Contract.Requires(b != null); - Contract.Ensures(Contract.Result>() != null); - - Contract.Assert(b.TraversingStatus == Block.VisitState.ToVisit); - Block renameInfo; - List retVal = removeEmptyBlocksWorker(b, true, out renameInfo); - if (renameInfo != null && !b.tok.IsValid) { - bool onlyAssumes = true; - foreach (Cmd c in b.Cmds) { - if (!(c is AssumeCmd)) { - onlyAssumes = false; - break; - } - } - if (onlyAssumes) { - b.tok = renameInfo.tok; - b.Label = renameInfo.Label; - } - } - return retVal; - } - - /// - /// For every not-yet-visited block n reachable from b, change n's successors to skip empty nodes. - /// Return the *set* of blocks reachable from b without passing through a nonempty block. - /// The target of any backedge is counted as a nonempty block. - /// If renameInfoForStartBlock is non-null, it denotes an empty block with location information, and that - /// information would be appropriate to display - /// - private static List removeEmptyBlocksWorker(Block b, bool startNode, out Block renameInfoForStartBlock) - { - Contract.Requires(b != null); - Contract.Ensures(Contract.ValueAtReturn(out renameInfoForStartBlock) == null || Contract.ValueAtReturn(out renameInfoForStartBlock).tok.IsValid); - // ensures: b in result ==> renameInfoForStartBlock == null; - - renameInfoForStartBlock = null; - List bs = new List(); - GotoCmd gtc = b.TransferCmd as GotoCmd; - - // b has no successors - if (gtc == null || gtc.labelTargets == null || gtc.labelTargets.Count == 0) - { - if (b.Cmds.Count != 0){ // only empty blocks are removed... - bs.Add(b); - } else if (b.tok.IsValid) { - renameInfoForStartBlock = b; - } - return bs; - } - else if (b.TraversingStatus == Block.VisitState.ToVisit) // if b has some successors and we have not seen it so far... - { - b.TraversingStatus = Block.VisitState.BeingVisited; - - // Before recursing down to successors, make a sobering observation: - // If b has no commands and is not the start node, then it will see - // extinction (because it will not be included in the "return setOfSuccessors" - // statement below). In that case, if b has a location, then the location - // information would be lost. Hence, make an attempt to save the location - // by pushing the location onto b's successor. This can be done if (0) b has - // exactly one successor, (1) that successor has no location of its own, and - // (2) that successor has no other predecessors. - if (b.Cmds.Count == 0 && !startNode) { - // b is about to become extinct; try to save its name and location, if possible - if (b.tok.IsValid && gtc.labelTargets.Count == 1) { - Block succ = cce.NonNull(gtc.labelTargets[0]); - if (!succ.tok.IsValid && succ.Predecessors.Count == 1) { - succ.tok = b.tok; - succ.Label = b.Label; - } - } - } - - // recursively call this method on each successor - // merge result into a *set* of blocks - HashSet mergedSuccessors = new HashSet(); - int m = 0; // in the following loop, set renameInfoForStartBlock to the value that all recursive calls agree on, if possible; otherwise, null - foreach (Block dest in gtc.labelTargets){Contract.Assert(dest != null); - Block renameInfo; - List ys = removeEmptyBlocksWorker(dest, false, out renameInfo); - Contract.Assert(ys != null); - if (m == 0) { - renameInfoForStartBlock = renameInfo; - } else if (renameInfoForStartBlock != renameInfo) { - renameInfoForStartBlock = null; - } - foreach (Block successor in ys){ - if (!mergedSuccessors.Contains(successor)) - mergedSuccessors.Add(successor); - } - m++; - } - b.TraversingStatus = Block.VisitState.AlreadyVisited; - - List setOfSuccessors = new List(); - foreach (Block d in mergedSuccessors) - setOfSuccessors.Add(d); - if (b.Cmds.Count == 0 && !startNode) { - // b is about to become extinct - if (b.tok.IsValid) { - renameInfoForStartBlock = b; - } - return setOfSuccessors; - } - // otherwise, update the list of successors of b to be the blocks in setOfSuccessors - gtc.labelTargets = setOfSuccessors; - gtc.labelNames = new List(); - foreach (Block d in setOfSuccessors){ - Contract.Assert(d != null); - gtc.labelNames.Add(d.Label);} - if (!startNode) { - renameInfoForStartBlock = null; - } - return new List { b }; - } - else // b has some successors, but we are already visiting it, or we have already visited it... - { - return new List { b }; - } - } - - static void DumpMap(Hashtable /*Variable->Expr*/ map) { - Contract.Requires(map != null); - foreach (DictionaryEntry de in map) { - Variable v = (Variable)de.Key; - Contract.Assert(v != null); - Expr e = (Expr)de.Value; - Contract.Assert(e != null); - Console.Write(" "); - v.Emit(new TokenTextWriter("", Console.Out, /*setTokens=*/ false, /*pretty=*/ false), 0); - Console.Write(" --> "); - e.Emit(new TokenTextWriter("", Console.Out, /*setTokens=*/ false, /*pretty=*/ false)); - Console.WriteLine(); - } - } - } -} +//----------------------------------------------------------------------------- +// +// Copyright (C) Microsoft Corporation. All Rights Reserved. +// +//----------------------------------------------------------------------------- +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.IO; +using Microsoft.Boogie; +using Microsoft.Boogie.GraphUtil; +using System.Diagnostics.Contracts; +using Microsoft.Basetypes; +using Microsoft.Boogie.VCExprAST; + +namespace VC { + using Bpl = Microsoft.Boogie; + using System.Threading.Tasks; + + public class VCGen : ConditionGeneration { + private const bool _print_time = false; + /// + /// Constructor. Initializes the theorem prover. + /// + [NotDelayed] + public VCGen(Program program, string/*?*/ logFilePath, bool appendLogFile, List checkers) + : base(program, checkers) + { + Contract.Requires(program != null); + this.appendLogFile = appendLogFile; + this.logFilePath = logFilePath; + } + + private static AssumeCmd AssertTurnedIntoAssume(AssertCmd assrt) { + Contract.Requires(assrt != null); + Contract.Ensures(Contract.Result() != null); + + Expr expr = assrt.Expr; + Contract.Assert(expr != null); + switch (Wlp.Subsumption(assrt)) { + case CommandLineOptions.SubsumptionOption.Never: + expr = Expr.True; + break; + case CommandLineOptions.SubsumptionOption.Always: + break; + case CommandLineOptions.SubsumptionOption.NotForQuantifiers: + if (expr is QuantifierExpr) { + expr = Expr.True; + } + break; + default: + Contract.Assert(false); + throw new cce.UnreachableException(); // unexpected case + } + + return new AssumeCmd(assrt.tok, expr); + } + + #region Soundness smoke tester + class SmokeTester { + [ContractInvariantMethod] + void ObjectInvariant() { + Contract.Invariant(parent != null); + Contract.Invariant(impl != null); + Contract.Invariant(initial != null); + Contract.Invariant(cce.NonNullDictionaryAndValues(copies)); + Contract.Invariant(cce.NonNull(visited)); + Contract.Invariant(callback != null); + } + + VCGen parent; + Implementation impl; + Block initial; + int id; + Dictionary copies = new Dictionary(); + HashSet visited = new HashSet(); + VerifierCallback callback; + + internal SmokeTester(VCGen par, Implementation i, VerifierCallback callback) { + Contract.Requires(par != null); + Contract.Requires(i != null); + Contract.Requires(callback != null); + parent = par; + impl = i; + initial = i.Blocks[0]; + this.callback = callback; + } + + internal void Copy() { + CloneBlock(impl.Blocks[0]); + initial = GetCopiedBlocks()[0]; + } + + internal void Test() { + Contract.EnsuresOnThrow(true); + + DFS(initial); + } + + void TopologicalSortImpl() { + Graph dag = new Graph(); + dag.AddSource(cce.NonNull(impl.Blocks[0])); // there is always at least one node in the graph + foreach (Block b in impl.Blocks) { + GotoCmd gtc = b.TransferCmd as GotoCmd; + if (gtc != null) { + Contract.Assume(gtc.labelTargets != null); + foreach (Block dest in gtc.labelTargets) { + Contract.Assert(dest != null); + dag.AddEdge(b, dest); + } + } + } + impl.Blocks = new List(); + foreach (Block b in dag.TopologicalSort()) { + Contract.Assert(b != null); + impl.Blocks.Add(b); + } + } + + void Emit() { + TopologicalSortImpl(); + EmitImpl(impl, false); + } + + // this one copies forward + Block CloneBlock(Block b) { + Contract.Requires(b != null); + Contract.Ensures(Contract.Result() != null); + + Block fake_res; + if (copies.TryGetValue(b, out fake_res)) { + return cce.NonNull(fake_res); + } + Block res = new Block(b.tok, b.Label, new List(b.Cmds), null); + copies[b] = res; + if (b.TransferCmd is GotoCmd) { + foreach (Block ch in cce.NonNull((GotoCmd)b.TransferCmd).labelTargets) { + Contract.Assert(ch != null); + CloneBlock(ch); + } + } + foreach (Block p in b.Predecessors) { + Contract.Assert(p != null); + res.Predecessors.Add(CloneBlock(p)); + } + return res; + } + + // this one copies backwards + Block CopyBlock(Block b) { + Contract.Requires(b != null); + Contract.Ensures(Contract.Result() != null); + + Block fake_res; + if (copies.TryGetValue(b, out fake_res)) { + // fake_res should be Block! but the compiler fails + return cce.NonNull(fake_res); + } + Block res; + List seq = new List(); + foreach (Cmd c in b.Cmds) { + Contract.Assert(c != null); + AssertCmd turn = c as AssertCmd; + if (!turnAssertIntoAssumes || turn == null) { + seq.Add(c); + } else { + seq.Add(AssertTurnedIntoAssume(turn)); + } + } + res = new Block(b.tok, b.Label, seq, null); + copies[b] = res; + foreach (Block p in b.Predecessors) { + Contract.Assert(p != null); + res.Predecessors.Add(CopyBlock(p)); + } + return res; + } + + List GetCopiedBlocks() { + Contract.Ensures(cce.NonNullElements(Contract.Result>())); + + // the order of nodes in res is random (except for the first one, being the entry) + List res = new List(); + res.Add(copies[initial]); + + foreach (KeyValuePair kv in copies) { + Contract.Assert(kv.Key != null&&kv.Value!=null); + GotoCmd go = kv.Key.TransferCmd as GotoCmd; + ReturnCmd ret = kv.Key.TransferCmd as ReturnCmd; + if (kv.Key != initial) { + res.Add(kv.Value); + } + if (go != null) { + GotoCmd copy = new GotoCmd(go.tok, new List(), new List()); + kv.Value.TransferCmd = copy; + foreach (Block b in cce.NonNull(go.labelTargets)) { + Contract.Assert(b != null); + Block c; + if (copies.TryGetValue(b, out c)) { + copy.AddTarget(cce.NonNull(c)); + } + } + } else if (ret != null) { + kv.Value.TransferCmd = ret; + } else { + Contract.Assume(false); + throw new cce.UnreachableException(); + } + } + + copies.Clear(); + + return res; + } + + // check if e is true, false, !true, !false + // if so return true and the value of the expression in val + bool BooleanEval(Expr e, ref bool val) { + Contract.Requires(e != null); + LiteralExpr lit = e as LiteralExpr; + NAryExpr call = e as NAryExpr; + + if (lit != null && lit.isBool) { + val = lit.asBool; + return true; + } else if (call != null && + call.Fun is UnaryOperator && + ((UnaryOperator)call.Fun).Op == UnaryOperator.Opcode.Not && + BooleanEval(cce.NonNull(call.Args[0]), ref val)) { + val = !val; + return true; + } + // this is for the 0bv32 != 0bv32 generated by vcc + else if (call != null && + call.Fun is BinaryOperator && + ((BinaryOperator)call.Fun).Op == BinaryOperator.Opcode.Neq && + call.Args[0] is LiteralExpr && + cce.NonNull(call.Args[0]).Equals(call.Args[1])) { + val = false; + return true; + } + + return false; + } + + bool IsFalse(Expr e) { + Contract.Requires(e != null); + bool val = false; + return BooleanEval(e, ref val) && !val; + } + + bool CheckUnreachable(Block cur, List seq) + { + Contract.Requires(cur != null); + Contract.Requires(seq != null); + Contract.EnsuresOnThrow(true); + foreach (Cmd cmd in seq) + { + AssertCmd assrt = cmd as AssertCmd; + if (assrt != null && QKeyValue.FindBoolAttribute(assrt.Attributes, "PossiblyUnreachable")) + return false; + } + + DateTime start = DateTime.UtcNow; + if (CommandLineOptions.Clo.Trace) + { + System.Console.Write(" soundness smoke test #{0} ... ", id); + } + callback.OnProgress("smoke", id, id, 0.0); + + Token tok = new Token(); + tok.val = "soundness smoke test assertion"; + seq.Add(new AssertCmd(tok, Expr.False)); + Block copy = CopyBlock(cur); + Contract.Assert(copy != null); + copy.Cmds = seq; + List backup = impl.Blocks; + Contract.Assert(backup != null); + impl.Blocks = GetCopiedBlocks(); + copy.TransferCmd = new ReturnCmd(Token.NoToken); + if (CommandLineOptions.Clo.TraceVerify) + { + System.Console.WriteLine(); + System.Console.WriteLine(" --- smoke #{0}, before passify", id); + Emit(); + } + parent.CurrentLocalVariables = impl.LocVars; + ModelViewInfo mvInfo; + parent.PassifyImpl(impl, out mvInfo); + Dictionary label2Absy; + Checker ch = parent.FindCheckerFor(CommandLineOptions.Clo.SmokeTimeout); + Contract.Assert(ch != null); + + ProverInterface.Outcome outcome = ProverInterface.Outcome.Undetermined; + try + { + lock (ch) + { + var exprGen = ch.TheoremProver.Context.ExprGen; + VCExpr controlFlowVariableExpr = CommandLineOptions.Clo.UseLabels ? null : exprGen.Integer(BigNum.ZERO); + + VCExpr vc = parent.GenerateVC(impl, controlFlowVariableExpr, out label2Absy, ch.TheoremProver.Context); + Contract.Assert(vc != null); + + if (!CommandLineOptions.Clo.UseLabels) + { + VCExpr controlFlowFunctionAppl = exprGen.ControlFlowFunctionApplication(exprGen.Integer(BigNum.ZERO), exprGen.Integer(BigNum.ZERO)); + VCExpr eqExpr = exprGen.Eq(controlFlowFunctionAppl, exprGen.Integer(BigNum.FromInt(impl.Blocks[0].UniqueId))); + vc = exprGen.Implies(eqExpr, vc); + } + + impl.Blocks = backup; + + if (CommandLineOptions.Clo.TraceVerify) + { + System.Console.WriteLine(" --- smoke #{0}, after passify", id); + Emit(); + } + + ch.BeginCheck(cce.NonNull(impl.Name + "_smoke" + id++), vc, new ErrorHandler(label2Absy, this.callback)); + } + + ch.ProverTask.Wait(); + + lock (ch) + { + outcome = ch.ReadOutcome(); + } + } + finally + { + ch.GoBackToIdle(); + } + + parent.CurrentLocalVariables = null; + + DateTime end = DateTime.UtcNow; + TimeSpan elapsed = end - start; + if (CommandLineOptions.Clo.Trace) + { + System.Console.WriteLine(" [{0} s] {1}", elapsed.TotalSeconds, + outcome == ProverInterface.Outcome.Valid ? "OOPS" : + "OK" + (outcome == ProverInterface.Outcome.Invalid ? "" : " (" + outcome + ")")); + } + + if (outcome == ProverInterface.Outcome.Valid) + { + // copy it again, so we get the version with calls, assignments and such + copy = CopyBlock(cur); + copy.Cmds = seq; + impl.Blocks = GetCopiedBlocks(); + TopologicalSortImpl(); + callback.OnUnreachableCode(impl); + impl.Blocks = backup; + return true; + } + return false; + } + + const bool turnAssertIntoAssumes = false; + + void DFS(Block cur) { + Contract.Requires(cur != null); + Contract.EnsuresOnThrow(true); + if (visited.Contains(cur)) + return; + visited.Add(cur); + + List seq = new List(); + foreach (Cmd cmd_ in cur.Cmds) { + Cmd cmd = cmd_; + Contract.Assert(cmd != null); + AssertCmd assrt = cmd as AssertCmd; + AssumeCmd assm = cmd as AssumeCmd; + CallCmd call = cmd as CallCmd; + + bool assumeFalse = false; + + if (assrt != null) { + // we're not going any further + // it's clear the user expected unreachable code here + // it's not clear where did he expect it, maybe it would be right to insert + // a check just one command before + if (IsFalse(assrt.Expr)) + return; + +#if TURN_ASSERT_INFO_ASSUMES + if (turnAssertIntoAssumes) { + cmd = AssertTurnedIntoAssume(assrt); + } +#endif + } else if (assm != null) { + if (IsFalse(assm.Expr)) + assumeFalse = true; + } else if (call != null) { + foreach (Ensures e in (cce.NonNull(call.Proc)).Ensures) { + Contract.Assert(e != null); + if (IsFalse(e.Condition)) + assumeFalse = true; + } + } + + if (assumeFalse) { + CheckUnreachable(cur, seq); + return; + } + + seq.Add(cmd); + } + + + GotoCmd go = cur.TransferCmd as GotoCmd; + ReturnCmd ret = cur.TransferCmd as ReturnCmd; + + Contract.Assume(!(go != null && go.labelTargets == null && go.labelNames != null && go.labelNames.Count > 0)); + + 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); + } else if (go != null) { + bool needToCheck = true; + // if all of our children have more than one parent, then + // we're in the right place to check + foreach (Block target in cce.NonNull(go.labelTargets)) { + Contract.Assert(target != null); + if (target.Predecessors.Count == 1) { + needToCheck = false; + } + } + if (needToCheck) { + CheckUnreachable(cur, seq); + } + foreach (Block target in go.labelTargets) { + Contract.Assert(target != null); + DFS(target); + } + } + } + + class ErrorHandler : ProverInterface.ErrorHandler { + Dictionary label2Absy; + VerifierCallback callback; + [ContractInvariantMethod] + void ObjectInvariant() { + Contract.Invariant(label2Absy != null); + Contract.Invariant(callback != null); + } + + + public ErrorHandler(Dictionary label2Absy, VerifierCallback callback) { + Contract.Requires(label2Absy != null); + Contract.Requires(callback != null); + this.label2Absy = label2Absy; + this.callback = callback; + } + + public override Absy Label2Absy(string label) { + //Contract.Requires(label != null); + Contract.Ensures(Contract.Result() != null); + + int id = int.Parse(label); + return cce.NonNull((Absy)label2Absy[id]); + } + + public override void OnProverWarning(string msg) { + //Contract.Requires(msg != null); + this.callback.OnWarning(msg); + } + } + } + + + #endregion + + #region Splitter + class Split { + class BlockStats { + public bool big_block; + public int id; + public double assertion_cost; + public double assumption_cost; // before multiplier + public double incomming_paths; + public List/*!>!*/ virtual_successors = new List(); + public List/*!>!*/ virtual_predecesors = new List(); + public HashSet reachable_blocks; + public readonly Block block; + [ContractInvariantMethod] + void ObjectInvariant() { + Contract.Invariant(cce.NonNullElements(virtual_successors)); + Contract.Invariant(cce.NonNullElements(virtual_predecesors)); + Contract.Invariant(block != null); + } + + + public BlockStats(Block b, int i) { + Contract.Requires(b != null); + block = b; + assertion_cost = -1; + id = i; + } + } + [ContractInvariantMethod] + void ObjectInvariant() { + Contract.Invariant(cce.NonNullElements(blocks)); + Contract.Invariant(cce.NonNullElements(big_blocks)); + Contract.Invariant(cce.NonNullDictionaryAndValues(stats)); + Contract.Invariant(cce.NonNullElements(assumized_branches)); + Contract.Invariant(gotoCmdOrigins != null); + Contract.Invariant(parent != null); + Contract.Invariant(impl != null); + Contract.Invariant(copies != null); + Contract.Invariant(cce.NonNull(protected_from_assert_to_assume)); + Contract.Invariant(cce.NonNull(keep_at_all)); + } + + + readonly List blocks; + readonly List big_blocks = new List(); + readonly Dictionary/*!*/ stats = new Dictionary(); + readonly int id; + static int current_id = -1; + Block split_block; + bool assert_to_assume; + List/*!*/ assumized_branches = new List(); + + double score; + bool score_computed; + double total_cost; + int assertion_count; + double assertion_cost; // without multiplication by paths + Dictionary/*!*/ gotoCmdOrigins; + readonly public VCGen/*!*/ parent; + Implementation/*!*/ impl; + + Dictionary/*!*/ copies = new Dictionary(); + bool doing_slice; + double slice_initial_limit; + double slice_limit; + bool slice_pos; + HashSet/*!*/ protected_from_assert_to_assume = new HashSet(); + HashSet/*!*/ keep_at_all = new HashSet(); + + // async interface + private Checker checker; + private int splitNo; + internal ErrorReporter reporter; + + public Split(List/*!*/ blocks, Dictionary/*!*/ gotoCmdOrigins, VCGen/*!*/ par, Implementation/*!*/ impl) { + Contract.Requires(cce.NonNullElements(blocks)); + Contract.Requires(gotoCmdOrigins != null); + Contract.Requires(par != null); + Contract.Requires(impl != null); + this.blocks = blocks; + this.gotoCmdOrigins = gotoCmdOrigins; + this.parent = par; + this.impl = impl; + this.id = Interlocked.Increment(ref current_id); + } + + public double Cost { + get { + ComputeBestSplit(); + return total_cost; + } + } + + public bool LastChance { + get { + ComputeBestSplit(); + return assertion_count == 1 && score < 0; + } + } + + public string Stats { + get { + ComputeBestSplit(); + return string.Format("(cost:{0:0}/{1:0}{2})", total_cost, assertion_cost, LastChance ? " last" : ""); + } + } + + public void DumpDot(int no) { + using (System.IO.StreamWriter sw = System.IO.File.CreateText(string.Format("split.{0}.dot", no))) { + sw.WriteLine("digraph G {"); + + ComputeBestSplit(); + List saved = assumized_branches; + Contract.Assert(saved != null); + assumized_branches = new List(); + DoComputeScore(false); + assumized_branches = saved; + + foreach (Block b in big_blocks) { + Contract.Assert(b != null); + BlockStats s = GetBlockStats(b); + foreach (Block t in s.virtual_successors) { + Contract.Assert(t != null); + sw.WriteLine("n{0} -> n{1};", s.id, GetBlockStats(t).id); + } + sw.WriteLine("n{0} [label=\"{1}:\\n({2:0.0}+{3:0.0})*{4:0.0}\"{5}];", + s.id, b.Label, + s.assertion_cost, s.assumption_cost, s.incomming_paths, + s.assertion_cost > 0 ? ",shape=box" : ""); + + } + sw.WriteLine("}"); + sw.Close(); + } + + string filename = string.Format("split.{0}.bpl", no); + 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; + List backup = impl.Blocks; + Contract.Assert(backup != null); + impl.Blocks = blocks; + impl.Emit(new TokenTextWriter(filename, sw, /*setTokens=*/ false, /*pretty=*/ false), 0); + impl.Blocks = backup; + CommandLineOptions.Clo.PrintDesugarings = oldPrintDesugaringSetting; + CommandLineOptions.Clo.PrintUnstructured = oldPrintUnstructured; + } + } + + int bsid; + BlockStats GetBlockStats(Block b) { + Contract.Requires(b != null); + Contract.Ensures(Contract.Result() != null); + + BlockStats s; + if (!stats.TryGetValue(b, out s)) { + s = new BlockStats(b, bsid++); + stats[b] = s; + } + return cce.NonNull(s); + } + + double AssertionCost(PredicateCmd c) { + return 1.0; + } + + void CountAssertions(Block b) { + Contract.Requires(b != null); + BlockStats s = GetBlockStats(b); + if (s.assertion_cost >= 0) + return; // already done + s.big_block = true; + s.assertion_cost = 0; + s.assumption_cost = 0; + foreach (Cmd c in b.Cmds) { + if (c is AssertCmd) { + double cost = AssertionCost((AssertCmd)c); + s.assertion_cost += cost; + assertion_count++; + assertion_cost += cost; + } else if (c is AssumeCmd) { + s.assumption_cost += AssertionCost((AssumeCmd)c); + } + } + foreach (Block c in Exits(b)) { + Contract.Assert(c != null); + s.virtual_successors.Add(c); + } + if (s.virtual_successors.Count == 1) { + Block next = s.virtual_successors[0]; + BlockStats se = GetBlockStats(next); + CountAssertions(next); + if (next.Predecessors.Count > 1 || se.virtual_successors.Count != 1) + return; + s.virtual_successors[0] = se.virtual_successors[0]; + s.assertion_cost += se.assertion_cost; + s.assumption_cost += se.assumption_cost; + se.big_block = false; + } + } + + HashSet/*!*/ ComputeReachableNodes(Block/*!*/ b) { + Contract.Requires(b != null); + Contract.Ensures(cce.NonNull(Contract.Result>())); + BlockStats s = GetBlockStats(b); + if (s.reachable_blocks != null) { + return s.reachable_blocks; + } + HashSet blocks = new HashSet(); + s.reachable_blocks = blocks; + blocks.Add(b); + foreach (Block/*!*/ succ in Exits(b)) { + Contract.Assert(succ != null); + foreach (Block r in ComputeReachableNodes(succ)) { + Contract.Assert(r != null); + blocks.Add(r); + } + } + return blocks; + } + + double ProverCost(double vc_cost) { + return vc_cost * vc_cost; + } + + void ComputeBestSplit() { + if (score_computed) + return; + score_computed = true; + + assertion_count = 0; + + foreach (Block b in blocks) { + Contract.Assert(b != null); + CountAssertions(b); + } + + foreach (Block b in blocks) { + Contract.Assert(b != null); + BlockStats bs = GetBlockStats(b); + if (bs.big_block) { + big_blocks.Add(b); + foreach (Block ch in bs.virtual_successors) { + Contract.Assert(ch != null); + BlockStats chs = GetBlockStats(ch); + if (!chs.big_block) { + Console.WriteLine("non-big {0} accessed from {1}", ch, b); + DumpDot(-1); + Contract.Assert(false); + throw new cce.UnreachableException(); + } + chs.virtual_predecesors.Add(b); + } + } + } + + assumized_branches.Clear(); + total_cost = ProverCost(DoComputeScore(false)); + + score = double.PositiveInfinity; + Block best_split = null; + List saved_branches = new List(); + + foreach (Block b in big_blocks) { + Contract.Assert(b != null); + GotoCmd gt = b.TransferCmd as GotoCmd; + if (gt == null) + continue; + List targ = cce.NonNull(gt.labelTargets); + if (targ.Count < 2) + continue; + // caution, we only consider two first exits + + double left0, right0, left1, right1; + split_block = b; + + assumized_branches.Clear(); + assumized_branches.Add(cce.NonNull(targ[0])); + left0 = DoComputeScore(true); + right0 = DoComputeScore(false); + + assumized_branches.Clear(); + for (int idx = 1; idx < targ.Count; idx++) { + assumized_branches.Add(cce.NonNull(targ[idx])); + } + left1 = DoComputeScore(true); + right1 = DoComputeScore(false); + + double current_score = ProverCost(left1) + ProverCost(right1); + double other_score = ProverCost(left0) + ProverCost(right0); + + if (other_score < current_score) { + current_score = other_score; + assumized_branches.Clear(); + assumized_branches.Add(cce.NonNull(targ[0])); + } + + if (current_score < score) { + score = current_score; + best_split = split_block; + saved_branches.Clear(); + saved_branches.AddRange(assumized_branches); + } + } + + if (CommandLineOptions.Clo.VcsPathSplitMult * score > total_cost) { + split_block = null; + score = -1; + } else { + assumized_branches = saved_branches; + split_block = best_split; + } + } + + void UpdateIncommingPaths(BlockStats s) { + Contract.Requires(s != null); + if (s.incomming_paths < 0.0) { + int count = 0; + s.incomming_paths = 0.0; + if (!keep_at_all.Contains(s.block)) + return; + foreach (Block b in s.virtual_predecesors) { + Contract.Assert(b != null); + BlockStats ch = GetBlockStats(b); + Contract.Assert(ch != null); + UpdateIncommingPaths(ch); + if (ch.incomming_paths > 0.0) { + s.incomming_paths += ch.incomming_paths; + count++; + } + } + if (count > 1) { + s.incomming_paths *= CommandLineOptions.Clo.VcsPathJoinMult; + } + } + } + + void ComputeBlockSetsHelper(Block b, bool allow_small) { + Contract.Requires(b != null); + if (keep_at_all.Contains(b)) + return; + keep_at_all.Add(b); + + if (allow_small) { + foreach (Block ch in Exits(b)) { + Contract.Assert(ch != null); + if (b == split_block && assumized_branches.Contains(ch)) + continue; + ComputeBlockSetsHelper(ch, allow_small); + } + } else { + foreach (Block ch in GetBlockStats(b).virtual_successors) { + Contract.Assert(ch != null); + if (b == split_block && assumized_branches.Contains(ch)) + continue; + ComputeBlockSetsHelper(ch, allow_small); + } + } + } + + void ComputeBlockSets(bool allow_small) { + protected_from_assert_to_assume.Clear(); + keep_at_all.Clear(); + + Debug.Assert(split_block == null || GetBlockStats(split_block).big_block); + Debug.Assert(GetBlockStats(blocks[0]).big_block); + + if (assert_to_assume) { + foreach (Block b in allow_small ? blocks : big_blocks) { + Contract.Assert(b != null); + if (ComputeReachableNodes(b).Contains(cce.NonNull(split_block))) { + keep_at_all.Add(b); + } + } + + foreach (Block b in assumized_branches) { + Contract.Assert(b != null); + foreach (Block r in ComputeReachableNodes(b)) { + Contract.Assert(r != null); + if (allow_small || GetBlockStats(r).big_block) { + keep_at_all.Add(r); + protected_from_assert_to_assume.Add(r); + } + } + } + } else { + ComputeBlockSetsHelper(blocks[0], allow_small); + } + } + + bool ShouldAssumize(Block b) { + Contract.Requires(b != null); + return assert_to_assume && !protected_from_assert_to_assume.Contains(b); + } + + double DoComputeScore(bool aa) { + assert_to_assume = aa; + ComputeBlockSets(false); + + foreach (Block b in big_blocks) { + Contract.Assert(b != null); + GetBlockStats(b).incomming_paths = -1.0; + } + + GetBlockStats(blocks[0]).incomming_paths = 1.0; + + double cost = 0.0; + foreach (Block b in big_blocks) { + Contract.Assert(b != null); + if (keep_at_all.Contains(b)) { + BlockStats s = GetBlockStats(b); + UpdateIncommingPaths(s); + double local = s.assertion_cost; + if (ShouldAssumize(b)) { + local = (s.assertion_cost + s.assumption_cost) * CommandLineOptions.Clo.VcsAssumeMult; + } else { + local = s.assumption_cost * CommandLineOptions.Clo.VcsAssumeMult + s.assertion_cost; + } + local = local + local * s.incomming_paths * CommandLineOptions.Clo.VcsPathCostMult; + cost += local; + } + } + + return cost; + } + + List SliceCmds(Block b) { + Contract.Requires(b != null); + Contract.Ensures(Contract.Result>() != null); + + List seq = b.Cmds; + Contract.Assert(seq != null); + if (!doing_slice && !ShouldAssumize(b)) + return seq; + List res = new List(); + foreach (Cmd c in seq) { + Contract.Assert(c != null); + AssertCmd a = c as AssertCmd; + Cmd the_new = c; + bool swap = false; + if (a != null) { + if (doing_slice) { + double cost = AssertionCost(a); + bool first = (slice_limit - cost) >= 0 || slice_initial_limit == slice_limit; + slice_limit -= cost; + swap = slice_pos == first; + } else if (assert_to_assume) { + swap = true; + } else { + Contract.Assert(false); + throw new cce.UnreachableException(); + } + + if (swap) { + the_new = AssertTurnedIntoAssume(a); + } + } + res.Add(the_new); + } + return res; + } + + Block CloneBlock(Block b) { + Contract.Requires(b != null); + Contract.Ensures(Contract.Result() != null); + + Block res; + if (copies.TryGetValue(b, out res)) { + return cce.NonNull(res); + } + res = new Block(b.tok, b.Label, SliceCmds(b), b.TransferCmd); + GotoCmd gt = b.TransferCmd as GotoCmd; + copies[b] = res; + if (gt != null) { + GotoCmd newGoto = new GotoCmd(gt.tok, new List(), new List()); + res.TransferCmd = newGoto; + int pos = 0; + foreach (Block ch in cce.NonNull(gt.labelTargets)) { + Contract.Assert(ch != null); + Contract.Assert(doing_slice || + (assert_to_assume || (keep_at_all.Contains(ch) || assumized_branches.Contains(ch)))); + if (doing_slice || + ((b != split_block || assumized_branches.Contains(ch) == assert_to_assume) && + keep_at_all.Contains(ch))) { + newGoto.AddTarget(CloneBlock(ch)); + } + pos++; + } + } + return res; + } + + Split DoSplit() { + Contract.Ensures(Contract.Result() != null); + + copies.Clear(); + CloneBlock(blocks[0]); + List newBlocks = new List(); + Dictionary newGotoCmdOrigins = new Dictionary(); + foreach (Block b in blocks) { + Contract.Assert(b != null); + Block tmp; + if (copies.TryGetValue(b, out tmp)) { + newBlocks.Add(cce.NonNull(tmp)); + if (gotoCmdOrigins.ContainsKey(b.TransferCmd)) { + newGotoCmdOrigins[tmp.TransferCmd] = gotoCmdOrigins[b.TransferCmd]; + } + + foreach (Block p in b.Predecessors) { + Contract.Assert(p != null); + Block tmp2; + if (copies.TryGetValue(p, out tmp2)) { + tmp.Predecessors.Add(tmp2); + } + } + } + } + + return new Split(newBlocks, newGotoCmdOrigins, parent, impl); + } + + Split SplitAt(int idx) { + Contract.Ensures(Contract.Result() != null); + + assert_to_assume = idx == 0; + doing_slice = false; + ComputeBlockSets(true); + + return DoSplit(); + } + + Split SliceAsserts(double limit, bool pos) { + Contract.Ensures(Contract.Result() != null); + + slice_pos = pos; + slice_limit = limit; + slice_initial_limit = limit; + doing_slice = true; + Split r = DoSplit(); + /* + Console.WriteLine("split {0} / {1} -->", limit, pos); + List tmp = impl.Blocks; + impl.Blocks = r.blocks; + EmitImpl(impl, false); + impl.Blocks = tmp; + */ + + return r; + } + + void Print() { + List tmp = impl.Blocks; + Contract.Assert(tmp != null); + impl.Blocks = blocks; + EmitImpl(impl, false); + impl.Blocks = tmp; + } + + public Counterexample ToCounterexample(ProverContext context) { + Contract.Requires(context != null); + Contract.Ensures(Contract.Result() != null); + + List trace = new List(); + foreach (Block b in blocks) { + Contract.Assert(b != null); + trace.Add(b); + } + foreach (Block b in blocks) { + Contract.Assert(b != null); + foreach (Cmd c in b.Cmds) { + Contract.Assert(c != null); + if (c is AssertCmd) { + return AssertCmdToCounterexample((AssertCmd)c, cce.NonNull(b.TransferCmd), trace, null, null, context); + } + } + } + Contract.Assume(false); + throw new cce.UnreachableException(); + } + + /// + /// Starting from the 0-index "split_here" annotation in begin, verifies until it reaches a subsequent "split_here" annotation + /// Returns a list of blocks where all code not verified has asserts converted into assumes + /// + /// Implementation's collection of blocks + /// Block containing the first split_here from which to start verifying + /// 0-based ID of the "split_here" annotation within begin at which to start verifying + /// True if the entire split is contained within block begin + /// Set of all blocks containing a "split_here" annotation + /// + // Note: Current implementation may over report errors. + // For example, if the control flow graph is a diamond (e.g., A -> B, C, B->D, C->D), + // and there is a split in B and an error in D, then D will be verified twice and hence report the error twice. + // Best solution may be to memoize blocks that have been fully verified and be sure not to verify them again + private static List DoManualSplit(List blocks, Block begin, int begin_split_id, bool blockInternalSplit, IEnumerable endPoints) { + // Compute the set of blocks reachable from begin but not included in endPoints. These will be verified in their entirety. + var blocksToVerifyEntirely = new HashSet(); + var reachableEndPoints = new HashSet(); // Reachable end points will be verified up to their first split point + var todo = new Stack(); + todo.Push(begin); + while (todo.Count > 0) { + var currentBlock = todo.Pop(); + if (blocksToVerifyEntirely.Contains(currentBlock)) continue; + blocksToVerifyEntirely.Add(currentBlock); + var exit = currentBlock.TransferCmd as GotoCmd; + if (exit != null) + foreach (Block targetBlock in exit.labelTargets) { + if (!endPoints.Contains(targetBlock)) { + todo.Push(targetBlock); + } else { + reachableEndPoints.Add(targetBlock); + } + } + + } + blocksToVerifyEntirely.Remove(begin); + + // Convert assumes to asserts in "unreachable" blocks, including portions of blocks containing "split_here" + var newBlocks = new List(blocks.Count()); // Copies of the original blocks + var duplicator = new Duplicator(); + var oldToNewBlockMap = new Dictionary(blocks.Count()); // Maps original blocks to their new copies in newBlocks + + foreach (var currentBlock in blocks) { + var newBlock = (Block)duplicator.VisitBlock(currentBlock); + oldToNewBlockMap[currentBlock] = newBlock; + newBlocks.Add(newBlock); + + if (!blockInternalSplit && blocksToVerifyEntirely.Contains(currentBlock)) continue; // All reachable blocks must be checked in their entirety, so don't change anything + // Otherwise, we only verify a portion of the current block, so we'll need to look at each of its commands + + // !verify -> convert assert to assume + var verify = (currentBlock == begin && begin_split_id == -1) // -1 tells us to start verifying from the very beginning (i.e., there is no split in the begin block) + || (reachableEndPoints.Contains(currentBlock) // This endpoint is reachable from begin, so we verify until we hit the first split point + && !blockInternalSplit); // Don't bother verifying if all of the splitting is within the begin block + var newCmds = new List(); + var split_here_count = 0; + + foreach (Cmd c in currentBlock.Cmds) { + var p = c as PredicateCmd; + if (p != null && QKeyValue.FindBoolAttribute(p.Attributes, "split_here")) { + if (currentBlock == begin) { // Verify everything between the begin_split_id we were given and the next split + if (split_here_count == begin_split_id) { + verify = true; + } else if (split_here_count == begin_split_id + 1) { + verify = false; + } + } else { // We're in an endPoint so we stop verifying as soon as we hit a "split_here" + verify = false; + } + split_here_count++; + } + + var asrt = c as AssertCmd; + if (verify || asrt == null) + newCmds.Add(c); + else + newCmds.Add(AssertTurnedIntoAssume(asrt)); + } + + newBlock.Cmds = newCmds; + } + + // Patch the edges between the new blocks + foreach (var oldBlock in blocks) { + if (oldBlock.TransferCmd is ReturnCmd) { continue; } + var gotoCmd = (GotoCmd)oldBlock.TransferCmd; + var newLabelTargets = new List(gotoCmd.labelTargets.Count()); + var newLabelNames = new List(gotoCmd.labelTargets.Count()); + foreach (var target in gotoCmd.labelTargets) { + newLabelTargets.Add(oldToNewBlockMap[target]); + newLabelNames.Add(oldToNewBlockMap[target].Label); + } + oldToNewBlockMap[oldBlock].TransferCmd = new GotoCmd(gotoCmd.tok, newLabelNames, newLabelTargets); + } + + return newBlocks; + } + + public static List FindManualSplits(Implementation/*!*/ impl, Dictionary/*!*/ gotoCmdOrigins, VCGen/*!*/ par) { + Contract.Requires(impl != null); + Contract.Ensures(Contract.Result>() == null || cce.NonNullElements(Contract.Result>())); + + var splitPoints = new Dictionary(); + foreach (var b in impl.Blocks) { + foreach (Cmd c in b.Cmds) { + var p = c as PredicateCmd; + if (p != null && QKeyValue.FindBoolAttribute(p.Attributes, "split_here")) { + int count; + splitPoints.TryGetValue(b, out count); + splitPoints[b] = count + 1; + } + } + } + + if (splitPoints.Count() == 0) { // No manual split points here + return null; + } + + List splits = new List(); + Block entryPoint = impl.Blocks[0]; + var newEntryBlocks = DoManualSplit(impl.Blocks, entryPoint, -1, splitPoints.Keys.Contains(entryPoint), splitPoints.Keys); + splits.Add(new Split(newEntryBlocks, gotoCmdOrigins, par, impl)); // REVIEW: Does gotoCmdOrigins need to be changed at all? + + foreach (KeyValuePair pair in splitPoints) { + for (int i = 0; i < pair.Value; i++) { + bool blockInternalSplit = i < pair.Value - 1; // There's at least one more split, after this one, in the current block + var newBlocks = DoManualSplit(impl.Blocks, pair.Key, i, blockInternalSplit, splitPoints.Keys); + Split s = new Split(newBlocks, gotoCmdOrigins, par, impl); // REVIEW: Does gotoCmdOrigins need to be changed at all? + splits.Add(s); + } + } + + return splits; + } + + public static List/*!*/ DoSplit(Split initial, double max_cost, int max) { + Contract.Requires(initial != null); + Contract.Ensures(cce.NonNullElements(Contract.Result>())); + + List res = new List(); + res.Add(initial); + + while (res.Count < max) { + Split best = null; + int best_idx = 0, pos = 0; + foreach (Split s in res) { + Contract.Assert(s != null); + s.ComputeBestSplit(); // TODO check total_cost first + if (s.total_cost > max_cost && + (best == null || best.total_cost < s.total_cost) && + (s.assertion_count > 1 || s.split_block != null)) { + best = s; + best_idx = pos; + } + pos++; + } + + if (best == null) + break; // no split found + + Split s0, s1; + + bool split_stats = CommandLineOptions.Clo.TraceVerify; + + if (split_stats) { + Console.WriteLine("{0} {1} -->", best.split_block == null ? "SLICE" : ("SPLIT@" + best.split_block.Label), best.Stats); + if (best.split_block != null) { + GotoCmd g = best.split_block.TransferCmd as GotoCmd; + if (g != null) { + Console.Write(" exits: "); + foreach (Block b in cce.NonNull(g.labelTargets)) { + Contract.Assert(b != null); + Console.Write("{0} ", b.Label); + } + Console.WriteLine(""); + Console.Write(" assumized: "); + foreach (Block b in best.assumized_branches) { + Contract.Assert(b != null); + Console.Write("{0} ", b.Label); + } + Console.WriteLine(""); + } + } + } + + if (best.split_block != null) { + s0 = best.SplitAt(0); + s1 = best.SplitAt(1); + } else { + best.split_block = null; + s0 = best.SliceAsserts(best.assertion_cost / 2, true); + s1 = best.SliceAsserts(best.assertion_cost / 2, false); + } + + if (true) { + List ss = new List(); + ss.Add(s0.blocks[0]); + ss.Add(s1.blocks[0]); + try { + best.SoundnessCheck(new HashSet>(new BlockListComparer()), best.blocks[0], ss); + } catch (System.Exception e) { + Console.WriteLine(e); + best.DumpDot(-1); + s0.DumpDot(-2); + s1.DumpDot(-3); + Contract.Assert(false); + throw new cce.UnreachableException(); + } + } + + if (split_stats) { + s0.ComputeBestSplit(); + s1.ComputeBestSplit(); + Console.WriteLine(" --> {0}", s0.Stats); + Console.WriteLine(" --> {0}", s1.Stats); + } + + if (CommandLineOptions.Clo.TraceVerify) { + best.Print(); + } + + res[best_idx] = s0; + res.Add(s1); + } + + return res; + } + + class BlockListComparer : IEqualityComparer> + { + public bool Equals(List x, List y) + { + return x == y || x.SequenceEqual(y); + } + + public int GetHashCode(List obj) + { + int h = 0; + Contract.Assume(obj != null); + foreach (var b in obj) + { + if (b != null) + { + h += b.GetHashCode(); + } + } + return h; + } + } + + public Checker Checker { + get { + Contract.Ensures(Contract.Result() != null); + + Contract.Assert(checker != null); + return checker; + } + } + + public Task ProverTask { + get { + Contract.Assert(checker != null); + return checker.ProverTask; + } + } + + public void ReadOutcome(ref Outcome cur_outcome, out bool prover_failed) { + Contract.EnsuresOnThrow(true); + ProverInterface.Outcome outcome = cce.NonNull(checker).ReadOutcome(); + + if (CommandLineOptions.Clo.Trace && splitNo >= 0) { + System.Console.WriteLine(" --> split #{0} done, [{1} s] {2}", splitNo, checker.ProverRunTime.TotalSeconds, outcome); + } + + if (CommandLineOptions.Clo.VcsDumpSplits) { + DumpDot(splitNo); + } + + prover_failed = false; + + switch (outcome) { + case ProverInterface.Outcome.Valid: + return; + case ProverInterface.Outcome.Invalid: + cur_outcome = Outcome.Errors; + return; + case ProverInterface.Outcome.OutOfMemory: + prover_failed = true; + if (cur_outcome != Outcome.Errors && cur_outcome != Outcome.Inconclusive) + cur_outcome = Outcome.OutOfMemory; + return; + case ProverInterface.Outcome.TimeOut: + prover_failed = true; + if (cur_outcome != Outcome.Errors && cur_outcome != Outcome.Inconclusive) + cur_outcome = Outcome.TimedOut; + return; + case ProverInterface.Outcome.Undetermined: + if (cur_outcome != Outcome.Errors) + cur_outcome = Outcome.Inconclusive; + return; + default: + Contract.Assert(false); + throw new cce.UnreachableException(); + } + } + + /// + /// As a side effect, updates "this.parent.CumulativeAssertionCount". + /// + public void BeginCheck(Checker checker, VerifierCallback callback, ModelViewInfo mvInfo, int no, int timeout) + { + Contract.Requires(checker != null); + Contract.Requires(callback != null); + + splitNo = no; + + impl.Blocks = blocks; + + this.checker = checker; + + Dictionary label2absy = new Dictionary(); + + ProverContext ctx = checker.TheoremProver.Context; + Boogie2VCExprTranslator bet = ctx.BoogieExprTranslator; + CodeExprConversionClosure cc = new CodeExprConversionClosure(label2absy, ctx); + bet.SetCodeExprConverter(cc.CodeExprToVerificationCondition); + + var exprGen = ctx.ExprGen; + VCExpr controlFlowVariableExpr = CommandLineOptions.Clo.UseLabels ? null : exprGen.Integer(BigNum.ZERO); + + VCExpr vc = parent.GenerateVCAux(impl, controlFlowVariableExpr, label2absy, checker.TheoremProver.Context); + Contract.Assert(vc != null); + + if (!CommandLineOptions.Clo.UseLabels) + { + VCExpr controlFlowFunctionAppl = exprGen.ControlFlowFunctionApplication(exprGen.Integer(BigNum.ZERO), exprGen.Integer(BigNum.ZERO)); + VCExpr eqExpr = exprGen.Eq(controlFlowFunctionAppl, exprGen.Integer(BigNum.FromInt(impl.Blocks[0].UniqueId))); + vc = exprGen.Implies(eqExpr, vc); + } + + if (CommandLineOptions.Clo.vcVariety == CommandLineOptions.VCVariety.Local) + { + reporter = new ErrorReporterLocal(gotoCmdOrigins, label2absy, impl.Blocks, parent.incarnationOriginMap, callback, mvInfo, cce.NonNull(this.Checker.TheoremProver.Context), parent.program); + } + else + { + reporter = new ErrorReporter(gotoCmdOrigins, label2absy, impl.Blocks, parent.incarnationOriginMap, callback, mvInfo, this.Checker.TheoremProver.Context, parent.program); + } + + if (CommandLineOptions.Clo.TraceVerify && no >= 0) + { + Console.WriteLine("-- after split #{0}", no); + Print(); + } + + string desc = cce.NonNull(impl.Name); + if (no >= 0) + desc += "_split" + no; + checker.BeginCheck(desc, vc, reporter); + } + + private void SoundnessCheck(HashSet/*!*/>/*!*/ cache, Block/*!*/ orig, List/*!*/ copies) { + Contract.Requires(cce.NonNull(cache)); + Contract.Requires(orig != null); + Contract.Requires(copies != null); + { + var t = new List { orig }; + foreach (Block b in copies) { + Contract.Assert(b != null); + t.Add(b); + } + if (cache.Contains(t)) { + return; + } + cache.Add(t); + } + + for (int i = 0; i < orig.Cmds.Count; ++i) { + Cmd cmd = orig.Cmds[i]; + if (cmd is AssertCmd) { + int found = 0; + foreach (Block c in copies) { + Contract.Assert(c != null); + if (c.Cmds[i] == cmd) { + found++; + } + } + if (found == 0) { + throw new System.Exception(string.Format("missing assertion: {0}({1})", cmd.tok.filename, cmd.tok.line)); + } + } + } + + foreach (Block exit in Exits(orig)) { + Contract.Assert(exit != null); + List newcopies = new List(); + foreach (Block c in copies) { + foreach (Block cexit in Exits(c)) { + Contract.Assert(cexit != null); + if (cexit.Label == exit.Label) { + newcopies.Add(cexit); + } + } + } + if (newcopies.Count == 0) { + throw new System.Exception("missing exit " + exit.Label); + } + SoundnessCheck(cache, exit, newcopies); + } + } + } + #endregion + + + public class CodeExprConversionClosure + { + Dictionary label2absy; + ProverContext ctx; + public CodeExprConversionClosure(Dictionary label2absy, ProverContext ctx) + { + this.label2absy = label2absy; + this.ctx = ctx; + } + + public VCExpr CodeExprToVerificationCondition(CodeExpr codeExpr, Hashtable blockVariables, List bindings, bool isPositiveContext) + { + VCGen vcgen = new VCGen(new Program(), null, false, new List()); + vcgen.variable2SequenceNumber = new Dictionary(); + vcgen.incarnationOriginMap = new Dictionary(); + vcgen.CurrentLocalVariables = codeExpr.LocVars; + + ResetPredecessors(codeExpr.Blocks); + vcgen.AddBlocksBetween(codeExpr.Blocks); + Dictionary gotoCmdOrigins = vcgen.ConvertBlocks2PassiveCmd(codeExpr.Blocks, new List(), new ModelViewInfo(codeExpr)); + int ac; // computed, but then ignored for this CodeExpr + VCExpr startCorrect = VCGen.LetVCIterative(codeExpr.Blocks, null, label2absy, ctx, out ac, isPositiveContext); + VCExpr vce = ctx.ExprGen.Let(bindings, startCorrect); + if (vcgen.CurrentLocalVariables.Count != 0) + { + Boogie2VCExprTranslator translator = ctx.BoogieExprTranslator; + List boundVars = new List(); + foreach (Variable v in vcgen.CurrentLocalVariables) + { + Contract.Assert(v != null); + VCExprVar ev = translator.LookupVariable(v); + Contract.Assert(ev != null); + boundVars.Add(ev); + if (v.TypedIdent.Type.Equals(Bpl.Type.Bool)) + { + // add an antecedent (tickleBool ev) to help the prover find a possible trigger + vce = ctx.ExprGen.Implies(ctx.ExprGen.Function(VCExpressionGenerator.TickleBoolOp, ev), vce); + } + } + vce = ctx.ExprGen.Forall(boundVars, new List(), vce); + } + if (isPositiveContext) + { + vce = ctx.ExprGen.Not(vce); + } + return vce; + } + } + + public VCExpr GenerateVC(Implementation/*!*/ impl, VCExpr controlFlowVariableExpr, out Dictionary/*!*/ label2absy, ProverContext proverContext) + { + Contract.Requires(impl != null); + Contract.Requires(proverContext != null); + Contract.Ensures(Contract.ValueAtReturn(out label2absy) != null); + Contract.Ensures(Contract.Result() != null); + + label2absy = new Dictionary(); + return GenerateVCAux(impl, controlFlowVariableExpr, label2absy, proverContext); + } + + public VCExpr GenerateVCAux(Implementation/*!*/ impl, VCExpr controlFlowVariableExpr, Dictionary/*!*/ label2absy, ProverContext proverContext) { + Contract.Requires(impl != null); + Contract.Requires(proverContext != null); + Contract.Ensures(Contract.Result() != null); + + TypecheckingContext tc = new TypecheckingContext(null); + impl.Typecheck(tc); + + VCExpr vc; + int assertionCount; + switch (CommandLineOptions.Clo.vcVariety) { + case CommandLineOptions.VCVariety.Structured: + vc = VCViaStructuredProgram(impl, label2absy, proverContext, out assertionCount); + break; + case CommandLineOptions.VCVariety.Block: + vc = FlatBlockVC(impl, label2absy, false, false, false, proverContext, out assertionCount); + break; + case CommandLineOptions.VCVariety.BlockReach: + vc = FlatBlockVC(impl, label2absy, false, true, false, proverContext, out assertionCount); + break; + case CommandLineOptions.VCVariety.Local: + vc = FlatBlockVC(impl, label2absy, true, false, false, proverContext, out assertionCount); + break; + case CommandLineOptions.VCVariety.BlockNested: + vc = NestedBlockVC(impl, label2absy, false, proverContext, out assertionCount); + break; + case CommandLineOptions.VCVariety.BlockNestedReach: + vc = NestedBlockVC(impl, label2absy, true, proverContext, out assertionCount); + break; + case CommandLineOptions.VCVariety.Dag: + if (cce.NonNull(CommandLineOptions.Clo.TheProverFactory).SupportsDags || CommandLineOptions.Clo.FixedPointEngine != null) { + vc = DagVC(cce.NonNull(impl.Blocks[0]), controlFlowVariableExpr, label2absy, new Hashtable/**/(), proverContext, out assertionCount); + } else { + vc = LetVC(cce.NonNull(impl.Blocks[0]), controlFlowVariableExpr, label2absy, proverContext, out assertionCount); + } + break; + case CommandLineOptions.VCVariety.DagIterative: + vc = LetVCIterative(impl.Blocks, controlFlowVariableExpr, label2absy, proverContext, out assertionCount); + break; + case CommandLineOptions.VCVariety.Doomed: + vc = FlatBlockVC(impl, label2absy, false, false, true, proverContext, out assertionCount); + break; + default: + Contract.Assert(false); + throw new cce.UnreachableException(); // unexpected enumeration value + } + CumulativeAssertionCount += assertionCount; + return vc; + } + + void CheckIntAttributeOnImpl(Implementation impl, string name, ref int val) { + Contract.Requires(impl != null); + Contract.Requires(name != null); + if (!(cce.NonNull(impl.Proc).CheckIntAttribute(name, ref val) || !impl.CheckIntAttribute(name, ref val))) { + Console.WriteLine("ignoring ill-formed {:{0} ...} attribute on {1}, parameter should be an int", name, impl.Name); + } + } + + public override Outcome VerifyImplementation(Implementation/*!*/ impl, VerifierCallback/*!*/ callback) { + Contract.EnsuresOnThrow(true); + + if (impl.SkipVerification) { + return Outcome.Inconclusive; // not sure about this one + } + + callback.OnProgress("VCgen", 0, 0, 0.0); + + Stopwatch watch = new Stopwatch(); + if (_print_time) + { + Console.WriteLine("Checking function {0}", impl.Name); + watch.Reset(); + watch.Start(); + } + + ConvertCFG2DAG(impl); + + SmokeTester smoke_tester = null; + if (CommandLineOptions.Clo.SoundnessSmokeTest) { + smoke_tester = new SmokeTester(this, impl, callback); + smoke_tester.Copy(); + } + + ModelViewInfo mvInfo; + var gotoCmdOrigins = PassifyImpl(impl, out mvInfo); + + // If "expand" attribute is supplied, expand any assertion of conjunctions into multiple assertions, one per conjunct + foreach (var b in impl.Blocks) + { + List newCmds = new List(); + bool changed = false; + foreach (var c in b.Cmds) + { + var a = c as AssertCmd; + var ar = c as AssertRequiresCmd; + var ae = c as AssertEnsuresCmd; + var ai = c as LoopInitAssertCmd; + var am = c as LoopInvMaintainedAssertCmd; + // TODO: + //use Duplicator and Substituter rather than new + //nested IToken? + //document expand attribute (search for {:ignore}, for example) + //fix up new CallCmd, new Requires, new Ensures in OwickiGries.cs + Func withType = (Expr from, Expr to) => + { + NAryExpr nFrom = from as NAryExpr; + NAryExpr nTo = to as NAryExpr; + to.Type = from.Type; + if (nFrom != null && nTo != null) nTo.TypeParameters = nFrom.TypeParameters; + return to; + }; + + Action> traverse = null; + traverse = (depth, e, act) => + { + ForallExpr forall = e as ForallExpr; + NAryExpr nary = e as NAryExpr; + if (forall != null) + { + traverse(depth, forall.Body, e1 => act(withType(forall, + new ForallExpr(e1.tok, forall.TypeParameters, forall.Dummies, forall.Attributes, forall.Triggers, e1)))); + return; + } + if (nary != null) + { + var args = nary.Args; + IAppliable fun = nary.Fun; + BinaryOperator bop = fun as BinaryOperator; + FunctionCall call = fun as FunctionCall; + if (bop != null) + { + switch (bop.Op) + { + case BinaryOperator.Opcode.And: + traverse(depth, args[0], act); + traverse(depth, args[1], act); + return; + case BinaryOperator.Opcode.Imp: + traverse(depth, args[1], e1 => act(withType(nary, + new NAryExpr(e1.tok, fun, new List() { args[0], e1 })))); + return; + } + } + if (depth > 0 && call != null && call.Func != null) + { + Function cf = call.Func; + Expr body = cf.Body; + List ins = cf.InParams; + if (body == null && cf.DefinitionAxiom != null) + { + ForallExpr all = cf.DefinitionAxiom.Expr as ForallExpr; + if (all != null) + { + NAryExpr def = all.Body as NAryExpr; + if (def != null && def.Fun is BinaryOperator && ((BinaryOperator) (def.Fun)).Op == BinaryOperator.Opcode.Iff) + { + body = def.Args[1]; + ins = all.Dummies; + } + } + } + if (body != null) + { + Func new_f = e1 => + { + Function f = new Function(cf.tok, "expand<" + cf.Name + ">", cf.TypeParameters, ins, cf.OutParams[0], cf.Comment); + f.Body = e1; + Token tok = new Token(e1.tok.line, e1.tok.col); + tok.filename = e.tok.filename + "(" + e.tok.line + "," + e.tok.col + ") --> " + e1.tok.filename; + return withType(nary, new NAryExpr(tok, new FunctionCall(f), args)); + }; + traverse(depth - 1, body, e1 => act(new_f(e1))); + return; + } + } + } + act(e); + }; + + if (a != null) + { + var attr = a.Attributes; + if (ar != null && ar.Requires.Attributes != null) attr = ar.Requires.Attributes; + if (ar != null && ar.Call.Attributes != null) attr = ar.Call.Attributes; + if (ae != null && ae.Ensures.Attributes != null) attr = ae.Ensures.Attributes; + if (QKeyValue.FindExprAttribute(attr, "expand") != null || QKeyValue.FindBoolAttribute(attr, "expand")) + { + int depth = QKeyValue.FindIntAttribute(attr, "expand", 100); + Func fe = e => Expr.Or(a.Expr, e); + //traverse(depth, a.Expr, e => System.Console.WriteLine(e.GetType() + " :: " + e + " @ " + e.tok.line + ", " + e.tok.col)); + traverse(depth, a.Expr, e => + { + AssertCmd new_c = + (ar != null) ? new AssertRequiresCmd(ar.Call, new Requires(e.tok, ar.Requires.Free, fe(e), ar.Requires.Comment)) : + (ae != null) ? new AssertEnsuresCmd(new Ensures(e.tok, ae.Ensures.Free, fe(e), ae.Ensures.Comment)) : + (ai != null) ? new LoopInitAssertCmd(e.tok, fe(e)) : + (am != null) ? new LoopInvMaintainedAssertCmd(e.tok, fe(e)) : + new AssertCmd(e.tok, fe(e)); + new_c.Attributes = new QKeyValue(e.tok, "subsumption", new List() { new LiteralExpr(e.tok, BigNum.FromInt(0)) }, a.Attributes); + newCmds.Add(new_c); + }); + } + newCmds.Add(c); + changed = true; + } + else + { + newCmds.Add(c); + } + } + if (changed) b.Cmds = newCmds; + } + + double max_vc_cost = CommandLineOptions.Clo.VcsMaxCost; + int tmp_max_vc_cost = -1, max_splits = CommandLineOptions.Clo.VcsMaxSplits, + max_kg_splits = CommandLineOptions.Clo.VcsMaxKeepGoingSplits; + CheckIntAttributeOnImpl(impl, "vcs_max_cost", ref tmp_max_vc_cost); + CheckIntAttributeOnImpl(impl, "vcs_max_splits", ref max_splits); + CheckIntAttributeOnImpl(impl, "vcs_max_keep_going_splits", ref max_kg_splits); + if (tmp_max_vc_cost >= 0) { + max_vc_cost = tmp_max_vc_cost; + } + + Outcome outcome = Outcome.Correct; + + // Report all recycled failing assertions for this implementation. + if (impl.RecycledFailingAssertions != null && impl.RecycledFailingAssertions.Any()) + { + outcome = Outcome.Errors; + foreach (var a in impl.RecycledFailingAssertions) + { + var checksum = a.Checksum; + var oldCex = impl.ErrorChecksumToCachedError[checksum] as Counterexample; + if (oldCex != null) + { + callback.OnCounterexample(oldCex, null); + } + } + } + + Cores = CommandLineOptions.Clo.VcsCores; + Stack work = new Stack(); + List currently_running = new List(); + ResetPredecessors(impl.Blocks); + List manual_splits = Split.FindManualSplits(impl, gotoCmdOrigins, this); + if (manual_splits != null) { + foreach (var split in manual_splits) { + work.Push(split); + } + } else { + work.Push(new Split(impl.Blocks, gotoCmdOrigins, this, impl)); + } + + bool keep_going = max_kg_splits > 1; + int total = 0; + int no = max_splits == 1 && !keep_going ? -1 : 0; + bool first_round = true; + bool do_splitting = keep_going || max_splits > 1; + double remaining_cost = 0.0, proven_cost = 0.0; + + if (do_splitting) { + remaining_cost = work.Peek().Cost; + } + + while (work.Any() || currently_running.Any()) + { + bool prover_failed = false; + Split s = null; + var isWaiting = !work.Any(); + + if (!isWaiting) + { + s = work.Peek(); + + if (first_round && max_splits > 1) + { + prover_failed = true; + remaining_cost -= s.Cost; + } + else + { + var timeout = (keep_going && s.LastChance) ? CommandLineOptions.Clo.VcsFinalAssertTimeout : + keep_going ? CommandLineOptions.Clo.VcsKeepGoingTimeout : + impl.TimeLimit; + + var checker = s.parent.FindCheckerFor(timeout, false); + try + { + if (checker == null) + { + isWaiting = true; + goto waiting; + } + else + { + s = work.Pop(); + } + + if (CommandLineOptions.Clo.Trace && no >= 0) + { + System.Console.WriteLine(" checking split {1}/{2}, {3:0.00}%, {0} ...", + s.Stats, no + 1, total, 100 * proven_cost / (proven_cost + remaining_cost)); + } + callback.OnProgress("VCprove", no < 0 ? 0 : no, total, proven_cost / (remaining_cost + proven_cost)); + + Contract.Assert(s.parent == this); + lock (checker) + { + s.BeginCheck(checker, callback, mvInfo, no, timeout); + } + + no++; + + currently_running.Add(s); + } + catch (Exception) + { + checker.GoBackToIdle(); + throw; + } + } + } + + waiting: + if (isWaiting) + { + // Wait for one split to terminate. + var tasks = currently_running.Select(splt => splt.ProverTask).ToArray(); + + if (tasks.Any()) + { + try + { + int index = Task.WaitAny(tasks); + s = currently_running[index]; + currently_running.RemoveAt(index); + + if (do_splitting) + { + remaining_cost -= s.Cost; + } + + lock (s.Checker) + { + s.ReadOutcome(ref outcome, out prover_failed); + } + + if (do_splitting) + { + if (prover_failed) + { + // even if the prover fails, we have learned something, i.e., it is + // annoying to watch Boogie say Timeout, 0.00% a couple of times + proven_cost += s.Cost / 100; + } + else + { + proven_cost += s.Cost; + } + } + callback.OnProgress("VCprove", no < 0 ? 0 : no, total, proven_cost / (remaining_cost + proven_cost)); + + if (prover_failed && !first_round && s.LastChance) + { + string msg = "some timeout"; + if (s.reporter != null && s.reporter.resourceExceededMessage != null) + { + msg = s.reporter.resourceExceededMessage; + } + callback.OnCounterexample(s.ToCounterexample(s.Checker.TheoremProver.Context), msg); + outcome = Outcome.Errors; + break; + } + } + finally + { + s.Checker.GoBackToIdle(); + } + + Contract.Assert(prover_failed || outcome == Outcome.Correct || outcome == Outcome.Errors || outcome == Outcome.Inconclusive); + } + } + + if (prover_failed) + { + int splits = first_round && max_splits > 1 ? max_splits : max_kg_splits; + + if (splits > 1) + { + List tmp = Split.DoSplit(s, max_vc_cost, splits); + Contract.Assert(tmp != null); + max_vc_cost = 1.0; // for future + first_round = false; + //tmp.Sort(new Comparison(Split.Compare)); + foreach (Split a in tmp) + { + Contract.Assert(a != null); + work.Push(a); + total++; + remaining_cost += a.Cost; + } + if (outcome != Outcome.Errors) + { + outcome = Outcome.Correct; + } + } + else + { + Contract.Assert(outcome != Outcome.Correct); + if (outcome == Outcome.TimedOut) + { + string msg = "some timeout"; + if (s.reporter != null && s.reporter.resourceExceededMessage != null) + { + msg = s.reporter.resourceExceededMessage; + } + callback.OnTimeout(msg); + } + else if (outcome == Outcome.OutOfMemory) + { + string msg = "out of memory"; + if (s.reporter != null && s.reporter.resourceExceededMessage != null) + { + msg = s.reporter.resourceExceededMessage; + } + callback.OnOutOfMemory(msg); + } + + break; + } + } + } + + if (outcome == Outcome.Correct && smoke_tester != null) { + smoke_tester.Test(); + } + + callback.OnProgress("done", 0, 0, 1.0); + + if (_print_time) + { + watch.Stop(); + Console.WriteLine("Total time for this method: {0}", watch.Elapsed.ToString()); + } + + return outcome; + } + + public class ErrorReporter : ProverInterface.ErrorHandler { + Dictionary/*!*/ gotoCmdOrigins; + Dictionary/*!*/ label2absy; + List/*!*/ blocks; + protected Dictionary/*!*/ incarnationOriginMap; + protected VerifierCallback/*!*/ callback; + protected ModelViewInfo MvInfo; + internal string resourceExceededMessage; + static System.IO.TextWriter modelWriter; + [ContractInvariantMethod] + void ObjectInvariant() { + Contract.Invariant(gotoCmdOrigins != null); + Contract.Invariant(label2absy != null); + Contract.Invariant(cce.NonNullElements(blocks)); + Contract.Invariant(cce.NonNullDictionaryAndValues(incarnationOriginMap)); + Contract.Invariant(callback != null); + Contract.Invariant(context != null); + Contract.Invariant(program != null); + } + + + public static TextWriter ModelWriter { + get { + Contract.Ensures(Contract.Result() != null); + + if (ErrorReporter.modelWriter == null) + ErrorReporter.modelWriter = CommandLineOptions.Clo.PrintErrorModelFile == null ? Console.Out : new StreamWriter(CommandLineOptions.Clo.PrintErrorModelFile, false); + return ErrorReporter.modelWriter; + } + } + + protected ProverContext/*!*/ context; + Program/*!*/ program; + + public ErrorReporter(Dictionary/*!*/ gotoCmdOrigins, + Dictionary/*!*/ label2absy, + List/*!*/ blocks, + Dictionary/*!*/ incarnationOriginMap, + VerifierCallback/*!*/ callback, + ModelViewInfo mvInfo, + ProverContext/*!*/ context, + Program/*!*/ program) { + Contract.Requires(gotoCmdOrigins != null); + Contract.Requires(label2absy != null); + Contract.Requires(cce.NonNullElements(blocks)); + Contract.Requires(cce.NonNullDictionaryAndValues(incarnationOriginMap)); + Contract.Requires(callback != null); + Contract.Requires(context!=null); + Contract.Requires(program!=null); + this.gotoCmdOrigins = gotoCmdOrigins; + this.label2absy = label2absy; + this.blocks = blocks; + this.incarnationOriginMap = incarnationOriginMap; + this.callback = callback; + this.MvInfo = mvInfo; + + this.context = context; + this.program = program; + } + + public override void OnModel(IList/*!*/ labels, Model model, ProverInterface.Outcome proverOutcome) { + //Contract.Requires(cce.NonNullElements(labels)); + if (CommandLineOptions.Clo.PrintErrorModel >= 1 && model != null) { + if (VC.ConditionGeneration.errorModelList != null) + { + VC.ConditionGeneration.errorModelList.Add(model); + } + + model.Write(ErrorReporter.ModelWriter); + ErrorReporter.ModelWriter.Flush(); + } + + Hashtable traceNodes = new Hashtable(); + foreach (string s in labels) { + Contract.Assert(s != null); + Absy absy = Label2Absy(s); + Contract.Assert(absy != null); + if (traceNodes.ContainsKey(absy)) + System.Console.WriteLine("Warning: duplicate label: " + s + " read while tracing nodes"); + else + traceNodes.Add(absy, null); + } + + List trace = new List(); + Block entryBlock = cce.NonNull(this.blocks[0]); + Contract.Assert(traceNodes.Contains(entryBlock)); + trace.Add(entryBlock); + + Counterexample newCounterexample = TraceCounterexample(entryBlock, traceNodes, trace, model, MvInfo, incarnationOriginMap, context, new Dictionary()); + + if (newCounterexample == null) + return; + + #region Map passive program errors back to original program errors + ReturnCounterexample returnExample = newCounterexample as ReturnCounterexample; + if (returnExample != null) { + foreach (Block b in returnExample.Trace) { + Contract.Assert(b != null); + Contract.Assume(b.TransferCmd != null); + ReturnCmd cmd = gotoCmdOrigins.ContainsKey(b.TransferCmd) ? gotoCmdOrigins[b.TransferCmd] : null; + if (cmd != null) { + returnExample.FailingReturn = cmd; + break; + } + } + } + #endregion + callback.OnCounterexample(newCounterexample, null); + } + + public override Absy Label2Absy(string label) { + //Contract.Requires(label != null); + Contract.Ensures(Contract.Result() != null); + + int id = int.Parse(label); + return cce.NonNull((Absy)label2absy[id]); + } + + public override void OnResourceExceeded(string msg, IEnumerable> assertCmds = null) { + //Contract.Requires(msg != null); + resourceExceededMessage = msg; + if (assertCmds != null) + { + foreach (var cmd in assertCmds) + { + Counterexample cex = AssertCmdToCounterexample(cmd.Item1, cmd.Item2 , new List(), null, null, context); + callback.OnCounterexample(cex, msg); + } + } + } + + public override void OnProverWarning(string msg) { + //Contract.Requires(msg != null); + callback.OnWarning(msg); + } + } + + public class ErrorReporterLocal : ErrorReporter { + public ErrorReporterLocal(Dictionary/*!*/ gotoCmdOrigins, + Dictionary/*!*/ label2absy, + List/*!*/ blocks, + Dictionary/*!*/ incarnationOriginMap, + VerifierCallback/*!*/ callback, + ModelViewInfo mvInfo, + ProverContext/*!*/ context, + Program/*!*/ program) + : base(gotoCmdOrigins, label2absy, blocks, incarnationOriginMap, callback, mvInfo, context, program) // here for aesthetic purposes //TODO: Maybe nix? + { + Contract.Requires(gotoCmdOrigins != null); + Contract.Requires(label2absy != null); + Contract.Requires(cce.NonNullElements(blocks)); + Contract.Requires(cce.NonNullDictionaryAndValues(incarnationOriginMap)); + Contract.Requires(callback != null); + Contract.Requires(context != null); + Contract.Requires(program != null); + } + + public override void OnModel(IList/*!*/ labels, Model model, ProverInterface.Outcome proverOutcome) { + //Contract.Requires(cce.NonNullElements(labels)); + // We ignore the error model here for enhanced error message purposes. + // It is only printed to the command line. + if (CommandLineOptions.Clo.PrintErrorModel >= 1 && model != null) { + if (CommandLineOptions.Clo.PrintErrorModelFile != null) { + model.Write(ErrorReporter.ModelWriter); + ErrorReporter.ModelWriter.Flush(); + } + } + List traceNodes = new List(); + List assertNodes = new List(); + foreach (string s in labels) { + Contract.Assert(s != null); + Absy node = Label2Absy(s); + if (node is Block) { + Block b = (Block)node; + traceNodes.Add(b); + } else { + AssertCmd a = (AssertCmd)node; + assertNodes.Add(a); + } + } + Contract.Assert(assertNodes.Count > 0); + Contract.Assert(traceNodes.Count == assertNodes.Count); + + foreach (AssertCmd a in assertNodes) { + // find the corresponding Block (assertNodes.Count is likely to be 1, or small in any case, so just do a linear search here) + foreach (Block b in traceNodes) { + if (b.Cmds.Contains(a)) { + List trace = new List(); + trace.Add(b); + Counterexample newCounterexample = AssertCmdToCounterexample(a, cce.NonNull(b.TransferCmd), trace, model, MvInfo, context); + callback.OnCounterexample(newCounterexample, null); + goto NEXT_ASSERT; + } + } + Contract.Assert(false); + throw new cce.UnreachableException(); // there was no block that contains the assert + NEXT_ASSERT: { + } + } + } + } + + private void RecordCutEdge(Dictionary> edgesCut, Block from, Block to){ + if (edgesCut != null) + { + if (!edgesCut.ContainsKey(from)) + edgesCut.Add(from, new List()); + edgesCut[from].Add(to); + } + } + + 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 + + CurrentLocalVariables = impl.LocVars; + variable2SequenceNumber = new Dictionary(); + incarnationOriginMap = new Dictionary(); + + #region Debug Tracing + if (CommandLineOptions.Clo.TraceVerify) + { + Console.WriteLine("original implementation"); + EmitImpl(impl, false); + } + #endregion + + #region Debug Tracing + if (CommandLineOptions.Clo.TraceVerify) + { + Console.WriteLine("after desugaring sugared commands like procedure calls"); + EmitImpl(impl, true); + } + #endregion + + // Recompute the predecessors, but first insert a dummy start node that is sure not to be the target of any goto (because the cutting of back edges + // below assumes that the start node has no predecessor) + impl.Blocks.Insert(0, new Block(new Token(-17, -4), "0", new List(), new GotoCmd(Token.NoToken, new List { impl.Blocks[0].Label }, new List { impl.Blocks[0] }))); + ResetPredecessors(impl.Blocks); + + if(CommandLineOptions.Clo.KInductionDepth < 0) { + ConvertCFG2DAGStandard(impl, edgesCut, taskID); + } else { + ConvertCFG2DAGKInduction(impl, edgesCut, taskID); + } + + #region Debug Tracing + if (CommandLineOptions.Clo.TraceVerify) + { + Console.WriteLine("after conversion into a DAG"); + EmitImpl(impl, true); + } + #endregion + } + + private void ConvertCFG2DAGStandard(Implementation impl, Dictionary> edgesCut, int taskID) + { + #region Convert program CFG into a DAG + + #region Use the graph library to figure out where the (natural) loops are + + #region Create the graph by adding the source node and each edge + Graph g = Program.GraphFromImpl(impl); + #endregion + + //Graph g = program.ProcessLoops(impl); + + g.ComputeLoops(); // this is the call that does all of the processing + if (!g.Reducible) + { + throw new VCGenException("Irreducible flow graphs are unsupported."); + } + + #endregion + + #region Cut the backedges, push assert/assume statements from loop header into predecessors, change them all into assume statements at top of loop, introduce havoc statements + foreach (Block header in cce.NonNull(g.Headers)) + { + Contract.Assert(header != null); + IDictionary backEdgeNodes = new Dictionary(); + foreach (Block b in cce.NonNull(g.BackEdgeNodes(header))) + { + Contract.Assert(b != null); + backEdgeNodes.Add(b, null); + } + + #region Find the (possibly empty) prefix of assert commands in the header, replace each assert with an assume of the same condition + List prefixOfPredicateCmdsInit = new List(); + List prefixOfPredicateCmdsMaintained = new List(); + for (int i = 0, n = header.Cmds.Count; i < n; i++) + { + PredicateCmd a = header.Cmds[i] as PredicateCmd; + if (a != null) + { + if (a is AssertCmd) + { + AssertCmd c = (AssertCmd)a; + AssertCmd b = null; + + if (CommandLineOptions.Clo.ConcurrentHoudini) + { + Contract.Assert(taskID >= 0); + if (CommandLineOptions.Clo.Cho[taskID].DisableLoopInvEntryAssert) + b = new LoopInitAssertCmd(c.tok, Expr.True); + else + b = new LoopInitAssertCmd(c.tok, c.Expr); + } + else + { + b = new LoopInitAssertCmd(c.tok, c.Expr); + } + + b.Attributes = c.Attributes; + b.ErrorData = c.ErrorData; + prefixOfPredicateCmdsInit.Add(b); + + if (CommandLineOptions.Clo.ConcurrentHoudini) + { + Contract.Assert(taskID >= 0); + if (CommandLineOptions.Clo.Cho[taskID].DisableLoopInvMaintainedAssert) + b = new Bpl.LoopInvMaintainedAssertCmd(c.tok, Expr.True); + else + b = new Bpl.LoopInvMaintainedAssertCmd(c.tok, c.Expr); + } + else + { + b = new Bpl.LoopInvMaintainedAssertCmd(c.tok, c.Expr); + } + + b.Attributes = c.Attributes; + b.ErrorData = c.ErrorData; + prefixOfPredicateCmdsMaintained.Add(b); + header.Cmds[i] = new AssumeCmd(c.tok, c.Expr); + } + else + { + Contract.Assert(a is AssumeCmd); + if (Bpl.CommandLineOptions.Clo.AlwaysAssumeFreeLoopInvariants) + { + // Usually, "free" stuff, like free loop invariants (and the assume statements + // that stand for such loop invariants) are ignored on the checking side. This + // command-line option changes that behavior to always assume the conditions. + prefixOfPredicateCmdsInit.Add(a); + prefixOfPredicateCmdsMaintained.Add(a); + } + } + } + else if (header.Cmds[i] is CommentCmd) + { + // ignore + } + else + { + break; // stop when an assignment statement (or any other non-predicate cmd) is encountered + } + } + #endregion + + #region Copy the prefix of predicate commands into each predecessor. Do this *before* cutting the backedge!! + for (int predIndex = 0, n = header.Predecessors.Count; predIndex < n; predIndex++) + { + Block pred = cce.NonNull(header.Predecessors[predIndex]); + + // Create a block between header and pred for the predicate commands if pred has more than one successor + GotoCmd gotocmd = cce.NonNull((GotoCmd)pred.TransferCmd); + Contract.Assert(gotocmd.labelNames != null); // if "pred" is really a predecessor, it may be a GotoCmd with at least one label + if (gotocmd.labelNames.Count > 1) + { + Block newBlock = CreateBlockBetween(predIndex, header); + impl.Blocks.Add(newBlock); + + // if pred is a back edge node, then now newBlock is the back edge node + if (backEdgeNodes.ContainsKey(pred)) + { + backEdgeNodes.Remove(pred); + backEdgeNodes.Add(newBlock, null); + } + + pred = newBlock; + } + // Add the predicate commands + if (backEdgeNodes.ContainsKey(pred)) + { + pred.Cmds.AddRange(prefixOfPredicateCmdsMaintained); + } + else + { + pred.Cmds.AddRange(prefixOfPredicateCmdsInit); + } + } + #endregion + + #region Cut the back edge + foreach (Block backEdgeNode in cce.NonNull(backEdgeNodes.Keys)) + { + Contract.Assert(backEdgeNode != null); + Debug.Assert(backEdgeNode.TransferCmd is GotoCmd, "An node was identified as the source for a backedge, but it does not have a goto command."); + GotoCmd gtc = backEdgeNode.TransferCmd as GotoCmd; + if (gtc != null && gtc.labelTargets != null && gtc.labelTargets.Count > 1) + { + // then remove the backedge by removing the target block from the list of gotos + List remainingTargets = new List(); + List remainingLabels = new List(); + Contract.Assume(gtc.labelNames != null); + for (int i = 0, n = gtc.labelTargets.Count; i < n; i++) + { + if (gtc.labelTargets[i] != header) + { + remainingTargets.Add(gtc.labelTargets[i]); + remainingLabels.Add(gtc.labelNames[i]); + } + else + RecordCutEdge(edgesCut, backEdgeNode, header); + } + gtc.labelTargets = remainingTargets; + gtc.labelNames = remainingLabels; + } + else + { + // This backedge is the only out-going edge from this node. + // Add an "assume false" statement to the end of the statements + // inside of the block and change the goto command to a return command. + AssumeCmd ac = new AssumeCmd(Token.NoToken, Expr.False); + backEdgeNode.Cmds.Add(ac); + backEdgeNode.TransferCmd = new ReturnCmd(Token.NoToken); + if (gtc != null && gtc.labelTargets != null && gtc.labelTargets.Count == 1) + RecordCutEdge(edgesCut, backEdgeNode, gtc.labelTargets[0]); + } + #region Remove the backedge node from the list of predecessor nodes in the header + List newPreds = new List(); + foreach (Block p in header.Predecessors) + { + if (p != backEdgeNode) + newPreds.Add(p); + } + header.Predecessors = newPreds; + #endregion + } + #endregion + + #region Collect all variables that are assigned to in all of the natural loops for which this is the header + List varsToHavoc = VarsAssignedInLoop(g, header); + List havocExprs = new List(); + foreach (Variable v in varsToHavoc) + { + Contract.Assert(v != null); + IdentifierExpr ie = new IdentifierExpr(Token.NoToken, v); + if (!havocExprs.Contains(ie)) + havocExprs.Add(ie); + } + // pass the token of the enclosing loop header to the HavocCmd so we can reconstruct + // the source location for this later on + HavocCmd hc = new HavocCmd(header.tok, havocExprs); + List newCmds = new List(); + newCmds.Add(hc); + foreach (Cmd c in header.Cmds) + { + newCmds.Add(c); + } + header.Cmds = newCmds; + #endregion + } + #endregion + #endregion Convert program CFG into a DAG + } + + public static List VarsAssignedInLoop(Graph g, Block header) + { + List varsToHavoc = new List(); + foreach (Block backEdgeNode in cce.NonNull(g.BackEdgeNodes(header))) + { + Contract.Assert(backEdgeNode != null); + foreach (Block b in g.NaturalLoops(header, backEdgeNode)) + { + Contract.Assert(b != null); + foreach (Cmd c in b.Cmds) + { + Contract.Assert(c != null); + c.AddAssignedVariables(varsToHavoc); + } + } + } + return varsToHavoc; + } + + public static IEnumerable VarsReferencedInLoop(Graph g, Block header) + { + HashSet referencedVars = new HashSet(); + foreach (Block backEdgeNode in cce.NonNull(g.BackEdgeNodes(header))) + { + Contract.Assert(backEdgeNode != null); + foreach (Block b in g.NaturalLoops(header, backEdgeNode)) + { + Contract.Assert(b != null); + foreach (Cmd c in b.Cmds) + { + Contract.Assert(c != null); + var Collector = new VariableCollector(); + Collector.Visit(c); + foreach(var v in Collector.usedVars) { + referencedVars.Add(v); + } + } + } + } + return referencedVars; + } + + private void ConvertCFG2DAGKInduction(Implementation impl, Dictionary> edgesCut, int taskID) { + + // K-induction has not been adapted to be aware of these parameters which standard CFG to DAG transformation uses + Contract.Requires(edgesCut == null); + Contract.Requires(taskID == -1); + + int inductionK = CommandLineOptions.Clo.KInductionDepth; + Contract.Assume(inductionK >= 0); + + bool contRuleApplication = true; + while (contRuleApplication) { + contRuleApplication = false; + + #region Use the graph library to figure out where the (natural) loops are + + #region Create the graph by adding the source node and each edge + Graph g = Program.GraphFromImpl(impl); + #endregion + + g.ComputeLoops(); // this is the call that does all of the processing + if (!g.Reducible) { + throw new VCGenException("Irreducible flow graphs are unsupported."); + } + + #endregion + + foreach (Block header in cce.NonNull(g.Headers)) { + Contract.Assert(header != null); + + #region Debug Tracing + if (CommandLineOptions.Clo.TraceVerify) + { + Console.WriteLine("Applying k-induction rule with k=" + inductionK); + } + #endregion + + #region generate the step case + Block newHeader = DuplicateLoop(impl, g, header, null, + false, false, "_step_assertion"); + for (int i = 0; i < inductionK; ++i) + { + newHeader = DuplicateLoop(impl, g, header, newHeader, + true, true, + "_step_" + (inductionK - i)); + } + #endregion + + #region havoc variables that can be assigned in the loop + + List varsToHavoc = VarsAssignedInLoop(g, header); + List havocExprs = new List(); + foreach (Variable v in varsToHavoc) + { + Contract.Assert(v != null); + IdentifierExpr ie = new IdentifierExpr(Token.NoToken, v); + if (!havocExprs.Contains(ie)) + havocExprs.Add(ie); + } + // pass the token of the enclosing loop header to the HavocCmd so we can reconstruct + // the source location for this later on + HavocCmd hc = new HavocCmd(newHeader.tok, havocExprs); + List havocCmds = new List(); + havocCmds.Add(hc); + + Block havocBlock = new Block(newHeader.tok, newHeader.Label + "_havoc", havocCmds, + new GotoCmd (newHeader.tok, new List { newHeader })); + + impl.Blocks.Add(havocBlock); + newHeader.Predecessors.Add(havocBlock); + newHeader = havocBlock; + + #endregion + + #region generate the base case loop copies + for (int i = 0; i < inductionK; ++i) + { + newHeader = DuplicateLoop(impl, g, header, newHeader, + false, false, + "_base_" + (inductionK - i)); + } + #endregion + + #region redirect into the new loop copies and remove the original loop (but don't redirect back-edges) + + IDictionary backEdgeNodes = new Dictionary(); + foreach (Block b in cce.NonNull(g.BackEdgeNodes(header))) { Contract.Assert(b != null); backEdgeNodes.Add(b, null); } + + for (int predIndex = 0, n = header.Predecessors.Count(); predIndex < n; predIndex++) + { + Block pred = cce.NonNull(header.Predecessors[predIndex]); + if (!backEdgeNodes.ContainsKey(pred)) + { + GotoCmd gc = pred.TransferCmd as GotoCmd; + Contract.Assert(gc != null); + for (int i = 0; i < gc.labelTargets.Count(); ++i) + { + if (gc.labelTargets[i] == header) + { + gc.labelTargets[i] = newHeader; + gc.labelNames[i] = newHeader.Label; + newHeader.Predecessors.Add(pred); + } + } + } + } + impl.PruneUnreachableBlocks(); + + #endregion + + contRuleApplication = true; + break; + } + + } + + ResetPredecessors(impl.Blocks); + impl.FreshenCaptureStates(); + + } + + private Block DuplicateLoop(Implementation impl, Graph g, + Block header, Block nextHeader, bool cutExits, + bool toAssumptions, string suffix) + { + IDictionary ori2CopiedBlocks = new Dictionary(); + Duplicator duplicator = new Duplicator(); + + #region create copies of all blocks in the loop + foreach (Block backEdgeNode in cce.NonNull(g.BackEdgeNodes(header))) + { + Contract.Assert(backEdgeNode != null); + foreach (Block b in g.NaturalLoops(header, backEdgeNode)) + { + Contract.Assert(b != null); + if (!ori2CopiedBlocks.ContainsKey(b)) + { + Block copy = (Block)duplicator.Visit(b); + copy.Cmds = new List(copy.Cmds); // Philipp Ruemmer commented that this was necessary due to a bug in the Duplicator. That was a long time; worth checking whether this has been fixed + copy.Predecessors = new List(); + copy.Label = copy.Label + suffix; + + #region turn asserts into assumptions + if (toAssumptions) + { + for (int i = 0; i < copy.Cmds.Count(); ++i) + { + AssertCmd ac = copy.Cmds[i] as AssertCmd; + if (ac != null) + { + copy.Cmds[i] = new AssumeCmd(ac.tok, ac.Expr); + } + } + } + #endregion + + impl.Blocks.Add(copy); + ori2CopiedBlocks.Add(b, copy); + } + } + } + #endregion + + #region adjust the transfer commands of the newly created blocks + foreach (KeyValuePair pair in ori2CopiedBlocks) + { + Block copy = pair.Value; + GotoCmd gc = copy.TransferCmd as GotoCmd; + if (gc != null) + { + List newTargets = new List(); + List newLabels = new List(); + + for (int i = 0; i < gc.labelTargets.Count(); ++i) + { + Block newTarget; + if (gc.labelTargets[i] == header) + { + if (nextHeader != null) + { + newTargets.Add(nextHeader); + newLabels.Add(nextHeader.Label); + nextHeader.Predecessors.Add(copy); + } + } + else if (ori2CopiedBlocks.TryGetValue(gc.labelTargets[i], out newTarget)) + { + newTargets.Add(newTarget); + newLabels.Add(newTarget.Label); + newTarget.Predecessors.Add(copy); + } + else if (!cutExits) + { + newTargets.Add(gc.labelTargets[i]); + newLabels.Add(gc.labelNames[i]); + gc.labelTargets[i].Predecessors.Add(copy); + } + } + + if (newTargets.Count() == 0) + { + // if no targets are left, we assume false and return + copy.Cmds.Add(new AssumeCmd(Token.NoToken, Expr.False)); + copy.TransferCmd = new ReturnCmd(Token.NoToken); + } + else + { + copy.TransferCmd = new GotoCmd(gc.tok, newLabels, newTargets); + } + } + else if (cutExits && (copy.TransferCmd is ReturnCmd)) + { + // because return is a kind of exit from the loop, we + // assume false to cut this path + copy.Cmds.Add(new AssumeCmd(Token.NoToken, Expr.False)); + } + } + #endregion + + return ori2CopiedBlocks[header]; + } + + public void DesugarCalls(Implementation impl) { + foreach (Block block in impl.Blocks) { + List newCmds = new List(); + foreach (Cmd cmd in block.Cmds) { + SugaredCmd sugaredCmd = cmd as SugaredCmd; + if (sugaredCmd != null) { + StateCmd stateCmd = sugaredCmd.Desugaring as StateCmd; + foreach (Variable v in stateCmd.Locals) { + impl.LocVars.Add(v); + } + newCmds.AddRange(stateCmd.Cmds); + } + else { + newCmds.Add(cmd); + } + } + block.Cmds = newCmds; + } + } + + public Dictionary PassifyImpl(Implementation impl, out ModelViewInfo mvInfo) + { + Contract.Requires(impl != null); + Contract.Requires(program != null); + Contract.Ensures(Contract.Result>() != null); + + Dictionary gotoCmdOrigins = new Dictionary(); + Block exitBlock = GenerateUnifiedExit(impl, gotoCmdOrigins); + + #region Debug Tracing + if (CommandLineOptions.Clo.TraceVerify) + { + Console.WriteLine("after creating a unified exit block"); + EmitImpl(impl, true); + } + #endregion + + #region Insert pre- and post-conditions and where clauses as assume and assert statements + { + List cc = new List(); + // where clauses of global variables + lock (program.TopLevelDeclarations) + { + foreach (var gvar in program.GlobalVariables) + { + if (gvar != null && gvar.TypedIdent.WhereExpr != null) + { + Cmd c = new AssumeCmd(gvar.tok, gvar.TypedIdent.WhereExpr); + cc.Add(c); + } + } + } + // where clauses of in- and out-parameters + cc.AddRange(GetParamWhereClauses(impl)); + // where clauses of local variables + foreach (Variable lvar in impl.LocVars) {Contract.Assert(lvar != null); + if (lvar.TypedIdent.WhereExpr != null) { + Cmd c = new AssumeCmd(lvar.tok, lvar.TypedIdent.WhereExpr); + cc.Add(c); + } else if (QKeyValue.FindBoolAttribute(lvar.Attributes, "assumption")) { + cc.Add(new AssumeCmd(lvar.tok, new IdentifierExpr(lvar.tok, lvar), new QKeyValue(lvar.tok, "assumption_variable_initialization", new List(), null))); + } + } + // add cc and the preconditions to new blocks preceding impl.Blocks[0] + InjectPreconditions(impl, cc); + + // append postconditions, starting in exitBlock and continuing into other blocks, if needed + InjectPostConditions(impl, exitBlock, gotoCmdOrigins); + } + #endregion + + #region Support for stratified inlining + addExitAssert(impl.Name, exitBlock); + #endregion + + + #region Debug Tracing + if (CommandLineOptions.Clo.TraceVerify) + { + Console.WriteLine("after inserting pre- and post-conditions"); + EmitImpl(impl, true); + } + #endregion + + AddBlocksBetween(impl.Blocks); + + #region Debug Tracing + if (CommandLineOptions.Clo.TraceVerify) + { + Console.WriteLine("after adding empty blocks as needed to catch join assumptions"); + EmitImpl(impl, true); + } + #endregion + + if (CommandLineOptions.Clo.LiveVariableAnalysis > 0) { + Microsoft.Boogie.LiveVariableAnalysis.ComputeLiveVariables(impl); + } + + mvInfo = new ModelViewInfo(program, impl); + Convert2PassiveCmd(impl, mvInfo); + + #region Peep-hole optimizations + if (CommandLineOptions.Clo.RemoveEmptyBlocks){ + #region Get rid of empty blocks + { + RemoveEmptyBlocksIterative(impl.Blocks); + impl.PruneUnreachableBlocks(); + } + #endregion Get rid of empty blocks + + #region Debug Tracing + if (CommandLineOptions.Clo.TraceVerify) + { + Console.WriteLine("after peep-hole optimizations"); + EmitImpl(impl, true); + } + #endregion + } + #endregion Peep-hole optimizations + + HandleSelectiveChecking(impl); + + +// #region Constant Folding +// #endregion +// #region Debug Tracing +// if (CommandLineOptions.Clo.TraceVerify) +// { +// Console.WriteLine("after constant folding"); +// EmitImpl(impl, true); +// } +// #endregion + + return gotoCmdOrigins; + } + + private static void HandleSelectiveChecking(Implementation impl) + { + if (QKeyValue.FindBoolAttribute(impl.Attributes, "selective_checking") || + QKeyValue.FindBoolAttribute(impl.Proc.Attributes, "selective_checking")) { + + var startPoints = new List(); + foreach (var b in impl.Blocks) { + foreach (Cmd c in b.Cmds) { + var p = c as PredicateCmd; + if (p != null && QKeyValue.FindBoolAttribute(p.Attributes, "start_checking_here")) { + startPoints.Add(b); + break; + } + } + } + + // Compute the set of blocks reachable from blocks containing "start_checking_here" + var blocksToCheck = new HashSet(); + foreach (var b in startPoints) { + var todo = new Stack(); + var wasThere = blocksToCheck.Contains(b); + todo.Push(b); + while (todo.Count > 0) { + var x = todo.Pop(); + if (blocksToCheck.Contains(x)) continue; + blocksToCheck.Add(x); + var ex = x.TransferCmd as GotoCmd; + if (ex != null) + foreach (Block e in ex.labelTargets) + todo.Push(e); + } + if (!wasThere) blocksToCheck.Remove(b); + } + + // Convert asserts to assumes in "unreachable" blocks, as well as in portions of blocks before we reach "start_checking_here" + foreach (var b in impl.Blocks) { + if (blocksToCheck.Contains(b)) continue; // All reachable blocks must be checked in their entirety, so don't change anything + var newCmds = new List(); + var copyMode = false; + foreach (Cmd c in b.Cmds) { + var p = c as PredicateCmd; + if (p != null && QKeyValue.FindBoolAttribute(p.Attributes, "start_checking_here")) + copyMode = true; + var asrt = c as AssertCmd; + if (copyMode || asrt == null) + newCmds.Add(c); + else + newCmds.Add(AssertTurnedIntoAssume(asrt)); + } + + b.Cmds = newCmds; + } + } + } + + // Used by stratified inlining + protected virtual void addExitAssert(string implName, Block exitBlock) + { + } + + public virtual Counterexample extractLoopTrace(Counterexample cex, string mainProcName, Program program, Dictionary> extractLoopMappingInfo) + { + // Construct the set of inlined procs in the original program + var inlinedProcs = new HashSet(); + foreach (var proc in program.Procedures) + { + if (!(proc is LoopProcedure)) + { + inlinedProcs.Add(proc.Name); + } + } + + return extractLoopTraceRec( + new CalleeCounterexampleInfo(cex, new List()), + mainProcName, inlinedProcs, extractLoopMappingInfo).counterexample; + } + + protected CalleeCounterexampleInfo extractLoopTraceRec( + CalleeCounterexampleInfo cexInfo, string currProc, + HashSet inlinedProcs, + Dictionary> extractLoopMappingInfo) + { + Contract.Requires(currProc != null); + if (cexInfo.counterexample == null) return cexInfo; + + var cex = cexInfo.counterexample; + // Go through all blocks in the trace, map them back to blocks in the original program (if there is one) + var ret = cex.Clone(); + ret.Trace = new List(); + ret.calleeCounterexamples = new Dictionary(); + + for (int numBlock = 0; numBlock < cex.Trace.Count; numBlock ++ ) + { + Block block = cex.Trace[numBlock]; + var origBlock = elGetBlock(currProc, block, extractLoopMappingInfo); + if (origBlock != null) ret.Trace.Add(origBlock); + var callCnt = 1; + for (int numInstr = 0; numInstr < block.Cmds.Count; numInstr ++) { + Cmd cmd = block.Cmds[numInstr]; + var loc = new TraceLocation(numBlock, numInstr); + if (!cex.calleeCounterexamples.ContainsKey(loc)) + { + if (getCallee(cex.getTraceCmd(loc), inlinedProcs) != null) callCnt++; + continue; + } + string callee = cex.getCalledProcName(cex.getTraceCmd(loc)); + Contract.Assert(callee != null); + var calleeTrace = cex.calleeCounterexamples[loc]; + Debug.Assert(calleeTrace != null); + + var origTrace = extractLoopTraceRec(calleeTrace, callee, inlinedProcs, extractLoopMappingInfo); + + if (elIsLoop(callee)) + { + // Absorb the trace into the current trace + + int currLen = ret.Trace.Count; + ret.Trace.AddRange(origTrace.counterexample.Trace); + + foreach (var kvp in origTrace.counterexample.calleeCounterexamples) + { + var newloc = new TraceLocation(kvp.Key.numBlock + currLen, kvp.Key.numInstr); + ret.calleeCounterexamples.Add(newloc, kvp.Value); + } + + } + else + { + var origLoc = new TraceLocation(ret.Trace.Count - 1, getCallCmdPosition(origBlock, callCnt, inlinedProcs, callee)); + ret.calleeCounterexamples.Add(origLoc, origTrace); + callCnt++; + } + } + } + return new CalleeCounterexampleInfo(ret, cexInfo.args); + } + + // return the position of the i^th CallCmd in the block (count only those Calls that call a procedure in inlinedProcs). + // Assert failure if there isn't any. + // Assert that the CallCmd found calls "callee" + private int getCallCmdPosition(Block block, int i, HashSet inlinedProcs, string callee) + { + Debug.Assert(i >= 1); + for (int pos = 0; pos < block.Cmds.Count; pos++) + { + Cmd cmd = block.Cmds[pos]; + string procCalled = getCallee(cmd, inlinedProcs); + + if (procCalled != null) + { + if (i == 1) + { + Debug.Assert(procCalled == callee); + return pos; + } + i--; + } + } + + Debug.Assert(false, "Didn't find the i^th call cmd"); + return -1; + } + + private string getCallee(Cmd cmd, HashSet inlinedProcs) + { + string procCalled = null; + if (cmd is CallCmd) + { + var cc = (CallCmd)cmd; + if (inlinedProcs.Contains(cc.Proc.Name)) + { + procCalled = cc.Proc.Name; + } + } + + if (cmd is AssumeCmd) + { + var expr = (cmd as AssumeCmd).Expr as NAryExpr; + if (expr != null) + { + if (inlinedProcs.Contains(expr.Fun.FunctionName)) + { + procCalled = expr.Fun.FunctionName; + } + } + } + return procCalled; + } + + protected virtual bool elIsLoop(string procname) + { + return false; + } + + private Block elGetBlock(string procname, Block block, Dictionary> extractLoopMappingInfo) + { + Contract.Requires(procname != null); + + if (!extractLoopMappingInfo.ContainsKey(procname)) + return block; + + if (!extractLoopMappingInfo[procname].ContainsKey(block.Label)) + return null; + + return extractLoopMappingInfo[procname][block.Label]; + } + + static Counterexample TraceCounterexample( + Block/*!*/ b, Hashtable/*!*/ traceNodes, List/*!*/ trace, Model errModel, ModelViewInfo mvInfo, + Dictionary/*!*/ incarnationOriginMap, + ProverContext/*!*/ context, + Dictionary/*!*/ calleeCounterexamples) + { + Contract.Requires(b != null); + Contract.Requires(traceNodes != null); + Contract.Requires(trace != null); + Contract.Requires(cce.NonNullDictionaryAndValues(incarnationOriginMap)); + Contract.Requires(context != null); + Contract.Requires(cce.NonNullDictionaryAndValues(calleeCounterexamples)); + // After translation, all potential errors come from asserts. + + while (true) + { + List cmds = b.Cmds; + Contract.Assert(cmds != null); + TransferCmd transferCmd = cce.NonNull(b.TransferCmd); + for (int i = 0; i < cmds.Count; i++) + { + Cmd cmd = cce.NonNull(cmds[i]); + + // Skip if 'cmd' not contained in the trace or not an assert + if (cmd is AssertCmd && traceNodes.Contains(cmd)) + { + Counterexample newCounterexample = AssertCmdToCounterexample((AssertCmd)cmd, transferCmd, trace, errModel, mvInfo, context); + Contract.Assert(newCounterexample != null); + newCounterexample.AddCalleeCounterexample(calleeCounterexamples); + return newCounterexample; + } + } + + GotoCmd gotoCmd = transferCmd as GotoCmd; + if (gotoCmd == null) return null; + Block foundBlock = null; + foreach (Block bb in cce.NonNull(gotoCmd.labelTargets)) + { + Contract.Assert(bb != null); + if (traceNodes.Contains(bb)) + { + foundBlock = bb; + break; + } + } + if (foundBlock == null) return null; + trace.Add(foundBlock); + b = foundBlock; + } + } + + public static Counterexample AssertCmdToCounterexample(AssertCmd cmd, TransferCmd transferCmd, List trace, Model errModel, ModelViewInfo mvInfo, ProverContext context) + { + Contract.Requires(cmd != null); + Contract.Requires(transferCmd != null); + Contract.Requires(trace != null); + Contract.Requires(context != null); + Contract.Ensures(Contract.Result() != null); + + List relatedInformation = new List(); + + // See if it is a special assert inserted in translation + if (cmd is AssertRequiresCmd) + { + AssertRequiresCmd assertCmd = (AssertRequiresCmd)cmd; + Contract.Assert(assertCmd != null); + CallCounterexample cc = new CallCounterexample(trace, assertCmd.Call, assertCmd.Requires, errModel, mvInfo, context, assertCmd.Checksum); + cc.relatedInformation = relatedInformation; + return cc; + } + else if (cmd is AssertEnsuresCmd) + { + AssertEnsuresCmd assertCmd = (AssertEnsuresCmd)cmd; + Contract.Assert(assertCmd != null); + ReturnCounterexample rc = new ReturnCounterexample(trace, transferCmd, assertCmd.Ensures, errModel, mvInfo, context, cmd.Checksum); + rc.relatedInformation = relatedInformation; + return rc; + } + else + { + AssertCounterexample ac = new AssertCounterexample(trace, (AssertCmd)cmd, errModel, mvInfo, context); + ac.relatedInformation = relatedInformation; + return ac; + } + } + + static VCExpr LetVC(Block startBlock, + VCExpr controlFlowVariableExpr, + Dictionary label2absy, + ProverContext proverCtxt, + out int assertionCount) { + Contract.Requires(startBlock != null); + Contract.Requires(proverCtxt != null); + + Contract.Ensures(Contract.Result() != null); + + Hashtable/**/ blockVariables = new Hashtable/**/(); + List bindings = new List(); + VCExpr startCorrect = LetVC(startBlock, controlFlowVariableExpr, label2absy, blockVariables, bindings, proverCtxt, out assertionCount); + return proverCtxt.ExprGen.Let(bindings, startCorrect); + } + + static VCExpr LetVCIterative(List blocks, + VCExpr controlFlowVariableExpr, + Dictionary label2absy, + ProverContext proverCtxt, + out int assertionCount, + bool isPositiveContext = true) + { + Contract.Requires(blocks != null); + Contract.Requires(proverCtxt != null); + Contract.Ensures(Contract.Result() != null); + + assertionCount = 0; + + Graph dag = new Graph(); + dag.AddSource(blocks[0]); + foreach (Block b in blocks) { + GotoCmd gtc = b.TransferCmd as GotoCmd; + if (gtc != null) { + Contract.Assume(gtc.labelTargets != null); + foreach (Block dest in gtc.labelTargets) { + Contract.Assert(dest != null); + dag.AddEdge(dest, b); + } + } + } + IEnumerable sortedNodes = dag.TopologicalSort(); + Contract.Assert(sortedNodes != null); + + Dictionary blockVariables = new Dictionary(); + List bindings = new List(); + VCExpressionGenerator gen = proverCtxt.ExprGen; + Contract.Assert(gen != null); + foreach (Block block in sortedNodes) { + VCExpr SuccCorrect; + GotoCmd gotocmd = block.TransferCmd as GotoCmd; + if (gotocmd == null) { + ReturnExprCmd re = block.TransferCmd as ReturnExprCmd; + if (re == null) { + SuccCorrect = VCExpressionGenerator.True; + } + else { + SuccCorrect = proverCtxt.BoogieExprTranslator.Translate(re.Expr); + if (isPositiveContext) + { + SuccCorrect = gen.Not(SuccCorrect); + } + } + } + else { + Contract.Assert(gotocmd.labelTargets != null); + List SuccCorrectVars = new List(gotocmd.labelTargets.Count); + foreach (Block successor in gotocmd.labelTargets) { + Contract.Assert(successor != null); + VCExpr s = blockVariables[successor]; + if (controlFlowVariableExpr != null) { + VCExpr controlFlowFunctionAppl = gen.ControlFlowFunctionApplication(controlFlowVariableExpr, gen.Integer(BigNum.FromInt(block.UniqueId))); + VCExpr controlTransferExpr = gen.Eq(controlFlowFunctionAppl, gen.Integer(BigNum.FromInt(successor.UniqueId))); + s = gen.Implies(controlTransferExpr, s); + } + SuccCorrectVars.Add(s); + } + SuccCorrect = gen.NAry(VCExpressionGenerator.AndOp, SuccCorrectVars); + } + + VCContext context = new VCContext(label2absy, proverCtxt, controlFlowVariableExpr, isPositiveContext); + VCExpr vc = Wlp.Block(block, SuccCorrect, context); + assertionCount += context.AssertionCount; + + VCExprVar v = gen.Variable(block.Label + "_correct", Bpl.Type.Bool); + bindings.Add(gen.LetBinding(v, vc)); + blockVariables.Add(block, v); + } + + return proverCtxt.ExprGen.Let(bindings, blockVariables[blocks[0]]); + } + + static VCExpr LetVC(Block block, + VCExpr controlFlowVariableExpr, + Dictionary label2absy, + Hashtable/**/ blockVariables, + List/*!*/ bindings, + ProverContext proverCtxt, + out int assertionCount) + { + Contract.Requires(block != null); + Contract.Requires(blockVariables!= null); + Contract.Requires(cce.NonNullElements(bindings)); + Contract.Requires(proverCtxt != null); + + Contract.Ensures(Contract.Result() != null); + + assertionCount = 0; + + VCExpressionGenerator gen = proverCtxt.ExprGen; + Contract.Assert(gen != null); + VCExprVar v = (VCExprVar)blockVariables[block]; + if (v == null) { + /* + * For block A (= block), generate: + * LET_binding A_correct = wp(A_body, (/\ S \in Successors(A) :: S_correct)) + * with the side effect of adding the let bindings to "bindings" for any + * successor not yet visited. + */ + VCExpr SuccCorrect; + GotoCmd gotocmd = block.TransferCmd as GotoCmd; + if (gotocmd == null) { + ReturnExprCmd re = block.TransferCmd as ReturnExprCmd; + if (re == null) { + SuccCorrect = VCExpressionGenerator.True; + } else { + SuccCorrect = proverCtxt.BoogieExprTranslator.Translate(re.Expr); + } + } else { + Contract.Assert( gotocmd.labelTargets != null); + List SuccCorrectVars = new List(gotocmd.labelTargets.Count); + foreach (Block successor in gotocmd.labelTargets) { + Contract.Assert(successor != null); + int ac; + VCExpr s = LetVC(successor, controlFlowVariableExpr, label2absy, blockVariables, bindings, proverCtxt, out ac); + assertionCount += ac; + if (controlFlowVariableExpr != null) + { + VCExpr controlFlowFunctionAppl = gen.ControlFlowFunctionApplication(controlFlowVariableExpr, gen.Integer(BigNum.FromInt(block.UniqueId))); + VCExpr controlTransferExpr = gen.Eq(controlFlowFunctionAppl, gen.Integer(BigNum.FromInt(successor.UniqueId))); + s = gen.Implies(controlTransferExpr, s); + } + SuccCorrectVars.Add(s); + } + SuccCorrect = gen.NAry(VCExpressionGenerator.AndOp, SuccCorrectVars); + } + + + VCContext context = new VCContext(label2absy, proverCtxt, controlFlowVariableExpr); + VCExpr vc = Wlp.Block(block, SuccCorrect, context); + assertionCount += context.AssertionCount; + + v = gen.Variable(block.Label + "_correct", Bpl.Type.Bool); + bindings.Add(gen.LetBinding(v, vc)); + blockVariables.Add(block, v); + } + return v; + } + + static VCExpr DagVC(Block block, + VCExpr controlFlowVariableExpr, + Dictionary label2absy, + Hashtable/**/ blockEquations, + ProverContext proverCtxt, + out int assertionCount) + { + Contract.Requires(block != null); + Contract.Requires(label2absy != null); + Contract.Requires(blockEquations != null); + Contract.Requires(proverCtxt != null); + Contract.Ensures(Contract.Result() != null); + + assertionCount = 0; + VCExpressionGenerator gen = proverCtxt.ExprGen; + Contract.Assert(gen != null); + VCExpr vc = (VCExpr)blockEquations[block]; + if (vc != null) { + return vc; + } + + /* + * For block A (= block), generate: + * wp(A_body, (/\ S \in Successors(A) :: DagVC(S))) + */ + VCExpr SuccCorrect = null; + GotoCmd gotocmd = block.TransferCmd as GotoCmd; + if (gotocmd != null) + { + foreach (Block successor in cce.NonNull(gotocmd.labelTargets)) { + Contract.Assert(successor != null); + int ac; + VCExpr c = DagVC(successor, controlFlowVariableExpr, label2absy, blockEquations, proverCtxt, out ac); + assertionCount += ac; + if (controlFlowVariableExpr != null) { + VCExpr controlFlowFunctionAppl = gen.ControlFlowFunctionApplication(controlFlowVariableExpr, gen.Integer(BigNum.FromInt(block.UniqueId))); + VCExpr controlTransferExpr = gen.Eq(controlFlowFunctionAppl, gen.Integer(BigNum.FromInt(successor.UniqueId))); + c = gen.Implies(controlTransferExpr, c); + } + SuccCorrect = SuccCorrect == null ? c : gen.And(SuccCorrect, c); + } + } + if (SuccCorrect == null) { + SuccCorrect = VCExpressionGenerator.True; + } + + VCContext context = new VCContext(label2absy, proverCtxt, controlFlowVariableExpr); + vc = Wlp.Block(block, SuccCorrect, context); + assertionCount += context.AssertionCount; + + // gen.MarkAsSharedFormula(vc); PR: don't know yet what to do with this guy + + blockEquations.Add(block, vc); + return vc; + } + + static VCExpr FlatBlockVC(Implementation impl, + Dictionary label2absy, + bool local, bool reach, bool doomed, + ProverContext proverCtxt, + out int assertionCount) + { + Contract.Requires(impl != null); + Contract.Requires(label2absy != null); + Contract.Requires(proverCtxt != null); + Contract.Requires( !local || !reach); // "reach" must be false for local + + VCExpressionGenerator gen = proverCtxt.ExprGen; + Contract.Assert(gen != null); + Hashtable/* Block --> VCExprVar */ BlkCorrect = BlockVariableMap(impl.Blocks, "_correct", gen); + Hashtable/* Block --> VCExprVar */ BlkReached = reach ? BlockVariableMap(impl.Blocks, "_reached", gen) : null; + + List blocks = impl.Blocks; + Contract.Assert(blocks != null); + // block sorting is now done on the VCExpr + // if (!local && (cce.NonNull(CommandLineOptions.Clo.TheProverFactory).NeedsBlockSorting) { + // blocks = SortBlocks(blocks); + // } + + VCExpr proofObligation; + if (!local) { + proofObligation = cce.NonNull((VCExprVar)BlkCorrect[impl.Blocks[0]]); + } else { + List conjuncts = new List(blocks.Count); + foreach (Block b in blocks) {Contract.Assert(b != null); + VCExpr v = cce.NonNull((VCExprVar)BlkCorrect[b]); + conjuncts.Add(v); + } + proofObligation = gen.NAry(VCExpressionGenerator.AndOp, conjuncts); + } + + VCContext context = new VCContext(label2absy, proverCtxt); + Contract.Assert(context != null); + + List programSemantics = new List(blocks.Count); + foreach (Block b in blocks) {Contract.Assert(b != null); + /* + * In block mode, + * For a return block A, generate: + * A_correct <== wp(A_body, true) [post-condition has been translated into an assert] + * For all other blocks, generate: + * A_correct <== wp(A_body, (/\ S \in Successors(A) :: S_correct)) + * + * In doomed mode, proceed as in block mode, except for a return block A, generate: + * A_correct <== wp(A_body, false) [post-condition has been translated into an assert] + * + * In block reach mode, the wp(A_body,...) in the equations above change to: + * A_reached ==> wp(A_body,...) + * and the conjunction above changes to: + * (/\ S \in Successors(A) :: S_correct \/ (\/ T \in Successors(A) && T != S :: T_reached)) + * + * In local mode, generate: + * A_correct <== wp(A_body, true) + */ + VCExpr SuccCorrect; + if (local) { + SuccCorrect = VCExpressionGenerator.True; + } else { + SuccCorrect = SuccessorsCorrect(b, BlkCorrect, BlkReached, doomed, gen); + } + + VCExpr wlp = Wlp.Block(b, SuccCorrect, context); + if (BlkReached != null) { + wlp = gen.Implies(cce.NonNull((VCExprVar)BlkReached[b]), wlp); + } + + VCExprVar okVar = cce.NonNull((VCExprVar)BlkCorrect[b]); + VCExprLetBinding binding = gen.LetBinding(okVar, wlp); + programSemantics.Add(binding); + } + + assertionCount = context.AssertionCount; + return gen.Let(programSemantics, proofObligation); + } + + private static Hashtable/* Block --> VCExprVar */ BlockVariableMap(List/*!*/ blocks, string suffix, + Microsoft.Boogie.VCExpressionGenerator gen) { + Contract.Requires(cce.NonNullElements(blocks)); + Contract.Requires(suffix != null); + Contract.Requires(gen != null); + Contract.Ensures(Contract.Result() != null); + + Hashtable/* Block --> VCExprVar */ map = new Hashtable/* Block --> (Let)Variable */(); + foreach (Block b in blocks) { + Contract.Assert(b != null); + VCExprVar v = gen.Variable(b.Label + suffix, Bpl.Type.Bool); + Contract.Assert(v != null); + map.Add(b, v); + } + return map; + } + + private static VCExpr SuccessorsCorrect( + Block b, + Hashtable/* Block --> VCExprVar */ BlkCorrect, + Hashtable/* Block --> VCExprVar */ BlkReached, + bool doomed, + Microsoft.Boogie.VCExpressionGenerator gen) { + Contract.Requires(b != null); + Contract.Requires(BlkCorrect != null); + Contract.Requires(gen != null); + Contract.Ensures(Contract.Result() != null); + + VCExpr SuccCorrect = null; + GotoCmd gotocmd = b.TransferCmd as GotoCmd; + if (gotocmd != null) { + foreach (Block successor in cce.NonNull(gotocmd.labelTargets)) { + Contract.Assert(successor != null); + // c := S_correct + VCExpr c = (VCExprVar)BlkCorrect[successor]; + Contract.Assert(c != null); + if (BlkReached != null) { + // c := S_correct \/ Sibling0_reached \/ Sibling1_reached \/ ...; + foreach (Block successorSibling in gotocmd.labelTargets) { + Contract.Assert(successorSibling != null); + if (successorSibling != successor) { + c = gen.Or(c, cce.NonNull((VCExprVar)BlkReached[successorSibling])); + } + } + } + SuccCorrect = SuccCorrect == null ? c : gen.And(SuccCorrect, c); + } + } + if (SuccCorrect == null) { + return VCExpressionGenerator.True; + } else if (doomed) { + return VCExpressionGenerator.False; + } else { + return SuccCorrect; + } + } + + static VCExpr NestedBlockVC(Implementation impl, + Dictionary label2absy, + bool reach, + ProverContext proverCtxt, + out int assertionCount){ + Contract.Requires(impl != null); + Contract.Requires(label2absy != null); + Contract.Requires(proverCtxt != null); + Contract.Requires( impl.Blocks.Count != 0); + Contract.Ensures(Contract.Result() != null); + + VCExpressionGenerator gen = proverCtxt.ExprGen; + Contract.Assert(gen != null); + Graph g = Program.GraphFromImpl(impl); + + Hashtable/* Block --> VCExprVar */ BlkCorrect = BlockVariableMap(impl.Blocks, "_correct", gen); + Hashtable/* Block --> VCExprVar */ BlkReached = reach ? BlockVariableMap(impl.Blocks, "_reached", gen) : null; + + Block startBlock = cce.NonNull( impl.Blocks[0]); + VCExpr proofObligation = (VCExprVar)BlkCorrect[startBlock]; + Contract.Assert(proofObligation != null); + VCContext context = new VCContext(label2absy, proverCtxt); + + Hashtable/*Block->int*/ totalOrder = new Hashtable/*Block->int*/(); + { + List blocks = impl.Blocks; + + // block sorting is now done on the VCExpr + // if (((!)CommandLineOptions.Clo.TheProverFactory).NeedsBlockSorting) { + // blocks = SortBlocks(blocks); + // } + int i = 0; + foreach (Block b in blocks) { + Contract.Assert(b != null); + totalOrder[b] = i; + i++; + } + } + + VCExprLetBinding programSemantics = NestedBlockEquation(cce.NonNull(impl.Blocks[0]), BlkCorrect, BlkReached, totalOrder, context, g, gen); + List ps = new List(1); + ps.Add(programSemantics); + + assertionCount = context.AssertionCount; + return gen.Let(ps, proofObligation); + } + + private static VCExprLetBinding NestedBlockEquation(Block b, + Hashtable/*Block-->VCExprVar*/ BlkCorrect, + Hashtable/*Block-->VCExprVar*/ BlkReached, + Hashtable/*Block->int*/ totalOrder, + VCContext context, + Graph g, + Microsoft.Boogie.VCExpressionGenerator gen) { + Contract.Requires(b != null); + Contract.Requires(BlkCorrect != null); + Contract.Requires(totalOrder != null); + Contract.Requires(g != null); + Contract.Requires(context != null); + + Contract.Ensures(Contract.Result() != null); + + /* + * For a block b, return: + * LET_BINDING b_correct = wp(b_body, X) + * where X is: + * LET (THOSE d \in DirectDominates(b) :: BlockEquation(d)) + * IN (/\ s \in Successors(b) :: s_correct) + * + * When the VC-expression generator does not support LET expresions, this + * will eventually turn into: + * b_correct <== wp(b_body, X) + * where X is: + * (/\ s \in Successors(b) :: s_correct) + * <== + * (/\ d \in DirectDominatees(b) :: BlockEquation(d)) + * + * In both cases above, if BlkReached is non-null, then the wp expression + * is instead: + * b_reached ==> wp(b_body, X) + */ + + VCExpr SuccCorrect = SuccessorsCorrect(b, BlkCorrect, null, false, gen); + Contract.Assert(SuccCorrect != null); + + List bindings = new List(); + foreach (Block dominee in GetSortedBlocksImmediatelyDominatedBy(g, b, totalOrder)) { + Contract.Assert(dominee != null); + VCExprLetBinding c = NestedBlockEquation(dominee, BlkCorrect, BlkReached, totalOrder, context, g, gen); + bindings.Add(c); + } + + VCExpr X = gen.Let(bindings, SuccCorrect); + VCExpr wlp = Wlp.Block(b, X, context); + if (BlkReached != null) { + wlp = gen.Implies((VCExprVar)BlkReached[b], wlp); + Contract.Assert(wlp != null); + } + VCExprVar okVar = cce.NonNull((VCExprVar)BlkCorrect[b]); + return gen.LetBinding(okVar, wlp); + } + + /// + /// Returns a list of g.ImmediatelyDominatedBy(b), but in a sorted order, hoping to steer around + /// the nondeterminism problems we've been seeing by using just this call. + /// + static List/*!*/ GetSortedBlocksImmediatelyDominatedBy(Graph/*!*/ g, Block/*!*/ b, Hashtable/*Block->int*//*!*/ totalOrder) { + Contract.Requires(g != null); + Contract.Requires(b != null); + Contract.Requires(totalOrder != null); + Contract.Ensures(Contract.Result>() != null); + + List list = new List(); + foreach (Block dominee in g.ImmediatelyDominatedBy(b)) { + Contract.Assert(dominee != null); + list.Add(dominee); + } + list.Sort(new Comparison(delegate(Block x, Block y) { + return (int)cce.NonNull(totalOrder[x]) - (int)cce.NonNull(totalOrder[y]); + })); + return list; + } + + static VCExpr VCViaStructuredProgram + (Implementation impl, Dictionary label2absy, + ProverContext proverCtxt, + out int assertionCount) + { + Contract.Requires(impl != null); + Contract.Requires(label2absy != null); + Contract.Requires(proverCtxt != null); + Contract.Ensures(Contract.Result() != null); + + #region Convert block structure back to a "regular expression" + RE r = DAG2RE.Transform(cce.NonNull(impl.Blocks[0])); + Contract.Assert(r != null); + #endregion + + VCContext ctxt = new VCContext(label2absy, proverCtxt); + Contract.Assert(ctxt != null); + #region Send wlp(program,true) to Simplify + var vcexp = Wlp.RegExpr(r, VCExpressionGenerator.True, ctxt); + assertionCount = ctxt.AssertionCount; + return vcexp; + #endregion + } + + /// + /// Remove empty blocks reachable from the startBlock of the CFG + /// + static void RemoveEmptyBlocksIterative(List blocks) { + // postorder traversal of cfg + // noting loop heads in [keep] and + // generating token information in [renameInfo] + Block startBlock = blocks[0]; + var postorder = new List(); + var keep = new HashSet(); + var visited = new HashSet(); + var grey = new HashSet(); + var stack = new Stack(); + Dictionary renameInfo = new Dictionary(); + + stack.Push(startBlock); + visited.Add(startBlock); + while (stack.Count != 0) { + var curr = stack.Pop(); + if (grey.Contains(curr)) { + postorder.Add(curr); + + // generate renameInfoForStartBlock + GotoCmd gtc = curr.TransferCmd as GotoCmd; + renameInfo[curr] = null; + if (gtc == null || gtc.labelTargets == null || gtc.labelTargets.Count == 0) { + if (curr.Cmds.Count == 0 && curr.tok.IsValid) { + renameInfo[curr] = curr; + } + } else { + if (curr.Cmds.Count == 0 || curr == startBlock) { + if (curr.tok.IsValid) { + renameInfo[curr] = curr; + } else { + HashSet successorRenameInfo = new HashSet(); + foreach (Block s in gtc.labelTargets) { + if (keep.Contains(s)) { + successorRenameInfo.Add(null); + } else { + successorRenameInfo.Add(renameInfo[s]); + } + } + if (successorRenameInfo.Count == 1) { + renameInfo[curr] = successorRenameInfo.Single(); + } + } + } + } + // end generate renameInfoForStartBlock + + } else { + grey.Add(curr); + stack.Push(curr); + GotoCmd gtc = curr.TransferCmd as GotoCmd; + if (gtc == null || gtc.labelTargets == null || gtc.labelTargets.Count == 0) continue; + foreach (Block s in gtc.labelTargets) { + if (!visited.Contains(s)) { + visited.Add(s); + stack.Push(s); + } else if (grey.Contains(s) && !postorder.Contains(s)) { // s is a loop head + keep.Add(s); + } + } + } + } + keep.Add(startBlock); + + foreach (Block b in postorder) { + if (!keep.Contains(b) && b.Cmds.Count == 0) { + GotoCmd bGtc = b.TransferCmd as GotoCmd; + foreach (Block p in b.Predecessors) { + GotoCmd pGtc = p.TransferCmd as GotoCmd; + Contract.Assert(pGtc != null); + pGtc.labelTargets.Remove(b); + pGtc.labelNames.Remove(b.Label); + } + if (bGtc == null || bGtc.labelTargets == null || bGtc.labelTargets.Count == 0) { + continue; + } + + List successors = bGtc.labelTargets; + + // Try to push token information if possible + if (b.tok.IsValid && successors.Count == 1 && b != renameInfo[startBlock]) { + var s = successors.Single(); + if (!s.tok.IsValid) { + foreach (Block p in s.Predecessors) { + if (p != b) { + GotoCmd pGtc = p.TransferCmd as GotoCmd; + Contract.Assert(pGtc != null); + pGtc.labelTargets.Remove(s); + pGtc.labelNames.Remove(s.Label); + pGtc.labelTargets.Add(s); + pGtc.labelNames.Add(b.Label); + } + } + s.tok = b.tok; + s.Label = b.Label; + } + } + + foreach (Block p in b.Predecessors) { + GotoCmd pGtc = p.TransferCmd as GotoCmd; + Contract.Assert(pGtc != null); + foreach (Block s in successors) { + if (!pGtc.labelTargets.Contains(s)) { + pGtc.labelTargets.Add(s); + pGtc.labelNames.Add(s.Label); + } + } + } + } + } + + if (!startBlock.tok.IsValid && startBlock.Cmds.All(c => c is AssumeCmd)) { + if (renameInfo[startBlock] != null) { + startBlock.tok = renameInfo[startBlock].tok; + startBlock.Label = renameInfo[startBlock].Label; + } + } + + } + + /// + /// Remove the empty blocks reachable from the block. + /// It changes the visiting state of the blocks, so that if you want to visit again the blocks, you have to reset them... + /// + static List RemoveEmptyBlocks(Block b) { + Contract.Requires(b != null); + Contract.Ensures(Contract.Result>() != null); + + Contract.Assert(b.TraversingStatus == Block.VisitState.ToVisit); + Block renameInfo; + List retVal = removeEmptyBlocksWorker(b, true, out renameInfo); + if (renameInfo != null && !b.tok.IsValid) { + bool onlyAssumes = true; + foreach (Cmd c in b.Cmds) { + if (!(c is AssumeCmd)) { + onlyAssumes = false; + break; + } + } + if (onlyAssumes) { + b.tok = renameInfo.tok; + b.Label = renameInfo.Label; + } + } + return retVal; + } + + /// + /// For every not-yet-visited block n reachable from b, change n's successors to skip empty nodes. + /// Return the *set* of blocks reachable from b without passing through a nonempty block. + /// The target of any backedge is counted as a nonempty block. + /// If renameInfoForStartBlock is non-null, it denotes an empty block with location information, and that + /// information would be appropriate to display + /// + private static List removeEmptyBlocksWorker(Block b, bool startNode, out Block renameInfoForStartBlock) + { + Contract.Requires(b != null); + Contract.Ensures(Contract.ValueAtReturn(out renameInfoForStartBlock) == null || Contract.ValueAtReturn(out renameInfoForStartBlock).tok.IsValid); + // ensures: b in result ==> renameInfoForStartBlock == null; + + renameInfoForStartBlock = null; + List bs = new List(); + GotoCmd gtc = b.TransferCmd as GotoCmd; + + // b has no successors + if (gtc == null || gtc.labelTargets == null || gtc.labelTargets.Count == 0) + { + if (b.Cmds.Count != 0){ // only empty blocks are removed... + bs.Add(b); + } else if (b.tok.IsValid) { + renameInfoForStartBlock = b; + } + return bs; + } + else if (b.TraversingStatus == Block.VisitState.ToVisit) // if b has some successors and we have not seen it so far... + { + b.TraversingStatus = Block.VisitState.BeingVisited; + + // Before recursing down to successors, make a sobering observation: + // If b has no commands and is not the start node, then it will see + // extinction (because it will not be included in the "return setOfSuccessors" + // statement below). In that case, if b has a location, then the location + // information would be lost. Hence, make an attempt to save the location + // by pushing the location onto b's successor. This can be done if (0) b has + // exactly one successor, (1) that successor has no location of its own, and + // (2) that successor has no other predecessors. + if (b.Cmds.Count == 0 && !startNode) { + // b is about to become extinct; try to save its name and location, if possible + if (b.tok.IsValid && gtc.labelTargets.Count == 1) { + Block succ = cce.NonNull(gtc.labelTargets[0]); + if (!succ.tok.IsValid && succ.Predecessors.Count == 1) { + succ.tok = b.tok; + succ.Label = b.Label; + } + } + } + + // recursively call this method on each successor + // merge result into a *set* of blocks + HashSet mergedSuccessors = new HashSet(); + int m = 0; // in the following loop, set renameInfoForStartBlock to the value that all recursive calls agree on, if possible; otherwise, null + foreach (Block dest in gtc.labelTargets){Contract.Assert(dest != null); + Block renameInfo; + List ys = removeEmptyBlocksWorker(dest, false, out renameInfo); + Contract.Assert(ys != null); + if (m == 0) { + renameInfoForStartBlock = renameInfo; + } else if (renameInfoForStartBlock != renameInfo) { + renameInfoForStartBlock = null; + } + foreach (Block successor in ys){ + if (!mergedSuccessors.Contains(successor)) + mergedSuccessors.Add(successor); + } + m++; + } + b.TraversingStatus = Block.VisitState.AlreadyVisited; + + List setOfSuccessors = new List(); + foreach (Block d in mergedSuccessors) + setOfSuccessors.Add(d); + if (b.Cmds.Count == 0 && !startNode) { + // b is about to become extinct + if (b.tok.IsValid) { + renameInfoForStartBlock = b; + } + return setOfSuccessors; + } + // otherwise, update the list of successors of b to be the blocks in setOfSuccessors + gtc.labelTargets = setOfSuccessors; + gtc.labelNames = new List(); + foreach (Block d in setOfSuccessors){ + Contract.Assert(d != null); + gtc.labelNames.Add(d.Label);} + if (!startNode) { + renameInfoForStartBlock = null; + } + return new List { b }; + } + else // b has some successors, but we are already visiting it, or we have already visited it... + { + return new List { b }; + } + } + + static void DumpMap(Hashtable /*Variable->Expr*/ map) { + Contract.Requires(map != null); + foreach (DictionaryEntry de in map) { + Variable v = (Variable)de.Key; + Contract.Assert(v != null); + Expr e = (Expr)de.Value; + Contract.Assert(e != null); + Console.Write(" "); + v.Emit(new TokenTextWriter("", Console.Out, /*setTokens=*/ false, /*pretty=*/ false), 0); + Console.Write(" --> "); + e.Emit(new TokenTextWriter("", Console.Out, /*setTokens=*/ false, /*pretty=*/ false)); + Console.WriteLine(); + } + } + } +} diff --git a/Source/VCGeneration/VCGeneration.csproj b/Source/VCGeneration/VCGeneration.csproj index 74e1eef8..9bd3fb1a 100644 --- a/Source/VCGeneration/VCGeneration.csproj +++ b/Source/VCGeneration/VCGeneration.csproj @@ -1,230 +1,230 @@ - - - - Debug - AnyCPU - 9.0.21022 - 2.0 - {E1F10180-C7B9-4147-B51F-FA1B701966DC} - Library - Properties - VCGeneration - VCGeneration - v4.0 - 512 - 1 - true - ..\InterimKey.snk - - - 3.5 - - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - false - true - Client - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - False - False - True - False - False - False - False - False - False - False - False - True - False - False - False - - - - - - - - - - - - - Full - %28none%29 - AllRules.ruleset - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - AllRules.ruleset - - - true - bin\z3apidebug\ - DEBUG;TRACE - full - AnyCPU - - - true - GlobalSuppressions.cs - prompt - Migrated rules for VCGeneration.ruleset - true - 4 - false - - - true - bin\Checked\ - DEBUG;TRACE - full - AnyCPU - bin\Debug\VCGeneration.dll.CodeAnalysisLog.xml - true - GlobalSuppressions.cs - prompt - AllRules.ruleset - ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets - ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules - True - False - True - False - False - False - False - False - False - False - False - False - True - False - False - False - - - - - - - False - Full - Build - 0 - 4 - false - - - true - bin\QED\ - DEBUG;TRACE - full - AnyCPU - prompt - AllRules.ruleset - - - - - - - - - - - - - - - - - - - - - - {43DFAD18-3E35-4558-9BE2-CAFF6B5BA8A0} - Basetypes - - - {ACCC0156-0921-43ED-8F67-AD8BDC8CDE31} - CodeContractsExtender - - - {B230A69C-C466-4065-B9C1-84D80E76D802} - Core - - - {69A2B0B8-BCAC-4101-AE7A-556FCC58C06E} - Graph - - - {ACEF88D5-DADD-46DA-BAE1-2144D63F4C83} - Model - - - {FCD3AC7F-9DFD-46C8-AB1E-09F0B0F16DC5} - ParserHelper - - - {56FFDBCA-7D14-43B8-A6CA-22A20E417EE1} - VCExpr - - - - - - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 3.5 SP1 - true - - - False - Windows Installer 3.1 - true - - - + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {E1F10180-C7B9-4147-B51F-FA1B701966DC} + Library + Properties + VCGeneration + VCGeneration + v4.0 + 512 + 1 + true + ..\InterimKey.snk + + + 3.5 + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + Client + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + False + False + True + False + False + False + False + False + False + False + False + True + False + False + False + + + + + + + + + + + + + Full + %28none%29 + AllRules.ruleset + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + AllRules.ruleset + + + true + bin\z3apidebug\ + DEBUG;TRACE + full + AnyCPU + + + true + GlobalSuppressions.cs + prompt + Migrated rules for VCGeneration.ruleset + true + 4 + false + + + true + bin\Checked\ + DEBUG;TRACE + full + AnyCPU + bin\Debug\VCGeneration.dll.CodeAnalysisLog.xml + true + GlobalSuppressions.cs + prompt + AllRules.ruleset + ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets + ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules + True + False + True + False + False + False + False + False + False + False + False + False + True + False + False + False + + + + + + + False + Full + Build + 0 + 4 + false + + + true + bin\QED\ + DEBUG;TRACE + full + AnyCPU + prompt + AllRules.ruleset + + + + + + + + + + + + + + + + + + + + + + {43DFAD18-3E35-4558-9BE2-CAFF6B5BA8A0} + Basetypes + + + {ACCC0156-0921-43ED-8F67-AD8BDC8CDE31} + CodeContractsExtender + + + {B230A69C-C466-4065-B9C1-84D80E76D802} + Core + + + {69A2B0B8-BCAC-4101-AE7A-556FCC58C06E} + Graph + + + {ACEF88D5-DADD-46DA-BAE1-2144D63F4C83} + Model + + + {FCD3AC7F-9DFD-46C8-AB1E-09F0B0F16DC5} + ParserHelper + + + {56FFDBCA-7D14-43B8-A6CA-22A20E417EE1} + VCExpr + + + + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + --> diff --git a/Source/VCGeneration/Wlp.cs b/Source/VCGeneration/Wlp.cs index 82d3b607..b4ee4c09 100644 --- a/Source/VCGeneration/Wlp.cs +++ b/Source/VCGeneration/Wlp.cs @@ -1,255 +1,255 @@ -//----------------------------------------------------------------------------- -// -// Copyright (C) Microsoft Corporation. All Rights Reserved. -// -//----------------------------------------------------------------------------- -using System; -using System.Collections; -using Microsoft.Boogie; -using Microsoft.Boogie.VCExprAST; -using System.Diagnostics.Contracts; -using System.Collections.Generic; -using Microsoft.Basetypes; - -namespace VC { - public class VCContext - { - [ContractInvariantMethod] - void ObjectInvariant() - { - Contract.Invariant(Ctxt != null); - } - - [Rep] public readonly Dictionary Label2absy; - [Rep] public readonly ProverContext Ctxt; - public readonly VCExpr ControlFlowVariableExpr; - public int AssertionCount; // counts the number of assertions for which Wlp has been computed - public bool isPositiveContext; - - public VCContext(Dictionary label2absy, ProverContext ctxt, bool isPositiveContext = true) - { - Contract.Requires(ctxt != null); - this.Label2absy = label2absy; - this.Ctxt = ctxt; - this.isPositiveContext = isPositiveContext; - } - - public VCContext(Dictionary label2absy, ProverContext ctxt, VCExpr controlFlowVariableExpr, bool isPositiveContext = true) - { - Contract.Requires(ctxt != null); - this.Label2absy = label2absy; - this.Ctxt = ctxt; - this.ControlFlowVariableExpr = controlFlowVariableExpr; - this.isPositiveContext = isPositiveContext; - } - } - - #region A class to compute wlp of a passive command program - - public class Wlp - { - public static VCExpr Block(Block b, VCExpr N, VCContext ctxt) - //modifies ctxt.*; - { - Contract.Requires(b != null); - Contract.Requires(N != null); - Contract.Requires(ctxt != null); - Contract.Ensures(Contract.Result() != null); - - VCExpressionGenerator gen = ctxt.Ctxt.ExprGen; - Contract.Assert(gen != null); - - VCExpr res = N; - - for (int i = b.Cmds.Count; --i >= 0; ) - { - res = Cmd(b, cce.NonNull( b.Cmds[i]), res, ctxt); - } - - int id = b.UniqueId; - if (ctxt.Label2absy != null) { - ctxt.Label2absy[id] = b; - } - - try { - cce.BeginExpose(ctxt); - if (ctxt.Label2absy == null) { - return res; - } - else { - return gen.Implies(gen.LabelPos(cce.NonNull(id.ToString()), VCExpressionGenerator.True), res); - } - } finally { - cce.EndExpose(); - } - } - - /// - /// Computes the wlp for an assert or assume command "cmd". - /// - public static VCExpr Cmd(Block b, Cmd cmd, VCExpr N, VCContext ctxt) { - Contract.Requires(cmd != null); - Contract.Requires(N != null); - Contract.Requires(ctxt != null); - Contract.Ensures(Contract.Result() != null); - - VCExpressionGenerator gen = ctxt.Ctxt.ExprGen; - Contract.Assert(gen != null); - if (cmd is AssertCmd) { - AssertCmd ac = (AssertCmd)cmd; - - var isFullyVerified = false; - if (ac.VerifiedUnder != null) - { - var litExpr = ac.VerifiedUnder as LiteralExpr; - isFullyVerified = litExpr != null && litExpr.IsTrue; - } - - if (!isFullyVerified) - { - ctxt.Ctxt.BoogieExprTranslator.isPositiveContext = !ctxt.Ctxt.BoogieExprTranslator.isPositiveContext; - } - - VCExpr C = ctxt.Ctxt.BoogieExprTranslator.Translate(ac.Expr); - - VCExpr VU = null; - if (!isFullyVerified) - { - if (ac.VerifiedUnder != null) - { - VU = ctxt.Ctxt.BoogieExprTranslator.Translate(ac.VerifiedUnder); - - if (CommandLineOptions.Clo.RunDiagnosticsOnTimeout) - { - ctxt.Ctxt.TimeoutDiagnosticIDToAssertion[ctxt.Ctxt.TimoutDiagnosticsCount] = new Tuple(ac, b.TransferCmd); - VU = gen.Or(VU, gen.Function(VCExpressionGenerator.TimeoutDiagnosticsOp, gen.Integer(BigNum.FromInt(ctxt.Ctxt.TimoutDiagnosticsCount++)))); - } - } - else if (CommandLineOptions.Clo.RunDiagnosticsOnTimeout) - { - ctxt.Ctxt.TimeoutDiagnosticIDToAssertion[ctxt.Ctxt.TimoutDiagnosticsCount] = new Tuple(ac, b.TransferCmd); - VU = gen.Function(VCExpressionGenerator.TimeoutDiagnosticsOp, gen.Integer(BigNum.FromInt(ctxt.Ctxt.TimoutDiagnosticsCount++))); - } - ctxt.Ctxt.BoogieExprTranslator.isPositiveContext = !ctxt.Ctxt.BoogieExprTranslator.isPositiveContext; - } - - VCExpr R = null; - if (CommandLineOptions.Clo.vcVariety == CommandLineOptions.VCVariety.Doomed) { - R = gen.Implies(C, N); - } else { - var subsumption = Subsumption(ac); - if (subsumption == CommandLineOptions.SubsumptionOption.Always - || (subsumption == CommandLineOptions.SubsumptionOption.NotForQuantifiers && !(C is VCExprQuantifier))) - { - N = gen.ImpliesSimp(C, N, false); - } - - if (isFullyVerified) - { - return N; - } - else if (VU != null) - { - C = gen.OrSimp(VU, C); - } - - int id = ac.UniqueId; - if (ctxt.Label2absy != null) - { - ctxt.Label2absy[id] = ac; - } - - ctxt.AssertionCount++; - - if (ctxt.ControlFlowVariableExpr == null) { - Contract.Assert(ctxt.Label2absy != null); - R = gen.AndSimp(gen.LabelNeg(cce.NonNull(id.ToString()), C), N); - } else { - VCExpr controlFlowFunctionAppl = gen.ControlFlowFunctionApplication(ctxt.ControlFlowVariableExpr, gen.Integer(BigNum.FromInt(b.UniqueId))); - Contract.Assert(controlFlowFunctionAppl != null); - VCExpr assertFailure = gen.Eq(controlFlowFunctionAppl, gen.Integer(BigNum.FromInt(-ac.UniqueId))); - if (ctxt.Label2absy == null) { - R = gen.AndSimp(gen.Implies(assertFailure, C), N); - } else { - R = gen.AndSimp(gen.LabelNeg(cce.NonNull(id.ToString()), gen.Implies(assertFailure, C)), N); - } - } - } - return R; - } else if (cmd is AssumeCmd) { - AssumeCmd ac = (AssumeCmd)cmd; - - if (CommandLineOptions.Clo.StratifiedInlining > 0) { - // Label the assume if it is a procedure call - NAryExpr naryExpr = ac.Expr as NAryExpr; - if (naryExpr != null) { - if (naryExpr.Fun is FunctionCall) { - int id = ac.UniqueId; - ctxt.Label2absy[id] = ac; - return gen.ImpliesSimp(gen.LabelPos(cce.NonNull("si_fcall_" + id.ToString()), ctxt.Ctxt.BoogieExprTranslator.Translate(ac.Expr)), N); - } - } - } - return gen.ImpliesSimp(ctxt.Ctxt.BoogieExprTranslator.Translate(ac.Expr), N); - } else { - Console.WriteLine(cmd.ToString()); - Contract.Assert(false); throw new cce.UnreachableException(); // unexpected command - } - } - - public static CommandLineOptions.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; - } - } - - public static VCExpr RegExpr(RE r, VCExpr N, VCContext ctxt) - { - Contract.Requires(r != null); - Contract.Requires(N != null); - Contract.Requires(ctxt != null); - Contract.Ensures(Contract.Result() != null); - - if ( r is AtomicRE ) - { - AtomicRE ar = (AtomicRE) r; - return Block(ar.b, N, ctxt); - } - else if ( r is Sequential ) - { - Sequential s = (Sequential) r; - return RegExpr(s.first, RegExpr(s.second, N, ctxt), ctxt); - } - else if ( r is Choice ) - { - Choice ch = (Choice) r; - VCExpr res; - if (ch.rs == null || ch.rs.Count==0) - { - res = N; - } - else - { - VCExpr currentWLP = RegExpr(cce.NonNull(ch.rs[0]), N, ctxt); - for (int i = 1, n = ch.rs.Count; i < n; i++) - { - currentWLP = ctxt.Ctxt.ExprGen.And(currentWLP, RegExpr(cce.NonNull(ch.rs[i]), N, ctxt)); - } - res = currentWLP; - } - return res; - } - else - { - Contract.Assert(false);throw new cce.UnreachableException(); // unexpected RE subtype - } - } - } - #endregion - -} +//----------------------------------------------------------------------------- +// +// Copyright (C) Microsoft Corporation. All Rights Reserved. +// +//----------------------------------------------------------------------------- +using System; +using System.Collections; +using Microsoft.Boogie; +using Microsoft.Boogie.VCExprAST; +using System.Diagnostics.Contracts; +using System.Collections.Generic; +using Microsoft.Basetypes; + +namespace VC { + public class VCContext + { + [ContractInvariantMethod] + void ObjectInvariant() + { + Contract.Invariant(Ctxt != null); + } + + [Rep] public readonly Dictionary Label2absy; + [Rep] public readonly ProverContext Ctxt; + public readonly VCExpr ControlFlowVariableExpr; + public int AssertionCount; // counts the number of assertions for which Wlp has been computed + public bool isPositiveContext; + + public VCContext(Dictionary label2absy, ProverContext ctxt, bool isPositiveContext = true) + { + Contract.Requires(ctxt != null); + this.Label2absy = label2absy; + this.Ctxt = ctxt; + this.isPositiveContext = isPositiveContext; + } + + public VCContext(Dictionary label2absy, ProverContext ctxt, VCExpr controlFlowVariableExpr, bool isPositiveContext = true) + { + Contract.Requires(ctxt != null); + this.Label2absy = label2absy; + this.Ctxt = ctxt; + this.ControlFlowVariableExpr = controlFlowVariableExpr; + this.isPositiveContext = isPositiveContext; + } + } + + #region A class to compute wlp of a passive command program + + public class Wlp + { + public static VCExpr Block(Block b, VCExpr N, VCContext ctxt) + //modifies ctxt.*; + { + Contract.Requires(b != null); + Contract.Requires(N != null); + Contract.Requires(ctxt != null); + Contract.Ensures(Contract.Result() != null); + + VCExpressionGenerator gen = ctxt.Ctxt.ExprGen; + Contract.Assert(gen != null); + + VCExpr res = N; + + for (int i = b.Cmds.Count; --i >= 0; ) + { + res = Cmd(b, cce.NonNull( b.Cmds[i]), res, ctxt); + } + + int id = b.UniqueId; + if (ctxt.Label2absy != null) { + ctxt.Label2absy[id] = b; + } + + try { + cce.BeginExpose(ctxt); + if (ctxt.Label2absy == null) { + return res; + } + else { + return gen.Implies(gen.LabelPos(cce.NonNull(id.ToString()), VCExpressionGenerator.True), res); + } + } finally { + cce.EndExpose(); + } + } + + /// + /// Computes the wlp for an assert or assume command "cmd". + /// + public static VCExpr Cmd(Block b, Cmd cmd, VCExpr N, VCContext ctxt) { + Contract.Requires(cmd != null); + Contract.Requires(N != null); + Contract.Requires(ctxt != null); + Contract.Ensures(Contract.Result() != null); + + VCExpressionGenerator gen = ctxt.Ctxt.ExprGen; + Contract.Assert(gen != null); + if (cmd is AssertCmd) { + AssertCmd ac = (AssertCmd)cmd; + + var isFullyVerified = false; + if (ac.VerifiedUnder != null) + { + var litExpr = ac.VerifiedUnder as LiteralExpr; + isFullyVerified = litExpr != null && litExpr.IsTrue; + } + + if (!isFullyVerified) + { + ctxt.Ctxt.BoogieExprTranslator.isPositiveContext = !ctxt.Ctxt.BoogieExprTranslator.isPositiveContext; + } + + VCExpr C = ctxt.Ctxt.BoogieExprTranslator.Translate(ac.Expr); + + VCExpr VU = null; + if (!isFullyVerified) + { + if (ac.VerifiedUnder != null) + { + VU = ctxt.Ctxt.BoogieExprTranslator.Translate(ac.VerifiedUnder); + + if (CommandLineOptions.Clo.RunDiagnosticsOnTimeout) + { + ctxt.Ctxt.TimeoutDiagnosticIDToAssertion[ctxt.Ctxt.TimoutDiagnosticsCount] = new Tuple(ac, b.TransferCmd); + VU = gen.Or(VU, gen.Function(VCExpressionGenerator.TimeoutDiagnosticsOp, gen.Integer(BigNum.FromInt(ctxt.Ctxt.TimoutDiagnosticsCount++)))); + } + } + else if (CommandLineOptions.Clo.RunDiagnosticsOnTimeout) + { + ctxt.Ctxt.TimeoutDiagnosticIDToAssertion[ctxt.Ctxt.TimoutDiagnosticsCount] = new Tuple(ac, b.TransferCmd); + VU = gen.Function(VCExpressionGenerator.TimeoutDiagnosticsOp, gen.Integer(BigNum.FromInt(ctxt.Ctxt.TimoutDiagnosticsCount++))); + } + ctxt.Ctxt.BoogieExprTranslator.isPositiveContext = !ctxt.Ctxt.BoogieExprTranslator.isPositiveContext; + } + + VCExpr R = null; + if (CommandLineOptions.Clo.vcVariety == CommandLineOptions.VCVariety.Doomed) { + R = gen.Implies(C, N); + } else { + var subsumption = Subsumption(ac); + if (subsumption == CommandLineOptions.SubsumptionOption.Always + || (subsumption == CommandLineOptions.SubsumptionOption.NotForQuantifiers && !(C is VCExprQuantifier))) + { + N = gen.ImpliesSimp(C, N, false); + } + + if (isFullyVerified) + { + return N; + } + else if (VU != null) + { + C = gen.OrSimp(VU, C); + } + + int id = ac.UniqueId; + if (ctxt.Label2absy != null) + { + ctxt.Label2absy[id] = ac; + } + + ctxt.AssertionCount++; + + if (ctxt.ControlFlowVariableExpr == null) { + Contract.Assert(ctxt.Label2absy != null); + R = gen.AndSimp(gen.LabelNeg(cce.NonNull(id.ToString()), C), N); + } else { + VCExpr controlFlowFunctionAppl = gen.ControlFlowFunctionApplication(ctxt.ControlFlowVariableExpr, gen.Integer(BigNum.FromInt(b.UniqueId))); + Contract.Assert(controlFlowFunctionAppl != null); + VCExpr assertFailure = gen.Eq(controlFlowFunctionAppl, gen.Integer(BigNum.FromInt(-ac.UniqueId))); + if (ctxt.Label2absy == null) { + R = gen.AndSimp(gen.Implies(assertFailure, C), N); + } else { + R = gen.AndSimp(gen.LabelNeg(cce.NonNull(id.ToString()), gen.Implies(assertFailure, C)), N); + } + } + } + return R; + } else if (cmd is AssumeCmd) { + AssumeCmd ac = (AssumeCmd)cmd; + + if (CommandLineOptions.Clo.StratifiedInlining > 0) { + // Label the assume if it is a procedure call + NAryExpr naryExpr = ac.Expr as NAryExpr; + if (naryExpr != null) { + if (naryExpr.Fun is FunctionCall) { + int id = ac.UniqueId; + ctxt.Label2absy[id] = ac; + return gen.ImpliesSimp(gen.LabelPos(cce.NonNull("si_fcall_" + id.ToString()), ctxt.Ctxt.BoogieExprTranslator.Translate(ac.Expr)), N); + } + } + } + return gen.ImpliesSimp(ctxt.Ctxt.BoogieExprTranslator.Translate(ac.Expr), N); + } else { + Console.WriteLine(cmd.ToString()); + Contract.Assert(false); throw new cce.UnreachableException(); // unexpected command + } + } + + public static CommandLineOptions.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; + } + } + + public static VCExpr RegExpr(RE r, VCExpr N, VCContext ctxt) + { + Contract.Requires(r != null); + Contract.Requires(N != null); + Contract.Requires(ctxt != null); + Contract.Ensures(Contract.Result() != null); + + if ( r is AtomicRE ) + { + AtomicRE ar = (AtomicRE) r; + return Block(ar.b, N, ctxt); + } + else if ( r is Sequential ) + { + Sequential s = (Sequential) r; + return RegExpr(s.first, RegExpr(s.second, N, ctxt), ctxt); + } + else if ( r is Choice ) + { + Choice ch = (Choice) r; + VCExpr res; + if (ch.rs == null || ch.rs.Count==0) + { + res = N; + } + else + { + VCExpr currentWLP = RegExpr(cce.NonNull(ch.rs[0]), N, ctxt); + for (int i = 1, n = ch.rs.Count; i < n; i++) + { + currentWLP = ctxt.Ctxt.ExprGen.And(currentWLP, RegExpr(cce.NonNull(ch.rs[i]), N, ctxt)); + } + res = currentWLP; + } + return res; + } + else + { + Contract.Assert(false);throw new cce.UnreachableException(); // unexpected RE subtype + } + } + } + #endregion + +} diff --git a/Source/VCGeneration/cce.cs b/Source/VCGeneration/cce.cs index 23d79815..42cabfcb 100644 --- a/Source/VCGeneration/cce.cs +++ b/Source/VCGeneration/cce.cs @@ -1,105 +1,105 @@ - -using System; -using System.Collections.Generic; -using System.Diagnostics.Contracts; -using System.Text; -using Microsoft.Boogie; - - /// - /// A class containing static methods to extend the functionality of Code Contracts - /// - -public static class cce { - [Pure] - public static T NonNull(T t) { - Contract.Assert(t != null); - return t; - } - [Pure] - public static bool NonNullElements(IEnumerable collection) { - return collection != null && Contract.ForAll(collection, c => c != null); - } - [Pure] - public static bool NonNullElements(IDictionary collection) { - return collection != null && NonNullElements(collection.Keys) && NonNullElements(collection.Values); - } - [Pure] - public static bool NonNullElements(VariableSeq collection) { - return collection != null && Contract.ForAll(0, collection.Length, i => collection[i] != null); - } - [Pure] - public static void BeginExpose(object o) { - } - [Pure] - public static void EndExpose() { - } - [Pure] - public static bool IsPeerConsistent(object o) { - return true; - } - [Pure] - public static bool IsConsistent(object o) { - return true; - } - [Pure] - public static bool IsExposable(object o) { - return true; - } - [Pure] - public static bool IsExposed(object o) { - return true; - } - public static class Owner { - [Pure] - public static bool Same(object o, object p) { - return true; - } - [Pure] - public static void AssignSame(object o, object p) { - } - [Pure] - public static object ElementProxy(object o) { - return o; - } - [Pure] - public static bool None(object o) { - return true; - } - } - [Pure] - public static void LoopInvariant(bool p) { - Contract.Assert(p); - } - - public class UnreachableException : Exception { - public UnreachableException() { - } - } -} - -public class PeerAttribute : System.Attribute { -} -public class RepAttribute : System.Attribute { -} -public class CapturedAttribute : System.Attribute { -} -public class NotDelayedAttribute : System.Attribute { -} -public class NoDefaultContractAttribute : System.Attribute { -} -public class VerifyAttribute : System.Attribute { - public VerifyAttribute(bool b) { - - } -} -public class StrictReadonlyAttribute : System.Attribute { - } -public class AdditiveAttribute : System.Attribute { -} -public class ReadsAttribute : System.Attribute { - public enum Reads { - Nothing, - }; - public ReadsAttribute(object o) { - } -} + +using System; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Text; +using Microsoft.Boogie; + + /// + /// A class containing static methods to extend the functionality of Code Contracts + /// + +public static class cce { + [Pure] + public static T NonNull(T t) { + Contract.Assert(t != null); + return t; + } + [Pure] + public static bool NonNullElements(IEnumerable collection) { + return collection != null && Contract.ForAll(collection, c => c != null); + } + [Pure] + public static bool NonNullElements(IDictionary collection) { + return collection != null && NonNullElements(collection.Keys) && NonNullElements(collection.Values); + } + [Pure] + public static bool NonNullElements(VariableSeq collection) { + return collection != null && Contract.ForAll(0, collection.Length, i => collection[i] != null); + } + [Pure] + public static void BeginExpose(object o) { + } + [Pure] + public static void EndExpose() { + } + [Pure] + public static bool IsPeerConsistent(object o) { + return true; + } + [Pure] + public static bool IsConsistent(object o) { + return true; + } + [Pure] + public static bool IsExposable(object o) { + return true; + } + [Pure] + public static bool IsExposed(object o) { + return true; + } + public static class Owner { + [Pure] + public static bool Same(object o, object p) { + return true; + } + [Pure] + public static void AssignSame(object o, object p) { + } + [Pure] + public static object ElementProxy(object o) { + return o; + } + [Pure] + public static bool None(object o) { + return true; + } + } + [Pure] + public static void LoopInvariant(bool p) { + Contract.Assert(p); + } + + public class UnreachableException : Exception { + public UnreachableException() { + } + } +} + +public class PeerAttribute : System.Attribute { +} +public class RepAttribute : System.Attribute { +} +public class CapturedAttribute : System.Attribute { +} +public class NotDelayedAttribute : System.Attribute { +} +public class NoDefaultContractAttribute : System.Attribute { +} +public class VerifyAttribute : System.Attribute { + public VerifyAttribute(bool b) { + + } +} +public class StrictReadonlyAttribute : System.Attribute { + } +public class AdditiveAttribute : System.Attribute { +} +public class ReadsAttribute : System.Attribute { + public enum Reads { + Nothing, + }; + public ReadsAttribute(object o) { + } +} -- cgit v1.2.3