From 1f7016e583f2264340385b480a4507e35133669d Mon Sep 17 00:00:00 2001 From: tabarbe Date: Wed, 28 Jul 2010 22:18:03 +0000 Subject: Boogie\VCGeneration: Renaming sources in preparation for my addition of the ported C# version --- Source/VCGeneration/Check.cs | 505 ++++ Source/VCGeneration/Check.ssc | 505 ---- Source/VCGeneration/ConditionGeneration.cs | 1178 ++++++++ Source/VCGeneration/ConditionGeneration.ssc | 1178 -------- Source/VCGeneration/Context.cs | 200 ++ Source/VCGeneration/Context.ssc | 200 -- Source/VCGeneration/DoomCheck.cs | 797 +++++ Source/VCGeneration/DoomCheck.ssc | 797 ----- Source/VCGeneration/DoomErrorHandler.cs | 93 + Source/VCGeneration/DoomErrorHandler.ssc | 93 - Source/VCGeneration/OrderingAxioms.cs | 285 ++ Source/VCGeneration/OrderingAxioms.ssc | 285 -- Source/VCGeneration/VC.cs | 4340 +++++++++++++++++++++++++++ Source/VCGeneration/VC.ssc | 4340 --------------------------- Source/VCGeneration/VCDoomed.cs | 1258 ++++++++ Source/VCGeneration/VCDoomed.ssc | 1258 -------- Source/VCGeneration/VCGeneration.csproj | 154 + Source/VCGeneration/VCGeneration.sscproj | 154 - Source/VCGeneration/Wlp.cs | 184 ++ Source/VCGeneration/Wlp.ssc | 184 -- 20 files changed, 8994 insertions(+), 8994 deletions(-) create mode 100644 Source/VCGeneration/Check.cs delete mode 100644 Source/VCGeneration/Check.ssc create mode 100644 Source/VCGeneration/ConditionGeneration.cs delete mode 100644 Source/VCGeneration/ConditionGeneration.ssc create mode 100644 Source/VCGeneration/Context.cs delete mode 100644 Source/VCGeneration/Context.ssc create mode 100644 Source/VCGeneration/DoomCheck.cs delete mode 100644 Source/VCGeneration/DoomCheck.ssc create mode 100644 Source/VCGeneration/DoomErrorHandler.cs delete mode 100644 Source/VCGeneration/DoomErrorHandler.ssc create mode 100644 Source/VCGeneration/OrderingAxioms.cs delete mode 100644 Source/VCGeneration/OrderingAxioms.ssc create mode 100644 Source/VCGeneration/VC.cs delete mode 100644 Source/VCGeneration/VC.ssc create mode 100644 Source/VCGeneration/VCDoomed.cs delete mode 100644 Source/VCGeneration/VCDoomed.ssc create mode 100644 Source/VCGeneration/VCGeneration.csproj delete mode 100644 Source/VCGeneration/VCGeneration.sscproj create mode 100644 Source/VCGeneration/Wlp.cs delete mode 100644 Source/VCGeneration/Wlp.ssc diff --git a/Source/VCGeneration/Check.cs b/Source/VCGeneration/Check.cs new file mode 100644 index 00000000..4bd6edea --- /dev/null +++ b/Source/VCGeneration/Check.cs @@ -0,0 +1,505 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) Microsoft Corporation. All Rights Reserved. +// +//----------------------------------------------------------------------------- +using System; +using System.Collections; +using System.Collections.Generic; +using System.Threading; +using System.IO; +using System.Diagnostics; +using Microsoft.Contracts; +using Microsoft.Boogie.AbstractInterpretation; +using Microsoft.Boogie.VCExprAST; +using Microsoft.Basetypes; + +namespace Microsoft.Boogie +{ + /// + /// 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 + { + private readonly VCExpressionGenerator! gen; + + private ProverInterface! thmProver; + private CommandLineOptions.BvHandling bitvectors; + private int timeout; + + // state for the async interface + private volatile ProverInterface.Outcome outcome; + private volatile bool busy; + private volatile bool hasOutput; + private volatile UnexpectedProverOutputException outputExn; + private DateTime proverStart; + private TimeSpan proverRunTime; + private volatile ProverInterface.ErrorHandler handler; + private volatile bool closed; + + public readonly AutoResetEvent! ProverDone = new AutoResetEvent(false); + + private static CommandLineOptions.BvHandling BvHandlingForImpl(Implementation impl) + { + if (impl == null) return CommandLineOptions.Clo.Bitvectors; + bool force_int = false; + bool force_native = false; + ((!)impl.Proc).CheckBooleanAttribute("forceBvInt", ref force_int); + impl.Proc.CheckBooleanAttribute("forceBvZ3Native", ref force_native); + impl.CheckBooleanAttribute("forceBvInt", ref force_int); + impl.CheckBooleanAttribute("forceBvZ3Native", ref force_native); + if (force_native) return CommandLineOptions.BvHandling.Z3Native; + if (force_int) return CommandLineOptions.BvHandling.ToInt; + return CommandLineOptions.Clo.Bitvectors; + } + + public bool WillingToHandle(Implementation impl, int timeout) + { + return !closed && timeout == this.timeout && bitvectors == BvHandlingForImpl(impl); + } + + public VCExpressionGenerator! VCExprGen + { + get { return this.gen; } + } + public ProverInterface! TheoremProver + { + get { return this.thmProver; } + } + + ///////////////////////////////////////////////////////////////////////////////// + // We share context information for the same program between different Checkers + + private struct ContextCacheKey { + public readonly Program! program; + public readonly CommandLineOptions.BvHandling bitvectors; + + public ContextCacheKey(Program! prog, + CommandLineOptions.BvHandling bitvectors) { + this.program = prog; + this.bitvectors = bitvectors; + } + + [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) && + this.bitvectors.Equals(thatKey.bitvectors); + } + return false; + } + + [Pure] + public override int GetHashCode() { + return this.program.GetHashCode() + this.bitvectors.GetHashCode(); + } + } + + ///////////////////////////////////////////////////////////////////////////////// + + /// + /// Constructor. Initialize a checker with the program and log file. + /// + public Checker(VC.ConditionGeneration! vcgen, Program! prog, string/*?*/ logFilePath, bool appendLogFile, Implementation impl, int timeout) + { + this.bitvectors = BvHandlingForImpl(impl); + this.timeout = timeout; + + ProverOptions! options = ((!)CommandLineOptions.Clo.TheProverFactory).BlankProverOptions(); + + if (logFilePath != null) { + options.LogFilename = logFilePath; + if (appendLogFile) + options.AppendLogFile = appendLogFile; + } + + options.Parse(CommandLineOptions.Clo.ProverOptions); + + if (timeout > 0) { + options.TimeLimit = timeout * 1000; + } + options.BitVectors = this.bitvectors; + + ContextCacheKey key = new ContextCacheKey (prog, this.bitvectors); + ProverContext ctx; + ProverInterface prover; + + if (vcgen.CheckerCommonState == null) { + vcgen.CheckerCommonState = new Dictionary (); + } + IDictionary! cachedContexts = (IDictionary) vcgen.CheckerCommonState; + + if (cachedContexts.TryGetValue(key, out ctx)) { + ctx = (ProverContext!)((!)ctx).Clone(); + prover = (ProverInterface) + CommandLineOptions.Clo.TheProverFactory.SpawnProver(options, ctx); + } else { + ctx = (ProverContext!)CommandLineOptions.Clo.TheProverFactory.NewProverContext(options); + + // set up the context + foreach (Declaration! decl in prog.TopLevelDeclarations) { + TypeCtorDecl t = decl as TypeCtorDecl; + if (t != null) { + ctx.DeclareType(t, null); + } + } + foreach (Declaration! decl in prog.TopLevelDeclarations) { + 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 (Declaration! decl in prog.TopLevelDeclarations) { + bool expand = false; + Axiom ax = decl as Axiom; + decl.CheckBooleanAttribute("inline", ref expand); + if (!expand && ax != null) { + ctx.AddAxiom(ax, null); + } + } + foreach (Declaration! decl in prog.TopLevelDeclarations) { + GlobalVariable v = decl as GlobalVariable; + if (v != null) { + ctx.DeclareGlobalVariable(v, null); + } + } + + // 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, (ProverContext!)ctx.Clone()); + } + + this.thmProver = prover; + this.gen = prover.VCExprGen; + + // base(); + } + + + /// + /// Clean-up. + /// + public void Close() + { + this.closed = true; + thmProver.Close(); + } + + /// + /// Push a Verification Condition as an Axiom + /// (Required for Doomed Program Point detection) + /// + public void PushVCExpr(VCExpr! vc) + { + //thmProver.Context.AddAxiom(vc); + thmProver.PushVCExpression(vc); + } + + public bool IsBusy + { + get { return busy; } + } + + public bool Closed + { + get { return closed; } + } + + public bool HasOutput + { + get { return hasOutput; } + } + + public TimeSpan ProverRunTime + { + get { return proverRunTime; } + } + + private void WaitForOutput(object dummy) + { + try { + outcome = thmProver.CheckOutcome((!)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; + } + + assert busy; + hasOutput = true; + proverRunTime = DateTime.Now - proverStart; + + ProverDone.Set(); + } + + public void BeginCheck(string! descriptiveName, VCExpr! vc, ProverInterface.ErrorHandler! handler) + requires !IsBusy; + { + assert !busy; + busy = true; + hasOutput = false; + outputExn = null; + this.handler = handler; + + proverStart = DateTime.Now; + thmProver.BeginCheck(descriptiveName, vc, handler); + // gen.ClearSharedFormulas(); PR: don't know yet what to do with this guy + + ThreadPool.QueueUserWorkItem(WaitForOutput); + } + + public ProverInterface.Outcome ReadOutcome() + throws UnexpectedProverOutputException; + requires IsBusy; + requires HasOutput; + { + hasOutput = false; + busy = false; + + if (outputExn != null) { + throw outputExn; + } + + return outcome; + } + } + + // ----------------------------------------------------------------------------------------------- + // ----------------------------------------------------------------------------------------------- + // ----------------------------------------------------------------------------------------------- + + public class ErrorModel { + public Dictionary! identifierToPartition; + public List>! partitionToIdentifiers; + public List! partitionToValue; + public Dictionary! valueToPartition; + public Dictionary>!>! definedFunctions; + + public ErrorModel(Dictionary! identifierToPartition, List>! partitionToIdentifiers, List! partitionToValue, Dictionary! valueToPartition, Dictionary>!>! definedFunctions) { + this.identifierToPartition = identifierToPartition; + this.partitionToIdentifiers = partitionToIdentifiers; + this.partitionToValue = partitionToValue; + this.valueToPartition = valueToPartition; + this.definedFunctions = definedFunctions; + } + + public virtual void Print(TextWriter! writer) { } + + public int LookupPartitionValue(int partition) + { + BigNum bignum = (BigNum) (!)partitionToValue[partition]; + return bignum.ToInt; + } + + public int LookupControlFlowFunctionAt(int cfc, int id) + { + List>! tuples = this.definedFunctions["ControlFlow"]; + foreach (List tuple in tuples) + { + if (tuple == null) continue; + if (tuple.Count != 3) continue; + if (LookupPartitionValue(tuple[0]) == cfc && LookupPartitionValue(tuple[1]) == id) + return LookupPartitionValue(tuple[2]); + } + assert false; + return 0; + } + + private string! LookupSkolemConstant(string! name) { + foreach (string! functionName in identifierToPartition.Keys) + { + int index = functionName.LastIndexOf("!"); + if (index == -1) continue; + string! newFunctionName = (!)functionName.Remove(index); + if (newFunctionName.Equals(name)) + return functionName; + } + return ""; + } + private string! LookupSkolemFunction(string! name) { + foreach (string! functionName in definedFunctions.Keys) + { + int index = functionName.LastIndexOf("!"); + if (index == -1) continue; + string! newFunctionName = (!)functionName.Remove(index); + if (newFunctionName.Equals(name)) + return functionName; + } + return ""; + } + public int LookupSkolemFunctionAt(string! functionName, List! values) + { + string! actualFunctionName = LookupSkolemFunction(functionName); + if (actualFunctionName.Equals("")) + { + // The skolem function is actually a skolem constant + actualFunctionName = LookupSkolemConstant(functionName); + assert !actualFunctionName.Equals(""); + return identifierToPartition[actualFunctionName]; + } + List>! tuples = this.definedFunctions[actualFunctionName]; + assert tuples.Count > 0; + // the last tuple is a dummy tuple + for (int n = 0; n < tuples.Count - 1; n++) + { + List! tuple = (!)tuples[n]; + assert tuple.Count - 1 <= values.Count; + for (int i = 0, j = 0; i < values.Count; i++) + { + if (values[i] == tuple[j]) { + // succeeded in matching tuple[j] + j++; + if (j == tuple.Count-1) return tuple[tuple.Count - 1]; + } + } + } + assert false; + return 0; + } + + public List! PartitionsToValues(List! args) + { + List! vals = new List(); + foreach(int i in args) + { + object! o = (!)partitionToValue[i]; + if (o is bool) { + vals.Add(o); + } else if (o is BigNum) { + vals.Add(o); + } else if (o is List!>) { + List!> array = (List!>) o; + List!> arrayVal = new List!>(); + foreach (List! tuple in array) { + List tupleVal = new List(); + foreach (int i in tuple) { + tupleVal.Add((!)partitionToValue[i]); + } + arrayVal.Add(tupleVal); + } + vals.Add(arrayVal); + } else { + assert false; + } + } + return vals; + } + } + + public abstract class ProverInterface + { + public enum Outcome { Valid, Invalid, TimeOut, OutOfMemory, Undetermined } + + public class ErrorHandler + { + public virtual void OnModel(IList! labels, ErrorModel errModel) + { + } + + public virtual void OnResourceExceeded(string! message) + { + } + + public virtual void OnProverWarning(string! message) + modifies Console.Out.*, Console.Error.*; + { + 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: + assume false; // unexpected case + } + } + + + public virtual Absy! Label2Absy(string! label) + { + throw new System.NotImplementedException(); + } + } + + public abstract void BeginCheck(string! descriptiveName, VCExpr! vc, ErrorHandler! handler); + [NoDefaultContract] + public abstract Outcome CheckOutcome(ErrorHandler! handler); + throws UnexpectedProverOutputException; + + public virtual void LogComment(string! comment) { + } + + public virtual void Close() { + } + + /// + /// 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) {} + public virtual string! VCExpressionToString(VCExpr! vc) { return ""; } + public virtual void Pop() + throws UnexpectedProverOutputException; + {} + public virtual int NumAxiomsPushed() + { return 0; } + public virtual int FlushAxiomsToTheoremProver() + throws UnexpectedProverOutputException; + { return 0; } + + + public abstract ProverContext! Context { get; } + public abstract VCExpressionGenerator! VCExprGen { get; } + } + + public class ProverException : Exception + { + public ProverException(string s) : base(s) + { + } + } + public class UnexpectedProverOutputException : ProverException, ICheckedException + { + 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/Check.ssc b/Source/VCGeneration/Check.ssc deleted file mode 100644 index 4bd6edea..00000000 --- a/Source/VCGeneration/Check.ssc +++ /dev/null @@ -1,505 +0,0 @@ -//----------------------------------------------------------------------------- -// -// Copyright (C) Microsoft Corporation. All Rights Reserved. -// -//----------------------------------------------------------------------------- -using System; -using System.Collections; -using System.Collections.Generic; -using System.Threading; -using System.IO; -using System.Diagnostics; -using Microsoft.Contracts; -using Microsoft.Boogie.AbstractInterpretation; -using Microsoft.Boogie.VCExprAST; -using Microsoft.Basetypes; - -namespace Microsoft.Boogie -{ - /// - /// 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 - { - private readonly VCExpressionGenerator! gen; - - private ProverInterface! thmProver; - private CommandLineOptions.BvHandling bitvectors; - private int timeout; - - // state for the async interface - private volatile ProverInterface.Outcome outcome; - private volatile bool busy; - private volatile bool hasOutput; - private volatile UnexpectedProverOutputException outputExn; - private DateTime proverStart; - private TimeSpan proverRunTime; - private volatile ProverInterface.ErrorHandler handler; - private volatile bool closed; - - public readonly AutoResetEvent! ProverDone = new AutoResetEvent(false); - - private static CommandLineOptions.BvHandling BvHandlingForImpl(Implementation impl) - { - if (impl == null) return CommandLineOptions.Clo.Bitvectors; - bool force_int = false; - bool force_native = false; - ((!)impl.Proc).CheckBooleanAttribute("forceBvInt", ref force_int); - impl.Proc.CheckBooleanAttribute("forceBvZ3Native", ref force_native); - impl.CheckBooleanAttribute("forceBvInt", ref force_int); - impl.CheckBooleanAttribute("forceBvZ3Native", ref force_native); - if (force_native) return CommandLineOptions.BvHandling.Z3Native; - if (force_int) return CommandLineOptions.BvHandling.ToInt; - return CommandLineOptions.Clo.Bitvectors; - } - - public bool WillingToHandle(Implementation impl, int timeout) - { - return !closed && timeout == this.timeout && bitvectors == BvHandlingForImpl(impl); - } - - public VCExpressionGenerator! VCExprGen - { - get { return this.gen; } - } - public ProverInterface! TheoremProver - { - get { return this.thmProver; } - } - - ///////////////////////////////////////////////////////////////////////////////// - // We share context information for the same program between different Checkers - - private struct ContextCacheKey { - public readonly Program! program; - public readonly CommandLineOptions.BvHandling bitvectors; - - public ContextCacheKey(Program! prog, - CommandLineOptions.BvHandling bitvectors) { - this.program = prog; - this.bitvectors = bitvectors; - } - - [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) && - this.bitvectors.Equals(thatKey.bitvectors); - } - return false; - } - - [Pure] - public override int GetHashCode() { - return this.program.GetHashCode() + this.bitvectors.GetHashCode(); - } - } - - ///////////////////////////////////////////////////////////////////////////////// - - /// - /// Constructor. Initialize a checker with the program and log file. - /// - public Checker(VC.ConditionGeneration! vcgen, Program! prog, string/*?*/ logFilePath, bool appendLogFile, Implementation impl, int timeout) - { - this.bitvectors = BvHandlingForImpl(impl); - this.timeout = timeout; - - ProverOptions! options = ((!)CommandLineOptions.Clo.TheProverFactory).BlankProverOptions(); - - if (logFilePath != null) { - options.LogFilename = logFilePath; - if (appendLogFile) - options.AppendLogFile = appendLogFile; - } - - options.Parse(CommandLineOptions.Clo.ProverOptions); - - if (timeout > 0) { - options.TimeLimit = timeout * 1000; - } - options.BitVectors = this.bitvectors; - - ContextCacheKey key = new ContextCacheKey (prog, this.bitvectors); - ProverContext ctx; - ProverInterface prover; - - if (vcgen.CheckerCommonState == null) { - vcgen.CheckerCommonState = new Dictionary (); - } - IDictionary! cachedContexts = (IDictionary) vcgen.CheckerCommonState; - - if (cachedContexts.TryGetValue(key, out ctx)) { - ctx = (ProverContext!)((!)ctx).Clone(); - prover = (ProverInterface) - CommandLineOptions.Clo.TheProverFactory.SpawnProver(options, ctx); - } else { - ctx = (ProverContext!)CommandLineOptions.Clo.TheProverFactory.NewProverContext(options); - - // set up the context - foreach (Declaration! decl in prog.TopLevelDeclarations) { - TypeCtorDecl t = decl as TypeCtorDecl; - if (t != null) { - ctx.DeclareType(t, null); - } - } - foreach (Declaration! decl in prog.TopLevelDeclarations) { - 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 (Declaration! decl in prog.TopLevelDeclarations) { - bool expand = false; - Axiom ax = decl as Axiom; - decl.CheckBooleanAttribute("inline", ref expand); - if (!expand && ax != null) { - ctx.AddAxiom(ax, null); - } - } - foreach (Declaration! decl in prog.TopLevelDeclarations) { - GlobalVariable v = decl as GlobalVariable; - if (v != null) { - ctx.DeclareGlobalVariable(v, null); - } - } - - // 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, (ProverContext!)ctx.Clone()); - } - - this.thmProver = prover; - this.gen = prover.VCExprGen; - - // base(); - } - - - /// - /// Clean-up. - /// - public void Close() - { - this.closed = true; - thmProver.Close(); - } - - /// - /// Push a Verification Condition as an Axiom - /// (Required for Doomed Program Point detection) - /// - public void PushVCExpr(VCExpr! vc) - { - //thmProver.Context.AddAxiom(vc); - thmProver.PushVCExpression(vc); - } - - public bool IsBusy - { - get { return busy; } - } - - public bool Closed - { - get { return closed; } - } - - public bool HasOutput - { - get { return hasOutput; } - } - - public TimeSpan ProverRunTime - { - get { return proverRunTime; } - } - - private void WaitForOutput(object dummy) - { - try { - outcome = thmProver.CheckOutcome((!)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; - } - - assert busy; - hasOutput = true; - proverRunTime = DateTime.Now - proverStart; - - ProverDone.Set(); - } - - public void BeginCheck(string! descriptiveName, VCExpr! vc, ProverInterface.ErrorHandler! handler) - requires !IsBusy; - { - assert !busy; - busy = true; - hasOutput = false; - outputExn = null; - this.handler = handler; - - proverStart = DateTime.Now; - thmProver.BeginCheck(descriptiveName, vc, handler); - // gen.ClearSharedFormulas(); PR: don't know yet what to do with this guy - - ThreadPool.QueueUserWorkItem(WaitForOutput); - } - - public ProverInterface.Outcome ReadOutcome() - throws UnexpectedProverOutputException; - requires IsBusy; - requires HasOutput; - { - hasOutput = false; - busy = false; - - if (outputExn != null) { - throw outputExn; - } - - return outcome; - } - } - - // ----------------------------------------------------------------------------------------------- - // ----------------------------------------------------------------------------------------------- - // ----------------------------------------------------------------------------------------------- - - public class ErrorModel { - public Dictionary! identifierToPartition; - public List>! partitionToIdentifiers; - public List! partitionToValue; - public Dictionary! valueToPartition; - public Dictionary>!>! definedFunctions; - - public ErrorModel(Dictionary! identifierToPartition, List>! partitionToIdentifiers, List! partitionToValue, Dictionary! valueToPartition, Dictionary>!>! definedFunctions) { - this.identifierToPartition = identifierToPartition; - this.partitionToIdentifiers = partitionToIdentifiers; - this.partitionToValue = partitionToValue; - this.valueToPartition = valueToPartition; - this.definedFunctions = definedFunctions; - } - - public virtual void Print(TextWriter! writer) { } - - public int LookupPartitionValue(int partition) - { - BigNum bignum = (BigNum) (!)partitionToValue[partition]; - return bignum.ToInt; - } - - public int LookupControlFlowFunctionAt(int cfc, int id) - { - List>! tuples = this.definedFunctions["ControlFlow"]; - foreach (List tuple in tuples) - { - if (tuple == null) continue; - if (tuple.Count != 3) continue; - if (LookupPartitionValue(tuple[0]) == cfc && LookupPartitionValue(tuple[1]) == id) - return LookupPartitionValue(tuple[2]); - } - assert false; - return 0; - } - - private string! LookupSkolemConstant(string! name) { - foreach (string! functionName in identifierToPartition.Keys) - { - int index = functionName.LastIndexOf("!"); - if (index == -1) continue; - string! newFunctionName = (!)functionName.Remove(index); - if (newFunctionName.Equals(name)) - return functionName; - } - return ""; - } - private string! LookupSkolemFunction(string! name) { - foreach (string! functionName in definedFunctions.Keys) - { - int index = functionName.LastIndexOf("!"); - if (index == -1) continue; - string! newFunctionName = (!)functionName.Remove(index); - if (newFunctionName.Equals(name)) - return functionName; - } - return ""; - } - public int LookupSkolemFunctionAt(string! functionName, List! values) - { - string! actualFunctionName = LookupSkolemFunction(functionName); - if (actualFunctionName.Equals("")) - { - // The skolem function is actually a skolem constant - actualFunctionName = LookupSkolemConstant(functionName); - assert !actualFunctionName.Equals(""); - return identifierToPartition[actualFunctionName]; - } - List>! tuples = this.definedFunctions[actualFunctionName]; - assert tuples.Count > 0; - // the last tuple is a dummy tuple - for (int n = 0; n < tuples.Count - 1; n++) - { - List! tuple = (!)tuples[n]; - assert tuple.Count - 1 <= values.Count; - for (int i = 0, j = 0; i < values.Count; i++) - { - if (values[i] == tuple[j]) { - // succeeded in matching tuple[j] - j++; - if (j == tuple.Count-1) return tuple[tuple.Count - 1]; - } - } - } - assert false; - return 0; - } - - public List! PartitionsToValues(List! args) - { - List! vals = new List(); - foreach(int i in args) - { - object! o = (!)partitionToValue[i]; - if (o is bool) { - vals.Add(o); - } else if (o is BigNum) { - vals.Add(o); - } else if (o is List!>) { - List!> array = (List!>) o; - List!> arrayVal = new List!>(); - foreach (List! tuple in array) { - List tupleVal = new List(); - foreach (int i in tuple) { - tupleVal.Add((!)partitionToValue[i]); - } - arrayVal.Add(tupleVal); - } - vals.Add(arrayVal); - } else { - assert false; - } - } - return vals; - } - } - - public abstract class ProverInterface - { - public enum Outcome { Valid, Invalid, TimeOut, OutOfMemory, Undetermined } - - public class ErrorHandler - { - public virtual void OnModel(IList! labels, ErrorModel errModel) - { - } - - public virtual void OnResourceExceeded(string! message) - { - } - - public virtual void OnProverWarning(string! message) - modifies Console.Out.*, Console.Error.*; - { - 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: - assume false; // unexpected case - } - } - - - public virtual Absy! Label2Absy(string! label) - { - throw new System.NotImplementedException(); - } - } - - public abstract void BeginCheck(string! descriptiveName, VCExpr! vc, ErrorHandler! handler); - [NoDefaultContract] - public abstract Outcome CheckOutcome(ErrorHandler! handler); - throws UnexpectedProverOutputException; - - public virtual void LogComment(string! comment) { - } - - public virtual void Close() { - } - - /// - /// 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) {} - public virtual string! VCExpressionToString(VCExpr! vc) { return ""; } - public virtual void Pop() - throws UnexpectedProverOutputException; - {} - public virtual int NumAxiomsPushed() - { return 0; } - public virtual int FlushAxiomsToTheoremProver() - throws UnexpectedProverOutputException; - { return 0; } - - - public abstract ProverContext! Context { get; } - public abstract VCExpressionGenerator! VCExprGen { get; } - } - - public class ProverException : Exception - { - public ProverException(string s) : base(s) - { - } - } - public class UnexpectedProverOutputException : ProverException, ICheckedException - { - 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 new file mode 100644 index 00000000..a2db980d --- /dev/null +++ b/Source/VCGeneration/ConditionGeneration.cs @@ -0,0 +1,1178 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) Microsoft Corporation. All Rights Reserved. +// +//----------------------------------------------------------------------------- +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; +using System.IO; +using Microsoft.Boogie; +using Graphing; +using AI = Microsoft.AbstractInterpretationFramework; +using Microsoft.Contracts; +using Microsoft.Basetypes; +using Microsoft.Boogie.VCExprAST; + +namespace Microsoft.Boogie +{ + public class CalleeCounterexampleInfo { + public Counterexample! counterexample; + public List! args; + + public CalleeCounterexampleInfo(Counterexample! cex, List! x) { + counterexample = cex; + args = x; + } + } + + public abstract class Counterexample + { + [Peer] public BlockSeq! Trace; + [Peer] public List! relatedInformation; + + public Dictionary! calleeCounterexamples; + + internal Counterexample(BlockSeq! trace) + { + this.Trace = trace; + this.relatedInformation = new List(); + this.calleeCounterexamples = new Dictionary(); + // base(); + } + + public void AddCalleeCounterexample(Absy! absy, CalleeCounterexampleInfo! cex) + { + calleeCounterexamples[absy] = cex; + } + + public void AddCalleeCounterexample(Dictionary! cs) + { + foreach (Absy! absy in cs.Keys) + { + AddCalleeCounterexample(absy, cs[absy]); + } + } + + public void Print(int spaces) + { + foreach (Block! b in Trace) { + if (b.tok == null) { + Console.WriteLine(" "); + } 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)) { + for (int i = 0; i < spaces+4; i++) Console.Write(" "); + Console.WriteLine("{0}({1},{2}): {3}", b.tok.filename, b.tok.line, b.tok.col, b.Label); + foreach (Cmd! cmd in b.Cmds) { + if (calleeCounterexamples.ContainsKey(cmd)) { + AssumeCmd! assumeCmd = (AssumeCmd!) cmd; + NAryExpr! naryExpr = (NAryExpr!) assumeCmd.Expr; + for (int i = 0; i < spaces+4; i++) Console.Write(" "); + Console.WriteLine("Inlined call to procedure {0} begins", naryExpr.Fun.FunctionName); + calleeCounterexamples[cmd].counterexample.Print(spaces+4); + for (int i = 0; i < spaces+4; i++) Console.Write(" "); + Console.WriteLine("Inlined call to procedure {0} ends", naryExpr.Fun.FunctionName); + } + } + } + } + } + } + + public abstract int GetLocation(); + } + + public class CounterexampleComparer : IComparer + { + public int Compare(Counterexample! c1, Counterexample! c2) { + if (c1.GetLocation() == c2.GetLocation()) return 0; + if (c1.GetLocation() > c2.GetLocation()) return 1; + return -1; + } + } + + public class AssertCounterexample : Counterexample + { + [Peer] public AssertCmd! FailingAssert; + + public AssertCounterexample(BlockSeq! trace, AssertCmd! failingAssert) + : base(trace) + { + this.FailingAssert = failingAssert; + // base(trace); + } + + public override int GetLocation() { + return FailingAssert.tok.line * 1000 + FailingAssert.tok.col; + } + } + + public class CallCounterexample : Counterexample + { + public CallCmd! FailingCall; + public Requires! FailingRequires; + + public CallCounterexample(BlockSeq! trace, CallCmd! failingCall, Requires! failingRequires) + : base(trace) + requires !failingRequires.Free; + { + this.FailingCall = failingCall; + this.FailingRequires = failingRequires; + // base(trace); + } + + public override int GetLocation() { + return FailingCall.tok.line * 1000 + FailingCall.tok.col; + } + } + + public class ReturnCounterexample : Counterexample + { + public TransferCmd! FailingReturn; + public Ensures! FailingEnsures; + + public ReturnCounterexample(BlockSeq! trace, TransferCmd! failingReturn, Ensures! failingEnsures) + : base(trace) + requires !failingEnsures.Free; + { + this.FailingReturn = failingReturn; + this.FailingEnsures = failingEnsures; + // base(trace); + } + + public override int GetLocation() { + return FailingReturn.tok.line * 1000 + FailingReturn.tok.col; + } + } + + 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) + { + } + + // called in case resource is exceeded and we don't have counterexample + public virtual void OnTimeout(string! reason) + { + } + + public virtual void OnOutOfMemory(string! reason) + { + } + + public virtual void OnProgress(string phase, int step, int totalSteps, double progressEstimate) + { + } + + public virtual void OnUnreachableCode(Implementation! impl) + { + } + + public virtual void OnWarning(string! msg) + { + 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: + assume false; // unexpected case + } + } + } +} + +//////////////////////////////////////////// + +namespace VC +{ + using Bpl = Microsoft.Boogie; + + public class VCGenException : Exception + { + public VCGenException(string s) : base(s) + { + } + } + + public abstract class ConditionGeneration + { + protected internal object CheckerCommonState; + + public enum Outcome { Correct, Errors, TimedOut, OutOfMemory, Inconclusive } + + protected readonly List! checkers = new List(); + protected Implementation current_impl = null; + + // shared across each implementation; created anew for each implementation + protected Hashtable /*Variable -> int*/ variable2SequenceNumber; + public Dictionary! incarnationOriginMap = new Dictionary(); + + // used only by FindCheckerFor + public Program! program; + protected string/*?*/ logFilePath; + protected bool appendLogFile; + + public ConditionGeneration(Program! p) + { + program = p; + } + + /// + /// 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, Program! program, out List? errors) + ensures result == Outcome.Errors ==> errors != null; + throws UnexpectedProverOutputException; + { + Helpers.ExtraTraceInformation("Starting implementation verification"); + + CounterexampleCollector collector = new CounterexampleCollector(); + Outcome outcome = VerifyImplementation(impl, program, collector); + if (outcome == Outcome.Errors) { + errors = collector.examples; + } else { + errors = null; + } + + Helpers.ExtraTraceInformation("Finished implementation verification"); + return outcome; + } + + public Outcome StratifiedVerifyImplementation(Implementation! impl, Program! program, out List? errors) + ensures result == Outcome.Errors ==> errors != null; + throws UnexpectedProverOutputException; + { + Helpers.ExtraTraceInformation("Starting implementation verification"); + + CounterexampleCollector collector = new CounterexampleCollector(); + Outcome outcome = StratifiedVerifyImplementation(impl, program, collector); + if (outcome == Outcome.Errors) { + errors = collector.examples; + } else { + errors = null; + } + + Helpers.ExtraTraceInformation("Finished implementation verification"); + return outcome; + } + + public abstract Outcome VerifyImplementation(Implementation! impl, Program! program, VerifierCallback! callback) + throws UnexpectedProverOutputException; + + public virtual Outcome StratifiedVerifyImplementation(Implementation! impl, Program! program, VerifierCallback! callback) + throws UnexpectedProverOutputException; + { + return VerifyImplementation(impl, program, callback); + } + +/////////////////////////////////// Common Methods and Classes ////////////////////////////////////////// + +#region Methods for injecting pre- and postconditions + private static void + ThreadInBlockExpr(Implementation! impl, + Block! targetBlock, + BlockExpr! blockExpr, + bool replaceWithAssert, + TokenTextWriter debugWriter){ + // Go through blockExpr 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 blockExpr to the implementation (at the end) + foreach (Block! b in blockExpr.Blocks){ + 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); + Cmd! c = new AssertEnsuresCmd(ens); + b.Cmds.Add(c); + }else{ + b.Cmds.Add(new AssumeCmd(rec.tok, rec.Expr)); + } + b.TransferCmd = new GotoCmd(Token.NoToken, + new StringSeq(targetBlock.Label), + new BlockSeq(targetBlock)); + targetBlock.Predecessors.Add(b); + } + impl.Blocks.Add(b); + } + if (debugWriter != null){ + blockExpr.Emit(debugWriter, 1,false); + } + return; + } + + private static void AddAsPrefix(Block! b, CmdSeq! cs){ + CmdSeq newCommands = new CmdSeq(); + 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] CmdSeq! startCmds) + requires impl.Proc != null; + { + TokenTextWriter debugWriter = null; + if (CommandLineOptions.Clo.PrintWithUniqueASTIds) { + debugWriter = new TokenTextWriter("", Console.Out, false); + debugWriter.WriteLine("Effective precondition:"); + } + + Substitution formalProcImplSubst = Substituter.SubstitutionFromHashtable(impl.GetImplFormalMap()); + string LabelPrefix = "PreconditionGeneratedEntry"; + int k = 0; + + Block origStartBlock = impl.Blocks[0]; + Block insertionPoint = new Block( + new Token(-17, -4), LabelPrefix + k, startCmds, + new GotoCmd(Token.NoToken, new StringSeq(origStartBlock.Label), new BlockSeq(origStartBlock))); + k++; + 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 + { + Expr e = Substituter.Apply(formalProcImplSubst, req.Condition); + BlockExpr be = e as BlockExpr; + if (be == null) { + // This is the normal case, where the precondition is an ordinary expression + Cmd c = new AssumeCmd(req.tok, e); + insertionPoint.Cmds.Add(c); + if (debugWriter != null) { c.Emit(debugWriter, 1); } + } else { + // This is a BlockExpr, so append all of its blocks (changing return expressions + // to assume statements), make the insertion-point block goto the head block of the + // BlockExpr, and create a new empty block as the current insertion point. + // Here goes: First, create the new block, which will become the new insertion + // point and which will serve as a target for the BlockExpr. Move the goto's from + // the current insertion point to this new block. + Block nextIP = new Block(new Token(-17, -4), LabelPrefix + k, new CmdSeq(), insertionPoint.TransferCmd); + k++; + // Second, append the BlockExpr blocks to the implementation's blocks + ThreadInBlockExpr(impl, nextIP, be, false, debugWriter); + // Third, make the old insertion-point block goto the entry block of the BlockExpr + Block beEntry = (!)be.Blocks[0]; + insertionPoint.TransferCmd = new GotoCmd(Token.NoToken, new StringSeq(beEntry.Label), new BlockSeq(beEntry)); + beEntry.Predecessors.Add(insertionPoint); + // Fourth, update the insertion point + insertionPoint = nextIP; + } + } + origStartBlock.Predecessors.Add(insertionPoint); + + 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 Block! InjectPostConditions(Implementation! impl, Block! unifiedExitBlock, Hashtable/*TransferCmd->ReturnCmd*/! gotoCmdOrigins) + requires impl.Proc != null; + requires unifiedExitBlock.TransferCmd is ReturnCmd; + ensures result.TransferCmd is ReturnCmd; + { + TokenTextWriter debugWriter = null; + if (CommandLineOptions.Clo.PrintWithUniqueASTIds) { + debugWriter = new TokenTextWriter("", Console.Out, false); + debugWriter.WriteLine("Effective postcondition:"); + } + + Substitution formalProcImplSubst = Substituter.SubstitutionFromHashtable(impl.GetImplFormalMap()); + Block insertionPoint = unifiedExitBlock; + string LabelPrefix = "ReallyLastGeneratedExit"; + int k = 0; + + // (free and checked) ensures clauses + foreach (Ensures! ens in impl.Proc.Ensures) + invariant insertionPoint.TransferCmd is ReturnCmd; + { + if (!ens.Free) { // skip free ensures clauses + Expr e = Substituter.Apply(formalProcImplSubst, ens.Condition); + BlockExpr be = ens.Condition as BlockExpr; + if (be == null) { + // This is the normal case, where the postcondition is an ordinary expression + Ensures ensCopy = (Ensures!) ens.Clone(); + ensCopy.Condition = e; + AssertEnsuresCmd c = new AssertEnsuresCmd(ensCopy); + c.ErrorDataEnhanced = ensCopy.ErrorDataEnhanced; + insertionPoint.Cmds.Add(c); + if (debugWriter != null) { c.Emit(debugWriter, 1); } + } else { + // This is a BlockExpr, so append all of its blocks (changing return expressions + // to assert statements), insert a goto to its head block from the current insertion + // point, and create a new empty block as the current insertion point. + // Here goes: First, create the new block, which will become the new insertion + // point and which will serve as a target for the BlockExpr. Steal the TransferCmd + // from insertionPoint, since insertionPoint's TransferCmd will soon be replaced anyhow. + Block nextIP = new Block(new Token(-17, -4), LabelPrefix + k, new CmdSeq(), insertionPoint.TransferCmd); + k++; + // Second, append the BlockExpr blocks to the implementation's blocks + ThreadInBlockExpr(impl, nextIP, be, true, debugWriter); + // Third, make the old insertion-point block goto the entry block of the BlockExpr + Block beEntry = (!)be.Blocks[0]; + insertionPoint.TransferCmd = new GotoCmd(Token.NoToken, new StringSeq(beEntry.Label), new BlockSeq(beEntry)); + beEntry.Predecessors.Add(insertionPoint); + // Fourth, update the insertion point + insertionPoint = nextIP; + } + } + } + + if (debugWriter != null) { debugWriter.WriteLine(); } + return insertionPoint; + } + + + /// + /// Get the pre-condition of an implementation, including the where clauses from the in-parameters. + /// + /// + protected static CmdSeq! GetPre(Implementation! impl) + requires impl.Proc != null; + { + TokenTextWriter debugWriter = null; + if (CommandLineOptions.Clo.PrintWithUniqueASTIds) { + debugWriter = new TokenTextWriter("", Console.Out, false); + debugWriter.WriteLine("Effective precondition:"); + } + + Substitution formalProcImplSubst = Substituter.SubstitutionFromHashtable(impl.GetImplFormalMap()); + CmdSeq! pre = new CmdSeq(); + + // (free and checked) requires clauses + foreach (Requires! req in impl.Proc.Requires) + { + Expr! e = Substituter.Apply(formalProcImplSubst, req.Condition); + Cmd! c = new AssumeCmd(req.tok, e); + 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 CmdSeq! GetPost(Implementation! impl) + requires impl.Proc != null; + { + if (CommandLineOptions.Clo.PrintWithUniqueASTIds) { Console.WriteLine("Effective postcondition:"); } + + // Construct an Expr for the post-condition + Substitution formalProcImplSubst = Substituter.SubstitutionFromHashtable(impl.GetImplFormalMap()); + CmdSeq! post = new CmdSeq(); + foreach (Ensures! ens in impl.Proc.Ensures) + { + if (!ens.Free) { + Expr! e = Substituter.Apply(formalProcImplSubst, ens.Condition); + Ensures! ensCopy = (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, 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 CmdSeq! GetParamWhereClauses(Implementation! impl) + requires impl.Proc != null; + { + TokenTextWriter debugWriter = null; + if (CommandLineOptions.Clo.PrintWithUniqueASTIds) { + debugWriter = new TokenTextWriter("", Console.Out, false); + debugWriter.WriteLine("Effective precondition from where-clauses:"); + } + + Substitution formalProcImplSubst = Substituter.SubstitutionFromHashtable(impl.GetImplFormalMap()); + CmdSeq! whereClauses = new CmdSeq(); + + // where clauses of in-parameters + foreach (Formal! f in impl.Proc.InParams) + { + 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 + assert impl.OutParams.Length == impl.Proc.OutParams.Length; + for (int i = 0; i < impl.OutParams.Length; i++) { + Variable f = (!)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 = (!)impl.OutParams[i]; + assume fi.TypedIdent.WhereExpr == null; + fi.TypedIdent.WhereExpr = e; + + if (debugWriter != null) { c.Emit(debugWriter, 1); } + } + } + + if (debugWriter != null) { debugWriter.WriteLine(); } + + return whereClauses; + } + +#endregion + + + protected Checker! FindCheckerFor(Implementation impl, int timeout) + { + int i = 0; + while (i < checkers.Count) { + if (checkers[i].Closed) { + checkers.RemoveAt(i); + continue; + } else { + if (!checkers[i].IsBusy && checkers[i].WillingToHandle(impl, timeout)) return checkers[i]; + } + ++i; + } + string? log = logFilePath; + if (log != null && !log.Contains("@PROC@") && checkers.Count > 0) + log = log + "." + checkers.Count; + Checker! ch = new Checker(this, program, log, appendLogFile, impl, timeout); + checkers.Add(ch); + return ch; + } + + + public void Close() { foreach (Checker! checker in checkers) checker.Close(); } + + + protected class CounterexampleCollector : VerifierCallback + { + public readonly List! examples = new List(); + public override void OnCounterexample(Counterexample! ce, string? reason) + { + examples.Add(ce); + } + + public override void OnUnreachableCode(Implementation! impl) + { + 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) { + 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, false), 0); + CommandLineOptions.Clo.PrintDesugarings = oldPrintDesugaringSetting; + CommandLineOptions.Clo.PrintUnstructured = oldPrintUnstructured; + } + + + protected Block! GenerateUnifiedExit(Implementation! impl, Hashtable! gotoCmdOrigins) + ensures 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 CmdSeq(),new ReturnCmd(Token.NoToken)); + + foreach ( Block b in impl.Blocks ) + { + if ( b.TransferCmd is ReturnCmd ) + { + StringSeq labels = new StringSeq(); + labels.Add(unifiedExitLabel); + BlockSeq bs = new BlockSeq(); + bs.Add(unifiedExit); + GotoCmd go = new GotoCmd(Token.NoToken,labels,bs); + gotoCmdOrigins[go] = b.TransferCmd; + b.TransferCmd = go; + unifiedExit.Predecessors.Add(b); + } + } + + exitBlock = unifiedExit; + impl.Blocks.Add(unifiedExit); + } + assert exitBlock != null; + } + return exitBlock; + #endregion + } + + /// + /// Helperfunction to restore the predecessor relations after loop unrolling + /// + protected void ComputePredecessors(List! blocks) + { + #region Compute and store the Predecessor Relation on the blocks + // This code just here to try things out. + // Compute the predecessor relation for each block + // Store it in the Predecessors field within each block + foreach (Block b in blocks) + { + GotoCmd gtc = b.TransferCmd as GotoCmd; + if (gtc != null) + { + assume gtc.labelTargets != null; + foreach (Block! dest in gtc.labelTargets) + { + dest.Predecessors.Add(b); + } + } + } + #endregion Compute and store the Predecessor Relation on the blocks + } + + protected static void ResetPredecessors(List! blocks) + { + foreach (Block! b in blocks) { + b.Predecessors = new BlockSeq(); + } + foreach (Block! b in blocks) { + foreach (Block! ch in Exits(b)) { + ch.Predecessors.Add(b); + } + } + } + + protected static IEnumerable! Exits(Block! b) + { + GotoCmd g = b.TransferCmd as GotoCmd; + if (g != null) { + return (!)g.labelTargets; + } + return new List(); + } + + protected Variable! CreateIncarnation(Variable! x, Absy a) + requires this.variable2SequenceNumber != null; + requires this.current_impl != null; + requires a is Block || a is AssignCmd || a is HavocCmd; + { + int currentIncarnationNumber = + variable2SequenceNumber.ContainsKey(x) + ? + (int) ((!)variable2SequenceNumber[x]) + : + -1; + Variable v = new Incarnation(x,currentIncarnationNumber + 1); + variable2SequenceNumber[x] = currentIncarnationNumber + 1; + assert current_impl != null; // otherwise, the field current_impl wasn't set + current_impl.LocVars.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 Hashtable! /*Variable -> Expr*/ ComputeIncarnationMap(Block! b, Hashtable! /*Variable -> Expr*/ block2Incarnation) + { + if (b.Predecessors.Length == 0) + { + return new Hashtable(); + } + + Hashtable /*Variable -> Expr*/ incarnationMap = null; + Set /*Variable*/ fixUps = new Set /*Variable*/ (); + foreach (Block! pred in b.Predecessors) + { + assert block2Incarnation.Contains(pred); // otherwise, Passive Transformation found a block whose predecessors have not been processed yet + Hashtable /*Variable -> Expr*/ predMap = (Hashtable! /*Variable -> Expr*/) block2Incarnation[pred]; + if (incarnationMap == null) + { + incarnationMap = (Hashtable /*Variable -> Expr*/)predMap.Clone(); + continue; + } + + ArrayList /*Variable*/ conflicts = new ArrayList /*Variable*/ (); + foreach (Variable! v in incarnationMap.Keys) + { + if (!predMap.Contains(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) + { + incarnationMap.Remove(v); + } + foreach (Variable! v in predMap.Keys) + { + if (!incarnationMap.Contains(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) + { + if (!b.IsLive(v)) continue; + Variable v_prime = CreateIncarnation(v, b); + IdentifierExpr ie = new IdentifierExpr(v_prime.tok, v_prime); + assert incarnationMap != null; + incarnationMap[v] = ie; + foreach (Block! pred in b.Predecessors ) + { + #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 + Hashtable /*Variable -> Expr*/ predMap = (Hashtable! /*Variable -> Expr*/) block2Incarnation[pred]; + Expr pred_incarnation_exp; + Expr o = (Expr) predMap[v]; + 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 + Expr e = Expr.Binary(Token.NoToken, + BinaryOperator.Opcode.Eq, + v_prime_exp, + pred_incarnation_exp + ); + AssumeCmd ac = new AssumeCmd(v.tok,e); + pred.Cmds.Add(ac); + #endregion + #endregion + } + } + #endregion + + assert incarnationMap != null; + return incarnationMap; + } + + Hashtable 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, Hashtable! /*Variable -> Expr*/ incarnationMap, Substitution! oldFrameSubst) + { + #region Walk forward over the commands in this block and convert them to passive commands + + CmdSeq passiveCmds = new CmdSeq(); + foreach (Cmd! c in b.Cmds) + { // walk forward over the commands because the map gets modified in a forward direction + TurnIntoPassiveCmd(c, incarnationMap, oldFrameSubst, passiveCmds); + } + b.Cmds = passiveCmds; + + #endregion + } + + protected Hashtable /*Variable -> Expr*/ Convert2PassiveCmd(Implementation! impl) + { + #region Convert to Passive Commands + + #region Topological sort -- need to process in a linearization of the partial order + Graph dag = new Graph(); + dag.AddSource((!)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) + { + assume gtc.labelTargets != null; + foreach (Block! dest in gtc.labelTargets) + { + dag.AddEdge(b,dest); + } + } + } + IEnumerable! sortedNodes = dag.TopologicalSort(); + // assume sortedNodes != null; + #endregion + + // Create substitution for old expressions + Hashtable/*Variable!->Expr!*/ oldFrameMap = new Hashtable(); + assume impl.Proc != null; + foreach (IdentifierExpr! ie in impl.Proc.Modifies) { + if (!oldFrameMap.Contains((!)ie.Decl)) + oldFrameMap.Add(ie.Decl, ie); + } + Substitution oldFrameSubst = Substituter.SubstitutionFromHashtable(oldFrameMap); + + // 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. + Hashtable /*Block -> IncarnationMap*/ block2Incarnation = new Hashtable/*Block -> IncarnationMap*/(); + Block exitBlock = null; + foreach (Block! b in sortedNodes ) + { + assert !block2Incarnation.Contains(b); + Hashtable /*Variable -> Expr*/ incarnationMap = ComputeIncarnationMap(b, block2Incarnation); + + #region Each block's map needs to be available to successor blocks + block2Incarnation.Add(b,incarnationMap); + #endregion Each block's map needs to be available to successor blocks + + TurnIntoPassiveBlock(b, incarnationMap, oldFrameSubst); + exitBlock = b; + } + + // Verify that exitBlock is indeed the unique exit block + assert exitBlock != null; + assert exitBlock.TransferCmd is ReturnCmd; + + // 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) { + f.TypedIdent.WhereExpr = null; + } + #endregion Convert to Passive Commands + + #region Debug Tracing + if (CommandLineOptions.Clo.TraceVerify) + { + Console.WriteLine("after conversion to passive commands"); + EmitImpl(impl, true); + } + #endregion + + return (Hashtable) block2Incarnation[exitBlock]; + } + + /// + /// 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 remebers the incarnation map BEFORE the havoc + /// + protected void TurnIntoPassiveCmd(Cmd! c, Hashtable /*Variable -> Expr*/! incarnationMap, Substitution! oldFrameSubst, CmdSeq! passiveCmds) + { + Substitution incarnationSubst = Substituter.SubstitutionFromHashtable(incarnationMap); + #region assert/assume P |--> assert/assume P[x := in(x)], out := in + if ( c is PredicateCmd ) + { + assert c is AssertCmd || c is AssumeCmd; // otherwise, unexpected PredicateCmd type + + PredicateCmd! pc = (PredicateCmd) c.Clone(); + + Expr! copy = Substituter.ApplyReplacingOldExprs(incarnationSubst, oldFrameSubst, pc.Expr); + if (pc is AssertCmd) { + ((AssertCmd) pc).OrigExpr = pc.Expr; + assert ((AssertCmd) pc).IncarnationMap == null; + ((AssertCmd) pc).IncarnationMap = (Hashtable /*Variable -> Expr*/!) incarnationMap.Clone(); + } + pc.Expr = copy; + 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 + #region Substitute all variables in E with the current map + List copies = new List (); + foreach (Expr! e in assign.Rhss) + 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 = + ((SimpleAssignLhs!)assign.Lhss[i]).AssignedVariable; + Variable! lhs = (!)lhsIdExpr.Decl; + Expr! rhs = assign.Rhss[i]; + + // 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((!)ie.Decl) ) + newIncarnationMappings[lhs] = (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 + #region Create an assume command with the new variable + assumptions.Add(Expr.Eq(x_prime_exp, copies[i])); + #endregion + } + } + + foreach (KeyValuePair pair in newIncarnationMappings) + incarnationMap[pair.Key] = pair.Value; + + if (assumptions.Count > 0) { + Expr! assumption = assumptions[0]; + for (int i = 1; i < assumptions.Count; ++i) + assumption = Expr.And(assumption, assumptions[i]); + passiveCmds.Add(new AssumeCmd(c.tok, assumption)); + } + } + #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 = (Hashtable) incarnationMap.Clone(); + + HavocCmd! hc = (HavocCmd) c; + IdentifierExprSeq havocVars = hc.Vars; + // First, compute the new incarnations + foreach (IdentifierExpr! ie in havocVars) + { + if ( !(ie.Decl is Incarnation) ) + { + Variable x = (!)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) + { + if ( !(ie.Decl is Incarnation) ) + { + Variable x = (!)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)); + } + } + } + } + #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; + Cmd! cmd = sug.Desugaring; + TurnIntoPassiveCmd(cmd, incarnationMap, oldFrameSubst, passiveCmds); + } + else if (c is StateCmd) + { + this.preHavocIncarnationMap = null; // we do not need to remeber the previous incarnations + StateCmd! st = (StateCmd)c; + // account for any where clauses among the local variables + foreach (Variable! v in st.Locals) { + 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) { + TurnIntoPassiveCmd(s, incarnationMap, oldFrameSubst, passiveCmds); + } + // remove the local variables from the incarnation map + foreach (Variable! v in st.Locals) { + 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 rember 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 + } + + /// + /// 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) + requires 0 <= predIndex && predIndex < succ.Predecessors.Length; + { + Block! pred = (!) succ.Predecessors[predIndex]; + + string! newBlockLabel = pred.Label + "_@2_" + succ.Label; + + // successor of newBlock list + StringSeq ls = new StringSeq(); + ls.Add(succ.Label); + BlockSeq bs = new BlockSeq(); + bs.Add(succ); + + Block newBlock = new Block( + new Token(-17, -4), + newBlockLabel, + new CmdSeq(), + new GotoCmd(Token.NoToken,ls,bs) + ); + + // predecessors of newBlock + BlockSeq ps = new BlockSeq(); + ps.Add(pred); + newBlock.Predecessors = ps; + + // fix successors of pred + #region Change the edge "pred->succ" to "pred->newBlock" + GotoCmd gtc = (GotoCmd!) pred.TransferCmd; + assume gtc.labelTargets != null; + assume gtc.labelNames != null; + for ( int i = 0, n = gtc.labelTargets.Length; 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(Implementation! impl) + { + #region Introduce empty blocks between join points and their multi-successor predecessors + List tweens = new List(); + foreach ( Block b in impl.Blocks ) + { + int nPreds = b.Predecessors.Length; + 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!)((!)b.Predecessors[i]).TransferCmd; + if (gotocmd.labelNames != null && gotocmd.labelNames.Length > 1) + { + tweens.Add(CreateBlockBetween(i, b)); + } + } + } + } + impl.Blocks.AddRange(tweens); // must wait until iteration is done before changing the list + #endregion + } + + } +} \ No newline at end of file diff --git a/Source/VCGeneration/ConditionGeneration.ssc b/Source/VCGeneration/ConditionGeneration.ssc deleted file mode 100644 index a2db980d..00000000 --- a/Source/VCGeneration/ConditionGeneration.ssc +++ /dev/null @@ -1,1178 +0,0 @@ -//----------------------------------------------------------------------------- -// -// Copyright (C) Microsoft Corporation. All Rights Reserved. -// -//----------------------------------------------------------------------------- -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.Threading; -using System.IO; -using Microsoft.Boogie; -using Graphing; -using AI = Microsoft.AbstractInterpretationFramework; -using Microsoft.Contracts; -using Microsoft.Basetypes; -using Microsoft.Boogie.VCExprAST; - -namespace Microsoft.Boogie -{ - public class CalleeCounterexampleInfo { - public Counterexample! counterexample; - public List! args; - - public CalleeCounterexampleInfo(Counterexample! cex, List! x) { - counterexample = cex; - args = x; - } - } - - public abstract class Counterexample - { - [Peer] public BlockSeq! Trace; - [Peer] public List! relatedInformation; - - public Dictionary! calleeCounterexamples; - - internal Counterexample(BlockSeq! trace) - { - this.Trace = trace; - this.relatedInformation = new List(); - this.calleeCounterexamples = new Dictionary(); - // base(); - } - - public void AddCalleeCounterexample(Absy! absy, CalleeCounterexampleInfo! cex) - { - calleeCounterexamples[absy] = cex; - } - - public void AddCalleeCounterexample(Dictionary! cs) - { - foreach (Absy! absy in cs.Keys) - { - AddCalleeCounterexample(absy, cs[absy]); - } - } - - public void Print(int spaces) - { - foreach (Block! b in Trace) { - if (b.tok == null) { - Console.WriteLine(" "); - } 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)) { - for (int i = 0; i < spaces+4; i++) Console.Write(" "); - Console.WriteLine("{0}({1},{2}): {3}", b.tok.filename, b.tok.line, b.tok.col, b.Label); - foreach (Cmd! cmd in b.Cmds) { - if (calleeCounterexamples.ContainsKey(cmd)) { - AssumeCmd! assumeCmd = (AssumeCmd!) cmd; - NAryExpr! naryExpr = (NAryExpr!) assumeCmd.Expr; - for (int i = 0; i < spaces+4; i++) Console.Write(" "); - Console.WriteLine("Inlined call to procedure {0} begins", naryExpr.Fun.FunctionName); - calleeCounterexamples[cmd].counterexample.Print(spaces+4); - for (int i = 0; i < spaces+4; i++) Console.Write(" "); - Console.WriteLine("Inlined call to procedure {0} ends", naryExpr.Fun.FunctionName); - } - } - } - } - } - } - - public abstract int GetLocation(); - } - - public class CounterexampleComparer : IComparer - { - public int Compare(Counterexample! c1, Counterexample! c2) { - if (c1.GetLocation() == c2.GetLocation()) return 0; - if (c1.GetLocation() > c2.GetLocation()) return 1; - return -1; - } - } - - public class AssertCounterexample : Counterexample - { - [Peer] public AssertCmd! FailingAssert; - - public AssertCounterexample(BlockSeq! trace, AssertCmd! failingAssert) - : base(trace) - { - this.FailingAssert = failingAssert; - // base(trace); - } - - public override int GetLocation() { - return FailingAssert.tok.line * 1000 + FailingAssert.tok.col; - } - } - - public class CallCounterexample : Counterexample - { - public CallCmd! FailingCall; - public Requires! FailingRequires; - - public CallCounterexample(BlockSeq! trace, CallCmd! failingCall, Requires! failingRequires) - : base(trace) - requires !failingRequires.Free; - { - this.FailingCall = failingCall; - this.FailingRequires = failingRequires; - // base(trace); - } - - public override int GetLocation() { - return FailingCall.tok.line * 1000 + FailingCall.tok.col; - } - } - - public class ReturnCounterexample : Counterexample - { - public TransferCmd! FailingReturn; - public Ensures! FailingEnsures; - - public ReturnCounterexample(BlockSeq! trace, TransferCmd! failingReturn, Ensures! failingEnsures) - : base(trace) - requires !failingEnsures.Free; - { - this.FailingReturn = failingReturn; - this.FailingEnsures = failingEnsures; - // base(trace); - } - - public override int GetLocation() { - return FailingReturn.tok.line * 1000 + FailingReturn.tok.col; - } - } - - 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) - { - } - - // called in case resource is exceeded and we don't have counterexample - public virtual void OnTimeout(string! reason) - { - } - - public virtual void OnOutOfMemory(string! reason) - { - } - - public virtual void OnProgress(string phase, int step, int totalSteps, double progressEstimate) - { - } - - public virtual void OnUnreachableCode(Implementation! impl) - { - } - - public virtual void OnWarning(string! msg) - { - 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: - assume false; // unexpected case - } - } - } -} - -//////////////////////////////////////////// - -namespace VC -{ - using Bpl = Microsoft.Boogie; - - public class VCGenException : Exception - { - public VCGenException(string s) : base(s) - { - } - } - - public abstract class ConditionGeneration - { - protected internal object CheckerCommonState; - - public enum Outcome { Correct, Errors, TimedOut, OutOfMemory, Inconclusive } - - protected readonly List! checkers = new List(); - protected Implementation current_impl = null; - - // shared across each implementation; created anew for each implementation - protected Hashtable /*Variable -> int*/ variable2SequenceNumber; - public Dictionary! incarnationOriginMap = new Dictionary(); - - // used only by FindCheckerFor - public Program! program; - protected string/*?*/ logFilePath; - protected bool appendLogFile; - - public ConditionGeneration(Program! p) - { - program = p; - } - - /// - /// 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, Program! program, out List? errors) - ensures result == Outcome.Errors ==> errors != null; - throws UnexpectedProverOutputException; - { - Helpers.ExtraTraceInformation("Starting implementation verification"); - - CounterexampleCollector collector = new CounterexampleCollector(); - Outcome outcome = VerifyImplementation(impl, program, collector); - if (outcome == Outcome.Errors) { - errors = collector.examples; - } else { - errors = null; - } - - Helpers.ExtraTraceInformation("Finished implementation verification"); - return outcome; - } - - public Outcome StratifiedVerifyImplementation(Implementation! impl, Program! program, out List? errors) - ensures result == Outcome.Errors ==> errors != null; - throws UnexpectedProverOutputException; - { - Helpers.ExtraTraceInformation("Starting implementation verification"); - - CounterexampleCollector collector = new CounterexampleCollector(); - Outcome outcome = StratifiedVerifyImplementation(impl, program, collector); - if (outcome == Outcome.Errors) { - errors = collector.examples; - } else { - errors = null; - } - - Helpers.ExtraTraceInformation("Finished implementation verification"); - return outcome; - } - - public abstract Outcome VerifyImplementation(Implementation! impl, Program! program, VerifierCallback! callback) - throws UnexpectedProverOutputException; - - public virtual Outcome StratifiedVerifyImplementation(Implementation! impl, Program! program, VerifierCallback! callback) - throws UnexpectedProverOutputException; - { - return VerifyImplementation(impl, program, callback); - } - -/////////////////////////////////// Common Methods and Classes ////////////////////////////////////////// - -#region Methods for injecting pre- and postconditions - private static void - ThreadInBlockExpr(Implementation! impl, - Block! targetBlock, - BlockExpr! blockExpr, - bool replaceWithAssert, - TokenTextWriter debugWriter){ - // Go through blockExpr 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 blockExpr to the implementation (at the end) - foreach (Block! b in blockExpr.Blocks){ - 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); - Cmd! c = new AssertEnsuresCmd(ens); - b.Cmds.Add(c); - }else{ - b.Cmds.Add(new AssumeCmd(rec.tok, rec.Expr)); - } - b.TransferCmd = new GotoCmd(Token.NoToken, - new StringSeq(targetBlock.Label), - new BlockSeq(targetBlock)); - targetBlock.Predecessors.Add(b); - } - impl.Blocks.Add(b); - } - if (debugWriter != null){ - blockExpr.Emit(debugWriter, 1,false); - } - return; - } - - private static void AddAsPrefix(Block! b, CmdSeq! cs){ - CmdSeq newCommands = new CmdSeq(); - 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] CmdSeq! startCmds) - requires impl.Proc != null; - { - TokenTextWriter debugWriter = null; - if (CommandLineOptions.Clo.PrintWithUniqueASTIds) { - debugWriter = new TokenTextWriter("", Console.Out, false); - debugWriter.WriteLine("Effective precondition:"); - } - - Substitution formalProcImplSubst = Substituter.SubstitutionFromHashtable(impl.GetImplFormalMap()); - string LabelPrefix = "PreconditionGeneratedEntry"; - int k = 0; - - Block origStartBlock = impl.Blocks[0]; - Block insertionPoint = new Block( - new Token(-17, -4), LabelPrefix + k, startCmds, - new GotoCmd(Token.NoToken, new StringSeq(origStartBlock.Label), new BlockSeq(origStartBlock))); - k++; - 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 - { - Expr e = Substituter.Apply(formalProcImplSubst, req.Condition); - BlockExpr be = e as BlockExpr; - if (be == null) { - // This is the normal case, where the precondition is an ordinary expression - Cmd c = new AssumeCmd(req.tok, e); - insertionPoint.Cmds.Add(c); - if (debugWriter != null) { c.Emit(debugWriter, 1); } - } else { - // This is a BlockExpr, so append all of its blocks (changing return expressions - // to assume statements), make the insertion-point block goto the head block of the - // BlockExpr, and create a new empty block as the current insertion point. - // Here goes: First, create the new block, which will become the new insertion - // point and which will serve as a target for the BlockExpr. Move the goto's from - // the current insertion point to this new block. - Block nextIP = new Block(new Token(-17, -4), LabelPrefix + k, new CmdSeq(), insertionPoint.TransferCmd); - k++; - // Second, append the BlockExpr blocks to the implementation's blocks - ThreadInBlockExpr(impl, nextIP, be, false, debugWriter); - // Third, make the old insertion-point block goto the entry block of the BlockExpr - Block beEntry = (!)be.Blocks[0]; - insertionPoint.TransferCmd = new GotoCmd(Token.NoToken, new StringSeq(beEntry.Label), new BlockSeq(beEntry)); - beEntry.Predecessors.Add(insertionPoint); - // Fourth, update the insertion point - insertionPoint = nextIP; - } - } - origStartBlock.Predecessors.Add(insertionPoint); - - 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 Block! InjectPostConditions(Implementation! impl, Block! unifiedExitBlock, Hashtable/*TransferCmd->ReturnCmd*/! gotoCmdOrigins) - requires impl.Proc != null; - requires unifiedExitBlock.TransferCmd is ReturnCmd; - ensures result.TransferCmd is ReturnCmd; - { - TokenTextWriter debugWriter = null; - if (CommandLineOptions.Clo.PrintWithUniqueASTIds) { - debugWriter = new TokenTextWriter("", Console.Out, false); - debugWriter.WriteLine("Effective postcondition:"); - } - - Substitution formalProcImplSubst = Substituter.SubstitutionFromHashtable(impl.GetImplFormalMap()); - Block insertionPoint = unifiedExitBlock; - string LabelPrefix = "ReallyLastGeneratedExit"; - int k = 0; - - // (free and checked) ensures clauses - foreach (Ensures! ens in impl.Proc.Ensures) - invariant insertionPoint.TransferCmd is ReturnCmd; - { - if (!ens.Free) { // skip free ensures clauses - Expr e = Substituter.Apply(formalProcImplSubst, ens.Condition); - BlockExpr be = ens.Condition as BlockExpr; - if (be == null) { - // This is the normal case, where the postcondition is an ordinary expression - Ensures ensCopy = (Ensures!) ens.Clone(); - ensCopy.Condition = e; - AssertEnsuresCmd c = new AssertEnsuresCmd(ensCopy); - c.ErrorDataEnhanced = ensCopy.ErrorDataEnhanced; - insertionPoint.Cmds.Add(c); - if (debugWriter != null) { c.Emit(debugWriter, 1); } - } else { - // This is a BlockExpr, so append all of its blocks (changing return expressions - // to assert statements), insert a goto to its head block from the current insertion - // point, and create a new empty block as the current insertion point. - // Here goes: First, create the new block, which will become the new insertion - // point and which will serve as a target for the BlockExpr. Steal the TransferCmd - // from insertionPoint, since insertionPoint's TransferCmd will soon be replaced anyhow. - Block nextIP = new Block(new Token(-17, -4), LabelPrefix + k, new CmdSeq(), insertionPoint.TransferCmd); - k++; - // Second, append the BlockExpr blocks to the implementation's blocks - ThreadInBlockExpr(impl, nextIP, be, true, debugWriter); - // Third, make the old insertion-point block goto the entry block of the BlockExpr - Block beEntry = (!)be.Blocks[0]; - insertionPoint.TransferCmd = new GotoCmd(Token.NoToken, new StringSeq(beEntry.Label), new BlockSeq(beEntry)); - beEntry.Predecessors.Add(insertionPoint); - // Fourth, update the insertion point - insertionPoint = nextIP; - } - } - } - - if (debugWriter != null) { debugWriter.WriteLine(); } - return insertionPoint; - } - - - /// - /// Get the pre-condition of an implementation, including the where clauses from the in-parameters. - /// - /// - protected static CmdSeq! GetPre(Implementation! impl) - requires impl.Proc != null; - { - TokenTextWriter debugWriter = null; - if (CommandLineOptions.Clo.PrintWithUniqueASTIds) { - debugWriter = new TokenTextWriter("", Console.Out, false); - debugWriter.WriteLine("Effective precondition:"); - } - - Substitution formalProcImplSubst = Substituter.SubstitutionFromHashtable(impl.GetImplFormalMap()); - CmdSeq! pre = new CmdSeq(); - - // (free and checked) requires clauses - foreach (Requires! req in impl.Proc.Requires) - { - Expr! e = Substituter.Apply(formalProcImplSubst, req.Condition); - Cmd! c = new AssumeCmd(req.tok, e); - 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 CmdSeq! GetPost(Implementation! impl) - requires impl.Proc != null; - { - if (CommandLineOptions.Clo.PrintWithUniqueASTIds) { Console.WriteLine("Effective postcondition:"); } - - // Construct an Expr for the post-condition - Substitution formalProcImplSubst = Substituter.SubstitutionFromHashtable(impl.GetImplFormalMap()); - CmdSeq! post = new CmdSeq(); - foreach (Ensures! ens in impl.Proc.Ensures) - { - if (!ens.Free) { - Expr! e = Substituter.Apply(formalProcImplSubst, ens.Condition); - Ensures! ensCopy = (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, 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 CmdSeq! GetParamWhereClauses(Implementation! impl) - requires impl.Proc != null; - { - TokenTextWriter debugWriter = null; - if (CommandLineOptions.Clo.PrintWithUniqueASTIds) { - debugWriter = new TokenTextWriter("", Console.Out, false); - debugWriter.WriteLine("Effective precondition from where-clauses:"); - } - - Substitution formalProcImplSubst = Substituter.SubstitutionFromHashtable(impl.GetImplFormalMap()); - CmdSeq! whereClauses = new CmdSeq(); - - // where clauses of in-parameters - foreach (Formal! f in impl.Proc.InParams) - { - 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 - assert impl.OutParams.Length == impl.Proc.OutParams.Length; - for (int i = 0; i < impl.OutParams.Length; i++) { - Variable f = (!)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 = (!)impl.OutParams[i]; - assume fi.TypedIdent.WhereExpr == null; - fi.TypedIdent.WhereExpr = e; - - if (debugWriter != null) { c.Emit(debugWriter, 1); } - } - } - - if (debugWriter != null) { debugWriter.WriteLine(); } - - return whereClauses; - } - -#endregion - - - protected Checker! FindCheckerFor(Implementation impl, int timeout) - { - int i = 0; - while (i < checkers.Count) { - if (checkers[i].Closed) { - checkers.RemoveAt(i); - continue; - } else { - if (!checkers[i].IsBusy && checkers[i].WillingToHandle(impl, timeout)) return checkers[i]; - } - ++i; - } - string? log = logFilePath; - if (log != null && !log.Contains("@PROC@") && checkers.Count > 0) - log = log + "." + checkers.Count; - Checker! ch = new Checker(this, program, log, appendLogFile, impl, timeout); - checkers.Add(ch); - return ch; - } - - - public void Close() { foreach (Checker! checker in checkers) checker.Close(); } - - - protected class CounterexampleCollector : VerifierCallback - { - public readonly List! examples = new List(); - public override void OnCounterexample(Counterexample! ce, string? reason) - { - examples.Add(ce); - } - - public override void OnUnreachableCode(Implementation! impl) - { - 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) { - 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, false), 0); - CommandLineOptions.Clo.PrintDesugarings = oldPrintDesugaringSetting; - CommandLineOptions.Clo.PrintUnstructured = oldPrintUnstructured; - } - - - protected Block! GenerateUnifiedExit(Implementation! impl, Hashtable! gotoCmdOrigins) - ensures 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 CmdSeq(),new ReturnCmd(Token.NoToken)); - - foreach ( Block b in impl.Blocks ) - { - if ( b.TransferCmd is ReturnCmd ) - { - StringSeq labels = new StringSeq(); - labels.Add(unifiedExitLabel); - BlockSeq bs = new BlockSeq(); - bs.Add(unifiedExit); - GotoCmd go = new GotoCmd(Token.NoToken,labels,bs); - gotoCmdOrigins[go] = b.TransferCmd; - b.TransferCmd = go; - unifiedExit.Predecessors.Add(b); - } - } - - exitBlock = unifiedExit; - impl.Blocks.Add(unifiedExit); - } - assert exitBlock != null; - } - return exitBlock; - #endregion - } - - /// - /// Helperfunction to restore the predecessor relations after loop unrolling - /// - protected void ComputePredecessors(List! blocks) - { - #region Compute and store the Predecessor Relation on the blocks - // This code just here to try things out. - // Compute the predecessor relation for each block - // Store it in the Predecessors field within each block - foreach (Block b in blocks) - { - GotoCmd gtc = b.TransferCmd as GotoCmd; - if (gtc != null) - { - assume gtc.labelTargets != null; - foreach (Block! dest in gtc.labelTargets) - { - dest.Predecessors.Add(b); - } - } - } - #endregion Compute and store the Predecessor Relation on the blocks - } - - protected static void ResetPredecessors(List! blocks) - { - foreach (Block! b in blocks) { - b.Predecessors = new BlockSeq(); - } - foreach (Block! b in blocks) { - foreach (Block! ch in Exits(b)) { - ch.Predecessors.Add(b); - } - } - } - - protected static IEnumerable! Exits(Block! b) - { - GotoCmd g = b.TransferCmd as GotoCmd; - if (g != null) { - return (!)g.labelTargets; - } - return new List(); - } - - protected Variable! CreateIncarnation(Variable! x, Absy a) - requires this.variable2SequenceNumber != null; - requires this.current_impl != null; - requires a is Block || a is AssignCmd || a is HavocCmd; - { - int currentIncarnationNumber = - variable2SequenceNumber.ContainsKey(x) - ? - (int) ((!)variable2SequenceNumber[x]) - : - -1; - Variable v = new Incarnation(x,currentIncarnationNumber + 1); - variable2SequenceNumber[x] = currentIncarnationNumber + 1; - assert current_impl != null; // otherwise, the field current_impl wasn't set - current_impl.LocVars.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 Hashtable! /*Variable -> Expr*/ ComputeIncarnationMap(Block! b, Hashtable! /*Variable -> Expr*/ block2Incarnation) - { - if (b.Predecessors.Length == 0) - { - return new Hashtable(); - } - - Hashtable /*Variable -> Expr*/ incarnationMap = null; - Set /*Variable*/ fixUps = new Set /*Variable*/ (); - foreach (Block! pred in b.Predecessors) - { - assert block2Incarnation.Contains(pred); // otherwise, Passive Transformation found a block whose predecessors have not been processed yet - Hashtable /*Variable -> Expr*/ predMap = (Hashtable! /*Variable -> Expr*/) block2Incarnation[pred]; - if (incarnationMap == null) - { - incarnationMap = (Hashtable /*Variable -> Expr*/)predMap.Clone(); - continue; - } - - ArrayList /*Variable*/ conflicts = new ArrayList /*Variable*/ (); - foreach (Variable! v in incarnationMap.Keys) - { - if (!predMap.Contains(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) - { - incarnationMap.Remove(v); - } - foreach (Variable! v in predMap.Keys) - { - if (!incarnationMap.Contains(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) - { - if (!b.IsLive(v)) continue; - Variable v_prime = CreateIncarnation(v, b); - IdentifierExpr ie = new IdentifierExpr(v_prime.tok, v_prime); - assert incarnationMap != null; - incarnationMap[v] = ie; - foreach (Block! pred in b.Predecessors ) - { - #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 - Hashtable /*Variable -> Expr*/ predMap = (Hashtable! /*Variable -> Expr*/) block2Incarnation[pred]; - Expr pred_incarnation_exp; - Expr o = (Expr) predMap[v]; - 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 - Expr e = Expr.Binary(Token.NoToken, - BinaryOperator.Opcode.Eq, - v_prime_exp, - pred_incarnation_exp - ); - AssumeCmd ac = new AssumeCmd(v.tok,e); - pred.Cmds.Add(ac); - #endregion - #endregion - } - } - #endregion - - assert incarnationMap != null; - return incarnationMap; - } - - Hashtable 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, Hashtable! /*Variable -> Expr*/ incarnationMap, Substitution! oldFrameSubst) - { - #region Walk forward over the commands in this block and convert them to passive commands - - CmdSeq passiveCmds = new CmdSeq(); - foreach (Cmd! c in b.Cmds) - { // walk forward over the commands because the map gets modified in a forward direction - TurnIntoPassiveCmd(c, incarnationMap, oldFrameSubst, passiveCmds); - } - b.Cmds = passiveCmds; - - #endregion - } - - protected Hashtable /*Variable -> Expr*/ Convert2PassiveCmd(Implementation! impl) - { - #region Convert to Passive Commands - - #region Topological sort -- need to process in a linearization of the partial order - Graph dag = new Graph(); - dag.AddSource((!)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) - { - assume gtc.labelTargets != null; - foreach (Block! dest in gtc.labelTargets) - { - dag.AddEdge(b,dest); - } - } - } - IEnumerable! sortedNodes = dag.TopologicalSort(); - // assume sortedNodes != null; - #endregion - - // Create substitution for old expressions - Hashtable/*Variable!->Expr!*/ oldFrameMap = new Hashtable(); - assume impl.Proc != null; - foreach (IdentifierExpr! ie in impl.Proc.Modifies) { - if (!oldFrameMap.Contains((!)ie.Decl)) - oldFrameMap.Add(ie.Decl, ie); - } - Substitution oldFrameSubst = Substituter.SubstitutionFromHashtable(oldFrameMap); - - // 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. - Hashtable /*Block -> IncarnationMap*/ block2Incarnation = new Hashtable/*Block -> IncarnationMap*/(); - Block exitBlock = null; - foreach (Block! b in sortedNodes ) - { - assert !block2Incarnation.Contains(b); - Hashtable /*Variable -> Expr*/ incarnationMap = ComputeIncarnationMap(b, block2Incarnation); - - #region Each block's map needs to be available to successor blocks - block2Incarnation.Add(b,incarnationMap); - #endregion Each block's map needs to be available to successor blocks - - TurnIntoPassiveBlock(b, incarnationMap, oldFrameSubst); - exitBlock = b; - } - - // Verify that exitBlock is indeed the unique exit block - assert exitBlock != null; - assert exitBlock.TransferCmd is ReturnCmd; - - // 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) { - f.TypedIdent.WhereExpr = null; - } - #endregion Convert to Passive Commands - - #region Debug Tracing - if (CommandLineOptions.Clo.TraceVerify) - { - Console.WriteLine("after conversion to passive commands"); - EmitImpl(impl, true); - } - #endregion - - return (Hashtable) block2Incarnation[exitBlock]; - } - - /// - /// 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 remebers the incarnation map BEFORE the havoc - /// - protected void TurnIntoPassiveCmd(Cmd! c, Hashtable /*Variable -> Expr*/! incarnationMap, Substitution! oldFrameSubst, CmdSeq! passiveCmds) - { - Substitution incarnationSubst = Substituter.SubstitutionFromHashtable(incarnationMap); - #region assert/assume P |--> assert/assume P[x := in(x)], out := in - if ( c is PredicateCmd ) - { - assert c is AssertCmd || c is AssumeCmd; // otherwise, unexpected PredicateCmd type - - PredicateCmd! pc = (PredicateCmd) c.Clone(); - - Expr! copy = Substituter.ApplyReplacingOldExprs(incarnationSubst, oldFrameSubst, pc.Expr); - if (pc is AssertCmd) { - ((AssertCmd) pc).OrigExpr = pc.Expr; - assert ((AssertCmd) pc).IncarnationMap == null; - ((AssertCmd) pc).IncarnationMap = (Hashtable /*Variable -> Expr*/!) incarnationMap.Clone(); - } - pc.Expr = copy; - 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 - #region Substitute all variables in E with the current map - List copies = new List (); - foreach (Expr! e in assign.Rhss) - 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 = - ((SimpleAssignLhs!)assign.Lhss[i]).AssignedVariable; - Variable! lhs = (!)lhsIdExpr.Decl; - Expr! rhs = assign.Rhss[i]; - - // 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((!)ie.Decl) ) - newIncarnationMappings[lhs] = (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 - #region Create an assume command with the new variable - assumptions.Add(Expr.Eq(x_prime_exp, copies[i])); - #endregion - } - } - - foreach (KeyValuePair pair in newIncarnationMappings) - incarnationMap[pair.Key] = pair.Value; - - if (assumptions.Count > 0) { - Expr! assumption = assumptions[0]; - for (int i = 1; i < assumptions.Count; ++i) - assumption = Expr.And(assumption, assumptions[i]); - passiveCmds.Add(new AssumeCmd(c.tok, assumption)); - } - } - #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 = (Hashtable) incarnationMap.Clone(); - - HavocCmd! hc = (HavocCmd) c; - IdentifierExprSeq havocVars = hc.Vars; - // First, compute the new incarnations - foreach (IdentifierExpr! ie in havocVars) - { - if ( !(ie.Decl is Incarnation) ) - { - Variable x = (!)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) - { - if ( !(ie.Decl is Incarnation) ) - { - Variable x = (!)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)); - } - } - } - } - #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; - Cmd! cmd = sug.Desugaring; - TurnIntoPassiveCmd(cmd, incarnationMap, oldFrameSubst, passiveCmds); - } - else if (c is StateCmd) - { - this.preHavocIncarnationMap = null; // we do not need to remeber the previous incarnations - StateCmd! st = (StateCmd)c; - // account for any where clauses among the local variables - foreach (Variable! v in st.Locals) { - 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) { - TurnIntoPassiveCmd(s, incarnationMap, oldFrameSubst, passiveCmds); - } - // remove the local variables from the incarnation map - foreach (Variable! v in st.Locals) { - 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 rember 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 - } - - /// - /// 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) - requires 0 <= predIndex && predIndex < succ.Predecessors.Length; - { - Block! pred = (!) succ.Predecessors[predIndex]; - - string! newBlockLabel = pred.Label + "_@2_" + succ.Label; - - // successor of newBlock list - StringSeq ls = new StringSeq(); - ls.Add(succ.Label); - BlockSeq bs = new BlockSeq(); - bs.Add(succ); - - Block newBlock = new Block( - new Token(-17, -4), - newBlockLabel, - new CmdSeq(), - new GotoCmd(Token.NoToken,ls,bs) - ); - - // predecessors of newBlock - BlockSeq ps = new BlockSeq(); - ps.Add(pred); - newBlock.Predecessors = ps; - - // fix successors of pred - #region Change the edge "pred->succ" to "pred->newBlock" - GotoCmd gtc = (GotoCmd!) pred.TransferCmd; - assume gtc.labelTargets != null; - assume gtc.labelNames != null; - for ( int i = 0, n = gtc.labelTargets.Length; 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(Implementation! impl) - { - #region Introduce empty blocks between join points and their multi-successor predecessors - List tweens = new List(); - foreach ( Block b in impl.Blocks ) - { - int nPreds = b.Predecessors.Length; - 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!)((!)b.Predecessors[i]).TransferCmd; - if (gotocmd.labelNames != null && gotocmd.labelNames.Length > 1) - { - tweens.Add(CreateBlockBetween(i, b)); - } - } - } - } - impl.Blocks.AddRange(tweens); // must wait until iteration is done before changing the list - #endregion - } - - } -} \ No newline at end of file diff --git a/Source/VCGeneration/Context.cs b/Source/VCGeneration/Context.cs new file mode 100644 index 00000000..2db00964 --- /dev/null +++ b/Source/VCGeneration/Context.cs @@ -0,0 +1,200 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) Microsoft Corporation. All Rights Reserved. +// +//----------------------------------------------------------------------------- +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Microsoft.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. + /// + public abstract class ProverContext : ICloneable { + protected virtual void ProcessDeclaration(Declaration! decl) {} + public virtual void DeclareType(TypeCtorDecl! t, string attributes) { ProcessDeclaration(t); } + public virtual void DeclareConstant(Constant! c, bool uniq, string attributes) { ProcessDeclaration(c); } + public virtual void DeclareFunction(Function! f, string attributes) { ProcessDeclaration(f); } + public virtual void AddAxiom(Axiom! a, string attributes) { ProcessDeclaration(a); } + public abstract void AddAxiom(VCExpr! vc); + public virtual void DeclareGlobalVariable(GlobalVariable! v, string attributes) { ProcessDeclaration(v); } + + public abstract VCExpressionGenerator! ExprGen { get; } + public abstract Boogie2VCExprTranslator! BoogieExprTranslator { get; } + public abstract VCGenerationOptions! VCGenOptions { get; } + + public abstract object! Clone(); + } + + // ----------------------------------------------------------------------------------------------- + // ----------------------------------------------------------------------------------------------- + // ----------------------------------------------------------------------------------------------- + + /// + /// 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; + + StringBuilder! proverCommands; + StringBuilder! incrementalProverCommands; + + protected List! distincts; + protected List! axiomConjuncts; + + public VCExprTranslator exprTranslator; + + public DeclFreeProverContext(VCExpressionGenerator! gen, + VCGenerationOptions! genOptions) { + this.gen = gen; + this.genOptions = genOptions; + Boogie2VCExprTranslator! t = new Boogie2VCExprTranslator (gen, genOptions); + this.translator = t; + OrderingAxiomBuilder! oab = new OrderingAxiomBuilder(gen, t); + oab.Setup(); + this.orderingAxiomBuilder = oab; + + proverCommands = new StringBuilder(); + incrementalProverCommands = new StringBuilder(); + + distincts = new List(); + axiomConjuncts = new List(); + + exprTranslator = null; + } + + private DeclFreeProverContext(DeclFreeProverContext! ctxt) { + this.gen = ctxt.gen; + this.genOptions = ctxt.genOptions; + Boogie2VCExprTranslator! t = (Boogie2VCExprTranslator)ctxt.translator.Clone(); + this.translator = t; + this.orderingAxiomBuilder = new OrderingAxiomBuilder(ctxt.gen, t, ctxt.orderingAxiomBuilder); + + StringBuilder! cmds = new StringBuilder (); + cmds.Append(ctxt.proverCommands); + proverCommands = cmds; + StringBuilder! incCmds = new StringBuilder (); + incCmds.Append(ctxt.incrementalProverCommands); + incrementalProverCommands = incCmds; + + distincts = new List(ctxt.distincts); + axiomConjuncts = new List(ctxt.axiomConjuncts); + + if (ctxt.exprTranslator == null) + exprTranslator = null; + else + exprTranslator = (VCExprTranslator!)ctxt.exprTranslator.Clone(); + } + + public override object! Clone() { + return new DeclFreeProverContext(this); + } + + internal protected void SayToProver(string! msg) + { + msg = msg + "\r\n"; + proverCommands.Append(msg); + incrementalProverCommands.Append(msg); + } + + protected override void ProcessDeclaration(Declaration! decl) + { + for (QKeyValue a = decl.Attributes; a != null; a = a.Next) { + if (a.Key == "prover" && a.Params.Count == 1) { + string cmd = a.Params[0] as string; + if (cmd != null) { + int pos = cmd.IndexOf(':'); + if (pos <= 0) + throw new ProverException("Invalid syntax of :prover string: `" + cmd + "'"); + string kind = cmd.Substring(0, pos); + if (genOptions.IsAnyProverCommandSupported(kind)) + SayToProver(cmd.Substring(pos + 1)); + } + } + } + } + + public override void DeclareFunction(Function! f, string attributes) { + base.ProcessDeclaration(f); + } + + public override void DeclareConstant(Constant! c, bool uniq, string attributes) { + 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) { + base.AddAxiom(ax, attributes); + + string ignore = ax.FindStringAttribute("ignore"); + if (ignore != null && genOptions.IsAnyProverCommandSupported(ignore)) { + return; + } + + axiomConjuncts.Add(translator.Translate(ax.Expr)); + } + + public override void AddAxiom(VCExpr! vc) + { + axiomConjuncts.Add(vc); + } + + public VCExpr! Axioms { + get { + VCExpr axioms = gen.NAry(VCExpressionGenerator.AndOp, axiomConjuncts); + List! distinctVars = new List (); + foreach (Variable! v in distincts) + 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 string! GetProverCommands(bool full) { + string! res = (full ? proverCommands : incrementalProverCommands).ToString(); + incrementalProverCommands.Length = 0; + return res; + } + + public override VCExpressionGenerator! ExprGen { get { + return gen; + } } + public override Boogie2VCExprTranslator! BoogieExprTranslator { get { + return translator; + } } + public override VCGenerationOptions! VCGenOptions { get { + return genOptions; + } } + } + + // Translator from VCExpressions to strings, which are implemented + // by the various provers + public abstract class VCExprTranslator : ICloneable { + public abstract string! translate(VCExpr! expr, int polarity); + public abstract string! Lookup(VCExprVar! var); + public abstract Object! Clone(); + } +} diff --git a/Source/VCGeneration/Context.ssc b/Source/VCGeneration/Context.ssc deleted file mode 100644 index 2db00964..00000000 --- a/Source/VCGeneration/Context.ssc +++ /dev/null @@ -1,200 +0,0 @@ -//----------------------------------------------------------------------------- -// -// Copyright (C) Microsoft Corporation. All Rights Reserved. -// -//----------------------------------------------------------------------------- -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -using Microsoft.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. - /// - public abstract class ProverContext : ICloneable { - protected virtual void ProcessDeclaration(Declaration! decl) {} - public virtual void DeclareType(TypeCtorDecl! t, string attributes) { ProcessDeclaration(t); } - public virtual void DeclareConstant(Constant! c, bool uniq, string attributes) { ProcessDeclaration(c); } - public virtual void DeclareFunction(Function! f, string attributes) { ProcessDeclaration(f); } - public virtual void AddAxiom(Axiom! a, string attributes) { ProcessDeclaration(a); } - public abstract void AddAxiom(VCExpr! vc); - public virtual void DeclareGlobalVariable(GlobalVariable! v, string attributes) { ProcessDeclaration(v); } - - public abstract VCExpressionGenerator! ExprGen { get; } - public abstract Boogie2VCExprTranslator! BoogieExprTranslator { get; } - public abstract VCGenerationOptions! VCGenOptions { get; } - - public abstract object! Clone(); - } - - // ----------------------------------------------------------------------------------------------- - // ----------------------------------------------------------------------------------------------- - // ----------------------------------------------------------------------------------------------- - - /// - /// 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; - - StringBuilder! proverCommands; - StringBuilder! incrementalProverCommands; - - protected List! distincts; - protected List! axiomConjuncts; - - public VCExprTranslator exprTranslator; - - public DeclFreeProverContext(VCExpressionGenerator! gen, - VCGenerationOptions! genOptions) { - this.gen = gen; - this.genOptions = genOptions; - Boogie2VCExprTranslator! t = new Boogie2VCExprTranslator (gen, genOptions); - this.translator = t; - OrderingAxiomBuilder! oab = new OrderingAxiomBuilder(gen, t); - oab.Setup(); - this.orderingAxiomBuilder = oab; - - proverCommands = new StringBuilder(); - incrementalProverCommands = new StringBuilder(); - - distincts = new List(); - axiomConjuncts = new List(); - - exprTranslator = null; - } - - private DeclFreeProverContext(DeclFreeProverContext! ctxt) { - this.gen = ctxt.gen; - this.genOptions = ctxt.genOptions; - Boogie2VCExprTranslator! t = (Boogie2VCExprTranslator)ctxt.translator.Clone(); - this.translator = t; - this.orderingAxiomBuilder = new OrderingAxiomBuilder(ctxt.gen, t, ctxt.orderingAxiomBuilder); - - StringBuilder! cmds = new StringBuilder (); - cmds.Append(ctxt.proverCommands); - proverCommands = cmds; - StringBuilder! incCmds = new StringBuilder (); - incCmds.Append(ctxt.incrementalProverCommands); - incrementalProverCommands = incCmds; - - distincts = new List(ctxt.distincts); - axiomConjuncts = new List(ctxt.axiomConjuncts); - - if (ctxt.exprTranslator == null) - exprTranslator = null; - else - exprTranslator = (VCExprTranslator!)ctxt.exprTranslator.Clone(); - } - - public override object! Clone() { - return new DeclFreeProverContext(this); - } - - internal protected void SayToProver(string! msg) - { - msg = msg + "\r\n"; - proverCommands.Append(msg); - incrementalProverCommands.Append(msg); - } - - protected override void ProcessDeclaration(Declaration! decl) - { - for (QKeyValue a = decl.Attributes; a != null; a = a.Next) { - if (a.Key == "prover" && a.Params.Count == 1) { - string cmd = a.Params[0] as string; - if (cmd != null) { - int pos = cmd.IndexOf(':'); - if (pos <= 0) - throw new ProverException("Invalid syntax of :prover string: `" + cmd + "'"); - string kind = cmd.Substring(0, pos); - if (genOptions.IsAnyProverCommandSupported(kind)) - SayToProver(cmd.Substring(pos + 1)); - } - } - } - } - - public override void DeclareFunction(Function! f, string attributes) { - base.ProcessDeclaration(f); - } - - public override void DeclareConstant(Constant! c, bool uniq, string attributes) { - 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) { - base.AddAxiom(ax, attributes); - - string ignore = ax.FindStringAttribute("ignore"); - if (ignore != null && genOptions.IsAnyProverCommandSupported(ignore)) { - return; - } - - axiomConjuncts.Add(translator.Translate(ax.Expr)); - } - - public override void AddAxiom(VCExpr! vc) - { - axiomConjuncts.Add(vc); - } - - public VCExpr! Axioms { - get { - VCExpr axioms = gen.NAry(VCExpressionGenerator.AndOp, axiomConjuncts); - List! distinctVars = new List (); - foreach (Variable! v in distincts) - 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 string! GetProverCommands(bool full) { - string! res = (full ? proverCommands : incrementalProverCommands).ToString(); - incrementalProverCommands.Length = 0; - return res; - } - - public override VCExpressionGenerator! ExprGen { get { - return gen; - } } - public override Boogie2VCExprTranslator! BoogieExprTranslator { get { - return translator; - } } - public override VCGenerationOptions! VCGenOptions { get { - return genOptions; - } } - } - - // Translator from VCExpressions to strings, which are implemented - // by the various provers - public abstract class VCExprTranslator : ICloneable { - public abstract string! translate(VCExpr! expr, int polarity); - public abstract string! Lookup(VCExprVar! var); - public abstract Object! Clone(); - } -} diff --git a/Source/VCGeneration/DoomCheck.cs b/Source/VCGeneration/DoomCheck.cs new file mode 100644 index 00000000..b65f7f24 --- /dev/null +++ b/Source/VCGeneration/DoomCheck.cs @@ -0,0 +1,797 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) Microsoft Corporation. All Rights Reserved. +// +//----------------------------------------------------------------------------- + +/* + Todo: + - Inject Pre- and Postcondition +*/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; +using System.IO; +using Microsoft.Boogie; +using Graphing; +using AI = Microsoft.AbstractInterpretationFramework; +using Microsoft.Contracts; +using Microsoft.Basetypes; +using Microsoft.Boogie.VCExprAST; + +namespace VC +{ + internal class Evc { + + public DoomErrorHandler ErrorHandler { + set { + m_ErrorHandler = value; + } + } + + private Checker! m_Checker; + private DoomErrorHandler m_ErrorHandler; + + [NotDelayed] + public Evc(Checker! check) { + m_Checker = check; + base(); + } + + public void Initialize(VCExpr! evc) { + m_Checker.PushVCExpr(evc); + } + + + public bool CheckReachvar(Variable! reachvar, out ProverInterface.Outcome outcome) { + VCExpr! vc = m_Checker.TheoremProver.Context.BoogieExprTranslator.LookupVariable(reachvar); + // Todo: Check if vc is trivial true or false + outcome = ProverInterface.Outcome.Undetermined; + assert m_ErrorHandler !=null; + m_Checker.BeginCheck(reachvar.Name, vc, m_ErrorHandler); + m_Checker.ProverDone.WaitOne(); + + try { + outcome = m_Checker.ReadOutcome(); + } catch (UnexpectedProverOutputException e) + { + if (CommandLineOptions.Clo.TraceVerify) { + Console.WriteLine("Prover is unable to check {0}! Reason:", reachvar.Name); + Console.WriteLine(e.ToString()); + } + return false; + } + return true; + } + } + + internal class DoomCheck { + + #region Attributes + public Hashtable! Label2Absy; + public DoomErrorHandler ErrorHandler { + set { + m_ErrHandler = value; + m_Evc.ErrorHandler = value; + } + + get { + return m_ErrHandler; + } + } + + private DoomErrorHandler m_ErrHandler; + private Checker! m_Check; + private InclusionOrder! m_Order; + private Evc! m_Evc; + #endregion + + [NotDelayed] + public DoomCheck (Implementation! passive_impl, Checker! check) { + m_Check = check; + if (!VC.DCGen.UseItAsDebugger ) { + m_Order = new InclusionOrder(passive_impl.Blocks[0]); + } else { + m_Order = new InclusionOrder((!)VC.DCGen.firstNonDebugBlock ); + } + Label2Absy = new Hashtable(); // This is only a dummy + m_Evc = new Evc(check); + base(); + Hashtable l2a = null; + VCExpr! vce = this.GenerateEVC(passive_impl, out l2a, check); + assert l2a!=null; + Label2Absy = l2a; +// vce = check.VCExprGen.Implies(vce, vce); + m_Evc.Initialize(vce); + } + + /* - Set b to the next block that needs to be checked. + - Returns false and set b to null if all blocks are checked. + - Has to be alternated with CheckLabel; might crash otherwise + */ + public bool GetNextBlock(out Block b) { + return m_Order.GetNextBlock(out b); + } + + /* - Checking a label means to ask the prover if |= ( rvar=false -> vc ) holds. + - outcome is set to Outcome.Invalid if the Block denoted by reachvar is doomed. + - returns false if the theorem prover throws an exception, otherwise true. + */ + public bool CheckLabel(Variable! reachvar, out ProverInterface.Outcome outcome) { + + outcome = ProverInterface.Outcome.Undetermined; + if (m_Evc.CheckReachvar(reachvar, out outcome) ) { + if (!m_Order.SetCurrentResult(reachvar, outcome, m_ErrHandler)) { + outcome = ProverInterface.Outcome.Undetermined; + } + return true; + } else { + m_Order.SetCurrentResult(reachvar, ProverInterface.Outcome.Undetermined, m_ErrHandler); + return false; + } + } + + public List!>! DoomedSequences { + get { + return m_Order.DetectedBlock; + } + } + + + #region Error Verification Condition Generation + /* + #region _TESTING_NEW_STUFF_ + CommandLineOptions.Clo.vcVariety = CommandLineOptions.VCVariety.Block; + //VCExpr wp = Wlp.Block(block, SuccCorrect, context); // Computes wp.S.true + + CommandLineOptions.Clo.vcVariety = CommandLineOptions.VCVariety.Doomed; + #endregion + + */ + private bool _tmpUseFreshBVars; + + VCExpr! GenerateEVC(Implementation! impl, out Hashtable label2absy, Checker! ch) + { + TypecheckingContext tc = new TypecheckingContext(null); + impl.Typecheck(tc); + label2absy = new Hashtable/**/(); + VCExpr! vc; + switch (CommandLineOptions.Clo.vcVariety) { + case CommandLineOptions.VCVariety.Doomed: + + if (!VC.DCGen.UseItAsDebugger ) { + vc = LetVC((!)impl.Blocks[0], label2absy, ch.TheoremProver.Context); + } + #region _TESTING_NEW_STUFF_ + else { + + + CommandLineOptions.Clo.vcVariety = CommandLineOptions.VCVariety.Block; + assert VC.DCGen.firstDebugBlock != null; + VCExpr! a = LetVC(VC.DCGen.firstDebugBlock, label2absy, ch.TheoremProver.Context); + CommandLineOptions.Clo.vcVariety = CommandLineOptions.VCVariety.Doomed; + + + VCExpr! b = LetVC((!)VC.DCGen.firstNonDebugBlock, label2absy, ch.TheoremProver.Context); + + //_tmpUseFreshBVars=false; + //vc = ch.VCExprGen.Not(ch.VCExprGen.Implies( wp, ch.VCExprGen.Not(vc))); + + vc = ch.VCExprGen.Not(ch.VCExprGen.Implies(ch.VCExprGen.Not(a), ch.VCExprGen.Not(b))); + + + /* + ConsoleColor col = Console.ForegroundColor; + Console.ForegroundColor = ConsoleColor.Green; + Console.WriteLine(vc.ToString()); + Console.ForegroundColor = col; + Console.WriteLine(" ... was asked."); + */ + + } + #endregion + + break; + + default: + assert false; // unexpected enumeration value + } + return vc; + } + + public VCExpr! LetVC(Block! startBlock, + Hashtable/**/! label2absy, + ProverContext! proverCtxt) + { + Hashtable/**/! blockVariables = new Hashtable/**/(); + List! bindings = new List(); + VCExpr startCorrect = LetVC(startBlock, label2absy, blockVariables, bindings, proverCtxt); + if (CommandLineOptions.Clo.vcVariety == CommandLineOptions.VCVariety.Doomed) { + return proverCtxt.ExprGen.Let(bindings, proverCtxt.ExprGen.Not(startCorrect) ); + } else { + return proverCtxt.ExprGen.Let(bindings, startCorrect ); + } + } + + VCExpr! LetVC(Block! block, + Hashtable/**/! label2absy, + Hashtable/**/! blockVariables, + List! bindings, + ProverContext! proverCtxt) + { + VCExpressionGenerator! gen = proverCtxt.ExprGen; + 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) { + if (CommandLineOptions.Clo.vcVariety == CommandLineOptions.VCVariety.Doomed) { + SuccCorrect = VCExpressionGenerator.False; + } else { + SuccCorrect = VCExpressionGenerator.True; + } + } else { + assert gotocmd.labelTargets != null; + List SuccCorrectVars = new List(gotocmd.labelTargets.Length); + foreach (Block! successor in gotocmd.labelTargets) { + VCExpr s = LetVC(successor, label2absy, blockVariables, bindings, proverCtxt); + SuccCorrectVars.Add(s); + } + SuccCorrect = gen.NAry(VCExpressionGenerator.AndOp, SuccCorrectVars); + } + + VCContext context = new VCContext(label2absy, proverCtxt); + // m_Context = context; + + VCExpr vc = Wlp.Block(block, SuccCorrect, context); + + v = gen.Variable(block.Label + "_correct", Microsoft.Boogie.Type.Bool); + + bindings.Add(gen.LetBinding(v, vc)); + blockVariables.Add(block, v); + } + return v; + } + + + #endregion + + + #region Build Inclusion Order to minimize the number of checks + class InclusionOrder + { + + #region Attributes + public List!>! DetectedBlock = new List!>(); + + InclusionTree! RootNode = new InclusionTree(null); + InclusionTree currentNode = null; + + #endregion + + [NotDelayed] + public InclusionOrder(Block! rootblock) { + /* + We now compute for each block the set of blocks that + are reached on every execution reaching this block (dominator). + We first compute it form the start block to the current + block and second from the Term block to the current one. + Finally we build the union. + */ + + base(); + Dictionary! map2 = new Dictionary(); + Dictionary! map = new Dictionary(); + + Dictionary!>! unavoidablemap = new Dictionary!>(); + + Block! exitblock = BreadthFirst(rootblock, map2); + BreadthFirstBwd(exitblock, map); + + foreach (KeyValuePair kvp in map) { + List! blist = new List(); + foreach (TraceNode tn in kvp.Value.Unavoidables ) { + blist.Add(tn.block); + } + unavoidablemap.Add(kvp.Key, blist); + } + foreach (KeyValuePair!> kvp in unavoidablemap) { + TraceNode tn = null; + if (map2.TryGetValue(kvp.Key, out tn) ) { + assert tn!=null; + foreach (TraceNode! t0 in tn.Unavoidables) { + if (!kvp.Value.Contains(t0.block)) + kvp.Value.Add(t0.block); + } + } else { + //assert false; + } + } + + foreach (KeyValuePair!> kvp in unavoidablemap) { + Insert2Tree(RootNode,kvp); + } + InitCurrentNode(RootNode); + //printtree(RootNode, "",0); + } + + void InitCurrentNode(InclusionTree! n) { + if (n.Children.Count>0) { + InitCurrentNode(n.Children[0]); + } else { + currentNode = n; + } + } + + public bool GetNextBlock(out Block b) { + if (currentNode!=null && currentNode.EquivBlock.Count>0) { + b = currentNode.EquivBlock[0]; + return true; + } + b = null; + return false; + } + + + /* + Warning: this is a very slow implementation. There should be + a map from Block to InclusionTree to prevent running through + the tree over and over again. + */ + private void MarkUndoomedFromCE(Block! b, InclusionTree! node) + { + if (node.EquivBlock.Contains(b) ) { + //Console.WriteLine("Block {0} does not need to be checked - appears in CE", b.Label); + node.HasBeenChecked = true; + node.IsDoomed = false; + MarkUndoomedParents(node); + return; + } else + { + foreach (InclusionTree! c in node.Children) + { + MarkUndoomedFromCE(b, c); + } + } + } + + // Takes the outcome for the current reachvar and marks all blocks + // the do not need any further checking because they share the same + // desteny + // returns false if an explicite assert false was found. + // Warning: Still slow; we do not need to do this while constructing the error report! + public bool SetCurrentResult(Variable! reachvar, ProverInterface.Outcome outcome, DoomErrorHandler cb) { + if (outcome!=ProverInterface.Outcome.Valid) { + if (currentNode != null) { + currentNode.IsDoomed = false; + currentNode.HasBeenChecked = true; + MarkUndoomedParents(currentNode); + if (cb != null) { + //Console.WriteLine("CE contains: "); + foreach (Block! b in cb.TraceNodes) + { + //Console.Write("{0}, ", b.Label); + MarkUndoomedFromCE(b, RootNode); + } + //Console.WriteLine(); + } else { + Console.WriteLine("ErrorHandler is not valid! ------ (DoomCheck)"); + } + + currentNode = FindNextNode(currentNode); + } + } else { + if (currentNode != null) { + + // Check if there is an assert false in this node or in one of its parents + // if so do not report anything +// if (ECContainsAssertFalse(currentNode.EquivBlock)) { +// +// ConsoleColor col = Console.ForegroundColor; +// Console.ForegroundColor = ConsoleColor.Blue; +// Console.WriteLine("Assert false or PossiblyUnreachable was detected, but ignored"); +// Console.ForegroundColor = col; +// +// currentNode.HasBeenChecked = true; +// currentNode.IsDoomed = false; +// currentNode = currentNode.Parent; +// return false; +// } + + List! traceblocks = new List(); + + TracedChildern(currentNode, traceblocks); + TracedParents(currentNode, traceblocks); + +// cb.OnWarning("Doomed program points found!"); + if (cb != null) { + cb.OnDoom(reachvar, currentNode.EquivBlock, traceblocks); + } + + if (currentNode.EquivBlock.Count>0) { + + foreach (InclusionTree! n in currentNode.Children) { + if (DetectedBlock.Contains(n.EquivBlock) ) { + DetectedBlock.Remove(n.EquivBlock); + } + } + + DetectedBlock.Add(currentNode.EquivBlock); + + } else { + Console.WriteLine("An empty equivalence class has been detected"); + assert false; + } + + currentNode.IsDoomed = true; + currentNode.HasBeenChecked = true; + MarkDoomedChildren(currentNode); + currentNode = currentNode.Parent; + if (currentNode!=null) { + foreach (InclusionTree! it in currentNode.Children) { + if (!it.HasBeenChecked) { + currentNode = DescendToDeepestUnchecked(it); + break; + } + } + } + } + } + return true; + } + + private InclusionTree! DescendToDeepestUnchecked(InclusionTree! node) + { + foreach (InclusionTree! it in node.Children) { + if (!it.HasBeenChecked) { + return DescendToDeepestUnchecked(it); + } + } + return node; + } + + private bool ECContainsAssertFalse(List! equiv) { + foreach (Block! b in equiv) { + if (BlockContainsAssertFalse(b)) return true; + } + return false; + } + + private bool BlockContainsAssertFalse(Block! b) { + foreach (Cmd! c in b.Cmds) { + AssertCmd ac = c as AssertCmd; + if (ac!=null) { + if (IsFalse(ac.Expr) || QKeyValue.FindBoolAttribute(ac.Attributes, "PossiblyUnreachable") ) { + return true; + } + } + } + return false; + } + + // 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) + { + 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((!)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 && + ((!)call.Args[0]).Equals(call.Args[1])) + { + val = false; + return true; + } + + return false; + } + + bool IsFalse(Expr! e) + { + bool val = false; + return BooleanEval(e, ref val) && !val; + } + + private void TracedChildern(InclusionTree! node, List! blist) { + foreach (Block! b in node.EquivBlock) { + if (!blist.Contains(b)) blist.Add(b); + } + foreach (InclusionTree! n in node.Children) { + TracedChildern(n, blist); + } + } + + private void TracedParents(InclusionTree! node, List! blist) { + foreach (Block! b in node.EquivBlock) { + if (!blist.Contains(b)) blist.Add(b); + } + if (node.Parent!=null) { + TracedParents(node.Parent, blist); + } + } + + InclusionTree FindNextNode(InclusionTree! n) { + assert n!=n.Parent; + InclusionTree next = n.Parent; + if (next!=null) { + foreach (InclusionTree! n0 in next.Children) { + if (!n0.HasBeenChecked) { + return n0; + } + } + return FindNextNode(next); + } + return next; + } + + void MarkUndoomedParents(InclusionTree! n) { + if (n.Parent != null) { + n.Parent.HasBeenChecked = true; + n.Parent.IsDoomed = false; + MarkUndoomedParents(n.Parent); + } + } + + void MarkDoomedChildren(InclusionTree! n) { + foreach (InclusionTree! t in n.Children) { + t.IsDoomed = true; + t.HasBeenChecked = true; + MarkDoomedChildren(t); + } + } + + bool Insert2Tree(InclusionTree! node, KeyValuePair!> kvp) { + if (IsSubset(node.PathBlocks, kvp.Value) ) { + if (IsSubset(kvp.Value, node.PathBlocks) ) { + // The set of unavoidable blocks is equal, so + // we can put the block in the same node. + node.EquivBlock.Add(kvp.Key); + return true; + } else { + foreach (InclusionTree! n in node.Children) { + if (Insert2Tree(n,kvp) ) { + return true; + } + } + // we have not been able to add the block to one of + // the children, so we have to create a new child. + InclusionTree! it = new InclusionTree(node); + it.EquivBlock.Add(kvp.Key); + it.PathBlocks.AddRange(kvp.Value); + node.Children.Add(it); + return true; + } + // If we reached this point, we have to add a new node since + // our current set of pathnodes is not a subset of anything else + } else { + // seems, that we have reached a new leaf. + } + return false; + } + + bool IsSubset(List! sub, List! super ) { + foreach (Block! b in sub) { + if (!super.Contains(b) ) return false; + } + return true; + } + + void printtree(InclusionTree! n, string indent, int level) { + Console.Write("{0}Level {1}: Blocks ", indent, level); + foreach (Block! b in n.EquivBlock) { + Console.Write("{0}, ", b.Label); + } + Console.WriteLine(); + + foreach (InclusionTree! t in n.Children) { + printtree(t, indent+" ", level+1); + } + } + + private class InclusionTree { + public InclusionTree(InclusionTree p) { + Parent = p; + HasBeenChecked=false; + IsDoomed = false; + } + + public bool HasBeenChecked; + public bool IsDoomed; + public InclusionTree Parent; + public List! EquivBlock = new List(); + public List! PathBlocks = new List(); + public List! Children = new List(); + } + + #region Collect Unavoidable Blocks + private Block! BreadthFirst(Block! start, Dictionary! blockmap) { + List! JobList = new List(); + List! DoneList = new List(); + Block exitblock=null; + JobList.Add(start); + Block! currentBlock = JobList[0]; + // Travers the Graph Breadth First and collect all + // predecessors of each node that are reached on any + // path to this node + while (JobList.Count>0) + { + currentBlock = JobList[0]; + TraceNode! tn = new TraceNode(currentBlock); + + if (currentBlock.Predecessors.Length>0 ) { + TraceNode t0 =null; + Block firstpred = currentBlock.Predecessors[0]; + + assert firstpred!=null; + if (blockmap.TryGetValue(firstpred, out t0)) { + assert t0 !=null; + tn.Unavoidables.AddRange(t0.Unavoidables); + } + } + + foreach (Block! b0 in currentBlock.Predecessors) { + TraceNode t = null; + if (blockmap.TryGetValue(b0, out t)) { + assert t!=null; + tn.Predecessors.Add(t); + IntersectUnavoidables(t,tn); + if (!t.Successors.Contains(tn)) t.Successors.Add(tn); + blockmap[b0]=t; + } + } + if (!tn.Unavoidables.Contains(tn)) { + tn.Unavoidables.Add(tn); + } else + { + assert false; + } + + + blockmap.Add(currentBlock, tn); + + GotoCmd gc = currentBlock.TransferCmd as GotoCmd; + if (gc!=null) { + assert gc.labelTargets!=null; + foreach (Block! b0 in gc.labelTargets) { + if (!JobList.Contains(b0) && !DoneList.Contains(b0)) { + + JobList.Add(b0); + } + } + } else { + exitblock=currentBlock; + } + DoneList.Add(currentBlock); + JobList.RemoveAt(0); + } + assert exitblock!=null; + return exitblock; + } + + // WARNING: It is only for testing reasons that + // BreadthFirstBwd and BreadthFirst and separat functions + // it should be implemented using one function later on. + private void BreadthFirstBwd(Block! start, Dictionary! blockmap) { + List! JobList = new List(); + List! DoneList = new List(); + JobList.Add(start); + Block! currentBlock = JobList[0]; + // Travers the Graph Breadth First and collect all + // predecessors of each node that are reached on any + // path to this node + while (JobList.Count>0) + { + currentBlock = JobList[0]; + TraceNode! tn = new TraceNode(currentBlock); + + GotoCmd gc = currentBlock.TransferCmd as GotoCmd; + BlockSeq preds = null; + if (gc!=null) { + preds = gc.labelTargets; + } + + if (preds != null ) { + + TraceNode t0 =null; + Block firstpred = preds[0]; + + assert firstpred!=null; + if (blockmap.TryGetValue(firstpred, out t0)) { + assert t0 !=null; + tn.Unavoidables.AddRange(t0.Unavoidables); + } + + + foreach (Block! b0 in preds) { + TraceNode t = null; + if (blockmap.TryGetValue(b0, out t)) { + assert t!=null; + tn.Successors.Add(t); + IntersectUnavoidables(t,tn); + if (!t.Predecessors.Contains(tn)) t.Predecessors.Add(tn); + blockmap[b0]=t; + } + } + } + if (!tn.Unavoidables.Contains(tn)) { + tn.Unavoidables.Add(tn); + } else + { + assert false; + } + blockmap.Add(currentBlock, tn); + + if (currentBlock.Predecessors.Length>0) { + foreach (Block! b0 in currentBlock.Predecessors) { + if (!JobList.Contains(b0) && !DoneList.Contains(b0) ) + JobList.Add(b0); + } + } + DoneList.Add(currentBlock); + JobList.RemoveAt(0); + } + } + + private void IntersectUnavoidables(TraceNode! parent, TraceNode! child) { + List! ret = new List(); + List! tmp = new List(); + tmp.AddRange(parent.Unavoidables); + tmp.AddRange(child.Unavoidables); + + foreach (TraceNode! tn in tmp) { + if (parent.Unavoidables.Contains(tn) && child.Unavoidables.Contains(tn) + && !ret.Contains(tn) ) { + ret.Add(tn); + } + } + assert ret.Count <= parent.Unavoidables.Count && ret.Count <= child.Unavoidables.Count; + child.Unavoidables = ret; + + } + + #region TraceNode Class + // We assume that the program is already loopfree, otherwise we will + // not terminate + private class TraceNode { + public List! Predecessors = new List(); + public List! Successors = new List(); + public List! Unavoidables = new List(); + public Block! block; + + + public TraceNode(Block! b) { + block=b; + } + + } + #endregion + #endregion + } + #endregion + + + } +} diff --git a/Source/VCGeneration/DoomCheck.ssc b/Source/VCGeneration/DoomCheck.ssc deleted file mode 100644 index b65f7f24..00000000 --- a/Source/VCGeneration/DoomCheck.ssc +++ /dev/null @@ -1,797 +0,0 @@ -//----------------------------------------------------------------------------- -// -// Copyright (C) Microsoft Corporation. All Rights Reserved. -// -//----------------------------------------------------------------------------- - -/* - Todo: - - Inject Pre- and Postcondition -*/ - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.Threading; -using System.IO; -using Microsoft.Boogie; -using Graphing; -using AI = Microsoft.AbstractInterpretationFramework; -using Microsoft.Contracts; -using Microsoft.Basetypes; -using Microsoft.Boogie.VCExprAST; - -namespace VC -{ - internal class Evc { - - public DoomErrorHandler ErrorHandler { - set { - m_ErrorHandler = value; - } - } - - private Checker! m_Checker; - private DoomErrorHandler m_ErrorHandler; - - [NotDelayed] - public Evc(Checker! check) { - m_Checker = check; - base(); - } - - public void Initialize(VCExpr! evc) { - m_Checker.PushVCExpr(evc); - } - - - public bool CheckReachvar(Variable! reachvar, out ProverInterface.Outcome outcome) { - VCExpr! vc = m_Checker.TheoremProver.Context.BoogieExprTranslator.LookupVariable(reachvar); - // Todo: Check if vc is trivial true or false - outcome = ProverInterface.Outcome.Undetermined; - assert m_ErrorHandler !=null; - m_Checker.BeginCheck(reachvar.Name, vc, m_ErrorHandler); - m_Checker.ProverDone.WaitOne(); - - try { - outcome = m_Checker.ReadOutcome(); - } catch (UnexpectedProverOutputException e) - { - if (CommandLineOptions.Clo.TraceVerify) { - Console.WriteLine("Prover is unable to check {0}! Reason:", reachvar.Name); - Console.WriteLine(e.ToString()); - } - return false; - } - return true; - } - } - - internal class DoomCheck { - - #region Attributes - public Hashtable! Label2Absy; - public DoomErrorHandler ErrorHandler { - set { - m_ErrHandler = value; - m_Evc.ErrorHandler = value; - } - - get { - return m_ErrHandler; - } - } - - private DoomErrorHandler m_ErrHandler; - private Checker! m_Check; - private InclusionOrder! m_Order; - private Evc! m_Evc; - #endregion - - [NotDelayed] - public DoomCheck (Implementation! passive_impl, Checker! check) { - m_Check = check; - if (!VC.DCGen.UseItAsDebugger ) { - m_Order = new InclusionOrder(passive_impl.Blocks[0]); - } else { - m_Order = new InclusionOrder((!)VC.DCGen.firstNonDebugBlock ); - } - Label2Absy = new Hashtable(); // This is only a dummy - m_Evc = new Evc(check); - base(); - Hashtable l2a = null; - VCExpr! vce = this.GenerateEVC(passive_impl, out l2a, check); - assert l2a!=null; - Label2Absy = l2a; -// vce = check.VCExprGen.Implies(vce, vce); - m_Evc.Initialize(vce); - } - - /* - Set b to the next block that needs to be checked. - - Returns false and set b to null if all blocks are checked. - - Has to be alternated with CheckLabel; might crash otherwise - */ - public bool GetNextBlock(out Block b) { - return m_Order.GetNextBlock(out b); - } - - /* - Checking a label means to ask the prover if |= ( rvar=false -> vc ) holds. - - outcome is set to Outcome.Invalid if the Block denoted by reachvar is doomed. - - returns false if the theorem prover throws an exception, otherwise true. - */ - public bool CheckLabel(Variable! reachvar, out ProverInterface.Outcome outcome) { - - outcome = ProverInterface.Outcome.Undetermined; - if (m_Evc.CheckReachvar(reachvar, out outcome) ) { - if (!m_Order.SetCurrentResult(reachvar, outcome, m_ErrHandler)) { - outcome = ProverInterface.Outcome.Undetermined; - } - return true; - } else { - m_Order.SetCurrentResult(reachvar, ProverInterface.Outcome.Undetermined, m_ErrHandler); - return false; - } - } - - public List!>! DoomedSequences { - get { - return m_Order.DetectedBlock; - } - } - - - #region Error Verification Condition Generation - /* - #region _TESTING_NEW_STUFF_ - CommandLineOptions.Clo.vcVariety = CommandLineOptions.VCVariety.Block; - //VCExpr wp = Wlp.Block(block, SuccCorrect, context); // Computes wp.S.true - - CommandLineOptions.Clo.vcVariety = CommandLineOptions.VCVariety.Doomed; - #endregion - - */ - private bool _tmpUseFreshBVars; - - VCExpr! GenerateEVC(Implementation! impl, out Hashtable label2absy, Checker! ch) - { - TypecheckingContext tc = new TypecheckingContext(null); - impl.Typecheck(tc); - label2absy = new Hashtable/**/(); - VCExpr! vc; - switch (CommandLineOptions.Clo.vcVariety) { - case CommandLineOptions.VCVariety.Doomed: - - if (!VC.DCGen.UseItAsDebugger ) { - vc = LetVC((!)impl.Blocks[0], label2absy, ch.TheoremProver.Context); - } - #region _TESTING_NEW_STUFF_ - else { - - - CommandLineOptions.Clo.vcVariety = CommandLineOptions.VCVariety.Block; - assert VC.DCGen.firstDebugBlock != null; - VCExpr! a = LetVC(VC.DCGen.firstDebugBlock, label2absy, ch.TheoremProver.Context); - CommandLineOptions.Clo.vcVariety = CommandLineOptions.VCVariety.Doomed; - - - VCExpr! b = LetVC((!)VC.DCGen.firstNonDebugBlock, label2absy, ch.TheoremProver.Context); - - //_tmpUseFreshBVars=false; - //vc = ch.VCExprGen.Not(ch.VCExprGen.Implies( wp, ch.VCExprGen.Not(vc))); - - vc = ch.VCExprGen.Not(ch.VCExprGen.Implies(ch.VCExprGen.Not(a), ch.VCExprGen.Not(b))); - - - /* - ConsoleColor col = Console.ForegroundColor; - Console.ForegroundColor = ConsoleColor.Green; - Console.WriteLine(vc.ToString()); - Console.ForegroundColor = col; - Console.WriteLine(" ... was asked."); - */ - - } - #endregion - - break; - - default: - assert false; // unexpected enumeration value - } - return vc; - } - - public VCExpr! LetVC(Block! startBlock, - Hashtable/**/! label2absy, - ProverContext! proverCtxt) - { - Hashtable/**/! blockVariables = new Hashtable/**/(); - List! bindings = new List(); - VCExpr startCorrect = LetVC(startBlock, label2absy, blockVariables, bindings, proverCtxt); - if (CommandLineOptions.Clo.vcVariety == CommandLineOptions.VCVariety.Doomed) { - return proverCtxt.ExprGen.Let(bindings, proverCtxt.ExprGen.Not(startCorrect) ); - } else { - return proverCtxt.ExprGen.Let(bindings, startCorrect ); - } - } - - VCExpr! LetVC(Block! block, - Hashtable/**/! label2absy, - Hashtable/**/! blockVariables, - List! bindings, - ProverContext! proverCtxt) - { - VCExpressionGenerator! gen = proverCtxt.ExprGen; - 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) { - if (CommandLineOptions.Clo.vcVariety == CommandLineOptions.VCVariety.Doomed) { - SuccCorrect = VCExpressionGenerator.False; - } else { - SuccCorrect = VCExpressionGenerator.True; - } - } else { - assert gotocmd.labelTargets != null; - List SuccCorrectVars = new List(gotocmd.labelTargets.Length); - foreach (Block! successor in gotocmd.labelTargets) { - VCExpr s = LetVC(successor, label2absy, blockVariables, bindings, proverCtxt); - SuccCorrectVars.Add(s); - } - SuccCorrect = gen.NAry(VCExpressionGenerator.AndOp, SuccCorrectVars); - } - - VCContext context = new VCContext(label2absy, proverCtxt); - // m_Context = context; - - VCExpr vc = Wlp.Block(block, SuccCorrect, context); - - v = gen.Variable(block.Label + "_correct", Microsoft.Boogie.Type.Bool); - - bindings.Add(gen.LetBinding(v, vc)); - blockVariables.Add(block, v); - } - return v; - } - - - #endregion - - - #region Build Inclusion Order to minimize the number of checks - class InclusionOrder - { - - #region Attributes - public List!>! DetectedBlock = new List!>(); - - InclusionTree! RootNode = new InclusionTree(null); - InclusionTree currentNode = null; - - #endregion - - [NotDelayed] - public InclusionOrder(Block! rootblock) { - /* - We now compute for each block the set of blocks that - are reached on every execution reaching this block (dominator). - We first compute it form the start block to the current - block and second from the Term block to the current one. - Finally we build the union. - */ - - base(); - Dictionary! map2 = new Dictionary(); - Dictionary! map = new Dictionary(); - - Dictionary!>! unavoidablemap = new Dictionary!>(); - - Block! exitblock = BreadthFirst(rootblock, map2); - BreadthFirstBwd(exitblock, map); - - foreach (KeyValuePair kvp in map) { - List! blist = new List(); - foreach (TraceNode tn in kvp.Value.Unavoidables ) { - blist.Add(tn.block); - } - unavoidablemap.Add(kvp.Key, blist); - } - foreach (KeyValuePair!> kvp in unavoidablemap) { - TraceNode tn = null; - if (map2.TryGetValue(kvp.Key, out tn) ) { - assert tn!=null; - foreach (TraceNode! t0 in tn.Unavoidables) { - if (!kvp.Value.Contains(t0.block)) - kvp.Value.Add(t0.block); - } - } else { - //assert false; - } - } - - foreach (KeyValuePair!> kvp in unavoidablemap) { - Insert2Tree(RootNode,kvp); - } - InitCurrentNode(RootNode); - //printtree(RootNode, "",0); - } - - void InitCurrentNode(InclusionTree! n) { - if (n.Children.Count>0) { - InitCurrentNode(n.Children[0]); - } else { - currentNode = n; - } - } - - public bool GetNextBlock(out Block b) { - if (currentNode!=null && currentNode.EquivBlock.Count>0) { - b = currentNode.EquivBlock[0]; - return true; - } - b = null; - return false; - } - - - /* - Warning: this is a very slow implementation. There should be - a map from Block to InclusionTree to prevent running through - the tree over and over again. - */ - private void MarkUndoomedFromCE(Block! b, InclusionTree! node) - { - if (node.EquivBlock.Contains(b) ) { - //Console.WriteLine("Block {0} does not need to be checked - appears in CE", b.Label); - node.HasBeenChecked = true; - node.IsDoomed = false; - MarkUndoomedParents(node); - return; - } else - { - foreach (InclusionTree! c in node.Children) - { - MarkUndoomedFromCE(b, c); - } - } - } - - // Takes the outcome for the current reachvar and marks all blocks - // the do not need any further checking because they share the same - // desteny - // returns false if an explicite assert false was found. - // Warning: Still slow; we do not need to do this while constructing the error report! - public bool SetCurrentResult(Variable! reachvar, ProverInterface.Outcome outcome, DoomErrorHandler cb) { - if (outcome!=ProverInterface.Outcome.Valid) { - if (currentNode != null) { - currentNode.IsDoomed = false; - currentNode.HasBeenChecked = true; - MarkUndoomedParents(currentNode); - if (cb != null) { - //Console.WriteLine("CE contains: "); - foreach (Block! b in cb.TraceNodes) - { - //Console.Write("{0}, ", b.Label); - MarkUndoomedFromCE(b, RootNode); - } - //Console.WriteLine(); - } else { - Console.WriteLine("ErrorHandler is not valid! ------ (DoomCheck)"); - } - - currentNode = FindNextNode(currentNode); - } - } else { - if (currentNode != null) { - - // Check if there is an assert false in this node or in one of its parents - // if so do not report anything -// if (ECContainsAssertFalse(currentNode.EquivBlock)) { -// -// ConsoleColor col = Console.ForegroundColor; -// Console.ForegroundColor = ConsoleColor.Blue; -// Console.WriteLine("Assert false or PossiblyUnreachable was detected, but ignored"); -// Console.ForegroundColor = col; -// -// currentNode.HasBeenChecked = true; -// currentNode.IsDoomed = false; -// currentNode = currentNode.Parent; -// return false; -// } - - List! traceblocks = new List(); - - TracedChildern(currentNode, traceblocks); - TracedParents(currentNode, traceblocks); - -// cb.OnWarning("Doomed program points found!"); - if (cb != null) { - cb.OnDoom(reachvar, currentNode.EquivBlock, traceblocks); - } - - if (currentNode.EquivBlock.Count>0) { - - foreach (InclusionTree! n in currentNode.Children) { - if (DetectedBlock.Contains(n.EquivBlock) ) { - DetectedBlock.Remove(n.EquivBlock); - } - } - - DetectedBlock.Add(currentNode.EquivBlock); - - } else { - Console.WriteLine("An empty equivalence class has been detected"); - assert false; - } - - currentNode.IsDoomed = true; - currentNode.HasBeenChecked = true; - MarkDoomedChildren(currentNode); - currentNode = currentNode.Parent; - if (currentNode!=null) { - foreach (InclusionTree! it in currentNode.Children) { - if (!it.HasBeenChecked) { - currentNode = DescendToDeepestUnchecked(it); - break; - } - } - } - } - } - return true; - } - - private InclusionTree! DescendToDeepestUnchecked(InclusionTree! node) - { - foreach (InclusionTree! it in node.Children) { - if (!it.HasBeenChecked) { - return DescendToDeepestUnchecked(it); - } - } - return node; - } - - private bool ECContainsAssertFalse(List! equiv) { - foreach (Block! b in equiv) { - if (BlockContainsAssertFalse(b)) return true; - } - return false; - } - - private bool BlockContainsAssertFalse(Block! b) { - foreach (Cmd! c in b.Cmds) { - AssertCmd ac = c as AssertCmd; - if (ac!=null) { - if (IsFalse(ac.Expr) || QKeyValue.FindBoolAttribute(ac.Attributes, "PossiblyUnreachable") ) { - return true; - } - } - } - return false; - } - - // 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) - { - 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((!)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 && - ((!)call.Args[0]).Equals(call.Args[1])) - { - val = false; - return true; - } - - return false; - } - - bool IsFalse(Expr! e) - { - bool val = false; - return BooleanEval(e, ref val) && !val; - } - - private void TracedChildern(InclusionTree! node, List! blist) { - foreach (Block! b in node.EquivBlock) { - if (!blist.Contains(b)) blist.Add(b); - } - foreach (InclusionTree! n in node.Children) { - TracedChildern(n, blist); - } - } - - private void TracedParents(InclusionTree! node, List! blist) { - foreach (Block! b in node.EquivBlock) { - if (!blist.Contains(b)) blist.Add(b); - } - if (node.Parent!=null) { - TracedParents(node.Parent, blist); - } - } - - InclusionTree FindNextNode(InclusionTree! n) { - assert n!=n.Parent; - InclusionTree next = n.Parent; - if (next!=null) { - foreach (InclusionTree! n0 in next.Children) { - if (!n0.HasBeenChecked) { - return n0; - } - } - return FindNextNode(next); - } - return next; - } - - void MarkUndoomedParents(InclusionTree! n) { - if (n.Parent != null) { - n.Parent.HasBeenChecked = true; - n.Parent.IsDoomed = false; - MarkUndoomedParents(n.Parent); - } - } - - void MarkDoomedChildren(InclusionTree! n) { - foreach (InclusionTree! t in n.Children) { - t.IsDoomed = true; - t.HasBeenChecked = true; - MarkDoomedChildren(t); - } - } - - bool Insert2Tree(InclusionTree! node, KeyValuePair!> kvp) { - if (IsSubset(node.PathBlocks, kvp.Value) ) { - if (IsSubset(kvp.Value, node.PathBlocks) ) { - // The set of unavoidable blocks is equal, so - // we can put the block in the same node. - node.EquivBlock.Add(kvp.Key); - return true; - } else { - foreach (InclusionTree! n in node.Children) { - if (Insert2Tree(n,kvp) ) { - return true; - } - } - // we have not been able to add the block to one of - // the children, so we have to create a new child. - InclusionTree! it = new InclusionTree(node); - it.EquivBlock.Add(kvp.Key); - it.PathBlocks.AddRange(kvp.Value); - node.Children.Add(it); - return true; - } - // If we reached this point, we have to add a new node since - // our current set of pathnodes is not a subset of anything else - } else { - // seems, that we have reached a new leaf. - } - return false; - } - - bool IsSubset(List! sub, List! super ) { - foreach (Block! b in sub) { - if (!super.Contains(b) ) return false; - } - return true; - } - - void printtree(InclusionTree! n, string indent, int level) { - Console.Write("{0}Level {1}: Blocks ", indent, level); - foreach (Block! b in n.EquivBlock) { - Console.Write("{0}, ", b.Label); - } - Console.WriteLine(); - - foreach (InclusionTree! t in n.Children) { - printtree(t, indent+" ", level+1); - } - } - - private class InclusionTree { - public InclusionTree(InclusionTree p) { - Parent = p; - HasBeenChecked=false; - IsDoomed = false; - } - - public bool HasBeenChecked; - public bool IsDoomed; - public InclusionTree Parent; - public List! EquivBlock = new List(); - public List! PathBlocks = new List(); - public List! Children = new List(); - } - - #region Collect Unavoidable Blocks - private Block! BreadthFirst(Block! start, Dictionary! blockmap) { - List! JobList = new List(); - List! DoneList = new List(); - Block exitblock=null; - JobList.Add(start); - Block! currentBlock = JobList[0]; - // Travers the Graph Breadth First and collect all - // predecessors of each node that are reached on any - // path to this node - while (JobList.Count>0) - { - currentBlock = JobList[0]; - TraceNode! tn = new TraceNode(currentBlock); - - if (currentBlock.Predecessors.Length>0 ) { - TraceNode t0 =null; - Block firstpred = currentBlock.Predecessors[0]; - - assert firstpred!=null; - if (blockmap.TryGetValue(firstpred, out t0)) { - assert t0 !=null; - tn.Unavoidables.AddRange(t0.Unavoidables); - } - } - - foreach (Block! b0 in currentBlock.Predecessors) { - TraceNode t = null; - if (blockmap.TryGetValue(b0, out t)) { - assert t!=null; - tn.Predecessors.Add(t); - IntersectUnavoidables(t,tn); - if (!t.Successors.Contains(tn)) t.Successors.Add(tn); - blockmap[b0]=t; - } - } - if (!tn.Unavoidables.Contains(tn)) { - tn.Unavoidables.Add(tn); - } else - { - assert false; - } - - - blockmap.Add(currentBlock, tn); - - GotoCmd gc = currentBlock.TransferCmd as GotoCmd; - if (gc!=null) { - assert gc.labelTargets!=null; - foreach (Block! b0 in gc.labelTargets) { - if (!JobList.Contains(b0) && !DoneList.Contains(b0)) { - - JobList.Add(b0); - } - } - } else { - exitblock=currentBlock; - } - DoneList.Add(currentBlock); - JobList.RemoveAt(0); - } - assert exitblock!=null; - return exitblock; - } - - // WARNING: It is only for testing reasons that - // BreadthFirstBwd and BreadthFirst and separat functions - // it should be implemented using one function later on. - private void BreadthFirstBwd(Block! start, Dictionary! blockmap) { - List! JobList = new List(); - List! DoneList = new List(); - JobList.Add(start); - Block! currentBlock = JobList[0]; - // Travers the Graph Breadth First and collect all - // predecessors of each node that are reached on any - // path to this node - while (JobList.Count>0) - { - currentBlock = JobList[0]; - TraceNode! tn = new TraceNode(currentBlock); - - GotoCmd gc = currentBlock.TransferCmd as GotoCmd; - BlockSeq preds = null; - if (gc!=null) { - preds = gc.labelTargets; - } - - if (preds != null ) { - - TraceNode t0 =null; - Block firstpred = preds[0]; - - assert firstpred!=null; - if (blockmap.TryGetValue(firstpred, out t0)) { - assert t0 !=null; - tn.Unavoidables.AddRange(t0.Unavoidables); - } - - - foreach (Block! b0 in preds) { - TraceNode t = null; - if (blockmap.TryGetValue(b0, out t)) { - assert t!=null; - tn.Successors.Add(t); - IntersectUnavoidables(t,tn); - if (!t.Predecessors.Contains(tn)) t.Predecessors.Add(tn); - blockmap[b0]=t; - } - } - } - if (!tn.Unavoidables.Contains(tn)) { - tn.Unavoidables.Add(tn); - } else - { - assert false; - } - blockmap.Add(currentBlock, tn); - - if (currentBlock.Predecessors.Length>0) { - foreach (Block! b0 in currentBlock.Predecessors) { - if (!JobList.Contains(b0) && !DoneList.Contains(b0) ) - JobList.Add(b0); - } - } - DoneList.Add(currentBlock); - JobList.RemoveAt(0); - } - } - - private void IntersectUnavoidables(TraceNode! parent, TraceNode! child) { - List! ret = new List(); - List! tmp = new List(); - tmp.AddRange(parent.Unavoidables); - tmp.AddRange(child.Unavoidables); - - foreach (TraceNode! tn in tmp) { - if (parent.Unavoidables.Contains(tn) && child.Unavoidables.Contains(tn) - && !ret.Contains(tn) ) { - ret.Add(tn); - } - } - assert ret.Count <= parent.Unavoidables.Count && ret.Count <= child.Unavoidables.Count; - child.Unavoidables = ret; - - } - - #region TraceNode Class - // We assume that the program is already loopfree, otherwise we will - // not terminate - private class TraceNode { - public List! Predecessors = new List(); - public List! Successors = new List(); - public List! Unavoidables = new List(); - public Block! block; - - - public TraceNode(Block! b) { - block=b; - } - - } - #endregion - #endregion - } - #endregion - - - } -} diff --git a/Source/VCGeneration/DoomErrorHandler.cs b/Source/VCGeneration/DoomErrorHandler.cs new file mode 100644 index 00000000..860f5334 --- /dev/null +++ b/Source/VCGeneration/DoomErrorHandler.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; +using System.IO; +using Microsoft.Boogie; +using Graphing; +using AI = Microsoft.AbstractInterpretationFramework; +using Microsoft.Contracts; +using Microsoft.Basetypes; +using Microsoft.Boogie.VCExprAST; + +namespace VC +{ + internal class DoomErrorHandler : ProverInterface.ErrorHandler { + + protected Hashtable! label2Absy; + protected VerifierCallback! callback; + + public Variable m_Reachvar; + public List m_DoomedBlocks, m_TraceBlocks; + + + public DoomErrorHandler(Hashtable! label2Absy, VerifierCallback! callback) { + this.label2Absy = label2Absy; + this.callback = callback; + } + + public override Absy! Label2Absy(string! label) { + int id = int.Parse(label); + return (Absy!) label2Absy[id]; + } + + public override void OnProverWarning(string! msg) { + this.callback.OnWarning(msg); + } + + public void OnDoom(Variable! reachvar, List! doomedblocks, List! traceblocks) { + m_Reachvar = reachvar; + m_DoomedBlocks = doomedblocks; + m_TraceBlocks = traceblocks; + } + + private List! m_CurrentTrace = new List(); + + public List! TraceNodes + { + get + { + return m_CurrentTrace; + } + } + + public override void OnModel(IList! labels, ErrorModel errModel) { + m_CurrentTrace.Clear(); + //Console.Write("Used Blocks: "); + List traceNodes = new List(); + List assertNodes = new List(); + foreach (string! s in labels) { + Absy node = Label2Absy(s); + if (node is Block) { + Block b = (Block)node; + traceNodes.Add(b); + //Console.Write("{0}, ", b.Label); + } else { + AssertCmd a = (AssertCmd)node; + assertNodes.Add(a); + } + } + m_CurrentTrace.AddRange(traceNodes); + //Console.WriteLine(); +// assert assertNodes.Count > 0; +// 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.Has(a)) { +// BlockSeq trace = new BlockSeq(); +// trace.Add(b); +// goto NEXT_ASSERT; +// } +// } +// assert false; // there was no block that contains the assert +// NEXT_ASSERT: {} +// } + + } + + } + +} \ No newline at end of file diff --git a/Source/VCGeneration/DoomErrorHandler.ssc b/Source/VCGeneration/DoomErrorHandler.ssc deleted file mode 100644 index 860f5334..00000000 --- a/Source/VCGeneration/DoomErrorHandler.ssc +++ /dev/null @@ -1,93 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.Threading; -using System.IO; -using Microsoft.Boogie; -using Graphing; -using AI = Microsoft.AbstractInterpretationFramework; -using Microsoft.Contracts; -using Microsoft.Basetypes; -using Microsoft.Boogie.VCExprAST; - -namespace VC -{ - internal class DoomErrorHandler : ProverInterface.ErrorHandler { - - protected Hashtable! label2Absy; - protected VerifierCallback! callback; - - public Variable m_Reachvar; - public List m_DoomedBlocks, m_TraceBlocks; - - - public DoomErrorHandler(Hashtable! label2Absy, VerifierCallback! callback) { - this.label2Absy = label2Absy; - this.callback = callback; - } - - public override Absy! Label2Absy(string! label) { - int id = int.Parse(label); - return (Absy!) label2Absy[id]; - } - - public override void OnProverWarning(string! msg) { - this.callback.OnWarning(msg); - } - - public void OnDoom(Variable! reachvar, List! doomedblocks, List! traceblocks) { - m_Reachvar = reachvar; - m_DoomedBlocks = doomedblocks; - m_TraceBlocks = traceblocks; - } - - private List! m_CurrentTrace = new List(); - - public List! TraceNodes - { - get - { - return m_CurrentTrace; - } - } - - public override void OnModel(IList! labels, ErrorModel errModel) { - m_CurrentTrace.Clear(); - //Console.Write("Used Blocks: "); - List traceNodes = new List(); - List assertNodes = new List(); - foreach (string! s in labels) { - Absy node = Label2Absy(s); - if (node is Block) { - Block b = (Block)node; - traceNodes.Add(b); - //Console.Write("{0}, ", b.Label); - } else { - AssertCmd a = (AssertCmd)node; - assertNodes.Add(a); - } - } - m_CurrentTrace.AddRange(traceNodes); - //Console.WriteLine(); -// assert assertNodes.Count > 0; -// 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.Has(a)) { -// BlockSeq trace = new BlockSeq(); -// trace.Add(b); -// goto NEXT_ASSERT; -// } -// } -// assert false; // there was no block that contains the assert -// NEXT_ASSERT: {} -// } - - } - - } - -} \ No newline at end of file diff --git a/Source/VCGeneration/OrderingAxioms.cs b/Source/VCGeneration/OrderingAxioms.cs new file mode 100644 index 00000000..e4fc54f8 --- /dev/null +++ b/Source/VCGeneration/OrderingAxioms.cs @@ -0,0 +1,285 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) Microsoft Corporation. All Rights Reserved. +// +//----------------------------------------------------------------------------- +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Microsoft.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 { + + private readonly VCExpressionGenerator! Gen; + private readonly Boogie2VCExprTranslator! Translator; + + public OrderingAxiomBuilder(VCExpressionGenerator! gen, + Boogie2VCExprTranslator! translator) { + 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) { + 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 readonly IDictionary! OneStepFuns; + + private Function! OneStepFunFor(Type! t) { + Function res; + if (!OneStepFuns.TryGetValue(t, out res)) { + VariableSeq! args = new VariableSeq (); + 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 TypeVariableSeq (), args, result); + OneStepFuns.Add(t, res); + } + return (!)res; + } + + //////////////////////////////////////////////////////////////////////////// + + 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 (); + + private void AddAxiom(VCExpr! axiom) { + 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() { + CloseChildrenCompleteConstants(); + VCExpr! res = Gen.NAry(VCExpressionGenerator.AndOp, IncAxioms); + IncAxioms.Clear(); + return res; + } + + // return all axioms + public VCExpr! Axioms { get { + 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"); + List! typeParams = new List (); + typeParams.Add(alpha); + + List! triggers = new List (); + + VCExprVar! x = Gen.Variable("x", alpha); + VCExprVar! y = Gen.Variable("y", alpha); + VCExprVar! z = Gen.Variable("z", alpha); + + 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)); + 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) { + AddAxiom(GenParentConstraints(c)); + Constants.Add(c); + if (c.ChildrenComplete) + CompleteConstantsOpen.Add(c); + + // ensure that no further children are added to closed + // children-complete constants + assert !(c.Parents != null && + exists{ConstantParent! p in c.Parents; + ((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) { + 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) { + VCExprVar! par = Translator.LookupVariable((!)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) { + VCExprVar! par = Translator.LookupVariable((!)p.Parent.Decl); + VCExpr! antecedent1 = Gen.AtMost(cAsVar, w); + VCExpr! antecedent2 = Gen.AtMost(w, par); + VCExpr! body = Gen.Implies(Gen.And(antecedent1, antecedent2), + Gen.Or(Gen.Eq(cAsVar, w), Gen.Eq(par, w))); + 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); + foreach (ConstantParent! p in c.Parents) + minAncestors = + Gen.Or(minAncestors, + Gen.AtMost(Translator.LookupVariable((!)p.Parent.Decl), w)); + + VCExpr! antecedent = Gen.AtMost(cAsVar, w); + 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) { + if (p.Unique) + res = + Gen.AndSimp(res, + GenUniqueParentConstraint(c, (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) + requires c.ChildrenComplete; { + + VCExprVar! cAsVar = Translator.LookupVariable(c); + VCExprVar! w = Gen.Variable("w", c.TypedIdent.Type); + + VCExpr! maxDescendants = Gen.Eq(cAsVar, w); + foreach (Constant! d in Constants) { + if (d.Parents != null && + exists{ConstantParent! p in d.Parents; c.Equals(p.Parent.Decl)}) + maxDescendants = Gen.Or(maxDescendants, + Gen.AtMost(w, Translator.LookupVariable(d))); + } + + VCExpr! antecedent = Gen.AtMost(w, cAsVar); + return Gen.Forall(w, + Gen.Trigger(true, antecedent), + Gen.Implies(antecedent, maxDescendants)); + } + + private void CloseChildrenCompleteConstants() { + foreach (Constant! c in CompleteConstantsOpen) + 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) + requires child.TypedIdent.Type.Equals(parent.TypedIdent.Type); { + + VCExprVar! w = Gen.Variable("w", child.TypedIdent.Type); + + VCExpr! antecedent = + Gen.AtMost(w, Translator.LookupVariable(child)); + VCExpr! succedent = + Gen.Eq(Gen.Function(OneStepFunFor(child.TypedIdent.Type), + Translator.LookupVariable(parent), w), + Translator.LookupVariable(child)); + + return Gen.Forall(w, + Gen.Trigger(true, antecedent), + Gen.Implies(antecedent, succedent)); + } + + } + +} diff --git a/Source/VCGeneration/OrderingAxioms.ssc b/Source/VCGeneration/OrderingAxioms.ssc deleted file mode 100644 index e4fc54f8..00000000 --- a/Source/VCGeneration/OrderingAxioms.ssc +++ /dev/null @@ -1,285 +0,0 @@ -//----------------------------------------------------------------------------- -// -// Copyright (C) Microsoft Corporation. All Rights Reserved. -// -//----------------------------------------------------------------------------- -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -using Microsoft.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 { - - private readonly VCExpressionGenerator! Gen; - private readonly Boogie2VCExprTranslator! Translator; - - public OrderingAxiomBuilder(VCExpressionGenerator! gen, - Boogie2VCExprTranslator! translator) { - 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) { - 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 readonly IDictionary! OneStepFuns; - - private Function! OneStepFunFor(Type! t) { - Function res; - if (!OneStepFuns.TryGetValue(t, out res)) { - VariableSeq! args = new VariableSeq (); - 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 TypeVariableSeq (), args, result); - OneStepFuns.Add(t, res); - } - return (!)res; - } - - //////////////////////////////////////////////////////////////////////////// - - 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 (); - - private void AddAxiom(VCExpr! axiom) { - 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() { - CloseChildrenCompleteConstants(); - VCExpr! res = Gen.NAry(VCExpressionGenerator.AndOp, IncAxioms); - IncAxioms.Clear(); - return res; - } - - // return all axioms - public VCExpr! Axioms { get { - 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"); - List! typeParams = new List (); - typeParams.Add(alpha); - - List! triggers = new List (); - - VCExprVar! x = Gen.Variable("x", alpha); - VCExprVar! y = Gen.Variable("y", alpha); - VCExprVar! z = Gen.Variable("z", alpha); - - 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)); - 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) { - AddAxiom(GenParentConstraints(c)); - Constants.Add(c); - if (c.ChildrenComplete) - CompleteConstantsOpen.Add(c); - - // ensure that no further children are added to closed - // children-complete constants - assert !(c.Parents != null && - exists{ConstantParent! p in c.Parents; - ((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) { - 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) { - VCExprVar! par = Translator.LookupVariable((!)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) { - VCExprVar! par = Translator.LookupVariable((!)p.Parent.Decl); - VCExpr! antecedent1 = Gen.AtMost(cAsVar, w); - VCExpr! antecedent2 = Gen.AtMost(w, par); - VCExpr! body = Gen.Implies(Gen.And(antecedent1, antecedent2), - Gen.Or(Gen.Eq(cAsVar, w), Gen.Eq(par, w))); - 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); - foreach (ConstantParent! p in c.Parents) - minAncestors = - Gen.Or(minAncestors, - Gen.AtMost(Translator.LookupVariable((!)p.Parent.Decl), w)); - - VCExpr! antecedent = Gen.AtMost(cAsVar, w); - 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) { - if (p.Unique) - res = - Gen.AndSimp(res, - GenUniqueParentConstraint(c, (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) - requires c.ChildrenComplete; { - - VCExprVar! cAsVar = Translator.LookupVariable(c); - VCExprVar! w = Gen.Variable("w", c.TypedIdent.Type); - - VCExpr! maxDescendants = Gen.Eq(cAsVar, w); - foreach (Constant! d in Constants) { - if (d.Parents != null && - exists{ConstantParent! p in d.Parents; c.Equals(p.Parent.Decl)}) - maxDescendants = Gen.Or(maxDescendants, - Gen.AtMost(w, Translator.LookupVariable(d))); - } - - VCExpr! antecedent = Gen.AtMost(w, cAsVar); - return Gen.Forall(w, - Gen.Trigger(true, antecedent), - Gen.Implies(antecedent, maxDescendants)); - } - - private void CloseChildrenCompleteConstants() { - foreach (Constant! c in CompleteConstantsOpen) - 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) - requires child.TypedIdent.Type.Equals(parent.TypedIdent.Type); { - - VCExprVar! w = Gen.Variable("w", child.TypedIdent.Type); - - VCExpr! antecedent = - Gen.AtMost(w, Translator.LookupVariable(child)); - VCExpr! succedent = - Gen.Eq(Gen.Function(OneStepFunFor(child.TypedIdent.Type), - Translator.LookupVariable(parent), w), - Translator.LookupVariable(child)); - - return Gen.Forall(w, - Gen.Trigger(true, antecedent), - Gen.Implies(antecedent, succedent)); - } - - } - -} diff --git a/Source/VCGeneration/VC.cs b/Source/VCGeneration/VC.cs new file mode 100644 index 00000000..0f42939d --- /dev/null +++ b/Source/VCGeneration/VC.cs @@ -0,0 +1,4340 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) Microsoft Corporation. All Rights Reserved. +// +//----------------------------------------------------------------------------- +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; +using System.IO; +using Microsoft.Boogie; +using Graphing; +using AI = Microsoft.AbstractInterpretationFramework; +using Microsoft.Contracts; +using Microsoft.Basetypes; +using Microsoft.Boogie.VCExprAST; + + +namespace VC +{ + using Bpl = Microsoft.Boogie; + + public class VCGen : ConditionGeneration + { + + /// + /// Constructor. Initializes the theorem prover. + /// + [NotDelayed] + public VCGen(Program! program, string/*?*/ logFilePath, bool appendLogFile) + // throws ProverException + { + this.appendLogFile = appendLogFile; + this.logFilePath = logFilePath; + implName2LazyInliningInfo = new Dictionary(); + implName2StratifiedInliningInfo = new Dictionary(); + base(program); + if (CommandLineOptions.Clo.LazyInlining > 0) + { + this.GenerateVCsForLazyInlining(program); + } + if (CommandLineOptions.Clo.StratifiedInlining > 0) + { + + this.GenerateVCsForStratifiedInlining(program); + + } + // base(); + } + + private static AssumeCmd! AssertTurnedIntoAssume(AssertCmd! assrt) + { + Expr! expr = assrt.Expr; + + 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: + assert false; // unexpected case + } + + return new AssumeCmd(assrt.tok, expr); + } + + #region LazyInlining + public class LazyInliningInfo { + public Implementation! impl; + public int uniqueId; + public Function! function; + public Variable! controlFlowVariable; + public List! interfaceVars; + public Expr! assertExpr; + public VCExpr vcexpr; + public Dictionary incarnationOriginMap; + public Hashtable /*Variable->Expr*/ exitIncarnationMap; + public Hashtable /*GotoCmd->returnCmd*/ gotoCmdOrigins; + public Hashtable/**/ label2absy; + + public LazyInliningInfo(Implementation! impl, Program! program, int uniqueId) + { + this.impl = impl; + this.uniqueId = uniqueId; + this.controlFlowVariable = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, "cfc", Microsoft.Boogie.Type.Int)); + + Procedure! proc = (!)impl.Proc; + List! interfaceVars = new List(); + Expr! assertExpr = new LiteralExpr(Token.NoToken, true); + foreach (Variable! v in program.GlobalVariables()) + { + interfaceVars.Add(v); + } + // InParams must be obtained from impl and not proc + foreach (Variable! v in impl.InParams) + { + interfaceVars.Add(v); + } + // OutParams must be obtained from impl and not proc + foreach (Variable! v in impl.OutParams) + { + 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 (IdentifierExpr! e in 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)); + interfaceVars.Add(c); + 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); + VariableSeq! functionInterfaceVars = new VariableSeq(); + foreach (Variable! v in interfaceVars) + { + 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); + Formal! returnVar = new Formal(Token.NoToken, ti, false); + this.function = new Function(Token.NoToken, proc.Name, functionInterfaceVars, returnVar); + } + } + + private Dictionary! implName2LazyInliningInfo; + + public void GenerateVCsForLazyInlining(Program! program) + { + Checker! checker = FindCheckerFor(null, CommandLineOptions.Clo.ProverKillTime); + foreach (Declaration! decl in program.TopLevelDeclarations) + { + Implementation impl = decl as Implementation; + if (impl == null) continue; + Procedure! proc = (!)impl.Proc; + if (proc.FindExprAttribute("inline") != null) { + LazyInliningInfo info = new LazyInliningInfo(impl, program, QuantifierExpr.GetNextSkolemId()); + implName2LazyInliningInfo[impl.Name] = info; + impl.LocVars.Add(info.controlFlowVariable); + ExprSeq! exprs = new ExprSeq(); + foreach (Variable! v in program.GlobalVariables()) + { + exprs.Add(new OldExpr(Token.NoToken, new IdentifierExpr(Token.NoToken, v))); + } + foreach (Variable! v in proc.InParams) + { + exprs.Add(new IdentifierExpr(Token.NoToken, v)); + } + foreach (Variable! v in proc.OutParams) + { + exprs.Add(new IdentifierExpr(Token.NoToken, v)); + } + foreach (IdentifierExpr! ie in proc.Modifies) + { + if (ie.Decl == null) continue; + exprs.Add(ie); + } + Expr freePostExpr = new NAryExpr(Token.NoToken, new FunctionCall(info.function), exprs); + proc.Ensures.Add(new Ensures(true, freePostExpr)); + } + } + + foreach (LazyInliningInfo! info in implName2LazyInliningInfo.Values) + { + GenerateVCForLazyInlining(program, info, checker); + } + } + + private void GenerateVCForLazyInlining(Program! program, LazyInliningInfo! info, Checker! checker) + requires info.impl != null; + requires info.impl.Proc != null; + { + Implementation! impl = info.impl; + ConvertCFG2DAG(impl, program); + info.gotoCmdOrigins = PassifyImpl(impl, program); + assert info.exitIncarnationMap != null; + Hashtable/**/! label2absy; + VCExpressionGenerator! gen = checker.VCExprGen; + VCExpr! vcexpr = gen.Not(GenerateVC(impl, info.controlFlowVariable, out label2absy, checker)); + info.label2absy = label2absy; + + Boogie2VCExprTranslator! translator = checker.TheoremProver.Context.BoogieExprTranslator; + List privateVars = new List(); + foreach (Variable! v in impl.LocVars) + { + privateVars.Add(translator.LookupVariable(v)); + } + foreach (Variable! v in impl.OutParams) + { + privateVars.Add(translator.LookupVariable(v)); + } + if (privateVars.Count > 0) + { + vcexpr = gen.Exists(new List(), privateVars, new List(), + new VCQuantifierInfos(impl.Name, info.uniqueId, false, null), vcexpr); + } + + List interfaceExprVars = new List(); + List interfaceExprs = new List(); + foreach (Variable! v in info.interfaceVars) + { + VCExprVar! ev = translator.LookupVariable(v); + interfaceExprVars.Add(ev); + interfaceExprs.Add(ev); + } + + Function! function = (!)info.function; + VCExpr! expr = gen.Function(function, interfaceExprs); + if (CommandLineOptions.Clo.LazyInlining == 1) { + vcexpr = gen.Implies(expr, vcexpr); + } else { + assert CommandLineOptions.Clo.LazyInlining == 2; + vcexpr = gen.Eq(expr, vcexpr); + } + + List triggers = new List(); + List exprs = new List(); + exprs.Add(expr); + VCTrigger! trigger = new VCTrigger(true, exprs); + triggers.Add(trigger); + + Expr e = new LiteralExpr(Token.NoToken, BigNum.FromInt(1)); + QKeyValue q = new QKeyValue(Token.NoToken, "weight", new List(new object![] { e }), null); + interfaceExprVars.Reverse(); + vcexpr = gen.Forall(new List(), interfaceExprVars, triggers, + new VCQuantifierInfos(impl.Name, QuantifierExpr.GetNextSkolemId(), false, q), vcexpr); + + info.vcexpr = vcexpr; + checker.TheoremProver.PushVCExpression(vcexpr); + } + #endregion + + #region StratifiedInlining + public class StratifiedInliningInfo : LazyInliningInfo + { + public int inline_cnt; + public List! privateVars; + public List! interfaceExprVars; + public VCExpr funcExpr; + public VCExpr falseExpr; + + public StratifiedInliningInfo(Implementation! impl, Program! program, int uniqueid) + : base(impl, program, uniqueid) + { + inline_cnt = 0; + privateVars = new List(); + interfaceExprVars = new List(); + } + + } + + private Dictionary! implName2StratifiedInliningInfo; + + public void GenerateVCsForStratifiedInlining(Program! program) + { + //Checker! checker = FindCheckerFor(null, CommandLineOptions.Clo.ProverKillTime); + foreach (Declaration! decl in program.TopLevelDeclarations) + { + Implementation impl = decl as Implementation; + if (impl == null) continue; + Procedure! proc = (!)impl.Proc; + if (proc.FindExprAttribute("inline") != null) { + StratifiedInliningInfo info = new StratifiedInliningInfo(impl, program, QuantifierExpr.GetNextSkolemId()); + implName2StratifiedInliningInfo[impl.Name] = info; + // We don't need controlFlowVariable for stratified Inlining + //impl.LocVars.Add(info.controlFlowVariable); + ExprSeq! exprs = new ExprSeq(); + foreach (Variable! v in program.GlobalVariables()) + { + exprs.Add(new OldExpr(Token.NoToken, new IdentifierExpr(Token.NoToken, v))); + } + foreach (Variable! v in proc.InParams) + { + exprs.Add(new IdentifierExpr(Token.NoToken, v)); + } + foreach (Variable! v in proc.OutParams) + { + exprs.Add(new IdentifierExpr(Token.NoToken, v)); + } + foreach (IdentifierExpr! ie in proc.Modifies) + { + if (ie.Decl == null) continue; + exprs.Add(ie); + } + Expr freePostExpr = new NAryExpr(Token.NoToken, new FunctionCall(info.function), exprs); + proc.Ensures.Add(new Ensures(true, freePostExpr)); + } + } + + } + + private void GenerateVCForStratifiedInlining(Program! program, StratifiedInliningInfo! info, Checker! checker) + requires info.impl != null; + requires info.impl.Proc != null; + { + Implementation! impl = info.impl; + ConvertCFG2DAG(impl, program); + info.gotoCmdOrigins = PassifyImpl(impl, program); + assert info.exitIncarnationMap != null; + Hashtable/**/! label2absy; + VCExpressionGenerator! gen = checker.VCExprGen; + VCExpr! vcexpr = gen.Not(GenerateVC(impl, null, out label2absy, checker)); + info.label2absy = label2absy; + + Boogie2VCExprTranslator! translator = checker.TheoremProver.Context.BoogieExprTranslator; + info.privateVars = new List(); + foreach (Variable! v in impl.LocVars) + { + info.privateVars.Add(translator.LookupVariable(v)); + } + foreach (Variable! v in impl.OutParams) + { + info.privateVars.Add(translator.LookupVariable(v)); + } + + info.interfaceExprVars = new List(); + List interfaceExprs = new List(); + foreach (Variable! v in info.interfaceVars) + { + VCExprVar! ev = translator.LookupVariable(v); + info.interfaceExprVars.Add(ev); + interfaceExprs.Add(ev); + } + + Function! function = (!)info.function; + info.funcExpr = gen.Function(function, interfaceExprs); + info.vcexpr = vcexpr; + + // Build the "false" expression: forall a b c: foo(a,b,c) <==> false + + info.falseExpr = gen.Eq(info.funcExpr, VCExpressionGenerator.False); + + List triggers = new List(); + List exprs = new List(); + exprs.Add(info.funcExpr); + VCTrigger! trigger = new VCTrigger(true, exprs); + triggers.Add(trigger); + + Expr e = new LiteralExpr(Token.NoToken, BigNum.FromInt(1)); + QKeyValue q = new QKeyValue(Token.NoToken, "weight", new List(new object![] { e }), null); + //info.interfaceExprVars.Reverse(); + info.falseExpr = gen.Forall(new List(), info.interfaceExprVars, triggers, + new VCQuantifierInfos(impl.Name, QuantifierExpr.GetNextSkolemId(), false, q), info.falseExpr); + + //checker.TheoremProver.PushVCExpression(vcexpr); + /* + Console.WriteLine("Procedure: {0}", info.impl.Name); + Console.Write("For all: "); + foreach(VCExprVar! v in info.interfaceExprVars) { + Console.Write(v.ToString() + " "); + } + Console.WriteLine(); + Console.Write("There exists: "); + foreach(VCExprVar! v in info.privateVars) { + Console.Write(v.ToString() + " "); + } + Console.WriteLine(); + Console.WriteLine(vcexpr.ToString()); + */ + } + #endregion + + #region Soundness smoke tester + class SmokeTester + { + VCGen! parent; + Implementation! impl; + Block! initial; + Program! program; + int id; + Dictionary! copies = new Dictionary(); + Dictionary! visited = new Dictionary(); + VerifierCallback! callback; + + internal SmokeTester(VCGen! par, Implementation! i, Program! prog, VerifierCallback! callback) + { + parent = par; + impl = i; + initial = i.Blocks[0]; + program = prog; + this.callback = callback; + } + + internal void Copy() + { + CloneBlock(impl.Blocks[0]); + initial = GetCopiedBlocks()[0]; + } + + internal void Test() + throws UnexpectedProverOutputException; + { + DFS(initial); + } + + void TopologicalSortImpl() + { + Graph dag = new Graph(); + dag.AddSource((!)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) + { + assume gtc.labelTargets != null; + foreach (Block! dest in gtc.labelTargets) + { + dag.AddEdge(b,dest); + } + } + } + impl.Blocks = new List(); + foreach (Block! b in dag.TopologicalSort()) { + impl.Blocks.Add(b); + } + } + + void Emit() + { + TopologicalSortImpl(); + EmitImpl(impl, false); + } + + // this one copies forward + Block! CloneBlock(Block! b) + { + Block fake_res; + if (copies.TryGetValue(b, out fake_res)) { + return (!)fake_res; + } + Block! res; + res = new Block(b.tok, b.Label, new CmdSeq(b.Cmds), null); + copies[b] = res; + if (b.TransferCmd is GotoCmd) { + foreach (Block! ch in (!)((GotoCmd)b.TransferCmd).labelTargets) { + CloneBlock(ch); + } + } + foreach (Block! p in b.Predecessors) { + res.Predecessors.Add(CloneBlock(p)); + } + return res; + } + + // this one copies backwards + Block! CopyBlock(Block! b) + { + Block fake_res; + if (copies.TryGetValue(b, out fake_res)) { + // fake_res should be Block! but the compiler fails + return (!)fake_res; + } + Block! res; + CmdSeq seq = new CmdSeq(); + foreach (Cmd! c in b.Cmds) { + 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) { + res.Predecessors.Add(CopyBlock(p)); + } + return res; + } + + List! GetCopiedBlocks() + { + // 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) { + 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 StringSeq(), new BlockSeq()); + kv.Value.TransferCmd = copy; + foreach (Block! b in (!)go.labelTargets) { + Block c; + if (copies.TryGetValue(b, out c)) { + copy.AddTarget((!)c); + } + } + } else if (ret != null) { + kv.Value.TransferCmd = ret; + } else { + assume false; + } + } + + 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) + { + 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((!)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 && + ((!)call.Args[0]).Equals(call.Args[1])) + { + val = false; + return true; + } + + return false; + } + + bool IsFalse(Expr! e) + { + bool val = false; + return BooleanEval(e, ref val) && !val; + } + + bool CheckUnreachable(Block! cur, CmdSeq! seq) + throws UnexpectedProverOutputException; + { + foreach (Cmd cmd in seq) { + AssertCmd assrt = cmd as AssertCmd; + if (assrt != null && QKeyValue.FindBoolAttribute(assrt.Attributes, "PossiblyUnreachable")) + return false; + } + + DateTime start = DateTime.Now; + 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); + copy.Cmds = seq; + List! backup = impl.Blocks; + 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.current_impl = impl; + parent.PassifyImpl(impl, program); + Hashtable! label2Absy; + Checker! ch = parent.FindCheckerFor(impl, CommandLineOptions.Clo.SmokeTimeout); + VCExpr! vc = parent.GenerateVC(impl, null, out label2Absy, ch); + impl.Blocks = backup; + + if (CommandLineOptions.Clo.TraceVerify) { + System.Console.WriteLine(" --- smoke #{0}, after passify", id); + Emit(); + } + ch.BeginCheck((!) impl.Name + "_smoke" + id++, vc, new ErrorHandler(label2Absy, this.callback)); + ch.ProverDone.WaitOne(); + ProverInterface.Outcome outcome = ch.ReadOutcome(); + parent.current_impl = null; + + DateTime end = DateTime.Now; + 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) + throws UnexpectedProverOutputException; + { + if (visited.ContainsKey(cur)) return; + visited[cur] = true; + + CmdSeq! seq = new CmdSeq(); + foreach (Cmd! cmd_ in cur.Cmds) { + Cmd! cmd = cmd_; + 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 (turnAssertIntoAssumes) { + cmd = AssertTurnedIntoAssume(assrt); + } + } else if (assm != null) { + if (IsFalse(assm.Expr)) assumeFalse = true; + } else if (call != null) { + foreach (Ensures! e in ((!)call.Proc).Ensures) { + 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; + + assume !(go!= null&&go.labelTargets==null&&go.labelNames!=null&&go.labelNames.Length>0) ; + + if (ret != null || (go != null && ((!)go.labelTargets).Length == 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 (!)go.labelTargets) { + if (target.Predecessors.Length == 1) { + needToCheck = false; + } + } + if (needToCheck) { + CheckUnreachable(cur, seq); + } + foreach (Block! target in go.labelTargets) { + DFS(target); + } + } + } + + class ErrorHandler : ProverInterface.ErrorHandler { + Hashtable! label2Absy; + VerifierCallback! callback; + + public ErrorHandler(Hashtable! label2Absy, VerifierCallback! callback) { + this.label2Absy = label2Absy; + this.callback = callback; + } + + public override Absy! Label2Absy(string! label) { + int id = int.Parse(label); + return (Absy!) label2Absy[id]; + } + + public override void OnProverWarning(string! msg) { + 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 Dictionary? reachable_blocks; + public readonly Block! block; + + public BlockStats(Block! b, int i) + { + block = b; + assertion_cost = -1; + id = i; + } + } + + readonly List! blocks; + readonly List! big_blocks = new List(); + readonly Dictionary! stats = new Dictionary(); + readonly int id; + static int current_id; + Block? split_block; + bool assert_to_assume; + List! assumized_branches = new List(); + public AssertCmd? first_assert; + + double score; + bool score_computed; + double total_cost; + int assertion_count; + double assertion_cost; // without multiplication by paths + Hashtable/*TransferCmd->ReturnCmd*/! gotoCmdOrigins; + VCGen! parent; + Implementation! impl; + + Dictionary! copies = new Dictionary(); + bool doing_slice; + double slice_initial_limit; + double slice_limit; + bool slice_pos; + Dictionary! protected_from_assert_to_assume = new Dictionary(); + Dictionary! keep_at_all = new Dictionary(); + + // async interface + private Checker checker; + private int splitNo; + internal ErrorReporter reporter; + + public Split(List! blocks, Hashtable/*TransferCmd->ReturnCmd*/! gotoCmdOrigins, VCGen! par, Implementation! impl) + { + this.blocks = blocks; + this.gotoCmdOrigins = gotoCmdOrigins; + this.parent = par; + this.impl = impl; + this.id = 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; + assumized_branches = new List(); + DoComputeScore(false); + assumized_branches = saved; + + foreach (Block! b in big_blocks) { + BlockStats s = GetBlockStats(b); + foreach (Block! t in s.virtual_successors) { + 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; + impl.Blocks = blocks; + impl.Emit(new TokenTextWriter(filename, sw, false), 0); + impl.Blocks = backup; + CommandLineOptions.Clo.PrintDesugarings = oldPrintDesugaringSetting; + CommandLineOptions.Clo.PrintUnstructured = oldPrintUnstructured; + } + } + + int bsid; + BlockStats! GetBlockStats(Block! b) + { + BlockStats s; + if (!stats.TryGetValue(b, out s)) { + s = new BlockStats(b, bsid++); + stats[b] = s; + } + return (!)s; + } + + double AssertionCost(PredicateCmd c) + { + return 1.0; + } + + void CountAssertions(Block! b) + { + 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! b in Exits(b)) { + s.virtual_successors.Add(b); + } + if (s.virtual_successors.Count == 1) { + Block next = s.virtual_successors[0]; + BlockStats se = GetBlockStats(next); + CountAssertions(next); + if (next.Predecessors.Length > 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; + } + } + + Dictionary! ComputeReachableNodes(Block! b) + { + BlockStats s = GetBlockStats(b); + if (s.reachable_blocks != null) { + return s.reachable_blocks; + } + Dictionary blocks = new Dictionary(); + s.reachable_blocks = blocks; + blocks[b] = true; + foreach (Block! succ in Exits(b)) { + foreach (Block! r in ComputeReachableNodes(succ).Keys) { + blocks[r] = true; + } + } + 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) { + CountAssertions(b); + } + + foreach (Block! b in blocks) { + BlockStats bs = GetBlockStats(b); + if (bs.big_block) { + big_blocks.Add(b); + foreach (Block! ch in bs.virtual_successors) { + BlockStats chs = GetBlockStats(ch); + if (!chs.big_block) { + Console.WriteLine("non-big {0} accessed from {1}", ch, b); + DumpDot(-1); + assert false; + } + 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) { + GotoCmd gt = b.TransferCmd as GotoCmd; + if (gt == null) continue; + BlockSeq targ = (!)gt.labelTargets; + if (targ.Length < 2) continue; + // caution, we only consider two first exits + + double left0, right0, left1, right1; + split_block = b; + + assumized_branches.Clear(); + assumized_branches.Add((!)targ[0]); + left0 = DoComputeScore(true); + right0 = DoComputeScore(false); + + assumized_branches.Clear(); + for (int idx = 1; idx < targ.Length; idx++) { + assumized_branches.Add((!)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((!)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) + { + if (s.incomming_paths < 0.0) { + int count = 0; + s.incomming_paths = 0.0; + if (!keep_at_all.ContainsKey(s.block)) return; + foreach (Block! b in s.virtual_predecesors) { + BlockStats! ch = GetBlockStats(b); + 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) + { + if (keep_at_all.ContainsKey(b)) return; + keep_at_all[b] = true; + + if (allow_small) { + foreach (Block! ch in Exits(b)) { + if (b == split_block && assumized_branches.Contains(ch)) continue; + ComputeBlockSetsHelper(ch, allow_small); + } + } else { + foreach (Block! ch in GetBlockStats(b).virtual_successors) { + 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) { + if (ComputeReachableNodes(b).ContainsKey((!)split_block)) { + keep_at_all[b] = true; + } + } + + foreach (Block! b in assumized_branches) { + foreach (Block! r in ComputeReachableNodes(b).Keys) { + if (allow_small || GetBlockStats(r).big_block) { + keep_at_all[r] = true; + protected_from_assert_to_assume[r] = true; + } + } + } + } else { + ComputeBlockSetsHelper(blocks[0], allow_small); + } + } + + bool ShouldAssumize(Block! b) + { + return assert_to_assume && !protected_from_assert_to_assume.ContainsKey(b); + } + + double DoComputeScore(bool aa) + { + assert_to_assume = aa; + ComputeBlockSets(false); + + foreach (Block! b in big_blocks) { + GetBlockStats(b).incomming_paths = -1.0; + } + + GetBlockStats(blocks[0]).incomming_paths = 1.0; + + double cost = 0.0; + foreach (Block! b in big_blocks) { + if (keep_at_all.ContainsKey(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; + } + + CmdSeq! SliceCmds(Block! b) + { + CmdSeq! seq = b.Cmds; + if (!doing_slice && !ShouldAssumize(b)) return seq; + CmdSeq! res = new CmdSeq(); + foreach (Cmd! c in seq) { + 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 { + assert false; + } + + if (swap) { + the_new = AssertTurnedIntoAssume(a); + } + } + res.Add(the_new); + } + return res; + } + + Block! CloneBlock(Block! b) + { + Block res; + if (copies.TryGetValue(b, out res)) { + return (!)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 StringSeq(), new BlockSeq()); + res.TransferCmd = newGoto; + int pos = 0; + foreach (Block! ch in (!)gt.labelTargets) { + assert doing_slice || + (!assert_to_assume ==> (keep_at_all.ContainsKey(ch) || assumized_branches.Contains(ch))); + if (doing_slice || + ((b != split_block || assumized_branches.Contains(ch) == assert_to_assume) && + keep_at_all.ContainsKey(ch))) { + newGoto.AddTarget(CloneBlock(ch)); + } + pos++; + } + } + return res; + } + + Split! DoSplit() + { + copies.Clear(); + CloneBlock(blocks[0]); + List newBlocks = new List(); + Hashtable newGotoCmdOrigins = new Hashtable(); + foreach (Block! b in blocks) { + Block tmp; + if (copies.TryGetValue(b, out tmp)) { + newBlocks.Add((!)tmp); + if (gotoCmdOrigins.ContainsKey(b)) { + newGotoCmdOrigins[tmp] = gotoCmdOrigins[b]; + } + + foreach (Block! p in b.Predecessors) { + Block tmp2; + if (copies.TryGetValue(p, out tmp2)) { + tmp.Predecessors.Add(tmp2); + } + } + } + } + + return new Split(newBlocks, newGotoCmdOrigins, parent, impl); + } + + Split! SplitAt(int idx) + { + assert_to_assume = idx == 0; + doing_slice = false; + ComputeBlockSets(true); + + return DoSplit(); + } + + Split! SliceAsserts(double limit, bool pos) + { + 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; + impl.Blocks = blocks; + EmitImpl(impl, false); + impl.Blocks = tmp; + } + + public Counterexample! ToCounterexample() + { + BlockSeq trace = new BlockSeq(); + foreach (Block! b in blocks) { + trace.Add(b); + } + foreach (Block! b in blocks) { + foreach (Cmd! c in b.Cmds) { + if (c is AssertCmd) { + return AssertCmdToCounterexample((AssertCmd)c, (!)b.TransferCmd, trace, null, new Dictionary()); + } + } + } + assume false; + } + + public static List! DoSplit(Split! initial, double max_cost, int max) + { + 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) { + 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 (!)g.labelTargets) { + Console.Write("{0} ", b.Label); + } + Console.WriteLine(""); + Console.Write(" assumized: "); + foreach (Block! b in best.assumized_branches) { + 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 Dictionary(), best.blocks[0], ss); + } catch (System.Exception e) { + Console.WriteLine(e); + best.DumpDot(-1); + s0.DumpDot(-2); + s1.DumpDot(-3); + assert false; + } + } + + 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; + } + + public Checker! Checker + { + get { + assert checker != null; + return checker; + } + } + + public WaitHandle ProverDone + { + get { + assert checker != null; + return checker.ProverDone; + } + } + + public void ReadOutcome(ref Outcome cur_outcome, out bool prover_failed) + throws UnexpectedProverOutputException; + { + ProverInterface.Outcome outcome = ((!)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: + prover_failed = true; + if (cur_outcome != Outcome.Errors) + cur_outcome = Outcome.Inconclusive; + return; + default: + assert false; + } + } + + public void BeginCheck(VerifierCallback! callback, int no, int timeout) + { + splitNo = no; + + impl.Blocks = blocks; + + checker = parent.FindCheckerFor(impl, timeout); + + Hashtable/**/! label2absy; + VCExpr! vc = parent.GenerateVC(impl, null, out label2absy, checker); + + if (CommandLineOptions.Clo.vcVariety == CommandLineOptions.VCVariety.Local) { + reporter = new ErrorReporterLocal(gotoCmdOrigins, label2absy, impl.Blocks, parent.incarnationOriginMap, callback, parent.implName2LazyInliningInfo, (DeclFreeProverContext!) this.Checker.TheoremProver.Context, parent.program); + } else { + reporter = new ErrorReporter(gotoCmdOrigins, label2absy, impl.Blocks, parent.incarnationOriginMap, callback, parent.implName2LazyInliningInfo, (DeclFreeProverContext!) this.Checker.TheoremProver.Context, parent.program); + } + + if (CommandLineOptions.Clo.TraceVerify && no >= 0) + { + Console.WriteLine("-- after split #{0}", no); + Print(); + } + + string! desc = (!) impl.Name; + if (no >= 0) + desc += "_split" + no; + checker.BeginCheck(desc, vc, reporter); + } + + private void SoundnessCheck(Dictionary! cache, Block! orig, List! copies) + { + { + PureCollections.Tuple t = new PureCollections.Tuple(new PureCollections.Capacity(1 + copies.Count)); + int i = 0; + t[i++] = orig; + foreach (Block! b in copies) { + t[i++] = b; + } + if (cache.ContainsKey(t)) { return; } + cache[t] = true; + } + + for (int i = 0; i < orig.Cmds.Length; ++i) { + Cmd cmd = orig.Cmds[i]; + if (cmd is AssertCmd) { + int found = 0; + foreach (Block! c in copies) { + 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)) { + List! newcopies = new List(); + foreach (Block! c in copies) { + foreach (Block! cexit in Exits(c)) { + 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 + + + protected VCExpr! GenerateVC(Implementation! impl, Variable controlFlowVariable, out Hashtable/**/! label2absy, Checker! ch) + { + TypecheckingContext tc = new TypecheckingContext(null); + impl.Typecheck(tc); + + label2absy = new Hashtable/**/(); + VCExpr! vc; + switch (CommandLineOptions.Clo.vcVariety) { + case CommandLineOptions.VCVariety.Structured: + vc = VCViaStructuredProgram(impl, label2absy, ch.TheoremProver.Context); + break; + case CommandLineOptions.VCVariety.Block: + vc = FlatBlockVC(impl, label2absy, false, false, false, ch.TheoremProver.Context); + break; + case CommandLineOptions.VCVariety.BlockReach: + vc = FlatBlockVC(impl, label2absy, false, true, false, ch.TheoremProver.Context); + break; + case CommandLineOptions.VCVariety.Local: + vc = FlatBlockVC(impl, label2absy, true, false, false, ch.TheoremProver.Context); + break; + case CommandLineOptions.VCVariety.BlockNested: + vc = NestedBlockVC(impl, label2absy, false, ch.TheoremProver.Context); + break; + case CommandLineOptions.VCVariety.BlockNestedReach: + vc = NestedBlockVC(impl, label2absy, true, ch.TheoremProver.Context); + break; + case CommandLineOptions.VCVariety.Dag: + if (((!)CommandLineOptions.Clo.TheProverFactory).SupportsDags) { + vc = DagVC((!)impl.Blocks[0], label2absy, new Hashtable/**/(), ch.TheoremProver.Context); + } else { + vc = LetVC((!)impl.Blocks[0], controlFlowVariable, label2absy, ch.TheoremProver.Context); + } + break; + case CommandLineOptions.VCVariety.Doomed: + vc = FlatBlockVC(impl, label2absy, false, false, true, ch.TheoremProver.Context); + break; + default: + assert false; // unexpected enumeration value + } + return vc; + } + + void CheckIntAttributeOnImpl(Implementation! impl, string! name, ref int val) + { + if (!((!)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, Program! program, VerifierCallback! callback) + throws UnexpectedProverOutputException; + { + if (impl.SkipVerification) { + return Outcome.Inconclusive; // not sure about this one + } + + if (CommandLineOptions.Clo.StratifiedInlining > 0) { + return StratifiedVerifyImplementation(impl, program, callback); + } + + callback.OnProgress("VCgen", 0, 0, 0.0); + + ConvertCFG2DAG(impl, program); + + SmokeTester smoke_tester = null; + if (CommandLineOptions.Clo.SoundnessSmokeTest) { + smoke_tester = new SmokeTester(this, impl, program, callback); + smoke_tester.Copy(); + } + + Hashtable/*TransferCmd->ReturnCmd*/ gotoCmdOrigins = PassifyImpl(impl, program); + + 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; + + int cores = CommandLineOptions.Clo.VcsCores; + Stack work = new Stack(); + List currently_running = new List(); + ResetPredecessors(impl.Blocks); + 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.Count > 0 || currently_running.Count > 0) { + bool prover_failed = false; + Split! s; + + if (work.Count > 0 && currently_running.Count < cores) { + s = work.Pop(); + + if (first_round && max_splits > 1) { + prover_failed = true; + remaining_cost -= s.Cost; + } else { + 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)); + + s.BeginCheck(callback, no, + (keep_going && s.LastChance) ? CommandLineOptions.Clo.VcsFinalAssertTimeout : + keep_going ? CommandLineOptions.Clo.VcsKeepGoingTimeout : + CommandLineOptions.Clo.ProverKillTime); + + no++; + + currently_running.Add(s); + } + } else { + WaitHandle[] handles = new WaitHandle[currently_running.Count]; + for (int i = 0; i < currently_running.Count; ++i) { + handles[i] = currently_running[i].ProverDone; + } + int index = WaitHandle.WaitAny(handles); + s = currently_running[index]; + currently_running.RemoveAt(index); + + if (do_splitting) { + remaining_cost -= s.Cost; + } + + 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(), msg); + outcome = Outcome.Errors; + break; + } + + assert prover_failed || outcome == Outcome.Correct || outcome == Outcome.Errors; + } + + 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); + max_vc_cost = 1.0; // for future + first_round = false; + //tmp.Sort(new Comparison(Split.Compare)); + foreach (Split! a in tmp) { + work.Push(a); + total++; + remaining_cost += a.Cost; + } + if (outcome != Outcome.Errors) { + outcome = Outcome.Correct; + } + } else { + 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); + + return outcome; + } + + public override Outcome StratifiedVerifyImplementation(Implementation! impl, Program! program, VerifierCallback! callback) + throws UnexpectedProverOutputException; + { + // This flag control the nature of queries made by StratifiedVerifyImplementation + // true: incremental search; false: in-place inlining + bool incrementalSearch = true; + + // Get the checker + Checker! checker = FindCheckerFor(null, CommandLineOptions.Clo.ProverKillTime); + + // Run live variable analysis + if(CommandLineOptions.Clo.LiveVariableAnalysis == 2) { + Microsoft.Boogie.InterProcGenKill.ComputeLiveVars(impl, program); + } + + // Build VCs for all procedures + assert implName2StratifiedInliningInfo != null; + foreach(StratifiedInliningInfo! info in implName2StratifiedInliningInfo.Values) + { + GenerateVCForStratifiedInlining(program, info, checker); + } + + // Get the VC of the current procedure + VCExpr! vc; + StratifiedInliningErrorReporter! reporter; + Hashtable/**/! mainLabel2absy; + GetVC(impl, program, callback, out vc, out mainLabel2absy, out reporter); + + // Find all procedure calls in vc and put labels on them + FCallHandler calls = new FCallHandler(checker.VCExprGen, implName2StratifiedInliningInfo, mainLabel2absy); + calls.setCurrProcAsMain(); + vc = calls.Mutate(vc, true); + reporter.SetCandidateHandler(calls); + + Outcome ret = Outcome.Correct; + + int expansionCount = 0; + int total_axioms_pushed = 0; + + // Do 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) { + toExpand.Add(id); + } + expansionCount += toExpand.Count; + if(incrementalSearch) + { + total_axioms_pushed += + DoExpansion(toExpand, calls, checker); + } else + { + vc = DoExpansionAndInline(vc, toExpand, calls, checker); + } + } + + int cnt = 0; + while(cnt < 500) { + cnt ++; + + // Push "false" + checker.TheoremProver.LogComment(";;;;;;;;;;;; Underapprox mode begin ;;;;;;;;;;"); + + int prev_axioms_pushed = checker.TheoremProver.NumAxiomsPushed(); + + foreach(int id in calls.currCandidates) { + VCExprNAry! vce = calls.id2Candidate[id]; + VCExpr! falseExpr = checker.VCExprGen.Eq(vce, VCExpressionGenerator.False); + checker.TheoremProver.PushVCExpression(falseExpr); + } + int axioms_pushed = checker.TheoremProver.NumAxiomsPushed(); + + // Note: axioms_pushed may not be the same as calls.currCandidates.Count + // because PushVCExpression pushes other stuff too (which always seems + // to be TRUE) + + reporter.underapproximationMode = true; + + // Check! + //Console.Write("Checking with preds == false: "); Console.Out.Flush(); + ret = CheckVC(vc, reporter, checker, impl.Name); + //Console.WriteLine(ret.ToString()); + + // Pop + for(int i = 0; i < axioms_pushed - prev_axioms_pushed; i++) { + checker.TheoremProver.Pop(); + } + + checker.TheoremProver.LogComment(";;;;;;;;;;;; Underapprox mode end ;;;;;;;;;;"); + + if(ret == Outcome.Errors) { + break; + } + + // If we didn't underapproximate, then we're done + if(calls.currCandidates.Count == 0) { + break; + } + + checker.TheoremProver.LogComment(";;;;;;;;;;;; Overapprox mode begin ;;;;;;;;;;"); + // Push "true" (No-op) + // Check + reporter.underapproximationMode = false; + + //Console.Write("Checking with preds == true: "); Console.Out.Flush(); + ret = CheckVC(vc, reporter, checker, impl.Name); + //Console.WriteLine(ret.ToString()); + + if(ret == Outcome.Correct) { + break; + } + + checker.TheoremProver.LogComment(";;;;;;;;;;;; Overapprox mode end ;;;;;;;;;;"); + + checker.TheoremProver.LogComment(";;;;;;;;;;;; Expansion begin ;;;;;;;;;;"); + + // Look at the errors to see what to inline + assert reporter.candidatesToExpand.Count != 0; + + expansionCount += reporter.candidatesToExpand.Count; + + if(incrementalSearch) + { + total_axioms_pushed += + DoExpansion(reporter.candidatesToExpand, calls, checker); + } else + { + vc = DoExpansionAndInline(vc, reporter.candidatesToExpand, calls, checker); + } + + checker.TheoremProver.LogComment(";;;;;;;;;;;; Expansion end ;;;;;;;;;;"); + } + + // Pop off everything that we pushed so that there are no side effects from + // this call to VerifyImplementation + for(int i = 0; i < total_axioms_pushed; i++) { + checker.TheoremProver.Pop(); + } + + if(cnt == 500) + { + checker.TheoremProver.LogComment("Stratified Inlining: timeout"); + } + + checker.TheoremProver.LogComment(string.Format("Stratified Inlining: Calls to Z3: {0}", 2*cnt)); + checker.TheoremProver.LogComment(string.Format("Stratified Inlining: Expansions performed: {0}", expansionCount)); + checker.TheoremProver.LogComment(string.Format("Stratified Inlining: Candidates left: {0}", calls.currCandidates.Count)); + + return ret; + } + + // A counter for adding new variables + static int newVarCnt = 0; + + // Does on-demand inlining -- pushes procedure bodies on the theorem prover stack. + // Returns the number of axioms pushed. + private int DoExpansion(List! candidates, + FCallHandler! calls, Checker! checker) + throws UnexpectedProverOutputException; + { + int old_axioms_pushed = checker.TheoremProver.NumAxiomsPushed(); + VCExpr! exprToPush = VCExpressionGenerator.True; + foreach(int id in candidates) { + VCExprNAry! expr = calls.id2Candidate[id]; + string procName = ((!)(expr.Op as VCExprBoogieFunctionOp)).Func.Name; + if(!implName2StratifiedInliningInfo.ContainsKey(procName)) continue; + + //Console.WriteLine("Expanding: {0}", expr.ToString()); + + StratifiedInliningInfo info = implName2StratifiedInliningInfo[procName]; + VCExpr! expansion = (!)info.vcexpr; + + // Instantiate the "forall" variables + Dictionary! substForallDict = new Dictionary(); + 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(checker.VCExprGen); + expansion = subst.Mutate(expansion, substForall); + + // Instantiate and declare the "exists" variables + Dictionary! substExistsDict = new Dictionary(); + for(int i = 0; i < info.privateVars.Count; i++) { + VCExprVar! v = info.privateVars[i]; + string newName = v.Name + "_si_" + newVarCnt.ToString(); + newVarCnt ++; + checker.TheoremProver.Context.DeclareConstant(new Constant(Token.NoToken, new TypedIdent(Token.NoToken, newName, v.Type)), false, null); + substExistsDict.Add(v, checker.VCExprGen.Variable(newName, v.Type)); + } + VCExprSubstitution substExists = new VCExprSubstitution(substExistsDict, new Dictionary()); + + subst = new SubstitutingVCExprVisitor(checker.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(expr, expansion); + exprToPush = checker.VCExprGen.And(exprToPush, expansion); + //checker.TheoremProver.PushVCExpression(expansion); + + } + checker.TheoremProver.PushVCExpression(exprToPush); + + int axioms_pushed = checker.TheoremProver.NumAxiomsPushed() - old_axioms_pushed; + checker.TheoremProver.FlushAxiomsToTheoremProver(); + return axioms_pushed; + } + + // Does on-demand inlining -- pushes procedure bodies on the theorem prover stack. + // Returns the number of axioms pushed. + private VCExpr! DoExpansionAndInline( + VCExpr! mainVC, List! candidates, + FCallHandler! calls, Checker! checker) + throws UnexpectedProverOutputException; + { + FCallInliner! inliner = new FCallInliner(checker.VCExprGen); + foreach(int id in candidates) { + VCExprNAry! expr = calls.id2Candidate[id]; + + string procName = ((!)(expr.Op as VCExprBoogieFunctionOp)).Func.Name; + if(!implName2StratifiedInliningInfo.ContainsKey(procName)) continue; + + StratifiedInliningInfo info = implName2StratifiedInliningInfo[procName]; + VCExpr! expansion = (!)info.vcexpr; + + // Instantiate the "forall" variables + Dictionary! substForallDict = new Dictionary(); + 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(checker.VCExprGen); + expansion = subst.Mutate(expansion, substForall); + + // Instantiate and declare the "exists" variables + Dictionary! substExistsDict = new Dictionary(); + for(int i = 0; i < info.privateVars.Count; i++) { + VCExprVar! v = info.privateVars[i]; + string newName = v.Name + "_si_" + newVarCnt.ToString(); + newVarCnt ++; + checker.TheoremProver.Context.DeclareConstant(new Constant(Token.NoToken, new TypedIdent(Token.NoToken, newName, v.Type)), false, null); + substExistsDict.Add(v, checker.VCExprGen.Variable(newName, v.Type)); + } + VCExprSubstitution substExists = new VCExprSubstitution(substExistsDict, new Dictionary()); + + subst = new SubstitutingVCExprVisitor(checker.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); + + inliner.subst.Add(id, expansion); + + } + + return inliner.Mutate(mainVC, true); + } + + // Return the VC for the impl (don't pass it to the theorem prover). + // GetVC is a cheap imitation of VerifyImplementatio, except that the VC is not passed to the theorem prover. + private void GetVC(Implementation! impl, Program! program, VerifierCallback! callback, out VCExpr! vc, out Hashtable/**/! label2absy, out StratifiedInliningErrorReporter! reporter) + { + ConvertCFG2DAG(impl, program); + Hashtable/*TransferCmd->ReturnCmd*/ gotoCmdOrigins = PassifyImpl(impl, program); + Checker! checker = FindCheckerFor(impl, CommandLineOptions.Clo.ProverKillTime); + + vc = GenerateVC(impl, null, out label2absy, checker); + + /* + ErrorReporter errReporter; + if (CommandLineOptions.Clo.vcVariety == CommandLineOptions.VCVariety.Local) { + errReporter = new ErrorReporterLocal(gotoCmdOrigins, label2absy, impl.Blocks, incarnationOriginMap, callback, implName2LazyInliningInfo, (DeclFreeProverContext!) checker.TheoremProver.Context, program); + } else { + errReporter = new ErrorReporter(gotoCmdOrigins, label2absy, impl.Blocks, incarnationOriginMap, callback, implName2LazyInliningInfo, (DeclFreeProverContext!) checker.TheoremProver.Context, program); + } + */ + + reporter = new StratifiedInliningErrorReporter( + (!)implName2StratifiedInliningInfo, checker.TheoremProver, callback, + (DeclFreeProverContext)checker.TheoremProver.Context, gotoCmdOrigins, program, impl); + + } + + private Outcome CheckVC(VCExpr! vc, StratifiedInliningErrorReporter! reporter, Checker! checker, string! implName) + throws UnexpectedProverOutputException; + { + checker.TheoremProver.FlushAxiomsToTheoremProver(); + checker.BeginCheck(implName, vc, reporter); + checker.ProverDone.WaitOne(); + + ProverInterface.Outcome outcome = (checker).ReadOutcome(); + + //checker.BeginCheck(implName, vc, reporter); + //checker.ProverDone.WaitOne(); + //outcome = (checker).ReadOutcome(); + + switch (outcome) { + case ProverInterface.Outcome.Valid: + return Outcome.Correct; + 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; + default: + assert false; + } + + } + + /* + // Collects all function calls in the VCExpr + public class FCallCollector : TraversingVCExprVisitor { + Dictionary! implName2StratifiedInliningInfo; + public List! fcalls; + + public FCallCollector(Dictionary! implName2StratifiedInliningInfo) { + this.implName2StratifiedInliningInfo = implName2StratifiedInliningInfo; + fcalls = new List(); + } + + protected override bool StandardResult(VCExpr! node, bool arg) { + VCExprNAry vnary = node as VCExprNAry; + if(vnary == null) return true; + VCExprBoogieFunctionOp bop = vnary.Op as VCExprBoogieFunctionOp; + if(bop == null) return true; + if(implName2StratifiedInliningInfo.ContainsKey(bop.Func.Name)) { + fcalls.Add(vnary); + } + return true; + } + + } + */ + + // 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 Hashtable/**/! mainLabel2absy; + public Dictionary! boogieExpr2Id; + public Dictionary! id2Candidate; + public Dictionary! candidate2Id; + public Dictionary! label2Id; + public Microsoft.SpecSharp.Collections.Set currCandidates; + + // Name of the procedure who's VC we're mutating + string currProc; + + // The 0^th candidate is main + static int candidateCount = 1; + public int currInlineCount; + + public FCallHandler(VCExpressionGenerator! gen, + Dictionary! implName2StratifiedInliningInfo, + Hashtable/**/! mainLabel2absy) + : base(gen) + { + this.implName2StratifiedInliningInfo = implName2StratifiedInliningInfo; + this.mainLabel2absy = mainLabel2absy; + id2Candidate = new Dictionary(); + candidate2Id = new Dictionary(); + boogieExpr2Id = new Dictionary(); + label2Id = new Dictionary(); + currCandidates = new Microsoft.SpecSharp.Collections.Set(); + currInlineCount = 0; + currProc = null; + } + + public void Clear() + { + currCandidates = new Microsoft.SpecSharp.Collections.Set(); + } + + private int GetId(VCExprNAry! vc) + { + if(candidate2Id.ContainsKey(vc)) + return candidate2Id[vc]; + + + int id = candidateCount; + candidate2Id[vc] = id; + id2Candidate[id] = vc; + + candidateCount ++; + currCandidates.Add(id); + + return id; + } + + private string! GetLabel(int id) + { + string! ret = "si_fcall_" + id.ToString(); + if(!label2Id.ContainsKey(ret)) + label2Id[ret] = id; + + return ret; + } + + public int GetId(string! label) + { + if(!label2Id.ContainsKey(label)) return -1; + return label2Id[label]; + } + + public string! RenameAbsyLabel(string !label) + requires label.Length >= 1; + { + // Remove the sign from the label + string! nosign = label.Substring(1); + return "si_inline_" + currInlineCount.ToString() + "_" + nosign; + } + + public string ParseRenamedAbsyLabel(string! label) + { + string! prefix = RenameAbsyLabel("+"); + if(!label.StartsWith(prefix)) + return null; + return label.Substring(prefix.Length); + } + + public void setCurrProc(string! name) + { + currProc = name; + assert implName2StratifiedInliningInfo.ContainsKey(name); + } + + public void setCurrProcAsMain() + { + currProc = ""; + } + + private Hashtable/**/! getLabel2absy() + { + assert currProc != null; + if(currProc == "") { + return mainLabel2absy; + } + return (!)implName2StratifiedInliningInfo[currProc].label2absy; + } + + // Finds labels and changes them: + // si_fcall_id: if "id" corresponds to a tracked procedure call, then + // si_fcall_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) + { + 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; + 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)); + Hashtable! label2absy = getLabel2absy(); + Absy cmd = label2absy[id] as Absy; + //label2absy.Remove(id); + + assert cmd != null; + AssumeCmd acmd = cmd as AssumeCmd; + assert acmd != null; + NAryExpr naryExpr = acmd.Expr as NAryExpr; + assert naryExpr != null; + + string! calleeName = naryExpr.Fun.FunctionName; + + VCExprNAry callExpr = retnary[0] as VCExprNAry; + assert callExpr != null; + + if(implName2StratifiedInliningInfo.ContainsKey(calleeName)) { + int candidateId = GetId(callExpr); + boogieExpr2Id[naryExpr] = candidateId; + string! label = GetLabel(candidateId); + return Gen.LabelPos(label, callExpr); + } else { + return callExpr; + } + } + + // Else, rename label + string! newLabel = RenameAbsyLabel(lop.label); + if(lop.pos) { + return Gen.LabelPos(newLabel, retnary[0]); + } else { + return Gen.LabelNeg(newLabel, retnary[0]); + } + + } + + } // end FCallHandler + + + public class FCallInliner : MutatingVCExprVisitor { + public Dictionary! subst; + + public FCallInliner(VCExpressionGenerator! gen) + : base(gen) + { + 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) + { + 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 ErrorReporter : ProverInterface.ErrorHandler { + Hashtable/*TransferCmd->ReturnCmd*/! gotoCmdOrigins; + Hashtable/**/! label2absy; + List! blocks; + protected Dictionary! incarnationOriginMap; + protected VerifierCallback! callback; + internal string? resourceExceededMessage; + static System.IO.TextWriter? modelWriter; + + public static TextWriter! ModelWriter { + get { + if (ErrorReporter.modelWriter == null) + ErrorReporter.modelWriter = CommandLineOptions.Clo.PrintErrorModelFile == null ? Console.Out : new StreamWriter(CommandLineOptions.Clo.PrintErrorModelFile, false); + return ErrorReporter.modelWriter; + } + } + + Dictionary! implName2LazyInliningInfo; + DeclFreeProverContext! context; + Program! program; + + public ErrorReporter(Hashtable/*TransferCmd->ReturnCmd*/! gotoCmdOrigins, + Hashtable/**/! label2absy, + List! blocks, + Dictionary! incarnationOriginMap, + VerifierCallback! callback, + Dictionary! implName2LazyInliningInfo, + DeclFreeProverContext! context, + Program! program) + { + this.gotoCmdOrigins = gotoCmdOrigins; + this.label2absy = label2absy; + this.blocks = blocks; + this.incarnationOriginMap = incarnationOriginMap; + this.callback = callback; + + this.implName2LazyInliningInfo = implName2LazyInliningInfo; + this.context = context; + this.program = program; + // base(); + } + + public override void OnModel(IList! labels, ErrorModel errModel) { + if (CommandLineOptions.Clo.PrintErrorModel >= 1 && errModel != null) { + errModel.Print(ErrorReporter.ModelWriter); + ErrorReporter.ModelWriter.Flush(); + } + Hashtable traceNodes = new Hashtable(); + foreach (string! s in labels) { + Absy! absy = Label2Absy(s); + if (traceNodes.ContainsKey(absy)) + System.Console.WriteLine("Warning: duplicate label: " + s + " read while tracing nodes"); + else + traceNodes.Add(absy, null); + } + + BlockSeq! trace = new BlockSeq(); + Block! entryBlock = (!) this.blocks[0]; + assert traceNodes.Contains(entryBlock); + trace.Add(entryBlock); + + Counterexample newCounterexample = TraceCounterexample(entryBlock, traceNodes, trace, errModel, incarnationOriginMap, implName2LazyInliningInfo, context, program, 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) { + assume b.TransferCmd != null; + ReturnCmd cmd = (ReturnCmd) gotoCmdOrigins[b.TransferCmd]; + if (cmd != null) + { + returnExample.FailingReturn = cmd; + break; + } + } + } + #endregion + callback.OnCounterexample(newCounterexample, null); + } + + public override Absy! Label2Absy(string! label) + { + int id = int.Parse(label); + return (Absy!) label2absy[id]; + } + + public override void OnResourceExceeded(string! msg) + { + resourceExceededMessage = msg; + } + + public override void OnProverWarning(string! msg) + { + callback.OnWarning(msg); + } + } + + public class ErrorReporterLocal : ErrorReporter { + public ErrorReporterLocal(Hashtable/*TransferCmd->ReturnCmd*/! gotoCmdOrigins, + Hashtable/**/! label2absy, + List! blocks, + Dictionary! incarnationOriginMap, + VerifierCallback! callback, + Dictionary! implName2LazyInliningInfo, + DeclFreeProverContext! context, + Program! program) + { + base(gotoCmdOrigins, label2absy, blocks, incarnationOriginMap, callback, implName2LazyInliningInfo, context, program); // here for aesthetic purposes + } + + public override void OnModel(IList! labels, ErrorModel errModel) { + // We ignore the error model here for enhanced error message purposes. + // It is only printed to the command line. + if (CommandLineOptions.Clo.PrintErrorModel >= 1 && errModel != null) { + if (CommandLineOptions.Clo.PrintErrorModelFile != null) { + errModel.Print(ErrorReporter.ModelWriter); + ErrorReporter.ModelWriter.Flush(); + } + } + List traceNodes = new List(); + List assertNodes = new List(); + foreach (string! s in labels) { + Absy node = Label2Absy(s); + if (node is Block) { + Block b = (Block)node; + traceNodes.Add(b); + } else { + AssertCmd a = (AssertCmd)node; + assertNodes.Add(a); + } + } + assert assertNodes.Count > 0; + 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.Has(a)) { + BlockSeq trace = new BlockSeq(); + trace.Add(b); + Counterexample newCounterexample = AssertCmdToCounterexample(a, (!)b.TransferCmd, trace, errModel, incarnationOriginMap); + callback.OnCounterexample(newCounterexample, null); + goto NEXT_ASSERT; + } + } + assert false; // there was no block that contains the assert + NEXT_ASSERT: {} + } + } + } + + public class StratifiedInliningErrorReporter : ProverInterface.ErrorHandler { + Dictionary! implName2StratifiedInliningInfo; + ProverInterface! theoremProver; + VerifierCallback! callback; + FCallHandler calls; + Program! program; + Implementation! mainImpl; + DeclFreeProverContext! context; + Hashtable/*TransferCmd->ReturnCmd*/ gotoCmdOrigins; + + public bool underapproximationMode; + public List! candidatesToExpand; + + public StratifiedInliningErrorReporter(Dictionary! implName2StratifiedInliningInfo, + ProverInterface! theoremProver, VerifierCallback! callback, DeclFreeProverContext! context, + Hashtable/*TransferCmd->ReturnCmd*/ gotoCmdOrigins, + Program! program, Implementation! mainImpl) { + this.implName2StratifiedInliningInfo = implName2StratifiedInliningInfo; + this.theoremProver = theoremProver; + this.callback = callback; + this.context = context; + this.program = program; + this.mainImpl = mainImpl; + this.underapproximationMode = false; + this.calls = null; + this.candidatesToExpand = new List(); + this.gotoCmdOrigins = gotoCmdOrigins; + } + + public void SetCandidateHandler(FCallHandler! calls) + { + this.calls = calls; + } + + public override void OnModel(IList! labels, ErrorModel errModel) { + if(underapproximationMode) { + if(errModel == null) return; + GenerateTraceMain(labels, errModel); + return; + } + + assert calls != null; + assert errModel != null; + + candidatesToExpand = new List(); + foreach(string! lab in labels) + { + int id = calls.GetId(lab); + if(id < 0) continue; + if(!calls.currCandidates.Contains(id)) continue; + candidatesToExpand.Add(id); + } + + } + + // Construct the interprocedural trace + private void GenerateTraceMain(IList! labels, ErrorModel! errModel) { + if (CommandLineOptions.Clo.PrintErrorModel >= 1 && errModel != null) { + errModel.Print(ErrorReporter.ModelWriter); + ErrorReporter.ModelWriter.Flush(); + } + + Counterexample newCounterexample = + GenerateTrace(labels, errModel, 0, mainImpl); + + if(newCounterexample == null) return; + + #region Map passive program errors back to original program errors + ReturnCounterexample returnExample = newCounterexample as ReturnCounterexample; + if (returnExample != null && gotoCmdOrigins != null) + { + foreach (Block! b in returnExample.Trace) { + assume b.TransferCmd != null; + ReturnCmd cmd = (ReturnCmd) gotoCmdOrigins[b.TransferCmd]; + if (cmd != null) + { + returnExample.FailingReturn = cmd; + break; + } + } + } + #endregion + + callback.OnCounterexample(newCounterexample, null); + } + + private Counterexample GenerateTrace(IList! labels, ErrorModel! errModel, + int candidateId, Implementation! procImpl) { + + Hashtable traceNodes = new Hashtable(); + string! procPrefix = "si_inline_" + candidateId.ToString() + "_"; + + foreach (string! s in labels) { + if(!s.StartsWith(procPrefix)) + continue; + + Absy! absy; + + if(candidateId == 0) { + absy = Label2Absy(s.Substring(procPrefix.Length)); + } else { + absy = Label2Absy(procImpl.Name, s.Substring(procPrefix.Length)); + } + + if (traceNodes.ContainsKey(absy)) + System.Console.WriteLine("Warning: duplicate label: " + s + " read while tracing nodes"); + else + traceNodes.Add(absy, null); + } + + BlockSeq! trace = new BlockSeq(); + Block! entryBlock = (!) procImpl.Blocks[0]; + assert traceNodes.Contains(entryBlock); + trace.Add(entryBlock); + + Dictionary! calleeCounterexamples = new Dictionary(); + Counterexample newCounterexample = GenerateTraceRec(labels, errModel, entryBlock, traceNodes, trace, calleeCounterexamples); + + return newCounterexample; + + } + + private Counterexample GenerateTraceRec( + IList! labels, ErrorModel! errModel, + Block! b, Hashtable! traceNodes, BlockSeq! trace, + Dictionary! calleeCounterexamples) + { + // After translation, all potential errors come from asserts. + CmdSeq! cmds = b.Cmds; + TransferCmd! transferCmd = (!)b.TransferCmd; + for (int i = 0; i < cmds.Length; i++) + { + Cmd! cmd = (!) 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, new Dictionary()); + 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; + if (!implName2StratifiedInliningInfo.ContainsKey(calleeName)) continue; + + assert calls != null; + int calleeId = calls.boogieExpr2Id[naryExpr]; + + calleeCounterexamples[assumeCmd] = + new CalleeCounterexampleInfo( + (!)GenerateTrace(labels, errModel, calleeId, implName2StratifiedInliningInfo[calleeName].impl), + new List()); + + } + + GotoCmd gotoCmd = transferCmd as GotoCmd; + if (gotoCmd != null) + { + foreach (Block! bb in (!)gotoCmd.labelTargets) + { + if (traceNodes.Contains(bb)){ + trace.Add(bb); + return GenerateTraceRec(labels, errModel, bb, traceNodes, trace, calleeCounterexamples); + } + } + } + + return null; + + } + + public override Absy! Label2Absy(string! label) + { + int id = int.Parse(label); + assert calls != null; + return (Absy!) calls.mainLabel2absy[id]; + } + + public Absy! Label2Absy(string! procName, string! label) + { + int id = int.Parse(label); + Hashtable! l2a = (!)implName2StratifiedInliningInfo[procName].label2absy; + return (Absy!) l2a[id]; + } + + public override void OnResourceExceeded(string! msg) + { + //resourceExceededMessage = msg; + } + + public override void OnProverWarning(string! msg) + { + callback.OnWarning(msg); + } + } + + protected void ConvertCFG2DAG(Implementation! impl, Program! program) + { + impl.PruneUnreachableBlocks(); // This is needed for VCVariety.BlockNested, and is otherwise just an optimization + + current_impl = impl; + variable2SequenceNumber = new Hashtable/*Variable -> int*/(); + 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 + + ComputePredecessors(impl.Blocks); + + #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 = 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 + + #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 (!) g.Headers) + { + IDictionary backEdgeNodes = new Dictionary(); + foreach (Block! b in (!) g.BackEdgeNodes(header)) { 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 + CmdSeq prefixOfPredicateCmdsInit = new CmdSeq(); + CmdSeq prefixOfPredicateCmdsMaintained = new CmdSeq(); + for (int i = 0, n = header.Cmds.Length; i < n; i++) + { + PredicateCmd a = header.Cmds[i] as PredicateCmd; + if (a != null) + { + if (a is AssertCmd) { + Bpl.AssertCmd c = (AssertCmd) a; + Bpl.AssertCmd b = new Bpl.LoopInitAssertCmd(c.tok, c.Expr); + b.ErrorData = c.ErrorData; + prefixOfPredicateCmdsInit.Add(b); + b = new Bpl.LoopInvMaintainedAssertCmd(c.tok, c.Expr); + b.ErrorData = c.ErrorData; + prefixOfPredicateCmdsMaintained.Add(b); + header.Cmds[i] = new AssumeCmd(c.tok,c.Expr); + } else { + 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.Length; predIndex < n; predIndex++ ) + { + Block! pred = (!)header.Predecessors[predIndex]; + + // Create a block between header and pred for the predicate commands if pred has more than one successor + GotoCmd gotocmd = (GotoCmd!)pred.TransferCmd; + assert gotocmd.labelNames != null; // if "pred" is really a predecessor, it may be a GotoCmd with at least one label + if (gotocmd.labelNames.Length > 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 (!)backEdgeNodes.Keys) + { + 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.Length > 1 ) + { + // then remove the backedge by removing the target block from the list of gotos + BlockSeq remainingTargets = new BlockSeq(); + StringSeq remainingLabels = new StringSeq(); + assume gtc.labelNames != null; + for (int i = 0, n = gtc.labelTargets.Length; i < n; i++) + { + if ( gtc.labelTargets[i] != header ) + { + remainingTargets.Add(gtc.labelTargets[i]); + remainingLabels.Add(gtc.labelNames[i]); + } + } + 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); + } + #region Remove the backedge node from the list of predecessor nodes in the header + BlockSeq newPreds = new BlockSeq(); + 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 + VariableSeq varsToHavoc = new VariableSeq(); + foreach (Block! backEdgeNode in (!) g.BackEdgeNodes(header)) + { + foreach ( Block! b in g.NaturalLoops(header,backEdgeNode) ) + { + foreach ( Cmd! c in b.Cmds ) + { + c.AddAssignedVariables(varsToHavoc); + } + } + } + IdentifierExprSeq havocExprs = new IdentifierExprSeq(); + foreach ( Variable! v in varsToHavoc ) + { + IdentifierExpr ie = new IdentifierExpr(Token.NoToken, v); + if(!havocExprs.Has(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); + CmdSeq newCmds = new CmdSeq(); + newCmds.Add(hc); + foreach ( Cmd c in header.Cmds ) + { + newCmds.Add(c); + } + header.Cmds = newCmds; + #endregion + } + #endregion + #endregion Convert program CFG into a DAG + + #region Debug Tracing + if (CommandLineOptions.Clo.TraceVerify) + { + Console.WriteLine("after conversion into a DAG"); + EmitImpl(impl, true); + } + #endregion + } + + protected Hashtable/*TransferCmd->ReturnCmd*/! PassifyImpl(Implementation! impl, Program! program) + { + Hashtable/*TransferCmd->ReturnCmd*/ gotoCmdOrigins = new Hashtable/*TransferCmd->ReturnCmd*/(); + 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 + { + CmdSeq cc = new CmdSeq(); + // where clauses of global variables + foreach (Declaration d in program.TopLevelDeclarations) { + GlobalVariable gvar = d as GlobalVariable; + 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) { + if (lvar.TypedIdent.WhereExpr != null) { + Cmd c = new AssumeCmd(lvar.tok, lvar.TypedIdent.WhereExpr); + cc.Add(c); + } + } + + // 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 + exitBlock = InjectPostConditions(impl, exitBlock, gotoCmdOrigins); + } + #endregion + + #region Support for lazy inlining + if (implName2LazyInliningInfo != null && implName2LazyInliningInfo.ContainsKey(impl.Name)) + { + Expr! assertExpr = implName2LazyInliningInfo[impl.Name].assertExpr; + exitBlock.Cmds.Add(new AssertCmd(Token.NoToken, assertExpr)); + } + #endregion + + #region Support for lazy inlining + if (implName2StratifiedInliningInfo != null && implName2StratifiedInliningInfo.ContainsKey(impl.Name)) + { + Expr! assertExpr = implName2StratifiedInliningInfo[impl.Name].assertExpr; + exitBlock.Cmds.Add(new AssertCmd(Token.NoToken, assertExpr)); + } + #endregion + + #region Debug Tracing + if (CommandLineOptions.Clo.TraceVerify) + { + Console.WriteLine("after inserting pre- and post-conditions"); + EmitImpl(impl, true); + } + #endregion + + AddBlocksBetween(impl); + + #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); + } + + Hashtable exitIncarnationMap = Convert2PassiveCmd(impl); + if (implName2LazyInliningInfo != null && implName2LazyInliningInfo.ContainsKey(impl.Name)) + { + LazyInliningInfo! info = implName2LazyInliningInfo[impl.Name]; + info.exitIncarnationMap = exitIncarnationMap; + info.incarnationOriginMap = this.incarnationOriginMap; + } + if (implName2StratifiedInliningInfo != null && implName2StratifiedInliningInfo.ContainsKey(impl.Name)) + { + StratifiedInliningInfo! info = implName2StratifiedInliningInfo[impl.Name]; + info.exitIncarnationMap = exitIncarnationMap; + info.incarnationOriginMap = this.incarnationOriginMap; + } + + if (CommandLineOptions.Clo.LiveVariableAnalysis == 1) { + Microsoft.Boogie.LiveVariableAnalysis.ClearLiveVariables(impl); + } + // TODO: fix + //else if (CommandLineOptions.Clo.LiveVariableAnalysis == 2) { + // Microsoft.Boogie.InterProcGenKill.ClearLiveVariables(impl, program); + //} + + #region Peep-hole optimizations + if (CommandLineOptions.Clo.RemoveEmptyBlocks){ + #region Get rid of empty blocks + { + Block! entryBlock = (!) impl.Blocks[0]; + RemoveEmptyBlocks(entryBlock); + 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 + + if (CommandLineOptions.Clo.ExpandLambdas) + { + List! axioms; + List! functions; + LambdaHelper.Desugar(impl, out axioms, out functions); + // TODO: do something with functions (Z3 currently doesn't need them) + + if (axioms.Count > 0) { + CmdSeq cmds = new CmdSeq(); + foreach (Expr! ax in axioms) { + cmds.Add(new AssumeCmd(ax.tok, ax)); + } + Block! entryBlock = (!) impl.Blocks[0]; + cmds.AddRange(entryBlock.Cmds); + entryBlock.Cmds = cmds; + } + } + + + +// #region Constant Folding +// #endregion +// #region Debug Tracing +// if (CommandLineOptions.Clo.TraceVerify) +// { +// Console.WriteLine("after constant folding"); +// EmitImpl(impl, true); +// } +// #endregion + + return gotoCmdOrigins; + } + + private static Counterexample! LazyCounterexample( + ErrorModel! errModel, + Dictionary! implName2LazyInliningInfo, + DeclFreeProverContext! context, + Program! program, + string! implName, List! values) + { + VCExprTranslator! vcExprTranslator = (!)context.exprTranslator; + Boogie2VCExprTranslator! boogieExprTranslator = context.BoogieExprTranslator; + LazyInliningInfo! info = implName2LazyInliningInfo[implName]; + BlockSeq! trace = new BlockSeq(); + Block! b = ((!) info.impl).Blocks[0]; + trace.Add(b); + VCExprVar! cfcVar = boogieExprTranslator.LookupVariable(info.controlFlowVariable); + string! cfcName = vcExprTranslator.Lookup(cfcVar); + int cfcPartition = errModel.LookupSkolemFunctionAt(cfcName + "!" + info.uniqueId, values); + int cfcValue = errModel.LookupPartitionValue(cfcPartition); + + Dictionary calleeCounterexamples = new Dictionary(); + while (true) { + CmdSeq! cmds = b.Cmds; + TransferCmd! transferCmd = (!)b.TransferCmd; + for (int i = 0; i < cmds.Length; i++) + { + Cmd! cmd = (!) cmds[i]; + AssertCmd assertCmd = cmd as AssertCmd; + if (assertCmd != null && errModel.LookupControlFlowFunctionAt(cfcValue, assertCmd.UniqueId) == 0) + { + Counterexample newCounterexample; + newCounterexample = AssertCmdToCounterexample(assertCmd, transferCmd, trace, errModel, (!)info.incarnationOriginMap); + newCounterexample.AddCalleeCounterexample(calleeCounterexamples); + return newCounterexample; + } + + AssumeCmd assumeCmd = cmd as AssumeCmd; + if (assumeCmd == null) continue; + NAryExpr naryExpr = assumeCmd.Expr as NAryExpr; + if (naryExpr == null) continue; + string! calleeName = naryExpr.Fun.FunctionName; + if (!implName2LazyInliningInfo.ContainsKey(calleeName)) continue; + + List! args = new List(); + foreach (Expr! expr in naryExpr.Args) + { + VCExprVar exprVar; + string name; + LiteralExpr litExpr = expr as LiteralExpr; + if (litExpr != null) + { + args.Add(errModel.valueToPartition[litExpr.Val]); + continue; + } + + IdentifierExpr idExpr = expr as IdentifierExpr; + assert idExpr != null; + Variable! var = (!)idExpr.Decl; + + if (var is Constant) + { + exprVar = boogieExprTranslator.LookupVariable(var); + name = vcExprTranslator.Lookup(exprVar); + args.Add(errModel.identifierToPartition[name]); + continue; + } + + int index = 0; + List globalVars = program.GlobalVariables(); + foreach (Variable! global in globalVars) + { + if (global == var) break; + index++; + } + if (index < globalVars.Count) + { + args.Add(values[index]); + continue; + } + + foreach (Variable! input in info.impl.InParams) + { + if (input == var) break; + index++; + } + if (index < globalVars.Count + info.impl.InParams.Length) + { + args.Add(values[index]); + continue; + } + + foreach (Variable! output in info.impl.OutParams) + { + if (output == var) break; + index++; + } + if (index < globalVars.Count + info.impl.InParams.Length + info.impl.OutParams.Length) + { + args.Add(values[index]); + continue; + } + + exprVar = boogieExprTranslator.LookupVariable(var); + name = vcExprTranslator.Lookup(exprVar); + args.Add(errModel.LookupSkolemFunctionAt(name + "!" + info.uniqueId, values)); + } + calleeCounterexamples[assumeCmd] = + new CalleeCounterexampleInfo( + LazyCounterexample(errModel, implName2LazyInliningInfo, context, program, calleeName, args), + errModel.PartitionsToValues(args)); + } + + GotoCmd gotoCmd = transferCmd as GotoCmd; + if (gotoCmd == null) break; + int nextBlockId = errModel.LookupControlFlowFunctionAt(cfcValue, b.UniqueId); + b = (Block!) ((!)info.label2absy)[nextBlockId]; + trace.Add(b); + } + assert false; + } + + static Counterexample TraceCounterexample(Block! b, BlockSeq! trace, ErrorModel errModel, Dictionary! incarnationOriginMap) + { + // After translation, all potential errors come from asserts. + + return null; + } + + static Counterexample TraceCounterexample( + Block! b, Hashtable! traceNodes, BlockSeq! trace, ErrorModel errModel, + Dictionary! incarnationOriginMap, + Dictionary! implName2LazyInliningInfo, + DeclFreeProverContext! context, Program! program, + Dictionary! calleeCounterexamples) + { + // After translation, all potential errors come from asserts. + CmdSeq! cmds = b.Cmds; + TransferCmd! transferCmd = (!)b.TransferCmd; + for (int i = 0; i < cmds.Length; i++) + { + Cmd! cmd = (!) 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, incarnationOriginMap); + newCounterexample.AddCalleeCounterexample(calleeCounterexamples); + return newCounterexample; + } + + #region Counterexample generation for lazily inlined procedures + if (errModel == null) continue; + AssumeCmd assumeCmd = cmd as AssumeCmd; + if (assumeCmd == null) continue; + NAryExpr naryExpr = assumeCmd.Expr as NAryExpr; + if (naryExpr == null) continue; + string! calleeName = naryExpr.Fun.FunctionName; + if (!implName2LazyInliningInfo.ContainsKey(calleeName)) continue; + VCExprTranslator! vcExprTranslator = (!)context.exprTranslator; + Boogie2VCExprTranslator! boogieExprTranslator = context.BoogieExprTranslator; + List! args = new List(); + foreach (Expr! expr in naryExpr.Args) + { + LiteralExpr litExpr = expr as LiteralExpr; + if (litExpr != null) + { + args.Add(errModel.valueToPartition[litExpr.Val]); + continue; + } + + IdentifierExpr idExpr = expr as IdentifierExpr; + assert idExpr != null; + assert idExpr.Decl != null; + VCExprVar! var = boogieExprTranslator.LookupVariable(idExpr.Decl); + string! name = vcExprTranslator.Lookup(var); + args.Add(errModel.identifierToPartition[name]); + } + calleeCounterexamples[assumeCmd] = + new CalleeCounterexampleInfo( + LazyCounterexample(errModel, implName2LazyInliningInfo, context, program, calleeName, args), + errModel.PartitionsToValues(args)); + #endregion + } + + GotoCmd gotoCmd = transferCmd as GotoCmd; + if (gotoCmd != null) + { + foreach (Block! bb in (!)gotoCmd.labelTargets) + { + if (traceNodes.Contains(bb)){ + trace.Add(bb); + return TraceCounterexample(bb, traceNodes, trace, errModel, incarnationOriginMap, implName2LazyInliningInfo, context, program, calleeCounterexamples); + } + } + } + + return null; + + // Debug.Fail("Could not find failing node."); + // throw new Microsoft.Contracts.AssertException(); + } + + + static void /*return printable error!*/ ApplyEnhancedErrorPrintingStrategy (Bpl.Expr! expr, Hashtable /*Variable -> Expr*/! incarnationMap, MiningStrategy errorDataEnhanced, ErrorModel! errModel, Dictionary! exprToPrintableValue, List! relatedInformation, bool printInternalStateDumpOnce, Dictionary! incarnationOriginMap) { + if (errorDataEnhanced is ListOfMiningStrategies) { + ListOfMiningStrategies loms = (ListOfMiningStrategies) errorDataEnhanced; + List! l = loms.msList; + for (int i = 0; i < l.Count; i++) { + MiningStrategy ms = l[i]; + if (ms != null) { + ApplyEnhancedErrorPrintingStrategy(expr, incarnationMap, l[i], errModel, exprToPrintableValue, relatedInformation, false, incarnationOriginMap); + } + } + } + else if (errorDataEnhanced is EEDTemplate /*EDEverySubExpr*/) { + EEDTemplate eedT = (EEDTemplate) errorDataEnhanced; + string reason = eedT.reason; + List listOfExprs = eedT.exprList; + if (listOfExprs != null) { + List holeFillers = new List(); + for (int i = 0; i < listOfExprs.Count; i++) { + bool alreadySet = false; + foreach (KeyValuePair kvp in exprToPrintableValue) { + Bpl.Expr! e = kvp.Key; + Bpl.Expr! f = listOfExprs[i]; + // the strings are compared instead of the actual expressions because + // the expressions might not be identical, but their print-out strings will be + if (e.ToString() == f.ToString()) { + object o = kvp.Value; + if (o != null) { + holeFillers.Add(o.ToString()); + alreadySet = true; + break; + } + } + } + if (!alreadySet) { + // no information about that Expression found, so put in + holeFillers.Add(""); + } + } + reason = FormatReasonString(reason, holeFillers); + } + if (reason != null) { + relatedInformation.Add("(related information): "+reason); + } + } else { + // define new templates here! + } + + if (printInternalStateDumpOnce) { + ComputeAndTreatHeapSuccessions(incarnationMap, errModel, incarnationOriginMap, relatedInformation); + + // default action: print all values! + foreach (KeyValuePair kvp in exprToPrintableValue) { + object o = kvp.Value; + if (o != null) { + // We do not want to print LiteralExprs because that gives things like 0 == 0. + // If both arguments to the string.Format are the same it is also useless, + // as that would print e.g. $a == $a. + if (!(kvp.Key is LiteralExpr)&& kvp.Key.ToString() != o.ToString()) { + string boogieExpr; + // check whether we are handling BPL or SSC input + if (CommandLineOptions.Clo.RunningBoogieOnSsc) { + boogieExpr = Helpers.PrettyPrintBplExpr(kvp.Key); + } else { + boogieExpr = kvp.Key.ToString(); + } + relatedInformation.Add("(internal state dump): "+string.Format("{0} == {1}", boogieExpr, o)); + } + } + } + } + } + + static void ComputeAndTreatHeapSuccessions(System.Collections.Hashtable! incarnationMap, ErrorModel! errModel, Dictionary! incarnationOriginMap, List! relatedInformation) { + List heapSuccList = ComputeHeapSuccessions(incarnationMap, errModel); + TreatHeapSuccessions(heapSuccList, incarnationMap, errModel, incarnationOriginMap, relatedInformation); + } + + static List ComputeHeapSuccessions(System.Collections.Hashtable! incarnationMap, ErrorModel! errModel) { + // find the heap variable + Variable heap = null; + ICollection ic = incarnationMap.Keys; + foreach (object o in ic) { + if (o is GlobalVariable) { + GlobalVariable gv = (GlobalVariable) o; + if (gv.Name == "$Heap") { + heap = gv; + } + } + } + List heapIdSuccession = new List(); + if (heap == null) { + // without knowing the name of the current heap we cannot create a heap succession! + } else { + object oHeap = incarnationMap[heap]; + if (oHeap != null) { + string currentHeap = oHeap.ToString(); + int currentHeapId; + if (errModel.identifierToPartition.TryGetValue(currentHeap, out currentHeapId)) { + while (currentHeapId != -1) { + if (!heapIdSuccession.Contains(currentHeapId)) { + heapIdSuccession.Add(currentHeapId); + currentHeapId = ComputePredecessorHeapId(currentHeapId, errModel); + } else { + // looping behavior, just stop here and do not add this value (again!) + break; + } + } + } + } + } + if (heapIdSuccession.Count > 0) { + int heapId = heapIdSuccession[heapIdSuccession.Count-1]; + List strl = errModel.partitionToIdentifiers[heapId]; + if (strl != null && strl.Contains("$Heap")) { + // we have a proper succession of heaps that starts with $Heap + return heapIdSuccession; + } else { + // no proper heap succession, not starting with $Heap! + return null; + } + } else { + // no heap succession found because either the $Heap does not have a current incarnation + // or because (unlikely!) the model is somehow messed up + return null; + } + } + + static int ComputePredecessorHeapId(int id, ErrorModel! errModel) { + //check "$HeapSucc" and "store2" functions: + List heapSuccPredIdList = new List(); + List> heapSuccFunc; + errModel.definedFunctions.TryGetValue("$HeapSucc", out heapSuccFunc); + if (heapSuccFunc != null) { + foreach (List heapSuccFuncDef in heapSuccFunc) { + // do not look at the else case of the function def, so check .Count + if (heapSuccFuncDef != null && heapSuccFuncDef.Count == 3 && heapSuccFuncDef[1] == id) { + // make sure each predecessor is put into the list at most once + if (!heapSuccPredIdList.Contains(heapSuccFuncDef[0])) { + heapSuccPredIdList.Add(heapSuccFuncDef[0]); + } + } + } + } + List store2PredIdList = new List();; + List> store2Func; + errModel.definedFunctions.TryGetValue("store2", out store2Func); + if (store2Func != null) { + foreach (List store2FuncDef in store2Func) { + // do not look at the else case of the function def, so check .Count + if (store2FuncDef != null && store2FuncDef.Count == 5 && store2FuncDef[4] == id) { + // make sure each predecessor is put into the list at most once + if (!store2PredIdList.Contains(store2FuncDef[0])) { + store2PredIdList.Add(store2FuncDef[0]); + } + } + } + } + if (heapSuccPredIdList.Count + store2PredIdList.Count > 0) { + if (store2PredIdList.Count == 1) { + return store2PredIdList[0]; + } else if (store2PredIdList.Count == 0) { + if (heapSuccPredIdList.Count == 1) { + return heapSuccPredIdList[0]; + } else { //(heapSuccPredIdList.Count > 1) + if (heapSuccPredIdList.Count == 2) { + // if one of the 2 is a self-loop, take the other! + if (heapSuccPredIdList[0] == id) { + return heapSuccPredIdList[1]; + } else if (heapSuccPredIdList[1] == id) { + return heapSuccPredIdList[0]; + } else { + //no self-loop, two different predecessors, cannot choose + return -1; + } + } else { + // at least 3 different predecessors available, one of them could be a self-loop, but + // we cannot choose between the other 2 (or more) candidates + return -1; + } + } + } else { + // more than one result in the succession coming from store2, no way + // to decide which is right, end here + return -1; + } + } else { + // no predecessor found + return -1; + } + } + + static void TreatHeapSuccessions (List heapSuccessionList, System.Collections.Hashtable! incarnationMap, ErrorModel! errModel, Dictionary! incarnationOriginMap, List! relatedInformation) { + if (heapSuccessionList == null) { + // empty list of heap successions, nothing we can do! + return; + } + // primarily look for $o and $f (with skolem-id stuff) and then look where they were last changed! + // find the o and f variables + Variable dollarO = null; + Variable dollarF = null; + ICollection ic = incarnationMap.Keys; + foreach (object o in ic) { + if (o is BoundVariable) { + BoundVariable bv = (BoundVariable) o; + if (bv.Name == "$o") { + dollarO = bv; + } + else if (bv.Name == "$f") { + dollarF = bv; + } + } + } + if (dollarO == null || dollarF == null) { + // without knowing the name of the current incarnation of $Heap, $o and $f we don't do anything here + } else { + object objO = incarnationMap[dollarO]; + object objF = incarnationMap[dollarF]; + if (objO != null && objF != null) { + int zidO = errModel.identifierToPartition[objO.ToString()]; + int zidF = errModel.identifierToPartition[objF.ToString()]; + + List> select2Func = null; + if (errModel.definedFunctions.TryGetValue("select2", out select2Func) && select2Func != null) { + // check for all changes to $o.$f! + List heapsChangedOFZid = new List(); + int oldValueZid = -1; + int newValueZid = -1; + + for (int i = 0; i < heapSuccessionList.Count; i++) { + bool foundValue = false; + foreach (List f in select2Func) { + if (f != null && f.Count == 4 && f[0] == heapSuccessionList[i] && f[1] == zidO && f[2] == zidF) { + newValueZid = f[3]; + foundValue = true; + break; + } + } + if (!foundValue) { + // get default of the function once Leo&Nikolaj have changed it so the default is type correct + // for now treat it as a -1 ! + // the last element of select2Func is the else case, it has only 1 element, so grab that: + // newValueZid = (select2Func[select2Func.Count-1])[0]; + newValueZid = -1; + } + + if (oldValueZid != newValueZid) { + // there was a change here, record that in the list: + if (oldValueZid != -1) { + // don't record a change at the "initial" location, which refers to the $Heap in + // its current incarnation, and is marked by the oldValueZid being uninitialized + heapsChangedOFZid.Add(heapSuccessionList[i-1]); + } + oldValueZid = newValueZid; + } + } + + foreach (int id in heapsChangedOFZid) { + //get the heap name out of the errModel for this zid: + List l = errModel.partitionToIdentifiers[id]; + List heaps = new List(); + if (l!=null) { + foreach (string s in l) { + if (s.StartsWith("$Heap")) { + heaps.Add(s); + } + } + } + // easy case first: + if (heaps.Count == 1) { + string heapName = heaps[0]; + // we have a string with the name of the heap, but we need to get the + // source location out of a map that uses Incarnations! + + ICollection incOrgMKeys = incarnationOriginMap.Keys; + foreach (Incarnation inc in incOrgMKeys) { + if (inc!= null) { + if (inc.Name == heapName) { + Absy source = null; + incarnationOriginMap.TryGetValue(inc, out source); + if (source != null) { + if (source is Block) { + Block b = (Block) source; + string fileName = b.tok.filename; + if (fileName != null) { + fileName = fileName.Substring(fileName.LastIndexOf('\\') + 1); + } + relatedInformation.Add("(related information): Changed $o.$f here: " + fileName + "(" + b.tok.line + "," + b.tok.col + ")"); + } else if (source is Cmd) { + Cmd c = (Cmd) source; + string fileName = c.tok.filename; + if (fileName != null) { + fileName = fileName.Substring(fileName.LastIndexOf('\\') + 1); + } + relatedInformation.Add("(related information) Changed $o.$f here: " + fileName + "(" + c.tok.line + "," + c.tok.col + ")"); + } else { + assert false; + } + } + } + } + } + } else { + // more involved! best use some of the above code and try to synchronize joint parts + // here there is more than one "$Heap@i" in the partition, check if they all agree on one + // source location or maybe if some of them are joins (i.e. blocks) that should be ignored + } + + } + } + } + } + } + + static string FormatReasonString (string reason, List holeFillers) { + if (holeFillers != null) { + // in case all elements of holeFillers are "" we can not say anything useful + // so just say nothing and return null + bool allUnknown = true; + foreach (string s in holeFillers) { + if (s != "") { + allUnknown = false; + break; + } + } + if (allUnknown) { + return null; + } + string[] a = holeFillers.ToArray(); + reason = string.Format(reason, a); + } + return reason; + } + + static object ValueFromZID (ErrorModel! errModel, int id) { + return ValueFromZID(errModel, id, null); + } + + static object ValueFromZID (ErrorModel! errModel, int id, string searchForAlternate) { + object o = errModel.partitionToValue[id]; + if (o != null) { + return o; + } else { + // more elaborate scheme to find something better, as in: look at the identifiers that + // this partition maps to, or similar things! + + //treatment for 'null': + int idForNull = -1; + if (errModel.valueToPartition.TryGetValue("nullObject", out idForNull) && idForNull == id) { + return "nullObject"; + } + + string returnStr = null; + + // "good identifiers" if there is no value found are 'unique consts' or + // '$in' parameters; '$in' parameters are treated, unclear how to get 'unique const' info + List identifiers = errModel.partitionToIdentifiers[id]; + if (identifiers != null) { + foreach (string s in identifiers) { + //$in parameters are (more) interesting than other identifiers, return the + // first one found + if (s.EndsWith("$in")) { + returnStr = s; + break; + } + } + } + + // try to get mappings from one identifier to another if there are exactly + // two identifiers in the partition, where one of them is the identifier for which + // we search for alternate encodings (third parameter of the method) [or an incarnation + // of such an identifier] + if (returnStr == null && searchForAlternate != null && identifiers != null && identifiers.Count == 2) { + if (identifiers[0] == searchForAlternate || identifiers[0].StartsWith(searchForAlternate + ".sk.")) { + returnStr = identifiers[1]; + } else if (identifiers[1] == searchForAlternate || identifiers[1].StartsWith(searchForAlternate + ".sk.")) { + returnStr = identifiers[0]; + } + } + + if (returnStr != null) { + return Helpers.BeautifyBplString(returnStr); + } + + return null; + } + } + + static int TreatInterpretedFunction(string! functionName, List! zargs, ErrorModel! errModel) { + if (zargs.Count != 2) { + //all interpreted functions are binary, so there have to be exactly two arguments + return -1; + } + else { + object arg0 = ValueFromZID(errModel, zargs[0]); + object arg1 = ValueFromZID(errModel, zargs[1]); + if (arg0 is BigNum && arg1 is BigNum) { + BigNum arg0i = (BigNum)arg0; + BigNum arg1i = (BigNum)arg1; + BigNum result; + if (functionName == "+") { + result = arg0i + arg1i; + } else if (functionName == "-") { + result = arg0i - arg1i; + } else /*if (functionName == "*")*/ { + result = arg0i * arg1i; + } + int returnId = -1; + if (errModel.valueToPartition.TryGetValue(result, out returnId)) { + return returnId; + } else { + return -1; + } + } + else { + //both arguments need to be integers for this to work! + return -1; + } + } + } + + static int TreatFunction (string! functionName, List! zargs, bool undefined, ErrorModel! errModel) { + List> functionDef; + if ((!errModel.definedFunctions.TryGetValue(functionName, out functionDef) && functionName != "+" && functionName != "-" && functionName != "*") || undefined) { + // no fitting function found or one of the arguments is undefined + return -1; + } else { + if (functionName == "+" || functionName == "-" || functionName == "*") { + return TreatInterpretedFunction(functionName, zargs, errModel); + } + assert functionDef != null; + foreach (List pWiseF in functionDef) { + assert pWiseF != null; + // else case in the function definition: + if (pWiseF.Count == 1) { + return pWiseF[0]; + } + // number of arguments is exactly the right number + assert zargs.Count == pWiseF.Count - 1; + if (forall{int i in (0: zargs.Count); zargs[i] == pWiseF[i]}) { + return pWiseF[pWiseF.Count - 1]; + } + } + // all functions should have an 'else ->' case defined, therefore this should be + // unreachable code! + assert false; + } + } + + //returned int is zID + static int GetValuesFromModel (Bpl.Expr! expr, Hashtable /*Variable -> Expr*/! incarnationMap, ErrorModel! errModel, Dictionary! exprToPrintableValue) + modifies exprToPrintableValue.*; + { + // call GetValuesFromModel on all proper subexpressions, returning their value, + // so they only have to be computed once! + if (expr is LiteralExpr) { + // nothing needs to be added to the exprToPrintableValue map, because e.g. 0 -> 0 is not interesting + object o = ((LiteralExpr) expr).Val; + if (o == null) { + o = "nullObject"; + } + int idForExprVal; + if (errModel.valueToPartition.TryGetValue(o, out idForExprVal)) { + return idForExprVal; + } else { + return -1; + } + } + else if (expr is IdentifierExpr) { + // if the expression expr is in the incarnation map, then use its incarnation, + // otherwise just use the actual expression + string s = ((IdentifierExpr) expr).Name; + object o = null; + Variable v = ((IdentifierExpr) expr).Decl; + if (v != null && incarnationMap.ContainsKey(v)) { + if (incarnationMap[v] is IdentifierExpr!) { + s = ((IdentifierExpr!) incarnationMap[v]).Name; + } else if (incarnationMap[v] is LiteralExpr!) { + o = ((LiteralExpr!) incarnationMap[v]).Val; + } + } + // if o is not null, then we got a LiteralExpression, that needs separate treatment + if (o == null) { + // if the expression (respectively its incarnation) is mapped to some partition + // then return that id, else return the error code -1 + if (errModel.identifierToPartition.ContainsKey(s)) { + int i = errModel.identifierToPartition[s]; + // if the key is already in the map we can assume that it is the same map we would + // get now and thus just ignore it + if (!exprToPrintableValue.ContainsKey(expr)) { + exprToPrintableValue.Add(expr, ValueFromZID(errModel, i, ((IdentifierExpr) expr).Name)); + } + return i; + } else { + return -1; + } + } else if (errModel.valueToPartition.ContainsKey(o)) { + int i = errModel.valueToPartition[o]; + if (!exprToPrintableValue.ContainsKey(expr)) + exprToPrintableValue.Add(expr, ValueFromZID(errModel, i)); + return i; + } else { + return -1; + } + } + else if (expr is Bpl.NAryExpr) { + Bpl.NAryExpr e = (Bpl.NAryExpr)expr; + List zargs = new List(); + bool undefined = false; + // do the recursion first + foreach (Expr argument in ((NAryExpr) expr).Args) { + int zid = -1; + if (argument != null) { + zid = GetValuesFromModel(argument, incarnationMap, errModel, exprToPrintableValue); + } + // if one of the arguments is 'undefined' then return -1 ('noZid') for this + // but make sure the recursion is complete first1 + if (zid == -1) { + undefined = true; + } + zargs.Add(zid); + } + IAppliable! fun = e.Fun; + string functionName = fun.FunctionName; // PR: convert to select1, select2, etc in case of a map? + // same as IndexedExpr: + int id = TreatFunction(functionName, zargs, undefined, errModel); + if (id != -1 && !exprToPrintableValue.ContainsKey(expr)) { + exprToPrintableValue.Add(expr, ValueFromZID(errModel, id)); + } + return id; + } + else if (expr is Bpl.OldExpr) { + //Bpl.OldExpr e = (Bpl.OldExpr)expr; + // We do not know which heap is the old heap and what the latest state change was, + // therefore we cannot return anything useful here! + return -1; + } + else if (expr is Bpl.QuantifierExpr) { + Bpl.QuantifierExpr q = (Bpl.QuantifierExpr)expr; + for (int i = 0; i < q.Dummies.Length; i++) { + Bpl.Variable v = q.Dummies[i]; + if (v != null) { + // add to the incarnation map a connection between the bound variable v + // of the quantifier and its skolemized incarnation, if available, + // i.e., search through the list of identifiers in the model and look for + // v.sk.(q.SkolemId), only pick those that are directly associated to a value + // DISCLAIMER: of course it is very well possible that one of these incarnations + // could be used without actually having a value, but it seems better to pick those + // with a value, that is they are more likely to contribute useful information to + // the output + List quantVarIncarnationList = new List(); + List incarnationZidList = new List(); + int numberOfNonNullValueIncarnations = 0; + for (int j = 0; j < errModel.partitionToIdentifiers.Count; j++){ + List pti = errModel.partitionToIdentifiers[j]; + if (pti != null) { + for (int k = 0; k < pti.Count; k++) { + // look for v.sk.(q.SkolemId) + // if there is more than one look at if there is exactly one with a non-null value + // associated, see above explanation + if (pti[k].StartsWith(v + ".sk." + q.SkolemId) && + errModel.partitionToValue[errModel.identifierToPartition[pti[k]]] != null) { + quantVarIncarnationList.Add(new Bpl.IdentifierExpr(Bpl.Token.NoToken, pti[k], new Bpl.UnresolvedTypeIdentifier(Token.NoToken, "TName"))); + incarnationZidList.Add(j); + if (errModel.partitionToValue[errModel.identifierToPartition[pti[k]]] != null) { + numberOfNonNullValueIncarnations++; + } + } + } + } + } + // only one such variable found, associate it with v + if (quantVarIncarnationList.Count == 1) { + incarnationMap[v] = quantVarIncarnationList[0]; + } else if (quantVarIncarnationList.Count > 1 && numberOfNonNullValueIncarnations == 1) { + // if there are multiple candidate incarnations and exactly one of them has a value + // we can pick that one; otherwise it is not clear how to pick one out of multiple + // incarnations without a value or out of multiple incarnations with a value associated + for (int n = 0; n < incarnationZidList.Count; n++) { + if (errModel.partitionToValue[incarnationZidList[n]] != null) { + // quantVarIncarnationList and incarnationZidList are indexed in lockstep, so if + // for the associated zid the partitionToValue map is non-null then that is the one + // thus distinguished incarnation we want to put into the incarnationMap + incarnationMap[v] = quantVarIncarnationList[n]; + break; + } + } + } + } + } + // generate the value of the body but do not return that outside + GetValuesFromModel(q.Body, incarnationMap, errModel, exprToPrintableValue); + // the quantifier cannot contribute any one value to the rest of the + // expression, thus just return -1 + return -1; + } + else if (expr is Bpl.BvExtractExpr) { + Bpl.BvExtractExpr ex = (Bpl.BvExtractExpr) expr; + Bpl.Expr e0 = ex.Bitvector; + Bpl.Expr e1 = new LiteralExpr(Token.NoToken, BigNum.FromInt(ex.Start)); + Bpl.Expr e2 = new LiteralExpr(Token.NoToken, BigNum.FromInt(ex.End)); + string functionName = "$bv_extract"; + List zargs = new List(); + bool undefined = false; + + int zid = -1; + zid = GetValuesFromModel(e0, incarnationMap, errModel, exprToPrintableValue); + if (zid == -1) { + undefined = true; + } + zargs.Add(zid); + + zid = -1; + zid = GetValuesFromModel(e1, incarnationMap, errModel, exprToPrintableValue); + if (zid == -1) { + undefined = true; + } + zargs.Add(zid); + + zid = -1; + zid = GetValuesFromModel(e2, incarnationMap, errModel, exprToPrintableValue); + if (zid == -1) { + undefined = true; + } + zargs.Add(zid); + + //same as NAryExpr: + int id = TreatFunction(functionName, zargs, undefined, errModel); + if (id != -1 && !exprToPrintableValue.ContainsKey(expr)) { + exprToPrintableValue.Add(expr, ValueFromZID(errModel, id)); + } + return id; + } + else if (expr is Bpl.BvConcatExpr) { + // see comment above + Bpl.BvConcatExpr bvc = (Bpl.BvConcatExpr) expr; + string functionName = "$bv_concat"; + List zargs = new List(); + bool undefined = false; + + int zid = -1; + zid = GetValuesFromModel(bvc.E0, incarnationMap, errModel, exprToPrintableValue); + if (zid == -1) { + undefined = true; + } + zargs.Add(zid); + + zid = -1; + zid = GetValuesFromModel(bvc.E0, incarnationMap, errModel, exprToPrintableValue); + if (zid == -1) { + undefined = true; + } + zargs.Add(zid); + + //same as NAryExpr: + int id = TreatFunction(functionName, zargs, undefined, errModel); + if (id != -1 && !exprToPrintableValue.ContainsKey(expr)) { + exprToPrintableValue.Add(expr, ValueFromZID(errModel, id)); + } + return id; + } + else { + assert false; // unexpected Bpl.Expr + } + return -1; + } + + static Counterexample! AssertCmdToCounterexample (AssertCmd! cmd, TransferCmd! transferCmd, BlockSeq! trace, ErrorModel errModel, Dictionary! incarnationOriginMap) { + List! relatedInformation = new List(); + if (CommandLineOptions.Clo.EnhancedErrorMessages == 1) { + if (cmd.OrigExpr != null && cmd.IncarnationMap != null && errModel != null) { + + // get all possible information first + Dictionary exprToPrintableValue = new Dictionary(); + GetValuesFromModel(cmd.OrigExpr, cmd.IncarnationMap, errModel, exprToPrintableValue); + // then apply the strategies + ApplyEnhancedErrorPrintingStrategy(cmd.OrigExpr, cmd.IncarnationMap, cmd.ErrorDataEnhanced, errModel, exprToPrintableValue, relatedInformation, true, incarnationOriginMap); + } + } + + // See if it is a special assert inserted in translation + if (cmd is AssertRequiresCmd) + { + AssertRequiresCmd! assertCmd = (AssertRequiresCmd)cmd; + CallCounterexample cc = new CallCounterexample(trace, assertCmd.Call, assertCmd.Requires); + cc.relatedInformation = relatedInformation; + return cc; + } + else if (cmd is AssertEnsuresCmd) + { + AssertEnsuresCmd! assertCmd = (AssertEnsuresCmd)cmd; + ReturnCounterexample rc = new ReturnCounterexample(trace, transferCmd, assertCmd.Ensures); + rc.relatedInformation = relatedInformation; + return rc; + } + else + { + AssertCounterexample ac = new AssertCounterexample(trace, (AssertCmd)cmd); + ac.relatedInformation = relatedInformation; + return ac; + } + } + +// static void EmitImpl(Implementation! impl, bool printDesugarings) { +// 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, false), 0); +// CommandLineOptions.Clo.PrintDesugarings = oldPrintDesugaringSetting; +// CommandLineOptions.Clo.PrintUnstructured = oldPrintUnstructured; +// } + + static VCExpr! LetVC(Block! startBlock, + Variable controlFlowVariable, + Hashtable/**/! label2absy, + ProverContext! proverCtxt) + { + Hashtable/**/! blockVariables = new Hashtable/**/(); + List! bindings = new List(); + VCExpr startCorrect = LetVC(startBlock, controlFlowVariable, label2absy, blockVariables, bindings, proverCtxt); + return proverCtxt.ExprGen.Let(bindings, startCorrect); + } + + static VCExpr! LetVC(Block! block, + Variable controlFlowVariable, + Hashtable/**/! label2absy, + Hashtable/**/! blockVariables, + List! bindings, + ProverContext! proverCtxt) + { + VCExpressionGenerator! gen = proverCtxt.ExprGen; + 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) { + SuccCorrect = VCExpressionGenerator.True; + } else { + assert gotocmd.labelTargets != null; + List SuccCorrectVars = new List(gotocmd.labelTargets.Length); + foreach (Block! successor in gotocmd.labelTargets) { + VCExpr s = LetVC(successor, controlFlowVariable, label2absy, blockVariables, bindings, proverCtxt); + if (controlFlowVariable != null) + { + VCExprVar controlFlowVariableExpr = proverCtxt.BoogieExprTranslator.LookupVariable(controlFlowVariable); + 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, controlFlowVariable); + VCExpr vc = Wlp.Block(block, SuccCorrect, context); + + 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, + Hashtable/**/! label2absy, + Hashtable/**/! blockEquations, + ProverContext! proverCtxt) + { + VCExpressionGenerator! gen = proverCtxt.ExprGen; + 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 (!)gotocmd.labelTargets) { + VCExpr c = DagVC(successor, label2absy, blockEquations, proverCtxt); + SuccCorrect = SuccCorrect == null ? c : gen.And(SuccCorrect, c); + } + } + if (SuccCorrect == null) { + SuccCorrect = VCExpressionGenerator.True; + } + + VCContext context = new VCContext(label2absy, proverCtxt); + vc = Wlp.Block(block, SuccCorrect, context); + + // 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, + Hashtable/**/! label2absy, + bool local, bool reach, bool doomed, + ProverContext! proverCtxt) + requires local ==> !reach; // "reach" must be false for local + { + VCExpressionGenerator! gen = proverCtxt.ExprGen; + Hashtable/* Block --> VCExprVar */ BlkCorrect = BlockVariableMap(impl.Blocks, "_correct", gen); + Hashtable/* Block --> VCExprVar */ BlkReached = reach ? BlockVariableMap(impl.Blocks, "_reached", gen) : null; + + List blocks = impl.Blocks; + // block sorting is now done on the VCExpr + // if (!local && ((!)CommandLineOptions.Clo.TheProverFactory).NeedsBlockSorting) { + // blocks = SortBlocks(blocks); + // } + + VCExpr proofObligation; + if (!local) { + proofObligation = (VCExprVar!)BlkCorrect[impl.Blocks[0]]; + } else { + List conjuncts = new List(blocks.Count); + foreach (Block! b in blocks) { + VCExpr v = (VCExprVar!)BlkCorrect[b]; + conjuncts.Add(v); + } + proofObligation = gen.NAry(VCExpressionGenerator.AndOp, conjuncts); + } + + VCContext! context = new VCContext(label2absy, proverCtxt); + + List programSemantics = new List(blocks.Count); + foreach (Block! b in blocks) { + /* + * 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((VCExprVar!)BlkReached[b], wlp); + } + + VCExprVar okVar = (VCExprVar!)BlkCorrect[b]; + VCExprLetBinding binding = gen.LetBinding(okVar, wlp); + programSemantics.Add(binding); + } + + return gen.Let(programSemantics, proofObligation); + } + + private static Hashtable/* Block --> VCExprVar */! BlockVariableMap(List! blocks, string! suffix, + Microsoft.Boogie.VCExpressionGenerator! gen) + { + Hashtable/* Block --> VCExprVar */ map = new Hashtable/* Block --> (Let)Variable */(); + foreach (Block! b in blocks) + { + VCExprVar! v = gen.Variable(b.Label+suffix, Bpl.Type.Bool); + 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) + { + VCExpr SuccCorrect = null; + GotoCmd gotocmd = b.TransferCmd as GotoCmd; + if (gotocmd != null) + { + foreach (Block! successor in (!)gotocmd.labelTargets) + { + // c := S_correct + VCExpr c = (VCExprVar!)BlkCorrect[successor]; + if (BlkReached != null) + { + // c := S_correct \/ Sibling0_reached \/ Sibling1_reached \/ ...; + foreach (Block! successorSibling in gotocmd.labelTargets) + { + if (successorSibling != successor) + { + c = gen.Or(c, (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, + Hashtable/**/! label2absy, + bool reach, + ProverContext! proverCtxt) + requires impl.Blocks.Count != 0; + { + VCExpressionGenerator! gen = proverCtxt.ExprGen; + Graph! g = GraphFromImpl(impl); + + Hashtable/* Block --> VCExprVar */ BlkCorrect = BlockVariableMap(impl.Blocks, "_correct", gen); + Hashtable/* Block --> VCExprVar */ BlkReached = reach ? BlockVariableMap(impl.Blocks, "_reached", gen) : null; + + Block! startBlock = (!) impl.Blocks[0]; + VCExpr proofObligation = (VCExprVar!)BlkCorrect[startBlock]; + + 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) { + totalOrder[b] = i; + i++; + } + } + + VCExprLetBinding programSemantics = NestedBlockEquation((!)impl.Blocks[0], BlkCorrect, BlkReached, totalOrder, context, g, gen); + List ps = new List(1); + ps.Add(programSemantics); + + 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) + { + /* + * 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); + + List bindings = new List(); + foreach (Block! dominee in GetSortedBlocksImmediatelyDominatedBy(g, b, totalOrder)) + { + 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); + } + VCExprVar okVar = (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) { + List list = new List(); + foreach (Block! dominee in g.ImmediatelyDominatedBy(b)) { + list.Add(dominee); + } + list.Sort(new Comparison(delegate (Block! x, Block! y) {return (int)(!)totalOrder[x] - (int)(!)totalOrder[y];} )); + return list; + } + + static VCExpr! VCViaStructuredProgram + (Implementation! impl, Hashtable/**/! label2absy, + ProverContext! proverCtxt) + { + #region Convert block structure back to a "regular expression" + RE! r = DAG2RE.Transform((!)impl.Blocks[0]); + #endregion + + VCContext! ctxt = new VCContext(label2absy, proverCtxt); + #region Send wlp(program,true) to Simplify + return Wlp.RegExpr(r, VCExpressionGenerator.True, ctxt); + #endregion + } + + /// + /// 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 BlockSeq! RemoveEmptyBlocks(Block! b) + { + assert b.TraversingStatus == Block.VisitState.ToVisit; + Block renameInfo; + BlockSeq 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 BlockSeq! removeEmptyBlocksWorker(Block! b, bool startNode, out Block renameInfoForStartBlock) + ensures renameInfoForStartBlock != null ==> renameInfoForStartBlock.tok.IsValid; + // ensures: b in result ==> renameInfoForStartBlock == null; + { + renameInfoForStartBlock = null; + BlockSeq bs = new BlockSeq(); + GotoCmd gtc = b.TransferCmd as GotoCmd; + + // b has no successors + if (gtc == null || gtc.labelTargets == null || gtc.labelTargets.Length == 0) + { + if (b.Cmds.Length != 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.Length == 0 && !startNode) { + // b is about to become extinct; try to save its name and location, if possible + if (b.tok.IsValid && gtc.labelTargets.Length == 1) { + Block succ = (!)gtc.labelTargets[0]; + if (!succ.tok.IsValid && succ.Predecessors.Length == 1) { + succ.tok = b.tok; + succ.Label = b.Label; + } + } + } + + // recursively call this method on each successor + // merge result into a *set* of blocks + Dictionary mergedSuccessors = new Dictionary(); + 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){ + Block renameInfo; + BlockSeq! ys = removeEmptyBlocksWorker(dest, false, out renameInfo); + if (m == 0) { + renameInfoForStartBlock = renameInfo; + } else if (renameInfoForStartBlock != renameInfo) { + renameInfoForStartBlock = null; + } + foreach (Block successor in ys){ + if (!mergedSuccessors.ContainsKey(successor)) + mergedSuccessors.Add(successor,true); + } + m++; + } + b.TraversingStatus = Block.VisitState.AlreadyVisited; + + BlockSeq setOfSuccessors = new BlockSeq(); + foreach (Block d in mergedSuccessors.Keys) + setOfSuccessors.Add(d); + if (b.Cmds.Length == 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 StringSeq(); + foreach (Block! d in setOfSuccessors) + gtc.labelNames.Add(d.Label); + if (!startNode) { + renameInfoForStartBlock = null; + } + return new BlockSeq(b); + } + else // b has some successors, but we are already visiting it, or we have already visited it... + { + return new BlockSeq(b); + } + } + + static Graph! GraphFromImpl(Implementation! impl) { + Graph! g = new Graph(); + g.AddSource((!)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) + { + foreach (Block! dest in (!)gtc.labelTargets) + { + g.AddEdge(b,dest); + } + } + } + return g; + } + + + static void DumpMap(Hashtable /*Variable->Expr*/! map) { + foreach (DictionaryEntry de in map) { + Variable! v = (Variable!)de.Key; + Expr! e = (Expr!)de.Value; + Console.Write(" "); + v.Emit(new TokenTextWriter("", Console.Out, false), 0); + Console.Write(" --> "); + e.Emit(new TokenTextWriter("", Console.Out, false)); + Console.WriteLine(); + } + } + } +} diff --git a/Source/VCGeneration/VC.ssc b/Source/VCGeneration/VC.ssc deleted file mode 100644 index 0f42939d..00000000 --- a/Source/VCGeneration/VC.ssc +++ /dev/null @@ -1,4340 +0,0 @@ -//----------------------------------------------------------------------------- -// -// Copyright (C) Microsoft Corporation. All Rights Reserved. -// -//----------------------------------------------------------------------------- -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.Threading; -using System.IO; -using Microsoft.Boogie; -using Graphing; -using AI = Microsoft.AbstractInterpretationFramework; -using Microsoft.Contracts; -using Microsoft.Basetypes; -using Microsoft.Boogie.VCExprAST; - - -namespace VC -{ - using Bpl = Microsoft.Boogie; - - public class VCGen : ConditionGeneration - { - - /// - /// Constructor. Initializes the theorem prover. - /// - [NotDelayed] - public VCGen(Program! program, string/*?*/ logFilePath, bool appendLogFile) - // throws ProverException - { - this.appendLogFile = appendLogFile; - this.logFilePath = logFilePath; - implName2LazyInliningInfo = new Dictionary(); - implName2StratifiedInliningInfo = new Dictionary(); - base(program); - if (CommandLineOptions.Clo.LazyInlining > 0) - { - this.GenerateVCsForLazyInlining(program); - } - if (CommandLineOptions.Clo.StratifiedInlining > 0) - { - - this.GenerateVCsForStratifiedInlining(program); - - } - // base(); - } - - private static AssumeCmd! AssertTurnedIntoAssume(AssertCmd! assrt) - { - Expr! expr = assrt.Expr; - - 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: - assert false; // unexpected case - } - - return new AssumeCmd(assrt.tok, expr); - } - - #region LazyInlining - public class LazyInliningInfo { - public Implementation! impl; - public int uniqueId; - public Function! function; - public Variable! controlFlowVariable; - public List! interfaceVars; - public Expr! assertExpr; - public VCExpr vcexpr; - public Dictionary incarnationOriginMap; - public Hashtable /*Variable->Expr*/ exitIncarnationMap; - public Hashtable /*GotoCmd->returnCmd*/ gotoCmdOrigins; - public Hashtable/**/ label2absy; - - public LazyInliningInfo(Implementation! impl, Program! program, int uniqueId) - { - this.impl = impl; - this.uniqueId = uniqueId; - this.controlFlowVariable = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, "cfc", Microsoft.Boogie.Type.Int)); - - Procedure! proc = (!)impl.Proc; - List! interfaceVars = new List(); - Expr! assertExpr = new LiteralExpr(Token.NoToken, true); - foreach (Variable! v in program.GlobalVariables()) - { - interfaceVars.Add(v); - } - // InParams must be obtained from impl and not proc - foreach (Variable! v in impl.InParams) - { - interfaceVars.Add(v); - } - // OutParams must be obtained from impl and not proc - foreach (Variable! v in impl.OutParams) - { - 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 (IdentifierExpr! e in 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)); - interfaceVars.Add(c); - 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); - VariableSeq! functionInterfaceVars = new VariableSeq(); - foreach (Variable! v in interfaceVars) - { - 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); - Formal! returnVar = new Formal(Token.NoToken, ti, false); - this.function = new Function(Token.NoToken, proc.Name, functionInterfaceVars, returnVar); - } - } - - private Dictionary! implName2LazyInliningInfo; - - public void GenerateVCsForLazyInlining(Program! program) - { - Checker! checker = FindCheckerFor(null, CommandLineOptions.Clo.ProverKillTime); - foreach (Declaration! decl in program.TopLevelDeclarations) - { - Implementation impl = decl as Implementation; - if (impl == null) continue; - Procedure! proc = (!)impl.Proc; - if (proc.FindExprAttribute("inline") != null) { - LazyInliningInfo info = new LazyInliningInfo(impl, program, QuantifierExpr.GetNextSkolemId()); - implName2LazyInliningInfo[impl.Name] = info; - impl.LocVars.Add(info.controlFlowVariable); - ExprSeq! exprs = new ExprSeq(); - foreach (Variable! v in program.GlobalVariables()) - { - exprs.Add(new OldExpr(Token.NoToken, new IdentifierExpr(Token.NoToken, v))); - } - foreach (Variable! v in proc.InParams) - { - exprs.Add(new IdentifierExpr(Token.NoToken, v)); - } - foreach (Variable! v in proc.OutParams) - { - exprs.Add(new IdentifierExpr(Token.NoToken, v)); - } - foreach (IdentifierExpr! ie in proc.Modifies) - { - if (ie.Decl == null) continue; - exprs.Add(ie); - } - Expr freePostExpr = new NAryExpr(Token.NoToken, new FunctionCall(info.function), exprs); - proc.Ensures.Add(new Ensures(true, freePostExpr)); - } - } - - foreach (LazyInliningInfo! info in implName2LazyInliningInfo.Values) - { - GenerateVCForLazyInlining(program, info, checker); - } - } - - private void GenerateVCForLazyInlining(Program! program, LazyInliningInfo! info, Checker! checker) - requires info.impl != null; - requires info.impl.Proc != null; - { - Implementation! impl = info.impl; - ConvertCFG2DAG(impl, program); - info.gotoCmdOrigins = PassifyImpl(impl, program); - assert info.exitIncarnationMap != null; - Hashtable/**/! label2absy; - VCExpressionGenerator! gen = checker.VCExprGen; - VCExpr! vcexpr = gen.Not(GenerateVC(impl, info.controlFlowVariable, out label2absy, checker)); - info.label2absy = label2absy; - - Boogie2VCExprTranslator! translator = checker.TheoremProver.Context.BoogieExprTranslator; - List privateVars = new List(); - foreach (Variable! v in impl.LocVars) - { - privateVars.Add(translator.LookupVariable(v)); - } - foreach (Variable! v in impl.OutParams) - { - privateVars.Add(translator.LookupVariable(v)); - } - if (privateVars.Count > 0) - { - vcexpr = gen.Exists(new List(), privateVars, new List(), - new VCQuantifierInfos(impl.Name, info.uniqueId, false, null), vcexpr); - } - - List interfaceExprVars = new List(); - List interfaceExprs = new List(); - foreach (Variable! v in info.interfaceVars) - { - VCExprVar! ev = translator.LookupVariable(v); - interfaceExprVars.Add(ev); - interfaceExprs.Add(ev); - } - - Function! function = (!)info.function; - VCExpr! expr = gen.Function(function, interfaceExprs); - if (CommandLineOptions.Clo.LazyInlining == 1) { - vcexpr = gen.Implies(expr, vcexpr); - } else { - assert CommandLineOptions.Clo.LazyInlining == 2; - vcexpr = gen.Eq(expr, vcexpr); - } - - List triggers = new List(); - List exprs = new List(); - exprs.Add(expr); - VCTrigger! trigger = new VCTrigger(true, exprs); - triggers.Add(trigger); - - Expr e = new LiteralExpr(Token.NoToken, BigNum.FromInt(1)); - QKeyValue q = new QKeyValue(Token.NoToken, "weight", new List(new object![] { e }), null); - interfaceExprVars.Reverse(); - vcexpr = gen.Forall(new List(), interfaceExprVars, triggers, - new VCQuantifierInfos(impl.Name, QuantifierExpr.GetNextSkolemId(), false, q), vcexpr); - - info.vcexpr = vcexpr; - checker.TheoremProver.PushVCExpression(vcexpr); - } - #endregion - - #region StratifiedInlining - public class StratifiedInliningInfo : LazyInliningInfo - { - public int inline_cnt; - public List! privateVars; - public List! interfaceExprVars; - public VCExpr funcExpr; - public VCExpr falseExpr; - - public StratifiedInliningInfo(Implementation! impl, Program! program, int uniqueid) - : base(impl, program, uniqueid) - { - inline_cnt = 0; - privateVars = new List(); - interfaceExprVars = new List(); - } - - } - - private Dictionary! implName2StratifiedInliningInfo; - - public void GenerateVCsForStratifiedInlining(Program! program) - { - //Checker! checker = FindCheckerFor(null, CommandLineOptions.Clo.ProverKillTime); - foreach (Declaration! decl in program.TopLevelDeclarations) - { - Implementation impl = decl as Implementation; - if (impl == null) continue; - Procedure! proc = (!)impl.Proc; - if (proc.FindExprAttribute("inline") != null) { - StratifiedInliningInfo info = new StratifiedInliningInfo(impl, program, QuantifierExpr.GetNextSkolemId()); - implName2StratifiedInliningInfo[impl.Name] = info; - // We don't need controlFlowVariable for stratified Inlining - //impl.LocVars.Add(info.controlFlowVariable); - ExprSeq! exprs = new ExprSeq(); - foreach (Variable! v in program.GlobalVariables()) - { - exprs.Add(new OldExpr(Token.NoToken, new IdentifierExpr(Token.NoToken, v))); - } - foreach (Variable! v in proc.InParams) - { - exprs.Add(new IdentifierExpr(Token.NoToken, v)); - } - foreach (Variable! v in proc.OutParams) - { - exprs.Add(new IdentifierExpr(Token.NoToken, v)); - } - foreach (IdentifierExpr! ie in proc.Modifies) - { - if (ie.Decl == null) continue; - exprs.Add(ie); - } - Expr freePostExpr = new NAryExpr(Token.NoToken, new FunctionCall(info.function), exprs); - proc.Ensures.Add(new Ensures(true, freePostExpr)); - } - } - - } - - private void GenerateVCForStratifiedInlining(Program! program, StratifiedInliningInfo! info, Checker! checker) - requires info.impl != null; - requires info.impl.Proc != null; - { - Implementation! impl = info.impl; - ConvertCFG2DAG(impl, program); - info.gotoCmdOrigins = PassifyImpl(impl, program); - assert info.exitIncarnationMap != null; - Hashtable/**/! label2absy; - VCExpressionGenerator! gen = checker.VCExprGen; - VCExpr! vcexpr = gen.Not(GenerateVC(impl, null, out label2absy, checker)); - info.label2absy = label2absy; - - Boogie2VCExprTranslator! translator = checker.TheoremProver.Context.BoogieExprTranslator; - info.privateVars = new List(); - foreach (Variable! v in impl.LocVars) - { - info.privateVars.Add(translator.LookupVariable(v)); - } - foreach (Variable! v in impl.OutParams) - { - info.privateVars.Add(translator.LookupVariable(v)); - } - - info.interfaceExprVars = new List(); - List interfaceExprs = new List(); - foreach (Variable! v in info.interfaceVars) - { - VCExprVar! ev = translator.LookupVariable(v); - info.interfaceExprVars.Add(ev); - interfaceExprs.Add(ev); - } - - Function! function = (!)info.function; - info.funcExpr = gen.Function(function, interfaceExprs); - info.vcexpr = vcexpr; - - // Build the "false" expression: forall a b c: foo(a,b,c) <==> false - - info.falseExpr = gen.Eq(info.funcExpr, VCExpressionGenerator.False); - - List triggers = new List(); - List exprs = new List(); - exprs.Add(info.funcExpr); - VCTrigger! trigger = new VCTrigger(true, exprs); - triggers.Add(trigger); - - Expr e = new LiteralExpr(Token.NoToken, BigNum.FromInt(1)); - QKeyValue q = new QKeyValue(Token.NoToken, "weight", new List(new object![] { e }), null); - //info.interfaceExprVars.Reverse(); - info.falseExpr = gen.Forall(new List(), info.interfaceExprVars, triggers, - new VCQuantifierInfos(impl.Name, QuantifierExpr.GetNextSkolemId(), false, q), info.falseExpr); - - //checker.TheoremProver.PushVCExpression(vcexpr); - /* - Console.WriteLine("Procedure: {0}", info.impl.Name); - Console.Write("For all: "); - foreach(VCExprVar! v in info.interfaceExprVars) { - Console.Write(v.ToString() + " "); - } - Console.WriteLine(); - Console.Write("There exists: "); - foreach(VCExprVar! v in info.privateVars) { - Console.Write(v.ToString() + " "); - } - Console.WriteLine(); - Console.WriteLine(vcexpr.ToString()); - */ - } - #endregion - - #region Soundness smoke tester - class SmokeTester - { - VCGen! parent; - Implementation! impl; - Block! initial; - Program! program; - int id; - Dictionary! copies = new Dictionary(); - Dictionary! visited = new Dictionary(); - VerifierCallback! callback; - - internal SmokeTester(VCGen! par, Implementation! i, Program! prog, VerifierCallback! callback) - { - parent = par; - impl = i; - initial = i.Blocks[0]; - program = prog; - this.callback = callback; - } - - internal void Copy() - { - CloneBlock(impl.Blocks[0]); - initial = GetCopiedBlocks()[0]; - } - - internal void Test() - throws UnexpectedProverOutputException; - { - DFS(initial); - } - - void TopologicalSortImpl() - { - Graph dag = new Graph(); - dag.AddSource((!)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) - { - assume gtc.labelTargets != null; - foreach (Block! dest in gtc.labelTargets) - { - dag.AddEdge(b,dest); - } - } - } - impl.Blocks = new List(); - foreach (Block! b in dag.TopologicalSort()) { - impl.Blocks.Add(b); - } - } - - void Emit() - { - TopologicalSortImpl(); - EmitImpl(impl, false); - } - - // this one copies forward - Block! CloneBlock(Block! b) - { - Block fake_res; - if (copies.TryGetValue(b, out fake_res)) { - return (!)fake_res; - } - Block! res; - res = new Block(b.tok, b.Label, new CmdSeq(b.Cmds), null); - copies[b] = res; - if (b.TransferCmd is GotoCmd) { - foreach (Block! ch in (!)((GotoCmd)b.TransferCmd).labelTargets) { - CloneBlock(ch); - } - } - foreach (Block! p in b.Predecessors) { - res.Predecessors.Add(CloneBlock(p)); - } - return res; - } - - // this one copies backwards - Block! CopyBlock(Block! b) - { - Block fake_res; - if (copies.TryGetValue(b, out fake_res)) { - // fake_res should be Block! but the compiler fails - return (!)fake_res; - } - Block! res; - CmdSeq seq = new CmdSeq(); - foreach (Cmd! c in b.Cmds) { - 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) { - res.Predecessors.Add(CopyBlock(p)); - } - return res; - } - - List! GetCopiedBlocks() - { - // 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) { - 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 StringSeq(), new BlockSeq()); - kv.Value.TransferCmd = copy; - foreach (Block! b in (!)go.labelTargets) { - Block c; - if (copies.TryGetValue(b, out c)) { - copy.AddTarget((!)c); - } - } - } else if (ret != null) { - kv.Value.TransferCmd = ret; - } else { - assume false; - } - } - - 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) - { - 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((!)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 && - ((!)call.Args[0]).Equals(call.Args[1])) - { - val = false; - return true; - } - - return false; - } - - bool IsFalse(Expr! e) - { - bool val = false; - return BooleanEval(e, ref val) && !val; - } - - bool CheckUnreachable(Block! cur, CmdSeq! seq) - throws UnexpectedProverOutputException; - { - foreach (Cmd cmd in seq) { - AssertCmd assrt = cmd as AssertCmd; - if (assrt != null && QKeyValue.FindBoolAttribute(assrt.Attributes, "PossiblyUnreachable")) - return false; - } - - DateTime start = DateTime.Now; - 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); - copy.Cmds = seq; - List! backup = impl.Blocks; - 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.current_impl = impl; - parent.PassifyImpl(impl, program); - Hashtable! label2Absy; - Checker! ch = parent.FindCheckerFor(impl, CommandLineOptions.Clo.SmokeTimeout); - VCExpr! vc = parent.GenerateVC(impl, null, out label2Absy, ch); - impl.Blocks = backup; - - if (CommandLineOptions.Clo.TraceVerify) { - System.Console.WriteLine(" --- smoke #{0}, after passify", id); - Emit(); - } - ch.BeginCheck((!) impl.Name + "_smoke" + id++, vc, new ErrorHandler(label2Absy, this.callback)); - ch.ProverDone.WaitOne(); - ProverInterface.Outcome outcome = ch.ReadOutcome(); - parent.current_impl = null; - - DateTime end = DateTime.Now; - 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) - throws UnexpectedProverOutputException; - { - if (visited.ContainsKey(cur)) return; - visited[cur] = true; - - CmdSeq! seq = new CmdSeq(); - foreach (Cmd! cmd_ in cur.Cmds) { - Cmd! cmd = cmd_; - 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 (turnAssertIntoAssumes) { - cmd = AssertTurnedIntoAssume(assrt); - } - } else if (assm != null) { - if (IsFalse(assm.Expr)) assumeFalse = true; - } else if (call != null) { - foreach (Ensures! e in ((!)call.Proc).Ensures) { - 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; - - assume !(go!= null&&go.labelTargets==null&&go.labelNames!=null&&go.labelNames.Length>0) ; - - if (ret != null || (go != null && ((!)go.labelTargets).Length == 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 (!)go.labelTargets) { - if (target.Predecessors.Length == 1) { - needToCheck = false; - } - } - if (needToCheck) { - CheckUnreachable(cur, seq); - } - foreach (Block! target in go.labelTargets) { - DFS(target); - } - } - } - - class ErrorHandler : ProverInterface.ErrorHandler { - Hashtable! label2Absy; - VerifierCallback! callback; - - public ErrorHandler(Hashtable! label2Absy, VerifierCallback! callback) { - this.label2Absy = label2Absy; - this.callback = callback; - } - - public override Absy! Label2Absy(string! label) { - int id = int.Parse(label); - return (Absy!) label2Absy[id]; - } - - public override void OnProverWarning(string! msg) { - 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 Dictionary? reachable_blocks; - public readonly Block! block; - - public BlockStats(Block! b, int i) - { - block = b; - assertion_cost = -1; - id = i; - } - } - - readonly List! blocks; - readonly List! big_blocks = new List(); - readonly Dictionary! stats = new Dictionary(); - readonly int id; - static int current_id; - Block? split_block; - bool assert_to_assume; - List! assumized_branches = new List(); - public AssertCmd? first_assert; - - double score; - bool score_computed; - double total_cost; - int assertion_count; - double assertion_cost; // without multiplication by paths - Hashtable/*TransferCmd->ReturnCmd*/! gotoCmdOrigins; - VCGen! parent; - Implementation! impl; - - Dictionary! copies = new Dictionary(); - bool doing_slice; - double slice_initial_limit; - double slice_limit; - bool slice_pos; - Dictionary! protected_from_assert_to_assume = new Dictionary(); - Dictionary! keep_at_all = new Dictionary(); - - // async interface - private Checker checker; - private int splitNo; - internal ErrorReporter reporter; - - public Split(List! blocks, Hashtable/*TransferCmd->ReturnCmd*/! gotoCmdOrigins, VCGen! par, Implementation! impl) - { - this.blocks = blocks; - this.gotoCmdOrigins = gotoCmdOrigins; - this.parent = par; - this.impl = impl; - this.id = 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; - assumized_branches = new List(); - DoComputeScore(false); - assumized_branches = saved; - - foreach (Block! b in big_blocks) { - BlockStats s = GetBlockStats(b); - foreach (Block! t in s.virtual_successors) { - 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; - impl.Blocks = blocks; - impl.Emit(new TokenTextWriter(filename, sw, false), 0); - impl.Blocks = backup; - CommandLineOptions.Clo.PrintDesugarings = oldPrintDesugaringSetting; - CommandLineOptions.Clo.PrintUnstructured = oldPrintUnstructured; - } - } - - int bsid; - BlockStats! GetBlockStats(Block! b) - { - BlockStats s; - if (!stats.TryGetValue(b, out s)) { - s = new BlockStats(b, bsid++); - stats[b] = s; - } - return (!)s; - } - - double AssertionCost(PredicateCmd c) - { - return 1.0; - } - - void CountAssertions(Block! b) - { - 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! b in Exits(b)) { - s.virtual_successors.Add(b); - } - if (s.virtual_successors.Count == 1) { - Block next = s.virtual_successors[0]; - BlockStats se = GetBlockStats(next); - CountAssertions(next); - if (next.Predecessors.Length > 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; - } - } - - Dictionary! ComputeReachableNodes(Block! b) - { - BlockStats s = GetBlockStats(b); - if (s.reachable_blocks != null) { - return s.reachable_blocks; - } - Dictionary blocks = new Dictionary(); - s.reachable_blocks = blocks; - blocks[b] = true; - foreach (Block! succ in Exits(b)) { - foreach (Block! r in ComputeReachableNodes(succ).Keys) { - blocks[r] = true; - } - } - 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) { - CountAssertions(b); - } - - foreach (Block! b in blocks) { - BlockStats bs = GetBlockStats(b); - if (bs.big_block) { - big_blocks.Add(b); - foreach (Block! ch in bs.virtual_successors) { - BlockStats chs = GetBlockStats(ch); - if (!chs.big_block) { - Console.WriteLine("non-big {0} accessed from {1}", ch, b); - DumpDot(-1); - assert false; - } - 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) { - GotoCmd gt = b.TransferCmd as GotoCmd; - if (gt == null) continue; - BlockSeq targ = (!)gt.labelTargets; - if (targ.Length < 2) continue; - // caution, we only consider two first exits - - double left0, right0, left1, right1; - split_block = b; - - assumized_branches.Clear(); - assumized_branches.Add((!)targ[0]); - left0 = DoComputeScore(true); - right0 = DoComputeScore(false); - - assumized_branches.Clear(); - for (int idx = 1; idx < targ.Length; idx++) { - assumized_branches.Add((!)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((!)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) - { - if (s.incomming_paths < 0.0) { - int count = 0; - s.incomming_paths = 0.0; - if (!keep_at_all.ContainsKey(s.block)) return; - foreach (Block! b in s.virtual_predecesors) { - BlockStats! ch = GetBlockStats(b); - 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) - { - if (keep_at_all.ContainsKey(b)) return; - keep_at_all[b] = true; - - if (allow_small) { - foreach (Block! ch in Exits(b)) { - if (b == split_block && assumized_branches.Contains(ch)) continue; - ComputeBlockSetsHelper(ch, allow_small); - } - } else { - foreach (Block! ch in GetBlockStats(b).virtual_successors) { - 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) { - if (ComputeReachableNodes(b).ContainsKey((!)split_block)) { - keep_at_all[b] = true; - } - } - - foreach (Block! b in assumized_branches) { - foreach (Block! r in ComputeReachableNodes(b).Keys) { - if (allow_small || GetBlockStats(r).big_block) { - keep_at_all[r] = true; - protected_from_assert_to_assume[r] = true; - } - } - } - } else { - ComputeBlockSetsHelper(blocks[0], allow_small); - } - } - - bool ShouldAssumize(Block! b) - { - return assert_to_assume && !protected_from_assert_to_assume.ContainsKey(b); - } - - double DoComputeScore(bool aa) - { - assert_to_assume = aa; - ComputeBlockSets(false); - - foreach (Block! b in big_blocks) { - GetBlockStats(b).incomming_paths = -1.0; - } - - GetBlockStats(blocks[0]).incomming_paths = 1.0; - - double cost = 0.0; - foreach (Block! b in big_blocks) { - if (keep_at_all.ContainsKey(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; - } - - CmdSeq! SliceCmds(Block! b) - { - CmdSeq! seq = b.Cmds; - if (!doing_slice && !ShouldAssumize(b)) return seq; - CmdSeq! res = new CmdSeq(); - foreach (Cmd! c in seq) { - 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 { - assert false; - } - - if (swap) { - the_new = AssertTurnedIntoAssume(a); - } - } - res.Add(the_new); - } - return res; - } - - Block! CloneBlock(Block! b) - { - Block res; - if (copies.TryGetValue(b, out res)) { - return (!)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 StringSeq(), new BlockSeq()); - res.TransferCmd = newGoto; - int pos = 0; - foreach (Block! ch in (!)gt.labelTargets) { - assert doing_slice || - (!assert_to_assume ==> (keep_at_all.ContainsKey(ch) || assumized_branches.Contains(ch))); - if (doing_slice || - ((b != split_block || assumized_branches.Contains(ch) == assert_to_assume) && - keep_at_all.ContainsKey(ch))) { - newGoto.AddTarget(CloneBlock(ch)); - } - pos++; - } - } - return res; - } - - Split! DoSplit() - { - copies.Clear(); - CloneBlock(blocks[0]); - List newBlocks = new List(); - Hashtable newGotoCmdOrigins = new Hashtable(); - foreach (Block! b in blocks) { - Block tmp; - if (copies.TryGetValue(b, out tmp)) { - newBlocks.Add((!)tmp); - if (gotoCmdOrigins.ContainsKey(b)) { - newGotoCmdOrigins[tmp] = gotoCmdOrigins[b]; - } - - foreach (Block! p in b.Predecessors) { - Block tmp2; - if (copies.TryGetValue(p, out tmp2)) { - tmp.Predecessors.Add(tmp2); - } - } - } - } - - return new Split(newBlocks, newGotoCmdOrigins, parent, impl); - } - - Split! SplitAt(int idx) - { - assert_to_assume = idx == 0; - doing_slice = false; - ComputeBlockSets(true); - - return DoSplit(); - } - - Split! SliceAsserts(double limit, bool pos) - { - 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; - impl.Blocks = blocks; - EmitImpl(impl, false); - impl.Blocks = tmp; - } - - public Counterexample! ToCounterexample() - { - BlockSeq trace = new BlockSeq(); - foreach (Block! b in blocks) { - trace.Add(b); - } - foreach (Block! b in blocks) { - foreach (Cmd! c in b.Cmds) { - if (c is AssertCmd) { - return AssertCmdToCounterexample((AssertCmd)c, (!)b.TransferCmd, trace, null, new Dictionary()); - } - } - } - assume false; - } - - public static List! DoSplit(Split! initial, double max_cost, int max) - { - 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) { - 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 (!)g.labelTargets) { - Console.Write("{0} ", b.Label); - } - Console.WriteLine(""); - Console.Write(" assumized: "); - foreach (Block! b in best.assumized_branches) { - 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 Dictionary(), best.blocks[0], ss); - } catch (System.Exception e) { - Console.WriteLine(e); - best.DumpDot(-1); - s0.DumpDot(-2); - s1.DumpDot(-3); - assert false; - } - } - - 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; - } - - public Checker! Checker - { - get { - assert checker != null; - return checker; - } - } - - public WaitHandle ProverDone - { - get { - assert checker != null; - return checker.ProverDone; - } - } - - public void ReadOutcome(ref Outcome cur_outcome, out bool prover_failed) - throws UnexpectedProverOutputException; - { - ProverInterface.Outcome outcome = ((!)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: - prover_failed = true; - if (cur_outcome != Outcome.Errors) - cur_outcome = Outcome.Inconclusive; - return; - default: - assert false; - } - } - - public void BeginCheck(VerifierCallback! callback, int no, int timeout) - { - splitNo = no; - - impl.Blocks = blocks; - - checker = parent.FindCheckerFor(impl, timeout); - - Hashtable/**/! label2absy; - VCExpr! vc = parent.GenerateVC(impl, null, out label2absy, checker); - - if (CommandLineOptions.Clo.vcVariety == CommandLineOptions.VCVariety.Local) { - reporter = new ErrorReporterLocal(gotoCmdOrigins, label2absy, impl.Blocks, parent.incarnationOriginMap, callback, parent.implName2LazyInliningInfo, (DeclFreeProverContext!) this.Checker.TheoremProver.Context, parent.program); - } else { - reporter = new ErrorReporter(gotoCmdOrigins, label2absy, impl.Blocks, parent.incarnationOriginMap, callback, parent.implName2LazyInliningInfo, (DeclFreeProverContext!) this.Checker.TheoremProver.Context, parent.program); - } - - if (CommandLineOptions.Clo.TraceVerify && no >= 0) - { - Console.WriteLine("-- after split #{0}", no); - Print(); - } - - string! desc = (!) impl.Name; - if (no >= 0) - desc += "_split" + no; - checker.BeginCheck(desc, vc, reporter); - } - - private void SoundnessCheck(Dictionary! cache, Block! orig, List! copies) - { - { - PureCollections.Tuple t = new PureCollections.Tuple(new PureCollections.Capacity(1 + copies.Count)); - int i = 0; - t[i++] = orig; - foreach (Block! b in copies) { - t[i++] = b; - } - if (cache.ContainsKey(t)) { return; } - cache[t] = true; - } - - for (int i = 0; i < orig.Cmds.Length; ++i) { - Cmd cmd = orig.Cmds[i]; - if (cmd is AssertCmd) { - int found = 0; - foreach (Block! c in copies) { - 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)) { - List! newcopies = new List(); - foreach (Block! c in copies) { - foreach (Block! cexit in Exits(c)) { - 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 - - - protected VCExpr! GenerateVC(Implementation! impl, Variable controlFlowVariable, out Hashtable/**/! label2absy, Checker! ch) - { - TypecheckingContext tc = new TypecheckingContext(null); - impl.Typecheck(tc); - - label2absy = new Hashtable/**/(); - VCExpr! vc; - switch (CommandLineOptions.Clo.vcVariety) { - case CommandLineOptions.VCVariety.Structured: - vc = VCViaStructuredProgram(impl, label2absy, ch.TheoremProver.Context); - break; - case CommandLineOptions.VCVariety.Block: - vc = FlatBlockVC(impl, label2absy, false, false, false, ch.TheoremProver.Context); - break; - case CommandLineOptions.VCVariety.BlockReach: - vc = FlatBlockVC(impl, label2absy, false, true, false, ch.TheoremProver.Context); - break; - case CommandLineOptions.VCVariety.Local: - vc = FlatBlockVC(impl, label2absy, true, false, false, ch.TheoremProver.Context); - break; - case CommandLineOptions.VCVariety.BlockNested: - vc = NestedBlockVC(impl, label2absy, false, ch.TheoremProver.Context); - break; - case CommandLineOptions.VCVariety.BlockNestedReach: - vc = NestedBlockVC(impl, label2absy, true, ch.TheoremProver.Context); - break; - case CommandLineOptions.VCVariety.Dag: - if (((!)CommandLineOptions.Clo.TheProverFactory).SupportsDags) { - vc = DagVC((!)impl.Blocks[0], label2absy, new Hashtable/**/(), ch.TheoremProver.Context); - } else { - vc = LetVC((!)impl.Blocks[0], controlFlowVariable, label2absy, ch.TheoremProver.Context); - } - break; - case CommandLineOptions.VCVariety.Doomed: - vc = FlatBlockVC(impl, label2absy, false, false, true, ch.TheoremProver.Context); - break; - default: - assert false; // unexpected enumeration value - } - return vc; - } - - void CheckIntAttributeOnImpl(Implementation! impl, string! name, ref int val) - { - if (!((!)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, Program! program, VerifierCallback! callback) - throws UnexpectedProverOutputException; - { - if (impl.SkipVerification) { - return Outcome.Inconclusive; // not sure about this one - } - - if (CommandLineOptions.Clo.StratifiedInlining > 0) { - return StratifiedVerifyImplementation(impl, program, callback); - } - - callback.OnProgress("VCgen", 0, 0, 0.0); - - ConvertCFG2DAG(impl, program); - - SmokeTester smoke_tester = null; - if (CommandLineOptions.Clo.SoundnessSmokeTest) { - smoke_tester = new SmokeTester(this, impl, program, callback); - smoke_tester.Copy(); - } - - Hashtable/*TransferCmd->ReturnCmd*/ gotoCmdOrigins = PassifyImpl(impl, program); - - 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; - - int cores = CommandLineOptions.Clo.VcsCores; - Stack work = new Stack(); - List currently_running = new List(); - ResetPredecessors(impl.Blocks); - 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.Count > 0 || currently_running.Count > 0) { - bool prover_failed = false; - Split! s; - - if (work.Count > 0 && currently_running.Count < cores) { - s = work.Pop(); - - if (first_round && max_splits > 1) { - prover_failed = true; - remaining_cost -= s.Cost; - } else { - 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)); - - s.BeginCheck(callback, no, - (keep_going && s.LastChance) ? CommandLineOptions.Clo.VcsFinalAssertTimeout : - keep_going ? CommandLineOptions.Clo.VcsKeepGoingTimeout : - CommandLineOptions.Clo.ProverKillTime); - - no++; - - currently_running.Add(s); - } - } else { - WaitHandle[] handles = new WaitHandle[currently_running.Count]; - for (int i = 0; i < currently_running.Count; ++i) { - handles[i] = currently_running[i].ProverDone; - } - int index = WaitHandle.WaitAny(handles); - s = currently_running[index]; - currently_running.RemoveAt(index); - - if (do_splitting) { - remaining_cost -= s.Cost; - } - - 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(), msg); - outcome = Outcome.Errors; - break; - } - - assert prover_failed || outcome == Outcome.Correct || outcome == Outcome.Errors; - } - - 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); - max_vc_cost = 1.0; // for future - first_round = false; - //tmp.Sort(new Comparison(Split.Compare)); - foreach (Split! a in tmp) { - work.Push(a); - total++; - remaining_cost += a.Cost; - } - if (outcome != Outcome.Errors) { - outcome = Outcome.Correct; - } - } else { - 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); - - return outcome; - } - - public override Outcome StratifiedVerifyImplementation(Implementation! impl, Program! program, VerifierCallback! callback) - throws UnexpectedProverOutputException; - { - // This flag control the nature of queries made by StratifiedVerifyImplementation - // true: incremental search; false: in-place inlining - bool incrementalSearch = true; - - // Get the checker - Checker! checker = FindCheckerFor(null, CommandLineOptions.Clo.ProverKillTime); - - // Run live variable analysis - if(CommandLineOptions.Clo.LiveVariableAnalysis == 2) { - Microsoft.Boogie.InterProcGenKill.ComputeLiveVars(impl, program); - } - - // Build VCs for all procedures - assert implName2StratifiedInliningInfo != null; - foreach(StratifiedInliningInfo! info in implName2StratifiedInliningInfo.Values) - { - GenerateVCForStratifiedInlining(program, info, checker); - } - - // Get the VC of the current procedure - VCExpr! vc; - StratifiedInliningErrorReporter! reporter; - Hashtable/**/! mainLabel2absy; - GetVC(impl, program, callback, out vc, out mainLabel2absy, out reporter); - - // Find all procedure calls in vc and put labels on them - FCallHandler calls = new FCallHandler(checker.VCExprGen, implName2StratifiedInliningInfo, mainLabel2absy); - calls.setCurrProcAsMain(); - vc = calls.Mutate(vc, true); - reporter.SetCandidateHandler(calls); - - Outcome ret = Outcome.Correct; - - int expansionCount = 0; - int total_axioms_pushed = 0; - - // Do 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) { - toExpand.Add(id); - } - expansionCount += toExpand.Count; - if(incrementalSearch) - { - total_axioms_pushed += - DoExpansion(toExpand, calls, checker); - } else - { - vc = DoExpansionAndInline(vc, toExpand, calls, checker); - } - } - - int cnt = 0; - while(cnt < 500) { - cnt ++; - - // Push "false" - checker.TheoremProver.LogComment(";;;;;;;;;;;; Underapprox mode begin ;;;;;;;;;;"); - - int prev_axioms_pushed = checker.TheoremProver.NumAxiomsPushed(); - - foreach(int id in calls.currCandidates) { - VCExprNAry! vce = calls.id2Candidate[id]; - VCExpr! falseExpr = checker.VCExprGen.Eq(vce, VCExpressionGenerator.False); - checker.TheoremProver.PushVCExpression(falseExpr); - } - int axioms_pushed = checker.TheoremProver.NumAxiomsPushed(); - - // Note: axioms_pushed may not be the same as calls.currCandidates.Count - // because PushVCExpression pushes other stuff too (which always seems - // to be TRUE) - - reporter.underapproximationMode = true; - - // Check! - //Console.Write("Checking with preds == false: "); Console.Out.Flush(); - ret = CheckVC(vc, reporter, checker, impl.Name); - //Console.WriteLine(ret.ToString()); - - // Pop - for(int i = 0; i < axioms_pushed - prev_axioms_pushed; i++) { - checker.TheoremProver.Pop(); - } - - checker.TheoremProver.LogComment(";;;;;;;;;;;; Underapprox mode end ;;;;;;;;;;"); - - if(ret == Outcome.Errors) { - break; - } - - // If we didn't underapproximate, then we're done - if(calls.currCandidates.Count == 0) { - break; - } - - checker.TheoremProver.LogComment(";;;;;;;;;;;; Overapprox mode begin ;;;;;;;;;;"); - // Push "true" (No-op) - // Check - reporter.underapproximationMode = false; - - //Console.Write("Checking with preds == true: "); Console.Out.Flush(); - ret = CheckVC(vc, reporter, checker, impl.Name); - //Console.WriteLine(ret.ToString()); - - if(ret == Outcome.Correct) { - break; - } - - checker.TheoremProver.LogComment(";;;;;;;;;;;; Overapprox mode end ;;;;;;;;;;"); - - checker.TheoremProver.LogComment(";;;;;;;;;;;; Expansion begin ;;;;;;;;;;"); - - // Look at the errors to see what to inline - assert reporter.candidatesToExpand.Count != 0; - - expansionCount += reporter.candidatesToExpand.Count; - - if(incrementalSearch) - { - total_axioms_pushed += - DoExpansion(reporter.candidatesToExpand, calls, checker); - } else - { - vc = DoExpansionAndInline(vc, reporter.candidatesToExpand, calls, checker); - } - - checker.TheoremProver.LogComment(";;;;;;;;;;;; Expansion end ;;;;;;;;;;"); - } - - // Pop off everything that we pushed so that there are no side effects from - // this call to VerifyImplementation - for(int i = 0; i < total_axioms_pushed; i++) { - checker.TheoremProver.Pop(); - } - - if(cnt == 500) - { - checker.TheoremProver.LogComment("Stratified Inlining: timeout"); - } - - checker.TheoremProver.LogComment(string.Format("Stratified Inlining: Calls to Z3: {0}", 2*cnt)); - checker.TheoremProver.LogComment(string.Format("Stratified Inlining: Expansions performed: {0}", expansionCount)); - checker.TheoremProver.LogComment(string.Format("Stratified Inlining: Candidates left: {0}", calls.currCandidates.Count)); - - return ret; - } - - // A counter for adding new variables - static int newVarCnt = 0; - - // Does on-demand inlining -- pushes procedure bodies on the theorem prover stack. - // Returns the number of axioms pushed. - private int DoExpansion(List! candidates, - FCallHandler! calls, Checker! checker) - throws UnexpectedProverOutputException; - { - int old_axioms_pushed = checker.TheoremProver.NumAxiomsPushed(); - VCExpr! exprToPush = VCExpressionGenerator.True; - foreach(int id in candidates) { - VCExprNAry! expr = calls.id2Candidate[id]; - string procName = ((!)(expr.Op as VCExprBoogieFunctionOp)).Func.Name; - if(!implName2StratifiedInliningInfo.ContainsKey(procName)) continue; - - //Console.WriteLine("Expanding: {0}", expr.ToString()); - - StratifiedInliningInfo info = implName2StratifiedInliningInfo[procName]; - VCExpr! expansion = (!)info.vcexpr; - - // Instantiate the "forall" variables - Dictionary! substForallDict = new Dictionary(); - 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(checker.VCExprGen); - expansion = subst.Mutate(expansion, substForall); - - // Instantiate and declare the "exists" variables - Dictionary! substExistsDict = new Dictionary(); - for(int i = 0; i < info.privateVars.Count; i++) { - VCExprVar! v = info.privateVars[i]; - string newName = v.Name + "_si_" + newVarCnt.ToString(); - newVarCnt ++; - checker.TheoremProver.Context.DeclareConstant(new Constant(Token.NoToken, new TypedIdent(Token.NoToken, newName, v.Type)), false, null); - substExistsDict.Add(v, checker.VCExprGen.Variable(newName, v.Type)); - } - VCExprSubstitution substExists = new VCExprSubstitution(substExistsDict, new Dictionary()); - - subst = new SubstitutingVCExprVisitor(checker.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(expr, expansion); - exprToPush = checker.VCExprGen.And(exprToPush, expansion); - //checker.TheoremProver.PushVCExpression(expansion); - - } - checker.TheoremProver.PushVCExpression(exprToPush); - - int axioms_pushed = checker.TheoremProver.NumAxiomsPushed() - old_axioms_pushed; - checker.TheoremProver.FlushAxiomsToTheoremProver(); - return axioms_pushed; - } - - // Does on-demand inlining -- pushes procedure bodies on the theorem prover stack. - // Returns the number of axioms pushed. - private VCExpr! DoExpansionAndInline( - VCExpr! mainVC, List! candidates, - FCallHandler! calls, Checker! checker) - throws UnexpectedProverOutputException; - { - FCallInliner! inliner = new FCallInliner(checker.VCExprGen); - foreach(int id in candidates) { - VCExprNAry! expr = calls.id2Candidate[id]; - - string procName = ((!)(expr.Op as VCExprBoogieFunctionOp)).Func.Name; - if(!implName2StratifiedInliningInfo.ContainsKey(procName)) continue; - - StratifiedInliningInfo info = implName2StratifiedInliningInfo[procName]; - VCExpr! expansion = (!)info.vcexpr; - - // Instantiate the "forall" variables - Dictionary! substForallDict = new Dictionary(); - 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(checker.VCExprGen); - expansion = subst.Mutate(expansion, substForall); - - // Instantiate and declare the "exists" variables - Dictionary! substExistsDict = new Dictionary(); - for(int i = 0; i < info.privateVars.Count; i++) { - VCExprVar! v = info.privateVars[i]; - string newName = v.Name + "_si_" + newVarCnt.ToString(); - newVarCnt ++; - checker.TheoremProver.Context.DeclareConstant(new Constant(Token.NoToken, new TypedIdent(Token.NoToken, newName, v.Type)), false, null); - substExistsDict.Add(v, checker.VCExprGen.Variable(newName, v.Type)); - } - VCExprSubstitution substExists = new VCExprSubstitution(substExistsDict, new Dictionary()); - - subst = new SubstitutingVCExprVisitor(checker.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); - - inliner.subst.Add(id, expansion); - - } - - return inliner.Mutate(mainVC, true); - } - - // Return the VC for the impl (don't pass it to the theorem prover). - // GetVC is a cheap imitation of VerifyImplementatio, except that the VC is not passed to the theorem prover. - private void GetVC(Implementation! impl, Program! program, VerifierCallback! callback, out VCExpr! vc, out Hashtable/**/! label2absy, out StratifiedInliningErrorReporter! reporter) - { - ConvertCFG2DAG(impl, program); - Hashtable/*TransferCmd->ReturnCmd*/ gotoCmdOrigins = PassifyImpl(impl, program); - Checker! checker = FindCheckerFor(impl, CommandLineOptions.Clo.ProverKillTime); - - vc = GenerateVC(impl, null, out label2absy, checker); - - /* - ErrorReporter errReporter; - if (CommandLineOptions.Clo.vcVariety == CommandLineOptions.VCVariety.Local) { - errReporter = new ErrorReporterLocal(gotoCmdOrigins, label2absy, impl.Blocks, incarnationOriginMap, callback, implName2LazyInliningInfo, (DeclFreeProverContext!) checker.TheoremProver.Context, program); - } else { - errReporter = new ErrorReporter(gotoCmdOrigins, label2absy, impl.Blocks, incarnationOriginMap, callback, implName2LazyInliningInfo, (DeclFreeProverContext!) checker.TheoremProver.Context, program); - } - */ - - reporter = new StratifiedInliningErrorReporter( - (!)implName2StratifiedInliningInfo, checker.TheoremProver, callback, - (DeclFreeProverContext)checker.TheoremProver.Context, gotoCmdOrigins, program, impl); - - } - - private Outcome CheckVC(VCExpr! vc, StratifiedInliningErrorReporter! reporter, Checker! checker, string! implName) - throws UnexpectedProverOutputException; - { - checker.TheoremProver.FlushAxiomsToTheoremProver(); - checker.BeginCheck(implName, vc, reporter); - checker.ProverDone.WaitOne(); - - ProverInterface.Outcome outcome = (checker).ReadOutcome(); - - //checker.BeginCheck(implName, vc, reporter); - //checker.ProverDone.WaitOne(); - //outcome = (checker).ReadOutcome(); - - switch (outcome) { - case ProverInterface.Outcome.Valid: - return Outcome.Correct; - 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; - default: - assert false; - } - - } - - /* - // Collects all function calls in the VCExpr - public class FCallCollector : TraversingVCExprVisitor { - Dictionary! implName2StratifiedInliningInfo; - public List! fcalls; - - public FCallCollector(Dictionary! implName2StratifiedInliningInfo) { - this.implName2StratifiedInliningInfo = implName2StratifiedInliningInfo; - fcalls = new List(); - } - - protected override bool StandardResult(VCExpr! node, bool arg) { - VCExprNAry vnary = node as VCExprNAry; - if(vnary == null) return true; - VCExprBoogieFunctionOp bop = vnary.Op as VCExprBoogieFunctionOp; - if(bop == null) return true; - if(implName2StratifiedInliningInfo.ContainsKey(bop.Func.Name)) { - fcalls.Add(vnary); - } - return true; - } - - } - */ - - // 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 Hashtable/**/! mainLabel2absy; - public Dictionary! boogieExpr2Id; - public Dictionary! id2Candidate; - public Dictionary! candidate2Id; - public Dictionary! label2Id; - public Microsoft.SpecSharp.Collections.Set currCandidates; - - // Name of the procedure who's VC we're mutating - string currProc; - - // The 0^th candidate is main - static int candidateCount = 1; - public int currInlineCount; - - public FCallHandler(VCExpressionGenerator! gen, - Dictionary! implName2StratifiedInliningInfo, - Hashtable/**/! mainLabel2absy) - : base(gen) - { - this.implName2StratifiedInliningInfo = implName2StratifiedInliningInfo; - this.mainLabel2absy = mainLabel2absy; - id2Candidate = new Dictionary(); - candidate2Id = new Dictionary(); - boogieExpr2Id = new Dictionary(); - label2Id = new Dictionary(); - currCandidates = new Microsoft.SpecSharp.Collections.Set(); - currInlineCount = 0; - currProc = null; - } - - public void Clear() - { - currCandidates = new Microsoft.SpecSharp.Collections.Set(); - } - - private int GetId(VCExprNAry! vc) - { - if(candidate2Id.ContainsKey(vc)) - return candidate2Id[vc]; - - - int id = candidateCount; - candidate2Id[vc] = id; - id2Candidate[id] = vc; - - candidateCount ++; - currCandidates.Add(id); - - return id; - } - - private string! GetLabel(int id) - { - string! ret = "si_fcall_" + id.ToString(); - if(!label2Id.ContainsKey(ret)) - label2Id[ret] = id; - - return ret; - } - - public int GetId(string! label) - { - if(!label2Id.ContainsKey(label)) return -1; - return label2Id[label]; - } - - public string! RenameAbsyLabel(string !label) - requires label.Length >= 1; - { - // Remove the sign from the label - string! nosign = label.Substring(1); - return "si_inline_" + currInlineCount.ToString() + "_" + nosign; - } - - public string ParseRenamedAbsyLabel(string! label) - { - string! prefix = RenameAbsyLabel("+"); - if(!label.StartsWith(prefix)) - return null; - return label.Substring(prefix.Length); - } - - public void setCurrProc(string! name) - { - currProc = name; - assert implName2StratifiedInliningInfo.ContainsKey(name); - } - - public void setCurrProcAsMain() - { - currProc = ""; - } - - private Hashtable/**/! getLabel2absy() - { - assert currProc != null; - if(currProc == "") { - return mainLabel2absy; - } - return (!)implName2StratifiedInliningInfo[currProc].label2absy; - } - - // Finds labels and changes them: - // si_fcall_id: if "id" corresponds to a tracked procedure call, then - // si_fcall_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) - { - 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; - 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)); - Hashtable! label2absy = getLabel2absy(); - Absy cmd = label2absy[id] as Absy; - //label2absy.Remove(id); - - assert cmd != null; - AssumeCmd acmd = cmd as AssumeCmd; - assert acmd != null; - NAryExpr naryExpr = acmd.Expr as NAryExpr; - assert naryExpr != null; - - string! calleeName = naryExpr.Fun.FunctionName; - - VCExprNAry callExpr = retnary[0] as VCExprNAry; - assert callExpr != null; - - if(implName2StratifiedInliningInfo.ContainsKey(calleeName)) { - int candidateId = GetId(callExpr); - boogieExpr2Id[naryExpr] = candidateId; - string! label = GetLabel(candidateId); - return Gen.LabelPos(label, callExpr); - } else { - return callExpr; - } - } - - // Else, rename label - string! newLabel = RenameAbsyLabel(lop.label); - if(lop.pos) { - return Gen.LabelPos(newLabel, retnary[0]); - } else { - return Gen.LabelNeg(newLabel, retnary[0]); - } - - } - - } // end FCallHandler - - - public class FCallInliner : MutatingVCExprVisitor { - public Dictionary! subst; - - public FCallInliner(VCExpressionGenerator! gen) - : base(gen) - { - 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) - { - 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 ErrorReporter : ProverInterface.ErrorHandler { - Hashtable/*TransferCmd->ReturnCmd*/! gotoCmdOrigins; - Hashtable/**/! label2absy; - List! blocks; - protected Dictionary! incarnationOriginMap; - protected VerifierCallback! callback; - internal string? resourceExceededMessage; - static System.IO.TextWriter? modelWriter; - - public static TextWriter! ModelWriter { - get { - if (ErrorReporter.modelWriter == null) - ErrorReporter.modelWriter = CommandLineOptions.Clo.PrintErrorModelFile == null ? Console.Out : new StreamWriter(CommandLineOptions.Clo.PrintErrorModelFile, false); - return ErrorReporter.modelWriter; - } - } - - Dictionary! implName2LazyInliningInfo; - DeclFreeProverContext! context; - Program! program; - - public ErrorReporter(Hashtable/*TransferCmd->ReturnCmd*/! gotoCmdOrigins, - Hashtable/**/! label2absy, - List! blocks, - Dictionary! incarnationOriginMap, - VerifierCallback! callback, - Dictionary! implName2LazyInliningInfo, - DeclFreeProverContext! context, - Program! program) - { - this.gotoCmdOrigins = gotoCmdOrigins; - this.label2absy = label2absy; - this.blocks = blocks; - this.incarnationOriginMap = incarnationOriginMap; - this.callback = callback; - - this.implName2LazyInliningInfo = implName2LazyInliningInfo; - this.context = context; - this.program = program; - // base(); - } - - public override void OnModel(IList! labels, ErrorModel errModel) { - if (CommandLineOptions.Clo.PrintErrorModel >= 1 && errModel != null) { - errModel.Print(ErrorReporter.ModelWriter); - ErrorReporter.ModelWriter.Flush(); - } - Hashtable traceNodes = new Hashtable(); - foreach (string! s in labels) { - Absy! absy = Label2Absy(s); - if (traceNodes.ContainsKey(absy)) - System.Console.WriteLine("Warning: duplicate label: " + s + " read while tracing nodes"); - else - traceNodes.Add(absy, null); - } - - BlockSeq! trace = new BlockSeq(); - Block! entryBlock = (!) this.blocks[0]; - assert traceNodes.Contains(entryBlock); - trace.Add(entryBlock); - - Counterexample newCounterexample = TraceCounterexample(entryBlock, traceNodes, trace, errModel, incarnationOriginMap, implName2LazyInliningInfo, context, program, 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) { - assume b.TransferCmd != null; - ReturnCmd cmd = (ReturnCmd) gotoCmdOrigins[b.TransferCmd]; - if (cmd != null) - { - returnExample.FailingReturn = cmd; - break; - } - } - } - #endregion - callback.OnCounterexample(newCounterexample, null); - } - - public override Absy! Label2Absy(string! label) - { - int id = int.Parse(label); - return (Absy!) label2absy[id]; - } - - public override void OnResourceExceeded(string! msg) - { - resourceExceededMessage = msg; - } - - public override void OnProverWarning(string! msg) - { - callback.OnWarning(msg); - } - } - - public class ErrorReporterLocal : ErrorReporter { - public ErrorReporterLocal(Hashtable/*TransferCmd->ReturnCmd*/! gotoCmdOrigins, - Hashtable/**/! label2absy, - List! blocks, - Dictionary! incarnationOriginMap, - VerifierCallback! callback, - Dictionary! implName2LazyInliningInfo, - DeclFreeProverContext! context, - Program! program) - { - base(gotoCmdOrigins, label2absy, blocks, incarnationOriginMap, callback, implName2LazyInliningInfo, context, program); // here for aesthetic purposes - } - - public override void OnModel(IList! labels, ErrorModel errModel) { - // We ignore the error model here for enhanced error message purposes. - // It is only printed to the command line. - if (CommandLineOptions.Clo.PrintErrorModel >= 1 && errModel != null) { - if (CommandLineOptions.Clo.PrintErrorModelFile != null) { - errModel.Print(ErrorReporter.ModelWriter); - ErrorReporter.ModelWriter.Flush(); - } - } - List traceNodes = new List(); - List assertNodes = new List(); - foreach (string! s in labels) { - Absy node = Label2Absy(s); - if (node is Block) { - Block b = (Block)node; - traceNodes.Add(b); - } else { - AssertCmd a = (AssertCmd)node; - assertNodes.Add(a); - } - } - assert assertNodes.Count > 0; - 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.Has(a)) { - BlockSeq trace = new BlockSeq(); - trace.Add(b); - Counterexample newCounterexample = AssertCmdToCounterexample(a, (!)b.TransferCmd, trace, errModel, incarnationOriginMap); - callback.OnCounterexample(newCounterexample, null); - goto NEXT_ASSERT; - } - } - assert false; // there was no block that contains the assert - NEXT_ASSERT: {} - } - } - } - - public class StratifiedInliningErrorReporter : ProverInterface.ErrorHandler { - Dictionary! implName2StratifiedInliningInfo; - ProverInterface! theoremProver; - VerifierCallback! callback; - FCallHandler calls; - Program! program; - Implementation! mainImpl; - DeclFreeProverContext! context; - Hashtable/*TransferCmd->ReturnCmd*/ gotoCmdOrigins; - - public bool underapproximationMode; - public List! candidatesToExpand; - - public StratifiedInliningErrorReporter(Dictionary! implName2StratifiedInliningInfo, - ProverInterface! theoremProver, VerifierCallback! callback, DeclFreeProverContext! context, - Hashtable/*TransferCmd->ReturnCmd*/ gotoCmdOrigins, - Program! program, Implementation! mainImpl) { - this.implName2StratifiedInliningInfo = implName2StratifiedInliningInfo; - this.theoremProver = theoremProver; - this.callback = callback; - this.context = context; - this.program = program; - this.mainImpl = mainImpl; - this.underapproximationMode = false; - this.calls = null; - this.candidatesToExpand = new List(); - this.gotoCmdOrigins = gotoCmdOrigins; - } - - public void SetCandidateHandler(FCallHandler! calls) - { - this.calls = calls; - } - - public override void OnModel(IList! labels, ErrorModel errModel) { - if(underapproximationMode) { - if(errModel == null) return; - GenerateTraceMain(labels, errModel); - return; - } - - assert calls != null; - assert errModel != null; - - candidatesToExpand = new List(); - foreach(string! lab in labels) - { - int id = calls.GetId(lab); - if(id < 0) continue; - if(!calls.currCandidates.Contains(id)) continue; - candidatesToExpand.Add(id); - } - - } - - // Construct the interprocedural trace - private void GenerateTraceMain(IList! labels, ErrorModel! errModel) { - if (CommandLineOptions.Clo.PrintErrorModel >= 1 && errModel != null) { - errModel.Print(ErrorReporter.ModelWriter); - ErrorReporter.ModelWriter.Flush(); - } - - Counterexample newCounterexample = - GenerateTrace(labels, errModel, 0, mainImpl); - - if(newCounterexample == null) return; - - #region Map passive program errors back to original program errors - ReturnCounterexample returnExample = newCounterexample as ReturnCounterexample; - if (returnExample != null && gotoCmdOrigins != null) - { - foreach (Block! b in returnExample.Trace) { - assume b.TransferCmd != null; - ReturnCmd cmd = (ReturnCmd) gotoCmdOrigins[b.TransferCmd]; - if (cmd != null) - { - returnExample.FailingReturn = cmd; - break; - } - } - } - #endregion - - callback.OnCounterexample(newCounterexample, null); - } - - private Counterexample GenerateTrace(IList! labels, ErrorModel! errModel, - int candidateId, Implementation! procImpl) { - - Hashtable traceNodes = new Hashtable(); - string! procPrefix = "si_inline_" + candidateId.ToString() + "_"; - - foreach (string! s in labels) { - if(!s.StartsWith(procPrefix)) - continue; - - Absy! absy; - - if(candidateId == 0) { - absy = Label2Absy(s.Substring(procPrefix.Length)); - } else { - absy = Label2Absy(procImpl.Name, s.Substring(procPrefix.Length)); - } - - if (traceNodes.ContainsKey(absy)) - System.Console.WriteLine("Warning: duplicate label: " + s + " read while tracing nodes"); - else - traceNodes.Add(absy, null); - } - - BlockSeq! trace = new BlockSeq(); - Block! entryBlock = (!) procImpl.Blocks[0]; - assert traceNodes.Contains(entryBlock); - trace.Add(entryBlock); - - Dictionary! calleeCounterexamples = new Dictionary(); - Counterexample newCounterexample = GenerateTraceRec(labels, errModel, entryBlock, traceNodes, trace, calleeCounterexamples); - - return newCounterexample; - - } - - private Counterexample GenerateTraceRec( - IList! labels, ErrorModel! errModel, - Block! b, Hashtable! traceNodes, BlockSeq! trace, - Dictionary! calleeCounterexamples) - { - // After translation, all potential errors come from asserts. - CmdSeq! cmds = b.Cmds; - TransferCmd! transferCmd = (!)b.TransferCmd; - for (int i = 0; i < cmds.Length; i++) - { - Cmd! cmd = (!) 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, new Dictionary()); - 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; - if (!implName2StratifiedInliningInfo.ContainsKey(calleeName)) continue; - - assert calls != null; - int calleeId = calls.boogieExpr2Id[naryExpr]; - - calleeCounterexamples[assumeCmd] = - new CalleeCounterexampleInfo( - (!)GenerateTrace(labels, errModel, calleeId, implName2StratifiedInliningInfo[calleeName].impl), - new List()); - - } - - GotoCmd gotoCmd = transferCmd as GotoCmd; - if (gotoCmd != null) - { - foreach (Block! bb in (!)gotoCmd.labelTargets) - { - if (traceNodes.Contains(bb)){ - trace.Add(bb); - return GenerateTraceRec(labels, errModel, bb, traceNodes, trace, calleeCounterexamples); - } - } - } - - return null; - - } - - public override Absy! Label2Absy(string! label) - { - int id = int.Parse(label); - assert calls != null; - return (Absy!) calls.mainLabel2absy[id]; - } - - public Absy! Label2Absy(string! procName, string! label) - { - int id = int.Parse(label); - Hashtable! l2a = (!)implName2StratifiedInliningInfo[procName].label2absy; - return (Absy!) l2a[id]; - } - - public override void OnResourceExceeded(string! msg) - { - //resourceExceededMessage = msg; - } - - public override void OnProverWarning(string! msg) - { - callback.OnWarning(msg); - } - } - - protected void ConvertCFG2DAG(Implementation! impl, Program! program) - { - impl.PruneUnreachableBlocks(); // This is needed for VCVariety.BlockNested, and is otherwise just an optimization - - current_impl = impl; - variable2SequenceNumber = new Hashtable/*Variable -> int*/(); - 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 - - ComputePredecessors(impl.Blocks); - - #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 = 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 - - #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 (!) g.Headers) - { - IDictionary backEdgeNodes = new Dictionary(); - foreach (Block! b in (!) g.BackEdgeNodes(header)) { 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 - CmdSeq prefixOfPredicateCmdsInit = new CmdSeq(); - CmdSeq prefixOfPredicateCmdsMaintained = new CmdSeq(); - for (int i = 0, n = header.Cmds.Length; i < n; i++) - { - PredicateCmd a = header.Cmds[i] as PredicateCmd; - if (a != null) - { - if (a is AssertCmd) { - Bpl.AssertCmd c = (AssertCmd) a; - Bpl.AssertCmd b = new Bpl.LoopInitAssertCmd(c.tok, c.Expr); - b.ErrorData = c.ErrorData; - prefixOfPredicateCmdsInit.Add(b); - b = new Bpl.LoopInvMaintainedAssertCmd(c.tok, c.Expr); - b.ErrorData = c.ErrorData; - prefixOfPredicateCmdsMaintained.Add(b); - header.Cmds[i] = new AssumeCmd(c.tok,c.Expr); - } else { - 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.Length; predIndex < n; predIndex++ ) - { - Block! pred = (!)header.Predecessors[predIndex]; - - // Create a block between header and pred for the predicate commands if pred has more than one successor - GotoCmd gotocmd = (GotoCmd!)pred.TransferCmd; - assert gotocmd.labelNames != null; // if "pred" is really a predecessor, it may be a GotoCmd with at least one label - if (gotocmd.labelNames.Length > 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 (!)backEdgeNodes.Keys) - { - 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.Length > 1 ) - { - // then remove the backedge by removing the target block from the list of gotos - BlockSeq remainingTargets = new BlockSeq(); - StringSeq remainingLabels = new StringSeq(); - assume gtc.labelNames != null; - for (int i = 0, n = gtc.labelTargets.Length; i < n; i++) - { - if ( gtc.labelTargets[i] != header ) - { - remainingTargets.Add(gtc.labelTargets[i]); - remainingLabels.Add(gtc.labelNames[i]); - } - } - 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); - } - #region Remove the backedge node from the list of predecessor nodes in the header - BlockSeq newPreds = new BlockSeq(); - 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 - VariableSeq varsToHavoc = new VariableSeq(); - foreach (Block! backEdgeNode in (!) g.BackEdgeNodes(header)) - { - foreach ( Block! b in g.NaturalLoops(header,backEdgeNode) ) - { - foreach ( Cmd! c in b.Cmds ) - { - c.AddAssignedVariables(varsToHavoc); - } - } - } - IdentifierExprSeq havocExprs = new IdentifierExprSeq(); - foreach ( Variable! v in varsToHavoc ) - { - IdentifierExpr ie = new IdentifierExpr(Token.NoToken, v); - if(!havocExprs.Has(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); - CmdSeq newCmds = new CmdSeq(); - newCmds.Add(hc); - foreach ( Cmd c in header.Cmds ) - { - newCmds.Add(c); - } - header.Cmds = newCmds; - #endregion - } - #endregion - #endregion Convert program CFG into a DAG - - #region Debug Tracing - if (CommandLineOptions.Clo.TraceVerify) - { - Console.WriteLine("after conversion into a DAG"); - EmitImpl(impl, true); - } - #endregion - } - - protected Hashtable/*TransferCmd->ReturnCmd*/! PassifyImpl(Implementation! impl, Program! program) - { - Hashtable/*TransferCmd->ReturnCmd*/ gotoCmdOrigins = new Hashtable/*TransferCmd->ReturnCmd*/(); - 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 - { - CmdSeq cc = new CmdSeq(); - // where clauses of global variables - foreach (Declaration d in program.TopLevelDeclarations) { - GlobalVariable gvar = d as GlobalVariable; - 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) { - if (lvar.TypedIdent.WhereExpr != null) { - Cmd c = new AssumeCmd(lvar.tok, lvar.TypedIdent.WhereExpr); - cc.Add(c); - } - } - - // 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 - exitBlock = InjectPostConditions(impl, exitBlock, gotoCmdOrigins); - } - #endregion - - #region Support for lazy inlining - if (implName2LazyInliningInfo != null && implName2LazyInliningInfo.ContainsKey(impl.Name)) - { - Expr! assertExpr = implName2LazyInliningInfo[impl.Name].assertExpr; - exitBlock.Cmds.Add(new AssertCmd(Token.NoToken, assertExpr)); - } - #endregion - - #region Support for lazy inlining - if (implName2StratifiedInliningInfo != null && implName2StratifiedInliningInfo.ContainsKey(impl.Name)) - { - Expr! assertExpr = implName2StratifiedInliningInfo[impl.Name].assertExpr; - exitBlock.Cmds.Add(new AssertCmd(Token.NoToken, assertExpr)); - } - #endregion - - #region Debug Tracing - if (CommandLineOptions.Clo.TraceVerify) - { - Console.WriteLine("after inserting pre- and post-conditions"); - EmitImpl(impl, true); - } - #endregion - - AddBlocksBetween(impl); - - #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); - } - - Hashtable exitIncarnationMap = Convert2PassiveCmd(impl); - if (implName2LazyInliningInfo != null && implName2LazyInliningInfo.ContainsKey(impl.Name)) - { - LazyInliningInfo! info = implName2LazyInliningInfo[impl.Name]; - info.exitIncarnationMap = exitIncarnationMap; - info.incarnationOriginMap = this.incarnationOriginMap; - } - if (implName2StratifiedInliningInfo != null && implName2StratifiedInliningInfo.ContainsKey(impl.Name)) - { - StratifiedInliningInfo! info = implName2StratifiedInliningInfo[impl.Name]; - info.exitIncarnationMap = exitIncarnationMap; - info.incarnationOriginMap = this.incarnationOriginMap; - } - - if (CommandLineOptions.Clo.LiveVariableAnalysis == 1) { - Microsoft.Boogie.LiveVariableAnalysis.ClearLiveVariables(impl); - } - // TODO: fix - //else if (CommandLineOptions.Clo.LiveVariableAnalysis == 2) { - // Microsoft.Boogie.InterProcGenKill.ClearLiveVariables(impl, program); - //} - - #region Peep-hole optimizations - if (CommandLineOptions.Clo.RemoveEmptyBlocks){ - #region Get rid of empty blocks - { - Block! entryBlock = (!) impl.Blocks[0]; - RemoveEmptyBlocks(entryBlock); - 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 - - if (CommandLineOptions.Clo.ExpandLambdas) - { - List! axioms; - List! functions; - LambdaHelper.Desugar(impl, out axioms, out functions); - // TODO: do something with functions (Z3 currently doesn't need them) - - if (axioms.Count > 0) { - CmdSeq cmds = new CmdSeq(); - foreach (Expr! ax in axioms) { - cmds.Add(new AssumeCmd(ax.tok, ax)); - } - Block! entryBlock = (!) impl.Blocks[0]; - cmds.AddRange(entryBlock.Cmds); - entryBlock.Cmds = cmds; - } - } - - - -// #region Constant Folding -// #endregion -// #region Debug Tracing -// if (CommandLineOptions.Clo.TraceVerify) -// { -// Console.WriteLine("after constant folding"); -// EmitImpl(impl, true); -// } -// #endregion - - return gotoCmdOrigins; - } - - private static Counterexample! LazyCounterexample( - ErrorModel! errModel, - Dictionary! implName2LazyInliningInfo, - DeclFreeProverContext! context, - Program! program, - string! implName, List! values) - { - VCExprTranslator! vcExprTranslator = (!)context.exprTranslator; - Boogie2VCExprTranslator! boogieExprTranslator = context.BoogieExprTranslator; - LazyInliningInfo! info = implName2LazyInliningInfo[implName]; - BlockSeq! trace = new BlockSeq(); - Block! b = ((!) info.impl).Blocks[0]; - trace.Add(b); - VCExprVar! cfcVar = boogieExprTranslator.LookupVariable(info.controlFlowVariable); - string! cfcName = vcExprTranslator.Lookup(cfcVar); - int cfcPartition = errModel.LookupSkolemFunctionAt(cfcName + "!" + info.uniqueId, values); - int cfcValue = errModel.LookupPartitionValue(cfcPartition); - - Dictionary calleeCounterexamples = new Dictionary(); - while (true) { - CmdSeq! cmds = b.Cmds; - TransferCmd! transferCmd = (!)b.TransferCmd; - for (int i = 0; i < cmds.Length; i++) - { - Cmd! cmd = (!) cmds[i]; - AssertCmd assertCmd = cmd as AssertCmd; - if (assertCmd != null && errModel.LookupControlFlowFunctionAt(cfcValue, assertCmd.UniqueId) == 0) - { - Counterexample newCounterexample; - newCounterexample = AssertCmdToCounterexample(assertCmd, transferCmd, trace, errModel, (!)info.incarnationOriginMap); - newCounterexample.AddCalleeCounterexample(calleeCounterexamples); - return newCounterexample; - } - - AssumeCmd assumeCmd = cmd as AssumeCmd; - if (assumeCmd == null) continue; - NAryExpr naryExpr = assumeCmd.Expr as NAryExpr; - if (naryExpr == null) continue; - string! calleeName = naryExpr.Fun.FunctionName; - if (!implName2LazyInliningInfo.ContainsKey(calleeName)) continue; - - List! args = new List(); - foreach (Expr! expr in naryExpr.Args) - { - VCExprVar exprVar; - string name; - LiteralExpr litExpr = expr as LiteralExpr; - if (litExpr != null) - { - args.Add(errModel.valueToPartition[litExpr.Val]); - continue; - } - - IdentifierExpr idExpr = expr as IdentifierExpr; - assert idExpr != null; - Variable! var = (!)idExpr.Decl; - - if (var is Constant) - { - exprVar = boogieExprTranslator.LookupVariable(var); - name = vcExprTranslator.Lookup(exprVar); - args.Add(errModel.identifierToPartition[name]); - continue; - } - - int index = 0; - List globalVars = program.GlobalVariables(); - foreach (Variable! global in globalVars) - { - if (global == var) break; - index++; - } - if (index < globalVars.Count) - { - args.Add(values[index]); - continue; - } - - foreach (Variable! input in info.impl.InParams) - { - if (input == var) break; - index++; - } - if (index < globalVars.Count + info.impl.InParams.Length) - { - args.Add(values[index]); - continue; - } - - foreach (Variable! output in info.impl.OutParams) - { - if (output == var) break; - index++; - } - if (index < globalVars.Count + info.impl.InParams.Length + info.impl.OutParams.Length) - { - args.Add(values[index]); - continue; - } - - exprVar = boogieExprTranslator.LookupVariable(var); - name = vcExprTranslator.Lookup(exprVar); - args.Add(errModel.LookupSkolemFunctionAt(name + "!" + info.uniqueId, values)); - } - calleeCounterexamples[assumeCmd] = - new CalleeCounterexampleInfo( - LazyCounterexample(errModel, implName2LazyInliningInfo, context, program, calleeName, args), - errModel.PartitionsToValues(args)); - } - - GotoCmd gotoCmd = transferCmd as GotoCmd; - if (gotoCmd == null) break; - int nextBlockId = errModel.LookupControlFlowFunctionAt(cfcValue, b.UniqueId); - b = (Block!) ((!)info.label2absy)[nextBlockId]; - trace.Add(b); - } - assert false; - } - - static Counterexample TraceCounterexample(Block! b, BlockSeq! trace, ErrorModel errModel, Dictionary! incarnationOriginMap) - { - // After translation, all potential errors come from asserts. - - return null; - } - - static Counterexample TraceCounterexample( - Block! b, Hashtable! traceNodes, BlockSeq! trace, ErrorModel errModel, - Dictionary! incarnationOriginMap, - Dictionary! implName2LazyInliningInfo, - DeclFreeProverContext! context, Program! program, - Dictionary! calleeCounterexamples) - { - // After translation, all potential errors come from asserts. - CmdSeq! cmds = b.Cmds; - TransferCmd! transferCmd = (!)b.TransferCmd; - for (int i = 0; i < cmds.Length; i++) - { - Cmd! cmd = (!) 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, incarnationOriginMap); - newCounterexample.AddCalleeCounterexample(calleeCounterexamples); - return newCounterexample; - } - - #region Counterexample generation for lazily inlined procedures - if (errModel == null) continue; - AssumeCmd assumeCmd = cmd as AssumeCmd; - if (assumeCmd == null) continue; - NAryExpr naryExpr = assumeCmd.Expr as NAryExpr; - if (naryExpr == null) continue; - string! calleeName = naryExpr.Fun.FunctionName; - if (!implName2LazyInliningInfo.ContainsKey(calleeName)) continue; - VCExprTranslator! vcExprTranslator = (!)context.exprTranslator; - Boogie2VCExprTranslator! boogieExprTranslator = context.BoogieExprTranslator; - List! args = new List(); - foreach (Expr! expr in naryExpr.Args) - { - LiteralExpr litExpr = expr as LiteralExpr; - if (litExpr != null) - { - args.Add(errModel.valueToPartition[litExpr.Val]); - continue; - } - - IdentifierExpr idExpr = expr as IdentifierExpr; - assert idExpr != null; - assert idExpr.Decl != null; - VCExprVar! var = boogieExprTranslator.LookupVariable(idExpr.Decl); - string! name = vcExprTranslator.Lookup(var); - args.Add(errModel.identifierToPartition[name]); - } - calleeCounterexamples[assumeCmd] = - new CalleeCounterexampleInfo( - LazyCounterexample(errModel, implName2LazyInliningInfo, context, program, calleeName, args), - errModel.PartitionsToValues(args)); - #endregion - } - - GotoCmd gotoCmd = transferCmd as GotoCmd; - if (gotoCmd != null) - { - foreach (Block! bb in (!)gotoCmd.labelTargets) - { - if (traceNodes.Contains(bb)){ - trace.Add(bb); - return TraceCounterexample(bb, traceNodes, trace, errModel, incarnationOriginMap, implName2LazyInliningInfo, context, program, calleeCounterexamples); - } - } - } - - return null; - - // Debug.Fail("Could not find failing node."); - // throw new Microsoft.Contracts.AssertException(); - } - - - static void /*return printable error!*/ ApplyEnhancedErrorPrintingStrategy (Bpl.Expr! expr, Hashtable /*Variable -> Expr*/! incarnationMap, MiningStrategy errorDataEnhanced, ErrorModel! errModel, Dictionary! exprToPrintableValue, List! relatedInformation, bool printInternalStateDumpOnce, Dictionary! incarnationOriginMap) { - if (errorDataEnhanced is ListOfMiningStrategies) { - ListOfMiningStrategies loms = (ListOfMiningStrategies) errorDataEnhanced; - List! l = loms.msList; - for (int i = 0; i < l.Count; i++) { - MiningStrategy ms = l[i]; - if (ms != null) { - ApplyEnhancedErrorPrintingStrategy(expr, incarnationMap, l[i], errModel, exprToPrintableValue, relatedInformation, false, incarnationOriginMap); - } - } - } - else if (errorDataEnhanced is EEDTemplate /*EDEverySubExpr*/) { - EEDTemplate eedT = (EEDTemplate) errorDataEnhanced; - string reason = eedT.reason; - List listOfExprs = eedT.exprList; - if (listOfExprs != null) { - List holeFillers = new List(); - for (int i = 0; i < listOfExprs.Count; i++) { - bool alreadySet = false; - foreach (KeyValuePair kvp in exprToPrintableValue) { - Bpl.Expr! e = kvp.Key; - Bpl.Expr! f = listOfExprs[i]; - // the strings are compared instead of the actual expressions because - // the expressions might not be identical, but their print-out strings will be - if (e.ToString() == f.ToString()) { - object o = kvp.Value; - if (o != null) { - holeFillers.Add(o.ToString()); - alreadySet = true; - break; - } - } - } - if (!alreadySet) { - // no information about that Expression found, so put in - holeFillers.Add(""); - } - } - reason = FormatReasonString(reason, holeFillers); - } - if (reason != null) { - relatedInformation.Add("(related information): "+reason); - } - } else { - // define new templates here! - } - - if (printInternalStateDumpOnce) { - ComputeAndTreatHeapSuccessions(incarnationMap, errModel, incarnationOriginMap, relatedInformation); - - // default action: print all values! - foreach (KeyValuePair kvp in exprToPrintableValue) { - object o = kvp.Value; - if (o != null) { - // We do not want to print LiteralExprs because that gives things like 0 == 0. - // If both arguments to the string.Format are the same it is also useless, - // as that would print e.g. $a == $a. - if (!(kvp.Key is LiteralExpr)&& kvp.Key.ToString() != o.ToString()) { - string boogieExpr; - // check whether we are handling BPL or SSC input - if (CommandLineOptions.Clo.RunningBoogieOnSsc) { - boogieExpr = Helpers.PrettyPrintBplExpr(kvp.Key); - } else { - boogieExpr = kvp.Key.ToString(); - } - relatedInformation.Add("(internal state dump): "+string.Format("{0} == {1}", boogieExpr, o)); - } - } - } - } - } - - static void ComputeAndTreatHeapSuccessions(System.Collections.Hashtable! incarnationMap, ErrorModel! errModel, Dictionary! incarnationOriginMap, List! relatedInformation) { - List heapSuccList = ComputeHeapSuccessions(incarnationMap, errModel); - TreatHeapSuccessions(heapSuccList, incarnationMap, errModel, incarnationOriginMap, relatedInformation); - } - - static List ComputeHeapSuccessions(System.Collections.Hashtable! incarnationMap, ErrorModel! errModel) { - // find the heap variable - Variable heap = null; - ICollection ic = incarnationMap.Keys; - foreach (object o in ic) { - if (o is GlobalVariable) { - GlobalVariable gv = (GlobalVariable) o; - if (gv.Name == "$Heap") { - heap = gv; - } - } - } - List heapIdSuccession = new List(); - if (heap == null) { - // without knowing the name of the current heap we cannot create a heap succession! - } else { - object oHeap = incarnationMap[heap]; - if (oHeap != null) { - string currentHeap = oHeap.ToString(); - int currentHeapId; - if (errModel.identifierToPartition.TryGetValue(currentHeap, out currentHeapId)) { - while (currentHeapId != -1) { - if (!heapIdSuccession.Contains(currentHeapId)) { - heapIdSuccession.Add(currentHeapId); - currentHeapId = ComputePredecessorHeapId(currentHeapId, errModel); - } else { - // looping behavior, just stop here and do not add this value (again!) - break; - } - } - } - } - } - if (heapIdSuccession.Count > 0) { - int heapId = heapIdSuccession[heapIdSuccession.Count-1]; - List strl = errModel.partitionToIdentifiers[heapId]; - if (strl != null && strl.Contains("$Heap")) { - // we have a proper succession of heaps that starts with $Heap - return heapIdSuccession; - } else { - // no proper heap succession, not starting with $Heap! - return null; - } - } else { - // no heap succession found because either the $Heap does not have a current incarnation - // or because (unlikely!) the model is somehow messed up - return null; - } - } - - static int ComputePredecessorHeapId(int id, ErrorModel! errModel) { - //check "$HeapSucc" and "store2" functions: - List heapSuccPredIdList = new List(); - List> heapSuccFunc; - errModel.definedFunctions.TryGetValue("$HeapSucc", out heapSuccFunc); - if (heapSuccFunc != null) { - foreach (List heapSuccFuncDef in heapSuccFunc) { - // do not look at the else case of the function def, so check .Count - if (heapSuccFuncDef != null && heapSuccFuncDef.Count == 3 && heapSuccFuncDef[1] == id) { - // make sure each predecessor is put into the list at most once - if (!heapSuccPredIdList.Contains(heapSuccFuncDef[0])) { - heapSuccPredIdList.Add(heapSuccFuncDef[0]); - } - } - } - } - List store2PredIdList = new List();; - List> store2Func; - errModel.definedFunctions.TryGetValue("store2", out store2Func); - if (store2Func != null) { - foreach (List store2FuncDef in store2Func) { - // do not look at the else case of the function def, so check .Count - if (store2FuncDef != null && store2FuncDef.Count == 5 && store2FuncDef[4] == id) { - // make sure each predecessor is put into the list at most once - if (!store2PredIdList.Contains(store2FuncDef[0])) { - store2PredIdList.Add(store2FuncDef[0]); - } - } - } - } - if (heapSuccPredIdList.Count + store2PredIdList.Count > 0) { - if (store2PredIdList.Count == 1) { - return store2PredIdList[0]; - } else if (store2PredIdList.Count == 0) { - if (heapSuccPredIdList.Count == 1) { - return heapSuccPredIdList[0]; - } else { //(heapSuccPredIdList.Count > 1) - if (heapSuccPredIdList.Count == 2) { - // if one of the 2 is a self-loop, take the other! - if (heapSuccPredIdList[0] == id) { - return heapSuccPredIdList[1]; - } else if (heapSuccPredIdList[1] == id) { - return heapSuccPredIdList[0]; - } else { - //no self-loop, two different predecessors, cannot choose - return -1; - } - } else { - // at least 3 different predecessors available, one of them could be a self-loop, but - // we cannot choose between the other 2 (or more) candidates - return -1; - } - } - } else { - // more than one result in the succession coming from store2, no way - // to decide which is right, end here - return -1; - } - } else { - // no predecessor found - return -1; - } - } - - static void TreatHeapSuccessions (List heapSuccessionList, System.Collections.Hashtable! incarnationMap, ErrorModel! errModel, Dictionary! incarnationOriginMap, List! relatedInformation) { - if (heapSuccessionList == null) { - // empty list of heap successions, nothing we can do! - return; - } - // primarily look for $o and $f (with skolem-id stuff) and then look where they were last changed! - // find the o and f variables - Variable dollarO = null; - Variable dollarF = null; - ICollection ic = incarnationMap.Keys; - foreach (object o in ic) { - if (o is BoundVariable) { - BoundVariable bv = (BoundVariable) o; - if (bv.Name == "$o") { - dollarO = bv; - } - else if (bv.Name == "$f") { - dollarF = bv; - } - } - } - if (dollarO == null || dollarF == null) { - // without knowing the name of the current incarnation of $Heap, $o and $f we don't do anything here - } else { - object objO = incarnationMap[dollarO]; - object objF = incarnationMap[dollarF]; - if (objO != null && objF != null) { - int zidO = errModel.identifierToPartition[objO.ToString()]; - int zidF = errModel.identifierToPartition[objF.ToString()]; - - List> select2Func = null; - if (errModel.definedFunctions.TryGetValue("select2", out select2Func) && select2Func != null) { - // check for all changes to $o.$f! - List heapsChangedOFZid = new List(); - int oldValueZid = -1; - int newValueZid = -1; - - for (int i = 0; i < heapSuccessionList.Count; i++) { - bool foundValue = false; - foreach (List f in select2Func) { - if (f != null && f.Count == 4 && f[0] == heapSuccessionList[i] && f[1] == zidO && f[2] == zidF) { - newValueZid = f[3]; - foundValue = true; - break; - } - } - if (!foundValue) { - // get default of the function once Leo&Nikolaj have changed it so the default is type correct - // for now treat it as a -1 ! - // the last element of select2Func is the else case, it has only 1 element, so grab that: - // newValueZid = (select2Func[select2Func.Count-1])[0]; - newValueZid = -1; - } - - if (oldValueZid != newValueZid) { - // there was a change here, record that in the list: - if (oldValueZid != -1) { - // don't record a change at the "initial" location, which refers to the $Heap in - // its current incarnation, and is marked by the oldValueZid being uninitialized - heapsChangedOFZid.Add(heapSuccessionList[i-1]); - } - oldValueZid = newValueZid; - } - } - - foreach (int id in heapsChangedOFZid) { - //get the heap name out of the errModel for this zid: - List l = errModel.partitionToIdentifiers[id]; - List heaps = new List(); - if (l!=null) { - foreach (string s in l) { - if (s.StartsWith("$Heap")) { - heaps.Add(s); - } - } - } - // easy case first: - if (heaps.Count == 1) { - string heapName = heaps[0]; - // we have a string with the name of the heap, but we need to get the - // source location out of a map that uses Incarnations! - - ICollection incOrgMKeys = incarnationOriginMap.Keys; - foreach (Incarnation inc in incOrgMKeys) { - if (inc!= null) { - if (inc.Name == heapName) { - Absy source = null; - incarnationOriginMap.TryGetValue(inc, out source); - if (source != null) { - if (source is Block) { - Block b = (Block) source; - string fileName = b.tok.filename; - if (fileName != null) { - fileName = fileName.Substring(fileName.LastIndexOf('\\') + 1); - } - relatedInformation.Add("(related information): Changed $o.$f here: " + fileName + "(" + b.tok.line + "," + b.tok.col + ")"); - } else if (source is Cmd) { - Cmd c = (Cmd) source; - string fileName = c.tok.filename; - if (fileName != null) { - fileName = fileName.Substring(fileName.LastIndexOf('\\') + 1); - } - relatedInformation.Add("(related information) Changed $o.$f here: " + fileName + "(" + c.tok.line + "," + c.tok.col + ")"); - } else { - assert false; - } - } - } - } - } - } else { - // more involved! best use some of the above code and try to synchronize joint parts - // here there is more than one "$Heap@i" in the partition, check if they all agree on one - // source location or maybe if some of them are joins (i.e. blocks) that should be ignored - } - - } - } - } - } - } - - static string FormatReasonString (string reason, List holeFillers) { - if (holeFillers != null) { - // in case all elements of holeFillers are "" we can not say anything useful - // so just say nothing and return null - bool allUnknown = true; - foreach (string s in holeFillers) { - if (s != "") { - allUnknown = false; - break; - } - } - if (allUnknown) { - return null; - } - string[] a = holeFillers.ToArray(); - reason = string.Format(reason, a); - } - return reason; - } - - static object ValueFromZID (ErrorModel! errModel, int id) { - return ValueFromZID(errModel, id, null); - } - - static object ValueFromZID (ErrorModel! errModel, int id, string searchForAlternate) { - object o = errModel.partitionToValue[id]; - if (o != null) { - return o; - } else { - // more elaborate scheme to find something better, as in: look at the identifiers that - // this partition maps to, or similar things! - - //treatment for 'null': - int idForNull = -1; - if (errModel.valueToPartition.TryGetValue("nullObject", out idForNull) && idForNull == id) { - return "nullObject"; - } - - string returnStr = null; - - // "good identifiers" if there is no value found are 'unique consts' or - // '$in' parameters; '$in' parameters are treated, unclear how to get 'unique const' info - List identifiers = errModel.partitionToIdentifiers[id]; - if (identifiers != null) { - foreach (string s in identifiers) { - //$in parameters are (more) interesting than other identifiers, return the - // first one found - if (s.EndsWith("$in")) { - returnStr = s; - break; - } - } - } - - // try to get mappings from one identifier to another if there are exactly - // two identifiers in the partition, where one of them is the identifier for which - // we search for alternate encodings (third parameter of the method) [or an incarnation - // of such an identifier] - if (returnStr == null && searchForAlternate != null && identifiers != null && identifiers.Count == 2) { - if (identifiers[0] == searchForAlternate || identifiers[0].StartsWith(searchForAlternate + ".sk.")) { - returnStr = identifiers[1]; - } else if (identifiers[1] == searchForAlternate || identifiers[1].StartsWith(searchForAlternate + ".sk.")) { - returnStr = identifiers[0]; - } - } - - if (returnStr != null) { - return Helpers.BeautifyBplString(returnStr); - } - - return null; - } - } - - static int TreatInterpretedFunction(string! functionName, List! zargs, ErrorModel! errModel) { - if (zargs.Count != 2) { - //all interpreted functions are binary, so there have to be exactly two arguments - return -1; - } - else { - object arg0 = ValueFromZID(errModel, zargs[0]); - object arg1 = ValueFromZID(errModel, zargs[1]); - if (arg0 is BigNum && arg1 is BigNum) { - BigNum arg0i = (BigNum)arg0; - BigNum arg1i = (BigNum)arg1; - BigNum result; - if (functionName == "+") { - result = arg0i + arg1i; - } else if (functionName == "-") { - result = arg0i - arg1i; - } else /*if (functionName == "*")*/ { - result = arg0i * arg1i; - } - int returnId = -1; - if (errModel.valueToPartition.TryGetValue(result, out returnId)) { - return returnId; - } else { - return -1; - } - } - else { - //both arguments need to be integers for this to work! - return -1; - } - } - } - - static int TreatFunction (string! functionName, List! zargs, bool undefined, ErrorModel! errModel) { - List> functionDef; - if ((!errModel.definedFunctions.TryGetValue(functionName, out functionDef) && functionName != "+" && functionName != "-" && functionName != "*") || undefined) { - // no fitting function found or one of the arguments is undefined - return -1; - } else { - if (functionName == "+" || functionName == "-" || functionName == "*") { - return TreatInterpretedFunction(functionName, zargs, errModel); - } - assert functionDef != null; - foreach (List pWiseF in functionDef) { - assert pWiseF != null; - // else case in the function definition: - if (pWiseF.Count == 1) { - return pWiseF[0]; - } - // number of arguments is exactly the right number - assert zargs.Count == pWiseF.Count - 1; - if (forall{int i in (0: zargs.Count); zargs[i] == pWiseF[i]}) { - return pWiseF[pWiseF.Count - 1]; - } - } - // all functions should have an 'else ->' case defined, therefore this should be - // unreachable code! - assert false; - } - } - - //returned int is zID - static int GetValuesFromModel (Bpl.Expr! expr, Hashtable /*Variable -> Expr*/! incarnationMap, ErrorModel! errModel, Dictionary! exprToPrintableValue) - modifies exprToPrintableValue.*; - { - // call GetValuesFromModel on all proper subexpressions, returning their value, - // so they only have to be computed once! - if (expr is LiteralExpr) { - // nothing needs to be added to the exprToPrintableValue map, because e.g. 0 -> 0 is not interesting - object o = ((LiteralExpr) expr).Val; - if (o == null) { - o = "nullObject"; - } - int idForExprVal; - if (errModel.valueToPartition.TryGetValue(o, out idForExprVal)) { - return idForExprVal; - } else { - return -1; - } - } - else if (expr is IdentifierExpr) { - // if the expression expr is in the incarnation map, then use its incarnation, - // otherwise just use the actual expression - string s = ((IdentifierExpr) expr).Name; - object o = null; - Variable v = ((IdentifierExpr) expr).Decl; - if (v != null && incarnationMap.ContainsKey(v)) { - if (incarnationMap[v] is IdentifierExpr!) { - s = ((IdentifierExpr!) incarnationMap[v]).Name; - } else if (incarnationMap[v] is LiteralExpr!) { - o = ((LiteralExpr!) incarnationMap[v]).Val; - } - } - // if o is not null, then we got a LiteralExpression, that needs separate treatment - if (o == null) { - // if the expression (respectively its incarnation) is mapped to some partition - // then return that id, else return the error code -1 - if (errModel.identifierToPartition.ContainsKey(s)) { - int i = errModel.identifierToPartition[s]; - // if the key is already in the map we can assume that it is the same map we would - // get now and thus just ignore it - if (!exprToPrintableValue.ContainsKey(expr)) { - exprToPrintableValue.Add(expr, ValueFromZID(errModel, i, ((IdentifierExpr) expr).Name)); - } - return i; - } else { - return -1; - } - } else if (errModel.valueToPartition.ContainsKey(o)) { - int i = errModel.valueToPartition[o]; - if (!exprToPrintableValue.ContainsKey(expr)) - exprToPrintableValue.Add(expr, ValueFromZID(errModel, i)); - return i; - } else { - return -1; - } - } - else if (expr is Bpl.NAryExpr) { - Bpl.NAryExpr e = (Bpl.NAryExpr)expr; - List zargs = new List(); - bool undefined = false; - // do the recursion first - foreach (Expr argument in ((NAryExpr) expr).Args) { - int zid = -1; - if (argument != null) { - zid = GetValuesFromModel(argument, incarnationMap, errModel, exprToPrintableValue); - } - // if one of the arguments is 'undefined' then return -1 ('noZid') for this - // but make sure the recursion is complete first1 - if (zid == -1) { - undefined = true; - } - zargs.Add(zid); - } - IAppliable! fun = e.Fun; - string functionName = fun.FunctionName; // PR: convert to select1, select2, etc in case of a map? - // same as IndexedExpr: - int id = TreatFunction(functionName, zargs, undefined, errModel); - if (id != -1 && !exprToPrintableValue.ContainsKey(expr)) { - exprToPrintableValue.Add(expr, ValueFromZID(errModel, id)); - } - return id; - } - else if (expr is Bpl.OldExpr) { - //Bpl.OldExpr e = (Bpl.OldExpr)expr; - // We do not know which heap is the old heap and what the latest state change was, - // therefore we cannot return anything useful here! - return -1; - } - else if (expr is Bpl.QuantifierExpr) { - Bpl.QuantifierExpr q = (Bpl.QuantifierExpr)expr; - for (int i = 0; i < q.Dummies.Length; i++) { - Bpl.Variable v = q.Dummies[i]; - if (v != null) { - // add to the incarnation map a connection between the bound variable v - // of the quantifier and its skolemized incarnation, if available, - // i.e., search through the list of identifiers in the model and look for - // v.sk.(q.SkolemId), only pick those that are directly associated to a value - // DISCLAIMER: of course it is very well possible that one of these incarnations - // could be used without actually having a value, but it seems better to pick those - // with a value, that is they are more likely to contribute useful information to - // the output - List quantVarIncarnationList = new List(); - List incarnationZidList = new List(); - int numberOfNonNullValueIncarnations = 0; - for (int j = 0; j < errModel.partitionToIdentifiers.Count; j++){ - List pti = errModel.partitionToIdentifiers[j]; - if (pti != null) { - for (int k = 0; k < pti.Count; k++) { - // look for v.sk.(q.SkolemId) - // if there is more than one look at if there is exactly one with a non-null value - // associated, see above explanation - if (pti[k].StartsWith(v + ".sk." + q.SkolemId) && - errModel.partitionToValue[errModel.identifierToPartition[pti[k]]] != null) { - quantVarIncarnationList.Add(new Bpl.IdentifierExpr(Bpl.Token.NoToken, pti[k], new Bpl.UnresolvedTypeIdentifier(Token.NoToken, "TName"))); - incarnationZidList.Add(j); - if (errModel.partitionToValue[errModel.identifierToPartition[pti[k]]] != null) { - numberOfNonNullValueIncarnations++; - } - } - } - } - } - // only one such variable found, associate it with v - if (quantVarIncarnationList.Count == 1) { - incarnationMap[v] = quantVarIncarnationList[0]; - } else if (quantVarIncarnationList.Count > 1 && numberOfNonNullValueIncarnations == 1) { - // if there are multiple candidate incarnations and exactly one of them has a value - // we can pick that one; otherwise it is not clear how to pick one out of multiple - // incarnations without a value or out of multiple incarnations with a value associated - for (int n = 0; n < incarnationZidList.Count; n++) { - if (errModel.partitionToValue[incarnationZidList[n]] != null) { - // quantVarIncarnationList and incarnationZidList are indexed in lockstep, so if - // for the associated zid the partitionToValue map is non-null then that is the one - // thus distinguished incarnation we want to put into the incarnationMap - incarnationMap[v] = quantVarIncarnationList[n]; - break; - } - } - } - } - } - // generate the value of the body but do not return that outside - GetValuesFromModel(q.Body, incarnationMap, errModel, exprToPrintableValue); - // the quantifier cannot contribute any one value to the rest of the - // expression, thus just return -1 - return -1; - } - else if (expr is Bpl.BvExtractExpr) { - Bpl.BvExtractExpr ex = (Bpl.BvExtractExpr) expr; - Bpl.Expr e0 = ex.Bitvector; - Bpl.Expr e1 = new LiteralExpr(Token.NoToken, BigNum.FromInt(ex.Start)); - Bpl.Expr e2 = new LiteralExpr(Token.NoToken, BigNum.FromInt(ex.End)); - string functionName = "$bv_extract"; - List zargs = new List(); - bool undefined = false; - - int zid = -1; - zid = GetValuesFromModel(e0, incarnationMap, errModel, exprToPrintableValue); - if (zid == -1) { - undefined = true; - } - zargs.Add(zid); - - zid = -1; - zid = GetValuesFromModel(e1, incarnationMap, errModel, exprToPrintableValue); - if (zid == -1) { - undefined = true; - } - zargs.Add(zid); - - zid = -1; - zid = GetValuesFromModel(e2, incarnationMap, errModel, exprToPrintableValue); - if (zid == -1) { - undefined = true; - } - zargs.Add(zid); - - //same as NAryExpr: - int id = TreatFunction(functionName, zargs, undefined, errModel); - if (id != -1 && !exprToPrintableValue.ContainsKey(expr)) { - exprToPrintableValue.Add(expr, ValueFromZID(errModel, id)); - } - return id; - } - else if (expr is Bpl.BvConcatExpr) { - // see comment above - Bpl.BvConcatExpr bvc = (Bpl.BvConcatExpr) expr; - string functionName = "$bv_concat"; - List zargs = new List(); - bool undefined = false; - - int zid = -1; - zid = GetValuesFromModel(bvc.E0, incarnationMap, errModel, exprToPrintableValue); - if (zid == -1) { - undefined = true; - } - zargs.Add(zid); - - zid = -1; - zid = GetValuesFromModel(bvc.E0, incarnationMap, errModel, exprToPrintableValue); - if (zid == -1) { - undefined = true; - } - zargs.Add(zid); - - //same as NAryExpr: - int id = TreatFunction(functionName, zargs, undefined, errModel); - if (id != -1 && !exprToPrintableValue.ContainsKey(expr)) { - exprToPrintableValue.Add(expr, ValueFromZID(errModel, id)); - } - return id; - } - else { - assert false; // unexpected Bpl.Expr - } - return -1; - } - - static Counterexample! AssertCmdToCounterexample (AssertCmd! cmd, TransferCmd! transferCmd, BlockSeq! trace, ErrorModel errModel, Dictionary! incarnationOriginMap) { - List! relatedInformation = new List(); - if (CommandLineOptions.Clo.EnhancedErrorMessages == 1) { - if (cmd.OrigExpr != null && cmd.IncarnationMap != null && errModel != null) { - - // get all possible information first - Dictionary exprToPrintableValue = new Dictionary(); - GetValuesFromModel(cmd.OrigExpr, cmd.IncarnationMap, errModel, exprToPrintableValue); - // then apply the strategies - ApplyEnhancedErrorPrintingStrategy(cmd.OrigExpr, cmd.IncarnationMap, cmd.ErrorDataEnhanced, errModel, exprToPrintableValue, relatedInformation, true, incarnationOriginMap); - } - } - - // See if it is a special assert inserted in translation - if (cmd is AssertRequiresCmd) - { - AssertRequiresCmd! assertCmd = (AssertRequiresCmd)cmd; - CallCounterexample cc = new CallCounterexample(trace, assertCmd.Call, assertCmd.Requires); - cc.relatedInformation = relatedInformation; - return cc; - } - else if (cmd is AssertEnsuresCmd) - { - AssertEnsuresCmd! assertCmd = (AssertEnsuresCmd)cmd; - ReturnCounterexample rc = new ReturnCounterexample(trace, transferCmd, assertCmd.Ensures); - rc.relatedInformation = relatedInformation; - return rc; - } - else - { - AssertCounterexample ac = new AssertCounterexample(trace, (AssertCmd)cmd); - ac.relatedInformation = relatedInformation; - return ac; - } - } - -// static void EmitImpl(Implementation! impl, bool printDesugarings) { -// 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, false), 0); -// CommandLineOptions.Clo.PrintDesugarings = oldPrintDesugaringSetting; -// CommandLineOptions.Clo.PrintUnstructured = oldPrintUnstructured; -// } - - static VCExpr! LetVC(Block! startBlock, - Variable controlFlowVariable, - Hashtable/**/! label2absy, - ProverContext! proverCtxt) - { - Hashtable/**/! blockVariables = new Hashtable/**/(); - List! bindings = new List(); - VCExpr startCorrect = LetVC(startBlock, controlFlowVariable, label2absy, blockVariables, bindings, proverCtxt); - return proverCtxt.ExprGen.Let(bindings, startCorrect); - } - - static VCExpr! LetVC(Block! block, - Variable controlFlowVariable, - Hashtable/**/! label2absy, - Hashtable/**/! blockVariables, - List! bindings, - ProverContext! proverCtxt) - { - VCExpressionGenerator! gen = proverCtxt.ExprGen; - 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) { - SuccCorrect = VCExpressionGenerator.True; - } else { - assert gotocmd.labelTargets != null; - List SuccCorrectVars = new List(gotocmd.labelTargets.Length); - foreach (Block! successor in gotocmd.labelTargets) { - VCExpr s = LetVC(successor, controlFlowVariable, label2absy, blockVariables, bindings, proverCtxt); - if (controlFlowVariable != null) - { - VCExprVar controlFlowVariableExpr = proverCtxt.BoogieExprTranslator.LookupVariable(controlFlowVariable); - 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, controlFlowVariable); - VCExpr vc = Wlp.Block(block, SuccCorrect, context); - - 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, - Hashtable/**/! label2absy, - Hashtable/**/! blockEquations, - ProverContext! proverCtxt) - { - VCExpressionGenerator! gen = proverCtxt.ExprGen; - 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 (!)gotocmd.labelTargets) { - VCExpr c = DagVC(successor, label2absy, blockEquations, proverCtxt); - SuccCorrect = SuccCorrect == null ? c : gen.And(SuccCorrect, c); - } - } - if (SuccCorrect == null) { - SuccCorrect = VCExpressionGenerator.True; - } - - VCContext context = new VCContext(label2absy, proverCtxt); - vc = Wlp.Block(block, SuccCorrect, context); - - // 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, - Hashtable/**/! label2absy, - bool local, bool reach, bool doomed, - ProverContext! proverCtxt) - requires local ==> !reach; // "reach" must be false for local - { - VCExpressionGenerator! gen = proverCtxt.ExprGen; - Hashtable/* Block --> VCExprVar */ BlkCorrect = BlockVariableMap(impl.Blocks, "_correct", gen); - Hashtable/* Block --> VCExprVar */ BlkReached = reach ? BlockVariableMap(impl.Blocks, "_reached", gen) : null; - - List blocks = impl.Blocks; - // block sorting is now done on the VCExpr - // if (!local && ((!)CommandLineOptions.Clo.TheProverFactory).NeedsBlockSorting) { - // blocks = SortBlocks(blocks); - // } - - VCExpr proofObligation; - if (!local) { - proofObligation = (VCExprVar!)BlkCorrect[impl.Blocks[0]]; - } else { - List conjuncts = new List(blocks.Count); - foreach (Block! b in blocks) { - VCExpr v = (VCExprVar!)BlkCorrect[b]; - conjuncts.Add(v); - } - proofObligation = gen.NAry(VCExpressionGenerator.AndOp, conjuncts); - } - - VCContext! context = new VCContext(label2absy, proverCtxt); - - List programSemantics = new List(blocks.Count); - foreach (Block! b in blocks) { - /* - * 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((VCExprVar!)BlkReached[b], wlp); - } - - VCExprVar okVar = (VCExprVar!)BlkCorrect[b]; - VCExprLetBinding binding = gen.LetBinding(okVar, wlp); - programSemantics.Add(binding); - } - - return gen.Let(programSemantics, proofObligation); - } - - private static Hashtable/* Block --> VCExprVar */! BlockVariableMap(List! blocks, string! suffix, - Microsoft.Boogie.VCExpressionGenerator! gen) - { - Hashtable/* Block --> VCExprVar */ map = new Hashtable/* Block --> (Let)Variable */(); - foreach (Block! b in blocks) - { - VCExprVar! v = gen.Variable(b.Label+suffix, Bpl.Type.Bool); - 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) - { - VCExpr SuccCorrect = null; - GotoCmd gotocmd = b.TransferCmd as GotoCmd; - if (gotocmd != null) - { - foreach (Block! successor in (!)gotocmd.labelTargets) - { - // c := S_correct - VCExpr c = (VCExprVar!)BlkCorrect[successor]; - if (BlkReached != null) - { - // c := S_correct \/ Sibling0_reached \/ Sibling1_reached \/ ...; - foreach (Block! successorSibling in gotocmd.labelTargets) - { - if (successorSibling != successor) - { - c = gen.Or(c, (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, - Hashtable/**/! label2absy, - bool reach, - ProverContext! proverCtxt) - requires impl.Blocks.Count != 0; - { - VCExpressionGenerator! gen = proverCtxt.ExprGen; - Graph! g = GraphFromImpl(impl); - - Hashtable/* Block --> VCExprVar */ BlkCorrect = BlockVariableMap(impl.Blocks, "_correct", gen); - Hashtable/* Block --> VCExprVar */ BlkReached = reach ? BlockVariableMap(impl.Blocks, "_reached", gen) : null; - - Block! startBlock = (!) impl.Blocks[0]; - VCExpr proofObligation = (VCExprVar!)BlkCorrect[startBlock]; - - 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) { - totalOrder[b] = i; - i++; - } - } - - VCExprLetBinding programSemantics = NestedBlockEquation((!)impl.Blocks[0], BlkCorrect, BlkReached, totalOrder, context, g, gen); - List ps = new List(1); - ps.Add(programSemantics); - - 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) - { - /* - * 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); - - List bindings = new List(); - foreach (Block! dominee in GetSortedBlocksImmediatelyDominatedBy(g, b, totalOrder)) - { - 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); - } - VCExprVar okVar = (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) { - List list = new List(); - foreach (Block! dominee in g.ImmediatelyDominatedBy(b)) { - list.Add(dominee); - } - list.Sort(new Comparison(delegate (Block! x, Block! y) {return (int)(!)totalOrder[x] - (int)(!)totalOrder[y];} )); - return list; - } - - static VCExpr! VCViaStructuredProgram - (Implementation! impl, Hashtable/**/! label2absy, - ProverContext! proverCtxt) - { - #region Convert block structure back to a "regular expression" - RE! r = DAG2RE.Transform((!)impl.Blocks[0]); - #endregion - - VCContext! ctxt = new VCContext(label2absy, proverCtxt); - #region Send wlp(program,true) to Simplify - return Wlp.RegExpr(r, VCExpressionGenerator.True, ctxt); - #endregion - } - - /// - /// 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 BlockSeq! RemoveEmptyBlocks(Block! b) - { - assert b.TraversingStatus == Block.VisitState.ToVisit; - Block renameInfo; - BlockSeq 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 BlockSeq! removeEmptyBlocksWorker(Block! b, bool startNode, out Block renameInfoForStartBlock) - ensures renameInfoForStartBlock != null ==> renameInfoForStartBlock.tok.IsValid; - // ensures: b in result ==> renameInfoForStartBlock == null; - { - renameInfoForStartBlock = null; - BlockSeq bs = new BlockSeq(); - GotoCmd gtc = b.TransferCmd as GotoCmd; - - // b has no successors - if (gtc == null || gtc.labelTargets == null || gtc.labelTargets.Length == 0) - { - if (b.Cmds.Length != 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.Length == 0 && !startNode) { - // b is about to become extinct; try to save its name and location, if possible - if (b.tok.IsValid && gtc.labelTargets.Length == 1) { - Block succ = (!)gtc.labelTargets[0]; - if (!succ.tok.IsValid && succ.Predecessors.Length == 1) { - succ.tok = b.tok; - succ.Label = b.Label; - } - } - } - - // recursively call this method on each successor - // merge result into a *set* of blocks - Dictionary mergedSuccessors = new Dictionary(); - 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){ - Block renameInfo; - BlockSeq! ys = removeEmptyBlocksWorker(dest, false, out renameInfo); - if (m == 0) { - renameInfoForStartBlock = renameInfo; - } else if (renameInfoForStartBlock != renameInfo) { - renameInfoForStartBlock = null; - } - foreach (Block successor in ys){ - if (!mergedSuccessors.ContainsKey(successor)) - mergedSuccessors.Add(successor,true); - } - m++; - } - b.TraversingStatus = Block.VisitState.AlreadyVisited; - - BlockSeq setOfSuccessors = new BlockSeq(); - foreach (Block d in mergedSuccessors.Keys) - setOfSuccessors.Add(d); - if (b.Cmds.Length == 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 StringSeq(); - foreach (Block! d in setOfSuccessors) - gtc.labelNames.Add(d.Label); - if (!startNode) { - renameInfoForStartBlock = null; - } - return new BlockSeq(b); - } - else // b has some successors, but we are already visiting it, or we have already visited it... - { - return new BlockSeq(b); - } - } - - static Graph! GraphFromImpl(Implementation! impl) { - Graph! g = new Graph(); - g.AddSource((!)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) - { - foreach (Block! dest in (!)gtc.labelTargets) - { - g.AddEdge(b,dest); - } - } - } - return g; - } - - - static void DumpMap(Hashtable /*Variable->Expr*/! map) { - foreach (DictionaryEntry de in map) { - Variable! v = (Variable!)de.Key; - Expr! e = (Expr!)de.Value; - Console.Write(" "); - v.Emit(new TokenTextWriter("", Console.Out, false), 0); - Console.Write(" --> "); - e.Emit(new TokenTextWriter("", Console.Out, false)); - Console.WriteLine(); - } - } - } -} diff --git a/Source/VCGeneration/VCDoomed.cs b/Source/VCGeneration/VCDoomed.cs new file mode 100644 index 00000000..50307c0b --- /dev/null +++ b/Source/VCGeneration/VCDoomed.cs @@ -0,0 +1,1258 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) Microsoft Corporation. All Rights Reserved. +// +//----------------------------------------------------------------------------- +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; +using System.IO; +using Microsoft.Boogie; +using Graphing; +using AI = Microsoft.AbstractInterpretationFramework; +using Microsoft.Contracts; +using Microsoft.Basetypes; +using Microsoft.Boogie.VCExprAST; + +namespace VC +{ + public class DCGen : ConditionGeneration + { + #region Attributes + private Dictionary! m_BlockReachabilityMap; + Dictionary! m_copiedBlocks = new Dictionary(); + const string reachvarsuffix = "__ivebeenthere"; + List! m_doomedCmds = new List(); + #endregion + /// + /// Constructor. Initializes the theorem prover. + /// + public DCGen(Program! program, string/*?*/ logFilePath, bool appendLogFile) + { + base(program); + this.appendLogFile = appendLogFile; + this.logFilePath = logFilePath; + m_BlockReachabilityMap = new Dictionary(); + } + + + private void Impl2Dot(Implementation! impl, string! filename) { + List! nodes = new List(); + List! edges = new List(); + + string nodestyle = "[shape=box];" ; + + foreach (Block! b in impl.Blocks) { + nodes.Add(string.Format("\"{0}\" {1}", b.Label, nodestyle) ); + GotoCmd gc = b.TransferCmd as GotoCmd; + if (gc!=null) { + assert gc.labelTargets!=null; + foreach (Block! b_ in gc.labelTargets) { + edges.Add(String.Format("\"{0}\" -> \"{1}\";", b.Label, b_.Label ) ); + } + } + } + + using (StreamWriter sw = new StreamWriter(filename)) + { + sw.WriteLine(String.Format("digraph {0} {{", impl.Name)); +// foreach (string! s in nodes) { +// sw.WriteLine(s); +// } + foreach (string! s in edges) { + sw.WriteLine(s); + } + sw.WriteLine("}}"); + sw.Close(); + } + } + + private const string _copyPrefix = "Yeah"; + + private Block! CopyImplBlocks(Block! b, ref List! blocklist, Block! targetBlock, ref Dictionary! alreadySeen) { + Block seen; + if (alreadySeen.TryGetValue(b, out seen) ) { + assert seen!=null; + return seen; + } + + GotoCmd gc = b.TransferCmd as GotoCmd; + TransferCmd tcmd = null; + if (gc!=null) { + BlockSeq! bseq = new BlockSeq(); + assert gc.labelTargets!=null; + foreach (Block! c in gc.labelTargets) { + bseq.Add(CopyImplBlocks(c, ref blocklist, targetBlock, ref alreadySeen)); + } + tcmd = new GotoCmd(gc.tok, bseq); + } else { +// BlockSeq! bseq_ = new BlockSeq(); +// bseq_.Add(targetBlock); + assert b.TransferCmd != null; +// tcmd = new GotoCmd(b.TransferCmd.tok, bseq_); + tcmd = new ReturnCmd(b.TransferCmd.tok); + } + + CodeCopier codeCopier = new CodeCopier(); + CmdSeq! cl = new CmdSeq(); + foreach (Cmd! _c in b.Cmds) { + if (!ContainsReachVariable(_c)) + cl.Add( codeCopier.CopyCmd(_c)); + } + + Block! b_ = new Block(b.tok, b.Label+_copyPrefix, cl, tcmd); + blocklist.Add(b_); + + alreadySeen[b] = b_; + + return b_; + } + + /* + After adding a copy of the implementation in front of our code + we remove all the edges leading from the copy to the original code + */ + private void RemoveArtificialGoto(Block! b, Block! target) { + GotoCmd gc = b.TransferCmd as GotoCmd; + + if (gc!=null) { + assert gc.labelTargets!=null; + foreach (Block! gt in gc.labelTargets) { + if (gt==target) { + assert gc.labelTargets.Length==1; + assert gc.labelTargets[0]!=null; + b.TransferCmd = new ReturnCmd(gc.tok); + return; + } else { + RemoveArtificialGoto(gt, target); + } + } + + } + } + + static public bool UseItAsDebugger = false; + +// public static Implementation _tmpImpl = null; // (MsSchaef) HACK! + + public static Block firstNonDebugBlock = null; + public static Block firstDebugBlock = null; + + private void ModifyImplForDebugging(Implementation! impl) + { + //List backup_blocks=null; + + + if (UseItAsDebugger) { + #region Copy the Implementation ///////////////////// + + ConsoleColor col = Console.ForegroundColor; + Console.ForegroundColor = ConsoleColor.Magenta; + Console.WriteLine("Warning you are using the Infinite Improbability Drive!"); + Console.ForegroundColor = col; + + List! blist = new List(); + Dictionary! tmpdict = new Dictionary(); + CopyImplBlocks(impl.Blocks[0], ref blist, impl.Blocks[0], ref tmpdict); + blist.Reverse(); + //_tmpImpl = new Implementation(impl.tok, impl.Name, impl.TypeParameters, impl.InParams, impl.OutParams, impl.LocVars, blist); + + #endregion //////////////////////////////////// + + #region Add implementation copy in front of implementation + // memorize where the original code starts + firstNonDebugBlock = impl.Blocks[0]; + firstDebugBlock = blist[0]; + // now add the copied program in front of the original one + blist.AddRange(impl.Blocks); + //backup_blocks = new List(impl.Blocks); + + BlockSeq! newbseq = new BlockSeq(); + newbseq.Add(firstNonDebugBlock); + newbseq.Add(firstDebugBlock); + + GotoCmd! newtcmd = new GotoCmd(Token.NoToken, newbseq); + Block! newfirst = new Block(Token.NoToken, "MySuperFirstBlock", new CmdSeq(), newtcmd); + + + impl.Blocks = new List(); + impl.Blocks.Add(newfirst); + impl.Blocks.AddRange(blist); + + //Impl2Dot(impl, String.Format("c:/dot/{0}_copied.dot", impl.Name) ); + #endregion + } + } + + void RemoveReachVars(Block! b) { + GotoCmd gc = b.TransferCmd as GotoCmd; + if (gc!=null) { + assert gc.labelTargets!=null; + + CmdSeq! cs = new CmdSeq(); + foreach (Cmd! c in b.Cmds) { + if (!ContainsReachVariable(c)) cs.Add(c); + } + b.Cmds = cs; + + foreach (Block! c in gc.labelTargets) { + if (c.Label != "GeneratedUnifiedExit") { + RemoveReachVars(c); + } + } + } + } + + void RemoveLastBlock(Block! b) + { + GotoCmd gc = b.TransferCmd as GotoCmd; + if (gc==null) { + //Console.WriteLine("WARNING: Check Node {0}", b.Label); + return; + } + assert gc!=null; + assert gc.labelTargets !=null; + BlockSeq! tmp = new BlockSeq(); + foreach (Block! c in gc.labelTargets) { + // Warning, we should not search by name! + if (c.Label != "GeneratedUnifiedExit" ) { + tmp.Add(c); + RemoveLastBlock(c); + } else { + c.Predecessors.Remove(b); + } + } + if (tmp.Length==0) { + b.TransferCmd = new ReturnCmd(gc.tok); + } else { + b.TransferCmd = new GotoCmd(gc.tok, tmp); + } + } + + void FindCopiedBlocks(Block! b) { + _copiedBlock.Add(b); + GotoCmd gc = b.TransferCmd as GotoCmd; + if (gc!=null) { + assert gc.labelTargets!=null; + foreach (Block! c in gc.labelTargets) { + FindCopiedBlocks(c); + } + } + } + + private List! _copiedBlock = new List(); + + /// + /// MSchaef: + /// - remove loops and add reach variables + /// - make it a passive program + /// - compute the wlp for each block + /// - check if |= (reach=false) => wlp.S.false holds for each reach + /// + /// + public override Outcome VerifyImplementation(Implementation! impl, Program! program, VerifierCallback! callback) + throws UnexpectedProverOutputException; + { + + UseItAsDebugger = CommandLineOptions.Clo.useDoomDebug; + Stopwatch watch = new Stopwatch(); + + //Impl2Dot(impl, String.Format("c:/dot/{0}_raw.dot", impl.Name) ); + + if (CommandLineOptions.Clo.TraceVerify) { + Console.WriteLine(">>> Checking function {0} for doomed points.", impl.Name); + } + Console.WriteLine("Checking function {0} for doomed points:", impl.Name); + callback.OnProgress("doomdetector",0,0,0); + + watch.Reset(); + watch.Start(); + + + + #region Transform the Program into loop-free passive form + variable2SequenceNumber = new Hashtable/*Variable -> int*/(); + incarnationOriginMap = new Dictionary(); + List! cblocks = new List(); + + //List! orig_blocks = new List(impl.Blocks); + + Dictionary copiedblocks; + impl.Blocks = DCProgramTransformer.Convert2Dag(impl, program, cblocks, out copiedblocks); + assert copiedblocks!=null; + +// List! blist = new List(); +// blist.AddRange(impl.Blocks); + + if (UseItAsDebugger) ModifyImplForDebugging(impl); + + ComputePredecessors(impl.Blocks); + + m_BlockReachabilityMap = new Dictionary(); + GenerateReachVars(impl); + + if (UseItAsDebugger) RemoveReachVars((!)firstDebugBlock); + + PassifyProgram(impl); + + + #endregion + //EmitImpl(impl,false); + + + //Impl2Dot(impl, String.Format("c:/dot/{0}_passive.dot", impl.Name) ); + +// --------------------------------------------------------------------------- + if (UseItAsDebugger) { + assert firstNonDebugBlock != null && firstDebugBlock != null; + firstNonDebugBlock.Predecessors.Remove(impl.Blocks[0]); + firstDebugBlock.Predecessors.Remove(impl.Blocks[0]); +// impl.Blocks.Remove(impl.Blocks[0]); // remove the artificial first block + RemoveLastBlock(firstDebugBlock); // remove the goto to the unified exit + _copiedBlock.Clear(); + FindCopiedBlocks(firstDebugBlock); + } +// --------------------------------------------------------------------------- + +// EmitImpl(impl,false); + + //Impl2Dot(impl, String.Format("c:/dot/{0}_final.dot", impl.Name) ); + + bool __debug = false; + + watch.Stop(); + if (__debug) Console.WriteLine("Transformation takes: {0}", watch.Elapsed.ToString() ); + watch.Reset(); + + Checker! checker = FindCheckerFor(impl, 1000); + + DoomCheck dc = new DoomCheck(impl, checker); + + int _totalchecks = 0; + Block b = null; + ProverInterface.Outcome outcome; + dc.ErrorHandler = new DoomErrorHandler(dc.Label2Absy, callback); + + System.TimeSpan ts = watch.Elapsed; + + while (dc.GetNextBlock(out b) ) { + assert b!=null; + outcome = ProverInterface.Outcome.Undetermined; + //Console.WriteLine("Checking block {0} ...",b.Label); + Variable v = null; + m_BlockReachabilityMap.TryGetValue(b, out v); + assert v!=null; + _totalchecks++; + + + watch.Start(); + if (!dc.CheckLabel(v, out outcome) ) { + return Outcome.Inconclusive; + } + watch.Stop(); + ts+=watch.Elapsed; + if (__debug) Console.WriteLine(" Time for Block {0}: {1} elapsed",b.Label, watch.Elapsed.ToString()); + watch.Reset(); + + + switch (outcome) { + case ProverInterface.Outcome.Valid: { + break; + } + case ProverInterface.Outcome.Invalid: { + + break; + } + default: { + + break; + } + } + + } + checker.Close(); + + if (__debug) Console.WriteLine("Number of Checked Blocks: {0} of {1}", _totalchecks, impl.Blocks.Count ); + if (__debug) Console.WriteLine("Total time for this method: {0}", ts.ToString()); + + #region Try to produce a counter example (brute force) + if (dc.DoomedSequences.Count>0 ) { + ConsoleColor col = Console.ForegroundColor; +// Console.ForegroundColor = ConsoleColor.Red; +// Console.WriteLine(" {0} is DOOMED!", impl.Name); +// foreach (List bl in dc.DoomedSequences) { +// Console.Write("Doomed Blocks: "); +// foreach (Block! b_ in bl) { +// Console.Write("{0}, ", b_.Label); +// } +// Console.WriteLine(); +// } + Console.ForegroundColor = col; + + int counter=1; + foreach (List! bl in dc.DoomedSequences) { + Console.WriteLine("Doomed program point {0} of {1}", counter++, dc.DoomedSequences.Count); + dc.ErrorHandler.m_DoomedBlocks = bl; + foreach (Block! b_ in bl) { + if (m_BlockReachabilityMap.TryGetValue(b_, out dc.ErrorHandler.m_Reachvar) )break; + } + SearchCounterexample(impl, dc.ErrorHandler, callback); + } + + //SearchCounterexample(impl, dc.ErrorHandler, callback); + Console.WriteLine("------------------------------ \n\n"); + return Outcome.Errors; + } + #endregion + + Console.WriteLine("------------------------------ \n\n"); + + return Outcome.Correct; + } + + + + private void SearchCounterexample(Implementation! impl, DoomErrorHandler! errh, VerifierCallback! callback) { + if (errh.m_Reachvar==null) { + assert false; + } + m_doomedCmds.Clear(); + + Dictionary! cmdbackup = new Dictionary(); + + BruteForceCESearch(errh.m_Reachvar, impl, callback, cmdbackup, 0, impl.Blocks.Count/2-1); + BruteForceCESearch(errh.m_Reachvar, impl, callback, cmdbackup, impl.Blocks.Count/2,impl.Blocks.Count-1); + + List! causals = CollectCausalStatements(impl.Blocks[0]); + foreach (Cmd! c in causals ) { + GenerateErrorMessage(c, causals); + } + + #region Undo all modifications + foreach (KeyValuePair kvp in cmdbackup) { + kvp.Key.Cmds = kvp.Value; + } + #endregion + } + +#region Causal Statement Tree + + private void GenerateErrorMessage(Cmd! causalStatement, List! causals) + { + AssumeCmd uc = causalStatement as AssumeCmd; + AssertCmd ac = causalStatement as AssertCmd; + ConsoleColor col = Console.ForegroundColor; + + // Trivial case. Must be either assume or assert false + if (m_doomedCmds.Count==1) { + Console.WriteLine("Found a trivial error:" ); + if (uc!=null) { + Console.Write("Trivial false assumption: " ); + Console.Write("({0};{1}):", uc.tok.line, uc.tok.col ); + } + if (ac!=null) { + Console.Write("Trivial false assertion: " ); + Console.Write("({0};{1}):", ac.tok.line, ac.tok.col ); + } + causalStatement.Emit(new TokenTextWriter("", Console.Out, false), 0); + } else { + // Safety error + if (ac!=null) { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("Safety error:"); + Console.ForegroundColor = col; + Console.Write("This assertion is violated: "); + Console.Write("({0};{1}):", ac.tok.line, ac.tok.col ); + ac.Emit(new TokenTextWriter("", Console.Out, false), 0); + } + if (uc!=null) { + bool containsAssert = false; + foreach (Cmd! c in m_doomedCmds) { + if (causals.Contains(c) ) { + continue; + } + AssertCmd asrt = c as AssertCmd; + if (asrt!=null) { + containsAssert=true; + break; + } + } + // Plausibility error + if (containsAssert) { + Console.ForegroundColor = ConsoleColor.Yellow ; + Console.WriteLine("Plausibility error:"); + Console.ForegroundColor = col; + Console.Write("There is no legal exeuction passing: "); + Console.Write("({0};{1})", uc.tok.line, uc.tok.col ); + uc.Emit(new TokenTextWriter("", Console.Out, false), 0); + } else { // Reachability error + Console.ForegroundColor = ConsoleColor.DarkRed ; + Console.WriteLine("Reachability error:"); + Console.ForegroundColor = col; + Console.Write("No execution can reach: "); + Console.Write("({0};{1})", uc.tok.line, uc.tok.col ); + uc.Emit(new TokenTextWriter("", Console.Out, false), 0); + } + + } + + Console.ForegroundColor = ConsoleColor.White; + Console.WriteLine("...on any execution passing through:"); + foreach (Cmd! c in m_doomedCmds) { + if (causals.Contains(c) ) { + continue; + } + Console.ForegroundColor = col; + Console.Write("In ({0};{1}): ", c.tok.line, c.tok.col ); + Console.ForegroundColor = ConsoleColor.DarkYellow ; + c.Emit(new TokenTextWriter("", Console.Out, false), 0); + } + Console.ForegroundColor = col; + Console.WriteLine("--"); + + } + } + + private List! CollectCausalStatements(Block! b) { + Cmd lastCausal=null; + foreach (Cmd! c in b.Cmds) { + AssertCmd ac = c as AssertCmd; + AssumeCmd uc = c as AssumeCmd; + if (ac!=null && !ContainsReachVariable(ac)) { + if (ac.Expr!=Expr.True) { + lastCausal = c; + } + } else if (uc!=null && !ContainsReachVariable(uc)) { + lastCausal = c; + } + } + + List causals = new List(); + GotoCmd gc = b.TransferCmd as GotoCmd; + if (gc!=null && gc.labelTargets!=null) { + List tmp; + //bool allcausal = true; + foreach (Block! b_ in gc.labelTargets) { + tmp = CollectCausalStatements(b_); + + foreach (Cmd cau in tmp) { + if (!causals.Contains(cau)) causals.Add(cau); + } + } + //if (allcausal) + if (causals.Count>0) return causals; + } + if (lastCausal!=null) causals.Add( lastCausal); + return causals; + } + +#endregion + + bool BruteForceCESearch(Variable! reachvar, Implementation! impl, VerifierCallback! callback, + Dictionary! cmdbackup, int startidx, int endidx) { + #region Modify implementation + for (int i=startidx; i<=endidx; i++) { + if (_copiedBlock.Contains(impl.Blocks[i])) continue; + CmdSeq! cs = new CmdSeq(); + cmdbackup.Add(impl.Blocks[i],impl.Blocks[i].Cmds); + foreach (Cmd! c in impl.Blocks[i].Cmds) { + if (ContainsReachVariable(c)) { + cs.Add(c); + continue; + } + AssertCmd ac = c as AssertCmd; + AssumeCmd uc = c as AssumeCmd; + if (ac!=null) { + cs.Add(new AssertCmd(ac.tok, Expr.True) ); + } else if (uc!=null) { + cs.Add(new AssertCmd(uc.tok, Expr.True) ); + } else { + cs.Add(c); + } + } + impl.Blocks[i].Cmds = cs; + } + #endregion + + ProverInterface.Outcome outcome = ProverInterface.Outcome.Undetermined; + if (!ReCheckImpl(reachvar, impl, callback, out outcome) ) { + UndoBlockModifications(impl, cmdbackup, startidx, endidx); + return false; + } + if (outcome == ProverInterface.Outcome.Valid) { + return true; + } else if (outcome == ProverInterface.Outcome.Invalid) { + UndoBlockModifications(impl, cmdbackup, startidx, endidx); + int mid = startidx + (endidx-startidx)/2; + if (startidx>=endidx) { + // Now we found an interesting Block and we have to + // search for the interesting statements. + int cmdcount = impl.Blocks[endidx].Cmds.Length; + BruteForceCmd(impl.Blocks[endidx],0,cmdcount/2 -1,reachvar, impl, callback); + BruteForceCmd(impl.Blocks[endidx],cmdcount/2,cmdcount-1,reachvar, impl, callback); + return true; + } else { + BruteForceCESearch(reachvar,impl, callback, cmdbackup, startidx, mid); + BruteForceCESearch(reachvar,impl, callback, cmdbackup, mid+1, endidx); + return true; + } + } else { + UndoBlockModifications(impl, cmdbackup, startidx, endidx); + return false; + } + } + + bool BruteForceCmd(Block! b, int startidx, int endidx, Variable! reachvar, + Implementation! impl, VerifierCallback! callback) { + #region Modify Cmds + CmdSeq! backup = b.Cmds; + CmdSeq! cs = new CmdSeq(); + for (int i=0;i=endidx) { + if (!ContainsReachVariable(b.Cmds[endidx])) { +// Console.Write(" Witness ("); +// +// ConsoleColor col = Console.ForegroundColor; +// Console.ForegroundColor = ConsoleColor.White; +// Console.Write("{0};{1}", b.Cmds[endidx].tok.line, b.Cmds[endidx].tok.col ); +// Console.ForegroundColor = col; +// Console.Write("): "); +// Console.ForegroundColor = ConsoleColor.Yellow; +// b.Cmds[endidx].Emit(new TokenTextWriter("", Console.Out, false), 0); +// Console.ForegroundColor = col; + + m_doomedCmds.Add(b.Cmds[endidx]); + return true; + } else { + return false; + } + } else { + int mid = startidx + (endidx-startidx)/2; + BruteForceCmd(b, startidx, mid, reachvar, impl, callback); + BruteForceCmd(b, mid+1, endidx, reachvar, impl, callback); + return false; // This is pure random + } + } else { + b.Cmds = backup; + return false; + } + + return false; + } + + void UndoBlockModifications(Implementation! impl, Dictionary! cmdbackup, + int startidx, int endidx) { + for (int i=startidx; i<=endidx; i++) { + CmdSeq cs = null; + if (cmdbackup.TryGetValue(impl.Blocks[i], out cs) ) { + assert cs!=null; + impl.Blocks[i].Cmds = cs; + cmdbackup.Remove(impl.Blocks[i]); + } + } + } + + bool ReCheckImpl(Variable! reachvar, Implementation! impl, VerifierCallback! callback, + out ProverInterface.Outcome outcome) { + Checker! checker = FindCheckerFor(impl, 1000); + DoomCheck dc = new DoomCheck(impl, checker); + dc.ErrorHandler = new DoomErrorHandler(dc.Label2Absy, callback); + outcome = ProverInterface.Outcome.Undetermined; + if (!dc.CheckLabel(reachvar, out outcome)) { + checker.Close(); + return false; + } + checker.Close(); + return true; + } + + + + bool ContainsReachVariable(Cmd! c) { + AssertCmd artc = c as AssertCmd; + AssumeCmd amec = c as AssumeCmd; + Expr e; + if (artc!=null) { + e = artc.Expr; + } else if (amec!=null) { + e = amec.Expr; + } else { + return false; + } + Set! freevars = new Set(); + e.ComputeFreeVariables(freevars); + foreach (Variable! v in freevars) { + if (v.Name.Contains(reachvarsuffix)) return true; + } + return false; + } + + + #region Loop Removal + /// + /// This class is accessed only via the static method Convert2Dag + /// It converts the program into a loopfree one by unrolling the loop threetimes and adding the appropriate havoc + /// statements. The first and the last unrolling represent the first and last iteration of the loop. The second + /// unrolling stands for any other iteration. + /// + private class DCProgramTransformer + { + private List! Blocks; + private List! m_checkableBlocks; + private Dictionary! m_copyMap = new Dictionary(); + + public static List! Convert2Dag(Implementation! impl, Program! program, List! checkableBlocks, + out Dictionary copiedblocks) + { + Block! start = impl.Blocks[0]; + Dictionary gd = new Dictionary(); + Set/*Block*/! beingVisited = new Set/*Block*/(); + GraphNode gStart = GraphNode.ComputeGraphInfo(null, start, gd, beingVisited); + + DCProgramTransformer pt = new DCProgramTransformer(checkableBlocks); + pt.LoopUnrolling(gStart, new Dictionary(), true, ""); + pt.Blocks.Reverse(); + copiedblocks = pt.m_copyMap; + return pt.Blocks; + } + + + DCProgramTransformer(List! checkableBlocks) + { + Blocks = new List(); + m_checkableBlocks = checkableBlocks; + } + + +#region Loop Unrolling Methods + + private Block! LoopUnrolling(GraphNode! node, Dictionary! visited, bool unrollable, String! prefix) + { + Block newb; + if (visited.TryGetValue(node, out newb)) + { + assert newb!=null; + return newb; + } else + { + if (node.IsCutPoint) + { + // compute the loop body and the blocks leaving the loop + + List! loopNodes = new List(); + GatherLoopBodyNodes(node, node, loopNodes); + + List! exitNodes = GatherLoopExitNodes(loopNodes); + + // Continue Unrolling after the current loop + Dictionary! _visited = new Dictionary(); + foreach (GraphNode! g in exitNodes) + { + Block b = LoopUnrolling(g, visited, unrollable, prefix); + _visited.Add(g,b); + } + newb = UnrollCurrentLoop(node, _visited, loopNodes,unrollable, prefix); + visited.Add(node,newb); + } else + { + BlockSeq! newSuccs = new BlockSeq(); + foreach(GraphNode! g in node.Succecessors) + { + newSuccs.Add( LoopUnrolling(g,visited,unrollable,prefix) ); + } + newb = new Block(node.Block.tok, node.Block.Label + prefix , node.Body, node.Block.TransferCmd); + Block b; + if (m_copyMap.TryGetValue(node.Block, out b) ) { + assert b!=null; + m_copyMap.Add(newb, b); + } else { + m_copyMap.Add(newb, node.Block); + } + + + assert newb!=null; assert newb.TransferCmd!=null; + if (newSuccs.Length == 0) + newb.TransferCmd = new ReturnCmd(newb.TransferCmd.tok); + else + newb.TransferCmd = new GotoCmd(newb.TransferCmd.tok, newSuccs); + + visited.Add(node, newb); + Blocks.Add(newb); + if (unrollable) + { + m_checkableBlocks.Add(newb); + } + } + } + assert newb!=null; + //newb.checkable = unrollable; + return newb; + } + + private Block! UnrollCurrentLoop(GraphNode! cutPoint, Dictionary! visited, + List! loopNodes, bool unrollable, String! prefix) + { + if (unrollable) + { + Dictionary! visited1 = new Dictionary(visited); + Dictionary! visited2 = new Dictionary(visited); + Dictionary! visited3 = new Dictionary(visited); + + Block! loopend = ConstructLoopExitBlock(cutPoint, loopNodes, visited, prefix+"#Last"); + + Block! last = UnrollOnce(cutPoint, loopend,visited1,false, prefix+"#Last"); + AddHavocCmd(last,loopNodes); + + // You might use true for the unrollable flag as well. + Block! arb = UnrollOnce(cutPoint, last,visited2,false, prefix+"#Arb"); + AddHavocCmd(arb,loopNodes); + + + BlockSeq! succ = new BlockSeq(); + succ.Add(last); succ.Add(arb); + assert arb.TransferCmd!=null; + Block! tmp = new Block(arb.tok, arb.Label + prefix+"#Dummy" , new CmdSeq(), new GotoCmd(arb.TransferCmd.tok, succ)); + Blocks.Add(tmp); + m_checkableBlocks.Add(tmp); + + // check if arb is already a copy of something else + // if not then write to m_copyMap that tmp is a copy + // of arb + Block b = null; + if (m_copyMap.TryGetValue(arb,out b) ) { + assert b!=null; + m_copyMap.Add(tmp, b); + } else { + m_copyMap.Add(tmp, arb); + } + + Block! first = UnrollOnce(cutPoint, tmp,visited3,false, prefix+"#First"); + + return first; + + } else + { + Dictionary! visited_ = new Dictionary(visited); + Block! loopend = AbstractIteration(cutPoint, prefix+"#UR"); + Block! ret = UnrollOnce(cutPoint, loopend,visited_,false, prefix); + AddHavocCmd(ret, loopNodes); + return ret; + } + } + + private Block! UnrollOnce(GraphNode! node, Block! nextIter, Dictionary! visited, bool unrollable, String! prefix) + { + visited.Add(node, nextIter); + Block newb,b; + BlockSeq! newSuccs = new BlockSeq(); + foreach(GraphNode! g in node.Succecessors) + { + newSuccs.Add( LoopUnrolling(g,visited,unrollable,prefix) ); + } + newb = new Block(node.Block.tok, node.Block.Label + prefix , node.Body, node.Block.TransferCmd); + if (m_copyMap.TryGetValue(node.Block, out b) ) { + assert b!=null; + m_copyMap.Add(newb, b); + } else { + m_copyMap.Add(newb, node.Block); + } + + assert newb!=null; assert newb.TransferCmd!=null; + if (newSuccs.Length == 0) + newb.TransferCmd = new ReturnCmd(newb.TransferCmd.tok); + else + newb.TransferCmd = new GotoCmd(newb.TransferCmd.tok, newSuccs); + + Blocks.Add(newb); + if (unrollable) m_checkableBlocks.Add(newb); + return newb; + } + + private Block! AbstractIteration(GraphNode! node, String! prefix) + { + CmdSeq body = new CmdSeq(); + foreach (Cmd! c in node.Body) + { + if (c is PredicateCmd || c is CommentCmd) + body.Add(c ); + else + break; + } + body.Add(new AssumeCmd(node.Block.tok, Expr.False) ); + TransferCmd! tcmd = new ReturnCmd(node.Block.tok); + Block! b = new Block(node.Block.tok, node.Block.Label + prefix, body, tcmd); + Blocks.Add(b); + Block tmp; + if (m_copyMap.TryGetValue(node.Block, out tmp) ) { + assert tmp!=null; + m_copyMap.Add(b, tmp); + } else { + m_copyMap.Add(b, node.Block); + } + + return b; + } + + private Block! ConstructLoopExitBlock(GraphNode! cutPoint, List! loopNodes, + Dictionary! visited, String! prefix) + { + BlockSeq! newSucc = new BlockSeq(); + Block! orig = cutPoint.Block; + + // detect the block after the loop + // FixMe: What happens when using break commands? + foreach (GraphNode! g in cutPoint.Succecessors) + { + if (!loopNodes.Contains(g)) + { + Block b; + if (visited.TryGetValue(g,out b) ) + newSucc.Add(b); + } + } + TransferCmd tcmd; + assert orig.TransferCmd!=null; + if (newSucc.Length==0) + tcmd = new ReturnCmd(orig.TransferCmd.tok); + else + tcmd = new GotoCmd(orig.TransferCmd.tok, newSucc); + // FixMe: Genertate IToken for counterexample creation + Block! newb = new Block(orig.tok, orig.Label+prefix+"#Leave", orig.Cmds, tcmd); + Blocks.Add(newb); + m_checkableBlocks.Add(newb); + return newb; + } + + + private void GatherLoopBodyNodes(GraphNode! current, GraphNode! cutPoint, List! loopNodes) + { + loopNodes.Add(current); + if (false) System.Diagnostics.Debugger.Break(); + foreach (GraphNode! g in current.Predecessors) + { + if (cutPoint.firstPredecessor == g || g == cutPoint || loopNodes.Contains(g) ) continue; + GatherLoopBodyNodes(g, cutPoint, loopNodes); + } + } + + private List! GatherLoopExitNodes(List! loopNodes) + { + List! exitnodes = new List(); + + foreach (GraphNode! g in loopNodes) + { + foreach (GraphNode! s in g.Succecessors) + { + if (!loopNodes.Contains(s) /*&& !exitnodes.Contains(s)*/ ) exitnodes.Add(s); + } + } + return exitnodes; + } + + private void AddHavocCmd(Block! b, List! loopNodes) + { + List! loopBlocks = new List(); + foreach (GraphNode! g in loopNodes) loopBlocks.Add(g.Block); + HavocCmd! hcmd = HavocLoopTargets(loopBlocks,b.tok); + CmdSeq! body = new CmdSeq(); + body.Add(hcmd); + body.AddRange(b.Cmds); + b.Cmds = body; + } + + private HavocCmd! HavocLoopTargets(List! bl, IToken! tok) + { + VariableSeq varsToHavoc = new VariableSeq(); + foreach ( Block! b in bl ) + { + foreach ( Cmd! c in b.Cmds ) + { + c.AddAssignedVariables(varsToHavoc); + } + } + IdentifierExprSeq havocExprs = new IdentifierExprSeq(); + foreach ( Variable! v in varsToHavoc ) + { + IdentifierExpr ie = new IdentifierExpr(Token.NoToken, v); + if(!havocExprs.Has(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 + return new HavocCmd(tok,havocExprs); + } + + #endregion + + + #region GraphNode + private class GraphNode + { + public readonly Block! Block; + public readonly CmdSeq! Body; + public bool IsCutPoint; // is set during ComputeGraphInfo + [Rep] public readonly List! Predecessors = new List(); + [Rep] public readonly List! Succecessors = new List(); + public GraphNode firstPredecessor; + public List! UnavoidableNodes = new List(); // should be done using a set + + GraphNode(Block! b, CmdSeq! body) + { + Block = b; Body = body; + IsCutPoint = false; + + } + + static CmdSeq! GetOptimizedBody(CmdSeq! cmds) + { + int n = 0; + foreach (Cmd c in cmds) + { + n++; + PredicateCmd pc = c as PredicateCmd; + if (pc != null && pc.Expr is LiteralExpr && ((LiteralExpr)pc.Expr).IsFalse) + { + // return a sequence consisting of the commands seen so far + Cmd[] s = new Cmd[n]; + for (int i = 0; i < n; i++) + { + s[i] = cmds[i]; + } + return new CmdSeq(s); + } + } + return cmds; + } + + private static List! Intersect(List! left, List! right) + { + List! ret = new List(); + List! tmp = left; + tmp.AddRange(right); + foreach (GraphNode! gn in tmp) { + if (ret.Contains(gn) ) continue; + if (left.Contains(gn) && right.Contains(gn)) ret.Add(gn); + } + return ret; + } + + public static GraphNode! ComputeGraphInfo(GraphNode from, Block! b, Dictionary! gd, Set /*Block*/! beingVisited) + { + GraphNode g; + if (gd.TryGetValue(b, out g)) + { + assume from != null; + assert g != null; + + g.UnavoidableNodes = Intersect(g.UnavoidableNodes, from.UnavoidableNodes); + if (!g.UnavoidableNodes.Contains(g)) g.UnavoidableNodes.Add(g); + + g.Predecessors.Add(from); + if (g.firstPredecessor==null) + g.firstPredecessor = from; + + if (beingVisited.Contains(b)) + g.IsCutPoint = true; // it's a cut point + } else + { + CmdSeq body = GetOptimizedBody(b.Cmds); + g = new GraphNode(b, body); + gd.Add(b, g); + if (from != null) + { + g.Predecessors.Add(from); + if (from==null) + g.firstPredecessor = g; + + if (g.firstPredecessor==null) + g.firstPredecessor = from; + + } + if (body != b.Cmds) + { + // the body was optimized -- there is no way through this block + } else + { + beingVisited.Add(b); + GotoCmd gcmd = b.TransferCmd as GotoCmd; + if (gcmd != null) + { + assume gcmd.labelTargets != null; + foreach (Block! succ in gcmd.labelTargets) + { + g.Succecessors.Add( ComputeGraphInfo(g, succ, gd, beingVisited) ); + } + } + beingVisited.Remove(b); + } + } + return g; + } + } + + } + #endregion + + #endregion + + #region Program Passification + private void GenerateReachVars(Implementation! impl) + { + Hashtable gotoCmdOrigins = new Hashtable(); + Block! exitBlock = GenerateUnifiedExit(impl, gotoCmdOrigins); + AddBlocksBetween(impl); + + #region Insert pre- and post-conditions and where clauses as assume and assert statements + { + CmdSeq cc = new CmdSeq(); + // where clauses of global variables + foreach (Declaration d in program.TopLevelDeclarations) { + GlobalVariable gvar = d as GlobalVariable; + 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) { + if (lvar.TypedIdent.WhereExpr != null) { + Cmd c = new AssumeCmd(lvar.tok, lvar.TypedIdent.WhereExpr); + cc.Add(c); + } + } + + // 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 + exitBlock = InjectPostConditions(impl,exitBlock,gotoCmdOrigins); + } + #endregion + GenerateReachabilityPredicates(impl, exitBlock); + } + + + private Hashtable/*TransferCmd->ReturnCmd*/! PassifyProgram(Implementation! impl) + { + current_impl = impl; + Convert2PassiveCmd(impl); + impl = current_impl; + return new Hashtable(); + } + + /// + /// Add additional variable to allow checking as described in the paper + /// "It's doomed; we can prove it" + /// + private void GenerateReachabilityPredicates(Implementation! impl, Block! exitBlock) + { + ExprSeq! es = new ExprSeq(); + Cmd eblockcond = null; + + foreach (Block! b in impl.Blocks) + { + //if (b.Predecessors.Length==0) continue; + //if (b.Cmds.Length == 0 ) continue; + + Variable v_ = new LocalVariable(Token.NoToken, + new TypedIdent(b.tok, b.Label+reachvarsuffix,new BasicType(SimpleType.Bool) ) ); + + impl.LocVars.Add(v_); + + m_BlockReachabilityMap[b] = v_; + + IdentifierExpr! lhs = new IdentifierExpr(b.tok, v_); + + es.Add( new IdentifierExpr(b.tok, v_) ); + + List! lhsl = new List(); + lhsl.Add(new SimpleAssignLhs(Token.NoToken, lhs) ); + List! rhsl = new List(); + rhsl.Add(Expr.True); + + if (b!=exitBlock) + { + CmdSeq cs = new CmdSeq(new AssignCmd(Token.NoToken, lhsl, rhsl)); + cs.AddRange(b.Cmds); + b.Cmds = cs; + } else + { + eblockcond = new AssignCmd(Token.NoToken, lhsl, rhsl); + } + + //checkBlocks.Add(new CheckableBlock(v_,b)); + } + if (es.Length==0) return; + + Expr aexp = null; + + if (es.Length==1) + { + aexp = es[0]; + } else if (es.Length==2) + { + aexp = Expr.Binary(Token.NoToken, + BinaryOperator.Opcode.And, + (!)es[0], + (!)es[1]); + } else + { + aexp = Expr.True; + foreach (Expr e_ in es) + { + aexp = Expr.Binary(Token.NoToken, + BinaryOperator.Opcode.And, + (!)e_, aexp); + } + } + assert (aexp!=null); + assert (eblockcond!=null); + + AssumeCmd ac = new AssumeCmd(Token.NoToken, aexp); + + assert(exitBlock!=null); + + CmdSeq cseq = new CmdSeq(eblockcond); + cseq.AddRange(exitBlock.Cmds); + cseq.Add(ac); + + exitBlock.Cmds = cseq; + } + + #endregion + + } +} diff --git a/Source/VCGeneration/VCDoomed.ssc b/Source/VCGeneration/VCDoomed.ssc deleted file mode 100644 index 50307c0b..00000000 --- a/Source/VCGeneration/VCDoomed.ssc +++ /dev/null @@ -1,1258 +0,0 @@ -//----------------------------------------------------------------------------- -// -// Copyright (C) Microsoft Corporation. All Rights Reserved. -// -//----------------------------------------------------------------------------- -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.Threading; -using System.IO; -using Microsoft.Boogie; -using Graphing; -using AI = Microsoft.AbstractInterpretationFramework; -using Microsoft.Contracts; -using Microsoft.Basetypes; -using Microsoft.Boogie.VCExprAST; - -namespace VC -{ - public class DCGen : ConditionGeneration - { - #region Attributes - private Dictionary! m_BlockReachabilityMap; - Dictionary! m_copiedBlocks = new Dictionary(); - const string reachvarsuffix = "__ivebeenthere"; - List! m_doomedCmds = new List(); - #endregion - /// - /// Constructor. Initializes the theorem prover. - /// - public DCGen(Program! program, string/*?*/ logFilePath, bool appendLogFile) - { - base(program); - this.appendLogFile = appendLogFile; - this.logFilePath = logFilePath; - m_BlockReachabilityMap = new Dictionary(); - } - - - private void Impl2Dot(Implementation! impl, string! filename) { - List! nodes = new List(); - List! edges = new List(); - - string nodestyle = "[shape=box];" ; - - foreach (Block! b in impl.Blocks) { - nodes.Add(string.Format("\"{0}\" {1}", b.Label, nodestyle) ); - GotoCmd gc = b.TransferCmd as GotoCmd; - if (gc!=null) { - assert gc.labelTargets!=null; - foreach (Block! b_ in gc.labelTargets) { - edges.Add(String.Format("\"{0}\" -> \"{1}\";", b.Label, b_.Label ) ); - } - } - } - - using (StreamWriter sw = new StreamWriter(filename)) - { - sw.WriteLine(String.Format("digraph {0} {{", impl.Name)); -// foreach (string! s in nodes) { -// sw.WriteLine(s); -// } - foreach (string! s in edges) { - sw.WriteLine(s); - } - sw.WriteLine("}}"); - sw.Close(); - } - } - - private const string _copyPrefix = "Yeah"; - - private Block! CopyImplBlocks(Block! b, ref List! blocklist, Block! targetBlock, ref Dictionary! alreadySeen) { - Block seen; - if (alreadySeen.TryGetValue(b, out seen) ) { - assert seen!=null; - return seen; - } - - GotoCmd gc = b.TransferCmd as GotoCmd; - TransferCmd tcmd = null; - if (gc!=null) { - BlockSeq! bseq = new BlockSeq(); - assert gc.labelTargets!=null; - foreach (Block! c in gc.labelTargets) { - bseq.Add(CopyImplBlocks(c, ref blocklist, targetBlock, ref alreadySeen)); - } - tcmd = new GotoCmd(gc.tok, bseq); - } else { -// BlockSeq! bseq_ = new BlockSeq(); -// bseq_.Add(targetBlock); - assert b.TransferCmd != null; -// tcmd = new GotoCmd(b.TransferCmd.tok, bseq_); - tcmd = new ReturnCmd(b.TransferCmd.tok); - } - - CodeCopier codeCopier = new CodeCopier(); - CmdSeq! cl = new CmdSeq(); - foreach (Cmd! _c in b.Cmds) { - if (!ContainsReachVariable(_c)) - cl.Add( codeCopier.CopyCmd(_c)); - } - - Block! b_ = new Block(b.tok, b.Label+_copyPrefix, cl, tcmd); - blocklist.Add(b_); - - alreadySeen[b] = b_; - - return b_; - } - - /* - After adding a copy of the implementation in front of our code - we remove all the edges leading from the copy to the original code - */ - private void RemoveArtificialGoto(Block! b, Block! target) { - GotoCmd gc = b.TransferCmd as GotoCmd; - - if (gc!=null) { - assert gc.labelTargets!=null; - foreach (Block! gt in gc.labelTargets) { - if (gt==target) { - assert gc.labelTargets.Length==1; - assert gc.labelTargets[0]!=null; - b.TransferCmd = new ReturnCmd(gc.tok); - return; - } else { - RemoveArtificialGoto(gt, target); - } - } - - } - } - - static public bool UseItAsDebugger = false; - -// public static Implementation _tmpImpl = null; // (MsSchaef) HACK! - - public static Block firstNonDebugBlock = null; - public static Block firstDebugBlock = null; - - private void ModifyImplForDebugging(Implementation! impl) - { - //List backup_blocks=null; - - - if (UseItAsDebugger) { - #region Copy the Implementation ///////////////////// - - ConsoleColor col = Console.ForegroundColor; - Console.ForegroundColor = ConsoleColor.Magenta; - Console.WriteLine("Warning you are using the Infinite Improbability Drive!"); - Console.ForegroundColor = col; - - List! blist = new List(); - Dictionary! tmpdict = new Dictionary(); - CopyImplBlocks(impl.Blocks[0], ref blist, impl.Blocks[0], ref tmpdict); - blist.Reverse(); - //_tmpImpl = new Implementation(impl.tok, impl.Name, impl.TypeParameters, impl.InParams, impl.OutParams, impl.LocVars, blist); - - #endregion //////////////////////////////////// - - #region Add implementation copy in front of implementation - // memorize where the original code starts - firstNonDebugBlock = impl.Blocks[0]; - firstDebugBlock = blist[0]; - // now add the copied program in front of the original one - blist.AddRange(impl.Blocks); - //backup_blocks = new List(impl.Blocks); - - BlockSeq! newbseq = new BlockSeq(); - newbseq.Add(firstNonDebugBlock); - newbseq.Add(firstDebugBlock); - - GotoCmd! newtcmd = new GotoCmd(Token.NoToken, newbseq); - Block! newfirst = new Block(Token.NoToken, "MySuperFirstBlock", new CmdSeq(), newtcmd); - - - impl.Blocks = new List(); - impl.Blocks.Add(newfirst); - impl.Blocks.AddRange(blist); - - //Impl2Dot(impl, String.Format("c:/dot/{0}_copied.dot", impl.Name) ); - #endregion - } - } - - void RemoveReachVars(Block! b) { - GotoCmd gc = b.TransferCmd as GotoCmd; - if (gc!=null) { - assert gc.labelTargets!=null; - - CmdSeq! cs = new CmdSeq(); - foreach (Cmd! c in b.Cmds) { - if (!ContainsReachVariable(c)) cs.Add(c); - } - b.Cmds = cs; - - foreach (Block! c in gc.labelTargets) { - if (c.Label != "GeneratedUnifiedExit") { - RemoveReachVars(c); - } - } - } - } - - void RemoveLastBlock(Block! b) - { - GotoCmd gc = b.TransferCmd as GotoCmd; - if (gc==null) { - //Console.WriteLine("WARNING: Check Node {0}", b.Label); - return; - } - assert gc!=null; - assert gc.labelTargets !=null; - BlockSeq! tmp = new BlockSeq(); - foreach (Block! c in gc.labelTargets) { - // Warning, we should not search by name! - if (c.Label != "GeneratedUnifiedExit" ) { - tmp.Add(c); - RemoveLastBlock(c); - } else { - c.Predecessors.Remove(b); - } - } - if (tmp.Length==0) { - b.TransferCmd = new ReturnCmd(gc.tok); - } else { - b.TransferCmd = new GotoCmd(gc.tok, tmp); - } - } - - void FindCopiedBlocks(Block! b) { - _copiedBlock.Add(b); - GotoCmd gc = b.TransferCmd as GotoCmd; - if (gc!=null) { - assert gc.labelTargets!=null; - foreach (Block! c in gc.labelTargets) { - FindCopiedBlocks(c); - } - } - } - - private List! _copiedBlock = new List(); - - /// - /// MSchaef: - /// - remove loops and add reach variables - /// - make it a passive program - /// - compute the wlp for each block - /// - check if |= (reach=false) => wlp.S.false holds for each reach - /// - /// - public override Outcome VerifyImplementation(Implementation! impl, Program! program, VerifierCallback! callback) - throws UnexpectedProverOutputException; - { - - UseItAsDebugger = CommandLineOptions.Clo.useDoomDebug; - Stopwatch watch = new Stopwatch(); - - //Impl2Dot(impl, String.Format("c:/dot/{0}_raw.dot", impl.Name) ); - - if (CommandLineOptions.Clo.TraceVerify) { - Console.WriteLine(">>> Checking function {0} for doomed points.", impl.Name); - } - Console.WriteLine("Checking function {0} for doomed points:", impl.Name); - callback.OnProgress("doomdetector",0,0,0); - - watch.Reset(); - watch.Start(); - - - - #region Transform the Program into loop-free passive form - variable2SequenceNumber = new Hashtable/*Variable -> int*/(); - incarnationOriginMap = new Dictionary(); - List! cblocks = new List(); - - //List! orig_blocks = new List(impl.Blocks); - - Dictionary copiedblocks; - impl.Blocks = DCProgramTransformer.Convert2Dag(impl, program, cblocks, out copiedblocks); - assert copiedblocks!=null; - -// List! blist = new List(); -// blist.AddRange(impl.Blocks); - - if (UseItAsDebugger) ModifyImplForDebugging(impl); - - ComputePredecessors(impl.Blocks); - - m_BlockReachabilityMap = new Dictionary(); - GenerateReachVars(impl); - - if (UseItAsDebugger) RemoveReachVars((!)firstDebugBlock); - - PassifyProgram(impl); - - - #endregion - //EmitImpl(impl,false); - - - //Impl2Dot(impl, String.Format("c:/dot/{0}_passive.dot", impl.Name) ); - -// --------------------------------------------------------------------------- - if (UseItAsDebugger) { - assert firstNonDebugBlock != null && firstDebugBlock != null; - firstNonDebugBlock.Predecessors.Remove(impl.Blocks[0]); - firstDebugBlock.Predecessors.Remove(impl.Blocks[0]); -// impl.Blocks.Remove(impl.Blocks[0]); // remove the artificial first block - RemoveLastBlock(firstDebugBlock); // remove the goto to the unified exit - _copiedBlock.Clear(); - FindCopiedBlocks(firstDebugBlock); - } -// --------------------------------------------------------------------------- - -// EmitImpl(impl,false); - - //Impl2Dot(impl, String.Format("c:/dot/{0}_final.dot", impl.Name) ); - - bool __debug = false; - - watch.Stop(); - if (__debug) Console.WriteLine("Transformation takes: {0}", watch.Elapsed.ToString() ); - watch.Reset(); - - Checker! checker = FindCheckerFor(impl, 1000); - - DoomCheck dc = new DoomCheck(impl, checker); - - int _totalchecks = 0; - Block b = null; - ProverInterface.Outcome outcome; - dc.ErrorHandler = new DoomErrorHandler(dc.Label2Absy, callback); - - System.TimeSpan ts = watch.Elapsed; - - while (dc.GetNextBlock(out b) ) { - assert b!=null; - outcome = ProverInterface.Outcome.Undetermined; - //Console.WriteLine("Checking block {0} ...",b.Label); - Variable v = null; - m_BlockReachabilityMap.TryGetValue(b, out v); - assert v!=null; - _totalchecks++; - - - watch.Start(); - if (!dc.CheckLabel(v, out outcome) ) { - return Outcome.Inconclusive; - } - watch.Stop(); - ts+=watch.Elapsed; - if (__debug) Console.WriteLine(" Time for Block {0}: {1} elapsed",b.Label, watch.Elapsed.ToString()); - watch.Reset(); - - - switch (outcome) { - case ProverInterface.Outcome.Valid: { - break; - } - case ProverInterface.Outcome.Invalid: { - - break; - } - default: { - - break; - } - } - - } - checker.Close(); - - if (__debug) Console.WriteLine("Number of Checked Blocks: {0} of {1}", _totalchecks, impl.Blocks.Count ); - if (__debug) Console.WriteLine("Total time for this method: {0}", ts.ToString()); - - #region Try to produce a counter example (brute force) - if (dc.DoomedSequences.Count>0 ) { - ConsoleColor col = Console.ForegroundColor; -// Console.ForegroundColor = ConsoleColor.Red; -// Console.WriteLine(" {0} is DOOMED!", impl.Name); -// foreach (List bl in dc.DoomedSequences) { -// Console.Write("Doomed Blocks: "); -// foreach (Block! b_ in bl) { -// Console.Write("{0}, ", b_.Label); -// } -// Console.WriteLine(); -// } - Console.ForegroundColor = col; - - int counter=1; - foreach (List! bl in dc.DoomedSequences) { - Console.WriteLine("Doomed program point {0} of {1}", counter++, dc.DoomedSequences.Count); - dc.ErrorHandler.m_DoomedBlocks = bl; - foreach (Block! b_ in bl) { - if (m_BlockReachabilityMap.TryGetValue(b_, out dc.ErrorHandler.m_Reachvar) )break; - } - SearchCounterexample(impl, dc.ErrorHandler, callback); - } - - //SearchCounterexample(impl, dc.ErrorHandler, callback); - Console.WriteLine("------------------------------ \n\n"); - return Outcome.Errors; - } - #endregion - - Console.WriteLine("------------------------------ \n\n"); - - return Outcome.Correct; - } - - - - private void SearchCounterexample(Implementation! impl, DoomErrorHandler! errh, VerifierCallback! callback) { - if (errh.m_Reachvar==null) { - assert false; - } - m_doomedCmds.Clear(); - - Dictionary! cmdbackup = new Dictionary(); - - BruteForceCESearch(errh.m_Reachvar, impl, callback, cmdbackup, 0, impl.Blocks.Count/2-1); - BruteForceCESearch(errh.m_Reachvar, impl, callback, cmdbackup, impl.Blocks.Count/2,impl.Blocks.Count-1); - - List! causals = CollectCausalStatements(impl.Blocks[0]); - foreach (Cmd! c in causals ) { - GenerateErrorMessage(c, causals); - } - - #region Undo all modifications - foreach (KeyValuePair kvp in cmdbackup) { - kvp.Key.Cmds = kvp.Value; - } - #endregion - } - -#region Causal Statement Tree - - private void GenerateErrorMessage(Cmd! causalStatement, List! causals) - { - AssumeCmd uc = causalStatement as AssumeCmd; - AssertCmd ac = causalStatement as AssertCmd; - ConsoleColor col = Console.ForegroundColor; - - // Trivial case. Must be either assume or assert false - if (m_doomedCmds.Count==1) { - Console.WriteLine("Found a trivial error:" ); - if (uc!=null) { - Console.Write("Trivial false assumption: " ); - Console.Write("({0};{1}):", uc.tok.line, uc.tok.col ); - } - if (ac!=null) { - Console.Write("Trivial false assertion: " ); - Console.Write("({0};{1}):", ac.tok.line, ac.tok.col ); - } - causalStatement.Emit(new TokenTextWriter("", Console.Out, false), 0); - } else { - // Safety error - if (ac!=null) { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("Safety error:"); - Console.ForegroundColor = col; - Console.Write("This assertion is violated: "); - Console.Write("({0};{1}):", ac.tok.line, ac.tok.col ); - ac.Emit(new TokenTextWriter("", Console.Out, false), 0); - } - if (uc!=null) { - bool containsAssert = false; - foreach (Cmd! c in m_doomedCmds) { - if (causals.Contains(c) ) { - continue; - } - AssertCmd asrt = c as AssertCmd; - if (asrt!=null) { - containsAssert=true; - break; - } - } - // Plausibility error - if (containsAssert) { - Console.ForegroundColor = ConsoleColor.Yellow ; - Console.WriteLine("Plausibility error:"); - Console.ForegroundColor = col; - Console.Write("There is no legal exeuction passing: "); - Console.Write("({0};{1})", uc.tok.line, uc.tok.col ); - uc.Emit(new TokenTextWriter("", Console.Out, false), 0); - } else { // Reachability error - Console.ForegroundColor = ConsoleColor.DarkRed ; - Console.WriteLine("Reachability error:"); - Console.ForegroundColor = col; - Console.Write("No execution can reach: "); - Console.Write("({0};{1})", uc.tok.line, uc.tok.col ); - uc.Emit(new TokenTextWriter("", Console.Out, false), 0); - } - - } - - Console.ForegroundColor = ConsoleColor.White; - Console.WriteLine("...on any execution passing through:"); - foreach (Cmd! c in m_doomedCmds) { - if (causals.Contains(c) ) { - continue; - } - Console.ForegroundColor = col; - Console.Write("In ({0};{1}): ", c.tok.line, c.tok.col ); - Console.ForegroundColor = ConsoleColor.DarkYellow ; - c.Emit(new TokenTextWriter("", Console.Out, false), 0); - } - Console.ForegroundColor = col; - Console.WriteLine("--"); - - } - } - - private List! CollectCausalStatements(Block! b) { - Cmd lastCausal=null; - foreach (Cmd! c in b.Cmds) { - AssertCmd ac = c as AssertCmd; - AssumeCmd uc = c as AssumeCmd; - if (ac!=null && !ContainsReachVariable(ac)) { - if (ac.Expr!=Expr.True) { - lastCausal = c; - } - } else if (uc!=null && !ContainsReachVariable(uc)) { - lastCausal = c; - } - } - - List causals = new List(); - GotoCmd gc = b.TransferCmd as GotoCmd; - if (gc!=null && gc.labelTargets!=null) { - List tmp; - //bool allcausal = true; - foreach (Block! b_ in gc.labelTargets) { - tmp = CollectCausalStatements(b_); - - foreach (Cmd cau in tmp) { - if (!causals.Contains(cau)) causals.Add(cau); - } - } - //if (allcausal) - if (causals.Count>0) return causals; - } - if (lastCausal!=null) causals.Add( lastCausal); - return causals; - } - -#endregion - - bool BruteForceCESearch(Variable! reachvar, Implementation! impl, VerifierCallback! callback, - Dictionary! cmdbackup, int startidx, int endidx) { - #region Modify implementation - for (int i=startidx; i<=endidx; i++) { - if (_copiedBlock.Contains(impl.Blocks[i])) continue; - CmdSeq! cs = new CmdSeq(); - cmdbackup.Add(impl.Blocks[i],impl.Blocks[i].Cmds); - foreach (Cmd! c in impl.Blocks[i].Cmds) { - if (ContainsReachVariable(c)) { - cs.Add(c); - continue; - } - AssertCmd ac = c as AssertCmd; - AssumeCmd uc = c as AssumeCmd; - if (ac!=null) { - cs.Add(new AssertCmd(ac.tok, Expr.True) ); - } else if (uc!=null) { - cs.Add(new AssertCmd(uc.tok, Expr.True) ); - } else { - cs.Add(c); - } - } - impl.Blocks[i].Cmds = cs; - } - #endregion - - ProverInterface.Outcome outcome = ProverInterface.Outcome.Undetermined; - if (!ReCheckImpl(reachvar, impl, callback, out outcome) ) { - UndoBlockModifications(impl, cmdbackup, startidx, endidx); - return false; - } - if (outcome == ProverInterface.Outcome.Valid) { - return true; - } else if (outcome == ProverInterface.Outcome.Invalid) { - UndoBlockModifications(impl, cmdbackup, startidx, endidx); - int mid = startidx + (endidx-startidx)/2; - if (startidx>=endidx) { - // Now we found an interesting Block and we have to - // search for the interesting statements. - int cmdcount = impl.Blocks[endidx].Cmds.Length; - BruteForceCmd(impl.Blocks[endidx],0,cmdcount/2 -1,reachvar, impl, callback); - BruteForceCmd(impl.Blocks[endidx],cmdcount/2,cmdcount-1,reachvar, impl, callback); - return true; - } else { - BruteForceCESearch(reachvar,impl, callback, cmdbackup, startidx, mid); - BruteForceCESearch(reachvar,impl, callback, cmdbackup, mid+1, endidx); - return true; - } - } else { - UndoBlockModifications(impl, cmdbackup, startidx, endidx); - return false; - } - } - - bool BruteForceCmd(Block! b, int startidx, int endidx, Variable! reachvar, - Implementation! impl, VerifierCallback! callback) { - #region Modify Cmds - CmdSeq! backup = b.Cmds; - CmdSeq! cs = new CmdSeq(); - for (int i=0;i=endidx) { - if (!ContainsReachVariable(b.Cmds[endidx])) { -// Console.Write(" Witness ("); -// -// ConsoleColor col = Console.ForegroundColor; -// Console.ForegroundColor = ConsoleColor.White; -// Console.Write("{0};{1}", b.Cmds[endidx].tok.line, b.Cmds[endidx].tok.col ); -// Console.ForegroundColor = col; -// Console.Write("): "); -// Console.ForegroundColor = ConsoleColor.Yellow; -// b.Cmds[endidx].Emit(new TokenTextWriter("", Console.Out, false), 0); -// Console.ForegroundColor = col; - - m_doomedCmds.Add(b.Cmds[endidx]); - return true; - } else { - return false; - } - } else { - int mid = startidx + (endidx-startidx)/2; - BruteForceCmd(b, startidx, mid, reachvar, impl, callback); - BruteForceCmd(b, mid+1, endidx, reachvar, impl, callback); - return false; // This is pure random - } - } else { - b.Cmds = backup; - return false; - } - - return false; - } - - void UndoBlockModifications(Implementation! impl, Dictionary! cmdbackup, - int startidx, int endidx) { - for (int i=startidx; i<=endidx; i++) { - CmdSeq cs = null; - if (cmdbackup.TryGetValue(impl.Blocks[i], out cs) ) { - assert cs!=null; - impl.Blocks[i].Cmds = cs; - cmdbackup.Remove(impl.Blocks[i]); - } - } - } - - bool ReCheckImpl(Variable! reachvar, Implementation! impl, VerifierCallback! callback, - out ProverInterface.Outcome outcome) { - Checker! checker = FindCheckerFor(impl, 1000); - DoomCheck dc = new DoomCheck(impl, checker); - dc.ErrorHandler = new DoomErrorHandler(dc.Label2Absy, callback); - outcome = ProverInterface.Outcome.Undetermined; - if (!dc.CheckLabel(reachvar, out outcome)) { - checker.Close(); - return false; - } - checker.Close(); - return true; - } - - - - bool ContainsReachVariable(Cmd! c) { - AssertCmd artc = c as AssertCmd; - AssumeCmd amec = c as AssumeCmd; - Expr e; - if (artc!=null) { - e = artc.Expr; - } else if (amec!=null) { - e = amec.Expr; - } else { - return false; - } - Set! freevars = new Set(); - e.ComputeFreeVariables(freevars); - foreach (Variable! v in freevars) { - if (v.Name.Contains(reachvarsuffix)) return true; - } - return false; - } - - - #region Loop Removal - /// - /// This class is accessed only via the static method Convert2Dag - /// It converts the program into a loopfree one by unrolling the loop threetimes and adding the appropriate havoc - /// statements. The first and the last unrolling represent the first and last iteration of the loop. The second - /// unrolling stands for any other iteration. - /// - private class DCProgramTransformer - { - private List! Blocks; - private List! m_checkableBlocks; - private Dictionary! m_copyMap = new Dictionary(); - - public static List! Convert2Dag(Implementation! impl, Program! program, List! checkableBlocks, - out Dictionary copiedblocks) - { - Block! start = impl.Blocks[0]; - Dictionary gd = new Dictionary(); - Set/*Block*/! beingVisited = new Set/*Block*/(); - GraphNode gStart = GraphNode.ComputeGraphInfo(null, start, gd, beingVisited); - - DCProgramTransformer pt = new DCProgramTransformer(checkableBlocks); - pt.LoopUnrolling(gStart, new Dictionary(), true, ""); - pt.Blocks.Reverse(); - copiedblocks = pt.m_copyMap; - return pt.Blocks; - } - - - DCProgramTransformer(List! checkableBlocks) - { - Blocks = new List(); - m_checkableBlocks = checkableBlocks; - } - - -#region Loop Unrolling Methods - - private Block! LoopUnrolling(GraphNode! node, Dictionary! visited, bool unrollable, String! prefix) - { - Block newb; - if (visited.TryGetValue(node, out newb)) - { - assert newb!=null; - return newb; - } else - { - if (node.IsCutPoint) - { - // compute the loop body and the blocks leaving the loop - - List! loopNodes = new List(); - GatherLoopBodyNodes(node, node, loopNodes); - - List! exitNodes = GatherLoopExitNodes(loopNodes); - - // Continue Unrolling after the current loop - Dictionary! _visited = new Dictionary(); - foreach (GraphNode! g in exitNodes) - { - Block b = LoopUnrolling(g, visited, unrollable, prefix); - _visited.Add(g,b); - } - newb = UnrollCurrentLoop(node, _visited, loopNodes,unrollable, prefix); - visited.Add(node,newb); - } else - { - BlockSeq! newSuccs = new BlockSeq(); - foreach(GraphNode! g in node.Succecessors) - { - newSuccs.Add( LoopUnrolling(g,visited,unrollable,prefix) ); - } - newb = new Block(node.Block.tok, node.Block.Label + prefix , node.Body, node.Block.TransferCmd); - Block b; - if (m_copyMap.TryGetValue(node.Block, out b) ) { - assert b!=null; - m_copyMap.Add(newb, b); - } else { - m_copyMap.Add(newb, node.Block); - } - - - assert newb!=null; assert newb.TransferCmd!=null; - if (newSuccs.Length == 0) - newb.TransferCmd = new ReturnCmd(newb.TransferCmd.tok); - else - newb.TransferCmd = new GotoCmd(newb.TransferCmd.tok, newSuccs); - - visited.Add(node, newb); - Blocks.Add(newb); - if (unrollable) - { - m_checkableBlocks.Add(newb); - } - } - } - assert newb!=null; - //newb.checkable = unrollable; - return newb; - } - - private Block! UnrollCurrentLoop(GraphNode! cutPoint, Dictionary! visited, - List! loopNodes, bool unrollable, String! prefix) - { - if (unrollable) - { - Dictionary! visited1 = new Dictionary(visited); - Dictionary! visited2 = new Dictionary(visited); - Dictionary! visited3 = new Dictionary(visited); - - Block! loopend = ConstructLoopExitBlock(cutPoint, loopNodes, visited, prefix+"#Last"); - - Block! last = UnrollOnce(cutPoint, loopend,visited1,false, prefix+"#Last"); - AddHavocCmd(last,loopNodes); - - // You might use true for the unrollable flag as well. - Block! arb = UnrollOnce(cutPoint, last,visited2,false, prefix+"#Arb"); - AddHavocCmd(arb,loopNodes); - - - BlockSeq! succ = new BlockSeq(); - succ.Add(last); succ.Add(arb); - assert arb.TransferCmd!=null; - Block! tmp = new Block(arb.tok, arb.Label + prefix+"#Dummy" , new CmdSeq(), new GotoCmd(arb.TransferCmd.tok, succ)); - Blocks.Add(tmp); - m_checkableBlocks.Add(tmp); - - // check if arb is already a copy of something else - // if not then write to m_copyMap that tmp is a copy - // of arb - Block b = null; - if (m_copyMap.TryGetValue(arb,out b) ) { - assert b!=null; - m_copyMap.Add(tmp, b); - } else { - m_copyMap.Add(tmp, arb); - } - - Block! first = UnrollOnce(cutPoint, tmp,visited3,false, prefix+"#First"); - - return first; - - } else - { - Dictionary! visited_ = new Dictionary(visited); - Block! loopend = AbstractIteration(cutPoint, prefix+"#UR"); - Block! ret = UnrollOnce(cutPoint, loopend,visited_,false, prefix); - AddHavocCmd(ret, loopNodes); - return ret; - } - } - - private Block! UnrollOnce(GraphNode! node, Block! nextIter, Dictionary! visited, bool unrollable, String! prefix) - { - visited.Add(node, nextIter); - Block newb,b; - BlockSeq! newSuccs = new BlockSeq(); - foreach(GraphNode! g in node.Succecessors) - { - newSuccs.Add( LoopUnrolling(g,visited,unrollable,prefix) ); - } - newb = new Block(node.Block.tok, node.Block.Label + prefix , node.Body, node.Block.TransferCmd); - if (m_copyMap.TryGetValue(node.Block, out b) ) { - assert b!=null; - m_copyMap.Add(newb, b); - } else { - m_copyMap.Add(newb, node.Block); - } - - assert newb!=null; assert newb.TransferCmd!=null; - if (newSuccs.Length == 0) - newb.TransferCmd = new ReturnCmd(newb.TransferCmd.tok); - else - newb.TransferCmd = new GotoCmd(newb.TransferCmd.tok, newSuccs); - - Blocks.Add(newb); - if (unrollable) m_checkableBlocks.Add(newb); - return newb; - } - - private Block! AbstractIteration(GraphNode! node, String! prefix) - { - CmdSeq body = new CmdSeq(); - foreach (Cmd! c in node.Body) - { - if (c is PredicateCmd || c is CommentCmd) - body.Add(c ); - else - break; - } - body.Add(new AssumeCmd(node.Block.tok, Expr.False) ); - TransferCmd! tcmd = new ReturnCmd(node.Block.tok); - Block! b = new Block(node.Block.tok, node.Block.Label + prefix, body, tcmd); - Blocks.Add(b); - Block tmp; - if (m_copyMap.TryGetValue(node.Block, out tmp) ) { - assert tmp!=null; - m_copyMap.Add(b, tmp); - } else { - m_copyMap.Add(b, node.Block); - } - - return b; - } - - private Block! ConstructLoopExitBlock(GraphNode! cutPoint, List! loopNodes, - Dictionary! visited, String! prefix) - { - BlockSeq! newSucc = new BlockSeq(); - Block! orig = cutPoint.Block; - - // detect the block after the loop - // FixMe: What happens when using break commands? - foreach (GraphNode! g in cutPoint.Succecessors) - { - if (!loopNodes.Contains(g)) - { - Block b; - if (visited.TryGetValue(g,out b) ) - newSucc.Add(b); - } - } - TransferCmd tcmd; - assert orig.TransferCmd!=null; - if (newSucc.Length==0) - tcmd = new ReturnCmd(orig.TransferCmd.tok); - else - tcmd = new GotoCmd(orig.TransferCmd.tok, newSucc); - // FixMe: Genertate IToken for counterexample creation - Block! newb = new Block(orig.tok, orig.Label+prefix+"#Leave", orig.Cmds, tcmd); - Blocks.Add(newb); - m_checkableBlocks.Add(newb); - return newb; - } - - - private void GatherLoopBodyNodes(GraphNode! current, GraphNode! cutPoint, List! loopNodes) - { - loopNodes.Add(current); - if (false) System.Diagnostics.Debugger.Break(); - foreach (GraphNode! g in current.Predecessors) - { - if (cutPoint.firstPredecessor == g || g == cutPoint || loopNodes.Contains(g) ) continue; - GatherLoopBodyNodes(g, cutPoint, loopNodes); - } - } - - private List! GatherLoopExitNodes(List! loopNodes) - { - List! exitnodes = new List(); - - foreach (GraphNode! g in loopNodes) - { - foreach (GraphNode! s in g.Succecessors) - { - if (!loopNodes.Contains(s) /*&& !exitnodes.Contains(s)*/ ) exitnodes.Add(s); - } - } - return exitnodes; - } - - private void AddHavocCmd(Block! b, List! loopNodes) - { - List! loopBlocks = new List(); - foreach (GraphNode! g in loopNodes) loopBlocks.Add(g.Block); - HavocCmd! hcmd = HavocLoopTargets(loopBlocks,b.tok); - CmdSeq! body = new CmdSeq(); - body.Add(hcmd); - body.AddRange(b.Cmds); - b.Cmds = body; - } - - private HavocCmd! HavocLoopTargets(List! bl, IToken! tok) - { - VariableSeq varsToHavoc = new VariableSeq(); - foreach ( Block! b in bl ) - { - foreach ( Cmd! c in b.Cmds ) - { - c.AddAssignedVariables(varsToHavoc); - } - } - IdentifierExprSeq havocExprs = new IdentifierExprSeq(); - foreach ( Variable! v in varsToHavoc ) - { - IdentifierExpr ie = new IdentifierExpr(Token.NoToken, v); - if(!havocExprs.Has(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 - return new HavocCmd(tok,havocExprs); - } - - #endregion - - - #region GraphNode - private class GraphNode - { - public readonly Block! Block; - public readonly CmdSeq! Body; - public bool IsCutPoint; // is set during ComputeGraphInfo - [Rep] public readonly List! Predecessors = new List(); - [Rep] public readonly List! Succecessors = new List(); - public GraphNode firstPredecessor; - public List! UnavoidableNodes = new List(); // should be done using a set - - GraphNode(Block! b, CmdSeq! body) - { - Block = b; Body = body; - IsCutPoint = false; - - } - - static CmdSeq! GetOptimizedBody(CmdSeq! cmds) - { - int n = 0; - foreach (Cmd c in cmds) - { - n++; - PredicateCmd pc = c as PredicateCmd; - if (pc != null && pc.Expr is LiteralExpr && ((LiteralExpr)pc.Expr).IsFalse) - { - // return a sequence consisting of the commands seen so far - Cmd[] s = new Cmd[n]; - for (int i = 0; i < n; i++) - { - s[i] = cmds[i]; - } - return new CmdSeq(s); - } - } - return cmds; - } - - private static List! Intersect(List! left, List! right) - { - List! ret = new List(); - List! tmp = left; - tmp.AddRange(right); - foreach (GraphNode! gn in tmp) { - if (ret.Contains(gn) ) continue; - if (left.Contains(gn) && right.Contains(gn)) ret.Add(gn); - } - return ret; - } - - public static GraphNode! ComputeGraphInfo(GraphNode from, Block! b, Dictionary! gd, Set /*Block*/! beingVisited) - { - GraphNode g; - if (gd.TryGetValue(b, out g)) - { - assume from != null; - assert g != null; - - g.UnavoidableNodes = Intersect(g.UnavoidableNodes, from.UnavoidableNodes); - if (!g.UnavoidableNodes.Contains(g)) g.UnavoidableNodes.Add(g); - - g.Predecessors.Add(from); - if (g.firstPredecessor==null) - g.firstPredecessor = from; - - if (beingVisited.Contains(b)) - g.IsCutPoint = true; // it's a cut point - } else - { - CmdSeq body = GetOptimizedBody(b.Cmds); - g = new GraphNode(b, body); - gd.Add(b, g); - if (from != null) - { - g.Predecessors.Add(from); - if (from==null) - g.firstPredecessor = g; - - if (g.firstPredecessor==null) - g.firstPredecessor = from; - - } - if (body != b.Cmds) - { - // the body was optimized -- there is no way through this block - } else - { - beingVisited.Add(b); - GotoCmd gcmd = b.TransferCmd as GotoCmd; - if (gcmd != null) - { - assume gcmd.labelTargets != null; - foreach (Block! succ in gcmd.labelTargets) - { - g.Succecessors.Add( ComputeGraphInfo(g, succ, gd, beingVisited) ); - } - } - beingVisited.Remove(b); - } - } - return g; - } - } - - } - #endregion - - #endregion - - #region Program Passification - private void GenerateReachVars(Implementation! impl) - { - Hashtable gotoCmdOrigins = new Hashtable(); - Block! exitBlock = GenerateUnifiedExit(impl, gotoCmdOrigins); - AddBlocksBetween(impl); - - #region Insert pre- and post-conditions and where clauses as assume and assert statements - { - CmdSeq cc = new CmdSeq(); - // where clauses of global variables - foreach (Declaration d in program.TopLevelDeclarations) { - GlobalVariable gvar = d as GlobalVariable; - 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) { - if (lvar.TypedIdent.WhereExpr != null) { - Cmd c = new AssumeCmd(lvar.tok, lvar.TypedIdent.WhereExpr); - cc.Add(c); - } - } - - // 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 - exitBlock = InjectPostConditions(impl,exitBlock,gotoCmdOrigins); - } - #endregion - GenerateReachabilityPredicates(impl, exitBlock); - } - - - private Hashtable/*TransferCmd->ReturnCmd*/! PassifyProgram(Implementation! impl) - { - current_impl = impl; - Convert2PassiveCmd(impl); - impl = current_impl; - return new Hashtable(); - } - - /// - /// Add additional variable to allow checking as described in the paper - /// "It's doomed; we can prove it" - /// - private void GenerateReachabilityPredicates(Implementation! impl, Block! exitBlock) - { - ExprSeq! es = new ExprSeq(); - Cmd eblockcond = null; - - foreach (Block! b in impl.Blocks) - { - //if (b.Predecessors.Length==0) continue; - //if (b.Cmds.Length == 0 ) continue; - - Variable v_ = new LocalVariable(Token.NoToken, - new TypedIdent(b.tok, b.Label+reachvarsuffix,new BasicType(SimpleType.Bool) ) ); - - impl.LocVars.Add(v_); - - m_BlockReachabilityMap[b] = v_; - - IdentifierExpr! lhs = new IdentifierExpr(b.tok, v_); - - es.Add( new IdentifierExpr(b.tok, v_) ); - - List! lhsl = new List(); - lhsl.Add(new SimpleAssignLhs(Token.NoToken, lhs) ); - List! rhsl = new List(); - rhsl.Add(Expr.True); - - if (b!=exitBlock) - { - CmdSeq cs = new CmdSeq(new AssignCmd(Token.NoToken, lhsl, rhsl)); - cs.AddRange(b.Cmds); - b.Cmds = cs; - } else - { - eblockcond = new AssignCmd(Token.NoToken, lhsl, rhsl); - } - - //checkBlocks.Add(new CheckableBlock(v_,b)); - } - if (es.Length==0) return; - - Expr aexp = null; - - if (es.Length==1) - { - aexp = es[0]; - } else if (es.Length==2) - { - aexp = Expr.Binary(Token.NoToken, - BinaryOperator.Opcode.And, - (!)es[0], - (!)es[1]); - } else - { - aexp = Expr.True; - foreach (Expr e_ in es) - { - aexp = Expr.Binary(Token.NoToken, - BinaryOperator.Opcode.And, - (!)e_, aexp); - } - } - assert (aexp!=null); - assert (eblockcond!=null); - - AssumeCmd ac = new AssumeCmd(Token.NoToken, aexp); - - assert(exitBlock!=null); - - CmdSeq cseq = new CmdSeq(eblockcond); - cseq.AddRange(exitBlock.Cmds); - cseq.Add(ac); - - exitBlock.Cmds = cseq; - } - - #endregion - - } -} diff --git a/Source/VCGeneration/VCGeneration.csproj b/Source/VCGeneration/VCGeneration.csproj new file mode 100644 index 00000000..768c44bc --- /dev/null +++ b/Source/VCGeneration/VCGeneration.csproj @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/VCGeneration/VCGeneration.sscproj b/Source/VCGeneration/VCGeneration.sscproj deleted file mode 100644 index 768c44bc..00000000 --- a/Source/VCGeneration/VCGeneration.sscproj +++ /dev/null @@ -1,154 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Source/VCGeneration/Wlp.cs b/Source/VCGeneration/Wlp.cs new file mode 100644 index 00000000..be99dd6d --- /dev/null +++ b/Source/VCGeneration/Wlp.cs @@ -0,0 +1,184 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) Microsoft Corporation. All Rights Reserved. +// +//----------------------------------------------------------------------------- +using System; +using System.Collections; +using Microsoft.Boogie; +using Microsoft.Boogie.VCExprAST; +using Microsoft.Contracts; +using System.Collections.Generic; +using Microsoft.Basetypes; + +namespace VC { + public class VCContext + { + [Rep] public readonly Hashtable/**/! Label2absy; + [Rep] public readonly ProverContext! Ctxt; + public readonly Variable ControlFlowVariable; + + public VCContext(Hashtable/**/! label2absy, ProverContext! ctxt) + { + this.Label2absy = label2absy; + this.Ctxt = ctxt; + // base(); + } + + public VCContext(Hashtable/**/! label2absy, ProverContext! ctxt, Variable controlFlowVariable) + { + this.Label2absy = label2absy; + this.Ctxt = ctxt; + this.ControlFlowVariable = controlFlowVariable; + } + + } + + #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.*; + { + VCExpressionGenerator! gen = ctxt.Ctxt.ExprGen; + VCExpr! res = N; + + for (int i = b.Cmds.Length; --i>=0; ) + { + res = Cmd((!) b.Cmds[i], res, ctxt); + } + + int id = b.UniqueId; + expose (ctxt) { + ctxt.Label2absy[id] = b; + if (ctxt.ControlFlowVariable != null) + return res; + else + return gen.Implies(gen.LabelPos((!)id.ToString(), VCExpressionGenerator.True), res); + } + } + + /// + /// Computes the wlp for an assert or assume command "cmd". + /// + public static VCExpr! Cmd(Cmd! cmd, VCExpr! N, VCContext! ctxt) + { + VCExpressionGenerator! gen = ctxt.Ctxt.ExprGen; + if (cmd is AssertCmd) { + AssertCmd ac = (AssertCmd)cmd; + VCExpr C = ctxt.Ctxt.BoogieExprTranslator.Translate(ac.Expr); + if (CommandLineOptions.Clo.vcVariety == CommandLineOptions.VCVariety.Doomed) { + return gen.Implies(C, N); + } else { + int id = ac.UniqueId; + ctxt.Label2absy[id] = ac; + switch (Subsumption(ac)) { + case CommandLineOptions.SubsumptionOption.Never: + break; + case CommandLineOptions.SubsumptionOption.Always: + N = gen.Implies(C, N); + break; + case CommandLineOptions.SubsumptionOption.NotForQuantifiers: + if (!(C is VCExprQuantifier)) { + N = gen.Implies(C, N); + } + break; + default: + assert false; // unexpected case + } + + // (MSchaef) Hack: This line might be useless, but at least it is not harmfull + // need to test it + if (CommandLineOptions.Clo.vcVariety == CommandLineOptions.VCVariety.Doomed) + return gen.Implies(C, N); + + if (ctxt.ControlFlowVariable != null) + { + VCExpr! controlFlowVariableExpr = + ctxt.Ctxt.BoogieExprTranslator.LookupVariable(ctxt.ControlFlowVariable); + VCExpr! controlFlowFunctionAppl1 = + gen.ControlFlowFunctionApplication(controlFlowVariableExpr, gen.Integer(BigNum.FromInt(id))); + VCExpr! controlFlowFunctionAppl2 = + gen.ControlFlowFunctionApplication(controlFlowVariableExpr, gen.Integer(BigNum.FromInt(id))); + VCExpr! assertFailure = gen.Eq(controlFlowFunctionAppl1, gen.Integer(BigNum.FromInt(0))); + VCExpr! assertSuccess = gen.Neq(controlFlowFunctionAppl2, gen.Integer(BigNum.FromInt(0))); + return gen.And(gen.Implies(assertFailure, C), gen.Implies(assertSuccess, N)); + } + else + return gen.AndSimp(gen.LabelNeg((!)id.ToString(), C), N); + } + + } 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((!)"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()); + assert false; // unexpected command + } + } + + public static CommandLineOptions.SubsumptionOption Subsumption(AssertCmd! ac) { + 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) + { + 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.Length==0) + { + res = N; + } + else + { + VCExpr currentWLP = RegExpr((!)ch.rs[0], N, ctxt); + for (int i = 1, n = ch.rs.Length; i < n; i++) + { + currentWLP = ctxt.Ctxt.ExprGen.And(currentWLP, RegExpr((!)ch.rs[i], N, ctxt)); + } + res = currentWLP; + } + return res; + } + else + { + assert false; // unexpected RE subtype + } + } + } + #endregion + +} diff --git a/Source/VCGeneration/Wlp.ssc b/Source/VCGeneration/Wlp.ssc deleted file mode 100644 index be99dd6d..00000000 --- a/Source/VCGeneration/Wlp.ssc +++ /dev/null @@ -1,184 +0,0 @@ -//----------------------------------------------------------------------------- -// -// Copyright (C) Microsoft Corporation. All Rights Reserved. -// -//----------------------------------------------------------------------------- -using System; -using System.Collections; -using Microsoft.Boogie; -using Microsoft.Boogie.VCExprAST; -using Microsoft.Contracts; -using System.Collections.Generic; -using Microsoft.Basetypes; - -namespace VC { - public class VCContext - { - [Rep] public readonly Hashtable/**/! Label2absy; - [Rep] public readonly ProverContext! Ctxt; - public readonly Variable ControlFlowVariable; - - public VCContext(Hashtable/**/! label2absy, ProverContext! ctxt) - { - this.Label2absy = label2absy; - this.Ctxt = ctxt; - // base(); - } - - public VCContext(Hashtable/**/! label2absy, ProverContext! ctxt, Variable controlFlowVariable) - { - this.Label2absy = label2absy; - this.Ctxt = ctxt; - this.ControlFlowVariable = controlFlowVariable; - } - - } - - #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.*; - { - VCExpressionGenerator! gen = ctxt.Ctxt.ExprGen; - VCExpr! res = N; - - for (int i = b.Cmds.Length; --i>=0; ) - { - res = Cmd((!) b.Cmds[i], res, ctxt); - } - - int id = b.UniqueId; - expose (ctxt) { - ctxt.Label2absy[id] = b; - if (ctxt.ControlFlowVariable != null) - return res; - else - return gen.Implies(gen.LabelPos((!)id.ToString(), VCExpressionGenerator.True), res); - } - } - - /// - /// Computes the wlp for an assert or assume command "cmd". - /// - public static VCExpr! Cmd(Cmd! cmd, VCExpr! N, VCContext! ctxt) - { - VCExpressionGenerator! gen = ctxt.Ctxt.ExprGen; - if (cmd is AssertCmd) { - AssertCmd ac = (AssertCmd)cmd; - VCExpr C = ctxt.Ctxt.BoogieExprTranslator.Translate(ac.Expr); - if (CommandLineOptions.Clo.vcVariety == CommandLineOptions.VCVariety.Doomed) { - return gen.Implies(C, N); - } else { - int id = ac.UniqueId; - ctxt.Label2absy[id] = ac; - switch (Subsumption(ac)) { - case CommandLineOptions.SubsumptionOption.Never: - break; - case CommandLineOptions.SubsumptionOption.Always: - N = gen.Implies(C, N); - break; - case CommandLineOptions.SubsumptionOption.NotForQuantifiers: - if (!(C is VCExprQuantifier)) { - N = gen.Implies(C, N); - } - break; - default: - assert false; // unexpected case - } - - // (MSchaef) Hack: This line might be useless, but at least it is not harmfull - // need to test it - if (CommandLineOptions.Clo.vcVariety == CommandLineOptions.VCVariety.Doomed) - return gen.Implies(C, N); - - if (ctxt.ControlFlowVariable != null) - { - VCExpr! controlFlowVariableExpr = - ctxt.Ctxt.BoogieExprTranslator.LookupVariable(ctxt.ControlFlowVariable); - VCExpr! controlFlowFunctionAppl1 = - gen.ControlFlowFunctionApplication(controlFlowVariableExpr, gen.Integer(BigNum.FromInt(id))); - VCExpr! controlFlowFunctionAppl2 = - gen.ControlFlowFunctionApplication(controlFlowVariableExpr, gen.Integer(BigNum.FromInt(id))); - VCExpr! assertFailure = gen.Eq(controlFlowFunctionAppl1, gen.Integer(BigNum.FromInt(0))); - VCExpr! assertSuccess = gen.Neq(controlFlowFunctionAppl2, gen.Integer(BigNum.FromInt(0))); - return gen.And(gen.Implies(assertFailure, C), gen.Implies(assertSuccess, N)); - } - else - return gen.AndSimp(gen.LabelNeg((!)id.ToString(), C), N); - } - - } 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((!)"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()); - assert false; // unexpected command - } - } - - public static CommandLineOptions.SubsumptionOption Subsumption(AssertCmd! ac) { - 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) - { - 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.Length==0) - { - res = N; - } - else - { - VCExpr currentWLP = RegExpr((!)ch.rs[0], N, ctxt); - for (int i = 1, n = ch.rs.Length; i < n; i++) - { - currentWLP = ctxt.Ctxt.ExprGen.And(currentWLP, RegExpr((!)ch.rs[i], N, ctxt)); - } - res = currentWLP; - } - return res; - } - else - { - assert false; // unexpected RE subtype - } - } - } - #endregion - -} -- cgit v1.2.3