From b617dda87871573b826dada4af73fc48dce94f15 Mon Sep 17 00:00:00 2001 From: tabarbe Date: Fri, 20 Aug 2010 22:29:44 +0000 Subject: Boogie: Renaming core sources in preparation for port commit --- Source/Core/Absy.cs | 3244 ++++++++++++++++++++++++++++++++++++ Source/Core/AbsyCmd.cs | 2472 +++++++++++++++++++++++++++ Source/Core/AbsyExpr.cs | 2430 +++++++++++++++++++++++++++ Source/Core/AbsyQuant.cs | 860 ++++++++++ Source/Core/AbsyType.cs | 2857 +++++++++++++++++++++++++++++++ Source/Core/AssemblyInfo.cs | 4 + Source/Core/CommandLineOptions.cs | 2109 +++++++++++++++++++++++ Source/Core/Core.csproj | 232 +++ Source/Core/DeadVarElim.cs | 1234 ++++++++++++++ Source/Core/Duplicator.cs | 408 +++++ Source/Core/GraphAlgorithms.cs | 175 ++ Source/Core/Inline.cs | 689 ++++++++ Source/Core/LambdaHelper.cs | 161 ++ Source/Core/LoopUnroll.cs | 224 +++ Source/Core/OOLongUtil.cs | 174 ++ Source/Core/Parser.cs | 2168 ++++++++++++++++++++++++ Source/Core/ParserHelper.cs | 188 +++ Source/Core/PureCollections.cs | 785 +++++++++ Source/Core/ResolutionContext.cs | 539 ++++++ Source/Core/Scanner.cs | 726 ++++++++ Source/Core/StandardVisitor.cs | 503 ++++++ Source/Core/TypeAmbiguitySeeker.cs | 95 ++ Source/Core/Util.cs | 481 ++++++ Source/Core/VCExp.cs | 191 +++ Source/Core/Xml.cs | 291 ++++ 25 files changed, 23240 insertions(+) create mode 100644 Source/Core/Absy.cs create mode 100644 Source/Core/AbsyCmd.cs create mode 100644 Source/Core/AbsyExpr.cs create mode 100644 Source/Core/AbsyQuant.cs create mode 100644 Source/Core/AbsyType.cs create mode 100644 Source/Core/AssemblyInfo.cs create mode 100644 Source/Core/CommandLineOptions.cs create mode 100644 Source/Core/Core.csproj create mode 100644 Source/Core/DeadVarElim.cs create mode 100644 Source/Core/Duplicator.cs create mode 100644 Source/Core/GraphAlgorithms.cs create mode 100644 Source/Core/Inline.cs create mode 100644 Source/Core/LambdaHelper.cs create mode 100644 Source/Core/LoopUnroll.cs create mode 100644 Source/Core/OOLongUtil.cs create mode 100644 Source/Core/Parser.cs create mode 100644 Source/Core/ParserHelper.cs create mode 100644 Source/Core/PureCollections.cs create mode 100644 Source/Core/ResolutionContext.cs create mode 100644 Source/Core/Scanner.cs create mode 100644 Source/Core/StandardVisitor.cs create mode 100644 Source/Core/TypeAmbiguitySeeker.cs create mode 100644 Source/Core/Util.cs create mode 100644 Source/Core/VCExp.cs create mode 100644 Source/Core/Xml.cs diff --git a/Source/Core/Absy.cs b/Source/Core/Absy.cs new file mode 100644 index 00000000..4d0113ee --- /dev/null +++ b/Source/Core/Absy.cs @@ -0,0 +1,3244 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) Microsoft Corporation. All Rights Reserved. +// +//----------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------- +// BoogiePL - Absy.cs +//--------------------------------------------------------------------------------------------- +namespace Microsoft.Boogie.AbstractInterpretation +{ + using System.Diagnostics; + using CCI = System.Compiler; + using System.Collections; + using AI = Microsoft.AbstractInterpretationFramework; + + public class CallSite + { + public readonly Implementation! Impl; + public readonly Block! Block; + public readonly int Statement; // invariant: Block[Statement] is CallCmd + public readonly AI.Lattice.Element! KnownBeforeCall; + public readonly ProcedureSummaryEntry! SummaryEntry; + + public CallSite (Implementation! impl, Block! b, int stmt, AI.Lattice.Element! e, ProcedureSummaryEntry! summaryEntry) + { + this.Impl = impl; + this.Block = b; + this.Statement = stmt; + this.KnownBeforeCall = e; + this.SummaryEntry = summaryEntry; + } + } + + public class ProcedureSummaryEntry + { + public AI.Lattice! Lattice; + public AI.Lattice.Element! OnEntry; + public AI.Lattice.Element! OnExit; + public CCI.IMutableSet/**/! ReturnPoints; // whenever OnExit changes, we start analysis again at all the ReturnPoints + + public ProcedureSummaryEntry (AI.Lattice! lattice, AI.Lattice.Element! onEntry) + { + this.Lattice = lattice; + this.OnEntry = onEntry; + this.OnExit = lattice.Bottom; + this.ReturnPoints = new CCI.HashSet(); + // base(); + } + + } // class + + public class ProcedureSummary : ArrayList/**/ + { + invariant !IsReadOnly && !IsFixedSize; + + public new ProcedureSummaryEntry! this [int i] + { + get + requires 0 <= i && i < Count; + { return (ProcedureSummaryEntry!) base[i]; } + } + + } // class +} // namespace + + +namespace Microsoft.Boogie +{ + using System; + using System.Collections; + using System.Diagnostics; + using System.Collections.Generic; + using Microsoft.Boogie.AbstractInterpretation; + using AI = Microsoft.AbstractInterpretationFramework; + using Microsoft.Contracts; + using Graphing; + + + public abstract class Absy + { + public IToken! tok; + private int uniqueId; + + public int Line { get { return tok != null ? tok.line : -1; } } + public int Col { get { return tok != null ? tok.col : -1; } } + + public Absy (IToken! tok) + { + this.tok = tok; + this.uniqueId = AbsyNodeCount++; + // base(); + } + + private static int AbsyNodeCount = 0; + + // We uniquely number every AST node to make them + // suitable for our implementation of functional maps. + // + public int UniqueId { get { return this.uniqueId; } } + + private const int indent_size = 2; + protected static string Indent (int level) + { + return new string(' ', (indent_size * level)); + } + + public abstract void Resolve (ResolutionContext! rc); + + /// + /// Requires the object to have been successfully resolved. + /// + /// + public abstract void Typecheck (TypecheckingContext! tc); + /// + /// Intorduced this so the uniqueId is not the same on a cloned object. + /// + /// + public virtual Absy! Clone() + { + Absy! result = (Absy!) this.MemberwiseClone(); + result.uniqueId = AbsyNodeCount++; // BUGBUG?? + return result; + } + + public virtual Absy! StdDispatch(StandardVisitor! visitor) + { + System.Diagnostics.Debug.Fail("Unknown Absy node type: " + this.GetType()); + throw new System.NotImplementedException(); + } + } + + // TODO: Ideally, this would use generics. + public interface IPotentialErrorNode + { + object ErrorData { get; set; } + } + + public class Program : Absy + { + [Rep] + public List! TopLevelDeclarations; + + public Program() + : base(Token.NoToken) + { + this.TopLevelDeclarations = new List(); + // base(Token.NoToken); + } + + public void Emit (TokenTextWriter! stream) + { + stream.SetToken(this); + Emitter.Declarations(this.TopLevelDeclarations, stream); + } + /// + /// Returns the number of name resolution errors. + /// + /// + public int Resolve () + { + return Resolve((IErrorSink) null); + } + + public int Resolve (IErrorSink errorSink) + { + ResolutionContext rc = new ResolutionContext(errorSink); + Resolve(rc); + return rc.ErrorCount; + } + + public override void Resolve (ResolutionContext! rc) + { + Helpers.ExtraTraceInformation("Starting resolution"); + + foreach (Declaration d in TopLevelDeclarations) { + d.Register(rc); + } + + ResolveTypes(rc); + + List prunedTopLevelDecls = CommandLineOptions.Clo.OverlookBoogieTypeErrors ? new List() : null; + + foreach (Declaration d in TopLevelDeclarations) { + // resolve all the non-type-declarations + if (d is TypeCtorDecl || d is TypeSynonymDecl) { + if (prunedTopLevelDecls != null) + prunedTopLevelDecls.Add(d); + } else { + int e = rc.ErrorCount; + d.Resolve(rc); + if (prunedTopLevelDecls != null) { + if (rc.ErrorCount != e && d is Implementation) { + // ignore this implementation + System.Console.WriteLine("Warning: Ignoring implementation {0} because of translation resolution errors", ((Implementation)d).Name); + rc.ErrorCount = e; + } else { + prunedTopLevelDecls.Add(d); + } + } + } + } + if (prunedTopLevelDecls != null) { + TopLevelDeclarations = prunedTopLevelDecls; + } + + foreach (Declaration d in TopLevelDeclarations) { + Variable v = d as Variable; + if (v != null) { + v.ResolveWhere(rc); + } + } + } + + + private void ResolveTypes (ResolutionContext! rc) { + // first resolve type constructors + foreach (Declaration d in TopLevelDeclarations) { + if (d is TypeCtorDecl) + d.Resolve(rc); + } + + // collect type synonym declarations + List! synonymDecls = new List (); + foreach (Declaration d in TopLevelDeclarations) { + if (d is TypeSynonymDecl) + synonymDecls.Add((TypeSynonymDecl)d); + } + + // then resolve the type synonyms by a simple + // fixed-point iteration + TypeSynonymDecl.ResolveTypeSynonyms(synonymDecls, rc); + } + + + public int Typecheck () + { + return this.Typecheck((IErrorSink) null); + } + + public int Typecheck (IErrorSink errorSink) + { + TypecheckingContext tc = new TypecheckingContext(errorSink); + Typecheck(tc); + return tc.ErrorCount; + } + + public override void Typecheck (TypecheckingContext! tc) + { + Helpers.ExtraTraceInformation("Starting typechecking"); + + int oldErrorCount = tc.ErrorCount; + foreach (Declaration d in TopLevelDeclarations) { + d.Typecheck(tc); + } + + if (oldErrorCount == tc.ErrorCount) { + // check whether any type proxies have remained uninstantiated + TypeAmbiguitySeeker! seeker = new TypeAmbiguitySeeker (tc); + foreach (Declaration d in TopLevelDeclarations) { + seeker.Visit(d); + } + } + + AxiomExpander expander = new AxiomExpander(this, tc); + expander.CollectExpansions(); + } + + public void ComputeStronglyConnectedComponents() + { + foreach(Declaration d in this.TopLevelDeclarations) { + d.ComputeStronglyConnectedComponents(); + } + } + + public void InstrumentWithInvariants () + { + foreach (Declaration d in this.TopLevelDeclarations) { + d.InstrumentWithInvariants(); + } + } + + /// + /// Reset the abstract stated computed before + /// + public void ResetAbstractInterpretationState() + { + foreach(Declaration d in this.TopLevelDeclarations) { + d.ResetAbstractInterpretationState(); + } + } + + public void UnrollLoops(int n) + requires 0 <= n; + { + foreach (Declaration d in this.TopLevelDeclarations) { + Implementation impl = d as Implementation; + if (impl != null && impl.Blocks != null && impl.Blocks.Count > 0) { + expose (impl) { + Block start = impl.Blocks[0]; + assume start != null; + assume start.IsConsistent; + impl.Blocks = LoopUnroll.UnrollLoops(start, n); + } + } + } + } + + void CreateProceduresForLoops(Implementation! impl, Graph! g, List! loopImpls) + { + // Enumerate the headers + // for each header h: + // create implementation p_h with + // inputs = inputs, outputs, and locals of impl + // outputs = outputs and locals of impl + // locals = empty set + // add call o := p_h(i) at the beginning of the header block + // break the back edges whose target is h + // Enumerate the headers again to create the bodies of p_h + // for each header h: + // compute the loop corresponding to h + // make copies of all blocks in the loop for h + // delete all target edges that do not go to a block in the loop + // create a new entry block and a new return block + // add edges from entry block to the loop header and the return block + // add calls o := p_h(i) at the end of the blocks that are sources of back edges + Dictionary! loopHeaderToName = new Dictionary(); + Dictionary! loopHeaderToInputs = new Dictionary(); + Dictionary! loopHeaderToOutputs = new Dictionary(); + Dictionary! loopHeaderToSubstMap = new Dictionary(); + Dictionary! loopHeaderToLoopProc = new Dictionary(); + Dictionary! loopHeaderToCallCmd = new Dictionary(); + foreach (Block! header in g.Headers) + { + Contract.Assert(header != null); + string name = header.ToString(); + loopHeaderToName[header] = name; + VariableSeq inputs = new VariableSeq(); + VariableSeq outputs = new VariableSeq(); + ExprSeq callInputs = new ExprSeq(); + IdentifierExprSeq callOutputs = new IdentifierExprSeq(); + Hashtable substMap = new Hashtable(); // Variable -> IdentifierExpr + + foreach (Variable! v in impl.InParams) + { + callInputs.Add(new IdentifierExpr(Token.NoToken, v)); + Formal f = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "in_" + v.Name, v.TypedIdent.Type), true); + inputs.Add(f); + substMap[v] = new IdentifierExpr(Token.NoToken, f); + } + foreach (Variable! v in impl.OutParams) + { + callInputs.Add(new IdentifierExpr(Token.NoToken, v)); + inputs.Add(new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "in_" + v.Name, v.TypedIdent.Type), true)); + callOutputs.Add(new IdentifierExpr(Token.NoToken, v)); + Formal f = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "out_" + v.Name, v.TypedIdent.Type), false); + outputs.Add(f); + substMap[v] = new IdentifierExpr(Token.NoToken, f); + } + foreach (Variable! v in impl.LocVars) + { + callInputs.Add(new IdentifierExpr(Token.NoToken, v)); + inputs.Add(new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "in_" + v.Name, v.TypedIdent.Type), true)); + callOutputs.Add(new IdentifierExpr(Token.NoToken, v)); + Formal f = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "out_" + v.Name, v.TypedIdent.Type), false); + outputs.Add(f); + substMap[v] = new IdentifierExpr(Token.NoToken, f); + } + VariableSeq! targets = new VariableSeq(); + foreach (Block! b in g.BackEdgeNodes(header)) + { + foreach (Block! block in g.NaturalLoops(header, b)) + { + foreach (Cmd! cmd in block.Cmds) + { + cmd.AddAssignedVariables(targets); + } + } + } + IdentifierExprSeq! globalMods = new IdentifierExprSeq(); + Set globalModsSet = new Set(); + foreach (Variable! v in targets) + { + if (!(v is GlobalVariable)) continue; + if (globalModsSet.Contains(v)) continue; + globalModsSet.Add(v); + globalMods.Add(new IdentifierExpr(Token.NoToken, v)); + } + loopHeaderToInputs[header] = inputs; + loopHeaderToOutputs[header] = outputs; + loopHeaderToSubstMap[header] = substMap; + Procedure! proc = + new Procedure(Token.NoToken, "loop_" + header.ToString(), + new TypeVariableSeq(), inputs, outputs, + new RequiresSeq(), globalMods, new EnsuresSeq()); + if (CommandLineOptions.Clo.LazyInlining > 0 || CommandLineOptions.Clo.StratifiedInlining > 0) + { + proc.AddAttribute("inline", Expr.Literal(1)); + } + loopHeaderToLoopProc[header] = proc; + CallCmd callCmd = new CallCmd(Token.NoToken, name, callInputs, callOutputs); + callCmd.Proc = proc; + loopHeaderToCallCmd[header] = callCmd; + } + + foreach (Block! header in g.Headers) + { + Procedure loopProc = loopHeaderToLoopProc[header]; + Dictionary blockMap = new Dictionary(); + CodeCopier codeCopier = new CodeCopier(loopHeaderToSubstMap[header]); // fix me + VariableSeq inputs = loopHeaderToInputs[header]; + VariableSeq outputs = loopHeaderToOutputs[header]; + foreach (Block! source in g.BackEdgeNodes(header)) + { + foreach (Block! block in g.NaturalLoops(header, source)) + { + if (blockMap.ContainsKey(block)) continue; + Block newBlock = new Block(); + newBlock.Label = block.Label; + newBlock.Cmds = codeCopier.CopyCmdSeq(block.Cmds); + blockMap[block] = newBlock; + } + string callee = loopHeaderToName[header]; + ExprSeq ins = new ExprSeq(); + IdentifierExprSeq outs = new IdentifierExprSeq(); + for (int i = 0; i < impl.InParams.Length; i++) + { + ins.Add(new IdentifierExpr(Token.NoToken, (!) inputs[i])); + } + foreach (Variable! v in outputs) + { + ins.Add(new IdentifierExpr(Token.NoToken, v)); + outs.Add(new IdentifierExpr(Token.NoToken, v)); + } + CallCmd callCmd = new CallCmd(Token.NoToken, callee, ins, outs); + callCmd.Proc = loopProc; + Block! block1 = new Block(Token.NoToken, source.Label + "_dummy", + new CmdSeq(new AssumeCmd(Token.NoToken, Expr.False)), new ReturnCmd(Token.NoToken)); + Block! block2 = new Block(Token.NoToken, block1.Label, + new CmdSeq(callCmd), new ReturnCmd(Token.NoToken)); + impl.Blocks.Add(block1); + + GotoCmd gotoCmd = source.TransferCmd as GotoCmd; + assert gotoCmd != null && gotoCmd.labelNames != null && gotoCmd.labelTargets != null && gotoCmd.labelTargets.Length >= 1; + StringSeq! newLabels = new StringSeq(); + BlockSeq! newTargets = new BlockSeq(); + for (int i = 0; i < gotoCmd.labelTargets.Length; i++) + { + if (gotoCmd.labelTargets[i] == header) continue; + newTargets.Add(gotoCmd.labelTargets[i]); + newLabels.Add(gotoCmd.labelNames[i]); + } + newTargets.Add(block1); + newLabels.Add(block1.Label); + gotoCmd.labelNames = newLabels; + gotoCmd.labelTargets = newTargets; + + blockMap[block1] = block2; + } + List! blocks = new List(); + Block exit = new Block(Token.NoToken, "exit", new CmdSeq(), new ReturnCmd(Token.NoToken)); + GotoCmd cmd = new GotoCmd(Token.NoToken, + new StringSeq(((!)blockMap[header]).Label, exit.Label), + new BlockSeq(blockMap[header], exit)); + + Debug.Assert(outputs.Length + impl.InParams.Length == inputs.Length); + List! lhss = new List(); + List! rhss = new List(); + for (int i = impl.InParams.Length; i < inputs.Length; i++) + { + Variable! inv = (!)inputs[i]; + Variable! outv = (!)outputs[i - impl.InParams.Length]; + AssignLhs lhs = new SimpleAssignLhs(Token.NoToken, new IdentifierExpr(Token.NoToken, outv)); + Expr rhs = new IdentifierExpr(Token.NoToken, inv); + lhss.Add(lhs); + rhss.Add(rhs); + } + AssignCmd assignCmd = new AssignCmd(Token.NoToken, lhss, rhss); + Block entry = new Block(Token.NoToken, "entry", new CmdSeq(assignCmd), cmd); + blocks.Add(entry); + foreach (Block! block in blockMap.Keys) + { + Block! newBlock = (!) blockMap[block]; + GotoCmd gotoCmd = block.TransferCmd as GotoCmd; + if (gotoCmd == null) + { + newBlock.TransferCmd = new ReturnCmd(Token.NoToken); + } + else + { + assume gotoCmd.labelNames != null && gotoCmd.labelTargets != null; + StringSeq newLabels = new StringSeq(); + BlockSeq newTargets = new BlockSeq(); + for (int i = 0; i < gotoCmd.labelTargets.Length; i++) + { + Block target = gotoCmd.labelTargets[i]; + if (blockMap.ContainsKey(target)) + { + newLabels.Add(gotoCmd.labelNames[i]); + newTargets.Add(blockMap[target]); + } + } + if (newTargets.Length == 0) + { + newBlock.Cmds.Add(new AssumeCmd(Token.NoToken, Expr.False)); + newBlock.TransferCmd = new ReturnCmd(Token.NoToken); + } + else + { + newBlock.TransferCmd = new GotoCmd(Token.NoToken, newLabels, newTargets); + } + } + blocks.Add(newBlock); + } + blocks.Add(exit); + Implementation loopImpl = + new Implementation(Token.NoToken, loopProc.Name, + new TypeVariableSeq(), inputs, outputs, new VariableSeq(), blocks); + loopImpl.Proc = loopProc; + loopImpls.Add(loopImpl); + + // Finally, add call to the loop in the containing procedure + CmdSeq cmdSeq = new CmdSeq(); + cmdSeq.Add(loopHeaderToCallCmd[header]); + cmdSeq.AddRange(header.Cmds); + header.Cmds = cmdSeq; + } + } + + public static Graph! GraphFromImpl(Implementation! impl) { + Contract.Ensures(Contract.Result>() != null); + + 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) { + Contract.Assert(b != null); + GotoCmd gtc = b.TransferCmd as GotoCmd; + if (gtc != null) { + foreach (Block! dest in (!)gtc.labelTargets) { + g.AddEdge(b, dest); + } + } + } + return g; + } + + public void ExtractLoops() + { + List! loopImpls = new List(); + foreach (Declaration d in this.TopLevelDeclarations) { + Implementation impl = d as Implementation; + if (impl != null && impl.Blocks != null && impl.Blocks.Count > 0) { + Graph! g = GraphFromImpl(impl); + g.ComputeLoops(); + if (!g.Reducible) + { + throw new Exception("Irreducible flow graphs are unsupported."); + } + CreateProceduresForLoops(impl, g, loopImpls); + } + } + foreach (Implementation! loopImpl in loopImpls) + { + TopLevelDeclarations.Add(loopImpl); + } + } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitProgram(this); + } + + private List globals = null; + public List! GlobalVariables() + { + if (globals != null) return globals; + globals = new List(); + foreach (Declaration d in TopLevelDeclarations) { + GlobalVariable gvar = d as GlobalVariable; + if (gvar != null) globals.Add(gvar); + } + return globals; + } + } + + //--------------------------------------------------------------------- + // Declarations + + public abstract class Declaration : Absy + { + public QKeyValue Attributes; + + public Declaration(IToken! tok) + : base(tok) + { + } + + protected void EmitAttributes(TokenTextWriter! stream) + { + for (QKeyValue kv = this.Attributes; kv != null; kv = kv.Next) { + kv.Emit(stream); + stream.Write(" "); + } + } + + protected void ResolveAttributes(ResolutionContext! rc) + { + for (QKeyValue kv = this.Attributes; kv != null; kv = kv.Next) { + kv.Resolve(rc); + } + } + + protected void TypecheckAttributes(TypecheckingContext! rc) + { + for (QKeyValue kv = this.Attributes; kv != null; kv = kv.Next) { + kv.Typecheck(rc); + } + } + + // Look for {:name true} or {:name false} in list of attributes. Return result in 'result' + // (which is not touched if there is no attribute specified). + // + // Returns false is there was an error processing the flag, true otherwise. + public bool CheckBooleanAttribute(string! name, ref bool result) + { + Expr? expr = FindExprAttribute(name); + if (expr != null) { + if (expr is LiteralExpr && ((LiteralExpr)expr).isBool) { + result = ((LiteralExpr)expr).asBool; + } else { + return false; + } + } + return true; + } + + // Look for {:name expr} in list of attributes. + public Expr? FindExprAttribute(string! name) + { + Expr? res = null; + for (QKeyValue kv = this.Attributes; kv != null; kv = kv.Next) { + if (kv.Key == name) { + if (kv.Params.Count == 1 && kv.Params[0] is Expr) { + res = (Expr)kv.Params[0]; + } + } + } + return res; + } + + // Look for {:name string} in list of attributes. + public string? FindStringAttribute(string! name) + { + return QKeyValue.FindStringAttribute(this.Attributes, name); + } + + // Look for {:name N} or {:name N} in list of attributes. Return result in 'result' + // (which is not touched if there is no attribute specified). + // + // Returns false is there was an error processing the flag, true otherwise. + public bool CheckIntAttribute(string! name, ref int result) + { + Expr? expr = FindExprAttribute(name); + if (expr != null) { + if (expr is LiteralExpr && ((LiteralExpr)expr).isBigNum) { + result = ((LiteralExpr)expr).asBigNum.ToInt; + } else { + return false; + } + } + return true; + } + + public void AddAttribute(string! name, object! val) + { + QKeyValue kv; + for (kv = this.Attributes; kv != null; kv = kv.Next) { + if (kv.Key == name) { + kv.Params.Add(val); + break; + } + } + if (kv == null) { + Attributes = new QKeyValue(tok, name, new List(new object![] { val }), Attributes); + } + } + + public abstract void Emit(TokenTextWriter! stream, int level); + public abstract void Register(ResolutionContext! rc); + + /// + /// Compute the strongly connected components of the declaration. + /// By default, it does nothing + /// + public virtual void ComputeStronglyConnectedComponents() { /* Does nothing */} + + /// + /// This method inserts the abstract-interpretation-inferred invariants + /// as assume (or possibly assert) statements in the statement sequences of + /// each block. + /// + public virtual void InstrumentWithInvariants () {} + + /// + /// Reset the abstract stated computed before + /// + public virtual void ResetAbstractInterpretationState() { /* does nothing */ } + } + + public class Axiom : Declaration + { + public Expr! Expr; + public string? Comment; + + public Axiom(IToken! tok, Expr! expr) + { + this(tok, expr, null); + } + + public Axiom(IToken! tok, Expr! expr, string? comment) + : base(tok) + { + Expr = expr; + Comment = comment; + // base(tok); + } + + public Axiom(IToken! tok, Expr! expr, string? comment, QKeyValue kv) + { + this(tok, expr, comment); + this.Attributes = kv; + } + + public override void Emit(TokenTextWriter! stream, int level) + { + if (Comment != null) { + stream.WriteLine(this, level, "// " + Comment); + } + stream.Write(this, level, "axiom "); + EmitAttributes(stream); + this.Expr.Emit(stream); + stream.WriteLine(";"); + } + public override void Register(ResolutionContext! rc) + { + // nothing to register + } + public override void Resolve(ResolutionContext! rc) + { + ResolveAttributes(rc); + rc.StateMode = ResolutionContext.State.StateLess; + Expr.Resolve(rc); + rc.StateMode = ResolutionContext.State.Single; + } + public override void Typecheck(TypecheckingContext! tc) + { + TypecheckAttributes(tc); + Expr.Typecheck(tc); + assert Expr.Type != null; // follows from postcondition of Expr.Typecheck + if (! Expr.Type.Unify(Type.Bool)) + { + tc.Error(this, "axioms must be of type bool"); + } + } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitAxiom(this); + } + } + + public abstract class NamedDeclaration : Declaration + { + private string! name; + public string! Name + { + get + { + return this.name; + } + set + { + this.name = value; + } + } + + + public NamedDeclaration(IToken! tok, string! name) + : base(tok) + { + this.name = name; + // base(tok); + } + [Pure] + public override string! ToString() + { + return (!) Name; + } + } + + public class TypeCtorDecl : NamedDeclaration + { + public readonly int Arity; + + public TypeCtorDecl(IToken! tok, string! name, int Arity) + : base(tok, name) + { + this.Arity = Arity; + } + public TypeCtorDecl(IToken! tok, string! name, int Arity, QKeyValue kv) + : base(tok, name) + { + this.Arity = Arity; + this.Attributes = kv; + } + public override void Emit(TokenTextWriter! stream, int level) + { + stream.Write(this, level, "type "); + EmitAttributes(stream); + stream.Write("{0}", TokenTextWriter.SanitizeIdentifier(Name)); + for (int i = 0; i < Arity; ++i) + stream.Write(" _"); + stream.WriteLine(";"); + } + public override void Register(ResolutionContext! rc) + { + rc.AddType(this); + } + public override void Resolve(ResolutionContext! rc) + { + ResolveAttributes(rc); + } + public override void Typecheck(TypecheckingContext! tc) + { + TypecheckAttributes(tc); + } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitTypeCtorDecl(this); + } + } + + + public class TypeSynonymDecl : NamedDeclaration + { + public TypeVariableSeq! TypeParameters; + public Type! Body; + + public TypeSynonymDecl(IToken! tok, string! name, + TypeVariableSeq! typeParams, Type! body) + : base(tok, name) + { + this.TypeParameters = typeParams; + this.Body = body; + } + public TypeSynonymDecl(IToken! tok, string! name, + TypeVariableSeq! typeParams, Type! body, QKeyValue kv) + : base(tok, name) + { + this.TypeParameters = typeParams; + this.Body = body; + this.Attributes = kv; + } + public override void Emit(TokenTextWriter! stream, int level) + { + stream.Write(this, level, "type "); + EmitAttributes(stream); + stream.Write("{0}", TokenTextWriter.SanitizeIdentifier(Name)); + if (TypeParameters.Length > 0) + stream.Write(" "); + TypeParameters.Emit(stream, " "); + stream.Write(" = "); + Body.Emit(stream); + stream.WriteLine(";"); + } + public override void Register(ResolutionContext! rc) + { + rc.AddType(this); + } + public override void Resolve(ResolutionContext! rc) + { + ResolveAttributes(rc); + + int previousState = rc.TypeBinderState; + try { + foreach (TypeVariable! v in TypeParameters) + rc.AddTypeBinder(v); + Body = Body.ResolveType(rc); + } finally { + rc.TypeBinderState = previousState; + } + } + public override void Typecheck(TypecheckingContext! tc) + { + TypecheckAttributes(tc); + } + + public static void ResolveTypeSynonyms(List! synonymDecls, + ResolutionContext! rc) { + // then discover all dependencies between type synonyms + IDictionary!>! deps = + new Dictionary!> (); + foreach (TypeSynonymDecl! decl in synonymDecls) { + List! declDeps = new List (); + FindDependencies(decl.Body, declDeps, rc); + deps.Add(decl, declDeps); + } + + List! resolved = new List (); + + int unresolved = synonymDecls.Count - resolved.Count; + while (unresolved > 0) { + foreach (TypeSynonymDecl! decl in synonymDecls) { + if (!resolved.Contains(decl) && + forall{TypeSynonymDecl! d in deps[decl]; resolved.Contains(d)}) { + decl.Resolve(rc); + resolved.Add(decl); + } + } + + int newUnresolved = synonymDecls.Count - resolved.Count; + if (newUnresolved < unresolved) { + // we are making progress + unresolved = newUnresolved; + } else { + // there have to be cycles in the definitions + foreach (TypeSynonymDecl! decl in synonymDecls) { + if (!resolved.Contains(decl)) { + rc.Error(decl, + "type synonym could not be resolved because of cycles: {0}" + + " (replacing body with \"bool\" to continue resolving)", + decl.Name); + + // we simply replace the bodies of all remaining type + // synonyms with "bool" so that resolution can continue + decl.Body = Type.Bool; + decl.Resolve(rc); + } + } + + unresolved = 0; + } + } + } + + // determine a list of all type synonyms that occur in "type" + private static void FindDependencies(Type! type, List! deps, + ResolutionContext! rc) { + if (type.IsVariable || type.IsBasic) { + // nothing + } else if (type.IsUnresolved) { + UnresolvedTypeIdentifier! unresType = type.AsUnresolved; + TypeSynonymDecl dep = rc.LookUpTypeSynonym(unresType.Name); + if (dep != null) + deps.Add(dep); + foreach (Type! subtype in unresType.Arguments) + FindDependencies(subtype, deps, rc); + } else if (type.IsMap) { + MapType! mapType = type.AsMap; + foreach (Type! subtype in mapType.Arguments) + FindDependencies(subtype, deps, rc); + FindDependencies(mapType.Result, deps, rc); + } else if (type.IsCtor) { + // this can happen because we allow types to be resolved multiple times + CtorType! ctorType = type.AsCtor; + foreach (Type! subtype in ctorType.Arguments) + FindDependencies(subtype, deps, rc); + } else { + System.Diagnostics.Debug.Fail("Did not expect this type during resolution: " + + type); + } + } + + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitTypeSynonymDecl(this); + } + } + + + public abstract class Variable : NamedDeclaration, AI.IVariable + { + public TypedIdent! TypedIdent; + public Variable(IToken! tok, TypedIdent! typedIdent) + : base(tok, typedIdent.Name) + { + this.TypedIdent = typedIdent; + // base(tok, typedIdent.Name); + } + + public Variable(IToken! tok, TypedIdent! typedIdent, QKeyValue kv) + : base(tok, typedIdent.Name) + { + this.TypedIdent = typedIdent; + // base(tok, typedIdent.Name); + this.Attributes = kv; + } + + public abstract bool IsMutable + { + get; + } + + public override void Emit(TokenTextWriter! stream, int level) + { + stream.Write(this, level, "var "); + EmitAttributes(stream); + EmitVitals(stream, level); + stream.WriteLine(";"); + } + public void EmitVitals(TokenTextWriter! stream, int level) + { + if (CommandLineOptions.Clo.PrintWithUniqueASTIds && this.TypedIdent.HasName) + { + stream.Write("h{0}^^", this.GetHashCode()); // the idea is that this will prepend the name printed by TypedIdent.Emit + } + this.TypedIdent.Emit(stream); + } + public override void Resolve(ResolutionContext! rc) + { + this.TypedIdent.Resolve(rc); + } + public void ResolveWhere(ResolutionContext! rc) + { + if (this.TypedIdent.WhereExpr != null) { + this.TypedIdent.WhereExpr.Resolve(rc); + } + ResolveAttributes(rc); + } + public override void Typecheck(TypecheckingContext! tc) + { + TypecheckAttributes(tc); + this.TypedIdent.Typecheck(tc); + } + [Pure] + public object DoVisit(AI.ExprVisitor! visitor) + { + return visitor.VisitVariable(this); + } + } + + public class VariableComparer : IComparer + { + public int Compare(object a, object b) { + Variable A = a as Variable; + Variable B = b as Variable; + if (A == null || B == null) { + throw new ArgumentException("VariableComparer works only on objects of type Variable"); + } + return ((!)A.Name).CompareTo(B.Name); + } + } + + // class to specify the <:-parents of the values of constants + public class ConstantParent { + public readonly IdentifierExpr! Parent; + // if true, the sub-dag underneath this constant-parent edge is + // disjoint from all other unique sub-dags + public readonly bool Unique; + + public ConstantParent(IdentifierExpr! parent, bool unique) { + Parent = parent; + Unique = unique; + } + } + + public class Constant : Variable + { + // when true, the value of this constant is meant to be distinct + // from all other constants. + public readonly bool Unique; + + // the <:-parents of the value of this constant. If the field is + // null, no information about the parents is provided, which means + // that the parental situation is unconstrained. + public readonly List Parents; + + // if true, it is assumed that the immediate <:-children of the + // value of this constant are completely specified + public readonly bool ChildrenComplete; + + public Constant(IToken! tok, TypedIdent! typedIdent) + : base(tok, typedIdent) + requires typedIdent.Name != null && typedIdent.Name.Length > 0; + requires typedIdent.WhereExpr == null; + { + // base(tok, typedIdent); + this.Unique = true; + this.Parents = null; + this.ChildrenComplete = false; + } + public Constant(IToken! tok, TypedIdent! typedIdent, bool unique) + : base(tok, typedIdent) + requires typedIdent.Name != null && typedIdent.Name.Length > 0; + requires typedIdent.WhereExpr == null; + { + // base(tok, typedIdent); + this.Unique = unique; + this.Parents = null; + this.ChildrenComplete = false; + } + public Constant(IToken! tok, TypedIdent! typedIdent, + bool unique, + List parents, bool childrenComplete, + QKeyValue kv) + : base(tok, typedIdent, kv) + requires typedIdent.Name != null && typedIdent.Name.Length > 0; + requires typedIdent.WhereExpr == null; + { + // base(tok, typedIdent); + this.Unique = unique; + this.Parents = parents; + this.ChildrenComplete = childrenComplete; + } + public override bool IsMutable + { + get + { + return false; + } + } + public override void Emit(TokenTextWriter! stream, int level) + { + stream.Write(this, level, "const "); + EmitAttributes(stream); + if (this.Unique){ + stream.Write(this, level, "unique "); + } + EmitVitals(stream, level); + + if (Parents != null || ChildrenComplete) { + stream.Write(this, level, " extends"); + string! sep = " "; + foreach (ConstantParent! p in (!)Parents) { + stream.Write(this, level, sep); + sep = ", "; + if (p.Unique) + stream.Write(this, level, "unique "); + p.Parent.Emit(stream); + } + if (ChildrenComplete) + stream.Write(this, level, " complete"); + } + + stream.WriteLine(";"); + } + public override void Register(ResolutionContext! rc) + { + rc.AddVariable(this, true); + } + public override void Resolve(ResolutionContext! rc) + { + base.Resolve(rc); + if (Parents != null) { + foreach (ConstantParent! p in Parents) { + p.Parent.Resolve(rc); + if (p.Parent.Decl != null && !(p.Parent.Decl is Constant)) + rc.Error(p.Parent, "the parent of a constant has to be a constant"); + if (this.Equals(p.Parent.Decl)) + rc.Error(p.Parent, "constant cannot be its own parent"); + } + } + + // check that no parent occurs twice + // (could be optimised) + if (Parents != null) { + for (int i = 0; i < Parents.Count; ++i) { + if (Parents[i].Parent.Decl != null) { + for (int j = i + 1; j < Parents.Count; ++j) { + if (Parents[j].Parent.Decl != null && + ((!)Parents[i].Parent.Decl).Equals(Parents[j].Parent.Decl)) + rc.Error(Parents[j].Parent, + "{0} occurs more than once as parent", + Parents[j].Parent.Decl); + } + } + } + } + } + public override void Typecheck(TypecheckingContext! tc) + { + base.Typecheck(tc); + + if (Parents != null) { + foreach (ConstantParent! p in Parents) { + p.Parent.Typecheck(tc); + if (!((!)p.Parent.Decl).TypedIdent.Type.Unify(this.TypedIdent.Type)) + tc.Error(p.Parent, + "parent of constant has incompatible type ({0} instead of {1})", + p.Parent.Decl.TypedIdent.Type, this.TypedIdent.Type); + } + } + } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitConstant(this); + } + } + public class GlobalVariable : Variable + { + public GlobalVariable(IToken! tok, TypedIdent! typedIdent) + : base(tok, typedIdent) + { + } + public GlobalVariable(IToken! tok, TypedIdent! typedIdent, QKeyValue kv) + : base(tok, typedIdent, kv) + { + } + public override bool IsMutable + { + get + { + return true; + } + } + public override void Register(ResolutionContext! rc) + { + rc.AddVariable(this, true); + } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitGlobalVariable(this); + } + } + public class Formal : Variable + { + public bool InComing; + public Formal(IToken! tok, TypedIdent! typedIdent, bool incoming) + : base(tok, typedIdent) + { + InComing = incoming; + } + public override bool IsMutable + { + get + { + return !InComing; + } + } + public override void Register(ResolutionContext! rc) + { + rc.AddVariable(this, false); + } + + /// + /// Given a sequence of Formal declarations, returns sequence of Formals like the given one but without where clauses. + /// The Type of each Formal is cloned. + /// + public static VariableSeq! StripWhereClauses(VariableSeq! w) + { + VariableSeq s = new VariableSeq(); + foreach (Variable! v in w) { + Formal f = (Formal)v; + TypedIdent ti = f.TypedIdent; + s.Add(new Formal(f.tok, new TypedIdent(ti.tok, ti.Name, ti.Type.CloneUnresolved()), f.InComing)); + } + return s; + } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitFormal(this); + } + } + public class LocalVariable : Variable + { + public LocalVariable(IToken! tok, TypedIdent! typedIdent, QKeyValue kv) + { + base(tok, typedIdent, kv); + } + public LocalVariable(IToken! tok, TypedIdent! typedIdent) + { + base(tok, typedIdent, null); + } + public override bool IsMutable + { + get + { + return true; + } + } + public override void Register(ResolutionContext! rc) + { + rc.AddVariable(this, false); + } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitLocalVariable(this); + } + } + public class Incarnation : LocalVariable + { + public int incarnationNumber; + public Incarnation(Variable! var, int i) : + base( + var.tok, + new TypedIdent(var.TypedIdent.tok,var.TypedIdent.Name + "@" + i,var.TypedIdent.Type) + ) + { + incarnationNumber = i; + } + + } + public class BoundVariable : Variable + { + public BoundVariable(IToken! tok, TypedIdent! typedIdent) + requires typedIdent.WhereExpr == null; + { + base(tok, typedIdent); // here for aesthetic reasons + } + public override bool IsMutable + { + get + { + return false; + } + } + public override void Register(ResolutionContext! rc) + { + rc.AddVariable(this, false); + } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitBoundVariable(this); + } + } + + public abstract class DeclWithFormals : NamedDeclaration + { + public TypeVariableSeq! TypeParameters; + public /*readonly--except in StandardVisitor*/ VariableSeq! InParams, OutParams; + + public DeclWithFormals (IToken! tok, string! name, TypeVariableSeq! typeParams, + VariableSeq! inParams, VariableSeq! outParams) + : base(tok, name) + { + this.TypeParameters = typeParams; + this.InParams = inParams; + this.OutParams = outParams; + // base(tok, name); + } + + protected DeclWithFormals (DeclWithFormals! that) + : base(that.tok, (!) that.Name) + { + this.TypeParameters = that.TypeParameters; + this.InParams = that.InParams; + this.OutParams = that.OutParams; + // base(that.tok, (!) that.Name); + } + + protected void EmitSignature (TokenTextWriter! stream, bool shortRet) + { + Type.EmitOptionalTypeParams(stream, TypeParameters); + stream.Write("("); + InParams.Emit(stream); + stream.Write(")"); + + if (shortRet) + { + assert OutParams.Length == 1; + stream.Write(" : "); + ((!)OutParams[0]).TypedIdent.Type.Emit(stream); + } + else if (OutParams.Length > 0) + { + stream.Write(" returns ("); + OutParams.Emit(stream); + stream.Write(")"); + } + } + + // Register all type parameters at the resolution context + protected void RegisterTypeParameters(ResolutionContext! rc) { + foreach (TypeVariable! v in TypeParameters) + rc.AddTypeBinder(v); + } + + protected void SortTypeParams() { + TypeSeq! allTypes = InParams.ToTypeSeq; + allTypes.AddRange(OutParams.ToTypeSeq); + TypeParameters = Type.SortTypeParams(TypeParameters, allTypes, null); + } + + /// + /// Adds the given formals to the current variable context, and then resolves + /// the types of those formals. Does NOT resolve the where clauses of the + /// formals. + /// Relies on the caller to first create, and later tear down, that variable + /// context. + /// + /// + protected void RegisterFormals(VariableSeq! formals, ResolutionContext! rc) + { + foreach (Formal! f in formals) + { + if (f.Name != TypedIdent.NoName) + { + rc.AddVariable(f, false); + } + f.Resolve(rc); + } + } + + /// + /// Resolves the where clauses (and attributes) of the formals. + /// + /// + protected void ResolveFormals(VariableSeq! formals, ResolutionContext! rc) + { + foreach (Formal! f in formals) + { + f.ResolveWhere(rc); + } + } + + public override void Typecheck(TypecheckingContext! tc) { + TypecheckAttributes(tc); + foreach (Formal! p in InParams) { + p.Typecheck(tc); + } + foreach (Formal! p in OutParams) { + p.Typecheck(tc); + } + } + } + + public class Expansion { + public string? ignore; // when to ignore + public Expr! body; + public TypeVariableSeq! TypeParameters; + public Variable[]! formals; + + public Expansion(string? ignore, Expr! body, + TypeVariableSeq! typeParams, Variable[]! formals) + { + this.ignore = ignore; + this.body = body; + this.TypeParameters = typeParams; + this.formals = formals; + } + } + + public class Function : DeclWithFormals + { + public string? Comment; + + // the body is only set if the function is declared with {:expand true} + public Expr Body; + public List? expansions; + public bool doingExpansion; + + private bool neverTrigger; + private bool neverTriggerComputed; + + public Function(IToken! tok, string! name, VariableSeq! args, Variable! result) + { + this(tok, name, new TypeVariableSeq(), args, result, null); + } + public Function(IToken! tok, string! name, TypeVariableSeq! typeParams, VariableSeq! args, Variable! result) + { + this(tok, name, typeParams, args, result, null); + } + public Function(IToken! tok, string! name, VariableSeq! args, Variable! result, + string? comment) + { + this(tok, name, new TypeVariableSeq(), args, result, comment); + } + public Function(IToken! tok, string! name, TypeVariableSeq! typeParams, VariableSeq! args, Variable! result, + string? comment) + : base(tok, name, typeParams, args, new VariableSeq(result)) + { + Comment = comment; + // base(tok, name, args, new VariableSeq(result)); + } + public Function(IToken! tok, string! name, TypeVariableSeq! typeParams, VariableSeq! args, Variable! result, + string? comment, QKeyValue kv) + { + this(tok, name, typeParams, args, result, comment); + this.Attributes = kv; + } + public override void Emit(TokenTextWriter! stream, int level) + { + if (Comment != null) { + stream.WriteLine(this, level, "// " + Comment); + } + stream.Write(this, level, "function "); + EmitAttributes(stream); + if (CommandLineOptions.Clo.PrintWithUniqueASTIds) { + stream.Write("h{0}^^{1}", this.GetHashCode(), TokenTextWriter.SanitizeIdentifier(this.Name)); + } else { + stream.Write("{0}", TokenTextWriter.SanitizeIdentifier(this.Name)); + } + EmitSignature(stream, true); + if (Body != null) { + stream.WriteLine(); + stream.WriteLine("{"); + stream.Write(level+1, ""); + Body.Emit(stream); + stream.WriteLine(); + stream.WriteLine("}"); + } else { + stream.WriteLine(";"); + } + } + public override void Register(ResolutionContext! rc) + { + rc.AddProcedure(this); + } + public override void Resolve(ResolutionContext! rc) + { + int previousTypeBinderState = rc.TypeBinderState; + try { + RegisterTypeParameters(rc); + rc.PushVarContext(); + RegisterFormals(InParams, rc); + RegisterFormals(OutParams, rc); + ResolveAttributes(rc); + if (Body != null) + Body.Resolve(rc); + rc.PopVarContext(); + Type.CheckBoundVariableOccurrences(TypeParameters, + InParams.ToTypeSeq, OutParams.ToTypeSeq, + this.tok, "function arguments", + rc); + } finally { + rc.TypeBinderState = previousTypeBinderState; + } + SortTypeParams(); + } + public override void Typecheck(TypecheckingContext! tc) + { + // PR: why was the base call left out previously? + base.Typecheck(tc); + // TypecheckAttributes(tc); + if (Body != null) { + Body.Typecheck(tc); + if (!((!)Body.Type).Unify(((!)OutParams[0]).TypedIdent.Type)) + tc.Error(Body, + "function body with invalid type: {0} (expected: {1})", + Body.Type, ((!)OutParams[0]).TypedIdent.Type); + } + } + + public bool NeverTrigger + { + get { + if (!neverTriggerComputed) { + this.CheckBooleanAttribute("never_pattern", ref neverTrigger); + neverTriggerComputed = true; + } + return neverTrigger; + } + } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitFunction(this); + } + } + + public class Requires : Absy, IPotentialErrorNode + { + public readonly bool Free; + public Expr! Condition; + public string? Comment; + + // TODO: convert to use generics + private object errorData; + public object ErrorData { + get { return errorData; } + set { errorData = value; } + } + invariant errorData != null ==> errorData is string; + + private MiningStrategy errorDataEnhanced; + public MiningStrategy ErrorDataEnhanced { + get { return errorDataEnhanced; } + set { errorDataEnhanced = value; } + } + + public QKeyValue Attributes; + + public String ErrorMessage { + get { + return QKeyValue.FindStringAttribute(Attributes, "msg"); + } + } + + public Requires(IToken! token, bool free, Expr! condition, string? comment, QKeyValue kv) + : base(token) + { + this.Free = free; + this.Condition = condition; + this.Comment = comment; + this.Attributes = kv; + // base(token); + } + + public Requires(IToken! token, bool free, Expr! condition, string? comment) + { + this(token, free, condition, comment, null); + } + + public Requires(bool free, Expr! condition) + { + this(Token.NoToken, free, condition, null); + } + + public Requires(bool free, Expr! condition, string? comment) + { + this(Token.NoToken, free, condition, comment); + } + + public void Emit(TokenTextWriter! stream, int level) + { + if (Comment != null) { + stream.WriteLine(this, level, "// " + Comment); + } + stream.Write(this, level, "{0}requires ", Free ? "free " : ""); + this.Condition.Emit(stream); + stream.WriteLine(";"); + } + + public override void Resolve(ResolutionContext! rc) + { + this.Condition.Resolve(rc); + } + + public override void Typecheck(TypecheckingContext! tc) + { + this.Condition.Typecheck(tc); + assert this.Condition.Type != null; // follows from postcondition of Expr.Typecheck + if (!this.Condition.Type.Unify(Type.Bool)) + { + tc.Error(this, "preconditions must be of type bool"); + } + } + } + + public class Ensures : Absy, IPotentialErrorNode + { + public readonly bool Free; + public Expr! Condition; + public string? Comment; + + // TODO: convert to use generics + private object errorData; + public object ErrorData { + get { return errorData; } + set { errorData = value; } + } + invariant errorData != null ==> errorData is string; + + private MiningStrategy errorDataEnhanced; + public MiningStrategy ErrorDataEnhanced { + get { return errorDataEnhanced; } + set { errorDataEnhanced = value; } + } + + public String ErrorMessage { + get { + return QKeyValue.FindStringAttribute(Attributes, "msg"); + } + } + + public QKeyValue Attributes; + + public Ensures(IToken! token, bool free, Expr! condition, string? comment, QKeyValue kv) + : base(token) + { + this.Free = free; + this.Condition = condition; + this.Comment = comment; + this.Attributes = kv; + // base(token); + } + + public Ensures(IToken! token, bool free, Expr! condition, string? comment) + { + this(token, free, condition, comment, null); + } + + public Ensures(bool free, Expr! condition) + { + this(Token.NoToken, free, condition, null); + } + + public Ensures(bool free, Expr! condition, string? comment) + { + this(Token.NoToken, free, condition, comment); + } + + public void Emit(TokenTextWriter! stream, int level) + { + if (Comment != null) { + stream.WriteLine(this, level, "// " + Comment); + } + stream.Write(this, level, "{0}ensures ", Free ? "free " : ""); + this.Condition.Emit(stream); + stream.WriteLine(";"); + } + + public override void Resolve(ResolutionContext! rc) + { + this.Condition.Resolve(rc); + } + + public override void Typecheck(TypecheckingContext! tc) + { + this.Condition.Typecheck(tc); + assert this.Condition.Type != null; // follows from postcondition of Expr.Typecheck + if (!this.Condition.Type.Unify(Type.Bool)) + { + tc.Error(this, "postconditions must be of type bool"); + } + } + } + + public class Procedure : DeclWithFormals + { + public RequiresSeq! Requires; + public IdentifierExprSeq! Modifies; + public EnsuresSeq! Ensures; + + // Abstract interpretation: Procedure-specific invariants... + [Rep] + public readonly ProcedureSummary! Summary; + + public Procedure ( + IToken! tok, + string! name, + TypeVariableSeq! typeParams, + VariableSeq! inParams, + VariableSeq! outParams, + RequiresSeq! @requires, + IdentifierExprSeq! @modifies, + EnsuresSeq! @ensures + ) + { + this(tok, name, typeParams, inParams, outParams, @requires, @modifies, @ensures, null); + } + + public Procedure ( + IToken! tok, + string! name, + TypeVariableSeq! typeParams, + VariableSeq! inParams, + VariableSeq! outParams, + RequiresSeq! @requires, + IdentifierExprSeq! @modifies, + EnsuresSeq! @ensures, + QKeyValue kv + ) + : base(tok, name, typeParams, inParams, outParams) + { + this.Requires = @requires; + this.Modifies = @modifies; + this.Ensures = @ensures; + this.Summary = new ProcedureSummary(); + this.Attributes = kv; + } + + public override void Emit(TokenTextWriter! stream, int level) + { + stream.Write(this, level, "procedure "); + EmitAttributes(stream); + stream.Write(this, level, "{0}", TokenTextWriter.SanitizeIdentifier(this.Name)); + EmitSignature(stream, false); + stream.WriteLine(";"); + + level++; + + foreach (Requires! e in this.Requires) + { + e.Emit(stream, level); + } + + if (this.Modifies.Length > 0) + { + stream.Write(level, "modifies "); + this.Modifies.Emit(stream, false); + stream.WriteLine(";"); + } + + foreach (Ensures! e in this.Ensures) + { + e.Emit(stream, level); + } + + if (!CommandLineOptions.Clo.IntraproceduralInfer) + { + for (int s=0; s < this.Summary.Count; s++) + { + ProcedureSummaryEntry! entry = (!) this.Summary[s]; + stream.Write(level + 1, "// "); + Expr e; + e = (Expr)entry.Lattice.ToPredicate(entry.OnEntry); + e.Emit(stream); + stream.Write(" ==> "); + e = (Expr)entry.Lattice.ToPredicate(entry.OnExit); + e.Emit(stream); + stream.WriteLine(); + } + } + + stream.WriteLine(); + stream.WriteLine(); + } + + public override void Register(ResolutionContext! rc) + { + rc.AddProcedure(this); + } + public override void Resolve(ResolutionContext! rc) + { + rc.PushVarContext(); + + foreach (IdentifierExpr! ide in Modifies) + { + ide.Resolve(rc); + } + + int previousTypeBinderState = rc.TypeBinderState; + try { + RegisterTypeParameters(rc); + + RegisterFormals(InParams, rc); + ResolveFormals(InParams, rc); // "where" clauses of in-parameters are resolved without the out-parameters in scope + foreach (Requires! e in Requires) + { + e.Resolve(rc); + } + RegisterFormals(OutParams, rc); + ResolveFormals(OutParams, rc); // "where" clauses of out-parameters are resolved with both in- and out-parametes in scope + + rc.StateMode = ResolutionContext.State.Two; + foreach (Ensures! e in Ensures) + { + e.Resolve(rc); + } + rc.StateMode = ResolutionContext.State.Single; + ResolveAttributes(rc); + + Type.CheckBoundVariableOccurrences(TypeParameters, + InParams.ToTypeSeq, OutParams.ToTypeSeq, + this.tok, "procedure arguments", + rc); + + } finally { + rc.TypeBinderState = previousTypeBinderState; + } + + rc.PopVarContext(); + + SortTypeParams(); + } + public override void Typecheck(TypecheckingContext! tc) + { + base.Typecheck(tc); + foreach (IdentifierExpr! ide in Modifies) + { + assume ide.Decl != null; + if (!ide.Decl.IsMutable) + { + tc.Error(this, "modifies list contains constant: {0}", ide.Name); + } + ide.Typecheck(tc); + } + foreach (Requires! e in Requires) + { + e.Typecheck(tc); + } + foreach (Ensures! e in Ensures) + { + e.Typecheck(tc); + } + } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitProcedure(this); + } + } + + public class Implementation : DeclWithFormals + { + public VariableSeq! LocVars; + [Rep] public StmtList StructuredStmts; + [Rep] public List! Blocks; + public Procedure Proc; + + // Blocks before applying passification etc. + // Both are used only when /inline is set. + public List? OriginalBlocks; + public VariableSeq? OriginalLocVars; + + // Strongly connected components + private StronglyConnectedComponents scc; + private bool BlockPredecessorsComputed; + public bool StronglyConnectedComponentsComputed + { + get + { + return this.scc != null; + } + } + + public bool SkipVerification + { + get + { + bool verify = true; + ((!)this.Proc).CheckBooleanAttribute("verify", ref verify); + this.CheckBooleanAttribute("verify", ref verify); + if (!verify) { + return true; + } + + if (CommandLineOptions.Clo.ProcedureInlining == CommandLineOptions.Inlining.Assert || + CommandLineOptions.Clo.ProcedureInlining == CommandLineOptions.Inlining.Assume) { + Expr? inl = this.FindExprAttribute("inline"); + if (inl == null) inl = this.Proc.FindExprAttribute("inline"); + if (inl != null && inl is LiteralExpr && ((LiteralExpr)inl).isBigNum && ((LiteralExpr)inl).asBigNum.Signum > 0) { + return true; + } + } + + return false; + } + } + + public Implementation(IToken! tok, string! name, TypeVariableSeq! typeParams, + VariableSeq! inParams, VariableSeq! outParams, + VariableSeq! localVariables, [Captured] StmtList! structuredStmts) + { + this(tok, name, typeParams, inParams, outParams, localVariables, structuredStmts, null, new Errors()); + } + + public Implementation(IToken! tok, string! name, TypeVariableSeq! typeParams, + VariableSeq! inParams, VariableSeq! outParams, + VariableSeq! localVariables, [Captured] StmtList! structuredStmts, + Errors! errorHandler) + { + this(tok, name, typeParams, inParams, outParams, localVariables, structuredStmts, null, errorHandler); + } + + public Implementation(IToken! tok, string! name, TypeVariableSeq! typeParams, + VariableSeq! inParams, VariableSeq! outParams, + VariableSeq! localVariables, [Captured] StmtList! structuredStmts, QKeyValue kv, + Errors! errorHandler) + : base(tok, name, typeParams, inParams, outParams) + { + LocVars = localVariables; + StructuredStmts = structuredStmts; + BigBlocksResolutionContext ctx = new BigBlocksResolutionContext(structuredStmts, errorHandler); + Blocks = ctx.Blocks; + BlockPredecessorsComputed = false; + scc = null; + Attributes = kv; + + // base(tok, name, inParams, outParams); + } + + public Implementation(IToken! tok, string! name, TypeVariableSeq! typeParams, + VariableSeq! inParams, VariableSeq! outParams, + VariableSeq! localVariables, [Captured] List! block) + { + this(tok, name, typeParams, inParams, outParams, localVariables, block, null); + } + + public Implementation(IToken! tok, string! name, TypeVariableSeq! typeParams, + VariableSeq! inParams, VariableSeq! outParams, + VariableSeq! localVariables, [Captured] List! blocks, QKeyValue kv) + : base(tok, name, typeParams, inParams, outParams) + { + LocVars = localVariables; + Blocks = blocks; + BlockPredecessorsComputed = false; + scc = null; + Attributes = kv; + + //base(tok, name, inParams, outParams); + } + + public override void Emit(TokenTextWriter! stream, int level) + { + stream.Write(this, level, "implementation "); + EmitAttributes(stream); + stream.Write(this, level, "{0}", TokenTextWriter.SanitizeIdentifier(this.Name)); + EmitSignature(stream, false); + stream.WriteLine(); + + stream.WriteLine(level, "{0}", '{'); + + foreach (Variable! v in this.LocVars) { + v.Emit(stream, level + 1); + } + + if (this.StructuredStmts != null && !CommandLineOptions.Clo.PrintInstrumented && !CommandLineOptions.Clo.PrintInlined) { + if (this.LocVars.Length > 0) { + stream.WriteLine(); + } + if (CommandLineOptions.Clo.PrintUnstructured < 2) { + if (CommandLineOptions.Clo.PrintUnstructured == 1) { + stream.WriteLine(this, level+1, "/*** structured program:"); + } + this.StructuredStmts.Emit(stream, level+1); + if (CommandLineOptions.Clo.PrintUnstructured == 1) { + stream.WriteLine(level+1, "**** end structured program */"); + } + } + } + + if (this.StructuredStmts == null || 1 <= CommandLineOptions.Clo.PrintUnstructured || + CommandLineOptions.Clo.PrintInstrumented || CommandLineOptions.Clo.PrintInlined) + { + foreach (Block b in this.Blocks) + { + b.Emit(stream, level+1); + } + } + + stream.WriteLine(level, "{0}", '}'); + + stream.WriteLine(); + stream.WriteLine(); + } + public override void Register(ResolutionContext! rc) + { + // nothing to register + } + public override void Resolve(ResolutionContext! rc) + { + if (Proc != null) + { + // already resolved + return; + } + DeclWithFormals dwf = rc.LookUpProcedure((!) this.Name); + Proc = dwf as Procedure; + if (dwf == null) + { + rc.Error(this, "implementation given for undeclared procedure: {0}", this.Name); + } + else if (Proc == null) + { + rc.Error(this, "implementations given for function, not procedure: {0}", this.Name); + } + + int previousTypeBinderState = rc.TypeBinderState; + try { + RegisterTypeParameters(rc); + + rc.PushVarContext(); + RegisterFormals(InParams, rc); + RegisterFormals(OutParams, rc); + + foreach (Variable! v in LocVars) + { + v.Register(rc); + v.Resolve(rc); + } + foreach (Variable! v in LocVars) + { + v.ResolveWhere(rc); + } + + rc.PushProcedureContext(); + foreach (Block b in Blocks) + { + b.Register(rc); + } + + ResolveAttributes(rc); + + rc.StateMode = ResolutionContext.State.Two; + foreach (Block b in Blocks) + { + b.Resolve(rc); + } + rc.StateMode = ResolutionContext.State.Single; + + rc.PopProcedureContext(); + rc.PopVarContext(); + + Type.CheckBoundVariableOccurrences(TypeParameters, + InParams.ToTypeSeq, OutParams.ToTypeSeq, + this.tok, "implementation arguments", + rc); + } finally { + rc.TypeBinderState = previousTypeBinderState; + } + SortTypeParams(); + } + public override void Typecheck(TypecheckingContext! tc) + { + base.Typecheck(tc); + + assume this.Proc != null; + + if (this.TypeParameters.Length != Proc.TypeParameters.Length) { + tc.Error(this, "mismatched number of type parameters in procedure implementation: {0}", + this.Name); + } else { + // if the numbers of type parameters are different, it is + // difficult to compare the argument types + MatchFormals(this.InParams, Proc.InParams, "in", tc); + MatchFormals(this.OutParams, Proc.OutParams, "out", tc); + } + + foreach (Variable! v in LocVars) + { + v.Typecheck(tc); + } + IdentifierExprSeq oldFrame = tc.Frame; + tc.Frame = Proc.Modifies; + foreach (Block b in Blocks) + { + b.Typecheck(tc); + } + assert tc.Frame == Proc.Modifies; + tc.Frame = oldFrame; + } + void MatchFormals(VariableSeq! implFormals, VariableSeq! procFormals, + string! inout, TypecheckingContext! tc) + { + if (implFormals.Length != procFormals.Length) + { + tc.Error(this, "mismatched number of {0}-parameters in procedure implementation: {1}", + inout, this.Name); + } + else + { + // unify the type parameters so that types can be compared + assert Proc != null; + assert this.TypeParameters.Length == Proc.TypeParameters.Length; + + IDictionary! subst1 = + new Dictionary (); + IDictionary! subst2 = + new Dictionary (); + + for (int i = 0; i < this.TypeParameters.Length; ++i) { + TypeVariable! newVar = + new TypeVariable (Token.NoToken, Proc.TypeParameters[i].Name); + subst1.Add(Proc.TypeParameters[i], newVar); + subst2.Add(this.TypeParameters[i], newVar); + } + + for (int i = 0; i < implFormals.Length; i++) + { + // the names of the formals are allowed to change from the proc to the impl + + // but types must be identical + Type t = ((Variable!)implFormals[i]).TypedIdent.Type.Substitute(subst2); + Type u = ((Variable!)procFormals[i]).TypedIdent.Type.Substitute(subst1); + if (!t.Equals(u)) + { + string! a = (!) ((Variable!)implFormals[i]).Name; + string! b = (!) ((Variable!)procFormals[i]).Name; + string! c; + if (a == b) { + c = a; + } else { + c = String.Format("{0} (named {1} in implementation)", b, a); + } + tc.Error(this, "mismatched type of {0}-parameter in implementation {1}: {2}", inout, this.Name, c); + } + } + } + } + + private Hashtable/*Variable->Expr*//*?*/ formalMap = null; + public void ResetImplFormalMap() { + this.formalMap = null; + } + public Hashtable /*Variable->Expr*/! GetImplFormalMap() + { + if (this.formalMap != null) + return this.formalMap; + else + { + Hashtable /*Variable->Expr*/! map = new Hashtable /*Variable->Expr*/ (InParams.Length + OutParams.Length); + + assume this.Proc != null; + assume InParams.Length == Proc.InParams.Length; + for (int i = 0; i < InParams.Length; i++) + { + Variable! v = (!) InParams[i]; + IdentifierExpr ie = new IdentifierExpr(v.tok, v); + Variable! pv = (!) Proc.InParams[i]; + map.Add(pv, ie); + } + System.Diagnostics.Debug.Assert(OutParams.Length == Proc.OutParams.Length); + for (int i = 0; i < OutParams.Length; i++) + { + Variable! v = (!) OutParams[i]; + IdentifierExpr ie = new IdentifierExpr(v.tok, v); + Variable! pv = (!) Proc.OutParams[i]; + map.Add(pv, ie); + } + this.formalMap = map; + + if (CommandLineOptions.Clo.PrintWithUniqueASTIds) + { + Console.WriteLine("Implementation.GetImplFormalMap on {0}:", this.Name); + using (TokenTextWriter stream = new TokenTextWriter("", Console.Out, false)) + { + foreach (DictionaryEntry e in map) + { + Console.Write(" "); + ((Variable!)e.Key).Emit(stream, 0); + Console.Write(" --> "); + ((Expr!)e.Value).Emit(stream); + Console.WriteLine(); + } + } + } + + return map; + } + } + + /// + /// Instrument the blocks with the inferred invariants + /// + public override void InstrumentWithInvariants() + { + foreach (Block b in this.Blocks) + { + if (b.Lattice != null) + { + assert b.PreInvariant != null; /* If the pre-abstract state is null, then something is wrong */ + assert b.PostInvariant != null; /* If the post-state is null, then something is wrong */ + + bool instrumentEntry; + bool instrumentExit; + switch (CommandLineOptions.Clo.InstrumentInfer) { + case CommandLineOptions.InstrumentationPlaces.Everywhere: + instrumentEntry = true; + instrumentExit = true; + break; + case CommandLineOptions.InstrumentationPlaces.LoopHeaders: + instrumentEntry = b.widenBlock; + instrumentExit = false; + break; + default: + assert false; // unexpected InstrumentationPlaces value + } + + if (instrumentEntry || instrumentExit) { + CmdSeq newCommands = new CmdSeq(); + if (instrumentEntry) { + Expr inv = (Expr) b.Lattice.ToPredicate(b.PreInvariant); /*b.PreInvariantBuckets.GetDisjunction(b.Lattice);*/ + PredicateCmd cmd = CommandLineOptions.Clo.InstrumentWithAsserts ? (PredicateCmd)new AssertCmd(Token.NoToken,inv) : (PredicateCmd)new AssumeCmd(Token.NoToken, inv); + newCommands.Add(cmd); + } + newCommands.AddRange(b.Cmds); + if (instrumentExit) { + Expr inv = (Expr) b.Lattice.ToPredicate(b.PostInvariant); + PredicateCmd cmd = CommandLineOptions.Clo.InstrumentWithAsserts ? (PredicateCmd)new AssertCmd(Token.NoToken,inv) : (PredicateCmd)new AssumeCmd(Token.NoToken, inv); + newCommands.Add(cmd); + } + b.Cmds = newCommands; + } + } + } + } + + /// + /// Return a collection of blocks that are reachable from the block passed as a parameter. + /// The block must be defined in the current implementation + /// + public ICollection GetConnectedComponents(Block! startingBlock) + { + assert this.Blocks.Contains(startingBlock); + + if(!this.BlockPredecessorsComputed) + ComputeStronglyConnectedComponents(); + +#if DEBUG_PRINT + System.Console.WriteLine("* Strongly connected components * \n{0} \n ** ", scc); +#endif + + foreach(ICollection component in (!) this.scc) + { + foreach(Block! b in component) + { + if(b == startingBlock) // We found the compontent that owns the startingblock + { + return component; + } + } + } + + assert false; // if we are here, it means that the block is not in one of the components. This is an error. + } + + /// + /// Compute the strongly connected compontents of the blocks in the implementation. + /// As a side effect, it also computes the "predecessor" relation for the block in the implementation + /// + override public void ComputeStronglyConnectedComponents() + { + if(!this.BlockPredecessorsComputed) + ComputedPredecessorsForBlocks(); + + Adjacency next = new Adjacency(Successors); + Adjacency prev = new Adjacency(Predecessors); + + this.scc = new StronglyConnectedComponents(this.Blocks, next, prev); + scc.Compute(); + + foreach(Block! block in this.Blocks) + { + block.Predecessors = new BlockSeq(); + } + + } + + /// + /// Reset the abstract stated computed before + /// + override public void ResetAbstractInterpretationState() + { + foreach(Block! b in this.Blocks) + { + b.ResetAbstractInterpretationState(); + } + } + + /// + /// A private method used as delegate for the strongly connected components. + /// It return, given a node, the set of its successors + /// + private IEnumerable/**/! Successors(Block! node) + { + GotoCmd gotoCmd = node.TransferCmd as GotoCmd; + + if(gotoCmd != null) + { // If it is a gotoCmd + assert gotoCmd.labelTargets != null; + + return gotoCmd.labelTargets; + } + else + { // otherwise must be a ReturnCmd + assert node.TransferCmd is ReturnCmd; + + return new List(); + } + } + + /// + /// A private method used as delegate for the strongly connected components. + /// It return, given a node, the set of its predecessors + /// + private IEnumerable/**/! Predecessors(Block! node) + { + assert this.BlockPredecessorsComputed; + + return node.Predecessors; + } + + /// + /// Compute the predecessor informations for the blocks + /// + private void ComputedPredecessorsForBlocks() + { + foreach (Block b in this.Blocks) + { + GotoCmd gtc = b.TransferCmd as GotoCmd; + if (gtc != null) + { + assert gtc.labelTargets != null; + foreach (Block! dest in gtc.labelTargets) + { + dest.Predecessors.Add(b); + } + } + } + this.BlockPredecessorsComputed = true; + } + + public void PruneUnreachableBlocks() { + ArrayList /*Block!*/ visitNext = new ArrayList /*Block!*/ (); + List reachableBlocks = new List(); + System.Compiler.IMutableSet /*Block!*/ reachable = new System.Compiler.HashSet /*Block!*/ (); // the set of elements in "reachableBlocks" + + visitNext.Add(this.Blocks[0]); + while (visitNext.Count != 0) { + Block! b = (Block!)visitNext[visitNext.Count-1]; + visitNext.RemoveAt(visitNext.Count-1); + if (!reachable.Contains(b)) { + reachableBlocks.Add(b); + reachable.Add(b); + if (b.TransferCmd is GotoCmd) { + foreach (Cmd! s in b.Cmds) { + if (s is PredicateCmd) { + LiteralExpr e = ((PredicateCmd)s).Expr as LiteralExpr; + if (e != null && e.IsFalse) { + // This statement sequence will never reach the end, because of this "assume false" or "assert false". + // Hence, it does not reach its successors. + b.TransferCmd = new ReturnCmd(b.TransferCmd.tok); + goto NEXT_BLOCK; + } + } + } + // it seems that the goto statement at the end may be reached + foreach (Block! succ in (!)((GotoCmd)b.TransferCmd).labelTargets) { + visitNext.Add(succ); + } + } + } + NEXT_BLOCK: {} + } + + this.Blocks = reachableBlocks; + } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitImplementation(this); + } + } + + + public class TypedIdent : Absy + { + public const string NoName = ""; + public string! Name; + public Type! Type; + public Expr WhereExpr; + // [NotDelayed] + public TypedIdent (IToken! tok, string! name, Type! type) + ensures this.WhereExpr == null; //PM: needed to verify BoogiePropFactory.FreshBoundVariable + { + this(tok, name, type, null); // here for aesthetic reasons + } + // [NotDelayed] + public TypedIdent (IToken! tok, string! name, Type! type, Expr whereExpr) + : base(tok) + ensures this.WhereExpr == whereExpr; + { + this.Name = name; + this.Type = type; + this.WhereExpr = whereExpr; + // base(tok); + } + public bool HasName { + get { + return this.Name != NoName; + } + } + public void Emit(TokenTextWriter! stream) + { + stream.SetToken(this); + if (this.Name != NoName) + { + stream.Write("{0}: ", TokenTextWriter.SanitizeIdentifier(this.Name)); + } + this.Type.Emit(stream); + if (this.WhereExpr != null) + { + stream.Write(" where "); + this.WhereExpr.Emit(stream); + } + } + public override void Resolve(ResolutionContext! rc) + { + // NOTE: WhereExpr needs to be resolved by the caller, because the caller must provide a modified ResolutionContext + this.Type = this.Type.ResolveType(rc); + } + public override void Typecheck(TypecheckingContext! tc) + { +// type variables can occur when working with polymorphic functions/procedures +// if (!this.Type.IsClosed) +// tc.Error(this, "free variables in type of an identifier: {0}", +// this.Type.FreeVariables); + if (this.WhereExpr != null) { + this.WhereExpr.Typecheck(tc); + assert this.WhereExpr.Type != null; // follows from postcondition of Expr.Typecheck + if (!this.WhereExpr.Type.Unify(Type.Bool)) + { + tc.Error(this, "where clauses must be of type bool"); + } + } + } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitTypedIdent(this); + } + } + + /// + /// Conceptually, a LatticeElementList is a infinite array indexed from 0, + /// where some finite number of elements have a non-null value. All elements + /// have type Lattice.Element. + /// + /// The Count property returns the first index above all non-null values. + /// + /// The [i] getter returns the element at position i, which may be null. The + /// index i is not allowed to be negative. + /// The [i] setter sets the element at position i. As a side effect, this + /// operation may increase Count. The index i is not allowed to be negative. + /// The right-hand value of the setter is not allowed to be null; that is, + /// null can occur in the list only as an "unused" element. + /// + public class LatticeElementList : ArrayList + { + public new /*Maybe null*/ AI.Lattice.Element this [ int i ] + { + get + { + if (i < Count) + { + return (AI.Lattice.Element)base[i]; + } + else + { + return null; + } + } + set + { + System.Diagnostics.Debug.Assert(value != null); + while (Count <= i) + { + Add(null); + } + base[i] = value; + } + } + /// + /// Returns the disjunction of (the expression formed from) the + /// non-null lattice elements in the list. The expressions are + /// formed according to the given "lattice", which is assumed to + /// be the lattice of the lattice elements stored in the list. + /// + /// + /// + public Expr GetDisjunction(AI.Lattice! lattice) + { + Expr disjunction = null; + foreach (AI.Lattice.Element el in this) + { + if (el != null) + { + Expr e = (Expr) lattice.ToPredicate(el); + if (disjunction == null) + { + disjunction = e; + } + else + { + disjunction = Expr.Or(disjunction, e); + } + } + } + if (disjunction == null) + { + return Expr.False; + } + else + { + return disjunction; + } + } + } + + + + public abstract class BoogieFactory { + public static Expr! IExpr2Expr(AI.IExpr! e) { + Variable v = e as Variable; + if (v != null) { + return new IdentifierExpr(Token.NoToken, v); + } + else if (e is AI.IVariable) { // but not a Variable + return new AIVariableExpr(Token.NoToken, (AI.IVariable)e); + } + else if (e is IdentifierExpr.ConstantFunApp) { + return ((IdentifierExpr.ConstantFunApp)e).IdentifierExpr; + } + else if (e is QuantifierExpr.AIQuantifier) { + return ((QuantifierExpr.AIQuantifier)e).arg.RealQuantifier; + } + else { + return (Expr)e; + } + } + public static ExprSeq! IExprArray2ExprSeq(IList/**/! a) { + Expr[] e = new Expr[a.Count]; + int i = 0; + foreach (AI.IExpr! aei in a) { + e[i] = IExpr2Expr(aei); + i++; + } + return new ExprSeq(e); + } + + // Convert a Boogie type into an AIType if possible. This should be + // extended when AIFramework gets more types. + public static AI.AIType! Type2AIType(Type! t) + { +// if (t.IsRef) +// return AI.Ref.Type; +// else + if (t.IsInt) + return AI.Int.Type; +// else if (t.IsName) PR: how to handle this case? +// return AI.FieldName.Type; + else + return AI.Value.Type; + } + } + + #region Generic Sequences + //--------------------------------------------------------------------- + // Generic Sequences + //--------------------------------------------------------------------- + + public sealed class TypedIdentSeq : PureCollections.Sequence + { + public TypedIdentSeq(params Type[]! args) : base(args) { } + public new TypedIdent this[int index] + { + get + { + return (TypedIdent)base[index]; + } + set + { + base[index] = value; + } + } + } + + public sealed class RequiresSeq : PureCollections.Sequence + { + public RequiresSeq(params Requires[]! args) : base(args) { } + public new Requires! this[int index] + { + get + { + return (Requires!) base[index]; + } + set + { + base[index] = value; + } + } + } + + public sealed class EnsuresSeq : PureCollections.Sequence + { + public EnsuresSeq(params Ensures[]! args) : base(args) { } + public new Ensures! this[int index] + { + get + { + return (Ensures!) base[index]; + } + set + { + base[index] = value; + } + } + } + + public sealed class VariableSeq : PureCollections.Sequence + { + public VariableSeq(params Variable[]! args) + : base(args) + { + } + public VariableSeq(VariableSeq! varSeq) + : base(varSeq) + { + } + public new Variable this[int index] + { + get + { + return (Variable)base[index]; + } + set + { + base[index] = value; + } + } + public void Emit(TokenTextWriter! stream) + { + string sep = ""; + foreach (Variable! v in this) + { + stream.Write(sep); + sep = ", "; + v.EmitVitals(stream, 0); + } + } + public TypeSeq! ToTypeSeq { get { + TypeSeq! res = new TypeSeq (); + foreach(Variable! v in this) + res.Add(v.TypedIdent.Type); + return res; + } } + } + + public sealed class TypeSeq : PureCollections.Sequence + { + public TypeSeq(params Type[]! args) + : base(args) + { + } + public TypeSeq(TypeSeq! varSeq) + : base(varSeq) + { + } + public new Type! this[int index] + { + get + { + return (Type!)base[index]; + } + set + { + base[index] = value; + } + } + public List! ToList() { + List! res = new List (Length); + foreach (Type! t in this) + res.Add(t); + return res; + } + public void Emit(TokenTextWriter! stream, string! separator) + { + string sep = ""; + foreach (Type! v in this) + { + stream.Write(sep); + sep = separator; + v.Emit(stream); + } + } + } + + public sealed class TypeVariableSeq : PureCollections.Sequence + { + public TypeVariableSeq(params TypeVariable[]! args) + : base(args) + { + } + public TypeVariableSeq(TypeVariableSeq! varSeq) + : base(varSeq) + { + } +/* PR: the following two constructors cause Spec# crashes + public TypeVariableSeq(TypeVariable! var) + : base(new TypeVariable! [] { var }) + { + } + public TypeVariableSeq() + : base(new TypeVariable![0]) + { + } */ + public new TypeVariable! this[int index] + { + get + { + return (TypeVariable!)base[index]; + } + set + { + base[index] = value; + } + } + public void AppendWithoutDups(TypeVariableSeq! s1) { + for (int i = 0; i < s1.card; i++) { + TypeVariable! next = s1[i]; + if (!this.Has(next)) this.Add(next); + } + } + public void Emit(TokenTextWriter! stream, string! separator) + { + string sep = ""; + foreach (TypeVariable! v in this) + { + stream.Write(sep); + sep = separator; + v.Emit(stream); + } + } + public new TypeVariable[]! ToArray() { + TypeVariable[]! n = new TypeVariable[Length]; + int ct = 0; + foreach (TypeVariable! var in this) + n[ct++] = var; + return n; + } + public List! ToList() { + List! res = new List (Length); + foreach (TypeVariable! var in this) + res.Add(var); + return res; + } + } + + public sealed class IdentifierExprSeq : PureCollections.Sequence + { + public IdentifierExprSeq(params IdentifierExpr[]! args) + : base(args) + { + } + public IdentifierExprSeq(IdentifierExprSeq! ideSeq) + : base(ideSeq) + { + } + public new IdentifierExpr! this[int index] + { + get + { + return (IdentifierExpr!)base[index]; + } + set + { + base[index] = value; + } + } + + public void Emit(TokenTextWriter! stream, bool printWhereComments) + { + string sep = ""; + foreach (IdentifierExpr! e in this) + { + stream.Write(sep); + sep = ", "; + e.Emit(stream); + + if (printWhereComments && e.Decl != null && e.Decl.TypedIdent.WhereExpr != null) { + stream.Write(" /* where "); + e.Decl.TypedIdent.WhereExpr.Emit(stream); + stream.Write(" */"); + } + } + } + } + + + public sealed class CmdSeq : PureCollections.Sequence + { + public CmdSeq(params Cmd[]! args) : base(args){} + public CmdSeq(CmdSeq! cmdSeq) + : base(cmdSeq) + { + } + public new Cmd! this[int index] + { + get + { + return (Cmd!)base[index]; + } + set + { + base[index] = value; + } + } + } + + public sealed class ExprSeq : PureCollections.Sequence + { + public ExprSeq(params Expr[]! args) + : base(args) + { + } + public ExprSeq(ExprSeq! exprSeq) + : base(exprSeq) + { + } + public new Expr this[int index] + { + get + { + return (Expr)base[index]; + } + set + { + base[index] = value; + } + } + + public new Expr Last() { return (Expr)base.Last(); } + + public static ExprSeq operator +(ExprSeq a, ExprSeq b) + { + if (a==null) throw new ArgumentNullException("a"); + if (b==null) throw new ArgumentNullException("b"); + return Append(a,b); + } + + public static ExprSeq Append(ExprSeq! s, ExprSeq! t) + { + Expr[] n = new Expr[s.card+t.card]; + for (int i = 0; i< s.card; i++) n[i] = s[i]; + for (int i = 0; i< t.card; i++) n[s.card+i] = t[i]; + return new ExprSeq(n); + } + public void Emit(TokenTextWriter! stream) + { + string sep = ""; + foreach (Expr! e in this) + { + stream.Write(sep); + sep = ", "; + e.Emit(stream); + } + } + public TypeSeq! ToTypeSeq { get { + TypeSeq! res = new TypeSeq (); + foreach(Expr e in this) + res.Add(((!)e).Type); + return res; + } } + } + + public sealed class TokenSeq : PureCollections.Sequence + { + public TokenSeq(params Token[]! args) + : base(args) + { + } + public new Token this[int index] + { + get + { + return (Token)base[index]; + } + set + { + base[index] = value; + } + } + } + + public sealed class StringSeq : PureCollections.Sequence + { + public StringSeq(params string[]! args) + : base(args) + { + } + public new String this[int index] + { + get + { + return (String)base[index]; + } + set + { + base[index] = value; + } + } + public void Emit(TokenTextWriter! stream) + { + string sep = ""; + foreach (string! s in this) + { + stream.Write(sep); + sep = ", "; + stream.Write(s); + } + } + } + + public sealed class BlockSeq : PureCollections.Sequence + { + public BlockSeq(params Block[]! args) + : base(args) + { + } + public BlockSeq(BlockSeq! blockSeq) + : base(blockSeq) + { + } + + public new Block this[int index] + { + get + { + return (Block)base[index]; + } + set + { + base[index] = value; + } + } + } + + public static class Emitter { + public static void Declarations(List! decls, TokenTextWriter! stream) + { + bool first = true; + foreach (Declaration d in decls) + { + if (d == null) continue; + if (first) + { + first = false; + } + else + { + stream.WriteLine(); + } + d.Emit(stream, 0); + } + } + } + public sealed class DeclarationSeq : PureCollections.Sequence + { + public DeclarationSeq(params string[]! args) + : base(args) + { + } + public new Declaration this[int index] + { + get + { + return (Declaration)base[index]; + } + set + { + base[index] = value; + } + } + public void Emit(TokenTextWriter! stream) + { + bool first = true; + foreach (Declaration d in this) + { + if (d == null) continue; + if (first) + { + first = false; + } + else + { + stream.WriteLine(); + } + d.Emit(stream, 0); + } + } + public void InstrumentWithInvariants () + { + foreach (Declaration! d in this) + { + d.InstrumentWithInvariants(); + } + } + } + #endregion + + + #region Regular Expressions + // a data structure to recover the "program structure" from the flow graph + public sealed class RESeq : PureCollections.Sequence + { + public RESeq(params RE[]! args) + : base (args) + { + } + public RESeq(RESeq! reSeq) + : base(reSeq) + { + } + public new RE this[int index] + { + get + { + return (RE)base[index]; + } + set + { + base[index] = value; + } + } + // public void Emit(TokenTextWriter stream) + // { + // string sep = ""; + // foreach (RE e in this) + // { + // stream.Write(sep); + // sep = ", "; + // e.Emit(stream); + // } + // } + } + public abstract class RE : Cmd + { + public RE() : base(Token.NoToken) {} + public override void AddAssignedVariables(VariableSeq! vars) { throw new NotImplementedException(); } + } + public class AtomicRE : RE + { + public Block! b; + public AtomicRE(Block! block) { b = block; } + public override void Resolve(ResolutionContext! rc) + { + b.Resolve(rc); + } + public override void Typecheck(TypecheckingContext! tc) + { + b.Typecheck(tc); + } + public override void Emit(TokenTextWriter! stream, int level) + { + b.Emit(stream,level); + } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitAtomicRE(this); + } + } + public abstract class CompoundRE : RE + { + public override void Resolve(ResolutionContext! rc) + { + return; + } + public override void Typecheck(TypecheckingContext! tc) + { + return; + } + } + public class Sequential : CompoundRE + { + public RE! first; + public RE! second; + public Sequential(RE! a, RE! b) + { + first = a; + second = b; + } + public override void Emit(TokenTextWriter! stream, int level) + { + stream.WriteLine(); + stream.WriteLine("{0};", Indent(level)); + first.Emit(stream,level+1); + second.Emit(stream,level+1); + } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitSequential(this); + } + } + public class Choice : CompoundRE + { + public RESeq! rs; + public Choice(RESeq! operands) + { + rs = operands; + // base(); + } + public override void Emit(TokenTextWriter! stream, int level) + { + stream.WriteLine(); + stream.WriteLine("{0}[]", Indent(level)); + foreach (RE! r in rs ) + r.Emit(stream,level+1); + } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitChoice(this); + } + } + public class DAG2RE + { + public static RE! Transform(Block! b) + { + TransferCmd tc = b.TransferCmd; + if ( tc is ReturnCmd ) + { + return new AtomicRE(b); + } + else if ( tc is GotoCmd ) + { + GotoCmd! g = (GotoCmd) tc ; + assume g.labelTargets != null; + if ( g.labelTargets.Length == 1 ) + { + return new Sequential(new AtomicRE(b),Transform( (!) g.labelTargets[0])); + } + else + { + RESeq rs = new RESeq(); + foreach (Block! target in g.labelTargets ) + { + RE r = Transform(target); + rs.Add(r); + } + RE second = new Choice(rs); + return new Sequential(new AtomicRE(b),second); + } + } + else + { + assume false; + return new AtomicRE(b); + } + } + } + + #endregion + + // NOTE: This class is here for convenience, since this file's + // classes are used pretty much everywhere. + + public class BoogieDebug + { + public static bool DoPrinting = false; + + public static void Write (string! format, params object[]! args) + { + if (DoPrinting) { Console.Error.Write(format, args); } + } + + public static void WriteLine (string! format, params object[]! args) + { + if (DoPrinting) { Console.Error.WriteLine(format, args); } + } + + public static void WriteLine () { if (DoPrinting) { Console.Error.WriteLine(); } } + } + +} diff --git a/Source/Core/AbsyCmd.cs b/Source/Core/AbsyCmd.cs new file mode 100644 index 00000000..7aa3e1fc --- /dev/null +++ b/Source/Core/AbsyCmd.cs @@ -0,0 +1,2472 @@ +//----------------------------------------------------------------------------- +// +// 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 Microsoft.Boogie.AbstractInterpretation; + using AI = Microsoft.AbstractInterpretationFramework; + using Microsoft.Contracts; + + + //--------------------------------------------------------------------- + // BigBlock + public class BigBlock + { + public readonly IToken! tok; + public string LabelName; + public readonly bool Anonymous; + invariant !Anonymous ==> LabelName != null; + [Rep] public CmdSeq! simpleCmds; + public StructuredCmd ec; + public TransferCmd tc; + invariant ec == null || tc == null; + public BigBlock successorBigBlock; // null if successor is end of proceduure body (or if field has not yet been initialized) + + public BigBlock(IToken! tok, string? labelName, [Captured] CmdSeq! simpleCmds, StructuredCmd? ec, TransferCmd? tc) + requires ec == null || tc == null; + { + this.tok = tok; + this.LabelName = labelName; + this.Anonymous = labelName == null; + this.simpleCmds = simpleCmds; + this.ec = ec; + this.tc = tc; + } + + public void Emit(TokenTextWriter! stream, int level) { + 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) { + 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] public readonly List! BigBlocks; + public CmdSeq PrefixCommands; + public readonly IToken! EndCurly; + public StmtList ParentContext; + public BigBlock ParentBigBlock; + public Set! Labels = new Set(); + + public StmtList([Captured] List! bigblocks, IToken! endCurly) + requires bigblocks.Count > 0; + { + this.BigBlocks = bigblocks; + this.EndCurly = endCurly; + } + + // prints the list of statements, not the surrounding curly braces + public void Emit(TokenTextWriter! stream, int level) { + bool needSeperator = false; + foreach (BigBlock b in BigBlocks) { + assume b.IsPeerConsistent; + 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] CmdSeq! prefixCmds, ref string! suggestedLabel) + ensures !result ==> Owner.None(prefixCmds); // "prefixCmds" is captured only on success + { + assume PrefixCommands == null; // prefix has not been used + + BigBlock bb0 = BigBlocks[0]; + if (prefixCmds.Length == 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 { + 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; + CmdSeq simpleCmds; + + void Dump(StructuredCmd scmd, TransferCmd tcmd) + requires scmd == null || tcmd == null; + ensures label == null && simpleCmds == null; + { + if (label == null && simpleCmds == null && scmd == null && tcmd == null) { + // nothing to do + } else { + if (simpleCmds == null) { + simpleCmds = new CmdSeq(); + } + 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) { + Dump(null, null); + if (bigBlocks.Count == 0) { + simpleCmds = new CmdSeq(); // 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) { + if (simpleCmds == null) { + simpleCmds = new CmdSeq(); + } + simpleCmds.Add(cmd); + } + + public void Add(StructuredCmd! scmd) { + Dump(scmd, null); + } + + public void Add(TransferCmd! tcmd) { + Dump(null, tcmd); + } + + public void AddLabelCmd(string! label) { + Dump(null, null); + this.label = label; + } + + public void AddLocalVariable(string! name) { + // TODO + } + } + + class BigBlocksResolutionContext { + StmtList! stmtList; + [Peer] List blocks; + string! prefix = "anon"; + int anon = 0; + Set allLabels = new Set(); + Errors! errorHandler; + + public BigBlocksResolutionContext(StmtList! stmtList, Errors! errorHandler) { + this.stmtList = stmtList; + this.errorHandler = errorHandler; + } + + public List! Blocks { + get { + if (blocks == null) { + blocks = new List(); + + int startErrorCount = this.errorHandler.count; + // Check that there are no goto's into the middle of a block, 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) + requires parentContext == null <==> parentBigBlock == null; + requires stmtList.ParentContext == null; // it hasn't been set yet + modifies stmtList.*; + 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.Labels.Add(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 (!)g.labelNames) { + 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"); + } + } + } + + // break labels must refer to an enclosing while statement + else if (b.ec is BreakCmd) { + BreakCmd bcmd = (BreakCmd)b.ec; + assert bcmd.BreakEnclosure == null; // it hasn't been initialized yet + bool found = false; + for (StmtList sl = stmtList; sl.ParentBigBlock != null; sl = sl.ParentContext) + invariant 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.Length == 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) { + foreach (BigBlock b in stmtList.BigBlocks) { + if (b.LabelName == null) { + b.LabelName = prefix + anon; + anon++; + } + 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) { + 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, successor); + } 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) + requires blocks != null; + { + CmdSeq cmdPrefixToApply = stmtList.PrefixCommands; + + int n = stmtList.BigBlocks.Count; + foreach (BigBlock b in stmtList.BigBlocks) { + n--; + assert b.LabelName != null; + CmdSeq theSimpleCmds; + if (cmdPrefixToApply == null) { + theSimpleCmds = b.simpleCmds; + } else { + theSimpleCmds = new CmdSeq(); + 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 + 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 StringSeq(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; + 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; + string loopHeadLabel = prefix + anon + "_LoopHead"; + string! loopBodyLabel = prefix + anon + "_LoopBody"; + string loopDoneLabel = prefix + anon + "_LoopDone"; + anon++; + + CmdSeq ssBody = new CmdSeq(); + CmdSeq ssDone = new CmdSeq(); + if (wcmd.Guard != null) { + ssBody.Add(new AssumeCmd(wcmd.tok, wcmd.Guard)); + ssDone.Add(new AssumeCmd(wcmd.tok, Expr.Not(wcmd.Guard))); + } + + // 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 StringSeq(loopHeadLabel))); + blocks.Add(block); + + // LoopHead: assert/assume loop_invariant; goto LoopDone, LoopBody; + CmdSeq ssHead = new CmdSeq(); + foreach (PredicateCmd inv in wcmd.Invariants) { + ssHead.Add(inv); + } + block = new Block(wcmd.tok, loopHeadLabel, ssHead, new GotoCmd(wcmd.tok, new StringSeq(loopDoneLabel, loopBodyLabel))); + blocks.Add(block); + + if (!bodyGuardTakenCareOf) { + // LoopBody: assume guard; goto firstLoopBlock; + block = new Block(wcmd.tok, loopBodyLabel, ssBody, new GotoCmd(wcmd.tok, new StringSeq(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 StringSeq(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; + CmdSeq predCmds = theSimpleCmds; + + for (; ifcmd != null; ifcmd = ifcmd.elseIf) { + string! thenLabel = prefix + anon + "_Then"; + string! elseLabel = prefix + anon + "_Else"; + anon++; + + CmdSeq ssThen = new CmdSeq(); + CmdSeq ssElse = new CmdSeq(); + if (ifcmd.Guard != null) { + ssThen.Add(new AssumeCmd(ifcmd.tok, ifcmd.Guard)); + ssElse.Add(new AssumeCmd(ifcmd.tok, Expr.Not(ifcmd.Guard))); + } + + // 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 StringSeq(thenLabel, elseLabel))); + blocks.Add(block); + + if (!thenGuardTakenCareOf) { + // Then: assume guard; goto firstThenBlock; + block = new Block(ifcmd.tok, thenLabel, ssThen, new GotoCmd(ifcmd.tok, new StringSeq(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) { + assert ifcmd.elseIf == null; + if (!elseGuardTakenCareOf) { + // Else: assume !guard; goto firstElseBlock; + block = new Block(ifcmd.tok, elseLabel, ssElse, new GotoCmd(ifcmd.tok, new StringSeq(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 CmdSeq(); + if (ifcmd.Guard != null) { + predCmds.Add(new AssumeCmd(ifcmd.tok, Expr.Not(ifcmd.Guard))); + } + + } 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 StringSeq(runOffTheEndLabel)); + } else { + trCmd = GotoSuccessor(ifcmd.tok, b); + } + block = new Block(ifcmd.tok, elseLabel, ssElse, trCmd); + blocks.Add(block); + } + } + } + } + } + + TransferCmd! GotoSuccessor(IToken! tok, BigBlock! b) { + if (b.successorBigBlock != null) { + return new GotoCmd(tok, new StringSeq(b.successorBigBlock.LabelName)); + } else { + return new ReturnCmd(tok); + } + } + } + + public abstract class StructuredCmd + { + public IToken! tok; + public StructuredCmd(IToken! tok) + { + this.tok = tok; + } + + public abstract void Emit(TokenTextWriter! stream, int level); + } + + public class IfCmd : StructuredCmd + { + public Expr? Guard; + public StmtList! thn; + public IfCmd? elseIf; + public StmtList elseBlock; + invariant elseIf == null || elseBlock == null; + + public IfCmd(IToken! tok, Expr? guard, StmtList! thn, IfCmd? elseIf, StmtList elseBlock) + : base(tok) + requires elseIf == null || elseBlock == null; + { + this.Guard = guard; + this.thn = thn; + this.elseIf = elseIf; + this.elseBlock = elseBlock; + // base(tok); + } + + 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; + + public WhileCmd(IToken! tok, [Captured] Expr? guard, List! invariants, StmtList! body) + : base(tok) + { + this.Guard = guard; + this.Invariants = invariants; + this.Body = body; + /// base(tok); + } + + 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 "); + } + 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) + { + this.Label = label; + // base(tok); + } + + 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 + { + public 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 + [Rep] [ElementsPeer] public CmdSeq! Cmds; + [Rep] //PM: needed to verify Traverse.Visit + public TransferCmd TransferCmd; // maybe null only because we allow deferred initialization (necessary for cyclic structures) + + // Abstract interpretation + + // public bool currentlyTraversed; + + public enum VisitState {ToVisit, BeingVisited, AlreadyVisited}; // used by WidenPoints.Compute + public VisitState TraversingStatus; + + 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 + + // Block-specific invariants... + public AI.Lattice Lattice; // The lattice used for the analysis of this block + public AI.Lattice.Element PreInvariant; // The initial abstract states for this block + public AI.Lattice.Element PostInvariant; // The exit abstract states for this block + // KRML: We want to include the following invariant, but at the moment, doing so causes a run-time error (something about committed): invariant PreInvariant != null <==> PostInvariant != null; + + // VC generation and SCC computation + public BlockSeq! Predecessors; + + public Set liveVarsBefore; + public bool IsLive(Variable! v) { + if (liveVarsBefore == null) return true; + return liveVarsBefore.Contains(v); + } + + public Block() { this(Token.NoToken, "", new CmdSeq(), new ReturnCmd(Token.NoToken));} + + public Block (IToken! tok, string! label, CmdSeq! cmds, TransferCmd transferCmd) + : base(tok) + { + this.Label = label; + this.Cmds = cmds; + this.TransferCmd = transferCmd; + this.PreInvariant = null; + this.PostInvariant = null; + this.Predecessors = new BlockSeq(); + this.liveVarsBefore = null; + this.TraversingStatus = VisitState.ToVisit; + this.iterations = 0; + // base(tok); + } + + public void Emit (TokenTextWriter! stream, int level) + { + 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) + { + c.Emit(stream, level + 1); + } + assume this.TransferCmd != null; + this.TransferCmd.Emit(stream, level + 1); + } + + public void Register (ResolutionContext! rc) + { + rc.AddBlock(this); + } + + public override void Resolve (ResolutionContext! rc) + { + foreach (Cmd! c in Cmds) + { + c.Resolve(rc); + } + assume this.TransferCmd != null; + TransferCmd.Resolve(rc); + } + + public override void Typecheck (TypecheckingContext! tc) + { + foreach (Cmd! c in Cmds) + { + c.Typecheck(tc); + } + 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; + this.Lattice = null; + this.PreInvariant = null; + this.PostInvariant = null; + } + + [Pure] + public override string! ToString() + { + return this.Label + (this.widenBlock? "[w]" : ""); + } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitBlock(this); + } + } + + //--------------------------------------------------------------------- + // Commands + + public abstract class Cmd : Absy + { + public Cmd(IToken! tok) : base(tok) { } + public abstract void Emit(TokenTextWriter! stream, int level); + public abstract void AddAssignedVariables(VariableSeq! vars); + public void CheckAssignments(TypecheckingContext! tc) + { + VariableSeq! vars = new VariableSeq(); + this.AddAssignedVariables(vars); + foreach (Variable! v in vars) + { + if (!v.IsMutable) + { + tc.Error(this, "command assigns to an immutable variable: {0}", v.Name); + } + else if (v is GlobalVariable && !tc.InFrame(v)) + { + tc.Error(this, "command assigns to a global variable that is not in the enclosing method's modifies clause: {0}", v.Name); + } + } + } + + // Methods to simulate the old SimpleAssignCmd and MapAssignCmd + public static AssignCmd! SimpleAssign(IToken! tok, IdentifierExpr! lhs, Expr! rhs) { + 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, + ExprSeq! indexes, Expr! rhs) { + List! lhss = new List (); + List! rhss = new List (); + List! indexesList = new List (); + + foreach (Expr e in indexes) + indexesList.Add((!)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) + requires args.Length > 0; // at least the rhs + requires forall{int i in (0:args.Length); args[i] != null}; + { + List! lhss = new List (); + List! rhss = new List (); + List! indexesList = new List (); + + for (int i = 0; i < args.Length - 1; ++i) + indexesList.Add((!)args[i]); + + lhss.Add(new MapAssignLhs (map.tok, + new SimpleAssignLhs (map.tok, map), + indexesList)); + rhss.Add((!)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) + { + for (QKeyValue kv = attributes; kv != null; kv = kv.Next) { + kv.Emit(stream); + stream.Write(" "); + } + } + public static void ResolveAttributes(QKeyValue attributes, ResolutionContext! rc) + { + for (QKeyValue kv = attributes; kv != null; kv = kv.Next) { + kv.Resolve(rc); + } + } + public static void TypecheckAttributes(QKeyValue attributes, TypecheckingContext! tc) + { + for (QKeyValue kv = attributes; kv != null; kv = kv.Next) { + kv.Typecheck(tc); + } + } + } + + public class CommentCmd : Cmd // just a convenience for debugging + { + public readonly string! Comment; + public CommentCmd (string! c) + : base(Token.NoToken) + { + Comment = c; + // base(Token.NoToken); + } + public override void Emit(TokenTextWriter! stream, int level) + { + 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(VariableSeq! 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 { + public List! Lhss; + public List! Rhss; + + public AssignCmd(IToken! tok, List! lhss, List! rhss) { + base(tok); + Lhss = lhss; + Rhss = rhss; + } + + public override void Emit(TokenTextWriter! stream, int level) + { + stream.Write(this, level, ""); + + string! sep = ""; + foreach (AssignLhs! l in Lhss) { + stream.Write(sep); + sep = ", "; + l.Emit(stream); + } + + stream.Write(" := "); + + sep = ""; + foreach (Expr! e in Rhss) { + 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) + e.Resolve(rc); + foreach (Expr! e in Rhss) + 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 (((!)Lhss[i].DeepAssignedVariable).Equals( + Lhss[j].DeepAssignedVariable)) + rc.Error(Lhss[j], + "variable {0} is assigned more than once in parallel assignment", + Lhss[j].DeepAssignedVariable); + } + } + } + + public override void Typecheck(TypecheckingContext! tc) { + foreach (AssignLhs! e in Lhss) + e.Typecheck(tc); + foreach (Expr! e in Rhss) + 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(VariableSeq! vars) + { + foreach (AssignLhs! l in Lhss) + 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 { + 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 + 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) {} + 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); + } + + public class SimpleAssignLhs : AssignLhs { + public IdentifierExpr! AssignedVariable; + + public override Type Type { get { + return AssignedVariable.Type; + } } + + public override IdentifierExpr! DeepAssignedIdentifier { get { + return AssignedVariable; + } } + + public override Variable DeepAssignedVariable { get { + return AssignedVariable.Decl; + } } + + public SimpleAssignLhs(IToken! tok, IdentifierExpr! assignedVariable) { + base(tok); + 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 { + 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; + + // 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 { + return Map.DeepAssignedIdentifier; + } } + + public override Variable DeepAssignedVariable { get { + return Map.DeepAssignedVariable; + } } + + public MapAssignLhs(IToken! tok, AssignLhs! map, List! indexes) { + base(tok); + Map = map; + Indexes = indexes; + } + public override void Resolve(ResolutionContext! rc) { + Map.Resolve(rc); + foreach (Expr! e in Indexes) + e.Resolve(rc); + } + public override void Typecheck(TypecheckingContext! tc) { + Map.Typecheck(tc); + foreach (Expr! e in Indexes) + e.Typecheck(tc); + + // we use the same typechecking code as in MapSelect + ExprSeq! selectArgs = new ExprSeq (); + foreach (Expr! e in Indexes) + selectArgs.Add(e); + TypeParamInstantiation! tpInsts; + TypeAttr = + MapSelect.Typecheck((!)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) { + stream.Write(sep); + sep = ", "; + e.Emit(stream); + } + stream.Write("]"); + } + public override Expr! AsExpr { get { + NAryExpr! res = Expr.Select(Map.AsExpr, Indexes); + res.TypeParameters = this.TypeParameters; + return res; + } } + internal override void AsSimpleAssignment(Expr! rhs, + out IdentifierExpr! simpleLhs, + out Expr! simpleRhs) { + NAryExpr! newRhs = Expr.Store(Map.AsExpr, Indexes, rhs); + newRhs.TypeParameters = this.TypeParameters; + Map.AsSimpleAssignment(newRhs, out simpleLhs, out simpleRhs); + } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + 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 + { + public /*readonly, except for the StandardVisitor*/ VariableSeq! Locals; + public /*readonly, except for the StandardVisitor*/ CmdSeq! Cmds; + + public StateCmd(IToken! tok, VariableSeq! locals, CmdSeq! cmds) + : base(tok) + { + this.Locals = locals; + this.Cmds = cmds; + // base(tok); + } + + public override void Resolve(ResolutionContext! rc) { + rc.PushVarContext(); + foreach (Variable! v in Locals) { + rc.AddVariable(v, false); + } + foreach (Cmd! cmd in Cmds) { + cmd.Resolve(rc); + } + rc.PopVarContext(); + } + + public override void AddAssignedVariables(VariableSeq! vars) { + VariableSeq! vs = new VariableSeq(); + foreach (Cmd! cmd in this.Cmds) + { + cmd.AddAssignedVariables(vs); + } + System.Collections.Hashtable! localsSet = new System.Collections.Hashtable(); + foreach (Variable! local in this.Locals) + { + localsSet[local] = bool.TrueString; + } + foreach (Variable! v in vs) + { + if (!localsSet.ContainsKey(v)) + { + vars.Add(v); + } + } + } + + public override void Typecheck(TypecheckingContext! tc) { + foreach (Cmd! cmd in Cmds) { + cmd.Typecheck(tc); + } + } + + public override void Emit(TokenTextWriter! stream, int level) { + stream.WriteLine(this, level, "{"); + foreach (Variable! v in Locals) { + v.Emit(stream, level+1); + } + foreach (Cmd! c in Cmds) { + c.Emit(stream, level+1); + } + stream.WriteLine(level, "}"); + } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitStateCmd(this); + } + } + + abstract public class SugaredCmd : Cmd + { + private Cmd desugaring; // null until desugared + + public SugaredCmd(IToken! tok) : base(tok) {} + + public Cmd! Desugaring { + get { + if (desugaring == null) { + desugaring = ComputeDesugaring(); + } + return desugaring; + } + } + protected abstract Cmd! ComputeDesugaring(); + + public override void Emit(TokenTextWriter! stream, int level) { + if (CommandLineOptions.Clo.PrintDesugarings) { + stream.WriteLine(this, level, "/*** desugaring:"); + Desugaring.Emit(stream, level); + stream.WriteLine(level, "**** end desugaring */"); + } + } + } + + public abstract class CallCommonality : SugaredCmd + { + public QKeyValue Attributes; + + protected CallCommonality(IToken! tok, QKeyValue kv) { + base(tok); + 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(VariableSeq! tempVars, Variable! likeThisOne, Type! ty, TempVarKind kind) { + string! tempNamePrefix; + switch (kind) { + case TempVarKind.Formal: + tempNamePrefix = "formal@"; + break; + case TempVarKind.Old: + tempNamePrefix = "old@"; + break; + case TempVarKind.Bound: + tempNamePrefix = "forall@"; + break; + default: + assert false; // unexpected kind + } + TypedIdent ti = likeThisOne.TypedIdent; + TypedIdent newTi = new TypedIdent(ti.tok, "call" + UniqueId + 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 CallCmd : CallCommonality, IPotentialErrorNode + { + string! callee; + public Procedure Proc; + + // Element of the following lists can be null, which means that + // the call happens with * as these parameters + public List! Ins; + public List! Outs; + //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, ExprSeq! ins, IdentifierExprSeq! outs) + { + List! insList = new List (); + List! outsList = new List (); + foreach (Expr e in ins) + insList.Add(e); + foreach (IdentifierExpr e in outs) + outsList.Add(e); + + this(tok, callee, insList, outsList); + } + public CallCmd(IToken! tok, string! callee, List! ins, List! outs) + { + base(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); + this.callee = callee; + this.Ins = ins; + this.Outs = outs; + } + + public override void Emit(TokenTextWriter! stream, int level) + { + stream.Write(this, level, "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) + { + 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); + } + } + Set/**/ actualOuts = new Set/**/ (Outs.Count); + foreach (IdentifierExpr ide in Outs) + { + if (ide != null) { + ide.Resolve(rc); + if (ide.Decl != null) { + if (actualOuts[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.Length) { + rc.Error(this.tok, + "wrong number of arguments in call to {0}: {1}", + callee, Ins.Count); + return; + } + if (Outs.Count != Proc.OutParams.Length) { + rc.Error(this.tok, + "wrong number of result variables in call to {0}: {1}", + callee, Outs.Count); + return; + } + if (QKeyValue.FindBoolAttribute(this.Attributes, "async")) { + if (Proc.OutParams.Length > 1) { + rc.Error(this.tok, "a procedure called asynchronously can have at most one output parameter"); + 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 + TypeSeq! formalInTypes = new TypeSeq(); + TypeSeq! formalOutTypes = new TypeSeq(); + for (int i = 0; i < Ins.Count; ++i) + if (Ins[i] != null) + formalInTypes.Add(((!)Proc.InParams[i]).TypedIdent.Type); + for (int i = 0; i < Outs.Count; ++i) + if (Outs[i] != null) + formalOutTypes.Add(((!)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) + rc.AddTypeBinder(v); + Type.CheckBoundVariableOccurrences(Proc.TypeParameters, + formalInTypes, formalOutTypes, + this.tok, "types of given arguments", + rc); + } finally { + rc.TypeBinderState = previousTypeBinderState; + } + } + + public override void AddAssignedVariables(VariableSeq! vars) + { + foreach (IdentifierExpr e in Outs) + { + if (e!=null) { + vars.Add(e.Decl); + } + } + assume this.Proc != null; + foreach (IdentifierExpr! e in this.Proc.Modifies) + { + vars.Add(e.Decl); + } + } + + public override void Typecheck(TypecheckingContext! tc) + { + 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); + + TypeSeq! formalInTypes = new TypeSeq(); + TypeSeq! formalOutTypes = new TypeSeq(); + ExprSeq! actualIns = new ExprSeq(); + IdentifierExprSeq! actualOuts = new IdentifierExprSeq(); + for (int i = 0; i < Ins.Count; ++i) + { + if (Ins[i] != null) { + formalInTypes.Add(((!)Proc.InParams[i]).TypedIdent.Type); + actualIns.Add(Ins[i]); + } + } + for (int i = 0; i < Outs.Count; ++i) + { + if (Outs[i] != null) { + formalOutTypes.Add(((!)Proc.OutParams[i]).TypedIdent.Type); + actualOuts.Add(Outs[i]); + } + } + + if (QKeyValue.FindBoolAttribute(this.Attributes, "async") && Outs.Count > 0) { + Type returnType = ((!)Outs[0]).ShallowType; + if (!returnType.Equals(Type.Int)) + { + tc.Error(this.tok, "the return from an asynchronous call should be an integer"); + return; + } + } + + // match actuals with formals + List! actualTypeParams; + Type.CheckArgumentTypes(Proc.TypeParameters, + out actualTypeParams, + formalInTypes, actualIns, + formalOutTypes, actualOuts, + this.tok, + "call to " + callee, + tc); + TypeParameters = SimpleTypeParamInstantiation.From(Proc.TypeParameters, + actualTypeParams); + } + + private IDictionary! TypeParamSubstitution() { + assume TypeParameters != null; + IDictionary! res = new Dictionary (); + foreach (TypeVariable! v in TypeParameters.FormalTypeParams) + res.Add(v, TypeParameters[v]); + return res; + } + + protected override Cmd! ComputeDesugaring() { + CmdSeq newBlockBody = new CmdSeq(); + Hashtable /*Variable -> Expr*/ substMap = new Hashtable/*Variable -> Expr*/(); + Hashtable /*Variable -> Expr*/ substMapOld = new Hashtable/*Variable -> Expr*/(); + Hashtable /*Variable -> Expr*/ substMapBound = new Hashtable/*Variable -> Expr*/(); + VariableSeq! tempVars = new VariableSeq(); + + // 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 + VariableSeq! cins = new VariableSeq(); + VariableSeq wildcardVars = new VariableSeq(); + assume this.Proc != null; + for (int i = 0; i < this.Proc.InParams.Length; ++i) + { + Variable! param = (!)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 = (!)((!)Ins[i]).Type; + + Variable cin = CreateTemporaryVariable(tempVars, param, actualType, + TempVarKind.Formal); + cins.Add(cin); + IdentifierExpr ie = new IdentifierExpr(cin.tok, cin); + substMap.Add(param, ie); + if (isWildcard) { + cin = CreateTemporaryVariable(tempVars, param, + actualType, TempVarKind.Bound); + 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(((!)cins[i]).tok, (!) cins[i]); + if (this.Ins[i] != null) { + AssignCmd assign = Cmd.SimpleAssign(Token.NoToken, cin_exp, (!) this.Ins[i]); + newBlockBody.Add(assign); + } else { + IdentifierExprSeq! ies = new IdentifierExprSeq(); + 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.Length != 0); + Expr preConjunction = null; + for (int i = 0; i < this.Proc.Requires.Length; i++) + { + Requires! req = (!) this.Proc.Requires[i]; + if (!req.Free) { + if (hasWildcard) { + Expr pre = Substituter.Apply(s, req.Condition); + if (preConjunction == null) { + preConjunction = pre; + } else { + preConjunction = Expr.And(preConjunction, pre); + } + } else { + Requires! reqCopy = (Requires!) req.Clone(); + reqCopy.Condition = Substituter.Apply(s, req.Condition); + AssertCmd! a = new AssertRequiresCmd(this, reqCopy); + a.ErrorDataEnhanced = reqCopy.ErrorDataEnhanced; + newBlockBody.Add(a); + } + } + } + if (hasWildcard) { + if (preConjunction == null) { + preConjunction = Expr.True; + } + Expr! expr = new ExistsExpr(tok, wildcardVars, preConjunction); + AssertCmd! a = new AssertCmd(tok, expr); + 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.Length; i++) + { + Requires! req = (!) this.Proc.Requires[i]; + if (!req.Free) { + Requires! reqCopy = (Requires!) req.Clone(); + reqCopy.Condition = Substituter.Apply(s, req.Condition); + AssumeCmd! a = new AssumeCmd(tok, reqCopy.Condition); + newBlockBody.Add(a); + } + } + } + #endregion + + #region cframe := frame (to hold onto frame values in case they are referred to in the postcondition) + IdentifierExprSeq havocVarExprs = new IdentifierExprSeq(); + + foreach (IdentifierExpr! f in this.Proc.Modifies) + { + assume f.Decl != null; + assert f.Type != null; + Variable v = CreateTemporaryVariable(tempVars, f.Decl, f.Type, TempVarKind.Old); + 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.Has(f)) + havocVarExprs.Add(f); + } + #endregion + #region Create couts + VariableSeq! couts = new VariableSeq(); + for (int i = 0; i < this.Proc.OutParams.Length; ++i) + { + Variable! param = (!)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 = (!)((!)Outs[i]).Type; + + Variable cout = CreateTemporaryVariable(tempVars, param, actualType, + TempVarKind.Formal); + couts.Add(cout); + IdentifierExpr ie = new IdentifierExpr(cout.tok, cout); + substMap.Add(param, ie); + + if(!havocVarExprs.Has(ie)) + havocVarExprs.Add(ie); + } + // add the where clauses, now that we have the entire substitution map + foreach (Variable! param in this.Proc.OutParams) { + Expr w = param.TypedIdent.WhereExpr; + if (w != null) { + IdentifierExpr ie = (IdentifierExpr!)substMap[param]; + 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] + Substitution s2 = Substituter.SubstitutionFromHashtable(substMap); + Substitution s2old = Substituter.SubstitutionFromHashtable(substMapOld); + foreach (Ensures! e in this.Proc.Ensures) + { + Expr copy = Substituter.ApplyReplacingOldExprs(s2, s2old, e.Condition); + AssumeCmd assume = new AssumeCmd(this.tok, copy); + 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 = (!) this.Proc.OutParams[i]; + Expr! cout_exp = new IdentifierExpr(((!)couts[i]).tok, (!) couts[i]); + AssignCmd assign = Cmd.SimpleAssign(param_i.tok, (!) this.Outs[i], cout_exp); + newBlockBody.Add(assign); + } + } + #endregion + #endregion + + return new StateCmd(this.tok, tempVars, newBlockBody); + } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitCallCmd(this); + } + } + + public class CallForallCmd : CallCommonality + { + string! callee; + public Procedure Proc; + public List! Ins; + + // the types of the formal in-parameters after instantiating all + // type variables whose value could be inferred using the given + // actual non-wildcard arguments + public TypeSeq InstantiatedTypes; + + public CallForallCmd(IToken! tok, string! callee, List! ins) + { + base(tok, null); + this.callee = callee; + this.Ins = ins; + } + public CallForallCmd(IToken! tok, string! callee, List! ins, QKeyValue kv) + { + base(tok, kv); + this.callee = callee; + this.Ins = ins; + } + public override void Emit(TokenTextWriter! stream, int level) + { + stream.Write(this, level, "call "); + EmitAttributes(stream, Attributes); + stream.Write("forall "); + stream.Write(TokenTextWriter.SanitizeIdentifier(callee)); + stream.Write("("); + string 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) + { + 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); + } + } + } + public override void AddAssignedVariables(VariableSeq! vars) { } + public override void Typecheck(TypecheckingContext! tc) + { + TypecheckAttributes(Attributes, tc); + // typecheck in-parameters + foreach (Expr e in Ins) { + if (e != null) { + e.Typecheck(tc); + } + } + + if (this.Proc == null) + { + // called procedure didn't resolve, so bug out + return; + } + + // match actuals with formals + if (Ins.Count != Proc.InParams.Length) + { + tc.Error(this, "wrong number of in-parameters in call: {0}", callee); + } + else + { + // determine the lists of formal and actual arguments that need + // to be matched (stars are left out) + TypeSeq! formalTypes = new TypeSeq (); + ExprSeq! actualArgs = new ExprSeq (); + for (int i = 0; i < Ins.Count; i++) + if (Ins[i] != null) { + formalTypes.Add(((!)Proc.InParams[i]).TypedIdent.Type); + actualArgs.Add(Ins[i]); + } + IDictionary! subst = + Type.MatchArgumentTypes(Proc.TypeParameters, + formalTypes, actualArgs, null, null, + "call forall to " + callee, tc); + + InstantiatedTypes = new TypeSeq (); + foreach (Variable! var in Proc.InParams) { + InstantiatedTypes.Add(var.TypedIdent.Type.Substitute(subst)); + } + } + +// if (Proc.OutParams.Length != 0) +// { +// tc.Error(this, "call forall is allowed only on procedures with no out-parameters: {0}", callee); +// } + + if (Proc.Modifies.Length != 0) + { + tc.Error(this, "call forall is allowed only on procedures with no modifies clause: {0}", callee); + } + } + + protected override Cmd! ComputeDesugaring() { + CmdSeq newBlockBody = new CmdSeq(); + Hashtable /*Variable -> Expr*/ substMap = new Hashtable/*Variable -> Expr*/(); + VariableSeq! tempVars = new VariableSeq(); + + // proc P(ins) returns () + // requires Pre; + // modifies ; + // ensures Post; + // + // call forall P(ains); + + // ins : formal in-parameters of procedure + // ains : actual in-arguments passed to call + // cins : new variables created just for this call, one per ains + // wildcardVars : the bound variables to be wrapped up in a quantification + + #region Create cins; each one is an incarnation of the corresponding in parameter + VariableSeq! cins = new VariableSeq(); + VariableSeq wildcardVars = new VariableSeq(); + assume this.Proc != null; + for (int i = 0, n = this.Proc.InParams.Length; i < n; i++) { + Variable param = (!)this.Proc.InParams[i]; + Type! paramType = ((!)this.InstantiatedTypes)[i]; // might contain type variables + bool isWildcard = this.Ins[i] == null; + Variable cin = CreateTemporaryVariable(tempVars, param, paramType, + isWildcard ? TempVarKind.Bound : TempVarKind.Formal); + if (isWildcard) { + cins.Add(null); + wildcardVars.Add(cin); + } else { + cins.Add(cin); + } + IdentifierExpr ie = new IdentifierExpr(cin.tok, cin); + substMap.Add(param, ie); + } + #endregion + + #region call forall P(ains) becomes: (open outlining one level to see) + #region cins := ains + for (int i = 0, n = this.Ins.Count; i < n; i++) + { + if (this.Ins[i] != null) { + IdentifierExpr! cin_exp = new IdentifierExpr(((!)cins[i]).tok, (!) cins[i]); + AssignCmd assign = Cmd.SimpleAssign(Token.NoToken, cin_exp, (!) this.Ins[i]); + newBlockBody.Add(assign); + } + } + #endregion + + #region assert Pre[ins := cins] + Substitution s = Substituter.SubstitutionFromHashtable(substMap); + Expr preConjunction = null; + for (int i = 0; i < this.Proc.Requires.Length; i++) + { + Requires! req = (!) this.Proc.Requires[i]; + if (!req.Free) { + Expr pre = Substituter.Apply(s, req.Condition); + if (preConjunction == null) { + preConjunction = pre; + } else { + preConjunction = Expr.And(preConjunction, pre); + } + } + } + if (preConjunction == null) { + preConjunction = Expr.True; + } + #endregion + + #region Create couts + VariableSeq! couts = new VariableSeq(); + foreach ( Variable! param in this.Proc.OutParams ) + { + Variable cout = CreateTemporaryVariable(tempVars, param, + param.TypedIdent.Type, TempVarKind.Bound); + couts.Add(cout); + IdentifierExpr ie = new IdentifierExpr(cout.tok, cout); + substMap.Add(param, ie); + } + // add the where clauses, now that we have the entire substitution map + foreach (Variable! param in this.Proc.OutParams) { + Expr w = param.TypedIdent.WhereExpr; + if (w != null) { + IdentifierExpr ie = (IdentifierExpr!)substMap[param]; + assert ie.Decl != null; + ie.Decl.TypedIdent.WhereExpr = Substituter.Apply(Substituter.SubstitutionFromHashtable(substMap), w); + } + } + #endregion + + #region assume Post[ins := cins] + s = Substituter.SubstitutionFromHashtable(substMap); + Expr postConjunction = null; + foreach (Ensures! e in this.Proc.Ensures) + { + Expr post = Substituter.Apply(s, e.Condition); + if (postConjunction == null) { + postConjunction = post; + } else { + postConjunction = Expr.And(postConjunction, post); + } + } + if (postConjunction == null) { + postConjunction = Expr.True; + } + #endregion + + #region assume (forall wildcardVars :: Pre ==> Post); + Expr body = postConjunction; + if (couts.Length > 0) { + body = new ExistsExpr(tok, couts, body); + } + body = Expr.Imp(preConjunction, body); + if (wildcardVars.Length != 0) { + TypeVariableSeq! typeParams = Type.FreeVariablesIn((!)InstantiatedTypes); + body = new ForallExpr(tok, typeParams, wildcardVars, body); + } + newBlockBody.Add(new AssumeCmd(tok, body)); + #endregion + #endregion + + return new StateCmd(this.tok, tempVars, newBlockBody); + } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitCallForallCmd(this); + } + } + + public abstract class PredicateCmd : Cmd + { + public /*readonly--except in StandardVisitor*/ Expr! Expr; + public PredicateCmd(IToken! tok, Expr! expr) + : base(tok) + { + Expr = expr; + } + public override void Resolve(ResolutionContext! rc) + { + Expr.Resolve(rc); + } + public override void AddAssignedVariables(VariableSeq! vars) { } + } + + public abstract class MiningStrategy { + // abstract class to bind all MiningStrategys, i.e., all types of enhanced error data + // types together + } + + public class ListOfMiningStrategies : MiningStrategy { + public List! msList; + + public ListOfMiningStrategies (List! l) { + this.msList = l; + } + } + + public class EEDTemplate : MiningStrategy { + public string! reason; + public List! exprList; + + public EEDTemplate (string! reason, List! exprList) { + this.reason = reason; + this.exprList = exprList; + } + } + + public class AssertCmd : PredicateCmd, IPotentialErrorNode + { + public Expr OrigExpr; + public Hashtable /*Variable -> Expr*/ IncarnationMap; + + // 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"); + } + } + + public QKeyValue Attributes; + + private MiningStrategy errorDataEnhanced; + public MiningStrategy ErrorDataEnhanced { + get { return errorDataEnhanced; } + set { errorDataEnhanced = value; } + } + + public AssertCmd(IToken! tok, Expr! expr) + : base(tok, expr) + { + errorDataEnhanced = GenerateBoundVarMiningStrategy(expr); + } + + public AssertCmd(IToken! tok, Expr! expr, QKeyValue kv) + : base(tok, expr) + { + errorDataEnhanced = GenerateBoundVarMiningStrategy(expr); + Attributes = kv; + } + + public override void Emit(TokenTextWriter! stream, int level) + { + stream.Write(this, level, "assert "); + EmitAttributes(stream, Attributes); + this.Expr.Emit(stream); + stream.WriteLine(";"); + } + public override void Resolve(ResolutionContext! rc) + { + ResolveAttributes(Attributes, rc); + base.Resolve(rc); + } + + public override void Typecheck(TypecheckingContext! tc) + { + TypecheckAttributes(Attributes, tc); + Expr.Typecheck(tc); + 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) { + List l = new List(); + if (expr != null) { + l = GenerateBoundVarListForMining(expr, l); + } + return new ListOfMiningStrategies(l); + } + + public static List! GenerateBoundVarListForMining (Expr! expr, List! l) { + // 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) { + 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; + VariableSeq vs = qe.Dummies; + foreach (Variable! x in vs) { + 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) + { + 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) + { + } + } + + // 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) + { + } + } + + /// + /// An AssertCmd that is introduced in translation from the requires on a call. + /// + public class AssertRequiresCmd : AssertCmd + { + public CallCmd! Call; + public Requires! Requires; + + public AssertRequiresCmd(CallCmd! call, Requires! @requires) + : base(call.tok, @requires.Condition) + { + this.Call = call; + this.Requires = @requires; + // base(call.tok, @requires.Condition); + } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitAssertRequiresCmd(this); + } + } + + /// + /// An AssertCmd that is introduced in translation from an ensures + /// declaration. + /// + public class AssertEnsuresCmd : AssertCmd + { + public Ensures! Ensures; + public AssertEnsuresCmd(Ensures! ens) + : base(ens.tok, ens.Condition) + { + this.Ensures = ens; + // base(ens.tok, ens.Condition); + } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitAssertEnsuresCmd(this); + } + } + + public class AssumeCmd : PredicateCmd + { + public AssumeCmd(IToken! tok, Expr! expr) + : base(tok, expr) + { + //Debug.Assert(expr != null); + } + public override void Emit(TokenTextWriter! stream, int level) + { + stream.Write(this, level, "assume "); + this.Expr.Emit(stream); + stream.WriteLine(";"); + } + public override void Typecheck(TypecheckingContext! tc) + { + Expr.Typecheck(tc); + 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) + { + return visitor.VisitAssumeCmd(this); + } + } + + public class ReturnExprCmd : ReturnCmd + { + public Expr! Expr; + public ReturnExprCmd(IToken! tok, Expr! expr) + : base(tok) + { + Expr = expr; + } + public override void Emit(TokenTextWriter! stream, int level) + { + stream.Write(this, level, "return "); + this.Expr.Emit(stream); + stream.WriteLine(";"); + } + public override void Typecheck(TypecheckingContext! tc) + { + Expr.Typecheck(tc); + 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) + { + Expr.Resolve(rc); + } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitReturnExprCmd(this); + } + } + + public class HavocCmd : Cmd + { + public IdentifierExprSeq! Vars; + public HavocCmd(IToken! tok, IdentifierExprSeq! vars) + : base(tok) + { + Vars = vars; + } + public override void Emit(TokenTextWriter! stream, int level) + { + stream.Write(this, level, "havoc "); + Vars.Emit(stream, true); + stream.WriteLine(";"); + } + public override void Resolve(ResolutionContext! rc) + { + foreach (IdentifierExpr! ide in Vars) + { + ide.Resolve(rc); + } + } + public override void AddAssignedVariables(VariableSeq! vars) + { + foreach (IdentifierExpr! e in this.Vars) + { + vars.Add(e.Decl); + } + } + public override void Typecheck(TypecheckingContext! tc) + { + this.CheckAssignments(tc); + } + + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitHavocCmd(this); + } + } + + //--------------------------------------------------------------------- + // Transfer commands + + public abstract class TransferCmd : Absy + { + internal TransferCmd(IToken! tok) + : base(tok) + { + } + public abstract void Emit(TokenTextWriter! stream, int level); + public override void Typecheck(TypecheckingContext! tc) + { + // nothing to typecheck + } + } + + public class ReturnCmd : TransferCmd + { + public ReturnCmd(IToken! tok) + : base(tok) + { + } + public override void Emit(TokenTextWriter! stream, int level) + { + stream.WriteLine(this, level, "return;"); + } + public override void Resolve(ResolutionContext! rc) + { + // nothing to resolve + } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitReturnCmd(this); + } + } + + public class GotoCmd : TransferCmd + { + [Rep] + public StringSeq labelNames; + [Rep] + public BlockSeq labelTargets; + + invariant labelNames != null && labelTargets != null ==> labelNames.Length == labelTargets.Length; + + [NotDelayed] + public GotoCmd(IToken! tok, StringSeq! labelSeq) + : base (tok) + { + this.labelNames = labelSeq; + } + public GotoCmd(IToken! tok, StringSeq! labelSeq, BlockSeq! blockSeq) + : base (tok) + { + Debug.Assert(labelSeq.Length == blockSeq.Length); + for (int i=0; i blockSeq[i].Label != null; + StringSeq labelSeq = new StringSeq(); + for (int i=0; i 0 || labelTargets.Length == labelNames.Length); + } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitGotoCmd(this); + } + } + +} diff --git a/Source/Core/AbsyExpr.cs b/Source/Core/AbsyExpr.cs new file mode 100644 index 00000000..250820ed --- /dev/null +++ b/Source/Core/AbsyExpr.cs @@ -0,0 +1,2430 @@ +//----------------------------------------------------------------------------- +// +// 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 Microsoft.Boogie.AbstractInterpretation; + using AI = Microsoft.AbstractInterpretationFramework; + using Microsoft.Contracts; + using Microsoft.Basetypes; + + + //--------------------------------------------------------------------- + // Expressions + // + // For expressions, we override the Equals and GetHashCode method to + // implement structural equality. Note this is not logical equivalence + // and is not modulo alpha-renaming. + //--------------------------------------------------------------------- + + + public abstract class Expr : Absy + { + public Expr(IToken! tok) + : base(tok) + { + } + + public void Emit (TokenTextWriter! stream) + { + Emit(stream, 0, false); + } + + public abstract void Emit (TokenTextWriter! wr, int contextBindingStrength, bool fragileContext); + + [Pure] + public override string! ToString () + { + System.IO.StringWriter buffer = new System.IO.StringWriter(); + using (TokenTextWriter stream = new TokenTextWriter("", buffer, false)) + { + this.Emit(stream, 0, false); + } + return buffer.ToString(); + } + + /// + /// Add to "freeVars" the free variables in the expression. + /// + public abstract void ComputeFreeVariables(Set /*Variable*/! freeVars); + + /// + /// Filled in by the Typecheck method. A value of "null" means a succeeding + /// call to Typecheck has not taken place (that is, either Typecheck hasn't + /// been called or Typecheck encountered an error in the expression to be + /// typechecked). + /// + public Type Type; + + public override void Typecheck (TypecheckingContext! tc) + ensures Type != null; + { + // This body is added only because C# insists on it. It should really be left out, as if TypeCheck still were abstract. + // The reason for mentioning the method here at all is to give TypeCheck a postcondition for all expressions. + assert false; + } + + /// + /// Returns the type of the expression, supposing that all its subexpressions are well typed. + /// + public abstract Type! ShallowType { get; } + + // Handy syntactic sugar follows: + + public static NAryExpr! Unary (IToken! x, UnaryOperator.Opcode op, Expr! e1) + { + return new NAryExpr(x, new UnaryOperator(x, op), new ExprSeq(e1)); + } + + public static NAryExpr! Binary (IToken! x, BinaryOperator.Opcode op, Expr! e0, Expr! e1) + { + return new NAryExpr(x, new BinaryOperator(x, op), new ExprSeq(e0, e1)); + } + + public static NAryExpr! Binary (BinaryOperator.Opcode op, Expr! e0, Expr! e1) + { + return Binary(Token.NoToken, op, e0, e1); + } + + public static NAryExpr! Eq (Expr! e1, Expr! e2) { return Binary(BinaryOperator.Opcode.Eq, e1, e2); } + public static NAryExpr! Neq (Expr! e1, Expr! e2) { return Binary(BinaryOperator.Opcode.Neq, e1, e2); } + public static NAryExpr! Le (Expr! e1, Expr! e2) { return Binary(BinaryOperator.Opcode.Le, e1, e2); } + public static NAryExpr! Ge (Expr! e1, Expr! e2) { return Binary(BinaryOperator.Opcode.Ge, e1, e2); } + public static NAryExpr! Lt (Expr! e1, Expr! e2) { return Binary(BinaryOperator.Opcode.Lt, e1, e2); } + public static NAryExpr! Gt (Expr! e1, Expr! e2) { return Binary(BinaryOperator.Opcode.Gt, e1, e2); } + public static Expr! And (Expr! e1, Expr! e2) { + if (e1 == true_) { return e2; } + else if (e2 == true_) { return e1; } + else if (e1 == false_ || e2 == false_) { return false_; } + else { return Binary(BinaryOperator.Opcode.And, e1, e2); } + } + public static Expr! Or (Expr! e1, Expr! e2) { + if (e1 == false_) { return e2; } + else if (e2 == false_) { return e1; } + else if (e1 == true_ || e2 == true_) { return true_; } + else { return Binary(BinaryOperator.Opcode.Or, e1, e2); } + } + public static Expr! Not (Expr! e1) { + NAryExpr nary = e1 as NAryExpr; + + if (e1 == true_) { return false_; } + else if (e1 == false_) { return true_; } + else if (nary != null) + { + if (nary.Fun is UnaryOperator) + { + UnaryOperator op = (UnaryOperator)nary.Fun; + if (op.Op == UnaryOperator.Opcode.Not) { return (!) nary.Args[0]; } + } + else if (nary.Fun is BinaryOperator) + { + BinaryOperator op = (BinaryOperator)nary.Fun; + Expr arg0 = (!)nary.Args[0]; + Expr arg1 = (!)nary.Args[1]; + if (op.Op == BinaryOperator.Opcode.Eq) { return Neq(arg0, arg1); } + else if (op.Op == BinaryOperator.Opcode.Neq) { return Eq(arg0, arg1); } + else if (op.Op == BinaryOperator.Opcode.Lt) { return Ge(arg0, arg1); } + else if (op.Op == BinaryOperator.Opcode.Le) { return Gt(arg0, arg1); } + else if (op.Op == BinaryOperator.Opcode.Ge) { return Lt(arg0, arg1); } + else if (op.Op == BinaryOperator.Opcode.Gt) { return Le(arg0, arg1); } + } + } + + return Unary(Token.NoToken, UnaryOperator.Opcode.Not, e1); + } + public static NAryExpr! Imp (Expr! e1, Expr! e2) { return Binary(BinaryOperator.Opcode.Imp, e1, e2); } + public static NAryExpr! Iff (Expr! e1, Expr! e2) { return Binary(BinaryOperator.Opcode.Iff, e1, e2); } + public static NAryExpr! Add (Expr! e1, Expr! e2) { return Binary(BinaryOperator.Opcode.Add, e1, e2); } + public static NAryExpr! Sub (Expr! e1, Expr! e2) { return Binary(BinaryOperator.Opcode.Sub, e1, e2); } + public static NAryExpr! Mul (Expr! e1, Expr! e2) { return Binary(BinaryOperator.Opcode.Mul, e1, e2); } + public static NAryExpr! Div (Expr! e1, Expr! e2) { return Binary(BinaryOperator.Opcode.Div, e1, e2); } + public static NAryExpr! Mod (Expr! e1, Expr! e2) { return Binary(BinaryOperator.Opcode.Mod, e1, e2); } + public static NAryExpr! Subtype (Expr! e1, Expr! e2) { return Binary(BinaryOperator.Opcode.Subtype, e1, e2); } + + public static IdentifierExpr! Ident (string! name, Type! type) + { + return new IdentifierExpr(Token.NoToken, name, type); + } + + public static IdentifierExpr! Ident (Variable! decl) + { + IdentifierExpr result = new IdentifierExpr(Token.NoToken, decl); + return result; + } + + public static LiteralExpr! Literal (bool value) { return new LiteralExpr(Token.NoToken, value); } + public static LiteralExpr! Literal (int value) { return new LiteralExpr(Token.NoToken, BigNum.FromInt(value)); } + public static LiteralExpr! Literal (BigNum value) { return new LiteralExpr(Token.NoToken, value); } + + private static LiteralExpr! true_ = Literal(true); + public static LiteralExpr! True { get { return true_; } } + + private static LiteralExpr! false_ = Literal(false); + public static LiteralExpr! False { get { return false_; } } + + + public static NAryExpr! Select(Expr! map, params Expr[]! args) { + return SelectTok(Token.NoToken, map, args); + } + + public static NAryExpr! Select(Expr! map, List! args) { + return Select(map, args.ToArray()); + } + + // use a different name for this variant of the method + // (-> some bug prevents overloading in this case) + public static NAryExpr! SelectTok(IToken! x, Expr! map, params Expr[]! args) + { + ExprSeq! allArgs = new ExprSeq (); + allArgs.Add(map); + foreach (Expr! a in args) + allArgs.Add(a); + return new NAryExpr(x, new MapSelect(Token.NoToken, args.Length), allArgs); + } + + public static NAryExpr! Store(Expr! map, params Expr[]! args) { + return StoreTok(Token.NoToken, map, args); + } + + public static NAryExpr! Store(Expr! map, List! indexes, Expr! rhs) { + Expr[]! allArgs = new Expr [indexes.Count + 1]; + for (int i = 0; i < indexes.Count; ++i) + allArgs[i] = indexes[i]; + allArgs[indexes.Count] = rhs; + return Store(map, allArgs); + } + + // use a different name for this variant of the method + // (-> some bug prevents overloading in this case) + public static NAryExpr! StoreTok(IToken! x, Expr! map, params Expr[]! args) + requires args.Length > 0; // zero or more indices, plus the value + { + ExprSeq! allArgs = new ExprSeq (); + allArgs.Add(map); + foreach (Expr! a in args) + allArgs.Add(a); + return new NAryExpr(x, new MapStore(Token.NoToken, args.Length - 1), allArgs); + } + + public static NAryExpr! CoerceType(IToken! x, Expr! subexpr, Type! type) { + ExprSeq! args = new ExprSeq (); + args.Add(subexpr); + return new NAryExpr(x, new TypeCoercion(x, type), args); + } + + + /// + /// This property returns a representation for the expression suitable for use + /// by the AIFramework. Usually, the property just returns "this", but not + /// every Expr is an AI.IExpr (besides, AI.IExpr is to be thought of as an + /// abstract interface--any class that implements AI.IExpr is supposed to + /// implement some proper subinterface of AI.IExpr). + /// The converse operations of this property are found in AbsInt\ExprFactories.ssc. + /// + public abstract AI.IExpr! IExpr { + [Peer] get; + } + + } + + public class LiteralExpr : Expr, AI.IFunApp + { + public readonly object! Val; // false, true, a BigNum, or a BvConst + /// + /// Creates a literal expression for the boolean value "b". + /// + /// + /// + public LiteralExpr(IToken! tok, bool b) + : base(tok) + { + Val = b; + } + /// + /// Creates a literal expression for the integer value "v". + /// + /// + /// + public LiteralExpr(IToken! tok, BigNum v) + : base(tok) + { + Val = v; + } + + /// + /// Creates a literal expression for the bitvector value "v". + /// + public LiteralExpr(IToken! tok, BigNum v, int b) + : base(tok) + { + Val = new BvConst(v, b); + } + + [Pure][Reads(ReadsAttribute.Reads.Nothing)] + public override bool Equals(object obj) + { + if (obj == null) return false; + if (!(obj is LiteralExpr)) return false; + + LiteralExpr other = (LiteralExpr)obj; + return object.Equals(this.Val, other.Val); + } + [Pure] + public override int GetHashCode() + { + return this.Val.GetHashCode(); + } + public override void Emit(TokenTextWriter! stream, int contextBindingStrength, bool fragileContext) + { + stream.SetToken(this); + if (this.Val is bool) + { + stream.Write((bool)this.Val ? "true" : "false"); // correct capitalization + } + else + { + stream.Write((!) this.Val.ToString()); + } + } + public override void Resolve(ResolutionContext! rc) + { + // nothing to resolve + } + public override void ComputeFreeVariables(Set /*Variable*/! freeVars) { + // no free variables to add + } + + public override void Typecheck(TypecheckingContext! tc) + { + if (Val is BvConst && CommandLineOptions.Clo.Verify && CommandLineOptions.Clo.Bitvectors == CommandLineOptions.BvHandling.None) + tc.Error(this, "no bitvector handling specified, please use /bv:i or /bv:z flag"); + this.Type = ShallowType; + } + + public override Type! ShallowType { + get { + if (Val is bool) + { + return Type.Bool; + } + else if (Val is BigNum) + { + return Type.Int; + } + else if (Val is BvConst) + { + return Type.GetBvType(((BvConst)Val).Bits); + } + else + { + assert false; // like, where did this value come from?! + } + } + } + + public bool IsFalse { + get { + return Val is bool && ((bool)Val) == false; + } + } + public bool IsTrue { + get { + return Val is bool && ((bool)Val) == true; + } + } + public override AI.IExpr! IExpr { + get { + return this; + } + } + + // should be eliminated after converting everything to BigNums + private int asInt { + get { + return asBigNum.ToIntSafe; + } + } + + public bool isBigNum { + get { + return Val is BigNum; + } + } + + public BigNum asBigNum { + get { + assert isBigNum; + return (BigNum)(!)Val; + } + } + + public bool isBool { + get { + return Val is bool; + } + } + + public bool asBool { + get { + assert isBool; + return (bool)(!)Val; + } + } + + public AI.IFunctionSymbol! FunctionSymbol { + get { + if (Val is bool) + { + if ((bool)Val) + { + return AI.Prop.True; + } + else + { + return AI.Prop.False; + } + } + else if (Val is BigNum) + { + return AI.Int.Const((BigNum)Val); + } + else if (Val is BvConst) + { + return AI.Bv.Const(((BvConst)Val).Value, ((BvConst)Val).Bits); + } + else + { + assert false; // like, where did this value come from?! + } + } + } + public IList/**/! Arguments { + get { + return ArrayList.ReadOnly(new AI.IExpr[0]); + } + } + public Microsoft.AbstractInterpretationFramework.IFunApp! CloneWithArguments(IList/**/! args) { + assert args.Count == 0; + return this; + } + public AI.AIType! AIType { + get { + if (Val is bool) { + return AI.Prop.Type; + } else if (Val is BigNum) { + return AI.Int.Type; + } else if (Val is BvConst) { + return AI.Bv.Type; + } else { + assert false; // like, where did this value come from?! + } + } + } + [Pure] + public object DoVisit(AI.ExprVisitor! visitor) + { + return visitor.VisitFunApp(this); + } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitLiteralExpr(this); + } + } + + public class BvConst + { + public BigNum Value; + public int Bits; + + public BvConst(BigNum v, int b) + { + assert v.Signum >= 0; + Value = v; + Bits = b; + } + + [Pure] + public override string! ToString() + { + return Value + "bv" + Bits; + } + + [Pure] + public string! ToReadableString() + { + if (Value > BigNum.FromInt(10000)) { + string! val = (!)Value.ToString("x"); + int pos = val.Length % 4; + string! res = "0x" + val.Substring(0, pos); + while (pos < val.Length) { + res += "." + val.Substring(pos, 4); + pos += 4; + } + return res + ".bv" + Bits; + } else + return ToString(); + } + + [Pure][Reads(ReadsAttribute.Reads.Nothing)] + public override bool Equals(object obj) + { + BvConst other = obj as BvConst; + if (other == null) return false; + + return Bits == other.Bits && Value == other.Value; + } + + [Pure] + public override int GetHashCode() + { + unchecked { + return Value.GetHashCode() ^ Bits; + } + } + } + + public class AIVariableExpr : Expr + { + + public string Name; // identifier symbol + public AI.IVariable! Decl; // identifier declaration + + /// + /// Creates an unresolved identifier expression. + /// + /// + /// + public AIVariableExpr(IToken! tok, AI.IVariable! var) + : base(tok) + { + Name = var.ToString(); + Decl = var; + } + [Pure][Reads(ReadsAttribute.Reads.Nothing)] + public override bool Equals(object obj) + { + if (obj == null) return false; + if (!(obj is AIVariableExpr)) return false; + + AIVariableExpr other = (AIVariableExpr)obj; + return object.Equals(this.Name, other.Name) && object.Equals(this.Decl, other.Decl); + } + [Pure] + public override int GetHashCode() + { + int h = this.Name == null ? 0 : this.Name.GetHashCode(); + h ^= this.Decl == null ? 0 : this.Decl.GetHashCode(); + return h; + } + public override void Emit(TokenTextWriter! stream, int contextBindingStrength, bool fragileContext) + { + if (CommandLineOptions.Clo.PrintWithUniqueASTIds) + { + stream.Write("{0}^^", this.Decl == null ? "NoDecl" : "h"+this.Decl.GetHashCode()); + } + stream.Write(this, "{0}", this.Name); + } + public override void Resolve(ResolutionContext! rc) + { + } + public override void ComputeFreeVariables(Set /*Variable*/! freeVars) { + if (Decl is Variable) { + freeVars.Add((Variable)Decl); + } + } + public override void Typecheck(TypecheckingContext! tc) + { + throw new System.NotImplementedException(); + } + public override Type! ShallowType + { + get { throw new System.NotImplementedException(); } + } + public override AI.IExpr! IExpr { + get { + return Decl; + } + } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitAIVariableExpr(this); + } + } + + public class IdentifierExpr : Expr + { + public string! Name; // identifier symbol + public Variable Decl; // identifier declaration + + /// + /// Creates an unresolved identifier expression. This constructor is intended to be called + /// only from within the parser; for use inside the translation, use another constructor, which + /// specifies the type of the expression. + /// + /// + /// + internal IdentifierExpr(IToken! tok, string! name) + : base(tok) + { + Name = name; + // base(tok); + } + /// + /// Creates an unresolved identifier expression. + /// + /// + /// + /// + public IdentifierExpr(IToken! tok, string! name, Type! type) + : base(tok) + { + Name = name; + Type = type; + // base(tok); + } + + /// + /// Creates a resolved identifier expression. + /// + /// + /// + public IdentifierExpr(IToken! tok, Variable! d) + : base(tok) + { + Name = (!) d.Name; + Decl = d; + Type = d.TypedIdent.Type; + // base(tok); + } + [Pure][Reads(ReadsAttribute.Reads.Nothing)] + public override bool Equals(object obj) + { + if (obj == null) return false; + if (!(obj is IdentifierExpr)) return false; + + IdentifierExpr other = (IdentifierExpr)obj; + return object.Equals(this.Name, other.Name) && object.Equals(this.Decl, other.Decl); + } + [Pure] + public override int GetHashCode() + { + int h = this.Name == null ? 0 : this.Name.GetHashCode(); + h ^= this.Decl == null ? 0 : this.Decl.GetHashCode(); + return h; + } + public override void Emit(TokenTextWriter! stream, int contextBindingStrength, bool fragileContext) + { + if (CommandLineOptions.Clo.PrintWithUniqueASTIds) + { + stream.Write("{0}^^", this.Decl == null ? "NoDecl" : "h"+this.Decl.GetHashCode()); + } + stream.Write(this, "{0}", TokenTextWriter.SanitizeIdentifier(this.Name)); + } + public override void Resolve(ResolutionContext! rc) + { + if (Decl != null) + { + // already resolved, but re-resolve type just in case it came from an unresolved type + if (Type != null) { + Type = Type.ResolveType(rc); + } + return; + } + Decl = rc.LookUpVariable(Name); + if (Decl == null) { + rc.Error(this, "undeclared identifier: {0}", Name); + } else if (rc.StateMode == ResolutionContext.State.StateLess && Decl is GlobalVariable) { + rc.Error(this, "cannot refer to a global variable in this context: {0}", Name); + } + if (Type != null) { + Type = Type.ResolveType(rc); + } + } + public override void ComputeFreeVariables(Set /*Variable*/! freeVars) { + assume this.Decl != null; + freeVars.Add(Decl); + } + public override void Typecheck(TypecheckingContext! tc) + { + if (this.Decl != null) + { + // sanity check + if (Type != null && !Type.Equals(Decl.TypedIdent.Type)) { + tc.Error(this, "internal error, shallow-type assignment was done incorrectly, {0}:{1} != {2}", + Name, Type, Decl.TypedIdent.Type); + assert false; + } + Type = Decl.TypedIdent.Type; + } + } + + public override Type! ShallowType { + get { + assert Type != null; + return Type; + } + } + + public sealed class ConstantFunApp : AI.IFunApp + { + private IdentifierExpr! identifierExpr; + public IdentifierExpr! IdentifierExpr { get { return identifierExpr; } } + + private AI.IFunctionSymbol! symbol; + public AI.IFunctionSymbol! FunctionSymbol { get { return symbol; } } + + private static IList! emptyArgs = ArrayList.ReadOnly((IList!)new ArrayList()); + public IList! Arguments { get { return emptyArgs; } } + + public AI.IFunApp! CloneWithArguments(IList! newargs) { return this; } + + [Pure] + public object DoVisit(AI.ExprVisitor! visitor) { return visitor.VisitFunApp(this); } + + public ConstantFunApp(IdentifierExpr! ie, Constant! c) + { + this.identifierExpr = ie; + this.symbol = + new AI.NamedSymbol(c.TypedIdent.Name, BoogieFactory.Type2AIType(c.TypedIdent.Type)); + // base(); + } + + } + private AI.IExpr iexprCache = null; + public override AI.IExpr! IExpr { + get + { + if (iexprCache == null) + { + if (Decl is Constant) + iexprCache = new ConstantFunApp(this, (Constant)Decl); + else{ + assume this.Decl != null; + iexprCache = Decl; + } + } + return iexprCache; + } + } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitIdentifierExpr(this); + } + } + + public class OldExpr : Expr + , AI.IFunApp // HACK + { + public Expr! Expr; + public OldExpr(IToken! tok, Expr! expr) + : base(tok) + { + Expr = expr; + } + [Pure][Reads(ReadsAttribute.Reads.Nothing)] + public override bool Equals(object obj) + { + if (obj == null) return false; + if (!(obj is OldExpr)) return false; + + OldExpr other = (OldExpr)obj; + return object.Equals(this.Expr, other.Expr); + } + [Pure] + public override int GetHashCode() + { + return this.Expr == null ? 0 : this.Expr.GetHashCode(); + } + public override void Emit(TokenTextWriter! stream, int contextBindingStrength, bool fragileContext) + { + stream.Write(this, "old("); + this.Expr.Emit(stream); + stream.Write(")"); + } + public override void Resolve(ResolutionContext! rc) + { + if (rc.StateMode != ResolutionContext.State.Two) + { + rc.Error(this, "old expressions allowed only in two-state contexts"); + } + Expr.Resolve(rc); + } + public override void ComputeFreeVariables(Set /*Variable*/! freeVars) { + Expr.ComputeFreeVariables(freeVars); + } + public override void Typecheck(TypecheckingContext! tc) + { + Expr.Typecheck(tc); + Type = Expr.Type; + } + public override Type! ShallowType { + get { + return Expr.ShallowType; + } + } + public override AI.IExpr! IExpr { + get { +// Put back these lines when "HACK" removed +// // An Old expression has no AI.IExpr representation +// assert false; +// throw new System.Exception(); // make compiler shut up + return this; // HACK + } + } + [Pure] + public object DoVisit(AI.ExprVisitor! visitor) + { + return visitor.VisitFunApp(this); + } + public AI.IFunApp! CloneWithArguments(IList/**/! args) + { + assume args.Count == 1; + AI.IExpr! iexpr = (AI.IExpr!)args[0]; + return new OldExpr(Token.NoToken, BoogieFactory.IExpr2Expr(iexpr)); + } + private IList/*?*/ argCache = null; + public IList/* { + + T Visit(UnaryOperator! unaryOperator); + + T Visit(BinaryOperator! binaryOperator); + + T Visit(FunctionCall! functionCall); + + T Visit(MapSelect! mapSelect); + + T Visit(MapStore! mapStore); + + T Visit(TypeCoercion! typeCoercion); + + T Visit(IfThenElse! ifThenElse); + } + + public interface IAppliable + { + string! FunctionName { get; } + + /// + /// Emits to "stream" the operator applied to the given arguments. + /// The length of "args" can be anything that the parser allows for this appliable operator + /// (but can be nothing else). + /// + /// + /// + /// + /// + void Emit(ExprSeq! args, TokenTextWriter! stream, int contextBindingStrength, bool fragileContext); + + void Resolve(ResolutionContext! rc, Expr! subjectForErrorReporting); + + /// + /// Requires the object to have been properly resolved. + /// + int ArgumentCount { get; } + + /// + /// Typechecks the arguments "args" for the Appliable. If the arguments are + /// appropriate, returns the result type; otherwise returns null. + /// As result of the type checking, the values of type parameters of the + /// appliable can be returned (which are then stored in the NAryExpr and later + /// also used in the VCExprAST). + /// Requires the object to have been successfully resolved. + /// Requires args.Length == ArgumentCount. + /// Requires all elements of "args" to have a non-null Type field. + /// + /// + /// + Type Typecheck(ref ExprSeq! args, out TypeParamInstantiation! tpInstantiation, TypecheckingContext! tc); + ensures args.Length == old(args.Length); + // requires Microsoft.SpecSharp.Collections.Reductions.Forall{Expr! arg in args; arg.Type != null}; + + /// + /// Returns the result type of the IAppliable, supposing the argument are of the correct types. + /// + Type! ShallowType(ExprSeq! args); + + AI.IFunctionSymbol! AIFunctionSymbol { get; } + + T Dispatch(IAppliableVisitor! visitor); + } + + public interface IOverloadedAppliable + { + void ResolveOverloading(NAryExpr! expr); + } + + public class UnaryOperator : IAppliable + { + private IToken! tok; + public enum Opcode { Not }; + private Opcode op; + public Opcode Op { get { return op; } } + public UnaryOperator (IToken! tok, Opcode op) { this.tok = tok; this.op = op; } + + [Pure][Reads(ReadsAttribute.Reads.Nothing)] + public override bool Equals(object obj) + { + if (obj == null) return false; + if (!(obj is UnaryOperator)) return false; + + UnaryOperator other = (UnaryOperator)obj; + return object.Equals(this.op, other.op); + } + [Pure] + public override int GetHashCode() + { + return (int) this.op; + } + + public string! FunctionName + { + get + { + switch (this.op) + { + case Opcode.Not: return "!"; + } + System.Diagnostics.Debug.Fail("unknown unary operator: " + op.ToString()); + throw new Exception(); + } + } + + public AI.IFunctionSymbol! AIFunctionSymbol { + get { + switch (this.op) { + case Opcode.Not: return AI.Prop.Not; + } + System.Diagnostics.Debug.Fail("unknown unary operator: " + op.ToString()); + throw new Exception(); + } + } + + public void Emit(ExprSeq! args, TokenTextWriter! stream, int contextBindingStrength, bool fragileContext) + { + stream.SetToken(ref this.tok); + assert args.Length == 1; + // determine if parens are needed + int opBindingStrength = 0x60; + bool parensNeeded = opBindingStrength < contextBindingStrength || + (fragileContext && opBindingStrength == contextBindingStrength); + + if (parensNeeded) + { + stream.Write("("); + } + stream.Write(FunctionName); + ((!)args[0]).Emit(stream, opBindingStrength, false); + if (parensNeeded) + { + stream.Write(")"); + } + } + + public void Resolve(ResolutionContext! rc, Expr! subjectForErrorReporting) + { + if (rc.TriggerMode && this.op == Opcode.Not) { + rc.Error(subjectForErrorReporting, "boolean operators are not allowed in triggers"); + } + } + + public int ArgumentCount + { + get + { + return 1; + } + } + + public Type Typecheck(ref ExprSeq! args, out TypeParamInstantiation! tpInstantiation, TypecheckingContext! tc) + { + assume args.Length == 1; + tpInstantiation = SimpleTypeParamInstantiation.EMPTY; + Type arg0type = (!)((!)args[0]).Type; + switch (this.op) + { + case Opcode.Not: + if (arg0type.Unify(Type.Bool)) + { + return Type.Bool; + } + goto BAD_TYPE; + } + System.Diagnostics.Debug.Fail("unknown unary operator: " + op.ToString()); + assert false; + BAD_TYPE: + tc.Error(this.tok, "invalid argument type ({1}) to unary operator {0}", + this.FunctionName, arg0type); + return null; + } + public Type! ShallowType(ExprSeq! args) { + switch (this.op) { + case Opcode.Not: + return Type.Bool; + default: + assert false; // unexpected unary operator + } + } + + public object Evaluate (object argument) + { + if (argument == null) { return null; } + switch (this.op) + { + case Opcode.Not: + if (argument is bool) { return ! ((bool)argument); } + throw new System.InvalidOperationException("unary Not only applies to bool"); + } + return null; // unreachable + } + + public T Dispatch(IAppliableVisitor! visitor) { + return visitor.Visit(this); + } + } + + public class BinaryOperator : IAppliable, IOverloadedAppliable + { + private IToken! tok; + public enum Opcode { Add, Sub, Mul, Div, Mod, Eq, Neq, Gt, Ge, Lt, Le, And, Or, Imp, Iff, Subtype }; + private Opcode op; + public Opcode Op { get { return op; } } + public BinaryOperator (IToken! tok, Opcode op) { this.tok = tok; this.op = op; } + + [Pure][Reads(ReadsAttribute.Reads.Nothing)] + public override bool Equals(object obj) + { + if (obj == null) return false; + if (!(obj is BinaryOperator)) return false; + + BinaryOperator other = (BinaryOperator)obj; + return object.Equals(this.op, other.op); + } + + [Pure] + public override int GetHashCode() + { + return (int) this.op << 1; + } + + public string! FunctionName + { + get + { + switch (this.op) + { + case Opcode.Add: return "+"; + case Opcode.Sub: return "-"; + case Opcode.Mul: return "*"; + case Opcode.Div: return "/"; + case Opcode.Mod: return "%"; + case Opcode.Eq: return "=="; + case Opcode.Neq: return "!="; + case Opcode.Gt: return ">"; + case Opcode.Ge: return ">="; + case Opcode.Lt: return "<"; + case Opcode.Le: return "<="; + case Opcode.And: return "&&"; + case Opcode.Or: return "||"; + case Opcode.Imp: return "==>"; + case Opcode.Iff: return "<==>"; + case Opcode.Subtype: return "<:"; + } + System.Diagnostics.Debug.Fail("unknown binary operator: " + op.ToString()); + throw new Exception(); + } + } + + public AI.IFunctionSymbol! AIFunctionSymbol { + get { + switch (this.op) { + case Opcode.Add: return AI.Int.Add; + case Opcode.Sub: return AI.Int.Sub; + case Opcode.Mul: return AI.Int.Mul; + case Opcode.Div: return AI.Int.Div; + case Opcode.Mod: return AI.Int.Mod; + case Opcode.Eq: return AI.Value.Eq; + case Opcode.Neq: return AI.Value.Neq; + case Opcode.Gt: return AI.Int.Greater; + case Opcode.Ge: return AI.Int.AtLeast; + case Opcode.Lt: return AI.Int.Less; + case Opcode.Le: return AI.Int.AtMost; + case Opcode.And: return AI.Prop.And; + case Opcode.Or: return AI.Prop.Or; + case Opcode.Imp: return AI.Prop.Implies; + case Opcode.Iff: return AI.Value.Eq; + case Opcode.Subtype: return AI.Value.Subtype; + } + System.Diagnostics.Debug.Fail("unknown binary operator: " + op.ToString()); + throw new Exception(); + } + } + + public void Emit(ExprSeq! args, TokenTextWriter! stream, int contextBindingStrength, bool fragileContext) + { + stream.SetToken(ref this.tok); + assert args.Length == 2; + // determine if parens are needed + int opBindingStrength; + bool fragileLeftContext = false; // false means "allow same binding power on left without parens" + bool fragileRightContext = false; // false means "allow same binding power on right without parens" + switch (this.op) + { + case Opcode.Add: + opBindingStrength = 0x40; break; + case Opcode.Sub: + opBindingStrength = 0x40; fragileRightContext = true; break; + case Opcode.Mul: + opBindingStrength = 0x50; break; + case Opcode.Div: + opBindingStrength = 0x50; fragileRightContext = true; break; + case Opcode.Mod: + opBindingStrength = 0x50; fragileRightContext = true; break; + case Opcode.Eq: + case Opcode.Neq: + case Opcode.Gt: + case Opcode.Ge: + case Opcode.Lt: + case Opcode.Le: + case Opcode.Subtype: + opBindingStrength = 0x30; + fragileLeftContext = fragileRightContext = true; + break; + case Opcode.And: + opBindingStrength = 0x20; break; + case Opcode.Or: + opBindingStrength = 0x21; break; + case Opcode.Imp: + opBindingStrength = 0x10; fragileLeftContext = true; break; + case Opcode.Iff: + opBindingStrength = 0x00; break; + default: + System.Diagnostics.Debug.Fail("unknown binary operator: " + op.ToString()); + opBindingStrength = -1; // to please compiler, which refuses to consider whether or not all enumeration cases have been considered! + break; + } + int opBS = opBindingStrength & 0xF0; + int ctxtBS = contextBindingStrength & 0xF0; + bool parensNeeded = opBS < ctxtBS || + (opBS == ctxtBS && (opBindingStrength != contextBindingStrength || fragileContext)); + + if (parensNeeded) + { + stream.Write("("); + } + ((!)args[0]).Emit(stream, opBindingStrength, fragileLeftContext); + stream.Write(" {0} ", FunctionName); + ((!)args[1]).Emit(stream, opBindingStrength, fragileRightContext); + if (parensNeeded) + { + stream.Write(")"); + } + } + public void Resolve(ResolutionContext! rc, Expr! subjectForErrorReporting) + { + if (rc.TriggerMode) { + switch (this.op) + { + case Opcode.Add: + case Opcode.Sub: + case Opcode.Mul: + case Opcode.Div: + case Opcode.Mod: + case Opcode.Neq: // Neq is allowed, but not Eq + case Opcode.Subtype: + // These are fine + break; + + case Opcode.Eq: + rc.Error(subjectForErrorReporting, "equality is not allowed in triggers"); + break; + + case Opcode.Gt: + case Opcode.Ge: + case Opcode.Lt: + case Opcode.Le: + rc.Error(subjectForErrorReporting, "arithmetic comparisons are not allowed in triggers"); + break; + + case Opcode.And: + case Opcode.Or: + case Opcode.Imp: + case Opcode.Iff: + rc.Error(subjectForErrorReporting, "boolean operators are not allowed in triggers"); + break; + + default: + System.Diagnostics.Debug.Fail("unknown binary operator: " + this.op.ToString()); + break; + } + } + } + public int ArgumentCount + { + get + { + return 2; + } + } + public Type Typecheck(ref ExprSeq! args, out TypeParamInstantiation! tpInstantiation, TypecheckingContext! tc) + { + assert args.Length == 2; + // the default; the only binary operator with a type parameter is equality, but right + // we don't store this parameter because it does not appear necessary + tpInstantiation = SimpleTypeParamInstantiation.EMPTY; + Expr arg0 = (!)args[0]; + Expr arg1 = (!)args[1]; + Type arg0type = (!)arg0.Type; + Type arg1type = (!)arg1.Type; + switch (this.op) + { + case Opcode.Add: + case Opcode.Sub: + case Opcode.Mul: + case Opcode.Div: + case Opcode.Mod: + if (arg0type.Unify(Type.Int) && arg1type.Unify(Type.Int)) { + return Type.Int; + } + goto BAD_TYPE; + case Opcode.Eq: + case Opcode.Neq: + // Comparison is allowed if the argument types are unifiable + // (i.e., if there is any chance that the values of the arguments are + // in the same domain) + if (arg0type.Equals(arg1type)) { + // quick path + return Type.Bool; + } + TypeVariableSeq! unifiable = new TypeVariableSeq (); + unifiable.AddRange(arg0type.FreeVariables); + unifiable.AddRange(arg1type.FreeVariables); + + if (arg0type.Unify(arg1type, unifiable, new Dictionary ())) + return Type.Bool; + goto BAD_TYPE; + case Opcode.Gt: + case Opcode.Ge: + case Opcode.Lt: + case Opcode.Le: + if (arg0type.Unify(Type.Int) && arg1type.Unify(Type.Int)) { + return Type.Bool; + } + goto BAD_TYPE; + case Opcode.And: + case Opcode.Or: + case Opcode.Imp: + case Opcode.Iff: + if (arg0type.Unify(Type.Bool) && arg1type.Unify(Type.Bool)) { + return Type.Bool; + } + goto BAD_TYPE; + case Opcode.Subtype: + // Subtype is polymorphically typed and can compare things of + // arbitrary types (but both arguments must have the same type) + if (arg0type.Unify(arg1type)) + { + return Type.Bool; + } + goto BAD_TYPE; + } + System.Diagnostics.Debug.Fail("unknown binary operator: " + op.ToString()); + assert false; + BAD_TYPE: + tc.Error(this.tok, "invalid argument types ({1} and {2}) to binary operator {0}", this.FunctionName, arg0type, arg1type); + return null; + } + + public Type! ShallowType(ExprSeq! args) { + switch (this.op) + { + case Opcode.Add: + case Opcode.Sub: + case Opcode.Mul: + case Opcode.Div: + case Opcode.Mod: + return Type.Int; + + case Opcode.Eq: + case Opcode.Neq: + case Opcode.Gt: + case Opcode.Ge: + case Opcode.Lt: + case Opcode.Le: + case Opcode.And: + case Opcode.Or: + case Opcode.Imp: + case Opcode.Iff: + case Opcode.Subtype: + return Type.Bool; + + default: + assert false; // unexpected binary operator + } + } + + public void ResolveOverloading(NAryExpr! expr) + { + Expr arg0 = (!) expr.Args[0]; + Expr arg1 = (!) expr.Args[1]; + switch (op) + { + case Opcode.Eq: + if (arg0.Type != null && arg0.Type.IsBool && arg1.Type != null && arg1.Type.IsBool) + { + expr.Fun = new BinaryOperator(tok, Opcode.Iff); + } + break; + case Opcode.Neq: + if (arg0.Type != null && arg0.Type.IsBool && arg1.Type != null && arg1.Type.IsBool) + { + expr.Fun = new BinaryOperator(tok, Opcode.Iff); + arg1 = new NAryExpr(expr.tok, new UnaryOperator(tok, UnaryOperator.Opcode.Not), new ExprSeq(arg1)); + + // ugly ... there should be some more general approach, + // e.g., to typecheck the whole expression again + arg1.Type = Type.Bool; + ((NAryExpr)arg1).TypeParameters = SimpleTypeParamInstantiation.EMPTY; + + expr.Args[1] = arg1; + } + break; + } + } + + public object Evaluate (object e1, object e2) + { + if (e1 == null || e2 == null) { return null; } + + switch (this.op) + { + case Opcode.Add: + if (e1 is BigNum && e2 is BigNum) { return ((BigNum)e1)+((BigNum)e2); } + break; + case Opcode.Sub: + if (e1 is BigNum && e2 is BigNum) { return ((BigNum)e1)-((BigNum)e2); } + break; + case Opcode.Mul: + if (e1 is BigNum && e2 is BigNum) { return ((BigNum)e1)*((BigNum)e2); } + break; + case Opcode.Div: + if (e1 is BigNum && e2 is BigNum) { return /* TODO: right semantics? */ ((BigNum)e1)/((BigNum)e2); } + break; + case Opcode.Mod: + if (e1 is BigNum && e2 is BigNum) { return /* TODO: right semantics? */ ((BigNum)e1)%((BigNum)e2); } + break; + case Opcode.Lt: + if (e1 is BigNum && e2 is BigNum) { return ((BigNum)e1)<((BigNum)e2); } + break; + case Opcode.Le: + if (e1 is BigNum && e2 is BigNum) { return ((BigNum)e1)<=((BigNum)e2); } + break; + case Opcode.Gt: + if (e1 is BigNum && e2 is BigNum) { return ((BigNum)e1)>((BigNum)e2); } + break; + case Opcode.Ge: + if (e1 is BigNum && e2 is BigNum) { return ((BigNum)e1)>=((BigNum)e2); } + break; + + case Opcode.And: if (e1 is bool && e2 is bool) { return (bool)e1 && (bool)e2; } break; + case Opcode.Or: if (e1 is bool && e2 is bool) { return (bool)e1 || (bool)e2; } break; + case Opcode.Imp: if (e1 is bool && e2 is bool) { return ! (bool)e1 || (bool)e2; } break; + case Opcode.Iff: if (e1 is bool && e2 is bool) { return e1 == e2; } break; + + case Opcode.Eq: return Equals(e1,e2); + case Opcode.Neq: return ! Equals(e1,e2); + + case Opcode.Subtype: throw new System.NotImplementedException(); + } + throw new System.InvalidOperationException("bad types to binary operator " + this.op); + } + + public T Dispatch(IAppliableVisitor! visitor) { + return visitor.Visit(this); + } + + } + + public class FunctionCall : IAppliable, AI.IFunctionSymbol + { + private IdentifierExpr! name; + public Function Func; + public FunctionCall(IdentifierExpr! name) { this.name = name; } + public FunctionCall(Function! f) { this.Func = f; this.name = new IdentifierExpr(Token.NoToken, f.Name); } + public string! FunctionName { get { return this.name.Name; } } + + public AI.IFunctionSymbol! AIFunctionSymbol { + get { + if (name.Name == "$typeof") { + return AI.Value.Typeof; + } else if (name.Name == "$allocated") { + return AI.FieldName.Allocated; + } else { + return this; + } + } + } + + [Pure] + public override string! ToString() { + return name.Name; + } + + [Pure][Reads(ReadsAttribute.Reads.Nothing)] + public override bool Equals(object other) { + FunctionCall fc = other as FunctionCall; + return fc != null && this.Func == fc.Func; + } + [Pure] + public override int GetHashCode() + { + assume this.Func != null; + return Func.GetHashCode(); + } + + public AI.AIType! AIType { + get + { + assume this.Func != null; + return AI.Value.FunctionType(this.Func.InParams.Length); + } + } + + virtual public void Emit(ExprSeq! args, TokenTextWriter! stream, int contextBindingStrength, bool fragileContext) + { + this.name.Emit(stream, 0xF0, false); + stream.Write("("); + args.Emit(stream); + stream.Write(")"); + } + public void Resolve(ResolutionContext! rc, Expr! subjectForErrorReporting) + { + if (Func != null) + { + // already resolved + return; + } + Func = rc.LookUpProcedure(name.Name) as Function; + if (Func == null) + { + rc.Error(this.name, "use of undeclared function: {0}", name.Name); + } + } + public virtual int ArgumentCount + { + get + { + assume Func != null; // ArgumentCount requires object to be properly resolved. + return Func.InParams.Length; + } + } + public virtual Type Typecheck(ref ExprSeq! actuals, out TypeParamInstantiation! tpInstantiation, TypecheckingContext! tc) + { + assume this.Func != null; + assume actuals.Length == Func.InParams.Length; + assume Func.OutParams.Length == 1; + + List! resultingTypeArgs; + TypeSeq actualResultType = + Type.CheckArgumentTypes(Func.TypeParameters, + out resultingTypeArgs, + Func.InParams.ToTypeSeq, + actuals, + Func.OutParams.ToTypeSeq, + null, + // we need some token to report a possibly wrong number of + // arguments + actuals.Length > 0 ? ((!)actuals[0]).tok : Token.NoToken, + "application of " + name.Name, + tc); + + if (actualResultType == null) { + tpInstantiation = SimpleTypeParamInstantiation.EMPTY; + return null; + } else { + assert actualResultType.Length == 1; + tpInstantiation = + SimpleTypeParamInstantiation.From(Func.TypeParameters, resultingTypeArgs); + return actualResultType[0]; + } + } + public Type! ShallowType(ExprSeq! args) { + assume name.Type != null; + return name.Type; + } + + public virtual T Dispatch(IAppliableVisitor! visitor) { + return visitor.Visit(this); + } + } + + public class TypeCoercion : IAppliable { + private IToken! tok; + public Type! Type; + + public TypeCoercion(IToken! tok, Type! type) { + this.tok = tok; + this.Type = type; + } + + public string! FunctionName { get { + return ":"; + } } + + public void Emit(ExprSeq! args, TokenTextWriter! stream, + int contextBindingStrength, bool fragileContext) { + stream.SetToken(ref this.tok); + assert args.Length == 1; + // determine if parens are needed + int opBindingStrength = 0x90; + bool parensNeeded = opBindingStrength < contextBindingStrength || + (fragileContext && opBindingStrength == contextBindingStrength); + + if (parensNeeded) + stream.Write("("); + + ((!)args[0]).Emit(stream, opBindingStrength, false); + stream.Write("{0} ", FunctionName); + Type.Emit(stream, 0); + + if (parensNeeded) + stream.Write(")"); + } + + public void Resolve(ResolutionContext! rc, Expr! subjectForErrorReporting) { + this.Type = this.Type.ResolveType(rc); + } + + public int ArgumentCount { get { + return 1; + } } + + public Type Typecheck(ref ExprSeq! args, + out TypeParamInstantiation! tpInstantiation, + TypecheckingContext! tc) { + assume args.Length == 1; + tpInstantiation = SimpleTypeParamInstantiation.EMPTY; + + if (!this.Type.Unify((!)((!)args[0]).Type)) + tc.Error(this.tok, "{0} cannot be coerced to {1}", + ((!)args[0]).Type, this.Type); + return this.Type; + } + + public Type! ShallowType(ExprSeq! args) { + return this.Type; + } + + public AI.IFunctionSymbol! AIFunctionSymbol { get { + // not really clear what should be returned here ... + // should the operation be completely invisible for the abstract interpretation? + return AI.Heap.UnsupportedHeapOp; + } } + + public T Dispatch(IAppliableVisitor! visitor) { + return visitor.Visit(this); + } + + } + + public class NAryExpr : Expr, AI.IFunApp + { + [Additive] [Peer] + public IAppliable! Fun; + public ExprSeq! Args; + + // The instantiation of type parameters that is determined during type checking. + // Which type parameters are available depends on the IAppliable + public TypeParamInstantiation TypeParameters = null; + + [Captured] + public NAryExpr(IToken! tok, IAppliable! fun, ExprSeq! args) + : base(tok) + { + Fun = fun; + Args = args; + assert forall{Expr arg in args; arg != null}; + } + [Pure][Reads(ReadsAttribute.Reads.Nothing)] + public override bool Equals(object obj) + { + if (obj == null) return false; + if (!(obj is NAryExpr)) return false; + + NAryExpr other = (NAryExpr)obj; + return object.Equals(this.Fun, other.Fun) && object.Equals(this.Args, other.Args); + } + [Pure] + public override int GetHashCode() + { + int h = this.Fun.GetHashCode(); + h ^= this.Args.GetHashCode(); + return h; + } + public override void Emit(TokenTextWriter! stream, int contextBindingStrength, bool fragileContext) + { + stream.SetToken(this); + Fun.Emit(Args, stream, contextBindingStrength, fragileContext); + } + public override void Resolve(ResolutionContext! rc) + { + Fun.Resolve(rc, this); + foreach (Expr! e in Args) + { + e.Resolve(rc); + } + } + public override void ComputeFreeVariables(Set /*Variable*/! freeVars) { + foreach (Expr! e in Args) { + e.ComputeFreeVariables(freeVars); + } + // also add the free type variables + if (TypeParameters != null) { + foreach (TypeVariable! var in TypeParameters.FormalTypeParams) + foreach (TypeVariable! w in TypeParameters[var].FreeVariables) + freeVars.Add(w); + } + } + public override void Typecheck(TypecheckingContext! tc) + { + int prevErrorCount = tc.ErrorCount; + foreach (Expr! e in Args) + { + e.Typecheck(tc); + } + if (Fun.ArgumentCount != Args.Length) + { + tc.Error(this, "wrong number of arguments to function: {0} ({1} instead of {2})", + Fun.FunctionName, Args.Length, Fun.ArgumentCount); + } + else if (tc.ErrorCount == prevErrorCount && + // if the type parameters are set, this node has already been + // typechecked and does not need to be checked again + TypeParameters == null) + { + TypeParamInstantiation! tpInsts; + Type = Fun.Typecheck(ref Args, out tpInsts, tc); + if (Type != null && Type.IsBv && CommandLineOptions.Clo.Verify && CommandLineOptions.Clo.Bitvectors == CommandLineOptions.BvHandling.None) { + tc.Error(this, "no bitvector handling specified, please use /bv:i or /bv:z flag"); + } + TypeParameters = tpInsts; + } + IOverloadedAppliable oa = Fun as IOverloadedAppliable; + if (oa != null) + { + oa.ResolveOverloading(this); + } + if (Type == null) { + // set Type to some non-null value + Type = new TypeProxy(this.tok, "type_checking_error"); + } + } + public override Type! ShallowType { + get { + return Fun.ShallowType(Args); + } + } + + public override AI.IExpr! IExpr { + get { + return this; + } + } + public AI.IFunctionSymbol! FunctionSymbol { + get { + return Fun.AIFunctionSymbol; + } + } + public IList/**/! Arguments { + get { + AI.IExpr[] a = new AI.IExpr[Args.Length]; + for (int i = 0; i < Args.Length; i++) { + a[i] = ((!)Args[i]).IExpr; + } + return ArrayList.ReadOnly(a); + } + } + public AI.IFunApp! CloneWithArguments(IList/**/! args) { + return new NAryExpr(this.tok, this.Fun, BoogieFactory.IExprArray2ExprSeq(args)); + } + + [Pure] + public object DoVisit(AI.ExprVisitor! visitor) + { + return visitor.VisitFunApp(this); + } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitNAryExpr(this); + } + } + + public class MapSelect : IAppliable, AI.IFunctionSymbol { + + public readonly int Arity; + private readonly IToken! tok; + + public MapSelect(IToken! tok, int arity) { + this.tok = tok; + this.Arity = arity; + } + + public string! FunctionName { get { + return "MapSelect"; + } } + + [Pure][Reads(ReadsAttribute.Reads.Nothing)] + public override bool Equals(object obj) + { + if (!(obj is MapSelect)) return false; + + MapSelect other = (MapSelect)obj; + return this.Arity == other.Arity; + } + + [Pure] + public override int GetHashCode() + { + return Arity.GetHashCode() * 2823; + } + + public void Emit(ExprSeq! args, TokenTextWriter! stream, + int contextBindingStrength, bool fragileContext) { + assume args.Length == Arity + 1; + Emit(args, stream, contextBindingStrength, fragileContext, false); + } + + public static void Emit(ExprSeq! args, TokenTextWriter! stream, + int contextBindingStrength, bool fragileContext, + bool withRhs) { + const int opBindingStrength = 0x70; + bool parensNeeded = opBindingStrength < contextBindingStrength || + (fragileContext && opBindingStrength == contextBindingStrength); + + if (parensNeeded) + { + stream.Write("("); + } + ((!)args[0]).Emit(stream, opBindingStrength, false); + stream.Write("["); + + string sep = ""; + int lastIndex = withRhs ? args.Length - 1 : args.Length; + for (int i = 1; i < lastIndex; ++i) { + stream.Write(sep); + sep = ", "; + ((!)args[i]).Emit(stream); + } + + if (withRhs) { + stream.Write(" := "); + ((!)args.Last()).Emit(stream); + } + + stream.Write("]"); + if (parensNeeded) + { + stream.Write(")"); + } + } + + public void Resolve(ResolutionContext! rc, Expr! subjectForErrorReporting) { + // PR: nothing? + } + + public int ArgumentCount { get { + return Arity + 1; + } } + + // it is assumed that each of the arguments has already been typechecked + public static Type Typecheck(Type! mapType, + // we just pass an Absy, because in + // the AssignCmd maps can also be + // represented by non-expressions + Absy! map, + ExprSeq! indexes, + // the type parameters, in this context, are the parameters of the + // potentially polymorphic map type. Because it might happen that + // the whole map type is unknown and represented using a MapTypeProxy, + // the instantiations given in the following out-parameter are subject + // to change if further unifications are done. + out TypeParamInstantiation! tpInstantiation, + TypecheckingContext! tc, + IToken! typeCheckingSubject, + string! opName) { + mapType = mapType.Expanded; + if (mapType.IsMap && mapType.MapArity != indexes.Length) { + tc.Error(typeCheckingSubject, "wrong number of arguments in {0}: {1} instead of {2}", + opName, indexes.Length, mapType.MapArity); + tpInstantiation = SimpleTypeParamInstantiation.EMPTY; + return null; + } else if (!mapType.Unify(new MapTypeProxy(map.tok, "select", indexes.Length))) { + tc.Error(map.tok, "{0} applied to a non-map: {1}", opName, map); + tpInstantiation = SimpleTypeParamInstantiation.EMPTY; + return null; + } + mapType = TypeProxy.FollowProxy(mapType); + + if (mapType is MapType) { + MapType mt = (MapType)mapType; + return mt.CheckArgumentTypes(indexes, out tpInstantiation, + typeCheckingSubject, opName, tc); + } else { + MapTypeProxy mt = (MapTypeProxy)mapType; + return mt.CheckArgumentTypes(indexes, out tpInstantiation, + typeCheckingSubject, opName, tc); + } + } + + public Type Typecheck(ref ExprSeq! args, out TypeParamInstantiation! tpInstantiation, TypecheckingContext! tc) + { + assume args.Length == Arity + 1; + + ExprSeq actualArgs = new ExprSeq (); + for (int i = 1; i < args.Length; ++i) + actualArgs.Add(args[i]); + + return Typecheck((!)((!)args[0]).Type, (!)args[0], + actualArgs, out tpInstantiation, tc, this.tok, "map select"); + } + + /// + /// Returns the result type of the IAppliable, supposing the argument are of the correct types. + /// + public Type! ShallowType(ExprSeq! args) { + Expr a0 = (!)args[0]; + Type a0Type = a0.ShallowType; + if (a0Type == null || !a0Type.IsMap) { + // we are unable to determine the type of the select, so just return an arbitrary type + return Type.Int; + } + MapType mapType = a0Type.AsMap; + TypeSeq actualArgTypes = new TypeSeq (); + for (int i = 1; i < args.Length; ++i) { + actualArgTypes.Add(((!)args[i]).ShallowType); + } + return Type.InferValueType(mapType.TypeParameters, mapType.Arguments, mapType.Result, actualArgTypes); + } + + public AI.IFunctionSymbol! AIFunctionSymbol { get { + switch (Arity) { + case 1: return AI.Heap.Select1; + case 2: return AI.Heap.Select2; + default: + // Maps with Arity arguments are not fully supported yet + return AI.Heap.UnsupportedHeapOp; + } + } } + + public AI.AIType! AIType { + [Rep][ResultNotNewlyAllocated] + get { + return AI.Prop.Type; // THAT is a type? PR: no idea whether this makes sense, + // but it is the type of select1 + } } + + public T Dispatch(IAppliableVisitor! visitor) { + return visitor.Visit(this); + } + } + + public class MapStore : IAppliable, AI.IFunctionSymbol { + + public readonly int Arity; + public readonly IToken! tok; + + public MapStore(IToken! tok, int arity) { + this.tok = tok; + this.Arity = arity; + } + + public string! FunctionName { get { + return "MapStore"; + } } + + [Pure][Reads(ReadsAttribute.Reads.Nothing)] + public override bool Equals(object obj) + { + if (!(obj is MapStore)) return false; + + MapStore other = (MapStore)obj; + return this.Arity == other.Arity; + } + + [Pure] + public override int GetHashCode() + { + return Arity.GetHashCode() * 28231; + } + + public void Emit(ExprSeq! args, TokenTextWriter! stream, + int contextBindingStrength, bool fragileContext) { + assert args.Length == Arity + 2; + MapSelect.Emit(args, stream, contextBindingStrength, fragileContext, true); + } + + public void Resolve(ResolutionContext! rc, Expr! subjectForErrorReporting) { + // PR: nothing? + } + + public int ArgumentCount { get { + return Arity + 2; + } } + + // it is assumed that each of the arguments has already been typechecked + public static Type Typecheck(ExprSeq! args, out TypeParamInstantiation! tpInstantiation, + TypecheckingContext! tc, + IToken! typeCheckingSubject, + string! opName) { + // part of the type checking works exactly as for MapSelect + ExprSeq! selectArgs = new ExprSeq (); + for (int i = 1; i < args.Length - 1; ++i) + selectArgs.Add(args[i]); + Type resultType = + MapSelect.Typecheck((!)((!)args[0]).Type, (!)args[0], + selectArgs, out tpInstantiation, tc, typeCheckingSubject, opName); + + // check the the rhs has the right type + if (resultType == null) { + // error messages have already been created by MapSelect.Typecheck + return null; + } + Type rhsType = (!)((!)args.Last()).Type; + if (!resultType.Unify(rhsType)) { + tc.Error(((!)args.Last()).tok, + "right-hand side in {0} with wrong type: {1} (expected: {2})", + opName, rhsType, resultType); + return null; + } + + return ((!)args[0]).Type; + } + + public Type Typecheck(ref ExprSeq! args, + out TypeParamInstantiation! tpInstantiation, + TypecheckingContext! tc) + { + assert args.Length == Arity + 2; + return Typecheck(args, out tpInstantiation, tc, this.tok, "map store"); + } + + /// + /// Returns the result type of the IAppliable, supposing the argument are of the correct types. + /// + public Type! ShallowType(ExprSeq! args) { + return ((!)args[0]).ShallowType; + } + + public AI.IFunctionSymbol! AIFunctionSymbol { get { + switch (Arity) { + case 1: return AI.Heap.Update1; + case 2: return AI.Heap.Update2; + default: + // Maps with Arity arguments are not fully supported yet + return AI.Heap.UnsupportedHeapOp; + } + } } + + public AI.AIType! AIType { + [Rep][ResultNotNewlyAllocated] + get { + return AI.Heap.Type; + } } + + public T Dispatch(IAppliableVisitor! visitor) { + return visitor.Visit(this); + } + } + + + public class IfThenElse : IAppliable, AI.IFunctionSymbol { + + public IToken! tok; + + public IfThenElse(IToken! tok) { + this.tok = tok; + } + + public string! FunctionName { get { + return "if-then-else"; + } } + + [Pure][Reads(ReadsAttribute.Reads.Nothing)] + public override bool Equals(object obj) + { + if (!(obj is IfThenElse)) return false; + return true; + } + + [Pure] + public override int GetHashCode() + { + return 1; + } + + public void Emit(ExprSeq! args, TokenTextWriter! stream, int contextBindingStrength, bool fragileContext) + { + stream.SetToken(ref this.tok); + assert args.Length == 3; + stream.Write("(if "); + ((!)args[0]).Emit(stream, 0x00, false); + stream.Write(" then "); + ((!)args[1]).Emit(stream, 0x00, false); + stream.Write(" else "); + ((!)args[2]).Emit(stream, 0x00, false); + stream.Write(")"); + } + + public void Resolve(ResolutionContext! rc, Expr! subjectForErrorReporting) { + // PR: nothing? + } + + public int ArgumentCount { get { + return 3; + } } + + public Type Typecheck(ref ExprSeq! args, out TypeParamInstantiation! tpInstantiation, TypecheckingContext! tc) + { + assert args.Length == 3; + // the default; the only binary operator with a type parameter is equality, but right + // we don't store this parameter because it does not appear necessary + tpInstantiation = SimpleTypeParamInstantiation.EMPTY; + Expr arg0 = (!)args[0]; + Expr arg1 = (!)args[1]; + Expr arg2 = (!)args[2]; + + if (!((!)arg0.Type).Unify(Type.Bool)) { + tc.Error(this.tok, "the first argument to if-then-else should be bool, not {0}", arg0.Type); + } else if (!((!)arg1.Type).Unify((!)arg2.Type)) { + tc.Error(this.tok, "branches of if-then-else have incompatible types {0} and {1}", arg1.Type, arg2.Type); + } else { + return arg1.Type; + } + + return null; + } + + /// + /// Returns the result type of the IAppliable, supposing the argument are of the correct types. + /// + public Type! ShallowType(ExprSeq! args) { + return ((!)args[1]).ShallowType; + } + + public AI.IFunctionSymbol! AIFunctionSymbol { get { return this; } } + + public AI.AIType! AIType { + [Rep][ResultNotNewlyAllocated] + get { + return AI.Value.FunctionType(3); + } } + + public T Dispatch(IAppliableVisitor! visitor) { + return visitor.Visit(this); + } + } + + + + public class CodeExpr : Expr, AI.IUnknown + { + public VariableSeq! LocVars; + [Rep] + public List! Blocks; + public CodeExpr(VariableSeq! localVariables, List! blocks) + : base(Token.NoToken) + requires 0 < blocks.Count; + { + LocVars = localVariables; + Blocks = blocks; + } + + public override AI.IExpr! IExpr { get { return this; } } + [Pure] public object DoVisit(AI.ExprVisitor! visitor) { return this; } + + public override void ComputeFreeVariables(Set /*Variable*/! freeVars) { + // Treat a BlockEexpr as if it has no free variables at all + } + public override void Emit (TokenTextWriter! stream, int contextBindingStrength, bool fragileContext) + { + //level++; + int level = 0; + stream.WriteLine(level, "|{"); + + if (this.LocVars.Length > 0) + { + stream.Write(level + 1, "var "); + this.LocVars.Emit(stream); + stream.WriteLine(";"); + } + + foreach (Block! b in this.Blocks) + { + b.Emit(stream, level+1); + } + + stream.WriteLine(); + stream.WriteLine(level, "}|"); + + stream.WriteLine(); + stream.WriteLine(); + } + + public override void Resolve(ResolutionContext! rc) + { + + rc.PushVarContext(); + foreach (Variable! v in LocVars) + { + v.Register(rc); + v.Resolve(rc); + } + + rc.PushProcedureContext(); + foreach (Block! b in Blocks) + { + b.Register(rc); + } + + foreach (Block! b in Blocks) + { + b.Resolve(rc); + } + + rc.PopProcedureContext(); + rc.PopVarContext(); + } + + public override void Typecheck(TypecheckingContext! tc){ + foreach (Variable! v in LocVars){ + v.Typecheck(tc); + } + foreach (Block! b in Blocks){ + b.Typecheck(tc); + } + this.Type = Type.Bool; + } + public override Type! ShallowType { + get { + return Type.Bool; + } + } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitCodeExpr(this); + } + } + + + + public class BvExtractExpr : Expr, AI.IFunApp + { + public /*readonly--except in StandardVisitor*/ Expr! Bitvector; + public readonly int Start, End; + + public BvExtractExpr(IToken! tok, Expr! bv, int end, int start) + : base(tok) + { + Bitvector = bv; + Start = start; + End = end; + // base(tok); + } + + [Pure][Reads(ReadsAttribute.Reads.Nothing)] + public override bool Equals(object obj) + { + if (obj == null) return false; + if (!(obj is BvExtractExpr)) return false; + + BvExtractExpr other = (BvExtractExpr)obj; + return object.Equals(this.Bitvector, other.Bitvector) && + this.Start.Equals(other.Start) && this.End.Equals(other.End); + } + [Pure] + public override int GetHashCode() + { + int h = this.Bitvector.GetHashCode(); + h ^= Start * 17 ^ End * 13; + return h; + } + public override void Emit(TokenTextWriter! stream, int contextBindingStrength, bool fragileContext) + { + stream.SetToken(this); + int opBindingStrength = 0x70; + bool parensNeeded = opBindingStrength < contextBindingStrength || + (fragileContext && opBindingStrength == contextBindingStrength); + + if (parensNeeded) + { + stream.Write("("); + } + Bitvector.Emit(stream, opBindingStrength, false); + stream.Write("[" + End + ":" + Start + "]"); + if (parensNeeded) + { + stream.Write(")"); + } + } + public override void Resolve(ResolutionContext! rc) + { + Bitvector.Resolve(rc); + } + public override void ComputeFreeVariables(Set /*Variable*/! freeVars) { + Bitvector.ComputeFreeVariables(freeVars); + } + public override void Typecheck(TypecheckingContext! tc) + { + Bitvector.Typecheck(tc); + assert Bitvector.Type != null; // follows from postcondition of Expr.Typecheck + + if (Start < 0) { + tc.Error(this, "start index in extract must not be negative"); + } else if (End < 0) { + tc.Error(this, "end index in extract must not be negative"); + } else if (End < Start) { + tc.Error(this, "start index in extract must be no bigger than the end index"); + } else { + Type typeConstraint = new BvTypeProxy(this.tok, "extract", End - Start); + if (typeConstraint.Unify(Bitvector.Type)) { + Type = Type.GetBvType(End - Start); + } else { + tc.Error(this, "extract operand must be a bitvector of at least {0} bits (got {1})", End - Start, Bitvector.Type); + } + } + if (Type == null) { + Type = new TypeProxy(this.tok, "type_checking_error"); + } + } + + public override Type! ShallowType { + get { + return Type.GetBvType(End - Start); + } + } + + public override AI.IExpr! IExpr { + get { + return this; + } + } + public AI.IFunctionSymbol! FunctionSymbol { + get { return AI.Bv.Extract; + } + } + public IList/**/! Arguments { + get { + AI.IExpr[] a = new AI.IExpr[3]; + a[0] = Bitvector.IExpr; + a[1] = new LiteralExpr(Token.NoToken, BigNum.FromInt(End)); + a[2] = new LiteralExpr(Token.NoToken, BigNum.FromInt(Start)); + return ArrayList.ReadOnly(a); + } + } + public AI.IFunApp! CloneWithArguments(IList/**/! args) + { + AI.IFunApp! retFun; + + if(args.Count == 3) + { + retFun = new BvExtractExpr(this.tok, + BoogieFactory.IExpr2Expr((AI.IExpr!)args[0]), + ((LiteralExpr!)args[1]).asBigNum.ToIntSafe, + ((LiteralExpr!)args[2]).asBigNum.ToIntSafe); + } + else + { + assert false; // If we are something wrong is happended + } + return retFun; + } + + [Pure] + public object DoVisit(AI.ExprVisitor! visitor) + { + return visitor.VisitFunApp(this); + } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitBvExtractExpr(this); + } + } + + public class BvConcatExpr : Expr, AI.IFunApp + { + public /*readonly--except in StandardVisitor*/ Expr! E0, E1; + + public BvConcatExpr(IToken! tok, Expr! e0, Expr! e1) + : base(tok) + { + E0 = e0; + E1 = e1; + // base(tok); + } + + [Pure][Reads(ReadsAttribute.Reads.Nothing)] + public override bool Equals(object obj) + { + if (obj == null) return false; + if (!(obj is BvConcatExpr)) return false; + + BvConcatExpr other = (BvConcatExpr)obj; + return object.Equals(this.E0, other.E0) && object.Equals(this.E1, other.E1); + } + [Pure] + public override int GetHashCode() + { + int h = this.E0.GetHashCode() ^ this.E1.GetHashCode() * 17; + return h; + } + public override void Emit(TokenTextWriter! stream, int contextBindingStrength, bool fragileContext) + { + stream.SetToken(this); + int opBindingStrength = 0x32; + bool parensNeeded = opBindingStrength < contextBindingStrength || + (fragileContext && opBindingStrength == contextBindingStrength); + + if (parensNeeded) + { + stream.Write("("); + } + E0.Emit(stream, opBindingStrength, false); + stream.Write(" ++ "); + // while this operator is associative, our incomplete axioms in int translation don't + // make much use of it, so better stick to the actual tree shape + E1.Emit(stream, opBindingStrength, true); + if (parensNeeded) + { + stream.Write(")"); + } + } + public override void Resolve(ResolutionContext! rc) + { + E0.Resolve(rc); + E1.Resolve(rc); + } + public override void ComputeFreeVariables(Set /*Variable*/! freeVars) { + E0.ComputeFreeVariables(freeVars); + E1.ComputeFreeVariables(freeVars); + } + public override void Typecheck(TypecheckingContext! tc) + { + E0.Typecheck(tc); + assert E0.Type != null; // follows from postcondition of Expr.Typecheck + E1.Typecheck(tc); + assert E1.Type != null; // follows from postcondition of Expr.Typecheck + + if (E0.Type.Unify(new BvTypeProxy(this.tok, "concat0", 0)) && E1.Type.Unify(new BvTypeProxy(this.tok, "concat1", 0))) { + Type = new BvTypeProxy(this.tok, "concat", E0.Type, E1.Type); + } else { + tc.Error(this, "++ operands need to be bitvectors (got {0}, {1})", E0.Type, E1.Type); + } + if (Type == null) { + Type = new TypeProxy(this.tok, "type_checking_error"); + } + } + + public override Type! ShallowType { + get { + Type t0 = E0.ShallowType; + Type t1 = E1.ShallowType; + int len0 = t0.IsBv ? t0.BvBits : /*expression is not type correct, so just pick an arbitrary number of bits*/0; + int len1 = t1.IsBv ? t1.BvBits : /*expression is not type correct, so just pick an arbitrary number of bits*/0; + return Type.GetBvType(len0 + len1); + } + } + + public override AI.IExpr! IExpr { + get { + return this; + } + } + public AI.IFunctionSymbol! FunctionSymbol { + get { return AI.Bv.Concat; + } + } + public IList/**/! Arguments { + get { + AI.IExpr[] a = new AI.IExpr[2]; + a[0] = E0.IExpr; + a[1] = E1.IExpr; + return ArrayList.ReadOnly(a); + } + } + public AI.IFunApp! CloneWithArguments(IList/**/! args) + { + AI.IFunApp! retFun; + + if(args.Count == 2) + { + retFun = new BvConcatExpr(this.tok, + BoogieFactory.IExpr2Expr((AI.IExpr!)args[0]), + BoogieFactory.IExpr2Expr((AI.IExpr!)args[1])); + } + else + { + assert false; // If we are something wrong is happended + } + return retFun; + } + + [Pure] + public object DoVisit(AI.ExprVisitor! visitor) + { + return visitor.VisitFunApp(this); + } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitBvConcatExpr(this); + } + } +} + diff --git a/Source/Core/AbsyQuant.cs b/Source/Core/AbsyQuant.cs new file mode 100644 index 00000000..7515d37d --- /dev/null +++ b/Source/Core/AbsyQuant.cs @@ -0,0 +1,860 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) Microsoft Corporation. All Rights Reserved. +// +//----------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------- +// BoogiePL - AbsyQuant.cs +//--------------------------------------------------------------------------------------------- + +namespace Microsoft.Boogie +{ + using System; + using System.Collections; + using System.Diagnostics; + using System.Collections.Generic; + using Microsoft.Boogie.AbstractInterpretation; + using AI = Microsoft.AbstractInterpretationFramework; + using Microsoft.Contracts; + using Microsoft.Basetypes; + + + //--------------------------------------------------------------------- + // Quantifiers and general binders + //--------------------------------------------------------------------- + + public enum BinderKind { + Forall, + Exists, + Lambda + } + + public abstract class BinderExpr : Expr + { + public TypeVariableSeq! TypeParameters; + public VariableSeq! Dummies; + public QKeyValue Attributes; + public Expr! Body; + + public BinderExpr(IToken! tok, TypeVariableSeq! typeParameters, + VariableSeq! dummies, QKeyValue kv, Expr! body) + requires dummies.Length + typeParameters.Length > 0; + { + base(tok); + + TypeParameters = typeParameters; + Dummies = dummies; + Attributes = kv; + Body = body; + } + + abstract public BinderKind Kind { get; } + + [Pure][Reads(ReadsAttribute.Reads.Nothing)] + public override bool Equals(object obj) + { + if (obj == null) return false; + if (!(obj is BinderExpr) || + this.Kind != ((BinderExpr)obj).Kind) return false; + + BinderExpr other = (BinderExpr)obj; + // Note, we consider quantifiers equal modulo the Triggers. + return object.Equals(this.TypeParameters, other.TypeParameters) + && object.Equals(this.Dummies, other.Dummies) + && object.Equals(this.Body, other.Body); + } + + [Pure] + public override int GetHashCode() + { + int h = this.Dummies.GetHashCode(); + // Note, we consider quantifiers equal modulo the Triggers. + h ^= this.Body.GetHashCode(); + h = h*5 + this.TypeParameters.GetHashCode(); + h *= ((int)Kind + 1); + return h; + } + + protected virtual void EmitTypeHint(TokenTextWriter! stream) + { + } + + protected virtual void EmitTriggers(TokenTextWriter! stream) + { + } + + public override void Emit(TokenTextWriter! stream, int contextBindingStrength, bool fragileContext) + { + stream.Write(this, "({0}", Kind.ToString().ToLower()); + this.EmitTypeHint(stream); + Type.EmitOptionalTypeParams(stream, TypeParameters); + stream.Write(this, " "); + this.Dummies.Emit(stream); + stream.Write(" :: "); + for (QKeyValue kv = this.Attributes; kv != null; kv = kv.Next) { + kv.Emit(stream); + stream.Write(" "); + } + this.EmitTriggers(stream); + + this.Body.Emit(stream); + stream.Write(")"); + } + + protected virtual void ResolveTriggers(ResolutionContext! rc) + { + } + + public override void Resolve(ResolutionContext! rc) + { + if (rc.TriggerMode) { + rc.Error(this, "quantifiers are not allowed in triggers"); + } + + int previousTypeBinderState = rc.TypeBinderState; + try { + foreach (TypeVariable! v in TypeParameters) + rc.AddTypeBinder(v); + + rc.PushVarContext(); + foreach (Variable! v in Dummies) + { + v.Register(rc); + v.Resolve(rc); + } + for (QKeyValue kv = this.Attributes; kv != null; kv = kv.Next) { + kv.Resolve(rc); + } + this.ResolveTriggers(rc); + Body.Resolve(rc); + rc.PopVarContext(); + + // establish a canonical order of the type parameters + this.TypeParameters = Type.SortTypeParams(TypeParameters, Dummies.ToTypeSeq, null); + + } finally { + rc.TypeBinderState = previousTypeBinderState; + } + } + + public override void ComputeFreeVariables(Set /*Variable*/! freeVars) { + foreach (Variable! v in Dummies) { + assert !freeVars[v]; + } + Body.ComputeFreeVariables(freeVars); + foreach (Variable! v in Dummies) { + foreach (TypeVariable! w in v.TypedIdent.Type.FreeVariables) + freeVars.Add(w); + } + foreach (Variable! v in Dummies) { + freeVars.Remove(v); + } + foreach (TypeVariable! v in TypeParameters) { + freeVars.Remove(v); + } + } + + protected TypeVariableSeq! GetUnmentionedTypeParameters() + { + TypeVariableSeq! dummyParameters = Type.FreeVariablesIn(Dummies.ToTypeSeq); + TypeVariableSeq! unmentionedParameters = new TypeVariableSeq (); + foreach (TypeVariable! var in TypeParameters) + if (!dummyParameters.Has(var)) + unmentionedParameters.Add(var); + return unmentionedParameters; + } + + + public abstract AI.IFunctionSymbol! FunctionSymbol { get; } + + internal sealed class AIQuantifier : AI.IFunApp + { + internal readonly AIFunctionRep! arg; + internal AIQuantifier(BinderExpr! realQuantifier, int dummyIndex) + : this(new AIFunctionRep(realQuantifier, dummyIndex)) + { + } + [Pure][Reads(ReadsAttribute.Reads.Nothing)] + public override bool Equals(object obj) + { + if (obj == null) return false; + if (!(obj is AIQuantifier)) return false; + + AIQuantifier other = (AIQuantifier)obj; + return object.Equals(this.arg, other.arg); + } + [Pure] + public override int GetHashCode() + { + return this.arg.GetHashCode(); + } + + private AIQuantifier(AIFunctionRep! arg) + { + this.arg = arg; + // base(); + } + + [Pure] + public object DoVisit(AI.ExprVisitor! visitor) + { + return visitor.VisitFunApp(this); + } + + public AI.IFunctionSymbol! FunctionSymbol { get { return arg.RealQuantifier.FunctionSymbol; } } + + private IList/*?*/ argCache = null; + public IList/**/! Arguments + { + get + { + if (argCache == null) + { + IList a = new ArrayList(1); + a.Add(arg); + argCache = ArrayList.ReadOnly(a); + } + return argCache; + } + } + + public AI.IFunApp! CloneWithArguments(IList/**/! args) + { + assume args.Count == 1; + + AIFunctionRep rep = args[0] as AIFunctionRep; + if (rep != null) + return new AIQuantifier(rep); + else + throw new System.NotImplementedException(); + } + + [Pure] + public override string! ToString() + { + return string.Format("{0}({1})", FunctionSymbol, arg); + } + } + + internal sealed class AIFunctionRep : AI.IFunction + { + internal readonly BinderExpr! RealQuantifier; + private readonly int dummyIndex; + + internal AIFunctionRep(BinderExpr! realQuantifier, int dummyIndex) + { + this.RealQuantifier = realQuantifier; + this.dummyIndex = dummyIndex; + assert realQuantifier.TypeParameters.Length == 0; // PR: don't know how to handle this yet + // base(); + } + [Pure][Reads(ReadsAttribute.Reads.Nothing)] + public override bool Equals(object obj) + { + if (obj == null) return false; + if (!(obj is AIFunctionRep)) return false; + + AIFunctionRep other = (AIFunctionRep)obj; + return object.Equals(this.RealQuantifier, other.RealQuantifier) && this.dummyIndex == other.dummyIndex; + } + [Pure] + public override int GetHashCode() + { + return this.RealQuantifier.GetHashCode() ^ dummyIndex; + } + + [Pure] + public object DoVisit(AI.ExprVisitor! visitor) + { + return visitor.VisitFunction(this); + } + + public AI.IVariable! Param + { + get { return (!)RealQuantifier.Dummies[dummyIndex]; } + } + public AI.AIType! ParamType { get { throw new System.NotImplementedException(); } } + + // We lazily convert to 1 dummy per quantifier representation for AIFramework + private AI.IExpr/*?*/ bodyCache = null; + public AI.IExpr! Body + { + get + { + if (bodyCache == null) + { + int dummyi = dummyIndex; + int dummylen = RealQuantifier.Dummies.Length; + assume dummylen > dummyi; + + // return the actual body if there are no more dummies + if (dummyi + 1 == dummylen) + bodyCache = RealQuantifier.Body.IExpr; + else + { + AIQuantifier innerquant = new AIQuantifier(RealQuantifier, dummyi + 1); + bodyCache = innerquant; + } + } + return bodyCache; + } + } + public AI.IFunction! CloneWithBody(AI.IExpr! body) + { + BinderExpr realquant; + + AIQuantifier innerquant = body as AIQuantifier; + if (innerquant == null) + { + // new quantifier body, clone the real quantifier + realquant = (BinderExpr)RealQuantifier.Clone(); + realquant.Body = BoogieFactory.IExpr2Expr(body); + } + else + { + if (innerquant.arg.dummyIndex > 0) + { + realquant = innerquant.arg.RealQuantifier; + } + else + { + realquant = (QuantifierExpr)RealQuantifier.Clone(); + VariableSeq! newdummies = new VariableSeq(); + newdummies.Add(Param); + newdummies.AddRange(innerquant.arg.RealQuantifier.Dummies); + realquant.Dummies = newdummies; + realquant.Body = innerquant.arg.RealQuantifier.Body; + } + } + + return new AIFunctionRep(realquant, dummyIndex); + } + [Pure] + public override string! ToString() + { + return string.Format("\\{0} :: {1}", Param, Body); + } + } + + private AI.IExpr aiexprCache = null; + public override AI.IExpr! IExpr { + get { + if (TypeParameters.Length > 0) + return new Constant(Token.NoToken, new TypedIdent(Token.NoToken, "anon", Type.Bool)); + if (aiexprCache == null) + { + aiexprCache = new AIQuantifier(this, 0); + } + return aiexprCache; + } + } + } + + public class QKeyValue : Absy { + public readonly string! Key; + public readonly List! Params; // each element is either a string or an Expr + public QKeyValue Next; + + public QKeyValue(IToken! tok, string! key, [Captured] List! parameters, QKeyValue next) + { + base(tok); + Key = key; + Params = parameters; + Next = next; + } + + public void Emit(TokenTextWriter! stream) { + stream.Write("{:"); + stream.Write(Key); + string sep = " "; + foreach (object p in Params) { + stream.Write(sep); sep = ", "; + if (p is string) { + stream.Write("\""); + stream.Write((string)p); + stream.Write("\""); + } else { + ((Expr)p).Emit(stream); + } + } + stream.Write("}"); + } + + public override void Resolve(ResolutionContext! rc) { + foreach (object p in Params) { + if (p is Expr) { + ((Expr)p).Resolve(rc); + } + } + } + + public override void Typecheck(TypecheckingContext! tc) { + foreach (object p in Params) { + if (p is Expr) { + ((Expr)p).Typecheck(tc); + } + } + } + public void AddLast(QKeyValue! other){ + QKeyValue current = this; + while(current.Next!=null){ + current = current.Next; + } + current.Next = other; + } + // Look for {:name string} in list of attributes. + public static string? FindStringAttribute(QKeyValue? kv, string! name) + { + for (; kv != null; kv = kv.Next) { + if (kv.Key == name) { + if (kv.Params.Count == 1 && kv.Params[0] is string) { + return (string)kv.Params[0]; + } + } + } + return null; + } + // Look for {:name expr} in list of attributes. + public static Expr? FindExprAttribute(QKeyValue? kv, string! name) + { + for (; kv != null; kv = kv.Next) { + if (kv.Key == name) { + if (kv.Params.Count == 1 && kv.Params[0] is Expr) { + return (Expr)kv.Params[0]; + } + } + } + return null; + } + // Return 'true' if {:name true} or {:name} is an attribute in 'kv' + public static bool FindBoolAttribute(QKeyValue? kv, string! name) + { + for (; kv != null; kv = kv.Next) { + if (kv.Key == name) { + return kv.Params.Count == 0 || + (kv.Params.Count == 1 && kv.Params[0] is LiteralExpr && ((LiteralExpr)kv.Params[0]).IsTrue); + } + } + return false; + } + + public static int FindIntAttribute(QKeyValue? kv, string! name, int defl) + { + Expr? e = FindExprAttribute(kv, name); + LiteralExpr? l = e as LiteralExpr; + if (l != null && l.isBigNum) + return l.asBigNum.ToIntSafe; + return defl; + } + } + + public class Trigger : Absy { + public readonly bool Pos; + [Rep] + public ExprSeq! Tr; + invariant 1 <= Tr.Length; + invariant !Pos ==> Tr.Length == 1; + public Trigger Next; + + public Trigger(IToken! tok, bool pos, ExprSeq! tr) + requires 1 <= tr.Length; + requires !pos ==> tr.Length == 1; + { + this(tok, pos, tr, null); + } + + public Trigger(IToken! tok, bool pos, ExprSeq! tr, Trigger next) + : base(tok) + requires 1 <= tr.Length; + requires !pos ==> tr.Length == 1; + { + this.Pos = pos; + this.Tr = tr; + this.Next = next; + // base(tok); + } + + public void Emit(TokenTextWriter! stream) { + stream.SetToken(this); + assert this.Tr.Length >= 1; + string! sep = Pos ? "{ " : "{:nopats "; + foreach (Expr! e in this.Tr) { + stream.Write(sep); + sep = ", "; + e.Emit(stream); + } + stream.Write(" }"); + } + public override void Resolve(ResolutionContext! rc) { + rc.TriggerMode = true; + foreach (Expr! e in this.Tr) { + e.Resolve(rc); + + // just a variable by itself is not allowed + if (e is IdentifierExpr) { + rc.Error(e, "a matching pattern must be more than just a variable by itself: {0}", e); + } + + // the free-variable check is performed in the surrounding quantifier expression (because that's + // where the bound variables are known) + } + rc.TriggerMode = false; + } + + /// + /// Add to "freeVars" the free variables in the triggering expressions. + /// + public void ComputeFreeVariables(Set /*Variable*/! freeVars) { + foreach (Expr! e in this.Tr) { + e.ComputeFreeVariables(freeVars); + } + } + + public override void Typecheck(TypecheckingContext! tc) { + foreach (Expr! e in this.Tr) { + e.Typecheck(tc); + } + } + + public void AddLast(Trigger other){ + Trigger current = this; + while(current.Next!=null){ + current = current.Next; + } + current.Next = other; + } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitTrigger(this); + } + } + + + public class ForallExpr : QuantifierExpr + { + public ForallExpr(IToken! tok, TypeVariableSeq! typeParams, + VariableSeq! dummies, QKeyValue kv, Trigger triggers, Expr! body) + requires dummies.Length + typeParams.Length > 0; + { + base(tok, typeParams, dummies, kv, triggers, body); // here for aesthetic reasons + } + public ForallExpr(IToken! tok, VariableSeq! dummies, Trigger triggers, Expr! body) + requires dummies.Length > 0; + { + base(tok, new TypeVariableSeq(), dummies, null, triggers, body); // here for aesthetic reasons + } + public ForallExpr(IToken! tok, VariableSeq! dummies, Expr! body) + requires dummies.Length > 0; + { + base(tok, new TypeVariableSeq(), dummies, null, null, body); // here for aesthetic reasons + } + public ForallExpr(IToken! tok, TypeVariableSeq! typeParams, VariableSeq! dummies, Expr! body) + requires dummies.Length + typeParams.Length > 0; + { + base(tok, typeParams, dummies, null, null, body); // here for aesthetic reasons + } + public override AI.IFunctionSymbol! FunctionSymbol + { + get { + return AI.Prop.Forall; + } + } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitForallExpr(this); + } + + public override BinderKind Kind { get { return BinderKind.Forall; } } + } + + + public class ExistsExpr : QuantifierExpr + { + public ExistsExpr(IToken! tok, TypeVariableSeq! typeParams, VariableSeq! dummies, + QKeyValue kv, Trigger triggers, Expr! body) + requires dummies.Length + typeParams.Length > 0; + { + base(tok, typeParams, dummies, kv, triggers, body); // here for aesthetic reasons + } + public ExistsExpr(IToken! tok, VariableSeq! dummies, Trigger triggers, Expr! body) + requires dummies.Length > 0; + { + base(tok, new TypeVariableSeq (), dummies, null, triggers, body); // here for aesthetic reasons + } + public ExistsExpr(IToken! tok, VariableSeq! dummies, Expr! body) + requires dummies.Length > 0; + { + base(tok, new TypeVariableSeq(), dummies, null, null, body); // here for aesthetic reasons + } + public override AI.IFunctionSymbol! FunctionSymbol + { + get { + return AI.Prop.Exists; + } + } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitExistsExpr(this); + } + + public override BinderKind Kind { get { return BinderKind.Exists; } } + } + + + + public abstract class QuantifierExpr : BinderExpr + { + public Trigger Triggers; + + static int SkolemIds = 0; + public static int GetNextSkolemId() + { + SkolemIds++; + return SkolemIds; + } + + public readonly int SkolemId; + + public QuantifierExpr(IToken! tok, TypeVariableSeq! typeParameters, + VariableSeq! dummies, QKeyValue kv, Trigger triggers, Expr! body) + requires dummies.Length + typeParameters.Length > 0; + { + base(tok, typeParameters, dummies, kv, body); + + assert (this is ForallExpr) || (this is ExistsExpr); + + Triggers = triggers; + SkolemId = SkolemIds++; + } + + protected override void EmitTriggers(TokenTextWriter! stream) + { + for (Trigger tr = this.Triggers; tr != null; tr = tr.Next) { + tr.Emit(stream); + stream.Write(" "); + } + } + + // if the user says ( forall x :: forall y :: { f(x,y) } ... ) we transform it to + // (forall x, y :: { f(x,y) } ... ) otherwise the prover ignores the trigger + private void MergeAdjecentQuantifier() + { + QuantifierExpr qbody = Body as QuantifierExpr; + if (!(qbody != null && (qbody is ForallExpr) == (this is ForallExpr) && Triggers == null)) { + return; + } + qbody.MergeAdjecentQuantifier(); + if (qbody.Triggers == null) { + return; + } + Body = qbody.Body; + TypeParameters.AddRange(qbody.TypeParameters); + Dummies.AddRange(qbody.Dummies); + Triggers = qbody.Triggers; + if (qbody.Attributes != null) { + if (Attributes == null) { + Attributes = qbody.Attributes; + } else { + QKeyValue p = Attributes; + while (p.Next != null) { + p = p.Next; + } + p.Next = qbody.Attributes; + } + } + } + + #region never triggers + private class NeverTriggerCollector : StandardVisitor + { + QuantifierExpr! parent; + public NeverTriggerCollector(QuantifierExpr! p) + { + parent = p; + } + + public override Expr! VisitNAryExpr(NAryExpr! node) + { + FunctionCall fn = node.Fun as FunctionCall; + if (fn != null && ((!)fn.Func).NeverTrigger) { + parent.Triggers = new Trigger(fn.Func.tok, false, new ExprSeq(node), parent.Triggers); + } + return base.VisitNAryExpr(node); + } + } + + private bool neverTriggerApplied; + private void ApplyNeverTriggers() + { + if (neverTriggerApplied) { + return; + } + neverTriggerApplied = true; + + for (Trigger t = Triggers; t != null; t = t.Next) { + if (t.Pos) { return; } + } + + NeverTriggerCollector visitor = new NeverTriggerCollector(this); + visitor.VisitExpr(Body); + } + #endregion + + protected override void ResolveTriggers(ResolutionContext! rc) + { + for (Trigger tr = this.Triggers; tr != null; tr = tr.Next) { + int prevErrorCount = rc.ErrorCount; + tr.Resolve(rc); + if (prevErrorCount == rc.ErrorCount) { + // for positive triggers, make sure all bound variables are mentioned + if (tr.Pos) { + Set /*Variable*/ freeVars = new Set /*Variable*/ (); + tr.ComputeFreeVariables(freeVars); + foreach (Variable! v in Dummies) { + if (!freeVars[v]) { + rc.Error(tr, "trigger must mention all quantified variables, but does not mention: {0}", v); + } + } + } + } + } + } + + public override void Resolve(ResolutionContext! rc) + { + int oldErrorCount = rc.ErrorCount; + + this.MergeAdjecentQuantifier(); + + base.Resolve(rc); + + if (oldErrorCount == rc.ErrorCount) { + this.ApplyNeverTriggers(); + } + } + + + public override void Typecheck(TypecheckingContext! tc) + { + for (QKeyValue kv = this.Attributes; kv != null; kv = kv.Next) { + kv.Typecheck(tc); + } + for (Trigger tr = this.Triggers; tr != null; tr = tr.Next) { + tr.Typecheck(tc); + } + Body.Typecheck(tc); + assert Body.Type != null; // follows from postcondition of Expr.Typecheck + if (!Body.Type.Unify(Type.Bool)) + { + tc.Error(this, "quantifier body must be of type bool"); + } + this.Type = Type.Bool; + + // Check that type parameters occur in the types of the + // dummies, or otherwise in the triggers. This can only be + // done after typechecking + TypeVariableSeq! unmentionedParameters = GetUnmentionedTypeParameters(); + + if (unmentionedParameters.Length > 0) { + // all the type parameters that do not occur in dummy types + // have to occur in triggers + + for (Trigger tr = this.Triggers; tr != null; tr = tr.Next) { + // for positive triggers, make sure all bound variables are mentioned + if (tr.Pos) { + Set /*Variable*/ freeVars = new Set /*Variable*/ (); + tr.ComputeFreeVariables(freeVars); + foreach (TypeVariable! v in unmentionedParameters) { + if (!freeVars[v]) + tc.Error(tr, + "trigger does not mention {0}, which does not occur in variables types either", + v); + } + } + } + } + } + public override Type! ShallowType { + get { + return Type.Bool; + } + } + + } + + + public class LambdaExpr : BinderExpr + { + public LambdaExpr(IToken! tok, TypeVariableSeq! typeParameters, + VariableSeq! dummies, QKeyValue kv, Expr! body) + requires dummies.Length + typeParameters.Length > 0; + { + base(tok, typeParameters, dummies, kv, body); + } + + public override BinderKind Kind { get { return BinderKind.Lambda; } } + + public override void Resolve(ResolutionContext! rc) + { + base.Resolve(rc); + } + + public override void Typecheck(TypecheckingContext! tc) + { + for (QKeyValue kv = this.Attributes; kv != null; kv = kv.Next) { + kv.Typecheck(tc); + } + Body.Typecheck(tc); + assert Body.Type != null; // follows from postcondition of Expr.Typecheck + + TypeSeq! argTypes = new TypeSeq(); + foreach (Variable! v in Dummies) { + argTypes.Add(v.TypedIdent.Type); + } + this.Type = new MapType(this.tok, this.TypeParameters, argTypes, Body.Type); + + // Check that type parameters occur in the types of the + // dummies, or otherwise in the triggers. This can only be + // done after typechecking + TypeVariableSeq! unmentionedParameters = GetUnmentionedTypeParameters(); + + if (unmentionedParameters.Length > 0) { + tc.Error(this, "the type variable {0} does not occur in types of the lambda parameters", unmentionedParameters[0]); + } + } + + private Type? mapType; + public override Type! ShallowType { + get { + if (mapType == null) { + TypeSeq! argTypes = new TypeSeq(); + foreach (Variable! v in Dummies) { + argTypes.Add(v.TypedIdent.Type); + } + mapType = new MapType(this.tok, this.TypeParameters, argTypes, Body.ShallowType); + } + + return mapType; + } + } + + public override AI.IFunctionSymbol! FunctionSymbol + { + get { + return AI.Prop.Lambda; + } + } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitLambdaExpr(this); + } + + } + + +} + diff --git a/Source/Core/AbsyType.cs b/Source/Core/AbsyType.cs new file mode 100644 index 00000000..55b8913f --- /dev/null +++ b/Source/Core/AbsyType.cs @@ -0,0 +1,2857 @@ +//----------------------------------------------------------------------------- +// +// 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 Microsoft.Boogie.AbstractInterpretation; + using AI = Microsoft.AbstractInterpretationFramework; + using Microsoft.Contracts; + + //===================================================================== + //--------------------------------------------------------------------- + // Types + + public abstract class Type : Absy { + public Type(IToken! token) + : base(token) + { + } + + //----------- Cloning ---------------------------------- + // We implement our own clone-method, because bound type variables + // have to be created in the right way. It is /not/ ok to just clone + // everything recursively. Applying Clone to a type will return + // a type in which all bound variables have been replaced with new + // variables, whereas free variables have not changed + + public override Absy! Clone() { + return this.Clone(new Dictionary ()); + } + + public abstract Type! Clone(IDictionary! varMap); + + /// + /// Clones the type, but only syntactically. Anything resolved in the source + /// type is left unresolved (that is, with just the name) in the destination type. + /// + public abstract Type! CloneUnresolved(); + + //----------- Linearisation ---------------------------------- + + public void Emit(TokenTextWriter! stream) { + this.Emit(stream, 0); + } + + public abstract void Emit(TokenTextWriter! stream, int contextBindingStrength); + + [Pure] + public override string! ToString() { + System.IO.StringWriter buffer = new System.IO.StringWriter(); + using (TokenTextWriter stream = new TokenTextWriter("", buffer, false)) + { + this.Emit(stream); + } + return buffer.ToString(); + } + + //----------- Equality ---------------------------------- + + [Pure][Reads(ReadsAttribute.Reads.Nothing)] + public override bool Equals(object that) + { + if (ReferenceEquals(this, that)) + return true; + Type thatType = that as Type; + return thatType != null && this.Equals(thatType, + new TypeVariableSeq (), + new TypeVariableSeq ()); + } + + [Pure] + public abstract bool Equals(Type! that, + TypeVariableSeq! thisBoundVariables, + TypeVariableSeq! thatBoundVariables); + + // used to skip leading type annotations (subexpressions of the + // resulting type might still contain annotations) + internal virtual Type! Expanded { get { + return this; + } } + + //----------- Unification of types ----------- + + /// + /// Add a constraint that this==that, if possible, and return true. + /// If not possible, return false (which may have added some partial constraints). + /// No error is printed. + /// + public bool Unify(Type! that) { + return Unify(that, new TypeVariableSeq(), new Dictionary ()); + } + + public abstract bool Unify(Type! that, + TypeVariableSeq! unifiableVariables, + // an idempotent substitution that describes the + // unification result up to a certain point + IDictionary! unifier); + requires forall{TypeVariable key in unifier.Keys; unifiableVariables.Has(key)}; + requires IsIdempotent(unifier); + + [Pure] + public static bool IsIdempotent(IDictionary! unifier) { + return forall{Type! t in unifier.Values; + forall{TypeVariable! var in t.FreeVariables; + !unifier.ContainsKey(var)}}; + } + + +#if OLD_UNIFICATION + // Compute a most general unification of two types. null is returned if + // no such unifier exists. The unifier is not allowed to subtitute any + // type variables other than the ones in "unifiableVariables" + public IDictionary Unify(Type! that, + TypeVariableSeq! unifiableVariables) { + Dictionary! result = new Dictionary (); + try { + this.Unify(that, unifiableVariables, + new TypeVariableSeq (), new TypeVariableSeq (), result); + } catch (UnificationFailedException) { + return null; + } + return result; + } + + // Compute an idempotent most general unifier and add the result to the argument + // unifier. The result is true iff the unification succeeded + public bool Unify(Type! that, + TypeVariableSeq! unifiableVariables, + // given mappings that need to be taken into account + // the old unifier has to be idempotent as well + IDictionary! unifier) + requires forall{TypeVariable key in unifier.Keys; unifiableVariables.Has(key)}; + requires IsIdempotent(unifier); + { + try { + this.Unify(that, unifiableVariables, + new TypeVariableSeq (), new TypeVariableSeq (), unifier); + } catch (UnificationFailedException) { + return false; + } + return true; + } + + public abstract void Unify(Type! that, + TypeVariableSeq! unifiableVariables, + TypeVariableSeq! thisBoundVariables, + TypeVariableSeq! thatBoundVariables, + // an idempotent substitution that describes the + // unification result up to a certain point + IDictionary! result); +#endif + + //----------- Substitution of free variables with types not containing bound variables ----------------- + + public abstract Type! Substitute(IDictionary! subst); + + //----------- Hashcodes ---------------------------------- + + // Hack to be able to access the hashcode of superclasses further up + // (from the subclasses of this class) + [Pure] + protected int GetBaseHashCode() { + return base.GetHashCode(); + } + + [Pure] + public override int GetHashCode() + { + return this.GetHashCode(new TypeVariableSeq ()); + } + + [Pure] + public abstract int GetHashCode(TypeVariableSeq! boundVariables); + + //----------- Resolution ---------------------------------- + + public override void Resolve(ResolutionContext! rc) + { + System.Diagnostics.Debug.Fail("Type.Resolve should never be called." + + " Use Type.ResolveType instead"); + } + + public abstract Type! ResolveType(ResolutionContext! rc); + + public override void Typecheck(TypecheckingContext! tc) + { + System.Diagnostics.Debug.Fail("Type.Typecheck should never be called"); + } + + // determine the free variables in a type, in the order in which the variables occur + public abstract TypeVariableSeq! FreeVariables { get; } + + // determine the free type proxies in a type, in the order in which they occur + public abstract List! FreeProxies { get; } + + protected static void AppendWithoutDups(List! a, List! b) { + foreach (A x in b) + if (!a.Contains(x)) + a.Add(x); + } + + public bool IsClosed { get { + return FreeVariables.Length == 0; + } } + + //----------- Getters/Issers ---------------------------------- + + // the following methods should be used instead of simple casts or the + // C# "is" operator, because they handle type synonym annotations and + // type proxies correctly + + public virtual bool IsBasic { get { return false; } } + public virtual bool IsInt { get { return false; } } + public virtual bool IsBool { get { return false; } } + + public virtual bool IsVariable { get { return false; } } + public virtual TypeVariable! AsVariable { get { + assert false; // Type.AsVariable should never be called + } } + public virtual bool IsCtor { get { return false; } } + public virtual CtorType! AsCtor { get { + assert false; // Type.AsCtor should never be called + } } + public virtual bool IsMap { get { return false; } } + public virtual MapType! AsMap { get { + assert false; // Type.AsMap should never be called + } } + public virtual int MapArity { get { + assert false; // Type.MapArity should never be called + } } + public virtual bool IsUnresolved { get { return false; } } + public virtual UnresolvedTypeIdentifier! AsUnresolved { get { + assert false; // Type.AsUnresolved should never be called + } } + + public virtual bool IsBv { get { return false; } } + public virtual int BvBits { get { + assert false; // Type.BvBits should never be called + } } + + public static readonly Type! Int = new BasicType(SimpleType.Int); + public static readonly Type! Bool = new BasicType(SimpleType.Bool); + private static BvType[] bvtypeCache; + + static public BvType! GetBvType(int sz) + requires 0 <= sz; + { + if (bvtypeCache == null) { + bvtypeCache = new BvType[128]; + } + if (sz < bvtypeCache.Length) { + BvType t = bvtypeCache[sz]; + if (t == null) { + t = new BvType(sz); + bvtypeCache[sz] = t; + } + return t; + } else { + return new BvType(sz); + } + } + + //------------ Match formal argument types on actual argument types + //------------ and return the resulting substitution of type variables + +#if OLD_UNIFICATION + public static IDictionary! + MatchArgumentTypes(TypeVariableSeq! typeParams, + TypeSeq! formalArgs, + ExprSeq! actualArgs, + TypeSeq formalOuts, + IdentifierExprSeq actualOuts, + string! opName, + TypecheckingContext! tc) + requires formalArgs.Length == actualArgs.Length; + requires formalOuts == null <==> actualOuts == null; + requires formalOuts != null ==> formalOuts.Length == actualOuts.Length; + { + TypeVariableSeq! boundVarSeq0 = new TypeVariableSeq (); + TypeVariableSeq! boundVarSeq1 = new TypeVariableSeq (); + Dictionary! subst = new Dictionary(); + + for (int i = 0; i < formalArgs.Length; ++i) { + try { + Type! actualType = (!)((!)actualArgs[i]).Type; + // if the type variables to be matched occur in the actual + // argument types, something has gone very wrong + assert forall{TypeVariable! var in typeParams; + !actualType.FreeVariables.Has(var)}; + formalArgs[i].Unify(actualType, + typeParams, + boundVarSeq0, boundVarSeq1, + subst); + } catch (UnificationFailedException) { + tc.Error(actualArgs[i], + "invalid type for argument {0} in {1}: {2} (expected: {3})", + i, opName, actualArgs[i].Type, + // we insert the type parameters that have already been + // chosen to get a more precise error message + formalArgs[i].Substitute(subst)); + // the bound variable sequences should be empty ... + // so that we can continue with the unification + assert boundVarSeq0.Length == 0 && boundVarSeq1.Length == 0; + } + } + + if (formalOuts != null) { + for (int i = 0; i < formalOuts.Length; ++i) { + try { + Type! actualType = (!)((!)actualOuts[i]).Type; + // if the type variables to be matched occur in the actual + // argument types, something has gone very wrong + assert forall{TypeVariable! var in typeParams; + !actualType.FreeVariables.Has(var)}; + formalOuts[i].Unify(actualType, + typeParams, + boundVarSeq0, boundVarSeq1, + subst); + } catch (UnificationFailedException) { + tc.Error(actualOuts[i], + "invalid type for result {0} in {1}: {2} (expected: {3})", + i, opName, actualOuts[i].Type, + // we insert the type parameters that have already been + // chosen to get a more precise error message + formalOuts[i].Substitute(subst)); + // the bound variable sequences should be empty ... + // so that we can continue with the unification + assert boundVarSeq0.Length == 0 && boundVarSeq1.Length == 0; + } + } + } + + // we only allow type parameters to be substituted + assert forall{TypeVariable! var in subst.Keys; typeParams.Has(var)}; + + return subst; + } +#else + public static IDictionary! + MatchArgumentTypes(TypeVariableSeq! typeParams, + TypeSeq! formalArgs, + ExprSeq! actualArgs, + TypeSeq formalOuts, + IdentifierExprSeq actualOuts, + string! opName, + TypecheckingContext! tc) + requires formalArgs.Length == actualArgs.Length; + requires formalOuts == null <==> actualOuts == null; + requires formalOuts != null ==> formalOuts.Length == ((!)actualOuts).Length; + requires tc != null ==> opName != null; + // requires "actualArgs" and "actualOuts" to have been type checked + { + Dictionary subst = new Dictionary(); + foreach (TypeVariable! tv in typeParams) { + TypeProxy proxy = new TypeProxy(Token.NoToken, tv.Name); + subst.Add(tv, proxy); + } + + for (int i = 0; i < formalArgs.Length; i++) { + Type formal = formalArgs[i].Substitute(subst); + Type actual = (!)((!)actualArgs[i]).Type; + // if the type variables to be matched occur in the actual + // argument types, something has gone very wrong + assert forall{TypeVariable! var in typeParams; !actual.FreeVariables.Has(var)}; + + if (!formal.Unify(actual)) { + assume tc != null; // caller expected no errors + assert opName != null; // follows from precondition + tc.Error((!)actualArgs[i], + "invalid type for argument {0} in {1}: {2} (expected: {3})", + i, opName, actual, formalArgs[i]); + } + } + + if (formalOuts != null) { + for (int i = 0; i < formalOuts.Length; ++i) { + Type formal = formalOuts[i].Substitute(subst); + Type actual = (!)((!)actualOuts)[i].Type; + // if the type variables to be matched occur in the actual + // argument types, something has gone very wrong + assert forall{TypeVariable! var in typeParams; !actual.FreeVariables.Has(var)}; + + if (!formal.Unify(actual)) { + assume tc != null; // caller expected no errors + assert opName != null; // follows from precondition + tc.Error(actualOuts[i], + "invalid type for out-parameter {0} in {1}: {2} (expected: {3})", + i, opName, actual, formal); + } + } + } + + return subst; + } +#endif + + //------------ Match formal argument types of a function or map + //------------ on concrete types, substitute the result into the + //------------ result type. Null is returned for type errors + + public static TypeSeq CheckArgumentTypes(TypeVariableSeq! typeParams, + out List! actualTypeParams, + TypeSeq! formalIns, + ExprSeq! actualIns, + TypeSeq! formalOuts, + IdentifierExprSeq actualOuts, + IToken! typeCheckingSubject, + string! opName, + TypecheckingContext! tc) + // requires "actualIns" and "actualOuts" to have been type checked + { + actualTypeParams = new List (); + + if (formalIns.Length != actualIns.Length) { + tc.Error(typeCheckingSubject, "wrong number of arguments in {0}: {1}", + opName, actualIns.Length); + // if there are no type parameters, we can still return the result + // type and hope that the type checking proceeds + return typeParams.Length == 0 ? formalOuts : null; + } else if (actualOuts != null && formalOuts.Length != actualOuts.Length) { + tc.Error(typeCheckingSubject, "wrong number of result variables in {0}: {1}", + opName, actualOuts.Length); + // if there are no type parameters, we can still return the result + // type and hope that the type checking proceeds + actualTypeParams = new List (); + return typeParams.Length == 0 ? formalOuts : null; + } + + int previousErrorCount = tc.ErrorCount; + IDictionary subst = + MatchArgumentTypes(typeParams, formalIns, actualIns, + actualOuts != null ? formalOuts : null, actualOuts, opName, tc); + + foreach (TypeVariable! var in typeParams) + actualTypeParams.Add(subst[var]); + + TypeSeq! actualResults = new TypeSeq (); + foreach (Type! t in formalOuts) { + actualResults.Add(t.Substitute(subst)); + } + TypeVariableSeq resultFreeVars = FreeVariablesIn(actualResults); + if (previousErrorCount != tc.ErrorCount) { + // errors occured when matching the formal arguments + // in case we have been able to substitute all type parameters, + // we can still return the result type and hope that the + // type checking proceeds in a meaningful manner + if (forall{TypeVariable! var in typeParams; !resultFreeVars.Has(var)}) + return actualResults; + else + // otherwise there is no point in returning the result type, + // type checking would only get confused even further + return null; + } + + assert forall{TypeVariable! var in typeParams; !resultFreeVars.Has(var)}; + return actualResults; + } + + /////////////////////////////////////////////////////////////////////////// + + // about the same as Type.CheckArgumentTypes, but without + // detailed error reports + public static Type! InferValueType(TypeVariableSeq! typeParams, + TypeSeq! formalArgs, + Type! formalResult, + TypeSeq! actualArgs) { + IDictionary! subst = + InferTypeParameters(typeParams, formalArgs, actualArgs); + + Type! res = formalResult.Substitute(subst); + // all type parameters have to be substituted with concrete types + TypeVariableSeq! resFreeVars = res.FreeVariables; + assert forall{TypeVariable! var in typeParams; !resFreeVars.Has(var)}; + return res; + } + +#if OLD_UNIFICATION + public static IDictionary! + InferTypeParameters(TypeVariableSeq! typeParams, + TypeSeq! formalArgs, + TypeSeq! actualArgs) + requires formalArgs.Length == actualArgs.Length; { + + TypeVariableSeq! boundVarSeq0 = new TypeVariableSeq (); + TypeVariableSeq! boundVarSeq1 = new TypeVariableSeq (); + Dictionary! subst = new Dictionary(); + + for (int i = 0; i < formalArgs.Length; ++i) { + try { + assert forall{TypeVariable! var in typeParams; + !actualArgs[i].FreeVariables.Has(var)}; + formalArgs[i].Unify(actualArgs[i], typeParams, + boundVarSeq0, boundVarSeq1, subst); + } catch (UnificationFailedException) { + System.Diagnostics.Debug.Fail("Type unification failed: " + + formalArgs[i] + " vs " + actualArgs[i]); + } + } + + // we only allow type parameters to be substituted + assert forall{TypeVariable! var in subst.Keys; typeParams.Has(var)}; + return subst; + } +#else + /// + /// like Type.CheckArgumentTypes, but assumes no errors + /// (and only does arguments, not results; and takes actuals as TypeSeq, not ExprSeq) + /// + public static IDictionary! + InferTypeParameters(TypeVariableSeq! typeParams, + TypeSeq! formalArgs, + TypeSeq! actualArgs) + requires formalArgs.Length == actualArgs.Length; + { + TypeSeq proxies = new TypeSeq(); + Dictionary! subst = new Dictionary(); + foreach (TypeVariable! tv in typeParams) { + TypeProxy proxy = new TypeProxy(Token.NoToken, tv.Name); + proxies.Add(proxy); + subst.Add(tv, proxy); + } + + for (int i = 0; i < formalArgs.Length; i++) { + Type formal = formalArgs[i].Substitute(subst); + Type actual = actualArgs[i]; + // if the type variables to be matched occur in the actual + // argument types, something has gone very wrong + assert forall{TypeVariable! var in typeParams; !actual.FreeVariables.Has(var)}; + + if (!formal.Unify(actual)) { + assume false; // caller expected no errors + } + } + + return subst; + } +#endif + + //----------- Helper methods to deal with bound type variables --------------- + + public static void EmitOptionalTypeParams(TokenTextWriter! stream, TypeVariableSeq! typeParams) { + if (typeParams.Length > 0) { + stream.Write("<"); + typeParams.Emit(stream, ","); // default binding strength of 0 is ok + stream.Write(">"); + } + } + + // Sort the type parameters according to the order of occurrence in the argument types + public static TypeVariableSeq! SortTypeParams(TypeVariableSeq! typeParams, + TypeSeq! argumentTypes, Type resultType) + ensures result.Length == typeParams.Length; { + if (typeParams.Length == 0) { + return typeParams; + } + + TypeVariableSeq freeVarsInUse = FreeVariablesIn(argumentTypes); + if (resultType != null) { + freeVarsInUse.AppendWithoutDups(resultType.FreeVariables); + } + // "freeVarsInUse" is already sorted, but it may contain type variables not in "typeParams". + // So, project "freeVarsInUse" onto "typeParams": + TypeVariableSeq! sortedTypeParams = new TypeVariableSeq (); + foreach (TypeVariable! var in freeVarsInUse) { + if (typeParams.Has(var)) { + sortedTypeParams.Add(var); + } + } + + if (sortedTypeParams.Length < typeParams.Length) + // add the type parameters not mentioned in "argumentTypes" in + // the end of the list (this can happen for quantifiers) + sortedTypeParams.AppendWithoutDups(typeParams); + + return sortedTypeParams; + } + + // Check that each of the type parameters occurs in at least one argument type. + // Return true if some type parameters appear only among "moreArgumentTypes" and + // not in "argumentTypes". + [Pure] + public static bool CheckBoundVariableOccurrences(TypeVariableSeq! typeParams, + TypeSeq! argumentTypes, + TypeSeq moreArgumentTypes, + IToken! resolutionSubject, + string! subjectName, + ResolutionContext! rc) { + TypeVariableSeq freeVarsInArgs = FreeVariablesIn(argumentTypes); + TypeVariableSeq moFreeVarsInArgs = moreArgumentTypes == null ? null : FreeVariablesIn(moreArgumentTypes); + bool someTypeParamsAppearOnlyAmongMo = false; + foreach (TypeVariable! var in typeParams) { + if (rc.LookUpTypeBinder(var.Name) == var) // avoid to complain twice about variables that are bound multiple times + { + if (freeVarsInArgs.Has(var)) { + // cool + } else if (moFreeVarsInArgs != null && moFreeVarsInArgs.Has(var)) { + someTypeParamsAppearOnlyAmongMo = true; + } else { + rc.Error(resolutionSubject, + "type variable must occur in {0}: {1}", + subjectName, var); + } + } + } + return someTypeParamsAppearOnlyAmongMo; + } + + [Pure] + public static TypeVariableSeq! FreeVariablesIn(TypeSeq! arguments) { + TypeVariableSeq! res = new TypeVariableSeq (); + foreach (Type! t in arguments) + res.AppendWithoutDups(t.FreeVariables); + return res; + } + } + + //===================================================================== + + public class BasicType : Type + { + public readonly SimpleType T; + public BasicType(IToken! token, SimpleType t) + : base(token) + { + T = t; + // base(token); + } + public BasicType(SimpleType t) + : base(Token.NoToken) + { + T = t; + // base(Token.NoToken); + } + + //----------- Cloning ---------------------------------- + // We implement our own clone-method, because bound type variables + // have to be created in the right way. It is /not/ ok to just clone + // everything recursively. + + public override Type! Clone(IDictionary! varMap) { + // BasicTypes are immutable anyway, we do not clone + return this; + } + + public override Type! CloneUnresolved() { + return this; + } + + //----------- Linearisation ---------------------------------- + + public override void Emit(TokenTextWriter! stream, int contextBindingStrength) + { + // no parentheses are necessary for basic types + stream.SetToken(this); + stream.Write("{0}", this); + } + + [Pure] + public override string! ToString() + { + switch (T) + { + case SimpleType.Int: return "int"; + case SimpleType.Bool: return "bool"; + } + Debug.Assert(false, "bad type " + T); + assert false; // make compiler happy + } + + //----------- Equality ---------------------------------- + + [Pure][Reads(ReadsAttribute.Reads.Nothing)] + public override bool Equals(object that) { + // shortcut + Type thatType = that as Type; + if (thatType == null) + return false; + BasicType thatBasicType = TypeProxy.FollowProxy(thatType.Expanded) as BasicType; + return thatBasicType != null && this.T == thatBasicType.T; + } + + [Pure] + public override bool Equals(Type! that, + TypeVariableSeq! thisBoundVariables, + TypeVariableSeq! thatBoundVariables) { + return this.Equals(that); + } + + //----------- Unification of types ----------- + + public override bool Unify(Type! that, + TypeVariableSeq! unifiableVariables, + // an idempotent substitution that describes the + // unification result up to a certain point + IDictionary! unifier) { + that = that.Expanded; + if (that is TypeProxy || that is TypeVariable) { + return that.Unify(this, unifiableVariables, unifier); + } else { + return this.Equals(that); + } + } + +#if OLD_UNIFICATION + public override void Unify(Type! that, + TypeVariableSeq! unifiableVariables, + TypeVariableSeq! thisBoundVariables, + TypeVariableSeq! thatBoundVariables, + IDictionary! result) { + that = that.Expanded; + if (that is TypeVariable) { + that.Unify(this, unifiableVariables, thatBoundVariables, thisBoundVariables, result); + } else { + if (!this.Equals(that)) + throw UNIFICATION_FAILED; + } + } +#endif + + //----------- Substitution of free variables with types not containing bound variables ----------------- + + public override Type! Substitute(IDictionary! subst) { + return this; + } + + //----------- Hashcodes ---------------------------------- + + [Pure] + public override int GetHashCode(TypeVariableSeq! boundVariables) + { + return this.T.GetHashCode(); + } + + //----------- Resolution ---------------------------------- + + public override Type! ResolveType(ResolutionContext! rc) { + // nothing to resolve + return this; + } + + // determine the free variables in a type, in the order in which the variables occur + public override TypeVariableSeq! FreeVariables { + get { + return new TypeVariableSeq (); // basic type are closed + } + } + + public override List! FreeProxies { get { + return new List (); + } } + + //----------- Getters/Issers ---------------------------------- + + public override bool IsBasic { get { return true; } } + public override bool IsInt { get { return this.T == SimpleType.Int; } } + public override bool IsBool { get { return this.T == SimpleType.Bool; } } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitBasicType(this); + } + } + + //===================================================================== + + public class BvType : Type + { + public readonly int Bits; + + public BvType(IToken! token, int bits) + : base(token) + { + Bits = bits; + } + + public BvType(int bits) + : base(Token.NoToken) + { + Bits = bits; + } + + //----------- Cloning ---------------------------------- + // We implement our own clone-method, because bound type variables + // have to be created in the right way. It is /not/ ok to just clone + // everything recursively. + + public override Type! Clone(IDictionary! varMap) { + // BvTypes are immutable anyway, we do not clone + return this; + } + + public override Type! CloneUnresolved() { + return this; + } + + //----------- Linearisation ---------------------------------- + + public override void Emit(TokenTextWriter! stream, int contextBindingStrength) + { + // no parentheses are necessary for bitvector-types + stream.SetToken(this); + stream.Write("{0}", this); + } + + [Pure] + public override string! ToString() + { + return "bv" + Bits; + } + + //----------- Equality ---------------------------------- + + [Pure] + public override bool Equals(Type! that, + TypeVariableSeq! thisBoundVariables, + TypeVariableSeq! thatBoundVariables) { + BvType thatBvType = TypeProxy.FollowProxy(that.Expanded) as BvType; + return thatBvType != null && this.Bits == thatBvType.Bits; + } + + //----------- Unification of types ----------- + + public override bool Unify(Type! that, + TypeVariableSeq! unifiableVariables, + // an idempotent substitution that describes the + // unification result up to a certain point + IDictionary! unifier) { + that = that.Expanded; + if (that is TypeProxy || that is TypeVariable) { + return that.Unify(this, unifiableVariables, unifier); + } else { + return this.Equals(that); + } + } + +#if OLD_UNIFICATION + public override void Unify(Type! that, + TypeVariableSeq! unifiableVariables, + TypeVariableSeq! thisBoundVariables, + TypeVariableSeq! thatBoundVariables, + IDictionary! result) { + that = that.Expanded; + if (that is TypeVariable) { + that.Unify(this, unifiableVariables, thatBoundVariables, thisBoundVariables, result); + } else { + if (!this.Equals(that)) + throw UNIFICATION_FAILED; + } + } +#endif + + //----------- Substitution of free variables with types not containing bound variables ----------------- + + public override Type! Substitute(IDictionary! subst) { + return this; + } + + //----------- Hashcodes ---------------------------------- + + [Pure] + public override int GetHashCode(TypeVariableSeq! boundVariables) + { + return this.Bits.GetHashCode(); + } + + //----------- Resolution ---------------------------------- + + public override Type! ResolveType(ResolutionContext! rc) { + // nothing to resolve + return this; + } + + // determine the free variables in a type, in the order in which the variables occur + public override TypeVariableSeq! FreeVariables { + get { + return new TypeVariableSeq (); // bitvector-type are closed + } + } + + public override List! FreeProxies { get { + return new List (); + } } + + //----------- Getters/Issers ---------------------------------- + + public override bool IsBv { get { return true; } } + public override int BvBits { get { + return Bits; + } } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitBvType(this); + } + } + + //===================================================================== + + // An AST node containing an identifier and a sequence of type arguments, which + // will be turned either into a TypeVariable, into a CtorType or into a BvType + // during the resolution phase + public class UnresolvedTypeIdentifier : Type { + public readonly string! Name; + public readonly TypeSeq! Arguments; + + public UnresolvedTypeIdentifier(IToken! token, string! name) { + this(token, name, new TypeSeq ()); + } + + public UnresolvedTypeIdentifier(IToken! token, string! name, TypeSeq! arguments) + : base(token) + { + this.Name = name; + this.Arguments = arguments; + } + + //----------- Cloning ---------------------------------- + // We implement our own clone-method, because bound type variables + // have to be created in the right way. It is /not/ ok to just clone + // everything recursively + + public override Type! Clone(IDictionary! varMap) { + TypeSeq! newArgs = new TypeSeq (); + foreach(Type! t in Arguments) + newArgs.Add(t.Clone(varMap)); + return new UnresolvedTypeIdentifier(tok, Name, newArgs); + } + + public override Type! CloneUnresolved() { + TypeSeq! newArgs = new TypeSeq (); + foreach(Type! t in Arguments) + newArgs.Add(t.CloneUnresolved()); + return new UnresolvedTypeIdentifier(tok, Name, newArgs); + } + + //----------- Equality ---------------------------------- + + [Pure] + public override bool Equals(Type! that, + TypeVariableSeq! thisBoundVariables, + TypeVariableSeq! thatBoundVariables) { + System.Diagnostics.Debug.Fail("UnresolvedTypeIdentifier.Equals should never be called"); + return false; // to make the compiler happy + } + + //----------- Unification of types ----------- + + public override bool Unify(Type! that, + TypeVariableSeq! unifiableVariables, + IDictionary! result) { + assert false; // UnresolvedTypeIdentifier.Unify should never be called + } + +#if OLD_UNIFICATION + public override void Unify(Type! that, + TypeVariableSeq! unifiableVariables, + TypeVariableSeq! thisBoundVariables, + TypeVariableSeq! thatBoundVariables, + IDictionary! result) { + System.Diagnostics.Debug.Fail("UnresolvedTypeIdentifier.Unify should never be called"); + } +#endif + + //----------- Substitution of free variables with types not containing bound variables ----------------- + + public override Type! Substitute(IDictionary! subst) { + assert false; // UnresolvedTypeIdentifier.Substitute should never be called + } + + //----------- Hashcodes ---------------------------------- + + [Pure] + public override int GetHashCode(TypeVariableSeq! boundVariables) { + assert false; // UnresolvedTypeIdentifier.GetHashCode should never be called + } + + //----------- Resolution ---------------------------------- + + public override Type! ResolveType(ResolutionContext! rc) { + // first case: the type name denotes a bitvector-type + if (Name.StartsWith("bv") && Name.Length > 2) { + bool is_bv = true; + for (int i = 2; i < Name.Length; ++i) { + if (!char.IsDigit(Name[i])) { + is_bv = false; + break; + } + } + if (is_bv) { + if (Arguments.Length > 0) { + rc.Error(this, + "bitvector types must not be applied to arguments: {0}", + Name); + } + return new BvType(tok, int.Parse(Name.Substring(2))); + } + } + + // second case: the identifier is resolved to a type variable + TypeVariable var = rc.LookUpTypeBinder(Name); + if (var != null) { + if (Arguments.Length > 0) { + rc.Error(this, + "type variables must not be applied to arguments: {0}", + var); + } + return var; + } + + // third case: the identifier denotes a type constructor and we + // recursively resolve the arguments + TypeCtorDecl ctorDecl = rc.LookUpType(Name); + if (ctorDecl != null) { + if (Arguments.Length != ctorDecl.Arity) { + rc.Error(this, + "type constructor received wrong number of arguments: {0}", + ctorDecl); + return this; + } + return new CtorType (tok, ctorDecl, ResolveArguments(rc)); + } + + // fourth case: the identifier denotes a type synonym + TypeSynonymDecl synDecl = rc.LookUpTypeSynonym(Name); + if (synDecl != null) { + if (Arguments.Length != synDecl.TypeParameters.Length) { + rc.Error(this, + "type synonym received wrong number of arguments: {0}", + synDecl); + return this; + } + TypeSeq! resolvedArgs = ResolveArguments(rc); + + + return new TypeSynonymAnnotation(this.tok, synDecl, resolvedArgs); + + } + + // otherwise: this name is not declared anywhere + rc.Error(this, "undeclared type: {0}", Name); + return this; + } + + private TypeSeq! ResolveArguments(ResolutionContext! rc) { + TypeSeq! resolvedArgs = new TypeSeq (); + foreach (Type! t in Arguments) + resolvedArgs.Add(t.ResolveType(rc)); + return resolvedArgs; + } + + public override TypeVariableSeq! FreeVariables { + get { + return new TypeVariableSeq (); + } + } + + public override List! FreeProxies { get { + return new List (); + } } + + //----------- Linearisation ---------------------------------- + + public override void Emit(TokenTextWriter! stream, int contextBindingStrength) + { + stream.SetToken(this); + // PR: should unresolved types be syntactically distinguished from resolved types? + CtorType.EmitCtorType(this.Name, Arguments, stream, contextBindingStrength); + } + + //----------- Getters/Issers ---------------------------------- + + public override bool IsUnresolved { get { return true; } } + public override UnresolvedTypeIdentifier! AsUnresolved { get { return this; } } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitUnresolvedTypeIdentifier(this); + } + } + + //===================================================================== + + public class TypeVariable : Type { + public readonly string! Name; + + public TypeVariable(IToken! token, string! name) + : base(token) + { + this.Name = name; + } + + //----------- Cloning ---------------------------------- + // We implement our own clone-method, because bound type variables + // have to be created in the right way. It is /not/ ok to just clone + // everything recursively + + public override Type! Clone(IDictionary! varMap) { + // if this variable is mapped to some new variable, we take the new one + // otherwise, return this + TypeVariable res; + varMap.TryGetValue(this, out res); + if (res == null) + return this; + else + return res; + } + + public override Type! CloneUnresolved() { + return this; + } + + //----------- Equality ---------------------------------- + + [Pure] + public override bool Equals(Type! that, + TypeVariableSeq! thisBoundVariables, + TypeVariableSeq! thatBoundVariables) { + TypeVariable thatAsTypeVar = TypeProxy.FollowProxy(that.Expanded) as TypeVariable; + + if (thatAsTypeVar == null) + return false; + + int thisIndex = thisBoundVariables.LastIndexOf(this); + int thatIndex = thatBoundVariables.LastIndexOf(thatAsTypeVar); + return (thisIndex >= 0 && thisIndex == thatIndex) || + (thisIndex == -1 && thatIndex == -1 && + Object.ReferenceEquals(this, thatAsTypeVar)); + } + + //----------- Unification of types ----------- + + public override bool Unify(Type! that, + TypeVariableSeq! unifiableVariables, + // an idempotent substitution that describes the + // unification result up to a certain point + IDictionary! unifier) { + that = that.Expanded; + if (that is TypeProxy && !(that is ConstrainedProxy)) + return that.Unify(this, unifiableVariables, unifier); + + if (this.Equals(that)) + return true; + + if (unifiableVariables.Has(this)) { + Type previousSubst; + unifier.TryGetValue(this, out previousSubst); + if (previousSubst == null) { + return addSubstitution(unifier, that); + } else { + // we have to unify the old instantiation with the new one + return previousSubst.Unify(that, unifiableVariables, unifier); + } + } + + // this cannot be instantiated with anything + // but that possibly can ... + + TypeVariable tv = that as TypeVariable; + + return tv != null && + unifiableVariables.Has(tv) && + that.Unify(this, unifiableVariables, unifier); + } + + // TODO: the following might cause problems, because when applying substitutions + // to type proxies the substitutions are not propagated to the proxy + // constraints (right now at least) + private bool addSubstitution(IDictionary! oldSolution, + // the type that "this" is instantiated with + Type! newSubst) + requires !oldSolution.ContainsKey(this); { + + Dictionary! newMapping = new Dictionary (); + // apply the old (idempotent) substitution to the new instantiation + Type! substSubst = newSubst.Substitute(oldSolution); + // occurs check + if (substSubst.FreeVariables.Has(this)) + return false; + newMapping.Add(this, substSubst); + + // apply the new substitution to the old ones to ensure idempotence + List! keys = new List (); + keys.AddRange(oldSolution.Keys); + foreach (TypeVariable! var in keys) + oldSolution[var] = oldSolution[var].Substitute(newMapping); + oldSolution.Add(this, substSubst); + + assert IsIdempotent(oldSolution); + return true; + } + +#if OLD_UNIFICATION + public override void Unify(Type! that, + TypeVariableSeq! unifiableVariables, + TypeVariableSeq! thisBoundVariables, + TypeVariableSeq! thatBoundVariables, + IDictionary! result) { + that = that.Expanded; + int thisIndex = thisBoundVariables.LastIndexOf(this); + if (thisIndex == -1) { + // this is not a bound variable and can possibly be matched on that + // that must not contain any bound variables + TypeVariableSeq! thatFreeVars = that.FreeVariables; + if (exists{TypeVariable! var in thatBoundVariables; thatFreeVars.Has(var)}) + throw UNIFICATION_FAILED; + + // otherwise, in case that is a typevariable it cannot be bound and + // we can just check for equality + if (this.Equals(that)) + return; + + if (!unifiableVariables.Has(this)) { + // this cannot be instantiated with anything + // but that possibly can ... + if ((that is TypeVariable) && + unifiableVariables.Has(that as TypeVariable)) { + that.Unify(this, unifiableVariables, thatBoundVariables, thisBoundVariables, result); + return; + } else { + throw UNIFICATION_FAILED; + } + } + + Type previousSubst; + result.TryGetValue(this, out previousSubst); + if (previousSubst == null) { + addSubstitution(result, that); + } else { + // we have to unify the old instantiation with the new one + previousSubst.Unify(that, unifiableVariables, thisBoundVariables, thatBoundVariables, result); + } + } else { + // this is a bound variable, that also has to be one (with the same index) + if (!(that is TypeVariable) || + thatBoundVariables.LastIndexOf(that) != thisIndex) + throw UNIFICATION_FAILED; + } + } + +#endif + + //----------- Substitution of free variables with types not containing bound variables ----------------- + + public override Type! Substitute(IDictionary! subst) { + Type res; + if (subst.TryGetValue(this, out res)) { + assert res != null; + return res; + } else { + return this; + } + } + + //----------- Hashcodes ---------------------------------- + + [Pure] + public override int GetHashCode(TypeVariableSeq! boundVariables) { + int thisIndex = boundVariables.LastIndexOf(this); + if (thisIndex == -1) + return GetBaseHashCode(); + return thisIndex * 27473671; + } + + //----------- Linearisation ---------------------------------- + + public override void Emit(TokenTextWriter! stream, int contextBindingStrength) + { + // never put parentheses around variables + stream.SetToken(this); + stream.Write("{0}", TokenTextWriter.SanitizeIdentifier(this.Name)); + } + + //----------- Resolution ---------------------------------- + + public override Type! ResolveType(ResolutionContext! rc) { + // nothing to resolve + return this; + } + + public override TypeVariableSeq! FreeVariables { + get { return new TypeVariableSeq(this); } + } + + public override List! FreeProxies { get { + return new List (); + } } + + //----------- Getters/Issers ---------------------------------- + + public override bool IsVariable { get { return true; } } + public override TypeVariable! AsVariable { get { return this; } } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitTypeVariable(this); + } + } + + //===================================================================== + + public class TypeProxy : Type { + static int proxies = 0; + protected readonly string! Name; + + public TypeProxy(IToken! token, string! givenName) + { + this(token, givenName, "proxy"); + } + + protected TypeProxy(IToken! token, string! givenName, string! kind) + { + Name = givenName + "$" + kind + "#" + proxies; + proxies++; + base(token); + } + + private Type proxyFor; + public Type ProxyFor { + // apply path shortening, and then return the value of proxyFor + get { + TypeProxy anotherProxy = proxyFor as TypeProxy; + if (anotherProxy != null && anotherProxy.proxyFor != null) { + // apply path shortening by bypassing "anotherProxy" (and possibly others) + proxyFor = anotherProxy.ProxyFor; + assert proxyFor != null; + } + return proxyFor; + } + } + + [Pure][Reads(ReadsAttribute.Reads.Everything)] + public static Type! FollowProxy(Type! t) + ensures result is TypeProxy ==> ((TypeProxy)result).proxyFor == null; + { + if (t is TypeProxy) { + Type p = ((TypeProxy)t).ProxyFor; + if (p != null) { + return p; + } + } + return t; + } + + protected void DefineProxy(Type! ty) + requires ProxyFor == null; + { + // follow ty down to the leaf level, so that we can avoid creating a cycle + ty = FollowProxy(ty); + if (!object.ReferenceEquals(this, ty)) { + proxyFor = ty; + } + } + + //----------- Cloning ---------------------------------- + + public override Type! Clone(IDictionary! varMap) { + Type p = ProxyFor; + if (p != null) { + return p.Clone(varMap); + } else { + return new TypeProxy(this.tok, this.Name); // the clone will have a name that ends with $proxy$proxy + } + } + + public override Type! CloneUnresolved() { + return new TypeProxy(this.tok, this.Name); // the clone will have a name that ends with $proxy$proxy + } + + //----------- Equality ---------------------------------- + + [Pure] + public override bool Equals(Type! that, + TypeVariableSeq! thisBoundVariables, + TypeVariableSeq! thatBoundVariables) { + if (object.ReferenceEquals(this, that)) { + return true; + } + Type p = ProxyFor; + if (p != null) { + return p.Equals(that, thisBoundVariables, thatBoundVariables); + } else { + // This proxy could be made to be equal to anything, so what to return? + return false; + } + } + + //----------- Unification of types ----------- + + // determine whether the occurs check fails: this is a strict subtype of that + protected bool ReallyOccursIn(Type! that) { + that = FollowProxy(that.Expanded); + return that.FreeProxies.Contains(this) && + (that.IsCtor || that.IsMap && this != that && this.ProxyFor != that); + } + + public override bool Unify(Type! that, + TypeVariableSeq! unifiableVariables, + IDictionary! result) { + Type p = ProxyFor; + if (p != null) { + return p.Unify(that, unifiableVariables, result); + } else { + // unify this with that + if (this.ReallyOccursIn(that)) + return false; + DefineProxy(that.Expanded); + return true; + } + } + + //----------- Substitution of free variables with types not containing bound variables ----------------- + + public override Type! Substitute(IDictionary! subst) { + Type p = ProxyFor; + if (p != null) { + return p.Substitute(subst); + } else { + return this; + } + } + + //----------- Hashcodes ---------------------------------- + + [Pure] + public override int GetHashCode(TypeVariableSeq! boundVariables) { + Type p = ProxyFor; + if (p != null) { + return p.GetHashCode(boundVariables); + } else { + return GetBaseHashCode(); + } + } + + //----------- Linearisation ---------------------------------- + + public override void Emit(TokenTextWriter! stream, int contextBindingStrength) + { + Type p = ProxyFor; + if (p != null) { + p.Emit(stream, contextBindingStrength); + } else { + // no need for parentheses + stream.SetToken(this); + stream.Write("{0}", TokenTextWriter.SanitizeIdentifier(this.Name)); + } + } + + //----------- Resolution ---------------------------------- + + public override Type! ResolveType(ResolutionContext! rc) { + Type p = ProxyFor; + if (p != null) { + return p.ResolveType(rc); + } else { + return this; + } + } + + public override TypeVariableSeq! FreeVariables { + get { + Type p = ProxyFor; + if (p != null) { + return p.FreeVariables; + } else { + return new TypeVariableSeq(); + } + } + } + + public override List! FreeProxies { get { + Type p = ProxyFor; + if (p != null) { + return p.FreeProxies; + } else { + List! res = new List (); + res.Add(this); + return res; + } + } } + + //----------- Getters/Issers ---------------------------------- + + public override bool IsBasic { get { + Type p = ProxyFor; + return p != null && p.IsBasic; + } } + public override bool IsInt { get { + Type p = ProxyFor; + return p != null && p.IsInt; + } } + public override bool IsBool { get { + Type p = ProxyFor; + return p != null && p.IsBool; + } } + + public override bool IsVariable { get { + Type p = ProxyFor; + return p != null && p.IsVariable; + } } + public override TypeVariable! AsVariable { get { + Type p = ProxyFor; + assume p != null; + return p.AsVariable; + } } + + public override bool IsCtor { get { + Type p = ProxyFor; + return p != null && p.IsCtor; + } } + public override CtorType! AsCtor { get { + Type p = ProxyFor; + assume p != null; + return p.AsCtor; + } } + public override bool IsMap { get { + Type p = ProxyFor; + return p != null && p.IsMap; + } } + public override MapType! AsMap { get { + Type p = ProxyFor; + assume p != null; + return p.AsMap; + } } + public override int MapArity { get { + Type p = ProxyFor; + assume p != null; + return p.MapArity; + } } + public override bool IsUnresolved { get { + Type p = ProxyFor; + return p != null && p.IsUnresolved; + } } + public override UnresolvedTypeIdentifier! AsUnresolved { get { + Type p = ProxyFor; + assume p != null; + return p.AsUnresolved; + } } + + public override bool IsBv { get { + Type p = ProxyFor; + return p != null && p.IsBv; + } } + public override int BvBits { get { + Type p = ProxyFor; + assume p != null; + return p.BvBits; + } } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitTypeProxy(this); + } + } + + public abstract class ConstrainedProxy : TypeProxy { + protected ConstrainedProxy(IToken! token, string! givenName, string! kind) { + base(token, givenName, kind); + } + } + + /// + /// Each instance of this class represents a set of bitvector types. In particular, it represents + /// a bitvector type bvN iff + /// minBits ATMOST N and + /// foreach constraint (t0,t1), the types represented by t0 and t1 are bitvector types whose + /// number of bits add up to N. + /// This means that the size of a BvTypeProxy p is constrained not only by p.minBits, but also + /// by the size of various t0 and t1 types that are transitively part of BvTypeProxy constraints. + /// If such a t0 or t1 were to get its ProxyFor field defined, then p would have to be further + /// constrained too. This doesn't seem like it would ever occur in a Boogie 2 program, because: + /// the only place where a BvTypeProxy with constraints can occur is as the type of a + /// BvConcatExpr, and + /// the types of all local variables are explicitly declared, which means that the types of + /// subexpressions of a BvConcatExpr are not going to change other than via the type of the + /// BvConcatExpr. + /// So, this implementation of BvTypeProxy does not keep track of where a BvTypeProxy may occur + /// transitively in some other BvTypeProxy's constraints. + /// + public class BvTypeProxy : ConstrainedProxy { + public int MinBits; + List constraints; + class BvTypeConstraint { + public Type! T0; + public Type! T1; + public BvTypeConstraint(Type! t0, Type! t1) + requires t0.IsBv && t1.IsBv; + { + T0 = t0; + T1 = t1; + } + } + + public BvTypeProxy(IToken! token, string! name, int minBits) + { + base(token, name, "bv" + minBits + "proxy"); + this.MinBits = minBits; + } + + /// + /// Requires that any further constraints to be placed on t0 and t1 go via the object to + /// be constructed. + /// + public BvTypeProxy(IToken! token, string! name, Type! t0, Type! t1) + requires t0.IsBv && t1.IsBv; + { + base(token, name, "bvproxy"); + t0 = FollowProxy(t0); + t1 = FollowProxy(t1); + this.MinBits = MinBitsFor(t0) + MinBitsFor(t1); + List list = new List(); + list.Add(new BvTypeConstraint(t0, t1)); + this.constraints = list; + } + + /// + /// Construct a BvTypeProxy like p, but with minBits. + /// + private BvTypeProxy(BvTypeProxy! p, int minBits) + { + base(p.tok, p.Name, ""); + this.MinBits = minBits; + this.constraints = p.constraints; + } + + private BvTypeProxy(IToken! token, string! name, int minBits, List constraints) { + base(token, name, ""); + this.MinBits = minBits; + this.constraints = constraints; + } + + [Pure][Reads(ReadsAttribute.Reads.Everything)] + private static int MinBitsFor(Type! t) + requires t.IsBv; + ensures 0 <= result; + { + if (t is BvType) { + return t.BvBits; + } else { + return ((BvTypeProxy)t).MinBits; + } + } + + //----------- Cloning ---------------------------------- + + public override Type! Clone(IDictionary! varMap) { + Type p = ProxyFor; + if (p != null) { + return p.Clone(varMap); + } else { + return new BvTypeProxy(this.tok, this.Name, this.MinBits, this.constraints); // the clone will have a name that ends with $bvproxy$bvproxy + } + } + + public override Type! CloneUnresolved() { + return new BvTypeProxy(this.tok, this.Name, this.MinBits, this.constraints); // the clone will have a name that ends with $bvproxy$bvproxy + } + + //----------- Unification of types ----------- + + public override bool Unify(Type! that, + TypeVariableSeq! unifiableVariables, + IDictionary! result) { + Type p = ProxyFor; + if (p != null) { + return p.Unify(that, unifiableVariables, result); + } + + // unify this with that, if possible + that = that.Expanded; + that = FollowProxy(that); + + if (this.ReallyOccursIn(that)) + return false; + + TypeVariable tv = that as TypeVariable; + + if (tv != null && unifiableVariables.Has(tv)) + return that.Unify(this, unifiableVariables, result); + + if (object.ReferenceEquals(this, that)) { + return true; + } else if (that is BvType) { + if (MinBits <= that.BvBits) { + if (constraints != null) { + foreach (BvTypeConstraint btc in constraints) { + int minT1 = MinBitsFor(btc.T1); + int left = IncreaseBits(btc.T0, that.BvBits - minT1); + left = IncreaseBits(btc.T1, minT1 + left); + assert left == 0; // because it should always be possible to increase the total size of a BvTypeConstraint pair (t0,t1) arbitrarily + } + } + DefineProxy(that); + return true; + } + } else if (that is BvTypeProxy) { + BvTypeProxy bt = (BvTypeProxy)that; + // keep the proxy with the stronger constraint (that is, the higher minBits), but if either + // has a constraints list, then concatenate both constraints lists and define the previous + // proxies to the new one + if (this.constraints != null || bt.constraints != null) { + List list = new List(); + if (this.constraints != null) { list.AddRange(this.constraints); } + if (bt.constraints != null) { list.AddRange(bt.constraints); } + BvTypeProxy np = new BvTypeProxy(this.tok, this.Name, max{this.MinBits, bt.MinBits}, list); + this.DefineProxy(np); + bt.DefineProxy(np); + } else if (this.MinBits <= bt.MinBits) { + this.DefineProxy(bt); + } else { + bt.DefineProxy(this); + } + return true; + } else if (that is ConstrainedProxy) { + // only bitvector proxies can be unified with this BvTypeProxy + return false; + } else if (that is TypeProxy) { + // define: that.ProxyFor := this; + return that.Unify(this, unifiableVariables, result); + } + return false; + } + + private static int IncreaseBits(Type! t, int to) + requires t.IsBv && 0 <= to && MinBitsFor(t) <= to; + ensures 0 <= result && result <= to; + { + t = FollowProxy(t); + if (t is BvType) { + return to - t.BvBits; + } else { + BvTypeProxy p = (BvTypeProxy)t; + assert p.MinBits <= to; + if (p.MinBits < to) { + BvTypeProxy q = new BvTypeProxy(p, to); + p.DefineProxy(q); + } + return 0; // we were able to satisfy the request completely + } + } + + //----------- Substitution of free variables with types not containing bound variables ----------------- + + public override Type! Substitute(IDictionary! subst) { + if (this.ProxyFor == null) { + // check that the constraints are clean and do not contain any + // of the substituted variables (otherwise, we are in big trouble) + assert forall{BvTypeConstraint! c in constraints; + forall{TypeVariable! var in subst.Keys; + !c.T0.FreeVariables.Has(var) && !c.T1.FreeVariables.Has(var)}}; + } + return base.Substitute(subst); + } + + //----------- Getters/Issers ---------------------------------- + + public override bool IsBv { get { + return true; + } } + public override int BvBits { get { + // This method is supposed to return the number of bits supplied, but unless the proxy has been resolved, + // we only have a lower bound on the number of bits supplied. But this method is not supposed to be + // called until type checking has finished, at which time the minBits is stable. + Type p = ProxyFor; + if (p != null) { + return p.BvBits; + } else { + return MinBits; + } + } } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitBvTypeProxy(this); + } + } + + // Proxy representing map types with a certain arity. Apart from the arity, + // a number of constraints on the index and value type of the map type may + // be known (such constraints result from applied select and store operations). + // Because map type can be polymorphic (in the most general case, each index or + // value type is described by a separate type parameter) any combination of + // constraints can be satisfied. + public class MapTypeProxy : ConstrainedProxy { + public readonly int Arity; + private readonly List! constraints = new List (); + + // each constraint specifies that the given combination of argument/result + // types must be a possible instance of the formal map argument/result types + private struct Constraint { + public readonly TypeSeq! Arguments; + public readonly Type! Result; + + public Constraint(TypeSeq! arguments, Type! result) { + Arguments = arguments; + Result = result; + } + + public Constraint Clone(IDictionary! varMap) { + TypeSeq! args = new TypeSeq (); + foreach (Type! t in Arguments) + args.Add(t.Clone(varMap)); + Type! res = Result.Clone(varMap); + return new Constraint(args, res); + } + + public bool Unify(MapType! that, + TypeVariableSeq! unifiableVariables, + IDictionary! result) + requires Arguments.Length == that.Arguments.Length; { + Dictionary! subst = new Dictionary(); + foreach (TypeVariable! tv in that.TypeParameters) { + TypeProxy proxy = new TypeProxy(Token.NoToken, tv.Name); + subst.Add(tv, proxy); + } + + bool good = true; + for (int i = 0; i < that.Arguments.Length; i++) { + Type t0 = that.Arguments[i].Substitute(subst); + Type t1 = this.Arguments[i]; + good &= t0.Unify(t1, unifiableVariables, result); + } + good &= that.Result.Substitute(subst).Unify(this.Result, unifiableVariables, result); + return good; + } + } + + public MapTypeProxy(IToken! token, string! name, int arity) + requires 0 <= arity; { + base(token, name, "mapproxy"); + this.Arity = arity; + } + + private void AddConstraint(Constraint c) + requires c.Arguments.Length == Arity; { + + Type f = ProxyFor; + MapType mf = f as MapType; + if (mf != null) { + bool success = c.Unify(mf, new TypeVariableSeq(), new Dictionary ()); + assert success; + return; + } + + MapTypeProxy mpf = f as MapTypeProxy; + if (mpf != null) { + mpf.AddConstraint(c); + return; + } + + assert f == null; // no other types should occur as specialisations of this proxy + + constraints.Add(c); + } + + public Type CheckArgumentTypes(ExprSeq! actualArgs, + out TypeParamInstantiation! tpInstantiation, + IToken! typeCheckingSubject, + string! opName, + TypecheckingContext! tc) + { + Type f = ProxyFor; + MapType mf = f as MapType; + if (mf != null) + return mf.CheckArgumentTypes(actualArgs, out tpInstantiation, typeCheckingSubject, opName, tc); + + MapTypeProxy mpf = f as MapTypeProxy; + if (mpf != null) + return mpf.CheckArgumentTypes(actualArgs, out tpInstantiation, typeCheckingSubject, opName, tc); + + assert f == null; // no other types should occur as specialisations of this proxy + + // otherwise, we just record the constraints given by this usage of the map type + TypeSeq! arguments = new TypeSeq (); + foreach (Expr! e in actualArgs) + arguments.Add(e.Type); + Type! result = new TypeProxy (tok, "result"); + AddConstraint(new Constraint (arguments, result)); + + TypeSeq! argumentsResult = new TypeSeq (); + foreach (Expr! e in actualArgs) + argumentsResult.Add(e.Type); + argumentsResult.Add(result); + + tpInstantiation = new MapTypeProxyParamInstantiation(this, argumentsResult); + return result; + } + + //----------- Cloning ---------------------------------- + + public override Type! Clone(IDictionary! varMap) { + Type p = ProxyFor; + if (p != null) { + return p.Clone(varMap); + } else { + MapTypeProxy p2 = new MapTypeProxy(tok, Name, Arity); + foreach (Constraint c in constraints) + p2.AddConstraint(c.Clone(varMap)); + return p2; // the clone will have a name that ends with $mapproxy$mapproxy (hopefully) + } + } + + //----------- Linearisation ---------------------------------- + + public override void Emit(TokenTextWriter! stream, int contextBindingStrength) + { + Type p = ProxyFor; + if (p != null) { + p.Emit(stream, contextBindingStrength); + } else { + stream.Write("["); + string! sep = ""; + for (int i = 0; i < Arity; ++i) { + stream.Write(sep); + sep = ", "; + stream.Write("?"); + } + stream.Write("]?"); + } + } + + //----------- Unification of types ----------- + + public override bool Unify(Type! that, + TypeVariableSeq! unifiableVariables, + IDictionary! result) { + Type p = ProxyFor; + if (p != null) { + return p.Unify(that, unifiableVariables, result); + } + + // unify this with that, if possible + that = that.Expanded; + that = FollowProxy(that); + + if (this.ReallyOccursIn(that)) + return false; + + TypeVariable tv = that as TypeVariable; + + if (tv != null && unifiableVariables.Has(tv)) + return that.Unify(this, unifiableVariables, result); + + if (object.ReferenceEquals(this, that)) { + return true; + } else if (that is MapType) { + MapType mapType = (MapType)that; + if (mapType.Arguments.Length == Arity) { + bool good = true; + foreach (Constraint c in constraints) + good &= c.Unify(mapType, unifiableVariables, result); + if (good) { + DefineProxy(mapType); + return true; + } + } + } else if (that is MapTypeProxy) { + MapTypeProxy mt = (MapTypeProxy)that; + if (mt.Arity == this.Arity) { + // we propagate the constraints of this proxy to the more specific one + foreach (Constraint c in constraints) + mt.AddConstraint(c); + DefineProxy(mt); + return true; + } + } else if (that is ConstrainedProxy) { + // only map-type proxies can be unified with this MapTypeProxy + return false; + } else if (that is TypeProxy) { + // define: that.ProxyFor := this; + return that.Unify(this, unifiableVariables, result); + } + return false; + } + + //----------- Substitution of free variables with types not containing bound variables ----------------- + + public override Type! Substitute(IDictionary! subst) { + if (this.ProxyFor == null) { + // check that the constraints are clean and do not contain any + // of the substituted variables (otherwise, we are in big trouble) + assert forall{Constraint c in constraints; + forall{TypeVariable! var in subst.Keys; + forall{Type! t in c.Arguments; !t.FreeVariables.Has(var)} && + !c.Result.FreeVariables.Has(var)}}; + } + return base.Substitute(subst); + } + + //----------- Getters/Issers ---------------------------------- + + public override bool IsMap { get { return true; } } + public override MapType! AsMap { get { + Type p = ProxyFor; + if (p != null) { + return p.AsMap; + } else { + assert false; // what to do now? + } + } } + public override int MapArity { get { + return Arity; + } } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitMapTypeProxy(this); + } + } + + //===================================================================== + + // Used to annotate types with type synoyms that were used in the + // original unresolved types. Such types should be considered as + // equivalent to ExpandedType, the annotations are only used to enable + // better pretty-printing + public class TypeSynonymAnnotation : Type { + public Type! ExpandedType; + + public readonly TypeSeq! Arguments; + // is set during resolution and determines whether the right number of arguments is given + public readonly TypeSynonymDecl! Decl; + + public TypeSynonymAnnotation(IToken! token, TypeSynonymDecl! decl, TypeSeq! arguments) + : base(token) + requires arguments.Length == decl.TypeParameters.Length; + { + this.Decl = decl; + this.Arguments = arguments; + + // build a substitution that can be applied to the definition of + // the type synonym + IDictionary! subst = + new Dictionary (); + for (int i = 0; i < arguments.Length; ++i) + subst.Add(decl.TypeParameters[i], arguments[i]); + + ExpandedType = decl.Body.Substitute(subst); + } + + private TypeSynonymAnnotation(IToken! token, TypeSynonymDecl! decl, TypeSeq! arguments, + Type! expandedType) + : base(token) { + this.Decl = decl; + this.Arguments = arguments; + this.ExpandedType = expandedType; + } + + //----------- Cloning ---------------------------------- + // We implement our own clone-method, because bound type variables + // have to be created in the right way. It is /not/ ok to just clone + // everything recursively + + public override Type! Clone(IDictionary! varMap) { + TypeSeq! newArgs = new TypeSeq (); + foreach(Type! t in Arguments) + newArgs.Add(t.Clone(varMap)); + Type! newExpandedType = ExpandedType.Clone(varMap); + return new TypeSynonymAnnotation(tok, Decl, newArgs, newExpandedType); + } + + public override Type! CloneUnresolved() { + TypeSeq! newArgs = new TypeSeq (); + foreach(Type! t in Arguments) + newArgs.Add(t.CloneUnresolved()); + return new TypeSynonymAnnotation(tok, Decl, newArgs); + } + + //----------- Equality ---------------------------------- + + [Pure] + public override bool Equals(Type! that, + TypeVariableSeq! thisBoundVariables, + TypeVariableSeq! thatBoundVariables) { + return ExpandedType.Equals(that, thisBoundVariables, thatBoundVariables); + } + + // used to skip leading type annotations + internal override Type! Expanded { get { + return ExpandedType.Expanded; + } } + + //----------- Unification of types ----------- + + public override bool Unify(Type! that, + TypeVariableSeq! unifiableVariables, + IDictionary! result) { + return ExpandedType.Unify(that, unifiableVariables, result); + } + +#if OLD_UNIFICATION + public override void Unify(Type! that, + TypeVariableSeq! unifiableVariables, + TypeVariableSeq! thisBoundVariables, + TypeVariableSeq! thatBoundVariables, + IDictionary! result) { + ExpandedType.Unify(that, unifiableVariables, + thisBoundVariables, thatBoundVariables, result); + } +#endif + + //----------- Substitution of free variables with types not containing bound variables ----------------- + + public override Type! Substitute(IDictionary! subst) { + if (subst.Count == 0) + return this; + TypeSeq newArgs = new TypeSeq (); + foreach (Type! t in Arguments) + newArgs.Add(t.Substitute(subst)); + Type! newExpandedType = ExpandedType.Substitute(subst); + return new TypeSynonymAnnotation(tok, Decl, newArgs, newExpandedType); + } + + //----------- Hashcodes ---------------------------------- + + [Pure] + public override int GetHashCode(TypeVariableSeq! boundVariables) { + return ExpandedType.GetHashCode(boundVariables); + } + + //----------- Linearisation ---------------------------------- + + public override void Emit(TokenTextWriter! stream, int contextBindingStrength) + { + stream.SetToken(this); + CtorType.EmitCtorType(this.Decl.Name, Arguments, stream, contextBindingStrength); + } + + //----------- Resolution ---------------------------------- + + public override Type! ResolveType(ResolutionContext! rc) { + TypeSeq resolvedArgs = new TypeSeq (); + foreach (Type! t in Arguments) + resolvedArgs.Add(t.ResolveType(rc)); + return new TypeSynonymAnnotation(tok, Decl, resolvedArgs); + } + + public override TypeVariableSeq! FreeVariables { get { + return ExpandedType.FreeVariables; + } } + + public override List! FreeProxies { get { + return ExpandedType.FreeProxies; + } } + + //----------- Getters/Issers ---------------------------------- + + public override bool IsBasic { get { return ExpandedType.IsBasic; } } + public override bool IsInt { get { return ExpandedType.IsInt; } } + public override bool IsBool { get { return ExpandedType.IsBool; } } + + public override bool IsVariable { get { return ExpandedType.IsVariable; } } + public override TypeVariable! AsVariable { get { return ExpandedType.AsVariable; } } + public override bool IsCtor { get { return ExpandedType.IsCtor; } } + public override CtorType! AsCtor { get { return ExpandedType.AsCtor; } } + public override bool IsMap { get { return ExpandedType.IsMap; } } + public override MapType! AsMap { get { return ExpandedType.AsMap; } } + public override bool IsUnresolved { get { return ExpandedType.IsUnresolved; } } + public override UnresolvedTypeIdentifier! AsUnresolved { get { + return ExpandedType.AsUnresolved; } } + + public override bool IsBv { get { return ExpandedType.IsBv; } } + public override int BvBits { get { return ExpandedType.BvBits; } } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitTypeSynonymAnnotation(this); + } + } + + //===================================================================== + + public class CtorType : Type { + public readonly TypeSeq! Arguments; + // is set during resolution and determines whether the right number of arguments is given + public readonly TypeCtorDecl! Decl; + + public CtorType(IToken! token, TypeCtorDecl! decl, TypeSeq! arguments) + : base(token) + requires arguments.Length == decl.Arity; + { + this.Decl = decl; + this.Arguments = arguments; + } + + //----------- Cloning ---------------------------------- + // We implement our own clone-method, because bound type variables + // have to be created in the right way. It is /not/ ok to just clone + // everything recursively + + public override Type! Clone(IDictionary! varMap) { + TypeSeq! newArgs = new TypeSeq (); + foreach(Type! t in Arguments) + newArgs.Add(t.Clone(varMap)); + return new CtorType(tok, Decl, newArgs); + } + + public override Type! CloneUnresolved() { + TypeSeq! newArgs = new TypeSeq (); + foreach(Type! t in Arguments) + newArgs.Add(t.CloneUnresolved()); + return new CtorType(tok, Decl, newArgs); + } + + //----------- Equality ---------------------------------- + + [Pure][Reads(ReadsAttribute.Reads.Nothing)] + public override bool Equals(object that) { + Type thatType = that as Type; + if (thatType == null) + return false; + thatType = TypeProxy.FollowProxy(thatType.Expanded); + // shortcut + CtorType thatCtorType = thatType as CtorType; + if (thatCtorType == null || !this.Decl.Equals(thatCtorType.Decl)) + return false; + if (Arguments.Length == 0) + return true; + return base.Equals(thatType); + } + + [Pure] + public override bool Equals(Type! that, + TypeVariableSeq! thisBoundVariables, + TypeVariableSeq! thatBoundVariables) { + that = TypeProxy.FollowProxy(that.Expanded); + CtorType thatCtorType = that as CtorType; + if (thatCtorType == null || !this.Decl.Equals(thatCtorType.Decl)) + return false; + for (int i = 0; i < Arguments.Length; ++i) { + if (!Arguments[i].Equals(thatCtorType.Arguments[i], + thisBoundVariables, thatBoundVariables)) + return false; + } + return true; + } + + //----------- Unification of types ----------- + + public override bool Unify(Type! that, + TypeVariableSeq! unifiableVariables, + IDictionary! result) { + that = that.Expanded; + if (that is TypeProxy || that is TypeVariable) + return that.Unify(this, unifiableVariables, result); + + CtorType thatCtorType = that as CtorType; + if (thatCtorType == null || !thatCtorType.Decl.Equals(Decl)) { + return false; + } else { + bool good = true; + for (int i = 0; i < Arguments.Length; ++i) + good &= Arguments[i].Unify(thatCtorType.Arguments[i], unifiableVariables, result); + return good; + } + } + +#if OLD_UNIFICATION + public override void Unify(Type! that, + TypeVariableSeq! unifiableVariables, + TypeVariableSeq! thisBoundVariables, + TypeVariableSeq! thatBoundVariables, + IDictionary! result) { + that = that.Expanded; + if (that is TypeVariable) { + that.Unify(this, unifiableVariables, thatBoundVariables, thisBoundVariables, result); + return; + } + + CtorType thatCtorType = that as CtorType; + if (thatCtorType == null || !thatCtorType.Decl.Equals(Decl)) + throw UNIFICATION_FAILED; + for (int i = 0; i < Arguments.Length; ++i) + Arguments[i].Unify(thatCtorType.Arguments[i], + unifiableVariables, + thisBoundVariables, thatBoundVariables, + result); + } +#endif + + //----------- Substitution of free variables with types not containing bound variables ----------------- + + public override Type! Substitute(IDictionary! subst) { + if (subst.Count == 0) + return this; + TypeSeq newArgs = new TypeSeq (); + foreach (Type! t in Arguments) + newArgs.Add(t.Substitute(subst)); + return new CtorType(tok, Decl, newArgs); + } + + //----------- Hashcodes ---------------------------------- + + [Pure] + public override int GetHashCode(TypeVariableSeq! boundVariables) { + int res = 1637643879 * Decl.GetHashCode(); + foreach (Type! t in Arguments) + res = res * 3 + t.GetHashCode(boundVariables); + return res; + } + + //----------- Linearisation ---------------------------------- + + public override void Emit(TokenTextWriter! stream, int contextBindingStrength) + { + stream.SetToken(this); + EmitCtorType(this.Decl.Name, Arguments, stream, contextBindingStrength); + } + + internal static void EmitCtorType(string! name, TypeSeq! args, TokenTextWriter! stream, int contextBindingStrength) { + int opBindingStrength = args.Length > 0 ? 0 : 2; + if (opBindingStrength < contextBindingStrength) + stream.Write("("); + + stream.Write("{0}", TokenTextWriter.SanitizeIdentifier(name)); + int i = args.Length; + foreach (Type! t in args) { + stream.Write(" "); + // use a lower binding strength for the last argument + // to allow map-types without parentheses + t.Emit(stream, i == 1 ? 1 : 2); + i = i - 1; + } + + if (opBindingStrength < contextBindingStrength) + stream.Write(")"); + } + + //----------- Resolution ---------------------------------- + + public override Type! ResolveType(ResolutionContext! rc) { + TypeSeq resolvedArgs = new TypeSeq (); + foreach (Type! t in Arguments) + resolvedArgs.Add(t.ResolveType(rc)); + return new CtorType(tok, Decl, resolvedArgs); + } + + public override TypeVariableSeq! FreeVariables { + get { + TypeVariableSeq! res = new TypeVariableSeq (); + foreach (Type! t in Arguments) + res.AppendWithoutDups(t.FreeVariables); + return res; + } + } + + public override List! FreeProxies { get { + List! res = new List (); + foreach (Type! t in Arguments) + AppendWithoutDups(res, t.FreeProxies); + return res; + } } + + //----------- Getters/Issers ---------------------------------- + + public override bool IsCtor { get { return true; } } + public override CtorType! AsCtor { get { return this; } } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitCtorType(this); + } + } + + //===================================================================== + + public class MapType : Type { + // an invariant is that each of the type parameters has to occur as + // free variable in at least one of the arguments + public readonly TypeVariableSeq! TypeParameters; + public readonly TypeSeq! Arguments; + public Type! Result; + + public MapType(IToken! token, TypeVariableSeq! typeParameters, TypeSeq! arguments, Type! result) + : base(token) + { + this.TypeParameters = typeParameters; + this.Result = result; + this.Arguments = arguments; + } + + //----------- Cloning ---------------------------------- + // We implement our own clone-method, because bound type variables + // have to be created in the right way. It is /not/ ok to just clone + // everything recursively + + public override Type! Clone(IDictionary! varMap) { + IDictionary! newVarMap = + new Dictionary(); + foreach (KeyValuePair p in varMap) { + if (!TypeParameters.Has(p.Key)) + newVarMap.Add(p); + } + + TypeVariableSeq! newTypeParams = new TypeVariableSeq (); + foreach (TypeVariable! var in TypeParameters) { + TypeVariable! newVar = new TypeVariable (var.tok, var.Name); + newVarMap.Add(var, newVar); + newTypeParams.Add(newVar); + } + + TypeSeq! newArgs = new TypeSeq (); + foreach(Type! t in Arguments) + newArgs.Add(t.Clone(newVarMap)); + Type! newResult = Result.Clone(newVarMap); + + return new MapType (this.tok, newTypeParams, newArgs, newResult); + } + + public override Type! CloneUnresolved() { + TypeVariableSeq! newTypeParams = new TypeVariableSeq (); + foreach (TypeVariable! var in TypeParameters) { + TypeVariable! newVar = new TypeVariable (var.tok, var.Name); + newTypeParams.Add(newVar); + } + + TypeSeq! newArgs = new TypeSeq (); + foreach(Type! t in Arguments) + newArgs.Add(t.CloneUnresolved()); + Type! newResult = Result.CloneUnresolved(); + + return new MapType (this.tok, newTypeParams, newArgs, newResult); + } + + //----------- Equality ---------------------------------- + + [Pure] + public override bool Equals(Type! that, + TypeVariableSeq! thisBoundVariables, + TypeVariableSeq! thatBoundVariables) { + that = TypeProxy.FollowProxy(that.Expanded); + MapType thatMapType = that as MapType; + if (thatMapType == null || + this.TypeParameters.Length != thatMapType.TypeParameters.Length || + this.Arguments.Length != thatMapType.Arguments.Length) + return false; + + foreach (TypeVariable! var in this.TypeParameters) + thisBoundVariables.Add(var); + foreach (TypeVariable! var in thatMapType.TypeParameters) + thatBoundVariables.Add(var); + + try { + + for (int i = 0; i < Arguments.Length; ++i) { + if (!Arguments[i].Equals(thatMapType.Arguments[i], + thisBoundVariables, thatBoundVariables)) + return false; + } + if (!this.Result.Equals(thatMapType.Result, + thisBoundVariables, thatBoundVariables)) + return false; + + } finally { + // make sure that the bound variables are removed again + for (int i = 0; i < this.TypeParameters.Length; ++i) { + thisBoundVariables.Remove(); + thatBoundVariables.Remove(); + } + } + + return true; + } + + //----------- Unification of types ----------- + + public override bool Unify(Type! that, + TypeVariableSeq! unifiableVariables, + IDictionary! result) { + that = that.Expanded; + if (that is TypeProxy || that is TypeVariable) + return that.Unify(this, unifiableVariables, result); + + MapType thatMapType = that as MapType; + if (thatMapType == null || + this.TypeParameters.Length != thatMapType.TypeParameters.Length || + this.Arguments.Length != thatMapType.Arguments.Length) + return false; + + // treat the bound variables of the two map types as equal... + Dictionary! subst0 = new Dictionary(); + Dictionary! subst1 = new Dictionary(); + TypeVariableSeq freshies = new TypeVariableSeq(); + for (int i = 0; i < this.TypeParameters.Length; i++) { + TypeVariable tp0 = this.TypeParameters[i]; + TypeVariable tp1 = thatMapType.TypeParameters[i]; + TypeVariable freshVar = new TypeVariable(tp0.tok, tp0.Name); + freshies.Add(freshVar); + subst0.Add(tp0, freshVar); + subst1.Add(tp1, freshVar); + } + // ... and then unify the domain and range types + bool good = true; + for (int i = 0; i < this.Arguments.Length; i++) { + Type t0 = this.Arguments[i].Substitute(subst0); + Type t1 = thatMapType.Arguments[i].Substitute(subst1); + good &= t0.Unify(t1, unifiableVariables, result); + } + Type r0 = this.Result.Substitute(subst0); + Type r1 = thatMapType.Result.Substitute(subst1); + good &= r0.Unify(r1, unifiableVariables, result); + + // Finally, check that none of the bound variables has escaped + if (good && freshies.Length != 0) { + // This is done by looking for occurrences of the fresh variables in the + // non-substituted types ... + TypeVariableSeq freeVars = this.FreeVariables; + foreach (TypeVariable fr in freshies) + if (freeVars.Has(fr)) { return false; } // fresh variable escaped + freeVars = thatMapType.FreeVariables; + foreach (TypeVariable fr in freshies) + if (freeVars.Has(fr)) { return false; } // fresh variable escaped + + // ... and in the resulting unifier of type variables + foreach (KeyValuePair pair in result) { + freeVars = pair.Value.FreeVariables; + foreach (TypeVariable fr in freshies) + if (freeVars.Has(fr)) { return false; } // fresh variable escaped + } + } + + return good; + } + +#if OLD_UNIFICATION + public override void Unify(Type! that, + TypeVariableSeq! unifiableVariables, + TypeVariableSeq! thisBoundVariables, + TypeVariableSeq! thatBoundVariables, + IDictionary! result) { + that = that.Expanded; + if (that is TypeVariable) { + that.Unify(this, unifiableVariables, thatBoundVariables, thisBoundVariables, result); + return; + } + + MapType thatMapType = that as MapType; + if (thatMapType == null || + this.TypeParameters.Length != thatMapType.TypeParameters.Length || + this.Arguments.Length != thatMapType.Arguments.Length) + throw UNIFICATION_FAILED; + + // ensure that no collisions occur + if (this.collisionsPossible(result)) { + ((MapType)this.Clone()) + .Unify(that, unifiableVariables, + thisBoundVariables, thatBoundVariables, result); + return; + } + if (thatMapType.collisionsPossible(result)) + thatMapType = (MapType)that.Clone(); + + foreach (TypeVariable! var in this.TypeParameters) + thisBoundVariables.Add(var); + foreach (TypeVariable! var in thatMapType.TypeParameters) + thatBoundVariables.Add(var); + + try { + + for (int i = 0; i < Arguments.Length; ++i) + Arguments[i].Unify(thatMapType.Arguments[i], + unifiableVariables, + thisBoundVariables, thatBoundVariables, + result); + Result.Unify(thatMapType.Result, + unifiableVariables, + thisBoundVariables, thatBoundVariables, + result); + + } finally { + // make sure that the bound variables are removed again + for (int i = 0; i < this.TypeParameters.Length; ++i) { + thisBoundVariables.Remove(); + thatBoundVariables.Remove(); + } + } + } +#endif + + //----------- Substitution of free variables with types not containing bound variables ----------------- + + [Pure] + private bool collisionsPossible(IDictionary! subst) { + // PR: could be written more efficiently + return exists{TypeVariable! var in TypeParameters; + subst.ContainsKey(var) || + exists{Type! t in subst.Values; t.FreeVariables.Has(var)}}; + } + + public override Type! Substitute(IDictionary! subst) { + if (subst.Count == 0) + return this; + + // there are two cases in which we have to be careful: + // * a variable to be substituted is shadowed by a variable binder + // * a substituted term contains variables that are bound in the + // type (variable capture) + // + // in both cases, we first clone the type to ensure that bound + // variables are fresh + + if (collisionsPossible(subst)) { + MapType! newType = (MapType)this.Clone(); + assert newType.Equals(this) && !newType.collisionsPossible(subst); + return newType.Substitute(subst); + } + + TypeSeq newArgs = new TypeSeq (); + foreach (Type! t in Arguments) + newArgs.Add(t.Substitute(subst)); + Type! newResult = Result.Substitute(subst); + + return new MapType(tok, TypeParameters, newArgs, newResult); + } + + //----------- Hashcodes ---------------------------------- + + [Pure] + public override int GetHashCode(TypeVariableSeq! boundVariables) { + int res = 7643761 * TypeParameters.Length + 65121 * Arguments.Length; + + foreach (TypeVariable! var in this.TypeParameters) + boundVariables.Add(var); + + foreach (Type! t in Arguments) + res = res * 5 + t.GetHashCode(boundVariables); + res = res * 7 + Result.GetHashCode(boundVariables); + + for (int i = 0; i < this.TypeParameters.Length; ++i) + boundVariables.Remove(); + + return res; + } + + //----------- Linearisation ---------------------------------- + + public override void Emit(TokenTextWriter! stream, int contextBindingStrength) + { + stream.SetToken(this); + + const int opBindingStrength = 1; + if (opBindingStrength < contextBindingStrength) + stream.Write("("); + + EmitOptionalTypeParams(stream, TypeParameters); + + stream.Write("["); + Arguments.Emit(stream, ","); // default binding strength of 0 is ok + stream.Write("]"); + Result.Emit(stream); // default binding strength of 0 is ok + + if (opBindingStrength < contextBindingStrength) + stream.Write(")"); + } + + //----------- Resolution ---------------------------------- + + public override Type! ResolveType(ResolutionContext! rc) { + int previousState = rc.TypeBinderState; + try { + foreach (TypeVariable! v in TypeParameters) { + rc.AddTypeBinder(v); + } + + TypeSeq resolvedArgs = new TypeSeq (); + foreach (Type! ty in Arguments) { + resolvedArgs.Add(ty.ResolveType(rc)); + } + + Type resolvedResult = Result.ResolveType(rc); + + CheckBoundVariableOccurrences(TypeParameters, + resolvedArgs, new TypeSeq(resolvedResult), + this.tok, "map arguments", + rc); + + // sort the type parameters so that they are bound in the order of occurrence + TypeVariableSeq! sortedTypeParams = SortTypeParams(TypeParameters, resolvedArgs, resolvedResult); + return new MapType(tok, sortedTypeParams, resolvedArgs, resolvedResult); + } finally { + rc.TypeBinderState = previousState; + } + } + + public override TypeVariableSeq! FreeVariables { + get { + TypeVariableSeq! res = FreeVariablesIn(Arguments); + res.AppendWithoutDups(Result.FreeVariables); + foreach (TypeVariable! v in TypeParameters) + res.Remove(v); + return res; + } + } + + public override List! FreeProxies { get { + List! res = new List (); + foreach (Type! t in Arguments) + AppendWithoutDups(res, t.FreeProxies); + AppendWithoutDups(res, Result.FreeProxies); + return res; + } } + + //----------- Getters/Issers ---------------------------------- + + public override bool IsMap { get { return true; } } + public override MapType! AsMap { get { return this; } } + public override int MapArity { get { + return Arguments.Length; + } } + + //------------ Match formal argument types of the map + //------------ on concrete types, substitute the result into the + //------------ result type. Null is returned if so many type checking + //------------ errors occur that the situation is hopeless + + public Type CheckArgumentTypes(ExprSeq! actualArgs, + out TypeParamInstantiation! tpInstantiation, + IToken! typeCheckingSubject, + string! opName, + TypecheckingContext! tc) { + List! actualTypeParams; + TypeSeq actualResult = + Type.CheckArgumentTypes(TypeParameters, out actualTypeParams, Arguments, actualArgs, + new TypeSeq (Result), null, typeCheckingSubject, opName, tc); + if (actualResult == null) { + tpInstantiation = SimpleTypeParamInstantiation.EMPTY; + return null; + } else { + assert actualResult.Length == 1; + tpInstantiation = SimpleTypeParamInstantiation.From(TypeParameters, actualTypeParams); + return actualResult[0]; + } + } + + public override Absy! StdDispatch(StandardVisitor! visitor) + { + return visitor.VisitMapType(this); + } + } + + //--------------------------------------------------------------------- + + public enum SimpleType { Int, Bool }; + + + //===================================================================== + + // Interface for representing the instantiations of type parameters of + // polymorphic functions or maps. We introduce an own interface for this + // instead of using a simple list or dictionary, because in some cases + // (due to the type proxies for map types) the actual number and instantiation + // of type parameters can only be determined very late. + public interface TypeParamInstantiation { + // return what formal type parameters there are + List! FormalTypeParams { get; } + // given a formal type parameter, return the actual instantiation + Type! this[TypeVariable! var] { get; } + } + + public class SimpleTypeParamInstantiation : TypeParamInstantiation { + private readonly List! TypeParams; + private readonly IDictionary! Instantiations; + + public SimpleTypeParamInstantiation(List! typeParams, + IDictionary! instantiations) { + this.TypeParams = typeParams; + this.Instantiations = instantiations; + } + + public static TypeParamInstantiation! + From(TypeVariableSeq! typeParams, List! actualTypeParams) + requires typeParams.Length == actualTypeParams.Count; { + if (typeParams.Length == 0) + return EMPTY; + + List! typeParamList = new List (); + IDictionary! dict = new Dictionary (); + for (int i = 0; i < typeParams.Length; ++i) { + typeParamList.Add(typeParams[i]); + dict.Add(typeParams[i], actualTypeParams[i]); + } + return new SimpleTypeParamInstantiation(typeParamList, dict); + } + + public static readonly TypeParamInstantiation! EMPTY = + new SimpleTypeParamInstantiation (new List (), + new Dictionary ()); + + // return what formal type parameters there are + public List! FormalTypeParams { get { + return TypeParams; + } } + // given a formal type parameter, return the actual instantiation + public Type! this[TypeVariable! var] { get { + return Instantiations[var]; + } } + } + + // Implementation of TypeParamInstantiation that refers to the current + // value of a MapTypeProxy. This means that the values return by the + // methods of this implementation can change in case the MapTypeProxy + // receives further unifications. + class MapTypeProxyParamInstantiation : TypeParamInstantiation { + private readonly MapTypeProxy! Proxy; + + // the argument and result type of this particular usage of the map + // type. these are necessary to derive the values of the type parameters + private readonly TypeSeq! ArgumentsResult; + + // field that is initialised once all necessary information is available + // (the MapTypeProxy is instantiated to an actual type) and the instantiation + // of a type parameter is queried + private IDictionary Instantiations = null; + + public MapTypeProxyParamInstantiation(MapTypeProxy! proxy, + TypeSeq! argumentsResult) { + this.Proxy = proxy; + this.ArgumentsResult = argumentsResult; + } + + // return what formal type parameters there are + public List! FormalTypeParams { get { + MapType realType = Proxy.ProxyFor as MapType; + if (realType == null) + // no instantiation of the map type is known, which means + // that the map type is assumed to be monomorphic + return new List (); + else + return realType.TypeParameters.ToList(); + } } + + // given a formal type parameter, return the actual instantiation + public Type! this[TypeVariable! var] { get { + // then there has to be an instantiation that is a polymorphic map type + if (Instantiations == null) { + MapType realType = Proxy.ProxyFor as MapType; + assert realType != null; + TypeSeq! formalArgs = new TypeSeq (); + foreach (Type! t in realType.Arguments) + formalArgs.Add(t); + formalArgs.Add(realType.Result); + Instantiations = + Type.InferTypeParameters(realType.TypeParameters, formalArgs, ArgumentsResult); + } + return Instantiations[var]; + } } + } + +} diff --git a/Source/Core/AssemblyInfo.cs b/Source/Core/AssemblyInfo.cs new file mode 100644 index 00000000..6ed99a25 --- /dev/null +++ b/Source/Core/AssemblyInfo.cs @@ -0,0 +1,4 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: AssemblyKeyFile("..\\InterimKey.snk")] diff --git a/Source/Core/CommandLineOptions.cs b/Source/Core/CommandLineOptions.cs new file mode 100644 index 00000000..6808be61 --- /dev/null +++ b/Source/Core/CommandLineOptions.cs @@ -0,0 +1,2109 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) Microsoft Corporation. All Rights Reserved. +// +//----------------------------------------------------------------------------- +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.IO; +using System.Diagnostics; +using Microsoft.Contracts; +using Cci = System.Compiler; + +namespace Microsoft.Boogie +{ + public class CommandLineOptions + { + public static string! VersionNumber { get { return (!)((!)System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location)).FileVersion; } } + public const string ToolNameBoogie = "Boogie program verifier"; + public const string ToolNameSpecSharp = "Spec# program verifier"; + public const string ToolNameDafny = "Dafny program verifier"; + public static string! VersionSuffix { get { return " version " + VersionNumber + ", Copyright (c) 2003-2010, Microsoft."; } } + public string! InputFileExtension { + set + modifies _toolname, _version; + { + switch (value) { + case ".bpl": _toolname = ToolNameBoogie; break; + case ".dfy": _toolname = ToolNameDafny; break; + default: _toolname = ToolNameSpecSharp; break; + } + _version = _toolname + VersionSuffix; + } + } + string! _toolname = ToolNameBoogie; + string! _version = ToolNameBoogie + VersionSuffix; + public string! ToolName { get { return _toolname; } } + public string! Version { get { return _version; } } + + public static CommandLineOptions! Clo = new CommandLineOptions(); // singleton to access all global data + + public string! Environment = ""; + public string! FileName = "unknown"; + + public const long Megabyte = 1048576; + + // Flags and arguments + + public bool RunningBoogieFromCommandLine = false; // "false" means running Boogie from the plug-in + public bool RunningBoogieOnSsc = true; // "true" means running Boogie on ssc input while false means running it on bpl input + + public bool AttrHelpRequested = false; + + [Peer] public List! Files = new List(); + public List! ContractAssemblies = new List(); + + public string! FileTimestamp = ((!)DateTime.Now.ToString("o")).Replace(':', '.'); + public void ExpandFilename(ref string pattern) + { + if (pattern != null) { + pattern = pattern.Replace("@PREFIX@", LogPrefix).Replace("@TIME@", FileTimestamp); + string fn = Files.Count == 0 ? "" : Files[Files.Count-1]; + fn = fn.Replace('/', '-').Replace('\\', '-'); + pattern = pattern.Replace("@FILE@", fn); + } + } + + public string PrintFile = null; + public int PrintUnstructured = 0; + invariant 0 <= PrintUnstructured && PrintUnstructured < 3; // 0 = print only structured, 1 = both structured and unstructured, 2 = only unstructured + public bool PrintDesugarings = false; + public string SimplifyLogFilePath = null; + public string SMTLibOutputPath = "boogie-vc-@PROC@.smt"; + public string! LogPrefix = ""; + public bool PrintInstrumented = false; + public bool InstrumentWithAsserts = false; + public enum InstrumentationPlaces { LoopHeaders, Everywhere } + public InstrumentationPlaces InstrumentInfer = InstrumentationPlaces.LoopHeaders; + public bool PrintWithUniqueASTIds = false; + private string XmlSinkFilename = null; + [Peer] public XmlSink XmlSink = null; + public bool Wait = false; + public bool Trace = false; + public bool TraceTimes = false; + public bool NoResolve = false; + public bool NoTypecheck = false; + public bool OverlookBoogieTypeErrors = false; + public bool Verify = true; + public bool TraceVerify = false; + public int /*(0:3)*/ ErrorTrace = 1; + public bool IntraproceduralInfer = true; + public bool ContractInfer = false; + public bool UseUncheckedContracts = false; + public bool SimplifyLogFileAppend = false; + public bool SoundnessSmokeTest = false; + + private bool noConsistencyChecks = false; + public bool NoConsistencyChecks { + get {return !Verify ? true : noConsistencyChecks;} + set + modifies noConsistencyChecks; + {noConsistencyChecks = value;} + } + + public string DafnyPrintFile = null; + public bool Compile = true; + + public enum ProverWarnings { None, Stdout, Stderr } + public ProverWarnings PrintProverWarnings = ProverWarnings.None; + public int ProverShutdownLimit = 0; + + public enum SubsumptionOption { Never, NotForQuantifiers, Always } + public SubsumptionOption UseSubsumption = SubsumptionOption.Always; + + public bool AlwaysAssumeFreeLoopInvariants = false; + + public enum ShowEnvironment { Never, DuringPrint, Always } + public ShowEnvironment ShowEnv = ShowEnvironment.DuringPrint; + public bool DontShowLogo = false; + + public int CheckingLevel = 2; + invariant 0 <= CheckingLevel && CheckingLevel < 3; + public enum Methodology { Boogie, VisibleState } + public Methodology MethodologySelection = Methodology.Boogie; + public int OrderStrength = 0; + invariant 0 <= OrderStrength && OrderStrength < 2; + public bool UseArithDistributionAxioms = false; + public int SummationAxiomStrength = 1; + invariant 0 <= SummationAxiomStrength && SummationAxiomStrength < 2; + public int InductiveMinMax = 0; + invariant 0 <= InductiveMinMax && InductiveMinMax < 6; + public int FCOStrength = 5; + invariant 0 <= FCOStrength && FCOStrength < 6; + public int LoopUnrollCount = -1; // -1 means don't unroll loops + public int LoopFrameConditions = -1; // -1 means not specified -- this will be replaced by the "implications" section below + invariant -1 <= LoopFrameConditions && LoopFrameConditions < 3; + public int ModifiesDefault = 5; + invariant 0 <= ModifiesDefault && ModifiesDefault < 7; + public bool LocalModifiesChecks = true; + public bool NoVerifyByDefault = false; + public enum OwnershipModelOption { Standard, Experimental, Trivial } + public OwnershipModelOption OwnershipModelEncoding = OwnershipModelOption.Standard; + public int PrintErrorModel = 0; + public string PrintErrorModelFile = null; + invariant (0 <= PrintErrorModel && PrintErrorModel <= 2) || PrintErrorModel == 4; + public bool CEVPrint = false; + public int EnhancedErrorMessages = 0; + invariant 0 <= EnhancedErrorMessages && EnhancedErrorMessages < 2; + public bool ForceBplErrors = false; // if true, boogie error is shown even if "msg" attribute is present + + public enum BvHandling { None, Z3Native, ToInt } + public BvHandling Bitvectors = BvHandling.Z3Native; + + public bool UseArrayTheory = false; + public bool MonomorphicArrays { get { return UseArrayTheory || TypeEncodingMethod == TypeEncoding.Monomorphic; } } + public bool ExpandLambdas = true; // not useful from command line, only to be set to false programatically + + public bool DoModSetAnalysis = false; + public bool UseAbstractInterpretation = true; // true iff the user want to use abstract interpretation + public int /*0..9*/StepsBeforeWidening = 0; // The number of steps that must be done before applying a widen operator + invariant 0 <= StepsBeforeWidening && StepsBeforeWidening <= 9; + + public enum VCVariety { Structured, Block, Local, BlockNested, BlockReach, BlockNestedReach, Dag, Doomed, Unspecified } + public VCVariety vcVariety = VCVariety.Unspecified; // will not be Unspecified after command line has been parsed + public bool useDoomDebug = false; // Will use doomed analysis to search for errors if set + + public bool RemoveEmptyBlocks = true; + public bool CoalesceBlocks = true; + + [Rep] public ProverFactory TheProverFactory; + public string ProverName; + [Peer] public List! ProverOptions = new List(); + + public int BracketIdsInVC = -1; // -1 - not specified, 0 - no, 1 - yes + public bool CausalImplies = false; + invariant -1 <= BracketIdsInVC && BracketIdsInVC <= 1; + public int SimplifyProverMatchDepth = -1; // -1 means not specified + public int ProverKillTime = -1; // -1 means not specified + public int SmokeTimeout = 10; // default to 10s + public int ProverCCLimit = 5; + public bool z3AtFlag = true; + public bool RestartProverPerVC = false; + + public double VcsMaxCost = 1.0; + public double VcsPathJoinMult = 0.8; + public double VcsPathCostMult = 1.0; + public double VcsAssumeMult = 0.01; + public double VcsPathSplitMult = 0.5; // 0.5-always, 2-rarely do path splitting + public int VcsMaxSplits = 1; + public int VcsMaxKeepGoingSplits = 1; + public int VcsFinalAssertTimeout = 30; + public int VcsKeepGoingTimeout = 1; + public int VcsCores = 1; + public bool VcsDumpSplits = false; + + public bool houdiniEnabled = false; + public bool DebugRefuted = false; + + public XmlSink XmlRefuted + { + get { if (DebugRefuted) return XmlSink; else return null; } + } + + [Peer] public List! Z3Options = new List(); + public bool Z3types = false; + public int Z3lets = 3; // 0 - none, 1 - only LET TERM, 2 - only LET FORMULA, 3 - (default) any + invariant 0 <= Z3lets && Z3lets < 4; + + // Maximum amount of virtual memory (in bytes) for the prover to use + // + // Non-positive number indicates unbounded. + public long MaxProverMemory = 100 * Megabyte; + + // Minimum number of prover calls before restart + public int MinNumOfProverCalls = 5; + + public enum PlatformType{notSpecified, v1, v11, v2, cli1} + public PlatformType TargetPlatform; + public string TargetPlatformLocation; + public string StandardLibraryLocation; + + // whether procedure inlining is enabled at call sites. + public enum Inlining { None, Assert, Assume, Spec }; + public Inlining ProcedureInlining = Inlining.Assume; + public bool PrintInlined = false; + public bool ExtractLoops = false; + public int LazyInlining = 0; + public int StratifiedInlining = 0; + public int StratifiedInliningOption = 0; + public int RecursionBound = 500; + public string CoverageReporterPath = null; + + public enum TypeEncoding { None, Predicates, Arguments, Monomorphic }; + public TypeEncoding TypeEncodingMethod = TypeEncoding.Predicates; + + public bool Monomorphize = false; + + public bool ReflectAdd = false; + + public int LiveVariableAnalysis = 1; + + // Static constructor + static CommandLineOptions() + { + if (System.Type.GetType("Mono.Runtime") == null) { // MONO + TraceListenerCollection! dbl = Debug.Listeners; + assume dbl.IsPeerConsistent; // hangs off static field +#if WHIDBEY + dbl.Add(new ConsoleTraceListener()); +#else + dpl.Add(new DefaultTraceListener()); +#endif + } + } + + private string methodToLog = null; + private string methodToBreakOn = null; + + [Rep] private List procsToCheck = null; // null means "no restriction" + [Rep] private List methodsToTranslateSubstring = null; // null means "no restriction" + [Rep] private List methodsToTranslateMethod = null; // null means "no restriction" + [Rep] private List methodsToTranslateMethodQualified = null; // null means "no restriction" + [Rep] private List methodsToTranslateClass = null; // null means "no restriction" + [Rep] private List methodsToTranslateClassQualified = null; // null means "no restriction" + [Rep] private List methodsToTranslateFile = null; // null means "no restriction" + [Rep] private List! methodsToTranslateExclude = new List(); + + public class AiFlags + { + public bool Intervals = false; + public bool Constant = false; + public bool DynamicType = false; + public bool Nullness = false; + public bool Polyhedra = false; + public bool DebugStatistics = false; + + public bool AnySet + { + get + { + return Intervals + || Constant + || DynamicType + || Nullness + || Polyhedra; + } + } + } + public AiFlags! Ai = new AiFlags(); + + public class HoudiniFlags { + public bool continueAtError = false; + public bool incremental = false; + } + + public HoudiniFlags! houdiniFlags = new HoudiniFlags(); + + [Verify(false)] + public CommandLineOptions() { + // this is just to suppress verification. + } + + + /// + /// Parses the command-line arguments "args" into the global flag variables. The possible result + /// values are: + /// -2: an error occurred and help was requested + /// -1: no error occurred and help was requested + /// 1: no error occurred and help was not requested + /// 2: an error occurred and help was not requested + /// + /// Consumed ("captured" and possibly modified) by the method. + [Verify(false)] + public int Parse([Captured] string[]! args) + requires forall{int i in (0:args.Length); args[i] != null}; + ensures TheProverFactory != null; + ensures vcVariety != VCVariety.Unspecified; + ensures -2 <= result && result <= 2 && result != 0; + { + // save the command line options for the log files + Environment += "Command Line Options:"; + foreach (string s in args) + Environment += " " + s; + args = (string[]!)args.Clone(); // the operations performed may mutate the array, so make a copy + CommandLineParseState! ps = new CommandLineParseState(args); + int res = 1; // the result value + + while (ps.i < args.Length) + invariant ps.IsPeerConsistent; + invariant ps.args == args; + { + ps.s = args[ps.i]; + assert ps.s != null; + ps.s = ps.s.Trim(); + + int colonIndex = ps.s.IndexOf(':'); + if (colonIndex >= 0 && (ps.s.StartsWith("-") || ps.s.StartsWith("/"))) + { + ps.hasColonArgument = true; + args[ps.i] = ps.s.Substring(colonIndex+1); + ps.s = ps.s.Substring(0, colonIndex); + } + else + { + expose(ps) { + ps.i++; + } + ps.hasColonArgument = false; + } + ps.nextIndex = ps.i; + + + switch (ps.s) + { + case "-help": + case "/help": + case "-?": + case "/?": + if (ps.ConfirmArgumentCount(0)) { + res = -1; // help requested + } + break; + + case "-attrHelp": + case "/attrHelp": + if (ps.ConfirmArgumentCount(0)) { + AttrHelpRequested = true; + } + break; + + case "-infer": + case "/infer": + if (ps.ConfirmArgumentCount(1)) { + foreach (char c in (!)args[ps.i]) + { + switch (c) + { + case 'i': Ai.Intervals = true; UseAbstractInterpretation = true; break; + case 'c': Ai.Constant = true; UseAbstractInterpretation = true; break; + case 'd': Ai.DynamicType = true; UseAbstractInterpretation = true; break; + case 'n': Ai.Nullness = true; UseAbstractInterpretation = true; break; + case 'p': Ai.Polyhedra = true; UseAbstractInterpretation = true; break; + case 's': Ai.DebugStatistics = true; UseAbstractInterpretation = true; break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + StepsBeforeWidening = (int) char.GetNumericValue(c); break; + default: + ps.Error("Invalid argument '{0}' to option {1}", c.ToString(), ps.s); + break; + } + } + } + break; + + case "-noinfer": + case "/noinfer": + if (ps.ConfirmArgumentCount(0)) { + UseAbstractInterpretation = false; + } + break; + + case "-log": + case "/log": + if (ps.hasColonArgument) { + methodToLog = args[ps.i]; + ps.nextIndex = ps.i + 1; + } else { + methodToLog = "*"; + } + break; + + case "-logInfer": + case "/logInfer": + Microsoft.AbstractInterpretationFramework.Lattice.LogSwitch = true; + break; + + case "-break": + case "/break": + if (ps.hasColonArgument) + { + methodToBreakOn = args[ps.i]; + ps.nextIndex = ps.i + 1; + } + else + { + System.Diagnostics.Debugger.Break(); + } + break; + + case "-launch": + case "/launch": + System.Diagnostics.Debugger.Launch(); + break; + + case "-proc": + case "/proc": + if (procsToCheck == null) { + procsToCheck = new List(); + } + if (ps.ConfirmArgumentCount(1)) { + procsToCheck.Add((!)args[ps.i]); + } + break; + + case "-translate": + case "/translate": + if (methodsToTranslateSubstring == null) { + methodsToTranslateSubstring = new List(); + } + if (ps.ConfirmArgumentCount(1)) { + methodsToTranslateSubstring.Add((!)args[ps.i]); + } + break; + + case "-trMethod": + case "/trMethod": + if (ps.ConfirmArgumentCount(1)) { + string m = (!)args[ps.i]; + if (0 <= m.IndexOf('.')) { + if (methodsToTranslateMethodQualified == null) { + methodsToTranslateMethodQualified = new List(); + } + methodsToTranslateMethodQualified.Add(m); + } else { + if (methodsToTranslateMethod == null) { + methodsToTranslateMethod = new List(); + } + methodsToTranslateMethod.Add(m); + } + } + break; + + case "-trClass": + case "/trClass": + if (ps.ConfirmArgumentCount(1)) { + string m = (!)args[ps.i]; + if (0 <= m.IndexOf('.')) { + if (methodsToTranslateClassQualified == null) { + methodsToTranslateClassQualified = new List(); + } + methodsToTranslateClassQualified.Add(m); + } else { + if (methodsToTranslateClass == null) { + methodsToTranslateClass = new List(); + } + methodsToTranslateClass.Add(m); + } + } + break; + + case "-trFile": + case "/trFile": + if (methodsToTranslateFile == null) { + methodsToTranslateFile = new List(); + } + if (ps.ConfirmArgumentCount(1)) { + methodsToTranslateFile.Add((!)args[ps.i]); + } + break; + + case "-trExclude": + case "/trExclude": + if (ps.ConfirmArgumentCount(1)) { + methodsToTranslateExclude.Add((!)args[ps.i]); + } + break; + + case "-xml": + case "/xml": + if (ps.ConfirmArgumentCount(1)) + { + XmlSinkFilename = args[ps.i]; + } + break; + + case "-print": + case "/print": + if (ps.ConfirmArgumentCount(1)) + { + PrintFile = args[ps.i]; + } + break; + + case "-dprint": + case "/dprint": + if (ps.ConfirmArgumentCount(1)) + { + DafnyPrintFile = args[ps.i]; + } + break; + + case "-compile": + case "/compile": { + int compile = 0; + if (ps.GetNumericArgument(ref compile, 2)) { + Compile = compile == 1; + } + break; + } + + case "-contracts": + case "/contracts": + case "-c": + case "/c": + if (ps.ConfirmArgumentCount(1)) + { + ContractAssemblies.Add((!)args[ps.i]); + } + break; + + case "-proverLog": + case "/proverLog": + if (ps.ConfirmArgumentCount(1)) + { + SimplifyLogFilePath = args[ps.i]; + } + break; + + case "-logPrefix": + case "/logPrefix": + if (ps.ConfirmArgumentCount(1)) + { + string s = (!)args[ps.i]; + LogPrefix += s.Replace('/', '-').Replace('\\', '-'); + } + break; + + case "-proverShutdownLimit": + case "/proverShutdownLimit": + ps.GetNumericArgument(ref ProverShutdownLimit); + break; + + case "-smtOutput": + case "/smtOutput": + if (ps.ConfirmArgumentCount(1)) + { + SMTLibOutputPath = args[ps.i]; + } + break; + + case "-errorTrace": + case "/errorTrace": + ps.GetNumericArgument(ref ErrorTrace, 3); + break; + + case "-level": + case "/level": + ps.GetNumericArgument(ref CheckingLevel, 3); + break; + + case "-methodology": + case "/methodology": + if (ps.ConfirmArgumentCount(1)) + { + switch (args[ps.i]) + { + case "b": + case "Boogie": + case "boogie": + MethodologySelection = Methodology.Boogie; + break; + case "vs": + case "visible-state": + MethodologySelection = Methodology.VisibleState; + break; + default: + ps.Error("Invalid argument \"{0}\" to option {1}", args[ps.i], ps.s); + break; + } + } + break; + + case "-proverWarnings": + case "/proverWarnings": { + int pw = 0; + if (ps.GetNumericArgument(ref pw, 3)) { + switch (pw) { + case 0: + PrintProverWarnings = ProverWarnings.None; + break; + case 1: + PrintProverWarnings = ProverWarnings.Stdout; + break; + case 2: + PrintProverWarnings = ProverWarnings.Stderr; + break; + default: + assert false; // postcondition of GetNumericArgument guarantees that we don't get here + } + } + break; + } + + case "-env": + case "/env": { + int e = 0; + if (ps.GetNumericArgument(ref e, 3)) { + switch (e) { + case 0: + ShowEnv = ShowEnvironment.Never; + break; + case 1: + ShowEnv = ShowEnvironment.DuringPrint; + break; + case 2: + ShowEnv = ShowEnvironment.Always; + break; + default: + assert false; // postcondition of GetNumericArgument guarantees that we don't get here + } + } + break; + } + + case "-loopUnroll": + case "/loopUnroll": + ps.GetNumericArgument(ref LoopUnrollCount); + break; + + case "-modifiesOnLoop": + case "/modifiesOnLoop": + ps.GetNumericArgument(ref LoopFrameConditions, 3); + break; + + case "-modifiesDefault": + case "/modifiesDefault": + ps.GetNumericArgument(ref ModifiesDefault, 7); + break; + + case "-localModifiesChecks": + case "/localModifiesChecks": + { + int localChecks = 0; + ps.GetNumericArgument(ref localChecks, 2); + LocalModifiesChecks = (localChecks != 0); + } + break; + + case "-orderStrength": + case "/orderStrength": + ps.GetNumericArgument(ref OrderStrength, 2); + break; + + case "-summationStrength": + case "/summationStrength": + ps.GetNumericArgument(ref SummationAxiomStrength, 2); + break; + + case "-inductiveMinMax": + case "/inductiveMinMax": + ps.GetNumericArgument(ref InductiveMinMax, 6); + break; + + case "-fcoStrength": + case "/fcoStrength": + ps.GetNumericArgument(ref FCOStrength, 6); + break; + + case "-ownerModelEncoding": + case "/ownerModelEncoding": + if (ps.ConfirmArgumentCount(1)) + { + switch (args[ps.i]) + { + case "s": + case "standard": + OwnershipModelEncoding = OwnershipModelOption.Standard; + break; + case "e": + case "experimental": + OwnershipModelEncoding = OwnershipModelOption.Experimental; + break; + case "t": + case "trivial": + OwnershipModelEncoding = OwnershipModelOption.Trivial; + break; + default: + ps.Error("Invalid argument \"{0}\" to option {1}", args[ps.i], ps.s); + break; + } + } + break; + + case "-printModel": + case "/printModel": + if (ps.ConfirmArgumentCount(1)) + { + switch (args[ps.i]) + { + case "0": + PrintErrorModel = 0; + break; + case "1": + PrintErrorModel = 1; + break; + case "2": + PrintErrorModel = 2; + break; + case "4": + PrintErrorModel = 4; + break; + default: + ps.Error("Invalid argument \"{0}\" to option {1}", args[ps.i], ps.s); + break; + } + } + break; + + + case "-cev": + case "/cev": + if (ps.ConfirmArgumentCount(1)) + { + PrintErrorModelFile = args[ps.i]; + } + CEVPrint = true; + break; + + case "-printModelToFile": + case "/printModelToFile": + if (ps.ConfirmArgumentCount(1)) + { + PrintErrorModelFile = args[ps.i]; + } + break; + + + case "-enhancedErrorMessages": + case "/enhancedErrorMessages": + ps.GetNumericArgument(ref EnhancedErrorMessages, 2); + break; + + case "-forceBplErrors": + case "/forceBplErrors": + ForceBplErrors = true; + break; + + case "-bv": + case "/bv": + if (ps.ConfirmArgumentCount(1)) + { + if (TheProverFactory == null) { + TheProverFactory = ProverFactory.Load("Z3"); + ProverName = "Z3".ToUpper(); + } + + switch (args[ps.i]) + { + case "n": + Bitvectors = BvHandling.None; + break; + case "z": + Bitvectors = BvHandling.Z3Native; + break; + case "i": + Bitvectors = BvHandling.ToInt; + break; + default: + ps.Error("Invalid argument \"{0}\" to option {1}", args[ps.i], ps.s); + break; + } + } + break; + + case "-contractInfer": + case "/contractInfer": + ContractInfer = true; + TheProverFactory = ProverFactory.Load("ContractInference"); + ProverName = "ContractInference".ToUpper(); + break; + + case "-subsumption": + case "/subsumption": { + int s = 0; + if (ps.GetNumericArgument(ref s, 3)) { + switch (s) { + case 0: + UseSubsumption = SubsumptionOption.Never; + break; + case 1: + UseSubsumption = SubsumptionOption.NotForQuantifiers; + break; + case 2: + UseSubsumption = SubsumptionOption.Always; + break; + default: + assert false; // postcondition of GetNumericArgument guarantees that we don't get here + } + } + break; + } + + case "-liveVariableAnalysis": + case "/liveVariableAnalysis": { + int lva = 0; + if (ps.GetNumericArgument(ref lva, 3)) { + LiveVariableAnalysis = lva; + } + break; + } + + case "-removeEmptyBlocks": + case "/removeEmptyBlocks": { + int reb = 0; + if (ps.GetNumericArgument(ref reb, 2)) { + RemoveEmptyBlocks = reb == 1; + } + break; + } + + case "-coalesceBlocks": + case "/coalesceBlocks": { + int cb = 0; + if (ps.GetNumericArgument(ref cb, 2)) { + CoalesceBlocks = cb == 1; + } + break; + } + + case "/DoomDebug": + vcVariety = VCVariety.Doomed; + useDoomDebug = true; + break; + + case "-vc": + case "/vc": + if (ps.ConfirmArgumentCount(1)) { + switch (args[ps.i]) + { + case "s": + case "structured": + vcVariety = VCVariety.Structured; + break; + case "b": + case "block": + vcVariety = VCVariety.Block; + break; + case "l": + case "local": + vcVariety = VCVariety.Local; + break; + case "n": + case "nested": + vcVariety = VCVariety.BlockNested; + break; + case "m": + vcVariety = VCVariety.BlockNestedReach; + break; + case "r": + vcVariety = VCVariety.BlockReach; + break; + case "d": + case "dag": + vcVariety = VCVariety.Dag; + break; + case "doomed": + vcVariety = VCVariety.Doomed; + break; + default: + ps.Error("Invalid argument \"{0}\" to option {1}", args[ps.i], ps.s); + break; + } + } + break; + + case "-prover": + case "/prover": + if (ps.ConfirmArgumentCount(1)) { + TheProverFactory = ProverFactory.Load((!)args[ps.i]); + ProverName = ((!)args[ps.i]).ToUpper(); + } + break; + + case "-proverOpt": + case "/proverOpt": + if (ps.ConfirmArgumentCount(1)) { + ProverOptions.Add((!)args[ps.i]); + } + break; + + case "-extractLoops": + case "/extractLoops": + if (ps.ConfirmArgumentCount(0)) { + ExtractLoops = true; + } + break; + case "-inline": + case "/inline": + if (ps.ConfirmArgumentCount(1)) { + switch (args[ps.i]) + { + case "none": + ProcedureInlining = Inlining.None; + break; + case "assert": + ProcedureInlining = Inlining.Assert; + break; + case "assume": + ProcedureInlining = Inlining.Assume; + break; + case "spec": + ProcedureInlining = Inlining.Spec; + break; + default: + ps.Error("Invalid argument \"{0}\" to option {1}", args[ps.i], ps.s); + break; + } + } + break; + case "-lazyInline": + case "/lazyInline": + if (ps.ConfirmArgumentCount(1)) { + switch (args[ps.i]) + { + case "0": + LazyInlining = 0; + break; + case "1": + LazyInlining = 1; + break; + case "2": + LazyInlining = 2; + break; + default: + ps.Error("Invalid argument \"{0}\" to option {1}", args[ps.i], ps.s); + break; + } + } + break; + case "-stratifiedInline": + case "/stratifiedInline": + if (ps.ConfirmArgumentCount(1)) { + switch (args[ps.i]) + { + case "0": + StratifiedInlining = 0; + break; + case "1": + StratifiedInlining = 1; + break; + default: + StratifiedInlining = Int32.Parse((!)args[ps.i]); + //ps.Error("Invalid argument \"{0}\" to option {1}", args[ps.i], ps.s); + break; + } + } + break; + case "-recursionBound": + case "/recursionBound": + if (ps.ConfirmArgumentCount(1)) { + RecursionBound = Int32.Parse((!)args[ps.i]); + } + break; + case "-coverageReporter": + case "/coverageReporter": + if (ps.ConfirmArgumentCount(1)) { + CoverageReporterPath = args[ps.i]; + } + break; + case "-stratifiedInlineOption": + case "/stratifiedInlineOption": + if (ps.ConfirmArgumentCount(1)) { + StratifiedInliningOption = Int32.Parse((!)args[ps.i]); + } + break; + case "-typeEncoding": + case "/typeEncoding": + if (ps.ConfirmArgumentCount(1)) { + switch (args[ps.i]) + { + case "n": + case "none": + TypeEncodingMethod = TypeEncoding.None; + break; + case "p": + case "predicates": + TypeEncodingMethod = TypeEncoding.Predicates; + break; + case "a": + case "arguments": + TypeEncodingMethod = TypeEncoding.Arguments; + break; + case "m": + case "monomorphic": + TypeEncodingMethod = TypeEncoding.Monomorphic; + break; + default: + ps.Error("Invalid argument \"{0}\" to option {1}", args[ps.i], ps.s); + break; + } + } + break; + + case "-instrumentInfer": + case "/instrumentInfer": + if (ps.ConfirmArgumentCount(1)) { + switch (args[ps.i]) + { + case "e": + InstrumentInfer = InstrumentationPlaces.Everywhere; + break; + case "h": + InstrumentInfer = InstrumentationPlaces.LoopHeaders; + break; + default: + ps.Error("Invalid argument \"{0}\" to option {1}", args[ps.i], ps.s); + break; + } + } + break; + + case "-vcBrackets": + case "/vcBrackets": + ps.GetNumericArgument(ref BracketIdsInVC, 2); + break; + + case "-proverMemoryLimit": + case "/proverMemoryLimit": + { + int d = 0; + if (ps.GetNumericArgument(ref d)) { + MaxProverMemory = d * Megabyte; + } + break; + } + + case "-vcsMaxCost": + case "/vcsMaxCost": + ps.GetNumericArgument(ref VcsMaxCost); + break; + + case "-vcsPathJoinMult": + case "/vcsPathJoinMult": + ps.GetNumericArgument(ref VcsPathJoinMult); + break; + + case "-vcsPathCostMult": + case "/vcsPathCostMult": + ps.GetNumericArgument(ref VcsPathCostMult); + break; + + case "-vcsAssumeMult": + case "/vcsAssumeMult": + ps.GetNumericArgument(ref VcsAssumeMult); + break; + + case "-vcsPathSplitMult": + case "/vcsPathSplitMult": + ps.GetNumericArgument(ref VcsPathSplitMult); + break; + + case "-vcsMaxSplits": + case "/vcsMaxSplits": + ps.GetNumericArgument(ref VcsMaxSplits); + break; + + case "-vcsMaxKeepGoingSplits": + case "/vcsMaxKeepGoingSplits": + ps.GetNumericArgument(ref VcsMaxKeepGoingSplits); + break; + + case "-vcsFinalAssertTimeout": + case "/vcsFinalAssertTimeout": + ps.GetNumericArgument(ref VcsFinalAssertTimeout); + break; + + case "-vcsKeepGoingTimeout": + case "/vcsKeepGoingTimeout": + ps.GetNumericArgument(ref VcsKeepGoingTimeout); + break; + + case "-vcsCores": + case "/vcsCores": + ps.GetNumericArgument(ref VcsCores); + break; + + case "-simplifyMatchDepth": + case "/simplifyMatchDepth": + ps.GetNumericArgument(ref SimplifyProverMatchDepth); + break; + + case "-timeLimit": + case "/timeLimit": + ps.GetNumericArgument(ref ProverKillTime); + break; + + case "-smokeTimeout": + case "/smokeTimeout": + ps.GetNumericArgument(ref SmokeTimeout); + break; + + case "-errorLimit": + case "/errorLimit": + ps.GetNumericArgument(ref ProverCCLimit); + break; + + case "-z3opt": + case "/z3opt": + if (ps.ConfirmArgumentCount(1)) + { + Z3Options.Add((!)args[ps.i]); + } + break; + + case "-z3lets": + case "/z3lets": + ps.GetNumericArgument(ref Z3lets, 4); + break; + + case "-platform": + case "/platform": + if (ps.ConfirmArgumentCount(1)) + { + StringCollection platformOptions = this.ParseNamedArgumentList(args[ps.i]); + if (platformOptions != null && platformOptions.Count > 0){ + try{ + this.TargetPlatform = (PlatformType)(!)Enum.Parse(typeof(PlatformType), (!)platformOptions[0]); + } + catch { + ps.Error("Bad /platform type '{0}'", platformOptions[0]); + break; + } + if (platformOptions.Count > 1){ + this.TargetPlatformLocation = platformOptions[1]; + if (!Directory.Exists(platformOptions[1])) { + ps.Error("/platform directory '{0}' does not exist", platformOptions[1]); + break; + } + } + } + } + break; + + case "-stdlib": + case "/stdlib": + if (ps.ConfirmArgumentCount(1)) + { + this.StandardLibraryLocation = args[ps.i]; + } + break; + + case "-Houdini": + case "/Houdini": + this.houdiniEnabled=true; + if (ps.hasColonArgument) { + if (ps.ConfirmArgumentCount(1)) { + foreach (char c in (!)args[ps.i]) + { + switch (c) + { + case 'c': houdiniFlags.continueAtError = true; break; + case 'i': houdiniFlags.incremental = true; break; + default : ps.Error("Unknown houdini flag: " + c + "\n"); break; + } + } + } + } + break; + + default: + assume true; + bool option = false; + if (ps.CheckBooleanFlag("printUnstructured", ref option)) { + expose(this) { + PrintUnstructured = option ? 1 : 0; + } + } else if ( + ps.CheckBooleanFlag("printDesugared", ref PrintDesugarings) || + ps.CheckBooleanFlag("printInstrumented", ref PrintInstrumented) || + ps.CheckBooleanFlag("printWithUniqueIds", ref PrintWithUniqueASTIds) || + ps.CheckBooleanFlag("wait", ref Wait) || + ps.CheckBooleanFlag("trace", ref Trace) || + ps.CheckBooleanFlag("traceTimes", ref TraceTimes) || + ps.CheckBooleanFlag("noResolve", ref NoResolve) || + ps.CheckBooleanFlag("noTypecheck", ref NoTypecheck) || + ps.CheckBooleanFlag("overlookTypeErrors", ref OverlookBoogieTypeErrors) || + ps.CheckBooleanFlag("noVerify", ref Verify, false) || + ps.CheckBooleanFlag("traceverify", ref TraceVerify) || + ps.CheckBooleanFlag("noConsistencyChecks", ref NoConsistencyChecks, true) || + ps.CheckBooleanFlag("alwaysAssumeFreeLoopInvariants", ref AlwaysAssumeFreeLoopInvariants, true) || + ps.CheckBooleanFlag("nologo", ref DontShowLogo) || + ps.CheckBooleanFlag("noVerifyByDefault", ref NoVerifyByDefault) || + ps.CheckBooleanFlag("useUncheckedContracts", ref UseUncheckedContracts) || + ps.CheckBooleanFlag("proverLogAppend", ref SimplifyLogFileAppend) || + ps.CheckBooleanFlag("checkInfer", ref InstrumentWithAsserts) || + ps.CheckBooleanFlag("interprocInfer", ref IntraproceduralInfer, false) || + ps.CheckBooleanFlag("restartProver", ref RestartProverPerVC) || + ps.CheckBooleanFlag("printInlined", ref PrintInlined) || + ps.CheckBooleanFlag("arithDistributionAxioms", ref UseArithDistributionAxioms) || + ps.CheckBooleanFlag("smoke", ref SoundnessSmokeTest) || + ps.CheckBooleanFlag("vcsDumpSplits", ref VcsDumpSplits) || + ps.CheckBooleanFlag("dbgRefuted", ref DebugRefuted) || + ps.CheckBooleanFlag("causalImplies", ref CausalImplies) || + ps.CheckBooleanFlag("reflectAdd", ref ReflectAdd) || + ps.CheckBooleanFlag("z3types", ref Z3types) || + ps.CheckBooleanFlag("z3multipleErrors", ref z3AtFlag, false) || + ps.CheckBooleanFlag("monomorphize", ref Monomorphize) || + ps.CheckBooleanFlag("useArrayTheory", ref UseArrayTheory) || + ps.CheckBooleanFlag("doModSetAnalysis", ref DoModSetAnalysis) + ) + { + // one of the boolean flags matched + } + else if (ps.s.StartsWith("-") || ps.s.StartsWith("/")) + { + ps.Error("unknown switch: {0}", ps.s); + } + else if (ps.ConfirmArgumentCount(0)) + { + string filename = ps.s; + string extension = Path.GetExtension(filename); + if (extension != null) { + InputFileExtension = extension.ToLower(); + } + Files.Add(filename); + FileName = filename; + } + break; + } + expose(ps) ps.i = ps.nextIndex; + } + + assume true; + if (ps.encounteredErrors) res *= 2; + if (res < 0) { // help requested + Usage(); + } else if (AttrHelpRequested) { + AttributeUsage(); + } else if (ps.encounteredErrors) { + Console.WriteLine("Use /help for available options"); + } + + SetProverOptions(); + + if (Trace) { BoogieDebug.DoPrinting = true; } // reuse the -trace option for debug printing + return res; + } + + private void SetProverOptions() + modifies this.*; + ensures TheProverFactory != null; + ensures vcVariety != VCVariety.Unspecified; + { + // expand macros in filenames, now that LogPrefix is fully determined + ExpandFilename(ref XmlSinkFilename); + ExpandFilename(ref PrintFile); + ExpandFilename(ref DafnyPrintFile); + ExpandFilename(ref SimplifyLogFilePath); + ExpandFilename(ref SMTLibOutputPath); + ExpandFilename(ref PrintErrorModelFile); + + assume XmlSink == null; // XmlSink is to be set here + if (XmlSinkFilename != null) { + XmlSink = new XmlSink(XmlSinkFilename); + } + + if (TheProverFactory == null) { + expose(this) { + TheProverFactory = ProverFactory.Load("Z3"); + ProverName = "Z3".ToUpper(); + } + } + + if (vcVariety == VCVariety.Unspecified) { + vcVariety = TheProverFactory.DefaultVCVariety; + } + + if (LoopFrameConditions == -1) { + // /modifiesOnLoop not specified. Set its default depending on /localModifiesChecks + if (LocalModifiesChecks) { + LoopFrameConditions = 1; + } else { + LoopFrameConditions = 2; + } + } + + if (CEVPrint && PrintErrorModel == 0) { + PrintErrorModel = 1; + } + + switch (InductiveMinMax) { + case 1: case 2: case 4: case 5: + ReflectAdd = true; // these InductiveMinMax modes imply ReflectAdd + break; + default: + break; + } + + if (MethodologySelection == Methodology.VisibleState) { + OwnershipModelEncoding = OwnershipModelOption.Trivial; + } + + if (UseArrayTheory) { + Monomorphize = true; + } + + if (LazyInlining > 0) { + TypeEncodingMethod = TypeEncoding.Monomorphic; + UseArrayTheory = true; + UseAbstractInterpretation = false; + } + + if (StratifiedInlining > 0) { + TypeEncodingMethod = TypeEncoding.Monomorphic; + UseArrayTheory = true; + UseAbstractInterpretation = false; + } + } + + + + public bool UserWantsMethodLogging (string! methodFullName) + { + if (methodToLog == null) { return false; } + return methodToLog == "*" || methodFullName.IndexOf(methodToLog) >= 0; + } + + public bool UserWantsToBreak (string! methodFullName) + { + if (methodToBreakOn == null) { return false; } + return methodFullName.IndexOf(methodToBreakOn) >= 0; + } + + public bool UserWantsToCheckRoutine(string! methodFullname) + { + if (procsToCheck == null) { + // no preference + return true; + } + return exists{string s in procsToCheck; 0 <= methodFullname.IndexOf(s)}; + } + + public bool UserWantsToTranslateRoutine(Cci.Method! method, string! methodFullname) { + return UserWantsToTranslateRoutineInclude(method, methodFullname) && + !exists{string s in methodsToTranslateExclude; 0 <= methodFullname.IndexOf(s)}; + } + + public bool UserWantsToTranslateRoutineInclude(Cci.Method! method, string! methodFullname) + { + if (methodsToTranslateSubstring == null && + methodsToTranslateClass == null && + methodsToTranslateClassQualified == null && + methodsToTranslateFile == null && + methodsToTranslateMethod == null && + methodsToTranslateMethodQualified == null) { + // no preference + return true; + } + if (methodsToTranslateSubstring != null) { + if (exists{string s in methodsToTranslateSubstring; 0 <= methodFullname.IndexOf(s)}) { + return true; + } + } + if (methodsToTranslateMethod != null) { + string methodName = method.Name.Name; + assert methodsToTranslateMethod != null; + if (methodsToTranslateMethod.Contains(methodName)) { + return true; + } + } + if (methodsToTranslateMethodQualified != null && method.DeclaringType != null) { + string methodName = method.DeclaringType.Name.Name + "." + method.Name.Name; + assert methodsToTranslateMethodQualified != null; + if (methodsToTranslateMethodQualified.Contains(methodName)) { + return true; + } + } + if (method.DeclaringType != null) { + if (methodsToTranslateClass != null) { + string className = method.DeclaringType.Name.Name; + if (methodsToTranslateClass.Contains(className)) { + return true; + } + } + if (methodsToTranslateClassQualified != null) { + string className = method.DeclaringType.FullName; + if (className != null) { + className = className.Replace('+', '.'); + if (methodsToTranslateClassQualified.Contains(className)) { + return true; + } + } + } + } + if (methodsToTranslateFile != null) { + string methodFilename = GetSourceDocument(method); + if (methodFilename != null) { + string path = methodFilename; + if (path != null) { + string filename = Path.GetFileName(path); + if (methodsToTranslateFile.Contains(filename)) { + return true; + } + } + } + } + // the method is not among the desired routines + return false; + } + + /// + /// Returns the file containing "method". Returns null f that information is not available. + /// + static string GetSourceDocument(Cci.Method! method) { + // Start by looking for a source context in the method itself. However, if the program + // was read from a binary, then there is no source location for the method. If so, there + // some other ways we might find a source location. + if (method.SourceContext.Document != null) { + return method.SourceContext.Document.Name; + } + // Try to find a source location in the method's contract + if (method.Contract != null) { + if (method.Contract.Requires != null) { + foreach (Cci.Requires c in method.Contract.Requires) { + if (c != null && c.SourceContext.Document != null) { + return c.SourceContext.Document.Name; + } + } + } + if (method.Contract.Modifies != null) { + foreach (Cci.Expression c in method.Contract.Modifies) { + if (c != null && c.SourceContext.Document != null) { + return c.SourceContext.Document.Name; + } + } + } + if (method.Contract.Ensures != null) { + foreach (Cci.Ensures c in method.Contract.Ensures) { + if (c != null && c.SourceContext.Document != null) { + return c.SourceContext.Document.Name; + } + } + } + } + // As a last attempt, look for a source location among the statements + if (method.Body != null) { + return GetSourceDocumentFromStatements(method.Body.Statements); + } + return null; // no source location found + } + + [Pure] static string GetSourceDocumentFromStatements(Cci.StatementList list) { + if (list != null) { + foreach (Cci.Statement c in list) { + if (c != null && c.SourceContext.Document != null) { + return c.SourceContext.Document.Name; + } + if (c is Cci.Block) { + Cci.Block b = (Cci.Block)c; + string n = GetSourceDocumentFromStatements(b.Statements); + if (n != null) { + return n; + } + } + } + } + return null; + } + + class CommandLineParseState + { + public string s; + public bool hasColonArgument; + public readonly string[]! args; + public int i; + public int nextIndex; + public bool encounteredErrors; + + invariant 0 <= i && i <= args.Length; + invariant 0 <= nextIndex && nextIndex <= args.Length; + + public CommandLineParseState(string[]! args) + requires forall{int i in (0:args.Length); args[i] != null}; + ensures this.args == args; + { + this.s = null; // set later by client + this.hasColonArgument = false; // set later by client + this.args = args; + this.i = 0; + this.nextIndex = 0; // set later by client + this.encounteredErrors = false; + } + + public bool CheckBooleanFlag(string! flagName, ref bool flag, bool valueWhenPresent) + modifies nextIndex, encounteredErrors, Console.Error.*; + { + bool flagPresent = false; + + if ((s == "/"+flagName || s == "-"+flagName) && ConfirmArgumentCount(0)) + { + flag = valueWhenPresent; + flagPresent = true; + } + return flagPresent; + } + + public bool CheckBooleanFlag(string! flagName, ref bool flag) + modifies nextIndex, encounteredErrors, Console.Error.*; + { + return CheckBooleanFlag(flagName, ref flag, true); + } + + /// + /// If there is one argument and it is a non-negative integer, then set "arg" to that number and return "true". + /// Otherwise, emit error message, leave "arg" unchanged, and return "false". + /// + public bool GetNumericArgument(ref int arg) + modifies nextIndex, encounteredErrors, Console.Error.*; + { + if (this.ConfirmArgumentCount(1)) + { + try { + assume args[i] != null; + assert args[i] is string; // needed to prove args[i].IsPeerConsistent + int d = Convert.ToInt32(this.args[this.i]); + if (0 <= d) { + arg = d; + return true; + } + } catch (System.FormatException) { + } catch (System.OverflowException) { + } + } else { + return false; + } + Error("Invalid argument \"{0}\" to option {1}", args[this.i], this.s); + return false; + } + + /// + /// If there is one argument and it is a non-negative integer less than "limit", + /// then set "arg" to that number and return "true". + /// Otherwise, emit error message, leave "arg" unchanged, and return "false". + /// + public bool GetNumericArgument(ref int arg, int limit) + requires this.i < args.Length; + modifies nextIndex, encounteredErrors, Console.Error.*; + { + int a = arg; + if (!GetNumericArgument(ref a)) { + return false; + } else if (a < limit) { + arg = a; + return true; + } else { + Error("Invalid argument \"{0}\" to option {1}", args[this.i], this.s); + return false; + } + } + + /// + /// If there is one argument and it is a non-negative real, then set "arg" to that number and return "true". + /// Otherwise, emit an error message, leave "arg" unchanged, and return "false". + /// + public bool GetNumericArgument(ref double arg) + modifies nextIndex, encounteredErrors, Console.Error.*; + { + if (this.ConfirmArgumentCount(1)) + { + try { + assume args[i] != null; + assert args[i] is string; // needed to prove args[i].IsPeerConsistent + double d = Convert.ToDouble(this.args[this.i]); + if (0 <= d) { + arg = d; + return true; + } + } catch (System.FormatException) { + } catch (System.OverflowException) { + } + } else { + return false; + } + Error("Invalid argument \"{0}\" to option {1}", args[this.i], this.s); + return false; + } + + public bool ConfirmArgumentCount(int argCount) + requires 0 <= argCount; + modifies nextIndex, encounteredErrors, Console.Error.*; + ensures result == ( !(hasColonArgument && argCount != 1) && !(args.Length < i + argCount) ); + { + if (hasColonArgument && argCount != 1) + { + Error("\"{0}\" cannot take a colon argument", s); + nextIndex = args.Length; + return false; + } + else if (args.Length < i + argCount) + { + Error("\"{0}\" expects {1} argument{2}", s, argCount.ToString(), (string)(argCount == 1 ? "" : "s")); + nextIndex = args.Length; + return false; + } + else + { + nextIndex = i + argCount; + return true; + } + } + + public void Error(string! message, params string[]! args) + modifies encounteredErrors, Console.Error.*; + { + Console.Error.WriteLine("Boogie: Error: " + String.Format(message, args)); + encounteredErrors = true; + } + } + + public virtual StringCollection ParseNamedArgumentList(string argList){ + if (argList == null || argList.Length == 0) return null; + StringCollection result = new StringCollection(); + int i = 0; + for (int n = argList.Length; i < n;) + invariant 0 <= i; + { + int separatorIndex = this.GetArgumentSeparatorIndex(argList, i); + if (separatorIndex > i){ + result.Add(argList.Substring(i, separatorIndex-i)); + i = separatorIndex+1; + continue; + } + result.Add(argList.Substring(i)); + break; + } + return result; + } + public int GetArgumentSeparatorIndex(string! argList, int startIndex) + requires 0 <= startIndex && startIndex <= argList.Length; + ensures result < argList.Length; + { + int commaIndex = argList.IndexOf(",", startIndex); + int semicolonIndex = argList.IndexOf(";", startIndex); + if (commaIndex == -1) return semicolonIndex; + if (semicolonIndex == -1) return commaIndex; + if (commaIndex < semicolonIndex) return commaIndex; + return semicolonIndex; + } + + public static void AttributeUsage() + { + Console.WriteLine( +@"Boogie: The following attributes are supported by this implementation. + + ---- On axioms ------------------------------------------------------------- + + {:inline true} + Works on axiom of the form: + (forall VARS :: f(VARS) = expr(VARS)) + Makes Boogie replace f(VARS) with expr(VARS) everywhere just before + doing VC generation. + + {:ignore ""p,q..""} + Exclude the axiom when generating VC for a prover supporting any + of the features 'p', 'q', ... + All the provers support the feature 'all'. + Simplify supports 'simplify' and 'simplifyLike'. + Z3 supports 'z3', 'simplifyLike' and either 'bvInt' (if the /bv:i + option is passed) or 'bvDefSem' (for /bv:z). + + ---- On implementations and procedures ------------------------------------- + + {:inline N} + Inline given procedure (can be also used on implementation). + N should be a non-negative number and represents the inlining depth. + With /inline:assume call is replaced with ""assume false"" once inlining depth is reached. + With /inline:assert call is replaced with ""assert false"" once inlining depth is reached. + With /inline:spec call is left as is once inlining depth is reached. + With the above three options, methods with the attribute {:inline N} are not verified. + With /inline:none the entire attribute is ignored. + + {:verify false} + Skip verification of an implementation. + + {:forceBvZ3Native true} + Verify the given implementation with the native Z3 bitvector + handling. Only works if /bv:i (or /bv:z, but then it does not do + anything) is given on the command line. + + {:forceBvInt true} + Use int translation for the given implementation. Only work with + /bv:z (or /bv:i). + + {:vcs_max_cost N} + {:vcs_max_splits N} + {:vcs_max_keep_going_splits N} + Per-implementation versions of + /vcsMaxCost, /vcsMaxSplits and /vcsMaxKeepGoingSplits. + + ---- On functions ---------------------------------------------------------- + + {:bvbuiltin ""spec""} + Z3 specific, used only with /bv:z. + + {:bvint ""fn""} + With /bv:i rewrite the function to function symbol 'fn', except if + the 'fn' is 'id', in which case just strip the function altogether. + + {:never_pattern true} + Terms starting with this function symbol will never be + automatically selected as patterns. It does not prevent them + from being used inside the triggers, and does not affect explicit + trigger annotations. Internally it works by adding {:nopats ...} + annotations to quantifiers. + + ---- On variables ---------------------------------------------------------- + + {:existential true} + Marks a global Boolean variable as existentially quantified. If + used in combination with option /contractInfer Boogie will check + whether there exists a Boolean assignment to the existentials + that makes all verification conditions valid. Without option + /contractInfer the attribute is ignored. + + ---- On assert statements -------------------------------------------------- + + {:subsumption n} + Overrides the /subsumption command-line setting for this assertion. + + ---- The end --------------------------------------------------------------- +"); + } + + private static bool printedHelp = false; + + public static void Usage() + { + // Ensure that we only print the help message once, + // no matter how many enabling conditions for printing + // help were triggered. + if (printedHelp) { return; } + printedHelp = true; + + Console.WriteLine(@"Boogie: usage: Boogie [ option ... ] [ filename ... ] + where