//----------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All Rights Reserved. // //----------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------- // BoogiePL - Absy.cs //--------------------------------------------------------------------------------------------- namespace Microsoft.Boogie { using System; using System.Collections; using System.Diagnostics; using System.Collections.Generic; using System.Linq; using Microsoft.Boogie.AbstractInterpretation; using System.Diagnostics.Contracts; using Set = GSet; //--------------------------------------------------------------------- // BigBlock public class BigBlock { [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(tok != null); Contract.Invariant(Anonymous || this.labelName != null); Contract.Invariant(this._ec == null || this._tc == null); Contract.Invariant(this._simpleCmds != null); } public readonly IToken/*!*/ tok; public readonly bool Anonymous; private string labelName; public string LabelName { get { Contract.Ensures(Anonymous || Contract.Result() != null); return this.labelName; } set { Contract.Requires(Anonymous || value != null); this.labelName = value; } } [Rep] private List/*!*/ _simpleCmds; public List/*!*/ simpleCmds { get { Contract.Ensures(Contract.Result>() != null); return this._simpleCmds; } set { Contract.Requires(value != null); this._simpleCmds = value; } } private StructuredCmd _ec; public StructuredCmd ec { get { return this._ec; } set { Contract.Requires(value == null || this.tc == null); this._ec = value; } } private TransferCmd _tc; public TransferCmd tc { get { return this._tc; } set { Contract.Requires(value == null || this.ec == null); this._tc = value; } } public BigBlock successorBigBlock; // semantic successor (may be a back-edge, pointing back to enclosing while statement); null if successor is end of procedure body (or if field has not yet been initialized) public BigBlock(IToken tok, string labelName, [Captured] List simpleCmds, StructuredCmd ec, TransferCmd tc) { Contract.Requires(simpleCmds != null); Contract.Requires(tok != null); Contract.Requires(ec == null || tc == null); this.tok = tok; this.Anonymous = labelName == null; this.labelName = labelName; this._simpleCmds = simpleCmds; this._ec = ec; this._tc = tc; } public void Emit(TokenTextWriter stream, int level) { Contract.Requires(stream != null); if (!Anonymous) { stream.WriteLine(level, "{0}:", CommandLineOptions.Clo.PrintWithUniqueASTIds ? String.Format("h{0}^^{1}", this.GetHashCode(), this.LabelName) : this.LabelName); } foreach (Cmd/*!*/ c in this.simpleCmds) { Contract.Assert(c != null); c.Emit(stream, level + 1); } if (this.ec != null) { this.ec.Emit(stream, level + 1); } else if (this.tc != null) { this.tc.Emit(stream, level + 1); } } } public class StmtList { [Rep] private readonly List/*!*/ bigBlocks; public IList/*!*/ BigBlocks { get { Contract.Ensures(Contract.Result>() != null); Contract.Ensures(Contract.Result>().IsReadOnly); return this.bigBlocks.AsReadOnly(); } } public List PrefixCommands; public readonly IToken/*!*/ EndCurly; public StmtList ParentContext; public BigBlock ParentBigBlock; private readonly HashSet/*!*/ labels = new HashSet(); public void AddLabel(string label) { labels.Add(label); } public IEnumerable/*!*/ Labels { get { Contract.Ensures(cce.NonNullElements(Contract.Result/*!*/>())); return this.labels.AsEnumerable(); } } [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(EndCurly != null); Contract.Invariant(cce.NonNullElements(this.bigBlocks)); Contract.Invariant(cce.NonNullElements(this.labels)); } public StmtList(IList/*!*/ bigblocks, IToken endCurly) { Contract.Requires(endCurly != null); Contract.Requires(cce.NonNullElements(bigblocks)); Contract.Requires(bigblocks.Count > 0); this.bigBlocks = new List(bigblocks); this.EndCurly = endCurly; } // prints the list of statements, not the surrounding curly braces public void Emit(TokenTextWriter stream, int level) { Contract.Requires(stream != null); bool needSeperator = false; foreach (BigBlock b in BigBlocks) { Contract.Assert(b != null); Contract.Assume(cce.IsPeerConsistent(b)); if (needSeperator) { stream.WriteLine(); } b.Emit(stream, level); needSeperator = true; } } /// /// Tries to insert the commands "prefixCmds" at the beginning of the first block /// of the StmtList, and returns "true" iff it succeeded. /// In the event of success, the "suggestedLabel" returns as the name of the /// block inside StmtList where "prefixCmds" were inserted. This name may be the /// same as the one passed in, in case this StmtList has no preference as to what /// to call its first block. In the event of failure, "suggestedLabel" is returned /// as its input value. /// Note, to be conservative (that is, ignoring the possible optimization that this /// method enables), this method can do nothing and return false. /// public bool PrefixFirstBlock([Captured] List prefixCmds, ref string suggestedLabel) { Contract.Requires(suggestedLabel != null); Contract.Requires(prefixCmds != null); Contract.Ensures(Contract.Result() || cce.Owner.None(prefixCmds)); // "prefixCmds" is captured only on success Contract.Assume(PrefixCommands == null); // prefix has not been used BigBlock bb0 = BigBlocks[0]; if (prefixCmds.Count == 0) { // This is always a success, since there is nothing to insert. Now, decide // which name to use for the first block. if (bb0.Anonymous) { bb0.LabelName = suggestedLabel; } else { Contract.Assert(bb0.LabelName != null); suggestedLabel = bb0.LabelName; } return true; } else { // There really is something to insert. We can do this inline only if the first // block is anonymous (which implies there is no branch to it from within the block). if (bb0.Anonymous) { PrefixCommands = prefixCmds; bb0.LabelName = suggestedLabel; return true; } else { return false; } } } } /// /// The AST for Boogie structured commands was designed to support backward compatibility with /// the Boogie unstructured commands. This has made the structured commands hard to construct. /// The StmtListBuilder class makes it easier to build structured commands. /// public class StmtListBuilder { List/*!*/ bigBlocks = new List(); string label; List simpleCmds; [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(cce.NonNullElements(bigBlocks)); } void Dump(StructuredCmd scmd, TransferCmd tcmd) { Contract.Requires(scmd == null || tcmd == null); Contract.Ensures(label == null && simpleCmds == null); if (label == null && simpleCmds == null && scmd == null && tcmd == null) { // nothing to do } else { if (simpleCmds == null) { simpleCmds = new List(); } bigBlocks.Add(new BigBlock(Token.NoToken, label, simpleCmds, scmd, tcmd)); label = null; simpleCmds = null; } } /// /// Collects the StmtList built so far and returns it. The StmtListBuilder should no longer /// be used once this method has been invoked. /// public StmtList Collect(IToken endCurlyBrace) { Contract.Requires(endCurlyBrace != null); Contract.Ensures(Contract.Result() != null); Dump(null, null); if (bigBlocks.Count == 0) { simpleCmds = new List(); // the StmtList constructor doesn't like an empty list of BigBlock's Dump(null, null); } return new StmtList(bigBlocks, endCurlyBrace); } public void Add(Cmd cmd) { Contract.Requires(cmd != null); if (simpleCmds == null) { simpleCmds = new List(); } simpleCmds.Add(cmd); } public void Add(StructuredCmd scmd) { Contract.Requires(scmd != null); Dump(scmd, null); } public void Add(TransferCmd tcmd) { Contract.Requires(tcmd != null); Dump(null, tcmd); } public void AddLabelCmd(string label) { Contract.Requires(label != null); Dump(null, null); this.label = label; } public void AddLocalVariable(string name) { Contract.Requires(name != null); // TODO } } class BigBlocksResolutionContext { StmtList/*!*/ stmtList; [Peer] List blocks; string/*!*/ prefix = "anon"; int anon = 0; int FreshAnon() { return anon++; } HashSet allLabels = new HashSet(); Errors/*!*/ errorHandler; [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(stmtList != null); Contract.Invariant(cce.NonNullElements(blocks, true)); Contract.Invariant(prefix != null); Contract.Invariant(cce.NonNullElements(allLabels, true)); Contract.Invariant(errorHandler != null); } private void ComputeAllLabels(StmtList stmts) { if (stmts == null) return; foreach (BigBlock bb in stmts.BigBlocks) { if (bb.LabelName != null) { allLabels.Add(bb.LabelName); } ComputeAllLabels(bb.ec); } } private void ComputeAllLabels(StructuredCmd cmd) { if (cmd == null) return; if (cmd is IfCmd) { IfCmd ifCmd = (IfCmd)cmd; ComputeAllLabels(ifCmd.thn); ComputeAllLabels(ifCmd.elseIf); ComputeAllLabels(ifCmd.elseBlock); } else if (cmd is WhileCmd) { WhileCmd whileCmd = (WhileCmd)cmd; ComputeAllLabels(whileCmd.Body); } } public BigBlocksResolutionContext(StmtList stmtList, Errors errorHandler) { Contract.Requires(errorHandler != null); Contract.Requires(stmtList != null); this.stmtList = stmtList; this.errorHandler = errorHandler; ComputeAllLabels(stmtList); } public List/*!*/ Blocks { get { Contract.Ensures(cce.NonNullElements(Contract.Result>())); if (blocks == null) { blocks = new List(); int startErrorCount = this.errorHandler.count; // Check that all goto statements go to a label in allLabels, and no break statement to a non-enclosing loop. // Also, determine a good value for "prefix". CheckLegalLabels(stmtList, null, null); // fill in names of anonymous blocks NameAnonymousBlocks(stmtList); // determine successor blocks RecordSuccessors(stmtList, null); if (this.errorHandler.count == startErrorCount) { // generate blocks from the big blocks CreateBlocks(stmtList, null); } } return blocks; } } void CheckLegalLabels(StmtList stmtList, StmtList parentContext, BigBlock parentBigBlock) { Contract.Requires(stmtList != null); Contract.Requires((parentContext == null) == (parentBigBlock == null)); Contract.Requires(stmtList.ParentContext == null); // it hasn't been set yet //modifies stmtList.*; Contract.Ensures(stmtList.ParentContext == parentContext); stmtList.ParentContext = parentContext; stmtList.ParentBigBlock = parentBigBlock; // record the labels declared in this StmtList foreach (BigBlock b in stmtList.BigBlocks) { if (b.LabelName != null) { string n = b.LabelName; if (n.StartsWith(prefix)) { if (prefix.Length < n.Length && n[prefix.Length] == '0') { prefix += "1"; } else { prefix += "0"; } } stmtList.AddLabel(b.LabelName); } } // check that labels in this and nested StmtList's are legal foreach (BigBlock b in stmtList.BigBlocks) { // goto's must reference blocks in enclosing blocks if (b.tc is GotoCmd) { GotoCmd g = (GotoCmd)b.tc; foreach (string/*!*/ lbl in cce.NonNull(g.labelNames)) { Contract.Assert(lbl != null); /* bool found = false; for (StmtList sl = stmtList; sl != null; sl = sl.ParentContext) { if (sl.Labels.Contains(lbl)) { found = true; break; } } if (!found) { this.errorHandler.SemErr(g.tok, "Error: goto label '" + lbl + "' is undefined or out of reach"); } */ if (!allLabels.Contains(lbl)) { this.errorHandler.SemErr(g.tok, "Error: goto label '" + lbl + "' is undefined"); } } } // break labels must refer to an enclosing while statement else if (b.ec is BreakCmd) { BreakCmd bcmd = (BreakCmd)b.ec; Contract.Assert(bcmd.BreakEnclosure == null); // it hasn't been initialized yet bool found = false; for (StmtList sl = stmtList; sl.ParentBigBlock != null; sl = sl.ParentContext) { cce.LoopInvariant(sl != null); BigBlock bb = sl.ParentBigBlock; if (bcmd.Label == null) { // a label-less break statement breaks out of the innermost enclosing while statement if (bb.ec is WhileCmd) { bcmd.BreakEnclosure = bb; found = true; break; } } else if (bcmd.Label == bb.LabelName) { // a break statement with a label can break out of both if statements and while statements if (bb.simpleCmds.Count == 0) { // this is a good target: the label refers to the if/while statement bcmd.BreakEnclosure = bb; } else { // the label of bb refers to the first statement of bb, which in which case is a simple statement, not an if/while statement this.errorHandler.SemErr(bcmd.tok, "Error: break label '" + bcmd.Label + "' must designate an enclosing statement"); } found = true; // don't look any further, since we've found a matching label break; } } if (!found) { if (bcmd.Label == null) { this.errorHandler.SemErr(bcmd.tok, "Error: break statement is not inside a loop"); } else { this.errorHandler.SemErr(bcmd.tok, "Error: break label '" + bcmd.Label + "' must designate an enclosing statement"); } } } // recurse else if (b.ec is WhileCmd) { WhileCmd wcmd = (WhileCmd)b.ec; CheckLegalLabels(wcmd.Body, stmtList, b); } else { for (IfCmd ifcmd = b.ec as IfCmd; ifcmd != null; ifcmd = ifcmd.elseIf) { CheckLegalLabels(ifcmd.thn, stmtList, b); if (ifcmd.elseBlock != null) { CheckLegalLabels(ifcmd.elseBlock, stmtList, b); } } } } } void NameAnonymousBlocks(StmtList stmtList) { Contract.Requires(stmtList != null); foreach (BigBlock b in stmtList.BigBlocks) { if (b.LabelName == null) { b.LabelName = prefix + FreshAnon(); } if (b.ec is WhileCmd) { WhileCmd wcmd = (WhileCmd)b.ec; NameAnonymousBlocks(wcmd.Body); } else { for (IfCmd ifcmd = b.ec as IfCmd; ifcmd != null; ifcmd = ifcmd.elseIf) { NameAnonymousBlocks(ifcmd.thn); if (ifcmd.elseBlock != null) { NameAnonymousBlocks(ifcmd.elseBlock); } } } } } void RecordSuccessors(StmtList stmtList, BigBlock successor) { Contract.Requires(stmtList != null); for (int i = stmtList.BigBlocks.Count; 0 <= --i; ) { BigBlock big = stmtList.BigBlocks[i]; big.successorBigBlock = successor; if (big.ec is WhileCmd) { WhileCmd wcmd = (WhileCmd)big.ec; RecordSuccessors(wcmd.Body, big); } else { for (IfCmd ifcmd = big.ec as IfCmd; ifcmd != null; ifcmd = ifcmd.elseIf) { RecordSuccessors(ifcmd.thn, successor); if (ifcmd.elseBlock != null) { RecordSuccessors(ifcmd.elseBlock, successor); } } } successor = big; } } // If the enclosing context is a loop, then "runOffTheEndLabel" is the loop head label; // otherwise, it is null. void CreateBlocks(StmtList stmtList, string runOffTheEndLabel) { Contract.Requires(stmtList != null); Contract.Requires(blocks != null); List cmdPrefixToApply = stmtList.PrefixCommands; int n = stmtList.BigBlocks.Count; foreach (BigBlock b in stmtList.BigBlocks) { n--; Contract.Assert(b.LabelName != null); List theSimpleCmds; if (cmdPrefixToApply == null) { theSimpleCmds = b.simpleCmds; } else { theSimpleCmds = new List(); theSimpleCmds.AddRange(cmdPrefixToApply); theSimpleCmds.AddRange(b.simpleCmds); cmdPrefixToApply = null; // now, we've used 'em up } if (b.tc != null) { // this BigBlock has the very same components as a Block Contract.Assert(b.ec == null); Block block = new Block(b.tok, b.LabelName, theSimpleCmds, b.tc); blocks.Add(block); } else if (b.ec == null) { TransferCmd trCmd; if (n == 0 && runOffTheEndLabel != null) { // goto the given label instead of the textual successor block trCmd = new GotoCmd(stmtList.EndCurly, new List { runOffTheEndLabel }); } else { trCmd = GotoSuccessor(stmtList.EndCurly, b); } Block block = new Block(b.tok, b.LabelName, theSimpleCmds, trCmd); blocks.Add(block); } else if (b.ec is BreakCmd) { BreakCmd bcmd = (BreakCmd)b.ec; Contract.Assert(bcmd.BreakEnclosure != null); Block block = new Block(b.tok, b.LabelName, theSimpleCmds, GotoSuccessor(b.ec.tok, bcmd.BreakEnclosure)); blocks.Add(block); } else if (b.ec is WhileCmd) { WhileCmd wcmd = (WhileCmd)b.ec; var a = FreshAnon(); string loopHeadLabel = prefix + a + "_LoopHead"; string/*!*/ loopBodyLabel = prefix + a + "_LoopBody"; string loopDoneLabel = prefix + a + "_LoopDone"; List ssBody = new List(); List ssDone = new List(); if (wcmd.Guard != null) { var ac = new AssumeCmd(wcmd.tok, wcmd.Guard); ac.Attributes = new QKeyValue(wcmd.tok, "partition", new List(), null); ssBody.Add(ac); ac = new AssumeCmd(wcmd.tok, Expr.Not(wcmd.Guard)); ac.Attributes = new QKeyValue(wcmd.tok, "partition", new List(), null); ssDone.Add(ac); } // Try to squeeze in ssBody into the first block of wcmd.Body bool bodyGuardTakenCareOf = wcmd.Body.PrefixFirstBlock(ssBody, ref loopBodyLabel); // ... goto LoopHead; Block block = new Block(b.tok, b.LabelName, theSimpleCmds, new GotoCmd(wcmd.tok, new List { loopHeadLabel })); blocks.Add(block); // LoopHead: assert/assume loop_invariant; goto LoopDone, LoopBody; List ssHead = new List(); foreach (PredicateCmd inv in wcmd.Invariants) { ssHead.Add(inv); } block = new Block(wcmd.tok, loopHeadLabel, ssHead, new GotoCmd(wcmd.tok, new List { loopDoneLabel, loopBodyLabel })); blocks.Add(block); if (!bodyGuardTakenCareOf) { // LoopBody: assume guard; goto firstLoopBlock; block = new Block(wcmd.tok, loopBodyLabel, ssBody, new GotoCmd(wcmd.tok, new List { wcmd.Body.BigBlocks[0].LabelName })); blocks.Add(block); } // recurse to create the blocks for the loop body CreateBlocks(wcmd.Body, loopHeadLabel); // LoopDone: assume !guard; goto loopSuccessor; TransferCmd trCmd; if (n == 0 && runOffTheEndLabel != null) { // goto the given label instead of the textual successor block trCmd = new GotoCmd(wcmd.tok, new List { runOffTheEndLabel }); } else { trCmd = GotoSuccessor(wcmd.tok, b); } block = new Block(wcmd.tok, loopDoneLabel, ssDone, trCmd); blocks.Add(block); } else { IfCmd ifcmd = (IfCmd)b.ec; string predLabel = b.LabelName; List predCmds = theSimpleCmds; for (; ifcmd != null; ifcmd = ifcmd.elseIf) { var a = FreshAnon(); string thenLabel = prefix + a + "_Then"; Contract.Assert(thenLabel != null); string elseLabel = prefix + a + "_Else"; Contract.Assert(elseLabel != null); List ssThen = new List(); List ssElse = new List(); if (ifcmd.Guard != null) { var ac = new AssumeCmd(ifcmd.tok, ifcmd.Guard); ac.Attributes = new QKeyValue(ifcmd.tok, "partition", new List(), null); ssThen.Add(ac); ac = new AssumeCmd(ifcmd.tok, Expr.Not(ifcmd.Guard)); ac.Attributes = new QKeyValue(ifcmd.tok, "partition", new List(), null); ssElse.Add(ac); } // Try to squeeze in ssThen/ssElse into the first block of ifcmd.thn/ifcmd.elseBlock bool thenGuardTakenCareOf = ifcmd.thn.PrefixFirstBlock(ssThen, ref thenLabel); bool elseGuardTakenCareOf = false; if (ifcmd.elseBlock != null) { elseGuardTakenCareOf = ifcmd.elseBlock.PrefixFirstBlock(ssElse, ref elseLabel); } // ... goto Then, Else; Block block = new Block(b.tok, predLabel, predCmds, new GotoCmd(ifcmd.tok, new List { thenLabel, elseLabel })); blocks.Add(block); if (!thenGuardTakenCareOf) { // Then: assume guard; goto firstThenBlock; block = new Block(ifcmd.tok, thenLabel, ssThen, new GotoCmd(ifcmd.tok, new List { ifcmd.thn.BigBlocks[0].LabelName })); blocks.Add(block); } // recurse to create the blocks for the then branch CreateBlocks(ifcmd.thn, n == 0 ? runOffTheEndLabel : null); if (ifcmd.elseBlock != null) { Contract.Assert(ifcmd.elseIf == null); if (!elseGuardTakenCareOf) { // Else: assume !guard; goto firstElseBlock; block = new Block(ifcmd.tok, elseLabel, ssElse, new GotoCmd(ifcmd.tok, new List { ifcmd.elseBlock.BigBlocks[0].LabelName })); blocks.Add(block); } // recurse to create the blocks for the else branch CreateBlocks(ifcmd.elseBlock, n == 0 ? runOffTheEndLabel : null); } else if (ifcmd.elseIf != null) { // this is an "else if" predLabel = elseLabel; predCmds = new List(); if (ifcmd.Guard != null) { var ac = new AssumeCmd(ifcmd.tok, Expr.Not(ifcmd.Guard)); ac.Attributes = new QKeyValue(ifcmd.tok, "partition", new List(), null); predCmds.Add(ac); } } else { // no else alternative is specified, so else branch is just "skip" // Else: assume !guard; goto ifSuccessor; TransferCmd trCmd; if (n == 0 && runOffTheEndLabel != null) { // goto the given label instead of the textual successor block trCmd = new GotoCmd(ifcmd.tok, new List { runOffTheEndLabel }); } else { trCmd = GotoSuccessor(ifcmd.tok, b); } block = new Block(ifcmd.tok, elseLabel, ssElse, trCmd); blocks.Add(block); } } } } } TransferCmd GotoSuccessor(IToken tok, BigBlock b) { Contract.Requires(b != null); Contract.Requires(tok != null); Contract.Ensures(Contract.Result() != null); if (b.successorBigBlock != null) { return new GotoCmd(tok, new List { b.successorBigBlock.LabelName }); } else { return new ReturnCmd(tok); } } } [ContractClass(typeof(StructuredCmdContracts))] public abstract class StructuredCmd { private IToken/*!*/ _tok; public IToken/*!*/ tok { get { Contract.Ensures(Contract.Result() != null); return this._tok; } set { Contract.Requires(value != null); this._tok = value; } } [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(this._tok != null); } public StructuredCmd(IToken tok) { Contract.Requires(tok != null); this._tok = tok; } public abstract void Emit(TokenTextWriter/*!*/ stream, int level); } [ContractClassFor(typeof(StructuredCmd))] public abstract class StructuredCmdContracts : StructuredCmd { public override void Emit(TokenTextWriter stream, int level) { Contract.Requires(stream != null); throw new NotImplementedException(); } public StructuredCmdContracts() :base(null){ } } public class IfCmd : StructuredCmd { public Expr Guard; private StmtList/*!*/ _thn; public StmtList/*!*/ thn { get { Contract.Ensures(Contract.Result() != null); return this._thn; } set { Contract.Requires(value != null); this._thn = value; } } private IfCmd _elseIf; public IfCmd elseIf { get { return this._elseIf; } set { Contract.Requires(value == null || this.elseBlock == null); this._elseIf = value; } } private StmtList _elseBlock; public StmtList elseBlock { get { return this._elseBlock; } set { Contract.Requires(value == null || this.elseIf == null); this._elseBlock = value; } } [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(this._thn != null); Contract.Invariant(this._elseIf == null || this._elseBlock == null); } public IfCmd(IToken/*!*/ tok, Expr guard, StmtList/*!*/ thn, IfCmd elseIf, StmtList elseBlock) : base(tok) { Contract.Requires(tok != null); Contract.Requires(thn != null); Contract.Requires(elseIf == null || elseBlock == null); this.Guard = guard; this._thn = thn; this._elseIf = elseIf; this._elseBlock = elseBlock; } public override void Emit(TokenTextWriter stream, int level) { stream.Write(level, "if ("); IfCmd/*!*/ ifcmd = this; while (true) { if (ifcmd.Guard == null) { stream.Write("*"); } else { ifcmd.Guard.Emit(stream); } stream.WriteLine(")"); stream.WriteLine(level, "{"); ifcmd.thn.Emit(stream, level + 1); stream.WriteLine(level, "}"); if (ifcmd.elseIf != null) { stream.Write(level, "else if ("); ifcmd = ifcmd.elseIf; continue; } else if (ifcmd.elseBlock != null) { stream.WriteLine(level, "else"); stream.WriteLine(level, "{"); ifcmd.elseBlock.Emit(stream, level + 1); stream.WriteLine(level, "}"); } break; } } } public class WhileCmd : StructuredCmd { [Peer] public Expr Guard; public List/*!*/ Invariants; public StmtList/*!*/ Body; [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(Body != null); Contract.Invariant(cce.NonNullElements(Invariants)); } public WhileCmd(IToken tok, [Captured] Expr guard, List/*!*/ invariants, StmtList/*!*/ body) : base(tok) { Contract.Requires(cce.NonNullElements(invariants)); Contract.Requires(body != null); Contract.Requires(tok != null); this.Guard = guard; this.Invariants = invariants; this.Body = body; } public override void Emit(TokenTextWriter stream, int level) { stream.Write(level, "while ("); if (Guard == null) { stream.Write("*"); } else { Guard.Emit(stream); } stream.WriteLine(")"); foreach (PredicateCmd inv in Invariants) { if (inv is AssumeCmd) { stream.Write(level + 1, "free invariant "); } else { stream.Write(level + 1, "invariant "); } Cmd.EmitAttributes(stream, inv.Attributes); inv.Expr.Emit(stream); stream.WriteLine(";"); } stream.WriteLine(level, "{"); Body.Emit(stream, level + 1); stream.WriteLine(level, "}"); } } public class BreakCmd : StructuredCmd { public string Label; public BigBlock BreakEnclosure; public BreakCmd(IToken tok, string label) : base(tok) { Contract.Requires(tok != null); this.Label = label; } public override void Emit(TokenTextWriter stream, int level) { if (Label == null) { stream.WriteLine(level, "break;"); } else { stream.WriteLine(level, "break {0};", Label); } } } //--------------------------------------------------------------------- // Block public sealed class Block : Absy { private string/*!*/ label; // Note, Label is mostly readonly, but it can change to the name of a nearby block during block coalescing and empty-block removal public string/*!*/ Label { get { Contract.Ensures(Contract.Result() != null); return this.label; } set { Contract.Requires(value != null); this.label = value; } } [Rep] [ElementsPeer] public List/*!*/ cmds; public List/*!*/ Cmds { get { Contract.Ensures(Contract.Result>() != null); return this.cmds; } set { Contract.Requires(value != null); this.cmds = value; } } [Rep] //PM: needed to verify Traverse.Visit public TransferCmd TransferCmd; // maybe null only because we allow deferred initialization (necessary for cyclic structures) public byte[] Checksum; // Abstract interpretation // public bool currentlyTraversed; public enum VisitState { ToVisit, BeingVisited, AlreadyVisited }; // used by WidenPoints.Compute public VisitState TraversingStatus; public int aiId; // block ID used by the abstract interpreter, which may change these numbers with each AI run public bool widenBlock; public int iterations; // Count the number of time we visited the block during fixpoint computation. Used to decide if we widen or not // VC generation and SCC computation public List/*!*/ Predecessors; // This field is used during passification to null-out entries in block2Incartion hashtable early public int succCount; private HashSet _liveVarsBefore; public IEnumerable liveVarsBefore { get { Contract.Ensures(cce.NonNullElements(Contract.Result>(), true)); if (this._liveVarsBefore == null) return null; else return this._liveVarsBefore.AsEnumerable(); } set { Contract.Requires(cce.NonNullElements(value, true)); if (value == null) this._liveVarsBefore = null; else this._liveVarsBefore = new HashSet(value); } } [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(this.label != null); Contract.Invariant(this.cmds != null); Contract.Invariant(cce.NonNullElements(this._liveVarsBefore, true)); } public bool IsLive(Variable v) { Contract.Requires(v != null); if (liveVarsBefore == null) return true; return liveVarsBefore.Contains(v); } public Block() : this(Token.NoToken, "", new List(), new ReturnCmd(Token.NoToken)) { } public Block(IToken tok, string/*!*/ label, List/*!*/ cmds, TransferCmd transferCmd) : base(tok) { Contract.Requires(label != null); Contract.Requires(cmds != null); Contract.Requires(tok != null); this.label = label; this.cmds = cmds; this.TransferCmd = transferCmd; this.Predecessors = new List(); this._liveVarsBefore = null; this.TraversingStatus = VisitState.ToVisit; this.iterations = 0; } public void Emit(TokenTextWriter stream, int level) { Contract.Requires(stream != null); stream.WriteLine(); stream.WriteLine( this, level, "{0}:{1}", CommandLineOptions.Clo.PrintWithUniqueASTIds ? String.Format("h{0}^^{1}", this.GetHashCode(), this.Label) : this.Label, this.widenBlock ? " // cut point" : ""); foreach (Cmd/*!*/ c in this.Cmds) { Contract.Assert(c != null); c.Emit(stream, level + 1); } Contract.Assume(this.TransferCmd != null); this.TransferCmd.Emit(stream, level + 1); } public void Register(ResolutionContext rc) { Contract.Requires(rc != null); rc.AddBlock(this); } public override void Resolve(ResolutionContext rc) { foreach (Cmd/*!*/ c in Cmds) { Contract.Assert(c != null); c.Resolve(rc); } Contract.Assume(this.TransferCmd != null); TransferCmd.Resolve(rc); } public override void Typecheck(TypecheckingContext tc) { foreach (Cmd/*!*/ c in Cmds) { Contract.Assert(c != null); c.Typecheck(tc); } Contract.Assume(this.TransferCmd != null); TransferCmd.Typecheck(tc); } /// /// Reset the abstract intepretation state of this block. It does this by putting the iterations to 0 and the pre and post states to null /// public void ResetAbstractInterpretationState() { // this.currentlyTraversed = false; this.TraversingStatus = VisitState.ToVisit; this.iterations = 0; } [Pure] public override string ToString() { Contract.Ensures(Contract.Result() != null); return this.Label + (this.widenBlock ? "[w]" : ""); } public override Absy StdDispatch(StandardVisitor visitor) { Contract.Ensures(Contract.Result() != null); return visitor.VisitBlock(this); } } //--------------------------------------------------------------------- // Commands [ContractClassFor(typeof(Cmd))] public abstract class CmdContracts : Cmd { public CmdContracts() :base(null){ } public override void Emit(TokenTextWriter stream, int level) { Contract.Requires(stream != null); throw new NotImplementedException(); } public override void AddAssignedVariables(List vars) { Contract.Requires(vars != null); throw new NotImplementedException(); } } public static class ChecksumHelper { public static void ComputeChecksums(Cmd cmd, Implementation impl, ISet usedVariables, byte[] currentChecksum = null) { if (CommandLineOptions.Clo.VerifySnapshots < 2) { return; } var assumeCmd = cmd as AssumeCmd; if (assumeCmd != null && QKeyValue.FindBoolAttribute(assumeCmd.Attributes, "assumption_variable_initialization")) { // Ignore assumption variable initializations. assumeCmd.Checksum = currentChecksum; return; } using (var strWr = new System.IO.StringWriter()) using (var tokTxtWr = new TokenTextWriter("", strWr, false, false)) { tokTxtWr.UseForComputingChecksums = true; var havocCmd = cmd as HavocCmd; if (havocCmd != null) { tokTxtWr.Write("havoc "); var relevantVars = havocCmd.Vars.Where(e => usedVariables.Contains(e.Decl) && !e.Decl.Name.StartsWith("a##post##")).OrderBy(e => e.Name).ToList(); relevantVars.Emit(tokTxtWr, true); tokTxtWr.WriteLine(";"); } else { cmd.Emit(tokTxtWr, 0); } var md5 = System.Security.Cryptography.MD5.Create(); var str = strWr.ToString(); if (str.Any()) { var data = System.Text.Encoding.UTF8.GetBytes(str); var checksum = md5.ComputeHash(data); currentChecksum = currentChecksum != null ? CombineChecksums(currentChecksum, checksum) : checksum; } cmd.Checksum = currentChecksum; } var assertCmd = cmd as AssertCmd; if (assertCmd != null && assertCmd.Checksum != null) { var assertRequiresCmd = assertCmd as AssertRequiresCmd; if (assertRequiresCmd != null) { impl.AddAssertionChecksum(assertRequiresCmd.Checksum); impl.AddAssertionChecksum(assertRequiresCmd.Call.Checksum); assertRequiresCmd.SugaredCmdChecksum = assertRequiresCmd.Call.Checksum; } else { impl.AddAssertionChecksum(assertCmd.Checksum); } } var sugaredCmd = cmd as SugaredCmd; if (sugaredCmd != null) { // The checksum of a sugared command should not depend on the desugaring itself. var stateCmd = sugaredCmd.Desugaring as StateCmd; if (stateCmd != null) { foreach (var c in stateCmd.Cmds) { ComputeChecksums(c, impl, usedVariables, currentChecksum); currentChecksum = c.Checksum; if (c.SugaredCmdChecksum == null) { c.SugaredCmdChecksum = cmd.Checksum; } } } else { ComputeChecksums(sugaredCmd.Desugaring, impl, usedVariables, currentChecksum); } } } public static byte[] CombineChecksums(byte[] first, byte[] second, bool unordered = false) { Contract.Requires(first != null && (second == null || first.Length == second.Length)); var result = (byte[])(first.Clone()); for (int i = 0; second != null && i < second.Length; i++) { if (unordered) { result[i] += second[i]; } else { result[i] = (byte)(result[i] * 31 ^ second[i]); } } return result; } } [ContractClass(typeof(CmdContracts))] public abstract class Cmd : Absy { public byte[] Checksum { get; internal set; } public byte[] SugaredCmdChecksum { get; internal set; } public Cmd(IToken/*!*/ tok) : base(tok) { Contract.Assert(tok != null); } public abstract void Emit(TokenTextWriter/*!*/ stream, int level); public abstract void AddAssignedVariables(List/*!*/ vars); public void CheckAssignments(TypecheckingContext tc) { Contract.Requires(tc != null); List/*!*/ vars = new List(); this.AddAssignedVariables(vars); foreach (Variable/*!*/ v in vars) { Contract.Assert(v != null); if (!v.IsMutable) { tc.Error(this, "command assigns to an immutable variable: {0}", v.Name); } else if (!CommandLineOptions.Clo.DoModSetAnalysis && v is GlobalVariable) { if (tc.Yields) { // a yielding procedure is allowed to modify any global variable } else if (tc.Frame == null) { tc.Error(this, "update to a global variable allowed only inside an atomic action of a yielding procedure"); } else if (!tc.InFrame(v)) { tc.Error(this, "command assigns to a global variable that is not in the enclosing procedure's modifies clause: {0}", v.Name); } } } } // Methods to simulate the old SimpleAssignCmd and MapAssignCmd public static AssignCmd SimpleAssign(IToken tok, IdentifierExpr lhs, Expr rhs) { Contract.Requires(rhs != null); Contract.Requires(lhs != null); Contract.Requires(tok != null); Contract.Ensures(Contract.Result() != null); List/*!*/ lhss = new List(); List/*!*/ rhss = new List(); lhss.Add(new SimpleAssignLhs(lhs.tok, lhs)); rhss.Add(rhs); return new AssignCmd(tok, lhss, rhss); } public static AssignCmd/*!*/ MapAssign(IToken tok, IdentifierExpr/*!*/ map, List/*!*/ indexes, Expr/*!*/ rhs) { Contract.Requires(tok != null); Contract.Requires(map != null); Contract.Requires(indexes != null); Contract.Requires(rhs != null); Contract.Ensures(Contract.Result() != null); List/*!*/ lhss = new List(); List/*!*/ rhss = new List(); List/*!*/ indexesList = new List(); foreach (Expr e in indexes) indexesList.Add(cce.NonNull(e)); lhss.Add(new MapAssignLhs(map.tok, new SimpleAssignLhs(map.tok, map), indexesList)); rhss.Add(rhs); return new AssignCmd(tok, lhss, rhss); } public static AssignCmd/*!*/ MapAssign(IToken tok, IdentifierExpr/*!*/ map, params Expr[]/*!*/ args) { Contract.Requires(tok != null); Contract.Requires(map != null); Contract.Requires(args != null); Contract.Requires(args.Length > 0); // at least the rhs Contract.Requires(Contract.ForAll(args, i => i != null)); Contract.Ensures(Contract.Result() != null); List/*!*/ lhss = new List(); List/*!*/ rhss = new List(); List/*!*/ indexesList = new List(); for (int i = 0; i < args.Length - 1; ++i) indexesList.Add(cce.NonNull(args[i])); lhss.Add(new MapAssignLhs(map.tok, new SimpleAssignLhs(map.tok, map), indexesList)); rhss.Add(cce.NonNull(args[args.Length - 1])); return new AssignCmd(tok, lhss, rhss); } /// /// This is a helper routine for printing a linked list of attributes. Each attribute /// is terminated by a space. /// public static void EmitAttributes(TokenTextWriter stream, QKeyValue attributes) { Contract.Requires(stream != null); if (stream.UseForComputingChecksums) { return; } for (QKeyValue kv = attributes; kv != null; kv = kv.Next) { kv.Emit(stream); stream.Write(" "); } } public static void ResolveAttributes(QKeyValue attributes, ResolutionContext rc) { Contract.Requires(rc != null); for (QKeyValue kv = attributes; kv != null; kv = kv.Next) { kv.Resolve(rc); } } public static void TypecheckAttributes(QKeyValue attributes, TypecheckingContext tc) { Contract.Requires(tc != null); for (QKeyValue kv = attributes; kv != null; kv = kv.Next) { kv.Typecheck(tc); } } [Pure] public override string ToString() { Contract.Ensures(Contract.Result() != null); System.IO.StringWriter buffer = new System.IO.StringWriter(); using (TokenTextWriter stream = new TokenTextWriter("", buffer, /*setTokens=*/ false , /*pretty=*/ false)) { this.Emit(stream, 0); } return buffer.ToString(); } } public class YieldCmd : Cmd { public YieldCmd(IToken/*!*/ tok) : base(tok) { Contract.Requires(tok != null); } public override void Emit(TokenTextWriter stream, int level) { //Contract.Requires(stream != null); stream.WriteLine(this, level, "yield;"); } public override void Resolve(ResolutionContext rc) { // nothing to resolve } public override void Typecheck(TypecheckingContext tc) { if (!CommandLineOptions.Clo.DoModSetAnalysis && !tc.Yields) { tc.Error(this, "enclosing procedure of a yield command must yield"); } } public override void AddAssignedVariables(List vars) { // nothing to add } public override Absy StdDispatch(StandardVisitor visitor) { Contract.Ensures(Contract.Result() != null); return visitor.VisitYieldCmd(this); } } public class CommentCmd : Cmd // just a convenience for debugging { public readonly string/*!*/ Comment; [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(Comment != null); } public CommentCmd(string c) : base(Token.NoToken) { Contract.Requires(c != null); Comment = c; } public override void Emit(TokenTextWriter stream, int level) { if (stream.UseForComputingChecksums) { return; } if (this.Comment.Contains("\n")) { stream.WriteLine(this, level, "/* {0} */", this.Comment); } else { stream.WriteLine(this, level, "// {0}", this.Comment); } } public override void Resolve(ResolutionContext rc) { } public override void AddAssignedVariables(List vars) { } public override void Typecheck(TypecheckingContext tc) { } public override Absy StdDispatch(StandardVisitor visitor) { return visitor.VisitCommentCmd(this); } } // class for parallel assignments, which subsumes both the old // SimpleAssignCmd and the old MapAssignCmd public class AssignCmd : Cmd { private List/*!*/ _lhss; public IList/*!*/ Lhss { get { Contract.Ensures(cce.NonNullElements(Contract.Result>())); Contract.Ensures(Contract.Result>().IsReadOnly); return this._lhss.AsReadOnly(); } set { Contract.Requires(cce.NonNullElements(value)); this._lhss = new List(value); } } internal void SetLhs(int index, AssignLhs lhs) { Contract.Requires(0 <= index && index < this.Lhss.Count); Contract.Requires(lhs != null); Contract.Ensures(this.Lhss[index] == lhs); this._lhss[index] = lhs; } private List/*!*/ _rhss; public IList/*!*/ Rhss { get { Contract.Ensures(cce.NonNullElements(Contract.Result>())); Contract.Ensures(Contract.Result>().IsReadOnly); return this._rhss.AsReadOnly(); } set { Contract.Requires(cce.NonNullElements(value)); this._rhss = new List(value); } } internal void SetRhs(int index, Expr rhs) { Contract.Requires(0 <= index && index < this.Rhss.Count); Contract.Requires(rhs != null); Contract.Ensures(this.Rhss[index] == rhs); this._rhss[index] = rhs; } [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(cce.NonNullElements(this._lhss)); Contract.Invariant(cce.NonNullElements(this._rhss)); } public AssignCmd(IToken tok, IList/*!*/ lhss, IList/*!*/ rhss) : base(tok) { Contract.Requires(tok != null); Contract.Requires(cce.NonNullElements(rhss)); Contract.Requires(cce.NonNullElements(lhss)); this._lhss = new List(lhss); this._rhss = new List(rhss); } public override void Emit(TokenTextWriter stream, int level) { if (stream.UseForComputingChecksums) { var lhs = Lhss.FirstOrDefault() as SimpleAssignLhs; if (lhs != null && lhs.AssignedVariable.Decl != null && (QKeyValue.FindBoolAttribute(lhs.AssignedVariable.Decl.Attributes, "assumption") || lhs.AssignedVariable.Decl.Name.Contains("##old##"))) { return; } } stream.Write(this, level, ""); string/*!*/ sep = ""; foreach (AssignLhs/*!*/ l in Lhss) { Contract.Assert(l != null); stream.Write(sep); sep = ", "; l.Emit(stream); } stream.Write(" := "); sep = ""; foreach (Expr/*!*/ e in Rhss) { Contract.Assert(e != null); stream.Write(sep); sep = ", "; e.Emit(stream); } stream.WriteLine(";"); } public override void Resolve(ResolutionContext rc) { if (Lhss.Count != Rhss.Count) rc.Error(this, "number of left-hand sides does not match number of right-hand sides"); foreach (AssignLhs/*!*/ e in Lhss) { Contract.Assert(e != null); e.Resolve(rc); } foreach (Expr/*!*/ e in Rhss) { Contract.Assert(e != null); e.Resolve(rc); } // check for double occurrences of assigned variables // (could be optimised) for (int i = 0; i < Lhss.Count; ++i) { for (int j = i + 1; j < Lhss.Count; ++j) { if (cce.NonNull(Lhss[i].DeepAssignedVariable).Equals( Lhss[j].DeepAssignedVariable)) rc.Error(Lhss[j], "variable {0} is assigned more than once in parallel assignment", Lhss[j].DeepAssignedVariable); } } for (int i = 0; i < Lhss.Count; i++) { var lhs = Lhss[i].AsExpr as IdentifierExpr; if (lhs != null && lhs.Decl != null && QKeyValue.FindBoolAttribute(lhs.Decl.Attributes, "assumption")) { var rhs = Rhss[i] as NAryExpr; if (rhs == null || !(rhs.Fun is BinaryOperator) || ((BinaryOperator)(rhs.Fun)).Op != BinaryOperator.Opcode.And || !(rhs.Args[0] is IdentifierExpr) || ((IdentifierExpr)(rhs.Args[0])).Name != lhs.Name) { rc.Error(tok, string.Format("RHS of assignment to assumption variable {0} must match expression \"{0} && \"", lhs.Name)); } else if (rc.HasVariableBeenAssigned(lhs.Decl.Name)) { rc.Error(tok, "assumption variable may not be assigned to more than once"); } else { rc.MarkVariableAsAssigned(lhs.Decl.Name); } } } } public override void Typecheck(TypecheckingContext tc) { foreach (AssignLhs/*!*/ e in Lhss) { Contract.Assert(e != null); e.Typecheck(tc); } foreach (Expr/*!*/ e in Rhss) { Contract.Assert(e != null); e.Typecheck(tc); } this.CheckAssignments(tc); for (int i = 0; i < Lhss.Count; ++i) { Type ltype = Lhss[i].Type; Type rtype = Rhss[i].Type; if (ltype != null && rtype != null) { // otherwise, there has already been an error when // typechecking the lhs or rhs if (!ltype.Unify(rtype)) tc.Error(Lhss[i], "mismatched types in assignment command (cannot assign {0} to {1})", rtype, ltype); } } } public override void AddAssignedVariables(List vars) { foreach (AssignLhs/*!*/ l in Lhss) { Contract.Assert(l != null); vars.Add(l.DeepAssignedVariable); } } // transform away the syntactic sugar of map assignments and // determine an equivalent assignment in which all rhs are simple // variables public AssignCmd/*!*/ AsSimpleAssignCmd { get { Contract.Ensures(Contract.Result() != null); List/*!*/ newLhss = new List(); List/*!*/ newRhss = new List(); for (int i = 0; i < Lhss.Count; ++i) { IdentifierExpr/*!*/ newLhs; Expr/*!*/ newRhs; Lhss[i].AsSimpleAssignment(Rhss[i], out newLhs, out newRhs); newLhss.Add(new SimpleAssignLhs(Token.NoToken, newLhs)); newRhss.Add(newRhs); } return new AssignCmd(Token.NoToken, newLhss, newRhss); } } public override Absy StdDispatch(StandardVisitor visitor) { return visitor.VisitAssignCmd(this); } } // There are two different kinds of left-hand sides in assignments: // simple variables (identifiers), or locations of a map [ContractClass(typeof(AssignLhsContracts))] public abstract class AssignLhs : Absy { // The type of the lhs is determined during typechecking public abstract Type Type { get; } // Determine the variable that is actually assigned in this lhs public abstract IdentifierExpr/*!*/ DeepAssignedIdentifier { get; } public abstract Variable DeepAssignedVariable { get; } public AssignLhs(IToken/*!*/ tok) : base(tok) { Contract.Requires(tok != null); } public abstract void Emit(TokenTextWriter/*!*/ stream); public abstract Expr/*!*/ AsExpr { get; } // transform away the syntactic sugar of map assignments and // determine an equivalent simple assignment internal abstract void AsSimpleAssignment(Expr/*!*/ rhs, out IdentifierExpr/*!*/ simpleLhs, out Expr/*!*/ simpleRhs); } [ContractClassFor(typeof(AssignLhs))] public abstract class AssignLhsContracts : AssignLhs { public AssignLhsContracts():base(null) { }public override IdentifierExpr DeepAssignedIdentifier { get { Contract.Ensures(Contract.Result() != null); throw new NotImplementedException(); } } public override Expr AsExpr { get { Contract.Ensures(Contract.Result() != null); throw new NotImplementedException(); } } internal override void AsSimpleAssignment(Expr rhs, out IdentifierExpr simpleLhs, out Expr simpleRhs) { Contract.Requires(rhs != null); Contract.Ensures(Contract.ValueAtReturn(out simpleLhs) != null); Contract.Ensures(Contract.ValueAtReturn(out simpleRhs) != null); throw new NotImplementedException(); } } public class SimpleAssignLhs : AssignLhs { public IdentifierExpr/*!*/ AssignedVariable; [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(AssignedVariable != null); } public override Type Type { get { return AssignedVariable.Type; } } public override IdentifierExpr/*!*/ DeepAssignedIdentifier { get { Contract.Ensures(Contract.Result() != null); return AssignedVariable; } } public override Variable DeepAssignedVariable { get { return AssignedVariable.Decl; } } public SimpleAssignLhs(IToken tok, IdentifierExpr assignedVariable) : base(tok) { Contract.Requires(assignedVariable != null); Contract.Requires(tok != null); AssignedVariable = assignedVariable; } public override void Resolve(ResolutionContext rc) { AssignedVariable.Resolve(rc); } public override void Typecheck(TypecheckingContext tc) { AssignedVariable.Typecheck(tc); } public override void Emit(TokenTextWriter stream) { AssignedVariable.Emit(stream); } public override Expr/*!*/ AsExpr { get { Contract.Ensures(Contract.Result() != null); return AssignedVariable; } } internal override void AsSimpleAssignment(Expr rhs, out IdentifierExpr/*!*/ simpleLhs, out Expr/*!*/ simpleRhs) { simpleLhs = AssignedVariable; simpleRhs = rhs; } public override Absy StdDispatch(StandardVisitor visitor) { return visitor.VisitSimpleAssignLhs(this); } } // A map-assignment-lhs (m[t1, t2, ...] := ...) is quite similar to // a map select expression, but it is cleaner to keep those two // things separate public class MapAssignLhs : AssignLhs { public AssignLhs/*!*/ Map; public List/*!*/ Indexes; [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(Map != null); Contract.Invariant(cce.NonNullElements(Indexes)); } // The instantiation of type parameters of the map that is // determined during type checking. public TypeParamInstantiation TypeParameters = null; private Type TypeAttr = null; public override Type Type { get { return TypeAttr; } } public override IdentifierExpr/*!*/ DeepAssignedIdentifier { get { Contract.Ensures(Contract.Result() != null); return Map.DeepAssignedIdentifier; } } public override Variable DeepAssignedVariable { get { return Map.DeepAssignedVariable; } } public MapAssignLhs(IToken tok, AssignLhs map, List/*!*/ indexes) : base(tok) { Contract.Requires(map != null); Contract.Requires(tok != null); Contract.Requires(cce.NonNullElements(indexes)); Map = map; Indexes = indexes; } public override void Resolve(ResolutionContext rc) { Map.Resolve(rc); foreach (Expr/*!*/ e in Indexes) { Contract.Assert(e != null); e.Resolve(rc); } } public override void Typecheck(TypecheckingContext tc) { Map.Typecheck(tc); foreach (Expr/*!*/ e in Indexes) { Contract.Assert(e != null); e.Typecheck(tc); } // we use the same typechecking code as in MapSelect List/*!*/ selectArgs = new List(); foreach (Expr/*!*/ e in Indexes) { Contract.Assert(e != null); selectArgs.Add(e); } TypeParamInstantiation/*!*/ tpInsts; TypeAttr = MapSelect.Typecheck(cce.NonNull(Map.Type), Map, selectArgs, out tpInsts, tc, tok, "map assignment"); TypeParameters = tpInsts; } public override void Emit(TokenTextWriter stream) { Map.Emit(stream); stream.Write("["); string/*!*/ sep = ""; foreach (Expr/*!*/ e in Indexes) { Contract.Assert(e != null); stream.Write(sep); sep = ", "; e.Emit(stream); } stream.Write("]"); } public override Expr/*!*/ AsExpr { get { Contract.Ensures(Contract.Result() != null); NAryExpr/*!*/ res = Expr.Select(Map.AsExpr, Indexes); Contract.Assert(res != null); res.TypeParameters = this.TypeParameters; res.Type = this.Type; return res; } } internal override void AsSimpleAssignment(Expr rhs, out IdentifierExpr/*!*/ simpleLhs, out Expr/*!*/ simpleRhs) { //Contract.Requires(rhs != null); Contract.Ensures(Contract.ValueAtReturn(out simpleLhs) != null); Contract.Ensures(Contract.ValueAtReturn(out simpleRhs) != null); NAryExpr/*!*/ newRhs = Expr.Store(Map.AsExpr, Indexes, rhs); Contract.Assert(newRhs != null); newRhs.TypeParameters = this.TypeParameters; newRhs.Type = Map.Type; Map.AsSimpleAssignment(newRhs, out simpleLhs, out simpleRhs); } public override Absy StdDispatch(StandardVisitor visitor) { //Contract.Requires(visitor != null); Contract.Ensures(Contract.Result() != null); return visitor.VisitMapAssignLhs(this); } } /// /// A StateCmd is like an imperative-let binding around a sequence of commands. /// There is no user syntax for a StateCmd. Instead, a StateCmd is only used /// temporarily during the desugaring phase inside the VC generator. /// public class StateCmd : Cmd { [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(this._locals != null); Contract.Invariant(this._cmds != null); } private List _locals; public /*readonly, except for the StandardVisitor*/ List/*!*/ Locals { get { Contract.Ensures(Contract.Result>() != null); return this._locals; } internal set { Contract.Requires(value != null); this._locals = value; } } private List _cmds; public /*readonly, except for the StandardVisitor*/ List/*!*/ Cmds { get { Contract.Ensures(Contract.Result>() != null); return this._cmds; } set { Contract.Requires(value != null); this._cmds = value; } } public StateCmd(IToken tok, List/*!*/ locals, List/*!*/ cmds) : base(tok) { Contract.Requires(locals != null); Contract.Requires(cmds != null); Contract.Requires(tok != null); this._locals = locals; this._cmds = cmds; } public override void Resolve(ResolutionContext rc) { //Contract.Requires(rc != null); rc.PushVarContext(); foreach (Variable/*!*/ v in Locals) { Contract.Assert(v != null); rc.AddVariable(v, false); } foreach (Cmd/*!*/ cmd in Cmds) { Contract.Assert(cmd != null); cmd.Resolve(rc); } rc.PopVarContext(); } public override void AddAssignedVariables(List vars) { //Contract.Requires(vars != null); List/*!*/ vs = new List(); foreach (Cmd/*!*/ cmd in this.Cmds) { Contract.Assert(cmd != null); cmd.AddAssignedVariables(vs); } System.Collections.Hashtable/*!*/ localsSet = new System.Collections.Hashtable(); foreach (Variable/*!*/ local in this.Locals) { Contract.Assert(local != null); localsSet[local] = bool.TrueString; } foreach (Variable/*!*/ v in vs) { Contract.Assert(v != null); if (!localsSet.ContainsKey(v)) { vars.Add(v); } } } public override void Typecheck(TypecheckingContext tc) { //Contract.Requires(tc != null); foreach (Cmd/*!*/ cmd in Cmds) { Contract.Assert(cmd != null); cmd.Typecheck(tc); } } public override void Emit(TokenTextWriter stream, int level) { //Contract.Requires(stream != null); stream.WriteLine(this, level, "{"); foreach (Variable/*!*/ v in Locals) { Contract.Assert(v != null); v.Emit(stream, level + 1); } foreach (Cmd/*!*/ c in Cmds) { Contract.Assert(c != null); c.Emit(stream, level + 1); } stream.WriteLine(level, "}"); } public override Absy StdDispatch(StandardVisitor visitor) { //Contract.Requires(visitor != null); Contract.Ensures(Contract.Result() != null); return visitor.VisitStateCmd(this); } } [ContractClass(typeof(SugaredCmdContracts))] abstract public class SugaredCmd : Cmd { private Cmd desugaring; // null until desugared public SugaredCmd(IToken/*!*/ tok) : base(tok) { Contract.Requires(tok != null); } public Cmd/*!*/ Desugaring { get { Contract.Ensures(Contract.Result() != null); if (desugaring == null) { desugaring = ComputeDesugaring(); } return desugaring; } } /// /// This method invokes "visitor.Visit" on the desugaring, and then updates the /// desugaring to the result thereof. The method's intended use is for subclasses /// of StandardVisitor that need to also visit the desugaring. Note, since the /// "desugaring" field is updated, this is not an appropriate method to be called /// be a ReadOnlyVisitor; such visitors should instead just call /// visitor.Visit(sugaredCmd.Desugaring). /// public void VisitDesugaring(StandardVisitor visitor) { Contract.Requires(visitor != null && !(visitor is ReadOnlyVisitor)); if (desugaring != null) { desugaring = (Cmd)visitor.Visit(desugaring); } } protected abstract Cmd/*!*/ ComputeDesugaring(); public void ExtendDesugaring(IEnumerable before, IEnumerable beforePreconditionCheck, IEnumerable after) { var desug = Desugaring; var stCmd = desug as StateCmd; if (stCmd != null) { stCmd.Cmds.InsertRange(0, before); var idx = stCmd.Cmds.FindIndex(c => c is AssertCmd || c is HavocCmd || c is AssumeCmd); if (idx < 0) { idx = 0; } stCmd.Cmds.InsertRange(idx, beforePreconditionCheck); stCmd.Cmds.AddRange(after); } else if (desug != null) { var cmds = new List(before); cmds.Add(desug); cmds.AddRange(after); desugaring = new StateCmd(Token.NoToken, new List(), cmds); } } public override void Emit(TokenTextWriter stream, int level) { //Contract.Requires(stream != null); if (CommandLineOptions.Clo.PrintDesugarings && !stream.UseForComputingChecksums) { stream.WriteLine(this, level, "/*** desugaring:"); Desugaring.Emit(stream, level); stream.WriteLine(level, "**** end desugaring */"); } } } [ContractClassFor(typeof(SugaredCmd))] public abstract class SugaredCmdContracts : SugaredCmd { public SugaredCmdContracts() :base(null){ } protected override Cmd ComputeDesugaring() { Contract.Ensures(Contract.Result() != null); throw new NotImplementedException(); } } public abstract class CallCommonality : SugaredCmd { public QKeyValue Attributes; private bool isFree = false; public bool IsFree { get { return isFree; } set { isFree = value; } } private bool isAsync = false; public bool IsAsync { get { return isAsync; } set { isAsync = value; } } protected CallCommonality(IToken tok, QKeyValue kv) : base(tok) { Contract.Requires(tok != null); Attributes = kv; } protected enum TempVarKind { Formal, Old, Bound } // We have to give the type explicitly, because the type of the formal "likeThisOne" can contain type variables protected Variable CreateTemporaryVariable(List tempVars, Variable likeThisOne, Type ty, TempVarKind kind, ref int uniqueId) { Contract.Requires(ty != null); Contract.Requires(likeThisOne != null); Contract.Requires(tempVars != null); Contract.Ensures(Contract.Result() != null); string/*!*/ tempNamePrefix; switch (kind) { case TempVarKind.Formal: tempNamePrefix = "formal@"; break; case TempVarKind.Old: tempNamePrefix = "old@"; break; case TempVarKind.Bound: tempNamePrefix = "forall@"; break; default: { Contract.Assert(false); throw new cce.UnreachableException(); } // unexpected kind } TypedIdent ti = likeThisOne.TypedIdent; // KLM: uniqueId was messing up FixedPointVC for unknown reason. // I reverted this change for FixedPointVC only. int id = CommandLineOptions.Clo.FixedPointEngine != null ? UniqueId : (uniqueId++); TypedIdent newTi = new TypedIdent(ti.tok, "call" + id + tempNamePrefix + ti.Name, ty); Variable/*!*/ v; if (kind == TempVarKind.Bound) { v = new BoundVariable(likeThisOne.tok, newTi); } else { v = new LocalVariable(likeThisOne.tok, newTi); tempVars.Add(v); } return v; } } public class ParCallCmd : CallCommonality, IPotentialErrorNode { public List CallCmds; public ParCallCmd(IToken tok, List callCmds) : base(tok, null) { this.CallCmds = callCmds; } public ParCallCmd(IToken tok, List callCmds, QKeyValue kv) : base(tok, kv) { this.CallCmds = callCmds; } protected override Cmd ComputeDesugaring() { throw new NotImplementedException(); } private object errorData; public object ErrorData { get { return errorData; } set { errorData = value; } } public override void Resolve(ResolutionContext rc) { ResolveAttributes(Attributes, rc); foreach (CallCmd callCmd in CallCmds) { callCmd.Resolve(rc); } HashSet parallelCallLhss = new HashSet(); foreach (CallCmd callCmd in CallCmds) { foreach (IdentifierExpr ie in callCmd.Outs) { if (parallelCallLhss.Contains(ie.Decl)) { rc.Error(this, "left-hand side of parallel call command contains variable twice: {0}", ie.Name); } else { parallelCallLhss.Add(ie.Decl); } } } } public override void Typecheck(TypecheckingContext tc) { TypecheckAttributes(Attributes, tc); if (!CommandLineOptions.Clo.DoModSetAnalysis) { if (!tc.Yields) { tc.Error(this, "enclosing procedure of a parallel call must yield"); } foreach (CallCmd callCmd in CallCmds) { if (!QKeyValue.FindBoolAttribute(callCmd.Proc.Attributes, "yields")) { tc.Error(callCmd, "target procedure of a parallel call must yield"); } } } foreach (CallCmd callCmd in CallCmds) { callCmd.Typecheck(tc); } } public override void AddAssignedVariables(List vars) { foreach (CallCmd callCmd in CallCmds) { callCmd.AddAssignedVariables(vars); } } public override Absy StdDispatch(StandardVisitor visitor) { //Contract.Requires(visitor != null); Contract.Ensures(Contract.Result() != null); return visitor.VisitParCallCmd(this); } } public class CallCmd : CallCommonality, IPotentialErrorNode { public string/*!*/ callee { get; set; } public Procedure Proc; public LocalVariable AssignedAssumptionVariable; // Element of the following lists can be null, which means that // the call happens with * as these parameters public List/*!*/ Ins; public List/*!*/ Outs; [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(callee != null); Contract.Invariant(Ins != null); Contract.Invariant(Outs != null); } //public Lattice.Element StateAfterCall; // The instantiation of type parameters that is determined during // type checking public TypeParamInstantiation TypeParameters = null; // TODO: convert to use generics private object errorData; public object ErrorData { get { return errorData; } set { errorData = value; } } public CallCmd(IToken tok, string callee, List ins, List outs) : base(tok, null) { Contract.Requires(outs != null); Contract.Requires(ins != null); Contract.Requires(callee != null); Contract.Requires(tok != null); this.callee = callee; this.Ins = ins; this.Outs = outs; } public CallCmd(IToken tok, string callee, List ins, List outs, QKeyValue kv) : base(tok, kv) { Contract.Requires(outs != null); Contract.Requires(ins != null); Contract.Requires(callee != null); Contract.Requires(tok != null); this.callee = callee; this.Ins = ins; this.Outs = outs; } public CallCmd(IToken tok, string callee, List ins, List outs, QKeyValue kv, bool IsAsync) : base(tok, kv) { Contract.Requires(outs != null); Contract.Requires(ins != null); Contract.Requires(callee != null); Contract.Requires(tok != null); this.callee = callee; this.Ins = ins; this.Outs = outs; this.IsAsync = IsAsync; } public override void Emit(TokenTextWriter stream, int level) { //Contract.Requires(stream != null); stream.Write(this, level, ""); if (IsFree) { stream.Write("free "); } if (IsAsync) { stream.Write("async "); } stream.Write("call "); EmitAttributes(stream, Attributes); string sep = ""; if (Outs.Count > 0) { foreach (Expr arg in Outs) { stream.Write(sep); sep = ", "; if (arg == null) { stream.Write("*"); } else { arg.Emit(stream); } } stream.Write(" := "); } stream.Write(TokenTextWriter.SanitizeIdentifier(callee)); stream.Write("("); sep = ""; foreach (Expr arg in Ins) { stream.Write(sep); sep = ", "; if (arg == null) { stream.Write("*"); } else { arg.Emit(stream); } } stream.WriteLine(");"); base.Emit(stream, level); } public override void Resolve(ResolutionContext rc) { //Contract.Requires(rc != null); if (Proc != null) { // already resolved return; } ResolveAttributes(Attributes, rc); Proc = rc.LookUpProcedure(callee) as Procedure; if (Proc == null) { rc.Error(this, "call to undeclared procedure: {0}", callee); } foreach (Expr e in Ins) { if (e != null) { e.Resolve(rc); } } HashSet actualOuts = new HashSet(); foreach (IdentifierExpr ide in Outs) { if (ide != null) { ide.Resolve(rc); if (ide.Decl != null) { if (actualOuts.Contains(ide.Decl)) { rc.Error(this, "left-hand side of call command contains variable twice: {0}", ide.Name); } else { actualOuts.Add(ide.Decl); } } } } if (Proc == null) return; // first make sure that the right number of parameters is given // (a similar check is in CheckArgumentTypes, but we are not // able to call this method because it cannot cope with Ins/Outs // that are null) if (Ins.Count != Proc.InParams.Count) { rc.Error(this.tok, "wrong number of arguments in call to {0}: {1}", callee, Ins.Count); return; } if (Outs.Count != Proc.OutParams.Count) { rc.Error(this.tok, "wrong number of result variables in call to {0}: {1}", callee, Outs.Count); return; } if (IsAsync) { if (Proc.OutParams.Count > 0) { rc.Error(this.tok, "a procedure called asynchronously can have no output parameters"); return; } } // Check that type parameters can be determined using the given // actual i/o arguments. This is done already during resolution // because CheckBoundVariableOccurrences needs a resolution // context List/*!*/ formalInTypes = new List(); List/*!*/ formalOutTypes = new List(); for (int i = 0; i < Ins.Count; ++i) if (Ins[i] != null) formalInTypes.Add(cce.NonNull(Proc.InParams[i]).TypedIdent.Type); for (int i = 0; i < Outs.Count; ++i) if (Outs[i] != null) formalOutTypes.Add(cce.NonNull(Proc.OutParams[i]).TypedIdent.Type); // we need to bind the type parameters for this // (this is expected by CheckBoundVariableOccurrences) int previousTypeBinderState = rc.TypeBinderState; try { foreach (TypeVariable/*!*/ v in Proc.TypeParameters) { Contract.Assert(v != null); rc.AddTypeBinder(v); } Type.CheckBoundVariableOccurrences(Proc.TypeParameters, formalInTypes, formalOutTypes, this.tok, "types of given arguments", rc); } finally { rc.TypeBinderState = previousTypeBinderState; } } public override void AddAssignedVariables(List vars) { if (this.IsAsync) return; foreach (IdentifierExpr e in Outs) { if (e != null) { vars.Add(e.Decl); } } Contract.Assume(this.Proc != null); foreach (IdentifierExpr/*!*/ e in this.Proc.Modifies) { Contract.Assert(e != null); vars.Add(e.Decl); } if (AssignedAssumptionVariable != null) { vars.Add(AssignedAssumptionVariable); } } public override void Typecheck(TypecheckingContext tc) { //Contract.Requires(tc != null); Contract.Assume(this.Proc != null); // we assume the CallCmd has been successfully resolved before calling this Typecheck method TypecheckAttributes(Attributes, tc); // typecheck in-parameters foreach (Expr e in Ins) if (e != null) e.Typecheck(tc); foreach (Expr e in Outs) if (e != null) e.Typecheck(tc); this.CheckAssignments(tc); List/*!*/ formalInTypes = new List(); List/*!*/ formalOutTypes = new List(); List/*!*/ actualIns = new List(); List/*!*/ actualOuts = new List(); for (int i = 0; i < Ins.Count; ++i) { if (Ins[i] != null) { formalInTypes.Add(cce.NonNull(Proc.InParams[i]).TypedIdent.Type); actualIns.Add(Ins[i]); } } for (int i = 0; i < Outs.Count; ++i) { if (Outs[i] != null) { formalOutTypes.Add(cce.NonNull(Proc.OutParams[i]).TypedIdent.Type); actualOuts.Add(Outs[i]); } } // match actuals with formals List/*!*/ actualTypeParams; Type.CheckArgumentTypes(Proc.TypeParameters, out actualTypeParams, formalInTypes, actualIns, formalOutTypes, actualOuts, this.tok, "call to " + callee, tc); Contract.Assert(cce.NonNullElements(actualTypeParams)); TypeParameters = SimpleTypeParamInstantiation.From(Proc.TypeParameters, actualTypeParams); if (!CommandLineOptions.Clo.DoModSetAnalysis && IsAsync) { if (!tc.Yields) { tc.Error(this, "enclosing procedure of an async call must yield"); } if (!QKeyValue.FindBoolAttribute(Proc.Attributes, "yields")) { tc.Error(this, "target procedure of an async call must yield"); } } } private IDictionary/*!*/ TypeParamSubstitution() { Contract.Ensures(cce.NonNullDictionaryAndValues(Contract.Result>())); Contract.Assume(TypeParameters != null); IDictionary/*!*/ res = new Dictionary(); foreach (TypeVariable/*!*/ v in TypeParameters.FormalTypeParams) { Contract.Assert(v != null); res.Add(v, TypeParameters[v]); } return res; } protected override Cmd ComputeDesugaring() { Contract.Ensures(Contract.Result() != null); int uniqueId = 0; List newBlockBody = new List(); Dictionary substMap = new Dictionary(); Dictionary substMapOld = new Dictionary(); Dictionary substMapBound = new Dictionary(); List/*!*/ tempVars = new List(); // proc P(ins) returns (outs) // requires Pre // //modifies frame // ensures Post // // call aouts := P(ains) // ins : formal in parameters of procedure // frame : a list of global variables from the modifies clause // outs : formal out parameters of procedure // ains : actual in arguments passed to call // aouts : actual variables assigned to from call // cins : new variables created just for this call, one per ains // cframe : new variables created just for this call, to keep track of OLD values // couts : new variables created just for this call, one per aouts // WildcardVars : new variables created just for this call, one per null in ains #region Create cins; each one is an incarnation of the corresponding in parameter List/*!*/ cins = new List(); List wildcardVars = new List(); Contract.Assume(this.Proc != null); for (int i = 0; i < this.Proc.InParams.Count; ++i) { Variable/*!*/ param = cce.NonNull(this.Proc.InParams[i]); bool isWildcard = this.Ins[i] == null; Type/*!*/ actualType; if (isWildcard) actualType = param.TypedIdent.Type.Substitute(TypeParamSubstitution()); else // during type checking, we have ensured that the type of the actual // parameter Ins[i] is correct, so we can use it here actualType = cce.NonNull(cce.NonNull(Ins[i]).Type); Variable cin = CreateTemporaryVariable(tempVars, param, actualType, TempVarKind.Formal, ref uniqueId); cins.Add(cin); IdentifierExpr ie = new IdentifierExpr(cin.tok, cin); substMap.Add(param, ie); if (isWildcard) { cin = CreateTemporaryVariable(tempVars, param, actualType, TempVarKind.Bound, ref uniqueId); wildcardVars.Add(cin); ie = new IdentifierExpr(cin.tok, cin); } substMapBound.Add(param, ie); } #endregion #region call aouts := P(ains) becomes: (open outlining one level to see) #region cins := ains (or havoc cin when ain is null) for (int i = 0, n = this.Ins.Count; i < n; i++) { IdentifierExpr/*!*/ cin_exp = new IdentifierExpr(cce.NonNull(cins[i]).tok, cce.NonNull(cins[i])); Contract.Assert(cin_exp != null); if (this.Ins[i] != null) { AssignCmd assign = Cmd.SimpleAssign(Token.NoToken, cin_exp, cce.NonNull(this.Ins[i])); newBlockBody.Add(assign); } else { List/*!*/ ies = new List(); ies.Add(cin_exp); HavocCmd havoc = new HavocCmd(Token.NoToken, ies); newBlockBody.Add(havoc); } } #endregion #region assert (exists wildcardVars :: Pre[ins := cins]) Substitution s = Substituter.SubstitutionFromHashtable(substMapBound); bool hasWildcard = (wildcardVars.Count != 0); Expr preConjunction = null; for (int i = 0; i < this.Proc.Requires.Count; i++) { Requires/*!*/ req = cce.NonNull(this.Proc.Requires[i]); if (!req.Free && !IsFree) { if (hasWildcard) { Expr pre = Substituter.Apply(s, req.Condition); if (preConjunction == null) { preConjunction = pre; } else { preConjunction = Expr.And(preConjunction, pre); } } else { Requires/*!*/ reqCopy = (Requires/*!*/)cce.NonNull(req.Clone()); reqCopy.Condition = Substituter.Apply(s, req.Condition); AssertCmd/*!*/ a = new AssertRequiresCmd(this, reqCopy); Contract.Assert(a != null); a.ErrorDataEnhanced = reqCopy.ErrorDataEnhanced; newBlockBody.Add(a); } } else if (CommandLineOptions.Clo.StratifiedInlining > 0) { // inject free requires as assume statements at the call site AssumeCmd/*!*/ a = new AssumeCmd(req.tok, Substituter.Apply(s, req.Condition)); Contract.Assert(a != null); newBlockBody.Add(a); } } if (hasWildcard) { if (preConjunction == null) { preConjunction = Expr.True; } Expr/*!*/ expr = new ExistsExpr(tok, wildcardVars, preConjunction); Contract.Assert(expr != null); AssertCmd/*!*/ a = new AssertCmd(tok, expr); Contract.Assert(a != null); a.ErrorDataEnhanced = AssertCmd.GenerateBoundVarMiningStrategy(expr); newBlockBody.Add(a); } #endregion #region assume Pre[ins := cins] with formal paramters if (hasWildcard) { s = Substituter.SubstitutionFromHashtable(substMap); for (int i = 0; i < this.Proc.Requires.Count; i++) { Requires/*!*/ req = cce.NonNull(this.Proc.Requires[i]); if (!req.Free) { Requires/*!*/ reqCopy = (Requires/*!*/)cce.NonNull(req.Clone()); reqCopy.Condition = Substituter.Apply(s, req.Condition); AssumeCmd/*!*/ a = new AssumeCmd(tok, reqCopy.Condition); Contract.Assert(a != null); newBlockBody.Add(a); } } } #endregion #region cframe := frame (to hold onto frame values in case they are referred to in the postcondition) List havocVarExprs = new List(); foreach (IdentifierExpr/*!*/ f in this.Proc.Modifies) { Contract.Assert(f != null); Contract.Assume(f.Decl != null); Contract.Assert(f.Type != null); Variable v = CreateTemporaryVariable(tempVars, f.Decl, f.Type, TempVarKind.Old, ref uniqueId); IdentifierExpr v_exp = new IdentifierExpr(v.tok, v); substMapOld.Add(f.Decl, v_exp); // this assumes no duplicates in this.Proc.Modifies AssignCmd assign = Cmd.SimpleAssign(f.tok, v_exp, f); newBlockBody.Add(assign); // fra if (!havocVarExprs.Contains(f)) havocVarExprs.Add(f); } #endregion #region Create couts List/*!*/ couts = new List(); for (int i = 0; i < this.Proc.OutParams.Count; ++i) { Variable/*!*/ param = cce.NonNull(this.Proc.OutParams[i]); bool isWildcard = this.Outs[i] == null; Type/*!*/ actualType; if (isWildcard) actualType = param.TypedIdent.Type.Substitute(TypeParamSubstitution()); else // during type checking, we have ensured that the type of the actual // out parameter Outs[i] is correct, so we can use it here actualType = cce.NonNull(cce.NonNull(Outs[i]).Type); Variable cout = CreateTemporaryVariable(tempVars, param, actualType, TempVarKind.Formal, ref uniqueId); couts.Add(cout); IdentifierExpr ie = new IdentifierExpr(cout.tok, cout); substMap.Add(param, ie); if (!havocVarExprs.Contains(ie)) havocVarExprs.Add(ie); } // add the where clauses, now that we have the entire substitution map foreach (Variable/*!*/ param in this.Proc.OutParams) { Contract.Assert(param != null); Expr w = param.TypedIdent.WhereExpr; if (w != null) { IdentifierExpr ie = (IdentifierExpr/*!*/)cce.NonNull(substMap[param]); Contract.Assert(ie.Decl != null); ie.Decl.TypedIdent.WhereExpr = Substituter.Apply(Substituter.SubstitutionFromHashtable(substMap), w); } } #endregion #region havoc frame, couts // pass on this's token HavocCmd hc = new HavocCmd(this.tok, havocVarExprs); newBlockBody.Add(hc); #endregion #region assume Post[ins, outs, old(frame) := cins, couts, cframe] calleeSubstitution = Substituter.SubstitutionFromHashtable(substMap, true, Proc); calleeSubstitutionOld = Substituter.SubstitutionFromHashtable(substMapOld, true, Proc); foreach (Ensures/*!*/ e in this.Proc.Ensures) { Contract.Assert(e != null); Expr copy = Substituter.ApplyReplacingOldExprs(calleeSubstitution, calleeSubstitutionOld, e.Condition); AssumeCmd assume = new AssumeCmd(this.tok, copy); #region stratified inlining support if (QKeyValue.FindBoolAttribute(e.Attributes, "si_fcall")) { assume.Attributes = Attributes; } if (QKeyValue.FindBoolAttribute(e.Attributes, "candidate")) { assume.Attributes = new QKeyValue(Token.NoToken, "candidate", new List(), assume.Attributes); assume.Attributes.AddParam(this.callee); } #endregion newBlockBody.Add(assume); } #endregion #region aouts := couts for (int i = 0, n = this.Outs.Count; i < n; i++) { if (this.Outs[i] != null) { Variable/*!*/ param_i = cce.NonNull(this.Proc.OutParams[i]); Expr/*!*/ cout_exp = new IdentifierExpr(cce.NonNull(couts[i]).tok, cce.NonNull(couts[i])); Contract.Assert(cout_exp != null); AssignCmd assign = Cmd.SimpleAssign(param_i.tok, cce.NonNull(this.Outs[i]), cout_exp); newBlockBody.Add(assign); } } #endregion #endregion return new StateCmd(this.tok, tempVars, newBlockBody); } class NameEqualityComparer : EqualityComparer { public override bool Equals(IdentifierExpr x, IdentifierExpr y) { return x.Name.Equals(y.Name); } public override int GetHashCode(IdentifierExpr obj) { return obj.Name.GetHashCode(); } } NameEqualityComparer comparer = new NameEqualityComparer(); public Substitution calleeSubstitution; public Substitution calleeSubstitutionOld; public IEnumerable UnmodifiedBefore(Procedure oldProcedure) { Contract.Requires(oldProcedure != null); return Proc.Modifies.Except(oldProcedure.Modifies, comparer).Select(e => new IdentifierExpr(Token.NoToken, e.Decl)); } public IEnumerable ModifiedBefore(Procedure oldProcedure) { Contract.Requires(oldProcedure != null); return oldProcedure.Modifies.Except(Proc.Modifies, comparer).Select(e => new IdentifierExpr(Token.NoToken, e.Decl)); } public Expr Postcondition(Procedure procedure, List modifies, Dictionary oldSubst, Program program, Func extract) { Contract.Requires(calleeSubstitution != null && calleeSubstitutionOld != null && modifies != null && oldSubst != null && program != null && extract != null); Substitution substOldCombined = v => { Expr s; if (oldSubst.TryGetValue(v, out s)) { return s; } return calleeSubstitutionOld(v); }; var clauses = procedure.Ensures.Select(e => Substituter.FunctionCallReresolvingApplyReplacingOldExprs(calleeSubstitution, substOldCombined, e.Condition, program)).Concat(modifies); // TODO(wuestholz): Try extracting a function for each clause: // return Conjunction(clauses.Select(c => extract(c))); var conj = Conjunction(clauses); return conj != null ? extract(conj) : conj; } public Expr CheckedPrecondition(Procedure procedure, Program program, Func extract) { Contract.Requires(calleeSubstitution != null && calleeSubstitutionOld != null && program != null && extract != null); var clauses = procedure.Requires.Where(r => !r.Free).Select(r => Substituter.FunctionCallReresolvingApplyReplacingOldExprs(calleeSubstitution, calleeSubstitutionOld, r.Condition, program)); // TODO(wuestholz): Try extracting a function for each clause: // return Conjunction(clauses.Select(c => extract(c))); var conj = Conjunction(clauses); return conj != null ? extract(conj) : conj; } private static Expr Conjunction(IEnumerable conjuncts) { // TODO(wuestholz): Maybe we should use 'LiteralExpr.BinaryTreeAnd' instead. Expr result = null; foreach (var c in conjuncts) { if (result != null) { result = LiteralExpr.And(result, c); result.Type = Type.Bool; } else { result = c; result.Type = Type.Bool; } } return result; } public override Absy StdDispatch(StandardVisitor visitor) { //Contract.Requires(visitor != null); Contract.Ensures(Contract.Result() != null); return visitor.VisitCallCmd(this); } } public abstract class PredicateCmd : Cmd { public QKeyValue Attributes; public /*readonly--except in StandardVisitor*/ Expr/*!*/ Expr; [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(Expr != null); } public PredicateCmd(IToken/*!*/ tok, Expr/*!*/ expr) : base(tok) { Contract.Requires(tok != null); Contract.Requires(expr != null); Expr = expr; } public PredicateCmd(IToken/*!*/ tok, Expr/*!*/ expr, QKeyValue kv) : base(tok) { Contract.Requires(tok != null); Contract.Requires(expr != null); Expr = expr; Attributes = kv; } public override void Resolve(ResolutionContext rc) { //Contract.Requires(rc != null); Expr.Resolve(rc); } public override void AddAssignedVariables(List vars) { //Contract.Requires(vars != null); } } public abstract class MiningStrategy { // abstract class to bind all MiningStrategys, i.e., all types of enhanced error data // types together } public class ListOfMiningStrategies : MiningStrategy { private List/*!*/ _msList; public List/*!*/ msList { get { Contract.Ensures(Contract.Result>() != null); return this._msList; } set { Contract.Requires(value != null); this._msList = value; } } [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(this._msList != null); } public ListOfMiningStrategies(List l) { Contract.Requires(l != null); this._msList = l; } } public class EEDTemplate : MiningStrategy { private string/*!*/ _reason; public string/*!*/ reason { get { Contract.Ensures(Contract.Result() != null); return this._reason; } set { Contract.Requires(value != null); this._reason = value; } } private List/*!*/ exprList; public IEnumerable Expressions { get { Contract.Ensures(cce.NonNullElements(Contract.Result>())); return this.exprList.AsReadOnly(); } set { Contract.Requires(cce.NonNullElements(value)); this.exprList = new List(value); } } [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(this._reason != null); Contract.Invariant(cce.NonNullElements(this.exprList)); } public EEDTemplate(string reason, List/*!*/ exprList) { Contract.Requires(reason != null); Contract.Requires(cce.NonNullElements(exprList)); this._reason = reason; this.exprList = exprList; } } public class AssertCmd : PredicateCmd, IPotentialErrorNode { public Expr OrigExpr; public Dictionary IncarnationMap; Expr verifiedUnder; public Expr VerifiedUnder { get { if (verifiedUnder != null) { return verifiedUnder; } verifiedUnder = QKeyValue.FindExprAttribute(Attributes, "verified_under"); return verifiedUnder; } } public void MarkAsVerifiedUnder(Expr expr) { Attributes = new QKeyValue(tok, "verified_under", new List { expr }, Attributes); verifiedUnder = expr; } // TODO: convert to use generics private object errorData; public object ErrorData { get { return errorData; } set { errorData = value; } } public string ErrorMessage { get { return QKeyValue.FindStringAttribute(Attributes, "msg"); } } private MiningStrategy errorDataEnhanced; public MiningStrategy ErrorDataEnhanced { get { return errorDataEnhanced; } set { errorDataEnhanced = value; } } public AssertCmd(IToken/*!*/ tok, Expr/*!*/ expr) : base(tok, expr) { Contract.Requires(tok != null); Contract.Requires(expr != null); errorDataEnhanced = GenerateBoundVarMiningStrategy(expr); } public AssertCmd(IToken/*!*/ tok, Expr/*!*/ expr, QKeyValue kv) : base(tok, expr, kv) { Contract.Requires(tok != null); Contract.Requires(expr != null); errorDataEnhanced = GenerateBoundVarMiningStrategy(expr); } public override void Emit(TokenTextWriter stream, int level) { //Contract.Requires(stream != null); stream.Write(this, level, "assert "); EmitAttributes(stream, Attributes); this.Expr.Emit(stream); stream.WriteLine(";"); } public override void Resolve(ResolutionContext rc) { //Contract.Requires(rc != null); ResolveAttributes(Attributes, rc); base.Resolve(rc); } public override void Typecheck(TypecheckingContext tc) { //Contract.Requires(tc != null); TypecheckAttributes(Attributes, tc); Expr.Typecheck(tc); Contract.Assert(Expr.Type != null); // follows from Expr.Typecheck postcondition if (!Expr.Type.Unify(Type.Bool)) { tc.Error(this, "an asserted expression must be of type bool (got: {0})", Expr.Type); } } public static MiningStrategy GenerateBoundVarMiningStrategy(Expr expr) { Contract.Requires(expr != null); List l = new List(); if (expr != null) { l = GenerateBoundVarListForMining(expr, l); } return new ListOfMiningStrategies(l); } public static List/*!*/ GenerateBoundVarListForMining(Expr expr, List l) { Contract.Requires(l != null); Contract.Requires(expr != null); Contract.Ensures(Contract.Result>() != null); // go through the origExpr and identify all bound variables in the AST. if (expr is LiteralExpr || expr is IdentifierExpr) { //end recursion } else if (expr is NAryExpr) { NAryExpr e = (NAryExpr)expr; foreach (Expr/*!*/ arg in e.Args) { Contract.Assert(arg != null); l = GenerateBoundVarListForMining(arg, l); } } else if (expr is OldExpr) { OldExpr e = (OldExpr)expr; l = GenerateBoundVarListForMining(e.Expr, l); } else if (expr is QuantifierExpr) { QuantifierExpr qe = (QuantifierExpr)expr; List vs = qe.Dummies; foreach (Variable/*!*/ x in vs) { Contract.Assert(x != null); string name = x.Name; if (name.StartsWith("^")) { name = name.Substring(1); List exprList = new List(); exprList.Add(new IdentifierExpr(Token.NoToken, x.ToString(), x.TypedIdent.Type)); MiningStrategy eed = new EEDTemplate("The bound variable " + name + " has the value {0}.", exprList); l.Add(eed); } } l = GenerateBoundVarListForMining(qe.Body, l); } return l; } public override Absy StdDispatch(StandardVisitor visitor) { //Contract.Requires(visitor != null); Contract.Ensures(Contract.Result() != null); return visitor.VisitAssertCmd(this); } } // An AssertCmd that is a loop invariant check before the loop iteration starts public class LoopInitAssertCmd : AssertCmd { public LoopInitAssertCmd(IToken/*!*/ tok, Expr/*!*/ expr) : base(tok, expr) { Contract.Requires(tok != null); Contract.Requires(expr != null); } } // An AssertCmd that is a loop invariant check to maintain the invariant after iteration public class LoopInvMaintainedAssertCmd : AssertCmd { public LoopInvMaintainedAssertCmd(IToken/*!*/ tok, Expr/*!*/ expr) : base(tok, expr) { Contract.Requires(tok != null); Contract.Requires(expr != null); } } /// /// An AssertCmd that is introduced in translation from the requires on a call. /// public class AssertRequiresCmd : AssertCmd { public CallCmd/*!*/ Call; public Requires/*!*/ Requires; [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(Call != null); Contract.Invariant(Requires != null); } public AssertRequiresCmd(CallCmd/*!*/ call, Requires/*!*/ requires) : base(call.tok, requires.Condition) { Contract.Requires(call != null); Contract.Requires(requires != null); this.Call = call; this.Requires = requires; } public override Absy StdDispatch(StandardVisitor visitor) { //Contract.Requires(visitor != null); Contract.Ensures(Contract.Result() != null); return visitor.VisitAssertRequiresCmd(this); } } /// /// An AssertCmd that is introduced in translation from an ensures /// declaration. /// public class AssertEnsuresCmd : AssertCmd { public Ensures/*!*/ Ensures; [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(Ensures != null); } public AssertEnsuresCmd(Ensures/*!*/ ens) : base(ens.tok, ens.Condition) { Contract.Requires(ens != null); this.Ensures = ens; } public override Absy StdDispatch(StandardVisitor visitor) { //Contract.Requires(visitor != null); Contract.Ensures(Contract.Result() != null); return visitor.VisitAssertEnsuresCmd(this); } } public class AssumeCmd : PredicateCmd { public AssumeCmd(IToken/*!*/ tok, Expr/*!*/ expr) : base(tok, expr) { Contract.Requires(tok != null); Contract.Requires(expr != null); } public AssumeCmd(IToken/*!*/ tok, Expr/*!*/ expr, QKeyValue kv) : base(tok, expr, kv) { Contract.Requires(tok != null); Contract.Requires(expr != null); } public override void Emit(TokenTextWriter stream, int level) { //Contract.Requires(stream != null); if (stream.UseForComputingChecksums && QKeyValue.FindBoolAttribute(Attributes, "precondition_previous_snapshot")) { return; } stream.Write(this, level, "assume "); EmitAttributes(stream, Attributes); this.Expr.Emit(stream); stream.WriteLine(";"); } public override void Typecheck(TypecheckingContext tc) { //Contract.Requires(tc != null); Expr.Typecheck(tc); Contract.Assert(Expr.Type != null); // follows from Expr.Typecheck postcondition if (!Expr.Type.Unify(Type.Bool)) { tc.Error(this, "an assumed expression must be of type bool (got: {0})", Expr.Type); } } public override Absy StdDispatch(StandardVisitor visitor) { //Contract.Requires(visitor != null); Contract.Ensures(Contract.Result() != null); return visitor.VisitAssumeCmd(this); } } public class ReturnExprCmd : ReturnCmd { public Expr/*!*/ Expr; [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(Expr != null); } public ReturnExprCmd(IToken/*!*/ tok, Expr/*!*/ expr) : base(tok) { Contract.Requires(tok != null); Contract.Requires(expr != null); Expr = expr; } public override void Emit(TokenTextWriter stream, int level) { //Contract.Requires(stream != null); stream.Write(this, level, "return "); this.Expr.Emit(stream); stream.WriteLine(";"); } public override void Typecheck(TypecheckingContext tc) { //Contract.Requires(tc != null); Expr.Typecheck(tc); Contract.Assert(Expr.Type != null); // follows from Expr.Typecheck postcondition if (!Expr.Type.Unify(Type.Bool)) { tc.Error(this, "a return expression must be of type bool (got: {0})", Expr.Type); } } public override void Resolve(ResolutionContext rc) { //Contract.Requires(rc != null); Expr.Resolve(rc); } public override Absy StdDispatch(StandardVisitor visitor) { //Contract.Requires(visitor != null); Contract.Ensures(Contract.Result() != null); return visitor.VisitReturnExprCmd(this); } } public class HavocCmd : Cmd { private List/*!*/ _vars; public List/*!*/ Vars { get { Contract.Ensures(Contract.Result>() != null); return this._vars; } set { Contract.Requires(value != null); this._vars = value; } } [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(this._vars != null); } public HavocCmd(IToken/*!*/ tok, List/*!*/ vars) : base(tok) { Contract.Requires(tok != null); Contract.Requires(vars != null); this._vars = vars; } public override void Emit(TokenTextWriter stream, int level) { //Contract.Requires(stream != null); stream.Write(this, level, "havoc "); Vars.Emit(stream, true); stream.WriteLine(";"); } public override void Resolve(ResolutionContext rc) { //Contract.Requires(rc != null); foreach (IdentifierExpr/*!*/ ide in Vars) { Contract.Assert(ide != null); ide.Resolve(rc); } } public override void AddAssignedVariables(List vars) { //Contract.Requires(vars != null); foreach (IdentifierExpr/*!*/ e in this.Vars) { Contract.Assert(e != null); vars.Add(e.Decl); } } public override void Typecheck(TypecheckingContext tc) { //Contract.Requires(tc != null); foreach (IdentifierExpr ie in Vars) { ie.Typecheck(tc); } this.CheckAssignments(tc); } public override Absy StdDispatch(StandardVisitor visitor) { //Contract.Requires(visitor != null); Contract.Ensures(Contract.Result() != null); return visitor.VisitHavocCmd(this); } } //--------------------------------------------------------------------- // Transfer commands [ContractClass(typeof(TransferCmdContracts))] public abstract class TransferCmd : Absy { internal TransferCmd(IToken/*!*/ tok) : base(tok) { Contract.Requires(tok != null); } public abstract void Emit(TokenTextWriter/*!*/ stream, int level); public override void Typecheck(TypecheckingContext tc) { //Contract.Requires(tc != null); // nothing to typecheck } public override string ToString() { Contract.Ensures(Contract.Result() != null); System.IO.StringWriter buffer = new System.IO.StringWriter(); using (TokenTextWriter stream = new TokenTextWriter("", buffer, /*setTokens=*/ false , /*pretty=*/ false)) { this.Emit(stream, 0); } return buffer.ToString(); } } [ContractClassFor(typeof(TransferCmd))] public abstract class TransferCmdContracts : TransferCmd { public TransferCmdContracts() :base(null){ } public override void Emit(TokenTextWriter stream, int level) { Contract.Requires(stream != null); throw new NotImplementedException(); } } public class ReturnCmd : TransferCmd { public ReturnCmd(IToken/*!*/ tok) : base(tok) { Contract.Requires(tok != null); } public override void Emit(TokenTextWriter stream, int level) { //Contract.Requires(stream != null); stream.WriteLine(this, level, "return;"); } public override void Resolve(ResolutionContext rc) { //Contract.Requires(rc != null); // nothing to resolve } public override Absy StdDispatch(StandardVisitor visitor) { //Contract.Requires(visitor != null); Contract.Ensures(Contract.Result() != null); return visitor.VisitReturnCmd(this); } } public class GotoCmd : TransferCmd { [Rep] public List labelNames; [Rep] public List labelTargets; [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(labelNames == null || labelTargets == null || labelNames.Count == labelTargets.Count); } [NotDelayed] public GotoCmd(IToken/*!*/ tok, List/*!*/ labelSeq) : base(tok) { Contract.Requires(tok != null); Contract.Requires(labelSeq != null); this.labelNames = labelSeq; } public GotoCmd(IToken/*!*/ tok, List/*!*/ labelSeq, List/*!*/ blockSeq) : base(tok) { Contract.Requires(tok != null); Contract.Requires(labelSeq != null); Contract.Requires(blockSeq != null); Debug.Assert(labelSeq.Count == blockSeq.Count); for (int i = 0; i < labelSeq.Count; i++) { Debug.Assert(Equals(labelSeq[i], cce.NonNull(blockSeq[i]).Label)); } this.labelNames = labelSeq; this.labelTargets = blockSeq; } public GotoCmd(IToken/*!*/ tok, List/*!*/ blockSeq) : base(tok) { //requires (blockSeq[i] != null ==> blockSeq[i].Label != null); Contract.Requires(tok != null); Contract.Requires(blockSeq != null); List labelSeq = new List(); for (int i = 0; i < blockSeq.Count; i++) labelSeq.Add(cce.NonNull(blockSeq[i]).Label); this.labelNames = labelSeq; this.labelTargets = blockSeq; } public void AddTarget(Block b) { Contract.Requires(b != null); Contract.Requires(b.Label != null); Contract.Requires(this.labelTargets != null); Contract.Requires(this.labelNames != null); this.labelTargets.Add(b); this.labelNames.Add(b.Label); } public override void Emit(TokenTextWriter stream, int level) { //Contract.Requires(stream != null); Contract.Assume(this.labelNames != null); stream.Write(this, level, "goto "); if (CommandLineOptions.Clo.PrintWithUniqueASTIds) { if (labelTargets == null) { string sep = ""; foreach (string name in labelNames) { stream.Write("{0}{1}^^{2}", sep, "NoDecl", name); sep = ", "; } } else { string sep = ""; foreach (Block/*!*/ b in labelTargets) { Contract.Assert(b != null); stream.Write("{0}h{1}^^{2}", sep, b.GetHashCode(), b.Label); sep = ", "; } } } else { labelNames.Emit(stream); } stream.WriteLine(";"); } public override void Resolve(ResolutionContext rc) { //Contract.Requires(rc != null); Contract.Ensures(labelTargets != null); if (labelTargets != null) { // already resolved return; } Contract.Assume(this.labelNames != null); labelTargets = new List(); foreach (string/*!*/ lbl in labelNames) { Contract.Assert(lbl != null); Block b = rc.LookUpBlock(lbl); if (b == null) { rc.Error(this, "goto to unknown block: {0}", lbl); } else { labelTargets.Add(b); } } Debug.Assert(rc.ErrorCount > 0 || labelTargets.Count == labelNames.Count); } public override Absy StdDispatch(StandardVisitor visitor) { //Contract.Requires(visitor != null); Contract.Ensures(Contract.Result() != null); return visitor.VisitGotoCmd(this); } } }