diff options
author | mikebarnett <unknown> | 2009-07-15 21:03:41 +0000 |
---|---|---|
committer | mikebarnett <unknown> | 2009-07-15 21:03:41 +0000 |
commit | ce1c2de044c91624370411e23acab13b0381949b (patch) | |
tree | 592539996fe08050ead5ee210c973801611dde40 /Source/Core |
Initial set of files.
Diffstat (limited to 'Source/Core')
26 files changed, 23371 insertions, 0 deletions
diff --git a/Source/Core/Absy.ssc b/Source/Core/Absy.ssc new file mode 100644 index 00000000..fd54412c --- /dev/null +++ b/Source/Core/Absy.ssc @@ -0,0 +1,3073 @@ +//-----------------------------------------------------------------------------
+//
+// 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 Implementation! Impl;
+ public Block! Block;
+ public int Statement; // invariant: Block[Statement] is CallCmd
+ public AI.Lattice.Element! KnownBeforeCall;
+
+ public CallSite (Implementation! impl, Block! b, int stmt, AI.Lattice.Element! e)
+ {
+ this.Impl = impl;
+ this.Block = b;
+ this.Statement = stmt;
+ this.KnownBeforeCall = e;
+ }
+ }
+
+ public class ProcedureSummaryEntry
+ {
+ public AI.Lattice! Lattice;
+ public AI.Lattice.Element! OnEntry;
+ public AI.Lattice.Element! OnExit;
+ public CCI.IMutableSet/*<CallSite>*/! 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/*<ProcedureSummaryEntry>*/
+ {
+ 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;
+
+
+ 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);
+
+ /// <summary>
+ /// Requires the object to have been successfully resolved.
+ /// </summary>
+ /// <param name="tc"></param>
+ public abstract void Typecheck (TypecheckingContext! tc);
+ /// <summary>
+ /// Intorduced this so the uniqueId is not the same on a cloned object.
+ /// </summary>
+ /// <param name="tc"></param>
+ 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<Declaration!>! TopLevelDeclarations;
+
+ public Program()
+ : base(Token.NoToken)
+ {
+ this.TopLevelDeclarations = new List<Declaration!>();
+ // base(Token.NoToken);
+ }
+
+ public void Emit (TokenTextWriter! stream)
+ {
+ stream.SetToken(this);
+ Emitter.Declarations(this.TopLevelDeclarations, stream);
+ }
+ /// <summary>
+ /// Returns the number of name resolution errors.
+ /// </summary>
+ /// <returns></returns>
+ 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<Declaration!> prunedTopLevelDecls = CommandLineOptions.Clo.OverlookBoogieTypeErrors ? new List<Declaration!>() : 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<TypeSynonymDecl!>! synonymDecls = new List<TypeSynonymDecl!> ();
+ 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();
+
+ if (CommandLineOptions.Clo.ProcedureInlining != CommandLineOptions.Inlining.None) {
+ bool inline = false;
+ foreach (Declaration d in TopLevelDeclarations) {
+ if (d.FindExprAttribute("inline") != null) inline = true;
+ }
+ if (inline) {
+ foreach (Declaration d in TopLevelDeclarations) {
+ Implementation impl = d as Implementation;
+ if (impl != null) {
+ impl.OriginalBlocks = impl.Blocks;
+ impl.OriginalLocVars = impl.LocVars;
+ }
+ }
+ foreach (Declaration d in TopLevelDeclarations) {
+ Implementation impl = d as Implementation;
+ if (impl != null) {
+ Inliner.ProcessImplementation(tc, this, impl);
+ }
+ }
+ }
+ }
+ }
+
+ public void ComputeStronglyConnectedComponents()
+ {
+ foreach(Declaration d in this.TopLevelDeclarations) {
+ d.ComputeStronglyConnectedComponents();
+ }
+ }
+
+ public void InstrumentWithInvariants ()
+ {
+ foreach (Declaration d in this.TopLevelDeclarations) {
+ d.InstrumentWithInvariants();
+ }
+ }
+
+ /// <summary>
+ /// Instrument the "widen" blocks with a statement in the form of "assume J", where J is a loop predicate "variable"
+ /// </summary>
+ public void InstrumentWithLoopInvariantPredicates()
+ {
+ foreach (Declaration d in this.TopLevelDeclarations) {
+ d.InstrumentWithLoopInvariantPredicates();
+ }
+ }
+
+ /// <summary>
+ /// Reset the abstract stated computed before
+ /// </summary>
+ 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);
+ }
+ }
+ }
+ }
+
+
+ public override Absy! StdDispatch(StandardVisitor! visitor)
+ {
+ return visitor.VisitProgram(this);
+ }
+ }
+
+ //---------------------------------------------------------------------
+ // 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 abstract void Emit(TokenTextWriter! stream, int level);
+ public abstract void Register(ResolutionContext! rc);
+
+ /// <summary>
+ /// Compute the strongly connected components of the declaration.
+ /// By default, it does nothing
+ /// </summary>
+ public virtual void ComputeStronglyConnectedComponents() { /* Does nothing */}
+
+ /// <summary>
+ /// This method inserts the abstract-interpretation-inferred invariants
+ /// as assume (or possibly assert) statements in the statement sequences of
+ /// each block.
+ /// </summary>
+ public virtual void InstrumentWithInvariants () {}
+
+ /// <summary>
+ /// Insert a statement "assume J", for an opportune J - loop invariant predicate - in the "widen" blocks
+ /// </summary>
+ public virtual void InstrumentWithLoopInvariantPredicates() { /* by default it does nothing */ }
+
+ /// <summary>
+ /// Reset the abstract stated computed before
+ /// </summary>
+ 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][Reads(ReadsAttribute.Reads.Owned)]
+ 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<TypeSynonymDecl!>! synonymDecls,
+ ResolutionContext! rc) {
+ // then discover all dependencies between type synonyms
+ IDictionary<TypeSynonymDecl!, List<TypeSynonymDecl!>!>! deps =
+ new Dictionary<TypeSynonymDecl!, List<TypeSynonymDecl!>!> ();
+ foreach (TypeSynonymDecl! decl in synonymDecls) {
+ List<TypeSynonymDecl!>! declDeps = new List<TypeSynonymDecl!> ();
+ FindDependencies(decl.Body, declDeps, rc);
+ deps.Add(decl, declDeps);
+ }
+
+ List<TypeSynonymDecl!>! resolved = new List<TypeSynonymDecl!> ();
+
+ 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<TypeSynonymDecl!>! 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][Reads(ReadsAttribute.Reads.Owned)]
+ 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<ConstantParent!> 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<ConstantParent!> 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);
+ }
+
+ /// <summary>
+ /// 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.
+ /// </summary>
+ 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);
+ }
+ }
+
+ // A generic variable. It is used for the LoopInvariantsOnDemand
+ public class SimpleVariable : Variable
+ {
+ public SimpleVariable(IToken! tok, TypedIdent! typedIdent)
+ {
+ 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 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)
+ {
+ Type.EmitOptionalTypeParams(stream, TypeParameters);
+ stream.Write("(");
+ InParams.Emit(stream);
+ stream.Write(")");
+
+ 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);
+ }
+
+ /// <summary>
+ /// 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.
+ /// </summary>
+ /// <param name="rc"></param>
+ protected void RegisterFormals(VariableSeq! formals, ResolutionContext! rc)
+ {
+ foreach (Formal! f in formals)
+ {
+ if (f.Name != TypedIdent.NoName)
+ {
+ rc.AddVariable(f, false);
+ }
+ f.Resolve(rc);
+ }
+ }
+
+ /// <summary>
+ /// Resolves the where clauses (and attributes) of the formals.
+ /// </summary>
+ /// <param name="rc"></param>
+ 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<Expansion!>? 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);
+ 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 static bool ShowSummaries = false;
+
+ 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);
+ 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 (ShowSummaries)
+ {
+ 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<Block!>! Blocks;
+ public Procedure Proc;
+
+ // Blocks before applying passification etc.
+ // Both are used only when /inline is set.
+ public List<Block!>? OriginalBlocks;
+ public VariableSeq? OriginalLocVars;
+
+ // Strongly connected components
+ private StronglyConnectedComponents<Block!> 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.MacroLike) {
+ 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);
+ }
+
+ public Implementation(IToken! tok, string! name, TypeVariableSeq! typeParams,
+ VariableSeq! inParams, VariableSeq! outParams,
+ VariableSeq! localVariables, [Captured] StmtList! structuredStmts, QKeyValue kv)
+ : base(tok, name, typeParams, inParams, outParams)
+ {
+ LocVars = localVariables;
+ StructuredStmts = structuredStmts;
+ BigBlocksResolutionContext ctx = new BigBlocksResolutionContext(structuredStmts);
+ 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!>! 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<Block!>! 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);
+ 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.StartProcedureContext();
+ 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.EndProcedureContext();
+ 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<TypeVariable!, Type!>! subst1 =
+ new Dictionary<TypeVariable!, Type!> ();
+ IDictionary<TypeVariable!, Type!>! subst2 =
+ new Dictionary<TypeVariable!, Type!> ();
+
+ 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>", 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;
+ }
+ }
+
+ /// <summary>
+ /// Instrument the blocks with the inferred invariants
+ /// </summary>
+ public override void InstrumentWithInvariants()
+ {
+ foreach (Block b in this.Blocks)
+ {
+ if (b.Lattice != null)
+ {
+ CmdSeq newCommands = new CmdSeq();
+
+ assert b.PreInvariant != null; /* If the pre-abstract state is null, then something is wrong */
+
+ 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);
+
+ assert b.PostInvariant != null; /* If the post-state is null, then something is wrong */
+
+ // if(b.Cmds.Length > 0) // If it is not an empty block
+ // {
+ inv = (Expr) b.Lattice.ToPredicate(b.PostInvariant);
+ cmd = CommandLineOptions.Clo.InstrumentWithAsserts ? (PredicateCmd)new AssertCmd(Token.NoToken,inv) : (PredicateCmd)new AssumeCmd(Token.NoToken, inv);
+ newCommands.Add(cmd);
+ // }
+
+ b.Cmds = newCommands;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Insert a statement "assume J", for an opportune J - loop invariant predicate - in the "widen" blocks
+ /// </summary>
+ override public void InstrumentWithLoopInvariantPredicates()
+ {
+ foreach(Block b in this.Blocks)
+ {
+ if(b.widenBlock) { // if it is the head of a loop
+ CmdSeq newCommands = new CmdSeq();
+
+ Block! entryBlock = b;
+
+ ICollection<Block!> connectedComponents = this.GetConnectedComponents(b); // Get the connected components from this block
+
+ // Duplicate the connected components.
+ // Note that as we duplicate all the nodes, in particular we have also to keep track of the *copy* of the entry node.
+ // This is the reason why we pass entryBlockByReference
+ ICollection<Block!> dupConnectedComponents = duplicate((!) connectedComponents, ref entryBlock);
+
+ Expr loopInvariantPredicate = new LoopPredicate(entryBlock, dupConnectedComponents); // Create a new predicate J_{b.Label}
+
+ PredicateCmd cmd = new AssumeCmd(Token.NoToken, loopInvariantPredicate);
+
+ newCommands.Add(cmd);
+ newCommands.AddRange(b.Cmds);
+
+ b.Cmds = newCommands;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Return a collection of blocks that are reachable from the block passed as a parameter.
+ /// The block must be defined in the current implementation
+ /// </summary>
+ public ICollection<Block!> 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<Block!> 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.
+
+ return null; // unreachable code, just to please the compiler
+ }
+
+ /// <summary>
+ /// 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
+ /// </summary>
+ override public void ComputeStronglyConnectedComponents()
+ {
+ if(!this.BlockPredecessorsComputed)
+ ComputedPredecessorsForBlocks();
+
+ Adjacency<Block!> next = new Adjacency<Block!>(Successors);
+ Adjacency<Block!> prev = new Adjacency<Block!>(Predecessors);
+
+ this.scc = new StronglyConnectedComponents<Block><Block!>(this.Blocks, next, prev);
+ scc.Compute();
+
+ foreach(Block! block in this.Blocks)
+ {
+ block.Predecessors = new BlockSeq();
+ }
+
+ }
+
+ /// <summary>
+ /// Reset the abstract stated computed before
+ /// </summary>
+ override public void ResetAbstractInterpretationState()
+ {
+ foreach(Block! b in this.Blocks)
+ {
+ b.ResetAbstractInterpretationState();
+ }
+ }
+
+ /// <summary>
+ /// A private method used as delegate for the strongly connected components.
+ /// It return, given a node, the set of its successors
+ /// </summary>
+ private IEnumerable/*<Block!>*/! 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<Block!>();
+ }
+ }
+
+ /// <summary>
+ /// A private method used as delegate for the strongly connected components.
+ /// It return, given a node, the set of its predecessors
+ /// </summary>
+ private IEnumerable/*<Block!>*/! Predecessors(Block! node)
+ {
+ assert this.BlockPredecessorsComputed;
+
+ return node.Predecessors;
+ }
+
+ /// <summary>
+ /// Compute the predecessor informations for the blocks
+ /// </summary>
+ 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;
+ }
+
+ /// <summary>
+ /// Make a deep copy of the set of blocks in the input.
+ /// The input must NOT contain two copies of the same block
+ /// </summary>
+ private ICollection<Block!>! duplicate(ICollection<Block!>! blocks, ref Block! head)
+ {
+ Dictionary<Block!, Block!> transMap = new Dictionary<Block!, Block!>(); // A map from the old block to a fresh one
+
+ foreach(Block! block in blocks) // Create fresh copies for the blocks
+ {
+ Block! freshBlock = new Block(block.tok, block.Label, block.Cmds, block.TransferCmd); // Construct a copy of the block
+ freshBlock.widenBlock = block.widenBlock;
+ freshBlock.Predecessors = new BlockSeq();
+ transMap.Add(block, freshBlock); // Add a link block -> freshblock
+ }
+
+ foreach(Block! block in blocks) // Update the references
+ {
+ Block! freshBlock = transMap[block];
+
+ GotoCmd gotoCmd = freshBlock.TransferCmd as GotoCmd;
+ if(gotoCmd != null)
+ {
+ StringSeq! targetNames = new StringSeq();
+ BlockSeq! targetBlocks = new BlockSeq();
+
+ foreach(Block! next in (!) gotoCmd.labelTargets)
+ {
+ if(blocks.Contains(next))
+ {
+ assert transMap[next] != null;
+ targetNames.Add(next.Label);
+ targetBlocks.Add(transMap[next]);
+ }
+ }
+
+ GotoCmd freshGotoCmd = new GotoCmd(gotoCmd.tok, targetNames, targetBlocks);
+ freshBlock.TransferCmd = freshGotoCmd;
+ }
+ else
+ {
+ assert freshBlock.TransferCmd is ReturnCmd; // Do nothing as we do not need to update a returnCmd.
+ // However, as we want to make the code robust, we check that it is a ReturnCmd
+ }
+ }
+
+ head = transMap[head];
+
+ return transMap.Values;
+ }
+
+ public void PruneUnreachableBlocks() {
+ ArrayList /*Block!*/ visitNext = new ArrayList /*Block!*/ ();
+ List<Block!> reachableBlocks = new List<Block!>();
+ 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)
+ {
+// this.Type.Resolve(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);
+ }
+ }
+
+ /// <summary>
+ /// 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.
+ /// </summary>
+ 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;
+ }
+ }
+ /// <summary>
+ /// 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.
+ /// </summary>
+ /// <param name="lattice"></param>
+ /// <returns></returns>
+ 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/*<AI.IExpr!>*/! 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<Type!>! ToList() {
+ List<Type!>! res = new List<Type!> (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<TypeVariable!>! ToList() {
+ List<TypeVariable!>! res = new List<TypeVariable!> (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<Declaration!>! 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.ssc b/Source/Core/AbsyCmd.ssc new file mode 100644 index 00000000..5fb97548 --- /dev/null +++ b/Source/Core/AbsyCmd.ssc @@ -0,0 +1,2389 @@ +//-----------------------------------------------------------------------------
+//
+// 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<BigBlock!>! BigBlocks;
+ public CmdSeq PrefixCommands;
+ public readonly IToken! EndCurly;
+ public StmtList ParentContext;
+ public BigBlock ParentBigBlock;
+ public Set<string!>! Labels = new Set<string!>();
+
+ public StmtList([Captured] List<BigBlock!>! 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;
+ }
+ }
+
+ /// <summary>
+ /// 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.
+ /// </summary>
+ 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;
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// 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.
+ /// </summary>
+ public class StmtListBuilder {
+ List<BigBlock!>! bigBlocks = new List<BigBlock!>();
+ 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;
+ }
+ }
+
+ /// <summary>
+ /// Collects the StmtList built so far and returns it. The StmtListBuilder should no longer
+ /// be used once this method has been invoked.
+ /// </summary>
+ 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<Block!> blocks;
+ string! prefix = "anon";
+ int anon = 0;
+ Set<string!> allLabels = new Set<string!>();
+
+ public BigBlocksResolutionContext(StmtList! stmtList) {
+ this.stmtList = stmtList;
+ }
+
+ public List<Block!>! Blocks {
+ get {
+ if (blocks == null) {
+ blocks = new List<Block!>();
+
+ int startErrorCount = BoogiePL.Errors.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 (BoogiePL.Errors.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) {
+ BoogiePL.Errors.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
+ BoogiePL.Errors.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) {
+ BoogiePL.Errors.SemErr(bcmd.tok, "Error: break statement is not inside a loop");
+ } else {
+ BoogiePL.Errors.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<PredicateCmd!>! Invariants;
+ public StmtList! Body;
+
+ public WhileCmd(IToken! tok, [Captured] Expr? guard, List<PredicateCmd!>! 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 readonly string! Label;
+ [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 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.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);
+ }
+
+ /// <summary>
+ /// 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
+ /// </summary>
+ public void ResetAbstractInterpretationState()
+ {
+// this.currentlyTraversed = false;
+ this.TraversingStatus = VisitState.ToVisit;
+ this.iterations = 0;
+ this.Lattice = null;
+ this.PreInvariant = null;
+ this.PostInvariant = null;
+ }
+
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ 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<AssignLhs!>! lhss = new List<AssignLhs!> ();
+ List<Expr!>! rhss = new List<Expr!> ();
+
+ 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<AssignLhs!>! lhss = new List<AssignLhs!> ();
+ List<Expr!>! rhss = new List<Expr!> ();
+ List<Expr!>! indexesList = new List<Expr!> ();
+
+ 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<AssignLhs!>! lhss = new List<AssignLhs!> ();
+ List<Expr!>! rhss = new List<Expr!> ();
+ List<Expr!>! indexesList = new List<Expr!> ();
+
+ 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);
+ }
+
+ }
+
+ 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<AssignLhs!>! Lhss;
+ public List<Expr!>! Rhss;
+
+ public AssignCmd(IToken! tok, List<AssignLhs!>! lhss, List<Expr!>! 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<AssignLhs!>! newLhss = new List<AssignLhs!> ();
+ List<Expr!>! newRhss = new List<Expr!> ();
+
+ 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<Expr!>! 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<Expr!>! 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);
+ }
+ }
+
+ /// <summary>
+ /// 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.
+ /// </summary>
+ 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
+ {
+ protected CallCommonality(IToken! tok) {
+ base(tok);
+ }
+
+ 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<Expr>! Ins;
+ public List<IdentifierExpr>! 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<Expr>! insList = new List<Expr> ();
+ List<IdentifierExpr>! outsList = new List<IdentifierExpr> ();
+ 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<Expr>! ins, List<IdentifierExpr>! outs)
+ : base(tok)
+ {
+ this.callee = callee;
+ this.Ins = ins;
+ this.Outs = outs;
+ // base(tok);
+ }
+ public override void Emit(TokenTextWriter! stream, int level)
+ {
+ stream.Write(this, level, "call ");
+ 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;
+ }
+ 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/*<Variable>*/ actualOuts = new Set/*<Variable>*/ (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;
+ }
+
+ // 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
+
+ // 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]);
+ }
+
+ // match actuals with formals
+ List<Type!>! 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<TypeVariable!, Type!>! TypeParamSubstitution() {
+ assume TypeParameters != null;
+ IDictionary<TypeVariable!, Type!>! res = new Dictionary<TypeVariable!, Type!> ();
+ 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
+ // WildcarVars : 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<Expr>! 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<Expr>! ins)
+ : base(tok)
+ {
+ this.callee = callee;
+ this.Ins = ins;
+ }
+ public override void Emit(TokenTextWriter! stream, int level)
+ {
+ stream.Write(this, level, "call 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;
+ }
+ 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)
+ {
+ // 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<TypeVariable!, Type!>! 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<MiningStrategy>! msList;
+
+ public ListOfMiningStrategies (List<MiningStrategy>! l) {
+ this.msList = l;
+ }
+ }
+
+ public class EEDTemplate : MiningStrategy {
+ public string! reason;
+ public List<Expr!>! exprList;
+
+ public EEDTemplate (string! reason, List<Expr!>! 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 ");
+ 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 asserted expression must be of type bool (got: {0})", Expr.Type);
+ }
+ }
+
+ public static MiningStrategy GenerateBoundVarMiningStrategy (Expr! expr) {
+ List<MiningStrategy> l = new List<MiningStrategy>();
+ if (expr != null) {
+ l = GenerateBoundVarListForMining(expr, l);
+ }
+ return new ListOfMiningStrategies(l);
+ }
+
+ public static List<MiningStrategy>! GenerateBoundVarListForMining (Expr! expr, List<MiningStrategy>! 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<Expr!> exprList = new List<Expr!>();
+ 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)
+ {
+ }
+ }
+
+ /// <summary>
+ /// An AssertCmd that is introduced in translation from the requires on a call.
+ /// </summary>
+ 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);
+ }
+ }
+
+ /// <summary>
+ /// An AssertCmd that is introduced in translation from an ensures
+ /// declaration.
+ /// </summary>
+ 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<labelSeq.Length; i++) { Debug.Assert(Equals(labelSeq[i], ((!)blockSeq[i]).Label)); }
+
+ this.labelNames = labelSeq;
+ this.labelTargets = blockSeq;
+ }
+ public GotoCmd(IToken! tok, BlockSeq! blockSeq)
+ : base (tok)
+ { //requires blockSeq[i] != null ==> blockSeq[i].Label != null;
+ StringSeq labelSeq = new StringSeq();
+ for (int i=0; i<blockSeq.Length; i++)
+ labelSeq.Add(((!)blockSeq[i]).Label);
+ this.labelNames = labelSeq;
+ this.labelTargets = blockSeq;
+ }
+ public void AddTarget(Block! b)
+ requires b.Label != null;
+ requires this.labelTargets != null;
+ requires this.labelNames != null;
+ {
+ this.labelTargets.Add(b);
+ this.labelNames.Add(b.Label);
+ }
+ public override void Emit(TokenTextWriter! stream, int level)
+ {
+ assume this.labelNames != null;
+ stream.Write(this, level, "goto ");
+ if (CommandLineOptions.Clo.PrintWithUniqueASTIds)
+ {
+ if (labelTargets == null)
+ {
+ string sep = "";
+ foreach (string name in labelNames)
+ {
+ stream.Write("{0}{1}^^{2}", sep, "NoDecl", name);
+ sep = ", ";
+ }
+ }
+ else
+ {
+ string sep = "";
+ foreach (Block! b in labelTargets)
+ {
+ stream.Write("{0}h{1}^^{2}", sep, b.GetHashCode(), b.Label);
+ sep = ", ";
+ }
+ }
+ }
+ else
+ {
+ labelNames.Emit(stream);
+ }
+ stream.WriteLine(";");
+ }
+ public override void Resolve(ResolutionContext! rc)
+ ensures labelTargets != null;
+ {
+ if (labelTargets != null)
+ {
+ // already resolved
+ return;
+ }
+ assume this.labelNames != null;
+ labelTargets = new BlockSeq();
+ foreach (string! lbl in labelNames)
+ {
+ Block b = rc.LookUpBlock(lbl);
+ if (b == null)
+ {
+ rc.Error(this, "goto to unknown block: {0}", lbl);
+ }
+ else
+ {
+ labelTargets.Add(b);
+ }
+ }
+ Debug.Assert(rc.ErrorCount > 0 || labelTargets.Length == labelNames.Length);
+ }
+
+ public override Absy! StdDispatch(StandardVisitor! visitor)
+ {
+ return visitor.VisitGotoCmd(this);
+ }
+ }
+
+}
diff --git a/Source/Core/AbsyExpr.ssc b/Source/Core/AbsyExpr.ssc new file mode 100644 index 00000000..82854412 --- /dev/null +++ b/Source/Core/AbsyExpr.ssc @@ -0,0 +1,3256 @@ +//-----------------------------------------------------------------------------
+//
+// 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>", buffer, false))
+ {
+ this.Emit(stream, 0, false);
+ }
+ return buffer.ToString();
+ }
+
+ /// <summary>
+ /// Add to "freeVars" the free variables in the expression.
+ /// </summary>
+ public abstract void ComputeFreeVariables(Set /*Variable*/! freeVars);
+
+ /// <summary>
+ /// 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).
+ /// </summary>
+ 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;
+ }
+
+ /// <summary>
+ /// Returns the type of the expression, supposing that all its subexpressions are well typed.
+ /// </summary>
+ public abstract Type! ShallowType { get; }
+
+ // Handy syntactic sugar follows:
+
+ public static NAryExpr! Binary (IToken! x, BinaryOperator.Opcode op, Expr! e1, Expr! e2)
+ {
+ return new NAryExpr(x, new BinaryOperator(x, op), new ExprSeq(e1, e2));
+ }
+
+ 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 (BinaryOperator.Opcode op, Expr! e1, Expr! e2)
+ {
+ return new NAryExpr(Token.NoToken, new BinaryOperator(Token.NoToken, op), new ExprSeq(e1, e2));
+ }
+
+ 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<Expr!>! 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<Expr!>! 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);
+ }
+
+
+ /// <summary>
+ /// 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.
+ /// </summary>
+ public abstract AI.IExpr! IExpr {
+ [Peer] get;
+ }
+
+ }
+
+ public class LiteralExpr : Expr, AI.IFunApp
+ {
+ public readonly object! Val; // false, true, a BigNum, or a BvConst
+ /// <summary>
+ /// Creates a literal expression for the boolean value "b".
+ /// </summary>
+ /// <param name="tok"></param>
+ /// <param name="b"></param>
+ public LiteralExpr(IToken! tok, bool b)
+ : base(tok)
+ {
+ Val = b;
+ }
+ /// <summary>
+ /// Creates a literal expression for the integer value "v".
+ /// </summary>
+ /// <param name="tok"></param>
+ /// <param name="v"></param>
+ public LiteralExpr(IToken! tok, BigNum v)
+ : base(tok)
+ {
+ Val = v;
+ }
+
+ /// <summary>
+ /// Creates a literal expression for the bitvector value "v".
+ /// </summary>
+ 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/*<AI.IExpr!>*/! Arguments {
+ get {
+ return ArrayList.ReadOnly(new AI.IExpr[0]);
+ }
+ }
+ public Microsoft.AbstractInterpretationFramework.IFunApp! CloneWithArguments(IList/*<AI.IExpr!>*/! 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
+
+ /// <summary>
+ /// Creates an unresolved identifier expression.
+ /// </summary>
+ /// <param name="tok"></param>
+ /// <param name="name"></param>
+ 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
+
+ /// <summary>
+ /// 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.
+ /// </summary>
+ /// <param name="tok"></param>
+ /// <param name="name"></param>
+ internal IdentifierExpr(IToken! tok, string! name)
+ : base(tok)
+ {
+ Name = name;
+ // base(tok);
+ }
+ /// <summary>
+ /// Creates an unresolved identifier expression.
+ /// </summary>
+ /// <param name="tok"></param>
+ /// <param name="name"></param>
+ /// <param name="type"></param>
+ public IdentifierExpr(IToken! tok, string! name, Type! type)
+ : base(tok)
+ {
+ Name = name;
+ Type = type;
+ // base(tok);
+ }
+
+ /// <summary>
+ /// Creates a resolved identifier expression.
+ /// </summary>
+ /// <param name="tok"></param>
+ /// <param name="d"></param>
+ 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 (rc.StateMode == ResolutionContext.State.StateLess && Decl is GlobalVariable) {
+ rc.Error(this, "cannot refer to a global variable in this context: {0}", Name);
+ } else if (Decl == null) {
+ rc.Error(this, "undeclared identifier: {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/*<IExpr!>*/! 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/*<IExpr!*/! Arguments
+ {
+ get {
+ if (argCache == null)
+ {
+ IList l = new ArrayList(1);
+ l.Add(Expr.IExpr);
+ argCache = ArrayList.ReadOnly(l);
+ }
+ return argCache;
+ }
+ }
+ private sealed class OldFunctionSymbol : AI.IFunctionSymbol
+ {
+ private static AI.AIType! aitype = new AI.FunctionType(AI.Value.Type, AI.Value.Type);
+ public AI.AIType! AIType { get { return aitype; } }
+ private OldFunctionSymbol() { }
+ internal static OldFunctionSymbol! Sym = new OldFunctionSymbol();
+ [Pure]
+ public override string! ToString() { return "old"; }
+ }
+ public AI.IFunctionSymbol! FunctionSymbol
+ {
+ get { return OldFunctionSymbol.Sym; }
+ }
+
+ public override Absy! StdDispatch(StandardVisitor! visitor)
+ {
+ return visitor.VisitOldExpr(this);
+ }
+ }
+
+ public interface IAppliableVisitor<T> {
+
+ T Visit(UnaryOperator! unaryOperator);
+
+ T Visit(BinaryOperator! binaryOperator);
+
+ T Visit(FunctionCall! functionCall);
+
+ T Visit(LoopPredicateName! loopPredicateName);
+
+ T Visit(MapSelect! mapSelect);
+
+ T Visit(MapStore! mapStore);
+
+ T Visit(TypeCoercion! typeCoercion);
+ }
+
+ public interface IAppliable
+ {
+ string! FunctionName { get; }
+
+ /// <summary>
+ /// 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).
+ /// </summary>
+ /// <param name="args"></param>
+ /// <param name="stream"></param>
+ /// <param name="contextBindingStrength"></param>
+ /// <param name="fragileContext"></param>
+ void Emit(ExprSeq! args, TokenTextWriter! stream, int contextBindingStrength, bool fragileContext);
+
+ void Resolve(ResolutionContext! rc, Expr! subjectForErrorReporting);
+
+ /// <summary>
+ /// Requires the object to have been properly resolved.
+ /// </summary>
+ int ArgumentCount { get; }
+
+ /// <summary>
+ /// 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.
+ /// </summary>
+ /// <param name="args"></param>
+ /// <param name="tc"></param>
+ 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};
+
+ /// <summary>
+ /// Returns the result type of the IAppliable, supposing the argument are of the correct types.
+ /// </summary>
+ Type! ShallowType(ExprSeq! args);
+
+ AI.IFunctionSymbol! AIFunctionSymbol { get; }
+
+ T Dispatch<T>(IAppliableVisitor<T>! 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<T>(IAppliableVisitor<T>! 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<TypeVariable!, Type!> ()))
+ 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<T>(IAppliableVisitor<T>! 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 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<Type!>! 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<T>(IAppliableVisitor<T>! 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(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<T>(IAppliableVisitor<T>! visitor) {
+ return visitor.Visit(this);
+ }
+
+ }
+
+ /// <summary>
+ /// A subclass of FunctionCall that stands for a zero-argument function call, used as a loop invariant placeholder
+ /// </summary>
+ public class LoopPredicateName : FunctionCall
+ {
+ private Block! block; // The block the predicate refers to
+ public Block! Block
+ {
+ get
+ {
+ return block;
+ }
+ }
+
+ private string! blockName; // The name of the block
+ private ICollection<Block!>! component;
+ public ICollection<Block!>! Component
+ {
+ get
+ {
+ return this.component;
+ }
+ }
+
+ public string! Name
+ {
+ get
+ {
+ return "J_" + this.blockName;
+ }
+ }
+
+ invariant block.Label == blockName; // Note that we ask for object equality of strings...
+
+ private Hashtable /* Var -> Incarnations */ preHavocIncarnationMap;
+ public Hashtable PreHavocIncarnationMap
+ {
+ get
+ {
+ return this.preHavocIncarnationMap;
+ }
+ }
+
+ private Hashtable /* Var -> Incarnations */ postHavocIncarnationMap;
+ public Hashtable PostHavocIncarnationMap
+ {
+ get
+ {
+ return this.postHavocIncarnationMap;
+ }
+ }
+
+
+ // Those below are the inverse maps of the maps between variables and incarnations
+ private Hashtable /* String -> Var */ preHavocIncarnationInverseMap;
+ private Hashtable /* String -> Var */ postHavocIncarnationInverseMap;
+
+ public Hashtable PreHavocInverseMap
+ {
+ get
+ {
+ if(this.preHavocIncarnationInverseMap == null)
+ {
+ this.preHavocIncarnationInverseMap = new Hashtable();
+ assert this.preHavocIncarnationMap != null; // If we get at this point, then something is wrong with the program
+ foreach(object! key in (!) (this.preHavocIncarnationMap).Keys)
+ {
+ object! val = (!) this.preHavocIncarnationMap[key];
+ if(!this.preHavocIncarnationInverseMap.ContainsKey(val.ToString()))
+ this.preHavocIncarnationInverseMap.Add(val.ToString(), key);
+ }
+ }
+ return this.preHavocIncarnationInverseMap;
+ }
+ }
+
+ public Hashtable PostHavocInverseMap
+ {
+ get
+ {
+ if(this.postHavocIncarnationInverseMap == null)
+ {
+ this.postHavocIncarnationInverseMap = new Hashtable();
+ assert this.postHavocIncarnationMap != null; // If it is null, something is wrong before...
+ foreach(object! key in (!) (this.postHavocIncarnationMap).Keys)
+ {
+ object! val = (!) this.postHavocIncarnationMap[key];
+ if(!this.postHavocIncarnationInverseMap.ContainsKey(val.ToString()))
+ this.postHavocIncarnationInverseMap.Add(val.ToString(), key);
+ }
+ }
+ return this.postHavocIncarnationInverseMap;
+ }
+ }
+
+ /// <summary>
+ /// Create a new LoopPredicate
+ /// </summary>
+ public LoopPredicateName(Block! block, ICollection<Block!>! component)
+ : base(new IdentifierExpr(Token.NoToken, "J_"+ block.Label, Type.Bool))
+ {
+ this.block = block;
+ this.blockName = block.Label;
+ this.component = component;
+ // base(new IdentifierExpr(Token.NoToken, "J_"+ block.Label, Type.Bool));
+ }
+
+ public void SetPreAndPostHavocIncarnationMaps(Hashtable pre, Hashtable post)
+ {
+ assert this.preHavocIncarnationMap == null && this.postHavocIncarnationMap == null;
+
+ this.preHavocIncarnationMap = pre;
+ this.postHavocIncarnationMap = post;
+ }
+
+ /// <summary>
+ /// Writes down the loop predicate and the incarnations map before and after the havoc statements
+ /// </summary>
+ public override void Emit(ExprSeq! args, TokenTextWriter! stream, int contextBindingStrength, bool fragileContext)
+ {
+ string! pre, post;
+
+ if(this.postHavocIncarnationMap != null && this.preHavocIncarnationMap != null)
+ {
+ pre = "pre: [ " + hashtableToString(this.preHavocIncarnationMap) +" ]";
+ post = "post: [ " + hashtableToString(this.postHavocIncarnationMap) +" ]";
+ }
+ else
+ {
+ pre = post = "[]";
+ }
+
+ stream.Write("J_" + this.blockName);
+ stream.Write("(");
+ stream.Write(pre + ", " + post);
+ stream.Write(")");
+ }
+
+ public override Type Typecheck(ref ExprSeq! actuals, out TypeParamInstantiation! tpInstantiation, TypecheckingContext! tc)
+ {
+ tpInstantiation = SimpleTypeParamInstantiation.EMPTY;
+ return Type.Bool;
+ }
+
+ public override int ArgumentCount { get { return 0; } }
+
+ private string! hashtableToString(Hashtable! map)
+ {
+ string! outVal = "";
+
+ foreach (object! key in map.Keys)
+ {
+ object val = map[key];
+ outVal += key + " -> " + val + ",";
+ }
+
+ return outVal;
+ }
+
+ public override T Dispatch<T>(IAppliableVisitor<T>! 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/*<AI.IExpr!>*/! 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/*<AI.IExpr!>*/! 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);
+ }
+ }
+
+ /// <summary>
+ /// An instance of LoopPredicate stands for a loop invariant predicate
+ /// </summary>
+ public class LoopPredicate : NAryExpr
+ {
+ private static ExprSeq! emptyArgs = new ExprSeq(new Expr[0]); // Share the emptylist of arguments among several instances
+
+ invariant loopPredicateName == Fun; // loopPredicateName is just a way of subtyping Fun...
+ LoopPredicateName! loopPredicateName;
+
+ Hashtable /* Var -> Expr */ preHavocIncarnationMap;
+ Hashtable /* Var -> Expr */ postHavocIncarnationMap;
+
+ private Block! block; // The block the predicate refers to
+ private string! BlockName; // Name of the widen block the predicate refers to
+ private ICollection<Block!> component; // The component the block belongs to
+
+ /// <summary>
+ /// Create a predicate (standing for a loop invariant). The parameter are the name of the block and the (strong) connect components the block belongs to
+ /// </summary>
+ public LoopPredicate(Block! block, ICollection<Block!>! component)
+ {
+ this.block = block;
+ this.BlockName = block.Label;
+ this.component = component;
+
+ LoopPredicateName! tmp; // to please the compiler we introde a temp variable
+ this.loopPredicateName = tmp = new LoopPredicateName(block, component);
+ base(Token.NoToken, tmp, emptyArgs); // due to locally computed data
+ }
+
+ public void SetPreAndPostHavocIncarnationMaps(Hashtable pre, Hashtable post)
+ {
+ assert this.preHavocIncarnationMap == null && this.postHavocIncarnationMap == null; // The method must be called just once
+
+ this.preHavocIncarnationMap = pre;
+ this.postHavocIncarnationMap = post;
+ this.loopPredicateName.SetPreAndPostHavocIncarnationMaps(pre, post);
+ }
+
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)]
+ public override bool Equals(object obj)
+ {
+ if (obj == null) return false;
+ if (!(obj is LoopPredicate)) return false;
+
+ LoopPredicate pred = (LoopPredicate) obj;
+ return this.BlockName.Equals(pred.BlockName);
+ }
+
+ [Pure]
+ public override int GetHashCode()
+ {
+ return this.BlockName.GetHashCode();
+ }
+
+ public override Absy! StdDispatch(StandardVisitor! visitor)
+ {
+ return visitor.VisitLoopPredicate(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");
+ }
+
+ /// <summary>
+ /// Returns the result type of the IAppliable, supposing the argument are of the correct types.
+ /// </summary>
+ 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<T>(IAppliableVisitor<T>! 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");
+ }
+
+ /// <summary>
+ /// Returns the result type of the IAppliable, supposing the argument are of the correct types.
+ /// </summary>
+ 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<T>(IAppliableVisitor<T>! visitor) {
+ return visitor.Visit(this);
+ }
+ }
+
+ public abstract class QuantifierExpr : Expr
+ {
+ public TypeVariableSeq! TypeParameters;
+ public VariableSeq! Dummies;
+ public QKeyValue Attributes;
+ public Trigger Triggers;
+ public Expr! Body;
+
+ static int SkolemIds = 0;
+ 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);
+
+ assert (this is ForallExpr) || (this is ExistsExpr);
+
+ TypeParameters = typeParameters;
+ Dummies = dummies;
+ Attributes = kv;
+ Triggers = triggers;
+ Body = body;
+
+ SkolemId = SkolemIds++;
+ }
+
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)]
+ public override bool Equals(object obj)
+ {
+ if (obj == null) return false;
+ if (!(obj is QuantifierExpr) ||
+ (obj is ForallExpr) != (this is ForallExpr)) return false;
+
+ QuantifierExpr other = (QuantifierExpr)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();
+ if (this is ForallExpr) h = h * 3;
+ return h;
+ }
+
+ public override void Emit(TokenTextWriter! stream, int contextBindingStrength, bool fragileContext)
+ {
+ stream.Write(this, "({0}", this is ForallExpr ? "forall" : "exists");
+ 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(" ");
+ }
+ for (Trigger tr = this.Triggers; tr != null; tr = tr.Next) {
+ tr.Emit(stream);
+ stream.Write(" ");
+ }
+
+ this.Body.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;
+ }
+ }
+ }
+ public override void Resolve(ResolutionContext! rc)
+ {
+ int oldErrorCount = rc.ErrorCount;
+
+ this.MergeAdjecentQuantifier();
+ 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);
+ }
+ 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);
+ }
+ }
+ }
+ }
+ }
+ 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;
+ }
+
+ if (oldErrorCount == rc.ErrorCount) {
+ this.ApplyNeverTriggers();
+ }
+ }
+
+ #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
+
+ 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);
+ }
+ }
+ 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! dummyParameters = Type.FreeVariablesIn(Dummies.ToTypeSeq);
+ TypeVariableSeq! unmentionedParameters = new TypeVariableSeq ();
+ foreach (TypeVariable! var in TypeParameters)
+ if (!dummyParameters.Has(var))
+ unmentionedParameters.Add(var);
+
+ 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 abstract AI.IFunctionSymbol! FunctionSymbol { get; }
+
+ internal sealed class AIQuantifier : AI.IFunApp
+ {
+ internal readonly AIFunctionRep! arg;
+ internal AIQuantifier(QuantifierExpr! 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/*<IExpr!>*/! Arguments
+ {
+ get
+ {
+ if (argCache == null)
+ {
+ IList a = new ArrayList(1);
+ a.Add(arg);
+ argCache = ArrayList.ReadOnly(a);
+ }
+ return argCache;
+ }
+ }
+
+ public AI.IFunApp! CloneWithArguments(IList/*<IExpr!>*/! 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 QuantifierExpr! RealQuantifier;
+ private readonly int dummyIndex;
+
+ internal AIFunctionRep(QuantifierExpr! 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)
+ {
+ QuantifierExpr realquant;
+
+ AIQuantifier innerquant = body as AIQuantifier;
+ if (innerquant == null)
+ {
+ // new quantifier body, clone the real quantifier
+ realquant = (QuantifierExpr)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<object!>! Params; // each element is either a string or an Expr
+ public QKeyValue Next;
+
+ public QKeyValue(IToken! tok, string! key, [Captured] List<object!>! 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;
+ }
+
+ /// <summary>
+ /// Add to "freeVars" the free variables in the triggering expressions.
+ /// </summary>
+ 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 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 class BlockExpr : Expr
+ {
+ public VariableSeq! LocVars;
+ [Rep]
+ public BlockSeq! Blocks;
+ public BlockExpr(VariableSeq! localVariables, BlockSeq! blocks)
+ : base(Token.NoToken)
+ {
+ LocVars = localVariables;
+ Blocks = blocks;
+ }
+ public override AI.IExpr! IExpr {
+ get {
+ // An BlockExpr has no AI.IExpr representation
+ assert false;
+ throw new System.Exception(); // make compiler shut up
+ return Expr.False;
+ }
+ }
+ 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, "{0}", '{');
+
+ 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, "{0}", '}');
+
+ stream.WriteLine();
+ stream.WriteLine();
+ }
+
+ public override void Resolve(ResolutionContext! rc)
+ {
+
+ rc.PushVarContext();
+ foreach (Variable! v in LocVars)
+ {
+ v.Register(rc);
+ v.Resolve(rc);
+ }
+
+ rc.StartProcedureContext();
+ foreach (Block! b in Blocks)
+ {
+ b.Register(rc);
+ }
+
+ foreach (Block! b in Blocks)
+ {
+ b.Resolve(rc);
+ }
+
+ rc.EndProcedureContext();
+ 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.VisitBlockExpr(this);
+ }
+ }
+
+
+
+ public class ExtractExpr : Expr, AI.IFunApp
+ {
+ public /*readonly--except in StandardVisitor*/ Expr! Bitvector;
+ public readonly int Start, End;
+
+ public ExtractExpr(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 ExtractExpr)) return false;
+
+ ExtractExpr other = (ExtractExpr)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/*<AI.IExpr!>*/! 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/*<AI.IExpr!>*/! args)
+ {
+ AI.IFunApp! retFun;
+
+ if(args.Count == 3)
+ {
+ retFun = new ExtractExpr(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.VisitExtractExpr(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/*<AI.IExpr!>*/! 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/*<AI.IExpr!>*/! 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/AbsyType.ssc b/Source/Core/AbsyType.ssc new file mode 100644 index 00000000..c5f1309d --- /dev/null +++ b/Source/Core/AbsyType.ssc @@ -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<TypeVariable!, TypeVariable!> ());
+ }
+
+ public abstract Type! Clone(IDictionary<TypeVariable!, TypeVariable!>! varMap);
+
+ /// <summary>
+ /// 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.
+ /// </summary>
+ 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>", 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 -----------
+
+ /// <summary>
+ /// 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.
+ /// </summary>
+ public bool Unify(Type! that) {
+ return Unify(that, new TypeVariableSeq(), new Dictionary<TypeVariable!, Type!> ());
+ }
+
+ public abstract bool Unify(Type! that,
+ TypeVariableSeq! unifiableVariables,
+ // an idempotent substitution that describes the
+ // unification result up to a certain point
+ IDictionary<TypeVariable!, Type!>! unifier);
+ requires forall{TypeVariable key in unifier.Keys; unifiableVariables.Has(key)};
+ requires IsIdempotent(unifier);
+
+ [Pure]
+ public static bool IsIdempotent(IDictionary<TypeVariable!, Type!>! 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<TypeVariable!, Type!> Unify(Type! that,
+ TypeVariableSeq! unifiableVariables) {
+ Dictionary<TypeVariable!, Type!>! result = new Dictionary<TypeVariable!, Type!> ();
+ 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<TypeVariable!, Type!>! 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<TypeVariable!, Type!>! result);
+#endif
+
+ //----------- Substitution of free variables with types not containing bound variables -----------------
+
+ public abstract Type! Substitute(IDictionary<TypeVariable!, Type!>! 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<TypeProxy!>! FreeProxies { get; }
+
+ protected static void AppendWithoutDups<A>(List<A>! a, List<A>! 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<TypeVariable!, Type!>!
+ 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<TypeVariable!, Type!>! subst = new Dictionary<TypeVariable!, Type!>();
+
+ 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<TypeVariable!, Type!>!
+ 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<TypeVariable!, Type!> subst = new Dictionary<TypeVariable!, Type!>();
+ 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<Type!>! 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<Type!> ();
+
+ 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<Type!> ();
+ return typeParams.Length == 0 ? formalOuts : null;
+ }
+
+ int previousErrorCount = tc.ErrorCount;
+ IDictionary<TypeVariable!, Type!> 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<TypeVariable!, Type!>! 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<TypeVariable!, Type!>!
+ InferTypeParameters(TypeVariableSeq! typeParams,
+ TypeSeq! formalArgs,
+ TypeSeq! actualArgs)
+ requires formalArgs.Length == actualArgs.Length; {
+
+ TypeVariableSeq! boundVarSeq0 = new TypeVariableSeq ();
+ TypeVariableSeq! boundVarSeq1 = new TypeVariableSeq ();
+ Dictionary<TypeVariable!, Type!>! subst = new Dictionary<TypeVariable!, Type!>();
+
+ 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
+ /// <summary>
+ /// like Type.CheckArgumentTypes, but assumes no errors
+ /// (and only does arguments, not results; and takes actuals as TypeSeq, not ExprSeq)
+ /// </summary>
+ public static IDictionary<TypeVariable!, Type!>!
+ InferTypeParameters(TypeVariableSeq! typeParams,
+ TypeSeq! formalArgs,
+ TypeSeq! actualArgs)
+ requires formalArgs.Length == actualArgs.Length;
+ {
+ TypeSeq proxies = new TypeSeq();
+ Dictionary<TypeVariable!, Type!>! subst = new Dictionary<TypeVariable!, Type!>();
+ 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<TypeVariable!, TypeVariable!>! 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<TypeVariable!, Type!>! 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<TypeVariable!, Type!>! 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<TypeVariable!, Type!>! 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<TypeProxy!>! FreeProxies { get {
+ return new List<TypeProxy!> ();
+ } }
+
+ //----------- 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<TypeVariable!, TypeVariable!>! 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<TypeVariable!, Type!>! 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<TypeVariable!, Type!>! 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<TypeVariable!, Type!>! 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<TypeProxy!>! FreeProxies { get {
+ return new List<TypeProxy!> ();
+ } }
+
+ //----------- 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<TypeVariable!, TypeVariable!>! 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<TypeVariable!, Type!>! 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<TypeVariable!, Type!>! 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<TypeVariable!, Type!>! 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<TypeProxy!>! FreeProxies { get {
+ return new List<TypeProxy!> ();
+ } }
+
+ //----------- 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<TypeVariable!, TypeVariable!>! 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<TypeVariable!, Type!>! 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<TypeVariable!, Type!>! oldSolution,
+ // the type that "this" is instantiated with
+ Type! newSubst)
+ requires !oldSolution.ContainsKey(this); {
+
+ Dictionary<TypeVariable!, Type!>! newMapping = new Dictionary<TypeVariable!, Type!> ();
+ // 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<TypeVariable!>! keys = new List<TypeVariable!> ();
+ 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<TypeVariable!, Type!>! 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<TypeVariable!, Type!>! 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<TypeProxy!>! FreeProxies { get {
+ return new List<TypeProxy!> ();
+ } }
+
+ //----------- 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<TypeVariable!, TypeVariable!>! 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<n>$proxy<m>
+ }
+ }
+
+ public override Type! CloneUnresolved() {
+ return new TypeProxy(this.tok, this.Name); // the clone will have a name that ends with $proxy<n>$proxy<m>
+ }
+
+ //----------- 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<TypeVariable!, Type!>! 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<TypeVariable!, Type!>! 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<TypeProxy!>! FreeProxies { get {
+ Type p = ProxyFor;
+ if (p != null) {
+ return p.FreeProxies;
+ } else {
+ List<TypeProxy!>! res = new List<TypeProxy!> ();
+ 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);
+ }
+ }
+
+ /// <summary>
+ /// 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.
+ /// </summary>
+ public class BvTypeProxy : ConstrainedProxy {
+ public int MinBits;
+ List<BvTypeConstraint!> 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;
+ }
+
+ /// <summary>
+ /// Requires that any further constraints to be placed on t0 and t1 go via the object to
+ /// be constructed.
+ /// </summary>
+ 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<BvTypeConstraint!> list = new List<BvTypeConstraint!>();
+ list.Add(new BvTypeConstraint(t0, t1));
+ this.constraints = list;
+ }
+
+ /// <summary>
+ /// Construct a BvTypeProxy like p, but with minBits.
+ /// </summary>
+ 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<BvTypeConstraint!> 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<TypeVariable!, TypeVariable!>! 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<n>$bvproxy<m>
+ }
+ }
+
+ 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<n>$bvproxy<m>
+ }
+
+ //----------- Unification of types -----------
+
+ public override bool Unify(Type! that,
+ TypeVariableSeq! unifiableVariables,
+ IDictionary<TypeVariable!, Type!>! 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<BvTypeConstraint!> list = new List<BvTypeConstraint!>();
+ 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<TypeVariable!, Type!>! 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<Constraint>! constraints = new List<Constraint> ();
+
+ // 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<TypeVariable!, TypeVariable!>! 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<TypeVariable!, Type!>! result)
+ requires Arguments.Length == that.Arguments.Length; {
+ Dictionary<TypeVariable!, Type!>! subst = new Dictionary<TypeVariable!, Type!>();
+ 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<TypeVariable!, Type!> ());
+ 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<TypeVariable!, TypeVariable!>! 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<n>$mapproxy<m> (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<TypeVariable!, Type!>! 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<TypeVariable!, Type!>! 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<TypeVariable!, Type!>! subst =
+ new Dictionary<TypeVariable!, Type!> ();
+ 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<TypeVariable!, TypeVariable!>! 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<TypeVariable!, Type!>! result) {
+ return ExpandedType.Unify(that, unifiableVariables, result);
+ }
+
+#if OLD_UNIFICATION
+ public override void Unify(Type! that,
+ TypeVariableSeq! unifiableVariables,
+ TypeVariableSeq! thisBoundVariables,
+ TypeVariableSeq! thatBoundVariables,
+ IDictionary<TypeVariable!, Type!>! result) {
+ ExpandedType.Unify(that, unifiableVariables,
+ thisBoundVariables, thatBoundVariables, result);
+ }
+#endif
+
+ //----------- Substitution of free variables with types not containing bound variables -----------------
+
+ public override Type! Substitute(IDictionary<TypeVariable!, Type!>! 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<TypeProxy!>! 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<TypeVariable!, TypeVariable!>! 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<TypeVariable!, Type!>! 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<TypeVariable!, Type!>! 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<TypeVariable!, Type!>! 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<TypeProxy!>! FreeProxies { get {
+ List<TypeProxy!>! res = new List<TypeProxy!> ();
+ 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<TypeVariable!, TypeVariable!>! varMap) {
+ IDictionary<TypeVariable!, TypeVariable!>! newVarMap =
+ new Dictionary<TypeVariable!, TypeVariable!>();
+ foreach (KeyValuePair<TypeVariable!, TypeVariable!> 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<TypeVariable!, Type!>! 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<TypeVariable!, Type!>! subst0 = new Dictionary<TypeVariable!, Type!>();
+ Dictionary<TypeVariable!, Type!>! subst1 = new Dictionary<TypeVariable!, Type!>();
+ 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<TypeVariable!, Type!> 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<TypeVariable!, Type!>! 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<TypeVariable!, Type!>! 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<TypeVariable!, Type!>! 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<TypeProxy!>! FreeProxies { get {
+ List<TypeProxy!>! res = new List<TypeProxy!> ();
+ 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<Type!>! 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<TypeVariable!>! FormalTypeParams { get; }
+ // given a formal type parameter, return the actual instantiation
+ Type! this[TypeVariable! var] { get; }
+ }
+
+ public class SimpleTypeParamInstantiation : TypeParamInstantiation {
+ private readonly List<TypeVariable!>! TypeParams;
+ private readonly IDictionary<TypeVariable!, Type!>! Instantiations;
+
+ public SimpleTypeParamInstantiation(List<TypeVariable!>! typeParams,
+ IDictionary<TypeVariable!, Type!>! instantiations) {
+ this.TypeParams = typeParams;
+ this.Instantiations = instantiations;
+ }
+
+ public static TypeParamInstantiation!
+ From(TypeVariableSeq! typeParams, List<Type!>! actualTypeParams)
+ requires typeParams.Length == actualTypeParams.Count; {
+ if (typeParams.Length == 0)
+ return EMPTY;
+
+ List<TypeVariable!>! typeParamList = new List<TypeVariable!> ();
+ IDictionary<TypeVariable!, Type!>! dict = new Dictionary<TypeVariable!, Type!> ();
+ 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<TypeVariable!> (),
+ new Dictionary<TypeVariable!, Type!> ());
+
+ // return what formal type parameters there are
+ public List<TypeVariable!>! 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<TypeVariable!, Type!> Instantiations = null;
+
+ public MapTypeProxyParamInstantiation(MapTypeProxy! proxy,
+ TypeSeq! argumentsResult) {
+ this.Proxy = proxy;
+ this.ArgumentsResult = argumentsResult;
+ }
+
+ // return what formal type parameters there are
+ public List<TypeVariable!>! 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<TypeVariable!> ();
+ 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/BoogiePL.atg b/Source/Core/BoogiePL.atg new file mode 100644 index 00000000..a690c1e2 --- /dev/null +++ b/Source/Core/BoogiePL.atg @@ -0,0 +1,1374 @@ +
+/*---------------------------------------------------------------------------
+// BoogiePL -
+//--------------------------------------------------------------------------*/
+
+/*using System;*/
+using PureCollections;
+using System.Collections;
+using System.Collections.Generic;
+using Microsoft.Boogie;
+using Microsoft.Basetypes;
+using Bpl = Microsoft.Boogie;
+using AI = Microsoft.AbstractInterpretationFramework;
+
+
+COMPILER BoogiePL
+
+/*--------------------------------------------------------------------------*/
+
+static Program! Pgm = new Program();
+
+static Expr! dummyExpr = new LiteralExpr(Token.NoToken, false);
+static Cmd! dummyCmd = new AssumeCmd(Token.NoToken, dummyExpr);
+static Block! dummyBlock = new Block(Token.NoToken, "dummyBlock", new CmdSeq(),
+ new ReturnCmd(Token.NoToken));
+static Bpl.Type! dummyType = new BasicType(Token.NoToken, SimpleType.Bool);
+static Bpl.ExprSeq! dummyExprSeq = new ExprSeq ();
+static TransferCmd! dummyTransferCmd = new ReturnCmd(Token.NoToken);
+static StructuredCmd! dummyStructuredCmd = new BreakCmd(Token.NoToken, null);
+
+///<summary>
+///Returns the number of parsing errors encountered. If 0, "program" returns as
+///the parsed program.
+///</summary>
+public static int Parse (string! filename, out /*maybe null*/ Program program) /* throws System.IO.IOException */ {
+ using (System.IO.StreamReader reader = new System.IO.StreamReader(filename)) {
+ Buffer.Fill(reader);
+ Scanner.Init(filename);
+ return Parse(out program);
+ }
+}
+
+///<summary>
+///Returns the number of parsing errors encountered. If 0, "program" returns as
+///the parsed program.
+///Note: first initialize the Scanner.
+///</summary>
+public static int Parse (out /*maybe null*/ Program program) {
+ Pgm = new Program(); // reset the global variable
+ Parse();
+ if (Errors.count == 0)
+ {
+ program = Pgm;
+ return 0;
+ }
+ else
+ {
+ program = null;
+ return Errors.count;
+ }
+}
+
+
+public static int ParseProposition (string! text, out Expr! expression)
+{
+ Buffer.Fill(text);
+ Scanner.Init(string.Format("\"{0}\"", text));
+
+ Errors.SynErr = new ErrorProc(SynErr);
+ t = new Token();
+ Get();
+ Proposition(out expression);
+ return Errors.count;
+}
+
+// Class to represent the bounds of a bitvector expression t[a:b].
+// Objects of this class only exist during parsing and are directly
+// turned into BvExtract before they get anywhere else
+private class BvBounds : Expr {
+ public BigNum Lower;
+ public BigNum Upper;
+ public BvBounds(IToken! tok, BigNum lower, BigNum upper) {
+ base(tok);
+ this.Lower = lower;
+ this.Upper = upper;
+ }
+ public override Type! ShallowType { get { return Bpl.Type.Int; } }
+ public override void Resolve(ResolutionContext! rc) {
+ rc.Error(this, "bitvector bounds in illegal position");
+ }
+ public override void Emit(TokenTextWriter! stream,
+ int contextBindingStrength, bool fragileContext) {
+ assert false;
+ }
+ public override void ComputeFreeVariables(Set! freeVars) { assert false; }
+ public override AI.IExpr! IExpr { get { assert false; } }
+}
+
+/*--------------------------------------------------------------------------*/
+CHARACTERS
+ letter = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".
+ digit = "0123456789".
+ special = "'~#$^_.?`".
+ glyph = "`~!@#$%^&*()-_=+[{]}|;:',<.>/?\\".
+
+ cr = '\r'.
+ lf = '\n'.
+ tab = '\t'.
+
+ space = ' '.
+ quote = '"'.
+
+ nondigit = letter + special.
+ nonquote = letter + digit + space + glyph.
+
+
+/*------------------------------------------------------------------------*/
+TOKENS
+ ident = [ '\\' ] nondigit {nondigit | digit}.
+ bvlit = digit {digit} 'b' 'v' digit {digit}.
+ digits = digit {digit}.
+ string = quote {nonquote} quote.
+ float = digit {digit} '.' {digit}.
+
+COMMENTS FROM "/*" TO "*/" NESTED
+COMMENTS FROM "//" TO lf
+
+IGNORE cr + lf + tab
+
+
+/*------------------------------------------------------------------------*/
+PRODUCTIONS
+
+
+/*------------------------------------------------------------------------*/
+BoogiePL
+= (. VariableSeq! vs;
+ DeclarationSeq! ds;
+ Axiom! ax;
+ List<Declaration!>! ts;
+ Procedure! pr;
+ Implementation im;
+ Implementation! nnim;
+ .)
+ { Consts<out vs> (. foreach (Bpl.Variable! v in vs) { Pgm.TopLevelDeclarations.Add(v); } .)
+ | Function<out ds> (. foreach (Bpl.Declaration! d in ds) { Pgm.TopLevelDeclarations.Add(d); } .)
+ | Axiom<out ax> (. Pgm.TopLevelDeclarations.Add(ax); .)
+ | UserDefinedTypes<out ts> (. foreach (Declaration! td in ts) {
+ Pgm.TopLevelDeclarations.Add(td);
+ } .)
+ | GlobalVars<out vs> (. foreach (Bpl.Variable! v in vs) { Pgm.TopLevelDeclarations.Add(v); } .)
+ | Procedure<out pr, out im> (. Pgm.TopLevelDeclarations.Add(pr);
+ if (im != null) {
+ Pgm.TopLevelDeclarations.Add(im);
+ }
+ .)
+ | Implementation<out nnim> (. Pgm.TopLevelDeclarations.Add(nnim); .)
+ }
+ EOF
+ .
+
+/*------------------------------------------------------------------------*/
+GlobalVars<out VariableSeq! ds>
+= (. TypedIdentSeq! tyds = new TypedIdentSeq(); ds = new VariableSeq(); QKeyValue kv = null; .)
+ "var"
+ { Attribute<ref kv> }
+ IdsTypeWheres<true, tyds> ";"
+ (. foreach(TypedIdent! tyd in tyds) {
+ ds.Add(new GlobalVariable(tyd.tok, tyd, kv));
+ }
+ .)
+ .
+
+LocalVars<VariableSeq! ds>
+= (. TypedIdentSeq! tyds = new TypedIdentSeq(); QKeyValue kv = null; .)
+ "var"
+ { Attribute<ref kv> }
+ IdsTypeWheres<true, tyds> ";"
+ (. foreach(TypedIdent! tyd in tyds) {
+ ds.Add(new LocalVariable(tyd.tok, tyd, kv));
+ }
+ .)
+ .
+
+ProcFormals<bool incoming, bool allowWhereClauses, out VariableSeq! ds>
+= (. TypedIdentSeq! tyds = new TypedIdentSeq(); ds = new VariableSeq(); .)
+ "("
+ [ IdsTypeWheres<allowWhereClauses, tyds> ]
+ ")"
+ (. foreach (TypedIdent! tyd in tyds) {
+ ds.Add(new Formal(tyd.tok, tyd, incoming));
+ }
+ .)
+ .
+
+BoundVars<IToken! x, out VariableSeq! ds>
+= (. TypedIdentSeq! tyds = new TypedIdentSeq(); ds = new VariableSeq(); .)
+ IdsTypeWheres<false, tyds>
+ (. foreach (TypedIdent! tyd in tyds) {
+ ds.Add(new BoundVariable(tyd.tok, tyd));
+ }
+ .)
+ .
+
+/*------------------------------------------------------------------------*/
+/* IdsType is used with const declarations */
+IdsType<out TypedIdentSeq! tyds>
+= (. TokenSeq! ids; Bpl.Type! ty; .)
+ Idents<out ids> ":" Type<out ty>
+ (. tyds = new TypedIdentSeq();
+ foreach (Token! id in ids) {
+ tyds.Add(new TypedIdent(id, id.val, ty, null));
+ }
+ .)
+ .
+
+/* IdsTypeWheres is used with the declarations of global and local variables,
+ procedure parameters, and quantifier bound variables. */
+IdsTypeWheres<bool allowWhereClauses, TypedIdentSeq! tyds>
+=
+ IdsTypeWhere<allowWhereClauses, tyds>
+ { "," IdsTypeWhere<allowWhereClauses, tyds> }
+ .
+
+IdsTypeWhere<bool allowWhereClauses, TypedIdentSeq! tyds>
+= (. TokenSeq! ids; Bpl.Type! ty; Expr wh = null; Expr! nne; .)
+ Idents<out ids> ":" Type<out ty>
+ [ "where" Expression<out nne> (. if (allowWhereClauses) {
+ wh = nne;
+ } else {
+ SemErr("where clause not allowed here");
+ }
+ .)
+ ]
+ (. foreach (Token! id in ids) {
+ tyds.Add(new TypedIdent(id, id.val, ty, wh));
+ }
+ .)
+ .
+
+/*------------------------------------------------------------------------*/
+Type<out Bpl.Type! ty>
+= (. IToken! tok; ty = dummyType; .)
+ (
+ TypeAtom<out ty>
+ |
+ Ident<out tok> (. TypeSeq! args = new TypeSeq (); .)
+ [ TypeArgs<args> ] (. ty = new UnresolvedTypeIdentifier (tok, tok.val, args); .)
+ |
+ MapType<out ty>
+ )
+ .
+
+TypeArgs<TypeSeq! ts>
+= (. IToken! tok; Type! ty; .)
+ (
+ TypeAtom<out ty> (. ts.Add(ty); .)
+ [ TypeArgs<ts> ]
+ |
+ Ident<out tok> (. TypeSeq! args = new TypeSeq ();
+ ts.Add(new UnresolvedTypeIdentifier (tok, tok.val, args)); .)
+ [ TypeArgs<ts> ]
+ |
+ MapType<out ty> (. ts.Add(ty); .)
+ )
+ .
+
+TypeAtom<out Bpl.Type! ty>
+= (. ty = dummyType; .)
+ ( "int" (. ty = new BasicType(token, SimpleType.Int); .)
+ | "bool" (. ty = new BasicType(token, SimpleType.Bool); .)
+ /* note: bitvectors are handled in UnresolvedTypeIdentifier */
+ |
+ "("
+ Type<out ty>
+ ")"
+ )
+ .
+
+MapType<out Bpl.Type! ty>
+= (. IToken tok = null;
+ IToken! nnTok;
+ TypeSeq! arguments = new TypeSeq();
+ Type! result;
+ TypeVariableSeq! typeParameters = new TypeVariableSeq();
+ .)
+ [ TypeParams<out nnTok, out typeParameters> (. tok = nnTok; .) ]
+ "[" (. if (tok == null) tok = token; .)
+ [ Types<arguments> ]
+ "]"
+ Type<out result>
+ (.
+ ty = new MapType(tok, typeParameters, arguments, result);
+ .)
+ .
+
+TypeParams<out IToken! tok, out Bpl.TypeVariableSeq! typeParams>
+= (. TokenSeq! typeParamToks; .)
+ "<" (. tok = token; .)
+ Idents<out typeParamToks>
+ ">"
+ (.
+ typeParams = new TypeVariableSeq ();
+ foreach (Token! id in typeParamToks)
+ typeParams.Add(new TypeVariable(id, id.val));
+ .)
+ .
+
+Types<TypeSeq! ts>
+= (. Bpl.Type! ty; .)
+ Type<out ty> (. ts.Add(ty); .)
+ { "," Type<out ty> (. ts.Add(ty); .)
+ }
+ .
+
+
+/*------------------------------------------------------------------------*/
+Consts<out VariableSeq! ds>
+= (. IToken! y; TypedIdentSeq! xs;
+ ds = new VariableSeq();
+ bool u = false; QKeyValue kv = null;
+ bool ChildrenComplete = false;
+ List<ConstantParent!> Parents = null; .)
+ "const" (. y = token; .)
+ { Attribute<ref kv> }
+ [ "unique" (. u = true; .)
+ ]
+ IdsType<out xs>
+ [ OrderSpec<out ChildrenComplete, out Parents> ]
+ (. bool makeClone = false;
+ foreach(TypedIdent! x in xs) {
+
+ // ensure that no sharing is introduced
+ List<ConstantParent!> ParentsClone;
+ if (makeClone && Parents != null) {
+ ParentsClone = new List<ConstantParent!> ();
+ foreach (ConstantParent! p in Parents)
+ ParentsClone.Add(new ConstantParent (
+ new IdentifierExpr (p.Parent.tok, p.Parent.Name),
+ p.Unique));
+ } else {
+ ParentsClone = Parents;
+ }
+ makeClone = true;
+
+ ds.Add(new Constant(y, x, u, ParentsClone, ChildrenComplete, kv));
+ }
+ .)
+ ";"
+ .
+
+OrderSpec<out bool ChildrenComplete, out List<ConstantParent!\> Parents>
+= (. ChildrenComplete = false;
+ Parents = null;
+ bool u;
+ IToken! parent; .)
+ "extends" (. Parents = new List<ConstantParent!> ();
+ u = false; .)
+ [
+ [ "unique" (. u = true; .)
+ ]
+ Ident<out parent> (. Parents.Add(new ConstantParent (
+ new IdentifierExpr(parent, parent.val), u)); .)
+ {
+ "," (. u = false; .)
+ [ "unique" (. u = true; .)
+ ]
+ Ident<out parent> (. Parents.Add(new ConstantParent (
+ new IdentifierExpr(parent, parent.val), u)); .)
+ }
+ ]
+ [ "complete" (. ChildrenComplete = true; .)
+ ]
+ .
+
+/*------------------------------------------------------------------------*/
+Function<out DeclarationSeq! ds>
+= (. ds = new DeclarationSeq(); IToken! z;
+ IToken! typeParamTok;
+ TypeVariableSeq! typeParams = new TypeVariableSeq();
+ VariableSeq arguments = new VariableSeq();
+ TypedIdent! tyd;
+ QKeyValue kv = null;
+ Expr definition = null;
+ Expr! tmp;
+ .)
+ "function" { Attribute<ref kv> } Ident<out z>
+ [ TypeParams<out typeParamTok, out typeParams> ]
+ "("
+ [ VarOrType<out tyd> (. arguments.Add(new Formal(tyd.tok, tyd, true)); .)
+ { "," VarOrType<out tyd> (. arguments.Add(new Formal(tyd.tok, tyd, true)); .)
+ } ] ")"
+ "returns" "(" VarOrType<out tyd> ")"
+ ( "{" Expression<out tmp> (. definition = tmp; .) "}" | ";" )
+ (.
+ Function! func = new Function(z, z.val, typeParams, arguments,
+ new Formal(tyd.tok, tyd, false), null, kv);
+ ds.Add(func);
+ if (definition != null) {
+ // generate either an axiom or a function body
+ if (QKeyValue.FindBoolAttribute(kv, "inline")) {
+ func.Body = definition;
+ } else {
+ VariableSeq dummies = new VariableSeq();
+ ExprSeq callArgs = new ExprSeq();
+ int i = 0;
+ foreach (Formal! f in arguments) {
+ string nm = f.TypedIdent.HasName ? f.TypedIdent.Name : "_" + i;
+ dummies.Add(new BoundVariable(f.tok, new TypedIdent(f.tok, nm, f.TypedIdent.Type)));
+ callArgs.Add(new IdentifierExpr(f.tok, nm));
+ i++;
+ }
+ TypeVariableSeq! quantifiedTypeVars = new TypeVariableSeq ();
+ foreach (TypeVariable! t in typeParams)
+ quantifiedTypeVars.Add(new TypeVariable (Token.NoToken, t.Name));
+
+ Expr call = new NAryExpr(z, new FunctionCall(new IdentifierExpr(z, z.val)), callArgs);
+ // specify the type of the function, because it might be that
+ // type parameters only occur in the output type
+ call = Expr.CoerceType(z, call, (Type)tyd.Type.Clone());
+ Expr def = new ForallExpr(z, quantifiedTypeVars, dummies,
+ kv,
+ new Trigger(z, true, new ExprSeq(call), null),
+ Expr.Eq(call, definition));
+ ds.Add(new Axiom(z, def, "autogenerated definition axiom", null));
+ }
+ }
+ .)
+ .
+
+VarOrType<out TypedIdent! tyd>
+= (. string! varName = ""; Bpl.Type! ty; IToken! tok; .)
+ Type<out ty> (. tok = ty.tok; .)
+ [ ":" (. if (ty is UnresolvedTypeIdentifier &&
+ ((!)(ty as UnresolvedTypeIdentifier)).Arguments.Length == 0) {
+ varName = ((!)(ty as UnresolvedTypeIdentifier)).Name;
+ } else {
+ SemErr("expected identifier before ':'");
+ }
+ .)
+ Type<out ty>
+ ]
+ (. tyd = new TypedIdent(tok, varName, ty); .)
+ .
+
+/*------------------------------------------------------------------------*/
+Axiom<out Axiom! m>
+= (. Expr! e; QKeyValue kv = null; .)
+ "axiom"
+ { Attribute<ref kv> }
+ (. IToken! x = token; .)
+ Proposition<out e> ";" (. m = new Axiom(x,e, null, kv); .)
+ .
+
+/*------------------------------------------------------------------------*/
+UserDefinedTypes<out List<Declaration!\>! ts>
+= (. Declaration! decl; QKeyValue kv = null; ts = new List<Declaration!> (); .)
+ "type"
+ { Attribute<ref kv> }
+ UserDefinedType<out decl, kv> (. ts.Add(decl); .)
+ { "," UserDefinedType<out decl, kv> (. ts.Add(decl); .) }
+ ";"
+ .
+
+UserDefinedType<out Declaration! decl, QKeyValue kv>
+= (. IToken! id; IToken! id2; TokenSeq! paramTokens = new TokenSeq ();
+ Type! body = dummyType; bool synonym = false; .)
+ Ident<out id>
+ [ WhiteSpaceIdents<out paramTokens> ]
+ [
+ "=" Type<out body>
+ (. synonym = true; .)
+ ]
+ (.
+ if (synonym) {
+ TypeVariableSeq! typeParams = new TypeVariableSeq();
+ foreach (Token! t in paramTokens)
+ typeParams.Add(new TypeVariable(t, t.val));
+ decl = new TypeSynonymDecl(id, id.val, typeParams, body, kv);
+ } else {
+ decl = new TypeCtorDecl(id, id.val, paramTokens.Length, kv);
+ }
+ .)
+ .
+
+
+/*------------------------------------------------------------------------*/
+Procedure<out Procedure! proc, out /*maybe null*/ Implementation impl>
+= (. IToken! x;
+ TypeVariableSeq! typeParams;
+ VariableSeq! ins, outs;
+ RequiresSeq! pre = new RequiresSeq();
+ IdentifierExprSeq! mods = new IdentifierExprSeq();
+ EnsuresSeq! post = new EnsuresSeq();
+
+ VariableSeq! locals = new VariableSeq();
+ StmtList! stmtList;
+ QKeyValue kv = null;
+ impl = null;
+ .)
+
+ "procedure"
+ ProcSignature<true, out x, out typeParams, out ins, out outs, out kv>
+ ( ";"
+ { Spec<pre, mods, post> }
+ | { Spec<pre, mods, post> }
+ ImplBody<out locals, out stmtList>
+ (.
+ // here we attach kv only to the Procedure, not its implementation
+ impl = new Implementation(x, x.val, typeParams,
+ Formal.StripWhereClauses(ins), Formal.StripWhereClauses(outs), locals, stmtList, null);
+ .)
+ )
+ (. proc = new Procedure(x, x.val, typeParams, ins, outs, pre, mods, post, kv); .)
+ .
+
+
+Implementation<out Implementation! impl>
+= (. IToken! x;
+ TypeVariableSeq! typeParams;
+ VariableSeq! ins, outs;
+ VariableSeq! locals;
+ StmtList! stmtList;
+ QKeyValue kv;
+ .)
+
+ "implementation"
+ ProcSignature<false, out x, out typeParams, out ins, out outs, out kv>
+ ImplBody<out locals, out stmtList>
+ (. impl = new Implementation(x, x.val, typeParams, ins, outs, locals, stmtList, kv); .)
+ .
+
+
+ProcSignature<bool allowWhereClausesOnFormals, out IToken! name, out TypeVariableSeq! typeParams,
+ out VariableSeq! ins, out VariableSeq! outs, out QKeyValue kv>
+= (. IToken! typeParamTok; typeParams = new TypeVariableSeq();
+ outs = new VariableSeq(); kv = null; .)
+ { Attribute<ref kv> }
+ Ident<out name>
+ [ TypeParams<out typeParamTok, out typeParams> ]
+ ProcFormals<true, allowWhereClausesOnFormals, out ins>
+ [ "returns" ProcFormals<false, allowWhereClausesOnFormals, out outs> ]
+ .
+
+
+Spec<RequiresSeq! pre, IdentifierExprSeq! mods, EnsuresSeq! post>
+= (. TokenSeq! ms; .)
+ ( "modifies"
+ [ Idents<out ms> (. foreach (IToken! m in ms) {
+ mods.Add(new IdentifierExpr(m, m.val));
+ }
+ .)
+ ] ";"
+ | "free" SpecPrePost<true, pre, post>
+ | SpecPrePost<false, pre, post>
+ )
+ .
+
+SpecPrePost<bool free, RequiresSeq! pre, EnsuresSeq! post>
+= (. Expr! e; VariableSeq! locals; BlockSeq! blocks; Token tok = null; QKeyValue kv = null; .)
+ ( "requires" (. tok = token; .)
+ { Attribute<ref kv> }
+ (Proposition<out e> ";" (. pre.Add(new Requires(tok, free, e, null, kv)); .)
+ |
+ SpecBody<out locals, out blocks> ";"
+ (. pre.Add(new Requires(tok, free, new BlockExpr(locals, blocks), null, kv)); .)
+ )
+ | "ensures" (. tok = token; .)
+ { Attribute<ref kv> }
+ (Proposition<out e> ";" (. post.Add(new Ensures(tok, free, e, null, kv)); .)
+ |
+ SpecBody<out locals, out blocks> ";"
+ (. post.Add(new Ensures(tok, free, new BlockExpr(locals, blocks), null, kv)); .)
+ )
+ )
+ .
+
+SpecBody<out VariableSeq! locals, out BlockSeq! blocks>
+= (. locals = new VariableSeq(); Block! b; .)
+ "{{"
+ { LocalVars<locals> }
+ SpecBlock<out b> (. blocks = new BlockSeq(b); .)
+ { SpecBlock<out b> (. blocks.Add(b); .)
+ }
+ "}}"
+ .
+
+SpecBlock<out Block! b>
+= (. IToken! x; IToken! y;
+ Cmd c; IToken label;
+ CmdSeq cs = new CmdSeq();
+ TokenSeq! xs;
+ StringSeq ss = new StringSeq();
+ b = dummyBlock;
+ Expr! e;
+ .)
+ Ident<out x> ":"
+ { LabelOrCmd<out c, out label>
+ (. if (c != null) {
+ assert label == null;
+ cs.Add(c);
+ } else {
+ assert label != null;
+ SemErr("SpecBlock's can only have one label");
+ }
+ .)
+ }
+ ( "goto" (. y = token; .)
+ Idents<out xs> (. foreach (IToken! s in xs) { ss.Add(s.val); }
+ b = new Block(x,x.val,cs,new GotoCmd(y,ss));
+ .)
+ | "return" Expression<out e>
+ (. b = new Block(x,x.val,cs,new ReturnExprCmd(token,e)); .)
+ ) ";"
+ .
+
+/*------------------------------------------------------------------------*/
+
+ImplBody<out VariableSeq! locals, out StmtList! stmtList>
+= (. locals = new VariableSeq(); .)
+ "{"
+ { LocalVars<locals> }
+ StmtList<out stmtList>
+ .
+
+/* the StmtList also reads the final curly brace */
+StmtList<out StmtList! stmtList>
+= (. List<BigBlock!> bigblocks = new List<BigBlock!>();
+ /* built-up state for the current BigBlock: */
+ IToken startToken = null; string currentLabel = null;
+ CmdSeq cs = null; /* invariant: startToken != null ==> cs != null */
+ /* temporary variables: */
+ IToken label; Cmd c; BigBlock b;
+ StructuredCmd ec = null; StructuredCmd! ecn;
+ TransferCmd tc = null; TransferCmd! tcn;
+ .)
+
+ {
+ ( LabelOrCmd<out c, out label>
+ (. if (c != null) {
+ // LabelOrCmd read a Cmd
+ assert label == null;
+ if (startToken == null) { startToken = c.tok; cs = new CmdSeq(); }
+ assert cs != null;
+ cs.Add(c);
+ } else {
+ // LabelOrCmd read a label
+ assert label != null;
+ if (startToken != null) {
+ assert cs != null;
+ // dump the built-up state into a BigBlock
+ b = new BigBlock(startToken, currentLabel, cs, null, null);
+ bigblocks.Add(b);
+ cs = null;
+ }
+ startToken = label;
+ currentLabel = label.val;
+ cs = new CmdSeq();
+ }
+ .)
+
+ | StructuredCmd<out ecn>
+ (. ec = ecn;
+ if (startToken == null) { startToken = ec.tok; cs = new CmdSeq(); }
+ assert cs != null;
+ b = new BigBlock(startToken, currentLabel, cs, ec, null);
+ bigblocks.Add(b);
+ startToken = null; currentLabel = null; cs = null;
+ .)
+
+ | TransferCmd<out tcn>
+ (. tc = tcn;
+ if (startToken == null) { startToken = tc.tok; cs = new CmdSeq(); }
+ assert cs != null;
+ b = new BigBlock(startToken, currentLabel, cs, null, tc);
+ bigblocks.Add(b);
+ startToken = null; currentLabel = null; cs = null;
+ .)
+
+ )
+ }
+ "}"
+ (. IToken! endCurly = token;
+ if (startToken == null && bigblocks.Count == 0) {
+ startToken = token; cs = new CmdSeq();
+ }
+ if (startToken != null) {
+ assert cs != null;
+ b = new BigBlock(startToken, currentLabel, cs, null, null);
+ bigblocks.Add(b);
+ }
+
+ stmtList = new StmtList(bigblocks, endCurly);
+ .)
+ .
+
+TransferCmd<out TransferCmd! tc>
+= (. tc = dummyTransferCmd;
+ Token y; TokenSeq! xs;
+ StringSeq ss = new StringSeq();
+ .)
+ ( "goto" (. y = token; .)
+ Idents<out xs> (. foreach (IToken! s in xs) { ss.Add(s.val); }
+ tc = new GotoCmd(y, ss);
+ .)
+ | "return" (. tc = new ReturnCmd(token); .)
+ ) ";"
+ .
+
+StructuredCmd<out StructuredCmd! ec>
+= (. ec = dummyStructuredCmd; assume ec.IsPeerConsistent;
+ IfCmd! ifcmd; WhileCmd! wcmd; BreakCmd! bcmd;
+ .)
+ ( IfCmd<out ifcmd> (. ec = ifcmd; .)
+ | WhileCmd<out wcmd> (. ec = wcmd; .)
+ | BreakCmd<out bcmd> (. ec = bcmd; .)
+ )
+ .
+
+IfCmd<out IfCmd! ifcmd>
+= (. IToken! x;
+ Expr guard;
+ StmtList! thn;
+ IfCmd! elseIf; IfCmd elseIfOption = null;
+ StmtList! els; StmtList elseOption = null;
+ .)
+ "if" (. x = token; .)
+ Guard<out guard>
+ "{" StmtList<out thn>
+ [ "else"
+ ( IfCmd<out elseIf> (. elseIfOption = elseIf; .)
+ | "{"
+ StmtList<out els> (. elseOption = els; .)
+ )
+ ]
+ (. ifcmd = new IfCmd(x, guard, thn, elseIfOption, elseOption); .)
+ .
+
+WhileCmd<out WhileCmd! wcmd>
+= (. IToken! x; Token z;
+ Expr guard; Expr! e; bool isFree;
+ List<PredicateCmd!> invariants = new List<PredicateCmd!>();
+ StmtList! body;
+ .)
+ "while" (. x = token; .)
+ Guard<out guard> (. assume guard == null || Owner.None(guard); .)
+ { (. isFree = false; z = t/*lookahead token*/; .)
+ [ "free" (. isFree = true; .)
+ ]
+ "invariant"
+ Expression<out e> (. if (isFree) {
+ invariants.Add(new AssumeCmd(z, e));
+ } else {
+ invariants.Add(new AssertCmd(z, e));
+ }
+ .)
+ ";"
+ }
+ "{"
+ StmtList<out body> (. wcmd = new WhileCmd(x, guard, invariants, body); .)
+ .
+
+Guard<out Expr e>
+= (. Expr! ee; e = null; .)
+ "("
+ ( "*" (. e = null; .)
+ | Expression<out ee> (. e = ee; .)
+ )
+ ")"
+ .
+
+BreakCmd<out BreakCmd! bcmd>
+= (. IToken! x; IToken! y;
+ string breakLabel = null;
+ .)
+ "break" (. x = token; .)
+ [ Ident<out y> (. breakLabel = y.val; .)
+ ] ";" (. bcmd = new BreakCmd(x, breakLabel); .)
+ .
+
+/*------------------------------------------------------------------------*/
+
+LabelOrCmd<out Cmd c, out IToken label>
+/* ensures (c == null) != (label != null) */
+= (. IToken! x; Expr! e;
+ TokenSeq! xs;
+ IdentifierExprSeq ids;
+ c = dummyCmd; label = null;
+ Cmd! cn;
+ QKeyValue kv = null;
+ .)
+ ( LabelOrAssign<out c, out label>
+ | "assert" (. x = token; .)
+ { Attribute<ref kv> }
+ Proposition<out e> (. c = new AssertCmd(x,e, kv); .)
+ ";"
+ | "assume" (. x = token; .)
+ Proposition<out e> (. c = new AssumeCmd(x,e); .)
+ ";"
+ | "havoc" (. x = token; .)
+ Idents<out xs> ";" (. ids = new IdentifierExprSeq();
+ foreach (IToken! y in xs) {
+ ids.Add(new IdentifierExpr(y, y.val));
+ }
+ c = new HavocCmd(x,ids);
+ .)
+ | CallCmd<out cn> ";" (. c = cn; .)
+ )
+ .
+
+/*------------------------------------------------------------------------*/
+
+LabelOrAssign<out Cmd c, out IToken label>
+/* ensures (c == null) != (label != null) */
+= (. IToken! id; IToken! x; Expr! e, e0;
+ c = dummyCmd; label = null;
+ AssignLhs! lhs;
+ List<AssignLhs!>! lhss;
+ List<Expr!>! rhss;
+ .)
+ Ident<out id> (. x = token; .)
+ ( ":" (. c = null; label = x; .)
+ |
+ MapAssignIndexes<id, out lhs> (. lhss = new List<AssignLhs!> ();
+ lhss.Add(lhs); .)
+ { ","
+ Ident<out id>
+ MapAssignIndexes<id, out lhs> (. lhss.Add(lhs); .)
+ }
+ ":=" (. x = token; /* use location of := */ .)
+ Expression<out e0> (. rhss = new List<Expr!> ();
+ rhss.Add(e0); .)
+ { ","
+ Expression<out e0> (. rhss.Add(e0); .)
+ }
+ ";" (. c = new AssignCmd(x, lhss, rhss); .)
+ )
+ .
+
+MapAssignIndexes<IToken! assignedVariable, out AssignLhs! lhs>
+= (. IToken! x;
+ AssignLhs! runningLhs =
+ new SimpleAssignLhs(assignedVariable,
+ new IdentifierExpr(assignedVariable, assignedVariable.val));
+ List<Expr!>! indexes;
+ Expr! e0;
+ .)
+ {
+ "[" (. x = token;
+ indexes = new List<Expr!> (); .)
+ [
+ Expression<out e0> (. indexes.Add(e0); .)
+ { ","
+ Expression<out e0> (. indexes.Add(e0); .)
+ }
+ ]
+ "]" (. runningLhs =
+ new MapAssignLhs (x, runningLhs, indexes); .)
+ }
+ (. lhs = runningLhs; .)
+ .
+
+/*------------------------------------------------------------------------*/
+CallCmd<out Cmd! c>
+= (. IToken! x; IToken! first; IToken p;
+ List<IdentifierExpr>! ids = new List<IdentifierExpr>();
+ List<Expr>! es = new List<Expr>();
+ Expr en; List<Expr> args;
+ c = dummyCmd;
+ .)
+ "call" (. x = token; .)
+ ( Ident<out first>
+ ( "("
+ [ CallForallArg<out en> (. es.Add(en); .)
+ { "," CallForallArg<out en> (. es.Add(en); .)
+ }
+ ]
+ ")" (. c = new CallCmd(x, first.val, es, ids); .)
+ |
+ (. ids.Add(new IdentifierExpr(first, first.val)); .)
+ [ "," CallOutIdent<out p> (.
+ if (p==null) {
+ ids.Add(null);
+ } else {
+ ids.Add(new IdentifierExpr(p, p.val));
+ }
+ .)
+ { "," CallOutIdent<out p> (.
+ if (p==null) {
+ ids.Add(null);
+ } else {
+ ids.Add(new IdentifierExpr(p, p.val));
+ }
+ .)
+ }
+ ] ":="
+ Ident<out first> "("
+ [ CallForallArg<out en> (. es.Add(en); .)
+ { "," CallForallArg<out en> (. es.Add(en); .)
+ }
+ ]
+ ")" (. c = new CallCmd(x, first.val, es, ids); .)
+ )
+ | "forall"
+ Ident<out first> "(" (. args = new List<Expr>(); .)
+ [ CallForallArg<out en> (. args.Add(en); .)
+ { "," CallForallArg<out en> (. args.Add(en); .)
+ }
+ ]
+ ")" (. c = new CallForallCmd(x, first.val, args); .)
+ | "*"
+ (. ids.Add(null); .)
+ [ "," CallOutIdent<out p> (.
+ if (p==null) {
+ ids.Add(null);
+ } else {
+ ids.Add(new IdentifierExpr(p, p.val));
+ }
+ .)
+ { "," CallOutIdent<out p> (.
+ if (p==null) {
+ ids.Add(null);
+ } else {
+ ids.Add(new IdentifierExpr(p, p.val));
+ }
+ .)
+ }
+ ] ":="
+ Ident<out first> "("
+ [ CallForallArg<out en> (. es.Add(en); .)
+ { "," CallForallArg<out en> (. es.Add(en); .)
+ }
+ ]
+ ")" (. c = new CallCmd(x, first.val, es, ids); .)
+ )
+ .
+
+CallOutIdent<out IToken id>
+= (. id = null;
+ IToken! p;
+ .)
+ ( "*"
+ | Ident<out p> (. id = p; .)
+ )
+ .
+
+CallForallArg<out Expr exprOptional>
+= (. exprOptional = null;
+ Expr! e;
+ .)
+ ( "*"
+ | Expression<out e> (. exprOptional = e; .)
+ )
+ .
+
+/*------------------------------------------------------------------------*/
+Proposition<out Expr! e>
+=
+ Expression<out e>
+ .
+
+/*------------------------------------------------------------------------*/
+Idents<out TokenSeq! xs>
+= (. IToken! id; xs = new TokenSeq(); .)
+ Ident<out id> (. xs.Add(id); .)
+ { "," Ident<out id> (. xs.Add(id); .)
+ }
+ .
+
+/*------------------------------------------------------------------------*/
+WhiteSpaceIdents<out TokenSeq! xs>
+= (. IToken! id; xs = new TokenSeq(); .)
+ Ident<out id> (. xs.Add(id); .)
+ { Ident<out id> (. xs.Add(id); .)
+ }
+ .
+
+/*------------------------------------------------------------------------*/
+Expressions<out ExprSeq! es>
+= (. Expr! e; es = new ExprSeq(); .)
+ Expression<out e> (. es.Add(e); .)
+ { "," Expression<out e> (. es.Add(e); .)
+ }
+ .
+
+/*------------------------------------------------------------------------*/
+Expression<out Expr! e0>
+= (. IToken! x; Expr! e1; .)
+ ImpliesExpression<false, out e0>
+ { EquivOp (. x = token; .)
+ ImpliesExpression<false, out e1>
+ (. e0 = Expr.Binary(x, BinaryOperator.Opcode.Iff, e0, e1); .)
+ }
+ .
+
+EquivOp = "<==>" | '\u21d4'.
+
+/*------------------------------------------------------------------------*/
+ImpliesExpression<bool noExplies, out Expr! e0>
+= (. IToken! x; Expr! e1; .)
+ LogicalExpression<out e0>
+ [
+ ImpliesOp (. x = token; .)
+ /* recurse because implication is right-associative */
+ ImpliesExpression<true, out e1>
+ (. e0 = Expr.Binary(x, BinaryOperator.Opcode.Imp, e0, e1); .)
+ |
+ ExpliesOp (. if (noExplies)
+ SemErr("illegal mixture of ==> and <==, use parentheses to disambiguate");
+ x = token; .)
+ LogicalExpression<out e1>
+ (. e0 = Expr.Binary(x, BinaryOperator.Opcode.Imp, e1, e0); .)
+ /* loop because explies is left-associative */
+ {
+ ExpliesOp (. x = token; .)
+ LogicalExpression<out e1>
+ (. e0 = Expr.Binary(x, BinaryOperator.Opcode.Imp, e1, e0); .)
+ }
+ ]
+ .
+
+ImpliesOp = "==>" | '\u21d2'.
+ExpliesOp = "<==" | '\u21d0'.
+
+/*------------------------------------------------------------------------*/
+LogicalExpression<out Expr! e0>
+= (. IToken! x; Expr! e1; BinaryOperator.Opcode op; .)
+ RelationalExpression<out e0>
+ [ AndOp (. x = token; .)
+ RelationalExpression<out e1>
+ (. e0 = Expr.Binary(x, BinaryOperator.Opcode.And, e0, e1); .)
+ { AndOp (. x = token; .)
+ RelationalExpression<out e1>
+ (. e0 = Expr.Binary(x, BinaryOperator.Opcode.And, e0, e1); .)
+ }
+ | OrOp (. x = token; .)
+ RelationalExpression<out e1>
+ (. e0 = Expr.Binary(x, BinaryOperator.Opcode.Or, e0, e1); .)
+ { OrOp (. x = token; .)
+ RelationalExpression<out e1>
+ (. e0 = Expr.Binary(x, BinaryOperator.Opcode.Or, e0, e1); .)
+ }
+ ]
+ .
+
+AndOp = "&&" | '\u2227'.
+OrOp = "||" | '\u2228'.
+
+/*------------------------------------------------------------------------*/
+RelationalExpression<out Expr! e0>
+= (. IToken! x; Expr! e1; BinaryOperator.Opcode op; .)
+ BvTerm<out e0>
+ [ RelOp<out x, out op>
+ BvTerm<out e1> (. e0 = Expr.Binary(x, op, e0, e1); .)
+ ]
+ .
+
+RelOp<out IToken! x, out BinaryOperator.Opcode op>
+= (. x = Token.NoToken; op=BinaryOperator.Opcode.Add/*(dummy)*/; .)
+ ( "==" (. x = token; op=BinaryOperator.Opcode.Eq; .)
+ | "<" (. x = token; op=BinaryOperator.Opcode.Lt; .)
+ | ">" (. x = token; op=BinaryOperator.Opcode.Gt; .)
+ | "<=" (. x = token; op=BinaryOperator.Opcode.Le; .)
+ | ">=" (. x = token; op=BinaryOperator.Opcode.Ge; .)
+ | "!=" (. x = token; op=BinaryOperator.Opcode.Neq; .)
+ | "<:" (. x = token; op=BinaryOperator.Opcode.Subtype; .)
+ | '\u2260' (. x = token; op=BinaryOperator.Opcode.Neq; .)
+ | '\u2264' (. x = token; op=BinaryOperator.Opcode.Le; .)
+ | '\u2265' (. x = token; op=BinaryOperator.Opcode.Ge; .)
+ )
+ .
+
+/*------------------------------------------------------------------------*/
+BvTerm<out Expr! e0>
+= (. IToken! x; Expr! e1; .)
+ Term<out e0>
+ { "++" (. x = token; .)
+ Term<out e1> (. e0 = new BvConcatExpr(x, e0, e1); .)
+ }
+ .
+
+
+/*------------------------------------------------------------------------*/
+Term<out Expr! e0>
+= (. IToken! x; Expr! e1; BinaryOperator.Opcode op; .)
+ Factor<out e0>
+ { AddOp<out x, out op>
+ Factor<out e1> (. e0 = Expr.Binary(x, op, e0, e1); .)
+ }
+ .
+
+AddOp<out IToken! x, out BinaryOperator.Opcode op>
+= (. x = Token.NoToken; op=BinaryOperator.Opcode.Add/*(dummy)*/; .)
+ ( "+" (. x = token; op=BinaryOperator.Opcode.Add; .)
+ | "-" (. x = token; op=BinaryOperator.Opcode.Sub; .)
+ )
+ .
+
+/*------------------------------------------------------------------------*/
+Factor<out Expr! e0>
+= (. IToken! x; Expr! e1; BinaryOperator.Opcode op; .)
+ UnaryExpression<out e0>
+ { MulOp<out x, out op>
+ UnaryExpression<out e1> (. e0 = Expr.Binary(x, op, e0, e1); .)
+ }
+ .
+
+MulOp<out IToken! x, out BinaryOperator.Opcode op>
+= (. x = Token.NoToken; op=BinaryOperator.Opcode.Add/*(dummy)*/; .)
+ ( "*" (. x = token; op=BinaryOperator.Opcode.Mul; .)
+ | "/" (. x = token; op=BinaryOperator.Opcode.Div; .)
+ | "%" (. x = token; op=BinaryOperator.Opcode.Mod; .)
+ )
+ .
+
+/*------------------------------------------------------------------------*/
+UnaryExpression<out Expr! e>
+= (. IToken! x;
+ e = dummyExpr;
+ .)
+ ( "-" (. x = token; .)
+ UnaryExpression<out e> (. e = Expr.Binary(x, BinaryOperator.Opcode.Sub, new LiteralExpr(x, BigNum.ZERO), e); .)
+ | NegOp (. x = token; .)
+ UnaryExpression<out e> (. e = Expr.Unary(x, UnaryOperator.Opcode.Not, e); .)
+ | CoercionExpression<out e>
+ )
+ .
+
+NegOp = "!" | '\u00ac'.
+
+/*------------------------------------------------------------------------*/
+
+/* This production creates ambiguities, because types can start with "<"
+ (polymorphic map types), but can also be followed by "<" (inequalities).
+ Coco deals with these ambiguities in a reasonable way by preferring to read
+ further types (type arguments) over relational symbols. E.g., "5 : C < 0"
+ will cause a parse error because "<" is treated as the beginning of a
+ map type. */
+
+CoercionExpression<out Expr! e>
+= (. IToken! x;
+ Type! coercedTo;
+ BigNum bn;
+ .)
+ ArrayExpression<out e>
+ { ":" (. x = token; .)
+ (
+ Type<out coercedTo> (. e = Expr.CoerceType(x, e, coercedTo); .)
+ |
+ Nat<out bn> /* This means that we really look at a bitvector
+ expression t[a:b] */
+ (. if (!(e is LiteralExpr) || !((LiteralExpr)e).isBigNum) {
+ SemErr("arguments of extract need to be integer literals");
+ e = new BvBounds(x, bn, BigNum.ZERO);
+ } else {
+ e = new BvBounds(x, bn, ((LiteralExpr)e).asBigNum);
+ }
+ .)
+ )
+ }
+ .
+
+/*------------------------------------------------------------------------*/
+ArrayExpression<out Expr! e>
+= (. IToken! x;
+ Expr! index0 = dummyExpr; Expr! e1;
+ bool store; bool bvExtract;
+ ExprSeq! allArgs = dummyExprSeq;
+ .)
+ AtomExpression<out e>
+ { "[" (. x = token; allArgs = new ExprSeq ();
+ allArgs.Add(e);
+ store = false; bvExtract = false; .)
+ [
+ Expression<out index0>
+ (. if (index0 is BvBounds)
+ bvExtract = true;
+ else
+ allArgs.Add(index0);
+ .)
+ { "," Expression<out e1>
+ (. if (bvExtract || e1 is BvBounds)
+ SemErr("bitvectors only have one dimension");
+ allArgs.Add(e1);
+ .)
+ }
+ [ ":=" Expression<out e1>
+ (. if (bvExtract || e1 is BvBounds)
+ SemErr("assignment to bitvectors is not possible");
+ allArgs.Add(e1); store = true;
+ .)
+ ]
+ | ":=" Expression<out e1> (. allArgs.Add(e1); store = true; .)
+ ]
+ "]"
+ (. if (store)
+ e = new NAryExpr(x, new MapStore(x, allArgs.Length - 2), allArgs);
+ else if (bvExtract)
+ e = new ExtractExpr(x, e,
+ ((BvBounds)index0).Upper.ToIntSafe,
+ ((BvBounds)index0).Lower.ToIntSafe);
+ else
+ e = new NAryExpr(x, new MapSelect(x, allArgs.Length - 1), allArgs);
+ .)
+ }
+ .
+
+
+/*------------------------------------------------------------------------*/
+AtomExpression<out Expr! e>
+= (. IToken! x; int n; BigNum bn;
+ ExprSeq! es; VariableSeq! ds; Trigger trig;
+ TypeVariableSeq! typeParams;
+ IdentifierExpr! id;
+ Bpl.Type! ty;
+ QKeyValue kv;
+ e = dummyExpr;
+ .)
+ ( "false" (. e = new LiteralExpr(token, false); .)
+ | "true" (. e = new LiteralExpr(token, true); .)
+ | Nat<out bn> (. e = new LiteralExpr(token, bn); .)
+ | BvLit<out bn, out n> (. e = new LiteralExpr(token, bn, n); .)
+
+ | Ident<out x> (. id = new IdentifierExpr(x, x.val); e = id; .)
+ [ "("
+ ( Expressions<out es> (. e = new NAryExpr(x, new FunctionCall(id), es); .)
+ | /* empty */ (. e = new NAryExpr(x, new FunctionCall(id), new ExprSeq()); .)
+ )
+ ")"
+ ]
+
+ | "old" (. x = token; .)
+ "("
+ Expression<out e>
+ ")" (. e = new OldExpr(x, e); .)
+
+ | "(" ( Expression<out e> (. if (e is BvBounds)
+ SemErr("parentheses around bitvector bounds " +
+ "are not allowed"); .)
+ | Forall (. x = token; .)
+ QuantifierBody<x, out typeParams, out ds, out kv, out trig, out e>
+ (. if (typeParams.Length + ds.Length > 0)
+ e = new ForallExpr(x, typeParams, ds, kv, trig, e); .)
+ | Exists (. x = token; .)
+ QuantifierBody<x, out typeParams, out ds, out kv, out trig, out e>
+ (. if (typeParams.Length + ds.Length > 0)
+ e = new ExistsExpr(x, typeParams, ds, kv, trig, e); .)
+ )
+ ")"
+ )
+ .
+
+Attribute<ref QKeyValue kv>
+= (. Trigger trig = null; .)
+ AttributeOrTrigger<ref kv, ref trig> (. if (trig != null) SemErr("only attributes, not triggers, allowed here"); .)
+.
+
+AttributeOrTrigger<ref QKeyValue kv, ref Trigger trig>
+= (. IToken! tok; Expr! e; ExprSeq! es;
+ string key; string value;
+ List<object!> parameters; object! param;
+ .)
+ "{" (. tok = token; .)
+ (
+ ":" ident (. key = token.val; parameters = new List<object!>(); .)
+ [ AttributeParameter<out param> (. parameters.Add(param); .)
+ { "," AttributeParameter<out param> (. parameters.Add(param); .)
+ }
+ ]
+ (. if (key == "nopats") {
+ if (parameters.Count == 1 && parameters[0] is Expr) {
+ e = (Expr)parameters[0];
+ if(trig==null){
+ trig = new Trigger(tok, false, new ExprSeq(e), null);
+ } else {
+ trig.AddLast(new Trigger(tok, false, new ExprSeq(e), null));
+ }
+ } else {
+ SemErr("the 'nopats' quantifier attribute expects a string-literal parameter");
+ }
+ } else {
+ if (kv==null) {
+ kv = new QKeyValue(tok, key, parameters, null);
+ } else {
+ kv.AddLast(new QKeyValue(tok, key, parameters, null));
+ }
+ }
+ .)
+ |
+ Expression<out e> (. es = new ExprSeq(e); .)
+ { "," Expression<out e> (. es.Add(e); .)
+ } (. if (trig==null) {
+ trig = new Trigger(tok, true, es, null);
+ } else {
+ trig.AddLast(new Trigger(tok, true, es, null));
+ }
+ .)
+ )
+ "}"
+ .
+
+AttributeParameter<out object! o>
+= (. o = "error";
+ Expr! e;
+ .)
+ ( string (. o = token.val.Substring(1, token.val.Length-2); .)
+ | Expression<out e> (. o = e; .)
+ )
+ .
+
+QuantifierBody<IToken! q, out TypeVariableSeq! typeParams, out VariableSeq! ds,
+ out QKeyValue kv, out Trigger trig, out Expr! body>
+= (. trig = null; typeParams = new TypeVariableSeq ();
+ IToken! tok; Expr! e; ExprSeq! es;
+ kv = null; string key; string value;
+ ds = new VariableSeq ();
+ .)
+ (
+ TypeParams<out tok, out typeParams>
+ [ BoundVars<q, out ds> ]
+ |
+ BoundVars<q, out ds>
+ )
+ QSep
+ { AttributeOrTrigger<ref kv, ref trig> }
+ Expression<out body>
+ .
+
+Forall = "forall" | '\u2200'.
+Exists = "exists" | '\u2203'.
+QSep = "::" | '\u2022'.
+
+/*------------------------------------------------------------------------*/
+Ident<out IToken! x>
+=
+ ident (. x = token;
+ if (x.val.StartsWith("\\"))
+ x.val = x.val.Substring(1);
+ .)
+ .
+
+/*------------------------------------------------------------------------*/
+Nat<out BigNum n>
+=
+ digits
+ (. try {
+ n = BigNum.FromString(token.val);
+ } catch (FormatException) {
+ SemErr("incorrectly formatted number");
+ n = BigNum.ZERO;
+ }
+ .)
+ .
+
+/*------------------------------------------------------------------------*/
+BvLit<out BigNum n, out int m>
+=
+ bvlit
+ (.
+ int pos = token.val.IndexOf("bv");
+ string a = token.val.Substring(0, pos);
+ string b = token.val.Substring(pos + 2);
+ try {
+ n = BigNum.FromString(a);
+ m = Convert.ToInt32(b);
+ } catch (FormatException) {
+ SemErr("incorrectly formatted bitvector");
+ n = BigNum.ZERO;
+ m = 0;
+ }
+ .)
+ .
+
+END BoogiePL.
diff --git a/Source/Core/CommandLineOptions.ssc b/Source/Core/CommandLineOptions.ssc new file mode 100644 index 00000000..1a25e283 --- /dev/null +++ b/Source/Core/CommandLineOptions.ssc @@ -0,0 +1,1935 @@ +//-----------------------------------------------------------------------------
+//
+// 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 const string VersionNumber = "2.00";
+ public const string ToolNameBoogie = "Boogie program verifier";
+ public const string ToolNameSpecSharp = "Spec# program verifier";
+ public const string ToolNameDafny = "Dafny program verifier";
+ public const string VersionSuffix = " version " + VersionNumber + ", Copyright (c) 2003-2009, 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<string!>! Files = new List<string!>();
+ public List<string!>! ContractAssemblies = new List<string!>();
+
+ 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 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;
+
+ public List<string!>! Z3DebugTraces = new List<string!>();
+
+ private bool noConsistencyChecks = false;
+ public bool NoConsistencyChecks {
+ get {return !Verify ? true : noConsistencyChecks;}
+ set
+ modifies noConsistencyChecks;
+ {noConsistencyChecks = value;}
+ }
+
+ public string DafnyPrintFile = null;
+
+ 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 ShowTrace = 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 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 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 RemoveEmptyBlocks = true;
+
+ [Rep] public ProverFactory TheProverFactory;
+ public string ProverName;
+ [Peer] public List<string!>! ProverOptions = new List<string!>();
+
+ 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 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; }
+ }
+
+ public int Z3mam = 0;
+ [Peer] public List<string!>! Z3Options = new List<string!>();
+ public bool Z3types = false;
+
+ // 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, MacroLike, Bounded };
+ public Inlining ProcedureInlining = Inlining.MacroLike;
+ public bool PrintInlined = false;
+
+ public enum TypeEncoding { None, Predicates, Arguments, Monomorphic };
+ public TypeEncoding TypeEncodingMethod = TypeEncoding.Predicates;
+
+ public bool Monomorphize = false;
+
+ public bool ReflectAdd = false;
+
+ // 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<string!> procsToCheck = null; // null means "no restriction"
+ [Rep] private List<string!> methodsToTranslateSubstring = null; // null means "no restriction"
+ [Rep] private List<string!> methodsToTranslateMethod = null; // null means "no restriction"
+ [Rep] private List<string!> methodsToTranslateMethodQualified = null; // null means "no restriction"
+ [Rep] private List<string!> methodsToTranslateClass = null; // null means "no restriction"
+ [Rep] private List<string!> methodsToTranslateClassQualified = null; // null means "no restriction"
+ [Rep] private List<string!> methodsToTranslateFile = null; // null means "no restriction"
+ [Rep] private List<string!>! methodsToTranslateExclude = new List<string!>();
+
+ 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.
+ }
+
+
+ /// <summary>
+ /// 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
+ /// </summary>
+ /// <param name="args">Consumed ("captured" and possibly modified) by the method.</param>
+ [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<string!>();
+ }
+ if (ps.ConfirmArgumentCount(1)) {
+ procsToCheck.Add((!)args[ps.i]);
+ }
+ break;
+
+ case "-translate":
+ case "/translate":
+ if (methodsToTranslateSubstring == null) {
+ methodsToTranslateSubstring = new List<string!>();
+ }
+ 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<string!>();
+ }
+ methodsToTranslateMethodQualified.Add(m);
+ } else {
+ if (methodsToTranslateMethod == null) {
+ methodsToTranslateMethod = new List<string!>();
+ }
+ 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<string!>();
+ }
+ methodsToTranslateClassQualified.Add(m);
+ } else {
+ if (methodsToTranslateClass == null) {
+ methodsToTranslateClass = new List<string!>();
+ }
+ methodsToTranslateClass.Add(m);
+ }
+ }
+ break;
+
+ case "-trFile":
+ case "/trFile":
+ if (methodsToTranslateFile == null) {
+ methodsToTranslateFile = new List<string!>();
+ }
+ 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 "-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":
+ if (ps.ConfirmArgumentCount(1))
+ {
+ switch (args[ps.i])
+ {
+ case "0":
+ PrintProverWarnings = ProverWarnings.None;
+ break;
+ case "1":
+ PrintProverWarnings = ProverWarnings.Stdout;
+ break;
+ case "2":
+ PrintProverWarnings = ProverWarnings.Stderr;
+ break;
+ default:
+ ps.Error("Invalid argument \"{0}\" to option {1}", args[ps.i], ps.s);
+ break;
+ }
+ }
+ break;
+
+ case "-env":
+ case "/env":
+ if (ps.ConfirmArgumentCount(1))
+ {
+ switch (args[ps.i])
+ {
+ case "0":
+ ShowEnv = ShowEnvironment.Never;
+ break;
+ case "1":
+ ShowEnv = ShowEnvironment.DuringPrint;
+ break;
+ case "2":
+ ShowEnv = ShowEnvironment.Always;
+ break;
+ default:
+ ps.Error("Invalid argument \"{0}\" to option {1}", args[ps.i], ps.s);
+ break;
+ }
+ }
+ 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, 7);
+ 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 "-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":
+ if (ps.ConfirmArgumentCount(1))
+ {
+ switch (args[ps.i])
+ {
+ case "0":
+ UseSubsumption = SubsumptionOption.Never;
+ break;
+ case "1":
+ UseSubsumption = SubsumptionOption.NotForQuantifiers;
+ break;
+ case "2":
+ UseSubsumption = SubsumptionOption.Always;
+ break;
+ default:
+ ps.Error("Invalid argument \"{0}\" to option {1}", args[ps.i], ps.s);
+ break;
+ }
+ }
+ 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 "-z3DebugTrace":
+ case "/z3DebugTrace":
+ if (ps.ConfirmArgumentCount(1)) {
+ CommandLineOptions.Clo.Z3DebugTraces.Add((!)args[ps.i]);
+ }
+ break;
+
+ case "-inline":
+ case "/inline":
+ if (ps.ConfirmArgumentCount(1)) {
+ switch (args[ps.i])
+ {
+ case "m":
+ case "macrolike":
+ ProcedureInlining = Inlining.MacroLike;
+ break;
+ case "n":
+ case "none":
+ ProcedureInlining = Inlining.None;
+ break;
+ case "b":
+ case "bounded":
+ ProcedureInlining = Inlining.Bounded;
+ break;
+ default:
+ ps.Error("Invalid argument \"{0}\" to option {1}", args[ps.i], ps.s);
+ break;
+ }
+ }
+ 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 "-monomorphize":
+ case "/monomorphize":
+ Monomorphize = true;
+ 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 "-z3mam":
+ case "/z3mam":
+ {
+ int mam = 0;
+ if (ps.GetNumericArgument(ref mam)) {
+ if (mam == 0 || mam == 3 || mam == 4) {
+ Z3mam = mam;
+ } else
+ ps.Error("Invalid argument \"{0}\" to option {1}", args[ps.i], ps.s);
+ }
+ }
+ break;
+
+ case "-z3opt":
+ case "/z3opt":
+ if (ps.ConfirmArgumentCount(1))
+ {
+ Z3Options.Add((!)args[ps.i]);
+ }
+ break;
+
+ case "-z3types":
+ case "/z3types":
+ Z3types = true;
+ 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("noRemoveEmptyBlocks", ref RemoveEmptyBlocks, false) ||
+ ps.CheckBooleanFlag("traceverify", ref TraceVerify) ||
+ ps.CheckBooleanFlag("noConsistencyChecks", ref NoConsistencyChecks, 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)
+ )
+ {
+ // 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 /level
+ if (CheckingLevel == 2) {
+ LoopFrameConditions = 2;
+ } else {
+ LoopFrameConditions = 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;
+ }
+ }
+
+
+
+ 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;
+ }
+
+ /// <summary>
+ /// Returns the file containing "method". Returns null f that information is not available.
+ /// </summary>
+ 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);
+ }
+
+ /// <summary>
+ /// 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".
+ /// </summary>
+ 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;
+ }
+
+ /// <summary>
+ /// 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".
+ /// </summary>
+ 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;
+ }
+ }
+
+ /// <summary>
+ /// 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".
+ /// </summary>
+ 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).
+ With /inline:b, N is the maximal number of inlinings per calling
+ procedure (not depth).
+ With /inline:m (default), N is ignored, and recursive calls to
+ inlined procedure will result in an error.
+ With /inline:n the entire attribute is ignored.
+ With /inline:m methods with /inline are not verified at all.
+
+ {: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.
+
+ ---- 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 <option> is one of
+
+ ---- General options -------------------------------------------------------
+
+ /help : this message
+ /attrHelp : print a message about declaration attributes supported by
+ this implementation
+ /nologo : suppress printing of version number, copyright message
+ /env:<n> : print command line arguments
+ 0 - never, 1 (default) - during BPL print and prover log,
+ 2 - like 1 and also to standard output
+ /wait : await Enter from keyboard before terminating program
+ /xml:<file> : also produce output in XML format to <file>
+
+ ---- Spec# options ---------------------------------------------------------
+
+ If any of the following switches is included, only those methods specified
+ by some switch are translated into BoogiePL. Furthermore, a method is
+ not translated if a [Verify(false)] attribute applies to it or if the
+ flag /trExclude applies (see below).
+ /translate:<str> : include method if its full name contains substring <str>
+ /trMethod:<method> : include method if its name is <method>
+ Format: Name or Class.Name
+ /trClass:<class> : include method if the enclosing class is <class>
+ Format: Name or Qualification.Name
+ /trFile:<filename> : include method if it is contained in file <filename>
+ Format: Filename.ssc
+
+ /trExclude:<str> : exclude method it its full name contains substring <str>
+
+ /c[ontracts]:<file>
+ : apply the contracts from <file> to
+ the referenced assemblies of the input assembly.
+ Note: the contracts for Xyz must be in Xyz.Contracts
+ /methodology:<m> : selects the specification and verification methodology
+ b = boogie (default)
+ vs = visible-state
+ /level:<n> : 0 - reduced checks,
+ 1 - no modifies checking, 2 - full (default)
+ /useUncheckedContracts : generate purity axioms even when the postconditions
+ could not be checked to be sound (this option only for
+ experts and dare devils)
+ /modifiesDefault:<n> :
+ 0 - just what is declared
+ 1 - what is declared plus E.snapshot for every top-level
+ E.f, E.*, E.**, E[i], or E[*] in the modifies clause
+ 2 - (1) but also for nested E's
+ 3 - (1) plus this.snapshot
+ 4 - (2) plus this.snapshot
+ 5 - (default) (1) plus p.* for receiver parameter p not
+ occurring at the top-level of modifies clause as
+ p.f, p.*, p.**, p[i], p[*], or p.0
+ 6 - (5) but for every parameter p
+ /modifiesOnLoop:<n> : 0 - never, 1 - assume only (usual default),
+ 2 - assume and check (default with /level:2)
+ /localModifiesChecks: 0 - check modifies-clause as post-condition of a
+ procedure
+ 1 - check violations of modifies-clause at each
+ assignment and procedure call (default)
+ /loopUnroll:<n> : unroll loops, following up to n back edges (and then some)
+ /noVerifyByDefault : change the default to [Verify(false)]
+ /orderStrength:<n> : 0 (default) - only few properties of subtyping
+ axiomatized,
+ 1 - full strength
+ /summationStrength:<n> : 0 - less applicable triggers in the axioms
+ 1 - (default) default set of axioms for summation-
+ like quantifiers;
+ /arithDistributionAxioms : emit +/* distribution axioms
+ /inductiveMinMax:<n> : 0 (default) - extreme axioms for min/max;
+ 1 - inductive axioms for min/max;
+ 2 - both extreme and inductive axioms for min/max
+ 3,4,5 - like 0,1,2 but adding the plus-over-min/max
+ distribution axiom
+ Modes 1,2,4,5 imply /reflectAdd.
+ /fcoStrength:<n> : adjusts the amount of information encoded about 'first
+ consistent owners'
+ 0 - no FCO axiom, 1 - cheaper but weaker FCO axiom,
+ 2 - pure-method FCO axiom,
+ 3, 4, 5 (default) - like 0,1,2 plus more specific
+ FCO information on pure-method return
+ /ownerModelEncoding:<enc> : s = standard (default)
+ e = experimental
+ t = trivial (implied by /methodology:vs)
+ /printModel:<n> : 0 (default) - do not print Z3's error model
+ 1 - print Z3's error model
+ 2 - print Z3's error model plus reverse mappings
+ 4 - print Z3's error model in a more human readable way
+ /printModelToFile:<file> : print model to <file> instead of console
+ /enhancedErrorMessages:<n> : 0 (default) - no enhanced error messages
+ 1 - Z3 error model enhanced error messages
+
+ ---- Dafny options ---------------------------------------------------------
+
+ Multiple .dfy files supplied on the command line are concatenated into one
+ Dafny program.
+
+ /dprint:<file> : print Dafny program after parsing it
+ (use - as <file> to print to console)
+
+ ---- Boogie options --------------------------------------------------------
+
+ Multiple .bpl files supplied on the command line are concatenated into one
+ Boogie program.
+
+ /proc:<p> : limits which procedures to check
+ /noResolve : parse only
+ /noTypecheck : parse and resolve only
+
+ /print:<file> : print BoogiePL program after parsing it
+ (use - as <file> to print to console)
+ /printWithUniqueIds : print augmented information that uniquely
+ identifies variables
+ /printUnstructured : with /print option, desugars all structured statements
+ /printDesugared : with /print option, desugars calls
+
+ /overlookTypeErrors : skip any implementation with resolution or type
+ checking errors
+
+ ---- Inference options -----------------------------------------------------
+
+ /infer:<flags> : use abstract interpretation to infer invariants
+ The default is /infer:i" // This is not 100% true, as the /infer ALWAYS creates
+ // a multilattice, whereas if nothing is specified then
+ // intervals are isntantiated WITHOUT being embedded in
+ // a multilattice
+ + @"
+ <flags> are as follows (missing <flags> means all)
+ i = intervals
+ c = constant propagation
+ d = dynamic type
+ n = nullness
+ p = polyhedra for linear inequalities
+ s = debug statistics
+ 0..9 = number of iterations before applying a widen (default=0)
+ /loopInvOnDemand : Infer loop invariants on demand
+ /noinfer : turn off the default inference, and overrides the /infer
+ switch on its left
+ /checkInfer : instrument inferred invariants as asserts to be checked by
+ theorem prover
+ /interprocInfer : perform interprocedural inference
+ /contractInfer : perform procedure contract inference
+ /logInfer : print debug output during inference
+ /printInstrumented : print BoogiePL program after it has been
+ instrumented with invariants
+ /Houdini[:<flags>] : perform procedure Houdini
+ c = continue when an error found
+ i = use incremental queries
+ /dbgRefuted : log refuted Houdini candidates to XmlSink
+
+ ---- Debugging and general tracing options ---------------------------------
+
+ /trace : blurt out various debug trace information
+ /traceTimes : output timing information at certain points in the pipeline
+ /log[:method] : Print debug output during translation
+
+ /break[:method] : break into debugger
+
+ ---- Verification-condition generation options -----------------------------
+
+ /noVerify : skip VC generation and invocation of the theorem prover
+ /noRemoveEmptyBlocks : do not remove empty blocks during VC generation
+ /vc:<variety> : n = nested block (default for non-/prover:z3),
+ m = nested block reach,
+ b = flat block, r = flat block reach,
+ s = structured, l = local, d = dag (default with /prover:z3)
+ doomed = doomed
+ /traceverify : print debug output during verification condition generation
+ /subsumption:<c> : apply subsumption to asserted conditions:
+ 0 - never, 1 - not for quantifiers, 2 (default) - always
+ /bv:<bv> : bitvector handling
+ n = none
+ z = native Z3 bitvectors (default)
+ i = unsoundly translate bitvectors to integers
+ /inline:<i> : use inlining strategy <i> for procedures with the :inline
+ attribute, see /attrHelp for details:
+ n = none
+ m = macrolike (default)
+ b = bounded
+ /printInlined : print the implementation after inlining calls to
+ procedures with the :inline attribute (works with /inline)
+ /smoke : Soundness Smoke Test: try to stick assert false; in some
+ places in the BPL and see if we can still prove it
+ /smokeTimeout:<n> : Timeout, in seconds, for a single theorem prover
+ invocation during smoke test, defaults to 10.
+ /causalImplies : Translate Boogie's A ==> B into prover's A ==> A && B.
+ /typeEncoding:<m> : how to encode types when sending VC to theorem prover
+ n = none (unsound)
+ p = predicates (default)
+ a = arguments
+ /reflectAdd In the VC, generate an auxiliary symbol, elsewhere defined
+ to be +, instead of +.
+
+ ---- Verification-condition splitting --------------------------------------
+
+ /vcsMaxCost:<f> : VC will not be split unless the cost of a VC exceeds this
+ number, defaults to 2000.0. This does NOT apply in the
+ keep-going mode after first round of splitting.
+ /vcsMaxSplits:<n> : Maximal number of VC generated per method. In keep
+ going mode only applies to the first round.
+ Defaults to 1.
+ /vcsMaxKeepGoingSplits:<n> : If set to more than 1, activates the keep
+ going mode, where after the first round of splitting,
+ VCs that timed out are split into <n> pieces and retried
+ until we succeed proving them, or there is only one
+ assertion on a single path and it timeouts (in which
+ case error is reported for that assertion).
+ Defaults to 1.
+ /vcsKeepGoingTimeout:<n> : Timeout in seconds for a single theorem prover
+ invocation in keep going mode, except for the final
+ single-assertion case. Defaults to 1s.
+ /vcsFinalAssertTimeout:<n> : Timeout in seconds for the single last
+ assertion in the keep going mode. Defaults to 30s.
+ /vcsPathJoinMult:<f> : If more than one path join at a block, by how much
+ multiply the number of paths in that block, to accomodate
+ for the fact that the prover will learn something on one
+ paths, before proceeding to another. Defaults to 0.8.
+ /vcsPathCostMult:<f1>
+ /vcsAssumeMult:<f2> : The cost of a block is
+ (<assert-cost> + <f2>*<assume-cost>)*(1.0 + <f1>*<entering-paths>)
+ <f1> defaults to 1.0, <f2> defaults to 0.01.
+ The cost of a single assertion or assumption is
+ currently always 1.0.
+ /vcsPathSplitMult:<f> : If the best path split of a VC of cost A is into
+ VCs of cost B and C, then the split is applied if
+ A >= <f>*(B+C), otherwise assertion splitting will be
+ applied. Defaults to 0.5 (always do path splitting if
+ possible), set to more to do less path splitting
+ and more assertion splitting.
+ /vcsDumpSplits For split #n dump split.n.dot and split.n.bpl.
+ Warning: Affects error reporting.
+ /vcsCores:<n> : Try to verify <n> VCs at once. Defaults to 1.
+
+ ---- Prover options --------------------------------------------------------
+
+ /errorLimit:<num> : Limit the number of errors produced for each procedure
+ (default is 5, some provers may support only 1)
+ /timeLimit:<num> : Limit the number of seconds spent trying to verify
+ each procedure
+ /errorTrace:<n> : 0 - no Trace labels in the error output,
+ 1 (default) - include useful Trace labels in error output,
+ 2 - include all Trace labels in the error output
+ /vcBrackets:<b> : bracket odd-charactered identifier names with |'s. <b> is:
+ 0 - no (default with /prover:Z3),
+ 1 - yes (default with /prover:Simplify)
+ /prover:<tp> : use theorem prover <tp>, where <tp> is either the name of
+ a DLL containing the prover interface located in the
+ Boogie directory, or a full path to a DLL containing such
+ an interface. The standard interfaces shipped include:
+ Z3 (default)
+ Simplify
+ SMTLib (only writes to a file)
+ ContractInference (uses Z3)
+ Z3api (Z3 using Managed .NET API)
+ /proverOpt:KEY[=VALUE] : Provide a prover-specific option.
+ V1 - enables Z3 version 1.3 (with /prover:z3).
+ /proverLog:<file> : Log input for the theorem prover. Like filenames
+ supplied as arguments to other options, <file> can use the
+ following macros:
+ @TIME@' expands to the current time
+ @PREFIX@ expands to the concatenation of strings given
+ by /logPrefix options
+ @FILE@ expands to the last filename specified on the
+ command line
+ In addition, /proverLog can also use the macro '@PROC@',
+ which causes there to be one prover log file per
+ verification condition, and the macro then expands to the
+ name of the procedure that the verification condition is
+ for.
+ /logPrefix:<str> : Defines the expansion of the macro '@PREFIX@', which can
+ be used in various filenames specified by other options.
+ /proverLogAppend : Append (not overwrite) the specified prover log file
+ /proverWarnings : 0 (default) - don't print, 1 - print to stdout,
+ 2 - print to stderr
+ /proverMemoryLimit:<num> : Limit on the virtual memory for prover before
+ restart in MB (default:100MB)
+ /restartProver : Restart the prover after each query
+ /proverShutdownLimit<num> : Time between closing the stream to the prover and
+ killing the prover process (default: 0s)
+ /platform:<ptype>,<location>
+ : ptype = v11,v2,cli1
+ : location = platform libraries directory
+
+ Simplify specific options:
+ /simplifyMatchDepth:<num> : Set Simplify prover's matching depth limit
+
+ Z3 specific options:
+ /z3mam:<num> : Z3 matching engine: 0 (default) - eager instantiation,
+ 3 - lazy instantiation,
+ 4 - eager instantiation w/ matching-loop breaking heuristic
+ /z3opt:<arg> : specify additional Z3 options
+ /z3DebugTrace:<arg> : z3 command line options so that Z3 generates debug
+ tracing
+
+ SMT-Lib specific options:
+ /smtOutput:<file> : Path and basename to which the prover output is
+ written (default: boogie-vc-@PROC@.smt). The same
+ macros as in /proverLog can be used.
+
+ /z3types : generate multi-sorted VC that make use of Z3 types
+");
+ }
+ }
+}
diff --git a/Source/Core/Core.sscproj b/Source/Core/Core.sscproj new file mode 100644 index 00000000..a1f8e9ca --- /dev/null +++ b/Source/Core/Core.sscproj @@ -0,0 +1,212 @@ +<?xml version="1.0" encoding="utf-8"?>
+<VisualStudioProject>
+ <XEN ProjectType="Local"
+ SchemaVersion="1.0"
+ Name="Core"
+ ProjectGuid="47bc34f1-a173-40be-84c2-9332b4418387"
+ >
+ <Build>
+ <Settings ApplicationIcon=""
+ AssemblyName="Core"
+ OutputType="Library"
+ RootNamespace="Core"
+ StartupObject=""
+ TargetPlatform="v2"
+ TargetPlatformLocation=""
+ ShadowedAssembly=""
+ StandardLibraryLocation=""
+ >
+ <Config Name="Debug"
+ AllowUnsafeBlocks="False"
+ BaseAddress="285212672"
+ CheckForOverflowUnderflow="False"
+ ConfigurationOverrideFile=""
+ DefineConstants="DEBUG;TRACE;WHIDBEY"
+ DocumentationFile=""
+ DebugSymbols="True"
+ FileAlignment="4096"
+ IncrementalBuild="True"
+ Optimize="False"
+ OutputPath="bin\Debug"
+ RegisterForComInterop="False"
+ RemoveIntegerChecks="false"
+ TreatWarningsAsErrors="False"
+ WarningLevel="4"
+ RunProgramVerifier="False"
+ ProgramVerifierCommandLineOptions=""
+ ReferenceTypesAreNonNullByDefault="False"
+ RunProgramVerifierWhileEditing="False"
+ AllowPointersToManagedStructures="False"
+ CheckContractAdmissibility="True"
+ CheckPurity="False"
+ DisableAssumeChecks="False"
+ DisableDefensiveChecks="False"
+ DisableGuardedClassesChecks="False"
+ DisableInternalChecks="False"
+ DisableInternalContractsMetadata="False"
+ DisablePublicContractsMetadata="False"
+ DebugMode="Project"
+ StartProgram=""
+ StartURL=""
+ StartPage=""
+ UseIE="False"
+ EnableRemoteDebugging="False"
+ RemoteDebugMachine=""
+ />
+ <Config Name="Release"
+ AllowUnsafeBlocks="false"
+ BaseAddress="285212672"
+ CheckForOverflowUnderflow="false"
+ ConfigurationOverrideFile=""
+ DefineConstants="TRACE;WHIDBEY"
+ DocumentationFile=""
+ DebugSymbols="false"
+ FileAlignment="4096"
+ IncrementalBuild="false"
+ Optimize="true"
+ OutputPath="bin\release"
+ RegisterForComInterop="false"
+ RemoveIntegerChecks="false"
+ TreatWarningsAsErrors="True"
+ WarningLevel="4"
+ />
+ </Settings>
+ <References>
+ <Reference Name="Mscorlib.Contracts"
+ AssemblyName="Mscorlib.Contracts"
+ Private="false"
+ HintPath="../../Binaries/Mscorlib.Contracts.dll"
+ />
+ <Reference Name="System"
+ AssemblyName="System"
+ Private="false"
+ />
+ <Reference Name="System.Compiler.Framework"
+ AssemblyName="System.Compiler.Framework"
+ Private="true"
+ HintPath="../../Binaries/System.Compiler.Framework.dll"
+ />
+ <Reference Name="AIFramework"
+ Project="{24B55172-AD8B-47D1-8952-5A95CFDB9B31}"
+ Private="true"
+ />
+ <Reference Name="System.Compiler"
+ AssemblyName="System.Compiler"
+ Private="true"
+ HintPath="../../Binaries/System.Compiler.dll"
+ />
+ <Reference Name="System.Compiler.Contracts"
+ AssemblyName="System.Compiler.Contracts"
+ Private="false"
+ HintPath="../../Binaries/System.Compiler.Contracts.dll"
+ />
+ <Reference Name="Graph"
+ Project="{4C28FB90-630E-4B55-A937-11A011B79765}"
+ Private="true"
+ />
+ <Reference Name="System.XML"
+ AssemblyName="System.XML"
+ Private="false"
+ />
+ <Reference Name="System.Xml.Contracts"
+ AssemblyName="System.Xml.Contracts"
+ Private="false"
+ HintPath="../../Binaries/System.Xml.Contracts.dll"
+ />
+ <Reference Name="FSharp.Core"
+ AssemblyName="FSharp.Core"
+ Private="false"
+ HintPath="../../Binaries/FSharp.Core.dll"
+ />
+ <Reference Name="Basetypes"
+ Project="{0C692837-77EC-415F-BF04-395E3ED06E9A}"
+ Private="true"
+ />
+ </References>
+ </Build>
+ <Files>
+ <Include>
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="Absy.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="..\version.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="Duplicator.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="OOLongUtil.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="PureCollections.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="ResolutionContext.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="StandardVisitor.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="Util.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="CommandLineOptions.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="scanner.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="parser.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="GraphAlgorithms.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="AbsyType.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="AbsyCmd.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="AbsyExpr.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="Xml.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="Inline.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="LoopUnroll.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="VCExp.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="TypeAmbiguitySeeker.ssc"
+ />
+ </Include>
+ </Files>
+ </XEN>
+</VisualStudioProject>
\ No newline at end of file diff --git a/Source/Core/Duplicator.ssc b/Source/Core/Duplicator.ssc new file mode 100644 index 00000000..f3330739 --- /dev/null +++ b/Source/Core/Duplicator.ssc @@ -0,0 +1,348 @@ +//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+//---------------------------------------------------------------------------------------------
+// BoogiePL - Duplicator.cs
+//---------------------------------------------------------------------------------------------
+
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Microsoft.Boogie
+{
+ public class Duplicator : StandardVisitor
+ {
+ public override Absy! Visit(Absy! node)
+ {
+ node = base.Visit(node);
+ return node;
+ }
+
+ public override Cmd! VisitAssertCmd(AssertCmd! node)
+ {
+ return base.VisitAssertCmd ((AssertCmd)node.Clone());
+ }
+ public override Cmd! VisitAssignCmd(AssignCmd! node)
+ {
+ AssignCmd clone = (AssignCmd)node.Clone();
+ clone.Lhss = new List<AssignLhs!>(clone.Lhss);
+ clone.Rhss = new List<Expr!>(clone.Rhss);
+ return base.VisitAssignCmd (clone);
+ }
+ public override Cmd! VisitAssumeCmd(AssumeCmd! node)
+ {
+ return base.VisitAssumeCmd ((AssumeCmd)node.Clone());
+ }
+ public override AtomicRE! VisitAtomicRE(AtomicRE! node)
+ {
+ return base.VisitAtomicRE ((AtomicRE)node.Clone());
+ }
+ public override Axiom! VisitAxiom(Axiom! node)
+ {
+ return base.VisitAxiom ((Axiom)node.Clone());
+ }
+ public override Type! VisitBasicType(BasicType! node)
+ {
+ // do /not/ clone the type recursively
+ return (BasicType)node.Clone();
+ }
+ public override Block! VisitBlock(Block! node)
+ {
+ return base.VisitBlock ((Block)node.Clone());
+ }
+ public override BlockSeq! VisitBlockSeq(BlockSeq! blockSeq)
+ {
+ return base.VisitBlockSeq (blockSeq);
+ }
+ public override BoundVariable! VisitBoundVariable(BoundVariable! node)
+ {
+ return base.VisitBoundVariable ((BoundVariable)node.Clone());
+ }
+ public override Type! VisitBvType(BvType! node)
+ {
+ // do /not/ clone the type recursively
+ return (BvType)node.Clone();
+ }
+ public override Cmd! VisitCallCmd(CallCmd! node)
+ {
+ CallCmd! newNode = (CallCmd)node.Clone();
+ newNode.Ins = new List<Expr> (node.Ins);
+ newNode.Outs = new List<IdentifierExpr> (node.Outs);
+ return base.VisitCallCmd (node);
+ }
+ public override Choice! VisitChoice(Choice! node)
+ {
+ return base.VisitChoice ((Choice)node.Clone());
+ }
+ public override CmdSeq! VisitCmdSeq(CmdSeq! cmdSeq)
+ {
+ return base.VisitCmdSeq (cmdSeq);
+ }
+ public override Constant! VisitConstant(Constant! node)
+ {
+ return base.VisitConstant ((Constant)node.Clone());
+ }
+ public override CtorType! VisitCtorType(CtorType! node)
+ {
+ // do /not/ clone the type recursively
+ return (CtorType)node.Clone();
+ }
+ public override Declaration! VisitDeclaration(Declaration! node)
+ {
+ return base.VisitDeclaration ((Declaration)node.Clone());
+ }
+ public override List<Declaration!>! VisitDeclarationList(List<Declaration!>! declarationList)
+ {
+ return base.VisitDeclarationList(declarationList);
+ }
+ public override DeclWithFormals! VisitDeclWithFormals(DeclWithFormals! node)
+ {
+ return base.VisitDeclWithFormals ((DeclWithFormals)node.Clone());
+ }
+ public override ExistsExpr! VisitExistsExpr(ExistsExpr! node)
+ {
+ return base.VisitExistsExpr ((ExistsExpr)node.Clone());
+ }
+ public override Expr! VisitExpr(Expr! node)
+ {
+ return base.VisitExpr ((Expr)node.Clone());
+ }
+ public override ExprSeq! VisitExprSeq(ExprSeq! list)
+ {
+ return base.VisitExprSeq (new ExprSeq(list));
+ }
+ public override ForallExpr! VisitForallExpr(ForallExpr! node)
+ {
+ return base.VisitForallExpr ((ForallExpr)node.Clone());
+ }
+ public override Formal! VisitFormal(Formal! node)
+ {
+ return base.VisitFormal ((Formal)node.Clone());
+ }
+ public override Function! VisitFunction(Function! node)
+ {
+ return base.VisitFunction ((Function)node.Clone());
+ }
+ public override GlobalVariable! VisitGlobalVariable(GlobalVariable! node)
+ {
+ return base.VisitGlobalVariable ((GlobalVariable)node.Clone());
+ }
+ public override GotoCmd! VisitGotoCmd(GotoCmd! node)
+ {
+ return base.VisitGotoCmd ((GotoCmd)node.Clone());
+ }
+ public override Cmd! VisitHavocCmd(HavocCmd! node)
+ {
+ return base.VisitHavocCmd ((HavocCmd)node.Clone());
+ }
+ public override Expr! VisitIdentifierExpr(IdentifierExpr! node)
+ {
+ return base.VisitIdentifierExpr ((IdentifierExpr) node.Clone());
+ }
+ public override IdentifierExprSeq! VisitIdentifierExprSeq(IdentifierExprSeq! identifierExprSeq)
+ {
+ return base.VisitIdentifierExprSeq (new IdentifierExprSeq(identifierExprSeq));
+ }
+ public override Implementation! VisitImplementation(Implementation! node)
+ {
+ return base.VisitImplementation ((Implementation)node.Clone());
+ }
+ public override LiteralExpr! VisitLiteralExpr(LiteralExpr! node)
+ {
+ return base.VisitLiteralExpr ((LiteralExpr)node.Clone());
+ }
+ public override LocalVariable! VisitLocalVariable(LocalVariable! node)
+ {
+ return base.VisitLocalVariable ((LocalVariable)node.Clone());
+ }
+ public override AssignLhs! VisitMapAssignLhs(MapAssignLhs! node)
+ {
+ return base.VisitMapAssignLhs ((MapAssignLhs)node.Clone());
+ }
+ public override MapType! VisitMapType(MapType! node)
+ {
+ // do /not/ clone the type recursively
+ return (MapType)node.Clone();
+ }
+ public override Expr! VisitNAryExpr(NAryExpr! node)
+ {
+ return base.VisitNAryExpr ((NAryExpr)node.Clone());
+ }
+ public override Expr! VisitOldExpr(OldExpr! node)
+ {
+ return base.VisitOldExpr ((OldExpr) node.Clone());
+ }
+ public override Procedure! VisitProcedure(Procedure! node)
+ {
+ return base.VisitProcedure ((Procedure)node.Clone());
+ }
+ public override Program! VisitProgram(Program! node)
+ {
+ return base.VisitProgram ((Program) node.Clone());
+ }
+ public override QuantifierExpr! VisitQuantifierExpr(QuantifierExpr! node)
+ {
+ return base.VisitQuantifierExpr ((QuantifierExpr) node.Clone());
+ }
+ public override Cmd! VisitRE(RE! node)
+ {
+ return base.VisitRE ((RE) node.Clone());
+ }
+ public override RESeq! VisitRESeq(RESeq! reSeq)
+ {
+ return base.VisitRESeq (new RESeq(reSeq));
+ }
+ public override ReturnCmd! VisitReturnCmd(ReturnCmd! node)
+ {
+ return base.VisitReturnCmd ((ReturnCmd) node.Clone());
+ }
+ public override Sequential! VisitSequential(Sequential! node)
+ {
+ return base.VisitSequential ((Sequential) node.Clone());
+ }
+ public override AssignLhs! VisitSimpleAssignLhs(SimpleAssignLhs! node)
+ {
+ return base.VisitSimpleAssignLhs ((SimpleAssignLhs)node.Clone());
+ }
+ public override Cmd! VisitStateCmd(StateCmd! node)
+ {
+ return base.VisitStateCmd ((StateCmd)node.Clone());
+ }
+ public override TransferCmd! VisitTransferCmd(TransferCmd! node)
+ {
+ return base.VisitTransferCmd ((TransferCmd) node.Clone());
+ }
+ public override Trigger! VisitTrigger(Trigger! node)
+ {
+ return base.VisitTrigger ((Trigger) node.Clone());
+ }
+ public override Type! VisitType(Type! node)
+ {
+ // do /not/ clone the type recursively
+ return (Type)node.Clone();
+ }
+ public override TypedIdent! VisitTypedIdent(TypedIdent! node)
+ {
+ return base.VisitTypedIdent ((TypedIdent) node.Clone());
+ }
+ public override Variable! VisitVariable(Variable! node)
+ {
+ return node;
+ }
+ public override VariableSeq! VisitVariableSeq(VariableSeq! variableSeq)
+ {
+ return base.VisitVariableSeq (new VariableSeq(variableSeq));
+ }
+ public override Cmd! VisitAssertRequiresCmd(AssertRequiresCmd! node)
+ {
+ return base.VisitAssertRequiresCmd((AssertRequiresCmd)node.Clone());
+ }
+ public override Cmd! VisitAssertEnsuresCmd(AssertEnsuresCmd! node)
+ {
+ return base.VisitAssertEnsuresCmd((AssertEnsuresCmd)node.Clone());
+ }
+ public override Ensures! VisitEnsures(Ensures! node)
+ {
+ return base.VisitEnsures((Ensures)node.Clone());
+ }
+ public override Requires! VisitRequires(Requires! node)
+ {
+ return base.VisitRequires((Requires)node.Clone());
+ }
+ }
+
+
+ #region A duplicator that also does substitutions for a set of variables
+ /// <summary>
+ /// A substitution is a partial mapping from Variables to Exprs.
+ /// </summary>
+ public delegate Expr/*?*/ Substitution(Variable! v);
+
+ public static class Substituter
+ {
+ public static Substitution! SubstitutionFromHashtable(Hashtable/*Variable!->Expr!*/! map)
+ {
+ // TODO: With Whidbey, could use anonymous functions.
+ return new Substitution(new CreateSubstitutionClosure(map).Method);
+ }
+ private sealed class CreateSubstitutionClosure
+ {
+ Hashtable/*Variable!->Expr!*/! map;
+ public CreateSubstitutionClosure(Hashtable/*Variable!->Expr!*/! map) { this.map = map; base(); }
+ public Expr/*?*/ Method(Variable! v) { return (Expr) map[v]; }
+ }
+
+ /// <summary>
+ /// Apply a substitution to an expression. Any variables not in domain(subst)
+ /// is not changed. The substitutions applies within the "old", but the "old"
+ /// expression remains.
+ /// </summary>
+ public static Expr! Apply(Substitution! subst, Expr! expr)
+ {
+ return (Expr) new NormalSubstituter(subst).Visit(expr);
+ }
+
+ /// <summary>
+ /// Apply a substitution to an expression replacing "old" expressions.
+ /// Outside "old" expressions, the substitution "always" is applied; any variable not in
+ /// domain(always) is not changed. Inside "old" expressions, apply map "oldExpr" to
+ /// variables in domain(oldExpr), apply map "always" to variables in
+ /// domain(always)-domain(oldExpr), and leave variable unchanged otherwise.
+ /// </summary>
+ public static Expr! ApplyReplacingOldExprs(Substitution! always, Substitution! forold, Expr! expr)
+ {
+ return (Expr) new ReplacingOldSubstituter(always, forold).Visit(expr);
+ }
+
+ private sealed class NormalSubstituter : Duplicator
+ {
+ private readonly Substitution! subst;
+ public NormalSubstituter(Substitution! subst) { this.subst = subst; base(); }
+
+ public override Expr! VisitIdentifierExpr(IdentifierExpr! node)
+ {
+ Expr/*?*/ e = subst((!)node.Decl);
+ return e == null ? base.VisitIdentifierExpr(node) : e;
+ }
+ }
+
+ private sealed class ReplacingOldSubstituter : Duplicator
+ {
+ private readonly Substitution! always;
+ private readonly Substitution! forold;
+ public ReplacingOldSubstituter(Substitution! always, Substitution! forold)
+ { this.always = always; this.forold = forold; base(); }
+
+ private bool insideOldExpr = false;
+
+ public override Expr! VisitIdentifierExpr(IdentifierExpr! node)
+ {
+ Expr/*?*/ e = null;
+
+ if (insideOldExpr)
+ {
+ e = forold((!)node.Decl);
+ }
+
+ if (e == null)
+ {
+ e = always((!)node.Decl);
+ }
+
+ return e == null ? base.VisitIdentifierExpr(node) : e;
+ }
+
+ public override Expr! VisitOldExpr(OldExpr! node)
+ {
+ bool previouslyInOld = insideOldExpr;
+ insideOldExpr = true;
+ Expr! e = (Expr!)this.Visit(node.Expr);
+ insideOldExpr = previouslyInOld;
+ return e;
+ }
+ }
+ }
+ #endregion
+}
\ No newline at end of file diff --git a/Source/Core/Graph.as b/Source/Core/Graph.as new file mode 100644 index 00000000..1466c341 --- /dev/null +++ b/Source/Core/Graph.as @@ -0,0 +1,352 @@ +using System.Collections;
+namespace Graphing;
+
+type Node = object;
+type Edge = <Node,Node>;
+
+class PreHeader {
+ Node myHeader;
+ PreHeader(Node h) { myHeader = h; }
+
+ public override string ToString() { return "#" + myHeader.ToString(); }
+}
+
+public class Graph {
+ private Set<Edge> es;
+ private Set<Node> ns;
+ private Node source;
+ private bool reducible;
+ private Set<Node> headers;
+ private Map<Node,Set<Node>> backEdgeNodes;
+ private Map<Edge,Set<Node>> naturalLoops;
+ private Map<Node,Set<Node>> dominatorMap;
+ private Map<Node,Set<Node>> immediateDominatorMap;
+
+ public Graph(Set<Edge> edges)
+ {
+ es = edges;
+ ns = Set<Node>{ x : <x,y> in es } + Set<Node>{ y : <x,y> in es };
+ }
+ public Graph()
+ { es = Set<Edge>{}; ns = Set<Node>{}; }
+
+ public void AddSource(Node x)
+ {
+ ns += Set<Node>{x};
+ source = x;
+ }
+ public void AddEdge(Node source, Node dest)
+ {
+ es += Set<Edge>{<source,dest>};
+ ns += Set<Node>{source, dest};
+ }
+
+ public Set<Node> Nodes { get { return ns; } }
+ public Set<Edge> Edges { get { return es; } }
+
+ public bool Edge(Node x, Node y) { return <x,y> in es; }
+ Set<Node> predecessors(Node n)
+ {
+ Set<Node> result = Set{ x : x in Nodes, Edge(x,n) };
+ return result;
+ }
+ public override string ToString() { return es.ToString(); }
+
+ public IEnumerable TopologicalSort()
+ {
+ <bool,Seq<Node>> <res,ns> = TopSort(this);
+ return res ? ns : null;
+ }
+ public void ComputeLoops()
+ {
+ <bool, Set<Node>, Map<Node,Set<Node>>, Map<Edge,Set<Node>>>
+ <reducible,headers,backEdgeNodes,naturalLoops> = Reducible(this,this.source);
+ this.reducible = reducible;
+ this.headers = headers;
+ this.backEdgeNodes = backEdgeNodes;
+ this.naturalLoops = naturalLoops;
+ return;
+ }
+ public bool Reducible { get { return reducible; } }
+ public IEnumerable Headers { get { return headers; } }
+ public IEnumerable BackEdgeNodes(Node h) { return h in backEdgeNodes ? backEdgeNodes[h] : null; }
+ public IEnumerable NaturalLoops(Node header, Node backEdgeNode)
+ { Edge e = <backEdgeNode,header>; return e in naturalLoops ? naturalLoops[e] : null; }
+ public bool Acyclic { get { return Acyclic(this,this.source); } }
+ public Map<Node,Set<Node>> DominatorMap
+ {
+ get {
+ if (dominatorMap == null) dominatorMap = ComputeDominators(this, source);
+ return dominatorMap;
+ }
+ }
+ public Map<Node,Set<Node>> ImmediateDominatorMap
+ {
+ get {
+ if (immediateDominatorMap == null)
+ {
+ immediateDominatorMap = Map{};
+ foreach(Node y in Nodes)
+ {
+ Set<Node> nodesThatYDominates = Set{ x : x in Nodes, x != y && (y in DominatorMap[x]) };
+ Set<Node> immediateDominatees = Set{ x : x in nodesThatYDominates,
+ !(Exists{ v != y && v != x && (v in DominatorMap[x]) : v in nodesThatYDominates })
+ };
+ immediateDominatorMap[y] = immediateDominatees;
+ }
+ }
+ return immediateDominatorMap;
+ }
+ }
+ public Set<Node> ImmediatelyDominatedBy(Node n) { return ImmediateDominatorMap[n]; }
+
+}
+
+// From AsmL distribution example: TopologicalSort
+<bool,Seq<Node>> TopSort(Graph g)
+{
+ Seq<Node> S = Seq{};
+ Set<Node> V = g.Nodes;
+ bool change = true;
+ while ( change )
+ {
+ change = false;
+ Set<Node> X = V - ((Set<Node>) S);
+ if ( X != Set{} )
+ {
+ Node temp = Choose{ v : v in X, !(Exists{ g.Edge(u,v) : u in X }) ifnone null };
+ if ( temp == null )
+ {
+ return <false,Seq<Node>{}>;
+ }
+ else if ( temp != Seq<Node>{} )
+ {
+ S += Seq{temp};
+ change = true;
+ }
+ }
+ }
+ return <true,S>;
+}
+
+bool Acyclic(Graph g, Node source)
+{
+ <bool,Seq<Node>> <acyc,xs> = TopSort(g);
+ return acyc;
+}
+
+//
+// [Dragon, pp. 670--671]
+// returns map D s.t. d in D(n) iff d dom n
+//
+Map<Node,Set<Node>> ComputeDominators(Graph g, Node source) {
+ Set<Node> N = g.Nodes;
+ Set<Node> nonSourceNodes = N - Set{source};
+ Map<Node,Set<Node>> D = Map{};
+ D[source] = Set<Node>{ source };
+ foreach (Node n in nonSourceNodes)
+ {
+ D[n] = N;
+ }
+ bool change = true;
+ while ( change )
+ {
+ change = false;
+ foreach (Node n in nonSourceNodes)
+ {
+ Set<Set<Node>> allPreds = Set{ D[p] : p in g.predecessors(n) };
+ Set<Node> temp = Set<Node>{ n } + BigIntersect(allPreds);
+ if ( temp != D[n] )
+ {
+ change = true;
+ D[n] = temp;
+ }
+ }
+ }
+ return D;
+}
+
+// [Dragon, Fig. 10.15, p. 604. Algorithm for constructing the natural loop.]
+Set<Node> NaturalLoop(Graph g, Edge backEdge)
+{
+ <Node,Node> <n,d> = backEdge;
+ Seq<Node> stack = Seq{};
+ Set<Node> loop = Set{ d };
+ if ( n != d ) // then n is not in loop
+ {
+ loop += Set{ n };
+ stack = Seq{ n } + stack; // push n onto stack
+ }
+ while ( stack != Seq{} ) // not empty
+ {
+ Node m = Head(stack);
+ stack = Tail(stack); // pop stack
+ foreach (Node p in g.predecessors(m))
+ {
+ if ( !(p in loop) )
+ {
+ loop += Set{ p };
+ stack = Seq{ p } + stack; // push p onto stack
+ }
+ }
+ }
+ return loop;
+}
+
+// [Dragon, p. 606]
+<bool, Set<Node>, Map<Node,Set<Node>>, Map<Edge,Set<Node>>>
+ Reducible(Graph g, Node source) {
+ // first, compute the dom relation
+ Map<Node,Set<Node>> D = g.DominatorMap;
+ return Reducible(g,source,D);
+}
+
+// [Dragon, p. 606]
+<bool, Set<Node>, Map<Node,Set<Node>>, Map<Edge,Set<Node>>>
+ Reducible(Graph g, Node source, Map<Node,Set<Node>> DomRelation) {
+
+ Set<Edge> edges = g.Edges;
+ Set<Edge> backEdges = Set{};
+ Set<Edge> nonBackEdges = Set{};
+ foreach (Edge e in edges)
+ {
+ <Node,Node> <x,y> = e; // so there is an edge from x to y
+ if ( y in DomRelation[x] ) // y dom x: which means y dominates x
+ {
+ backEdges += Set{ e };
+ }
+ else
+ {
+ nonBackEdges += Set{ e };
+ }
+ }
+ if ( !Acyclic(new Graph(nonBackEdges), source) )
+ {
+ return <false,Set<Node>{},Map<Node,Set<Node>>{},Map<Edge,Set<Node>>{}>;
+ }
+ else
+ {
+ Set<Node> headers = Set{ d : <n,d> in backEdges };
+ Map<Node,Set<Node>> backEdgeNodes = Map{ h -> bs : h in headers, bs = Set<Node>{ b : <b,x> in backEdges, x == h } };
+ Map<Edge,Set<Node>> naturalLoops = Map{ e -> NaturalLoop(g,e) : e in backEdges };
+
+ return <true, headers, backEdgeNodes, naturalLoops>;
+ }
+}
+
+// [Dragon, p. 606]
+bool OldReducible(Graph g, Node source) {
+ // first, compute the dom relation
+ Map<Node,Set<Node>> D = ComputeDominators(g, source);
+ return OldReducible(g,source,D);
+}
+
+// [Dragon, p. 606]
+bool OldReducible(Graph g, Node source, Map<Node,Set<Node>> DomRelation) {
+
+ Set<Edge> edges = g.Edges;
+ Set<Edge> backEdges = Set{};
+ Set<Edge> nonBackEdges = Set{};
+ foreach (Edge e in edges)
+ {
+ <Node,Node> <x,y> = e;
+ if ( y in DomRelation[x] ) // y dom x
+ {
+ backEdges += Set{ e };
+ }
+ else
+ {
+ nonBackEdges += Set{ e };
+ }
+ }
+ WriteLine("backEdges: " + backEdges);
+ WriteLine("nonBackEdges: " + nonBackEdges);
+ if ( Acyclic(new Graph(nonBackEdges), source) )
+ {
+ foreach(Edge e in backEdges)
+ {
+ Set<Node> naturalLoop = NaturalLoop(g,e);
+ WriteLine("Natural loop for back edge '" + e + "' is: " + naturalLoop);
+ }
+ Set<Node> headers = Set{ d : <n,d> in backEdges };
+ WriteLine("Loop headers = " + headers);
+
+ edges -= backEdges; // this cuts all of the back edges
+ foreach (Node h in headers)
+ {
+ Set<Edge> bs = Set{ <n,d> : <n,d> in backEdges, d == h };
+ Set<Node> preds = Set<Node>{ p : <p,y> in edges, y == h };
+ Node preheader = new PreHeader(h);
+ edges += Set{ <preheader,h> };
+ foreach (Node p in preds)
+ {
+ edges -= Set{ <p,h> };
+ edges += Set{ <p,preheader> };
+ }
+ }
+ Graph newGraph = new Graph(edges);
+ WriteLine("transformed graph = " + newGraph);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+void Main()
+{
+ Graph g;
+ Map<Node,Set<Node>> D;
+/*
+ g = new Graph(Set<Edge>{ <1,2>, <1,3>, <2,3> });
+ g.AddSource(1);
+ Map<Node,Set<Node>> doms = ComputeDominators(g,1);
+ WriteLine(doms);
+*/
+ g = new Graph(Set<Edge>{
+ <1,2>, <1,3>,
+ <2,3>,
+ <3,4>,
+ <4,3>, <4,5>, <4,6>,
+ <5,7>,
+ <6,7>,
+ <7,4>, <7,8>,
+ <8,3>, <8,9>, <8,10>,
+ <9,1>,
+ <10,7>
+ });
+ g.AddSource(1);
+ WriteLine("G = " + g);
+ D = ComputeDominators(g,1);
+ WriteLine("Dom relation: " + D);
+ WriteLine("G's Dominator Map = " + g.DominatorMap);
+ WriteLine("G's Immediate Dominator Map = " + g.ImmediateDominatorMap);
+ WriteLine("G is reducible: " + OldReducible(g,1,D));
+ g.ComputeLoops();
+
+ WriteLine("");
+
+ g = new Graph(Set<Edge>{ <1,2>, <1,3>, <2,3>, <3,2> });
+ g.AddSource(1);
+ WriteLine("G = " + g);
+ D = ComputeDominators(g,1);
+ WriteLine("Dom relation: " + D);
+ WriteLine("G's Dominator Map = " + g.DominatorMap);
+ WriteLine("G's Immediate Dominator Map = " + g.ImmediateDominatorMap);
+ WriteLine("G is reducible: " + OldReducible(g,1,D));
+ g.ComputeLoops();
+
+ WriteLine("");
+
+ g = new Graph(Set<Edge>{ <1,2>, <2,3>, <2,4>, <3,2> });
+ g.AddSource(1);
+ WriteLine("G = " + g);
+ WriteLine("G's Dominator Map = " + g.DominatorMap);
+ WriteLine("G's Immediate Dominator Map = " + g.ImmediateDominatorMap);
+// D = ComputeDominators(g,1);
+// WriteLine("Dom relation: " + D);
+// WriteLine("G is reducible: " + OldReducible(g,1,D));
+ g.ComputeLoops();
+
+}
\ No newline at end of file diff --git a/Source/Core/GraphAlgorithms.ssc b/Source/Core/GraphAlgorithms.ssc new file mode 100644 index 00000000..7ce07d6e --- /dev/null +++ b/Source/Core/GraphAlgorithms.ssc @@ -0,0 +1,175 @@ +//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System.Collections.Generic;
+using Microsoft.Contracts;
+
+namespace Microsoft.Boogie
+{
+ public delegate System.Collections.IEnumerable/*<Node!>*/! Adjacency<T>(T! node);
+
+
+ // An SCC is a set of nodes
+ public sealed class SCC<Node> : ICollection<Node>
+ {
+ private IDictionary<Node,object>! nodesMap = new Dictionary<Node,object>();
+ private ICollection<Node>! nodes { get { return (!) nodesMap.Keys; } }
+
+ [Pure] [GlobalAccess(false)] [Escapes(true,false)]
+ System.Collections.IEnumerator! System.Collections.IEnumerable.GetEnumerator()
+ {
+ return ((System.Collections.IEnumerable)nodes).GetEnumerator();
+ }
+
+ [Pure] [GlobalAccess(false)] [Escapes(true,false)]
+ IEnumerator<Node>! IEnumerable<Node>.GetEnumerator()
+ {
+ return ((IEnumerable<Node>)nodes).GetEnumerator();
+ }
+
+ public int Count { get { return nodes.Count; } }
+ public bool IsReadOnly { get { return nodesMap.IsReadOnly; } }
+ public void Add(Node item) { nodesMap.Add(item,null); }
+ public void Clear() { nodesMap.Clear(); }
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public bool Contains(Node item) { return nodesMap.ContainsKey(item); }
+ public void CopyTo(Node[]! array, int arrayIndex) { nodes.CopyTo(array, arrayIndex); }
+ public bool Remove(Node item) { return nodesMap.Remove(item); }
+ }
+
+ public sealed class StronglyConnectedComponents<Node> : IEnumerable<SCC<Node>!>
+ {
+ private readonly IDictionary<Node!,object>! graph;
+ private readonly Adjacency<Node>! preds;
+ private readonly Adjacency<Node>! succs;
+
+ private bool computed = false;
+ public bool Computed { get { return computed; } }
+
+ [NotDelayed]
+ public StronglyConnectedComponents(System.Collections.IEnumerable/*<Node!>*/! graph, Adjacency<Node>! preds, Adjacency<Node>! succs)
+ ensures !Computed;
+ {
+ IDictionary<Node!,object>! dict = new Dictionary<Node!,object>();
+ foreach (Node! n in graph) { dict.Add(n,null); }
+
+ this.graph = dict;
+ this.preds = preds;
+ this.succs = succs;
+ base();
+ }
+
+ [Pure] [GlobalAccess(false)] [Escapes(true,false)]
+ System.Collections.IEnumerator! System.Collections.IEnumerable.GetEnumerator()
+ {
+ return ((System.Collections.IEnumerable)sccs).GetEnumerator();
+ }
+
+ [Pure] [GlobalAccess(false)] [Escapes(true,false)]
+ IEnumerator<SCC<Node>!>! IEnumerable<SCC<Node>!>.GetEnumerator()
+ {
+ assume Computed;
+ return ((IEnumerable<SCC<Node>!>)sccs).GetEnumerator();
+ }
+
+ private readonly IList<SCC<Node>!>! sccs = new List<SCC<Node>!>();
+
+ public void Compute()
+ requires !Computed;
+ ensures Computed;
+ {
+ // Compute post times on graph with edges reversed
+ this.dfsNext = this.preds;
+ foreach (Node! n in (!)graph.Keys)
+ {
+ if (!seen.ContainsKey(n))
+ {
+ OrderNodes(n);
+ }
+ }
+
+ // Clear seen
+ seen.Clear();
+
+ // Compute SCCs
+ this.dfsNext = this.succs;
+ while (postOrder.Count > 0)
+ {
+ Node! n = postOrder.Pop();
+
+ if (!seen.ContainsKey(n))
+ {
+ SCC<Node>! curr = new SCC<Node>();
+ FindSCCs(n, curr);
+ sccs.Add(curr);
+ }
+ }
+
+ // Clear seen
+ seen.Clear();
+
+ this.computed = true;
+ }
+
+ private Adjacency<Node>/*?*/ dfsNext = null;
+
+ private readonly IDictionary<Node!,object>! seen = new Dictionary<Node!,object>();
+ private readonly Stack<Node!>! postOrder = new Stack<Node!>();
+
+ // DFS to order nodes by post times
+ private void OrderNodes(Node! node)
+ {
+ seen.Add(node,null);
+
+ assert dfsNext != null;
+ System.Collections.IEnumerable! nexts = dfsNext(node);
+ foreach (Node! n in nexts)
+ {
+ if (graph.ContainsKey(n) && !seen.ContainsKey(n)) { OrderNodes(n); }
+ }
+
+ postOrder.Push(node);
+ }
+
+ // DFS to compute SCCs
+ private void FindSCCs(Node! node, SCC<Node>! currSCC)
+ //modifies currSCC.*;
+ {
+ seen.Add(node,null);
+ currSCC.Add(node);
+
+ assert dfsNext != null;
+ System.Collections.IEnumerable! nexts = dfsNext(node);
+ foreach (Node! n in nexts)
+ {
+ if (graph.ContainsKey(n) && !seen.ContainsKey(n)) { FindSCCs(n,currSCC); }
+ }
+ }
+
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public override string! ToString()
+ {
+ string outStr = "";
+ int i = 0;
+
+ foreach(ICollection<Node> component in this)
+ {
+ string! tmp = System.String.Format("\nComponent #{0} = ", i++);
+ outStr += tmp;
+
+ bool firstInRow = true;
+
+ foreach(Node b in component)
+ {
+ string! tmpComponent = System.String.Format("{0}{1}", firstInRow? "" : ", ", b);
+ outStr += tmpComponent;
+ firstInRow = false;
+ }
+ }
+ return outStr;
+ }
+
+ }
+}
diff --git a/Source/Core/Inline.ssc b/Source/Core/Inline.ssc new file mode 100644 index 00000000..931c35eb --- /dev/null +++ b/Source/Core/Inline.ssc @@ -0,0 +1,907 @@ +//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+namespace Microsoft.Boogie {
+
+ using System;
+ using System.IO;
+ using System.Collections;
+ using System.Collections.Generic;
+ using BoogiePL;
+ using System.Diagnostics;
+ using System.Text.RegularExpressions; // for procedure inlining
+
+ // this callback is called before inlining a procedure
+ public delegate void InlineCallback(Implementation! impl);
+
+ public class Inliner
+ {
+ private InlineCallback inlineCallback;
+ private TypecheckingContext! checkingCtx;
+
+ // Maximum number of unrolling per procedure.
+ // This is now given as a commnd line option.
+ // While typically this number will be given with the :inline attribute in the source code
+ // we set this bound in case the programmer does not provide the number along with the :inline attribute.
+ public static int MaxUnrollPerProcedure = 3;
+
+ protected CodeCopier! codeCopier;
+
+ protected Dictionary<string!,int>! /* Procedure.Name -> int */ recursiveProcUnrollMap;
+
+ protected Dictionary<string!,int>! /* Procedure.Name -> int */ inlinedProcLblMap;
+
+ protected void NextInlinedProcLabel(string! procName) {
+ int currentId;
+ if (inlinedProcLblMap.TryGetValue(procName, out currentId)) {
+ inlinedProcLblMap[procName] = currentId + 1;
+ } else {
+ inlinedProcLblMap.Add(procName, 0);
+ }
+ }
+
+ protected string! GetInlinedProcLabel(string! procName) {
+ int currentId;
+ if (!inlinedProcLblMap.TryGetValue(procName, out currentId)) {
+ currentId = 0;
+ inlinedProcLblMap.Add(procName, currentId);
+ }
+ return "inline$" + procName + "$" + currentId;
+ }
+
+ protected string! GetProcVarName(string! procName, string! formalName) {
+ string! prefix = GetInlinedProcLabel(procName);
+ return prefix + "$" + formalName;
+ }
+
+ protected Inliner(TypecheckingContext! ctx, InlineCallback cb) {
+ inlinedProcLblMap = new Dictionary<string!,int>();
+ recursiveProcUnrollMap = new Dictionary<string!,int>();
+ codeCopier = new CodeCopier();
+ inlineCallback = cb;
+ checkingCtx = ctx;
+ }
+
+ public static void ProcessImplementation(TypecheckingContext! ctx, Program! program, Implementation! impl, InlineCallback cb)
+ requires impl.Proc != null;
+ {
+ Inliner inliner = new Inliner(ctx, cb);
+
+ VariableSeq! newInParams = new VariableSeq(impl.InParams);
+ VariableSeq! newOutParams = new VariableSeq(impl.OutParams);
+ VariableSeq! newLocalVars = new VariableSeq(impl.LocVars);
+
+ IdentifierExprSeq! newModifies = new IdentifierExprSeq(impl.Proc.Modifies);
+
+ bool inlined = false;
+ List<Block!>! newBlocks = inliner.DoInline(impl.Blocks, program, newLocalVars, newModifies, out inlined);
+
+ if (!inlined) return;
+
+ impl.InParams = newInParams;
+ impl.OutParams = newOutParams;
+ impl.LocVars = newLocalVars;
+ impl.Blocks = newBlocks;
+ impl.Proc.Modifies = newModifies;
+
+ impl.ResetImplFormalMap();
+
+ // we need to resolve the new code
+ inliner.ResolveImpl(program, impl);
+
+ if(CommandLineOptions.Clo.PrintInlined) {
+ inliner.EmitImpl(impl);
+ }
+ }
+
+
+ public static void ProcessImplementation(TypecheckingContext! ctx, Program! program, Implementation! impl)
+ requires impl.Proc != null;
+ {
+
+ ProcessImplementation(ctx, program, impl, null);
+ }
+
+ protected void EmitImpl(Implementation! impl)
+ requires impl.Proc != null;
+ {
+ Console.WriteLine("after inlining procedure calls");
+ impl.Proc.Emit(new TokenTextWriter("<console>", Console.Out), 0);
+ impl.Emit(new TokenTextWriter("<console>", Console.Out), 0);
+ }
+
+ private sealed class DummyErrorSink : IErrorSink
+ {
+ public void Error(IToken! tok, string! msg) {
+ // FIXME
+ // noop.
+ // This is required because during the resolution, some resolution errors happen
+ // (such as the ones caused addion of loop invariants J_(block.Label) by the AI package
+ }
+ }
+
+ protected void ResolveImpl(Program! program, Implementation! impl)
+ ensures impl.Proc != null;
+ {
+ ResolutionContext rc = new ResolutionContext(new DummyErrorSink());
+
+ foreach(Declaration decl in program.TopLevelDeclarations) {
+ decl.Register(rc);
+ }
+
+ impl.Proc = null; // to force Resolve() redo the operation
+ impl.Resolve(rc);
+
+ TypecheckingContext tc = new TypecheckingContext(new DummyErrorSink());
+
+ impl.Typecheck(tc);
+ }
+
+
+ // returns true if it is ok to further unroll the procedure
+ // otherwise, the procedure is not inlined at the call site
+ protected bool CheckInline(Implementation! impl) {
+ string! procName = impl.Name;
+ int c;
+ if (recursiveProcUnrollMap.TryGetValue(procName, out c)) {
+ if(c > 0) {
+ if (CommandLineOptions.Clo.ProcedureInlining == CommandLineOptions.Inlining.Bounded) {
+ recursiveProcUnrollMap[procName] = c - 1;
+ }
+ return true;
+ }
+ recursiveProcUnrollMap[procName] = 0;
+ return false;
+ }
+
+ bool doinline = false;
+
+ QKeyValue kv = null;
+ // try proc attributes
+ if(impl.Proc != null) {
+ kv = impl.Proc.Attributes;
+ while(kv != null) {
+ if(kv.Key.Equals("inline")) {
+ doinline = true;
+ break;
+ }
+ kv = kv.Next;
+ }
+ }
+ // try impl attributes
+ if(!doinline) {
+ kv = impl.Attributes;
+ while(kv != null) {
+ if(kv.Key.Equals("inline")) {
+ doinline = true;
+ break;
+ }
+ kv = kv.Next;
+ }
+ }
+
+ // try impl attributes
+ if(doinline) {
+ assert(kv != null && kv.Key.Equals("inline"));
+ // check the recursion
+ if (!recursiveProcUnrollMap.TryGetValue(procName, out c)) {
+ c = MaxUnrollPerProcedure;
+ if (kv.Params.Count == 1) {
+ LiteralExpr lit = kv.Params[0] as LiteralExpr;
+ if (lit != null && lit.isBigNum) {
+ c = lit.asBigNum.ToIntSafe;
+ }
+ }
+ recursiveProcUnrollMap.Add(procName, c);
+ }
+
+ if(c > 0) {
+ if (CommandLineOptions.Clo.ProcedureInlining == CommandLineOptions.Inlining.Bounded) {
+ recursiveProcUnrollMap[procName] = c - 1;
+ }
+ return true;
+ }
+ recursiveProcUnrollMap[procName] = 0;
+ return false;
+ }
+
+ // if not inlined, then record its inline count as 0
+ recursiveProcUnrollMap[procName] = 0;
+ return false;
+ }
+
+
+ private List<Block!>! DoInlineBlocks(Stack<Procedure!>! callStack, List<Block!>! blocks, Program! program,
+ VariableSeq! newLocalVars, IdentifierExprSeq! newModifies, ref bool inlinedSomething)
+ {
+ List<Block!>! newBlocks = new List<Block!>();
+
+ foreach(Block block in blocks) {
+
+ TransferCmd! transferCmd = (!) block.TransferCmd;
+
+ CmdSeq cmds = block.Cmds;
+ CmdSeq newCmds = new CmdSeq();
+ Block newBlock;
+ string label = block.Label;
+ int lblCount = 0;
+
+ for(int i = 0; i < cmds.Length; ++i) {
+ Cmd cmd = cmds[i];
+
+ CallCmd callCmd = cmd as CallCmd;
+
+ if(callCmd == null) {
+ // if not call command, leave it as is
+ newCmds.Add(codeCopier.CopyCmd(cmd));
+
+ } else {
+ assert(callCmd.Proc != null);
+ Procedure proc = null;
+ Implementation impl = null;
+ string calleeName = callCmd.Proc.Name;
+
+ bool inline = false;
+
+ // *** now we do not allow constructors to be inlined
+ if(! calleeName.Contains("..ctor")) {
+ // FIXME why on earth are we searching by name?!
+ bool implExists = FindProcImpl(program, calleeName, out proc, out impl);
+ assume(!implExists || (proc != null && impl != null));
+ if(implExists) {
+ if(CheckInline((!)impl)) {
+ inline = true;
+ inlinedSomething = true;
+ }
+ }
+ }
+
+ if (impl != null && CommandLineOptions.Clo.ProcedureInlining == CommandLineOptions.Inlining.MacroLike) {
+ foreach (Procedure! p in callStack) {
+ if (p == impl.Proc) {
+ inline = false;
+ string msg = "";
+ foreach (Procedure! p in callStack) {
+ msg = p.Name + " -> " + msg;
+ }
+ msg += p.Name;
+ checkingCtx.Error(impl, "the inlined procedure is recursive, call stack: {0}", msg);
+ }
+ }
+ }
+
+ assert(!inline || (impl != null));
+
+ if(inline) { // at least one block should exist
+ assume(impl != null && proc != null);
+ assert(((!)impl.OriginalBlocks).Count > 0);
+
+ // do inline now
+ int nextlblCount = lblCount + 1;
+ string nextBlockLabel = label + "$" + nextlblCount;
+
+ // run the callback before each inline
+ if(inlineCallback != null) {
+ inlineCallback(impl);
+ }
+
+ // increment the counter for the procedure to be used in constructing the locals and formals
+ NextInlinedProcLabel(proc.Name);
+
+ BeginInline(newLocalVars, newModifies, proc, impl);
+
+ List<Block!>! inlinedBlocks = CreateInlinedBlocks(callCmd, proc, impl, nextBlockLabel);
+
+ EndInline();
+
+ callStack.Push((!)impl.Proc);
+ inlinedBlocks = DoInlineBlocks(callStack, inlinedBlocks, program, newLocalVars, newModifies, ref inlinedSomething);
+ callStack.Pop();
+
+ Block! startBlock = inlinedBlocks[0];
+
+ GotoCmd gotoCmd = new GotoCmd(Token.NoToken, new StringSeq(startBlock.Label));
+ newBlock = new Block(block.tok, ((lblCount == 0) ? (label) : (label + "$" + lblCount)), newCmds, gotoCmd);
+
+ newBlocks.Add(newBlock);
+ newBlocks.AddRange(inlinedBlocks);
+
+ lblCount = nextlblCount;
+ newCmds = new CmdSeq();
+
+ } else {
+ // if this call will not be inlined, so leave it as is
+ newCmds.Add(codeCopier.CopyCmd(callCmd));
+ }
+ }
+ }
+
+ newBlock = new Block(block.tok, ((lblCount == 0) ? (label) : (label + "$" + lblCount)), newCmds, codeCopier.CopyTransferCmd(transferCmd));
+ newBlocks.Add(newBlock);
+ }
+
+ return newBlocks;
+ }
+
+ protected List<Block!>! DoInline(List<Block!>! blocks, Program! program, VariableSeq! newLocalVars, IdentifierExprSeq! newModifies, out bool inlined)
+ {
+ inlinedProcLblMap.Clear();
+ recursiveProcUnrollMap.Clear();
+
+ inlined = false;
+ return DoInlineBlocks(new Stack<Procedure!>(), blocks, program, newLocalVars, newModifies, ref inlined);
+ }
+
+ protected void BeginInline(VariableSeq! newLocalVars, IdentifierExprSeq! newModifies, Procedure! proc, Implementation! impl) {
+ Hashtable substMap = new Hashtable();
+
+ foreach(Variable! locVar in (!)impl.OriginalLocVars) {
+ LocalVariable localVar = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, GetProcVarName(proc.Name, locVar.Name), locVar.TypedIdent.Type, locVar.TypedIdent.WhereExpr));
+ newLocalVars.Add(localVar);
+ IdentifierExpr ie = new IdentifierExpr(Token.NoToken, localVar);
+ substMap.Add(locVar, ie);
+ }
+
+ for (int i = 0; i < impl.InParams.Length; i++) {
+ Variable inVar = (!) impl.InParams[i];
+ LocalVariable localVar = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, GetProcVarName(proc.Name, inVar.Name), inVar.TypedIdent.Type, inVar.TypedIdent.WhereExpr));
+ newLocalVars.Add(localVar);
+ IdentifierExpr ie = new IdentifierExpr(Token.NoToken, localVar);
+ substMap.Add(inVar, ie);
+ // also add a substitution from the corresponding formal occurring in the PROCEDURE declaration
+ Variable procInVar = (!)proc.InParams[i];
+ if (procInVar != inVar) {
+ substMap.Add(procInVar, ie);
+ }
+ }
+
+ for (int i = 0; i < impl.OutParams.Length; i++) {
+ Variable outVar = (!) impl.OutParams[i];
+ LocalVariable localVar = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, GetProcVarName(proc.Name, outVar.Name), outVar.TypedIdent.Type, outVar.TypedIdent.WhereExpr));
+ newLocalVars.Add(localVar);
+ IdentifierExpr ie = new IdentifierExpr(Token.NoToken, localVar);
+ substMap.Add(outVar, ie);
+ // also add a substitution from the corresponding formal occurring in the PROCEDURE declaration
+ Variable procOutVar = (!)proc.OutParams[i];
+ if (procOutVar != outVar) {
+ substMap.Add(procOutVar, ie);
+ }
+ }
+
+ Hashtable /*Variable -> Expr*/ substMapOld = new Hashtable/*Variable -> Expr*/();
+
+ foreach (IdentifierExpr! mie in proc.Modifies) {
+ Variable! mVar = (!) mie.Decl;
+ LocalVariable localVar = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, GetProcVarName(proc.Name, mVar.Name), mVar.TypedIdent.Type));
+ newLocalVars.Add(localVar);
+ IdentifierExpr ie = new IdentifierExpr(Token.NoToken, localVar);
+ substMapOld.Add(mVar, ie);
+ // FIXME why are we doing this? the modifies list should already include them.
+ // add the modified variable to the modifies list of the procedure
+ if(!newModifies.Has(mie)) {
+ newModifies.Add(mie);
+ }
+ }
+
+ codeCopier.Subst = Substituter.SubstitutionFromHashtable(substMap);
+ codeCopier.SubstForOld = Substituter.SubstitutionFromHashtable(substMapOld);
+ }
+
+ protected void EndInline() {
+ codeCopier.Subst = null;
+ codeCopier.SubstForOld = null;
+ }
+
+
+ // result[0] is the entry block
+ protected List<Block!>! CreateInlinedBlocks(CallCmd! callCmd, Procedure! proc, Implementation! impl, string! nextBlockLabel)
+ requires (codeCopier.Subst != null);
+ requires (codeCopier.SubstForOld != null);
+ {
+
+ List<Block!>! implBlocks = (!)impl.OriginalBlocks;
+ assert (implBlocks.Count > 0);
+
+ string startLabel = implBlocks[0].Label;
+
+ List<Block!>! inlinedBlocks = new List<Block!>();
+
+ // create in block
+ CmdSeq inCmds = new CmdSeq();
+
+ // assign in parameters
+ for(int i = 0; i < impl.InParams.Length; ++i) {
+ Cmd cmd = Cmd.SimpleAssign(impl.tok,
+ (IdentifierExpr) (!) codeCopier.Subst( (!)impl.InParams[i]),
+ (!)callCmd.Ins[i]);
+ inCmds.Add(cmd);
+ }
+
+ // inject non-free requires
+ for (int i = 0; i < proc.Requires.Length; i++) {
+ Requires! req = (!) proc.Requires[i];
+ if (!req.Free) {
+ Requires! reqCopy = (Requires!) req.Clone();
+ reqCopy.Condition = codeCopier.CopyExpr(req.Condition);
+ AssertCmd! a = new AssertRequiresCmd(callCmd, reqCopy);
+ a.ErrorDataEnhanced = reqCopy.ErrorDataEnhanced;
+ inCmds.Add(a);
+ }
+ }
+
+ VariableSeq locVars = (!)impl.OriginalLocVars;
+
+ // add where clauses of local vars as assume
+ for(int i = 0; i < locVars.Length; ++i) {
+ Expr whereExpr = ((!)locVars[i]).TypedIdent.WhereExpr;
+ if(whereExpr != null) {
+ whereExpr = Substituter.Apply(codeCopier.Subst, whereExpr);
+ // FIXME we cannot overwrite it, can we?!
+ ((!)locVars[i]).TypedIdent.WhereExpr = whereExpr;
+ AssumeCmd! a = new AssumeCmd(Token.NoToken, whereExpr);
+ inCmds.Add(a);
+ }
+ }
+
+ // add where clauses of output params as assume
+ for(int i = 0; i < impl.OutParams.Length; ++i) {
+ Expr whereExpr = ((!)impl.OutParams[i]).TypedIdent.WhereExpr;
+ if(whereExpr != null) {
+ whereExpr = Substituter.Apply(codeCopier.Subst, whereExpr);
+ // FIXME likewise
+ ((!)impl.OutParams[i]).TypedIdent.WhereExpr = whereExpr;
+ AssumeCmd! a = new AssumeCmd(Token.NoToken, whereExpr);
+ inCmds.Add(a);
+ }
+ }
+
+ // assign modifies old values
+ foreach (IdentifierExpr! mie in proc.Modifies)
+ {
+ Variable! mvar = (!) mie.Decl;
+ AssignCmd assign = Cmd.SimpleAssign(impl.tok, (IdentifierExpr) (!) codeCopier.SubstForOld(mvar), mie);
+ inCmds.Add(assign);
+ }
+
+ GotoCmd inGotoCmd = new GotoCmd(callCmd.tok, new StringSeq(GetInlinedProcLabel(proc.Name) + "$" + startLabel));
+ Block inBlock = new Block(impl.tok, GetInlinedProcLabel(proc.Name) + "$Entry", inCmds, inGotoCmd);
+ inlinedBlocks.Add(inBlock);
+
+ // inject the blocks of the implementation
+ Block intBlock;
+ foreach (Block block in implBlocks) {
+ CmdSeq copyCmds = codeCopier.CopyCmdSeq(block.Cmds);
+ TransferCmd transferCmd = CreateInlinedTransferCmd((!) block.TransferCmd, GetInlinedProcLabel(proc.Name));
+ intBlock = new Block(block.tok, GetInlinedProcLabel(proc.Name) + "$" + block.Label, copyCmds, transferCmd);
+ inlinedBlocks.Add(intBlock);
+ }
+
+ // create out block
+ CmdSeq outCmds = new CmdSeq();
+
+ // inject non-free ensures
+ for (int i = 0; i < proc.Ensures.Length; i++) {
+ Ensures! ens = (!) proc.Ensures[i];
+ if (!ens.Free) {
+ Ensures! ensCopy = (Ensures!) ens.Clone();
+ ensCopy.Condition = codeCopier.CopyExpr(ens.Condition);
+ AssertCmd! a = new AssertEnsuresCmd(ensCopy);
+ outCmds.Add(a);
+ }
+ }
+
+ // assign out params
+ for(int i = 0; i < impl.OutParams.Length; ++i) {
+ Expr! cout_exp = (IdentifierExpr) (!) codeCopier.Subst((!)impl.OutParams[i]);
+ Cmd cmd = Cmd.SimpleAssign(impl.tok, (!)callCmd.Outs[i], cout_exp);
+ outCmds.Add(cmd);
+ }
+
+ // create out block
+ GotoCmd outGotoCmd = new GotoCmd(Token.NoToken, new StringSeq(nextBlockLabel));
+ Block outBlock = new Block(impl.tok, GetInlinedProcLabel(proc.Name) + "$Return", outCmds, outGotoCmd);
+ inlinedBlocks.Add(outBlock);
+
+ return inlinedBlocks;
+ }
+
+ protected TransferCmd CreateInlinedTransferCmd(TransferCmd! transferCmd, string! procLabel) {
+ TransferCmd newTransferCmd;
+
+ GotoCmd gotoCmd = transferCmd as GotoCmd;
+ if(gotoCmd != null) {
+ StringSeq gotoSeq = gotoCmd.labelNames;
+ StringSeq newGotoSeq = new StringSeq();
+ foreach(string! blockLabel in (!) gotoSeq) {
+ newGotoSeq.Add(procLabel + "$" + blockLabel);
+ }
+ newTransferCmd = new GotoCmd(transferCmd.tok, newGotoSeq);
+ } else {
+ newTransferCmd = new GotoCmd(transferCmd.tok, new StringSeq(procLabel + "$Return"));
+ }
+
+ return newTransferCmd;
+ }
+
+ protected static bool FindProcImpl(Program! program, string! procName, out Procedure outProc, out Implementation outImpl)
+ {
+ // this assumes that there is at most one procedure and only one associated implementation in the current context
+
+ foreach(Declaration decl in program.TopLevelDeclarations) {
+ Implementation impl = decl as Implementation;
+ if(impl != null) {
+ if(impl.Name.Equals(procName)) {
+ assert(impl.Proc != null);
+ outProc = impl.Proc;
+ outImpl = impl;
+ return true;
+ }
+ }
+ }
+
+ foreach(Declaration decl in program.TopLevelDeclarations) {
+ Procedure proc = decl as Procedure;
+ if(proc != null) {
+ if(proc.Name.Equals(procName)) {
+ outProc = proc;
+ outImpl = null;
+ return false;
+ }
+ }
+ }
+
+ outProc = null;
+ outImpl = null;
+ return false;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public class CodeCopier
+ {
+
+ private DefaultDuplicator! dupl;
+
+ private sealed class DefaultDuplicator : Duplicator
+ {
+ public Substitution subst;
+ public Substitution oldSubst;
+ private bool insideOldExpr = false;
+ public DefaultDuplicator(Substitution subst, Substitution oldSubst) {
+ this.subst = subst;
+ this.oldSubst = oldSubst;
+ base();
+ }
+ public override Cmd! VisitAssumeCmd(AssumeCmd! node)
+ {
+ return new AssumeCmd(node.tok, VisitExpr(node.Expr));
+ }
+ public override Cmd! VisitAssertCmd(AssertCmd! node)
+ {
+ return new AssertCmd(node.tok, VisitExpr(node.Expr));
+ }
+ public override Cmd! VisitAssertRequiresCmd(AssertRequiresCmd! node)
+ {
+ return new AssertRequiresCmd((CallCmd)VisitCallCmd(node.Call), VisitRequires(node.Requires));
+ }
+ public override Cmd! VisitAssertEnsuresCmd(AssertEnsuresCmd! node)
+ {
+ return new AssertEnsuresCmd(VisitEnsures(node.Ensures));
+ }
+
+ public override Cmd! VisitCallCmd(CallCmd! node)
+ {
+ List<Expr>! newIns = new List<Expr> ();
+ List<IdentifierExpr>! newOuts = new List<IdentifierExpr> ();
+ foreach (Expr e in node.Ins)
+ newIns.Add(e == null ? null : this.VisitExpr(e));
+ foreach (IdentifierExpr e in node.Outs)
+ newOuts.Add(e == null ? null : (IdentifierExpr)this.VisitIdentifierExpr(e));
+
+ CallCmd! newCmd = new CallCmd(node.tok, ((!)node.Proc).Name, newIns, newOuts);
+ newCmd.Proc = node.Proc;
+ return newCmd;
+ }
+ public override Expr! VisitIdentifierExpr(IdentifierExpr! node)
+ {
+ Expr e = null;
+ if(insideOldExpr && oldSubst != null) {
+ e = oldSubst((!)node.Decl);
+ }
+ if(e == null) {
+ if(subst != null) {
+ e = subst((!)node.Decl);
+ return e == null ? base.VisitIdentifierExpr(node) : e;
+ } else {
+ return base.VisitIdentifierExpr(node);
+ }
+ }
+ return e;
+ }
+ public override Expr! VisitOldExpr(OldExpr! node)
+ {
+ if(this.oldSubst != null) {
+ bool previouslyInOld = insideOldExpr;
+ insideOldExpr = true;
+ Expr! e = (Expr!)this.Visit(node.Expr);
+ insideOldExpr = previouslyInOld;
+ return e;
+ } else {
+ return base.VisitOldExpr(node);
+ }
+ }
+ }
+
+ public CodeCopier(Hashtable! substMap) {
+ this.dupl = new DefaultDuplicator(Substituter.SubstitutionFromHashtable(substMap), null);
+ }
+
+ public CodeCopier(Hashtable! substMap, Hashtable! oldSubstMap) {
+ this.dupl = new DefaultDuplicator(Substituter.SubstitutionFromHashtable(substMap), Substituter.SubstitutionFromHashtable(oldSubstMap));
+ }
+
+ public CodeCopier() {
+ this.dupl = new DefaultDuplicator(null, null);
+ }
+
+ public Substitution Subst {
+ set {
+ this.dupl.subst = value;
+ }
+ get {
+ return this.dupl.subst;
+ }
+ }
+
+ public Substitution SubstForOld {
+ set {
+ this.dupl.oldSubst = value;
+ }
+ get {
+ return this.dupl.oldSubst;
+ }
+ }
+
+ public Duplicator! Dupl {
+ get {
+ return this.dupl;
+ }
+ }
+
+ public List<Block!>! CopyBlocks(List<Block!>! blocks) {
+
+ List<Block!>! newBlocks = new List<Block!>();
+
+ foreach(Block block in blocks) {
+ Block newBlock = CopyBlock(block);
+ newBlocks.Add(newBlock);
+ }
+
+ return newBlocks;
+ }
+
+ public List<Block!>! CopyBlocks(List<Block!>! blocks, string! prefix) {
+
+ List<Block!>! newBlocks = new List<Block!>();
+
+ foreach(Block block in blocks) {
+ Block newBlock = CopyBlock(block, prefix);
+ newBlocks.Add(newBlock);
+ }
+
+ return newBlocks;
+ }
+
+
+ #region CopyBlock
+ public Block! CopyBlock(Block! block) {
+
+ assert(block.TransferCmd != null);
+ Block newBlock = new Block(block.tok, block.Label, CopyCmdSeq(block.Cmds), CopyTransferCmd(block.TransferCmd));
+
+ return newBlock;
+
+ }
+
+ public Block! CopyBlock(Block! block, string! prefix) {
+
+ assert(block.TransferCmd != null);
+ Block newBlock = new Block(block.tok, prefix + "$" + block.Label, CopyCmdSeq(block.Cmds), CopyTransferCmd(block.TransferCmd));
+
+ return newBlock;
+
+ }
+ #endregion
+
+ #region CopyCmdSeq
+ public CmdSeq! CopyCmdSeq(CmdSeq! cmds) {
+
+ CmdSeq newCmds = new CmdSeq();
+
+ for (int i = 0; i < cmds.Length; ++i) {
+ newCmds.Add(CopyCmd(cmds[i]));
+ }
+
+ return newCmds;
+
+ }
+ #endregion
+
+
+ #region CopyTransferCmd
+ public TransferCmd! CopyTransferCmd(TransferCmd! cmd) {
+ TransferCmd transferCmd;
+ GotoCmd gotocmd = cmd as GotoCmd;
+ if(gotocmd != null) {
+ assert(gotocmd.labelNames != null);
+ StringSeq labels = new StringSeq();
+ labels.AddRange(gotocmd.labelNames);
+ transferCmd = new GotoCmd(cmd.tok, labels);
+ } else {
+ transferCmd = new ReturnCmd(cmd.tok);
+ }
+ return transferCmd;
+ }
+
+ public TransferCmd! CopyTransferCmd(TransferCmd! cmd, string! prefix) {
+ TransferCmd transferCmd;
+ GotoCmd gotocmd = cmd as GotoCmd;
+ if(gotocmd != null) {
+ assert(gotocmd.labelNames != null);
+ StringSeq labels = new StringSeq();
+ foreach(string label in gotocmd.labelNames) {
+ labels.Add(prefix + "$" + label);
+ }
+ transferCmd = new GotoCmd(cmd.tok, labels);
+ } else {
+ transferCmd = new ReturnCmd(cmd.tok);
+ }
+ return transferCmd;
+ }
+ #endregion
+
+
+
+ #region CopyCmd
+ public Cmd! CopyCmd(Cmd! cmd) {
+ Cmd newCmd = (Cmd) Dupl.Visit(cmd);
+ return newCmd;
+ }
+ #endregion
+
+ #region CopyExpr
+ public Expr! CopyExpr(Expr! expr) {
+ Expr newExpr = (Expr) Dupl.Visit(expr);
+ return newExpr;
+ }
+ #endregion
+
+ } // end class CodeCopier
+
+
+ public class AxiomExpander : Duplicator
+ {
+ readonly Program! program;
+ readonly TypecheckingContext! tc;
+
+ public AxiomExpander(Program! prog, TypecheckingContext! t)
+ {
+ program = prog;
+ tc = t;
+ }
+
+ public void CollectExpansions()
+ {
+ foreach (Declaration! decl in program.TopLevelDeclarations) {
+ Axiom ax = decl as Axiom;
+ if (ax != null) {
+ bool expand = false;
+ if (!ax.CheckBooleanAttribute("inline", ref expand)) {
+ Error(decl.tok, "{:inline ...} expects either true or false as the argument");
+ }
+ if (expand) {
+ AddExpansion(ax.Expr, ax.FindStringAttribute("ignore"));
+ }
+ }
+ Function f = decl as Function;
+ if (f != null && f.Body != null) {
+ Variable[]! formals = new Variable [f.InParams.Length];
+ for (int i = 0; i < formals.Length; ++i)
+ formals[i] = f.InParams[i];
+ AddExpansion(f, new Expansion(null, f.Body,
+ new TypeVariableSeq (f.TypeParameters),
+ formals));
+ }
+ }
+ }
+
+ void Error(IToken! tok, string msg)
+ {
+ tc.Error(tok, "expansion: " + msg);
+ }
+
+ void AddExpansion(Expr! axiomBody, string? ignore)
+ {
+ // it would be sooooooooo much easier with pattern matching
+ ForallExpr all = axiomBody as ForallExpr;
+ if (all != null) {
+ NAryExpr nary = all.Body as NAryExpr;
+ BinaryOperator bin = nary == null ? null : nary.Fun as BinaryOperator;
+ //System.Console.WriteLine("{0} {1} {2}", nary==null, bin==null, bin==null?0 : bin.Op);
+ if (nary != null && bin != null && (bin.Op == BinaryOperator.Opcode.Eq || bin.Op == BinaryOperator.Opcode.Iff)) {
+ NAryExpr? func = nary.Args[0] as NAryExpr;
+ //System.Console.WriteLine("{0} {1}", func == null, func == null ? null : func.Fun.GetType());
+ while (func != null && func.Fun is TypeCoercion)
+ func = func.Args[0] as NAryExpr;
+ if (func != null && func.Fun is FunctionCall) {
+ Function fn = (!)((FunctionCall)func.Fun).Func;
+ Expansion exp = new Expansion(ignore, (!)nary.Args[1],
+ new TypeVariableSeq (),
+ new Variable[func.Args.Length]);
+ int pos = 0;
+ Dictionary<Declaration, bool> parms = new Dictionary<Declaration, bool>();
+ foreach (Expr! e in func.Args) {
+ IdentifierExpr id = e as IdentifierExpr;
+ if (id == null) {
+ Error(e.tok, "only identifiers supported as function arguments");
+ return;
+ }
+ exp.formals[pos++] = id.Decl;
+ if (parms.ContainsKey(id.Decl)) {
+ Error(all.tok, "an identifier was used more than once");
+ return;
+ }
+ parms[id.Decl] = true;
+ if (!all.Dummies.Has(id.Decl)) {
+ Error(all.tok, "identifier was not quantified over");
+ return;
+ }
+ }
+ if (func.Args.Length != all.Dummies.Length) {
+ Error(all.tok, "more variables quantified over, than used in function");
+ return;
+ }
+
+ Dictionary<TypeVariable!, bool> typeVars = new Dictionary<TypeVariable!, bool>();
+ foreach (TypeVariable! v in ((!)func.TypeParameters).FormalTypeParams) {
+ if (!func.TypeParameters[v].IsVariable) {
+ Error(all.tok, "only identifiers supported as type parameters");
+ return;
+ }
+ TypeVariable! formal = func.TypeParameters[v].AsVariable;
+ exp.TypeParameters.Add(formal);
+ if (typeVars.ContainsKey(formal)) {
+ Error(all.tok, "an identifier was used more than once");
+ return;
+ }
+ typeVars[formal] = true;
+ if (!all.TypeParameters.Has(formal)) {
+ Error(all.tok, "identifier was not quantified over");
+ return;
+ }
+ }
+ if (((FunctionCall)func.Fun).Func.TypeParameters.Length != all.TypeParameters.Length) {
+ Error(all.tok, "more variables quantified over, than used in function");
+ return;
+ }
+ AddExpansion(fn, exp);
+ return;
+ }
+ }
+ }
+
+ Error(axiomBody.tok, "axiom to be expanded must have form (forall VARS :: f(VARS) == expr(VARS))");
+ }
+
+ void AddExpansion(Function! fn, Expansion! x) {
+ if (fn.expansions == null) {
+ fn.expansions = new List<Expansion!>();
+ }
+ fn.expansions.Add(x);
+ }
+ }
+} // end namespace
+
diff --git a/Source/Core/LoopUnroll.ssc b/Source/Core/LoopUnroll.ssc new file mode 100644 index 00000000..4335a834 --- /dev/null +++ b/Source/Core/LoopUnroll.ssc @@ -0,0 +1,174 @@ +//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using Microsoft.Contracts;
+using System.Collections.Generic;
+using Cci = System.Compiler;
+using Bpl = Microsoft.Boogie;
+
+namespace Microsoft.Boogie
+{
+ public class LoopUnroll {
+ public static List<Block!>! UnrollLoops(Block! start, int unrollMaxDepth)
+ requires 0 <= unrollMaxDepth;
+ {
+ Dictionary<Block,GraphNode!> gd = new Dictionary<Block,GraphNode!>();
+ Cci.HashSet/*Block*/! beingVisited = new Cci.HashSet/*Block*/();
+ GraphNode gStart = GraphNode.ComputeGraphInfo(null, start, gd, beingVisited);
+
+ LoopUnroll lu = new LoopUnroll(gd, unrollMaxDepth, null);
+ lu.Visit(gStart);
+ lu.newBlockSeqGlobal.Reverse();
+ return lu.newBlockSeqGlobal;
+ }
+
+ class GraphNode {
+ public readonly Block! Block;
+ public readonly CmdSeq! Body;
+ bool isCutPoint; // is set during ComputeGraphInfo
+ public bool IsCutPoint { get { return isCutPoint; } }
+ [Rep] public readonly List<GraphNode!>! ForwardEdges = new List<GraphNode!>();
+ [Rep] public readonly List<GraphNode!>! BackEdges = new List<GraphNode!>();
+ invariant isCutPoint <==> BackEdges.Count != 0;
+
+ GraphNode(Block! b, CmdSeq! body) {
+ this.Block = b;
+ this.Body = body;
+ }
+
+ static CmdSeq! GetOptimizedBody(CmdSeq! cmds) {
+ int n = 0;
+ foreach (Cmd c in cmds) {
+ n++;
+ PredicateCmd pc = c as PredicateCmd;
+ if (pc != null && pc.Expr is LiteralExpr && ((LiteralExpr)pc.Expr).IsFalse) {
+ // return a sequence consisting of the commands seen so far
+ Cmd[] s = new Cmd[n];
+ for (int i = 0; i < n; i++) {
+ s[i] = cmds[i];
+ }
+ return new CmdSeq(s);
+ }
+ }
+ return cmds;
+ }
+
+ public static GraphNode! ComputeGraphInfo(GraphNode from, Block! b, Dictionary<Block,GraphNode!>! gd, Cci.HashSet/*Block*/! beingVisited) {
+ GraphNode g;
+ if (gd.TryGetValue(b, out g)) {
+ assume from != null;
+ assert g != null;
+ if (beingVisited.Contains(b)) {
+ // it's a cut point
+ g.isCutPoint = true;
+ from.BackEdges.Add(g);
+ } else {
+ from.ForwardEdges.Add(g);
+ }
+
+ } else {
+ CmdSeq body = GetOptimizedBody(b.Cmds);
+ g = new GraphNode(b, body);
+ gd.Add(b, g);
+ if (from != null) {
+ from.ForwardEdges.Add(g);
+ }
+
+ if (body != b.Cmds) {
+ // the body was optimized -- there is no way through this block
+ } else {
+ beingVisited.Add(b);
+
+ GotoCmd gcmd = b.TransferCmd as GotoCmd;
+ if (gcmd != null) {
+ assume gcmd.labelTargets != null;
+ foreach (Block! succ in gcmd.labelTargets) {
+ ComputeGraphInfo(g, succ, gd, beingVisited);
+ }
+ }
+
+ beingVisited.Remove(b);
+ }
+ }
+ return g;
+ }
+ }
+
+ List<Block!>! newBlockSeqGlobal;
+ readonly int c;
+ readonly LoopUnroll next;
+ Dictionary<Block,int>! visitsRemaining = new Dictionary</*cut-point-*/Block,int>();
+ Dictionary<Block,Block!>! newBlocks = new Dictionary<Block,Block!>();
+
+ private LoopUnroll(Dictionary<Block,GraphNode!>! gd, int unrollMaxDepth, List<Block!> newBlockSeqGlobal)
+ requires 0 <= unrollMaxDepth;
+ {
+ if (newBlockSeqGlobal == null) {
+ newBlockSeqGlobal = new List<Block!>();
+ }
+ this.newBlockSeqGlobal = newBlockSeqGlobal;
+ this.c = unrollMaxDepth;
+ if (unrollMaxDepth != 0) {
+ next = new LoopUnroll(gd, unrollMaxDepth - 1, newBlockSeqGlobal);
+ }
+ }
+
+ Block! Visit(GraphNode! node) {
+ Block orig = node.Block;
+ Block nw;
+ if (newBlocks.TryGetValue(orig, out nw)) {
+ assert nw != null;
+
+ } else {
+ CmdSeq body;
+ TransferCmd tcmd;
+ assert orig.TransferCmd != null;
+
+ if (next == null && node.IsCutPoint) {
+ // as the body, use the assert/assume commands that make up the loop invariant
+ body = new CmdSeq();
+ foreach (Cmd! c in node.Body) {
+ if (c is PredicateCmd || c is CommentCmd) {
+ body.Add(c);
+ } else {
+ break;
+ }
+ }
+ body.Add(new AssumeCmd(orig.tok, Bpl.Expr.False));
+
+ tcmd = new ReturnCmd(orig.TransferCmd.tok);
+
+ } else {
+ body = node.Body;
+ BlockSeq newSuccs = new BlockSeq();
+
+ foreach (GraphNode succ in node.ForwardEdges) {
+ Block s = Visit(succ);
+ newSuccs.Add(s);
+ }
+
+ assert next == null ==> node.BackEdges.Count == 0; // follows from if-else test above and the GraphNode invariant
+ foreach (GraphNode succ in node.BackEdges) {
+ assert next != null; // since if we get here, node.BackEdges.Count != 0
+ Block s = next.Visit(succ);
+ newSuccs.Add(s);
+ }
+
+ if (newSuccs.Length == 0) {
+ tcmd = new ReturnCmd(orig.TransferCmd.tok);
+ } else {
+ tcmd = new GotoCmd(orig.TransferCmd.tok, newSuccs);
+ }
+ }
+
+ nw = new Block(orig.tok, orig.Label + "#" + this.c, body, tcmd);
+ newBlocks.Add(orig, nw);
+ newBlockSeqGlobal.Add(nw);
+ }
+
+ return nw;
+ }
+ }
+}
diff --git a/Source/Core/Makefile b/Source/Core/Makefile new file mode 100644 index 00000000..ee73b25d --- /dev/null +++ b/Source/Core/Makefile @@ -0,0 +1,18 @@ +COCO = ..\..\Binaries\Coco.exe
+ASML = ..\..\Binaries\asmlc.boot.exe
+
+# "all" depends on 2 files, really (Parser.cs and Scanner.cs), but they
+# are both generated in one go and I don't know a better way to tell
+# nmake that. --KRML
+all: Parser.ssc
+
+#Graph.dll: Graph.as
+# $(ASML) /target:library Graph.as
+
+Parser.ssc: Scanner.frame Parser.frame BoogiePL.atg
+ $(COCO) BoogiePL.atg
+ copy Parser.cs Parser.ssc
+ copy Scanner.cs Scanner.ssc
+
+clean:
+ rm -f Scanner.ssc Parser.ssc
diff --git a/Source/Core/OOLongUtil.ssc b/Source/Core/OOLongUtil.ssc new file mode 100644 index 00000000..81dadf1b --- /dev/null +++ b/Source/Core/OOLongUtil.ssc @@ -0,0 +1,174 @@ +//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using System.IO;
+using Microsoft.Contracts;
+
+namespace Boogie.Util {
+ public class TeeWriter : TextWriter {
+ readonly TextWriter! a;
+ readonly TextWriter! b;
+
+ public TeeWriter(TextWriter! a, TextWriter! b) {
+ this.a = a;
+ this.b = b;
+ }
+
+ public override System.Text.Encoding Encoding {
+ get {
+ return a.Encoding;
+ }
+ }
+
+ public override void Close() {
+ a.Close();
+ b.Close();
+ }
+
+ public override void Flush() {
+ a.Flush();
+ b.Flush();
+ }
+
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public override string! ToString() {
+ return "<TeeWriter: " + a.ToString() + ", " + b.ToString() + ">";
+ }
+
+ public override void Write(char ch) {
+ a.Write(ch);
+ b.Write(ch);
+ }
+
+ public override void Write(string s) {
+ a.Write(s);
+ b.Write(s);
+ }
+ }
+
+ /// <summary>
+ /// A LineReader is a class that allows further subclasses to just override the ReadLine() method.
+ /// It simply reads from the given "reader".
+ /// </summary>
+ public class LineReader : TextReader {
+ [Rep] readonly TextReader! reader;
+ string readAhead;
+ int readAheadConsumed;
+ invariant readAhead == null || (0 <= readAheadConsumed && readAheadConsumed < readAhead.Length);
+
+ public LineReader([Captured] TextReader! reader) {
+ this.reader = reader;
+ }
+ public override void Close() {
+ expose (this) {
+ reader.Close();
+ }
+ }
+ public override int Read() {
+ expose (this) {
+ while (readAhead == null) {
+ readAhead = reader.ReadLine();
+ if (readAhead == null) {
+ // we're at EOF
+ return -1;
+ } else if (readAhead.Length > 0) {
+ readAheadConsumed = 0;
+ break;
+ }
+ }
+ int res = readAhead[readAheadConsumed++];
+ if (readAheadConsumed == readAhead.Length) {
+ readAhead = null;
+ }
+ return res;
+ }
+ }
+ public override int Read(char[]! buffer, int index, int count) {
+ int n = 0;
+ for (; n < count; n++) {
+ int ch = Read();
+ if (ch == -1) {
+ break;
+ }
+ buffer[index + n] = (char)ch;
+ }
+ return n;
+ }
+ public override string ReadLine() {
+ string res;
+ if (readAhead != null) {
+ expose (this) {
+ res = readAhead.Substring(readAheadConsumed);
+ readAhead = null;
+ }
+ } else {
+ res = reader.ReadLine();
+ }
+ return res;
+ }
+ }
+
+ public class IfdefReader : LineReader {
+ [Rep] readonly List<string!>! defines;
+ [Rep] readonly List<bool>! readState = new List<bool>();
+ int ignoreCutoff = 0; // 0 means we're not ignoring
+ invariant 0 <= ignoreCutoff && ignoreCutoff <= readState.Count;
+
+ public IfdefReader([Captured] TextReader! reader, [Captured] List<string!>! defines) {
+ base(reader);
+ this.defines = defines;
+ }
+
+ public override string ReadLine() {
+ while (true) {
+ string s = base.ReadLine();
+ if (s == null) {
+ return s;
+ }
+ string t = s.Trim();
+ if (t.StartsWith("#if")) {
+ string arg = t.Substring(3).TrimStart();
+ bool sense = true;
+ while (t.StartsWith("!")) {
+ sense = !sense;
+ t = t.Substring(1).TrimStart();
+ }
+ // push "true", since we're in a "then" branch
+ readState.Add(true);
+ if (ignoreCutoff == 0 && defines.Contains(arg) != sense) {
+ ignoreCutoff = readState.Count; // start ignoring
+ }
+ } else if (t == "#else") {
+ if (readState.Count == 0 || !readState[readState.Count-1]) {
+ return s; // malformed input; return the read line as if it were not special
+ }
+ // change the "true" to a "false" on top of the state, since we're now going into the "else" branch
+ readState[readState.Count-1] = false;
+ if (ignoreCutoff == 0) {
+ // the "then" branch had been included, so we'll ignore the "else" branch
+ ignoreCutoff = readState.Count;
+ } else if (ignoreCutoff == readState.Count) {
+ // we had ignored the "then" branch, so we'll include the "else" branch
+ ignoreCutoff = 0;
+ }
+ } else if (t == "#endif") {
+ if (readState.Count == 0) {
+ return s; // malformed input; return the read line as if it were not special
+ }
+ if (ignoreCutoff == readState.Count) {
+ // we had ignored the branch that ends here; so, now we start including again
+ ignoreCutoff = 0;
+ }
+ // pop
+ readState.RemoveAt(readState.Count-1);
+ } else if (ignoreCutoff == 0) {
+ return s;
+ }
+ }
+ }
+ }
+}
diff --git a/Source/Core/Parser.ssc b/Source/Core/Parser.ssc new file mode 100644 index 00000000..3d0f4449 --- /dev/null +++ b/Source/Core/Parser.ssc @@ -0,0 +1,2021 @@ +//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using PureCollections;
+using System.Collections;
+using System.Collections.Generic;
+using Microsoft.Boogie;
+using Microsoft.Basetypes;
+using Bpl = Microsoft.Boogie;
+using AI = Microsoft.AbstractInterpretationFramework;
+using System;
+using Microsoft.Contracts;
+
+namespace BoogiePL {
+
+public class Parser {
+ const int maxT = 85;
+
+ const bool T = true;
+ const bool x = false;
+ const int minErrDist = 2;
+
+ static Token/*!*/ token; // last recognized token
+ static Token/*!*/ t; // lookahead token
+ static int errDist = minErrDist;
+
+ static Program! Pgm = new Program();
+
+static Expr! dummyExpr = new LiteralExpr(Token.NoToken, false);
+static Cmd! dummyCmd = new AssumeCmd(Token.NoToken, dummyExpr);
+static Block! dummyBlock = new Block(Token.NoToken, "dummyBlock", new CmdSeq(),
+ new ReturnCmd(Token.NoToken));
+static Bpl.Type! dummyType = new BasicType(Token.NoToken, SimpleType.Bool);
+static Bpl.ExprSeq! dummyExprSeq = new ExprSeq ();
+static TransferCmd! dummyTransferCmd = new ReturnCmd(Token.NoToken);
+static StructuredCmd! dummyStructuredCmd = new BreakCmd(Token.NoToken, null);
+
+///<summary>
+///Returns the number of parsing errors encountered. If 0, "program" returns as
+///the parsed program.
+///</summary>
+public static int Parse (string! filename, out /*maybe null*/ Program program) /* throws System.IO.IOException */ {
+ using (System.IO.StreamReader reader = new System.IO.StreamReader(filename)) {
+ Buffer.Fill(reader);
+ Scanner.Init(filename);
+ return Parse(out program);
+ }
+}
+
+///<summary>
+///Returns the number of parsing errors encountered. If 0, "program" returns as
+///the parsed program.
+///Note: first initialize the Scanner.
+///</summary>
+public static int Parse (out /*maybe null*/ Program program) {
+ Pgm = new Program(); // reset the global variable
+ Parse();
+ if (Errors.count == 0)
+ {
+ program = Pgm;
+ return 0;
+ }
+ else
+ {
+ program = null;
+ return Errors.count;
+ }
+}
+
+
+public static int ParseProposition (string! text, out Expr! expression)
+{
+ Buffer.Fill(text);
+ Scanner.Init(string.Format("\"{0}\"", text));
+
+ Errors.SynErr = new ErrorProc(SynErr);
+ t = new Token();
+ Get();
+ Proposition(out expression);
+ return Errors.count;
+}
+
+// Class to represent the bounds of a bitvector expression t[a:b].
+// Objects of this class only exist during parsing and are directly
+// turned into BvExtract before they get anywhere else
+private class BvBounds : Expr {
+ public BigNum Lower;
+ public BigNum Upper;
+ public BvBounds(IToken! tok, BigNum lower, BigNum upper) {
+ base(tok);
+ this.Lower = lower;
+ this.Upper = upper;
+ }
+ public override Type! ShallowType { get { return Bpl.Type.Int; } }
+ public override void Resolve(ResolutionContext! rc) {
+ rc.Error(this, "bitvector bounds in illegal position");
+ }
+ public override void Emit(TokenTextWriter! stream,
+ int contextBindingStrength, bool fragileContext) {
+ assert false;
+ }
+ public override void ComputeFreeVariables(Set! freeVars) { assert false; }
+ public override AI.IExpr! IExpr { get { assert false; } }
+}
+
+/*--------------------------------------------------------------------------*/
+
+
+ static void Error(int n) {
+ if (errDist >= minErrDist) Errors.SynErr(n, t.filename, t.line, t.col);
+ errDist = 0;
+ }
+
+ public static void SemErr(string! msg) {
+ if (errDist >= minErrDist) Errors.SemErr(token.filename, token.line, token.col, msg);
+ errDist = 0;
+ }
+
+ static void Get() {
+ for (;;) {
+ token = t;
+ t = Scanner.Scan();
+ if (t.kind<=maxT) {errDist++; return;}
+
+ t = token;
+ }
+ }
+
+ static void Expect(int n) {
+ if (t.kind==n) Get(); else Error(n);
+ }
+
+ static bool StartOf(int s) {
+ return set[s, t.kind];
+ }
+
+ static void ExpectWeak(int n, int follow) {
+ if (t.kind == n) Get();
+ else {
+ Error(n);
+ while (!StartOf(follow)) Get();
+ }
+ }
+
+ static bool WeakSeparator(int n, int syFol, int repFol) {
+ bool[] s = new bool[maxT+1];
+ if (t.kind == n) {Get(); return true;}
+ else if (StartOf(repFol)) return false;
+ else {
+ for (int i=0; i <= maxT; i++) {
+ s[i] = set[syFol, i] || set[repFol, i] || set[0, i];
+ }
+ Error(n);
+ while (!s[t.kind]) Get();
+ return StartOf(syFol);
+ }
+ }
+
+ static void BoogiePL() {
+ VariableSeq! vs;
+ DeclarationSeq! ds;
+ Axiom! ax;
+ List<Declaration!>! ts;
+ Procedure! pr;
+ Implementation im;
+ Implementation! nnim;
+
+ while (StartOf(1)) {
+ switch (t.kind) {
+ case 19: {
+ Consts(out vs);
+ foreach (Bpl.Variable! v in vs) { Pgm.TopLevelDeclarations.Add(v); }
+ break;
+ }
+ case 23: {
+ Function(out ds);
+ foreach (Bpl.Declaration! d in ds) { Pgm.TopLevelDeclarations.Add(d); }
+ break;
+ }
+ case 27: {
+ Axiom(out ax);
+ Pgm.TopLevelDeclarations.Add(ax);
+ break;
+ }
+ case 28: {
+ UserDefinedTypes(out ts);
+ foreach (Declaration! td in ts) {
+ Pgm.TopLevelDeclarations.Add(td);
+ }
+ break;
+ }
+ case 6: {
+ GlobalVars(out vs);
+ foreach (Bpl.Variable! v in vs) { Pgm.TopLevelDeclarations.Add(v); }
+ break;
+ }
+ case 30: {
+ Procedure(out pr, out im);
+ Pgm.TopLevelDeclarations.Add(pr);
+ if (im != null) {
+ Pgm.TopLevelDeclarations.Add(im);
+ }
+
+ break;
+ }
+ case 31: {
+ Implementation(out nnim);
+ Pgm.TopLevelDeclarations.Add(nnim);
+ break;
+ }
+ }
+ }
+ Expect(0);
+ }
+
+ static void Consts(out VariableSeq! ds) {
+ IToken! y; TypedIdentSeq! xs;
+ ds = new VariableSeq();
+ bool u = false; QKeyValue kv = null;
+ bool ChildrenComplete = false;
+ List<ConstantParent!> Parents = null;
+ Expect(19);
+ y = token;
+ while (t.kind == 25) {
+ Attribute(ref kv);
+ }
+ if (t.kind == 20) {
+ Get();
+ u = true;
+ }
+ IdsType(out xs);
+ if (t.kind == 21) {
+ OrderSpec(out ChildrenComplete, out Parents);
+ }
+ bool makeClone = false;
+ foreach(TypedIdent! x in xs) {
+ // ensure that no sharing is introduced
+ List<ConstantParent!> ParentsClone;
+ if (makeClone && Parents != null) {
+ ParentsClone = new List<ConstantParent!> ();
+ foreach (ConstantParent! p in Parents)
+ ParentsClone.Add(new ConstantParent (
+ new IdentifierExpr (p.Parent.tok, p.Parent.Name),
+ p.Unique));
+ } else {
+ ParentsClone = Parents;
+ }
+ makeClone = true;
+ ds.Add(new Constant(y, x, u, ParentsClone, ChildrenComplete, kv));
+ }
+
+ Expect(7);
+ }
+
+ static void Function(out DeclarationSeq! ds) {
+ ds = new DeclarationSeq(); IToken! z;
+ IToken! typeParamTok;
+ TypeVariableSeq! typeParams = new TypeVariableSeq();
+ VariableSeq arguments = new VariableSeq();
+ TypedIdent! tyd;
+ QKeyValue kv = null;
+ Expr definition = null;
+ Expr! tmp;
+
+ Expect(23);
+ while (t.kind == 25) {
+ Attribute(ref kv);
+ }
+ Ident(out z);
+ if (t.kind == 17) {
+ TypeParams(out typeParamTok, out typeParams);
+ }
+ Expect(8);
+ if (StartOf(2)) {
+ VarOrType(out tyd);
+ arguments.Add(new Formal(tyd.tok, tyd, true));
+ while (t.kind == 11) {
+ Get();
+ VarOrType(out tyd);
+ arguments.Add(new Formal(tyd.tok, tyd, true));
+ }
+ }
+ Expect(9);
+ Expect(24);
+ Expect(8);
+ VarOrType(out tyd);
+ Expect(9);
+ if (t.kind == 25) {
+ Get();
+ Expression(out tmp);
+ definition = tmp;
+ Expect(26);
+ } else if (t.kind == 7) {
+ Get();
+ } else Error(86);
+ Function! func = new Function(z, z.val, typeParams, arguments,
+ new Formal(tyd.tok, tyd, false), null, kv);
+ ds.Add(func);
+ if (definition != null) {
+ // generate either an axiom or a function body
+ if (QKeyValue.FindBoolAttribute(kv, "inline")) {
+ func.Body = definition;
+ } else {
+ VariableSeq dummies = new VariableSeq();
+ ExprSeq callArgs = new ExprSeq();
+ int i = 0;
+ foreach (Formal! f in arguments) {
+ string nm = f.TypedIdent.HasName ? f.TypedIdent.Name : "_" + i;
+ dummies.Add(new BoundVariable(f.tok, new TypedIdent(f.tok, nm, f.TypedIdent.Type)));
+ callArgs.Add(new IdentifierExpr(f.tok, nm));
+ i++;
+ }
+ TypeVariableSeq! quantifiedTypeVars = new TypeVariableSeq ();
+ foreach (TypeVariable! t in typeParams)
+ quantifiedTypeVars.Add(new TypeVariable (Token.NoToken, t.Name));
+ Expr call = new NAryExpr(z, new FunctionCall(new IdentifierExpr(z, z.val)), callArgs);
+ // specify the type of the function, because it might be that
+ // type parameters only occur in the output type
+ call = Expr.CoerceType(z, call, (Type)tyd.Type.Clone());
+ Expr def = new ForallExpr(z, quantifiedTypeVars, dummies,
+ kv,
+ new Trigger(z, true, new ExprSeq(call), null),
+ Expr.Eq(call, definition));
+ ds.Add(new Axiom(z, def, "autogenerated definition axiom", null));
+ }
+ }
+
+ }
+
+ static void Axiom(out Axiom! m) {
+ Expr! e; QKeyValue kv = null;
+ Expect(27);
+ while (t.kind == 25) {
+ Attribute(ref kv);
+ }
+ IToken! x = token;
+ Proposition(out e);
+ Expect(7);
+ m = new Axiom(x,e, null, kv);
+ }
+
+ static void UserDefinedTypes(out List<Declaration!>! ts) {
+ Declaration! decl; QKeyValue kv = null; ts = new List<Declaration!> ();
+ Expect(28);
+ while (t.kind == 25) {
+ Attribute(ref kv);
+ }
+ UserDefinedType(out decl, kv);
+ ts.Add(decl);
+ while (t.kind == 11) {
+ Get();
+ UserDefinedType(out decl, kv);
+ ts.Add(decl);
+ }
+ Expect(7);
+ }
+
+ static void GlobalVars(out VariableSeq! ds) {
+ TypedIdentSeq! tyds = new TypedIdentSeq(); ds = new VariableSeq(); QKeyValue kv = null;
+ Expect(6);
+ while (t.kind == 25) {
+ Attribute(ref kv);
+ }
+ IdsTypeWheres(true, tyds);
+ Expect(7);
+ foreach(TypedIdent! tyd in tyds) {
+ ds.Add(new GlobalVariable(tyd.tok, tyd, kv));
+ }
+
+ }
+
+ static void Procedure(out Procedure! proc, out /*maybe null*/ Implementation impl) {
+ IToken! x;
+ TypeVariableSeq! typeParams;
+ VariableSeq! ins, outs;
+ RequiresSeq! pre = new RequiresSeq();
+ IdentifierExprSeq! mods = new IdentifierExprSeq();
+ EnsuresSeq! post = new EnsuresSeq();
+ VariableSeq! locals = new VariableSeq();
+ StmtList! stmtList;
+ QKeyValue kv = null;
+ impl = null;
+
+ Expect(30);
+ ProcSignature(true, out x, out typeParams, out ins, out outs, out kv);
+ if (t.kind == 7) {
+ Get();
+ while (StartOf(3)) {
+ Spec(pre, mods, post);
+ }
+ } else if (StartOf(4)) {
+ while (StartOf(3)) {
+ Spec(pre, mods, post);
+ }
+ ImplBody(out locals, out stmtList);
+ // here we attach kv only to the Procedure, not its implementation
+ impl = new Implementation(x, x.val, typeParams,
+ Formal.StripWhereClauses(ins), Formal.StripWhereClauses(outs), locals, stmtList, null);
+
+ } else Error(87);
+ proc = new Procedure(x, x.val, typeParams, ins, outs, pre, mods, post, kv);
+ }
+
+ static void Implementation(out Implementation! impl) {
+ IToken! x;
+ TypeVariableSeq! typeParams;
+ VariableSeq! ins, outs;
+ VariableSeq! locals;
+ StmtList! stmtList;
+ QKeyValue kv;
+
+ Expect(31);
+ ProcSignature(false, out x, out typeParams, out ins, out outs, out kv);
+ ImplBody(out locals, out stmtList);
+ impl = new Implementation(x, x.val, typeParams, ins, outs, locals, stmtList, kv);
+ }
+
+ static void Attribute(ref QKeyValue kv) {
+ Trigger trig = null;
+ AttributeOrTrigger(ref kv, ref trig);
+ if (trig != null) SemErr("only attributes, not triggers, allowed here");
+ }
+
+ static void IdsTypeWheres(bool allowWhereClauses, TypedIdentSeq! tyds) {
+ IdsTypeWhere(allowWhereClauses, tyds);
+ while (t.kind == 11) {
+ Get();
+ IdsTypeWhere(allowWhereClauses, tyds);
+ }
+ }
+
+ static void LocalVars(VariableSeq! ds) {
+ TypedIdentSeq! tyds = new TypedIdentSeq(); QKeyValue kv = null;
+ Expect(6);
+ while (t.kind == 25) {
+ Attribute(ref kv);
+ }
+ IdsTypeWheres(true, tyds);
+ Expect(7);
+ foreach(TypedIdent! tyd in tyds) {
+ ds.Add(new LocalVariable(tyd.tok, tyd, kv));
+ }
+
+ }
+
+ static void ProcFormals(bool incoming, bool allowWhereClauses, out VariableSeq! ds) {
+ TypedIdentSeq! tyds = new TypedIdentSeq(); ds = new VariableSeq();
+ Expect(8);
+ if (t.kind == 1) {
+ IdsTypeWheres(allowWhereClauses, tyds);
+ }
+ Expect(9);
+ foreach (TypedIdent! tyd in tyds) {
+ ds.Add(new Formal(tyd.tok, tyd, incoming));
+ }
+
+ }
+
+ static void BoundVars(IToken! x, out VariableSeq! ds) {
+ TypedIdentSeq! tyds = new TypedIdentSeq(); ds = new VariableSeq();
+ IdsTypeWheres(false, tyds);
+ foreach (TypedIdent! tyd in tyds) {
+ ds.Add(new BoundVariable(tyd.tok, tyd));
+ }
+
+ }
+
+ static void IdsType(out TypedIdentSeq! tyds) {
+ TokenSeq! ids; Bpl.Type! ty;
+ Idents(out ids);
+ Expect(10);
+ Type(out ty);
+ tyds = new TypedIdentSeq();
+ foreach (Token! id in ids) {
+ tyds.Add(new TypedIdent(id, id.val, ty, null));
+ }
+
+ }
+
+ static void Idents(out TokenSeq! xs) {
+ IToken! id; xs = new TokenSeq();
+ Ident(out id);
+ xs.Add(id);
+ while (t.kind == 11) {
+ Get();
+ Ident(out id);
+ xs.Add(id);
+ }
+ }
+
+ static void Type(out Bpl.Type! ty) {
+ IToken! tok; ty = dummyType;
+ if (t.kind == 8 || t.kind == 13 || t.kind == 14) {
+ TypeAtom(out ty);
+ } else if (t.kind == 1) {
+ Ident(out tok);
+ TypeSeq! args = new TypeSeq ();
+ if (StartOf(2)) {
+ TypeArgs(args);
+ }
+ ty = new UnresolvedTypeIdentifier (tok, tok.val, args);
+ } else if (t.kind == 15 || t.kind == 17) {
+ MapType(out ty);
+ } else Error(88);
+ }
+
+ static void IdsTypeWhere(bool allowWhereClauses, TypedIdentSeq! tyds) {
+ TokenSeq! ids; Bpl.Type! ty; Expr wh = null; Expr! nne;
+ Idents(out ids);
+ Expect(10);
+ Type(out ty);
+ if (t.kind == 12) {
+ Get();
+ Expression(out nne);
+ if (allowWhereClauses) {
+ wh = nne;
+ } else {
+ SemErr("where clause not allowed here");
+ }
+
+ }
+ foreach (Token! id in ids) {
+ tyds.Add(new TypedIdent(id, id.val, ty, wh));
+ }
+
+ }
+
+ static void Expression(out Expr! e0) {
+ IToken! x; Expr! e1;
+ ImpliesExpression(false, out e0);
+ while (t.kind == 52 || t.kind == 53) {
+ EquivOp();
+ x = token;
+ ImpliesExpression(false, out e1);
+ e0 = Expr.Binary(x, BinaryOperator.Opcode.Iff, e0, e1);
+ }
+ }
+
+ static void TypeAtom(out Bpl.Type! ty) {
+ ty = dummyType;
+ if (t.kind == 13) {
+ Get();
+ ty = new BasicType(token, SimpleType.Int);
+ } else if (t.kind == 14) {
+ Get();
+ ty = new BasicType(token, SimpleType.Bool);
+ } else if (t.kind == 8) {
+ Get();
+ Type(out ty);
+ Expect(9);
+ } else Error(89);
+ }
+
+ static void Ident(out IToken! x) {
+ Expect(1);
+ x = token;
+ if (x.val.StartsWith("\\"))
+ x.val = x.val.Substring(1);
+
+ }
+
+ static void TypeArgs(TypeSeq! ts) {
+ IToken! tok; Type! ty;
+ if (t.kind == 8 || t.kind == 13 || t.kind == 14) {
+ TypeAtom(out ty);
+ ts.Add(ty);
+ if (StartOf(2)) {
+ TypeArgs(ts);
+ }
+ } else if (t.kind == 1) {
+ Ident(out tok);
+ TypeSeq! args = new TypeSeq ();
+ ts.Add(new UnresolvedTypeIdentifier (tok, tok.val, args));
+ if (StartOf(2)) {
+ TypeArgs(ts);
+ }
+ } else if (t.kind == 15 || t.kind == 17) {
+ MapType(out ty);
+ ts.Add(ty);
+ } else Error(90);
+ }
+
+ static void MapType(out Bpl.Type! ty) {
+ IToken tok = null;
+ IToken! nnTok;
+ TypeSeq! arguments = new TypeSeq();
+ Type! result;
+ TypeVariableSeq! typeParameters = new TypeVariableSeq();
+
+ if (t.kind == 17) {
+ TypeParams(out nnTok, out typeParameters);
+ tok = nnTok;
+ }
+ Expect(15);
+ if (tok == null) tok = token;
+ if (StartOf(2)) {
+ Types(arguments);
+ }
+ Expect(16);
+ Type(out result);
+ ty = new MapType(tok, typeParameters, arguments, result);
+
+ }
+
+ static void TypeParams(out IToken! tok, out Bpl.TypeVariableSeq! typeParams) {
+ TokenSeq! typeParamToks;
+ Expect(17);
+ tok = token;
+ Idents(out typeParamToks);
+ Expect(18);
+ typeParams = new TypeVariableSeq ();
+ foreach (Token! id in typeParamToks)
+ typeParams.Add(new TypeVariable(id, id.val));
+
+ }
+
+ static void Types(TypeSeq! ts) {
+ Bpl.Type! ty;
+ Type(out ty);
+ ts.Add(ty);
+ while (t.kind == 11) {
+ Get();
+ Type(out ty);
+ ts.Add(ty);
+ }
+ }
+
+ static void OrderSpec(out bool ChildrenComplete, out List<ConstantParent!> Parents) {
+ ChildrenComplete = false;
+ Parents = null;
+ bool u;
+ IToken! parent;
+ Expect(21);
+ Parents = new List<ConstantParent!> ();
+ u = false;
+ if (t.kind == 1 || t.kind == 20) {
+ if (t.kind == 20) {
+ Get();
+ u = true;
+ }
+ Ident(out parent);
+ Parents.Add(new ConstantParent (
+ new IdentifierExpr(parent, parent.val), u));
+ while (t.kind == 11) {
+ Get();
+ u = false;
+ if (t.kind == 20) {
+ Get();
+ u = true;
+ }
+ Ident(out parent);
+ Parents.Add(new ConstantParent (
+ new IdentifierExpr(parent, parent.val), u));
+ }
+ }
+ if (t.kind == 22) {
+ Get();
+ ChildrenComplete = true;
+ }
+ }
+
+ static void VarOrType(out TypedIdent! tyd) {
+ string! varName = ""; Bpl.Type! ty; IToken! tok;
+ Type(out ty);
+ tok = ty.tok;
+ if (t.kind == 10) {
+ Get();
+ if (ty is UnresolvedTypeIdentifier &&
+ ((!)(ty as UnresolvedTypeIdentifier)).Arguments.Length == 0) {
+ varName = ((!)(ty as UnresolvedTypeIdentifier)).Name;
+ } else {
+ SemErr("expected identifier before ':'");
+ }
+
+ Type(out ty);
+ }
+ tyd = new TypedIdent(tok, varName, ty);
+ }
+
+ static void Proposition(out Expr! e) {
+ Expression(out e);
+ }
+
+ static void UserDefinedType(out Declaration! decl, QKeyValue kv) {
+ IToken! id; IToken! id2; TokenSeq! paramTokens = new TokenSeq ();
+ Type! body = dummyType; bool synonym = false;
+ Ident(out id);
+ if (t.kind == 1) {
+ WhiteSpaceIdents(out paramTokens);
+ }
+ if (t.kind == 29) {
+ Get();
+ Type(out body);
+ synonym = true;
+ }
+ if (synonym) {
+ TypeVariableSeq! typeParams = new TypeVariableSeq();
+ foreach (Token! t in paramTokens)
+ typeParams.Add(new TypeVariable(t, t.val));
+ decl = new TypeSynonymDecl(id, id.val, typeParams, body, kv);
+ } else {
+ decl = new TypeCtorDecl(id, id.val, paramTokens.Length, kv);
+ }
+
+ }
+
+ static void WhiteSpaceIdents(out TokenSeq! xs) {
+ IToken! id; xs = new TokenSeq();
+ Ident(out id);
+ xs.Add(id);
+ while (t.kind == 1) {
+ Ident(out id);
+ xs.Add(id);
+ }
+ }
+
+ static void ProcSignature(bool allowWhereClausesOnFormals, out IToken! name, out TypeVariableSeq! typeParams,
+out VariableSeq! ins, out VariableSeq! outs, out QKeyValue kv) {
+ IToken! typeParamTok; typeParams = new TypeVariableSeq();
+ outs = new VariableSeq(); kv = null;
+ while (t.kind == 25) {
+ Attribute(ref kv);
+ }
+ Ident(out name);
+ if (t.kind == 17) {
+ TypeParams(out typeParamTok, out typeParams);
+ }
+ ProcFormals(true, allowWhereClausesOnFormals, out ins);
+ if (t.kind == 24) {
+ Get();
+ ProcFormals(false, allowWhereClausesOnFormals, out outs);
+ }
+ }
+
+ static void Spec(RequiresSeq! pre, IdentifierExprSeq! mods, EnsuresSeq! post) {
+ TokenSeq! ms;
+ if (t.kind == 32) {
+ Get();
+ if (t.kind == 1) {
+ Idents(out ms);
+ foreach (IToken! m in ms) {
+ mods.Add(new IdentifierExpr(m, m.val));
+ }
+
+ }
+ Expect(7);
+ } else if (t.kind == 33) {
+ Get();
+ SpecPrePost(true, pre, post);
+ } else if (t.kind == 34 || t.kind == 35) {
+ SpecPrePost(false, pre, post);
+ } else Error(91);
+ }
+
+ static void ImplBody(out VariableSeq! locals, out StmtList! stmtList) {
+ locals = new VariableSeq();
+ Expect(25);
+ while (t.kind == 6) {
+ LocalVars(locals);
+ }
+ StmtList(out stmtList);
+ }
+
+ static void SpecPrePost(bool free, RequiresSeq! pre, EnsuresSeq! post) {
+ Expr! e; VariableSeq! locals; BlockSeq! blocks; Token tok = null; QKeyValue kv = null;
+ if (t.kind == 34) {
+ Get();
+ tok = token;
+ while (t.kind == 25) {
+ Attribute(ref kv);
+ }
+ if (StartOf(5)) {
+ Proposition(out e);
+ Expect(7);
+ pre.Add(new Requires(tok, free, e, null, kv));
+ } else if (t.kind == 36) {
+ SpecBody(out locals, out blocks);
+ Expect(7);
+ pre.Add(new Requires(tok, free, new BlockExpr(locals, blocks), null, kv));
+ } else Error(92);
+ } else if (t.kind == 35) {
+ Get();
+ tok = token;
+ while (t.kind == 25) {
+ Attribute(ref kv);
+ }
+ if (StartOf(5)) {
+ Proposition(out e);
+ Expect(7);
+ post.Add(new Ensures(tok, free, e, null, kv));
+ } else if (t.kind == 36) {
+ SpecBody(out locals, out blocks);
+ Expect(7);
+ post.Add(new Ensures(tok, free, new BlockExpr(locals, blocks), null, kv));
+ } else Error(93);
+ } else Error(94);
+ }
+
+ static void SpecBody(out VariableSeq! locals, out BlockSeq! blocks) {
+ locals = new VariableSeq(); Block! b;
+ Expect(36);
+ while (t.kind == 6) {
+ LocalVars(locals);
+ }
+ SpecBlock(out b);
+ blocks = new BlockSeq(b);
+ while (t.kind == 1) {
+ SpecBlock(out b);
+ blocks.Add(b);
+ }
+ Expect(37);
+ }
+
+ static void SpecBlock(out Block! b) {
+ IToken! x; IToken! y;
+ Cmd c; IToken label;
+ CmdSeq cs = new CmdSeq();
+ TokenSeq! xs;
+ StringSeq ss = new StringSeq();
+ b = dummyBlock;
+ Expr! e;
+
+ Ident(out x);
+ Expect(10);
+ while (StartOf(6)) {
+ LabelOrCmd(out c, out label);
+ if (c != null) {
+ assert label == null;
+ cs.Add(c);
+ } else {
+ assert label != null;
+ SemErr("SpecBlock's can only have one label");
+ }
+
+ }
+ if (t.kind == 38) {
+ Get();
+ y = token;
+ Idents(out xs);
+ foreach (IToken! s in xs) { ss.Add(s.val); }
+ b = new Block(x,x.val,cs,new GotoCmd(y,ss));
+
+ } else if (t.kind == 39) {
+ Get();
+ Expression(out e);
+ b = new Block(x,x.val,cs,new ReturnExprCmd(token,e));
+ } else Error(95);
+ Expect(7);
+ }
+
+ static void LabelOrCmd(out Cmd c, out IToken label) {
+ IToken! x; Expr! e;
+ TokenSeq! xs;
+ IdentifierExprSeq ids;
+ c = dummyCmd; label = null;
+ Cmd! cn;
+ QKeyValue kv = null;
+
+ if (t.kind == 1) {
+ LabelOrAssign(out c, out label);
+ } else if (t.kind == 46) {
+ Get();
+ x = token;
+ while (t.kind == 25) {
+ Attribute(ref kv);
+ }
+ Proposition(out e);
+ c = new AssertCmd(x,e, kv);
+ Expect(7);
+ } else if (t.kind == 47) {
+ Get();
+ x = token;
+ Proposition(out e);
+ c = new AssumeCmd(x,e);
+ Expect(7);
+ } else if (t.kind == 48) {
+ Get();
+ x = token;
+ Idents(out xs);
+ Expect(7);
+ ids = new IdentifierExprSeq();
+ foreach (IToken! y in xs) {
+ ids.Add(new IdentifierExpr(y, y.val));
+ }
+ c = new HavocCmd(x,ids);
+
+ } else if (t.kind == 50) {
+ CallCmd(out cn);
+ Expect(7);
+ c = cn;
+ } else Error(96);
+ }
+
+ static void StmtList(out StmtList! stmtList) {
+ List<BigBlock!> bigblocks = new List<BigBlock!>();
+ /* built-up state for the current BigBlock: */
+ IToken startToken = null; string currentLabel = null;
+ CmdSeq cs = null; /* invariant: startToken != null ==> cs != null */
+ /* temporary variables: */
+ IToken label; Cmd c; BigBlock b;
+ StructuredCmd ec = null; StructuredCmd! ecn;
+ TransferCmd tc = null; TransferCmd! tcn;
+
+ while (StartOf(7)) {
+ if (StartOf(6)) {
+ LabelOrCmd(out c, out label);
+ if (c != null) {
+ // LabelOrCmd read a Cmd
+ assert label == null;
+ if (startToken == null) { startToken = c.tok; cs = new CmdSeq(); }
+ assert cs != null;
+ cs.Add(c);
+ } else {
+ // LabelOrCmd read a label
+ assert label != null;
+ if (startToken != null) {
+ assert cs != null;
+ // dump the built-up state into a BigBlock
+ b = new BigBlock(startToken, currentLabel, cs, null, null);
+ bigblocks.Add(b);
+ cs = null;
+ }
+ startToken = label;
+ currentLabel = label.val;
+ cs = new CmdSeq();
+ }
+
+ } else if (t.kind == 40 || t.kind == 42 || t.kind == 45) {
+ StructuredCmd(out ecn);
+ ec = ecn;
+ if (startToken == null) { startToken = ec.tok; cs = new CmdSeq(); }
+ assert cs != null;
+ b = new BigBlock(startToken, currentLabel, cs, ec, null);
+ bigblocks.Add(b);
+ startToken = null; currentLabel = null; cs = null;
+
+ } else {
+ TransferCmd(out tcn);
+ tc = tcn;
+ if (startToken == null) { startToken = tc.tok; cs = new CmdSeq(); }
+ assert cs != null;
+ b = new BigBlock(startToken, currentLabel, cs, null, tc);
+ bigblocks.Add(b);
+ startToken = null; currentLabel = null; cs = null;
+
+ }
+ }
+ Expect(26);
+ IToken! endCurly = token;
+ if (startToken == null && bigblocks.Count == 0) {
+ startToken = token; cs = new CmdSeq();
+ }
+ if (startToken != null) {
+ assert cs != null;
+ b = new BigBlock(startToken, currentLabel, cs, null, null);
+ bigblocks.Add(b);
+ }
+ stmtList = new StmtList(bigblocks, endCurly);
+
+ }
+
+ static void StructuredCmd(out StructuredCmd! ec) {
+ ec = dummyStructuredCmd; assume ec.IsPeerConsistent;
+ IfCmd! ifcmd; WhileCmd! wcmd; BreakCmd! bcmd;
+
+ if (t.kind == 40) {
+ IfCmd(out ifcmd);
+ ec = ifcmd;
+ } else if (t.kind == 42) {
+ WhileCmd(out wcmd);
+ ec = wcmd;
+ } else if (t.kind == 45) {
+ BreakCmd(out bcmd);
+ ec = bcmd;
+ } else Error(97);
+ }
+
+ static void TransferCmd(out TransferCmd! tc) {
+ tc = dummyTransferCmd;
+ Token y; TokenSeq! xs;
+ StringSeq ss = new StringSeq();
+
+ if (t.kind == 38) {
+ Get();
+ y = token;
+ Idents(out xs);
+ foreach (IToken! s in xs) { ss.Add(s.val); }
+ tc = new GotoCmd(y, ss);
+
+ } else if (t.kind == 39) {
+ Get();
+ tc = new ReturnCmd(token);
+ } else Error(98);
+ Expect(7);
+ }
+
+ static void IfCmd(out IfCmd! ifcmd) {
+ IToken! x;
+ Expr guard;
+ StmtList! thn;
+ IfCmd! elseIf; IfCmd elseIfOption = null;
+ StmtList! els; StmtList elseOption = null;
+
+ Expect(40);
+ x = token;
+ Guard(out guard);
+ Expect(25);
+ StmtList(out thn);
+ if (t.kind == 41) {
+ Get();
+ if (t.kind == 40) {
+ IfCmd(out elseIf);
+ elseIfOption = elseIf;
+ } else if (t.kind == 25) {
+ Get();
+ StmtList(out els);
+ elseOption = els;
+ } else Error(99);
+ }
+ ifcmd = new IfCmd(x, guard, thn, elseIfOption, elseOption);
+ }
+
+ static void WhileCmd(out WhileCmd! wcmd) {
+ IToken! x; Token z;
+ Expr guard; Expr! e; bool isFree;
+ List<PredicateCmd!> invariants = new List<PredicateCmd!>();
+ StmtList! body;
+
+ Expect(42);
+ x = token;
+ Guard(out guard);
+ assume guard == null || Owner.None(guard);
+ while (t.kind == 33 || t.kind == 43) {
+ isFree = false; z = t/*lookahead token*/;
+ if (t.kind == 33) {
+ Get();
+ isFree = true;
+ }
+ Expect(43);
+ Expression(out e);
+ if (isFree) {
+ invariants.Add(new AssumeCmd(z, e));
+ } else {
+ invariants.Add(new AssertCmd(z, e));
+ }
+
+ Expect(7);
+ }
+ Expect(25);
+ StmtList(out body);
+ wcmd = new WhileCmd(x, guard, invariants, body);
+ }
+
+ static void BreakCmd(out BreakCmd! bcmd) {
+ IToken! x; IToken! y;
+ string breakLabel = null;
+
+ Expect(45);
+ x = token;
+ if (t.kind == 1) {
+ Ident(out y);
+ breakLabel = y.val;
+ }
+ Expect(7);
+ bcmd = new BreakCmd(x, breakLabel);
+ }
+
+ static void Guard(out Expr e) {
+ Expr! ee; e = null;
+ Expect(8);
+ if (t.kind == 44) {
+ Get();
+ e = null;
+ } else if (StartOf(5)) {
+ Expression(out ee);
+ e = ee;
+ } else Error(100);
+ Expect(9);
+ }
+
+ static void LabelOrAssign(out Cmd c, out IToken label) {
+ IToken! id; IToken! x; Expr! e, e0;
+ c = dummyCmd; label = null;
+ AssignLhs! lhs;
+ List<AssignLhs!>! lhss;
+ List<Expr!>! rhss;
+
+ Ident(out id);
+ x = token;
+ if (t.kind == 10) {
+ Get();
+ c = null; label = x;
+ } else if (t.kind == 11 || t.kind == 15 || t.kind == 49) {
+ MapAssignIndexes(id, out lhs);
+ lhss = new List<AssignLhs!> ();
+ lhss.Add(lhs);
+ while (t.kind == 11) {
+ Get();
+ Ident(out id);
+ MapAssignIndexes(id, out lhs);
+ lhss.Add(lhs);
+ }
+ Expect(49);
+ x = token; /* use location of := */
+ Expression(out e0);
+ rhss = new List<Expr!> ();
+ rhss.Add(e0);
+ while (t.kind == 11) {
+ Get();
+ Expression(out e0);
+ rhss.Add(e0);
+ }
+ Expect(7);
+ c = new AssignCmd(x, lhss, rhss);
+ } else Error(101);
+ }
+
+ static void CallCmd(out Cmd! c) {
+ IToken! x; IToken! first; IToken p;
+ List<IdentifierExpr>! ids = new List<IdentifierExpr>();
+ List<Expr>! es = new List<Expr>();
+ Expr en; List<Expr> args;
+ c = dummyCmd;
+
+ Expect(50);
+ x = token;
+ if (t.kind == 1) {
+ Ident(out first);
+ if (t.kind == 8) {
+ Get();
+ if (StartOf(8)) {
+ CallForallArg(out en);
+ es.Add(en);
+ while (t.kind == 11) {
+ Get();
+ CallForallArg(out en);
+ es.Add(en);
+ }
+ }
+ Expect(9);
+ c = new CallCmd(x, first.val, es, ids);
+ } else if (t.kind == 11 || t.kind == 49) {
+ ids.Add(new IdentifierExpr(first, first.val));
+ if (t.kind == 11) {
+ Get();
+ CallOutIdent(out p);
+ if (p==null) {
+ ids.Add(null);
+ } else {
+ ids.Add(new IdentifierExpr(p, p.val));
+ }
+
+ while (t.kind == 11) {
+ Get();
+ CallOutIdent(out p);
+ if (p==null) {
+ ids.Add(null);
+ } else {
+ ids.Add(new IdentifierExpr(p, p.val));
+ }
+
+ }
+ }
+ Expect(49);
+ Ident(out first);
+ Expect(8);
+ if (StartOf(8)) {
+ CallForallArg(out en);
+ es.Add(en);
+ while (t.kind == 11) {
+ Get();
+ CallForallArg(out en);
+ es.Add(en);
+ }
+ }
+ Expect(9);
+ c = new CallCmd(x, first.val, es, ids);
+ } else Error(102);
+ } else if (t.kind == 51) {
+ Get();
+ Ident(out first);
+ Expect(8);
+ args = new List<Expr>();
+ if (StartOf(8)) {
+ CallForallArg(out en);
+ args.Add(en);
+ while (t.kind == 11) {
+ Get();
+ CallForallArg(out en);
+ args.Add(en);
+ }
+ }
+ Expect(9);
+ c = new CallForallCmd(x, first.val, args);
+ } else if (t.kind == 44) {
+ Get();
+ ids.Add(null);
+ if (t.kind == 11) {
+ Get();
+ CallOutIdent(out p);
+ if (p==null) {
+ ids.Add(null);
+ } else {
+ ids.Add(new IdentifierExpr(p, p.val));
+ }
+
+ while (t.kind == 11) {
+ Get();
+ CallOutIdent(out p);
+ if (p==null) {
+ ids.Add(null);
+ } else {
+ ids.Add(new IdentifierExpr(p, p.val));
+ }
+
+ }
+ }
+ Expect(49);
+ Ident(out first);
+ Expect(8);
+ if (StartOf(8)) {
+ CallForallArg(out en);
+ es.Add(en);
+ while (t.kind == 11) {
+ Get();
+ CallForallArg(out en);
+ es.Add(en);
+ }
+ }
+ Expect(9);
+ c = new CallCmd(x, first.val, es, ids);
+ } else Error(103);
+ }
+
+ static void MapAssignIndexes(IToken! assignedVariable, out AssignLhs! lhs) {
+ IToken! x;
+ AssignLhs! runningLhs =
+ new SimpleAssignLhs(assignedVariable,
+ new IdentifierExpr(assignedVariable, assignedVariable.val));
+ List<Expr!>! indexes;
+ Expr! e0;
+
+ while (t.kind == 15) {
+ Get();
+ x = token;
+ indexes = new List<Expr!> ();
+ if (StartOf(5)) {
+ Expression(out e0);
+ indexes.Add(e0);
+ while (t.kind == 11) {
+ Get();
+ Expression(out e0);
+ indexes.Add(e0);
+ }
+ }
+ Expect(16);
+ runningLhs =
+ new MapAssignLhs (x, runningLhs, indexes);
+ }
+ lhs = runningLhs;
+ }
+
+ static void CallForallArg(out Expr exprOptional) {
+ exprOptional = null;
+ Expr! e;
+
+ if (t.kind == 44) {
+ Get();
+ } else if (StartOf(5)) {
+ Expression(out e);
+ exprOptional = e;
+ } else Error(104);
+ }
+
+ static void CallOutIdent(out IToken id) {
+ id = null;
+ IToken! p;
+
+ if (t.kind == 44) {
+ Get();
+ } else if (t.kind == 1) {
+ Ident(out p);
+ id = p;
+ } else Error(105);
+ }
+
+ static void Expressions(out ExprSeq! es) {
+ Expr! e; es = new ExprSeq();
+ Expression(out e);
+ es.Add(e);
+ while (t.kind == 11) {
+ Get();
+ Expression(out e);
+ es.Add(e);
+ }
+ }
+
+ static void ImpliesExpression(bool noExplies, out Expr! e0) {
+ IToken! x; Expr! e1;
+ LogicalExpression(out e0);
+ if (StartOf(9)) {
+ if (t.kind == 54 || t.kind == 55) {
+ ImpliesOp();
+ x = token;
+ ImpliesExpression(true, out e1);
+ e0 = Expr.Binary(x, BinaryOperator.Opcode.Imp, e0, e1);
+ } else {
+ ExpliesOp();
+ if (noExplies)
+ SemErr("illegal mixture of ==> and <==, use parentheses to disambiguate");
+ x = token;
+ LogicalExpression(out e1);
+ e0 = Expr.Binary(x, BinaryOperator.Opcode.Imp, e1, e0);
+ while (t.kind == 56 || t.kind == 57) {
+ ExpliesOp();
+ x = token;
+ LogicalExpression(out e1);
+ e0 = Expr.Binary(x, BinaryOperator.Opcode.Imp, e1, e0);
+ }
+ }
+ }
+ }
+
+ static void EquivOp() {
+ if (t.kind == 52) {
+ Get();
+ } else if (t.kind == 53) {
+ Get();
+ } else Error(106);
+ }
+
+ static void LogicalExpression(out Expr! e0) {
+ IToken! x; Expr! e1; BinaryOperator.Opcode op;
+ RelationalExpression(out e0);
+ if (StartOf(10)) {
+ if (t.kind == 58 || t.kind == 59) {
+ AndOp();
+ x = token;
+ RelationalExpression(out e1);
+ e0 = Expr.Binary(x, BinaryOperator.Opcode.And, e0, e1);
+ while (t.kind == 58 || t.kind == 59) {
+ AndOp();
+ x = token;
+ RelationalExpression(out e1);
+ e0 = Expr.Binary(x, BinaryOperator.Opcode.And, e0, e1);
+ }
+ } else {
+ OrOp();
+ x = token;
+ RelationalExpression(out e1);
+ e0 = Expr.Binary(x, BinaryOperator.Opcode.Or, e0, e1);
+ while (t.kind == 60 || t.kind == 61) {
+ OrOp();
+ x = token;
+ RelationalExpression(out e1);
+ e0 = Expr.Binary(x, BinaryOperator.Opcode.Or, e0, e1);
+ }
+ }
+ }
+ }
+
+ static void ImpliesOp() {
+ if (t.kind == 54) {
+ Get();
+ } else if (t.kind == 55) {
+ Get();
+ } else Error(107);
+ }
+
+ static void ExpliesOp() {
+ if (t.kind == 56) {
+ Get();
+ } else if (t.kind == 57) {
+ Get();
+ } else Error(108);
+ }
+
+ static void RelationalExpression(out Expr! e0) {
+ IToken! x; Expr! e1; BinaryOperator.Opcode op;
+ BvTerm(out e0);
+ if (StartOf(11)) {
+ RelOp(out x, out op);
+ BvTerm(out e1);
+ e0 = Expr.Binary(x, op, e0, e1);
+ }
+ }
+
+ static void AndOp() {
+ if (t.kind == 58) {
+ Get();
+ } else if (t.kind == 59) {
+ Get();
+ } else Error(109);
+ }
+
+ static void OrOp() {
+ if (t.kind == 60) {
+ Get();
+ } else if (t.kind == 61) {
+ Get();
+ } else Error(110);
+ }
+
+ static void BvTerm(out Expr! e0) {
+ IToken! x; Expr! e1;
+ Term(out e0);
+ while (t.kind == 70) {
+ Get();
+ x = token;
+ Term(out e1);
+ e0 = new BvConcatExpr(x, e0, e1);
+ }
+ }
+
+ static void RelOp(out IToken! x, out BinaryOperator.Opcode op) {
+ x = Token.NoToken; op=BinaryOperator.Opcode.Add/*(dummy)*/;
+ switch (t.kind) {
+ case 62: {
+ Get();
+ x = token; op=BinaryOperator.Opcode.Eq;
+ break;
+ }
+ case 17: {
+ Get();
+ x = token; op=BinaryOperator.Opcode.Lt;
+ break;
+ }
+ case 18: {
+ Get();
+ x = token; op=BinaryOperator.Opcode.Gt;
+ break;
+ }
+ case 63: {
+ Get();
+ x = token; op=BinaryOperator.Opcode.Le;
+ break;
+ }
+ case 64: {
+ Get();
+ x = token; op=BinaryOperator.Opcode.Ge;
+ break;
+ }
+ case 65: {
+ Get();
+ x = token; op=BinaryOperator.Opcode.Neq;
+ break;
+ }
+ case 66: {
+ Get();
+ x = token; op=BinaryOperator.Opcode.Subtype;
+ break;
+ }
+ case 67: {
+ Get();
+ x = token; op=BinaryOperator.Opcode.Neq;
+ break;
+ }
+ case 68: {
+ Get();
+ x = token; op=BinaryOperator.Opcode.Le;
+ break;
+ }
+ case 69: {
+ Get();
+ x = token; op=BinaryOperator.Opcode.Ge;
+ break;
+ }
+ default: Error(111); break;
+ }
+ }
+
+ static void Term(out Expr! e0) {
+ IToken! x; Expr! e1; BinaryOperator.Opcode op;
+ Factor(out e0);
+ while (t.kind == 71 || t.kind == 72) {
+ AddOp(out x, out op);
+ Factor(out e1);
+ e0 = Expr.Binary(x, op, e0, e1);
+ }
+ }
+
+ static void Factor(out Expr! e0) {
+ IToken! x; Expr! e1; BinaryOperator.Opcode op;
+ UnaryExpression(out e0);
+ while (t.kind == 44 || t.kind == 73 || t.kind == 74) {
+ MulOp(out x, out op);
+ UnaryExpression(out e1);
+ e0 = Expr.Binary(x, op, e0, e1);
+ }
+ }
+
+ static void AddOp(out IToken! x, out BinaryOperator.Opcode op) {
+ x = Token.NoToken; op=BinaryOperator.Opcode.Add/*(dummy)*/;
+ if (t.kind == 71) {
+ Get();
+ x = token; op=BinaryOperator.Opcode.Add;
+ } else if (t.kind == 72) {
+ Get();
+ x = token; op=BinaryOperator.Opcode.Sub;
+ } else Error(112);
+ }
+
+ static void UnaryExpression(out Expr! e) {
+ IToken! x;
+ e = dummyExpr;
+
+ if (t.kind == 72) {
+ Get();
+ x = token;
+ UnaryExpression(out e);
+ e = Expr.Binary(x, BinaryOperator.Opcode.Sub, new LiteralExpr(x, BigNum.ZERO), e);
+ } else if (t.kind == 75 || t.kind == 76) {
+ NegOp();
+ x = token;
+ UnaryExpression(out e);
+ e = Expr.Unary(x, UnaryOperator.Opcode.Not, e);
+ } else if (StartOf(12)) {
+ CoercionExpression(out e);
+ } else Error(113);
+ }
+
+ static void MulOp(out IToken! x, out BinaryOperator.Opcode op) {
+ x = Token.NoToken; op=BinaryOperator.Opcode.Add/*(dummy)*/;
+ if (t.kind == 44) {
+ Get();
+ x = token; op=BinaryOperator.Opcode.Mul;
+ } else if (t.kind == 73) {
+ Get();
+ x = token; op=BinaryOperator.Opcode.Div;
+ } else if (t.kind == 74) {
+ Get();
+ x = token; op=BinaryOperator.Opcode.Mod;
+ } else Error(114);
+ }
+
+ static void NegOp() {
+ if (t.kind == 75) {
+ Get();
+ } else if (t.kind == 76) {
+ Get();
+ } else Error(115);
+ }
+
+ static void CoercionExpression(out Expr! e) {
+ IToken! x;
+ Type! coercedTo;
+ BigNum bn;
+
+ ArrayExpression(out e);
+ while (t.kind == 10) {
+ Get();
+ x = token;
+ if (StartOf(2)) {
+ Type(out coercedTo);
+ e = Expr.CoerceType(x, e, coercedTo);
+ } else if (t.kind == 3) {
+ Nat(out bn);
+ if (!(e is LiteralExpr) || !((LiteralExpr)e).isBigNum) {
+ SemErr("arguments of extract need to be integer literals");
+ e = new BvBounds(x, bn, BigNum.ZERO);
+ } else {
+ e = new BvBounds(x, bn, ((LiteralExpr)e).asBigNum);
+ }
+
+ } else Error(116);
+ }
+ }
+
+ static void ArrayExpression(out Expr! e) {
+ IToken! x;
+ Expr! index0 = dummyExpr; Expr! e1;
+ bool store; bool bvExtract;
+ ExprSeq! allArgs = dummyExprSeq;
+
+ AtomExpression(out e);
+ while (t.kind == 15) {
+ Get();
+ x = token; allArgs = new ExprSeq ();
+ allArgs.Add(e);
+ store = false; bvExtract = false;
+ if (StartOf(13)) {
+ if (StartOf(5)) {
+ Expression(out index0);
+ if (index0 is BvBounds)
+ bvExtract = true;
+ else
+ allArgs.Add(index0);
+
+ while (t.kind == 11) {
+ Get();
+ Expression(out e1);
+ if (bvExtract || e1 is BvBounds)
+ SemErr("bitvectors only have one dimension");
+ allArgs.Add(e1);
+
+ }
+ if (t.kind == 49) {
+ Get();
+ Expression(out e1);
+ if (bvExtract || e1 is BvBounds)
+ SemErr("assignment to bitvectors is not possible");
+ allArgs.Add(e1); store = true;
+
+ }
+ } else {
+ Get();
+ Expression(out e1);
+ allArgs.Add(e1); store = true;
+ }
+ }
+ Expect(16);
+ if (store)
+ e = new NAryExpr(x, new MapStore(x, allArgs.Length - 2), allArgs);
+ else if (bvExtract)
+ e = new ExtractExpr(x, e,
+ ((BvBounds)index0).Upper.ToIntSafe,
+ ((BvBounds)index0).Lower.ToIntSafe);
+ else
+ e = new NAryExpr(x, new MapSelect(x, allArgs.Length - 1), allArgs);
+
+ }
+ }
+
+ static void Nat(out BigNum n) {
+ Expect(3);
+ try {
+ n = BigNum.FromString(token.val);
+ } catch (FormatException) {
+ SemErr("incorrectly formatted number");
+ n = BigNum.ZERO;
+ }
+
+ }
+
+ static void AtomExpression(out Expr! e) {
+ IToken! x; int n; BigNum bn;
+ ExprSeq! es; VariableSeq! ds; Trigger trig;
+ TypeVariableSeq! typeParams;
+ IdentifierExpr! id;
+ Bpl.Type! ty;
+ QKeyValue kv;
+ e = dummyExpr;
+
+ switch (t.kind) {
+ case 77: {
+ Get();
+ e = new LiteralExpr(token, false);
+ break;
+ }
+ case 78: {
+ Get();
+ e = new LiteralExpr(token, true);
+ break;
+ }
+ case 3: {
+ Nat(out bn);
+ e = new LiteralExpr(token, bn);
+ break;
+ }
+ case 2: {
+ BvLit(out bn, out n);
+ e = new LiteralExpr(token, bn, n);
+ break;
+ }
+ case 1: {
+ Ident(out x);
+ id = new IdentifierExpr(x, x.val); e = id;
+ if (t.kind == 8) {
+ Get();
+ if (StartOf(5)) {
+ Expressions(out es);
+ e = new NAryExpr(x, new FunctionCall(id), es);
+ } else if (t.kind == 9) {
+ e = new NAryExpr(x, new FunctionCall(id), new ExprSeq());
+ } else Error(117);
+ Expect(9);
+ }
+ break;
+ }
+ case 79: {
+ Get();
+ x = token;
+ Expect(8);
+ Expression(out e);
+ Expect(9);
+ e = new OldExpr(x, e);
+ break;
+ }
+ case 8: {
+ Get();
+ if (StartOf(5)) {
+ Expression(out e);
+ if (e is BvBounds)
+ SemErr("parentheses around bitvector bounds " +
+ "are not allowed");
+ } else if (t.kind == 51 || t.kind == 80) {
+ Forall();
+ x = token;
+ QuantifierBody(x, out typeParams, out ds, out kv, out trig, out e);
+ if (typeParams.Length + ds.Length > 0)
+ e = new ForallExpr(x, typeParams, ds, kv, trig, e);
+ } else if (t.kind == 81 || t.kind == 82) {
+ Exists();
+ x = token;
+ QuantifierBody(x, out typeParams, out ds, out kv, out trig, out e);
+ if (typeParams.Length + ds.Length > 0)
+ e = new ExistsExpr(x, typeParams, ds, kv, trig, e);
+ } else Error(118);
+ Expect(9);
+ break;
+ }
+ default: Error(119); break;
+ }
+ }
+
+ static void BvLit(out BigNum n, out int m) {
+ Expect(2);
+ int pos = token.val.IndexOf("bv");
+ string a = token.val.Substring(0, pos);
+ string b = token.val.Substring(pos + 2);
+ try {
+ n = BigNum.FromString(a);
+ m = Convert.ToInt32(b);
+ } catch (FormatException) {
+ SemErr("incorrectly formatted bitvector");
+ n = BigNum.ZERO;
+ m = 0;
+ }
+
+ }
+
+ static void Forall() {
+ if (t.kind == 51) {
+ Get();
+ } else if (t.kind == 80) {
+ Get();
+ } else Error(120);
+ }
+
+ static void QuantifierBody(IToken! q, out TypeVariableSeq! typeParams, out VariableSeq! ds,
+out QKeyValue kv, out Trigger trig, out Expr! body) {
+ trig = null; typeParams = new TypeVariableSeq ();
+ IToken! tok; Expr! e; ExprSeq! es;
+ kv = null; string key; string value;
+ ds = new VariableSeq ();
+
+ if (t.kind == 17) {
+ TypeParams(out tok, out typeParams);
+ if (t.kind == 1) {
+ BoundVars(q, out ds);
+ }
+ } else if (t.kind == 1) {
+ BoundVars(q, out ds);
+ } else Error(121);
+ QSep();
+ while (t.kind == 25) {
+ AttributeOrTrigger(ref kv, ref trig);
+ }
+ Expression(out body);
+ }
+
+ static void Exists() {
+ if (t.kind == 81) {
+ Get();
+ } else if (t.kind == 82) {
+ Get();
+ } else Error(122);
+ }
+
+ static void AttributeOrTrigger(ref QKeyValue kv, ref Trigger trig) {
+ IToken! tok; Expr! e; ExprSeq! es;
+ string key; string value;
+ List<object!> parameters; object! param;
+
+ Expect(25);
+ tok = token;
+ if (t.kind == 10) {
+ Get();
+ Expect(1);
+ key = token.val; parameters = new List<object!>();
+ if (StartOf(14)) {
+ AttributeParameter(out param);
+ parameters.Add(param);
+ while (t.kind == 11) {
+ Get();
+ AttributeParameter(out param);
+ parameters.Add(param);
+ }
+ }
+ if (key == "nopats") {
+ if (parameters.Count == 1 && parameters[0] is Expr) {
+ e = (Expr)parameters[0];
+ if(trig==null){
+ trig = new Trigger(tok, false, new ExprSeq(e), null);
+ } else {
+ trig.AddLast(new Trigger(tok, false, new ExprSeq(e), null));
+ }
+ } else {
+ SemErr("the 'nopats' quantifier attribute expects a string-literal parameter");
+ }
+ } else {
+ if (kv==null) {
+ kv = new QKeyValue(tok, key, parameters, null);
+ } else {
+ kv.AddLast(new QKeyValue(tok, key, parameters, null));
+ }
+ }
+
+ } else if (StartOf(5)) {
+ Expression(out e);
+ es = new ExprSeq(e);
+ while (t.kind == 11) {
+ Get();
+ Expression(out e);
+ es.Add(e);
+ }
+ if (trig==null) {
+ trig = new Trigger(tok, true, es, null);
+ } else {
+ trig.AddLast(new Trigger(tok, true, es, null));
+ }
+
+ } else Error(123);
+ Expect(26);
+ }
+
+ static void AttributeParameter(out object! o) {
+ o = "error";
+ Expr! e;
+
+ if (t.kind == 4) {
+ Get();
+ o = token.val.Substring(1, token.val.Length-2);
+ } else if (StartOf(5)) {
+ Expression(out e);
+ o = e;
+ } else Error(124);
+ }
+
+ static void QSep() {
+ if (t.kind == 83) {
+ Get();
+ } else if (t.kind == 84) {
+ Get();
+ } else Error(125);
+ }
+
+
+
+ public static void Parse() {
+ Errors.SynErr = new ErrorProc(SynErr);
+ t = new Token();
+ Get();
+ BoogiePL();
+
+ }
+
+ [Microsoft.Contracts.Verify(false)]
+ static void SynErr(int n, string filename, int line, int col) {
+ Errors.count++;
+ Console.Write("{0}({1},{2}): syntax error: ", filename, line, col);
+ string s;
+ switch (n) {
+ case 0: s = "EOF expected"; break;
+ case 1: s = "ident expected"; break;
+ case 2: s = "bvlit expected"; break;
+ case 3: s = "digits expected"; break;
+ case 4: s = "string expected"; break;
+ case 5: s = "float expected"; break;
+ case 6: s = "var expected"; break;
+ case 7: s = "; expected"; break;
+ case 8: s = "( expected"; break;
+ case 9: s = ") expected"; break;
+ case 10: s = ": expected"; break;
+ case 11: s = ", expected"; break;
+ case 12: s = "where expected"; break;
+ case 13: s = "int expected"; break;
+ case 14: s = "bool expected"; break;
+ case 15: s = "[ expected"; break;
+ case 16: s = "] expected"; break;
+ case 17: s = "< expected"; break;
+ case 18: s = "> expected"; break;
+ case 19: s = "const expected"; break;
+ case 20: s = "unique expected"; break;
+ case 21: s = "extends expected"; break;
+ case 22: s = "complete expected"; break;
+ case 23: s = "function expected"; break;
+ case 24: s = "returns expected"; break;
+ case 25: s = "{ expected"; break;
+ case 26: s = "} expected"; break;
+ case 27: s = "axiom expected"; break;
+ case 28: s = "type expected"; break;
+ case 29: s = "= expected"; break;
+ case 30: s = "procedure expected"; break;
+ case 31: s = "implementation expected"; break;
+ case 32: s = "modifies expected"; break;
+ case 33: s = "free expected"; break;
+ case 34: s = "requires expected"; break;
+ case 35: s = "ensures expected"; break;
+ case 36: s = "{{ expected"; break;
+ case 37: s = "}} expected"; break;
+ case 38: s = "goto expected"; break;
+ case 39: s = "return expected"; break;
+ case 40: s = "if expected"; break;
+ case 41: s = "else expected"; break;
+ case 42: s = "while expected"; break;
+ case 43: s = "invariant expected"; break;
+ case 44: s = "* expected"; break;
+ case 45: s = "break expected"; break;
+ case 46: s = "assert expected"; break;
+ case 47: s = "assume expected"; break;
+ case 48: s = "havoc expected"; break;
+ case 49: s = ":= expected"; break;
+ case 50: s = "call expected"; break;
+ case 51: s = "forall expected"; break;
+ case 52: s = "<==> expected"; break;
+ case 53: s = "\\u21d4 expected"; break;
+ case 54: s = "==> expected"; break;
+ case 55: s = "\\u21d2 expected"; break;
+ case 56: s = "<== expected"; break;
+ case 57: s = "\\u21d0 expected"; break;
+ case 58: s = "&& expected"; break;
+ case 59: s = "\\u2227 expected"; break;
+ case 60: s = "|| expected"; break;
+ case 61: s = "\\u2228 expected"; break;
+ case 62: s = "== expected"; break;
+ case 63: s = "<= expected"; break;
+ case 64: s = ">= expected"; break;
+ case 65: s = "!= expected"; break;
+ case 66: s = "<: expected"; break;
+ case 67: s = "\\u2260 expected"; break;
+ case 68: s = "\\u2264 expected"; break;
+ case 69: s = "\\u2265 expected"; break;
+ case 70: s = "++ expected"; break;
+ case 71: s = "+ expected"; break;
+ case 72: s = "- expected"; break;
+ case 73: s = "/ expected"; break;
+ case 74: s = "% expected"; break;
+ case 75: s = "! expected"; break;
+ case 76: s = "\\u00ac expected"; break;
+ case 77: s = "false expected"; break;
+ case 78: s = "true expected"; break;
+ case 79: s = "old expected"; break;
+ case 80: s = "\\u2200 expected"; break;
+ case 81: s = "exists expected"; break;
+ case 82: s = "\\u2203 expected"; break;
+ case 83: s = ":: expected"; break;
+ case 84: s = "\\u2022 expected"; break;
+ case 85: s = "??? expected"; break;
+ case 86: s = "invalid Function"; break;
+ case 87: s = "invalid Procedure"; break;
+ case 88: s = "invalid Type"; break;
+ case 89: s = "invalid TypeAtom"; break;
+ case 90: s = "invalid TypeArgs"; break;
+ case 91: s = "invalid Spec"; break;
+ case 92: s = "invalid SpecPrePost"; break;
+ case 93: s = "invalid SpecPrePost"; break;
+ case 94: s = "invalid SpecPrePost"; break;
+ case 95: s = "invalid SpecBlock"; break;
+ case 96: s = "invalid LabelOrCmd"; break;
+ case 97: s = "invalid StructuredCmd"; break;
+ case 98: s = "invalid TransferCmd"; break;
+ case 99: s = "invalid IfCmd"; break;
+ case 100: s = "invalid Guard"; break;
+ case 101: s = "invalid LabelOrAssign"; break;
+ case 102: s = "invalid CallCmd"; break;
+ case 103: s = "invalid CallCmd"; break;
+ case 104: s = "invalid CallForallArg"; break;
+ case 105: s = "invalid CallOutIdent"; break;
+ case 106: s = "invalid EquivOp"; break;
+ case 107: s = "invalid ImpliesOp"; break;
+ case 108: s = "invalid ExpliesOp"; break;
+ case 109: s = "invalid AndOp"; break;
+ case 110: s = "invalid OrOp"; break;
+ case 111: s = "invalid RelOp"; break;
+ case 112: s = "invalid AddOp"; break;
+ case 113: s = "invalid UnaryExpression"; break;
+ case 114: s = "invalid MulOp"; break;
+ case 115: s = "invalid NegOp"; break;
+ case 116: s = "invalid CoercionExpression"; break;
+ case 117: s = "invalid AtomExpression"; break;
+ case 118: s = "invalid AtomExpression"; break;
+ case 119: s = "invalid AtomExpression"; break;
+ case 120: s = "invalid Forall"; break;
+ case 121: s = "invalid QuantifierBody"; break;
+ case 122: s = "invalid Exists"; break;
+ case 123: s = "invalid AttributeOrTrigger"; break;
+ case 124: s = "invalid AttributeParameter"; break;
+ case 125: s = "invalid QSep"; break;
+
+ default: s = "error " + n; break;
+ }
+ Console.WriteLine(s);
+ }
+
+ static bool[,]! set = {
+ {T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x},
+ {x,x,x,x, x,x,T,x, x,x,x,x, x,x,x,x, x,x,x,T, x,x,x,T, x,x,x,T, T,x,T,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x},
+ {x,T,x,x, x,x,x,x, T,x,x,x, x,T,T,T, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x},
+ {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,T,T,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x},
+ {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,x,x, x,x,x,x, T,T,T,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x},
+ {x,T,T,T, x,x,x,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,T, T,T,T,T, x,x,x,x, x,x,x},
+ {x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,T,T, T,x,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x},
+ {x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,T,T, T,x,T,x, x,T,T,T, T,x,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x},
+ {x,T,T,T, x,x,x,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,T, T,T,T,T, x,x,x,x, x,x,x},
+ {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,T,T, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x},
+ {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,T,T, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x},
+ {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,T,T, T,T,T,T, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x},
+ {x,T,T,T, x,x,x,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,T,T, x,x,x,x, x,x,x},
+ {x,T,T,T, x,x,x,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,T, T,T,T,T, x,x,x,x, x,x,x},
+ {x,T,T,T, T,x,x,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,T, T,T,T,T, x,x,x,x, x,x,x}
+
+ };
+
+ [Microsoft.Contracts.Verify(false)]
+ static Parser() {}
+} // end Parser
+
+} // end namespace
diff --git a/Source/Core/PureCollections.ssc b/Source/Core/PureCollections.ssc new file mode 100644 index 00000000..d51ebe62 --- /dev/null +++ b/Source/Core/PureCollections.ssc @@ -0,0 +1,785 @@ +//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+
+//---------------------------------------------------------------------
+// PureCollections.cs
+// - Mostly pure functions for tuples, sets, maps, Sequences
+// Version 1.0, WS, 3/23/2002
+//---------------------------------------------------------------------
+
+
+using System;
+using System.Collections;
+using Microsoft.Contracts;
+
+namespace PureCollections {
+ //-------------------------------------------------------------------
+ // General types
+ //-------------------------------------------------------------------
+
+ public class MissingCase :Exception{}
+
+ public struct Capacity{
+ public int capacity;
+ public Capacity (int i) {capacity = i;}
+ }
+
+ abstract public class Coll {
+ public object[] elems; // null is used to show empty spots!
+ protected int card;
+ protected Coll() {}
+ protected Coll(object[] elems, int card) {this.elems = elems; this.card = card; }
+ protected Coll(Coll! c)
+ requires c.elems != null;
+ {
+ this.elems = (object[])c.elems.Clone();
+ this.card = c.card;
+ }
+ }
+
+
+ // ------------------------------------------------------------------
+ // Tuple
+ // ------------------------------------------------------------------
+
+ public class Tuple : Coll, IComparable
+ {
+ //public object[] elems;
+
+ //invariant this.elems != null;
+
+ // Constructor - - - - - - - - - - - - - - - - - - - - - - - - - -
+ public Tuple(params object []! ts) {
+ elems = ts;
+ card = ts.Length;}
+ public Tuple(Capacity c) {
+ elems = new object[c.capacity];
+ card = c.capacity;
+ }
+
+ //Equality - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)]
+ public override bool Equals (object o){
+ assert this.elems != null;
+ if (o == null || !(o is Tuple) || elems.Length != ((!)((Tuple)o).elems).Length)
+ return false;
+
+ Tuple s = (Tuple) o;
+ for(int i = 0; i < elems.Length; i ++)
+ if ( ! Equals(this.elems[i], s.elems[i]))
+ return false;
+ return true;
+ }
+
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)] // ugh, is this right? --KRML
+ public static bool operator == (Tuple s, Tuple t) {return s == null ? t == null : s.Equals(t);}
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)] // ugh, is this right? --KRML
+ public static bool operator != (Tuple s, Tuple t) { return ! (t == s); }
+
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public override int GetHashCode (){
+ int h =0;
+ assume this.elems != null;
+ for(int i = 0; i < elems.Length; i++)
+ {
+ object elem = elems[i];
+ if (elem != null)
+ h += elem.GetHashCode();
+ }
+ return h;
+ }
+
+ //Compare - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ int IComparable.CompareTo(object o) {
+ assert this.elems != null;
+ if (o == null || !(o is Tuple) || elems.Length != ((!)((Tuple)o).elems).Length)
+ throw new MissingCase();
+
+ Tuple t = (Tuple) o;
+ for(int i = 0; i < elems.Length; i ++) {
+ int c = ((IComparable!) elems[i]).CompareTo(t.elems[i]);
+ if (c < 0) return -1;
+ else if (c > 0) return +1;
+ }
+ return 0;
+ }
+
+ public static bool operator <= (Tuple s, Tuple t) {return s == null ? t == null : ((IComparable) s).CompareTo(t) <= 0;}
+ public static bool operator < (Tuple s, Tuple t) {return s == null ? false : ((IComparable) s).CompareTo(t) < 0;}
+ public static bool operator >= (Tuple s, Tuple t) {return t <= s; }
+ public static bool operator > (Tuple s, Tuple t) { return t < s; }
+
+ //Select and Update - - - - - - - - - - - - - - - - - - - - - - - -
+ public object this[int index]{
+ get{assert this.elems != null; return elems[index];}
+ set{assert this.elems != null; elems[index] = value;}
+ }
+
+ //ToString - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public override string! ToString() {
+ assert this.elems != null;
+ if (elems.Length==0)
+ return "()";
+
+ string s = "(";
+ for (int i= 0; i< elems.Length-1; i++)
+ s += ((!)elems[i]).ToString() + ", ";
+ return s + ((!)elems[elems.Length-1]).ToString() + ")";
+ }
+ }
+
+ // ------------------------------------------------------------------
+ // Pair
+ public class Pair : Tuple{
+ protected Pair(){}
+ public Pair(object first, object second) {
+ elems = new object[]{first,second};
+ }
+ public object First{
+ get{assert this.elems != null; return elems[0];}
+ set{assert this.elems != null; elems[0]=value;}
+ }
+ public object Second{
+ get{assert this.elems != null; return elems[1];}
+ set{assert this.elems != null; elems[1]=value;}
+ }
+ }
+
+ // --------------------------------------------------------------------
+ // Map
+ // --------------------------------------------------------------------
+
+ public class MapEnumerator: IEnumerator{
+ private Map! map;
+ private int index = -1;
+ public MapEnumerator(Map! m) { map = m;}
+ public bool MoveNext() {
+ do{
+ index++;
+ assert map.elems != null;
+ } while (index < map.elems.Length && map.elems[index] == null);
+ return index < map.elems.Length;
+ }
+ public object Current{ [Pure][Reads(ReadsAttribute.Reads.Owned)] get {assert map.elems != null; return new Pair(map.elems[index],map.vals[index]); }}
+ public void Reset() {index = -1; }
+ }
+
+ public class Map:Coll, IEnumerable, IComparable
+ {
+ public Object[]! vals;
+
+ //invariant this.elems != null;
+
+ // constructors - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ public Map(Capacity c){
+ elems = new Object[c.capacity*2];
+ vals = new Object[c.capacity*2];
+ card = 0;
+ }
+
+ [NotDelayed]
+ public Map(params Pair []! ps){
+ elems = new Object[ps.Length*2];
+ vals = new Object[ps.Length*2];
+ base();
+ card = 0;
+ for(int i = 0; i < ps.Length; i++)
+ Insert( ((!)ps[i]).First, ((!)ps[i]).Second);
+ }
+
+ // iterators - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ [Pure] [GlobalAccess(false)] [Escapes(true,false)]
+ public IEnumerator! GetEnumerator() {
+ return new MapEnumerator(this);
+ }
+ public Pair[] ToArray() {
+ Pair [] n = new Pair[card];
+ int ct = 0;
+ assert this.elems != null;
+ for(int i =0; i < elems.Length ; i++)
+ if (elems[i] != null)
+ n[ct++] = new Pair(elems[i], vals[i]);
+ return n;
+ }
+
+ //(ASM) Update- - - - - - - - - - - - - - - - - - - - - - - - - - -
+ public Map Update(object k, object v) {
+ Map n = new Map(new Capacity(card+1));
+ assert this.elems != null;
+ for (int i = 0; i < elems.Length; i++ )
+ if (elems[i] != null && ! Equals(elems[i], k))
+ n.Insert(elems[i], vals[i]);
+ n.Insert(k,v);
+ return n;
+ }
+
+ //In place Update (and Remove)- - - - - - - - - - - - - - - - - - -
+ public object this[object index]{
+ get{return this.Apply(index);}
+ set{this.Insert(index,value);}
+ }
+
+ public void Remove(object! o) {
+ assert this.elems != null;
+ int h = Math.Abs(o.GetHashCode()) % elems.Length;
+ for (int i = 0; i < elems.Length; i++ ) {
+ int j = (i+ h) % elems.Length;
+ if (elems[j] == null) {
+ break;
+ } else if (Equals(elems[j], o)){
+ elems[j] = null;
+ vals[j] = null;
+ break;
+ }
+ }
+ }
+
+ public void Insert(Object key, Object val) {
+ if (key == null)
+ throw new MissingCase();
+
+ assert this.elems != null;
+ if (elems.Length == 0 || 2*card >= elems.Length){
+ int m = card*2; if (m < 4) m = 4;
+ object [] newElems = new object [m];
+ object [] newVals = new object [m];
+ for (int k = 0; k < elems.Length; k++) {
+ object elem = elems[k];
+ if (elem != null) {
+ int newHash = Math.Abs(elem.GetHashCode()) % newElems.Length;
+ for (int i = 0; i < newElems.Length; i++ ) {
+ int j = (i+ newHash) % newElems.Length;
+ if (newElems[j] == null) {
+ newElems[j] = elem;
+ newVals[j] = vals[k];
+ break;
+ }
+ }
+ }
+ }
+ elems = newElems;
+ vals = newVals;
+ }
+ int h = Math.Abs(key.GetHashCode()) % elems.Length;
+ for (int i = 0; i < elems.Length; i++ ) {
+ int j = (i+ h) % elems.Length;
+ if (elems[j] == null) {
+ elems[j] = key;
+ vals[j] = val;
+ card ++;
+ return;
+ } else if (key.Equals(elems[j])) {
+ vals[j] = val;
+ return;
+ }
+ }
+ }
+
+ //ToString - - - - - - - - - - - - - - - - - - - - - - - - -
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public override string! ToString() {
+ if (card ==0)
+ return "{|->}";
+ else {
+ string s = "{"; int ct = 0;
+ assert this.elems != null;
+ for(int i =0; i < elems.Length ; i++) {
+ object elem = elems[i];
+ if (elem != null){
+ s += elem.ToString() +"|->" + ((!)vals[i]).ToString() ;
+ s +=(ct!=card-1) ? ", " : "";
+ ct ++;
+ }
+ }
+ return s+"}";
+ }
+ }
+
+ // Subset operations - - - - - - - - - - - - - - - - - - - - - - -
+ // View Map as Set of Pairs
+
+ int IComparable.CompareTo(object o) {
+ if (o == null || !(o is Map))
+ throw new MissingCase();
+ // WS Improve performance!
+ Map t = (Map) o;
+ if (this < t) return -1;
+ else if(this > t) return +1;
+ else return 0;
+ }
+ public static bool operator <= (Map s, Map t){
+ if (s==null) return t==null;
+ if (t==null) return false;
+ assert s.elems != null;
+ for(int i = 0; i < s.elems.Length; i++)
+ if (s.elems[i]!= null) {
+ object o = t.Apply(s.elems[i]);
+ if (o == null || !o.Equals(s.vals[i]))
+ return false;
+ }
+ return true;
+ }
+ public static bool operator < (Map s, Map t){
+ return s == null || t == null ? false : s.card < t.card && s <= t;
+ }
+ public static bool operator >= (Map s, Map t){
+ return t <= s;
+ }
+ public static bool operator > (Map s, Map t){
+ return t < s;
+ }
+
+ // Equality - - - - - - - - - - - - - - - - - - - - - - -
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)]
+ public override bool Equals (Object t){
+ return t != null && t is Map && card == ((Map) t).card && this<= ((Map) t);
+ }
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)] // ugh, is this right? --KRML
+ public static bool operator == (Map s, Map t){
+ if ((object)s==null)
+ if ((object)t==null) return true;
+ else return t.Equals(s);
+ else
+ return s.Equals(t);
+ }
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)] // ugh, is this right? --KRML
+ public static bool operator != (Map s, Map t){
+ return ! (t == s);
+ }
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public override int GetHashCode (){
+ int h =0;
+ assert this.elems != null;
+ for(int i = 0; i < elems.Length; i++)
+ {
+ object elem = elems[i];
+ if (elem != null)
+ {
+ h += elem.GetHashCode() + ((!)vals[i]).GetHashCode();
+ }
+ }
+ return h;
+ }
+
+ //Ordinary map operations- - - - - - - - - - - - - - - - - - - - - - - -
+
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public bool Has(Object x) {
+ if (x == null)
+ throw new MissingCase();
+
+ assert this.elems != null;
+ if (elems.Length == 0)
+ return false;
+ int h = Math.Abs(x.GetHashCode()) % elems.Length;
+ for (int i = 0; i < elems.Length; i++ ) {
+ int j = (i+ h) % elems.Length;
+ if (x.Equals(elems[j]))
+ return true;
+ }
+ return false;
+ }
+
+ public object Apply(object x) {
+ if (x == null)
+ throw new MissingCase();
+ assert this.elems != null;
+ if (elems.Length == 0)
+ return null;
+ int h = Math.Abs(x.GetHashCode()) % elems.Length;
+ for (int i = 0; i < elems.Length; i++ ) {
+ int j = (i+ h) % elems.Length;
+ if (elems[j] != null && x.Equals(elems[j]))
+ return vals[j];
+ }
+ return null;
+ }
+
+ public static Map Override(Map! s, Map! t) {
+ Map m = new Map(new Capacity(s.card+t.card));
+ assert s.elems != null;
+ for(int i = 0; i< s.elems.Length; i++)
+ if (s.elems[i] != null)
+ m.Insert(s.elems[i], s.vals[i]);
+ assert t.elems != null;
+ for(int i = 0; i< t.elems.Length; i++)
+ if (t.elems[i] != null)
+ m.Insert(t.elems[i], t.vals[i]);
+ return m;
+ }
+ }
+
+ // --------------------------------------------------------------------
+ // Sequence
+
+ public class SequenceEnumerator: IEnumerator{
+ [Peer] private Sequence! seq;
+ private int index = -1;
+ [Captured]
+ public SequenceEnumerator(Sequence! s) { seq = s;}
+ public bool MoveNext() {
+ index++;
+ //while (index < seq.elems.Length); // Sequences allow nils ... && seq.elems[index] == null);
+ return index < seq.Length;
+ }
+ public object Current{ [Pure][Reads(ReadsAttribute.Reads.Owned)] get {assert seq.elems != null; return seq.elems[index]; }}
+ public void Reset() {index = -1; }
+ }
+
+ public class Sequence:Coll, IEnumerable, IComparable
+ {
+ public Sequence(){elems = new object [4];}
+
+ //invariant this.elems != null;
+
+ //constructors - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ public Sequence(params object []! ds){card = ds.Length; elems = ds; }
+ public Sequence(Sequence! seq) {
+ base(seq);
+ }
+ public Sequence(Capacity c){elems = new object [c.capacity];}
+
+ // Iterators - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ [Pure] [GlobalAccess(false)] [Escapes(true,false)]
+ public IEnumerator! GetEnumerator()
+ ensures Owner.Same(result, this);
+ ensures result.IsNew;
+ {
+ return new SequenceEnumerator(this);
+ }
+
+ public object[] ToArray() {
+ object [] n = new object[card];
+ int ct = 0;
+ assert this.elems != null;
+ for(int i =0; i < elems.Length ; i++)
+ n[ct++] = elems[i];
+ return n;
+ }
+
+ //ASM Update - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ public Sequence Update(int i, object v) {
+ Sequence n = new Sequence(new Capacity(card+1));
+ assert this.elems != null;
+ assert n.elems != null;
+ for (int j = 0; j < elems.Length; j++ )
+ n.elems[j] = elems[j];
+ if (i >= 0 && i < card){
+ n.elems[i] = v;
+ n.card = card;
+ return n;
+ } else if (i == card) {
+ n.elems[i] = v;
+ n.card = card+1;
+ return n;
+ } else
+ throw new Exception("Sequence Update out of range");
+ }
+
+ //In place Update (and Remove) and Length - - - - - - - - - - - - - - -
+ public int Length {
+ get{return this.card;}
+ }
+
+ public object this[int index]{
+ get{assert this.elems != null; return this.elems[index];}
+ set{assert this.elems != null; this.elems[index] = value;}
+ }
+
+ public void Add(object o){
+ assert this.elems != null;
+ int n = this.elems.Length;
+ int i = this.card++;
+ if (i == n){
+ int m = n*2; if (m < 4) m = 4;
+ object [] newElems = new object [m];
+ for (int j = 0; j < n; j++) newElems[j] = elems[j];
+ elems = newElems;
+ }
+ elems[i] = o;
+ }
+
+ public void AddRange(Sequence! seq){
+ foreach (object o in seq) {
+ Add(o);
+ }
+ }
+
+ public void Remove(){
+ if (card == 0)
+ return;
+ card--;
+ }
+
+ // remove the first occurrence of o from this sequence
+ public void Remove(Object x) {
+ if (x == null)
+ throw new MissingCase();
+ assert this.elems != null;
+ for (int i = 0; i < card; i++) {
+ if (x.Equals(elems[i])) {
+ ++i;
+ while (i < card) {
+ elems[i-1] = elems[i];
+ ++i;
+ }
+ card--;
+ elems[card] = null;
+ return;
+ }
+ }
+ }
+
+ public void Truncate(int newLen)
+ requires 0 <= newLen && newLen <= Length;
+ {
+ assert elems != null;
+ for (int i = newLen; i < card; i++) {
+ elems[i] = null;
+ }
+ card = newLen;
+ }
+
+ //ToString - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public override string! ToString() {
+ string s ="";
+ assert this.elems != null;
+ if (card > 0 && elems[0] is Char) {
+ for(int i =0; i < card ; i++)
+ {
+ object elem = elems[i];
+ if (elem != null) { s +=elem.ToString(); }
+ }
+ return s;
+ } else {
+ s = "[";
+ for(int i =0; i < card-1; i++) {
+ object elem = elems[i];
+ if (elem != null) { s += elem.ToString()+", "; }
+ }
+ if (card > 0)
+ {
+ object last = elems[card-1];
+ if (last != null) { s += last.ToString(); }
+ }
+ s += "]";
+ return s;
+ }
+ }
+ //Equality- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)]
+ public override bool Equals (object that){
+ return that != null && that is Sequence && ((Sequence) this == (Sequence) that);
+ }
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)] // ugh, is this right? --KRML
+ public static bool operator == (Sequence s, Sequence t){
+ if ((object)s == (object)t) {
+ return true;
+ } else if ((object)s == null || (object)t == null) {
+ return false;
+ }
+ if (s.card != t.card) return false;
+ assert s.elems != null;
+ assert t.elems != null;
+ for(int i = 0; i < s.card; i++)
+ if (! Equals(s.elems[i], t.elems[i]))
+ return false;
+ return true;
+ }
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)] // ugh, is this right? --KRML
+ public static bool operator != (Sequence s, Sequence t){
+ return !(s == t);
+ }
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public override int GetHashCode (){
+ int h = 0;
+ for(int i = 0; i < card; i++)
+ {
+ assert this.elems != null;
+ object elem = elems[i];
+ if (elem != null) { h += elem.GetHashCode(); }
+ }
+ return h;
+ }
+ //Subset- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ // View Sequence of T as Set of (Integer,T)
+ int IComparable.CompareTo(object o) {
+ if (o == null || !(o is Sequence))
+ throw new MissingCase();
+ // WS Improve performance!
+ Sequence t = (Sequence) o;
+ if (this < t) return -1;
+ else if(this > t) return +1;
+ else return 0;
+ }
+
+ public static bool operator < (Sequence s, Sequence t){
+ if (s==null) throw new ArgumentNullException("s");
+ if (t==null) throw new ArgumentNullException("t");
+ if (s.card >= t.card) return false;
+ assert s.elems != null;
+ assert t.elems != null;
+ for(int i = 0; i < s.card; i++)
+ if ( ! Equals(s.elems[i], t.elems[i]))
+ return false;
+ return true;
+ }
+ public static bool operator <= (Sequence s, Sequence t){
+ if (s==null) throw new ArgumentNullException("s");
+ if (t==null) throw new ArgumentNullException("t");
+ if (s.card > t.card) return false;
+ assert s.elems != null;
+ assert t.elems != null;
+ for(int i = 0; i < s.card; i++)
+ if ( ! Equals(s.elems[i], t.elems[i]))
+ return false;
+ return true;
+ }
+ public static bool operator > (Sequence s, Sequence t){ return t < s;}
+ public static bool operator >= (Sequence s, Sequence t){ return t <= s;}
+
+ //pure---------------------------------------------------------------
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public bool Has(object x) { // WS translate to tailrecursion
+ if (x == null)
+ throw new MissingCase();
+ assert this.elems != null;
+ for (int i = 0; i< card; i++)
+ if (x.Equals(elems[i]))
+ return true;
+ return false;
+ }
+
+ // the index of the first occurrence of x in this sequence,
+ // or -1 if x does not occur
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public int IndexOf(object x) {
+ if (x == null)
+ throw new MissingCase();
+ assert this.elems != null;
+ for (int i = 0; i< card; i++)
+ if (x.Equals(elems[i]))
+ return i;
+ return -1;
+ }
+
+ // the index of the last occurrence of x in this sequence,
+ // or -1 if x does not occur
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public int LastIndexOf(object x) {
+ if (x == null)
+ throw new MissingCase();
+ assert this.elems != null;
+ for (int i = card - 1; i >= 0; i--)
+ if (x.Equals(elems[i]))
+ return i;
+ return -1;
+ }
+
+ public object Head() {assert this.elems != null; return elems[0]; }
+ public object Last() {assert this.elems != null; return elems[card-1]; }
+
+ public static Sequence Tail(Sequence! s) {
+ Sequence n = new Sequence(new Capacity(s.card-1));
+ assert n.elems != null;
+ assert s.elems != null;
+ for (int i = 1; i< s.card; i++) n.elems[n.card++] = s.elems[i];
+ return n;
+ }
+
+ public static Sequence Front(Sequence! s) {
+ Sequence n = new Sequence(new Capacity(s.card-1));
+ assert n.elems != null;
+ assert s.elems != null;
+ for (int i = 0; i< s.card-1; i++) n.elems[n.card++] = s.elems[i];
+ return n;
+ }
+ public static Sequence Concat(Sequence! s) {
+ Sequence n = new Sequence(new Capacity(s.card));
+ assert n.elems != null;
+ assert s.elems != null;
+ for (int i = 0; i< s.card; i++) {
+ Sequence t = (Sequence!) s.elems[i];
+ assert t.elems != null;
+ for (int j = 0; j < t.card; j ++)
+ n.Add(t.elems[j]);
+ }
+ return n;
+ }
+
+ public static Sequence Reverse(Sequence! s) {
+ Sequence n = new Sequence(new Capacity(s.card));
+ assert n.elems != null;
+ assert s.elems != null;
+ for (int i = s.card-1; i>=0; i--) n.elems[n.card++] = s.elems[i];
+ return n;
+ }
+
+ public static Sequence operator + (Sequence s, Sequence t)
+ {
+ if (s==null) throw new ArgumentNullException("s");
+ if (t == null) throw new ArgumentNullException("t");
+ return Append(t,s);
+ }
+
+ public static Sequence! Append(Sequence! s, Sequence! t) {
+ Sequence! n = new Sequence(new Capacity(s.card + t.card));
+ assert n.elems != null;
+ assert s.elems != null;
+ assert t.elems != null;
+ for (int i = 0; i< s.card; i++) n.elems[n.card++] = s.elems[i];
+ for (int i = 0; i< t.card; i++) n.elems[n.card++] = t.elems[i];
+ return n;
+ }
+ public static Sequence Zip(Sequence! s, Sequence! t) {
+ int min = s.card<t.card ? s.card : t.card;
+ Sequence n = new Sequence(new Capacity(min));
+ assert n.elems != null;
+ assert s.elems != null;
+ assert t.elems != null;
+ for (int i = 0; i< min; i++) n.elems[n.card++] = new Tuple(s.elems[i], t.elems[i]);
+ return n;
+ }
+ public static Tuple Unzip(Sequence! s) {
+ Sequence n0 = new Sequence(new Capacity(s.card));
+ Sequence n1 = new Sequence(new Capacity(s.card));
+ assert s.elems != null;
+ assert n0.elems != null;
+ assert n1.elems != null;
+ for (int i = 0; i< s.card; i++) {
+ n0.elems[n0.card++] = ((!)((Tuple!)s.elems[i]).elems)[0];
+ n1.elems[n1.card++] = ((!)((Tuple!)s.elems[i]).elems)[1];
+ }
+ return new Tuple(n0,n1);
+ }
+
+ public static Sequence FromTo(int from, int to) { //WS hash the result!
+ if (from > to) return new Sequence();
+ Sequence n = new Sequence(new Capacity(to-from+1));
+ assert n.elems != null;
+ for (int i = from; i<= to; i++)
+ n.elems[n.card++] = i;
+ return n;
+ }
+
+ public static Sequence FromStepTo(int from, int step, int to) {
+ Sequence n = new Sequence();
+ int incr = step-from;
+ if (incr >0)
+ for (int i = from; i<= to; i+=incr)
+ n.Add(i);
+ else if (incr < 0)
+ for (int i = to; i>= from; i-=incr)
+ n.Add(i);
+ return n;
+ }
+
+ }
+
+}
diff --git a/Source/Core/Readme.txt b/Source/Core/Readme.txt new file mode 100644 index 00000000..1b0606a6 --- /dev/null +++ b/Source/Core/Readme.txt @@ -0,0 +1,61 @@ +// ----------------------------------------------------------------------------
+// Boogie-PL
+//
+// Readme
+// ws 5/9/03
+// ----------------------------------------------------------------------------
+
+
+This directory cointains the Boogie Procedural Language (BoogiePL)
+implementataion and "1" sample program.
+
+
+Scanner and parser are generated with Coco (ann LL1 parser generator for EBNFs)
+(see http://www.ssw.uni-linz.ac.at/Research/Projects/Coco/CSharp/)
+
+ The input file is
+ BoogiePL.atg
+ then simply call
+ ..\Coco\bin\Debug\Coco.exe BoogiePL.atg
+ it then uses (as input)
+ Scanner.frame
+ Parser.frame
+ as templates to generate an LL1 parser into
+ Scanner.cs
+ Parser.cs
+ as output
+
+The Csharp excutable then contains
+
+ BoogiePL.cs -- main program
+ Absy -- abstract syntax for BoogiePL
+ Error.cs -- error handling (contains still some oldstuff)
+ Parser.cs -- generated parser
+ Scanner.cs -- generated scanner
+ PureCollections.cs -- sets/maps/tuples/ (contains still some oldstuff)
+
+The directory Samples contains one parsing example
+ Parsing1.pl
+Please check it for the syntax, alternatively consult BoogiePL.atg
+
+Here is its output:
+ C:\Boogie> bin\debug\Boogiepl.exe samples\Parsing1.pl
+
+ Boogie Procedural Language Version 0.1 Copyright (c) Microsoft 2003
+ Parsing samples\Parsing1.pl <<<=== here is what is does
+ 0 errors detected
+
+Things left to do:
+
+ BoogiePL needs a tiny context analysis
+ checking names, updates, arities, OLD, etc.
+ (ws will do until 5/8)
+
+ BoogiePL Absy might be too flexible
+ simplify (if one things so..) (Mike/Rustan will do)
+
+ BoogiePL needs more examples/experiences
+ (all of us..)
+
+
+
diff --git a/Source/Core/ResolutionContext.ssc b/Source/Core/ResolutionContext.ssc new file mode 100644 index 00000000..618f4a3a --- /dev/null +++ b/Source/Core/ResolutionContext.ssc @@ -0,0 +1,518 @@ +//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+namespace Microsoft.Boogie
+{
+ using System.Collections;
+ using System.Collections.Generic;
+ using System;
+ using Microsoft.SpecSharp.Collections;
+ using Microsoft.Contracts;
+
+ public interface IErrorSink
+ {
+ void Error(IToken! tok, string! msg);
+ }
+
+ public class CheckingContext
+ {
+ // ------------------------------ Error counting ------------------------------
+
+ IErrorSink errorSink;
+ int errors;
+
+ public CheckingContext(IErrorSink errorSink)
+ {
+ this.errorSink = errorSink;
+ }
+
+ public int ErrorCount
+ {
+ get { return errors; }
+ set { errors = value; }
+ }
+
+ public void Error(Absy! subject, string! msg, params object[]! args)
+ {
+ Error(subject.tok, msg, args);
+ }
+
+ public virtual void Error(IToken! tok, string! msg)
+ {
+ errors++;
+ if (errorSink == null) {
+ ConsoleColor col = Console.ForegroundColor;
+ Console.ForegroundColor = ConsoleColor.Red;
+ Console.WriteLine("{0}({1},{2}): Error: {3}",
+ tok.filename, tok.line, tok.col-1,
+ msg);
+ Console.ForegroundColor = col;
+ } else {
+ errorSink.Error(tok, msg);
+ }
+ }
+
+ private string! Format(string! msg, params object[] args) {
+ if (System.Type.GetType("Mono.Runtime") != null) { // MONO
+ // something in mono seems to be broken so that calling
+ // NamedDeclarations.ToString (and similar ToString methods)
+ // causes a stack overflow. We therefore convert those to
+ // strings by hand
+ object[] fixedArgs = new object [((!)args).Length];
+ for (int i = 0; i < args.Length; ++i) {
+ if (args[i] is NamedDeclaration) {
+ fixedArgs[i] = ((NamedDeclaration!)args[i]).Name;
+ } else if (args[i] is Type) {
+ System.IO.StringWriter buffer = new System.IO.StringWriter();
+ using (TokenTextWriter stream = new TokenTextWriter("<buffer>", buffer, false)) {
+ ((Type!)args[i]).Emit(stream);
+ }
+ fixedArgs[i] = buffer.ToString();
+ } else if (args[i] is Expr) {
+ System.IO.StringWriter buffer = new System.IO.StringWriter();
+ using (TokenTextWriter stream = new TokenTextWriter("<buffer>", buffer, false)) {
+ ((Expr!)args[i]).Emit(stream, 0, false);
+ }
+ fixedArgs[i] = buffer.ToString();
+ } else {
+ fixedArgs[i] = args[i];
+ }
+ }
+ args = fixedArgs;
+ }
+ return string.Format(msg, args);
+ }
+
+ public void Error(IToken! tok, string! msg, params object[] args)
+ {
+ Error(tok, Format(msg, args));
+ }
+
+ public void Warning(Absy! subject, string! msg, params object[]! args)
+ {
+ Warning(subject.tok, msg, args);
+ }
+
+ public virtual void Warning(IToken! tok, string! msg)
+ {
+ // warnings are currently always written to the console
+ ConsoleColor col = Console.ForegroundColor;
+ Console.ForegroundColor = ConsoleColor.DarkYellow;
+ Console.WriteLine("{0}({1},{2}): Warning: {3}",
+ tok.filename, tok.line, tok.col-1,
+ msg);
+ Console.ForegroundColor = col;
+ }
+
+ public void Warning(IToken! tok, string! msg, params object[] args)
+ {
+ Warning(tok, Format(msg, args));
+ }
+ }
+
+ public class ResolutionContext : CheckingContext
+ {
+ public ResolutionContext(IErrorSink errorSink)
+ {
+ base(errorSink);
+ }
+
+ // ------------------------------ Boogie 2 Types -------------------------
+
+ // user-defined types, which can be either TypeCtorDecl or TypeSynonymDecl
+ Hashtable /*string->NamedDeclaration*/! types = new Hashtable /*string->NamedDeclaration*/ ();
+
+ private void CheckBvNameClashes(Absy! absy, string! name) {
+ if (name.StartsWith("bv") && name.Length > 2) {
+ bool isBv = true;
+ for (int i = 2; i < name.Length; ++i)
+ if (!char.IsDigit(name[i])) isBv = false;
+ if (isBv)
+ Error(absy, "type name: {0} is registered for bitvectors", name);
+ }
+ }
+
+ public void AddType(NamedDeclaration! td)
+ {
+ assert (td is TypeCtorDecl) || (td is TypeSynonymDecl);
+
+ string! name = (!)td.Name;
+ CheckBvNameClashes(td, name);
+
+ if (types[name] != null)
+ {
+ Error(td, "more than one declaration of type name: {0}", name);
+ }
+ else
+ {
+ types.Add(name, td);
+ }
+ }
+
+ /// <summary>
+ /// Returns the declaration of the named type, or null if
+ /// no such type is declared. Also return null if the type
+ /// declared with the given name is not a constructor but a
+ /// type synonym
+ /// </summary>
+ /// <param name="name"></param>
+ /// <returns></returns>
+ public TypeCtorDecl LookUpType(string! name)
+ {
+ return types[name] as TypeCtorDecl;
+ }
+
+ public TypeSynonymDecl LookUpTypeSynonym(string! name)
+ {
+ return types[name] as TypeSynonymDecl;
+ }
+
+ // ------------------------------ Boogie 2 Type Binders ------------------------------
+
+ List<TypeVariable!>! typeBinders = new List<TypeVariable!>(5);
+
+ public void AddTypeBinder(TypeVariable! td) {
+ CheckBvNameClashes(td, td.Name);
+ if (types.ContainsKey(td.Name)) {
+ Error(td, "name is already reserved for type constructor: {0}", td.Name);
+ return;
+ }
+ for (int i = 0; i < typeBinders.Count; i++) {
+ if (typeBinders[i].Name == td.Name) {
+ Error(td, "more than one declaration of type variable: {0}", td.Name);
+ return;
+ }
+ }
+ typeBinders.Add(td);
+ }
+
+ public int TypeBinderState {
+ get { return typeBinders.Count; }
+ set { typeBinders.RemoveRange(value, typeBinders.Count - value); }
+ }
+
+ /// <summary>
+ /// Returns the declaration of the named type binder, or null if
+ /// no such binder is declared.
+ /// </summary>
+ public TypeVariable LookUpTypeBinder(string! name)
+ {
+ for (int i = typeBinders.Count; 0 <= --i; ) {
+ TypeVariable! td = typeBinders[i];
+ if (td.Name == name) {
+ return td;
+ }
+ }
+ return null; // not present
+ }
+
+ // ------------------------------ Types ------------------------------
+
+ // user-defined types
+ // Hashtable /*string->TypeDecl*/! types = new Hashtable /*string->TypeDecl*/ ();
+/*
+ public void AddType(TypeDecl! td)
+ {
+ string! name = (!)td.Name;
+
+ if (name.StartsWith("bv") && name.Length > 2) {
+ bool isBv = true;
+ for (int i = 2; i < name.Length; ++i)
+ if (!char.IsDigit(name[i])) isBv = false;
+ if (isBv)
+ Error(td, "type name: {0} is registered for bitvectors", name);
+ }
+
+ if (types[name] != null)
+ {
+ Error(td, "more than one declaration of type name: {0}", name);
+ }
+ else
+ {
+ types.Add(name, td);
+ }
+ }
+*/
+ /// <summary>
+ /// Returns the declaration of the named type, or null if
+ /// no such type is declared.
+ /// </summary>
+ /// <param name="name"></param>
+ /// <returns></returns>
+ /* public TypeDecl LookUpType(string! name)
+ {
+ return (TypeDecl)types[name];
+ }
+ */
+ // ------------------------------ Type Binders ------------------------------
+/*
+ List<TypeBinderDecl!>! typeBinders = new List<TypeBinderDecl!>(5);
+
+ public void AddTypeBinder(TypeBinderDecl! td) {
+ for (int i = 0; i < typeBinders.Count; i++) {
+ if (typeBinders[i].Name == td.Name) {
+ Error(td, "more than one declaration of type binder name: {0}", td.Name);
+ return;
+ }
+ }
+ typeBinders.Add(td);
+ }
+
+ public int TypeBinderState {
+ get { return typeBinders.Count; }
+ set { typeBinders.RemoveRange(value, typeBinders.Count - value); }
+ }
+
+ /// <summary>
+ /// Returns the declaration of the named type binder, or null if
+ /// no such binder is declared.
+ /// </summary>
+ public TypeDecl LookUpTypeBinder(string! name)
+ {
+ for (int i = typeBinders.Count; 0 <= --i; ) {
+ TypeBinderDecl td = typeBinders[i];
+ if (td.Name == name) {
+ return td;
+ }
+ }
+ return null; // not present
+ }
+ */
+ // ------------------------------ Variables ------------------------------
+
+ class VarContextNode
+ {
+ public readonly Hashtable /*string->Variable*/! VarSymbols = new Hashtable /*string->Variable*/();
+ public /*maybe null*/ VarContextNode ParentContext;
+ public readonly bool Opaque;
+
+ public VarContextNode(/*maybe null*/ VarContextNode parentContext, bool opaque)
+ {
+ ParentContext = parentContext;
+ Opaque = opaque;
+ }
+ }
+
+ // symbolic constants, global variables, local variables, formals, expression-bound variables
+ VarContextNode! varContext = new VarContextNode(null, false);
+
+ /// <summary>
+ /// Adds a variable context.
+ /// </summary>
+ public void PushVarContext()
+ {
+ varContext = new VarContextNode(varContext, false);
+ }
+
+ /// <summary>
+ /// Adds an opaque variable context, that is, one that blocks all previously pushed contexts.
+ /// </summary>
+ public void PushOpaqueVarContext()
+ {
+ varContext = new VarContextNode(varContext, true);
+ }
+
+ /// <summary>
+ /// Requires there to be more than one variable context.
+ /// </summary>
+ public void PopVarContext()
+ {
+ assert varContext.ParentContext != null;
+ varContext = varContext.ParentContext;
+ }
+
+ public void AddVariable(Variable! var, bool global)
+ {
+ if (FindVariable((!)var.Name, !global) != null)
+ {
+ Error(var, "more than one declaration of variable name: {0}", var.Name);
+ }
+ else
+ {
+ varContext.VarSymbols.Add(var.Name, var);
+ }
+ }
+
+ /// <summary>
+ /// Returns the declaration of the named variable, or null if
+ /// no such variable is declared.
+ /// </summary>
+ /// <param name="name"></param>
+ /// <returns></returns>
+ public Variable LookUpVariable(string! name)
+ {
+ return FindVariable(name, false);
+ }
+
+ Variable FindVariable(string! name, bool ignoreTopLevelVars)
+ {
+ VarContextNode c = varContext;
+ bool lookOnlyForConstants = false;
+ do {
+ if (ignoreTopLevelVars && c.ParentContext == null) {
+ // this is the top level and we're asked to ignore the top level; hence, we're done
+ break;
+ }
+
+ Variable var = (Variable)c.VarSymbols[name];
+ if (var != null && (!lookOnlyForConstants || var is Constant)) {
+ return var;
+ }
+ // not at this level
+
+ if (c.Opaque) {
+ // from here on, only constants can be looked up
+ lookOnlyForConstants = true;
+ }
+ c = c.ParentContext;
+ } while (c != null);
+
+ // not present in the relevant levels
+ return null;
+ }
+
+ // ------------------------------ Functions/Procedures ------------------------------
+
+ // uninterpreted function symbols, procedures
+ Hashtable /*string->DeclWithFormals*/! funcdures = new Hashtable /*string->DeclWithFormals*/ ();
+
+ public void AddProcedure(DeclWithFormals! proc)
+ {
+ if (funcdures[(!)proc.Name] != null)
+ {
+ Error(proc, "more than one declaration of function/procedure name: {0}", proc.Name);
+ }
+ else
+ {
+ funcdures.Add(proc.Name, proc);
+ }
+ }
+
+ /// <summary>
+ /// Returns the declaration of the named function/procedure, or null if
+ /// no such function or procedure is declared.
+ /// </summary>
+ /// <param name="name"></param>
+ /// <returns></returns>
+ public DeclWithFormals LookUpProcedure(string! name)
+ {
+ return (DeclWithFormals)funcdures[name];
+ }
+
+ // ------------------------------ Blocks ------------------------------
+
+ // blocks
+ [Microsoft.Contracts.SpecPublic]
+ /*maybe null*/ Hashtable /*string->Block*/ blocks;
+
+ /// <summary>
+ /// Requires there not to be a procedure context. Creates one.
+ /// </summary>
+ public void StartProcedureContext()
+ {
+ System.Diagnostics.Debug.Assert(blocks == null);
+ blocks = new Hashtable /*string->Block*/ ();
+ }
+
+ /// <summary>
+ /// Requires there to be a procedure context. Removes it.
+ /// </summary>
+ public void EndProcedureContext()
+ {
+ System.Diagnostics.Debug.Assert(blocks != null);
+ blocks = null;
+ }
+
+ /// <summary>
+ /// Requires there to be a procedure context.
+ /// </summary>
+ /// <param name="block"></param>
+ public void AddBlock(Block! block)
+ requires this.blocks != null;
+ {
+ if (blocks[block.Label] != null)
+ {
+ Error(block, "more than one declaration of block name: {0}", block.Label);
+ }
+ else
+ {
+ blocks.Add(block.Label, block);
+ }
+ }
+
+ /// <summary>
+ /// Requires there to be a procedure context.
+ /// Returns the declaration of the named block, or null if
+ /// no such block is declared.
+ /// </summary>
+ /// <param name="name"></param>
+ /// <returns></returns>
+ public Block LookUpBlock(string! name)
+ requires this.blocks != null;
+ {
+ return (Block)blocks[name];
+ }
+
+ // ------------------------------ Flags ------------------------------
+
+ public enum State { StateLess, Single, Two }
+ State stateMode = State.Single;
+
+ /// <summary>
+ /// To increase our confidence in that the caller knows what it's doing, we only allow
+ /// the state mode to be changed in and out of the State.Single mode.
+ /// </summary>
+ public State StateMode {
+ get {
+ return stateMode;
+ }
+ set {
+ assert value != stateMode;
+ assert stateMode == State.Single || value == State.Single;
+ expose (this) {
+ stateMode = value;
+ }
+ }
+ }
+
+ bool triggerMode = false;
+
+ /// <summary>
+ /// Setting TriggerMode is allowed only if the setting has the effect of toggling the
+ /// boolean. That is, TriggerMode can be set to true only if it previously was false,
+ /// and TriggerMode can be set to false only if it previously was true.
+ /// </summary>
+ public bool TriggerMode
+ {
+ get
+ {
+ return triggerMode;
+ }
+ set
+ {
+ assert triggerMode != value;
+ expose (this) {
+ triggerMode = value;
+ }
+ }
+ }
+ }
+
+ public class TypecheckingContext : CheckingContext
+ {
+ public IdentifierExprSeq Frame; // used in checking the assignment targets of implementation bodies
+
+ public TypecheckingContext(IErrorSink errorSink)
+ {
+ base(errorSink);
+ }
+
+ public bool InFrame(Variable! v)
+ requires Frame != null;
+ {
+ return exists{IdentifierExpr! ie in Frame; ie.Decl == v};
+ }
+ }
+}
diff --git a/Source/Core/Scanner.ssc b/Source/Core/Scanner.ssc new file mode 100644 index 00000000..eb0c8b04 --- /dev/null +++ b/Source/Core/Scanner.ssc @@ -0,0 +1,720 @@ +//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.IO;
+using System.Collections;
+using System.Collections.Generic;
+using System.Text;
+using Microsoft.Contracts;
+
+
+namespace Microsoft.Boogie {
+
+ [Immutable]
+ public interface IToken {
+ int kind {get; set; } // token kind
+ string filename {get; set; } // token file
+ int pos {get; set; } // token position in the source text (starting at 0)
+ int col {get; set; } // token column (starting at 0)
+ int line {get; set; } // token line (starting at 1)
+ string/*!*/ val {get; set; } // token value
+
+ bool IsValid { get; }
+ }
+
+ [Immutable]
+ public class Token : IToken {
+ int _kind; // token kind
+ string _filename; // token file
+ int _pos; // token position in the source text (starting at 0)
+ int _col; // token column (starting at 0)
+ int _line; // token line (starting at 1)
+ string/*!*/ _val = "foo"; // token value
+
+ public static IToken! NoToken = new Token();
+
+ public Token();
+ public Token(int linenum, int colnum) {
+ this._line = linenum;
+ this._col = colnum;
+ base();
+ }
+
+ public int kind {
+ get { return this._kind; }
+ set { this._kind = value; }
+ }
+
+ public string filename{
+ get { return this._filename; }
+ set { this._filename = value; }
+ }
+
+ public int pos{
+ get { return this._pos; }
+ set { this._pos = value; }
+ }
+
+ public int col{
+ get { return this._col; }
+ set { this._col = value; }
+ }
+
+ public int line{
+ get { return this._line; }
+ set { this._line = value; }
+ }
+
+ public string/*!*/ val{
+ get { return this._val; }
+ set { this._val = value; }
+ }
+
+ public bool IsValid { get { return this._filename != null; } }
+ }
+
+}
+
+namespace BoogiePL {
+
+using Microsoft.Boogie;
+
+public class Buffer {
+ static string/*!*/ buf;
+ static int bufLen;
+ static int pos;
+ public const int eof = '\uffff';
+
+ public static void Fill(StreamReader! reader) {
+ List<string!> defines = new List<string!>();
+ Fill(reader, defines);
+ }
+
+ struct ReadState {
+ public bool hasSeenElse;
+ public bool mayStillIncludeAnotherAlternative;
+ public ReadState(bool hasSeenElse, bool mayStillIncludeAnotherAlternative) {
+ this.hasSeenElse = hasSeenElse;
+ this.mayStillIncludeAnotherAlternative = mayStillIncludeAnotherAlternative;
+ }
+ }
+
+ public static void Fill(StreamReader! reader, List<string!>! defines) {
+ StringBuilder sb = new StringBuilder();
+ List<ReadState>! readState = new List<ReadState>(); // readState.Count is the current nesting level of #if's
+ int ignoreCutoff = -1; // -1 means we're not ignoring; for 0<=n, n means we're ignoring because of something at nesting level n
+ while (true)
+ invariant -1 <= ignoreCutoff && ignoreCutoff < readState.Count;
+ {
+ string s = reader.ReadLine();
+ if (s == null) {
+ if (readState.Count != 0) {
+ sb.AppendLine("#MalformedInput: missing #endif");
+ }
+ break;
+ }
+ string t = s.Trim();
+ if (t.StartsWith("#if")) {
+ ReadState rs = new ReadState(false, false);
+ if (ignoreCutoff != -1) {
+ // we're already in a state of ignoring, so continue to ignore
+ } else if (IfdefConditionSaysToInclude(t.Substring(3).TrimStart(), defines)) {
+ // include this branch
+ } else {
+ ignoreCutoff = readState.Count; // start ignoring
+ rs.mayStillIncludeAnotherAlternative = true; // allow some later "elsif" or "else" branch to be included
+ }
+ readState.Add(rs);
+ sb.AppendLine(); // ignore the #if line
+
+ } else if (t.StartsWith("#elsif")) {
+ ReadState rs;
+ if (readState.Count == 0 || (rs = readState[readState.Count-1]).hasSeenElse) {
+ sb.AppendLine("#MalformedInput: misplaced #elsif"); // malformed input
+ break;
+ }
+ if (ignoreCutoff == -1) {
+ // we had included the previous branch
+ assert !rs.mayStillIncludeAnotherAlternative;
+ ignoreCutoff = readState.Count-1; // start ignoring
+ } else if (rs.mayStillIncludeAnotherAlternative && IfdefConditionSaysToInclude(t.Substring(6).TrimStart(), defines)) {
+ // include this branch, but no subsequent branch at this level
+ ignoreCutoff = -1;
+ rs.mayStillIncludeAnotherAlternative = false;
+ readState[readState.Count-1] = rs;
+ }
+ sb.AppendLine(); // ignore the #elsif line
+
+ } else if (t == "#else") {
+ ReadState rs;
+ if (readState.Count == 0 || (rs = readState[readState.Count-1]).hasSeenElse) {
+ sb.AppendLine("#MalformedInput: misplaced #else"); // malformed input
+ break;
+ }
+ rs.hasSeenElse = true;
+ if (ignoreCutoff == -1) {
+ // we had included the previous branch
+ assert !rs.mayStillIncludeAnotherAlternative;
+ ignoreCutoff = readState.Count-1; // start ignoring
+ } else if (rs.mayStillIncludeAnotherAlternative) {
+ // include this branch
+ ignoreCutoff = -1;
+ rs.mayStillIncludeAnotherAlternative = false;
+ }
+ readState[readState.Count-1] = rs;
+ sb.AppendLine(); // ignore the #else line
+
+ } else if (t == "#endif") {
+ if (readState.Count == 0) {
+ sb.AppendLine("#MalformedInput: misplaced #endif"); // malformed input
+ break;
+ }
+ readState.RemoveAt(readState.Count-1); // pop
+ if (ignoreCutoff == readState.Count) {
+ // we had ignored the branch that ends here; so, now we start including again
+ ignoreCutoff = -1;
+ }
+ sb.AppendLine(); // ignore the #endif line
+
+ } else if (ignoreCutoff == -1) {
+ sb.AppendLine(s); // included line
+
+ } else {
+ sb.AppendLine(); // ignore the line
+ }
+ }
+
+ Fill(sb.ToString());
+ }
+
+ // "arg" is assumed to be trimmed
+ private static bool IfdefConditionSaysToInclude(string! arg, List<string!>! defines) {
+ bool sense = true;
+ while (arg.StartsWith("!")) {
+ sense = !sense;
+ arg = arg.Substring(1).TrimStart();
+ }
+ return defines.Contains(arg) == sense;
+ }
+
+ public static void Fill(string! text) {
+ buf = text;
+ bufLen = buf.Length;
+ pos = 0;
+ }
+
+ public static int Read() {
+ if (pos < bufLen) {
+ return buf[pos++];
+ } else {
+ return eof;
+ }
+ }
+
+ public static string/*!*/ ReadToEOL() {
+ int x = buf.IndexOf('\n', pos);
+ if (x == -1) {
+ string s = buf.Substring(pos);
+ pos = buf.Length;
+ return s;
+ } else {
+ string s = buf.Substring(pos, x+1 - pos); // also include the '\n'
+ pos = x+1;
+ return s;
+ }
+ }
+
+ public static int Pos {
+ get { return pos; }
+ set {
+ if (value < 0) pos = 0; else if (value >= bufLen) pos = bufLen; else pos = value;
+ }
+ }
+}
+
+
+
+public class Scanner {
+ const char EOF = '\0';
+ const char CR = '\r';
+ const char LF = '\n';
+ [Microsoft.Contracts.Verify(false)]
+ static Scanner() {
+ start[0] = 57;
+ start[33] = 41;
+ start[34] = 6;
+ start[35] = 2;
+ start[36] = 2;
+ start[37] = 51;
+ start[38] = 34;
+ start[39] = 2;
+ start[40] = 11;
+ start[41] = 12;
+ start[42] = 24;
+ start[43] = 47;
+ start[44] = 14;
+ start[45] = 49;
+ start[46] = 2;
+ start[47] = 50;
+ start[48] = 9;
+ start[49] = 9;
+ start[50] = 9;
+ start[51] = 9;
+ start[52] = 9;
+ start[53] = 9;
+ start[54] = 9;
+ start[55] = 9;
+ start[56] = 9;
+ start[57] = 9;
+ start[58] = 13;
+ start[59] = 10;
+ start[60] = 17;
+ start[61] = 21;
+ start[62] = 18;
+ start[63] = 2;
+ start[65] = 2;
+ start[66] = 2;
+ start[67] = 2;
+ start[68] = 2;
+ start[69] = 2;
+ start[70] = 2;
+ start[71] = 2;
+ start[72] = 2;
+ start[73] = 2;
+ start[74] = 2;
+ start[75] = 2;
+ start[76] = 2;
+ start[77] = 2;
+ start[78] = 2;
+ start[79] = 2;
+ start[80] = 2;
+ start[81] = 2;
+ start[82] = 2;
+ start[83] = 2;
+ start[84] = 2;
+ start[85] = 2;
+ start[86] = 2;
+ start[87] = 2;
+ start[88] = 2;
+ start[89] = 2;
+ start[90] = 2;
+ start[91] = 15;
+ start[92] = 1;
+ start[93] = 16;
+ start[94] = 2;
+ start[95] = 2;
+ start[96] = 2;
+ start[97] = 2;
+ start[98] = 2;
+ start[99] = 2;
+ start[100] = 2;
+ start[101] = 2;
+ start[102] = 2;
+ start[103] = 2;
+ start[104] = 2;
+ start[105] = 2;
+ start[106] = 2;
+ start[107] = 2;
+ start[108] = 2;
+ start[109] = 2;
+ start[110] = 2;
+ start[111] = 2;
+ start[112] = 2;
+ start[113] = 2;
+ start[114] = 2;
+ start[115] = 2;
+ start[116] = 2;
+ start[117] = 2;
+ start[118] = 2;
+ start[119] = 2;
+ start[120] = 2;
+ start[121] = 2;
+ start[122] = 2;
+ start[123] = 19;
+ start[124] = 37;
+ start[125] = 20;
+ start[126] = 2;
+ start[172] = 52;
+ start[8226] = 56;
+ start[8656] = 33;
+ start[8658] = 32;
+ start[8660] = 29;
+ start[8704] = 53;
+ start[8707] = 54;
+ start[8743] = 36;
+ start[8744] = 39;
+ start[8800] = 44;
+ start[8804] = 45;
+ start[8805] = 46;
+ }
+ const int noSym = 85;
+ static short[] start = new short[16385];
+
+
+ static Token/*!*/ t; // current token
+ static char ch; // current input character
+ static int pos; // column number of current character
+ static int line; // line number of current character
+ static int lineStart; // start position of current line
+ static Queue! oldEols; // EOLs that appeared in a comment;
+ static BitArray/*!*/ ignore; // set of characters to be ignored by the scanner
+ static string Filename;
+
+ ///<summary>
+ ///Initializes the scanner. Note: first fill the Buffer.
+ ///</summary>
+ ///<param name="filename">File name used for error reporting</param>
+ public static void Init (string filename) {
+ Filename = filename;
+ pos = -1; line = 1; lineStart = 0;
+ oldEols = new Queue();
+ NextCh();
+ ignore = new BitArray(16384);
+ ignore[9] = true; ignore[10] = true; ignore[13] = true; ignore[32] = true;
+
+ }
+
+ private static void NextCh() {
+ if (oldEols.Count > 0) {
+ ch = (char) ((!)oldEols.Dequeue());
+ } else {
+ while (true) {
+ ch = (char)Buffer.Read(); pos++;
+ if (ch == Buffer.eof) {
+ ch = EOF;
+ } else if (ch == LF) {
+ line++;
+ lineStart = pos + 1;
+
+ } else if (ch == '#' && pos == lineStart) {
+ int prLine = line;
+ int prColumn = pos - lineStart; // which is 0
+
+ string hashLine = Buffer.ReadToEOL(); pos += hashLine.Length;
+ line++;
+ lineStart = pos + 1;
+
+ hashLine = hashLine.TrimEnd(null);
+ if (hashLine.StartsWith("line ") || hashLine == "line") {
+ // parse #line pragma: #line num [filename]
+ string h = hashLine.Substring(4).TrimStart(null);
+ int x = h.IndexOf(' ');
+ if (x == -1) {
+ x = h.Length; // this will be convenient below when we look for a filename
+ }
+ try {
+ int li = int.Parse(h.Substring(0, x));
+
+ h = h.Substring(x).Trim();
+
+ // act on #line
+ line = li;
+ if (h.Length != 0) {
+ // a filename was specified
+ Filename = h;
+ }
+ continue; // successfully parsed and acted on the #line pragma
+
+ } catch (FormatException) {
+ // just fall down through to produce an error message
+ }
+ Errors.SemErr(Filename, prLine, prColumn, "Malformed (#line num [filename]) pragma: #" + hashLine);
+ continue;
+ }
+
+ Errors.SemErr(Filename, prLine, prColumn, "Unrecognized pragma: #" + hashLine);
+ continue;
+ }
+ return;
+ }
+ }
+ }
+
+
+ static bool Comment0() {
+ int level = 1, line0 = line, lineStart0 = lineStart;
+ NextCh();
+ if (ch == '/') {
+ NextCh();
+ for(;;) {
+ if (ch == 10) {
+ level--;
+ if (level == 0) {
+ while(line0 < line) {oldEols.Enqueue('\r'); oldEols.Enqueue('\n'); line0++;}
+ NextCh(); return true;
+ }
+ NextCh();
+ } else if (ch == EOF) return false;
+ else NextCh();
+ }
+ } else {
+ if (ch==CR) {line--; lineStart = lineStart0;}
+ pos = pos - 2; Buffer.Pos = pos+1; NextCh();
+ }
+ return false;
+ }
+
+ static bool Comment1() {
+ int level = 1, line0 = line, lineStart0 = lineStart;
+ NextCh();
+ if (ch == '*') {
+ NextCh();
+ for(;;) {
+ if (ch == '*') {
+ NextCh();
+ if (ch == '/') {
+ level--;
+ if (level == 0) {
+ while(line0 < line) {oldEols.Enqueue('\r'); oldEols.Enqueue('\n'); line0++;}
+ NextCh(); return true;
+ }
+ NextCh();
+ }
+ } else if (ch == '/') {
+ NextCh();
+ if (ch == '*') {
+ level++; NextCh();
+ }
+ } else if (ch == EOF) return false;
+ else NextCh();
+ }
+ } else {
+ if (ch==CR) {line--; lineStart = lineStart0;}
+ pos = pos - 2; Buffer.Pos = pos+1; NextCh();
+ }
+ return false;
+ }
+
+
+ static void CheckLiteral() {
+ switch (t.val) {
+ case "var": t.kind = 6; break;
+ case "where": t.kind = 12; break;
+ case "int": t.kind = 13; break;
+ case "bool": t.kind = 14; break;
+ case "const": t.kind = 19; break;
+ case "unique": t.kind = 20; break;
+ case "extends": t.kind = 21; break;
+ case "complete": t.kind = 22; break;
+ case "function": t.kind = 23; break;
+ case "returns": t.kind = 24; break;
+ case "axiom": t.kind = 27; break;
+ case "type": t.kind = 28; break;
+ case "procedure": t.kind = 30; break;
+ case "implementation": t.kind = 31; break;
+ case "modifies": t.kind = 32; break;
+ case "free": t.kind = 33; break;
+ case "requires": t.kind = 34; break;
+ case "ensures": t.kind = 35; break;
+ case "goto": t.kind = 38; break;
+ case "return": t.kind = 39; break;
+ case "if": t.kind = 40; break;
+ case "else": t.kind = 41; break;
+ case "while": t.kind = 42; break;
+ case "invariant": t.kind = 43; break;
+ case "break": t.kind = 45; break;
+ case "assert": t.kind = 46; break;
+ case "assume": t.kind = 47; break;
+ case "havoc": t.kind = 48; break;
+ case "call": t.kind = 50; break;
+ case "forall": t.kind = 51; break;
+ case "false": t.kind = 77; break;
+ case "true": t.kind = 78; break;
+ case "old": t.kind = 79; break;
+ case "exists": t.kind = 81; break;
+ default: break;
+
+ }
+ }
+
+ public static Token/*!*/ Scan() {
+ while (ignore[ch]) { NextCh(); }
+ if (ch == '/' && Comment0() || ch == '/' && Comment1() ) return Scan();
+ t = new Token();
+ t.pos = pos; t.col = pos - lineStart + 1; t.line = line; t.filename = Filename;
+ int state = (/*^ (!) ^*/ start)[ch];
+ StringBuilder buf = new StringBuilder(16);
+ buf.Append(ch); NextCh();
+
+ switch (state) {
+ case 0: {t.kind = noSym; goto done;} // NextCh already done
+ case 1:
+ if ((ch >= '#' && ch <= '$' || ch == 39 || ch == '.' || ch == '?' || ch >= 'A' && ch <= 'Z' || ch >= '^' && ch <= 'z' || ch == '~')) {buf.Append(ch); NextCh(); goto case 2;}
+ else {t.kind = noSym; goto done;}
+ case 2:
+ if ((ch >= '#' && ch <= '$' || ch == 39 || ch == '.' || ch >= '0' && ch <= '9' || ch == '?' || ch >= 'A' && ch <= 'Z' || ch >= '^' && ch <= 'z' || ch == '~')) {buf.Append(ch); NextCh(); goto case 2;}
+ else {t.kind = 1; t.val = buf.ToString(); CheckLiteral(); return t;}
+ case 3:
+ if (ch == 'v') {buf.Append(ch); NextCh(); goto case 4;}
+ else {t.kind = noSym; goto done;}
+ case 4:
+ if ((ch >= '0' && ch <= '9')) {buf.Append(ch); NextCh(); goto case 5;}
+ else {t.kind = noSym; goto done;}
+ case 5:
+ if ((ch >= '0' && ch <= '9')) {buf.Append(ch); NextCh(); goto case 5;}
+ else {t.kind = 2; goto done;}
+ case 6:
+ if ((ch == '"')) {buf.Append(ch); NextCh(); goto case 7;}
+ else if ((ch >= ' ' && ch <= '!' || ch >= '#' && ch <= '~')) {buf.Append(ch); NextCh(); goto case 6;}
+ else {t.kind = noSym; goto done;}
+ case 7:
+ {t.kind = 4; goto done;}
+ case 8:
+ if ((ch >= '0' && ch <= '9')) {buf.Append(ch); NextCh(); goto case 8;}
+ else {t.kind = 5; goto done;}
+ case 9:
+ if ((ch >= '0' && ch <= '9')) {buf.Append(ch); NextCh(); goto case 9;}
+ else if (ch == 'b') {buf.Append(ch); NextCh(); goto case 3;}
+ else if (ch == '.') {buf.Append(ch); NextCh(); goto case 8;}
+ else {t.kind = 3; goto done;}
+ case 10:
+ {t.kind = 7; goto done;}
+ case 11:
+ {t.kind = 8; goto done;}
+ case 12:
+ {t.kind = 9; goto done;}
+ case 13:
+ if (ch == '=') {buf.Append(ch); NextCh(); goto case 25;}
+ else if (ch == ':') {buf.Append(ch); NextCh(); goto case 55;}
+ else {t.kind = 10; goto done;}
+ case 14:
+ {t.kind = 11; goto done;}
+ case 15:
+ {t.kind = 15; goto done;}
+ case 16:
+ {t.kind = 16; goto done;}
+ case 17:
+ if (ch == '=') {buf.Append(ch); NextCh(); goto case 26;}
+ else if (ch == ':') {buf.Append(ch); NextCh(); goto case 43;}
+ else {t.kind = 17; goto done;}
+ case 18:
+ if (ch == '=') {buf.Append(ch); NextCh(); goto case 40;}
+ else {t.kind = 18; goto done;}
+ case 19:
+ if (ch == '{') {buf.Append(ch); NextCh(); goto case 22;}
+ else {t.kind = 25; goto done;}
+ case 20:
+ if (ch == '}') {buf.Append(ch); NextCh(); goto case 23;}
+ else {t.kind = 26; goto done;}
+ case 21:
+ if (ch == '=') {buf.Append(ch); NextCh(); goto case 30;}
+ else {t.kind = 29; goto done;}
+ case 22:
+ {t.kind = 36; goto done;}
+ case 23:
+ {t.kind = 37; goto done;}
+ case 24:
+ {t.kind = 44; goto done;}
+ case 25:
+ {t.kind = 49; goto done;}
+ case 26:
+ if (ch == '=') {buf.Append(ch); NextCh(); goto case 27;}
+ else {t.kind = 63; goto done;}
+ case 27:
+ if (ch == '>') {buf.Append(ch); NextCh(); goto case 28;}
+ else {t.kind = 56; goto done;}
+ case 28:
+ {t.kind = 52; goto done;}
+ case 29:
+ {t.kind = 53; goto done;}
+ case 30:
+ if (ch == '>') {buf.Append(ch); NextCh(); goto case 31;}
+ else {t.kind = 62; goto done;}
+ case 31:
+ {t.kind = 54; goto done;}
+ case 32:
+ {t.kind = 55; goto done;}
+ case 33:
+ {t.kind = 57; goto done;}
+ case 34:
+ if (ch == '&') {buf.Append(ch); NextCh(); goto case 35;}
+ else {t.kind = noSym; goto done;}
+ case 35:
+ {t.kind = 58; goto done;}
+ case 36:
+ {t.kind = 59; goto done;}
+ case 37:
+ if (ch == '|') {buf.Append(ch); NextCh(); goto case 38;}
+ else {t.kind = noSym; goto done;}
+ case 38:
+ {t.kind = 60; goto done;}
+ case 39:
+ {t.kind = 61; goto done;}
+ case 40:
+ {t.kind = 64; goto done;}
+ case 41:
+ if (ch == '=') {buf.Append(ch); NextCh(); goto case 42;}
+ else {t.kind = 75; goto done;}
+ case 42:
+ {t.kind = 65; goto done;}
+ case 43:
+ {t.kind = 66; goto done;}
+ case 44:
+ {t.kind = 67; goto done;}
+ case 45:
+ {t.kind = 68; goto done;}
+ case 46:
+ {t.kind = 69; goto done;}
+ case 47:
+ if (ch == '+') {buf.Append(ch); NextCh(); goto case 48;}
+ else {t.kind = 71; goto done;}
+ case 48:
+ {t.kind = 70; goto done;}
+ case 49:
+ {t.kind = 72; goto done;}
+ case 50:
+ {t.kind = 73; goto done;}
+ case 51:
+ {t.kind = 74; goto done;}
+ case 52:
+ {t.kind = 76; goto done;}
+ case 53:
+ {t.kind = 80; goto done;}
+ case 54:
+ {t.kind = 82; goto done;}
+ case 55:
+ {t.kind = 83; goto done;}
+ case 56:
+ {t.kind = 84; goto done;}
+ case 57: {t.kind = 0; goto done;}
+ }
+ done:
+ t.val = buf.ToString();
+ return t;
+ }
+
+} // end Scanner
+
+
+public delegate void ErrorProc(int n, string filename, int line, int col);
+
+public class Errors {
+ public static int count = 0; // number of errors detected
+ public static ErrorProc/*!*/ SynErr; // syntactic errors
+
+ public static void SemErr(string filename, int line, int col, string! msg) { // semantic errors
+ ConsoleColor color = Console.ForegroundColor;
+ Console.ForegroundColor = ConsoleColor.Red;
+ Console.WriteLine("{0}({1},{2}): Error: {3}", filename, line, col, msg);
+ Console.ForegroundColor = color;
+ count++;
+ }
+
+ public static void SemErr(IToken! tok, string! msg) { // semantic errors
+ SemErr(tok.filename, tok.line, tok.col, msg);
+ }
+
+ public static void Exception (string s) {
+ ConsoleColor color = Console.ForegroundColor;
+ Console.ForegroundColor = ConsoleColor.Red;
+ Console.WriteLine(s);
+ Console.ForegroundColor = color;
+ System.Environment.Exit(0);
+ }
+
+} // Errors
+
+} // end namespace
diff --git a/Source/Core/StandardVisitor.ssc b/Source/Core/StandardVisitor.ssc new file mode 100644 index 00000000..a6068426 --- /dev/null +++ b/Source/Core/StandardVisitor.ssc @@ -0,0 +1,503 @@ +//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+//---------------------------------------------------------------------------------------------
+// BoogiePL - StandardVisitor.cs
+//---------------------------------------------------------------------------------------------
+
+using System.Collections.Generic;
+
+namespace Microsoft.Boogie
+{
+ /// <summary>
+ /// Base for all classes that process the Absy using the visitor pattern.
+ /// </summary>
+ public abstract class Visitor
+ {
+ /// <summary>
+ /// Switches on node.NodeType to call a visitor method that has been specialized for node.
+ /// </summary>
+ /// <param name="a">The Absy node to be visited.</param>
+ /// <returns> Returns null if node is null. Otherwise returns an updated node (possibly a different object).</returns>
+ public abstract Absy! Visit (Absy! node);
+
+ /// <summary>
+ /// Transfers the state from one visitor to another. This enables separate visitor instances to cooperative process a single IR.
+ /// </summary>
+ public virtual void TransferStateTo(Visitor targetVisitor)
+ {
+ }
+
+ public virtual ExprSeq! VisitExprSeq(ExprSeq! list)
+ {
+ for( int i = 0, n = list.Length; i < n; i++)
+ list[i] = (Expr)this.Visit( (!) list[i]);
+ return list;
+ }
+ }
+
+ /// <summary>
+ /// Walks an IR, mutuating it into a new form
+ /// </summary>
+ public abstract class StandardVisitor: Visitor
+ {
+ public Visitor callingVisitor;
+
+ public StandardVisitor()
+ {
+ }
+ public StandardVisitor(Visitor callingVisitor)
+ {
+ this.callingVisitor = callingVisitor;
+ }
+ public override Absy! Visit (Absy! node)
+ {
+ return node.StdDispatch(this);
+ }
+ public virtual AIVariableExpr! VisitAIVariableExpr(AIVariableExpr! node)
+ {
+ return node;
+ }
+ public virtual Cmd! VisitAssertCmd(AssertCmd! node)
+ {
+ node.Expr = this.VisitExpr(node.Expr);
+ return node;
+ }
+ public virtual Cmd! VisitAssignCmd(AssignCmd! node)
+ {
+ for (int i = 0; i < node.Lhss.Count; ++i) {
+ node.Lhss[i] = (AssignLhs!)this.Visit(node.Lhss[i]);
+ node.Rhss[i] = (Expr!)this.Visit(node.Rhss[i]);
+ }
+ return node;
+ }
+ public virtual Cmd! VisitAssumeCmd(AssumeCmd! node)
+ {
+ node.Expr = this.VisitExpr(node.Expr);
+ return node;
+ }
+ public virtual AtomicRE! VisitAtomicRE(AtomicRE! node)
+ {
+ node.b = this.VisitBlock(node.b);
+ return node;
+ }
+ public virtual Axiom! VisitAxiom(Axiom! node)
+ {
+ node.Expr = this.VisitExpr(node.Expr);
+ return node;
+ }
+ public virtual Type! VisitBasicType(BasicType! node)
+ {
+ return this.VisitType(node);
+ }
+ public virtual BvConcatExpr! VisitBvConcatExpr(BvConcatExpr! node)
+ {
+ node.E0 = this.VisitExpr(node.E0);
+ node.E1 = this.VisitExpr(node.E1);
+ return node;
+ }
+ public virtual Type! VisitBvType(BvType! node)
+ {
+ return this.VisitType(node);
+ }
+ public virtual Type! VisitBvTypeProxy(BvTypeProxy! node)
+ {
+ // if the type proxy is instantiated with some more
+ // specific type, we visit the instantiation
+ if (node.ProxyFor != null)
+ return (Type)this.Visit(node.ProxyFor);
+ return this.VisitType(node);
+ }
+ public virtual Block! VisitBlock(Block! node)
+ {
+ node.Cmds = this.VisitCmdSeq(node.Cmds);
+ node.TransferCmd = this.VisitTransferCmd((!)node.TransferCmd);
+ return node;
+ }
+ public virtual Expr! VisitBlockExpr(BlockExpr! node)
+ {
+ node.LocVars = this.VisitVariableSeq(node.LocVars);
+ node.Blocks = this.VisitBlockSeq(node.Blocks);
+ return node;
+ }
+ public virtual BlockSeq! VisitBlockSeq(BlockSeq! blockSeq)
+ {
+ for (int i = 0, n = blockSeq.Length; i < n; i++)
+ blockSeq[i] = this.VisitBlock( (!)blockSeq[i]);
+ return blockSeq;
+ }
+ public virtual List<Block!>! VisitBlockList(List<Block!>! blocks)
+ {
+ for (int i = 0, n = blocks.Count; i < n; i++) {
+ blocks[i] = this.VisitBlock(blocks[i]);
+ }
+ return blocks;
+ }
+ public virtual BoundVariable! VisitBoundVariable(BoundVariable! node)
+ {
+ node = (BoundVariable) this.VisitVariable(node);
+ return node;
+ }
+ public virtual Cmd! VisitCallCmd(CallCmd! node)
+ {
+ for (int i = 0; i < node.Ins.Count; ++i)
+ if (node.Ins[i] != null)
+ node.Ins[i] = this.VisitExpr((!)node.Ins[i]);
+ for (int i = 0; i < node.Outs.Count; ++i)
+ if (node.Outs[i] != null)
+ node.Outs[i] = (IdentifierExpr)this.VisitIdentifierExpr((!)node.Outs[i]);
+ node.Proc = this.VisitProcedure((!)node.Proc);
+ return node;
+ }
+ public virtual Cmd! VisitCallForallCmd(CallForallCmd! node)
+ {
+ List<Expr> elist = new List<Expr>(node.Ins.Count);
+ foreach (Expr arg in node.Ins) {
+ if (arg == null) {
+ elist.Add(null);
+ } else {
+ elist.Add(this.VisitExpr(arg));
+ }
+ }
+ node.Ins = elist;
+ node.Proc = this.VisitProcedure((!)node.Proc);
+ return node;
+ }
+ public virtual CmdSeq! VisitCmdSeq(CmdSeq! cmdSeq)
+ {
+ for (int i = 0, n = cmdSeq.Length; i < n; i++)
+ cmdSeq[i] = (Cmd) this.Visit( (!)cmdSeq[i]); // call general Visit so subtypes of Cmd get visited by their particular visitor
+ return cmdSeq;
+ }
+ public virtual Choice! VisitChoice(Choice! node)
+ {
+ node.rs = this.VisitRESeq(node.rs);
+ return node;
+ }
+ public virtual Cmd! VisitCommentCmd(CommentCmd! node)
+ {
+ return node;
+ }
+ public virtual Constant! VisitConstant(Constant! node)
+ {
+ return node;
+ }
+ public virtual CtorType! VisitCtorType(CtorType! node)
+ {
+ for (int i = 0; i < node.Arguments.Length; ++i)
+ node.Arguments[i] = (Type!)this.Visit(node.Arguments[i]);
+ return node;
+ }
+ public virtual Declaration! VisitDeclaration(Declaration! node)
+ {
+ return node;
+ }
+ public virtual List<Declaration!>! VisitDeclarationList(List<Declaration!>! declarationList)
+ {
+ for (int i = 0, n = declarationList.Count; i < n; i++)
+ declarationList[i] = this.VisitDeclaration(declarationList[i]);
+ return declarationList;
+ }
+ public virtual DeclWithFormals! VisitDeclWithFormals(DeclWithFormals! node)
+ {
+ node.InParams = this.VisitVariableSeq(node.InParams);
+ node.OutParams = this.VisitVariableSeq(node.OutParams);
+ return node;
+ }
+ public virtual ExistsExpr! VisitExistsExpr(ExistsExpr! node)
+ {
+ node = (ExistsExpr) this.VisitQuantifierExpr(node);
+ return node;
+ }
+ public virtual ExtractExpr! VisitExtractExpr(ExtractExpr! node)
+ {
+ node.Bitvector = this.VisitExpr(node.Bitvector);
+ return node;
+ }
+ public virtual Expr! VisitExpr(Expr! node)
+ {
+ Expr e = (Expr) this.Visit(node);
+ return e;
+ }
+ public override ExprSeq! VisitExprSeq(ExprSeq! exprSeq)
+ {
+ for (int i = 0, n = exprSeq.Length; i < n; i++)
+ exprSeq[i] = this.VisitExpr( (!)exprSeq[i]);
+ return exprSeq;
+ }
+ public virtual Requires! VisitRequires(Requires! @requires)
+ {
+ @requires.Condition = this.VisitExpr(@requires.Condition);
+ return @requires;
+ }
+ public virtual RequiresSeq! VisitRequiresSeq(RequiresSeq! requiresSeq)
+ {
+ for (int i = 0, n = requiresSeq.Length; i < n; i++)
+ requiresSeq[i] = this.VisitRequires(requiresSeq[i]);
+ return requiresSeq;
+ }
+ public virtual Ensures! VisitEnsures(Ensures! @ensures)
+ {
+ @ensures.Condition = this.VisitExpr(@ensures.Condition);
+ return @ensures;
+ }
+ public virtual EnsuresSeq! VisitEnsuresSeq(EnsuresSeq! ensuresSeq)
+ {
+ for (int i = 0, n = ensuresSeq.Length; i < n; i++)
+ ensuresSeq[i] = this.VisitEnsures(ensuresSeq[i]);
+ return ensuresSeq;
+ }
+ public virtual ForallExpr! VisitForallExpr(ForallExpr! node)
+ {
+ node = (ForallExpr) this.VisitQuantifierExpr(node);
+ return node;
+ }
+ public virtual Formal! VisitFormal(Formal! node)
+ {
+ return node;
+ }
+ public virtual Function! VisitFunction(Function! node)
+ {
+ node = (Function) this.VisitDeclWithFormals(node);
+ if (node.Body != null)
+ node.Body = this.VisitExpr(node.Body);
+ return node;
+ }
+ public virtual GlobalVariable! VisitGlobalVariable(GlobalVariable! node)
+ {
+ node = (GlobalVariable) this.VisitVariable(node);
+ return node;
+ }
+ public virtual GotoCmd! VisitGotoCmd(GotoCmd! node)
+ {
+ node.labelTargets = this.VisitBlockSeq((!)node.labelTargets);
+ return node;
+ }
+ public virtual Cmd! VisitHavocCmd(HavocCmd! node)
+ {
+ node.Vars = this.VisitIdentifierExprSeq(node.Vars);
+ return node;
+ }
+ public virtual Expr! VisitIdentifierExpr(IdentifierExpr! node)
+ {
+ if (node.Decl != null)
+ node.Decl = this.VisitVariable(node.Decl);
+ return node;
+ }
+ public virtual IdentifierExprSeq! VisitIdentifierExprSeq(IdentifierExprSeq! identifierExprSeq)
+ {
+ for (int i = 0, n = identifierExprSeq.Length; i < n; i++)
+ identifierExprSeq[i] = (IdentifierExpr) this.VisitIdentifierExpr( (!)identifierExprSeq[i]);
+ return identifierExprSeq;
+ }
+ public virtual Implementation! VisitImplementation(Implementation! node)
+ {
+ node.LocVars = this.VisitVariableSeq(node.LocVars);
+ node.Blocks = this.VisitBlockList(node.Blocks);
+ node.Proc = this.VisitProcedure((!)node.Proc);
+ node = (Implementation) this.VisitDeclWithFormals(node); // do this first or last?
+ return node;
+ }
+ public virtual LiteralExpr! VisitLiteralExpr(LiteralExpr! node)
+ {
+ return node;
+ }
+
+ public virtual LocalVariable! VisitLocalVariable(LocalVariable! node)
+ {
+ return node;
+ }
+
+ /// <summary>
+ /// Just invoke VisitNAryExpr
+ /// </summary>
+ public virtual Expr! VisitLoopPredicate(LoopPredicate! node)
+ {
+ return this.VisitNAryExpr(node);
+ }
+
+ public virtual AssignLhs! VisitMapAssignLhs(MapAssignLhs! node)
+ {
+ node.Map = (AssignLhs!)this.Visit(node.Map);
+ for (int i = 0; i < node.Indexes.Count; ++i)
+ node.Indexes[i] = (Expr!)this.Visit(node.Indexes[i]);
+ return node;
+ }
+ public virtual MapType! VisitMapType(MapType! node)
+ {
+ // not doing anything about the bound variables ... maybe
+ // these should be visited as well ...
+ //
+ // NOTE: when overriding this method, you have to make sure that
+ // the bound variables of the map type are updated correctly
+ for (int i = 0; i < node.Arguments.Length; ++i)
+ node.Arguments[i] = (Type!)this.Visit(node.Arguments[i]);
+ node.Result = (Type!)this.Visit(node.Result);
+ return node;
+ }
+ public virtual Type! VisitMapTypeProxy(MapTypeProxy! node)
+ {
+ // if the type proxy is instantiated with some more
+ // specific type, we visit the instantiation
+ if (node.ProxyFor != null)
+ return (Type)this.Visit(node.ProxyFor);
+ return this.VisitType(node);
+ }
+
+ public virtual Expr! VisitNAryExpr(NAryExpr! node)
+ {
+ node.Args = this.VisitExprSeq(node.Args);
+// if (node.Type != null) { node.Type = this.VisitType(node.Type); } // BUGBUG? Do this even though it
+ return node;
+ }
+ public virtual Expr! VisitOldExpr(OldExpr! node)
+ {
+ node.Expr = this.VisitExpr(node.Expr);
+ return node;
+ }
+ public virtual Procedure! VisitProcedure(Procedure! node)
+ {
+ node.Ensures = this.VisitEnsuresSeq(node.Ensures);
+ node.InParams = this.VisitVariableSeq(node.InParams);
+ node.Modifies = this.VisitIdentifierExprSeq(node.Modifies);
+ node.OutParams = this.VisitVariableSeq(node.OutParams);
+ node.Requires = this.VisitRequiresSeq(node.Requires);
+ return node;
+ }
+ public virtual Program! VisitProgram(Program! node)
+ {
+ node.TopLevelDeclarations = this.VisitDeclarationList(node.TopLevelDeclarations);
+ return node;
+ }
+ public virtual QuantifierExpr! VisitQuantifierExpr(QuantifierExpr! node)
+ {
+ if (node.Triggers != null) {
+ node.Triggers = this.VisitTrigger(node.Triggers);
+ }
+ node.Body = this.VisitExpr(node.Body);
+ node.Dummies = this.VisitVariableSeq(node.Dummies);
+ //node.Type = this.VisitType(node.Type);
+ return node;
+ }
+ public virtual Cmd! VisitRE(RE! node)
+ {
+ return (Cmd) this.Visit(node); // Call general visit so subtypes get visited by their particular visitor
+ }
+ public virtual RESeq! VisitRESeq(RESeq! reSeq)
+ {
+ for (int i = 0, n = reSeq.Length; i < n; i++)
+ reSeq[i] = (RE) this.VisitRE( (!)reSeq[i]);
+ return reSeq;
+ }
+ public virtual ReturnCmd! VisitReturnCmd(ReturnCmd! node)
+ {
+ return (ReturnCmd) this.VisitTransferCmd(node);
+ }
+ public virtual ReturnExprCmd! VisitReturnExprCmd(ReturnExprCmd! node)
+ {
+ node.Expr = this.VisitExpr(node.Expr);
+ return node;
+ }
+ public virtual Sequential! VisitSequential(Sequential! node)
+ {
+ node.first = (RE) this.VisitRE(node.first);
+ node.second = (RE) this.VisitRE(node.second);
+ return node;
+ }
+ public virtual AssignLhs! VisitSimpleAssignLhs(SimpleAssignLhs! node)
+ {
+ node.AssignedVariable =
+ (IdentifierExpr) this.VisitIdentifierExpr(node.AssignedVariable);
+ return node;
+ }
+ public virtual Cmd! VisitStateCmd(StateCmd! node)
+ {
+ node.Locals = this.VisitVariableSeq(node.Locals);
+ node.Cmds = this.VisitCmdSeq(node.Cmds);
+ return node;
+ }
+ public virtual TransferCmd! VisitTransferCmd(TransferCmd! node)
+ {
+ return node;
+ }
+ public virtual Trigger! VisitTrigger(Trigger! node)
+ {
+ Trigger origNext = node.Next;
+ if (origNext != null) {
+ Trigger newNext = this.VisitTrigger(origNext);
+ if (newNext != origNext) {
+ node = new Trigger(node.tok, node.Pos, node.Tr); // note: this creates sharing between the old and new Tr sequence
+ node.Next = newNext;
+ }
+ }
+ node.Tr = this.VisitExprSeq(node.Tr);
+ return node;
+ }
+ // called by default for all nullary type constructors and type variables
+ public virtual Type! VisitType(Type! node)
+ {
+ return node;
+ }
+ public virtual TypedIdent! VisitTypedIdent(TypedIdent! node)
+ {
+ node.Type = (Type)this.Visit(node.Type);
+ return node;
+ }
+ public virtual Declaration! VisitTypeCtorDecl(TypeCtorDecl! node)
+ {
+ return this.VisitDeclaration(node);
+ }
+ public virtual Type! VisitTypeSynonymAnnotation(TypeSynonymAnnotation! node)
+ {
+ node.ExpandedType = (Type!)this.Visit(node.ExpandedType);
+ for (int i = 0; i < node.Arguments.Length; ++i)
+ node.Arguments[i] = (Type!)this.Visit(node.Arguments[i]);
+ return node;
+ }
+ public virtual Declaration! VisitTypeSynonymDecl(TypeSynonymDecl! node)
+ {
+ return this.VisitDeclaration(node);
+ }
+ public virtual Type! VisitTypeVariable(TypeVariable! node)
+ {
+ return this.VisitType(node);
+ }
+ public virtual Type! VisitTypeProxy(TypeProxy! node)
+ {
+ // if the type proxy is instantiated with some more
+ // specific type, we visit the instantiation
+ if (node.ProxyFor != null)
+ return (Type!)this.Visit(node.ProxyFor);
+ return this.VisitType(node);
+ }
+ public virtual Type! VisitUnresolvedTypeIdentifier(UnresolvedTypeIdentifier! node)
+ {
+ return this.VisitType(node);
+ }
+ public virtual Variable! VisitVariable(Variable! node)
+ {
+ node.TypedIdent = this.VisitTypedIdent(node.TypedIdent);
+ return node;
+ }
+ public virtual VariableSeq! VisitVariableSeq(VariableSeq! variableSeq)
+ {
+ for (int i = 0, n = variableSeq.Length; i < n; i++)
+ variableSeq[i] = this.VisitVariable( (!)variableSeq[i]);
+ return variableSeq;
+ }
+ public virtual Cmd! VisitAssertEnsuresCmd(AssertEnsuresCmd! node)
+ {
+ node.Ensures = this.VisitEnsures(node.Ensures);
+ node.Expr = this.VisitExpr(node.Expr);
+ return node;
+ }
+ public virtual Cmd! VisitAssertRequiresCmd(AssertRequiresCmd! node)
+ {
+ node.Requires = this.VisitRequires(node.Requires);
+ node.Expr = this.VisitExpr(node.Expr);
+ return node;
+ }
+ }
+}
diff --git a/Source/Core/TypeAmbiguitySeeker.ssc b/Source/Core/TypeAmbiguitySeeker.ssc new file mode 100644 index 00000000..762b9c04 --- /dev/null +++ b/Source/Core/TypeAmbiguitySeeker.ssc @@ -0,0 +1,95 @@ +//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+
+// Visitor to search for types proxies that could not completely be
+// determined by type inference. If this happens, a warning is
+// generated and the proxies are instantiated in a more or less arbitrary
+// fashion.
+
+namespace Microsoft.Boogie
+{
+
+ public class TypeAmbiguitySeeker : StandardVisitor {
+
+ private readonly InTypeSeeker! inTypeSeeker = new InTypeSeeker ();
+ private readonly TypecheckingContext! TC;
+
+ public TypeAmbiguitySeeker(TypecheckingContext! tc) {
+ TC = tc;
+ }
+
+ private void CheckTypeParams(Absy! node, TypeParamInstantiation! insts) {
+ foreach (TypeVariable! var in insts.FormalTypeParams) {
+ Type! inst = insts[var];
+ inTypeSeeker.FoundAmbiguity = false;
+ inTypeSeeker.Visit(inst);
+ if (inTypeSeeker.FoundAmbiguity)
+ TC.Warning(node,
+ "type parameter {0} is ambiguous, instantiating to {1}",
+ var, inst);
+ }
+ }
+
+ public override Expr! VisitNAryExpr(NAryExpr! node)
+ {
+ CheckTypeParams(node, (!)node.TypeParameters);
+ return base.VisitNAryExpr(node);
+ }
+
+ public override AssignLhs! VisitMapAssignLhs(MapAssignLhs! node) {
+ CheckTypeParams(node, (!)node.TypeParameters);
+ return base.VisitMapAssignLhs(node);
+ }
+ }
+
+ internal class InTypeSeeker : StandardVisitor {
+
+ internal bool FoundAmbiguity = false;
+
+ // called when an uninstantiated proxy was found
+ private Type! Instantiate(Type! node, Type! inst) {
+ FoundAmbiguity = true;
+ bool success = node.Unify(inst);
+ assert success;
+ return node;
+ }
+
+ public override Type! VisitTypeProxy(TypeProxy! node) {
+ if (node.ProxyFor != null)
+ return base.VisitTypeProxy(node);
+
+ return Instantiate(node, Type.Int);
+ }
+
+ public override Type! VisitMapTypeProxy(MapTypeProxy! node) {
+ if (node.ProxyFor != null)
+ return base.VisitMapTypeProxy(node);
+
+ TypeVariableSeq! typeParams = new TypeVariableSeq ();
+ TypeSeq! arguments = new TypeSeq ();
+ for (int i = 0; i < node.Arity; ++i) {
+ TypeVariable! param = new TypeVariable (Token.NoToken, "arg" + i);
+ typeParams.Add(param);
+ arguments.Add(param);
+ }
+ TypeVariable! result = new TypeVariable (Token.NoToken, "res");
+ typeParams.Add(result);
+
+ Type! instantiation = new MapType (Token.NoToken, typeParams, arguments, result);
+
+ return Instantiate(node, instantiation);
+ }
+
+ public override Type! VisitBvTypeProxy(BvTypeProxy! node) {
+ if (node.ProxyFor != null)
+ return base.VisitBvTypeProxy(node);
+
+ return Instantiate(node, new BvType (node.MinBits));
+ }
+ }
+
+}
\ No newline at end of file diff --git a/Source/Core/Util.ssc b/Source/Core/Util.ssc new file mode 100644 index 00000000..914ccf09 --- /dev/null +++ b/Source/Core/Util.ssc @@ -0,0 +1,466 @@ +//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+namespace Microsoft.Boogie
+{
+ using System;
+ using System.IO;
+ using System.Collections;
+ using Microsoft.Contracts;
+
+ public class TokenTextWriter : IDisposable
+ {
+ string! filename;
+ TextWriter! writer;
+ bool writerOpenedHere = false;
+ bool setTokens = true;
+ int line = 1;
+ int col;
+
+ private const int indent_size = 2;
+ protected static string! Indent (int level)
+ {
+ return new string(' ', (indent_size * level));
+ }
+
+
+ // Keywords, this array *must* be sorted
+ public static readonly string[]! BplKeywords =
+ {
+ "assert",
+ "assume",
+ "axiom",
+ "bool",
+ "break",
+ "call",
+ "cast",
+ "const",
+ "else",
+ "ensures",
+ "exists",
+ "false",
+ "forall",
+ "free",
+ "function",
+ "goto",
+ "havoc",
+ "if",
+ "implementation",
+ "int",
+ "invariant",
+ "modifies",
+ "old",
+ "procedure",
+ "public",
+ "requires",
+ "return",
+ "returns",
+ "true",
+ "type",
+ "unique",
+ "var",
+ "where",
+ "while",
+ };
+
+ private IToken! CurrentToken
+ {
+ get
+ {
+ Token token = new Token();
+ token.filename = filename;
+ token.line = line;
+ token.col = col;
+ return token;
+ }
+ }
+
+ public void SetToken(Absy! absy)
+ {
+ this.SetToken(ref absy.tok);
+ }
+
+ public void SetToken(ref IToken! tok)
+ {
+ if (this.setTokens) {
+ tok = this.CurrentToken;
+ }
+ }
+
+ public static string! SanitizeIdentifier (string! name)
+ {
+ int index = Array.BinarySearch(TokenTextWriter.BplKeywords, name);
+ if (index >= 0) {
+ return "\\" + name;
+ } else if (name.Length > 2 && name[0] == 'b' && name[1] == 'v') {
+ int dummy;
+ return int.TryParse(name.Substring(2), out dummy) ? "\\" + name : name;
+ } else {
+ return name;
+ }
+ }
+
+ public TokenTextWriter(string! filename)
+ {
+ this.filename = filename;
+ this.writer = new StreamWriter(filename);
+ this.writerOpenedHere = true;
+ base();
+ }
+
+ public TokenTextWriter(string! filename, TextWriter! writer, bool setTokens)
+ {
+ this.filename = filename;
+ this.writer = writer;
+ this.setTokens = setTokens;
+ base();
+ }
+
+ public TokenTextWriter(string! filename, TextWriter! writer)
+ {
+ this.filename = filename;
+ this.writer = writer;
+ base();
+ }
+
+ public TokenTextWriter(TextWriter! writer)
+ {
+ this.filename = "<no file>";
+ this.writer = writer;
+ base();
+ }
+
+ public void Write(string! text)
+ {
+ this.writer.Write(text);
+ this.col += text.Length;
+ }
+
+ public void WriteIndent(int level)
+ {
+ this.Write(Indent(level));
+ }
+
+ public void Write(string! text, params object[] args)
+ {
+ this.Write(string.Format(text, args));
+ }
+
+ public void Write(int level, string! text)
+ {
+ this.WriteIndent(level);
+ this.Write(text);
+ }
+
+ public void Write(int level, string! text, params object[] args)
+ {
+ this.WriteIndent(level);
+ this.Write(text, args);
+ }
+
+ public void Write(Absy! node, string! text)
+ {
+ this.SetToken(node);
+ this.Write(text);
+ }
+
+ public void Write(Absy! node, string! text, params string[] args)
+ {
+ this.SetToken(node);
+ this.Write(text, args);
+ }
+
+ public void Write(Absy! node, int level, string! text)
+ {
+ this.WriteIndent(level);
+ this.SetToken(node);
+ this.Write(text);
+ }
+
+ public void Write(Absy! node, int level, string! text, params object[] args)
+ {
+ this.WriteIndent(level);
+ this.SetToken(node);
+ this.Write(text, args);
+ }
+
+ public void WriteLine()
+ {
+ this.writer.WriteLine();
+ this.line++;
+ this.col = 0;
+ }
+
+ public void WriteLine(string! text)
+ {
+ this.writer.WriteLine(text);
+ this.line++;
+ this.col = 0;
+ }
+
+ public void WriteLine(string! text, params object[] args)
+ {
+ this.WriteLine(string.Format(text, args));
+ }
+
+ public void WriteLine(int level, string! text)
+ {
+ this.WriteIndent(level);
+ this.WriteLine(text);
+ }
+
+ public void WriteLine(int level, string! text, params object[] args)
+ {
+ this.WriteIndent(level);
+ this.WriteLine(text, args);
+ }
+
+ public void WriteLine(Absy! node, string! text)
+ {
+ this.SetToken(node);
+ this.WriteLine(text);
+ }
+
+ public void WriteLine(Absy! node, int level, string! text)
+ {
+ this.SetToken(node);
+ this.WriteLine(level, text);
+ }
+
+ public void WriteLine(Absy! node, int level, string! text, params object[] args)
+ {
+ this.SetToken(node);
+ this.WriteLine(level, text, args);
+ }
+
+ public void Close()
+ {
+ this.writer.Close();
+ }
+
+ public void Dispose()
+ {
+ this.Close();
+ }
+ }
+
+ public class Helpers {
+ public static string! BeautifyBplString (string! s) {
+ // strip "^" if it is the first character, change "$result" to "result"
+ if (s.StartsWith("^") || s == "$result") {
+ s = s.Substring(1);
+ } else if (s.StartsWith("call")) {
+ s = s.Substring(s.IndexOf('@') + 1);
+ if (s.StartsWith("formal@")) {
+ s = "(value of formal parameter: " + s.Substring(7) +")";
+ }
+ }
+ // strip "$in" from the end of identifier names
+ if (s.EndsWith("$in")) {
+ return "(initial value of: " + s.Substring(0, s.Length-3) +")";
+ } else {
+ return s;
+ }
+ }
+ public static string! PrettyPrintBplExpr (Expr! e) {
+ // anything that is unknown will just be printed via ToString
+ // OldExpr and QuantifierExpr, ExtractExpr, BvConcatExpr are ignored for now
+ // LiteralExpr is printed as itself by ToString
+ if (e is IdentifierExpr) {
+ string s = e.ToString();
+ return Helpers.BeautifyBplString(s);
+ }
+ else if (e is NAryExpr) {
+ NAryExpr ne = (NAryExpr) e;
+ IAppliable fun = ne.Fun;
+ ExprSeq eSeq = ne.Args;
+ if (fun != null) {
+ if ((fun.FunctionName == "$Length" || fun.FunctionName == "$StringLength" ) && eSeq.Length == 1) {
+ Expr e0 = eSeq[0];
+ if (e0 != null) {
+ string s0 = PrettyPrintBplExpr(e0);
+ return s0 + ".Length";
+ }
+ //unexpected, just fall outside to the default
+ } else if (fun.FunctionName == "$typeof" && eSeq.Length == 1) {
+ Expr e0 = eSeq[0];
+ if (e0 != null) {
+ string s0 = PrettyPrintBplExpr(e0);
+ return "(the dynamic type of: " + s0 + ")";
+ }
+ //unexpected, just fall outside to the default
+ } else if (fun.FunctionName == "IntArrayGet" && eSeq.Length == 2) {
+ Expr e0 = eSeq[0];
+ Expr e1 = eSeq[1];
+ if (e0 != null && e1 != null) {
+ string s0 = PrettyPrintBplExpr(e0);
+ string s1 = PrettyPrintBplExpr(e1);
+ return s0 + "[" + s1 + "]";
+ }
+ //unexpected, just fall outside to the default
+ } else if (fun.FunctionName == "$Is" && eSeq.Length == 2) {
+ Expr e0 = eSeq[0];
+ Expr e1 = eSeq[1];
+ if (e0 != null && e1 != null) {
+ string s0 = PrettyPrintBplExpr(e0);
+ string s1 = PrettyPrintBplExpr(e1);
+ return "(" + s0 + " == null || (" + s0 + " is " + s1 + "))";
+ }
+ //unexpected, just fall outside to the default
+ } else if (fun.FunctionName == "$IsNotNull" && eSeq.Length == 2) {
+ Expr e0 = eSeq[0];
+ Expr e1 = eSeq[1];
+ if (e0 != null && e1 != null) {
+ string s0 = PrettyPrintBplExpr(e0);
+ string s1 = PrettyPrintBplExpr(e1);
+ return "(" + s0 + " is " + s1 +")";
+ }
+ //unexpected, just fall outside to the default
+ } else if (fun is MapSelect && eSeq.Length <= 3) {
+ // only maps with up to two arguments are supported right now (here)
+ if (((!)eSeq[0]).ToString() == "$Heap") {
+ //print Index0.Index1, unless Index1 is "$elements", then just print Index0
+ string s0 = PrettyPrintBplExpr((!)eSeq[1]);
+ if (eSeq.Length > 2) {
+ string s1 = PrettyPrintBplExpr((!)eSeq[2]);
+ if (s1 == "$elements") {
+ return s0;
+ } else {
+ if (eSeq[2] is IdentifierExpr) {
+ // strip the class name out of a fieldname
+ s1 = s1.Substring(s1.LastIndexOf('.') + 1);
+ }
+ return s0 + "." + s1;
+ }
+ }
+ }
+ //unexpected, just fall outside to the default
+ } else if (fun is Microsoft.Boogie.BinaryOperator && eSeq.Length == 2) {
+ Microsoft.Boogie.BinaryOperator f = (Microsoft.Boogie.BinaryOperator) fun;
+ Expr e0 = eSeq[0];
+ Expr e1 = eSeq[1];
+ if (e0 != null && e1 != null) {
+ string s0 = PrettyPrintBplExpr(e0);
+ string s1 = PrettyPrintBplExpr(e1);
+ string op = "";
+ switch (f.Op) {
+ case Microsoft.Boogie.BinaryOperator.Opcode.Add:
+ op = " + ";
+ break;
+ case Microsoft.Boogie.BinaryOperator.Opcode.And:
+ op = " && ";
+ break;
+ case Microsoft.Boogie.BinaryOperator.Opcode.Div:
+ op = " / ";
+ break;
+ case Microsoft.Boogie.BinaryOperator.Opcode.Eq:
+ op = " == ";
+ break;
+ case Microsoft.Boogie.BinaryOperator.Opcode.Ge:
+ op = " >= ";
+ break;
+ case Microsoft.Boogie.BinaryOperator.Opcode.Gt:
+ op = " > ";
+ break;
+ case Microsoft.Boogie.BinaryOperator.Opcode.Iff:
+ op = " <==> ";
+ break;
+ case Microsoft.Boogie.BinaryOperator.Opcode.Imp:
+ op = " ==> ";
+ break;
+ case Microsoft.Boogie.BinaryOperator.Opcode.Le:
+ op = " <= ";
+ break;
+ case Microsoft.Boogie.BinaryOperator.Opcode.Lt:
+ op = " < ";
+ break;
+ case Microsoft.Boogie.BinaryOperator.Opcode.Mod:
+ op = " % ";
+ break;
+ case Microsoft.Boogie.BinaryOperator.Opcode.Mul:
+ op = " * ";
+ break;
+ case Microsoft.Boogie.BinaryOperator.Opcode.Neq:
+ op = " != ";
+ break;
+ case Microsoft.Boogie.BinaryOperator.Opcode.Or:
+ op = " || ";
+ break;
+ case Microsoft.Boogie.BinaryOperator.Opcode.Sub:
+ op = " - ";
+ break;
+ case Microsoft.Boogie.BinaryOperator.Opcode.Subtype:
+ op = " <: ";
+ break;
+ default: op = " ";
+ break;
+ }
+ return "(" + s0 + op + s1 + ")";
+ }
+ //unexpected, just fall outside to the default
+ } else {
+ string s = fun.FunctionName + "(";
+ for (int i = 0; i < eSeq.Length; i++) {
+ Expr ex = eSeq[i];
+ assume ex != null;
+ if (i>0) {
+ s += ", ";
+ }
+ string t = PrettyPrintBplExpr(ex);
+ if (t.StartsWith("(") && t.EndsWith(")")) {
+ t = t.Substring(1, t.Length -2);
+ }
+ s += t;
+ }
+ s += ")";
+ return s;
+ //unexpected, just fall outside to the default
+ }
+ }
+ }
+
+ return e.ToString();
+ }
+
+ private static readonly DateTime StartUp = DateTime.Now;
+
+ public static void ExtraTraceInformation(string! point) {
+ if (CommandLineOptions.Clo.TraceTimes) {
+ DateTime now = DateTime.Now;
+ TimeSpan timeSinceStartUp = now - StartUp;
+ Console.WriteLine(">>> {0} [{1} s]", point, timeSinceStartUp.TotalSeconds);
+ }
+ }
+
+ // Substitute @PROC@ in a filename with the given descName
+ public static string! SubstituteAtPROC(string! descName, string! fileName) {
+ System.Text.StringBuilder! sb =
+ new System.Text.StringBuilder(descName.Length);
+ // quote the name, characters like ^ cause trouble in CMD
+ // while $ could cause trouble in SH
+ foreach (char c in descName) {
+ if (Char.IsLetterOrDigit(c) || c == '.') {
+ sb.Append(c);
+ } else {
+ sb.Append('_');
+ }
+ }
+ string pn = sb.ToString();
+ // We attempt to avoid filenames that are too long, but we only
+ // do it by truncating the @PROC@ replacement, which leaves unchanged
+ // any filename extension specified by the user. We base our
+ // calculations on that there is at most one occurrence of @PROC@.
+ if (180 <= fileName.Length - 6 + pn.Length) {
+ pn = pn.Substring(0, max{180 - (fileName.Length - 6), 0}) + "-n" + sequenceNumber;
+ sequenceNumber++;
+ }
+
+ return fileName.Replace("@PROC@", pn);
+ }
+
+ private static int sequenceNumber = 0;
+
+ }
+}
diff --git a/Source/Core/VCExp.ssc b/Source/Core/VCExp.ssc new file mode 100644 index 00000000..4b42a639 --- /dev/null +++ b/Source/Core/VCExp.ssc @@ -0,0 +1,190 @@ +//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.IO;
+using System.Collections;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Text;
+using Microsoft.Contracts;
+using Microsoft.Basetypes;
+
+
+namespace Microsoft.Boogie {
+
+ public class ProverOptions
+ {
+ public class OptionException : Exception {
+ public OptionException(string! msg) { base(msg); }
+ }
+
+ public string/*?*/ LogFilename = null;
+ public bool AppendLogFile = false;
+ public bool SeparateLogFiles = false;
+ // Say (DBG_WAS_VALID) or (DBG_WAS_INVALID) after query
+ public bool ForceLogStatus = false;
+ public int TimeLimit = 0;
+ public int MemoryLimit = 0;
+ public CommandLineOptions.BvHandling BitVectors = CommandLineOptions.BvHandling.None;
+ public int Verbosity = 0;
+
+ private string! stringRepr = "";
+
+ [Pure]
+ public override string! ToString()
+ {
+ return stringRepr;
+ }
+
+ // The usual thing to override.
+ protected virtual bool Parse(string! opt)
+ {
+ return ParseString(opt, "LOG_FILE", ref LogFilename) ||
+ ParseBool(opt, "APPEND_LOG_FILE", ref AppendLogFile) ||
+ ParseBool(opt, "FORCE_LOG_STATUS", ref ForceLogStatus) ||
+ ParseInt(opt, "MEMORY_LIMIT", ref MemoryLimit) ||
+ ParseInt(opt, "VERBOSITY", ref Verbosity) ||
+ ParseInt(opt, "TIME_LIMIT", ref TimeLimit);
+ // || base.Parse(opt)
+ }
+
+ public virtual void Parse(List<string!>! opts)
+ {
+ StringBuilder! sb = new StringBuilder(stringRepr);
+ foreach (string! opt in opts) {
+ if (!Parse(opt)) {
+ ReportError("Unrecognised prover option: " + opt);
+ }
+ sb.Append(opt).Append(" ");
+ }
+ stringRepr = sb.ToString();
+ PostParse();
+ }
+
+ protected virtual void PostParse()
+ {
+ if (LogFilename != null && LogFilename.Contains("@PROC@")) {
+ SeparateLogFiles = true;
+ }
+ }
+
+ protected void ReportError(string! msg)
+ {
+ throw new OptionException(msg);
+ }
+
+ protected virtual bool ParseString(string! opt, string! name, ref string field)
+ {
+ if (opt.Length >= name.Length && opt.StartsWith(name)) {
+ if (opt.Length == name.Length) {
+ field = "";
+ return true;
+ } else if (opt[name.Length] == '=') {
+ field = opt.Substring(name.Length + 1);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ protected virtual bool ParseBool(string! opt, string! name, ref bool field)
+ {
+ string tmp = null;
+ if (ParseString(opt, name, ref tmp))
+ switch (((!)tmp).ToLower()) {
+ case "1":
+ case "true":
+ case "":
+ field = true;
+ return true;
+ case "0":
+ case "false":
+ field = false;
+ return true;
+ default:
+ ReportError("Invalid Boolean option \"" + opt + "\"");
+ return false;
+ }
+ return false;
+ }
+
+ protected virtual bool ParseInt(string! opt, string! name, ref int field)
+ {
+ string tmp = null;
+ int t2;
+ if (ParseString(opt, name, ref tmp)) {
+ if (int.TryParse((!)tmp, out t2)) {
+ field = t2;
+ return true;
+ } else {
+ ReportError("Invalid integer option \"" + opt + "\"");
+ }
+ }
+ return false;
+ }
+
+ static int sequenceNumber = 0;
+ public virtual TextWriter? OpenLog(string/*?*/ descName)
+ {
+ if (LogFilename != null) {
+ string! filename = LogFilename;
+ if (descName != null)
+ filename = Helpers.SubstituteAtPROC(descName, filename);
+ return new StreamWriter(filename, AppendLogFile);
+ } else {
+ return null;
+ }
+ }
+ }
+
+ public abstract class ProverFactory
+ {
+ // Really returns ProverInterface.
+ public abstract object! SpawnProver(ProverOptions! options, object! ctxt);
+
+ // Really returns ProverContext
+ public abstract object! NewProverContext(ProverOptions! options);
+
+ public virtual ProverOptions! BlankProverOptions()
+ {
+ return new ProverOptions();
+ }
+
+ // return true if the prover supports DAG AST as opposed to LET AST
+ public virtual bool SupportsDags
+ {
+ get { return false; }
+ }
+
+ public virtual CommandLineOptions.VCVariety DefaultVCVariety
+ {
+ get
+ ensures result != CommandLineOptions.VCVariety.Unspecified;
+ { return CommandLineOptions.VCVariety.Dag; }
+ }
+
+ public virtual void Close()
+ {
+ }
+
+ public static ProverFactory! Load(string! proverName)
+ ensures result.IsNew && Microsoft.Contracts.Owner.New(result);
+ {
+ string! path;
+ if (proverName.IndexOf("/") > 0 || proverName.IndexOf("\\") > 0) {
+ path = proverName;
+ } else {
+ string! codebase = (!) System.IO.Path.GetDirectoryName(
+ (!)System.Reflection.Assembly.GetExecutingAssembly().Location);
+ path = System.IO.Path.Combine(codebase, "Provers." + proverName + ".dll");
+ }
+ Assembly asm = (!)Assembly.LoadFrom(path);
+ string name = (!)asm.GetName().Name;
+ System.Type factoryType = (!)asm.GetType("Microsoft.Boogie." + name.Replace("Provers.", "") + ".Factory");
+ return (ProverFactory!)Activator.CreateInstance(factoryType);
+ }
+ }
+}
diff --git a/Source/Core/Xml.ssc b/Source/Core/Xml.ssc new file mode 100644 index 00000000..5714849c --- /dev/null +++ b/Source/Core/Xml.ssc @@ -0,0 +1,292 @@ +//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.IO;
+using System.Xml;
+using System.Collections.Generic;
+using Microsoft.Contracts;
+using Cci = System.Compiler;
+
+namespace Microsoft.Boogie
+{
+
+ public class XmlSink {
+ string! filename;
+ [Rep] XmlWriter wr;
+ public bool IsOpen {
+ get { return wr != null; }
+ }
+
+ public XmlSink(string! filename) {
+ this.filename = filename;
+ }
+
+ /// <summary>
+ /// Returns null on success, in which case the caller should eventually invoke Close.
+ /// Returns an error string on failure.
+ /// </summary>
+ public string Open()
+ modifies this.*;
+ ensures IsOpen;
+ {
+ if (wr != null) {
+ Close();
+ }
+ expose (this) {
+ XmlWriterSettings settings = new XmlWriterSettings();
+ settings.Indent = true;
+ wr = XmlWriter.Create(filename, settings);
+ wr.WriteStartDocument();
+ wr.WriteStartElement("boogie");
+ wr.WriteAttributeString("version", CommandLineOptions.VersionNumber);
+ wr.WriteAttributeString("commandLine", Environment.CommandLine);
+ }
+ return null; // success
+ }
+
+ public void Close()
+ modifies this.*;
+ {
+ if (wr != null) {
+ expose (this) {
+ wr.WriteEndDocument();
+ wr.Close();
+ wr = null;
+ }
+ }
+ }
+
+ const string DateTimeFormatString = "u";
+
+ public void WriteStartMethod(string! methodName, DateTime startTime)
+ requires IsOpen;
+ modifies this.*;
+ ensures IsOpen;
+ {
+ assert wr != null;
+ expose (this) {
+ wr.WriteStartElement("method");
+ wr.WriteAttributeString("name", methodName);
+ wr.WriteAttributeString("startTime", startTime.ToString(DateTimeFormatString));
+ }
+ }
+
+ public void WriteEndMethod(string! outcome, DateTime endTime, TimeSpan elapsed)
+ requires IsOpen;
+ modifies this.*;
+ ensures IsOpen;
+ {
+ assert wr != null;
+ expose (this) {
+ wr.WriteStartElement("conclusion");
+ wr.WriteAttributeString("endTime", endTime.ToString(DateTimeFormatString));
+ wr.WriteAttributeString("duration", elapsed.TotalSeconds.ToString());
+ wr.WriteAttributeString("outcome", outcome);
+
+ wr.WriteEndElement(); // outcome
+ wr.WriteEndElement(); // method
+ }
+ }
+
+ public void WriteError(string! message, IToken! errorToken, IToken relatedToken, BlockSeq trace)
+ requires IsOpen && (trace == null || Owner.Different(this, trace));
+ modifies this.*, errorToken.*, relatedToken.*, trace.*;
+ ensures IsOpen;
+ {
+ assert wr != null;
+ expose(this){
+ wr.WriteStartElement("error");
+ wr.WriteAttributeString("message", message);
+ WriteTokenAttributes(errorToken);
+ if (relatedToken != null)
+ {
+ wr.WriteStartElement("related");
+ WriteTokenAttributes(relatedToken);
+ wr.WriteEndElement();
+ }
+ if (trace != null)
+ {
+ wr.WriteStartElement("trace");
+ {
+ foreach (object bo in trace)
+ invariant wr != null;
+ {
+ assume bo is Block;
+ Block b = (Block)bo;
+ wr.WriteStartElement("traceNode");
+ {
+ WriteTokenAttributes(b.tok);
+ wr.WriteAttributeString("label", b.Label);
+ }
+ wr.WriteEndElement();
+ }
+ wr.WriteEndElement();
+ }
+ }
+ wr.WriteEndElement();
+ }
+ }
+
+ public void WriteError(string! message, Cci.Node! offendingNode, BlockSeq trace)
+ requires IsOpen && Owner.Different(this, offendingNode);
+ requires trace == null || Owner.Different(this, trace);
+ modifies this.*, offendingNode.*, trace.*;
+ ensures IsOpen;
+ {
+ assert wr != null;
+ expose(this){
+ wr.WriteStartElement("error");
+ wr.WriteAttributeString("message", message);
+ WriteTokenAttributes(offendingNode);
+ if (trace != null)
+ {
+ wr.WriteStartElement("trace");
+ {
+ foreach (object bo in trace)
+ invariant wr != null;
+ {
+ assume bo is Block;
+ Block b = (Block)bo;
+ wr.WriteStartElement("traceNode");
+ {
+ this.WriteTokenAttributes(b.tok);
+ wr.WriteAttributeString("label", b.Label);
+ }
+ wr.WriteEndElement();
+ }
+ wr.WriteEndElement();
+ }
+ }
+ wr.WriteEndElement();
+ }
+ }
+
+ [Inside]
+ private void WriteTokenAttributes(IToken tok)
+ requires wr != null && wr.IsPeerConsistent;
+ modifies this.0, wr.*;
+ {
+ if (tok != null && tok.filename != null)
+ {
+ wr.WriteAttributeString("file", tok.filename);
+ wr.WriteAttributeString("line", tok.line.ToString());
+ wr.WriteAttributeString("column", tok.col.ToString());
+ }
+ }
+
+ [Inside]
+ private void WriteTokenAttributes(Cci.Node! node)
+ requires wr != null && wr.IsPeerConsistent;
+ modifies this.0, wr.*;
+ {
+ assert wr != null;
+ if (node.SourceContext.Document != null)
+ {
+ wr.WriteAttributeString("file", node.SourceContext.Document.Name);
+ wr.WriteAttributeString("line", node.SourceContext.StartLine.ToString());
+ wr.WriteAttributeString("column", node.SourceContext.StartColumn.ToString());
+ }
+ }
+
+ public void WriteStartInference(string! inferenceName)
+ requires IsOpen;
+ modifies this.*;
+ ensures IsOpen;
+ {
+ assert wr != null;
+ expose (this) {
+ wr.WriteStartElement("inference");
+ wr.WriteAttributeString("name", inferenceName);
+ }
+ }
+
+ public void WriteEndInference()
+ requires IsOpen;
+ modifies this.*;
+ ensures IsOpen;
+ {
+ assert wr != null;
+ expose (this) {
+ wr.WriteEndElement(); // inference
+ }
+ }
+
+ public void WriteContractParaAssignment(string! varName, string val)
+ requires IsOpen;
+ modifies this.*;
+ ensures IsOpen;
+ {
+ assert wr != null;
+ expose (this) {
+ wr.WriteStartElement("assignment");
+ wr.WriteAttributeString("name", varName);
+ wr.WriteAttributeString("value", val);
+ wr.WriteEndElement();
+ }
+ }
+
+ public void WriteStartFile(string! filename)
+ requires IsOpen;
+ modifies this.*;
+ ensures IsOpen;
+ {
+ assert wr != null;
+ expose (this) {
+ wr.WriteStartElement("file");
+ wr.WriteAttributeString("name", filename);
+ }
+ }
+
+ public void WriteEndFile()
+ requires IsOpen;
+ modifies this.*;
+ ensures IsOpen;
+ {
+ assert wr != null;
+ expose (this) {
+ wr.WriteEndElement();
+ }
+ }
+
+ public void WriteFileFragment(string! fragment)
+ requires IsOpen;
+ modifies this.*;
+ ensures IsOpen;
+ {
+ assert wr != null;
+ expose (this) {
+ wr.WriteStartElement("fileFragment");
+ wr.WriteAttributeString("name", fragment);
+ wr.WriteEndElement();
+ }
+ }
+ }
+
+ public class XmlFileScope : IDisposable {
+ [Peer] [SpecPublic] XmlSink sink;
+
+ [Captured]
+ public XmlFileScope(XmlSink? sink, string! filename)
+ requires sink != null ==> sink.IsOpen;
+ modifies sink.*;
+ {
+ if (sink != null) {
+ sink.WriteStartFile(filename); // invoke this method while "sink" is still peer consistent
+ Owner.AssignSame(this, sink);
+ this.sink = sink;
+ }
+ }
+
+ public void Dispose()
+ modifies this.*, sink.*;
+ {
+ if (sink != null) {
+ assume sink.IsOpen;
+ sink.WriteEndFile();
+ }
+ }
+ }
+}
diff --git a/Source/Core/parser.frame b/Source/Core/parser.frame new file mode 100644 index 00000000..81c7e67c --- /dev/null +++ b/Source/Core/parser.frame @@ -0,0 +1,99 @@ +
+using System;
+using Microsoft.Contracts;
+
+namespace -->namespace {
+
+public class Parser {
+-->constants
+ const bool T = true;
+ const bool x = false;
+ const int minErrDist = 2;
+
+ static Token/*!*/ token; // last recognized token
+ static Token/*!*/ t; // lookahead token
+ static int errDist = minErrDist;
+
+ -->declarations
+
+ static void Error(int n) {
+ if (errDist >= minErrDist) Errors.SynErr(n, t.filename, t.line, t.col);
+ errDist = 0;
+ }
+
+ public static void SemErr(string! msg) {
+ if (errDist >= minErrDist) Errors.SemErr(token.filename, token.line, token.col, msg);
+ errDist = 0;
+ }
+
+ static void Get() {
+ for (;;) {
+ token = t;
+ t = Scanner.Scan();
+ if (t.kind<=maxT) {errDist++; return;}
+-->pragmas
+ t = token;
+ }
+ }
+
+ static void Expect(int n) {
+ if (t.kind==n) Get(); else Error(n);
+ }
+
+ static bool StartOf(int s) {
+ return set[s, t.kind];
+ }
+
+ static void ExpectWeak(int n, int follow) {
+ if (t.kind == n) Get();
+ else {
+ Error(n);
+ while (!StartOf(follow)) Get();
+ }
+ }
+
+ static bool WeakSeparator(int n, int syFol, int repFol) {
+ bool[] s = new bool[maxT+1];
+ if (t.kind == n) {Get(); return true;}
+ else if (StartOf(repFol)) return false;
+ else {
+ for (int i=0; i <= maxT; i++) {
+ s[i] = set[syFol, i] || set[repFol, i] || set[0, i];
+ }
+ Error(n);
+ while (!s[t.kind]) Get();
+ return StartOf(syFol);
+ }
+ }
+
+-->productions
+
+ public static void Parse() {
+ Errors.SynErr = new ErrorProc(SynErr);
+ t = new Token();
+ Get();
+-->parseRoot
+ }
+
+ [Microsoft.Contracts.Verify(false)]
+ static void SynErr(int n, string filename, int line, int col) {
+ Errors.count++;
+ Console.Write("{0}({1},{2}): syntax error: ", filename, line, col);
+ string s;
+ switch (n) {
+-->errors
+ default: s = "error " + n; break;
+ }
+ Console.WriteLine(s);
+ }
+
+ static bool[,]! set = {
+-->initialization
+ };
+
+ [Microsoft.Contracts.Verify(false)]
+ static Parser() {}
+} // end Parser
+
+} // end namespace
+$$$
\ No newline at end of file diff --git a/Source/Core/scanner.frame b/Source/Core/scanner.frame new file mode 100644 index 00000000..3753c6e9 --- /dev/null +++ b/Source/Core/scanner.frame @@ -0,0 +1,377 @@ +using System;
+using System.IO;
+using System.Collections;
+using System.Collections.Generic;
+using System.Text;
+using Microsoft.Contracts;
+
+
+namespace Microsoft.Boogie {
+
+ [Immutable]
+ public interface IToken {
+ int kind {get; set; } // token kind
+ string filename {get; set; } // token file
+ int pos {get; set; } // token position in the source text (starting at 0)
+ int col {get; set; } // token column (starting at 0)
+ int line {get; set; } // token line (starting at 1)
+ string/*!*/ val {get; set; } // token value
+
+ bool IsValid { get; }
+ }
+
+ [Immutable]
+ public class Token : IToken {
+ int _kind; // token kind
+ string _filename; // token file
+ int _pos; // token position in the source text (starting at 0)
+ int _col; // token column (starting at 0)
+ int _line; // token line (starting at 1)
+ string/*!*/ _val = "foo"; // token value
+
+ public static IToken! NoToken = new Token();
+
+ public Token();
+ public Token(int linenum, int colnum) {
+ this._line = linenum;
+ this._col = colnum;
+ base();
+ }
+
+ public int kind {
+ get { return this._kind; }
+ set { this._kind = value; }
+ }
+
+ public string filename{
+ get { return this._filename; }
+ set { this._filename = value; }
+ }
+
+ public int pos{
+ get { return this._pos; }
+ set { this._pos = value; }
+ }
+
+ public int col{
+ get { return this._col; }
+ set { this._col = value; }
+ }
+
+ public int line{
+ get { return this._line; }
+ set { this._line = value; }
+ }
+
+ public string/*!*/ val{
+ get { return this._val; }
+ set { this._val = value; }
+ }
+
+ public bool IsValid { get { return this._filename != null; } }
+ }
+
+}
+
+namespace -->namespace {
+
+using Microsoft.Boogie;
+
+public class Buffer {
+ static string/*!*/ buf;
+ static int bufLen;
+ static int pos;
+ public const int eof = '\uffff';
+
+ public static void Fill(StreamReader! reader) {
+ List<string!> defines = new List<string!>();
+ Fill(reader, defines);
+ }
+
+ struct ReadState {
+ public bool hasSeenElse;
+ public bool mayStillIncludeAnotherAlternative;
+ public ReadState(bool hasSeenElse, bool mayStillIncludeAnotherAlternative) {
+ this.hasSeenElse = hasSeenElse;
+ this.mayStillIncludeAnotherAlternative = mayStillIncludeAnotherAlternative;
+ }
+ }
+
+ public static void Fill(StreamReader! reader, List<string!>! defines) {
+ StringBuilder sb = new StringBuilder();
+ List<ReadState>! readState = new List<ReadState>(); // readState.Count is the current nesting level of #if's
+ int ignoreCutoff = -1; // -1 means we're not ignoring; for 0<=n, n means we're ignoring because of something at nesting level n
+ while (true)
+ invariant -1 <= ignoreCutoff && ignoreCutoff < readState.Count;
+ {
+ string s = reader.ReadLine();
+ if (s == null) {
+ if (readState.Count != 0) {
+ sb.AppendLine("#MalformedInput: missing #endif");
+ }
+ break;
+ }
+ string t = s.Trim();
+ if (t.StartsWith("#if")) {
+ ReadState rs = new ReadState(false, false);
+ if (ignoreCutoff != -1) {
+ // we're already in a state of ignoring, so continue to ignore
+ } else if (IfdefConditionSaysToInclude(t.Substring(3).TrimStart(), defines)) {
+ // include this branch
+ } else {
+ ignoreCutoff = readState.Count; // start ignoring
+ rs.mayStillIncludeAnotherAlternative = true; // allow some later "elsif" or "else" branch to be included
+ }
+ readState.Add(rs);
+ sb.AppendLine(); // ignore the #if line
+
+ } else if (t.StartsWith("#elsif")) {
+ ReadState rs;
+ if (readState.Count == 0 || (rs = readState[readState.Count-1]).hasSeenElse) {
+ sb.AppendLine("#MalformedInput: misplaced #elsif"); // malformed input
+ break;
+ }
+ if (ignoreCutoff == -1) {
+ // we had included the previous branch
+ assert !rs.mayStillIncludeAnotherAlternative;
+ ignoreCutoff = readState.Count-1; // start ignoring
+ } else if (rs.mayStillIncludeAnotherAlternative && IfdefConditionSaysToInclude(t.Substring(6).TrimStart(), defines)) {
+ // include this branch, but no subsequent branch at this level
+ ignoreCutoff = -1;
+ rs.mayStillIncludeAnotherAlternative = false;
+ readState[readState.Count-1] = rs;
+ }
+ sb.AppendLine(); // ignore the #elsif line
+
+ } else if (t == "#else") {
+ ReadState rs;
+ if (readState.Count == 0 || (rs = readState[readState.Count-1]).hasSeenElse) {
+ sb.AppendLine("#MalformedInput: misplaced #else"); // malformed input
+ break;
+ }
+ rs.hasSeenElse = true;
+ if (ignoreCutoff == -1) {
+ // we had included the previous branch
+ assert !rs.mayStillIncludeAnotherAlternative;
+ ignoreCutoff = readState.Count-1; // start ignoring
+ } else if (rs.mayStillIncludeAnotherAlternative) {
+ // include this branch
+ ignoreCutoff = -1;
+ rs.mayStillIncludeAnotherAlternative = false;
+ }
+ readState[readState.Count-1] = rs;
+ sb.AppendLine(); // ignore the #else line
+
+ } else if (t == "#endif") {
+ if (readState.Count == 0) {
+ sb.AppendLine("#MalformedInput: misplaced #endif"); // malformed input
+ break;
+ }
+ readState.RemoveAt(readState.Count-1); // pop
+ if (ignoreCutoff == readState.Count) {
+ // we had ignored the branch that ends here; so, now we start including again
+ ignoreCutoff = -1;
+ }
+ sb.AppendLine(); // ignore the #endif line
+
+ } else if (ignoreCutoff == -1) {
+ sb.AppendLine(s); // included line
+
+ } else {
+ sb.AppendLine(); // ignore the line
+ }
+ }
+
+ Fill(sb.ToString());
+ }
+
+ // "arg" is assumed to be trimmed
+ private static bool IfdefConditionSaysToInclude(string! arg, List<string!>! defines) {
+ bool sense = true;
+ while (arg.StartsWith("!")) {
+ sense = !sense;
+ arg = arg.Substring(1).TrimStart();
+ }
+ return defines.Contains(arg) == sense;
+ }
+
+ public static void Fill(string! text) {
+ buf = text;
+ bufLen = buf.Length;
+ pos = 0;
+ }
+
+ public static int Read() {
+ if (pos < bufLen) {
+ return buf[pos++];
+ } else {
+ return eof;
+ }
+ }
+
+ public static string/*!*/ ReadToEOL() {
+ int x = buf.IndexOf('\n', pos);
+ if (x == -1) {
+ string s = buf.Substring(pos);
+ pos = buf.Length;
+ return s;
+ } else {
+ string s = buf.Substring(pos, x+1 - pos); // also include the '\n'
+ pos = x+1;
+ return s;
+ }
+ }
+
+ public static int Pos {
+ get { return pos; }
+ set {
+ if (value < 0) pos = 0; else if (value >= bufLen) pos = bufLen; else pos = value;
+ }
+ }
+}
+
+
+
+public class Scanner {
+ const char EOF = '\0';
+ const char CR = '\r';
+ const char LF = '\n';
+ [Microsoft.Contracts.Verify(false)]
+-->declarations
+
+ static Token/*!*/ t; // current token
+ static char ch; // current input character
+ static int pos; // column number of current character
+ static int line; // line number of current character
+ static int lineStart; // start position of current line
+ static Queue! oldEols; // EOLs that appeared in a comment;
+ static BitArray/*!*/ ignore; // set of characters to be ignored by the scanner
+ static string Filename;
+
+ ///<summary>
+ ///Initializes the scanner. Note: first fill the Buffer.
+ ///</summary>
+ ///<param name="filename">File name used for error reporting</param>
+ public static void Init (string filename) {
+ Filename = filename;
+ pos = -1; line = 1; lineStart = 0;
+ oldEols = new Queue();
+ NextCh();
+-->initialization
+ }
+
+ private static void NextCh() {
+ if (oldEols.Count > 0) {
+ ch = (char) ((!)oldEols.Dequeue());
+ } else {
+ while (true) {
+ ch = (char)Buffer.Read(); pos++;
+ if (ch == Buffer.eof) {
+ ch = EOF;
+ } else if (ch == LF) {
+ line++;
+ lineStart = pos + 1;
+
+ } else if (ch == '#' && pos == lineStart) {
+ int prLine = line;
+ int prColumn = pos - lineStart; // which is 0
+
+ string hashLine = Buffer.ReadToEOL(); pos += hashLine.Length;
+ line++;
+ lineStart = pos + 1;
+
+ hashLine = hashLine.TrimEnd(null);
+ if (hashLine.StartsWith("line ") || hashLine == "line") {
+ // parse #line pragma: #line num [filename]
+ string h = hashLine.Substring(4).TrimStart(null);
+ int x = h.IndexOf(' ');
+ if (x == -1) {
+ x = h.Length; // this will be convenient below when we look for a filename
+ }
+ try {
+ int li = int.Parse(h.Substring(0, x));
+
+ h = h.Substring(x).Trim();
+
+ // act on #line
+ line = li;
+ if (h.Length != 0) {
+ // a filename was specified
+ Filename = h;
+ }
+ continue; // successfully parsed and acted on the #line pragma
+
+ } catch (FormatException) {
+ // just fall down through to produce an error message
+ }
+ Errors.SemErr(Filename, prLine, prColumn, "Malformed (#line num [filename]) pragma: #" + hashLine);
+ continue;
+ }
+
+ Errors.SemErr(Filename, prLine, prColumn, "Unrecognized pragma: #" + hashLine);
+ continue;
+ }
+ return;
+ }
+ }
+ }
+
+-->comment
+
+ static void CheckLiteral() {
+ switch (t.val) {
+-->literals
+ }
+ }
+
+ public static Token/*!*/ Scan() {
+ while (ignore[ch]) { NextCh(); }
+-->scan1
+ t = new Token();
+ t.pos = pos; t.col = pos - lineStart + 1; t.line = line; t.filename = Filename;
+ int state = (/*^ (!) ^*/ start)[ch];
+ StringBuilder buf = new StringBuilder(16);
+ buf.Append(ch); NextCh();
+
+ switch (state) {
+ case 0: {t.kind = noSym; goto done;} // NextCh already done
+-->scan2
+ }
+ done:
+ t.val = buf.ToString();
+ return t;
+ }
+
+} // end Scanner
+
+
+public delegate void ErrorProc(int n, string filename, int line, int col);
+
+public class Errors {
+ public static int count = 0; // number of errors detected
+ public static ErrorProc/*!*/ SynErr; // syntactic errors
+
+ public static void SemErr(string filename, int line, int col, string! msg) { // semantic errors
+ ConsoleColor color = Console.ForegroundColor;
+ Console.ForegroundColor = ConsoleColor.Red;
+ Console.WriteLine("{0}({1},{2}): Error: {3}", filename, line, col, msg);
+ Console.ForegroundColor = color;
+ count++;
+ }
+
+ public static void SemErr(IToken! tok, string! msg) { // semantic errors
+ SemErr(tok.filename, tok.line, tok.col, msg);
+ }
+
+ public static void Exception (string s) {
+ ConsoleColor color = Console.ForegroundColor;
+ Console.ForegroundColor = ConsoleColor.Red;
+ Console.WriteLine(s);
+ Console.ForegroundColor = color;
+ System.Environment.Exit(0);
+ }
+
+} // Errors
+
+} // end namespace
+$$$
|