summaryrefslogtreecommitdiff
path: root/Source
diff options
context:
space:
mode:
authorGravatar mikebarnett <unknown>2009-07-15 21:03:41 +0000
committerGravatar mikebarnett <unknown>2009-07-15 21:03:41 +0000
commitce1c2de044c91624370411e23acab13b0381949b (patch)
tree592539996fe08050ead5ee210c973801611dde40 /Source
Initial set of files.
Diffstat (limited to 'Source')
-rw-r--r--Source/..svnbridge/Boogie.suo1
-rw-r--r--Source/..svnbridge/Dafny.suo1
-rw-r--r--Source/AIFramework/AIFramework.sscproj157
-rw-r--r--Source/AIFramework/CommonFunctionSymbols.ssc926
-rw-r--r--Source/AIFramework/Expr.ssc447
-rw-r--r--Source/AIFramework/Functional.ssc284
-rw-r--r--Source/AIFramework/Lattice.ssc685
-rw-r--r--Source/AIFramework/Logger.ssc49
-rw-r--r--Source/AIFramework/MultiLattice.ssc563
-rw-r--r--Source/AIFramework/Mutable.ssc117
-rw-r--r--Source/AIFramework/Polyhedra/LinearConstraint.ssc588
-rw-r--r--Source/AIFramework/Polyhedra/LinearConstraintSystem.ssc1856
-rw-r--r--Source/AIFramework/Polyhedra/PolyhedraAbstraction.ssc744
-rw-r--r--Source/AIFramework/Polyhedra/SimplexTableau.ssc717
-rw-r--r--Source/AIFramework/VariableMap/ConstantAbstraction.ssc210
-rw-r--r--Source/AIFramework/VariableMap/ConstantExpressions.ssc538
-rw-r--r--Source/AIFramework/VariableMap/DynamicTypeLattice.ssc475
-rw-r--r--Source/AIFramework/VariableMap/Intervals.ssc790
-rw-r--r--Source/AIFramework/VariableMap/MicroLattice.ssc79
-rw-r--r--Source/AIFramework/VariableMap/Nullness.ssc227
-rw-r--r--Source/AIFramework/VariableMap/VariableMapLattice.ssc749
-rw-r--r--Source/AbsInt/AbsInt.sscproj120
-rw-r--r--Source/AbsInt/AbstractInterpretation.ssc1074
-rw-r--r--Source/AbsInt/ExprFactories.ssc233
-rw-r--r--Source/AbsInt/LoopInvariantsOnDemand.ssc78
-rw-r--r--Source/AbsInt/Traverse.ssc182
-rw-r--r--Source/Basetypes/Basetypes.sscproj103
-rw-r--r--Source/Basetypes/BigNum.ssc341
-rw-r--r--Source/Basetypes/Rational.ssc300
-rw-r--r--Source/Basetypes/Set.ssc282
-rw-r--r--Source/Boogie.sln99
-rw-r--r--Source/Boogie.suobin0 -> 60928 bytes
-rw-r--r--Source/BoogieDriver/BoogieDriver.ssc648
-rw-r--r--Source/BoogieDriver/BoogieDriver.sscproj140
-rw-r--r--Source/Core.sscproj210
-rw-r--r--Source/Core/Absy.ssc3073
-rw-r--r--Source/Core/AbsyCmd.ssc2389
-rw-r--r--Source/Core/AbsyExpr.ssc3256
-rw-r--r--Source/Core/AbsyType.ssc2857
-rw-r--r--Source/Core/BoogiePL.atg1374
-rw-r--r--Source/Core/CommandLineOptions.ssc1935
-rw-r--r--Source/Core/Core.sscproj212
-rw-r--r--Source/Core/Duplicator.ssc348
-rw-r--r--Source/Core/Graph.as352
-rw-r--r--Source/Core/GraphAlgorithms.ssc175
-rw-r--r--Source/Core/Inline.ssc907
-rw-r--r--Source/Core/LoopUnroll.ssc174
-rw-r--r--Source/Core/Makefile18
-rw-r--r--Source/Core/OOLongUtil.ssc174
-rw-r--r--Source/Core/Parser.ssc2021
-rw-r--r--Source/Core/PureCollections.ssc785
-rw-r--r--Source/Core/Readme.txt61
-rw-r--r--Source/Core/ResolutionContext.ssc518
-rw-r--r--Source/Core/Scanner.ssc720
-rw-r--r--Source/Core/StandardVisitor.ssc503
-rw-r--r--Source/Core/TypeAmbiguitySeeker.ssc95
-rw-r--r--Source/Core/Util.ssc466
-rw-r--r--Source/Core/VCExp.ssc190
-rw-r--r--Source/Core/Xml.ssc292
-rw-r--r--Source/Core/parser.frame99
-rw-r--r--Source/Core/scanner.frame377
-rw-r--r--Source/Dafny.sln26
-rw-r--r--Source/Dafny.suobin0 -> 10752 bytes
-rw-r--r--Source/Dafny/Dafny.atg963
-rw-r--r--Source/Dafny/DafnyAst.ssc1049
-rw-r--r--Source/Dafny/DafnyMain.ssc73
-rw-r--r--Source/Dafny/DafnyPipeline.sscproj118
-rw-r--r--Source/Dafny/Makefile15
-rw-r--r--Source/Dafny/Parser.ssc1592
-rw-r--r--Source/Dafny/Printer.ssc663
-rw-r--r--Source/Dafny/Resolver.ssc1419
-rw-r--r--Source/Dafny/Scanner.ssc491
-rw-r--r--Source/Dafny/Translator.ssc2446
-rw-r--r--Source/Dafny/parser.frame103
-rw-r--r--Source/Dafny/scanner.frame170
-rw-r--r--Source/DafnyDriver/DafnyDriver.ssc681
-rw-r--r--Source/DafnyDriver/DafnyDriver.sscproj124
-rw-r--r--Source/Graph/Graph.ssc746
-rw-r--r--Source/Graph/Graph.sscproj89
-rw-r--r--Source/Houdini/Checker.ssc460
-rw-r--r--Source/Houdini/Houdini.ssc1188
-rw-r--r--Source/Houdini/Houdini.sscproj118
-rw-r--r--Source/Provers/SMTLib/ProverInterface.ssc240
-rw-r--r--Source/Provers/SMTLib/SMTLib.sscproj116
-rw-r--r--Source/Provers/SMTLib/SMTLibLineariser.ssc635
-rw-r--r--Source/Provers/SMTLib/TypeDeclCollector.ssc107
-rw-r--r--Source/Provers/Simplify/Let2ImpliesVisitor.ssc183
-rw-r--r--Source/Provers/Simplify/Prover.ssc606
-rw-r--r--Source/Provers/Simplify/ProverInterface.ssc633
-rw-r--r--Source/Provers/Simplify/Simplify.sscproj135
-rw-r--r--Source/Provers/Z3/Inspector.ssc130
-rw-r--r--Source/Provers/Z3/Prover.ssc870
-rw-r--r--Source/Provers/Z3/ProverInterface.ssc320
-rw-r--r--Source/Provers/Z3/TypeDeclCollector.ssc212
-rw-r--r--Source/Provers/Z3/Z3.sscproj140
-rw-r--r--Source/VCExpr/BigLiteralAbstracter.ssc198
-rw-r--r--Source/VCExpr/Boogie2VCExpr.ssc853
-rw-r--r--Source/VCExpr/Clustering.ssc449
-rw-r--r--Source/VCExpr/LetBindingSorter.ssc135
-rw-r--r--Source/VCExpr/NameClashResolver.ssc129
-rw-r--r--Source/VCExpr/SimplifyLikeLineariser.ssc795
-rw-r--r--Source/VCExpr/TermFormulaFlattening.ssc222
-rw-r--r--Source/VCExpr/TypeErasure.ssc1160
-rw-r--r--Source/VCExpr/TypeErasureArguments.ssc618
-rw-r--r--Source/VCExpr/TypeErasurePremisses.ssc1025
-rw-r--r--Source/VCExpr/VCExpr.sscproj141
-rw-r--r--Source/VCExpr/VCExprAST.ssc1285
-rw-r--r--Source/VCExpr/VCExprASTPrinter.ssc240
-rw-r--r--Source/VCExpr/VCExprASTVisitors.ssc999
-rw-r--r--Source/VCGeneration/Check.ssc375
-rw-r--r--Source/VCGeneration/ConditionGeneration.ssc790
-rw-r--r--Source/VCGeneration/Context.ssc199
-rw-r--r--Source/VCGeneration/OrderingAxioms.ssc285
-rw-r--r--Source/VCGeneration/VC.ssc3215
-rw-r--r--Source/VCGeneration/VCDoomed.ssc817
-rw-r--r--Source/VCGeneration/VCGeneration.sscproj142
-rw-r--r--Source/VCGeneration/Wlp.ssc131
-rw-r--r--Source/XAHA/XAHA.ssc601
-rw-r--r--Source/XAHA/XAHA.sscproj93
-rw-r--r--Source/foo.txt93
-rw-r--r--Source/version.ssc8
121 files changed, 69243 insertions, 0 deletions
diff --git a/Source/..svnbridge/Boogie.suo b/Source/..svnbridge/Boogie.suo
new file mode 100644
index 00000000..08149303
--- /dev/null
+++ b/Source/..svnbridge/Boogie.suo
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="utf-8"?><ItemProperties xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><Properties><Property><Name>svn:mime-type</Name><Value>application/octet-stream</Value></Property></Properties></ItemProperties> \ No newline at end of file
diff --git a/Source/..svnbridge/Dafny.suo b/Source/..svnbridge/Dafny.suo
new file mode 100644
index 00000000..08149303
--- /dev/null
+++ b/Source/..svnbridge/Dafny.suo
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="utf-8"?><ItemProperties xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><Properties><Property><Name>svn:mime-type</Name><Value>application/octet-stream</Value></Property></Properties></ItemProperties> \ No newline at end of file
diff --git a/Source/AIFramework/AIFramework.sscproj b/Source/AIFramework/AIFramework.sscproj
new file mode 100644
index 00000000..dd79b9b8
--- /dev/null
+++ b/Source/AIFramework/AIFramework.sscproj
@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="utf-8"?>
+<VisualStudioProject>
+ <XEN ProjectType="Local"
+ SchemaVersion="1.0"
+ Name="AIFramework"
+ ProjectGuid="24b55172-ad8b-47d1-8952-5a95cfdb9b31"
+ >
+ <Build>
+ <Settings ApplicationIcon=""
+ AssemblyName="AIFramework"
+ OutputType="Library"
+ RootNamespace="AIFramework"
+ StartupObject=""
+ StandardLibraryLocation=""
+ TargetPlatform="v2"
+ TargetPlatformLocation=""
+ ShadowedAssembly=""
+ >
+ <Config Name="Debug"
+ AllowUnsafeBlocks="False"
+ BaseAddress="285212672"
+ CheckForOverflowUnderflow="False"
+ ConfigurationOverrideFile=""
+ DefineConstants="DEBUG;TRACE"
+ 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"
+ />
+ <Config Name="Release"
+ AllowUnsafeBlocks="false"
+ BaseAddress="285212672"
+ CheckForOverflowUnderflow="false"
+ ConfigurationOverrideFile=""
+ DefineConstants="TRACE"
+ DocumentationFile=""
+ DebugSymbols="false"
+ FileAlignment="4096"
+ IncrementalBuild="false"
+ Optimize="true"
+ OutputPath="bin\release"
+ RegisterForComInterop="false"
+ RemoveIntegerChecks="false"
+ TreatWarningsAsErrors="True"
+ WarningLevel="4"
+ />
+ </Settings>
+ <References>
+ <Reference Name="System"
+ AssemblyName="System"
+ Private="false"
+ />
+ <Reference Name="Basetypes"
+ Project="{0C692837-77EC-415F-BF04-395E3ED06E9A}"
+ Private="true"
+ />
+ </References>
+ </Build>
+ <Files>
+ <Include>
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="Expr.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="CommonFunctionSymbols.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="Lattice.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="Functional.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="Mutable.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="Logger.ssc"
+ />
+ <Folder RelPath="VariableMap\"
+ />
+ <Folder RelPath="Polyhedra\"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="VariableMap\MicroLattice.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="VariableMap\VariableMapLattice.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="MultiLattice.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="VariableMap\Nullness.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="VariableMap\ConstantAbstraction.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="VariableMap\DynamicTypeLattice.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="Polyhedra\SimplexTableau.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="Polyhedra\LinearConstraint.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="Polyhedra\LinearConstraintSystem.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="Polyhedra\PolyhedraAbstraction.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="..\version.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="VariableMap\Intervals.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="VariableMap\ConstantExpressions.ssc"
+ />
+ </Include>
+ </Files>
+ </XEN>
+</VisualStudioProject> \ No newline at end of file
diff --git a/Source/AIFramework/CommonFunctionSymbols.ssc b/Source/AIFramework/CommonFunctionSymbols.ssc
new file mode 100644
index 00000000..4e38fbcb
--- /dev/null
+++ b/Source/AIFramework/CommonFunctionSymbols.ssc
@@ -0,0 +1,926 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+namespace Microsoft.AbstractInterpretationFramework
+{
+ using Microsoft.Contracts;
+ using System.Collections;
+ using Microsoft.SpecSharp.Collections;
+ using Microsoft.Basetypes;
+
+ /// <summary>
+ /// A basic class for function symbols.
+ /// </summary>
+ public class FunctionSymbol : IFunctionSymbol
+ {
+ private readonly string! display;
+ private readonly AIType! typ;
+
+ public FunctionSymbol(AIType! typ)
+ : this("FunctionSymbol", typ)
+ {
+ }
+
+ internal FunctionSymbol(string! display, AIType! typ)
+ {
+ this.display = display;
+ this.typ = typ;
+ // base();
+ }
+
+// public AIType! AIType { [Pure][Reads(ReadsAttribute.Reads.Owned)] get { return typ; } }
+ public AIType! AIType { [Pure][Reads(ReadsAttribute.Reads.Owned)] get { return typ; } }
+
+ [NoDefaultContract]
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public override string! ToString()
+ {
+ return display;
+ }
+
+ }
+
+ /// <summary>
+ /// A class for integer constants.
+ /// </summary>
+ public class IntSymbol : FunctionSymbol
+ {
+ public readonly BigNum Value;
+
+ /// <summary>
+ /// The intention is that this constructor be called only from the Int.Const method.
+ /// </summary>
+ internal IntSymbol(BigNum x)
+ : base((!)x.ToString(), Int.Type)
+ {
+ this.Value = x;
+ }
+
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)]
+ public override bool Equals(object other)
+ {
+ IntSymbol isym = other as IntSymbol;
+ return isym != null && isym.Value.Equals(this.Value);
+ }
+
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public override int GetHashCode()
+ {
+ return Value.GetHashCode();
+ }
+ }
+
+ /// <summary>
+ /// A class for bitvector constants.
+ /// </summary>
+ public class BvSymbol : FunctionSymbol
+ {
+ public readonly BigNum Value;
+ public readonly int Bits;
+
+ /// <summary>
+ /// The intention is that this constructor be called only from the Int.Const method.
+ /// </summary>
+ internal BvSymbol(BigNum x, int y)
+ : base(x + "bv" + y, Bv.Type)
+ {
+ this.Value = x;
+ this.Bits = y;
+ }
+
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)]
+ public override bool Equals(object other)
+ {
+ BvSymbol isym = other as BvSymbol;
+ return isym != null && isym.Value == this.Value && isym.Bits == this.Bits;
+ }
+
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public override int GetHashCode()
+ {
+ unchecked {
+ return Value.GetHashCode() ^ Bits;
+ }
+ }
+ }
+
+ public class DoubleSymbol : FunctionSymbol
+ {
+ public readonly double Value;
+
+ /// <summary>
+ /// The intention is that this constructor be called only from the Double.Const method.
+ /// </summary>
+ internal DoubleSymbol(double x)
+ : base((!)x.ToString(), Double.Type)
+ {
+ this.Value = x;
+ }
+
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)]
+ public override bool Equals(object other)
+ {
+ DoubleSymbol dsym = other as DoubleSymbol;
+ return dsym != null && dsym.Value == this.Value;
+ }
+
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public override int GetHashCode()
+ {
+ return Value.GetHashCode();
+ }
+ }
+
+ /// <summary>
+ /// Function symbol based on a string. Uses the string equality for determining equality
+ /// of symbol.
+ /// </summary>
+ public class NamedSymbol : FunctionSymbol
+ {
+ public string! Value { [NoDefaultContract] get { return (!) this.ToString(); } }
+
+ public NamedSymbol(string! symbol, AIType! typ)
+ : base(symbol, typ)
+ {
+ }
+
+ [NoDefaultContract]
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)]
+ public override bool Equals(object other)
+ {
+ NamedSymbol nsym = other as NamedSymbol;
+ return nsym != null && this.Value.Equals(nsym.Value);
+ }
+
+ [NoDefaultContract]
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public override int GetHashCode()
+ {
+ return Value.GetHashCode();
+ }
+ }
+
+ //
+ // In the following, the classes like Value and Prop serve two
+ // roles. The primary role is to be the base types for AIType.
+ // The only objects of these classes are the representative
+ // objects that denote an AIType, which are given by the
+ // "Type" property. Subtypes in the AIType language are
+ // encoded by subclassing. This yields some "higher-orderness"
+ // for checking subtyping in the AIType language, by using
+ // the Spec#/C# subclassing checks.
+ //
+ // The other role is simply as a module for collecting like function
+ // symbols.
+ //
+
+ //-------------------------- Terms ----------------------------------
+
+ /// <summary>
+ /// A class with the equality symbol and the ValueType.Type.
+ /// </summary>
+ public class Value : AIType
+ {
+ private static readonly AIType! valtype = new Value();
+ public static AIType! Type { get { return valtype; } }
+
+ private static readonly FunctionType[]! funtypeCache = new FunctionType[5];
+ public static FunctionType! FunctionType(int inParameterCount)
+ requires 0 <= inParameterCount;
+ // ensures result.Arity == inParameterCount;
+ {
+ FunctionType result;
+ if (inParameterCount < funtypeCache.Length) {
+ result = funtypeCache[inParameterCount];
+ if (result != null) {
+ return result;
+ }
+ }
+ AIType[] signature = new AIType[1 + inParameterCount];
+ for (int i = 0; i < signature.Length; i++) {
+ signature[i] = valtype;
+ }
+ result = new FunctionType(signature);
+ if (inParameterCount < funtypeCache.Length) {
+ funtypeCache[inParameterCount] = result;
+ }
+ return result;
+ }
+
+ [Once] private static AIType! binreltype;
+ private static AIType! BinrelType {
+ get {
+ if (binreltype == null) {
+ binreltype = new FunctionType(Type, Type, Prop.Type);
+ }
+ return binreltype;
+ }
+ }
+
+ [Once] private static FunctionSymbol! _eq;
+ public static FunctionSymbol! Eq {
+ get {
+ if (_eq == null) {
+ _eq = new FunctionSymbol("=", BinrelType);
+ }
+ return _eq;
+ }
+ }
+ [Once] private static FunctionSymbol! _neq;
+ public static FunctionSymbol! Neq {
+ get {
+ if (_neq == null) {
+ _neq = new FunctionSymbol("!=", BinrelType);
+ }
+ return _neq;
+ }
+ }
+ [Once] private static FunctionSymbol! _subtype;
+ public static FunctionSymbol! Subtype {
+ get {
+ if (_subtype == null) {
+ _subtype = new FunctionSymbol("<:", BinrelType);
+ }
+ return _subtype;
+ }
+ }
+
+ [Once] private static AIType! typeof_type;
+ private static AIType! TypeofType {
+ get {
+ if (typeof_type == null) {
+ typeof_type = new FunctionType(Ref.Type, Type);
+ }
+ return typeof_type;
+ }
+ }
+ [Once] private static FunctionSymbol! _typeof;
+ public static FunctionSymbol! Typeof {
+ get {
+ if (_typeof == null) {
+ _typeof = new FunctionSymbol("typeof", TypeofType);
+ }
+ return _typeof;
+ }
+ }
+
+ /// <summary>
+ /// Value should not be instantiated from the outside, except perhaps in
+ /// subclasses.
+ /// </summary>
+ protected Value() { }
+
+ }
+
+ public class Int : Value
+ {
+ private static readonly AIType! inttype = new Int();
+ public static AIType! Type { get { return inttype; } }
+
+ private static readonly AIType! unaryinttype = new FunctionType(Type, Type);
+ private static readonly AIType! bininttype = new FunctionType(Type, Type, Type);
+ private static readonly AIType! relationtype = new FunctionType(Type, Type, Prop.Type);
+
+ private static readonly FunctionSymbol! _negate = new FunctionSymbol("~", unaryinttype);
+ private static readonly FunctionSymbol! _add = new FunctionSymbol("+", bininttype);
+ private static readonly FunctionSymbol! _sub = new FunctionSymbol("-", bininttype);
+ private static readonly FunctionSymbol! _mul = new FunctionSymbol("*", bininttype);
+ private static readonly FunctionSymbol! _div = new FunctionSymbol("/", bininttype);
+ private static readonly FunctionSymbol! _mod = new FunctionSymbol("%", bininttype);
+ private static readonly FunctionSymbol! _atmost = new FunctionSymbol("<=", relationtype);
+ private static readonly FunctionSymbol! _less = new FunctionSymbol("<", relationtype);
+ private static readonly FunctionSymbol! _greater = new FunctionSymbol(">", relationtype);
+ private static readonly FunctionSymbol! _atleast = new FunctionSymbol(">=", relationtype);
+
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)] public static FunctionSymbol! Negate { get { return _negate; } }
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)] public static FunctionSymbol! Add { get { return _add; } }
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)] public static FunctionSymbol! Sub { get { return _sub; } }
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)] public static FunctionSymbol! Mul { get { return _mul; } }
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)] public static FunctionSymbol! Div { get { return _div; } }
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)] public static FunctionSymbol! Mod { get { return _mod; } }
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)] public static FunctionSymbol! AtMost { get { return _atmost; } }
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)] public static FunctionSymbol! Less { get { return _less; } }
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)] public static FunctionSymbol! Greater { get { return _greater; } }
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)] public static FunctionSymbol! AtLeast { get { return _atleast; } }
+
+ public static IntSymbol! Const(BigNum x)
+ {
+ // We could cache things here, but for now we don't.
+ return new IntSymbol(x);
+ }
+
+ /// <summary>
+ /// Int should not be instantiated from the outside, except perhaps in
+ /// subclasses.
+ /// </summary>
+ private Int() { }
+ }
+
+ public class Double : Value
+ {
+ private static readonly AIType! doubletype = new Double();
+ public static AIType! Type { get { return doubletype; } }
+
+ public static DoubleSymbol! Const(double x)
+ {
+ // We could cache things here, but for now we don't.
+ return new DoubleSymbol(x);
+ }
+
+ /// <summary>
+ /// Double should not be instantiated from the outside, except perhaps in
+ /// subclasses.
+ /// </summary>
+ private Double() { }
+ }
+
+ public class Bv : Value
+ {
+ private static readonly AIType! bvtype = new Bv();
+ public static AIType! Type { get { return bvtype; } }
+
+ private static readonly AIType! unaryinttype = new FunctionType(Type, Type);
+ private static readonly AIType! bininttype = new FunctionType(Type, Type, Type);
+ private static readonly AIType! relationtype = new FunctionType(Type, Type, Prop.Type);
+
+ private static readonly FunctionSymbol! _negate = new FunctionSymbol("~", unaryinttype);
+ private static readonly FunctionSymbol! _add = new FunctionSymbol("+", bininttype);
+ private static readonly FunctionSymbol! _sub = new FunctionSymbol("-", bininttype);
+ private static readonly FunctionSymbol! _mul = new FunctionSymbol("*", bininttype);
+ private static readonly FunctionSymbol! _div = new FunctionSymbol("/", bininttype);
+ private static readonly FunctionSymbol! _mod = new FunctionSymbol("%", bininttype);
+ private static readonly FunctionSymbol! _concat = new FunctionSymbol("$concat", bininttype);
+ private static readonly FunctionSymbol! _extract = new FunctionSymbol("$extract", unaryinttype);
+ private static readonly FunctionSymbol! _atmost = new FunctionSymbol("<=", relationtype);
+ private static readonly FunctionSymbol! _less = new FunctionSymbol("<", relationtype);
+ private static readonly FunctionSymbol! _greater = new FunctionSymbol(">", relationtype);
+ private static readonly FunctionSymbol! _atleast = new FunctionSymbol(">=", relationtype);
+
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)] public static FunctionSymbol! Negate { get { return _negate; } }
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)] public static FunctionSymbol! Add { get { return _add; } }
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)] public static FunctionSymbol! Sub { get { return _sub; } }
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)] public static FunctionSymbol! Mul { get { return _mul; } }
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)] public static FunctionSymbol! Div { get { return _div; } }
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)] public static FunctionSymbol! Mod { get { return _mod; } }
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)] public static FunctionSymbol! AtMost { get { return _atmost; } }
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)] public static FunctionSymbol! Less { get { return _less; } }
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)] public static FunctionSymbol! Greater { get { return _greater; } }
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)] public static FunctionSymbol! AtLeast { get { return _atleast; } }
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)] public static FunctionSymbol! Extract { get { return _extract; } }
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)] public static FunctionSymbol! Concat { get { return _concat; } }
+
+ public static BvSymbol! Const(BigNum x, int y)
+ {
+ // We could cache things here, but for now we don't.
+ return new BvSymbol(x, y);
+ }
+
+ /// <summary>
+ /// Int should not be instantiated from the outside, except perhaps in
+ /// subclasses.
+ /// </summary>
+ private Bv() { }
+ }
+
+ public class Ref : Value
+ {
+ private static readonly AIType! reftype = new Ref();
+ public static AIType! Type { get { return reftype; } }
+
+ private static readonly FunctionSymbol! _null = new FunctionSymbol("null", Type);
+
+ public static FunctionSymbol! Null { get { return _null; } }
+
+ /// <summary>
+ /// Ref should not be instantiated from the outside, except perhaps in
+ /// subclasses.
+ /// </summary>
+ private Ref() { }
+ }
+
+ public class HeapStructure : Value
+ {
+ private static readonly AIType! reftype = new HeapStructure();
+ public static AIType! Type { get { return reftype; } }
+
+
+
+ /// <summary>
+ /// HeapStructure should not be instantiated from the outside, except perhaps in
+ /// subclasses.
+ /// </summary>
+ private HeapStructure() { }
+ }
+
+ public class FieldName : Value
+ {
+ private static readonly AIType! fieldnametype = new FieldName();
+ public static AIType! Type { get { return fieldnametype; } }
+
+ private static readonly FunctionSymbol! _allocated = new FunctionSymbol("$allocated", FieldName.Type);
+ public static FunctionSymbol! Allocated { get { return _allocated; } }
+
+ /// <summary>
+ /// Is this a boolean field that monotonically goes from false to true?
+ /// </summary>
+ public static bool IsBooleanMonotonicallyWeakening(IFunctionSymbol! f)
+ {
+ return f.Equals(Allocated);
+ }
+
+ /// <summary>
+ /// FieldName should not be instantiated from the outside, except perhaps in
+ /// subclasses.
+ /// </summary>
+ private FieldName() { }
+ }
+
+ public class Heap : Value
+ {
+ private static readonly AIType! heaptype = new Heap();
+ public static AIType! Type { get { return heaptype; } }
+
+ // the types in the following, select1, select2, are hard-coded;
+ // these types may not always be appropriate
+ private static readonly FunctionSymbol! _select1 = new FunctionSymbol("sel1",
+ // Heap x FieldName -> Prop
+ new FunctionType(Type, FieldName.Type, Prop.Type)
+ );
+ public static FunctionSymbol! Select1 { get { return _select1; } }
+
+ private static readonly FunctionSymbol! _select2 = new FunctionSymbol("sel2",
+ // Heap x Ref x FieldName -> Value
+ new FunctionType(Type, Ref.Type, FieldName.Type, Value.Type)
+ );
+ public static FunctionSymbol! Select2 { get { return _select2; } }
+
+ // the types in the following, store1, store2, are hard-coded;
+ // these types may not always be appropriate
+ private static readonly FunctionSymbol! _update1 = new FunctionSymbol("upd1",
+ // Heap x FieldName x Value -> Heap
+ new FunctionType(Type, FieldName.Type, Value.Type, Type)
+ );
+ public static FunctionSymbol! Update1 { get { return _update1; } }
+
+ private static readonly FunctionSymbol! _update2 = new FunctionSymbol("upd2",
+ // Heap x Ref x FieldName x Value -> Heap
+ new FunctionType(Type, Ref.Type, FieldName.Type, Value.Type, Type)
+ );
+ public static FunctionSymbol! Update2 { get { return _update2; } }
+
+ private static readonly FunctionSymbol! _unsupportedHeapOp =
+ new FunctionSymbol("UnsupportedHeapOp",
+ // Heap x FieldName -> Prop
+ new FunctionType(Type, FieldName.Type, Prop.Type)
+ );
+ public static FunctionSymbol! UnsupportedHeapOp { get { return _unsupportedHeapOp; } }
+
+ /// <summary>
+ /// Heap should not be instantiated from the outside, except perhaps in
+ /// subclasses.
+ /// </summary>
+ private Heap() { }
+ }
+
+// public class List : Value
+// {
+// private static IDictionary/*<AIType!,AIType!>*/! lists = new Hashtable();
+// public static AIType! Type(AIType! typeParameter)
+// {
+// if (lists.Contains(typeParameter))
+// return lists[typeParameter];
+// else
+// {
+// AIType! result = new List(typeParameter);
+// lists[typeParameter] = result;
+// return result;
+// }
+// }
+//
+// private static IDictionary/*<AIType!,AIType!>*/! nils = new Hashtable();
+// public static FunctionSymbol! Nil(AIType! typeParameter)
+// {
+// if (nils.Contains(typeParameter))
+// return nils[typeParameter];
+// else
+// {
+// FunctionSymbol! result = new FunctionSymbol(Type(typeParameter));
+// nils[typeParameter] = result;
+// return result;
+// }
+// }
+//
+// private static IDictionary/*<AIType!,AIType!>*/! cons = new Hashtable();
+// public static FunctionSymbol! Cons(AIType! typeParameter)
+// {
+// if (cons.Contains(typeParameter))
+// return cons[typeParameter];
+// else
+// {
+// FunctionSymbol! result = new FunctionSymbol(
+// new FunctionType(typeParameter, Type(typeParameter), Type(typeParameter))
+// );
+// cons[typeParameter] = result;
+// return result;
+// }
+// }
+//
+// private AIType! typeParameter;
+// public AIType! TypeParameter { get { return typeParameter; } }
+//
+// /// <summary>
+// /// List should not be instantiated from the outside.
+// /// </summary>
+// private List(AIType! typeParameter)
+// {
+// this.typeParameter = typeParameter;
+// }
+// }
+//
+// public class Pair : Value
+// {
+// private static IDictionary! pairs = new Hashtable();
+// public static AIType! Type(AIType! type1, AIType! type2)
+// {
+// Microsoft.AbstractInterpretationFramework.Collections.Pair typpair
+// = new Microsoft.AbstractInterpretationFramework.Collections.Pair(type1, type2);
+//
+// if (pairs.Contains(typpair))
+// return pairs[typpair];
+// else
+// {
+// AIType! result = new Pair(type1, type2);
+// pairs[typpair] = result;
+// return result;
+// }
+// }
+//
+// private static IDictionary! constructs = new Hashtable();
+// public static FunctionSymbol! Pair(AIType! type1, AIType! type2)
+// {
+// Microsoft.AbstractInterpretationFramework.Collections.Pair typpair
+// = new Microsoft.AbstractInterpretationFramework.Collections.Pair(type1, type2);
+//
+// if (constructs.Contains(typpair))
+// return constructs[typpair];
+// else
+// {
+// FunctionSymbol! result = new FunctionSymbol(
+// new FunctionType(type1, type2, Type(type1, type2))
+// );
+// constructs[typpair] = result;
+// return result;
+// }
+// }
+//
+// protected AIType! type1;
+// protected AIType! type2;
+//
+// public AIType! Type1 { get { return type1; } }
+// public AIType! Type2 { get { return type2; } }
+//
+// /// <summary>
+// /// Pair should not be instantiated from the outside, except by subclasses.
+// /// </summary>
+// protected Pair(AIType! type1, AIType! type2)
+// {
+// this.type1 = type1;
+// this.type2 = type2;
+// }
+// }
+
+ //-------------------------- Propositions ---------------------------
+
+
+ /// <summary>
+ /// A class with global propositional symbols and the Prop.Type.
+ /// </summary>
+ public sealed class Prop : AIType
+ {
+ private static readonly AIType! proptype = new Prop();
+ public static AIType! Type { get { return proptype; } }
+
+ private static readonly AIType! unaryproptype = new FunctionType(Type, Type);
+ private static readonly AIType! binproptype = new FunctionType(Type, Type, Type);
+ private static readonly AIType! quantifiertype =
+ new FunctionType(new FunctionType(Value.Type, Type), Type);
+
+ private static readonly FunctionSymbol! _false = new FunctionSymbol("false", Type);
+ private static readonly FunctionSymbol! _true = new FunctionSymbol("true", Type);
+ private static readonly FunctionSymbol! _not = new FunctionSymbol("!", unaryproptype);
+ private static readonly FunctionSymbol! _and = new FunctionSymbol("/\\", binproptype);
+ private static readonly FunctionSymbol! _or = new FunctionSymbol("\\/", binproptype);
+ private static readonly FunctionSymbol! _implies = new FunctionSymbol("==>", binproptype);
+ private static readonly FunctionSymbol! _exists = new FunctionSymbol("Exists", quantifiertype);
+ private static readonly FunctionSymbol! _forall = new FunctionSymbol("Forall", quantifiertype);
+
+
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)] public static FunctionSymbol! False { get { return _false; } }
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)] public static FunctionSymbol! True { get { return _true; } }
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)] public static FunctionSymbol! Not { get { return _not; } }
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)] public static FunctionSymbol! And { [Pure] get { return _and; } }
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)] public static FunctionSymbol! Or { get { return _or; } }
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)] public static FunctionSymbol! Implies { get { return _implies; } }
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)] public static FunctionSymbol! Exists { get { return _exists; } }
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)] public static FunctionSymbol! Forall { get { return _forall; } }
+
+
+ /// <summary>
+ /// Prop should not be instantiated from the outside.
+ /// </summary>
+ private Prop() { }
+
+
+
+ //
+ // Utility Methods
+ //
+
+ public static IExpr! SimplifiedAnd(IPropExprFactory! factory, IExpr! e0, IExpr! e1)
+ {
+ IFunApp fun0 = e0 as IFunApp;
+ if (fun0 != null)
+ {
+ if (fun0.FunctionSymbol.Equals(Prop.True))
+ {
+ return e1;
+ }
+ else if (fun0.FunctionSymbol.Equals(Prop.False))
+ {
+ return e0;
+ }
+ }
+
+ IFunApp fun1 = e1 as IFunApp;
+ if (fun1 != null)
+ {
+ if (fun1.FunctionSymbol.Equals(Prop.True))
+ {
+ return e0;
+ }
+ else if (fun1.FunctionSymbol.Equals(Prop.False))
+ {
+ return e1;
+ }
+ }
+
+ return factory.And(e0, e1);
+ }
+
+ public static IExpr! SimplifiedAnd(IPropExprFactory! factory, IEnumerable/*<IExpr!>*/! exprs)
+ {
+ IExpr! result = factory.True;
+ foreach (IExpr! conjunct in exprs)
+ {
+ result = SimplifiedAnd(factory, result, conjunct);
+ }
+ return result;
+ }
+
+ public static IExpr! SimplifiedOr(IPropExprFactory! factory, IExpr! e0, IExpr! e1)
+ {
+ IFunApp fun0 = e0 as IFunApp;
+ if (fun0 != null)
+ {
+ if (fun0.FunctionSymbol.Equals(Prop.False))
+ {
+ return e1;
+ }
+ else if (fun0.FunctionSymbol.Equals(Prop.True))
+ {
+ return e0;
+ }
+ }
+
+ IFunApp fun1 = e1 as IFunApp;
+ if (fun1 != null)
+ {
+ if (fun1.FunctionSymbol.Equals(Prop.False))
+ {
+ return e0;
+ }
+ else if (fun1.FunctionSymbol.Equals(Prop.True))
+ {
+ return e1;
+ }
+ }
+
+ return factory.Or(e0, e1);
+ }
+
+ public static IExpr! SimplifiedOr(IPropExprFactory! factory, IEnumerable/*<IExpr!>*/! exprs)
+ {
+ IExpr! result = factory.False;
+ foreach (IExpr! disj in exprs)
+ {
+ result = SimplifiedOr(factory, result, disj);
+ }
+ return result;
+ }
+
+
+
+ /// <summary>
+ /// Break top-level conjuncts into a list of sub-expressions.
+ /// </summary>
+ /// <param name="e">The expression to examine.</param>
+ /// <returns>A list of conjuncts.</returns>
+ internal static IList/*<IExpr!>*/! BreakConjuncts(IExpr! e)
+ ensures forall{ IExpr sub in result; sub is IFunApp ==> !((IFunApp) sub).FunctionSymbol.Equals(Prop.And) };
+ {
+ return BreakJuncts(e, Prop.And);
+ }
+
+ /// <summary>
+ /// Break top-level disjuncts into a list of sub-expressions.
+ /// </summary>
+ /// <param name="e">The expression to examine.</param>
+ /// <returns>A list of conjuncts.</returns>
+ internal static IList/*<IExpr!>*/! BreakDisjuncts(IExpr! e)
+ ensures forall{ IExpr sub in result; sub is IFunApp ==> !((IFunApp) sub).FunctionSymbol.Equals(Prop.Or) };
+ {
+ return BreakJuncts(e, Prop.Or);
+ }
+
+ private static IList/*<IExpr!>*/! BreakJuncts(IExpr! e, IFunctionSymbol! sym)
+ ensures forall{ IExpr sub in result; sub is IFunApp ==> !((IFunApp) sub).FunctionSymbol.Equals(sym) };
+ {
+ ArrayList/*<IExpr!>*/! result = new ArrayList();
+
+ IFunApp f = e as IFunApp;
+ if (f != null)
+ {
+ // If it is a sym, go down into sub-expressions.
+ if (f.FunctionSymbol.Equals(sym))
+ {
+ foreach (IExpr! arg in f.Arguments)
+ {
+ result.AddRange(BreakJuncts(arg,sym));
+ }
+ }
+ // Otherwise, stop.
+ else
+ {
+ result.Add(e);
+ }
+ }
+ else
+ {
+ result.Add(e);
+ }
+
+ return result;
+ }
+ }
+
+ /// <summary>
+ /// A callback to produce a function body given the bound variable.
+ /// </summary>
+ /// <param name="var">The bound variable to use.</param>
+ /// <returns>The function body.</returns>
+ public delegate IExpr! FunctionBody(IVariable! var);
+
+ /// <summary>
+ /// An interface for constructing propositional expressions.
+ ///
+ /// This interface should be implemented by the client. An implementation of
+ /// of this class should generally be used as a singleton object.
+ /// </summary>
+ public interface IPropExprFactory
+ {
+ IFunApp! False { get /*ensures result.FunctionSymbol.Equals(Prop.False);*/; }
+ IFunApp! True { get /*ensures result.FunctionSymbol.Equals(Prop.True);*/; }
+
+ IFunApp! Not(IExpr! p) /*ensures result.FunctionSymbol.Equals(Prop.Not);*/;
+
+ IFunApp! And(IExpr! p, IExpr! q) /*ensures result.FunctionSymbol.Equals(Prop.And);*/;
+ IFunApp! Or(IExpr! p, IExpr! q) /*ensures result.FunctionSymbol.Equals(Prop.Or);*/;
+
+ IFunApp! Implies(IExpr! p, IExpr! q) /*ensures result.FunctionSymbol.Equals(Prop.Implies);*/;
+ }
+
+ /// <summary>
+ /// Like IPropExprFactory, but also with quantifiers.
+ /// </summary>
+ public interface IQuantPropExprFactory : IPropExprFactory {
+ /// <summary>
+ /// Produce an existential given the lambda-expression.
+ /// </summary>
+ /// <param name="p">The lambda-expression.</param>
+ /// <returns>The existential.</returns>
+ IFunApp! Exists(IFunction! p) /*ensures result.FunctionSymbol.Equals(Prop.Exists);*/;
+ IFunApp! Forall(IFunction! p) /*ensures result.FunctionSymbol.Equals(Prop.Forall);*/;
+
+ /// <summary>
+ /// Produce an existential given a callback that can produce a function body given the
+ /// bound variable to use. The implementer of this method is responsible for generating
+ /// a fresh new variable to pass to the FunctionBody callback to use as the bound variable.
+ /// </summary>
+ /// <param name="body">The function body callback.</param>
+ /// <returns>The existential.</returns>
+ IFunApp! Exists(AIType paramType, FunctionBody! body) /*ensures result.FunctionSymbol.Equals(Prop.Exists);*/;
+ IFunApp! Forall(AIType paramType, FunctionBody! body) /*ensures result.FunctionSymbol.Equals(Prop.Forall);*/;
+ }
+
+ /// <summary>
+ /// An interface for constructing value expressions.
+ ///
+ /// This interface should be implemented by the client. An implementation of
+ /// of this class should generally be used as a singleton object.
+ /// </summary>
+ public interface IValueExprFactory
+ {
+ IFunApp! Eq(IExpr! e0, IExpr! e1) /*ensures result.FunctionSymbol.Equals(Value.Eq);*/;
+ IFunApp! Neq(IExpr! e0, IExpr! e1) /*ensures result.FunctionSymbol.Equals(Value.Neq);*/;
+ }
+
+ /// <summary>
+ /// An interface for constructing value expressions having to with null.
+ ///
+ /// This interface should be implemented by the client. An implementation of
+ /// of this class should generally be used as a singleton object.
+ /// </summary>
+ public interface INullnessFactory
+ {
+ IFunApp! Eq(IExpr! e0, IExpr! e1) /*ensures result.FunctionSymbol.Equals(Value.Eq);*/;
+ IFunApp! Neq(IExpr! e0, IExpr! e1) /*ensures result.FunctionSymbol.Equals(Value.Neq);*/;
+ IFunApp! Null { get; /*ensures result.FunctionSymbol.Equals(Ref.Null);*/ }
+ }
+
+ /// <summary>
+ /// An interface for constructing integer expressions.
+ ///
+ /// This interface should be implemented by the client. An implementation of
+ /// of this class should generally be used as a singleton object.
+ /// </summary>
+ public interface IIntExprFactory : IValueExprFactory
+ {
+ IFunApp! Const(BigNum i) /*ensures result.FunctionSymbol.Equals(new IntSymbol(i));*/;
+ }
+
+ /// <summary>
+ /// An interface for constructing linear integer expressions.
+ ///
+ /// This interface should be implemented by the client. An implementation of
+ /// of this class should generally be used as a singleton object.
+ /// </summary>
+ public interface ILinearExprFactory : IIntExprFactory
+ {
+ IFunApp! AtMost(IExpr! e0, IExpr! e1) /*ensures result.FunctionSymbol.Equals(Value.AtMost);*/;
+ IFunApp! Add(IExpr! e0, IExpr! e1) /*ensures result.FunctionSymbol.Equals(Value.Add);*/;
+ /// <summary>
+ /// If "var" is null, returns an expression representing r.
+ /// Otherwise, returns an expression representing r*var.
+ /// </summary>
+ IExpr! Term(Microsoft.Basetypes.Rational r, IVariable var);
+
+ IFunApp! False { get /*ensures result.FunctionSymbol.Equals(Prop.False);*/; }
+ IFunApp! True { get /*ensures result.FunctionSymbol.Equals(Prop.True);*/; }
+ IFunApp! And(IExpr! p, IExpr! q) /*ensures result.FunctionSymbol.Equals(Prop.And);*/;
+ }
+
+ /// <summary>
+ /// An interface for constructing type expressions and performing some type operations.
+ /// The types are assumed to be arranged in a rooted tree.
+ ///
+ /// This interface should be implemented by the client. An implementation of
+ /// of this class should generally be used as a singleton object.
+ /// </summary>
+ public interface ITypeExprFactory
+ {
+ /// <summary>
+ /// Returns an expression denoting the top of the type hierarchy.
+ /// </summary>
+ IExpr! RootType { get; }
+
+ /// <summary>
+ /// Returns true iff "t" denotes a type constant.
+ /// </summary>
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ bool IsTypeConstant(IExpr! t);
+
+ /// <summary>
+ /// Returns true iff t0 and t1 are types such that t0 and t1 are equal.
+ /// </summary>
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ bool IsTypeEqual(IExpr! t0, IExpr! t1);
+
+ /// <summary>
+ /// Returns true iff t0 and t1 are types such that t0 is a subtype of t1.
+ /// </summary>
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ bool IsSubType(IExpr! t0, IExpr! t1);
+
+ /// <summary>
+ /// Returns the most derived supertype of both "t0" and "t1". A precondition is
+ /// that "t0" and "t1" both represent types.
+ /// </summary>
+ IExpr! JoinTypes(IExpr! t0, IExpr! t1);
+
+ IFunApp! IsExactlyA(IExpr! e, IExpr! type) /*requires IsTypeConstant(type); ensures result.FunctionSymbol.Equals(Value.Eq);*/;
+ IFunApp! IsA(IExpr! e, IExpr! type) /*requires IsTypeConstant(type); ensures result.FunctionSymbol.Equals(Value.Subtype);*/;
+ }
+
+}
diff --git a/Source/AIFramework/Expr.ssc b/Source/AIFramework/Expr.ssc
new file mode 100644
index 00000000..94dc4dc7
--- /dev/null
+++ b/Source/AIFramework/Expr.ssc
@@ -0,0 +1,447 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+// This file specifies the expression language used by the Abstract
+// Interpretation Framework.
+//
+// expressions e ::= x variables
+// | f(e1,...,en) uninterpreted functions
+// | \x:t.e lambda expressions
+//
+// types t ::= b user-defined/built-in base types
+// | t1 * ... * tn -> t' function type
+
+namespace Microsoft.AbstractInterpretationFramework
+{
+ using System.Collections;
+ using Microsoft.Contracts;
+
+ //----------------------------- Expressions -----------------------------
+
+ /// <summary>
+ /// An interface for expressions. This expression language is specified
+ /// by interfaces to allow the client to be able to use their existing
+ /// AST nodes as AIF expressions.
+ ///
+ /// This only serves as a place for operations on expressions. Clients
+ /// should implement directly either IVariable or IFunApp.
+ /// </summary>
+ public interface IExpr
+ {
+ /// <summary>
+ /// Execute a visit over the expression.
+ /// </summary>
+ /// <param name="visitor">The expression visitor.</param>
+ /// <returns>The result of the visit.</returns>
+ [Pure][Reads(ReadsAttribute.Reads.Owned)] object DoVisit(ExprVisitor! visitor);
+
+ // TODO: Type checking of the expressions.
+ }
+
+ /// <summary>
+ /// An interface for variables.
+ ///
+ /// This interface should be implemented by the client.
+ /// </summary>
+ public interface IVariable : IExpr
+ {
+ string! Name { get; } // Each client must define the name for variables
+ }
+
+ /// <summary>
+ /// An interface for function applications.
+ ///
+ /// This interface should be implemented by the client.
+ /// </summary>
+ public interface IFunApp : IExpr
+ {
+ IFunctionSymbol! FunctionSymbol { [Pure][Reads(ReadsAttribute.Reads.Owned)] get; }
+ IList/*<IExpr!>*/! Arguments
+ {
+ [Pure][Reads(ReadsAttribute.Reads.Owned)][Rep] get
+ ensures result.IsReadOnly;
+ ;
+ }
+
+ /// <summary>
+ /// Provides a method to create a new uninterpreted function
+ /// with the same function symbol but with the arguments with
+ /// args.
+ /// </summary>
+ /// <param name="args">The new arguments.</param>
+ /// <returns>A copy of the function with the new arguments.</returns>
+ IFunApp! CloneWithArguments(IList/*<IExpr!>*/! args)
+ //TODO requires this.Arguments.Count == args.Count;
+ ;
+ }
+
+ /// <summary>
+ /// An interface for anonymous functions (i.e., lambda expressions)
+ /// </summary>
+ public interface IFunction : IExpr
+ {
+ IVariable! Param { [Pure][Reads(ReadsAttribute.Reads.Owned)] get; }
+ AIType! ParamType { [Pure][Reads(ReadsAttribute.Reads.Owned)] get; }
+ IExpr! Body { [Pure][Reads(ReadsAttribute.Reads.Owned)] get; }
+
+ IFunction! CloneWithBody(IExpr! body);
+ }
+
+ /// <summary>
+ /// An abstract class that provides an interface for expression visitors.
+ /// </summary>
+ public abstract class ExprVisitor
+ {
+ public abstract object Default(IExpr! expr);
+
+ public virtual object VisitVariable(IVariable! var)
+ {
+ return Default(var);
+ }
+
+ public virtual object VisitFunApp(IFunApp! funapp)
+ {
+ return Default(funapp);
+ }
+
+ public virtual object VisitFunction(IFunction! fun)
+ {
+ return Default(fun);
+ }
+ }
+
+ /// <summary>
+ /// A utility class for dealing with expressions.
+ /// </summary>
+ public sealed class ExprUtil
+ {
+ /// <summary>
+ /// Yield an expression that is 'inexpr' with 'var' replaced by 'subst'.
+ /// </summary>
+ /// <param name="subst">The expression to substitute.</param>
+ /// <param name="var">The variable to substitute for.</param>
+ /// <param name="inexpr">The expression to substitute into.</param>
+ public static IExpr! Substitute(IExpr! subst, IVariable! var, IExpr! inexpr)
+ {
+ IExpr result = null;
+
+ if (inexpr is IVariable)
+ {
+ result = inexpr.Equals(var) ? subst : inexpr;
+ }
+ else if (inexpr is IFunApp)
+ {
+ IFunApp! funapp = (IFunApp!)inexpr;
+ IList newargs = null;
+ newargs = new ArrayList{ IExpr! arg in funapp.Arguments; Substitute(subst, var, arg) };
+ result = funapp.CloneWithArguments(newargs);
+ }
+ else if (inexpr is IFunction)
+ {
+ IFunction! fun = (IFunction!)inexpr;
+
+ if (fun.Param.Equals(var))
+ result = fun;
+ else
+ result = fun.CloneWithBody(Substitute(subst, var, fun.Body));
+ }
+ else
+ {
+ assert false;
+ }
+
+ return result;
+ }
+
+
+ //
+ // Poor man's pattern matching.
+ //
+ // The methods below implement pattern matching for AI expressions.
+ //
+ // Example Usage:
+ // Match(e, Prop.Imp,
+ // (Matcher)delegate (IExpr e) { return Match(e, Prop.And, out x, out y); }
+ // out z)
+ // which sees if 'e' matches Prop.Imp(Prop.And(x,y),z) binding x,y,z to the subtrees.
+ //
+ public delegate bool Matcher(IExpr! expr);
+
+ private static IFunApp/*?*/ MatchFunctionSymbol(IExpr! expr, IFunctionSymbol! f)
+ {
+ IFunApp app = expr as IFunApp;
+ if (app != null)
+ {
+ if (app.FunctionSymbol.Equals(f))
+ return app;
+ else
+ return null;
+ }
+ else
+ return null;
+ }
+
+ public static bool Match(IExpr! expr, IFunctionSymbol! f, params Matcher[]! subs)
+ {
+ IFunApp app = MatchFunctionSymbol(expr,f);
+ if (app != null)
+ {
+ int i = 0; // Note ***0***
+ foreach (Matcher! s in subs)
+ {
+ if (!s((IExpr!)app.Arguments[i])) { return false; }
+ i++;
+ }
+ return true;
+ }
+ else { return false; }
+ }
+
+ // Unary Binding
+ public static bool Match(IExpr! expr, IFunctionSymbol! f, out IExpr arg0, params Matcher[]! subs)
+ {
+ arg0 = null;
+
+ IFunApp app = MatchFunctionSymbol(expr,f);
+ if (app != null)
+ {
+ arg0 = (IExpr!)app.Arguments[0];
+
+ int i = 1; // Note ***1***
+ foreach (Matcher! s in subs)
+ {
+ if (!s((IExpr!)app.Arguments[i])) { return false; }
+ i++;
+ }
+ return true;
+ }
+ else { return false; }
+ }
+
+ // Binary Binding
+ public static bool Match(IExpr! expr, IFunctionSymbol! f, Matcher! sub0, out IExpr arg1, params Matcher[]! subs)
+ {
+ arg1 = null;
+
+ IFunApp app = MatchFunctionSymbol(expr,f);
+ if (app != null)
+ {
+ if (!sub0((IExpr!)app.Arguments[0])) { return false; }
+
+ arg1 = (IExpr!)app.Arguments[1];
+
+ int i = 2; // Note ***2***
+ foreach (Matcher! s in subs)
+ {
+ if (!s((IExpr!)app.Arguments[i])) { return false; }
+ i++;
+ }
+ return true;
+ }
+ else { return false; }
+ }
+
+ public static bool Match(IExpr! expr, IFunctionSymbol! f, out IExpr arg0, out IExpr arg1, params Matcher[]! subs)
+ {
+ arg0 = null;
+ arg1 = null;
+
+ IFunApp app = MatchFunctionSymbol(expr,f);
+ if (app != null)
+ {
+ arg0 = (IExpr!)app.Arguments[0];
+ arg1 = (IExpr!)app.Arguments[1];
+
+ int i = 2; // Note ***2***
+ foreach (Matcher! s in subs)
+ {
+ if (!s((IExpr!)app.Arguments[i])) { return false; }
+ i++;
+ }
+ return true;
+ }
+ else { return false; }
+ }
+
+ // Ternary Binding
+ public static bool Match(IExpr! expr, IFunctionSymbol! f, out IExpr arg0, out IExpr arg1, out IExpr arg2, params Matcher[]! subs)
+ {
+ arg0 = null;
+ arg1 = null;
+ arg2 = null;
+
+ IFunApp app = MatchFunctionSymbol(expr,f);
+ if (app != null)
+ {
+ arg0 = (IExpr!)app.Arguments[0];
+ arg1 = (IExpr!)app.Arguments[1];
+ arg2 = (IExpr!)app.Arguments[2];
+
+ int i = 3; // Note ***3***
+ foreach (Matcher! s in subs)
+ {
+ if (!s((IExpr!)app.Arguments[i])) { return false; }
+ i++;
+ }
+ return true;
+ }
+ else { return false; }
+ }
+
+ /// <summary>
+ /// Not intended to be instantiated.
+ /// </summary>
+ private ExprUtil() { }
+ }
+
+ //------------------------------ Symbols --------------------------------
+
+ /// <summary>
+ /// An interface for function symbols. Constants are represented by
+ /// 0-ary function symbols.
+ ///
+ /// This interface should be implemented by abstract domains, but client
+ /// expressions need keep track of function symbols.
+ /// </summary>
+ public interface IFunctionSymbol
+ {
+ AIType! AIType { [Pure][Reads(ReadsAttribute.Reads.Owned)][Rep][ResultNotNewlyAllocated]
+ get; }
+ }
+
+ /// <summary>
+ /// The type of the arguments to ExprUtil.Match, a poor man's pattern
+ /// matching.
+ /// </summary>
+ public interface IMatchable
+ {
+ }
+
+ //-------------------------------- Types --------------------------------
+
+ /// <summary>
+ /// Types.
+ /// </summary>
+ public interface AIType
+ {
+ }
+
+ /// <summary>
+ /// Function type constructor.
+ /// </summary>
+ public sealed class FunctionType : AIType
+ {
+ /*[Own]*/ private readonly IList/*<Type!>*/! argTypes;
+ /*[Own]*/ private readonly AIType! retType;
+
+ public FunctionType(params AIType[]! types)
+ requires types.Length >= 2;
+ {
+ AIType type = types[types.Length-1];
+ assume type != null;
+ this.retType = type;
+ ArrayList argTypes = new ArrayList();
+ for (int i = 0; i < types.Length-1; i++)
+ {
+ type = types[i];
+ assume type != null;
+ argTypes.Add(types);
+ }
+ this.argTypes = ArrayList.ReadOnly(argTypes);
+ }
+
+ public IList/*<AIType!>*/! Arguments
+ {
+ [Pure][Reads(ReadsAttribute.Reads.Owned)][Rep]
+ get
+ ensures result.IsReadOnly;
+ {
+ return argTypes;
+ }
+ }
+
+ public int Arity
+ {
+ [Pure][Reads(ReadsAttribute.Reads.Owned)] get { return argTypes.Count; }
+ }
+
+ public AIType! ReturnType
+ {
+ [Pure][Reads(ReadsAttribute.Reads.Owned)] get { return retType; }
+ }
+
+ /* TODO Do we have the invariant that two functions are equal iff they're the same object.
+ public override bool Equals(object o)
+ {
+ if (o != null && o is FunctionType)
+ {
+ FunctionType other = (FunctionType) o;
+
+ if (Arity == other.Arity
+ && ReturnType.Equals(other.ReturnType))
+ {
+ for (int i = 0; i < Arity; i++)
+ {
+ if (!argTypes[i].Equals(other.argTypes[i]))
+ return false;
+ }
+ return true;
+ }
+ else
+ return false;
+ }
+ else
+ return false;
+ }
+ */
+ }
+
+ //------------------------------ Queries -------------------------------
+
+ public enum Answer { Yes, No, Maybe };
+
+ /// <summary>
+ /// An interface that specifies a queryable object that can answer
+ /// whether a predicate holds.
+ /// </summary>
+ public interface IQueryable
+ {
+ /// <summary>
+ /// Answers the query whether the given predicate holds.
+ /// </summary>
+ /// <param name="pred">The given predicate.</param>
+ /// <returns>Yes, No, or Maybe.</returns>
+ Answer CheckPredicate(IExpr! pred);
+
+ /// <summary>
+ /// A simplified interface for disequalities. One can always
+ /// implement this by calling CheckPredicate, but it may be
+ /// more efficient with this method.
+ /// </summary>
+ Answer CheckVariableDisequality(IVariable! var1, IVariable! var2);
+ }
+
+ public static class QueryUtil
+ {
+ public static Answer Negate(Answer ans)
+ {
+ switch (ans)
+ {
+ case Answer.Yes:
+ return Answer.No;
+ case Answer.No:
+ return Answer.Yes;
+ default:
+ return Answer.Maybe;
+ }
+ }
+ }
+
+ //----------------------------- Exceptions -----------------------------
+
+ public class TypeError : CheckedException
+ {
+ }
+}
diff --git a/Source/AIFramework/Functional.ssc b/Source/AIFramework/Functional.ssc
new file mode 100644
index 00000000..4c4a5791
--- /dev/null
+++ b/Source/AIFramework/Functional.ssc
@@ -0,0 +1,284 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using Microsoft.Contracts;
+
+namespace Microsoft.AbstractInterpretationFramework.Collections
+{
+ using System.Collections;
+
+ /// <summary>Represents a functional collection of key/value pairs.</summary>
+ /// <filterpriority>2</filterpriority>
+ public interface IFunctionalMap : System.Collections.ICollection, System.Collections.IEnumerable
+ {
+ /// <summary>Adds an element with the provided key and value to the <see cref="T:Microsoft.AbstractInterpretationFramework.Collections.IFunctionalMap" />.</summary>
+ /// <param name="value">The <see cref="T:System.Object" /> to use as the value of the element to add. </param>
+ /// <param name="key">The <see cref="T:System.Object" /> to use as the key of the element to add. </param>
+ /// <filterpriority>2</filterpriority>
+ IFunctionalMap! Add(object! key, object value);
+
+ /// <summary>
+ /// Set the value of the key (that is already in the map)
+ /// </summary>
+ IFunctionalMap! Set(object! key, object value);
+
+ /// <summary>Determines whether the <see cref="T:Microsoft.AbstractInterpretationFramework.Collections.IFunctionalMap" /> contains an element with the specified key.</summary>
+ /// <returns>true if the <see cref="T:Microsoft.AbstractInterpretationFramework.Collections.IFunctionalMap" /> contains an element with the key; otherwise, false.</returns>
+ /// <param name="key">The key to locate in the <see cref="T:Microsoft.AbstractInterpretationFramework.Collections.IFunctionalMap" />. </param>
+ /// <filterpriority>2</filterpriority>
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ bool Contains(object! key);
+
+ /// <summary>Returns an <see cref="T:System.Collections.IDictionaryEnumerator" /> for the <see cref="T:Microsoft.AbstractInterpretationFramework.Collections.IFunctionalMap" />.</summary>
+ /// <returns>An <see cref="T:System.Collections.IDictionaryEnumerator" /> for the <see cref="T:Microsoft.AbstractInterpretationFramework.Collections.IFunctionalMap" />.</returns>
+ /// <filterpriority>2</filterpriority>
+ [Pure] [GlobalAccess(false)] [Escapes(true,false)]
+ new System.Collections.IDictionaryEnumerator GetEnumerator();
+
+ /// <summary>Gets an <see cref="T:System.Collections.ICollection" /> containing the keys of the <see cref="T:Microsoft.AbstractInterpretationFramework.Collections.IFunctionalMap" />.</summary>
+ /// <returns>An <see cref="T:System.Collections.ICollection" /> containing the keys of the <see cref="T:Microsoft.AbstractInterpretationFramework.Collections.IFunctionalMap" />.</returns>
+ /// <filterpriority>2</filterpriority>
+ System.Collections.ICollection Keys { get; }
+
+ /// <summary>Removes the element with the specified key from the <see cref="T:Microsoft.AbstractInterpretationFramework.Collections.IFunctionalMap" />.</summary>
+ /// <param name="key">The key of the element to remove. </param>
+ /// <filterpriority>2</filterpriority>
+ IFunctionalMap! Remove(object! key);
+
+ /// <summary>Gets an <see cref="T:System.Collections.ICollection" /> containing the values in the <see cref="T:Microsoft.AbstractInterpretationFramework.Collections.IFunctionalMap" />.</summary>
+ /// <returns>An <see cref="T:System.Collections.ICollection" /> containing the values in the <see cref="T:Microsoft.AbstractInterpretationFramework.Collections.IFunctionalMap" />.</returns>
+ /// <filterpriority>2</filterpriority>
+ System.Collections.ICollection Values { get; }
+
+ object this [object! key] { get; /*set;*/ }
+ }
+
+
+
+ /// <summary>
+ /// An implementation of the
+ /// <see cref="T:Microsoft.AbstractInterpretationFramework.Collections.IFunctionalMap" />
+ /// interface with a <see cref="T:System.Collections.Hashtable" /> as the backing store.
+ /// </summary>
+ class FunctionalHashtable : IFunctionalMap
+ {
+ private readonly Hashtable! h;
+
+ /// <summary>
+ /// Cannot directly construct an instance of a FunctionalHashtbl.
+ /// </summary>
+ private FunctionalHashtable()
+ {
+ this.h = new Hashtable();
+ // base();
+ }
+
+ /// <summary>
+ /// Cannot directly construct an instance of a FunctionalHashtbl.
+ /// </summary>
+ private FunctionalHashtable(Hashtable! h)
+ {
+ this.h = h;
+ // base();
+ }
+
+ private static readonly IFunctionalMap! empty = new FunctionalHashtable();
+ public static IFunctionalMap! Empty { get { return empty; } }
+
+ public IFunctionalMap! Add(object! key, object value)
+ {
+ Hashtable r = h.Clone() as Hashtable;
+ assume r != null;
+ r.Add(key, value);
+ return new FunctionalHashtable(r);
+ }
+
+ public IFunctionalMap! Set(object! key, object value)
+ {
+ Hashtable r = h.Clone() as Hashtable;
+
+ assume r != null;
+ assert this.Contains(key); // The entry must be defined
+
+ r[key] = value;
+ return new FunctionalHashtable(r);
+ }
+
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public bool Contains(object! key)
+ {
+ return h.Contains(key);
+ }
+
+ [Pure] [GlobalAccess(false)] [Escapes(true,false)]
+ IEnumerator! IEnumerable.GetEnumerator()
+ {
+ return h.GetEnumerator();
+ }
+
+ [Pure] [GlobalAccess(false)] [Escapes(true,false)]
+ IDictionaryEnumerator IFunctionalMap.GetEnumerator()
+ {
+ return h.GetEnumerator();
+ }
+
+ public ICollection Keys
+ {
+ get { return h.Keys; }
+ }
+
+ public IFunctionalMap! Remove(object! key)
+ {
+ Hashtable r = h.Clone() as Hashtable;
+ assume r != null;
+ r.Remove(key);
+ return new FunctionalHashtable(r);
+ }
+
+ public ICollection Values
+ {
+ get { return h.Values; }
+ }
+
+
+ public object this[object! key]
+ {
+ get { return h[key]; }
+ }
+
+ public int Count
+ {
+ [Pure] get { return h.Count; }
+ }
+
+ public bool IsSynchronized
+ {
+ [Pure] get { return h.IsSynchronized; }
+ }
+
+ public object! SyncRoot
+ {
+ [Pure] get { return h.SyncRoot; }
+ }
+
+ public void CopyTo(System.Array! a, int index)
+ {
+ h.CopyTo(a, index);
+ }
+ }
+
+ public struct Pair/*<T1,T2>*/
+ {
+ private object first;
+ private object second;
+
+ public object First { get { return first; } }
+ public object Second { get { return second; } }
+
+ public Pair(object first, object second)
+ {
+ this.first = first;
+ this.second = second;
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (obj == null) return false;
+ if (!(obj is Pair)) return false;
+
+ Pair other = (Pair)obj;
+ return object.Equals(this.first, other.first) && object.Equals(this.second, other.second);
+ }
+
+ public override int GetHashCode()
+ {
+ int h = this.first == null ? 0 : this.first.GetHashCode();
+ h ^= this.second == null ? 0 : this.second.GetHashCode();
+ return h;
+ }
+ }
+}
+
+
+namespace Microsoft.AbstractInterpretationFramework.Collections.Generic
+{
+ using System.Collections.Generic;
+
+ public struct Pair<T1,T2>
+ {
+ private T1 first;
+ private T2 second;
+
+ public T1 First { get { return first; } }
+ public T2 Second { get { return second; } }
+
+ public Pair(T1 first, T2 second)
+ {
+ this.first = first;
+ this.second = second;
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (obj == null) return false;
+ if (!(obj is Pair<T1,T2>)) return false;
+
+ Pair<T1,T2> other = (Pair<T1,T2>)obj;
+ return object.Equals(this.first, other.first) && object.Equals(this.second, other.second);
+ }
+
+ public override int GetHashCode()
+ {
+ int h = this.first == null ? 0 : this.first.GetHashCode();
+ h ^= this.second == null ? 0 : this.second.GetHashCode();
+ return h;
+ }
+
+ public override string! ToString()
+ {
+ return string.Format("({0},{1})", first, second);
+ }
+ }
+
+ public struct Triple<T1,T2,T3>
+ {
+ private T1 first;
+ private T2 second;
+ private T3 third;
+
+ public T1 First { get { return first; } }
+ public T2 Second { get { return second; } }
+ public T3 Third { get { return third; } }
+
+ public Triple(T1 first, T2 second, T3 third)
+ {
+ this.first = first;
+ this.second = second;
+ this.third = third;
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (obj == null) return false;
+ if (!(obj is Triple<T1,T2,T3>)) return false;
+
+ Triple<T1,T2,T3> other = (Triple<T1,T2,T3>)obj;
+ return object.Equals(this.first, other.first) && object.Equals(this.second, other.second) && object.Equals(this.third, other.third);
+ }
+
+ public override int GetHashCode()
+ {
+ int h = this.first == null ? 0 : this.first.GetHashCode();
+ h ^= this.second == null ? 0 : this.second.GetHashCode();
+ h ^= this.third == null ? 0 : this.third.GetHashCode();
+ return h;
+ }
+
+ public override string! ToString()
+ {
+ return string.Format("({0},{1},{2})", first, second, third);
+ }
+ }
+}
diff --git a/Source/AIFramework/Lattice.ssc b/Source/AIFramework/Lattice.ssc
new file mode 100644
index 00000000..2b606492
--- /dev/null
+++ b/Source/AIFramework/Lattice.ssc
@@ -0,0 +1,685 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+namespace Microsoft.AbstractInterpretationFramework
+{
+ using Microsoft.Contracts;
+ using System.Collections;
+ using G = System.Collections.Generic;
+ using System.Diagnostics;
+ using Microsoft.AbstractInterpretationFramework.Collections;
+ using Microsoft.Boogie;
+ using IMutableSet = Microsoft.Boogie.Set;
+ using ISet = Microsoft.Boogie.Set;
+ using HashSet = Microsoft.Boogie.Set;
+ using ArraySet = Microsoft.Boogie.Set;
+
+
+
+ /// <summary>
+ /// Specifies the operations (e.g., join) on a mathematical lattice that depend
+ /// only on the elements of the lattice.
+ /// </summary>
+ public abstract class MathematicalLattice
+ {
+ /// <summary>
+ /// An element of the lattice. This class should be derived from in any
+ /// implementation of MathematicalLattice.
+ /// </summary>
+ public abstract class Element : System.ICloneable {
+ /// <summary>
+ /// Print out a debug-useful representation of the internal data structure of the lattice element.
+ /// </summary>
+ public virtual void Dump(string! msg) {
+ System.Console.WriteLine("Dump({0}) = {1}", msg, this);
+ }
+
+ public abstract Element! Clone();
+ object! System.ICloneable.Clone() { return this.Clone(); }
+
+ public abstract G.ICollection<IVariable!>! FreeVariables()
+ ensures result.IsReadOnly;
+ }
+
+ public abstract Element! Top { get; }
+ public abstract Element! Bottom { get; }
+
+ public abstract bool IsTop(Element! e);
+ public abstract bool IsBottom(Element! e);
+
+ /// <summary>
+ /// Returns true if a &lt;= this.
+ /// </summary>
+ protected abstract bool AtMost(Element! a, Element! b)
+ /* The following cases are handled elsewhere and need not be considered in subclass. */
+ // requires a.GetType() == b.GetType();
+ // requires ! a.IsTop;
+ // requires ! a.IsBottom;
+ // requires ! b.IsTop;
+ // requires ! b.IsBottom;
+ ;
+
+ protected Answer TrivialLowerThan(Element! a, Element! b)
+ {
+ if (a.GetType() != b.GetType())
+ {
+ throw new System.InvalidOperationException(
+ "operands to <= must be of same Element type"
+ );
+ }
+ if (IsBottom(a)) { return Answer.Yes; }
+ if (IsTop(b)) { return Answer.Yes; }
+ if (IsTop(a)) { return Answer.No; }
+ if (IsBottom(b)) { return Answer.No; }
+
+ return Answer.Maybe;
+ }
+
+ // Is 'a' better information than 'b'?
+ //
+ public bool LowerThan(Element! a, Element! b)
+ {
+ Answer ans = TrivialLowerThan(a,b);
+ return ans != Answer.Maybe ? ans == Answer.Yes : AtMost(a, b);
+ }
+
+ // Is 'a' worse information than 'b'?
+ //
+ public bool HigherThan(Element! a, Element! b)
+ {
+ return LowerThan(b, a);
+ }
+
+ // Are 'a' and 'b' equivalent?
+ //
+ public bool Equivalent(Element! a, Element! b)
+ {
+ return LowerThan(a, b) && LowerThan(b, a);
+ }
+
+ public abstract Element! NontrivialJoin(Element! a, Element! b)
+ /* The following cases are handled elsewhere and need not be considered in subclass. */
+ // requires a.GetType() == b.GetType();
+ // requires ! a.IsTop;
+ // requires ! a.IsBottom;
+ // requires ! b.IsTop;
+ // requires ! b.IsBottom;
+ ;
+
+ protected Element/*?*/ TrivialJoin(Element! a, Element! b)
+ {
+ if (a.GetType() != b.GetType())
+ {
+ throw new System.InvalidOperationException(
+ "operands to Join must be of same Lattice.Element type"
+ );
+ }
+ if (IsTop(a)) { return a; }
+ if (IsTop(b)) { return b; }
+ if (IsBottom(a)) { return b; }
+ if (IsBottom(b)) { return a; }
+
+ return null;
+ }
+
+ public Element! Join(Element! a, Element! b)
+ {
+ Element/*?*/ r = TrivialJoin(a,b);
+ return r != null ? r : NontrivialJoin(a, b);
+ }
+
+ public abstract Element! NontrivialMeet(Element! a, Element! b)
+ /* The following cases are handled elsewhere and need not be considered in subclass. */
+ // requires a.GetType() == b.GetType();
+ // requires ! a.IsTop;
+ // requires ! a.IsBottom;
+ // requires ! b.IsTop;
+ // requires ! b.IsBottom;
+ ;
+
+ protected Element/*?*/ TrivialMeet(Element! a, Element! b)
+ {
+ if (a.GetType() != b.GetType())
+ {
+ throw new System.InvalidOperationException(
+ "operands to Meet must be of same Lattice.Element type"
+ );
+ }
+ if (IsTop(a)) { return b; }
+ if (IsTop(b)) { return a; }
+ if (IsBottom(a)) { return a; }
+ if (IsBottom(b)) { return b; }
+
+ return null;
+ }
+
+ public Element! Meet(Element! a, Element! b)
+ {
+ Element/*?*/ r = TrivialMeet(a,b);
+ return r != null ? r : NontrivialMeet(a, b);
+ }
+
+ public abstract Element! Widen(Element! a, Element! b);
+
+ public virtual void Validate()
+ {
+ Debug.Assert(IsTop(Top));
+ Debug.Assert(IsBottom(Bottom));
+ Debug.Assert(!IsBottom(Top));
+ Debug.Assert(!IsTop(Bottom));
+
+ Debug.Assert(LowerThan(Top, Top));
+ Debug.Assert(LowerThan(Bottom, Top));
+ Debug.Assert(LowerThan(Bottom, Bottom));
+
+ Debug.Assert(IsTop(Join(Top, Top)));
+ Debug.Assert(IsBottom(Join(Bottom, Bottom)));
+ }
+ }
+
+
+ /// <summary>
+ /// Provides an abstract interface for the operations of a lattice specific
+ /// to abstract interpretation (i.e., that deals with the expression language).
+ /// </summary>
+ public abstract class Lattice : MathematicalLattice
+ {
+ internal readonly IValueExprFactory! valueExprFactory;
+
+ public Lattice(IValueExprFactory! valueExprFactory)
+ {
+ this.valueExprFactory = valueExprFactory;
+ // base();
+ }
+
+ #region Primitives that commands translate into
+
+ public abstract Element! Eliminate(Element! e, IVariable! variable);
+
+ public abstract Element! Rename(Element! e, IVariable! oldName, IVariable! newName);
+
+ public abstract Element! Constrain(Element! e, IExpr! expr);
+
+ #endregion
+
+
+// TODO keep this?
+// public Element! Eliminate(Element! e, VariableSeq! variables)
+// {
+// Lattice.Element result = e;
+// foreach (IVariable var in variables)
+// {
+// result = this.Eliminate(result, var);
+// }
+// return result;
+// }
+
+
+ //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ // Note!
+ //
+ // Concrete classes that implement Lattice must implement one of the AtMost
+ // overloads. We provide here a default implementation for one given a "real"
+ // implementation of the other. Otherwise, there will be an infinite loop!
+ //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+ protected override bool AtMost(Element! a, Element! b)
+ {
+ return AtMost(a, IdentityCombineNameMap.Map, b, IdentityCombineNameMap.Map);
+ }
+
+ protected virtual bool AtMost(Element! a, ICombineNameMap! aToResult, Element! b, ICombineNameMap! bToResult)
+ {
+ return AtMost(ApplyCombineNameMap(a,aToResult), ApplyCombineNameMap(b,bToResult));
+ }
+
+ public bool LowerThan(Element! a, ICombineNameMap! aToResult, Element! b, ICombineNameMap! bToResult)
+ {
+ Answer ans = TrivialLowerThan(a,b);
+ return ans != Answer.Maybe ? ans == Answer.Yes : AtMost(a, aToResult, b, bToResult);
+ }
+
+ public bool HigherThan(Element! a, ICombineNameMap! aToResult, Element! b, ICombineNameMap! bToResult)
+ {
+ return LowerThan(b, bToResult, a, aToResult);
+ }
+
+
+ //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ // Note!
+ //
+ // Concrete classes that implement Lattice must implement one of the NontrivialJoin
+ // overloads. We provide here a default implementation for one given a "real"
+ // implementation of the other. Otherwise, there will be an infinite loop!
+ //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+ public override Element! NontrivialJoin(Element! a, Element! b)
+ {
+ return NontrivialJoin(a, IdentityCombineNameMap.Map, b, IdentityCombineNameMap.Map);
+ }
+
+ public virtual Element! NontrivialJoin(Element! a, ICombineNameMap! aToResult, Element! b, ICombineNameMap! bToResult)
+ {
+ return NontrivialJoin(ApplyCombineNameMap(a,aToResult), ApplyCombineNameMap(b,bToResult));
+ }
+
+ public Element! Join(Element! a, ICombineNameMap! aToResult, Element! b, ICombineNameMap! bToResult)
+ {
+ Element/*?*/ r = TrivialJoin(a,b);
+ return r != null ? r : NontrivialJoin(a, aToResult, b, bToResult);
+ }
+
+
+ //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ // Note!
+ //
+ // Concrete classes that implement Lattice must implement one of the Widen
+ // overloads. We provide here a default implementation for one given a "real"
+ // implementation of the other. Otherwise, there will be an infinite loop!
+ //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+ public override Element! Widen(Element! a, Element! b)
+ {
+ return Widen(a, IdentityCombineNameMap.Map, b, IdentityCombineNameMap.Map);
+ }
+
+ public virtual Element! Widen(Element! a, ICombineNameMap! aToResult, Element! b, ICombineNameMap! bToResult)
+ {
+ return Widen(ApplyCombineNameMap(a,aToResult), ApplyCombineNameMap(b,bToResult));
+ }
+
+
+ /// <summary>
+ /// Returns the predicate that corresponds to the given lattice element.
+ /// </summary>
+ public abstract IExpr! ToPredicate(Element! e);
+
+ /// <summary>
+ /// Allows the lattice to specify whether it understands a particular function symbol.
+ ///
+ /// The lattice is always allowed to return "true" even when it really can't do anything
+ /// with such functions; however, it is advantageous to say "false" when possible to
+ /// avoid being called to do certain things.
+ ///
+ /// The arguments to a function are provided for context so that the lattice can say
+ /// true or false for the same function symbol in different situations. For example,
+ /// a lattice may understand the multiplication of a variable and a constant but not
+ /// of two variables. The implementation of a lattice should not hold on to the
+ /// arguments.
+ /// </summary>
+ /// <param name="f">The function symbol.</param>
+ /// <param name="args">The argument context.</param>
+ /// <returns>True if it may understand f, false if it does not understand f.</returns>
+ public abstract bool Understands(IFunctionSymbol! f, IList/*<IExpr!>*/! args);
+
+ /// <summary>
+ /// Return an expression that is equivalent to the given expression that does not
+ /// contain the given variable according to the lattice element and queryable.
+ /// </summary>
+ /// <param name="e">The lattice element.</param>
+ /// <param name="q">A queryable for asking addtional information.</param>
+ /// <param name="expr">The expression to find an equivalent expression.</param>
+ /// <param name="var">The variable to eliminate.</param>
+ /// <param name="prohibitedVars">The set of variables that can't be used in the resulting expression.</param>
+ /// <returns>
+ /// An equivalent expression to <paramref name="expr"/> without <paramref name="var"/>
+ /// or null if not possible.
+ /// </returns>
+ public abstract IExpr/*?*/ EquivalentExpr(Element! e, IQueryable! q, IExpr! expr, IVariable! var, Set/*<IVariable!>*/! prohibitedVars);
+
+ /// <summary>
+ /// Answers a query about whether the given predicate holds given the lattice element.
+ /// </summary>
+ /// <param name="e">The lattice element.</param>
+ /// <param name="pred">The predicate.</param>
+ /// <returns>Yes, No, or Maybe.</returns>
+ public abstract Answer CheckPredicate(Element! e, IExpr! pred);
+
+ /// <summary>
+ /// Answers a disequality about two variables. The same information could be obtained
+ /// by asking CheckPredicate, but a different implementation may be simpler and more
+ /// efficient.
+ /// </summary>
+ /// <param name="e">The lattice element.</param>
+ /// <param name="var1">The first variable.</param>
+ /// <param name="var2">The second variable.</param>
+ /// <returns>Yes, No, or Maybe.</returns>
+ public abstract Answer CheckVariableDisequality(Element! e, IVariable! var1, IVariable! var2);
+
+ /// <summary>
+ /// A default implementation of the <see cref="CheckVariableDisequality"/> given
+ /// the appropriate expression factories by calling CheckPredicate.
+ /// </summary>
+ protected Answer DefaultCheckVariableDisequality(
+ IPropExprFactory! propExprFactory, IValueExprFactory! valExprFactory,
+ Element! e, IVariable! var1, IVariable! var2)
+ {
+ return this.CheckPredicate(e, propExprFactory.Not(valExprFactory.Eq(var1, var2)));
+ }
+
+ private Element! ApplyCombineNameMap(Element! e, ICombineNameMap! eToResult)
+ {
+ Element! result = e;
+
+ foreach (G.KeyValuePair<IVariable!,ISet/*<IVariable!>*/!> entry in eToResult.GetSourceToResult())
+ {
+ IVariable! sourceName = entry.Key;
+ ISet/*<IVariable!*/! resultNames = entry.Value;
+
+ // Renaming s to r is okay if
+ // (1) s is not used in the result
+ // and (2) s has not been renamed already
+ bool renameOkay = !resultNames.Contains(sourceName);
+ IVariable! representative = sourceName;
+
+ foreach (IVariable! rname in resultNames)
+ {
+ // skip if sourceName and rname are the same
+ if (object.Equals(sourceName, rname)) { continue; }
+
+ if (renameOkay)
+ {
+ result = this.Rename(result, sourceName, rname);
+ representative = rname; // representative now rname
+ renameOkay = false; // no longer okay to rename
+ }
+ else
+ {
+ result = this.Constrain(result, valueExprFactory.Eq(representative, rname));
+ }
+ }
+ }
+
+ return result;
+ }
+
+ private sealed class IdentityCombineNameMap : ICombineNameMap
+ {
+ public static readonly IdentityCombineNameMap! Map = new IdentityCombineNameMap();
+
+ private static readonly G.Dictionary<IVariable!,ISet/*<IVariable!>*/!>! emptyDictionary1 = new G.Dictionary<IVariable!,ISet/*<IVariable!>*/!>();
+ private static readonly G.Dictionary<IVariable!,IVariable!>! emptyDictionary2 = new G.Dictionary<IVariable!,IVariable!>();
+
+ public ISet/*<IVariable!>*//*?*/ GetResultNames(IVariable! srcname)
+ {
+ ArraySet a = new ArraySet();
+ a.Add(srcname);
+ return a;
+ }
+
+ public IVariable/*?*/ GetSourceName(IVariable! resname)
+ {
+ return resname;
+ }
+
+ //TODO: uncomment when works in compiler
+ //public G.IEnumerable<G.KeyValuePair<IVariable!,ISet/*<IVariable!>*/!>> GetSourceToResult()
+ public IEnumerable! GetSourceToResult()
+ {
+ return emptyDictionary1;
+ }
+
+ //public G.IEnumerable<G.KeyValuePair<IVariable!,IVariable!>> GetResultToSource()
+ public IEnumerable! GetResultToSource()
+ {
+ return emptyDictionary2;
+ }
+
+ private IdentityCombineNameMap() { }
+ }
+
+ public abstract string! ToString(Element! e); // for debugging
+
+ #region Support for MultiLattice to uniquely number every subclass of Lattice
+
+ private static Hashtable/*<System.Type,int>*/! indexMap = new Hashtable();
+ private static Hashtable/*<int,Lattice>*/! reverseIndexMap = new Hashtable();
+ private static int globalCount = 0;
+
+ protected virtual object! UniqueId { get { return (!)this.GetType(); } }
+
+ public int Index
+ {
+ get
+ {
+ object unique = this.UniqueId;
+ if (indexMap.ContainsKey(unique))
+ {
+ object index = indexMap[unique];
+ assert index != null; // this does nothing for nonnull analysis
+ if (index != null) { return (int)index; }
+ return 0;
+ }
+ else
+ {
+ int myIndex = globalCount++;
+ indexMap[unique] = myIndex;
+ reverseIndexMap[myIndex] = this;
+ return myIndex;
+ }
+ }
+ }
+
+ public static Lattice GetGlobalLattice(int i) { return reverseIndexMap[i] as Lattice; }
+ #endregion
+
+ public static bool LogSwitch = false;
+ }
+
+
+ /// <summary>
+ /// Defines the relation between names used in the respective input lattice elements to the
+ /// various combination operators (Join,Widen,Meet,AtMost) and the names that should be used
+ /// in the resulting lattice element.
+ /// </summary>
+ public interface ICombineNameMap
+ {
+ ISet/*<IVariable!>*//*?*/ GetResultNames(IVariable! srcname);
+ IVariable/*?*/ GetSourceName(IVariable! resname);
+
+ //TODO: uncommet when works in compiler
+ //G.IEnumerable<G.KeyValuePair<IVariable!,ISet/*<IVariable!>*/!>> GetSourceToResult();
+ IEnumerable! GetSourceToResult();
+ //G.IEnumerable<G.KeyValuePair<IVariable!,IVariable!>> GetResultToSource();
+ IEnumerable! GetResultToSource();
+ }
+
+ /// <summary>
+ /// Provides statistics on the number of times an operation is performed
+ /// and forwards the real operations to the given lattice in the constructor.
+ /// </summary>
+ public class StatisticsLattice : Lattice
+ {
+ readonly Lattice! lattice;
+ int eliminateCount;
+ int renameCount;
+ int constrainCount;
+ int toPredicateCount;
+ int atMostCount;
+ int topCount;
+ int bottomCount;
+ int isTopCount;
+ int isBottomCount;
+ int joinCount;
+ int meetCount;
+ int widenCount;
+ int understandsCount;
+ int equivalentExprCount;
+ int checkPredicateCount;
+ int checkVariableDisequalityCount;
+
+ public StatisticsLattice(Lattice! lattice)
+ : base(lattice.valueExprFactory)
+ {
+ this.lattice = lattice;
+ // base(lattice.valueExprFactory);
+ }
+
+ public override Element! Eliminate(Element! e, IVariable! variable)
+ {
+ eliminateCount++;
+ return lattice.Eliminate(e, variable);
+ }
+
+ public override Element! Rename(Element! e, IVariable! oldName, IVariable! newName)
+ {
+ renameCount++;
+ return lattice.Rename(e, oldName, newName);
+ }
+
+ public override Element! Constrain(Element! e, IExpr! expr)
+ {
+ constrainCount++;
+ return lattice.Constrain(e, expr);
+ }
+
+
+ public override bool Understands(IFunctionSymbol! f, IList! args)
+ {
+ understandsCount++;
+ return lattice.Understands(f, args);
+ }
+
+
+ public override IExpr/*?*/ EquivalentExpr(Element! e, IQueryable! q, IExpr! expr, IVariable! var, ISet/*<IVariable!>*/! prohibitedVars)
+ {
+ equivalentExprCount++;
+ return lattice.EquivalentExpr(e, q, expr, var, prohibitedVars);
+ }
+
+
+ public override Answer CheckPredicate(Element! e, IExpr! pred)
+ {
+ checkPredicateCount++;
+ return lattice.CheckPredicate(e, pred);
+ }
+
+
+ public override Answer CheckVariableDisequality(Element! e, IVariable! var1, IVariable! var2)
+ {
+ checkVariableDisequalityCount++;
+ return lattice.CheckVariableDisequality(e, var1, var2);
+ }
+
+
+
+ public override IExpr! ToPredicate(Element! e)
+ {
+ toPredicateCount++;
+ return lattice.ToPredicate(e);
+ }
+
+ public override string! ToString(Element! e)
+ {
+ return lattice.ToString(e);
+ }
+
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public override string! ToString()
+ {
+ return string.Format(
+ "StatisticsLattice: #Eliminate={0} #Rename={1} #Constrain={2} #ToPredicate={3} " +
+ "#Understands={4} #EquivalentExpr={5} #CheckPredicate={6} #CheckVariableDisequality={7} " +
+ "#AtMost={8} #Top={9} #Bottom={9} #IsTop={10} #IsBottom={11} " +
+ "#NonTrivialJoin={12} #NonTrivialMeet={13} #Widen={14}",
+ eliminateCount, renameCount, constrainCount, toPredicateCount,
+ understandsCount, equivalentExprCount, checkPredicateCount, checkVariableDisequalityCount,
+ atMostCount, topCount, bottomCount, isTopCount, isBottomCount,
+ joinCount, meetCount, widenCount);
+ }
+
+ protected override bool AtMost(Element! a, Element! b)
+ {
+ atMostCount++;
+ return lattice.LowerThan(a, b);
+ }
+
+ public override Element! Top
+ {
+ get
+ {
+ topCount++;
+ return lattice.Top;
+ }
+ }
+ public override Element! Bottom
+ {
+ get
+ {
+ bottomCount++;
+ return lattice.Bottom;
+ }
+ }
+
+ public override bool IsTop(Element! e)
+ {
+ isTopCount++;
+ return lattice.IsTop(e);
+ }
+
+ public override bool IsBottom(Element! e)
+ {
+ isBottomCount++;
+ return lattice.IsBottom(e);
+ }
+
+ public override Element! NontrivialJoin(Element! a, Element! b)
+ {
+ joinCount++;
+ return lattice.NontrivialJoin(a, b);
+ }
+
+ public override Element! NontrivialMeet(Element! a, Element! b)
+ {
+ meetCount++;
+ return lattice.NontrivialMeet(a, b);
+ }
+
+ public override Element! Widen(Element! a, Element! b)
+ {
+ widenCount++;
+ return lattice.Widen(a, b);
+ }
+
+ public override void Validate()
+ {
+ base.Validate();
+ lattice.Validate();
+ }
+
+ protected override object! UniqueId
+ {
+ get
+ {
+ // use the base id, not the underlying-lattice id (is that the right thing to do?)
+ return base.UniqueId;
+ }
+ }
+ }
+
+
+ public sealed class LatticeQueryable : IQueryable
+ {
+ private Lattice! lattice;
+ private Lattice.Element! element;
+
+ public LatticeQueryable(Lattice! lattice, Lattice.Element! element)
+ {
+ this.lattice = lattice;
+ this.element = element;
+ // base();
+ }
+
+ public Answer CheckPredicate(IExpr! pred)
+ {
+ return lattice.CheckPredicate(element, pred);
+ }
+
+ public Answer CheckVariableDisequality(IVariable! var1, IVariable! var2)
+ {
+ return lattice.CheckVariableDisequality(element, var1, var2);
+ }
+ }
+}
diff --git a/Source/AIFramework/Logger.ssc b/Source/AIFramework/Logger.ssc
new file mode 100644
index 00000000..12c3ba08
--- /dev/null
+++ b/Source/AIFramework/Logger.ssc
@@ -0,0 +1,49 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+namespace Microsoft.AbstractInterpretationFramework
+{
+ using System;
+ using System.Diagnostics;
+
+ public class Logger
+ {
+ private string! dbgmsgContext;
+ private static int contextWidth = 0;
+
+ public bool Enabled = false;
+
+ public Logger(string! contextMsg)
+ {
+ this.dbgmsgContext = "[" + contextMsg + "] ";
+ contextWidth = Math.Max(contextWidth, contextMsg.Length + 3);
+ // base();
+ }
+
+ private static System.Text.StringBuilder! dbgmsgIndent = new System.Text.StringBuilder();
+ public void DbgMsgIndent() { dbgmsgIndent.Append(' ', 2); }
+ public void DbgMsgUnindent()
+ { if (dbgmsgIndent.Length >= 2) dbgmsgIndent.Remove(0,2); }
+
+ [ConditionalAttribute("DEBUG")]
+ public void DbgMsg(string msg)
+ {
+ if (Enabled)
+ Debug.WriteLine(dbgmsgContext.PadRight(contextWidth) + dbgmsgIndent + msg);
+ }
+ [ConditionalAttribute("DEBUG")]
+ public void DbgMsgNoLine(string msg)
+ {
+ if (Enabled)
+ Debug.Write(dbgmsgContext.PadRight(contextWidth) + dbgmsgIndent + msg);
+ }
+ [ConditionalAttribute("DEBUG")]
+ public void DbgMsgPlain(string msg)
+ {
+ if (Enabled)
+ Debug.Write(msg);
+ }
+ }
+}
diff --git a/Source/AIFramework/MultiLattice.ssc b/Source/AIFramework/MultiLattice.ssc
new file mode 100644
index 00000000..b6b6ba5e
--- /dev/null
+++ b/Source/AIFramework/MultiLattice.ssc
@@ -0,0 +1,563 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+namespace Microsoft.AbstractInterpretationFramework
+{
+ using Microsoft.Contracts;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using Microsoft.AbstractInterpretationFramework.Collections;
+
+ using Microsoft.Boogie;
+ using ISet = Microsoft.Boogie.Set;
+
+
+ /// <summary>
+ /// The cartesian product lattice.
+ /// </summary>
+ public class MultiLattice : Lattice, IEnumerable
+ {
+ internal class Elt : Element
+ {
+ public /*MaybeNull*/Element[] elementPerLattice;
+
+ public Elt(int domainCount, bool isBottom)
+ {
+ this.elementPerLattice = (domainCount == 0 && isBottom) ? null : new Element[domainCount];
+ }
+
+ private Elt(Elt! other)
+ {
+ Element[] otherEPL = other.elementPerLattice;
+ if (otherEPL != null)
+ {
+ Element[] newEPL = new Element[otherEPL.Length];
+ for (int i = 0; i < newEPL.Length; i++)
+ {
+ newEPL[i] = (Element) ((!)otherEPL[i]).Clone();
+ }
+ this.elementPerLattice = newEPL;
+ }
+ }
+
+ public override Element! Clone()
+ {
+ return new Elt(this);
+ }
+
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public override string! ToString()
+ {
+// string s = "MultiLattice+Elt{";
+// string sep = "";
+// Element[] epl = this.elementPerLattice;
+// if (epl != null)
+// {
+// foreach (Element! e in epl)
+// {
+// s += sep + e.ToString();
+// sep = ", ";
+// }
+// }
+// return s + "}";
+ if (elementPerLattice == null) return "";
+ System.Text.StringBuilder buffer = new System.Text.StringBuilder();
+ for (int i = 0; i < this.Count; i++)
+ {
+ if (i > 0) buffer.Append("; ");
+ buffer.AppendFormat("{0}", elementPerLattice[i]);
+ }
+ return buffer.ToString();
+ }
+
+ public override void Dump(string! msg) {
+ System.Console.WriteLine("MultiLattice.Elt.Dump({0})", msg);
+ Element[] epl = this.elementPerLattice;
+ if (epl != null) {
+ foreach (Element! e in epl) {
+ e.Dump(msg);
+ }
+ }
+ }
+
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public override ICollection<IVariable!>! FreeVariables()
+ {
+ List<IVariable!>! list = new List<IVariable!>();
+ for (int i = 0; i < this.Count; i++)
+ {
+ list.AddRange(((!)this[i]).FreeVariables());
+ }
+ return (!)list.AsReadOnly();
+ }
+
+ public static Elt! Top(ArrayList/*<Lattice>*/! lattices)
+ {
+ Elt multiValue = new Elt(lattices.Count, false);
+ for (int i = 0; i < lattices.Count; i++)
+ {
+ Lattice d = (Lattice!)lattices[i];
+ multiValue[d.Index] = d.Top;
+ }
+ Debug.Assert(multiValue.IsValid);
+ return multiValue;
+ }
+
+
+ public static Elt! Bottom(ArrayList/*<Lattice>*/! lattices)
+ {
+ Elt multiValue = new Elt(lattices.Count, true);
+ for (int i = 0; i < lattices.Count; i++)
+ {
+ Lattice d = (Lattice!)lattices[i];
+ multiValue[d.Index] = d.Bottom;
+ }
+ Debug.Assert(multiValue.IsValid);
+ return multiValue;
+ }
+
+ public bool IsValid
+ {
+ get
+ {
+ if (this.elementPerLattice == null) { return true; /*bottom*/ }
+
+ Element[] epl = this.elementPerLattice;
+ for (int i = 0; i < epl.Length; i++)
+ {
+ if (epl[i] == null) { return false; }
+ }
+ return true;
+ }
+ }
+
+ public int Count { get { return this.elementPerLattice == null ? 0 : this.elementPerLattice.Length; } }
+
+ public bool Contains(int i) { return 0 <= i && i < this.Count; }
+
+ public Element this[int i] // just syntactic sugar
+ {
+ get { Element[] epl = this.elementPerLattice; return epl == null ? null : epl[i]; }
+ set { Element[] epl = this.elementPerLattice; if (epl == null) return; epl[i] = value; }
+ }
+
+ } // class
+
+
+
+ ArrayList/*<Lattice>*/! lattices = new ArrayList();
+
+ private readonly IPropExprFactory! propExprFactory;
+
+
+ public MultiLattice(IPropExprFactory! propExprFactory, IValueExprFactory! valueExprFactory)
+ : base(valueExprFactory)
+ {
+ this.propExprFactory = propExprFactory;
+ // base(valueExprFactory);
+ }
+
+
+
+ public void AddLattice(Lattice lattice) { this.lattices.Add(lattice); }
+
+ private Lattice! SubLattice(int i) { return (Lattice!)this.lattices[i]; }
+
+
+ public override Element! Top { get { return Elt.Top(this.lattices); } }
+
+ public override Element! Bottom { get { return Elt.Bottom(this.lattices); } }
+
+
+
+
+ public override bool IsBottom(Element! element)
+ {
+ Elt e = (Elt)element;
+ // The program is errorneous/nonterminating if any subdomain knows it is.
+ //
+ if (e.elementPerLattice == null) { return true; }
+ for (int i = 0; i < e.Count; i++) { if (SubLattice(i).IsBottom((!)e[i])) { return true; } }
+ return false;
+ }
+
+ public override bool IsTop(Element! element)
+ {
+ Elt e = (Elt)element;
+ if (e.elementPerLattice == null) { return false; }
+ // The multidomain knows nothing about the program only if no subdomain
+ // knows anything about it.
+ //
+ for (int i = 0; i < e.Count; i++) { if (!SubLattice(i).IsTop((!)e[i])) { return false; } }
+ return true;
+ }
+
+ protected override bool AtMost(Element! first, Element! second)
+ {
+ Elt a = (Elt)first;
+ Elt b = (Elt)second;
+
+ for (int i = 0; i < a.Count; i++)
+ {
+ Element thisElement = (!) a[i];
+ Element thatElement = (!) b[i];
+ if (thisElement.GetType() != thatElement.GetType())
+ {
+ throw new System.InvalidOperationException(
+ "AtMost called on MultiDomain objects with different lattices"
+ );
+ }
+ if (!SubLattice(i).LowerThan(thisElement, thatElement)) { return false; }
+ }
+ return true;
+ }
+
+ protected override bool AtMost(Element! first, ICombineNameMap! firstToResult, Element! second, ICombineNameMap! secondToResult)
+ {
+ Elt a = (Elt)first;
+ Elt b = (Elt)second;
+
+ for (int i = 0; i < a.Count; i++)
+ {
+ Element thisElement = (!) a[i];
+ Element thatElement = (!) b[i];
+ if (thisElement.GetType() != thatElement.GetType())
+ {
+ throw new System.InvalidOperationException(
+ "AtMost called on MultiDomain objects with different lattices"
+ );
+ }
+ if (!SubLattice(i).LowerThan(thisElement, firstToResult, thatElement, secondToResult)) { return false; }
+ }
+ return true;
+ }
+
+
+ private enum CombineOp { Meet, Join, Widen }
+
+ private Element! Combine(Element! first, ICombineNameMap/*?*/ firstToResult, Element! second, ICombineNameMap/*?*/ secondToResult, CombineOp c)
+ {
+ Elt a = (Elt)first;
+ Elt b = (Elt)second;
+
+ int unionCount = System.Math.Max(a.Count, b.Count);
+ Elt combined = new Elt(unionCount, IsBottom(a) && IsBottom(b));
+ for (int i = 0; i < unionCount; i++)
+ {
+ bool thisExists = a.Contains(i);
+ bool thatExists = b.Contains(i);
+
+ if (thisExists && thatExists)
+ {
+ Lattice.Element suba = a[i];
+ Lattice.Element subb = b[i];
+ assert suba != null && subb != null;
+
+ switch (c)
+ {
+ case CombineOp.Meet:
+ combined[i] = SubLattice(i).Meet(suba, subb);
+ break;
+ case CombineOp.Join:
+ if (firstToResult != null && secondToResult != null)
+ combined[i] = SubLattice(i).Join(suba, firstToResult, subb, secondToResult);
+ else
+ combined[i] = SubLattice(i).Join(suba, subb);
+ break;
+ case CombineOp.Widen:
+ if (firstToResult != null && secondToResult != null)
+ combined[i] = SubLattice(i).Widen(suba, firstToResult, subb, secondToResult);
+ else
+ combined[i] = SubLattice(i).Widen(suba, subb);
+ break;
+ }
+ }
+ else if (thisExists)
+ {
+ combined[i] = a[i];
+ }
+ else
+ {
+ combined[i] = b[i];
+ }
+ }
+ Debug.Assert(combined.IsValid);
+ return combined;
+ }
+
+ public override Element! NontrivialJoin(Element! a, Element! b) { return this.Combine(a, null, b, null, CombineOp.Join); }
+
+ public override Element! NontrivialJoin(Element! a, ICombineNameMap! aToResult, Element! b, ICombineNameMap! bToResult) { return this.Combine(a, aToResult, b, bToResult, CombineOp.Join); }
+
+ public override Element! NontrivialMeet(Element! a, Element! b) { return this.Combine(a, null, b, null, CombineOp.Meet); }
+
+ public override Element! Widen(Element! a, Element! b) { return this.Combine(a, null, b, null, CombineOp.Widen); }
+
+ public override Element! Widen(Element! a, ICombineNameMap! aToResult, Element! b, ICombineNameMap! bToResult) { return this.Combine(a, aToResult, b, bToResult, CombineOp.Widen); }
+
+ public override Element! Eliminate(Element! element, IVariable! variable)
+ {
+ Elt e = (Elt)element;
+ if (IsBottom(e))
+ {
+ return e;
+ }
+ Elt newValue = new Elt(e.Count, false);
+ for (int i = 0; i < this.lattices.Count; i++)
+ {
+ newValue[i] = SubLattice(i).Eliminate((!) e[i], variable);
+ }
+ return newValue;
+ }
+
+
+ public override Element! Constrain(Element! element, IExpr! expr)
+ {
+ Elt e = (Elt)element;
+ if (IsBottom(e))
+ {
+ return e;
+ }
+ Elt newValue = new Elt(e.Count, false);
+ for (int i = 0; i < this.lattices.Count; i++)
+ {
+ newValue[i] = SubLattice(i).Constrain((!)e[i], expr);
+ }
+ return newValue;
+ }
+
+
+ public override Element! Rename(Element! element, IVariable! oldName, IVariable! newName)
+ {
+ Elt e = (Elt)element;
+ if (IsBottom(e))
+ {
+ return e;
+ }
+ Elt newValue = new Elt(e.Count, false);
+ for (int i = 0; i < this.lattices.Count; i++)
+ {
+ newValue[i] = SubLattice(i).Rename((!)e[i], oldName, newName);
+ }
+ return newValue;
+ }
+
+
+ public override bool Understands(IFunctionSymbol! f, IList! args)
+ {
+ bool result = false;
+
+ for (int i = 0; i < this.lattices.Count; i++)
+ {
+ result = (result || SubLattice(i).Understands(f, args));
+ }
+
+ return result;
+ }
+
+
+ public override string! ToString(Element! element)
+ {
+ Elt e = (Elt)element;
+ return e.ToString();
+ }
+
+
+ public override IExpr! ToPredicate(Element! element)
+ {
+ Elt e = (Elt)element;
+
+ IExpr result = propExprFactory.True;
+ for (int i = 0; i < e.Count; i++)
+ {
+ IExpr conjunct = SubLattice(i).ToPredicate((!)e[i]);
+ assert conjunct != null;
+
+ result = Prop.SimplifiedAnd(propExprFactory, conjunct, result);
+ }
+ return result;
+ }
+
+ /// <summary>
+ /// Return an expression that is equivalent to the given expression that does not
+ /// contain the given variable according to the lattice element and queryable.
+ ///
+ /// Simply asks each sublattice to try to generate an equivalent expression. We
+ /// do not try to combine information to infer new equivalences here.
+ /// </summary>
+ /// <param name="e">The lattice element.</param>
+ /// <param name="q">A queryable for asking addtional information.</param>
+ /// <param name="expr">The expression to find an equivalent expression.</param>
+ /// <param name="var">The variable to eliminate.</param>
+ /// <returns>
+ /// An equivalent expression to <paramref name="expr"/> without <paramref name="var"/>
+ /// or null if not possible.
+ /// </returns>
+ public override IExpr/*?*/ EquivalentExpr(Element! element, IQueryable! q, IExpr! expr, IVariable! var, Set/*<IVariable!>*/! prohibitedVars)
+ {
+ Elt! e = (Elt!)element;
+
+ for (int i = 0; i < e.Count; i++)
+ {
+ IExpr equivexpr = SubLattice(i).EquivalentExpr((!)e[i], q, expr, var, prohibitedVars);
+
+ if (equivexpr != null)
+ return equivexpr;
+ }
+
+ return null;
+ }
+
+
+ public override Answer CheckPredicate(Element! element, IExpr! pred)
+ {
+ Elt! e = (Elt!)element;
+
+ for (int i = 0; i < e.Count; i++)
+ {
+ Answer ans = SubLattice(i).CheckPredicate((!)e[i], pred);
+
+ if (ans == Answer.Yes || ans == Answer.No)
+ return ans;
+ }
+
+ return Answer.Maybe;
+ }
+
+
+ public override Answer CheckVariableDisequality(Element! element, IVariable! var1, IVariable! var2)
+ {
+ Elt! e = (Elt!)element;
+
+ for (int i = 0; i < e.Count; i++)
+ {
+ Answer ans = SubLattice(i).CheckVariableDisequality((!)e[i], var1, var2);
+
+ if (ans == Answer.Yes || ans == Answer.No)
+ return ans;
+ }
+
+ return Answer.Maybe;
+ }
+
+
+
+ public override void Validate()
+ {
+ base.Validate();
+ foreach (Lattice! l in lattices)
+ {
+ l.Validate();
+ }
+ }
+
+ /// <summary>
+ /// The enumeration over a MultiLattice is its sublattices.
+ /// </summary>
+ /// <returns>An enumerator over the sublattices.</returns>
+ [Pure] [GlobalAccess(false)] [Escapes(true,false)]
+ public IEnumerator/*<Lattice!>*/! GetEnumerator()
+ {
+ return lattices.GetEnumerator();
+ }
+
+ /// <summary>
+ /// Return an enumerable over a mapping of sublattices to the their corresponding
+ /// lattice elements given a MultiLattice element.
+ /// </summary>
+ /// <param name="element">The MultiLattice element.</param>
+ /// <returns>
+ /// An enumerable that yields an IDictionaryEnumerator over the
+ /// (Lattice, Lattice.Element) pairs.
+ /// </returns>
+ public IEnumerable! Subelements(Element! element)
+ {
+ return new SubelementsEnumerable(this, (Elt!) element);
+ }
+
+ /// <summary>
+ /// An enumerator over the sublattices and elements.
+ /// </summary>
+ private sealed class SubelementsEnumerable : IEnumerable
+ {
+ private sealed class SubelementsEnumerator : IDictionaryEnumerator
+ {
+ private readonly IEnumerator/*<Lattice!>*/! multiLatticeIter;
+ private readonly IEnumerator/*<Lattice.Element!>*/! multiElementIter;
+
+ public SubelementsEnumerator(MultiLattice! multiLattice, Elt! multiElement)
+ requires multiElement.elementPerLattice != null;
+ {
+ this.multiLatticeIter = multiLattice.lattices.GetEnumerator();
+ this.multiElementIter = multiElement.elementPerLattice.GetEnumerator();
+ // base();
+ }
+
+ public DictionaryEntry Entry
+ {
+ get
+ {
+ return new DictionaryEntry((!)multiLatticeIter.Current, multiElementIter.Current);
+ }
+ }
+
+ public object Key
+ {
+ get
+ {
+ return multiLatticeIter.Current;
+ }
+ }
+
+ public object Value
+ {
+ get
+ {
+ return multiElementIter.Current;
+ }
+ }
+
+ public object Current
+ {
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ get
+ {
+ return this.Entry;
+ }
+ }
+
+ public bool MoveNext()
+ {
+ return multiLatticeIter.MoveNext() && multiElementIter.MoveNext();
+ }
+
+ public void Reset()
+ {
+ multiLatticeIter.Reset();
+ multiElementIter.Reset();
+ }
+ }
+
+ private MultiLattice! multiLattice;
+ private Elt! multiElement;
+
+ public SubelementsEnumerable(MultiLattice! multiLattice, Elt! multiElement)
+ {
+ this.multiLattice = multiLattice;
+ this.multiElement = multiElement;
+ // base();
+ }
+
+ [Pure] [GlobalAccess(false)] [Escapes(true,false)]
+ public IEnumerator! GetEnumerator()
+ {
+ return new SubelementsEnumerator(multiLattice, multiElement);
+ }
+ }
+
+
+ }
+}
diff --git a/Source/AIFramework/Mutable.ssc b/Source/AIFramework/Mutable.ssc
new file mode 100644
index 00000000..894359ef
--- /dev/null
+++ b/Source/AIFramework/Mutable.ssc
@@ -0,0 +1,117 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+namespace Microsoft.AbstractInterpretationFramework.Collections
+{
+ using System.Collections;
+ using Microsoft.Contracts;
+
+ /// <summary>
+ /// Extend sets for using as a IWorkList.
+ /// </summary>
+ public class WorkSet : Microsoft.Boogie.Set, Microsoft.Boogie.IWorkList
+ {
+
+ // See Bug #148 for an explanation of why this is here.
+ // Without it, the contract inheritance rules will complain since it
+ // has nowhere to attach the out-of-band contract it gets from
+ // ICollection.Count that it gets from IWorkList.
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public override int Count { get { return base.Count; } }
+
+ [Pure][Reads(ReadsAttribute.Reads.Owned)] public bool IsEmpty()
+ {
+ return Count == 0;
+ }
+
+ /// <summary>
+ /// Pull an element out of the workset.
+ /// </summary>
+ public object Pull()
+ {
+ IEnumerator iter = GetEnumerator();
+ iter.MoveNext();
+
+ object result = (!)iter.Current;
+ Remove(result);
+
+ return result;
+ }
+
+ bool Microsoft.Boogie.IWorkList.Add(object o){
+ if (o == null) throw new System.ArgumentNullException();
+ this.Add(o);
+ return true;
+ }
+ bool Microsoft.Boogie.IWorkList.AddAll(IEnumerable objs){
+ if (objs == null) throw new System.ArgumentNullException();
+ return this.AddAll(objs);
+ }
+
+ // ICollection members
+ public void CopyTo (System.Array! a, int i) {
+ if (this.Count > a.Length - i) throw new System.ArgumentException();
+ int j = i;
+ foreach(object o in this){
+ a.SetValue(o, j++);
+ }
+ return;
+ }
+ object! ICollection.SyncRoot { [Pure] get { return this; } }
+ public bool IsSynchronized { get { return false; } }
+
+ }
+}
+
+namespace Microsoft.AbstractInterpretationFramework.Collections.Generic
+{
+ using System.Collections.Generic;
+
+ public class HashMultiset<T>
+ {
+ private readonly IDictionary<T,int>! dict;
+
+ //invariant forall{KeyValuePair<T,int> entry in dict; entry.Value >= 1};
+
+ public HashMultiset()
+ {
+ this.dict = new Dictionary<T,int>();
+ // base();
+ }
+
+ public HashMultiset(int size)
+ {
+ this.dict = new Dictionary<T,int>(size);
+ // base();
+ }
+
+ public void Add(T t)
+ { expose (this) {
+ if (dict.ContainsKey(t))
+ {
+ dict[t] = dict[t] + 1;
+ }
+ else
+ {
+ dict.Add(t,1);
+ }
+ }}
+
+ public void Remove(T t)
+ {
+ if (dict.ContainsKey(t))
+ { expose (this) {
+ int count = dict[t];
+ if (count == 1) { dict.Remove(t); }
+ else { dict[t] = count - 1; }
+ }}
+ }
+
+ public bool Contains(T t)
+ {
+ return dict.ContainsKey(t);
+ }
+ }
+}
diff --git a/Source/AIFramework/Polyhedra/LinearConstraint.ssc b/Source/AIFramework/Polyhedra/LinearConstraint.ssc
new file mode 100644
index 00000000..9ce1552b
--- /dev/null
+++ b/Source/AIFramework/Polyhedra/LinearConstraint.ssc
@@ -0,0 +1,588 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using Microsoft.Contracts;
+namespace Microsoft.AbstractInterpretationFramework
+{
+ using System;
+ using System.Compiler;
+ using System.Collections;
+ using Microsoft.Basetypes;
+ using IMutableSet = Microsoft.Boogie.Set;
+ using HashSet = Microsoft.Boogie.Set;
+ using ISet = Microsoft.Boogie.Set;
+
+
+ /// <summary>
+ /// Represents a single linear constraint, coefficients are stored as Rationals.
+ /// </summary>
+ public class LinearConstraint
+ {
+
+ public enum ConstraintRelation
+ {
+ EQ, // equal
+ LE, // less-than or equal
+ }
+
+ public readonly ConstraintRelation Relation;
+ internal Hashtable /*IVariable->Rational*/! coefficients = new Hashtable /*IVariable->Rational*/ ();
+ internal Rational rhs;
+
+ public LinearConstraint (ConstraintRelation rel)
+ {
+ Relation = rel;
+ }
+
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public override string! ToString()
+ {
+ string s = null;
+ foreach (DictionaryEntry /*IVariable->Rational*/ entry in coefficients)
+ {
+ if (s == null)
+ {
+ s = "";
+ }
+ else
+ {
+ s += " + ";
+ }
+ s += String.Format("{0}*{1}", entry.Value, entry.Key);
+ }
+ System.Diagnostics.Debug.Assert(s != null, "malformed LinearConstraint: no variables");
+ s += String.Format(" {0} {1}", Relation == ConstraintRelation.EQ ? "==" : "<=", rhs);
+ return s;
+ }
+
+
+#if DONT_KNOW_HOW_TO_TAKE_THE_TYPE_OF_AN_IVARIABLE_YET
+ public bool IsOverIntegers
+ {
+ get
+ {
+ foreach (DictionaryEntry /*IVariable->Rational*/ entry in coefficients)
+ {
+ IVariable var = (IVariable)entry.Key;
+ if ( ! var.TypedIdent.Type.IsInt) { return false; }
+ }
+ return true;
+ }
+ }
+#endif
+
+
+ /// <summary>
+ /// Note: This method requires that all dimensions are of type Variable, something that's
+ /// not required elsewhere in this class.
+ /// </summary>
+ /// <returns></returns>
+ public IExpr! ConvertToExpression(ILinearExprFactory! factory)
+ {
+ IExpr leftSum = null;
+ IExpr rightSum = null;
+ foreach (DictionaryEntry /*object->Rational*/ entry in coefficients)
+ {
+ IVariable var = (IVariable)entry.Key;
+ Rational coeff = (Rational) ((!)entry.Value);
+ if (coeff.IsPositive)
+ {
+ leftSum = AddTerm(factory, leftSum, coeff, var);
+ }
+ else if (coeff.IsNegative)
+ {
+ rightSum = AddTerm(factory, rightSum, -coeff, var);
+ }
+ else
+ {
+ // ignore the term is coeff==0
+ }
+ }
+
+ if (leftSum == null && rightSum == null)
+ {
+ // there are no variables in this constraint
+ if (Relation == ConstraintRelation.EQ ? rhs.IsZero : rhs.IsNonNegative) {
+ return factory.True;
+ } else {
+ return factory.False;
+ }
+ }
+
+ if (leftSum == null || (rightSum != null && rhs.IsNegative))
+ {
+ // show the constant on the left side
+ leftSum = AddTerm(factory, leftSum, -rhs, null);
+ }
+ else if (rightSum == null || rhs.IsPositive)
+ {
+ // show the constant on the right side
+ rightSum = AddTerm(factory, rightSum, rhs, null);
+ }
+
+ assert leftSum != null;
+ assert rightSum != null;
+ return Relation == ConstraintRelation.EQ ? factory.Eq(leftSum, rightSum) : factory.AtMost(leftSum, rightSum);
+ }
+
+ /// <summary>
+ /// Returns an expression that denotes sum + r*x.
+ /// If sum==null, drops the "sum +".
+ /// If x==null, drops the "*x".
+ /// if x!=null and r==1, drops the "r*".
+ /// </summary>
+ /// <param name="factory"></param>
+ /// <param name="sum"></param>
+ /// <param name="r"></param>
+ /// <param name="x"></param>
+ static IExpr! AddTerm(ILinearExprFactory! factory, /*MayBeNull*/ IExpr sum, Rational r, /*MayBeNull*/ IVariable x)
+ {
+ IExpr! product = factory.Term(r, x);
+ if (sum == null) {
+ return product;
+ } else {
+ return factory.Add(sum, product);
+ }
+ }
+
+ public ISet /*IVariable!*/! GetDefinedDimensions()
+ {
+ HashSet /*IVariable!*/ dims = new HashSet /*IVariable!*/ (coefficients.Count);
+ int j = 0;
+ foreach (IVariable! dim in coefficients.Keys)
+ {
+ dims.Add(dim);
+ j++;
+ }
+ System.Diagnostics.Debug.Assert(j == coefficients.Count);
+ return dims;
+ }
+
+ /// <summary>
+ /// Returns true iff all of the coefficients in the constraint are 0. In that
+ /// case, the constraint has the form 0 &lt;= C for some constant C; hence, the
+ /// constraint is either unsatisfiable or trivially satisfiable.
+ /// </summary>
+ /// <returns></returns>
+ public bool IsConstant()
+ {
+ foreach (Rational coeff in coefficients.Values)
+ {
+ if (coeff.IsNonZero)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /// <summary>
+ /// For an equality constraint, returns 0 == rhs.
+ /// For an inequality constraint, returns 0 &lt;= rhs.
+ /// </summary>
+ public bool IsConstantSatisfiable()
+ {
+ if (Relation == ConstraintRelation.EQ)
+ {
+ return rhs.IsZero;
+ }
+ else
+ {
+ return rhs.IsNonNegative;
+ }
+ }
+
+ /// <summary>
+ /// Returns 0 if "this" and "c" are not equivalent constraints. If "this" and "c"
+ /// are equivalent constraints, the non-0 return value "m" satisfies "this == m*c".
+ /// </summary>
+ /// <param name="c"></param>
+ /// <returns></returns>
+ public Rational IsEquivalent(LinearConstraint! c)
+ {
+ // "m" is the scale factor. If it is 0, it hasn't been used yet. If it
+ // is non-0, it will remain that value throughout, and it then says that
+ // for every dimension "d", "this[d] == m * c[d]".
+ Rational m = Rational.ZERO;
+
+ ArrayList /*IVariable*/ dd = new ArrayList /*IVariable*/ ();
+ foreach (IVariable! d in this.GetDefinedDimensions())
+ {
+ if (!dd.Contains(d)) { dd.Add(d); }
+ }
+ foreach (IVariable! d in c.GetDefinedDimensions())
+ {
+ if (!dd.Contains(d)) { dd.Add(d); }
+ }
+
+ foreach (IVariable! d in dd)
+ {
+ Rational a = this[d];
+ Rational b = c[d];
+
+ if (a.IsZero || b.IsZero)
+ {
+ if (a.IsNonZero || b.IsNonZero)
+ {
+ return Rational.ZERO; // not equivalent
+ }
+ }
+ else if (m.IsZero)
+ {
+ m = a / b;
+ }
+ else if (a != m * b)
+ {
+ return Rational.ZERO; // not equivalent
+ }
+ }
+
+ // we expect there to have been some non-zero coefficient, so "m" should have been used by now
+ System.Diagnostics.Debug.Assert(m.IsNonZero);
+
+ // finally, check the rhs
+ if (this.rhs == m * c.rhs)
+ {
+ return m; // equivalent
+ }
+ else
+ {
+ return Rational.ZERO; // not equivalent
+ }
+ }
+
+ /// <summary>
+ /// Splits an equality constraint into two inequality constraints, the conjunction of
+ /// which equals the equality constraint. Assumes "this" is a equality constraint.
+ /// </summary>
+ /// <param name="a"></param>
+ /// <param name="b"></param>
+ public void GenerateInequalityConstraints(out LinearConstraint a, out LinearConstraint b)
+ {
+ System.Diagnostics.Debug.Assert(this.Relation == ConstraintRelation.EQ);
+
+ a = new LinearConstraint(ConstraintRelation.LE);
+ a.coefficients = (Hashtable)this.coefficients.Clone();
+ a.rhs = this.rhs;
+
+ b = new LinearConstraint(ConstraintRelation.LE);
+ b.coefficients = new Hashtable /*IVariable->Rational*/ ();
+ foreach (DictionaryEntry entry in this.coefficients)
+ {
+ b.coefficients[entry.Key] = -(Rational) ((!)entry.Value);
+ }
+ b.rhs = -this.rhs;
+ }
+
+ public void SetCoefficient(IVariable! dimension, Rational coefficient)
+ {
+ coefficients[dimension] = coefficient;
+ }
+
+ /// <summary>
+ /// Removes dimension "dim" from the constraint. Only dimensions with coefficient 0 can
+ /// be removed.
+ /// </summary>
+ /// <param name="dim"></param>
+ public void RemoveDimension(IVariable! dim)
+ {
+ object val = coefficients[dim];
+ if (val != null)
+ {
+#if FIXED_SERIALIZER
+ assert ((Rational)val).IsZero;
+#endif
+ coefficients.Remove(dim);
+ }
+ }
+
+ /// <summary>
+ /// The getter returns 0 if the dimension is not present.
+ /// </summary>
+ public Rational this [IVariable! dimension]
+ {
+ get
+ {
+ object z = coefficients[dimension];
+ if (z == null)
+ {
+ return Rational.ZERO;
+ }
+ else
+ {
+ return (Rational)z;
+ }
+ }
+ set { SetCoefficient(dimension, value); }
+ }
+
+ public LinearConstraint Rename(IVariable! oldName, IVariable! newName)
+ {
+ object /*Rational*/ z = coefficients[oldName];
+ if (z == null)
+ {
+ return this;
+ }
+ else
+ {
+ System.Diagnostics.Debug.Assert(z is Rational);
+ Hashtable /*IVariable->Rational*/ newCoeffs = (Hashtable! /*IVariable->Rational*/)coefficients.Clone();
+ newCoeffs.Remove(oldName);
+ newCoeffs.Add(newName, z);
+
+ LinearConstraint lc = new LinearConstraint(this.Relation);
+ lc.coefficients = newCoeffs;
+ lc.rhs = this.rhs;
+ return lc;
+ }
+ }
+
+ public LinearConstraint Clone()
+ {
+ LinearConstraint z = new LinearConstraint(Relation);
+ z.coefficients = (Hashtable /*IVariable->Rational*/)this.coefficients.Clone();
+ z.rhs = this.rhs;
+ return z;
+ }
+
+ /// <summary>
+ /// Returns a constraint like "this", but with the given relation "r".
+ /// </summary>
+ /// <returns></returns>
+ public LinearConstraint! ChangeRelation(ConstraintRelation rel)
+ {
+ if (Relation == rel)
+ {
+ return this;
+ }
+ else
+ {
+ LinearConstraint z = new LinearConstraint(rel);
+ z.coefficients = (Hashtable)this.coefficients.Clone();
+ z.rhs = this.rhs;
+ return z;
+ }
+ }
+
+ /// <summary>
+ /// Returns a constraint like "this", but, conceptually, with the inequality relation >=.
+ /// </summary>
+ /// <returns></returns>
+ public LinearConstraint! ChangeRelationToAtLeast()
+ {
+ LinearConstraint z = new LinearConstraint(ConstraintRelation.LE);
+ foreach (DictionaryEntry /*IVariable->Rational*/ entry in this.coefficients)
+ {
+ z.coefficients.Add(entry.Key, -(Rational) ((!)entry.Value));
+ }
+ z.rhs = -this.rhs;
+ return z;
+ }
+
+ /// <summary>
+ /// Returns the left-hand side of the constraint evaluated at the point "v".
+ /// Any coordinate not present in "v" is treated as if it were 0.
+ /// Stated differently, this routine treats the left-hand side of the constraint
+ /// as a row vector and "v" as a column vector, and then returns the dot-product
+ /// of the two.
+ /// </summary>
+ /// <param name="v"></param>
+ /// <returns></returns>
+ public Rational EvaluateLhs(FrameElement! v)
+ {
+ Rational q = Rational.ZERO;
+ foreach (DictionaryEntry /*IVariable,Rational*/ term in coefficients)
+ {
+ IVariable dim = (IVariable!)term.Key;
+ Rational a = (Rational) ((!)term.Value);
+ Rational x = v[dim];
+ q += a * x;
+ }
+ return q;
+ }
+
+ /// <summary>
+ /// Determines whether or not a given vertex or ray saturates the constraint.
+ /// </summary>
+ /// <param name="fe"></param>
+ /// <param name="vertex">true if "fe" is a vertex; false if "fe" is a ray</param>
+ /// <returns></returns>
+ public bool IsSaturatedBy(FrameElement! fe, bool vertex)
+ {
+ Rational lhs = EvaluateLhs(fe);
+ Rational rhs = vertex ? this.rhs : Rational.ZERO;
+ return lhs == rhs;
+ }
+
+ /// <summary>
+ /// Changes the current constraint A*X &lt;= B into (A + m*aa)*X &lt;= B + m*bb,
+ /// where "cc" is the constraint aa*X &lt;= bb.
+ /// </summary>
+ /// <param name="m"></param>
+ /// <param name="cc"></param>
+ /// <returns></returns>
+ public void AddMultiple(Rational m, LinearConstraint! cc)
+ {
+ foreach (DictionaryEntry /*IVariable->Rational*/ entry in cc.coefficients)
+ {
+ IVariable dim = (IVariable)entry.Key;
+ Rational d = m * (Rational) ((!)entry.Value);
+ if (d.IsNonZero)
+ {
+ object prev = coefficients[dim];
+ if (prev == null)
+ {
+ coefficients[dim] = d;
+ }
+ else
+ {
+ coefficients[dim] = (Rational)prev + d;
+ }
+ }
+ }
+ rhs += m * cc.rhs;
+ }
+
+ /// <summary>
+ /// Try to reduce the magnitude of the coefficients used.
+ /// Has a side effect on the coefficients, but leaves the meaning of the linear constraint
+ /// unchanged.
+ /// </summary>
+ public void Normalize() {
+ // compute the gcd of the numerators and the gcd of the denominators
+ Rational gcd = rhs;
+ foreach (Rational r in coefficients.Values) {
+ gcd = Rational.Gcd(gcd, r);
+ }
+ // Change all coefficients, to divide their numerators with gcdNum and to
+ // divide their denominators with gcdDen.
+ Hashtable /*IVariable->Rational*/ newCoefficients = new Hashtable /*IVariable->Rational*/ (coefficients.Count);
+ foreach (DictionaryEntry /*IVarianble->Rational*/ e in coefficients) {
+ Rational r = (Rational) ((!)e.Value);
+ if (r.IsNonZero) {
+ newCoefficients.Add(e.Key, new Rational(r.Numerator / gcd.Numerator, r.Denominator / gcd.Denominator));
+ } else {
+ newCoefficients.Add(e.Key, r);
+ }
+ }
+
+ coefficients = newCoefficients;
+ rhs = rhs.IsNonZero ? (Rational)new Rational(rhs.Numerator / gcd.Numerator, rhs.Denominator / gcd.Denominator) : rhs;
+ }
+ }
+
+ /// <summary>
+ /// Represents a frame element (vector of dimension/value tuples). Used only
+ /// internally in class LinearConstraintSystem and its communication with class
+ /// LinearConstraint.
+ /// </summary>
+ public class FrameElement
+ {
+
+ Hashtable /*IVariable->Rational*/! terms = new Hashtable /*IVariable->Rational*/ ();
+
+ /// <summary>
+ /// Constructs an empty FrameElement. To add dimensions, call AddCoordinate after construction.
+ /// </summary>
+ public FrameElement()
+ {
+ }
+
+ /// <summary>
+ /// This method is to be thought of as being part of the FrameElement object's construction process.
+ /// Assumes "dimension" is not already in FrameElement.
+ /// </summary>
+ /// <param name="dimension"></param>
+ /// <param name="value"></param>
+ public void AddCoordinate(IVariable! dimension, Rational value)
+ {
+ terms.Add(dimension, value);
+ }
+
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public override string! ToString()
+ {
+ string s = null;
+ foreach (DictionaryEntry item in terms)
+ {
+ if (s == null)
+ {
+ s = "(";
+ }
+ else
+ {
+ s += ", ";
+ }
+ s += String.Format("<{0},{1}>", item.Key, (Rational) ((!)item.Value));
+ }
+ if (s == null)
+ {
+ s = "(";
+ }
+ return s + ")";
+ }
+
+ public IMutableSet /*IVariable!*/! GetDefinedDimensions()
+ {
+ HashSet /*IVariable!*/! dims = new HashSet /*IVariable!*/ (terms.Count);
+ foreach (IVariable! dim in terms.Keys)
+ {
+ dims.Add(dim);
+ }
+ System.Diagnostics.Debug.Assert(dims.Count == terms.Count);
+ return dims;
+ }
+
+ /// <summary>
+ /// The getter returns the value at the given dimension, or 0 if that dimension is not defined.
+ /// </summary>
+ public Rational this [IVariable! dimension]
+ {
+ get
+ {
+ object z = terms[dimension];
+ if (z == null)
+ {
+ return Rational.ZERO;
+ }
+ else
+ {
+ return (Rational)z;
+ }
+ }
+ set
+ {
+ terms[dimension] = value;
+ }
+ }
+
+ public FrameElement Rename(IVariable! oldName, IVariable! newName)
+ {
+ object /*Rational*/ z = terms[oldName];
+ if (z == null)
+ {
+ return this;
+ }
+ else
+ {
+ System.Diagnostics.Debug.Assert(z is Rational);
+ Hashtable /*IVariable->Rational*/ newTerms = (Hashtable! /*IVariable->Rational*/)terms.Clone();
+ newTerms.Remove(oldName);
+ newTerms.Add(newName, z);
+
+ FrameElement fe = new FrameElement();
+ fe.terms = newTerms;
+ return fe;
+ }
+ }
+
+ public FrameElement Clone()
+ {
+ FrameElement z = new FrameElement();
+ z.terms = (Hashtable /*IVariable->Rational*/)this.terms.Clone();
+ return z;
+ }
+ }
+}
diff --git a/Source/AIFramework/Polyhedra/LinearConstraintSystem.ssc b/Source/AIFramework/Polyhedra/LinearConstraintSystem.ssc
new file mode 100644
index 00000000..b4e33a28
--- /dev/null
+++ b/Source/AIFramework/Polyhedra/LinearConstraintSystem.ssc
@@ -0,0 +1,1856 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+namespace Microsoft.AbstractInterpretationFramework
+{
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System;
+ using Microsoft.SpecSharp.Collections;
+ using Microsoft.Contracts;
+ using Microsoft.Basetypes;
+ using IMutableSet = Microsoft.Boogie.Set;
+ using HashSet = Microsoft.Boogie.Set;
+ using ISet = Microsoft.Boogie.Set;
+
+ /// <summary>
+ /// Represents a system of linear constraints (constraint/frame representations).
+ /// </summary>
+ public class LinearConstraintSystem
+ {
+ // --------------------------------------------------------------------------------------------------------
+ // ------------------ Data structure ----------------------------------------------------------------------
+ // --------------------------------------------------------------------------------------------------------
+
+ public /*maybe null*/ ArrayList /*LinearConstraint!*/ Constraints;
+ /*maybe null*/ ArrayList /*FrameElement!*/ FrameVertices;
+ /*maybe null*/ ArrayList /*FrameElement!*/ FrameRays;
+ IMutableSet/*IVariable!*/! FrameDimensions;
+ /*maybe null*/ ArrayList /*FrameElement!*/ FrameLines;
+ // Invariant: Either all of Constraints, FrameVertices, FrameRays, and FrameLines are
+ // null, or all are non-null.
+ // Invariant: Any dimension mentioned in Constraints, FrameVertices, FrameRays, or
+ // FrameLines is mentioned in FrameDimensions.
+ // The meaning of FrameDimensions is that for any dimension x not in FrameDimensions,
+ // there is an implicit line along dimension x (that is, (<x,1>)).
+
+ void CheckInvariant()
+ {
+ if (Constraints == null)
+ {
+ System.Diagnostics.Debug.Assert(FrameVertices == null);
+ System.Diagnostics.Debug.Assert(FrameRays == null);
+ System.Diagnostics.Debug.Assert(FrameLines == null);
+ System.Diagnostics.Debug.Assert(FrameDimensions.Count == 0);
+ }
+ else
+ {
+ System.Diagnostics.Debug.Assert(FrameVertices != null);
+ System.Diagnostics.Debug.Assert(FrameRays != null);
+ System.Diagnostics.Debug.Assert(FrameLines != null);
+
+ foreach (LinearConstraint! cc in Constraints)
+ {
+#if FIXED_DESERIALIZER
+ assert Forall{IVariable! var in cc.GetDefinedDimensions(); FrameDimensions.Contains(var)};
+#endif
+ assert cc.coefficients.Count != 0;
+ }
+ foreach (ArrayList /*FrameElement*/! FrameComponent in new ArrayList /*FrameElement*/ [] {FrameVertices, FrameRays, FrameLines})
+ {
+ foreach (FrameElement fe in FrameComponent)
+ {
+ if (fe == null) continue;
+#if FIXED_DESERIALIZER
+ assert Forall{IVariable! var in fe.GetDefinedDimensions(); FrameDimensions.Contains(var)};
+#endif
+ }
+ }
+ }
+ }
+
+ // --------------------------------------------------------------------------------------------------------
+ // ------------------ Constructors ------------------------------------------------------------------------
+ // --------------------------------------------------------------------------------------------------------
+
+ /// <summary>
+ /// Creates a LinearConstraintSystem representing the bottom element, that is, representing
+ /// an unsatisfiable system of constraints.
+ /// </summary>
+ [NotDelayed]
+ public LinearConstraintSystem()
+ {
+ FrameDimensions = new HashSet /*IVariable!*/ ();
+ base();
+ CheckInvariant();
+ }
+
+ /// <summary>
+ /// Constructs a linear constraint system with constraints "cs".
+ /// The constructor captures all constraints in "cs".
+ /// </summary>
+ /// <param name="cs"></param>
+ [NotDelayed]
+ public LinearConstraintSystem(ArrayList /*LinearConstraint!*/! cs)
+#if BUG_159_HAS_BEEN_FIXED
+ requires Forall{LinearConstraint! cc in cs; cc.coefficients.Count != 0};
+#endif
+ {
+ ArrayList constraints = new ArrayList /*LinearConstraint!*/ (cs.Count);
+ foreach (LinearConstraint! cc in cs)
+ {
+ constraints.Add(cc);
+ }
+ Constraints = constraints;
+ FrameDimensions = new HashSet /*IVariable!*/ (); // to please compiler; this value will be overridden in the call to GenerateFrameConstraints below
+ base();
+
+ GenerateFrameFromConstraints();
+ SimplifyConstraints();
+ CheckInvariant();
+#if DEBUG_PRINT
+ Console.WriteLine("LinearConstraintSystem: constructor produced:");
+ Dump();
+#endif
+ }
+
+ /// <summary>
+ /// Constructs a linear constraint system corresponding to given vertex. This constructor
+ /// is only used in the test harness--it is not needed for abstract interpretation.
+ /// </summary>
+ /// <param name="v"></param>
+ [NotDelayed]
+ LinearConstraintSystem(FrameElement! v)
+ {
+ IMutableSet! frameDims = v.GetDefinedDimensions();
+ ArrayList /*LinearConstraint!*/ constraints = new ArrayList /*LinearConstraint!*/ ();
+ foreach (IVariable! dim in frameDims)
+ {
+ LinearConstraint lc = new LinearConstraint(LinearConstraint.ConstraintRelation.EQ);
+ lc.SetCoefficient(dim, Rational.ONE);
+ lc.rhs = v[dim];
+ constraints.Add(lc);
+ }
+ FrameDimensions = frameDims;
+ Constraints = constraints;
+
+ ArrayList /*FrameElement*/ frameVertices = new ArrayList /*FrameElement*/ ();
+ frameVertices.Add(v);
+ FrameVertices = frameVertices;
+
+ FrameRays = new ArrayList /*FrameElement*/ ();
+ FrameLines = new ArrayList /*FrameElement*/ ();
+
+ base();
+ CheckInvariant();
+ }
+
+ void ChangeIntoBottom()
+ {
+ Constraints = null;
+ FrameVertices = null;
+ FrameRays = null;
+ FrameLines = null;
+ IMutableSet ss;
+ FrameDimensions.Clear(); // no implicit lines
+ }
+
+ // --------------------------------------------------------------------------------------------------------
+ // ------------------ Public operations and their support routines ----------------------------------------
+ // --------------------------------------------------------------------------------------------------------
+
+ public bool IsBottom()
+ {
+ return Constraints == null;
+ }
+
+ public bool IsTop()
+ {
+ return Constraints != null && Constraints.Count == 0;
+ }
+
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public override string! ToString()
+ {
+ if (Constraints == null)
+ {
+ return "<bottom>";
+ }
+ else if (Constraints.Count == 0)
+ {
+ return "<top>";
+ }
+ else
+ {
+ string z = null;
+ foreach (LinearConstraint! lc in Constraints)
+ {
+ string s = lc.ToString();
+ if (z == null)
+ {
+ z = s;
+ }
+ else
+ {
+ z += " AND " + s;
+ }
+ }
+ assert z != null;
+ return z;
+ }
+ }
+
+
+ public ICollection<IVariable!>! FreeVariables()
+ ensures result.IsReadOnly;
+ {
+ List<IVariable!> list = new List<IVariable!>();
+ foreach (IVariable! v in FrameDimensions) { list.Add(v); }
+ return (!)list.AsReadOnly();
+ }
+
+ /// <summary>
+ /// Note: This method requires that all dimensions are of type Variable, something that's
+ /// not required elsewhere in this class.
+ /// </summary>
+ /// <returns></returns>
+ public IExpr! ConvertToExpression(ILinearExprFactory! factory)
+ {
+ if (this.Constraints == null) {
+ return factory.False;
+ }
+ if (this.Constraints.Count == 0) {
+ return factory.True;
+ }
+
+ IExpr result = null;
+ foreach (LinearConstraint! lc in Constraints)
+ {
+ IExpr conjunct = lc.ConvertToExpression(factory);
+ result = (result == null) ? conjunct : (IExpr)factory.And(conjunct, result);
+ }
+ assert result != null;
+ return result;
+ }
+
+
+ /* IsSubset(): determines if 'lcs' is a subset of 'this'
+ * -- See Cousot/Halbwachs 1978, section
+ */
+ public bool IsSubset(LinearConstraintSystem! lcs)
+ {
+ if (lcs.IsBottom())
+ {
+ return true;
+ }
+ else if (this.IsBottom())
+ {
+ return false;
+#if DEBUG
+#else
+ } else if (this.IsTop()) { // optimization -- this case not needed for correctness
+ return true;
+ } else if (lcs.IsTop()) { // optimization -- this case not needed for correctness
+ return false;
+#endif
+ }
+ else
+ {
+ // phase 0: check if frame dimensions are a superset of the constraint dimensions
+ ISet /*IVariable!*/! frameDims = lcs.GetDefinedDimensions();
+#if DEBUG_PRINT
+ Console.WriteLine("DEBUG: IsSubset:");
+ Console.WriteLine(" --- this:");
+ this.Dump();
+ Console.WriteLine(" --- lcs:");
+ lcs.Dump();
+ Console.WriteLine(" ---");
+#endif
+ foreach (LinearConstraint! cc in (!)this.Constraints)
+ {
+#if DEBUG_PRINT
+ Console.WriteLine(" cc: {0}", cc);
+ Console.WriteLine(" cc.GetDefinedDimensions(): {0}", cc.GetDefinedDimensions());
+#endif
+ if (!forall{IVariable! var in cc.GetDefinedDimensions(); frameDims.Contains(var)})
+ {
+#if DEBUG_PRINT
+ Console.WriteLine(" ---> phase 0 subset violated, return false from IsSubset");
+#endif
+ return false;
+ }
+ }
+ }
+
+ // phase 1: check frame vertices against each constraint...
+ foreach (FrameElement! v in (!)lcs.FrameVertices)
+ {
+ foreach (LinearConstraint! cc in this.Constraints)
+ {
+ Rational q = cc.EvaluateLhs(v);
+ if (cc.Relation == LinearConstraint.ConstraintRelation.LE)
+ {
+ if (!(q <= cc.rhs))
+ {
+#if DEBUG_PRINT
+ Console.WriteLine(" ---> phase 1a subset violated, return false from IsSubset");
+#endif
+ return false;
+ }
+ }
+ else
+ {
+ if (!(q == cc.rhs))
+ {
+#if DEBUG_PRINT
+ Console.WriteLine(" ---> phase 1b subset violated, return false from IsSubset");
+#endif
+ return false;
+ }
+ }
+ }
+ }
+
+ // phase 2: check frame rays against each constraint...
+ // To check if a ray "r" falls within a constraint "cc", we add the vector "r" to
+ // any point "p" on the side of the half-space or plane described by constraint, and
+ // then check if the resulting point satisfies the constraint. That is, we check (for
+ // an inequality constraint with coefficients a1,a2,...,an and right-hand side
+ // constant C):
+ // a1*(r1+p1) + a2*(r2+p2) + ... + an*(rn+pn) <= C
+ // Equivalently:
+ // a1*r1 + a2*r2 + ... + an*rn + a1*p1 + a2*p2 + ... + an*pn <= C
+ // To find a point "p", we can pick out a coordinate, call it 1, with a non-zero
+ // coefficient in the constraint, and then choose "p" as the point that has the
+ // value C/a1 in coordinate 1 and has 0 in all other coordinates. We then check:
+ // a1*r1 + a2*r2 + ... + an*rn + a1*(C/a1) + a2*0 + ... + an*0 <= C
+ // which simplifies to:
+ // a1*r1 + a2*r2 + ... + an*rn + C <= C
+ // which in turn simplifies to:
+ // a1*r1 + a2*r2 + ... + an*rn <= 0
+ // If the constraint is an equality constraint, we simply replace "<=" with "=="
+ // above.
+ foreach (FrameElement! r in (!)lcs.FrameRays)
+ {
+ System.Diagnostics.Debug.Assert(r != null, "encountered a null ray...");
+ foreach (LinearConstraint! cc in this.Constraints)
+ {
+ System.Diagnostics.Debug.Assert(cc != null, "encountered an null constraint...");
+ Rational q = cc.EvaluateLhs(r);
+ if (cc.Relation == LinearConstraint.ConstraintRelation.LE)
+ {
+ if (q.IsPositive)
+ {
+#if DEBUG_PRINT
+ Console.WriteLine(" ---> phase 2a subset violated, return false from IsSubset");
+#endif
+ return false;
+ }
+ }
+ else
+ {
+ if (q.IsNonZero)
+ {
+#if DEBUG_PRINT
+ Console.WriteLine(" ---> phase 2b subset violated, return false from IsSubset");
+#endif
+ return false;
+ }
+ }
+ }
+ }
+
+ // phase 3: check frame lines against each constraint...
+ // To check if a line "L" falls within a constraint "cc", we check if both the
+ // vector "L" and "-L", interpreted as rays, fall within the constraint. From
+ // the discussion above, this means we check the following two properties:
+ // a1*L1 + a2*L2 + ... + an*Ln <= 0 (*)
+ // a1*(-L1) + a2*(-L2) + ... + an*(-Ln) <= 0
+ // The second of these lines can be rewritten as:
+ // - a1*L1 - a2*L2 - ... - an*Ln <= 0
+ // which is equivalent to:
+ // -1 * (a1*L1 + a2*L2 + ... + an*Ln) <= 0
+ // Multiplying both sides by -1 and flipping the direction of the inequality,
+ // we have:
+ // a1*L1 + a2*L2 + ... + an*Ln >= 0 (**)
+ // Putting (*) and (**) together, we conclude that we need to check:
+ // a1*L1 + a2*L2 + ... + an*Ln == 0
+ // If the constraint is an equality constraint, we end up with the same equation.
+ foreach (FrameElement! line in (!)lcs.FrameLines)
+ {
+ System.Diagnostics.Debug.Assert(line != null, "encountered a null line...");
+ foreach (LinearConstraint! cc in this.Constraints)
+ {
+ System.Diagnostics.Debug.Assert(cc != null, "encountered an null constraint...");
+ Rational q = cc.EvaluateLhs(line);
+ if (q.IsNonZero)
+ {
+#if DEBUG_PRINT
+ Console.WriteLine(" ---> phase 3 subset violated, return false from IsSubset");
+#endif
+ return false;
+ }
+ }
+ }
+
+#if DEBUG_PRINT
+ Console.WriteLine(" ---> IsSubset returns true");
+#endif
+ return true;
+ }
+
+ public LinearConstraintSystem! Meet(LinearConstraintSystem! lcs)
+ requires this.Constraints != null;
+ requires lcs.Constraints != null;
+ {
+ ArrayList /*LinearConstraint*/ clist = new ArrayList(this.Constraints.Count + lcs.Constraints.Count);
+ clist.AddRange(this.Constraints);
+ clist.AddRange(lcs.Constraints);
+ return new LinearConstraintSystem(clist);
+ }
+
+#if DEBUG_PRINT
+ public LinearConstraintSystem Join(LinearConstraintSystem lcs)
+ {
+ Console.WriteLine("===================================================================================");
+ Console.WriteLine("DEBUG: Join");
+ Console.WriteLine("Join: this=");
+ Dump();
+ Console.WriteLine("Join: lcs=");
+ lcs.Dump();
+ LinearConstraintSystem z = JoinX(lcs);
+ Console.WriteLine("----------Join------------------------------>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
+ Console.WriteLine("Join: result=");
+ z.Dump();
+ Console.WriteLine("===================================================================================");
+ return z;
+ }
+#endif
+
+ /// <summary>
+ /// The join is computed as described in section 4.4 in Cousot and Halbwachs.
+ /// </summary>
+ /// <param name="lcs"></param>
+ /// <returns></returns>
+#if DEBUG_PRINT
+ public LinearConstraintSystem JoinX(LinearConstraintSystem lcs)
+#else
+ public LinearConstraintSystem! Join(LinearConstraintSystem! lcs)
+#endif
+ {
+ if (this.IsBottom())
+ {
+ return (!) lcs.Clone();
+ }
+ else if (lcs.IsBottom())
+ {
+ return (!) this.Clone();
+ }
+ else if (this.IsTop() || lcs.IsTop())
+ {
+ return new LinearConstraintSystem(new ArrayList /*LinearConstraint*/ ());
+ }
+ else
+ {
+ LinearConstraintSystem! z;
+ // Start from the "larger" of the two frames (this is just a heuristic measure intended
+ // to save work).
+ assume this.FrameVertices != null;
+ assume this.FrameRays != null;
+ assume this.FrameLines != null;
+ assume lcs.FrameVertices != null;
+ assume lcs.FrameRays != null;
+ assume lcs.FrameLines != null;
+ if (this.FrameVertices.Count + this.FrameRays.Count + this.FrameLines.Count - this.FrameDimensions.Count <
+ lcs.FrameVertices.Count + lcs.FrameRays.Count + lcs.FrameLines.Count - lcs.FrameDimensions.Count)
+ {
+ z = (!) lcs.Clone();
+ lcs = this;
+ }
+ else
+ {
+ z = (!) this.Clone();
+ }
+#if DEBUG_PRINT
+ Console.WriteLine("DEBUG: LinearConstraintSystem.Join ---------------");
+ Console.WriteLine("z:");
+ z.Dump();
+ Console.WriteLine("lcs:");
+ lcs.Dump();
+#endif
+
+ // Start by explicating the implicit lines of z for the dimensions dims(lcs)-dims(z).
+ foreach (IVariable! dim in lcs.FrameDimensions)
+ {
+ if (!z.FrameDimensions.Contains(dim))
+ {
+ z.FrameDimensions.Add(dim);
+ FrameElement line = new FrameElement();
+ line.AddCoordinate(dim, Rational.ONE);
+ // Note: AddLine is not called (because the line already exists in z--it's just that
+ // it was represented implicitly). Instead, just tack the explicit representation onto
+ // FrameLines.
+ assume z.FrameLines != null;
+ z.FrameLines.Add(line);
+#if DEBUG_PRINT
+ Console.WriteLine("Join: After explicating line: {0}", line);
+ z.Dump();
+#endif
+ }
+ }
+
+ // Now, the vertices, rays, and lines can be added.
+ foreach (FrameElement! v in lcs.FrameVertices)
+ {
+ z.AddVertex(v);
+#if DEBUG_PRINT
+ Console.WriteLine("Join: After adding vertex: {0}", v);
+ z.Dump();
+#endif
+ }
+ foreach (FrameElement! r in lcs.FrameRays)
+ {
+ z.AddRay(r);
+#if DEBUG_PRINT
+ Console.WriteLine("Join: After adding ray: {0}", r);
+ z.Dump();
+#endif
+ }
+ foreach (FrameElement! l in lcs.FrameLines)
+ {
+ z.AddLine(l);
+#if DEBUG_PRINT
+ Console.WriteLine("Join: After adding line: {0}", l);
+ z.Dump();
+#endif
+ }
+ // also add to z the implicit lines of lcs
+ foreach (IVariable! dim in z.FrameDimensions)
+ {
+ if (!lcs.FrameDimensions.Contains(dim))
+ {
+ // "dim" is a dimension that's explicit in "z" but implicit in "lcs"
+ FrameElement line = new FrameElement();
+ line.AddCoordinate(dim, Rational.ONE);
+ z.AddLine(line);
+#if DEBUG_PRINT
+ Console.WriteLine("Join: After adding lcs's implicit line: {0}", line);
+ z.Dump();
+#endif
+ }
+ }
+
+ z.SimplifyFrame();
+ z.SimplifyConstraints();
+ z.CheckInvariant();
+#if DEBUG_PRINT
+ Console.WriteLine("Join: Returning z:");
+ z.Dump();
+ Console.WriteLine("----------------------------------------");
+#endif
+ return z;
+ }
+ }
+
+#if DEBUG_PRINT
+ public LinearConstraintSystem Widen(LinearConstraintSystem lcs)
+ {
+ Console.WriteLine("===================================================================================");
+ Console.WriteLine("DEBUG: Widen");
+ Console.WriteLine("Widen: this=");
+ Dump();
+ Console.WriteLine("Widen: lcs=");
+ lcs.Dump();
+ LinearConstraintSystem z = WidenX(lcs);
+ Console.WriteLine("----------Widen------------------------------>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
+ Console.WriteLine("Widen: result=");
+ z.Dump();
+ Console.WriteLine("===================================================================================");
+ return z;
+ }
+#endif
+
+#if DEBUG_PRINT
+ public LinearConstraintSystem WidenX(LinearConstraintSystem lcs)
+#else
+ public LinearConstraintSystem! Widen(LinearConstraintSystem! lcs)
+#endif
+ {
+ if (this.IsBottom())
+ {
+ return (!) lcs.Clone();
+ }
+ else if (lcs.IsBottom())
+ {
+ return (!) this.Clone();
+ }
+ else if (this.IsTop() || lcs.IsTop())
+ {
+ return new LinearConstraintSystem(new ArrayList /*LinearConstraint*/ ());
+ }
+
+ // create new LCS, we will add only verified constraints to this...
+ ArrayList /*LinearConstraint*/ newConstraints = new ArrayList /*LinearConstraint*/ ();
+ assume this.Constraints != null;
+ foreach (LinearConstraint! ccX in this.Constraints)
+ {
+ LinearConstraint cc = ccX;
+#if DEBUG_PRINT
+ Console.WriteLine("Widen checking: Starting to check constraint: {0}", cc);
+#endif
+ if (cc.IsConstant())
+ {
+ // (Can this ever occur in the stable state of a LinearConstraintSystem? --KRML)
+ // constraint is unaffected by the frame components
+#if DEBUG_PRINT
+ Console.WriteLine("Widen checking: --Adding it!");
+#endif
+ newConstraints.Add(cc);
+ continue;
+ }
+
+ // PHASE I: verify constraints against all frame vertices...
+
+ foreach (FrameElement! vertex in (!)lcs.FrameVertices)
+ {
+ Rational lhs = cc.EvaluateLhs(vertex);
+ if (lhs > cc.rhs)
+ {
+ // the vertex does not satisfy the inequality <=
+ if (cc.Relation == LinearConstraint.ConstraintRelation.LE)
+ {
+#if DEBUG_PRINT
+ Console.WriteLine("Widen checking: throwing out because of vertex: {0}", vertex);
+#endif
+ goto CHECK_NEXT_CONSTRAINT;
+ }
+ else
+ {
+ // ... but it does satisfy the inequality >=
+#if DEBUG_PRINT
+ Console.WriteLine("Widen checking: throwing out <= because of vertex: {0}", vertex);
+#endif
+ cc = cc.ChangeRelationToAtLeast();
+#if DEBUG_PRINT
+ Console.WriteLine("Widen checking: left with constraint: {0}", cc);
+#endif
+ }
+ }
+ else if (cc.Relation == LinearConstraint.ConstraintRelation.EQ && lhs < cc.rhs)
+ {
+ // the vertex does not satisfy the inequality >=, and the constraint is an equality constraint
+#if DEBUG_PRINT
+ Console.WriteLine("Widen checking: throwing out >= because of vertex: {0}", vertex);
+#endif
+ cc = cc.ChangeRelation(LinearConstraint.ConstraintRelation.LE);
+#if DEBUG_PRINT
+ Console.WriteLine("Widen checking: left with contraint: {0}", cc);
+#endif
+ }
+ }
+
+ // PHASE II: verify constraints against all frame rays...
+
+ foreach (FrameElement! ray in (!)lcs.FrameRays)
+ {
+ // The following assumes the constraint to have some dimension with a non-zero coefficient
+ Rational lhs = cc.EvaluateLhs(ray);
+ if (lhs.IsPositive)
+ {
+ // the ray does not satisfy the inequality <=
+ if (cc.Relation == LinearConstraint.ConstraintRelation.LE)
+ {
+#if DEBUG_PRINT
+ Console.WriteLine("Widen checking: throwing out because of ray: {0}", ray);
+#endif
+ goto CHECK_NEXT_CONSTRAINT;
+ }
+ else
+ {
+ // ... but it does satisfy the inequality >=
+#if DEBUG_PRINT
+ Console.WriteLine("Widen checking: throwing out <= because of ray: {0}", ray);
+#endif
+ cc = cc.ChangeRelationToAtLeast();
+#if DEBUG_PRINT
+ Console.WriteLine("Widen checking: left with contraint: {0}", cc);
+#endif
+ }
+ }
+ else if (cc.Relation == LinearConstraint.ConstraintRelation.EQ && lhs.IsNegative)
+ {
+ // the ray does not satisfy the inequality >=, and the constraint is an equality constraint
+#if DEBUG_PRINT
+ Console.WriteLine("Widen checking: throwing out >= because of ray: {0}", ray);
+#endif
+ cc = cc.ChangeRelation(LinearConstraint.ConstraintRelation.LE);
+#if DEBUG_PRINT
+ Console.WriteLine("Widen checking: left with constraint: {0}", cc);
+#endif
+ }
+ }
+
+ // PHASE III: verify constraints against all frame lines...
+
+ foreach (FrameElement! line in (!)lcs.FrameLines)
+ {
+ // The following assumes the constraint to have some dimension with a non-zero coefficient
+ Rational lhs = cc.EvaluateLhs(line);
+ if (!lhs.IsZero)
+ {
+ // The line satisfies neither the inequality <= nor the equality ==
+#if DEBUG_PRINT
+ Console.WriteLine("Widen checking: throwing out because of line: {0}", line);
+#endif
+ goto CHECK_NEXT_CONSTRAINT;
+ }
+ }
+
+ // constraint has been verified, so add to new constraint system
+#if DEBUG_PRINT
+ Console.WriteLine("Widen checking: --Adding it!");
+#endif
+ newConstraints.Add(cc);
+
+ CHECK_NEXT_CONSTRAINT: {}
+#if DEBUG_PRINT
+ Console.WriteLine("Widen checking: done with that constraint");
+#endif
+ }
+
+ return new LinearConstraintSystem(newConstraints);
+ }
+
+#if DEBUG_PRINT
+ public LinearConstraintSystem Project(IVariable! dim)
+ {
+ Console.WriteLine("===================================================================================");
+ Console.WriteLine("DEBUG: Project(dim={0})", dim);
+ Console.WriteLine("Project: this=");
+ Dump();
+ LinearConstraintSystem z = ProjectX(dim);
+ Console.WriteLine("----------Project------------------------------>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
+ Console.WriteLine("Project: result=");
+ z.Dump();
+ Console.WriteLine("===================================================================================");
+ return z;
+ }
+#endif
+
+#if DEBUG_PRINT
+ public LinearConstraintSystem ProjectX(IVariable! dim)
+#else
+ public LinearConstraintSystem! Project(IVariable! dim)
+#endif
+ requires this.Constraints != null;
+ {
+ ArrayList /*LinearConstraint!*/! cc = Project(dim, Constraints);
+ return new LinearConstraintSystem(cc);
+ }
+
+#if DEBUG_PRINT
+ public LinearConstraintSystem Rename(IVariable! oldName, IVariable! newName)
+ {
+ Console.WriteLine("===================================================================================");
+ Console.WriteLine("DEBUG: Rename(oldName={0}, newName={1})", oldName, newName);
+ Console.WriteLine("Rename: this=");
+ Dump();
+ LinearConstraintSystem z = RenameX(oldName, newName);
+ Console.WriteLine("----------Rename------------------------------>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
+ Console.WriteLine("Rename: result=");
+ z.Dump();
+ Console.WriteLine("===================================================================================");
+ return z;
+ }
+#endif
+
+#if DEBUG_PRINT
+ public LinearConstraintSystem RenameX(IVariable! oldName, IVariable! newName)
+#else
+ public LinearConstraintSystem! Rename(IVariable! oldName, IVariable! newName)
+#endif
+ {
+ if (this.Constraints == null)
+ {
+ System.Diagnostics.Debug.Assert(this.FrameVertices == null);
+ System.Diagnostics.Debug.Assert(this.FrameRays == null);
+ System.Diagnostics.Debug.Assert(this.FrameLines == null);
+ return this;
+ }
+ IMutableSet /*IVariable!*/! dims = this.FrameDimensions;
+ if (!dims.Contains(oldName))
+ {
+ return this;
+ }
+
+ LinearConstraintSystem z = new LinearConstraintSystem();
+ z.FrameDimensions = (HashSet! /*IVariable!*/)dims.Clone();
+ z.FrameDimensions.Remove(oldName);
+ z.FrameDimensions.Add(newName);
+
+ z.Constraints = new ArrayList /*LinearConstraint!*/ (this.Constraints.Count);
+ foreach (LinearConstraint! lc in (!)this.Constraints)
+ {
+ z.Constraints.Add(lc.Rename(oldName, newName));
+ }
+ z.FrameVertices = RenameInFE((!)this.FrameVertices, oldName, newName);
+ z.FrameRays = RenameInFE((!)this.FrameRays, oldName, newName);
+ z.FrameLines = RenameInFE((!)this.FrameLines, oldName, newName);
+ return z;
+ }
+
+ static ArrayList /*FrameElement*/ RenameInFE(ArrayList! /*FrameElement*/ list, IVariable! oldName, IVariable! newName)
+ {
+ ArrayList/*FrameElement!*/! z = new ArrayList/*FrameElement!*/ (list.Count);
+ foreach (FrameElement! fe in list)
+ {
+ z.Add(fe.Rename(oldName, newName));
+ }
+ System.Diagnostics.Debug.Assert(z.Count == list.Count);
+ return z;
+ }
+
+ // --------------------------------------------------------------------------------------------------------
+ // ------------------ support routines --------------------------------------------------------------------
+ // --------------------------------------------------------------------------------------------------------
+
+ /// <summary>
+ /// Returns a set of constraints that is the given set of constraints with dimension "dim"
+ /// projected out. See Cousot and Halbwachs, section 3.3.1.1.
+ /// </summary>
+ /// <param name="dim"></param>
+ /// <param name="constraints"></param>
+ /// <returns></returns>
+ static ArrayList /*LinearConstraint!*/! Project(IVariable! dim, ArrayList /*LinearConstraint!*/! constraints)
+ {
+ // Sort the inequality constaints into ones where dimension "dim" is 0, negative, and
+ // positive, respectively. Put equality constraints with a non-0 "dim" into "eq".
+ ArrayList /*LinearConstraint!*/! final = new ArrayList /*LinearConstraint!*/ ();
+ ArrayList /*LinearConstraint!*/! negative = new ArrayList /*LinearConstraint!*/ ();
+ ArrayList /*LinearConstraint!*/! positive = new ArrayList /*LinearConstraint!*/ ();
+ ArrayList /*LinearConstraint!*/! eq = new ArrayList /*LinearConstraint!*/ ();
+ foreach (LinearConstraint! cc in constraints)
+ {
+ Rational coeff = cc[dim];
+ if (coeff.IsZero)
+ {
+ LinearConstraint lc = (!) cc.Clone();
+ if (!lc.IsConstant())
+ {
+ lc.RemoveDimension(dim);
+ final.Add(lc);
+ }
+ }
+ else if (cc.Relation == LinearConstraint.ConstraintRelation.EQ)
+ {
+ eq.Add(cc);
+ }
+ else if (coeff.IsNegative)
+ {
+ negative.Add(cc);
+ }
+ else
+ {
+ System.Diagnostics.Debug.Assert(coeff.IsPositive);
+ positive.Add(cc);
+ }
+ }
+
+ if (eq.Count != 0)
+ {
+ LinearConstraint eqConstraint = (LinearConstraint!)eq[eq.Count-1];
+ eq.RemoveAt(eq.Count-1);
+ Rational eqC = -eqConstraint[dim];
+
+ foreach (ArrayList /*LinearConstraint!*/! list in new ArrayList[]{eq, negative, positive})
+ {
+ foreach (LinearConstraint! lcX in list)
+ {
+ LinearConstraint lc = (!) lcX.Clone();
+ lc.AddMultiple(lc[dim]/eqC, eqConstraint);
+ System.Diagnostics.Debug.Assert(lc[dim].IsZero);
+ if (!lc.IsConstant())
+ {
+ lc.RemoveDimension(dim);
+ final.Add(lc);
+ }
+ else
+ {
+ System.Diagnostics.Debug.Assert(lc.IsConstantSatisfiable());
+ }
+ }
+ }
+ }
+ else
+ {
+ // Consider all pairs of constraints with (negative,positive) coefficients of "dim".
+ foreach (LinearConstraint! cn in negative)
+ {
+ Rational dn = -cn[dim];
+ System.Diagnostics.Debug.Assert(dn.IsNonNegative);
+ foreach (LinearConstraint! cp in positive)
+ {
+ Rational dp = cp[dim];
+
+ LinearConstraint lc = new LinearConstraint(LinearConstraint.ConstraintRelation.LE);
+ lc.AddMultiple(dn, cp);
+ lc.AddMultiple(dp, cn);
+ System.Diagnostics.Debug.Assert(lc[dim].IsZero);
+ if (!lc.IsConstant())
+ {
+ lc.RemoveDimension(dim);
+ final.Add(lc);
+ }
+ else
+ {
+ System.Diagnostics.Debug.Assert(lc.IsConstantSatisfiable());
+ }
+ }
+ }
+ }
+
+ return final;
+ }
+
+ /// <summary>
+ /// Initializes FrameVertices, FrameRays, FrameLines, and FrameDimensions, see
+ /// Cousot and Halbwachs, section 3.4. Any previous values of these fields are
+ /// ignored and overwritten.
+ ///
+ /// If the set of Constraints is unsatisfiable, then "this" is changed into Bottom.
+ /// </summary>
+ void GenerateFrameFromConstraints()
+ {
+ if (Constraints == null)
+ {
+ FrameVertices = null;
+ FrameRays = null;
+ FrameLines = null;
+ FrameDimensions = new HashSet /*IVariable!*/ ();
+ return;
+ }
+
+ // Step 1 (see Cousot and Halbwachs, section 3.4.3): create a Simplex Tableau.
+#if DEBUG_PRINT
+ Console.WriteLine("DEBUG: --- GenerateFrameFromConstraint ---");
+ Console.WriteLine("Constraints:");
+ foreach (LinearConstraint cc in Constraints)
+ {
+ Console.WriteLine(" {0}", cc);
+ }
+#endif
+ SimplexTableau tableau = new SimplexTableau(Constraints);
+#if DEBUG_PRINT
+ Console.WriteLine("Initial tableau:");
+ tableau.Dump();
+#endif
+ FrameDimensions = tableau.GetDimensions();
+#if DEBUG_PRINT
+ Console.WriteLine("Dimensions:");
+ foreach (object dim in FrameDimensions)
+ {
+ Console.Write(" {0}", dim);
+ }
+ Console.WriteLine();
+#endif
+
+ // Step 3 and 2: Put as many initial variables as possible into basis, then check if
+ // we reached a feasible basis
+ tableau.AddInitialVarsToBasis();
+#if DEBUG_PRINT
+ Console.WriteLine("Tableau after Step 3:");
+ tableau.Dump();
+#endif
+ if (!tableau.IsFeasibleBasis)
+ {
+ // The polyhedron is empty (according to Cousot and Halbwachs)
+ ChangeIntoBottom();
+ return;
+ }
+
+ FrameVertices = new ArrayList /*FrameElement*/ ();
+ FrameRays = new ArrayList /*FrameElement*/ ();
+ FrameLines = new ArrayList /*FrameElement*/ ();
+ if (FrameDimensions.Count == 0)
+ {
+ // top element
+ return;
+ }
+
+ if (tableau.AllInitialVarsInBasis)
+ {
+ // All initial variables are in basis; there are no lines.
+#if DEBUG_PRINT
+ Console.WriteLine("Tableau after Steps 2 and 3 (all initial variables in basis):");
+ tableau.Dump();
+#endif
+ }
+ else
+ {
+ // There are lines
+#if DEBUG_PRINT
+ Console.WriteLine("Tableau after Steps 2 and 3 (NOT all initial variables in basis--there are lines):");
+ tableau.Dump();
+#endif
+ // Step 4.2: Pick out the lines, then produce the tableau for a new polyhedron without those lines.
+ ArrayList /*LinearConstraint*/ moreConstraints = (ArrayList! /*LinearConstraint*/) Constraints.Clone();
+ tableau.ProduceLines(FrameLines, moreConstraints);
+ tableau = new SimplexTableau(moreConstraints);
+#if DEBUG_PRINT
+ Console.WriteLine("Lines produced:");
+ foreach (FrameElement line in FrameLines)
+ {
+ Console.WriteLine(" {0}", line);
+ }
+ Console.WriteLine("The new list of constraints is:");
+ foreach (LinearConstraint c in moreConstraints)
+ {
+ Console.WriteLine(" {0}", c);
+ }
+ Console.WriteLine("Tableau after producing lines in Step 4.2:");
+ tableau.Dump();
+#endif
+
+ // Repeat step 3 for the new tableau.
+ // Since the new tableau contains no lines, the following call should cause all initial
+ // variables to be in basis (see step 4.2 in section 3.4.3 of Cousot and Halbwachs).
+ tableau.AddInitialVarsToBasis();
+ System.Diagnostics.Debug.Assert(tableau.AllInitialVarsInBasis);
+ System.Diagnostics.Debug.Assert(tableau.IsFeasibleBasis); // the new tableau represents a set of feasible constraints, so this basis should be found to be feasible
+#if DEBUG_PRINT
+ Console.WriteLine("Tableau after all initial variables have been moved into basis:");
+ tableau.Dump();
+#endif
+ }
+
+ // Step 4.1: One vertex has been found. Find all others, too.
+ tableau.TraverseVertices(FrameVertices, FrameRays);
+#if DEBUG_PRINT
+ Console.WriteLine("Tableau after vertex traversal:");
+ tableau.Dump();
+#endif
+ }
+
+ class LambdaDimension : IVariable
+ {
+ readonly int id;
+ static int count = 0;
+
+ /// <summary>
+ /// Return the name of the variable
+ /// </summary>
+ public string! Name
+ {
+ get
+ {
+ return this.ToString();
+ }
+ }
+
+ public LambdaDimension()
+ {
+ id = count;
+ count++;
+ }
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public override string! ToString()
+ {
+ return "lambda" + id;
+ }
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public object DoVisit(ExprVisitor! visitor)
+ {
+ return visitor.VisitVariable(this);
+ }
+ }
+
+ /// <summary>
+ /// Adds a vertex to the frame of "this" and updates Constraints accordingly, see
+ /// Cousot and Halbwachs, section 3.3.1.1. However, this method does not simplify
+ /// Constraints after the operation; that remains the caller's responsibility (which
+ /// gives the caller the opportunity to make multiple calls to AddVertex, AddRay,
+ /// and AddLine before calling SimplifyConstraints).
+ /// Assumes Constraints (and the frame fields) to be non-null.
+ /// </summary>
+ /// <param name="vertex"></param>
+ void AddVertex(FrameElement! vertex)
+ requires this.FrameVertices != null;
+ {
+#if DEBUG_PRINT
+ Console.WriteLine("DEBUG: AddVertex called on {0}", vertex);
+ Console.WriteLine(" Initial constraints:");
+ foreach (LinearConstraint cc in Constraints) {
+ Console.WriteLine(" {0}", cc);
+ }
+#endif
+
+ FrameVertices.Add(vertex.Clone());
+#if FIXED_DESERIALIZER
+ assert Forall{IVariable! var in vertex.GetDefinedDimensions(); FrameDimensions.Contains(var)};
+#endif
+
+ // We use a new temporary dimension.
+ IVariable! lambda = new LambdaDimension();
+
+ // We change the constraints A*X <= B into
+ // A*X + (A*vector - B)*lambda <= A*vector.
+ // That means that each row k in A (which corresponds to one LinearConstraint
+ // in Constraints) is changed by adding
+ // (A*vector - B)[k] * lambda
+ // to row k and changing the right-hand side of row k to
+ // (A*vector)[k]
+ // Note:
+ // (A*vector - B)[k]
+ // = { vector subtraction is pointwise }
+ // (A*vector)[k] - B[k]
+ // = { A*vector is a row vector whose every row i is the dot-product of
+ // row i of A with the column vector "vector" }
+ // A[k]*vector - B[k]
+ foreach (LinearConstraint! cc in (!)Constraints)
+ {
+ Rational d = cc.EvaluateLhs(vertex);
+ cc.SetCoefficient(lambda, d - cc.rhs);
+ cc.rhs = d;
+ }
+
+ // We also add the constraints that lambda lies between 0 ...
+ LinearConstraint la = new LinearConstraint(LinearConstraint.ConstraintRelation.LE);
+ la.SetCoefficient(lambda, Rational.MINUS_ONE);
+ la.rhs = Rational.ZERO;
+ Constraints.Add(la);
+ // ... and 1.
+ la = new LinearConstraint(LinearConstraint.ConstraintRelation.LE);
+ la.SetCoefficient(lambda, Rational.ONE);
+ la.rhs = Rational.ONE;
+ Constraints.Add(la);
+#if DEBUG_PRINT
+ Console.WriteLine(" Constraints after addition:");
+ foreach (LinearConstraint cc in Constraints) {
+ Console.WriteLine(" {0}", cc);
+ }
+#endif
+
+ // Finally, project out the dummy dimension.
+ Constraints = Project(lambda, Constraints);
+
+#if DEBUG_PRINT
+ Console.WriteLine(" Resulting constraints:");
+ foreach (LinearConstraint cc in Constraints) {
+ Console.WriteLine(" {0}", cc);
+ }
+#endif
+ }
+
+ /// <summary>
+ /// Adds a ray to the frame of "this" and updates Constraints accordingly, see
+ /// Cousot and Halbwachs, section 3.3.1.1. However, this method does not simplify
+ /// Constraints after the operation; that remains the caller's responsibility (which
+ /// gives the caller the opportunity to make multiple calls to AddVertex, AddRay,
+ /// and AddLine before calling SimplifyConstraints).
+ /// Assumes Constraints (and the frame fields) to be non-null.
+ /// </summary>
+ /// <param name="ray"></param>
+ void AddRay(FrameElement! ray)
+ requires this.FrameRays != null;
+ {
+#if DEBUG_PRINT
+ Console.WriteLine("DEBUG: AddRay called on {0}", ray);
+ Console.WriteLine(" Initial constraints:");
+ foreach (LinearConstraint cc in Constraints) {
+ Console.WriteLine(" {0}", cc);
+ }
+#endif
+
+ FrameRays.Add(ray.Clone());
+#if FIXED_DESERIALIZER
+ assert Forall{IVariable! var in ray.GetDefinedDimensions(); FrameDimensions.Contains(var)};
+#endif
+
+ // We use a new temporary dimension.
+ IVariable! lambda = new LambdaDimension();
+
+ // We change the constraints A*X <= B into
+ // A*X - (A*ray)*lambda <= B.
+ // That means that each row k in A (which corresponds to one LinearConstraint
+ // in Constraints) is changed by subtracting
+ // (A*ray)[k] * lambda
+ // from row k.
+ // Note:
+ // (A*ray)[k]
+ // = { A*ray is a row vector whose every row i is the dot-product of
+ // row i of A with the column vector "ray" }
+ // A[k]*ray
+ foreach (LinearConstraint! cc in (!)Constraints)
+ {
+ Rational d = cc.EvaluateLhs(ray);
+ cc.SetCoefficient(lambda, -d);
+ }
+
+ // We also add the constraints that lambda is at least 0.
+ LinearConstraint la = new LinearConstraint(LinearConstraint.ConstraintRelation.LE);
+ la.SetCoefficient(lambda, Rational.MINUS_ONE);
+ la.rhs = Rational.ZERO;
+ Constraints.Add(la);
+#if DEBUG_PRINT
+ Console.WriteLine(" Constraints after addition:");
+ foreach (LinearConstraint cc in Constraints) {
+ Console.WriteLine(" {0}", cc);
+ }
+#endif
+
+ // Finally, project out the dummy dimension.
+ Constraints = Project(lambda, Constraints);
+
+#if DEBUG_PRINT
+ Console.WriteLine(" Resulting constraints:");
+ foreach (LinearConstraint cc in Constraints) {
+ Console.WriteLine(" {0}", cc);
+ }
+#endif
+ }
+
+ /// <summary>
+ /// Adds a line to the frame of "this" and updates Constraints accordingly, see
+ /// Cousot and Halbwachs, section 3.3.1.1. However, this method does not simplify
+ /// Constraints after the operation; that remains the caller's responsibility (which
+ /// gives the caller the opportunity to make multiple calls to AddVertex, AddRay,
+ /// and AddLine before calling SimplifyConstraints).
+ /// Assumes Constraints (and the frame fields) to be non-null.
+ /// </summary>
+ /// <param name="line"></param>
+ void AddLine(FrameElement! line)
+ requires this.FrameLines != null;
+ {
+ // Note: The code for AddLine is identical to that of AddRay, except the AddLine
+ // does not introduce the constraint 0 <= lambda. (One could imagine sharing the
+ // code between AddRay and AddLine.)
+#if DEBUG_PRINT
+ Console.WriteLine("DEBUG: AddLine called on {0}", line);
+ Console.WriteLine(" Initial constraints:");
+ foreach (LinearConstraint cc in Constraints) {
+ Console.WriteLine(" {0}", cc);
+ }
+#endif
+
+ FrameLines.Add(line.Clone());
+#if FIXED_DESERIALIZER
+ assert Forall{IVariable! var in line.GetDefinedDimensions(); FrameDimensions.Contains(var)};
+#endif
+
+ // We use a new temporary dimension.
+ IVariable! lambda = new LambdaDimension();
+
+ // We change the constraints A*X <= B into
+ // A*X - (A*line)*lambda <= B.
+ // That means that each row k in A (which corresponds to one LinearConstraint
+ // in Constraints) is changed by subtracting
+ // (A*line)[k] * lambda
+ // from row k.
+ // Note:
+ // (A*line)[k]
+ // = { A*line is a row vector whose every row i is the dot-product of
+ // row i of A with the column vector "line" }
+ // A[k]*line
+ foreach (LinearConstraint! cc in (!)Constraints)
+ {
+ Rational d = cc.EvaluateLhs(line);
+ cc.SetCoefficient(lambda, -d);
+ }
+
+#if DEBUG_PRINT
+ Console.WriteLine(" Constraints after addition:");
+ foreach (LinearConstraint cc in Constraints) {
+ Console.WriteLine(" {0}", cc);
+ }
+#endif
+
+ // Finally, project out the dummy dimension.
+ Constraints = Project(lambda, Constraints);
+
+#if DEBUG_PRINT
+ Console.WriteLine(" Resulting constraints:");
+ foreach (LinearConstraint cc in Constraints) {
+ Console.WriteLine(" {0}", cc);
+ }
+#endif
+ }
+
+ ISet /*IVariable!*/! GetDefinedDimensions()
+ {
+ HashSet /*IVariable!*/! dims = new HashSet /*IVariable!*/ ();
+ foreach (ArrayList p in new ArrayList[]{FrameVertices, FrameRays, FrameLines})
+ {
+ if (p != null)
+ {
+ foreach (FrameElement! element in p)
+ {
+ foreach (IVariable! dim in element.GetDefinedDimensions())
+ {
+ dims.Add(dim);
+ }
+ }
+ }
+ }
+ return dims;
+ }
+
+ // --------------------------------------------------------------------------------------------------------
+ // ------------------ Simplification routines -------------------------------------------------------------
+ // --------------------------------------------------------------------------------------------------------
+
+ /// <summary>
+ /// Uses the Constraints to simplify the frame. See section 3.4.4 of Cousot and Halbwachs.
+ /// </summary>
+ void SimplifyFrame()
+ requires this.Constraints != null;
+ {
+ SimplificationStatus[]! status;
+
+ SimplifyFrameElements((!)FrameVertices, true, Constraints, out status);
+ RemoveIrrelevantFrameElements(FrameVertices, status, null);
+
+ SimplifyFrameElements((!)FrameRays, false, Constraints, out status);
+ RemoveIrrelevantFrameElements(FrameRays, status, FrameLines);
+ }
+
+ enum SimplificationStatus { Irrelevant, Relevant, More };
+
+ /// <summary>
+ /// For each i, sets status[i] to:
+ /// <ul>
+ /// <li>Irrelevant if ff[i] is irrelevant</li>
+ /// <li>Relevant if ff[i] is irrelevant</li>
+ /// <li>More if vertices is true and ray ff[i] can be replaced by a line ff[i]</li>
+ /// </ul>
+ /// </summary>
+ /// <param name="ff"></param>
+ /// <param name="vertices">true if "ff" contains vertices; false if "ff" contains rays</param>
+ /// <param name="constraints"></param>
+ /// <param name="status"></param>
+ static void SimplifyFrameElements(ArrayList! /*FrameElement*/ ff, bool vertices,
+ ArrayList! /*LinearConstraint*/ constraints,
+ out SimplificationStatus[]! status)
+ {
+ status = new SimplificationStatus[ff.Count];
+ bool[,] sat = new bool[ff.Count, constraints.Count];
+ for (int i = 0; i < ff.Count; i++)
+ {
+ FrameElement f = (FrameElement!)ff[i];
+ int cnt = 0;
+ for (int c = 0; c < constraints.Count; c++)
+ {
+ LinearConstraint lc = (LinearConstraint!)constraints[c];
+ bool s = lc.IsSaturatedBy(f, vertices);
+ if (s)
+ {
+ sat[i,c] = true;
+ cnt++;
+ }
+ }
+ if (!vertices && cnt == constraints.Count)
+ {
+ status[i] = SimplificationStatus.More;
+ }
+ else
+ {
+ status[i] = SimplificationStatus.Relevant;
+ }
+ }
+
+ CheckPairSimplifications(sat, status);
+ }
+
+ /// <summary>
+ /// Requires sat.GetLength(0) == status.Length.
+ /// </summary>
+ /// <param name="sat"></param>
+ /// <param name="status"></param>
+ static void CheckPairSimplifications(bool[,]! sat, SimplificationStatus[]! status)
+ requires sat.GetLength(0) == status.Length;
+ {
+ int M = sat.GetLength(0);
+ int N = sat.GetLength(1);
+
+ for (int i = 0; i < M-1; i++)
+ {
+ if (status[i] != SimplificationStatus.Relevant)
+ {
+ continue;
+ }
+ for (int j = i+1; j < M; j++)
+ {
+ if (status[j] != SimplificationStatus.Relevant)
+ {
+ continue;
+ }
+ // check (sat[i,*] <= sat[j,*]) and (sat[i,*] >= sat[j,*])
+ int cmp = 0; // -1: (sat[i,*] <= sat[j,*]), 0: equal, 1: (sat[i,*] >= sat[j,*])
+ for (int c = 0; c < N; c++)
+ {
+ if (cmp < 0)
+ {
+ if (sat[i,c] && !sat[j,c])
+ {
+ // incomparable
+ goto NEXT_PAIR;
+ }
+ }
+ else if (0 < cmp)
+ {
+ if (!sat[i,c] && sat[j,c])
+ {
+ // incomparable
+ goto NEXT_PAIR;
+ }
+ }
+ else if (sat[i,c] != sat[j,c])
+ {
+ if (!sat[i,c])
+ {
+ cmp = -1;
+ }
+ else
+ {
+ cmp = 1;
+ }
+ }
+ }
+ if (cmp <= 0)
+ {
+ // sat[i,*] <= sat[j,*] holds, so mark i as irrelevant
+ status[i] = SimplificationStatus.Irrelevant;
+ goto NEXT_OUTER;
+ }
+ else
+ {
+ // sat[i,*] >= sat[j,*] holds, so mark j as irrelevant
+ status[j] = SimplificationStatus.Irrelevant;
+ }
+ NEXT_PAIR: {}
+ }
+ NEXT_OUTER: {}
+ }
+ }
+
+ static void RemoveIrrelevantFrameElements(ArrayList! /*FrameElement*/ ff, SimplificationStatus[]! status,
+ /*maybe null*/ ArrayList /*FrameElement*/ lines)
+ requires ff.Count == status.Length;
+ {
+ for (int j = ff.Count - 1; 0 <= j; j--)
+ {
+ switch (status[j])
+ {
+ case SimplificationStatus.Relevant:
+ break;
+ case SimplificationStatus.Irrelevant:
+#if DEBUG_PRINT
+ Console.WriteLine("Removing irrelevant {0}: {1}", lines == null ? "vertex" : "ray", ff[j]);
+#endif
+ ff.RemoveAt(j);
+ break;
+ case SimplificationStatus.More:
+ System.Diagnostics.Debug.Assert(lines != null);
+ FrameElement f = (FrameElement)ff[j];
+#if DEBUG_PRINT
+ Console.WriteLine("Changing ray into line: {0}", f);
+#endif
+ ff.RemoveAt(j);
+ assert lines != null;
+ lines.Add(f);
+ break;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Uses the frame to simplify Constraints. See section 3.3.1.2 of Cousot and Halbwachs.
+ ///
+ /// Note: This code does not necessarily eliminate all irrelevant equalities; Cousot and
+ /// Halbwachs only claim that the technique eliminates all irrelevant inequalities.
+ /// </summary>
+ void SimplifyConstraints()
+ {
+ if (Constraints == null)
+ {
+ return;
+ }
+ assume this.FrameVertices != null;
+ assume this.FrameRays != null;
+
+ SimplificationStatus[] status = new SimplificationStatus[Constraints.Count];
+ /*readonly*/ int feCount = FrameVertices.Count + FrameRays.Count;
+
+ // Create a table that keeps track of which constraints are satisfied by which vertices and rays
+ bool[,] sat = new bool[Constraints.Count, FrameVertices.Count + FrameRays.Count];
+ for (int i = 0; i < Constraints.Count; i++)
+ {
+ status[i] = SimplificationStatus.Relevant;
+ LinearConstraint lc = (LinearConstraint!)Constraints[i];
+ int cnt = 0; // number of vertices and rays that saturate lc
+ for (int j = 0; j < FrameVertices.Count; j++)
+ {
+ FrameElement vertex = (FrameElement!)FrameVertices[j];
+ if (lc.IsSaturatedBy(vertex, true))
+ {
+ sat[i,j] = true;
+ cnt++;
+ }
+ }
+ if (cnt == 0)
+ {
+ // no vertex saturates the constraint, so the constraint is irrelevant
+ status[i] = SimplificationStatus.Irrelevant;
+ continue;
+ }
+ for (int j = 0; j < FrameRays.Count; j++)
+ {
+ FrameElement ray = (FrameElement!)FrameRays[j];
+ if (lc.IsSaturatedBy(ray, false))
+ {
+ sat[i, FrameVertices.Count + j] = true;
+ cnt++;
+ }
+ }
+ if (cnt == feCount)
+ {
+ status[i] = SimplificationStatus.More;
+ }
+ else
+ {
+ // Cousot and Halbwachs says that all equalities are found in the way we just tested.
+ // If I understand that right, then we should not get here if the constraint is an
+ // equality constraint. The following assertion tests my understanding. --KRML
+ System.Diagnostics.Debug.Assert(lc.Relation == LinearConstraint.ConstraintRelation.LE);
+ }
+ }
+
+ CheckPairSimplifications(sat, status);
+
+ // Finally, make the changes to the list of constraints
+ for (int i = Constraints.Count - 1; 0 <= i; i--)
+ {
+ switch (status[i])
+ {
+ case SimplificationStatus.Relevant:
+ break;
+ case SimplificationStatus.Irrelevant:
+#if DEBUG_PRINT
+ Console.WriteLine("Removing irrelevant constraint: {0}", Constraints[i]);
+#endif
+ Constraints.RemoveAt(i);
+ break;
+ case SimplificationStatus.More:
+ LinearConstraint lc = (LinearConstraint!)Constraints[i];
+ if (lc.Relation == LinearConstraint.ConstraintRelation.LE)
+ {
+#if DEBUG_PRINT
+ Console.WriteLine("Converting the following constraint into an equality: {0}", lc);
+#endif
+ LinearConstraint lcEq = lc.ChangeRelation(LinearConstraint.ConstraintRelation.EQ);
+ Constraints[i] = lcEq;
+ }
+ break;
+ }
+ }
+
+ foreach (LinearConstraint! lc in Constraints) {
+ lc.Normalize();
+ }
+ }
+
+ // --------------------------------------------------------------------------------------------------------
+ // ------------------ Cloning routines --------------------------------------------------------------------
+ // --------------------------------------------------------------------------------------------------------
+
+ public LinearConstraintSystem! Clone()
+ {
+ LinearConstraintSystem z = new LinearConstraintSystem();
+ z.FrameDimensions = (IMutableSet /*IVariable!*/!)this.FrameDimensions.Clone();
+ if (this.Constraints != null)
+ {
+ z.Constraints = DeeperListCopy_LC(this.Constraints);
+ z.FrameVertices = DeeperListCopy_FE((!)this.FrameVertices);
+ z.FrameRays = DeeperListCopy_FE((!)this.FrameRays);
+ z.FrameLines = DeeperListCopy_FE((!)this.FrameLines);
+ }
+ else
+ {
+ System.Diagnostics.Debug.Assert(this.FrameVertices == null);
+ System.Diagnostics.Debug.Assert(this.FrameRays == null);
+ System.Diagnostics.Debug.Assert(this.FrameLines == null);
+ // the constructor should already have set these fields of z to null
+ System.Diagnostics.Debug.Assert(z.Constraints == null);
+ System.Diagnostics.Debug.Assert(z.FrameVertices == null);
+ System.Diagnostics.Debug.Assert(z.FrameRays == null);
+ System.Diagnostics.Debug.Assert(z.FrameLines == null);
+ }
+ return z;
+ }
+
+ /// <summary>
+ /// Clones "list" and the elements of "list".
+ /// </summary>
+ /// <param name="list"></param>
+ /// <returns></returns>
+ ArrayList /*LinearConstraint*/ DeeperListCopy_LC(ArrayList! /*LinearConstraint*/ list)
+ {
+ ArrayList /*LinearConstraint*/ z = new ArrayList /*LinearConstraint*/ (list.Count);
+ foreach (LinearConstraint! lc in list)
+ {
+ z.Add(lc.Clone());
+ }
+ System.Diagnostics.Debug.Assert(z.Count == list.Count);
+ return z;
+ }
+
+ /// <summary>
+ /// Clones "list" and the elements of "list".
+ /// </summary>
+ /// <param name="list"></param>
+ /// <returns></returns>
+ ArrayList /*FrameElement*/ DeeperListCopy_FE(ArrayList! /*FrameElement*/ list)
+ {
+ ArrayList /*FrameElement*/ z = new ArrayList /*FrameElement*/ (list.Count);
+ foreach (FrameElement! fe in list)
+ {
+ z.Add(fe.Clone());
+ }
+ System.Diagnostics.Debug.Assert(z.Count == list.Count);
+ return z;
+ }
+
+ // --------------------------------------------------------------------------------------------------------
+ // ------------------ Debugging and unit test routines ----------------------------------------------------
+ // --------------------------------------------------------------------------------------------------------
+
+ public void Dump()
+ {
+ Console.WriteLine(" Constraints:");
+ if (Constraints == null)
+ {
+ Console.WriteLine(" <bottom>");
+ }
+ else
+ {
+ foreach (LinearConstraint cc in Constraints)
+ {
+ Console.WriteLine(" {0}", cc);
+ }
+ }
+
+ Console.WriteLine(" FrameDimensions: {0}", FrameDimensions);
+
+ Console.WriteLine(" FrameVerticies:");
+ if (FrameVertices == null)
+ {
+ Console.WriteLine(" <null>");
+ }
+ else
+ {
+ foreach (FrameElement fe in FrameVertices)
+ {
+ Console.WriteLine(" {0}", fe);
+ }
+ }
+
+ Console.WriteLine(" FrameRays:");
+ if (FrameRays == null)
+ {
+ Console.WriteLine(" <null>");
+ }
+ else
+ {
+ foreach (FrameElement fe in FrameRays)
+ {
+ Console.WriteLine(" {0}", fe);
+ }
+ }
+
+ Console.WriteLine(" FrameLines:");
+ if (FrameLines == null)
+ {
+ Console.WriteLine(" <null>");
+ }
+ else
+ {
+ foreach (FrameElement fe in FrameLines)
+ {
+ Console.WriteLine(" {0}", fe);
+ }
+ }
+ }
+
+ class TestVariable : IVariable {
+ readonly string! name;
+
+ public string! Name
+ {
+ get
+ {
+ return name;
+ }
+ }
+
+ public TestVariable(string! name) {
+ this.name = name;
+ }
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public object DoVisit(ExprVisitor! visitor) {
+ return visitor.VisitVariable(this);
+ }
+ }
+
+ public static void RunValidationA()
+ {
+ IVariable! dim1 = new TestVariable("X");
+ IVariable! dim2 = new TestVariable("Y");
+ IVariable! dim3 = new TestVariable("Z");
+
+ FrameElement s1 = new FrameElement();
+ s1.AddCoordinate(dim1, Rational.ONE);
+ s1.AddCoordinate(dim2, Rational.MINUS_ONE);
+ s1.AddCoordinate(dim3, Rational.ZERO);
+ FrameElement s2 = new FrameElement();
+ s2.AddCoordinate(dim1, Rational.MINUS_ONE);
+ s2.AddCoordinate(dim2, Rational.ONE);
+ s2.AddCoordinate(dim3, Rational.ZERO);
+ FrameElement r1 = new FrameElement();
+ r1.AddCoordinate(dim1, Rational.ZERO);
+ r1.AddCoordinate(dim2, Rational.ZERO);
+ r1.AddCoordinate(dim3, Rational.ONE);
+ FrameElement d1 = new FrameElement();
+ d1.AddCoordinate(dim1, Rational.ONE);
+ d1.AddCoordinate(dim2, Rational.ONE);
+ d1.AddCoordinate(dim3, Rational.ZERO);
+
+ // create lcs from frame -- cf. Cousot/Halbwachs 1978, section 3.3.1.1
+ LinearConstraintSystem lcs = new LinearConstraintSystem(s1);
+ lcs.Dump();
+
+ lcs.AddVertex(s2);
+ lcs.Dump();
+
+ lcs.AddRay(r1);
+ lcs.Dump();
+
+ lcs.AddLine(d1);
+ lcs.Dump();
+
+ lcs.SimplifyConstraints();
+ lcs.Dump();
+
+#if LATER
+ lcs.GenerateFrameFromConstraints(); // should give us back the original frame...
+#endif
+ Console.WriteLine("IsSubset? {0}", lcs.IsSubset(lcs.Clone()));
+ lcs.Dump();
+ }
+
+ /// <summary>
+ /// Tests the example in section 3.4.3 of Cousot and Halbwachs.
+ /// </summary>
+ public static void RunValidationB()
+ {
+ IVariable! X = new TestVariable("X");
+ IVariable! Y = new TestVariable("Y");
+ IVariable! Z = new TestVariable("Z");
+ ArrayList /*LinearConstraint*/ cs = new ArrayList /*LinearConstraint*/ ();
+
+ LinearConstraint c = new LinearConstraint(LinearConstraint.ConstraintRelation.LE);
+ c.SetCoefficient(X, Rational.MINUS_ONE);
+ c.SetCoefficient(Y, Rational.ONE);
+ c.SetCoefficient(Z, Rational.MINUS_ONE);
+ c.rhs = Rational.ZERO;
+ cs.Add(c);
+
+ c = new LinearConstraint(LinearConstraint.ConstraintRelation.LE);
+ c.SetCoefficient(X, Rational.MINUS_ONE);
+ c.rhs = Rational.MINUS_ONE;
+ cs.Add(c);
+
+ c = new LinearConstraint(LinearConstraint.ConstraintRelation.LE);
+ c.SetCoefficient(X, Rational.MINUS_ONE);
+ c.SetCoefficient(Y, Rational.MINUS_ONE);
+ c.SetCoefficient(Z, Rational.ONE);
+ c.rhs = Rational.ZERO;
+ cs.Add(c);
+
+ c = new LinearConstraint(LinearConstraint.ConstraintRelation.LE);
+ c.SetCoefficient(Y, Rational.MINUS_ONE);
+ c.SetCoefficient(Z, Rational.ONE);
+ c.rhs = Rational.FromInt(3);
+ cs.Add(c);
+
+ LinearConstraintSystem lcs = new LinearConstraintSystem(cs);
+ Console.WriteLine("==================== The final linear constraint system ====================");
+ lcs.Dump();
+ }
+
+ public static void RunValidationC()
+ {
+ // Run the example in section 3.4.3 of Cousot and Halbwachs backwards, that is, from
+ // from to constraints.
+ IVariable! dim1 = new TestVariable("X");
+ IVariable! dim2 = new TestVariable("Y");
+ IVariable! dim3 = new TestVariable("Z");
+
+ FrameElement s0 = new FrameElement();
+ s0.AddCoordinate(dim1, Rational.ONE);
+ s0.AddCoordinate(dim2, Rational.FromInts(1, 2));
+ s0.AddCoordinate(dim3, Rational.FromInts(-1, 2));
+
+ FrameElement s1 = new FrameElement();
+ s1.AddCoordinate(dim1, Rational.ONE);
+ s1.AddCoordinate(dim2, Rational.FromInts(-1, 2));
+ s1.AddCoordinate(dim3, Rational.FromInts(1, 2));
+
+ FrameElement s2 = new FrameElement();
+ s2.AddCoordinate(dim1, Rational.FromInt(3));
+ s2.AddCoordinate(dim2, Rational.FromInts(-3, 2));
+ s2.AddCoordinate(dim3, Rational.FromInts(3, 2));
+
+ FrameElement r0 = new FrameElement();
+ r0.AddCoordinate(dim1, Rational.ONE);
+ r0.AddCoordinate(dim2, Rational.FromInts(1, 2));
+ r0.AddCoordinate(dim3, Rational.FromInts(-1, 2));
+
+ FrameElement r1 = new FrameElement();
+ r1.AddCoordinate(dim1, Rational.ONE);
+ r1.AddCoordinate(dim2, Rational.ZERO);
+ r1.AddCoordinate(dim3, Rational.ZERO);
+
+ FrameElement d0 = new FrameElement();
+ d0.AddCoordinate(dim1, Rational.ZERO);
+ d0.AddCoordinate(dim2, Rational.ONE);
+ d0.AddCoordinate(dim3, Rational.ONE);
+
+ LinearConstraintSystem lcs = new LinearConstraintSystem(s0);
+ lcs.Dump();
+
+ lcs.AddVertex(s1);
+ lcs.Dump();
+
+ lcs.AddVertex(s2);
+ lcs.Dump();
+
+ lcs.AddRay(r0);
+ lcs.Dump();
+
+ lcs.AddRay(r1);
+ lcs.Dump();
+
+ lcs.AddLine(d0);
+ lcs.Dump();
+
+ lcs.SimplifyConstraints();
+ lcs.Dump();
+
+#if LATER
+ lcs.GenerateFrameFromConstraints(); // should give us back the original frame...
+#endif
+ }
+ }
+} \ No newline at end of file
diff --git a/Source/AIFramework/Polyhedra/PolyhedraAbstraction.ssc b/Source/AIFramework/Polyhedra/PolyhedraAbstraction.ssc
new file mode 100644
index 00000000..bcf9c64d
--- /dev/null
+++ b/Source/AIFramework/Polyhedra/PolyhedraAbstraction.ssc
@@ -0,0 +1,744 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+namespace Microsoft.AbstractInterpretationFramework
+{
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using Microsoft.Contracts;
+ using Microsoft.Basetypes;
+
+ using ISet = Microsoft.Boogie.Set;
+ using HashSet = Microsoft.Boogie.Set;
+
+
+ /// <summary>
+ /// Represents an invariant over linear variable constraints, represented by a polyhedron.
+ /// </summary>
+ public class PolyhedraLattice : Lattice
+ {
+ private static readonly Logger! log = new Logger("Polyhedra");
+
+ private class PolyhedraLatticeElement : Element
+ {
+
+ public LinearConstraintSystem! lcs;
+
+ /// <summary>
+ /// Creates a top or bottom elements, according to parameter "top".
+ /// </summary>
+ public PolyhedraLatticeElement (bool top)
+ {
+ if (top)
+ {
+ lcs = new LinearConstraintSystem(new ArrayList /*LinearConstraint*/ ());
+ }
+ else
+ {
+ lcs = new LinearConstraintSystem();
+ }
+ }
+
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public override string! ToString()
+ {
+ return lcs.ToString();
+ }
+
+ public override void Dump(string! msg) {
+ System.Console.WriteLine("PolyhedraLatticeElement.Dump({0}):", msg);
+ lcs.Dump();
+ }
+
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public override ICollection<IVariable!>! FreeVariables()
+ {
+ return lcs.FreeVariables();
+ }
+
+ public PolyhedraLatticeElement (LinearConstraintSystem! lcs)
+ {
+ this.lcs = lcs;
+ }
+
+ public override Element! Clone ()
+ {
+ return new PolyhedraLatticeElement( (!) lcs.Clone());
+ }
+
+ } // class
+
+ readonly ILinearExprFactory! factory;
+ readonly IPropExprFactory! propFactory;
+
+ public PolyhedraLattice(ILinearExprFactory! linearFactory, IPropExprFactory! propFactory)
+ : base(linearFactory)
+ {
+ log.Enabled = Lattice.LogSwitch;
+ this.factory = linearFactory;
+ this.propFactory = propFactory;
+ // base(linearFactory);
+ }
+
+ public override Element! Top
+ {
+ get
+ {
+ return new PolyhedraLatticeElement(true);
+ }
+ }
+
+ public override Element! Bottom
+ {
+ get
+ {
+ return new PolyhedraLatticeElement(false);
+ }
+ }
+
+ public override bool IsBottom (Element! element)
+ {
+ PolyhedraLatticeElement e = (PolyhedraLatticeElement)element;
+ return e.lcs.IsBottom();
+ }
+
+ public override bool IsTop (Element! element)
+ {
+ PolyhedraLatticeElement e = (PolyhedraLatticeElement)element;
+ return e.lcs.IsTop();
+ }
+
+
+ /// <summary>
+ /// Returns true iff a is a subset of this.
+ /// </summary>
+ /// <param name="a"></param>
+ /// <returns></returns>
+ protected override bool AtMost (Element! first, Element! second) // this <= that
+ {
+ PolyhedraLatticeElement a = (PolyhedraLatticeElement) first;
+ PolyhedraLatticeElement b = (PolyhedraLatticeElement) second;
+ return b.lcs.IsSubset(a.lcs);
+ }
+
+
+ public override string! ToString (Element! e)
+ {
+ return ((PolyhedraLatticeElement)e).lcs.ToString();
+ }
+
+ public override IExpr! ToPredicate(Element! element)
+ {
+ PolyhedraLatticeElement e = (PolyhedraLatticeElement)element;
+ return e.lcs.ConvertToExpression(factory);
+ }
+
+
+
+ public override Lattice.Element! NontrivialJoin (Element! first, Element! second)
+ {
+ log.DbgMsg("Joining ..."); log.DbgMsgIndent();
+ PolyhedraLatticeElement aa = (PolyhedraLatticeElement) first;
+ PolyhedraLatticeElement bb = (PolyhedraLatticeElement) second;
+ PolyhedraLatticeElement result = new PolyhedraLatticeElement(aa.lcs.Join(bb.lcs));
+ log.DbgMsg(string.Format("{0} |_| {1} --> {2}", this.ToString(first), this.ToString(second), this.ToString(result)));
+ log.DbgMsgUnindent();
+ return result;
+ }
+
+
+ public override Lattice.Element! NontrivialMeet (Element! first, Element! second)
+ {
+ PolyhedraLatticeElement aa = (PolyhedraLatticeElement) first;
+ PolyhedraLatticeElement bb = (PolyhedraLatticeElement) second;
+ return new PolyhedraLatticeElement(aa.lcs.Meet(bb.lcs));
+ }
+
+
+ public override Lattice.Element! Widen (Element! first, Element! second)
+ {
+ log.DbgMsg("Widening ..."); log.DbgMsgIndent();
+ PolyhedraLatticeElement aa = (PolyhedraLatticeElement)first;
+ PolyhedraLatticeElement bb = (PolyhedraLatticeElement)second;
+
+ LinearConstraintSystem lcs = aa.lcs.Widen(bb.lcs);
+ PolyhedraLatticeElement result = new PolyhedraLatticeElement(lcs);
+ log.DbgMsg(string.Format("{0} |_| {1} --> {2}", this.ToString(first), this.ToString(second), this.ToString(result)));
+ log.DbgMsgUnindent();
+ return result;
+ }
+
+
+ public override Element! Eliminate (Element! e, IVariable! variable)
+ {
+ log.DbgMsg(string.Format("Eliminating {0} ...", variable));
+
+ PolyhedraLatticeElement ple = (PolyhedraLatticeElement)e;
+ if (ple.lcs.IsBottom())
+ {
+ return ple;
+ }
+ return new PolyhedraLatticeElement(ple.lcs.Project(variable));
+ }
+
+
+ public override Element! Rename (Element! e, IVariable! oldName, IVariable! newName)
+ {
+ log.DbgMsg(string.Format("Renaming {0} to {1} in {2} ...", oldName, newName, this.ToString(e)));
+
+ PolyhedraLatticeElement ple = (PolyhedraLatticeElement)e;
+ if (ple.lcs.IsBottom())
+ {
+ return ple;
+ }
+ return new PolyhedraLatticeElement(ple.lcs.Rename(oldName, newName));
+ }
+
+ public override bool Understands(IFunctionSymbol! f, IList/*<IExpr!>*/! args) {
+ return f is IntSymbol ||
+ f.Equals(Int.Add) ||
+ f.Equals(Int.Sub) ||
+ f.Equals(Int.Negate) ||
+ f.Equals(Int.Mul) ||
+ f.Equals(Int.Eq) ||
+ f.Equals(Int.Neq) ||
+ f.Equals(Prop.Not) ||
+ f.Equals(Int.AtMost) ||
+ f.Equals(Int.Less) ||
+ f.Equals(Int.Greater) ||
+ f.Equals(Int.AtLeast);
+ }
+
+ public override Answer CheckVariableDisequality(Element! e, IVariable! var1, IVariable! var2) {
+ PolyhedraLatticeElement! ple = (PolyhedraLatticeElement)e;
+ assume ple.lcs.Constraints != null;
+ ArrayList /*LinearConstraint!*/! clist = (ArrayList /*LinearConstraint!*/!)ple.lcs.Constraints.Clone();
+ LinearConstraint! lc = new LinearConstraint(LinearConstraint.ConstraintRelation.EQ);
+ lc.SetCoefficient(var1, Rational.ONE);
+ lc.SetCoefficient(var2, Rational.MINUS_ONE);
+ clist.Add(lc);
+ LinearConstraintSystem newLcs = new LinearConstraintSystem(clist);
+ if (newLcs.IsBottom()) {
+ return Answer.Yes;
+ } else {
+ return Answer.Maybe;
+ }
+ }
+
+ public override Answer CheckPredicate(Element! e, IExpr! pred) {
+ PolyhedraLatticeElement! ple = (PolyhedraLatticeElement)Constrain(e, pred);
+ if (ple.lcs.IsBottom()) {
+ return Answer.No;
+ }
+
+ // Note, "pred" may contain expressions that are not understood by the propFactory (in
+ // particular, this may happen because--currently, and perhaps is a design we'll want
+ // to change in the future--propFactory deals with BoogiePL expressions whereas "pred"
+ // may also refer to Equivalences.UninterpFun expressions). Thus, we cannot just
+ // call propFactory.Not(pred) to get the negation of "pred".
+ pred = new PolyhedraLatticeNegation(pred);
+ ple = (PolyhedraLatticeElement)Constrain(e, pred);
+ if (ple.lcs.IsBottom()) {
+ return Answer.Yes;
+ } else {
+ return Answer.Maybe;
+ }
+ }
+
+ class PolyhedraLatticeNegation : IFunApp
+ {
+ IExpr! arg;
+
+ public PolyhedraLatticeNegation(IExpr! arg) {
+ this.arg = arg;
+ // base();
+ }
+
+ [Pure][Reads(ReadsAttribute.Reads.Owned)] public object DoVisit(ExprVisitor! visitor) {
+ return visitor.VisitFunApp(this);
+ }
+
+ public IFunctionSymbol! FunctionSymbol { [Pure][Reads(ReadsAttribute.Reads.Owned)] get { return Prop.Not; } }
+
+ public IList/*<IExpr!>*/! Arguments {
+ [Pure][Reads(ReadsAttribute.Reads.Owned)] get {
+ IExpr[] args = new IExpr[] { arg };
+ return ArrayList.ReadOnly(args);
+ }
+ }
+
+ public IFunApp! CloneWithArguments(IList/*<IExpr!>*/! args) {
+ assert args.Count == 1;
+ return new PolyhedraLatticeNegation((IExpr!)args[0]);
+ }
+ }
+
+ public override IExpr/*?*/ EquivalentExpr(Element! e, IQueryable! q, IExpr! expr, IVariable! var, ISet/*<IVariable!>*/! prohibitedVars) {
+ // BUGBUG: TODO: this method can be implemented in a more precise way
+ return null;
+ }
+
+
+ public override Element! Constrain (Element! e, IExpr! expr)
+ {
+ log.DbgMsg(string.Format("Constraining with {0} into {1} ...", expr, this.ToString(e)));
+
+ PolyhedraLatticeElement ple = (PolyhedraLatticeElement)e;
+ if (ple.lcs.IsBottom())
+ {
+ return ple;
+ }
+ LinearCondition le = LinearExpressionBuilder.AsCondition(expr);
+ if (le != null) {
+ // update the polyhedron according to the linear expression
+ assume ple.lcs.Constraints != null;
+ ArrayList /*LinearConstraint*/ clist = (ArrayList! /*LinearConstraint*/)ple.lcs.Constraints.Clone();
+ le.AddToConstraintSystem(clist);
+ LinearConstraintSystem newLcs = new LinearConstraintSystem(clist);
+
+ return new PolyhedraLatticeElement(newLcs);
+ }
+ return ple;
+ }
+
+ } // class
+
+
+ /// <summary>
+ /// A LinearCondition follows this grammar:
+ /// LinearCondition ::= unsatisfiable
+ /// | LinearConstraint
+ /// | ! LinearConstraint
+ /// Note that negations are distributed to the leaves.
+ /// </summary>
+ abstract class LinearCondition
+ {
+ /// <summary>
+ /// Adds constraints to the list "clist". If "this"
+ /// entails some disjunctive constraints, they may not be added.
+ /// </summary>
+ /// <param name="clist"></param>
+ public abstract void AddToConstraintSystem(ArrayList! /*LinearConstraint*/ clist);
+ }
+
+ class LCBottom : LinearCondition
+ {
+ public override void AddToConstraintSystem(ArrayList! /*LinearConstraint*/ clist)
+ {
+ // make an unsatisfiable constraint
+ LinearConstraint lc = new LinearConstraint(LinearConstraint.ConstraintRelation.EQ);
+ lc.rhs = Rational.FromInt(1);
+ clist.Add(lc);
+ }
+ }
+
+ class LinearConditionLiteral : LinearCondition
+ {
+ public readonly bool positive;
+ public readonly LinearConstraint! constraint;
+ /// <summary>
+ /// Precondition: positive || constraint.Relation == LinearConstraint.ConstraintRelation.EQ
+ /// </summary>
+ /// <param name="positive"></param>
+ /// <param name="constraint"></param>
+ public LinearConditionLiteral(bool positive, LinearConstraint! constraint)
+ requires positive || constraint.Relation == LinearConstraint.ConstraintRelation.EQ;
+ {
+ this.positive = positive;
+ this.constraint = constraint;
+ }
+ public override void AddToConstraintSystem(ArrayList! /*LinearConstraint*/ clist)
+ {
+ if (positive)
+ {
+ clist.Add(constraint);
+ }
+ else
+ {
+ assert constraint.Relation == LinearConstraint.ConstraintRelation.EQ;
+ // the constraint is disjunctive, so just ignore it
+ }
+ }
+ }
+
+ class LinearExpressionBuilder
+ {
+ /// <summary>
+ /// Builds a linear condition from "e", if possible; returns null if not possible.
+ /// </summary>
+ /// <param name="e"></param>
+ /// <returns></returns>
+ public static /*maybe null*/ LinearCondition AsCondition(IExpr e) /* throws ArithmeticException */
+ {
+ return GetCond(e, true);
+ }
+
+ static /*maybe null*/ LinearCondition GetCond(IExpr e, bool positive) /* throws ArithmeticException */
+ {
+ IFunApp funapp = e as IFunApp;
+ if (funapp == null) {
+ return null;
+ }
+ IFunctionSymbol! s = funapp.FunctionSymbol;
+ if ((positive && s.Equals(Prop.False)) ||
+ (!positive && s.Equals(Prop.True))) {
+ return new LCBottom();
+ } else if (s.Equals(Prop.Not)) {
+ assert funapp.Arguments.Count == 1;
+ return GetCond((IExpr!)funapp.Arguments[0], !positive);
+ } else if (funapp.Arguments.Count == 2) {
+ IExpr! arg0 = (IExpr!)funapp.Arguments[0];
+ IExpr! arg1 = (IExpr!)funapp.Arguments[1];
+ LinearExpr le0 = AsExpr(arg0);
+ if (le0 == null) {
+ return null;
+ }
+ LinearExpr le1 = AsExpr(arg1);
+ if (le1 == null) {
+ return null;
+ }
+
+ LinearConstraint constraint = null;
+ bool sense = true;
+ if ((positive && s.Equals(Int.Less)) || (!positive && s.Equals(Int.AtLeast)))
+ {
+ constraint = MakeConstraint(le0, le1, LinearConstraint.ConstraintRelation.LE, BigNum.ONE);
+ }
+ else if ((positive && s.Equals(Int.AtMost)) || (!positive && s.Equals(Int.Greater)))
+ {
+ constraint = MakeConstraint(le0, le1, LinearConstraint.ConstraintRelation.LE, BigNum.ZERO);
+ }
+ else if ((positive && s.Equals(Int.AtLeast)) || (!positive && s.Equals(Int.Less)))
+ {
+ constraint = MakeConstraint(le1, le0, LinearConstraint.ConstraintRelation.LE, BigNum.ZERO);
+ }
+ else if ((positive && s.Equals(Int.Greater)) || (!positive && s.Equals(Int.AtMost)))
+ {
+ constraint = MakeConstraint(le1, le0, LinearConstraint.ConstraintRelation.LE, BigNum.ONE);
+ }
+ else if (s.Equals(Int.Eq))
+ {
+ constraint = MakeConstraint(le0, le1, LinearConstraint.ConstraintRelation.EQ, BigNum.ZERO);
+ sense = positive;
+ }
+ else if (s.Equals(Int.Neq))
+ {
+ constraint = MakeConstraint(le0, le1, LinearConstraint.ConstraintRelation.EQ, BigNum.ZERO);
+ sense = !positive;
+ }
+ if (constraint != null) {
+ if (constraint.coefficients.Count != 0) {
+ return new LinearConditionLiteral(sense, constraint);
+ } else if (constraint.IsConstantSatisfiable()) {
+ return null;
+ } else {
+ return new LCBottom();
+ }
+ }
+ }
+ return null;
+ }
+
+ public static LinearConstraint MakeConstraint(LinearExpr! le0, LinearExpr! le1,
+ LinearConstraint.ConstraintRelation rel, BigNum constantOffset) /* throws ArithmeticException */
+ {
+ le1.Negate();
+ le0.Add(le1);
+ le0.AddConstant(constantOffset);
+ return le0.ToConstraint(rel);
+ }
+
+ /// <summary>
+ /// Builds a linear expression from "e", if possible; returns null if not possible.
+ /// </summary>
+ /// <param name="e"></param>
+ /// <returns></returns>
+ public static /*maybe null*/ LinearExpr AsExpr(IExpr! e) /* throws ArithmeticException */
+ {
+ if (e is IVariable) {
+ // Note, without a type for the variable, we don't know if the identifier is intended to hold an integer value.
+ // However, it seems that no harm can be caused by here treating the identifier as if it held an
+ // integer value, because other parts of this method will reject the expression as a linear expression
+ // if non-numeric operations other than equality are applied to the identifier.
+ return new LinearExpr((IVariable)e);
+ } else if (e is IFunApp) {
+ IFunApp! funapp = (IFunApp)e;
+ IFunctionSymbol! s = funapp.FunctionSymbol;
+
+ if (s is IntSymbol) {
+ return new LinearExpr(((IntSymbol)s).Value);
+ } else if (s.Equals(Int.Negate)) {
+ assert funapp.Arguments.Count == 1;
+ LinearExpr le = AsExpr((IExpr!)funapp.Arguments[0]);
+ if (le != null) {
+ le.Negate();
+ return le;
+ }
+ } else if (s.Equals(Int.Add) || s.Equals(Int.Sub) || s.Equals(Int.Mul)) {
+ assert funapp.Arguments.Count == 2;
+ IExpr! arg0 = (IExpr!)funapp.Arguments[0];
+ IExpr! arg1 = (IExpr!)funapp.Arguments[1];
+ LinearExpr le0 = AsExpr(arg0);
+ if (le0 == null) {
+ return null;
+ }
+ LinearExpr le1 = AsExpr(arg1);
+ if (le1 == null) {
+ return null;
+ }
+
+ if (s.Equals(Int.Add)) {
+ le0.Add(le1);
+ return le0;
+ } else if (s.Equals(Int.Sub)) {
+ le1.Negate();
+ le0.Add(le1);
+ return le0;
+ } else if (s.Equals(Int.Mul)) {
+ BigNum x;
+ if (le0.AsConstant(out x))
+ {
+ le1.Multiply(x);
+ return le1;
+ }
+ else if (le1.AsConstant(out x))
+ {
+ le0.Multiply(x);
+ return le0;
+ }
+ }
+ }
+ }
+ return null;
+ }
+ }
+
+ class LinearExpr
+ {
+ BigNum constant;
+ Term terms;
+
+ class Term
+ {
+ public BigNum coeff; // non-0, if the node is used
+ public IVariable! var;
+ public Term next;
+
+ public Term(BigNum coeff, IVariable! var)
+ {
+ this.coeff = coeff;
+ this.var = var;
+ // base();
+ }
+ }
+
+ public LinearExpr(BigNum x)
+ {
+ constant = x;
+ }
+
+ public LinearExpr(IVariable! var)
+ {
+ constant = BigNum.ZERO;
+ terms = new Term(BigNum.ONE, var);
+ }
+
+ public ISet /*IVariable!*/ GetDefinedDimensions()
+ {
+ HashSet /*IVariable!*/! dims = new HashSet /*IVariable!*/ ();
+ for (Term current = terms; current != null; current = current.next)
+ {
+ dims.Add(current.var);
+ }
+ return dims;
+ }
+
+ public BigNum TermCoefficient(/*MayBeNull*/ IVariable! var)
+ {
+ BigNum z = BigNum.ZERO;
+ if (var == null)
+ {
+ z = this.constant;
+ }
+ else if (terms != null)
+ {
+ Term current = terms;
+ while (current != null)
+ {
+ if (current.var == var)
+ {
+ break;
+ }
+ current = current.next;
+ }
+ if (current != null)
+ {
+ z = current.coeff;
+ }
+ }
+ return z;
+ }
+
+ public bool AsConstant(out BigNum x)
+ {
+ if (terms == null)
+ {
+ x = constant;
+ return true;
+ }
+ else
+ {
+ x = BigNum.FromInt(-70022); // to please complier
+ return false;
+ }
+ }
+
+ public void Negate() /* throws ArithmeticException */
+ {
+ checked
+ {
+ constant = -constant;
+ }
+
+ for (Term t = terms; t != null; t = t.next)
+ {
+ checked
+ {
+ t.coeff = -t.coeff;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Adds "x" to "this".
+ /// </summary>
+ /// <param name="x"></param>
+ public void AddConstant(BigNum x) /* throws ArithmeticException */
+ {
+ checked
+ {
+ constant += x;
+ }
+ }
+
+ /// <summary>
+ /// Adds "le" to "this". Afterwards, "le" should not be used, because it will have been destroyed.
+ /// </summary>
+ /// <param name="le"></param>
+ public void Add(LinearExpr! le) /* throws ArithmeticException */
+ requires le != this;
+ {
+ checked
+ {
+ constant += le.constant;
+ }
+ le.constant = BigNum.FromInt(-70029); // "le" should no longer be used; assign it a strange value so that misuse is perhaps more easily detected
+
+ // optimization:
+ if (le.terms == null)
+ {
+ return;
+ }
+ else if (terms == null)
+ {
+ terms = le.terms;
+ le.terms = null;
+ return;
+ }
+
+ // merge the two term lists
+ // Use a nested loop, which is quadratic in time complexity, but we hope the lists will be small
+ Term newTerms = null;
+ while (le.terms != null)
+ {
+ // take off next term from "le"
+ Term t = le.terms;
+ le.terms = t.next;
+ t.next = null;
+
+ for (Term u = terms; u != null; u = u.next)
+ {
+ if (u.var == t.var)
+ {
+ checked
+ {
+ u.coeff += t.coeff;
+ }
+ goto NextOuter;
+ }
+ }
+ t.next = newTerms;
+ newTerms = t;
+
+ NextOuter: ;
+ }
+
+ // finally, include all non-0 terms
+ while (terms != null)
+ {
+ // take off next term from "this"
+ Term t = terms;
+ terms = t.next;
+
+ if (!t.coeff.IsZero)
+ {
+ t.next = newTerms;
+ newTerms = t;
+ }
+ }
+ terms = newTerms;
+ }
+
+ public void Multiply(BigNum x) /* throws ArithmeticException */
+ {
+ if (x.IsZero)
+ {
+ constant = BigNum.ZERO;
+ terms = null;
+ }
+ else
+ {
+ for (Term t = terms; t != null; t = t.next)
+ {
+ checked
+ {
+ t.coeff *= x;
+ }
+ }
+ checked
+ {
+ constant *= x;
+ }
+ }
+ }
+
+ public bool IsInvertible(IVariable! var)
+ {
+ for (Term t = terms; t != null; t = t.next)
+ {
+ if (t.var == var)
+ {
+ System.Diagnostics.Debug.Assert(!t.coeff.IsZero);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public LinearConstraint ToConstraint(LinearConstraint.ConstraintRelation rel) /* throws ArithmeticException */
+ {
+ LinearConstraint constraint = new LinearConstraint(rel);
+ for (Term t = terms; t != null; t = t.next)
+ {
+ constraint.SetCoefficient(t.var, t.coeff.ToRational);
+ }
+ BigNum rhs = -constant;
+ constraint.rhs = rhs.ToRational;
+ return constraint;
+ }
+ }
+}
diff --git a/Source/AIFramework/Polyhedra/SimplexTableau.ssc b/Source/AIFramework/Polyhedra/SimplexTableau.ssc
new file mode 100644
index 00000000..b6f4095c
--- /dev/null
+++ b/Source/AIFramework/Polyhedra/SimplexTableau.ssc
@@ -0,0 +1,717 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+namespace Microsoft.AbstractInterpretationFramework
+{
+ using System.Collections;
+ using System;
+ using Microsoft.Contracts;
+ using Microsoft.Basetypes;
+ using IMutableSet = Microsoft.Boogie.Set;
+ using HashSet = Microsoft.Boogie.Set;
+
+
+ /// <summary>
+ /// Used by LinearConstraintSystem.GenerateFrameFromConstraints.
+ /// </summary>
+ public class SimplexTableau
+ {
+ readonly int rows;
+ readonly int columns;
+ readonly Rational[,]! m;
+
+ readonly int numInitialVars;
+ readonly int numSlackVars;
+ readonly int rhsColumn;
+
+ readonly ArrayList /*IVariable!*/! dims;
+ readonly int[]! basisColumns;
+ readonly int[]! inBasis;
+ bool constructionDone = false;
+
+ void CheckInvariant()
+ {
+ assert(rows == m.GetLength(0));
+ assert(1 <= columns && columns == m.GetLength(1));
+ assert(0 <= numInitialVars);
+ assert(0 <= numSlackVars && numSlackVars <= rows);
+ assert(numInitialVars + numSlackVars + 1 == columns);
+ assert(rhsColumn == columns - 1);
+ assert(dims.Count == numInitialVars);
+ assert(basisColumns.Length == rows);
+ assert(inBasis.Length == numInitialVars + numSlackVars);
+
+ bool[] b = new bool[numInitialVars + numSlackVars];
+ int numColumnsInBasis = 0;
+ int numUninitializedRowInfo = 0;
+ for (int i = 0; i < rows; i++)
+ {
+ int c = basisColumns[i];
+ if (c == rhsColumn)
+ {
+ // all coefficients in this row are 0 (but the right-hand side may be non-0)
+ for (int j = 0; j < rhsColumn; j++)
+ {
+ assert m[i,j].IsZero;
+ }
+ numColumnsInBasis++;
+ }
+ else if (c == -1)
+ {
+ assert(!constructionDone);
+ numUninitializedRowInfo++;
+ }
+ else
+ {
+ // basis column is a column
+ assert(0 <= c && c < numInitialVars + numSlackVars);
+ // basis column is unique
+ assert(!b[c]);
+ b[c] = true;
+ // column is marked as being in basis
+ assert(inBasis[c] == i);
+ // basis column really is a basis column
+ for (int j = 0; j < rows; j++)
+ {
+ if (j == i)
+ {
+ assert m[j,c].HasValue(1);// == (Rational)new Rational(1));
+ }
+ else
+ {
+ assert m[j,c].IsZero;
+ }
+ }
+ }
+ }
+ // no other columns are marked as being in basis
+ foreach (int i in inBasis)
+ {
+ if (0 <= i)
+ {
+ assert(i < rows);
+ numColumnsInBasis++;
+ }
+ else
+ {
+ assert(i == -1);
+ }
+ }
+ assert(rows - numUninitializedRowInfo <= numColumnsInBasis && numColumnsInBasis <= rows);
+ assert(!constructionDone || numUninitializedRowInfo == 0);
+ }
+
+ /// <summary>
+ /// Constructs a matrix that represents the constraints "constraints", adding slack
+ /// variables for the inequalities among "constraints". Puts the matrix in canonical
+ /// form.
+ /// </summary>
+ /// <param name="constraints"></param>
+ [NotDelayed]
+ public SimplexTableau(ArrayList /*LinearConstraint*/! constraints)
+ {
+#if DEBUG_PRINT
+ Console.WriteLine("DEBUG: SimplexTableau constructor called with:");
+ foreach (LinearConstraint lc in constraints)
+ {
+ Console.WriteLine(" {0}", lc);
+ }
+#endif
+ // Note: This implementation is not particularly efficient, but it'll do for now.
+
+ ArrayList dims = this.dims = new ArrayList /*IVariable!*/ ();
+ int slacks = 0;
+ foreach (LinearConstraint! cc in constraints)
+ {
+ foreach (IVariable! dim in cc.coefficients.Keys)
+ {
+ if (!dims.Contains(dim))
+ {
+ dims.Add(dim);
+ }
+ }
+ if (cc.Relation == LinearConstraint.ConstraintRelation.LE)
+ {
+ slacks++;
+ }
+ }
+
+ int numInitialVars = this.numInitialVars = dims.Count;
+ int numSlackVars = this.numSlackVars = slacks;
+ int rows = this.rows = constraints.Count;
+ int columns = this.columns = numInitialVars + numSlackVars + 1;
+ this.m = new Rational[rows, columns];
+ this.rhsColumn = columns-1;
+ this.basisColumns = new int[rows];
+ this.inBasis = new int[columns-1];
+
+ base();
+
+ for (int i = 0; i < inBasis.Length; i++)
+ {
+ inBasis[i] = -1;
+ }
+
+ // Fill in the matrix
+ int r = 0;
+ int iSlack = 0;
+ foreach (LinearConstraint! cc in constraints)
+ {
+ for (int i = 0; i < dims.Count; i++)
+ {
+ m[r,i] = cc[(IVariable!)dims[i]];
+ }
+ if (cc.Relation == LinearConstraint.ConstraintRelation.LE)
+ {
+ m[r, numInitialVars + iSlack] = Rational.ONE;
+ basisColumns[r] = numInitialVars + iSlack;
+ inBasis[numInitialVars + iSlack] = r;
+ iSlack++;
+ }
+ else
+ {
+ basisColumns[r] = -1; // special value to communicate to Pivot that basis column i hasn't been set up yet
+ }
+ m[r,rhsColumn] = cc.rhs;
+ r++;
+ }
+ assert(r == constraints.Count);
+ assert(iSlack == numSlackVars);
+#if DEBUG_PRINT
+ Console.WriteLine("DEBUG: Intermediate tableau state in SimplexTableau constructor:");
+ Dump();
+#endif
+
+ // Go through the rows with uninitialized basis columns. These correspond to equality constraints.
+ // For each one, find an initial variable (non-slack variable) whose column we can make the basis
+ // column of the row.
+ for (int i = 0; i < rows; i++)
+ {
+ if (basisColumns[i] != -1)
+ {
+ continue;
+ }
+ // Find a non-0 column in row i that we can make a basis column. Since rows corresponding
+ // to equality constraints don't have slack variables and since the pivot operations performed
+ // by iterations of this loop don't introduce any non-0 coefficients in the slack-variable
+ // columns of these rows, we only need to look through the columns corresponding to initial
+ // variables.
+ for (int j = 0; j < numInitialVars; j++)
+ {
+ if (m[i,j].IsNonZero)
+ {
+#if DEBUG_PRINT
+ Console.WriteLine("-- About to Pivot({0},{1})", i, j);
+#endif
+ assert(inBasis[j] == -1);
+ Pivot(i,j);
+#if DEBUG_PRINT
+ Console.WriteLine("Tableau after Pivot:");
+ Dump();
+ #endif
+ goto SET_UP_NEXT_INBASIS_COLUMN;
+ }
+ }
+ // Check the assertion in the comment above, that is, that columns corresponding to slack variables
+ // are 0 in this row.
+ for (int j = numInitialVars; j < rhsColumn; j++)
+ {
+ assert m[i,j].IsZero;
+ }
+ // There is no column in this row that we can put into basis.
+ basisColumns[i] = rhsColumn;
+ SET_UP_NEXT_INBASIS_COLUMN: {}
+ }
+
+ constructionDone = true;
+ CheckInvariant();
+ }
+
+ public IMutableSet! /*IVariable!*/ GetDimensions()
+ {
+ HashSet /*IVariable!*/ z = new HashSet /*IVariable!*/ ();
+ foreach (IVariable! dim in dims)
+ {
+ z.Add(dim);
+ }
+ return z;
+ }
+
+ public Rational this [int r, int c]
+ {
+ get
+ {
+ return m[r,c];
+ }
+ set
+ {
+ m[r,c] = value;
+ }
+ }
+
+ /// <summary>
+ /// Applies the Pivot Operation on row "r" and column "c".
+ ///
+ /// This method can be called when !constructionDone, that is, at a time when not all basis
+ /// columns have been set up (indicated by -1 in basisColumns). This method helps set up
+ /// those basis columns.
+ ///
+ /// The return value is an undo record that can be used with UnPivot.
+ /// </summary>
+ /// <param name="r"></param>
+ /// <param name="c"></param>
+ public Rational[]! Pivot(int r, int c)
+ {
+ assert(0 <= r && r < rows);
+ assert(0 <= c && c < columns-1);
+ assert(m[r,c].IsNonZero);
+ assert(inBasis[c] == -1); // follows from invariant and m[r,c] != 0
+ assert(basisColumns[r] != rhsColumn); // follows from invariant and m[r,c] != 0
+
+ Rational[] undo = new Rational[rows+1];
+ for (int i = 0; i < rows; i++)
+ {
+ undo[i] = m[i,c];
+ }
+
+ // scale the pivot row
+ Rational q = m[r,c];
+ if (q != Rational.ONE)
+ {
+ for (int j = 0; j < columns; j++)
+ {
+ m[r,j] /= q;
+ }
+ }
+
+ // subtract a multiple of the pivot row from all other rows
+ for (int i = 0; i < rows; i++)
+ {
+ if (i != r)
+ {
+ q = m[i,c];
+ if (q.IsNonZero)
+ {
+ for (int j = 0; j < columns; j++)
+ {
+ m[i,j] -= q * m[r,j];
+ }
+ }
+ }
+ }
+
+ // update basis information
+ int prevCol = basisColumns[r];
+ undo[rows] = Rational.FromInt(prevCol);
+ basisColumns[r] = c;
+ if (prevCol != -1)
+ {
+ inBasis[prevCol] = -1;
+ }
+ inBasis[c] = r;
+
+ return undo;
+ }
+
+ /// <summary>
+ /// If the last operation applied to the tableau was:
+ /// undo = Pivot(i,j);
+ /// then UnPivot(i, j, undo) undoes the pivot operation.
+ /// Note: This operation is not supported for any call to Pivot before constructionDone
+ /// is set to true.
+ /// </summary>
+ /// <param name="r"></param>
+ /// <param name="c"></param>
+ /// <param name="undo"></param>
+ void UnPivot(int r, int c, Rational[]! undo)
+ {
+ assert(0 <= r && r < rows);
+ assert(0 <= c && c < columns-1);
+ assert(m[r,c].HasValue(1));
+ assert(undo.Length == rows+1);
+
+ // add a multiple of the pivot row to all other rows
+ for (int i = 0; i < rows; i++)
+ {
+ if (i != r)
+ {
+ Rational q = undo[i];
+ if (q.IsNonZero)
+ {
+ for (int j = 0; j < columns; j++)
+ {
+ m[i,j] += q * m[r,j];
+ }
+ }
+ }
+ }
+
+ // scale the pivot row
+ Rational p = undo[r];
+ for (int j = 0; j < columns; j++)
+ {
+ m[r,j] *= p;
+ }
+
+ // update basis information
+ int prevCol = undo[rows].AsInteger;
+ assert(prevCol != -1);
+ basisColumns[r] = prevCol;
+ inBasis[c] = -1;
+ inBasis[prevCol] = r;
+ }
+
+ /// <summary>
+ /// Returns true iff the current basis of the system of constraints modeled by the simplex tableau
+ /// is feasible. May have a side effect of performing a number of pivot operations on the tableau,
+ /// but any such pivot operation will be in the columns of slack variables (that is, this routine
+ /// does not change the set of initial-variable columns in basis).
+ ///
+ /// CAVEAT: I have no particular reason to believe that the algorithm used here will terminate. --KRML
+ /// </summary>
+ /// <returns></returns>
+ public bool IsFeasibleBasis
+ {
+ get
+ {
+ // while there is a slack variable in basis whose row has a negative right-hand side
+ while (true)
+ {
+ bool feasibleBasis = true;
+ for (int c = numInitialVars; c < rhsColumn; c++)
+ {
+ int k = inBasis[c];
+ if (0 <= k && k < rhsColumn && m[k,rhsColumn].IsNegative)
+ {
+ assert(m[k,c].HasValue(1)); // c is in basis
+ // Try to pivot on a different slack variable in this row
+ for (int i = numInitialVars; i < rhsColumn; i++)
+ {
+ if (m[k,i].IsNegative)
+ {
+ assert(c != i); // c is in basis, so m[k,c]==1, which is not negative
+ Pivot(k, i);
+#if DEBUG_PRINT
+ Console.WriteLine("Tableau after Pivot operation on ({0},{1}) in IsFeasibleBasis:", k, i);
+ Dump();
+#endif
+ assert(inBasis[c] == -1);
+ assert(inBasis[i] == k);
+ assert(m[k,rhsColumn].IsNonNegative);
+ goto START_ANEW;
+ }
+ }
+ feasibleBasis = false;
+ }
+ }
+ return feasibleBasis;
+ START_ANEW: ;
+ }
+ return false; // make compiler shut up
+ }
+ }
+
+ /// <summary>
+ /// Whether or not all initial variables (the non-slack variables) are in basis)
+ /// </summary>
+ public bool AllInitialVarsInBasis
+ {
+ get
+ {
+ for (int i = 0; i < numInitialVars; i++)
+ {
+ if (inBasis[i] == -1)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ /// <summary>
+ /// Adds as many initial variables as possible to the basis.
+ /// </summary>
+ /// <returns></returns>
+ public void AddInitialVarsToBasis()
+ {
+ // while there exists an initial variable not in the basis and not satisfying
+ // condition 3.4.2.2 in Cousot and Halbwachs, perform a pivot operation
+ while (true)
+ {
+ for (int i = 0; i < numInitialVars; i++)
+ {
+ if (inBasis[i] == -1)
+ {
+ // initial variable i is not in the basis
+ for (int j = 0; j < rows; j++)
+ {
+ if (m[j,i].IsNonZero)
+ {
+ int k = basisColumns[j];
+ if (numInitialVars <= k && k < rhsColumn)
+ {
+ // slack variable k is in basis for row j
+ Pivot(j, i);
+ assert(inBasis[k] == -1);
+ assert(inBasis[i] == j && basisColumns[j] == i);
+ goto START_ANEW;
+ }
+ }
+ }
+ }
+ }
+ // No more initial variables can be moved into basis.
+ return;
+ START_ANEW: {}
+ }
+ }
+
+ /// <summary>
+ /// Adds to "lines" the lines implied by initial-variable columns not in basis
+ /// (see section 3.4.2 of Cousot and Halbwachs), and adds to "constraints" the
+ /// constraints to exclude those lines (see step 4.2 of section 3.4.3 of
+ /// Cousot and Halbwachs).
+ /// </summary>
+ /// <param name="lines"></param>
+ /// <param name="constraints"></param>
+ public void ProduceLines(ArrayList /*FrameElement*/! lines, ArrayList /*LinearConstraint*/! constraints)
+ {
+ // for every initial variable not in basis
+ for (int i0 = 0; i0 < numInitialVars; i0++)
+ {
+ if (inBasis[i0] == -1)
+ {
+ FrameElement fe = new FrameElement();
+ LinearConstraint lc = new LinearConstraint(LinearConstraint.ConstraintRelation.EQ);
+ for (int i = 0; i < numInitialVars; i++)
+ {
+ if (i == i0)
+ {
+ fe.AddCoordinate((IVariable!)dims[i], Rational.ONE);
+ lc.SetCoefficient((IVariable!)dims[i], Rational.ONE);
+ }
+ else if (inBasis[i] != -1)
+ {
+ // i is a basis column
+ assert(m[inBasis[i],i].HasValue(1));
+ Rational val = -m[inBasis[i],i0];
+ fe.AddCoordinate((IVariable!)dims[i], val);
+ lc.SetCoefficient((IVariable!)dims[i], val);
+ }
+ }
+ lines.Add(fe);
+ constraints.Add(lc);
+ }
+ }
+ }
+
+ /// <summary>
+ /// From a feasible point where all initial variables are in the basis, traverses
+ /// all feasible bases containing all initial variables. For each such basis, adds
+ /// the vertices to "vertices" and adds to "rays" the extreme rays. See step 4.2
+ /// in section 3.4.3 of Cousot and Halbwachs.
+ /// A more efficient algorithm is found in the paper "An algorithm for
+ /// determining all extreme points of a convex polytope" by N. E. Dyer and L. G. Proll,
+ /// Mathematical Programming, 12, 1977.
+ /// Assumes that the tableau is in a state where all initial variables are in the basis.
+ /// This method has no net effect on the tableau.
+ /// Note: Duplicate vertices and rays may be added.
+ /// </summary>
+ /// <param name="vertices"></param>
+ /// <param name="rays"></param>
+ public void TraverseVertices(ArrayList! /*FrameElement*/ vertices, ArrayList! /*FrameElement*/ rays)
+ {
+ ArrayList /*bool[]*/ basesSeenSoFar = new ArrayList /*bool[]*/ ();
+ TraverseBases(basesSeenSoFar, vertices, rays);
+ }
+
+ /// <summary>
+ /// Worker method of TraverseVertices.
+ /// This method has no net effect on the tableau.
+ /// </summary>
+ /// <param name="basesSeenSoFar"></param>
+ /// <param name="vertices"></param>
+ /// <param name="rays"></param>
+ void TraverseBases(ArrayList /*bool[]*/! basesSeenSoFar, ArrayList /*FrameElement*/! vertices, ArrayList /*FrameElement*/! rays)
+ {
+ CheckInvariant();
+
+ bool[] thisBasis = new bool[numSlackVars];
+ for (int i = numInitialVars; i < rhsColumn; i++)
+ {
+ if (inBasis[i] != -1)
+ {
+ thisBasis[i-numInitialVars] = true;
+ }
+ }
+ foreach (bool[]! basis in basesSeenSoFar)
+ {
+ assert(basis.Length == numSlackVars);
+ for (int i = 0; i < numSlackVars; i++)
+ {
+ if (basis[i] != thisBasis[i])
+ {
+ goto COMPARE_WITH_NEXT_BASIS;
+ }
+ }
+ // thisBasis and basis are the same--that is, basisColumns has been visited before--so
+ // we don't traverse anything from here
+ return;
+ COMPARE_WITH_NEXT_BASIS: {}
+ }
+ // basisColumns has not been seen before; record thisBasis and continue with the traversal here
+ basesSeenSoFar.Add(thisBasis);
+
+#if DEBUG_PRINT
+ Console.Write("TraverseBases, new basis: ");
+ foreach (bool t in thisBasis) {
+ Console.Write("{0}", t ? "*" : ".");
+ }
+ Console.WriteLine();
+ Dump();
+#endif
+ // Add vertex
+ FrameElement v = new FrameElement();
+ for (int i = 0; i < rows; i++)
+ {
+ int j = basisColumns[i];
+ if (j < numInitialVars)
+ {
+ v.AddCoordinate((IVariable!)dims[j], m[i,rhsColumn]);
+ }
+ }
+#if DEBUG_PRINT
+ Console.WriteLine(" Adding vertex: {0}", v);
+#endif
+ vertices.Add(v);
+
+ // Add rays. Traverse all columns corresponding to slack variables that
+ // are not in basis (see second bullet of section 3.4.2 of Cousot and Halbwachs).
+ for (int i0 = numInitialVars; i0 < rhsColumn; i0++)
+ {
+ if (inBasis[i0] != -1)
+ {
+ // skip those slack-variable columns that are in basis
+ continue;
+ }
+ // check if slack-variable, non-basis column i corresponds to an extreme ray
+ for (int row = 0; row < rows; row++)
+ {
+ if (m[row,i0].IsPositive)
+ {
+ for (int k = numInitialVars; k < rhsColumn; k++)
+ {
+ if (inBasis[k] != -1 && m[row,k].IsNonZero)
+ {
+ // does not correspond to an extreme ray
+ goto CHECK_NEXT_SLACK_VAR;
+ }
+ }
+ }
+ }
+ // corresponds to an extreme ray
+ FrameElement ray = new FrameElement();
+ for (int i = 0; i < numInitialVars; i++)
+ {
+ int j0 = inBasis[i];
+ Rational val = -m[j0,i0];
+ ray.AddCoordinate((IVariable!)dims[i], val);
+ }
+#if DEBUG_PRINT
+ Console.WriteLine(" Adding ray: {0}", ray);
+#endif
+ rays.Add(ray);
+ CHECK_NEXT_SLACK_VAR: {}
+ }
+
+ // Continue traversal
+ for (int i = numInitialVars; i < rhsColumn; i++)
+ {
+ int j = inBasis[i];
+ if (j != -1)
+ {
+ // try moving i out of basis and some other slack-variable column into basis
+ for (int k = numInitialVars; k < rhsColumn; k++)
+ {
+ if (inBasis[k] == -1 && m[j,k].IsPositive)
+ {
+ Rational[] undo = Pivot(j, k);
+ // check if the new basis is feasible
+ for (int p = 0; p < rows; p++)
+ {
+ int c = basisColumns[p];
+ if (numInitialVars <= c && c < rhsColumn && m[p,rhsColumn].IsNegative)
+ {
+ // not feasible
+ goto AFTER_TRAVERSE;
+ }
+ }
+ TraverseBases(basesSeenSoFar, vertices, rays);
+ AFTER_TRAVERSE:
+ UnPivot(j, k, undo);
+ }
+ }
+ }
+ }
+ }
+
+ public void Dump()
+ {
+ // names
+ Console.Write(" ");
+ for (int i = 0; i < numInitialVars; i++)
+ {
+ Console.Write(" {0,4} ", dims[i]);
+ }
+ Console.WriteLine();
+ // numbers
+ Console.Write(" ");
+ for (int i = 0; i < columns; i++)
+ {
+ if (i == numInitialVars || i == rhsColumn)
+ {
+ Console.Write("|");
+ }
+ Console.Write(" {0,4}", i);
+ if (i < rhsColumn && inBasis[i] != -1)
+ {
+ Console.Write("* ");
+ assert(basisColumns[inBasis[i]] == i);
+ }
+ else
+ {
+ Console.Write(" ");
+ }
+ }
+ Console.WriteLine();
+ // line
+ Console.Write(" ");
+ for (int i = 0; i < columns; i++)
+ {
+ if (i == numInitialVars || i == rhsColumn)
+ {
+ Console.Write("+");
+ }
+ Console.Write("---------");
+ }
+ Console.WriteLine();
+
+ for (int j = 0; j < rows; j++)
+ {
+ Console.Write("{0,4}: ", basisColumns[j]);
+ for (int i = 0; i < columns; i++)
+ {
+ if (i == numInitialVars || i == rhsColumn)
+ {
+ Console.Write("|");
+ }
+ Console.Write(" {0,4:n1} ", m[j,i]);
+ }
+ Console.WriteLine();
+ }
+ }
+ }
+}
diff --git a/Source/AIFramework/VariableMap/ConstantAbstraction.ssc b/Source/AIFramework/VariableMap/ConstantAbstraction.ssc
new file mode 100644
index 00000000..7b7fd87e
--- /dev/null
+++ b/Source/AIFramework/VariableMap/ConstantAbstraction.ssc
@@ -0,0 +1,210 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using Microsoft.Contracts;
+namespace Microsoft.AbstractInterpretationFramework
+{
+ using System.Collections;
+ using System.Diagnostics;
+ using System.Compiler.Analysis;
+ using Microsoft.Basetypes;
+
+ /// <summary>
+ /// Represents an invariant over constant variable assignments.
+ /// </summary>
+ public class ConstantLattice : MicroLattice
+ {
+ enum Value { Top, Bottom, Constant }
+
+ private class Elt : Element
+ {
+ public Value domainValue;
+ public BigNum constantValue; // valid iff domainValue == Value.Constant
+
+ public Elt (Value v) { this.domainValue = v; }
+
+ public Elt (BigNum i) { this.domainValue = Value.Constant; this.constantValue = i; }
+
+ public bool IsConstant { get { return this.domainValue == Value.Constant; } }
+
+ public BigNum Constant { get { return this.constantValue; } } // only when IsConstant
+
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public override System.Collections.Generic.ICollection<IVariable!>! FreeVariables()
+ {
+ return (!) (new System.Collections.Generic.List<IVariable!>()).AsReadOnly();
+ }
+
+ public override Element! Clone()
+ {
+ if (this.IsConstant)
+ return new Elt(constantValue);
+ else
+ return new Elt(domainValue);
+ }
+ }
+
+ readonly IIntExprFactory! factory;
+
+ public ConstantLattice(IIntExprFactory! factory)
+ {
+ this.factory = factory;
+ // base();
+ }
+
+ public override Element! Top
+ {
+ get { return new Elt(Value.Top); }
+ }
+
+ public override Element! Bottom
+ {
+ get { return new Elt(Value.Bottom); }
+ }
+
+ public override bool IsTop (Element! element)
+ {
+ Elt e = (Elt)element;
+ return e.domainValue == Value.Top;
+ }
+
+ public override bool IsBottom (Element! element)
+ {
+ Elt e = (Elt)element;
+ return e.domainValue == Value.Bottom;
+ }
+
+ public override Element! NontrivialJoin (Element! first, Element! second)
+ {
+ Elt a = (Elt)first;
+ Elt b = (Elt)second;
+ Debug.Assert(a.domainValue == Value.Constant && b.domainValue == Value.Constant);
+ return (a.constantValue.Equals(b.constantValue)) ? a : (Elt)Top;
+ }
+
+ public override Element! NontrivialMeet (Element! first, Element! second)
+ {
+ Elt a = (Elt)first;
+ Elt b = (Elt)second;
+ Debug.Assert(a.domainValue == Value.Constant && b.domainValue == Value.Constant);
+ return (a.constantValue.Equals(b.constantValue)) ? a : (Elt)Bottom;
+ }
+
+ public override Element! Widen (Element! first, Element! second)
+ {
+ return Join(first,second);
+ }
+
+ protected override bool AtMost (Element! first, Element! second) // this <= that
+ {
+ Elt a = (Elt)first;
+ Elt b = (Elt)second;
+ return a.Constant.Equals(b.Constant);
+ }
+
+ public override IExpr! ToPredicate(IVariable! var, Element! element) {
+ return factory.Eq(var, (!)GetFoldExpr(element));
+ }
+
+ public override IExpr GetFoldExpr(Element! element) {
+ Elt e = (Elt)element;
+ assert e.domainValue == Value.Constant;
+ return factory.Const(e.constantValue);
+ }
+
+ public override bool Understands(IFunctionSymbol! f, IList/*<IExpr!>*/! args) {
+ return f.Equals(Microsoft.AbstractInterpretationFramework.Value.Eq);
+ }
+
+ public override Element! EvaluatePredicate(IExpr! e) {
+
+ IFunApp nary = e as IFunApp;
+ if (nary != null) {
+ if (nary.FunctionSymbol.Equals(Microsoft.AbstractInterpretationFramework.Value.Eq)) {
+ IList/*<IExpr!>*/! args = nary.Arguments;
+ assert args.Count == 2;
+ IExpr! arg0 = (IExpr!)args[0];
+ IExpr! arg1 = (IExpr!)args[1];
+
+ // Look for "x == const" or "const == x".
+ try {
+ if (arg0 is IVariable) {
+ BigNum z;
+ if (Fold(arg1, out z)) {
+ return new Elt(z);
+ }
+ } else if (arg1 is IVariable) {
+ BigNum z;
+ if (Fold(arg0, out z)) {
+ return new Elt(z);
+ }
+ }
+ } catch (System.ArithmeticException) {
+ // fall through and return Top. (Note, an alternative design may
+ // consider returning Bottom.)
+ }
+ }
+ }
+ return Top;
+ }
+
+ /// <summary>
+ /// Returns true if "expr" represents a constant integer expressions, in which case
+ /// "z" returns as that integer. Otherwise, returns false, in which case "z" should
+ /// not be used by the caller.
+ ///
+ /// This method throws an System.ArithmeticException in the event that folding the
+ /// constant expression results in an arithmetic overflow or division by zero.
+ /// </summary>
+ private bool Fold(IExpr! expr, out BigNum z)
+ {
+ IFunApp e = expr as IFunApp;
+ if (e == null) {
+ z = BigNum.ZERO;
+ return false;
+ }
+
+ if (e.FunctionSymbol is IntSymbol) {
+ z = ((IntSymbol)e.FunctionSymbol).Value;
+ return true;
+
+ } else if (e.FunctionSymbol.Equals(Int.Negate)) {
+ IList/*<IExpr!>*/! args = e.Arguments;
+ assert args.Count == 1;
+ IExpr! arg0 = (IExpr!)args[0];
+
+ if (Fold(arg0, out z)) {
+ z = z.Neg;
+ return true;
+ }
+
+ } else if (e.Arguments.Count == 2) {
+ IExpr! arg0 = (IExpr!)e.Arguments[0];
+ IExpr! arg1 = (IExpr!)e.Arguments[1];
+ BigNum z0, z1;
+ if (Fold(arg0, out z0) && Fold(arg1, out z1)) {
+ if (e.FunctionSymbol.Equals(Int.Add)) {
+ z = z0 + z1;
+ } else if (e.FunctionSymbol.Equals(Int.Sub)) {
+ z = z0 - z1;
+ } else if (e.FunctionSymbol.Equals(Int.Mul)) {
+ z = z0 * z1;
+ } else if (e.FunctionSymbol.Equals(Int.Div)) {
+ z = z0 / z1;
+ } else if (e.FunctionSymbol.Equals(Int.Mod)) {
+ z = z0 % z1;
+ } else {
+ z = BigNum.ZERO;
+ return false;
+ }
+ return true;
+ }
+ }
+
+ z = BigNum.ZERO;
+ return false;
+ }
+ }
+}
diff --git a/Source/AIFramework/VariableMap/ConstantExpressions.ssc b/Source/AIFramework/VariableMap/ConstantExpressions.ssc
new file mode 100644
index 00000000..306c4e8f
--- /dev/null
+++ b/Source/AIFramework/VariableMap/ConstantExpressions.ssc
@@ -0,0 +1,538 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+
+ /////////////////////////////////////////////////////////////////////////////////
+ // The Abstract domain for determining "constant" expressions
+ // i.e. It determines which expression are statically binded
+ /////////////////////////////////////////////////////////////////////////////////
+/*
+using System;
+
+namespace Microsoft.AbstractInterpretationFramework
+{
+ using Microsoft.Contracts;
+ using System.Collections.Generic;
+ using Microsoft.AbstractInterpretationFramework;
+
+ /// <summary>
+ /// This is an abstract domain for inferring constant expressions
+ /// </summary>
+
+ public class ConstantExpressions : Lattice
+ {
+ /// <summary>
+ /// An abstract element is made of two maps:
+ /// + A map from variables to expressions \cup top ( i.e. for each variable, the expression it is binded )
+ /// + A map from variables to set of variabes ( i.e. for each variable, the set of variables that depends on its value )
+ /// </summary>
+ private class AbstractElement: Element
+ {
+ private Dictionary<IVariable!, BindExpr> variableBindings;
+ private Dictionary<IVariable!, List<IVariable>> variableDependences;
+
+ static private AbstractElement! bottom;
+ static public Element! Bottom
+ {
+ get
+ {
+ if(bottom == null)
+ {
+ bottom = new AbstractElement();
+ bottom.variableBindings = null;
+ bottom.variableDependences = null;
+ }
+ assert bottom.variableBindings == null && bottom.variableDependences == null;
+ return bottom;
+ }
+ }
+
+ static public Element! Top
+ {
+ get
+ {
+ return new AbstractElement();
+ }
+ }
+
+ AbstractElement()
+ {
+ this.variableBindings = new Dictionary<IVariable!, BindExpr>();
+ this.variableDependences = new Dictionary<IVariable!, List<IVariable>>();
+ }
+
+ /// <summary>
+ /// Our abstract element is top if and only if it has any constraint on variables
+ /// </summary>
+ public bool IsTop
+ {
+ get
+ {
+ return this.variableBindings.Keys.Count == 0 && this.variableDependences.Keys.Count == 0;
+ }
+ }
+
+ /// <summary>
+ /// Our abstract element is bottom if and only if the maps are null
+ /// </summary>
+ public bool IsBottom
+ {
+ get
+ {
+ assert (this.variableBindings == null) <==> (this.variableDependences == null);
+ return this.variableBindings == null && this.variableDependences == null;
+ }
+ }
+
+ /// <summary>
+ /// The pointwise join...
+ /// </summary>
+ public static AbstractElement! Join(AbstractElement! left, AbstractElement! right)
+ {
+ AbstractElement! result = new AbstractElement();
+
+ // Put all the variables in the left
+ foreach(IVariable! var in left.variableBindings.Keys)
+ {
+ BindExpr leftVal = left.variableBindings[var];
+ assert leftVal != null;
+
+ BindExpr rightVal = right.variableBindings[var];
+
+ if(rightVal== null) // the expression is not there
+ {
+ result.variableBindings.Add(var, leftVal);
+ }
+ else // both abstract elements have a definition for the variable....
+ {
+ result.variableBindings.Add(var, BindExpr.Join(leftVal, rightVal));
+ }
+ }
+
+ // Put all the variables in the right
+ foreach(IVariable! var in right.variableBindings.Keys)
+ {
+ BindExpr rightVal = right.variableBindings[var];
+ assert rightVal != null;
+
+ BindExpr leftVal = left.variableBindings[var];
+
+ if(rightVal== null) // the expression is not there
+ {
+ result.variableBindings.Add(var, rightVal);
+ }
+ else // both abstract elements have a definition for the variable....
+ {
+ result.variableBindings.Add(var, BindExpr.Join(rightVal, leftVal));
+ }
+ }
+
+ // Join the dependencies...
+ foreach(IVariable! var in left.variableDependences.Keys)
+ {
+ List<IVariable> dependencies = left.variableDependences[var];
+ List<IVariable> dup = new List<IVariable>(dependencies);
+
+ result.variableDependences.Add(var, dup);
+ }
+
+ foreach(IVariable! var in right.variableDependences.Keys)
+ {
+ if(result.variableDependences.ContainsKey(var))
+ {
+ List<IVariable> dependencies = result.variableDependences[var];
+ dependencies.AddRange(right.variableDependences[var]);
+ }
+ else
+ {
+ List<IVariable> dependencies = right.variableDependences[var];
+ List<IVariable> dup = new List<IVariable>(dependencies);
+
+ result.variableDependences.Add(var, dup);
+ }
+ }
+
+ // Normalize... i.e. for the variables such thas they point to an unknown expression (top) we have to update also their values
+ result.Normalize();
+
+ return result;
+ }
+
+
+ ///<summary>
+ /// Normalize the current abstract element, in that it propagetes the "dynamic" information throughtout the abstract element
+ ///</summary>
+ public void Normalize()
+ {
+ if(this.IsBottom)
+ return;
+ if(this.IsTop)
+ return;
+ assert this.variableBindings != null;
+
+ bool atFixpoint = false;
+
+ while(!atFixpoint)
+ {
+ atFixpoint = true; // guess that we've got the fixpoint...
+
+ foreach(IVariable x in this.variableBindings.Keys)
+ {
+ if(this.variableBindings[x].IsTop) // It means that the variable is tied to a dynamic expression
+ {
+ foreach(IVariable y in this.variableDependences[x]) // The all the variables that depend on x are also dynamic...
+ {
+ assert x != y; // A variable cannot depend on itself...
+ if(!this.variableBindings[y].IsTop)
+ {
+ this.variableBindings[y] = BindExpr.Top;
+ atFixpoint = false; // the assumption that we were at the fixpoint was false, we have still to propagate some information...
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// The pointwise meet...
+ /// </summary>
+ public static AbstractElement! Meet(AbstractElement! left, AbstractElement! right)
+ {
+ AbstractElement! result = new AbstractElement();
+
+ // Put the variables that are both in left and right
+ foreach(IVariable var in left.variableBindings.Keys)
+ {
+ if(right.variableBindings.ContainsKey(var))
+ {
+ result.variableBindings.Add(var, BindExpr.Meet(left.variableBindings[var], right.variableBindings[var]));
+ }
+ }
+
+ // Intersect the dependencies
+ foreach(IVariable var in result.variableBindings.Keys)
+ {
+ List<IVariable> depLeft = left.variableDependences[var];
+ List<IVariable> depRight = right.variableDependences[var];
+
+ // Intersect the two sets
+ result.variableDependences.Add(var, depLeft);
+ foreach(IVariable v in depRight)
+ {
+ if(!result.variableDependences.ContainsKey(v))
+ {
+ result.variableDependences.Remove(v);
+ }
+ }
+ }
+
+ // Now we remove the dependencies with variables not in variableBindings
+ List<IVariable>! varsToRemove = new List<IVariable>();
+
+ foreach(IVariable var in result.
+
+
+ }
+
+ /// <summary>
+ /// Clone the current abstract element
+ /// </summary>
+ public override Element! Clone()
+ {
+ AbstractElement cloned = new AbstractElement();
+ foreach(IVariable var in this.variableBindings.Keys)
+ {
+ cloned.variableBindings.Add(var, this.variableBindings[var]);
+ }
+
+ foreach(IVariable var in this.variableDependences.Keys)
+ {
+ List<IVariable> dependingVars = this.variableDependences[var];
+ List<IVariable> clonedDependingVars = new List<IVariable>(dependingVars);
+ cloned.variableDependences.Add(var, clonedDependingVars);
+ }
+ }
+
+ /// <summary>
+ /// Return the variables that have a binding
+ /// </summary>
+ public override ICollection<IVariable!>! FreeVariables()
+ {
+ List<IVariable!> vars = new List<IVariable!>(this.variableBindings.Keys);
+
+ return vars;
+ }
+
+ public override string! ToString()
+ {
+ string! retString = "";
+ retString += "Bindings";
+
+ foreach(IVariable var in this.variableBindings.Keys)
+ {
+ string! toAdd = var.ToString() + " -> " + this.variableBindings[var];
+ retString += toAdd + ",";
+ }
+
+ retString += "\nDependencies";
+ foreach(IVariable var in this.variableDependences.Keys)
+ {
+ string! toAdd = var.ToString() + " -> " + this.variableDependences[var];
+ retString += toAdd + ",";
+ }
+
+ return retString;
+ }
+ }
+
+ public override Element! Top
+ {
+ get
+ {
+ return AbstractElement.Top;
+ }
+ }
+
+ public override Element! Bottom
+ {
+ get
+ {
+ return AbstractElement.Bottom;
+ }
+ }
+
+ public override bool IsTop(Element! e)
+ {
+ assert e is AbstractElement;
+ AbstractElement! absElement = (AbstractElement) e;
+
+ return absElement.IsTop;
+ }
+
+ public override bool IsBottom(Element! e)
+ {
+ assert e is AbstractElement;
+ AbstractElement absElement = (AbstractElement) e;
+ return absElement.IsBottom;
+ }
+
+ /// <summary>
+ /// Perform the pointwise join of the two abstract elements
+ /// </summary>
+ public override Element! NontrivialJoin(Element! a, Element! b)
+ {
+ assert a is AbstractElement;
+ assert b is AbstractElement;
+
+ AbstractElement! left = (AbstractElement!) a;
+ AbstractElement! right = (AbstractElement!) b;
+
+ return AbstractElement.Join(left, right);
+ }
+
+ /// <summary>
+ /// Perform the pointwise meet of two abstract elements
+ /// </summary>
+ public override Element! NontrivialMeet(Element! a, Element!b)
+ {
+ assert a is AbstractElement;
+ assert b is AbstractElement;
+
+ AbstractElement! left = (AbstractElement!) a;
+ AbstractElement! right = (AbstractElement!) b;
+
+ return AbstractElement.Meet(left, right);
+ }
+
+
+ }
+
+ /// <summary>
+ /// A wrapper in order to have the algebraic datatype BindExpr := IExpr | Top
+ /// </summary>
+ abstract class BindExpr
+ {
+ /// <summary>
+ /// True iff this expression is instance of BindExprTop
+ /// </summary>
+ public bool IsTop
+ {
+ get
+ {
+ return this is BindExprTop;
+ }
+ }
+
+ static public BindExpr Top
+ {
+ get
+ {
+ return BindExprTop.UniqueTop;
+ }
+ }
+
+ /// <summary>
+ /// True iff this expression is instance of BindExprBottom
+ /// </summary>
+ public bool IsBottom
+ {
+ get
+ {
+ return this is BindExprBottom;
+ }
+ }
+
+ static public BindExpr Bottom
+ {
+ get
+ {
+ return BindExprBottom.UniqueBottom;
+ }
+ }
+
+ public static BindExpr! Join(BindExpr! left, BindExpr! right)
+ {
+ if(left.IsTop || right.IsTop)
+ {
+ return BindExpr.Top;
+ }
+ else if(left.IsBottom)
+ {
+ return right;
+ }
+ else if(right.IsBottom)
+ {
+ return left;
+ }
+ else if(left.EmbeddedExpr != right.EmbeddedExpr)
+ {
+ return BindExpr.Top;
+ }
+ else // left.EmbeddedExpr == right.EmbeddedExpr
+ {
+ return left;
+ }
+ }
+
+ public static BindExpr! Meet(BindExpr! left, BindExpr! right)
+ {
+ if(left.IsTop)
+ {
+ return right;
+ }
+ else if(right.IsTop)
+ {
+ return right;
+ }
+ else if(left.IsBottom || right.IsBottom)
+ {
+ return BindExpr.Bottom;
+ }
+ else if(left.EmbeddedExpr != right.EmbeddedExpr)
+ {
+ return BindExpr.Bottom;
+ }
+ else // left.EmbeddedExpr == right.EmbeddedExpr
+ {
+ return left;
+ }
+ }
+
+ abstract public IExpr! EmbeddedExpr
+ {
+ get;
+ }
+
+ }
+
+ /// <summary>
+ /// A wrapper for an integer
+ /// </summary>
+ class Expr : BindExpr
+ {
+ private IExpr! exp;
+
+ public Expr(IExpr! exp)
+ {
+ this.exp = exp;
+ }
+
+ override public IExpr! EmbeddedExpr
+ {
+ get
+ {
+ return this.exp;
+ }
+ }
+
+ public override string! ToString()
+ {
+ return this.exp.ToString();
+ }
+ }
+
+ /// <summary>
+ /// The dynamic expression
+ /// </summary>
+ class BindExprTop : BindExpr
+ {
+ private BindExprTop top = new BindExprTop();
+ static public BindExprTop! UniqueTop
+ {
+ get
+ {
+ return this.top;
+ }
+ }
+
+ private BindExprTop() {}
+
+ override public IExpr! EmbeddedExpr
+ {
+ get
+ {
+ assert false; // If we get there, we have an error
+ }
+ }
+
+ public override string! ToString()
+ {
+ return "<dynamic expression>";
+ }
+ }
+
+ /// <summary>
+ /// The unreachable expression
+ /// </summary>
+ class BindExprBottom : BindExpr
+ {
+ private BindExprBottom! bottom = new BindExprBottom();
+ static public BindExprBottom! UniqueBottom
+ {
+ get
+ {
+ return this.bottom;
+ }
+ }
+
+ private BindExprBottom() {}
+
+ override public IExpr! EmbeddedExpr
+ {
+ get
+ {
+ assert false;
+ }
+ }
+
+ public override string! ToString()
+ {
+ return "<unreachable expression>";
+ }
+ }
+
+} // end namespace Microsoft.AbstractInterpretationFramework
+*/ \ No newline at end of file
diff --git a/Source/AIFramework/VariableMap/DynamicTypeLattice.ssc b/Source/AIFramework/VariableMap/DynamicTypeLattice.ssc
new file mode 100644
index 00000000..b9d1a7a4
--- /dev/null
+++ b/Source/AIFramework/VariableMap/DynamicTypeLattice.ssc
@@ -0,0 +1,475 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+namespace Microsoft.AbstractInterpretationFramework
+{
+ using System.Collections;
+ using System.Diagnostics;
+ using System.Compiler.Analysis;
+ using Microsoft.SpecSharp.Collections;
+ using Microsoft.Contracts;
+
+ /// <summary>
+ /// Represents information about the dynamic type of a variable. In particular, for a
+ /// variable "v", represents either Bottom, "typeof(v)==T" for some type T, or a set
+ /// of constraints "typeof(v) subtype of T_i for some number of T_i's.
+ /// </summary>
+ public class DynamicTypeLattice : MicroLattice
+ {
+ enum What { Bottom, Exact, Bounds }
+
+ private class Elt : Element {
+ // Representation:
+ // - Bottom is represented by: what==What.Bottom
+ // - An exact type T is represented by: what==What.Exact && ty==T
+ // - A set of type constraints T0, T1, T2, ..., T{n-1} is represented by:
+ // -- if n==0: what==What.Bounds && ty==null && manyBounds==null
+ // -- if n==1: what==What.Bounds && ty==T0 && manyBounds==null
+ // -- if n>=2: what==What.Bounds && ty==null &&
+ // manyBounds!=null && manyBounds.Length==n &&
+ // manyBounds[0]==T0 && manyBounds[1]==T1 && ... && manyBounds[n-1]==T{n-1}
+ // The reason for keeping the one-and-only bound in "ty" in case n==1 is to try
+ // to prevent the need for allocating a whole array of bounds, since 1 bound is
+ // bound to be common.
+ // In the representation, there are no redundant bounds in manyBounds.
+ // It is assumed that the types can can occur as exact bounds form a single-inheritance
+ // hierarchy. That is, if T0 and T1 are types that can occur as exact types, then
+ // there is no v such that typeof(v) is a subtype of both T0 and T1, unless T0 and T1 are
+ // the same type.
+ public readonly What what;
+ public readonly IExpr ty;
+ [Rep]
+ public readonly IExpr[] manyBounds;
+ invariant what == What.Bottom ==> ty == null && manyBounds == null;
+ invariant manyBounds != null ==> what == What.Bounds;
+ invariant manyBounds != null ==> forall{int i in (0:manyBounds.Length); manyBounds[i] != null};
+
+ public Elt(What what, IExpr ty)
+ requires what == What.Bottom ==> ty == null;
+ requires what == What.Exact ==> ty != null;
+ {
+ this.what = what;
+ this.ty = ty;
+ this.manyBounds = null;
+ }
+
+ public Elt(IExpr[]! bounds)
+ requires forall{int i in (0:bounds.Length); bounds[i] != null};
+ {
+ this.what = What.Bounds;
+ if (bounds.Length == 0) {
+ this.ty = null;
+ this.manyBounds = null;
+ } else if (bounds.Length == 1) {
+ this.ty = bounds[0];
+ this.manyBounds = null;
+ } else {
+ this.ty = null;
+ this.manyBounds = bounds;
+ }
+ }
+
+ /// <summary>
+ /// Constructs an Elt with "n" bounds, namely the n non-null values of the "bounds" list.
+ /// </summary>
+ [NotDelayed]
+ public Elt(ArrayList /*IExpr*/! bounds, int n)
+ requires 0 <= n && n <= bounds.Count;
+ {
+ this.what = What.Bounds;
+ if (n > 1) {
+ this.manyBounds = new IExpr[n];
+ }
+ int k = 0;
+ foreach (IExpr bound in bounds) {
+ if (bound != null) {
+ assert k != n;
+ if (n == 1) {
+ assert this.ty == null;
+ this.ty = bound;
+ } else {
+ assume manyBounds != null;
+ manyBounds[k] = bound;
+ }
+ k++;
+ }
+ }
+ assert k == n;
+ }
+
+ public int BoundsCount
+ {
+ get
+ ensures 0 <= result;
+ {
+ if (manyBounds != null) {
+ return manyBounds.Length;
+ } else if (ty != null) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+ }
+
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public override System.Collections.Generic.ICollection<IVariable!>! FreeVariables()
+ {
+ return (!) (new System.Collections.Generic.List<IVariable!>()).AsReadOnly();
+ }
+
+ public override Element! Clone()
+ {
+ if (this.manyBounds != null)
+ return new Elt(this.manyBounds);
+ else
+ return new Elt(this.what, this.ty);
+ }
+ }
+
+ readonly ITypeExprFactory! factory;
+ readonly IPropExprFactory! propFactory;
+
+ public DynamicTypeLattice(ITypeExprFactory! factory, IPropExprFactory! propFactory)
+ {
+ this.factory = factory;
+ this.propFactory = propFactory;
+ // base();
+ }
+
+ public override Element! Top
+ {
+ get { return new Elt(What.Bounds, null); }
+ }
+
+ public override Element! Bottom
+ {
+ get { return new Elt(What.Bottom, null); }
+ }
+
+ public override bool IsTop (Element! element)
+ {
+ Elt e = (Elt)element;
+ return e.what == What.Bounds && e.ty == null && e.manyBounds == null;
+ }
+
+ public override bool IsBottom(Element! element)
+ {
+ Elt e = (Elt)element;
+ return e.what == What.Bottom;
+ }
+
+ public override Element! NontrivialJoin(Element! first, Element! second)
+ {
+ Elt a = (Elt)first;
+ Elt b = (Elt)second;
+ assert a.what != What.Bottom && b.what != What.Bottom;
+ if (a.what == What.Exact && b.what == What.Exact) {
+ assert a.ty != null && b.ty != null;
+ if (factory.IsTypeEqual(a.ty, b.ty)) {
+ return a;
+ } else {
+ return new Elt(What.Bounds, factory.JoinTypes(a.ty, b.ty));
+ }
+ }
+
+ // The result is going to be a Bounds, since at least one of the operands is a Bounds.
+ assert 1 <= a.BoundsCount && 1 <= b.BoundsCount; // a preconditions is that neither operand is Top
+ int n = a.BoundsCount + b.BoundsCount;
+
+ // Special case: a and b each has exactly one bound
+ if (n == 2) {
+ assert a.ty != null && b.ty != null;
+ IExpr! join = factory.JoinTypes(a.ty, b.ty);
+ if (join == a.ty && a.what == What.Bounds) {
+ return a;
+ } else if (join == b.ty && b.what == What.Bounds) {
+ return b;
+ } else {
+ return new Elt(What.Bounds, join);
+ }
+ }
+
+ // General case
+ ArrayList /*IExpr*/ allBounds = new ArrayList /*IExpr*/ (n); // final size
+ ArrayList /*IExpr!*/ result = new ArrayList /*IExpr!*/ (n); // a guess at the size, but could be as big as size(a)*size(b)
+ if (a.ty != null) {
+ allBounds.Add(a.ty);
+ } else {
+ allBounds.AddRange((!)a.manyBounds);
+ }
+ int bStart = allBounds.Count;
+ if (b.ty != null) {
+ allBounds.Add(b.ty);
+ } else {
+ allBounds.AddRange((!)b.manyBounds);
+ }
+ // compute the join of each pair, putting non-redundant joins into "result"
+ for (int i = 0; i < bStart; i++) {
+ IExpr! aBound = (IExpr!)allBounds[i];
+ for (int j = bStart; j < allBounds.Count; j++) {
+ IExpr! bBound = (IExpr!)allBounds[j];
+
+ IExpr! join = factory.JoinTypes(aBound, bBound);
+
+ int k = 0;
+ while (k < result.Count) {
+ IExpr! r = (IExpr!)result[k];
+ if (factory.IsSubType(join, r)) {
+ // "join" is more restrictive than a bound already placed in "result",
+ // so toss out "join" and compute the join of the next pair
+ goto NEXT_PAIR;
+ } else if (factory.IsSubType(r, join)) {
+ // "join" is less restrictive than a bound already placed in "result",
+ // so toss out that old bound
+ result.RemoveAt(k);
+ } else {
+ k++;
+ }
+ }
+ result.Add(join);
+ NEXT_PAIR: {}
+ }
+ }
+ return new Elt(result, result.Count);
+ }
+
+
+ public override Element! NontrivialMeet(Element! first, Element! second)
+ {
+ Elt a = (Elt)first;
+ Elt b = (Elt)second;
+ assert a.what != What.Bottom && b.what != What.Bottom;
+
+ if (a.what == What.Exact && b.what == What.Exact) {
+ assert a.ty != null && b.ty != null;
+ if (factory.IsTypeEqual(a.ty, b.ty)) {
+ return a;
+ } else {
+ return Bottom;
+ }
+
+ } else if (a.what == What.Exact || b.what == What.Exact) {
+ // One is Bounds, the other Exact. Make b be the Bounds one.
+ if (a.what == What.Bounds) {
+ Elt tmp = a;
+ a = b;
+ b = tmp;
+ }
+ assert a.what == What.Exact && b.what == What.Bounds;
+ // Check the exact type against all bounds. If the exact type is more restrictive
+ // than all bounds, then return it. If some bound is not met by the exact type, return
+ // bottom.
+ assert a.ty != null;
+ if (b.ty != null && !factory.IsSubType(a.ty, b.ty)) {
+ return Bottom;
+ }
+ if (b.manyBounds != null) {
+ foreach (IExpr! bound in b.manyBounds) {
+ if (!factory.IsSubType(a.ty, bound)) {
+ return Bottom;
+ }
+ }
+ }
+ return a;
+ }
+
+ else {
+ // Both operands are Bounds.
+ assert a.what == What.Bounds && b.what == What.Bounds;
+
+ // Take all the bounds, but prune those bounds that follow from others.
+ assert 1 <= a.BoundsCount && 1 <= b.BoundsCount; // a preconditions is that neither operand is Top
+ int n = a.BoundsCount + b.BoundsCount;
+ // Special case: a and b each has exactly one bound
+ if (n == 2) {
+ assert a.ty != null && b.ty != null;
+ if (factory.IsSubType(a.ty, b.ty)) {
+ // a is more restrictive
+ return a;
+ } else if (factory.IsSubType(b.ty, a.ty)) {
+ // b is more restrictive
+ return b;
+ } else {
+ IExpr[]! bounds = new IExpr[2];
+ bounds[0] = a.ty;
+ bounds[1] = b.ty;
+ return new Elt(bounds);
+ }
+ }
+
+ // General case
+ ArrayList /*IExpr*/ allBounds = new ArrayList /*IExpr*/ (n);
+ if (a.ty != null) {
+ allBounds.Add(a.ty);
+ } else {
+ allBounds.AddRange((!)a.manyBounds);
+ }
+ int bStart = allBounds.Count;
+ if (b.ty != null) {
+ allBounds.Add(b.ty);
+ } else {
+ allBounds.AddRange((!)b.manyBounds);
+ }
+ for (int i = 0; i < bStart; i++) {
+ IExpr! aBound = (IExpr!)allBounds[i];
+ for (int j = bStart; j < allBounds.Count; j++) {
+ IExpr bBound = (IExpr!)allBounds[j];
+ if (bBound == null) {
+ continue;
+ } else if (factory.IsSubType(aBound, bBound)) {
+ // a is more restrictive, so blot out the b bound
+ allBounds[j] = null;
+ n--;
+ } else if (factory.IsSubType(bBound, aBound)) {
+ // b is more restrictive, so blot out the a bound
+ allBounds[i] = null;
+ n--;
+ goto CONTINUE_OUTER_LOOP;
+ }
+ }
+ CONTINUE_OUTER_LOOP: {}
+ }
+ assert 1 <= n;
+ return new Elt(allBounds, n);
+ }
+ }
+
+ public override Element! Widen (Element! first, Element! second)
+ {
+ return Join(first,second);
+ }
+
+ protected override bool AtMost (Element! first, Element! second) // this <= that
+ {
+ Elt! a = (Elt!)first;
+ Elt! b = (Elt!)second;
+ assert a.what != What.Bottom && b.what != What.Bottom;
+
+ if (a.what == What.Exact && b.what == What.Exact) {
+ assert a.ty != null && b.ty != null;
+ return factory.IsTypeEqual(a.ty, b.ty);
+ } else if (b.what == What.Exact) {
+ return false;
+ } else if (a.what == What.Exact) {
+ assert a.ty != null;
+ if (b.ty != null) {
+ return factory.IsSubType(a.ty, b.ty);
+ } else {
+ return forall{IExpr! bound in b.manyBounds; factory.IsSubType(a.ty, bound)};
+ }
+ } else {
+ assert a.what == What.Bounds && b.what == What.Bounds;
+ assert a.ty != null || a.manyBounds != null; // a precondition is that a is not Top
+ assert b.ty != null || b.manyBounds != null; // a precondition is that b is not Top
+ // Return true iff: for each constraint in b, there is a stricter constraint in a.
+ if (a.ty != null && b.ty != null) {
+ return factory.IsSubType(a.ty, b.ty);
+ } else if (a.ty != null) {
+ return forall{IExpr! bound in b.manyBounds; factory.IsSubType(a.ty, bound)};
+ } else if (b.ty != null) {
+ return exists{IExpr! bound in a.manyBounds; factory.IsSubType(bound, b.ty)};
+ } else {
+ return forall{IExpr! bBound in b.manyBounds;
+ exists{IExpr! aBound in a.manyBounds; factory.IsSubType(aBound, bBound)}};
+ }
+ }
+ }
+
+ public override IExpr! ToPredicate(IVariable! var, Element! element) {
+ Elt e = (Elt)element;
+ switch (e.what) {
+ case What.Bottom:
+ return propFactory.False;
+ case What.Exact:
+ return factory.IsExactlyA(var, (!)e.ty);
+ case What.Bounds:
+ if (e.ty == null && e.manyBounds == null) {
+ return propFactory.True;
+ } else if (e.ty != null) {
+ return factory.IsA(var, e.ty);
+ } else {
+ IExpr! p = factory.IsA(var, (IExpr!)((!)e.manyBounds)[0]);
+ for (int i = 1; i < e.manyBounds.Length; i++) {
+ p = propFactory.And(p, factory.IsA(var, (IExpr!)e.manyBounds[i]));
+ }
+ return p;
+ }
+ default:
+ assert false;
+ throw new System.Exception();
+ }
+ }
+
+ public override IExpr GetFoldExpr(Element! e) {
+ // cannot fold into an expression that can be substituted for the variable
+ return null;
+ }
+
+ public override bool Understands(IFunctionSymbol! f, IList/*<IExpr!>*/! args) {
+ bool isEq = f.Equals(Microsoft.AbstractInterpretationFramework.Value.Eq);
+ if (isEq || f.Equals(Microsoft.AbstractInterpretationFramework.Value.Subtype)) {
+ assert args.Count == 2;
+ IExpr! arg0 = (IExpr!)args[0];
+ IExpr! arg1 = (IExpr!)args[1];
+
+ // Look for $typeof(var) == t or t == $typeof(var) or $typeof(var) <: t
+ if (isEq && factory.IsTypeConstant(arg0)) {
+ // swap the arguments
+ IExpr! tmp = arg0;
+ arg0 = arg1;
+ arg1 = tmp;
+ } else if (!factory.IsTypeConstant(arg1)) {
+ return false;
+ }
+ IFunApp typeofExpr = arg0 as IFunApp;
+ if (typeofExpr != null &&
+ typeofExpr.FunctionSymbol.Equals(Microsoft.AbstractInterpretationFramework.Value.Typeof)) {
+ assert typeofExpr.Arguments.Count == 1;
+ if (typeofExpr.Arguments[0] is IVariable) {
+ // we have a match
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public override Element! EvaluatePredicate(IExpr! e) {
+ IFunApp nary = e as IFunApp;
+ if (nary != null) {
+
+ bool isEq = nary.FunctionSymbol.Equals(Microsoft.AbstractInterpretationFramework.Value.Eq);
+ if (isEq || nary.FunctionSymbol.Equals(Microsoft.AbstractInterpretationFramework.Value.Subtype)) {
+ IList/*<IExpr!>*/! args = nary.Arguments;
+ assert args.Count == 2;
+ IExpr! arg0 = (IExpr!)args[0];
+ IExpr! arg1 = (IExpr!)args[1];
+
+ // Look for $typeof(var) == t or t == $typeof(var) or $typeof(var) <: t
+ if (isEq && factory.IsTypeConstant(arg0)) {
+ // swap the arguments
+ IExpr! tmp = arg0;
+ arg0 = arg1;
+ arg1 = tmp;
+ } else if (!factory.IsTypeConstant(arg1)) {
+ return Top;
+ }
+ IFunApp typeofExpr = arg0 as IFunApp;
+ if (typeofExpr != null &&
+ typeofExpr.FunctionSymbol.Equals(Microsoft.AbstractInterpretationFramework.Value.Typeof)) {
+ assert typeofExpr.Arguments.Count == 1;
+ if (typeofExpr.Arguments[0] is IVariable) {
+ // we have a match
+ return new Elt(isEq ? What.Exact : What.Bounds, arg1);
+ }
+ }
+ }
+ }
+ return Top;
+ }
+
+ }
+}
diff --git a/Source/AIFramework/VariableMap/Intervals.ssc b/Source/AIFramework/VariableMap/Intervals.ssc
new file mode 100644
index 00000000..721b175d
--- /dev/null
+++ b/Source/AIFramework/VariableMap/Intervals.ssc
@@ -0,0 +1,790 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Collections;
+using System.Compiler.Analysis;
+using Microsoft.AbstractInterpretationFramework.Collections;
+using Microsoft.Contracts;
+using Microsoft.Basetypes;
+
+/////////////////////////////////////////////////////////////////////////////////
+// An implementation of the interval abstract domain
+/////////////////////////////////////////////////////////////////////////////////
+
+namespace Microsoft.AbstractInterpretationFramework
+{
+ public class IntervalLattice : MicroLattice
+ {
+ readonly ILinearExprFactory! factory;
+
+ public IntervalLattice(ILinearExprFactory! factory)
+ {
+ this.factory = factory;
+ // base();
+ }
+
+ public override bool UnderstandsBasicArithmetics
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ public override Element! Top
+ {
+ get
+ {
+ return IntervalElement.Top;
+ }
+ }
+
+ public override Element! Bottom
+ {
+ get
+ {
+ return IntervalElement.Bottom;
+ }
+ }
+
+ /// <summary>
+ /// The paramter is the top?
+ /// </summary>
+ public override bool IsTop(Element! element)
+ {
+ IntervalElement interval = (IntervalElement) element;
+
+ return interval.IsTop();
+ }
+
+ /// <summary>
+ /// The parameter is the bottom?
+ /// </summary>
+ public override bool IsBottom(Element! element)
+ {
+ IntervalElement interval = (IntervalElement) element;
+
+ return interval.IsBottom();
+ }
+
+ /// <summary>
+ /// The classic, pointwise, join of intervals
+ /// </summary>
+ public override Element! NontrivialJoin(Element! left, Element! right)
+ {
+ IntervalElement! leftInterval = (IntervalElement!) left;
+ IntervalElement! rightInterval = (IntervalElement) right;
+
+ ExtendedInt inf = ExtendedInt.Inf(leftInterval.Inf, rightInterval.Inf);
+ ExtendedInt sup = ExtendedInt.Sup(leftInterval.Sup, rightInterval.Sup);
+
+ IntervalElement! join = IntervalElement.Factory(inf, sup);
+
+ return join;
+ }
+
+ /// <summary>
+ /// The classic, pointwise, meet of intervals
+ /// </summary>
+ public override Element! NontrivialMeet(Element! left, Element! right)
+ {
+ IntervalElement! leftInterval = (IntervalElement!) left;
+ IntervalElement! rightInterval = (IntervalElement) right;
+
+ ExtendedInt inf = ExtendedInt.Sup(leftInterval.Inf, rightInterval.Inf);
+ ExtendedInt sup = ExtendedInt.Inf(leftInterval.Sup, rightInterval.Sup);
+
+ return IntervalElement.Factory(inf, sup);
+ }
+
+
+ /// <summary>
+ /// The very simple widening of intervals, to be improved with thresholds
+ /// left is the PREVIOUS value in the iterations and right is the NEW one
+ /// </summary>
+ public override Element! Widen(Element! left, Element! right)
+ {
+ IntervalElement! prevInterval = (IntervalElement!) left;
+ IntervalElement! nextInterval = (IntervalElement!) right;
+
+ ExtendedInt inf = nextInterval.Inf < prevInterval.Inf ? ExtendedInt.MinusInfinity : prevInterval.Inf;
+ ExtendedInt sup = nextInterval.Sup > prevInterval.Sup ? ExtendedInt.PlusInfinity : prevInterval.Sup;
+
+ IntervalElement widening = IntervalElement.Factory(inf, sup);
+
+ return widening;
+ }
+
+
+ /// <summary>
+ /// Return true iff the interval left is containted in right
+ /// </summary>
+ protected override bool AtMost(Element! left, Element! right)
+ {
+ IntervalElement! leftInterval = (IntervalElement!) left;
+ IntervalElement! rightInterval = (IntervalElement!) right;
+
+ if(leftInterval.IsBottom() || rightInterval.IsTop())
+ return true;
+
+ return rightInterval.Inf <= leftInterval.Inf && leftInterval.Sup <= rightInterval.Sup;
+ }
+
+ /// <summary>
+ /// Return just null
+ /// </summary>
+ public override IExpr GetFoldExpr(Element! element)
+ {
+ return null;
+ }
+
+ /// <summary>
+ /// return a predicate inf "\leq x and x "\leq" sup (if inf [or sup] is not oo)
+ /// </summary>
+ public override IExpr! ToPredicate(IVariable! var, Element! element)
+ {
+ IntervalElement! interval = (IntervalElement!) element;
+ IExpr lowerBound = null;
+ IExpr upperBound = null;
+
+ if(! (interval.Inf is InfinitaryInt))
+ {
+ IExpr constant = this.factory.Const(interval.Inf.Value);
+ lowerBound = this.factory.AtMost(constant, var); // inf <= var
+ }
+ if(! (interval.Sup is InfinitaryInt))
+ {
+ IExpr constant = this.factory.Const(interval.Sup.Value);
+ upperBound = this.factory.AtMost(var, constant); // var <= inf
+ }
+
+ if(lowerBound != null && upperBound != null)
+ return this.factory.And(lowerBound, upperBound); // inf <= var && var <= sup
+ else
+ if(lowerBound != null)
+ return lowerBound;
+ else
+ if(upperBound != null)
+ return upperBound;
+ else // If we reach this point, both lowerBound and upperBound are null, i.e. we have no bounds on var, so we return simply true...
+ return this.factory.True;
+ }
+
+ /// <summary>
+ /// For the moment consider just equalities. Other case must be considered
+ /// </summary>
+ public override bool Understands(IFunctionSymbol! f, IList /*<IExpr*/ ! args)
+ {
+ return f.Equals(Microsoft.AbstractInterpretationFramework.Value.Eq);
+ }
+
+
+ /// <summary>
+ /// Evaluate the predicate passed as input according the semantics of intervals
+ /// </summary>
+ public override Element! EvaluatePredicate(IExpr! pred)
+ {
+ return this.EvaluatePredicateWithState(pred, null);
+ }
+
+ /// <summary>
+ /// Evaluate the predicate passed as input according the semantics of intervals and the given state.
+ /// Right now just basic arithmetic operations are supported. A future extension may consider an implementation of boolean predicates
+ /// </summary>
+ public override Element! EvaluatePredicateWithState(IExpr! pred, IFunctionalMap/* Var -> Element */ state)
+ {
+ if(pred is IFunApp)
+ {
+ IFunApp fun = (IFunApp) pred;
+ if(fun.FunctionSymbol.Equals(Microsoft.AbstractInterpretationFramework.Value.Eq)) // if it is a symbol of equality
+ {
+ IExpr! leftArg = (IExpr!) fun.Arguments[0];
+ IExpr! rightArg = (IExpr!) fun.Arguments[1];
+ if (leftArg is IVariable) {
+ return Eval(rightArg, state);
+ } else if (rightArg is IVariable) {
+ return Eval(leftArg, state);
+ }
+ }
+ }
+ // otherwise we simply return Top
+ return IntervalElement.Top;
+ }
+
+ /// <summary>
+ /// Evaluate the expression (that is assured to be an arithmetic expression, in the state passed as a parameter
+ /// </summary>
+ private IntervalElement! Eval(IExpr! exp, IFunctionalMap/* Var -> Element */ state)
+ {
+
+ IntervalElement! retVal = (IntervalElement!) Top;
+
+ // Eval the expression by structural induction
+
+
+ if(exp is IVariable && state != null) // A variable
+ {
+ object lookup = state[exp];
+ if(lookup is IntervalElement)
+ retVal = (IntervalElement) lookup;
+ else
+ {
+ retVal = (IntervalElement) Top;
+ }
+ }
+ else if(exp is IFunApp)
+ {
+ IFunApp fun = (IFunApp) exp;
+
+ if(fun.FunctionSymbol is IntSymbol) // An integer
+ {
+ IntSymbol intSymb = (IntSymbol) fun.FunctionSymbol;
+ BigNum val = intSymb.Value;
+
+ retVal = IntervalElement.Factory(val);
+ }
+ else if(fun.FunctionSymbol.Equals(Int.Negate)) // An unary minus
+ {
+ IExpr! arg = (IExpr!) fun.Arguments[0];
+ IntervalElement! argEval = Eval(arg, state);
+ IntervalElement! zero = IntervalElement.Factory(BigNum.ZERO);
+
+ retVal = zero - argEval;
+ }
+ else if(fun.Arguments.Count == 2)
+ {
+ IExpr! left = (IExpr!) fun.Arguments[0];
+ IExpr! right = (IExpr!) fun.Arguments[1];
+
+ IntervalElement! leftVal = Eval(left, state);
+ IntervalElement! rightVal = Eval(right, state);
+
+ if(fun.FunctionSymbol.Equals(Int.Add))
+ retVal = leftVal + rightVal;
+ else if(fun.FunctionSymbol.Equals(Int.Sub))
+ retVal = leftVal - rightVal;
+ else if(fun.FunctionSymbol.Equals(Int.Mul))
+ retVal = leftVal * rightVal;
+ else if(fun.FunctionSymbol.Equals(Int.Div))
+ retVal = leftVal / rightVal;
+ else if(fun.FunctionSymbol.Equals(Int.Mod))
+ retVal = leftVal % rightVal;
+ }
+ }
+
+ return retVal;
+ }
+
+ /// <summary>
+ /// Inner class standing for an interval on integers, possibly unbounded
+ /// </summary>
+ private class IntervalElement : Element
+ {
+ protected static readonly IntervalElement! TopInterval = new IntervalElement(new MinusInfinity(), new PlusInfinity()); // Top = [-oo , +oo]
+ protected static readonly IntervalElement! BottomInterval = new IntervalElement(new PlusInfinity(), new MinusInfinity()); // Bottom = [+oo, -oo]
+
+ private readonly ExtendedInt! inf;
+ private readonly ExtendedInt! sup;
+
+ public ExtendedInt! Inf
+ {
+ get
+ {
+ return inf;
+ }
+ }
+
+ public ExtendedInt! Sup
+ {
+ get
+ {
+ return sup;
+ }
+ }
+
+ // Construct the inteval [val, val]
+ protected IntervalElement(BigNum val)
+ {
+ this.inf = this.sup = ExtendedInt.Factory(val);
+ // base();
+ }
+
+ // Construct the interval [inf, sup]
+ protected IntervalElement(BigNum infInt, BigNum supInt)
+ {
+ this.inf = ExtendedInt.Factory(infInt);
+ this.sup = ExtendedInt.Factory(supInt);
+ // base(); // to please the compiler...
+ }
+
+ protected IntervalElement(ExtendedInt! inf, ExtendedInt! sup)
+ {
+ this.inf = inf;
+ this.sup = sup;
+ // base();
+ }
+
+ // Construct an Interval
+ public static IntervalElement! Factory(ExtendedInt! inf, ExtendedInt! sup)
+ {
+ if(inf is MinusInfinity && sup is PlusInfinity)
+ return Top;
+ if(inf > sup)
+ return Bottom;
+ // otherwise...
+ return new IntervalElement(inf, sup);
+ }
+
+ public static IntervalElement! Factory(BigNum i)
+ {
+ return new IntervalElement(i);
+ }
+
+ public static IntervalElement! Factory(BigNum inf, BigNum sup)
+ {
+ ExtendedInt! i = ExtendedInt.Factory(inf);
+ ExtendedInt! s = ExtendedInt.Factory(sup);
+
+ return Factory(i, s);
+ }
+
+ static public IntervalElement! Top
+ {
+ get
+ {
+ return TopInterval;
+ }
+ }
+
+ static public IntervalElement! Bottom
+ {
+ get
+ {
+ return BottomInterval;
+ }
+ }
+
+ public bool IsTop()
+ {
+ return this.inf is MinusInfinity && this.sup is PlusInfinity;
+ }
+
+ public bool IsBottom()
+ {
+ return this.inf > this.sup;
+ }
+
+ #region Below are the arithmetic operations lifted to intervals
+
+ // Addition
+ public static IntervalElement! operator+(IntervalElement! a, IntervalElement! b)
+ {
+ ExtendedInt! inf = a.inf + b.inf;
+ ExtendedInt! sup = a.sup + b.sup;
+
+ return Factory(inf, sup);
+ }
+
+ // Subtraction
+ public static IntervalElement! operator-(IntervalElement! a, IntervalElement! b)
+ {
+ ExtendedInt! inf = a.inf - b.sup;
+ ExtendedInt! sup = a.sup - b.inf;
+
+ IntervalElement! sub = Factory(inf, sup);
+
+ return sub;
+ }
+
+ // Multiplication
+ public static IntervalElement! operator*(IntervalElement! a, IntervalElement! b)
+ {
+ ExtendedInt! infinf = a.inf * b.inf;
+ ExtendedInt! infsup = a.inf * b.sup;
+ ExtendedInt! supinf = a.sup * b.inf;
+ ExtendedInt! supsup = a.sup * b.sup;
+
+ ExtendedInt! inf = ExtendedInt.Inf(infinf, infsup, supinf, supsup);
+ ExtendedInt! sup = ExtendedInt.Sup(infinf, infsup, supinf, supsup);
+
+ return Factory(inf, sup);
+ }
+
+ // Division
+ public static IntervalElement! operator/(IntervalElement! a, IntervalElement! b)
+ {
+ if(b.inf.IsZero && b.sup.IsZero) // Check division by zero
+ return IntervalElement.Top;
+
+ ExtendedInt! infinf = a.inf / b.inf;
+ ExtendedInt! infsup = a.inf / b.sup;
+ ExtendedInt! supinf = a.sup / b.inf;
+ ExtendedInt! supsup = a.sup / b.sup;
+
+ ExtendedInt! inf = ExtendedInt.Inf(infinf, infsup, supinf, supsup);
+ ExtendedInt! sup = ExtendedInt.Sup(infinf, infsup, supinf, supsup);
+
+ return Factory(inf, sup);
+ }
+
+ // Division
+ public static IntervalElement! operator%(IntervalElement! a, IntervalElement! b)
+ {
+ if(b.inf.IsZero && b.sup.IsZero) // Check division by zero
+ return IntervalElement.Top;
+
+ ExtendedInt! infinf = a.inf % b.inf;
+ ExtendedInt! infsup = a.inf % b.sup;
+ ExtendedInt! supinf = a.sup % b.inf;
+ ExtendedInt! supsup = a.sup % b.sup;
+
+ ExtendedInt inf = ExtendedInt.Inf(infinf, infsup, supinf, supsup);
+ ExtendedInt sup = ExtendedInt.Sup(infinf, infsup, supinf, supsup);
+
+ return Factory(inf, sup);
+ }
+
+ #endregion
+
+ #region Overriden methods
+
+ public override Element! Clone()
+ {
+ // Real copying should not be needed because intervals are immutable?
+ return this;
+ /*
+ int valInf = this.inf.Value;
+ int valSup = this.sup.Value;
+
+ ExtendedInt clonedInf = ExtendedInt.Factory(valInf);
+ ExtendedInt clonedSup = ExtendedInt.Factory(valSup);
+
+ return Factory(clonedInf, clonedSup);
+ */
+ }
+
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public override System.Collections.Generic.ICollection<IVariable!>! FreeVariables()
+ {
+ return (!) (new System.Collections.Generic.List<IVariable!>()).AsReadOnly();
+ }
+
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public override string! ToString()
+ {
+ return "[" + this.inf + ", " + this.sup + "]";
+ }
+
+ #endregion
+ }
+}
+
+
+ /// The interface for an extended integer
+ abstract class ExtendedInt
+ {
+ private static readonly PlusInfinity! cachedPlusInf = new PlusInfinity();
+ private static readonly MinusInfinity! cachedMinusInf = new MinusInfinity();
+
+ static public ExtendedInt! PlusInfinity
+ {
+ get
+ {
+ return cachedPlusInf;
+ }
+ }
+
+ static public ExtendedInt! MinusInfinity
+ {
+ get
+ {
+ return cachedMinusInf;
+ }
+ }
+
+ public abstract BigNum Value { get; }
+
+ public abstract int Signum { get; }
+
+ public bool IsZero {
+ get {
+ return Signum == 0;
+ }
+ }
+
+ public bool IsPositive {
+ get {
+ return Signum > 0;
+ }
+ }
+
+ public bool IsNegative {
+ get {
+ return Signum < 0;
+ }
+ }
+
+
+ #region Below are the extensions of arithmetic operations on extended integers
+
+ // Addition
+ public static ExtendedInt! operator+(ExtendedInt! a, ExtendedInt! b)
+ {
+ if (a is InfinitaryInt) {
+ return a;
+ } else if (b is InfinitaryInt) {
+ return b;
+ } else {
+ return ExtendedInt.Factory(a.Value + b.Value);
+ }
+ }
+
+ // Subtraction
+ public static ExtendedInt! operator-(ExtendedInt! a, ExtendedInt! b)
+ {
+ if (a is InfinitaryInt) {
+ return a;
+ } else if (b is InfinitaryInt) {
+ return UnaryMinus(b);
+ } else {
+ return ExtendedInt.Factory(a.Value - b.Value);
+ }
+ }
+
+ // Unary minus
+ public static ExtendedInt! operator-(ExtendedInt! a)
+ {
+ // BUGBUG: Some compiler error prevents the unary minus operator from being used
+ return UnaryMinus(a);
+ }
+
+ // Unary minus
+ public static ExtendedInt! UnaryMinus(ExtendedInt! a)
+ {
+ if(a is PlusInfinity)
+ return cachedMinusInf;
+ if(a is MinusInfinity)
+ return cachedPlusInf;
+ else // a is a PureInteger
+ return new PureInteger(-a.Value);
+ }
+
+ // Multiplication
+ public static ExtendedInt! operator*(ExtendedInt! a, ExtendedInt! b)
+ {
+ if (a.IsZero) {
+ return a;
+ } else if (b.IsZero) {
+ return b;
+ } else if (a is InfinitaryInt) {
+ if (b.IsPositive) {
+ return a;
+ } else {
+ return UnaryMinus(a);
+ }
+ } else if (b is InfinitaryInt) {
+ if (a.IsPositive) {
+ return b;
+ } else {
+ return UnaryMinus(b);
+ }
+ } else {
+ return ExtendedInt.Factory(a.Value * b.Value);
+ }
+ }
+
+ // Division
+ public static ExtendedInt! operator/(ExtendedInt! a, ExtendedInt! b)
+ {
+ if(b.IsZero)
+ {
+ return a.IsPositive? (ExtendedInt) cachedPlusInf : cachedMinusInf;
+ }
+ if (a is InfinitaryInt) {
+ return a;
+ } else if (b is InfinitaryInt) {
+ return b;
+ } else {
+ return ExtendedInt.Factory(a.Value / b.Value);
+ }
+ }
+
+ // Modulo
+ public static ExtendedInt! operator%(ExtendedInt! a, ExtendedInt! b)
+ {
+ if(b.IsZero)
+ {
+ return a.IsPositive? (ExtendedInt) cachedPlusInf : cachedMinusInf;
+ }
+ if (a is InfinitaryInt) {
+ return a;
+ } else if (b is InfinitaryInt) {
+ return b;
+ } else {
+ return ExtendedInt.Factory(a.Value % b.Value);
+ }
+ }
+
+ #endregion
+
+ #region Inf and Sup operations
+
+ public abstract int CompareTo(ExtendedInt! that);
+
+ public static bool operator<(ExtendedInt! inf, ExtendedInt! sup)
+ {
+ return inf.CompareTo(sup) < 0;
+ }
+
+ public static bool operator>(ExtendedInt! inf, ExtendedInt! sup)
+ {
+ return inf.CompareTo(sup) > 0;
+ }
+
+ public static bool operator<=(ExtendedInt! inf, ExtendedInt! sup)
+ {
+ return inf.CompareTo(sup) <= 0;
+ }
+
+ public static bool operator>=(ExtendedInt! inf, ExtendedInt! sup)
+ requires inf != null && sup != null;
+ {
+ return inf.CompareTo(sup) >= 0;
+ }
+
+ public static ExtendedInt! Inf(ExtendedInt! inf, ExtendedInt! sup)
+ {
+ if(inf < sup)
+ return inf;
+ else
+ return sup;
+ }
+
+ public static ExtendedInt! Inf(ExtendedInt! a, ExtendedInt! b, ExtendedInt! c, ExtendedInt! d)
+ {
+ ExtendedInt! infab = Inf(a,b);
+ ExtendedInt! infcd = Inf(c,d);
+
+ return Inf(infab, infcd);
+ }
+
+ public static ExtendedInt! Sup(ExtendedInt! inf, ExtendedInt! sup)
+ {
+ if(inf > sup)
+ return inf;
+ else
+ return sup;
+ }
+
+ public static ExtendedInt! Sup(ExtendedInt! a, ExtendedInt! b, ExtendedInt! c, ExtendedInt! d)
+ {
+ ExtendedInt! supab = Sup(a,b);
+ ExtendedInt! supcd = Sup(c,d);
+
+ return Sup(supab, supcd);
+ }
+
+ #endregion
+
+ // Return the ExtendedInt corresponding to the value
+ public static ExtendedInt! Factory(BigNum val)
+ {
+ return new PureInteger(val);
+ }
+ }
+
+ // Stands for a normal (finite) integer x
+ class PureInteger : ExtendedInt
+ {
+ public PureInteger(BigNum i)
+ {
+ this.val = i;
+ }
+
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public override string! ToString()
+ {
+ return this.Value.ToString();
+ }
+
+ private BigNum val;
+ public override BigNum Value {
+ get
+ {
+ return this.val;
+ }
+ }
+
+ public override int Signum {
+ get {
+ return val.Signum;
+ }
+ }
+
+ public override int CompareTo(ExtendedInt! that) {
+ if (that is PlusInfinity)
+ return -1;
+ else if (that is PureInteger)
+ return this.Value.CompareTo(that.Value);
+ else // then that is a MinusInfinity
+ return 1;
+ }
+ }
+
+ abstract class InfinitaryInt : ExtendedInt
+ {
+ public override BigNum Value
+ {
+ get {
+ throw new InvalidOperationException();
+ }
+ }
+ }
+
+ class PlusInfinity : InfinitaryInt
+ {
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public override string! ToString()
+ {
+ return "+oo";
+ }
+
+ public override int Signum {
+ get {
+ return 1;
+ }
+ }
+
+ public override int CompareTo(ExtendedInt! that) {
+ if (that is PlusInfinity)
+ return 0;
+ else
+ return 1;
+ }
+ }
+
+ class MinusInfinity : InfinitaryInt
+ {
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public override string! ToString()
+ {
+ return "-oo";
+ }
+
+ public override int Signum {
+ get {
+ return -1;
+ }
+ }
+
+ public override int CompareTo(ExtendedInt! that) {
+ if (that is MinusInfinity)
+ return 0;
+ else
+ return -1;
+ }
+ }
+}
diff --git a/Source/AIFramework/VariableMap/MicroLattice.ssc b/Source/AIFramework/VariableMap/MicroLattice.ssc
new file mode 100644
index 00000000..d38a37c0
--- /dev/null
+++ b/Source/AIFramework/VariableMap/MicroLattice.ssc
@@ -0,0 +1,79 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+namespace Microsoft.AbstractInterpretationFramework
+{
+ using Microsoft.Contracts;
+ using System.Collections;
+ using System.Diagnostics;
+ using System.Compiler;
+ using Microsoft.AbstractInterpretationFramework.Collections;
+
+ /// <summary>
+ /// Interface for a lattice that works on a per-variable basis.
+ /// </summary>
+ public abstract class MicroLattice : MathematicalLattice
+ {
+ /// <summary>
+ /// Returns the predicate on the given variable for the given
+ /// lattice element.
+ /// </summary>
+ public abstract IExpr! ToPredicate(IVariable! v, Element! e);
+ /* requires !e.IsBottom && !e.IsTop; */
+
+ /// <summary>
+ /// Allows the lattice to specify whether it understands a particular function symbol.
+ ///
+ /// The lattice is always allowed to "true" even when it really can't do anything with
+ /// such functions; however, it is advantageous to say "false" when possible to avoid
+ /// being called to do certain things.
+ ///
+ /// The arguments to a function are provided for context so that the lattice can say
+ /// true or false for the same function symbol in different situations. For example,
+ /// a lattice may understand the multiplication of a variable and a constant but not
+ /// of two variables. The implementation of a lattice should not hold on to the
+ /// arguments.
+ /// </summary>
+ /// <param name="f">The function symbol.</param>
+ /// <param name="args">The argument context.</param>
+ /// <returns>True if it may understand f, false if it does not understand f.</returns>
+ public abstract bool Understands(IFunctionSymbol! f, IList/*<IExpr!>*/! args);
+
+ /// <summary>
+ /// Set this property to true if the implemented MicroLattice can handle basic arithmetic.
+ /// Stated otherwise this property is set to true if the MicroLattice provides a transfer function for a predicate in a given state
+ /// </summary>
+ public virtual bool UnderstandsBasicArithmetics
+ {
+ get { return false; }
+ }
+
+ /// <summary>
+ /// Evaluate the predicate e and a yield the lattice element
+ /// that is implied by it.
+ /// </summary>
+ /// <param name="e">The predicate that is assumed to contain 1 variable.</param>
+ /// <returns>The most precise lattice element that is implied by the predicate.</returns>
+ public abstract Element! EvaluatePredicate(IExpr! e);
+
+ /// <summary>
+ /// Evaluate the predicate e and yield an overapproximation of the predicate under the state that is passed as a parameter
+ /// Note that unless the subclass implement it, the default behavior is to evaluate the predicate stateless, that implies that it
+ /// is evaluated in any possible context, i.e. it is an upper approximation
+ /// </summary>
+ public virtual Element! EvaluatePredicateWithState(IExpr! e, IFunctionalMap state)
+ {
+ return EvaluatePredicate(e);
+ }
+
+ /// <summary>
+ /// Give an expression (often a value) that can be used to substitute for
+ /// the variable.
+ /// </summary>
+ /// <param name="e">A lattice element.</param>
+ /// <returns>The null value if no such expression can be given.</returns>
+ public abstract IExpr GetFoldExpr(Element! e);
+ }
+} \ No newline at end of file
diff --git a/Source/AIFramework/VariableMap/Nullness.ssc b/Source/AIFramework/VariableMap/Nullness.ssc
new file mode 100644
index 00000000..63d637ce
--- /dev/null
+++ b/Source/AIFramework/VariableMap/Nullness.ssc
@@ -0,0 +1,227 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using Microsoft.Contracts;
+namespace Microsoft.AbstractInterpretationFramework
+{
+ using System.Collections;
+ using System.Diagnostics;
+ using System.Compiler.Analysis;
+
+ public class NullnessLattice : MicroLattice
+ {
+ readonly INullnessFactory! factory;
+
+ public NullnessLattice(INullnessFactory! factory) {
+ this.factory = factory;
+ // base();
+ }
+
+ enum Value { Bottom, NotNull, Null, MayBeNull }
+
+ private class Elt : Element
+ {
+ public Value value;
+
+ public Elt (Value v) { this.value = v; }
+
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public override System.Collections.Generic.ICollection<IVariable!>! FreeVariables()
+ {
+ return (!) (new System.Collections.Generic.List<IVariable!>()).AsReadOnly();
+ }
+
+ public override Element! Clone()
+ {
+ return new Elt(this.value);
+ }
+ }
+
+
+ public override Element! Top
+ {
+ get { return new Elt(Value.MayBeNull); }
+ }
+
+ public override Element! Bottom
+ {
+ get { return new Elt(Value.Bottom); }
+ }
+
+ public static Element! Null
+ {
+ get { return new Elt(Value.Null); }
+ }
+
+ public static Element! NotNull
+ {
+ get { return new Elt(Value.NotNull); }
+ }
+
+ public override bool IsTop (Element! element)
+ {
+ Elt e = (Elt) element;
+ return e.value == Value.MayBeNull;
+ }
+
+ public override bool IsBottom (Element! element)
+ {
+ Elt e = (Elt) element;
+ return e.value == Value.Bottom;
+ }
+
+ public override Lattice.Element! NontrivialJoin (Element! first, Element! second)
+ {
+ Elt a = (Elt) first;
+ Elt b = (Elt) second;
+ return (a.value == b.value) ? a : (Elt)Top;
+ }
+
+ public override Lattice.Element! NontrivialMeet (Element! first, Element! second)
+ {
+ Elt a = (Elt) first;
+ Elt b = (Elt) second;
+ return (a.value == b.value) ? a : (Elt)Bottom;
+ }
+
+ public override Element! Widen (Element! first, Element! second)
+ {
+ return Join(first,second);
+ }
+
+ protected override bool AtMost (Element! first, Element! second) // this <= that
+ {
+ Elt a = (Elt) first;
+ Elt b = (Elt) second;
+ return a.value == b.value;
+ }
+
+ public override IExpr! ToPredicate(IVariable! var, Element! element) {
+ Elt e = (Elt)element;
+
+ if (e.value == Value.NotNull)
+ {
+ return factory.Neq(var, factory.Null);
+ }
+ if (e.value == Value.Null)
+ {
+ return factory.Eq(var, factory.Null);
+ }
+ assert false;
+ throw new System.Exception();
+ }
+
+ public override IExpr GetFoldExpr(Element! e) {
+ Elt elt = (Elt)e;
+ if (elt.value == Value.Null) {
+ return factory.Null;
+ } else {
+ // can't fold into an expression
+ return null;
+ }
+ }
+
+ public override bool Understands(IFunctionSymbol! f, IList/*<IExpr!>*/! args) {
+ if (f.Equals(Microsoft.AbstractInterpretationFramework.Value.Eq) ||
+ f.Equals(Microsoft.AbstractInterpretationFramework.Value.Neq)) {
+
+ assert args.Count == 2;
+ IExpr! arg0 = (IExpr!)args[0];
+ IExpr! arg1 = (IExpr!)args[1];
+
+ // Look for "x OP null" or "null OP x" where OP is "==" or "!=".
+ if (arg0 is IVariable && arg1 is IFunApp && ((IFunApp)arg1).FunctionSymbol == Ref.Null) {
+ return true;
+ } else if (arg1 is IVariable && arg0 is IFunApp && ((IFunApp)arg0).FunctionSymbol == Ref.Null) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public override Element! EvaluatePredicate(IExpr! e) {
+ IFunApp nary = e as IFunApp;
+ if (nary != null) {
+ bool isEq = nary.FunctionSymbol.Equals(Microsoft.AbstractInterpretationFramework.Value.Eq);
+ if (isEq || nary.FunctionSymbol.Equals(Microsoft.AbstractInterpretationFramework.Value.Neq)) {
+ IList/*<IExpr!>*/! args = nary.Arguments;
+ assert args.Count == 2;
+ IExpr! arg0 = (IExpr!)args[0];
+ IExpr! arg1 = (IExpr!)args[1];
+
+ // Look for "x OP null" or "null OP x" where OP is "==" or "!=".
+ IVariable var = null;
+ if (arg0 is IVariable && arg1 is IFunApp && ((IFunApp)arg1).FunctionSymbol == Ref.Null)
+ {
+ var = (IVariable)arg0;
+ }
+ else if (arg1 is IVariable && arg0 is IFunApp && ((IFunApp)arg0).FunctionSymbol == Ref.Null)
+ {
+ var = (IVariable)arg1;
+ }
+
+ if (var != null) // found the pattern
+ {
+ return isEq ? Null:NotNull;
+ }
+ }
+ }
+ return Top;
+ }
+ }
+
+#if false
+
+ public class NullnessMicroLattice : MicroLattice
+ {
+ public override MicroLatticeElement Top { get { return NullnessLatticeElement.Top; } }
+ public override MicroLatticeElement Bottom { get { return NullnessLatticeElement.Bottom; } }
+
+
+ public override MicroLatticeElement EvaluateExpression (Expr e, LookupValue lookup)
+ {
+ if (e is LiteralExpr && ((LiteralExpr)e).Val == null)
+ {
+ return NullnessLatticeElement.Null;
+ }
+ return Top;
+ }
+
+
+ public override MicroLatticeElement EvaluatePredicate (Expr e, LookupValue lookup)
+ {
+ NAryExpr nary = e as NAryExpr;
+ if (nary != null &&
+ (nary.Fun.FunctionName.Equals("==") || nary.Fun.FunctionName.Equals("!=")))
+ {
+ Debug.Assert(nary.Args.Length == 2);
+
+ Expr arg0 = nary.Args[0], arg1 = nary.Args[1];
+ Variable var = null;
+
+ // Look for "x OP null" or "null OP x" where OP is "==" or "!=".
+ if (arg0 is IdentifierExpr && arg1 is LiteralExpr && ((LiteralExpr)arg1).Val == null)
+ {
+ var = ((IdentifierExpr)arg0).Decl;
+ }
+ else if (arg1 is IdentifierExpr && arg0 is LiteralExpr && ((LiteralExpr)arg0).Val == null)
+ {
+ var = ((IdentifierExpr)arg1).Decl;
+ }
+
+ if (var != null) // found the pattern
+ {
+ return nary.Fun.FunctionName.Equals("==") ?
+ NullnessLatticeElement.Null :
+ NullnessLatticeElement.NotNull;
+ }
+ }
+ return Top;
+ }
+ }
+
+#endif
+
+}
diff --git a/Source/AIFramework/VariableMap/VariableMapLattice.ssc b/Source/AIFramework/VariableMap/VariableMapLattice.ssc
new file mode 100644
index 00000000..aa15f1e6
--- /dev/null
+++ b/Source/AIFramework/VariableMap/VariableMapLattice.ssc
@@ -0,0 +1,749 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+namespace Microsoft.AbstractInterpretationFramework
+{
+ using Microsoft.Contracts;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+
+ using Microsoft.AbstractInterpretationFramework;
+ using Microsoft.AbstractInterpretationFramework.Collections;
+
+ using Microsoft.Boogie;
+ using IMutableSet = Microsoft.Boogie.Set;
+ using HashSet = Microsoft.Boogie.Set;
+ using ISet = Microsoft.Boogie.Set;
+
+ /// <summary>
+ /// Creates a lattice that works for several variables given a MicroLattice. Assumes
+ /// if one variable is bottom, then all variables are bottom.
+ /// </summary>
+ public class VariableMapLattice : Lattice
+ {
+ private class Elt : Element
+ {
+ /// <summary>
+ /// IsBottom(e) iff e.constraints == null
+ /// </summary>
+ /*MayBeNull*/
+ private IFunctionalMap constraints; // of type IVariable -> LATTICE_ELEMENT
+ public IFunctionalMap Constraints
+ {
+ get
+ {
+ return this.constraints;
+ }
+ }
+
+ private Elt(bool top) {
+ if (top) {
+ this.constraints = FunctionalHashtable.Empty;
+ } else {
+ this.constraints = null;
+ }
+ }
+
+ public override Element! Clone()
+ {
+ return new Elt(this.constraints);
+ }
+
+ public static Elt! Top = new Elt(true);
+ public static Elt! Bottom = new Elt(false);
+
+ public Elt(IFunctionalMap constraints)
+ {
+ this.constraints = constraints;
+ }
+
+ public bool IsBottom {
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ get {
+ return this.constraints == null;
+ }
+ }
+
+ public int Count { get { return this.constraints == null ? 0 : this.constraints.Count; } }
+
+ public IEnumerable/*<IVariable>*/! Variables {
+ get
+ requires !this.IsBottom;
+ {
+ assume this.constraints != null;
+ return (!) this.constraints.Keys;
+ }
+ }
+
+ public IEnumerable/*<IVariable>*/! SortedVariables(/*maybe null*/ IComparer variableComparer) {
+ if (variableComparer == null) {
+ return Variables;
+ } else {
+ ArrayList /*IVariable*/ vars = new ArrayList /*IVariable*/ (Count);
+ foreach (IVariable variable in Variables) {
+ vars.Add(variable);
+ }
+ vars.Sort(variableComparer);
+ return vars;
+ }
+ }
+
+ public Element Lookup(IVariable v)
+ {
+ if ((v == null) || (this.constraints == null)) { return null; }
+ return (Element)this.constraints[v];
+ }
+
+ public Element this [IVariable! key] {
+ get
+ requires !this.IsBottom;
+ {
+ assume this.constraints != null;
+ return (Element)constraints[key];
+ }
+ }
+
+ /// <summary>
+ /// Add a new entry in the functional map: var --> value.
+ /// If the variable is already there, throws an exception
+ /// </summary>
+ public Elt! Add(IVariable! var, Element! value, MicroLattice! microLattice)
+ requires !this.IsBottom;
+ {
+ assume this.constraints != null;
+ assert !this.constraints.Contains(var);
+
+ if (microLattice.IsBottom(value)) { return Bottom; }
+ if (microLattice.IsTop(value)) { return this.Remove(var, microLattice); }
+
+ return new Elt(this.constraints.Add(var, value));
+ }
+
+ /// <summary>
+ /// Set the value of the variable in the functional map
+ /// If the variable is not already there, throws an exception
+ /// </summary>
+ public Elt! Set(IVariable! var, Element! value, MicroLattice! microLattice)
+ {
+ if(microLattice.IsBottom(value)) { return Bottom; }
+ if(microLattice.IsTop(value)) { return this.Remove(var, microLattice); }
+
+ assume this.constraints != null;
+ assert this.constraints.Contains(var);
+
+ // this.constraints[var] = value;
+ IFunctionalMap newMap = this.constraints.Set(var, value);
+
+ return new Elt(newMap);
+ }
+
+ public Elt! Remove(IVariable! var, MicroLattice microLattice)
+ {
+ if (this.IsBottom) { return this; }
+ assume this.constraints != null;
+ return new Elt(this.constraints.Remove(var));
+ }
+
+ public Elt! Rename(IVariable! oldName, IVariable! newName, MicroLattice! microLattice)
+ requires !this.IsBottom;
+ {
+ Element value = this[oldName];
+ if (value == null) { return this; } // 'oldName' isn't in the map, so neither will be 'newName'
+ assume this.constraints != null;
+ IFunctionalMap newMap = this.constraints.Remove(oldName);
+ newMap = newMap.Add(newName, value);
+ return new Elt(newMap);
+ }
+
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public override ICollection<IVariable!>! FreeVariables()
+ {
+ throw new System.NotImplementedException();
+ }
+
+ } // class
+
+ private readonly MicroLattice! microLattice;
+
+ private readonly IPropExprFactory! propExprFactory;
+
+ private readonly /*maybe null*/IComparer variableComparer;
+
+ public VariableMapLattice(IPropExprFactory! propExprFactory, IValueExprFactory! valueExprFactory, MicroLattice! microLattice, /*maybe null*/IComparer variableComparer)
+ : base(valueExprFactory)
+ {
+ this.propExprFactory = propExprFactory;
+ this.microLattice = microLattice;
+ this.variableComparer = variableComparer;
+ // base(valueExprFactory);
+ }
+
+ protected override object! UniqueId { get { return this.microLattice.GetType(); } }
+
+ public override Element! Top { get { return Elt.Top; } }
+
+ public override Element! Bottom { get { return Elt.Bottom; } }
+
+ public override bool IsTop(Element! element)
+ {
+ Elt e = (Elt)element;
+ return !e.IsBottom && e.Count == 0;
+ }
+
+ public override bool IsBottom(Element! element)
+ {
+ return ((Elt)element).IsBottom;
+ }
+
+ protected override bool AtMost(Element! first, Element! second)
+ {
+ Elt a = (Elt)first;
+ Elt b = (Elt)second;
+
+ // return true iff every constraint in "this" is no weaker than the corresponding
+ // constraint in "that" and there are no additional constraints in "that"
+ foreach (IVariable! var in a.Variables)
+ {
+ Element thisValue = (!)a[var];
+
+ Element thatValue = b[var];
+ if (thatValue == null) { continue; } // it's okay for "a" to know something "b" doesn't
+
+ if (this.microLattice.LowerThan(thisValue, thatValue)) { continue; } // constraint for "var" satisfies AtMost relation
+
+ return false;
+ }
+ foreach (IVariable! var in b.Variables)
+ {
+ if (a.Lookup(var) != null) { continue; } // we checked this case in the loop above
+
+ Element thatValue = (!)b[var];
+ if (this.microLattice.IsTop(thatValue)) { continue; } // this is a trivial constraint
+
+ return false;
+ }
+ return true;
+ }
+
+ private Elt! AddConstraint(Element! element, IVariable! var, /*MicroLattice*/Element! newValue)
+ {
+ Elt e = (Elt)element;
+
+ if (!e.IsBottom && !this.microLattice.IsBottom(newValue)) // if we're not at bottom
+ {
+ /*MicroLattice*/Element currentValue = e[var];
+
+ if (currentValue == null)
+ {
+ // No information currently, so we just add the new info.
+ return e.Add(var, newValue, this.microLattice);
+ }
+ else
+ {
+ // Otherwise, take the meet of the new and current info.
+ //return e.Add(var, this.microLattice.Meet(currentValue, newValue), this.microLattice);
+ return e.Set(var, this.microLattice.Meet(currentValue, newValue), this.microLattice);
+ }
+ }
+ return e;
+ }
+
+ public override string! ToString(Element! element)
+ {
+ Elt e = (Elt)element;
+
+ if (IsTop(e)) { return "<top>"; }
+ if (IsBottom(e)) { return "<bottom>"; }
+
+ int k = 0;
+ System.Text.StringBuilder buffer = new System.Text.StringBuilder();
+ foreach (IVariable! key in e.SortedVariables(variableComparer))
+ {
+ if (k++ > 0) { buffer.Append("; "); }
+ buffer.AppendFormat("{0} = {1}", key, e[key]);
+ }
+ return buffer.ToString();
+ }
+
+ public override Element! NontrivialJoin(Element! first, Element! second)
+ {
+ Elt a = (Elt)first;
+ Elt b = (Elt)second;
+
+ IFunctionalMap newMap = FunctionalHashtable.Empty;
+ foreach (IVariable! key in a.Variables)
+ {
+ Element aValue = a[key];
+ Element bValue = b[key];
+
+ if (aValue != null && bValue != null)
+ {
+ // Keep only the variables known to both elements.
+ Element newValue = this.microLattice.Join(aValue, bValue);
+ newMap = newMap.Add(key, newValue);
+ }
+ }
+ Elt! join = new Elt(newMap);
+
+ // System.Console.WriteLine("{0} join {1} = {2} ", this.ToString(a), ToString(b), ToString(join));
+
+ return join;
+ }
+
+ public override Element! NontrivialMeet(Element! first, Element! second)
+ {
+ Elt a = (Elt)first;
+ Elt b = (Elt)second;
+
+ IFunctionalMap newMap = FunctionalHashtable.Empty;
+ foreach (IVariable! key in a.Variables)
+ {
+ Element! aValue = (!) a[key];
+ Element bValue = b[key];
+
+ Element newValue =
+ bValue == null ? aValue :
+ this.microLattice.Meet(aValue, bValue);
+
+ newMap = newMap.Add(key, newValue);
+ }
+ foreach (IVariable! key in b.Variables)
+ {
+ Element aValue = a[key];
+ Element bValue = b[key]; Debug.Assert(bValue != null);
+
+ if (aValue == null)
+ {
+ // It's a variable we didn't cover in the last loop.
+ newMap = newMap.Add(key, bValue);
+ }
+ }
+ return new Elt(newMap);
+ }
+
+ /// <summary>
+ /// Perform the pointwise widening of the elements in the map
+ /// </summary>
+ public override Element! Widen (Element! first, Element! second)
+ {
+ Elt a = (Elt)first;
+ Elt b = (Elt)second;
+
+ // Note we have to add those cases as we do not have a "NonTrivialWiden" method
+ if(a.IsBottom)
+ return new Elt(b.Constraints);
+ if(b.IsBottom)
+ return new Elt(a.Constraints);
+
+ IFunctionalMap newMap = FunctionalHashtable.Empty;
+ foreach (IVariable! key in a.Variables)
+ {
+ Element aValue = a[key];
+ Element bValue = b[key];
+
+ if (aValue != null && bValue != null)
+ {
+ // Keep only the variables known to both elements.
+ Element newValue = this.microLattice.Widen(aValue, bValue);
+ newMap = newMap.Add(key, newValue);
+ }
+ }
+ Element! widen= new Elt(newMap);
+
+ // System.Console.WriteLine("{0} widen {1} = {2} ", this.ToString(a), ToString(b), ToString(widen));
+
+ return widen;
+ }
+
+ internal static ISet/*<IVariable!>*/! VariablesInExpression(IExpr! e, ISet/*<IVariable!>*/! ignoreVars)
+ {
+ HashSet s = new HashSet();
+
+ IFunApp f = e as IFunApp;
+ IFunction lambda = e as IFunction;
+
+ if (e is IVariable)
+ {
+ if (!ignoreVars.Contains(e))
+ s.Add(e);
+ }
+ else if (f != null) // e is IFunApp
+ {
+ foreach (IExpr! arg in f.Arguments)
+ {
+ s.AddAll(VariablesInExpression(arg, ignoreVars));
+ }
+ }
+ else if (lambda != null)
+ {
+ IMutableSet x = new HashSet(1);
+ x.Add(lambda.Param);
+
+ // Ignore the bound variable
+ s.AddAll(VariablesInExpression(lambda.Body, (!) Set.Union(ignoreVars, x)));
+ }
+ else
+ {
+ Debug.Assert(false, "case not handled: " + e);
+ }
+ return s;
+ }
+
+
+ private static ArrayList/*<IExpr>*/! FindConjuncts(IExpr e)
+ {
+ ArrayList result = new ArrayList();
+
+ IFunApp f = e as IFunApp;
+ if (f != null)
+ {
+ if (f.FunctionSymbol.Equals(Prop.And))
+ {
+ foreach (IExpr arg in f.Arguments)
+ {
+ result.AddRange(FindConjuncts(arg));
+ }
+ }
+ else if (f.FunctionSymbol.Equals(Prop.Or)
+ || f.FunctionSymbol.Equals(Prop.Implies))
+ {
+ // Do nothing.
+ }
+ else
+ {
+ result.Add(e);
+ }
+ }
+ else
+ {
+ result.Add(e);
+ }
+
+ return result;
+ }
+
+ private static bool IsSimpleEquality(IExpr expr, out IVariable left, out IVariable right)
+ ensures result ==> left != null && right != null;
+ {
+ left = null;
+ right = null;
+
+ // See if we have an equality
+ IFunApp nary = expr as IFunApp;
+ if (nary == null || !nary.FunctionSymbol.Equals(Value.Eq)) { return false; }
+
+ // See if it is an equality of two variables
+ IVariable idLeft = nary.Arguments[0] as IVariable;
+ IVariable idRight = nary.Arguments[1] as IVariable;
+ if (idLeft == null || idRight == null) { return false; }
+
+ left = idLeft;
+ right = idRight;
+ return true;
+ }
+
+ /// <summary>
+ /// Returns true iff the expression is in the form var == arithmeticExpr
+ /// </summary>
+ private static bool IsArithmeticExpr(IExpr! expr)
+ {
+ // System.Console.WriteLine("\t\tIsArithmetic called with {0} of type {1}", expr, expr.GetType().ToString());
+
+ if(expr is IVariable) // expr is a variable
+ return true;
+ else if(expr is IFunApp) // may be ==, +, -, /, % or an integer
+ {
+ IFunApp fun = (IFunApp) expr;
+
+ if(fun.FunctionSymbol is IntSymbol) // it is an integer
+ return true;
+ else if(fun.FunctionSymbol.Equals(Int.Negate)) // it is an unary minus
+ return IsArithmeticExpr((IExpr!) fun.Arguments[0]);
+ else if(fun.Arguments.Count != 2) // A function of two or more operands is not arithmetic
+ return false;
+ else
+ {
+ IExpr! left = (IExpr!) fun.Arguments[0];
+ IExpr! right = (IExpr!) fun.Arguments[1];
+
+ if(!(left is IVariable || right is IVariable)) // At least one of the two operands must be a variable
+ return false;
+
+ if(fun.FunctionSymbol.Equals(Value.Eq)
+ || fun.FunctionSymbol.Equals(Int.Add)
+ || fun.FunctionSymbol.Equals(Int.Sub)
+ || fun.FunctionSymbol.Equals(Int.Mul)
+ || fun.FunctionSymbol.Equals(Int.Div)
+ || fun.FunctionSymbol.Equals(Int.Mod))
+ return IsArithmeticExpr(left) && IsArithmeticExpr(right);
+ else
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ public override IExpr! ToPredicate(Element! element)
+ {
+ if (IsTop(element)) { return propExprFactory.True; }
+ if (IsBottom(element)) { return propExprFactory.False; }
+
+ Elt e = (Elt)element;
+ IExpr truth = propExprFactory.True;
+ IExpr result = truth;
+
+ foreach (IVariable! variable in e.SortedVariables(variableComparer))
+ {
+ Element value = (Element)e[variable];
+
+ if (value == null || this.microLattice.IsTop(value)) { continue; } // Skip variables about which we know nothing.
+ if (this.microLattice.IsBottom(value)) { return propExprFactory.False; }
+
+ IExpr conjunct = this.microLattice.ToPredicate(variable, value);
+
+ result = (result == truth) ? (IExpr)conjunct : (IExpr)propExprFactory.And(result, conjunct);
+ }
+ return result;
+ }
+
+
+ public override Element! Eliminate(Element! element, IVariable! variable)
+ {
+ return ((Elt!)element).Remove(variable, this.microLattice);
+ }
+
+ private delegate IExpr! OnUnableToInline(IVariable! var);
+ private IExpr! IdentityVarToExpr(IVariable! var)
+ {
+ return var;
+ }
+
+ /// <summary>
+ /// Return a new expression in which each variable has been
+ /// replaced by an expression representing what is known about
+ /// that variable.
+ /// </summary>
+ private IExpr! InlineVariables(Elt! element, IExpr! expr, ISet/*<IVariable!>*/! notInlineable,
+ OnUnableToInline! unableToInline)
+ {
+ IVariable var = expr as IVariable;
+ if (var != null)
+ {
+ /*MicroLattice*/Element value = element[var];
+ if (notInlineable.Contains(var) || value == null || this.microLattice.IsTop(value))
+ {
+ return unableToInline(var); // We don't know anything about this variable.
+ }
+ else
+ {
+ // GetFoldExpr returns null when it can yield an expression that
+ // can be substituted for the variable.
+ IExpr valueExpr = this.microLattice.GetFoldExpr(value);
+ return (valueExpr == null) ? var : valueExpr;
+ }
+ }
+
+ // else
+
+ IFunApp fun = expr as IFunApp;
+ if (fun != null)
+ {
+ IList newargs = new ArrayList();
+ foreach (IExpr! arg in fun.Arguments)
+ {
+ newargs.Add(InlineVariables(element, arg, notInlineable, unableToInline));
+ }
+ return fun.CloneWithArguments(newargs);
+ }
+
+ // else
+
+ IFunction lambda = expr as IFunction;
+ if (lambda != null)
+ {
+ IMutableSet x = new HashSet(1);
+ x.Add(lambda.Param);
+
+ // Don't inline the bound variable
+ return lambda.CloneWithBody(
+ InlineVariables(element, lambda.Body,
+ (!) Set.Union(notInlineable, x), unableToInline)
+ );
+ }
+
+ else
+ {
+ throw
+ new System.NotImplementedException("cannot inline identifies in expression " + expr);
+ }
+ }
+
+
+ public override Element! Constrain(Element! element, IExpr! expr)
+ {
+ Elt! result = (Elt)element;
+
+ if(IsBottom(element))
+ {
+ return result; // == element
+ }
+
+ expr = InlineVariables(result, expr, (!)Set.Empty, new OnUnableToInline(IdentityVarToExpr));
+
+ foreach (IExpr! conjunct in FindConjuncts(expr))
+ {
+ IVariable left, right;
+
+ if (IsSimpleEquality(conjunct, out left, out right))
+ {
+ #region The conjunct is a simple equality
+
+
+ assert left != null && right != null;
+
+ Element leftValue = result[left], rightValue = result[right];
+ if (leftValue == null) { leftValue = this.microLattice.Top; }
+ if (rightValue == null) { rightValue = this.microLattice.Top; }
+ Element newValue = this.microLattice.Meet(leftValue, rightValue);
+ result = AddConstraint(result, left, newValue);
+ result = AddConstraint(result, right, newValue);
+
+ #endregion
+ }
+ else
+ {
+ ISet/*<IVariable>*/ variablesInvolved = VariablesInExpression(conjunct, Set.Empty);
+
+ if (variablesInvolved.Count == 1)
+ {
+ #region We have just one variable
+
+ IVariable var = null;
+ foreach (IVariable! v in variablesInvolved) { var = v; } // why is there no better way to get the elements?
+ assert var != null;
+ Element! value = this.microLattice.EvaluatePredicate(conjunct);
+ result = AddConstraint(result, var, value);
+
+ #endregion
+ }
+ else if(IsArithmeticExpr(conjunct) && this.microLattice.UnderstandsBasicArithmetics)
+ {
+ #region We evalaute an arithmetic expression
+
+ IFunApp fun = (IFunApp) conjunct;
+ if(fun.FunctionSymbol.Equals(Microsoft.AbstractInterpretationFramework.Value.Eq)) // if it is a symbol of equality
+ {
+ // get the variable to be assigned
+ IExpr! leftArg = (IExpr!) fun.Arguments[0];
+ IExpr! rightArg = (IExpr!) fun.Arguments[1];
+ IExpr! var = (leftArg is IVariable) ? leftArg : rightArg;
+
+ Element! value = this.microLattice.EvaluatePredicateWithState(conjunct, result.Constraints);
+ result = AddConstraint(result, (IVariable!) var, value);
+ }
+ #endregion
+ }
+ }
+ }
+ return result;
+ }
+
+
+ public override Element! Rename(Element! element, IVariable! oldName, IVariable! newName)
+ {
+ if(IsBottom(element))
+ {
+ return element;
+ }
+ else
+ {
+ return ((Elt)element).Rename(oldName, newName, this.microLattice);
+ }
+ }
+
+
+ public override bool Understands(IFunctionSymbol! f, IList! args)
+ {
+ return f.Equals(Prop.And) ||
+ f.Equals(Value.Eq) ||
+ microLattice.Understands(f, args);
+ }
+
+ private sealed class EquivalentExprException : CheckedException { }
+ private sealed class EquivalentExprInlineCallback
+ {
+ private readonly IVariable! var;
+ public EquivalentExprInlineCallback(IVariable! var)
+ {
+ this.var = var;
+ // base();
+ }
+
+ public IExpr! ThrowOnUnableToInline(IVariable! othervar)
+ throws EquivalentExprException;
+ {
+ if (othervar.Equals(var))
+ throw new EquivalentExprException();
+ else
+ return othervar;
+ }
+ }
+
+ public override IExpr/*?*/ EquivalentExpr(Element! e, IQueryable! q, IExpr! expr, IVariable! var, ISet/*<IVariable!>*/! prohibitedVars)
+ {
+ try
+ {
+ EquivalentExprInlineCallback closure = new EquivalentExprInlineCallback(var);
+ return InlineVariables((Elt)e, expr, (!)Set.Empty,
+ new OnUnableToInline(closure.ThrowOnUnableToInline));
+ }
+ catch (EquivalentExprException)
+ {
+ return null;
+ }
+ }
+
+
+ /// <summary>
+ /// Check to see if the given predicate holds in the given lattice element.
+ ///
+ /// TODO: We leave this unimplemented for now and just return maybe.
+ /// </summary>
+ /// <param name="e">The lattice element.</param>
+ /// <param name="pred">The predicate.</param>
+ /// <returns>Yes, No, or Maybe</returns>
+ public override Answer CheckPredicate(Element! e, IExpr! pred)
+ {
+ return Answer.Maybe;
+ }
+
+ /// <summary>
+ /// Answers a disequality about two variables. The same information could be obtained
+ /// by asking CheckPredicate, but a different implementation may be simpler and more
+ /// efficient.
+ ///
+ /// TODO: We leave this unimplemented for now and just return maybe.
+ /// </summary>
+ /// <param name="e">The lattice element.</param>
+ /// <param name="var1">The first variable.</param>
+ /// <param name="var2">The second variable.</param>
+ /// <returns>Yes, No, or Maybe.</returns>
+ public override Answer CheckVariableDisequality(Element! e, IVariable! var1, IVariable! var2)
+ {
+ return Answer.Maybe;
+ }
+
+ public override void Validate()
+ {
+ base.Validate();
+ microLattice.Validate();
+ }
+
+ }
+}
diff --git a/Source/AbsInt/AbsInt.sscproj b/Source/AbsInt/AbsInt.sscproj
new file mode 100644
index 00000000..95d6f1d2
--- /dev/null
+++ b/Source/AbsInt/AbsInt.sscproj
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<VisualStudioProject>
+ <XEN ProjectType="Local"
+ SchemaVersion="1.0"
+ Name="AbsInt"
+ ProjectGuid="11d06232-2039-4bca-853b-c596e2a4edb0"
+ >
+ <Build>
+ <Settings ApplicationIcon=""
+ AssemblyName="AbsInt"
+ OutputType="Library"
+ RootNamespace="AbsInt"
+ StartupObject=""
+ TargetPlatform="v2"
+ TargetPlatformLocation=""
+ ShadowedAssembly=""
+ StandardLibraryLocation=""
+ >
+ <Config Name="Debug"
+ AllowUnsafeBlocks="False"
+ BaseAddress="285212672"
+ CheckForOverflowUnderflow="False"
+ ConfigurationOverrideFile=""
+ DefineConstants="DEBUG;TRACE"
+ 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"
+ />
+ <Config Name="Release"
+ AllowUnsafeBlocks="false"
+ BaseAddress="285212672"
+ CheckForOverflowUnderflow="false"
+ ConfigurationOverrideFile=""
+ DefineConstants="TRACE"
+ 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"
+ AssemblyName="System.Compiler"
+ Private="false"
+ HintPath="../../Binaries/System.Compiler.dll"
+ />
+ <Reference Name="System.Compiler.Framework"
+ AssemblyName="System.Compiler.Framework"
+ Private="false"
+ HintPath="../../Binaries/System.Compiler.Framework.dll"
+ />
+ <Reference Name="AIFramework"
+ Project="{24B55172-AD8B-47D1-8952-5A95CFDB9B31}"
+ Private="true"
+ />
+ <Reference Name="Core"
+ Project="{47BC34F1-A173-40BE-84C2-9332B4418387}"
+ Private="true"
+ />
+ <Reference Name="Basetypes"
+ Project="{0C692837-77EC-415F-BF04-395E3ED06E9A}"
+ Private="true"
+ />
+ </References>
+ </Build>
+ <Files>
+ <Include>
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="Traverse.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="AbstractInterpretation.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="ExprFactories.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="..\version.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="LoopInvariantsOnDemand.ssc"
+ />
+ </Include>
+ </Files>
+ </XEN>
+</VisualStudioProject> \ No newline at end of file
diff --git a/Source/AbsInt/AbstractInterpretation.ssc b/Source/AbsInt/AbstractInterpretation.ssc
new file mode 100644
index 00000000..eee25a09
--- /dev/null
+++ b/Source/AbsInt/AbstractInterpretation.ssc
@@ -0,0 +1,1074 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using Microsoft.Contracts;
+
+namespace Microsoft.Boogie.AbstractInterpretation
+{
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using Microsoft.Boogie;
+ using Cci = System.Compiler;
+ using AI = Microsoft.AbstractInterpretationFramework;
+ using Microsoft.Contracts;
+
+ /// <summary>
+ /// Defines invariant propagation methods over ASTs for an abstract interpretation policy.
+ /// </summary>
+ public class AbstractionEngine
+ {
+ private AI.Lattice! lattice;
+ private Queue<ProcedureWorkItem!>! procWorkItems; //PM: changed to generic queue
+ private Queue/*<CallSite>*/! callReturnWorkItems;
+ private Cci.IRelation/*<Procedure,Implementation>*/ procedureImplementations;
+
+ private class ProcedureWorkItem
+ {
+ [Rep] // KRML: this doesn't seem like the right designation to me; but I'm not sure what is
+ public Procedure! Proc;
+ public int Index; // pre state is Impl.Summary[Index]
+
+ invariant 0 <= Index && Index < Proc.Summary.Count;
+
+ public ProcedureWorkItem ([Captured] Procedure! p, AI.Lattice.Element! v, AI.Lattice! lattice)
+ ensures p == Proc;
+ {
+ this.Proc = p;
+ p.Summary.Add(new ProcedureSummaryEntry(lattice, v));
+ this.Index = p.Summary.Count - 1;
+ // KRML: axioms are now in place: assume 0 <= Index && Index < Proc.Summary.Count; //PM: Should not be necessary once axioms for pure methods are there
+ }
+ }
+
+ private readonly static AI.Logger! log = new AI.Logger("Engine");
+
+ public AbstractionEngine (AI.Lattice! lattice)
+ {
+ assume log.IsExposable; //PM: One would need static class invariants to prove this property
+ expose(log) {
+ log.Enabled = AI.Lattice.LogSwitch;
+ }
+ this.lattice = lattice;
+ this.procWorkItems = new Queue<ProcedureWorkItem!>();
+ this.callReturnWorkItems = new Queue();
+ // base();
+ }
+
+ private Cci.IGraphNavigator ComputeCallGraph (Program! program)
+ ensures this.procedureImplementations != null;
+ {
+ // Since implementations call procedures (impl. signatures)
+ // rather than directly calling other implementations, we first
+ // need to compute which implementations implement which
+ // procedures and remember which implementations call which
+ // procedures.
+
+ Cci.IMutableRelation/*<Implementation,Procedure>*/ callsProcedure = new Cci.Relation();
+ Cci.IMutableRelation/*<Procedure,Implementation>*/ implementsProcedure = new Cci.Relation();
+ this.procedureImplementations = implementsProcedure;
+
+// ArrayList publics = new ArrayList();
+// publicImpls = publics;
+
+ foreach (Declaration member in program.TopLevelDeclarations)
+ {
+ Implementation impl = member as Implementation;
+ if (impl != null)
+ {
+ implementsProcedure.Add(impl.Proc, impl);
+ // if (impl.IsPublic) { publics.Add(impl); } // PR: what does "IsPublic" stand for?
+
+ foreach (Block block in impl.Blocks)
+ {
+ foreach (Cmd cmd in block.Cmds)
+ {
+ CallCmd call = cmd as CallCmd;
+ if (call != null)
+ {
+ callsProcedure.Add(impl, call.Proc);
+ }
+ }
+ }
+ }
+ }
+
+ // Now compute a graph from implementations to implementations.
+
+ Cci.GraphBuilder callGraph = new Cci.GraphBuilder();
+
+ IEnumerable callerImpls = callsProcedure.GetKeys();
+ assume callerImpls != null; // because of non-generic collection
+ foreach (Implementation caller in callerImpls)
+ {
+ IEnumerable callerProcs = callsProcedure.GetValues(caller);
+ assume callerProcs != null; // because of non-generic collection
+ foreach (Procedure callee in callerProcs)
+ {
+ IEnumerable calleeImpls = implementsProcedure.GetValues(callee);
+ assume calleeImpls != null; // because of non-generic collection
+ foreach (Implementation calleeImpl in calleeImpls)
+ {
+ callGraph.AddEdge(caller, calleeImpl);
+ }
+ }
+ }
+
+ return callGraph;
+ }
+
+#if OLDCODE
+ public void ComputeImplementationInvariants (Implementation impl)
+ {
+ // process each procedure implementation by recursively processing the first (entry) block...
+ ComputeBlockInvariants(impl.Blocks[0], lattice.Top);
+
+ // compute the procedure invariant by joining all terminal block invariants...
+ AI.Lattice.Element post = lattice.Bottom;
+ foreach (Block block in impl.Blocks)
+ {
+ if (block.TransferCmd is ReturnCmd)
+ {
+ AI.Lattice.Element postValue = block.PostInvariantBuckets[invariantIndex];
+ Debug.Assert(postValue != null);
+ post = post.Join(postValue);
+ }
+ }
+ impl.PostInvariant = post;
+
+ // Now update the procedure to reflect the new properties
+ // of this implementation.
+
+ if (impl.Proc.PreInvariant <= impl.PreInvariant)
+ {
+ // Strengthen the precondition
+ impl.Proc.PreInvariant = impl.PreInvariant;
+ foreach (Implementation otherImpl in this.procedureImplementations.GetValues(impl.Proc))
+ {
+ if (otherImpl == impl) { continue; }
+ if (otherImpl.PreInvariant <= impl.Proc.PreInvariant)
+ {
+ // If another implementation of the same procedure has
+ // a weaker precondition, re-do it with the stronger
+ // precondition.
+ otherImpl.PreInvariant = impl.Proc.PreInvariant;
+ this.implWorkItems.Enqueue(otherImpl);
+ }
+ }
+ }
+ }
+#endif
+
+
+#if NOTYET
+ public void ComputeSccInvariants (IEnumerable/*<Implementation>*/ implementations)
+ {
+ Debug.Assert(this.implWorkItems.Count == 0); // no work left over from last SCC
+
+ foreach (Implementation impl in implementations)
+ {
+ impl.AbstractFunction = AbstractFunction.Empty.WithPair(this.lattice.Bottom, this.lattice.Bottom);
+ this.implWorkItems.Enqueue(impl);
+ }
+
+ while (this.implWorkItems.Count > 0) // until fixed point reached
+ {
+ Implementation impl = (Implementation)this.implWorkItems.Dequeue();
+ }
+ }
+#endif
+
+ public AI.Lattice.Element! ApplyProcedureSummary (CallCmd! call, Implementation! caller, AI.Lattice.Element! knownAtCallSite, CallSite! callSite)
+ requires call.Proc != null;
+ {
+ Procedure! proc = call.Proc;
+
+ // NOTE: Here, we count on the fact that an implementation's variables
+ // are distinct from an implementation's procedure's variables. So, even for
+ // a recursive implementation, we're free to use the implementation's
+ // procedure's input parameters as though they were temporary local variables.
+ //
+ // Hence, in the program
+ // procedure Foo (i:int); implementation Foo (i':int) { ...call Foo(i'+1)... }
+ // we can treat the recursive call as
+ // i:=i'+1; call Foo(i);
+ // where the notation i' means a variable with the same (string) name as i,
+ // but a different identity.
+
+ AI.Lattice.Element! releventToCall = knownAtCallSite;
+ for (int i=0; i<proc.InParams.Length; i++)
+ {
+ // "Assign" the actual expressions to the corresponding formal variables.
+ assume proc.InParams[i] != null; //PM: this can be fixed once VariableSeq is replaced by List<Variable!>;
+ assume call.Ins[i] != null; //PM: this can be fixed once VariableSeq is replaced by List<Variable!>;
+ Expr equality = Expr.Eq(Expr.Ident( (!) proc.InParams[i]), (!) call.Ins[i]);
+ releventToCall = lattice.Constrain(releventToCall, equality.IExpr);
+ }
+ foreach (Variable! var in caller.LocVars) {
+ releventToCall = this.lattice.Eliminate(releventToCall, var);
+ }
+
+ ProcedureSummary! summary = proc.Summary;
+ ProcedureSummaryEntry applicableEntry = null;
+
+ for (int i=0; i<summary.Count; i++)
+ {
+ ProcedureSummaryEntry! current = (!) summary[i];
+
+ if (lattice.Equivalent(current.OnEntry, releventToCall))
+ {
+ applicableEntry = current;
+ break;
+ }
+ }
+
+ // Not found in current map, so add new entry.
+ if (applicableEntry == null)
+ {
+ ProcedureWorkItem! newWorkItem = new ProcedureWorkItem(proc, releventToCall, lattice);
+ this.procWorkItems.Enqueue(newWorkItem);
+ applicableEntry = (!) proc.Summary[newWorkItem.Index];
+ }
+ applicableEntry.ReturnPoints.Add(callSite);
+
+
+ AI.Lattice.Element atReturn = applicableEntry.OnExit;
+
+ for (int i=0; i<call.Outs.Count; i++)
+ {
+ atReturn = this.lattice.Rename(atReturn, (!) call.Proc.OutParams[i], (!)((!) call.Outs[i]).Decl);
+ knownAtCallSite = this.lattice.Eliminate(knownAtCallSite, (!)((!) call.Outs[i]).Decl);
+ }
+
+ return this.lattice.Meet(atReturn, knownAtCallSite);
+ }
+
+ private Cci.IGraphNavigator callGraph;
+ public Cci.IGraphNavigator CallGraph {
+ get { return this.callGraph; }
+ }
+
+ /// <summary>
+ /// Compute the invariants for the program using the underlying abstract domain
+ /// </summary>
+ public void ComputeProgramInvariants (Program! program)
+ {
+
+#if NOT_YET
+ Cci.IGraphNavigator callGraph =
+#endif
+
+ callGraph = this.ComputeCallGraph(program);
+ assert this.procedureImplementations != null;
+ Cci.IRelation! procedureImplementations = this.procedureImplementations;
+
+#if NOT_YET
+ IEnumerable/*<IStronglyConnectedComponent>*/ sccs =
+ StronglyConnectedComponent.ConstructSCCs(publicImpls, callGraph);
+
+ IList/*<IStronglyConnectedComponent>*/ sortedSccs =
+ GraphUtil.TopologicallySortComponentGraph(sccs);
+
+ // Go bottom-up through the SCCs of the call graph
+ foreach (IStronglyConnectedComponent scc in sortedSccs)
+ {
+ this.ComputeSccInvariants(scc.Nodes);
+ }
+#endif
+ AI.Lattice.Element! initialElement = this.lattice.Top;
+ // Gather all the axioms to create the initial lattice element
+ // Differently stated, it is the \alpha from axioms (i.e. first order formulae) to the underlyng abstract domain
+
+ foreach (Declaration decl in program.TopLevelDeclarations)
+ {
+ Axiom ax = decl as Axiom;
+ if (ax != null)
+ {
+ initialElement = this.lattice.Constrain(initialElement, ax.Expr.IExpr);
+ }
+ }
+
+ // propagate over all procedures...
+ foreach (Declaration decl in program.TopLevelDeclarations)
+ {
+ Procedure proc = decl as Procedure;
+ if (proc != null)
+ {
+ this.procWorkItems.Enqueue(new ProcedureWorkItem(proc, initialElement, this.lattice));
+ }
+ }
+
+ // analyze all the procedures...
+ while (this.procWorkItems.Count + this.callReturnWorkItems.Count > 0)
+ {
+ while (this.procWorkItems.Count > 0)
+ {
+ ProcedureWorkItem workItem = this.procWorkItems.Dequeue();
+
+ ProcedureSummaryEntry summaryEntry = (!) workItem.Proc.Summary[workItem.Index];
+
+ if (((!) procedureImplementations.GetValues(workItem.Proc)).Count == 0)
+ {
+ // This procedure has no given implementations. We therefore treat the procedure
+ // according to its specification only.
+
+ if (!CommandLineOptions.Clo.IntraproceduralInfer)
+ {
+ AI.Lattice.Element post = summaryEntry.OnEntry;
+ // BUGBUG. Here, we should process "post" according to the requires, modifies, ensures
+ // specification of the procedure, including any OLD expressions in the postcondition.
+ AI.Lattice.Element atReturn = post;
+
+ if ( ! this.lattice.LowerThan(atReturn, summaryEntry.OnExit))
+ {
+ // If the results of this analysis are strictly worse than
+ // what we previous knew for the same input assumptions,
+ // update the summary and re-do the call sites.
+
+ summaryEntry.OnExit = this.lattice.Join(summaryEntry.OnExit, atReturn);
+
+ foreach (CallSite callSite in summaryEntry.ReturnPoints)
+ {
+ this.callReturnWorkItems.Enqueue(callSite);
+ }
+ }
+ }
+ }
+ else
+ {
+ // There are implementations, so do inference based on those implementations
+
+ if (!CommandLineOptions.Clo.IntraproceduralInfer)
+ {
+ summaryEntry.OnExit = lattice.Bottom;
+ }
+
+ // For each implementation in the procedure...
+ foreach (Implementation! impl in (!) procedureImplementations.GetValues(workItem.Proc))
+ {
+ // process each procedure implementation by recursively processing the first (entry) block...
+ ((!)impl.Blocks[0]).Lattice = lattice;
+ ComputeBlockInvariants(impl, (!) impl.Blocks[0], summaryEntry.OnEntry);
+
+ if (!CommandLineOptions.Clo.IntraproceduralInfer)
+ {
+ // compute the procedure invariant by joining all terminal block invariants...
+ AI.Lattice.Element post = lattice.Bottom;
+ foreach (Block block in impl.Blocks)
+ {
+ if (block.TransferCmd is ReturnCmd)
+ {
+ // note: if program control cannot reach this block, then postValue will be null
+ if (block.PostInvariant != null)
+ {
+ post = (AI.Lattice.Element) lattice.Join(post, block.PostInvariant);
+ }
+ }
+ }
+
+ AI.Lattice.Element atReturn = post;
+ foreach (Variable! var in impl.LocVars)
+ {
+ atReturn = this.lattice.Eliminate(atReturn, var);
+ }
+ foreach (Variable! var in impl.InParams)
+ {
+ atReturn = this.lattice.Eliminate(atReturn, var);
+ }
+
+ if ( ! this.lattice.LowerThan(atReturn, summaryEntry.OnExit))
+ {
+ // If the results of this analysis are strictly worse than
+ // what we previous knew for the same input assumptions,
+ // update the summary and re-do the call sites.
+
+ summaryEntry.OnExit = this.lattice.Join(summaryEntry.OnExit, atReturn);
+
+ foreach (CallSite callSite in summaryEntry.ReturnPoints)
+ {
+ this.callReturnWorkItems.Enqueue(callSite);
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ while (this.callReturnWorkItems.Count > 0)
+ {
+ CallSite callSite = (CallSite!) this.callReturnWorkItems.Dequeue();
+
+ PropagateStartingAtStatement( callSite.Impl, callSite.Block, callSite.Statement, callSite.KnownBeforeCall);
+ }
+
+
+ } // both queues
+
+ }
+
+ private static int freshVarId = 0;
+ private static Variable! FreshVar(Boogie.Type! ty)
+ {
+ Variable fresh = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, "fresh" + freshVarId, ty));
+ freshVarId++;
+ return fresh;
+ }
+
+ private delegate CallSite! MarkCallSite(AI.Lattice.Element! currentValue);
+
+ /// <summary>
+ /// Given a basic block, it propagates the abstract state at the entry point through the exit point of the block
+ /// <param name="impl"> The implementation that owns the block </param>
+ /// <param name="block"> The from where we propagate </param>
+ /// <param name="statementIndex"> </param>
+ /// <param name="currentValue"> The initial value </param>
+ /// </summary>
+ private void PropagateStartingAtStatement (Implementation! impl, Block! block, int statementIndex, AI.Lattice.Element! currentValue)
+ {
+ assume log.IsPeerConsistent;
+ log.DbgMsg(string.Format("{0}:", block.Label)); log.DbgMsgIndent();
+
+ #region Apply the abstract transition relation to the statements in the block
+ for (int cmdIndex = statementIndex; cmdIndex < block.Cmds.Length; cmdIndex++)
+ {
+ Cmd! cmd = (!) block.Cmds[cmdIndex]; // Fetch the command
+ currentValue = Step(cmd, currentValue, impl, // Apply the transition function
+ delegate (AI.Lattice.Element! currentValue)
+ {
+ return new CallSite(impl, block, cmdIndex, currentValue);
+ }
+ );
+ }
+
+ block.PostInvariant = currentValue; // The invariant at the exit point of the block is that of the last statement
+
+ log.DbgMsg(string.Format("pre {0}", ((!)block.PreInvariant).ToString()));
+ log.DbgMsg(string.Format("post {0}", (block.PostInvariant).ToString()));
+ log.DbgMsgUnindent();
+ #endregion
+ #region Propagate the post-condition to the successor nodes
+ GotoCmd @goto = block.TransferCmd as GotoCmd;
+ if (@goto != null)
+ {
+ // labelTargets is non-null after calling Resolve in a prior phase.
+ assume @goto.labelTargets != null;
+
+ // For all the successors of this block, propagate the abstract state
+ foreach (Block! succ in @goto.labelTargets)
+ {
+ if(impl.Blocks.Contains(succ))
+ {
+ succ.Lattice = block.Lattice; // The lattice is the same
+ // Propagate the post-abstract state of this block to the successor
+ ComputeBlockInvariants(impl, succ, block.PostInvariant);
+ }
+ }
+ }
+ #endregion
+ }
+
+ /// <summary>
+ /// The abstract transition relation.
+ /// </summary>
+ private AI.Lattice.Element! Step(Cmd! cmd, AI.Lattice.Element! pre, Implementation! impl, MarkCallSite/*?*/ callSiteMarker)
+ {
+ assume log.IsPeerConsistent;
+ log.DbgMsg(string.Format("{0}", cmd)); log.DbgMsgIndent();
+
+ AI.Lattice.Element! currentValue = pre;
+
+ // Case split...
+ #region AssignCmd
+ if (cmd is AssignCmd) { // parallel assignment
+ // we first eliminate map assignments
+ AssignCmd! assmt = ((AssignCmd)cmd).AsSimpleAssignCmd;
+ //PM: Assume variables have been resolved
+ assume forall {AssignLhs! lhs in assmt.Lhss;
+ lhs.DeepAssignedVariable != null};
+
+ List<IdentifierExpr!>! freshLhs = new List<IdentifierExpr!> ();
+ foreach (AssignLhs! lhs in assmt.Lhss)
+ freshLhs.Add(Expr.Ident(FreshVar(((!)lhs.DeepAssignedVariable)
+ .TypedIdent.Type)));
+
+ for (int i = 0; i < freshLhs.Count; ++i)
+ currentValue =
+ this.lattice.Constrain(currentValue,
+ Expr.Eq(freshLhs[i], assmt.Rhss[i]).IExpr);
+ foreach (AssignLhs! lhs in assmt.Lhss)
+ currentValue =
+ this.lattice.Eliminate(currentValue, (!)lhs.DeepAssignedVariable);
+ for (int i = 0; i < freshLhs.Count; ++i)
+ currentValue =
+ this.lattice.Rename(currentValue, (!)freshLhs[i].Decl,
+ (!)assmt.Lhss[i].DeepAssignedVariable);
+ }
+
+ /*
+ if (cmd is SimpleAssignCmd)
+ {
+ SimpleAssignCmd! assmt = (SimpleAssignCmd)cmd;
+ assume assmt.Lhs.Decl != null; //PM: Assume variables have been resolved
+ Variable! dest = assmt.Lhs.Decl;
+ Variable! fresh = FreshVar(dest.TypedIdent.Type);
+ IdentifierExpr newLhs = Expr.Ident(fresh);
+ Expr equality = Expr.Eq(newLhs, assmt.Rhs);
+
+ currentValue = this.lattice.Constrain(currentValue, equality.IExpr);
+ currentValue = this.lattice.Eliminate(currentValue, dest);
+ currentValue = this.lattice.Rename(currentValue, fresh, dest);
+ }
+ #endregion
+ #region ArrayAssignCmd
+ else if (cmd is ArrayAssignCmd)
+ {
+ ArrayAssignCmd assmt = (ArrayAssignCmd)cmd;
+
+ assume assmt.Array.Type != null; //PM: assume that type checker has run
+ ArrayType! arrayType = (ArrayType)assmt.Array.Type;
+
+ Variable newHeapVar = FreshVar(arrayType);
+ IdentifierExpr newHeap = Expr.Ident(newHeapVar);
+ IdentifierExpr oldHeap = assmt.Array;
+ assume oldHeap.Decl != null; //PM: assume that variable has been resolved
+
+ // For now, we only know how to handle heaps
+ if (arrayType.IndexType0.IsRef && arrayType.IndexType1 != null && arrayType.IndexType1.IsName)
+ {
+ //PM: The following assertion follows from a nontrivial invariant of ArrayAssignCmd,
+ //PM: which we do not have yet. Therefore, we put in an assume fo now.
+ assume assmt.Index1 != null;
+ assert assmt.Index1 != null;
+ // heap succession predicate
+ Expr heapsucc = Expr.HeapSucc(oldHeap, newHeap, assmt.Index0, assmt.Index1);
+
+ currentValue = this.lattice.Constrain(currentValue, heapsucc.IExpr);
+
+ }
+ else
+ {
+ // TODO: We can do this case as well if the heap succession array can handle non-heap arrays
+ }
+ // new select expression
+ IndexedExpr newLhs = new IndexedExpr(Token.NoToken, newHeap, assmt.Index0, assmt.Index1);
+ Expr equality = Expr.Eq(newLhs, assmt.Rhs);
+
+ currentValue = this.lattice.Constrain(currentValue, equality.IExpr);
+ currentValue = this.lattice.Eliminate(currentValue, oldHeap.Decl);
+ currentValue = this.lattice.Rename(currentValue, newHeapVar, oldHeap.Decl);
+
+
+ } */
+ #endregion
+ #region Havoc
+ else if (cmd is HavocCmd)
+ {
+ HavocCmd havoc = (HavocCmd)cmd;
+ foreach (IdentifierExpr! id in havoc.Vars)
+ {
+ currentValue = this.lattice.Eliminate(currentValue, (!)id.Decl);
+ }
+ }
+ #endregion
+ #region PredicateCmd
+ else if (cmd is PredicateCmd)
+ {
+ //System.Console.WriteLine("Abstract State BEFORE " + ((PredicateCmd) cmd).Expr + " : " +this.lattice.ToPredicate(currentValue));
+
+ Expr! embeddedExpr = (!)((PredicateCmd)cmd).Expr;
+ List<Expr!>! conjuncts = flatConjunction(embeddedExpr); // Handle "assume P && Q" as if it was "assume P; assume Q"
+ foreach(Expr! c in conjuncts) {
+ currentValue = this.lattice.Constrain(currentValue, c.IExpr);
+ }
+
+ //System.Console.WriteLine("Abstract State AFTER assert/assume "+ this.lattice.ToPredicate(currentValue));
+ }
+ #endregion
+ #region CallCmd
+ else if (cmd is CallCmd)
+ {
+ CallCmd call = (CallCmd)cmd;
+
+ if (!CommandLineOptions.Clo.IntraproceduralInfer)
+ {
+ // Interprocedural analysis
+
+ if (callSiteMarker == null)
+ {
+ throw new System.InvalidOperationException("INTERNAL ERROR: Context does not allow CallCmd.");
+ }
+
+ CallSite here = callSiteMarker(currentValue);
+ currentValue = ApplyProcedureSummary(call, impl, currentValue, here);
+ }
+ else
+ {
+ // Intraprocedural analysis
+
+ StateCmd statecmd = call.Desugaring as StateCmd;
+ if (statecmd != null)
+ {
+ // Iterate the abstract transition on all the commands in the desugaring of the call
+ foreach (Cmd! callDesug in statecmd.Cmds) { currentValue = Step(callDesug, currentValue, impl, null); }
+
+ // Now, project out the local variables
+ foreach (Variable! local in statecmd.Locals) { currentValue = this.lattice.Eliminate(currentValue, local); }
+ }
+ else throw new System.InvalidOperationException("INTERNAL ERROR: CallCmd does not desugar to StateCmd.");
+ }
+ }
+ #endregion
+ #region CommentCmd
+ else if (cmd is CommentCmd)
+ {
+ // skip
+ }
+ #endregion
+ else if (cmd is SugaredCmd)
+ {
+ // other sugared commands are treated like their desugaring
+ SugaredCmd sugar = (SugaredCmd)cmd;
+ Cmd desugaring = sugar.Desugaring;
+ if (desugaring is StateCmd) {
+ StateCmd statecmd = (StateCmd)desugaring;
+ // Iterate the abstract transition on all the commands in the desugaring of the call
+ foreach (Cmd! callDesug in statecmd.Cmds) { currentValue = Step(callDesug, currentValue, impl, null); }
+ // Now, project out the local variables
+ foreach (Variable! local in statecmd.Locals) { currentValue = this.lattice.Eliminate(currentValue, local); }
+ } else {
+ currentValue = Step(desugaring, currentValue, impl, null);
+ }
+ }
+ else
+ {
+ assert false; // unknown command
+ }
+
+ log.DbgMsgUnindent();
+
+ return currentValue;
+ }
+
+ /// <summary>
+ /// Flat an expresion in the form P AND Q ... AND R into a list [P, Q, ..., R]
+ /// </summary>
+ private List<Expr!>! flatConjunction(Expr! embeddedExpr)
+ {
+ List<Expr!>! retValue = new List<Expr!>();
+ NAryExpr e = embeddedExpr as NAryExpr;
+ if(e != null && e.Fun.FunctionName.CompareTo("&&") == 0) { // if it is a conjunction
+ foreach(Expr! arg in e.Args)
+ {
+ List<Expr!>! newConjuncts = flatConjunction(arg);
+ retValue.AddRange(newConjuncts);
+ }
+ }
+ else
+ {
+ retValue.Add(embeddedExpr);
+ }
+ return retValue;
+ }
+
+ /// <summary>
+ /// Compute the invariants for a basic block
+ /// <param name="impl"> The implementation the block belongs to </param>
+ /// <param name="block"> The block for which we compute the invariants </param>
+ /// <param name="incomingValue"> The "init" abstract state for the block </param>
+ /// </summary>
+ private void ComputeBlockInvariants (Implementation! impl, Block! block, AI.Lattice.Element! incomingValue)
+ {
+ if (block.PreInvariant == null) // block has not yet been processed
+ {
+ assert block.PostInvariant == null;
+
+ // To a first approximation the block is unreachable
+ block.PreInvariant = this.lattice.Bottom;
+ block.PostInvariant = this.lattice.Bottom;
+ }
+
+ assert block.PreInvariant != null;
+ assert block.PostInvariant != null;
+
+ #region Check if we have reached a postfixpoint
+
+ if (lattice.LowerThan(incomingValue, block.PreInvariant))
+ {
+ // We have reached a post-fixpoint, so we are done...
+#if DEBUG_PRINT
+ System.Console.WriteLine("@@ Compared for block {0}:", block.Label);
+ System.Console.WriteLine("@@ {0}", lattice.ToPredicate(incomingValue));
+ System.Console.WriteLine("@@ {0}", lattice.ToPredicate(block.PreInvariant));
+ System.Console.WriteLine("@@ result = True");
+ System.Console.WriteLine("@@ end Compare");
+#endif
+ return;
+ }
+#if DEBUG_PRINT
+ // Compute the free variables in incoming and block.PreInvariant
+ FreeVariablesVisitor freeVarsVisitorForA = new FreeVariablesVisitor();
+ FreeVariablesVisitor freeVarsVisitorForB = new FreeVariablesVisitor();
+
+ lattice.ToPredicate(incomingValue).DoVisit(freeVarsVisitorForA);
+ lattice.ToPredicate(block.PreInvariant).DoVisit(freeVarsVisitorForB);
+
+ List<AI.IVariable!>! freeVarsOfA = freeVarsVisitorForA.FreeVariables;
+ List<AI.IVariable!>! freeVarsOfB = freeVarsVisitorForB.FreeVariables;
+
+ System.Console.WriteLine("@@ Compared for block {0}:", block.Label);
+ System.Console.WriteLine("@@ Incoming: {0}", lattice.ToPredicate((!) incomingValue));
+ System.Console.WriteLine("@@ Free Variables : {0}", ToString(freeVarsOfA));
+ System.Console.WriteLine("@@ Previous: {0}", lattice.ToPredicate(block.PreInvariant));
+ System.Console.WriteLine("@@ Free Variables : {0}", ToString(freeVarsOfB));
+ System.Console.WriteLine("@@ result = False");
+ System.Console.WriteLine("@@ end Compare");
+ string operation = "";
+#endif
+ #endregion
+ #region If it is not the case, then join or widen the incoming abstract state with the previous one
+ if (block.widenBlock) // If the considered block is the entry point of a loop
+ {
+ if( block.iterations <= CommandLineOptions.Clo.StepsBeforeWidening+1)
+ {
+#if DEBUG_PRINT
+ operation = "join";
+#endif
+ block.PreInvariant = (AI.Lattice.Element) lattice.Join( block.PreInvariant, incomingValue);
+ }
+ else
+ {
+#if DEBUG_PRINT
+ operation = "widening";
+#endif
+
+ // The default is to have have a widening that perform a (approximation of) the closure of the operands, so to improve the precision
+ // block.PreInvariant = WideningWithClosure.MorePreciseWiden(lattice, (!) block.PreInvariant, incomingValue);
+ block.PreInvariant = (AI.Lattice.Element) lattice.Widen( block.PreInvariant, incomingValue);
+ }
+ block.iterations++;
+ }
+ else
+ {
+#if DEBUG_PRINT
+ operation = "join";
+#endif
+ block.PreInvariant = (AI.Lattice.Element) lattice.Join( block.PreInvariant, incomingValue);
+ }
+
+#if DEBUG_PRINT
+ System.Console.WriteLine("@@ {0} for block {1}:", operation, block.Label);
+ System.Console.WriteLine("@@ {0}", lattice.ToPredicate(block.PreInvariant));
+ System.Console.WriteLine("@@ end");
+#endif
+ #endregion
+ #region Propagate the entry abstract state through the method
+ PropagateStartingAtStatement(impl, block, 0, (!) block.PreInvariant.Clone());
+ #endregion
+ }
+
+#if DEBUG_PRINT
+private string! ToString(List<AI.IVariable!>! vars)
+{
+ string s = "";
+
+ foreach(AI.IVariable! v in vars)
+ {
+ s += v.Name +" ";
+ }
+ return s;
+}
+
+#endif
+
+
+ /// <summary>
+ /// Analyze the given set of blocks.
+ /// <param name="blocks"> The set of blocks to analyze </param>
+ /// <param name="entryPoint"> The initial block (from where we start the analysis </param>
+ /// <param name="initialState"> The initial abstract state </param>
+ /// </summary>
+ public void AnalyzeBlocks(ICollection<Block!>! blocks, Block! entryPoint, AI.MathematicalLattice.Element! initialState)
+ requires blocks.Contains(entryPoint);
+ {
+ TypeVariableSeq! dummyVarSeq = new TypeVariableSeq();
+ VariableSeq! dummySeq = new VariableSeq();
+ List<Block!> blockSequence = new List<Block!>();
+
+ foreach(Block! block in blocks) {
+ blockSequence.Add(block);
+ }
+
+ Implementation dummyImplementation =
+ new Implementation(Token.NoToken, "dummyImplementation", dummyVarSeq, dummySeq, dummySeq, dummySeq, blockSequence);
+
+ ComputeBlockInvariants(dummyImplementation, entryPoint, initialState);
+ }
+
+ } // class
+
+
+ /// <summary>
+ /// Defines a class for building the abstract domain according to the parameters switch
+ /// </summary>
+ public class AbstractDomainBuilder
+ {
+
+ private AbstractDomainBuilder()
+ { /* do nothing */ }
+
+ /// <summary>
+ /// Return a fresh instance of the abstract domain of intervals
+ /// </summary>
+ static public AbstractAlgebra! BuildIntervalsAbstractDomain()
+ {
+ AI.IQuantPropExprFactory propfactory = new BoogiePropFactory();
+ AI.ILinearExprFactory linearfactory = new BoogieLinearFactory();
+ AI.IValueExprFactory valuefactory = new BoogieValueFactory();
+ IComparer variableComparer = new VariableComparer();
+
+ AbstractAlgebra! retAlgebra;
+ AI.Lattice! intervals = new AI.VariableMapLattice(propfactory, valuefactory, new AI.IntervalLattice(linearfactory), variableComparer);
+
+ retAlgebra = new AbstractAlgebra(intervals, propfactory, linearfactory, null, valuefactory, null, variableComparer);
+
+ return retAlgebra;
+ }
+
+ /// <summary>
+ /// Return a fresh abstract domain, according to the parameters specified by the command line
+ /// </summary>
+ static public AbstractAlgebra! BuildAbstractDomain()
+ {
+ AbstractAlgebra! retAlgebra;
+
+ AI.Lattice! returnLattice;
+
+ AI.IQuantPropExprFactory propfactory = new BoogiePropFactory();
+ AI.ILinearExprFactory linearfactory = new BoogieLinearFactory();
+ AI.IIntExprFactory intfactory = new BoogieIntFactory();
+ AI.IValueExprFactory valuefactory = new BoogieValueFactory();
+ AI.INullnessFactory nullfactory = new BoogieNullnessFactory();
+ IComparer variableComparer = new VariableComparer();
+
+ AI.MultiLattice multilattice = new AI.MultiLattice(propfactory, valuefactory);
+
+ if (CommandLineOptions.Clo.Ai.Intervals) // Intervals
+ {
+ multilattice.AddLattice(new AI.VariableMapLattice(propfactory, valuefactory,
+ new AI.IntervalLattice(linearfactory),
+ variableComparer));
+ }
+ if (CommandLineOptions.Clo.Ai.Constant) // Constant propagation
+
+ {
+ multilattice.AddLattice(new AI.VariableMapLattice(propfactory, valuefactory,
+ new AI.ConstantLattice(intfactory),
+ variableComparer));
+ }
+ if (CommandLineOptions.Clo.Ai.DynamicType) // Class types
+ {
+ BoogieTypeFactory typeFactory = new BoogieTypeFactory();
+ multilattice.AddLattice(new AI.VariableMapLattice(propfactory, valuefactory,
+ new AI.DynamicTypeLattice(typeFactory, propfactory),
+ variableComparer));
+ }
+ if (CommandLineOptions.Clo.Ai.Nullness) // Nullness
+ {
+ multilattice.AddLattice(new AI.VariableMapLattice(propfactory, valuefactory,
+ new AI.NullnessLattice(nullfactory),
+ variableComparer));
+ }
+ if (CommandLineOptions.Clo.Ai.Polyhedra) // Polyhedra
+ {
+ multilattice.AddLattice(new AI.PolyhedraLattice(linearfactory, propfactory));
+ }
+
+
+ returnLattice = multilattice;
+ if (CommandLineOptions.Clo.Ai.DebugStatistics)
+ {
+ returnLattice = new AI.StatisticsLattice(returnLattice);
+ }
+
+ returnLattice.Validate();
+
+ retAlgebra = new AbstractAlgebra(returnLattice, propfactory, linearfactory, intfactory, valuefactory, nullfactory,
+ variableComparer);
+
+ return retAlgebra;
+
+ }
+ }
+
+ /// <summary>
+ /// An Abstract Algebra is a tuple made of a Lattice and several factories
+ /// </summary>
+ public class AbstractAlgebra
+ {
+ [Peer] private AI.Lattice! lattice;
+ [Peer] private AI.IQuantPropExprFactory propFactory;
+ [Peer] private AI.ILinearExprFactory linearFactory;
+ [Peer] private AI.IIntExprFactory intFactory;
+ [Peer] private AI.IValueExprFactory valueFactory;
+ [Peer] private AI.INullnessFactory nullFactory;
+ [Peer] private IComparer variableComparer;
+
+ public AI.Lattice! Lattice
+ {
+ get
+ {
+ return lattice;
+ }
+ }
+
+ public AI.IQuantPropExprFactory PropositionFactory
+ {
+ get
+ {
+ return this.propFactory;
+ }
+ }
+
+ public AI.ILinearExprFactory LinearExprFactory
+ {
+ get
+ {
+ return this.linearFactory;
+ }
+ }
+
+ public AI.IIntExprFactory IntExprFactory
+ {
+ get
+ {
+ return this.intFactory;
+ }
+ }
+
+ public AI.IValueExprFactory ValueFactory
+ {
+ get
+ {
+ return this.valueFactory;
+ }
+ }
+
+ public AI.INullnessFactory NullFactory
+ {
+ get
+ {
+ return this.nullFactory;
+ }
+ }
+
+ public IComparer VariableComparer
+ {
+ get
+ {
+ return this.variableComparer;
+ }
+ }
+
+ [Captured]
+ public AbstractAlgebra(AI.Lattice! lattice,
+ AI.IQuantPropExprFactory propFactory,
+ AI.ILinearExprFactory linearFactory,
+ AI.IIntExprFactory intFactory,
+ AI.IValueExprFactory valueFactory,
+ AI.INullnessFactory nullFactory,
+ IComparer variableComparer)
+ requires propFactory != null ==> Owner.Same(lattice, propFactory);
+ requires linearFactory != null ==> Owner.Same(lattice, linearFactory);
+ requires intFactory != null ==> Owner.Same(lattice, intFactory);
+ requires valueFactory != null ==> Owner.Same(lattice, valueFactory);
+ requires nullFactory != null ==> Owner.Same(lattice, nullFactory);
+ requires variableComparer != null ==> Owner.Same(lattice, variableComparer);
+ // ensures Owner.Same(this, lattice); // KRML:
+ {
+ this.lattice = lattice;
+
+ this.propFactory = propFactory;
+ this.linearFactory = linearFactory;
+ this.intFactory = intFactory;
+ this.valueFactory = valueFactory;
+ this.nullFactory = nullFactory;
+ this.variableComparer = variableComparer;
+ }
+
+ }
+
+public class AbstractInterpretation
+{
+ /// <summary>
+ /// Run the abstract interpretation.
+ /// It has two entry points. One is the RunBoogie method. The other is the CCI PlugIn
+ /// </summary>
+ public static void RunAbstractInterpretation(Program! program)
+ {
+ Helpers.ExtraTraceInformation("Starting abstract interpretation");
+
+ if(CommandLineOptions.Clo.UseAbstractInterpretation)
+ {
+ DateTime start = new DateTime(); // to please compiler's definite assignment rules
+ if (CommandLineOptions.Clo.Trace) {
+ Console.WriteLine();
+ Console.WriteLine("Running abstract interpretation...");
+ start = DateTime.Now;
+ }
+
+ WidenPoints.Compute(program);
+
+ if (CommandLineOptions.Clo.Ai.AnySet) // if there is some user defined domain we override the default (= intervals)
+ {
+ AI.Lattice! lattice;
+
+ lattice = AbstractDomainBuilder.BuildAbstractDomain().Lattice;
+
+ ApplyAbstractInterpretation(program, lattice);
+
+ if (CommandLineOptions.Clo.Ai.DebugStatistics)
+ {
+ Console.Error.WriteLine(lattice);
+ }
+
+ if (!CommandLineOptions.Clo.IntraproceduralInfer)
+ {
+ Procedure.ShowSummaries = true;
+ }
+ }
+ else // Otherwise the default is the use of the abstract domain of intervals (useful for simple for loops)
+ {
+ AI.Lattice! lattice = AbstractDomainBuilder.BuildIntervalsAbstractDomain().Lattice;
+ ApplyAbstractInterpretation(program, lattice);
+ }
+
+ program.InstrumentWithInvariants();
+
+ if (CommandLineOptions.Clo.Trace) {
+ DateTime end = DateTime.Now;
+ TimeSpan elapsed = end - start;
+ Console.WriteLine(" [{0} s]", elapsed.TotalSeconds);
+ Console.Out.Flush();
+ }
+ }
+ }
+
+ static void ApplyAbstractInterpretation (Program! program, AI.Lattice! lattice)
+ {
+ AbstractionEngine engine = new AbstractionEngine(lattice);
+ engine.ComputeProgramInvariants(program);
+ callGraph = engine.CallGraph;
+ }
+ private static Cci.IGraphNavigator callGraph;
+ public static Cci.IGraphNavigator CallGraph {
+ get { return callGraph; }
+ }
+
+
+}
+
+} // namespace
diff --git a/Source/AbsInt/ExprFactories.ssc b/Source/AbsInt/ExprFactories.ssc
new file mode 100644
index 00000000..1569c36c
--- /dev/null
+++ b/Source/AbsInt/ExprFactories.ssc
@@ -0,0 +1,233 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using Microsoft.Boogie;
+using AI = Microsoft.AbstractInterpretationFramework;
+using Microsoft.Contracts;
+using Microsoft.Basetypes;
+
+namespace Microsoft.Boogie.AbstractInterpretation {
+
+ public class BoogiePropFactory : BoogieFactory, AI.IQuantPropExprFactory
+ {
+ public AI.IFunApp! False {
+ get {
+
+ return Expr.False;
+ }
+ }
+ public AI.IFunApp! True {
+ get {
+ return Expr.True;
+ }
+ }
+
+ public AI.IFunApp! Not(AI.IExpr! p) {
+ return Expr.Unary(Token.NoToken, UnaryOperator.Opcode.Not, IExpr2Expr(p));
+ }
+
+ public AI.IFunApp! And(AI.IExpr! p, AI.IExpr! q) {
+ return Expr.Binary(BinaryOperator.Opcode.And, IExpr2Expr(p), IExpr2Expr(q));
+ }
+
+ public AI.IFunApp! Or(AI.IExpr! p, AI.IExpr! q) {
+ return Expr.Binary(BinaryOperator.Opcode.Or, IExpr2Expr(p), IExpr2Expr(q));
+ }
+
+ public AI.IFunApp! Implies(AI.IExpr! p, AI.IExpr! q) {
+ return Expr.Binary(BinaryOperator.Opcode.Imp, IExpr2Expr(p), IExpr2Expr(q));
+ }
+
+ public AI.IFunApp! Exists(AI.IFunction! p) {
+ return (AI.IFunApp)(new ExistsExpr(Token.NoToken, new VariableSeq((Variable)p.Param), IExpr2Expr(p.Body))).IExpr;
+ }
+ public AI.IFunApp! Exists(AI.AIType paramType, AI.FunctionBody! bodygen) {
+ // generate a fresh new variable
+ Variable! var = FreshBoundVariable(paramType);
+ Expr! expr = IExpr2Expr(bodygen(var));
+ return (AI.IFunApp)(new ExistsExpr(Token.NoToken, new VariableSeq(var), expr)).IExpr;
+ }
+
+ public AI.IFunApp! Forall(AI.IFunction! p) {
+ return (AI.IFunApp)(new ForallExpr(Token.NoToken, new VariableSeq((Variable)p.Param), IExpr2Expr(p.Body))).IExpr;
+ }
+ public AI.IFunApp! Forall(AI.AIType paramType, AI.FunctionBody! bodygen) {
+ // generate a fresh new variable
+ Variable! var = FreshBoundVariable(paramType);
+ Expr! expr = IExpr2Expr(bodygen(var));
+ return (AI.IFunApp)(new ForallExpr(Token.NoToken, new VariableSeq(var), expr)).IExpr;
+ }
+
+ private static int freshVarNum = 0;
+ private static Variable! FreshBoundVariable(AI.AIType paramType)
+ {
+ // TODO: Should use the AI.AIType given in Exists and Forall to determine what Boogie type
+ // to make this new variable? For now, we use Type.Any.
+ Type! t = Type.Int;
+ /*
+ if (paramType is AI.Ref)
+ t = Type.Ref;
+ else if (paramType is AI.FieldName)
+ t = Type.Name;
+ else
+ System.Console.WriteLine("*** unsupported type in AI {0}", paramType); */
+ assert false;
+ TypedIdent id = new TypedIdent(Token.NoToken, "propfactory_freshvar" + freshVarNum, t);
+ freshVarNum++;
+ return new BoundVariable(Token.NoToken, id);
+ }
+ }
+
+ public class BoogieValueFactory : BoogieFactory, AI.IValueExprFactory
+ {
+ public AI.IFunApp! Eq(AI.IExpr! e0, AI.IExpr! e1) {
+ return Expr.Eq(IExpr2Expr(e0), IExpr2Expr(e1));
+ }
+ public AI.IFunApp! Neq(AI.IExpr! e0, AI.IExpr! e1) {
+ return Expr.Neq(IExpr2Expr(e0), IExpr2Expr(e1));
+ }
+ }
+
+ public class BoogieNullnessFactory : BoogieFactory, AI.INullnessFactory
+ {
+ public AI.IFunApp! Eq(AI.IExpr! e0, AI.IExpr! e1) {
+ return Expr.Eq(IExpr2Expr(e0), IExpr2Expr(e1));
+ }
+
+ public AI.IFunApp! Neq(AI.IExpr! e0, AI.IExpr! e1) {
+ return Expr.Neq(IExpr2Expr(e0), IExpr2Expr(e1));
+ }
+
+ public AI.IFunApp! Null {
+ get {
+ assert false; // don't know where to get null from
+ }
+ }
+ }
+
+ public class BoogieIntFactory : BoogieValueFactory, AI.IIntExprFactory {
+ public AI.IFunApp! Const(BigNum i) {
+ return new LiteralExpr(Token.NoToken, i);
+ }
+ }
+
+ public class BoogieLinearFactory : BoogieIntFactory, AI.ILinearExprFactory {
+ public AI.IFunApp! AtMost(AI.IExpr! e0, AI.IExpr! e1) {
+ return Expr.Le(IExpr2Expr(e0), IExpr2Expr(e1));
+ }
+ public AI.IFunApp! Add(AI.IExpr! e0, AI.IExpr! e1) {
+ return Expr.Add(IExpr2Expr(e0), IExpr2Expr(e1));
+ }
+ public AI.IExpr! Term(Rational r, AI.IVariable var) {
+ if (var != null && r == Rational.ONE) {
+ return var;
+ } else {
+ Expr! product;
+ if (r.IsIntegral) {
+ product = Expr.Literal(r.AsBigNum);
+ } else {
+ product = Expr.Div(Expr.Literal(r.Numerator), Expr.Literal(r.Denominator));
+ }
+ if (var != null) {
+ product = Expr.Mul(product, IExpr2Expr(var));
+ }
+ return product.IExpr;
+ }
+ }
+
+ public AI.IFunApp! False {
+ get {
+ return Expr.False;
+ }
+ }
+ public AI.IFunApp! True {
+ get {
+ return Expr.True;
+ }
+ }
+ public AI.IFunApp! And(AI.IExpr! p, AI.IExpr! q) {
+ return Expr.Binary(BinaryOperator.Opcode.And, IExpr2Expr(p), IExpr2Expr(q));
+ }
+ }
+
+ public class BoogieTypeFactory : BoogieFactory, AI.ITypeExprFactory {
+ /// <summary>
+ /// Returns an expression denoting the top of the type hierarchy.
+ /// </summary>
+ public AI.IExpr! RootType {
+ get {
+ assert false; // BUGBUG: TODO
+ throw new System.NotImplementedException();
+ }
+ }
+
+ /// <summary>
+ /// Returns true iff "t" denotes a type constant.
+ /// </summary>
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public bool IsTypeConstant(AI.IExpr! t)
+ ensures result == (t is Constant);
+ {
+ return t is Constant;
+ }
+
+ /// <summary>
+ /// Returns true iff t0 and t1 are types such that t0 and t1 are equal.
+ /// </summary>
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public bool IsTypeEqual(AI.IExpr! t0, AI.IExpr! t1) {
+ Constant c0 = t0 as Constant;
+ Constant c1 = t1 as Constant;
+ return c0 != null && c1 != null && c0 == c1;
+ }
+
+ /// <summary>
+ /// Returns true iff t0 and t1 are types such that t0 is a subtype of t1.
+ /// </summary>
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public bool IsSubType(AI.IExpr! t0, AI.IExpr! t1) {
+ assert false; // BUGBUG: TODO
+ return false;
+ }
+
+ /// <summary>
+ /// Returns the most derived supertype of both "t0" and "t1". A precondition is
+ /// that "t0" and "t1" both represent types.
+ /// </summary>
+ public AI.IExpr! JoinTypes(AI.IExpr! t0, AI.IExpr! t1) {
+ assert false; // BUGBUG: TODO
+ throw new System.NotImplementedException();
+ }
+
+ public AI.IFunApp! IsExactlyA(AI.IExpr! e, AI.IExpr! type) {
+ //PM: We need this assume because Boogie does not yet allow us to use the
+ //PM: inherited precondition "requires IsTypeConstant(type)".
+ //PM: Note that that precondition is currently commented out.
+ assume type is Constant;
+ Constant theType = (Constant)type;
+ assert false;
+ Expr typeofExpr = new NAryExpr(Token.NoToken,
+ new FunctionCall(new IdentifierExpr(Token.NoToken, "$typeof", Type.Int // Name
+ )),
+ new ExprSeq(IExpr2Expr(e)));
+ return Expr.Eq(typeofExpr, Expr.Ident(theType));
+ }
+
+ public AI.IFunApp! IsA(AI.IExpr! e, AI.IExpr! type) {
+ //PM: We need this assume because Boogie does not yet allow us to use the
+ //PM: inherited precondition "requires IsTypeConstant(type)".
+ //PM: Note that that precondition is currently commented out.
+ assume type is Constant;
+ assert false;
+ Expr typeofExpr = new NAryExpr(Token.NoToken,
+ new FunctionCall(new IdentifierExpr(Token.NoToken, "$typeof", Type.Int // Name
+ )),
+ new ExprSeq(IExpr2Expr(e)));
+ return new NAryExpr(Token.NoToken,
+ new BinaryOperator(Token.NoToken, BinaryOperator.Opcode.Subtype),
+ new ExprSeq(typeofExpr, IExpr2Expr(e)));
+ }
+ }
+}
diff --git a/Source/AbsInt/LoopInvariantsOnDemand.ssc b/Source/AbsInt/LoopInvariantsOnDemand.ssc
new file mode 100644
index 00000000..1ebd11b2
--- /dev/null
+++ b/Source/AbsInt/LoopInvariantsOnDemand.ssc
@@ -0,0 +1,78 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+namespace Microsoft.Boogie.AbstractInterpretation
+{
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using Microsoft.Contracts;
+using AI = Microsoft.AbstractInterpretationFramework;
+using Boogie = Microsoft.Boogie;
+ /// <summary>
+ /// A visitor of an abstract interpretation expression that collects the free variables
+ /// </summary>
+ class FreeVariablesVisitor : AI.ExprVisitor
+ {
+ [Peer] List<AI.IVariable!>! variables;
+ public List<AI.IVariable!>! FreeVariables
+ {
+ get
+ {
+ return this.variables;
+ }
+ }
+
+ List<string!>! varNames; // used to check the consinstency!
+
+ public FreeVariablesVisitor()
+ {
+ this.variables = new List<AI.IVariable!>();
+ this.varNames = new List<string!>();
+ }
+
+ override public object Default(AI.IExpr! expr)
+ {
+ if (expr is AI.IVariable)
+ {
+ if(!variables.Contains((AI.IVariable) expr))
+ {
+ this.variables.Add((AI.IVariable) expr);
+
+ assert ! this.varNames.Contains(expr.ToString()); // If we get there, we have an error: two variables with the same name but different identity
+
+ this.varNames.Add(expr.ToString());
+ }
+ return null;
+ }
+ else if (expr is AI.IFunApp)
+ return VisitFunApp((AI.IFunApp) expr);
+ else if (expr is AI.IFunction)
+ return VisitFunction((AI.IFunction)expr);
+ else
+ {
+ assert false;
+ }
+ }
+
+ public override object VisitFunApp(AI.IFunApp! funapp)
+ {
+ foreach (AI.IExpr! arg in funapp.Arguments)
+ {
+ arg.DoVisit(this);
+ }
+ return true;
+ }
+
+ public override object VisitFunction(AI.IFunction! fun)
+ {
+ fun.Body.DoVisit(this);
+ this.variables.Remove(fun.Param);
+ return true;
+ }
+
+ }
+
+} \ No newline at end of file
diff --git a/Source/AbsInt/Traverse.ssc b/Source/AbsInt/Traverse.ssc
new file mode 100644
index 00000000..694811da
--- /dev/null
+++ b/Source/AbsInt/Traverse.ssc
@@ -0,0 +1,182 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+namespace Microsoft.Boogie
+{
+ using System;
+ using System.Collections.Generic;
+ using Microsoft.Contracts;
+
+ /// <summary>
+ /// This class provides the functionality of traversing a program to determine which
+ /// blocks are blocks where the widening operator may need to be applied. Assumes
+ /// all 'currentlyTraversed' bits to be initially false, and leaves them that way in
+ /// the end. Assumes the 'widenBlock' bits are initially false, and sets them
+ /// appropriately.
+ /// </summary>
+ public class WidenPoints
+ {
+ /// <summary>
+ /// Compute the widen points of a program
+ /// </summary>
+ public static void Compute(Program! program)
+ {
+ expose (program)
+ {
+ foreach (Declaration m in program.TopLevelDeclarations)
+ {
+ Implementation impl = m as Implementation;
+ if (impl != null)
+ {
+ if (impl.Blocks != null && impl.Blocks.Count > 0)
+ {
+ assume impl.IsConsistent;
+ expose (impl) {
+ Block start = impl.Blocks[0];
+ assume start != null;
+ assume start.IsConsistent;
+ Visit(start);
+
+ // We reset the state...
+ foreach(Block b in impl.Blocks) {
+ expose (b) {
+ b.TraversingStatus = Block.VisitState.ToVisit;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ static void Visit(Block! b)
+ {
+ assume b.IsExposable;
+ if (b.TraversingStatus == Block.VisitState.BeingVisited)
+ {
+ expose (b)
+ {
+ // we got here through a back-edge
+ b.widenBlock = true;
+ }
+ }
+ else if(b.TraversingStatus == Block.VisitState.AlreadyVisited)
+ {
+ // do nothing... we already saw this node
+ }
+ else if (b.TransferCmd is GotoCmd)
+ {
+ assert b.TraversingStatus == Block.VisitState.ToVisit;
+
+ GotoCmd g = (GotoCmd)b.TransferCmd;
+ expose (b)
+ {
+ expose (g) { //PM: required for the subsequent expose (g.labelTargets)
+ b.TraversingStatus = Block.VisitState.BeingVisited;
+
+ // labelTargets is made non-null by Resolve, which we assume
+ // has already called in a prior pass.
+ assume g.labelTargets != null;
+ expose (g.labelTargets) {
+ foreach (Block succ in g.labelTargets)
+// invariant b.currentlyTraversed;
+ //PM: The following loop invariant will work once properties are axiomatized
+ //&& (g.labelNames != null && g.labelTargets != null ==> g.labelNames.Length == g.labelTargets.Length);
+ {
+ assert succ != null;
+ Visit(succ);
+ }
+ }
+
+ assert b.TraversingStatus == Block.VisitState.BeingVisited;
+// System.Diagnostics.Debug.Assert(b.currentlyTraversed);
+
+ b.TraversingStatus = Block.VisitState.AlreadyVisited;
+
+ //PM: The folowing assumption is needed because we cannot prove that a simple field update
+ //PM: leaves the value of a property unchanged.
+ assume (g.labelNames != null ==> g.labelNames.Length == g.labelTargets.Length);
+ }
+ }
+ }
+ else
+ {
+ assert b.TransferCmd == null || b.TransferCmd is ReturnCmd; // It must be a returnCmd;
+ }
+ }
+
+ static private Block rootBlock = null; // The root point we have to consider
+
+ /// <summary>
+ /// Compute the blocks in the body loop.
+ /// <param name ="block"> Tt is the head of the loop. It must be a widen block </param>
+ /// <return> The blocks that are in the loop from block </return>
+ /// </summary>
+ public static List<Block!> ComputeLoopBodyFrom(Block! block)
+ requires block.widenBlock;
+ {
+ assert rootBlock == null;
+ rootBlock = block;
+
+ List<Block!> blocksInLoop = new List<Block!>(); // We use a list just because .net does not define a set
+ List<Block!> visitingPath = new List<Block!>(); // The order is important, as we want paths
+
+ blocksInLoop.Add(block);
+
+ DoDFSVisit(block, visitingPath, blocksInLoop);
+
+ visitingPath.Add(block);
+
+
+ rootBlock = null; // We reset the invariant
+
+ return blocksInLoop;
+ }
+
+ /// <summary>
+ /// Perform the Depth-first search of the so to find the loop
+ /// <param name = "block"> The block to visit </param>
+ /// <param name = "path"> The path we are visiting so far </param>
+ /// </summary>
+ private static void DoDFSVisit(Block! block, List<Block!>! path, List<Block!>! blocksInPath)
+ {
+ #region case 1. We visit the root => We are done, "path" is a path inside the loop
+ if(block == rootBlock && path.Count > 1)
+ {
+ blocksInPath.AddRange(path); // Add all the blocks in this path
+ }
+
+ #endregion
+ #region case 2. We visit a node that ends with a return => "path" is not inside the loop
+ if(block.TransferCmd is ReturnCmd)
+ {
+ return;
+ }
+ #endregion
+ #region case 3. We visit a node with successors => continue the exploration of its successors
+ {
+ assert block.TransferCmd is GotoCmd;
+ GotoCmd! successors = (GotoCmd) block.TransferCmd;
+
+ if (successors.labelTargets != null)
+ foreach (Block! nextBlock in successors.labelTargets)
+ {
+ if(path.Contains(nextBlock)) // If the current path has already seen the block, just skip it
+ continue;
+ // Otherwise we perform the DFS visit
+ path.Add(nextBlock);
+ DoDFSVisit(nextBlock, path, blocksInPath);
+
+ assert nextBlock == path[path.Count-1];
+ path.RemoveAt(path.Count-1);
+ }
+
+ }
+
+ #endregion
+ }
+ }
+}
diff --git a/Source/Basetypes/Basetypes.sscproj b/Source/Basetypes/Basetypes.sscproj
new file mode 100644
index 00000000..4c3e3cb6
--- /dev/null
+++ b/Source/Basetypes/Basetypes.sscproj
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="utf-8"?>
+<VisualStudioProject>
+ <XEN ProjectType="Local"
+ SchemaVersion="1.0"
+ Name="Basetypes"
+ ProjectGuid="0c692837-77ec-415f-bf04-395e3ed06e9a"
+ >
+ <Build>
+ <Settings ApplicationIcon=""
+ AssemblyName="Basetypes"
+ OutputType="Library"
+ RootNamespace="Basetypes"
+ StartupObject=""
+ StandardLibraryLocation=""
+ TargetPlatform="v2"
+ TargetPlatformLocation=""
+ >
+ <Config Name="Debug"
+ AllowUnsafeBlocks="False"
+ BaseAddress="285212672"
+ CheckForOverflowUnderflow="False"
+ ConfigurationOverrideFile=""
+ DefineConstants="DEBUG;TRACE"
+ DocumentationFile=""
+ DebugSymbols="True"
+ FileAlignment="4096"
+ IncrementalBuild="True"
+ Optimize="False"
+ OutputPath="bin\debug"
+ RegisterForComInterop="False"
+ RemoveIntegerChecks="false"
+ TreatWarningsAsErrors="False"
+ WarningLevel="4"
+ CheckContractAdmissibility="True"
+ CheckPurity="False"
+ />
+ <Config Name="Release"
+ AllowUnsafeBlocks="false"
+ BaseAddress="285212672"
+ CheckForOverflowUnderflow="false"
+ ConfigurationOverrideFile=""
+ DefineConstants="TRACE"
+ DocumentationFile=""
+ DebugSymbols="false"
+ FileAlignment="4096"
+ IncrementalBuild="false"
+ Optimize="true"
+ OutputPath="bin\release"
+ RegisterForComInterop="false"
+ RemoveIntegerChecks="false"
+ TreatWarningsAsErrors="false"
+ WarningLevel="4"
+ CheckContractAdmissibility="True"
+ CheckPurity="False"
+ />
+ </Settings>
+ <References>
+ <Reference Name="System"
+ AssemblyName="System"
+ Private="false"
+ />
+ <Reference Name="System.Data"
+ AssemblyName="System.Data"
+ Private="false"
+ />
+ <Reference Name="System.Xml"
+ AssemblyName="System.Xml"
+ Private="false"
+ />
+ <Reference Name="FSharp.Core"
+ AssemblyName="FSharp.Core"
+ Private="false"
+ HintPath="../../Binaries/FSharp.Core.dll"
+ />
+ <Reference Name="FSharp.PowerPack"
+ AssemblyName="FSharp.PowerPack"
+ Private="false"
+ HintPath="../../Binaries/FSharp.PowerPack.dll"
+ />
+ </References>
+ </Build>
+ <Files>
+ <Include>
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="BigNum.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="Rational.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="..\version.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="Set.ssc"
+ />
+ </Include>
+ </Files>
+ </XEN>
+</VisualStudioProject> \ No newline at end of file
diff --git a/Source/Basetypes/BigNum.ssc b/Source/Basetypes/BigNum.ssc
new file mode 100644
index 00000000..0bf52290
--- /dev/null
+++ b/Source/Basetypes/BigNum.ssc
@@ -0,0 +1,341 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Text;
+
+
+namespace Microsoft.Basetypes {
+ using Microsoft.Contracts;
+ using BIM = Microsoft.FSharp.Math.BigInt;
+
+ /// <summary>
+ /// A thin wrapper around Microsoft.FSharp.Math.BigInt
+ /// (to be able to define equality, etc. properly)
+ /// </summary>
+ public struct BigNum {
+
+ // the internal representation
+ [Rep] internal readonly Microsoft.FSharp.Math.BigInt val;
+
+ public static readonly BigNum ZERO = new BigNum (BIM.Zero);
+ public static readonly BigNum ONE = new BigNum (BIM.One);
+ public static readonly BigNum MINUS_ONE = new BigNum (-BIM.One);
+
+ [Pure]
+ public static BigNum FromInt(int v) {
+ return new BigNum(new BIM(v));
+ }
+
+ [Pure]
+ public static BigNum FromUInt(uint v) {
+ return new BigNum(new BIM((long)v));
+ }
+
+ [Pure]
+ public static BigNum FromLong(long v) {
+ return new BigNum(new BIM(v));
+ }
+
+ [Pure]
+ public static BigNum FromBigInt(Microsoft.FSharp.Math.BigInt v) {
+ return new BigNum(v);
+ }
+
+ [Pure]
+ public static BigNum FromULong(ulong v) {
+ return FromString("" + v);
+ }
+
+ [Pure]
+ public static BigNum FromString(string v) {
+ try {
+ return new BigNum(BIM.Parse(v));
+ } catch (System.ArgumentException) {
+ throw new FormatException();
+ }
+ }
+
+ public static bool TryParse(string v, out BigNum res) {
+ try {
+ res = BigNum.FromString(v);
+ return true;
+ } catch (FormatException) {
+ res = ZERO;
+ return false;
+ }
+ }
+
+ // Convert to int, without checking whether overflows occur
+ public int ToInt {
+ get {
+ return BIM.ToInt32(val);
+ }
+ }
+
+ // Convert to int; assert that no overflows occur
+ public int ToIntSafe {
+ get {
+ assert this.InInt32;
+ return this.ToInt;
+ }
+ }
+
+ public Rational ToRational {
+ get {
+ return new Rational (this, BigNum.ONE);
+ }
+ }
+
+ internal BigNum(Microsoft.FSharp.Math.BigInt val) {
+ this.val = val;
+ }
+
+ public static bool operator==(BigNum x, BigNum y) {
+ return (x.val == y.val);
+ }
+
+ public static bool operator!=(BigNum x, BigNum y) {
+ return !(x.val == y.val);
+ }
+
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)]
+ public override bool Equals(object obj) {
+ if (obj == null) return false;
+ if (!(obj is BigNum)) return false;
+
+ BigNum other = (BigNum)obj;
+ return (this.val == other.val);
+ }
+
+ [Pure]
+ public override int GetHashCode() {
+ return this.val.GetHashCode();
+ }
+
+ [Pure]
+ public override string! ToString() {
+ return (!)val.ToString();
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+ // Very limited support for format strings
+ // Note: Negative integers are linearised with a minus "-" in hexadecimal,
+ // not in 2-complement notation (in contrast to what the method
+ // int32.ToString(format) does)
+
+ [Pure]
+ public string! ToString(string! format) {
+ if (format.StartsWith("d") || format.StartsWith("D")) {
+ string! res = this.Abs.ToString();
+ return addMinus(this.Signum,
+ prefixWithZeros(extractPrecision(format), res));
+ } else if (format.StartsWith("x") || format.StartsWith("X")) {
+ string! res = this.toHex(format.Substring(0, 1));
+ return addMinus(this.Signum,
+ prefixWithZeros(extractPrecision(format), res));
+ } else {
+ throw new FormatException("Format " + format + " is not supported");
+ }
+ }
+
+ private static readonly Microsoft.FSharp.Math.BigInt BI_2_TO_24 = new BIM(0x1000000);
+
+ [Pure]
+ private string! toHex(string! format) {
+ string! res = "";
+ Microsoft.FSharp.Math.BigInt rem = this.Abs.val;
+
+ while (rem > BIM.Zero) {
+ res = BIM.ToInt32(rem %BI_2_TO_24).ToString(format) + res;
+ rem = rem / BI_2_TO_24;
+ }
+
+ return res;
+ }
+
+ [Pure]
+ private int extractPrecision(string! format) {
+ if (format.Length > 1)
+ // will throw a FormatException if the precision is invalid;
+ // that is ok
+ return Int32.Parse(format.Substring(1));
+ // always output at least one digit
+ return 1;
+ }
+
+ [Pure]
+ private string! addMinus(int signum, string! suffix) {
+ if (signum < 0)
+ return "-" + suffix;
+ return suffix;
+ }
+
+ [Pure]
+ private string! prefixWithZeros(int minLength, string! suffix) {
+ StringBuilder res = new StringBuilder();
+ while (res.Length + suffix.Length < minLength)
+ res.Append("0");
+ res.Append(suffix);
+ return res.ToString();
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Basic arithmetic operations
+
+ public BigNum Abs {
+ get {
+ return new BigNum(BIM.Abs(this.val));
+ }
+ }
+
+ public BigNum Neg {
+ get {
+ return new BigNum(-this.val);
+ }
+ }
+
+ [Pure]
+ public static BigNum operator-(BigNum x) {
+ return x.Neg;
+ }
+
+ [Pure]
+ public static BigNum operator+(BigNum x, BigNum y) {
+ return new BigNum(x.val + y.val);
+ }
+
+ [Pure]
+ public static BigNum operator-(BigNum x, BigNum y) {
+ return new BigNum(x.val - y.val);
+ }
+
+ [Pure]
+ public static BigNum operator*(BigNum x, BigNum y) {
+ return new BigNum(x.val * y.val);
+ }
+
+ // TODO: check that this has a proper semantics (which? :-))
+ [Pure]
+ public static BigNum operator/(BigNum x, BigNum y) {
+ return new BigNum(x.val / y.val);
+ }
+
+ // TODO: check that this has a proper semantics (which? :-))
+ [Pure]
+ public static BigNum operator%(BigNum x, BigNum y) {
+ return new BigNum(x.val - ((x.val / y.val) * y.val));
+ }
+
+ [Pure]
+ public BigNum Min(BigNum that) {
+ return new BigNum(this.val <= that.val ? this.val : that.val);
+ }
+
+ [Pure]
+ public BigNum Max(BigNum that) {
+ return new BigNum(this.val >= that.val ? this.val : that.val);
+ }
+
+ /// <summary>
+ /// Returns the greatest common divisor of this and _y.
+ /// </summary>
+ /// <param name="_y"></param>
+ /// <returns></returns>
+ public BigNum Gcd(BigNum _y)
+ ensures !result.IsNegative;
+ {
+ BigNum x = this.Abs;
+ BigNum y = _y.Abs;
+
+ while (true)
+ {
+ if (x < y)
+ {
+ y = y % x;
+ if (y.IsZero)
+ {
+ return x;
+ }
+ }
+ else
+ {
+ x = x % y;
+ if (x.IsZero)
+ {
+ return y;
+ }
+ }
+ }
+ return ZERO; // make compiler shut up
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Some basic comparison operations
+
+ public int Signum {
+ get {
+ return this.val.Sign;
+ }
+ }
+
+ public bool IsPositive {
+ get {
+ return (this.val > BIM.Zero);
+ }
+ }
+
+ public bool IsNegative {
+ get {
+ return (this.val < BIM.Zero);
+ }
+ }
+
+ public bool IsZero {
+ get {
+ return this.val.IsZero;
+ }
+ }
+
+ [Pure]
+ public int CompareTo(BigNum that) {
+ if (this.val == that.val) return 0;
+ if (this.val < that.val) return -1;
+ return 1;
+ }
+
+ [Pure]
+ public static bool operator<(BigNum x, BigNum y) {
+ return (x.val < y.val);
+ }
+
+ [Pure]
+ public static bool operator>(BigNum x, BigNum y) {
+ return (x.val > y.val);
+ }
+
+ [Pure]
+ public static bool operator<=(BigNum x, BigNum y) {
+ return (x.val <= y.val);
+ }
+
+ [Pure]
+ public static bool operator>=(BigNum x, BigNum y) {
+ return (x.val >= y.val);
+ }
+
+
+ private static readonly Microsoft.FSharp.Math.BigInt MaxInt32 =
+ new BIM(Int32.MaxValue);
+ private static readonly Microsoft.FSharp.Math.BigInt MinInt32 =
+ new BIM(Int32.MinValue);
+
+ public bool InInt32 {
+ get {
+ return (val >= MinInt32) && (val <= MaxInt32);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Source/Basetypes/Rational.ssc b/Source/Basetypes/Rational.ssc
new file mode 100644
index 00000000..08485cdc
--- /dev/null
+++ b/Source/Basetypes/Rational.ssc
@@ -0,0 +1,300 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using Microsoft.Contracts;
+
+namespace Microsoft.Basetypes
+{
+ using BNM = Microsoft.FSharp.Math.BigNum;
+
+ /// <summary>
+ /// The representation of a rational number.
+ /// </summary>
+ public struct Rational
+ {
+ public static readonly Rational ZERO = new Rational ((!)BNM.Zero);
+ public static readonly Rational ONE = new Rational ((!)BNM.One);
+ public static readonly Rational MINUS_ONE = new Rational ((!)(-BNM.One));
+
+ private readonly Microsoft.FSharp.Math.BigNum _val;
+
+ private Microsoft.FSharp.Math.BigNum! val {
+ get {
+ if (_val == null)
+ return (!)BNM.Zero;
+ else
+ return _val;
+ }
+ }
+
+
+ // int numerator;
+ // int denominator;
+
+
+ // invariant: 0 < denominator || (numerator == 0 && denominator == 0);
+ // invariant: numerator != 0 ==> gcd(abs(numerator),denominator) == 1;
+ // invariant: numerator == 0 ==> denominator == 1 || denominator == 0;
+
+ private Rational(int x)
+ {
+ this._val = BNM.FromInt(x);
+ }
+
+ public static Rational FromInt(int x) {
+ return new Rational(x);
+ }
+
+ private Rational(Microsoft.FSharp.Math.BigNum! val) {
+ this._val = val;
+ }
+
+ public Rational(BigNum num, BigNum den) {
+ System.Diagnostics.Debug.Assert(!den.IsZero);
+
+ this._val = BNM.FromBigInt(num.val) / BNM.FromBigInt(den.val);
+ }
+
+ public static Rational FromInts(int num, int den) {
+ return new Rational(BigNum.FromInt(num), BigNum.FromInt(den));
+ }
+
+ /// <summary>
+ /// Returns the absolute value of the rational.
+ /// </summary>
+ public Rational Abs()
+ ensures result.IsNonNegative;
+ {
+ if (IsNonNegative) {
+ return this;
+ } else {
+ return -this;
+ }
+ }
+
+ /// <summary>
+ /// Returns a rational whose numerator and denominator, resepctively, are the Gcd
+ /// of the numerators and denominators of r and s. If one of r and s is 0, the absolute
+ /// value of the other is returned. If both are 0, 1 is returned.
+ /// </summary>
+ public static Rational Gcd(Rational r, Rational s)
+ ensures result.IsPositive;
+ {
+ if (r.IsZero) {
+ if (s.IsZero) {
+ return ONE;
+ } else {
+ return s.Abs();
+ }
+ } else if (s.IsZero) {
+ return r.Abs();
+ } else {
+ return new Rational (r.Numerator.Gcd(s.Numerator),
+ r.Denominator.Gcd(s.Denominator));
+ }
+ }
+
+ public BigNum Numerator
+ {
+ get
+ {
+ // Better way to obtain the numerator?
+ // The FSharp library does not seem to offer any methods for this
+ string! lin = (!)val.ToString();
+ int i = lin.IndexOf('/');
+ if (i == -1)
+ return new BigNum(BNM.ToBigInt(this.val));
+ else
+ return BigNum.FromString(lin.Substring(0, i));
+ }
+ }
+
+ public BigNum Denominator
+ {
+ get
+ {
+ // Better way to obtain the numerator?
+ // The FSharp library does not seem to offer any methods for this
+ string! lin = (!)val.ToString();
+ int i = lin.IndexOf('/');
+ if (i == -1)
+ return BigNum.ONE;
+ else
+ return BigNum.FromString(lin.Substring(i+1));
+ }
+ }
+
+ public override string! ToString()
+ {
+ return String.Format("{0}/{1}", Numerator, Denominator);
+ }
+
+
+ public static bool operator==(Rational r, Rational s)
+ {
+ return (r.val == s.val);
+ }
+
+ public static bool operator!=(Rational r, Rational s)
+ {
+ return !(r.val == s.val);
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (obj == null) return false;
+ return obj is Rational && (Rational)obj == this;
+ }
+
+ public override int GetHashCode()
+ {
+ return this.val.GetHashCode();
+ }
+
+ public int Signum {
+ get { return this.val.Sign; }
+ }
+
+ public bool IsZero
+ {
+ get
+ {
+ return Signum == 0;
+ }
+ }
+
+ public bool IsNonZero
+ {
+ get
+ {
+ return Signum != 0;
+ }
+ }
+
+ public bool IsIntegral
+ {
+ get
+ {
+ // better way?
+ return !((!)this.val.ToString()).Contains("/");
+ }
+ }
+
+
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)]
+ public bool HasValue (int n) { return this == new Rational(n); }
+
+ /// <summary>
+ /// Returns the rational as an integer. Requires the rational to be integral.
+ /// </summary>
+ public int AsInteger
+ {
+ get
+ {
+ System.Diagnostics.Debug.Assert(this.IsIntegral);
+ return Numerator.ToIntSafe;
+ }
+ }
+
+ public BigNum AsBigNum {
+ get
+ {
+ System.Diagnostics.Debug.Assert(this.IsIntegral);
+ return new BigNum (BNM.ToBigInt(this.val));
+ }
+ }
+
+ public double AsDouble
+ {
+ [Pure]
+ get
+ {
+ if (this.IsZero)
+ {
+ return 0.0;
+ }
+ else
+ {
+ return (double)Numerator.ToIntSafe / (double)Denominator.ToIntSafe;
+ }
+ }
+ }
+
+ public bool IsNegative
+ {
+ [Pure]
+ get
+ {
+ return Signum < 0;
+ }
+ }
+
+ public bool IsPositive
+ {
+ [Pure]
+ get
+ {
+ return 0 < Signum;
+ }
+ }
+
+ public bool IsNonNegative
+ {
+ [Pure]
+ get
+ {
+ return 0 <= Signum;
+ }
+ }
+
+
+ public static Rational operator-(Rational r)
+ {
+ return new Rational ((!)(BNM.Zero -r.val)); // for whatever reason, the Spec# compiler refuses to accept -r.val (unary negation)
+ }
+
+
+ public static Rational operator+(Rational r, Rational s)
+ {
+ return new Rational ((!)(r.val + s.val));
+ }
+
+ public static Rational operator-(Rational r, Rational s)
+ {
+ return new Rational ((!)(r.val - s.val));
+ }
+
+ public static Rational operator*(Rational r, Rational s)
+ {
+ return new Rational ((!)(r.val * s.val));
+ }
+
+ public static Rational operator/(Rational r, Rational s)
+ {
+ return new Rational ((!)(r.val / s.val));
+ }
+
+ public static bool operator<=(Rational r, Rational s)
+ {
+ return (r.val <= s.val);
+ }
+
+ public static bool operator>=(Rational r, Rational s)
+ {
+ return (r.val >= s.val);
+ }
+
+ public static bool operator<(Rational r, Rational s)
+ {
+ return (r.val < s.val);
+ }
+
+ public static bool operator>(Rational r, Rational s)
+ {
+ return (r.val > s.val);
+ }
+ }
+}
diff --git a/Source/Basetypes/Set.ssc b/Source/Basetypes/Set.ssc
new file mode 100644
index 00000000..221a9ad4
--- /dev/null
+++ b/Source/Basetypes/Set.ssc
@@ -0,0 +1,282 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+namespace Microsoft.Boogie
+{
+ using System;
+ using System.IO;
+ using System.Collections;
+ using Microsoft.Contracts;
+
+ /// <summary>
+ /// A class representing a mathematical set.
+ /// </summary>
+ public class Set : ICloneable, IEnumerable
+ {
+ /*[Own]*/ Hashtable! ht;
+
+ public Set()
+ {
+ ht = new Hashtable();
+ base();
+ }
+
+ private Set(Hashtable! ht)
+ {
+ this.ht = ht;
+ base();
+ }
+
+ public Set(int capacity)
+ {
+ ht = new Hashtable(capacity);
+ base();
+ }
+
+
+ public readonly static Set! Empty = new Set();
+
+ public void Clear()
+ {
+ ht.Clear();
+ }
+
+ /// <summary>
+ /// This method idempotently adds "o" to the set.
+ /// In notation:
+ /// this.SetElements = this.SetElements_old \union {o};
+ /// </summary>
+ public void Add(object! o)
+ {
+ ht[o] = o;
+ }
+
+ /// <summary>
+ /// this.SetElements = this.SetElements_old \union s.SetElements;
+ /// </summary>
+ public void AddRange(Set! s)
+ {
+ foreach (object! o in s)
+ {
+ Add(o);
+ }
+ }
+
+ /// <summary>
+ /// this.SetElements = this.SetElements_old \setminus {o};
+ /// </summary>
+ public void Remove(object! o)
+ {
+ ht.Remove(o);
+ }
+
+ /// <summary>
+ /// this.SetElements = this.SetElements_old \setminus s.SetElements;
+ /// </summary>
+ public void RemoveRange(Set! s)
+ {
+ if (s == this)
+ {
+ ht.Clear();
+ }
+ else
+ {
+ foreach (object! o in s)
+ {
+ ht.Remove(o);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Returns an arbitrary element from the set.
+ /// </summary>
+ public object! Choose()
+ requires Count > 0;
+ {
+ IEnumerator iter = GetEnumerator();
+ iter.MoveNext();
+ return (!)iter.Current;
+ }
+
+ /// <summary>
+ /// Picks an arbitrary element from the set, removes it, and returns it.
+ /// </summary>
+ public object! Take()
+ requires Count > 0;
+ ensures Count == old(Count) - 1;
+ {
+ object r = Choose();
+ Remove(r);
+ return r;
+ }
+
+ public void Intersect(Set! s)
+ {
+ Hashtable h = new Hashtable(ht.Count);
+ foreach (object! key in ht.Keys)
+ {
+ if (s.ht.ContainsKey(key))
+ {
+ h.Add(key, key);
+ }
+ }
+ ht = h;
+ }
+
+ /// <summary>
+ /// The getter returns true iff "o" is in the set.
+ /// The setter adds the value "o" (for "true") or removes "o" (for "false")
+ /// </summary>
+ public bool this[object! o]
+ {
+ get
+ {
+ return ht.ContainsKey(o);
+ }
+ set
+ {
+ if (value) {
+ Add(o);
+ } else {
+ Remove(o);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Returns true iff "o" is an element of "this".
+ /// </summary>
+ /// <param name="o"></param>
+ /// <returns></returns>
+ [Pure]
+ public bool Contains(object! o)
+ {
+ if (!this.ht.ContainsKey(o))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ /// <summary>
+ /// Returns true iff every element of "s" is an element of "this", that is, if
+ /// "s" is a subset of "this".
+ /// </summary>
+ /// <param name="s"></param>
+ /// <returns></returns>
+ public bool ContainsRange(Set! s)
+ {
+ if (s != this)
+ {
+ foreach (object! key in s.ht.Keys)
+ {
+ if (!this.ht.ContainsKey(key))
+ {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ public object! Clone()
+ {
+ return new Set((Hashtable!)ht.Clone());
+ }
+
+ public virtual int Count
+ {
+ [Pure][Reads(ReadsAttribute.Reads.Owned)] get
+ {
+ return ht.Count;
+ }
+ }
+
+ [Pure]
+ public IEnumerator! GetEnumerator()
+ {
+ return ht.Keys.GetEnumerator();
+ }
+
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public override string! ToString()
+ {
+ string s = null;
+ foreach (object! key in ht.Keys)
+ {
+ if (s == null)
+ {
+ s = "{";
+ }
+ else
+ {
+ s += ", ";
+ }
+ s += key.ToString();
+ }
+ if (s == null)
+ {
+ return "{}";
+ }
+ else
+ {
+ return s + "}";
+ }
+ }
+
+ public bool AddAll(IEnumerable! objs){
+ foreach (object! o in objs){
+ this.Add(o);
+ }
+ return true;
+ }
+ //----------------------------- Static Methods ---------------------------------
+
+ // Functional Intersect
+ public static Set! Intersect(Set! a, Set! b)
+ // ensures Forall{ object x in result; a[x] && b[x] };
+ {
+ Set! res = (Set!) a.Clone();
+ res.Intersect(b);
+ return res;
+ }
+ // Functional Union
+ public static Set! Union(Set! a, Set! b)
+ // ensures Forall{ object x in result; a[x] || b[x] };
+ {
+ Set! res = (Set!) a.Clone();
+ res.AddRange(b);
+ return res;
+ }
+
+ public delegate bool SetFilter(object! obj);
+
+ public static Set! Filter(Set! a, SetFilter! filter)
+ {
+ Set inter = new Set();
+
+ foreach(object! elem in a)
+ {
+ if (filter(elem))
+ {
+ inter.Add(elem);
+ }
+ }
+ return inter;
+ }
+
+ }
+
+ public interface IWorkList: ICollection
+ {
+ bool Add(object o);
+ bool AddAll(IEnumerable objs);
+ bool IsEmpty();
+ object Pull();
+ }
+
+
+} \ No newline at end of file
diff --git a/Source/Boogie.sln b/Source/Boogie.sln
new file mode 100644
index 00000000..40c39af5
--- /dev/null
+++ b/Source/Boogie.sln
@@ -0,0 +1,99 @@
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Provers", "Provers", "{B758C1E3-824A-439F-AA2F-0BA1143E8C8D}"
+EndProject
+Project("{07C4E3D1-6B67-4060-8A92-940DB82041ED}") = "Simplify", "Provers\Simplify\Simplify.sscproj", "{F75666DE-FB56-457C-8782-09BE243450FC}"
+EndProject
+Project("{07C4E3D1-6B67-4060-8A92-940DB82041ED}") = "Z3", "Provers\Z3\Z3.sscproj", "{F75666DE-CD56-457C-8782-09BE243450FC}"
+EndProject
+Project("{07C4E3D1-6B67-4060-8A92-940DB82041ED}") = "SMTLib", "Provers\SMTLib\SMTLib.sscproj", "{13C3A68C-462A-4CDA-A480-738046E37C5A}"
+EndProject
+Project("{07C4E3D1-6B67-4060-8A92-940DB82041ED}") = "AbsInt", "AbsInt\AbsInt.sscproj", "{11D06232-2039-4BCA-853B-C596E2A4EDB0}"
+EndProject
+Project("{07C4E3D1-6B67-4060-8A92-940DB82041ED}") = "AIFramework", "AIFramework\AIFramework.sscproj", "{24B55172-AD8B-47D1-8952-5A95CFDB9B31}"
+EndProject
+Project("{07C4E3D1-6B67-4060-8A92-940DB82041ED}") = "Basetypes", "Basetypes\Basetypes.sscproj", "{0C692837-77EC-415F-BF04-395E3ED06E9A}"
+EndProject
+Project("{07C4E3D1-6B67-4060-8A92-940DB82041ED}") = "Core", "Core\Core.sscproj", "{47BC34F1-A173-40BE-84C2-9332B4418387}"
+EndProject
+Project("{07C4E3D1-6B67-4060-8A92-940DB82041ED}") = "Graph", "Graph\Graph.sscproj", "{4C28FB90-630E-4B55-A937-11A011B79765}"
+EndProject
+Project("{07C4E3D1-6B67-4060-8A92-940DB82041ED}") = "VCExpr", "VCExpr\VCExpr.sscproj", "{CF42B700-10AA-4DA9-8992-48A800251C11}"
+EndProject
+Project("{07C4E3D1-6B67-4060-8A92-940DB82041ED}") = "VCGeneration", "VCGeneration\VCGeneration.sscproj", "{F65666DE-FB56-457C-8782-09BE243450FC}"
+EndProject
+Project("{07C4E3D1-6B67-4060-8A92-940DB82041ED}") = "BoogieDriver", "BoogieDriver\BoogieDriver.sscproj", "{D2B98E4A-2EAB-4065-ABFA-709AC5CA7D4C}"
+ ProjectSection(ProjectDependencies) = postProject
+ {CF42B700-10AA-4DA9-8992-48A800251C11} = {CF42B700-10AA-4DA9-8992-48A800251C11}
+ {11D06232-2039-4BCA-853B-C596E2A4EDB0} = {11D06232-2039-4BCA-853B-C596E2A4EDB0}
+ {0C692837-77EC-415F-BF04-395E3ED06E9A} = {0C692837-77EC-415F-BF04-395E3ED06E9A}
+ {24B55172-AD8B-47D1-8952-5A95CFDB9B31} = {24B55172-AD8B-47D1-8952-5A95CFDB9B31}
+ {13C3A68C-462A-4CDA-A480-738046E37C5A} = {13C3A68C-462A-4CDA-A480-738046E37C5A}
+ {4C28FB90-630E-4B55-A937-11A011B79765} = {4C28FB90-630E-4B55-A937-11A011B79765}
+ {F65666DE-FB56-457C-8782-09BE243450FC} = {F65666DE-FB56-457C-8782-09BE243450FC}
+ {F75666DE-CD56-457C-8782-09BE243450FC} = {F75666DE-CD56-457C-8782-09BE243450FC}
+ {F75666DE-FB56-457C-8782-09BE243450FC} = {F75666DE-FB56-457C-8782-09BE243450FC}
+ {47BC34F1-A173-40BE-84C2-9332B4418387} = {47BC34F1-A173-40BE-84C2-9332B4418387}
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|.NET = Debug|.NET
+ Release|.NET = Release|.NET
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {F75666DE-FB56-457C-8782-09BE243450FC}.Debug|.NET.ActiveCfg = Debug|.NET
+ {F75666DE-FB56-457C-8782-09BE243450FC}.Debug|.NET.Build.0 = Debug|.NET
+ {F75666DE-FB56-457C-8782-09BE243450FC}.Release|.NET.ActiveCfg = Release|.NET
+ {F75666DE-FB56-457C-8782-09BE243450FC}.Release|.NET.Build.0 = Release|.NET
+ {F75666DE-CD56-457C-8782-09BE243450FC}.Debug|.NET.ActiveCfg = Debug|.NET
+ {F75666DE-CD56-457C-8782-09BE243450FC}.Debug|.NET.Build.0 = Debug|.NET
+ {F75666DE-CD56-457C-8782-09BE243450FC}.Release|.NET.ActiveCfg = Release|.NET
+ {F75666DE-CD56-457C-8782-09BE243450FC}.Release|.NET.Build.0 = Release|.NET
+ {13C3A68C-462A-4CDA-A480-738046E37C5A}.Debug|.NET.ActiveCfg = Debug|.NET
+ {13C3A68C-462A-4CDA-A480-738046E37C5A}.Debug|.NET.Build.0 = Debug|.NET
+ {13C3A68C-462A-4CDA-A480-738046E37C5A}.Release|.NET.ActiveCfg = Release|.NET
+ {13C3A68C-462A-4CDA-A480-738046E37C5A}.Release|.NET.Build.0 = Release|.NET
+ {11D06232-2039-4BCA-853B-C596E2A4EDB0}.Debug|.NET.ActiveCfg = Debug|.NET
+ {11D06232-2039-4BCA-853B-C596E2A4EDB0}.Debug|.NET.Build.0 = Debug|.NET
+ {11D06232-2039-4BCA-853B-C596E2A4EDB0}.Release|.NET.ActiveCfg = Release|.NET
+ {11D06232-2039-4BCA-853B-C596E2A4EDB0}.Release|.NET.Build.0 = Release|.NET
+ {24B55172-AD8B-47D1-8952-5A95CFDB9B31}.Debug|.NET.ActiveCfg = Debug|.NET
+ {24B55172-AD8B-47D1-8952-5A95CFDB9B31}.Debug|.NET.Build.0 = Debug|.NET
+ {24B55172-AD8B-47D1-8952-5A95CFDB9B31}.Release|.NET.ActiveCfg = Release|.NET
+ {24B55172-AD8B-47D1-8952-5A95CFDB9B31}.Release|.NET.Build.0 = Release|.NET
+ {0C692837-77EC-415F-BF04-395E3ED06E9A}.Debug|.NET.ActiveCfg = Debug|.NET
+ {0C692837-77EC-415F-BF04-395E3ED06E9A}.Debug|.NET.Build.0 = Debug|.NET
+ {0C692837-77EC-415F-BF04-395E3ED06E9A}.Release|.NET.ActiveCfg = Release|.NET
+ {0C692837-77EC-415F-BF04-395E3ED06E9A}.Release|.NET.Build.0 = Release|.NET
+ {47BC34F1-A173-40BE-84C2-9332B4418387}.Debug|.NET.ActiveCfg = Debug|.NET
+ {47BC34F1-A173-40BE-84C2-9332B4418387}.Debug|.NET.Build.0 = Debug|.NET
+ {47BC34F1-A173-40BE-84C2-9332B4418387}.Release|.NET.ActiveCfg = Release|.NET
+ {47BC34F1-A173-40BE-84C2-9332B4418387}.Release|.NET.Build.0 = Release|.NET
+ {4C28FB90-630E-4B55-A937-11A011B79765}.Debug|.NET.ActiveCfg = Debug|.NET
+ {4C28FB90-630E-4B55-A937-11A011B79765}.Debug|.NET.Build.0 = Debug|.NET
+ {4C28FB90-630E-4B55-A937-11A011B79765}.Release|.NET.ActiveCfg = Release|.NET
+ {4C28FB90-630E-4B55-A937-11A011B79765}.Release|.NET.Build.0 = Release|.NET
+ {CF42B700-10AA-4DA9-8992-48A800251C11}.Debug|.NET.ActiveCfg = Debug|.NET
+ {CF42B700-10AA-4DA9-8992-48A800251C11}.Debug|.NET.Build.0 = Debug|.NET
+ {CF42B700-10AA-4DA9-8992-48A800251C11}.Release|.NET.ActiveCfg = Release|.NET
+ {CF42B700-10AA-4DA9-8992-48A800251C11}.Release|.NET.Build.0 = Release|.NET
+ {F65666DE-FB56-457C-8782-09BE243450FC}.Debug|.NET.ActiveCfg = Debug|.NET
+ {F65666DE-FB56-457C-8782-09BE243450FC}.Debug|.NET.Build.0 = Debug|.NET
+ {F65666DE-FB56-457C-8782-09BE243450FC}.Release|.NET.ActiveCfg = Release|.NET
+ {F65666DE-FB56-457C-8782-09BE243450FC}.Release|.NET.Build.0 = Release|.NET
+ {D2B98E4A-2EAB-4065-ABFA-709AC5CA7D4C}.Debug|.NET.ActiveCfg = Debug|.NET
+ {D2B98E4A-2EAB-4065-ABFA-709AC5CA7D4C}.Debug|.NET.Build.0 = Debug|.NET
+ {D2B98E4A-2EAB-4065-ABFA-709AC5CA7D4C}.Release|.NET.ActiveCfg = Release|.NET
+ {D2B98E4A-2EAB-4065-ABFA-709AC5CA7D4C}.Release|.NET.Build.0 = Release|.NET
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {F75666DE-FB56-457C-8782-09BE243450FC} = {B758C1E3-824A-439F-AA2F-0BA1143E8C8D}
+ {F75666DE-CD56-457C-8782-09BE243450FC} = {B758C1E3-824A-439F-AA2F-0BA1143E8C8D}
+ {13C3A68C-462A-4CDA-A480-738046E37C5A} = {B758C1E3-824A-439F-AA2F-0BA1143E8C8D}
+ EndGlobalSection
+EndGlobal
diff --git a/Source/Boogie.suo b/Source/Boogie.suo
new file mode 100644
index 00000000..cf2332be
--- /dev/null
+++ b/Source/Boogie.suo
Binary files differ
diff --git a/Source/BoogieDriver/BoogieDriver.ssc b/Source/BoogieDriver/BoogieDriver.ssc
new file mode 100644
index 00000000..0254c3ef
--- /dev/null
+++ b/Source/BoogieDriver/BoogieDriver.ssc
@@ -0,0 +1,648 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+//---------------------------------------------------------------------------------------------
+// OnlyBoogie OnlyBoogie.ssc
+// - main program for taking a BPL program and verifying it
+//---------------------------------------------------------------------------------------------
+
+namespace Microsoft.Boogie
+{
+ using System;
+ using System.IO;
+ using System.Collections;
+ using System.Collections.Generic;
+ using PureCollections;
+ using Microsoft.Boogie;
+ using Microsoft.Boogie.AbstractInterpretation;
+ using System.Diagnostics;
+ using VC;
+ using Cci = System.Compiler;
+ using AI = Microsoft.AbstractInterpretationFramework;
+
+/*
+ The following assemblies are referenced because they are needed at runtime, not at compile time:
+ BaseTypes
+ Provers.Z3
+ System.Compiler.Framework
+*/
+
+ public class OnlyBoogie
+ {
+ // ------------------------------------------------------------------------
+ // Main
+
+ public static void Main (string[]! args)
+ {
+ assert forall{int i in (0:args.Length); args[i] != null};
+ CommandLineOptions.Clo.RunningBoogieFromCommandLine = true;
+ if (CommandLineOptions.Clo.Parse(args) != 1)
+ {
+ goto END;
+ }
+ if (CommandLineOptions.Clo.Files.Count == 0)
+ {
+ ErrorWriteLine("*** Error: No input files were specified.");
+ goto END;
+ }
+ if (CommandLineOptions.Clo.XmlSink != null) {
+ string errMsg = CommandLineOptions.Clo.XmlSink.Open();
+ if (errMsg != null) {
+ ErrorWriteLine("*** Error: " + errMsg);
+ goto END;
+ }
+ }
+ if (!CommandLineOptions.Clo.DontShowLogo)
+ {
+ Console.WriteLine(CommandLineOptions.Clo.Version);
+ }
+ if (CommandLineOptions.Clo.ShowEnv == CommandLineOptions.ShowEnvironment.Always)
+ {
+ Console.WriteLine("---Command arguments");
+ foreach (string! arg in args)
+ {
+ Console.WriteLine(arg);
+ }
+ Console.WriteLine("--------------------");
+ }
+
+ SelectPlatform(CommandLineOptions.Clo);
+
+ Helpers.ExtraTraceInformation("Becoming sentinent");
+
+ // Make sure the Spec# runtime is initialized.
+ // This happens when the static constructor for the Runtime type is executed.
+ // Otherwise, if a reference happens to get chased to it, it is loaded twice
+ // and then the types in it do not get unique representations.
+ if (System.Type.GetType("Mono.Runtime") == null) { // MONO
+ Cci.AssemblyNode a = Microsoft.SpecSharp.Runtime.RuntimeAssembly;
+ assert a != null;
+ }
+
+ foreach (string! file in CommandLineOptions.Clo.Files)
+ {
+ string extension = Path.GetExtension(file);
+ if (extension != null) { extension = extension.ToLower(); }
+ if (extension != ".bpl")
+ {
+ ErrorWriteLine("*** Error: '{0}': Filename extension '{1}' is not supported. Input files must be BoogiePL programs (.bpl).", file,
+ extension == null ? "" : extension);
+ goto END;
+ }
+ }
+ CommandLineOptions.Clo.RunningBoogieOnSsc = false;
+ ProcessFiles(CommandLineOptions.Clo.Files);
+
+ END:
+ if (CommandLineOptions.Clo.XmlSink != null) {
+ CommandLineOptions.Clo.XmlSink.Close();
+ }
+ if (CommandLineOptions.Clo.Wait)
+ {
+ Console.WriteLine("Press Enter to exit.");
+ Console.ReadLine();
+ }
+ }
+
+ public static void ErrorWriteLine(string! s) {
+ if (!s.Contains("Error: ") && !s.Contains("Error BP")) {
+ Console.WriteLine(s);
+ return;
+ }
+
+ // split the string up into its first line and the remaining lines
+ string remaining = null;
+ int i = s.IndexOf('\r');
+ if (0 <= i) {
+ remaining = s.Substring(i+1);
+ if (remaining.StartsWith("\n")) {
+ remaining = remaining.Substring(1);
+ }
+ s = s.Substring(0, i);
+ }
+
+ ConsoleColor col = Console.ForegroundColor;
+ Console.ForegroundColor = ConsoleColor.Red;
+ Console.WriteLine(s);
+ Console.ForegroundColor = col;
+
+ if (remaining != null) {
+ Console.WriteLine(remaining);
+ }
+ }
+
+ public static void ErrorWriteLine(string! format, params object[] args) {
+ string s = string.Format(format, args);
+ ErrorWriteLine(s);
+ }
+
+ public static void AdvisoryWriteLine(string! format, params object[] args) {
+ ConsoleColor col = Console.ForegroundColor;
+ Console.ForegroundColor = ConsoleColor.Yellow;
+ Console.WriteLine(format, args);
+ Console.ForegroundColor = col;
+ }
+
+ public static void SelectPlatform(CommandLineOptions! options)
+ {
+ if (options.TargetPlatformLocation != null) {
+ // Make sure static constructor runs before we start setting locations, etc.
+ System.Compiler.SystemTypes.Clear();
+
+ switch (options.TargetPlatform){
+ case CommandLineOptions.PlatformType.v1: Microsoft.SpecSharp.TargetPlatform.SetToV1(options.TargetPlatformLocation); break;
+ case CommandLineOptions.PlatformType.v11: Microsoft.SpecSharp.TargetPlatform.SetToV1_1(options.TargetPlatformLocation); break;
+ case CommandLineOptions.PlatformType.v2: Microsoft.SpecSharp.TargetPlatform.SetToV2(options.TargetPlatformLocation); break;
+ case CommandLineOptions.PlatformType.cli1: Microsoft.SpecSharp.TargetPlatform.SetToPostV1_1(options.TargetPlatformLocation); break;
+ }
+
+ if (options.StandardLibraryLocation != null && options.StandardLibraryLocation.Length > 0){
+ System.Compiler.SystemAssemblyLocation.Location = options.StandardLibraryLocation;
+ }
+ System.Compiler.SystemCompilerRuntimeAssemblyLocation.Location = options.TargetPlatformLocation + @"\System.Compiler.Runtime.dll";
+
+ System.Compiler.SystemTypes.Initialize(true, true);
+ }
+ }
+
+ static string! GetErrorLine(Cci.ErrorNode! node)
+ requires node.SourceContext.Document != null ==> node.SourceContext.Document.Name != null;
+ {
+ string message = node.GetMessage(System.Threading.Thread.CurrentThread.CurrentUICulture);
+ string kind;
+ if (message.Contains("(trace position)")) {
+ kind = "Related information";
+ } else {
+ kind = "Error";
+ }
+ if (node.SourceContext.Document != null) {
+ return string.Format("{0}({1},{2}): {3}: {4}", Path.GetFileName((!)node.SourceContext.Document.Name), node.SourceContext.StartLine, node.SourceContext.StartColumn, kind, message);
+ } else {
+ return string.Format("{0}: {1}", kind, message);
+ }
+ }
+
+ enum FileType { Unknown, Cil, Bpl, Dafny };
+
+ class ErrorReporter
+ {
+ public Cci.ErrorNodeList! errors = new Cci.ErrorNodeList();
+ public int errorsReported;
+
+ public void ReportErrors()
+ {
+ //sort the portion of the array that will be reported to make output more deterministic
+ errors.Sort(errorsReported,errors.Count-errorsReported);
+ for(;errorsReported < errors.Count; errorsReported++) {
+ Cci.ErrorNode error = errors[errorsReported];
+ if (error != null)
+ ErrorWriteLine(GetErrorLine(error));
+ }
+ }
+ }
+
+ static void ProcessFiles (List<string!>! fileNames)
+ {
+ using (XmlFileScope xf = new XmlFileScope(CommandLineOptions.Clo.XmlSink, fileNames[fileNames.Count-1])) {
+ BoogiePL.Errors.count = 0;
+ Program program = ParseBoogieProgram(fileNames, false);
+ if (program == null) return;
+ if (CommandLineOptions.Clo.PrintFile != null) {
+ PrintBplFile(CommandLineOptions.Clo.PrintFile, program, false);
+ }
+
+ PipelineOutcome oc = ResolveAndTypecheck(program, fileNames[fileNames.Count-1]);
+ if (oc != PipelineOutcome.ResolvedAndTypeChecked) return;
+ BoogiePL.Errors.count = 0;
+ int errorCount, verified, inconclusives, timeOuts, outOfMemories;
+ oc = InferAndVerify(program, null, out errorCount, out verified, out inconclusives, out timeOuts, out outOfMemories);
+ switch (oc) {
+ case PipelineOutcome.Done:
+ case PipelineOutcome.VerificationCompleted:
+ WriteTrailer(verified, errorCount, inconclusives, timeOuts, outOfMemories);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+
+ static void PrintBplFile (string! filename, Program! program, bool allowPrintDesugaring)
+ {
+ bool oldPrintDesugaring = CommandLineOptions.Clo.PrintDesugarings;
+ if (!allowPrintDesugaring) {
+ CommandLineOptions.Clo.PrintDesugarings = false;
+ }
+ using (TokenTextWriter writer = filename == "-" ?
+ new TokenTextWriter("<console>", Console.Out) :
+ new TokenTextWriter(filename))
+ {
+ if (CommandLineOptions.Clo.ShowEnv != CommandLineOptions.ShowEnvironment.Never) {
+ writer.WriteLine("// " + CommandLineOptions.Clo.Version);
+ writer.WriteLine("// " + CommandLineOptions.Clo.Environment);
+ }
+ writer.WriteLine();
+ program.Emit(writer);
+ }
+ CommandLineOptions.Clo.PrintDesugarings = oldPrintDesugaring;
+ }
+
+
+ static bool ProgramHasDebugInfo (Program! program)
+ {
+ // We inspect the last declaration because the first comes from the prelude and therefore always has source context.
+ return program.TopLevelDeclarations.Count > 0 &&
+ ((!)program.TopLevelDeclarations[program.TopLevelDeclarations.Count - 1]).tok.IsValid;
+ }
+
+
+ /// <summary>
+ /// Inform the user about something and proceed with translation normally.
+ /// Print newline after the message.
+ /// </summary>
+ public static void Inform(string s) {
+ if ( ! CommandLineOptions.Clo.Trace) { return; }
+ Console.WriteLine(s);
+ }
+
+ static void WriteTrailer(int verified, int errors, int inconclusives, int timeOuts, int outOfMemories)
+ requires 0 <= errors && 0 <= inconclusives && 0 <= timeOuts && 0 <= outOfMemories;
+ {
+ Console.WriteLine();
+ Console.Write("{0} finished with {1} verified, {2} error{3}", CommandLineOptions.Clo.ToolName, verified, errors, errors == 1 ? "" : "s");
+ if (inconclusives != 0) {
+ Console.Write(", {0} inconclusive{1}", inconclusives, inconclusives == 1 ? "" : "s");
+ }
+ if (timeOuts != 0) {
+ Console.Write(", {0} time out{1}", timeOuts, timeOuts == 1 ? "" : "s");
+ }
+ if (outOfMemories != 0) {
+ Console.Write(", {0} out of memory", outOfMemories);
+ }
+ Console.WriteLine();
+ Console.Out.Flush();
+ }
+
+
+
+ static void ReportBplError(Absy! node, string! message, bool error, bool showBplLocation)
+ {
+ IToken tok = node.tok;
+ string s;
+ if (tok != null && showBplLocation) {
+ s = string.Format("{0}({1},{2}): {3}", tok.filename, tok.line, tok.col, message);
+ } else {
+ s = message;
+ }
+ if (error) {
+ ErrorWriteLine(s);
+ } else {
+ Console.WriteLine(s);
+ }
+ }
+
+ /// <summary>
+ /// Parse the given files into one Boogie program. If an I/O or parse error occurs, an error will be printed
+ /// and null will be returned. On success, a non-null program is returned.
+ /// </summary>
+ static Program ParseBoogieProgram(List<string!>! fileNames, bool suppressTraceOutput)
+ {
+ BoogiePL.Errors.count = 0;
+ Program program = null;
+ bool okay = true;
+ foreach (string bplFileName in fileNames) {
+ if (!suppressTraceOutput) {
+ if (CommandLineOptions.Clo.XmlSink != null) {
+ CommandLineOptions.Clo.XmlSink.WriteFileFragment(bplFileName);
+ }
+ if (CommandLineOptions.Clo.Trace) {
+ Console.WriteLine("Parsing " + bplFileName);
+ }
+ }
+
+ Program programSnippet;
+ int errorCount;
+ try {
+ errorCount = BoogiePL.Parser.Parse(bplFileName, out programSnippet);
+ if (programSnippet == null || errorCount != 0) {
+ Console.WriteLine("{0} parse errors detected in {1}", BoogiePL.Errors.count, bplFileName);
+ okay = false;
+ continue;
+ }
+ } catch (IOException e) {
+ ErrorWriteLine("Error opening file \"{0}\": {1}", bplFileName, e.Message);
+ okay = false;
+ continue;
+ }
+ if (program == null) {
+ program = programSnippet;
+ } else if (programSnippet != null) {
+ program.TopLevelDeclarations.AddRange(programSnippet.TopLevelDeclarations);
+ }
+ }
+ if (!okay) {
+ return null;
+ } else if (program == null) {
+ return new Program();
+ } else {
+ return program;
+ }
+ }
+
+
+ enum PipelineOutcome { Done, ResolutionError, TypeCheckingError, ResolvedAndTypeChecked, FatalError, VerificationCompleted }
+
+ /// <summary>
+ /// Resolves and type checks the given Boogie program. Any errors are reported to the
+ /// console. Returns:
+ /// - Done if no errors occurred, and command line specified no resolution or no type checking.
+ /// - ResolutionError if a resolution error occurred
+ /// - TypeCheckingError if a type checking error occurred
+ /// - ResolvedAndTypeChecked if both resolution and type checking succeeded
+ /// </summary>
+ static PipelineOutcome ResolveAndTypecheck (Program! program, string! bplFileName)
+ {
+ // ---------- Resolve ------------------------------------------------------------
+
+ if (CommandLineOptions.Clo.NoResolve) { return PipelineOutcome.Done; }
+
+ int errorCount = program.Resolve();
+ if (errorCount != 0) {
+ Console.WriteLine("{0} name resolution errors detected in {1}", errorCount, bplFileName);
+ return PipelineOutcome.ResolutionError;
+ }
+
+ // ---------- Type check ------------------------------------------------------------
+
+ if (CommandLineOptions.Clo.NoTypecheck) { return PipelineOutcome.Done; }
+
+ errorCount = program.Typecheck();
+ if (errorCount != 0) {
+ Console.WriteLine("{0} type checking errors detected in {1}", errorCount, bplFileName);
+ return PipelineOutcome.TypeCheckingError;
+ }
+
+ if (CommandLineOptions.Clo.PrintFile != null && CommandLineOptions.Clo.PrintDesugarings)
+ {
+ // if PrintDesugaring option is engaged, print the file here, after resolution and type checking
+ PrintBplFile(CommandLineOptions.Clo.PrintFile, program, true);
+ }
+
+ return PipelineOutcome.ResolvedAndTypeChecked;
+ }
+
+ /// <summary>
+ /// Given a resolved and type checked Boogie program, infers invariants for the program
+ /// and then attempts to verify it. Returns:
+ /// - Done if command line specified no verification
+ /// - FatalError if a fatal error occurred, in which case an error has been printed to console
+ /// - VerificationCompleted if inference and verification completed, in which the out
+ /// parameters contain meaningful values
+ /// </summary>
+ static PipelineOutcome InferAndVerify (Program! program,
+ ErrorReporter errorReporter,
+ out int errorCount, out int verified, out int inconclusives, out int timeOuts, out int outOfMemories)
+ ensures 0 <= inconclusives && 0 <= timeOuts;
+ {
+ errorCount = verified = inconclusives = timeOuts = outOfMemories = 0;
+
+ // ---------- Infer invariants --------------------------------------------------------
+
+ // Abstract interpretation -> Always use (at least) intervals, if not specified otherwise (e.g. with the "/noinfer" switch)
+ Microsoft.Boogie.AbstractInterpretation.AbstractInterpretation.RunAbstractInterpretation(program);
+
+ if (CommandLineOptions.Clo.LoopUnrollCount != -1) {
+ program.UnrollLoops(CommandLineOptions.Clo.LoopUnrollCount);
+ }
+
+ if (CommandLineOptions.Clo.PrintInstrumented) {
+ program.Emit(new TokenTextWriter(Console.Out));
+ }
+
+ // ---------- Verify ------------------------------------------------------------
+
+ if (!CommandLineOptions.Clo.Verify) { return PipelineOutcome.Done; }
+
+ #region Verify each implementation
+
+#if ROB_DEBUG
+ string now = DateTime.Now.ToString().Replace(' ','-').Replace('/','-').Replace(':','-');
+ System.IO.StreamWriter w = new System.IO.StreamWriter(@"\temp\batch_"+now+".bpl");
+ program.Emit(new TokenTextWriter(w));
+ w.Close();
+#endif
+
+ ConditionGeneration vcgen = null;
+ try
+ {
+ if (CommandLineOptions.Clo.vcVariety == CommandLineOptions.VCVariety.Doomed)
+ {
+ vcgen = new DCGen(program, CommandLineOptions.Clo.SimplifyLogFilePath, CommandLineOptions.Clo.SimplifyLogFileAppend);
+ } else
+ {
+ vcgen = new VCGen(program, CommandLineOptions.Clo.SimplifyLogFilePath, CommandLineOptions.Clo.SimplifyLogFileAppend);
+ }
+ }
+ catch (ProverException e)
+ {
+ ErrorWriteLine("Fatal Error: ProverException: {0}", e);
+ return PipelineOutcome.FatalError;
+ }
+
+ foreach ( Declaration! decl in program.TopLevelDeclarations )
+ {
+ Implementation impl = decl as Implementation;
+ if (impl != null && CommandLineOptions.Clo.UserWantsToCheckRoutine((!)impl.Name) && !impl.SkipVerification) {
+ List<Counterexample!>? errors;
+
+ DateTime start = new DateTime(); // to please compiler's definite assignment rules
+ if (CommandLineOptions.Clo.Trace || CommandLineOptions.Clo.XmlSink != null) {
+ start = DateTime.Now;
+ if (CommandLineOptions.Clo.Trace) {
+ Console.WriteLine();
+ Console.WriteLine("Verifying {0} ...", impl.Name);
+ }
+ if (CommandLineOptions.Clo.XmlSink != null) {
+ CommandLineOptions.Clo.XmlSink.WriteStartMethod(impl.Name, start);
+ }
+ }
+
+ VCGen.Outcome outcome;
+ try
+ {
+ outcome = vcgen.VerifyImplementation(impl, program, out errors);
+ }
+ catch (VCGenException e)
+ {
+ ReportBplError(impl, String.Format("Error BP5010: {0} Encountered in implementation {1}.", e.Message, impl.Name), true, true);
+ errors = null;
+ outcome = VCGen.Outcome.Inconclusive;
+ }
+ catch (UnexpectedProverOutputException upo)
+ {
+ AdvisoryWriteLine("Advisory: {0} SKIPPED because of internal error: unexpected prover output: {1}", impl.Name, upo.Message);
+ errors = null;
+ outcome = VCGen.Outcome.Inconclusive;
+ }
+
+ string! timeIndication = "";
+ DateTime end = DateTime.Now;
+ TimeSpan elapsed = end - start;
+ if (CommandLineOptions.Clo.Trace || CommandLineOptions.Clo.XmlSink != null) {
+ if (CommandLineOptions.Clo.Trace) {
+ timeIndication = string.Format(" [{0} s] ", elapsed.TotalSeconds);
+ }
+ }
+
+
+ switch (outcome) {
+ default:
+ assert false; // unexpected outcome
+ case VCGen.Outcome.Correct:
+ if (CommandLineOptions.Clo.vcVariety == CommandLineOptions.VCVariety.Doomed) {
+ Inform(String.Format("{0}doomed", timeIndication));
+ errorCount++;
+ } else {
+ Inform(String.Format("{0}verified", timeIndication));
+ verified++;
+ }
+ break;
+ case VCGen.Outcome.TimedOut:
+ timeOuts++;
+ Inform(String.Format("{0}timed out", timeIndication));
+ break;
+ case VCGen.Outcome.OutOfMemory:
+ outOfMemories++;
+ Inform(String.Format("{0}out of memory", timeIndication));
+ break;
+ case VCGen.Outcome.Inconclusive:
+ inconclusives++;
+ Inform(String.Format("{0}inconclusive", timeIndication));
+ break;
+ case VCGen.Outcome.Errors:
+ if (CommandLineOptions.Clo.vcVariety == CommandLineOptions.VCVariety.Doomed) {
+ Inform(String.Format("{0}credible", timeIndication));
+ verified++;
+ } else {
+ assert errors != null; // guaranteed by postcondition of VerifyImplementation
+ if (errorReporter != null)
+ {
+ // assert translatedProgram != null;
+ // ErrorReporting h = new ErrorReporting();
+ // h.errorReportingWithTrace(translatedProgram, errors, impl);
+
+ errorReporter.ReportErrors();
+ }
+ else
+ {
+ // BP1xxx: Parsing errors
+ // BP2xxx: Name resolution errors
+ // BP3xxx: Typechecking errors
+ // BP4xxx: Abstract interpretation errors (Is there such a thing?)
+ // BP5xxx: Verification errors
+
+ errors.Sort(new CounterexampleComparer());
+ foreach (Counterexample error in errors)
+ {
+ if (error is CallCounterexample)
+ {
+ CallCounterexample! err = (CallCounterexample) error;
+ if (!CommandLineOptions.Clo.ForceBplErrors && err.FailingRequires.ErrorMessage != null) {
+ ReportBplError(err.FailingRequires, err.FailingRequires.ErrorMessage, true, false);
+ } else {
+ ReportBplError(err.FailingCall, "Error BP5002: A precondition for this call might not hold.", true, true);
+ ReportBplError(err.FailingRequires, "Related location: This is the precondition that might not hold.", false, true);
+ }
+ if (CommandLineOptions.Clo.XmlSink != null) {
+ CommandLineOptions.Clo.XmlSink.WriteError("precondition violation", err.FailingCall.tok, err.FailingRequires.tok, error.Trace);
+ }
+ }
+ else if (error is ReturnCounterexample)
+ {
+ ReturnCounterexample! err = (ReturnCounterexample) error;
+ if (!CommandLineOptions.Clo.ForceBplErrors && err.FailingEnsures.ErrorMessage != null) {
+ ReportBplError(err.FailingEnsures, err.FailingEnsures.ErrorMessage, true, false);
+ } else {
+ ReportBplError(err.FailingReturn, "Error BP5003: A postcondition might not hold at this return statement.", true, true);
+ ReportBplError(err.FailingEnsures, "Related location: This is the postcondition that might not hold.", false, true);
+ }
+ if (CommandLineOptions.Clo.XmlSink != null) {
+ CommandLineOptions.Clo.XmlSink.WriteError("postcondition violation", err.FailingReturn.tok, err.FailingEnsures.tok, error.Trace);
+ }
+ }
+ else // error is AssertCounterexample
+ {
+ AssertCounterexample! err = (AssertCounterexample) error;
+ if (err.FailingAssert is LoopInitAssertCmd) {
+ ReportBplError(err.FailingAssert, "Error BP5004: This loop invariant might not hold on entry.", true, true);
+ if (CommandLineOptions.Clo.XmlSink != null) {
+ CommandLineOptions.Clo.XmlSink.WriteError("loop invariant entry violation", err.FailingAssert.tok, null, error.Trace);
+ }
+ }
+ else if (err.FailingAssert is LoopInvMaintainedAssertCmd) {
+ // this assertion is a loop invariant which is not maintained
+ ReportBplError(err.FailingAssert, "Error BP5005: This loop invariant might not be maintained by the loop.", true, true);
+ if (CommandLineOptions.Clo.XmlSink != null) {
+ CommandLineOptions.Clo.XmlSink.WriteError("loop invariant maintenance violation", err.FailingAssert.tok, null, error.Trace);
+ }
+ } else {
+ if (!CommandLineOptions.Clo.ForceBplErrors && err.FailingAssert.ErrorMessage != null) {
+ ReportBplError(err.FailingAssert, err.FailingAssert.ErrorMessage, true, false);
+ } else if (err.FailingAssert.ErrorData is string) {
+ ReportBplError(err.FailingAssert, (string)err.FailingAssert.ErrorData, true, true);
+ } else {
+ ReportBplError(err.FailingAssert, "Error BP5001: This assertion might not hold.", true, true);
+ }
+ if (CommandLineOptions.Clo.XmlSink != null) {
+ CommandLineOptions.Clo.XmlSink.WriteError("assertion violation", err.FailingAssert.tok, null, error.Trace);
+ }
+ }
+ }
+ if (CommandLineOptions.Clo.EnhancedErrorMessages == 1) {
+ foreach (string! info in error.relatedInformation) {
+ Console.WriteLine(" " + info);
+ }
+ }
+ if (CommandLineOptions.Clo.ErrorTrace > 0) {
+ Console.WriteLine("Execution trace:");
+ foreach (Block! b in error.Trace) {
+ if (b.tok == null) {
+ Console.WriteLine(" <intermediate block>");
+ } else {
+ // for ErrorTrace == 1 restrict the output;
+ // do not print tokens with -17:-4 as their location because they have been
+ // introduced in the translation and do not give any useful feedback to the user
+ if (!(CommandLineOptions.Clo.ErrorTrace == 1 && b.tok.line == -17 && b.tok.col == -4)) {
+ Console.WriteLine(" {0}({1},{2}): {3}", b.tok.filename, b.tok.line, b.tok.col, b.Label);
+ }
+ }
+ }
+ }
+ errorCount++;
+ }
+ }
+ Inform(String.Format("{0}error{1}", timeIndication, errors.Count == 1 ? "" : "s"));
+ }
+ break;
+ }
+ if (CommandLineOptions.Clo.XmlSink != null) {
+ CommandLineOptions.Clo.XmlSink.WriteEndMethod(outcome.ToString().ToLowerInvariant(), end, elapsed);
+ }
+ if (outcome == VCGen.Outcome.Errors || CommandLineOptions.Clo.Trace) {
+ Console.Out.Flush();
+ }
+ }
+ }
+ vcgen.Close();
+ ((!)CommandLineOptions.Clo.TheProverFactory).Close();
+
+ #endregion
+
+ return PipelineOutcome.VerificationCompleted;
+ }
+
+ }
+}
diff --git a/Source/BoogieDriver/BoogieDriver.sscproj b/Source/BoogieDriver/BoogieDriver.sscproj
new file mode 100644
index 00000000..37a31003
--- /dev/null
+++ b/Source/BoogieDriver/BoogieDriver.sscproj
@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="utf-8"?>
+<VisualStudioProject>
+ <XEN ProjectType="Local"
+ SchemaVersion="1.0"
+ Name="BoogieDriver"
+ ProjectGuid="d2b98e4a-2eab-4065-abfa-709ac5ca7d4c"
+ >
+ <Build>
+ <Settings ApplicationIcon=""
+ AssemblyName="Boogie"
+ OutputType="Exe"
+ RootNamespace="Boogie"
+ StartupObject=""
+ StandardLibraryLocation=""
+ TargetPlatform="v2"
+ TargetPlatformLocation=""
+ >
+ <Config Name="Debug"
+ AllowUnsafeBlocks="False"
+ BaseAddress="285212672"
+ CheckForOverflowUnderflow="False"
+ ConfigurationOverrideFile=""
+ DefineConstants="DEBUG;TRACE"
+ DocumentationFile=""
+ DebugSymbols="True"
+ FileAlignment="4096"
+ IncrementalBuild="True"
+ Optimize="False"
+ OutputPath="..\..\Binaries"
+ RegisterForComInterop="False"
+ RemoveIntegerChecks="false"
+ TreatWarningsAsErrors="False"
+ WarningLevel="4"
+ CheckContractAdmissibility="True"
+ CheckPurity="False"
+ />
+ <Config Name="Release"
+ AllowUnsafeBlocks="false"
+ BaseAddress="285212672"
+ CheckForOverflowUnderflow="false"
+ ConfigurationOverrideFile=""
+ DefineConstants="TRACE"
+ DocumentationFile=""
+ DebugSymbols="false"
+ FileAlignment="4096"
+ IncrementalBuild="false"
+ Optimize="true"
+ OutputPath="bin\release"
+ RegisterForComInterop="false"
+ RemoveIntegerChecks="false"
+ TreatWarningsAsErrors="false"
+ WarningLevel="4"
+ CheckContractAdmissibility="True"
+ CheckPurity="False"
+ />
+ </Settings>
+ <References>
+ <Reference Name="System"
+ AssemblyName="System"
+ Private="false"
+ />
+ <Reference Name="System.Data"
+ AssemblyName="System.Data"
+ Private="false"
+ />
+ <Reference Name="System.Xml"
+ AssemblyName="System.Xml"
+ Private="false"
+ />
+ <Reference Name="System.Compiler"
+ AssemblyName="System.Compiler"
+ Private="false"
+ HintPath="../../Binaries/System.Compiler.dll"
+ />
+ <Reference Name="Microsoft.SpecSharp"
+ AssemblyName="Microsoft.SpecSharp"
+ Private="false"
+ HintPath="../../Binaries/Microsoft.SpecSharp.dll"
+ />
+ <Reference Name="System.Compiler.Framework"
+ AssemblyName="System.Compiler.Framework"
+ Private="false"
+ HintPath="../../Binaries/System.Compiler.Framework.dll"
+ />
+ <Reference Name="AbsInt"
+ Project="{11D06232-2039-4BCA-853B-C596E2A4EDB0}"
+ Private="true"
+ />
+ <Reference Name="AIFramework"
+ Project="{24B55172-AD8B-47D1-8952-5A95CFDB9B31}"
+ Private="true"
+ />
+ <Reference Name="Basetypes"
+ Project="{0C692837-77EC-415F-BF04-395E3ED06E9A}"
+ Private="true"
+ />
+ <Reference Name="Core"
+ Project="{47BC34F1-A173-40BE-84C2-9332B4418387}"
+ Private="true"
+ />
+ <Reference Name="Graph"
+ Project="{4C28FB90-630E-4B55-A937-11A011B79765}"
+ Private="true"
+ />
+ <Reference Name="Simplify"
+ Project="{F75666DE-FB56-457C-8782-09BE243450FC}"
+ Private="true"
+ />
+ <Reference Name="SMTLib"
+ Project="{13C3A68C-462A-4CDA-A480-738046E37C5A}"
+ Private="true"
+ />
+ <Reference Name="Z3"
+ Project="{F75666DE-CD56-457C-8782-09BE243450FC}"
+ Private="true"
+ />
+ <Reference Name="VCExpr"
+ Project="{CF42B700-10AA-4DA9-8992-48A800251C11}"
+ Private="true"
+ />
+ <Reference Name="VCGeneration"
+ Project="{F65666DE-FB56-457C-8782-09BE243450FC}"
+ Private="true"
+ />
+ </References>
+ </Build>
+ <Files>
+ <Include>
+ <File RelPath="BoogieDriver.ssc"
+ SubType="Code"
+ BuildAction="Compile"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="..\version.ssc"
+ />
+ </Include>
+ </Files>
+ </XEN>
+</VisualStudioProject> \ No newline at end of file
diff --git a/Source/Core.sscproj b/Source/Core.sscproj
new file mode 100644
index 00000000..f1b7aea7
--- /dev/null
+++ b/Source/Core.sscproj
@@ -0,0 +1,210 @@
+<?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=""
+ CmdArgs="houd1.bpl /Houdini /prover:z3api"
+ WorkingDirectory="c:\boogie\test\houdini"
+ 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="C:/Program Files/Microsoft/Spec#/bin/Mscorlib.Contracts.dll"
+ />
+ <Reference Name="System"
+ AssemblyName="System"
+ Private="false"
+ />
+ <Reference Name="System.Compiler.Framework"
+ AssemblyName="System.Compiler.Framework"
+ Private="true"
+ HintPath="C:/Program Files/Microsoft/Spec#/bin/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="C:/Program Files/Microsoft/Spec#/bin/System.Compiler.dll"
+ />
+ <Reference Name="Graph"
+ Project="{4C28FB90-630E-4B55-A937-11A011B79765}"
+ Private="true"
+ />
+ <Reference Name="System.XML"
+ AssemblyName="System.XML"
+ Private="false"
+ HintPath="C:/WINDOWS/Microsoft.NET/Framework/v2.0.50727/System.XML.dll"
+ />
+ <Reference Name="System.Xml.Contracts"
+ AssemblyName="System.Xml.Contracts"
+ Private="true"
+ HintPath="C:/Program Files/Microsoft/Spec#/bin/System.Xml.Contracts.dll"
+ />
+ <Reference Name="FSharp.Core"
+ AssemblyName="FSharp.Core"
+ Private="true"
+ HintPath="C:/Program Files/FSharp-1.9.6.16/bin/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/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
+$$$
diff --git a/Source/Dafny.sln b/Source/Dafny.sln
new file mode 100644
index 00000000..307f5d1d
--- /dev/null
+++ b/Source/Dafny.sln
@@ -0,0 +1,26 @@
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{07C4E3D1-6B67-4060-8A92-940DB82041ED}") = "DafnyDriver", "DafnyDriver\DafnyDriver.sscproj", "{1F1E6F68-E9DF-4181-8CD3-E8C98637084D}"
+EndProject
+Project("{07C4E3D1-6B67-4060-8A92-940DB82041ED}") = "Dafny", "Dafny\DafnyPipeline.sscproj", "{DEAD83C6-1510-4AF9-8F7D-C837DDBB2632}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|.NET = Debug|.NET
+ Release|.NET = Release|.NET
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {1F1E6F68-E9DF-4181-8CD3-E8C98637084D}.Debug|.NET.ActiveCfg = Debug|.NET
+ {1F1E6F68-E9DF-4181-8CD3-E8C98637084D}.Debug|.NET.Build.0 = Debug|.NET
+ {1F1E6F68-E9DF-4181-8CD3-E8C98637084D}.Release|.NET.ActiveCfg = Release|.NET
+ {1F1E6F68-E9DF-4181-8CD3-E8C98637084D}.Release|.NET.Build.0 = Release|.NET
+ {DEAD83C6-1510-4AF9-8F7D-C837DDBB2632}.Debug|.NET.ActiveCfg = Debug|.NET
+ {DEAD83C6-1510-4AF9-8F7D-C837DDBB2632}.Debug|.NET.Build.0 = Debug|.NET
+ {DEAD83C6-1510-4AF9-8F7D-C837DDBB2632}.Release|.NET.ActiveCfg = Release|.NET
+ {DEAD83C6-1510-4AF9-8F7D-C837DDBB2632}.Release|.NET.Build.0 = Release|.NET
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/Source/Dafny.suo b/Source/Dafny.suo
new file mode 100644
index 00000000..b6720c92
--- /dev/null
+++ b/Source/Dafny.suo
Binary files differ
diff --git a/Source/Dafny/Dafny.atg b/Source/Dafny/Dafny.atg
new file mode 100644
index 00000000..3d7630ae
--- /dev/null
+++ b/Source/Dafny/Dafny.atg
@@ -0,0 +1,963 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+
+/*---------------------------------------------------------------------------
+// Dafny
+// Rustan Leino, first created 25 January 2008
+//--------------------------------------------------------------------------*/
+
+using System.Collections.Generic;
+using Microsoft.Boogie;
+
+
+COMPILER Dafny
+
+/*--------------------------------------------------------------------------*/
+
+static List<ClassDecl!>! theClasses = new List<ClassDecl!>();
+
+
+static Expression! dummyExpr = new LiteralExpr(Token.NoToken);
+static Statement! dummyStmt = new ReturnStmt(Token.NoToken);
+static Attributes.Argument! dummyAttrArg = new Attributes.Argument("dummyAttrArg");
+static Scope<string>! parseVarScope = new Scope<string>();
+
+///<summary>
+/// Parses top level declarations from "filename" and appends them to "classes".
+/// Returns the number of parsing errors encountered.
+/// Note: first initialize the Scanner.
+///</summary>
+public static int Parse (string! filename, List<ClassDecl!>! classes) /* throws System.IO.IOException */ {
+ using (System.IO.StreamReader reader = new System.IO.StreamReader(filename)) {
+ BoogiePL.Buffer.Fill(reader);
+ Scanner.Init(filename);
+ return Parse(classes);
+ }
+}
+
+///<summary>
+/// Parses top level declarations and appends them to "classes".
+/// Returns the number of parsing errors encountered.
+/// Note: first initialize the Scanner.
+///</summary>
+public static int Parse (List<ClassDecl!>! classes) {
+ List<ClassDecl!> oldClasses = theClasses;
+ theClasses = classes;
+ Parse();
+ theClasses = oldClasses;
+ return Errors.count;
+}
+
+/*--------------------------------------------------------------------------*/
+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}.
+ digits = digit {digit}.
+ string = quote {nonquote} quote.
+
+COMMENTS FROM "/*" TO "*/" NESTED
+COMMENTS FROM "//" TO lf
+
+IGNORE cr + lf + tab
+
+
+/*------------------------------------------------------------------------*/
+PRODUCTIONS
+
+Dafny
+= (. ClassDecl! c; .)
+ { ClassDecl<out c> (. theClasses.Add(c); .)
+ }
+ EOF
+ .
+
+ClassDecl<out ClassDecl! c>
+= (. Token! id;
+ Attributes attrs = null;
+ List<TypeParameter!> typeArgs = new List<TypeParameter!>();
+ List<MemberDecl!> members = new List<MemberDecl!>();
+ .)
+ "class"
+ { Attribute<ref attrs> }
+ Ident<out id>
+ [ GenericParameters<typeArgs> ]
+ "{"
+ { ClassMemberDecl<members>
+ }
+ "}" (. c = new ClassDecl(id, id.val, typeArgs, members, attrs); .)
+ .
+
+ClassMemberDecl<List<MemberDecl!\>! mm>
+= (. Method! m;
+ Function! f;
+ .)
+ ( FieldDecl<mm>
+ | FunctionDecl<out f> (. mm.Add(f); .)
+ | MethodDecl<out m> (. mm.Add(m); .)
+ | FrameDecl
+ )
+ .
+
+FieldDecl<List<MemberDecl!\>! mm>
+= (. Attributes attrs = null;
+ Token! id; Type! ty;
+ .)
+ "var"
+ { Attribute<ref attrs> }
+ IdentType<out id, out ty> (. mm.Add(new Field(id, id.val, ty, attrs)); .)
+ { "," IdentType<out id, out ty> (. mm.Add(new Field(id, id.val, ty, attrs)); .)
+ }
+ ";"
+ .
+
+IdentType<out Token! id, out Type! ty>
+= Ident<out id>
+ ":"
+ Type<out ty>
+ .
+
+IdentTypeOptional<out BoundVar! var>
+= (. Token! id; Type! ty; Type optType = null;
+ .)
+ Ident<out id>
+ [ ":" Type<out ty> (. optType = ty; .)
+ ]
+ (. var = new BoundVar(id, id.val, optType == null ? new InferredTypeProxy() : optType); .)
+ .
+
+/*------------------------------------------------------------------------*/
+
+GenericParameters<List<TypeParameter!\>! typeArgs>
+= (. Token! id; .)
+ "<"
+ Ident<out id> (. typeArgs.Add(new TypeParameter(id, id.val)); .)
+ { "," Ident<out id> (. typeArgs.Add(new TypeParameter(id, id.val)); .)
+ }
+ ">"
+ .
+
+FrameDecl
+= (. Token! id;
+ Attributes attrs = null;
+ .)
+ "frame"
+ { Attribute<ref attrs> }
+ Ident<out id>
+ "{"
+ /* TBD */
+ "}"
+ .
+
+/*------------------------------------------------------------------------*/
+
+MethodDecl<out Method! m>
+= (. Token! id;
+ Attributes attrs = null;
+ List<TypeParameter!>! typeArgs = new List<TypeParameter!>();
+ List<Formal!> ins = new List<Formal!>();
+ List<Formal!> outs = new List<Formal!>();
+ List<MaybeFreeExpression!> req = new List<MaybeFreeExpression!>();
+ List<Expression!> mod = new List<Expression!>();
+ List<MaybeFreeExpression!> ens = new List<MaybeFreeExpression!>();
+ Statement! bb; Statement body = null;
+ .)
+ "method"
+ { Attribute<ref attrs> }
+ Ident<out id>
+ [ GenericParameters<typeArgs> ]
+ (. parseVarScope.PushMarker(); .)
+ Formals<true, ins>
+ [ "returns"
+ Formals<false, outs>
+ ]
+
+ ( ";" { MethodSpec<req, mod, ens> }
+ | { MethodSpec<req, mod, ens> } BlockStmt<out bb> (. body = bb; .)
+ )
+
+ (. parseVarScope.PopMarker();
+ m = new Method(id, id.val, typeArgs, ins, outs, req, mod, ens, body, attrs);
+ .)
+ .
+
+MethodSpec<List<MaybeFreeExpression!\>! req, List<Expression!\>! mod, List<MaybeFreeExpression!\>! ens>
+= (. Expression! e; bool isFree = false;
+ .)
+ ( "modifies" [ Expression<out e> (. mod.Add(e); .)
+ { "," Expression<out e> (. mod.Add(e); .)
+ }
+ ] ";"
+ | [ "free" (. isFree = true; .)
+ ]
+ ( "requires" Expression<out e> ";" (. req.Add(new MaybeFreeExpression(e, isFree)); .)
+ | "ensures" Expression<out e> ";" (. ens.Add(new MaybeFreeExpression(e, isFree)); .)
+ )
+ )
+ .
+
+Formals<bool incoming, List<Formal!\>! formals>
+= (. Token! id; Type! ty; .)
+ "("
+ [
+ IdentType<out id, out ty> (. formals.Add(new Formal(id, id.val, ty, incoming)); parseVarScope.Push(id.val, id.val); .)
+ { "," IdentType<out id, out ty> (. formals.Add(new Formal(id, id.val, ty, incoming)); parseVarScope.Push(id.val, id.val); .)
+ }
+ ]
+ ")"
+ .
+
+/*------------------------------------------------------------------------*/
+
+Type<out Type! ty>
+= (. ty = new BoolType(); /*keep compiler happy*/
+ .)
+ ( "bool" (. /* yeah, that's it! */ .)
+ | "int" (. ty = new IntType(); .)
+ | ReferenceType<out ty>
+ )
+ .
+
+ReferenceType<out Type! ty>
+= (. Token! x;
+ ty = new BoolType(); /*keep compiler happy*/
+ List<Type!>! gt;
+ .)
+ ( "object" (. ty = new ObjectType(); .)
+
+ | (. gt = new List<Type!>(); .)
+ Ident<out x>
+ [ GenericInstantiation<gt> ] (. ty = new ClassType(x, x.val, gt); .)
+
+ | (. gt = new List<Type!>(); .)
+ "set"
+ GenericInstantiation<gt> (. if (gt.Count != 1) {
+ SemErr("set type expects exactly one type argument");
+ }
+ ty = new SetType(gt[0]);
+ .)
+
+ | (. gt = new List<Type!>(); .)
+ "seq"
+ GenericInstantiation<gt> (. if (gt.Count != 1) {
+ SemErr("seq type expects exactly one type argument");
+ }
+ ty = new SeqType(gt[0]);
+ .)
+ )
+ .
+
+GenericInstantiation<List<Type!\>! gt>
+= (. Type! ty; .)
+ "<"
+ Type<out ty> (. gt.Add(ty); .)
+ { "," Type<out ty> (. gt.Add(ty); .)
+ }
+ ">"
+ .
+
+/*------------------------------------------------------------------------*/
+
+FunctionDecl<out Function! f>
+= (. Attributes attrs = null;
+ Token! id;
+ List<TypeParameter!> typeArgs = new List<TypeParameter!>();
+ List<Formal!> formals = new List<Formal!>();
+ Type! returnType;
+ List<Expression!> reqs = new List<Expression!>();
+ List<Expression!> reads = new List<Expression!>();
+ Expression! bb; Expression body = null;
+ bool use = false;
+ .)
+ "function"
+ { Attribute<ref attrs> }
+ [ "use" (. use = true; .) ]
+ Ident<out id>
+ [ GenericParameters<typeArgs> ]
+ (. parseVarScope.PushMarker(); .)
+ Formals<true, formals>
+ ":"
+ Type<out returnType>
+ ( ";"
+ { FunctionSpec<reqs, reads> }
+ | { FunctionSpec<reqs, reads> }
+ ExtendedExpr<out bb> (. body = bb; .)
+ )
+ (. parseVarScope.PopMarker();
+ f = new Function(id, id.val, use, typeArgs, formals, returnType, reqs, reads, body, attrs);
+ .)
+ .
+
+FunctionSpec<List<Expression!\>! reqs, List<Expression!\>! reads>
+= (. Expression! e; .)
+ ( "requires" Expression<out e> ";" (. reqs.Add(e); .)
+ | ReadsClause<reads>
+ )
+ .
+
+ReadsClause<List<Expression!\>! reads>
+= "reads"
+ [ Expressions<reads> ]
+ ";"
+ .
+
+ExtendedExpr<out Expression! e>
+= (. e = dummyExpr; .)
+ "{"
+ ( IfThenElseExpr<out e>
+ | Expression<out e>
+ )
+ "}"
+ .
+
+IfThenElseExpr<out Expression! e>
+= (. Token! x; Expression! e0; Expression! e1 = dummyExpr; .)
+ "if" (. x = token; .)
+ "(" Expression<out e> ")"
+ ExtendedExpr<out e0>
+ "else"
+ ( IfThenElseExpr<out e1>
+ | ExtendedExpr<out e1>
+ ) (. e = new ITEExpr(x, e, e0, e1); .)
+ .
+
+/*------------------------------------------------------------------------*/
+
+BlockStmt<out Statement! block>
+= (. Token! x;
+ List<Statement!> body = new List<Statement!>();
+ Statement! s;
+ .)
+ (. parseVarScope.PushMarker(); .)
+ "{" (. x = token; .)
+ { Stmt<body>
+ }
+ "}" (. block = new BlockStmt(x, body); .)
+ (. parseVarScope.PopMarker(); .)
+ .
+
+Stmt<List<Statement!\>! ss>
+= (. Statement! s; .)
+ /* By first reading a sequence of block statements, we avoid problems in the generated parser, despite
+ the ambiguity in the grammar. See Note in ConstAtomExpression production.
+ */
+ { BlockStmt<out s> (. ss.Add(s); .)
+ }
+ ( OneStmt<out s> (. ss.Add(s); .)
+ | VarDeclStmts<ss>
+ )
+ .
+
+OneStmt<out Statement! s>
+= (. Token! x; Token! id; string label = null;
+ s = dummyStmt; /* to please the compiler */
+ .)
+ /* This list does not contain BlockStmt, see comment above in Stmt production. */
+ ( AssertStmt<out s>
+ | AssumeStmt<out s>
+ | UseStmt<out s>
+ | AssignStmt<out s>
+ | HavocStmt<out s>
+ | CallStmt<out s>
+ | IfStmt<out s>
+ | WhileStmt<out s>
+ | ForeachStmt<out s>
+ | "label" (. x = token; .)
+ Ident<out id> ":" (. s = new LabelStmt(x, id.val); .)
+ | "break" (. x = token; .)
+ [ Ident<out id> (. label = id.val; .)
+ ] ";" (. s = new BreakStmt(x, label); .)
+ | "return" (. x = token; .)
+ ";" (. s = new ReturnStmt(x); .)
+ )
+ .
+
+AssignStmt<out Statement! s>
+= (. Token! x;
+ Expression! lhs;
+ Expression rhs;
+ Type ty;
+ s = dummyStmt;
+ .)
+ LhsExpr<out lhs>
+ ":=" (. x = token; .)
+ AssignRhs<out rhs, out ty> (. if (rhs != null) {
+ s = new AssignStmt(x, lhs, rhs);
+ } else {
+ assert ty != null;
+ s = new AssignStmt(x, lhs, ty);
+ }
+ .)
+ ";"
+ .
+
+AssignRhs<out Expression e, out Type ty>
+/* ensures e == null <==> ty == null; */
+= (. Expression! ee; Type! tt;
+ e = null; ty = null;
+ .)
+ ( "new" ReferenceType<out tt> (. ty = tt; .)
+ | Expression<out ee> (. e = ee; .)
+ ) (. if (e == null && ty == null) { e = dummyExpr; } .)
+ .
+
+HavocStmt<out Statement! s>
+= (. Token! x; Expression! lhs; .)
+ "havoc" (. x = token; .)
+ LhsExpr<out lhs> ";" (. s = new AssignStmt(x, lhs); .)
+ .
+
+LhsExpr<out Expression! e>
+= Expression<out e> /* TODO: restrict LHS further */
+ .
+
+VarDeclStmts<List<Statement!\>! ss>
+= (. VarDecl! d; .)
+ "var"
+ IdentTypeRhs<out d> (. ss.Add(d); parseVarScope.Push(d.Name, d.Name); .)
+ { "," IdentTypeRhs<out d> (. ss.Add(d); parseVarScope.Push(d.Name, d.Name); .)
+ }
+ ";"
+ .
+
+IdentTypeRhs<out VarDecl! d>
+= (. Token! id; Type! ty; Expression! e;
+ Expression rhs = null; Type newType = null;
+ Type optionalType = null; DeterminedAssignmentRhs optionalRhs = null;
+ .)
+ Ident<out id>
+ [ ":" Type<out ty> (. optionalType = ty; .)
+ ]
+ [ ":="
+ AssignRhs<out rhs, out newType>
+ ]
+ (. if (rhs != null) {
+ assert newType == null;
+ optionalRhs = new ExprRhs(rhs);
+ } else if (newType != null) {
+ optionalRhs = new TypeRhs(newType);
+ } else if (optionalType == null) {
+ optionalType = new InferredTypeProxy();
+ }
+ d = new VarDecl(id, id.val, optionalType, optionalRhs);
+ .)
+ .
+
+IfStmt<out Statement! ifStmt>
+= (. Token! x;
+ Expression guard;
+ Statement! thn;
+ Statement! s;
+ Statement els = null;
+ .)
+ "if" (. x = token; .)
+ Guard<out guard>
+ BlockStmt<out thn>
+ [ "else"
+ ( IfStmt<out s> (. els = s; .)
+ | BlockStmt<out s> (. els = s; .)
+ )
+ ]
+ (. ifStmt = new IfStmt(x, guard, thn, els); .)
+ .
+
+WhileStmt<out Statement! stmt>
+= (. Token! x;
+ Expression guard;
+ bool isFree; Expression! e;
+ List<MaybeFreeExpression!> invariants = new List<MaybeFreeExpression!>();
+ List<Expression!> decreases = new List<Expression!>();
+ Statement! body;
+ .)
+ "while" (. x = token; .)
+ Guard<out guard> (. assume guard == null || Owner.None(guard); .)
+ {( (. isFree = false; .)
+ [ "free" (. isFree = true; .)
+ ]
+ "invariant"
+ Expression<out e> (. invariants.Add(new MaybeFreeExpression(e, isFree)); .)
+ ";"
+ )
+ |
+ (
+ "decreases"
+ Expression<out e> (. decreases.Add(e); .)
+ { "," Expression<out e> (. decreases.Add(e); .)
+ }
+ ";"
+ )
+ }
+ BlockStmt<out body> (. stmt = new WhileStmt(x, guard, invariants, decreases, body); .)
+ .
+
+Guard<out Expression e> /* null represents demonic-choice */
+= (. Expression! ee; e = null; .)
+ "("
+ ( "*" (. e = null; .)
+ | Expression<out ee> (. e = ee; .)
+ )
+ ")"
+ .
+
+CallStmt<out Statement! s>
+= (. Token! x, id;
+ Expression! e;
+ List<IdentifierExpr!> lhs = new List<IdentifierExpr!>();
+ .)
+ "call" (. x = token; .)
+ CallStmtSubExpr<out e>
+
+ [ "," /* call a,b,c,... := ... */
+ (. if (e is IdentifierExpr) {
+ lhs.Add((IdentifierExpr)e);
+ } else if (e is FieldSelectExpr) {
+ SemErr(e.tok, "each LHS of call statement must be a variable, not a field");
+ } else {
+ SemErr(e.tok, "each LHS of call statement must be a variable");
+ }
+ .)
+ Ident<out id> (. lhs.Add(new IdentifierExpr(id, id.val)); .)
+ { "," Ident<out id> (. lhs.Add(new IdentifierExpr(id, id.val)); .)
+ }
+ ":="
+ CallStmtSubExpr<out e>
+
+ | ":=" /* call a := ... */
+ (. if (e is IdentifierExpr) {
+ lhs.Add((IdentifierExpr)e);
+ } else if (e is FieldSelectExpr) {
+ SemErr(e.tok, "each LHS of call statement must be a variable, not a field");
+ } else {
+ SemErr(e.tok, "each LHS of call statement must be a variable");
+ }
+ .)
+ CallStmtSubExpr<out e>
+ ]
+ ";"
+
+ /* "e" has now been parsed as one of: IdentifierExpr, FunctionCallExpr, FieldSelectExpr.
+ It denotes the RHS, so to be legal it must be a FunctionCallExpr. */
+ (. if (e is FunctionCallExpr) {
+ FunctionCallExpr fce = (FunctionCallExpr)e;
+ s = new CallStmt(x, lhs, fce.Receiver, fce.Name, fce.Args); // this actually does an ownership transfer of fce.Args
+ } else {
+ SemErr("RHS of call statement must denote a method invocation");
+ s = new CallStmt(x, lhs, dummyExpr, "dummyMethodName", new List<Expression!>());
+ }
+ .)
+ .
+
+/*------------------------------------------------------------------------*/
+
+ForeachStmt<out Statement! s>
+= (. Token! x, boundVar;
+ Type! ty;
+ Expression! collection;
+ Expression! range;
+ List<PredicateStmt!> bodyPrefix = new List<PredicateStmt!>();
+ AssignStmt bodyAssign = null;
+ .)
+ (. parseVarScope.PushMarker(); .)
+ "foreach" (. x = token;
+ range = new LiteralExpr(x, true);
+ ty = new InferredTypeProxy();
+ .)
+ "(" Ident<out boundVar>
+ [ ":" Type<out ty> ]
+ "in" Expression<out collection>
+ (. parseVarScope.Push(boundVar.val, boundVar.val); .)
+ [ "|" Expression<out range> ]
+ ")"
+ "{"
+ { AssertStmt<out s> (. if (s is PredicateStmt) { bodyPrefix.Add((PredicateStmt)s); } .)
+ | AssumeStmt<out s> (. if (s is PredicateStmt) { bodyPrefix.Add((PredicateStmt)s); } .)
+ | UseStmt<out s> (. if (s is PredicateStmt) { bodyPrefix.Add((PredicateStmt)s); } .)
+ }
+ ( AssignStmt<out s> (. if (s is AssignStmt) { bodyAssign = (AssignStmt)s; } .)
+ | HavocStmt<out s> (. if (s is AssignStmt) { bodyAssign = (AssignStmt)s; } .)
+ )
+ "}" (. s = new ForeachStmt(x, new BoundVar(boundVar, boundVar.val, ty), collection, range, bodyPrefix, bodyAssign); .)
+ (. parseVarScope.PopMarker(); .)
+ .
+
+AssertStmt<out Statement! s>
+= (. Token! x; Expression! e; .)
+ "assert" (. x = token; .)
+ Expression<out e> ";" (. s = new AssertStmt(x, e); .)
+ .
+
+AssumeStmt<out Statement! s>
+= (. Token! x; Expression! e; .)
+ "assume" (. x = token; .)
+ Expression<out e> ";" (. s = new AssumeStmt(x, e); .)
+ .
+
+UseStmt<out Statement! s>
+= (. Token! x; Expression! e; .)
+ "use" (. x = token; .)
+ Expression<out e> ";" (. s = new UseStmt(x, e); .)
+ .
+
+/*------------------------------------------------------------------------*/
+
+Expression<out Expression! e0>
+= (. Token! x; Expression! e1; .)
+ ImpliesExpression<out e0>
+ { EquivOp (. x = token; .)
+ ImpliesExpression<out e1> (. e0 = new BinaryExpr(x, BinaryExpr.Opcode.Iff, e0, e1); .)
+ }
+ .
+
+EquivOp = "<==>" | '\u21d4'.
+
+/*------------------------------------------------------------------------*/
+ImpliesExpression<out Expression! e0>
+= (. Token! x; Expression! e1; .)
+ LogicalExpression<out e0>
+ [ ImpliesOp (. x = token; .)
+ ImpliesExpression<out e1> (. e0 = new BinaryExpr(x, BinaryExpr.Opcode.Imp, e0, e1); .)
+ ]
+ .
+
+ImpliesOp = "==>" | '\u21d2'.
+
+/*------------------------------------------------------------------------*/
+LogicalExpression<out Expression! e0>
+= (. Token! x; Expression! e1; .)
+ RelationalExpression<out e0>
+ [ AndOp (. x = token; .)
+ RelationalExpression<out e1> (. e0 = new BinaryExpr(x, BinaryExpr.Opcode.And, e0, e1); .)
+ { AndOp (. x = token; .)
+ RelationalExpression<out e1> (. e0 = new BinaryExpr(x, BinaryExpr.Opcode.And, e0, e1); .)
+ }
+ | OrOp (. x = token; .)
+ RelationalExpression<out e1> (. e0 = new BinaryExpr(x, BinaryExpr.Opcode.Or, e0, e1); .)
+ { OrOp (. x = token; .)
+ RelationalExpression<out e1> (. e0 = new BinaryExpr(x, BinaryExpr.Opcode.Or, e0, e1); .)
+ }
+ ]
+ .
+
+AndOp = "&&" | '\u2227'.
+OrOp = "||" | '\u2228'.
+
+/*------------------------------------------------------------------------*/
+RelationalExpression<out Expression! e0>
+= (. Token! x; Expression! e1; BinaryExpr.Opcode op; .)
+ Term<out e0>
+ [ RelOp<out x, out op>
+ Term<out e1> (. e0 = new BinaryExpr(x, op, e0, e1); .)
+ ]
+ .
+
+RelOp<out Token! x, out BinaryExpr.Opcode op>
+= (. x = Token.NoToken; op = BinaryExpr.Opcode.Add/*(dummy)*/; .)
+ ( "==" (. x = token; op = BinaryExpr.Opcode.Eq; .)
+ | "<" (. x = token; op = BinaryExpr.Opcode.Lt; .)
+ | ">" (. x = token; op = BinaryExpr.Opcode.Gt; .)
+ | "<=" (. x = token; op = BinaryExpr.Opcode.Le; .)
+ | ">=" (. x = token; op = BinaryExpr.Opcode.Ge; .)
+ | "!=" (. x = token; op = BinaryExpr.Opcode.Neq; .)
+ | "!!" (. x = token; op = BinaryExpr.Opcode.Disjoint; .)
+ | "in" (. x = token; op = BinaryExpr.Opcode.In; .)
+ | '\u2260' (. x = token; op = BinaryExpr.Opcode.Neq; .)
+ | '\u2264' (. x = token; op = BinaryExpr.Opcode.Le; .)
+ | '\u2265' (. x = token; op = BinaryExpr.Opcode.Ge; .)
+ )
+ .
+
+/*------------------------------------------------------------------------*/
+Term<out Expression! e0>
+= (. Token! x; Expression! e1; BinaryExpr.Opcode op; .)
+ Factor<out e0>
+ { AddOp<out x, out op>
+ Factor<out e1> (. e0 = new BinaryExpr(x, op, e0, e1); .)
+ }
+ .
+
+AddOp<out Token! x, out BinaryExpr.Opcode op>
+= (. x = Token.NoToken; op=BinaryExpr.Opcode.Add/*(dummy)*/; .)
+ ( "+" (. x = token; op = BinaryExpr.Opcode.Add; .)
+ | "-" (. x = token; op = BinaryExpr.Opcode.Sub; .)
+ )
+ .
+
+/*------------------------------------------------------------------------*/
+Factor<out Expression! e0>
+= (. Token! x; Expression! e1; BinaryExpr.Opcode op; .)
+ UnaryExpression<out e0>
+ { MulOp<out x, out op>
+ UnaryExpression<out e1> (. e0 = new BinaryExpr(x, op, e0, e1); .)
+ }
+ .
+
+MulOp<out Token! x, out BinaryExpr.Opcode op>
+= (. x = Token.NoToken; op = BinaryExpr.Opcode.Add/*(dummy)*/; .)
+ ( "*" (. x = token; op = BinaryExpr.Opcode.Mul; .)
+ | "/" (. x = token; op = BinaryExpr.Opcode.Div; .)
+ | "%" (. x = token; op = BinaryExpr.Opcode.Mod; .)
+ )
+ .
+
+/*------------------------------------------------------------------------*/
+UnaryExpression<out Expression! e>
+= (. Token! x; e = dummyExpr; .)
+ ( "-" (. x = token; .)
+ UnaryExpression<out e> (. e = new BinaryExpr(x, BinaryExpr.Opcode.Sub, new LiteralExpr(x, 0), e); .)
+ | NegOp (. x = token; .)
+ UnaryExpression<out e> (. e = new UnaryExpr(x, UnaryExpr.Opcode.Not, e); .)
+ | SelectExpression<out e>
+ | ConstAtomExpression<out e>
+ )
+ .
+
+NegOp = "!" | '\u00ac'.
+
+ConstAtomExpression<out Expression! e>
+= (. Token! x; int n; List<Expression!>! elements;
+ e = dummyExpr;
+ .)
+ ( "false" (. e = new LiteralExpr(token, false); .)
+ | "true" (. e = new LiteralExpr(token, true); .)
+ | "null" (. e = new LiteralExpr(token); .)
+ | Nat<out n> (. e = new LiteralExpr(token, n); .)
+ | "fresh" (. x = token; .)
+ "(" Expression<out e> ")" (. e = new FreshExpr(x, e); .)
+ | "|" (. x = token; .)
+ Expression<out e> (. e = new UnaryExpr(x, UnaryExpr.Opcode.SeqLength, e); .)
+ "|"
+ /* Note, the following open-curly-brace causes grammar ambiguities that result in two Coco warnings.
+ One of these is the confusion between BlockStmt and AssignStmt, the former starting with an open
+ curly brace and the latter starting with a left-hand side Expression, which syntactically can
+ start with an open curly brace. The other is the confusion between a quantifier's triggers/attributes
+ and the quantifier's body. The disamiguation I've chosen involves giving priority to BlockStmt
+ (since no semantically legal AssignStmt can have a set constructor as its left-hand side) and to
+ triggers/attributes (which, unfortunately, changes what programs are syntactically allowed, but there
+ is a simple workaround, which is for a user to put parentheses around any quantifier body that begins
+ with a set constructor.
+ */
+ | "{" (. x = token; elements = new List<Expression!>(); .)
+ [ Expressions<elements> ] (. e = new SetDisplayExpr(x, elements); .)
+ "}"
+ | "[" (. x = token; elements = new List<Expression!>(); .)
+ [ Expressions<elements> ] (. e = new SeqDisplayExpr(x, elements); .)
+ "]"
+ )
+ .
+
+/*------------------------------------------------------------------------*/
+
+/* returns one of:
+ -- IdentifierExpr
+ -- FunctionCallExpr
+ -- FieldSelectExpr
+*/
+CallStmtSubExpr<out Expression! e>
+= (. e = dummyExpr; .)
+ ( IdentOrFuncExpression<out e>
+ | ObjectExpression<out e>
+ SelectOrCallSuffix<ref e>
+ )
+ { SelectOrCallSuffix<ref e> }
+ .
+
+SelectExpression<out Expression! e>
+= (. Token! id; e = dummyExpr; .)
+ ( IdentOrFuncExpression<out e>
+ | ObjectExpression<out e>
+ )
+ { SelectOrCallSuffix<ref e> }
+ .
+
+IdentOrFuncExpression<out Expression! e>
+= (. Token! id; e = dummyExpr; List<Expression!>! args; .)
+ Ident<out id>
+ [ "(" (. args = new List<Expression!>(); .)
+ [ Expressions<args> ]
+ ")" (. e = new FunctionCallExpr(id, id.val, new ImplicitThisExpr(id), args); .)
+ ] (. if (e == dummyExpr) {
+ if (parseVarScope.Find(id.val) != null) {
+ e = new IdentifierExpr(id, id.val);
+ } else {
+ e = new FieldSelectExpr(id, new ImplicitThisExpr(id), id.val);
+ }
+ }
+ .)
+ .
+
+SelectOrCallSuffix<ref Expression! e>
+= (. Token! id, x; List<Expression!>! args;
+ Expression e0 = null; Expression e1 = null; Expression! ee; bool anyDots = false;
+ bool func = false;
+ .)
+ ( "."
+ Ident<out id>
+ [ "(" (. args = new List<Expression!>(); func = true; .)
+ [ Expressions<args> ]
+ ")" (. e = new FunctionCallExpr(id, id.val, e, args); .)
+ ] (. if (!func) { e = new FieldSelectExpr(id, e, id.val); } .)
+
+ | "[" (. x = token; .)
+ ( Expression<out ee> (. e0 = ee; .)
+ [ ".." (. anyDots = true; .)
+ [ Expression<out ee> (. e1 = ee; .)
+ ]
+ ]
+ | ".." Expression<out ee> (. anyDots = true; e1 = ee; .)
+ ) (. if (!anyDots) {
+ assert e1 == null;
+ e = new SeqSelectExpr(x, true, e, e0, null);
+ } else {
+ assert e0 != null || e1 != null;
+ e = new SeqSelectExpr(x, false, e, e0, e1);
+ }
+ .)
+ "]"
+ )
+ .
+
+/* ObjectExpression represents those expressions E that could possibly be used in E.f
+ or E(...), except Ident. Since the lookahead is just 1, quantifier expressions are also
+ parsed here. The expression returned is never an lvalue.
+*/
+ObjectExpression<out Expression! e>
+= (. Token! x; e = dummyExpr; .)
+ ( "this" (. e = new ThisExpr(token); .)
+ | "old" (. x = token; .)
+ "("
+ Expression<out e>
+ ")" (. e = new OldExpr(x, e); .)
+ | "(" ( QuantifierGuts<out e>
+ | Expression<out e>
+ )
+ ")"
+ )
+ .
+
+/*------------------------------------------------------------------------*/
+
+QuantifierGuts<out Expression! q>
+= (. Token! x = Token.NoToken;
+ bool univ = false;
+ BoundVar! bv;
+ List<BoundVar!> bvars = new List<BoundVar!>();
+ Token! tok; Expr! e; ExprSeq! es;
+ Attributes attrs = null;
+ Triggers trigs = null;
+ Expression! body;
+ .)
+ ( Forall (. x = token; univ = true; .)
+ | Exists (. x = token; .)
+ )
+ (. parseVarScope.PushMarker(); .)
+ IdentTypeOptional<out bv> (. bvars.Add(bv); parseVarScope.Push(bv.Name, bv.Name); .)
+ { ","
+ IdentTypeOptional<out bv> (. bvars.Add(bv); parseVarScope.Push(bv.Name, bv.Name); .)
+ }
+ QSep
+ /* The grammar is ambiguous in how to resolve triggers/attributes versus the expression body.
+ However, the loop generated by Coco for the next two lines of grammar declarations is still
+ fine--it will loop, picking up as many triggers/attributes it can before going on to parse an
+ expression. This seems good, because there's a simple workaround for quantifier bodies that
+ begin with a set constructor: simply put parentheses around the quantifier body. See also
+ the Note in production ConstAtomExpression.
+ */
+ { AttributeOrTrigger<ref attrs, ref trigs> }
+ Expression<out body>
+ (. if (univ) {
+ q = new ForallExpr(x, bvars, body, trigs, attrs);
+ } else {
+ q = new ExistsExpr(x, bvars, body, trigs, attrs);
+ }
+ parseVarScope.PopMarker();
+ .)
+ .
+
+Forall = "forall" | '\u2200'.
+Exists = "exists" | '\u2203'.
+QSep = "::" | '\u2022'.
+
+Expressions<List<Expression!\>! args>
+= (. Expression! e; .)
+ Expression<out e> (. args.Add(e); .)
+ { "," Expression<out e> (. args.Add(e); .)
+ }
+ .
+
+/*------------------------------------------------------------------------*/
+
+Attribute<ref Attributes attrs>
+= "{"
+ AttributeBody<ref attrs>
+ "}"
+ .
+
+AttributeBody<ref Attributes attrs>
+= (. string aName;
+ List<Attributes.Argument!> aArgs = new List<Attributes.Argument!>();
+ Attributes.Argument! aArg;
+ .)
+ ":" ident (. aName = token.val; .)
+ [ AttributeArg<out aArg> (. aArgs.Add(aArg); .)
+ { "," AttributeArg<out aArg> (. aArgs.Add(aArg); .)
+ }
+ ] (. attrs = new Attributes(aName, aArgs, attrs); .)
+ .
+
+AttributeArg<out Attributes.Argument! arg>
+= (. Expression! e; arg = dummyAttrArg; .)
+ ( string (. arg = new Attributes.Argument(token.val.Substring(1, token.val.Length-2)); .)
+ | Expression<out e> (. arg = new Attributes.Argument(e); .)
+ )
+ .
+
+AttributeOrTrigger<ref Attributes attrs, ref Triggers trigs>
+= (. List<Expression!> es = new List<Expression!>();
+ .)
+ "{"
+ ( AttributeBody<ref attrs>
+ | (. es = new List<Expression!>(); .)
+ Expressions<es> (. trigs = new Triggers(es, trigs); .)
+ )
+ "}"
+ .
+
+/*------------------------------------------------------------------------*/
+
+Ident<out Token! x>
+=
+ ident (. x = token; .)
+ .
+
+Nat<out int n>
+=
+ digits
+ (. try {
+ n = System.Convert.ToInt32(token.val);
+ } catch (System.FormatException) {
+ SemErr("incorrectly formatted number");
+ n = 0;
+ }
+ .)
+ .
+
+END Dafny.
diff --git a/Source/Dafny/DafnyAst.ssc b/Source/Dafny/DafnyAst.ssc
new file mode 100644
index 00000000..8f6821dd
--- /dev/null
+++ b/Source/Dafny/DafnyAst.ssc
@@ -0,0 +1,1049 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using Microsoft.Contracts;
+
+namespace Microsoft.Dafny
+{
+ public class Program {
+ public readonly string! Name;
+ public readonly List<ClassDecl!>! Classes;
+ public Program(string! name, [Captured] List<ClassDecl!>! classes) {
+ Name = name;
+ Classes = classes;
+ }
+ }
+
+ public class Attributes {
+ public readonly string! Name;
+ /*Frozen*/ public readonly List<Argument!>! Args;
+ public readonly Attributes Prev;
+
+ public Attributes(string! name, [Captured] List<Argument!>! args, Attributes prev)
+ {
+ Name = name;
+ Args = args;
+ Prev = prev;
+ }
+
+ public class Argument {
+ public readonly string S;
+ public readonly Expression E;
+ invariant (S == null) != (E == null);
+
+ public Argument(string! s) {
+ S = s;
+ }
+ public Argument(Expression! e) {
+ E = e;
+ }
+ }
+ }
+
+ // ------------------------------------------------------------------------------------------------------
+
+ public abstract class Type {
+ public static readonly BoolType! Bool = new BoolType();
+ public static readonly IntType! Int = new IntType();
+ /// <summary>
+ /// Used in error situations in order to reduce further error messages.
+ /// </summary>
+ [Pure(false)]
+ public static Type! Flexible {
+ get { return new InferredTypeProxy(); }
+ }
+
+ public bool IsRefType {
+ get {
+ if (this is ObjectType) {
+ return true;
+ } else {
+ ClassType ct = this as ClassType;
+ return ct != null && ct.ResolvedParam == null;
+ }
+ }
+ }
+ public bool IsTypeParameter {
+ get
+ ensures result ==> this is ClassType && ((ClassType)this).ResolvedParam != null;
+ {
+ ClassType ct = this as ClassType;
+ return ct != null && ct.ResolvedParam != null;
+ }
+ }
+ }
+
+ public abstract class BasicType : Type {
+ }
+
+ public class BoolType : BasicType {
+ [Pure] public override string! ToString() {
+ return "bool";
+ }
+ }
+
+ public class IntType : BasicType {
+ [Pure] public override string! ToString() {
+ return "int";
+ }
+ }
+
+ public class ObjectType : BasicType {
+ [Pure] public override string! ToString() {
+ return "object";
+ }
+ }
+
+ public abstract class CollectionType : Type {
+ public readonly Type! Arg;
+ public CollectionType(Type! arg) {
+ this.Arg = arg;
+ }
+ }
+
+ public class SetType : CollectionType {
+ public SetType(Type! arg) {
+ base(arg);
+ }
+ [Pure] public override string! ToString() {
+ assume Arg.IsPeerConsistent;
+ return "set<" + Arg + ">";
+ }
+ }
+
+ public class SeqType : CollectionType {
+ public SeqType(Type! arg) {
+ base(arg);
+ }
+ [Pure] public override string! ToString() {
+ assume Arg.IsPeerConsistent;
+ return "seq<" + Arg + ">";
+ }
+ }
+
+ public class ClassType : Type {
+ public readonly Token! tok;
+ public readonly string! Name;
+ [Rep] public readonly List<Type!>! TypeArgs;
+
+ public ClassDecl ResolvedClass; // filled in by resolution, if Name denotes a class and TypeArgs match the type parameters of that class
+ public TypeParameter ResolvedParam; // filled in by resolution, if Name denotes an enclosing type parameter and TypeArgs is the empty list
+
+ public ClassType(Token! tok, string! name, [Captured] List<Type!>! typeArgs) {
+ this.tok = tok;
+ this.Name = name;
+ this.TypeArgs = typeArgs;
+ }
+
+ /// <summary>
+ /// This constructor constructs a resolved class type
+ /// </summary>
+ public ClassType(Token! tok, string! name, ClassDecl! cd, [Captured] List<Type!>! typeArgs) {
+ this.tok = tok;
+ this.Name = name;
+ this.TypeArgs = typeArgs;
+ this.ResolvedClass = cd;
+ }
+
+ /// <summary>
+ /// This constructor constructs a resolved type parameter
+ /// </summary>
+ public ClassType(Token! tok, string! name, TypeParameter! tp) {
+ this.tok = tok;
+ this.Name = name;
+ this.TypeArgs = new List<Type!>();
+ this.ResolvedParam = tp;
+ }
+
+ /// <summary>
+ /// If type denotes a resolved class type, then return that class type.
+ /// Otherwise, return null.
+ /// </summary>
+ public static ClassType DenotesClass(Type! type)
+ ensures result != null ==> result.ResolvedClass != null;
+ {
+ while (true)
+ invariant type.IsPeerConsistent;
+ {
+ TypeProxy pt = type as TypeProxy;
+ if (pt != null && pt.T != null) {
+ type = pt.T;
+ assume type.IsPeerConsistent;
+ } else {
+ break;
+ }
+ }
+ ClassType ct = type as ClassType;
+ if (ct != null && ct.ResolvedClass != null) {
+ return ct;
+ } else {
+ return null;
+ }
+ }
+
+ [Pure] public override string! ToString() {
+ string s = Name;
+ if (TypeArgs.Count != 0) {
+ string sep = "<";
+ foreach (Type t in TypeArgs) {
+ assume t.IsPeerConsistent;
+ s += sep + t;
+ sep = ",";
+ }
+ s += ">";
+ }
+ return s;
+ }
+ }
+
+ public abstract class TypeProxy : Type {
+ public Type T; // filled in during resolution
+ internal TypeProxy() { }
+
+ [Pure] public override string! ToString() {
+ assume T == null || T.IsPeerConsistent;
+ return T == null ? "?" : T.ToString();
+ }
+ }
+
+ public abstract class UnrestrictedTypeProxy : TypeProxy { }
+
+ /// <summary>
+ /// This proxy stands for any type.
+ /// </summary>
+ public class InferredTypeProxy : UnrestrictedTypeProxy {
+ }
+
+ /// <summary>
+ /// This proxy stands for any type, but it originates from an instantiated type parameter.
+ /// </summary>
+ public class ParamTypeProxy : UnrestrictedTypeProxy {
+ TypeParameter! orig;
+ public ParamTypeProxy(TypeParameter! orig) {
+ this.orig = orig;
+ }
+ }
+
+ public abstract class RestrictedTypeProxy : TypeProxy {
+ /// <summary>
+ /// The OrderID is used to simplify the unification code. Each restricted type proxy should use its
+ /// own OrderID.
+ /// </summary>
+ public abstract int OrderID { get; }
+ }
+
+ /// <summary>
+ /// This proxy stands for object or any class type.
+ /// </summary>
+ public class ObjectTypeProxy : RestrictedTypeProxy {
+ public override int OrderID { get { return 0; } }
+ }
+
+ /// <summary>
+ /// This proxy stands for object or any class type or a set or sequence of object or a class type.
+ /// </summary>
+ public class ObjectsTypeProxy : RestrictedTypeProxy {
+ public override int OrderID { get { return 1; } }
+ }
+
+ /// <summary>
+ /// This proxy stands for either:
+ /// set(Arg) or seq(Arg)
+ /// </summary>
+ public class CollectionTypeProxy : RestrictedTypeProxy {
+ public readonly Type! Arg;
+ public CollectionTypeProxy(Type! arg) {
+ Arg = arg;
+ }
+ public override int OrderID { get { return 2; } }
+ }
+
+ /// <summary>
+ /// This proxy stands for either:
+ /// int or set or seq
+ /// if AllowSeq, or:
+ /// int or set
+ /// if !AllowSeq.
+ /// </summary>
+ public class OperationTypeProxy : RestrictedTypeProxy {
+ public readonly bool AllowSeq;
+ public OperationTypeProxy(bool allowSeq) {
+ AllowSeq = allowSeq;
+ }
+ public override int OrderID { get { return 3; } }
+ }
+
+ // ------------------------------------------------------------------------------------------------------
+
+ public abstract class Declaration {
+ public Token! tok;
+ public readonly string! Name;
+ public readonly Attributes Attributes;
+
+ public Declaration(Token! tok, string! name, Attributes attributes) {
+ this.tok = tok;
+ this.Name = name;
+ this.Attributes = attributes;
+ }
+ }
+
+ public class TypeParameter : Declaration {
+ public interface ParentType { }
+ [Peer] ParentType parent;
+ public ParentType Parent {
+ get {
+ return parent;
+ }
+ [param: Captured]
+ set
+ requires Parent == null; // set it only once
+ requires value != null;
+ // BUGBUG: The following line is a workaround to tell the verifier that 'value' is not of an Immutable type.
+ // A proper solution would be to be able to express that in the program (in a specification or attribute) or
+ // to be able to declare 'parent' as [PeerOrImmutable].
+ requires value is ClassDecl || value is Function || value is Method;
+ modifies parent;
+ {
+ parent = value;
+ }
+ }
+ public TypeParameter(Token! tok, string! name) {
+ base(tok, name, null);
+ }
+ }
+
+ public class ClassDecl : Declaration, TypeParameter.ParentType {
+ public List<TypeParameter!>! TypeArgs;
+ public List<MemberDecl!>! Members;
+
+ public ClassDecl(Token! tok, string! name, List<TypeParameter!>! typeArgs, [Captured] List<MemberDecl!>! members, Attributes attributes) {
+ TypeArgs = typeArgs;
+ Members = members;
+ base(tok, name, attributes);
+ }
+ }
+
+ public abstract class MemberDecl : Declaration {
+ public ClassDecl EnclosingClass; // filled in during resolution
+
+ public MemberDecl(Token! tok, string! name, Attributes attributes) {
+ base(tok, name, attributes);
+ }
+ /// <summary>
+ /// Returns className+"."+memberName. Available only after resolution.
+ /// </summary>
+ public string! FullName {
+ get
+ requires EnclosingClass != null;
+ {
+ return EnclosingClass.Name + "." + Name;
+ }
+ }
+ }
+
+ public class Field : MemberDecl {
+ public readonly Type! Type;
+
+ public Field(Token! tok, string! name, Type! type, Attributes attributes) {
+ Type = type;
+ base(tok, name, attributes);
+ }
+ }
+
+ public interface IVariable {
+ string! Name { get; }
+ string! UniqueName { get; }
+ Type! Type { get; }
+ bool IsMutable { get; }
+ }
+
+ public abstract class NonglobalVariable : IVariable {
+ public readonly Token! tok;
+ readonly string! name;
+ public string! Name { get { return name; } }
+ readonly int varId = varIdCount++;
+ public string! UniqueName { get { return name + "#" + varId; } }
+ Type! type;
+ [Pure(false)] // TODO: if Type gets the status of [Frozen], then this attribute is not needed
+ public Type! Type { get {
+ assume type.IsPeerConsistent;
+ while (true)
+ invariant type.IsPeerConsistent;
+ {
+ TypeProxy t = type as TypeProxy;
+ if (t != null && t.T != null) {
+ type = t.T;
+ assume type.IsPeerConsistent;
+ } else {
+ return type;
+ }
+ }
+ } }
+ public abstract bool IsMutable { get; }
+
+ public NonglobalVariable(Token! tok, string! name, Type! type) {
+ this.tok = tok;
+ this.name = name;
+ this.type = type;
+ }
+
+ internal static int varIdCount; // this varIdCount is used for both NonglobalVariable's and VarDecl's.
+ }
+
+ public class Formal : NonglobalVariable {
+ public readonly bool InParam; // true to in-parameter, false for out-parameter
+ public override bool IsMutable { get { return !InParam; } }
+
+ public Formal(Token! tok, string! name, Type! type, bool inParam) {
+ InParam = inParam;
+ base(tok, name, type);
+ }
+ }
+
+ public class BoundVar : NonglobalVariable {
+ public override bool IsMutable { get { return false; } }
+
+ public BoundVar(Token! tok, string! name, Type! type) {
+ base(tok, name, type);
+ }
+ }
+
+ public class Function : MemberDecl, TypeParameter.ParentType {
+ public readonly bool Use;
+ public readonly List<TypeParameter!>! TypeArgs;
+ public readonly List<Formal!>! Formals;
+ public readonly Type! ResultType;
+ public readonly List<Expression!>! Req;
+ public readonly List<Expression!>! Reads;
+ public readonly Expression Body; // an extended expression
+
+ public Function(Token! tok, string! name, bool use, [Captured] List<TypeParameter!>! typeArgs, [Captured] List<Formal!>! formals, Type! resultType,
+ List<Expression!>! req, List<Expression!>! reads, Expression body, Attributes attributes) {
+ this.Use = use;
+ this.TypeArgs = typeArgs;
+ this.Formals = formals;
+ this.ResultType = resultType;
+ this.Req = req;
+ this.Reads = reads;
+ this.Body = body;
+ base(tok, name, attributes);
+ }
+ }
+
+ public class Method : MemberDecl, TypeParameter.ParentType {
+ public readonly List<TypeParameter!>! TypeArgs;
+ public readonly List<Formal!>! Ins;
+ public readonly List<Formal!>! Outs;
+ public readonly List<MaybeFreeExpression!>! Req;
+ public readonly List<Expression!>! Mod;
+ public readonly List<MaybeFreeExpression!>! Ens;
+ public readonly Statement Body;
+
+ public Method(Token! tok, string! name,
+ [Captured] List<TypeParameter!>! typeArgs,
+ [Captured] List<Formal!>! ins, [Captured] List<Formal!>! outs,
+ [Captured] List<MaybeFreeExpression!>! req, [Captured] List<Expression!>! mod, [Captured] List<MaybeFreeExpression!>! ens,
+ [Captured] Statement body,
+ Attributes attributes) {
+ this.TypeArgs = typeArgs;
+ this.Ins = ins;
+ this.Outs = outs;
+ this.Req = req;
+ this.Mod = mod;
+ this.Ens = ens;
+ this.Body = body;
+ base(tok, name, attributes);
+ }
+ }
+
+ // ------------------------------------------------------------------------------------------------------
+
+ public abstract class Statement {
+ public readonly Token! Tok;
+ public Statement(Token! tok) {
+ this.Tok = tok;
+ }
+ }
+
+ public abstract class PredicateStmt : Statement {
+ [Peer] public readonly Expression! Expr;
+ [Captured]
+ public PredicateStmt(Token! tok, Expression! expr)
+ ensures Owner.Same(this, expr);
+ {
+ base(tok);
+ Owner.AssignSame(this, expr);
+ this.Expr = expr;
+ }
+ }
+
+ public class AssertStmt : PredicateStmt {
+ [Captured]
+ public AssertStmt(Token! tok, Expression! expr)
+ ensures Owner.Same(this, expr);
+ {
+ base(tok, expr);
+ }
+ }
+
+ public class AssumeStmt : PredicateStmt {
+ [Captured]
+ public AssumeStmt(Token! tok, Expression! expr)
+ ensures Owner.Same(this, expr);
+ {
+ base(tok, expr);
+ }
+ }
+
+ public class UseStmt : PredicateStmt {
+ [Captured]
+ public UseStmt(Token! tok, Expression! expr)
+ ensures Owner.Same(this, expr);
+ {
+ base(tok, expr);
+ }
+ [Peer] private FunctionCallExpr fce;
+ /// <summary>
+ /// This method assumes the statement has been successfully resolved.
+ /// </summary>
+ [Pure(false)]
+ public FunctionCallExpr! FunctionCallExpr {
+ get {
+ if (fce == null) {
+ Expression expr = Expr;
+ while (true)
+ invariant Owner.Same(this, expr);
+ {
+ if (expr is OldExpr) {
+ expr = ((OldExpr)expr).E;
+ } else {
+ break;
+ }
+ }
+ assume expr is FunctionCallExpr;
+ fce = (FunctionCallExpr)expr;
+ }
+ return fce;
+ }
+ }
+ public bool EvalInOld {
+ get {
+ return Expr is OldExpr;
+ }
+ }
+ }
+
+ public class LabelStmt : Statement {
+ public readonly string! Label;
+ public LabelStmt(Token! tok, string! label) {
+ this.Label = label;
+ base(tok);
+ }
+ }
+
+ public class BreakStmt : Statement {
+ public readonly string TargetLabel;
+ public Statement TargetStmt; // filled in during resolution
+
+ public BreakStmt(Token! tok, string targetLabel) {
+ this.TargetLabel = targetLabel;
+ base(tok);
+ }
+ }
+
+ public class ReturnStmt : Statement {
+ public ReturnStmt(Token! tok) {
+ base(tok);
+ }
+ }
+
+ public abstract class AssignmentRhs {
+ internal AssignmentRhs() { }
+ }
+
+ public abstract class DeterminedAssignmentRhs : AssignmentRhs {
+ internal DeterminedAssignmentRhs() { }
+ }
+
+ public class ExprRhs : DeterminedAssignmentRhs {
+ public readonly Expression! Expr;
+ public ExprRhs(Expression! expr) {
+ Expr = expr;
+ }
+ }
+
+ public class TypeRhs : DeterminedAssignmentRhs {
+ public readonly Type! Type;
+ public TypeRhs(Type! type) {
+ Type = type;
+ }
+ }
+
+ public class HavocRhs : AssignmentRhs {
+ }
+
+ public class AssignStmt : Statement {
+ public readonly Expression! Lhs;
+ public readonly AssignmentRhs! Rhs;
+ public AssignStmt(Token! tok, Expression! lhs, Expression! rhs) { // ordinary assignment statement
+ this.Lhs = lhs;
+ this.Rhs = new ExprRhs(rhs);
+ base(tok);
+ }
+ public AssignStmt(Token! tok, Expression! lhs, Type! type) { // alloc statement
+ this.Lhs = lhs;
+ this.Rhs = new TypeRhs(type);
+ base(tok);
+ }
+ public AssignStmt(Token! tok, Expression! lhs) { // havoc
+ this.Lhs = lhs;
+ this.Rhs = new HavocRhs();
+ base(tok);
+ }
+ }
+
+ public class VarDecl : Statement, IVariable {
+ readonly string! name;
+ public string! Name { get { return name; } }
+ readonly int varId = NonglobalVariable.varIdCount++;
+ public string! UniqueName { get { return name + "#" + varId; } }
+ public readonly Type OptionalType; // this is the type mentioned in the declaration, if any
+ internal Type type; // this is the declared or inferred type of the variable; it is non-null after resolution (even if resolution fails)
+ [Pure(false)]
+ public Type! Type { get {
+ assume type != null; /* we assume object has been resolved */
+ assume type.IsPeerConsistent;
+ while (true)
+ invariant type != null && type.IsPeerConsistent;
+ {
+ TypeProxy t = type as TypeProxy;
+ if (t != null && t.T != null) {
+ type = t.T;
+ assume type.IsPeerConsistent;
+ } else {
+ return type;
+ }
+ }
+ } }
+ public bool IsMutable { get { return true; } }
+
+ public readonly DeterminedAssignmentRhs Rhs;
+ invariant OptionalType != null || Rhs != null;
+
+ public VarDecl(Token! tok, string! name, Type type, DeterminedAssignmentRhs rhs)
+ requires type != null || rhs != null;
+ {
+ this.name = name;
+ this.OptionalType = type;
+ this.Rhs = rhs;
+ base(tok);
+ }
+ }
+
+ public class CallStmt : Statement {
+ public readonly List<IdentifierExpr!>! Lhs;
+ public readonly Expression! Receiver;
+ public readonly string! MethodName;
+ public readonly List<Expression!>! Args;
+ public Method Method; // filled in by resolution
+
+ public CallStmt(Token! tok, List<IdentifierExpr!>! lhs, Expression! receiver, string! methodName, List<Expression!>! args) {
+ this.Lhs = lhs;
+ this.Receiver = receiver;
+ this.MethodName = methodName;
+ this.Args = args;
+ base(tok);
+ }
+ }
+
+ public class BlockStmt : Statement {
+ public readonly List<Statement!>! Body;
+ public BlockStmt(Token! tok, [Captured] List<Statement!>! body) {
+ this.Body = body;
+ base(tok);
+ }
+ }
+
+ public class IfStmt : Statement {
+ public readonly Expression Guard;
+ public readonly Statement! Thn;
+ public readonly Statement Els;
+ invariant Els == null || Els is BlockStmt || Els is IfStmt;
+
+ public IfStmt(Token! tok, Expression guard, Statement! thn, Statement els)
+ requires els == null || els is BlockStmt || els is IfStmt;
+ {
+ this.Guard = guard;
+ this.Thn = thn;
+ this.Els = els;
+ base(tok);
+ }
+ }
+
+ public class WhileStmt : Statement {
+ public readonly Expression Guard;
+ public readonly List<MaybeFreeExpression!>! Invariants;
+ public readonly List<Expression!>! Decreases;
+ public readonly Statement! Body;
+
+ public WhileStmt(Token! tok, Expression guard,
+ List<MaybeFreeExpression!>! invariants, List<Expression!>! decreases,
+ Statement! body) {
+ this.Guard = guard;
+ this.Invariants = invariants;
+ this.Decreases = decreases;
+ this.Body = body;
+ base(tok);
+ }
+ }
+
+ public class ForeachStmt : Statement {
+ public readonly BoundVar! BoundVar;
+ public readonly Expression! Collection;
+ public readonly Expression! Range;
+ public readonly List<PredicateStmt!>! BodyPrefix;
+ public readonly AssignStmt BodyAssign;
+
+ public ForeachStmt(Token! tok, BoundVar! boundVar, Expression! collection, Expression! range, List<PredicateStmt!>! bodyPrefix, AssignStmt bodyAssign) {
+ this.BoundVar = boundVar;
+ this.Collection = collection;
+ this.Range = range;
+ this.BodyPrefix = bodyPrefix;
+ this.BodyAssign = bodyAssign;
+ base(tok);
+ }
+ }
+
+ // ------------------------------------------------------------------------------------------------------
+
+ public abstract class Expression {
+ public readonly Token! tok;
+ protected Type type;
+ public Type Type { // filled in during resolution
+ [Verify(false)] // TODO: how do we allow Type.get to modify type and still be [Pure]?
+ [Additive] // validity of proper subclasses is not required
+ get
+ ensures type == null ==> result == null; // useful in conjunction with postcondition of constructor
+ {
+ while (true) {
+ TypeProxy t = type as TypeProxy;
+ if (t != null && t.T != null) {
+ type = t.T;
+ } else {
+ assume type == null || type.IsPeerConsistent;
+ return type;
+ }
+ }
+ }
+ [NoDefaultContract] // no particular validity of 'this' is required, except that it not be committed
+ set
+ requires this.IsValid;
+ requires Type == null; // set it only once
+ requires value != null && value.IsPeerConsistent;
+ modifies type;
+ {
+ type = value;
+ while (true) {
+ TypeProxy t = type as TypeProxy;
+ if (t != null && t.T != null) {
+ type = t.T;
+ } else {
+ return;
+ }
+ }
+ }
+ }
+
+ public Expression(Token! tok)
+ ensures type == null; // we would have liked to have written Type==null, but that's not admissible or provable
+ {
+ this.tok = tok;
+ }
+ }
+
+ public class LiteralExpr : Expression {
+ public readonly object Value;
+
+ public static bool IsTrue(Expression! e) {
+ if (e is LiteralExpr) {
+ LiteralExpr le = (LiteralExpr)e;
+ return le.Value is bool && (bool)le.Value;
+ } else {
+ return false;
+ }
+ }
+
+ public LiteralExpr(Token! tok) { // represents the Dafny literal "null"
+ this.Value = null;
+ base(tok);
+ }
+
+ public LiteralExpr(Token! tok, int n)
+ requires 0 <= n;
+ {
+ this.Value = n;
+ base(tok);
+ }
+
+ public LiteralExpr(Token! tok, bool b) {
+ this.Value = b;
+ base(tok);
+ }
+ }
+
+ public class ThisExpr : Expression {
+ public ThisExpr(Token! tok) {
+ base(tok);
+ }
+ }
+
+ public class ImplicitThisExpr : ThisExpr {
+ public ImplicitThisExpr(Token! tok) {
+ base(tok);
+ }
+ }
+
+ public class IdentifierExpr : Expression {
+ public readonly string! Name;
+ public IVariable Var; // filled in by resolution
+
+ public IdentifierExpr(Token! tok, string! name) {
+ Name = name;
+ base(tok);
+ }
+ }
+
+ public abstract class DisplayExpression : Expression {
+ public readonly List<Expression!>! Elements;
+ public DisplayExpression(Token! tok, List<Expression!>! elements) {
+ Elements = elements;
+ base(tok);
+ }
+ }
+
+ public class SetDisplayExpr : DisplayExpression {
+ public SetDisplayExpr(Token! tok, List<Expression!>! elements) {
+ base(tok, elements);
+ }
+ }
+
+ public class SeqDisplayExpr : DisplayExpression {
+ public SeqDisplayExpr(Token! tok, List<Expression!>! elements) {
+ base(tok, elements);
+ }
+ }
+
+ public class FieldSelectExpr : Expression {
+ public readonly Expression! Obj;
+ public readonly string! FieldName;
+ public Field Field; // filled in by resolution
+
+ public FieldSelectExpr(Token! tok, Expression! obj, string! fieldName) {
+ this.Obj = obj;
+ this.FieldName = fieldName;
+ base(tok);
+ }
+ }
+
+ public class SeqSelectExpr : Expression {
+ public readonly bool SelectOne; // false means select a range
+ public readonly Expression! Seq;
+ public readonly Expression E0;
+ public readonly Expression E1;
+ invariant SelectOne ==> E1 == null;
+ invariant E0 != null || E1 != null;
+
+ public SeqSelectExpr(Token! tok, bool selectOne, Expression! seq, Expression e0, Expression e1)
+ requires selectOne ==> e1 == null;
+ requires e0 != null || e1 != null;
+ {
+ SelectOne = selectOne;
+ Seq = seq;
+ E0 = e0;
+ E1 = e1;
+ base(tok);
+ }
+ }
+
+ public class FunctionCallExpr : Expression {
+ public readonly string! Name;
+ [Peer] public readonly Expression! Receiver;
+ [Peer] public readonly List<Expression!>! Args;
+ public Function Function; // filled in by resolution
+
+ [Captured]
+ public FunctionCallExpr(Token! tok, string! fn, Expression! receiver, [Captured] List<Expression!>! args)
+ ensures type == null;
+ ensures Owner.Same(this, receiver);
+ {
+ base(tok);
+ this.Name = fn;
+ Owner.AssignSame(this, receiver);
+ this.Receiver = receiver;
+ this.Args = args;
+ }
+ }
+
+ /// <summary>
+ /// UseExpr's are used only temporarily during translation.
+ /// </summary>
+ public class UseExpr : FunctionCallExpr {
+ [NotDelayed] [Captured]
+ public UseExpr(FunctionCallExpr! fce)
+ requires fce.Function != null && fce.Function.Use;
+ {
+ base(fce.tok, fce.Name, fce.Receiver, new List<Expression!>(fce.Args));
+ this.Function = fce.Function;
+ assume Type.Bool.IsPeerConsistent; // This would follow from BoolType being an Immutable type, or from Type.Bool being [Frozen]
+ this.Type = Type.Bool;
+ }
+ }
+
+ public class OldExpr : Expression {
+ [Peer] public readonly Expression! E;
+ [Captured]
+ public OldExpr(Token! tok, Expression! expr) {
+ base(tok);
+ Owner.AssignSame(this, expr);
+ E = expr;
+ }
+ }
+
+ public class FreshExpr : Expression {
+ public readonly Expression! E;
+ public FreshExpr(Token! tok, Expression! expr) {
+ E = expr;
+ base(tok);
+ }
+ }
+
+ public class UnaryExpr : Expression {
+ public enum Opcode { Not, SeqLength }
+ public readonly Opcode Op;
+ public readonly Expression! E;
+
+ public UnaryExpr(Token! tok, Opcode op, Expression! e) {
+ this.Op = op;
+ this.E = e;
+ base(tok);
+ }
+ }
+
+ public class BinaryExpr : Expression {
+ public enum Opcode { Iff, Imp, And, Or,
+ Eq, Neq, Lt, Le, Ge, Gt,
+ Disjoint, In,
+ Add, Sub, Mul, Div, Mod }
+ public readonly Opcode Op;
+ public enum ResolvedOpcode {
+ // logical operators
+ Iff, Imp, And, Or,
+ // non-collection types
+ EqCommon, NeqCommon,
+ // integers
+ Lt, Le, Ge, Gt, Add, Sub, Mul, Div, Mod,
+ // sets
+ SetEq, SetNeq, ProperSubset, Subset, Superset, ProperSuperset, Disjoint, InSet,
+ Union, Intersection, SetDifference,
+ // sequences
+ SeqEq, SeqNeq, ProperPrefix, Prefix, Concat, InSeq
+ }
+ public ResolvedOpcode ResolvedOp; // filled in by resolution
+
+ public static string! OpcodeString(Opcode op) {
+ switch (op) {
+ case Opcode.Iff: return "<==>";
+ case Opcode.Imp: return "==>";
+ case Opcode.And: return "&&";
+ case Opcode.Or: return "||";
+ case Opcode.Eq: return "==";
+ case Opcode.Lt: return "<";
+ case Opcode.Gt: return ">";
+ case Opcode.Le: return "<=";
+ case Opcode.Ge: return ">=";
+ case Opcode.Neq: return "!=";
+ case Opcode.Disjoint: return "!!";
+ case Opcode.In: return "in";
+ case Opcode.Add: return "+";
+ case Opcode.Sub: return "-";
+ case Opcode.Mul: return "*";
+ case Opcode.Div: return "/";
+ case Opcode.Mod: return "%";
+ default:
+ assert false; // unexpected operator
+ }
+ }
+ public readonly Expression! E0;
+ public readonly Expression! E1;
+
+ public BinaryExpr(Token! tok, Opcode op, Expression! e0, Expression! e1) {
+ this.Op = op;
+ this.E0 = e0;
+ this.E1 = e1;
+ base(tok);
+ }
+ }
+
+ public abstract class QuantifierExpr : Expression {
+ public readonly List<BoundVar!>! BoundVars;
+ public readonly Expression! Body;
+ public readonly Triggers Trigs;
+ public readonly Attributes Attributes;
+
+ public QuantifierExpr(Token! tok, List<BoundVar!>! bvars, Expression! body, Triggers trigs, Attributes attrs) {
+ this.BoundVars = bvars;
+ this.Body = body;
+ this.Trigs = trigs;
+ this.Attributes = attrs;
+ base(tok);
+ }
+ }
+
+ public class Triggers {
+ public readonly List<Expression!>! Terms;
+ public readonly Triggers Prev;
+
+ public Triggers(List<Expression!>! terms, Triggers prev) {
+ this.Terms = terms;
+ this.Prev = prev;
+ }
+ }
+
+ public class ForallExpr : QuantifierExpr {
+ public ForallExpr(Token! tok, List<BoundVar!>! bvars, Expression! body, Triggers trig, Attributes attrs) {
+ base(tok, bvars, body, trig, attrs);
+ }
+ }
+
+ public class ExistsExpr : QuantifierExpr {
+ public ExistsExpr(Token! tok, List<BoundVar!>! bvars, Expression! body, Triggers trig, Attributes attrs) {
+ base(tok, bvars, body, trig, attrs);
+ }
+ }
+
+ public class ITEExpr : Expression { // an ITEExpr is an "extended expression" and is only allowed in certain places
+ public readonly Expression! Test;
+ public readonly Expression! Thn;
+ public readonly Expression! Els;
+
+ public ITEExpr(Token! tok, Expression! test, Expression! thn, Expression! els) {
+ this.Test = test;
+ this.Thn = thn;
+ this.Els = els;
+ base(tok);
+ }
+ }
+
+ public class MaybeFreeExpression {
+ public readonly Expression! E;
+ public readonly bool IsFree;
+ public MaybeFreeExpression(Expression! e, bool isFree) {
+ E = e;
+ IsFree = isFree;
+ }
+ }
+}
diff --git a/Source/Dafny/DafnyMain.ssc b/Source/Dafny/DafnyMain.ssc
new file mode 100644
index 00000000..84e366e2
--- /dev/null
+++ b/Source/Dafny/DafnyMain.ssc
@@ -0,0 +1,73 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.IO;
+using System.Collections.Generic;
+using Microsoft.Contracts;
+using Bpl = Microsoft.Boogie;
+
+namespace Microsoft.Dafny {
+ public class Main {
+ /// <summary>
+ /// Returns null on success, or an error string otherwise.
+ /// </summary>
+ public static string ParseCheck(List<string!>! fileNames, string! programName, out Program program)
+ modifies Bpl.CommandLineOptions.Clo.XmlSink.*;
+ {
+ program = null;
+ Dafny.Errors.count = 0;
+ List<ClassDecl!> classes = new List<ClassDecl!>();
+ foreach (string! dafnyFileName in fileNames){
+ if (Bpl.CommandLineOptions.Clo.XmlSink != null && Bpl.CommandLineOptions.Clo.XmlSink.IsOpen) {
+ Bpl.CommandLineOptions.Clo.XmlSink.WriteFileFragment(dafnyFileName);
+ }
+ if (Bpl.CommandLineOptions.Clo.Trace)
+ {
+ Console.WriteLine("Parsing " + dafnyFileName);
+ }
+
+ int errorCount;
+ try
+ {
+ errorCount = Dafny.Parser.Parse(dafnyFileName, classes);
+ if (errorCount != 0)
+ {
+ return string.Format("{0} parse errors detected in {1}", Dafny.Errors.count, dafnyFileName);
+ }
+ }
+ catch (IOException e)
+ {
+ return string.Format("Error opening file \"{0}\": {1}", dafnyFileName, e.Message);
+ }
+ }
+
+ program = new Program(programName, classes);
+
+ if (Bpl.CommandLineOptions.Clo.DafnyPrintFile != null) {
+ string filename = Bpl.CommandLineOptions.Clo.DafnyPrintFile;
+ if (filename == "-") {
+ Printer pr = new Printer(System.Console.Out);
+ pr.PrintProgram(program);
+ } else {
+ using (TextWriter writer = new System.IO.StreamWriter(filename)) {
+ Printer pr = new Printer(writer);
+ pr.PrintProgram(program);
+ }
+ }
+ }
+
+ if (Bpl.CommandLineOptions.Clo.NoResolve) { return null; }
+
+ Dafny.Resolver r = new Dafny.Resolver();
+ r.ResolveProgram(program);
+ if (r.ErrorCount != 0) {
+ return string.Format("{0} resolution/type errors detected in {1}", r.ErrorCount, programName);
+ }
+
+ return null; // success
+ }
+ }
+}
diff --git a/Source/Dafny/DafnyPipeline.sscproj b/Source/Dafny/DafnyPipeline.sscproj
new file mode 100644
index 00000000..38ff8646
--- /dev/null
+++ b/Source/Dafny/DafnyPipeline.sscproj
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="utf-8"?>
+<VisualStudioProject>
+ <XEN ProjectType="Local"
+ SchemaVersion="1.0"
+ Name="Dafny"
+ ProjectGuid="dead83c6-1510-4af9-8f7d-c837ddbb2632"
+ >
+ <Build>
+ <Settings ApplicationIcon=""
+ AssemblyName="DafnyPipeline"
+ OutputType="Library"
+ RootNamespace="DafnyPipeline"
+ StartupObject=""
+ StandardLibraryLocation=""
+ TargetPlatform="v2"
+ TargetPlatformLocation=""
+ >
+ <Config Name="Debug"
+ AllowUnsafeBlocks="False"
+ BaseAddress="285212672"
+ CheckForOverflowUnderflow="False"
+ ConfigurationOverrideFile=""
+ DefineConstants="DEBUG;TRACE"
+ DocumentationFile=""
+ DebugSymbols="True"
+ FileAlignment="4096"
+ IncrementalBuild="True"
+ Optimize="False"
+ OutputPath="bin\debug"
+ RegisterForComInterop="False"
+ RemoveIntegerChecks="false"
+ TreatWarningsAsErrors="False"
+ WarningLevel="4"
+ CheckContractAdmissibility="True"
+ CheckPurity="False"
+ />
+ <Config Name="Release"
+ AllowUnsafeBlocks="false"
+ BaseAddress="285212672"
+ CheckForOverflowUnderflow="false"
+ ConfigurationOverrideFile=""
+ DefineConstants="TRACE"
+ DocumentationFile=""
+ DebugSymbols="false"
+ FileAlignment="4096"
+ IncrementalBuild="false"
+ Optimize="true"
+ OutputPath="bin\release"
+ RegisterForComInterop="false"
+ RemoveIntegerChecks="false"
+ TreatWarningsAsErrors="false"
+ WarningLevel="4"
+ CheckContractAdmissibility="True"
+ CheckPurity="False"
+ />
+ </Settings>
+ <References>
+ <Reference Name="System"
+ AssemblyName="System"
+ Private="false"
+ />
+ <Reference Name="System.Data"
+ AssemblyName="System.Data"
+ Private="false"
+ />
+ <Reference Name="System.Xml"
+ AssemblyName="System.Xml"
+ Private="false"
+ />
+ <Reference Name="Core"
+ AssemblyName="Core"
+ Private="false"
+ HintPath="../Core/bin/Debug/Core.dll"
+ />
+ </References>
+ </Build>
+ <Files>
+ <Include>
+ <File RelPath="DafnyAst.ssc"
+ SubType="Code"
+ BuildAction="Compile"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="Scanner.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="Parser.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="DafnyMain.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="Printer.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="Resolver.ssc"
+ />
+ <File BuildAction="None"
+ SubType="Content"
+ RelPath="Dafny.atg"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="Translator.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="..\version.ssc"
+ />
+ </Include>
+ </Files>
+ </XEN>
+</VisualStudioProject> \ No newline at end of file
diff --git a/Source/Dafny/Makefile b/Source/Dafny/Makefile
new file mode 100644
index 00000000..fd2141b3
--- /dev/null
+++ b/Source/Dafny/Makefile
@@ -0,0 +1,15 @@
+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
+
+Parser.ssc: Scanner.frame Parser.frame Dafny.atg
+ $(COCO) Dafny.atg
+ copy Parser.cs Parser.ssc
+ copy Scanner.cs Scanner.ssc
+
+clean:
+ rm -f Scanner.ssc Parser.ssc
diff --git a/Source/Dafny/Parser.ssc b/Source/Dafny/Parser.ssc
new file mode 100644
index 00000000..a7c30d2f
--- /dev/null
+++ b/Source/Dafny/Parser.ssc
@@ -0,0 +1,1592 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System.Collections.Generic;
+using Microsoft.Boogie;
+using Microsoft.Contracts;
+
+namespace Microsoft.Dafny {
+
+public class Parser {
+ const int maxT = 86;
+
+ 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 List<ClassDecl!>! theClasses = new List<ClassDecl!>();
+
+
+static Expression! dummyExpr = new LiteralExpr(Token.NoToken);
+static Statement! dummyStmt = new ReturnStmt(Token.NoToken);
+static Attributes.Argument! dummyAttrArg = new Attributes.Argument("dummyAttrArg");
+static Scope<string>! parseVarScope = new Scope<string>();
+
+///<summary>
+/// Parses top level declarations from "filename" and appends them to "classes".
+/// Returns the number of parsing errors encountered.
+/// Note: first initialize the Scanner.
+///</summary>
+public static int Parse (string! filename, List<ClassDecl!>! classes) /* throws System.IO.IOException */ {
+ using (System.IO.StreamReader reader = new System.IO.StreamReader(filename)) {
+ BoogiePL.Buffer.Fill(reader);
+ Scanner.Init(filename);
+ return Parse(classes);
+ }
+}
+
+///<summary>
+/// Parses top level declarations and appends them to "classes".
+/// Returns the number of parsing errors encountered.
+/// Note: first initialize the Scanner.
+///</summary>
+public static int Parse (List<ClassDecl!>! classes) {
+ List<ClassDecl!> oldClasses = theClasses;
+ theClasses = classes;
+ Parse();
+ theClasses = oldClasses;
+ return Errors.count;
+}
+
+/*--------------------------------------------------------------------------*/
+
+
+ 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;
+ }
+
+ public static void SemErr(Token! tok, string! msg) {
+ if (errDist >= minErrDist) Errors.SemErr(tok.filename, tok.line, tok.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 Dafny() {
+ ClassDecl! c;
+ while (t.kind == 4) {
+ ClassDecl(out c);
+ theClasses.Add(c);
+ }
+ Expect(0);
+ }
+
+ static void ClassDecl(out ClassDecl! c) {
+ Token! id;
+ Attributes attrs = null;
+ List<TypeParameter!> typeArgs = new List<TypeParameter!>();
+ List<MemberDecl!> members = new List<MemberDecl!>();
+
+ Expect(4);
+ while (t.kind == 5) {
+ Attribute(ref attrs);
+ }
+ Ident(out id);
+ if (t.kind == 11) {
+ GenericParameters(typeArgs);
+ }
+ Expect(5);
+ while (StartOf(1)) {
+ ClassMemberDecl(members);
+ }
+ Expect(6);
+ c = new ClassDecl(id, id.val, typeArgs, members, attrs);
+ }
+
+ static void Attribute(ref Attributes attrs) {
+ Expect(5);
+ AttributeBody(ref attrs);
+ Expect(6);
+ }
+
+ static void Ident(out Token! x) {
+ Expect(1);
+ x = token;
+ }
+
+ static void GenericParameters(List<TypeParameter!>! typeArgs) {
+ Token! id;
+ Expect(11);
+ Ident(out id);
+ typeArgs.Add(new TypeParameter(id, id.val));
+ while (t.kind == 8) {
+ Get();
+ Ident(out id);
+ typeArgs.Add(new TypeParameter(id, id.val));
+ }
+ Expect(12);
+ }
+
+ static void ClassMemberDecl(List<MemberDecl!>! mm) {
+ Method! m;
+ Function! f;
+
+ if (t.kind == 7) {
+ FieldDecl(mm);
+ } else if (t.kind == 27) {
+ FunctionDecl(out f);
+ mm.Add(f);
+ } else if (t.kind == 14) {
+ MethodDecl(out m);
+ mm.Add(m);
+ } else if (t.kind == 13) {
+ FrameDecl();
+ } else Error(87);
+ }
+
+ static void FieldDecl(List<MemberDecl!>! mm) {
+ Attributes attrs = null;
+ Token! id; Type! ty;
+
+ Expect(7);
+ while (t.kind == 5) {
+ Attribute(ref attrs);
+ }
+ IdentType(out id, out ty);
+ mm.Add(new Field(id, id.val, ty, attrs));
+ while (t.kind == 8) {
+ Get();
+ IdentType(out id, out ty);
+ mm.Add(new Field(id, id.val, ty, attrs));
+ }
+ Expect(9);
+ }
+
+ static void FunctionDecl(out Function! f) {
+ Attributes attrs = null;
+ Token! id;
+ List<TypeParameter!> typeArgs = new List<TypeParameter!>();
+ List<Formal!> formals = new List<Formal!>();
+ Type! returnType;
+ List<Expression!> reqs = new List<Expression!>();
+ List<Expression!> reads = new List<Expression!>();
+ Expression! bb; Expression body = null;
+ bool use = false;
+
+ Expect(27);
+ while (t.kind == 5) {
+ Attribute(ref attrs);
+ }
+ if (t.kind == 28) {
+ Get();
+ use = true;
+ }
+ Ident(out id);
+ if (t.kind == 11) {
+ GenericParameters(typeArgs);
+ }
+ parseVarScope.PushMarker();
+ Formals(true, formals);
+ Expect(10);
+ Type(out returnType);
+ if (t.kind == 9) {
+ Get();
+ while (t.kind == 18 || t.kind == 29) {
+ FunctionSpec(reqs, reads);
+ }
+ } else if (t.kind == 5 || t.kind == 18 || t.kind == 29) {
+ while (t.kind == 18 || t.kind == 29) {
+ FunctionSpec(reqs, reads);
+ }
+ ExtendedExpr(out bb);
+ body = bb;
+ } else Error(88);
+ parseVarScope.PopMarker();
+ f = new Function(id, id.val, use, typeArgs, formals, returnType, reqs, reads, body, attrs);
+
+ }
+
+ static void MethodDecl(out Method! m) {
+ Token! id;
+ Attributes attrs = null;
+ List<TypeParameter!>! typeArgs = new List<TypeParameter!>();
+ List<Formal!> ins = new List<Formal!>();
+ List<Formal!> outs = new List<Formal!>();
+ List<MaybeFreeExpression!> req = new List<MaybeFreeExpression!>();
+ List<Expression!> mod = new List<Expression!>();
+ List<MaybeFreeExpression!> ens = new List<MaybeFreeExpression!>();
+ Statement! bb; Statement body = null;
+
+ Expect(14);
+ while (t.kind == 5) {
+ Attribute(ref attrs);
+ }
+ Ident(out id);
+ if (t.kind == 11) {
+ GenericParameters(typeArgs);
+ }
+ parseVarScope.PushMarker();
+ Formals(true, ins);
+ if (t.kind == 15) {
+ Get();
+ Formals(false, outs);
+ }
+ if (t.kind == 9) {
+ Get();
+ while (StartOf(2)) {
+ MethodSpec(req, mod, ens);
+ }
+ } else if (StartOf(3)) {
+ while (StartOf(2)) {
+ MethodSpec(req, mod, ens);
+ }
+ BlockStmt(out bb);
+ body = bb;
+ } else Error(89);
+ parseVarScope.PopMarker();
+ m = new Method(id, id.val, typeArgs, ins, outs, req, mod, ens, body, attrs);
+
+ }
+
+ static void FrameDecl() {
+ Token! id;
+ Attributes attrs = null;
+
+ Expect(13);
+ while (t.kind == 5) {
+ Attribute(ref attrs);
+ }
+ Ident(out id);
+ Expect(5);
+ Expect(6);
+ }
+
+ static void IdentType(out Token! id, out Type! ty) {
+ Ident(out id);
+ Expect(10);
+ Type(out ty);
+ }
+
+ static void Type(out Type! ty) {
+ ty = new BoolType(); /*keep compiler happy*/
+
+ if (t.kind == 22) {
+ Get();
+
+ } else if (t.kind == 23) {
+ Get();
+ ty = new IntType();
+ } else if (StartOf(4)) {
+ ReferenceType(out ty);
+ } else Error(90);
+ }
+
+ static void IdentTypeOptional(out BoundVar! var) {
+ Token! id; Type! ty; Type optType = null;
+
+ Ident(out id);
+ if (t.kind == 10) {
+ Get();
+ Type(out ty);
+ optType = ty;
+ }
+ var = new BoundVar(id, id.val, optType == null ? new InferredTypeProxy() : optType);
+ }
+
+ static void Formals(bool incoming, List<Formal!>! formals) {
+ Token! id; Type! ty;
+ Expect(20);
+ if (t.kind == 1) {
+ IdentType(out id, out ty);
+ formals.Add(new Formal(id, id.val, ty, incoming)); parseVarScope.Push(id.val, id.val);
+ while (t.kind == 8) {
+ Get();
+ IdentType(out id, out ty);
+ formals.Add(new Formal(id, id.val, ty, incoming)); parseVarScope.Push(id.val, id.val);
+ }
+ }
+ Expect(21);
+ }
+
+ static void MethodSpec(List<MaybeFreeExpression!>! req, List<Expression!>! mod, List<MaybeFreeExpression!>! ens) {
+ Expression! e; bool isFree = false;
+
+ if (t.kind == 16) {
+ Get();
+ if (StartOf(5)) {
+ Expression(out e);
+ mod.Add(e);
+ while (t.kind == 8) {
+ Get();
+ Expression(out e);
+ mod.Add(e);
+ }
+ }
+ Expect(9);
+ } else if (t.kind == 17 || t.kind == 18 || t.kind == 19) {
+ if (t.kind == 17) {
+ Get();
+ isFree = true;
+ }
+ if (t.kind == 18) {
+ Get();
+ Expression(out e);
+ Expect(9);
+ req.Add(new MaybeFreeExpression(e, isFree));
+ } else if (t.kind == 19) {
+ Get();
+ Expression(out e);
+ Expect(9);
+ ens.Add(new MaybeFreeExpression(e, isFree));
+ } else Error(91);
+ } else Error(92);
+ }
+
+ static void BlockStmt(out Statement! block) {
+ Token! x;
+ List<Statement!> body = new List<Statement!>();
+ Statement! s;
+
+ parseVarScope.PushMarker();
+ Expect(5);
+ x = token;
+ while (StartOf(6)) {
+ Stmt(body);
+ }
+ Expect(6);
+ block = new BlockStmt(x, body);
+ parseVarScope.PopMarker();
+ }
+
+ static void Expression(out Expression! e0) {
+ Token! x; Expression! e1;
+ ImpliesExpression(out e0);
+ while (t.kind == 48 || t.kind == 49) {
+ EquivOp();
+ x = token;
+ ImpliesExpression(out e1);
+ e0 = new BinaryExpr(x, BinaryExpr.Opcode.Iff, e0, e1);
+ }
+ }
+
+ static void ReferenceType(out Type! ty) {
+ Token! x;
+ ty = new BoolType(); /*keep compiler happy*/
+ List<Type!>! gt;
+
+ if (t.kind == 24) {
+ Get();
+ ty = new ObjectType();
+ } else if (t.kind == 1) {
+ gt = new List<Type!>();
+ Ident(out x);
+ if (t.kind == 11) {
+ GenericInstantiation(gt);
+ }
+ ty = new ClassType(x, x.val, gt);
+ } else if (t.kind == 25) {
+ gt = new List<Type!>();
+ Get();
+ GenericInstantiation(gt);
+ if (gt.Count != 1) {
+ SemErr("set type expects exactly one type argument");
+ }
+ ty = new SetType(gt[0]);
+
+ } else if (t.kind == 26) {
+ gt = new List<Type!>();
+ Get();
+ GenericInstantiation(gt);
+ if (gt.Count != 1) {
+ SemErr("seq type expects exactly one type argument");
+ }
+ ty = new SeqType(gt[0]);
+
+ } else Error(93);
+ }
+
+ static void GenericInstantiation(List<Type!>! gt) {
+ Type! ty;
+ Expect(11);
+ Type(out ty);
+ gt.Add(ty);
+ while (t.kind == 8) {
+ Get();
+ Type(out ty);
+ gt.Add(ty);
+ }
+ Expect(12);
+ }
+
+ static void FunctionSpec(List<Expression!>! reqs, List<Expression!>! reads) {
+ Expression! e;
+ if (t.kind == 18) {
+ Get();
+ Expression(out e);
+ Expect(9);
+ reqs.Add(e);
+ } else if (t.kind == 29) {
+ ReadsClause(reads);
+ } else Error(94);
+ }
+
+ static void ExtendedExpr(out Expression! e) {
+ e = dummyExpr;
+ Expect(5);
+ if (t.kind == 30) {
+ IfThenElseExpr(out e);
+ } else if (StartOf(5)) {
+ Expression(out e);
+ } else Error(95);
+ Expect(6);
+ }
+
+ static void ReadsClause(List<Expression!>! reads) {
+ Expect(29);
+ if (StartOf(5)) {
+ Expressions(reads);
+ }
+ Expect(9);
+ }
+
+ static void Expressions(List<Expression!>! args) {
+ Expression! e;
+ Expression(out e);
+ args.Add(e);
+ while (t.kind == 8) {
+ Get();
+ Expression(out e);
+ args.Add(e);
+ }
+ }
+
+ static void IfThenElseExpr(out Expression! e) {
+ Token! x; Expression! e0; Expression! e1 = dummyExpr;
+ Expect(30);
+ x = token;
+ Expect(20);
+ Expression(out e);
+ Expect(21);
+ ExtendedExpr(out e0);
+ Expect(31);
+ if (t.kind == 30) {
+ IfThenElseExpr(out e1);
+ } else if (t.kind == 5) {
+ ExtendedExpr(out e1);
+ } else Error(96);
+ e = new ITEExpr(x, e, e0, e1);
+ }
+
+ static void Stmt(List<Statement!>! ss) {
+ Statement! s;
+ while (t.kind == 5) {
+ BlockStmt(out s);
+ ss.Add(s);
+ }
+ if (StartOf(7)) {
+ OneStmt(out s);
+ ss.Add(s);
+ } else if (t.kind == 7) {
+ VarDeclStmts(ss);
+ } else Error(97);
+ }
+
+ static void OneStmt(out Statement! s) {
+ Token! x; Token! id; string label = null;
+ s = dummyStmt; /* to please the compiler */
+
+ switch (t.kind) {
+ case 46: {
+ AssertStmt(out s);
+ break;
+ }
+ case 47: {
+ AssumeStmt(out s);
+ break;
+ }
+ case 28: {
+ UseStmt(out s);
+ break;
+ }
+ case 1: case 2: case 5: case 20: case 45: case 65: case 68: case 69: case 70: case 71: case 72: case 73: case 74: case 78: case 79: {
+ AssignStmt(out s);
+ break;
+ }
+ case 37: {
+ HavocStmt(out s);
+ break;
+ }
+ case 42: {
+ CallStmt(out s);
+ break;
+ }
+ case 30: {
+ IfStmt(out s);
+ break;
+ }
+ case 38: {
+ WhileStmt(out s);
+ break;
+ }
+ case 43: {
+ ForeachStmt(out s);
+ break;
+ }
+ case 32: {
+ Get();
+ x = token;
+ Ident(out id);
+ Expect(10);
+ s = new LabelStmt(x, id.val);
+ break;
+ }
+ case 33: {
+ Get();
+ x = token;
+ if (t.kind == 1) {
+ Ident(out id);
+ label = id.val;
+ }
+ Expect(9);
+ s = new BreakStmt(x, label);
+ break;
+ }
+ case 34: {
+ Get();
+ x = token;
+ Expect(9);
+ s = new ReturnStmt(x);
+ break;
+ }
+ default: Error(98); break;
+ }
+ }
+
+ static void VarDeclStmts(List<Statement!>! ss) {
+ VarDecl! d;
+ Expect(7);
+ IdentTypeRhs(out d);
+ ss.Add(d); parseVarScope.Push(d.Name, d.Name);
+ while (t.kind == 8) {
+ Get();
+ IdentTypeRhs(out d);
+ ss.Add(d); parseVarScope.Push(d.Name, d.Name);
+ }
+ Expect(9);
+ }
+
+ static void AssertStmt(out Statement! s) {
+ Token! x; Expression! e;
+ Expect(46);
+ x = token;
+ Expression(out e);
+ Expect(9);
+ s = new AssertStmt(x, e);
+ }
+
+ static void AssumeStmt(out Statement! s) {
+ Token! x; Expression! e;
+ Expect(47);
+ x = token;
+ Expression(out e);
+ Expect(9);
+ s = new AssumeStmt(x, e);
+ }
+
+ static void UseStmt(out Statement! s) {
+ Token! x; Expression! e;
+ Expect(28);
+ x = token;
+ Expression(out e);
+ Expect(9);
+ s = new UseStmt(x, e);
+ }
+
+ static void AssignStmt(out Statement! s) {
+ Token! x;
+ Expression! lhs;
+ Expression rhs;
+ Type ty;
+ s = dummyStmt;
+
+ LhsExpr(out lhs);
+ Expect(35);
+ x = token;
+ AssignRhs(out rhs, out ty);
+ if (rhs != null) {
+ s = new AssignStmt(x, lhs, rhs);
+ } else {
+ assert ty != null;
+ s = new AssignStmt(x, lhs, ty);
+ }
+
+ Expect(9);
+ }
+
+ static void HavocStmt(out Statement! s) {
+ Token! x; Expression! lhs;
+ Expect(37);
+ x = token;
+ LhsExpr(out lhs);
+ Expect(9);
+ s = new AssignStmt(x, lhs);
+ }
+
+ static void CallStmt(out Statement! s) {
+ Token! x, id;
+ Expression! e;
+ List<IdentifierExpr!> lhs = new List<IdentifierExpr!>();
+
+ Expect(42);
+ x = token;
+ CallStmtSubExpr(out e);
+ if (t.kind == 8 || t.kind == 35) {
+ if (t.kind == 8) {
+ Get();
+ if (e is IdentifierExpr) {
+ lhs.Add((IdentifierExpr)e);
+ } else if (e is FieldSelectExpr) {
+ SemErr(e.tok, "each LHS of call statement must be a variable, not a field");
+ } else {
+ SemErr(e.tok, "each LHS of call statement must be a variable");
+ }
+
+ Ident(out id);
+ lhs.Add(new IdentifierExpr(id, id.val));
+ while (t.kind == 8) {
+ Get();
+ Ident(out id);
+ lhs.Add(new IdentifierExpr(id, id.val));
+ }
+ Expect(35);
+ CallStmtSubExpr(out e);
+ } else {
+ Get();
+ if (e is IdentifierExpr) {
+ lhs.Add((IdentifierExpr)e);
+ } else if (e is FieldSelectExpr) {
+ SemErr(e.tok, "each LHS of call statement must be a variable, not a field");
+ } else {
+ SemErr(e.tok, "each LHS of call statement must be a variable");
+ }
+
+ CallStmtSubExpr(out e);
+ }
+ }
+ Expect(9);
+ if (e is FunctionCallExpr) {
+ FunctionCallExpr fce = (FunctionCallExpr)e;
+ s = new CallStmt(x, lhs, fce.Receiver, fce.Name, fce.Args); // this actually does an ownership transfer of fce.Args
+ } else {
+ SemErr("RHS of call statement must denote a method invocation");
+ s = new CallStmt(x, lhs, dummyExpr, "dummyMethodName", new List<Expression!>());
+ }
+
+ }
+
+ static void IfStmt(out Statement! ifStmt) {
+ Token! x;
+ Expression guard;
+ Statement! thn;
+ Statement! s;
+ Statement els = null;
+
+ Expect(30);
+ x = token;
+ Guard(out guard);
+ BlockStmt(out thn);
+ if (t.kind == 31) {
+ Get();
+ if (t.kind == 30) {
+ IfStmt(out s);
+ els = s;
+ } else if (t.kind == 5) {
+ BlockStmt(out s);
+ els = s;
+ } else Error(99);
+ }
+ ifStmt = new IfStmt(x, guard, thn, els);
+ }
+
+ static void WhileStmt(out Statement! stmt) {
+ Token! x;
+ Expression guard;
+ bool isFree; Expression! e;
+ List<MaybeFreeExpression!> invariants = new List<MaybeFreeExpression!>();
+ List<Expression!> decreases = new List<Expression!>();
+ Statement! body;
+
+ Expect(38);
+ x = token;
+ Guard(out guard);
+ assume guard == null || Owner.None(guard);
+ while (t.kind == 17 || t.kind == 39 || t.kind == 40) {
+ if (t.kind == 17 || t.kind == 39) {
+ isFree = false;
+ if (t.kind == 17) {
+ Get();
+ isFree = true;
+ }
+ Expect(39);
+ Expression(out e);
+ invariants.Add(new MaybeFreeExpression(e, isFree));
+ Expect(9);
+ } else {
+ Get();
+ Expression(out e);
+ decreases.Add(e);
+ while (t.kind == 8) {
+ Get();
+ Expression(out e);
+ decreases.Add(e);
+ }
+ Expect(9);
+ }
+ }
+ BlockStmt(out body);
+ stmt = new WhileStmt(x, guard, invariants, decreases, body);
+ }
+
+ static void ForeachStmt(out Statement! s) {
+ Token! x, boundVar;
+ Type! ty;
+ Expression! collection;
+ Expression! range;
+ List<PredicateStmt!> bodyPrefix = new List<PredicateStmt!>();
+ AssignStmt bodyAssign = null;
+
+ parseVarScope.PushMarker();
+ Expect(43);
+ x = token;
+ range = new LiteralExpr(x, true);
+ ty = new InferredTypeProxy();
+
+ Expect(20);
+ Ident(out boundVar);
+ if (t.kind == 10) {
+ Get();
+ Type(out ty);
+ }
+ Expect(44);
+ Expression(out collection);
+ parseVarScope.Push(boundVar.val, boundVar.val);
+ if (t.kind == 45) {
+ Get();
+ Expression(out range);
+ }
+ Expect(21);
+ Expect(5);
+ while (t.kind == 28 || t.kind == 46 || t.kind == 47) {
+ if (t.kind == 46) {
+ AssertStmt(out s);
+ if (s is PredicateStmt) { bodyPrefix.Add((PredicateStmt)s); }
+ } else if (t.kind == 47) {
+ AssumeStmt(out s);
+ if (s is PredicateStmt) { bodyPrefix.Add((PredicateStmt)s); }
+ } else {
+ UseStmt(out s);
+ if (s is PredicateStmt) { bodyPrefix.Add((PredicateStmt)s); }
+ }
+ }
+ if (StartOf(5)) {
+ AssignStmt(out s);
+ if (s is AssignStmt) { bodyAssign = (AssignStmt)s; }
+ } else if (t.kind == 37) {
+ HavocStmt(out s);
+ if (s is AssignStmt) { bodyAssign = (AssignStmt)s; }
+ } else Error(100);
+ Expect(6);
+ s = new ForeachStmt(x, new BoundVar(boundVar, boundVar.val, ty), collection, range, bodyPrefix, bodyAssign);
+ parseVarScope.PopMarker();
+ }
+
+ static void LhsExpr(out Expression! e) {
+ Expression(out e);
+ }
+
+ static void AssignRhs(out Expression e, out Type ty) {
+ Expression! ee; Type! tt;
+ e = null; ty = null;
+
+ if (t.kind == 36) {
+ Get();
+ ReferenceType(out tt);
+ ty = tt;
+ } else if (StartOf(5)) {
+ Expression(out ee);
+ e = ee;
+ } else Error(101);
+ if (e == null && ty == null) { e = dummyExpr; }
+ }
+
+ static void IdentTypeRhs(out VarDecl! d) {
+ Token! id; Type! ty; Expression! e;
+ Expression rhs = null; Type newType = null;
+ Type optionalType = null; DeterminedAssignmentRhs optionalRhs = null;
+
+ Ident(out id);
+ if (t.kind == 10) {
+ Get();
+ Type(out ty);
+ optionalType = ty;
+ }
+ if (t.kind == 35) {
+ Get();
+ AssignRhs(out rhs, out newType);
+ }
+ if (rhs != null) {
+ assert newType == null;
+ optionalRhs = new ExprRhs(rhs);
+ } else if (newType != null) {
+ optionalRhs = new TypeRhs(newType);
+ } else if (optionalType == null) {
+ optionalType = new InferredTypeProxy();
+ }
+ d = new VarDecl(id, id.val, optionalType, optionalRhs);
+
+ }
+
+ static void Guard(out Expression e) {
+ Expression! ee; e = null;
+ Expect(20);
+ if (t.kind == 41) {
+ Get();
+ e = null;
+ } else if (StartOf(5)) {
+ Expression(out ee);
+ e = ee;
+ } else Error(102);
+ Expect(21);
+ }
+
+ static void CallStmtSubExpr(out Expression! e) {
+ e = dummyExpr;
+ if (t.kind == 1) {
+ IdentOrFuncExpression(out e);
+ } else if (t.kind == 20 || t.kind == 78 || t.kind == 79) {
+ ObjectExpression(out e);
+ SelectOrCallSuffix(ref e);
+ } else Error(103);
+ while (t.kind == 74 || t.kind == 76) {
+ SelectOrCallSuffix(ref e);
+ }
+ }
+
+ static void ImpliesExpression(out Expression! e0) {
+ Token! x; Expression! e1;
+ LogicalExpression(out e0);
+ if (t.kind == 50 || t.kind == 51) {
+ ImpliesOp();
+ x = token;
+ ImpliesExpression(out e1);
+ e0 = new BinaryExpr(x, BinaryExpr.Opcode.Imp, e0, e1);
+ }
+ }
+
+ static void EquivOp() {
+ if (t.kind == 48) {
+ Get();
+ } else if (t.kind == 49) {
+ Get();
+ } else Error(104);
+ }
+
+ static void LogicalExpression(out Expression! e0) {
+ Token! x; Expression! e1;
+ RelationalExpression(out e0);
+ if (StartOf(8)) {
+ if (t.kind == 52 || t.kind == 53) {
+ AndOp();
+ x = token;
+ RelationalExpression(out e1);
+ e0 = new BinaryExpr(x, BinaryExpr.Opcode.And, e0, e1);
+ while (t.kind == 52 || t.kind == 53) {
+ AndOp();
+ x = token;
+ RelationalExpression(out e1);
+ e0 = new BinaryExpr(x, BinaryExpr.Opcode.And, e0, e1);
+ }
+ } else {
+ OrOp();
+ x = token;
+ RelationalExpression(out e1);
+ e0 = new BinaryExpr(x, BinaryExpr.Opcode.Or, e0, e1);
+ while (t.kind == 54 || t.kind == 55) {
+ OrOp();
+ x = token;
+ RelationalExpression(out e1);
+ e0 = new BinaryExpr(x, BinaryExpr.Opcode.Or, e0, e1);
+ }
+ }
+ }
+ }
+
+ static void ImpliesOp() {
+ if (t.kind == 50) {
+ Get();
+ } else if (t.kind == 51) {
+ Get();
+ } else Error(105);
+ }
+
+ static void RelationalExpression(out Expression! e0) {
+ Token! x; Expression! e1; BinaryExpr.Opcode op;
+ Term(out e0);
+ if (StartOf(9)) {
+ RelOp(out x, out op);
+ Term(out e1);
+ e0 = new BinaryExpr(x, op, e0, e1);
+ }
+ }
+
+ static void AndOp() {
+ if (t.kind == 52) {
+ Get();
+ } else if (t.kind == 53) {
+ Get();
+ } else Error(106);
+ }
+
+ static void OrOp() {
+ if (t.kind == 54) {
+ Get();
+ } else if (t.kind == 55) {
+ Get();
+ } else Error(107);
+ }
+
+ static void Term(out Expression! e0) {
+ Token! x; Expression! e1; BinaryExpr.Opcode op;
+ Factor(out e0);
+ while (t.kind == 64 || t.kind == 65) {
+ AddOp(out x, out op);
+ Factor(out e1);
+ e0 = new BinaryExpr(x, op, e0, e1);
+ }
+ }
+
+ static void RelOp(out Token! x, out BinaryExpr.Opcode op) {
+ x = Token.NoToken; op = BinaryExpr.Opcode.Add/*(dummy)*/;
+ switch (t.kind) {
+ case 56: {
+ Get();
+ x = token; op = BinaryExpr.Opcode.Eq;
+ break;
+ }
+ case 11: {
+ Get();
+ x = token; op = BinaryExpr.Opcode.Lt;
+ break;
+ }
+ case 12: {
+ Get();
+ x = token; op = BinaryExpr.Opcode.Gt;
+ break;
+ }
+ case 57: {
+ Get();
+ x = token; op = BinaryExpr.Opcode.Le;
+ break;
+ }
+ case 58: {
+ Get();
+ x = token; op = BinaryExpr.Opcode.Ge;
+ break;
+ }
+ case 59: {
+ Get();
+ x = token; op = BinaryExpr.Opcode.Neq;
+ break;
+ }
+ case 60: {
+ Get();
+ x = token; op = BinaryExpr.Opcode.Disjoint;
+ break;
+ }
+ case 44: {
+ Get();
+ x = token; op = BinaryExpr.Opcode.In;
+ break;
+ }
+ case 61: {
+ Get();
+ x = token; op = BinaryExpr.Opcode.Neq;
+ break;
+ }
+ case 62: {
+ Get();
+ x = token; op = BinaryExpr.Opcode.Le;
+ break;
+ }
+ case 63: {
+ Get();
+ x = token; op = BinaryExpr.Opcode.Ge;
+ break;
+ }
+ default: Error(108); break;
+ }
+ }
+
+ static void Factor(out Expression! e0) {
+ Token! x; Expression! e1; BinaryExpr.Opcode op;
+ UnaryExpression(out e0);
+ while (t.kind == 41 || t.kind == 66 || t.kind == 67) {
+ MulOp(out x, out op);
+ UnaryExpression(out e1);
+ e0 = new BinaryExpr(x, op, e0, e1);
+ }
+ }
+
+ static void AddOp(out Token! x, out BinaryExpr.Opcode op) {
+ x = Token.NoToken; op=BinaryExpr.Opcode.Add/*(dummy)*/;
+ if (t.kind == 64) {
+ Get();
+ x = token; op = BinaryExpr.Opcode.Add;
+ } else if (t.kind == 65) {
+ Get();
+ x = token; op = BinaryExpr.Opcode.Sub;
+ } else Error(109);
+ }
+
+ static void UnaryExpression(out Expression! e) {
+ Token! x; e = dummyExpr;
+ if (t.kind == 65) {
+ Get();
+ x = token;
+ UnaryExpression(out e);
+ e = new BinaryExpr(x, BinaryExpr.Opcode.Sub, new LiteralExpr(x, 0), e);
+ } else if (t.kind == 68 || t.kind == 69) {
+ NegOp();
+ x = token;
+ UnaryExpression(out e);
+ e = new UnaryExpr(x, UnaryExpr.Opcode.Not, e);
+ } else if (StartOf(10)) {
+ SelectExpression(out e);
+ } else if (StartOf(11)) {
+ ConstAtomExpression(out e);
+ } else Error(110);
+ }
+
+ static void MulOp(out Token! x, out BinaryExpr.Opcode op) {
+ x = Token.NoToken; op = BinaryExpr.Opcode.Add/*(dummy)*/;
+ if (t.kind == 41) {
+ Get();
+ x = token; op = BinaryExpr.Opcode.Mul;
+ } else if (t.kind == 66) {
+ Get();
+ x = token; op = BinaryExpr.Opcode.Div;
+ } else if (t.kind == 67) {
+ Get();
+ x = token; op = BinaryExpr.Opcode.Mod;
+ } else Error(111);
+ }
+
+ static void NegOp() {
+ if (t.kind == 68) {
+ Get();
+ } else if (t.kind == 69) {
+ Get();
+ } else Error(112);
+ }
+
+ static void SelectExpression(out Expression! e) {
+ Token! id; e = dummyExpr;
+ if (t.kind == 1) {
+ IdentOrFuncExpression(out e);
+ } else if (t.kind == 20 || t.kind == 78 || t.kind == 79) {
+ ObjectExpression(out e);
+ } else Error(113);
+ while (t.kind == 74 || t.kind == 76) {
+ SelectOrCallSuffix(ref e);
+ }
+ }
+
+ static void ConstAtomExpression(out Expression! e) {
+ Token! x; int n; List<Expression!>! elements;
+ e = dummyExpr;
+
+ switch (t.kind) {
+ case 70: {
+ Get();
+ e = new LiteralExpr(token, false);
+ break;
+ }
+ case 71: {
+ Get();
+ e = new LiteralExpr(token, true);
+ break;
+ }
+ case 72: {
+ Get();
+ e = new LiteralExpr(token);
+ break;
+ }
+ case 2: {
+ Nat(out n);
+ e = new LiteralExpr(token, n);
+ break;
+ }
+ case 73: {
+ Get();
+ x = token;
+ Expect(20);
+ Expression(out e);
+ Expect(21);
+ e = new FreshExpr(x, e);
+ break;
+ }
+ case 45: {
+ Get();
+ x = token;
+ Expression(out e);
+ e = new UnaryExpr(x, UnaryExpr.Opcode.SeqLength, e);
+ Expect(45);
+ break;
+ }
+ case 5: {
+ Get();
+ x = token; elements = new List<Expression!>();
+ if (StartOf(5)) {
+ Expressions(elements);
+ }
+ e = new SetDisplayExpr(x, elements);
+ Expect(6);
+ break;
+ }
+ case 74: {
+ Get();
+ x = token; elements = new List<Expression!>();
+ if (StartOf(5)) {
+ Expressions(elements);
+ }
+ e = new SeqDisplayExpr(x, elements);
+ Expect(75);
+ break;
+ }
+ default: Error(114); break;
+ }
+ }
+
+ static void Nat(out int n) {
+ Expect(2);
+ try {
+ n = System.Convert.ToInt32(token.val);
+ } catch (System.FormatException) {
+ SemErr("incorrectly formatted number");
+ n = 0;
+ }
+
+ }
+
+ static void IdentOrFuncExpression(out Expression! e) {
+ Token! id; e = dummyExpr; List<Expression!>! args;
+ Ident(out id);
+ if (t.kind == 20) {
+ Get();
+ args = new List<Expression!>();
+ if (StartOf(5)) {
+ Expressions(args);
+ }
+ Expect(21);
+ e = new FunctionCallExpr(id, id.val, new ImplicitThisExpr(id), args);
+ }
+ if (e == dummyExpr) {
+ if (parseVarScope.Find(id.val) != null) {
+ e = new IdentifierExpr(id, id.val);
+ } else {
+ e = new FieldSelectExpr(id, new ImplicitThisExpr(id), id.val);
+ }
+ }
+
+ }
+
+ static void ObjectExpression(out Expression! e) {
+ Token! x; e = dummyExpr;
+ if (t.kind == 78) {
+ Get();
+ e = new ThisExpr(token);
+ } else if (t.kind == 79) {
+ Get();
+ x = token;
+ Expect(20);
+ Expression(out e);
+ Expect(21);
+ e = new OldExpr(x, e);
+ } else if (t.kind == 20) {
+ Get();
+ if (StartOf(12)) {
+ QuantifierGuts(out e);
+ } else if (StartOf(5)) {
+ Expression(out e);
+ } else Error(115);
+ Expect(21);
+ } else Error(116);
+ }
+
+ static void SelectOrCallSuffix(ref Expression! e) {
+ Token! id, x; List<Expression!>! args;
+ Expression e0 = null; Expression e1 = null; Expression! ee; bool anyDots = false;
+ bool func = false;
+
+ if (t.kind == 76) {
+ Get();
+ Ident(out id);
+ if (t.kind == 20) {
+ Get();
+ args = new List<Expression!>(); func = true;
+ if (StartOf(5)) {
+ Expressions(args);
+ }
+ Expect(21);
+ e = new FunctionCallExpr(id, id.val, e, args);
+ }
+ if (!func) { e = new FieldSelectExpr(id, e, id.val); }
+ } else if (t.kind == 74) {
+ Get();
+ x = token;
+ if (StartOf(5)) {
+ Expression(out ee);
+ e0 = ee;
+ if (t.kind == 77) {
+ Get();
+ anyDots = true;
+ if (StartOf(5)) {
+ Expression(out ee);
+ e1 = ee;
+ }
+ }
+ } else if (t.kind == 77) {
+ Get();
+ Expression(out ee);
+ anyDots = true; e1 = ee;
+ } else Error(117);
+ if (!anyDots) {
+ assert e1 == null;
+ e = new SeqSelectExpr(x, true, e, e0, null);
+ } else {
+ assert e0 != null || e1 != null;
+ e = new SeqSelectExpr(x, false, e, e0, e1);
+ }
+
+ Expect(75);
+ } else Error(118);
+ }
+
+ static void QuantifierGuts(out Expression! q) {
+ Token! x = Token.NoToken;
+ bool univ = false;
+ BoundVar! bv;
+ List<BoundVar!> bvars = new List<BoundVar!>();
+ Token! tok; Expr! e; ExprSeq! es;
+ Attributes attrs = null;
+ Triggers trigs = null;
+ Expression! body;
+
+ if (t.kind == 80 || t.kind == 81) {
+ Forall();
+ x = token; univ = true;
+ } else if (t.kind == 82 || t.kind == 83) {
+ Exists();
+ x = token;
+ } else Error(119);
+ parseVarScope.PushMarker();
+ IdentTypeOptional(out bv);
+ bvars.Add(bv); parseVarScope.Push(bv.Name, bv.Name);
+ while (t.kind == 8) {
+ Get();
+ IdentTypeOptional(out bv);
+ bvars.Add(bv); parseVarScope.Push(bv.Name, bv.Name);
+ }
+ QSep();
+ while (t.kind == 5) {
+ AttributeOrTrigger(ref attrs, ref trigs);
+ }
+ Expression(out body);
+ if (univ) {
+ q = new ForallExpr(x, bvars, body, trigs, attrs);
+ } else {
+ q = new ExistsExpr(x, bvars, body, trigs, attrs);
+ }
+ parseVarScope.PopMarker();
+
+ }
+
+ static void Forall() {
+ if (t.kind == 80) {
+ Get();
+ } else if (t.kind == 81) {
+ Get();
+ } else Error(120);
+ }
+
+ static void Exists() {
+ if (t.kind == 82) {
+ Get();
+ } else if (t.kind == 83) {
+ Get();
+ } else Error(121);
+ }
+
+ static void QSep() {
+ if (t.kind == 84) {
+ Get();
+ } else if (t.kind == 85) {
+ Get();
+ } else Error(122);
+ }
+
+ static void AttributeOrTrigger(ref Attributes attrs, ref Triggers trigs) {
+ List<Expression!> es = new List<Expression!>();
+
+ Expect(5);
+ if (t.kind == 10) {
+ AttributeBody(ref attrs);
+ } else if (StartOf(5)) {
+ es = new List<Expression!>();
+ Expressions(es);
+ trigs = new Triggers(es, trigs);
+ } else Error(123);
+ Expect(6);
+ }
+
+ static void AttributeBody(ref Attributes attrs) {
+ string aName;
+ List<Attributes.Argument!> aArgs = new List<Attributes.Argument!>();
+ Attributes.Argument! aArg;
+
+ Expect(10);
+ Expect(1);
+ aName = token.val;
+ if (StartOf(13)) {
+ AttributeArg(out aArg);
+ aArgs.Add(aArg);
+ while (t.kind == 8) {
+ Get();
+ AttributeArg(out aArg);
+ aArgs.Add(aArg);
+ }
+ }
+ attrs = new Attributes(aName, aArgs, attrs);
+ }
+
+ static void AttributeArg(out Attributes.Argument! arg) {
+ Expression! e; arg = dummyAttrArg;
+ if (t.kind == 3) {
+ Get();
+ arg = new Attributes.Argument(token.val.Substring(1, token.val.Length-2));
+ } else if (StartOf(5)) {
+ Expression(out e);
+ arg = new Attributes.Argument(e);
+ } else Error(124);
+ }
+
+
+
+ public static void Parse() {
+ Errors.SynErr = new ErrorProc(SynErr);
+ t = new Token();
+ Get();
+ Dafny();
+
+ }
+
+ [Microsoft.Contracts.Verify(false)]
+ static void SynErr(int n, string filename, int line, int col) {
+ Errors.count++;
+ System.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 = "digits expected"; break;
+ case 3: s = "string expected"; break;
+ case 4: s = "class expected"; break;
+ case 5: s = "{ expected"; break;
+ case 6: s = "} expected"; break;
+ case 7: s = "var 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 = "> expected"; break;
+ case 13: s = "frame expected"; break;
+ case 14: s = "method expected"; break;
+ case 15: s = "returns expected"; break;
+ case 16: s = "modifies expected"; break;
+ case 17: s = "free expected"; break;
+ case 18: s = "requires expected"; break;
+ case 19: s = "ensures expected"; break;
+ case 20: s = "( expected"; break;
+ case 21: s = ") expected"; break;
+ case 22: s = "bool expected"; break;
+ case 23: s = "int expected"; break;
+ case 24: s = "object expected"; break;
+ case 25: s = "set expected"; break;
+ case 26: s = "seq expected"; break;
+ case 27: s = "function expected"; break;
+ case 28: s = "use expected"; break;
+ case 29: s = "reads expected"; break;
+ case 30: s = "if expected"; break;
+ case 31: s = "else expected"; break;
+ case 32: s = "label expected"; break;
+ case 33: s = "break expected"; break;
+ case 34: s = "return expected"; break;
+ case 35: s = ":= expected"; break;
+ case 36: s = "new expected"; break;
+ case 37: s = "havoc expected"; break;
+ case 38: s = "while expected"; break;
+ case 39: s = "invariant expected"; break;
+ case 40: s = "decreases expected"; break;
+ case 41: s = "* expected"; break;
+ case 42: s = "call expected"; break;
+ case 43: s = "foreach expected"; break;
+ case 44: s = "in expected"; break;
+ case 45: s = "| expected"; break;
+ case 46: s = "assert expected"; break;
+ case 47: s = "assume expected"; break;
+ case 48: s = "<==> expected"; break;
+ case 49: s = "\\u21d4 expected"; break;
+ case 50: s = "==> expected"; break;
+ case 51: s = "\\u21d2 expected"; break;
+ case 52: s = "&& expected"; break;
+ case 53: s = "\\u2227 expected"; break;
+ case 54: s = "|| expected"; break;
+ case 55: s = "\\u2228 expected"; break;
+ case 56: s = "== expected"; break;
+ case 57: s = "<= expected"; break;
+ case 58: s = ">= expected"; break;
+ case 59: s = "!= expected"; break;
+ case 60: s = "!! expected"; break;
+ case 61: s = "\\u2260 expected"; break;
+ case 62: s = "\\u2264 expected"; break;
+ case 63: s = "\\u2265 expected"; break;
+ case 64: s = "+ expected"; break;
+ case 65: s = "- expected"; break;
+ case 66: s = "/ expected"; break;
+ case 67: s = "% expected"; break;
+ case 68: s = "! expected"; break;
+ case 69: s = "\\u00ac expected"; break;
+ case 70: s = "false expected"; break;
+ case 71: s = "true expected"; break;
+ case 72: s = "null expected"; break;
+ case 73: s = "fresh expected"; break;
+ case 74: s = "[ expected"; break;
+ case 75: s = "] expected"; break;
+ case 76: s = ". expected"; break;
+ case 77: s = ".. expected"; break;
+ case 78: s = "this expected"; break;
+ case 79: s = "old expected"; break;
+ case 80: s = "forall expected"; break;
+ case 81: s = "\\u2200 expected"; break;
+ case 82: s = "exists expected"; break;
+ case 83: s = "\\u2203 expected"; break;
+ case 84: s = ":: expected"; break;
+ case 85: s = "\\u2022 expected"; break;
+ case 86: s = "??? expected"; break;
+ case 87: s = "invalid ClassMemberDecl"; break;
+ case 88: s = "invalid FunctionDecl"; break;
+ case 89: s = "invalid MethodDecl"; break;
+ case 90: s = "invalid Type"; break;
+ case 91: s = "invalid MethodSpec"; break;
+ case 92: s = "invalid MethodSpec"; break;
+ case 93: s = "invalid ReferenceType"; break;
+ case 94: s = "invalid FunctionSpec"; break;
+ case 95: s = "invalid ExtendedExpr"; break;
+ case 96: s = "invalid IfThenElseExpr"; break;
+ case 97: s = "invalid Stmt"; break;
+ case 98: s = "invalid OneStmt"; break;
+ case 99: s = "invalid IfStmt"; break;
+ case 100: s = "invalid ForeachStmt"; break;
+ case 101: s = "invalid AssignRhs"; break;
+ case 102: s = "invalid Guard"; break;
+ case 103: s = "invalid CallStmtSubExpr"; break;
+ case 104: s = "invalid EquivOp"; break;
+ case 105: s = "invalid ImpliesOp"; break;
+ case 106: s = "invalid AndOp"; break;
+ case 107: s = "invalid OrOp"; break;
+ case 108: s = "invalid RelOp"; break;
+ case 109: s = "invalid AddOp"; break;
+ case 110: s = "invalid UnaryExpression"; break;
+ case 111: s = "invalid MulOp"; break;
+ case 112: s = "invalid NegOp"; break;
+ case 113: s = "invalid SelectExpression"; break;
+ case 114: s = "invalid ConstAtomExpression"; break;
+ case 115: s = "invalid ObjectExpression"; break;
+ case 116: s = "invalid ObjectExpression"; break;
+ case 117: s = "invalid SelectOrCallSuffix"; break;
+ case 118: s = "invalid SelectOrCallSuffix"; break;
+ case 119: s = "invalid QuantifierGuts"; break;
+ case 120: s = "invalid Forall"; break;
+ case 121: s = "invalid Exists"; break;
+ case 122: s = "invalid QSep"; break;
+ case 123: s = "invalid AttributeOrTrigger"; break;
+ case 124: s = "invalid AttributeArg"; break;
+
+ default: s = "error " + n; break;
+ }
+ System.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,x,x,T, x,x,x,x, x,T,T,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, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, 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,T,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,T,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,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,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,T,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,T,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,T,T,x, x,x,T,T, x,x,x,x, x,x,x,x},
+ {x,T,T,x, x,T,x,T, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,x,x,x, T,x,T,x, T,T,T,x, x,T,T,x, x,x,T,T, x,T,T,T, 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,T,T,x, x,x,T,T, x,x,x,x, x,x,x,x},
+ {x,T,T,x, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,x,x,x, T,x,T,x, T,T,T,x, x,T,T,x, x,x,T,T, x,T,T,T, 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,T,T,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,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,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, T,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,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, 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,T,T, x,x,x,x, x,x,x,x},
+ {x,x,T,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,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,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, x,x,x,x, x,x,x,x, 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,T,T,T, x,T,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,T,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,T,T,x, x,x,T,T, x,x,x,x, x,x,x,x}
+
+ };
+
+ [Microsoft.Contracts.Verify(false)]
+ static Parser() {}
+} // end Parser
+
+} // end namespace
diff --git a/Source/Dafny/Printer.ssc b/Source/Dafny/Printer.ssc
new file mode 100644
index 00000000..4a861553
--- /dev/null
+++ b/Source/Dafny/Printer.ssc
@@ -0,0 +1,663 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.IO;
+using System.Collections.Generic;
+using Microsoft.Contracts;
+using Bpl = Microsoft.Boogie;
+
+namespace Microsoft.Dafny {
+ class Printer {
+ TextWriter! wr;
+ public Printer(TextWriter! wr) {
+ this.wr = wr;
+ }
+
+ public void PrintProgram(Program! prog) {
+ if (Bpl.CommandLineOptions.Clo.ShowEnv != Bpl.CommandLineOptions.ShowEnvironment.Never) {
+ wr.WriteLine("// " + Bpl.CommandLineOptions.Clo.Version);
+ wr.WriteLine("// " + Bpl.CommandLineOptions.Clo.Environment);
+ }
+ wr.WriteLine("// {0}", prog.Name);
+ foreach (ClassDecl c in prog.Classes) {
+ wr.WriteLine();
+ PrintClass(c);
+ }
+ }
+
+ public void PrintClass(ClassDecl! c) {
+ PrintClassMethodHelper("class", c.Attributes, "", c.Name, c.TypeArgs);
+ if (c.Members.Count == 0) {
+ wr.WriteLine(" { }");
+ } else {
+ int state = 0; // 0 - no members yet; 1 - previous member was a field; 2 - previous member was non-field
+ wr.WriteLine(" {");
+ foreach (MemberDecl m in c.Members) {
+ if (m is Method) {
+ if (state != 0) { wr.WriteLine(); }
+ PrintMethod((Method)m);
+ state = 2;
+ } else if (m is Field) {
+ if (state == 2) { wr.WriteLine(); }
+ PrintField((Field)m);
+ state = 1;
+ } else if (m is Function) {
+ if (state != 0) { wr.WriteLine(); }
+ PrintFunction((Function)m);
+ state = 2;
+ } else {
+ assert false; // unexpected member
+ }
+ }
+ wr.WriteLine("}");
+ }
+ }
+
+ void PrintClassMethodHelper(string! kind, Attributes attrs, string! modifiers, string! name, List<TypeParameter!>! typeArgs) {
+ wr.Write("{0} ", kind);
+ PrintAttributes(attrs);
+ wr.Write(modifiers);
+ wr.Write(name);
+ if (typeArgs.Count != 0) {
+ wr.Write("<");
+ string sep = "";
+ foreach (TypeParameter tp in typeArgs) {
+ wr.Write("{0}{1}", sep, tp.Name);
+ sep = ", ";
+ }
+ wr.Write(">");
+ }
+ }
+
+ public void PrintAttributes(Attributes a) {
+ if (a != null) {
+ PrintAttributes(a.Prev);
+
+ wr.Write("{ :{0}", a.Name);
+ string prefix = " ";
+ foreach (Attributes.Argument arg in a.Args) {
+ wr.Write(prefix);
+ prefix = ", ";
+ if (arg.S != null) {
+ wr.Write("\"{0}\"", arg.S);
+ } else {
+ assert arg.E != null;
+ PrintExpression(arg.E);
+ }
+ }
+ wr.Write(" } ");
+ }
+ }
+
+ public void PrintField(Field! field) {
+ Indent(IndentAmount);
+ wr.Write("var ");
+ PrintAttributes(field.Attributes);
+ wr.Write("{0}: ", field.Name);
+ PrintType(field.Type);
+ wr.WriteLine(";");
+ }
+
+ public void PrintFunction(Function! f) {
+ Indent(IndentAmount);
+ PrintClassMethodHelper("function", f.Attributes, f.Use ? "use " : "", f.Name, f.TypeArgs);
+ PrintFormals(f.Formals);
+ wr.Write(": ");
+ PrintType(f.ResultType);
+ if (f.Body == null) {
+ wr.WriteLine(";");
+ }
+ foreach (Expression r in f.Req) {
+ Indent(2 * IndentAmount);
+ wr.Write("requires ");
+ PrintExpression(r);
+ wr.WriteLine(";");
+ }
+ foreach (Expression r in f.Reads) {
+ Indent(2 * IndentAmount);
+ wr.Write("reads ");
+ PrintExpression(r);
+ wr.WriteLine(";");
+ }
+ if (f.Body != null) {
+ Indent(IndentAmount);
+ PrintExtendedExpr(f.Body, IndentAmount);
+ wr.WriteLine();
+ }
+ }
+
+ // ----------------------------- PrintMethod -----------------------------
+
+ const int IndentAmount = 2;
+ const string! BunchaSpaces = " ";
+ void Indent(int amount)
+ requires 0 <= amount;
+ {
+ while (0 < amount) {
+ wr.Write(BunchaSpaces.Substring(0, amount));
+ amount -= BunchaSpaces.Length;
+ }
+ }
+
+ public void PrintMethod(Method! method) {
+ Indent(IndentAmount);
+ PrintClassMethodHelper("method", method.Attributes, "", method.Name, method.TypeArgs);
+ PrintFormals(method.Ins);
+ if (method.Outs.Count != 0) {
+ if (method.Ins.Count + method.Outs.Count <= 3) {
+ wr.Write(" returns ");
+ } else {
+ wr.WriteLine();
+ Indent(3 * IndentAmount);
+ wr.Write("returns ");
+ }
+ PrintFormals(method.Outs);
+ }
+ wr.WriteLine(method.Body == null ? ";" : ":");
+
+ PrintSpec("requires", method.Req, 2 * IndentAmount);
+ PrintSpec("modifies", method.Mod, 2 * IndentAmount);
+ PrintSpec("ensures", method.Ens, 2 * IndentAmount);
+
+ if (method.Body != null) {
+ Indent(IndentAmount);
+ PrintStatement(method.Body, IndentAmount);
+ wr.WriteLine();
+ }
+ }
+
+ void PrintFormals(List<Formal!>! ff) {
+ wr.Write("(");
+ string sep = "";
+ foreach (Formal f in ff) {
+ wr.Write(sep);
+ sep = ", ";
+ PrintFormal(f);
+ }
+ wr.Write(")");
+ }
+
+ void PrintFormal(Formal! f) {
+ if (!f.Name.StartsWith("#")) {
+ wr.Write("{0}: ", f.Name);
+ }
+ PrintType(f.Type);
+ }
+
+ void PrintSpec(string! kind, List<Expression!>! ee, int indent) {
+ foreach (Expression e in ee) {
+ Indent(indent);
+ wr.Write("{0} ", kind);
+ PrintExpression(e);
+ wr.WriteLine(";");
+ }
+ }
+
+ void PrintSpec(string! kind, List<MaybeFreeExpression!>! ee, int indent) {
+ foreach (MaybeFreeExpression e in ee) {
+ Indent(indent);
+ wr.Write("{0}{1} ", e.IsFree ? "free " : "", kind);
+ PrintExpression(e.E);
+ wr.WriteLine(";");
+ }
+ }
+
+ // ----------------------------- PrintType -----------------------------
+
+ public void PrintType(Type! ty) {
+ wr.Write(ty.ToString());
+ }
+
+ // ----------------------------- PrintStatement -----------------------------
+
+ /// <summary>
+ /// Prints from the current position of the current line.
+ /// If the statement requires several lines, subsequent lines are indented at "indent".
+ /// No newline is printed after the statement.
+ /// </summary>
+ public void PrintStatement(Statement! stmt, int indent) {
+ if (stmt is AssertStmt) {
+ wr.Write("assert ");
+ PrintExpression(((AssertStmt)stmt).Expr);
+ wr.Write(";");
+
+ } else if (stmt is AssumeStmt) {
+ wr.Write("assume ");
+ PrintExpression(((AssertStmt)stmt).Expr);
+ wr.Write(";");
+
+ } else if (stmt is UseStmt) {
+ wr.Write("use ");
+ PrintExpression(((AssertStmt)stmt).Expr);
+ wr.Write(";");
+
+ } else if (stmt is LabelStmt) {
+ wr.Write("label {0}:", ((LabelStmt)stmt).Label);
+
+ } else if (stmt is BreakStmt) {
+ BreakStmt s = (BreakStmt)stmt;
+ if (s.TargetLabel == null) {
+ wr.Write("break;");
+ } else {
+ wr.Write("break {0};", s.TargetLabel);
+ }
+
+ } else if (stmt is ReturnStmt) {
+ wr.Write("return;");
+
+ } else if (stmt is AssignStmt) {
+ AssignStmt s = (AssignStmt)stmt;
+ if (s.Rhs is HavocRhs) {
+ wr.Write("havoc ");
+ PrintExpression(s.Lhs);
+ } else {
+ PrintExpression(s.Lhs);
+ wr.Write(" := ");
+ PrintDeterminedRhs((DeterminedAssignmentRhs)s.Rhs);
+ }
+ wr.Write(";");
+
+ } else if (stmt is VarDecl) {
+ VarDecl s = (VarDecl)stmt;
+ wr.Write("var {0}", s.Name);
+ if (s.OptionalType != null) {
+ wr.Write(": ");
+ PrintType(s.OptionalType);
+ }
+ if (s.Rhs != null) {
+ wr.Write(" := ");
+ PrintDeterminedRhs(s.Rhs);
+ }
+ wr.Write(";");
+
+ } else if (stmt is CallStmt) {
+ CallStmt s = (CallStmt)stmt;
+ wr.Write("call ");
+ if (s.Lhs.Count != 0) {
+ string sep = "";
+ foreach (IdentifierExpr v in s.Lhs) {
+ wr.Write(sep);
+ PrintExpression(v);
+ sep = ", ";
+ }
+ wr.Write(" := ");
+ }
+ if (!(s.Receiver is ThisExpr)) {
+ PrintExpr(s.Receiver, 0x70, false, -1);
+ wr.Write(".");
+ }
+ wr.Write("{0}(", s.MethodName);
+ PrintExpressionList(s.Args);
+ wr.Write(")");
+
+ } else if (stmt is BlockStmt) {
+ wr.WriteLine("{");
+ int ind = indent + IndentAmount;
+ foreach (Statement s in ((BlockStmt)stmt).Body) {
+ Indent(ind);
+ PrintStatement(s, ind);
+ wr.WriteLine();
+ }
+ Indent(indent);
+ wr.Write("}");
+
+ } else if (stmt is IfStmt) {
+ IfStmt s = (IfStmt)stmt;
+ while (true) {
+ wr.Write("if (");
+ PrintGuard(s.Guard);
+ wr.Write(") ");
+ PrintStatement(s.Thn, indent);
+ if (s.Els == null) {
+ break;
+ }
+ wr.Write(" else ");
+ if (s.Els is IfStmt) {
+ s = (IfStmt)s.Els;
+ } else {
+ PrintStatement(s.Els, indent);
+ break;
+ }
+ }
+
+ } else if (stmt is WhileStmt) {
+ WhileStmt s = (WhileStmt)stmt;
+ wr.Write("while (");
+ PrintGuard(s.Guard);
+ wr.WriteLine(")");
+
+ int ind = indent + IndentAmount;
+ PrintSpec("invariant", s.Invariants, ind);
+ if (s.Decreases.Count != 0) {
+ Indent(indent);
+ string sep = "decreases ";
+ foreach (Expression e in s.Decreases) {
+ wr.Write(sep);
+ sep = ", ";
+ PrintExpression(e);
+ }
+ wr.WriteLine(";");
+ }
+ Indent(indent);
+ PrintStatement(s.Body, indent);
+
+ } else if (stmt is ForeachStmt) {
+ ForeachStmt s = (ForeachStmt)stmt;
+ wr.Write("foreach ({0} in ", s.BoundVar);
+ PrintExpression(s.Collection);
+ if (!LiteralExpr.IsTrue(s.Range)) {
+ wr.Write(" | ");
+ PrintExpression(s.Range);
+ }
+ wr.WriteLine(") {");
+ int ind = indent + IndentAmount;
+ foreach (PredicateStmt t in s.BodyPrefix) {
+ Indent(ind);
+ PrintStatement(t, ind);
+ wr.WriteLine();
+ }
+ if (s.BodyAssign != null) {
+ Indent(ind);
+ PrintStatement(s.BodyAssign, ind);
+ wr.WriteLine();
+ }
+ wr.Write("}");
+
+ } else {
+ assert false; // unexpected statement
+ }
+ }
+
+ void PrintDeterminedRhs(DeterminedAssignmentRhs! rhs) {
+ if (rhs is ExprRhs) {
+ PrintExpression(((ExprRhs)rhs).Expr);
+ } else if (rhs is TypeRhs) {
+ wr.Write("new ");
+ PrintType(((TypeRhs)rhs).Type);
+ } else {
+ assert false; // unexpected RHS
+ }
+ }
+
+ void PrintGuard(Expression guard) {
+ if (guard == null) {
+ wr.Write("*");
+ } else {
+ PrintExpression(guard);
+ }
+ }
+
+ // ----------------------------- PrintExpression -----------------------------
+
+ public void PrintExtendedExpr(Expression! expr, int indent) {
+ wr.WriteLine("{");
+ int ind = indent + IndentAmount;
+ Indent(ind);
+ if (expr is ITEExpr) {
+ Expression els = expr; // bogus assignment, just to please the compiler
+ for (ITEExpr ite = (ITEExpr)expr; ite != null; ite = els as ITEExpr) {
+ wr.Write("if (");
+ PrintExpression(ite.Test);
+ wr.Write(") ");
+ PrintExtendedExpr(ite.Thn, ind);
+ wr.Write(" else ");
+ els = ite.Els;
+ }
+ PrintExtendedExpr(els, ind);
+ } else {
+ PrintExpression(expr, ind);
+ wr.WriteLine();
+ }
+ Indent(indent);
+ wr.Write("}");
+ }
+
+ public void PrintExpression(Expression! expr) {
+ PrintExpr(expr, 0, false, -1);
+ }
+
+ /// <summary>
+ /// An indent of -1 means print the entire expression on one line.
+ /// </summary>
+ public void PrintExpression(Expression! expr, int indent) {
+ PrintExpr(expr, 0, false, indent);
+ }
+
+ /// <summary>
+ /// An indent of -1 means print the entire expression on one line.
+ /// </summary>
+ void PrintExpr(Expression! expr, int contextBindingStrength, bool fragileContext, int indent)
+ requires -1 <= indent;
+ {
+ if (expr is LiteralExpr) {
+ LiteralExpr e = (LiteralExpr)expr;
+ if (e.Value == null) {
+ wr.Write("null");
+ } else if (e.Value is bool) {
+ wr.Write((bool)e.Value ? "true" : "false");
+ } else {
+ wr.Write((int)e.Value);
+ }
+
+ } else if (expr is ThisExpr) {
+ wr.Write("this");
+
+ } else if (expr is IdentifierExpr) {
+ wr.Write(((IdentifierExpr)expr).Name);
+
+ } else if (expr is DisplayExpression) {
+ DisplayExpression e = (DisplayExpression)expr;
+ wr.Write(e is SetDisplayExpr ? "{" : "[");
+ PrintExpressionList(e.Elements);
+ wr.Write(e is SetDisplayExpr ? "}" : "]");
+
+ } else if (expr is FieldSelectExpr) {
+ FieldSelectExpr e = (FieldSelectExpr)expr;
+ // determine if parens are needed
+ int opBindingStrength = 0x70;
+ bool parensNeeded = !(e.Obj is ImplicitThisExpr) &&
+ opBindingStrength < contextBindingStrength ||
+ (fragileContext && opBindingStrength == contextBindingStrength);
+
+ if (parensNeeded) { wr.Write("("); }
+ if (!(e.Obj is ImplicitThisExpr)) {
+ PrintExpr(e.Obj, opBindingStrength, false, -1);
+ wr.Write(".");
+ }
+ wr.Write(e.FieldName);
+ if (parensNeeded) { wr.Write(")"); }
+
+ } else if (expr is SeqSelectExpr) {
+ SeqSelectExpr e = (SeqSelectExpr)expr;
+ // determine if parens are needed
+ int opBindingStrength = 0x70;
+ bool parensNeeded = opBindingStrength < contextBindingStrength ||
+ (fragileContext && opBindingStrength == contextBindingStrength);
+
+ if (parensNeeded) { wr.Write("("); }
+ PrintExpr(e.Seq, 00, false, indent); // BOGUS: fix me
+ wr.Write("[");
+ if (e.SelectOne) {
+ assert e.E0 != null;
+ PrintExpression(e.E0);
+ } else {
+ if (e.E0 != null) {
+ PrintExpression(e.E0);
+ }
+ wr.Write(e.E0 != null && e.E1 != null ? " .. " : "..");
+ if (e.E1 != null) {
+ PrintExpression(e.E1);
+ }
+ }
+ wr.Write("]");
+ if (parensNeeded) { wr.Write(")"); }
+
+ } else if (expr is FunctionCallExpr) {
+ FunctionCallExpr e = (FunctionCallExpr)expr;
+ // determine if parens are needed
+ int opBindingStrength = 0x70;
+ bool parensNeeded = !(e.Receiver is ThisExpr) &&
+ opBindingStrength < contextBindingStrength ||
+ (fragileContext && opBindingStrength == contextBindingStrength);
+
+ if (parensNeeded) { wr.Write("("); }
+ if (!(e.Receiver is ThisExpr)) {
+ PrintExpr(e.Receiver, opBindingStrength, false, -1);
+ wr.Write(".");
+ }
+ wr.Write(e.Name);
+ wr.Write("(");
+ PrintExpressionList(e.Args);
+ wr.Write(")");
+ if (parensNeeded) { wr.Write(")"); }
+
+ } else if (expr is OldExpr) {
+ wr.Write("old(");
+ PrintExpression(((OldExpr)expr).E);
+ wr.Write(")");
+
+ } else if (expr is FreshExpr) {
+ wr.Write("fresh(");
+ PrintExpression(((FreshExpr)expr).E);
+ wr.Write(")");
+
+ } else if (expr is UnaryExpr) {
+ UnaryExpr e = (UnaryExpr)expr;
+ if (e.Op == UnaryExpr.Opcode.SeqLength) {
+ wr.Write("|");
+ PrintExpression(e.E);
+ wr.Write("|");
+ } else {
+ // Prefix operator.
+ // determine if parens are needed
+ string op;
+ int opBindingStrength;
+ switch (e.Op) {
+ case UnaryExpr.Opcode.Not:
+ op = "!"; opBindingStrength = 0x60; break;
+ default:
+ assert false; // unexpected unary opcode
+ }
+ bool parensNeeded = opBindingStrength < contextBindingStrength ||
+ (fragileContext && opBindingStrength == contextBindingStrength);
+
+ if (parensNeeded) { wr.Write("("); }
+ wr.Write(op);
+ PrintExpr(e.E, opBindingStrength, false, -1);
+ if (parensNeeded) { wr.Write(")"); }
+ }
+
+ } else if (expr is BinaryExpr) {
+ BinaryExpr e = (BinaryExpr)expr;
+ // 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 (e.Op)
+ {
+ case BinaryExpr.Opcode.Add:
+ opBindingStrength = 0x40; break;
+ case BinaryExpr.Opcode.Sub:
+ opBindingStrength = 0x40; fragileRightContext = true; break;
+ case BinaryExpr.Opcode.Mul:
+ opBindingStrength = 0x50; break;
+ case BinaryExpr.Opcode.Div:
+ case BinaryExpr.Opcode.Mod:
+ opBindingStrength = 0x50; fragileRightContext = true; break;
+ case BinaryExpr.Opcode.Eq:
+ case BinaryExpr.Opcode.Neq:
+ case BinaryExpr.Opcode.Gt:
+ case BinaryExpr.Opcode.Ge:
+ case BinaryExpr.Opcode.Lt:
+ case BinaryExpr.Opcode.Le:
+ case BinaryExpr.Opcode.Disjoint:
+ case BinaryExpr.Opcode.In:
+ opBindingStrength = 0x30; fragileLeftContext = fragileRightContext = true; break;
+ case BinaryExpr.Opcode.And:
+ opBindingStrength = 0x20; break;
+ case BinaryExpr.Opcode.Or:
+ opBindingStrength = 0x21; break;
+ case BinaryExpr.Opcode.Imp:
+ opBindingStrength = 0x10; fragileLeftContext = true; break;
+ case BinaryExpr.Opcode.Iff:
+ opBindingStrength = 0x00; break;
+ default:
+ assert false; // unexpected binary operator
+ }
+ int opBS = opBindingStrength & 0xF0;
+ int ctxtBS = contextBindingStrength & 0xF0;
+ bool parensNeeded = opBS < ctxtBS ||
+ (opBS == ctxtBS && (opBindingStrength != contextBindingStrength || fragileContext));
+
+ string op = BinaryExpr.OpcodeString(e.Op);
+ if (parensNeeded) { wr.Write("("); }
+ if (0 <= indent && e.Op == BinaryExpr.Opcode.And) {
+ PrintExpr(e.E0, opBindingStrength, fragileLeftContext, indent);
+ wr.WriteLine(" {0}", op);
+ Indent(indent);
+ PrintExpr(e.E1, opBindingStrength, fragileRightContext, indent);
+ } else if (0 <= indent && e.Op == BinaryExpr.Opcode.Imp) {
+ PrintExpr(e.E0, opBindingStrength, fragileLeftContext, indent);
+ wr.WriteLine(" {0}", op);
+ int ind = indent + IndentAmount;
+ Indent(ind);
+ PrintExpr(e.E1, opBindingStrength, fragileRightContext, ind);
+ } else {
+ PrintExpr(e.E0, opBindingStrength, fragileLeftContext, -1);
+ wr.Write(" {0} ", op);
+ PrintExpr(e.E1, opBindingStrength, fragileRightContext, -1);
+ }
+ if (parensNeeded) { wr.Write(")"); }
+
+ } else if (expr is QuantifierExpr) {
+ QuantifierExpr e = (QuantifierExpr)expr;
+ wr.Write(e is ForallExpr ? "(forall " : "(exists ");
+ string sep = "";
+ foreach (BoundVar bv in e.BoundVars) {
+ wr.Write("{0}{1}: ", sep, bv.Name);
+ sep = ", ";
+ PrintType(bv.Type);
+ }
+ wr.Write(" :: ");
+ PrintAttributes(e.Attributes);
+ PrintTriggers(e.Trigs);
+ if (0 <= indent) {
+ int ind = indent + IndentAmount;
+ wr.WriteLine();
+ Indent(ind);
+ PrintExpression(e.Body, ind);
+ } else {
+ PrintExpression(e.Body);
+ }
+ wr.Write(")");
+
+ } else if (expr is ITEExpr) {
+ assert false; // ITE is an extended expression and should be printed only using PrintExtendedExpr
+ } else {
+ assert false; // unexpected expression
+ }
+ }
+
+ void PrintTriggers(Triggers trigs) {
+ if (trigs != null) {
+ PrintTriggers(trigs.Prev);
+
+ wr.Write("{ ");
+ PrintExpressionList(trigs.Terms);
+ wr.Write(" } ");
+ }
+ }
+
+ void PrintExpressionList(List<Expression!>! exprs) {
+ string sep = "";
+ foreach (Expression e in exprs) {
+ wr.Write(sep);
+ sep = ", ";
+ PrintExpression(e);
+ }
+ }
+ }
+}
diff --git a/Source/Dafny/Resolver.ssc b/Source/Dafny/Resolver.ssc
new file mode 100644
index 00000000..66cdabe1
--- /dev/null
+++ b/Source/Dafny/Resolver.ssc
@@ -0,0 +1,1419 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using Microsoft.Contracts;
+
+namespace Microsoft.Dafny {
+ public class Resolver {
+ public int ErrorCount = 0;
+ void Error(Token! tok, string! msg, params object[] args) {
+ ConsoleColor col = Console.ForegroundColor;
+ Console.ForegroundColor = ConsoleColor.Red;
+ Console.WriteLine("{0}({1},{2}): Error: {3}",
+ tok.filename, tok.line, tok.col-1,
+ string.Format(msg, args));
+ Console.ForegroundColor = col;
+ ErrorCount++;
+ }
+ void Error(Declaration! d, string! msg, params object[] args) {
+ Error(d.tok, msg, args);
+ }
+ void Error(Statement! s, string! msg, params object[] args) {
+ Error(s.Tok, msg, args);
+ }
+ void Error(NonglobalVariable! v, string! msg, params object[] args) {
+ Error(v.tok, msg, args);
+ }
+ void Error(Expression! e, string! msg, params object[] args) {
+ Error(e.tok, msg, args);
+ }
+
+ readonly Dictionary<string!,ClassDecl!>! classes = new Dictionary<string!,ClassDecl!>();
+ readonly Dictionary<ClassDecl!,Dictionary<string!,MemberDecl!>!>! classMembers = new Dictionary<ClassDecl!,Dictionary<string!,MemberDecl!>!>();
+
+ public void ResolveProgram(Program! prog) {
+ foreach (ClassDecl cl in prog.Classes) {
+ // register the class name
+ if (classes.ContainsKey(cl.Name)) {
+ Error(cl, "Duplicate class name: {0}", cl.Name);
+ } else {
+ classes.Add(cl.Name, cl);
+ }
+
+ // register the names of the class members
+ Dictionary<string!,MemberDecl!> members = new Dictionary<string!,MemberDecl!>();
+ classMembers.Add(cl, members);
+
+ foreach (MemberDecl m in cl.Members) {
+ if (members.ContainsKey(m.Name)) {
+ Error(m, "Duplicate member name: {0}", m.Name);
+ } else {
+ members.Add(m.Name, m);
+ }
+ }
+ }
+
+ // resolve each class
+ foreach (ClassDecl cl in prog.Classes) {
+ allTypeParameters.PushMarker();
+ ResolveTypeParameters(cl.TypeArgs, true, cl);
+ ResolveClassMemberTypes(cl);
+ allTypeParameters.PopMarker();
+ }
+ foreach (ClassDecl cl in prog.Classes) {
+ allTypeParameters.PushMarker();
+ ResolveTypeParameters(cl.TypeArgs, false, cl);
+ ResolveClassMemberBodies(cl);
+ allTypeParameters.PopMarker();
+ }
+ }
+
+ ClassDecl currentClass;
+ readonly Scope<TypeParameter>! allTypeParameters = new Scope<TypeParameter>();
+ readonly Scope<IVariable>! scope = new Scope<IVariable>();
+ readonly Scope<Statement>! labeledStatements = new Scope<Statement>();
+
+ /// <summary>
+ /// Assumes type parameters have already been pushed
+ /// </summary>
+ void ResolveClassMemberTypes(ClassDecl! cl)
+ requires currentClass == null;
+ ensures currentClass == null;
+ {
+ currentClass = cl;
+ foreach (MemberDecl member in cl.Members) {
+ member.EnclosingClass = cl;
+ if (member is Field) {
+ ResolveType(((Field)member).Type);
+
+ } else if (member is Function) {
+ Function f = (Function)member;
+ allTypeParameters.PushMarker();
+ ResolveTypeParameters(f.TypeArgs, true, f);
+ ResolveFunctionSignature(f);
+ allTypeParameters.PopMarker();
+
+ } else if (member is Method) {
+ Method m = (Method)member;
+ allTypeParameters.PushMarker();
+ ResolveTypeParameters(m.TypeArgs, true, m);
+ ResolveMethodSignature(m);
+ allTypeParameters.PopMarker();
+
+ } else {
+ assert false; // unexpected member type
+ }
+ }
+ currentClass = null;
+ }
+
+ /// <summary>
+ /// Assumes type parameters have already been pushed, and that all types in class members have been resolved
+ /// </summary>
+ void ResolveClassMemberBodies(ClassDecl! cl)
+ requires currentClass == null;
+ ensures currentClass == null;
+ {
+ ResolveAttributes(cl.Attributes, false);
+ currentClass = cl;
+ foreach (MemberDecl member in cl.Members) {
+ ResolveAttributes(member.Attributes, false);
+ if (member is Field) {
+ // nothing more to do
+
+ } else if (member is Function) {
+ Function f = (Function)member;
+ allTypeParameters.PushMarker();
+ ResolveTypeParameters(f.TypeArgs, false, f);
+ ResolveFunction(f);
+ allTypeParameters.PopMarker();
+
+ } else if (member is Method) {
+ Method m = (Method)member;
+ allTypeParameters.PushMarker();
+ ResolveTypeParameters(m.TypeArgs, false, m);
+ ResolveMethod(m);
+ allTypeParameters.PopMarker();
+
+ } else {
+ assert false; // unexpected member type
+ }
+ }
+ currentClass = null;
+ }
+
+ void ResolveAttributes(Attributes attrs, bool twoState) {
+ // order does not matter for resolution, so resolve them in reverse order
+ for (; attrs != null; attrs = attrs.Prev) {
+ foreach (Attributes.Argument aa in attrs.Args) {
+ if (aa.E != null) {
+ ResolveExpression(aa.E, twoState);
+ }
+ }
+ }
+ }
+
+ void ResolveTriggers(Triggers trigs, bool twoState) {
+ // order does not matter for resolution, so resolve them in reverse order
+ for (; trigs != null; trigs = trigs.Prev) {
+ foreach (Expression e in trigs.Terms) {
+ ResolveExpression(e, twoState);
+ }
+ }
+ }
+
+ void ResolveTypeParameters(List<TypeParameter!>! tparams, bool emitErrors, TypeParameter.ParentType! parent) {
+ // push non-duplicated type parameter names
+ foreach (TypeParameter tp in tparams) {
+ if (emitErrors) {
+ // we're seeing this TypeParameter for the first time
+ tp.Parent = parent;
+ }
+ if (!allTypeParameters.Push(tp.Name, tp) && emitErrors) {
+ Error(tp, "Duplicate type-parameter name: {0}", tp.Name);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Assumes type parameters have already been pushed
+ /// </summary>
+ void ResolveFunctionSignature(Function! f) {
+ scope.PushMarker();
+ foreach (Formal p in f.Formals) {
+ if (!scope.Push(p.Name, p)) {
+ Error(p, "Duplicate parameter name: {0}", p.Name);
+ }
+ ResolveType(p.Type);
+ }
+ ResolveType(f.ResultType);
+ scope.PopMarker();
+ }
+
+ /// <summary>
+ /// Assumes type parameters have already been pushed
+ /// </summary>
+ void ResolveFunction(Function! f) {
+ scope.PushMarker();
+ foreach (Formal p in f.Formals) {
+ scope.Push(p.Name, p);
+ }
+ foreach (Expression r in f.Req) {
+ ResolveExpression(r, false);
+ assert r.Type != null; // follows from postcondition of ResolveExpression
+ if (!UnifyTypes(r.Type, Type.Bool)) {
+ Error(r, "Precondition must be a boolean (got {0})", r.Type);
+ }
+ }
+ foreach (Expression r in f.Reads) {
+ ResolveExpression(r, false);
+ Type t = r.Type;
+ assert t != null; // follows from postcondition of ResolveExpression
+ if (t is CollectionType) {
+ t = ((CollectionType)t).Arg;
+ }
+ if (t is ObjectType) {
+ // fine
+ } else if (ClassType.DenotesClass(t) != null) {
+ // fine
+ } else {
+ Error(r, "a reads-clause expression must denote an object or a collection of objects (instead got {0})", r.Type);
+ }
+ }
+ if (f.Body != null) {
+ ResolveExpression(f.Body, false);
+ assert f.Body.Type != null; // follows from postcondition of ResolveExpression
+ if (!UnifyTypes(f.Body.Type, f.ResultType)) {
+ Error(f, "Function body type mismatch (expected {0}, got {1})", f.ResultType, f.Body.Type);
+ }
+ }
+ scope.PopMarker();
+ }
+
+ /// <summary>
+ /// Assumes type parameters have already been pushed
+ /// </summary>
+ void ResolveMethodSignature(Method! m) {
+ scope.PushMarker();
+ // resolve in-parameters
+ foreach (Formal p in m.Ins) {
+ if (!scope.Push(p.Name, p)) {
+ Error(p, "Duplicate parameter name: {0}", p.Name);
+ }
+ ResolveType(p.Type);
+ }
+ // resolve out-parameters
+ foreach (Formal p in m.Outs) {
+ if (!scope.Push(p.Name, p)) {
+ Error(p, "Duplicate parameter name: {0}", p.Name);
+ }
+ ResolveType(p.Type);
+ }
+ scope.PopMarker();
+ }
+
+ /// <summary>
+ /// Assumes type parameters have already been pushed
+ /// </summary>
+ void ResolveMethod(Method! m) {
+ // Add in-parameters to the scope, but don't care about any duplication errors, since they have already been reported
+ scope.PushMarker();
+ foreach (Formal p in m.Ins) {
+ scope.Push(p.Name, p);
+ }
+
+ // Start resolving specification...
+ foreach (MaybeFreeExpression e in m.Req) {
+ ResolveExpression(e.E, false);
+ assert e.E.Type != null; // follows from postcondition of ResolveExpression
+ if (!UnifyTypes(e.E.Type, Type.Bool)) {
+ Error(e.E, "Precondition must be a boolean (got {0})", e.E.Type);
+ }
+ }
+ foreach (Expression e in m.Mod) {
+ ResolveExpression(e, false);
+ Type t = e.Type;
+ assert t != null; // follows from postcondition of ResolveExpression
+ if (t is CollectionType) {
+ t = ((CollectionType)t).Arg;
+ }
+ if (t is ObjectType) {
+ // fine
+ } else if (ClassType.DenotesClass(t) != null) {
+ // fine
+ } else {
+ Error(e, "a modifies-clause expression must denote an object or a collection of objects (instead got {0})", e.Type);
+ }
+ }
+
+ // Add out-parameters to the scope, but don't care about any duplication errors, since they have already been reported
+ foreach (Formal p in m.Outs) {
+ scope.Push(p.Name, p);
+ }
+
+ // ... continue resolving specification
+ foreach (MaybeFreeExpression e in m.Ens) {
+ ResolveExpression(e.E, true);
+ assert e.E.Type != null; // follows from postcondition of ResolveExpression
+ if (!UnifyTypes(e.E.Type, Type.Bool)) {
+ Error(e.E, "Postcondition must be a boolean (got {0})", e.E.Type);
+ }
+ }
+
+ // Resolve body
+ if (m.Body != null) {
+ ResolveStatement(m.Body);
+ }
+
+ scope.PopMarker();
+ }
+
+ public void ResolveType(Type! type) {
+ if (type is BasicType) {
+ // nothing to resolve
+ } else if (type is CollectionType) {
+ ResolveType(((CollectionType)type).Arg);
+ } else if (type is ClassType) {
+ ClassType t = (ClassType)type;
+ foreach (Type tt in t.TypeArgs) {
+ ResolveType(tt);
+ }
+ TypeParameter tp = allTypeParameters.Find(t.Name);
+ if (tp != null) {
+ if (t.TypeArgs.Count == 0) {
+ t.ResolvedParam = tp;
+ } else {
+ Error(t.tok, "Type parameter expects no type arguments: {0}", t.Name);
+ }
+ } else {
+ ClassDecl cl;
+ if (!classes.TryGetValue(t.Name, out cl)) {
+ Error(t.tok, "Undeclared class type or type parameter: {0}", t.Name);
+ } else if (((!)cl).TypeArgs.Count == t.TypeArgs.Count) {
+ t.ResolvedClass = cl;
+ } else {
+ Error(t.tok, "Wrong number of type arguments ({0} instead of {1}) passed to class: {2}", t.TypeArgs.Count, cl.TypeArgs.Count, t.Name);
+ }
+ }
+
+ } else if (type is TypeProxy) {
+ TypeProxy t = (TypeProxy)type;
+ if (t.T != null) {
+ ResolveType(t.T);
+ }
+
+ } else {
+ assert false; // unexpected type
+ }
+ }
+
+ public bool UnifyTypes(Type! a, Type! b) {
+ while (a is TypeProxy) {
+ TypeProxy proxy = (TypeProxy)a;
+ if (proxy.T == null) {
+ // merge a and b; to avoid cycles, first get to the bottom of b
+ while (b is TypeProxy && ((TypeProxy)b).T != null) {
+ b = ((TypeProxy)b).T;
+ }
+ return AssignProxy(proxy, b);
+ } else {
+ a = proxy.T;
+ }
+ }
+
+ while (b is TypeProxy) {
+ TypeProxy proxy = (TypeProxy)b;
+ if (proxy.T == null) {
+ // merge a and b (we have already got to the bottom of a)
+ return AssignProxy(proxy, a);
+ } else {
+ b = proxy.T;
+ }
+ }
+
+#if !NO_CHEAP_OBJECT_WORKAROUND
+ if (a is ObjectType || b is ObjectType) { // TODO: remove this temporary hack
+ // allow anything with object; this is BOGUS
+ return true;
+ }
+#endif
+ // Now, a and b are non-proxies and stand for the same things as the original a and b, respectively.
+
+ if (a is BoolType) {
+ return b is BoolType;
+ } else if (a is IntType) {
+ return b is IntType;
+ } else if (a is ObjectType) {
+ return b is ObjectType;
+ } else if (a is SetType) {
+ return b is SetType && UnifyTypes(((SetType)a).Arg, ((SetType)b).Arg);
+ } else if (a is SeqType) {
+ return b is SeqType && UnifyTypes(((SeqType)a).Arg, ((SeqType)b).Arg);
+ } else if (a is ClassType) {
+ if (!(b is ClassType)) {
+ return false;
+ }
+ ClassType aa = (ClassType)a;
+ ClassType bb = (ClassType)b;
+ if (aa.ResolvedClass != null && aa.ResolvedClass == bb.ResolvedClass) {
+ // these are both resolved class types
+ assert aa.TypeArgs.Count == bb.TypeArgs.Count;
+ bool successSoFar = true;
+ for (int i = 0; i < aa.TypeArgs.Count; i++) {
+ if (!UnifyTypes(aa.TypeArgs[i], bb.TypeArgs[i])) {
+ successSoFar = false;
+ }
+ }
+ return successSoFar;
+ } else if (aa.ResolvedParam != null && aa.ResolvedParam == bb.ResolvedParam) {
+ // these are both resolved type parameters
+ assert aa.TypeArgs.Count == 0 && bb.TypeArgs.Count == 0;
+ return true;
+ } else {
+ // something is wrong; either aa or bb wasn't properly resolved, or they don't unify
+ return false;
+ }
+
+ } else {
+ assert false; // unexpected type
+ }
+ }
+
+ bool AssignProxy(TypeProxy! proxy, Type! t)
+ requires proxy.T == null;
+ requires t is TypeProxy ==> ((TypeProxy)t).T == null;
+ modifies proxy.T, ((TypeProxy)t).T; // might also change t.T if t is a proxy
+ ensures result ==> proxy == t || proxy.T != null || (t is TypeProxy && ((TypeProxy)t).T != null);
+ {
+ if (proxy == t) {
+ // they are already in the same equivalence class
+ return true;
+
+ } else if (proxy is UnrestrictedTypeProxy) {
+ // it's fine to redirect proxy to t (done below)
+
+ } else if (t is UnrestrictedTypeProxy) {
+ // merge proxy and t by redirecting t to proxy, rather than the other way around
+ ((TypeProxy)t).T = proxy;
+ return true;
+
+ } else if (t is RestrictedTypeProxy) {
+ // Both proxy and t are restricted type proxies. To simplify unification, order proxy and t
+ // according to their types.
+ RestrictedTypeProxy r0 = (RestrictedTypeProxy)proxy;
+ RestrictedTypeProxy r1 = (RestrictedTypeProxy)t;
+ if (r0.OrderID <= r1.OrderID) {
+ return AssignRestrictedProxies(r0, r1);
+ } else {
+ return AssignRestrictedProxies(r1, r0);
+ }
+
+ // In the remaining cases, proxy is a restricted proxy and t is a non-proxy
+ } else if (proxy is ObjectTypeProxy) {
+ if (t is ObjectType || ClassType.DenotesClass(t) != null) {
+ // all is fine, proxy can be redirected to t
+ } else {
+ return false;
+ }
+
+ } else if (proxy is ObjectsTypeProxy) {
+ if (t is ObjectType || ClassType.DenotesClass(t) != null) {
+ // all is good
+ } else if (t is CollectionType) {
+ proxy.T = new CollectionTypeProxy(new ObjectTypeProxy());
+ return UnifyTypes(proxy.T, t);
+ }
+
+ } else if (proxy is CollectionTypeProxy) {
+ CollectionTypeProxy collProxy = (CollectionTypeProxy)proxy;
+ if (t is CollectionType) {
+ if (!UnifyTypes(collProxy.Arg, ((CollectionType)t).Arg)) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+
+ } else if (proxy is OperationTypeProxy) {
+ OperationTypeProxy opProxy = (OperationTypeProxy)proxy;
+ if (t is IntType || t is SetType || (opProxy.AllowSeq && t is SeqType)) {
+ // this is the expected case
+ } else {
+ return false;
+ }
+
+ } else {
+ assert false; // unexpected proxy type
+ }
+
+ // do the merge
+ proxy.T = t;
+ return true;
+ }
+
+ bool AssignRestrictedProxies(RestrictedTypeProxy! a, RestrictedTypeProxy! b)
+ requires a != b;
+ requires a.T == null && b.T == null;
+ requires a.OrderID <= b.OrderID;
+ modifies a.T, b.T;
+ ensures result ==> a.T != null || b.T != null;
+ {
+ if (a is ObjectTypeProxy) {
+ if (b is ObjectTypeProxy) {
+ // all is fine
+ a.T = b;
+ return true;
+ } else if (b is ObjectsTypeProxy) {
+ // unify a and b by redirecting b to a, since a gives the stronger requirement
+ b.T = a;
+ return true;
+ } else {
+ return false;
+ }
+
+ } else if (a is ObjectsTypeProxy) {
+ if (b is ObjectsTypeProxy) {
+ // fine
+ a.T = b;
+ return true;
+ } else if (b is CollectionTypeProxy) {
+ // fine provided b's collection-element-type can be unified with object or a class type
+ a.T = new ObjectTypeProxy();
+ return UnifyTypes(a.T, ((CollectionTypeProxy)b).Arg);
+ } else if (b is OperationTypeProxy) {
+ // fine; restrict a to sets of object/class, and restrict b to set/seq of object/class
+ if (((OperationTypeProxy)b).AllowSeq) {
+ a.T = new CollectionTypeProxy(new ObjectTypeProxy());
+ b.T = a.T;
+ } else {
+ a.T = new SetType(new ObjectTypeProxy());
+ b.T = a.T;
+ }
+ return true;
+ } else {
+ assert false; // unexpected restricted-proxy type
+ }
+
+ } else if (a is CollectionTypeProxy) {
+ if (b is CollectionTypeProxy) {
+ a.T = b;
+ return UnifyTypes(((CollectionTypeProxy)a).Arg, ((CollectionTypeProxy)b).Arg);
+ } else if (b is OperationTypeProxy) {
+ if (((OperationTypeProxy)b).AllowSeq) {
+ b.T = a; // a is a stronger constraint than b
+ } else {
+ // a says set<T>,seq<T> and b says int,set; the intersection is set<T>
+ a.T = new SetType(((CollectionTypeProxy)a).Arg);
+ b.T = a.T;
+ }
+ return true;
+ } else {
+ assert false; // unexpected restricted-proxy type
+ }
+
+ } else if (a is OperationTypeProxy) {
+ assert b is OperationTypeProxy; // else we we have unexpected restricted-proxy type
+ if (((OperationTypeProxy)a).AllowSeq ==> ((OperationTypeProxy)b).AllowSeq) {
+ b.T = a;
+ } else {
+ a.T = b; // b has the stronger requirement
+ }
+ return true;
+
+ } else {
+ assert false; // unexpected restricted-proxy type
+ }
+ }
+
+ public void ResolveStatement(Statement! stmt)
+ requires !(stmt is LabelStmt); // these should be handled inside lists of statements
+ {
+ if (stmt is UseStmt) {
+ UseStmt s = (UseStmt)stmt;
+ ResolveExpression(s.Expr, true);
+ assert s.Expr.Type != null; // follows from postcondition of ResolveExpression
+ Expression expr = s.Expr;
+ while (true) {
+ if (expr is OldExpr) {
+ expr = ((OldExpr)expr).E;
+ } else {
+ break;
+ }
+ }
+ FunctionCallExpr fce = expr as FunctionCallExpr;
+ if (fce == null || fce.Function == null || !fce.Function.Use) {
+ Error(s.Expr, "use statement must indicate a function declared as use");
+ }
+ } else if (stmt is PredicateStmt) {
+ PredicateStmt s = (PredicateStmt)stmt;
+ ResolveExpression(s.Expr, true);
+ assert s.Expr.Type != null; // follows from postcondition of ResolveExpression
+ if (!UnifyTypes(s.Expr.Type, Type.Bool)) {
+ Error(s.Expr, "condition is expected to be of type {0}, but is {1}", Type.Bool, s.Expr.Type);
+ }
+
+ } else if (stmt is BreakStmt) {
+ BreakStmt s = (BreakStmt)stmt;
+ if (s.TargetLabel != null) {
+ Statement target = labeledStatements.Find(s.TargetLabel);
+ if (target == null) {
+ Error(s, "break label is undefined or not in scope: {0}", s.TargetLabel);
+ } else {
+ s.TargetStmt = target;
+ }
+ }
+
+ } else if (stmt is ReturnStmt) {
+ // nothing to resolve
+
+ } else if (stmt is AssignStmt) {
+ AssignStmt s = (AssignStmt)stmt;
+ ResolveExpression(s.Lhs, true);
+ assert s.Lhs.Type != null; // follows from postcondition of ResolveExpression
+ // check that LHS denotes a mutable variable or a field
+ if (s.Lhs is IdentifierExpr) {
+ IVariable var = ((IdentifierExpr)s.Lhs).Var;
+ if (var == null) {
+ // the LHS didn't resolve correctly; some error would already have been reported
+ } else if (!var.IsMutable) {
+ Error(stmt, "LHS of assignment must denote a mutable variable or field");
+ }
+ } else if (s.Lhs is FieldSelectExpr) {
+ // LHS is fine, but restrict the RHS to ExprRhs
+ if (!(s.Rhs is ExprRhs)) {
+ Error(stmt, "Assignment to field must have an expression RHS; try using a temporary local variable");
+ }
+ } else {
+ Error(stmt, "LHS of assignment must denote a mutable variable or field");
+ }
+
+ if (s.Rhs is ExprRhs) {
+ ExprRhs rr = (ExprRhs)s.Rhs;
+ ResolveExpression(rr.Expr, true);
+ assert rr.Expr.Type != null; // follows from postcondition of ResolveExpression
+ if (!UnifyTypes(s.Lhs.Type, rr.Expr.Type)) {
+ Error(stmt, "RHS (of type {0}) not assignable to LHS (of type {1})", rr.Expr.Type, s.Lhs.Type);
+ }
+ } else if (s.Rhs is TypeRhs) {
+ TypeRhs rr = (TypeRhs)s.Rhs;
+ ResolveType(rr.Type);
+ if (!UnifyTypes(s.Lhs.Type, rr.Type)) {
+ Error(stmt, "type {0} is not assignable to LHS (of type {1})", rr.Type, s.Lhs.Type);
+ }
+ } else if (s.Rhs is HavocRhs) {
+ // nothing else to do
+ } else {
+ assert false; // unexpected RHS
+ }
+
+ } else if (stmt is VarDecl) {
+ VarDecl s = (VarDecl)stmt;
+ if (s.OptionalType != null) {
+ ResolveType(s.OptionalType);
+ s.type = s.OptionalType;
+ }
+ if (s.Rhs != null) {
+ Type! rhsType;
+ if (s.Rhs is ExprRhs) {
+ ExprRhs rr = (ExprRhs)s.Rhs;
+ ResolveExpression(rr.Expr, true);
+ assert rr.Expr.Type != null; // follows from postcondition of ResolveExpression
+ rhsType = rr.Expr.Type;
+ } else if (s.Rhs is TypeRhs) {
+ TypeRhs rr = (TypeRhs)s.Rhs;
+ ResolveType(rr.Type);
+ rhsType = rr.Type;
+ } else {
+ assert false; // unexpected RHS
+ }
+ if (s.OptionalType == null) {
+ s.type = rhsType;
+ } else if (!UnifyTypes(s.OptionalType, rhsType)) {
+ Error(stmt, "initialization RHS (of type {0}) not assignable to variable (of type {1})", rhsType, s.OptionalType);
+ }
+ }
+ // now that the declaration has been processed, add the name to the scope
+ if (!scope.Push(s.Name, s)) {
+ Error(s, "Duplicate local-variable name: {0}", s.Name);
+ }
+
+ } else if (stmt is CallStmt) {
+ CallStmt s = (CallStmt)stmt;
+
+ // resolve left-hand side
+ Dictionary<string!,object> lhsNameSet = new Dictionary<string!,object>();
+ foreach (IdentifierExpr lhs in s.Lhs) {
+ ResolveExpression(lhs, true);
+ if (lhsNameSet.ContainsKey(lhs.Name)) {
+ Error(s, "Duplicate variable in left-hand side of call statement: {0}", lhs.Name);
+ } else {
+ lhsNameSet.Add(lhs.Name, null);
+ }
+ }
+ // resolve receiver
+ ResolveExpression(s.Receiver, true);
+ assert s.Receiver.Type != null; // follows from postcondition of ResolveExpression
+ // resolve arguments
+ foreach (Expression e in s.Args) {
+ ResolveExpression(e, true);
+ }
+
+ // resolve the method name
+ ClassType ctype;
+ MemberDecl member = ResolveMember(s.Tok, s.Receiver.Type, s.MethodName, out ctype);
+ if (member == null) {
+ // error has already been reported by ResolveMember
+ } else if (!(member is Method)) {
+ Error(s, "member {0} in class {1} does not refer to a method", s.MethodName, ((!)ctype).Name);
+ } else {
+ Method method = (Method)member;
+ s.Method = method;
+ if (method.Ins.Count != s.Args.Count) {
+ Error(s, "wrong number of method arguments (got {0}, expected {1})", s.Args.Count, method.Ins.Count);
+ } else if (method.Outs.Count != s.Lhs.Count) {
+ Error(s, "wrong number of method result arguments (got {0}, expected {1})", s.Lhs.Count, method.Outs.Count);
+ } else {
+ assert ctype != null; // follows from postcondition of ResolveMember
+ // build the type substitution map
+ Dictionary<TypeParameter!,Type!> subst = new Dictionary<TypeParameter!,Type!>();
+ for (int i = 0; i < ctype.TypeArgs.Count; i++) {
+ subst.Add(((!)ctype.ResolvedClass).TypeArgs[i], ctype.TypeArgs[i]);
+ }
+ foreach (TypeParameter p in method.TypeArgs) {
+ subst.Add(p, new ParamTypeProxy(p));
+ }
+ // type check the arguments
+ for (int i = 0; i < method.Ins.Count; i++) {
+ UnifyTypes((!)s.Args[i].Type, SubstType(method.Ins[i].Type, subst));
+ }
+ for (int i = 0; i < method.Outs.Count; i++) {
+ UnifyTypes((!)s.Lhs[i].Type, SubstType(method.Outs[i].Type, subst));
+ }
+ }
+ }
+
+ } else if (stmt is BlockStmt) {
+ scope.PushMarker();
+ int labelsToPop = 0;
+ foreach (Statement ss in ((BlockStmt)stmt).Body) {
+ if (ss is LabelStmt) {
+ LabelStmt ls = (LabelStmt)ss;
+ labeledStatements.PushMarker();
+ bool b = labeledStatements.Push(ls.Label, ls);
+ assert b; // since we just pushed a marker, we expect the Push to succeed
+ labelsToPop++;
+ } else {
+ ResolveStatement(ss);
+ for (; 0 < labelsToPop; labelsToPop--) { labeledStatements.PopMarker(); }
+ }
+ }
+ for (; 0 < labelsToPop; labelsToPop--) { labeledStatements.PopMarker(); }
+ scope.PopMarker();
+
+ } else if (stmt is IfStmt) {
+ IfStmt s = (IfStmt)stmt;
+ if (s.Guard != null) {
+ ResolveExpression(s.Guard, true);
+ assert s.Guard.Type != null; // follows from postcondition of ResolveExpression
+ if (!UnifyTypes(s.Guard.Type, Type.Bool)) {
+ Error(s.Guard, "condition is expected to be of type {0}, but is {1}", Type.Bool, s.Guard.Type);
+ }
+ }
+ ResolveStatement(s.Thn);
+ if (s.Els != null) {
+ ResolveStatement(s.Els);
+ }
+
+ } else if (stmt is WhileStmt) {
+ WhileStmt s = (WhileStmt)stmt;
+ if (s.Guard != null) {
+ ResolveExpression(s.Guard, true);
+ assert s.Guard.Type != null; // follows from postcondition of ResolveExpression
+ if (!UnifyTypes(s.Guard.Type, Type.Bool)) {
+ Error(s.Guard, "condition is expected to be of type {0}, but is {1}", Type.Bool, s.Guard.Type);
+ }
+ }
+ foreach (MaybeFreeExpression inv in s.Invariants) {
+ ResolveExpression(inv.E, true);
+ assert inv.E.Type != null; // follows from postcondition of ResolveExpression
+ if (!UnifyTypes(inv.E.Type, Type.Bool)) {
+ Error(inv.E, "invariant is expected to be of type {0}, but is {1}", Type.Bool, inv.E.Type);
+ }
+ }
+ foreach (Expression e in s.Decreases) {
+ ResolveExpression(e, true);
+ // any type is fine
+ }
+ ResolveStatement(s.Body);
+
+ } else if (stmt is ForeachStmt) {
+ ForeachStmt s = (ForeachStmt)stmt;
+ scope.PushMarker();
+ bool b = scope.Push(s.BoundVar.Name, s.BoundVar);
+ assert b; // since we just pushed a marker, we expect the Push to succeed
+ ResolveType(s.BoundVar.Type);
+
+ ResolveExpression(s.Collection, true);
+ assert s.Collection.Type != null; // follows from postcondition of ResolveExpression
+ if (!UnifyTypes(s.Collection.Type, new CollectionTypeProxy(s.BoundVar.Type))) {
+ Error(s.Collection, "The type is expected to be a collection of {0} (instead got {1})", s.BoundVar.Type, s.Collection.Type);
+ }
+
+ ResolveExpression(s.Range, true);
+ assert s.Range.Type != null; // follows from postcondition of ResolveExpression
+ if (!UnifyTypes(s.Range.Type, Type.Bool)) {
+ Error(s.Range, "range condition is expected to be of type {0}, but is {1}", Type.Bool, s.Range.Type);
+ }
+
+ foreach (PredicateStmt ss in s.BodyPrefix) {
+ ResolveStatement(ss);
+ }
+ if (s.BodyAssign != null) {
+ ResolveStatement(s.BodyAssign);
+ // check for correct usage of BoundVar in LHS and RHS of this assignment
+ FieldSelectExpr lhs = s.BodyAssign.Lhs as FieldSelectExpr;
+ IdentifierExpr obj = lhs == null ? null : lhs.Obj as IdentifierExpr;
+ if (obj != null && obj.Var == s.BoundVar) {
+ // exemplary!
+ } else {
+ Error(s, "assignment inside foreach must assign to a field of the bound variable of the foreach statement");
+ }
+ }
+ scope.PopMarker();
+
+ } else {
+ assert false;
+ }
+ }
+
+ MemberDecl ResolveMember(Token! tok, Type! receiverType, string! memberName, out ClassType ctype)
+ ensures result != null ==> ctype != null && ctype.ResolvedClass != null;
+ {
+ ctype = ClassType.DenotesClass(receiverType);
+ if (ctype == null) {
+ Error(tok, "receiver (of type {0}) must be of a class type", receiverType);
+ } else {
+ assert ctype.ResolvedClass != null; // follows from postcondition of DenotesClass
+ assert ctype.TypeArgs.Count == ctype.ResolvedClass.TypeArgs.Count; // follows from the fact that ctype was resolved
+ MemberDecl member;
+ if (!classMembers[ctype.ResolvedClass].TryGetValue(memberName, out member)) {
+ Error(tok, "member {0} does not exist in class {1}", memberName, ctype.Name);
+ } else {
+ return (!)member;
+ }
+ }
+ ctype = null;
+ return null;
+ }
+
+ Type! SubstType(Type! type, Dictionary<TypeParameter!,Type!>! subst) {
+ if (type is BasicType) {
+ return type;
+ } else if (type is CollectionType) {
+ CollectionType t = (CollectionType)type;
+ Type arg = SubstType(t.Arg, subst);
+ if (arg == t.Arg) {
+ return type;
+ } else if (type is SetType) {
+ return new SetType(arg);
+ } else if (type is SeqType) {
+ return new SeqType(arg);
+ } else {
+ assert false; // unexpected collection type
+ }
+ } else if (type is ClassType) {
+ ClassType t = (ClassType)type;
+ if (t.ResolvedParam != null) {
+ assert t.TypeArgs.Count == 0;
+ Type s;
+ if (subst.TryGetValue(t.ResolvedParam, out s)) {
+ return (!)s;
+ } else {
+ return type;
+ }
+ } else if (t.ResolvedClass != null) {
+ List<Type!> newArgs = null; // allocate it lazily
+ for (int i = 0; i < t.TypeArgs.Count; i++) {
+ Type p = t.TypeArgs[i];
+ Type s = SubstType(p, subst);
+ if (s != p && newArgs == null) {
+ // lazily construct newArgs
+ newArgs = new List<Type!>();
+ for (int j = 0; j < i; j++) {
+ newArgs.Add(t.TypeArgs[j]);
+ }
+ }
+ if (newArgs != null) {
+ newArgs.Add(s);
+ }
+ }
+ if (newArgs == null) {
+ // there were no substitutions
+ return type;
+ } else {
+ return new ClassType(t.tok, t.Name, t.ResolvedClass, newArgs);
+ }
+ } else {
+ // there's neither a resolved param nor a resolved class, which means the ClassType wasn't
+ // properly resolved; just return it
+ return type;
+ }
+ } else if (type is TypeProxy) {
+ TypeProxy t = (TypeProxy)type;
+ if (t.T == null) {
+ return type;
+ } else {
+ // bypass the proxy
+ return SubstType(t.T, subst);
+ }
+ } else {
+ assert false; // unexpected type
+ }
+ }
+
+ public static ClassType! GetThisType(Token! tok, ClassDecl! cl) {
+ List<Type!> args = new List<Type!>();
+ foreach (TypeParameter tp in cl.TypeArgs) {
+ args.Add(new ClassType(tok, tp.Name, tp));
+ }
+ return new ClassType(tok, cl.Name, cl, args);
+ }
+
+ /// <summary>
+ /// "twoState" implies that "old" and "fresh" expressions are allowed
+ /// </summary>
+ void ResolveExpression(Expression! expr, bool twoState)
+ requires currentClass != null;
+ ensures expr.Type != null;
+ {
+ if (expr.Type != null) {
+ // expression has already been resovled
+ return;
+ }
+
+ // The following cases will resolve the subexpressions and will attempt to assign a type of expr. However, if errors occur
+ // and it cannot be determined what the type of expr is, then it is fine to leave expr.Type as null. In that case, the end
+ // of this method will assign proxy type to the expression, which reduces the number of error messages that are produced
+ // while type checking the rest of the program.
+
+ if (expr is LiteralExpr) {
+ LiteralExpr e = (LiteralExpr)expr;
+ if (e.Value == null) {
+ e.Type = new ObjectTypeProxy();
+ } else if (e.Value is int) {
+ e.Type = Type.Int;
+ } else if (e.Value is bool) {
+ e.Type = Type.Bool;
+ } else {
+ assert false; // unexpected literal type
+ }
+
+ } else if (expr is ThisExpr) {
+ expr.Type = GetThisType(expr.tok, currentClass);
+
+ } else if (expr is IdentifierExpr) {
+ IdentifierExpr e = (IdentifierExpr)expr;
+ e.Var = scope.Find(e.Name);
+ if (e.Var == null) {
+ Error(expr, "Identifier does not denote a local variable, parameter, or bound variable: {0}", e.Name);
+ } else {
+ expr.Type = e.Var.Type;
+ }
+
+ } else if (expr is DisplayExpression) {
+ DisplayExpression e = (DisplayExpression)expr;
+ Type elementType = new InferredTypeProxy();
+ foreach (Expression ee in e.Elements) {
+ ResolveExpression(ee, twoState);
+ assert ee.Type != null; // follows from postcondition of ResolveExpression
+ if (!UnifyTypes(elementType, ee.Type)) {
+ Error(ee, "All elements of display must be of the same type (got {0}, but type of previous elements is {1})", ee.Type, elementType);
+ }
+ }
+ if (expr is SetDisplayExpr) {
+ expr.Type = new SetType(elementType);
+ } else {
+ expr.Type = new SeqType(elementType);
+ }
+
+ } else if (expr is FieldSelectExpr) {
+ FieldSelectExpr e = (FieldSelectExpr)expr;
+ ResolveExpression(e.Obj, twoState);
+ assert e.Obj.Type != null; // follows from postcondition of ResolveExpression
+ ClassType ctype;
+ MemberDecl member = ResolveMember(expr.tok, e.Obj.Type, e.FieldName, out ctype);
+ if (member == null) {
+ // error has already been reported by ResolveMember
+ } else if (!(member is Field)) {
+ Error(expr, "member {0} in class {1} does not refer to a field", e.FieldName, ((!)ctype).Name);
+ } else {
+ assert ctype != null && ctype.ResolvedClass != null; // follows from postcondition of ResolveMember
+ e.Field = (Field)member;
+ // build the type substitution map
+ Dictionary<TypeParameter!,Type!> subst = new Dictionary<TypeParameter!,Type!>();
+ for (int i = 0; i < ctype.TypeArgs.Count; i++) {
+ subst.Add(ctype.ResolvedClass.TypeArgs[i], ctype.TypeArgs[i]);
+ }
+ e.Type = SubstType(e.Field.Type, subst);
+ }
+
+ } else if (expr is SeqSelectExpr) {
+ SeqSelectExpr e = (SeqSelectExpr)expr;
+ bool seqErr = false;
+ ResolveExpression(e.Seq, twoState);
+ assert e.Seq.Type != null; // follows from postcondition of ResolveExpression
+ Type elementType = new InferredTypeProxy();
+ if (!UnifyTypes(e.Seq.Type, new SeqType(elementType))) {
+ Error(expr, "sequence selection requires a sequence (got {0})", e.Seq.Type);
+ seqErr = true;
+ }
+ if (e.E0 != null) {
+ ResolveExpression(e.E0, twoState);
+ assert e.E0.Type != null; // follows from postcondition of ResolveExpression
+ if (!UnifyTypes(e.E0.Type, Type.Int)) {
+ Error(e.E0, "sequence selections requires integer indices (got {0})", e.E0.Type);
+ }
+ }
+ if (e.E1 != null) {
+ ResolveExpression(e.E1, twoState);
+ assert e.E1.Type != null; // follows from postcondition of ResolveExpression
+ if (!UnifyTypes(e.E1.Type, Type.Int)) {
+ Error(e.E1, "sequence selections requires integer indices (got {0})", e.E1.Type);
+ }
+ }
+ if (!seqErr) {
+ if (e.SelectOne) {
+ expr.Type = elementType;
+ } else {
+ expr.Type = e.Seq.Type;
+ }
+ }
+
+ } else if (expr is FunctionCallExpr) {
+ FunctionCallExpr e = (FunctionCallExpr)expr;
+ ResolveExpression(e.Receiver, twoState);
+ assert e.Receiver.Type != null; // follows from postcondition of ResolveExpression
+ ClassType ctype;
+ MemberDecl member = ResolveMember(expr.tok, e.Receiver.Type, e.Name, out ctype);
+ if (member == null) {
+ // error has already been reported by ResolveMember
+ } else if (!(member is Function)) {
+ Error(expr, "member {0} in class {1} does not refer to a function", e.Name, ((!)ctype).Name);
+ } else {
+ Function function = (Function)member;
+ e.Function = function;
+ if (function.Formals.Count != e.Args.Count) {
+ Error(expr, "wrong number of function arguments (got {0}, expected {1})", e.Args.Count, function.Formals.Count);
+ } else {
+ assert ctype != null; // follows from postcondition of ResolveMember
+ // build the type substitution map
+ Dictionary<TypeParameter!,Type!> subst = new Dictionary<TypeParameter!,Type!>();
+ for (int i = 0; i < ctype.TypeArgs.Count; i++) {
+ subst.Add(((!)ctype.ResolvedClass).TypeArgs[i], ctype.TypeArgs[i]);
+ }
+ foreach (TypeParameter p in function.TypeArgs) {
+ subst.Add(p, new ParamTypeProxy(p));
+ }
+ // type check the arguments
+ for (int i = 0; i < function.Formals.Count; i++) {
+ Expression farg = e.Args[i];
+ ResolveExpression(farg, twoState);
+ assert farg.Type != null; // follows from postcondition of ResolveExpression
+ UnifyTypes(farg.Type, SubstType(function.Formals[i].Type, subst));
+ }
+ expr.Type = SubstType(function.ResultType, subst);
+ }
+ }
+
+ } else if (expr is OldExpr) {
+ OldExpr e = (OldExpr)expr;
+ if (!twoState) {
+ Error(expr, "old expressions are not allowed in this context");
+ }
+ ResolveExpression(e.E, twoState);
+ expr.Type = e.E.Type;
+
+ } else if (expr is FreshExpr) {
+ FreshExpr e = (FreshExpr)expr;
+ if (!twoState) {
+ Error(expr, "fresh expressions are not allowed in this context");
+ }
+ ResolveExpression(e.E, twoState);
+ // the type of e.E must be either an object or a collection of objects
+ Type t = e.E.Type;
+ assert t != null; // follows from postcondition of ResolveExpression
+ if (t is CollectionType) {
+ t = ((CollectionType)t).Arg;
+ }
+ if (t is ObjectType) {
+ // fine
+ } else if (ClassType.DenotesClass(t) != null) {
+ // fine
+ } else {
+ Error(expr, "the argument of a fresh expression must denote an object or a collection of objects (instead got {0})", e.E.Type);
+ }
+ expr.Type = Type.Bool;
+
+ } else if (expr is UnaryExpr) {
+ UnaryExpr e = (UnaryExpr)expr;
+ ResolveExpression(e.E, twoState);
+ assert e.E.Type != null; // follows from postcondition of ResolveExpression
+ switch (e.Op) {
+ case UnaryExpr.Opcode.Not:
+ if (!UnifyTypes(e.E.Type, Type.Bool)) {
+ Error(expr, "logical negation expects a boolean argument (instead got {0})", e.E.Type);
+ }
+ expr.Type = Type.Bool;
+ break;
+ case UnaryExpr.Opcode.SeqLength:
+ if (!UnifyTypes(e.E.Type, new SeqType(new InferredTypeProxy()))) {
+ Error(expr, "sequence-length operator expects a sequence argument (instead got {0})", e.E.Type);
+ }
+ expr.Type = Type.Int;
+ break;
+ default:
+ assert false; // unexpected unary operator
+ }
+
+ } else if (expr is BinaryExpr) {
+ BinaryExpr e = (BinaryExpr)expr;
+ ResolveExpression(e.E0, twoState);
+ assert e.E0.Type != null; // follows from postcondition of ResolveExpression
+ ResolveExpression(e.E1, twoState);
+ assert e.E1.Type != null; // follows from postcondition of ResolveExpression
+ switch (e.Op) {
+ case BinaryExpr.Opcode.Iff:
+ case BinaryExpr.Opcode.Imp:
+ case BinaryExpr.Opcode.And:
+ case BinaryExpr.Opcode.Or:
+ if (!UnifyTypes(e.E0.Type, Type.Bool)) {
+ Error(expr, "first argument to {0} must be of type bool (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E0.Type);
+ }
+ if (!UnifyTypes(e.E1.Type, Type.Bool)) {
+ Error(expr, "second argument to {0} must be of type bool (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E1.Type);
+ }
+ expr.Type = Type.Bool;
+ break;
+
+ case BinaryExpr.Opcode.Eq:
+ case BinaryExpr.Opcode.Neq:
+ if (!UnifyTypes(e.E0.Type, e.E1.Type)) {
+ Error(expr, "arguments must have the same type (got {0} and {1})", e.E0.Type, e.E1.Type);
+ }
+ expr.Type = Type.Bool;
+ break;
+
+ case BinaryExpr.Opcode.Disjoint:
+ if (!UnifyTypes(e.E0.Type, new SetType(new InferredTypeProxy()))) {
+ Error(expr, "arguments must be of a set type (got {0})", e.E0.Type);
+ }
+ if (!UnifyTypes(e.E0.Type, e.E1.Type)) {
+ Error(expr, "arguments must have the same type (got {0} and {1})", e.E0.Type, e.E1.Type);
+ }
+ expr.Type = Type.Bool;
+ break;
+
+ case BinaryExpr.Opcode.Lt:
+ case BinaryExpr.Opcode.Le:
+ case BinaryExpr.Opcode.Add:
+ {
+ bool err = false;
+ if (!UnifyTypes(e.E0.Type, new OperationTypeProxy(true))) {
+ Error(expr, "arguments to {0} must be int or a collection type (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E0.Type);
+ err = true;
+ }
+ if (!UnifyTypes(e.E1.Type, e.E0.Type)) {
+ Error(expr, "arguments to {0} must have the same type (got {1} and {2})", BinaryExpr.OpcodeString(e.Op), e.E0.Type, e.E1.Type);
+ err = true;
+ }
+ if (e.Op != BinaryExpr.Opcode.Add) {
+ expr.Type = Type.Bool;
+ } else if (!err) {
+ expr.Type = e.E0.Type;
+ }
+ }
+ break;
+
+ case BinaryExpr.Opcode.Sub:
+ case BinaryExpr.Opcode.Mul:
+ case BinaryExpr.Opcode.Gt:
+ case BinaryExpr.Opcode.Ge:
+ {
+ bool err = false;
+ if (!UnifyTypes(e.E0.Type, new OperationTypeProxy(false))) {
+ Error(expr, "arguments to {0} must be int or a set (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E0.Type);
+ err = true;
+ }
+ if (!UnifyTypes(e.E1.Type, e.E0.Type)) {
+ Error(expr, "arguments to {0} must have the same type (got {1} and {2})", BinaryExpr.OpcodeString(e.Op), e.E0.Type, e.E1.Type);
+ err = true;
+ }
+ if (e.Op == BinaryExpr.Opcode.Gt || e.Op == BinaryExpr.Opcode.Ge) {
+ expr.Type = Type.Bool;
+ } else if (!err) {
+ expr.Type = e.E0.Type;
+ }
+ }
+ break;
+
+ case BinaryExpr.Opcode.In:
+ if (!UnifyTypes(e.E1.Type, new CollectionTypeProxy(e.E0.Type))) {
+ Error(expr, "second argument to {0} must be a set or sequence of type {1} (instead got {2})", BinaryExpr.OpcodeString(e.Op), e.E0.Type, e.E1.Type);
+ }
+ expr.Type = Type.Bool;
+ break;
+
+ case BinaryExpr.Opcode.Div:
+ case BinaryExpr.Opcode.Mod:
+ if (!UnifyTypes(e.E0.Type, Type.Int)) {
+ Error(expr, "first argument to {0} must be of type int (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E0.Type);
+ }
+ if (!UnifyTypes(e.E1.Type, Type.Int)) {
+ Error(expr, "second argument to {0} must be of type int (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E1.Type);
+ }
+ expr.Type = Type.Int;
+ break;
+
+ default:
+ assert false; // unexpected operator
+ }
+ e.ResolvedOp = ResolveOp(e.Op, e.E1.Type);
+
+ } else if (expr is QuantifierExpr) {
+ QuantifierExpr e = (QuantifierExpr)expr;
+ scope.PushMarker();
+ foreach (BoundVar v in e.BoundVars) {
+ if (!scope.Push(v.Name, v)) {
+ Error(v, "Duplicate parameter name: {0}", v.Name);
+ }
+ ResolveType(v.Type);
+ }
+ ResolveExpression(e.Body, twoState);
+ assert e.Body.Type != null; // follows from postcondition of ResolveExpression
+ if (!UnifyTypes(e.Body.Type, Type.Bool)) {
+ Error(expr, "body of quantifier must be of type bool (instead got {0})", e.Body.Type);
+ }
+ // Since the body is more likely to infer the types of the bound variables, resolve it
+ // first (above) and only then resolve the attributes and triggers (below).
+ ResolveAttributes(e.Attributes, twoState);
+ ResolveTriggers(e.Trigs, twoState);
+ scope.PopMarker();
+ expr.Type = Type.Bool;
+
+ } else if (expr is ITEExpr) {
+ ITEExpr e = (ITEExpr)expr;
+ assert !twoState;
+ ResolveExpression(e.Test, twoState);
+ assert e.Test.Type != null; // follows from postcondition of ResolveExpression
+ ResolveExpression(e.Thn, twoState);
+ assert e.Thn.Type != null; // follows from postcondition of ResolveExpression
+ ResolveExpression(e.Els, twoState);
+ assert e.Els.Type != null; // follows from postcondition of ResolveExpression
+ if (!UnifyTypes(e.Test.Type, Type.Bool)) {
+ Error(expr, "guard condition in if-then-else expression must be a boolean (instead got {0})", e.Test.Type);
+ }
+ if (!UnifyTypes(e.Thn.Type, Type.Bool)) {
+ Error(expr, "the then branch of an if-then-else expression must be a boolean (instead got {0})", e.Thn.Type);
+ }
+ if (!UnifyTypes(e.Els.Type, Type.Bool)) {
+ Error(expr, "the else branch of an if-then-else expression must be a boolean (instead got {0})", e.Els.Type);
+ }
+ expr.Type = Type.Bool;
+
+ } else {
+ assert false; // unexpected expression
+ }
+
+ if (expr.Type == null) {
+ // some resolution error occurred
+ expr.Type = Type.Flexible;
+ }
+ }
+
+ /// <summary>
+ /// Note: this method is allowed to be called even if "type" does not make sense for "op", as might be the case if
+ /// resolution of the binary expression failed. If so, an arbitrary resolved opcode is returned.
+ /// </summary>
+ BinaryExpr.ResolvedOpcode ResolveOp(BinaryExpr.Opcode op, Type! operandType) {
+ switch (op) {
+ case BinaryExpr.Opcode.Iff: return BinaryExpr.ResolvedOpcode.Iff;
+ case BinaryExpr.Opcode.Imp: return BinaryExpr.ResolvedOpcode.Imp;
+ case BinaryExpr.Opcode.And: return BinaryExpr.ResolvedOpcode.And;
+ case BinaryExpr.Opcode.Or: return BinaryExpr.ResolvedOpcode.Or;
+ case BinaryExpr.Opcode.Eq:
+ if (operandType is SetType) {
+ return BinaryExpr.ResolvedOpcode.SetEq;
+ } else if (operandType is SeqType) {
+ return BinaryExpr.ResolvedOpcode.SeqEq;
+ } else {
+ return BinaryExpr.ResolvedOpcode.EqCommon;
+ }
+ case BinaryExpr.Opcode.Neq:
+ if (operandType is SetType) {
+ return BinaryExpr.ResolvedOpcode.SetNeq;
+ } else if (operandType is SeqType) {
+ return BinaryExpr.ResolvedOpcode.SeqNeq;
+ } else {
+ return BinaryExpr.ResolvedOpcode.NeqCommon;
+ }
+ case BinaryExpr.Opcode.Disjoint: return BinaryExpr.ResolvedOpcode.Disjoint;
+ case BinaryExpr.Opcode.Lt:
+ if (operandType is SetType) {
+ return BinaryExpr.ResolvedOpcode.ProperSubset;
+ } else if (operandType is SeqType) {
+ return BinaryExpr.ResolvedOpcode.ProperPrefix;
+ } else {
+ return BinaryExpr.ResolvedOpcode.Lt;
+ }
+ case BinaryExpr.Opcode.Le:
+ if (operandType is SetType) {
+ return BinaryExpr.ResolvedOpcode.Subset;
+ } else if (operandType is SeqType) {
+ return BinaryExpr.ResolvedOpcode.Prefix;
+ } else {
+ return BinaryExpr.ResolvedOpcode.Le;
+ }
+ case BinaryExpr.Opcode.Add:
+ if (operandType is SetType) {
+ return BinaryExpr.ResolvedOpcode.Union;
+ } else if (operandType is SeqType) {
+ return BinaryExpr.ResolvedOpcode.Concat;
+ } else {
+ return BinaryExpr.ResolvedOpcode.Add;
+ }
+ case BinaryExpr.Opcode.Sub:
+ if (operandType is SetType) {
+ return BinaryExpr.ResolvedOpcode.SetDifference;
+ } else {
+ return BinaryExpr.ResolvedOpcode.Sub;
+ }
+ case BinaryExpr.Opcode.Mul:
+ if (operandType is SetType) {
+ return BinaryExpr.ResolvedOpcode.Intersection;
+ } else {
+ return BinaryExpr.ResolvedOpcode.Mul;
+ }
+ case BinaryExpr.Opcode.Gt:
+ if (operandType is SetType) {
+ return BinaryExpr.ResolvedOpcode.ProperSuperset;
+ } else {
+ return BinaryExpr.ResolvedOpcode.Gt;
+ }
+ case BinaryExpr.Opcode.Ge:
+ if (operandType is SetType) {
+ return BinaryExpr.ResolvedOpcode.Superset;
+ } else {
+ return BinaryExpr.ResolvedOpcode.Ge;
+ }
+ case BinaryExpr.Opcode.In:
+ if (operandType is SetType) {
+ return BinaryExpr.ResolvedOpcode.InSet;
+ } else {
+ return BinaryExpr.ResolvedOpcode.InSeq;
+ }
+ case BinaryExpr.Opcode.Div: return BinaryExpr.ResolvedOpcode.Div;
+ case BinaryExpr.Opcode.Mod: return BinaryExpr.ResolvedOpcode.Mod;
+ default:
+ assert false; // unexpected operator
+ }
+ }
+ }
+
+ class Scope<Thing> where Thing : class {
+ [Rep] readonly List<string>! names = new List<string>(); // a null means a marker
+ [Rep] readonly List<Thing?>! things = new List<Thing?>();
+ invariant names.Count == things.Count;
+
+ public void PushMarker() {
+ names.Add(null);
+ things.Add(null);
+ }
+
+ public void PopMarker() {
+ int n = names.Count;
+ while (true) {
+ n--;
+ if (names[n] == null) {
+ break;
+ }
+ }
+ names.RemoveRange(n, names.Count - n);
+ things.RemoveRange(n, things.Count - n);
+ }
+
+ // Pushes name-->var association and returns "true", if name has not already been pushed since the last marker.
+ // If name already has been pushed since the last marker, does nothing and returns "false".
+ public bool Push(string! name, Thing! thing) {
+ if (Find(name, true) != null) {
+ return false;
+ } else {
+ names.Add(name);
+ things.Add(thing);
+ return true;
+ }
+ }
+
+ Thing? Find(string! name, bool topScopeOnly) {
+ for (int n = names.Count; 0 <= --n; ) {
+ if (names[n] == null) {
+ if (topScopeOnly) {
+ return null; // no present
+ }
+ } else if (names[n] == name) {
+ Thing t = things[n];
+ assert t != null;
+ return t;
+ }
+ }
+ return null; // not present
+ }
+
+ public Thing? Find(string! name) {
+ return Find(name, false);
+ }
+ }
+}
diff --git a/Source/Dafny/Scanner.ssc b/Source/Dafny/Scanner.ssc
new file mode 100644
index 00000000..e026ca7e
--- /dev/null
+++ b/Source/Dafny/Scanner.ssc
@@ -0,0 +1,491 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System.IO;
+using System.Collections;
+using System.Collections.Generic;
+using System.Text;
+using Microsoft.Contracts;
+using Bpl = Microsoft.Boogie;
+using BoogiePL;
+
+
+namespace Microsoft.Dafny {
+
+ [Immutable]
+ public class Token : Bpl.Token {
+ public Token();
+ public Token(int linenum, int colnum) {
+ base(linenum, colnum);
+ }
+ public new static Token! NoToken = new Token();
+ }
+
+}
+
+namespace Microsoft.Dafny {
+
+
+public class Scanner {
+ const char EOF = '\0';
+ const char CR = '\r';
+ const char LF = '\n';
+
+ [Microsoft.Contracts.Verify(false)]
+ static Scanner() {
+ start[0] = 50;
+ start[33] = 31;
+ start[34] = 3;
+ start[37] = 40;
+ start[38] = 25;
+ start[39] = 1;
+ start[40] = 12;
+ start[41] = 13;
+ start[42] = 15;
+ start[43] = 37;
+ start[44] = 7;
+ start[45] = 38;
+ start[46] = 44;
+ start[47] = 39;
+ start[48] = 2;
+ start[49] = 2;
+ start[50] = 2;
+ start[51] = 2;
+ start[52] = 2;
+ start[53] = 2;
+ start[54] = 2;
+ start[55] = 2;
+ start[56] = 2;
+ start[57] = 2;
+ start[58] = 9;
+ start[59] = 8;
+ start[60] = 10;
+ start[61] = 21;
+ start[62] = 11;
+ start[63] = 1;
+ start[65] = 1;
+ start[66] = 1;
+ start[67] = 1;
+ start[68] = 1;
+ start[69] = 1;
+ start[70] = 1;
+ start[71] = 1;
+ start[72] = 1;
+ start[73] = 1;
+ start[74] = 1;
+ start[75] = 1;
+ start[76] = 1;
+ start[77] = 1;
+ start[78] = 1;
+ start[79] = 1;
+ start[80] = 1;
+ start[81] = 1;
+ start[82] = 1;
+ start[83] = 1;
+ start[84] = 1;
+ start[85] = 1;
+ start[86] = 1;
+ start[87] = 1;
+ start[88] = 1;
+ start[89] = 1;
+ start[90] = 1;
+ start[91] = 42;
+ start[92] = 1;
+ start[93] = 43;
+ start[95] = 1;
+ start[96] = 1;
+ start[97] = 1;
+ start[98] = 1;
+ start[99] = 1;
+ start[100] = 1;
+ start[101] = 1;
+ start[102] = 1;
+ start[103] = 1;
+ start[104] = 1;
+ start[105] = 1;
+ start[106] = 1;
+ start[107] = 1;
+ start[108] = 1;
+ start[109] = 1;
+ start[110] = 1;
+ start[111] = 1;
+ start[112] = 1;
+ start[113] = 1;
+ start[114] = 1;
+ start[115] = 1;
+ start[116] = 1;
+ start[117] = 1;
+ start[118] = 1;
+ start[119] = 1;
+ start[120] = 1;
+ start[121] = 1;
+ start[122] = 1;
+ start[123] = 5;
+ start[124] = 16;
+ start[125] = 6;
+ start[172] = 41;
+ start[8226] = 49;
+ start[8658] = 24;
+ start[8660] = 20;
+ start[8704] = 46;
+ start[8707] = 47;
+ start[8743] = 27;
+ start[8744] = 29;
+ start[8800] = 34;
+ start[8804] = 35;
+ start[8805] = 36;
+ }
+ const int noSym = 86;
+ 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)BoogiePL.Buffer.Read(); pos++;
+ if (ch == BoogiePL.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 = BoogiePL.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 (System.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 "class": t.kind = 4; break;
+ case "var": t.kind = 7; break;
+ case "frame": t.kind = 13; break;
+ case "method": t.kind = 14; break;
+ case "returns": t.kind = 15; break;
+ case "modifies": t.kind = 16; break;
+ case "free": t.kind = 17; break;
+ case "requires": t.kind = 18; break;
+ case "ensures": t.kind = 19; break;
+ case "bool": t.kind = 22; break;
+ case "int": t.kind = 23; break;
+ case "object": t.kind = 24; break;
+ case "set": t.kind = 25; break;
+ case "seq": t.kind = 26; break;
+ case "function": t.kind = 27; break;
+ case "use": t.kind = 28; break;
+ case "reads": t.kind = 29; break;
+ case "if": t.kind = 30; break;
+ case "else": t.kind = 31; break;
+ case "label": t.kind = 32; break;
+ case "break": t.kind = 33; break;
+ case "return": t.kind = 34; break;
+ case "new": t.kind = 36; break;
+ case "havoc": t.kind = 37; break;
+ case "while": t.kind = 38; break;
+ case "invariant": t.kind = 39; break;
+ case "decreases": t.kind = 40; break;
+ case "call": t.kind = 42; break;
+ case "foreach": t.kind = 43; break;
+ case "in": t.kind = 44; break;
+ case "assert": t.kind = 46; break;
+ case "assume": t.kind = 47; break;
+ case "false": t.kind = 70; break;
+ case "true": t.kind = 71; break;
+ case "null": t.kind = 72; break;
+ case "fresh": t.kind = 73; break;
+ case "this": t.kind = 78; break;
+ case "old": t.kind = 79; break;
+ case "forall": t.kind = 80; break;
+ case "exists": t.kind = 82; 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 == 39 || ch >= '0' && ch <= '9' || ch == '?' || ch >= 'A' && ch <= 'Z' || ch == 92 || ch >= '_' && ch <= 'z')) {buf.Append(ch); NextCh(); goto case 1;}
+ else {t.kind = 1; t.val = buf.ToString(); CheckLiteral(); return t;}
+ case 2:
+ if ((ch >= '0' && ch <= '9')) {buf.Append(ch); NextCh(); goto case 2;}
+ else {t.kind = 2; goto done;}
+ case 3:
+ if ((ch == '"')) {buf.Append(ch); NextCh(); goto case 4;}
+ else if ((ch >= ' ' && ch <= '!' || ch >= '#' && ch <= '~')) {buf.Append(ch); NextCh(); goto case 3;}
+ else {t.kind = noSym; goto done;}
+ case 4:
+ {t.kind = 3; goto done;}
+ case 5:
+ {t.kind = 5; goto done;}
+ case 6:
+ {t.kind = 6; goto done;}
+ case 7:
+ {t.kind = 8; goto done;}
+ case 8:
+ {t.kind = 9; goto done;}
+ case 9:
+ if (ch == '=') {buf.Append(ch); NextCh(); goto case 14;}
+ else if (ch == ':') {buf.Append(ch); NextCh(); goto case 48;}
+ else {t.kind = 10; goto done;}
+ case 10:
+ if (ch == '=') {buf.Append(ch); NextCh(); goto case 17;}
+ else {t.kind = 11; goto done;}
+ case 11:
+ if (ch == '=') {buf.Append(ch); NextCh(); goto case 30;}
+ else {t.kind = 12; goto done;}
+ case 12:
+ {t.kind = 20; goto done;}
+ case 13:
+ {t.kind = 21; goto done;}
+ case 14:
+ {t.kind = 35; goto done;}
+ case 15:
+ {t.kind = 41; goto done;}
+ case 16:
+ if (ch == '|') {buf.Append(ch); NextCh(); goto case 28;}
+ else {t.kind = 45; goto done;}
+ case 17:
+ if (ch == '=') {buf.Append(ch); NextCh(); goto case 18;}
+ else {t.kind = 57; goto done;}
+ case 18:
+ if (ch == '>') {buf.Append(ch); NextCh(); goto case 19;}
+ else {t.kind = noSym; goto done;}
+ case 19:
+ {t.kind = 48; goto done;}
+ case 20:
+ {t.kind = 49; goto done;}
+ case 21:
+ if (ch == '=') {buf.Append(ch); NextCh(); goto case 22;}
+ else {t.kind = noSym; goto done;}
+ case 22:
+ if (ch == '>') {buf.Append(ch); NextCh(); goto case 23;}
+ else {t.kind = 56; goto done;}
+ case 23:
+ {t.kind = 50; goto done;}
+ case 24:
+ {t.kind = 51; goto done;}
+ case 25:
+ if (ch == '&') {buf.Append(ch); NextCh(); goto case 26;}
+ else {t.kind = noSym; goto done;}
+ case 26:
+ {t.kind = 52; goto done;}
+ case 27:
+ {t.kind = 53; goto done;}
+ case 28:
+ {t.kind = 54; goto done;}
+ case 29:
+ {t.kind = 55; goto done;}
+ case 30:
+ {t.kind = 58; goto done;}
+ case 31:
+ if (ch == '=') {buf.Append(ch); NextCh(); goto case 32;}
+ else if (ch == '!') {buf.Append(ch); NextCh(); goto case 33;}
+ else {t.kind = 68; goto done;}
+ case 32:
+ {t.kind = 59; goto done;}
+ case 33:
+ {t.kind = 60; goto done;}
+ case 34:
+ {t.kind = 61; goto done;}
+ case 35:
+ {t.kind = 62; goto done;}
+ case 36:
+ {t.kind = 63; goto done;}
+ case 37:
+ {t.kind = 64; goto done;}
+ case 38:
+ {t.kind = 65; goto done;}
+ case 39:
+ {t.kind = 66; goto done;}
+ case 40:
+ {t.kind = 67; goto done;}
+ case 41:
+ {t.kind = 69; goto done;}
+ case 42:
+ {t.kind = 74; goto done;}
+ case 43:
+ {t.kind = 75; goto done;}
+ case 44:
+ if (ch == '.') {buf.Append(ch); NextCh(); goto case 45;}
+ else {t.kind = 76; goto done;}
+ case 45:
+ {t.kind = 77; goto done;}
+ case 46:
+ {t.kind = 81; goto done;}
+ case 47:
+ {t.kind = 83; goto done;}
+ case 48:
+ {t.kind = 84; goto done;}
+ case 49:
+ {t.kind = 85; goto done;}
+ case 50: {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
+ System.ConsoleColor color = System.Console.ForegroundColor;
+ System.Console.ForegroundColor = System.ConsoleColor.Red;
+ System.Console.WriteLine("{0}({1},{2}): Error: {3}", filename, line, col, msg);
+ System.Console.ForegroundColor = color;
+ count++;
+ }
+
+ public static void SemErr(Bpl.IToken! tok, string! msg) { // semantic errors
+ SemErr(tok.filename, tok.line, tok.col, msg);
+ }
+
+ public static void Exception (string s) {
+ System.ConsoleColor color = System.Console.ForegroundColor;
+ System.Console.ForegroundColor = System.ConsoleColor.Red;
+ System.Console.WriteLine(s);
+ System.Console.ForegroundColor = color;
+ System.Environment.Exit(0);
+ }
+
+} // Errors
+
+} // end namespace
diff --git a/Source/Dafny/Translator.ssc b/Source/Dafny/Translator.ssc
new file mode 100644
index 00000000..c5f85130
--- /dev/null
+++ b/Source/Dafny/Translator.ssc
@@ -0,0 +1,2446 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using Microsoft.Contracts;
+using Bpl = Microsoft.Boogie;
+
+// TODO: wellformedness checks for function bodies
+// TODO: totality checks for statements
+// TODO: totality checks for pre/post conditions
+
+namespace Microsoft.Dafny {
+ public class Translator {
+ public Translator() {
+ Bpl.Program boogieProgram = ReadPrelude();
+ if (boogieProgram != null) {
+ sink = boogieProgram;
+ predef = FindPredefinedDecls(boogieProgram);
+ }
+ }
+
+ // translation state
+ readonly Dictionary<ClassDecl!,Bpl.Constant!>! classes = new Dictionary<ClassDecl!,Bpl.Constant!>();
+ readonly Dictionary<Field!,Bpl.Constant!>! fields = new Dictionary<Field!,Bpl.Constant!>();
+ readonly Dictionary<Function!,Bpl.Function!>! functions = new Dictionary<Function!,Bpl.Function!>();
+ readonly Dictionary<Method!,Bpl.Procedure!>! methods = new Dictionary<Method!,Bpl.Procedure!>();
+
+ readonly Bpl.Program sink;
+ readonly PredefinedDecls predef;
+ internal class PredefinedDecls {
+ public readonly Bpl.Type! RefType;
+ private readonly Bpl.TypeCtorDecl! seqTypeCtor;
+ public Bpl.Type! SeqType(Token! tok, Bpl.Type! ty) {
+ return new Bpl.CtorType(Token.NoToken, seqTypeCtor, new Bpl.TypeSeq(ty));
+ }
+ readonly Bpl.TypeCtorDecl! fieldName;
+ public Bpl.Type! FieldName(Token! tok, Bpl.Type! ty) {
+ return new Bpl.CtorType(tok, fieldName, new Bpl.TypeSeq(ty));
+ }
+ public readonly Bpl.Type! HeapType;
+ public readonly Bpl.Type! ClassNameType;
+ public readonly Bpl.Expr! Null;
+ private readonly Bpl.Constant! allocField;
+ public Bpl.IdentifierExpr! Alloc(Token! tok) {
+ return new Bpl.IdentifierExpr(tok, allocField);
+ }
+
+ public PredefinedDecls(Bpl.TypeCtorDecl! refType, Bpl.TypeCtorDecl! seqTypeCtor, Bpl.TypeCtorDecl! fieldNameType,
+ Bpl.GlobalVariable! heap, Bpl.TypeCtorDecl! classNameType, Bpl.Constant! allocField) {
+ Bpl.CtorType refT = new Bpl.CtorType(Token.NoToken, refType, new Bpl.TypeSeq());
+ this.RefType = refT;
+ this.seqTypeCtor = seqTypeCtor;
+ this.fieldName = fieldNameType;
+ this.HeapType = heap.TypedIdent.Type;
+ this.ClassNameType = new Bpl.CtorType(Token.NoToken, classNameType, new Bpl.TypeSeq());
+ this.allocField = allocField;
+ this.Null = new Bpl.IdentifierExpr(Token.NoToken, "null", refT);
+ }
+ }
+
+ static PredefinedDecls FindPredefinedDecls(Bpl.Program! prog) {
+ if (prog.Resolve() != 0) {
+ Console.WriteLine("Error: resolution errors encountered in Dafny prelude");
+ return null;
+ }
+
+ Bpl.TypeCtorDecl refType = null;
+ Bpl.TypeCtorDecl seqTypeCtor = null;
+ Bpl.TypeCtorDecl fieldNameType = null;
+ Bpl.TypeCtorDecl classNameType = null;
+ Bpl.GlobalVariable heap = null;
+ Bpl.Constant allocField = null;
+ foreach (Bpl.Declaration d in prog.TopLevelDeclarations) {
+ if (d is Bpl.TypeCtorDecl) {
+ Bpl.TypeCtorDecl dt = (Bpl.TypeCtorDecl)d;
+ if (dt.Name == "Seq") {
+ seqTypeCtor = dt;
+ } else if (dt.Name == "Field") {
+ fieldNameType = dt;
+ } else if (dt.Name == "ClassName") {
+ classNameType = dt;
+ } else if (dt.Name == "ref") {
+ refType = dt;
+ }
+ } else if (d is Bpl.Constant) {
+ Bpl.Constant c = (Bpl.Constant)d;
+ if (c.Name == "alloc") {
+ allocField = c;
+ }
+ } else if (d is Bpl.GlobalVariable) {
+ Bpl.GlobalVariable v = (Bpl.GlobalVariable)d;
+ if (v.Name == "$Heap") {
+ heap = v;
+ }
+ }
+ }
+ if (seqTypeCtor == null) {
+ Console.WriteLine("Error: Dafny prelude is missing declaration of type Seq");
+ } else if (fieldNameType == null) {
+ Console.WriteLine("Error: Dafny prelude is missing declaration of type Field");
+ } else if (classNameType == null) {
+ Console.WriteLine("Error: Dafny prelude is missing declaration of type ClassName");
+ } else if (refType == null) {
+ Console.WriteLine("Error: Dafny prelude is missing declaration of type ref");
+ } else if (heap == null) {
+ Console.WriteLine("Error: Dafny prelude is missing declaration of $Heap");
+ } else if (allocField == null) {
+ Console.WriteLine("Error: Dafny prelude is missing declaration of constant alloc");
+ } else {
+ return new PredefinedDecls(refType, seqTypeCtor, fieldNameType, heap, classNameType, allocField);
+ }
+ return null;
+ }
+
+ static Bpl.Program ReadPrelude() {
+ //using (System.IO.Stream stream = (!) System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("DafnyPrelude.bpl")) // Use this once Spec#/VSIP supports designating a non-.resx project item as an embedded resource
+ string! codebase = (!) System.IO.Path.GetDirectoryName((!)System.Reflection.Assembly.GetExecutingAssembly().Location);
+ string! preludePath = System.IO.Path.Combine(codebase, "DafnyPrelude.bpl");
+ List<string!> defines = new List<string!>();
+ using (System.IO.Stream stream = new System.IO.FileStream(preludePath, System.IO.FileMode.Open, System.IO.FileAccess.Read))
+ {
+ BoogiePL.Buffer.Fill(new System.IO.StreamReader(stream), defines);
+ BoogiePL.Scanner.Init("<DafnyPrelude.bpl>");
+ Bpl.Program prelude;
+ int errorCount = BoogiePL.Parser.Parse(out prelude);
+ if (prelude == null || errorCount > 0) {
+ return null;
+ } else {
+ return prelude;
+ }
+ }
+ }
+
+ public Bpl.Program! Translate(Program! program) {
+ if (sink == null || predef == null) {
+ // something went wrong during construction, which reads the prelude; an error has
+ // already been printed, so just return an empty program here (which is non-null)
+ return new Bpl.Program();
+ }
+ foreach (ClassDecl c in program.Classes) {
+ AddClassMembers(c);
+ }
+ foreach (ClassDecl c in program.Classes) {
+ foreach (MemberDecl member in c.Members) {
+ if (member is Method) {
+ Method m = (Method)member;
+ if (m.Body != null) {
+ AddMethodImpl(m);
+ }
+ } else if (member is Function) {
+ Function f = (Function)member;
+ AddFrameAxiom(f);
+ // TODO: also need a well-formedness check for the preconditions
+ if (f.Body != null) {
+ AddWellformednessCheck(f);
+ }
+ }
+ }
+ }
+ return sink;
+ }
+
+ void AddClassMembers(ClassDecl! c)
+ requires sink != null && predef != null;
+ {
+ sink.TopLevelDeclarations.Add(GetClass(c));
+
+ foreach (MemberDecl member in c.Members) {
+ if (member is Field) {
+ Field f = (Field)member;
+ Bpl.Constant fc = GetField(f);
+ sink.TopLevelDeclarations.Add(fc);
+
+ AddAllocationAxiom(f);
+
+ } else if (member is Function) {
+ Function f = (Function)member;
+ Bpl.Function useFunc;
+ Bpl.Function func = GetFunction(f, out useFunc);
+ sink.TopLevelDeclarations.Add(func);
+ if (useFunc != null) {
+ sink.TopLevelDeclarations.Add(useFunc);
+ }
+ if (f.Body != null) {
+ ExpressionTranslator etran = new ExpressionTranslator(this, predef, f.tok);
+
+ // axiom (forall $Heap, formals :: f#use(formals) && this != null && $IsHeap($Heap) && Pre($Heap,formals) ==> f(formals) == body)
+ // The antecedent f#use(formals) is included only if the function has been declared as 'use'.
+ // Note, an antecedent $Heap[this,alloc] is intentionally left out: including it would only weaken
+ // the axiom. Moreover, leaving it out does not introduce any soundness problem, because the Dafny
+ // allocation statement changes only an allocation bit and then re-assumes $IsGoodHeap; so if it is
+ // sound after that, then it would also have been sound just before the allocation.
+ Bpl.VariableSeq formals = new Bpl.VariableSeq();
+ Bpl.ExprSeq args = new Bpl.ExprSeq();
+ Bpl.BoundVariable bv = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "$Heap", predef.HeapType));
+ formals.Add(bv);
+ args.Add(new Bpl.IdentifierExpr(f.tok, bv));
+ Bpl.BoundVariable bvThis = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "this", predef.RefType));
+ formals.Add(bvThis);
+ Bpl.Expr bvThisIdExpr = new Bpl.IdentifierExpr(f.tok, bvThis);
+ args.Add(bvThisIdExpr);
+ foreach (Formal p in f.Formals) {
+ bv = new Bpl.BoundVariable(p.tok, new Bpl.TypedIdent(p.tok, p.UniqueName, TrType(p.Type)));
+ formals.Add(bv);
+ args.Add(new Bpl.IdentifierExpr(p.tok, bv));
+ }
+ Bpl.Expr ante = Bpl.Expr.And(
+ Bpl.Expr.Neq(bvThisIdExpr, predef.Null),
+ FunctionCall(f.tok, BuiltinFunction.IsGoodHeap, null, etran.HeapExpr));
+ foreach (Expression req in f.Req) {
+ ante = Bpl.Expr.And(ante, etran.TrExpr(req));
+ }
+ Bpl.FunctionCall funcID = new Bpl.FunctionCall(new Bpl.IdentifierExpr(f.tok, f.FullName, TrType(f.ResultType)));
+ Bpl.Expr funcAppl = new Bpl.NAryExpr(f.tok, funcID, args);
+ Bpl.Trigger tr;
+ if (f.Use) {
+ Bpl.FunctionCall useID = new Bpl.FunctionCall(new Bpl.IdentifierExpr(f.tok, f.FullName + "#use", TrType(f.ResultType)));
+ Bpl.Expr useAppl = new Bpl.NAryExpr(f.tok, useID, args);
+ ante = Bpl.Expr.And(useAppl, ante);
+ tr = new Bpl.Trigger(f.tok, true, new Bpl.ExprSeq(useAppl));
+ } else {
+ tr = new Bpl.Trigger(f.tok, true, new Bpl.ExprSeq(funcAppl));
+ }
+ Bpl.TypeVariableSeq typeParams = TrTypeParamDecls(f.TypeArgs);
+ Bpl.Expr ax = new Bpl.ForallExpr(f.tok, typeParams, formals, null, tr, Bpl.Expr.Imp(ante, Bpl.Expr.Eq(funcAppl, etran.TrExpr(f.Body))));
+ sink.TopLevelDeclarations.Add(new Bpl.Axiom(f.tok, ax));
+ }
+
+ } else if (member is Method) {
+ Method m = (Method)member;
+ Bpl.Procedure proc = GetMethod(m);
+ sink.TopLevelDeclarations.Add(proc);
+
+ } else {
+ assert false; // unexpected member
+ }
+ }
+ }
+
+ void AddAllocationAxiom(Field! f)
+ requires sink != null && predef != null;
+ {
+ if (f.Type is BoolType || f.Type is IntType || f.Type.IsTypeParameter) {
+ return;
+ }
+
+ Bpl.BoundVariable hVar = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "$h", predef.HeapType));
+ Bpl.Expr h = new Bpl.IdentifierExpr(f.tok, hVar);
+ ExpressionTranslator etran = new ExpressionTranslator(this, predef, h);
+ Bpl.BoundVariable oVar = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "$o", predef.RefType));
+ Bpl.Expr o = new Bpl.IdentifierExpr(f.tok, oVar);
+
+ // h[o,f]
+ Bpl.Expr oDotF = Bpl.Expr.SelectTok(f.tok, h, o, new Bpl.IdentifierExpr(f.tok, GetField(f)));
+ // $IsGoodHeap(h) && o != null && h[o,alloc]
+ Bpl.Expr ante = Bpl.Expr.And(Bpl.Expr.And(
+ FunctionCall(f.tok, BuiltinFunction.IsGoodHeap, null, h),
+ Bpl.Expr.Neq(o, predef.Null)),
+ etran.IsAlloced(f.tok, o));
+
+ if (f.Type is SetType) {
+ SetType st = (SetType)f.Type;
+ if (st.Arg.IsRefType) {
+ // axiom (forall h: [ref, Field x]x, o: ref, t: ref ::
+ // { h[o,f][t] }
+ // $IsGoodHeap(h) && o != null && h[o,alloc] && h[o,f][t] ==> t == null || h[t, alloc]);
+ Bpl.BoundVariable tVar = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "$t", predef.RefType));
+ Bpl.Expr t = new Bpl.IdentifierExpr(f.tok, tVar);
+ Bpl.Expr oDotFsubT = Bpl.Expr.SelectTok(f.tok, oDotF, t);
+
+ Bpl.Trigger tr = new Bpl.Trigger(f.tok, true, new Bpl.ExprSeq(oDotFsubT));
+
+ Bpl.Expr goodRef = etran.GoodRef(f.tok, t, st.Arg);
+ Bpl.Expr body = Bpl.Expr.Imp(Bpl.Expr.And(ante, oDotFsubT), Bpl.Expr.Or(Bpl.Expr.Eq(t, predef.Null), goodRef));
+ Bpl.Expr ax = new Bpl.ForallExpr(f.tok, new Bpl.VariableSeq(hVar, oVar, tVar), tr, body);
+ sink.TopLevelDeclarations.Add(new Bpl.Axiom(f.tok, ax));
+ } else {
+ // TODO: should also handle sets of sets, etc. This will probably require some extra predicates, and these predicates may even replace the above treatment of sets.
+ }
+
+ } else if (f.Type is SeqType) {
+ SeqType st = (SeqType)f.Type;
+ if (st.Arg.IsRefType) {
+ // axiom (forall h: [ref, Field x]x, o: ref, i: int ::
+ // { Seq#Index(h[o,f], i) }
+ // $IsGoodHeap(h) && o != null && h[o,alloc] && 0 <= i && i < Seq#Length(h[o,f]) ==> Seq#Index(h[o,f], i) == null || h[Seq#Index(h[o,f], i), alloc]);
+ Bpl.BoundVariable iVar = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "$i", Bpl.Type.Int));
+ Bpl.Expr i = new Bpl.IdentifierExpr(f.tok, iVar);
+ Bpl.Expr oDotFsubI = FunctionCall(f.tok, BuiltinFunction.SeqIndex, predef.RefType, oDotF, i);
+
+ Bpl.Expr range = InSeqRange(f.tok, i, oDotF, null, false);
+
+ Bpl.Trigger tr = new Bpl.Trigger(f.tok, true, new Bpl.ExprSeq(oDotFsubI));
+
+ Bpl.Expr goodRef = etran.GoodRef(f.tok, oDotFsubI, st.Arg);
+ Bpl.Expr body = Bpl.Expr.Imp(Bpl.Expr.And(ante, range), Bpl.Expr.Or(Bpl.Expr.Eq(oDotFsubI, predef.Null), goodRef));
+ Bpl.Expr ax = new Bpl.ForallExpr(f.tok, new Bpl.VariableSeq(hVar, oVar, iVar), tr, body);
+ sink.TopLevelDeclarations.Add(new Bpl.Axiom(f.tok, ax));
+ } else {
+ // TODO: should also handle sequences of sequences, etc. This will probably require some extra predicates, and these predicates may even replace the above treatment of sequences.
+ }
+
+ } else {
+ // reference type:
+ // axiom (forall h: [ref, Field x]x, o: ref ::
+ // { h[o,f] }
+ // $IsGoodHeap(h) && o != null && h[o,alloc] ==> h[o,f] == null || h[h[o,f], alloc]);
+ Bpl.Trigger tr = new Bpl.Trigger(f.tok, true, new Bpl.ExprSeq(oDotF));
+
+ Bpl.Expr goodRef = etran.GoodRef(f.tok, oDotF, f.Type);
+ Bpl.Expr body = Bpl.Expr.Imp(ante, Bpl.Expr.Or(Bpl.Expr.Eq(oDotF, predef.Null), goodRef));
+ Bpl.Expr ax = new Bpl.ForallExpr(f.tok, new Bpl.VariableSeq(hVar, oVar), tr, body);
+ sink.TopLevelDeclarations.Add(new Bpl.Axiom(f.tok, ax));
+ }
+ }
+
+ Bpl.Expr! InSeqRange(Token! tok, Bpl.Expr! index, Bpl.Expr! seq, Bpl.Expr lowerBound, bool includeUpperBound) {
+ if (lowerBound == null) {
+ lowerBound = Bpl.Expr.Literal(0);
+ }
+ Bpl.Expr lower = Bpl.Expr.Le(lowerBound, index);
+ Bpl.Expr upper;
+ if (includeUpperBound) {
+ upper = Bpl.Expr.Le(index, FunctionCall(tok, BuiltinFunction.SeqLength, null, seq));
+ } else {
+ upper = Bpl.Expr.Lt(index, FunctionCall(tok, BuiltinFunction.SeqLength, null, seq));
+ }
+ return Bpl.Expr.And(lower, upper);
+ }
+
+ Method currentMethod = null; // the method whose implementation is currently being translated
+ int loopHeapVarCount = 0;
+ int otherTmpVarCount = 0;
+ Bpl.IdentifierExpr _phvie = null;
+ Bpl.IdentifierExpr! GetPrevHeapVar_IdExpr(Token! tok, Bpl.VariableSeq! locals) // local variable that's shared between statements that need it
+ requires predef != null;
+ {
+ if (_phvie == null) {
+ // the "tok" of the first request for this variable is the one we use
+ Bpl.LocalVariable prevHeapVar = new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, "$prevHeap", predef.HeapType));
+ locals.Add(prevHeapVar);
+ _phvie = new Bpl.IdentifierExpr(tok, prevHeapVar);
+ }
+ return _phvie;
+ }
+ Bpl.IdentifierExpr _nwie = null;
+ Bpl.IdentifierExpr! GetNewVar_IdExpr(Token! tok, Bpl.VariableSeq! locals) // local variable that's shared between statements that need it
+ requires predef != null;
+ {
+ if (_nwie == null) {
+ // the "tok" of the first request for this variable is the one we use
+ Bpl.LocalVariable nwVar = new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, "$nw", predef.RefType)); // important: no where clause (that's why we're going through the trouble of setting of this variable in the first place)
+ locals.Add(nwVar);
+ _nwie = new Bpl.IdentifierExpr(tok, nwVar);
+ }
+ return _nwie;
+ }
+
+ void AddMethodImpl(Method! m)
+ requires sink != null && predef != null && m.Body != null;
+ requires currentMethod == null && loopHeapVarCount == 0 && _phvie == null && _nwie == null;
+ ensures currentMethod == null && loopHeapVarCount == 0 && _phvie == null && _nwie == null;
+ {
+ Bpl.Procedure proc = GetMethod(m);
+ currentMethod = m;
+
+ Bpl.VariableSeq localVariables = new Bpl.VariableSeq();
+
+ Bpl.StmtList stmts = TrStmt2StmtList(m.Body, localVariables, new ExpressionTranslator(this, predef, m.tok));
+ Bpl.TypeVariableSeq typeParams = TrTypeParamDecls(m.TypeArgs);
+ Bpl.Implementation impl = new Bpl.Implementation(m.tok, proc.Name,
+ typeParams,
+ Bpl.Formal.StripWhereClauses(proc.InParams),
+ Bpl.Formal.StripWhereClauses(proc.OutParams),
+ localVariables, stmts);
+ sink.TopLevelDeclarations.Add(impl);
+
+ currentMethod = null;
+ loopHeapVarCount = 0;
+ otherTmpVarCount = 0;
+ _phvie = null;
+ _nwie = null;
+ }
+
+ /// <summary>
+ /// Generates:
+ /// axiom (forall h0: [ref, Field x]x, h1: [ref, Field x]x, formals... ::
+ /// { F(h0,formals), F(h1,formals) }
+ /// (forall(alpha) o: ref, f: Field alpha :: o != null AND h0[o,alloc] AND o in reads clause of formals in h0 IMPLIES h0[o,f] == h1[o,f]) AND
+ /// (forall(alpha) o: ref, f: Field alpha :: o != null AND h1[o,alloc] AND o in reads clause of formals in h1 IMPLIES h0[o,f] == h1[o,f])
+ /// IMPLIES
+ /// F(h0,formals) == F(h1,formals)
+ /// );
+ /// </summary>
+ void AddFrameAxiom(Function! f)
+ requires sink != null && predef != null;
+ {
+ Bpl.BoundVariable h0Var = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "$h0", predef.HeapType));
+ Bpl.BoundVariable h1Var = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "$h1", predef.HeapType));
+ Bpl.Expr h0 = new Bpl.IdentifierExpr(f.tok, h0Var);
+ Bpl.Expr h1 = new Bpl.IdentifierExpr(f.tok, h1Var);
+ ExpressionTranslator etran0 = new ExpressionTranslator(this, predef, h0);
+ ExpressionTranslator etran1 = new ExpressionTranslator(this, predef, h1);
+
+ Bpl.TypeVariable alpha = new Bpl.TypeVariable(f.tok, "alpha");
+ Bpl.BoundVariable oVar = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "$o", predef.RefType));
+ Bpl.Expr o = new Bpl.IdentifierExpr(f.tok, oVar);
+ Bpl.BoundVariable fieldVar = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "$f", predef.FieldName(f.tok, alpha)));
+ Bpl.Expr field = new Bpl.IdentifierExpr(f.tok, fieldVar);
+ Bpl.Expr oNotNull = Bpl.Expr.Neq(o, predef.Null);
+ Bpl.Expr oNotNullAlloced0 = Bpl.Expr.And(oNotNull, etran0.IsAlloced(f.tok, o));
+ Bpl.Expr oNotNullAlloced1 = Bpl.Expr.And(oNotNull, etran1.IsAlloced(f.tok, o));
+ Bpl.Expr unchanged = Bpl.Expr.Eq(Bpl.Expr.SelectTok(f.tok, h0, o, field), Bpl.Expr.SelectTok(f.tok, h1, o, field));
+
+ Bpl.Expr r0 = InRWClause(f.tok, o, f.Reads, etran0);
+ Bpl.Expr r1 = InRWClause(f.tok, o, f.Reads, etran1);
+ Bpl.Expr q0 = new Bpl.ForallExpr(f.tok, new Bpl.TypeVariableSeq(alpha), new Bpl.VariableSeq(oVar, fieldVar),
+ Bpl.Expr.Imp(Bpl.Expr.And(oNotNullAlloced0, r0), unchanged));
+ Bpl.Expr q1 = new Bpl.ForallExpr(f.tok, new Bpl.TypeVariableSeq(alpha), new Bpl.VariableSeq(oVar, fieldVar),
+ Bpl.Expr.Imp(Bpl.Expr.And(oNotNullAlloced1, r1), unchanged));
+
+ // bvars: h0, h1, formals
+ // f0args: h0, formals
+ // f1args: h1, formals
+ Bpl.VariableSeq bvars = new Bpl.VariableSeq();
+ Bpl.ExprSeq f0args = new Bpl.ExprSeq();
+ Bpl.ExprSeq f1args = new Bpl.ExprSeq();
+ Bpl.BoundVariable thVar = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "this", predef.RefType));
+ Bpl.Expr th = new Bpl.IdentifierExpr(f.tok, thVar);
+ bvars.Add(h0Var); bvars.Add(h1Var); bvars.Add(thVar);
+ f0args.Add(h0); f0args.Add(th);
+ f1args.Add(h1); f1args.Add(th);
+ foreach (Formal p in f.Formals) {
+ Bpl.BoundVariable bv = new Bpl.BoundVariable(p.tok, new Bpl.TypedIdent(p.tok, p.UniqueName, TrType(p.Type)));
+ bvars.Add(bv);
+ Bpl.Expr formal = new Bpl.IdentifierExpr(p.tok, bv);
+ f0args.Add(formal);
+ f1args.Add(formal);
+ }
+
+ Bpl.FunctionCall fn = new Bpl.FunctionCall(new Bpl.IdentifierExpr(f.tok, f.FullName, TrType(f.ResultType)));
+ Bpl.Expr F0 = new Bpl.NAryExpr(f.tok, fn, f0args);
+ Bpl.Expr F1 = new Bpl.NAryExpr(f.tok, fn, f1args);
+ Bpl.Expr eq = Bpl.Expr.Eq(F0, F1);
+ Bpl.Trigger tr = new Bpl.Trigger(f.tok, true, new Bpl.ExprSeq(F0, F1));
+
+ Bpl.TypeVariableSeq typeParams = TrTypeParamDecls(f.TypeArgs);
+ Bpl.Expr ax = new Bpl.ForallExpr(f.tok, typeParams, bvars, null, tr, Bpl.Expr.Imp(Bpl.Expr.And(q0, q1), eq));
+ sink.TopLevelDeclarations.Add(new Bpl.Axiom(f.tok, ax, "frame axiom for " + f.FullName));
+ }
+
+ Bpl.Expr! InRWClause(Token! tok, Bpl.Expr! o, List<Expression!>! rw, ExpressionTranslator! etran) {
+ Bpl.Expr disjunction = null;
+ foreach (Expression e in rw) {
+ Bpl.Expr disjunct;
+ if (e.Type is SetType) {
+ // old(e)[o]
+ disjunct = etran.TrInSet(tok, o, e, null);
+ } else if (e.Type is SeqType) {
+ // (exists i: int :: 0 <= i && i < Seq#Length(old(e)) ==> Seq#Index(old(e),i) == o)
+ Bpl.Variable iVar = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$i", Bpl.Type.Int));
+ Bpl.Expr i = new Bpl.IdentifierExpr(tok, iVar);
+ Bpl.Expr iBounds = InSeqRange(tok, i, etran.TrExpr(e), null, false);
+ Bpl.Expr XsubI = FunctionCall(tok, BuiltinFunction.SeqIndex, TrType(((SeqType)e.Type).Arg), etran.TrExpr(e), i);
+ // TODO: the equality in the next line should be changed to one that understands extensionality
+ disjunct = new Bpl.ExistsExpr(tok, new Bpl.VariableSeq(iVar), Bpl.Expr.Imp(iBounds, Bpl.Expr.Eq(XsubI, o)));
+ } else {
+ // o == old(e)
+ disjunct = Bpl.Expr.Eq(o, etran.TrExpr(e));
+ }
+ disjunct = Bpl.Expr.And(IsTotal(e, etran), disjunct);
+ if (disjunction == null) {
+ disjunction = disjunct;
+ } else {
+ disjunction = Bpl.Expr.Or(disjunction, disjunct);
+ }
+ }
+ if (disjunction == null) {
+ return Bpl.Expr.False;
+ } else {
+ return disjunction;
+ }
+ }
+
+ void AddWellformednessCheck(Function! f)
+ requires sink != null && predef != null;
+ requires f.Body != null;
+ {
+ ExpressionTranslator etran = new ExpressionTranslator(this, predef, f.tok);
+ Bpl.VariableSeq inParams = new Bpl.VariableSeq();
+ Bpl.Expr wh = Bpl.Expr.And(
+ Bpl.Expr.Neq(new Bpl.IdentifierExpr(f.tok, "this", predef.RefType), predef.Null),
+ etran.GoodRef(f.tok, new Bpl.IdentifierExpr(f.tok, "this", predef.RefType), Resolver.GetThisType(f.tok, (!)f.EnclosingClass)));
+ Bpl.Formal thVar = new Bpl.Formal(f.tok, new Bpl.TypedIdent(f.tok, "this", predef.RefType, wh), true);
+ inParams.Add(thVar);
+ foreach (Formal p in f.Formals) {
+ Bpl.Type varType = TrType(p.Type);
+ wh = GetWhereClause(p.tok, new Bpl.IdentifierExpr(p.tok, p.UniqueName, varType), p.Type, etran);
+ inParams.Add(new Bpl.Formal(p.tok, new Bpl.TypedIdent(p.tok, p.UniqueName, varType, wh), true));
+ }
+ Bpl.TypeVariableSeq typeParams = TrTypeParamDecls(f.TypeArgs);
+ Bpl.Procedure proc = new Bpl.Procedure(f.tok, "CheckWellformed$$" + f.FullName, typeParams, inParams, new Bpl.VariableSeq(),
+ new Bpl.RequiresSeq(), new Bpl.IdentifierExprSeq(), new Bpl.EnsuresSeq());
+ sink.TopLevelDeclarations.Add(proc);
+
+ Bpl.VariableSeq localVariables = new Bpl.VariableSeq();
+ Bpl.StmtListBuilder builder = new Bpl.StmtListBuilder();
+
+ CheckWellformed(f.Body, f, Position.Positive, builder, etran);
+
+ Bpl.Implementation impl = new Bpl.Implementation(f.tok, proc.Name,
+ typeParams,
+ Bpl.Formal.StripWhereClauses(proc.InParams),
+ Bpl.Formal.StripWhereClauses(proc.OutParams),
+ localVariables, builder.Collect(f.tok));
+ sink.TopLevelDeclarations.Add(impl);
+ }
+
+ Bpl.Expr! IsTotal(Expression! expr, ExpressionTranslator! etran)
+ requires predef != null;
+ {
+ if (expr is LiteralExpr || expr is ThisExpr || expr is IdentifierExpr) {
+ return Bpl.Expr.True;
+ } else if (expr is DisplayExpression) {
+ DisplayExpression e = (DisplayExpression)expr;
+ return IsTotal(e.Elements, etran);
+ } else if (expr is FieldSelectExpr) {
+ FieldSelectExpr e = (FieldSelectExpr)expr;
+ if (e.Obj is ThisExpr) {
+ return Bpl.Expr.True;
+ } else {
+ return Bpl.Expr.And(IsTotal(e.Obj, etran), Bpl.Expr.Neq(etran.TrExpr(e.Obj), predef.Null));
+ }
+ } else if (expr is SeqSelectExpr) {
+ SeqSelectExpr e = (SeqSelectExpr)expr;
+ Bpl.Expr total = IsTotal(e.Seq, etran);
+ Bpl.Expr seq = etran.TrExpr(e.Seq);
+ Bpl.Expr e0 = null;
+ if (e.E0 != null) {
+ e0 = etran.TrExpr(e.E0);
+ total = BplAnd(total, IsTotal(e.E0, etran));
+ total = BplAnd(total, InSeqRange(expr.tok, e0, seq, null, !e.SelectOne));
+ }
+ if (e.E1 != null) {
+ total = BplAnd(total, IsTotal(e.E1, etran));
+ total = BplAnd(total, InSeqRange(expr.tok, etran.TrExpr(e.E1), seq, e0, true));
+ }
+ return total;
+ } else if (expr is FunctionCallExpr) {
+ FunctionCallExpr e = (FunctionCallExpr)expr;
+ Bpl.Expr r = IsTotal(e.Receiver, etran);
+ if (!(e.Receiver is ThisExpr)) {
+ r = BplAnd(r, Bpl.Expr.Neq(etran.TrExpr(e.Receiver), predef.Null));
+ }
+ // TODO: check reads permissions and check preconditions
+ return BplAnd(r, IsTotal(e.Args, etran));
+ } else if (expr is OldExpr) {
+ OldExpr e = (OldExpr)expr;
+ return new Bpl.OldExpr(expr.tok, IsTotal(e.E, etran));
+ } else if (expr is FreshExpr) {
+ FreshExpr e = (FreshExpr)expr;
+ return IsTotal(e.E, etran);
+ } else if (expr is UnaryExpr) {
+ UnaryExpr e = (UnaryExpr)expr;
+ return IsTotal(e.E, etran);
+ } else if (expr is BinaryExpr) {
+ BinaryExpr e = (BinaryExpr)expr;
+ Bpl.Expr t0 = IsTotal(e.E0, etran);
+ Bpl.Expr t1 = IsTotal(e.E1, etran);
+ Bpl.Expr z = null;
+ switch (e.ResolvedOp) {
+ case BinaryExpr.ResolvedOpcode.And:
+ case BinaryExpr.ResolvedOpcode.Imp:
+ t1 = Bpl.Expr.Imp(etran.TrExpr(e.E0), t1);
+ break;
+ case BinaryExpr.ResolvedOpcode.Or:
+ t1 = Bpl.Expr.Imp(Bpl.Expr.Not(etran.TrExpr(e.E0)), t1);
+ break;
+ case BinaryExpr.ResolvedOpcode.Div:
+ z = Bpl.Expr.Neq(etran.TrExpr(e.E1), Bpl.Expr.Literal(0));
+ break;
+ default:
+ break;
+ }
+ Bpl.Expr r = BplAnd(t0, t1);
+ return z == null ? r : BplAnd(r, z);
+ } else if (expr is QuantifierExpr) {
+ QuantifierExpr e = (QuantifierExpr)expr;
+ Bpl.Expr total = IsTotal(e.Body, etran);
+ if (total != Bpl.Expr.True) {
+ Bpl.VariableSeq bvars = new Bpl.VariableSeq();
+ foreach (BoundVar bv in e.BoundVars) {
+ bvars.Add(new Bpl.BoundVariable(bv.tok, new Bpl.TypedIdent(bv.tok, bv.UniqueName, TrType(bv.Type))));
+ }
+ total = new Bpl.ForallExpr(expr.tok, bvars, total);
+ }
+ return total;
+ } else if (expr is ITEExpr) {
+ ITEExpr e = (ITEExpr)expr;
+ Bpl.Expr total = IsTotal(e.Test, etran);
+ Bpl.Expr test = etran.TrExpr(e.Test);
+ total = BplAnd(total, Bpl.Expr.Imp(test, IsTotal(e.Thn, etran)));
+ total = BplAnd(total, Bpl.Expr.Imp(Bpl.Expr.Not(test), IsTotal(e.Els, etran)));
+ return total;
+ } else {
+ assert false; // unexpected expression
+ }
+ }
+
+ Bpl.Expr! IsTotal(List<Expression!>! exprs, ExpressionTranslator! etran) {
+ Bpl.Expr total = Bpl.Expr.True;
+ foreach (Expression e in exprs) {
+ total = BplAnd(total, IsTotal(e, etran));
+ }
+ return total;
+ }
+
+ Bpl.Expr! BplAnd(Bpl.Expr! a, Bpl.Expr! b) {
+ if (a == Bpl.Expr.True) {
+ return b;
+ } else if (b == Bpl.Expr.True) {
+ return a;
+ } else {
+ return Bpl.Expr.And(a, b);
+ }
+ }
+
+ enum Position { Positive, Negative, Neither }
+ Position Negate(Position pos) {
+ switch (pos) {
+ case Position.Positive: return Position.Negative;
+ case Position.Negative: return Position.Positive;
+ case Position.Neither: return Position.Neither;
+ default: assert false; // unexpected Position
+ }
+ }
+
+ void CheckNonNull(Token! tok, Expression! e, Bpl.StmtListBuilder! builder, ExpressionTranslator! etran)
+ requires predef != null;
+ {
+ if (e is ThisExpr) {
+ // already known to be non-null
+ } else {
+ builder.Add(Assert(tok, Bpl.Expr.Neq(etran.TrExpr(e), predef.Null), "target object may be null"));
+ }
+ }
+
+ void CheckWellformed(Expression! expr, Function func, Position pos, Bpl.StmtListBuilder! builder, ExpressionTranslator! etran) {
+ if (expr is LiteralExpr || expr is ThisExpr || expr is IdentifierExpr) {
+ // always allowed
+ } else if (expr is DisplayExpression) {
+ DisplayExpression e = (DisplayExpression)expr;
+ foreach (Expression el in e.Elements) {
+ CheckWellformed(el, func, Position.Neither, builder, etran);
+ }
+ } else if (expr is FieldSelectExpr) {
+ FieldSelectExpr e = (FieldSelectExpr)expr;
+ CheckWellformed(e.Obj, func, Position.Neither, builder, etran);
+ CheckNonNull(expr.tok, e.Obj, builder, etran);
+ if (func != null) {
+ builder.Add(Assert(expr.tok, InRWClause(expr.tok, etran.TrExpr(e.Obj), func.Reads, etran), "insufficient reads clause to read field"));
+ }
+ } else if (expr is SeqSelectExpr) {
+ SeqSelectExpr e = (SeqSelectExpr)expr;
+ CheckWellformed(e.Seq, func, Position.Neither, builder, etran);
+ Bpl.Expr seq = etran.TrExpr(e.Seq);
+ Bpl.Expr e0 = null;
+ if (e.E0 != null) {
+ e0 = etran.TrExpr(e.E0);
+ CheckWellformed(e.E0, func, Position.Neither, builder, etran);
+ builder.Add(new Bpl.AssertCmd(expr.tok, InSeqRange(expr.tok, e0, seq, null, !e.SelectOne)));
+ }
+ if (e.E1 != null) {
+ CheckWellformed(e.E1, func, Position.Neither, builder, etran);
+ builder.Add(new Bpl.AssertCmd(expr.tok, InSeqRange(expr.tok, etran.TrExpr(e.E1), seq, e0, true)));
+ }
+ } else if (expr is FunctionCallExpr) {
+ FunctionCallExpr e = (FunctionCallExpr)expr;
+ CheckWellformed(e.Receiver, func, Position.Neither, builder, etran);
+ CheckNonNull(expr.tok, e.Receiver, builder, etran);
+ foreach (Expression arg in e.Args) {
+ CheckWellformed(arg, func, Position.Neither, builder, etran);
+ }
+ // TODO: check wellformedness of call (call.reads is subset of reads, and either e.Function returns a boolean and is in a positive position, or e.Function returns something else and the subset is a proper subset)
+ // TODO: and check preconditions of call
+ } else if (expr is OldExpr || expr is FreshExpr) {
+ assert false; // unexpected expression in function body
+ } else if (expr is UnaryExpr) {
+ UnaryExpr e = (UnaryExpr)expr;
+ CheckWellformed(e.E, func, e.Op == UnaryExpr.Opcode.Not ? Negate(pos) : Position.Neither, builder, etran);
+ } else if (expr is BinaryExpr) {
+ BinaryExpr e = (BinaryExpr)expr;
+ CheckWellformed(e.E0, func, e.ResolvedOp == BinaryExpr.ResolvedOpcode.Imp ? Negate(pos) : pos, builder, etran);
+
+ switch (e.ResolvedOp) {
+ case BinaryExpr.ResolvedOpcode.And:
+ case BinaryExpr.ResolvedOpcode.Imp:
+ {
+ Bpl.StmtListBuilder b = new Bpl.StmtListBuilder();
+ CheckWellformed(e.E1, func, pos, b, etran);
+ builder.Add(new Bpl.IfCmd(expr.tok, etran.TrExpr(e.E0), b.Collect(expr.tok), null, null));
+ }
+ break;
+ case BinaryExpr.ResolvedOpcode.Or:
+ {
+ Bpl.StmtListBuilder b = new Bpl.StmtListBuilder();
+ CheckWellformed(e.E1, func, pos, b, etran);
+ builder.Add(new Bpl.IfCmd(expr.tok, Bpl.Expr.Not(etran.TrExpr(e.E0)), b.Collect(expr.tok), null, null));
+ }
+ break;
+ default:
+ CheckWellformed(e.E1, func, pos, builder, etran);
+ break;
+ }
+
+ } else if (expr is QuantifierExpr) {
+#if TODO
+ QuantifierExpr e = (QuantifierExpr)expr;
+ Bpl.Expr total = IsTotal(e.Body, etran);
+ if (total != Bpl.Expr.True) {
+ Bpl.VariableSeq bvars = new Bpl.VariableSeq();
+ foreach (BoundVar bv in e.BoundVars) {
+ // TODO: the following needs to take into account name clashes, for which the Boogie rules are different from the Dafny rules
+ bvars.Add(new Bpl.BoundVariable(bv.tok, new Bpl.TypedIdent(bv.tok, bv.UniqueName, TrType(bv.Type))));
+ }
+ if (expr is ForallExpr) {
+ total = new Bpl.ForallExpr(expr.tok, bvars, total);
+ } else {
+ total = new Bpl.ExistsExpr(expr.tok, bvars, total);
+ }
+ }
+ return total;
+#endif
+ } else if (expr is ITEExpr) {
+#if TODO
+ ITEExpr e = (ITEExpr)expr;
+ Bpl.Expr total = IsTotal(e.Test, etran);
+ total = BplAnd(total, IsTotal(e.Thn, etran));
+ total = BplAnd(total, IsTotal(e.Els, etran));
+ return total;
+#endif
+ } else {
+ assert false; // unexpected expression
+ }
+ }
+
+ Bpl.Constant! GetClass(ClassDecl! cl)
+ requires predef != null;
+ {
+ Bpl.Constant cc;
+ if (classes.TryGetValue(cl, out cc)) {
+ assert cc != null;
+ } else {
+ // TODO: make the following a function when the class has type parameters, so that different instantiations
+ // of a class can be distinguished
+ cc = new Bpl.Constant(cl.tok, new Bpl.TypedIdent(cl.tok, "class." + cl.Name, predef.ClassNameType), true);
+ classes.Add(cl, cc);
+ }
+ return cc;
+ }
+
+ Bpl.Expr GetTypeExpr(Token! tok, Type! type)
+ requires predef != null;
+ {
+ while (true) {
+ TypeProxy tp = type as TypeProxy;
+ if (tp == null) {
+ break;
+ } else if (tp.T == null) {
+ // unresolved proxy
+ // TODO: what to do here?
+ return null;
+ } else {
+ type = tp.T;
+ }
+ }
+
+ if (type is BoolType) {
+ return new Bpl.IdentifierExpr(tok, "class.bool", predef.ClassNameType);
+ } else if (type is IntType) {
+ return new Bpl.IdentifierExpr(tok, "class.int", predef.ClassNameType);
+ } else if (type is ObjectType) {
+ return new Bpl.IdentifierExpr(tok, "class.object", predef.ClassNameType);
+ } else if (type is CollectionType) {
+ CollectionType ct = (CollectionType)type;
+ Bpl.Expr a = GetTypeExpr(tok, ct.Arg);
+ if (a == null) {
+ return null;
+ }
+ Bpl.Expr t = new Bpl.IdentifierExpr(tok, ct is SetType ? "class.set" : "class.seq", predef.ClassNameType);
+ return FunctionCall(tok, BuiltinFunction.TypeTuple, null, t, a);
+ } else {
+ ClassType ct = (ClassType)type;
+ if (ct.ResolvedClass == null) {
+ return null; // TODO: what to do here?
+ }
+ Bpl.Expr t = new Bpl.IdentifierExpr(tok, GetClass(ct.ResolvedClass));
+ foreach (Type arg in ct.TypeArgs) {
+ Bpl.Expr a = GetTypeExpr(tok, arg);
+ if (a == null) {
+ return null;
+ }
+ t = FunctionCall(tok, BuiltinFunction.TypeTuple, null, t, a);
+ }
+ return t;
+ }
+ }
+
+ Bpl.Constant! GetField(Field! f)
+ requires predef != null;
+ {
+ Bpl.Constant fc;
+ if (fields.TryGetValue(f, out fc)) {
+ assert fc != null;
+ } else {
+ Bpl.Type ty = predef.FieldName(f.tok, TrType(f.Type));
+ fc = new Bpl.Constant(f.tok, new Bpl.TypedIdent(f.tok, f.FullName, ty), true);
+ fields.Add(f, fc);
+ }
+ return fc;
+ }
+
+ Bpl.Expr! GetField(FieldSelectExpr! fse)
+ requires fse.Field != null;
+ {
+ return new Bpl.IdentifierExpr(fse.tok, GetField(fse.Field));
+ }
+
+ Bpl.Function! GetFunction(Function! f, out Bpl.Function useF)
+ requires predef != null;
+ {
+ useF = null;
+ Bpl.Function func;
+ if (functions.TryGetValue(f, out func)) {
+ assert func != null;
+ } else {
+ Bpl.TypeVariableSeq typeParams = TrTypeParamDecls(f.TypeArgs);
+ Bpl.VariableSeq args = new Bpl.VariableSeq();
+ args.Add(new Bpl.Formal(f.tok, new Bpl.TypedIdent(f.tok, "$heap", predef.HeapType), true));
+ args.Add(new Bpl.Formal(f.tok, new Bpl.TypedIdent(f.tok, "this", predef.RefType), true));
+ foreach (Formal p in f.Formals) {
+ args.Add(new Bpl.Formal(p.tok, new Bpl.TypedIdent(p.tok, p.UniqueName, TrType(p.Type)), true));
+ }
+ Bpl.Formal res = new Bpl.Formal(f.tok, new Bpl.TypedIdent(f.tok, Bpl.TypedIdent.NoName, TrType(f.ResultType)), false);
+ func = new Bpl.Function(f.tok, f.FullName, typeParams, args, res);
+
+ functions.Add(f, func);
+
+ if (f.Use) {
+ Bpl.Formal boolRes = new Bpl.Formal(f.tok, new Bpl.TypedIdent(f.tok, Bpl.TypedIdent.NoName, Bpl.Type.Bool), false);
+ useF = new Bpl.Function(f.tok, f.FullName + "#use", args, boolRes);
+ }
+ }
+ return func;
+ }
+
+ Bpl.Procedure! GetMethod(Method! m)
+ requires predef != null;
+ {
+ Bpl.Procedure proc;
+ if (methods.TryGetValue(m, out proc)) {
+ assert proc != null;
+ } else {
+ ExpressionTranslator etran = new ExpressionTranslator(this, predef, m.tok);
+
+ Bpl.VariableSeq inParams = new Bpl.VariableSeq();
+ Bpl.VariableSeq outParams = new Bpl.VariableSeq();
+ Bpl.Expr wh = Bpl.Expr.And(
+ Bpl.Expr.Neq(new Bpl.IdentifierExpr(m.tok, "this", predef.RefType), predef.Null),
+ etran.GoodRef(m.tok, new Bpl.IdentifierExpr(m.tok, "this", predef.RefType), Resolver.GetThisType(m.tok, (!)m.EnclosingClass)));
+ Bpl.Formal thVar = new Bpl.Formal(m.tok, new Bpl.TypedIdent(m.tok, "this", predef.RefType, wh), true);
+ inParams.Add(thVar);
+ foreach (Formal p in m.Ins) {
+ Bpl.Type varType = TrType(p.Type);
+ wh = GetWhereClause(p.tok, new Bpl.IdentifierExpr(p.tok, p.UniqueName, varType), p.Type, etran);
+ inParams.Add(new Bpl.Formal(p.tok, new Bpl.TypedIdent(p.tok, p.UniqueName, varType, wh), true));
+ }
+ foreach (Formal p in m.Outs) {
+ Bpl.Type varType = TrType(p.Type);
+ wh = GetWhereClause(p.tok, new Bpl.IdentifierExpr(p.tok, p.UniqueName, varType), p.Type, etran);
+ outParams.Add(new Bpl.Formal(p.tok, new Bpl.TypedIdent(p.tok, p.UniqueName, varType, wh), false));
+ }
+
+ Bpl.RequiresSeq req = new Bpl.RequiresSeq();
+ Bpl.IdentifierExprSeq mod = new Bpl.IdentifierExprSeq();
+ Bpl.EnsuresSeq ens = new Bpl.EnsuresSeq();
+ mod.Add(etran.HeapExpr);
+ string comment = "user-defined preconditions";
+ foreach (MaybeFreeExpression p in m.Req) {
+ Bpl.RequiresSeq pieces = new Bpl.RequiresSeq();
+ if (!p.IsFree) {
+ foreach (Expression se in SplitExpr(p.E, true)) {
+ pieces.Add(Requires(se.tok, false, etran.TrExpr(se), null, null));
+ }
+ }
+ if (pieces.Length == 1) {
+ // add 1 checked precondition (the whole thing)
+ req.Add(Requires(p.E.tok, false, etran.TrExpr(p.E), null, comment));
+ } else {
+ // add 1 free precondition, followed by each piece (if any) as a checked precondition
+ req.Add(Requires(p.E.tok, true, etran.TrExpr(p.E), null, comment));
+ req.AddRange(pieces);
+ }
+ comment = null;
+ }
+ comment = "user-defined postconditions";
+ foreach (MaybeFreeExpression p in m.Ens) {
+ Bpl.EnsuresSeq pieces = new Bpl.EnsuresSeq();
+ if (!p.IsFree) {
+ foreach (Expression se in SplitExpr(p.E, true)) {
+ pieces.Add(Ensures(se.tok, false, etran.TrExpr(se), null, null));
+ }
+ }
+ if (pieces.Length == 1) {
+ ens.Add(Ensures(p.E.tok, false, etran.TrExpr(p.E), comment, null));
+ } else {
+ ens.Add(Ensures(p.E.tok, true, etran.TrExpr(p.E), comment, null));
+ ens.AddRange(pieces);
+ }
+ comment = null;
+ }
+
+ foreach (BoilerplateTriple tri in GetTwoStateBoilerplate(m.tok, m, etran.Old, etran)) {
+ ens.Add(Ensures(tri.tok, tri.IsFree, tri.Expr, tri.ErrorMessage, tri.Comment));
+ }
+
+ Bpl.TypeVariableSeq typeParams = TrTypeParamDecls(m.TypeArgs);
+ proc = new Bpl.Procedure(m.tok, m.FullName, typeParams, inParams, outParams, req, mod, ens);
+
+ methods.Add(m, proc);
+ }
+ return proc;
+ }
+
+ class BoilerplateTriple { // a triple that is now a quintuple
+ public readonly Token! tok;
+ public readonly bool IsFree;
+ public readonly Bpl.Expr! Expr;
+ public readonly string ErrorMessage;
+ invariant IsFree || ErrorMessage != null;
+ public readonly string Comment;
+ public BoilerplateTriple(Token! tok, bool isFree, Bpl.Expr! expr, string errorMessage, string comment)
+ requires isFree || errorMessage != null;
+ {
+ this.tok = tok;
+ IsFree = isFree;
+ Expr = expr;
+ ErrorMessage = errorMessage;
+ Comment = comment;
+ }
+ }
+
+ /// <summary>
+ /// There are 3 states of interest when generating two-state boilerplate:
+ /// S0. the beginning of the method, which is where the modifies clause is interpreted
+ /// S1. the pre-state of the two-state interval
+ /// S2. the post-state of the two-state interval
+ /// This method assumes that etranPre denotes S1, etran denotes S2, and that etran.Old denotes S0.
+ /// </summary>
+ List<BoilerplateTriple!>! GetTwoStateBoilerplate(Token! tok, Method! method, ExpressionTranslator! etranPre, ExpressionTranslator! etran)
+ {
+ List<BoilerplateTriple!> boilerplate = new List<BoilerplateTriple!>();
+
+ boilerplate.Add(new BoilerplateTriple(tok, false, FrameCondition(tok, method.Mod, etranPre, etran), "frame condition does not hold", "frame condition"));
+
+ // free specifications
+ Bpl.Expr heapSucc = FunctionCall(tok, BuiltinFunction.HeapSucc, null, etranPre.HeapExpr, etran.HeapExpr);
+ boilerplate.Add(new BoilerplateTriple(tok, true, heapSucc, null, "boilerplate"));
+
+ return boilerplate;
+ }
+
+ /// <summary>
+ /// There are 3 states of interest when generating a freame condition:
+ /// S0. the beginning of the method, which is where the modifies clause is interpreted
+ /// S1. the pre-state of the two-state interval
+ /// S2. the post-state of the two-state interval
+ /// This method assumes that etranPre denotes S1, etran denotes S2, and that etran.Old denotes S0.
+ /// </summary>
+ Bpl.Expr! FrameCondition(Token! tok, List<Expression!>! modifiesClause, ExpressionTranslator! etranPre, ExpressionTranslator! etran)
+ requires predef != null;
+ {
+ // generate:
+ // (forall<alpha> o: ref, f: Field alpha :: { $Heap[o,f] }
+ // o != null && old($Heap)[o,alloc] ==>
+ // $Heap[o,f] == PreHeap[o,f] ||
+ // o in modifiesClause)
+ Bpl.TypeVariable alpha = new Bpl.TypeVariable(tok, "alpha");
+ Bpl.BoundVariable oVar = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$o", predef.RefType));
+ Bpl.IdentifierExpr o = new Bpl.IdentifierExpr(tok, oVar);
+ Bpl.BoundVariable fVar = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$f", predef.FieldName(tok, alpha)));
+ Bpl.IdentifierExpr f = new Bpl.IdentifierExpr(tok, fVar);
+
+ Bpl.Expr heapOF = Bpl.Expr.SelectTok(tok, etran.HeapExpr, o, f);
+ Bpl.Expr preHeapOF = Bpl.Expr.SelectTok(tok, etranPre.HeapExpr, o, f);
+ Bpl.Expr ante = Bpl.Expr.And(Bpl.Expr.Neq(o, predef.Null), etran.Old.IsAlloced(tok, o));
+ Bpl.Expr consequent = Bpl.Expr.Eq(heapOF, preHeapOF);
+
+ consequent = Bpl.Expr.Or(consequent, InRWClause(tok, o, modifiesClause, etran.Old));
+
+ Bpl.Trigger tr = new Bpl.Trigger(tok, true, new Bpl.ExprSeq(heapOF));
+ return new Bpl.ForallExpr(tok, new Bpl.TypeVariableSeq(alpha), new Bpl.VariableSeq(oVar, fVar), null, tr, Bpl.Expr.Imp(ante, consequent));
+ }
+
+ // ----- Type ---------------------------------------------------------------------------------
+
+ Bpl.Type! TrType(Type! type)
+ requires predef != null;
+ {
+ while (true) {
+ TypeProxy tp = type as TypeProxy;
+ if (tp == null) {
+ break;
+ } else if (tp.T == null) {
+ // unresolved proxy; just treat as ref, since no particular type information is apparently needed for this type
+ return predef.RefType;
+ } else {
+ type = tp.T;
+ }
+ }
+
+ if (type is BoolType) {
+ return Bpl.Type.Bool;
+ } else if (type is IntType) {
+ return Bpl.Type.Int;
+ } else if (type.IsTypeParameter) {
+ return predef.RefType;
+ } else if (type.IsRefType) {
+ // object and class types translate to ref
+ return predef.RefType;
+ } else if (type is SetType) {
+ return new Bpl.MapType(Token.NoToken, new Bpl.TypeVariableSeq(), new Bpl.TypeSeq(TrType(((SetType)type).Arg)), Bpl.Type.Bool);
+ } else if (type is SeqType) {
+ return predef.SeqType(Token.NoToken, TrType(((SeqType)type).Arg));
+ } else {
+ assert false; // unexpected type
+ }
+ }
+
+ Bpl.TypeVariableSeq! TrTypeParamDecls(List<TypeParameter!>! tps)
+ {
+ Bpl.TypeVariableSeq typeParams = new Bpl.TypeVariableSeq();
+ return typeParams;
+ }
+
+ // ----- Statement ----------------------------------------------------------------------------
+
+ Bpl.AssertCmd! Assert(Token! tok, Bpl.Expr! condition, string! errorMessage)
+ {
+ Bpl.AssertCmd cmd = new Bpl.AssertCmd(tok, condition);
+ cmd.ErrorData = "Error: " + errorMessage;
+ return cmd;
+ }
+
+ Bpl.Ensures! Ensures(Token! tok, bool free, Bpl.Expr! condition, string errorMessage, string comment)
+ {
+ Bpl.Ensures ens = new Bpl.Ensures(tok, free, condition, comment);
+ if (errorMessage != null) {
+ ens.ErrorData = errorMessage;
+ }
+ return ens;
+ }
+
+ Bpl.Requires! Requires(Token! tok, bool free, Bpl.Expr! condition, string errorMessage, string comment)
+ {
+ Bpl.Requires req = new Bpl.Requires(tok, free, condition, comment);
+ if (errorMessage != null) {
+ req.ErrorData = errorMessage;
+ }
+ return req;
+ }
+
+ Bpl.StmtList! TrStmt2StmtList(Statement! block, Bpl.VariableSeq! locals, ExpressionTranslator! etran)
+ requires currentMethod != null && predef != null;
+ {
+ Bpl.StmtListBuilder builder = new Bpl.StmtListBuilder();
+ TrStmt(block, builder, locals, etran);
+ return builder.Collect(block.Tok); // TODO: would be nice to have an end-curly location for "block"
+ }
+
+ void TrStmt(Statement! stmt, Bpl.StmtListBuilder! builder, Bpl.VariableSeq! locals, ExpressionTranslator! etran)
+ requires currentMethod != null && predef != null;
+ {
+ if (stmt is AssertStmt) {
+ AddComment(builder, stmt, "assert statement");
+ AssertStmt s = (AssertStmt)stmt;
+ int pieces = 0;
+ foreach (Expression p in SplitExpr(s.Expr, true)) {
+ builder.Add(Assert(stmt.Tok, IsTotal(p, etran), "assert condition must be well defined")); // totality check
+ builder.Add(new Bpl.AssertCmd(stmt.Tok, etran.TrExpr(p)));
+ pieces++;
+ }
+ if (2 <= pieces) {
+ builder.Add(new Bpl.AssumeCmd(stmt.Tok, etran.TrExpr(s.Expr)));
+ }
+ } else if (stmt is AssumeStmt) {
+ AddComment(builder, stmt, "assume statement");
+ AssumeStmt s = (AssumeStmt)stmt;
+ builder.Add(Assert(stmt.Tok, IsTotal(s.Expr, etran), "assume condition must be well defined")); // totality check
+ builder.Add(new Bpl.AssumeCmd(stmt.Tok, etran.TrExpr(s.Expr)));
+ } else if (stmt is UseStmt) {
+ AddComment(builder, stmt, "use statement");
+ UseStmt s = (UseStmt)stmt;
+ builder.Add(Assert(stmt.Tok, IsTotal(s.Expr, etran), "use expression must be well defined")); // totality check
+ builder.Add(new Bpl.AssumeCmd(stmt.Tok, (s.EvalInOld ? etran.Old : etran).TrUseExpr(s.FunctionCallExpr)));
+ } else if (stmt is LabelStmt) {
+ AddComment(builder, stmt, "label statement"); // TODO: ouch, comments probably mess up what the label labels in the Boogie program
+ builder.AddLabelCmd(((LabelStmt)stmt).Label);
+ } else if (stmt is BreakStmt) {
+ AddComment(builder, stmt, "break statement");
+ builder.Add(new Bpl.BreakCmd(stmt.Tok, ((BreakStmt)stmt).TargetLabel)); // TODO: handle name clashes of labels
+ } else if (stmt is ReturnStmt) {
+ AddComment(builder, stmt, "return statement");
+ builder.Add(new Bpl.ReturnCmd(stmt.Tok));
+ } else if (stmt is AssignStmt) {
+ AddComment(builder, stmt, "assignment statement");
+ AssignStmt s = (AssignStmt)stmt;
+ TrAssignment(stmt.Tok, s.Lhs, s.Rhs, builder, locals, etran);
+ } else if (stmt is VarDecl) {
+ AddComment(builder, stmt, "var-declaration statement");
+ VarDecl s = (VarDecl)stmt;
+ Bpl.Type varType = TrType(s.Type);
+ Bpl.Expr wh = GetWhereClause(stmt.Tok, new Bpl.IdentifierExpr(stmt.Tok, s.UniqueName, varType), s.Type, etran);
+ Bpl.LocalVariable var = new Bpl.LocalVariable(stmt.Tok, new Bpl.TypedIdent(stmt.Tok, s.UniqueName, varType, wh));
+ locals.Add(var);
+ if (s.Rhs != null) {
+ IdentifierExpr ide = new IdentifierExpr(stmt.Tok, var.Name); // allocate an expression for the assignment LHS...
+ ide.Var = s; ide.Type = s.Type; // ... and resolve it right here
+ TrAssignment(stmt.Tok, ide, s.Rhs, builder, locals, etran);
+ }
+
+ } else if (stmt is CallStmt) {
+ AddComment(builder, stmt, "call statement");
+ CallStmt s = (CallStmt)stmt;
+ Bpl.ExprSeq ins = new Bpl.ExprSeq();
+ ins.Add(etran.TrExpr(s.Receiver));
+ for (int i = 0; i < s.Args.Count; i++) {
+ Expression e = s.Args[i];
+ Type t = ((!)s.Method).Ins[i].Type;
+ ins.Add(etran.CondApplyBox(stmt.Tok, etran.TrExpr(e), (!)e.Type, t));
+ }
+ Bpl.IdentifierExprSeq outs = new Bpl.IdentifierExprSeq();
+ List<Bpl.IdentifierExpr> tmpOuts = new List<Bpl.IdentifierExpr>(s.Lhs.Count);
+ for (int i = 0; i < s.Lhs.Count; i++) {
+ Expression e = s.Lhs[i];
+ if (ExpressionTranslator.ModeledAsRef(((!)s.Method).Outs[i].Type) && !ExpressionTranslator.ModeledAsRef((!)e.Type)) {
+ // we need an Unbox
+ Bpl.LocalVariable var = new Bpl.LocalVariable(stmt.Tok, new Bpl.TypedIdent(stmt.Tok, "$tmp#" + otherTmpVarCount, predef.RefType));
+ otherTmpVarCount++;
+ locals.Add(var);
+ Bpl.IdentifierExpr varIdE = new Bpl.IdentifierExpr(stmt.Tok, var.Name, predef.RefType);
+ tmpOuts.Add(varIdE);
+ outs.Add(varIdE);
+ } else {
+ tmpOuts.Add(null);
+ outs.Add(etran.TrExpr(e));
+ }
+ }
+
+ Bpl.CallCmd call = new Bpl.CallCmd(stmt.Tok, ((!)s.Method).FullName, ins, outs);
+ builder.Add(call);
+ for (int i = 0; i < s.Lhs.Count; i++) {
+ Bpl.IdentifierExpr tmpVarIdE = tmpOuts[i];
+ if (tmpVarIdE != null) {
+ IdentifierExpr e = s.Lhs[i];
+ // e := UnBox(tmpVar);
+ Bpl.IdentifierExpr lhs = (Bpl.IdentifierExpr)etran.TrExpr(e); // TODO: is this cast always justified?
+ Bpl.Cmd cmd = Bpl.Cmd.SimpleAssign(stmt.Tok, lhs, FunctionSpecial(stmt.Tok, BuiltinFunction.Unbox, TrType((!)e.Type), tmpVarIdE));
+ builder.Add(cmd);
+ }
+ }
+
+ } else if (stmt is BlockStmt) {
+ foreach (Statement ss in ((BlockStmt)stmt).Body) {
+ TrStmt(ss, builder, locals, etran);
+ }
+ } else if (stmt is IfStmt) {
+ AddComment(builder, stmt, "if statement");
+ IfStmt s = (IfStmt)stmt;
+ Bpl.Expr guard = s.Guard == null ? null : etran.TrExpr(s.Guard);
+ Bpl.StmtList thn = TrStmt2StmtList(s.Thn, locals, etran);
+ Bpl.StmtList els = null;
+ Bpl.IfCmd elsIf = null;
+ if (s.Els != null) {
+ els = TrStmt2StmtList(s.Els, locals, etran);
+ if (els.BigBlocks.Count == 1) {
+ Bpl.BigBlock bb = els.BigBlocks[0];
+ if (bb.LabelName == null && bb.simpleCmds.Length == 0 && bb.ec is Bpl.IfCmd) {
+ elsIf = (Bpl.IfCmd)bb.ec;
+ els = null;
+ }
+ }
+ }
+ builder.Add(new Bpl.IfCmd(stmt.Tok, guard, thn, elsIf, els));
+
+ } else if (stmt is WhileStmt) {
+ AddComment(builder, stmt, "while statement");
+ WhileStmt s = (WhileStmt)stmt;
+
+ Bpl.LocalVariable preLoopHeapVar = new Bpl.LocalVariable(stmt.Tok, new Bpl.TypedIdent(stmt.Tok, "$PreLoopHeap" + loopHeapVarCount, predef.HeapType));
+ locals.Add(preLoopHeapVar);
+ Bpl.IdentifierExpr preLoopHeap = new Bpl.IdentifierExpr(stmt.Tok, preLoopHeapVar);
+ ExpressionTranslator etranPreLoop = new ExpressionTranslator(this, predef, preLoopHeap);
+ builder.Add(Bpl.Cmd.SimpleAssign(stmt.Tok, preLoopHeap, etran.HeapExpr)); // TODO: does this screw up labeled breaks for this loop?
+
+ Bpl.Expr guard = s.Guard == null ? null : etran.TrExpr(s.Guard);
+ List<Bpl.PredicateCmd!> invariants = new List<Bpl.PredicateCmd!>();
+ foreach (MaybeFreeExpression loopInv in s.Invariants) {
+ int pieces = 0;
+ if (!loopInv.IsFree) {
+ foreach (Expression se in SplitExpr(loopInv.E, true)) {
+ invariants.Add(new Bpl.AssertCmd(se.tok, etran.TrExpr(se)));
+ pieces++;
+ }
+ }
+ if (pieces != 1) {
+ invariants.Add(new Bpl.AssumeCmd(loopInv.E.tok, etran.TrExpr(loopInv.E)));
+ }
+ }
+ foreach (BoilerplateTriple tri in GetTwoStateBoilerplate(stmt.Tok, currentMethod, etranPreLoop, etran)) {
+ if (tri.IsFree) {
+ invariants.Add(new Bpl.AssumeCmd(stmt.Tok, tri.Expr));
+ } else {
+ assert tri.ErrorMessage != null; // follows from BoilerplateTriple invariant
+ invariants.Add(Assert(stmt.Tok, tri.Expr, tri.ErrorMessage));
+ }
+ }
+
+ Bpl.StmtList body;
+ if (s.Decreases.Count == 0) {
+ body = TrStmt2StmtList(s.Body, locals, etran);
+ } else {
+ Bpl.StmtListBuilder loopBodyBuilder = new Bpl.StmtListBuilder();
+
+ List<Bpl.IdentifierExpr!> oldBfs = new List<Bpl.IdentifierExpr!>();
+ int c = 0;
+ foreach (Expression e in s.Decreases) {
+ Bpl.LocalVariable bfVar = new Bpl.LocalVariable(e.tok, new Bpl.TypedIdent(e.tok, "$decr" + loopHeapVarCount + "$" + c, TrType((!)e.Type)));
+ locals.Add(bfVar);
+ Bpl.IdentifierExpr bf = new Bpl.IdentifierExpr(e.tok, bfVar);
+ oldBfs.Add(bf);
+ // record value of each decreases expression at beginning of the loop iteration
+ invariants.Add(Assert(e.tok, IsTotal(e, etran), "decreases expression must be well defined at top of each loop iteration")); // totality check
+ Bpl.Cmd cmd = Bpl.Cmd.SimpleAssign(e.tok, bf, etran.TrExpr(e));
+ loopBodyBuilder.Add(cmd);
+
+ c++;
+ }
+ // time for the actual loop body
+ TrStmt(s.Body, loopBodyBuilder, locals, etran);
+ // check definedness of decreases expressions
+ foreach (Expression e in s.Decreases) {
+ // The following totality check implies that the loop invariant stating the same property will hold;
+ // thus, an alternative (perhaps preferable) design would be: add the totality check also just before
+ // the loop, and change the loop invariant to be free.
+ loopBodyBuilder.Add(Assert(e.tok, IsTotal(e, etran), "decreases expression must be well defined at end of loop iteration")); // totality check
+ }
+ // compute eq and less for each component of the lexicographic pair
+ List<Bpl.Expr!> Eq = new List<Bpl.Expr!>();
+ List<Bpl.Expr!> Less = new List<Bpl.Expr!>();
+ for (int i = 0; i < s.Decreases.Count; i++) {
+ Expression e = s.Decreases[i];
+ Bpl.Expr d = etran.TrExpr(e);
+ Bpl.IdentifierExpr bf = oldBfs[i];
+
+ Bpl.Expr less;
+ Bpl.Expr eq;
+ Type ty = (!)e.Type;
+ if (ty is BoolType) {
+ eq = Bpl.Expr.Iff(d, bf);
+ less = Bpl.Expr.And(Bpl.Expr.Not(d), bf);
+ } else if (ty is IntType) {
+ eq = Bpl.Expr.Eq(d, bf);
+ less = Bpl.Expr.Lt(d, bf);
+ } else if (ty is SetType) {
+ eq = FunctionCall(stmt.Tok, BuiltinFunction.SetEqual, null, d, bf);
+ less = etran.ProperSubset(stmt.Tok, d, bf);
+ } else if (ty is SeqType) {
+ Bpl.Expr e0 = FunctionCall(stmt.Tok, BuiltinFunction.SeqLength, null, d);
+ Bpl.Expr e1 = FunctionCall(stmt.Tok, BuiltinFunction.SeqLength, null, bf);
+ eq = Bpl.Expr.Eq(e0, e1);
+ less = Bpl.Expr.Lt(e0, e1);
+ } else {
+ // reference type
+ Bpl.Expr e0 = Bpl.Expr.Neq(d, predef.Null);
+ Bpl.Expr e1 = Bpl.Expr.Neq(bf, predef.Null);
+ eq = Bpl.Expr.Iff(e0, e1);
+ less = Bpl.Expr.And(Bpl.Expr.Not(e0), e1);
+ }
+ Eq.Add(eq);
+ Less.Add(less);
+ }
+ // check: 0 <= old(decreases)
+ // more precisely, for component k of the lexicographic decreases function, check:
+ // 0 <= old(dec(k)) || dec0 < old(dec0) || dec1 < old(dec1) || ... || dec(k-1) < old(dec((k-1) || old(dec(k)) == dec(k)
+ for (int k = 0; k < s.Decreases.Count; k++) {
+ Expression e = s.Decreases[k];
+ // we only need to check lower bound for integers--sets, sequences, booleans, and references all have natural lower bounds
+ if (e.Type is IntType) {
+ Bpl.IdentifierExpr bf = oldBfs[k];
+ Bpl.Expr bounded = Bpl.Expr.Le(Bpl.Expr.Literal(0), bf);
+ for (int i = 0; i < k; i++) {
+ bounded = Bpl.Expr.Or(bounded, Less[i]);
+ }
+ Bpl.Cmd cmd = Assert(e.tok, Bpl.Expr.Or(bounded, Eq[k]), "decreases expression must be bounded below by 0");
+ loopBodyBuilder.Add(cmd);
+ }
+ }
+ // check: decreases < old(decreases)
+ Bpl.Expr decrCheck = null;
+ for (int i = s.Decreases.Count; 0 <= --i; )
+ invariant i != s.Decreases.Count ==> decrCheck != null;
+ {
+ Bpl.Expr less = Less[i];
+ Bpl.Expr eq = Eq[i];
+ if (decrCheck == null) {
+ decrCheck = less;
+ } else {
+ // decrCheck = less || (eq && decrCheck)
+ decrCheck = Bpl.Expr.Or(less, Bpl.Expr.And(eq, decrCheck));
+ }
+ }
+ assert decrCheck != null; // follows from loop invariant and the fact that s.Decreases.Count != 0
+ loopBodyBuilder.Add(Assert(stmt.Tok, decrCheck, "decreases expression might not decrease"));
+
+ body = loopBodyBuilder.Collect(stmt.Tok);
+ }
+
+ builder.Add(new Bpl.WhileCmd(stmt.Tok, guard, invariants, body));
+ loopHeapVarCount++;
+
+ } else if (stmt is ForeachStmt) {
+ AddComment(builder, stmt, "foreach statement");
+ ForeachStmt s = (ForeachStmt)stmt;
+ // assert/assume (forall o: ref :: o in S ==> Expr);
+ // var oldHeap := $Heap;
+ // havoc $Heap;
+ // assume $HeapSucc(oldHeap, $Heap);
+ // assume (forall o: ref, f: Field :: $Heap[o,f] = oldHeap[o,f] || (f = F && o in S));
+ // assume (forall o: ref :: o != null && o in S ==> $Heap[o,F] = RHS[$Heap := oldHeap]);
+ // Note, $Heap[o,alloc] is intentionally omitted from the antecedent of the quantifier in the previous line. That
+ // allocatedness property should hold automatically, because the set/seq quantified is a program expression, which
+ // will have been constructed from allocated objects.
+ // For sets, "o in S" means just that. For sequences, "o in S" is:
+ // (exists i :: { Seq#Index(S,i) } 0 <= i && i < Seq#Length(S) && Seq#Index(S,i) == o)
+
+ Bpl.BoundVariable oVar = new Bpl.BoundVariable(stmt.Tok, new Bpl.TypedIdent(stmt.Tok, s.BoundVar.UniqueName, TrType(s.BoundVar.Type)));
+ Bpl.IdentifierExpr o = new Bpl.IdentifierExpr(stmt.Tok, oVar);
+
+ Bpl.Expr oInS;
+ if (s.Collection.Type is SetType) {
+ oInS = etran.TrInSet(stmt.Tok, o, s.Collection, null);
+ } else {
+ Bpl.BoundVariable iVar = new Bpl.BoundVariable(stmt.Tok, new Bpl.TypedIdent(stmt.Tok, "$i", Bpl.Type.Int));
+ Bpl.IdentifierExpr i = new Bpl.IdentifierExpr(stmt.Tok, iVar);
+ Bpl.Expr S = etran.TrExpr(s.Collection);
+ Bpl.Expr range = InSeqRange(stmt.Tok, i, S, null, false);
+ Bpl.Expr Si = FunctionCall(stmt.Tok, BuiltinFunction.SeqIndex, TrType(((SeqType!)s.Collection.Type).Arg), S, i);
+ Bpl.Trigger tr = new Bpl.Trigger(stmt.Tok, true, new Bpl.ExprSeq(Si));
+ // TODO: in the next line, the == should be replaced by something that understands extensionality, for sets and sequences
+ oInS = new Bpl.ExistsExpr(stmt.Tok, new Bpl.VariableSeq(iVar), tr, Bpl.Expr.And(range, Bpl.Expr.Eq(Si, o)));
+ }
+
+ foreach (PredicateStmt ps in s.BodyPrefix) {
+ int pieces = 0;
+ if (ps is AssertStmt) {
+ foreach (Expression se in SplitExpr(ps.Expr, true)) {
+ Bpl.Expr e = etran.TrExpr(se);
+ Bpl.Expr q = new Bpl.ForallExpr(se.tok, new Bpl.VariableSeq(oVar), Bpl.Expr.Imp(oInS, e));
+ builder.Add(new Bpl.AssertCmd(se.tok, q));
+ pieces++;
+ }
+ }
+ if (pieces != 1) {
+ Bpl.Expr e;
+ if (ps is UseStmt) {
+ UseStmt us = (UseStmt)ps;
+ e = (us.EvalInOld ? etran.Old : etran).TrUseExpr(us.FunctionCallExpr);
+ } else {
+ e = etran.TrExpr(ps.Expr);
+ }
+ Bpl.Expr q = new Bpl.ForallExpr(ps.Expr.tok, new Bpl.VariableSeq(oVar), Bpl.Expr.Imp(oInS, e));
+ builder.Add(new Bpl.AssumeCmd(ps.Expr.tok, q));
+ }
+ }
+
+ Bpl.IdentifierExpr prevHeap = GetPrevHeapVar_IdExpr(stmt.Tok, locals);
+ builder.Add(Bpl.Cmd.SimpleAssign(stmt.Tok, prevHeap, etran.HeapExpr));
+ builder.Add(new Bpl.HavocCmd(stmt.Tok, new Bpl.IdentifierExprSeq((Bpl.IdentifierExpr/*TODO: this cast is rather dubious*/)etran.HeapExpr)));
+ builder.Add(new Bpl.AssumeCmd(stmt.Tok, FunctionCall(stmt.Tok, BuiltinFunction.HeapSucc, null, prevHeap, etran.HeapExpr)));
+
+ // Here comes: assume (forall<alpha> o: ref, f: Field alpha :: $Heap[o,f] = oldHeap[o,f] || (f = F && o in S));
+ Bpl.TypeVariable alpha = new Bpl.TypeVariable(stmt.Tok, "alpha");
+ Bpl.BoundVariable fVar = new Bpl.BoundVariable(stmt.Tok, new Bpl.TypedIdent(stmt.Tok, "$f", predef.FieldName(stmt.Tok, alpha)));
+ Bpl.IdentifierExpr f = new Bpl.IdentifierExpr(stmt.Tok, fVar);
+ Bpl.Expr heapOF = Bpl.Expr.SelectTok(stmt.Tok, etran.HeapExpr, o, f);
+ Bpl.Expr oldHeapOF = Bpl.Expr.SelectTok(stmt.Tok, prevHeap, o, f);
+ Bpl.Expr body = Bpl.Expr.Or(
+ Bpl.Expr.Eq(heapOF, oldHeapOF),
+ Bpl.Expr.And(
+ Bpl.Expr.Eq(f, GetField((FieldSelectExpr)((!)s.BodyAssign).Lhs)),
+ oInS));
+ Bpl.Expr qq = new Bpl.ForallExpr(stmt.Tok, new Bpl.TypeVariableSeq(alpha), new Bpl.VariableSeq(oVar, fVar), body);
+ builder.Add(new Bpl.AssumeCmd(stmt.Tok, qq));
+
+ // Here comes: assume (forall o: ref :: o != null && o in S ==> $Heap[o,F] = RHS[$Heap := oldHeap]);
+ Bpl.Expr heapOField = Bpl.Expr.SelectTok(stmt.Tok, etran.HeapExpr, o, GetField((FieldSelectExpr)(s.BodyAssign).Lhs));
+ ExpressionTranslator oldEtran = new ExpressionTranslator(this, predef, prevHeap);
+ body = Bpl.Expr.Imp(
+ Bpl.Expr.And(Bpl.Expr.Neq(o, predef.Null), oInS),
+ Bpl.Expr.Eq(heapOField, oldEtran.TrExpr(((ExprRhs)s.BodyAssign.Rhs).Expr)));
+ qq = new Bpl.ForallExpr(stmt.Tok, new Bpl.VariableSeq(oVar), body);
+ builder.Add(new Bpl.AssumeCmd(stmt.Tok, qq));
+
+ } else {
+ assert false; // unexpected statement
+ }
+ }
+
+ void AddComment(Bpl.StmtListBuilder! builder, Statement! stmt, string! comment) {
+ builder.Add(new Bpl.CommentCmd(string.Format("----- {0} ----- {1}({2},{3})", comment, stmt.Tok.filename, stmt.Tok.line, stmt.Tok.col)));
+ }
+
+ Bpl.Expr GetWhereClause(Token! tok, Bpl.Expr! x, Type! type, ExpressionTranslator! etran)
+ requires predef != null;
+ {
+ if (type is TypeProxy) {
+ // unresolved proxy
+ assert ((TypeProxy)type).T == null;
+ // omit where clause (in other places, unresolved proxies are treated as a reference type; we could do that here too, but
+ // we might as well leave out the where clause altogether)
+ return null;
+ } else if (type is BoolType || type is IntType) {
+ return null;
+ } else if (type is SetType) {
+ SetType st = (SetType)type;
+ if (st.Arg.IsRefType) {
+ // (forall t: ref :: { x[t] } x[t] ==> t == null || $Heap[t,alloc])
+ Bpl.BoundVariable tVar = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$t", predef.RefType));
+ Bpl.Expr t = new Bpl.IdentifierExpr(tok, tVar);
+ Bpl.Expr xSubT = Bpl.Expr.SelectTok(tok, x, t);
+
+ Bpl.Trigger tr = new Bpl.Trigger(tok, true, new Bpl.ExprSeq(xSubT));
+
+ Bpl.Expr goodRef = etran.GoodRef(tok, t, st.Arg);
+ Bpl.Expr body = Bpl.Expr.Imp(xSubT, Bpl.Expr.Or(Bpl.Expr.Eq(t, predef.Null), goodRef));
+ return new Bpl.ForallExpr(tok, new Bpl.VariableSeq(tVar), tr, body);
+ } else {
+ // TODO: should also handle sets of sets, etc.
+ return null;
+ }
+
+ } else if (type is SeqType) {
+ SeqType st = (SeqType)type;
+ if (st.Arg.IsRefType) {
+ // (forall i: int :: { Seq#Index(x,i) }
+ // 0 <= i && i < Seq#Length(x) ==> Seq#Index(x,i) == null || $Heap[Seq#Index(x,i), alloc])
+ Bpl.BoundVariable iVar = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$i", Bpl.Type.Int));
+ Bpl.Expr i = new Bpl.IdentifierExpr(tok, iVar);
+ Bpl.Expr xSubI = FunctionCall(tok, BuiltinFunction.SeqIndex, predef.RefType, x, i);
+
+ Bpl.Expr range = InSeqRange(tok, i, x, null, false);
+
+ Bpl.Trigger tr = new Bpl.Trigger(tok, true, new Bpl.ExprSeq(xSubI));
+
+ Bpl.Expr goodRef = etran.GoodRef(tok, xSubI, st.Arg);
+ Bpl.Expr body = Bpl.Expr.Imp(range, Bpl.Expr.Or(Bpl.Expr.Eq(xSubI, predef.Null), goodRef));
+ return new Bpl.ForallExpr(tok, new Bpl.VariableSeq(iVar), tr, body);
+ } else {
+ // TODO: should also handle sequences or sequences, etc.
+ return null;
+ }
+
+ } else if (type.IsRefType) {
+ // reference type:
+ // x == null || $Heap[x,alloc]
+ return Bpl.Expr.Or(Bpl.Expr.Eq(x, predef.Null), etran.GoodRef(tok, x, type));
+ } else {
+ // type parameter
+ return null;
+ }
+ }
+
+ void TrAssignment(Token! tok, Expression! lhs, AssignmentRhs! rhs, Bpl.StmtListBuilder! builder, Bpl.VariableSeq! locals,
+ ExpressionTranslator! etran)
+ requires predef != null;
+ {
+ if (rhs is ExprRhs) {
+ builder.Add(Assert(tok, IsTotal(lhs, etran), "LHS expression must be well defined")); // totality check
+ builder.Add(Assert(tok, IsTotal(((ExprRhs)rhs).Expr, etran), "RHS expression must be well defined")); // totality check
+ Bpl.Expr bRhs = etran.TrExpr(((ExprRhs)rhs).Expr);
+ if (lhs is IdentifierExpr) {
+ Bpl.IdentifierExpr bLhs = (Bpl.IdentifierExpr)etran.TrExpr(lhs); // TODO: is this cast always justified?
+ Bpl.Cmd cmd = Bpl.Cmd.SimpleAssign(tok, bLhs, bRhs);
+ builder.Add(cmd);
+ } else {
+ Bpl.NAryExpr bLhs = (Bpl.NAryExpr)etran.TrExpr(lhs); // TODO: is this cast always justified?
+ assert bLhs.Args.Length == 3; // we're expecting h[o,f]
+ Bpl.IdentifierExpr h = (Bpl.IdentifierExpr!)bLhs.Args[0]; // TODO: is this cast always justified?
+ Bpl.Cmd cmd = Bpl.Cmd.MapAssign(tok, h, (!)bLhs.Args[1], (!)bLhs.Args[2], bRhs);
+ builder.Add(cmd);
+ // assume $IsGoodHeap($Heap);
+ builder.Add(AssumeGoodHeap(tok, etran));
+ }
+
+ } else if (rhs is HavocRhs) {
+ assert lhs is IdentifierExpr; // for this kind of RHS, the LHS is restricted to be a simple variable
+ Bpl.IdentifierExpr x = (Bpl.IdentifierExpr)etran.TrExpr(lhs); // TODO: is this cast always justified?
+ builder.Add(new Bpl.HavocCmd(tok, new Bpl.IdentifierExprSeq(x)));
+
+ } else {
+ assert rhs is TypeRhs; // otherwise, an unexpected AssignmentRhs
+ assert lhs is IdentifierExpr; // for this kind of RHS, the LHS is restricted to be a simple variable
+
+ Bpl.IdentifierExpr nw = GetNewVar_IdExpr(tok, locals);
+ builder.Add(new Bpl.HavocCmd(tok, new Bpl.IdentifierExprSeq(nw)));
+ // assume $nw != null && !$Heap[$nw, alloc] && dtype($nw) == RHS;
+ Bpl.Expr nwNotNull = Bpl.Expr.Neq(nw, predef.Null);
+ builder.Add(new Bpl.AssumeCmd(tok, Bpl.Expr.And(nwNotNull, etran.GoodRef_Class(tok, nw, (ClassType)((TypeRhs)rhs).Type, true))));
+ // $Heap[$nw, alloc] := true;
+ Bpl.Expr alloc = predef.Alloc(tok);
+ Bpl.Cmd cmd = Bpl.Cmd.MapAssign(tok, (Bpl.IdentifierExpr/*TODO: this cast is dubious*/)etran.HeapExpr, nw, alloc, Bpl.Expr.True);
+ builder.Add(cmd);
+ // x := $nw;
+ Bpl.IdentifierExpr x = (Bpl.IdentifierExpr)etran.TrExpr(lhs); // TODO: is this cast always justified?
+ builder.Add(Bpl.Cmd.SimpleAssign(tok, x, nw));
+ // assume $IsGoodHeap($Heap);
+ builder.Add(AssumeGoodHeap(tok, etran));
+ }
+ }
+
+ Bpl.AssumeCmd! AssumeGoodHeap(Token! tok, ExpressionTranslator! etran) {
+ return new Bpl.AssumeCmd(tok, FunctionCall(tok, BuiltinFunction.IsGoodHeap, null, etran.HeapExpr));
+ }
+
+ // ----- Expression ---------------------------------------------------------------------------
+
+ internal class ExpressionTranslator {
+ public readonly Bpl.Expr! HeapExpr;
+ public readonly PredefinedDecls! predef;
+ public readonly Translator! translator;
+ public ExpressionTranslator(Translator! translator, PredefinedDecls! predef, Token! heapToken) {
+ this.translator = translator;
+ this.predef = predef;
+ HeapExpr = new Bpl.IdentifierExpr(heapToken, "$Heap", predef.HeapType);
+ }
+
+ public ExpressionTranslator(Translator! translator, PredefinedDecls! predef, Bpl.Expr! heap) {
+ this.translator = translator;
+ this.predef = predef;
+ this.HeapExpr = heap;
+ }
+
+ ExpressionTranslator oldEtran;
+ public ExpressionTranslator! Old {
+ get {
+ if (oldEtran == null) {
+ oldEtran = new ExpressionTranslator(translator, predef, new Bpl.OldExpr(HeapExpr.tok, HeapExpr));
+ }
+ return oldEtran;
+ }
+ }
+
+ public Bpl.Expr! TrExpr(Expression! expr)
+ requires predef != null;
+ {
+ if (expr is LiteralExpr) {
+ LiteralExpr e = (LiteralExpr)expr;
+ if (e.Value == null) {
+ return predef.Null;
+ } else if (e.Value is bool) {
+ return Bpl.Expr.Literal((bool)e.Value);
+ } else if (e.Value is int) {
+ return Bpl.Expr.Literal((int)e.Value);
+ } else {
+ assert false; // unexpected literal
+ }
+
+ } else if (expr is ThisExpr) {
+ return new Bpl.IdentifierExpr(expr.tok, "this", predef.RefType);
+
+ } else if (expr is IdentifierExpr) {
+ IdentifierExpr e = (IdentifierExpr)expr;
+ return TrVar(expr.tok, (!)e.Var);
+
+ } else if (expr is SetDisplayExpr) {
+ SetDisplayExpr e = (SetDisplayExpr)expr;
+ Bpl.Expr s = translator.FunctionCall(expr.tok, BuiltinFunction.SetEmpty, translator.TrType((!)expr.Type));
+ foreach (Expression ee in e.Elements) {
+ Bpl.Expr ss = TrExpr(ee);
+ s = translator.FunctionCall(expr.tok, BuiltinFunction.SetUnionOne, translator.TrType(expr.Type), s, ss);
+ }
+ return s;
+
+ } else if (expr is SeqDisplayExpr) {
+ SeqDisplayExpr e = (SeqDisplayExpr)expr;
+ Bpl.Expr s = translator.FunctionCall(expr.tok, BuiltinFunction.SeqEmpty, translator.TrType((!)expr.Type));
+ int i = 0;
+ foreach (Expression ee in e.Elements) {
+ Bpl.Expr ss = TrExpr(ee);
+ s = translator.FunctionCall(expr.tok, BuiltinFunction.SeqBuild, translator.TrType(expr.Type), s, Bpl.Expr.Literal(i), ss, Bpl.Expr.Literal(i+1));
+ i++;
+ }
+ return s;
+
+ } else if (expr is FieldSelectExpr) {
+ FieldSelectExpr e = (FieldSelectExpr)expr;
+ return Bpl.Expr.SelectTok(expr.tok, HeapExpr, TrExpr(e.Obj), new Bpl.IdentifierExpr(expr.tok, translator.GetField((!)e.Field)));
+
+ } else if (expr is SeqSelectExpr) {
+ SeqSelectExpr e = (SeqSelectExpr)expr;
+ Bpl.Expr seq = TrExpr(e.Seq);
+ Bpl.Type elType = translator.TrType(((SeqType!)e.Seq.Type).Arg);
+ Bpl.Expr e0 = e.E0 == null ? null : TrExpr(e.E0);
+ Bpl.Expr e1 = e.E1 == null ? null : TrExpr(e.E1);
+ if (e.SelectOne) {
+ assert e1 == null;
+ return translator.FunctionCall(expr.tok, BuiltinFunction.SeqIndex, elType, seq, e0);
+ } else {
+ if (e1 != null) {
+ seq = translator.FunctionCall(expr.tok, BuiltinFunction.SeqTake, elType, seq, e1);
+ }
+ if (e0 != null) {
+ seq = translator.FunctionCall(expr.tok, BuiltinFunction.SeqDrop, elType, seq, e0);
+ }
+ return seq;
+ }
+
+ } else if (expr is FunctionCallExpr) {
+ FunctionCallExpr e = (FunctionCallExpr)expr;
+ string nm = ((!)e.Function).FullName + (e is UseExpr ? "#use" : "");
+ Bpl.IdentifierExpr id = new Bpl.IdentifierExpr(expr.tok, nm, translator.TrType((!)e.Type));
+ Bpl.ExprSeq args = new Bpl.ExprSeq();
+ args.Add(HeapExpr);
+ args.Add(TrExpr(e.Receiver));
+ for (int i = 0; i < e.Args.Count; i++) {
+ Expression ee = e.Args[i];
+ Type t = e.Function.Formals[i].Type;
+ args.Add(CondApplyBox(expr.tok, TrExpr(ee), (!)ee.Type, t));
+ }
+ Bpl.Expr result = new Bpl.NAryExpr(expr.tok, new Bpl.FunctionCall(id), args);
+ return CondApplyUnbox(expr.tok, result, e.Function.ResultType, expr.Type);
+
+ } else if (expr is OldExpr) {
+ OldExpr e = (OldExpr)expr;
+ return new Bpl.OldExpr(expr.tok, TrExpr(e.E));
+
+ } else if (expr is FreshExpr) {
+ FreshExpr e = (FreshExpr)expr;
+ Bpl.Expr oldHeap = new Bpl.OldExpr(expr.tok, HeapExpr);
+ if (e.E.Type is SetType) {
+ // generate: (forall $o: ref :: $o != null && X[$o] ==> !old($Heap)[$o,alloc])
+ // TODO: trigger?
+ Bpl.Variable oVar = new Bpl.BoundVariable(expr.tok, new Bpl.TypedIdent(expr.tok, "$o", predef.RefType));
+ Bpl.Expr o = new Bpl.IdentifierExpr(expr.tok, oVar);
+ Bpl.Expr oNotNull = Bpl.Expr.Neq(o, predef.Null);
+ Bpl.Expr oInSet = TrInSet(expr.tok, o, e.E, null);
+ Bpl.Expr oIsFresh = Bpl.Expr.Not(IsAlloced(expr.tok, o, oldHeap));
+ Bpl.Expr body = Bpl.Expr.Imp(Bpl.Expr.And(oNotNull, oInSet), oIsFresh);
+ return new Bpl.ForallExpr(expr.tok, new Bpl.VariableSeq(oVar), body);
+ } else if (e.E.Type is SeqType) {
+ // generate: (forall $i: int :: 0 <= $i && $i < Seq#Length(X) && Seq#Index(X,$i) != null ==> !old($Heap)[Seq#Index(X,$i),alloc])
+ // TODO: trigger?
+ Bpl.Variable iVar = new Bpl.BoundVariable(expr.tok, new Bpl.TypedIdent(expr.tok, "$i", Bpl.Type.Int));
+ Bpl.Expr i = new Bpl.IdentifierExpr(expr.tok, iVar);
+ Bpl.Expr iBounds = translator.InSeqRange(expr.tok, i, TrExpr(e.E), null, false);
+ Bpl.Expr XsubI = translator.FunctionCall(expr.tok, BuiltinFunction.SeqIndex, predef.RefType, TrExpr(e.E), i);
+ Bpl.Expr oIsFresh = Bpl.Expr.Not(IsAlloced(expr.tok, XsubI, oldHeap));
+ Bpl.Expr body = Bpl.Expr.Imp(Bpl.Expr.And(iBounds, XsubI), oIsFresh);
+ return new Bpl.ForallExpr(expr.tok, new Bpl.VariableSeq(iVar), body);
+ } else {
+ // generate: x == null || !old($Heap)[x]
+ Bpl.Expr oNotNull = Bpl.Expr.Neq(TrExpr(e.E), predef.Null);
+ Bpl.Expr oIsFresh = Bpl.Expr.Not(IsAlloced(expr.tok, TrExpr(e.E), oldHeap));
+ return Bpl.Expr.Or(oNotNull, oIsFresh);
+ }
+
+ } else if (expr is UnaryExpr) {
+ UnaryExpr e = (UnaryExpr)expr;
+ Bpl.Expr arg = TrExpr(e.E);
+ switch (e.Op) {
+ case UnaryExpr.Opcode.Not:
+ return Bpl.Expr.Not(arg);
+ case UnaryExpr.Opcode.SeqLength:
+ return translator.FunctionCall(expr.tok, BuiltinFunction.SeqLength, null, arg);
+ default:
+ assert false; // unexpected unary expression
+ }
+
+ } else if (expr is BinaryExpr) {
+ BinaryExpr e = (BinaryExpr)expr;
+ Bpl.Expr e0 = TrExpr(e.E0);
+ if (e.ResolvedOp == BinaryExpr.ResolvedOpcode.InSet) {
+ return TrInSet(expr.tok, e0, e.E1, e.E0.Type); // let TrInSet translate e.E1
+ }
+ Bpl.Expr e1 = TrExpr(e.E1);
+ switch (e.ResolvedOp) {
+ case BinaryExpr.ResolvedOpcode.Iff:
+ return Bpl.Expr.Iff(e0, e1);
+ case BinaryExpr.ResolvedOpcode.Imp:
+ return Bpl.Expr.Imp(e0, e1);
+ case BinaryExpr.ResolvedOpcode.And:
+ return Bpl.Expr.And(e0, e1);
+ case BinaryExpr.ResolvedOpcode.Or:
+ return Bpl.Expr.Or(e0, e1);
+
+ case BinaryExpr.ResolvedOpcode.EqCommon:
+ return Bpl.Expr.Eq(e0, e1);
+ case BinaryExpr.ResolvedOpcode.NeqCommon:
+ return Bpl.Expr.Neq(e0, e1);
+
+ case BinaryExpr.ResolvedOpcode.Lt:
+ return Bpl.Expr.Lt(e0, e1);
+ case BinaryExpr.ResolvedOpcode.Le:
+ return Bpl.Expr.Le(e0, e1);
+ case BinaryExpr.ResolvedOpcode.Ge:
+ return Bpl.Expr.Ge(e0, e1);
+ case BinaryExpr.ResolvedOpcode.Gt:
+ return Bpl.Expr.Gt(e0, e1);
+ case BinaryExpr.ResolvedOpcode.Add:
+ return Bpl.Expr.Add(e0, e1);
+ case BinaryExpr.ResolvedOpcode.Sub:
+ return Bpl.Expr.Sub(e0, e1);
+ case BinaryExpr.ResolvedOpcode.Mul:
+ return Bpl.Expr.Mul(e0, e1);
+ case BinaryExpr.ResolvedOpcode.Div:
+ return Bpl.Expr.Div(e0, e1);
+ case BinaryExpr.ResolvedOpcode.Mod:
+ return Bpl.Expr.Mod(e0, e1);
+
+ case BinaryExpr.ResolvedOpcode.SetEq:
+ return translator.FunctionCall(expr.tok, BuiltinFunction.SetEqual, null, e0, e1);
+ case BinaryExpr.ResolvedOpcode.SetNeq:
+ return Bpl.Expr.Not(translator.FunctionCall(expr.tok, BuiltinFunction.SetEqual, null, e0, e1));
+ case BinaryExpr.ResolvedOpcode.ProperSubset:
+ return ProperSubset(expr.tok, e0, e1);
+ case BinaryExpr.ResolvedOpcode.Subset:
+ return translator.FunctionCall(expr.tok, BuiltinFunction.SetSubset, null, e0, e1);
+ case BinaryExpr.ResolvedOpcode.Superset:
+ return translator.FunctionCall(expr.tok, BuiltinFunction.SetSubset, null, e1, e0);
+ case BinaryExpr.ResolvedOpcode.ProperSuperset:
+ return Bpl.Expr.And(
+ translator.FunctionCall(expr.tok, BuiltinFunction.SetSubset, null, e1, e0),
+ Bpl.Expr.Not(translator.FunctionCall(expr.tok, BuiltinFunction.SetEqual, null, e0, e1)));
+ case BinaryExpr.ResolvedOpcode.Disjoint:
+ return translator.FunctionCall(expr.tok, BuiltinFunction.SetDisjoint, null, e0, e1);
+ case BinaryExpr.ResolvedOpcode.InSet:
+ assert false; // this case handled above
+ case BinaryExpr.ResolvedOpcode.Union:
+ return translator.FunctionCall(expr.tok, BuiltinFunction.SetUnion, translator.TrType(((SetType!)expr.Type).Arg), e0, e1);
+ case BinaryExpr.ResolvedOpcode.Intersection:
+ return translator.FunctionCall(expr.tok, BuiltinFunction.SetIntersection, translator.TrType(((SetType!)expr.Type).Arg), e0, e1);
+ case BinaryExpr.ResolvedOpcode.SetDifference:
+ return translator.FunctionCall(expr.tok, BuiltinFunction.SetDifference, translator.TrType(((SetType!)expr.Type).Arg), e0, e1);
+
+ case BinaryExpr.ResolvedOpcode.SeqEq:
+ return translator.FunctionCall(expr.tok, BuiltinFunction.SeqEqual, null, e0, e1);
+ case BinaryExpr.ResolvedOpcode.SeqNeq:
+ return Bpl.Expr.Not(translator.FunctionCall(expr.tok, BuiltinFunction.SeqEqual, null, e0, e1));
+ case BinaryExpr.ResolvedOpcode.ProperPrefix:
+ return ProperPrefix(expr.tok, e0, e1);
+ case BinaryExpr.ResolvedOpcode.Prefix:
+ {
+ Bpl.Expr len0 = translator.FunctionCall(expr.tok, BuiltinFunction.SeqLength, null, e0);
+ Bpl.Expr len1 = translator.FunctionCall(expr.tok, BuiltinFunction.SeqLength, null, e1);
+ return Bpl.Expr.And(
+ Bpl.Expr.Le(len0, len1),
+ translator.FunctionCall(expr.tok, BuiltinFunction.SeqSameUntil, null, e0, e1, len0));
+ }
+ case BinaryExpr.ResolvedOpcode.Concat:
+ return translator.FunctionCall(expr.tok, BuiltinFunction.SeqAppend, translator.TrType(((SeqType!)expr.Type).Arg), e0, e1);
+ case BinaryExpr.ResolvedOpcode.InSeq:
+ return translator.FunctionCall(expr.tok, BuiltinFunction.SeqContains, null, e1, e0);
+
+ default:
+ assert false; // unexpected binary expression
+ }
+
+ } else if (expr is QuantifierExpr) {
+ QuantifierExpr e = (QuantifierExpr)expr;
+ Bpl.VariableSeq bvars = new Bpl.VariableSeq();
+ foreach (BoundVar bv in e.BoundVars) {
+ bvars.Add(new Bpl.BoundVariable(bv.tok, new Bpl.TypedIdent(bv.tok, bv.UniqueName, translator.TrType(bv.Type))));
+ }
+ Bpl.QKeyValue kv = TrAttributes(e.Attributes);
+ Bpl.Trigger tr = null;
+ for (Triggers trigs = e.Trigs; trigs != null; trigs = trigs.Prev) {
+ Bpl.ExprSeq tt = new Bpl.ExprSeq();
+ foreach (Expression term in trigs.Terms) {
+ tt.Add(TrExpr(term));
+ }
+ tr = new Bpl.Trigger(expr.tok, true, tt, tr);
+ }
+ Bpl.Expr body = TrExpr(e.Body);
+
+ if (e is ForallExpr) {
+ return new Bpl.ForallExpr(expr.tok, new Bpl.TypeVariableSeq(), bvars, kv, tr, body);
+ } else {
+ assert e is ExistsExpr;
+ return new Bpl.ExistsExpr(expr.tok, new Bpl.TypeVariableSeq(), bvars, kv, tr, body);
+ }
+
+ } else if (expr is ITEExpr) {
+ ITEExpr e = (ITEExpr)expr;
+ Bpl.Expr g = TrExpr(e.Test);
+ Bpl.Expr thn = TrExpr(e.Thn);
+ Bpl.Expr els = TrExpr(e.Els);
+ Bpl.Expr yea = Bpl.Expr.Imp(g, thn);
+ Bpl.Expr nay = Bpl.Expr.Imp(Bpl.Expr.Not(g), els);
+ return Bpl.Expr.And(yea, nay);
+
+ } else {
+ assert false; // unexpected expression
+ }
+ }
+
+ public Bpl.Expr! ProperSubset(Token! tok, Bpl.Expr! e0, Bpl.Expr! e1) {
+ return Bpl.Expr.And(
+ translator.FunctionCall(tok, BuiltinFunction.SetSubset, null, e0, e1),
+ Bpl.Expr.Not(translator.FunctionCall(tok, BuiltinFunction.SetEqual, null, e0, e1)));
+ }
+ public Bpl.Expr! ProperPrefix(Token! tok, Bpl.Expr! e0, Bpl.Expr! e1) {
+ Bpl.Expr len0 = translator.FunctionCall(tok, BuiltinFunction.SeqLength, null, e0);
+ Bpl.Expr len1 = translator.FunctionCall(tok, BuiltinFunction.SeqLength, null, e1);
+ return Bpl.Expr.And(
+ Bpl.Expr.Lt(len0, len1),
+ translator.FunctionCall(tok, BuiltinFunction.SeqSameUntil, null, e0, e1, len0));
+ }
+
+ public Bpl.Expr! TrUseExpr(FunctionCallExpr! e) {
+ Bpl.IdentifierExpr id = new Bpl.IdentifierExpr(e.tok, ((!)e.Function).FullName + "#use", translator.TrType((!)e.Type));
+ Bpl.ExprSeq args = new Bpl.ExprSeq();
+ args.Add(HeapExpr);
+ args.Add(TrExpr(e.Receiver));
+ foreach (Expression ee in e.Args) {
+ args.Add(TrExpr(ee));
+ }
+ return new Bpl.NAryExpr(e.tok, new Bpl.FunctionCall(id), args);
+ }
+
+ public Bpl.Expr! CondApplyBox(Token! tok, Bpl.Expr! e, Type! fromType, Type! toType) {
+ if (!ModeledAsRef(fromType) && ModeledAsRef(toType)) {
+ return translator.FunctionCall(tok, BuiltinFunction.Box, null, e);
+ } else {
+ return e;
+ }
+ }
+
+ public Bpl.Expr! CondApplyUnbox(Token! tok, Bpl.Expr! e, Type! fromType, Type! toType) {
+ if (ModeledAsRef(fromType) && !ModeledAsRef(toType)) {
+ return translator.FunctionSpecial(tok, BuiltinFunction.Unbox, translator.TrType(toType), e);
+ } else {
+ return e;
+ }
+ }
+
+ public static bool ModeledAsRef(Type! t) {
+ return !(t is BoolType || t is IntType || t is CollectionType);
+ }
+
+ public Bpl.Expr! TrVar(Token! tok, IVariable! var) {
+ return new Bpl.IdentifierExpr(tok, var.UniqueName, translator.TrType(var.Type));
+ }
+
+ /// <summary>
+ /// Translate like s[elmt], but try to avoid as many set functions as possible in the
+ /// translation, because such functions can mess up triggering.
+ /// If elmtType is non-null, boxing according to elmtType is applied as appropriate.
+ /// </summary>
+ public Bpl.Expr! TrInSet(Token! tok, Bpl.Expr! elmt, Expression! s, Type elmtType) {
+ if (s is BinaryExpr) {
+ BinaryExpr bin = (BinaryExpr)s;
+ switch (bin.ResolvedOp) {
+ case BinaryExpr.ResolvedOpcode.Union:
+ return Bpl.Expr.Or(TrInSet(tok, elmt, bin.E0, elmtType), TrInSet(tok, elmt, bin.E1, elmtType));
+ case BinaryExpr.ResolvedOpcode.Intersection:
+ return Bpl.Expr.And(TrInSet(tok, elmt, bin.E0, elmtType), TrInSet(tok, elmt, bin.E1, elmtType));
+ case BinaryExpr.ResolvedOpcode.SetDifference:
+ return Bpl.Expr.And(TrInSet(tok, elmt, bin.E0, elmtType), Bpl.Expr.Not(TrInSet(tok, elmt, bin.E1, elmtType)));
+ default:
+ break;
+ }
+ } else if (s is SetDisplayExpr) {
+ SetDisplayExpr disp = (SetDisplayExpr)s;
+ Bpl.Expr disjunction = null;
+ foreach (Expression a in disp.Elements) {
+ Bpl.Expr disjunct = Bpl.Expr.Eq(elmt, TrExpr(a));
+ if (disjunction == null) {
+ disjunction = disjunct;
+ } else {
+ disjunction = Bpl.Expr.Or(disjunction, disjunct);
+ }
+ }
+ if (disjunction == null) {
+ return Bpl.Expr.False;
+ } else {
+ return disjunction;
+ }
+ }
+ return Bpl.Expr.SelectTok(tok, TrExpr(s), elmt);
+ }
+
+ Bpl.QKeyValue TrAttributes(Attributes attrs) {
+ Bpl.QKeyValue kv = null;
+ while (attrs != null) {
+ List<object!> parms = new List<object!>();
+ foreach (Attributes.Argument arg in attrs.Args) {
+ if (arg.E != null) {
+ parms.Add(TrExpr(arg.E));
+ } else {
+ parms.Add((!)arg.S);
+ }
+ }
+ kv = new Bpl.QKeyValue(Token.NoToken, attrs.Name, parms, kv);
+ attrs = attrs.Prev;
+ }
+ return kv;
+ }
+
+ // --------------- help routines ---------------
+
+ public Bpl.Expr! IsAlloced(Token! tok, Bpl.Expr! e) {
+ return IsAlloced(tok, e, HeapExpr);
+ }
+
+ Bpl.Expr! IsAlloced(Token! tok, Bpl.Expr! e, Bpl.Expr! heap) {
+ return Bpl.Expr.SelectTok(tok, heap, e, predef.Alloc(tok));
+ }
+
+ public Bpl.Expr! GoodRef(Token! tok, Bpl.Expr! e, Type! type) {
+ Bpl.Expr goodRef;
+ if (type is ClassType && ((ClassType)type).ResolvedClass != null) {
+ // Heap[e, alloc] && dtype(e) == T
+ return GoodRef_Class(tok, e, (ClassType)type, false);
+ } else {
+ // Heap[e, alloc]
+ return IsAlloced(tok, e);
+ }
+ }
+
+ public Bpl.Expr! GoodRef_Class(Token! tok, Bpl.Expr! e, ClassType! type, bool isNew)
+ requires type.ResolvedClass != null;
+ {
+ // Heap[e, alloc]
+ Bpl.Expr r = IsAlloced(tok, e);
+ if (isNew) {
+ r = Bpl.Expr.Not(r); // use the conjunct: !Heap[e, alloc]
+ }
+
+ // dtype(e) == C
+ Bpl.Expr dtypeFunc = translator.FunctionCall(tok, BuiltinFunction.DynamicType, null, e);
+ Bpl.Expr dtype = Bpl.Expr.Eq(dtypeFunc, new Bpl.IdentifierExpr(tok, translator.GetClass(type.ResolvedClass)));
+ r = Bpl.Expr.And(r, dtype);
+
+ // dtypeParams(e, #) == T
+ int n = 0;
+ foreach (Type arg in type.TypeArgs) {
+ Bpl.Expr tpFunc = translator.FunctionCall(tok, BuiltinFunction.TypeParams, null, e, Bpl.Expr.Literal(n));
+ Bpl.Expr ta = translator.GetTypeExpr(tok, arg);
+ if (ta != null) {
+ r = Bpl.Expr.And(r, Bpl.Expr.Eq(tpFunc, ta));
+ }
+ n++;
+ }
+
+ return r;
+ }
+
+ public Bpl.Expr TypeAlloced(Token! tok, Bpl.Expr! e, Type! type) {
+ while (true) {
+ TypeProxy proxy = type as TypeProxy;
+ if (proxy == null) {
+ break;
+ } else if (proxy.T == null) {
+ return null;
+ } else {
+ type = proxy.T;
+ }
+ }
+
+ Bpl.BoundVariable bv = null;
+ Bpl.Expr ante = null;
+ if (type.IsRefType) { // object or class type
+ // e == null || $Heap[e, alloc]
+ // This is done below
+
+ } else if (type is SetType) {
+ // (forall tp: ref :: e[tp] ==> tp == null || $Heap[tp, alloc])
+ bv = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$tp", predef.RefType));
+ Bpl.Expr tp = new Bpl.IdentifierExpr(tok, bv);
+ ante = Bpl.Expr.SelectTok(tok, e, tp); // note, this means the set-inclusion does not undergo the set-inclusion expansion optimization done by TrInSet
+ e = tp;
+
+ } else if (type is SeqType) {
+ // (forall i: int :: Seq#Index(e,i) == null || $Heap[Seq#Index(e,i), alloc])
+ bv = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$i", Bpl.Type.Int));
+ e = translator.FunctionCall(tok, BuiltinFunction.SeqIndex, predef.RefType, e, new Bpl.IdentifierExpr(tok, bv));
+
+ } else {
+ return null;
+ }
+
+ Bpl.Expr r = Bpl.Expr.Or(Bpl.Expr.Eq(e, predef.Null), GoodRef(tok, e, type));
+ if (ante != null) {
+ r = Bpl.Expr.Imp(ante, r);
+ }
+ if (bv != null) {
+ r = new Bpl.ForallExpr(tok, new Bpl.VariableSeq(bv), r);
+ }
+ return r;
+ }
+ }
+
+ enum BuiltinFunction {
+ SetEmpty,
+ SetUnionOne,
+ SetUnion,
+ SetIntersection,
+ SetDifference,
+ SetEqual,
+ SetSubset,
+ SetDisjoint,
+
+ SeqLength,
+ SeqEmpty,
+ SeqBuild,
+ SeqAppend,
+ SeqIndex,
+ SeqContains,
+ SeqDrop,
+ SeqTake,
+ SeqEqual,
+ SeqSameUntil,
+
+ Box,
+ Unbox,
+
+ IsGoodHeap,
+ HeapSucc,
+
+ DynamicType, // allocated type
+ TypeParams, // type parameters to allocated type
+ TypeTuple
+ }
+
+ Bpl.NAryExpr! FunctionCall(Token! tok, BuiltinFunction f, Bpl.Type typeInstantiation, params Bpl.Expr[]! args)
+ requires predef != null;
+ {
+ switch (f) {
+ case BuiltinFunction.SetEmpty:
+ assert args.Length == 0;
+ assert typeInstantiation != null;
+ return FunctionCall(tok, "Set#Empty",
+ new Bpl.MapType(tok, new Bpl.TypeVariableSeq(), new Bpl.TypeSeq(typeInstantiation), Bpl.Type.Bool), args);
+ case BuiltinFunction.SetUnionOne:
+ assert args.Length == 2;
+ assert typeInstantiation != null;
+ return FunctionCall(tok, "Set#UnionOne",
+ new Bpl.MapType(tok, new Bpl.TypeVariableSeq(), new Bpl.TypeSeq(typeInstantiation), Bpl.Type.Bool), args);
+ case BuiltinFunction.SetUnion:
+ assert args.Length == 2;
+ assert typeInstantiation != null;
+ return FunctionCall(tok, "Set#Union",
+ new Bpl.MapType(tok, new Bpl.TypeVariableSeq(), new Bpl.TypeSeq(typeInstantiation), Bpl.Type.Bool), args);
+ case BuiltinFunction.SetIntersection:
+ assert args.Length == 2;
+ assert typeInstantiation != null;
+ return FunctionCall(tok, "Set#Intersection",
+ new Bpl.MapType(tok, new Bpl.TypeVariableSeq(), new Bpl.TypeSeq(typeInstantiation), Bpl.Type.Bool), args);
+ case BuiltinFunction.SetDifference:
+ assert args.Length == 2;
+ assert typeInstantiation != null;
+ return FunctionCall(tok, "Set#Difference",
+ new Bpl.MapType(tok, new Bpl.TypeVariableSeq(), new Bpl.TypeSeq(typeInstantiation), Bpl.Type.Bool), args);
+ case BuiltinFunction.SetEqual:
+ assert args.Length == 2;
+ assert typeInstantiation == null;
+ return FunctionCall(tok, "Set#Equal", Bpl.Type.Bool, args);
+ case BuiltinFunction.SetSubset:
+ assert args.Length == 2;
+ assert typeInstantiation == null;
+ return FunctionCall(tok, "Set#Subset", Bpl.Type.Bool, args);
+ case BuiltinFunction.SetDisjoint:
+ assert args.Length == 2;
+ assert typeInstantiation == null;
+ return FunctionCall(tok, "Set#Disjoint", Bpl.Type.Bool, args);
+
+ case BuiltinFunction.SeqLength:
+ assert args.Length == 1;
+ assert typeInstantiation == null;
+ return FunctionCall(tok, "Seq#Length", Bpl.Type.Int, args);
+ case BuiltinFunction.SeqEmpty:
+ assert args.Length == 0;
+ assert typeInstantiation != null;
+ return FunctionCall(tok, "Seq#Empty",
+ new Bpl.MapType(tok, new Bpl.TypeVariableSeq(), new Bpl.TypeSeq(typeInstantiation), Bpl.Type.Bool), args);
+ case BuiltinFunction.SeqBuild:
+ assert args.Length == 4;
+ assert typeInstantiation != null;
+ return FunctionCall(tok, "Seq#Build", predef.SeqType(tok, typeInstantiation), args);
+ case BuiltinFunction.SeqAppend:
+ assert args.Length == 2;
+ assert typeInstantiation != null;
+ return FunctionCall(tok, "Seq#Append", predef.SeqType(tok, typeInstantiation), args);
+ case BuiltinFunction.SeqIndex:
+ assert args.Length == 2;
+ assert typeInstantiation != null;
+ return FunctionCall(tok, "Seq#Index", typeInstantiation, args);
+ case BuiltinFunction.SeqContains:
+ assert args.Length == 2;
+ assert typeInstantiation == null;
+ return FunctionCall(tok, "Seq#Contains", Bpl.Type.Bool, args);
+ case BuiltinFunction.SeqDrop:
+ assert args.Length == 2;
+ assert typeInstantiation != null;
+ return FunctionCall(tok, "Seq#Drop", predef.SeqType(tok, typeInstantiation), args);
+ case BuiltinFunction.SeqTake:
+ assert args.Length == 2;
+ assert typeInstantiation != null;
+ return FunctionCall(tok, "Seq#Take", predef.SeqType(tok, typeInstantiation), args);
+ case BuiltinFunction.SeqEqual:
+ assert args.Length == 2;
+ assert typeInstantiation == null;
+ return FunctionCall(tok, "Seq#Equal", Bpl.Type.Bool, args);
+ case BuiltinFunction.SeqSameUntil:
+ assert args.Length == 3;
+ assert typeInstantiation == null;
+ return FunctionCall(tok, "Seq#SameUntil", Bpl.Type.Bool, args);
+
+ case BuiltinFunction.Box:
+ assert args.Length == 1;
+ assert typeInstantiation == null;
+ return FunctionCall(tok, "$Box", predef.RefType, args);
+ case BuiltinFunction.Unbox:
+ assert false; // Unbox should be handled by a call to FunctionSpecial
+
+ case BuiltinFunction.IsGoodHeap:
+ assert args.Length == 1;
+ assert typeInstantiation == null;
+ return FunctionCall(tok, "$IsGoodHeap", Bpl.Type.Bool, args);
+ case BuiltinFunction.HeapSucc:
+ assert args.Length == 2;
+ assert typeInstantiation == null;
+ return FunctionCall(tok, "$HeapSucc", Bpl.Type.Bool, args);
+
+ case BuiltinFunction.DynamicType:
+ assert args.Length == 1;
+ return FunctionCall(tok, "dtype", predef.ClassNameType, args);
+ case BuiltinFunction.TypeParams:
+ assert args.Length == 2;
+ return FunctionCall(tok, "TypeParams", predef.ClassNameType, args);
+ case BuiltinFunction.TypeTuple:
+ assert args.Length == 2;
+ return FunctionCall(tok, "TypeTuple", predef.ClassNameType, args);
+
+ default:
+ assert false; // unexpected built-in function
+ }
+ }
+
+ Bpl.NAryExpr! FunctionSpecial(Token! tok, BuiltinFunction f, Bpl.Type! resultType, params Bpl.Expr[]! args)
+ {
+ switch (f) {
+ case BuiltinFunction.Unbox:
+ assert args.Length == 1;
+ return Bpl.Expr.CoerceType(tok, FunctionCall(tok, "$Unbox", resultType, args), resultType);
+ default:
+ assert false; // unexpected enum value
+ }
+ }
+
+ Bpl.NAryExpr! FunctionCall(Token! tok, string! function, Bpl.Type! returnType, params Bpl.Expr[]! args)
+ {
+ return new Bpl.NAryExpr(tok, new Bpl.FunctionCall(new Bpl.IdentifierExpr(tok, function, returnType)), new Bpl.ExprSeq(args));
+ }
+
+ public IEnumerable<Expression!>! SplitExpr(Expression! expr, bool expandFunctions)
+ requires expr.Type is BoolType;
+ {
+ if (expr is BinaryExpr) {
+ BinaryExpr bin = (BinaryExpr)expr;
+ if (bin.ResolvedOp == BinaryExpr.ResolvedOpcode.And) {
+ foreach (Expression e in SplitExpr(bin.E0, expandFunctions)) {
+ yield return e;
+ }
+ assert bin != null; // the necessity of this cast is a compiler bug, but perhaps an irrepairable one
+ foreach (Expression e in SplitExpr(bin.E1, expandFunctions)) {
+ yield return e;
+ }
+ yield break;
+
+ } else if (bin.ResolvedOp == BinaryExpr.ResolvedOpcode.Imp) {
+ foreach (Expression e in SplitExpr(bin.E1, expandFunctions)) {
+ assert bin != null;
+ BinaryExpr redistributedExpr = new BinaryExpr(e.tok, bin.Op, bin.E0, e);
+ redistributedExpr.ResolvedOp = bin.ResolvedOp; redistributedExpr.Type = bin.Type; // resolve on the fly
+ yield return redistributedExpr;
+ }
+ yield break;
+ }
+
+ } else if (expr is ITEExpr) {
+ ITEExpr ite = (ITEExpr)expr;
+ foreach (Expression e in SplitExpr(ite.Thn, expandFunctions)) {
+ assert ite != null; // the necessity of this cast is a compiler bug, but perhaps an irrepairable one
+ BinaryExpr bin = new BinaryExpr(e.tok, BinaryExpr.Opcode.Imp, ite.Test, e);
+ bin.ResolvedOp = BinaryExpr.ResolvedOpcode.Imp; bin.Type = ite.Type; // resolve on the fly
+ yield return bin;
+ }
+ assert ite != null; // the necessity of this cast is a compiler bug, but perhaps an irrepairable one
+ Expression negatedGuard = new UnaryExpr(ite.Test.tok, UnaryExpr.Opcode.Not, ite.Test);
+ negatedGuard.Type = ite.Test.Type; // resolve on the fly
+ foreach (Expression e in SplitExpr(ite.Els, expandFunctions)) {
+ assert ite != null; // the necessity of this cast is a compiler bug, but perhaps an irrepairable one
+ assert negatedGuard != null; // the necessity of this cast is a compiler bug, but perhaps an irrepairable one
+ BinaryExpr bin = new BinaryExpr(e.tok, BinaryExpr.Opcode.Imp, negatedGuard, e);
+ bin.ResolvedOp = BinaryExpr.ResolvedOpcode.Imp; bin.Type = ite.Type; // resolve on the fly
+ yield return bin;
+ }
+ yield break;
+
+ } else if (expr is OldExpr) {
+ foreach (Expression se in SplitExpr(((OldExpr)expr).E, expandFunctions)) {
+ OldExpr oe = new OldExpr(expr.tok, se);
+ oe.Type = se.Type;
+ yield return oe;
+ }
+ yield break;
+
+ } else if (expandFunctions && expr is FunctionCallExpr) {
+ FunctionCallExpr fexp = (FunctionCallExpr)expr;
+ assert fexp.Function != null; // filled in during resolution
+ if (fexp.Function.Body != null) {
+ // inline this body
+ Dictionary<IVariable,Expression!> substMap = new Dictionary<IVariable,Expression!>();
+ assert fexp.Args.Count == fexp.Function.Formals.Count;
+ for (int i = 0; i < fexp.Function.Formals.Count; i++) {
+ substMap.Add(fexp.Function.Formals[i], fexp.Args[i]);
+ }
+ Expression body = Substitute(fexp.Function.Body, fexp.Receiver, substMap);
+ foreach (Expression se in SplitExpr(body, false)) {
+ assert fexp != null && fexp.Function != null; // already checked above, but the compiler seems to have forgotten that
+ if (fexp.Function.Use) {
+ BinaryExpr imp = new BinaryExpr(expr.tok, BinaryExpr.Opcode.Imp, new UseExpr(fexp), se);
+ imp.ResolvedOp = BinaryExpr.ResolvedOpcode.Imp;
+ imp.Type = Type.Bool;
+ yield return imp;
+ } else {
+ yield return se;
+ }
+ }
+ assert fexp != null && fexp.Function != null; // already checked above, but the compiler seems to have forgotten that
+ if (fexp.Function.Use) {
+ UnaryExpr ue = new UnaryExpr(expr.tok, UnaryExpr.Opcode.Not, new UseExpr(fexp));
+ ue.Type = Type.Bool;
+ BinaryExpr imp = new BinaryExpr(expr.tok, BinaryExpr.Opcode.Imp, ue, expr);
+ imp.ResolvedOp = BinaryExpr.ResolvedOpcode.Imp;
+ imp.Type = Type.Bool;
+ yield return imp;
+ }
+ yield break;
+ }
+ }
+
+ yield return expr;
+ }
+
+ static Expression! Substitute(Expression! expr, Expression! receiverReplacement, Dictionary<IVariable,Expression!>! substMap) {
+ Expression newExpr = null; // set to non-null value only if substitution has any effect; if non-null, newExpr will be resolved at end
+
+ if (expr is LiteralExpr) {
+ // nothing to substitute
+ } else if (expr is ThisExpr) {
+ return receiverReplacement;
+ } else if (expr is IdentifierExpr) {
+ IdentifierExpr e = (IdentifierExpr)expr;
+ Expression substExpr;
+ if (substMap.TryGetValue(e.Var, out substExpr)) {
+ return (!)substExpr;
+ }
+ } else if (expr is DisplayExpression) {
+ DisplayExpression e = (DisplayExpression)expr;
+ List<Expression!> newElements = SubstituteExprList(e.Elements, receiverReplacement, substMap);
+ DisplayExpression newDisplayExpr;
+ if (newElements != e.Elements) {
+ if (expr is SetDisplayExpr) {
+ newExpr = new SetDisplayExpr(expr.tok, newElements);
+ } else {
+ newExpr = new SeqDisplayExpr(expr.tok, newElements);
+ }
+ }
+
+ } else if (expr is FieldSelectExpr) {
+ FieldSelectExpr fse = (FieldSelectExpr)expr;
+ Expression substE = Substitute(fse.Obj, receiverReplacement, substMap);
+ if (substE != fse.Obj) {
+ FieldSelectExpr fseNew = new FieldSelectExpr(fse.tok, substE, fse.FieldName);
+ fseNew.Field = fse.Field; // resolve on the fly (and fseExpr.Type is set at end of method)
+ newExpr = fseNew;
+ }
+
+ } else if (expr is SeqSelectExpr) {
+ SeqSelectExpr sse = (SeqSelectExpr)expr;
+ Expression seq = Substitute(sse.Seq, receiverReplacement, substMap);
+ Expression e0 = sse.E0 == null ? null : Substitute(sse.E0, receiverReplacement, substMap);
+ Expression e1 = sse.E1 == null ? null : Substitute(sse.E1, receiverReplacement, substMap);
+ if (seq != sse.Seq || e0 != sse.E0 || e1 != sse.E1) {
+ newExpr = new SeqSelectExpr(sse.tok, sse.SelectOne, seq, e0, e1);
+ }
+
+ } else if (expr is FunctionCallExpr) {
+ FunctionCallExpr e = (FunctionCallExpr)expr;
+ Expression receiver = Substitute(e.Receiver, receiverReplacement, substMap);
+ List<Expression!> newArgs = SubstituteExprList(e.Args, receiverReplacement, substMap);
+ if (receiver != e.Receiver || newArgs != e.Args) {
+ FunctionCallExpr newFce = new FunctionCallExpr(expr.tok, e.Name, receiver, newArgs);
+ newFce.Function = e.Function; // resolve on the fly (and set newFce.Type below, at end)
+ newExpr = newFce;
+ }
+
+ } else if (expr is OldExpr) {
+ OldExpr e = (OldExpr)expr;
+ Expression se = Substitute(e.E, receiverReplacement, substMap);
+ if (se != e.E) {
+ newExpr = new OldExpr(expr.tok, se);
+ }
+ } else if (expr is FreshExpr) {
+ FreshExpr e = (FreshExpr)expr;
+ Expression se = Substitute(e.E, receiverReplacement, substMap);
+ if (se != e.E) {
+ newExpr = new FreshExpr(expr.tok, se);
+ }
+ } else if (expr is UnaryExpr) {
+ UnaryExpr e = (UnaryExpr)expr;
+ Expression se = Substitute(e.E, receiverReplacement, substMap);
+ if (se != e.E) {
+ newExpr = new UnaryExpr(expr.tok, e.Op, se);
+ }
+ } else if (expr is BinaryExpr) {
+ BinaryExpr e = (BinaryExpr)expr;
+ Expression e0 = Substitute(e.E0, receiverReplacement, substMap);
+ Expression e1 = Substitute(e.E1, receiverReplacement, substMap);
+ if (e0 != e.E0 || e1 != e.E1) {
+ BinaryExpr newBin = new BinaryExpr(expr.tok, e.Op, e0, e1);
+ newBin.ResolvedOp = e.ResolvedOp; // part of what needs to be done to resolve on the fly (newBin.Type is set below, at end)
+ newExpr = newBin;
+ }
+
+ } else if (expr is QuantifierExpr) {
+ QuantifierExpr e = (QuantifierExpr)expr;
+ Expression newBody = Substitute(e.Body, receiverReplacement, substMap);
+ Triggers newTrigs = SubstTriggers(e.Trigs, receiverReplacement, substMap);
+ Attributes newAttrs = SubstAttributes(e.Attributes, receiverReplacement, substMap);
+ if (newBody != e.Body || newTrigs != e.Trigs || newAttrs != e.Attributes) {
+ if (expr is ForallExpr) {
+ newExpr = new ForallExpr(expr.tok, e.BoundVars, newBody, newTrigs, newAttrs);
+ } else {
+ newExpr = new ExistsExpr(expr.tok, e.BoundVars, newBody, newTrigs, newAttrs);
+ }
+ }
+
+ } else if (expr is ITEExpr) {
+ ITEExpr e = (ITEExpr)expr;
+ Expression test = Substitute(e.Test, receiverReplacement, substMap);
+ Expression thn = Substitute(e.Thn, receiverReplacement, substMap);
+ Expression els = Substitute(e.Els, receiverReplacement, substMap);
+ if (test != e.Test || thn != e.Thn || els != e.Els) {
+ newExpr = new ITEExpr(expr.tok, test, thn, els);
+ }
+ }
+
+ if (newExpr == null) {
+ return expr;
+ } else {
+ newExpr.Type = expr.Type; // resolve on the fly (any additional resolution must be done above)
+ return newExpr;
+ }
+ }
+
+ static List<Expression!>! SubstituteExprList(List<Expression!>! elist,
+ Expression! receiverReplacement, Dictionary<IVariable,Expression!>! substMap) {
+ List<Expression!> newElist = null; // initialized lazily
+ for (int i = 0; i < elist.Count; i++)
+ invariant newElist == null || newElist.Count == i;
+ {
+ Expression substE = Substitute(elist[i], receiverReplacement, substMap);
+ if (substE != elist[i] && newElist == null) {
+ newElist = new List<Expression!>();
+ for (int j = 0; j < i; j++) {
+ newElist.Add(elist[j]);
+ }
+ }
+ if (newElist != null) {
+ newElist.Add(substE);
+ }
+ }
+ if (newElist == null) {
+ return elist;
+ } else {
+ return newElist;
+ }
+ }
+
+ static Triggers SubstTriggers(Triggers trigs, Expression! receiverReplacement, Dictionary<IVariable,Expression!>! substMap) {
+ if (trigs != null) {
+ List<Expression!> terms = SubstituteExprList(trigs.Terms, receiverReplacement, substMap);
+ Triggers prev = SubstTriggers(trigs.Prev, receiverReplacement, substMap);
+ if (terms != trigs.Terms || prev != trigs.Prev) {
+ return new Triggers(terms, prev);
+ }
+ }
+ return trigs;
+ }
+
+ static Attributes SubstAttributes(Attributes attrs, Expression! receiverReplacement, Dictionary<IVariable,Expression!>! substMap) {
+ if (attrs != null) {
+ List<Attributes.Argument!> newArgs = new List<Attributes.Argument!>(); // allocate it eagerly, what the heck, it doesn't seem worth the extra complexity in the code to do it lazily for the infrequently occurring attributes
+ bool anyArgSubst = false;
+ foreach (Attributes.Argument arg in attrs.Args) {
+ Attributes.Argument newArg = arg;
+ if (arg.E != null) {
+ Expression newE = Substitute(arg.E, receiverReplacement, substMap);
+ if (newE != arg.E) {
+ newArg = new Attributes.Argument(newE);
+ anyArgSubst = true;
+ }
+ }
+ newArgs.Add(newArg);
+ }
+ if (!anyArgSubst) {
+ newArgs = attrs.Args;
+ }
+
+ Attributes prev = SubstAttributes(attrs.Prev, receiverReplacement, substMap);
+ if (newArgs != attrs.Args || prev != attrs.Prev) {
+ return new Attributes(attrs.Name, newArgs, prev);
+ }
+ }
+ return attrs;
+ }
+
+ }
+}
diff --git a/Source/Dafny/parser.frame b/Source/Dafny/parser.frame
new file mode 100644
index 00000000..de0ba1fb
--- /dev/null
+++ b/Source/Dafny/parser.frame
@@ -0,0 +1,103 @@
+
+using Microsoft.Contracts;
+
+namespace Microsoft.-->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;
+ }
+
+ public static void SemErr(Token! tok, string! msg) {
+ if (errDist >= minErrDist) Errors.SemErr(tok.filename, tok.line, tok.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++;
+ System.Console.Write("{0}({1},{2}): syntax error: ", filename, line, col);
+ string s;
+ switch (n) {
+-->errors
+ default: s = "error " + n; break;
+ }
+ System.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/Dafny/scanner.frame b/Source/Dafny/scanner.frame
new file mode 100644
index 00000000..a526c7f2
--- /dev/null
+++ b/Source/Dafny/scanner.frame
@@ -0,0 +1,170 @@
+using System.IO;
+using System.Collections;
+using System.Collections.Generic;
+using System.Text;
+using Microsoft.Contracts;
+using Bpl = Microsoft.Boogie;
+using BoogiePL;
+
+
+namespace Microsoft.Dafny {
+
+ [Immutable]
+ public class Token : Bpl.Token {
+ public Token();
+ public Token(int linenum, int colnum) {
+ base(linenum, colnum);
+ }
+ public new static Token! NoToken = new Token();
+ }
+
+}
+
+namespace Microsoft.-->namespace {
+
+
+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)BoogiePL.Buffer.Read(); pos++;
+ if (ch == BoogiePL.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 = BoogiePL.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 (System.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
+ System.ConsoleColor color = System.Console.ForegroundColor;
+ System.Console.ForegroundColor = System.ConsoleColor.Red;
+ System.Console.WriteLine("{0}({1},{2}): Error: {3}", filename, line, col, msg);
+ System.Console.ForegroundColor = color;
+ count++;
+ }
+
+ public static void SemErr(Bpl.IToken! tok, string! msg) { // semantic errors
+ SemErr(tok.filename, tok.line, tok.col, msg);
+ }
+
+ public static void Exception (string s) {
+ System.ConsoleColor color = System.Console.ForegroundColor;
+ System.Console.ForegroundColor = System.ConsoleColor.Red;
+ System.Console.WriteLine(s);
+ System.Console.ForegroundColor = color;
+ System.Environment.Exit(0);
+ }
+
+} // Errors
+
+} // end namespace
+$$$
diff --git a/Source/DafnyDriver/DafnyDriver.ssc b/Source/DafnyDriver/DafnyDriver.ssc
new file mode 100644
index 00000000..06280e74
--- /dev/null
+++ b/Source/DafnyDriver/DafnyDriver.ssc
@@ -0,0 +1,681 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+//---------------------------------------------------------------------------------------------
+// OnlyDafny OnlyDafny.ssc
+// - main program for taking a Dafny program and verifying it
+//---------------------------------------------------------------------------------------------
+
+namespace Microsoft.Boogie
+{
+ using System;
+ using System.IO;
+ using System.Collections;
+ using System.Collections.Generic;
+ using PureCollections;
+ using Microsoft.Boogie;
+ using Microsoft.Boogie.AbstractInterpretation;
+ using System.Diagnostics;
+ using VC;
+ using Cci = System.Compiler;
+ using AI = Microsoft.AbstractInterpretationFramework;
+
+/*
+ The following assemblies are referenced because they are needed at runtime, not at compile time:
+ BaseTypes
+ Provers.Z3
+ System.Compiler.Framework
+*/
+
+ public class OnlyDafny
+ {
+ // ------------------------------------------------------------------------
+ // Main
+
+ public static void Main (string[]! args)
+ {
+ assert forall{int i in (0:args.Length); args[i] != null};
+ CommandLineOptions.Clo.RunningBoogieFromCommandLine = true;
+ if (CommandLineOptions.Clo.Parse(args) != 1)
+ {
+ goto END;
+ }
+ if (CommandLineOptions.Clo.Files.Count == 0)
+ {
+ ErrorWriteLine("*** Error: No input files were specified.");
+ goto END;
+ }
+ if (CommandLineOptions.Clo.XmlSink != null) {
+ string errMsg = CommandLineOptions.Clo.XmlSink.Open();
+ if (errMsg != null) {
+ ErrorWriteLine("*** Error: " + errMsg);
+ goto END;
+ }
+ }
+ if (!CommandLineOptions.Clo.DontShowLogo)
+ {
+ Console.WriteLine(CommandLineOptions.Clo.Version);
+ }
+ if (CommandLineOptions.Clo.ShowEnv == CommandLineOptions.ShowEnvironment.Always)
+ {
+ Console.WriteLine("---Command arguments");
+ foreach (string! arg in args)
+ {
+ Console.WriteLine(arg);
+ }
+ Console.WriteLine("--------------------");
+ }
+
+ SelectPlatform(CommandLineOptions.Clo);
+
+ // Make sure the Spec# runtime is initialized.
+ // This happens when the static constructor for the Runtime type is executed.
+ // Otherwise, if a reference happens to get chased to it, it is loaded twice
+ // and then the types in it do not get unique representations.
+ if (System.Type.GetType("Mono.Runtime") == null) { // MONO
+ Cci.AssemblyNode a = Microsoft.SpecSharp.Runtime.RuntimeAssembly;
+ assert a != null;
+ }
+
+ foreach (string! file in CommandLineOptions.Clo.Files)
+ {
+ string extension = Path.GetExtension(file);
+ if (extension != null) { extension = extension.ToLower(); }
+ if (extension != ".dfy")
+ {
+ ErrorWriteLine("*** Error: '{0}': Filename extension '{1}' is not supported. Input files must be Dafny programs (.dfy).", file,
+ extension == null ? "" : extension);
+ goto END;
+ }
+ }
+ CommandLineOptions.Clo.RunningBoogieOnSsc = false;
+ ProcessFiles(CommandLineOptions.Clo.Files);
+
+ END:
+ if (CommandLineOptions.Clo.XmlSink != null) {
+ CommandLineOptions.Clo.XmlSink.Close();
+ }
+ if (CommandLineOptions.Clo.Wait)
+ {
+ Console.WriteLine("Press Enter to exit.");
+ Console.ReadLine();
+ }
+ }
+
+ public static void ErrorWriteLine(string! s) {
+ if (!s.Contains("Error: ") && !s.Contains("Error BP")) {
+ Console.WriteLine(s);
+ return;
+ }
+
+ // split the string up into its first line and the remaining lines
+ string remaining = null;
+ int i = s.IndexOf('\r');
+ if (0 <= i) {
+ remaining = s.Substring(i+1);
+ if (remaining.StartsWith("\n")) {
+ remaining = remaining.Substring(1);
+ }
+ s = s.Substring(0, i);
+ }
+
+ ConsoleColor col = Console.ForegroundColor;
+ Console.ForegroundColor = ConsoleColor.Red;
+ Console.WriteLine(s);
+ Console.ForegroundColor = col;
+
+ if (remaining != null) {
+ Console.WriteLine(remaining);
+ }
+ }
+
+ public static void ErrorWriteLine(string! format, params object[] args) {
+ string s = string.Format(format, args);
+ ErrorWriteLine(s);
+ }
+
+ public static void AdvisoryWriteLine(string! format, params object[] args) {
+ ConsoleColor col = Console.ForegroundColor;
+ Console.ForegroundColor = ConsoleColor.Yellow;
+ Console.WriteLine(format, args);
+ Console.ForegroundColor = col;
+ }
+
+ public static void SelectPlatform(CommandLineOptions! options)
+ {
+ if (options.TargetPlatformLocation != null) {
+ // Make sure static constructor runs before we start setting locations, etc.
+ System.Compiler.SystemTypes.Clear();
+
+ switch (options.TargetPlatform){
+ case CommandLineOptions.PlatformType.v1: Microsoft.SpecSharp.TargetPlatform.SetToV1(options.TargetPlatformLocation); break;
+ case CommandLineOptions.PlatformType.v11: Microsoft.SpecSharp.TargetPlatform.SetToV1_1(options.TargetPlatformLocation); break;
+ case CommandLineOptions.PlatformType.v2: Microsoft.SpecSharp.TargetPlatform.SetToV2(options.TargetPlatformLocation); break;
+ case CommandLineOptions.PlatformType.cli1: Microsoft.SpecSharp.TargetPlatform.SetToPostV1_1(options.TargetPlatformLocation); break;
+ }
+
+ if (options.StandardLibraryLocation != null && options.StandardLibraryLocation.Length > 0){
+ System.Compiler.SystemAssemblyLocation.Location = options.StandardLibraryLocation;
+ }
+ System.Compiler.SystemCompilerRuntimeAssemblyLocation.Location = options.TargetPlatformLocation + @"\System.Compiler.Runtime.dll";
+
+ System.Compiler.SystemTypes.Initialize(true, true);
+ }
+ }
+
+ static string! GetErrorLine(Cci.ErrorNode! node)
+ requires node.SourceContext.Document != null ==> node.SourceContext.Document.Name != null;
+ {
+ string message = node.GetMessage(System.Threading.Thread.CurrentThread.CurrentUICulture);
+ string kind;
+ if (message.Contains("(trace position)")) {
+ kind = "Related information";
+ } else {
+ kind = "Error";
+ }
+ if (node.SourceContext.Document != null) {
+ return string.Format("{0}({1},{2}): {3}: {4}", Path.GetFileName((!)node.SourceContext.Document.Name), node.SourceContext.StartLine, node.SourceContext.StartColumn, kind, message);
+ } else {
+ return string.Format("{0}: {1}", kind, message);
+ }
+ }
+
+ enum FileType { Unknown, Cil, Bpl, Dafny };
+
+ class ErrorReporter
+ {
+ public Cci.ErrorNodeList! errors = new Cci.ErrorNodeList();
+ public int errorsReported;
+
+ public void ReportErrors()
+ {
+ //sort the portion of the array that will be reported to make output more deterministic
+ errors.Sort(errorsReported,errors.Count-errorsReported);
+ for(;errorsReported < errors.Count; errorsReported++) {
+ Cci.ErrorNode error = errors[errorsReported];
+ if (error != null)
+ ErrorWriteLine(GetErrorLine(error));
+ }
+ }
+ }
+
+ static void ProcessFiles (List<string!>! fileNames)
+ {
+ using (XmlFileScope xf = new XmlFileScope(CommandLineOptions.Clo.XmlSink, fileNames[fileNames.Count-1])) {
+ Dafny.Program dafnyProgram;
+ string err = Dafny.Main.ParseCheck(fileNames, "synthetic program", out dafnyProgram);
+ if (err != null) {
+ ErrorWriteLine(err);
+ } else if (dafnyProgram != null) {
+ Dafny.Translator translator = new Dafny.Translator();
+ Program boogieProgram = translator.Translate(dafnyProgram);
+ if (CommandLineOptions.Clo.PrintFile != null)
+ {
+ PrintBplFile(CommandLineOptions.Clo.PrintFile, boogieProgram, false);
+ }
+
+ string! bplFilename;
+ if (CommandLineOptions.Clo.PrintFile != null) {
+ bplFilename = CommandLineOptions.Clo.PrintFile;
+ } else {
+ string baseName = (!)Path.GetFileName(fileNames[fileNames.Count-1]);
+ baseName = (!)Path.ChangeExtension(baseName, "bpl");
+ bplFilename = Path.Combine(Path.GetTempPath(), baseName);
+ }
+
+ int errorCount, verified, inconclusives, timeOuts, outOfMemories;
+ PipelineOutcome oc = BoogiePipelineWithRerun(boogieProgram, bplFilename, null/*new ErrorReporter()*/, out errorCount, out verified, out inconclusives, out timeOuts, out outOfMemories);
+ switch (oc) {
+ case PipelineOutcome.Done:
+ case PipelineOutcome.VerificationCompleted:
+ WriteTrailer(verified, errorCount, inconclusives, timeOuts, outOfMemories);
+ break;
+ default:
+ // error has already been reported to user
+ break;
+ }
+ }
+ }
+ }
+
+
+ static void PrintBplFile (string! filename, Program! program, bool allowPrintDesugaring)
+ {
+ bool oldPrintDesugaring = CommandLineOptions.Clo.PrintDesugarings;
+ if (!allowPrintDesugaring) {
+ CommandLineOptions.Clo.PrintDesugarings = false;
+ }
+ using (TokenTextWriter writer = filename == "-" ?
+ new TokenTextWriter("<console>", Console.Out) :
+ new TokenTextWriter(filename))
+ {
+ writer.WriteLine("// " + CommandLineOptions.Clo.Version);
+ writer.WriteLine("// " + CommandLineOptions.Clo.Environment);
+ writer.WriteLine();
+ program.Emit(writer);
+ }
+ CommandLineOptions.Clo.PrintDesugarings = oldPrintDesugaring;
+ }
+
+
+ static bool ProgramHasDebugInfo (Program! program)
+ {
+ // We inspect the last declaration because the first comes from the prelude and therefore always has source context.
+ return program.TopLevelDeclarations.Count > 0 &&
+ ((!)program.TopLevelDeclarations[program.TopLevelDeclarations.Count - 1]).tok.IsValid;
+ }
+
+
+ /// <summary>
+ /// Inform the user about something and proceed with translation normally.
+ /// Print newline after the message.
+ /// </summary>
+ public static void Inform(string s) {
+ if ( ! CommandLineOptions.Clo.Trace) { return; }
+ Console.WriteLine(s);
+ }
+
+ static void WriteTrailer(int verified, int errors, int inconclusives, int timeOuts, int outOfMemories)
+ requires 0 <= errors && 0 <= inconclusives && 0 <= timeOuts && 0 <= outOfMemories;
+ {
+ Console.WriteLine();
+ Console.Write("{0} finished with {1} verified, {2} error{3}", CommandLineOptions.Clo.ToolName, verified, errors, errors == 1 ? "" : "s");
+ if (inconclusives != 0) {
+ Console.Write(", {0} inconclusive{1}", inconclusives, inconclusives == 1 ? "" : "s");
+ }
+ if (timeOuts != 0) {
+ Console.Write(", {0} time out{1}", timeOuts, timeOuts == 1 ? "" : "s");
+ }
+ if (outOfMemories != 0) {
+ Console.Write(", {0} out of memory", outOfMemories);
+ }
+ Console.WriteLine();
+ Console.Out.Flush();
+ }
+
+
+
+ static void ReportBplError(Absy! node, string! message, bool error)
+ {
+ IToken tok = node.tok;
+ string s;
+ if (tok == null) {
+ s = message;
+ } else {
+ s = string.Format("{0}({1},{2}): {3}", tok.filename, tok.line, tok.col, message);
+ }
+ if (error) {
+ ErrorWriteLine(s);
+ } else {
+ Console.WriteLine(s);
+ }
+ }
+
+ /// <summary>
+ /// Parse the given files into one Boogie program. If an I/O or parse error occurs, an error will be printed
+ /// and null will be returned. On success, a non-null program is returned.
+ /// </summary>
+ static Program ParseBoogieProgram(List<string!>! fileNames, bool suppressTraceOutput)
+ {
+ BoogiePL.Errors.count = 0;
+ Program program = null;
+ bool okay = true;
+ foreach (string bplFileName in fileNames) {
+ if (!suppressTraceOutput) {
+ if (CommandLineOptions.Clo.XmlSink != null) {
+ CommandLineOptions.Clo.XmlSink.WriteFileFragment(bplFileName);
+ }
+ if (CommandLineOptions.Clo.Trace) {
+ Console.WriteLine("Parsing " + bplFileName);
+ }
+ }
+
+ Program programSnippet;
+ int errorCount;
+ try {
+ errorCount = BoogiePL.Parser.Parse(bplFileName, out programSnippet);
+ if (programSnippet == null || errorCount != 0) {
+ Console.WriteLine("{0} parse errors detected in {1}", BoogiePL.Errors.count, bplFileName);
+ okay = false;
+ continue;
+ }
+ } catch (IOException e) {
+ ErrorWriteLine("Error opening file \"{0}\": {1}", bplFileName, e.Message);
+ okay = false;
+ continue;
+ }
+ if (program == null) {
+ program = programSnippet;
+ } else if (programSnippet != null) {
+ program.TopLevelDeclarations.AddRange(programSnippet.TopLevelDeclarations);
+ }
+ }
+ if (!okay) {
+ return null;
+ } else if (program == null) {
+ return new Program();
+ } else {
+ return program;
+ }
+ }
+
+ /// <summary>
+ /// Resolve, type check, infer invariants for, and verify the given Boogie program.
+ /// The intention is that this Boogie program has been produced by translation from something
+ /// else. Hence, any resolution errors and type checking errors are due to errors in
+ /// the translation.
+ /// The method prints errors for resolution and type checking errors, but still returns
+ /// their error code.
+ /// </summary>
+ static PipelineOutcome BoogiePipelineWithRerun (Program! program, string! bplFileName,
+ ErrorReporter errorReporter,
+ out int errorCount, out int verified, out int inconclusives, out int timeOuts, out int outOfMemories)
+ ensures 0 <= inconclusives && 0 <= timeOuts;
+ {
+ errorCount = verified = inconclusives = timeOuts = outOfMemories = 0;
+ PipelineOutcome oc = ResolveAndTypecheck(program, bplFileName);
+ switch (oc) {
+ case PipelineOutcome.Done:
+ return oc;
+
+ case PipelineOutcome.ResolutionError:
+ case PipelineOutcome.TypeCheckingError:
+ {
+ PrintBplFile(bplFileName, program, false);
+ Console.WriteLine();
+ Console.WriteLine("*** Encountered internal translation error - re-running Boogie to get better debug information");
+ Console.WriteLine();
+
+ List<string!>! fileNames = new List<string!> ();
+ fileNames.Add(bplFileName);
+ Program reparsedProgram = ParseBoogieProgram(fileNames, true);
+ if (reparsedProgram != null) {
+ ResolveAndTypecheck(reparsedProgram, bplFileName);
+ }
+ }
+ return oc;
+
+ case PipelineOutcome.ResolvedAndTypeChecked:
+ return InferAndVerify(program, errorReporter, out errorCount, out verified, out inconclusives, out timeOuts, out outOfMemories);
+
+ default:
+ assert false; // unexpected outcome
+ }
+ }
+
+
+ enum PipelineOutcome { Done, ResolutionError, TypeCheckingError, ResolvedAndTypeChecked, FatalError, VerificationCompleted }
+
+ /// <summary>
+ /// Resolves and type checks the given Boogie program. Any errors are reported to the
+ /// console. Returns:
+ /// - Done if no errors occurred, and command line specified no resolution or no type checking.
+ /// - ResolutionError if a resolution error occurred
+ /// - TypeCheckingError if a type checking error occurred
+ /// - ResolvedAndTypeChecked if both resolution and type checking succeeded
+ /// </summary>
+ static PipelineOutcome ResolveAndTypecheck (Program! program, string! bplFileName)
+ {
+ // ---------- Resolve ------------------------------------------------------------
+
+ if (CommandLineOptions.Clo.NoResolve) { return PipelineOutcome.Done; }
+
+ int errorCount = program.Resolve();
+ if (errorCount != 0) {
+ Console.WriteLine("{0} name resolution errors detected in {1}", errorCount, bplFileName);
+ return PipelineOutcome.ResolutionError;
+ }
+
+ // ---------- Type check ------------------------------------------------------------
+
+ if (CommandLineOptions.Clo.NoTypecheck) { return PipelineOutcome.Done; }
+
+ errorCount = program.Typecheck();
+ if (errorCount != 0) {
+ Console.WriteLine("{0} type checking errors detected in {1}", errorCount, bplFileName);
+ return PipelineOutcome.TypeCheckingError;
+ }
+
+ if (CommandLineOptions.Clo.PrintFile != null && CommandLineOptions.Clo.PrintDesugarings)
+ {
+ // if PrintDesugaring option is engaged, print the file here, after resolution and type checking
+ PrintBplFile(CommandLineOptions.Clo.PrintFile, program, true);
+ }
+
+ return PipelineOutcome.ResolvedAndTypeChecked;
+ }
+
+ /// <summary>
+ /// Given a resolved and type checked Boogie program, infers invariants for the program
+ /// and then attempts to verify it. Returns:
+ /// - Done if command line specified no verification
+ /// - FatalError if a fatal error occurred, in which case an error has been printed to console
+ /// - VerificationCompleted if inference and verification completed, in which the out
+ /// parameters contain meaningful values
+ /// </summary>
+ static PipelineOutcome InferAndVerify (Program! program,
+ ErrorReporter errorReporter,
+ out int errorCount, out int verified, out int inconclusives, out int timeOuts, out int outOfMemories)
+ ensures 0 <= inconclusives && 0 <= timeOuts;
+ {
+ errorCount = verified = inconclusives = timeOuts = outOfMemories = 0;
+
+ // ---------- Infer invariants --------------------------------------------------------
+
+ // Abstract interpretation -> Always use (at least) intervals, if not specified otherwise (e.g. with the "/noinfer" switch)
+ Microsoft.Boogie.AbstractInterpretation.AbstractInterpretation.RunAbstractInterpretation(program);
+
+ if (CommandLineOptions.Clo.LoopUnrollCount != -1) {
+ program.UnrollLoops(CommandLineOptions.Clo.LoopUnrollCount);
+ }
+
+ if (CommandLineOptions.Clo.PrintInstrumented) {
+ program.Emit(new TokenTextWriter(Console.Out));
+ }
+
+ // ---------- Verify ------------------------------------------------------------
+
+ if (!CommandLineOptions.Clo.Verify) { return PipelineOutcome.Done; }
+
+ #region Verify each implementation
+
+#if ROB_DEBUG
+ string now = DateTime.Now.ToString().Replace(' ','-').Replace('/','-').Replace(':','-');
+ System.IO.StreamWriter w = new System.IO.StreamWriter(@"\temp\batch_"+now+".bpl");
+ program.Emit(new TokenTextWriter(w));
+ w.Close();
+#endif
+
+ ConditionGeneration vcgen = null;
+ try
+ {
+ if (CommandLineOptions.Clo.vcVariety == CommandLineOptions.VCVariety.Doomed)
+ {
+ vcgen = new DCGen(program, CommandLineOptions.Clo.SimplifyLogFilePath, CommandLineOptions.Clo.SimplifyLogFileAppend);
+ } else
+ {
+ vcgen = new VCGen(program, CommandLineOptions.Clo.SimplifyLogFilePath, CommandLineOptions.Clo.SimplifyLogFileAppend);
+ }
+ }
+ catch (ProverException e)
+ {
+ ErrorWriteLine("Fatal Error: ProverException: {0}", e);
+ return PipelineOutcome.FatalError;
+ }
+
+ foreach ( Declaration! decl in program.TopLevelDeclarations )
+ {
+ Implementation impl = decl as Implementation;
+ if (impl != null && CommandLineOptions.Clo.UserWantsToCheckRoutine((!)impl.Name) && !impl.SkipVerification) {
+ List<Counterexample!>? errors;
+
+ DateTime start = new DateTime(); // to please compiler's definite assignment rules
+ if (CommandLineOptions.Clo.Trace || CommandLineOptions.Clo.XmlSink != null) {
+ start = DateTime.Now;
+ if (CommandLineOptions.Clo.Trace) {
+ Console.WriteLine();
+ Console.WriteLine("Verifying {0} ...", impl.Name);
+ }
+ if (CommandLineOptions.Clo.XmlSink != null) {
+ CommandLineOptions.Clo.XmlSink.WriteStartMethod(impl.Name, start);
+ }
+ }
+
+ ConditionGeneration.Outcome outcome;
+ try
+ {
+ outcome = vcgen.VerifyImplementation(impl, program, out errors);
+ }
+ catch (VCGenException e)
+ {
+ ReportBplError(impl, String.Format("Error BP5010: {0} Encountered in implementation {1}.", e.Message, impl.Name), true);
+ errors = null;
+ outcome = VCGen.Outcome.Inconclusive;
+ }
+ catch (UnexpectedProverOutputException upo)
+ {
+ AdvisoryWriteLine("Advisory: {0} SKIPPED because of internal error: unexpected prover output: {1}", impl.Name, upo.Message);
+ errors = null;
+ outcome = VCGen.Outcome.Inconclusive;
+ }
+
+ string! timeIndication = "";
+ DateTime end = DateTime.Now;
+ TimeSpan elapsed = end - start;
+ if (CommandLineOptions.Clo.Trace || CommandLineOptions.Clo.XmlSink != null) {
+ if (CommandLineOptions.Clo.Trace) {
+ timeIndication = string.Format(" [{0} s] ", elapsed.TotalSeconds);
+ }
+ }
+
+
+ switch (outcome) {
+ default:
+ assert false; // unexpected outcome
+ case VCGen.Outcome.Correct:
+ Inform(String.Format("{0}verified", timeIndication));
+ verified++;
+ break;
+ case VCGen.Outcome.TimedOut:
+ timeOuts++;
+ Inform(String.Format("{0}timed out", timeIndication));
+ break;
+ case VCGen.Outcome.OutOfMemory:
+ outOfMemories++;
+ Inform(String.Format("{0}out of memory", timeIndication));
+ break;
+ case VCGen.Outcome.Inconclusive:
+ inconclusives++;
+ Inform(String.Format("{0}inconclusive", timeIndication));
+ break;
+ case VCGen.Outcome.Errors:
+ assert errors != null; // guaranteed by postcondition of VerifyImplementation
+ if (errorReporter != null)
+ {
+// assert translatedProgram != null;
+// ErrorReporting h = new ErrorReporting();
+// h.errorReportingWithTrace(translatedProgram, errors, impl);
+
+ errorReporter.ReportErrors();
+ }
+ else
+ {
+ // BP1xxx: Parsing errors
+ // BP2xxx: Name resolution errors
+ // BP3xxx: Typechecking errors
+ // BP4xxx: Abstract interpretation errors (Is there such a thing?)
+ // BP5xxx: Verification errors
+
+ errors.Sort(new CounterexampleComparer());
+ foreach (Counterexample error in errors)
+ {
+ if (error is CallCounterexample)
+ {
+ CallCounterexample! err = (CallCounterexample) error;
+ ReportBplError(err.FailingCall, "Error BP5002: A precondition for this call might not hold.", true);
+ ReportBplError(err.FailingRequires, "Related location: This is the precondition that might not hold.", false);
+ if (CommandLineOptions.Clo.XmlSink != null) {
+ CommandLineOptions.Clo.XmlSink.WriteError("precondition violation", err.FailingCall.tok, err.FailingRequires.tok, error.Trace);
+ }
+ }
+ else if (error is ReturnCounterexample)
+ {
+ ReturnCounterexample! err = (ReturnCounterexample) error;
+ ReportBplError(err.FailingReturn, "Error BP5003: A postcondition might not hold at this return statement.", true);
+ ReportBplError(err.FailingEnsures, "Related location: This is the postcondition that might not hold.", false);
+ if (CommandLineOptions.Clo.XmlSink != null) {
+ CommandLineOptions.Clo.XmlSink.WriteError("postcondition violation", err.FailingReturn.tok, err.FailingEnsures.tok, error.Trace);
+ }
+ }
+ else // error is AssertCounterexample
+ {
+ AssertCounterexample! err = (AssertCounterexample) error;
+ if (err.FailingAssert is LoopInitAssertCmd) {
+ ReportBplError(err.FailingAssert, "Error BP5004: This loop invariant might not hold on entry.", true);
+ if (CommandLineOptions.Clo.XmlSink != null) {
+ CommandLineOptions.Clo.XmlSink.WriteError("loop invariant entry violation", err.FailingAssert.tok, null, error.Trace);
+ }
+ }
+ else if (err.FailingAssert is LoopInvMaintainedAssertCmd) {
+ // this assertion is a loop invariant which is not maintained
+ ReportBplError(err.FailingAssert, "Error BP5005: This loop invariant might not be maintained by the loop.", true);
+ if (CommandLineOptions.Clo.XmlSink != null) {
+ CommandLineOptions.Clo.XmlSink.WriteError("loop invariant maintenance violation", err.FailingAssert.tok, null, error.Trace);
+ }
+ } else {
+ string msg = err.FailingAssert.ErrorData as string;
+ if (msg == null) {
+ msg = "Error BP5001: This assertion might not hold.";
+ }
+ ReportBplError(err.FailingAssert, msg, true);
+ if (CommandLineOptions.Clo.XmlSink != null) {
+ CommandLineOptions.Clo.XmlSink.WriteError("assertion violation", err.FailingAssert.tok, null, error.Trace);
+ }
+ }
+ }
+ if (CommandLineOptions.Clo.EnhancedErrorMessages == 1) {
+ foreach (string! info in error.relatedInformation) {
+ Console.WriteLine(" " + info);
+ }
+ }
+ if (CommandLineOptions.Clo.ErrorTrace > 0) {
+ Console.WriteLine("Execution trace:");
+ foreach (Block! b in error.Trace) {
+ if (b.tok == null) {
+ Console.WriteLine(" <intermediate block>");
+ } else {
+ // for ErrorTrace == 1 restrict the output;
+ // do not print tokens with -17:-4 as their location because they have been
+ // introduced in the translation and do not give any useful feedback to the user
+ if (!(CommandLineOptions.Clo.ErrorTrace == 1 && b.tok.line == -17 && b.tok.col == -4)) {
+ Console.WriteLine(" {0}({1},{2}): {3}", b.tok.filename, b.tok.line, b.tok.col, b.Label);
+ }
+ }
+ }
+ }
+ errorCount++;
+ }
+ }
+ Inform(String.Format("{0}error{1}", timeIndication, errors.Count == 1 ? "" : "s"));
+ break;
+ }
+ if (CommandLineOptions.Clo.XmlSink != null) {
+ CommandLineOptions.Clo.XmlSink.WriteEndMethod(outcome.ToString().ToLowerInvariant(), end, elapsed);
+ }
+ if (outcome == VCGen.Outcome.Errors || CommandLineOptions.Clo.Trace) {
+ Console.Out.Flush();
+ }
+ }
+ }
+ vcgen.Close();
+ ((!)CommandLineOptions.Clo.TheProverFactory).Close();
+
+ #endregion
+
+ return PipelineOutcome.VerificationCompleted;
+ }
+
+ }
+}
diff --git a/Source/DafnyDriver/DafnyDriver.sscproj b/Source/DafnyDriver/DafnyDriver.sscproj
new file mode 100644
index 00000000..9beb51a0
--- /dev/null
+++ b/Source/DafnyDriver/DafnyDriver.sscproj
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="utf-8"?>
+<VisualStudioProject>
+ <XEN ProjectType="Local"
+ SchemaVersion="1.0"
+ Name="DafnyDriver"
+ ProjectGuid="1f1e6f68-e9df-4181-8cd3-e8c98637084d"
+ >
+ <Build>
+ <Settings ApplicationIcon=""
+ AssemblyName="Dafny"
+ OutputType="Exe"
+ RootNamespace="Dafny"
+ StartupObject=""
+ StandardLibraryLocation=""
+ TargetPlatform="v2"
+ TargetPlatformLocation=""
+ >
+ <Config Name="Debug"
+ AllowUnsafeBlocks="False"
+ BaseAddress="285212672"
+ CheckForOverflowUnderflow="False"
+ ConfigurationOverrideFile=""
+ DefineConstants="DEBUG;TRACE"
+ DocumentationFile=""
+ DebugSymbols="True"
+ FileAlignment="4096"
+ IncrementalBuild="True"
+ Optimize="False"
+ OutputPath="..\..\Binaries"
+ RegisterForComInterop="False"
+ RemoveIntegerChecks="false"
+ TreatWarningsAsErrors="False"
+ WarningLevel="4"
+ CheckContractAdmissibility="True"
+ CheckPurity="False"
+ />
+ <Config Name="Release"
+ AllowUnsafeBlocks="false"
+ BaseAddress="285212672"
+ CheckForOverflowUnderflow="false"
+ ConfigurationOverrideFile=""
+ DefineConstants="TRACE"
+ DocumentationFile=""
+ DebugSymbols="false"
+ FileAlignment="4096"
+ IncrementalBuild="false"
+ Optimize="true"
+ OutputPath="bin\release"
+ RegisterForComInterop="false"
+ RemoveIntegerChecks="false"
+ TreatWarningsAsErrors="false"
+ WarningLevel="4"
+ CheckContractAdmissibility="True"
+ CheckPurity="False"
+ />
+ </Settings>
+ <References>
+ <Reference Name="System"
+ AssemblyName="System"
+ Private="false"
+ />
+ <Reference Name="System.Data"
+ AssemblyName="System.Data"
+ Private="false"
+ />
+ <Reference Name="System.Xml"
+ AssemblyName="System.Xml"
+ Private="false"
+ />
+ <Reference Name="System.Compiler"
+ AssemblyName="System.Compiler"
+ Private="false"
+ HintPath="../../Binaries/System.Compiler.dll"
+ />
+ <Reference Name="Microsoft.SpecSharp"
+ AssemblyName="Microsoft.SpecSharp"
+ Private="false"
+ HintPath="../../Binaries/Microsoft.SpecSharp.dll"
+ />
+ <Reference Name="System.Compiler.Framework"
+ AssemblyName="System.Compiler.Framework"
+ Private="false"
+ HintPath="../../Binaries/System.Compiler.Framework.dll"
+ />
+ <Reference Name="AbsInt"
+ AssemblyName="AbsInt"
+ Private="false"
+ HintPath="../AbsInt/bin/Debug/AbsInt.dll"
+ />
+ <Reference Name="AIFramework"
+ AssemblyName="AIFramework"
+ Private="false"
+ HintPath="../AIFramework/bin/debug/AIFramework.dll"
+ />
+ <Reference Name="Core"
+ AssemblyName="Core"
+ Private="false"
+ HintPath="../Core/bin/Debug/Core.dll"
+ />
+ <Reference Name="VCGeneration"
+ AssemblyName="VCGeneration"
+ Private="false"
+ HintPath="../VCGeneration/bin/debug/VCGeneration.dll"
+ />
+ <Reference Name="Dafny"
+ Project="{DEAD83C6-1510-4AF9-8F7D-C837DDBB2632}"
+ Private="true"
+ />
+ </References>
+ </Build>
+ <Files>
+ <Include>
+ <File RelPath="DafnyDriver.ssc"
+ SubType="Code"
+ BuildAction="Compile"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="..\version.ssc"
+ />
+ </Include>
+ </Files>
+ </XEN>
+</VisualStudioProject> \ No newline at end of file
diff --git a/Source/Graph/Graph.ssc b/Source/Graph/Graph.ssc
new file mode 100644
index 00000000..c9795f15
--- /dev/null
+++ b/Source/Graph/Graph.ssc
@@ -0,0 +1,746 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using Microsoft.SpecSharp.Collections;
+using Microsoft.Contracts;
+using System.Text; // for StringBuilder
+namespace Graphing{
+
+internal static class Util{
+ private static string! ListToString<T>(IEnumerable<T> xs){
+ StringBuilder sb = new StringBuilder();
+ sb.Append("[");
+ bool first = true;
+ foreach(T! x in xs){
+ if (!first) sb.Append(", ");
+ sb.Append(x.ToString());
+ first = false;
+ }
+ sb.Append("]");
+ return sb.ToString();
+ }
+ public static string! MapToString<Node>(Dictionary<Node,List<Node>> d){
+ StringBuilder sb = new StringBuilder();
+ sb.Append("{");
+ bool first = true;
+ foreach (KeyValuePair<Node,List<Node>> de in d){
+ if (!first) sb.Append(", ");
+ sb.Append(((!) de.Key).ToString());
+ sb.Append("~>");
+ sb.Append(ListToString(de.Value));
+ first = false;
+ }
+ sb.Append("}");
+ return sb.ToString();
+ }
+}
+
+// own struct to represent possibly undefined values, because Mono does
+// not like arrays with element type T! or T?
+public struct Maybe<T> {
+ private T Value;
+ public bool IsSet; // initialised with false by the default ctor
+ public T Val {
+ get { assume IsSet; return Value; }
+ set { Value = value; IsSet = true; }
+ }
+ public void UnSet() {
+ IsSet = false;
+ }
+}
+
+internal class DomRelation<Node>{
+ // doms maps (unique) node numbers to the node numbers of the immediate dominator
+ // to use it on Nodes, one needs the two way mapping between nodes and their numbers.
+ private int[]? doms; // 0 is unused: means undefined
+ // here are the two mappings
+ private Maybe<Node>[]? postOrderNumberToNode;
+ private Dictionary<Node,int>? nodeToPostOrderNumber;
+ private int sourceNum; // (number for) root of the graph
+ private Node source; // root of the graph
+ private Graph<Node> graph;
+ private Dictionary<Node,List<Node>>? immediateDominatorMap;
+
+ [NotDelayed]
+ internal DomRelation(Graph<Node> g, Node source){
+ this.graph = g;
+ // slot 0 not used: nodes are numbered from 1 to n so zero
+ // can represent undefined.
+ this.source = source;
+ base();
+ this.NewComputeDominators();
+ }
+ public Dictionary<Node,List<Node>> ImmediateDominatorMap{
+ get { assume this.immediateDominatorMap != null; return this.immediateDominatorMap; }
+ }
+ public bool DominatedBy(Node dominee, Node dominator){
+ assume this.nodeToPostOrderNumber != null;
+ assume this.doms != null;
+ int domineeNum = this.nodeToPostOrderNumber[dominee];
+ int dominatorNum = this.nodeToPostOrderNumber[dominator];
+ if (domineeNum == dominatorNum) return true;
+ int currentNodeNum = this.doms[domineeNum];
+ do {
+ if (currentNodeNum == dominatorNum) return true;
+ currentNodeNum = this.doms[currentNodeNum];
+ } while (currentNodeNum != this.sourceNum);
+ return false;
+ }
+ private Dictionary<Node,List<Node>>? domMap = null;
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public override string ToString(){
+ assume this.doms != null;
+ int[] localDoms = this.doms;
+ assume this.postOrderNumberToNode != null;
+ if (domMap == null){
+ domMap = new Dictionary<Node,List<Node>>();
+ for (int i = 1; i < localDoms.Length; i++){ // 0 slot is not used
+ int domineeNum = i;
+ int currentNodeNum = domineeNum;
+ List<Node> dominators = new List<Node>();
+ while (currentNodeNum != this.sourceNum){
+ dominators.Add(this.postOrderNumberToNode[currentNodeNum].Val);
+ currentNodeNum = this.doms[currentNodeNum];
+ }
+ dominators.Add(this.postOrderNumberToNode[this.sourceNum].Val);
+ domMap.Add(this.postOrderNumberToNode[i].Val,dominators);
+ }
+ }
+ StringBuilder sb = new StringBuilder();
+ sb.Append("{");
+ bool first = true;
+ foreach (KeyValuePair<Node,List<Node>> de in domMap){
+ if (!first) sb.Append(", ");
+ sb.Append(((!)de.Key).ToString());
+ sb.Append("~>");
+ sb.Append(ListToString(de.Value));
+ first = false;
+ }
+ sb.Append("}");
+ return sb.ToString();
+ }
+ private void PrintIntArray(int[] xs){
+ Console.Write("[");
+ for (int i = 0; i < xs.Length; i++){
+ if (0 < i) Console.Write(", ");
+ Console.Write(xs[i]);
+ }
+ Console.WriteLine("]");
+ }
+ public void PrintList<T>(IEnumerable<T> xs){
+ Console.Write("[");
+ int i = 0;
+ foreach(T! x in xs){
+ if (0 < i) Console.Write(", ");
+ Console.Write(x.ToString());
+ i++;
+ }
+ Console.WriteLine("]");
+ }
+ public string! ListToString<T>(IEnumerable<T> xs){
+ StringBuilder sb = new StringBuilder();
+ sb.Append("[");
+ bool first = true;
+ foreach(T! x in xs){
+ if (!first) sb.Append(", ");
+ sb.Append(x.ToString());
+ first = false;
+ }
+ sb.Append("]");
+ return sb.ToString();
+ }
+
+ // Keith D. Cooper, Timothy J. Harvey, Ken Kennedy, "A Simple, Fast Dominance Algorithm ", Software Practice and Experience, 2001.
+ // http://citeseer.ist.psu.edu/cooper01simple.html
+ private void NewComputeDominators(){
+ int n = this.graph.Nodes.Count;
+ this.postOrderNumberToNode = new Maybe<Node>[n+1];
+ this.nodeToPostOrderNumber = new Dictionary<Node,int>();
+ Dictionary<Node,bool> visited = new Dictionary<Node,bool>(n);
+ int currentNumber = 1;
+ assume this.source != null;
+ this.PostOrderVisit(this.source, visited, ref currentNumber);
+ this.sourceNum = this.nodeToPostOrderNumber[source];
+// for (int i = 1; i <= n; i++){ Console.WriteLine(postOrderNumberToNode[i]); }
+ this.doms = new int[n+1]; // 0 is unused: means undefined
+ Node start_node = this.source;
+ this.doms[this.nodeToPostOrderNumber[start_node]] = this.nodeToPostOrderNumber[start_node];
+ bool changed = true;
+// PrintIntArray(doms);
+ while (changed){
+ changed = false;
+ // for all nodes, b, in reverse postorder (except start_node)
+ for (int nodeNum = n-1; 1 <= nodeNum; nodeNum--){
+ Node b = this.postOrderNumberToNode[nodeNum].Val;
+ IEnumerable<Node> predecessors = this.graph.Predecessors(b);
+ // find a predecessor (i.e., a higher number) for which
+ // the doms array has been set
+ int new_idom = 0;
+ int first_processed_predecessor = 0;
+ #region new_idom <- number of first (processed) predecessor of b (pick one)
+ foreach (Node p in predecessors){
+ if (this.doms[this.nodeToPostOrderNumber[p]] != 0){
+ int x = this.nodeToPostOrderNumber[p];
+ new_idom = x;
+ first_processed_predecessor = x;
+ break;
+ }
+ }
+ #endregion
+ #region for all other predecessors, p, of b
+ foreach (Node p in predecessors){
+ if (this.nodeToPostOrderNumber[p] == first_processed_predecessor){
+ continue;
+ }
+ if (this.doms[this.nodeToPostOrderNumber[p]] != 0)
+ new_idom = intersect(this.nodeToPostOrderNumber[p],new_idom,this.doms);
+ }
+ #endregion
+ if (this.doms[this.nodeToPostOrderNumber[b]] != new_idom){
+ this.doms[this.nodeToPostOrderNumber[b]] = new_idom;
+ changed = true;
+ }
+ }
+ }
+ #region Populate the Immediate Dominator Map
+ int sourceNum = this.nodeToPostOrderNumber[this.source];
+ immediateDominatorMap = new Dictionary<Node,List<Node>>();
+ for (int i = 1; i <= n; i++){
+ Node node = this.postOrderNumberToNode[i].Val;
+ Node idomNode = this.postOrderNumberToNode[this.doms[i]].Val;
+ if ( i == sourceNum && this.doms[i] == sourceNum){
+ continue;
+ }
+ if (immediateDominatorMap.ContainsKey(idomNode)){
+ immediateDominatorMap[idomNode].Add(node);
+ }else{
+ List<Node> l = new List<Node>();
+ l.Add(node);
+ immediateDominatorMap.Add(idomNode,l);
+ }
+ }
+ #endregion
+ }
+ private int intersect(int b1, int b2, int[] doms){
+ int finger1 = b1;
+ int finger2 = b2;
+ while (finger1 != finger2){
+ while (finger1 < finger2){
+ finger1 = doms[finger1];
+ }
+ while (finger2 < finger1){
+ finger2 = doms[finger2];
+ }
+ }
+ return finger1;
+ }
+ private void PostOrderVisit(Node! n, Dictionary<Node,bool> visited, ref int currentNumber){
+ if (visited.ContainsKey(n)) return;
+ visited[n] = true;
+ foreach(Node! child in this.graph.Successors(n)){
+ PostOrderVisit(child, visited, ref currentNumber);
+ }
+ assume this.postOrderNumberToNode != null;
+ assume this.nodeToPostOrderNumber != null;
+ this.postOrderNumberToNode[currentNumber].Val = n;
+ this.nodeToPostOrderNumber[n] = currentNumber;
+ currentNumber++;
+ return;
+ }
+}
+public class Graph<Node> {
+ private Set<Pair<Node!,Node!>> es;
+ private Set<Node> ns;
+ private Node source;
+ private bool reducible;
+ private Set<Node> headers;
+ private Map<Node,Set<Node>> backEdgeNodes;
+ private Map<Pair<Node!,Node!>,Set<Node>> naturalLoops;
+ private DomRelation<Node>? dominatorMap = null;
+ private Dictionary<Node, Set<Node>> predCache = new Dictionary<Node, Set<Node>>();
+ private Dictionary<Node, Set<Node>> succCache = new Dictionary<Node, Set<Node>>();
+ private bool predComputed;
+
+ private class PreHeader {
+ Node! myHeader;
+ internal PreHeader(Node! h) { myHeader = h; }
+
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public override string! ToString() { return "#" + myHeader.ToString(); }
+ }
+
+ public Graph(Set<Pair<Node!,Node!>> edges)
+ {
+ es = edges;
+
+ // original A#
+ //ns = Set<Node>{ x : <x,y> in es } + Set<Node>{ y : <x,y> in es };
+
+ // closest Spec#
+ //ns = new Set<Node>{ Pair<Node,Node> p in edges; p.First } + new Set<Node>{ Pair<Node,Node> p in edges; p.Second };
+
+ //
+ Set<Node> temp = new Set<Node>();
+ foreach (Pair<Node!,Node!> p in edges){
+ temp.Add(p.First);
+ temp.Add(p.Second);
+ }
+ ns = temp;
+ }
+ public Graph()
+ { es = new Set<Pair<Node!,Node!>>(); ns = new Set<Node>(); }
+
+ // BUGBUG: Set<T>.ToString() should return a non-null string
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ public override string! ToString() { return "" + es.ToString(); }
+
+ public void AddSource(Node! x)
+ {
+ // BUGBUG: This generates bad code in the compiler
+ //ns += new Set<Node>{x};
+ ns.Add(x);
+ source = x;
+ }
+
+ public void AddEdge(Node! source, Node! dest)
+ {
+ //es += Set<Edge>{<source,dest>};
+ //ns += Set<Node>{source, dest};
+ es.Add(new Pair<Node!,Node!>(source,dest));
+ ns.Add(source);
+ ns.Add(dest);
+ predComputed = false;
+ }
+
+ public Set<Node> Nodes { get { return ns; } }
+ public IEnumerable<Pair<Node!,Node!>> Edges { get { return es; } }
+
+ public bool Edge(Node! x, Node! y) {
+ // original A#
+ // return <x,y> in es;
+ return es.Contains(new Pair<Node!,Node!>(x,y));
+ }
+
+ private void ComputePredSuccCaches()
+ {
+ if (predComputed) return;
+ predComputed = true;
+ predCache = new Dictionary<Node, Set<Node>>();
+ succCache = new Dictionary<Node, Set<Node>>();
+
+ foreach (Node n in Nodes) {
+ predCache[n] = new Set<Node>();
+ succCache[n] = new Set<Node>();
+ }
+
+ foreach(Pair<Node!,Node!> p in Edges){
+ Set<Node> tmp;
+
+ tmp = predCache[p.Second];
+ tmp.Add(p.First);
+ predCache[p.Second] = tmp;
+
+ tmp = succCache[p.First];
+ tmp.Add(p.Second);
+ succCache[p.First] = tmp;
+ }
+ }
+
+ internal IEnumerable<Node> Predecessors(Node n)
+ {
+ // original A#
+ //Set<Node> result = Set{ x : x in Nodes, Edge(x,n) };
+
+ ComputePredSuccCaches();
+ return predCache[n];
+ }
+
+ internal IEnumerable<Node> Successors(Node n)
+ {
+ ComputePredSuccCaches();
+ return succCache[n];
+ }
+
+ internal DomRelation<Node> /*Map<Node,Set<Node>>*/ DominatorMap
+ {
+ get {
+ assert source != null;
+ if (this.dominatorMap == null){
+ this.dominatorMap = new DomRelation<Node>(this, this.source);
+ }
+ return this.dominatorMap;
+ }
+ }
+
+ public Dictionary<Node,List<Node>> ImmediateDominatorMap
+ {
+ get {
+ assert source != null;
+ if (this.dominatorMap == null){
+ this.dominatorMap = new DomRelation<Node>(this, this.source);
+ }
+ return this.dominatorMap.ImmediateDominatorMap;
+ }
+ }
+ public List<Node> ImmediatelyDominatedBy(Node! n) {
+ List<Node>? dominees;
+ this.ImmediateDominatorMap.TryGetValue(n, out dominees);
+ return dominees == null ? new List<Node>() : dominees;
+ }
+
+ public IEnumerable<Node?> TopologicalSort()
+ {
+ bool acyclic;
+ List<Node?> sortedList;
+ this.TarjanTopSort(out acyclic, out sortedList);
+ return acyclic ? sortedList : new List<Node?>();
+ }
+ // From Tarjan 1972
+ public void TarjanTopSort(out bool acyclic, out List<Node?> sortedNodes)
+ {
+ int n = this.Nodes.Count;
+ if (n == 0) { acyclic = true; sortedNodes = new List<Node?>(); return; }
+ int[] incomingEdges = new int[n];
+ // need an arbitrary numbering for the nodes to use as indices into
+ // the arrays used within this algorithm
+ Dictionary<Node,int> nodeToNumber = new Dictionary<Node,int>(n);
+ Maybe<Node>[] numberToNode = new Maybe<Node>[n];
+ int counter = 0;
+ foreach (Node node in this.Nodes){
+ numberToNode[counter].Val = node;
+ nodeToNumber[node] = counter;
+ counter++;
+ }
+ foreach (Pair<Node!,Node!> e in this.Edges){
+ Node! target = e.Second;
+ incomingEdges[nodeToNumber[target]]++;
+ }
+ List<Node?> sorted = new List<Node?> ();
+ int sortedIndex = 0;
+ while (sortedIndex < n){
+ // find a root (i.e., its index)
+ int rootIndex = -1;
+ for (int i = 0; i < n; i++){
+ if (incomingEdges[i] == 0){
+ rootIndex = i;
+ break;
+ }
+ }
+ if (rootIndex == -1){
+ acyclic = false; sortedNodes = new List<Node?> (); return;
+ }
+ // mark root so it won't be used again
+ incomingEdges[rootIndex] = -1;
+ Node root = numberToNode[rootIndex].Val;
+ sorted.Add(root);
+ ++sortedIndex;
+ foreach (Node s in this.Successors(root)){
+ incomingEdges[nodeToNumber[s]]--;
+ }
+ }
+ acyclic = true; sortedNodes = sorted; return;
+ }
+ private IEnumerable<Node> OldTopologicalSort()
+ {
+ Pair<bool,Seq<Node>> result = this.TopSort();
+ return result.First ? result.Second : (IEnumerable<Node>)new Seq<Node>();
+ }
+ // From AsmL distribution example
+ private Pair<bool,Seq<Node>> TopSort()
+ {
+ Seq<Node> S = new Seq<Node>();
+ Set<Node> V = this.Nodes;
+ Set<Node> X = new Set<Node>();
+ foreach (Node! n in V){ X.Add(n); }
+ bool change = true;
+ while ( change )
+ // invariant: X = V - S
+ {
+ change = false;
+ if (X.Count > 0){
+ foreach (Node! n in X){
+ // see if n has any incoming edges from any other node in X
+ bool inDegreeZero = true;
+ foreach(Node! u in X){
+ if (this.Edge(u,n)){
+ inDegreeZero = false;
+ break; // no point looking further
+ }
+ }
+ if (inDegreeZero){
+ S.Add(n);
+ X.Remove(n);
+ change = true;
+ break; // might as well go back and start looking through X from the beginning
+ }
+ }
+ // Then we made it all the way through X without finding a source node
+ if (!change){
+ return new Pair<bool,Seq<Node>>(false,new Seq<Node>());
+ }
+ }
+ }
+ return new Pair<bool,Seq<Node>>(true,S);
+ }
+
+ public static bool Acyclic(Graph<Node> g, Node source)
+ {
+ bool acyclic;
+ List<Node?> sortedList;
+ g.TarjanTopSort(out acyclic, out sortedList);
+ return acyclic;
+ }
+
+ //
+ // [Dragon, pp. 670--671]
+ // returns map D s.t. d in D(n) iff d dom n
+ //
+ static private Map<Node,Set<Node>> OldComputeDominators(Graph<Node> g, Node! source){
+ assert g.source != null;
+ Set<Node> N = g.Nodes;
+ Set<Node> nonSourceNodes = N - new Set<Node>(source);
+ Map<Node,Set<Node>> D = new Map<Node,Set<Node>>();
+ D[source] = new Set<Node>(source);
+ foreach (Node! n in nonSourceNodes){
+ D[n] = N;
+ }
+ bool change = true;
+ while ( change ){
+ change = false;
+ foreach (Node! n in nonSourceNodes){
+
+ // original A#
+ //Set<Set<Node>> allPreds = new Set<Set<Node>>{ Node p in this.Predecessors(n) ; D[p] };
+
+ Set<Set<Node>> allPreds = new Set<Set<Node>>();
+ foreach(Node! p in g.Predecessors(n)) allPreds.Add(D[p]);
+ Set<Node> temp = new Set<Node>(n) + Set<Node>.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.]
+ static Set<Node> NaturalLoop(Graph<Node> g, Pair<Node!,Node!> backEdge)
+ {
+ Node! n = backEdge.First;
+ Node! d = backEdge.Second;
+ Seq<Node> stack = new Seq<Node>();
+ Set<Node> loop = new Set<Node>(d);
+ if ( !n.Equals(d) ) // then n is not in loop
+ {
+ loop.Add(n);
+ stack = new Seq<Node>(n) + stack; // push n onto stack
+ }
+ while ( stack.Count > 0) // not empty
+ {
+ Node m = stack.Head;
+ stack = stack.Tail; // pop stack
+ foreach (Node! p in g.Predecessors(m))
+ {
+ if ( !(loop.Contains(p)) )
+ {
+ loop.Add(p);
+ stack = new Seq<Node>(p) + stack; // push p onto stack
+ }
+ }
+ }
+ return loop;
+ }
+
+ internal struct ReducibleResult{
+ internal bool reducible;
+ internal Set<Node> headers;
+ internal Map<Node,Set<Node>> backEdgeNodes;
+ internal Map<Pair<Node!,Node!>,Set<Node>> naturalLoops;
+ internal ReducibleResult(bool b,
+ Set<Node> headers,
+ Map<Node,Set<Node>> backEdgeNodes,
+ Map<Pair<Node!,Node!>,Set<Node>> naturalLoops){
+ this.reducible = b;
+ this.headers = headers;
+ this.backEdgeNodes = backEdgeNodes;
+ this.naturalLoops = naturalLoops;
+ }
+
+ }
+
+ // [Dragon, p. 606]
+ static ReducibleResult ComputeReducible(Graph<Node> g, Node source) {
+ // first, compute the dom relation
+ DomRelation<Node> /*Map<Node,Set<Node>>*/ D = g.DominatorMap;
+ return ComputeReducible(g,source,D);
+ }
+
+ // [Dragon, p. 606]
+ static ReducibleResult ComputeReducible(Graph<Node> g,
+ Node source,
+ DomRelation<Node>! DomRelation) {
+
+ //Console.WriteLine("[" + DateTime.Now +"]: begin ComputeReducible");
+ IEnumerable<Pair<Node!,Node!>> edges = g.Edges;
+ Set<Pair<Node!,Node!>> backEdges = new Set<Pair<Node!,Node!>>();
+ Set<Pair<Node!,Node!>> nonBackEdges = new Set<Pair<Node!,Node!>>();
+ foreach (Pair<Node!,Node!> e in edges){
+ Node x = e.First;
+ Node y = e.Second; // so there is an edge from x to y
+ if ( DomRelation.DominatedBy(x,y) ){ // y dom x: which means y dominates x
+ backEdges.Add(e);
+ }else{
+ nonBackEdges.Add(e);
+ }
+ }
+ if ( !Acyclic(new Graph<Node>(nonBackEdges), source) ){
+ return new ReducibleResult(false,
+ new Set<Node>(),
+ new Map<Node,Set<Node>>(),
+ new Map<Pair<Node!,Node!>,Set<Node>>());
+ }else{
+ // original A#:
+ //Set<Node> headers = Set{ d : <n,d> in backEdges };
+ Set<Node> headers = new Set<Node>();
+ foreach(Pair<Node!,Node!> e in backEdges)
+ headers.Add(e.Second);
+ // original A#:
+ //Map<Node,Set<Node>> backEdgeNodes = Map{ h -> bs : h in headers, bs = Set<Node>{ b : <b,x> in backEdges, x == h } };
+ Map<Node,Set<Node>> backEdgeNodes = new Map<Node,Set<Node>>();
+ foreach(Node! h in headers){
+ Set<Node> bs = new Set<Node>();
+ foreach(Pair<Node!,Node!> backedge in backEdges){
+ if (backedge.Second.Equals(h)){
+ bs.Add(backedge.First);
+ }
+ }
+ backEdgeNodes.Add(h,bs);
+ }
+
+ // original A#:
+ //Map<Pair<Node,Node>,Set<Node>> naturalLoops = Map{ e -> NaturalLoop(g,e) : e in backEdges };
+ Map<Pair<Node!,Node!>,Set<Node>> naturalLoops = new Map<Pair<Node!,Node!>,Set<Node>>();
+ foreach (Pair<Node!,Node!> e in backEdges){
+ naturalLoops.Add(e,NaturalLoop(g,e));
+ }
+
+ //Console.WriteLine("[" + DateTime.Now +"]: end ComputeReducible");
+ return new ReducibleResult(true, headers, backEdgeNodes, naturalLoops);
+ }
+ }
+
+ public bool Reducible { get { return reducible; } }
+ public IEnumerable<Node> Headers { get { return headers; } }
+ public IEnumerable<Node> BackEdgeNodes(Node! h){
+ // original A#:
+ //return h in backEdgeNodes ? backEdgeNodes[h] : null;
+ return (backEdgeNodes.ContainsKey(h) ? backEdgeNodes[h] : (IEnumerable<Node>)new Seq<Node>());
+ }
+ public IEnumerable<Node> NaturalLoops(Node! header, Node! backEdgeNode)
+ {
+ Pair<Node!,Node!> e = new Pair<Node!,Node!>(backEdgeNode,header);
+ return naturalLoops.ContainsKey(e) ? naturalLoops[e] : (IEnumerable<Node>)new Seq<Node>();
+ }
+
+ public void ComputeLoops()
+ {
+ ReducibleResult r = ComputeReducible(this,this.source);
+ this.reducible = r.reducible;
+ this.headers = r.headers;
+ this.backEdgeNodes = r.backEdgeNodes;
+ this.naturalLoops = r.naturalLoops;
+ return;
+ }
+
+
+} // end: class Graph
+
+public class GraphProgram
+{
+ static void TestGraph<T>(T! source, params Pair<T!,T!>[] edges){
+ Set<Pair<T!,T!>> es = new Set<Pair<T!,T!>>();
+ foreach (Pair<T!,T!> e in edges) es.Add(e);
+ Graph<T> g = new Graph<T>(es);
+ g.AddSource(source);
+ Console.WriteLine("G = " + g);
+ g.ComputeLoops();
+ Console.WriteLine("G's Dominator Map = " + g.DominatorMap);
+ Console.WriteLine("G's Immediate Dominator Map = " + Util.MapToString(g.ImmediateDominatorMap));
+ Console.WriteLine("G is reducible: " + (g.Reducible ? "yes" : "no"));
+ }
+
+ static void Main(string[] args)
+ //requires forall{string s in args; s != null};
+ {
+ Console.WriteLine("Spec# says hello!");
+ // This generates bad IL -- need to fix a bug in the compiler
+ //Graph<int> g = new Graph<int>(new Set<Pair<int,int>>{ new Pair<int,int>(1,2), new Pair<int,int>(1,3), new Pair<int,int>(2,3) });
+
+ Console.WriteLine("");
+ TestGraph<char>('a',
+ new Pair<char,char>('a','b'),
+ new Pair<char,char>('a','c'),
+ new Pair<char,char>('b','c')
+ );
+
+ Console.WriteLine("");
+ TestGraph<char>('a',
+ new Pair<char,char>('a','b'),
+ new Pair<char,char>('a','c'),
+ new Pair<char,char>('b','d'),
+ new Pair<char,char>('c','e'),
+ new Pair<char,char>('c','f'),
+ new Pair<char,char>('d','e'),
+ new Pair<char,char>('e','d'),
+ new Pair<char,char>('e','f'),
+ new Pair<char,char>('f','e')
+ );
+
+ Console.WriteLine("");
+ TestGraph<char>('a',
+ new Pair<char,char>('a','b'),
+ new Pair<char,char>('a','c'),
+ new Pair<char,char>('b','c'),
+ new Pair<char,char>('c','b')
+ );
+
+ Console.WriteLine("");
+ TestGraph<int>(1,
+ new Pair<int,int>(1,2),
+ new Pair<int,int>(1,3),
+ new Pair<int,int>(2,3)
+ );
+
+ Console.WriteLine("");
+ TestGraph<int>(1,
+ new Pair<int,int>(1,2),
+ new Pair<int,int>(1,3),
+ new Pair<int,int>(2,3),
+ new Pair<int,int>(3,2)
+ );
+
+ Console.WriteLine("");
+ TestGraph<int>(2,
+ new Pair<int,int>(2,3),
+ new Pair<int,int>(2,4),
+ new Pair<int,int>(3,2)
+ );
+
+ Console.WriteLine("");
+ TestGraph<char>('a',
+ new Pair<char,char>('a','b'),
+ new Pair<char,char>('a','c'),
+ new Pair<char,char>('b','c'),
+ new Pair<char,char>('b','b')
+ );
+
+
+ }
+}
+
+}
diff --git a/Source/Graph/Graph.sscproj b/Source/Graph/Graph.sscproj
new file mode 100644
index 00000000..137c9b0e
--- /dev/null
+++ b/Source/Graph/Graph.sscproj
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="utf-8"?>
+<VisualStudioProject>
+ <XEN ProjectType="Local"
+ SchemaVersion="1.0"
+ Name="Graph"
+ ProjectGuid="4c28fb90-630e-4b55-a937-11a011b79765"
+ >
+ <Build>
+ <Settings ApplicationIcon=""
+ AssemblyName="Graph"
+ OutputType="Library"
+ RootNamespace="Graph"
+ StartupObject=""
+ StandardLibraryLocation=""
+ TargetPlatform="v2"
+ TargetPlatformLocation=""
+ ShadowedAssembly=""
+ >
+ <Config Name="Debug"
+ AllowUnsafeBlocks="False"
+ BaseAddress="285212672"
+ CheckForOverflowUnderflow="False"
+ ConfigurationOverrideFile=""
+ DefineConstants="DEBUG;TRACE"
+ DocumentationFile=""
+ DebugSymbols="True"
+ FileAlignment="4096"
+ IncrementalBuild="True"
+ Optimize="False"
+ OutputPath="bin\debug"
+ RegisterForComInterop="False"
+ RemoveIntegerChecks="false"
+ TreatWarningsAsErrors="False"
+ WarningLevel="4"
+ ReferenceTypesAreNonNullByDefault="True"
+ RunProgramVerifier="False"
+ RunProgramVerifierWhileEditing="False"
+ ProgramVerifierCommandLineOptions=""
+ AllowPointersToManagedStructures="False"
+ CheckContractAdmissibility="True"
+ CheckPurity="False"
+ />
+ <Config Name="Release"
+ AllowUnsafeBlocks="false"
+ BaseAddress="285212672"
+ CheckForOverflowUnderflow="false"
+ ConfigurationOverrideFile=""
+ DefineConstants="TRACE"
+ DocumentationFile=""
+ DebugSymbols="false"
+ FileAlignment="4096"
+ IncrementalBuild="false"
+ Optimize="true"
+ OutputPath="bin\release"
+ RegisterForComInterop="false"
+ RemoveIntegerChecks="false"
+ TreatWarningsAsErrors="false"
+ WarningLevel="4"
+ />
+ </Settings>
+ <References>
+ <Reference Name="System"
+ AssemblyName="System"
+ Private="false"
+ />
+ <Reference Name="System.Data"
+ AssemblyName="System.Data"
+ Private="false"
+ />
+ <Reference Name="System.Xml"
+ AssemblyName="System.Xml"
+ Private="false"
+ />
+ </References>
+ </Build>
+ <Files>
+ <Include>
+ <File RelPath="Graph.ssc"
+ SubType="Code"
+ BuildAction="Compile"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="..\version.ssc"
+ />
+ </Include>
+ </Files>
+ </XEN>
+</VisualStudioProject> \ No newline at end of file
diff --git a/Source/Houdini/Checker.ssc b/Source/Houdini/Checker.ssc
new file mode 100644
index 00000000..03c2beda
--- /dev/null
+++ b/Source/Houdini/Checker.ssc
@@ -0,0 +1,460 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using Microsoft.Contracts;
+using System.Collections.Generic;
+using Microsoft.Boogie;
+using Microsoft.Boogie.Simplify.AST;
+using VC;
+using Microsoft.Boogie.Z3;
+using Microsoft.AbstractInterpretationFramework;
+using Microsoft.Boogie.AbstractInterpretation;
+using System.Collections;
+using System.Compiler;
+using System.IO;
+using System.Threading;
+
+namespace Microsoft.Boogie.Houdini {
+
+ public abstract class HdnCheckerFactory {
+ public static HdnChecker! BuildHdnChecker(Checker! checker) {
+ if (checker.TheoremProver is Z3ThreadTheoremProver)
+ return new HoudiniZ3ApiChecker(checker);
+ else if (checker.TheoremProver is Z3ProcessTheoremProver) {
+ if (CommandLineOptions.Clo.houdiniFlags.incremental) {
+ return new HoudiniZ3CheckerIncr(checker);
+ } else {
+ return new HoudiniZ3Checker(checker);
+ }
+ } else
+ throw new Exception("HdnChecker only works with z3");
+ }
+ }
+
+ public abstract class HdnChecker {
+ public abstract void PrepareCheck(string! _descriptiveName,
+ VCExpr! _vc, ProverInterface.ErrorHandler! _handler);
+ public abstract void PushAxiom(VCExpr! vc);
+ public abstract void Pop();
+ public abstract void Check();
+ public abstract ProverInterface.Outcome ReadOutcome()
+ throws UnexpectedProverOutputException;
+ }
+
+ public class HoudiniZ3ApiChecker:HdnChecker {
+
+ private Z3ThreadTheoremProver! z3prover;
+
+ private ProverInterface.ErrorHandler handler;
+ private ProverInterface.Outcome outcome;
+ private UnexpectedProverOutputException outputExn;
+
+ internal HoudiniZ3ApiChecker(Checker! checker)
+ requires checker.TheoremProver!=null;
+ requires checker.TheoremProver is Z3ThreadTheoremProver;
+ {
+ this.z3prover = (Z3ThreadTheoremProver)checker.TheoremProver;
+ }
+
+ public override void PrepareCheck(string! _descriptiveName, VCExpr! _vc, ProverInterface.ErrorHandler! _handler) {
+ handler = _handler;
+ z3prover.LogComment(_descriptiveName);
+ z3prover.PrepareCheck(_descriptiveName,_vc);
+ }
+
+ public override void PushAxiom(VCExpr! vc) {
+ z3prover.PushAxiom(vc);
+ }
+
+ public override void Pop() {
+ z3prover.Pop();
+ }
+
+ public override void Check() {
+ z3prover.BeginPreparedCheck();
+ try {
+ outcome = z3prover.CheckOutcome((!)handler);
+ } catch (UnexpectedProverOutputException e) {
+ outputExn = e;
+ }
+ }
+
+ public override ProverInterface.Outcome ReadOutcome()
+ throws UnexpectedProverOutputException;
+ {
+ if (outputExn != null) {
+ throw outputExn;
+ }
+ return outcome;
+ }
+ }
+
+ // version of Z3PTP exposing more of the prover process functionality
+ public class Z3ProcessTPExposed : Z3ProcessTheoremProver {
+
+ [NotDelayed]
+ public Z3ProcessTPExposed (TextWriter/*?*/ logFileWriter, VCExpressionGenerator! gen, DeclFreeProverContext! ctx, int timeout, bool typed)
+ throws UnexpectedProverOutputException;
+ {
+ base(logFileWriter, gen, ctx, timeout, typed, "z3.exe");
+ }
+
+
+ //MOVED Directly Z3 ProverInterface for now...
+ //To introduce this cleanly, need to add a new subproject under Provers
+ //and must use the commandline option for that, instead of saying /prover:z3
+ //That way, the class is constructed in class Checker the same way
+ //all the other provers are constructed
+
+ }
+
+ public class Z3ExposedFactory : Z3.Factory {
+
+ }
+
+
+ //Old version
+ public class HoudiniZ3Checker:HdnChecker {
+ private Stack<VCExpr!>! axioms = new Stack<VCExpr!>();
+ private Checker! checker;
+ private string descriptiveName;
+ private VCExpr conjecture;
+ private ProverInterface.ErrorHandler handler;
+
+ internal HoudiniZ3Checker(Checker! checker)
+ requires checker.TheoremProver!=null;
+ requires checker.TheoremProver is Z3ProcessTheoremProver;
+ {
+ this.checker = checker;
+ }
+
+ public override void PrepareCheck(string! _descriptiveName, VCExpr! _vc, ProverInterface.ErrorHandler! _handler)
+ {
+ this.descriptiveName = _descriptiveName;
+ this.conjecture = _vc;
+ this.handler = _handler;
+ }
+ public override void PushAxiom(VCExpr! vc) {
+ axioms.Push(vc);
+ }
+ public override void Pop() {
+ axioms.Pop();
+ }
+ private VCExpr! BuildVCAxioms(Stack<VCExpr!>! axioms)
+ requires axioms.Count>0;
+ {
+ VCExpr vc_axioms = null;
+ foreach (VCExpr! axiom in axioms) {
+ if (vc_axioms==null)
+ vc_axioms=axiom;
+ else
+ vc_axioms = checker.VCExprGen.And(vc_axioms,axiom);
+ }
+ return (!)vc_axioms;
+ }
+
+ public override void Check()
+ {
+ assert (descriptiveName!=null);
+ assert (conjecture!=null);
+ assert (handler!=null);
+ outcome = ProverInterface.Outcome.Undetermined;
+
+ VCExpr! vc;
+ if (axioms.Count>0) {
+ VCExpr vc_axioms = BuildVCAxioms(axioms);
+ vc = checker.VCExprGen.Implies(vc_axioms,conjecture);
+ } else
+ vc = conjecture;
+
+ checker.BeginCheck(descriptiveName,vc,handler);
+ WaitHandle.WaitAny(new WaitHandle[] {checker.ProverDone});
+ }
+
+ private ProverInterface.Outcome outcome;
+ private UnexpectedProverOutputException outputExn;
+
+ public override ProverInterface.Outcome ReadOutcome()
+ throws UnexpectedProverOutputException;
+ {
+ try {
+ outcome = checker.ReadOutcome();
+ } catch (UnexpectedProverOutputException e) {
+ throw e;
+ }
+ return outcome;
+ }
+ }
+
+ //new version
+ public class HoudiniZ3CheckerIncr:HdnChecker {
+ private Checker! checker;
+ private string descriptiveName;
+ private VCExpr conjecture;
+ private ProverInterface.ErrorHandler handler;
+
+ // Hack to delay the exception throwing (from Push/Pop...) for now
+ private UnexpectedProverOutputException delayedExc;
+ private Z3ProcessTheoremProver! thmProver;
+
+ internal HoudiniZ3CheckerIncr(Checker! checker)
+ requires checker.TheoremProver!=null;
+ requires checker.TheoremProver is Z3ProcessTheoremProver;
+ {
+ this.checker = checker;
+ this.thmProver = (Z3ProcessTheoremProver!)checker.TheoremProver;
+ }
+
+ private void delayException(UnexpectedProverOutputException exc) {
+ if (delayedExc == null) { delayedExc = exc; }
+ }
+
+ public override void PrepareCheck(string! _descriptiveName, VCExpr! _vc,
+ ProverInterface.ErrorHandler! _handler)
+ {
+ this.descriptiveName = _descriptiveName;
+ this.conjecture = _vc;
+ this.handler = _handler;
+ this.thmProver.LogComment(_descriptiveName);
+ try {
+ this.thmProver.PrepareCheck(_descriptiveName,_vc);
+ } catch (UnexpectedProverOutputException exc) {
+ delayException(exc);
+ }
+ }
+ public override void PushAxiom(VCExpr! vc) {
+ string! vcStr = vc.ToString();
+ try {
+ thmProver.PushAxioms(vcStr);
+ } catch (UnexpectedProverOutputException exc) {
+ delayException(exc);
+ }
+ }
+ public override void Pop() {
+ try {
+ thmProver.PopAxioms();
+ } catch (UnexpectedProverOutputException exc) {
+ delayException(exc);
+ }
+ }
+
+ public override void Check()
+ {
+ assert (descriptiveName!=null);
+ assert (conjecture!=null);
+ assert (handler!=null);
+ outcome = ProverInterface.Outcome.Undetermined;
+
+ VCExpr! vc = checker.VCExprGen.False;
+ checker.BeginCheck(descriptiveName,vc,handler);
+ WaitHandle.WaitAny(new WaitHandle[] {checker.ProverDone});
+ }
+
+ private ProverInterface.Outcome outcome;
+ private UnexpectedProverOutputException outputExn;
+
+ public override ProverInterface.Outcome ReadOutcome()
+ throws UnexpectedProverOutputException;
+ {
+ // HACK
+ if (delayedExc != null) {
+ UnexpectedProverOutputException temp = delayedExc;
+ delayedExc = null;
+ throw temp;
+ }
+ // Normal stuff
+ try {
+ outcome = checker.ReadOutcome();
+ } catch (UnexpectedProverOutputException e) {
+ throw e;
+ }
+ return outcome;
+ }
+ }
+
+ public abstract class HdnVCGenFactory {
+ public abstract HdnVCGen! BuildVCGen(Program! program, string logFilePath, bool appendLogFile);
+ }
+ public class Z3APIHdnVCGenFactory:HdnVCGenFactory {
+ public override HdnVCGen! BuildVCGen(Program! program, string logFilePath, bool appendLogFile) {
+ return new Z3ApiHdnVCGen(program, logFilePath, appendLogFile);
+ }
+ }
+ public class Z3HdnVCGenFactory:HdnVCGenFactory {
+ public override HdnVCGen! BuildVCGen(Program! program, string logFilePath, bool appendLogFile) {
+ return new Z3HdnVCGen(program, logFilePath, appendLogFile);
+ }
+ }
+
+ public abstract class HdnVCGen {
+ public abstract void PrepareVerification(Implementation! impl, Program! program);
+ public abstract void PushAxiom(Axiom! axiom);
+ public abstract void Pop();
+ public abstract VC.VCGen.Outcome Verify(out List<Counterexample!>? errors)
+ throws UnexpectedProverOutputException;
+
+ }
+
+ public class Z3HdnVCGen: HdnVCGen {
+ private Program! program;
+ private string logFilePath;
+ private bool appendLogFile;
+ private Implementation implementation;
+ private Axiom axiom;
+
+ private ExtVCGen vcgen;
+
+ public Z3HdnVCGen(Program! program, string logFilePath, bool appendLogFile) {
+ this.program = program;
+ this.logFilePath = logFilePath;
+ this.appendLogFile= appendLogFile;
+ }
+
+ public override void PrepareVerification(Implementation! impl, Program! program) {
+ this.implementation = impl;
+ this.program = program;
+ this.vcgen = new ExtVCGen(program,logFilePath,appendLogFile);
+ this.vcgen.PrepareVerification(this.implementation,this.program);
+ //watch out for > 1 call -- don't want CFG conversion to happen too many times!!!
+ }
+
+ public override void PushAxiom(Axiom! axiom) {
+ assert this.vcgen != null;
+ this.axiom=axiom;
+ this.vcgen.PushAxiom(this.axiom);
+ }
+
+ public override void Pop() {
+ assert this.vcgen != null;
+ this.vcgen.Pop();
+ this.axiom = null;
+ }
+
+ public override VC.VCGen.Outcome Verify(out List<Counterexample!>? errors)
+ throws UnexpectedProverOutputException; {
+ assert(this.vcgen!=null);
+
+ return vcgen.Verify(out errors);
+ }
+
+ }
+
+ public class Z3ApiHdnVCGen: HdnVCGen {
+ private ExtVCGen! vcgen;
+
+ public Z3ApiHdnVCGen(Program! program, string logFilePath, bool appendLogFile) {
+ this.vcgen = new ExtVCGen(program,logFilePath,appendLogFile);
+ }
+
+ public override void PrepareVerification(Implementation! impl, Program! program) {
+ vcgen.PrepareVerification(impl,program);
+ }
+
+ public override void PushAxiom(Axiom! axiom) {
+ vcgen.PushAxiom(axiom);
+ }
+
+ public override void Pop() {
+ vcgen.Pop();
+ }
+
+ public override VC.VCGen.Outcome Verify(out List<Counterexample!>? errors)
+ throws UnexpectedProverOutputException; {
+ return vcgen.Verify(out errors);
+ }
+
+ }
+
+
+
+ public class ExtVCGen : VCGen {
+
+ private HdnChecker hdnChecker;
+ private VCExpressionGenerator gen;
+ CounterexampleCollector! collector = new CounterexampleCollector();
+
+ public ExtVCGen(Program! program, string logFilePath, bool appendLogFile) {
+ base(program,logFilePath,appendLogFile);
+ }
+
+ public void PrepareVerification(Implementation! impl, Program! program) {
+ collector.OnProgress("HdnVCGen", 0, 0, 0.0);
+ if (CommandLineOptions.Clo.SoundnessSmokeTest) {
+ throw new Exception("HoudiniVCGen does not support Soundness smoke test.");
+ }
+
+ ConvertCFG2DAG(impl, program);
+ Hashtable/*TransferCmd->ReturnCmd*/ gotoCmdOrigins = PassifyImpl(impl, program);
+ Hashtable/*<int, Absy!>*/! label2absy;
+ Checker! checker = new Checker(program, this.logFilePath, this.appendLogFile, impl, CommandLineOptions.Clo.ProverKillTime);
+ VCExpr! vc = GenerateVC(impl, out label2absy, checker);
+
+ ErrorReporter reporter;
+ if (CommandLineOptions.Clo.vcVariety == CommandLineOptions.VCVariety.Local) {
+ reporter = new ErrorReporterLocal(gotoCmdOrigins, label2absy, impl.Blocks, incarnationOriginMap, collector);
+ } else {
+ reporter = new ErrorReporter(gotoCmdOrigins, label2absy, impl.Blocks, incarnationOriginMap, collector);
+ }
+ this.gen = checker.VCExprGen;
+ this.hdnChecker = HdnCheckerFactory.BuildHdnChecker(checker);
+ this.hdnChecker.PrepareCheck((!) impl.Name, vc, reporter);
+ }
+
+
+ public void PushAxiom(Axiom! axiom)
+ {
+ assume (hdnChecker!=null);
+ assume (gen!=null);
+ VCExpr vc = axiom.Expr.VCView(gen);
+ this.hdnChecker.PushAxiom(vc);
+ }
+ public void Pop() {
+ assume (hdnChecker!=null);
+ hdnChecker.Pop();
+ }
+
+ public Outcome Verify(out List<Counterexample!>? errors)
+ throws UnexpectedProverOutputException; {
+ assume (hdnChecker!=null);
+ collector.examples.Clear();
+ hdnChecker.Check();
+ ProverInterface.Outcome proverOutcome;
+ proverOutcome = hdnChecker.ReadOutcome();
+ Outcome verifyOutcome = ReadOutcome(proverOutcome);
+ if (verifyOutcome == Outcome.Errors) {
+ assume (collector.examples!=null);
+ if (collector.examples.Count == 0) {
+ string memStr = System.Convert.ToString(System.GC.GetTotalMemory(false));
+ if (memStr != null)
+ memStr = "?";
+ throw new UnexpectedProverOutputException("Outcome.Errors w/ 0 counter examples. " + memStr + " memory used");
+ }
+ errors = collector.examples;
+ } else {
+ errors = null;
+ }
+ return verifyOutcome;
+ }
+
+ private Outcome ReadOutcome(ProverInterface.Outcome proverOutcome) {
+ switch (proverOutcome) {
+ case ProverInterface.Outcome.Valid:
+ return Outcome.Correct;
+ case ProverInterface.Outcome.Invalid:
+ return Outcome.Errors;
+ case ProverInterface.Outcome.TimeOut:
+ return Outcome.TimedOut;
+ case ProverInterface.Outcome.Undetermined:
+ return Outcome.Inconclusive;
+ default:
+ throw new Exception("Unknown Prover Interface outcome while reading outcome.");
+ }
+ }
+
+ }
+
+
+} \ No newline at end of file
diff --git a/Source/Houdini/Houdini.ssc b/Source/Houdini/Houdini.ssc
new file mode 100644
index 00000000..bd8348be
--- /dev/null
+++ b/Source/Houdini/Houdini.ssc
@@ -0,0 +1,1188 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using Microsoft.Contracts;
+using System.Collections.Generic;
+using Microsoft.Boogie;
+using Microsoft.Boogie.Simplify.AST;
+using VC;
+using Microsoft.Boogie.Z3;
+using Microsoft.AbstractInterpretationFramework;
+using Microsoft.Boogie.AbstractInterpretation;
+using System.Collections;
+using System.Compiler;
+using System.IO;
+
+namespace Microsoft.Boogie.Houdini
+{
+
+ class ReadOnlyDictionary<K,V> {
+ private Dictionary<K,V>! dictionary;
+ public ReadOnlyDictionary(Dictionary<K,V>! dictionary) {
+ this.dictionary = dictionary;
+ }
+
+ public Dictionary<K,V>.KeyCollection Keys {
+ get { return this.dictionary.Keys; }
+ }
+
+ public bool TryGetValue(K k, out V? v) {
+ return this.dictionary.TryGetValue(k, out v);
+ }
+
+ public bool ContainsKey(K k) {
+ return this.dictionary.ContainsKey(k);
+ }
+ }
+
+ public class CallGraph {
+
+ private IGraphNavigator! aiCallGraph;
+
+ public CallGraph(IGraphNavigator! aiCallGraph) {
+ this.aiCallGraph = aiCallGraph;
+ }
+
+ public IEnumerable! PreviousNodes(Implementation! n) {
+ return (!)this.aiCallGraph.PreviousNodes(n);
+ }
+ public IEnumerable! NextNodes(Implementation! n) {
+ return (!)this.aiCallGraph.NextNodes(n);
+ }
+
+ }
+
+ public abstract class HoudiniObserver {
+ public virtual void UpdateStart(Program! program, int numConstants) {}
+ public virtual void UpdateIteration() {}
+ public virtual void UpdateImplementation(Implementation! implementation) {}
+ public virtual void UpdateAssignment(Dictionary<string!,bool>! assignment) {}
+ public virtual void UpdateOutcome(VCGen.Outcome outcome) {}
+ public virtual void UpdateEnqueue(Implementation! implementation) {}
+ public virtual void UpdateDequeue() {}
+ public virtual void UpdateConstant(string! constantName) {}
+ public virtual void UpdateEnd(bool isNormalEnd) {}
+ public virtual void UpdateFlushStart() {}
+ public virtual void UpdateFlushFinish() {}
+ public virtual void SeeException(string! msg){}
+ }
+
+ public class IterationTimer <K> {
+ private Dictionary<K, List<double>!>! times;
+
+ public IterationTimer () {
+ times = new Dictionary<K, List<double>!>();
+ }
+
+ public void AddTime(K key, double timeMS) {
+ List<double> oldList;
+ times.TryGetValue(key, out oldList);
+ if (oldList == null) {
+ oldList = new List<double>();
+ } else {
+ times.Remove(key);
+ }
+ oldList.Add(timeMS);
+ times.Add(key, oldList);
+ }
+
+ public void PrintTimes(TextWriter! wr) {
+ wr.WriteLine ("Total procedures: {0}", times.Count);
+ double total = 0;
+ int totalIters = 0;
+ foreach(KeyValuePair<K, List<double>!> kv in times) {
+ int curIter = 0;
+ wr.WriteLine("Times for {0}:", kv.Key);
+ foreach(double v in kv.Value) {
+ wr.WriteLine(" ({0})\t{1}ms", curIter, v);
+ total += v;
+ curIter++;
+ }
+ totalIters += curIter;
+ }
+ total = total / 1000.0;
+ wr.WriteLine ("Total time: {0} (s)", total);
+ wr.WriteLine ("Avg: {0} (s/iter)", total/totalIters);
+ }
+ }
+
+ public class HoudiniTimer : HoudiniObserver {
+ private DateTime startT;
+ private Implementation curImp;
+ private IterationTimer<string!>! times;
+ private TextWriter! wr;
+
+ public HoudiniTimer(TextWriter! wr) {
+ this.wr = wr;
+ times = new IterationTimer<string!>();
+ }
+ public override void UpdateIteration() {
+ startT = DateTime.Now;
+ }
+ public override void UpdateImplementation(Implementation! implementation){
+ curImp = implementation;
+ }
+ public override void UpdateOutcome (VCGen.Outcome o) {
+ assert curImp != null;
+ DateTime endT = DateTime.Now;
+ times.AddTime(curImp.Name, (endT - startT).TotalMilliseconds); // assuming names are unique
+ }
+ public void PrintTimes() {
+ wr.WriteLine("-----------------------------------------");
+ wr.WriteLine("Times for each iteration for each procedure");
+ wr.WriteLine("-----------------------------------------");
+ times.PrintTimes(wr);
+ }
+ }
+
+ public class HoudiniTextReporter: HoudiniObserver {
+ private TextWriter! wr;
+ private int currentIteration = -1;
+
+ public HoudiniTextReporter(TextWriter! wr) {
+ this.wr = wr;
+ }
+ public override void UpdateStart(Program! program, int numConstants) {
+ wr.WriteLine("Houdini started:" + program.ToString() + " #constants: " + numConstants.ToString());
+ currentIteration = -1;
+ wr.Flush();
+ }
+ public override void UpdateIteration() {
+ currentIteration++;
+ wr.WriteLine("---------------------------------------");
+ wr.WriteLine("Houdini iteration #" + currentIteration);
+ wr.Flush();
+ }
+ public override void UpdateImplementation(Implementation! implementation){
+ wr.WriteLine("implementation under analysis :" + implementation.Name);
+ wr.Flush();
+ }
+ public override void UpdateAssignment(Dictionary<string!,bool>! assignment) {
+ bool firstTime = true;
+ wr.Write("assignment under analysis : axiom (");
+ foreach (KeyValuePair<string!,bool> kv in assignment) {
+ if (!firstTime) wr.Write(" && "); else firstTime = false;
+ string! valString; // ugliness to get it lower cased
+ if (kv.Value) valString = "true"; else valString = "false";
+ wr.Write(kv.Key + " == " + valString);
+ }
+ wr.WriteLine(");");
+ wr.Flush();
+ }
+ public override void UpdateOutcome(VCGen.Outcome outcome) {
+ wr.WriteLine("analysis outcome :" + outcome);
+ wr.Flush();
+ }
+ public override void UpdateEnqueue(Implementation! implementation) {
+ wr.WriteLine("worklist enqueue :" + implementation.Name);
+ wr.Flush();
+ }
+ public override void UpdateDequeue() {
+ wr.WriteLine("worklist dequeue");
+ wr.Flush();
+ }
+ public override void UpdateConstant(string! constantName) {
+ wr.WriteLine("constant disabled : " + constantName);
+ wr.Flush();
+ }
+ public override void UpdateEnd(bool isNormalEnd) {
+ wr.WriteLine("Houdini ended: " + (isNormalEnd ? "Normal" : "Abnormal"));
+ wr.WriteLine("Number of iterations: " + (this.currentIteration+1));
+ wr.Flush();
+ }
+ public override void UpdateFlushStart() {
+ wr.WriteLine("***************************************");
+ wr.WriteLine("Flushing remaining implementations");
+ wr.Flush();
+ }
+ public override void UpdateFlushFinish() {
+ wr.WriteLine("***************************************");
+ wr.WriteLine("Flushing finished");
+ wr.Flush();
+ }
+ public override void SeeException(string! msg) {
+ wr.WriteLine("Caught exception: " + msg);
+ wr.Flush();
+ }
+
+ }
+
+
+ public abstract class ObservableHoudini {
+ private List<HoudiniObserver!>! observers= new List<HoudiniObserver!>();
+
+ public void AddObserver(HoudiniObserver! observer) {
+ if (!observers.Contains(observer))
+ observers.Add(observer);
+ }
+ private delegate void NotifyDelegate(HoudiniObserver! observer);
+
+ private void Notify(NotifyDelegate! notifyDelegate) {
+ foreach (HoudiniObserver! observer in observers) {
+ notifyDelegate(observer);
+ }
+ }
+ protected void NotifyStart(Program! program, int numConstants) {
+ NotifyDelegate notifyDelegate = (NotifyDelegate) delegate (HoudiniObserver! r) { r.UpdateStart(program, numConstants); };
+ Notify(notifyDelegate);
+ }
+ protected void NotifyIteration() {
+ Notify((NotifyDelegate) delegate (HoudiniObserver! r) { r.UpdateIteration(); });
+ }
+ protected void NotifyImplementation(Implementation! implementation) {
+ Notify((NotifyDelegate) delegate (HoudiniObserver! r) { r.UpdateImplementation(implementation); });
+ }
+ protected void NotifyAssignment(Dictionary<string!,bool>! assignment) {
+ Notify((NotifyDelegate) delegate (HoudiniObserver! r) { r.UpdateAssignment(assignment); });
+ }
+ protected void NotifyOutcome(VCGen.Outcome outcome) {
+ Notify((NotifyDelegate) delegate (HoudiniObserver! r) { r.UpdateOutcome(outcome); });
+ }
+ protected void NotifyEnqueue(Implementation! implementation){
+ Notify((NotifyDelegate) delegate (HoudiniObserver! r) { r.UpdateEnqueue(implementation); });
+ }
+ protected void NotifyDequeue(){
+ Notify((NotifyDelegate) delegate (HoudiniObserver! r) { r.UpdateDequeue(); });
+ }
+ protected void NotifyConstant(string! constantName){
+ Notify((NotifyDelegate) delegate (HoudiniObserver! r) { r.UpdateConstant(constantName); });
+ }
+ protected void NotifyEnd(bool isNormalEnd) {
+ Notify((NotifyDelegate) delegate (HoudiniObserver! r) { r.UpdateEnd(isNormalEnd); });
+ }
+ protected void NotifyFlushStart() {
+ Notify((NotifyDelegate) delegate (HoudiniObserver! r) { r.UpdateFlushStart(); });
+ }
+ protected void NotifyFlushFinish() {
+ Notify((NotifyDelegate) delegate (HoudiniObserver! r) { r.UpdateFlushFinish(); });
+ }
+
+ protected void NotifyException(string! msg) {
+ Notify((NotifyDelegate) delegate (HoudiniObserver! r) { r.SeeException(msg); });
+ }
+ }
+
+ public enum HoudiniProver { Z3, Z3API }
+
+ public class Houdini : ObservableHoudini {
+
+ private Program! program;
+
+ private ReadOnlyDictionary<string!,IdentifierExpr!>! houdiniConstants;
+ private ReadOnlyDictionary<Implementation!,HdnVCGen!>! vcgenSessions;
+ private CallGraph! callGraph;
+ private bool continueAtError;
+ private HoudiniProver houdiniProver;
+ private HdnVCGenFactory! vcgenFactory;
+
+ public Houdini(Program! program, CallGraph! callgraph, bool continueAtError, HoudiniProver houdiniProver) {
+ this.program=program;
+ this.callGraph = callgraph;
+ this.continueAtError = continueAtError;
+ this.houdiniProver = houdiniProver;
+ HdnVCGenFactory factory;
+ switch (this.houdiniProver) {
+ case HoudiniProver.Z3:
+ factory = new Z3HdnVCGenFactory();
+ break;
+ case HoudiniProver.Z3API:
+ default:
+ factory = new Z3APIHdnVCGenFactory();
+ break;
+ }
+ this.vcgenFactory = factory;
+ HoudiniHelper helper = new HoudiniHelper(factory);
+ this.houdiniConstants = helper.CollectExistentialConstants(program);
+ this.vcgenSessions = helper.PrepareVCGenSessions(program);
+ }
+
+ private Queue<Implementation!>! BuildWorkList(Program! program) {
+ Queue<Implementation!>! queue = new Queue<Implementation!>();
+ foreach ( Declaration! decl in program.TopLevelDeclarations ) {
+ Implementation impl = decl as Implementation;
+ if (impl!=null) {
+ queue.Enqueue(impl);
+ }
+ }
+ return queue;
+ }
+
+ private class HoudiniHelper {
+ private HdnVCGenFactory! vcgenFactory;
+ public HoudiniHelper(HdnVCGenFactory! vcgenFactory) {
+ this.vcgenFactory = vcgenFactory;
+ }
+
+ public ReadOnlyDictionary<Implementation!,HdnVCGen!>! PrepareVCGenSessions(Program! program) {
+ Dictionary<Implementation!,HdnVCGen!>! vcgenSessions = new Dictionary<Implementation!,HdnVCGen!>();
+
+ foreach ( Declaration! decl in program.TopLevelDeclarations ) {
+ Implementation impl = decl as Implementation;
+ if (impl!=null) {
+ // make a different simplify log file for each function
+ String simplifyLog = null;
+ if (CommandLineOptions.Clo.SimplifyLogFilePath != null) {
+ simplifyLog = impl.ToString() + CommandLineOptions.Clo.SimplifyLogFilePath;
+ }
+ HdnVCGen! vcgen = vcgenFactory.BuildVCGen(program, simplifyLog, CommandLineOptions.Clo.SimplifyLogFileAppend);
+ vcgen.PrepareVerification(impl,program);
+ vcgenSessions.Add(impl,vcgen);
+ }
+ }
+ return new ReadOnlyDictionary<Implementation!,HdnVCGen!>(vcgenSessions);
+ }
+
+ public ReadOnlyDictionary<string!,IdentifierExpr!>! CollectExistentialConstants(Program! program) {
+ Dictionary<string!,IdentifierExpr!>! existentialConstants = new Dictionary<string!,IdentifierExpr!>();
+ foreach ( Declaration! decl in program.TopLevelDeclarations ) {
+ Constant constant = decl as Constant;
+ if (constant!=null) {
+ bool result=false;
+ if (constant.CheckBooleanAttribute("existential",ref result)) {
+ if (result==true)
+ existentialConstants.Add(constant.Name, new IdentifierExpr(Token.NoToken,constant));
+ }
+ }
+ }
+ return new ReadOnlyDictionary<string!,IdentifierExpr!>(existentialConstants);
+ }
+ }
+
+ private bool MatchCandidate(Expr boogieExpr, out string candidateConstant) {
+ candidateConstant = null;
+ IExpr antecedent;
+ IExpr expr = boogieExpr as IExpr;
+ if (expr!=null && ExprUtil.Match(expr, Prop.Implies, out antecedent)) {
+ IdentifierExpr.ConstantFunApp constantFunApp = antecedent as IdentifierExpr.ConstantFunApp;
+ if ((constantFunApp !=null) && (houdiniConstants.ContainsKey(constantFunApp.IdentifierExpr.Name))) {
+ candidateConstant = constantFunApp.IdentifierExpr.Name;
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ private Axiom! BuildAxiom(Dictionary<string!,bool>! currentAssignment) {
+ Expr axiom = null;
+ foreach (KeyValuePair<string!,bool> kv in currentAssignment) {
+ IdentifierExpr constantExpr;
+ houdiniConstants.TryGetValue(kv.Key,out constantExpr);
+ assume (constantExpr!=null);
+ Expr valueExpr = new LiteralExpr(Token.NoToken, kv.Value);
+ Expr constantAssignment = Expr.Binary(Token.NoToken,BinaryOperator.Opcode.Eq,constantExpr,valueExpr);
+ if (axiom==null)
+ axiom = constantAssignment;
+ else
+ axiom = Expr.Binary(Token.NoToken,BinaryOperator.Opcode.And,axiom,constantAssignment);
+ }
+ if (axiom==null)
+ axiom = new LiteralExpr(Token.NoToken, true);
+ return new Axiom(Token.NoToken,axiom);
+ }
+
+ private Dictionary<string!,bool>! BuildAssignment(Dictionary<string!,IdentifierExpr!>.KeyCollection! constants) {
+ Dictionary<string!,bool>! initial = new Dictionary<string!,bool>();
+ foreach (string! constant in constants)
+ initial.Add(constant,true);
+ return initial;
+ }
+
+ private VCGen.Outcome VerifyUsingAxiom(Implementation! implementation, Axiom! axiom, out List<Counterexample!>? errors) {
+ HdnVCGen vcgen;
+ vcgenSessions.TryGetValue(implementation,out vcgen);
+ if(vcgen==null)
+ throw new Exception("HdnVCGen not found for implementation: " + implementation.Name);
+ vcgen.PushAxiom(axiom);
+ VCGen.Outcome outcome = TryCatchVerify(vcgen, out errors);
+ vcgen.Pop();
+ return outcome;
+ }
+
+ // the main procedure that checks a procedure and updates the
+ // assignment and the worklist
+ private VCGen.Outcome HoudiniVerifyCurrent(HoudiniState! current,
+ Program! program,
+ out List<Counterexample!>? errors,
+ out bool exc) {
+ HdnVCGen vcgen;
+ if (current.Implementation == null)
+ throw new Exception("HoudiniVerifyCurrent has null implementation");
+
+ Implementation! implementation = current.Implementation;
+ vcgenSessions.TryGetValue(implementation,out vcgen);
+ if(vcgen==null)
+ throw new Exception("HdnVCGen not found for implementation: " + implementation.Name);
+
+ VCGen.Outcome outcome = HoudiniVerifyCurrentAux(current, program, vcgen, out errors, out exc);
+ return outcome;
+ }
+
+ private VCGen.Outcome VerifyCurrent(HoudiniState! current,
+ Program! program,
+ out List<Counterexample!>? errors,
+ out bool exc) {
+ HdnVCGen vcgen;
+ if (current.Implementation != null) {
+ Implementation! implementation = current.Implementation;
+ vcgenSessions.TryGetValue(implementation,out vcgen);
+ if(vcgen==null)
+ throw new Exception("HdnVCGen not found for implementation: " + implementation.Name);
+
+ VCGen.Outcome outcome = TrySpinSameFunc(current, program, vcgen, out errors, out exc);
+ return outcome;
+ } else {
+ throw new Exception("VerifyCurrent has null implementation");
+ }
+ }
+
+ private bool IsOutcomeNotHoudini(VCGen.Outcome outcome, List<Counterexample!>? errors) {
+ switch (outcome) {
+ case VCGen.Outcome.Correct:
+ return false;
+ break;
+ case VCGen.Outcome.Errors:
+ assume (errors!=null);
+ foreach (Counterexample error in errors) {
+ if (ExtractRefutedAnnotation(error)==null)
+ return true;
+ }
+ return false;
+ break;
+ case VCGen.Outcome.TimedOut:
+ case VCGen.Outcome.Inconclusive:
+ default:
+ return true;
+ break;
+ }
+ }
+
+
+ // returns true if at least one of the violations is non-candidate
+ private bool AnyNonCandidateViolation(VCGen.Outcome outcome, List<Counterexample!>? errors) {
+ switch (outcome) {
+ case VCGen.Outcome.Errors:
+ assert (errors!=null);
+ foreach (Counterexample error in errors) {
+ if (ExtractRefutedAnnotation(error)==null)
+ return true;
+ }
+ return false;
+ break;
+ case VCGen.Outcome.Correct:
+ case VCGen.Outcome.TimedOut:
+ case VCGen.Outcome.Inconclusive:
+ default:
+ return false;
+ break;
+ }
+ }
+
+
+ private List<Counterexample!> emptyList = new List<Counterexample!>();
+
+ // Record most current Non-Candidate errors found by Boogie, etc.
+ private void UpdateHoudiniOutcome(HoudiniOutcome! houdiniOutcome,
+ Implementation! implementation,
+ VCGen.Outcome verificationOutcome,
+ List<Counterexample!>? errors) {
+ string! implName = implementation.ToString();
+ houdiniOutcome.implementationOutcomes.Remove(implName);
+ List<Counterexample!> nonCandidateErrors = new List<Counterexample!>();
+
+ switch (verificationOutcome) {
+ case VCGen.Outcome.Errors:
+ assume (errors!=null);
+ foreach (Counterexample! error in errors) {
+ if (ExtractRefutedAnnotation(error)==null)
+ nonCandidateErrors.Add(error);
+ }
+ break;
+ case VCGen.Outcome.TimedOut:
+ case VCGen.Outcome.Correct:
+ case VCGen.Outcome.Inconclusive:
+ default:
+ break;
+ }
+ houdiniOutcome.implementationOutcomes.Add(implName,
+ new VCGenOutcome(verificationOutcome, nonCandidateErrors));
+
+ }
+
+ private void FlushWorkList(HoudiniState! current) {
+ this.NotifyFlushStart();
+ Axiom axiom = BuildAxiom(current.Assignment);
+ while (current.WorkList.Count>0) {
+ this.NotifyIteration();
+
+ current.Implementation= current.WorkList.Peek();
+ this.NotifyImplementation(current.Implementation);
+
+ List<Counterexample!>? errors;
+ VCGen.Outcome outcome = VerifyUsingAxiom(current.Implementation,axiom,out errors);
+ UpdateHoudiniOutcome(current.Outcome,current.Implementation,outcome,errors);
+ this.NotifyOutcome(outcome);
+
+ current.WorkList.Dequeue();
+ this.NotifyDequeue();
+
+ }
+ this.NotifyFlushFinish();
+ }
+
+ private void UpdateAssignment(HoudiniState! current, RefutedAnnotation! refAnnot){
+ current.Assignment.Remove(refAnnot.Constant);
+ current.Assignment.Add(refAnnot.Constant,false);
+ this.NotifyConstant(refAnnot.Constant);
+ }
+
+ private void AddToWorkList(HoudiniState! current, Implementation! imp) {
+ if (!current.WorkList.Contains(imp) && !current.isBlackListed(imp.Name)) {
+ current.WorkList.Enqueue(imp);
+ this.NotifyEnqueue(imp);
+ }
+ }
+
+ private void UpdateWorkList(HoudiniState! current,
+ VCGen.Outcome outcome,
+ List<Counterexample!>? errors) {
+ assume (current.Implementation!=null);
+
+ switch (outcome) {
+ case VCGen.Outcome.Correct:
+ current.WorkList.Dequeue();
+ this.NotifyDequeue();
+ break;
+ case VCGen.Outcome.Errors:
+ assume (errors!=null);
+ bool dequeue = false;
+ foreach (Counterexample! error in errors) {
+ RefutedAnnotation refutedAnnotation = ExtractRefutedAnnotation(error);
+ if (refutedAnnotation!=null) {
+ foreach (Implementation! implementation in FindImplementationsToEnqueue(refutedAnnotation,current.Implementation))
+ { AddToWorkList(current, implementation); }
+ UpdateAssignment(current, refutedAnnotation);
+ }
+ else {
+ dequeue = true; //once one non-houdini error is hit dequeue?!
+ }
+ }
+ if (dequeue) {
+ current.WorkList.Dequeue();
+ this.NotifyDequeue();
+ }
+ break;
+ case VCGen.Outcome.TimedOut:
+ // TODO: reset session instead of blocking timed out funcs?
+ current.addToBlackList(current.Implementation.Name);
+ current.WorkList.Dequeue();
+ this.NotifyDequeue();
+ break;
+ case VCGen.Outcome.OutOfMemory:
+ case VCGen.Outcome.Inconclusive:
+ current.WorkList.Dequeue();
+ this.NotifyDequeue();
+ break;
+ default:
+ throw new Exception("Unknown vcgen outcome");
+ }
+ }
+
+
+ private void AddRelatedToWorkList(HoudiniState! current, RefutedAnnotation! refutedAnnotation) {
+ assume (current.Implementation != null);
+ foreach (Implementation! implementation in FindImplementationsToEnqueue(refutedAnnotation,current.Implementation)) {
+ AddToWorkList(current, implementation);
+ }
+ }
+
+
+ // Updates the worklist and current assignment
+ // @return true if the current function is kept on the queue
+ private bool UpdateAssignmentWorkList(HoudiniState! current,
+ VCGen.Outcome outcome,
+ List<Counterexample!>? errors) {
+ assume (current.Implementation!=null);
+ bool dequeue = true;
+
+ switch (outcome) {
+ case VCGen.Outcome.Correct:
+ //yeah, dequeue
+ break;
+ case VCGen.Outcome.Errors:
+ assume (errors!=null);
+ foreach (Counterexample! error in errors) {
+ RefutedAnnotation refutedAnnotation = ExtractRefutedAnnotation(error);
+ if (refutedAnnotation!=null) { // some candidate annotation removed
+ AddRelatedToWorkList(current, refutedAnnotation);
+ UpdateAssignment(current, refutedAnnotation);
+ dequeue = false;
+ }
+ }
+ break;
+
+ case VCGen.Outcome.TimedOut:
+ // TODO: reset session instead of blocking timed out funcs?
+ current.addToBlackList(current.Implementation.Name);
+ break;
+ case VCGen.Outcome.Inconclusive:
+ case VCGen.Outcome.OutOfMemory:
+ break;
+ default:
+ throw new Exception("Unknown vcgen outcome");
+ }
+ if (dequeue) {
+ current.WorkList.Dequeue();
+ this.NotifyDequeue();
+ }
+ return !dequeue;
+ }
+
+
+
+ private class HoudiniState {
+ private Queue<Implementation!>! _workList;
+ private Set<string!>! blackList;
+ private Dictionary<string!,bool>! _assignment;
+ private Implementation _implementation;
+ private HoudiniOutcome! _outcome;
+
+ public HoudiniState(Queue<Implementation!>! workList, Dictionary<string!,bool>! currentAssignment) {
+ this._workList = workList;
+ this._assignment = currentAssignment;
+ this._implementation = null;
+ this._outcome = new HoudiniOutcome();
+ this.blackList = new Set<string!>();
+ }
+
+ public Queue<Implementation!>! WorkList {
+ get { return this._workList; }
+ }
+ public Dictionary<string!,bool>! Assignment {
+ get { return this._assignment; }
+ }
+ public Implementation Implementation {
+ get { return this._implementation; }
+ set { this._implementation = value; }
+ }
+ public HoudiniOutcome! Outcome {
+ get { return this._outcome; }
+ }
+ public bool isBlackListed(string! funcName) {
+ return blackList.Contains(funcName);
+ }
+ public void addToBlackList(string! funcName) {
+ blackList.Add(funcName);
+ }
+ }
+
+ private void PrintBadList(string kind, List<string!>! list) {
+ if(list.Count != 0) {
+ Console.WriteLine("----------------------------------------");
+ Console.WriteLine("Functions: {0}", kind);
+ foreach(string! fname in list) {
+ Console.WriteLine("\t{0}", fname);
+ }
+ Console.WriteLine("----------------------------------------");
+ }
+ }
+
+ private void PrintBadOutcomes(List<string!>! timeouts, List<string!>! inconc, List<string!>! errors) {
+ PrintBadList("TimedOut", timeouts);
+ PrintBadList("Inconclusive", inconc);
+ PrintBadList("Errors", errors);
+ }
+
+ public HoudiniOutcome! VerifyProgram (Program! program) {
+ HoudiniOutcome outcome;
+ switch (this.houdiniProver) {
+ case HoudiniProver.Z3:
+ outcome = VerifyProgramSameFuncFirst(program);
+ //outcome = PerformHoudiniInference(program);
+ break;
+ case HoudiniProver.Z3API: // not that stable
+ outcome = VerifyProgramUnorderedWork(program);
+ break;
+ default:
+ throw new Exception("Unknown HoudiniProver: " + this.houdiniProver.ToString());
+ }
+ PrintBadOutcomes(outcome.ListOfTimeouts, outcome.ListOfInconclusives, outcome.ListOfErrors);
+ return outcome;
+ }
+
+ // Old main loop
+ public HoudiniOutcome! VerifyProgramUnorderedWork (Program! program) {
+ HoudiniState current = new HoudiniState(BuildWorkList(program),BuildAssignment((!)houdiniConstants.Keys));
+ this.NotifyStart(program, houdiniConstants.Keys.Count);
+
+ while (current.WorkList.Count>0) {
+ System.GC.Collect();
+ this.NotifyIteration();
+
+ Axiom axiom = BuildAxiom(current.Assignment);
+ this.NotifyAssignment(current.Assignment);
+
+ current.Implementation= current.WorkList.Peek();
+ this.NotifyImplementation(current.Implementation);
+
+ List<Counterexample!>? errors;
+ VCGen.Outcome outcome = VerifyUsingAxiom(current.Implementation, axiom, out errors);
+ this.NotifyOutcome(outcome);
+
+ UpdateHoudiniOutcome(current.Outcome,current.Implementation,outcome,errors);
+ if (IsOutcomeNotHoudini(outcome,errors) && !continueAtError) {
+ current.WorkList.Dequeue();
+ this.NotifyDequeue();
+ FlushWorkList(current);
+ } else
+ UpdateWorkList(current,outcome,errors);
+ }
+ this.NotifyEnd(true);
+ current.Outcome.assignment = current.Assignment;
+ return current.Outcome;
+ }
+
+ // New main loop
+ public HoudiniOutcome! VerifyProgramSameFuncFirst (Program! program) {
+ HoudiniState current = new HoudiniState(BuildWorkList(program),BuildAssignment((!)houdiniConstants.Keys));
+ this.NotifyStart(program, houdiniConstants.Keys.Count);
+
+ while (current.WorkList.Count>0) {
+ bool exceptional = false;
+ System.GC.Collect();
+ this.NotifyIteration();
+
+ current.Implementation = current.WorkList.Peek();
+ this.NotifyImplementation(current.Implementation);
+
+ List<Counterexample!>? errors;
+ VCGen.Outcome outcome = VerifyCurrent(current, program, out errors, out exceptional);
+
+ // updates to worklist already done in VerifyCurrent, unless there was an exception
+ if (exceptional) {
+ this.NotifyOutcome(outcome);
+ UpdateHoudiniOutcome(current.Outcome, current.Implementation, outcome, errors);
+ if (IsOutcomeNotHoudini(outcome,errors) && !continueAtError){
+ current.WorkList.Dequeue();
+ this.NotifyDequeue();
+ FlushWorkList(current);
+ } else {
+ UpdateAssignmentWorkList(current, outcome, errors);
+ }
+ exceptional = false;
+ }
+ }
+ this.NotifyEnd(true);
+ current.Outcome.assignment = current.Assignment;
+ return current.Outcome;
+ }
+
+ //Clean houdini (Based on "Houdini Spec in Boogie" email 10/22/08
+ //Aborts when there is a violation of non-candidate assertion
+ //This can be used in eager mode (continueAfterError) by simply making
+ //all non-candidate annotations as unchecked (free requires/ensures, assumes)
+ public HoudiniOutcome! PerformHoudiniInference(Program! program) {
+ HoudiniState current = new HoudiniState(BuildWorkList(program),BuildAssignment((!)houdiniConstants.Keys));
+ this.NotifyStart(program, houdiniConstants.Keys.Count);
+
+ Console.WriteLine("Using the new houdini algorithm\n");
+
+ while (current.WorkList.Count > 0) {
+ bool exceptional = false;
+ System.GC.Collect();
+ this.NotifyIteration();
+
+ current.Implementation = current.WorkList.Peek();
+ this.NotifyImplementation(current.Implementation);
+
+ List<Counterexample!>? errors;
+ VCGen.Outcome outcome = HoudiniVerifyCurrent(current, program, out errors, out exceptional);
+
+ // updates to worklist already done in VerifyCurrent, unless there was an exception
+ if (exceptional) {
+ this.NotifyOutcome(outcome);
+ UpdateHoudiniOutcome(current.Outcome, current.Implementation, outcome, errors);
+ if (AnyNonCandidateViolation(outcome, errors)) { //abort
+ current.WorkList.Dequeue();
+ this.NotifyDequeue();
+ FlushWorkList(current);
+ } else { //continue
+ UpdateAssignmentWorkList(current, outcome, errors);
+ }
+ }
+ }
+ this.NotifyEnd(true);
+ current.Outcome.assignment = current.Assignment;
+ return current.Outcome;
+ }
+
+
+ private List<Implementation!>! FindImplementationsToEnqueue(RefutedAnnotation! refutedAnnotation, Implementation! currentImplementation) {
+ List<Implementation!>! implementations = new List<Implementation!>();
+ switch (refutedAnnotation.Kind) {
+ case RefutedAnnotationKind.REQUIRES:
+ foreach (Implementation! callee in callGraph.NextNodes(currentImplementation)) {
+ assume (callee.Proc!=null);
+ if (callee.Proc.Equals(refutedAnnotation.CalleeProc))
+ implementations.Add(callee);
+ }
+ break;
+ case RefutedAnnotationKind.ENSURES:
+ foreach (Implementation! caller in callGraph.PreviousNodes(currentImplementation))
+ implementations.Add(caller);
+ break;
+ case RefutedAnnotationKind.ASSERT: //the implementation is already in queue
+ break;
+ default:
+ throw new Exception("Unknown Refuted annotation kind:" + refutedAnnotation.Kind);
+ break;
+ }
+ return implementations;
+ }
+
+ private enum RefutedAnnotationKind { REQUIRES, ENSURES, ASSERT};
+
+ private class RefutedAnnotation {
+ private string! _constant;
+ private RefutedAnnotationKind _kind;
+ private Procedure _callee;
+
+ private RefutedAnnotation(string! constant, RefutedAnnotationKind kind, Procedure callee) {
+ this._constant = constant;
+ this._kind = kind;
+ this._callee = callee;
+ }
+ public RefutedAnnotationKind Kind {
+ get { return this._kind; }
+ }
+ public string! Constant {
+ get { return this._constant; }
+ }
+ public Procedure CalleeProc {
+ get { return this._callee; }
+ }
+ public static RefutedAnnotation BuildRefutedRequires(string! constant, Procedure! callee) {
+ return new RefutedAnnotation(constant,RefutedAnnotationKind.REQUIRES,callee);
+ }
+ public static RefutedAnnotation BuildRefutedEnsures(string! constant) {
+ return new RefutedAnnotation(constant,RefutedAnnotationKind.ENSURES, null);
+ }
+ public static RefutedAnnotation BuildRefutedAssert(string! constant) {
+ return new RefutedAnnotation(constant,RefutedAnnotationKind.ASSERT, null);
+ }
+
+ }
+
+ private void PrintRefutedCall(CallCounterexample! err, XmlSink! xmlOut) {
+ Expr! cond = err.FailingRequires.Condition;
+ string houdiniConst;
+ if (MatchCandidate(cond, out houdiniConst)) {
+ xmlOut.WriteError("precondition violation", err.FailingCall.tok, err.FailingRequires.tok, err.Trace);
+ }
+ }
+
+ private void PrintRefutedReturn(ReturnCounterexample! err, XmlSink! xmlOut) {
+ Expr! cond = err.FailingEnsures.Condition;
+ string houdiniConst;
+ if (MatchCandidate(cond, out houdiniConst)) {
+ xmlOut.WriteError("postcondition violation", err.FailingReturn.tok, err.FailingEnsures.tok, err.Trace);
+ }
+ }
+
+ private void PrintRefutedAssert(AssertCounterexample! err, XmlSink! xmlOut) {
+ Expr cond = err.FailingAssert.OrigExpr;
+ string houdiniConst;
+ if (MatchCandidate(cond, out houdiniConst)) {
+ xmlOut.WriteError("postcondition violation", err.FailingAssert.tok, err.FailingAssert.tok, err.Trace);
+ }
+ }
+
+
+ private void DebugRefutedCandidates(Implementation! curFunc, List<Counterexample!> errors) {
+ XmlSink xmlRefuted = CommandLineOptions.Clo.XmlRefuted;
+ if (xmlRefuted != null && errors != null) {
+ DateTime start = DateTime.Now;
+ xmlRefuted.WriteStartMethod(curFunc.ToString(), start);
+
+ foreach (Counterexample! error in errors) {
+ CallCounterexample ce = error as CallCounterexample;
+ if (ce != null) PrintRefutedCall(ce, xmlRefuted);
+ ReturnCounterexample re = error as ReturnCounterexample;
+ if (re != null) PrintRefutedReturn(re, xmlRefuted);
+ AssertCounterexample ae = error as AssertCounterexample;
+ if (ae != null) PrintRefutedAssert(ae, xmlRefuted);
+ }
+
+ DateTime end = DateTime.Now;
+ xmlRefuted.WriteEndMethod("errors", end, end.Subtract(start));
+ }
+ }
+
+ private RefutedAnnotation ExtractRefutedAnnotation(Counterexample error) {
+ string houdiniConstant;
+ CallCounterexample callCounterexample = error as CallCounterexample;
+ if (callCounterexample!=null) {
+ Procedure! failingProcedure = (!)callCounterexample.FailingCall.Proc;
+ Requires! failingRequires = (!)callCounterexample.FailingRequires;
+ if (MatchCandidate(failingRequires.Condition,out houdiniConstant)) {
+ assert (houdiniConstant!=null);
+ return RefutedAnnotation.BuildRefutedRequires(houdiniConstant, failingProcedure);
+ }
+ }
+ ReturnCounterexample returnCounterexample = error as ReturnCounterexample;
+ if (returnCounterexample!=null) {
+ Ensures failingEnsures =returnCounterexample.FailingEnsures;
+ if (MatchCandidate(failingEnsures.Condition,out houdiniConstant)) {
+ assert (houdiniConstant!=null);
+ return RefutedAnnotation.BuildRefutedEnsures(houdiniConstant);
+ }
+ }
+ AssertCounterexample assertCounterexample = error as AssertCounterexample;
+ if (assertCounterexample!=null) {
+ AssertCmd failingAssert = assertCounterexample.FailingAssert;
+ if (MatchCandidate(failingAssert.OrigExpr,out houdiniConstant)) {
+ assert (houdiniConstant!=null);
+ return RefutedAnnotation.BuildRefutedAssert(houdiniConstant);
+ }
+ }
+
+ return null;
+ }
+
+ private VCGen.Outcome TryCatchVerify(HdnVCGen! vcgen, out List<Counterexample!>? errors) {
+ VCGen.Outcome outcome;
+ try {
+ outcome = vcgen.Verify(out errors);
+ } catch (VCGenException e) {
+ assume(e!=null);
+ errors = null;
+ outcome = VCGen.Outcome.Inconclusive;
+ } catch (UnexpectedProverOutputException upo) {
+ assume(upo!=null);
+ errors = null;
+ outcome = VCGen.Outcome.Inconclusive;
+ }
+ return outcome;
+ }
+
+ //version of TryCatchVerify that spins on the same function
+ //as long as the current assignment is changing
+ private VCGen.Outcome TrySpinSameFunc(HoudiniState! current,
+ Program! program,
+ HdnVCGen! vcgen,
+ out List<Counterexample!>? errors,
+ out bool exceptional) {
+ assert (current.Implementation != null);
+ VCGen.Outcome outcome;
+ bool pushed = false;
+ errors = null;
+ outcome = VCGen.Outcome.Inconclusive;
+ try {
+ bool trySameFunc = true;
+ bool pastFirstIter = false; //see if this new loop is even helping
+
+ do {
+ if (pastFirstIter) {
+ System.GC.Collect();
+ this.NotifyIteration();
+ }
+ Axiom! currentAx = BuildAxiom(current.Assignment);
+ this.NotifyAssignment(current.Assignment);
+
+ vcgen.PushAxiom(currentAx);
+ pushed = true;
+ outcome = vcgen.Verify(out errors);
+ vcgen.Pop();
+ pushed = false;
+ this.NotifyOutcome(outcome);
+
+ DebugRefutedCandidates(current.Implementation, errors);
+ UpdateHoudiniOutcome(current.Outcome, current.Implementation, outcome, errors);
+ if (!continueAtError && IsOutcomeNotHoudini(outcome,errors)) {
+ current.WorkList.Dequeue();
+ this.NotifyDequeue();
+ trySameFunc = false;
+ FlushWorkList(current);
+ } else {
+ trySameFunc = UpdateAssignmentWorkList(current, outcome, errors);
+ //reset for the next round
+ errors = null;
+ outcome = VCGen.Outcome.Inconclusive;
+ }
+ pastFirstIter = true;
+ } while (trySameFunc && current.WorkList.Count > 0);
+
+ } catch (VCGenException e) {
+ assume(e!=null);
+ if (pushed){
+ vcgen.Pop(); // what if session is dead?
+ }
+ NotifyException("VCGen");
+ exceptional = true;
+ return outcome;
+ } catch (UnexpectedProverOutputException upo) {
+ assume(upo!=null);
+ if (pushed){
+ vcgen.Pop();
+ }
+ NotifyException("UnexpectedProverOutput");
+ exceptional = true;
+ return outcome;
+ }
+ exceptional = false;
+ return outcome;
+ }
+
+
+
+ //Similar to TrySpinSameFunc except no Candidate logic
+ private VCGen.Outcome HoudiniVerifyCurrentAux(HoudiniState! current,
+ Program! program,
+ HdnVCGen! vcgen,
+ out List<Counterexample!>? errors,
+ out bool exceptional) {
+ assert (current.Implementation != null);
+ VCGen.Outcome outcome;
+ bool pushed = false;
+ errors = null;
+ outcome = VCGen.Outcome.Inconclusive;
+ try {
+ bool trySameFunc = true;
+ bool pastFirstIter = false; //see if this new loop is even helping
+
+ do {
+ if (pastFirstIter) {
+ System.GC.Collect();
+ this.NotifyIteration();
+ }
+
+ Axiom! currentAx = BuildAxiom(current.Assignment);
+ this.NotifyAssignment(current.Assignment);
+
+ //check the VC with the current assignment
+ vcgen.PushAxiom(currentAx);
+ pushed = true;
+ outcome = vcgen.Verify(out errors);
+ vcgen.Pop();
+ pushed = false;
+ this.NotifyOutcome(outcome);
+
+ DebugRefutedCandidates(current.Implementation, errors);
+ UpdateHoudiniOutcome(current.Outcome, current.Implementation, outcome, errors);
+
+ if (AnyNonCandidateViolation(outcome, errors)) { //abort
+ current.WorkList.Dequeue();
+ this.NotifyDequeue();
+ trySameFunc = false;
+ FlushWorkList(current);
+ } else { //continue
+ trySameFunc = UpdateAssignmentWorkList(current, outcome, errors);
+ //reset for the next round
+ errors = null;
+ outcome = VCGen.Outcome.Inconclusive;
+ }
+ pastFirstIter = true;
+ } while (trySameFunc && current.WorkList.Count > 0);
+
+ } catch (VCGenException e) {
+ assume(e!=null);
+ if (pushed){
+ vcgen.Pop(); // what if session is dead?
+ }
+ NotifyException("VCGen");
+ exceptional = true;
+ return outcome;
+ } catch (UnexpectedProverOutputException upo) {
+ assume(upo!=null);
+ if (pushed){
+ vcgen.Pop();
+ }
+ NotifyException("UnexpectedProverOutput");
+ exceptional = true;
+ return outcome;
+ }
+ exceptional = false;
+ return outcome;
+ }
+ }
+
+ public enum HoudiniOutcomeKind { Done, FatalError, VerificationCompleted }
+
+ public class VCGenOutcome {
+ public VCGen.Outcome outcome;
+ public List<Counterexample!> errors;
+ public VCGenOutcome(VCGen.Outcome outcome, List<Counterexample!> errors) {
+ this.outcome = outcome;
+ this.errors = errors;
+ }
+ }
+
+ public class HoudiniOutcome {
+ // final assignment
+ public Dictionary<string!,bool>! assignment = new Dictionary<string!,bool>();
+ // boogie errors
+ public Dictionary<string!,VCGenOutcome!>! implementationOutcomes = new Dictionary<string!,VCGenOutcome!>();
+ // outcome kind
+ public HoudiniOutcomeKind kind;
+
+ // statistics
+
+ private int CountResults(VCGen.Outcome outcome) {
+ int outcomeCount=0;
+ foreach (VCGenOutcome! verifyOutcome in implementationOutcomes.Values) {
+ if (verifyOutcome.outcome==outcome)
+ outcomeCount++;
+ }
+ return outcomeCount;
+ }
+
+ private List<string!>! ListOutcomeMatches(VCGen.Outcome outcome) {
+ List<string!>! result = new List<string!>();
+ foreach (KeyValuePair<string!, VCGenOutcome!> kvpair in implementationOutcomes) {
+ if (kvpair.Value.outcome==outcome)
+ result.Add(kvpair.Key);
+ }
+ return result;
+ }
+
+ public int ErrorCount {
+ get {
+ return CountResults(VCGen.Outcome.Errors);
+ }
+ }
+ public int Verified {
+ get {
+ return CountResults(VCGen.Outcome.Correct);
+ }
+ }
+ public int Inconclusives {
+ get {
+ return CountResults(VCGen.Outcome.Inconclusive);
+ }
+ }
+ public int TimeOuts {
+ get {
+ return CountResults(VCGen.Outcome.TimedOut);
+ }
+ }
+ public List<string!>! ListOfTimeouts {
+ get {
+ return ListOutcomeMatches(VCGen.Outcome.TimedOut);
+ }
+ }
+ public List<string!>! ListOfInconclusives {
+ get {
+ return ListOutcomeMatches(VCGen.Outcome.Inconclusive);
+ }
+ }
+ public List<string!>! ListOfErrors {
+ get {
+ return ListOutcomeMatches(VCGen.Outcome.Errors);
+ }
+ }
+ }
+
+}
diff --git a/Source/Houdini/Houdini.sscproj b/Source/Houdini/Houdini.sscproj
new file mode 100644
index 00000000..3f68bb91
--- /dev/null
+++ b/Source/Houdini/Houdini.sscproj
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="utf-8"?>
+<VisualStudioProject>
+ <XEN ProjectType="Local"
+ SchemaVersion="1.0"
+ Name="Houdini"
+ ProjectGuid="40454b39-4f61-48b2-bde3-e9271b24f469"
+ >
+ <Build>
+ <Settings ApplicationIcon=""
+ AssemblyName="Houdini"
+ OutputType="Library"
+ RootNamespace="Houdini"
+ StartupObject=""
+ StandardLibraryLocation=""
+ TargetPlatform="v2"
+ TargetPlatformLocation=""
+ >
+ <Config Name="Debug"
+ AllowUnsafeBlocks="False"
+ BaseAddress="285212672"
+ CheckForOverflowUnderflow="False"
+ ConfigurationOverrideFile=""
+ DefineConstants="DEBUG;TRACE"
+ DocumentationFile=""
+ DebugSymbols="True"
+ FileAlignment="4096"
+ IncrementalBuild="True"
+ Optimize="False"
+ OutputPath="bin\debug"
+ RegisterForComInterop="False"
+ RemoveIntegerChecks="false"
+ TreatWarningsAsErrors="False"
+ WarningLevel="4"
+ CheckContractAdmissibility="True"
+ CheckPurity="False"
+ />
+ <Config Name="Release"
+ AllowUnsafeBlocks="false"
+ BaseAddress="285212672"
+ CheckForOverflowUnderflow="false"
+ ConfigurationOverrideFile=""
+ DefineConstants="TRACE"
+ DocumentationFile=""
+ DebugSymbols="false"
+ FileAlignment="4096"
+ IncrementalBuild="false"
+ Optimize="true"
+ OutputPath="bin\release"
+ RegisterForComInterop="false"
+ RemoveIntegerChecks="false"
+ TreatWarningsAsErrors="false"
+ WarningLevel="4"
+ CheckContractAdmissibility="True"
+ CheckPurity="False"
+ />
+ </Settings>
+ <References>
+ <Reference Name="System"
+ AssemblyName="System"
+ Private="false"
+ />
+ <Reference Name="System.Data"
+ AssemblyName="System.Data"
+ Private="false"
+ />
+ <Reference Name="System.Xml"
+ AssemblyName="System.Xml"
+ Private="false"
+ />
+ <Reference Name="Core"
+ Project="{47BC34F1-A173-40BE-84C2-9332B4418387}"
+ Private="true"
+ />
+ <Reference Name="VCGeneration"
+ Project="{F65666DE-FB56-457C-8782-09BE243450FC}"
+ Private="true"
+ />
+ <Reference Name="Simplify"
+ Project="{F75666DE-FB56-457C-8782-09BE243450FC}"
+ Private="true"
+ />
+ <Reference Name="AIFramework"
+ Project="{24B55172-AD8B-47D1-8952-5A95CFDB9B31}"
+ Private="true"
+ />
+ <Reference Name="AbsInt"
+ Project="{11D06232-2039-4BCA-853B-C596E2A4EDB0}"
+ Private="true"
+ />
+ <Reference Name="System.Compiler.Framework"
+ AssemblyName="System.Compiler.Framework"
+ Private="false"
+ HintPath="../../Binaries/System.Compiler.Framework.dll"
+ />
+ <Reference Name="Z3"
+ Project="{F75666DE-CD56-457C-8782-09BE243450FC}"
+ Private="true"
+ />
+ </References>
+ </Build>
+ <Files>
+ <Include>
+ <File RelPath="Houdini.ssc"
+ SubType="Code"
+ BuildAction="Compile"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="Checker.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="..\version.ssc"
+ />
+ </Include>
+ </Files>
+ </XEN>
+</VisualStudioProject> \ No newline at end of file
diff --git a/Source/Provers/SMTLib/ProverInterface.ssc b/Source/Provers/SMTLib/ProverInterface.ssc
new file mode 100644
index 00000000..b55cc403
--- /dev/null
+++ b/Source/Provers/SMTLib/ProverInterface.ssc
@@ -0,0 +1,240 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Threading;
+using System.IO;
+using ExternalProver;
+using System.Diagnostics;
+using Microsoft.Contracts;
+using Microsoft.Boogie.AbstractInterpretation;
+using Microsoft.Boogie;
+using Microsoft.Boogie.VCExprAST;
+using Microsoft.Boogie.Clustering;
+using Microsoft.Boogie.TypeErasure;
+using Microsoft.Boogie.Simplify;
+
+namespace Microsoft.Boogie.SMTLib
+{
+ public class SMTLibProcessTheoremProver : LogProverInterface
+ {
+ private readonly DeclFreeProverContext! ctx;
+
+ [NotDelayed]
+ public SMTLibProcessTheoremProver(ProverOptions! options, VCExpressionGenerator! gen,
+ DeclFreeProverContext! ctx)
+ {
+ InitializeGlobalInformation("UnivBackPred2.smt");
+
+ this.ctx = ctx;
+
+ TypeAxiomBuilder! axBuilder;
+ switch (CommandLineOptions.Clo.TypeEncodingMethod) {
+ case CommandLineOptions.TypeEncoding.Arguments:
+ axBuilder = new TypeAxiomBuilderArguments (gen);
+ break;
+ default:
+ axBuilder = new TypeAxiomBuilderPremisses (gen);
+ break;
+ }
+ axBuilder.Setup();
+ AxBuilder = axBuilder;
+ UniqueNamer namer = new UniqueNamer ();
+ Namer = namer;
+ this.DeclCollector = new TypeDeclCollector (namer);
+ base(options, "", "", "", "", gen);
+ }
+
+ public override ProverContext! Context { get {
+ return ctx;
+ } }
+
+ private readonly TypeAxiomBuilder! AxBuilder;
+ private readonly UniqueNamer! Namer;
+ private readonly TypeDeclCollector! DeclCollector;
+
+ private void FeedTypeDeclsToProver() {
+ foreach (string! s in DeclCollector.GetNewDeclarations())
+ AddTypeDecl(s);
+ }
+
+ public override void BeginCheck(string! descriptiveName, VCExpr! vc, ErrorHandler! handler) {
+ TextWriter! output = OpenOutputFile(descriptiveName);
+
+ string! name =
+ MakeBenchmarkNameSafe(SMTLibExprLineariser.MakeIdPrintable(descriptiveName));
+ WriteLineAndLog(output, "(benchmark " + name);
+ WriteLineAndLog(output, _backgroundPredicates);
+
+ if (!AxiomsAreSetup) {
+ AddAxiom(VCExpr2String(ctx.Axioms, -1));
+ AxiomsAreSetup = true;
+ }
+
+ string vcString = ":formula (not " + VCExpr2String(vc, 1) + ")";
+ string! prelude = ctx.GetProverCommands(true);
+ WriteLineAndLog(output, prelude);
+
+ foreach (string! s in TypeDecls) {
+ WriteLineAndLog(output, s);
+ }
+ foreach (string! s in Axioms) {
+ WriteLineAndLog(output, ":assumption");
+ WriteLineAndLog(output, s);
+ }
+
+ WriteLineAndLog(output, vcString);
+ WriteLineAndLog(output, ")");
+
+ output.Close();
+ }
+
+ // certain words that should not occur in the name of a benchmark
+ // because some solvers don't like them
+ private readonly static List<string!>! BadBenchmarkWords = new List<string!> ();
+ static SMTLibProcessTheoremProver() {
+ BadBenchmarkWords.Add("Array"); BadBenchmarkWords.Add("Arrray");
+ }
+
+ private string! MakeBenchmarkNameSafe(string! name) {
+ for (int i = 0; i < BadBenchmarkWords.Count; i = i + 2)
+ name = name.Replace(BadBenchmarkWords[i], BadBenchmarkWords[i+1]);
+ return name;
+ }
+
+ private TextWriter! OpenOutputFile(string! descriptiveName) {
+ string filename = CommandLineOptions.Clo.SMTLibOutputPath;
+ filename = Helpers.SubstituteAtPROC(descriptiveName, (!)filename);
+ return new StreamWriter(filename, false);
+ }
+
+ private void WriteLineAndLog(TextWriter! output, string! msg) {
+ LogActivity(msg);
+ output.WriteLine(msg);
+ }
+
+ [NoDefaultContract]
+ public override Outcome CheckOutcome(ErrorHandler! handler)
+ throws UnexpectedProverOutputException; {
+ return Outcome.Undetermined;
+ }
+
+ protected string! VCExpr2String(VCExpr! expr, int polarity) {
+ DateTime start = DateTime.Now;
+ if (CommandLineOptions.Clo.Trace)
+ Console.Write("Linearising ... ");
+
+ // handle the types in the VCExpr
+ TypeEraser! eraser;
+ switch (CommandLineOptions.Clo.TypeEncodingMethod) {
+ case CommandLineOptions.TypeEncoding.Arguments:
+ eraser = new TypeEraserArguments((TypeAxiomBuilderArguments)AxBuilder, gen);
+ break;
+ default:
+ eraser = new TypeEraserPremisses((TypeAxiomBuilderPremisses)AxBuilder, gen);
+ break;
+ }
+ VCExpr! exprWithoutTypes = eraser.Erase(expr, polarity);
+
+ LetBindingSorter! letSorter = new LetBindingSorter(gen);
+ VCExpr! sortedExpr = letSorter.Mutate(exprWithoutTypes, true);
+ VCExpr! sortedAxioms = letSorter.Mutate(AxBuilder.GetNewAxioms(), true);
+
+ DeclCollector.Collect(sortedAxioms);
+ DeclCollector.Collect(sortedExpr);
+ FeedTypeDeclsToProver();
+
+ AddAxiom(SMTLibExprLineariser.ToString(sortedAxioms, Namer));
+ string! res = SMTLibExprLineariser.ToString(sortedExpr, Namer);
+
+ if (CommandLineOptions.Clo.Trace) {
+ DateTime end = DateTime.Now;
+ TimeSpan elapsed = end - start;
+ Console.WriteLine("finished [{0} s] ", elapsed.TotalSeconds);
+ }
+ return res;
+ }
+
+ // the list of all known axioms, where have to be included in each
+ // verification condition
+ private readonly List<string!>! Axioms = new List<string!> ();
+ private bool AxiomsAreSetup = false;
+
+ // similarly, a list of function/predicate declarations
+ private readonly List<string!>! TypeDecls = new List<string!> ();
+
+ protected void AddAxiom(string! axiom) {
+ Axioms.Add(axiom);
+// if (thmProver != null) {
+// LogActivity(":assume " + axiom);
+// thmProver.AddAxioms(axiom);
+// }
+ }
+
+ protected void AddTypeDecl(string! decl) {
+ TypeDecls.Add(decl);
+ // if (thmProver != null) {
+ // LogActivity(decl);
+ // thmProver.Feed(decl, 0);
+ // }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ private static string! _backgroundPredicates;
+
+ static void InitializeGlobalInformation(string! backgroundPred)
+ ensures _backgroundPredicates != null;
+ //throws ProverException, System.IO.FileNotFoundException;
+ {
+ if (_backgroundPredicates == null) {
+ string! codebaseString =
+ (!) Path.GetDirectoryName((!)System.Reflection.Assembly.GetExecutingAssembly().Location);
+
+ // Initialize '_backgroundPredicates'
+ string univBackPredPath = Path.Combine(codebaseString, backgroundPred);
+ using (StreamReader reader = new System.IO.StreamReader(univBackPredPath))
+ {
+ _backgroundPredicates = reader.ReadToEnd();
+ }
+ }
+ }
+ }
+
+ public class Factory : ProverFactory
+ {
+
+ public override object! SpawnProver(ProverOptions! options, object! ctxt)
+ {
+ return this.SpawnProver(options,
+ ((DeclFreeProverContext!)ctxt).ExprGen,
+ (DeclFreeProverContext!)ctxt);
+ }
+
+ public override object! NewProverContext(ProverOptions! options) {
+ if (CommandLineOptions.Clo.BracketIdsInVC < 0) {
+ CommandLineOptions.Clo.BracketIdsInVC = 0;
+ }
+
+ VCExpressionGenerator! gen = new VCExpressionGenerator ();
+ List<string!>! proverCommands = new List<string!> ();
+// TODO: what is supported?
+// proverCommands.Add("all");
+// proverCommands.Add("simplify");
+// proverCommands.Add("simplifyLike");
+ VCGenerationOptions! genOptions = new VCGenerationOptions(proverCommands);
+
+ return new DeclFreeProverContext(gen, genOptions);
+ }
+
+ protected virtual SMTLibProcessTheoremProver! SpawnProver(ProverOptions! options,
+ VCExpressionGenerator! gen,
+ DeclFreeProverContext! ctx) {
+ return new SMTLibProcessTheoremProver(options, gen, ctx);
+ }
+ }
+}
diff --git a/Source/Provers/SMTLib/SMTLib.sscproj b/Source/Provers/SMTLib/SMTLib.sscproj
new file mode 100644
index 00000000..d618b0dd
--- /dev/null
+++ b/Source/Provers/SMTLib/SMTLib.sscproj
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="utf-8"?>
+<VisualStudioProject>
+ <XEN ProjectType="Local"
+ SchemaVersion="1.0"
+ Name="SMTLib"
+ ProjectGuid="13c3a68c-462a-4cda-a480-738046e37c5a"
+ >
+ <Build>
+ <Settings ApplicationIcon=""
+ AssemblyName="Provers.SMTLib"
+ OutputType="Library"
+ RootNamespace="Microsoft.Boogie.SMTLib"
+ StartupObject=""
+ StandardLibraryLocation=""
+ TargetPlatform="v2"
+ TargetPlatformLocation=""
+ ShadowedAssembly=""
+ NoStandardLibraries="False"
+ >
+ <Config Name="Debug"
+ AllowUnsafeBlocks="False"
+ BaseAddress="285212672"
+ CheckForOverflowUnderflow="False"
+ ConfigurationOverrideFile=""
+ DefineConstants="DEBUG;TRACE"
+ DocumentationFile=""
+ DebugSymbols="True"
+ FileAlignment="4096"
+ IncrementalBuild="True"
+ Optimize="False"
+ OutputPath="bin\debug"
+ RegisterForComInterop="False"
+ RemoveIntegerChecks="false"
+ TreatWarningsAsErrors="False"
+ WarningLevel="4"
+ CheckContractAdmissibility="True"
+ CheckPurity="False"
+ />
+ <Config Name="Release"
+ AllowUnsafeBlocks="false"
+ BaseAddress="285212672"
+ CheckForOverflowUnderflow="false"
+ ConfigurationOverrideFile=""
+ DefineConstants="TRACE"
+ DocumentationFile=""
+ DebugSymbols="false"
+ FileAlignment="4096"
+ IncrementalBuild="false"
+ Optimize="true"
+ OutputPath="bin\release"
+ RegisterForComInterop="false"
+ RemoveIntegerChecks="false"
+ TreatWarningsAsErrors="false"
+ WarningLevel="4"
+ CheckContractAdmissibility="True"
+ CheckPurity="False"
+ />
+ </Settings>
+ <References>
+ <Reference Name="System"
+ AssemblyName="System"
+ Private="false"
+ />
+ <Reference Name="System.Data"
+ AssemblyName="System.Data"
+ Private="false"
+ />
+ <Reference Name="System.Xml"
+ AssemblyName="System.Xml"
+ Private="false"
+ />
+ <Reference Name="Basetypes"
+ Project="{0C692837-77EC-415F-BF04-395E3ED06E9A}"
+ Private="true"
+ />
+ <Reference Name="Core"
+ Project="{47BC34F1-A173-40BE-84C2-9332B4418387}"
+ Private="true"
+ />
+ <Reference Name="VCExpr"
+ Project="{CF42B700-10AA-4DA9-8992-48A800251C11}"
+ Private="true"
+ />
+ <Reference Name="VCGeneration"
+ Project="{F65666DE-FB56-457C-8782-09BE243450FC}"
+ Private="true"
+ />
+ <Reference Name="Mscorlib.Contracts"
+ AssemblyName="Mscorlib.Contracts"
+ Private="false"
+ HintPath="../../../Binaries/Mscorlib.Contracts.dll"
+ />
+ <Reference Name="Simplify"
+ Project="{F75666DE-FB56-457C-8782-09BE243450FC}"
+ Private="true"
+ />
+ </References>
+ </Build>
+ <Files>
+ <Include>
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="ProverInterface.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="SMTLibLineariser.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="TypeDeclCollector.ssc"
+ />
+ </Include>
+ </Files>
+ </XEN>
+</VisualStudioProject> \ No newline at end of file
diff --git a/Source/Provers/SMTLib/SMTLibLineariser.ssc b/Source/Provers/SMTLib/SMTLibLineariser.ssc
new file mode 100644
index 00000000..cbf69107
--- /dev/null
+++ b/Source/Provers/SMTLib/SMTLibLineariser.ssc
@@ -0,0 +1,635 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Text;
+using System.IO;
+using System.Collections;
+using System.Collections.Generic;
+using Microsoft.Contracts;
+using Microsoft.Basetypes;
+using Microsoft.Boogie.VCExprAST;
+
+// Method to turn VCExprs into strings that can be fed into SMT
+// solvers. This is currently quite similar to the
+// SimplifyLikeLineariser (but the code is independent)
+
+namespace Microsoft.Boogie.SMTLib
+{
+
+ // Options for the linearisation
+ public class LineariserOptions {
+
+ public readonly bool AsTerm;
+ public LineariserOptions! SetAsTerm(bool newVal) {
+ if (newVal)
+ return DefaultTerm;
+ else
+ return Default;
+ }
+
+ internal LineariserOptions(bool asTerm) {
+ this.AsTerm = asTerm;
+ }
+
+ public static readonly LineariserOptions! Default = new LineariserOptions (false);
+ internal static readonly LineariserOptions! DefaultTerm = new LineariserOptions (true);
+ }
+
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+
+ // Lineariser for expressions. The result (bool) is currently not used for anything
+ public class SMTLibExprLineariser : IVCExprVisitor<bool, LineariserOptions!> {
+
+ public static string! ToString(VCExpr! e, UniqueNamer! namer) {
+ StringWriter sw = new StringWriter();
+ SMTLibExprLineariser! lin = new SMTLibExprLineariser (sw, namer);
+ lin.Linearise(e, LineariserOptions.Default);
+ return (!)sw.ToString();
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+
+ private readonly TextWriter! wr;
+ private SMTLibOpLineariser OpLinObject = null;
+ private IVCExprOpVisitor<bool, LineariserOptions!>! OpLineariser { get {
+ if (OpLinObject == null)
+ OpLinObject = new SMTLibOpLineariser (this, wr);
+ return OpLinObject;
+ } }
+
+ internal readonly UniqueNamer! Namer;
+
+ public SMTLibExprLineariser(TextWriter! wr, UniqueNamer! namer) {
+ this.wr = wr;
+ this.Namer = namer;
+ }
+
+ public void Linearise(VCExpr! expr, LineariserOptions! options) {
+ expr.Accept<bool, LineariserOptions!>(this, options);
+ }
+
+ public void LineariseAsTerm(VCExpr! expr, LineariserOptions! options) {
+ Linearise(expr, options.SetAsTerm(true));
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////
+
+ internal static string! TypeToString(Type! t) {
+ if (t.IsBool)
+ return "TermBool";
+ else if (t.IsInt)
+ return "Int";
+ else if (t.IsBv)
+ assert false; // bitvectors are currently not handled for SMT-Lib solvers
+ else {
+ // at this point, only the types U, T should be left
+ System.IO.StringWriter buffer = new System.IO.StringWriter();
+ using (TokenTextWriter stream = new TokenTextWriter("<buffer>", buffer, false)) {
+ t.Emit(stream);
+ }
+ return "boogie" + buffer.ToString();
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////
+
+ public static string! MakeIdPrintable(string! s) {
+ // make sure that no keywords are used as identifiers
+ switch(s) {
+ case andName:
+ case orName:
+ case notName:
+ case impliesName:
+ case iffName:
+ case eqName:
+ case distinctName:
+ case TRUEName:
+ case FALSEName:
+ case "Array":
+ s = "nonkeyword_" + s;
+ break;
+ }
+
+ string! newS = "";
+ foreach (char ch in s) {
+ if (Char.IsLetterOrDigit(ch) || ch == '.' || ch == '\'' || ch == '_')
+ newS = newS + ch;
+ else
+ // replace everything else with a .
+ newS = newS + '.';
+ }
+
+ // ensure that the first character is not . or _ (some SMT-solvers do
+ // not like that, e.g., yices and cvc3)
+ if (newS[0] == '.' || newS[0] == '_')
+ newS = "x" + newS;
+
+ return newS;
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////
+
+ /// <summary>
+ /// The name for logical conjunction in Simplify
+ /// </summary>
+ internal const string! andName = "and"; // conjunction
+ internal const string! orName = "or"; // disjunction
+ internal const string! notName = "not"; // negation
+ internal const string! impliesName = "implies"; // implication
+ internal const string! iffName = "iff"; // logical equivalence
+ internal const string! eqName = "="; // equality
+ internal const string! lessName = "<";
+ internal const string! greaterName = ">";
+ internal const string! atmostName = "<=";
+ internal const string! atleastName = ">=";
+ internal const string! TRUEName = "true"; // nullary predicate that is always true
+ internal const string! FALSEName = "false"; // nullary predicate that is always false
+ internal const string! subtypeName = "UOrdering2";
+ internal const string! subtypeArgsName = "UOrdering3";
+
+ internal const string! distinctName = "distinct";
+
+ internal const string! boolTrueName = "boolTrue";
+ internal const string! boolFalseName = "boolFalse";
+ internal const string! boolAndName = "boolAnd";
+ internal const string! boolOrName = "boolOr";
+ internal const string! boolNotName = "boolNot";
+ internal const string! boolIffName = "boolIff";
+ internal const string! boolImpliesName = "boolImplies";
+ internal const string! termUEqual = "UEqual";
+ internal const string! termTEqual = "TEqual";
+ internal const string! termIntEqual = "IntEqual";
+ internal const string! termLessName = "intLess";
+ internal const string! termGreaterName = "intGreater";
+ internal const string! termAtmostName = "intAtMost";
+ internal const string! termAtleastName = "intAtLeast";
+ internal const string! intAddName = "+";
+ internal const string! intSubName = "-";
+ internal const string! intMulName = "*";
+ internal const string! intDivName = "boogieIntDiv";
+ internal const string! intModName = "boogieIntMod";
+
+ internal void AssertAsTerm(string! x, LineariserOptions! options) {
+ if (!options.AsTerm)
+ System.Diagnostics.Debug.Fail("One should never write " + x + " as a formula!");
+ }
+
+ internal void AssertAsFormula(string! x, LineariserOptions! options) {
+ if (options.AsTerm)
+ System.Diagnostics.Debug.Fail("One should never write " + x + " as a term!");
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////
+
+ public bool Visit(VCExprLiteral! node, LineariserOptions! options) {
+ if (options.AsTerm) {
+
+ if (node == VCExpressionGenerator.True)
+ wr.Write("{0}", boolTrueName);
+ else if (node == VCExpressionGenerator.False)
+ wr.Write("{0}", boolFalseName);
+ else if (node is VCExprIntLit) {
+ // some SMT-solvers do not understand negative literals
+ // (e.g., yices)
+ BigNum lit = ((VCExprIntLit)node).Val;
+ if (lit.IsNegative)
+ wr.Write("({0} 0 {1})", intSubName, lit.Abs);
+ else
+ wr.Write(lit);
+ } else
+ assert false;
+
+ } else {
+
+ if (node == VCExpressionGenerator.True)
+ wr.Write("{0}", TRUEName);
+ else if (node == VCExpressionGenerator.False)
+ wr.Write("{0}", FALSEName);
+ else if (node is VCExprIntLit) {
+ System.Diagnostics.Debug.Fail("One should never write IntLit as a predicate!");
+ } else
+ assert false;
+
+ }
+
+ return true;
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////
+
+ public bool Visit(VCExprNAry! node, LineariserOptions! options) {
+ VCExprOp! op = node.Op;
+
+ if (!options.AsTerm &&
+ (op.Equals(VCExpressionGenerator.AndOp) ||
+ op.Equals(VCExpressionGenerator.OrOp))) {
+ // handle these operators without recursion
+
+ wr.Write("({0}",
+ op.Equals(VCExpressionGenerator.AndOp) ? andName : orName);
+ IEnumerator! enumerator = new VCExprNAryUniformOpEnumerator (node);
+ while (enumerator.MoveNext()) {
+ VCExprNAry naryExpr = enumerator.Current as VCExprNAry;
+ if (naryExpr == null || !naryExpr.Op.Equals(op)) {
+ wr.Write(" ");
+ Linearise((VCExpr!)enumerator.Current, options);
+ }
+ }
+
+ wr.Write(")");
+
+ return true;
+ }
+
+ return node.Accept<bool, LineariserOptions!>(OpLineariser, options);
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////
+
+ public bool Visit(VCExprVar! node, LineariserOptions! options) {
+ string! printedName = Namer.GetName(node, MakeIdPrintable(node.Name));
+
+ if (options.AsTerm ||
+ // formula variables are easy to identify in SMT-Lib
+ printedName[0] == '$')
+ wr.Write("{0}", printedName);
+ else
+ wr.Write("({0} {1} {2})", eqName, printedName, boolTrueName);
+
+ return true;
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////
+
+ public bool Visit(VCExprQuantifier! node, LineariserOptions! options) {
+ AssertAsFormula(node.Quan.ToString(), options);
+ assert node.TypeParameters.Count == 0;
+
+ Namer.PushScope(); try {
+
+ string! kind = node.Quan == Quantifier.ALL ? "forall" : "exists";
+ wr.Write("({0} ", kind);
+
+ for (int i = 0; i < node.BoundVars.Count; i++)
+ {
+ VCExprVar! var = node.BoundVars[i];
+ // ensure that the variable name starts with ?
+ string! printedName = Namer.GetLocalName(var, "?" + MakeIdPrintable(var.Name));
+ assert printedName[0] == '?';
+ wr.Write("({0} {1}) ", printedName, TypeToString(var.Type));
+ }
+
+ /* if (options.QuantifierIds) {
+ // only needed for Z3
+ VCQuantifierInfos! infos = node.Infos;
+ if (infos.qid != null) {
+ wr.Write("(QID ");
+ wr.Write(infos.qid);
+ wr.Write(") ");
+ }
+ if (0 <= infos.uniqueId) {
+ wr.Write("(SKOLEMID ");
+ wr.Write(infos.uniqueId);
+ wr.Write(") ");
+ }
+ } */
+
+ Linearise(node.Body, options);
+
+ WriteTriggers(node.Triggers, options);
+ wr.Write(")");
+
+ return true;
+
+ } finally {
+ Namer.PopScope();
+ }
+ }
+
+ private void WriteTriggers(List<VCTrigger!>! triggers, LineariserOptions! options) {
+ // first, count how many neg/pos triggers there are
+ int negTriggers = 0;
+ int posTriggers = 0;
+ foreach (VCTrigger! vcTrig in triggers) {
+ if (vcTrig.Pos) {
+ posTriggers++;
+ } else {
+ negTriggers++;
+ }
+ }
+
+ if (posTriggers > 0) {
+ foreach (VCTrigger! vcTrig in triggers) {
+ if (vcTrig.Pos) {
+ wr.Write(" :pat {");
+ foreach (VCExpr! e in vcTrig.Exprs) {
+ wr.Write(" ");
+ LineariseAsTerm(e, options);
+ }
+ wr.Write(" } ");
+ }
+ }
+ } else if (negTriggers > 0) {
+ // if also positive triggers are given, the SMT solver (at least Z3)
+ // will ignore the negative patterns and output a warning. Therefore
+ // we never specify both negative and positive triggers
+ foreach (VCTrigger! vcTrig in triggers) {
+ if (!vcTrig.Pos) {
+ wr.Write(" :nopat { ");
+ assert vcTrig.Exprs.Count == 1;
+ wr.Write(" ");
+ LineariseAsTerm(vcTrig.Exprs[0], options);
+ wr.Write(" } ");
+ }
+ }
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////
+
+ public bool Visit(VCExprLet! node, LineariserOptions! options) {
+ Namer.PushScope(); try {
+
+ foreach (VCExprLetBinding! b in node) {
+ bool formula = b.V.Type.IsBool;
+
+ wr.Write("({0} (", formula ? "flet" : "let");
+ string! printedName = Namer.GetLocalName(b.V, "$" + MakeIdPrintable(b.V.Name));
+ assert printedName[0] == '$';
+
+ wr.Write("{0} ", printedName);
+ Linearise(b.E, options.SetAsTerm(!formula));
+ wr.Write(") ");
+ }
+ Linearise(node.Body, options);
+
+ for (int i = 0; i < node.Length; ++i)
+ wr.Write(")");
+
+ return true;
+
+ } finally {
+ Namer.PopScope();
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////
+
+ // Lineariser for operator terms. The result (bool) is currently not used for anything
+ internal class SMTLibOpLineariser : IVCExprOpVisitor<bool, LineariserOptions!> {
+ private readonly SMTLibExprLineariser! ExprLineariser;
+ private readonly TextWriter! wr;
+
+ public SMTLibOpLineariser(SMTLibExprLineariser! ExprLineariser, TextWriter! wr) {
+ this.ExprLineariser = ExprLineariser;
+ this.wr = wr;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////
+
+ private void WriteApplication(string! op, IEnumerable<VCExpr!>! args,
+ LineariserOptions! options,
+ bool argsAsTerms) {
+ WriteApplication(op, op, args, options, argsAsTerms);
+ }
+
+ private void WriteApplication(string! op, IEnumerable<VCExpr!>! args,
+ LineariserOptions! options) {
+ WriteApplication(op, op, args, options, options.AsTerm);
+ }
+
+ private void WriteTermApplication(string! op, IEnumerable<VCExpr!>! args,
+ LineariserOptions! options) {
+ ExprLineariser.AssertAsTerm(op, options);
+ WriteApplication(op, op, args, options, options.AsTerm);
+ }
+
+ private void WriteApplication(string! termOp, string! predOp,
+ IEnumerable<VCExpr!>! args, LineariserOptions! options) {
+ WriteApplication(termOp, predOp, args, options, options.AsTerm);
+ }
+
+ private void WriteApplication(string! termOp, string! predOp,
+ IEnumerable<VCExpr!>! args, LineariserOptions! options,
+ // change the AsTerm option for the arguments?
+ bool argsAsTerms) {
+ string! opName = options.AsTerm ? termOp : predOp;
+ LineariserOptions! newOptions = options.SetAsTerm(argsAsTerms);
+
+ bool hasArgs = false;
+ foreach (VCExpr! e in args) {
+ if (!hasArgs)
+ wr.Write("({0}", opName);
+ wr.Write(" ");
+ ExprLineariser.Linearise(e, newOptions);
+ hasArgs = true;
+ }
+
+ if (hasArgs)
+ wr.Write(")");
+ else
+ wr.Write("{0}", opName);
+ }
+
+ // write an application that can only be a term.
+ // if the expression is supposed to be printed as a formula,
+ // it is turned into an equation (EQ (f args) |@true|)
+ private void WriteApplicationTermOnly(string! termOp,
+ IEnumerable<VCExpr!>! args, LineariserOptions! options) {
+ if (!options.AsTerm)
+ // Write: (EQ (f args) |@true|)
+ // where "args" are written as terms
+ wr.Write("({0} ", eqName);
+
+ WriteApplication(termOp, args, options, true);
+
+ if (!options.AsTerm)
+ wr.Write(" {0})", boolTrueName);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////
+
+ public bool VisitNotOp (VCExprNAry! node, LineariserOptions! options) {
+ WriteApplication(boolNotName, notName, node, options); // arguments can be both terms and formulas
+ return true;
+ }
+
+ private bool PrintEq(VCExprNAry! node, LineariserOptions! options) {
+ if (options.AsTerm) {
+ // use equality on terms, also if the arguments have type bool
+ assert node[0].Type.Equals(node[1].Type);
+ if (node[0].Type.IsBool) {
+ WriteApplication(boolIffName, node, options);
+ } else if (node[0].Type.IsInt) {
+ WriteApplication(termIntEqual, node, options);
+ } else {
+ // TODO: make this less hackish
+ CtorType t = node[0].Type as CtorType;
+ if (t != null && t.Decl.Name.Equals("U")) {
+ WriteApplication(termUEqual, node, options);
+ } else if (t != null && t.Decl.Name.Equals("T")) {
+ WriteApplication(termTEqual, node, options);
+ } else {
+ assert false; // unknown type
+ }
+ }
+ } else {
+ if (node[0].Type.IsBool) {
+ assert node[1].Type.IsBool;
+ // use equivalence
+ WriteApplication(iffName, node, options);
+ } else {
+ // use equality and write the arguments as terms
+ WriteApplication(eqName, node, options, true);
+ }
+ }
+
+ return true;
+ }
+
+ public bool VisitEqOp (VCExprNAry! node, LineariserOptions! options) {
+ return PrintEq(node, options);
+ }
+
+ public bool VisitNeqOp (VCExprNAry! node, LineariserOptions! options) {
+ wr.Write("({0} ", options.AsTerm ? boolNotName : notName);
+ PrintEq(node, options);
+ wr.Write(")");
+ return true;
+ }
+
+ public bool VisitAndOp (VCExprNAry! node, LineariserOptions! options) {
+ assert options.AsTerm;
+ WriteApplication(boolAndName, andName, node, options);
+ return true;
+ }
+
+ public bool VisitOrOp (VCExprNAry! node, LineariserOptions! options) {
+ assert options.AsTerm;
+ WriteApplication(boolOrName, orName, node, options);
+ return true;
+ }
+
+ public bool VisitImpliesOp (VCExprNAry! node, LineariserOptions! options) {
+ WriteApplication(boolImpliesName, impliesName, node, options);
+ return true;
+ }
+
+ public bool VisitDistinctOp (VCExprNAry! node, LineariserOptions! options) {
+ ExprLineariser.AssertAsFormula(distinctName, options);
+
+ if (node.Length < 2) {
+ ExprLineariser.Linearise(VCExpressionGenerator.True, options);
+ } else {
+ wr.Write("({0}", distinctName);
+ foreach (VCExpr! e in node) {
+ wr.Write(" ");
+ ExprLineariser.LineariseAsTerm(e, options);
+ }
+ wr.Write(")");
+ }
+
+ return true;
+ }
+
+ public bool VisitLabelOp (VCExprNAry! node, LineariserOptions! options) {
+ // VCExprLabelOp! op = (VCExprLabelOp)node.Op;
+ // TODO
+ // wr.Write(String.Format("({0} |{1}| ", op.pos ? "LBLPOS" : "LBLNEG", op.label));
+ ExprLineariser.Linearise(node[0], options);
+ // wr.Write(")");
+ return true;
+ }
+
+ public bool VisitSelectOp (VCExprNAry! node, LineariserOptions! options) {
+ assert false; // should not occur in the output
+ }
+
+ public bool VisitStoreOp (VCExprNAry! node, LineariserOptions! options) {
+ assert false; // should not occur in the output
+ }
+
+ public bool VisitBvOp (VCExprNAry! node, LineariserOptions! options) {
+ assert false; // TODO
+ }
+
+ public bool VisitBvExtractOp(VCExprNAry! node, LineariserOptions! options) {
+ assert false; // TODO
+ }
+
+ public bool VisitBvConcatOp (VCExprNAry! node, LineariserOptions! options) {
+ assert false; // TODO
+ }
+
+ public bool VisitAddOp (VCExprNAry! node, LineariserOptions! options) {
+ WriteTermApplication(intAddName, node, options);
+ return true;
+ }
+
+ public bool VisitSubOp (VCExprNAry! node, LineariserOptions! options) {
+ WriteTermApplication(intSubName, node, options);
+ return true;
+ }
+
+ public bool VisitMulOp (VCExprNAry! node, LineariserOptions! options) {
+ WriteTermApplication(intMulName, node, options);
+ return true;
+ }
+
+ public bool VisitDivOp (VCExprNAry! node, LineariserOptions! options) {
+ WriteTermApplication(intDivName, node, options);
+ return true;
+ }
+
+ public bool VisitModOp (VCExprNAry! node, LineariserOptions! options) {
+ WriteTermApplication(intModName, node, options);
+ return true;
+ }
+
+ public bool VisitLtOp (VCExprNAry! node, LineariserOptions! options) {
+ WriteApplication(termLessName, lessName, node, options, true); // arguments are always terms
+ return true;
+ }
+
+ public bool VisitLeOp (VCExprNAry! node, LineariserOptions! options) {
+ WriteApplication(termAtmostName, atmostName, node, options, true); // arguments are always terms
+ return true;
+ }
+
+ public bool VisitGtOp (VCExprNAry! node, LineariserOptions! options) {
+ WriteApplication(termGreaterName, greaterName, node, options, true); // arguments are always terms
+ return true;
+ }
+
+ public bool VisitGeOp (VCExprNAry! node, LineariserOptions! options) {
+ WriteApplication(termAtleastName, atleastName, node, options, true); // arguments are always terms
+ return true;
+ }
+
+ public bool VisitSubtypeOp (VCExprNAry! node, LineariserOptions! options) {
+ WriteApplication(subtypeName, node, options, true); // arguments are always terms
+ return true;
+ }
+
+ public bool VisitSubtype3Op (VCExprNAry! node, LineariserOptions! options) {
+ WriteApplication(subtypeArgsName, node, options, true); // arguments are always terms
+ return true;
+ }
+
+ public bool VisitBoogieFunctionOp (VCExprNAry! node, LineariserOptions! options) {
+ VCExprBoogieFunctionOp! op = (VCExprBoogieFunctionOp)node.Op;
+ string! printedName = ExprLineariser.Namer.GetName(op.Func, MakeIdPrintable(op.Func.Name));
+
+ // arguments are always terms
+ WriteApplicationTermOnly(printedName, node, options);
+ return true;
+ }
+
+ }
+ }
+
+}
diff --git a/Source/Provers/SMTLib/TypeDeclCollector.ssc b/Source/Provers/SMTLib/TypeDeclCollector.ssc
new file mode 100644
index 00000000..08c0e6a3
--- /dev/null
+++ b/Source/Provers/SMTLib/TypeDeclCollector.ssc
@@ -0,0 +1,107 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using Microsoft.Contracts;
+using Microsoft.Boogie.VCExprAST;
+
+namespace Microsoft.Boogie.SMTLib
+{
+ // Visitor for collecting the occurring function symbols in a VCExpr,
+ // and for creating the corresponding declarations
+
+ public class TypeDeclCollector : BoundVarTraversingVCExprVisitor<bool, bool> {
+
+ private readonly UniqueNamer! Namer;
+
+ public TypeDeclCollector(UniqueNamer! namer) {
+ this.Namer = namer;
+ }
+
+ // not used
+ protected override bool StandardResult(VCExpr! node, bool arg) {
+ return true;
+ }
+
+ private readonly List<string!>! AllDecls = new List<string!> ();
+ private readonly List<string!>! IncDecls = new List<string!> ();
+
+ private readonly IDictionary<Function!, bool>! KnownFunctions =
+ new Dictionary<Function!, bool> ();
+ private readonly IDictionary<VCExprVar!, bool>! KnownVariables =
+ new Dictionary<VCExprVar!, bool> ();
+
+ public List<string!>! AllDeclarations { get {
+ List<string!>! res = new List<string!> ();
+ res.AddRange(AllDecls);
+ return res;
+ } }
+
+ public List<string!>! GetNewDeclarations() {
+ List<string!>! res = new List<string!> ();
+ res.AddRange(IncDecls);
+ IncDecls.Clear();
+ return res;
+ }
+
+ private void AddDeclaration(string! decl) {
+ AllDecls.Add(decl);
+ IncDecls.Add(decl);
+ }
+
+ public void Collect(VCExpr! expr) {
+ Traverse(expr, true);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ private static string! TypeToString(Type! t) {
+ return SMTLibExprLineariser.TypeToString(t);
+ }
+
+ public override bool Visit(VCExprNAry! node, bool arg) {
+ // there are a couple of cases where operators have to be
+ // registered by generating appropriate statements
+
+ VCExprBoogieFunctionOp op = node.Op as VCExprBoogieFunctionOp;
+ if (op != null && !KnownFunctions.ContainsKey(op.Func)) {
+ Function! f = op.Func;
+ string! printedName = Namer.GetName(f, SMTLibExprLineariser.MakeIdPrintable(f.Name));
+ string! decl = ":extrafuns ((" + printedName;
+
+ foreach (Variable! v in f.InParams) {
+ decl += " " + TypeToString(v.TypedIdent.Type);
+ }
+ assert f.OutParams.Length == 1;
+ foreach (Variable! v in f.OutParams) {
+ decl += " " + TypeToString(v.TypedIdent.Type);
+ }
+
+ decl += "))";
+
+ AddDeclaration(decl);
+ KnownFunctions.Add(f, true);
+ }
+
+ return base.Visit(node, arg);
+ }
+
+ public override bool Visit(VCExprVar! node, bool arg) {
+ if (!BoundTermVars.Contains(node) && !KnownVariables.ContainsKey(node)) {
+ string! printedName = Namer.GetName(node, SMTLibExprLineariser.MakeIdPrintable(node.Name));
+ string! decl =
+ ":extrafuns ((" + printedName + " " + TypeToString(node.Type) + "))";
+ AddDeclaration(decl);
+ KnownVariables.Add(node, true);
+ }
+
+ return base.Visit(node, arg);
+ }
+ }
+
+} \ No newline at end of file
diff --git a/Source/Provers/Simplify/Let2ImpliesVisitor.ssc b/Source/Provers/Simplify/Let2ImpliesVisitor.ssc
new file mode 100644
index 00000000..8443d950
--- /dev/null
+++ b/Source/Provers/Simplify/Let2ImpliesVisitor.ssc
@@ -0,0 +1,183 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using Microsoft.Contracts;
+using Microsoft.Boogie.VCExprAST;
+
+namespace Microsoft.Boogie.Simplify
+{
+ // Simplify does not understand the LET operator, so we have to replace
+ // it with implications (previously, this was done in the VCExprGenerator), or
+ // we have to apply the let as a substitution (in the case of terms)
+
+ // This visitor expects that let-bindings are sorted, so that bound
+ // variables only occur after their declaration
+
+ public class Let2ImpliesMutator : SubstitutingVCExprVisitor {
+
+ public Let2ImpliesMutator(VCExpressionGenerator! gen) {
+ base(gen);
+ }
+
+ public VCExpr! Mutate(VCExpr! expr) {
+ return Mutate(expr, new VCExprSubstitution ());
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ private int polarity = 1; // 1 for positive, -1 for negative, 0 for both
+
+ // we also track which variables occur in positive, negative, or
+ // in both positions (to decide whether implications or equations
+ // have to be used to define such a variable)
+ private enum OccurrenceTypes { None, Pos, Neg, PosNeg };
+ private OccurrenceTypes Union(OccurrenceTypes o1, OccurrenceTypes o2) {
+ switch(o1) {
+ case OccurrenceTypes.None: return o2;
+ case OccurrenceTypes.Pos:
+ switch(o2) {
+ case OccurrenceTypes.None:
+ case OccurrenceTypes.Pos:
+ return OccurrenceTypes.Pos;
+ default:
+ return OccurrenceTypes.PosNeg;
+ }
+ case OccurrenceTypes.Neg:
+ switch(o2) {
+ case OccurrenceTypes.None:
+ case OccurrenceTypes.Neg:
+ return OccurrenceTypes.Neg;
+ default:
+ return OccurrenceTypes.PosNeg;
+ }
+ default:
+ return OccurrenceTypes.PosNeg;
+ }
+ }
+
+ private IDictionary<VCExprVar!, OccurrenceTypes>! VarOccurrences =
+ new Dictionary<VCExprVar!, OccurrenceTypes>();
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ public override VCExpr! Visit(VCExprVar! node,
+ VCExprSubstitution! substitution) {
+ VCExpr! res = base.Visit(node, substitution);
+
+ VCExprVar resAsVar = res as VCExprVar;
+ if (resAsVar != null) {
+ OccurrenceTypes occ;
+ if (polarity > 0)
+ occ = OccurrenceTypes.Pos;
+ else if (polarity < 0)
+ occ = OccurrenceTypes.Neg;
+ else
+ occ = OccurrenceTypes.PosNeg;
+
+ OccurrenceTypes oldOcc;
+ if (VarOccurrences.TryGetValue(resAsVar, out oldOcc))
+ occ = Union(occ, oldOcc);
+ VarOccurrences[resAsVar] = occ;
+ }
+
+ return res;
+ }
+
+ public override VCExpr! Visit(VCExprNAry! node,
+ VCExprSubstitution! substitution) {
+ // track the polarity to ensure that no implications are introduced
+ // in negative positions
+ // UGLY: the code for tracking polarities should be factored out
+ // (similar code is used in the TypeEraser)
+
+ VCExpr! res;
+ if (node.Op.Equals(VCExpressionGenerator.NotOp)) {
+ polarity = -polarity;
+ res = base.Visit(node, substitution);
+ polarity = -polarity;
+ } else if (node.Op.Equals(VCExpressionGenerator.ImpliesOp)) {
+ polarity = -polarity;
+ VCExpr! newArg0 = Mutate(node[0], substitution);
+ polarity = -polarity;
+ VCExpr! newArg1 = Mutate(node[1], substitution);
+
+ res = Gen.Implies(newArg0, newArg1);
+ } else if (!node.Op.Equals(VCExpressionGenerator.AndOp) &&
+ !node.Op.Equals(VCExpressionGenerator.OrOp) &&
+ !(node.Op is VCExprLabelOp)) {
+ // standard is to set the polarity to 0 (fits most operators)
+ int oldPolarity = polarity;
+ polarity = 0;
+ res = base.Visit(node, substitution);
+ polarity = oldPolarity;
+ } else {
+ res = base.Visit(node, substitution);
+ }
+
+ return res;
+ }
+
+ public override VCExpr! Visit(VCExprLet! originalNode,
+ VCExprSubstitution! substitution) {
+ // first sort the bindings to be able to apply substitutions
+ LetBindingSorter! letSorter = new LetBindingSorter (Gen);
+ VCExpr! newNode = letSorter.Mutate(originalNode, true);
+ VCExprLet node = newNode as VCExprLet;
+
+ if (node == null)
+ // it can happen that the complete let-expressions gets eliminated by the
+ // sorter, which also checks whether let-bindings are actually used
+ return newNode;
+
+ substitution.PushScope(); try {
+
+ // the bindings that remain and that are later handled using an implication
+ List<VCExprLetBinding!>! bindings = new List<VCExprLetBinding!> ();
+
+ foreach (VCExprLetBinding! binding in node) {
+ // in all cases we apply the substitution up to this point
+ // to the bound formula
+ VCExpr! newE = Mutate(binding.E, substitution);
+
+ if (binding.V.Type.IsBool) {
+ // a bound formula is handled using an implication; we introduce
+ // a fresh variable to avoid clashes
+ assert polarity > 0;
+
+ VCExprVar! newVar = Gen.Variable(binding.V.Name, Type.Bool);
+ substitution[binding.V] = newVar;
+
+ bindings.Add(Gen.LetBinding(newVar, newE));
+ } else {
+ // a bound term is substituted
+ substitution[binding.V] = newE;
+ }
+ }
+
+ VCExpr! newBody = Mutate(node.Body, substitution);
+
+ // Depending on the places where the variable occurs, we would
+ // have to introduce implications or equations to define the
+ // bound variables. For the time being, we just assert that all
+ // occurrences are positive
+ foreach (VCExprLetBinding! b in bindings) {
+ OccurrenceTypes occ;
+ if (VarOccurrences.TryGetValue(b.V, out occ))
+ assert occ == OccurrenceTypes.None || occ == OccurrenceTypes.Pos;
+ }
+
+ return Gen.Implies(Gen.AsImplications(bindings), newBody);
+
+ } finally {
+ substitution.PopScope();
+ }
+ }
+ }
+
+} \ No newline at end of file
diff --git a/Source/Provers/Simplify/Prover.ssc b/Source/Provers/Simplify/Prover.ssc
new file mode 100644
index 00000000..f89d6a42
--- /dev/null
+++ b/Source/Provers/Simplify/Prover.ssc
@@ -0,0 +1,606 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.IO;
+using System.Diagnostics;
+using System.Collections;
+using System.Collections.Generic;
+using util;
+using Microsoft.Contracts;
+using Microsoft.Boogie;
+
+// Simplified interface to an external prover like Simplify or the z3 process, taken from Bird.
+namespace Microsoft.Boogie.Simplify
+{
+ /// <summary>
+ /// An interface to Simplify theorem prover.
+ /// </summary>
+ public abstract class ProverProcess
+ {
+ [Rep]
+ protected readonly Process! simplify;
+
+ [Rep] readonly TextReader! fromSimplify;
+ [Rep] readonly TextWriter! toSimplify;
+ [Rep] readonly TextReader! fromStdError;
+
+ protected bool readTimedOut;
+
+ int nFormulasChecked = 0;
+ public int NumFormulasChecked {
+ get { return nFormulasChecked; }
+ }
+
+ // Note: In the Everett build (.NET framework < 2.0), Process.PeakVirtualMemorySize64
+ // is not defined, but rather a version that returns an 'int' rather than a 'long'.
+ public long PeakVirtualMemorySize {
+ [NoDefaultContract]
+ get
+ requires IsPeerConsistent;
+ modifies this.*;
+ {
+ expose (this) {
+ simplify.Refresh();
+#if WHIDBEY
+ return simplify.PeakVirtualMemorySize64;
+#else
+ return simplify.PeakPagedMemorySize64;
+#endif
+ }
+ }
+ }
+
+ public bool HasExited {
+ get { return simplify.HasExited; }
+ }
+
+ public ProverProcess(ProcessStartInfo! psi, string! proverPath)
+ { // throws ProverException
+ try
+ {
+ Process simplify = Process.Start(psi);
+ this.simplify = simplify;
+ fromSimplify = simplify.StandardOutput;
+ toSimplify = simplify.StandardInput;
+ fromStdError = simplify.StandardError;
+ }
+ catch (System.ComponentModel.Win32Exception e)
+ {
+ throw new ProverException(string.Format("Unable to start the process {0}: {1}", proverPath, e.Message));
+ }
+ // base();
+ }
+
+ public abstract string! OptionComments();
+ [Pure(false)]
+ public virtual IEnumerable<string!>! ParameterSettings { get { yield break; } }
+
+ public void Close()
+ modifies this.*;
+ {
+ expose (this) {
+ toSimplify.Flush();
+ if (this.simplify != null)
+ {
+ if (!simplify.HasExited)
+ {
+ this.Kill();
+ }
+ simplify.Close();
+ }
+ }
+ }
+
+ [NoDefaultContract] // this method assumes nothing about the object, other than that it has been constructed (which means simplify!=null)
+ [Verify(false)] // The call simplify.Kill will require simplify.IsPeerConsistent, but since we don't know the state of "this" and "simplify", we cannot afford the run-time check that an assume statement here would impose
+ public void Kill() {
+ try {
+ if (CommandLineOptions.Clo.ProverShutdownLimit > 0) {
+ toSimplify.Close();
+ for (int i = 0; !simplify.HasExited && i <= CommandLineOptions.Clo.ProverShutdownLimit * 1000; i += 100)
+ {
+ System.Threading.Thread.Sleep(100);
+ }
+ }
+ if (!simplify.HasExited) { simplify.Kill(); }
+ } catch (InvalidOperationException) { /* already exited */ }
+ catch (System.ComponentModel.Win32Exception) { /* already exiting */ }
+ }
+
+ public static void ReportWarning(string! w)
+ modifies Console.Out.*, Console.Error.*;
+ {
+ switch (CommandLineOptions.Clo.PrintProverWarnings) {
+ case CommandLineOptions.ProverWarnings.None:
+ break;
+ case CommandLineOptions.ProverWarnings.Stdout:
+ Console.WriteLine("Prover warning: " + w);
+ break;
+ case CommandLineOptions.ProverWarnings.Stderr:
+ Console.Error.WriteLine("Prover warning: " + w);
+ break;
+ default:
+ assume false; // unexpected case
+ }
+ }
+
+ public virtual void AddAxioms(string! s)
+ modifies this.*;
+ modifies Console.Out.*, Console.Error.*;
+ throws UnexpectedProverOutputException;
+ {
+ expose (this) {
+ toSimplify.Write("(BG_PUSH ");
+ toSimplify.Write(s);
+ toSimplify.WriteLine(")");
+ }
+ }
+
+ public virtual void Feed(string! s, int statementCount)
+ modifies this.*;
+ modifies Console.Out.*, Console.Error.*;
+ throws UnexpectedProverOutputException;
+ {
+ expose (this) {
+ toSimplify.Write(s);
+ }
+ }
+
+ public virtual void PopAxioms()
+ modifies this.*;
+ modifies Console.Out.*, Console.Error.*;
+ throws UnexpectedProverOutputException;
+ {
+ expose (this) {
+ toSimplify.WriteLine("(BG_POP)");
+ }
+ }
+
+ public void ToFlush()
+ modifies this.*;
+ {
+ expose (this) {
+ toSimplify.Flush();
+ }
+ }
+
+ public enum ProverOutcome { Valid, NotValid, TimeOut, OutOfMemory, Inconclusive }
+
+ /// <summary>
+ /// Passes the formula to Simplify.
+ /// </summary>
+ public void BeginCheck(string! descriptiveName, string! formula)
+ modifies this.*;
+ modifies Console.Out.*, Console.Error.*;
+ {
+ DoBeginCheck(descriptiveName, formula);
+ nFormulasChecked++;
+ }
+
+ /// <summary>
+ /// Reports the outcome of formula checking. If the outcome is Invalid,
+ /// then the "handler" is invoked with each counterexample.
+ /// </summary>
+ public abstract ProverOutcome CheckOutcome(Microsoft.Boogie.ProverInterface.ErrorHandler! handler)
+ modifies this.**;
+ modifies Console.Out.*, Console.Error.*;
+ modifies handler.*;
+ throws UnexpectedProverOutputException;
+
+ protected abstract void DoBeginCheck(string! descriptiveName, string! formula)
+ modifies this.*;
+ modifies Console.Out.*, Console.Error.*;
+
+ /// <summary>
+ /// Returns an array of the labels in "labels", with "|" brackets (if any)
+ /// stripped off.
+ /// Assumes that every label begins with "|+" or "|@", or just "+" or "@",
+ /// and ends with "|" if it started with one, and that these "|" brackets are
+ /// the only "|"s in "labels".
+ /// </summary>
+ protected static List<string!>! ParseLabels(string! labels)
+ {
+ List<string!> list = new List<string!>();
+ int j = 0;
+ while (true)
+ // invariant: j is the number of characters of "labels" consumed so far
+ // invariant: an even number of '|' characters remain in "labels"
+ invariant 0 <= j && j <= labels.Length;
+ {
+ j = labels.IndexOfAny(new char[]{'|', '+', '@'}, j);
+ if (j < 0) {
+ // no more labels
+ return list;
+ }
+ char ch = labels[j];
+ if (ch == '|') {
+ j++; // skip the '|'
+ assume j < labels.Length; // there should now be a '+' or '@'
+ ch = labels[j];
+ }
+ assume ch == '+' || ch == '@';
+ j++; // skip the '+' or '@'
+ int k = labels.IndexOfAny(new char[]{'|', ' ', ')'}, j);
+ assume j+2 <= k;
+ string s = labels.Substring(j, k-j);
+ list.Add(s);
+ j = k+1;
+ }
+ }
+
+ [Rep] char[] expectBuffer = null;
+
+ /// <summary>
+ /// Expects s[0]==ch and the next s.Length-1 characters of the input to be s[1,..]
+ /// If not, more characters may be read from "fromSimplify" to provide additional context
+ /// for the UnexpectedProverOutputException exception that will be thrown.
+ /// </summary>
+ protected void Expect(int ch, string! s)
+ requires 1 <= s.Length;
+ modifies this.*;
+ throws UnexpectedProverOutputException;
+ {
+ if (ch == -1) {
+ // a return of -1 from FromReadChar means that there is no StdOutput
+ // to treat this we can return the error message we get from Z3 on StdError and then
+ // declare this case to be inconclusive
+ string str = FromStdErrorAll();
+ if (str == "") {
+ throw new ProverDiedException();
+ } else {
+ throw new UnexpectedProverOutputException("Expected \"" + s + "\", found:\r\n<<<start>>>\r\n" + str + "<<<end>>>");
+ }
+ }
+
+ string badInputPrefix;
+ if (ch != s[0])
+ {
+ badInputPrefix = Char.ToString((char)ch);
+ }
+ else
+ {
+ int len = s.Length - 1;
+ if (expectBuffer == null || expectBuffer.Length < len)
+ {
+ expose (this) {
+ expectBuffer = new char[len];
+ }
+ }
+ try
+ {
+ string s0;
+ expose (this) {
+ fromSimplify.ReadBlock(expectBuffer, 0, len);
+ s0 = new string(expectBuffer, 0, len);
+ }
+ string s1 = s.Substring(1, len);
+ if (s0.CompareTo(s1) == 0)
+ {
+ badInputPrefix = null; // no error
+ }
+ else
+ {
+ badInputPrefix = (char)ch + s0;
+ }
+ }
+ catch (IOException)
+ {
+ throw new UnexpectedProverOutputException("Expected \"" + s + "\", encountered IO exception.");
+ }
+ }
+
+ if (badInputPrefix != null)
+ {
+ // Read the rest of the available input, without blocking!
+ // Despite the confusing documentation for the Read method, it seems
+ // that Read does block. It if didn't, I would have written:
+ // string remaining = "";
+ // char[] buf = new char[1024];
+ // while (true) {
+ // int len = fromSimplify.Read(buf, 0, buf.Length);
+ // remaining += new String(buf, 0, len);
+ // if (len != buf.Length) {
+ // break;
+ // }
+ // }
+ // But instead, I'll just hope that one line of input is available and read
+ // it.
+ string remaining = fromSimplify.ReadLine() + "\r\n";
+ throw new UnexpectedProverOutputException("Expected \"" + s + "\", found:\r\n<<<start>>>\r\n" + badInputPrefix + remaining + "<<<end>>>");
+ }
+ }
+
+ protected int FromReadChar()
+ modifies this.*;
+ {
+ expose (this) {
+ return fromSimplify.Read();
+ }
+ }
+
+ private void KillProver(object state)
+ {
+ expose (this) {
+ this.readTimedOut = true;
+ simplify.Kill();
+ }
+ }
+
+ protected int FromReadChar(int timeout)
+ requires -1 <= timeout;
+ modifies this.*;
+ {
+ expose (this) {
+ this.readTimedOut = false;
+ System.Threading.Timer t = new System.Threading.Timer(this.KillProver, null, timeout, System.Threading.Timeout.Infinite);
+ int ch = fromSimplify.Read();
+ t.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
+ t.Dispose();
+ return ch;
+ }
+ }
+
+ protected string! FromReadLine()
+ modifies this.*;
+ {
+ expose (this) {
+ string s = fromSimplify.ReadLine();
+ if (s == null) {
+ // this is what ReadLine returns if all characters have been read
+ s = "";
+ }
+ return s;
+ }
+ }
+
+ protected string! FromStdErrorAll ()
+ modifies this.*;
+ {
+ expose (this) {
+ if (fromStdError != null) {
+ string s = fromStdError.ReadToEnd();
+ if (s == null) {
+ // this is what ReadLine returns if all characters have been read
+ s = "";
+ }
+ return s;
+ }
+ // there is no StdErrorReader available
+ else {
+ return "";
+ }
+ }
+ }
+
+ protected void ToWriteLine(string! s)
+ modifies this.*;
+ {
+ expose (this) {
+ toSimplify.WriteLine(s);
+ }
+ }
+ }
+
+ // derived by Z3ProverProcess
+ public class SimplifyProverProcess : ProverProcess {
+ public SimplifyProverProcess(string! proverPath, bool dummy)
+ { // throws ProverException
+ ProcessStartInfo psi = new ProcessStartInfo(proverPath, "-labelsonly");
+ psi.CreateNoWindow = true;
+ psi.UseShellExecute = false;
+ psi.RedirectStandardInput = true;
+ psi.RedirectStandardOutput = true;
+ psi.RedirectStandardError = true;
+ assume psi.EnvironmentVariables != null; // by inspecting the code through Reflector; the documentation says this property is "null by default", whatever that means --KRML
+ if (0 <= CommandLineOptions.Clo.ProverKillTime) {
+ psi.EnvironmentVariables["PROVER_KILL_TIME"] = CommandLineOptions.Clo.ProverKillTime.ToString();
+ }
+ if (0 <= CommandLineOptions.Clo.SimplifyProverMatchDepth) {
+ psi.EnvironmentVariables["PROVER_MATCH_DEPTH"] = CommandLineOptions.Clo.SimplifyProverMatchDepth.ToString();
+ }
+ if (0 <= CommandLineOptions.Clo.ProverCCLimit) {
+ psi.EnvironmentVariables["PROVER_CC_LIMIT"] = CommandLineOptions.Clo.ProverCCLimit.ToString();
+ }
+
+ base(psi, proverPath);
+ }
+
+ public override string! OptionComments()
+ {
+ // would we want the timeout stuff here?
+ return "";
+ }
+
+ [NotDelayed]
+ // TODO it complains about things not beeing peer consistent upon call to EatPrompt()
+ // not sure what is it about... --micmo
+ [Verify(false)]
+ public SimplifyProverProcess(string! proverPath)
+ modifies Console.Out.*, Console.Error.*;
+ throws UnexpectedProverOutputException;
+ {
+ this(proverPath, true);
+ EatPrompt();
+ }
+
+ private void EatPrompt()
+ modifies this.*;
+ modifies Console.Out.*, Console.Error.*;
+ throws UnexpectedProverOutputException;
+ {
+ // skips text matching the regular expression: (white space)* ">\t"
+ ToFlush();
+
+ int ch = 0;
+ do {
+ ch = FromReadChar();
+ } while (Char.IsWhiteSpace((char)ch));
+
+ while (ch == 'W') {
+ ch = ConsumeWarnings(ch);
+ }
+
+ Expect(ch, ">\t");
+ }
+
+ public override void AddAxioms(string! s)
+ throws UnexpectedProverOutputException;
+ {
+ //ToWriteLine("(PROMPT_OFF)");
+ base.AddAxioms(s);
+ //ToWriteLine("(PROMPT_ON)");
+ EatPrompt();
+ }
+
+ public override void Feed(string! s, int statementCount)
+ throws UnexpectedProverOutputException;
+ {
+ //ToWriteLine("(PROMPT_OFF)");
+ base.Feed(s, statementCount);
+ //ToWriteLine("(PROMPT_ON)");
+ for (int i = 0; i < statementCount; i++) {
+ EatPrompt();
+ }
+ }
+
+ public override void PopAxioms()
+ throws UnexpectedProverOutputException;
+ {
+ base.PopAxioms();
+ EatPrompt();
+ }
+
+ protected override void DoBeginCheck(string! descriptiveName, string! formula)
+ {
+ //simplify.Refresh();
+ //this.Comment("@@@@ Virtual Memory: " + simplify.PeakVirtualMemorySize64);
+ //this.Comment("@@@@ Working Set: " + simplify.PeakWorkingSet64);
+ //this.Comment("@@@@ Paged Memory: " + simplify.PeakPagedMemorySize64);
+
+ ToWriteLine(formula);
+ ToFlush();
+ }
+
+ public override ProverOutcome CheckOutcome(Microsoft.Boogie.ProverInterface.ErrorHandler! handler)
+ throws UnexpectedProverOutputException;
+ {
+ ProverOutcome outcome;
+
+ if (this.simplify == null) {
+ return ProverOutcome.Inconclusive;
+ }
+
+ int ch = FromReadChar();
+ while (ch == 'W')
+ {
+ ch = ConsumeWarnings(ch);
+ }
+ if (ch == 'E')
+ {
+ Expect(ch, "Exceeded PROVER_KILL_TIME -- discontinuing search for counterexamples.");
+ FromReadLine();
+ ch = FromReadChar();
+ if (ch == '\n')
+ {
+ ch = FromReadChar();
+ }
+ Expect(ch, " labels:");
+ FromReadLine();
+ ch = FromReadChar();
+ ch = FromReadChar();
+ ch = FromReadChar();
+ FromReadLine();
+ ch = FromReadChar();
+ ch = FromReadChar();
+ ch = FromReadChar();
+ return ProverOutcome.TimeOut;
+ }
+ if ('0' <= ch && ch <= '9')
+ {
+ // Valid!
+ do {
+ ch = FromReadChar();
+ } while ('0' <= ch && ch <= '9');
+ Expect(ch, ": Valid.");
+ outcome = ProverOutcome.Valid;
+ ToWriteLine(String.Format("; FORMULA {0} IS VALID!", NumFormulasChecked + 1 /*Simplify starts at 1*/));
+ }
+ else
+ {
+ // now we expect one or more counterexample contexts, each proving a list of labels
+ do {
+ List<string!> labels = ReadLabels(ch);
+ handler.OnModel(labels, null);
+ ch = FromReadChar();
+ } while (ch == 'C');
+ // now we expect "<N>: Invalid" where <N> is some number
+ while ('0' <= ch && ch <= '9')
+ {
+ ch = FromReadChar();
+ }
+ Expect(ch, ": Invalid.");
+ outcome = ProverOutcome.NotValid;
+ ToWriteLine(String.Format("; FORMULA {0} IS INVALID", NumFormulasChecked + 1 /*Simplify starts at 1*/));
+ }
+
+ EatPrompt();
+ return outcome;
+ }
+
+ List<string!>! ReadLabels(int ch)
+ modifies this.*;
+ throws UnexpectedProverOutputException;
+ {
+ Expect(ch, "Counterexample:\n"); // FIX! ? Is there a problem with \r\n here?
+ ch = FromReadChar();
+ List<string!> theLabels;
+ if (ch == ' ')
+ {
+ // there are labels
+ Expect(ch, " labels: ");
+ string labels = FromReadLine(); // reads "(A B C ...)\n"
+ theLabels = ParseLabels(labels);
+ ch = FromReadChar();
+ }
+ else
+ {
+ theLabels = new List<string!>();
+ }
+ Expect(ch, "\n"); // empty line
+
+ return theLabels;
+ }
+
+ int ConsumeWarnings(int ch)
+ requires ch == 'W';
+ modifies this.*;
+ modifies Console.Out.*, Console.Error.*;
+ throws UnexpectedProverOutputException;
+ {
+ Expect(ch, "Warning: ");
+ string w = FromReadLine();
+ if (w.StartsWith("triggerless quantifier body")) {
+ FromReadLine(); // blank line
+ w = "triggerless quantifier body: " + FromReadLine(); // expression (body)
+ FromReadLine(); // blank line
+ FromReadLine(); // "with X pattern variable(s)...
+ FromReadLine(); // blank line
+ FromReadLine(); // expression (entire quantifier)
+ }
+ ReportWarning(w);
+ ch = FromReadChar();
+ if (ch == '\n')
+ {
+ // make up for a poorly designed ReadLine routine (only the first
+ // character of the DOS end-of-line sequence "\r\n" is read)
+ ch = FromReadChar();
+ }
+ return ch;
+ }
+ }
+
+}
+
diff --git a/Source/Provers/Simplify/ProverInterface.ssc b/Source/Provers/Simplify/ProverInterface.ssc
new file mode 100644
index 00000000..df7f86d6
--- /dev/null
+++ b/Source/Provers/Simplify/ProverInterface.ssc
@@ -0,0 +1,633 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Threading;
+using System.IO;
+using System.Text;
+using System.Diagnostics;
+using Microsoft.Contracts;
+using Microsoft.Boogie.AbstractInterpretation;
+using Microsoft.Boogie.Simplify;
+using Microsoft.Boogie.VCExprAST;
+using Microsoft.Boogie.TypeErasure;
+
+namespace Microsoft.Boogie.Simplify
+{
+ public abstract class LogProverInterface : ProverInterface
+ {
+ [NotDelayed]
+ protected LogProverInterface(ProverOptions! options,
+ string! openComment, string! closeComment,
+ string! openActivity, string! closeActivity,
+ VCExpressionGenerator! gen)
+ ensures this.gen == gen;
+ {
+ if (options.SeparateLogFiles) {
+ this.commonPrefix = new List<string!> ();
+ } else {
+ this.logFileWriter = options.OpenLog(null);
+ }
+ this.openCommentString = openComment;
+ this.closeCommentString = closeComment;
+ this.openActivityString = openActivity;
+ this.closeActivityString = closeActivity;
+ this.gen = gen;
+ this.options = options;
+ base();
+
+ if (CommandLineOptions.Clo.ShowEnv != CommandLineOptions.ShowEnvironment.Never) {
+ // Emit version comment in the log
+ LogCommonComment(CommandLineOptions.Clo.Version);
+ LogCommonComment(CommandLineOptions.Clo.Environment);
+ }
+ }
+
+ [StrictReadonly][Additive]
+ protected readonly VCExpressionGenerator! gen;
+
+ private TextWriter/*?*/ logFileWriter;
+ [Microsoft.Contracts.StrictReadonly]
+ private readonly string! openCommentString;
+ [Microsoft.Contracts.StrictReadonly]
+ private readonly string! closeCommentString;
+ [Microsoft.Contracts.StrictReadonly]
+ private readonly string! openActivityString;
+ [Microsoft.Contracts.StrictReadonly]
+ private readonly string! closeActivityString;
+ [Microsoft.Contracts.StrictReadonly]
+ protected readonly ProverOptions! options;
+ [Microsoft.Contracts.StrictReadonly]
+ private readonly List<string!>/*?*/ commonPrefix;
+
+ public void LogActivity(string! s) {
+ LogActivity(s, false);
+ }
+
+ public void LogCommon(string! s) {
+ LogActivity(s, true);
+ }
+
+ private void LogActivity(string! s, bool common) {
+ assume common || !options.SeparateLogFiles || logFileWriter != null;
+ if (logFileWriter != null) {
+ logFileWriter.Write(openActivityString);
+ logFileWriter.Write(s);
+ logFileWriter.WriteLine(closeActivityString);
+ logFileWriter.Flush();
+ }
+ if (common && commonPrefix != null) {
+ commonPrefix.Add(openActivityString + s + closeActivityString);
+ }
+ }
+
+ /// <summary>
+ /// Write "comment" to logfile, if any, formatted as a comment for the theorem prover at hand.
+ /// Assumes that "comment" does not contain any characters that would prematurely terminate
+ /// the comment (like, perhaps, a newline or "*/").
+ /// </summary>
+ public override void LogComment(string! comment)
+ {
+ LogComment(comment, false);
+ }
+
+ public void LogCommonComment(string! comment)
+ {
+ LogComment(comment, true);
+ }
+
+ private void LogComment(string! comment, bool common)
+ {
+ assume common || !options.SeparateLogFiles || logFileWriter != null;
+ if (logFileWriter != null) {
+ logFileWriter.Write(openCommentString);
+ logFileWriter.Write(comment);
+ logFileWriter.WriteLine(closeCommentString);
+ logFileWriter.Flush();
+ }
+ if (common && commonPrefix != null) {
+ commonPrefix.Add(openCommentString + comment + closeCommentString);
+ }
+ }
+
+ public virtual void NewProblem(string! descName)
+ {
+ if (commonPrefix != null) {
+ if (logFileWriter != null) {
+ logFileWriter.Close();
+ }
+ logFileWriter = options.OpenLog(descName);
+ if (logFileWriter != null) {
+ foreach (string! s in commonPrefix)
+ logFileWriter.WriteLine(s);
+ }
+ }
+ LogComment("Proof obligation: " + descName);
+ }
+
+ public override void Close() {
+ if (logFileWriter != null) {
+ logFileWriter.Close();
+ logFileWriter = null;
+ }
+ }
+
+ public override VCExpressionGenerator! VCExprGen
+ {
+ get { return this.gen; }
+ }
+ }
+
+ // -----------------------------------------------------------------------------------------------
+ // -----------------------------------------------------------------------------------------------
+ // -----------------------------------------------------------------------------------------------
+
+ public abstract class ProcessTheoremProver : LogProverInterface
+ {
+ private static string! _proverPath;
+
+ protected AxiomVCExprTranslator! vcExprTranslator { get {
+ return (AxiomVCExprTranslator!)ctx.exprTranslator;
+ } }
+
+ protected abstract AxiomVCExprTranslator! SpawnVCExprTranslator();
+
+ protected void FeedNewAxiomsDecls2Prover() throws UnexpectedProverOutputException; {
+ if (thmProver == null)
+ return;
+ foreach (string! s in vcExprTranslator.NewTypeDecls) {
+ LogCommon(s);
+ thmProver.Feed(s, 0);
+ }
+ foreach (string! s in vcExprTranslator.NewAxioms) {
+ LogBgPush(s);
+ thmProver.AddAxioms(s);
+ }
+ }
+
+ protected static string! CodebaseString() {
+ return Path.GetDirectoryName((!)System.Reflection.Assembly.GetExecutingAssembly().Location);
+ }
+
+ private static IDictionary<string!, string!>! BackgroundPredicates =
+ new Dictionary<string!, string!> ();
+
+ protected static string! GetBackgroundPredicate(string! filename) {
+ string res;
+ if (!BackgroundPredicates.TryGetValue(filename, out res)) {
+ // do we have to lock/synchronise anything?
+ string univBackPredPath = Path.Combine(CodebaseString(), filename);
+ using (StreamReader reader = new System.IO.StreamReader(univBackPredPath))
+ {
+ res = reader.ReadToEnd();
+ }
+ BackgroundPredicates.Add(filename, res);
+ }
+ return (!)res;
+ }
+
+ static void InitializeGlobalInformation(string! proverExe)
+ ensures _proverPath != null;
+ //throws ProverException, System.IO.FileNotFoundException;
+ {
+ if (_proverPath == null) {
+ // Initialize '_proverPath'
+ _proverPath = Path.Combine(CodebaseString(), proverExe);
+ if (!File.Exists(_proverPath))
+ {
+ _proverPath = Path.Combine(@"c:\Program Files\Microsoft Research\Z3-2.0\bin", proverExe);
+ }
+ if (!File.Exists(_proverPath))
+ {
+ throw new ProverException("Cannot find executable: " + _proverPath);
+ }
+ }
+ }
+
+ [Rep] protected internal ProverProcess thmProver;
+ bool currentProverHasBeenABadBoy = false;
+ // invariant currentProverHasBeenABadBoy ==> thmProver != null;
+ protected int restarts = 0;
+ protected DeclFreeProverContext! ctx;
+ protected string! BackgroundPredFilename;
+ protected ConsoleCancelEventHandler? cancelEvent;
+
+ [NotDelayed]
+ public ProcessTheoremProver(ProverOptions! options, VCExpressionGenerator! gen, DeclFreeProverContext! ctx,
+ string! proverExe, string! backgroundPred)
+ throws UnexpectedProverOutputException;
+ {
+ BackgroundPredFilename = backgroundPred;
+ InitializeGlobalInformation(proverExe);
+ this.ctx = ctx;
+ base(options, "; ", "", "", "", gen);
+
+ // ensure that a VCExprTranslator is available
+ // if none exists so far, we have to create a new one
+ // from scratch and feed the axioms to it
+ if (ctx.exprTranslator == null) {
+ AxiomVCExprTranslator tl = SpawnVCExprTranslator();
+ ctx.exprTranslator = tl;
+ tl.AddAxiom(tl.translate(ctx.Axioms, -1));
+ // we clear the lists with new axioms and declarations;
+ // they are not needed at this point
+ List<string!> x = tl.NewAxioms;
+ x = x; // make the compiler happy: somebody uses the value of x
+ x = tl.NewTypeDecls;
+ }
+ }
+
+ /// <summary>
+ /// MSchaef: Allows to Push a VCExpression as Axiom on the prover stack (beta)
+ /// </summary>
+ public override void PushVCExpression(VCExpr! vc)
+ {
+ vcExprTranslator.AddAxiom( vcExprTranslator.translate(vc,1) );
+ }
+
+ [NoDefaultContract] // important, since we have no idea what state the object might be in when this handler is invoked
+ void ControlCHandler(object o, ConsoleCancelEventArgs a)
+ {
+ if (thmProver != null) {
+ thmProver.Kill();
+ }
+ }
+
+ public override void Close() {
+ if (cancelEvent != null) {
+ Console.CancelKeyPress -= cancelEvent;
+ cancelEvent = null;
+ }
+ if (thmProver != null) {
+ expose (this) {
+ thmProver.Close();
+ thmProver = null;
+ currentProverHasBeenABadBoy = false;
+ }
+ }
+ base.Close();
+ }
+
+ private UnexpectedProverOutputException proverException;
+
+ public override void BeginCheck(string! descriptiveName, VCExpr! vc, ErrorHandler! handler)
+ {
+ this.NewProblem(descriptiveName);
+ this.proverException = null;
+
+ try {
+ this.ResurrectProver();
+
+ string vcString = vcExprTranslator.translate(vc, 1);
+
+ Helpers.ExtraTraceInformation("Sending data to theorem prover");
+
+ FeedNewAxiomsDecls2Prover();
+ string! prelude = ctx.GetProverCommands(false);
+ vcString = prelude + vcString;
+ LogActivity(vcString);
+
+ assert thmProver != null;
+ thmProver.BeginCheck(descriptiveName, vcString);
+
+ if (CommandLineOptions.Clo.RestartProverPerVC) {
+ LogComment("Will restart the prover due to /restartProver option");
+ currentProverHasBeenABadBoy = true;
+ }
+ } catch (UnexpectedProverOutputException e) {
+ proverException = e;
+ }
+ }
+
+ public override Outcome CheckOutcome(ErrorHandler! handler)
+ throws UnexpectedProverOutputException;
+ {
+ if (this.thmProver == null) {
+ return Outcome.Undetermined;
+ }
+
+ if (proverException == null) {
+ try {
+ ProverProcess.ProverOutcome result = thmProver.CheckOutcome(handler);
+
+ if (options.ForceLogStatus) {
+ switch (result) {
+ case ProverProcess.ProverOutcome.Valid:
+ LogActivity("DBG_WAS_VALID");
+ break;
+ case ProverProcess.ProverOutcome.NotValid:
+ LogActivity("DBG_WAS_INVALID");
+ break;
+ }
+ }
+
+ switch (result) {
+ case ProverProcess.ProverOutcome.Valid:
+ return Outcome.Valid;
+ case ProverProcess.ProverOutcome.TimeOut:
+ return Outcome.TimeOut;
+ case ProverProcess.ProverOutcome.OutOfMemory:
+ return Outcome.OutOfMemory;
+ case ProverProcess.ProverOutcome.Inconclusive:
+ return Outcome.Undetermined;
+ case ProverProcess.ProverOutcome.NotValid:
+ return Outcome.Invalid;
+ }
+ } catch (UnexpectedProverOutputException e) {
+ proverException = e;
+ }
+ }
+
+ assume proverException != null;
+ LogComment("***** Unexpected prover output");
+ expose (this) {
+ currentProverHasBeenABadBoy = true; // this will cause the next resurrect to restart the prover
+ }
+ throw proverException;
+ }
+
+ protected virtual void ResurrectProver()
+ throws UnexpectedProverOutputException;
+ {
+ expose (this) {
+ if (thmProver != null) {
+ if (thmProver.HasExited) {
+ DateTime now = DateTime.Now;
+ LogComment("***** Prover Crashed at or before " + now.ToString("G"));
+
+ } else if (CommandLineOptions.Clo.MaxProverMemory > 0 &&
+ thmProver.NumFormulasChecked > CommandLineOptions.Clo.MinNumOfProverCalls &&
+ thmProver.PeakVirtualMemorySize > CommandLineOptions.Clo.MaxProverMemory)
+ {
+ LogComment("***** Exceeded memory limit. Peak memory usage so far: " +
+ thmProver.PeakVirtualMemorySize / CommandLineOptions.Megabyte + "MB");
+
+ } else if (!currentProverHasBeenABadBoy) {
+ // prover is ready to go
+ return;
+ }
+
+ thmProver.Close();
+ thmProver = null;
+ currentProverHasBeenABadBoy = false;
+ restarts++;
+ }
+ FireUpNewProver();
+ }
+ }
+
+ protected abstract ProverProcess! CreateProverProcess(string! proverPath);
+
+ public void LogBgPush(string! s) {
+ LogCommon("(BG_PUSH ");
+ LogCommon(s);
+ LogCommon(")");
+ }
+
+ [NoDefaultContract]
+ private void FireUpNewProver()
+ requires IsExposed;
+ requires thmProver == null;
+ throws UnexpectedProverOutputException;
+ {
+ if (cancelEvent == null && CommandLineOptions.Clo.RunningBoogieFromCommandLine) {
+ cancelEvent = new ConsoleCancelEventHandler(ControlCHandler);
+ Console.CancelKeyPress += cancelEvent;
+ }
+ thmProver = CreateProverProcess(_proverPath);
+ if (restarts == 0) {
+ foreach (string! s in thmProver.OptionComments().Split('\n')) {
+ LogCommonComment(s);
+ }
+ foreach (string! parmsetting in thmProver.ParameterSettings) {
+ LogCommon(parmsetting);
+ }
+ }
+ foreach (string! parmsetting in thmProver.ParameterSettings) {
+ thmProver.Feed(parmsetting, 0);
+ }
+ thmProver.Feed(GetBackgroundPredicate(BackgroundPredFilename), 3);
+ string! incProverCommands = ctx.GetProverCommands(false);
+ string! proverCommands = ctx.GetProverCommands(true);
+ string! prelude = ctx.GetProverCommands(false);
+
+ if (restarts == 0) {
+ // log the stuff before feeding it into the prover, so when it dies
+ // and takes Boogie with it, we know what happened
+ LogCommon(GetBackgroundPredicate(BackgroundPredFilename));
+ LogCommon(prelude);
+ LogCommon(proverCommands);
+
+ foreach (string! s in vcExprTranslator.AllTypeDecls)
+ LogCommon(s);
+ foreach (string! s in vcExprTranslator.AllAxioms)
+ LogBgPush(s);
+
+ LogCommonComment("Initialized all axioms.");
+ } else {
+ LogCommon(incProverCommands);
+ }
+
+ thmProver.Feed(prelude, 0);
+ thmProver.Feed(proverCommands, 0);
+
+ foreach (string! s in vcExprTranslator.AllTypeDecls)
+ thmProver.Feed(s, 0);
+ foreach (string! s in vcExprTranslator.AllAxioms)
+ thmProver.AddAxioms(s);
+
+ // we have sent everything to the prover and can clear the lists with
+ // new axioms and declarations
+ List<string!> x = vcExprTranslator.NewAxioms;
+ x = x; // make the compiler happy: somebody uses the value of x
+ x = vcExprTranslator.NewTypeDecls;
+ }
+
+ public override ProverContext! Context
+ {
+ get { return this.ctx; }
+ }
+ }
+
+ public class SimplifyTheoremProver : ProcessTheoremProver
+ {
+ [NotDelayed]
+ public SimplifyTheoremProver(ProverOptions! options, VCExpressionGenerator! gen, DeclFreeProverContext! ctx)
+ throws UnexpectedProverOutputException;
+ {
+ base(options, gen, ctx, "simplify.exe", "UnivBackPred2.sx");
+ }
+
+ protected override ProverProcess! CreateProverProcess(string! proverPath) {
+ return new SimplifyProverProcess(proverPath);
+ }
+
+ protected override AxiomVCExprTranslator! SpawnVCExprTranslator() {
+ return new SimplifyVCExprTranslator(gen);
+ }
+ }
+
+ // -----------------------------------------------------------------------------------------------
+ // -----------------------------------------------------------------------------------------------
+ // -----------------------------------------------------------------------------------------------
+
+ public abstract class AxiomVCExprTranslator : VCExprTranslator {
+ protected AxiomVCExprTranslator() {
+ AllAxioms = new List<string!> ();
+ NewAxiomsAttr = new List<string!> ();
+ AllTypeDecls = new List<string!> ();
+ NewTypeDeclsAttr = new List<string!> ();
+ }
+
+ protected AxiomVCExprTranslator(AxiomVCExprTranslator! tl) {
+ AllAxioms = new List<string!> (tl.AllAxioms);
+ NewAxiomsAttr = new List<string!> (tl.NewAxiomsAttr);
+ AllTypeDecls = new List<string!> (tl.AllTypeDecls);
+ NewTypeDeclsAttr = new List<string!> (tl.NewTypeDeclsAttr);
+ }
+
+ // we store all typing-related axioms that have been sent to the prover
+ // so that the prover can be re-initialised in case it dies
+ public readonly List<string!>! AllAxioms;
+ private List<string!>! NewAxiomsAttr;
+
+ public List<string!>! NewAxioms { get {
+ List<string!>! res = NewAxiomsAttr;
+ NewAxiomsAttr = new List<string!> ();
+ return res;
+ } }
+
+ // similarly, a list of declarations that have been sent to the prover
+ public readonly List<string!>! AllTypeDecls;
+ private List<string!>! NewTypeDeclsAttr;
+
+ public List<string!>! NewTypeDecls { get {
+ List<string!>! res = NewTypeDeclsAttr;
+ NewTypeDeclsAttr = new List<string!> ();
+ return res;
+ } }
+
+ public void AddAxiom(string! axiom) {
+ AllAxioms.Add(axiom);
+ NewAxiomsAttr.Add(axiom);
+ }
+
+ public void AddTypeDecl(string! typeDecl) {
+ AllTypeDecls.Add(typeDecl);
+ NewTypeDeclsAttr.Add(typeDecl);
+ }
+ }
+
+ // -----------------------------------------------------------------------------------------------
+ // -----------------------------------------------------------------------------------------------
+ // -----------------------------------------------------------------------------------------------
+
+ public class SimplifyVCExprTranslator : AxiomVCExprTranslator {
+ public SimplifyVCExprTranslator(VCExpressionGenerator! gen) {
+ Gen = gen;
+ TypeAxiomBuilder! axBuilder;
+ switch (CommandLineOptions.Clo.TypeEncodingMethod) {
+ case CommandLineOptions.TypeEncoding.Arguments:
+ axBuilder = new TypeAxiomBuilderArguments (gen);
+ break;
+ default:
+ axBuilder = new TypeAxiomBuilderPremisses (gen);
+ break;
+ }
+ axBuilder.Setup();
+ AxBuilder = axBuilder;
+ Namer = new UniqueNamer ();
+ LitAbstracter = new BigLiteralAbstracter (gen);
+ }
+
+ private SimplifyVCExprTranslator(SimplifyVCExprTranslator! tl) {
+ base(tl);
+ Gen = tl.Gen;
+ AxBuilder = (TypeAxiomBuilder)tl.AxBuilder.Clone();
+ Namer = (UniqueNamer)tl.Namer.Clone();
+ LitAbstracter = (BigLiteralAbstracter)tl.LitAbstracter.Clone();
+ }
+
+ public override Object! Clone() {
+ return new SimplifyVCExprTranslator(this);
+ }
+
+ private readonly VCExpressionGenerator! Gen;
+ private readonly TypeAxiomBuilder! AxBuilder;
+ private readonly UniqueNamer! Namer;
+ private readonly BigLiteralAbstracter! LitAbstracter;
+
+ public override string! translate(VCExpr! expr, int polarity) {
+ Let2ImpliesMutator! letImplier = new Let2ImpliesMutator (Gen);
+
+ // handle the types in the VCExpr
+ TypeEraser! eraser;
+ switch (CommandLineOptions.Clo.TypeEncodingMethod) {
+ case CommandLineOptions.TypeEncoding.Arguments:
+ eraser = new TypeEraserArguments((TypeAxiomBuilderArguments)AxBuilder, Gen);
+ break;
+ default:
+ eraser = new TypeEraserPremisses((TypeAxiomBuilderPremisses)AxBuilder, Gen);
+ break;
+ }
+ VCExpr! exprWithoutTypes = eraser.Erase(expr, polarity);
+
+ TermFormulaFlattener! flattener = new TermFormulaFlattener (Gen);
+ VCExpr! exprWithLet = flattener.Flatten(exprWithoutTypes);
+ VCExpr! exprWithoutLet = letImplier.Mutate(exprWithLet);
+
+ // big integer literals
+ VCExpr! exprWithoutBigLits = LitAbstracter.Abstract(exprWithoutLet);
+ AddAxiom(SimplifyLikeExprLineariser.ToSimplifyString(LitAbstracter.GetNewAxioms(),
+ Namer));
+
+ // type axioms
+ VCExpr! axiomsWithLet = flattener.Flatten(AxBuilder.GetNewAxioms());
+ VCExpr! axiomsWithoutLet = letImplier.Mutate(axiomsWithLet);
+
+ AddAxiom(SimplifyLikeExprLineariser.ToSimplifyString(axiomsWithoutLet, Namer));
+ return SimplifyLikeExprLineariser.ToSimplifyString(exprWithoutBigLits, Namer);
+ }
+ }
+
+ // -----------------------------------------------------------------------------------------------
+ // -----------------------------------------------------------------------------------------------
+ // -----------------------------------------------------------------------------------------------
+
+ public class Factory : ProverFactory
+ {
+ public override object! SpawnProver(ProverOptions! options, object! ctxt)
+ {
+ return new SimplifyTheoremProver(options,
+ ((DeclFreeProverContext!)ctxt).ExprGen,
+ (DeclFreeProverContext!)ctxt);
+ }
+
+ public override object! NewProverContext(ProverOptions! options) {
+ if (CommandLineOptions.Clo.BracketIdsInVC < 0) {
+ CommandLineOptions.Clo.BracketIdsInVC = 1;
+ }
+ VCExpressionGenerator! gen = new VCExpressionGenerator();
+ List<string!>! proverCommands = new List<string!> ();
+ proverCommands.Add("all");
+ proverCommands.Add("simplify");
+ proverCommands.Add("simplifyLike");
+ return new DeclFreeProverContext(gen, new VCGenerationOptions(proverCommands));
+ }
+
+ public override CommandLineOptions.VCVariety DefaultVCVariety
+ {
+ get { return CommandLineOptions.VCVariety.BlockNested; }
+ }
+
+ // needed to make test7 pass
+ public override bool SupportsDags
+ {
+ get { return true; }
+ }
+ }
+}
diff --git a/Source/Provers/Simplify/Simplify.sscproj b/Source/Provers/Simplify/Simplify.sscproj
new file mode 100644
index 00000000..675a766b
--- /dev/null
+++ b/Source/Provers/Simplify/Simplify.sscproj
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="utf-8"?>
+<VisualStudioProject>
+ <XEN ProjectType="Local"
+ SchemaVersion="1.0"
+ Name="Simplify"
+ ProjectGuid="f75666de-fb56-457c-8782-09be243450fc"
+ >
+ <Build>
+ <Settings ApplicationIcon=""
+ AssemblyName="Provers.Simplify"
+ OutputType="Library"
+ RootNamespace="Microsoft.Boogie.Simplify"
+ StartupObject=""
+ StandardLibraryLocation=""
+ TargetPlatform="v2"
+ TargetPlatformLocation=""
+ ShadowedAssembly=""
+ NoStandardLibraries="False"
+ >
+ <Config Name="Debug"
+ AllowUnsafeBlocks="False"
+ BaseAddress="285212672"
+ CheckForOverflowUnderflow="False"
+ ConfigurationOverrideFile=""
+ DefineConstants="DEBUG;TRACE"
+ DocumentationFile=""
+ DebugSymbols="True"
+ FileAlignment="4096"
+ IncrementalBuild="True"
+ Optimize="False"
+ OutputPath="bin\debug"
+ RegisterForComInterop="False"
+ RemoveIntegerChecks="false"
+ TreatWarningsAsErrors="False"
+ WarningLevel="4"
+ ReferenceTypesAreNonNullByDefault="False"
+ RunProgramVerifier="False"
+ RunProgramVerifierWhileEditing="False"
+ ProgramVerifierCommandLineOptions=""
+ AllowPointersToManagedStructures="False"
+ CheckContractAdmissibility="True"
+ CheckPurity="False"
+ />
+ <Config Name="Release"
+ AllowUnsafeBlocks="false"
+ BaseAddress="285212672"
+ CheckForOverflowUnderflow="false"
+ ConfigurationOverrideFile=""
+ DefineConstants="TRACE"
+ DocumentationFile=""
+ DebugSymbols="false"
+ FileAlignment="4096"
+ IncrementalBuild="false"
+ Optimize="true"
+ OutputPath="bin\release"
+ RegisterForComInterop="false"
+ RemoveIntegerChecks="false"
+ TreatWarningsAsErrors="false"
+ WarningLevel="4"
+ />
+ </Settings>
+ <References>
+ <Reference Name="System"
+ AssemblyName="System"
+ Private="false"
+ />
+ <Reference Name="System.Data"
+ AssemblyName="System.Data"
+ Private="false"
+ />
+ <Reference Name="System.Xml"
+ AssemblyName="System.Xml"
+ Private="false"
+ />
+ <Reference Name="Core"
+ Project="{47BC34F1-A173-40BE-84C2-9332B4418387}"
+ Private="true"
+ />
+ <Reference Name="AIFramework"
+ Project="{24B55172-AD8B-47D1-8952-5A95CFDB9B31}"
+ Private="true"
+ />
+ <Reference Name="Graph"
+ Project="{4C28FB90-630E-4B55-A937-11A011B79765}"
+ Private="true"
+ />
+ <Reference Name="Mscorlib.Contracts"
+ AssemblyName="Mscorlib.Contracts"
+ Private="false"
+ HintPath="../../../Binaries/Mscorlib.Contracts.dll"
+ />
+ <Reference Name="System.Contracts"
+ AssemblyName="System.Contracts"
+ Private="false"
+ HintPath="../../../Binaries/System.Contracts.dll"
+ />
+ <Reference Name="VCGeneration"
+ Project="{F65666DE-FB56-457C-8782-09BE243450FC}"
+ Private="true"
+ />
+ <Reference Name="Basetypes"
+ Project="{0C692837-77EC-415F-BF04-395E3ED06E9A}"
+ Private="true"
+ />
+ <Reference Name="VCExpr"
+ Project="{CF42B700-10AA-4DA9-8992-48A800251C11}"
+ Private="true"
+ />
+ </References>
+ </Build>
+ <Files>
+ <Include>
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="Prover.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="ProverInterface.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="..\..\version.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="Let2ImpliesVisitor.ssc"
+ />
+ </Include>
+ </Files>
+ <Target Name="AfterBuild"
+ >
+ </Target>
+ </XEN>
+</VisualStudioProject> \ No newline at end of file
diff --git a/Source/Provers/Z3/Inspector.ssc b/Source/Provers/Z3/Inspector.ssc
new file mode 100644
index 00000000..ca7fc084
--- /dev/null
+++ b/Source/Provers/Z3/Inspector.ssc
@@ -0,0 +1,130 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.IO;
+using System.Diagnostics;
+using System.Collections;
+using System.Collections.Generic;
+using util;
+using Microsoft.Contracts;
+using Microsoft.Boogie;
+using Microsoft.Boogie.Simplify;
+using Microsoft.Basetypes;
+using Microsoft.Boogie.VCExprAST;
+
+namespace Microsoft.Boogie.Z3
+{
+ internal class FindLabelsVisitor : TraversingVCExprVisitor<bool, bool> {
+ public Dictionary<string!,bool>! Labels = new Dictionary<string!,bool>();
+
+ public static Dictionary<string!,bool>! FindLabels(VCExpr! expr) {
+ FindLabelsVisitor! visitor = new FindLabelsVisitor();
+ visitor.Traverse(expr, true);
+ return visitor.Labels;
+ }
+
+ protected override bool StandardResult(VCExpr! node, bool arg) {
+ VCExprNAry nary = node as VCExprNAry;
+ if (nary != null) {
+ VCExprLabelOp lab = nary.Op as VCExprLabelOp;
+ if (lab != null) {
+ Labels[lab.label] = lab.pos;
+ }
+ }
+ return true;
+ }
+ }
+
+ internal class Inspector {
+ [Rep] protected readonly Process! inspector;
+ [Rep] readonly TextReader! fromInspector;
+ [Rep] readonly TextWriter! toInspector;
+
+ public Inspector(Z3InstanceOptions! opts)
+ {
+ ProcessStartInfo! psi = new ProcessStartInfo(opts.Inspector);
+ psi.CreateNoWindow = true;
+ psi.UseShellExecute = false;
+ psi.RedirectStandardInput = true;
+ psi.RedirectStandardOutput = true;
+ psi.RedirectStandardError = false;
+
+ try
+ {
+ Process inspector = Process.Start(psi);
+ this.inspector = inspector;
+ fromInspector = inspector.StandardOutput;
+ toInspector = inspector.StandardInput;
+ }
+ catch (System.ComponentModel.Win32Exception e)
+ {
+ throw new Exception(string.Format("Unable to start the inspector process {0}: {1}", opts.Inspector, e.Message));
+ }
+ }
+
+ public void NewProver()
+ {
+ }
+
+ public void NewProblem(string! descriptiveName, VCExpr! vc, ProverInterface.ErrorHandler! handler)
+ {
+ Dictionary<string!,bool>! labels = FindLabelsVisitor.FindLabels(vc);
+ toInspector.WriteLine("PROBLEM " + descriptiveName);
+ toInspector.WriteLine("TOKEN BEGIN");
+ foreach (string! lab in labels.Keys) {
+ string! no = lab.Substring(1);
+ Absy! absy = handler.Label2Absy(no);
+ IToken tok = absy.tok;
+ AssertCmd assrt = absy as AssertCmd;
+ Block blk = absy as Block;
+ string val = tok.val; // might require computation, so cache it
+ if (val == "foo" || tok.filename == null) continue; // no token
+ if (!val.Contains(" ")) val = null;
+
+ toInspector.Write("TOKEN ");
+ toInspector.Write(lab);
+ toInspector.Write(" ");
+
+ if (assrt != null) {
+ toInspector.Write("ASSERT");
+ string errData = assrt.ErrorData as string;
+ if (errData != null) {
+ val = errData;
+ } else if (assrt.ErrorMessage != null) {
+ val = assrt.ErrorMessage;
+ }
+ } else if (blk != null) {
+ toInspector.Write("BLOCK ");
+ toInspector.Write(blk.Label);
+ } else {
+ assume false;
+ }
+ if (val == null) { val = ""; }
+
+ if (absy is LoopInitAssertCmd) {
+ val += " (loop entry)";
+ } else if (absy is LoopInvMaintainedAssertCmd) {
+ val += " (loop body)";
+ } else if (absy is AssertRequiresCmd && val == "") {
+ AssertRequiresCmd req = (AssertRequiresCmd)absy;
+ IToken t2 = req.Requires.tok;
+ val = string.Format("precondition {0}({1},{2})", t2.filename, t2.line, t2.col);
+ }
+
+ val = val.Replace("\r", "").Replace("\n", " ");
+
+ toInspector.WriteLine(string.Format(" {0} {1} :@:{2}:@:{3}", tok.line, tok.col, tok.filename, val));
+ }
+ toInspector.WriteLine("TOKEN END");
+ }
+
+ public void StatsLine(string! line)
+ {
+ toInspector.WriteLine(line);
+ toInspector.Flush();
+ }
+ }
+}
diff --git a/Source/Provers/Z3/Prover.ssc b/Source/Provers/Z3/Prover.ssc
new file mode 100644
index 00000000..d3212a9c
--- /dev/null
+++ b/Source/Provers/Z3/Prover.ssc
@@ -0,0 +1,870 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.IO;
+using System.Diagnostics;
+using System.Collections;
+using System.Collections.Generic;
+using System.Text;
+using util;
+using Microsoft.Contracts;
+using Microsoft.Boogie;
+using Microsoft.Boogie.Simplify;
+using Microsoft.Basetypes;
+
+// Simplified interface to an external prover like Simplify or the z3 process, taken from Bird.
+namespace Microsoft.Boogie.Z3
+{
+ internal class Z3ProverProcess : ProverProcess {
+ [Peer] private Z3InstanceOptions! opts;
+ [Peer] private readonly Inspector? inspector;
+ private readonly bool expectingModel = false;
+
+ private string! cmdLineArgs = "";
+ class OptionValue {
+ public readonly string! Option;
+ public readonly string! Value;
+ public OptionValue(string! option, string! value) { Option = option; Value = value; }
+ }
+ static void AddOption(List<OptionValue!>! parms, string! option, string! value)
+ modifies parms.*;
+ {
+ OptionValue ov = new OptionValue(option, value);
+ Owner.AssignSame(ov, Owner.ElementProxy(parms));
+ parms.Add(ov);
+ }
+ private List<OptionValue!>! parameterSettings;
+
+ // [NotDelayed]
+ [Captured]
+ public Z3ProverProcess(Z3InstanceOptions! opts, Inspector? inspector)
+ requires inspector != null ==> Owner.Same(opts, inspector);
+ { // throws ProverException
+ string! z3args = OptionChar() + "si";
+ List<OptionValue!> parms = new List<OptionValue!>();
+
+ if (opts.V2) {
+ AddOption(parms, "MODEL_PARTIAL", "true");
+ AddOption(parms, "MODEL_VALUE_COMPLETION", "false");
+ AddOption(parms, "MODEL_HIDE_UNUSED_PARTITIONS", "false");
+ AddOption(parms, "MODEL_V1", "true");
+ AddOption(parms, "ASYNC_COMMANDS", "false");
+ } else {
+ AddOption(parms, "PARTIAL_MODELS", "true");
+ AddOption(parms, "MODEL_VALUE_COMPLETION", "false");
+ AddOption(parms, "HIDE_UNUSED_PARTITIONS", "false");
+ }
+
+ if (opts.V2) {
+ // Phase selection means to always try the negative literal polarity first, seems to be good for Boogie.
+ // The restart parameters change the restart behavior to match Z3 v1, which also seems to be good.
+ AddOption(parms, "PHASE_SELECTION", "0");
+ AddOption(parms, "RESTART_STRATEGY", "0");
+ AddOption(parms, "RESTART_FACTOR", "|1.5|");
+
+ // This is used by VCC, but could be also useful for others, if sk_hack(foo(x)) is included as trigger,
+ // the foo(x0) will be activated for e-matching when x is skolemized to x0.
+ AddOption(parms, "NNF_SK_HACK", "true");
+
+ // More or less like MAM=0.
+ AddOption(parms, "QI_EAGER_THRESHOLD", "100");
+
+ // Make the integer model more diverse by default, speeds up some benchmarks a lot.
+ AddOption(parms, "ARITH_RANDOM_INITIAL_VALUE", "true");
+
+ // The left-to-right structural case-splitting strategy.
+ AddOption(parms, "SORT_AND_OR", "false");
+ AddOption(parms, "CASE_SPLIT", "3");
+
+ // In addition delay adding unit conflicts.
+ AddOption(parms, "DELAY_UNITS", "true");
+ AddOption(parms, "DELAY_UNITS_THRESHOLD", "16");
+
+ if (opts.Inspector != null) {
+ AddOption(parms, "PROGRESS_SAMPLING_FREQ", "100");
+ }
+ } else {
+ z3args += " " + OptionChar() + "mam:" + CommandLineOptions.Clo.Z3mam;
+ }
+
+ if (0 <= CommandLineOptions.Clo.ProverCCLimit) {
+ z3args += " " + OptionChar() + "@ " +
+ OptionChar() + "cex:" + CommandLineOptions.Clo.ProverCCLimit;
+ }
+ if (0 <= opts.Timeout) {
+ z3args += " " + OptionChar() + "t:" + opts.Timeout;
+ }
+ if (opts.Typed) {
+ AddOption(parms, "TYPE_CHECK", "true");
+ if (opts.BitVectors == CommandLineOptions.BvHandling.Z3Native) {
+ if (opts.V2) {
+ AddOption(parms, "BV_REFLECT", "true");
+ } else {
+ AddOption(parms, "REFLECT_BV_OPS", "true");
+ }
+ }
+ }
+ if (CommandLineOptions.Clo.PrintErrorModel >= 1 || CommandLineOptions.Clo.EnhancedErrorMessages == 1 || CommandLineOptions.Clo.ContractInfer) {
+ z3args += " " + OptionChar() + "m";
+ expectingModel = true;
+ }
+ // Z3 version 1.3 does not support SETPARAMETER in the input, so we tack on the OPTION=value pairs to z3args
+ if (opts.V1) {
+ foreach (OptionValue opt in parms) {
+ z3args += string.Format(" \"{0}={1}\"", opt.Option, opt.Value);
+ }
+ }
+
+ foreach (string! opt in CommandLineOptions.Clo.Z3Options) {
+ int eq = opt.IndexOf("=");
+ // we add them both to command line and the input file:
+ // - allow for overriding default options
+ // - some options (like TRACE) work only from command line
+ // Also options with spaces do not work with (SETPARAMETER ...)
+ if (eq > 0 && opt.IndexOf(" ") < 0 && 'A' <= opt[0] && opt[0] <= 'Z') {
+ AddOption(parms, opt.Substring(0, eq), opt.Substring(eq + 1));
+ }
+ z3args += " \"" + opt + "\"";
+ }
+
+
+ cmdLineArgs = z3args;
+ parameterSettings = parms;
+
+ string exePath = opts.ExeName;
+ if (!File.Exists(exePath)){
+ exePath = @"c:\Program Files\Microsoft Research\Z3-2.0\bin";
+ }
+
+ ProcessStartInfo! psi = new ProcessStartInfo(exePath, z3args);
+ psi.CreateNoWindow = true;
+ psi.UseShellExecute = false;
+ psi.RedirectStandardInput = true;
+ psi.RedirectStandardOutput = true;
+ psi.RedirectStandardError = true;
+
+ base(psi, opts.ExeName);
+
+ Owner.AssignSame(this, opts);
+ this.opts = opts;
+ this.inspector = inspector;
+ }
+
+ public override string! OptionComments()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.AppendFormat("Z3 command line: {0} {1}\nUser supplied Z3 options:",
+ opts.ExeName, cmdLineArgs);
+ assume CommandLineOptions.Clo.IsPeerConsistent;
+ foreach (string! opt in CommandLineOptions.Clo.Z3Options) {
+ sb.Append(" ").Append(opt);
+ }
+ sb.AppendFormat("\nProver options: {0}\n", opts.ToString());
+ return sb.ToString();
+ }
+
+ [Pure(false)]
+ public override IEnumerable<string!>! ParameterSettings {
+ get {
+ if (opts.V2) {
+ foreach (OptionValue opt in parameterSettings) {
+ yield return "(SETPARAMETER " + opt.Option + " " + opt.Value + ")";
+ }
+ }
+ }
+ }
+
+ // z3 uses different magic characters for options on linux/unix and on windows
+ private static string! OptionChar() {
+ assume Environment.OSVersion != null;
+ switch (Environment.OSVersion.Platform) {
+ case PlatformID.Unix:
+ return "-";
+ default:
+ return "/";
+ }
+ }
+
+ protected override void DoBeginCheck(string! descriptiveName, string! formula)
+ {
+ ToWriteLine(formula);
+ ToWriteLine(String.Format("; END OF FORMULA {0} - {1}", NumFormulasChecked.ToString(), descriptiveName));
+ ToFlush();
+ }
+
+ protected int TimedFromReadChar()
+ {
+ if (opts.Timeout > 0)
+ return FromReadChar((opts.Timeout + 1) * 1000);
+ else
+ return FromReadChar();
+ }
+
+ private void Trace(string! msg)
+ {
+ Console.WriteLine("Z3: " + msg);
+ }
+
+ public override ProverOutcome CheckOutcome(Microsoft.Boogie.ProverInterface.ErrorHandler! handler)
+ throws UnexpectedProverOutputException;
+ {
+ ProverOutcome outcome;
+ bool isInvalid = false;
+
+ if (this.simplify == null) {
+ return ProverOutcome.Inconclusive;
+ }
+
+
+ while (true) {
+ int ch = TimedFromReadChar();
+ if (ch == -1 && this.readTimedOut) {
+ handler.OnResourceExceeded("timeout (forced)");
+ return ProverOutcome.TimeOut;
+ }
+
+ string line = new string((char)ch, 1) + FromReadLine();
+
+ if (line.StartsWith("STATS ")) {
+ if (inspector != null) {
+ inspector.StatsLine(line);
+ }
+ continue;
+ }
+
+ if (opts.Verbosity > 2) {
+ Trace("INPUT: " + line);
+ }
+
+ if (line.StartsWith("WARNING: Out of allocated virtual memory.")) {
+ handler.OnResourceExceeded("memory");
+ return ProverOutcome.OutOfMemory;
+ }
+
+
+ if (line.StartsWith("WARNING: ")) {
+ string w = line.Substring(9);
+ ReportWarning(w);
+ continue;
+ }
+
+ if (line.ToUpper().StartsWith("ERROR")) {
+ Console.WriteLine("Z3 returns the following error:");
+ Console.WriteLine(" " + line);
+ return ProverOutcome.Inconclusive;
+ }
+
+ int beg = 0;
+ while (beg < line.Length && '0' <= line[beg] && line[beg] <= '9')
+ invariant beg <= line.Length;
+ {
+ beg++;
+ }
+
+ if (beg > 0 && line.Substring(beg).StartsWith(": ")) {
+ string status = line.Substring(beg + 2);
+
+ if (status.StartsWith("Valid")) {
+ return ProverOutcome.Valid;
+ }
+
+ if (status.StartsWith("Timeout")) {
+ handler.OnResourceExceeded("timeout");
+ return ProverOutcome.TimeOut;
+ }
+
+ if (status.StartsWith("Inconclusive")) {
+ return ProverOutcome.Inconclusive;
+ }
+
+ if (status.StartsWith("Invalid")) {
+ isInvalid = true;
+ continue;
+ }
+ }
+
+ if (isInvalid && line == ".") {
+ return ProverOutcome.NotValid;
+ }
+
+ if (isInvalid && line.StartsWith("labels: (")) {
+ List<string!>! l = ParseLabels(line);
+ Z3ErrorModel errModel = null;
+ if (expectingModel) {
+ if (opts.Verbosity > 2) {
+ Trace("waiting for model");
+ }
+ line = FromReadLine();
+ if (line.StartsWith("partitions:")) {
+ line = ParseModel(out errModel);
+ if (opts.Verbosity > 2) {
+ Trace("model parsed, final line " + line);
+ }
+ // Z3 always ends the model with END_OF_MODEL, not with labels: or .
+ assume line == "END_OF_MODEL";
+ } else {
+ throw new UnexpectedProverOutputException(string.Format("evil input from z3 (expecting partitions): '{0}'", line));
+ }
+ }
+ handler.OnModel(l, errModel);
+ continue;
+ }
+
+ throw new UnexpectedProverOutputException(string.Format("evil input from z3: '{0}'", line));
+ }
+ }
+
+ /* ----------------------------------------------------------------------------
+ BNF Grammar to parse Z3 output, including the model generated when using the /m /si switch:
+
+ Output ::= VC*
+ VC ::= number ": " "Valid." | number ": " "Inconclusive" | VCI
+ VCI ::= number ": " "Invalid"
+ ("labels: " "(" ID* ")"
+ [MODEL] "END_OF_MODEL")+
+ "."
+ MODEL ::= MBOOL MFUNC
+ MBOOL ::= "boolean assignment:"
+ "partitions:"
+ MAPPING*
+ MAPPING ::= ZID ["{" ID+"}"] ["->" "(" (number | false | true | BITVECTOR) ")"]
+ BITVECTOR ::= ulong ":bv" int
+ MFUNC ::= "function interpretations:"
+ F*
+ F ::= Id "->" "{"
+ MAPLET*
+ "else" "->" ZID
+ "}"
+ MAPLET ::= ZID* "->" ZID
+
+ -----------------------------------------------------------------------------*/
+ private string! ParseModel(out Z3ErrorModel errModel)
+ modifies this.*;
+ ensures result == "." || result.StartsWith("labels: (") || result == "END_OF_MODEL";
+ throws UnexpectedProverOutputException;
+ {
+ //Format in the grammar:
+ // ZID ["{" ID+"}"] ["->" "(" (number | false | true) ")"]
+ // storing the model:
+ // map each ID (a string) to the corresping ZID (an integer) in a dictionary:
+ Dictionary<string!, int> identifierToPartition = new Dictionary<string!, int>();
+ // map each ZID to the set (in list form) of IDs belonging to it (possibly empty):
+ List<List<string!>> partitionToIdentifiers = new List<List<string!>>();
+ // map each ZID to the number or boolean given, if any:
+ List<Object> partitionToValue = new List<Object>();
+ // map each value (number or boolean) to the ZID, reverse map of partitionToValue
+ Dictionary<Object, int> valueToPartition = new Dictionary<Object, int>();
+ Owner.AssignSame(Owner.ElementProxy(partitionToValue), Owner.ElementProxy(valueToPartition));
+
+ int ch;
+
+ // read the MAPPING
+ for (int zID = 0; true; zID++)
+ {
+ ch = FromReadChar();
+ if (ch == 'f') {
+ break;
+ }
+ ParseModelMapping(zID, identifierToPartition, partitionToIdentifiers, partitionToValue, valueToPartition);
+ }// end MAPPING
+
+ // add the fake partition for the 'else -> #undefined' clause
+ List<string!> emptyList = new List<string!>();
+ Owner.AssignSame(emptyList, Owner.ElementProxy(partitionToIdentifiers));
+ partitionToIdentifiers.Add(emptyList);
+ partitionToValue.Add(null);
+
+ // continue in ParseModelFunctions, which breaks up this long method and enables its verification
+ return ParseModelFunctions(ch, out errModel, identifierToPartition, partitionToIdentifiers, partitionToValue, valueToPartition);
+ }
+
+ private void ParseModelMapping(int zID,
+ Dictionary<string!, int>! identifierToPartition,
+ List<List<string!>>! partitionToIdentifiers,
+ List<Object>! partitionToValue,
+ Dictionary<Object, int>! valueToPartition)
+ requires Owner.Same(Owner.ElementProxy(partitionToValue), Owner.ElementProxy(valueToPartition));
+ modifies this.*, identifierToPartition.*, partitionToIdentifiers.*, partitionToValue.*, valueToPartition.*;
+ {
+ string s = FromReadLine();
+ {
+ // sanity check
+ int pos = s.IndexOf(' ');
+ string n = s;
+ int k;
+ if (pos >= 0) {
+ n = s.Substring(0,pos);
+ }
+ if (! (int.TryParse(n, out k) && zID == k)) {
+ System.Console.WriteLine("mismatch: {0}!={1} '{2}'", zID, k, s);
+ assume false;
+ }
+ }
+
+ int j = ParseModelZidAndIdentifiers(zID, s, identifierToPartition, partitionToIdentifiers);
+
+ j = s.IndexOf(" -> ", j);
+ if (0 <= j) {
+ j += 4;
+ }
+ assume j == -1 || j < s.Length; // if ' -> ' is present, then more should remain of the line
+ if (j == -1) {
+ // no "-> " found, end of this line, store that there is no value:
+ partitionToValue.Add(null);
+ int idForNull;
+ if (identifierToPartition.TryGetValue("nullObject", out idForNull) && idForNull == zID) {
+ assume !valueToPartition.ContainsKey("nullObject"); // a RHS value should occur only once in the Z3 output
+ valueToPartition.Add("nullObject", zID);
+ // In this case partitionToValue (as the reverse of valueToPartition) should include
+ // a map from zID -> "nullObject", but doing that breaks printing out the model as
+ // it is printed out by Z3. Also, that information is not required, valueToPartition
+ // works well enough by itself.
+ }
+
+ } else if (s[j] == 't'/*rue*/) {
+ partitionToValue.Add(true);
+ object boxedTrue = true;
+ assume !valueToPartition.ContainsKey(boxedTrue); // a RHS value should occur only once in the Z3 output
+ valueToPartition.Add(boxedTrue, zID);
+ } else if (s[j] == 'f'/*alse*/) {
+ object boxedFalse = false;
+ Owner.AssignSame(boxedFalse, Owner.ElementProxy(partitionToValue));
+ partitionToValue.Add(boxedFalse);
+ assume !valueToPartition.ContainsKey(boxedFalse); // a RHS value should occur only once in the Z3 output
+ valueToPartition.Add(boxedFalse, zID);
+ } else if (s[j] == 'v') {
+ // -> val!..., i.e. no value
+ partitionToValue.Add(null);
+ } else {
+ string numberOrBv = s.Substring(j);
+ // make number an int, then store it:
+ BigNum bvVal;
+ int bvSize;
+ string number, type;
+
+ int l = numberOrBv.IndexOf(':', 0);
+ if (0 <= l) {
+ number = numberOrBv.Substring(0, l);
+ type = numberOrBv.Substring(l + 1);
+ } else {
+ l = numberOrBv.IndexOf('[', 0);
+ if (0 <= l) {
+ number = numberOrBv.Substring(2, l-2);
+ int closingBracePosition = numberOrBv.IndexOf(']', l);
+ if (l < closingBracePosition)
+ type = "bv" + numberOrBv.Substring(l + 1, closingBracePosition - l - 1);
+ else type = "int";
+ } else {
+ number = numberOrBv;
+ type = "int";
+ }
+ }
+
+ if (type == "int") {
+ object boxedN = BigNum.FromString(number);
+ assume Owner.None(boxedN);
+ Owner.AssignSame(boxedN, Owner.ElementProxy(partitionToValue));
+ partitionToValue.Add(boxedN);
+ assume !valueToPartition.ContainsKey(boxedN); // a RHS value should occur only once in the Z3 output
+ valueToPartition.Add(boxedN, zID);
+ } else if (type.StartsWith("bv") && BigNum.TryParse(number, out bvVal) && int.TryParse(type.Substring(2), out bvSize)) {
+ BvConst bitV = new BvConst(bvVal, bvSize);
+ Owner.AssignSame(bitV, Owner.ElementProxy(partitionToValue));
+ partitionToValue.Add(bitV);
+ assume !valueToPartition.ContainsKey(bitV); // a RHS value should occur only once in the Z3 output
+ valueToPartition.Add(bitV, zID);
+ } else {
+ System.Console.WriteLine("cannot parse type: '{0}':'{1}'", number, type);
+ assume false;
+ }
+
+ }
+ }
+
+ private static int ParseModelZidAndIdentifiers(int zID, string! s,
+ Dictionary<string!, int>! identifierToPartition,
+ List<List<string!>>! partitionToIdentifiers)
+ modifies identifierToPartition.*, partitionToIdentifiers.*;
+ ensures 0 <= result && result <= s.Length;
+ {
+ List<string!> identifiers = new List<string!>();
+ int j = s.IndexOf('{', 0) + 1; // skip the '{', if present, and set j to 0 otherwise
+ if (1 <= j) {
+ // There is a list of ID's.
+ assume j < s.Length; // there should be more characters; the ending '}', for one
+ //ID*
+ while (true)
+ invariant identifiers.IsPeerConsistent && identifierToPartition.IsPeerConsistent && partitionToIdentifiers.IsPeerConsistent;
+ invariant 0 <= j && j < s.Length;
+ {
+ int k = s.IndexOfAny(new char[]{' ', '}'}, j);
+ assume j <= k;
+ string id = s.Substring(j, k-j);
+ j = k+1;
+ assume !identifierToPartition.ContainsKey(id); // an ID is listed only once in this list, and an ID can only belong to one ZID equivalence class
+ identifierToPartition.Add(id, zID);
+ identifiers.Add(id);
+ if (s[k] == '}') {
+ // end of reading ID*
+ break;
+ }
+ assume j < s.Length; // there should be more characters; the ending '}', for one
+ }//end ID*
+ }
+ Owner.AssignSame(identifiers, Owner.ElementProxy(partitionToIdentifiers));
+ partitionToIdentifiers.Add(identifiers);
+ return j;
+ }
+
+ private string! ParseModelFunctions(int ch, out Z3ErrorModel errModel,
+ Dictionary<string!, int>! identifierToPartition,
+ List<List<string!>>! partitionToIdentifiers,
+ List<Object>! partitionToValue,
+ Dictionary<Object, int>! valueToPartition
+ )
+ modifies this.*;
+ ensures result == "." || result.StartsWith("labels: (") || result == "END_OF_MODEL";
+ throws UnexpectedProverOutputException;
+ {
+ // read the function F
+ Expect(ch, "function interpretations:");
+ FromReadLine();
+
+ // mapping of function names to function definitions
+ Dictionary<string!, List<List<int>>!> definedFunctions = new Dictionary<string!, List<List<int>>!>();
+ // function definition given as list of 'pointwise definitions' in the form of the arguments and result
+ // the last element here will always be a list with just one entry which corresponds to the else case
+ List<List<int>> functionDefinition = new List<List<int>>();
+ // list of arguments, followed by the result, the last element of this list is always the result
+ List<int> argumentsAndResult = new List<int>();
+
+ // read F
+ while (true)
+ {
+ functionDefinition = new List<List<int>>();
+ string s = FromReadLine();
+ // end of F, "END_OF_MODEL" ends model, '.' ends whole VC, 'l' starts a new set of labels and model
+ // whenever there is a model this will end with "END_OF_MODEL", the other cases can only
+ // happen when there is no model printed!
+ if (s == "." || s.StartsWith("labels: (") || s == "END_OF_MODEL") {
+ errModel = new Z3ErrorModel(identifierToPartition, partitionToIdentifiers, partitionToValue, valueToPartition, definedFunctions);
+ return s;
+ }
+ int j = s.IndexOf(' ', 0);
+ assume 0 <= j;
+ string id = s.Substring(0, j);
+ // id is stored into definedFunctions once the function definition for it has
+ // been completely parsed,
+
+ // just ignore the "-> {" by dropping string s
+ string zIDstring;
+
+ // MAPLET
+ while (true)
+ {
+ argumentsAndResult = new List<int>();
+ // remove the 2 spaces that are here
+ FromReadChar();
+ FromReadChar();
+ s = FromReadLine();
+ if (s.StartsWith("else ->")) break;
+ j = 0;
+
+ //ZID*
+ while(true)
+ invariant 0 <= j && j <= s.Length;
+ {
+ j = s.IndexOfAny(new Char[]{'*', '-'}, j);
+ // true because this always ends with a "->":
+ assume 0 <= j;
+
+ // reading -> means end of ZID*
+ if (s[j] == '-'/*>*/) break;
+
+ // start reading the ZID* with the number, not the *
+ j = j + 1;
+ // by input string format:
+ assume j < s.Length;
+ int k = s.IndexOf(' ', j);
+ // by input string format:
+ assume j <= k;
+ zIDstring = s.Substring(j, k-j);
+ // add the arguments
+ argumentsAndResult.Add(int.Parse(zIDstring));
+ j = k;
+ }// end ZID*
+
+ // j is the beginning of "-> *", we want the first character after this
+ j = j + 4;
+ // by input string format:
+ assume j <= s.Length;
+ zIDstring = s.Substring(j);
+ // add the result
+ argumentsAndResult.Add(int.Parse(zIDstring));
+ // add the input line as another 'pointwise defined' element to the functions definition
+ functionDefinition.Add(argumentsAndResult);
+ }// end MAPLET
+
+ // this is the 'else -> #unspecified' case
+ // by input string format:
+ assume s.IndexOf("#unspec") >= 0;
+ // this stores the else line as another argumentsAndResult list
+ argumentsAndResult = new List<int>();
+ argumentsAndResult.Add(partitionToIdentifiers.Count - 1); // use the fake partition we have created before
+ // which is then added to the function definition, which is now complete
+ Owner.AssignSame(argumentsAndResult, Owner.ElementProxy(functionDefinition));
+ functionDefinition.Add(argumentsAndResult);
+
+ /*
+ // this is the 'else -> *' case, that string is already in s
+ j = s.IndexOf('*', 0) + 1;
+ // by input string format:
+ assume 0 < j && j < s.Length;
+ zIDstring = s.Substring(j);
+ // this stores the else line as another argumentsAndResult list
+ argumentsAndResult = new List<int>();
+ argumentsAndResult.Add(int.Parse(zIDstring));
+ // which is then added to the function definition, which is now complete
+ functionDefinition.Add(argumentsAndResult); */
+
+ // and therefore added to the map of defined functions, together with the name 'id'
+ // which had been extracted before
+ assume !definedFunctions.ContainsKey(id); // each function name in the model is listed only once
+ definedFunctions.Add(id, functionDefinition);
+
+ // read the line with "}"
+ ch = FromReadChar();
+ Expect(ch, "}");
+ FromReadLine();
+ }// end F
+ }
+
+ }
+
+
+ public class Z3ErrorModel : ErrorModel
+ {
+ public Z3ErrorModel(Dictionary<string!, int>! identifierToPartition, List<List<string!>>! partitionToIdentifiers, List<Object>! partitionToValue, Dictionary<object, int>! valueToPartition, Dictionary<string!, List<List<int>>!>! definedFunctions)
+ : base(identifierToPartition, partitionToIdentifiers, partitionToValue, valueToPartition, definedFunctions)
+ {
+ this.partitionNames = new string?[partitionToIdentifiers.Count];
+ this.prevPartitionNames = new string?[partitionToIdentifiers.Count];
+ }
+
+ private string?[]! partitionNames;
+ private string?[]! prevPartitionNames;
+
+ private void SetNames()
+ {
+ int len = partitionToIdentifiers.Count;
+ for (int i = 0; i < 3; ++i) { // let the names stabilize a bit
+ prevPartitionNames = partitionNames;
+ partitionNames = new string?[len];
+ for (int pos = 0; pos < len; ++pos)
+ GetPartitionName(pos);
+ }
+ }
+
+ private int NameBadness(string! name)
+ {
+ int badness = name.Length;
+ if (name.StartsWith("call") && name.IndexOf("formal@") > 0)
+ badness += 1000;
+ if (name.IndexOf ("(") > 0)
+ badness += 500;
+ return badness;
+ }
+
+ private string! GetPartitionName(int pos)
+ {
+ string name = partitionNames[pos];
+ if (name != null) {
+ return name;
+ }
+
+ object tmp = partitionToValue[pos];
+ if (tmp != null) {
+ partitionNames[pos] = tmp is BvConst ? ((BvConst)tmp).ToReadableString() : tmp.ToString();
+ } else {
+ List<string!>! possible_names = new List<string!>();
+ List<string!> pti = partitionToIdentifiers[pos];
+
+ // make sure we're not called recursively
+ string prevName = prevPartitionNames[pos];
+ if (prevName == null) prevName = "*" + pos;
+ partitionNames[pos] = prevName;
+
+ if (pti != null && pti.Count > 0) {
+ // add identifiers
+ foreach (string! name in pti)
+ possible_names.Add(name);
+ }
+
+ // Then also look for functions,
+ // and then collect possible functional definitions
+ foreach (KeyValuePair<string!, List<List<int>>!> kv in definedFunctions) {
+ foreach (List<int>! parms in kv.Value) {
+ if (parms.Count > 1 && parms[parms.Count - 1] == pos) {
+ string! s = kv.Key + "(";
+ for (int i = 0; i < parms.Count - 1; ++i) {
+ if (i != 0) s += ", ";
+ s += GetPartitionName(parms[i]);
+ }
+ s += ")";
+ possible_names.Add(s);
+ }
+ }
+ }
+
+ // choose the shortest possible name
+ if (possible_names.Count > 0) {
+ string! best = possible_names[0];
+ foreach (string! s in possible_names)
+ if (NameBadness(s) < NameBadness(best)) best = s;
+ if (best.Length < 120)
+ partitionNames[pos] = best;
+ }
+ }
+
+ return (!)partitionNames[pos];
+ }
+
+ private void PrintReadableModel (TextWriter! writer) {
+ writer.WriteLine("Z3 error model: ");
+ SetNames();
+ writer.WriteLine("partitions:");
+ assert partitionToIdentifiers.Count == partitionToValue.Count;
+ for (int i = 0; i < partitionToIdentifiers.Count; i++){
+ writer.Write("{0,5}: {1} ", "*"+i, GetPartitionName(i));
+ List<string!> pti = partitionToIdentifiers[i];
+ if (pti != null && (pti.Count > 1 || (pti.Count == 1 && partitionToValue[i] != null))) {
+ writer.Write("{");
+ for (int k = 0; k < pti.Count - 1; k ++) {
+ writer.Write(pti[k] + " ");
+ }
+ //extra work to make sure no " " is at the end of the list of identifiers
+ if (pti.Count != 0) {
+ writer.Write(pti[pti.Count - 1]);
+ }
+ writer.Write("}");
+ }
+ writer.WriteLine();
+ }
+
+ writer.WriteLine();
+ writer.WriteLine("function interpretations:");
+ List<string!> funNames = new List<string!>(definedFunctions.Keys);
+ funNames.Sort();
+ foreach (string! name in funNames) {
+ if (definedFunctions[name].Count == 1) continue; // skip functions with only the else-> clause
+ foreach (List<int>! parms in definedFunctions[name]) {
+ string! s = name + "(";
+ if (parms.Count == 1) {
+ continue;
+ // s += "*";
+ } else {
+ for (int i = 0; i < parms.Count - 1; ++i) {
+ if (i != 0) s += ", ";
+ s += GetPartitionName(parms[i]);
+ }
+ }
+ s += ")";
+ string! res = GetPartitionName(parms[parms.Count - 1]);
+ if (res == s)
+ res = "*" + parms[parms.Count - 1] + " (SELF)";
+ writer.WriteLine("{0} = {1}", s, res);
+ }
+ writer.WriteLine();
+ }
+ writer.WriteLine("The end.");
+ writer.WriteLine();
+ }
+
+ public override void Print (TextWriter! writer) {
+ if (CommandLineOptions.Clo.PrintErrorModel == 4) {
+ PrintReadableModel(writer);
+ return;
+ }
+
+ writer.WriteLine("Z3 error model: ");
+
+ writer.WriteLine("partitions:");
+ assert partitionToIdentifiers.Count == partitionToValue.Count;
+ for (int i = 0; i < partitionToIdentifiers.Count; i++){
+ writer.Write("*"+i);
+ List<string!> pti = partitionToIdentifiers[i];
+ if (pti != null && pti.Count != 0) {
+ writer.Write(" {");
+ for (int k = 0; k < pti.Count - 1; k ++) {
+ writer.Write(pti[k] + " ");
+ }
+ //extra work to make sure no " " is at the end of the list of identifiers
+ if (pti.Count != 0) {
+ writer.Write(pti[pti.Count - 1]);
+ }
+ writer.Write("}");
+ }
+ // temp object needed for non-null checking
+ object tempPTVI = partitionToValue[i];
+ if (tempPTVI != null) {
+ if (tempPTVI.ToString() == "True") {
+ writer.Write(" -> " + "true" + "");
+ } else if (tempPTVI.ToString() == "False") {
+ writer.Write(" -> " + "false" + "");
+ } else if (tempPTVI is BigNum) {
+ writer.Write(" -> " + tempPTVI + ":int");
+ } else {
+ writer.Write(" -> " + tempPTVI + "");
+ }
+ } else {
+ writer.Write(" ");
+ }
+ writer.WriteLine();
+ }
+
+ writer.WriteLine("function interpretations:");
+ foreach (KeyValuePair<string!, List<List<int>>!> kvp in definedFunctions) {
+ writer.WriteLine(kvp.Key + " -> {");
+ List<List<int>> kvpValue = kvp.Value;
+ if (kvpValue != null) {
+ foreach(List<int> l in kvpValue) {
+ writer.Write(" ");
+ if (l != null) {
+ for (int i = 0; i < l.Count - 1; i++) {
+ writer.Write("*"+l[i] + " ");
+ }
+ if (l.Count == 1) {
+ writer.WriteLine("else -> #unspecified");
+ } else {
+ writer.WriteLine("-> "+ "*" + l[l.Count - 1]);
+ }
+ }
+ }
+ }
+ writer.WriteLine("}");
+ }
+ writer.WriteLine("END_OF_MODEL");
+ writer.WriteLine(".");
+
+ if (CommandLineOptions.Clo.PrintErrorModel >= 2) {
+ writer.WriteLine("identifierToPartition:");
+ foreach (KeyValuePair<string!, int> kvp in identifierToPartition) {
+ writer.WriteLine(kvp.Key + " : " + "*" + kvp.Value);
+ }
+
+ writer.WriteLine("valueToPartition:");
+ foreach (KeyValuePair<object, int> kvp in valueToPartition) {
+ writer.WriteLine(kvp.Key + " : " + "*" + kvp.Value);
+ }
+ writer.WriteLine("End of model.");
+ }
+ }
+ }
+
+
+}
+
+
diff --git a/Source/Provers/Z3/ProverInterface.ssc b/Source/Provers/Z3/ProverInterface.ssc
new file mode 100644
index 00000000..02b4f505
--- /dev/null
+++ b/Source/Provers/Z3/ProverInterface.ssc
@@ -0,0 +1,320 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Threading;
+using System.IO;
+using System.Text;
+using ExternalProver;
+using System.Diagnostics;
+using Microsoft.Contracts;
+using Microsoft.Boogie.AbstractInterpretation;
+using Microsoft.Boogie;
+using Microsoft.Boogie.VCExprAST;
+using Microsoft.Boogie.Clustering;
+using Microsoft.Boogie.TypeErasure;
+using Microsoft.Boogie.Z3;
+using Microsoft.Boogie.Simplify;
+
+namespace Microsoft.Boogie.Z3
+{
+ public class Z3ProcessTheoremProver : ProcessTheoremProver
+ {
+ private Z3InstanceOptions! opts;
+ private Inspector inspector;
+
+ [NotDelayed]
+ public Z3ProcessTheoremProver(VCExpressionGenerator! gen,
+ DeclFreeProverContext! ctx, Z3InstanceOptions! opts)
+ throws UnexpectedProverOutputException;
+ {
+ this.opts = opts;
+ string! backPred = opts.Typed ? "TypedUnivBackPred2.sx" : "UnivBackPred2.sx";
+ base(opts, gen, ctx, opts.ExeName, backPred);
+ }
+
+ private void FireUpInspector()
+ {
+ if (inspector == null && opts.Inspector != null) {
+ inspector = new Inspector(opts);
+ }
+ }
+
+ protected override Microsoft.Boogie.Simplify.ProverProcess! CreateProverProcess(string! proverPath) {
+ opts.ExeName = proverPath;
+ FireUpInspector();
+ if (inspector != null) {
+ inspector.NewProver();
+ }
+ return new Z3ProverProcess(opts, inspector);
+ }
+
+ protected override AxiomVCExprTranslator! SpawnVCExprTranslator() {
+ return new Z3VCExprTranslator(gen, opts);
+ }
+
+ public override void BeginCheck(string! descriptiveName, VCExpr! vc, ErrorHandler! handler)
+ {
+ FireUpInspector();
+ if (inspector != null) {
+ inspector.NewProblem(descriptiveName, vc, handler);
+ }
+ base.BeginCheck(descriptiveName, vc, handler);
+ }
+ }
+
+ public class Z3InstanceOptions : ProverOptions
+ {
+ public int Timeout { get { return TimeLimit / 1000; } }
+ public bool Typed {
+ get {
+ return CommandLineOptions.Clo.Z3types || BitVectors == CommandLineOptions.BvHandling.Z3Native;
+ }
+ }
+ public bool V1 = false;
+ public bool V2 = false; // default set in PostParse
+ public bool DistZ3 = false;
+ public string! ExeName = "z3.exe";
+ public bool InverseImplies = false;
+ public string? Inspector = null;
+
+ protected override bool Parse(string! opt)
+ {
+ return ParseBool(opt, "V1", ref V1) ||
+ ParseBool(opt, "V2", ref V2) ||
+ ParseBool(opt, "REVERSE_IMPLIES", ref InverseImplies) ||
+ ParseString(opt, "INSPECTOR", ref Inspector) ||
+ ParseBool(opt, "DIST", ref DistZ3) ||
+ base.Parse(opt);
+ }
+
+ protected override void PostParse()
+ {
+ base.PostParse();
+
+ if (!V1 && !V2) {
+ V2 = true; // default
+ } else if (V1 && V2) {
+ ReportError("V1 and V2 requested at the same time");
+ } else if (V1 && DistZ3) {
+ ReportError("V1 and DistZ3 requested at the same time");
+ }
+ if (V1) {
+ ExeName = "z3-v1.3.exe";
+ }
+ if (DistZ3) {
+ ExeName = "z3-dist.exe";
+ CommandLineOptions.Clo.RestartProverPerVC = true;
+ }
+
+ }
+ }
+
+ internal class Z3LineariserOptions : LineariserOptions {
+ private readonly Z3InstanceOptions! opts;
+
+ public override CommandLineOptions.BvHandling Bitvectors { get {
+ return opts.BitVectors;
+ } }
+
+ internal Z3LineariserOptions(bool asTerm, Z3InstanceOptions !opts, List<VCExprVar!>! letVariables) {
+ base(asTerm);
+ this.opts = opts;
+ this.LetVariablesAttr = letVariables;
+ }
+
+ public override bool UseWeights { get {
+ return opts.V2;
+ } }
+
+ public override bool UseTypes { get {
+ return opts.Typed;
+ } }
+
+ public override bool QuantifierIds { get {
+ return true;
+ } }
+
+ public override bool InverseImplies { get {
+ return opts.InverseImplies;
+ } }
+
+ public override LineariserOptions! SetAsTerm(bool newVal) {
+ if (newVal == AsTerm)
+ return this;
+ return new Z3LineariserOptions(newVal, opts, LetVariables);
+ }
+
+ // variables representing formulas in let-bindings have to be
+ // printed in a different way than other variables
+ private readonly List<VCExprVar!>! LetVariablesAttr;
+ public override List<VCExprVar!>! LetVariables { get {
+ return LetVariablesAttr;
+ } }
+
+ public override LineariserOptions! AddLetVariable(VCExprVar! furtherVar) {
+ List<VCExprVar!>! allVars = new List<VCExprVar!> ();
+ allVars.AddRange(LetVariables);
+ allVars.Add(furtherVar);
+ return new Z3LineariserOptions(AsTerm, opts, allVars);
+ }
+ }
+
+ // -----------------------------------------------------------------------------------------------
+ // -----------------------------------------------------------------------------------------------
+ // -----------------------------------------------------------------------------------------------
+
+ public class Z3VCExprTranslator : AxiomVCExprTranslator {
+ public Z3VCExprTranslator(VCExpressionGenerator! gen, Z3InstanceOptions! opts) {
+ Gen = gen;
+ Opts = opts;
+ TypeAxiomBuilder! axBuilder;
+ switch (CommandLineOptions.Clo.TypeEncodingMethod) {
+ case CommandLineOptions.TypeEncoding.Arguments:
+ axBuilder = new TypeAxiomBuilderArguments (gen);
+ break;
+ default:
+ axBuilder = new TypeAxiomBuilderPremisses (gen);
+ break;
+ }
+ axBuilder.Setup();
+ AxBuilder = axBuilder;
+ UniqueNamer namer = new UniqueNamer ();
+ Namer = namer;
+ this.DeclCollector =
+ new TypeDeclCollector (namer, opts.BitVectors == CommandLineOptions.BvHandling.Z3Native);
+ }
+
+ private Z3VCExprTranslator(Z3VCExprTranslator! tl) {
+ base(tl);
+ Gen = tl.Gen;
+ Opts = tl.Opts; // we assume that the options have not changed
+ AxBuilder = (TypeAxiomBuilder)tl.AxBuilder.Clone();
+ UniqueNamer namer = (UniqueNamer)tl.Namer.Clone();
+ Namer = namer;
+ DeclCollector = new TypeDeclCollector (namer, tl.DeclCollector);
+ }
+
+ public override Object! Clone() {
+ return new Z3VCExprTranslator(this);
+ }
+
+ private readonly Z3InstanceOptions! Opts;
+ private readonly VCExpressionGenerator! Gen;
+ private readonly TypeAxiomBuilder! AxBuilder;
+ private readonly UniqueNamer! Namer;
+ private readonly TypeDeclCollector! DeclCollector;
+
+ public override string! translate(VCExpr! expr, int polarity) {
+ DateTime start = DateTime.Now;
+ if (CommandLineOptions.Clo.Trace)
+ Console.Write("Linearising ... ");
+
+ // handle the types in the VCExpr
+ TypeEraser eraser;
+ switch (CommandLineOptions.Clo.TypeEncodingMethod) {
+ case CommandLineOptions.TypeEncoding.Arguments:
+ eraser = new TypeEraserArguments((TypeAxiomBuilderArguments)AxBuilder, Gen);
+ break;
+ case CommandLineOptions.TypeEncoding.Monomorphic:
+ eraser = null;
+ break;
+ default:
+ eraser = new TypeEraserPremisses((TypeAxiomBuilderPremisses)AxBuilder, Gen);
+ break;
+ }
+
+ VCExpr! exprWithoutTypes = eraser != null ? eraser.Erase(expr, polarity) : expr;
+
+ LetBindingSorter! letSorter = new LetBindingSorter(Gen);
+ VCExpr! sortedExpr = letSorter.Mutate(exprWithoutTypes, true);
+ VCExpr! sortedAxioms = letSorter.Mutate(AxBuilder.GetNewAxioms(), true);
+
+ if (Opts.Typed) {
+ DeclCollector.Collect(sortedAxioms);
+ DeclCollector.Collect(sortedExpr);
+ FeedTypeDeclsToProver();
+ } else {
+ TermFormulaFlattener! flattener = new TermFormulaFlattener (Gen);
+ sortedExpr = flattener.Flatten(sortedExpr);
+ sortedAxioms = flattener.Flatten(sortedAxioms);
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ //SubtermCollector! coll = new SubtermCollector (gen);
+ //coll.Traverse(sortedExpr, true);
+ //coll.Traverse(sortedAxioms, true);
+ //coll.UnifyClusters();
+ //Console.WriteLine(coll);
+ //////////////////////////////////////////////////////////////////////////
+
+ LineariserOptions linOptions = new Z3LineariserOptions(false, Opts, new List<VCExprVar!>());
+
+ AddAxiom(SimplifyLikeExprLineariser.ToString(sortedAxioms, linOptions, Namer));
+
+ string! res = SimplifyLikeExprLineariser.ToString(sortedExpr, linOptions, Namer);
+
+ if (CommandLineOptions.Clo.Trace) {
+ TimeSpan elapsed = DateTime.Now - start;
+ Console.WriteLine("finished [{0} s] ", elapsed.TotalSeconds);
+ }
+ return res;
+ }
+
+ private void FeedTypeDeclsToProver() {
+ foreach (string! s in DeclCollector.GetNewDeclarations())
+ AddTypeDecl(s);
+ }
+ }
+
+ // -----------------------------------------------------------------------------------------------
+ // -----------------------------------------------------------------------------------------------
+ // -----------------------------------------------------------------------------------------------
+
+ public class Factory : ProverFactory
+ {
+
+ public override object! SpawnProver(ProverOptions! popts, object! ctxt)
+ {
+ Z3InstanceOptions opts = (Z3InstanceOptions!)popts;
+ return this.SpawnProver(((DeclFreeProverContext!)ctxt).ExprGen,
+ (DeclFreeProverContext!)ctxt,
+ opts);
+ }
+
+ public override object! NewProverContext(ProverOptions! options) {
+ if (CommandLineOptions.Clo.BracketIdsInVC < 0) {
+ CommandLineOptions.Clo.BracketIdsInVC = 0;
+ }
+
+ Z3InstanceOptions opts = (Z3InstanceOptions!)options;
+ VCExpressionGenerator! gen = new VCExpressionGenerator ();
+ List<string!>! proverCommands = new List<string!> ();
+ proverCommands.Add("all");
+ proverCommands.Add("z3");
+ proverCommands.Add("simplifyLike");
+ if (opts.BitVectors == CommandLineOptions.BvHandling.Z3Native)
+ proverCommands.Add("bvDefSem");
+ if (opts.BitVectors == CommandLineOptions.BvHandling.ToInt)
+ proverCommands.Add("bvInt");
+ VCGenerationOptions! genOptions = new VCGenerationOptions(proverCommands);
+
+ return new DeclFreeProverContext(gen, genOptions);
+ }
+
+ public override ProverOptions! BlankProverOptions()
+ {
+ return new Z3InstanceOptions();
+ }
+
+ protected virtual Z3ProcessTheoremProver! SpawnProver(VCExpressionGenerator! gen,
+ DeclFreeProverContext! ctx,
+ Z3InstanceOptions! opts) {
+ return new Z3ProcessTheoremProver(gen, ctx, opts);
+ }
+ }
+}
diff --git a/Source/Provers/Z3/TypeDeclCollector.ssc b/Source/Provers/Z3/TypeDeclCollector.ssc
new file mode 100644
index 00000000..86307b45
--- /dev/null
+++ b/Source/Provers/Z3/TypeDeclCollector.ssc
@@ -0,0 +1,212 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using Microsoft.Contracts;
+using Microsoft.Boogie.VCExprAST;
+
+namespace Microsoft.Boogie.Z3
+{
+ // Visitor for collecting the occurring function symbols in a VCExpr,
+ // and for creating the corresponding declarations that can be fed into
+ // Z3
+
+ // (should this be rather done by Context.DeclareFunction? right now,
+ // the TypeErasure visitor introduces new function symbols that are
+ // not passed to this method)
+
+ public class TypeDeclCollector : BoundVarTraversingVCExprVisitor<bool, bool> {
+
+ private readonly UniqueNamer! Namer;
+
+ private readonly bool NativeBv;
+
+ public TypeDeclCollector(UniqueNamer! namer, bool nativeBv) {
+ this.Namer = namer;
+ this.NativeBv = nativeBv;
+ AllDecls = new List<string!> ();
+ IncDecls = new List<string!> ();
+ KnownFunctions = new Dictionary<Function!, bool> ();
+ KnownVariables = new Dictionary<VCExprVar!, bool> ();
+ KnownTypes = new Dictionary<Type!, bool> ();
+ KnownBvOps = new Dictionary<string!, bool> ();
+ }
+
+ internal TypeDeclCollector(UniqueNamer! namer, TypeDeclCollector! coll) {
+ this.Namer = namer;
+ this.NativeBv = coll.NativeBv;
+ AllDecls = new List<string!> (coll.AllDecls);
+ IncDecls = new List<string!> (coll.IncDecls);
+ KnownFunctions = new Dictionary<Function!, bool> (coll.KnownFunctions);
+ KnownVariables = new Dictionary<VCExprVar!, bool> (coll.KnownVariables);
+ KnownTypes = new Dictionary<Type!, bool> (coll.KnownTypes);
+ KnownBvOps = new Dictionary<string!, bool> (coll.KnownBvOps);
+ }
+
+ // not used
+ protected override bool StandardResult(VCExpr! node, bool arg) {
+ return true;
+ }
+
+ private readonly List<string!>! AllDecls;
+ private readonly List<string!>! IncDecls;
+
+ private readonly IDictionary<Function!, bool>! KnownFunctions;
+ private readonly IDictionary<VCExprVar!, bool>! KnownVariables;
+
+ // bitvector types have to be registered as well
+ private readonly IDictionary<Type!, bool>! KnownTypes;
+
+ // the names of registered BvConcatOps and BvExtractOps
+ private readonly IDictionary<string!, bool>! KnownBvOps;
+
+ public List<string!>! AllDeclarations { get {
+ List<string!>! res = new List<string!> ();
+ res.AddRange(AllDecls);
+ return res;
+ } }
+
+ public List<string!>! GetNewDeclarations() {
+ List<string!>! res = new List<string!> ();
+ res.AddRange(IncDecls);
+ IncDecls.Clear();
+ return res;
+ }
+
+ private void AddDeclaration(string! decl) {
+ AllDecls.Add(decl);
+ IncDecls.Add(decl);
+ }
+
+ public void Collect(VCExpr! expr) {
+ Traverse(expr, true);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ private static string! TypeToString(Type! t) {
+ return SimplifyLikeExprLineariser.TypeToString(t);
+ }
+
+ private void RegisterType(Type! type)
+ {
+ if (!type.IsBv || !NativeBv) return;
+
+ if (!KnownTypes.ContainsKey(type)) {
+ int bits = type.BvBits;
+ string name = TypeToString(type);
+
+ AddDeclaration("(DEFTYPE " + name + " :BUILTIN BitVec " + bits + ")");
+ // If we add the BUILTIN then the conversion axiom does not work
+ AddDeclaration("(DEFOP " + name + "_to_int " + name + " $int)"); // :BUILTIN bv2int $int)");
+ AddDeclaration("(DEFOP $make_bv" + bits + " $int " + name + " :BUILTIN int2bv " + bits + ")");
+ string! expr = "($make_bv" + bits + " (" + name + "_to_int x))";
+ AddDeclaration("(BG_PUSH (FORALL (x :TYPE " + name + ") (PATS "
+ + expr + ") (QID bvconv" + bits + ") (EQ " + expr + " x)))");
+
+ KnownTypes.Add(type, true);
+ }
+ }
+
+ public override bool Visit(VCExprNAry! node, bool arg) {
+ // there are a couple of cases where operators have to be
+ // registered by generating appropriate Z3 statements
+
+ if (node.Op.Equals(VCExpressionGenerator.BvConcatOp)) {
+ //
+ if (NativeBv) {
+ RegisterType(node[0].Type);
+ RegisterType(node[1].Type);
+ RegisterType(node.Type);
+
+ string name = SimplifyLikeExprLineariser.BvConcatOpName(node);
+ if (!KnownBvOps.ContainsKey(name)) {
+ AddDeclaration("(DEFOP " + name +
+ " " + TypeToString(node[0].Type) +
+ " " + TypeToString(node[1].Type) +
+ " " + TypeToString(node.Type) +
+ " :BUILTIN concat)");
+ KnownBvOps.Add(name, true);
+ }
+ }
+ //
+ } else if (node.Op is VCExprBvExtractOp) {
+ //
+ if (NativeBv) {
+ RegisterType(node[0].Type);
+ RegisterType(node.Type);
+
+ VCExprBvExtractOp! op = (VCExprBvExtractOp)node.Op;
+ string name = SimplifyLikeExprLineariser.BvExtractOpName(node);
+ if (!KnownBvOps.ContainsKey(name)) {
+ AddDeclaration("(DEFOP " + name +
+ " " + TypeToString(node[0].Type) +
+ " " + TypeToString(node.Type) +
+ " :BUILTIN extract " +
+ (op.End - 1) + " " + op.Start + ")");
+ KnownBvOps.Add(name, true);
+ }
+ }
+ //
+ } else {
+ //
+ VCExprBoogieFunctionOp op = node.Op as VCExprBoogieFunctionOp;
+ if (op != null && !KnownFunctions.ContainsKey(op.Func)) {
+ Function! f = op.Func;
+ string! printedName = Namer.GetName(f, f.Name);
+ string! decl = "(DEFOP " + SimplifyLikeExprLineariser.MakeIdPrintable(printedName);
+
+ foreach (Variable! v in f.InParams) {
+ decl += " " + TypeToString(v.TypedIdent.Type);
+ RegisterType(v.TypedIdent.Type);
+ }
+ assert f.OutParams.Length == 1;
+ foreach (Variable! v in f.OutParams) {
+ decl += " " + TypeToString(v.TypedIdent.Type);
+ RegisterType(v.TypedIdent.Type);
+ }
+
+ string? builtin = ExtractBuiltin(f);
+ if (builtin != null)
+ decl += " :BUILTIN " + builtin;
+
+ decl += ")";
+
+ AddDeclaration(decl);
+ KnownFunctions.Add(f, true);
+ }
+ //
+ }
+
+ return base.Visit(node, arg);
+ }
+
+ private string? ExtractBuiltin(Function! f) {
+ if (NativeBv) {
+ return f.FindStringAttribute("bvbuiltin");
+ } else {
+ return null;
+ }
+ }
+
+ public override bool Visit(VCExprVar! node, bool arg) {
+ if (!BoundTermVars.Contains(node) && !KnownVariables.ContainsKey(node)) {
+ RegisterType(node.Type);
+ string! printedName = Namer.GetName(node, node.Name);
+ string! decl =
+ "(DEFOP " + SimplifyLikeExprLineariser.MakeIdPrintable(printedName)
+ + " " + TypeToString(node.Type) + ")";
+ AddDeclaration(decl);
+ KnownVariables.Add(node, true);
+ }
+
+ return base.Visit(node, arg);
+ }
+ }
+
+} \ No newline at end of file
diff --git a/Source/Provers/Z3/Z3.sscproj b/Source/Provers/Z3/Z3.sscproj
new file mode 100644
index 00000000..5370a640
--- /dev/null
+++ b/Source/Provers/Z3/Z3.sscproj
@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="utf-8"?>
+<VisualStudioProject>
+ <XEN ProjectType="Local"
+ SchemaVersion="1.0"
+ Name="Z3"
+ ProjectGuid="f75666de-cd56-457c-8782-09be243450fc"
+ >
+ <Build>
+ <Settings ApplicationIcon=""
+ AssemblyName="Provers.Z3"
+ OutputType="Library"
+ RootNamespace="Microsoft.Boogie.Z3"
+ StartupObject=""
+ StandardLibraryLocation=""
+ TargetPlatform="v2"
+ TargetPlatformLocation=""
+ ShadowedAssembly=""
+ NoStandardLibraries="False"
+ >
+ <Config Name="Debug"
+ AllowUnsafeBlocks="False"
+ BaseAddress="285212672"
+ CheckForOverflowUnderflow="False"
+ ConfigurationOverrideFile=""
+ DefineConstants="DEBUG;TRACE"
+ DocumentationFile=""
+ DebugSymbols="True"
+ FileAlignment="4096"
+ IncrementalBuild="True"
+ Optimize="False"
+ OutputPath="bin\debug"
+ RegisterForComInterop="False"
+ RemoveIntegerChecks="false"
+ TreatWarningsAsErrors="False"
+ WarningLevel="4"
+ ReferenceTypesAreNonNullByDefault="False"
+ RunProgramVerifier="False"
+ RunProgramVerifierWhileEditing="False"
+ ProgramVerifierCommandLineOptions=""
+ AllowPointersToManagedStructures="False"
+ CheckContractAdmissibility="True"
+ CheckPurity="False"
+ />
+ <Config Name="Release"
+ AllowUnsafeBlocks="false"
+ BaseAddress="285212672"
+ CheckForOverflowUnderflow="false"
+ ConfigurationOverrideFile=""
+ DefineConstants="TRACE"
+ DocumentationFile=""
+ DebugSymbols="false"
+ FileAlignment="4096"
+ IncrementalBuild="false"
+ Optimize="true"
+ OutputPath="bin\release"
+ RegisterForComInterop="false"
+ RemoveIntegerChecks="false"
+ TreatWarningsAsErrors="false"
+ WarningLevel="4"
+ />
+ </Settings>
+ <References>
+ <Reference Name="System"
+ AssemblyName="System"
+ Private="false"
+ />
+ <Reference Name="System.Data"
+ AssemblyName="System.Data"
+ Private="false"
+ />
+ <Reference Name="System.Xml"
+ AssemblyName="System.Xml"
+ Private="false"
+ />
+ <Reference Name="Core"
+ Project="{47BC34F1-A173-40BE-84C2-9332B4418387}"
+ Private="true"
+ />
+ <Reference Name="AIFramework"
+ Project="{24B55172-AD8B-47D1-8952-5A95CFDB9B31}"
+ Private="true"
+ />
+ <Reference Name="Graph"
+ Project="{4C28FB90-630E-4B55-A937-11A011B79765}"
+ Private="true"
+ />
+ <Reference Name="Mscorlib.Contracts"
+ AssemblyName="Mscorlib.Contracts"
+ Private="false"
+ HintPath="../../../Binaries/Mscorlib.Contracts.dll"
+ />
+ <Reference Name="System.Contracts"
+ AssemblyName="System.Contracts"
+ Private="false"
+ HintPath="../../../Binaries/System.Contracts.dll"
+ />
+ <Reference Name="VCGeneration"
+ Project="{F65666DE-FB56-457C-8782-09BE243450FC}"
+ Private="true"
+ />
+ <Reference Name="Simplify"
+ Project="{F75666DE-FB56-457C-8782-09BE243450FC}"
+ Private="true"
+ />
+ <Reference Name="Basetypes"
+ Project="{0C692837-77EC-415F-BF04-395E3ED06E9A}"
+ Private="true"
+ />
+ <Reference Name="VCExpr"
+ Project="{CF42B700-10AA-4DA9-8992-48A800251C11}"
+ Private="true"
+ />
+ </References>
+ </Build>
+ <Files>
+ <Include>
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="Prover.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="ProverInterface.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="..\..\version.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="TypeDeclCollector.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="Inspector.ssc"
+ />
+ </Include>
+ </Files>
+ </XEN>
+</VisualStudioProject> \ No newline at end of file
diff --git a/Source/VCExpr/BigLiteralAbstracter.ssc b/Source/VCExpr/BigLiteralAbstracter.ssc
new file mode 100644
index 00000000..fac34a8d
--- /dev/null
+++ b/Source/VCExpr/BigLiteralAbstracter.ssc
@@ -0,0 +1,198 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Text;
+using System.IO;
+using System.Collections;
+using System.Collections.Generic;
+using Microsoft.Contracts;
+using Microsoft.Basetypes;
+
+// Code for replacing large integer literals in VCExpr with
+// constants. This is necessary for Simplify, which cannot deal with
+// literals larger than 32 bits
+
+namespace Microsoft.Boogie.VCExprAST
+{
+
+ public class BigLiteralAbstracter : MutatingVCExprVisitor<bool>, ICloneable {
+
+ public BigLiteralAbstracter(VCExpressionGenerator! gen) {
+ base(gen);
+ DummyVar = gen.Variable("x", Type.Int);
+ IncAxioms = new List<VCExpr!> ();
+ Literals = new List<KeyValuePair<BigNum, VCExprVar!>> ();
+ }
+
+ private BigLiteralAbstracter(BigLiteralAbstracter! abstracter) {
+ base(abstracter.Gen);
+ DummyVar = abstracter.DummyVar;
+ IncAxioms = new List<VCExpr!> (abstracter.IncAxioms);
+ Literals = new List<KeyValuePair<BigNum, VCExprVar!>> (abstracter.Literals);
+ }
+
+ public Object! Clone() {
+ return new BigLiteralAbstracter(this);
+ }
+
+ private static readonly BigNum ConstantDistance = BigNum.FromLong(100000);
+ private static readonly BigNum NegConstantDistance = BigNum.FromLong(-100000);
+ // distance twice plus one
+ private static readonly BigNum ConstantDistanceTPO = BigNum.FromLong(200001);
+ private static readonly BigNum ConstantDistancePO = BigNum.FromLong(100001);
+
+ public VCExpr! Abstract(VCExpr! expr) {
+ return Mutate(expr, true);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ // list in which axioms are incrementally collected
+ private readonly List<VCExpr!>! IncAxioms;
+
+ private void AddAxiom(VCExpr! axiom) {
+ IncAxioms.Add(axiom);
+ }
+
+ // Return all axioms that were added since the last time NewAxioms
+ // was called
+ public VCExpr! GetNewAxioms() {
+ VCExpr! res = Gen.NAry(VCExpressionGenerator.AndOp, IncAxioms);
+ IncAxioms.Clear();
+ return res;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ // All named integer literals known to the visitor, in ascending
+ // order. Such literals are always positive, and the distance
+ // between two literals is always more than ConstantDistance.
+ private readonly List<KeyValuePair<BigNum, VCExprVar!>>! Literals;
+
+ private class EntryComparerC : IComparer<KeyValuePair<BigNum, VCExprVar!>> {
+ public int Compare(KeyValuePair<BigNum, VCExprVar!> a,
+ KeyValuePair<BigNum, VCExprVar!> b) {
+ return a.Key.CompareTo(b.Key);
+ }
+ }
+
+ private static readonly EntryComparerC EntryComparer = new EntryComparerC ();
+
+ // variable used when searching for entries in the literal list
+ private readonly VCExprVar! DummyVar;
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ // Construct an expression to represent the given (large) integer
+ // literal. Constants are defined and axiomatised if necessary
+ private VCExpr! Represent(BigNum lit)
+ requires NegConstantDistance > lit || lit > ConstantDistance; {
+
+ if (lit.IsNegative)
+ return Gen.Function(VCExpressionGenerator.SubOp,
+ Gen.Integer(BigNum.ZERO), RepresentPos(lit.Neg));
+ else
+ return RepresentPos(lit);
+ }
+
+ private VCExpr! RepresentPos(BigNum lit)
+ requires lit > ConstantDistance; {
+
+ int index = GetIndexFor(lit);
+ if (index >= 0)
+ // precise match
+ return Literals[index].Value;
+
+ // check whether a constant is defined that is at most
+ // ConstantDistance away from lit
+ index = ~index;
+ VCExpr res = null;
+ BigNum resDistance = ConstantDistancePO;
+
+ if (index > 0) {
+ BigNum dist = lit - Literals[index - 1].Key;
+ if (dist < resDistance) {
+ resDistance = dist;
+ res = Gen.Function(VCExpressionGenerator.AddOp,
+ Literals[index - 1].Value, Gen.Integer(dist));
+ }
+ }
+
+ if (index < Literals.Count) {
+ BigNum dist = Literals[index].Key - lit;
+ if (dist < resDistance) {
+ resDistance = dist;
+ res = Gen.Function(VCExpressionGenerator.SubOp,
+ Literals[index].Value, Gen.Integer(dist));
+ }
+ }
+
+ if (res != null)
+ return res;
+
+ // otherwise, define a new constant to represent this literal
+ return AddConstantFor(lit);
+ }
+
+ private VCExpr! AddConstantFor(BigNum lit)
+ requires lit > ConstantDistance; {
+
+ VCExprVar! res = Gen.Variable("int#" + lit, Type.Int);
+ int index = GetIndexFor(lit);
+ assert index < 0;
+ index = ~index;
+
+ Literals.Insert(index, new KeyValuePair<BigNum, VCExprVar!>(lit, res));
+
+ // relate the new constant to the predecessor and successor
+ if (index > 0)
+ DefineRelationship(Literals[index - 1].Value, Literals[index - 1].Key,
+ res, lit);
+ else
+ DefineRelationship(Gen.Integer(BigNum.ZERO), BigNum.ZERO, res, lit);
+
+ if (index < Literals.Count - 1)
+ DefineRelationship(res, lit,
+ Literals[index + 1].Value, Literals[index + 1].Key);
+
+ return res;
+ }
+
+ private void DefineRelationship(VCExpr! aExpr, BigNum aValue,
+ VCExpr! bExpr, BigNum bValue)
+ requires aValue < bValue; {
+
+ BigNum dist = bValue - aValue;
+ VCExpr! distExpr = Gen.Function(VCExpressionGenerator.SubOp, bExpr, aExpr);
+ if (dist <= ConstantDistanceTPO)
+ // constants that are sufficiently close to each other are put
+ // into a precise relationship
+ AddAxiom(Gen.Eq(distExpr, Gen.Integer(dist)));
+ else
+ AddAxiom(Gen.Function(VCExpressionGenerator.GtOp,
+ distExpr, Gen.Integer(ConstantDistanceTPO)));
+ }
+
+ private int GetIndexFor(BigNum lit) {
+ return Literals.BinarySearch(new KeyValuePair<BigNum, VCExprVar!>
+ (lit, DummyVar),
+ EntryComparer);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ public override VCExpr! Visit(VCExprLiteral! node, bool arg) {
+ VCExprIntLit intLit = node as VCExprIntLit;
+ if (intLit != null) {
+ if (NegConstantDistance > intLit.Val || intLit.Val > ConstantDistance)
+ return Represent(intLit.Val);
+ }
+ return node;
+ }
+
+ }
+
+} \ No newline at end of file
diff --git a/Source/VCExpr/Boogie2VCExpr.ssc b/Source/VCExpr/Boogie2VCExpr.ssc
new file mode 100644
index 00000000..b344c03d
--- /dev/null
+++ b/Source/VCExpr/Boogie2VCExpr.ssc
@@ -0,0 +1,853 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Text;
+using System.IO;
+using System.Collections;
+using System.Collections.Generic;
+using Microsoft.Contracts;
+using Microsoft.Basetypes;
+
+// A translator from the Boogie AST to the VCExpr AST.
+// This was previously realised in the methods AbsyExpr.VCView
+
+namespace Microsoft.Boogie.VCExprAST
+{
+ using Microsoft.Boogie;
+
+ public class VCGenerationOptions {
+ private readonly List<string!>! SupportedProverCommands;
+
+ public bool IsProverCommandSupported(string! kind)
+ {
+ return SupportedProverCommands.Contains(kind);
+ }
+
+ public bool IsAnyProverCommandSupported(string! kinds)
+ {
+ if (kinds.IndexOf(',') < 0) {
+ return IsProverCommandSupported(kinds);
+ } else {
+ return exists{string k in kinds.Split(',', ' '); IsProverCommandSupported(k)};
+ }
+ }
+
+ public VCGenerationOptions(List<string!>! supportedProverCommands) {
+ this.SupportedProverCommands = supportedProverCommands;
+ }
+ }
+
+ public class Boogie2VCExprTranslator : StandardVisitor, ICloneable {
+ // Stack on which the various Visit-methods put the result of the translation
+ private readonly Stack<VCExpr!>! SubExpressions = new Stack<VCExpr!> ();
+
+ private void Push(VCExpr! expr) {
+ SubExpressions.Push(expr);
+ }
+
+ private VCExpr! Pop() {
+ return SubExpressions.Pop();
+ }
+
+ public VCExpr! Translate(Expr! expr) {
+ this.Visit(expr);
+ return Pop();
+ }
+
+ public List<VCExpr!>! Translate(ExprSeq! exprs) {
+ List<VCExpr!>! res = new List<VCExpr!> ();
+ foreach(Expr e in exprs)
+ res.Add(Translate((!)e));
+ return res;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////
+
+ internal readonly VCExpressionGenerator! Gen;
+
+ public Boogie2VCExprTranslator(VCExpressionGenerator! gen,
+ VCGenerationOptions! genOptions) {
+ this.Gen = gen;
+ this.GenerationOptions = genOptions;
+ UnboundVariables = new VariableMapping<Variable> ();
+ BoundVariables = new VariableMapping<BoundVariable> ();
+ Formals = new VariableMapping<Formal> ();
+ }
+
+ private Boogie2VCExprTranslator(Boogie2VCExprTranslator! tl) {
+ this.Gen = tl.Gen;
+ this.GenerationOptions = tl.GenerationOptions;
+ UnboundVariables =
+ (VariableMapping<Variable>)tl.UnboundVariables.Clone();
+ BoundVariables = new VariableMapping<BoundVariable> ();
+ Formals = new VariableMapping<Formal> ();
+ }
+
+ public object! Clone() {
+ return new Boogie2VCExprTranslator(this);
+ }
+
+ private IAppliableTranslator IAppTranslatorAttr = null;
+ private IAppliableTranslator! IAppTranslator { get {
+ if (IAppTranslatorAttr == null)
+ IAppTranslatorAttr = new IAppliableTranslator (this);
+ return IAppTranslatorAttr;
+ } }
+
+ ///////////////////////////////////////////////////////////////////////////////
+ // Class for handling occurring variables
+
+ private class VariableMapping<VarKind> : ICloneable {
+ private readonly List<Dictionary<VarKind!, VCExprVar!>!>! Mapping;
+
+ public VariableMapping() {
+ List<Dictionary<VarKind!, VCExprVar!>!>! mapping =
+ new List<Dictionary<VarKind!, VCExprVar!>!> ();
+ mapping.Add(new Dictionary<VarKind!, VCExprVar!> ());
+ this.Mapping = mapping;
+ }
+
+ private VariableMapping(VariableMapping<VarKind>! vm) {
+ List<Dictionary<VarKind!, VCExprVar!>!>! mapping =
+ new List<Dictionary<VarKind!, VCExprVar!>!> ();
+ foreach (Dictionary<VarKind!, VCExprVar!>! d in vm.Mapping)
+ mapping.Add(new Dictionary<VarKind!, VCExprVar!> (d));
+ this.Mapping = mapping;
+ }
+
+ public object! Clone() {
+ return new VariableMapping<VarKind> (this);
+ }
+
+ public void PushScope() {
+ Mapping.Add(new Dictionary<VarKind!, VCExprVar!> ());
+ }
+
+ public void PopScope() {
+ assume Mapping.Count > 0;
+ Mapping.RemoveAt(Mapping.Count - 1);
+ }
+
+ public void Bind(VarKind! boogieVar, VCExprVar! vcExprVar)
+ requires !Contains(boogieVar); {
+ Mapping[Mapping.Count - 1].Add(boogieVar, vcExprVar);
+ }
+
+ public VCExprVar! Lookup(VarKind! boogieVar) {
+ VCExprVar res = LookupHelp(boogieVar);
+ assume res != null;
+ return res;
+ }
+
+ [Pure]
+ public bool Contains(VarKind! boogieVar) {
+ return LookupHelp(boogieVar) != null;
+ }
+
+ public bool TryGetValue(VarKind! boogieVar, out VCExprVar res) {
+ res = LookupHelp(boogieVar);
+ return res != null;
+ }
+
+ [Pure]
+ private VCExprVar LookupHelp(VarKind! boogieVar) {
+ VCExprVar res;
+ foreach (Dictionary<VarKind!, VCExprVar!>! d in Mapping) {
+ if (d.TryGetValue(boogieVar, out res))
+ return res;
+ }
+ return null;
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////
+
+ private readonly VariableMapping<Variable>! UnboundVariables;
+ private readonly VariableMapping<BoundVariable>! BoundVariables;
+ // used when translating the bodies of function expansions
+ private readonly VariableMapping<Formal>! Formals;
+
+ internal void PushBoundVariableScope() {
+ BoundVariables.PushScope();
+ }
+ internal void PopBoundVariableScope() {
+ BoundVariables.PopScope();
+ }
+
+ internal void PushFormalsScope() {
+ Formals.PushScope();
+ }
+ internal void PopFormalsScope() {
+ Formals.PopScope();
+ }
+
+ public VCExprVar! BindVariable(Variable! boogieVar) {
+ if (boogieVar is BoundVariable) {
+ VCExprVar! newVar = Gen.Variable(boogieVar.Name, boogieVar.TypedIdent.Type);
+ BoundVariables.Bind((BoundVariable)boogieVar, newVar);
+ return newVar;
+ } else if (boogieVar is Formal) {
+ VCExprVar! newVar = Gen.Variable(boogieVar.Name, boogieVar.TypedIdent.Type);
+ Formals.Bind((Formal)boogieVar, newVar);
+ return newVar;
+ } else {
+ // only bound variables and formals are declared explicitly
+ assert false;
+ }
+ }
+
+ public VCExprVar! LookupVariable(Variable! boogieVar) {
+
+ BoundVariable bv = boogieVar as BoundVariable;
+ if (bv != null) {
+ return BoundVariables.Lookup(bv);
+ }
+ VCExprVar res;
+ Formal fml = boogieVar as Formal;
+ if (fml != null && Formals.TryGetValue(fml, out res))
+ return (!)res;
+
+ // global variables, local variables, incarnations, etc. are
+ // bound the first time they occur
+ if (!UnboundVariables.TryGetValue(boogieVar, out res)) {
+ res = new VCExprVar (boogieVar.Name, boogieVar.TypedIdent.Type);
+ UnboundVariables.Bind(boogieVar, res);
+ }
+ return (!)res;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////
+
+ internal readonly VCGenerationOptions! GenerationOptions;
+
+ ///////////////////////////////////////////////////////////////////////////////////
+
+ public override LiteralExpr! VisitLiteralExpr(LiteralExpr! node) {
+ Push(TranslateLiteralExpr(node));
+ return node;
+ }
+ private VCExpr! TranslateLiteralExpr(LiteralExpr! node) {
+ if ( node.Val is bool )
+ {
+ bool b = (bool) node.Val;
+ if ( b ) {
+ return VCExpressionGenerator.True;
+ } else {
+ return VCExpressionGenerator.False;
+ }
+ }
+ else if ( node.Val is BigNum )
+ {
+ return Gen.Integer(node.asBigNum);
+ }
+ else if ( node.Val is BvConst )
+ {
+ return Gen.Bitvector((BvConst)node.Val);
+ }
+ else
+ {
+ System.Diagnostics.Debug.Assert(false, "unknown kind of literal " + node.tok.ToString());
+ assert false;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////
+
+ public override AIVariableExpr! VisitAIVariableExpr(AIVariableExpr! node)
+ {
+ assert false;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////
+
+ public override Expr! VisitIdentifierExpr(IdentifierExpr! node) {
+ assume node.Decl != null; // the expression has to be resolved
+ Push(LookupVariable(node.Decl));
+ return node;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////
+
+ // Because of our scheme for numbering incarnations of variables, the pre-state
+ // value of a variable x is always just "x". (The first update to it in a method
+ // causes it to become "x0". So we just remove old expressions with a visitor
+ // before transforming it into a VCExpr.
+ public override Expr! VisitOldExpr(OldExpr! node)
+ {
+ assert false;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////
+
+ public override Expr! VisitNAryExpr(NAryExpr! node)
+ {
+ Push(TranslateNAryExpr(node));
+ return node;
+ }
+
+ public override Expr! VisitLoopPredicate(LoopPredicate! node)
+ {
+ return this.VisitNAryExpr(node);
+ }
+
+ private VCExpr! TranslateNAryExpr(NAryExpr! node) {
+ int n = node.Args.Length;
+ List<VCExpr!>! vcs = new List<VCExpr!> (n);
+ for(int i = 0; i < n; i++)
+ {
+ vcs.Add(Translate((!)node.Args[i]));
+ }
+
+ if (node.Type == null) {
+ System.Console.WriteLine("*** type is null for {0}", node);
+ assert false;
+ }
+
+ return IAppTranslator.Translate(node.Fun, node.Type, vcs,
+ ToList((!)node.TypeParameters));
+ }
+
+
+ private static List<Type!>! EMPTY_TYPE_LIST = new List<Type!> ();
+
+ private List<Type!>! ToList(TypeParamInstantiation! insts) {
+ if (insts.FormalTypeParams.Count == 0)
+ return EMPTY_TYPE_LIST;
+
+ List<Type!>! typeArgs = new List<Type!> ();
+ foreach (TypeVariable! var in insts.FormalTypeParams)
+ typeArgs.Add(insts[var]);
+ return typeArgs;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////
+
+ public override QuantifierExpr! VisitQuantifierExpr(QuantifierExpr! node) {
+ Push(TranslateQuantifierExpr(node));
+ return node;
+ }
+
+ public override ExistsExpr! VisitExistsExpr(ExistsExpr! node)
+ {
+ node = (ExistsExpr) this.VisitQuantifierExpr(node);
+ return node;
+ }
+
+ public override ForallExpr! VisitForallExpr(ForallExpr! node)
+ {
+ node = (ForallExpr) this.VisitQuantifierExpr(node);
+ return node;
+ }
+
+ private VCExpr! TranslateQuantifierExpr(QuantifierExpr! node)
+ {
+ List<TypeVariable!>! typeParams = new List<TypeVariable!> ();
+ foreach (TypeVariable! v in node.TypeParameters)
+ typeParams.Add(v);
+
+ PushBoundVariableScope();
+
+ List<VCExprVar!>! boundVars = new List<VCExprVar!> ();
+ foreach (Variable! v in node.Dummies)
+ boundVars.Add(BindVariable(v));
+
+ try {
+ List<VCTrigger!>! triggers = TranslateTriggers(node.Triggers);
+ VCExpr! body = Translate(node.Body);
+ VCQuantifierInfos! infos = GenerateQuantifierInfos(node);
+
+ Quantifier quan;
+ if (node is ForallExpr)
+ quan = Quantifier.ALL;
+ else if (node is ExistsExpr)
+ quan = Quantifier.EX;
+ else
+ assert false;
+
+ return Gen.Quantify(quan, typeParams, boundVars, triggers, infos, body);
+ } finally {
+ PopBoundVariableScope();
+ }
+ }
+
+ private List<VCTrigger!>! TranslateTriggers(Trigger node)
+ {
+ List<VCTrigger!>! res = new List<VCTrigger!> ();
+ Trigger curTrigger = node;
+ while (curTrigger != null) {
+ res.Add(Gen.Trigger(curTrigger.Pos, Translate(curTrigger.Tr)));
+ curTrigger = curTrigger.Next;
+ }
+ return res;
+ }
+
+ private VCQuantifierInfos! GenerateQuantifierInfos(QuantifierExpr! node) {
+ string qid = getQidNameFromQKeyValue(node.Dummies, node.Attributes);
+ return new VCQuantifierInfos(qid, node.SkolemId, false, node.Attributes);
+ }
+
+ private string getQidNameFromQKeyValue(VariableSeq! vars, QKeyValue attributes) {
+ // Check for a 'qid, name' pair in keyvalues
+ string qid = QKeyValue.FindStringAttribute(attributes, "qid");
+ if (qid == null && vars.Length != 0){
+ // generate default name (line:column position in .bpl file)
+ Variable v = vars[0];
+ assert v != null; // Rustan's claim!
+ // Include the first 8 characters of the filename in QID (helpful in case we use /concat)
+ // We limit it to 8, so the SX file doesn't grow too big, and who on earth would need
+ // more than 8 characters in a filename anyways.
+ int max = 8;
+ StringBuilder buf = new StringBuilder(max + 20);
+ string filename = v.tok.filename;
+ if (filename == null) filename = "unknown";
+ for (int i = 0; i < filename.Length; ++i) {
+ if (filename[i] == '/' || filename[i] == '\\') buf.Length = 0;
+ if (buf.Length < max && char.IsLetterOrDigit(filename[i]))
+ buf.Append(filename[i]);
+ }
+ buf.Append('.').Append(v.Line).Append(':').Append(v.Col);
+ qid = buf.ToString();
+ }
+ return qid;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////
+
+ public override ExtractExpr! VisitExtractExpr(ExtractExpr! node)
+ {
+ Push(TranslateExtractExpr(node));
+ return node;
+ }
+
+ private VCExpr! TranslateExtractExpr(ExtractExpr! node)
+ requires node.Start <= node.End; {
+ VCExpr! bv = Translate(node.Bitvector);
+ return Gen.BvExtract(bv, ((!)node.Bitvector.Type).BvBits, node.Start, node.End);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////
+
+ public override BvConcatExpr! VisitBvConcatExpr(BvConcatExpr! node)
+ {
+ Push(TranslateBvConcatExpr(node));
+ return node;
+ }
+
+ private VCExpr! TranslateBvConcatExpr(BvConcatExpr! node) {
+ VCExpr! bv0 = Translate(node.E0);
+ VCExpr! bv1 = Translate(node.E1);
+ return Gen.BvConcat(bv0, bv1);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////
+ // all the other cases should never happen
+
+ public override Cmd! VisitAssertCmd(AssertCmd! node)
+ {
+ assert false;
+ }
+ public override Cmd! VisitAssignCmd(AssignCmd! node)
+ {
+ assert false;
+ }
+ public override Cmd! VisitAssumeCmd(AssumeCmd! node)
+ {
+ assert false;
+ }
+ public override AtomicRE! VisitAtomicRE(AtomicRE! node)
+ {
+ assert false;
+ }
+ public override Axiom! VisitAxiom(Axiom! node)
+ {
+ assert false;
+ }
+ public override Type! VisitBasicType(BasicType! node)
+ {
+ assert false;
+ }
+ public override Type! VisitBvType(BvType! node)
+ {
+ assert false;
+ }
+ public override Block! VisitBlock(Block! node)
+ {
+ assert false;
+ }
+ public override Expr! VisitBlockExpr(BlockExpr! node)
+ {
+ assert false;
+ }
+ public override BlockSeq! VisitBlockSeq(BlockSeq! blockSeq)
+ {
+ assert false;
+ }
+ public override List<Block!>! VisitBlockList(List<Block!>! blocks)
+ {
+ assert false;
+ }
+ public override BoundVariable! VisitBoundVariable(BoundVariable! node)
+ {
+ assert false;
+ }
+ public override Cmd! VisitCallCmd(CallCmd! node)
+ {
+ assert false;
+ }
+ public override Cmd! VisitCallForallCmd(CallForallCmd! node)
+ {
+ assert false;
+ }
+ public override CmdSeq! VisitCmdSeq(CmdSeq! cmdSeq)
+ {
+ assert false;
+ }
+ public override Choice! VisitChoice(Choice! node)
+ {
+ assert false;
+ }
+ public override Cmd! VisitCommentCmd(CommentCmd! node)
+ {
+ assert false;
+ }
+ public override Constant! VisitConstant(Constant! node)
+ {
+ assert false;
+ }
+ public override CtorType! VisitCtorType(CtorType! node)
+ {
+ assert false;
+ }
+ public override Declaration! VisitDeclaration(Declaration! node)
+ {
+ assert false;
+ }
+ public override List<Declaration!>! VisitDeclarationList(List<Declaration!>! declarationList)
+ {
+ assert false;
+ }
+ public override DeclWithFormals! VisitDeclWithFormals(DeclWithFormals! node)
+ {
+ assert false;
+ }
+ public override Requires! VisitRequires(Requires! @requires)
+ {
+ assert false;
+ }
+ public override RequiresSeq! VisitRequiresSeq(RequiresSeq! requiresSeq)
+ {
+ assert false;
+ }
+ public override Ensures! VisitEnsures(Ensures! @ensures)
+ {
+ assert false;
+ }
+ public override EnsuresSeq! VisitEnsuresSeq(EnsuresSeq! ensuresSeq)
+ {
+ assert false;
+ }
+ public override Formal! VisitFormal(Formal! node)
+ {
+ assert false;
+ }
+ public override Function! VisitFunction(Function! node)
+ {
+ assert false;
+ }
+ public override GlobalVariable! VisitGlobalVariable(GlobalVariable! node)
+ {
+ assert false;
+ }
+ public override GotoCmd! VisitGotoCmd(GotoCmd! node)
+ {
+ assert false;
+ }
+ public override Cmd! VisitHavocCmd(HavocCmd! node)
+ {
+ assert false;
+ }
+ public override Implementation! VisitImplementation(Implementation! node)
+ {
+ assert false;
+ }
+ public override LocalVariable! VisitLocalVariable(LocalVariable! node)
+ {
+ assert false;
+ }
+ public override AssignLhs! VisitMapAssignLhs(MapAssignLhs! node)
+ {
+ assert false;
+ }
+ public override MapType! VisitMapType(MapType! node)
+ {
+ assert false;
+ }
+ public override Procedure! VisitProcedure(Procedure! node)
+ {
+ assert false;
+ }
+ public override Program! VisitProgram(Program! node)
+ {
+ assert false;
+ }
+ public override Cmd! VisitRE(RE! node)
+ {
+ assert false;
+ }
+ public override RESeq! VisitRESeq(RESeq! reSeq)
+ {
+ assert false;
+ }
+ public override ReturnCmd! VisitReturnCmd(ReturnCmd! node)
+ {
+ assert false;
+ }
+ public override ReturnExprCmd! VisitReturnExprCmd(ReturnExprCmd! node)
+ {
+ assert false;
+ }
+ public override Sequential! VisitSequential(Sequential! node)
+ {
+ assert false;
+ }
+ public override AssignLhs! VisitSimpleAssignLhs(SimpleAssignLhs! node)
+ {
+ assert false;
+ }
+ public override Cmd! VisitStateCmd(StateCmd! node)
+ {
+ assert false;
+ }
+ public override TransferCmd! VisitTransferCmd(TransferCmd! node)
+ {
+ assert false;
+ }
+ public override Trigger! VisitTrigger(Trigger! node)
+ {
+ assert false;
+ }
+ public override Type! VisitType(Type! node)
+ {
+ assert false;
+ }
+ public override TypedIdent! VisitTypedIdent(TypedIdent! node)
+ {
+ assert false;
+ }
+ public override Type! VisitTypeSynonymAnnotation(TypeSynonymAnnotation! node)
+ {
+ assert false;
+ }
+ public override Type! VisitTypeVariable(TypeVariable! node)
+ {
+ assert false;
+ }
+ public override Variable! VisitVariable(Variable! node)
+ {
+ assert false;
+ }
+ public override VariableSeq! VisitVariableSeq(VariableSeq! variableSeq)
+ {
+ assert false;
+ }
+ public override Cmd! VisitAssertEnsuresCmd(AssertEnsuresCmd! node)
+ {
+ assert false;
+ }
+ public override Cmd! VisitAssertRequiresCmd(AssertRequiresCmd! node)
+ {
+ assert false;
+ }
+
+ }
+
+
+ /////////////////////////////////////////////////////////////////////////////////
+
+ public class IAppliableTranslator : IAppliableVisitor<VCExpr!> {
+
+ private readonly Boogie2VCExprTranslator! BaseTranslator;
+
+ private VCExpressionGenerator! Gen { get {
+ return BaseTranslator.Gen;
+ } }
+ private VCGenerationOptions! GenerationOptions { get {
+ return BaseTranslator.GenerationOptions;
+ } }
+
+ public IAppliableTranslator(Boogie2VCExprTranslator! baseTranslator) {
+ this.BaseTranslator = baseTranslator;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////
+
+ private List<VCExpr!>! args = new List<VCExpr!>();
+ private List<Type!>! typeArgs = new List<Type!>();
+
+ public VCExpr! Translate(IAppliable! app, Type! ty, List<VCExpr!>! args, List<Type!>! typeArgs) {
+
+ List<VCExpr!>! oldArgs = this.args;
+ List<Type!>! oldTypeArgs = this.typeArgs;
+ this.args = args;
+ this.typeArgs = typeArgs;
+ VCExpr! result = app.Dispatch<VCExpr!>(this);
+ this.args = oldArgs;
+ this.typeArgs = oldTypeArgs;
+ return result;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////
+
+
+
+ public VCExpr! Visit(UnaryOperator! unaryOperator) {
+ assert unaryOperator.Op == UnaryOperator.Opcode.Not && this.args.Count == 1;
+ return Gen.Not(this.args);
+ }
+
+ public VCExpr! Visit(BinaryOperator! binaryOperator) {
+ return TranslateBinaryOperator(binaryOperator, this.args);
+ }
+
+ public VCExpr! Visit(FunctionCall! functionCall) {
+ return TranslateFunctionCall(functionCall, this.args, this.typeArgs);
+ }
+
+ public VCExpr! Visit(LoopPredicateName! loopPredicateName) {
+ return Gen.Variable(loopPredicateName.Name, Type.Bool); // We generate a variable. Intuition: it is a second order variable
+ }
+
+ public VCExpr! Visit(MapSelect! mapSelect) {
+ return Gen.Select(this.args, this.typeArgs);
+ }
+
+ public VCExpr! Visit(MapStore! mapStore) {
+ return Gen.Store(this.args, this.typeArgs);
+ }
+
+ public VCExpr! Visit(TypeCoercion! typeCoercion) {
+ assert this.args.Count == 1;
+ return this.args[0];
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////
+
+ private VCExpr! TranslateBinaryOperator(BinaryOperator! app, List<VCExpr!>! args) {
+ assert args.Count == 2;
+
+ switch (app.Op) {
+ case BinaryOperator.Opcode.Add:
+ return Gen.Function(VCExpressionGenerator.AddOp, args);
+ case BinaryOperator.Opcode.Sub:
+ return Gen.Function(VCExpressionGenerator.SubOp, args);
+ case BinaryOperator.Opcode.Mul:
+ return Gen.Function(VCExpressionGenerator.MulOp, args);
+ case BinaryOperator.Opcode.Div:
+ return Gen.Function(VCExpressionGenerator.DivOp, args);
+ case BinaryOperator.Opcode.Mod:
+ return Gen.Function(VCExpressionGenerator.ModOp, args);
+ case BinaryOperator.Opcode.Eq:
+ case BinaryOperator.Opcode.Iff:
+ // we don't distinguish between equality and equivalence at this point
+ return Gen.Function(VCExpressionGenerator.EqOp, args);
+ case BinaryOperator.Opcode.Neq:
+ return Gen.Function(VCExpressionGenerator.NeqOp, args);
+ case BinaryOperator.Opcode.Lt:
+ return Gen.Function(VCExpressionGenerator.LtOp, args);
+ case BinaryOperator.Opcode.Le:
+ return Gen.Function(VCExpressionGenerator.LeOp, args);
+ case BinaryOperator.Opcode.Ge:
+ return Gen.Function(VCExpressionGenerator.GeOp, args);
+ case BinaryOperator.Opcode.Gt:
+ return Gen.Function(VCExpressionGenerator.GtOp, args);
+ case BinaryOperator.Opcode.Imp:
+ return Gen.Function(VCExpressionGenerator.ImpliesOp, args);
+ case BinaryOperator.Opcode.And:
+ return Gen.Function(VCExpressionGenerator.AndOp, args);
+ case BinaryOperator.Opcode.Or:
+ return Gen.Function(VCExpressionGenerator.OrOp, args);
+ case BinaryOperator.Opcode.Subtype:
+ return Gen.Function(VCExpressionGenerator.SubtypeOp, args);
+ default:
+ assert false; // unexpected binary operator
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////
+
+ private VCExpr! TranslateFunctionCall(FunctionCall! app,
+ List<VCExpr!>! args, List<Type!>! typeArgs) {
+ assert app.Func != null; // resolution must have happened
+
+ VCExpr res = ApplyExpansion(app, args, typeArgs);
+ if (res != null)
+ return res;
+
+ VCExprOp! functionOp = Gen.BoogieFunctionOp(app.Func);
+ return Gen.Function(functionOp, args, typeArgs);
+ }
+
+ private VCExpr ApplyExpansion(FunctionCall! app,
+ List<VCExpr!>! args, List<Type!>! typeArgs) {
+ assert app.Func != null; // resolution must have happened
+
+ if (app.Func.doingExpansion) {
+ System.Console.WriteLine("*** detected expansion loop on {0}", app.Func);
+ return null;
+ }
+
+ Expansion exp = FindExpansion(app.Func);
+ if (exp == null)
+ return null;
+
+ VCExpr! translatedBody;
+ VCExprSubstitution! subst = new VCExprSubstitution();
+ try {
+ BaseTranslator.PushFormalsScope();
+ BaseTranslator.PushBoundVariableScope();
+ app.Func.doingExpansion = true;
+
+ // first bind the formals to VCExpr variables, which are later
+ // substituted with the actual parameters
+ for (int i = 0; i < exp.formals.Length; ++i)
+ subst[BaseTranslator.BindVariable((!)exp.formals[i])] = args[i];
+
+ // recursively translate the body of the expansion
+ translatedBody = BaseTranslator.Translate(exp.body);
+ } finally {
+ BaseTranslator.PopFormalsScope();
+ BaseTranslator.PopBoundVariableScope();
+ app.Func.doingExpansion = false;
+ }
+
+ // substitute the formals with the actual parameters in the body
+ assert typeArgs.Count == exp.TypeParameters.Length;
+ for (int i = 0; i < typeArgs.Count; ++i)
+ subst[exp.TypeParameters[i]] = typeArgs[i];
+ SubstitutingVCExprVisitor! substituter = new SubstitutingVCExprVisitor (Gen);
+ return substituter.Mutate(translatedBody, subst);
+ }
+
+ private Expansion? FindExpansion(Function! func)
+ {
+ if (func.expansions == null) return null;
+
+ Expansion? exp = null;
+ foreach (Expansion! e in func.expansions) {
+ if (e.ignore == null || !GenerationOptions.IsAnyProverCommandSupported(e.ignore)) {
+ if (exp == null) {
+ exp = e;
+ } else {
+ System.Console.WriteLine("*** more than one possible expansion for {0}", func);
+ return null;
+ }
+ }
+ }
+
+ return exp;
+ }
+ }
+}
diff --git a/Source/VCExpr/Clustering.ssc b/Source/VCExpr/Clustering.ssc
new file mode 100644
index 00000000..7692ceb1
--- /dev/null
+++ b/Source/VCExpr/Clustering.ssc
@@ -0,0 +1,449 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Text;
+using System.IO;
+using System.Collections;
+using System.Collections.Generic;
+using Microsoft.Contracts;
+using Microsoft.Basetypes;
+using Microsoft.Boogie.VCExprAST;
+
+// Code for managing and clusterings sets of terms; this is used to
+// compress the input given to the theorem prover
+
+namespace Microsoft.Boogie.Clustering
+{
+ using Microsoft.Boogie.VCExprAST;
+
+
+ public class SubtermCollector : BoundVarTraversingVCExprVisitor<bool, bool> {
+
+ private readonly VCExpressionGenerator! Gen;
+
+ public SubtermCollector(VCExpressionGenerator! gen) {
+ Gen = gen;
+ }
+
+ // variables that are global and treated like constants
+ private readonly IDictionary<VCExprVar!, VCExprVar!>! GlobalVariables =
+ new Dictionary<VCExprVar!, VCExprVar!> ();
+
+ private readonly IDictionary<VCExprOp!, TermClustersSameHead!>! SubtermClusters =
+ new Dictionary<VCExprOp!, TermClustersSameHead!> ();
+
+ public void UnifyClusters() {
+ foreach (KeyValuePair<VCExprOp!, TermClustersSameHead!> pair
+ in SubtermClusters)
+ pair.Value.UnifyClusters();
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ protected override bool StandardResult(VCExpr! node, bool arg) {
+ return false; // by default, do not collect terms containing node
+ }
+
+ public override bool Visit(VCExprLiteral! node, bool arg) {
+ return true;
+ }
+
+ public override bool Visit(VCExprNAry! node, bool arg) {
+ VCExprBoogieFunctionOp op = node.Op as VCExprBoogieFunctionOp;
+ if (op == null) {
+ base.Visit(node, arg);
+ return false;
+ }
+
+ bool res = true;
+ foreach (VCExpr! subexpr in node)
+ res &= this.Traverse(subexpr, arg);
+
+ if (res) {
+ TermClustersSameHead clusters;
+ if (!SubtermClusters.TryGetValue(op, out clusters)) {
+ clusters = new TermClustersSameHead(op, GlobalVariables, Gen);
+ SubtermClusters.Add(op, clusters);
+ }
+ ((!)clusters).AddExpr(node);
+ }
+
+ return res;
+ }
+
+ public override bool Visit(VCExprVar! node, bool arg) {
+ if (!BoundTermVars.Contains(node))
+ GlobalVariables[node] = node;
+ return true;
+ }
+
+ [Pure]
+ public override string! ToString()
+ {
+ string! res = "";
+ foreach (KeyValuePair<VCExprOp!, TermClustersSameHead!> pair
+ in SubtermClusters)
+ res = res + pair.Value + "\n";
+ return res;
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ // Class for managing and clustering a set of terms that all start
+ // with the same function symbol
+ internal class TermClustersSameHead {
+
+ public readonly VCExprOp! Op;
+ private readonly VCExpressionGenerator! Gen;
+
+ public TermClustersSameHead(VCExprOp! op,
+ IDictionary<VCExprVar!, VCExprVar!>! globalVars,
+ VCExpressionGenerator! gen) {
+ Op = op;
+ GlobalVariables = globalVars;
+ Gen = gen;
+ }
+
+ // variables that are global and treated like constants
+ private readonly IDictionary<VCExprVar!, VCExprVar!>! GlobalVariables;
+
+ private readonly List<Cluster>! Clusters = new List<Cluster> ();
+
+ private struct Cluster {
+ public readonly VCExprNAry! Generator;
+ public readonly int Size;
+
+ public Cluster(VCExprNAry! generator, int size) {
+ Generator = generator;
+ Size = size;
+ }
+ }
+
+ private int Distance(Cluster a, Cluster b) {
+ AntiUnificationVisitor! visitor = new AntiUnificationVisitor (Gen);
+ visitor.AntiUnify(a.Generator, b.Generator);
+
+ int reprSizeA, reprSizeB;
+ visitor.RepresentationSize(GlobalVariables, out reprSizeA, out reprSizeB);
+ return (a.Size - 1) * reprSizeA + (b.Size - 1) * reprSizeB;
+ }
+
+ private bool EqualUpToRenaming(Cluster a, Cluster b) {
+ AntiUnificationVisitor! visitor = new AntiUnificationVisitor (Gen);
+ visitor.AntiUnify(a.Generator, b.Generator);
+ return visitor.RepresentationIsRenaming(GlobalVariables);
+ }
+
+ private Cluster Merge(Cluster a, Cluster b) {
+ AntiUnificationVisitor! visitor = new AntiUnificationVisitor (Gen);
+ VCExpr! generator = visitor.AntiUnify(a.Generator, b.Generator);
+ VCExprNAry generatorNAry = generator as VCExprNAry;
+ assert generatorNAry != null && Op.Equals(generatorNAry.Op);
+ return new Cluster(generatorNAry, a.Size + b.Size);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ public void AddExpr(VCExprNAry! expr)
+ requires Op.Equals(expr.Op); {
+
+ Cluster c = new Cluster (expr, 1);
+ for (int i = 0; i < Clusters.Count; ++i) {
+ Cluster d = Clusters[i];
+ if (EqualUpToRenaming(c, d)) {
+ Clusters[i] = new Cluster (d.Generator, d.Size + 1);
+ return;
+ }
+ }
+
+ Clusters.Add(c);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ private struct ClusteringMatrix {
+
+ private readonly VCExpressionGenerator! Gen;
+ private readonly IDictionary<VCExprVar!, VCExprVar!>! GlobalVariables;
+
+ public readonly List<Cluster>! Clusters;
+ public readonly bool[]! RemainingClusters;
+
+ public readonly Distance[,]! Distances;
+
+ public struct Distance {
+ public readonly int Dist;
+ public readonly VCExprNAry! Generator;
+
+ public Distance(Cluster a, Cluster b,
+ IDictionary<VCExprVar!, VCExprVar!>! globalVars,
+ VCExpressionGenerator! gen) {
+ AntiUnificationVisitor! visitor = new AntiUnificationVisitor (gen);
+ Generator = (VCExprNAry)visitor.AntiUnify(a.Generator, b.Generator);
+
+ int reprSizeA, reprSizeB;
+ visitor.RepresentationSize(globalVars, out reprSizeA, out reprSizeB);
+ Dist = (a.Size - 1) * reprSizeA + (b.Size - 1) * reprSizeB;
+ }
+ }
+
+ public ClusteringMatrix(List<Cluster>! clusters,
+ IDictionary<VCExprVar!, VCExprVar!>! globalVars,
+ VCExpressionGenerator! gen) {
+ List<Cluster>! c = new List<Cluster> ();
+ c.AddRange(clusters);
+ Clusters = c;
+
+ GlobalVariables = globalVars;
+ Gen = gen;
+
+ bool[] remaining = new bool [clusters.Count];
+ RemainingClusters = remaining;
+ for (int i = 0; i < remaining.Length; ++i)
+ remaining[i] = true;
+
+ Distance[,]! distances = new Distance [clusters.Count, clusters.Count];
+ Distances = distances;
+ for (int i = 1; i < clusters.Count; ++i)
+ for (int j = 0; j < i; ++j)
+ distances[i, j] =
+ new Distance (clusters[i], clusters[j], GlobalVariables, Gen);
+ }
+
+ public void UnifyClusters(int maxDist) {
+ while (true) {
+ int i, j;
+ int minDist = FindMinDistance(out i, out j);
+
+ if (minDist > maxDist)
+ return;
+
+ MergeClusters(i, j);
+ }
+ }
+
+ public void ResultingClusters(List<Cluster>! clusters) {
+ clusters.Clear();
+ for (int i = 0; i < Clusters.Count; ++i)
+ if (RemainingClusters[i])
+ clusters.Add(Clusters[i]);
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+
+ private void Update(int i) {
+ for (int j = 0; j < i; ++j) {
+ if (RemainingClusters[j])
+ Distances[i, j] =
+ new Distance (Clusters[i], Clusters[j], GlobalVariables, Gen);
+ }
+ for (int j = i + 1; j < Clusters.Count; ++j) {
+ if (RemainingClusters[j])
+ Distances[j, i] =
+ new Distance (Clusters[j], Clusters[i], GlobalVariables, Gen);
+ }
+ }
+
+ private int FindMinDistance(out int c0, out int c1) {
+ int minDist = int.MaxValue;
+ c0 = -1;
+ c1 = -1;
+
+ for (int i = 0; i < Clusters.Count; ++i)
+ if (RemainingClusters[i]) {
+ for (int j = 0; j < i; ++j)
+ if (RemainingClusters[j]) {
+ if (Distances[i, j].Dist < minDist) {
+ minDist = Distances[i, j].Dist;
+ c0 = i;
+ c1 = j;
+ }
+ }
+ }
+
+ assert c0 == -1 && c1 == -1 || c0 > c1 && c1 >= 0;
+ return minDist;
+ }
+
+ private void MergeClusters(int i, int j)
+ requires j >= 0 && i > j &&
+ RemainingClusters[i] && RemainingClusters[j]; {
+ Clusters[i] = new Cluster (Distances[i, j].Generator,
+ Clusters[i].Size + Clusters[j].Size);
+ RemainingClusters[j] = false;
+ Update(i);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ public void UnifyClusters() {
+ ClusteringMatrix matrix =
+ new ClusteringMatrix (Clusters, GlobalVariables, Gen);
+ matrix.UnifyClusters(50);
+ matrix.ResultingClusters(Clusters);
+ }
+
+ [Pure]
+ public override string! ToString()
+ {
+ string! res = "";
+ foreach (Cluster c in Clusters)
+ res = res + c.Generator + "\t" + c.Size + "\n";
+ return res;
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ internal class AntiUnificationVisitor : TraversingVCExprVisitor<VCExpr!, VCExpr!> {
+
+ private readonly VCExpressionGenerator! Gen;
+
+ public AntiUnificationVisitor(VCExpressionGenerator! gen) {
+ Gen = gen;
+ }
+
+ // Sub-expressions in the first and second expression to be
+ // anti-unified that are replaced with variables
+ private readonly IDictionary<ExprPair, VCExprVar!>! Representation =
+ new Dictionary<ExprPair, VCExprVar!> ();
+
+ private struct ExprPair {
+ public readonly VCExpr! Expr0, Expr1;
+ public ExprPair(VCExpr! expr0, VCExpr! expr1) {
+ Expr0 = expr0;
+ Expr1 = expr1;
+ }
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)]
+ public override bool Equals(object that) {
+ if (that is ExprPair) {
+ ExprPair thatPair = (ExprPair)that;
+ return this.Expr0.Equals(thatPair.Expr0) &&
+ this.Expr1.Equals(thatPair.Expr1);
+ }
+ return false;
+ }
+ [Pure]
+ public override int GetHashCode() {
+ return Expr0.GetHashCode() + Expr1.GetHashCode() * 13;
+ }
+ }
+
+ public void Reset() {
+ Representation.Clear ();
+ }
+
+ public bool RepresentationIsRenaming(IDictionary<VCExprVar!, VCExprVar!>! globalVars) {
+ if (!forall{KeyValuePair<ExprPair, VCExprVar!> pair in Representation;
+ pair.Key.Expr0 is VCExprVar &&
+ pair.Key.Expr1 is VCExprVar &&
+ !globalVars.ContainsKey((VCExprVar!)pair.Key.Expr0) &&
+ !globalVars.ContainsKey((VCExprVar!)pair.Key.Expr1)})
+ return false;
+ // check that all substituted variables are distinct
+ // TODO: optimise
+ return
+ forall{KeyValuePair<ExprPair, VCExprVar!> pair1 in Representation;
+ forall{KeyValuePair<ExprPair, VCExprVar!> pair2 in Representation;
+ pair1.Value.Equals(pair2.Value) || !pair1.Key.Expr0.Equals(pair2.Key.Expr0)
+ && !pair1.Key.Expr1.Equals(pair2.Key.Expr1)
+ }};
+ }
+
+ public void RepresentationSize(IDictionary<VCExprVar!, VCExprVar!>! globalVars,
+ out int expr0Size, out int expr1Size) {
+ ReprSizeComputingVisitor! size0Visitor = new ReprSizeComputingVisitor ();
+ ReprSizeComputingVisitor! size1Visitor = new ReprSizeComputingVisitor ();
+
+ foreach (KeyValuePair<ExprPair, VCExprVar!> pair in Representation) {
+ size0Visitor.ComputeSize(pair.Key.Expr0, globalVars);
+ size1Visitor.ComputeSize(pair.Key.Expr1, globalVars);
+ }
+
+ expr0Size = size0Visitor.Size;
+ expr1Size = size1Visitor.Size;
+ }
+
+ public VCExpr! AntiUnify(VCExpr! s, VCExpr! t)
+ requires s.Type.Equals(t.Type); {
+ return Traverse(s, t);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ private VCExprVar! AbstractWithVariable(VCExpr! s, VCExpr! t)
+ requires s.Type.Equals(t.Type); {
+
+ ExprPair pair = new ExprPair (s, t);
+ VCExprVar repr;
+ if (!Representation.TryGetValue(pair, out repr)) {
+ repr = Gen.Variable("abs" + Representation.Count, s.Type);
+ Representation.Add(pair, repr);
+ }
+ return (!)repr;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ public override VCExpr! Visit(VCExprLiteral! node, VCExpr! that) {
+ if (node.Equals(that))
+ return node;
+ return AbstractWithVariable(node, that);
+ }
+
+ public override VCExpr! Visit(VCExprNAry! node, VCExpr! that) {
+ VCExprNAry thatNAry = that as VCExprNAry;
+ if (thatNAry != null && node.Op.Equals(thatNAry.Op)) {
+ // type parameters should already have been eliminated at this
+ // stage
+ assert node.TypeParamArity == 0 && thatNAry.TypeParamArity == 0 &&
+ node.Arity == thatNAry.Arity;
+
+ List<VCExpr!>! unifiedArgs = new List<VCExpr!> ();
+ for (int i = 0; i < node.Arity; ++i)
+ unifiedArgs.Add(Traverse(node[i], thatNAry[i]));
+
+ return Gen.Function(node.Op, unifiedArgs);
+ }
+ return AbstractWithVariable(node, that);
+ }
+
+ public override VCExpr! Visit(VCExprVar! node, VCExpr! that) {
+ if (node.Equals(that))
+ return node;
+ return AbstractWithVariable(node, that);
+ }
+
+ protected override VCExpr! StandardResult(VCExpr! node, VCExpr! that) {
+ assert false; // not handled here
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ internal class ReprSizeComputingVisitor
+ : TraversingVCExprVisitor<bool,
+ // variables considered as global constants
+ IDictionary<VCExprVar!, VCExprVar!>!> {
+
+ public int Size = 0;
+
+ public void ComputeSize(VCExpr! expr,
+ IDictionary<VCExprVar!, VCExprVar!>! globalVars) {
+ Traverse(expr, globalVars);
+ }
+
+ protected override bool StandardResult(VCExpr! node,
+ IDictionary<VCExprVar!, VCExprVar!>! globalVars) {
+ VCExprVar nodeAsVar = node as VCExprVar;
+ if (nodeAsVar == null || globalVars.ContainsKey(nodeAsVar))
+ Size = Size + 1;
+ return true;
+ }
+ }
+} \ No newline at end of file
diff --git a/Source/VCExpr/LetBindingSorter.ssc b/Source/VCExpr/LetBindingSorter.ssc
new file mode 100644
index 00000000..96eb0af2
--- /dev/null
+++ b/Source/VCExpr/LetBindingSorter.ssc
@@ -0,0 +1,135 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Text;
+using System.IO;
+using System.Collections;
+using System.Collections.Generic;
+using Microsoft.Contracts;
+using Microsoft.Basetypes;
+
+// Sort the bindings in a let-expression so that terms bound earlier do
+// not contain variables bound later
+
+namespace Microsoft.Boogie.VCExprAST
+{
+
+ // (argument is not used)
+ public class LetBindingSorter : MutatingVCExprVisitor<bool> {
+
+ private readonly FreeVariableCollector! FreeVarCollector =
+ new FreeVariableCollector ();
+
+ private List<VCExprVar!>! FreeVarsIn(VCExpr! expr) {
+ FreeVarCollector.Collect(expr);
+ List<VCExprVar!>! freeVars = new List<VCExprVar!> (FreeVarCollector.FreeTermVars.Keys);
+ FreeVarCollector.Reset();
+ return freeVars;
+ }
+
+ public LetBindingSorter(VCExpressionGenerator! gen) {
+ base(gen);
+ }
+
+ public override VCExpr! Visit(VCExprLet! node, bool arg) {
+ IDictionary<VCExprVar!, Binding!> boundVars =
+ new Dictionary<VCExprVar!, Binding!> ();
+
+ // recurse and collect the free variables in bound terms and formulae
+ foreach (VCExprLetBinding! binding in node) {
+ VCExpr! newE = Mutate(binding.E, arg);
+ Binding! b = new Binding (binding.V, newE, FreeVarsIn(newE));
+ boundVars.Add(b.V, b);
+ }
+
+ // generate the occurrence edges
+ foreach (KeyValuePair<VCExprVar!, Binding!> pair in boundVars) {
+ Binding! b = pair.Value;
+ foreach (VCExprVar! v in b.FreeVars) {
+ Binding b2;
+ if (boundVars.TryGetValue(v, out b2)) {
+ ((!)b2).Occurrences.Add(b);
+ b.InvOccurrencesNum = b.InvOccurrencesNum + 1;
+ }
+ }
+ }
+
+ // topological sort
+ Stack<Binding!> rootBindings = new Stack<Binding!> ();
+ foreach (KeyValuePair<VCExprVar!, Binding!> pair in boundVars)
+ if (pair.Value.InvOccurrencesNum == 0)
+ rootBindings.Push(pair.Value);
+
+ List<Binding!>! sortedBindings = new List<Binding!> ();
+ while (rootBindings.Count > 0) {
+ Binding! b = rootBindings.Pop();
+ sortedBindings.Add(b);
+ foreach (Binding! b2 in b.Occurrences) {
+ b2.InvOccurrencesNum = b2.InvOccurrencesNum - 1;
+ if (b2.InvOccurrencesNum == 0)
+ rootBindings.Push(b2);
+ }
+ }
+
+ if (exists{KeyValuePair<VCExprVar!, Binding!> pair in boundVars;
+ pair.Value.InvOccurrencesNum > 0})
+ System.Diagnostics.Debug.Fail("Cyclic let-bindings");
+
+ assert node.Length == sortedBindings.Count;
+
+ // check which of the bindings can be dropped
+ VCExpr! newBody = Mutate(node.Body, arg);
+
+ IDictionary<VCExprVar!, VCExprVar!>! usedVars =
+ new Dictionary<VCExprVar!, VCExprVar!> ();
+ foreach (VCExprVar! v in FreeVarsIn(newBody))
+ if (!usedVars.ContainsKey(v))
+ usedVars.Add(v, v);
+
+ for (int i = sortedBindings.Count - 1; i >= 0; --i) {
+ if (usedVars.ContainsKey(sortedBindings[i].V)) {
+ foreach (VCExprVar! v in sortedBindings[i].FreeVars)
+ if (!usedVars.ContainsKey(v))
+ usedVars.Add(v, v);
+ } else {
+ sortedBindings.RemoveAt(i);
+ }
+ }
+
+ // assemble the resulting let-expression
+ List<VCExprLetBinding!>! newBindings = new List<VCExprLetBinding!> ();
+ foreach (Binding b in sortedBindings)
+ newBindings.Add(Gen.LetBinding(b.V, b.E));
+
+ return Gen.Let(newBindings, newBody);
+ }
+
+ private class Binding {
+ public readonly VCExprVar! V;
+ public readonly VCExpr! E;
+ public readonly List<VCExprVar!>! FreeVars;
+
+ // list of all bound expression in which the variable V occurs
+ // (outgoing edges)
+ public readonly List<Binding>! Occurrences;
+
+ // number of variables that are bound in this let-expression
+ // and that occur in FreeVars
+ // (incoming edges)
+ public int InvOccurrencesNum;
+
+ public Binding(VCExprVar! v, VCExpr! e, List<VCExprVar!>! freeVars) {
+ this.V = v;
+ this.E = e;
+ this.FreeVars = freeVars;
+ this.Occurrences = new List<Binding> ();
+ this.InvOccurrencesNum = 0;
+ }
+ }
+
+ }
+
+}
diff --git a/Source/VCExpr/NameClashResolver.ssc b/Source/VCExpr/NameClashResolver.ssc
new file mode 100644
index 00000000..28a974a6
--- /dev/null
+++ b/Source/VCExpr/NameClashResolver.ssc
@@ -0,0 +1,129 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Text;
+using System.IO;
+using System.Collections;
+using System.Collections.Generic;
+using Microsoft.Contracts;
+using Microsoft.Basetypes;
+
+// Visitor that establishes unique variable (or constant) names in a VCExpr.
+// This is done by adding a counter as suffix if name clashes occur
+
+// TODO: also handle type variables here
+
+namespace Microsoft.Boogie.VCExprAST {
+ using TEHelperFuns = Microsoft.Boogie.TypeErasure.HelperFuns;
+
+ public class UniqueNamer : ICloneable {
+
+ public UniqueNamer() {
+ GlobalNames = new Dictionary<Object!, string!> ();
+ LocalNames = TEHelperFuns.ToList(new Dictionary<Object!, string!> ()
+ as IDictionary<Object!, string!>);
+ UsedNames = new Dictionary<string!, bool> ();
+ CurrentCounters = new Dictionary<string!, int> ();
+ }
+
+ private UniqueNamer(UniqueNamer! namer) {
+ GlobalNames = new Dictionary<Object!, string!> (namer.GlobalNames);
+
+ List<IDictionary<Object!, string!>!>! localNames =
+ new List<IDictionary<Object!, string!>!> ();
+ LocalNames = localNames;
+
+ foreach (IDictionary<Object!, string!>! d in namer.LocalNames)
+ localNames.Add(new Dictionary<Object!, string!> (d));
+
+ UsedNames = new Dictionary<string!, bool> (namer.UsedNames);
+ CurrentCounters = new Dictionary<string!, int> (namer.CurrentCounters);
+ }
+
+ public Object! Clone() {
+ return new UniqueNamer (this);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ private readonly IDictionary<Object!, string!>! GlobalNames;
+ private readonly List<IDictionary<Object!, string!>!>! LocalNames;
+
+ // dictionary of all names that have already been used
+ // (locally or globally)
+ private readonly IDictionary<string!, bool>! UsedNames;
+ private readonly IDictionary<string!, int>! CurrentCounters;
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ public void PushScope() {
+ LocalNames.Add(new Dictionary<Object!, string!> ());
+ }
+
+ public void PopScope() {
+ LocalNames.RemoveAt(LocalNames.Count - 1);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ private string! NextFreeName(string! baseName) {
+ string! candidate;
+ int counter;
+
+ if (CurrentCounters.TryGetValue(baseName, out counter)) {
+ candidate = baseName + "@@" + counter;
+ counter = counter + 1;
+ } else {
+ candidate = baseName;
+ counter = 0;
+ }
+
+ bool dummy;
+ while (UsedNames.TryGetValue(candidate, out dummy)) {
+ candidate = baseName + "@@" + counter;
+ counter = counter + 1;
+ }
+
+ UsedNames.Add(candidate, true);
+ CurrentCounters[baseName] = counter;
+ return candidate;
+ }
+
+ // retrieve the name of a thingie; if it does not have a name yet,
+ // generate a unique name for it (as close as possible to its inherent
+ // name) and register it globally
+ public string! GetName(Object! thingie, string! inherentName) {
+ string res = this[thingie];
+
+ if (res != null)
+ return res;
+
+ // if the object is not yet registered, create a name for it
+ res = NextFreeName(inherentName);
+ GlobalNames.Add(thingie, res);
+
+ return res;
+ }
+
+ [Pure]
+ public string this[Object! thingie] { get {
+ string res;
+ for (int i = LocalNames.Count - 1; i >= 0; --i) {
+ if (LocalNames[i].TryGetValue(thingie, out res))
+ return res;
+ }
+
+ GlobalNames.TryGetValue(thingie, out res);
+ return res;
+ } }
+
+ public string! GetLocalName(Object! thingie, string! inherentName) {
+ string! res = NextFreeName(inherentName);
+ LocalNames[LocalNames.Count - 1][thingie] = res;
+ return res;
+ }
+ }
+}
diff --git a/Source/VCExpr/SimplifyLikeLineariser.ssc b/Source/VCExpr/SimplifyLikeLineariser.ssc
new file mode 100644
index 00000000..7aff8e87
--- /dev/null
+++ b/Source/VCExpr/SimplifyLikeLineariser.ssc
@@ -0,0 +1,795 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Text;
+using System.IO;
+using System.Collections;
+using System.Collections.Generic;
+using Microsoft.Contracts;
+using Microsoft.Basetypes;
+using Microsoft.Boogie.VCExprAST;
+
+// a naive method to turn VCExprs into strings that can be fed into Simplify
+
+namespace Microsoft.Boogie.VCExprAST
+{
+
+ // Options for the linearisation. Here one can choose, for instance,
+ // whether Simplify or Z3 output is to be produced
+ public abstract class LineariserOptions {
+
+ public readonly bool AsTerm;
+ public abstract LineariserOptions! SetAsTerm(bool newVal);
+
+ public abstract bool QuantifierIds { get; }
+
+ public virtual bool UseWeights { get { return false; } }
+
+ public virtual bool InverseImplies { get { return false; } }
+
+ // whether to include type specifications in quantifiers
+ public abstract bool UseTypes { get; }
+
+ public virtual CommandLineOptions.BvHandling Bitvectors { get {
+ return CommandLineOptions.BvHandling.None;
+ } }
+
+ // variables representing formulas in let-bindings have to be
+ // printed in a different way than other variables
+ public virtual List<VCExprVar!>! LetVariables { get {
+ return EmptyList;
+ } }
+
+ public virtual LineariserOptions! AddLetVariable(VCExprVar! furtherVar) {
+ return this;
+ }
+
+ private static readonly List<VCExprVar!>! EmptyList = new List<VCExprVar!>();
+
+ public bool NativeBv { get {
+ return Bitvectors == CommandLineOptions.BvHandling.Z3Native;
+ } }
+
+ public bool IntBv { get {
+ return Bitvectors == CommandLineOptions.BvHandling.ToInt;
+ } }
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+
+ protected LineariserOptions(bool asTerm) {
+ this.AsTerm = asTerm;
+ }
+
+ public static readonly LineariserOptions! SimplifyDefault = new SimplifyOptions (false);
+ internal static readonly LineariserOptions! SimplifyDefaultTerm = new SimplifyOptions (true);
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+
+ private class SimplifyOptions : LineariserOptions {
+ internal SimplifyOptions(bool asTerm) {
+ base(asTerm);
+ }
+ public override bool QuantifierIds { get {
+ return false;
+ } }
+ public override bool UseTypes { get {
+ return false;
+ } }
+ public override LineariserOptions! SetAsTerm(bool newVal) {
+ if (newVal)
+ return SimplifyDefaultTerm;
+ else
+ return SimplifyDefault;
+ }
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+
+ // Lineariser for expressions. The result (bool) is currently not used for anything
+ public class SimplifyLikeExprLineariser : IVCExprVisitor<bool, LineariserOptions!> {
+
+ public static string! ToSimplifyString(VCExpr! e, UniqueNamer! namer) {
+ StringWriter sw = new StringWriter();
+ SimplifyLikeExprLineariser! lin = new SimplifyLikeExprLineariser (sw, namer);
+ lin.Linearise(e, LineariserOptions.SimplifyDefault);
+ return (!)sw.ToString();
+ }
+
+ public static string! ToString(VCExpr! e, LineariserOptions! options,
+ UniqueNamer! namer) {
+ StringWriter sw = new StringWriter();
+ SimplifyLikeExprLineariser! lin = new SimplifyLikeExprLineariser (sw, namer);
+ lin.Linearise(e, options);
+ return (!)sw.ToString();
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+
+ private readonly TextWriter! wr;
+ private SimplifyLikeOpLineariser OpLinObject = null;
+ private IVCExprOpVisitor<bool, LineariserOptions!>! OpLineariser { get {
+ if (OpLinObject == null)
+ OpLinObject = new SimplifyLikeOpLineariser (this, wr);
+ return OpLinObject;
+ } }
+
+ internal readonly UniqueNamer! Namer;
+
+ public SimplifyLikeExprLineariser(TextWriter! wr, UniqueNamer! namer) {
+ this.wr = wr;
+ this.Namer = namer;
+ }
+
+ public void Linearise(VCExpr! expr, LineariserOptions! options) {
+ expr.Accept<bool, LineariserOptions!>(this, options);
+ }
+
+ public void LineariseAsTerm(VCExpr! expr, LineariserOptions! options) {
+ Linearise(expr, options.SetAsTerm(true));
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////
+
+ public static string! MakeIdPrintable(string! s) {
+ // make sure that no keywords are used as identifiers
+ switch(s) {
+ case andName:
+ case orName:
+ case notName:
+ case impliesName:
+ case iffName:
+ case eqName:
+ case neqName:
+ case distinctName:
+ case TRUEName:
+ case FALSEName:
+ s = "nonkeyword_" + s;
+ break;
+ }
+
+ if (CommandLineOptions.Clo.BracketIdsInVC == 0) {
+ // In this form, we go with any identifier, so we don't ever bother about brackets.
+ // Except: @true and @false are always written with brackets
+ return s;
+ }
+ bool looksLikeOperator = true;
+ bool looksLikeSimpleId = true;
+ bool useBrackets = false;
+ foreach (char ch in s) {
+ switch (ch) {
+ case '=':
+ case '<':
+ case '>':
+ case '+':
+ case '-':
+ case '*':
+ case '/':
+ case '%':
+ case ':':
+ // looks like operator, not simple id
+ looksLikeSimpleId = false;
+ break;
+ default:
+ if (Char.IsLetterOrDigit(ch)) {
+ // looks like simple id, not operator
+ looksLikeOperator = false;
+ } else {
+ // looks like neither operator nor simple id
+ looksLikeOperator = false;
+ looksLikeSimpleId = false;
+ }
+ break;
+ }
+ if (!looksLikeOperator && !looksLikeSimpleId) {
+ useBrackets = true;
+ break;
+ }
+ }
+ if (useBrackets) {
+ return "|" + s + "|";
+ } else {
+ return s;
+ }
+ }
+
+ public static string! TypeToString(Type! t) {
+ if (t.IsBool)
+ return "$bool";
+ else if (t.IsInt)
+ return "$int";
+ else if (t.IsBv)
+ return "$bv" + t.BvBits;
+ else {
+ // at this point, only the types U, T, and bitvector types should be left
+ if (CommandLineOptions.Clo.TypeEncodingMethod == CommandLineOptions.TypeEncoding.Monomorphic)
+ return "U";
+ else {
+ System.IO.StringWriter buffer = new System.IO.StringWriter();
+ using (TokenTextWriter stream = new TokenTextWriter("<buffer>", buffer, false)) {
+ t.Emit(stream);
+ }
+ return buffer.ToString();
+ }
+ }
+ }
+
+ public static string! BvConcatOpName(VCExprNAry! node)
+ requires node.Op.Equals(VCExpressionGenerator.BvConcatOp); {
+ int bits1 = node[0].Type.BvBits;
+ int bits2 = node[1].Type.BvBits;
+ return "$bv" + (bits1 + bits2) + "_concat[" + bits1 + "." + bits2 + "]";
+ }
+
+ public static string! BvExtractOpName(VCExprNAry! node)
+ requires node.Op is VCExprBvExtractOp; {
+ VCExprBvExtractOp! op = (VCExprBvExtractOp)node.Op;
+ return "$bv" + node.Type.BvBits + "_extract[" + op.Start + ":" + op.End + "]";
+ }
+
+ internal void WriteId(string! s) {
+ wr.Write(MakeIdPrintable(s));
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////
+
+ /// <summary>
+ /// The name for logical conjunction in Simplify
+ /// </summary>
+ internal const string! andName = "AND"; // conjunction
+ internal const string! orName = "OR"; // disjunction
+ internal const string! notName = "NOT"; // negation
+ internal const string! impliesName = "IMPLIES"; // implication
+ internal const string! iffName = "IFF"; // logical equivalence
+ internal const string! eqName = "EQ"; // equality
+ internal const string! neqName = "NEQ"; // inequality
+ internal const string! lessName = "<";
+ internal const string! greaterName = ">";
+ internal const string! atmostName = "<=";
+ internal const string! atleastName = ">=";
+ internal const string! TRUEName = "TRUE"; // nullary predicate that is always true
+ internal const string! FALSEName = "FALSE"; // nullary predicate that is always false
+ internal const string! subtypeName = "<:";
+ internal const string! subtypeArgsName = "<::";
+
+ internal const string! distinctName = "DISTINCT";
+ /// <summary>
+ /// name of the main inclusion relation
+ /// </summary>
+ internal const string! boolTrueName = "|@true|";
+ internal const string! boolFalseName = "|@false|";
+ internal const string! boolAndName = "boolAnd";
+ internal const string! boolOrName = "boolOr";
+ internal const string! boolNotName = "boolNot";
+ internal const string! termEqName = "anyEqual";
+ internal const string! termNeqName = "anyNeq";
+ internal const string! termLessName = "intLess";
+ internal const string! termGreaterName = "intGreater";
+ internal const string! termAtmostName = "intAtMost";
+ internal const string! termAtleastName = "intAtLeast";
+ internal const string! intAddName = "+";
+ internal const string! intAddNameReflect = "Reflect$Add";
+ internal const string! intSubName = "-";
+ internal const string! intMulName = "*";
+ internal const string! intDivName = "/";
+ internal const string! intModName = "%";
+
+ internal void AssertAsTerm(string! x, LineariserOptions! options) {
+ if (!options.AsTerm)
+ System.Diagnostics.Debug.Fail("One should never write " + x + " as a formula!");
+ }
+
+ internal void AssertAsFormula(string! x, LineariserOptions! options) {
+ if (options.AsTerm)
+ System.Diagnostics.Debug.Fail("One should never write " + x + " as a term!");
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////
+
+ public bool Visit(VCExprLiteral! node, LineariserOptions! options) {
+ if (options.AsTerm) {
+
+ if (node == VCExpressionGenerator.True)
+ wr.Write(options.UseTypes ? TRUEName : boolTrueName);
+ else if (node == VCExpressionGenerator.False)
+ wr.Write(options.UseTypes ? FALSEName : boolFalseName);
+ else if (node is VCExprIntLit) {
+ wr.Write(((VCExprIntLit)node).Val);
+ } else
+ assert false;
+
+ } else {
+
+ if (node == VCExpressionGenerator.True)
+ wr.Write(TRUEName);
+ else if (node == VCExpressionGenerator.False)
+ wr.Write(FALSEName);
+ else if (node is VCExprIntLit) {
+ System.Diagnostics.Debug.Fail("One should never write IntLit as a predicate!");
+ } else
+ assert false;
+
+ }
+
+ return true;
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////
+
+ public bool Visit(VCExprNAry! node, LineariserOptions! options) {
+ VCExprOp! op = node.Op;
+
+ if (!options.AsTerm &&
+ (op.Equals(VCExpressionGenerator.AndOp) ||
+ op.Equals(VCExpressionGenerator.OrOp))) {
+ // handle these operators without recursion
+
+ wr.Write("({0}",
+ op.Equals(VCExpressionGenerator.AndOp) ? andName : orName);
+ IEnumerator! enumerator = new VCExprNAryUniformOpEnumerator (node);
+ while (enumerator.MoveNext()) {
+ VCExprNAry naryExpr = enumerator.Current as VCExprNAry;
+ if (naryExpr == null || !naryExpr.Op.Equals(op)) {
+ wr.Write(" ");
+ Linearise((VCExpr!)enumerator.Current, options);
+ }
+ }
+
+ wr.Write(")");
+
+ return true;
+ }
+
+ return node.Accept<bool, LineariserOptions!>(OpLineariser, options);
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////
+
+ public bool Visit(VCExprVar! node, LineariserOptions! options) {
+ string! printedName = Namer.GetName(node, node.Name);
+
+ if (options.AsTerm ||
+ // variables for formulas bound in a let-binding are never
+ // written as an equation
+ options.LetVariables.Contains(node) ||
+ // if variables are properly typed, they cannot be written as
+ // equation either
+ options.UseTypes) {
+ WriteId(printedName);
+ } else {
+ wr.Write("({0} ", eqName);
+ WriteId(printedName);
+ wr.Write(" {0})", boolTrueName);
+ }
+
+ return true;
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////
+
+ public bool Visit(VCExprQuantifier! node, LineariserOptions! options) {
+ AssertAsFormula(node.Quan.ToString(), options);
+ assert node.TypeParameters.Count == 0;
+
+ Namer.PushScope(); try {
+
+ string! kind = node.Quan == Quantifier.ALL ? "FORALL" : "EXISTS";
+ wr.Write("({0} (", kind);
+
+ for (int i = 0; i < node.BoundVars.Count; i++)
+ {
+ VCExprVar! var = node.BoundVars[i];
+ string! printedName = Namer.GetLocalName(var, var.Name);
+ if (i != 0)
+ wr.Write(" ");
+ WriteId(printedName);
+ if (options.UseTypes)
+ wr.Write(" :TYPE {0}", TypeToString(var.Type));
+ }
+ wr.Write(") ");
+
+ WriteTriggers(node.Triggers, options);
+
+ if (options.QuantifierIds) {
+ // only needed for Z3
+ VCQuantifierInfos! infos = node.Infos;
+ if (infos.qid != null) {
+ wr.Write("(QID ");
+ wr.Write(infos.qid);
+ wr.Write(") ");
+ }
+ if (0 <= infos.uniqueId) {
+ wr.Write("(SKOLEMID ");
+ wr.Write(infos.uniqueId);
+ wr.Write(") ");
+ }
+ }
+
+ if (options.UseWeights) {
+ int weight = QKeyValue.FindIntAttribute(node.Infos.attributes, "weight", 1);
+ if (weight != 1) {
+ wr.Write("(WEIGHT ");
+ wr.Write(weight);
+ wr.Write(") ");
+ }
+ }
+
+ Linearise(node.Body, options);
+ wr.Write(")");
+
+ return true;
+
+ } finally {
+ Namer.PopScope();
+ }
+ }
+
+ private void WriteTriggers(List<VCTrigger!>! triggers, LineariserOptions! options) {
+ // first, count how many neg/pos triggers there are
+ int negTriggers = 0;
+ int posTriggers = 0;
+ foreach (VCTrigger! vcTrig in triggers) {
+ if (vcTrig.Pos) {
+ posTriggers++;
+ } else {
+ negTriggers++;
+ }
+ }
+
+ if (posTriggers > 0) {
+ wr.Write("(PATS");
+ foreach (VCTrigger! vcTrig in triggers) {
+ if (vcTrig.Pos) {
+ if (vcTrig.Exprs.Count > 1) {
+ wr.Write(" (MPAT");
+ }
+ foreach (VCExpr! e in vcTrig.Exprs) {
+ wr.Write(" ");
+ LineariseAsTerm(e, options);
+ }
+ if (vcTrig.Exprs.Count > 1) {
+ wr.Write(")");
+ }
+ }
+ }
+ wr.Write(") ");
+ } else if (negTriggers > 0) {
+ // if also positive triggers are given, the SMT solver (at least Z3)
+ // will ignore the negative patterns and output a warning. Therefore
+ // we never specify both negative and positive triggers
+ wr.Write("(NOPATS");
+ foreach (VCTrigger! vcTrig in triggers) {
+ if (!vcTrig.Pos) {
+ wr.Write(" ");
+ assert vcTrig.Exprs.Count == 1;
+ LineariseAsTerm(vcTrig.Exprs[0], options);
+ }
+ }
+ wr.Write(") ");
+ }
+
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////
+
+ public bool Visit(VCExprLet! node, LineariserOptions! options) {
+ Namer.PushScope(); try {
+
+ wr.Write("(LET (");
+
+ LineariserOptions! optionsWithVars = options;
+ foreach (VCExprVar! var in node.BoundVars)
+ optionsWithVars = optionsWithVars.AddLetVariable(var);
+
+ string s = "(";
+ foreach (VCExprLetBinding! b in node) {
+ wr.Write(s);
+ string! printedName = Namer.GetLocalName(b.V, b.V.Name);
+
+ bool formula = b.V.Type.IsBool;
+ if (formula)
+ wr.Write("FORMULA ");
+ else
+ wr.Write("TERM ");
+ WriteId(printedName);
+ wr.Write(" ");
+ Linearise(b.E, optionsWithVars.SetAsTerm(!formula));
+ wr.Write(")");
+ s = " (";
+ }
+ wr.Write(") ");
+ Linearise(node.Body, optionsWithVars);
+ wr.Write(")");
+
+ return true;
+
+ } finally {
+ Namer.PopScope();
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////
+
+ // Lineariser for operator terms. The result (bool) is currently not used for anything
+ internal class SimplifyLikeOpLineariser : IVCExprOpVisitor<bool, LineariserOptions!> {
+ private readonly SimplifyLikeExprLineariser! ExprLineariser;
+ private readonly TextWriter! wr;
+
+ public SimplifyLikeOpLineariser(SimplifyLikeExprLineariser! ExprLineariser, TextWriter! wr) {
+ this.ExprLineariser = ExprLineariser;
+ this.wr = wr;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////
+
+ private void WriteApplication(string! op, IEnumerable<VCExpr!>! args,
+ LineariserOptions! options,
+ bool argsAsTerms) {
+ WriteApplication(op, op, args, options, argsAsTerms);
+ }
+
+ private void WriteApplication(string! op, IEnumerable<VCExpr!>! args,
+ LineariserOptions! options) {
+ WriteApplication(op, op, args, options, options.AsTerm);
+ }
+
+ private void WriteTermApplication(string! op, IEnumerable<VCExpr!>! args,
+ LineariserOptions! options) {
+ ExprLineariser.AssertAsTerm(op, options);
+ WriteApplication(op, op, args, options, options.AsTerm);
+ }
+
+ private void WriteApplication(string! termOp, string! predOp,
+ IEnumerable<VCExpr!>! args, LineariserOptions! options) {
+ WriteApplication(termOp, predOp, args, options, options.AsTerm);
+ }
+
+ private void WriteApplication(string! termOp, string! predOp,
+ IEnumerable<VCExpr!>! args, LineariserOptions! options,
+ // change the AsTerm option for the arguments?
+ bool argsAsTerms) {
+ wr.Write("({0}", options.AsTerm ? termOp : predOp);
+
+ LineariserOptions! newOptions = options.SetAsTerm(argsAsTerms);
+ foreach (VCExpr! e in args) {
+ wr.Write(" ");
+ ExprLineariser.Linearise(e, newOptions);
+ }
+
+ wr.Write(")");
+ }
+
+ // write an application that can only be a term.
+ // if the expression is supposed to be printed as a formula,
+ // it is turned into an equation (EQ (f args) |@true|)
+ private void WriteApplicationTermOnly(string! termOp,
+ IEnumerable<VCExpr!>! args, LineariserOptions! options) {
+ if (!options.AsTerm)
+ // Write: (EQ (f args) |@true|)
+ // where "args" are written as terms
+ wr.Write("({0} ", eqName);
+
+ WriteApplication(termOp, args, options, true);
+
+ if (!options.AsTerm)
+ wr.Write(" {0})", boolTrueName);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////
+
+ public bool VisitNotOp (VCExprNAry! node, LineariserOptions! options) {
+ WriteApplication(boolNotName, notName, node, options); // arguments can be both terms and formulas
+ return true;
+ }
+
+ public bool VisitEqOp (VCExprNAry! node, LineariserOptions! options) {
+ if (options.AsTerm) {
+ // use equality on terms, also if the arguments have type bool
+ WriteApplication(termEqName, node, options);
+ } else {
+ if (node[0].Type.IsBool) {
+ assert node[1].Type.IsBool;
+ // use equivalence
+ WriteApplication(iffName, node, options);
+ } else {
+ assert !node[1].Type.IsBool;
+ // use equality and write the arguments as terms
+ WriteApplication(eqName, node, options, true);
+ }
+ }
+
+ return true;
+ }
+
+ public bool VisitNeqOp (VCExprNAry! node, LineariserOptions! options) {
+ if (options.AsTerm) {
+ // use equality on terms, also if the arguments have type bool
+ WriteApplication(termNeqName, node, options);
+ } else {
+ if (node[0].Type.IsBool) {
+ assert node[1].Type.IsBool;
+ // use equivalence and negate the whole thing
+ wr.Write("({0} ", notName);
+ WriteApplication(iffName, node, options);
+ wr.Write(")");
+ } else {
+ // use equality and write the arguments as terms
+ WriteApplication(neqName, node, options, true);
+ }
+ }
+
+ return true;
+ }
+
+ public bool VisitAndOp (VCExprNAry! node, LineariserOptions! options) {
+ assert options.AsTerm;
+ WriteApplication(boolAndName, andName, node, options); // arguments can be both terms and formulas
+ return true;
+ }
+
+ public bool VisitOrOp (VCExprNAry! node, LineariserOptions! options) {
+ assert options.AsTerm;
+ WriteApplication(boolOrName, orName, node, options); // arguments can be both terms and formulas
+ return true;
+ }
+
+ public bool VisitImpliesOp (VCExprNAry! node, LineariserOptions! options) {
+ if (options.AsTerm) {
+ wr.Write("({0} ({1} ", boolOrName, boolNotName);
+ ExprLineariser.Linearise(node[0], options);
+ wr.Write(") ");
+ ExprLineariser.Linearise(node[1], options);
+ wr.Write(")");
+ } else if (options.InverseImplies) {
+ wr.Write("({0} ", orName);
+ ExprLineariser.Linearise(node[1], options);
+ wr.Write(" ({0} ", notName);
+ ExprLineariser.Linearise(node[0], options);
+ wr.Write("))");
+ } else {
+ WriteApplication(impliesName, node, options);
+ }
+ return true;
+ }
+
+ public bool VisitDistinctOp (VCExprNAry! node, LineariserOptions! options) {
+ ExprLineariser.AssertAsFormula(distinctName, options);
+
+ if (node.Length < 2) {
+ ExprLineariser.Linearise(VCExpressionGenerator.True, options);
+ } else {
+ wr.Write("({0}", distinctName);
+ foreach (VCExpr! e in node) {
+ wr.Write(" ");
+ ExprLineariser.LineariseAsTerm(e, options);
+ }
+ wr.Write(")");
+ }
+
+ return true;
+ }
+
+ public bool VisitLabelOp (VCExprNAry! node, LineariserOptions! options) {
+ VCExprLabelOp! op = (VCExprLabelOp)node.Op;
+ wr.Write(String.Format("({0} |{1}| ", op.pos ? "LBLPOS" : "LBLNEG", op.label));
+ ExprLineariser.Linearise(node[0], options); wr.Write(")");
+ return true;
+ }
+
+ public bool VisitSelectOp (VCExprNAry! node, LineariserOptions! options) {
+ assert false; // should not occur in the output
+ }
+
+ public bool VisitStoreOp (VCExprNAry! node, LineariserOptions! options) {
+ assert false; // should not occur in the output
+ }
+
+ public bool VisitBvOp (VCExprNAry! node, LineariserOptions! options) {
+ WriteTermApplication("$make_bv" + node.Type.BvBits, node, options);
+ return true;
+ }
+
+ public bool VisitBvExtractOp(VCExprNAry! node, LineariserOptions! options) {
+ WriteTermApplication(BvExtractOpName(node), node, options);
+ return true;
+ }
+
+ public bool VisitBvConcatOp (VCExprNAry! node, LineariserOptions! options) {
+ WriteTermApplication(BvConcatOpName(node), node, options);
+ return true;
+ }
+
+ public bool VisitAddOp (VCExprNAry! node, LineariserOptions! options) {
+ if (CommandLineOptions.Clo.ReflectAdd) {
+ WriteTermApplication(intAddNameReflect, node, options);
+ } else {
+ WriteTermApplication(intAddName, node, options);
+ }
+ return true;
+ }
+
+ public bool VisitSubOp (VCExprNAry! node, LineariserOptions! options) {
+ WriteTermApplication(intSubName, node, options);
+ return true;
+ }
+
+ public bool VisitMulOp (VCExprNAry! node, LineariserOptions! options) {
+ WriteTermApplication(intMulName, node, options);
+ return true;
+ }
+
+ public bool VisitDivOp (VCExprNAry! node, LineariserOptions! options) {
+ WriteTermApplication(intDivName, node, options);
+ return true;
+ }
+
+ public bool VisitModOp (VCExprNAry! node, LineariserOptions! options) {
+ WriteTermApplication(intModName, node, options);
+ return true;
+ }
+
+ public bool VisitLtOp (VCExprNAry! node, LineariserOptions! options) {
+ WriteApplication(termLessName, lessName, node, options, true); // arguments are always terms
+ return true;
+ }
+
+ public bool VisitLeOp (VCExprNAry! node, LineariserOptions! options) {
+ WriteApplication(termAtmostName, atmostName, node, options, true); // arguments are always terms
+ return true;
+ }
+
+ public bool VisitGtOp (VCExprNAry! node, LineariserOptions! options) {
+ WriteApplication(termGreaterName, greaterName, node, options, true); // arguments are always terms
+ return true;
+ }
+
+ public bool VisitGeOp (VCExprNAry! node, LineariserOptions! options) {
+ WriteApplication(termAtleastName, atleastName, node, options, true); // arguments are always terms
+ return true;
+ }
+
+ public bool VisitSubtypeOp (VCExprNAry! node, LineariserOptions! options) {
+ WriteApplication(subtypeName, node, options, true); // arguments are always terms
+ return true;
+ }
+
+ public bool VisitSubtype3Op (VCExprNAry! node, LineariserOptions! options) {
+ WriteApplication(subtypeArgsName, node, options, true); // arguments are always terms
+ return true;
+ }
+
+ public bool VisitBoogieFunctionOp (VCExprNAry! node, LineariserOptions! options) {
+ VCExprBoogieFunctionOp! op = (VCExprBoogieFunctionOp)node.Op;
+ string! funcName = op.Func.Name;
+ string? bvzName = op.Func.FindStringAttribute("external");
+ string! printedName = ExprLineariser.Namer.GetName(op.Func, funcName);
+ if (bvzName != null) printedName = bvzName;
+
+ if (options.UseTypes) {
+ // we use term notation for arguments whose type is not bool, and
+ // formula notation for boolean arguments
+
+ wr.Write("(");
+ ExprLineariser.WriteId(printedName);
+
+ foreach (VCExpr! e in node) {
+ wr.Write(" ");
+ ExprLineariser.Linearise(e, options.SetAsTerm(!e.Type.IsBool));
+ }
+
+ wr.Write(")");
+ } else {
+ // arguments are always terms
+ WriteApplicationTermOnly(SimplifyLikeExprLineariser.MakeIdPrintable(printedName),
+ node, options);
+ }
+ return true;
+ }
+
+ }
+ }
+
+}
diff --git a/Source/VCExpr/TermFormulaFlattening.ssc b/Source/VCExpr/TermFormulaFlattening.ssc
new file mode 100644
index 00000000..bfb8cb3a
--- /dev/null
+++ b/Source/VCExpr/TermFormulaFlattening.ssc
@@ -0,0 +1,222 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Text;
+using System.IO;
+using System.Collections;
+using System.Collections.Generic;
+using Microsoft.Contracts;
+using Microsoft.Basetypes;
+using Microsoft.Boogie.VCExprAST;
+
+// Ensure that no formulas (expressions of type boolean that are not
+// just a variable) occur with terms (expressions of some other
+// type). This is done by introducing let-binders for boolean
+// variables.
+
+namespace Microsoft.Boogie.VCExprAST
+{
+
+ public struct FlattenerState {
+ public readonly int Polarity;
+ public readonly bool InTerm;
+
+ public static FlattenerState INITIAL = new FlattenerState(1, false);
+
+ public FlattenerState(int polarity, bool inTerm) {
+ Polarity = polarity;
+ InTerm = inTerm;
+ }
+
+ public FlattenerState TogglePolarity { get {
+ return new FlattenerState(-Polarity, InTerm);
+ } }
+
+ public FlattenerState ZeroPolarity { get {
+ return new FlattenerState(0, InTerm);
+ } }
+
+ public FlattenerState EnterTerm { get {
+ return new FlattenerState(Polarity, true);
+ } }
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ public class TermFormulaFlattener : MutatingVCExprVisitor<FlattenerState> {
+
+ public TermFormulaFlattener(VCExpressionGenerator! gen) {
+ base(gen);
+ }
+
+ private readonly IDictionary<VCExpr!, VCExprVar!>! Bindings =
+ new Dictionary<VCExpr!, VCExprVar!> ();
+
+ private int varNameCounter = 0;
+
+ public VCExpr! Flatten(VCExpr! expr) {
+ VCExpr! res = Mutate(expr, FlattenerState.INITIAL);
+ while (Bindings.Count > 0) {
+ List<VCExprLetBinding!>! letBindings = new List<VCExprLetBinding!> ();
+ foreach (KeyValuePair<VCExpr!, VCExprVar!> pair in Bindings)
+ letBindings.Add(Gen.LetBinding(pair.Value, pair.Key));
+ Bindings.Clear();
+ res = AddBindings(letBindings, res, FlattenerState.INITIAL);
+ }
+ return res;
+ }
+
+ private VCExprVar! GetVarFor(VCExpr! expr) requires expr.Type.IsBool; {
+ VCExprVar res;
+ if (!Bindings.TryGetValue(expr, out res)) {
+ string! name = "flt" + varNameCounter;
+ varNameCounter = varNameCounter + 1;
+ res = Gen.Variable(name, Type.Bool);
+ Bindings.Add(expr, res);
+ }
+ return (!)res;
+ }
+
+ // Remove all let-bindings from the field bindings whose rhs
+ // contains any of the specified variables
+ private List<VCExprLetBinding!>!
+ RemoveBindingsWithVars(List<VCExprVar!>! boundVars,
+ List<TypeVariable!>! boundTypeVars) {
+
+ List<VCExprLetBinding!>! res = new List<VCExprLetBinding!> ();
+ FreeVariableCollector! coll = new FreeVariableCollector ();
+
+ foreach (KeyValuePair<VCExpr!, VCExprVar!> pair in Bindings) {
+ coll.Collect(pair.Key);
+ if (exists{VCExprVar! var in boundVars; coll.FreeTermVars.ContainsKey(var)} ||
+ exists{TypeVariable! var in boundTypeVars; coll.FreeTypeVars.Contains(var)})
+ res.Add(Gen.LetBinding(pair.Value, pair.Key));
+ coll.Reset();
+ }
+
+ foreach (VCExprLetBinding! b in res)
+ Bindings.Remove(b.E);
+
+ return res;
+ }
+
+ // Add bindings to a formula using an implication or
+ // conjunction. The bindings themselves will be flattened as well,
+ // which might introduce further bindings
+ private VCExpr! AddBindings(List<VCExprLetBinding!>! bindings,
+ VCExpr! body,
+ FlattenerState state)
+ requires body.Type.IsBool; {
+
+ List<VCExprLetBinding!>! mutatedBindings = FlattenBindings(bindings, state);
+ VCExpr! bindingEquations = Gen.AsEquations(mutatedBindings);
+ switch(state.Polarity) {
+ case 1:
+ return Gen.Implies(bindingEquations, body);
+ case -1:
+ return Gen.And(bindingEquations, body);
+ case 0:
+ // also add explicit quantifiers for the bound variables
+ List<VCExprVar!>! vars = new List<VCExprVar!> ();
+ foreach (VCExprLetBinding! binding in mutatedBindings)
+ vars.Add(binding.V);
+ return Gen.Forall(vars, new List<VCTrigger!>(),
+ Gen.Implies(bindingEquations, body));
+ }
+ assert false;
+ }
+
+ private List<VCExprLetBinding!>! FlattenBindings(List<VCExprLetBinding!>! bindings,
+ FlattenerState state) {
+ FlattenerState stateInBindings = state.ZeroPolarity;
+ List<VCExprLetBinding!>! mutatedBindings = new List<VCExprLetBinding!> ();
+ foreach (VCExprLetBinding! b in bindings)
+ mutatedBindings.Add(Gen.LetBinding(b.V, Mutate(b.E, stateInBindings)));
+ return mutatedBindings;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ public override VCExpr! Visit(VCExprNAry! node, FlattenerState state) {
+ // track the polarity to know whether implications or conjunctions
+ // are to be introduced
+
+ if (node.Op.Equals(VCExpressionGenerator.NotOp))
+ return Gen.Not(Mutate(node[0], state.TogglePolarity));
+
+ if (node.Op.Equals(VCExpressionGenerator.ImpliesOp)) {
+ VCExpr! newArg0 = Mutate(node[0], state.TogglePolarity);
+ VCExpr! newArg1 = Mutate(node[1], state);
+ return Gen.Implies(newArg0, newArg1);
+ }
+
+ if (!node.Type.IsBool)
+ state = state.EnterTerm;
+
+ if (!node.Op.Equals(VCExpressionGenerator.AndOp) &&
+ !node.Op.Equals(VCExpressionGenerator.OrOp) &&
+ !(node.Op is VCExprLabelOp))
+ // standard is to set the polarity to 0 (fits most operators)
+ return base.Visit(node, state.ZeroPolarity);
+
+ return base.Visit(node, state);
+ }
+
+ public override VCExpr! Visit(VCExprQuantifier! node, FlattenerState state) {
+ if (state.InTerm)
+ return GetVarFor(node);
+
+ // we only flatten within the matrix of the quantified formula,
+ // not within the triggers (since SMT-solvers do not seem to
+ // appreciate triggers with let-binders)
+ VCExpr! newBody = Mutate(node.Body, state);
+
+ // Check whether any of the extracted terms contain variables
+ // bound by this quantifier. In this case, we have to add
+ // let-binders and remove the extracted terms
+ bool cont = true;
+ while (cont) {
+ List<VCExprLetBinding!>! localBindings =
+ RemoveBindingsWithVars(node.BoundVars, node.TypeParameters);
+ if (localBindings.Count > 0)
+ newBody = AddBindings(localBindings, newBody, state);
+ else
+ cont = false;
+ }
+
+ return Gen.Quantify(node.Quan, node.TypeParameters,
+ node.BoundVars, node.Triggers,
+ node.Infos, newBody);
+ }
+
+ public override VCExpr! Visit(VCExprLet! node, FlattenerState state) {
+ if (state.InTerm)
+ return GetVarFor(node);
+
+ VCExprLet! prelimRes = (VCExprLet!)base.Visit(node, state);
+
+ List<VCExprLetBinding!>! allBindings = new List<VCExprLetBinding!> ();
+ allBindings.AddRange(prelimRes);
+
+ // Check whether any of the extracted terms contain variables
+ // bound by this binder. In this case, we have to add
+ // let-binders and remove the extracted terms
+ bool cont = true;
+ while (cont) {
+ List<VCExprLetBinding!>! localBindings =
+ RemoveBindingsWithVars(prelimRes.BoundVars, new List<TypeVariable!> ());
+ if (localBindings.Count > 0)
+ allBindings.AddRange(FlattenBindings(localBindings, state));
+ else
+ cont = false;
+ }
+
+ return Gen.Let(allBindings, prelimRes.Body);
+ }
+
+ }
+
+} \ No newline at end of file
diff --git a/Source/VCExpr/TypeErasure.ssc b/Source/VCExpr/TypeErasure.ssc
new file mode 100644
index 00000000..9cbd829a
--- /dev/null
+++ b/Source/VCExpr/TypeErasure.ssc
@@ -0,0 +1,1160 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Text;
+using System.IO;
+using System.Collections;
+using System.Collections.Generic;
+using Microsoft.Contracts;
+using Microsoft.Basetypes;
+using Microsoft.Boogie.VCExprAST;
+
+// different classes for erasing complex types in VCExprs, replacing them
+// with axioms that can be handled by theorem provers and SMT solvers
+
+namespace Microsoft.Boogie.TypeErasure
+{
+ using Microsoft.Boogie.VCExprAST;
+
+ // some functionality that is needed in many places (and that should
+ // really be provided by the Spec# container classes; maybe one
+ // could integrate the functions in a nicer way?)
+ public class HelperFuns {
+
+ public static Function! BoogieFunction(string! name, List<TypeVariable!>! typeParams,
+ params Type[]! types)
+ requires types.Length > 0;
+ requires forall{int i in (0:types.Length); types[i] != null};
+ {
+ VariableSeq! args = new VariableSeq ();
+ for (int i = 0; i < types.Length - 1; ++i)
+ args.Add(new Formal (Token.NoToken,
+ new TypedIdent (Token.NoToken, "arg" + i, (!)types[i]),
+ true));
+ Formal! result = new Formal (Token.NoToken,
+ new TypedIdent (Token.NoToken, "res",
+ (!)types[types.Length - 1]),
+ false);
+ return new Function (Token.NoToken, name, ToSeq(typeParams), args, result);
+ }
+
+ public static Function! BoogieFunction(string! name, params Type[]! types) {
+ return BoogieFunction(name, new List<TypeVariable!> (), types);
+ }
+
+ // boogie function where all arguments and the result have the same type U
+ public static Function! UniformBoogieFunction(string! name, int arity, Type! U) {
+ Type[]! types = new Type [arity + 1];
+ for (int i = 0; i < arity + 1; ++i)
+ types[i] = U;
+ return BoogieFunction(name, types);
+ }
+
+ public static List<VCExprVar!>! GenVarsForInParams(Function! fun,
+ VCExpressionGenerator! gen) {
+ List<VCExprVar!>! arguments = new List<VCExprVar!> (fun.InParams.Length);
+ foreach (Formal! f in fun.InParams) {
+ VCExprVar! var = gen.Variable(f.Name, f.TypedIdent.Type);
+ arguments.Add(var);
+ }
+ return arguments;
+ }
+
+ public static List<T!>! ToList<T> (params T[]! args) {
+ List<T!>! res = new List<T!> (args.Length);
+ foreach (T t in args)
+ res.Add((!)t);
+ return res;
+ }
+
+ public static List<TypeVariable!>! ToList(TypeVariableSeq! seq) {
+ List<TypeVariable!>! res = new List<TypeVariable!> (seq.Length);
+ foreach (TypeVariable! var in seq)
+ res.Add(var);
+ return res;
+ }
+
+ public static TypeVariableSeq! ToSeq(List<TypeVariable!>! list) {
+ TypeVariableSeq! res = new TypeVariableSeq ();
+ foreach (TypeVariable! var in list)
+ res.Add(var);
+ return res;
+ }
+
+ public static List<T>! Intersect<T>(List<T>! a, List<T>! b) {
+ List<T>! res = new List<T> (Math.Min(a.Count, b.Count));
+ foreach (T x in a)
+ if (b.Contains(x))
+ res.Add(x);
+ res.TrimExcess();
+ return res;
+ }
+
+ public static List<KeyValuePair<T1, T2>>! ToPairList<T1, T2>(IDictionary<T1, T2>! dict) {
+ List<KeyValuePair<T1, T2>>! res = new List<KeyValuePair<T1, T2>> (dict);
+ return res;
+ }
+
+ public static void AddRangeWithoutDups<T>(IEnumerable<T>! fromList, List<T>! toList) {
+ foreach (T t in fromList)
+ if (!toList.Contains(t))
+ toList.Add(t);
+ }
+
+ public static void AddFreeVariablesWithoutDups(Type! type, List<TypeVariable!>! toList) {
+ foreach (TypeVariable! var in type.FreeVariables) {
+ if (!toList.Contains(var))
+ toList.Add(var);
+ }
+ }
+
+ public static List<VCExpr!>! ToVCExprList(List<VCExprVar!>! list) {
+ List<VCExpr!>! res = new List<VCExpr!> (list.Count);
+ foreach (VCExprVar! var in list)
+ res.Add(var);
+ return res;
+ }
+
+ public static List<VCExprVar!>! VarVector(string! baseName, int num, Type! type,
+ VCExpressionGenerator! gen) {
+ List<VCExprVar!>! res = new List<VCExprVar!> (num);
+ for (int i = 0; i < num; ++i)
+ res.Add(gen.Variable(baseName + i, type));
+ return res;
+ }
+
+ public static List<VCExprVar!>! VarVector(string! baseName, List<Type!>! types,
+ VCExpressionGenerator! gen) {
+ List<VCExprVar!>! res = new List<VCExprVar!> (types.Count);
+ for (int i = 0; i < types.Count; ++i)
+ res.Add(gen.Variable(baseName + i, types[i]));
+ return res;
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ internal struct TypeCtorRepr {
+ // function that represents the application of the type constructor
+ // to smaller types
+ public readonly Function! Ctor;
+ // left-inverse functions that extract the subtypes of a compound type
+ public readonly List<Function!>! Dtors;
+
+ public TypeCtorRepr(Function! ctor, List<Function!>! dtors)
+ requires ctor.InParams.Length == dtors.Count; {
+ this.Ctor = ctor;
+ this.Dtors = dtors;
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ // The class responsible for creating and keeping track of all
+ // axioms related to the type system. This abstract class is made
+ // concrete in two subclasses, one for type erasure with type
+ // premisses in quantifiers (the semantic approach), and one for
+ // type erasure with explicit type arguments of polymorphic
+ // functions (the syntacted approach).
+ public abstract class TypeAxiomBuilder : ICloneable {
+
+ protected readonly VCExpressionGenerator! Gen;
+
+ internal abstract MapTypeAbstractionBuilder! MapTypeAbstracter { get; }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Type Axioms
+
+ // list in which all typed axioms are collected
+ private readonly List<VCExpr!>! AllTypeAxioms;
+
+ // list in which type axioms are incrementally collected
+ private readonly List<VCExpr!>! IncTypeAxioms;
+
+ internal void AddTypeAxiom(VCExpr! axiom) {
+ AllTypeAxioms.Add(axiom);
+ IncTypeAxioms.Add(axiom);
+ }
+
+ // Return all axioms that were added since the last time NewAxioms
+ // was called
+ public VCExpr! GetNewAxioms() {
+ VCExpr! res = Gen.NAry(VCExpressionGenerator.AndOp, IncTypeAxioms);
+ IncTypeAxioms.Clear();
+ return res;
+ }
+
+ // mapping from a type to its constructor number/index
+ private readonly Function! Ctor;
+ private BigNum CurrentCtorNum;
+
+ private VCExpr! GenCtorAssignment(VCExpr! typeRepr) {
+ if (CommandLineOptions.Clo.TypeEncodingMethod
+ == CommandLineOptions.TypeEncoding.None)
+ return VCExpressionGenerator.True;
+
+ VCExpr! res = Gen.Eq(Gen.Function(Ctor, typeRepr),
+ Gen.Integer(CurrentCtorNum));
+ CurrentCtorNum = CurrentCtorNum + BigNum.ONE;
+ return res;
+ }
+
+ private VCExpr! GenCtorAssignment(Function! typeRepr) {
+ if (CommandLineOptions.Clo.TypeEncodingMethod
+ == CommandLineOptions.TypeEncoding.None)
+ return VCExpressionGenerator.True;
+
+ List<VCExprVar!>! quantifiedVars = HelperFuns.GenVarsForInParams(typeRepr, Gen);
+ VCExpr! eq =
+ GenCtorAssignment(Gen.Function(typeRepr,
+ HelperFuns.ToVCExprList(quantifiedVars)));
+
+ if (typeRepr.InParams.Length == 0)
+ return eq;
+
+ return Gen.Forall(quantifiedVars, new List<VCTrigger!> (),
+ "ctor:" + typeRepr.Name, eq);
+ }
+
+ // generate an axiom (forall x0, x1, ... :: invFun(fun(x0, x1, ...) == xi)
+ protected VCExpr! GenLeftInverseAxiom(Function! fun, Function! invFun, int dtorNum) {
+ List<VCExprVar!>! quantifiedVars = HelperFuns.GenVarsForInParams(fun, Gen);
+
+ VCExpr! funApp = Gen.Function(fun, HelperFuns.ToVCExprList(quantifiedVars));
+ VCExpr! lhs = Gen.Function(invFun, funApp);
+ VCExpr! rhs = quantifiedVars[dtorNum];
+ VCExpr! eq = Gen.Eq(lhs, rhs);
+
+ List<VCTrigger!>! triggers = HelperFuns.ToList(Gen.Trigger(true, HelperFuns.ToList(funApp)));
+ return Gen.Forall(quantifiedVars, triggers, "typeInv:" + invFun.Name, eq);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ // the type of everything that is not int, bool, or a type
+ private readonly TypeCtorDecl! UDecl;
+ public readonly Type! U;
+
+ // the type of types
+ private readonly TypeCtorDecl! TDecl;
+ public readonly Type! T;
+
+ public abstract Type! TypeAfterErasure(Type! type);
+ public abstract bool UnchangedType(Type! type);
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Symbols for representing types
+
+ private readonly IDictionary<Type!, VCExpr!>! BasicTypeReprs;
+
+ private VCExpr! GetBasicTypeRepr(Type! type)
+ requires type.IsBasic || type.IsBv; {
+ VCExpr res;
+ if (!BasicTypeReprs.TryGetValue(type, out res)) {
+ res = Gen.Function(HelperFuns.BoogieFunction(type.ToString() + "Type", T));
+ AddTypeAxiom(GenCtorAssignment(res));
+ BasicTypeReprs.Add(type, res);
+ }
+ return (!)res;
+ }
+
+ private readonly IDictionary<TypeCtorDecl!, TypeCtorRepr>! TypeCtorReprs;
+
+ internal TypeCtorRepr GetTypeCtorReprStruct(TypeCtorDecl! decl) {
+ TypeCtorRepr reprSet;
+ if (!TypeCtorReprs.TryGetValue(decl, out reprSet)) {
+ Function! ctor = HelperFuns.UniformBoogieFunction(decl.Name + "Type", decl.Arity, T);
+ AddTypeAxiom(GenCtorAssignment(ctor));
+
+ List<Function!>! dtors = new List<Function!>(decl.Arity);
+ for (int i = 0; i < decl.Arity; ++i) {
+ Function! dtor = HelperFuns.UniformBoogieFunction(decl.Name + "TypeInv" + i, 1, T);
+ dtors.Add(dtor);
+ AddTypeAxiom(GenLeftInverseAxiom(ctor, dtor, i));
+ }
+
+ reprSet = new TypeCtorRepr(ctor, dtors);
+ TypeCtorReprs.Add(decl, reprSet);
+ }
+
+ return reprSet;
+ }
+
+ public Function! GetTypeCtorRepr(TypeCtorDecl! decl) {
+ return GetTypeCtorReprStruct(decl).Ctor;
+ }
+
+ public Function! GetTypeDtor(TypeCtorDecl! decl, int num) {
+ return GetTypeCtorReprStruct(decl).Dtors[num];
+ }
+
+ // mapping from free type variables to VCExpr variables
+ private readonly IDictionary<TypeVariable!, VCExprVar!>! TypeVariableMapping;
+
+ public VCExprVar! Typed2Untyped(TypeVariable! var) {
+ VCExprVar res;
+ if (!TypeVariableMapping.TryGetValue(var, out res)) {
+ res = new VCExprVar (var.Name, T);
+ TypeVariableMapping.Add(var, res);
+ }
+ return (!)res;
+ }
+
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Symbols for representing variables and constants
+
+ // Globally defined variables
+ private readonly IDictionary<VCExprVar!, VCExprVar!>! Typed2UntypedVariables;
+
+ // This method must only be used for free (unbound) variables
+ public VCExprVar! Typed2Untyped(VCExprVar! var) {
+ VCExprVar res;
+ if (!Typed2UntypedVariables.TryGetValue(var, out res)) {
+ res = Gen.Variable(var.Name, TypeAfterErasure(var.Type));
+ Typed2UntypedVariables.Add(var, res);
+ AddVarTypeAxiom(res, var.Type);
+ }
+ return (!)res;
+ }
+
+ protected abstract void AddVarTypeAxiom(VCExprVar! var, Type! originalType);
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Translation function from types to their term representation
+
+ public VCExpr! Type2Term(Type! type,
+ IDictionary<TypeVariable!, VCExpr!>! varMapping) {
+ //
+ if (type.IsBasic || type.IsBv) {
+ //
+ return GetBasicTypeRepr(type);
+ //
+ } else if (type.IsCtor) {
+ //
+ CtorType ctype = type.AsCtor;
+ Function! repr = GetTypeCtorRepr(ctype.Decl);
+ List<VCExpr!>! args = new List<VCExpr!> (ctype.Arguments.Length);
+ foreach (Type! t in ctype.Arguments)
+ args.Add(Type2Term(t, varMapping));
+ return Gen.Function(repr, args);
+ //
+ } else if (type.IsVariable) {
+ //
+ VCExpr res;
+ if (!varMapping.TryGetValue(type.AsVariable, out res))
+ // then the variable is free and we bind it at this point to a term
+ // variable
+ res = Typed2Untyped(type.AsVariable);
+ return (!)res;
+ //
+ } else if (type.IsMap) {
+ //
+ return Type2Term(MapTypeAbstracter.AbstractMapType(type.AsMap), varMapping);
+ //
+ } else {
+ System.Diagnostics.Debug.Fail("Don't know how to handle this type: " + type);
+ assert false; // please the compiler
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ public TypeAxiomBuilder(VCExpressionGenerator! gen) {
+ this.Gen = gen;
+ AllTypeAxioms = new List<VCExpr!> ();
+ IncTypeAxioms = new List<VCExpr!> ();
+ BasicTypeReprs = new Dictionary<Type!, VCExpr!> ();
+ CurrentCtorNum = BigNum.ZERO;
+ TypeCtorReprs = new Dictionary<TypeCtorDecl!, TypeCtorRepr> ();
+ TypeVariableMapping = new Dictionary<TypeVariable!, VCExprVar!> ();
+ Typed2UntypedVariables = new Dictionary<VCExprVar!, VCExprVar!> ();
+
+ TypeCtorDecl! uDecl = new TypeCtorDecl(Token.NoToken, "U", 0);
+ UDecl = uDecl;
+ Type! u = new CtorType (Token.NoToken, uDecl, new TypeSeq ());
+ U = u;
+
+ TypeCtorDecl! tDecl = new TypeCtorDecl(Token.NoToken, "T", 0);
+ TDecl = tDecl;
+ Type! t = new CtorType (Token.NoToken, tDecl, new TypeSeq ());
+ T = t;
+
+ Ctor = HelperFuns.BoogieFunction("Ctor", t, Type.Int);
+ }
+
+ public virtual void Setup() {
+ GetBasicTypeRepr(Type.Int);
+ GetBasicTypeRepr(Type.Bool);
+ }
+
+ // constructor to allow cloning
+ internal TypeAxiomBuilder(TypeAxiomBuilder! builder) {
+ Gen = builder.Gen;
+ AllTypeAxioms = new List<VCExpr!> (builder.AllTypeAxioms);
+ IncTypeAxioms = new List<VCExpr!> (builder.IncTypeAxioms);
+
+ UDecl = builder.UDecl;
+ U = builder.U;
+
+ TDecl = builder.TDecl;
+ T = builder.T;
+
+ Ctor = builder.Ctor;
+ CurrentCtorNum = builder.CurrentCtorNum;
+
+ BasicTypeReprs = new Dictionary<Type!, VCExpr!> (builder.BasicTypeReprs);
+ TypeCtorReprs = new Dictionary<TypeCtorDecl!, TypeCtorRepr> (builder.TypeCtorReprs);
+
+ TypeVariableMapping =
+ new Dictionary<TypeVariable!, VCExprVar!> (builder.TypeVariableMapping);
+ Typed2UntypedVariables =
+ new Dictionary<VCExprVar!, VCExprVar!> (builder.Typed2UntypedVariables);
+ }
+
+ public abstract Object! Clone();
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ // Subclass of the TypeAxiomBuilder that provides all functionality
+ // to deal with native sorts of a theorem prover (that are the only
+ // types left after erasing all other types). Currently, these are:
+ //
+ // U ... sort of all individuals/objects/values
+ // T ... sort of all types
+ // int ... integers
+ // bool ... booleans
+
+ public abstract class TypeAxiomBuilderIntBoolU : TypeAxiomBuilder {
+
+ public TypeAxiomBuilderIntBoolU(VCExpressionGenerator! gen) {
+ base(gen);
+ TypeCasts = new Dictionary<Type!, TypeCastSet> ();
+ }
+
+ // constructor to allow cloning
+ internal TypeAxiomBuilderIntBoolU(TypeAxiomBuilderIntBoolU! builder) {
+ base(builder);
+ TypeCasts = new Dictionary<Type!, TypeCastSet> (builder.TypeCasts);
+ }
+
+ public override void Setup() {
+ base.Setup();
+
+ GetTypeCasts(Type.Int);
+ GetTypeCasts(Type.Bool);
+ }
+
+ // generate inverse axioms for casts (castToU(castFromU(x)) = x, under certain premisses)
+ protected abstract VCExpr! GenReverseCastAxiom(Function! castToU, Function! castFromU);
+
+ protected VCExpr! GenReverseCastEq(Function! castToU, Function! castFromU,
+ out VCExprVar! var, out List<VCTrigger!>! triggers) {
+ var = Gen.Variable("x", U);
+
+ VCExpr! lhs = Gen.Function(castToU, Gen.Function(castFromU, var));
+ triggers = HelperFuns.ToList(Gen.Trigger(true, HelperFuns.ToList(lhs)));
+
+ return Gen.Eq(lhs, var);
+ }
+
+ protected abstract VCExpr! GenCastTypeAxioms(Function! castToU, Function! castFromU);
+
+ ///////////////////////////////////////////////////////////////////////////
+ // storage of type casts for types that are supposed to be left over in the
+ // VCs (like int, bool, bitvectors)
+
+ private readonly IDictionary<Type!, TypeCastSet>! TypeCasts;
+
+ private TypeCastSet GetTypeCasts(Type! type) {
+ TypeCastSet res;
+ if (!TypeCasts.TryGetValue(type, out res)) {
+ Function! castToU = HelperFuns.BoogieFunction(type.ToString() + "_2_U", type, U);
+ Function! castFromU = HelperFuns.BoogieFunction("U_2_" + type.ToString(), U, type);
+
+ AddTypeAxiom(GenLeftInverseAxiom(castToU, castFromU, 0));
+ AddTypeAxiom(GenReverseCastAxiom(castToU, castFromU));
+ AddTypeAxiom(GenCastTypeAxioms(castToU, castFromU));
+
+ res = new TypeCastSet (castToU, castFromU);
+ TypeCasts.Add(type, res);
+ }
+ return res;
+ }
+
+ public Function! CastTo(Type! type)
+ requires UnchangedType(type); {
+ return GetTypeCasts(type).CastFromU;
+ }
+
+ public Function! CastFrom(Type! type)
+ requires UnchangedType(type); {
+ return GetTypeCasts(type).CastToU;
+ }
+
+ private struct TypeCastSet {
+ public readonly Function! CastToU;
+ public readonly Function! CastFromU;
+
+ public TypeCastSet(Function! castToU, Function! castFromU) {
+ CastToU = castToU;
+ CastFromU = castFromU;
+ }
+ }
+
+ public bool IsCast(Function! fun) {
+ if (fun.InParams.Length != 1)
+ return false;
+ Type! inType = ((!)fun.InParams[0]).TypedIdent.Type;
+ if (inType.Equals(U)) {
+ Type! outType = ((!)fun.OutParams[0]).TypedIdent.Type;
+ if (!TypeCasts.ContainsKey(outType))
+ return false;
+ return fun.Equals(CastTo(outType));
+ } else {
+ if (!TypeCasts.ContainsKey(inType))
+ return false;
+ Type! outType = ((!)fun.OutParams[0]).TypedIdent.Type;
+ if (!outType.Equals(U))
+ return false;
+ return fun.Equals(CastFrom(inType));
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ // the only types that we allow in "untyped" expressions are U,
+ // Type.Int, and Type.Bool
+
+ public override Type! TypeAfterErasure(Type! type) {
+ if (UnchangedType(type))
+ // these types are kept
+ return type;
+ else
+ // all other types are replaced by U
+ return U;
+ }
+
+ [Pure]
+ public override bool UnchangedType(Type! type) {
+ return type.IsInt || type.IsBool || type.IsBv;
+ }
+
+ public VCExpr! Cast(VCExpr! expr, Type! toType)
+ requires expr.Type.Equals(U) || UnchangedType(expr.Type);
+ requires toType.Equals(U) || UnchangedType(toType);
+ {
+ if (expr.Type.Equals(toType))
+ return expr;
+
+ if (toType.Equals(U)) {
+ return Gen.Function(CastFrom(expr.Type), expr);
+ } else {
+ assert expr.Type.Equals(U);
+ return Gen.Function(CastTo(toType), expr);
+ }
+ }
+
+ public List<VCExpr!>! CastSeq(List<VCExpr!>! exprs, Type! toType) {
+ List<VCExpr!>! res = new List<VCExpr!> (exprs.Count);
+ foreach (VCExpr! expr in exprs)
+ res.Add(Cast(expr, toType));
+ return res;
+ }
+
+
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+ // Class for computing most general abstractions of map types. An abstraction
+ // of a map type t is a maptype t' in which closed proper subtypes have been replaced
+ // with type variables. E.g., an abstraction of <a>[C a, int]a would be <a>[C a, b]a.
+ // We subsequently consider most general abstractions as ordinary parametrised types,
+ // i.e., "<a>[C a, b]a" would be considered as a type "M b" with polymorphically typed
+ // access functions
+ //
+ // select<a,b>(M b, C a, b) returns (a)
+ // store<a,b>(M b, C a, b, a) returns (M b)
+
+ internal abstract class MapTypeAbstractionBuilder {
+
+ protected readonly TypeAxiomBuilder! AxBuilder;
+ protected readonly VCExpressionGenerator! Gen;
+
+ internal MapTypeAbstractionBuilder(TypeAxiomBuilder! axBuilder,
+ VCExpressionGenerator! gen) {
+ this.AxBuilder = axBuilder;
+ this.Gen = gen;
+ AbstractionVariables = new List<TypeVariable!> ();
+ ClassRepresentations = new Dictionary<MapType!, MapTypeClassRepresentation> ();
+ }
+
+ // constructor for cloning
+ internal MapTypeAbstractionBuilder(TypeAxiomBuilder! axBuilder,
+ VCExpressionGenerator! gen,
+ MapTypeAbstractionBuilder! builder) {
+ this.AxBuilder = axBuilder;
+ this.Gen = gen;
+ AbstractionVariables =
+ new List<TypeVariable!> (builder.AbstractionVariables);
+ ClassRepresentations =
+ new Dictionary<MapType!, MapTypeClassRepresentation> (builder.ClassRepresentations);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Type variables used in the abstractions. We use the same variables in the
+ // same order in all abstractions in order to obtain comparable abstractions
+ // (equals, hashcode)
+
+ private readonly List<TypeVariable!>! AbstractionVariables;
+
+ private TypeVariable! AbstractionVariable(int num)
+ requires num >= 0; {
+ while (AbstractionVariables.Count <= num)
+ AbstractionVariables.Add(new TypeVariable (Token.NoToken,
+ "aVar" + AbstractionVariables.Count));
+ return AbstractionVariables[num];
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // The untyped representation of a class of map types, i.e., of a map type
+ // <a0, a1, ...>[A0, A1, ...] R, where the argument types and the result type
+ // possibly contain free type variables. For each such class, a separate type
+ // constructor and separate select/store functions are introduced.
+
+ protected struct MapTypeClassRepresentation {
+ public readonly TypeCtorDecl! RepresentingType;
+ public readonly Function! Select;
+ public readonly Function! Store;
+
+ public MapTypeClassRepresentation(TypeCtorDecl! representingType,
+ Function! select, Function! store) {
+ this.RepresentingType = representingType;
+ this.Select = select;
+ this.Store = store;
+ }
+ }
+
+ private readonly IDictionary<MapType!, MapTypeClassRepresentation>! ClassRepresentations;
+
+ protected MapTypeClassRepresentation GetClassRepresentation(MapType! abstractedType) {
+ MapTypeClassRepresentation res;
+ if (!ClassRepresentations.TryGetValue(abstractedType, out res)) {
+ int num = ClassRepresentations.Count;
+ TypeCtorDecl! synonym =
+ new TypeCtorDecl(Token.NoToken, "MapType" + num, abstractedType.FreeVariables.Length);
+
+ Function! select, store;
+ GenSelectStoreFunctions(abstractedType, synonym, out select, out store);
+
+ res = new MapTypeClassRepresentation(synonym, select, store);
+ ClassRepresentations.Add(abstractedType, res);
+ }
+ return res;
+ }
+
+ // the actual select and store functions are generated by the
+ // concrete subclasses of this class
+ protected abstract void GenSelectStoreFunctions(MapType! abstractedType,
+ TypeCtorDecl! synonymDecl,
+ out Function! select, out Function! store);
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ public Function! Select(MapType! rawType, out TypeSeq! instantiations) {
+ return AbstractAndGetRepresentation(rawType, out instantiations).Select;
+ }
+
+ public Function! Store(MapType! rawType, out TypeSeq! instantiations) {
+ return AbstractAndGetRepresentation(rawType, out instantiations).Store;
+ }
+
+ private MapTypeClassRepresentation
+ AbstractAndGetRepresentation(MapType! rawType, out TypeSeq! instantiations) {
+ instantiations = new TypeSeq ();
+ MapType! abstraction = ThinOutMapType(rawType, instantiations);
+ return GetClassRepresentation(abstraction);
+ }
+
+ public CtorType! AbstractMapType(MapType! rawType) {
+ TypeSeq! instantiations = new TypeSeq ();
+ MapType! abstraction = ThinOutMapType(rawType, instantiations);
+
+ MapTypeClassRepresentation repr = GetClassRepresentation(abstraction);
+ assume repr.RepresentingType.Arity == instantiations.Length;
+ return new CtorType(Token.NoToken, repr.RepresentingType, instantiations);
+ }
+
+ // TODO: cache the result of this operation
+ protected MapType! ThinOutMapType(MapType! rawType,
+ TypeSeq! instantiations) {
+ TypeSeq! newArguments = new TypeSeq ();
+ foreach (Type! subtype in rawType.Arguments)
+ newArguments.Add(ThinOutType(subtype, rawType.TypeParameters,
+ instantiations));
+ Type! newResult = ThinOutType(rawType.Result, rawType.TypeParameters,
+ instantiations);
+ return new MapType(Token.NoToken, rawType.TypeParameters, newArguments, newResult);
+ }
+
+ private Type! ThinOutType(Type! rawType, TypeVariableSeq! boundTypeParams,
+ // the instantiations of inserted type variables,
+ // the order corresponds to the order in which
+ // "AbstractionVariable(int)" delivers variables
+ TypeSeq! instantiations) {
+
+ if (CommandLineOptions.Clo.Monomorphize && AxBuilder.UnchangedType(rawType))
+ return rawType;
+
+ if (forall{TypeVariable! var in rawType.FreeVariables;
+ !boundTypeParams.Has(var)}) {
+ // Bingo!
+ // if the type does not contain any bound variables, we can simply
+ // replace it with a type variable
+ TypeVariable! abstractionVar = AbstractionVariable(instantiations.Length);
+ assume !boundTypeParams.Has(abstractionVar);
+ instantiations.Add(rawType);
+ return abstractionVar;
+ }
+
+ if (rawType.IsVariable) {
+ //
+ // then the variable has to be bound, we cannot do anything
+ TypeVariable! rawVar = rawType.AsVariable;
+ assume boundTypeParams.Has(rawVar);
+ return rawVar;
+ //
+ } else if (rawType.IsMap) {
+ //
+ // recursively abstract this map type and continue abstracting
+ CtorType! abstraction = AbstractMapType(rawType.AsMap);
+ return ThinOutType(abstraction, boundTypeParams, instantiations);
+ //
+ } else if (rawType.IsCtor) {
+ //
+ // traverse the subtypes
+ CtorType! rawCtorType = rawType.AsCtor;
+ TypeSeq! newArguments = new TypeSeq ();
+ foreach (Type! subtype in rawCtorType.Arguments)
+ newArguments.Add(ThinOutType(subtype, boundTypeParams,
+ instantiations));
+ return new CtorType(Token.NoToken, rawCtorType.Decl, newArguments);
+ //
+ } else {
+ System.Diagnostics.Debug.Fail("Don't know how to handle this type: " + rawType);
+ return rawType; // compiler appeasement policy
+ }
+ }
+
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ public class VariableBindings {
+ public readonly IDictionary<VCExprVar!, VCExprVar!>! VCExprVarBindings;
+ public readonly IDictionary<TypeVariable!, VCExpr!>! TypeVariableBindings;
+
+ public VariableBindings(IDictionary<VCExprVar!, VCExprVar!>! vcExprVarBindings,
+ IDictionary<TypeVariable!, VCExpr!>! typeVariableBindings) {
+ this.VCExprVarBindings = vcExprVarBindings;
+ this.TypeVariableBindings = typeVariableBindings;
+ }
+
+ public VariableBindings() {
+ this (new Dictionary<VCExprVar!, VCExprVar!> (),
+ new Dictionary<TypeVariable!, VCExpr!> ());
+ }
+
+ public VariableBindings! Clone() {
+ IDictionary<VCExprVar!, VCExprVar!>! newVCExprVarBindings =
+ new Dictionary<VCExprVar!, VCExprVar!> ();
+ foreach (KeyValuePair<VCExprVar!, VCExprVar!> pair in VCExprVarBindings)
+ newVCExprVarBindings.Add(pair);
+ IDictionary<TypeVariable!, VCExpr!>! newTypeVariableBindings =
+ new Dictionary<TypeVariable!, VCExpr!> ();
+ foreach (KeyValuePair<TypeVariable!, VCExpr!> pair in TypeVariableBindings)
+ newTypeVariableBindings.Add(pair);
+ return new VariableBindings(newVCExprVarBindings, newTypeVariableBindings);
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ // The central class for turning types VCExprs into untyped
+ // VCExprs. This class makes use of the type axiom builder to manage
+ // the available types and symbols.
+
+ public abstract class TypeEraser : MutatingVCExprVisitor<VariableBindings!> {
+
+ protected readonly TypeAxiomBuilderIntBoolU! AxBuilder;
+
+ protected abstract OpTypeEraser! OpEraser { get; }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ public TypeEraser(TypeAxiomBuilderIntBoolU! axBuilder, VCExpressionGenerator! gen) {
+ base(gen);
+ AxBuilder = axBuilder;
+ }
+
+ public VCExpr! Erase(VCExpr! expr, int polarity)
+ requires polarity >= -1 && polarity <= 1; {
+ this.Polarity = polarity;
+ return Mutate(expr, new VariableBindings ());
+ }
+
+ internal int Polarity = 1; // 1 for positive, -1 for negative, 0 for both
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ public override VCExpr! Visit(VCExprLiteral! node, VariableBindings! bindings) {
+ assume node.Type == Type.Bool || node.Type == Type.Int;
+ return node;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ public override VCExpr! Visit(VCExprNAry! node, VariableBindings! bindings) {
+ VCExprOp! op = node.Op;
+ if (op == VCExpressionGenerator.AndOp || op == VCExpressionGenerator.OrOp)
+ // more efficient on large conjunctions/disjunctions
+ return base.Visit(node, bindings);
+
+ // the visitor that handles all other operators
+ return node.Accept<VCExpr!, VariableBindings!>(OpEraser, bindings);
+ }
+
+ // this method is called by MutatingVCExprVisitor.Visit(VCExprNAry, ...)
+ protected override VCExpr! UpdateModifiedNode(VCExprNAry! originalNode,
+ List<VCExpr!>! newSubExprs,
+ bool changed,
+ VariableBindings! bindings) {
+ assume originalNode.Op == VCExpressionGenerator.AndOp ||
+ originalNode.Op == VCExpressionGenerator.OrOp;
+ return Gen.Function(originalNode.Op,
+ AxBuilder.Cast(newSubExprs[0], Type.Bool),
+ AxBuilder.Cast(newSubExprs[1], Type.Bool));
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ public override VCExpr! Visit(VCExprVar! node, VariableBindings! bindings) {
+ VCExprVar res;
+ if (!bindings.VCExprVarBindings.TryGetValue(node, out res))
+ return AxBuilder.Typed2Untyped(node);
+ return (!)res;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ protected bool IsUniversalQuantifier(VCExprQuantifier! node) {
+ return Polarity == 1 && node.Quan == Quantifier.EX ||
+ Polarity == -1 && node.Quan == Quantifier.ALL;
+ }
+
+ protected List<VCExprVar!>! BoundVarsAfterErasure(List<VCExprVar!>! oldBoundVars,
+ // the mapping between old and new variables
+ // is added to this bindings-object
+ VariableBindings! bindings) {
+ List<VCExprVar!>! newBoundVars = new List<VCExprVar!> (oldBoundVars.Count);
+ foreach (VCExprVar! var in oldBoundVars) {
+ Type! newType = AxBuilder.TypeAfterErasure(var.Type);
+ VCExprVar! newVar = Gen.Variable(var.Name, newType);
+ newBoundVars.Add(newVar);
+ bindings.VCExprVarBindings.Add(var, newVar);
+ }
+ return newBoundVars;
+ }
+
+ // We check whether casts Int2U or Bool2U on the bound variables
+ // occur in triggers. In case a trigger like f(Int2U(x)) occurs,
+ // it may be better to give variable x the type U and remove the
+ // cast. The following method returns true if the quantifier
+ // should be translated again with a different typing
+ protected bool RedoQuantifier(VCExprQuantifier! node,
+ VCExprQuantifier! newNode,
+ // the bound vars that actually occur in the body or
+ // in any of the triggers
+ List<VCExprVar!>! occurringVars,
+ VariableBindings! oldBindings,
+ out VariableBindings! newBindings,
+ out List<VCExprVar!>! newBoundVars) {
+ List<VCExprVar!> castVariables =
+ VariableCastCollector.FindCastVariables(node, newNode, AxBuilder);
+ if (castVariables.Count == 0) {
+ newBindings = oldBindings; // to make the compiler happy
+ newBoundVars = newNode.BoundVars; // to make the compiler happy
+ return false;
+ }
+
+ // redo everything with a different typing ...
+
+ newBindings = oldBindings.Clone();
+ newBoundVars = new List<VCExprVar!> (node.BoundVars.Count);
+ foreach (VCExprVar! var in node.BoundVars) {
+ Type! newType =
+ castVariables.Contains(var) ? AxBuilder.U
+ : AxBuilder.TypeAfterErasure(var.Type);
+ VCExprVar! newVar = Gen.Variable(var.Name, newType);
+ newBoundVars.Add(newVar);
+ newBindings.VCExprVarBindings.Add(var, newVar);
+ }
+
+ return true;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ public override VCExpr! Visit(VCExprLet! node, VariableBindings! bindings) {
+ VariableBindings! newVarBindings = bindings.Clone();
+
+ List<VCExprVar!>! newBoundVars = new List<VCExprVar!> (node.BoundVars.Count);
+ foreach (VCExprVar! var in node.BoundVars) {
+ Type! newType = AxBuilder.TypeAfterErasure(var.Type);
+ VCExprVar! newVar = Gen.Variable(var.Name, newType);
+ newBoundVars.Add(newVar);
+ newVarBindings.VCExprVarBindings.Add(var, newVar);
+ }
+
+ List<VCExprLetBinding!>! newbindings = new List<VCExprLetBinding!> (node.Length);
+ for (int i = 0; i < node.Length; ++i) {
+ VCExprLetBinding! binding = node[i];
+ VCExprVar! newVar = newBoundVars[i];
+ Type! newType = newVar.Type;
+
+ VCExpr! newE = AxBuilder.Cast(Mutate(binding.E, newVarBindings), newType);
+ newbindings.Add(Gen.LetBinding(newVar, newE));
+ }
+
+ VCExpr! newbody = Mutate(node.Body, newVarBindings);
+ return Gen.Let(newbindings, newbody);
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ public abstract class OpTypeEraser : StandardVCExprOpVisitor<VCExpr!, VariableBindings!> {
+
+ protected readonly TypeAxiomBuilderIntBoolU! AxBuilder;
+
+ protected readonly TypeEraser! Eraser;
+ protected readonly VCExpressionGenerator! Gen;
+
+ public OpTypeEraser(TypeEraser! eraser, TypeAxiomBuilderIntBoolU! axBuilder,
+ VCExpressionGenerator! gen) {
+ this.AxBuilder = axBuilder;
+ this.Eraser = eraser;
+ this.Gen = gen;
+ }
+
+ protected override VCExpr! StandardResult(VCExprNAry! node, VariableBindings! bindings) {
+ System.Diagnostics.Debug.Fail("Don't know how to erase types in this expression: " + node);
+ assert false; // to please the compiler
+ }
+
+ private List<VCExpr!>! MutateSeq(VCExprNAry! node, VariableBindings! bindings,
+ int newPolarity) {
+ int oldPolarity = Eraser.Polarity;
+ Eraser.Polarity = newPolarity;
+ List<VCExpr!>! newArgs = Eraser.MutateSeq(node, bindings);
+ Eraser.Polarity = oldPolarity;
+ return newArgs;
+ }
+
+ private VCExpr! CastArguments(VCExprNAry! node, Type! argType, VariableBindings! bindings,
+ int newPolarity) {
+ return Gen.Function(node.Op,
+ AxBuilder.CastSeq(MutateSeq(node, bindings, newPolarity),
+ argType));
+ }
+
+ // Cast the arguments of the node to their old type if necessary and possible; otherwise use
+ // their new type (int, bool, or U)
+ private VCExpr! CastArgumentsToOldType(VCExprNAry! node, VariableBindings! bindings,
+ int newPolarity)
+ requires node.Arity > 0; {
+
+ List<VCExpr!>! newArgs = MutateSeq(node, bindings, newPolarity);
+ Type! oldType = node[0].Type;
+ if (AxBuilder.UnchangedType(oldType) &&
+ forall{int i in (1:node.Arity); node[i].Type.Equals(oldType)})
+ return Gen.Function(node.Op, AxBuilder.CastSeq(newArgs, oldType));
+ else
+ return Gen.Function(node.Op, AxBuilder.CastSeq(newArgs, AxBuilder.U));
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ public override VCExpr! VisitNotOp (VCExprNAry! node, VariableBindings! bindings) {
+ return CastArguments(node, Type.Bool, bindings, -Eraser.Polarity);
+ }
+ public override VCExpr! VisitEqOp (VCExprNAry! node, VariableBindings! bindings) {
+ return CastArgumentsToOldType(node, bindings, 0);
+ }
+ public override VCExpr! VisitNeqOp (VCExprNAry! node, VariableBindings! bindings) {
+ return CastArgumentsToOldType(node, bindings, 0);
+ }
+ public override VCExpr! VisitImpliesOp (VCExprNAry! node, VariableBindings! bindings) {
+ // UGLY: the code for tracking polarities should be factored out
+ List<VCExpr!>! newArgs = new List<VCExpr!> (2);
+ Eraser.Polarity = -Eraser.Polarity;
+ newArgs.Add(Eraser.Mutate(node[0], bindings));
+ Eraser.Polarity = -Eraser.Polarity;
+ newArgs.Add(Eraser.Mutate(node[1], bindings));
+ return Gen.Function(node.Op, AxBuilder.CastSeq(newArgs, Type.Bool));
+ }
+ public override VCExpr! VisitDistinctOp (VCExprNAry! node, VariableBindings! bindings) {
+ return CastArgumentsToOldType(node, bindings, 0);
+ }
+ public override VCExpr! VisitLabelOp (VCExprNAry! node, VariableBindings! bindings) {
+ // argument of the label operator should always be a formula
+ // (at least for Simplify ... should this be ensured at a later point?)
+ return CastArguments(node, Type.Bool, bindings, Eraser.Polarity);
+ }
+ public override VCExpr! VisitAddOp (VCExprNAry! node, VariableBindings! bindings) {
+ return CastArguments(node, Type.Int, bindings, 0);
+ }
+ public override VCExpr! VisitSubOp (VCExprNAry! node, VariableBindings! bindings) {
+ return CastArguments(node, Type.Int, bindings, 0);
+ }
+ public override VCExpr! VisitMulOp (VCExprNAry! node, VariableBindings! bindings) {
+ return CastArguments(node, Type.Int, bindings, 0);
+ }
+ public override VCExpr! VisitDivOp (VCExprNAry! node, VariableBindings! bindings) {
+ return CastArguments(node, Type.Int, bindings, 0);
+ }
+ public override VCExpr! VisitModOp (VCExprNAry! node, VariableBindings! bindings) {
+ return CastArguments(node, Type.Int, bindings, 0);
+ }
+ public override VCExpr! VisitLtOp (VCExprNAry! node, VariableBindings! bindings) {
+ return CastArguments(node, Type.Int, bindings, 0);
+ }
+ public override VCExpr! VisitLeOp (VCExprNAry! node, VariableBindings! bindings) {
+ return CastArguments(node, Type.Int, bindings, 0);
+ }
+ public override VCExpr! VisitGtOp (VCExprNAry! node, VariableBindings! bindings) {
+ return CastArguments(node, Type.Int, bindings, 0);
+ }
+ public override VCExpr! VisitGeOp (VCExprNAry! node, VariableBindings! bindings) {
+ return CastArguments(node, Type.Int, bindings, 0);
+ }
+ public override VCExpr! VisitSubtypeOp (VCExprNAry! node, VariableBindings! bindings) {
+ return CastArguments(node, AxBuilder.U, bindings, 0);
+ }
+ public override VCExpr! VisitBvOp (VCExprNAry! node, VariableBindings! bindings) {
+ return CastArgumentsToOldType(node, bindings, 0);
+ }
+ public override VCExpr! VisitBvExtractOp(VCExprNAry! node, VariableBindings! bindings) {
+ return CastArgumentsToOldType(node, bindings, 0);
+ }
+ public override VCExpr! VisitBvConcatOp (VCExprNAry! node, VariableBindings! bindings) {
+ List<VCExpr!>! newArgs = MutateSeq(node, bindings, 0);
+
+ // each argument is cast to its old type
+ assert newArgs.Count == node.Arity && newArgs.Count == 2;
+ VCExpr! arg0 = AxBuilder.Cast(newArgs[0], node[0].Type);
+ VCExpr! arg1 = AxBuilder.Cast(newArgs[1], node[1].Type);
+
+ return Gen.Function(node.Op, arg0, arg1);
+ }
+
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ /// <summary>
+ /// Collect all variables x occurring in expressions of the form Int2U(x) or Bool2U(x), and
+ /// collect all variables x occurring outside such forms.
+ /// </summary>
+ internal class VariableCastCollector : TraversingVCExprVisitor<bool, bool> {
+ /// <summary>
+ /// Determine those bound variables in "oldNode" <em>all</em> of whose relevant uses
+ /// have to be cast in potential triggers in "newNode". It is assume that
+ /// the bound variables of "oldNode" correspond to the first bound
+ /// variables of "newNode".
+ /// </summary>
+ public static List<VCExprVar!>! FindCastVariables(VCExprQuantifier! oldNode,
+ VCExprQuantifier! newNode,
+ TypeAxiomBuilderIntBoolU! axBuilder) {
+ VariableCastCollector! collector = new VariableCastCollector(axBuilder);
+ if (exists{VCTrigger! trigger in newNode.Triggers; trigger.Pos}) {
+ // look in the given triggers
+ foreach (VCTrigger! trigger in newNode.Triggers)
+ if (trigger.Pos)
+ foreach (VCExpr! expr in trigger.Exprs)
+ collector.Traverse(expr, true);
+ } else {
+ // look in the body of the quantifier
+ collector.Traverse(newNode.Body, true);
+ }
+
+ List<VCExprVar!>! castVariables = new List<VCExprVar!> (collector.varsInCasts.Count);
+ foreach (VCExprVar! castVar in collector.varsInCasts) {
+ int i = newNode.BoundVars.IndexOf(castVar);
+ if (0 <= i && i < oldNode.BoundVars.Count && !collector.varsOutsideCasts.ContainsKey(castVar))
+ castVariables.Add(oldNode.BoundVars[i]);
+ }
+ return castVariables;
+ }
+
+ public VariableCastCollector(TypeAxiomBuilderIntBoolU! axBuilder) {
+ this.AxBuilder = axBuilder;
+ }
+
+ readonly List<VCExprVar!>! varsInCasts = new List<VCExprVar!> ();
+ readonly Dictionary<VCExprVar!,object>! varsOutsideCasts = new Dictionary<VCExprVar!,object> ();
+
+ readonly TypeAxiomBuilderIntBoolU! AxBuilder;
+
+ protected override bool StandardResult(VCExpr! node, bool arg) {
+ return true; // not used
+ }
+
+ public override bool Visit(VCExprNAry! node, bool arg) {
+ if (node.Op is VCExprBoogieFunctionOp) {
+ Function! func = ((VCExprBoogieFunctionOp)node.Op).Func;
+ if ((AxBuilder.IsCast(func)) && node[0] is VCExprVar) {
+ VCExprVar castVar = (VCExprVar)node[0];
+ if (!varsInCasts.Contains(castVar))
+ varsInCasts.Add(castVar);
+ return true;
+ }
+ } else if (node.Op is VCExprNAryOp) {
+ VCExpressionGenerator.SingletonOp op = VCExpressionGenerator.SingletonOpDict[node.Op];
+ switch(op) {
+ // the following operators cannot be used in triggers, so disregard any uses of variables as direct arguments
+ case VCExpressionGenerator.SingletonOp.NotOp:
+ case VCExpressionGenerator.SingletonOp.EqOp:
+ case VCExpressionGenerator.SingletonOp.NeqOp:
+ case VCExpressionGenerator.SingletonOp.AndOp:
+ case VCExpressionGenerator.SingletonOp.OrOp:
+ case VCExpressionGenerator.SingletonOp.ImpliesOp:
+ case VCExpressionGenerator.SingletonOp.LtOp:
+ case VCExpressionGenerator.SingletonOp.LeOp:
+ case VCExpressionGenerator.SingletonOp.GtOp:
+ case VCExpressionGenerator.SingletonOp.GeOp:
+ foreach (VCExpr n in node) {
+ if (!(n is VCExprVar)) { // don't recurse on VCExprVar argument
+ n.Accept<bool,bool>(this, arg);
+ }
+ }
+ return true;
+ default:
+ break;
+ }
+ }
+ return base.Visit(node, arg);
+ }
+
+ public override bool Visit(VCExprVar! node, bool arg) {
+ if (!varsOutsideCasts.ContainsKey(node))
+ varsOutsideCasts.Add(node, null);
+ return true;
+ }
+ }
+
+}
diff --git a/Source/VCExpr/TypeErasureArguments.ssc b/Source/VCExpr/TypeErasureArguments.ssc
new file mode 100644
index 00000000..434866f8
--- /dev/null
+++ b/Source/VCExpr/TypeErasureArguments.ssc
@@ -0,0 +1,618 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Text;
+using System.IO;
+using System.Collections;
+using System.Collections.Generic;
+using Microsoft.Contracts;
+using Microsoft.Basetypes;
+using Microsoft.Boogie.VCExprAST;
+
+// Erasure of types using explicit type parameters for functions
+
+namespace Microsoft.Boogie.TypeErasure
+{
+ using Microsoft.Boogie.VCExprAST;
+ using HFNS = Microsoft.Boogie.VCExprAST.HelperFuns;
+
+ public class TypeAxiomBuilderArguments : TypeAxiomBuilderIntBoolU {
+
+ public TypeAxiomBuilderArguments(VCExpressionGenerator! gen) {
+ base(gen);
+ Typed2UntypedFunctions = new Dictionary<Function!, Function!> ();
+ }
+
+ // constructor to allow cloning
+ [NotDelayed]
+ internal TypeAxiomBuilderArguments(TypeAxiomBuilderArguments! builder) {
+ Typed2UntypedFunctions =
+ new Dictionary<Function!, Function!> (builder.Typed2UntypedFunctions);
+ base(builder);
+
+ MapTypeAbstracterAttr =
+ builder.MapTypeAbstracterAttr == null ?
+ null : new MapTypeAbstractionBuilderArguments(this, builder.Gen,
+ builder.MapTypeAbstracterAttr);
+ }
+
+ public override Object! Clone() {
+ return new TypeAxiomBuilderArguments(this);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////
+
+ // generate axioms of the kind "forall x:U. {Int2U(U2Int(x))} Int2U(U2Int(x))==x"
+ // (this makes use of the assumption that only well-typed terms are generated
+ // by the SMT-solver, i.e., that U2Int is only applied to terms that actually
+ // are of type int)
+ protected override VCExpr! GenReverseCastAxiom(Function! castToU,
+ Function! castFromU) {
+ List<VCTrigger!>! triggers;
+ VCExprVar! var;
+ VCExpr! eq = GenReverseCastEq(castToU, castFromU, out var, out triggers);
+ return Gen.Forall(HelperFuns.ToList(var), triggers, "cast:" + castFromU.Name, eq);
+ }
+
+ protected override VCExpr! GenCastTypeAxioms(Function! castToU,
+ Function! castFromU) {
+ // nothing
+ return VCExpressionGenerator.True;
+ }
+
+ private MapTypeAbstractionBuilderArguments MapTypeAbstracterAttr = null;
+
+ internal override MapTypeAbstractionBuilder! MapTypeAbstracter { get {
+ if (MapTypeAbstracterAttr == null)
+ MapTypeAbstracterAttr = new MapTypeAbstractionBuilderArguments (this, Gen);
+ return MapTypeAbstracterAttr;
+ } }
+
+ protected override void AddVarTypeAxiom(VCExprVar! var, Type! originalType) {
+ // no axioms are needed for variable or function types
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Symbols for representing functions
+
+ // Globally defined functions
+ private readonly IDictionary<Function!, Function!>! Typed2UntypedFunctions;
+
+ public Function! Typed2Untyped(Function! fun) {
+ Function res;
+ if (!Typed2UntypedFunctions.TryGetValue(fun, out res)) {
+ assert fun.OutParams.Length == 1;
+
+ // if all of the parameters are int or bool, the function does
+ // not have to be changed
+ if (forall{Formal f in fun.InParams; UnchangedType(((!)f).TypedIdent.Type)} &&
+ UnchangedType(((!)fun.OutParams[0]).TypedIdent.Type)) {
+ res = fun;
+ } else {
+ Type[]! types = new Type [fun.TypeParameters.Length + fun.InParams.Length + 1];
+
+ int i = 0;
+ // the first arguments are the explicit type parameters
+ for (int j = 0; j < fun.TypeParameters.Length; ++j) {
+ types[i] = T;
+ i = i + 1;
+ }
+ // followed by the actual parameters
+ foreach (Variable! x in fun.InParams) {
+ types[i] = TypeAfterErasure(x.TypedIdent.Type);
+ i = i + 1;
+ }
+
+ types[types.Length - 1] = TypeAfterErasure(((!)fun.OutParams[0]).TypedIdent.Type);
+
+ res = HelperFuns.BoogieFunction(fun.Name, types);
+ res.Attributes = fun.Attributes;
+ }
+
+ Typed2UntypedFunctions.Add(fun, res);
+ }
+ return (!)res;
+ }
+
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ internal class MapTypeAbstractionBuilderArguments : MapTypeAbstractionBuilder {
+
+ private readonly TypeAxiomBuilderArguments! AxBuilderArguments;
+
+ internal MapTypeAbstractionBuilderArguments(TypeAxiomBuilderArguments! axBuilder,
+ VCExpressionGenerator! gen) {
+ base(axBuilder, gen);
+ this.AxBuilderArguments = axBuilder;
+ }
+
+ // constructor for cloning
+ internal MapTypeAbstractionBuilderArguments(TypeAxiomBuilderArguments! axBuilder,
+ VCExpressionGenerator! gen,
+ MapTypeAbstractionBuilderArguments! builder) {
+ base(axBuilder, gen, builder);
+ this.AxBuilderArguments = axBuilder;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ protected override void GenSelectStoreFunctions(MapType! abstractedType,
+ TypeCtorDecl! synonym,
+ out Function! select,
+ out Function! store) {
+ string! baseName = synonym.Name;
+ int typeParamNum = abstractedType.FreeVariables.Length +
+ abstractedType.TypeParameters.Length;
+ int arity = typeParamNum + abstractedType.Arguments.Length;
+
+ Type![]! selectTypes = new Type! [arity + 2];
+ Type![]! storeTypes = new Type! [arity + 3];
+
+ int i = 0;
+ // Fill in the free variables and type parameters
+ for (; i < typeParamNum; i++) {
+ selectTypes[i] = AxBuilder.T;
+ storeTypes[i] = AxBuilder.T;
+ }
+ // Fill in the map type
+ selectTypes[i] = AxBuilder.U;
+ storeTypes[i] = AxBuilder.U;
+ i++;
+ // Fill in the index types
+ foreach (Type! type in abstractedType.Arguments)
+ {
+ if (CommandLineOptions.Clo.Monomorphize && AxBuilder.UnchangedType(type))
+ {
+ selectTypes[i] = type;
+ storeTypes[i] = type;
+ }
+ else
+ {
+ selectTypes[i] = AxBuilder.U;
+ storeTypes[i] = AxBuilder.U;
+ }
+ i++;
+ }
+ // Fill in the output type for select function which also happens
+ // to be the type of the last argument to the store function
+ if (CommandLineOptions.Clo.Monomorphize && AxBuilder.UnchangedType(abstractedType.Result))
+ {
+ selectTypes[i] = abstractedType.Result;
+ storeTypes[i] = abstractedType.Result;
+ }
+ else
+ {
+ selectTypes[i] = AxBuilder.U;
+ storeTypes[i] = AxBuilder.U;
+ }
+ i++;
+ // Fill in the map type which is the output of the store function
+ storeTypes[i] = AxBuilder.U;
+ NonNullType.AssertInitialized(selectTypes);
+ NonNullType.AssertInitialized(storeTypes);
+
+ select = HelperFuns.BoogieFunction(baseName + "Select", selectTypes);
+ store = HelperFuns.BoogieFunction(baseName + "Store", storeTypes);
+
+ AxBuilder.AddTypeAxiom(GenMapAxiom0(select, store,
+ abstractedType.TypeParameters.Length, abstractedType.FreeVariables.Length));
+ AxBuilder.AddTypeAxiom(GenMapAxiom1(select, store,
+ abstractedType.TypeParameters.Length, abstractedType.FreeVariables.Length));
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // The normal axioms of the theory of arrays (right now without extensionality)
+
+ private VCExpr! Select(Function! select, List<VCExprVar!>! types,
+ VCExpr! map, List<VCExprVar!>! indexes) {
+ List<VCExpr!>! selectArgs = new List<VCExpr!> ();
+ selectArgs.AddRange(HelperFuns.ToVCExprList(types));
+ selectArgs.Add(map);
+ selectArgs.AddRange(HelperFuns.ToVCExprList(indexes));
+ return Gen.Function(select, selectArgs);
+ }
+
+ private VCExpr! Store(Function! store, List<VCExprVar!>! types,
+ VCExpr! map, List<VCExprVar!>! indexes, VCExpr! val) {
+ List<VCExpr!>! storeArgs = new List<VCExpr!> ();
+ storeArgs.AddRange(HelperFuns.ToVCExprList(types));
+ storeArgs.Add(map);
+ storeArgs.AddRange(HelperFuns.ToVCExprList(indexes));
+ storeArgs.Add(val);
+ return Gen.Function(store, storeArgs);
+ }
+
+ private VCExpr! GenMapAxiom0(Function! select, Function! store,
+ // bound type variables in the map type
+ int mapTypeParamNum,
+ // free type variables in the map
+ // type (abstraction)
+ int mapAbstractionVarNum) {
+ int arity = select.InParams.Length - 1 - mapTypeParamNum - mapAbstractionVarNum;
+ List<VCExprVar!>! types =
+ HelperFuns.VarVector("t", mapTypeParamNum + mapAbstractionVarNum,
+ AxBuilder.T, Gen);
+
+ List<Type!> indexTypes = new List<Type!>();
+ for (int i = mapTypeParamNum + mapAbstractionVarNum + 1; i < select.InParams.Length; i++)
+ {
+ indexTypes.Add(((!)select.InParams[i]).TypedIdent.Type);
+ }
+ assert arity == indexTypes.Count;
+
+ List<VCExprVar!>! indexes = HelperFuns.VarVector("x", indexTypes, Gen);
+
+ VCExprVar! m = Gen.Variable("m", AxBuilder.U);
+ VCExprVar! val = Gen.Variable("val", ((!)select.OutParams[0]).TypedIdent.Type);
+
+ VCExpr! storeExpr = Store(store, types, m, indexes, val);
+ VCExpr! selectExpr = Select(select, types, storeExpr, indexes);
+
+ List<VCExprVar!>! quantifiedVars = new List<VCExprVar!> ();
+ quantifiedVars.AddRange(types);
+ quantifiedVars.Add(val);
+ quantifiedVars.Add(m);
+ quantifiedVars.AddRange(indexes);
+
+ VCExpr! eq = Gen.Eq(selectExpr, val);
+ return Gen.Forall(quantifiedVars, new List<VCTrigger!> (),
+ "mapAx0:" + select.Name, eq);
+ }
+
+ private VCExpr! GenMapAxiom1(Function! select, Function! store,
+ // bound type variables in the map
+ // type
+ int mapTypeParamNum,
+ // free type variables in the map
+ // type (abstraction)
+ int mapAbstractionVarNum) {
+ int arity = select.InParams.Length - 1 - mapTypeParamNum - mapAbstractionVarNum;
+
+ List<VCExprVar!>! freeTypeVars =
+ HelperFuns.VarVector("u", mapAbstractionVarNum, AxBuilder.T, Gen);
+ List<VCExprVar!>! boundTypeVars0 =
+ HelperFuns.VarVector("s", mapTypeParamNum, AxBuilder.T, Gen);
+ List<VCExprVar!>! boundTypeVars1 =
+ HelperFuns.VarVector("t", mapTypeParamNum, AxBuilder.T, Gen);
+
+ List<VCExprVar!>! types0 = new List<VCExprVar!> (boundTypeVars0);
+ types0.AddRange(freeTypeVars);
+
+ List<VCExprVar!>! types1 = new List<VCExprVar!> (boundTypeVars1);
+ types1.AddRange(freeTypeVars);
+
+ List<Type!> indexTypes = new List<Type!>();
+ for (int i = mapTypeParamNum + mapAbstractionVarNum + 1; i < select.InParams.Length; i++)
+ {
+ indexTypes.Add(((!)select.InParams[i]).TypedIdent.Type);
+ }
+ assert arity == indexTypes.Count;
+
+ List<VCExprVar!>! indexes0 = HelperFuns.VarVector("x", indexTypes, Gen);
+ List<VCExprVar!>! indexes1 = HelperFuns.VarVector("y", indexTypes, Gen);
+
+ VCExprVar! m = Gen.Variable("m", AxBuilder.U);
+ VCExprVar! val = Gen.Variable("val", ((!)select.OutParams[0]).TypedIdent.Type);
+
+ VCExpr! storeExpr = Store(store, types0, m, indexes0, val);
+ VCExpr! selectWithoutStoreExpr = Select(select, types1, m, indexes1);
+ VCExpr! selectExpr = Select(select, types1, storeExpr, indexes1);
+
+ VCExpr! selectEq = Gen.Eq(selectExpr, selectWithoutStoreExpr);
+
+ List<VCExprVar!>! quantifiedVars = new List<VCExprVar!> ();
+ quantifiedVars.AddRange(freeTypeVars);
+ quantifiedVars.AddRange(boundTypeVars0);
+ quantifiedVars.AddRange(boundTypeVars1);
+ quantifiedVars.Add(val);
+ quantifiedVars.Add(m);
+ quantifiedVars.AddRange(indexes0);
+ quantifiedVars.AddRange(indexes1);
+
+ List<VCTrigger!>! triggers = new List<VCTrigger!> ();
+
+ // different value arguments or different type arguments are sufficient
+ // to conclude that that value of the map at some point (after an update)
+ // has not changed
+
+ List<VCExpr!>! indexEqs = new List<VCExpr!> ();
+ for (int i = 0; i < mapTypeParamNum; ++i)
+ indexEqs.Add(Gen.Eq(boundTypeVars0[i], boundTypeVars1[i]));
+ for (int i = 0; i < arity; ++i)
+ indexEqs.Add(Gen.Eq(indexes0[i], indexes1[i]));
+
+ VCExpr! axiom = VCExpressionGenerator.True;
+ int n = 0;
+ foreach (VCExpr! indexesEq in indexEqs) {
+ VCExpr! matrix = Gen.Or(indexesEq, selectEq);
+ VCExpr! conjunct = Gen.Forall(quantifiedVars, triggers,
+ "mapAx1:" + select.Name + ":" + n, matrix);
+ axiom = Gen.AndSimp(axiom, conjunct);
+ n = n + 1;
+ }
+
+ return axiom;
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ public class TypeEraserArguments : TypeEraser {
+
+ private readonly TypeAxiomBuilderArguments! AxBuilderArguments;
+
+ private OpTypeEraser OpEraserAttr = null;
+ protected override OpTypeEraser! OpEraser { get {
+ if (OpEraserAttr == null)
+ OpEraserAttr = new OpTypeEraserArguments(this, AxBuilderArguments, Gen);
+ return OpEraserAttr;
+ } }
+
+ public TypeEraserArguments(TypeAxiomBuilderArguments! axBuilder,
+ VCExpressionGenerator! gen) {
+ base(axBuilder, gen);
+ this.AxBuilderArguments = axBuilder;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ public override VCExpr! Visit(VCExprQuantifier! node,
+ VariableBindings! oldBindings) {
+ VariableBindings! bindings = oldBindings.Clone();
+
+ // bound term variables are replaced with bound term variables
+ // typed in a simpler way
+ List<VCExprVar!>! newBoundVars =
+ BoundVarsAfterErasure(node.BoundVars, bindings);
+
+ // type variables are replaced with ordinary quantified variables
+ GenBoundVarsForTypeParams(node.TypeParameters, newBoundVars, bindings);
+ VCExpr! newNode = HandleQuantifier(node, newBoundVars, bindings);
+
+ if (!(newNode is VCExprQuantifier) || !IsUniversalQuantifier(node))
+ return newNode;
+
+ VariableBindings! bindings2;
+ if (!RedoQuantifier(node, (VCExprQuantifier)newNode, node.BoundVars, oldBindings,
+ out bindings2, out newBoundVars))
+ return newNode;
+
+ GenBoundVarsForTypeParams(node.TypeParameters, newBoundVars, bindings2);
+ return HandleQuantifier(node, newBoundVars, bindings2);
+ }
+
+ private void GenBoundVarsForTypeParams(List<TypeVariable!>! typeParams,
+ List<VCExprVar!>! newBoundVars,
+ VariableBindings! bindings) {
+ foreach (TypeVariable! tvar in typeParams) {
+ VCExprVar! var = Gen.Variable(tvar.Name, AxBuilder.T);
+ newBoundVars.Add(var);
+ bindings.TypeVariableBindings.Add(tvar, var);
+ }
+ }
+
+ private VCExpr! HandleQuantifier(VCExprQuantifier! node,
+ List<VCExprVar!>! newBoundVars,
+ VariableBindings! bindings) {
+ List<VCTrigger!>! newTriggers = MutateTriggers(node.Triggers, bindings);
+ VCExpr! newBody = Mutate(node.Body, bindings);
+ newBody = AxBuilder.Cast(newBody, Type.Bool);
+
+ if (newBoundVars.Count == 0) // might happen that no bound variables are left
+ return newBody;
+ return Gen.Quantify(node.Quan, new List<TypeVariable!> (), newBoundVars,
+ newTriggers, node.Infos, newBody);
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ public class OpTypeEraserArguments : OpTypeEraser {
+
+ protected readonly TypeAxiomBuilderArguments! AxBuilderArguments;
+
+ public OpTypeEraserArguments(TypeEraserArguments! eraser,
+ TypeAxiomBuilderArguments! axBuilder,
+ VCExpressionGenerator! gen) {
+ base(eraser, axBuilder, gen);
+ this.AxBuilderArguments = axBuilder;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ private VCExpr! AssembleOpExpression(OpTypesPair opTypes,
+ IEnumerable<VCExpr!>! oldArgs,
+ VariableBindings! bindings) {
+ // UGLY: the code for tracking polarities should be factored out
+ int oldPolarity = Eraser.Polarity;
+ Eraser.Polarity = 0;
+
+ List<VCExpr!>! newArgs = new List<VCExpr!> ();
+ // explicit type parameters
+ foreach (Type! t in opTypes.Types)
+ newArgs.Add(AxBuilder.Type2Term(t, bindings.TypeVariableBindings));
+
+ // and the actual value parameters
+ Function! newFun = ((VCExprBoogieFunctionOp)opTypes.Op).Func;
+ // ^ we only allow this operator at this point
+ int i = opTypes.Types.Count;
+ foreach (VCExpr! arg in oldArgs) {
+ newArgs.Add(AxBuilder.Cast(Eraser.Mutate(arg, bindings),
+ ((!)newFun.InParams[i]).TypedIdent.Type));
+ i = i + 1;
+ }
+
+ Eraser.Polarity = oldPolarity;
+ return Gen.Function(opTypes.Op, newArgs);
+ }
+
+ // for the time being, we store both the types of the arguments and the explicit
+ // type parameters (for most operators, this is more than actually necessary)
+ private OpTypesPair OriginalOpTypes(VCExprNAry! node) {
+ List<Type!>! originalTypes = new List<Type!> ();
+ foreach (VCExpr! expr in node)
+ originalTypes.Add(expr.Type);
+ originalTypes.AddRange(node.TypeArguments);
+ return new OpTypesPair (node.Op, originalTypes);
+ }
+
+ private VCExpr! EqualTypes(Type! t0, Type! t1, VariableBindings! bindings) {
+ if (t0.Equals(t1))
+ return VCExpressionGenerator.True;
+ VCExpr! t0Expr = AxBuilder.Type2Term(t0, bindings.TypeVariableBindings);
+ VCExpr! t1Expr = AxBuilder.Type2Term(t1, bindings.TypeVariableBindings);
+ return Gen.Eq(t0Expr, t1Expr);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ public override VCExpr! VisitEqOp (VCExprNAry! node, VariableBindings! bindings) {
+ // we also have to state that the types are equal, because the
+ // translation does not contain any information about the
+ // relationship between values and types
+ return Gen.AndSimp(base.VisitEqOp(node, bindings),
+ EqualTypes(node[0].Type, node[1].Type, bindings));
+ }
+
+ public override VCExpr! VisitNeqOp (VCExprNAry! node, VariableBindings! bindings) {
+ // we also have to state that the types are (un)equal, because the
+ // translation does not contain any information about the
+ // relationship between values and types
+ return Gen.OrSimp(base.VisitNeqOp(node, bindings),
+ Gen.Not(EqualTypes(node[0].Type, node[1].Type, bindings)));
+ }
+
+ public override VCExpr! VisitSubtypeOp (VCExprNAry! node, VariableBindings! bindings) {
+ // UGLY: the code for tracking polarities should be factored out
+ int oldPolarity = Eraser.Polarity;
+ Eraser.Polarity = 0;
+
+ VCExpr! res =
+ Gen.Function(VCExpressionGenerator.Subtype3Op,
+ AxBuilder.Type2Term(node[0].Type,
+ bindings.TypeVariableBindings),
+ AxBuilder.Cast(Eraser.Mutate(node[0], bindings),
+ AxBuilder.U),
+ AxBuilder.Cast(Eraser.Mutate(node[1], bindings),
+ AxBuilder.U));
+
+ Eraser.Polarity = oldPolarity;
+ return res;
+ }
+
+ public override VCExpr! VisitSelectOp (VCExprNAry! node, VariableBindings! bindings) {
+ OpTypesPair originalOpTypes = OriginalOpTypes(node);
+ OpTypesPair newOpTypes;
+
+ if (!NewOpCache.TryGetValue(originalOpTypes, out newOpTypes)) {
+ MapType! rawType = node[0].Type.AsMap;
+ TypeSeq! abstractionInstantiation;
+ Function! select =
+ AxBuilder.MapTypeAbstracter.Select(rawType, out abstractionInstantiation);
+
+ newOpTypes = TypesPairForSelectStore(node, select, abstractionInstantiation);
+ NewOpCache.Add(originalOpTypes, newOpTypes);
+ }
+
+ return AssembleOpExpression(newOpTypes, node, bindings);
+ }
+
+ public override VCExpr! VisitStoreOp (VCExprNAry! node, VariableBindings! bindings) {
+ OpTypesPair originalOpTypes = OriginalOpTypes(node);
+ OpTypesPair newOpTypes;
+
+ if (!NewOpCache.TryGetValue(originalOpTypes, out newOpTypes)) {
+ MapType! rawType = node[0].Type.AsMap;
+ TypeSeq! abstractionInstantiation;
+ Function! store =
+ AxBuilder.MapTypeAbstracter.Store(rawType, out abstractionInstantiation);
+
+ newOpTypes = TypesPairForSelectStore(node, store, abstractionInstantiation);
+ NewOpCache.Add(originalOpTypes, newOpTypes);
+ }
+
+ return AssembleOpExpression(newOpTypes, node, bindings);
+ }
+
+ private OpTypesPair TypesPairForSelectStore(VCExprNAry! node, Function! untypedOp,
+ // instantiation of the abstract map type parameters
+ TypeSeq! abstractionInstantiation) {
+ List<Type!>! inferredTypeArgs = new List<Type!> ();
+ foreach (Type! t in node.TypeArguments)
+// inferredTypeArgs.Add(AxBuilder.MapTypeAbstracter.AbstractMapTypeRecursively(t));
+ inferredTypeArgs.Add(t);
+ foreach (Type! t in abstractionInstantiation)
+ inferredTypeArgs.Add(t);
+
+ assert untypedOp.InParams.Length == inferredTypeArgs.Count + node.Arity;
+ return new OpTypesPair (Gen.BoogieFunctionOp(untypedOp), inferredTypeArgs);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ public override VCExpr! VisitBoogieFunctionOp (VCExprNAry! node, VariableBindings! bindings) {
+ OpTypesPair originalOpTypes = OriginalOpTypes(node);
+ OpTypesPair newOpTypes;
+
+ if (!NewOpCache.TryGetValue(originalOpTypes, out newOpTypes)) {
+ Function! oriFun = ((VCExprBoogieFunctionOp)node.Op).Func;
+
+ List<Type!>! inferredTypeArgs = new List<Type!> ();
+ foreach (Type! t in node.TypeArguments)
+// inferredTypeArgs.Add(AxBuilder.MapTypeAbstracter.AbstractMapTypeRecursively(t));
+ inferredTypeArgs.Add(t);
+
+ VCExprOp! newOp = Gen.BoogieFunctionOp(AxBuilderArguments.Typed2Untyped(oriFun));
+ newOpTypes = new OpTypesPair (newOp, inferredTypeArgs);
+
+ NewOpCache.Add(originalOpTypes, newOpTypes);
+ }
+
+ return AssembleOpExpression(newOpTypes, node, bindings);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ // cache from the typed operators to the untyped operators with
+ // explicit type arguments. the keys are pairs of the typed
+ // operator and the actual types of the argument expressions, the
+ // values are pairs of the new operators and the types that have
+ // to be given as explicit type arguments
+ private readonly IDictionary<OpTypesPair, OpTypesPair>! NewOpCache =
+ new Dictionary<OpTypesPair, OpTypesPair>();
+
+ private struct OpTypesPair {
+ public readonly VCExprOp! Op;
+ public readonly List<Type!>! Types;
+
+ public OpTypesPair(VCExprOp! op, List<Type!>! types) {
+ this.Op = op;
+ this.Types = types;
+ this.HashCode = HFNS.PolyHash(op.GetHashCode(), 17, types);
+ }
+
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)]
+ public override bool Equals(object that) {
+ if (that is OpTypesPair) {
+ OpTypesPair thatPair = (OpTypesPair)that;
+ return this.Op.Equals(thatPair.Op) &&
+ HFNS.SameElements(this.Types, thatPair.Types);
+ }
+ return false;
+ }
+
+ private readonly int HashCode;
+
+ [Pure]
+ public override int GetHashCode() {
+ return HashCode;
+ }
+ }
+ }
+
+}
diff --git a/Source/VCExpr/TypeErasurePremisses.ssc b/Source/VCExpr/TypeErasurePremisses.ssc
new file mode 100644
index 00000000..16ec87e6
--- /dev/null
+++ b/Source/VCExpr/TypeErasurePremisses.ssc
@@ -0,0 +1,1025 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Text;
+using System.IO;
+using System.Collections;
+using System.Collections.Generic;
+using Microsoft.Contracts;
+using Microsoft.Basetypes;
+using Microsoft.Boogie.VCExprAST;
+
+// Erasure of types using premisses (forall x :: type(x)=T ==> p(x))
+
+namespace Microsoft.Boogie.TypeErasure
+{
+ using Microsoft.Boogie.VCExprAST;
+
+ // When using type premisses, we can distinguish two kinds of type
+ // parameters of a function or map: parameters that occur in the
+ // formal argument types of the function are "implicit" because they
+ // can be inferred from the actual argument types; parameters that
+ // only occur in the result type of the function are "explicit"
+ // because they are not inferrable and have to be given to the
+ // function as additional arguments.
+ //
+ // The following structure is used to store the untyped version of a
+ // typed function, together with the lists of implicit and explicit
+ // type parameters (in the same order as they occur in the signature
+ // of the original function).
+
+ internal struct UntypedFunction {
+ public readonly Function! Fun;
+ // type parameters that can be extracted from the value parameters
+ public readonly List<TypeVariable!>! ImplicitTypeParams;
+ // type parameters that have to be given explicitly
+ public readonly List<TypeVariable!>! ExplicitTypeParams;
+
+ public UntypedFunction(Function! fun,
+ List<TypeVariable!>! implicitTypeParams,
+ List<TypeVariable!>! explicitTypeParams) {
+ Fun = fun;
+ ImplicitTypeParams = implicitTypeParams;
+ ExplicitTypeParams = explicitTypeParams;
+ }
+ }
+
+ public class TypeAxiomBuilderPremisses : TypeAxiomBuilderIntBoolU {
+
+ public TypeAxiomBuilderPremisses(VCExpressionGenerator! gen) {
+ base(gen);
+ TypeFunction = HelperFuns.BoogieFunction("dummy", Type.Int);
+ Typed2UntypedFunctions = new Dictionary<Function!, UntypedFunction> ();
+ MapTypeAbstracterAttr = null;
+ }
+
+ // constructor to allow cloning
+ [NotDelayed]
+ internal TypeAxiomBuilderPremisses(TypeAxiomBuilderPremisses! builder) {
+ TypeFunction = builder.TypeFunction;
+ Typed2UntypedFunctions =
+ new Dictionary<Function!, UntypedFunction> (builder.Typed2UntypedFunctions);
+ base(builder);
+
+ MapTypeAbstracterAttr =
+ builder.MapTypeAbstracterAttr == null ?
+ null : new MapTypeAbstractionBuilderPremisses(this, builder.Gen,
+ builder.MapTypeAbstracterAttr);
+ }
+
+ public override Object! Clone() {
+ return new TypeAxiomBuilderPremisses(this);
+ }
+
+ public override void Setup() {
+ TypeFunction = HelperFuns.BoogieFunction("type", U, T);
+ base.Setup();
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ // generate axioms of the kind "forall x:U. {Int2U(U2Int(x))}
+ // type(x)=int ==> Int2U(U2Int(x))==x"
+ protected override VCExpr! GenReverseCastAxiom(Function! castToU, Function! castFromU) {
+ List<VCTrigger!>! triggers;
+ VCExprVar! var;
+ VCExpr! eq = GenReverseCastEq(castToU, castFromU, out var, out triggers);
+ VCExpr! premiss;
+ if (CommandLineOptions.Clo.TypeEncodingMethod
+ == CommandLineOptions.TypeEncoding.None)
+ premiss = VCExpressionGenerator.True;
+ else
+ premiss = GenVarTypeAxiom(var, ((!)castFromU.OutParams[0]).TypedIdent.Type,
+ // we don't have any bindings available
+ new Dictionary<TypeVariable!, VCExpr!> ());
+ VCExpr! matrix = Gen.ImpliesSimp(premiss, eq);
+ return Gen.Forall(HelperFuns.ToList(var), triggers, "cast:" + castFromU.Name, matrix);
+ }
+
+ protected override VCExpr! GenCastTypeAxioms(Function! castToU, Function! castFromU) {
+ Type! fromType = ((!)castToU.InParams[0]).TypedIdent.Type;
+ return GenFunctionAxiom(castToU, new List<TypeVariable!> (), new List<TypeVariable!> (),
+ HelperFuns.ToList(fromType), fromType);
+ }
+
+ private MapTypeAbstractionBuilderPremisses MapTypeAbstracterAttr;
+
+ internal override MapTypeAbstractionBuilder! MapTypeAbstracter { get {
+ if (MapTypeAbstracterAttr == null)
+ MapTypeAbstracterAttr = new MapTypeAbstractionBuilderPremisses (this, Gen);
+ return MapTypeAbstracterAttr;
+ } }
+
+ internal MapTypeAbstractionBuilderPremisses! MapTypeAbstracterPremisses { get {
+ return (MapTypeAbstractionBuilderPremisses)MapTypeAbstracter;
+ } }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ // function that maps individuals to their type
+ // the field is overwritten with its actual value in "Setup"
+ private Function! TypeFunction;
+
+ public VCExpr! TypeOf(VCExpr! expr) {
+ return Gen.Function(TypeFunction, expr);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Generate type premisses and type parameter bindings for quantifiers, functions, procedures
+
+ // let-bindings to extract the instantiations of type parameters
+ public List<VCExprLetBinding!>!
+ GenTypeParamBindings(// the original bound variables and (implicit) type parameters
+ List<TypeVariable!>! typeParams, List<VCExprVar!>! oldBoundVars,
+ // VariableBindings to which the translation
+ // TypeVariable -> VCExprVar is added
+ VariableBindings! bindings) {
+ // type variables are replaced with ordinary variables that are bound using a
+ // let-expression
+ foreach (TypeVariable! tvar in typeParams)
+ bindings.TypeVariableBindings.Add(tvar, Gen.Variable(tvar.Name, T));
+
+ // extract the values of type variables from the term variables
+ List<VCExprVar!>! UtypedVars = new List<VCExprVar!> (oldBoundVars.Count);
+ List<Type!>! originalTypes = new List<Type!> (oldBoundVars.Count);
+ for (int i = 0; i < oldBoundVars.Count; ++i) {
+ VCExprVar! newVar = bindings.VCExprVarBindings[oldBoundVars[i]];
+ if (newVar.Type.Equals(U)) {
+ UtypedVars.Add(newVar);
+ originalTypes.Add(oldBoundVars[i].Type);
+ }
+ }
+
+ UtypedVars.TrimExcess();
+ originalTypes.TrimExcess();
+
+ return BestTypeVarExtractors(typeParams, originalTypes, UtypedVars,
+ bindings);
+ }
+
+
+ public VCExpr! AddTypePremisses(List<VCExprLetBinding!>! typeVarBindings,
+ VCExpr! typePremisses, bool universal,
+ VCExpr! body) {
+ VCExpr! bodyWithPremisses;
+ if (universal)
+ bodyWithPremisses = Gen.ImpliesSimp(typePremisses, body);
+ else
+ bodyWithPremisses = Gen.AndSimp(typePremisses, body);
+
+ return Gen.Let(typeVarBindings, bodyWithPremisses);
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Extract the instantiations of type variables from the concrete types of
+ // term variables. E.g., for a function f<a>(x : C a), we would extract the
+ // instantiation of "a" by looking at the concrete type of "x".
+
+ public List<VCExprLetBinding!>!
+ BestTypeVarExtractors(List<TypeVariable!>! vars, List<Type!>! types,
+ List<VCExprVar!>! concreteTypeSources,
+ VariableBindings! bindings) {
+ List<VCExprLetBinding!>! typeParamBindings = new List<VCExprLetBinding!> ();
+ foreach (TypeVariable! var in vars) {
+ VCExpr extractor = BestTypeVarExtractor(var, types, concreteTypeSources);
+ if (extractor != null)
+ typeParamBindings.Add(
+ Gen.LetBinding((VCExprVar)bindings.TypeVariableBindings[var],
+ extractor));
+ }
+ return typeParamBindings;
+ }
+
+ private VCExpr BestTypeVarExtractor(TypeVariable! var, List<Type!>! types,
+ List<VCExprVar!>! concreteTypeSources) {
+ List<VCExpr!> allExtractors = TypeVarExtractors(var, types, concreteTypeSources);
+ if (allExtractors.Count == 0)
+ return null;
+
+ VCExpr bestExtractor = allExtractors[0];
+ int bestExtractorSize = SizeComputingVisitor.ComputeSize(bestExtractor);
+ for (int i = 1; i < allExtractors.Count; ++i) {
+ int newSize = SizeComputingVisitor.ComputeSize(allExtractors[i]);
+ if (newSize < bestExtractorSize) {
+ bestExtractor = allExtractors[i];
+ bestExtractorSize = newSize;
+ }
+ }
+
+ return bestExtractor;
+ }
+
+ private List<VCExpr!>! TypeVarExtractors(TypeVariable! var, List<Type!>! types,
+ List<VCExprVar!>! concreteTypeSources)
+ requires types.Count == concreteTypeSources.Count; {
+ List<VCExpr!>! res = new List<VCExpr!>();
+ for (int i = 0; i < types.Count; ++i)
+ TypeVarExtractors(var, types[i], TypeOf(concreteTypeSources[i]), res);
+
+ return res;
+ }
+
+ private void TypeVarExtractors(TypeVariable! var, Type! completeType,
+ VCExpr! innerTerm, List<VCExpr!>! extractors) {
+ if (completeType.IsVariable) {
+ if (var.Equals(completeType)) {
+ extractors.Add(innerTerm);
+ } // else nothing
+ } else if (completeType.IsBasic) {
+ // nothing
+ } else if (completeType.IsCtor) {
+ CtorType! ctorType = completeType.AsCtor;
+ if (ctorType.Arguments.Length > 0) {
+ // otherwise there are no chances of extracting any
+ // instantiations from this type
+ TypeCtorRepr repr = GetTypeCtorReprStruct(ctorType.Decl);
+ for (int i = 0; i < ctorType.Arguments.Length; ++i) {
+ VCExpr! newInnerTerm = Gen.Function(repr.Dtors[i], innerTerm);
+ TypeVarExtractors(var, ctorType.Arguments[i], newInnerTerm, extractors);
+ }
+ }
+ } else if (completeType.IsMap) {
+ TypeVarExtractors(var, MapTypeAbstracter.AbstractMapType(completeType.AsMap),
+ innerTerm, extractors);
+ } else {
+ System.Diagnostics.Debug.Fail("Don't know how to handle this type: " + completeType);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Symbols for representing functions
+
+ // Globally defined functions
+ private readonly IDictionary<Function!, UntypedFunction>! Typed2UntypedFunctions;
+
+ // distinguish between implicit and explicit type parameters
+ internal static void SeparateTypeParams(List<Type!>! valueArgumentTypes,
+ TypeVariableSeq! allTypeParams,
+ out List<TypeVariable!>! implicitParams,
+ out List<TypeVariable!>! explicitParams) {
+ TypeVariableSeq! varsInInParamTypes = new TypeVariableSeq ();
+ foreach (Type! t in valueArgumentTypes)
+ varsInInParamTypes.AppendWithoutDups(t.FreeVariables);
+
+ implicitParams = new List<TypeVariable!> (allTypeParams.Length);
+ explicitParams = new List<TypeVariable!> (allTypeParams.Length);
+
+ foreach (TypeVariable! var in allTypeParams) {
+ if (varsInInParamTypes.Has(var))
+ implicitParams.Add(var);
+ else
+ explicitParams.Add(var);
+ }
+
+ implicitParams.TrimExcess();
+ explicitParams.TrimExcess();
+ }
+
+ internal UntypedFunction Typed2Untyped(Function! fun) {
+ UntypedFunction res;
+ if (!Typed2UntypedFunctions.TryGetValue(fun, out res)) {
+ assert fun.OutParams.Length == 1;
+
+ // if all of the parameters are int or bool, the function does
+ // not have to be changed
+ if (forall{Formal f in fun.InParams; UnchangedType(((!)f).TypedIdent.Type)} &&
+ UnchangedType(((!)fun.OutParams[0]).TypedIdent.Type) &&
+ fun.TypeParameters.Length == 0) {
+ res = new UntypedFunction(fun, new List<TypeVariable!> (), new List<TypeVariable!> ());
+ } else {
+ List<Type!>! argTypes = new List<Type!> ();
+ foreach (Variable! v in fun.InParams)
+ argTypes.Add(v.TypedIdent.Type);
+
+ List<TypeVariable!>! implicitParams, explicitParams;
+ SeparateTypeParams(argTypes, fun.TypeParameters, out implicitParams, out explicitParams);
+
+ Type[]! types = new Type [explicitParams.Count + fun.InParams.Length + 1];
+ int i = 0;
+ for (int j = 0; j < explicitParams.Count; ++j) {
+ types[i] = T;
+ i = i + 1;
+ }
+ for (int j = 0; j < fun.InParams.Length; ++i, ++j)
+ types[i] = TypeAfterErasure(((!)fun.InParams[j]).TypedIdent.Type);
+ types[types.Length - 1] = TypeAfterErasure(((!)fun.OutParams[0]).TypedIdent.Type);
+
+ Function! untypedFun = HelperFuns.BoogieFunction(fun.Name, types);
+ untypedFun.Attributes = fun.Attributes;
+ res = new UntypedFunction(untypedFun, implicitParams, explicitParams);
+ if (U.Equals(types[types.Length - 1]))
+ AddTypeAxiom(GenFunctionAxiom(res, fun));
+ }
+
+ Typed2UntypedFunctions.Add(fun, res);
+ }
+ return res;
+ }
+
+ private VCExpr! GenFunctionAxiom(UntypedFunction fun, Function! originalFun) {
+ List<Type!>! originalInTypes = new List<Type!> (originalFun.InParams.Length);
+ foreach (Formal! f in originalFun.InParams)
+ originalInTypes.Add(f.TypedIdent.Type);
+
+ return GenFunctionAxiom(fun.Fun, fun.ImplicitTypeParams, fun.ExplicitTypeParams,
+ originalInTypes,
+ ((!)originalFun.OutParams[0]).TypedIdent.Type);
+ }
+
+ internal VCExpr! GenFunctionAxiom(Function! fun,
+ List<TypeVariable!>! implicitTypeParams,
+ List<TypeVariable!>! explicitTypeParams,
+ List<Type!>! originalInTypes,
+ Type! originalResultType)
+ requires originalInTypes.Count +
+ explicitTypeParams.Count ==
+ fun.InParams.Length; {
+
+ if (CommandLineOptions.Clo.TypeEncodingMethod == CommandLineOptions.TypeEncoding.None) {
+ return VCExpressionGenerator.True;
+ }
+
+ List<VCExprVar!>! typedInputVars = new List<VCExprVar!>(originalInTypes.Count);
+ int i = 0;
+ foreach (Type! t in originalInTypes) {
+ typedInputVars.Add(Gen.Variable("arg" + i, t));
+ i = i + 1;
+ }
+
+ VariableBindings! bindings = new VariableBindings ();
+
+ // type parameters that have to be given explicitly are replaced
+ // with universally quantified type variables
+ List<VCExprVar!>! boundVars = new List<VCExprVar!> (explicitTypeParams.Count + typedInputVars.Count);
+ foreach (TypeVariable! var in explicitTypeParams) {
+ VCExprVar! newVar = Gen.Variable(var.Name, T);
+ boundVars.Add(newVar);
+ bindings.TypeVariableBindings.Add(var, newVar);
+ }
+
+
+ // bound term variables are replaced with bound term variables typed in
+ // a simpler way
+ foreach (VCExprVar! var in typedInputVars) {
+ Type! newType = TypeAfterErasure(var.Type);
+ VCExprVar! newVar = Gen.Variable(var.Name, newType);
+ boundVars.Add(newVar);
+ bindings.VCExprVarBindings.Add(var, newVar);
+ }
+
+ List<VCExprLetBinding!>! typeVarBindings =
+ GenTypeParamBindings(implicitTypeParams, typedInputVars, bindings);
+
+ VCExpr! funApp = Gen.Function(fun, HelperFuns.ToVCExprList(boundVars));
+ VCExpr! conclusion = Gen.Eq(TypeOf(funApp),
+ Type2Term(originalResultType, bindings.TypeVariableBindings));
+ VCExpr conclusionWithPremisses =
+ // leave out antecedents of function type axioms ... they don't appear necessary,
+ // because a function can always be extended to all U-values (right?)
+// AddTypePremisses(typeVarBindings, typePremisses, true, conclusion);
+ Gen.Let(typeVarBindings, conclusion);
+
+ if (boundVars.Count > 0) {
+ List<VCTrigger!>! triggers =
+ HelperFuns.ToList(Gen.Trigger(true, HelperFuns.ToList(funApp)));
+ return Gen.Forall(boundVars, triggers,
+ "funType:" + fun.Name, conclusionWithPremisses);
+ } else {
+ return conclusionWithPremisses;
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ protected override void AddVarTypeAxiom(VCExprVar! var, Type! originalType) {
+ if (CommandLineOptions.Clo.TypeEncodingMethod == CommandLineOptions.TypeEncoding.None) return;
+ AddTypeAxiom(GenVarTypeAxiom(var, originalType,
+ // we don't have any bindings available
+ new Dictionary<TypeVariable!, VCExpr!> ()));
+ }
+
+ public VCExpr! GenVarTypeAxiom(VCExprVar! var, Type! originalType,
+ IDictionary<TypeVariable!, VCExpr!>! varMapping) {
+ if (!var.Type.Equals(originalType)) {
+ VCExpr! typeRepr = Type2Term(originalType, varMapping);
+ return Gen.Eq(TypeOf(var), typeRepr);
+ }
+ return VCExpressionGenerator.True;
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////////
+
+ internal class MapTypeAbstractionBuilderPremisses : MapTypeAbstractionBuilder {
+
+ private readonly TypeAxiomBuilderPremisses! AxBuilderPremisses;
+
+ internal MapTypeAbstractionBuilderPremisses(TypeAxiomBuilderPremisses! axBuilder,
+ VCExpressionGenerator! gen) {
+ base(axBuilder, gen);
+ this.AxBuilderPremisses = axBuilder;
+ }
+
+ // constructor for cloning
+ internal MapTypeAbstractionBuilderPremisses(TypeAxiomBuilderPremisses! axBuilder,
+ VCExpressionGenerator! gen,
+ MapTypeAbstractionBuilderPremisses! builder) {
+ base(axBuilder, gen, builder);
+ this.AxBuilderPremisses = axBuilder;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ // Determine the type parameters of a map type that have to be
+ // given explicitly when applying the select function (the
+ // parameters that only occur in the result type of the
+ // map). These parameters are given as a list of indexes sorted in
+ // ascending order; the index i refers to the i'th bound variable
+ // in a type <a0, a1, ..., an>[...]...
+ public List<int>! ExplicitSelectTypeParams(MapType! type) {
+ List<int> res;
+ if (!explicitSelectTypeParamsCache.TryGetValue(type, out res)) {
+ List<TypeVariable!>! explicitParams, implicitParams;
+ TypeAxiomBuilderPremisses.SeparateTypeParams(type.Arguments.ToList(),
+ type.TypeParameters,
+ out implicitParams,
+ out explicitParams);
+ res = new List<int> (explicitParams.Count);
+ foreach (TypeVariable! var in explicitParams)
+ res.Add(type.TypeParameters.IndexOf(var));
+ explicitSelectTypeParamsCache.Add(type, res);
+ }
+ return (!)res;
+ }
+
+ private IDictionary<MapType!, List<int>!>! explicitSelectTypeParamsCache =
+ new Dictionary<MapType!, List<int>!> ();
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ protected override void GenSelectStoreFunctions(MapType! abstractedType,
+ TypeCtorDecl! synonym,
+ out Function! select,
+ out Function! store) {
+ Type! mapTypeSynonym;
+ List<TypeVariable!>! typeParams;
+ List<Type!>! originalInTypes;
+ GenTypeAxiomParams(abstractedType, synonym, out mapTypeSynonym,
+ out typeParams, out originalInTypes);
+
+ // select
+ List<TypeVariable!>! explicitSelectParams;
+ select = CreateAccessFun(typeParams, originalInTypes,
+ abstractedType.Result, synonym.Name + "Select",
+ out explicitSelectParams);
+
+ // store, which gets one further argument: the assigned rhs
+ originalInTypes.Add(abstractedType.Result);
+
+ List<TypeVariable!>! explicitStoreParams;
+ store = CreateAccessFun(typeParams, originalInTypes,
+ mapTypeSynonym, synonym.Name + "Store",
+ out explicitStoreParams);
+
+ // the store function does not have any explicit type parameters
+ assert explicitStoreParams.Count == 0;
+
+ AxBuilder.AddTypeAxiom(GenMapAxiom0(select, store,
+ abstractedType.Result,
+ explicitSelectParams));
+ AxBuilder.AddTypeAxiom(GenMapAxiom1(select, store,
+ abstractedType.Result,
+ explicitSelectParams));
+ }
+
+ protected void GenTypeAxiomParams(MapType! abstractedType, TypeCtorDecl! synonymDecl,
+ out Type! mapTypeSynonym,
+ out List<TypeVariable!>! typeParams,
+ out List<Type!>! originalIndexTypes) {
+ typeParams = new List<TypeVariable!> (abstractedType.TypeParameters.Length + abstractedType.FreeVariables.Length);
+ typeParams.AddRange(abstractedType.TypeParameters.ToList());
+ typeParams.AddRange(abstractedType.FreeVariables.ToList());
+
+ originalIndexTypes = new List<Type!> (abstractedType.Arguments.Length + 1);
+ TypeSeq! mapTypeParams = new TypeSeq ();
+ foreach (TypeVariable! var in abstractedType.FreeVariables)
+ mapTypeParams.Add(var);
+ mapTypeSynonym = new CtorType (Token.NoToken, synonymDecl, mapTypeParams);
+ originalIndexTypes.Add(mapTypeSynonym);
+ originalIndexTypes.AddRange(abstractedType.Arguments.ToList());
+ }
+
+ // method to actually create the select or store function
+ private Function! CreateAccessFun(List<TypeVariable!>! originalTypeParams,
+ List<Type!>! originalInTypes,
+ Type! originalResult,
+ string! name,
+ out List<TypeVariable!>! explicitTypeParams) {
+ // select and store are basically handled like normal functions: the type
+ // parameters are split into the implicit parameters, and into the parameters
+ // that have to be given explicitly
+ List<TypeVariable!>! implicitParams;
+ TypeAxiomBuilderPremisses.SeparateTypeParams(originalInTypes,
+ HelperFuns.ToSeq(originalTypeParams),
+ out implicitParams,
+ out explicitTypeParams);
+ Type[]! ioTypes = new Type [explicitTypeParams.Count + originalInTypes.Count + 1];
+ int i = 0;
+ for (; i < explicitTypeParams.Count; ++i)
+ ioTypes[i] = AxBuilder.T;
+ foreach (Type! type in originalInTypes)
+ {
+ if (CommandLineOptions.Clo.Monomorphize && AxBuilder.UnchangedType(type))
+ ioTypes[i] = type;
+ else
+ ioTypes[i] = AxBuilder.U;
+ i++;
+ }
+ if (CommandLineOptions.Clo.Monomorphize && AxBuilder.UnchangedType(originalResult))
+ ioTypes[i] = originalResult;
+ else
+ ioTypes[i] = AxBuilder.U;
+
+ Function! res = HelperFuns.BoogieFunction(name, ioTypes);
+
+ if (AxBuilder.U.Equals(ioTypes[i]))
+ {
+ AxBuilder.AddTypeAxiom(
+ AxBuilderPremisses.GenFunctionAxiom(res,
+ implicitParams, explicitTypeParams,
+ originalInTypes, originalResult));
+ }
+ return res;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // The normal axioms of the theory of arrays (without extensionality)
+
+ private VCExpr! Select(Function! select,
+ // in general, the select function has to
+ // receive explicit type parameters (which
+ // are here already represented as VCExpr
+ // of type T)
+ List<VCExpr!>! typeParams,
+ VCExpr! map,
+ List<VCExprVar!>! indexes) {
+ List<VCExpr!>! selectArgs = new List<VCExpr!> (typeParams.Count + indexes.Count + 1);
+ selectArgs.AddRange(typeParams);
+ selectArgs.Add(map);
+ selectArgs.AddRange(HelperFuns.ToVCExprList(indexes));
+ return Gen.Function(select, selectArgs);
+ }
+
+ private VCExpr! Store(Function! store,
+ VCExpr! map,
+ List<VCExprVar!>! indexes,
+ VCExpr! val) {
+ List<VCExpr!>! storeArgs = new List<VCExpr!> (indexes.Count + 2);
+ storeArgs.Add(map);
+ storeArgs.AddRange(HelperFuns.ToVCExprList(indexes));
+ storeArgs.Add(val);
+ return Gen.Function(store, storeArgs);
+ }
+
+ private VCExpr! GenMapAxiom0(Function! select, Function! store,
+ Type! mapResult,
+ List<TypeVariable!>! explicitSelectParams) {
+ int arity = store.InParams.Length - 2;
+ VCExprVar! typedVal = Gen.Variable("val", mapResult);
+ VCExprVar! val = Gen.Variable("val", ((!)select.OutParams[0]).TypedIdent.Type);
+
+ VariableBindings! bindings = new VariableBindings ();
+ bindings.VCExprVarBindings.Add(typedVal, val);
+
+ // explicit type parameters are handled using a let-binder
+ List<VCExprLetBinding!>! letBindings =
+ AxBuilderPremisses.GenTypeParamBindings(explicitSelectParams,
+ HelperFuns.ToList(typedVal),
+ bindings);
+ List<VCExpr!>! typeParams = new List<VCExpr!> (explicitSelectParams.Count);
+ foreach (TypeVariable! var in explicitSelectParams)
+ typeParams.Add(bindings.TypeVariableBindings[var]);
+
+ List<Type!> indexTypes = new List<Type!>();
+ for (int i = 1; i < store.InParams.Length-1; i++)
+ {
+ indexTypes.Add(((!)store.InParams[i]).TypedIdent.Type);
+ }
+ assert arity == indexTypes.Count;
+
+ List<VCExprVar!> indexes = HelperFuns.VarVector("x", indexTypes, Gen);
+ VCExprVar! m = Gen.Variable("m", AxBuilder.U);
+
+ VCExpr! storeExpr = Store(store, m, indexes, val);
+ VCExpr! selectExpr = Select(select, typeParams, storeExpr, indexes);
+
+ List<VCExprVar!>! quantifiedVars = new List<VCExprVar!> (indexes.Count + 2);
+ quantifiedVars.Add(val);
+ quantifiedVars.Add(m);
+ quantifiedVars.AddRange(indexes);
+
+ VCExpr! eq = Gen.Eq(selectExpr, val);
+ VCExpr! letEq = Gen.Let(letBindings, eq);
+ return Gen.Forall(quantifiedVars, new List<VCTrigger!> (),
+ "mapAx0:" + select.Name, letEq);
+ }
+
+ private VCExpr! GenMapAxiom1(Function! select, Function! store,
+ Type! mapResult,
+ List<TypeVariable!>! explicitSelectParams) {
+ int arity = store.InParams.Length - 2;
+
+ List<Type!> indexTypes = new List<Type!>();
+ for (int i = 1; i < store.InParams.Length-1; i++)
+ {
+ indexTypes.Add(((!)store.InParams[i]).TypedIdent.Type);
+ }
+ assert indexTypes.Count == arity;
+
+ List<VCExprVar!>! indexes0 = HelperFuns.VarVector("x", indexTypes, Gen);
+ List<VCExprVar!>! indexes1 = HelperFuns.VarVector("y", indexTypes, Gen);
+ VCExprVar! m = Gen.Variable("m", AxBuilder.U);
+ VCExprVar! val = Gen.Variable("val", ((!)select.OutParams[0]).TypedIdent.Type);
+
+ // extract the explicit type parameters from the actual result type ...
+ VCExprVar! typedVal = Gen.Variable("val", mapResult);
+ VariableBindings! bindings = new VariableBindings ();
+ bindings.VCExprVarBindings.Add(typedVal, val);
+
+ List<VCExprLetBinding!>! letBindings =
+ AxBuilderPremisses.GenTypeParamBindings(explicitSelectParams,
+ HelperFuns.ToList(typedVal),
+ bindings);
+
+ // ... and quantify the introduced term variables for type
+ // parameters universally
+ List<VCExprVar!>! typeParams = new List<VCExprVar!> (explicitSelectParams.Count);
+ List<VCExpr!>! typeParamsExpr = new List<VCExpr!> (explicitSelectParams.Count);
+ foreach (TypeVariable! var in explicitSelectParams) {
+ VCExprVar! newVar = (VCExprVar)bindings.TypeVariableBindings[var];
+ typeParams.Add(newVar);
+ typeParamsExpr.Add(newVar);
+ }
+
+ VCExpr! storeExpr = Store(store, m, indexes0, val);
+ VCExpr! selectWithoutStoreExpr = Select(select, typeParamsExpr, m, indexes1);
+ VCExpr! selectExpr = Select(select, typeParamsExpr, storeExpr, indexes1);
+
+ VCExpr! selectEq = Gen.Eq(selectExpr, selectWithoutStoreExpr);
+
+ List<VCExprVar!>! quantifiedVars = new List<VCExprVar!> (indexes0.Count + indexes1.Count + 2);
+ quantifiedVars.Add(val);
+ quantifiedVars.Add(m);
+ quantifiedVars.AddRange(indexes0);
+ quantifiedVars.AddRange(indexes1);
+ quantifiedVars.AddRange(typeParams);
+
+ List<VCTrigger!>! triggers = new List<VCTrigger!> ();
+
+ VCExpr! axiom = VCExpressionGenerator.True;
+
+ // first non-interference criterium: the queried location is
+ // different from the assigned location
+ for (int i = 0; i < arity; ++i) {
+ VCExpr! indexesEq = Gen.Eq(indexes0[i], indexes1[i]);
+ VCExpr! matrix = Gen.Or(indexesEq, selectEq);
+ VCExpr! conjunct = Gen.Forall(quantifiedVars, triggers,
+ "mapAx1:" + select.Name + ":" + i, matrix);
+ axiom = Gen.AndSimp(axiom, conjunct);
+ }
+
+ // second non-interference criterion: the queried type is
+ // different from the assigned type
+ VCExpr! typesEq = VCExpressionGenerator.True;
+ foreach (VCExprLetBinding! b in letBindings)
+ typesEq = Gen.AndSimp(typesEq, Gen.Eq(b.V, b.E));
+ VCExpr! matrix2 = Gen.Or(typesEq, selectEq);
+ VCExpr! conjunct2 = Gen.Forall(quantifiedVars, triggers,
+ "mapAx2:" + select.Name, matrix2);
+ axiom = Gen.AndSimp(axiom, conjunct2);
+
+ return axiom;
+ }
+
+ }
+
+ /////////////////////////////////////////////////////////////////////////////
+
+ public class TypeEraserPremisses : TypeEraser {
+
+ private readonly TypeAxiomBuilderPremisses! AxBuilderPremisses;
+
+ private OpTypeEraser OpEraserAttr = null;
+ protected override OpTypeEraser! OpEraser { get {
+ if (OpEraserAttr == null)
+ OpEraserAttr = new OpTypeEraserPremisses(this, AxBuilderPremisses, Gen);
+ return OpEraserAttr;
+ } }
+
+ public TypeEraserPremisses(TypeAxiomBuilderPremisses! axBuilder,
+ VCExpressionGenerator! gen) {
+ base(axBuilder, gen);
+ this.AxBuilderPremisses = axBuilder;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ public override VCExpr! Visit(VCExprQuantifier! node,
+ VariableBindings! oldBindings) {
+ VariableBindings bindings = oldBindings.Clone();
+
+ // determine the bound vars that actually occur in the body or
+ // in any of the triggers (if some variables do not occur, we
+ // need to take special care of type parameters that only occur
+ // in the types of such variables)
+ FreeVariableCollector coll = new FreeVariableCollector ();
+ coll.Collect(node.Body);
+ foreach (VCTrigger trigger in node.Triggers) {
+ if (trigger.Pos)
+ foreach (VCExpr! e in trigger.Exprs)
+ coll.Collect(e);
+ }
+
+ List<VCExprVar!> occurringVars = new List<VCExprVar!> (node.BoundVars.Count);
+ foreach (VCExprVar var in node.BoundVars)
+ if (coll.FreeTermVars.ContainsKey(var))
+ occurringVars.Add(var);
+
+ occurringVars.TrimExcess();
+
+ // bound term variables are replaced with bound term variables typed in
+ // a simpler way
+ List<VCExprVar!>! newBoundVars =
+ BoundVarsAfterErasure(occurringVars, bindings);
+ VCExpr! newNode = HandleQuantifier(node, occurringVars,
+ newBoundVars, bindings);
+
+ if (!(newNode is VCExprQuantifier) || !IsUniversalQuantifier(node))
+ return newNode;
+
+ VariableBindings! bindings2;
+ if (!RedoQuantifier(node, (VCExprQuantifier)newNode, occurringVars, oldBindings,
+ out bindings2, out newBoundVars))
+ return newNode;
+
+ return HandleQuantifier(node, occurringVars,
+ newBoundVars, bindings2);
+ }
+
+ private VCExpr! GenTypePremisses(List<VCExprVar!>! oldBoundVars,
+ List<VCExprVar!>! newBoundVars,
+ IDictionary<TypeVariable!, VCExpr!>!
+ typeVarTranslation,
+ List<VCExprLetBinding!>! typeVarBindings,
+ out List<VCTrigger!>! triggers) {
+ // build a substitution of the type variables that it can be checked
+ // whether type premisses are trivial
+ VCExprSubstitution! typeParamSubstitution = new VCExprSubstitution ();
+ foreach (VCExprLetBinding! binding in typeVarBindings)
+ typeParamSubstitution[binding.V] = binding.E;
+ SubstitutingVCExprVisitor! substituter = new SubstitutingVCExprVisitor (Gen);
+
+ List<VCExpr!>! typePremisses = new List<VCExpr!> (newBoundVars.Count);
+ triggers = new List<VCTrigger!> (newBoundVars.Count);
+
+ for (int i = 0; i < newBoundVars.Count; ++i) {
+ VCExprVar! oldVar = oldBoundVars[i];
+ VCExprVar! newVar = newBoundVars[i];
+
+ VCExpr! typePremiss =
+ AxBuilderPremisses.GenVarTypeAxiom(newVar, oldVar.Type,
+ typeVarTranslation);
+
+ if (!IsTriviallyTrue(substituter.Mutate(typePremiss,
+ typeParamSubstitution))) {
+ typePremisses.Add(typePremiss);
+ // generate a negative trigger for the variable occurrence
+ // in the type premiss
+ triggers.Add(Gen.Trigger(false,
+ HelperFuns.ToList(AxBuilderPremisses.TypeOf(newVar))));
+ }
+ }
+
+ typePremisses.TrimExcess();
+ triggers.TrimExcess();
+
+ return Gen.NAry(VCExpressionGenerator.AndOp, typePremisses);
+ }
+
+ // these optimisations should maybe be moved into a separate
+ // visitor (peep-hole optimisations)
+ private bool IsTriviallyTrue(VCExpr! expr) {
+ if (expr.Equals(VCExpressionGenerator.True))
+ return true;
+
+ if (expr is VCExprNAry) {
+ VCExprNAry! naryExpr = (VCExprNAry)expr;
+ if (naryExpr.Op.Equals(VCExpressionGenerator.EqOp) &&
+ naryExpr[0].Equals(naryExpr[1]))
+ return true;
+ }
+
+ return false;
+ }
+
+ private VCExpr! HandleQuantifier(VCExprQuantifier! node,
+ List<VCExprVar!>! occurringVars,
+ List<VCExprVar!>! newBoundVars,
+ VariableBindings! bindings) {
+ List<VCExprLetBinding!>! typeVarBindings =
+ AxBuilderPremisses.GenTypeParamBindings(node.TypeParameters,
+ occurringVars, bindings);
+
+ // Check whether some of the type parameters could not be
+ // determined from the bound variable types. In this case, we
+ // quantify explicitly over these variables
+ if (typeVarBindings.Count < node.TypeParameters.Count) {
+ foreach (TypeVariable! var in node.TypeParameters) {
+ if (!exists{VCExprLetBinding! b in typeVarBindings; b.V.Equals(var)})
+ newBoundVars.Add((VCExprVar)bindings.TypeVariableBindings[var]);
+ }
+ }
+
+ // the lists of old and new bound variables for which type
+ // antecedents are to be generated
+ List<VCExprVar!>! varsWithTypeSpecs = new List<VCExprVar!> ();
+ List<VCExprVar!>! newVarsWithTypeSpecs = new List<VCExprVar!> ();
+ if (!IsUniversalQuantifier(node) ||
+ CommandLineOptions.Clo.TypeEncodingMethod
+ == CommandLineOptions.TypeEncoding.Predicates) {
+ foreach (VCExprVar! oldVar in occurringVars) {
+ varsWithTypeSpecs.Add(oldVar);
+ newVarsWithTypeSpecs.Add(bindings.VCExprVarBindings[oldVar]);
+ }
+ } // else, no type antecedents are created for any variables
+
+ List<VCTrigger!>! furtherTriggers;
+ VCExpr! typePremisses =
+ GenTypePremisses(varsWithTypeSpecs, newVarsWithTypeSpecs,
+ bindings.TypeVariableBindings,
+ typeVarBindings, out furtherTriggers);
+
+ List<VCTrigger!>! newTriggers = MutateTriggers(node.Triggers, bindings);
+ newTriggers.AddRange(furtherTriggers);
+ newTriggers = AddLets2Triggers(newTriggers, typeVarBindings);
+
+ VCExpr! newBody = Mutate(node.Body, bindings);
+
+ // assemble the new quantified formula
+
+ if (CommandLineOptions.Clo.TypeEncodingMethod
+ == CommandLineOptions.TypeEncoding.None) {
+ typePremisses = VCExpressionGenerator.True;
+ }
+
+ VCExpr! bodyWithPremisses =
+ AxBuilderPremisses.AddTypePremisses(typeVarBindings, typePremisses,
+ node.Quan == Quantifier.ALL,
+ AxBuilder.Cast(newBody, Type.Bool));
+
+ if (newBoundVars.Count == 0) // might happen that no bound variables are left
+ return bodyWithPremisses;
+
+ foreach(VCExprVar! v in newBoundVars) {
+ if (v.Type == AxBuilderPremisses.U) {
+ newTriggers.Add(Gen.Trigger(false, AxBuilderPremisses.Cast(v, Type.Int)));
+ newTriggers.Add(Gen.Trigger(false, AxBuilderPremisses.Cast(v, Type.Bool)));
+ }
+ }
+
+ return Gen.Quantify(node.Quan, new List<TypeVariable!> (), newBoundVars,
+ newTriggers, node.Infos, bodyWithPremisses);
+ }
+
+ // check whether we need to add let-binders for any of the type
+ // parameters to the triggers (otherwise, the triggers will
+ // contain unbound/dangling variables for such parameters)
+ private List<VCTrigger!>! AddLets2Triggers(List<VCTrigger!>! triggers,
+ List<VCExprLetBinding!>! typeVarBindings) {
+ List<VCTrigger!>! triggersWithLets = new List<VCTrigger!> (triggers.Count);
+
+ foreach (VCTrigger! t in triggers) {
+ List<VCExpr!>! exprsWithLets = new List<VCExpr!> (t.Exprs.Count);
+
+ bool changed = false;
+ foreach (VCExpr! e in t.Exprs) {
+ Dictionary<VCExprVar!,object>! freeVars =
+ FreeVariableCollector.FreeTermVariables(e);
+
+ if (exists{VCExprLetBinding! b in typeVarBindings;
+ freeVars.ContainsKey(b.V)}) {
+ exprsWithLets.Add(Gen.Let(typeVarBindings, e));
+ changed = true;
+ } else {
+ exprsWithLets.Add(e);
+ }
+ }
+
+ if (changed)
+ triggersWithLets.Add(Gen.Trigger(t.Pos, exprsWithLets));
+ else
+ triggersWithLets.Add(t);
+ }
+
+ return triggersWithLets;
+ }
+
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ public class OpTypeEraserPremisses : OpTypeEraser {
+
+ private TypeAxiomBuilderPremisses! AxBuilderPremisses;
+
+ public OpTypeEraserPremisses(TypeEraserPremisses! eraser,
+ TypeAxiomBuilderPremisses! axBuilder,
+ VCExpressionGenerator! gen) {
+ base(eraser, axBuilder, gen);
+ this.AxBuilderPremisses = axBuilder;
+ }
+
+ private VCExpr! HandleFunctionOp(Function! newFun,
+ List<Type!>! typeArgs,
+ IEnumerable<VCExpr!>! oldArgs,
+ VariableBindings! bindings) {
+ // UGLY: the code for tracking polarities should be factored out
+ int oldPolarity = Eraser.Polarity;
+ Eraser.Polarity = 0;
+
+ List<VCExpr!>! newArgs = new List<VCExpr!> (typeArgs.Count);
+
+ // translate the explicit type arguments
+ foreach (Type! t in typeArgs)
+ newArgs.Add(AxBuilder.Type2Term(t, bindings.TypeVariableBindings));
+
+ // recursively translate the value arguments
+ foreach (VCExpr! arg in oldArgs) {
+ Type! newType = ((!)newFun.InParams[newArgs.Count]).TypedIdent.Type;
+ newArgs.Add(AxBuilder.Cast(Eraser.Mutate(arg, bindings), newType));
+ }
+
+ Eraser.Polarity = oldPolarity;
+ return Gen.Function(newFun, newArgs);
+ }
+
+ public override VCExpr! VisitSelectOp (VCExprNAry! node,
+ VariableBindings! bindings) {
+ MapType! mapType = node[0].Type.AsMap;
+ TypeSeq! instantiations; // not used
+ Function! select =
+ AxBuilder.MapTypeAbstracter.Select(mapType, out instantiations);
+
+ List<int>! explicitTypeParams =
+ AxBuilderPremisses.MapTypeAbstracterPremisses
+ .ExplicitSelectTypeParams(mapType);
+ assert select.InParams.Length == explicitTypeParams.Count + node.Arity;
+
+ List<Type!>! typeArgs = new List<Type!> (explicitTypeParams.Count);
+ foreach (int i in explicitTypeParams)
+ typeArgs.Add(node.TypeArguments[i]);
+ return HandleFunctionOp(select, typeArgs, node, bindings);
+ }
+
+ public override VCExpr! VisitStoreOp (VCExprNAry! node,
+ VariableBindings! bindings) {
+ TypeSeq! instantiations; // not used
+ Function! store =
+ AxBuilder.MapTypeAbstracter.Store(node[0].Type.AsMap, out instantiations);
+ return HandleFunctionOp(store,
+ // the store function never has explicit
+ // type parameters
+ new List<Type!> (),
+ node, bindings);
+ }
+
+ public override VCExpr! VisitBoogieFunctionOp (VCExprNAry! node,
+ VariableBindings! bindings) {
+ Function! oriFun = ((VCExprBoogieFunctionOp)node.Op).Func;
+ UntypedFunction untypedFun = AxBuilderPremisses.Typed2Untyped(oriFun);
+ assert untypedFun.Fun.InParams.Length ==
+ untypedFun.ExplicitTypeParams.Count + node.Arity;
+
+ List<Type!>! typeArgs =
+ ExtractTypeArgs(node,
+ oriFun.TypeParameters, untypedFun.ExplicitTypeParams);
+ return HandleFunctionOp(untypedFun.Fun, typeArgs, node, bindings);
+ }
+
+ private List<Type!>! ExtractTypeArgs(VCExprNAry! node,
+ TypeVariableSeq! allTypeParams,
+ List<TypeVariable!>! explicitTypeParams) {
+ List<Type!>! res = new List<Type!> (explicitTypeParams.Count);
+ foreach (TypeVariable! var in explicitTypeParams)
+ // this lookup could be optimised
+ res.Add(node.TypeArguments[allTypeParams.IndexOf(var)]);
+ return res;
+ }
+ }
+
+
+}
diff --git a/Source/VCExpr/VCExpr.sscproj b/Source/VCExpr/VCExpr.sscproj
new file mode 100644
index 00000000..3f3a903a
--- /dev/null
+++ b/Source/VCExpr/VCExpr.sscproj
@@ -0,0 +1,141 @@
+<?xml version="1.0" encoding="utf-8"?>
+<VisualStudioProject>
+ <XEN ProjectType="Local"
+ SchemaVersion="1.0"
+ Name="VCExpr"
+ ProjectGuid="cf42b700-10aa-4da9-8992-48a800251c11"
+ >
+ <Build>
+ <Settings ApplicationIcon=""
+ AssemblyName="VCExpr"
+ OutputType="Library"
+ RootNamespace="VCExpr"
+ StartupObject=""
+ StandardLibraryLocation=""
+ TargetPlatform="v2"
+ TargetPlatformLocation=""
+ >
+ <Config Name="Debug"
+ AllowUnsafeBlocks="False"
+ BaseAddress="285212672"
+ CheckForOverflowUnderflow="False"
+ ConfigurationOverrideFile=""
+ DefineConstants="DEBUG;TRACE"
+ DocumentationFile=""
+ DebugSymbols="True"
+ FileAlignment="4096"
+ IncrementalBuild="True"
+ Optimize="False"
+ OutputPath="bin\debug"
+ RegisterForComInterop="False"
+ RemoveIntegerChecks="false"
+ TreatWarningsAsErrors="False"
+ WarningLevel="4"
+ CheckContractAdmissibility="True"
+ CheckPurity="False"
+ />
+ <Config Name="Release"
+ AllowUnsafeBlocks="false"
+ BaseAddress="285212672"
+ CheckForOverflowUnderflow="false"
+ ConfigurationOverrideFile=""
+ DefineConstants="TRACE"
+ DocumentationFile=""
+ DebugSymbols="false"
+ FileAlignment="4096"
+ IncrementalBuild="false"
+ Optimize="true"
+ OutputPath="bin\release"
+ RegisterForComInterop="false"
+ RemoveIntegerChecks="false"
+ TreatWarningsAsErrors="false"
+ WarningLevel="4"
+ CheckContractAdmissibility="True"
+ CheckPurity="False"
+ />
+ </Settings>
+ <References>
+ <Reference Name="System"
+ AssemblyName="System"
+ Private="false"
+ />
+ <Reference Name="System.Data"
+ AssemblyName="System.Data"
+ Private="false"
+ />
+ <Reference Name="System.Xml"
+ AssemblyName="System.Xml"
+ Private="false"
+ />
+ <Reference Name="Core"
+ Project="{47BC34F1-A173-40BE-84C2-9332B4418387}"
+ Private="true"
+ />
+ <Reference Name="Basetypes"
+ Project="{0C692837-77EC-415F-BF04-395E3ED06E9A}"
+ Private="true"
+ />
+ </References>
+ </Build>
+ <Files>
+ <Include>
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="VCExprAST.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="VCExprASTVisitors.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="Boogie2VCExpr.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="VCExprASTPrinter.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="SimplifyLikeLineariser.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="TypeErasure.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="NameClashResolver.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="LetBindingSorter.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="TypeErasurePremisses.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="TypeErasureArguments.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="TermFormulaFlattening.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="Clustering.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="BigLiteralAbstracter.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="..\version.ssc"
+ />
+ </Include>
+ </Files>
+ </XEN>
+</VisualStudioProject> \ No newline at end of file
diff --git a/Source/VCExpr/VCExprAST.ssc b/Source/VCExpr/VCExprAST.ssc
new file mode 100644
index 00000000..330c76c4
--- /dev/null
+++ b/Source/VCExpr/VCExprAST.ssc
@@ -0,0 +1,1285 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Text;
+using System.IO;
+using System.Collections;
+using System.Collections.Generic;
+using Microsoft.Contracts;
+using Microsoft.Basetypes;
+
+// Prover-independent syntax trees for representing verification conditions
+// The language can be seen as a simple polymorphically typed first-order logic,
+// very similar to the expression language of Boogie
+
+namespace Microsoft.Boogie
+{
+ using Microsoft.Boogie.VCExprAST;
+
+ public class VCExpressionGenerator
+ {
+ public static readonly VCExpr! False = new VCExprLiteral (Type.Bool);
+ public static readonly VCExpr! True = new VCExprLiteral (Type.Bool);
+
+ public VCExpr! Integer(BigNum x) {
+ return new VCExprIntLit(x);
+ }
+
+ public VCExpr! Function(VCExprOp! op,
+ List<VCExpr!>! arguments,
+ List<Type!>! typeArguments) {
+ if (typeArguments.Count > 0)
+ return new VCExprMultiAry(op, arguments, typeArguments);
+
+ switch (arguments.Count) {
+ case 0: return new VCExprNullary(op);
+ case 1: return new VCExprUnary(op, arguments);
+ case 2: return new VCExprBinary(op, arguments);
+ default: return new VCExprMultiAry(op, arguments);
+ }
+ }
+
+ public VCExpr! Function(VCExprOp! op, List<VCExpr!>! arguments) {
+ return Function(op, arguments, VCExprNAry.EMPTY_TYPE_LIST);
+ }
+
+ public VCExpr! Function(VCExprOp! op, params VCExpr[]! arguments)
+ requires forall{int i in (0:arguments.Length); arguments[i] != null};
+ {
+ return Function(op,
+ HelperFuns.ToNonNullList(arguments),
+ VCExprNAry.EMPTY_TYPE_LIST);
+ }
+
+ public VCExpr! Function(VCExprOp! op, VCExpr[]! arguments, Type[]! typeArguments)
+ requires forall{int i in (0:arguments.Length); arguments[i] != null};
+ requires forall{int i in (0:typeArguments.Length); typeArguments[i] != null};
+ {
+ return Function(op,
+ HelperFuns.ToNonNullList(arguments),
+ HelperFuns.ToNonNullList(typeArguments));
+ }
+
+ public VCExpr! Function(Function! op, List<VCExpr!>! arguments) {
+ return Function(BoogieFunctionOp(op), arguments, VCExprNAry.EMPTY_TYPE_LIST);
+ }
+
+ public VCExpr! Function(Function! op, params VCExpr[]! arguments)
+ requires forall{int i in (0:arguments.Length); arguments[i] != null};
+ {
+ return Function(BoogieFunctionOp(op), arguments);
+ }
+
+
+ // The following method should really be called "ReduceLeft". It must
+ // only be used for the binary operators "and" and "or"
+ public VCExpr! NAry(VCExprOp! op, List<VCExpr!>! args) {
+ return NAry(op, args.ToArray());
+ }
+
+ public VCExpr! NAry(VCExprOp! op, params VCExpr[]! args)
+ requires forall{int i in (0:args.Length); args[i] != null};
+ requires op == AndOp || op == OrOp; {
+ bool and = (op == AndOp);
+
+ VCExpr! e = and ? True : False;
+ foreach (VCExpr a in args) {
+ e = and ? AndSimp(e, (!)a) : OrSimp(e, (!)a);
+ }
+ return e;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+
+ public static readonly VCExprOp! NotOp = new VCExprNAryOp (1, Type.Bool);
+ public static readonly VCExprOp! EqOp = new VCExprNAryOp (2, Type.Bool);
+ public static readonly VCExprOp! NeqOp = new VCExprNAryOp (2, Type.Bool);
+ public static readonly VCExprOp! AndOp = new VCExprNAryOp (2, Type.Bool);
+ public static readonly VCExprOp! OrOp = new VCExprNAryOp (2, Type.Bool);
+ public static readonly VCExprOp! ImpliesOp = new VCExprNAryOp (2, Type.Bool);
+
+ public VCExprDistinctOp! DistinctOp(int arity) {
+ return new VCExprDistinctOp (arity);
+ }
+
+ public VCExpr! Not(List<VCExpr!>! args)
+ requires args.Count == 1; {
+ return Function(NotOp, args);
+ }
+
+ public VCExpr! Not(VCExpr! e0) {
+ return Function(NotOp, e0);
+ }
+ public VCExpr! Eq(VCExpr! e0, VCExpr! e1) {
+ return Function(EqOp, e0, e1);
+ }
+ public VCExpr! Neq(VCExpr! e0, VCExpr! e1) {
+ return Function(NeqOp, e0, e1);
+ }
+ public VCExpr! And(VCExpr! e0, VCExpr! e1) {
+ return Function(AndOp, e0, e1);
+ }
+ public VCExpr! Or(VCExpr! e0, VCExpr! e1) {
+ return Function(OrOp, e0, e1);
+ }
+ public VCExpr! Implies(VCExpr! e0, VCExpr! e1) {
+ return Function(ImpliesOp, e0, e1);
+ }
+ public VCExpr! Distinct(List<VCExpr!>! args) {
+ if (args.Count <= 1)
+ // trivial case
+ return True;
+ return Function(DistinctOp(args.Count), args);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Versions of the propositional operators that automatically simplify in
+ // certain cases (if one of the operators is True or False)
+
+ public VCExpr! NotSimp(VCExpr! e0) {
+ if (e0.Equals(True))
+ return False;
+ if (e0.Equals(False))
+ return True;
+ return Not(e0);
+ }
+ public VCExpr! AndSimp(VCExpr! e0, VCExpr! e1) {
+ if (e0.Equals(True))
+ return e1;
+ if (e1.Equals(True))
+ return e0;
+ if (e0.Equals(False) || e1.Equals(False))
+ return False;
+ return And(e0, e1);
+ }
+ public VCExpr! OrSimp(VCExpr! e0, VCExpr! e1) {
+ if (e0.Equals(False))
+ return e1;
+ if (e1.Equals(False))
+ return e0;
+ if (e0.Equals(True) || e1.Equals(True))
+ return True;
+ return Or(e0, e1);
+ }
+ public VCExpr! ImpliesSimp(VCExpr! e0, VCExpr! e1) {
+ if (e0.Equals(True))
+ return e1;
+ if (e1.Equals(False))
+ return NotSimp(e0);
+ if (e0.Equals(False) || e1.Equals(True))
+ return True;
+ return Implies(e0, e1);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // Further operators
+
+ public static readonly VCExprOp! AddOp = new VCExprNAryOp (2, Type.Int);
+ public static readonly VCExprOp! SubOp = new VCExprNAryOp (2, Type.Int);
+ public static readonly VCExprOp! MulOp = new VCExprNAryOp (2, Type.Int);
+ public static readonly VCExprOp! DivOp = new VCExprNAryOp (2, Type.Int);
+ public static readonly VCExprOp! ModOp = new VCExprNAryOp (2, Type.Int);
+ public static readonly VCExprOp! LtOp = new VCExprNAryOp (2, Type.Bool);
+ public static readonly VCExprOp! LeOp = new VCExprNAryOp (2, Type.Bool);
+ public static readonly VCExprOp! GtOp = new VCExprNAryOp (2, Type.Bool);
+ public static readonly VCExprOp! GeOp = new VCExprNAryOp (2, Type.Bool);
+ public static readonly VCExprOp! SubtypeOp = new VCExprNAryOp (2, Type.Bool);
+ // ternary version of the subtype operator, the first argument of which gives
+ // the type of the compared terms
+ public static readonly VCExprOp! Subtype3Op = new VCExprNAryOp (3, Type.Bool);
+
+ public VCExprOp! BoogieFunctionOp(Function! func) {
+ return new VCExprBoogieFunctionOp(func);
+ }
+
+ // Bitvector nodes
+
+ public VCExpr! Bitvector(BvConst! bv) {
+ return Function(new VCExprBvOp(bv.Bits), Integer(bv.Value));
+ }
+
+ public VCExpr! BvExtract(VCExpr! bv, int bits, int start, int end) {
+ return Function(new VCExprBvExtractOp (start, end), bv);
+ }
+
+ public static readonly VCExprBvConcatOp! BvConcatOp = new VCExprBvConcatOp();
+ public VCExpr! BvConcat(VCExpr! bv1, VCExpr! bv2) {
+ return Function(BvConcatOp, bv1, bv2);
+ }
+
+ public VCExpr! AtMost(VCExpr! smaller, VCExpr! greater) {
+ return Function(SubtypeOp, smaller, greater);
+ }
+
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // Dispatcher for the visitor
+
+ // the declared singleton operators
+ internal enum SingletonOp { NotOp, EqOp, NeqOp, AndOp, OrOp, ImpliesOp,
+ AddOp, SubOp, MulOp,
+ DivOp, ModOp, LtOp, LeOp, GtOp, GeOp, SubtypeOp,
+ Subtype3Op, BvConcatOp };
+ internal static Dictionary<VCExprOp!, SingletonOp>! SingletonOpDict;
+
+ static VCExpressionGenerator() {
+ SingletonOpDict = new Dictionary<VCExprOp!, SingletonOp> ();
+ SingletonOpDict.Add(NotOp, SingletonOp.NotOp);
+ SingletonOpDict.Add(EqOp, SingletonOp.EqOp);
+ SingletonOpDict.Add(NeqOp, SingletonOp.NeqOp);
+ SingletonOpDict.Add(AndOp, SingletonOp.AndOp);
+ SingletonOpDict.Add(OrOp, SingletonOp.OrOp);
+ SingletonOpDict.Add(ImpliesOp, SingletonOp.ImpliesOp);
+ SingletonOpDict.Add(AddOp, SingletonOp.AddOp);
+ SingletonOpDict.Add(SubOp, SingletonOp.SubOp);
+ SingletonOpDict.Add(MulOp, SingletonOp.MulOp);
+ SingletonOpDict.Add(DivOp, SingletonOp.DivOp);
+ SingletonOpDict.Add(ModOp, SingletonOp.ModOp);
+ SingletonOpDict.Add(LtOp, SingletonOp.LtOp);
+ SingletonOpDict.Add(LeOp, SingletonOp.LeOp);
+ SingletonOpDict.Add(GtOp, SingletonOp.GtOp);
+ SingletonOpDict.Add(GeOp, SingletonOp.GeOp);
+ SingletonOpDict.Add(SubtypeOp, SingletonOp.SubtypeOp);
+ SingletonOpDict.Add(Subtype3Op,SingletonOp.Subtype3Op);
+ SingletonOpDict.Add(BvConcatOp,SingletonOp.BvConcatOp);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+
+
+ // Let-bindings
+
+ public VCExprLetBinding! LetBinding(VCExprVar! v, VCExpr! e) {
+ return new VCExprLetBinding(v, e);
+ }
+
+ // A "real" let expression. All let-bindings happen simultaneously, i.e.,
+ // at this level the order of the bindings does not matter. It is possible to
+ // create expressions like "let x = y, y = 5 in ...". All bound variables are
+ // bound in all bound terms/formulas and can occur there, but the dependencies
+ // have to be acyclic
+ public VCExpr! Let(List<VCExprLetBinding!>! bindings, VCExpr! body) {
+ if (bindings.Count == 0)
+ // no empty let-bindings
+ return body;
+ return new VCExprLet(bindings, body);
+ }
+
+ public VCExpr! Let(VCExpr! body, params VCExprLetBinding[]! bindings)
+ requires forall{int i in (0:bindings.Length); bindings[i] != null};
+ {
+ return Let(HelperFuns.ToNonNullList(bindings), body);
+ }
+
+
+ /// <summary>
+ /// In contrast to the previous method, the following methods are not a general LET.
+ /// Instead, it
+ /// is a boolean "LET b = P in Q", where P and Q are predicates, that is allowed to be
+ /// encoded as "(b == P) ==> Q" or even as "(P ==> b) ==> Q"
+ /// (or "(P ==> b) and Q" in negative positions).
+ /// The method assumes that the variables in the bindings are unique in the entire formula
+ /// to be produced, which allows the implementation to ignore scope issues in the event that
+ /// it needs to generate an alternate expression for LET.
+ /// </summary>
+
+
+ // Turn let-bindings let v = E in ... into implications E ==> v
+ public VCExpr! AsImplications(List<VCExprLetBinding!>! bindings) {
+ VCExpr! antecedents = True;
+ foreach (VCExprLetBinding b in bindings)
+ // turn "LET_binding v = E" into "v <== E"
+ antecedents = AndSimp(antecedents, Implies(b.E, b.V));
+ return antecedents;
+ }
+
+ // Turn let-bindings let v = E in ... into equations v == E
+ public VCExpr! AsEquations(List<VCExprLetBinding!>! bindings) {
+ VCExpr! antecedents = True;
+ foreach (VCExprLetBinding b in bindings)
+ // turn "LET_binding v = E" into "v <== E"
+ antecedents = AndSimp(antecedents, Eq(b.E, b.V));
+ return antecedents;
+ }
+
+
+
+ // Maps
+
+ public VCExpr! Select(params VCExpr[]! allArgs)
+ requires forall{int i in (0:allArgs.Length); allArgs[i] != null};
+ {
+ return Function(new VCExprSelectOp(allArgs.Length - 1, 0),
+ HelperFuns.ToNonNullList(allArgs),
+ VCExprNAry.EMPTY_TYPE_LIST);
+ }
+
+ public VCExpr! Select(VCExpr[]! allArgs, Type[]! typeArgs)
+ requires 1 <= allArgs.Length;
+ requires forall{int i in (0:allArgs.Length); allArgs[i] != null};
+ requires forall{int i in (0:typeArgs.Length); typeArgs[i] != null};
+ {
+ return Function(new VCExprSelectOp(allArgs.Length - 1, typeArgs.Length),
+ allArgs, typeArgs);
+ }
+
+ public VCExpr! Select(List<VCExpr!>! allArgs, List<Type!>! typeArgs)
+ requires 1 <= allArgs.Count;
+ {
+ return Function(new VCExprSelectOp(allArgs.Count - 1, typeArgs.Count),
+ allArgs, typeArgs);
+ }
+
+ public VCExpr! Store(params VCExpr[]! allArgs)
+ requires forall{int i in (0:allArgs.Length); allArgs[i] != null};
+ {
+ return Function(new VCExprStoreOp(allArgs.Length - 2, 0),
+ HelperFuns.ToNonNullList(allArgs),
+ VCExprNAry.EMPTY_TYPE_LIST);
+ }
+
+ public VCExpr! Store(VCExpr[]! allArgs, Type[]! typeArgs)
+ requires 2 <= allArgs.Length;
+ requires forall{int i in (0:allArgs.Length); allArgs[i] != null};
+ requires forall{int i in (0:typeArgs.Length); typeArgs[i] != null};
+ {
+ return Function(new VCExprStoreOp(allArgs.Length - 2, typeArgs.Length),
+ allArgs, typeArgs);
+ }
+
+ public VCExpr! Store(List<VCExpr!>! allArgs, List<Type!>! typeArgs)
+ requires 2 <= allArgs.Count;
+ {
+ return Function(new VCExprStoreOp(allArgs.Count - 2, typeArgs.Count),
+ allArgs, typeArgs);
+ }
+
+
+ // Labels
+
+ public VCExprLabelOp! LabelOp(bool pos, string! l) {
+ return new VCExprLabelOp(pos, l);
+ }
+
+ public VCExpr! LabelNeg(string! label, VCExpr! e) {
+ return Function(LabelOp(false, label), e);
+ }
+ public VCExpr! LabelPos(string! label, VCExpr! e) {
+ return Function(LabelOp(true, label), e);
+ }
+
+ // Quantifiers
+
+ public VCExpr! Quantify(Quantifier quan,
+ List<TypeVariable!>! typeParams, List<VCExprVar!>! vars,
+ List<VCTrigger!>! triggers, VCQuantifierInfos! infos,
+ VCExpr! body) {
+ return new VCExprQuantifier(quan, typeParams, vars, triggers, infos, body);
+ }
+
+ public VCExpr! Forall(List<TypeVariable!>! typeParams, List<VCExprVar!>! vars,
+ List<VCTrigger!>! triggers, VCQuantifierInfos! infos,
+ VCExpr! body) {
+ return Quantify(Quantifier.ALL, typeParams, vars, triggers, infos, body);
+ }
+ public VCExpr! Forall(List<VCExprVar!>! vars,
+ List<VCTrigger!>! triggers,
+ string! qid, VCExpr! body) {
+ return Quantify(Quantifier.ALL, new List<TypeVariable!> (), vars,
+ triggers, new VCQuantifierInfos (qid, -1, false, null), body);
+ }
+ public VCExpr! Forall(List<VCExprVar!>! vars,
+ List<VCTrigger!>! triggers,
+ VCExpr! body) {
+ return Quantify(Quantifier.ALL, new List<TypeVariable!> (), vars,
+ triggers, new VCQuantifierInfos (null, -1, false, null), body);
+ }
+ public VCExpr! Forall(VCExprVar! var, VCTrigger! trigger, VCExpr! body) {
+ return Forall(HelperFuns.ToNonNullList(var), HelperFuns.ToNonNullList(trigger), body);
+ }
+ public VCExpr! Exists(List<TypeVariable!>! typeParams, List<VCExprVar!>! vars,
+ List<VCTrigger!>! triggers, VCQuantifierInfos! infos,
+ VCExpr! body) {
+ return Quantify(Quantifier.EX, typeParams, vars, triggers, infos, body);
+ }
+ public VCExpr! Exists(List<VCExprVar!>! vars,
+ List<VCTrigger!>! triggers,
+ VCExpr! body) {
+ return Quantify(Quantifier.EX, new List<TypeVariable!> (), vars,
+ triggers, new VCQuantifierInfos (null, -1, false, null), body);
+ }
+ public VCExpr! Exists(VCExprVar! var, VCTrigger! trigger, VCExpr! body) {
+ return Exists(HelperFuns.ToNonNullList(var), HelperFuns.ToNonNullList(trigger), body);
+ }
+
+ public VCTrigger! Trigger(bool pos, List<VCExpr!>! exprs) {
+ return new VCTrigger(pos, exprs);
+ }
+
+ public VCTrigger! Trigger(bool pos, params VCExpr[]! exprs)
+ requires forall{int i in (0:exprs.Length); exprs[i] != null};
+ {
+ return Trigger(pos, HelperFuns.ToNonNullList(exprs));
+ }
+
+ // Reference to a bound or free variable
+
+ public VCExprVar! Variable(string! name, Type! type) {
+ return new VCExprVar(name, type);
+ }
+ }
+}
+
+namespace Microsoft.Boogie.VCExprAST
+{
+
+ public class HelperFuns {
+ public static bool SameElements(IEnumerable! a, IEnumerable! b) {
+ IEnumerator ia = a.GetEnumerator();
+ IEnumerator ib = b.GetEnumerator();
+ while (true) {
+ if (ia.MoveNext()) {
+ if (ib.MoveNext()) {
+ if (!((!)ia.Current).Equals(ib.Current))
+ return false;
+ } else {
+ return false;
+ }
+ } else {
+ return !ib.MoveNext();
+ }
+ }
+ return true;
+ }
+
+ public static int PolyHash(int init, int factor, IEnumerable! a) {
+ int res = init;
+ foreach(object x in a)
+ res = res * factor + ((!)x).GetHashCode();
+ return res;
+ }
+
+ public static List<T>! ToList<T>(IEnumerable<T>! l) {
+ List<T>! res = new List<T> ();
+ foreach (T x in l)
+ res.Add(x);
+ return res;
+ }
+
+ public static TypeSeq! ToTypeSeq(VCExpr[]! exprs, int startIndex)
+ requires forall{int i in (0:exprs.Length); exprs[i] != null};
+ {
+ TypeSeq! res = new TypeSeq ();
+ for (int i = startIndex; i < exprs.Length; ++i)
+ res.Add(((!)exprs[i]).Type);
+ return res;
+ }
+
+ public static List<T!>! ToNonNullList<T> (params T[]! args) {
+ List<T!>! res = new List<T!> (args.Length);
+ foreach (T t in args)
+ res.Add((!)t);
+ return res;
+ }
+
+ public static IDictionary<A, B>! Clone<A,B>(IDictionary<A,B>! dict) {
+ IDictionary<A,B>! res = new Dictionary<A,B> (dict.Count);
+ foreach (KeyValuePair<A,B> pair in dict)
+ res.Add(pair);
+ return res;
+ }
+ }
+
+ public abstract class VCExpr {
+ public abstract Type! Type { get; }
+
+ public abstract Result Accept<Result, Arg>(IVCExprVisitor<Result, Arg>! visitor, Arg arg);
+
+ [Pure]
+ public override string! ToString() {
+ StringWriter! sw = new StringWriter();
+ VCExprPrinter! printer = new VCExprPrinter ();
+ printer.Print(this, sw);
+ return (!)sw.ToString();
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////
+ // Literal expressions
+
+ public class VCExprLiteral : VCExpr {
+ private readonly Type! LitType;
+ public override Type! Type { get { return LitType; } }
+ internal VCExprLiteral(Type! type) {
+ this.LitType = type;
+ }
+ public override Result Accept<Result, Arg>(IVCExprVisitor<Result, Arg>! visitor, Arg arg) {
+ return visitor.Visit(this, arg);
+ }
+ }
+
+ public class VCExprIntLit : VCExprLiteral
+ {
+ public readonly BigNum Val;
+ internal VCExprIntLit(BigNum val) {
+ base(Type.Int);
+ this.Val = val;
+ }
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)]
+ public override bool Equals(object that) {
+ if (Object.ReferenceEquals(this, that))
+ return true;
+ if (that is VCExprIntLit)
+ return Val == ((VCExprIntLit)that).Val;
+ return false;
+ }
+ [Pure]
+ public override int GetHashCode() {
+ return Val.GetHashCode() * 72321;
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////
+ // Operator expressions with fixed arity
+
+ public abstract class VCExprNAry : VCExpr, IEnumerable<VCExpr!> {
+ public readonly VCExprOp! Op;
+ public int Arity { get { return Op.Arity; } }
+ public int TypeParamArity { get { return Op.TypeParamArity; } }
+ public int Length { get { return Arity; } }
+ // the sub-expressions of the expression
+ public abstract VCExpr! this[int index] { get; }
+
+ // the type arguments
+ public abstract List<Type!>! TypeArguments { get; }
+
+ [Pure] [GlobalAccess(false)] [Escapes(true,false)]
+ public IEnumerator<VCExpr!>! GetEnumerator() {
+ for (int i = 0; i < Arity; ++i)
+ yield return this[i];
+ }
+ [Pure] [GlobalAccess(false)] [Escapes(true,false)]
+ IEnumerator! System.Collections.IEnumerable.GetEnumerator() {
+ for (int i = 0; i < Arity; ++i)
+ yield return this[i];
+ }
+
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)]
+ public override bool Equals(object that) {
+ if (Object.ReferenceEquals(this, that))
+ return true;
+ if (that is VCExprNAry) {
+ // we compare the subterms iteratively (not recursively)
+ // to avoid stack overflows
+
+ VCExprNAryEnumerator enum0 = new VCExprNAryEnumerator(this);
+ VCExprNAryEnumerator enum1 = new VCExprNAryEnumerator((VCExprNAry)that);
+
+ while (true) {
+ bool next0 = enum0.MoveNext();
+ bool next1 = enum1.MoveNext();
+ if (next0 != next1)
+ return false;
+ if (!next0)
+ return true;
+
+ VCExprNAry nextExprNAry0 = enum0.Current as VCExprNAry;
+ VCExprNAry nextExprNAry1 = enum1.Current as VCExprNAry;
+
+ if ((nextExprNAry0 == null) != (nextExprNAry1 == null))
+ return false;
+ if (nextExprNAry0 != null && nextExprNAry1 != null) {
+ if (!nextExprNAry0.Op.Equals(nextExprNAry1.Op))
+ return false;
+ } else {
+ if (!((!)enum0.Current).Equals(enum1.Current))
+ return false;
+ }
+ }
+ }
+ return false;
+ }
+ [Pure]
+ public override int GetHashCode() {
+ return HelperFuns.PolyHash(Op.GetHashCode() * 123 + Arity * 61521,
+ 3, this);
+ }
+
+ internal VCExprNAry(VCExprOp! op) {
+ this.Op = op;
+ }
+ public override Result Accept<Result, Arg>(IVCExprVisitor<Result, Arg>! visitor, Arg arg) {
+ return visitor.Visit(this, arg);
+ }
+ public Result Accept<Result, Arg>(IVCExprOpVisitor<Result, Arg>! visitor, Arg arg) {
+ return Op.Accept(this, visitor, arg);
+ }
+
+ internal static readonly List<Type!>! EMPTY_TYPE_LIST = new List<Type!> ();
+ internal static readonly List<VCExpr!>! EMPTY_VCEXPR_LIST = new List<VCExpr!> ();
+ }
+
+ // We give specialised implementations for nullary, unary and binary expressions
+
+ internal class VCExprNullary : VCExprNAry {
+ private readonly Type! ExprType;
+ public override Type! Type { get { return ExprType; } }
+ public override VCExpr! this[int index] { get {
+ assert false; // no arguments
+ } }
+
+ // the type arguments
+ public override List<Type!>! TypeArguments { get {
+ return EMPTY_TYPE_LIST;
+ } }
+
+ internal VCExprNullary(VCExprOp! op)
+ requires op.Arity == 0 && op.TypeParamArity == 0; {
+ base(op);
+ this.ExprType = op.InferType(EMPTY_VCEXPR_LIST, EMPTY_TYPE_LIST);
+ }
+ }
+
+ internal class VCExprUnary : VCExprNAry {
+ private readonly VCExpr! Argument;
+ private readonly Type! ExprType;
+ public override Type! Type { get { return ExprType; } }
+ public override VCExpr! this[int index] { get {
+ assume index == 0;
+ return Argument;
+ } }
+
+ // the type arguments
+ public override List<Type!>! TypeArguments { get {
+ return EMPTY_TYPE_LIST;
+ } }
+
+ internal VCExprUnary(VCExprOp! op, List<VCExpr!>! arguments)
+ requires op.Arity == 1 && op.TypeParamArity == 0 && arguments.Count == 1; {
+ base(op);
+ this.Argument = arguments[0];
+ this.ExprType =
+ op.InferType(arguments, EMPTY_TYPE_LIST);
+ }
+
+ internal VCExprUnary(VCExprOp! op, VCExpr! argument)
+ requires op.Arity == 1 && op.TypeParamArity == 0; {
+ base(op);
+ this.Argument = argument;
+ // PR: could be optimised so that the argument does
+ // not have to be boxed in an array each time
+ this.ExprType =
+ op.InferType(HelperFuns.ToNonNullList(argument), EMPTY_TYPE_LIST);
+ }
+ }
+
+ internal class VCExprBinary : VCExprNAry {
+ private readonly VCExpr! Argument0;
+ private readonly VCExpr! Argument1;
+ private readonly Type! ExprType;
+ public override Type! Type { get { return ExprType; } }
+ public override VCExpr! this[int index] { get {
+ switch (index) {
+ case 0: return Argument0;
+ case 1: return Argument1;
+ default: assert false;
+ }
+ } }
+
+ // the type arguments
+ public override List<Type!>! TypeArguments { get {
+ return EMPTY_TYPE_LIST;
+ } }
+
+ internal VCExprBinary(VCExprOp! op, List<VCExpr!>! arguments)
+ requires op.Arity == 2 && op.TypeParamArity == 0 && arguments.Count == 2; {
+ base(op);
+ this.Argument0 = arguments[0];
+ this.Argument1 = arguments[1];
+ this.ExprType = op.InferType(arguments, EMPTY_TYPE_LIST);
+ }
+
+ internal VCExprBinary(VCExprOp! op, VCExpr! argument0, VCExpr! argument1)
+ requires op.Arity == 2 && op.TypeParamArity == 0; {
+ base(op);
+ this.Argument0 = argument0;
+ this.Argument1 = argument1;
+ // PR: could be optimised so that the arguments do
+ // not have to be boxed in an array each time
+ this.ExprType =
+ op.InferType(HelperFuns.ToNonNullList(argument0, argument1),
+ EMPTY_TYPE_LIST);
+ }
+ }
+
+ internal class VCExprMultiAry : VCExprNAry {
+ private readonly List<VCExpr!>! Arguments;
+ private readonly List<Type!>! TypeArgumentsAttr;
+
+ private readonly Type! ExprType;
+ public override Type! Type { get { return ExprType; } }
+ public override VCExpr! this[int index] { get {
+ assume index >= 0 && index < Arity;
+ return (!)Arguments[index];
+ } }
+
+ // the type arguments
+ public override List<Type!>! TypeArguments { get {
+ return TypeArgumentsAttr;
+ } }
+
+ internal VCExprMultiAry(VCExprOp! op, List<VCExpr!>! arguments) {
+ this(op, arguments, EMPTY_TYPE_LIST);
+ }
+ internal VCExprMultiAry(VCExprOp! op, List<VCExpr!>! arguments, List<Type!>! typeArguments)
+ requires (arguments.Count > 2 || typeArguments.Count > 0);
+ requires op.Arity == arguments.Count;
+ requires op.TypeParamArity == typeArguments.Count;
+ {
+ base(op);
+ this.Arguments = arguments;
+ this.TypeArgumentsAttr = typeArguments;
+ this.ExprType = op.InferType(arguments, typeArguments);
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////
+ // The various operators available
+
+ public abstract class VCExprOp {
+ // the number of value parameters
+ public abstract int Arity { get; }
+ // the number of type parameters
+ public abstract int TypeParamArity { get; }
+
+ public abstract Type! InferType(List<VCExpr!>! args, List<Type!>! typeArgs);
+
+ public virtual Result Accept<Result, Arg>
+ (VCExprNAry! expr, IVCExprOpVisitor<Result, Arg>! visitor, Arg arg) {
+ VCExpressionGenerator.SingletonOp op;
+ if (VCExpressionGenerator.SingletonOpDict.TryGetValue(this, out op)) {
+ switch(op) {
+ case VCExpressionGenerator.SingletonOp.NotOp:
+ return visitor.VisitNotOp(expr, arg);
+ case VCExpressionGenerator.SingletonOp.EqOp:
+ return visitor.VisitEqOp(expr, arg);
+ case VCExpressionGenerator.SingletonOp.NeqOp:
+ return visitor.VisitNeqOp(expr, arg);
+ case VCExpressionGenerator.SingletonOp.AndOp:
+ return visitor.VisitAndOp(expr, arg);
+ case VCExpressionGenerator.SingletonOp.OrOp:
+ return visitor.VisitOrOp(expr, arg);
+ case VCExpressionGenerator.SingletonOp.ImpliesOp:
+ return visitor.VisitImpliesOp(expr, arg);
+ case VCExpressionGenerator.SingletonOp.AddOp:
+ return visitor.VisitAddOp(expr, arg);
+ case VCExpressionGenerator.SingletonOp.SubOp:
+ return visitor.VisitSubOp(expr, arg);
+ case VCExpressionGenerator.SingletonOp.MulOp:
+ return visitor.VisitMulOp(expr, arg);
+ case VCExpressionGenerator.SingletonOp.DivOp:
+ return visitor.VisitDivOp(expr, arg);
+ case VCExpressionGenerator.SingletonOp.ModOp:
+ return visitor.VisitModOp(expr, arg);
+ case VCExpressionGenerator.SingletonOp.LtOp:
+ return visitor.VisitLtOp(expr, arg);
+ case VCExpressionGenerator.SingletonOp.LeOp:
+ return visitor.VisitLeOp(expr, arg);
+ case VCExpressionGenerator.SingletonOp.GtOp:
+ return visitor.VisitGtOp(expr, arg);
+ case VCExpressionGenerator.SingletonOp.GeOp:
+ return visitor.VisitGeOp(expr, arg);
+ case VCExpressionGenerator.SingletonOp.SubtypeOp:
+ return visitor.VisitSubtypeOp(expr, arg);
+ case VCExpressionGenerator.SingletonOp.Subtype3Op:
+ return visitor.VisitSubtype3Op(expr, arg);
+ case VCExpressionGenerator.SingletonOp.BvConcatOp:
+ return visitor.VisitBvConcatOp(expr, arg);
+ default:
+ assert false;
+ }
+ } else {
+ assert false;
+ }
+ }
+ }
+
+ public class VCExprNAryOp : VCExprOp {
+ private readonly Type! OpType;
+ private readonly int OpArity;
+
+ public override int Arity { get { return OpArity; } }
+ public override int TypeParamArity { get { return 0; } }
+ public override Type! InferType(List<VCExpr!>! args, List<Type!>! typeArgs) {
+ return OpType;
+ }
+
+ internal VCExprNAryOp(int arity, Type! type) {
+ this.OpArity = arity;
+ this.OpType = type;
+ }
+ }
+
+ public class VCExprDistinctOp : VCExprNAryOp {
+ internal VCExprDistinctOp(int arity) {
+ base(arity, Type.Bool);
+ }
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)]
+ public override bool Equals(object that) {
+ if (Object.ReferenceEquals(this, that))
+ return true;
+ if (that is VCExprDistinctOp)
+ return Arity == ((VCExprDistinctOp)that).Arity;
+ return false;
+ }
+ [Pure]
+ public override int GetHashCode() {
+ return Arity * 917632481;
+ }
+ public override Result Accept<Result, Arg>
+ (VCExprNAry! expr, IVCExprOpVisitor<Result, Arg>! visitor, Arg arg) {
+ return visitor.VisitDistinctOp(expr, arg);
+ }
+ }
+
+ public class VCExprLabelOp : VCExprOp {
+ public override int Arity { get { return 1; } }
+ public override int TypeParamArity { get { return 0; } }
+ public override Type! InferType(List<VCExpr!>! args, List<Type!>! typeArgs) {
+ return args[0].Type;
+ }
+
+ public readonly bool pos;
+ public readonly string! label;
+
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)]
+ public override bool Equals(object that) {
+ if (Object.ReferenceEquals(this, that))
+ return true;
+ if (that is VCExprLabelOp) {
+ VCExprLabelOp! thatOp = (VCExprLabelOp)that;
+ return this.pos == thatOp.pos && this.label.Equals(thatOp.label);
+ }
+ return false;
+ }
+ [Pure]
+ public override int GetHashCode() {
+ return (pos ? 9817231 : 7198639) + label.GetHashCode();
+ }
+
+ internal VCExprLabelOp(bool pos, string! l) {
+ this.pos = pos;
+ this.label = pos ? "+" + l : "@" + l;
+ }
+ public override Result Accept<Result, Arg>
+ (VCExprNAry! expr, IVCExprOpVisitor<Result, Arg>! visitor, Arg arg) {
+ return visitor.VisitLabelOp(expr, arg);
+ }
+ }
+
+ public class VCExprSelectOp : VCExprOp {
+ private readonly int MapArity;
+ private readonly int MapTypeParamArity;
+ public override int Arity { get { return MapArity + 1; } }
+ public override int TypeParamArity { get { return MapTypeParamArity; } }
+
+ public override Type! InferType(List<VCExpr!>! args, List<Type!>! typeArgs) {
+ MapType! mapType = args[0].Type.AsMap;
+ assert TypeParamArity == mapType.TypeParameters.Length;
+ IDictionary<TypeVariable!, Type!>! subst = new Dictionary<TypeVariable!, Type!> ();
+ for (int i = 0; i < TypeParamArity; ++i)
+ subst.Add(mapType.TypeParameters[i], typeArgs[i]);
+ return mapType.Result.Substitute(subst);
+ }
+
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)]
+ public override bool Equals(object that) {
+ if (Object.ReferenceEquals(this, that))
+ return true;
+ if (that is VCExprSelectOp)
+ return Arity == ((VCExprSelectOp)that).Arity &&
+ TypeParamArity == ((VCExprSelectOp)that).TypeParamArity;
+ return false;
+ }
+ [Pure]
+ public override int GetHashCode() {
+ return Arity * 1212481 + TypeParamArity * 298741;
+ }
+
+ internal VCExprSelectOp(int arity, int typeParamArity)
+ requires 0 <= arity && 0 <= typeParamArity;
+ {
+ this.MapArity = arity;
+ this.MapTypeParamArity = typeParamArity;
+ }
+ public override Result Accept<Result, Arg>
+ (VCExprNAry! expr, IVCExprOpVisitor<Result, Arg>! visitor, Arg arg) {
+ return visitor.VisitSelectOp(expr, arg);
+ }
+ }
+
+ public class VCExprStoreOp : VCExprOp {
+ private readonly int MapArity;
+ private readonly int MapTypeParamArity;
+ public override int Arity { get { return MapArity + 2; } }
+ // stores never need explicit type parameters, because also the
+ // rhs is a value argument
+ public override int TypeParamArity { get { return MapTypeParamArity; } }
+
+ public override Type! InferType(List<VCExpr!>! args, List<Type!>! typeArgs) {
+ return args[0].Type;
+ }
+
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)]
+ public override bool Equals(object that) {
+ if (Object.ReferenceEquals(this, that))
+ return true;
+ if (that is VCExprStoreOp)
+ return Arity == ((VCExprStoreOp)that).Arity;
+ return false;
+ }
+ [Pure]
+ public override int GetHashCode() {
+ return Arity * 91361821;
+ }
+
+ internal VCExprStoreOp(int arity, int typeParamArity)
+ requires 0 <= arity && 0 <= typeParamArity;
+ {
+ this.MapArity = arity;
+ this.MapTypeParamArity = typeParamArity;
+ }
+ public override Result Accept<Result, Arg>
+ (VCExprNAry! expr, IVCExprOpVisitor<Result, Arg>! visitor, Arg arg) {
+ return visitor.VisitStoreOp(expr, arg);
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////
+ // Bitvector operators
+
+ public class VCExprBvOp : VCExprOp {
+ public readonly int Bits;
+
+ public override int Arity { get { return 1; } }
+ public override int TypeParamArity { get { return 0; } }
+ public override Type! InferType(List<VCExpr!>! args, List<Type!>! typeArgs) {
+ return Type.GetBvType(Bits);
+ }
+
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)]
+ public override bool Equals(object that) {
+ if (Object.ReferenceEquals(this, that))
+ return true;
+ if (that is VCExprBvOp)
+ return this.Bits == ((VCExprBvOp)that).Bits;
+ return false;
+ }
+ [Pure]
+ public override int GetHashCode() {
+ return Bits * 81748912;
+ }
+
+ internal VCExprBvOp(int bits) {
+ this.Bits = bits;
+ }
+ public override Result Accept<Result, Arg>
+ (VCExprNAry! expr, IVCExprOpVisitor<Result, Arg>! visitor, Arg arg) {
+ return visitor.VisitBvOp(expr, arg);
+ }
+ }
+
+ public class VCExprBvExtractOp : VCExprOp {
+ public readonly int Start;
+ public readonly int End;
+
+ public override int Arity { get { return 1; } }
+ public override int TypeParamArity { get { return 0; } }
+ public override Type! InferType(List<VCExpr!>! args, List<Type!>! typeArgs) {
+ return Type.GetBvType(End - Start);
+ }
+
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)]
+ public override bool Equals(object that) {
+ if (Object.ReferenceEquals(this, that))
+ return true;
+ if (that is VCExprBvExtractOp) {
+ VCExprBvExtractOp! thatExtract = (VCExprBvExtractOp)that;
+ return this.Start == thatExtract.Start && this.End == thatExtract.End;
+ }
+ return false;
+ }
+ [Pure]
+ public override int GetHashCode() {
+ return Start * 81912 + End * 978132;
+ }
+
+ internal VCExprBvExtractOp(int start, int end) {
+ this.Start = start;
+ this.End = end;
+ }
+ public override Result Accept<Result, Arg>
+ (VCExprNAry! expr, IVCExprOpVisitor<Result, Arg>! visitor, Arg arg) {
+ return visitor.VisitBvExtractOp(expr, arg);
+ }
+ }
+
+ // singleton class
+ public class VCExprBvConcatOp : VCExprOp {
+ public override int Arity { get { return 2; } }
+ public override int TypeParamArity { get { return 0; } }
+ public override Type! InferType(List<VCExpr!>! args, List<Type!>! typeArgs) {
+ return Type.GetBvType(args[0].Type.BvBits + args[1].Type.BvBits);
+ }
+ internal VCExprBvConcatOp() {}
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////
+ // References to user-defined Boogie functions
+
+ public class VCExprBoogieFunctionOp : VCExprOp {
+ public readonly Function! Func;
+
+ public override int Arity { get { return Func.InParams.Length; } }
+ public override int TypeParamArity { get { return Func.TypeParameters.Length; } }
+ public override Type! InferType(List<VCExpr!>! args, List<Type!>! typeArgs) {
+ assert TypeParamArity == Func.TypeParameters.Length;
+ if (TypeParamArity == 0)
+ return ((!)Func.OutParams[0]).TypedIdent.Type;
+ IDictionary<TypeVariable!, Type!>! subst = new Dictionary<TypeVariable!, Type!> (TypeParamArity);
+ for (int i = 0; i < TypeParamArity; ++i)
+ subst.Add(Func.TypeParameters[i], typeArgs[i]);
+ return ((!)Func.OutParams[0]).TypedIdent.Type.Substitute(subst);
+ }
+
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)]
+ public override bool Equals(object that) {
+ if (Object.ReferenceEquals(this, that))
+ return true;
+ if (that is VCExprBoogieFunctionOp)
+ return this.Func.Equals(((VCExprBoogieFunctionOp)that).Func);
+ return false;
+ }
+ [Pure]
+ public override int GetHashCode() {
+ return Func.GetHashCode() + 18731;
+ }
+
+ // we require that the result type of the expression is specified, because we
+ // do not want to perform full type inference at this point
+ internal VCExprBoogieFunctionOp(Function! func) {
+ this.Func = func;
+ }
+ public override Result Accept<Result, Arg>
+ (VCExprNAry! expr, IVCExprOpVisitor<Result, Arg>! visitor, Arg arg) {
+ return visitor.VisitBoogieFunctionOp(expr, arg);
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////
+ // Binders (quantifiers and let-expressions). We introduce our own class for
+ // term variables, but use the Boogie-AST class for type variables
+
+ public class VCExprVar : VCExpr {
+ // the name of the variable. Note that the name is not used for comparison,
+ // i.e., there can be two distinct variables with the same name
+ public readonly string! Name;
+ private readonly Type! VarType;
+ public override Type! Type { get { return VarType; } }
+
+ internal VCExprVar(string! name, Type! type) {
+ this.Name = name;
+ this.VarType = type;
+ }
+ public override Result Accept<Result, Arg>(IVCExprVisitor<Result, Arg>! visitor, Arg arg) {
+ return visitor.Visit(this, arg);
+ }
+ }
+
+ public abstract class VCExprBinder : VCExpr {
+ public readonly VCExpr! Body;
+ public readonly List<TypeVariable!>! TypeParameters;
+ public readonly List<VCExprVar!>! BoundVars;
+
+ public override Type! Type { get { return Body.Type; } }
+
+ internal VCExprBinder(List<TypeVariable!>! typeParams,
+ List<VCExprVar!>! boundVars,
+ VCExpr! body)
+ requires boundVars.Count + typeParams.Count > 0; { // only nontrivial binders ...
+ this.TypeParameters = typeParams;
+ this.BoundVars = boundVars;
+ this.Body = body;
+ }
+ }
+
+ public class VCTrigger {
+ public readonly bool Pos;
+ public readonly List<VCExpr!>! Exprs;
+
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)]
+ public override bool Equals(object that) {
+ if (Object.ReferenceEquals(this, that))
+ return true;
+ if (that is VCTrigger) {
+ VCTrigger! thatTrigger = (VCTrigger)that;
+ return this.Pos == thatTrigger.Pos &&
+ HelperFuns.SameElements(this.Exprs, thatTrigger.Exprs);
+ }
+ return false;
+ }
+ [Pure]
+ public override int GetHashCode() {
+ return (Pos ? 913821 : 871334) +
+ HelperFuns.PolyHash(123, 7, this.Exprs);
+ }
+
+ public VCTrigger(bool pos, List<VCExpr!>! exprs) {
+ this.Pos = pos;
+ this.Exprs = exprs;
+ }
+ }
+
+ public class VCQuantifierInfos {
+ public readonly string qid;
+ public readonly int uniqueId;
+ public readonly bool bvZ3Native;
+ public QKeyValue attributes;
+
+ public VCQuantifierInfos(string qid, int uniqueId, bool bvZ3Native, QKeyValue attributes) {
+ this.qid = qid;
+ this.uniqueId = uniqueId;
+ this.bvZ3Native = bvZ3Native;
+ this.attributes = attributes;
+ }
+ }
+
+ public enum Quantifier { ALL, EX };
+
+ public class VCExprQuantifier : VCExprBinder {
+ public readonly Quantifier Quan;
+
+ public readonly List<VCTrigger!>! Triggers;
+ public readonly VCQuantifierInfos! Infos;
+
+ // Equality is /not/ modulo bound renaming at this point
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)]
+ public override bool Equals(object that) {
+ if (Object.ReferenceEquals(this, that))
+ return true;
+ if (that is VCExprQuantifier) {
+ VCExprQuantifier! thatQuan = (VCExprQuantifier)that;
+ return this.Quan == thatQuan.Quan &&
+ HelperFuns.SameElements(this.Triggers, thatQuan.Triggers) &&
+ HelperFuns.SameElements(this.TypeParameters, thatQuan.TypeParameters) &&
+ HelperFuns.SameElements(this.BoundVars, thatQuan.BoundVars) &&
+ this.Body.Equals(thatQuan.Body);
+ }
+ return false;
+ }
+ [Pure]
+ public override int GetHashCode() {
+ return Quan.GetHashCode() +
+ HelperFuns.PolyHash(973219, 7, TypeParameters) +
+ HelperFuns.PolyHash(998431, 9, BoundVars) +
+ HelperFuns.PolyHash(123, 11, Triggers);
+ }
+
+ internal VCExprQuantifier(Quantifier kind,
+ List<TypeVariable!>! typeParams,
+ List<VCExprVar!>! boundVars,
+ List<VCTrigger!>! triggers,
+ VCQuantifierInfos! infos,
+ VCExpr! body) {
+ base(typeParams, boundVars, body);
+ this.Quan = kind;
+ this.Triggers = triggers;
+ this.Infos = infos;
+ }
+ public override Result Accept<Result, Arg>(IVCExprVisitor<Result, Arg>! visitor, Arg arg) {
+ return visitor.Visit(this, arg);
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////
+ // Let-Bindings
+
+ public class VCExprLetBinding {
+ public readonly VCExprVar! V;
+ public readonly VCExpr! E;
+
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)]
+ public override bool Equals(object that) {
+ if (Object.ReferenceEquals(this, that))
+ return true;
+ if (that is VCExprLetBinding) {
+ VCExprLetBinding! thatB = (VCExprLetBinding)that;
+ return this.V.Equals(thatB.V) && this.E.Equals(thatB.E);
+ }
+ return false;
+ }
+ [Pure]
+ public override int GetHashCode() {
+ return V.GetHashCode() * 71261 + E.GetHashCode();
+ }
+
+ internal VCExprLetBinding(VCExprVar! v, VCExpr! e) {
+ this.V = v;
+ this.E = e;
+ assert v.Type.Equals(e.Type);
+ }
+ }
+
+ public class VCExprLet : VCExprBinder, IEnumerable<VCExprLetBinding!> {
+ private readonly List<VCExprLetBinding!>! Bindings;
+
+ public int Length { get { return Bindings.Count; } }
+ public VCExprLetBinding! this[int index] { get {
+ return Bindings[index];
+ } }
+
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)]
+ public override bool Equals(object that) {
+ if (Object.ReferenceEquals(this, that))
+ return true;
+ if (that is VCExprLet) {
+ VCExprLet! thatLet = (VCExprLet)that;
+ return this.Body.Equals(thatLet.Body) &&
+ HelperFuns.SameElements(this, (VCExprLet)that);
+ }
+ return false;
+ }
+ [Pure]
+ public override int GetHashCode() {
+ return HelperFuns.PolyHash(Body.GetHashCode(), 9, Bindings);
+ }
+
+ [Pure] [GlobalAccess(false)] [Escapes(true,false)]
+ public IEnumerator<VCExprLetBinding!>! GetEnumerator() {
+ return Bindings.GetEnumerator();
+ }
+ [Pure] [GlobalAccess(false)] [Escapes(true,false)]
+ IEnumerator! System.Collections.IEnumerable.GetEnumerator() {
+ return Bindings.GetEnumerator();
+ }
+
+ private static List<VCExprVar!>! toSeq(List<VCExprLetBinding!>! bindings) {
+ List<VCExprVar!>! res = new List<VCExprVar!> ();
+ foreach (VCExprLetBinding! b in bindings)
+ res.Add(b.V);
+ return res;
+ }
+
+ internal VCExprLet(List<VCExprLetBinding!>! bindings,
+ VCExpr! body) {
+ base(new List<TypeVariable!> (), toSeq(bindings), body);
+ this.Bindings = bindings;
+ }
+ public override Result Accept<Result, Arg>(IVCExprVisitor<Result, Arg>! visitor, Arg arg) {
+ return visitor.Visit(this, arg);
+ }
+ }
+}
diff --git a/Source/VCExpr/VCExprASTPrinter.ssc b/Source/VCExpr/VCExprASTPrinter.ssc
new file mode 100644
index 00000000..1c143516
--- /dev/null
+++ b/Source/VCExpr/VCExprASTPrinter.ssc
@@ -0,0 +1,240 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Text;
+using System.IO;
+using System.Collections;
+using System.Collections.Generic;
+using Microsoft.Contracts;
+using Microsoft.Basetypes;
+
+// A simple visitor for turning a VCExpr into a human-readable string
+// (S-expr syntax)
+
+namespace Microsoft.Boogie.VCExprAST
+{
+
+ public class VCExprPrinter : IVCExprVisitor<bool, TextWriter!> {
+ private VCExprOpPrinter OpPrinterVar = null;
+ private VCExprOpPrinter! OpPrinter { get {
+ if (OpPrinterVar == null)
+ OpPrinterVar = new VCExprOpPrinter(this);
+ return OpPrinterVar;
+ } }
+
+ public void Print(VCExpr! expr, TextWriter! wr) {
+ expr.Accept<bool, TextWriter!>(this, wr);
+ }
+
+ public bool Visit(VCExprLiteral! node, TextWriter! wr) {
+ if (node == VCExpressionGenerator.True)
+ wr.Write("true");
+ else if (node == VCExpressionGenerator.False)
+ wr.Write("false");
+ else if (node is VCExprIntLit) {
+ wr.Write(((VCExprIntLit)node).Val);
+ } else
+ assert false;
+ return true;
+ }
+ public bool Visit(VCExprNAry! node, TextWriter! wr) {
+ VCExprOp! op = node.Op;
+
+ if (op.Equals(VCExpressionGenerator.AndOp) ||
+ op.Equals(VCExpressionGenerator.OrOp)) {
+ // handle these operators without recursion
+
+ wr.Write("({0}",
+ op.Equals(VCExpressionGenerator.AndOp) ? "And" : "Or");
+ IEnumerator! enumerator = new VCExprNAryUniformOpEnumerator (node);
+ while (enumerator.MoveNext()) {
+ VCExprNAry naryExpr = enumerator.Current as VCExprNAry;
+ if (naryExpr == null || !naryExpr.Op.Equals(op)) {
+ wr.Write(" ");
+ Print((VCExpr!)enumerator.Current, wr);
+ }
+ }
+
+ wr.Write(")");
+
+ return true;
+ }
+
+ return node.Accept<bool, TextWriter!>(OpPrinter, wr);
+ }
+ public bool Visit(VCExprVar! node, TextWriter! wr) {
+ wr.Write(node.Name);
+ return true;
+ }
+ public bool Visit(VCExprQuantifier! node, TextWriter! wr) {
+ string! quan = node.Quan == Quantifier.ALL ? "Forall" : "Exists";
+
+ wr.Write("({0} ", quan);
+
+ if (node.TypeParameters.Count > 0) {
+ wr.Write("<");
+ string! sep = "";
+ foreach (TypeVariable! v in node.TypeParameters) {
+ wr.Write(sep);
+ sep = ", ";
+ wr.Write("{0}", v.Name);
+ }
+ wr.Write("> ");
+ }
+
+ if (node.BoundVars.Count > 0) {
+ string! sep = "";
+ foreach (VCExprVar! v in node.BoundVars) {
+ wr.Write(sep);
+ sep = ", ";
+ Print(v, wr);
+ }
+ wr.Write(" ");
+ }
+
+ wr.Write(":: ");
+
+ if (node.Triggers.Count > 0) {
+ wr.Write("{0} ", "{");
+ string! sep = "";
+ foreach (VCTrigger! t in node.Triggers) {
+ wr.Write(sep);
+ sep = ", ";
+ string! sep2 = "";
+ foreach (VCExpr! e in t.Exprs) {
+ wr.Write(sep2);
+ sep2 = "+";
+ Print(e, wr);
+ }
+ }
+ wr.Write(" {0} ", "}");
+ }
+
+ Print(node.Body, wr);
+ wr.Write(")");
+ return true;
+ }
+ public bool Visit(VCExprLet! node, TextWriter! wr) {
+ wr.Write("(Let ");
+
+ string! sep = "";
+ foreach (VCExprLetBinding! b in node) {
+ wr.Write(sep);
+ sep = ", ";
+ Print(b.V, wr);
+ wr.Write(" = ");
+ Print(b.E, wr);
+ }
+ wr.Write(" ");
+
+ Print(node.Body, wr);
+ wr.Write(")");
+ return true;
+ }
+ }
+
+ public class VCExprOpPrinter : IVCExprOpVisitor<bool, TextWriter!> {
+ private VCExprPrinter! ExprPrinter;
+
+ public VCExprOpPrinter(VCExprPrinter! exprPrinter) {
+ this.ExprPrinter = exprPrinter;
+ }
+
+ private bool PrintNAry(string! op, VCExprNAry! node, TextWriter! wr) {
+ wr.Write("({0}", op);
+ foreach (VCExpr! arg in node) {
+ wr.Write(" ");
+ ExprPrinter.Print(arg, wr);
+ }
+ wr.Write(")");
+ return true;
+ }
+
+ public bool VisitNotOp (VCExprNAry! node, TextWriter! wr) {
+ return PrintNAry("!", node, wr);
+ }
+ public bool VisitEqOp (VCExprNAry! node, TextWriter! wr) {
+ return PrintNAry("==", node, wr);
+ }
+ public bool VisitNeqOp (VCExprNAry! node, TextWriter! wr) {
+ return PrintNAry("!=", node, wr);
+ }
+ public bool VisitAndOp (VCExprNAry! node, TextWriter! wr) {
+ assert false;
+ }
+ public bool VisitOrOp (VCExprNAry! node, TextWriter! wr) {
+ assert false;
+ }
+ public bool VisitImpliesOp (VCExprNAry! node, TextWriter! wr) {
+ return PrintNAry("Implies", node, wr);
+ }
+ public bool VisitDistinctOp (VCExprNAry! node, TextWriter! wr) {
+ return PrintNAry("Distinct", node, wr);
+ }
+ public bool VisitLabelOp (VCExprNAry! node, TextWriter! wr) {
+ VCExprLabelOp! op = (VCExprLabelOp)node.Op;
+ return PrintNAry("Label " + op.label, node, wr);
+ }
+ public bool VisitSelectOp (VCExprNAry! node, TextWriter! wr) {
+ return PrintNAry("Select", node, wr);
+ }
+ public bool VisitStoreOp (VCExprNAry! node, TextWriter! wr) {
+ return PrintNAry("Store", node, wr);
+ }
+ public bool VisitBvOp (VCExprNAry! node, TextWriter! wr) {
+ return PrintNAry("Bv", node, wr);
+ }
+ public bool VisitBvExtractOp(VCExprNAry! node, TextWriter! wr) {
+ return PrintNAry("BvExtract", node, wr);
+ }
+ public bool VisitBvConcatOp (VCExprNAry! node, TextWriter! wr) {
+ return PrintNAry("BvConcat", node, wr);
+ }
+ public bool VisitAddOp (VCExprNAry! node, TextWriter! wr) {
+ if (CommandLineOptions.Clo.ReflectAdd) {
+ return PrintNAry("Reflect$Add", node, wr);
+ } else {
+ return PrintNAry("+", node, wr);
+ }
+ }
+ public bool VisitSubOp (VCExprNAry! node, TextWriter! wr) {
+ return PrintNAry("-", node, wr);
+ }
+ public bool VisitMulOp (VCExprNAry! node, TextWriter! wr) {
+ return PrintNAry("*", node, wr);
+ }
+ public bool VisitDivOp (VCExprNAry! node, TextWriter! wr) {
+ return PrintNAry("/", node, wr);
+ }
+ public bool VisitModOp (VCExprNAry! node, TextWriter! wr) {
+ return PrintNAry("%", node, wr);
+ }
+ public bool VisitLtOp (VCExprNAry! node, TextWriter! wr) {
+ return PrintNAry("<", node, wr);
+ }
+ public bool VisitLeOp (VCExprNAry! node, TextWriter! wr) {
+ return PrintNAry("<=", node, wr);
+ }
+ public bool VisitGtOp (VCExprNAry! node, TextWriter! wr) {
+ return PrintNAry(">", node, wr);
+ }
+ public bool VisitGeOp (VCExprNAry! node, TextWriter! wr) {
+ return PrintNAry(">=", node, wr);
+ }
+ public bool VisitSubtypeOp (VCExprNAry! node, TextWriter! wr) {
+ return PrintNAry("<:", node, wr);
+ }
+ public bool VisitSubtype3Op (VCExprNAry! node, TextWriter! wr) {
+ return PrintNAry("<::", node, wr);
+ }
+ public bool VisitBoogieFunctionOp (VCExprNAry! node, TextWriter! wr) {
+ VCExprBoogieFunctionOp! op = (VCExprBoogieFunctionOp)node.Op;
+ return PrintNAry(op.Func.Name, node, wr);
+ }
+ }
+
+
+} \ No newline at end of file
diff --git a/Source/VCExpr/VCExprASTVisitors.ssc b/Source/VCExpr/VCExprASTVisitors.ssc
new file mode 100644
index 00000000..cbb4ab8f
--- /dev/null
+++ b/Source/VCExpr/VCExprASTVisitors.ssc
@@ -0,0 +1,999 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Text;
+using System.IO;
+using System.Collections;
+using System.Collections.Generic;
+using Microsoft.Contracts;
+using Microsoft.Basetypes;
+
+// Some visitor skeletons for the VCExpression AST
+
+namespace Microsoft.Boogie.VCExprAST
+{
+ using Microsoft.Boogie;
+
+ public interface IVCExprVisitor<Result, Arg> {
+ Result Visit(VCExprLiteral! node, Arg arg);
+ Result Visit(VCExprNAry! node, Arg arg);
+ Result Visit(VCExprVar! node, Arg arg);
+ Result Visit(VCExprQuantifier! node, Arg arg);
+ Result Visit(VCExprLet! node, Arg arg);
+ }
+
+ public interface IVCExprOpVisitor<Result, Arg> {
+ Result VisitNotOp (VCExprNAry! node, Arg arg);
+ Result VisitEqOp (VCExprNAry! node, Arg arg);
+ Result VisitNeqOp (VCExprNAry! node, Arg arg);
+ Result VisitAndOp (VCExprNAry! node, Arg arg);
+ Result VisitOrOp (VCExprNAry! node, Arg arg);
+ Result VisitImpliesOp (VCExprNAry! node, Arg arg);
+ Result VisitDistinctOp (VCExprNAry! node, Arg arg);
+ Result VisitLabelOp (VCExprNAry! node, Arg arg);
+ Result VisitSelectOp (VCExprNAry! node, Arg arg);
+ Result VisitStoreOp (VCExprNAry! node, Arg arg);
+ Result VisitBvOp (VCExprNAry! node, Arg arg);
+ Result VisitBvExtractOp(VCExprNAry! node, Arg arg);
+ Result VisitBvConcatOp (VCExprNAry! node, Arg arg);
+ Result VisitAddOp (VCExprNAry! node, Arg arg);
+ Result VisitSubOp (VCExprNAry! node, Arg arg);
+ Result VisitMulOp (VCExprNAry! node, Arg arg);
+ Result VisitDivOp (VCExprNAry! node, Arg arg);
+ Result VisitModOp (VCExprNAry! node, Arg arg);
+ Result VisitLtOp (VCExprNAry! node, Arg arg);
+ Result VisitLeOp (VCExprNAry! node, Arg arg);
+ Result VisitGtOp (VCExprNAry! node, Arg arg);
+ Result VisitGeOp (VCExprNAry! node, Arg arg);
+ Result VisitSubtypeOp (VCExprNAry! node, Arg arg);
+ Result VisitSubtype3Op (VCExprNAry! node, Arg arg);
+ Result VisitBoogieFunctionOp (VCExprNAry! node, Arg arg);
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+ // Standard implementations that make it easier to create own visitors
+
+ // Simple traversal of VCExprs. The Visit implementations work
+ // recursively, apart from the implementation for VCExprNAry that
+ // uses a stack when applied to nested nodes with the same
+ // operator, e.g., (AND (AND (AND ...) ...) ...). This is necessary
+ // to avoid stack overflows
+
+ public abstract class TraversingVCExprVisitor<Result, Arg>
+ : IVCExprVisitor<Result, Arg> {
+ protected abstract Result StandardResult(VCExpr! node, Arg arg);
+
+ public Result Traverse(VCExpr! node, Arg arg) {
+ return node.Accept(this, arg);
+ }
+
+ public virtual Result Visit(VCExprLiteral! node, Arg arg) {
+ return StandardResult(node, arg);
+ }
+
+ public virtual Result Visit(VCExprNAry! node, Arg arg) {
+ Result res = StandardResult(node, arg);
+
+ if (node.TypeParamArity == 0) {
+ VCExprOp! op = node.Op;
+
+ IEnumerator enumerator = new VCExprNAryUniformOpEnumerator(node);
+ enumerator.MoveNext();
+
+ while (enumerator.MoveNext()) {
+ VCExpr! expr = (VCExpr!)enumerator.Current;
+ VCExprNAry naryExpr = expr as VCExprNAry;
+ if (naryExpr == null || !naryExpr.Op.Equals(op)) {
+ expr.Accept(this, arg);
+ } else {
+ StandardResult(expr, arg);
+ }
+ }
+ } else {
+ foreach (VCExpr! e in node)
+ e.Accept(this, arg);
+ }
+
+ return res;
+ }
+
+ public virtual Result Visit(VCExprVar! node, Arg arg) {
+ return StandardResult(node, arg);
+ }
+ public virtual Result Visit(VCExprQuantifier! node, Arg arg) {
+ Result res = StandardResult(node, arg);
+ foreach (VCTrigger! trigger in node.Triggers)
+ foreach (VCExpr! expr in trigger.Exprs)
+ expr.Accept(this, arg);
+ node.Body.Accept(this, arg);
+ return res;
+ }
+ public virtual Result Visit(VCExprLet! node, Arg arg) {
+ Result res = StandardResult(node, arg);
+ // visit the bound expressions first
+ foreach (VCExprLetBinding! binding in node)
+ binding.E.Accept(this, arg);
+ node.Body.Accept(this, arg);
+ return res;
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+ // Class to iterate over the nodes of a tree of VCExprNAry. This is
+ // used to avoid handling such VCExpr recursively, which can easily
+ // lead to stack overflows
+
+ public class VCExprNAryEnumerator : IEnumerator {
+
+ private readonly VCExprNAry! CompleteExpr;
+ private VCExpr CurrentExpr = null;
+ private readonly Stack<VCExpr!>! ExprTodo = new Stack<VCExpr!> ();
+
+ public VCExprNAryEnumerator(VCExprNAry! completeExpr) {
+ this.CompleteExpr = completeExpr;
+ Stack<VCExpr!>! exprTodo = new Stack<VCExpr!> ();
+ exprTodo.Push(completeExpr);
+ ExprTodo = exprTodo;
+ }
+
+ // Method using which a subclass can decide whether the
+ // subexpressions of an expression should be enumerated as well
+ // The default is to enumerate all nodes
+ protected virtual bool Descend(VCExprNAry! expr) {
+ return true;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ public bool MoveNext() {
+ if (ExprTodo.Count == 0)
+ return false;
+
+ CurrentExpr = ExprTodo.Pop();
+ VCExprNAry currentNAry = CurrentExpr as VCExprNAry;
+ if (currentNAry != null && Descend(currentNAry)) {
+ for (int i = currentNAry.Arity - 1; i >= 0; --i)
+ ExprTodo.Push(currentNAry[i]);
+ }
+
+ return true;
+ }
+
+ public object Current {
+ [Pure][Reads(ReadsAttribute.Reads.Owned)]
+ get {
+ return (!)CurrentExpr;
+ } }
+
+ public void Reset() {
+ ExprTodo.Clear();
+ CurrentExpr = null;
+ ExprTodo.Push(CompleteExpr);
+ }
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ public class VCExprNAryUniformOpEnumerator : VCExprNAryEnumerator {
+ private readonly VCExprOp! Op;
+ public VCExprNAryUniformOpEnumerator(VCExprNAry! completeExpr) {
+ base(completeExpr);
+ this.Op = completeExpr.Op;
+ }
+ protected override bool Descend(VCExprNAry! expr) {
+ return expr.Op.Equals(Op) &&
+ // we never skip nodes with type parameters
+ // (those are too interesting ...)
+ expr.TypeParamArity == 0;
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+ // Visitor that knows about the variables bound at each location in a VCExpr
+
+ public abstract class BoundVarTraversingVCExprVisitor<Result, Arg>
+ : TraversingVCExprVisitor<Result, Arg> {
+ // Maps with all variables bound above a certain location in the VCExpression.
+ // The value of the map tells how often a particular symbol was bound
+ private readonly IDictionary<VCExprVar!, int>! BoundTermVarsDict =
+ new Dictionary<VCExprVar!, int> ();
+ private readonly IDictionary<TypeVariable!, int>! BoundTypeVarsDict =
+ new Dictionary<TypeVariable!, int> ();
+
+ protected ICollection<VCExprVar!>! BoundTermVars { get {
+ return BoundTermVarsDict.Keys;
+ } }
+ protected ICollection<TypeVariable!>! BoundTypeVars { get {
+ return BoundTypeVarsDict.Keys;
+ } }
+
+ private void AddBoundVar<T>(IDictionary<T!, int>! dict, T! sym) {
+ int n;
+ if (dict.TryGetValue(sym, out n))
+ dict[sym] = n + 1;
+ else
+ dict[sym] = 1;
+ }
+
+ private void RemoveBoundVar<T>(IDictionary<T!, int>! dict, T! sym) {
+ int n;
+ bool b = dict.TryGetValue(sym, out n);
+ assert b && n > 0;
+ if (n == 1)
+ dict.Remove(sym);
+ else
+ dict[sym] = n - 1;
+ }
+
+ public override Result Visit(VCExprQuantifier! node, Arg arg) {
+ // we temporarily add bound (term and type) variables to the
+ // corresponding lists
+ foreach (VCExprVar! v in node.BoundVars)
+ AddBoundVar<VCExprVar>(BoundTermVarsDict, v);
+ foreach (TypeVariable! v in node.TypeParameters)
+ AddBoundVar<TypeVariable>(BoundTypeVarsDict, v);
+
+ Result res;
+ try {
+ res = VisitAfterBinding(node, arg);
+ } finally {
+ foreach (VCExprVar! v in node.BoundVars)
+ RemoveBoundVar<VCExprVar>(BoundTermVarsDict, v);
+ foreach (TypeVariable! v in node.TypeParameters)
+ RemoveBoundVar<TypeVariable>(BoundTypeVarsDict, v);
+ }
+ return res;
+ }
+ public override Result Visit(VCExprLet! node, Arg arg) {
+ // we temporarily add bound term variables to the
+ // corresponding lists
+ foreach (VCExprVar! v in node.BoundVars)
+ AddBoundVar<VCExprVar>(BoundTermVarsDict, v);
+
+ Result res;
+ try {
+ res = VisitAfterBinding(node, arg);
+ } finally {
+ foreach (VCExprVar! v in node.BoundVars)
+ RemoveBoundVar<VCExprVar>(BoundTermVarsDict, v);
+ }
+ return res;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // The possibility is provided to look at a (quantifier or let) node
+ // after its bound variables have been registered
+ // (when overriding the normal visit-methods, the node will be visited
+ // before the binding happens)
+
+ protected virtual Result VisitAfterBinding(VCExprQuantifier! node, Arg arg) {
+ return base.Visit(node, arg);
+ }
+
+ protected virtual Result VisitAfterBinding(VCExprLet! node, Arg arg) {
+ return base.Visit(node, arg);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // General visitor for recursively collecting information in a VCExpr.
+ // As the visitor is not used anywhere for the time being, it maybe should
+ // be removed
+
+ public abstract class CollectingVCExprVisitor<Result, Arg>
+ : IVCExprVisitor<Result, Arg> {
+ protected abstract Result CombineResults(List<Result>! results, Arg arg);
+
+ public Result Collect(VCExpr! node, Arg arg) {
+ return node.Accept(this, arg);
+ }
+
+ public virtual Result Visit(VCExprLiteral! node, Arg arg) {
+ return CombineResults(new List<Result> (), arg);
+ }
+ public virtual Result Visit(VCExprNAry! node, Arg arg) {
+ List<Result>! results = new List<Result> ();
+ foreach (VCExpr! subnode in node)
+ results.Add(subnode.Accept(this, arg));
+ return CombineResults(results, arg);
+ }
+ public virtual Result Visit(VCExprVar! node, Arg arg) {
+ return CombineResults(new List<Result> (), arg);
+ }
+ public virtual Result Visit(VCExprQuantifier! node, Arg arg) {
+ List<Result>! result = new List<Result> ();
+ result.Add(node.Body.Accept(this, arg));
+ foreach (VCTrigger! trigger in node.Triggers)
+ foreach (VCExpr! expr in trigger.Exprs)
+ result.Add(expr.Accept(this, arg));
+ return CombineResults(result, arg);
+ }
+ public virtual Result Visit(VCExprLet! node, Arg arg) {
+ List<Result>! results = new List<Result> ();
+ // visit the bound expressions first
+ foreach (VCExprLetBinding! binding in node)
+ results.Add(binding.E.Accept(this, arg));
+ results.Add(node.Body.Accept(this, arg));
+ return CombineResults(results, arg);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ public class SizeComputingVisitor : TraversingVCExprVisitor<bool, bool> {
+
+ private int Size = 0;
+
+ public static int ComputeSize(VCExpr! expr) {
+ SizeComputingVisitor! visitor = new SizeComputingVisitor();
+ visitor.Traverse(expr, true);
+ return visitor.Size;
+ }
+
+ protected override bool StandardResult(VCExpr! node, bool arg) {
+ Size = Size + 1;
+ return true;
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ // Collect all free term and type variables in a VCExpr. Type variables
+ // can occur free either in the types of bound variables, or in the type
+ // parameters of VCExprNAry.
+
+ // the result and argument (of type bool) are not used currently
+ public class FreeVariableCollector : BoundVarTraversingVCExprVisitor<bool, bool> {
+ public readonly Dictionary<VCExprVar!,object>! FreeTermVars = new Dictionary<VCExprVar!,object> ();
+ public readonly List<TypeVariable!>! FreeTypeVars = new List<TypeVariable!> ();
+
+ // not used
+ protected override bool StandardResult(VCExpr! node, bool arg) {
+ return true;
+ }
+
+ public static Dictionary<VCExprVar!,object>! FreeTermVariables(VCExpr! node) {
+ FreeVariableCollector collector = new FreeVariableCollector ();
+ collector.Traverse(node, true);
+ return collector.FreeTermVars;
+ }
+
+ public static List<TypeVariable!>! FreeTypeVariables(VCExpr! node) {
+ FreeVariableCollector collector = new FreeVariableCollector ();
+ collector.Traverse(node, true);
+ return collector.FreeTypeVars;
+ }
+
+ public void Reset() {
+ FreeTermVars.Clear();
+ FreeTypeVars.Clear();
+ }
+
+ public void Collect(VCExpr! node) {
+ Traverse(node, true);
+ }
+
+ public void Collect(Type! type) {
+ AddTypeVariables(type.FreeVariables.ToList());
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ private void CollectTypeVariables(IEnumerable<VCExprVar!>! boundVars) {
+ foreach (VCExprVar! var in boundVars)
+ Collect(var.Type);
+ }
+
+ private void AddTypeVariables(IEnumerable<TypeVariable!>! typeVars) {
+ foreach (TypeVariable! tvar in typeVars)
+ if (!BoundTypeVars.Contains(tvar) && !FreeTypeVars.Contains(tvar))
+ FreeTypeVars.Add(tvar);
+ }
+
+ public override bool Visit(VCExprVar! node, bool arg) {
+ if (!BoundTermVars.Contains(node) && !FreeTermVars.ContainsKey(node)) {
+ FreeTermVars.Add(node, null);
+ Collect(node.Type);
+ }
+ return true;
+ }
+
+ public override bool Visit(VCExprNAry! node, bool arg) {
+ foreach (Type! t in node.TypeArguments)
+ Collect(t);
+ return base.Visit(node, arg);
+ }
+
+ protected override bool VisitAfterBinding(VCExprQuantifier! node, bool arg) {
+ CollectTypeVariables(node.BoundVars);
+ return base.VisitAfterBinding(node, arg);
+ }
+
+ protected override bool VisitAfterBinding(VCExprLet! node, bool arg) {
+ CollectTypeVariables(node.BoundVars);
+ return base.VisitAfterBinding(node, arg);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Framework for mutating VCExprs
+
+ // The Visit implementations in the following visitor work
+ // recursively, apart from the implementation for VCExprNAry that
+ // uses its own stack when applied to nested nodes with the same
+ // operator, e.g., (AND (AND (AND ...) ...) ...). This is necessary
+ // to avoid stack overflows (like in TraversingVCExprVisitor)
+
+ public abstract class MutatingVCExprVisitor<Arg>
+ : IVCExprVisitor<VCExpr!, Arg> {
+ protected readonly VCExpressionGenerator! Gen;
+
+ public MutatingVCExprVisitor(VCExpressionGenerator! gen) {
+ this.Gen = gen;
+ }
+
+ public VCExpr! Mutate(VCExpr! expr, Arg arg) {
+ return expr.Accept(this, arg);
+ }
+
+ public List<VCExpr!>! MutateSeq(IEnumerable<VCExpr!>! exprs, Arg arg) {
+ List<VCExpr!>! res = new List<VCExpr!> ();
+ foreach (VCExpr! expr in exprs)
+ res.Add(expr.Accept(this, arg));
+ return res;
+ }
+
+ private List<VCExpr!>! MutateList(List<VCExpr!>! exprs, Arg arg) {
+ bool changed = false;
+ List<VCExpr!>! res = new List<VCExpr!> ();
+ foreach (VCExpr! expr in exprs) {
+ VCExpr! newExpr = expr.Accept(this, arg);
+ if (!Object.ReferenceEquals(expr, newExpr))
+ changed = true;
+ res.Add(newExpr);
+ }
+ if (!changed)
+ return exprs;
+ return res;
+ }
+
+ public virtual VCExpr! Visit(VCExprLiteral! node, Arg arg) {
+ return node;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ // Special element used to mark the positions in the todo-stack where
+ // results have to be popped from the result-stack.
+ private static readonly VCExpr! CombineResultsMarker = new VCExprLiteral (Type.Bool);
+
+ // The todo-stack contains records of the shape
+ //
+ // arg0
+ // arg1
+ // arg2
+ // ...
+ // CombineResultsMarker
+ // f(arg0, arg1, arg2, ...) (the original expression)
+
+ private readonly Stack<VCExpr!>! NAryExprTodoStack = new Stack<VCExpr!> ();
+ private readonly Stack<VCExpr!>! NAryExprResultStack = new Stack<VCExpr!> ();
+
+ private void PushTodo(VCExprNAry! exprTodo) {
+ NAryExprTodoStack.Push(exprTodo);
+ NAryExprTodoStack.Push(CombineResultsMarker);
+ for (int i = exprTodo.Arity - 1; i >= 0; --i)
+ NAryExprTodoStack.Push(exprTodo[i]);
+ }
+
+ public virtual VCExpr! Visit(VCExprNAry! node, Arg arg) {
+ VCExprOp! op = node.Op;
+ int initialStackSize = NAryExprTodoStack.Count;
+ int initialResultStackSize = NAryExprResultStack.Count;
+
+ PushTodo(node);
+
+ while (NAryExprTodoStack.Count > initialStackSize) {
+ VCExpr! subExpr = NAryExprTodoStack.Pop();
+
+ if (Object.ReferenceEquals(subExpr, CombineResultsMarker)) {
+ //
+ // assemble a result
+ VCExprNAry! originalExpr = (VCExprNAry)NAryExprTodoStack.Pop();
+ bool changed = false;
+ List<VCExpr!>! newSubExprs = new List<VCExpr!> ();
+
+ for (int i = op.Arity - 1; i >= 0; --i) {
+ VCExpr! nextSubExpr = NAryExprResultStack.Pop();
+ if (!Object.ReferenceEquals(nextSubExpr, originalExpr[i]))
+ changed = true;
+ newSubExprs.Insert(0, nextSubExpr);
+ }
+
+ NAryExprResultStack.Push(UpdateModifiedNode(originalExpr, newSubExprs, changed, arg));
+ //
+ } else {
+ //
+ VCExprNAry narySubExpr = subExpr as VCExprNAry;
+ if (narySubExpr != null && narySubExpr.Op.Equals(op) &&
+ // as in VCExprNAryUniformOpEnumerator, all expressions with
+ // type parameters are allowed to be inspected more closely
+ narySubExpr.TypeParamArity == 0) {
+ PushTodo(narySubExpr);
+ } else {
+ NAryExprResultStack.Push(subExpr.Accept(this, arg));
+ }
+ //
+ }
+ }
+
+ assert NAryExprTodoStack.Count == initialStackSize &&
+ NAryExprResultStack.Count == initialResultStackSize + 1;
+ return NAryExprResultStack.Pop();
+ }
+
+ protected virtual VCExpr! UpdateModifiedNode(VCExprNAry! originalNode,
+ List<VCExpr!>! newSubExprs,
+ // has any of the subexpressions changed?
+ bool changed,
+ Arg arg) {
+ if (changed)
+ return Gen.Function(originalNode.Op,
+ newSubExprs, originalNode.TypeArguments);
+ else
+ return originalNode;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ public virtual VCExpr! Visit(VCExprVar! node, Arg arg) {
+ return node;
+ }
+
+ protected List<VCTrigger!>! MutateTriggers(List<VCTrigger!>! triggers, Arg arg) {
+ List<VCTrigger!>! newTriggers = new List<VCTrigger!> ();
+ bool changed = false;
+ foreach (VCTrigger! trigger in triggers) {
+ List<VCExpr!>! exprs = trigger.Exprs;
+ List<VCExpr!>! newExprs = MutateList(exprs, arg);
+ if (Object.ReferenceEquals(exprs, newExprs)) {
+ newTriggers.Add(trigger);
+ } else {
+ newTriggers.Add(Gen.Trigger(trigger.Pos, newExprs));
+ changed = true;
+ }
+ }
+ if (!changed)
+ return triggers;
+ return newTriggers;
+ }
+
+ public virtual VCExpr! Visit(VCExprQuantifier! node, Arg arg) {
+ bool changed = false;
+
+ VCExpr! body = node.Body;
+ VCExpr! newbody = body.Accept(this, arg);
+ if (!Object.ReferenceEquals(body, newbody))
+ changed = true;
+
+ // visit the trigger expressions as well
+ List<VCTrigger!>! triggers = node.Triggers;
+ List<VCTrigger!>! newTriggers = MutateTriggers(triggers, arg);
+ if (!Object.ReferenceEquals(triggers, newTriggers))
+ changed = true;
+
+ if (!changed)
+ return node;
+ return Gen.Quantify(node.Quan, node.TypeParameters, node.BoundVars,
+ newTriggers, node.Infos, newbody);
+ }
+
+ public virtual VCExpr! Visit(VCExprLet! node, Arg arg) {
+ bool changed = false;
+
+ VCExpr! body = node.Body;
+ VCExpr! newbody = body.Accept(this, arg);
+ if (!Object.ReferenceEquals(body, newbody))
+ changed = true;
+
+ List<VCExprLetBinding!>! newbindings = new List<VCExprLetBinding!> ();
+ for (int i = 0; i < node.Length; ++i) {
+ VCExprLetBinding! binding = node[i];
+ VCExpr! e = binding.E;
+ VCExpr! newE = e.Accept(this, arg);
+ if (Object.ReferenceEquals(e, newE)) {
+ newbindings.Add(binding);
+ } else {
+ changed = true;
+ newbindings.Add(Gen.LetBinding(binding.V, newE));
+ }
+ }
+
+ if (!changed)
+ return node;
+ return Gen.Let(newbindings, newbody);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Substitutions and a visitor for applying substitutions. A substitution can
+ // substitute both type variables and term variables
+
+ public class VCExprSubstitution {
+ private readonly List<IDictionary<VCExprVar!, VCExpr!>!>! TermSubsts;
+ private readonly List<IDictionary<TypeVariable!, Type!>!>! TypeSubsts;
+
+ public VCExprSubstitution(IDictionary<VCExprVar!, VCExpr!>! termSubst,
+ IDictionary<TypeVariable!, Type!>! typeSubst) {
+ List<IDictionary<VCExprVar!, VCExpr!>!>! termSubsts =
+ new List<IDictionary<VCExprVar!, VCExpr!>!> ();
+ termSubsts.Add(termSubst);
+ List<IDictionary<TypeVariable!, Type!>!>! typeSubsts =
+ new List<IDictionary<TypeVariable!, Type!>!> ();
+ typeSubsts.Add(typeSubst);
+ this.TermSubsts = termSubsts;
+ this.TypeSubsts = typeSubsts;
+ }
+
+ public VCExprSubstitution() {
+ this(new Dictionary<VCExprVar!, VCExpr!> (), new Dictionary<TypeVariable!, Type!> ());
+ }
+
+ public void PushScope() {
+ TermSubsts.Add(new Dictionary<VCExprVar!, VCExpr!> ());
+ TypeSubsts.Add(new Dictionary<TypeVariable!, Type!> ());
+ }
+
+ public void PopScope() {
+ TermSubsts.RemoveAt(TermSubsts.Count - 1);
+ TypeSubsts.RemoveAt(TypeSubsts.Count - 1);
+ }
+
+ public VCExpr this[VCExprVar! var] {
+ get {
+ VCExpr res;
+ for (int i = TermSubsts.Count - 1; i >= 0; --i) {
+ if (TermSubsts[i].TryGetValue(var, out res))
+ return res;
+ }
+ return null;
+ }
+ set {
+ TermSubsts[TermSubsts.Count - 1][var] = (!)value;
+ }
+ }
+
+ public Type this[TypeVariable! var] {
+ get {
+ Type res;
+ for (int i = TypeSubsts.Count - 1; i >= 0; --i) {
+ if (TypeSubsts[i].TryGetValue(var, out res))
+ return res;
+ }
+ return null;
+ }
+ set {
+ TypeSubsts[TypeSubsts.Count - 1][var] = (!)value;
+ }
+ }
+
+ public bool ContainsKey(VCExprVar! var) {
+ return this[var] != null;
+ }
+
+ public bool ContainsKey(TypeVariable! var) {
+ return this[var] != null;
+ }
+
+ public bool TermSubstIsEmpty { get {
+ return forall{IDictionary<VCExprVar!, VCExpr!>! dict in TermSubsts;
+ dict.Count == 0};
+ } }
+
+ public bool TypeSubstIsEmpty { get {
+ return forall{IDictionary<TypeVariable!, Type!>! dict in TypeSubsts;
+ dict.Count == 0};
+ } }
+
+ public IDictionary<TypeVariable!, Type!>! ToTypeSubst { get {
+ IDictionary<TypeVariable!, Type!>! res = new Dictionary<TypeVariable!, Type!> ();
+ foreach (IDictionary<TypeVariable!, Type!>! dict in TypeSubsts)
+ foreach (KeyValuePair<TypeVariable!, Type!> pair in dict)
+ // later ones overwrite earlier ones
+ res[pair.Key] = pair.Value;
+ return res;
+ } }
+
+ // the variables that are not mapped to themselves
+ public IEnumerable<VCExprVar!>! TermDomain { get {
+ Dictionary<VCExprVar!, bool>! domain = new Dictionary<VCExprVar!, bool> ();
+ foreach (IDictionary<VCExprVar!, VCExpr!>! dict in TermSubsts)
+ foreach (VCExprVar! var in dict.Keys)
+ if (!var.Equals(this[var]))
+ domain.Add(var, true);
+ return domain.Keys;
+ } }
+
+ // the variables that are not mapped to themselves
+ public IEnumerable<TypeVariable!>! TypeDomain { get {
+ Dictionary<TypeVariable!, bool>! domain = new Dictionary<TypeVariable!, bool> ();
+ foreach (IDictionary<TypeVariable!, Type!>! dict in TypeSubsts)
+ foreach (TypeVariable! var in dict.Keys)
+ if (!var.Equals(this[var]))
+ domain.Add(var, true);
+ return domain.Keys;
+ } }
+
+ public FreeVariableCollector! Codomains { get {
+ FreeVariableCollector! coll = new FreeVariableCollector ();
+ foreach (VCExprVar! var in TermDomain)
+ coll.Collect((!)this[var]);
+ foreach (TypeVariable! var in TypeDomain)
+ coll.Collect((!)this[var]);
+ return coll;
+ } }
+
+ public VCExprSubstitution! Clone() {
+ VCExprSubstitution! res = new VCExprSubstitution ();
+ foreach (IDictionary<VCExprVar!, VCExpr!>! dict in TermSubsts)
+ res.TermSubsts.Add(HelperFuns.Clone(dict));
+ foreach (IDictionary<TypeVariable!, Type!>! dict in TypeSubsts)
+ res.TypeSubsts.Add(HelperFuns.Clone(dict));
+ return res;
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////
+
+ public class SubstitutingVCExprVisitor
+ : MutatingVCExprVisitor<VCExprSubstitution!> {
+ public SubstitutingVCExprVisitor(VCExpressionGenerator! gen) {
+ base(gen);
+ }
+
+ // when descending across a binder, we have to check that no collisions
+ // or variable capture can occur. if this might happen, we replace the
+ // term and type variables bound by the binder with fresh variables
+ private bool CollisionPossible(IEnumerable<TypeVariable!>! typeParams,
+ IEnumerable<VCExprVar!>! boundVars,
+ VCExprSubstitution! substitution) {
+ // variables can be shadowed by a binder
+ if (exists{TypeVariable! var in typeParams; substitution.ContainsKey(var)} ||
+ exists{VCExprVar! var in boundVars; substitution.ContainsKey(var)})
+ return true;
+ // compute the codomain of the substitution
+ FreeVariableCollector! coll = substitution.Codomains;
+ // variables could be captured when applying the substitution
+ return exists{TypeVariable! var in typeParams; coll.FreeTypeVars.Contains(var)} ||
+ exists{VCExprVar! var in boundVars; coll.FreeTermVars.ContainsKey(var)};
+ }
+
+ // can be overwritten if names of bound variables are to be changed
+ protected virtual string! ChooseNewVariableName(string! oldName) {
+ return oldName;
+ }
+
+ // handle type parameters in VCExprNAry
+ protected override VCExpr! UpdateModifiedNode(VCExprNAry! originalNode,
+ List<VCExpr!>! newSubExprs,
+ bool changed,
+ VCExprSubstitution! substitution) {
+ List<Type!>! typeParams = new List<Type!> ();
+ foreach (Type! t in originalNode.TypeArguments) {
+ Type! newType = t.Substitute(substitution.ToTypeSubst);
+ if (!ReferenceEquals(t, newType))
+ changed = true;
+ typeParams.Add(newType);
+ }
+ if (changed)
+ return Gen.Function(originalNode.Op, newSubExprs, typeParams);
+ else
+ return originalNode;
+ }
+
+ public override VCExpr! Visit(VCExprQuantifier! node,
+ VCExprSubstitution! substitution) {
+ // the default is to refresh bound variables only if necessary
+ // because of collisions
+ return Visit(node, substitution, false);
+ }
+
+ public VCExpr! Visit(VCExprQuantifier! node,
+ VCExprSubstitution! substitution,
+ bool refreshBoundVariables) {
+ substitution.PushScope(); try {
+
+ List<TypeVariable!>! typeParams = node.TypeParameters;
+ bool refreshAllVariables = refreshBoundVariables ||
+ CollisionPossible(node.TypeParameters, node.BoundVars, substitution);
+ if (refreshAllVariables) {
+ // we introduce fresh type variables to ensure that none gets captured
+ typeParams = new List<TypeVariable!> ();
+ foreach (TypeVariable! var in node.TypeParameters) {
+ TypeVariable! freshVar =
+ new TypeVariable (Token.NoToken, ChooseNewVariableName(var.Name));
+ typeParams.Add(freshVar);
+ substitution[var] = freshVar;
+ // this might overwrite other elements of the substitution, deliberately
+ }
+ }
+
+ List<VCExprVar!>! boundVars = node.BoundVars;
+ if (refreshAllVariables || !substitution.TypeSubstIsEmpty) {
+ // collisions are possible, or we also substitute type variables. in this case
+ // the bound term variables have to be replaced with fresh variables with the
+ // right types
+ boundVars = new List<VCExprVar!> ();
+ IDictionary<TypeVariable!, Type!>! typeSubst = substitution.ToTypeSubst;
+ foreach (VCExprVar! var in node.BoundVars) {
+ VCExprVar! freshVar =
+ Gen.Variable(ChooseNewVariableName(var.Name),
+ var.Type.Substitute(typeSubst));
+ boundVars.Add(freshVar);
+ substitution[var] = freshVar;
+ // this might overwrite other elements of the substitution, deliberately
+ }
+ }
+
+ List<VCTrigger!>! newTriggers = new List<VCTrigger!> ();
+ foreach (VCTrigger! trigger in node.Triggers)
+ newTriggers.Add(Gen.Trigger(trigger.Pos, MutateSeq(trigger.Exprs, substitution)));
+
+ VCExpr! newBody = Mutate(node.Body, substitution);
+
+ return Gen.Quantify(node.Quan, typeParams, boundVars,
+ newTriggers, node.Infos, newBody);
+
+ } finally {
+ substitution.PopScope();
+ }
+ }
+
+ public override VCExpr! Visit(VCExprVar! node,
+ VCExprSubstitution! substitution) {
+ VCExpr res = substitution[node];
+ if (res != null)
+ return res;
+ return node;
+ }
+
+ public override VCExpr! Visit(VCExprLet! node,
+ VCExprSubstitution! substitution) {
+ // the default is to refresh bound variables only if necessary
+ // because of collisions
+ return Visit(node, substitution, false);
+ }
+
+ public VCExpr! Visit(VCExprLet! node,
+ VCExprSubstitution! substitution,
+ bool refreshBoundVariables) {
+ // let-expressions do not have type parameters (fortunately ...)
+ substitution.PushScope (); try {
+
+ bool refreshAllVariables =
+ refreshBoundVariables ||
+ !substitution.TypeSubstIsEmpty ||
+ CollisionPossible(new List<TypeVariable!> (), node.BoundVars, substitution);
+
+ List<VCExprVar!>! newBoundVars = node.BoundVars;
+ if (refreshAllVariables) {
+ // collisions are possible, or we also substitute type variables. in this case
+ // the bound term variables have to be replaced with fresh variables with the
+ // right types
+ newBoundVars = new List<VCExprVar!> ();
+ IDictionary<TypeVariable!, Type!>! typeSubst = substitution.ToTypeSubst;
+ foreach (VCExprVar! var in node.BoundVars) {
+ VCExprVar! freshVar =
+ Gen.Variable(ChooseNewVariableName(var.Name),
+ var.Type.Substitute(typeSubst));
+ newBoundVars.Add(freshVar);
+ substitution[var] = freshVar;
+ // this might overwrite other elements of the substitution, deliberately
+ }
+ }
+
+ List<VCExprLetBinding!>! newbindings = new List<VCExprLetBinding!> ();
+ for (int i = 0; i < node.Length; ++i) {
+ VCExprLetBinding! binding = node[i];
+ newbindings.Add(Gen.LetBinding(newBoundVars[i], Mutate(binding.E, substitution)));
+ }
+
+ VCExpr! newBody = Mutate(node.Body, substitution);
+ return Gen.Let(newbindings, newBody);
+
+ } finally {
+ substitution.PopScope();
+ }
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ public abstract class StandardVCExprOpVisitor<Result, Arg>
+ : IVCExprOpVisitor<Result, Arg> {
+ protected abstract Result StandardResult(VCExprNAry! node, Arg arg);
+
+ public virtual Result VisitNotOp (VCExprNAry! node, Arg arg) {
+ return StandardResult(node, arg);
+ }
+ public virtual Result VisitEqOp (VCExprNAry! node, Arg arg) {
+ return StandardResult(node, arg);
+ }
+ public virtual Result VisitNeqOp (VCExprNAry! node, Arg arg) {
+ return StandardResult(node, arg);
+ }
+ public virtual Result VisitAndOp (VCExprNAry! node, Arg arg) {
+ return StandardResult(node, arg);
+ }
+ public virtual Result VisitOrOp (VCExprNAry! node, Arg arg) {
+ return StandardResult(node, arg);
+ }
+ public virtual Result VisitImpliesOp (VCExprNAry! node, Arg arg) {
+ return StandardResult(node, arg);
+ }
+ public virtual Result VisitDistinctOp (VCExprNAry! node, Arg arg) {
+ return StandardResult(node, arg);
+ }
+ public virtual Result VisitLabelOp (VCExprNAry! node, Arg arg) {
+ return StandardResult(node, arg);
+ }
+ public virtual Result VisitSelectOp (VCExprNAry! node, Arg arg) {
+ return StandardResult(node, arg);
+ }
+ public virtual Result VisitStoreOp (VCExprNAry! node, Arg arg) {
+ return StandardResult(node, arg);
+ }
+ public virtual Result VisitBvOp (VCExprNAry! node, Arg arg) {
+ return StandardResult(node, arg);
+ }
+ public virtual Result VisitBvExtractOp(VCExprNAry! node, Arg arg) {
+ return StandardResult(node, arg);
+ }
+ public virtual Result VisitBvConcatOp (VCExprNAry! node, Arg arg) {
+ return StandardResult(node, arg);
+ }
+ public virtual Result VisitAddOp (VCExprNAry! node, Arg arg) {
+ return StandardResult(node, arg);
+ }
+ public virtual Result VisitSubOp (VCExprNAry! node, Arg arg) {
+ return StandardResult(node, arg);
+ }
+ public virtual Result VisitMulOp (VCExprNAry! node, Arg arg) {
+ return StandardResult(node, arg);
+ }
+ public virtual Result VisitDivOp (VCExprNAry! node, Arg arg) {
+ return StandardResult(node, arg);
+ }
+ public virtual Result VisitModOp (VCExprNAry! node, Arg arg) {
+ return StandardResult(node, arg);
+ }
+ public virtual Result VisitLtOp (VCExprNAry! node, Arg arg) {
+ return StandardResult(node, arg);
+ }
+ public virtual Result VisitLeOp (VCExprNAry! node, Arg arg) {
+ return StandardResult(node, arg);
+ }
+ public virtual Result VisitGtOp (VCExprNAry! node, Arg arg) {
+ return StandardResult(node, arg);
+ }
+ public virtual Result VisitGeOp (VCExprNAry! node, Arg arg) {
+ return StandardResult(node, arg);
+ }
+ public virtual Result VisitSubtypeOp (VCExprNAry! node, Arg arg) {
+ return StandardResult(node, arg);
+ }
+ public virtual Result VisitSubtype3Op (VCExprNAry! node, Arg arg) {
+ return StandardResult(node, arg);
+ }
+ public virtual Result VisitBoogieFunctionOp (VCExprNAry! node, Arg arg) {
+ return StandardResult(node, arg);
+ }
+ }
+
+}
+
+
diff --git a/Source/VCGeneration/Check.ssc b/Source/VCGeneration/Check.ssc
new file mode 100644
index 00000000..f0d6c558
--- /dev/null
+++ b/Source/VCGeneration/Check.ssc
@@ -0,0 +1,375 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Threading;
+using System.IO;
+using System.Diagnostics;
+using Microsoft.Contracts;
+using Microsoft.Boogie.AbstractInterpretation;
+using Microsoft.Boogie.VCExprAST;
+
+namespace Microsoft.Boogie
+{
+ /// <summary>
+ /// Interface to the theorem prover specialized to Boogie.
+ ///
+ /// This class creates the appropriate background axioms. There
+ /// should be one instance per BoogiePL program.
+ /// </summary>
+ public sealed class Checker
+ {
+ private readonly VCExpressionGenerator! gen;
+
+ private ProverInterface! thmProver;
+ private CommandLineOptions.BvHandling bitvectors;
+ private int timeout;
+
+ // state for the async interface
+ private volatile ProverInterface.Outcome outcome;
+ private volatile bool busy;
+ private volatile bool hasOutput;
+ private volatile UnexpectedProverOutputException outputExn;
+ private DateTime proverStart;
+ private TimeSpan proverRunTime;
+ private volatile ProverInterface.ErrorHandler handler;
+ private volatile bool closed;
+
+ public readonly AutoResetEvent! ProverDone = new AutoResetEvent(false);
+
+ private static CommandLineOptions.BvHandling BvHandlingForImpl(Implementation! impl)
+ {
+ bool force_int = false;
+ bool force_native = false;
+ ((!)impl.Proc).CheckBooleanAttribute("forceBvInt", ref force_int);
+ impl.Proc.CheckBooleanAttribute("forceBvZ3Native", ref force_native);
+ impl.CheckBooleanAttribute("forceBvInt", ref force_int);
+ impl.CheckBooleanAttribute("forceBvZ3Native", ref force_native);
+ if (force_native) return CommandLineOptions.BvHandling.Z3Native;
+ if (force_int) return CommandLineOptions.BvHandling.ToInt;
+ return CommandLineOptions.Clo.Bitvectors;
+ }
+
+ public bool WillingToHandle(Implementation! impl, int timeout)
+ {
+ return !closed && timeout == this.timeout && bitvectors == BvHandlingForImpl(impl);
+ }
+
+ public VCExpressionGenerator! VCExprGen
+ {
+ get { return this.gen; }
+ }
+ public ProverInterface! TheoremProver
+ {
+ get { return this.thmProver; }
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////
+ // We share context information for the same program between different Checkers
+
+ private struct ContextCacheKey {
+ public readonly Program! program;
+ public readonly CommandLineOptions.BvHandling bitvectors;
+
+ public ContextCacheKey(Program! prog,
+ CommandLineOptions.BvHandling bitvectors) {
+ this.program = prog;
+ this.bitvectors = bitvectors;
+ }
+
+ [Pure][Reads(ReadsAttribute.Reads.Nothing)]
+ public override bool Equals(object that) {
+ if (that is ContextCacheKey) {
+ ContextCacheKey thatKey = (ContextCacheKey)that;
+ return this.program.Equals(thatKey.program) &&
+ this.bitvectors.Equals(thatKey.bitvectors);
+ }
+ return false;
+ }
+
+ [Pure]
+ public override int GetHashCode() {
+ return this.program.GetHashCode() + this.bitvectors.GetHashCode();
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////
+
+ /// <summary>
+ /// Constructor. Initialize a the checker with the program and log file.
+ /// </summary>
+ public Checker(VC.ConditionGeneration! vcgen, Program! prog, string/*?*/ logFilePath, bool appendLogFile, Implementation! impl, int timeout)
+ {
+ this.bitvectors = BvHandlingForImpl(impl);
+ this.timeout = timeout;
+
+ ProverOptions! options = ((!)CommandLineOptions.Clo.TheProverFactory).BlankProverOptions();
+
+ if (logFilePath != null) {
+ options.LogFilename = logFilePath;
+ if (appendLogFile)
+ options.AppendLogFile = appendLogFile;
+ }
+
+ options.Parse(CommandLineOptions.Clo.ProverOptions);
+
+ if (timeout > 0) {
+ options.TimeLimit = timeout * 1000;
+ }
+ options.BitVectors = this.bitvectors;
+
+ ContextCacheKey key = new ContextCacheKey (prog, this.bitvectors);
+ ProverContext ctx;
+ ProverInterface prover;
+
+ if (vcgen.CheckerCommonState == null) {
+ vcgen.CheckerCommonState = new Dictionary<ContextCacheKey, ProverContext!> ();
+ }
+ IDictionary<ContextCacheKey, ProverContext!>! cachedContexts = (IDictionary<ContextCacheKey, ProverContext!>) vcgen.CheckerCommonState;
+
+ if (cachedContexts.TryGetValue(key, out ctx)) {
+ ctx = (ProverContext!)((!)ctx).Clone();
+ prover = (ProverInterface)
+ CommandLineOptions.Clo.TheProverFactory.SpawnProver(options, ctx);
+ } else {
+ ctx = (ProverContext!)CommandLineOptions.Clo.TheProverFactory.NewProverContext(options);
+
+ // set up the context
+ foreach (Declaration! decl in prog.TopLevelDeclarations) {
+ TypeCtorDecl t = decl as TypeCtorDecl;
+ if (t != null) {
+ ctx.DeclareType(t, null);
+ }
+ }
+ foreach (Declaration! decl in prog.TopLevelDeclarations) {
+ Constant c = decl as Constant;
+ if (c != null) {
+ ctx.DeclareConstant(c, c.Unique, null);
+ } else {
+ Function f = decl as Function;
+ if (f != null) {
+ ctx.DeclareFunction(f, null);
+ }
+ }
+ }
+ foreach (Declaration! decl in prog.TopLevelDeclarations) {
+ bool expand = false;
+ Axiom ax = decl as Axiom;
+ decl.CheckBooleanAttribute("inline", ref expand);
+ if (!expand && ax != null) {
+ ctx.AddAxiom(ax, null);
+ }
+ }
+ foreach (Declaration! decl in prog.TopLevelDeclarations) {
+ GlobalVariable v = decl as GlobalVariable;
+ if (v != null) {
+ ctx.DeclareGlobalVariable(v, null);
+ }
+ }
+
+ // we first generate the prover and then store a clone of the
+ // context in the cache, so that the prover can setup stuff in
+ // the context to be cached
+ prover = (ProverInterface)
+ CommandLineOptions.Clo.TheProverFactory.SpawnProver(options, ctx);
+ cachedContexts.Add(key, (ProverContext!)ctx.Clone());
+ }
+
+ this.thmProver = prover;
+ this.gen = prover.VCExprGen;
+
+ // base();
+ }
+
+
+ /// <summary>
+ /// Clean-up.
+ /// </summary>
+ public void Close()
+ {
+ this.closed = true;
+ thmProver.Close();
+ }
+
+ /// <summary>
+ /// Push a Verification Condition as an Axiom
+ /// (Required for Doomed Program Point detection)
+ /// </summary>
+ public void PushVCExpr(VCExpr! vc)
+ {
+ //thmProver.Context.AddAxiom(vc);
+ thmProver.PushVCExpression(vc);
+ }
+
+ public bool IsBusy
+ {
+ get { return busy; }
+ }
+
+ public bool Closed
+ {
+ get { return closed; }
+ }
+
+ public bool HasOutput
+ {
+ get { return hasOutput; }
+ }
+
+ public TimeSpan ProverRunTime
+ {
+ get { return proverRunTime; }
+ }
+
+ private void WaitForOutput(object dummy)
+ {
+ try {
+ outcome = thmProver.CheckOutcome((!)handler);
+ } catch (UnexpectedProverOutputException e) {
+ outputExn = e;
+ }
+
+ switch (outcome) {
+ case ProverInterface.Outcome.Valid:
+ thmProver.LogComment("Valid");
+ break;
+ case ProverInterface.Outcome.Invalid:
+ thmProver.LogComment("Invalid");
+ break;
+ case ProverInterface.Outcome.TimeOut:
+ thmProver.LogComment("Timed out");
+ break;
+ case ProverInterface.Outcome.OutOfMemory:
+ thmProver.LogComment("Out of memory");
+ break;
+ case ProverInterface.Outcome.Undetermined:
+ thmProver.LogComment("Undetermined");
+ break;
+ }
+
+ assert busy;
+ hasOutput = true;
+ proverRunTime = DateTime.Now - proverStart;
+
+ ProverDone.Set();
+ }
+
+ public void BeginCheck(string! descriptiveName, VCExpr! vc, ProverInterface.ErrorHandler! handler)
+ requires !IsBusy;
+ {
+ assert !busy;
+ busy = true;
+ hasOutput = false;
+ outputExn = null;
+ this.handler = handler;
+
+ proverStart = DateTime.Now;
+ thmProver.BeginCheck(descriptiveName, vc, handler);
+ // gen.ClearSharedFormulas(); PR: don't know yet what to do with this guy
+
+ ThreadPool.QueueUserWorkItem(WaitForOutput);
+ }
+
+ public ProverInterface.Outcome ReadOutcome()
+ throws UnexpectedProverOutputException;
+ requires IsBusy;
+ requires HasOutput;
+ {
+ hasOutput = false;
+ busy = false;
+
+ if (outputExn != null) {
+ throw outputExn;
+ }
+
+ return outcome;
+ }
+ }
+
+ // -----------------------------------------------------------------------------------------------
+ // -----------------------------------------------------------------------------------------------
+ // -----------------------------------------------------------------------------------------------
+
+ public class ErrorModel {
+ public Dictionary<string!, int>! identifierToPartition;
+ public List<List<string!>>! partitionToIdentifiers;
+ public List<Object>! partitionToValue;
+ public Dictionary<object, int>! valueToPartition;
+ public Dictionary<string!, List<List<int>>!>! definedFunctions;
+
+ public ErrorModel(Dictionary<string!, int>! identifierToPartition, List<List<string!>>! partitionToIdentifiers, List<Object>! partitionToValue, Dictionary<object, int>! valueToPartition, Dictionary<string!, List<List<int>>!>! definedFunctions) {
+ this.identifierToPartition = identifierToPartition;
+ this.partitionToIdentifiers = partitionToIdentifiers;
+ this.partitionToValue = partitionToValue;
+ this.valueToPartition = valueToPartition;
+ this.definedFunctions = definedFunctions;
+ }
+
+ public virtual void Print(TextWriter! writer) { }
+ }
+
+ public abstract class ProverInterface
+ {
+ public enum Outcome { Valid, Invalid, TimeOut, OutOfMemory, Undetermined }
+
+ public class ErrorHandler
+ {
+ public virtual void OnModel(IList<string!>! labels, ErrorModel errModel)
+ {
+ }
+
+ public virtual void OnResourceExceeded(string! message)
+ {
+ }
+
+ public virtual Absy! Label2Absy(string! label)
+ {
+ throw new System.NotImplementedException();
+ }
+ }
+
+ public abstract void BeginCheck(string! descriptiveName, VCExpr! vc, ErrorHandler! handler);
+ [NoDefaultContract]
+ public abstract Outcome CheckOutcome(ErrorHandler! handler);
+ throws UnexpectedProverOutputException;
+
+ public virtual void LogComment(string! comment) {
+ }
+
+ public virtual void Close() {
+ }
+
+ /// <summary>
+ /// MSchaef: Allows to Push a VCExpression as Axiom on the prover stack (beta)
+ /// for now it is only implemented by ProcessTheoremProver and still requires some
+ /// testing
+ /// </summary>
+ public virtual void PushVCExpression(VCExpr! vc) {}
+
+ public abstract ProverContext! Context { get; }
+ public abstract VCExpressionGenerator! VCExprGen { get; }
+ }
+
+ public class ProverException : Exception
+ {
+ public ProverException(string s) : base(s)
+ {
+ }
+ }
+ public class UnexpectedProverOutputException : ProverException, ICheckedException
+ {
+ public UnexpectedProverOutputException(string s) : base(s)
+ {
+ }
+ }
+ public class ProverDiedException : UnexpectedProverOutputException
+ {
+ public ProverDiedException() : base("Prover died with no further output, perhaps it ran out of memory or was killed.")
+ {
+ }
+ }
+}
diff --git a/Source/VCGeneration/ConditionGeneration.ssc b/Source/VCGeneration/ConditionGeneration.ssc
new file mode 100644
index 00000000..2129b1fb
--- /dev/null
+++ b/Source/VCGeneration/ConditionGeneration.ssc
@@ -0,0 +1,790 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Threading;
+using System.IO;
+using Microsoft.Boogie;
+using Graphing;
+using AI = Microsoft.AbstractInterpretationFramework;
+using Microsoft.Contracts;
+using Microsoft.Basetypes;
+using Microsoft.Boogie.VCExprAST;
+
+namespace Microsoft.Boogie
+{
+ public abstract class Counterexample
+ {
+ [Peer] public BlockSeq! Trace;
+ [Peer] public List<string!>! relatedInformation;
+
+ internal Counterexample(BlockSeq! trace)
+ {
+ this.Trace = trace;
+ this.relatedInformation = new List<string!>();
+ // base();
+ }
+
+ public abstract int GetLocation();
+ }
+
+ public class CounterexampleComparer : IComparer<Counterexample!>
+ {
+ public int Compare(Counterexample! c1, Counterexample! c2) {
+ if (c1.GetLocation() == c2.GetLocation()) return 0;
+ if (c1.GetLocation() > c2.GetLocation()) return 1;
+ return -1;
+ }
+ }
+
+ public class AssertCounterexample : Counterexample
+ {
+ [Peer] public AssertCmd! FailingAssert;
+
+ public AssertCounterexample(BlockSeq! trace, AssertCmd! failingAssert)
+ : base(trace)
+ {
+ this.FailingAssert = failingAssert;
+ // base(trace);
+ }
+
+ public override int GetLocation() {
+ return FailingAssert.tok.line * 1000 + FailingAssert.tok.col;
+ }
+ }
+
+ public class CallCounterexample : Counterexample
+ {
+ public CallCmd! FailingCall;
+ public Requires! FailingRequires;
+
+ public CallCounterexample(BlockSeq! trace, CallCmd! failingCall, Requires! failingRequires)
+ : base(trace)
+ requires !failingRequires.Free;
+ {
+ this.FailingCall = failingCall;
+ this.FailingRequires = failingRequires;
+ // base(trace);
+ }
+
+ public override int GetLocation() {
+ return FailingCall.tok.line * 1000 + FailingCall.tok.col;
+ }
+ }
+
+ public class ReturnCounterexample : Counterexample
+ {
+ public TransferCmd! FailingReturn;
+ public Ensures! FailingEnsures;
+
+ public ReturnCounterexample(BlockSeq! trace, TransferCmd! failingReturn, Ensures! failingEnsures)
+ : base(trace)
+ requires !failingEnsures.Free;
+ {
+ this.FailingReturn = failingReturn;
+ this.FailingEnsures = failingEnsures;
+ // base(trace);
+ }
+
+ public override int GetLocation() {
+ return FailingReturn.tok.line * 1000 + FailingReturn.tok.col;
+ }
+ }
+
+ public class VerifierCallback
+ {
+ // reason == null means this is genuine counterexample returned by the prover
+ // other reason means it's time out/memory out/crash
+ public virtual void OnCounterexample(Counterexample! ce, string? reason)
+ {
+ }
+
+ // called in case resource is exceeded and we don't have counterexample
+ public virtual void OnTimeout(string! reason)
+ {
+ }
+
+ public virtual void OnOutOfMemory(string! reason)
+ {
+ }
+
+ public virtual void OnProgress(string phase, int step, int totalSteps, double progressEstimate)
+ {
+ }
+
+ public virtual void OnUnreachableCode(Implementation! impl)
+ {
+ }
+ }
+}
+
+////////////////////////////////////////////
+
+namespace VC
+{
+ using Bpl = Microsoft.Boogie;
+
+ public class VCGenException : Exception
+ {
+ public VCGenException(string s) : base(s)
+ {
+ }
+ }
+
+ public abstract class ConditionGeneration
+ {
+ protected internal object CheckerCommonState;
+
+ public enum Outcome { Correct, Errors, TimedOut, OutOfMemory, Inconclusive }
+
+ protected readonly List<Checker!>! provers = new List<Checker!>();
+ protected Implementation current_impl = null;
+
+
+ // shared across each implementation; created anew for each implementation
+ protected Hashtable /*Variable -> int*/ variable2SequenceNumber;
+ public Dictionary<Incarnation, Absy!>! incarnationOriginMap = new Dictionary<Incarnation, Absy!>();
+
+ // used only by FindCheckerFor
+ protected Program! program;
+ protected string/*?*/ logFilePath;
+ protected bool appendLogFile;
+
+ public ConditionGeneration(Program! p)
+ {
+ program = p;
+ }
+
+ /// <summary>
+ /// Takes an implementation and constructs a verification condition and sends
+ /// it to the theorem prover.
+ /// Returns null if "impl" is correct. Otherwise, returns a list of counterexamples,
+ /// each counterexample consisting of an array of labels.
+ /// </summary>
+ /// <param name="impl"></param>
+ public Outcome VerifyImplementation(Implementation! impl, Program! program, out List<Counterexample!>? errors)
+ ensures result == Outcome.Errors ==> errors != null;
+ throws UnexpectedProverOutputException;
+ {
+ Helpers.ExtraTraceInformation("Starting implementation verification");
+
+ CounterexampleCollector collector = new CounterexampleCollector();
+ Outcome outcome = VerifyImplementation(impl, program, collector);
+ if (outcome == Outcome.Errors) {
+ errors = collector.examples;
+ } else {
+ errors = null;
+ }
+
+ Helpers.ExtraTraceInformation("Finished implementation verification");
+ return outcome;
+ }
+
+ public abstract Outcome VerifyImplementation(Implementation! impl, Program! program, VerifierCallback! callback)
+ throws UnexpectedProverOutputException;
+
+
+/////////////////////////////////// Common Methods and Classes //////////////////////////////////////////
+
+ protected Checker! FindCheckerFor(Implementation! impl, int timeout)
+ {
+ int i = 0;
+ while (i < provers.Count) {
+ if (provers[i].Closed) {
+ provers.RemoveAt(i);
+ continue;
+ } else {
+ if (!provers[i].IsBusy && provers[i].WillingToHandle(impl, timeout)) return provers[i];
+ }
+ ++i;
+ }
+ string? log = logFilePath;
+ if (log != null && !log.Contains("@PROC@") && provers.Count > 0)
+ log = log + "." + provers.Count;
+ Checker! ch = new Checker(this, program, log, appendLogFile, impl, timeout);
+ provers.Add(ch);
+ return ch;
+ }
+
+
+ public void Close() { foreach (Checker! prover in provers) prover.Close(); }
+
+
+ protected class CounterexampleCollector : VerifierCallback
+ {
+ public readonly List<Counterexample!>! examples = new List<Counterexample!>();
+ public override void OnCounterexample(Counterexample! ce, string? reason)
+ {
+ examples.Add(ce);
+ }
+
+ public override void OnUnreachableCode(Implementation! impl)
+ {
+ System.Console.WriteLine("found unreachable code:");
+ EmitImpl(impl, false);
+ // TODO report error about next to last in seq
+ }
+ }
+
+ protected static void EmitImpl(Implementation! impl, bool printDesugarings) {
+ int oldPrintUnstructured = CommandLineOptions.Clo.PrintUnstructured;
+ CommandLineOptions.Clo.PrintUnstructured = 2; // print only the unstructured program
+ bool oldPrintDesugaringSetting = CommandLineOptions.Clo.PrintDesugarings;
+ CommandLineOptions.Clo.PrintDesugarings = printDesugarings;
+ impl.Emit(new TokenTextWriter("<console>", Console.Out, false), 0);
+ CommandLineOptions.Clo.PrintDesugarings = oldPrintDesugaringSetting;
+ CommandLineOptions.Clo.PrintUnstructured = oldPrintUnstructured;
+ }
+
+
+ protected Block! GenerateUnifiedExit(Implementation! impl, Hashtable! gotoCmdOrigins)
+ {
+ Block/*?*/ exitBlock = null;
+ #region Create a unified exit block, if there's more than one
+ {
+ int returnBlocks = 0;
+ foreach ( Block b in impl.Blocks )
+ {
+ if ( b.TransferCmd is ReturnCmd )
+ {
+ exitBlock = b;
+ returnBlocks++;
+ }
+ }
+ if ( returnBlocks > 1 )
+ {
+ string unifiedExitLabel = "GeneratedUnifiedExit";
+ Block! unifiedExit = new Block(new Token(-17, -4),unifiedExitLabel,new CmdSeq(),new ReturnCmd(Token.NoToken));
+
+ foreach ( Block b in impl.Blocks )
+ {
+ if ( b.TransferCmd is ReturnCmd )
+ {
+ StringSeq labels = new StringSeq();
+ labels.Add(unifiedExitLabel);
+ BlockSeq bs = new BlockSeq();
+ bs.Add(unifiedExit);
+ GotoCmd go = new GotoCmd(Token.NoToken,labels,bs);
+ gotoCmdOrigins[go] = b.TransferCmd;
+ b.TransferCmd = go;
+ unifiedExit.Predecessors.Add(b);
+ }
+ }
+
+ exitBlock = unifiedExit;
+ impl.Blocks.Add(unifiedExit);
+ }
+ assert exitBlock != null;
+ }
+ return exitBlock;
+ #endregion
+ }
+
+ /// <summary>
+ /// Helperfunction to restore the predecessor relations after loop unrolling
+ /// </summary>
+ protected void ComputePredecessors(List<Block!>! blocks)
+ {
+ #region Compute and store the Predecessor Relation on the blocks
+ // This code just here to try things out.
+ // Compute the predecessor relation for each block
+ // Store it in the Predecessors field within each block
+ foreach (Block b in blocks)
+ {
+ GotoCmd gtc = b.TransferCmd as GotoCmd;
+ if (gtc != null)
+ {
+ assume gtc.labelTargets != null;
+ foreach (Block! dest in gtc.labelTargets)
+ {
+ dest.Predecessors.Add(b);
+ }
+ }
+ }
+ #endregion Compute and store the Predecessor Relation on the blocks
+ }
+
+ protected static void ResetPredecessors(List<Block!>! blocks)
+ {
+ foreach (Block! b in blocks) {
+ b.Predecessors = new BlockSeq();
+ }
+ foreach (Block! b in blocks) {
+ foreach (Block! ch in Exits(b)) {
+ ch.Predecessors.Add(b);
+ }
+ }
+ }
+
+ protected static IEnumerable! Exits(Block! b)
+ {
+ GotoCmd g = b.TransferCmd as GotoCmd;
+ if (g != null) {
+ return (!)g.labelTargets;
+ }
+ return new List<Block!>();
+ }
+
+
+ protected Variable! CreateIncarnation(Variable! x, Absy a)
+ requires this.variable2SequenceNumber != null;
+ requires this.current_impl != null;
+ requires a is Block || a is AssignCmd || a is HavocCmd;
+ {
+ int currentIncarnationNumber =
+ variable2SequenceNumber.ContainsKey(x)
+ ?
+ (int) ((!)variable2SequenceNumber[x])
+ :
+ -1;
+ Variable v = new Incarnation(x,currentIncarnationNumber + 1);
+ variable2SequenceNumber[x] = currentIncarnationNumber + 1;
+ Debug.Assert(current_impl != null, "The field current_impl wasn't set.");
+ current_impl.LocVars.Add(v);
+ incarnationOriginMap.Add((Incarnation) v, a);
+ return v;
+ }
+
+ /// <summary>
+ /// Compute the incarnation map at the beginning of block "b" from the incarnation blocks of the
+ /// predecessors of "b".
+ ///
+ /// The predecessor map b.map for block "b" is defined as follows:
+ /// b.map.Domain == Union{Block p in b.predecessors; p.map.Domain}
+ /// Forall{Variable v in b.map.Domain;
+ /// b.map[v] == (v in Intersection{Block p in b.predecessors; p.map}.Domain
+ /// ? b.predecessors[0].map[v]
+ /// : new Variable())}
+ /// Every variable that b.map maps to a fresh variable requires a fixup in all predecessor blocks.
+ /// </summary>
+ /// <param name="b"></param>
+ /// <param name="block2Incarnation">Gives incarnation maps for b's predecessors.</param>
+ /// <returns></returns>
+ protected Hashtable! /*Variable -> Expr*/ ComputeIncarnationMap(Block! b, Hashtable! /*Variable -> Expr*/ block2Incarnation)
+ {
+ if (b.Predecessors.Length == 0)
+ {
+ return new Hashtable /*Variable -> Expr*/ ();
+ }
+
+ Hashtable /*Variable -> Expr*/ incarnationMap = null;
+ Set /*Variable*/ fixUps = new Set /*Variable*/ ();
+ foreach (Block! pred in b.Predecessors)
+ {
+ Debug.Assert(block2Incarnation.Contains(pred), "Passive Transformation found a block whose predecessors have not been processed yet.");
+ Hashtable /*Variable -> Expr*/ predMap = (Hashtable! /*Variable -> Expr*/) block2Incarnation[pred];
+ if (incarnationMap == null)
+ {
+ incarnationMap = (Hashtable /*Variable -> Expr*/)predMap.Clone();
+ continue;
+ }
+
+ ArrayList /*Variable*/ conflicts = new ArrayList /*Variable*/ ();
+ foreach (Variable! v in incarnationMap.Keys)
+ {
+ if (!predMap.Contains(v))
+ {
+ // conflict!!
+ conflicts.Add(v);
+ fixUps.Add(v);
+ }
+ }
+ // Now that we're done with enumeration, we'll do all the removes
+ foreach (Variable! v in conflicts)
+ {
+ incarnationMap.Remove(v);
+ }
+ foreach (Variable! v in predMap.Keys)
+ {
+ if (!incarnationMap.Contains(v))
+ {
+ // v was not in the domain of the precessors seen so far, so it needs to be fixed up
+ fixUps.Add(v);
+ }
+ else
+ {
+ // v in incarnationMap ==> all pred blocks (up to now) all agree on its incarnation
+ if (predMap[v] != incarnationMap[v])
+ {
+ // conflict!!
+ incarnationMap.Remove(v);
+ fixUps.Add(v);
+ }
+ }
+ }
+ }
+
+ #region Second, for all variables in the fixups list, introduce a new incarnation and push it back into the preds.
+ foreach (Variable! v in fixUps )
+ {
+ Variable v_prime = CreateIncarnation(v, b);
+ IdentifierExpr ie = new IdentifierExpr(v_prime.tok, v_prime);
+ assert incarnationMap != null;
+ incarnationMap[v] = ie;
+ foreach (Block! pred in b.Predecessors )
+ {
+ #region Create an assume command equating v_prime with its last incarnation in pred
+ #region Create an identifier expression for the last incarnation in pred
+ Hashtable /*Variable -> Expr*/ predMap = (Hashtable! /*Variable -> Expr*/) block2Incarnation[pred];
+ Expr pred_incarnation_exp;
+ Expr o = (Expr) predMap[v];
+ if (o == null)
+ {
+ Variable predIncarnation = v;
+ IdentifierExpr ie2 = new IdentifierExpr(predIncarnation.tok, predIncarnation);
+ pred_incarnation_exp = ie2;
+ }
+ else
+ {
+ pred_incarnation_exp = o;
+ }
+ #endregion
+ #region Create an identifier expression for the new incarnation
+ IdentifierExpr v_prime_exp = new IdentifierExpr(v_prime.tok, v_prime);
+ #endregion
+ #region Create the assume command itself
+ Expr e = Expr.Binary(Token.NoToken,
+ BinaryOperator.Opcode.Eq,
+ v_prime_exp,
+ pred_incarnation_exp
+ );
+ AssumeCmd ac = new AssumeCmd(v.tok,e);
+ pred.Cmds.Add(ac);
+ #endregion
+ #endregion
+ }
+ }
+ #endregion
+
+ assert incarnationMap != null;
+ return incarnationMap;
+ }
+
+ Hashtable preHavocIncarnationMap = null; // null = the previous command was not an HashCmd. Otherwise, a *copy* of the map before the havoc statement
+
+ protected void TurnIntoPassiveBlock(Block! b, Hashtable! /*Variable -> Expr*/ incarnationMap, Substitution! oldFrameSubst)
+ {
+ #region Walk forward over the commands in this block and convert them to passive commands
+
+ CmdSeq passiveCmds = new CmdSeq();
+ foreach (Cmd! c in b.Cmds)
+ { // walk forward over the commands because the map gets modified in a forward direction
+ TurnIntoPassiveCmd(c, incarnationMap, oldFrameSubst, passiveCmds);
+ }
+ b.Cmds = passiveCmds;
+
+ #endregion
+ }
+
+
+ protected void Convert2PassiveCmd(Implementation! impl)
+ {
+ #region Convert to Passive Commands
+
+ #region Topological sort -- need to process in a linearization of the partial order
+ Graph<Block> dag = new Graph<Block>();
+ dag.AddSource((!)impl.Blocks[0]); // there is always at least one node in the graph
+ foreach (Block b in impl.Blocks)
+ {
+ GotoCmd gtc = b.TransferCmd as GotoCmd;
+ if (gtc != null)
+ {
+ assume gtc.labelTargets != null;
+ foreach (Block! dest in gtc.labelTargets)
+ {
+ dag.AddEdge(b,dest);
+ }
+ }
+ }
+ IEnumerable! sortedNodes = dag.TopologicalSort();
+ //Debug.Assert( sortedNodes != null, "Topological Sort returned null." );
+ #endregion
+
+ // Create substitution for old expressions
+ Hashtable/*Variable!->Expr!*/ oldFrameMap = new Hashtable();
+ assume impl.Proc != null;
+ foreach (IdentifierExpr! ie in impl.Proc.Modifies) {
+ oldFrameMap.Add((!)ie.Decl, ie);
+ }
+ Substitution oldFrameSubst = Substituter.SubstitutionFromHashtable(oldFrameMap);
+
+ // Now we can process the nodes in an order so that we're guaranteed to have
+ // processed all of a node's predecessors before we process the node.
+ Hashtable /*Block -> IncarnationMap*/ block2Incarnation = new Hashtable/*Block -> IncarnationMap*/();
+ foreach (Block! b in sortedNodes )
+ {
+ Debug.Assert( !block2Incarnation.Contains(b) );
+ Hashtable /*Variable -> Expr*/ incarnationMap = ComputeIncarnationMap(b, block2Incarnation);
+
+ #region Each block's map needs to be available to successor blocks
+ block2Incarnation.Add(b,incarnationMap);
+ #endregion Each block's map needs to be available to successor blocks
+
+ TurnIntoPassiveBlock(b, incarnationMap, oldFrameSubst);
+ }
+
+ // We no longer need the where clauses on the out parameters, so we remove them to restore the situation from before VC generation
+ foreach (Formal! f in impl.OutParams) {
+ f.TypedIdent.WhereExpr = null;
+ }
+ #endregion Convert to Passive Commands
+
+ #region Debug Tracing
+ if (CommandLineOptions.Clo.TraceVerify)
+ {
+ Console.WriteLine("after conversion to passive commands");
+ EmitImpl(impl, true);
+ }
+ #endregion
+ }
+
+ /// <summary>
+ /// Turn a command into a passive command, and it remembers the previous step, to see if it is a havoc or not. In the case, it remebers the incarnation map BEFORE the havoc
+ /// </summary>
+ protected void TurnIntoPassiveCmd(Cmd! c, Hashtable /*Variable -> Expr*/! incarnationMap, Substitution! oldFrameSubst, CmdSeq! passiveCmds)
+ {
+ Substitution incarnationSubst = Substituter.SubstitutionFromHashtable(incarnationMap);
+ #region assert/assume P |--> assert/assume P[x := in(x)], out := in
+ if ( c is PredicateCmd )
+ {
+ Debug.Assert( c is AssertCmd || c is AssumeCmd, "Internal Error: Found a PredicateCmd h is not an assert or an assume." );
+
+ PredicateCmd! pc = (PredicateCmd) c.Clone();
+
+ if(pc is AssumeCmd && pc.Expr is LoopPredicate // Check if the PredicateCmd is in the form of "assume J", with J loop invariant predicate
+ && this.preHavocIncarnationMap != null) // Furthermore, the command just before was a (list of) havoc statements
+ {
+ LoopPredicate! lp = (LoopPredicate!) pc.Expr;
+ lp.SetPreAndPostHavocIncarnationMaps(this.preHavocIncarnationMap, (Hashtable /*Variable -> Expr*/!) incarnationMap.Clone());
+ }
+
+ Expr! copy = Substituter.ApplyReplacingOldExprs(incarnationSubst, oldFrameSubst, pc.Expr);
+ if (pc is AssertCmd) {
+ ((AssertCmd) pc).OrigExpr = pc.Expr;
+ assert ((AssertCmd) pc).IncarnationMap == null;
+ ((AssertCmd) pc).IncarnationMap = (Hashtable /*Variable -> Expr*/!) incarnationMap.Clone();
+ }
+ pc.Expr = copy;
+ passiveCmds.Add(pc);
+ }
+ #endregion
+ #region x1 := E1, x2 := E2, ... |--> assume x1' = E1[in] & x2' = E2[in], out := in( x |-> x' ) [except as noted below]
+ else if ( c is AssignCmd )
+ {
+ AssignCmd! assign = ((AssignCmd)c).AsSimpleAssignCmd; // first remove map assignments
+ #region Substitute all variables in E with the current map
+ List<Expr!>! copies = new List<Expr!> ();
+ foreach (Expr! e in assign.Rhss)
+ copies.Add(Substituter.ApplyReplacingOldExprs(incarnationSubst,
+ oldFrameSubst,
+ e));
+ #endregion
+
+ List<Expr!>! assumptions = new List<Expr!> ();
+ // it might be too slow to create a new dictionary each time ...
+ IDictionary<Variable!, Expr!>! newIncarnationMappings =
+ new Dictionary<Variable!, Expr!> ();
+
+ for (int i = 0; i < assign.Lhss.Count; ++i) {
+ IdentifierExpr! lhsIdExpr =
+ ((SimpleAssignLhs!)assign.Lhss[i]).AssignedVariable;
+ Variable! lhs = (!)lhsIdExpr.Decl;
+ Expr! rhs = assign.Rhss[i];
+
+ // don't create incarnations for assignments of literals or single variables.
+ if (rhs is LiteralExpr) {
+ incarnationMap[lhs] = rhs;
+ } else if (rhs is IdentifierExpr) {
+ IdentifierExpr! ie = (IdentifierExpr) rhs;
+ if ( incarnationMap.ContainsKey((!)ie.Decl) )
+ newIncarnationMappings[lhs] = (Expr!)incarnationMap[ie.Decl];
+ else
+ newIncarnationMappings[lhs] = ie;
+ } else {
+ IdentifierExpr x_prime_exp = null;
+ #region Make a new incarnation, x', for variable x, but only if x is *not* already an incarnation
+ if ( lhs is Incarnation ) {
+ // incarnations are already written only once, no need to make an incarnation of an incarnation
+ x_prime_exp = lhsIdExpr;
+ }
+ else
+ {
+ Variable v = CreateIncarnation(lhs, c);
+ x_prime_exp = new IdentifierExpr(lhsIdExpr.tok, v);
+ newIncarnationMappings[lhs] = x_prime_exp;
+ }
+ #endregion
+ #region Create an assume command with the new variable
+ assumptions.Add(Expr.Eq(x_prime_exp, copies[i]));
+ #endregion
+ }
+ }
+
+ foreach (KeyValuePair<Variable!, Expr!> pair in newIncarnationMappings)
+ incarnationMap[pair.Key] = pair.Value;
+
+ if (assumptions.Count > 0) {
+ Expr! assumption = assumptions[0];
+ for (int i = 1; i < assumptions.Count; ++i)
+ assumption = Expr.And(assumption, assumptions[i]);
+ passiveCmds.Add(new AssumeCmd(c.tok, assumption));
+ }
+ }
+ #endregion
+ #region havoc w |--> assume whereClauses, out := in( w |-> w' )
+ else if ( c is HavocCmd )
+ {
+ if(this.preHavocIncarnationMap == null) // Save a copy of the incarnation map (at the top of a sequence of havoc statements)
+ this.preHavocIncarnationMap = (Hashtable) incarnationMap.Clone();
+
+ HavocCmd! hc = (HavocCmd) c;
+ IdentifierExprSeq havocVars = hc.Vars;
+ // First, compute the new incarnations
+ foreach (IdentifierExpr! ie in havocVars)
+ {
+ if ( !(ie.Decl is Incarnation) )
+ {
+ Variable x = (!)ie.Decl;
+ Variable x_prime = CreateIncarnation(x, c);
+ incarnationMap[x] = new IdentifierExpr(x_prime.tok, x_prime);
+ }
+ }
+ // Then, perform the assume of the where clauses, using the updated incarnations
+ Substitution updatedIncarnationSubst = Substituter.SubstitutionFromHashtable(incarnationMap);
+ foreach (IdentifierExpr! ie in havocVars)
+ {
+ if ( !(ie.Decl is Incarnation) )
+ {
+ Variable x = (!)ie.Decl;
+ Bpl.Expr w = x.TypedIdent.WhereExpr;
+ if (w != null) {
+ Expr copy = Substituter.ApplyReplacingOldExprs(updatedIncarnationSubst, oldFrameSubst, w);
+ passiveCmds.Add(new AssumeCmd(c.tok, copy));
+ }
+ }
+ }
+ }
+ #endregion
+ else if (c is CommentCmd)
+ {
+ // comments are just for debugging and don't affect verification
+ }
+ else if (c is SugaredCmd)
+ {
+ SugaredCmd! sug = (SugaredCmd)c;
+ Cmd! cmd = sug.Desugaring;
+ TurnIntoPassiveCmd(cmd, incarnationMap, oldFrameSubst, passiveCmds);
+ }
+ else if (c is StateCmd)
+ {
+ this.preHavocIncarnationMap = null; // we do not need to remeber the previous incarnations
+ StateCmd! st = (StateCmd)c;
+ // account for any where clauses among the local variables
+ foreach (Variable! v in st.Locals) {
+ Expr w = v.TypedIdent.WhereExpr;
+ if (w != null) {
+ passiveCmds.Add(new AssumeCmd(v.tok, w));
+ }
+ }
+ // do the sub-commands
+ foreach (Cmd! s in st.Cmds) {
+ TurnIntoPassiveCmd(s, incarnationMap, oldFrameSubst, passiveCmds);
+ }
+ // remove the local variables from the incarnation map
+ foreach (Variable! v in st.Locals) {
+ incarnationMap.Remove(v);
+ }
+ }
+ #region There shouldn't be any other types of commands at this point
+ else
+ {
+ Debug.Fail("Internal Error: Passive transformation handed a command that is not one of assert,assume,havoc,assign." );
+ }
+ #endregion
+
+
+ #region We rember if we have put an havoc statement into a passive form
+
+ if(! (c is HavocCmd) )
+ this.preHavocIncarnationMap = null;
+ // else: it has already been set by the case for the HavocCmd
+ #endregion
+ }
+
+ /// <summary>
+ /// Creates a new block to add to impl.Blocks, where impl is the implementation that contains
+ /// succ. Caller must do the add to impl.Blocks.
+ /// </summary>
+ protected Block! CreateBlockBetween(int predIndex, Block! succ)
+ requires 0 <= predIndex && predIndex < succ.Predecessors.Length;
+ {
+ Block! pred = (!) succ.Predecessors[predIndex];
+
+ string! newBlockLabel = pred.Label + "_@2_" + succ.Label;
+
+ // successor of newBlock list
+ StringSeq ls = new StringSeq();
+ ls.Add(succ.Label);
+ BlockSeq bs = new BlockSeq();
+ bs.Add(succ);
+
+ Block newBlock = new Block(
+ new Token(-17, -4),
+ newBlockLabel,
+ new CmdSeq(),
+ new GotoCmd(Token.NoToken,ls,bs)
+ );
+
+ // predecessors of newBlock
+ BlockSeq ps = new BlockSeq();
+ ps.Add(pred);
+ newBlock.Predecessors = ps;
+
+ // fix successors of pred
+ #region Change the edge "pred->succ" to "pred->newBlock"
+ GotoCmd gtc = (GotoCmd!) pred.TransferCmd;
+ assume gtc.labelTargets != null;
+ assume gtc.labelNames != null;
+ for ( int i = 0, n = gtc.labelTargets.Length; i < n; i++ )
+ {
+ if ( gtc.labelTargets[i] == succ )
+ {
+ gtc.labelTargets[i] = newBlock;
+ gtc.labelNames[i] = newBlockLabel;
+ break;
+ }
+ }
+ #endregion Change the edge "pred->succ" to "pred->newBlock"
+
+ // fix predecessors of succ
+ succ.Predecessors[predIndex] = newBlock;
+
+ return newBlock;
+ }
+
+ protected void AddBlocksBetween(Implementation! impl)
+ {
+ #region Introduce empty blocks before all blocks with more than one predecessor
+ List<Block!> tweens = new List<Block!>();
+ foreach ( Block b in impl.Blocks )
+ {
+ int nPreds = b.Predecessors.Length;
+ if ( nPreds > 1 )
+ {
+ for (int i = 0; i < nPreds; i++ )
+ {
+ tweens.Add(CreateBlockBetween(i, b));
+ }
+ }
+ }
+ impl.Blocks.AddRange(tweens); // must wait until iteration is done before changing the list
+ #endregion
+ }
+
+ }
+} \ No newline at end of file
diff --git a/Source/VCGeneration/Context.ssc b/Source/VCGeneration/Context.ssc
new file mode 100644
index 00000000..be32cabe
--- /dev/null
+++ b/Source/VCGeneration/Context.ssc
@@ -0,0 +1,199 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using Microsoft.Contracts;
+using Microsoft.Boogie.VCExprAST;
+
+namespace Microsoft.Boogie
+{
+ /// <summary>
+ /// The methods of this class are called in the following order:
+ /// DeclareType*
+ /// (DeclareConstant DeclareFunction)*
+ /// AddAxiom*
+ /// DeclareGlobalVariable*
+ /// At this time, all "attributes" are passed in as null.
+ /// </summary>
+ public abstract class ProverContext : ICloneable {
+ protected virtual void ProcessDeclaration(Declaration! decl) {}
+ public virtual void DeclareType(TypeCtorDecl! t, string attributes) { ProcessDeclaration(t); }
+ public virtual void DeclareConstant(Constant! c, bool uniq, string attributes) { ProcessDeclaration(c); }
+ public virtual void DeclareFunction(Function! f, string attributes) { ProcessDeclaration(f); }
+ public virtual void AddAxiom(Axiom! a, string attributes) { ProcessDeclaration(a); }
+ public abstract void AddAxiom(VCExpr! vc);
+ public virtual void DeclareGlobalVariable(GlobalVariable! v, string attributes) { ProcessDeclaration(v); }
+
+ public abstract VCExpressionGenerator! ExprGen { get; }
+ public abstract Boogie2VCExprTranslator! BoogieExprTranslator { get; }
+ public abstract VCGenerationOptions! VCGenOptions { get; }
+
+ public abstract object! Clone();
+ }
+
+ // -----------------------------------------------------------------------------------------------
+ // -----------------------------------------------------------------------------------------------
+ // -----------------------------------------------------------------------------------------------
+
+ /// <summary>
+ /// This ProverContext subclass is intended for use with untyped provers that do not require names
+ /// to be declared before use. It constructs its context from unique constants and given axioms.
+ /// </summary>
+ public class DeclFreeProverContext : ProverContext {
+ protected VCExpressionGenerator! gen;
+ protected VCGenerationOptions! genOptions;
+ protected Boogie2VCExprTranslator! translator;
+
+ protected OrderingAxiomBuilder! orderingAxiomBuilder;
+
+ StringBuilder! proverCommands;
+ StringBuilder! incrementalProverCommands;
+
+ protected List<Variable!>! distincts;
+ protected List<VCExpr!>! axiomConjuncts;
+
+ public VCExprTranslator exprTranslator;
+
+ public DeclFreeProverContext(VCExpressionGenerator! gen,
+ VCGenerationOptions! genOptions) {
+ this.gen = gen;
+ this.genOptions = genOptions;
+ Boogie2VCExprTranslator! t = new Boogie2VCExprTranslator (gen, genOptions);
+ this.translator = t;
+ OrderingAxiomBuilder! oab = new OrderingAxiomBuilder(gen, t);
+ oab.Setup();
+ this.orderingAxiomBuilder = oab;
+
+ proverCommands = new StringBuilder();
+ incrementalProverCommands = new StringBuilder();
+
+ distincts = new List<Variable!>();
+ axiomConjuncts = new List<VCExpr!>();
+
+ exprTranslator = null;
+ }
+
+ private DeclFreeProverContext(DeclFreeProverContext! ctxt) {
+ this.gen = ctxt.gen;
+ this.genOptions = ctxt.genOptions;
+ Boogie2VCExprTranslator! t = (Boogie2VCExprTranslator)ctxt.translator.Clone();
+ this.translator = t;
+ this.orderingAxiomBuilder = new OrderingAxiomBuilder(ctxt.gen, t, ctxt.orderingAxiomBuilder);
+
+ StringBuilder! cmds = new StringBuilder ();
+ cmds.Append(ctxt.proverCommands);
+ proverCommands = cmds;
+ StringBuilder! incCmds = new StringBuilder ();
+ incCmds.Append(ctxt.incrementalProverCommands);
+ incrementalProverCommands = incCmds;
+
+ distincts = new List<Variable!>(ctxt.distincts);
+ axiomConjuncts = new List<VCExpr!>(ctxt.axiomConjuncts);
+
+ if (ctxt.exprTranslator == null)
+ exprTranslator = null;
+ else
+ exprTranslator = (VCExprTranslator!)ctxt.exprTranslator.Clone();
+ }
+
+ public override object! Clone() {
+ return new DeclFreeProverContext(this);
+ }
+
+ internal protected void SayToProver(string! msg)
+ {
+ msg = msg + "\r\n";
+ proverCommands.Append(msg);
+ incrementalProverCommands.Append(msg);
+ }
+
+ protected override void ProcessDeclaration(Declaration! decl)
+ {
+ for (QKeyValue a = decl.Attributes; a != null; a = a.Next) {
+ if (a.Key == "prover" && a.Params.Count == 1) {
+ string cmd = a.Params[0] as string;
+ if (cmd != null) {
+ int pos = cmd.IndexOf(':');
+ if (pos <= 0)
+ throw new ProverException("Invalid syntax of :prover string: `" + cmd + "'");
+ string kind = cmd.Substring(0, pos);
+ if (genOptions.IsAnyProverCommandSupported(kind))
+ SayToProver(cmd.Substring(pos + 1));
+ }
+ }
+ }
+ }
+
+ public override void DeclareFunction(Function! f, string attributes) {
+ base.ProcessDeclaration(f);
+ }
+
+ public override void DeclareConstant(Constant! c, bool uniq, string attributes) {
+ base.DeclareConstant(c, uniq, attributes);
+ orderingAxiomBuilder.AddConstant(c);
+
+ // TODO: make separate distinct lists for names coming from different types
+ // e.g., one for strings, one for ints, one for program types.
+ if (uniq){
+ distincts.Add(c);
+ }
+ }
+
+ public override void AddAxiom(Axiom! ax, string attributes) {
+ base.AddAxiom(ax, attributes);
+
+ string ignore = ax.FindStringAttribute("ignore");
+ if (ignore != null && genOptions.IsAnyProverCommandSupported(ignore)) {
+ return;
+ }
+
+ axiomConjuncts.Add(translator.Translate(ax.Expr));
+ }
+
+ public override void AddAxiom(VCExpr! vc)
+ {
+ axiomConjuncts.Add(vc);
+ }
+
+ public VCExpr! Axioms {
+ get {
+ VCExpr axioms = gen.NAry(VCExpressionGenerator.AndOp, axiomConjuncts);
+ List<VCExpr!>! distinctVars = new List<VCExpr!> ();
+ foreach (Variable! v in distincts)
+ distinctVars.Add(translator.LookupVariable(v));
+ axioms = gen.AndSimp(gen.Distinct(distinctVars), axioms);
+ if (CommandLineOptions.Clo.TypeEncodingMethod != CommandLineOptions.TypeEncoding.Monomorphic)
+ axioms = gen.AndSimp(orderingAxiomBuilder.Axioms, axioms);
+ return axioms;
+ }
+ }
+
+ public string! GetProverCommands(bool full) {
+ string! res = (full ? proverCommands : incrementalProverCommands).ToString();
+ incrementalProverCommands.Length = 0;
+ return res;
+ }
+
+ public override VCExpressionGenerator! ExprGen { get {
+ return gen;
+ } }
+ public override Boogie2VCExprTranslator! BoogieExprTranslator { get {
+ return translator;
+ } }
+ public override VCGenerationOptions! VCGenOptions { get {
+ return genOptions;
+ } }
+ }
+
+ // Translator from VCExpressions to strings, which are implemented
+ // by the various provers
+ public abstract class VCExprTranslator : ICloneable {
+ public abstract string! translate(VCExpr! expr, int polarity);
+ public abstract Object! Clone();
+ }
+}
diff --git a/Source/VCGeneration/OrderingAxioms.ssc b/Source/VCGeneration/OrderingAxioms.ssc
new file mode 100644
index 00000000..e4fc54f8
--- /dev/null
+++ b/Source/VCGeneration/OrderingAxioms.ssc
@@ -0,0 +1,285 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using Microsoft.Contracts;
+using Microsoft.Boogie.VCExprAST;
+
+// Class for constructing and collecting the axioms of the partial
+// order <:. The class also manages "unique" attributes of constants
+// and generated the necessary assumptions for the theorem prover.
+
+// TODO: there should be an interface so that different ways to handle
+// ordering relations can be accessed uniformly
+
+namespace Microsoft.Boogie
+{
+
+ public class OrderingAxiomBuilder {
+
+ private readonly VCExpressionGenerator! Gen;
+ private readonly Boogie2VCExprTranslator! Translator;
+
+ public OrderingAxiomBuilder(VCExpressionGenerator! gen,
+ Boogie2VCExprTranslator! translator) {
+ this.Gen = gen;
+ this.Translator = translator;
+ OneStepFuns = new Dictionary<Type!, Function!> ();
+ Constants = new List<Constant!> ();
+ CompleteConstantsOpen = new List<Constant!> ();
+ AllAxioms = new List<VCExpr!> ();
+ IncAxioms = new List<VCExpr!> ();
+ }
+
+ public OrderingAxiomBuilder(VCExpressionGenerator! gen,
+ Boogie2VCExprTranslator! translator,
+ OrderingAxiomBuilder! builder) {
+ this.Gen = gen;
+ this.Translator = translator;
+ OneStepFuns = new Dictionary<Type!, Function!> (builder.OneStepFuns);
+ Constants = new List<Constant!> (builder.Constants);
+ CompleteConstantsOpen = new List<Constant!> (builder.CompleteConstantsOpen);
+ AllAxioms = new List<VCExpr!> (builder.AllAxioms);
+ IncAxioms = new List<VCExpr!> (builder.IncAxioms);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ // Used to axiomatise the disjoint-sub-dag specs that are
+ // described by parents with the "unique" flag
+ private readonly IDictionary<Type!, Function!>! OneStepFuns;
+
+ private Function! OneStepFunFor(Type! t) {
+ Function res;
+ if (!OneStepFuns.TryGetValue(t, out res)) {
+ VariableSeq! args = new VariableSeq ();
+ args.Add(new Formal (Token.NoToken,
+ new TypedIdent (Token.NoToken, "arg0", t),
+ true));
+ args.Add(new Formal (Token.NoToken,
+ new TypedIdent (Token.NoToken, "arg1", t),
+ true));
+ Formal! result = new Formal (Token.NoToken,
+ new TypedIdent (Token.NoToken, "res", t),
+ false);
+ res = new Function (Token.NoToken, "oneStep",
+ new TypeVariableSeq (), args, result);
+ OneStepFuns.Add(t, res);
+ }
+ return (!)res;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ private readonly List<Constant!>! Constants = new List<Constant!> ();
+
+ // A list to handle constants whose direct children are fully
+ // specified (the "complete" keyword). Constants are removed from
+ // the list as soon as the corresponding axiom has been generated,
+ // which means that from this point on no further children can be
+ // added
+ private readonly List<Constant!>! CompleteConstantsOpen = new List<Constant!> ();
+
+ // list in which all axioms are collected
+ private readonly List<VCExpr!>! AllAxioms = new List<VCExpr!> ();
+
+ // list in which axioms are incrementally collected
+ private readonly List<VCExpr!>! IncAxioms = new List<VCExpr!> ();
+
+ private void AddAxiom(VCExpr! axiom) {
+ if (axiom.Equals(VCExpressionGenerator.True))
+ return;
+ AllAxioms.Add(axiom);
+ IncAxioms.Add(axiom);
+ }
+
+ // Return all axioms that were added since the last time NewAxioms
+ // was called
+ public VCExpr! GetNewAxioms() {
+ CloseChildrenCompleteConstants();
+ VCExpr! res = Gen.NAry(VCExpressionGenerator.AndOp, IncAxioms);
+ IncAxioms.Clear();
+ return res;
+ }
+
+ // return all axioms
+ public VCExpr! Axioms { get {
+ CloseChildrenCompleteConstants();
+ return Gen.NAry(VCExpressionGenerator.AndOp, AllAxioms);
+ } }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ // Generate the normal axioms for a partial order relation
+ public void Setup() {
+ TypeVariable! alpha = new TypeVariable(Token.NoToken, "alpha");
+ List<TypeVariable!>! typeParams = new List<TypeVariable!> ();
+ typeParams.Add(alpha);
+
+ List<VCTrigger!>! triggers = new List<VCTrigger!> ();
+
+ VCExprVar! x = Gen.Variable("x", alpha);
+ VCExprVar! y = Gen.Variable("y", alpha);
+ VCExprVar! z = Gen.Variable("z", alpha);
+
+ List<VCExprVar!>! boundVars = new List<VCExprVar!> ();
+
+ // reflexivity
+ boundVars.Add(x);
+ AddAxiom(Gen.Forall(typeParams, boundVars, triggers,
+ new VCQuantifierInfos ("bg:subtype-refl", -1, false, null),
+ Gen.AtMost(x, x)));
+
+ // transitivity
+ boundVars = new List<VCExprVar!> ();
+ boundVars.Add(x); boundVars.Add(y); boundVars.Add(z);
+ triggers = new List<VCTrigger!> ();
+ triggers.Add(Gen.Trigger(true, Gen.AtMost(x, y), Gen.AtMost(y, z)));
+ VCExpr! body = Gen.Implies(Gen.And(Gen.AtMost(x, y), Gen.AtMost(y, z)),
+ Gen.AtMost(x, z));
+ AddAxiom(Gen.Forall(typeParams, boundVars, triggers,
+ new VCQuantifierInfos ("bg:subtype-trans", -1, false, null),
+ body));
+
+ // anti-symmetry
+ boundVars = new List<VCExprVar!> ();
+ boundVars.Add(x); boundVars.Add(y);
+ triggers = new List<VCTrigger!> ();
+ triggers.Add(Gen.Trigger(true, Gen.AtMost(x, y), Gen.AtMost(y, x)));
+ body = Gen.Implies(Gen.And(Gen.AtMost(x, y), Gen.AtMost(y, x)),
+ Gen.Eq(x, y));
+ AddAxiom(Gen.Forall(typeParams, boundVars, triggers,
+ new VCQuantifierInfos ("bg:subtype-antisymm", -1, false, null),
+ body));
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ public void AddConstant(Constant! c) {
+ AddAxiom(GenParentConstraints(c));
+ Constants.Add(c);
+ if (c.ChildrenComplete)
+ CompleteConstantsOpen.Add(c);
+
+ // ensure that no further children are added to closed
+ // children-complete constants
+ assert !(c.Parents != null &&
+ exists{ConstantParent! p in c.Parents;
+ ((Constant!)p.Parent.Decl).ChildrenComplete &&
+ !CompleteConstantsOpen.Contains((Constant)p.Parent.Decl)});
+ }
+
+ // Generate the constraints telling that parents of a constant are
+ // strictly greater than the constant itself, and are the minimal
+ // elements with this property
+ private VCExpr! GenParentConstraints(Constant! c) {
+ VCExpr! res = VCExpressionGenerator.True;
+
+ if (c.Parents == null)
+ return res;
+
+ VCExprVar! cAsVar = Translator.LookupVariable(c);
+ VCExprVar! w = Gen.Variable("w", c.TypedIdent.Type);
+
+ // Parents of c are proper ancestors of c
+ foreach (ConstantParent! p in c.Parents) {
+ VCExprVar! par = Translator.LookupVariable((!)p.Parent.Decl);
+ res = Gen.AndSimp(res, Gen.Neq(cAsVar, par));
+ res = Gen.AndSimp(res, Gen.AtMost(cAsVar, par));
+ }
+
+ // Parents are direct ancestors of c (no other elements are in
+ // between c and a parent)
+ foreach (ConstantParent! p in c.Parents) {
+ VCExprVar! par = Translator.LookupVariable((!)p.Parent.Decl);
+ VCExpr! antecedent1 = Gen.AtMost(cAsVar, w);
+ VCExpr! antecedent2 = Gen.AtMost(w, par);
+ VCExpr! body = Gen.Implies(Gen.And(antecedent1, antecedent2),
+ Gen.Or(Gen.Eq(cAsVar, w), Gen.Eq(par, w)));
+ res = Gen.AndSimp(res,
+ Gen.Forall(w,
+ Gen.Trigger(true, antecedent1, antecedent2),
+ body));
+ }
+
+ // Ancestors of c are only c itself and the ancestors of the
+ // parents of c
+ VCExpr! minAncestors = Gen.Eq(cAsVar, w);
+ foreach (ConstantParent! p in c.Parents)
+ minAncestors =
+ Gen.Or(minAncestors,
+ Gen.AtMost(Translator.LookupVariable((!)p.Parent.Decl), w));
+
+ VCExpr! antecedent = Gen.AtMost(cAsVar, w);
+ res = Gen.AndSimp(res,
+ Gen.Forall(w,
+ Gen.Trigger(true, antecedent),
+ Gen.Implies(antecedent, minAncestors)));
+
+ // Constraints for unique child-parent edges
+ foreach (ConstantParent! p in c.Parents) {
+ if (p.Unique)
+ res =
+ Gen.AndSimp(res,
+ GenUniqueParentConstraint(c, (Constant!)p.Parent.Decl));
+ }
+
+ return res;
+ }
+
+ // Generate axioms that state that all direct children of c are
+ // specified; this is the dual of the axiom stating that all direct
+ // ancestors of a constant are known
+ private VCExpr! GenCompleteChildrenConstraints(Constant! c)
+ requires c.ChildrenComplete; {
+
+ VCExprVar! cAsVar = Translator.LookupVariable(c);
+ VCExprVar! w = Gen.Variable("w", c.TypedIdent.Type);
+
+ VCExpr! maxDescendants = Gen.Eq(cAsVar, w);
+ foreach (Constant! d in Constants) {
+ if (d.Parents != null &&
+ exists{ConstantParent! p in d.Parents; c.Equals(p.Parent.Decl)})
+ maxDescendants = Gen.Or(maxDescendants,
+ Gen.AtMost(w, Translator.LookupVariable(d)));
+ }
+
+ VCExpr! antecedent = Gen.AtMost(w, cAsVar);
+ return Gen.Forall(w,
+ Gen.Trigger(true, antecedent),
+ Gen.Implies(antecedent, maxDescendants));
+ }
+
+ private void CloseChildrenCompleteConstants() {
+ foreach (Constant! c in CompleteConstantsOpen)
+ AddAxiom(GenCompleteChildrenConstraints(c));
+ CompleteConstantsOpen.Clear();
+ }
+
+ // Generate the axiom ensuring that the sub-dags underneath unique
+ // child-parent edges are all disjoint
+ private VCExpr! GenUniqueParentConstraint(Constant! child, Constant! parent)
+ requires child.TypedIdent.Type.Equals(parent.TypedIdent.Type); {
+
+ VCExprVar! w = Gen.Variable("w", child.TypedIdent.Type);
+
+ VCExpr! antecedent =
+ Gen.AtMost(w, Translator.LookupVariable(child));
+ VCExpr! succedent =
+ Gen.Eq(Gen.Function(OneStepFunFor(child.TypedIdent.Type),
+ Translator.LookupVariable(parent), w),
+ Translator.LookupVariable(child));
+
+ return Gen.Forall(w,
+ Gen.Trigger(true, antecedent),
+ Gen.Implies(antecedent, succedent));
+ }
+
+ }
+
+}
diff --git a/Source/VCGeneration/VC.ssc b/Source/VCGeneration/VC.ssc
new file mode 100644
index 00000000..617e52e6
--- /dev/null
+++ b/Source/VCGeneration/VC.ssc
@@ -0,0 +1,3215 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Threading;
+using System.IO;
+using Microsoft.Boogie;
+using Graphing;
+using AI = Microsoft.AbstractInterpretationFramework;
+using Microsoft.Contracts;
+using Microsoft.Basetypes;
+using Microsoft.Boogie.VCExprAST;
+
+
+namespace VC
+{
+ using Bpl = Microsoft.Boogie;
+
+ public class VCGen : ConditionGeneration
+ {
+
+ /// <summary>
+ /// Constructor. Initializes the theorem prover.
+ /// </summary>
+ public VCGen(Program! program, string/*?*/ logFilePath, bool appendLogFile)
+ // throws ProverException
+ {
+ base(program);
+ this.appendLogFile = appendLogFile;
+ this.logFilePath = logFilePath;
+ // base();
+ }
+
+ private static AssumeCmd! AssertTurnedIntoAssume(AssertCmd! assrt)
+ {
+ Expr! expr = assrt.Expr;
+
+ switch (CommandLineOptions.Clo.UseSubsumption) {
+ case CommandLineOptions.SubsumptionOption.Never:
+ expr = Expr.True;
+ break;
+ case CommandLineOptions.SubsumptionOption.Always:
+ break;
+ case CommandLineOptions.SubsumptionOption.NotForQuantifiers:
+ if (expr is QuantifierExpr) {
+ expr = Expr.True;
+ }
+ break;
+ default:
+ assert false; // unexpected case
+ }
+
+ return new AssumeCmd(assrt.tok, expr);
+ }
+
+ /// <summary>
+ /// Get the where clauses from the in- and out-parameters as
+ /// a sequence of assume commands.
+ /// As a side effect, this method adds these where clauses to the out parameters.
+ /// </summary>
+ /// <param name="impl"></param>
+ private static CmdSeq! GetParamWhereClauses(Implementation! impl)
+ requires impl.Proc != null;
+ {
+ TokenTextWriter debugWriter = null;
+ if (CommandLineOptions.Clo.PrintWithUniqueASTIds) {
+ debugWriter = new TokenTextWriter("<console>", Console.Out, false);
+ debugWriter.WriteLine("Effective precondition from where-clauses:");
+ }
+
+ Substitution formalProcImplSubst = Substituter.SubstitutionFromHashtable(impl.GetImplFormalMap());
+ CmdSeq! whereClauses = new CmdSeq();
+
+ // where clauses of in-parameters
+ foreach (Formal! f in impl.Proc.InParams)
+ {
+ if (f.TypedIdent.WhereExpr != null) {
+ Expr e = Substituter.Apply(formalProcImplSubst, f.TypedIdent.WhereExpr);
+ Cmd c = new AssumeCmd(f.tok, e);
+ whereClauses.Add(c);
+
+ if (debugWriter != null) { c.Emit(debugWriter, 1); }
+ }
+ }
+
+ // where clauses of out-parameters
+ assert impl.OutParams.Length == impl.Proc.OutParams.Length;
+ for (int i = 0; i < impl.OutParams.Length; i++) {
+ Variable f = (!)impl.Proc.OutParams[i];
+ if (f.TypedIdent.WhereExpr != null) {
+ Expr e = Substituter.Apply(formalProcImplSubst, f.TypedIdent.WhereExpr);
+ Cmd c = new AssumeCmd(f.tok, e);
+ whereClauses.Add(c);
+
+ Variable fi = (!)impl.OutParams[i];
+ assume fi.TypedIdent.WhereExpr == null;
+ fi.TypedIdent.WhereExpr = e;
+
+ if (debugWriter != null) { c.Emit(debugWriter, 1); }
+ }
+ }
+
+ if (debugWriter != null) { debugWriter.WriteLine(); }
+
+ return whereClauses;
+ }
+
+ private static void
+ ThreadInBlockExpr(Implementation! impl,
+ Block! targetBlock,
+ BlockExpr! blockExpr,
+ bool replaceWithAssert,
+ TokenTextWriter debugWriter){
+ // Go through blockExpr and for all blocks that have a "return e"
+ // as their transfer command:
+ // Replace all "return e" with "assert/assume e"
+ // Change the transfer command to "goto targetBlock"
+ // Then add all of the blocks in blockExpr to the implementation (at the end)
+ foreach (Block! b in blockExpr.Blocks){
+ ReturnExprCmd rec = b.TransferCmd as ReturnExprCmd;
+ if (rec != null){ // otherwise it is a goto command
+ if (replaceWithAssert){
+ Ensures! ens = new Ensures(rec.tok, false, rec.Expr, null);
+ Cmd! c = new AssertEnsuresCmd(ens);
+ b.Cmds.Add(c);
+ }else{
+ b.Cmds.Add(new AssumeCmd(rec.tok, rec.Expr));
+ }
+ b.TransferCmd = new GotoCmd(Token.NoToken,
+ new StringSeq(targetBlock.Label),
+ new BlockSeq(targetBlock));
+ targetBlock.Predecessors.Add(b);
+ }
+ impl.Blocks.Add(b);
+ }
+ if (debugWriter != null){
+ blockExpr.Emit(debugWriter, 1,false);
+ }
+ return;
+ }
+
+ private static void AddAsPrefix(Block! b, CmdSeq! cs){
+ CmdSeq newCommands = new CmdSeq();
+ newCommands.AddRange(cs);
+ newCommands.AddRange(b.Cmds);
+ b.Cmds = newCommands;
+ }
+
+ /// <summary>
+ /// Modifies an implementation by inserting all preconditions
+ /// as assume statements at the beginning of the implementation
+ /// </summary>
+ /// <param name="impl"></param>
+ private static void InjectPreconditions(Implementation! impl)
+ requires impl.Proc != null;
+ {
+ TokenTextWriter debugWriter = null;
+ if (CommandLineOptions.Clo.PrintWithUniqueASTIds) {
+ debugWriter = new TokenTextWriter("<console>", Console.Out, false);
+ debugWriter.WriteLine("Effective precondition:");
+ }
+
+ Substitution formalProcImplSubst = Substituter.SubstitutionFromHashtable(impl.GetImplFormalMap());
+
+ Block! originalEntryPoint = (!) impl.Blocks[0];
+ Block! currentEntryPoint = (!) impl.Blocks[0];
+ CmdSeq! currentClump = new CmdSeq(); // to hold onto contiguous "regular" preconditions
+
+ // (free and checked) requires clauses
+ for (int i = impl.Proc.Requires.Length-1; 0 <= i; i--){
+
+ // need to process the preconditions from bottom up, because
+ // for any that are BlockExprs, we need to thread them on
+ // to the top of the implementation
+
+ Requires req = impl.Proc.Requires[i];
+ Expr! e = Substituter.Apply(formalProcImplSubst, req.Condition);
+ BlockExpr be = req.Condition as BlockExpr;
+ if (be != null){
+ if (currentClump.Length > 0){
+ AddAsPrefix(currentEntryPoint, currentClump);
+ currentClump = new CmdSeq();
+ }
+ ThreadInBlockExpr(impl,currentEntryPoint, be,false,debugWriter);
+ currentEntryPoint = (!)be.Blocks[0];
+ }else{
+ Cmd! c = new AssumeCmd(req.tok, e);
+ currentClump.Add(c);
+ if (debugWriter != null) { c.Emit(debugWriter, 1); }
+ }
+
+ }
+
+ if (currentClump.Length > 0){
+ AddAsPrefix(currentEntryPoint, currentClump);
+ }
+
+ if (currentEntryPoint != originalEntryPoint){
+ string EntryLabel = "PreconditionGeneratedEntry";
+ Block! newEntry = new Block(new Token(-17, -4),EntryLabel,new CmdSeq(),
+ new GotoCmd(Token.NoToken,
+ new StringSeq(currentEntryPoint.Label),
+ new BlockSeq(currentEntryPoint)));
+ currentEntryPoint.Predecessors.Add(newEntry);
+ List<Block!> newBody = new List<Block!>();
+ newBody.Add(newEntry);
+ newBody.AddRange(impl.Blocks);
+ impl.Blocks = newBody;
+ }
+
+ if (debugWriter != null) { debugWriter.WriteLine(); }
+
+ return;
+ }
+ /// <summary>
+ /// Modifies an implementation by inserting all postconditions
+ /// as assert statements at the end of the implementation
+ /// </summary>
+ /// <param name="impl"></param>
+ /// <param name="unifiedExitblock">The unified exit block that has
+ /// already been constructed for the implementation (and so
+ /// is already an element of impl.Blocks)
+ /// </param>
+ private static void InjectPostConditions(Implementation! impl, Block! unifiedExitBlock, Hashtable/*TransferCmd->ReturnCmd*/! gotoCmdOrigins)
+ requires impl.Proc != null;
+ {
+ TokenTextWriter debugWriter = null;
+ if (CommandLineOptions.Clo.PrintWithUniqueASTIds) {
+ debugWriter = new TokenTextWriter("<console>", Console.Out, false);
+ debugWriter.WriteLine("Effective postcondition:");
+ }
+
+ string ExitLabel = "ReallyLastGeneratedExit";
+ Block! newExit = new Block(new Token(-17, -4),ExitLabel,new CmdSeq(),new ReturnCmd(Token.NoToken));
+ impl.Blocks.Add(newExit);
+ Block! currentEntryPoint = newExit;
+ CmdSeq! currentClump = new CmdSeq(); // to hold onto contiguous "regular" postconditions
+
+ Substitution formalProcImplSubst = Substituter.SubstitutionFromHashtable(impl.GetImplFormalMap());
+
+ // (free and checked) ensures clauses
+ for (int i = impl.Proc.Ensures.Length-1; 0 <= i; i--){
+
+ // need to process the postconditions from bottom up, because
+ // for any that are BlockExprs, we need to thread them on
+ // to the top of the implementation
+
+ Ensures ens = (impl.Proc).Ensures[i];
+ if (!ens.Free) { // free ensures aren't needed for verifying the implementation
+ Expr! e = Substituter.Apply(formalProcImplSubst, ens.Condition);
+ BlockExpr be = ens.Condition as BlockExpr;
+ if (be != null){
+ if (currentClump.Length > 0){
+ AddAsPrefix(currentEntryPoint, currentClump);
+ currentClump = new CmdSeq();
+ }
+ ThreadInBlockExpr(impl,currentEntryPoint,be,true,debugWriter);
+ currentEntryPoint = (!)be.Blocks[0];
+ }else{
+ Ensures! ensCopy = (Ensures!) ens.Clone();
+ ensCopy.Condition = e;
+ Cmd! c = new AssertEnsuresCmd(ensCopy);
+ ((AssertEnsuresCmd) c).ErrorDataEnhanced = ensCopy.ErrorDataEnhanced;
+ currentClump.Add(c);
+ if (debugWriter != null) { c.Emit(debugWriter, 1); }
+ }
+ }
+
+ }
+
+ if (currentClump.Length > 0){
+ AddAsPrefix(currentEntryPoint, currentClump);
+ }
+
+ GotoCmd gtc = new GotoCmd(Token.NoToken,
+ new StringSeq(currentEntryPoint.Label),
+ new BlockSeq(currentEntryPoint));
+ gotoCmdOrigins[gtc] = unifiedExitBlock.TransferCmd;
+ unifiedExitBlock.TransferCmd = gtc;
+ currentEntryPoint.Predecessors.Add(unifiedExitBlock);
+
+ if (debugWriter != null) { debugWriter.WriteLine(); }
+
+ return;
+ }
+
+
+ /// <summary>
+ /// Get the pre-condition of an implementation, including the where clauses from the in-parameters.
+ /// </summary>
+ /// <param name="impl"></param>
+ private static CmdSeq! GetPre(Implementation! impl)
+ requires impl.Proc != null;
+ {
+ TokenTextWriter debugWriter = null;
+ if (CommandLineOptions.Clo.PrintWithUniqueASTIds) {
+ debugWriter = new TokenTextWriter("<console>", Console.Out, false);
+ debugWriter.WriteLine("Effective precondition:");
+ }
+
+ Substitution formalProcImplSubst = Substituter.SubstitutionFromHashtable(impl.GetImplFormalMap());
+ CmdSeq! pre = new CmdSeq();
+
+ // (free and checked) requires clauses
+ foreach (Requires! req in impl.Proc.Requires)
+ {
+ Expr! e = Substituter.Apply(formalProcImplSubst, req.Condition);
+ Cmd! c = new AssumeCmd(req.tok, e);
+ pre.Add(c);
+
+ if (debugWriter != null) { c.Emit(debugWriter, 1); }
+ }
+
+ if (debugWriter != null) { debugWriter.WriteLine(); }
+
+ return pre;
+ }
+
+ /// <summary>
+ /// Get the post-condition of an implementation.
+ /// </summary>
+ /// <param name="impl"></param>
+ private static CmdSeq! GetPost(Implementation! impl)
+ requires impl.Proc != null;
+ {
+ if (CommandLineOptions.Clo.PrintWithUniqueASTIds) { Console.WriteLine("Effective postcondition:"); }
+
+ // Construct an Expr for the post-condition
+ Substitution formalProcImplSubst = Substituter.SubstitutionFromHashtable(impl.GetImplFormalMap());
+ CmdSeq! post = new CmdSeq();
+ foreach (Ensures! ens in impl.Proc.Ensures)
+ {
+ if (!ens.Free) {
+ Expr! e = Substituter.Apply(formalProcImplSubst, ens.Condition);
+ Ensures! ensCopy = (Ensures!) ens.Clone();
+ ensCopy.Condition = e;
+ Cmd! c = new AssertEnsuresCmd(ensCopy);
+ ((AssertEnsuresCmd) c).ErrorDataEnhanced = ensCopy.ErrorDataEnhanced;
+ post.Add(c);
+
+ if (CommandLineOptions.Clo.PrintWithUniqueASTIds) { c.Emit(new TokenTextWriter("<console>", Console.Out, false), 1); }
+ }
+ }
+
+ if (CommandLineOptions.Clo.PrintWithUniqueASTIds) { Console.WriteLine(); }
+
+ return post;
+ }
+
+
+ #region Soundness smoke tester
+ class SmokeTester
+ {
+ VCGen! parent;
+ Implementation! impl;
+ Block! initial;
+ Program! program;
+ int id;
+ Dictionary<Block!, Block!>! copies = new Dictionary<Block!, Block!>();
+ Dictionary<Block!, bool>! visited = new Dictionary<Block!, bool>();
+ VerifierCallback! callback;
+
+ internal SmokeTester(VCGen! par, Implementation! i, Program! prog, VerifierCallback! callback)
+ {
+ parent = par;
+ impl = i;
+ initial = i.Blocks[0];
+ program = prog;
+ this.callback = callback;
+ }
+
+ internal void Copy()
+ {
+ CloneBlock(impl.Blocks[0]);
+ initial = GetCopiedBlocks()[0];
+ }
+
+ internal void Test()
+ throws UnexpectedProverOutputException;
+ {
+ DFS(initial);
+ }
+
+ void TopologicalSortImpl()
+ {
+ Graph<Block> dag = new Graph<Block>();
+ dag.AddSource((!)impl.Blocks[0]); // there is always at least one node in the graph
+ foreach (Block b in impl.Blocks)
+ {
+ GotoCmd gtc = b.TransferCmd as GotoCmd;
+ if (gtc != null)
+ {
+ assume gtc.labelTargets != null;
+ foreach (Block! dest in gtc.labelTargets)
+ {
+ dag.AddEdge(b,dest);
+ }
+ }
+ }
+ impl.Blocks = new List<Block!>();
+ foreach (Block! b in dag.TopologicalSort()) {
+ impl.Blocks.Add(b);
+ }
+ }
+
+ void Emit()
+ {
+ TopologicalSortImpl();
+ EmitImpl(impl, false);
+ }
+
+ // this one copies forward
+ Block! CloneBlock(Block! b)
+ {
+ Block fake_res;
+ if (copies.TryGetValue(b, out fake_res)) {
+ return (!)fake_res;
+ }
+ Block! res;
+ res = new Block(b.tok, b.Label, new CmdSeq(b.Cmds), null);
+ copies[b] = res;
+ if (b.TransferCmd is GotoCmd) {
+ foreach (Block! ch in (!)((GotoCmd)b.TransferCmd).labelTargets) {
+ CloneBlock(ch);
+ }
+ }
+ foreach (Block! p in b.Predecessors) {
+ res.Predecessors.Add(CloneBlock(p));
+ }
+ return res;
+ }
+
+ // this one copies backwards
+ Block! CopyBlock(Block! b)
+ {
+ Block fake_res;
+ if (copies.TryGetValue(b, out fake_res)) {
+ // fake_res should be Block! but the compiler fails
+ return (!)fake_res;
+ }
+ Block! res;
+ CmdSeq seq = new CmdSeq();
+ foreach (Cmd! c in b.Cmds) {
+ AssertCmd turn = c as AssertCmd;
+ if (!turnAssertIntoAssumes || turn == null) {
+ seq.Add(c);
+ } else {
+ seq.Add(AssertTurnedIntoAssume(turn));
+ }
+ }
+ res = new Block(b.tok, b.Label, seq, null);
+ copies[b] = res;
+ foreach (Block! p in b.Predecessors) {
+ res.Predecessors.Add(CopyBlock(p));
+ }
+ return res;
+ }
+
+ List<Block!>! GetCopiedBlocks()
+ {
+ // the order of nodes in res is random (except for the first one, being the entry)
+ List<Block!> res = new List<Block!>();
+ res.Add(copies[initial]);
+
+ foreach (KeyValuePair<Block!,Block!> kv in copies) {
+ GotoCmd go = kv.Key.TransferCmd as GotoCmd;
+ ReturnCmd ret = kv.Key.TransferCmd as ReturnCmd;
+ if (kv.Key != initial) {
+ res.Add(kv.Value);
+ }
+ if (go != null) {
+ GotoCmd copy = new GotoCmd(go.tok, new StringSeq(), new BlockSeq());
+ kv.Value.TransferCmd = copy;
+ foreach (Block! b in (!)go.labelTargets) {
+ Block c;
+ if (copies.TryGetValue(b, out c)) {
+ copy.AddTarget((!)c);
+ }
+ }
+ } else if (ret != null) {
+ kv.Value.TransferCmd = ret;
+ } else {
+ assume false;
+ }
+ }
+
+ copies.Clear();
+
+ return res;
+ }
+
+ // check if e is true, false, !true, !false
+ // if so return true and the value of the expression in val
+ bool BooleanEval(Expr! e, ref bool val)
+ {
+ LiteralExpr lit = e as LiteralExpr;
+ NAryExpr call = e as NAryExpr;
+
+ if (lit != null && lit.isBool) {
+ val = lit.asBool;
+ return true;
+ } else if (call != null &&
+ call.Fun is UnaryOperator &&
+ ((UnaryOperator)call.Fun).Op == UnaryOperator.Opcode.Not &&
+ BooleanEval((!)call.Args[0], ref val)) {
+ val = !val;
+ return true;
+ }
+ // this is for the 0bv32 != 0bv32 generated by vcc
+ else if (call != null &&
+ call.Fun is BinaryOperator &&
+ ((BinaryOperator)call.Fun).Op == BinaryOperator.Opcode.Neq &&
+ call.Args[0] is LiteralExpr &&
+ ((!)call.Args[0]).Equals(call.Args[1]))
+ {
+ val = false;
+ return true;
+ }
+
+ return false;
+ }
+
+ bool IsFalse(Expr! e)
+ {
+ bool val = false;
+ return BooleanEval(e, ref val) && !val;
+ }
+
+ bool CheckUnreachable(Block! cur, CmdSeq! seq)
+ throws UnexpectedProverOutputException;
+ {
+ DateTime start = DateTime.Now;
+ if (CommandLineOptions.Clo.Trace) {
+ System.Console.Write(" soundness smoke test #{0} ... ", id);
+ }
+ callback.OnProgress("smoke", id, id, 0.0);
+
+ Token tok = new Token();
+ tok.val = "soundness smoke test assertion";
+ seq.Add(new AssertCmd(tok, Expr.False));
+ Block! copy = CopyBlock(cur);
+ copy.Cmds = seq;
+ List<Block!>! backup = impl.Blocks;
+ impl.Blocks = GetCopiedBlocks();
+ copy.TransferCmd = new ReturnCmd(Token.NoToken);
+ if (CommandLineOptions.Clo.TraceVerify) {
+ System.Console.WriteLine();
+ System.Console.WriteLine(" --- smoke #{0}, before passify", id);
+ Emit();
+ }
+ parent.current_impl = impl;
+ parent.PassifyImpl(impl, program);
+ Hashtable! label2Absy;
+ Checker! ch = parent.FindCheckerFor(impl, CommandLineOptions.Clo.SmokeTimeout);
+ VCExpr! vc = parent.GenerateVC(impl, out label2Absy, ch);
+ impl.Blocks = backup;
+
+ if (CommandLineOptions.Clo.TraceVerify) {
+ System.Console.WriteLine(" --- smoke #{0}, after passify", id);
+ Emit();
+ }
+ ch.BeginCheck((!) impl.Name + "_smoke" + id++, vc, new ErrorHandler(label2Absy));
+ ch.ProverDone.WaitOne();
+ ProverInterface.Outcome outcome = ch.ReadOutcome();
+ parent.current_impl = null;
+
+ DateTime end = DateTime.Now;
+ TimeSpan elapsed = end - start;
+ if (CommandLineOptions.Clo.Trace) {
+ System.Console.WriteLine(" [{0} s] {1}", elapsed.TotalSeconds,
+ outcome == ProverInterface.Outcome.Valid ? "OOPS" :
+ "OK" + (outcome == ProverInterface.Outcome.Invalid ? "" : " (" + outcome + ")"));
+ }
+
+ if (outcome == ProverInterface.Outcome.Valid) {
+ // copy it again, so we get the version with calls, assignments and such
+ copy = CopyBlock(cur);
+ copy.Cmds = seq;
+ impl.Blocks = GetCopiedBlocks();
+ TopologicalSortImpl();
+ callback.OnUnreachableCode(impl);
+ impl.Blocks = backup;
+ return true;
+ }
+ return false;
+ }
+
+ const bool turnAssertIntoAssumes = false;
+
+ void DFS(Block! cur)
+ throws UnexpectedProverOutputException;
+ {
+ if (visited.ContainsKey(cur)) return;
+ visited[cur] = true;
+
+ CmdSeq! seq = new CmdSeq();
+ foreach (Cmd! cmd_ in cur.Cmds) {
+ Cmd! cmd = cmd_;
+ AssertCmd assrt = cmd as AssertCmd;
+ AssumeCmd assm = cmd as AssumeCmd;
+ CallCmd call = cmd as CallCmd;
+
+ bool assumeFalse = false;
+
+ if (assrt != null) {
+ // we're not going any further
+ // it's clear the user expected unreachable code here
+ // it's not clear where did he expect it, maybe it would be right to insert
+ // a check just one command before
+ if (IsFalse(assrt.Expr)) return;
+
+ if (turnAssertIntoAssumes) {
+ cmd = AssertTurnedIntoAssume(assrt);
+ }
+ } else if (assm != null) {
+ if (IsFalse(assm.Expr)) assumeFalse = true;
+ } else if (call != null) {
+ foreach (Ensures! e in ((!)call.Proc).Ensures) {
+ if (IsFalse(e.Condition)) assumeFalse = true;
+ }
+ }
+
+ if (assumeFalse) {
+ CheckUnreachable(cur, seq);
+ return;
+ }
+
+ seq.Add(cmd);
+ }
+
+
+ GotoCmd go = cur.TransferCmd as GotoCmd;
+ ReturnCmd ret = cur.TransferCmd as ReturnCmd;
+
+ assume !(go!= null&&go.labelTargets==null&&go.labelNames!=null&&go.labelNames.Length>0) ;
+
+ if (ret != null || (go != null && ((!)go.labelTargets).Length == 0)) {
+ // we end in return, so there will be no more places to check
+ CheckUnreachable(cur, seq);
+ } else if (go != null) {
+ bool needToCheck = true;
+ // if all of our children have more than one parent, then
+ // we're in the right place to check
+ foreach (Block! target in (!)go.labelTargets) {
+ if (target.Predecessors.Length == 1) {
+ needToCheck = false;
+ }
+ }
+ if (needToCheck) {
+ CheckUnreachable(cur, seq);
+ }
+ foreach (Block! target in go.labelTargets) {
+ DFS(target);
+ }
+ }
+ }
+
+ class ErrorHandler : ProverInterface.ErrorHandler {
+ Hashtable! label2Absy;
+
+ public ErrorHandler(Hashtable! label2Absy) {
+ this.label2Absy = label2Absy;
+ }
+
+ public override Absy! Label2Absy(string! label) {
+ int id = int.Parse(label);
+ return (Absy!) label2Absy[id];
+ }
+ }
+ }
+
+
+ #endregion
+
+ #region Splitter
+ class Split
+ {
+ class BlockStats {
+ public bool big_block;
+ public int id;
+ public double assertion_cost;
+ public double assumption_cost; // before multiplier
+ public double incomming_paths;
+ public List<Block!>! virtual_successors = new List<Block!>();
+ public List<Block!>! virtual_predecesors = new List<Block!>();
+ public Dictionary<Block!,bool>? reachable_blocks;
+ public readonly Block! block;
+
+ public BlockStats(Block! b, int i)
+ {
+ block = b;
+ assertion_cost = -1;
+ id = i;
+ }
+ }
+
+ readonly List<Block!>! blocks;
+ readonly List<Block!>! big_blocks = new List<Block!>();
+ readonly Dictionary<Block!, BlockStats!>! stats = new Dictionary<Block!, BlockStats!>();
+ readonly int id;
+ static int current_id;
+ Block? split_block;
+ bool assert_to_assume;
+ List<Block!>! assumized_branches = new List<Block!>();
+ public AssertCmd? first_assert;
+
+ double score;
+ bool score_computed;
+ double total_cost;
+ int assertion_count;
+ double assertion_cost; // without multiplication by paths
+ Hashtable/*TransferCmd->ReturnCmd*/! gotoCmdOrigins;
+ VCGen! parent;
+ Implementation! impl;
+
+ Dictionary<Block!, Block!>! copies = new Dictionary<Block!, Block!>();
+ bool doing_slice;
+ double slice_initial_limit;
+ double slice_limit;
+ bool slice_pos;
+ Dictionary<Block!, bool>! protected_from_assert_to_assume = new Dictionary<Block!,bool>();
+ Dictionary<Block!, bool>! keep_at_all = new Dictionary<Block!,bool>();
+
+ // async interface
+ private Checker checker;
+ private int splitNo;
+ internal ErrorReporter reporter;
+
+ public Split(List<Block!>! blocks, Hashtable/*TransferCmd->ReturnCmd*/! gotoCmdOrigins, VCGen! par, Implementation! impl)
+ {
+ this.blocks = blocks;
+ this.gotoCmdOrigins = gotoCmdOrigins;
+ this.parent = par;
+ this.impl = impl;
+ this.id = current_id++;
+ }
+
+ public double Cost
+ {
+ get {
+ ComputeBestSplit();
+ return total_cost;
+ }
+ }
+
+ public bool LastChance
+ {
+ get {
+ ComputeBestSplit();
+ return assertion_count == 1 && score < 0;
+ }
+ }
+
+ public string Stats
+ {
+ get {
+ ComputeBestSplit();
+ return string.Format("(cost:{0:0}/{1:0}{2})", total_cost, assertion_cost, LastChance ? " last" : "");
+ }
+ }
+
+ public void DumpDot(int no)
+ {
+ using (System.IO.StreamWriter sw = System.IO.File.CreateText(string.Format("split.{0}.dot", no))) {
+ sw.WriteLine("digraph G {");
+
+ ComputeBestSplit();
+ List<Block!> saved = assumized_branches;
+ assumized_branches = new List<Block!>();
+ DoComputeScore(false);
+ assumized_branches = saved;
+
+ foreach (Block! b in big_blocks) {
+ BlockStats s = GetBlockStats(b);
+ foreach (Block! t in s.virtual_successors) {
+ sw.WriteLine("n{0} -> n{1};", s.id, GetBlockStats(t).id);
+ }
+ sw.WriteLine("n{0} [label=\"{1}:\\n({2:0.0}+{3:0.0})*{4:0.0}\"{5}];",
+ s.id, b.Label,
+ s.assertion_cost, s.assumption_cost, s.incomming_paths,
+ s.assertion_cost > 0 ? ",shape=box" : "");
+
+ }
+ sw.WriteLine("}");
+ sw.Close();
+ }
+
+ string filename = string.Format("split.{0}.bpl", no);
+ using (System.IO.StreamWriter sw = System.IO.File.CreateText(filename)) {
+ int oldPrintUnstructured = CommandLineOptions.Clo.PrintUnstructured;
+ CommandLineOptions.Clo.PrintUnstructured = 2; // print only the unstructured program
+ bool oldPrintDesugaringSetting = CommandLineOptions.Clo.PrintDesugarings;
+ CommandLineOptions.Clo.PrintDesugarings = false;
+ List<Block!> backup = impl.Blocks;
+ impl.Blocks = blocks;
+ impl.Emit(new TokenTextWriter(filename, sw, false), 0);
+ impl.Blocks = backup;
+ CommandLineOptions.Clo.PrintDesugarings = oldPrintDesugaringSetting;
+ CommandLineOptions.Clo.PrintUnstructured = oldPrintUnstructured;
+ }
+ }
+
+ int bsid;
+ BlockStats! GetBlockStats(Block! b)
+ {
+ BlockStats s;
+ if (!stats.TryGetValue(b, out s)) {
+ s = new BlockStats(b, bsid++);
+ stats[b] = s;
+ }
+ return (!)s;
+ }
+
+ double AssertionCost(PredicateCmd c)
+ {
+ return 1.0;
+ }
+
+ void CountAssertions(Block! b)
+ {
+ BlockStats s = GetBlockStats(b);
+ if (s.assertion_cost >= 0) return; // already done
+ s.big_block = true;
+ s.assertion_cost = 0;
+ s.assumption_cost = 0;
+ foreach (Cmd c in b.Cmds) {
+ if (c is AssertCmd) {
+ double cost = AssertionCost((AssertCmd)c);
+ s.assertion_cost += cost;
+ assertion_count++;
+ assertion_cost += cost;
+ } else if (c is AssumeCmd) {
+ s.assumption_cost += AssertionCost((AssumeCmd)c);
+ }
+ }
+ foreach (Block! b in Exits(b)) {
+ s.virtual_successors.Add(b);
+ }
+ if (s.virtual_successors.Count == 1) {
+ Block next = s.virtual_successors[0];
+ BlockStats se = GetBlockStats(next);
+ CountAssertions(next);
+ if (next.Predecessors.Length > 1 || se.virtual_successors.Count != 1) return;
+ s.virtual_successors[0] = se.virtual_successors[0];
+ s.assertion_cost += se.assertion_cost;
+ s.assumption_cost += se.assumption_cost;
+ se.big_block = false;
+ }
+ }
+
+ Dictionary<Block!,bool>! ComputeReachableNodes(Block! b)
+ {
+ BlockStats s = GetBlockStats(b);
+ if (s.reachable_blocks != null) {
+ return s.reachable_blocks;
+ }
+ Dictionary<Block!, bool> blocks = new Dictionary<Block!, bool>();
+ s.reachable_blocks = blocks;
+ blocks[b] = true;
+ foreach (Block! succ in Exits(b)) {
+ foreach (Block! r in ComputeReachableNodes(succ).Keys) {
+ blocks[r] = true;
+ }
+ }
+ return blocks;
+ }
+
+ double ProverCost(double vc_cost)
+ {
+ return vc_cost * vc_cost;
+ }
+
+ void ComputeBestSplit()
+ {
+ if (score_computed) return;
+ score_computed = true;
+
+ assertion_count = 0;
+
+ foreach (Block! b in blocks) {
+ CountAssertions(b);
+ }
+
+ foreach (Block! b in blocks) {
+ BlockStats bs = GetBlockStats(b);
+ if (bs.big_block) {
+ big_blocks.Add(b);
+ foreach (Block! ch in bs.virtual_successors) {
+ BlockStats chs = GetBlockStats(ch);
+ if (!chs.big_block) {
+ Console.WriteLine("non-big {0} accessed from {1}", ch, b);
+ DumpDot(-1);
+ assert false;
+ }
+ chs.virtual_predecesors.Add(b);
+ }
+ }
+ }
+
+ assumized_branches.Clear();
+ total_cost = ProverCost(DoComputeScore(false));
+
+ score = double.PositiveInfinity;
+ Block? best_split = null;
+ List<Block!>! saved_branches = new List<Block!>();
+
+ foreach (Block! b in big_blocks) {
+ GotoCmd gt = b.TransferCmd as GotoCmd;
+ if (gt == null) continue;
+ BlockSeq targ = (!)gt.labelTargets;
+ if (targ.Length < 2) continue;
+ // caution, we only consider two first exits
+
+ double left0, right0, left1, right1;
+ split_block = b;
+
+ assumized_branches.Clear();
+ assumized_branches.Add((!)targ[0]);
+ left0 = DoComputeScore(true);
+ right0 = DoComputeScore(false);
+
+ assumized_branches.Clear();
+ for (int idx = 1; idx < targ.Length; idx++) {
+ assumized_branches.Add((!)targ[idx]);
+ }
+ left1 = DoComputeScore(true);
+ right1 = DoComputeScore(false);
+
+ double current_score = ProverCost(left1) + ProverCost(right1);
+ double other_score = ProverCost(left0) + ProverCost(right0);
+
+ if (other_score < current_score) {
+ current_score = other_score;
+ assumized_branches.Clear();
+ assumized_branches.Add((!)targ[0]);
+ }
+
+ if (current_score < score) {
+ score = current_score;
+ best_split = split_block;
+ saved_branches.Clear();
+ saved_branches.AddRange(assumized_branches);
+ }
+ }
+
+ if (CommandLineOptions.Clo.VcsPathSplitMult * score > total_cost) {
+ split_block = null;
+ score = -1;
+ } else {
+ assumized_branches = saved_branches;
+ split_block = best_split;
+ }
+ }
+
+ void UpdateIncommingPaths(BlockStats! s)
+ {
+ if (s.incomming_paths < 0.0) {
+ int count = 0;
+ s.incomming_paths = 0.0;
+ if (!keep_at_all.ContainsKey(s.block)) return;
+ foreach (Block! b in s.virtual_predecesors) {
+ BlockStats! ch = GetBlockStats(b);
+ UpdateIncommingPaths(ch);
+ if (ch.incomming_paths > 0.0) {
+ s.incomming_paths += ch.incomming_paths;
+ count++;
+ }
+ }
+ if (count > 1) {
+ s.incomming_paths *= CommandLineOptions.Clo.VcsPathJoinMult;
+ }
+ }
+ }
+
+ void ComputeBlockSetsHelper(Block! b, bool allow_small)
+ {
+ if (keep_at_all.ContainsKey(b)) return;
+ keep_at_all[b] = true;
+
+ if (allow_small) {
+ foreach (Block! ch in Exits(b)) {
+ if (b == split_block && assumized_branches.Contains(ch)) continue;
+ ComputeBlockSetsHelper(ch, allow_small);
+ }
+ } else {
+ foreach (Block! ch in GetBlockStats(b).virtual_successors) {
+ if (b == split_block && assumized_branches.Contains(ch)) continue;
+ ComputeBlockSetsHelper(ch, allow_small);
+ }
+ }
+ }
+
+ void ComputeBlockSets(bool allow_small)
+ {
+ protected_from_assert_to_assume.Clear();
+ keep_at_all.Clear();
+
+ Debug.Assert(split_block == null || GetBlockStats(split_block).big_block);
+ Debug.Assert(GetBlockStats(blocks[0]).big_block);
+
+ if (assert_to_assume) {
+ foreach (Block! b in allow_small ? blocks : big_blocks) {
+ if (ComputeReachableNodes(b).ContainsKey((!)split_block)) {
+ keep_at_all[b] = true;
+ }
+ }
+
+ foreach (Block! b in assumized_branches) {
+ foreach (Block! r in ComputeReachableNodes(b).Keys) {
+ if (allow_small || GetBlockStats(r).big_block) {
+ keep_at_all[r] = true;
+ protected_from_assert_to_assume[r] = true;
+ }
+ }
+ }
+ } else {
+ ComputeBlockSetsHelper(blocks[0], allow_small);
+ }
+ }
+
+ bool ShouldAssumize(Block! b)
+ {
+ return assert_to_assume && !protected_from_assert_to_assume.ContainsKey(b);
+ }
+
+ double DoComputeScore(bool aa)
+ {
+ assert_to_assume = aa;
+ ComputeBlockSets(false);
+
+ foreach (Block! b in big_blocks) {
+ GetBlockStats(b).incomming_paths = -1.0;
+ }
+
+ GetBlockStats(blocks[0]).incomming_paths = 1.0;
+
+ double cost = 0.0;
+ foreach (Block! b in big_blocks) {
+ if (keep_at_all.ContainsKey(b)) {
+ BlockStats s = GetBlockStats(b);
+ UpdateIncommingPaths(s);
+ double local = s.assertion_cost;
+ if (ShouldAssumize(b)) {
+ local = (s.assertion_cost + s.assumption_cost) * CommandLineOptions.Clo.VcsAssumeMult;
+ } else {
+ local = s.assumption_cost * CommandLineOptions.Clo.VcsAssumeMult + s.assertion_cost;
+ }
+ local = local + local * s.incomming_paths * CommandLineOptions.Clo.VcsPathCostMult;
+ cost += local;
+ }
+ }
+
+ return cost;
+ }
+
+ CmdSeq! SliceCmds(Block! b)
+ {
+ CmdSeq! seq = b.Cmds;
+ if (!doing_slice && !ShouldAssumize(b)) return seq;
+ CmdSeq! res = new CmdSeq();
+ foreach (Cmd! c in seq) {
+ AssertCmd a = c as AssertCmd;
+ Cmd! the_new = c;
+ bool swap = false;
+ if (a != null) {
+ if (doing_slice) {
+ double cost = AssertionCost(a);
+ bool first = (slice_limit - cost) >= 0 || slice_initial_limit == slice_limit;
+ slice_limit -= cost;
+ swap = slice_pos == first;
+ } else if (assert_to_assume) {
+ swap = true;
+ } else {
+ assert false;
+ }
+
+ if (swap) {
+ the_new = AssertTurnedIntoAssume(a);
+ }
+ }
+ res.Add(the_new);
+ }
+ return res;
+ }
+
+ Block! CloneBlock(Block! b)
+ {
+ Block res;
+ if (copies.TryGetValue(b, out res)) {
+ return (!)res;
+ }
+ res = new Block(b.tok, b.Label, SliceCmds(b), b.TransferCmd);
+ GotoCmd gt = b.TransferCmd as GotoCmd;
+ copies[b] = res;
+ if (gt != null) {
+ GotoCmd newGoto = new GotoCmd(gt.tok, new StringSeq(), new BlockSeq());
+ res.TransferCmd = newGoto;
+ int pos = 0;
+ foreach (Block! ch in (!)gt.labelTargets) {
+ assert doing_slice ||
+ (!assert_to_assume ==> (keep_at_all.ContainsKey(ch) || assumized_branches.Contains(ch)));
+ if (doing_slice ||
+ ((b != split_block || assumized_branches.Contains(ch) == assert_to_assume) &&
+ keep_at_all.ContainsKey(ch))) {
+ newGoto.AddTarget(CloneBlock(ch));
+ }
+ pos++;
+ }
+ }
+ return res;
+ }
+
+ Split! DoSplit()
+ {
+ copies.Clear();
+ CloneBlock(blocks[0]);
+ List<Block!> newBlocks = new List<Block!>();
+ Hashtable newGotoCmdOrigins = new Hashtable();
+ foreach (Block! b in blocks) {
+ Block tmp;
+ if (copies.TryGetValue(b, out tmp)) {
+ newBlocks.Add((!)tmp);
+ if (gotoCmdOrigins.ContainsKey(b)) {
+ newGotoCmdOrigins[tmp] = gotoCmdOrigins[b];
+ }
+
+ foreach (Block! p in b.Predecessors) {
+ Block tmp2;
+ if (copies.TryGetValue(p, out tmp2)) {
+ tmp.Predecessors.Add(tmp2);
+ }
+ }
+ }
+ }
+
+ return new Split(newBlocks, newGotoCmdOrigins, parent, impl);
+ }
+
+ Split! SplitAt(int idx)
+ {
+ assert_to_assume = idx == 0;
+ doing_slice = false;
+ ComputeBlockSets(true);
+
+ return DoSplit();
+ }
+
+ Split! SliceAsserts(double limit, bool pos)
+ {
+ slice_pos = pos;
+ slice_limit = limit;
+ slice_initial_limit = limit;
+ doing_slice = true;
+ Split! r = DoSplit();
+
+ /*
+ Console.WriteLine("split {0} / {1} -->", limit, pos);
+ List<Block!> tmp = impl.Blocks;
+ impl.Blocks = r.blocks;
+ EmitImpl(impl, false);
+ impl.Blocks = tmp;
+ */
+
+ return r;
+ }
+
+ void Print()
+ {
+ List<Block!> tmp = impl.Blocks;
+ impl.Blocks = blocks;
+ EmitImpl(impl, false);
+ impl.Blocks = tmp;
+ }
+
+ public Counterexample! ToCounterexample()
+ {
+ BlockSeq trace = new BlockSeq();
+ foreach (Block! b in blocks) {
+ trace.Add(b);
+ }
+ foreach (Block! b in blocks) {
+ foreach (Cmd! c in b.Cmds) {
+ if (c is AssertCmd) {
+ return AssertCmdToCounterexample((AssertCmd)c, (!)b.TransferCmd, trace, null, new Dictionary<Incarnation, Absy!>());
+ }
+ }
+ }
+ assume false;
+ }
+
+ public static List<Split!>! DoSplit(Split! initial, double max_cost, int max)
+ {
+ List<Split!> res = new List<Split!>();
+ res.Add(initial);
+
+ while (res.Count < max) {
+ Split best = null;
+ int best_idx = 0, pos = 0;
+ foreach (Split! s in res) {
+ s.ComputeBestSplit(); // TODO check total_cost first
+ if (s.total_cost > max_cost &&
+ (best == null || best.total_cost < s.total_cost) &&
+ (s.assertion_count > 1 || s.split_block != null)) {
+ best = s;
+ best_idx = pos;
+ }
+ pos++;
+ }
+
+ if (best == null) break; // no split found
+
+ Split! s0, s1;
+
+ bool split_stats = CommandLineOptions.Clo.TraceVerify;
+
+ if (split_stats) {
+ Console.WriteLine("{0} {1} -->", best.split_block == null ? "SLICE" : ("SPLIT@" + best.split_block.Label), best.Stats);
+ if (best.split_block != null) {
+ GotoCmd g = best.split_block.TransferCmd as GotoCmd;
+ if (g != null) {
+ Console.Write(" exits: ");
+ foreach (Block! b in (!)g.labelTargets) {
+ Console.Write("{0} ", b.Label);
+ }
+ Console.WriteLine("");
+ Console.Write(" assumized: ");
+ foreach (Block! b in best.assumized_branches) {
+ Console.Write("{0} ", b.Label);
+ }
+ Console.WriteLine("");
+ }
+ }
+ }
+
+ if (best.split_block != null) {
+ s0 = best.SplitAt(0);
+ s1 = best.SplitAt(1);
+ } else {
+ best.split_block = null;
+ s0 = best.SliceAsserts(best.assertion_cost / 2, true);
+ s1 = best.SliceAsserts(best.assertion_cost / 2, false);
+ }
+
+ if (true) {
+ List<Block!> ss = new List<Block!>();
+ ss.Add(s0.blocks[0]);
+ ss.Add(s1.blocks[0]);
+ try {
+ best.SoundnessCheck(new Dictionary<PureCollections.Tuple!, bool>(), best.blocks[0], ss);
+ } catch (System.Exception e) {
+ Console.WriteLine(e);
+ best.DumpDot(-1);
+ s0.DumpDot(-2);
+ s1.DumpDot(-3);
+ assert false;
+ }
+ }
+
+ if (split_stats) {
+ s0.ComputeBestSplit();
+ s1.ComputeBestSplit();
+ Console.WriteLine(" --> {0}", s0.Stats);
+ Console.WriteLine(" --> {0}", s1.Stats);
+ }
+
+ if (CommandLineOptions.Clo.TraceVerify) {
+ best.Print();
+ }
+
+ res[best_idx] = s0;
+ res.Add(s1);
+ }
+
+ return res;
+ }
+
+ public Checker! Checker
+ {
+ get {
+ assert checker != null;
+ return checker;
+ }
+ }
+
+ public WaitHandle ProverDone
+ {
+ get {
+ assert checker != null;
+ return checker.ProverDone;
+ }
+ }
+
+ public void ReadOutcome(ref Outcome cur_outcome, out bool prover_failed)
+ throws UnexpectedProverOutputException;
+ {
+ ProverInterface.Outcome outcome = ((!)checker).ReadOutcome();
+
+ if (CommandLineOptions.Clo.Trace && splitNo >= 0) {
+ System.Console.WriteLine(" --> split #{0} done, [{1} s] {2}", splitNo, checker.ProverRunTime.TotalSeconds, outcome);
+ }
+
+ if (CommandLineOptions.Clo.VcsDumpSplits) {
+ DumpDot(splitNo);
+ }
+
+ prover_failed = false;
+
+ switch (outcome) {
+ case ProverInterface.Outcome.Valid:
+ return;
+ case ProverInterface.Outcome.Invalid:
+ cur_outcome = Outcome.Errors;
+ return;
+ case ProverInterface.Outcome.OutOfMemory:
+ prover_failed = true;
+ if (cur_outcome != Outcome.Errors && cur_outcome != Outcome.Inconclusive)
+ cur_outcome = Outcome.OutOfMemory;
+ return;
+ case ProverInterface.Outcome.TimeOut:
+ prover_failed = true;
+ if (cur_outcome != Outcome.Errors && cur_outcome != Outcome.Inconclusive)
+ cur_outcome = Outcome.TimedOut;
+ return;
+ case ProverInterface.Outcome.Undetermined:
+ prover_failed = true;
+ if (cur_outcome != Outcome.Errors)
+ cur_outcome = Outcome.Inconclusive;
+ return;
+ default:
+ assert false;
+ }
+ }
+
+ public void BeginCheck(VerifierCallback! callback, int no, int timeout)
+ {
+ splitNo = no;
+
+ impl.Blocks = blocks;
+
+ checker = parent.FindCheckerFor(impl, timeout);
+
+ Hashtable/*<int, Absy!>*/! label2absy;
+ VCExpr! vc = parent.GenerateVC(impl, out label2absy, checker);
+
+ if (CommandLineOptions.Clo.vcVariety == CommandLineOptions.VCVariety.Local) {
+ reporter = new ErrorReporterLocal(gotoCmdOrigins, label2absy, impl.Blocks, parent.incarnationOriginMap, callback);
+ } else {
+ reporter = new ErrorReporter(gotoCmdOrigins, label2absy, impl.Blocks, parent.incarnationOriginMap, callback);
+ }
+
+ if (CommandLineOptions.Clo.TraceVerify && no >= 0)
+ {
+ Console.WriteLine("-- after split #{0}", no);
+ Print();
+ }
+
+ string! desc = (!) impl.Name;
+ if (no >= 0)
+ desc += "_split" + no;
+ checker.BeginCheck(desc, vc, reporter);
+
+ }
+
+ private void SoundnessCheck(Dictionary<PureCollections.Tuple!, bool>! cache, Block! orig, List<Block!>! copies)
+ {
+ {
+ PureCollections.Tuple t = new PureCollections.Tuple(new PureCollections.Capacity(1 + copies.Count));
+ int i = 0;
+ t[i++] = orig;
+ foreach (Block! b in copies) {
+ t[i++] = b;
+ }
+ if (cache.ContainsKey(t)) { return; }
+ cache[t] = true;
+ }
+
+ for (int i = 0; i < orig.Cmds.Length; ++i) {
+ Cmd cmd = orig.Cmds[i];
+ if (cmd is AssertCmd) {
+ int found = 0;
+ foreach (Block! c in copies) {
+ if (c.Cmds[i] == cmd) {
+ found++;
+ }
+ }
+ if (found == 0) {
+ throw new System.Exception(string.Format("missing assertion: {0}({1})", cmd.tok.filename, cmd.tok.line));
+ }
+ }
+ }
+
+ foreach (Block! exit in Exits(orig)) {
+ List<Block!>! newcopies = new List<Block!>();
+ foreach (Block! c in copies) {
+ foreach (Block! cexit in Exits(c)) {
+ if (cexit.Label == exit.Label) {
+ newcopies.Add(cexit);
+ }
+ }
+ }
+ if (newcopies.Count == 0) {
+ throw new System.Exception("missing exit " + exit.Label);
+ }
+ SoundnessCheck(cache, exit, newcopies);
+ }
+ }
+ }
+ #endregion
+
+
+ protected VCExpr! GenerateVC(Implementation! impl, out Hashtable/*<int, Absy!>*/! label2absy, Checker! ch)
+ {
+ TypecheckingContext tc = new TypecheckingContext(null);
+ impl.Typecheck(tc);
+
+ label2absy = new Hashtable/*<int, Absy!>*/();
+ VCExpr! vc;
+ switch (CommandLineOptions.Clo.vcVariety) {
+ case CommandLineOptions.VCVariety.Structured:
+ vc = VCViaStructuredProgram(impl, label2absy, ch.TheoremProver.Context);
+ break;
+ case CommandLineOptions.VCVariety.Block:
+ vc = FlatBlockVC(impl, label2absy, false, false, false, ch.TheoremProver.Context);
+ break;
+ case CommandLineOptions.VCVariety.BlockReach:
+ vc = FlatBlockVC(impl, label2absy, false, true, false, ch.TheoremProver.Context);
+ break;
+ case CommandLineOptions.VCVariety.Local:
+ vc = FlatBlockVC(impl, label2absy, true, false, false, ch.TheoremProver.Context);
+ break;
+ case CommandLineOptions.VCVariety.BlockNested:
+ vc = NestedBlockVC(impl, label2absy, false, ch.TheoremProver.Context);
+ break;
+ case CommandLineOptions.VCVariety.BlockNestedReach:
+ vc = NestedBlockVC(impl, label2absy, true, ch.TheoremProver.Context);
+ break;
+ case CommandLineOptions.VCVariety.Dag:
+ if (((!)CommandLineOptions.Clo.TheProverFactory).SupportsDags) {
+ vc = DagVC((!)impl.Blocks[0], label2absy, new Hashtable/*<Block, VCExpr!>*/(), ch.TheoremProver.Context);
+ } else {
+ vc = LetVC((!)impl.Blocks[0], label2absy, ch.TheoremProver.Context);
+ }
+ break;
+ case CommandLineOptions.VCVariety.Doomed:
+ vc = FlatBlockVC(impl, label2absy, false, false, true, ch.TheoremProver.Context);
+ break;
+ default:
+ assert false; // unexpected enumeration value
+ }
+ return vc;
+ }
+
+ void CheckIntAttributeOnImpl(Implementation! impl, string! name, ref int val)
+ {
+ if (!((!)impl.Proc).CheckIntAttribute(name, ref val) || !impl.CheckIntAttribute(name, ref val)) {
+ Console.WriteLine("ignoring ill-formed {:{0} ...} attribute on {1}, parameter should be an int", name, impl.Name);
+ }
+ }
+
+
+ public override Outcome VerifyImplementation(Implementation! impl, Program! program, VerifierCallback! callback)
+ throws UnexpectedProverOutputException;
+ {
+ if (impl.SkipVerification) {
+ return Outcome.Inconclusive; // not sure about this one
+ }
+
+ callback.OnProgress("VCgen", 0, 0, 0.0);
+
+ ConvertCFG2DAG(impl, program);
+
+ SmokeTester smoke_tester = null;
+ if (CommandLineOptions.Clo.SoundnessSmokeTest) {
+ smoke_tester = new SmokeTester(this, impl, program, callback);
+ smoke_tester.Copy();
+ }
+
+ Hashtable/*TransferCmd->ReturnCmd*/ gotoCmdOrigins = PassifyImpl(impl, program);
+
+ double max_vc_cost = CommandLineOptions.Clo.VcsMaxCost;
+ int tmp_max_vc_cost = -1, max_splits = CommandLineOptions.Clo.VcsMaxSplits,
+ max_kg_splits = CommandLineOptions.Clo.VcsMaxKeepGoingSplits;
+ CheckIntAttributeOnImpl(impl, "vcs_max_cost", ref tmp_max_vc_cost);
+ CheckIntAttributeOnImpl(impl, "vcs_max_splits", ref max_splits);
+ CheckIntAttributeOnImpl(impl, "vcs_max_keep_going_splits", ref max_kg_splits);
+ if (tmp_max_vc_cost >= 0) {
+ max_vc_cost = tmp_max_vc_cost;
+ }
+
+ Outcome outcome = Outcome.Correct;
+
+ int cores = CommandLineOptions.Clo.VcsCores;
+ Stack<Split!> work = new Stack<Split!>();
+ List<Split!> currently_running = new List<Split!>();
+ ResetPredecessors(impl.Blocks);
+ work.Push(new Split(impl.Blocks, gotoCmdOrigins, this, impl));
+
+ bool keep_going = max_kg_splits > 1;
+ int total = 0;
+ int no = max_splits == 1 && !keep_going ? -1 : 0;
+ bool first_round = true;
+ bool do_splitting = keep_going || max_splits > 1;
+ double remaining_cost = 0.0, proven_cost = 0.0;
+
+ if (do_splitting) {
+ remaining_cost = work.Peek().Cost;
+ }
+
+ while (work.Count > 0 || currently_running.Count > 0) {
+ bool prover_failed = false;
+ Split! s;
+
+ if (work.Count > 0 && currently_running.Count < cores) {
+ s = work.Pop();
+
+ if (first_round && max_splits > 1) {
+ prover_failed = true;
+ remaining_cost -= s.Cost;
+ } else {
+ if (CommandLineOptions.Clo.Trace && no >= 0) {
+ System.Console.WriteLine(" checking split {1}/{2}, {3:0.00}%, {0} ...",
+ s.Stats, no + 1, total, 100 * proven_cost / (proven_cost + remaining_cost));
+ }
+ callback.OnProgress("VCprove", no < 0 ? 0 : no, total, proven_cost / (remaining_cost + proven_cost));
+
+ s.BeginCheck(callback, no,
+ (keep_going && s.LastChance) ? CommandLineOptions.Clo.VcsFinalAssertTimeout :
+ keep_going ? CommandLineOptions.Clo.VcsKeepGoingTimeout :
+ CommandLineOptions.Clo.ProverKillTime);
+
+ no++;
+
+ currently_running.Add(s);
+ }
+ } else {
+ WaitHandle[] handles = new WaitHandle[currently_running.Count];
+ for (int i = 0; i < currently_running.Count; ++i) {
+ handles[i] = currently_running[i].ProverDone;
+ }
+ int index = WaitHandle.WaitAny(handles);
+ s = currently_running[index];
+ currently_running.RemoveAt(index);
+
+ if (do_splitting) {
+ remaining_cost -= s.Cost;
+ }
+
+ s.ReadOutcome(ref outcome, out prover_failed);
+
+ if (do_splitting) {
+ if (prover_failed) {
+ // even if the prover fails, we have learned something, i.e., it is
+ // annoying to watch Boogie say Timeout, 0.00% a couple of times
+ proven_cost += s.Cost / 100;
+ } else {
+ proven_cost += s.Cost;
+ }
+ }
+ callback.OnProgress("VCprove", no < 0 ? 0 : no, total, proven_cost / (remaining_cost + proven_cost));
+
+ if (prover_failed && !first_round && s.LastChance) {
+ string! msg = "some timeout";
+ if (s.reporter != null && s.reporter.resourceExceededMessage != null) {
+ msg = s.reporter.resourceExceededMessage;
+ }
+ callback.OnCounterexample(s.ToCounterexample(), msg);
+ outcome = Outcome.Errors;
+ break;
+ }
+
+ assert prover_failed || outcome == Outcome.Correct || outcome == Outcome.Errors;
+ }
+
+ if (prover_failed) {
+ int splits = first_round && max_splits > 1 ? max_splits : max_kg_splits;
+
+ if (splits > 1) {
+ List<Split!> tmp = Split.DoSplit(s, max_vc_cost, splits);
+ max_vc_cost = 1.0; // for future
+ first_round = false;
+ //tmp.Sort(new Comparison<Split!>(Split.Compare));
+ foreach (Split! a in tmp) {
+ work.Push(a);
+ total++;
+ remaining_cost += a.Cost;
+ }
+ if (outcome != Outcome.Errors) {
+ outcome = Outcome.Correct;
+ }
+ } else {
+ assert outcome != Outcome.Correct;
+ if (outcome == Outcome.TimedOut) {
+ string! msg = "some timeout";
+ if (s.reporter != null && s.reporter.resourceExceededMessage != null) {
+ msg = s.reporter.resourceExceededMessage;
+ }
+ callback.OnTimeout(msg);
+ } else if (outcome == Outcome.OutOfMemory) {
+ string! msg = "out of memory";
+ if (s.reporter != null && s.reporter.resourceExceededMessage != null) {
+ msg = s.reporter.resourceExceededMessage;
+ }
+ callback.OnOutOfMemory(msg);
+ }
+
+ break;
+ }
+ }
+ }
+
+ if (outcome == Outcome.Correct && smoke_tester != null) {
+ smoke_tester.Test();
+ }
+
+ callback.OnProgress("done", 0, 0, 1.0);
+
+ return outcome;
+ }
+
+ public class ErrorReporter : ProverInterface.ErrorHandler {
+ Hashtable/*TransferCmd->ReturnCmd*/! gotoCmdOrigins;
+ Hashtable/*<int, Absy!>*/! label2absy;
+ List<Block!>! blocks;
+ protected Dictionary<Incarnation, Absy!>! incarnationOriginMap;
+ protected VerifierCallback! callback;
+ internal string? resourceExceededMessage;
+ static private System.IO.TextWriter? modelWriter;
+
+ static protected TextWriter! ModelWriter {
+ get {
+ if (ErrorReporter.modelWriter == null)
+ ErrorReporter.modelWriter = CommandLineOptions.Clo.PrintErrorModelFile == null ? Console.Out : new StreamWriter(CommandLineOptions.Clo.PrintErrorModelFile, false);
+ return ErrorReporter.modelWriter;
+ }
+ }
+
+ public ErrorReporter(Hashtable/*TransferCmd->ReturnCmd*/! gotoCmdOrigins,
+ Hashtable/*<int, Absy!>*/! label2absy,
+ List<Block!>! blocks,
+ Dictionary<Incarnation, Absy!>! incarnationOriginMap,
+ VerifierCallback! callback)
+ {
+ this.gotoCmdOrigins = gotoCmdOrigins;
+ this.label2absy = label2absy;
+ this.blocks = blocks;
+ this.incarnationOriginMap = incarnationOriginMap;
+ this.callback = callback;
+ // base();
+ }
+
+ public override void OnModel(IList<string!>! labels, ErrorModel errModel) {
+ if (CommandLineOptions.Clo.PrintErrorModel >= 1 && errModel != null) {
+ errModel.Print(ErrorReporter.ModelWriter);
+ ErrorReporter.ModelWriter.Flush();
+ }
+ Hashtable traceNodes = new Hashtable();
+ foreach (string! s in labels) {
+ Absy! absy =Label2Absy(s);
+ if (traceNodes.ContainsKey(absy))
+ System.Console.WriteLine("Warning: duplicate label: " + s + " read while tracing nodes");
+ else
+ traceNodes.Add(absy, null);
+ }
+
+ BlockSeq! trace = new BlockSeq();
+ Block! entryBlock = (!) this.blocks[0];
+ assert traceNodes.Contains(entryBlock);
+ trace.Add(entryBlock);
+
+ Counterexample newCounterexample = TraceCounterexample(entryBlock, traceNodes, trace, errModel, incarnationOriginMap);
+
+ if (newCounterexample == null) return;
+
+ #region Map passive program errors back to original program errors
+ ReturnCounterexample returnExample = newCounterexample as ReturnCounterexample;
+ if (returnExample != null)
+ {
+ foreach (Block! b in returnExample.Trace) {
+ assume b.TransferCmd != null;
+ ReturnCmd cmd = (ReturnCmd) gotoCmdOrigins[b.TransferCmd];
+ if (cmd != null)
+ {
+ returnExample.FailingReturn = cmd;
+ break;
+ }
+ }
+ }
+ #endregion
+ callback.OnCounterexample(newCounterexample, null);
+ }
+
+ public override Absy! Label2Absy(string! label)
+ {
+ int id = int.Parse(label);
+ return (Absy!) label2absy[id];
+ }
+
+ public override void OnResourceExceeded(string! msg)
+ {
+ resourceExceededMessage = msg;
+ }
+ }
+
+ public class ErrorReporterLocal : ErrorReporter {
+ public ErrorReporterLocal(Hashtable/*TransferCmd->ReturnCmd*/! gotoCmdOrigins,
+ Hashtable/*<int, Absy!>*/! label2absy,
+ List<Block!>! blocks,
+ Dictionary<Incarnation, Absy!>! incarnationOriginMap,
+ VerifierCallback! callback)
+ {
+ base(gotoCmdOrigins, label2absy, blocks, incarnationOriginMap, callback); // here for aesthetic purposes
+ }
+
+ public override void OnModel(IList<string!>! labels, ErrorModel errModel) {
+ // We ignore the error model here for enhanced error message purposes.
+ // It is only printed to the command line.
+ if (CommandLineOptions.Clo.PrintErrorModel >= 1 && errModel != null) {
+ if (CommandLineOptions.Clo.PrintErrorModelFile != null) {
+ errModel.Print(ErrorReporter.ModelWriter);
+ ErrorReporter.ModelWriter.Flush();
+ }
+ }
+ List<Block!> traceNodes = new List<Block!>();
+ List<AssertCmd!> assertNodes = new List<AssertCmd!>();
+ foreach (string! s in labels) {
+ Absy node = Label2Absy(s);
+ if (node is Block) {
+ Block b = (Block)node;
+ traceNodes.Add(b);
+ } else {
+ AssertCmd a = (AssertCmd)node;
+ assertNodes.Add(a);
+ }
+ }
+ assert assertNodes.Count > 0;
+ assert traceNodes.Count == assertNodes.Count;
+
+ foreach (AssertCmd a in assertNodes) {
+ // find the corresponding Block (assertNodes.Count is likely to be 1, or small in any case, so just do a linear search here)
+ foreach (Block b in traceNodes) {
+ if (b.Cmds.Has(a)) {
+ BlockSeq trace = new BlockSeq();
+ trace.Add(b);
+ Counterexample newCounterexample = AssertCmdToCounterexample(a, (!)b.TransferCmd, trace, errModel, incarnationOriginMap);
+ callback.OnCounterexample(newCounterexample, null);
+ goto NEXT_ASSERT;
+ }
+ }
+ assert false; // there was no block that contains the assert
+ NEXT_ASSERT: {}
+ }
+ }
+ }
+
+ protected void ConvertCFG2DAG(Implementation! impl, Program! program)
+ {
+ impl.PruneUnreachableBlocks(); // This is needed for VCVariety.BlockNested, and is otherwise just an optimization
+
+ current_impl = impl;
+ variable2SequenceNumber = new Hashtable/*Variable -> int*/();
+ incarnationOriginMap = new Dictionary<Incarnation, Absy!>();
+
+ #region Debug Tracing
+ if (CommandLineOptions.Clo.TraceVerify)
+ {
+ Console.WriteLine("original implementation");
+ EmitImpl(impl, false);
+ }
+ #endregion
+
+ #region Debug Tracing
+ if (CommandLineOptions.Clo.TraceVerify)
+ {
+ Console.WriteLine("after desugaring sugared commands like procedure calls");
+ EmitImpl(impl, true);
+ }
+ #endregion
+
+ ComputePredecessors(impl.Blocks);
+
+ #region Convert program CFG into a DAG
+
+ #region Use the graph library to figure out where the (natural) loops are
+
+ #region Create the graph by adding the source node and each edge
+ Graph<Block>! g = GraphFromImpl(impl);
+ #endregion
+
+ g.ComputeLoops(); // this is the call that does all of the processing
+ if (!g.Reducible)
+ {
+ throw new VCGenException("Irreducible flow graphs are unsupported.");
+ }
+
+ #endregion
+
+ #region Cut the backedges, push assert/assume statements from loop header into predecessors, change them all into assume statements at top of loop, introduce havoc statements
+ foreach (Block! header in (!) g.Headers)
+ {
+ IDictionary<Block!,object> backEdgeNodes = new Dictionary<Block!,object>();
+ foreach (Block! b in (!) g.BackEdgeNodes(header)) { backEdgeNodes.Add(b, null); }
+
+ #region Find the (possibly empty) prefix of assert commands in the header, replace each assert with an assume of the same condition
+ CmdSeq prefixOfPredicateCmdsInit = new CmdSeq();
+ CmdSeq prefixOfPredicateCmdsMaintained = new CmdSeq();
+ for (int i = 0, n = header.Cmds.Length; i < n; i++)
+ {
+ PredicateCmd a = header.Cmds[i] as PredicateCmd;
+ if (a != null)
+ {
+ if (a is AssumeCmd) {
+ prefixOfPredicateCmdsInit.Add(a);
+ prefixOfPredicateCmdsMaintained.Add(a);
+ } else {
+ Bpl.AssertCmd c = (AssertCmd) a;
+ Bpl.AssertCmd b = new Bpl.LoopInitAssertCmd(c.tok, c.Expr);
+ b.ErrorData = c.ErrorData;
+ prefixOfPredicateCmdsInit.Add(b);
+ b = new Bpl.LoopInvMaintainedAssertCmd(c.tok, c.Expr);
+ b.ErrorData = c.ErrorData;
+ prefixOfPredicateCmdsMaintained.Add(b);
+ header.Cmds[i] = new AssumeCmd(c.tok,c.Expr);
+ }
+ }
+ else if ( header.Cmds[i] is CommentCmd )
+ {
+ // ignore
+ }
+ else
+ {
+ break; // stop when an assignment statement (or any other non-predicate cmd) is encountered
+ }
+ }
+ #endregion
+
+ #region Copy the prefix of predicate commands into each predecessor. Do this *before* cutting the backedge!!
+ for ( int predIndex = 0, n = header.Predecessors.Length; predIndex < n; predIndex++ )
+ {
+ Block! pred = (!)header.Predecessors[predIndex];
+
+ // Create a block between header and pred for the predicate commands if header has more than one successor
+ // or if pred is a back edge node
+ GotoCmd gotocmd = pred.TransferCmd as GotoCmd;
+ if ((backEdgeNodes.ContainsKey(pred)) || (gotocmd != null && gotocmd.labelNames != null && gotocmd.labelNames.Length > 1))
+ {
+ Block! newBlock = CreateBlockBetween(predIndex, header);
+ impl.Blocks.Add(newBlock);
+
+ // if pred is a back edge node, then now newBlock is the back edge node
+ if (backEdgeNodes.ContainsKey(pred))
+ {
+ backEdgeNodes.Remove(pred);
+ backEdgeNodes.Add(newBlock,null);
+ }
+
+ pred = newBlock;
+ }
+ // Add the predicate commands
+ if (backEdgeNodes.ContainsKey(pred)){
+ pred.Cmds.AddRange(prefixOfPredicateCmdsMaintained);
+ }
+ else {
+ pred.Cmds.AddRange(prefixOfPredicateCmdsInit);
+ }
+ }
+ #endregion
+
+ #region Cut the back edge
+ foreach (Block! backEdgeNode in (!)backEdgeNodes.Keys)
+ {
+ Debug.Assert(backEdgeNode.TransferCmd is GotoCmd,"An node was identified as the source for a backedge, but it does not have a goto command.");
+ GotoCmd gtc = backEdgeNode.TransferCmd as GotoCmd;
+ if (gtc != null && gtc.labelTargets != null && gtc.labelTargets.Length > 1 )
+ {
+ // then remove the backedge by removing the target block from the list of gotos
+ BlockSeq remainingTargets = new BlockSeq();
+ StringSeq remainingLabels = new StringSeq();
+ assume gtc.labelNames != null;
+ for (int i = 0, n = gtc.labelTargets.Length; i < n; i++)
+ {
+ if ( gtc.labelTargets[i] != header )
+ {
+ remainingTargets.Add(gtc.labelTargets[i]);
+ remainingLabels.Add(gtc.labelNames[i]);
+ }
+ }
+ gtc.labelTargets = remainingTargets;
+ gtc.labelNames = remainingLabels;
+ }
+ else
+ {
+ // This backedge is the only out-going edge from this node.
+ // Add an "assume false" statement to the end of the statements
+ // inside of the block and change the goto command to a return command.
+ AssumeCmd ac = new AssumeCmd(Token.NoToken,Expr.False);
+ backEdgeNode.Cmds.Add(ac);
+ backEdgeNode.TransferCmd = new ReturnCmd(Token.NoToken);
+ }
+ #region Remove the backedge node from the list of predecessor nodes in the header
+ BlockSeq newPreds = new BlockSeq();
+ foreach ( Block p in header.Predecessors )
+ {
+ if ( p != backEdgeNode )
+ newPreds.Add(p);
+ }
+ header.Predecessors = newPreds;
+ #endregion
+ }
+ #endregion
+
+ #region Collect all variables that are assigned to in all of the natural loops for which this is the header
+ VariableSeq varsToHavoc = new VariableSeq();
+ foreach (Block! backEdgeNode in (!) g.BackEdgeNodes(header))
+ {
+ foreach ( Block! b in g.NaturalLoops(header,backEdgeNode) )
+ {
+ foreach ( Cmd! c in b.Cmds )
+ {
+ c.AddAssignedVariables(varsToHavoc);
+ }
+ }
+ }
+ IdentifierExprSeq havocExprs = new IdentifierExprSeq();
+ foreach ( Variable! v in varsToHavoc )
+ {
+ IdentifierExpr ie = new IdentifierExpr(Token.NoToken, v);
+ if(!havocExprs.Has(ie))
+ havocExprs.Add(ie);
+ }
+ // pass the token of the enclosing loop header to the HavocCmd so we can reconstruct
+ // the source location for this later on
+ HavocCmd hc = new HavocCmd(header.tok,havocExprs);
+ CmdSeq newCmds = new CmdSeq();
+ newCmds.Add(hc);
+ foreach ( Cmd c in header.Cmds )
+ {
+ newCmds.Add(c);
+ }
+ header.Cmds = newCmds;
+ #endregion
+ }
+ #endregion
+ #endregion Convert program CFG into a DAG
+
+ #region Debug Tracing
+ if (CommandLineOptions.Clo.TraceVerify)
+ {
+ Console.WriteLine("after conversion into a DAG");
+ EmitImpl(impl, true);
+ }
+ #endregion
+ }
+
+ protected Hashtable/*TransferCmd->ReturnCmd*/! PassifyImpl(Implementation! impl, Program! program)
+ {
+ Hashtable/*TransferCmd->ReturnCmd*/ gotoCmdOrigins = new Hashtable/*TransferCmd->ReturnCmd*/();
+ Block/*?*/ exitBlock = GenerateUnifiedExit(impl, gotoCmdOrigins);
+
+ #region Debug Tracing
+ if (CommandLineOptions.Clo.TraceVerify)
+ {
+ Console.WriteLine("after creating a unified exit block");
+ EmitImpl(impl, true);
+ }
+ #endregion
+
+ #region Insert pre- and post-conditions and where clauses as assume and assert statements
+ {
+ CmdSeq cc = new CmdSeq();
+ // where clauses of global variables
+ foreach (Declaration d in program.TopLevelDeclarations) {
+ GlobalVariable gvar = d as GlobalVariable;
+ if (gvar != null && gvar.TypedIdent.WhereExpr != null) {
+ Cmd c = new AssumeCmd(gvar.tok, gvar.TypedIdent.WhereExpr);
+ cc.Add(c);
+ }
+ }
+ // where clauses of in- and out-parameters
+ cc.AddRange(GetParamWhereClauses(impl));
+ // where clauses of local variables
+ foreach (Variable! lvar in impl.LocVars) {
+ if (lvar.TypedIdent.WhereExpr != null) {
+ Cmd c = new AssumeCmd(lvar.tok, lvar.TypedIdent.WhereExpr);
+ cc.Add(c);
+ }
+ }
+
+ InjectPreconditions(impl);
+ //cc.AddRange(GetPre(impl));
+
+ Block! entryBlock = (!) impl.Blocks[0];
+ cc.AddRange(entryBlock.Cmds);
+ entryBlock.Cmds = cc;
+
+ InjectPostConditions(impl,exitBlock,gotoCmdOrigins);
+ //CmdSeq! post = GetPost(impl);
+ //exitBlock.Cmds.AddRange(post);
+ }
+ #endregion
+
+ #region Debug Tracing
+ if (CommandLineOptions.Clo.TraceVerify)
+ {
+ Console.WriteLine("after inserting pre- and post-conditions");
+ EmitImpl(impl, true);
+ }
+ #endregion
+
+ AddBlocksBetween(impl);
+
+ #region Debug Tracing
+ if (CommandLineOptions.Clo.TraceVerify)
+ {
+ Console.WriteLine("after adding empty blocks before all blocks with more than one predecessor");
+ EmitImpl(impl, true);
+ }
+ #endregion
+
+ Convert2PassiveCmd(impl);
+
+ #region Peep-hole optimizations
+ if (CommandLineOptions.Clo.RemoveEmptyBlocks){
+ #region Get rid of empty blocks
+ {
+ Block! entryBlock = (!) impl.Blocks[0];
+ RemoveEmptyBlocks(entryBlock);
+ impl.PruneUnreachableBlocks();
+ }
+ #endregion Get rid of empty blocks
+
+ #region Debug Tracing
+ if (CommandLineOptions.Clo.TraceVerify)
+ {
+ Console.WriteLine("after peep-hole optimizations");
+ EmitImpl(impl, true);
+ }
+ #endregion
+ }
+ #endregion Peep-hole optimizations
+
+// #region Constant Folding
+// #endregion
+// #region Debug Tracing
+// if (CommandLineOptions.Clo.TraceVerify)
+// {
+// Console.WriteLine("after constant folding");
+// EmitImpl(impl, true);
+// }
+// #endregion
+
+ return gotoCmdOrigins;
+ }
+
+ static Counterexample TraceCounterexample(Block! b, Hashtable! traceNodes, BlockSeq! trace, ErrorModel errModel, Dictionary<Incarnation, Absy!>! incarnationOriginMap)
+ {
+ // After translation, all potential errors come from asserts.
+ CmdSeq! cmds = b.Cmds;
+ TransferCmd! transferCmd = (!)b.TransferCmd;
+ for (int i = 0; i < cmds.Length; i++)
+ {
+ Cmd! cmd = (!) cmds[i];
+
+ // Skip if 'cmd' not contained in the trace or not an assert
+ if (!(cmd is AssertCmd) || !traceNodes.Contains(cmd))
+ continue;
+
+ return AssertCmdToCounterexample((AssertCmd)cmd, transferCmd, trace, errModel, incarnationOriginMap);
+ }
+
+ GotoCmd gotoCmd = transferCmd as GotoCmd;
+ if (gotoCmd != null)
+ {
+ foreach (Block! bb in (!)gotoCmd.labelTargets)
+ {
+ if (traceNodes.Contains(bb)){
+ trace.Add(bb);
+ return TraceCounterexample(bb, traceNodes, trace, errModel, incarnationOriginMap);
+ }
+ }
+ }
+
+ return null;
+
+ // Debug.Fail("Could not find failing node.");
+ // throw new Microsoft.Contracts.AssertException();
+ }
+
+
+ static void /*return printable error!*/ ApplyEnhancedErrorPrintingStrategy (Bpl.Expr! expr, Hashtable /*Variable -> Expr*/! incarnationMap, MiningStrategy errorDataEnhanced, ErrorModel! errModel, Dictionary<Expr!, object>! exprToPrintableValue, List<string!>! relatedInformation, bool printInternalStateDumpOnce, Dictionary<Incarnation, Absy!>! incarnationOriginMap) {
+ if (errorDataEnhanced is ListOfMiningStrategies) {
+ ListOfMiningStrategies loms = (ListOfMiningStrategies) errorDataEnhanced;
+ List<MiningStrategy>! l = loms.msList;
+ for (int i = 0; i < l.Count; i++) {
+ MiningStrategy ms = l[i];
+ if (ms != null) {
+ ApplyEnhancedErrorPrintingStrategy(expr, incarnationMap, l[i], errModel, exprToPrintableValue, relatedInformation, false, incarnationOriginMap);
+ }
+ }
+ }
+ else if (errorDataEnhanced is EEDTemplate /*EDEverySubExpr*/) {
+ EEDTemplate eedT = (EEDTemplate) errorDataEnhanced;
+ string reason = eedT.reason;
+ List<Bpl.Expr!> listOfExprs = eedT.exprList;
+ if (listOfExprs != null) {
+ List<string> holeFillers = new List<string>();
+ for (int i = 0; i < listOfExprs.Count; i++) {
+ bool alreadySet = false;
+ foreach (KeyValuePair<Bpl.Expr!, object> kvp in exprToPrintableValue) {
+ Bpl.Expr! e = kvp.Key;
+ Bpl.Expr! f = listOfExprs[i];
+ // the strings are compared instead of the actual expressions because
+ // the expressions might not be identical, but their print-out strings will be
+ if (e.ToString() == f.ToString()) {
+ object o = kvp.Value;
+ if (o != null) {
+ holeFillers.Add(o.ToString());
+ alreadySet = true;
+ break;
+ }
+ }
+ }
+ if (!alreadySet) {
+ // no information about that Expression found, so put in <unknown>
+ holeFillers.Add("<unknown>");
+ }
+ }
+ reason = FormatReasonString(reason, holeFillers);
+ }
+ if (reason != null) {
+ relatedInformation.Add("(related information): "+reason);
+ }
+ } else {
+ // define new templates here!
+ }
+
+ if (printInternalStateDumpOnce) {
+ ComputeAndTreatHeapSuccessions(incarnationMap, errModel, incarnationOriginMap, relatedInformation);
+
+ // default action: print all values!
+ foreach (KeyValuePair<Bpl.Expr!, object> kvp in exprToPrintableValue) {
+ object o = kvp.Value;
+ if (o != null) {
+ // We do not want to print LiteralExprs because that gives things like 0 == 0.
+ // If both arguments to the string.Format are the same it is also useless,
+ // as that would print e.g. $a == $a.
+ if (!(kvp.Key is LiteralExpr)&& kvp.Key.ToString() != o.ToString()) {
+ string boogieExpr;
+ // check whether we are handling BPL or SSC input
+ if (CommandLineOptions.Clo.RunningBoogieOnSsc) {
+ boogieExpr = Helpers.PrettyPrintBplExpr(kvp.Key);
+ } else {
+ boogieExpr = kvp.Key.ToString();
+ }
+ relatedInformation.Add("(internal state dump): "+string.Format("{0} == {1}", boogieExpr, o));
+ }
+ }
+ }
+ }
+ }
+
+ static void ComputeAndTreatHeapSuccessions(System.Collections.Hashtable! incarnationMap, ErrorModel! errModel, Dictionary<Incarnation, Absy!>! incarnationOriginMap, List<string!>! relatedInformation) {
+ List<int> heapSuccList = ComputeHeapSuccessions(incarnationMap, errModel);
+ TreatHeapSuccessions(heapSuccList, incarnationMap, errModel, incarnationOriginMap, relatedInformation);
+ }
+
+ static List<int> ComputeHeapSuccessions(System.Collections.Hashtable! incarnationMap, ErrorModel! errModel) {
+ // find the heap variable
+ Variable heap = null;
+ ICollection ic = incarnationMap.Keys;
+ foreach (object o in ic) {
+ if (o is GlobalVariable) {
+ GlobalVariable gv = (GlobalVariable) o;
+ if (gv.Name == "$Heap") {
+ heap = gv;
+ }
+ }
+ }
+ List<int> heapIdSuccession = new List<int>();
+ if (heap == null) {
+ // without knowing the name of the current heap we cannot create a heap succession!
+ } else {
+ object oHeap = incarnationMap[heap];
+ if (oHeap != null) {
+ string currentHeap = oHeap.ToString();
+ int currentHeapId;
+ if (errModel.identifierToPartition.TryGetValue(currentHeap, out currentHeapId)) {
+ while (currentHeapId != -1) {
+ if (!heapIdSuccession.Contains(currentHeapId)) {
+ heapIdSuccession.Add(currentHeapId);
+ currentHeapId = ComputePredecessorHeapId(currentHeapId, errModel);
+ } else {
+ // looping behavior, just stop here and do not add this value (again!)
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (heapIdSuccession.Count > 0) {
+ int heapId = heapIdSuccession[heapIdSuccession.Count-1];
+ List<string!> strl = errModel.partitionToIdentifiers[heapId];
+ if (strl != null && strl.Contains("$Heap")) {
+ // we have a proper succession of heaps that starts with $Heap
+ return heapIdSuccession;
+ } else {
+ // no proper heap succession, not starting with $Heap!
+ return null;
+ }
+ } else {
+ // no heap succession found because either the $Heap does not have a current incarnation
+ // or because (unlikely!) the model is somehow messed up
+ return null;
+ }
+ }
+
+ static int ComputePredecessorHeapId(int id, ErrorModel! errModel) {
+ //check "$HeapSucc" and "store2" functions:
+ List<int> heapSuccPredIdList = new List<int>();
+ List<List<int>> heapSuccFunc;
+ errModel.definedFunctions.TryGetValue("$HeapSucc", out heapSuccFunc);
+ if (heapSuccFunc != null) {
+ foreach (List<int> heapSuccFuncDef in heapSuccFunc) {
+ // do not look at the else case of the function def, so check .Count
+ if (heapSuccFuncDef != null && heapSuccFuncDef.Count == 3 && heapSuccFuncDef[1] == id) {
+ // make sure each predecessor is put into the list at most once
+ if (!heapSuccPredIdList.Contains(heapSuccFuncDef[0])) {
+ heapSuccPredIdList.Add(heapSuccFuncDef[0]);
+ }
+ }
+ }
+ }
+ List<int> store2PredIdList = new List<int>();;
+ List<List<int>> store2Func;
+ errModel.definedFunctions.TryGetValue("store2", out store2Func);
+ if (store2Func != null) {
+ foreach (List<int> store2FuncDef in store2Func) {
+ // do not look at the else case of the function def, so check .Count
+ if (store2FuncDef != null && store2FuncDef.Count == 5 && store2FuncDef[4] == id) {
+ // make sure each predecessor is put into the list at most once
+ if (!store2PredIdList.Contains(store2FuncDef[0])) {
+ store2PredIdList.Add(store2FuncDef[0]);
+ }
+ }
+ }
+ }
+ if (heapSuccPredIdList.Count + store2PredIdList.Count > 0) {
+ if (store2PredIdList.Count == 1) {
+ return store2PredIdList[0];
+ } else if (store2PredIdList.Count == 0) {
+ if (heapSuccPredIdList.Count == 1) {
+ return heapSuccPredIdList[0];
+ } else { //(heapSuccPredIdList.Count > 1)
+ if (heapSuccPredIdList.Count == 2) {
+ // if one of the 2 is a self-loop, take the other!
+ if (heapSuccPredIdList[0] == id) {
+ return heapSuccPredIdList[1];
+ } else if (heapSuccPredIdList[1] == id) {
+ return heapSuccPredIdList[0];
+ } else {
+ //no self-loop, two different predecessors, cannot choose
+ return -1;
+ }
+ } else {
+ // at least 3 different predecessors available, one of them could be a self-loop, but
+ // we cannot choose between the other 2 (or more) candidates
+ return -1;
+ }
+ }
+ } else {
+ // more than one result in the succession coming from store2, no way
+ // to decide which is right, end here
+ return -1;
+ }
+ } else {
+ // no predecessor found
+ return -1;
+ }
+ }
+
+ static void TreatHeapSuccessions (List<int> heapSuccessionList, System.Collections.Hashtable! incarnationMap, ErrorModel! errModel, Dictionary<Incarnation, Absy!>! incarnationOriginMap, List<string!>! relatedInformation) {
+ if (heapSuccessionList == null) {
+ // empty list of heap successions, nothing we can do!
+ return;
+ }
+ // primarily look for $o and $f (with skolem-id stuff) and then look where they were last changed!
+ // find the o and f variables
+ Variable dollarO = null;
+ Variable dollarF = null;
+ ICollection ic = incarnationMap.Keys;
+ foreach (object o in ic) {
+ if (o is BoundVariable) {
+ BoundVariable bv = (BoundVariable) o;
+ if (bv.Name == "$o") {
+ dollarO = bv;
+ }
+ else if (bv.Name == "$f") {
+ dollarF = bv;
+ }
+ }
+ }
+ if (dollarO == null || dollarF == null) {
+ // without knowing the name of the current incarnation of $Heap, $o and $f we don't do anything here
+ } else {
+ object objO = incarnationMap[dollarO];
+ object objF = incarnationMap[dollarF];
+ if (objO != null && objF != null) {
+ int zidO = errModel.identifierToPartition[objO.ToString()];
+ int zidF = errModel.identifierToPartition[objF.ToString()];
+
+ List<List<int>> select2Func = null;
+ if (errModel.definedFunctions.TryGetValue("select2", out select2Func) && select2Func != null) {
+ // check for all changes to $o.$f!
+ List<int> heapsChangedOFZid = new List<int>();
+ int oldValueZid = -1;
+ int newValueZid = -1;
+
+ for (int i = 0; i < heapSuccessionList.Count; i++) {
+ bool foundValue = false;
+ foreach (List<int> f in select2Func) {
+ if (f != null && f.Count == 4 && f[0] == heapSuccessionList[i] && f[1] == zidO && f[2] == zidF) {
+ newValueZid = f[3];
+ foundValue = true;
+ break;
+ }
+ }
+ if (!foundValue) {
+ // get default of the function once Leo&Nikolaj have changed it so the default is type correct
+ // for now treat it as a -1 !
+ // the last element of select2Func is the else case, it has only 1 element, so grab that:
+ // newValueZid = (select2Func[select2Func.Count-1])[0];
+ newValueZid = -1;
+ }
+
+ if (oldValueZid != newValueZid) {
+ // there was a change here, record that in the list:
+ if (oldValueZid != -1) {
+ // don't record a change at the "initial" location, which refers to the $Heap in
+ // its current incarnation, and is marked by the oldValueZid being uninitialized
+ heapsChangedOFZid.Add(heapSuccessionList[i-1]);
+ }
+ oldValueZid = newValueZid;
+ }
+ }
+
+ foreach (int id in heapsChangedOFZid) {
+ //get the heap name out of the errModel for this zid:
+ List<string!> l = errModel.partitionToIdentifiers[id];
+ List<string!> heaps = new List<string!>();
+ if (l!=null) {
+ foreach (string s in l) {
+ if (s.StartsWith("$Heap")) {
+ heaps.Add(s);
+ }
+ }
+ }
+ // easy case first:
+ if (heaps.Count == 1) {
+ string heapName = heaps[0];
+ // we have a string with the name of the heap, but we need to get the
+ // source location out of a map that uses Incarnations!
+
+ ICollection incOrgMKeys = incarnationOriginMap.Keys;
+ foreach (Incarnation inc in incOrgMKeys) {
+ if (inc!= null) {
+ if (inc.Name == heapName) {
+ Absy source = null;
+ incarnationOriginMap.TryGetValue(inc, out source);
+ if (source != null) {
+ if (source is Block) {
+ Block b = (Block) source;
+ string fileName = b.tok.filename;
+ if (fileName != null) {
+ fileName = fileName.Substring(fileName.LastIndexOf('\\') + 1);
+ }
+ relatedInformation.Add("(related information): Changed $o.$f here: " + fileName + "(" + b.tok.line + "," + b.tok.col + ")");
+ } else if (source is Cmd) {
+ Cmd c = (Cmd) source;
+ string fileName = c.tok.filename;
+ if (fileName != null) {
+ fileName = fileName.Substring(fileName.LastIndexOf('\\') + 1);
+ }
+ relatedInformation.Add("(related information) Changed $o.$f here: " + fileName + "(" + c.tok.line + "," + c.tok.col + ")");
+ } else {
+ assert false;
+ }
+ }
+ }
+ }
+ }
+ } else {
+ // more involved! best use some of the above code and try to synchronize joint parts
+ // here there is more than one "$Heap@i" in the partition, check if they all agree on one
+ // source location or maybe if some of them are joins (i.e. blocks) that should be ignored
+ }
+
+ }
+ }
+ }
+ }
+ }
+
+ static string FormatReasonString (string reason, List<string> holeFillers) {
+ if (holeFillers != null) {
+ // in case all elements of holeFillers are "<unknown>" we can not say anything useful
+ // so just say nothing and return null
+ bool allUnknown = true;
+ foreach (string s in holeFillers) {
+ if (s != "<unknown>") {
+ allUnknown = false;
+ break;
+ }
+ }
+ if (allUnknown) {
+ return null;
+ }
+ string[] a = holeFillers.ToArray();
+ reason = string.Format(reason, a);
+ }
+ return reason;
+ }
+
+ static object ValueFromZID (ErrorModel! errModel, int id) {
+ return ValueFromZID(errModel, id, null);
+ }
+
+ static object ValueFromZID (ErrorModel! errModel, int id, string searchForAlternate) {
+ object o = errModel.partitionToValue[id];
+ if (o != null) {
+ return o;
+ } else {
+ // more elaborate scheme to find something better, as in: look at the identifiers that
+ // this partition maps to, or similar things!
+
+ //treatment for 'null':
+ int idForNull = -1;
+ if (errModel.valueToPartition.TryGetValue("nullObject", out idForNull) && idForNull == id) {
+ return "nullObject";
+ }
+
+ string returnStr = null;
+
+ // "good identifiers" if there is no value found are 'unique consts' or
+ // '$in' parameters; '$in' parameters are treated, unclear how to get 'unique const' info
+ List<string!> identifiers = errModel.partitionToIdentifiers[id];
+ if (identifiers != null) {
+ foreach (string s in identifiers) {
+ //$in parameters are (more) interesting than other identifiers, return the
+ // first one found
+ if (s.EndsWith("$in")) {
+ returnStr = s;
+ break;
+ }
+ }
+ }
+
+ // try to get mappings from one identifier to another if there are exactly
+ // two identifiers in the partition, where one of them is the identifier for which
+ // we search for alternate encodings (third parameter of the method) [or an incarnation
+ // of such an identifier]
+ if (returnStr == null && searchForAlternate != null && identifiers != null && identifiers.Count == 2) {
+ if (identifiers[0] == searchForAlternate || identifiers[0].StartsWith(searchForAlternate + ".sk.")) {
+ returnStr = identifiers[1];
+ } else if (identifiers[1] == searchForAlternate || identifiers[1].StartsWith(searchForAlternate + ".sk.")) {
+ returnStr = identifiers[0];
+ }
+ }
+
+ if (returnStr != null) {
+ return Helpers.BeautifyBplString(returnStr);
+ }
+
+ return null;
+ }
+ }
+
+ static int TreatInterpretedFunction(string! functionName, List<int>! zargs, ErrorModel! errModel) {
+ if (zargs.Count != 2) {
+ //all interpreted functions are binary, so there have to be exactly two arguments
+ return -1;
+ }
+ else {
+ object arg0 = ValueFromZID(errModel, zargs[0]);
+ object arg1 = ValueFromZID(errModel, zargs[1]);
+ if (arg0 is BigNum && arg1 is BigNum) {
+ BigNum arg0i = (BigNum)arg0;
+ BigNum arg1i = (BigNum)arg1;
+ BigNum result;
+ if (functionName == "+") {
+ result = arg0i + arg1i;
+ } else if (functionName == "-") {
+ result = arg0i - arg1i;
+ } else /*if (functionName == "*")*/ {
+ result = arg0i * arg1i;
+ }
+ int returnId = -1;
+ if (errModel.valueToPartition.TryGetValue(result, out returnId)) {
+ return returnId;
+ } else {
+ return -1;
+ }
+ }
+ else {
+ //both arguments need to be integers for this to work!
+ return -1;
+ }
+ }
+ }
+
+ static int TreatFunction (string! functionName, List<int>! zargs, bool undefined, ErrorModel! errModel) {
+ List<List<int>> functionDef;
+ if ((!errModel.definedFunctions.TryGetValue(functionName, out functionDef) && functionName != "+" && functionName != "-" && functionName != "*") || undefined) {
+ // no fitting function found or one of the arguments is undefined
+ return -1;
+ } else {
+ if (functionName == "+" || functionName == "-" || functionName == "*") {
+ return TreatInterpretedFunction(functionName, zargs, errModel);
+ }
+ assert functionDef != null;
+ foreach (List<int> pWiseF in functionDef) {
+ assert pWiseF != null;
+ // else case in the function definition:
+ if (pWiseF.Count == 1) {
+ return pWiseF[0];
+ }
+ // number of arguments is exactly the right number
+ assert zargs.Count == pWiseF.Count - 1;
+ if (forall{int i in (0: zargs.Count); zargs[i] == pWiseF[i]}) {
+ return pWiseF[pWiseF.Count - 1];
+ }
+ }
+ // all functions should have an 'else ->' case defined, therefore this should be
+ // unreachable code!
+ assert false;
+ }
+ }
+
+ //returned int is zID
+ static int GetValuesFromModel (Bpl.Expr! expr, Hashtable /*Variable -> Expr*/! incarnationMap, ErrorModel! errModel, Dictionary<Bpl.Expr!, object>! exprToPrintableValue)
+ modifies exprToPrintableValue.*;
+ {
+ // call GetValuesFromModel on all proper subexpressions, returning their value,
+ // so they only have to be computed once!
+ if (expr is LiteralExpr) {
+ // nothing needs to be added to the exprToPrintableValue map, because e.g. 0 -> 0 is not interesting
+ object o = ((LiteralExpr) expr).Val;
+ if (o == null) {
+ o = "nullObject";
+ }
+ int idForExprVal;
+ if (errModel.valueToPartition.TryGetValue(o, out idForExprVal)) {
+ return idForExprVal;
+ } else {
+ return -1;
+ }
+ }
+ else if (expr is IdentifierExpr) {
+ // if the expression expr is in the incarnation map, then use its incarnation,
+ // otherwise just use the actual expression
+ string s = ((IdentifierExpr) expr).Name;
+ object o = null;
+ Variable v = ((IdentifierExpr) expr).Decl;
+ if (v != null && incarnationMap.ContainsKey(v)) {
+ if (incarnationMap[v] is IdentifierExpr!) {
+ s = ((IdentifierExpr!) incarnationMap[v]).Name;
+ } else if (incarnationMap[v] is LiteralExpr!) {
+ o = ((LiteralExpr!) incarnationMap[v]).Val;
+ }
+ }
+ // if o is not null, then we got a LiteralExpression, that needs separate treatment
+ if (o == null) {
+ // if the expression (respectively its incarnation) is mapped to some partition
+ // then return that id, else return the error code -1
+ if (errModel.identifierToPartition.ContainsKey(s)) {
+ int i = errModel.identifierToPartition[s];
+ // if the key is already in the map we can assume that it is the same map we would
+ // get now and thus just ignore it
+ if (!exprToPrintableValue.ContainsKey(expr)) {
+ exprToPrintableValue.Add(expr, ValueFromZID(errModel, i, ((IdentifierExpr) expr).Name));
+ }
+ return i;
+ } else {
+ return -1;
+ }
+ } else if (errModel.valueToPartition.ContainsKey(o)) {
+ int i = errModel.valueToPartition[o];
+ if (!exprToPrintableValue.ContainsKey(expr))
+ exprToPrintableValue.Add(expr, ValueFromZID(errModel, i));
+ return i;
+ } else {
+ return -1;
+ }
+ }
+ else if (expr is Bpl.NAryExpr) {
+ Bpl.NAryExpr e = (Bpl.NAryExpr)expr;
+ List<int> zargs = new List<int>();
+ bool undefined = false;
+ // do the recursion first
+ foreach (Expr argument in ((NAryExpr) expr).Args) {
+ int zid = -1;
+ if (argument != null) {
+ zid = GetValuesFromModel(argument, incarnationMap, errModel, exprToPrintableValue);
+ }
+ // if one of the arguments is 'undefined' then return -1 ('noZid') for this
+ // but make sure the recursion is complete first1
+ if (zid == -1) {
+ undefined = true;
+ }
+ zargs.Add(zid);
+ }
+ IAppliable! fun = e.Fun;
+ string functionName = fun.FunctionName; // PR: convert to select1, select2, etc in case of a map?
+ // same as IndexedExpr:
+ int id = TreatFunction(functionName, zargs, undefined, errModel);
+ if (id != -1 && !exprToPrintableValue.ContainsKey(expr)) {
+ exprToPrintableValue.Add(expr, ValueFromZID(errModel, id));
+ }
+ return id;
+ }
+ else if (expr is Bpl.OldExpr) {
+ //Bpl.OldExpr e = (Bpl.OldExpr)expr;
+ // We do not know which heap is the old heap and what the latest state change was,
+ // therefore we cannot return anything useful here!
+ return -1;
+ }
+ else if (expr is Bpl.QuantifierExpr) {
+ Bpl.QuantifierExpr q = (Bpl.QuantifierExpr)expr;
+ for (int i = 0; i < q.Dummies.Length; i++) {
+ Bpl.Variable v = q.Dummies[i];
+ if (v != null) {
+ // add to the incarnation map a connection between the bound variable v
+ // of the quantifier and its skolemized incarnation, if available,
+ // i.e., search through the list of identifiers in the model and look for
+ // v.sk.(q.SkolemId), only pick those that are directly associated to a value
+ // DISCLAIMER: of course it is very well possible that one of these incarnations
+ // could be used without actually having a value, but it seems better to pick those
+ // with a value, that is they are more likely to contribute useful information to
+ // the output
+ List<Bpl.IdentifierExpr!> quantVarIncarnationList = new List<Bpl.IdentifierExpr!>();
+ List<int> incarnationZidList = new List<int>();
+ int numberOfNonNullValueIncarnations = 0;
+ for (int j = 0; j < errModel.partitionToIdentifiers.Count; j++){
+ List<string!> pti = errModel.partitionToIdentifiers[j];
+ if (pti != null) {
+ for (int k = 0; k < pti.Count; k++) {
+ // look for v.sk.(q.SkolemId)
+ // if there is more than one look at if there is exactly one with a non-null value
+ // associated, see above explanation
+ if (pti[k].StartsWith(v + ".sk." + q.SkolemId) &&
+ errModel.partitionToValue[errModel.identifierToPartition[pti[k]]] != null) {
+ quantVarIncarnationList.Add(new Bpl.IdentifierExpr(Bpl.Token.NoToken, pti[k], new Bpl.UnresolvedTypeIdentifier(Token.NoToken, "TName")));
+ incarnationZidList.Add(j);
+ if (errModel.partitionToValue[errModel.identifierToPartition[pti[k]]] != null) {
+ numberOfNonNullValueIncarnations++;
+ }
+ }
+ }
+ }
+ }
+ // only one such variable found, associate it with v
+ if (quantVarIncarnationList.Count == 1) {
+ incarnationMap[v] = quantVarIncarnationList[0];
+ } else if (quantVarIncarnationList.Count > 1 && numberOfNonNullValueIncarnations == 1) {
+ // if there are multiple candidate incarnations and exactly one of them has a value
+ // we can pick that one; otherwise it is not clear how to pick one out of multiple
+ // incarnations without a value or out of multiple incarnations with a value associated
+ for (int n = 0; n < incarnationZidList.Count; n++) {
+ if (errModel.partitionToValue[incarnationZidList[n]] != null) {
+ // quantVarIncarnationList and incarnationZidList are indexed in lockstep, so if
+ // for the associated zid the partitionToValue map is non-null then that is the one
+ // thus distinguished incarnation we want to put into the incarnationMap
+ incarnationMap[v] = quantVarIncarnationList[n];
+ break;
+ }
+ }
+ }
+ }
+ }
+ // generate the value of the body but do not return that outside
+ GetValuesFromModel(q.Body, incarnationMap, errModel, exprToPrintableValue);
+ // the quantifier cannot contribute any one value to the rest of the
+ // expression, thus just return -1
+ return -1;
+ }
+ else if (expr is Bpl.ExtractExpr) {
+ Bpl.ExtractExpr ex = (Bpl.ExtractExpr) expr;
+ Bpl.Expr e0 = ex.Bitvector;
+ Bpl.Expr e1 = new LiteralExpr(Token.NoToken, BigNum.FromInt(ex.Start));
+ Bpl.Expr e2 = new LiteralExpr(Token.NoToken, BigNum.FromInt(ex.End));
+ string functionName = "$bv_extract";
+ List<int> zargs = new List<int>();
+ bool undefined = false;
+
+ int zid = -1;
+ zid = GetValuesFromModel(e0, incarnationMap, errModel, exprToPrintableValue);
+ if (zid == -1) {
+ undefined = true;
+ }
+ zargs.Add(zid);
+
+ zid = -1;
+ zid = GetValuesFromModel(e1, incarnationMap, errModel, exprToPrintableValue);
+ if (zid == -1) {
+ undefined = true;
+ }
+ zargs.Add(zid);
+
+ zid = -1;
+ zid = GetValuesFromModel(e2, incarnationMap, errModel, exprToPrintableValue);
+ if (zid == -1) {
+ undefined = true;
+ }
+ zargs.Add(zid);
+
+ //same as NAryExpr:
+ int id = TreatFunction(functionName, zargs, undefined, errModel);
+ if (id != -1 && !exprToPrintableValue.ContainsKey(expr)) {
+ exprToPrintableValue.Add(expr, ValueFromZID(errModel, id));
+ }
+ return id;
+ }
+ else if (expr is Bpl.BvConcatExpr) {
+ // see comment above
+ Bpl.BvConcatExpr bvc = (Bpl.BvConcatExpr) expr;
+ string functionName = "$bv_concat";
+ List<int> zargs = new List<int>();
+ bool undefined = false;
+
+ int zid = -1;
+ zid = GetValuesFromModel(bvc.E0, incarnationMap, errModel, exprToPrintableValue);
+ if (zid == -1) {
+ undefined = true;
+ }
+ zargs.Add(zid);
+
+ zid = -1;
+ zid = GetValuesFromModel(bvc.E0, incarnationMap, errModel, exprToPrintableValue);
+ if (zid == -1) {
+ undefined = true;
+ }
+ zargs.Add(zid);
+
+ //same as NAryExpr:
+ int id = TreatFunction(functionName, zargs, undefined, errModel);
+ if (id != -1 && !exprToPrintableValue.ContainsKey(expr)) {
+ exprToPrintableValue.Add(expr, ValueFromZID(errModel, id));
+ }
+ return id;
+ }
+ else {
+ assert false; // unexpected Bpl.Expr
+ }
+ return -1;
+ }
+
+ static Counterexample! AssertCmdToCounterexample (AssertCmd! cmd, TransferCmd! transferCmd, BlockSeq! trace, ErrorModel errModel, Dictionary<Incarnation, Absy!>! incarnationOriginMap) {
+ List<string!>! relatedInformation = new List<string!>();
+ if (CommandLineOptions.Clo.EnhancedErrorMessages == 1) {
+ if (cmd.OrigExpr != null && cmd.IncarnationMap != null && errModel != null) {
+
+ // get all possible information first
+ Dictionary<Expr!, object> exprToPrintableValue = new Dictionary<Expr!, object>();
+ GetValuesFromModel(cmd.OrigExpr, cmd.IncarnationMap, errModel, exprToPrintableValue);
+ // then apply the strategies
+ ApplyEnhancedErrorPrintingStrategy(cmd.OrigExpr, cmd.IncarnationMap, cmd.ErrorDataEnhanced, errModel, exprToPrintableValue, relatedInformation, true, incarnationOriginMap);
+ }
+ }
+
+ // See if it is a special assert inserted in translation
+ if (cmd is AssertRequiresCmd)
+ {
+ AssertRequiresCmd! assertCmd = (AssertRequiresCmd)cmd;
+ CallCounterexample cc = new CallCounterexample(trace, assertCmd.Call, assertCmd.Requires);
+ cc.relatedInformation = relatedInformation;
+ return cc;
+ }
+ else if (cmd is AssertEnsuresCmd)
+ {
+ AssertEnsuresCmd! assertCmd = (AssertEnsuresCmd)cmd;
+ ReturnCounterexample rc = new ReturnCounterexample(trace, transferCmd, assertCmd.Ensures);
+ rc.relatedInformation = relatedInformation;
+ return rc;
+ }
+ else
+ {
+ AssertCounterexample ac = new AssertCounterexample(trace, (AssertCmd)cmd);
+ ac.relatedInformation = relatedInformation;
+ return ac;
+ }
+ }
+
+// static void EmitImpl(Implementation! impl, bool printDesugarings) {
+// int oldPrintUnstructured = CommandLineOptions.Clo.PrintUnstructured;
+// CommandLineOptions.Clo.PrintUnstructured = 2; // print only the unstructured program
+// bool oldPrintDesugaringSetting = CommandLineOptions.Clo.PrintDesugarings;
+// CommandLineOptions.Clo.PrintDesugarings = printDesugarings;
+// impl.Emit(new TokenTextWriter("<console>", Console.Out, false), 0);
+// CommandLineOptions.Clo.PrintDesugarings = oldPrintDesugaringSetting;
+// CommandLineOptions.Clo.PrintUnstructured = oldPrintUnstructured;
+// }
+
+ static VCExpr! LetVC(Block! startBlock,
+ Hashtable/*<int, Absy!>*/! label2absy,
+ ProverContext! proverCtxt)
+ {
+ Hashtable/*<Block, LetVariable!>*/! blockVariables = new Hashtable/*<Block, LetVariable!!>*/();
+ List<VCExprLetBinding!>! bindings = new List<VCExprLetBinding!>();
+ VCExpr startCorrect = LetVC(startBlock, label2absy, blockVariables, bindings, proverCtxt);
+ return proverCtxt.ExprGen.Let(bindings, startCorrect);
+ }
+
+ static VCExpr! LetVC(Block! block,
+ Hashtable/*<int, Absy!>*/! label2absy,
+ Hashtable/*<Block, VCExprVar!>*/! blockVariables,
+ List<VCExprLetBinding!>! bindings,
+ ProverContext! proverCtxt)
+ {
+ VCExpressionGenerator! gen = proverCtxt.ExprGen;
+ VCExprVar v = (VCExprVar)blockVariables[block];
+ if (v == null) {
+ /*
+ * For block A (= block), generate:
+ * LET_binding A_correct = wp(A_body, (/\ S \in Successors(A) :: S_correct))
+ * with the side effect of adding the let bindings to "bindings" for any
+ * successor not yet visited.
+ */
+ VCExpr SuccCorrect;
+ GotoCmd gotocmd = block.TransferCmd as GotoCmd;
+ if (gotocmd == null) {
+ SuccCorrect = VCExpressionGenerator.True;
+ } else {
+ assert gotocmd.labelTargets != null;
+ List<VCExpr!> SuccCorrectVars = new List<VCExpr!>(gotocmd.labelTargets.Length);
+ foreach (Block! successor in gotocmd.labelTargets) {
+ VCExpr s = LetVC(successor, label2absy, blockVariables, bindings, proverCtxt);
+ SuccCorrectVars.Add(s);
+ }
+ SuccCorrect = gen.NAry(VCExpressionGenerator.AndOp, SuccCorrectVars);
+ }
+
+ VCContext context = new VCContext(label2absy, proverCtxt);
+ VCExpr vc = Wlp.Block(block, SuccCorrect, context);
+
+ v = gen.Variable(block.Label + "_correct", Bpl.Type.Bool);
+ bindings.Add(gen.LetBinding(v, vc));
+ blockVariables.Add(block, v);
+ }
+ return v;
+ }
+
+ static VCExpr! DagVC(Block! block,
+ Hashtable/*<int, Absy!>*/! label2absy,
+ Hashtable/*<Block, VCExpr!>*/! blockEquations,
+ ProverContext! proverCtxt)
+ {
+ VCExpressionGenerator! gen = proverCtxt.ExprGen;
+ VCExpr vc = (VCExpr)blockEquations[block];
+ if (vc != null) {
+ return vc;
+ }
+
+ /*
+ * For block A (= block), generate:
+ * wp(A_body, (/\ S \in Successors(A) :: DagVC(S)))
+ */
+ VCExpr SuccCorrect = null;
+ GotoCmd gotocmd = block.TransferCmd as GotoCmd;
+ if (gotocmd != null)
+ {
+ foreach (Block! successor in (!)gotocmd.labelTargets) {
+ VCExpr c = DagVC(successor, label2absy, blockEquations, proverCtxt);
+ SuccCorrect = SuccCorrect == null ? c : gen.And(SuccCorrect, c);
+ }
+ }
+ if (SuccCorrect == null) {
+ SuccCorrect = VCExpressionGenerator.True;
+ }
+
+ VCContext context = new VCContext(label2absy, proverCtxt);
+ vc = Wlp.Block(block, SuccCorrect, context);
+
+ // gen.MarkAsSharedFormula(vc); PR: don't know yet what to do with this guy
+
+ blockEquations.Add(block, vc);
+ return vc;
+ }
+
+ static VCExpr! FlatBlockVC(Implementation! impl,
+ Hashtable/*<int, Absy!>*/! label2absy,
+ bool local, bool reach, bool doomed,
+ ProverContext! proverCtxt)
+ requires local ==> !reach; // "reach" must be false for local
+ {
+ VCExpressionGenerator! gen = proverCtxt.ExprGen;
+ Hashtable/* Block --> VCExprVar */ BlkCorrect = BlockVariableMap(impl.Blocks, "_correct", gen);
+ Hashtable/* Block --> VCExprVar */ BlkReached = reach ? BlockVariableMap(impl.Blocks, "_reached", gen) : null;
+
+ List<Block!> blocks = impl.Blocks;
+ // block sorting is now done on the VCExpr
+ // if (!local && ((!)CommandLineOptions.Clo.TheProverFactory).NeedsBlockSorting) {
+ // blocks = SortBlocks(blocks);
+ // }
+
+ VCExpr proofObligation;
+ if (!local) {
+ proofObligation = (VCExprVar!)BlkCorrect[impl.Blocks[0]];
+ } else {
+ List<VCExpr!> conjuncts = new List<VCExpr!>(blocks.Count);
+ foreach (Block! b in blocks) {
+ VCExpr v = (VCExprVar!)BlkCorrect[b];
+ conjuncts.Add(v);
+ }
+ proofObligation = gen.NAry(VCExpressionGenerator.AndOp, conjuncts);
+ }
+
+ VCContext! context = new VCContext(label2absy, proverCtxt);
+
+ List<VCExprLetBinding!> programSemantics = new List<VCExprLetBinding!>(blocks.Count);
+ foreach (Block! b in blocks) {
+ /*
+ * In block mode,
+ * For a return block A, generate:
+ * A_correct <== wp(A_body, true) [post-condition has been translated into an assert]
+ * For all other blocks, generate:
+ * A_correct <== wp(A_body, (/\ S \in Successors(A) :: S_correct))
+ *
+ * In doomed mode, proceed as in block mode, except for a return block A, generate:
+ * A_correct <== wp(A_body, false) [post-condition has been translated into an assert]
+ *
+ * In block reach mode, the wp(A_body,...) in the equations above change to:
+ * A_reached ==> wp(A_body,...)
+ * and the conjunction above changes to:
+ * (/\ S \in Successors(A) :: S_correct \/ (\/ T \in Successors(A) && T != S :: T_reached))
+ *
+ * In local mode, generate:
+ * A_correct <== wp(A_body, true)
+ */
+ VCExpr! SuccCorrect;
+ if (local) {
+ SuccCorrect = VCExpressionGenerator.True;
+ } else {
+ SuccCorrect = SuccessorsCorrect(b, BlkCorrect, BlkReached, doomed, gen);
+ }
+
+ VCExpr wlp = Wlp.Block(b, SuccCorrect, context);
+ if (BlkReached != null) {
+ wlp = gen.Implies((VCExprVar!)BlkReached[b], wlp);
+ }
+
+ VCExprVar okVar = (VCExprVar!)BlkCorrect[b];
+ VCExprLetBinding binding = gen.LetBinding(okVar, wlp);
+ programSemantics.Add(binding);
+ }
+
+ return gen.Let(programSemantics, proofObligation);
+ }
+
+ private static Hashtable/* Block --> VCExprVar */! BlockVariableMap(List<Block!>! blocks, string! suffix,
+ Microsoft.Boogie.VCExpressionGenerator! gen)
+ {
+ Hashtable/* Block --> VCExprVar */ map = new Hashtable/* Block --> (Let)Variable */();
+ foreach (Block! b in blocks)
+ {
+ VCExprVar! v = gen.Variable(b.Label+suffix, Bpl.Type.Bool);
+ map.Add(b, v);
+ }
+ return map;
+ }
+
+ private static VCExpr! SuccessorsCorrect(
+ Block! b,
+ Hashtable/* Block --> VCExprVar */! BlkCorrect,
+ Hashtable/* Block --> VCExprVar */ BlkReached,
+ bool doomed,
+ Microsoft.Boogie.VCExpressionGenerator! gen)
+ {
+ VCExpr SuccCorrect = null;
+ GotoCmd gotocmd = b.TransferCmd as GotoCmd;
+ if (gotocmd != null)
+ {
+ foreach (Block! successor in (!)gotocmd.labelTargets)
+ {
+ // c := S_correct
+ VCExpr c = (VCExprVar!)BlkCorrect[successor];
+ if (BlkReached != null)
+ {
+ // c := S_correct \/ Sibling0_reached \/ Sibling1_reached \/ ...;
+ foreach (Block! successorSibling in gotocmd.labelTargets)
+ {
+ if (successorSibling != successor)
+ {
+ c = gen.Or(c, (VCExprVar!)BlkReached[successorSibling]);
+ }
+ }
+ }
+ SuccCorrect = SuccCorrect == null ? c : gen.And(SuccCorrect, c);
+ }
+ }
+ if (SuccCorrect == null) {
+ return VCExpressionGenerator.True;
+ } else if (doomed) {
+ return VCExpressionGenerator.False;
+ } else {
+ return SuccCorrect;
+ }
+ }
+
+ static VCExpr! NestedBlockVC(Implementation! impl,
+ Hashtable/*<int, Absy!>*/! label2absy,
+ bool reach,
+ ProverContext! proverCtxt)
+ requires impl.Blocks.Count != 0;
+ {
+ VCExpressionGenerator! gen = proverCtxt.ExprGen;
+ Graph<Block>! g = GraphFromImpl(impl);
+
+ Hashtable/* Block --> VCExprVar */ BlkCorrect = BlockVariableMap(impl.Blocks, "_correct", gen);
+ Hashtable/* Block --> VCExprVar */ BlkReached = reach ? BlockVariableMap(impl.Blocks, "_reached", gen) : null;
+
+ Block! startBlock = (!) impl.Blocks[0];
+ VCExpr proofObligation = (VCExprVar!)BlkCorrect[startBlock];
+
+ VCContext! context = new VCContext(label2absy, proverCtxt);
+
+ Hashtable/*Block->int*/ totalOrder = new Hashtable/*Block->int*/();
+ {
+ List<Block!> blocks = impl.Blocks;
+ // block sorting is now done on the VCExpr
+ // if (((!)CommandLineOptions.Clo.TheProverFactory).NeedsBlockSorting) {
+ // blocks = SortBlocks(blocks);
+ // }
+ int i = 0;
+ foreach (Block b in blocks) {
+ totalOrder[b] = i;
+ i++;
+ }
+ }
+
+ VCExprLetBinding programSemantics = NestedBlockEquation((!)impl.Blocks[0], BlkCorrect, BlkReached, totalOrder, context, g, gen);
+ List<VCExprLetBinding!> ps = new List<VCExprLetBinding!>(1);
+ ps.Add(programSemantics);
+
+ return gen.Let(ps, proofObligation);
+ }
+
+ private static VCExprLetBinding! NestedBlockEquation(Block! b,
+ Hashtable/*Block-->VCExprVar*/! BlkCorrect,
+ Hashtable/*Block-->VCExprVar*/ BlkReached,
+ Hashtable/*Block->int*/! totalOrder,
+ VCContext! context,
+ Graph<Block>! g,
+ Microsoft.Boogie.VCExpressionGenerator! gen)
+ {
+ /*
+ * For a block b, return:
+ * LET_BINDING b_correct = wp(b_body, X)
+ * where X is:
+ * LET (THOSE d \in DirectDominates(b) :: BlockEquation(d))
+ * IN (/\ s \in Successors(b) :: s_correct)
+ *
+ * When the VC-expression generator does not support LET expresions, this
+ * will eventually turn into:
+ * b_correct <== wp(b_body, X)
+ * where X is:
+ * (/\ s \in Successors(b) :: s_correct)
+ * <==
+ * (/\ d \in DirectDominatees(b) :: BlockEquation(d))
+ *
+ * In both cases above, if BlkReached is non-null, then the wp expression
+ * is instead:
+ * b_reached ==> wp(b_body, X)
+ */
+
+ VCExpr! SuccCorrect = SuccessorsCorrect(b, BlkCorrect, null, false, gen);
+
+ List<VCExprLetBinding!> bindings = new List<VCExprLetBinding!>();
+ foreach (Block! dominee in GetSortedBlocksImmediatelyDominatedBy(g, b, totalOrder))
+ {
+ VCExprLetBinding c = NestedBlockEquation(dominee, BlkCorrect, BlkReached, totalOrder, context, g, gen);
+ bindings.Add(c);
+ }
+
+ VCExpr X = gen.Let(bindings, SuccCorrect);
+ VCExpr wlp = Wlp.Block(b, X, context);
+ if (BlkReached != null) {
+ wlp = gen.Implies((VCExprVar!)BlkReached[b], wlp);
+ }
+ VCExprVar okVar = (VCExprVar!)BlkCorrect[b];
+ return gen.LetBinding(okVar, wlp);
+ }
+
+ /// <summary>
+ /// Returns a list of g.ImmediatelyDominatedBy(b), but in a sorted order, hoping to steer around
+ /// the nondeterminism problems we've been seeing by using just this call.
+ /// </summary>
+ static List<Block!>! GetSortedBlocksImmediatelyDominatedBy(Graph<Block>! g, Block! b, Hashtable/*Block->int*/! totalOrder) {
+ List<Block!> list = new List<Block!>();
+ foreach (Block! dominee in g.ImmediatelyDominatedBy(b)) {
+ list.Add(dominee);
+ }
+ list.Sort(new Comparison<Block!>(delegate (Block! x, Block! y) {return (int)(!)totalOrder[x] - (int)(!)totalOrder[y];} ));
+ return list;
+ }
+
+ static VCExpr! VCViaStructuredProgram
+ (Implementation! impl, Hashtable/*<int, Absy!>*/! label2absy,
+ ProverContext! proverCtxt)
+ {
+ #region Convert block structure back to a "regular expression"
+ RE! r = DAG2RE.Transform((!)impl.Blocks[0]);
+ #endregion
+
+ VCContext! ctxt = new VCContext(label2absy, proverCtxt);
+ #region Send wlp(program,true) to Simplify
+ return Wlp.RegExpr(r, VCExpressionGenerator.True, ctxt);
+ #endregion
+ }
+
+ /// <summary>
+ /// Remove the empty blocks reachable from the block.
+ /// It changes the visiting state of the blocks, so that if you want to visit again the blocks, you have to reset them...
+ /// </summary>
+ static BlockSeq! RemoveEmptyBlocks(Block! b)
+ {
+ assert b.TraversingStatus == Block.VisitState.ToVisit;
+ BlockSeq! retVal = removeEmptyBlocksWorker(b);
+
+ return retVal;
+ }
+
+ private static BlockSeq! removeEmptyBlocksWorker(Block! b)
+ {
+ BlockSeq bs = new BlockSeq();
+ GotoCmd gtc = b.TransferCmd as GotoCmd;
+
+ // b has no successors
+ if (gtc == null || gtc.labelTargets == null || gtc.labelTargets.Length == 0)
+ {
+ if (b.Cmds.Length != 0){ // only empty blocks are removed...
+ bs.Add(b);
+ }
+ return bs;
+ }
+ else if(b.TraversingStatus == Block.VisitState.ToVisit) // if b has some successors and we have not seen it so far...
+ {
+ b.TraversingStatus = Block.VisitState.BeingVisited;
+
+ // recursively call this method on each successor
+ // merge result into a *set* of blocks
+ Dictionary<Block,bool> mergedSuccessors = new Dictionary<Block,bool>();
+ foreach (Block! dest in gtc.labelTargets){
+ BlockSeq! ys = removeEmptyBlocksWorker(dest);
+ foreach (Block successor in ys){
+ if (!mergedSuccessors.ContainsKey(successor))
+ mergedSuccessors.Add(successor,true);
+ }
+ }
+ b.TraversingStatus = Block.VisitState.AlreadyVisited;
+
+ BlockSeq setOfSuccessors = new BlockSeq();
+ if (mergedSuccessors.Keys != null)
+ {
+ foreach (Block d in mergedSuccessors.Keys)
+ setOfSuccessors.Add(d);
+ }
+ if (b.Cmds.Length == 0)
+ return setOfSuccessors;
+ // otherwise, update the list of successors of b to be the blocks in setOfSuccessors
+ gtc.labelTargets = setOfSuccessors;
+ gtc.labelNames = new StringSeq();
+ foreach (Block! d in setOfSuccessors)
+ gtc.labelNames.Add(d.Label);
+ return new BlockSeq(b);
+ }
+ else // b has some successors, but we are already visiting it, or we have already visited it...
+ {
+ return new BlockSeq(b);
+ }
+ }
+
+ static Graph<Block>! GraphFromImpl(Implementation! impl) {
+ Graph<Block>! g = new Graph<Block>();
+ g.AddSource((!)impl.Blocks[0]); // there is always at least one node in the graph
+ foreach (Block! b in impl.Blocks)
+ {
+ GotoCmd gtc = b.TransferCmd as GotoCmd;
+ if (gtc != null)
+ {
+ foreach (Block! dest in (!)gtc.labelTargets)
+ {
+ g.AddEdge(b,dest);
+ }
+ }
+ }
+ return g;
+ }
+
+
+ static void DumpMap(Hashtable /*Variable->Expr*/! map) {
+ foreach (DictionaryEntry de in map) {
+ Variable! v = (Variable!)de.Key;
+ Expr! e = (Expr!)de.Value;
+ Console.Write(" ");
+ v.Emit(new TokenTextWriter("<console>", Console.Out, false), 0);
+ Console.Write(" --> ");
+ e.Emit(new TokenTextWriter("<console>", Console.Out, false));
+ Console.WriteLine();
+ }
+ }
+ }
+}
diff --git a/Source/VCGeneration/VCDoomed.ssc b/Source/VCGeneration/VCDoomed.ssc
new file mode 100644
index 00000000..0706bf2f
--- /dev/null
+++ b/Source/VCGeneration/VCDoomed.ssc
@@ -0,0 +1,817 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Threading;
+using System.IO;
+using Microsoft.Boogie;
+using Graphing;
+using AI = Microsoft.AbstractInterpretationFramework;
+using Microsoft.Contracts;
+using Microsoft.Basetypes;
+using Microsoft.Boogie.VCExprAST;
+
+namespace VC
+{
+ public class DCGen : ConditionGeneration
+ {
+
+ private Dictionary<Block, Variable!>! m_BlockReachabilityMap;
+
+
+ /// <summary>
+ /// Constructor. Initializes the theorem prover.
+ /// </summary>
+ public DCGen(Program! program, string/*?*/ logFilePath, bool appendLogFile)
+ {
+ base(program);
+ this.appendLogFile = appendLogFile;
+ this.logFilePath = logFilePath;
+ m_BlockReachabilityMap = new Dictionary<Block, Variable!>();
+ }
+
+ private class DummyErrorHandler : ProverInterface.ErrorHandler
+ {
+ public override void OnModel(IList<string!>! labels, ErrorModel errModel)
+ {}
+ public override void OnResourceExceeded(string! message)
+ {}
+ public override Absy! Label2Absy(string! label)
+ {
+ Absy! a = new Block();
+ return a;
+ }
+ }
+
+ /// <summary>
+ /// MSchaef: Todo: Write a good Comment
+ /// </summary>
+ public override Outcome VerifyImplementation(Implementation! impl, Program! program, VerifierCallback! callback)
+ throws UnexpectedProverOutputException;
+ {
+ // MSchaef: Just a Hack, errh is not used in this context, but required by the checker
+ DummyErrorHandler errh = new DummyErrorHandler();
+ callback.OnProgress("Whatever this stands for",0,0,0);
+ #region Transform the Program into loop-free passive form
+ variable2SequenceNumber = new Hashtable/*Variable -> int*/();
+ incarnationOriginMap = new Dictionary<Incarnation, Absy!>();
+ List<Block!>! cblocks = new List<Block!>();
+
+ impl.Blocks = DCProgramTransformer.Convert2Dag(impl, program, cblocks);
+ ComputePredecessors(impl.Blocks);
+
+ m_BlockReachabilityMap = new Dictionary<Block, Variable!>();
+ PassifyProgram(impl, program);
+ #endregion
+
+ Checker! checker = FindCheckerFor(impl, 1000);
+// Checker! checker = new Checker(this, program, logFilePath, false, impl, 1000);
+
+ Hashtable label2absy;
+ VCExpr! vc = GenerateEVC(impl, out label2absy, checker);
+
+ checker.PushVCExpr(vc);
+
+
+ foreach (Block! b in impl.Blocks)
+ {
+ #region Find out if one needs to check this one, or if the error is propagated (HACK)
+ if (!cblocks.Contains(b)) continue;
+
+ bool _needscheck = false;
+ foreach (Block! b_ in b.Predecessors)
+ {
+ GotoCmd gtc = b_.TransferCmd as GotoCmd;
+ if (gtc!=null && gtc.labelTargets!=null && gtc.labelTargets.Length>1)
+ {
+ _needscheck=true; break;
+ }
+ }
+ if (!_needscheck) continue;
+ #endregion
+
+ Variable v = null;
+ m_BlockReachabilityMap.TryGetValue(b, out v);
+ assert v!=null;
+
+ VCExpr! currentlabel = checker.TheoremProver.Context.BoogieExprTranslator.LookupVariable(v);
+ checker.BeginCheck(b.Label, currentlabel, errh);
+ WaitHandle[] wh = new WaitHandle[1];
+ ProverInterface.Outcome o = ProverInterface.Outcome.Undetermined;
+
+ wh[0] = checker.ProverDone;
+ WaitHandle.WaitAny(wh);
+ try {
+ o = checker.ReadOutcome();
+ } catch (UnexpectedProverOutputException e)
+ {
+ Console.WriteLine(e.ToString());
+ }
+
+ switch (o)
+ {
+ case ProverInterface.Outcome.Valid:
+ {
+ Console.WriteLine("In Function {0}", impl.Name);
+ Console.WriteLine("\t Block {0} has a guaranteed error!", b.Label);
+ break;
+ }
+ case ProverInterface.Outcome.Invalid:
+ {
+ break;
+ }
+ default:
+ {
+ Console.WriteLine("I'm confused about Block {0}.", b.Label);
+ break;
+ }
+ }
+
+ }
+ checker.Close();
+ return Outcome.Correct;
+ }
+
+ protected Hashtable/*TransferCmd->ReturnCmd*/! PassifyImpl(Implementation! impl, Program! program)
+ {
+ return new Hashtable();
+ }
+
+
+ #region Loop Removal
+ /// <summary>
+ /// This class is accessed only via the static method Convert2Dag
+ /// It converts the program into a loopfree one by unrolling the loop threetimes and adding the appropriate havoc
+ /// statements. The first and the last unrolling represent the first and last iteration of the loop. The second
+ /// unrolling stands for any other iteration.
+ /// </summary>
+ private class DCProgramTransformer
+ {
+ public static List<Block!>! Convert2Dag(Implementation! impl, Program! program, List<Block!>! checkableBlocks)
+ {
+ Block! start = impl.Blocks[0];
+ Dictionary<Block,GraphNode!> gd = new Dictionary<Block,GraphNode!>();
+ Set/*Block*/! beingVisited = new Set/*Block*/();
+ GraphNode gStart = GraphNode.ComputeGraphInfo(null, start, gd, beingVisited);
+
+ DCProgramTransformer pt = new DCProgramTransformer(checkableBlocks);
+ pt.LoopUnrolling(gStart, new Dictionary<GraphNode, Block!>(), true, "");
+ pt.Blocks.Reverse();
+
+ return pt.Blocks;
+ }
+
+ List<Block!>! Blocks;
+
+ private List<Block!>! m_checkableBlocks;
+
+
+ DCProgramTransformer(List<Block!>! checkableBlocks)
+ {
+ Blocks = new List<Block!>();
+ m_checkableBlocks = checkableBlocks;
+ }
+
+
+ #region Loop Unrolling Methods
+ private Block! LoopUnrolling(GraphNode! node, Dictionary<GraphNode, Block!>! visited, bool unrollable, String! prefix)
+ {
+ Block newb;
+ if (visited.TryGetValue(node, out newb))
+ {
+ assert newb!=null;
+ return newb;
+ } else
+ {
+ if (node.IsCutPoint)
+ {
+ // compute the loop body and the blocks leaving the loop
+
+ List<GraphNode!>! loopNodes = new List<GraphNode!>();
+ GatherLoopBodyNodes(node, node, loopNodes);
+
+ List<GraphNode!>! exitNodes = GatherLoopExitNodes(loopNodes);
+
+ // Continue Unrolling after the current loop
+ Dictionary<GraphNode, Block!>! _visited = new Dictionary<GraphNode, Block!>();
+ foreach (GraphNode! g in exitNodes)
+ {
+ Block b = LoopUnrolling(g, visited, unrollable, prefix);
+ _visited.Add(g,b);
+ }
+ newb = UnrollCurrentLoop(node, _visited, loopNodes,unrollable, prefix);
+ visited.Add(node,newb);
+ } else
+ {
+ BlockSeq! newSuccs = new BlockSeq();
+ foreach(GraphNode! g in node.Succecessors)
+ {
+ newSuccs.Add( LoopUnrolling(g,visited,unrollable,prefix) );
+ }
+ newb = new Block(node.Block.tok, node.Block.Label + prefix , node.Body, node.Block.TransferCmd);
+ assert newb!=null; assert newb.TransferCmd!=null;
+ if (newSuccs.Length == 0)
+ newb.TransferCmd = new ReturnCmd(newb.TransferCmd.tok);
+ else
+ newb.TransferCmd = new GotoCmd(newb.TransferCmd.tok, newSuccs);
+
+ visited.Add(node, newb);
+ Blocks.Add(newb);
+ if (unrollable)
+ {
+ m_checkableBlocks.Add(newb);
+ }
+ }
+ }
+ assert newb!=null;
+ //newb.checkable = unrollable;
+ return newb;
+ }
+
+ private Block! UnrollCurrentLoop(GraphNode! cutPoint, Dictionary<GraphNode, Block!>! visited,
+ List<GraphNode!>! loopNodes, bool unrollable, String! prefix)
+ {
+ if (unrollable)
+ {
+ Dictionary<GraphNode, Block!>! visited1 = new Dictionary<GraphNode, Block!>(visited);
+ Dictionary<GraphNode, Block!>! visited2 = new Dictionary<GraphNode, Block!>(visited);
+ Dictionary<GraphNode, Block!>! visited3 = new Dictionary<GraphNode, Block!>(visited);
+
+ Block! loopend = ConstructLoopExitBlock(cutPoint, loopNodes, visited, prefix+"#Last");
+
+ Block! last = UnrollOnce(cutPoint, loopend,visited1,false, prefix+"#Last");
+ AddHavocCmd(last,loopNodes);
+
+
+ Block! arb = UnrollOnce(cutPoint, last,visited2,true, prefix+"#Arb");
+ AddHavocCmd(arb,loopNodes);
+
+
+ BlockSeq! succ = new BlockSeq();
+ succ.Add(last); succ.Add(arb);
+ assert arb.TransferCmd!=null;
+ Block! tmp = new Block(arb.tok, arb.Label + prefix+"#Dummy" , new CmdSeq(), new GotoCmd(arb.TransferCmd.tok, succ));
+ Blocks.Add(tmp);
+ m_checkableBlocks.Add(tmp);
+
+ Block! first = UnrollOnce(cutPoint, tmp,visited3,false, prefix+"#First");
+
+ return first;
+
+ } else
+ {
+ Dictionary<GraphNode, Block!>! visited_ = new Dictionary<GraphNode, Block!>(visited);
+ Block! loopend = AbstractIteration(cutPoint, prefix+"#UR");
+ Block! ret = UnrollOnce(cutPoint, loopend,visited_,false, prefix);
+ AddHavocCmd(ret, loopNodes);
+ return ret;
+ }
+ }
+
+ private Block! UnrollOnce(GraphNode! node, Block! nextIter, Dictionary<GraphNode, Block!>! visited, bool unrollable, String! prefix)
+ {
+ visited.Add(node, nextIter);
+ Block newb;
+ BlockSeq! newSuccs = new BlockSeq();
+ foreach(GraphNode! g in node.Succecessors)
+ {
+ newSuccs.Add( LoopUnrolling(g,visited,unrollable,prefix) );
+ }
+ newb = new Block(node.Block.tok, node.Block.Label + prefix , node.Body, node.Block.TransferCmd);
+ assert newb!=null; assert newb.TransferCmd!=null;
+ if (newSuccs.Length == 0)
+ newb.TransferCmd = new ReturnCmd(newb.TransferCmd.tok);
+ else
+ newb.TransferCmd = new GotoCmd(newb.TransferCmd.tok, newSuccs);
+
+ Blocks.Add(newb);
+ if (unrollable) m_checkableBlocks.Add(newb);
+ return newb;
+ }
+
+ private Block! AbstractIteration(GraphNode! node, String! prefix)
+ {
+ CmdSeq body = new CmdSeq();
+ foreach (Cmd! c in node.Body)
+ {
+ if (c is PredicateCmd || c is CommentCmd)
+ body.Add(c);
+ else
+ break;
+ }
+ body.Add(new AssumeCmd(node.Block.tok, Expr.False) );
+ TransferCmd! tcmd = new ReturnCmd(node.Block.tok);
+ Block! b = new Block(node.Block.tok, node.Block.Label + prefix, body, tcmd);
+ Blocks.Add(b);
+ return b;
+ }
+
+ private Block! ConstructLoopExitBlock(GraphNode! cutPoint, List<GraphNode!>! loopNodes,
+ Dictionary<GraphNode, Block!>! visited, String! prefix)
+ {
+ BlockSeq! newSucc = new BlockSeq();
+ Block! orig = cutPoint.Block;
+
+ // detect the block after the loop
+ // FixMe: What happens when using break commands?
+ foreach (GraphNode! g in cutPoint.Succecessors)
+ {
+ if (!loopNodes.Contains(g))
+ {
+ Block b;
+ if (visited.TryGetValue(g,out b) )
+ newSucc.Add(b);
+ }
+ }
+ TransferCmd tcmd;
+ assert orig.TransferCmd!=null;
+ if (newSucc.Length==0)
+ tcmd = new ReturnCmd(orig.TransferCmd.tok);
+ else
+ tcmd = new GotoCmd(orig.TransferCmd.tok, newSucc);
+ // FixMe: Genertate IToken for counterexample creation
+ Block! newb = new Block(orig.tok, orig.Label+prefix+"#Leave", orig.Cmds, tcmd);
+ Blocks.Add(newb);
+ m_checkableBlocks.Add(newb);
+ return newb;
+ }
+
+
+ private void GatherLoopBodyNodes(GraphNode! current, GraphNode! cutPoint, List<GraphNode!>! loopNodes)
+ {
+ loopNodes.Add(current);
+ if (false) System.Diagnostics.Debugger.Break();
+ foreach (GraphNode! g in current.Predecessors)
+ {
+ if (cutPoint.firstPredecessor == g || g == cutPoint || loopNodes.Contains(g) ) continue;
+ GatherLoopBodyNodes(g, cutPoint, loopNodes);
+ }
+ }
+
+ private List<GraphNode!>! GatherLoopExitNodes(List<GraphNode!>! loopNodes)
+ {
+ List<GraphNode!>! exitnodes = new List<GraphNode!>();
+
+ foreach (GraphNode! g in loopNodes)
+ {
+ foreach (GraphNode! s in g.Succecessors)
+ {
+ if (!loopNodes.Contains(s) /*&& !exitnodes.Contains(s)*/ ) exitnodes.Add(s);
+ }
+ }
+ return exitnodes;
+ }
+
+ private void AddHavocCmd(Block! b, List<GraphNode!>! loopNodes)
+ {
+ List<Block!>! loopBlocks = new List<Block!>();
+ foreach (GraphNode! g in loopNodes) loopBlocks.Add(g.Block);
+ HavocCmd! hcmd = HavocLoopTargets(loopBlocks,b.tok);
+ CmdSeq! body = new CmdSeq();
+ body.Add(hcmd);
+ body.AddRange(b.Cmds);
+ b.Cmds = body;
+ }
+
+ private HavocCmd! HavocLoopTargets(List<Block!>! bl, IToken! tok)
+ {
+ VariableSeq varsToHavoc = new VariableSeq();
+ foreach ( Block! b in bl )
+ {
+ foreach ( Cmd! c in b.Cmds )
+ {
+ c.AddAssignedVariables(varsToHavoc);
+ }
+ }
+ IdentifierExprSeq havocExprs = new IdentifierExprSeq();
+ foreach ( Variable! v in varsToHavoc )
+ {
+ IdentifierExpr ie = new IdentifierExpr(Token.NoToken, v);
+ if(!havocExprs.Has(ie))
+ havocExprs.Add(ie);
+ }
+ // pass the token of the enclosing loop header to the HavocCmd so we can reconstruct
+ // the source location for this later on
+ return new HavocCmd(tok,havocExprs);
+ }
+
+ #endregion
+
+
+ #region GraphNode
+ private class GraphNode
+ {
+ public readonly Block! Block;
+ public readonly CmdSeq! Body;
+ public bool IsCutPoint; // is set during ComputeGraphInfo
+ [Rep] public readonly List<GraphNode!>! Predecessors = new List<GraphNode!>();
+ [Rep] public readonly List<GraphNode!>! Succecessors = new List<GraphNode!>();
+ public GraphNode firstPredecessor;
+
+ GraphNode(Block! b, CmdSeq! body)
+ {
+ Block = b; Body = body;
+ IsCutPoint = false;
+ }
+
+ static CmdSeq! GetOptimizedBody(CmdSeq! cmds)
+ {
+ int n = 0;
+ foreach (Cmd c in cmds)
+ {
+ n++;
+ PredicateCmd pc = c as PredicateCmd;
+ if (pc != null && pc.Expr is LiteralExpr && ((LiteralExpr)pc.Expr).IsFalse)
+ {
+ // return a sequence consisting of the commands seen so far
+ Cmd[] s = new Cmd[n];
+ for (int i = 0; i < n; i++)
+ {
+ s[i] = cmds[i];
+ }
+ return new CmdSeq(s);
+ }
+ }
+ return cmds;
+ }
+
+ public static GraphNode! ComputeGraphInfo(GraphNode from, Block! b, Dictionary<Block,GraphNode!>! gd, Set /*Block*/! beingVisited)
+ {
+ GraphNode g;
+ if (gd.TryGetValue(b, out g))
+ {
+ assume from != null;
+ assert g != null;
+ g.Predecessors.Add(from);
+ if (g.firstPredecessor==null)
+ g.firstPredecessor = from;
+
+ if (beingVisited.Contains(b))
+ g.IsCutPoint = true; // it's a cut point
+ } else
+ {
+ CmdSeq body = GetOptimizedBody(b.Cmds);
+ g = new GraphNode(b, body);
+ gd.Add(b, g);
+ if (from != null)
+ {
+ g.Predecessors.Add(from);
+ if (from==null)
+ g.firstPredecessor = g;
+
+ if (g.firstPredecessor==null)
+ g.firstPredecessor = from;
+
+ }
+ if (body != b.Cmds)
+ {
+ // the body was optimized -- there is no way through this block
+ } else
+ {
+ beingVisited.Add(b);
+ GotoCmd gcmd = b.TransferCmd as GotoCmd;
+ if (gcmd != null)
+ {
+ assume gcmd.labelTargets != null;
+ foreach (Block! succ in gcmd.labelTargets)
+ {
+ g.Succecessors.Add( ComputeGraphInfo(g, succ, gd, beingVisited) );
+ }
+ }
+ beingVisited.Remove(b);
+ }
+ }
+ return g;
+ }
+ }
+ #endregion
+
+ }
+ #endregion
+
+ #region Program Passification
+
+ private Hashtable/*TransferCmd->ReturnCmd*/! PassifyProgram(Implementation! impl,
+ Program! program)
+ {
+ Hashtable gotoCmdOrigins = new Hashtable();
+ Block! exitBlock = GenerateUnifiedExit(impl, gotoCmdOrigins);
+ AddBlocksBetween(impl);
+ GenerateReachabilityPredicates(impl, exitBlock);
+
+ current_impl = impl;
+ Convert2PassiveCmd(impl);
+ impl = current_impl;
+ return new Hashtable();
+ }
+
+ /// <summary>
+ /// Add additional variable to allow checking as described in the paper
+ /// "It's doomed; we can prove it"
+ /// </summary>
+ private void GenerateReachabilityPredicates(Implementation! impl, Block! exitBlock)
+ {
+ ExprSeq! es = new ExprSeq();
+ Cmd eblockcond = null;
+
+ foreach (Block! b in impl.Blocks)
+ {
+ //if (b.Predecessors.Length==0) continue;
+ //if (b.Cmds.Length == 0 ) continue;
+
+ Variable v_ = new LocalVariable(Token.NoToken,
+ new TypedIdent(b.tok, b.Label+"__ivebeenthere",new BasicType(SimpleType.Bool) ) );
+
+ impl.LocVars.Add(v_);
+
+ m_BlockReachabilityMap[b] = v_;
+
+ IdentifierExpr! lhs = new IdentifierExpr(b.tok, v_);
+
+ es.Add( new IdentifierExpr(b.tok, v_) );
+
+ List<AssignLhs!>! lhsl = new List<AssignLhs!>();
+ lhsl.Add(new SimpleAssignLhs(Token.NoToken, lhs) );
+ List<Expr!>! rhsl = new List<Expr!>();
+ rhsl.Add(Expr.True);
+
+ if (b!=exitBlock)
+ {
+ CmdSeq cs = new CmdSeq(new AssignCmd(Token.NoToken, lhsl, rhsl));
+ cs.AddRange(b.Cmds);
+ b.Cmds = cs;
+ } else
+ {
+ eblockcond = new AssignCmd(Token.NoToken, lhsl, rhsl);
+ }
+
+ //checkBlocks.Add(new CheckableBlock(v_,b));
+ }
+ if (es.Length==0) return;
+
+ Expr aexp = null;
+
+ if (es.Length==1)
+ {
+ aexp = es[0];
+ } else if (es.Length==2)
+ {
+ aexp = Expr.Binary(Token.NoToken,
+ BinaryOperator.Opcode.And,
+ (!)es[0],
+ (!)es[1]);
+ } else
+ {
+ aexp = Expr.True;
+ foreach (Expr e_ in es)
+ {
+ aexp = Expr.Binary(Token.NoToken,
+ BinaryOperator.Opcode.And,
+ (!)e_, aexp);
+ }
+ }
+ assert (aexp!=null);
+ assert (eblockcond!=null);
+
+ AssumeCmd ac = new AssumeCmd(Token.NoToken, aexp);
+
+ assert(exitBlock!=null);
+
+ CmdSeq cseq = new CmdSeq(eblockcond);
+ cseq.AddRange(exitBlock.Cmds);
+ cseq.Add(ac);
+
+ exitBlock.Cmds = cseq;
+ }
+
+ #endregion
+
+ #region Error Verification Condition Generation
+
+ VCExpr! GenerateEVC(Implementation! impl, out Hashtable label2absy, Checker! ch)
+ {
+ TypecheckingContext tc = new TypecheckingContext(null);
+ impl.Typecheck(tc);
+ label2absy = new Hashtable/*<int, Absy!>*/();
+ VCExpr! vc;
+ switch (CommandLineOptions.Clo.vcVariety) {
+ case CommandLineOptions.VCVariety.Structured:
+ //vc = VCViaStructuredProgram(impl, label2absy, ch.TheoremProver.Context);
+ //break;
+ Console.WriteLine("Not Implemented: This should be unreachable");
+ assert false;
+ case CommandLineOptions.VCVariety.Block:
+ //vc = FlatBlockVC(impl, label2absy, false, false, false, ch.TheoremProver.Context);
+ Console.WriteLine("Not Implemented: This should be unreachable");
+ assert false;
+ break;
+ case CommandLineOptions.VCVariety.BlockReach:
+ //vc = FlatBlockVC(impl, label2absy, false, true, false, ch.TheoremProver.Context);
+ Console.WriteLine("Not Implemented: This should be unreachable");
+ assert false;
+ break;
+ case CommandLineOptions.VCVariety.Local:
+ //vc = FlatBlockVC(impl, label2absy, true, false, false, ch.TheoremProver.Context);
+ Console.WriteLine("Not Implemented: This should be unreachable");
+ assert false;
+ break;
+ case CommandLineOptions.VCVariety.BlockNested:
+// vc = NestedBlockVC(impl, label2absy, false, ch.TheoremProver.Context);
+// break;
+ Console.WriteLine("Not Implemented: This should be unreachable");
+ assert false;
+ case CommandLineOptions.VCVariety.BlockNestedReach:
+// vc = NestedBlockVC(impl, label2absy, true, ch.TheoremProver.Context);
+// break;
+ Console.WriteLine("Not Implemented: This should be unreachable");
+ assert false;
+ case CommandLineOptions.VCVariety.Dag:
+ if (((!)CommandLineOptions.Clo.TheProverFactory).SupportsDags)
+ {
+// vc = DagVC((!)impl.Blocks[0], label2absy, new Hashtable/*<Block, VCExpr!>*/(), ch.TheoremProver.Context);
+ Console.WriteLine("Not Implemented: This should be unreachable");
+ assert false;
+ } else {
+ vc = LetVC((!)impl.Blocks[0], label2absy, ch.TheoremProver.Context);
+ }
+ break;
+ case CommandLineOptions.VCVariety.Doomed:
+ //vc = FlatBlockVC(impl, label2absy, false, false, true, ch.TheoremProver.Context);
+ vc = LetVC((!)impl.Blocks[0], label2absy, ch.TheoremProver.Context);
+ break;
+ default:
+ assert false; // unexpected enumeration value
+ }
+ return vc;
+ }
+
+ private Hashtable/* Block --> VCExprVar */! BlockVariableMap(List<Block!>! blocks, string! suffix,
+ Microsoft.Boogie.VCExpressionGenerator! gen)
+ {
+ Hashtable/* Block --> VCExprVar */ map = new Hashtable/* Block --> (Let)Variable */();
+ foreach (Block! b in blocks)
+ {
+ VCExprVar! v = gen.Variable(b.Label+suffix, Microsoft.Boogie.Type.Bool);
+ map.Add(b, v);
+ }
+ return map;
+ }
+
+ VCExpr! FlatBlockVC(Implementation! impl,
+ Hashtable/*<int, Absy!>*/! label2absy,
+ bool local, bool reach, bool doomed,
+ ProverContext! proverCtxt)
+ requires local ==> !reach; // "reach" must be false for local
+ {
+ VCExpressionGenerator! gen = proverCtxt.ExprGen;
+ Hashtable/* Block --> VCExprVar */ BlkCorrect = BlockVariableMap(impl.Blocks, "_correct", gen);
+ Hashtable/* Block --> VCExprVar */ BlkReached = reach ? BlockVariableMap(impl.Blocks, "_reached", gen) : null;
+
+ List<Block!> blocks = impl.Blocks;
+ // block sorting is now done on the VCExpr
+ // if (!local && ((!)CommandLineOptions.Clo.TheProverFactory).NeedsBlockSorting) {
+ // blocks = SortBlocks(blocks);
+ // }
+
+ VCExpr proofObligation;
+ if (!local) {
+ proofObligation = (VCExprVar!)BlkCorrect[impl.Blocks[0]];
+ } else {
+ List<VCExpr!> conjuncts = new List<VCExpr!>(blocks.Count);
+ foreach (Block! b in blocks) {
+ VCExpr v = (VCExprVar!)BlkCorrect[b];
+ conjuncts.Add(v);
+ }
+ proofObligation = gen.NAry(VCExpressionGenerator.AndOp, conjuncts);
+ }
+
+ VCContext! context = new VCContext(label2absy, proverCtxt);
+// m_Context = context;
+
+ List<VCExprLetBinding!> programSemantics = new List<VCExprLetBinding!>(blocks.Count);
+ foreach (Block! b in blocks) {
+ VCExpr! SuccCorrect;
+ if (local) {
+ SuccCorrect = VCExpressionGenerator.False; // Martin: Changed from true to false
+ } else {
+ SuccCorrect = SuccessorsCorrect(b, BlkCorrect, BlkReached, doomed, gen);
+ }
+
+ VCExpr wlp = Wlp.Block(b, SuccCorrect, context);
+ if (BlkReached != null) {
+ //wlp = gen.Implies((VCExprVar!)BlkReached[b], wlp);
+ }
+
+ VCExprVar okVar = (VCExprVar!)BlkCorrect[b];
+ VCExprLetBinding binding = gen.LetBinding(okVar, wlp);
+ programSemantics.Add(binding);
+ }
+
+ return gen.Let(programSemantics, proofObligation);
+ }
+
+ static VCExpr! SuccessorsCorrect(
+ Block! b,
+ Hashtable/* Block --> VCExprVar */! BlkCorrect,
+ Hashtable/* Block --> VCExprVar */ BlkReached,
+ bool doomed,
+ Microsoft.Boogie.VCExpressionGenerator! gen)
+ {
+ VCExpr SuccCorrect = null;
+ GotoCmd gotocmd = b.TransferCmd as GotoCmd;
+ if (gotocmd != null)
+ {
+ foreach (Block! successor in (!)gotocmd.labelTargets)
+ {
+ // c := S_correct
+ VCExpr c = (VCExprVar!)BlkCorrect[successor];
+
+ if (BlkReached != null)
+ {
+ // c := S_correct \/ Sibling0_reached \/ Sibling1_reached \/ ...;
+ foreach (Block! successorSibling in gotocmd.labelTargets)
+ {
+ if (successorSibling != successor)
+ {
+ //don't know what this is good for
+ // c = gen.Or(c, (VCExprVar!)BlkReached[successorSibling]);
+ }
+ }
+ }
+ SuccCorrect = SuccCorrect == null ? c : gen.And(SuccCorrect, c);
+ }
+ }
+ if (SuccCorrect == null) {
+ //return VCExpressionGenerator.True;
+ return VCExpressionGenerator.False;
+ } else if (doomed) {
+ return VCExpressionGenerator.False;
+ } else {
+ return SuccCorrect;
+ }
+ }
+
+
+
+ VCExpr! LetVC(Block! startBlock,
+ Hashtable/*<int, Absy!>*/! label2absy,
+ ProverContext! proverCtxt)
+ {
+ Hashtable/*<Block, LetVariable!>*/! blockVariables = new Hashtable/*<Block, LetVariable!!>*/();
+ List<VCExprLetBinding!>! bindings = new List<VCExprLetBinding!>();
+ VCExpr startCorrect = LetVC(startBlock, label2absy, blockVariables, bindings, proverCtxt);
+ //return proverCtxt.ExprGen.Let(bindings, startCorrect);
+ return proverCtxt.ExprGen.Let(bindings, proverCtxt.ExprGen.Not(startCorrect) );
+ }
+
+ VCExpr! LetVC(Block! block,
+ Hashtable/*<int, Absy!>*/! label2absy,
+ Hashtable/*<Block, VCExprVar!>*/! blockVariables,
+ List<VCExprLetBinding!>! bindings,
+ ProverContext! proverCtxt)
+ {
+ VCExpressionGenerator! gen = proverCtxt.ExprGen;
+ VCExprVar v = (VCExprVar)blockVariables[block];
+ if (v == null) {
+ /*
+ * For block A (= block), generate:
+ * LET_binding A_correct = wp(A_body, (/\ S \in Successors(A) :: S_correct))
+ * with the side effect of adding the let bindings to "bindings" for any
+ * successor not yet visited.
+ */
+ VCExpr SuccCorrect;
+ GotoCmd gotocmd = block.TransferCmd as GotoCmd;
+ if (gotocmd == null) {
+ SuccCorrect = VCExpressionGenerator.False; // FixMe: changed from true to false
+ } else {
+ assert gotocmd.labelTargets != null;
+ List<VCExpr!> SuccCorrectVars = new List<VCExpr!>(gotocmd.labelTargets.Length);
+ foreach (Block! successor in gotocmd.labelTargets) {
+ VCExpr s = LetVC(successor, label2absy, blockVariables, bindings, proverCtxt);
+ SuccCorrectVars.Add(s);
+ }
+ SuccCorrect = gen.NAry(VCExpressionGenerator.AndOp, SuccCorrectVars);
+ }
+
+ VCContext context = new VCContext(label2absy, proverCtxt);
+// m_Context = context;
+ VCExpr vc = Wlp.Block(block, SuccCorrect, context);
+
+ v = gen.Variable(block.Label + "_correct", Microsoft.Boogie.Type.Bool);
+ bindings.Add(gen.LetBinding(v, vc));
+ blockVariables.Add(block, v);
+ }
+ return v;
+ }
+
+
+
+ #endregion
+
+ }
+} \ No newline at end of file
diff --git a/Source/VCGeneration/VCGeneration.sscproj b/Source/VCGeneration/VCGeneration.sscproj
new file mode 100644
index 00000000..70f4055d
--- /dev/null
+++ b/Source/VCGeneration/VCGeneration.sscproj
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="utf-8"?>
+<VisualStudioProject>
+ <XEN ProjectType="Local"
+ SchemaVersion="1.0"
+ Name="VCGeneration"
+ ProjectGuid="f65666de-fb56-457c-8782-09be243450fc"
+ >
+ <Build>
+ <Settings ApplicationIcon=""
+ AssemblyName="VCGeneration"
+ OutputType="Library"
+ RootNamespace="VCGeneration"
+ StartupObject=""
+ StandardLibraryLocation=""
+ TargetPlatform="v2"
+ TargetPlatformLocation=""
+ >
+ <Config Name="Debug"
+ AllowUnsafeBlocks="False"
+ BaseAddress="285212672"
+ CheckForOverflowUnderflow="False"
+ ConfigurationOverrideFile=""
+ DefineConstants="DEBUG;TRACE"
+ DocumentationFile=""
+ DebugSymbols="True"
+ FileAlignment="4096"
+ IncrementalBuild="True"
+ Optimize="False"
+ OutputPath="bin\debug"
+ RegisterForComInterop="False"
+ RemoveIntegerChecks="false"
+ TreatWarningsAsErrors="False"
+ WarningLevel="4"
+ ReferenceTypesAreNonNullByDefault="False"
+ RunProgramVerifier="False"
+ RunProgramVerifierWhileEditing="False"
+ ProgramVerifierCommandLineOptions=""
+ AllowPointersToManagedStructures="False"
+ CheckContractAdmissibility="True"
+ CheckPurity="False"
+ />
+ <Config Name="Release"
+ AllowUnsafeBlocks="false"
+ BaseAddress="285212672"
+ CheckForOverflowUnderflow="false"
+ ConfigurationOverrideFile=""
+ DefineConstants="TRACE"
+ DocumentationFile=""
+ DebugSymbols="false"
+ FileAlignment="4096"
+ IncrementalBuild="false"
+ Optimize="true"
+ OutputPath="bin\release"
+ RegisterForComInterop="false"
+ RemoveIntegerChecks="false"
+ TreatWarningsAsErrors="false"
+ WarningLevel="4"
+ />
+ </Settings>
+ <References>
+ <Reference Name="System"
+ AssemblyName="System"
+ Private="false"
+ />
+ <Reference Name="System.Data"
+ AssemblyName="System.Data"
+ Private="false"
+ />
+ <Reference Name="System.Xml"
+ AssemblyName="System.Xml"
+ Private="false"
+ />
+ <Reference Name="Core"
+ Project="{47BC34F1-A173-40BE-84C2-9332B4418387}"
+ Private="true"
+ />
+ <Reference Name="AIFramework"
+ Project="{24B55172-AD8B-47D1-8952-5A95CFDB9B31}"
+ Private="true"
+ />
+ <Reference Name="Graph"
+ Project="{4C28FB90-630E-4B55-A937-11A011B79765}"
+ Private="true"
+ />
+ <Reference Name="Mscorlib.Contracts"
+ AssemblyName="Mscorlib.Contracts"
+ Private="false"
+ HintPath="../../Binaries/Mscorlib.Contracts.dll"
+ />
+ <Reference Name="System.Contracts"
+ AssemblyName="System.Contracts"
+ Private="false"
+ HintPath="../../Binaries/System.Contracts.dll"
+ />
+ <Reference Name="Basetypes"
+ Project="{0C692837-77EC-415F-BF04-395E3ED06E9A}"
+ Private="true"
+ />
+ <Reference Name="VCExpr"
+ Project="{CF42B700-10AA-4DA9-8992-48A800251C11}"
+ Private="true"
+ />
+ </References>
+ </Build>
+ <Files>
+ <Include>
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="Check.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="VC.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="Wlp.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="..\version.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="Context.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="OrderingAxioms.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="ConditionGeneration.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="VCDoomed.ssc"
+ />
+ </Include>
+ </Files>
+ </XEN>
+</VisualStudioProject> \ No newline at end of file
diff --git a/Source/VCGeneration/Wlp.ssc b/Source/VCGeneration/Wlp.ssc
new file mode 100644
index 00000000..d7692245
--- /dev/null
+++ b/Source/VCGeneration/Wlp.ssc
@@ -0,0 +1,131 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Collections;
+using Microsoft.Boogie;
+using Microsoft.Boogie.VCExprAST;
+using Microsoft.Contracts;
+
+namespace VC {
+ public class VCContext
+ {
+ [Rep] public readonly Hashtable/*<int, Absy!>*/! Label2absy;
+ [Rep] public readonly ProverContext! Ctxt;
+
+ public VCContext(Hashtable/*<int, Absy!>*/! label2absy, ProverContext! ctxt)
+ {
+ this.Label2absy = label2absy;
+ this.Ctxt = ctxt;
+ // base();
+ }
+ }
+
+ #region A class to compute wlp of a passive command program
+
+ public class Wlp
+ {
+ public static VCExpr! Block(Block! b, VCExpr! N, VCContext! ctxt)
+ modifies ctxt.*;
+ {
+ VCExpressionGenerator! gen = ctxt.Ctxt.ExprGen;
+ VCExpr! res = N;
+
+ for (int i = b.Cmds.Length; --i>=0; )
+ {
+ res = Cmd((!) b.Cmds[i], res, ctxt);
+ }
+
+ int id = b.UniqueId;
+ expose (ctxt) {
+ ctxt.Label2absy[id] = b;
+ return gen.Implies(gen.LabelPos((!)id.ToString(), VCExpressionGenerator.True), res);
+ }
+ }
+
+ /// <summary>
+ /// Computes the wlp for an assert or assume command "cmd".
+ /// </summary>
+ public static VCExpr! Cmd(Cmd! cmd, VCExpr! N, VCContext! ctxt)
+ {
+ VCExpressionGenerator! gen = ctxt.Ctxt.ExprGen;
+ if (cmd is AssertCmd) {
+ AssertCmd ac = (AssertCmd)cmd;
+ VCExpr C = ctxt.Ctxt.BoogieExprTranslator.Translate(ac.Expr);
+ if (CommandLineOptions.Clo.vcVariety == CommandLineOptions.VCVariety.Doomed) {
+ return gen.Implies(C, N);
+ } else {
+ int id = ac.UniqueId;
+ ctxt.Label2absy[id] = ac;
+ switch (CommandLineOptions.Clo.UseSubsumption) {
+ case CommandLineOptions.SubsumptionOption.Never:
+ break;
+ case CommandLineOptions.SubsumptionOption.Always:
+ N = gen.Implies(C, N);
+ break;
+ case CommandLineOptions.SubsumptionOption.NotForQuantifiers:
+ if (!(C is VCExprQuantifier)) {
+ N = gen.Implies(C, N);
+ }
+ break;
+ default:
+ assert false; // unexpected case
+ }
+// (MSchaef) Hack: This line might be useless, but at least it is not harmfull
+// need to test it
+ if (CommandLineOptions.Clo.vcVariety == CommandLineOptions.VCVariety.Doomed) return gen.Implies(C, N);
+
+ return gen.And(gen.LabelNeg((!)id.ToString(), C), N);
+ }
+
+ } else if (cmd is AssumeCmd) {
+ AssumeCmd ac = (AssumeCmd)cmd;
+ return gen.Implies(ctxt.Ctxt.BoogieExprTranslator.Translate(ac.Expr), N);
+
+ } else {
+ assert false; // unexpected command
+ }
+ }
+
+ public static VCExpr! RegExpr(RE! r, VCExpr! N, VCContext! ctxt)
+ {
+ if ( r is AtomicRE )
+ {
+ AtomicRE ar = (AtomicRE) r;
+ return Block(ar.b, N, ctxt);
+ }
+ else if ( r is Sequential )
+ {
+ Sequential s = (Sequential) r;
+ return RegExpr(s.first, RegExpr(s.second, N, ctxt), ctxt);
+ }
+ else if ( r is Choice )
+ {
+ Choice ch = (Choice) r;
+ VCExpr! res;
+ if (ch.rs == null || ch.rs.Length==0)
+ {
+ res = N;
+ }
+ else
+ {
+ VCExpr currentWLP = RegExpr((!)ch.rs[0], N, ctxt);
+ for (int i = 1, n = ch.rs.Length; i < n; i++)
+ {
+ currentWLP = ctxt.Ctxt.ExprGen.And(currentWLP, RegExpr((!)ch.rs[i], N, ctxt));
+ }
+ res = currentWLP;
+ }
+ return res;
+ }
+ else
+ {
+ assert false; // unexpected RE subtype
+ }
+ }
+ }
+ #endregion
+
+}
diff --git a/Source/XAHA/XAHA.ssc b/Source/XAHA/XAHA.ssc
new file mode 100644
index 00000000..35dbaf1c
--- /dev/null
+++ b/Source/XAHA/XAHA.ssc
@@ -0,0 +1,601 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+//XAHA: eXperimental Automated History Abstraction
+
+/* The top-level declarations of the initial file contain:
+Declaration of one constant totalPhases of type int.
+An axiom may be present saying what value (any natural number) does totalPhases have.
+Declarations of shared variables v1:tp1,...,vm:tpm.
+Functions
+A(i:int,p:int,V1:[int]tp1,...,Vm:[int]tpm,V1':[int]tp1,...,Vm':[int]tpm)
+and
+G(i:int,p:int,V1:[int]tp1,...,Vm:[int]tpm,V1':[int]tp1,...,Vm':[int]tpm).
+The order of the variables in the declarations of A and G should coincide with the order
+in which the variables were declared. I.e. if the top-level declaration order is
+a:A,c:C,d:D,b:B, then A should be declared as A(int,int, A,C,D,B, A,C,D,B).
+Probably axioms specifying A(i,p,...) as the rely predicate for thread number i (0<=i<n) and phase number p (0<=p<totalPhases).
+Probably axioms specifying G(i,p,v1,...,vm) as the guarantee predicate for thread number i (0<=i<n) and phase number p (0<=p<totalPhases).
+Function describing the initial condition on the shared variables.
+Init(tp1,...,tpm);
+Procedures T0(),...,T(n-1)() such that each modifies a subset of v1,...,vm.
+All procedures except T0, ...,T(n-1) should be inlined and there should be no procedure calls.
+
+The generated output is one large BoogiePL file, written to a standard output,
+which is the intrumentation of the threads T0,...,T(n-1) with phase description checking code.
+*/
+
+
+
+
+namespace Microsoft.Boogie.XAHA {
+ using System;
+ using Microsoft.Boogie;
+ using Microsoft.Basetypes;
+ using Microsoft.Contracts;
+ using System.Collections.Generic;
+ using PureCollections;
+
+ //class which implements the history-based verification procedure
+ public class clXAHA {
+ const string! totalPhasesName="totalPhases";
+ const string! currentPhaseVarName="phase";
+ const string! threadNoVarName="threadNo";
+ Program! prg;
+
+ //initializes the internal data structures needed for processing
+ public clXAHA(Program! program) {
+ prg=program;
+ }
+
+ //Checks whether the described format has been met. If not, prints a
+ //correspondent error message to the standard error stream.
+ //Otherwise generates to the output_file (or standard output if null)
+ //another program whose correctness implies the correct specification and
+ //the correctness of the original multithreaded program.
+ public bool CheckFormatAndGenerateInstrumentation(string output_file) {
+ Constant decl_totalPhases=FindTopLevelConstByName(totalPhasesName);
+ if(decl_totalPhases==null) {
+ Console.Error.WriteLine("The constant "+totalPhasesName+" was not found as a top-level declaration of the program.");
+ return false;
+ }
+ //Check whether we are doing a bounded verification and, if yes, how many
+ //phased do we have.
+ int phases_no;
+ bool phase_number_finite=
+ GetValueOfVarFromAxiom(totalPhasesName,out phases_no);
+ string! history_pre_prefix,history_post_prefix;
+ List<Variable!>! sharedVars=GetSharedVars(out history_pre_prefix,out history_post_prefix);
+ //find predicates A and G
+ Function A=GetTopLevelFunction("A");
+ if(A==null) { Console.Error.WriteLine("The rely predicate A was not found."); return false; };
+ if(!CheckFormat(A,sharedVars)) { Console.Error.WriteLine("The rely predicate A doesn't have the right format."); return false; };
+ Function G=GetTopLevelFunction("G");
+ if(G==null) { Console.Error.WriteLine("The guarantee predicate G was not found."); return false; };
+ if(!CheckFormat(G,sharedVars)) { Console.Error.WriteLine("The guarantee predicate G doesn't have the right format."); return false; };
+ //determine how many threads are there. All procedures should be inlined.
+ //So any procedure other than T1,...,Tn is useless.
+ int n;
+ if(!CheckThreads(out n)) {
+ Console.Error.WriteLine("Error while checking procedure format.");
+ return false;
+ }
+ TokenTextWriter outstream=((output_file!=null) && (output_file.Trim()!=""))?
+ (new TokenTextWriter(output_file)):
+ (new TokenTextWriter("<console>", Console.Out));
+ if(outstream==null) { Console.Error.WriteLine("Out of memory"); return false; }
+ if(!InstrumentizeThreads(outstream,n,sharedVars,history_pre_prefix,history_post_prefix,phase_number_finite)) {
+ Console.Error.WriteLine("Thread instrumentation failed.");
+ return false;
+ }
+ if(!PrintInterleaving(outstream,n,sharedVars,history_pre_prefix,history_post_prefix,n,phase_number_finite,phases_no)) {
+ Console.Error.WriteLine("Interleaving printing failed.");
+ return false;
+ }
+ outstream.Close();
+ return true;
+ }
+
+ //In the program, find a top-level constant declaration named s and
+ //returns it.
+ public Constant FindTopLevelConstByName(string s) {
+ foreach (Declaration d in prg.TopLevelDeclarations) {
+ if((d is Constant) && d!=null) {
+ Constant dd=(Constant)d;
+ if(dd.Name==s) return dd;
+ }
+ }
+ return null;
+ }
+
+ //In the program, find a top-level axiom speaking about a certain constant
+ //like "axiom c==digits". If such an axiom is found, returns true and fills the value
+ //by digits, otherwise returns false.
+ bool GetValueOfVarFromAxiom(string s,out int value) {
+ foreach (Declaration d in prg.TopLevelDeclarations) {
+ if((d is Axiom) && (d!=null)) {
+ Axiom a=(Axiom)d;
+ Expr e=a.Expr;
+ if((e is NAryExpr) && (e!=null)) {
+ NAryExpr ne=(NAryExpr)e;
+ IAppliable f=ne.Fun;
+ if((f is BinaryOperator) && (f!=null)) {
+ BinaryOperator bo=(BinaryOperator)f;
+ if(bo.Op==BinaryOperator.Opcode.Eq) {
+ ExprSeq args=ne.Args;
+ Expr lhs=(Expr)(args.Head());
+ Expr rhs=(Expr)(args.Last());
+ //Console.Error.WriteLine(lhs.ToString());
+ if((lhs is IdentifierExpr) && (lhs!=null)) {
+ IdentifierExpr! ie=(IdentifierExpr)lhs;
+ if(ie.Name==s) {
+ if((rhs is LiteralExpr)&&(rhs!=null)) {
+ LiteralExpr! lrrhs=(LiteralExpr)rhs;
+ if((lrrhs.Val is BigNum) && (lrrhs!=null)) {
+ value=((BigNum)(lrrhs.Val)).ToIntSafe;
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ value=-1;
+ return false;
+ }
+
+ //returns the set of all possible variables of the program, whenever shared or local
+ //Set! GetAllVars() {
+ //
+ // }
+
+ //Returns the set of all shared variables (except the constants) of the program.
+ //In the pre_prefix, returns "H_", in the post_prefix, returns "HP_".
+ //TODO: In pre_prefix and post_prefix, return such strings p,p' such that for any shared
+ //variable x, px is neither a shared, nor a local variable and p'x is neither a shared nor
+ //a local variable.
+ List<Variable!>! GetSharedVars(out string! pre_prefix, out string! post_prefix) {
+ List<Variable!>! retval=new List<Variable!>();
+ foreach(Declaration d in prg.TopLevelDeclarations) {
+ if((d is Variable) && !(d is Constant) && (d!=null)) {
+ retval.Add((Variable)d);
+ }
+ }
+ pre_prefix="H_";
+ post_prefix="HP_";
+ return retval;
+ }
+
+ //returns the first function which is declared on the top level and which is called fname.
+ Function GetTopLevelFunction(string fname) {
+ foreach(Declaration d in prg.TopLevelDeclarations) {
+ if((d is Function) && (d!=null)) {
+ Function! f=(Function)d;
+ if(f.Name==fname) return f;
+ }
+ }
+ return null;
+ }//end of GetTopLevelFunction
+
+
+ //returns true if f is declared as
+ //f(int,int,[int]tv1,...,[int]tvm,[int]tv1,...,[int]tvm) returns (bool);
+ //where m is the number of shared variables and
+ //tv1,...,tvm are types of the m shared variables.
+ bool CheckFormat(Function! f,List<Variable!>! sharedVars) {
+ VariableSeq! inparams=f.InParams;
+ //first check the correct number
+ int m=sharedVars.Count;
+ if(inparams.Length!=(2+2*m)) {
+ Console.Error.WriteLine("Bad number of arguments in "+f.Name); return false;
+ }
+ //now check the types of the first portion
+ List<Variable!>.Enumerator sv_ptr=sharedVars.GetEnumerator(); sv_ptr.MoveNext();
+ SequenceEnumerator! pr_ptr=(SequenceEnumerator!)(inparams.GetEnumerator()); pr_ptr.MoveNext();
+ //check that the first two arguments are of type int, i.e. the thread and the phase numbers
+ Variable! pr_var=(Variable!)(pr_ptr.Current);
+ if(pr_var.TypedIdent.Type.ToString()!="int") {
+ Console.Error.WriteLine("Error in function "+f.Name+
+ ": the first argument denotes a thread number and should be of type int.");
+ return false;
+ }
+ pr_ptr.MoveNext();
+ pr_var=(Variable)(pr_ptr.Current);
+ if(pr_var.TypedIdent.Type.ToString()!="int") {
+ Console.Error.WriteLine("Error in function "+f.Name+
+ ": the second argument denotes a phase number and should be of type int.");
+ return false;
+ }
+ pr_ptr.MoveNext();
+ for(int i=0;i<m;i++) {
+ Variable! sh_var=sv_ptr.Current;
+ pr_var=(Variable)(pr_ptr.Current);
+ string sh_type=sh_var.TypedIdent.Type.ToString();
+ string pr_type=pr_var.TypedIdent.Type.ToString();
+ if(pr_type!=("[int]"+sh_type)) {
+ Console.Error.WriteLine("In the declaration of "+f.Name+", the type "+pr_type+
+ " does not coincide with [int]"+sh_type);
+ return false;
+ }
+ sv_ptr.MoveNext();
+ pr_ptr.MoveNext();
+ }
+ //now check the types of the second portion
+ sv_ptr=sharedVars.GetEnumerator(); sv_ptr.MoveNext();
+ for(int i=0;i<m;i++) {
+ Variable! sh_var=sv_ptr.Current;
+ pr_var=(Variable)pr_ptr.Current;
+ string sh_type=sh_var.TypedIdent.Type.ToString();
+ string pr_type=pr_var.TypedIdent.Type.ToString();
+ if(pr_type!=("[int]"+sh_type)) {
+ Console.Error.WriteLine("In the declaration of "+f.Name+", the type "+pr_type+
+ " does not coincide with [int]"+sh_type);
+ return false;
+ }
+ sv_ptr.MoveNext();
+ pr_ptr.MoveNext();
+ }
+ //check that the return value is (bool);
+ VariableSeq! outparams=f.OutParams;
+ if(outparams.Length!=1) {
+ Console.Error.WriteLine("The function "+f.Name+" should have exactly one out parameter.");
+ return false;
+ }
+ Variable! rv=(Variable!)(outparams.Head());
+ string rv_type=rv.TypedIdent.Type.ToString();
+ if(rv_type != "bool") {
+ Console.Error.WriteLine("The function "+f.Name+" should return bool. Currently, it returns "+rv_type+".");
+ return false;
+ }
+ return true;
+ }//end of CheckFormat
+
+
+ //checks that all procedures are exactly T0,...,T(n-1) for some n in some order.
+ //Returns true, if it is so. In number_of_threads, returns the number of threads.
+ bool CheckThreads(out int number_of_threads) {
+ number_of_threads=0;
+ bool[] threads_specified=new bool[1] {false};
+ foreach(Declaration d in prg.TopLevelDeclarations) {
+ if((d is Procedure) && (d!=null)) {
+ Procedure! p=(Procedure)d;
+ if(p.Name[0]!='T') {
+ Console.Error.WriteLine("A procedure discovered which is not a thread.");
+ return false;
+ }
+ string probably_number=p.Name.Substring(1,p.Name.Length-1);
+ int no_of_procedure;
+ if(!Int32.TryParse(probably_number,out no_of_procedure)) {
+ Console.Error.WriteLine("The procedure "+p.Name+" has a bad name. After T a number should follow.");
+ return false;
+ }
+ VariableSeq! inparams=p.InParams;
+ if(inparams.Length>0) {
+ Console.Error.WriteLine("The thread "+p.Name+" has arguments. It should not.");
+ return false;
+ }
+ VariableSeq! outparams=p.OutParams;
+ if(outparams.Length>0) {
+ Console.Error.WriteLine("The thread "+p.Name+" returns some value. It should not.");
+ return false;
+ }
+ if(no_of_procedure>=number_of_threads) {
+ number_of_threads=no_of_procedure+1;
+ Array.Resize(ref threads_specified,number_of_threads);
+ if(threads_specified==null) {
+ Console.Error.WriteLine("Out of memory");
+ return false;
+ }
+ threads_specified[no_of_procedure]=true;
+ } else {
+ if(threads_specified[no_of_procedure]) {
+ Console.Error.WriteLine("Procedure "+p.Name+" was specified at least twice.");
+ return false;
+ } else threads_specified[no_of_procedure]=true;
+ }
+ }
+ }
+ //now check whether all threads were specified
+ for(int i=0;i<number_of_threads;i++)
+ if(!threads_specified[i]) {
+ Console.Error.WriteLine("Thread number "+i.ToString()+" was not specified.");
+ return false;
+ }
+ return true;
+ }//end CheckThreads
+
+ //Copy the non-procedural code from the input to the output, insert the prelude, go
+ //through the implementations, instrumentize them statement by statement.
+ bool InstrumentizeThreads(TokenTextWriter! outstream, int number_of_threads,List<Variable!>! sharedVars,
+ string! history_pre_prefix,string! history_post_prefix,bool phase_number_finite) {
+ outstream.SetToken(prg);
+ bool first = true;
+ bool implementation_seen=false;
+ foreach(Declaration d in prg.TopLevelDeclarations) {
+ if(d==null) {outstream.WriteLine(); continue;}
+ if(first) first=false; else outstream.WriteLine();
+ if(d is Implementation) {
+ if(!implementation_seen) {
+ implementation_seen=true;
+ EmitPrelude(outstream,sharedVars,history_pre_prefix,history_post_prefix,phase_number_finite);
+ }
+ Implementation! ipl=(Implementation)d;
+ int thread_no=Convert.ToInt32(ipl.Name.Substring(1,ipl.Name.Length-1));
+ outstream.WriteLine("procedure "+ipl.Name+"()");
+ InstrumentizeStartSeq(outstream,1,sharedVars,history_pre_prefix,history_post_prefix,thread_no);
+ if(!InstrumentizeImplInternals(outstream,1,ipl,thread_no)) return false;
+ } else if(d is Procedure) continue;
+ else d.Emit(outstream,0);
+ }//foreach
+ return true;
+ }//end InstrumentizeThreads()
+
+
+ //Print declarations of history variables, phase variable, instrumentation functions.
+ void EmitPrelude(TokenTextWriter! outstream, List<Variable!>! sharedVars,
+ string! history_pre_prefix, string! history_post_prefix,bool phase_number_finite) {
+ //write history variables
+ if(sharedVars.Count>0) {
+ outstream.Write("var ");
+ EmitVarList(outstream,sharedVars,history_pre_prefix,null,"[int]");
+ outstream.WriteLine(";");
+ outstream.Write("var ");
+ EmitVarList(outstream,sharedVars,history_post_prefix,null,"[int]");
+ outstream.WriteLine(";");
+ }
+ outstream.WriteLine("var "+currentPhaseVarName+":int;");
+ outstream.WriteLine();
+ //write the assume rely procedure
+ outstream.WriteLine("procedure{:inline 999} AssumeRely("+threadNoVarName+":int)");
+ outstream.Write("modifies ");
+ EmitVarList(outstream,sharedVars,null,null,null); outstream.Write(", ");
+ EmitVarList(outstream,sharedVars,history_pre_prefix,null,null); outstream.WriteLine(";");
+ outstream.WriteLine("{");
+ outstream.Write(" havoc ");
+ EmitVarList(outstream,sharedVars,null,null,null); outstream.WriteLine(";");
+ foreach(Variable! v in sharedVars)
+ outstream.WriteLine(" "+history_pre_prefix+v.Name+"["+currentPhaseVarName+"]:="+v.Name+";");
+ outstream.Write(" assume A("+threadNoVarName+","+currentPhaseVarName+",");
+ EmitVarList(outstream,sharedVars,history_pre_prefix,null,null);
+ outstream.Write(",");
+ EmitVarList(outstream,sharedVars,history_post_prefix,null,null);
+ outstream.WriteLine(");"); outstream.WriteLine("}");
+ outstream.WriteLine();
+ //write the CheckGuarantee procedure
+ outstream.WriteLine("procedure {:inline 999} CheckGuarantee("+threadNoVarName+":int)");
+ outstream.Write("modifies ");
+ EmitVarList(outstream,sharedVars,history_post_prefix,null,null); outstream.WriteLine(";");
+ outstream.WriteLine("{");
+ foreach(Variable! v in sharedVars)
+ outstream.WriteLine(" "+history_post_prefix+v.Name+"["+currentPhaseVarName+"]:="+v.Name+";");
+ outstream.Write(" assert G("+threadNoVarName+","+currentPhaseVarName+", ");
+ EmitVarList(outstream,sharedVars,history_pre_prefix,null,null);
+ outstream.Write(",");
+ EmitVarList(outstream,sharedVars,history_post_prefix,null,null);
+ outstream.WriteLine(");"); outstream.WriteLine("}");
+ outstream.WriteLine();
+ //write the MayUpdateProcedure
+ outstream.WriteLine("procedure {:inline 999} MayUpdate("+threadNoVarName+":int)");
+ outstream.Write("modifies "+currentPhaseVarName);
+ if(sharedVars.Count>0) {
+ outstream.Write(", "); EmitVarList(outstream,sharedVars,history_pre_prefix,null,null); outstream.Write(", ");
+ EmitVarList(outstream,sharedVars,history_post_prefix,null,null); outstream.Write(", ");
+ EmitVarList(outstream,sharedVars,null,null,null);
+ }
+ outstream.WriteLine(";"); outstream.WriteLine("{");
+ outstream.WriteLine(" goto updating,not_updating;");
+ outstream.WriteLine(" updating:");
+ outstream.WriteLine(" call CheckGuarantee("+threadNoVarName+");");
+ outstream.WriteLine(" if(("+currentPhaseVarName+"+1)<"+totalPhasesName+") {");
+ outstream.WriteLine(" "+currentPhaseVarName+":="+currentPhaseVarName+"+1;");
+ outstream.WriteLine(" call AssumeRely("+threadNoVarName+");");
+ outstream.WriteLine(" }");
+ outstream.WriteLine(" not_updating:");
+ outstream.WriteLine("}");
+ outstream.WriteLine();
+ //write the instrumentation for the assume predicate
+ outstream.WriteLine("procedure {:inline 999} MayUpdateAssume("+threadNoVarName+":int,condition:bool)");
+ outstream.Write("modifies "+currentPhaseVarName);
+ if(sharedVars.Count>0) {
+ outstream.Write(", "); EmitVarList(outstream,sharedVars,history_pre_prefix,null,null); outstream.Write(", ");
+ EmitVarList(outstream,sharedVars,history_post_prefix,null,null); outstream.Write(", ");
+ EmitVarList(outstream,sharedVars,null,null,null);
+ }
+ outstream.WriteLine(";"); outstream.WriteLine("{");
+ outstream.WriteLine(" if(condition) { call MayUpdate("+threadNoVarName+"); }");
+ outstream.WriteLine(" else { call CheckGuarantee("+threadNoVarName+"); assume false; }");
+ outstream.WriteLine("}");
+ outstream.WriteLine();
+ }//end EmitPrelude(...)
+
+
+ //Print a comma-separated variable list vars to outstream.
+ //Before each variable prints prefix, after each variable prints suffix if available.
+ //If type_prefix is not null, also output the types, prefixing them with type_prefix.
+ void EmitVarList(TokenTextWriter! outstream, List<Variable!>! vars, string var_prefix,
+ string var_suffix,string type_prefix) {
+ bool first=true;
+ foreach(Variable! v in vars) {
+ if(v.Name==null) continue;
+ if(first) first=false; else outstream.Write(", ");
+ outstream.Write(((var_prefix==null)?"":var_prefix)+v.Name+((var_suffix==null)?"":var_suffix));
+ if(type_prefix!=null) outstream.Write(":"+type_prefix+v.TypedIdent.Type.ToString());
+ }
+ }//end EmitVarList(...)
+
+ //prints the indented start sequence of the thread thread_no
+ void InstrumentizeStartSeq(TokenTextWriter! outstream,int level,List<Variable!>! sharedVars,
+ string! history_pre_prefix, string! history_post_prefix, int thread_no) {
+ outstream.Write("modifies "+currentPhaseVarName);
+ if(sharedVars.Count>0) {
+ outstream.Write(", "); EmitVarList(outstream,sharedVars,null,null,null);
+ outstream.Write(", "); EmitVarList(outstream,sharedVars,history_pre_prefix,null,null);
+ outstream.Write(", "); EmitVarList(outstream,sharedVars,history_post_prefix,null,null);
+ }
+ outstream.WriteLine(";");
+ outstream.WriteLine("{");
+ WriteIndent(outstream,level); outstream.WriteLine(currentPhaseVarName+":=0;");
+ WriteIndent(outstream,level); outstream.WriteLine("call AssumeRely("+thread_no.ToString()+");");
+ }//end InstrumentizeStartSeq(...)
+
+ //prints 2+level whitespaces
+ void WriteIndent(TokenTextWriter! outstream,int level) {
+ for(int i=0;i<level;i++) outstream.Write(" ");
+ }//end WriteIndent
+
+ //goes through the list of statements of procedure impl, instrumetizes them according to the history-algorithm
+ bool InstrumentizeImplInternals(TokenTextWriter! outstream,int level,Implementation! impl,int thread_no) {
+ foreach(Block! b in impl.Blocks) {
+ WriteIndent(outstream,level); outstream.WriteLine(b.Label+":");
+ if(!InstrumentizeCmdSeq(outstream,level,b.Cmds,thread_no,(b.TransferCmd==null || (b.TransferCmd is ReturnCmd)))) {
+ Console.Error.WriteLine("Error processing thread "+thread_no.ToString()+".");
+ return false;
+ }
+ if(b.TransferCmd!=null && !(b.TransferCmd is ReturnCmd))
+ if(!InstrumentizeTransferCmd(outstream,level,b.TransferCmd,thread_no)) return false;
+ }
+ outstream.WriteLine("}");
+ return true;
+ }//end InstrumentizeStmtSeq(...)
+
+ //Instrumentizing a sequence of commands. If last_cmd_seq==true, then the last statement should be
+ //instrumentized as a statement without successors
+ bool InstrumentizeCmdSeq(TokenTextWriter! outstream, int level, CmdSeq! cmds,
+ int thread_no, bool last_cmd_seq) {
+ SequenceEnumerator! curr_cmd=(SequenceEnumerator)(cmds.GetEnumerator()); curr_cmd.MoveNext();
+ Cmd cmd; int i=0;
+ for(;(i+1)<cmds.Length;i++) {
+ cmd=(Cmd)curr_cmd.Current;
+ if(cmd!=null) {
+ if(!InstrumentizeCmd(outstream,level,cmd,thread_no,false)) return false;
+ }
+ curr_cmd.MoveNext();
+ }
+ if(i<cmds.Length) {
+ cmd=(Cmd)(curr_cmd.Current);
+ if(cmd!=null)
+ if(!InstrumentizeCmd(outstream,level,cmd,thread_no,last_cmd_seq)) return false;
+ }
+ return true;
+ }//end InstrumentizeCmdSeq(...)
+
+ //instrumentizes the goto statement
+ bool InstrumentizeTransferCmd(TokenTextWriter! outstream,int level,TransferCmd! cmd,int thread_no) {
+ if(cmd is GotoCmd) {
+ WriteIndent(outstream,level); outstream.WriteLine("call MayUpdate("+thread_no.ToString()+");");
+ cmd.Emit(outstream,level);
+ } else {
+ Console.Error.Write("Unknown command: ");
+ TokenTextWriter ttw=new TokenTextWriter("<console_error>",Console.Error);
+ if(ttw!=null) { cmd.Emit(ttw,0); ttw.Close(); }
+ else Console.Error.Write("Out of memory");
+ return false;
+ }
+ return true;
+ }//end InstrumentizeTransferCmd(...)
+
+ //Instrumentize a single command. If last_cmd==true, this statement is the last in the thread.
+ bool InstrumentizeCmd(TokenTextWriter! outstream,int level,Cmd! cmd,int thread_no,bool last_cmd) {
+ if((cmd is AssertCmd) || (cmd is HavocCmd) || (cmd is SimpleAssignCmd)||(cmd is ArrayAssignCmd)) {
+ cmd.Emit(outstream,level);
+ WriteIndent(outstream,level);
+ if(last_cmd) outstream.WriteLine("call CheckGuarantee("+thread_no.ToString()+");");
+ else outstream.WriteLine("call MayUpdate("+thread_no.ToString()+");");
+ } else if(cmd is AssumeCmd) {
+ WriteIndent(outstream,level);
+ if(last_cmd) outstream.Write("call CheckGuarantee("+thread_no.ToString()+");");
+ else {
+ outstream.Write("call MayUpdateAssume(");
+ outstream.Write(thread_no.ToString()+",");
+ PredicateCmd! pcmd=(PredicateCmd)cmd;
+ pcmd.Expr.Emit(outstream);
+ outstream.WriteLine(");");
+ }
+ } else {
+ Console.Error.Write("Unknown command: ");
+ cmd.Emit(new TokenTextWriter("<console>",Console.Error),0);
+ return false;
+ }
+ return true;
+ }//end InstrumentizeCmd(...)
+
+ //Prints the procedure which checks the correct interleaving of the specifications
+ bool PrintInterleaving(TokenTextWriter! outstream,int number_of_threads,List<Variable!>! sharedVars,
+ string! history_pre_prefix,string! history_post_prefix,int n, bool phase_number_finite,
+ int phases_no) {
+ outstream.WriteLine();
+ outstream.WriteLine("procedure Interleave()");
+ outstream.Write("modifies "); EmitVarList(outstream,sharedVars,null,null,null); outstream.Write(",");
+ EmitVarList(outstream,sharedVars,history_pre_prefix,null,null); outstream.Write(",");
+ EmitVarList(outstream,sharedVars,history_post_prefix,null,null); outstream.WriteLine(";");
+ outstream.WriteLine("{");
+ outstream.WriteLine(" var "+threadNoVarName+":int, "+currentPhaseVarName+":[int]int,");
+ EmitVarList(outstream,sharedVars,history_pre_prefix,null,"[int][int]"); outstream.Write(",");
+ EmitVarList(outstream,sharedVars,history_post_prefix,null,"[int][int]"); outstream.WriteLine(";");
+ outstream.Write(" assume Init("); EmitVarList(outstream,sharedVars,null,null,null); outstream.WriteLine(");");
+ for(int i=0;i<n;i++) {
+ WriteIndent(outstream,1);
+ outstream.WriteLine(currentPhaseVarName+"["+i.ToString()+"]:=0;");
+ }
+ if(phase_number_finite)
+ for(int i=0;i<phases_no*n;i++) {
+ PrintSinglePhaseCode(outstream,1,sharedVars,history_pre_prefix,history_post_prefix,n,true);
+ outstream.WriteLine();
+ }
+ else {
+ outstream.WriteLine(" while(true) {");
+ PrintSinglePhaseCode(outstream,2,sharedVars,history_pre_prefix,history_post_prefix,n,false);
+ outstream.WriteLine(" }");
+ }
+ outstream.WriteLine("}");
+ return true;
+ }//end PrintInterleaving(...)
+
+ //print a code which checks the relies for some thread for its one phase
+ void PrintSinglePhaseCode(TokenTextWriter! outstream, int level,List<Variable!>! sharedVars,
+ string! history_pre_prefix,string! history_post_prefix, int n,
+ bool phase_number_finite) {
+ WriteIndent(outstream,level); outstream.WriteLine("havoc "+threadNoVarName+";");
+ WriteIndent(outstream,level); outstream.Write("assume ");
+ for(int i=0;(i+1)<n;i++)
+ outstream.Write(threadNoVarName+"=="+i.ToString()+" || ");
+ outstream.WriteLine(threadNoVarName+"=="+(n-1).ToString()+";");
+
+ if(phase_number_finite) {
+ WriteIndent(outstream,level);
+ outstream.WriteLine("assume "+currentPhaseVarName+"["+threadNoVarName+"]<"+totalPhasesName+";");
+ }
+ foreach(Variable! v in sharedVars) {
+ WriteIndent(outstream,level);
+ outstream.WriteLine(history_pre_prefix+v.Name+"["+threadNoVarName+"]:="+
+ history_pre_prefix+v.Name+"["+threadNoVarName+"]["+currentPhaseVarName+"["+threadNoVarName+"]:="+v.Name+"];");
+ }
+
+ WriteIndent(outstream,level);
+ outstream.Write("assert A("+threadNoVarName+","+currentPhaseVarName+"["+threadNoVarName+"],");
+ EmitVarList(outstream,sharedVars,history_pre_prefix,"["+threadNoVarName+"]",null); outstream.Write(",");
+ EmitVarList(outstream,sharedVars,history_post_prefix,"["+threadNoVarName+"]",null); outstream.WriteLine(");");
+
+ WriteIndent(outstream,level);
+ outstream.Write("assume G("+threadNoVarName+","+currentPhaseVarName+"["+threadNoVarName+"],");
+ EmitVarList(outstream,sharedVars,history_pre_prefix,"["+threadNoVarName+"]",null); outstream.Write(",");
+ EmitVarList(outstream,sharedVars,history_post_prefix,"["+threadNoVarName+"]",null); outstream.WriteLine(");");
+
+ WriteIndent(outstream,level); outstream.Write("havoc ");
+ EmitVarList(outstream,sharedVars,null,null,null); outstream.WriteLine(";");
+
+ foreach(Variable! v in sharedVars) {
+ WriteIndent(outstream,level);
+ outstream.WriteLine("assume "+history_post_prefix+v.Name+"["+threadNoVarName+"]["+currentPhaseVarName+"["+threadNoVarName+"]]=="+v.Name+";");
+ }
+ WriteIndent(outstream,level);
+ outstream.WriteLine(currentPhaseVarName+"["+threadNoVarName+"]:="+currentPhaseVarName+"["+threadNoVarName+"]+1;");
+ }//end PrintSinglePhaseCode(...)
+
+ }//end class
+}//end namespace
diff --git a/Source/XAHA/XAHA.sscproj b/Source/XAHA/XAHA.sscproj
new file mode 100644
index 00000000..402217cd
--- /dev/null
+++ b/Source/XAHA/XAHA.sscproj
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="utf-8"?>
+<VisualStudioProject>
+ <XEN ProjectType="Local"
+ SchemaVersion="1.0"
+ Name="XAHA"
+ ProjectGuid="bc052cd8-7d87-4520-aff9-d3ac81161905"
+ >
+ <Build>
+ <Settings ApplicationIcon=""
+ AssemblyName="XAHA"
+ OutputType="Library"
+ RootNamespace="XAHA"
+ StartupObject=""
+ StandardLibraryLocation=""
+ TargetPlatform="v2"
+ TargetPlatformLocation=""
+ >
+ <Config Name="Debug"
+ AllowUnsafeBlocks="False"
+ BaseAddress="285212672"
+ CheckForOverflowUnderflow="False"
+ ConfigurationOverrideFile=""
+ DefineConstants="DEBUG;TRACE"
+ DocumentationFile=""
+ DebugSymbols="True"
+ FileAlignment="4096"
+ IncrementalBuild="True"
+ Optimize="False"
+ OutputPath="bin\debug"
+ RegisterForComInterop="False"
+ RemoveIntegerChecks="false"
+ TreatWarningsAsErrors="False"
+ WarningLevel="4"
+ CheckContractAdmissibility="True"
+ CheckPurity="False"
+ />
+ <Config Name="Release"
+ AllowUnsafeBlocks="false"
+ BaseAddress="285212672"
+ CheckForOverflowUnderflow="false"
+ ConfigurationOverrideFile=""
+ DefineConstants="TRACE"
+ DocumentationFile=""
+ DebugSymbols="false"
+ FileAlignment="4096"
+ IncrementalBuild="false"
+ Optimize="true"
+ OutputPath="bin\release"
+ RegisterForComInterop="false"
+ RemoveIntegerChecks="false"
+ TreatWarningsAsErrors="false"
+ WarningLevel="4"
+ CheckContractAdmissibility="True"
+ CheckPurity="False"
+ />
+ </Settings>
+ <References>
+ <Reference Name="System"
+ AssemblyName="System"
+ Private="false"
+ />
+ <Reference Name="System.Data"
+ AssemblyName="System.Data"
+ Private="false"
+ />
+ <Reference Name="System.Xml"
+ AssemblyName="System.Xml"
+ Private="false"
+ />
+ <Reference Name="Core"
+ Project="{47BC34F1-A173-40BE-84C2-9332B4418387}"
+ Private="true"
+ />
+ <Reference Name="Basetypes"
+ Project="{0C692837-77EC-415F-BF04-395E3ED06E9A}"
+ Private="true"
+ />
+ </References>
+ </Build>
+ <Files>
+ <Include>
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="XAHA.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="..\version.ssc"
+ />
+ </Include>
+ </Files>
+ </XEN>
+</VisualStudioProject> \ No newline at end of file
diff --git a/Source/foo.txt b/Source/foo.txt
new file mode 100644
index 00000000..99d2b9ea
--- /dev/null
+++ b/Source/foo.txt
@@ -0,0 +1,93 @@
+Change 3641 by REDMOND\mbarnett@mbarnettx60 on 2009/07/14 16:08:17
+
+ Added copyright notices to all source files.
+
+Affected files ...
+
+... //depot/projects/BoogieBranches/CodePlex/Source/AbsInt/AbstractInterpretation.ssc#4 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/AbsInt/ExprFactories.ssc#3 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/AbsInt/LoopInvariantsOnDemand.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/AbsInt/Traverse.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/AIFramework/CommonFunctionSymbols.ssc#3 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/AIFramework/Expr.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/AIFramework/Functional.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/AIFramework/Lattice.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/AIFramework/Logger.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/AIFramework/MultiLattice.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/AIFramework/Mutable.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/AIFramework/Polyhedra/LinearConstraint.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/AIFramework/Polyhedra/LinearConstraintSystem.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/AIFramework/Polyhedra/PolyhedraAbstraction.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/AIFramework/Polyhedra/SimplexTableau.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/AIFramework/VariableMap/ConstantAbstraction.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/AIFramework/VariableMap/ConstantExpressions.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/AIFramework/VariableMap/DynamicTypeLattice.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/AIFramework/VariableMap/Intervals.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/AIFramework/VariableMap/MicroLattice.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/AIFramework/VariableMap/Nullness.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/AIFramework/VariableMap/VariableMapLattice.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Basetypes/BigNum.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Basetypes/Rational.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Basetypes/Set.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/BoogieDriver/BoogieDriver.ssc#3 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Core/Absy.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Core/AbsyCmd.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Core/AbsyExpr.ssc#3 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Core/AbsyType.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Core/CommandLineOptions.ssc#3 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Core/Duplicator.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Core/GraphAlgorithms.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Core/Inline.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Core/LoopUnroll.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Core/OOLongUtil.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Core/Parser.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Core/PureCollections.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Core/ResolutionContext.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Core/Scanner.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Core/StandardVisitor.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Core/TypeAmbiguitySeeker.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Core/Util.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Core/VCExp.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Core/Xml.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Dafny/Dafny.atg#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Dafny/DafnyAst.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Dafny/DafnyMain.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Dafny/Parser.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Dafny/Printer.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Dafny/Resolver.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Dafny/Scanner.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Dafny/Translator.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/DafnyDriver/DafnyDriver.ssc#3 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Graph/Graph.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Provers/Simplify/Let2ImpliesVisitor.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Provers/Simplify/Prover.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Provers/Simplify/ProverInterface.ssc#3 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Provers/SMTLib/ProverInterface.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Provers/SMTLib/SMTLibLineariser.ssc#3 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Provers/SMTLib/TypeDeclCollector.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Provers/Z3/Inspector.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Provers/Z3/Prover.ssc#3 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Provers/Z3/ProverInterface.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/Provers/Z3/TypeDeclCollector.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/VCExpr/BigLiteralAbstracter.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/VCExpr/Boogie2VCExpr.ssc#3 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/VCExpr/Clustering.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/VCExpr/LetBindingSorter.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/VCExpr/NameClashResolver.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/VCExpr/SimplifyLikeLineariser.ssc#3 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/VCExpr/TermFormulaFlattening.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/VCExpr/TypeErasure.ssc#3 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/VCExpr/TypeErasureArguments.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/VCExpr/TypeErasurePremisses.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/VCExpr/VCExprAST.ssc#3 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/VCExpr/VCExprASTPrinter.ssc#3 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/VCExpr/VCExprASTVisitors.ssc#3 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/VCGeneration/Check.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/VCGeneration/ConditionGeneration.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/VCGeneration/Context.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/VCGeneration/OrderingAxioms.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/VCGeneration/VC.ssc#3 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/VCGeneration/VCDoomed.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/VCGeneration/Wlp.ssc#2 edit
+... //depot/projects/BoogieBranches/CodePlex/Source/version.ssc#2 edit
+
diff --git a/Source/version.ssc b/Source/version.ssc
new file mode 100644
index 00000000..bad3d890
--- /dev/null
+++ b/Source/version.ssc
@@ -0,0 +1,8 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System.Reflection;
+[assembly: AssemblyVersion("1.0.21125.0")]
+[assembly: AssemblyFileVersion("1.0.21125.0")]