//-----------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation. All Rights Reserved.
//
//-----------------------------------------------------------------------------
using System;
using System.Text;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.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/*!*/ typeParams, params Type[] types) {
Contract.Requires(types != null);
Contract.Requires(name != null);
Contract.Requires(cce.NonNullElements(typeParams));
Contract.Requires(types.Length > 0);
Contract.Requires(Contract.ForAll(0, types.Length, i => types[i] != null));
Contract.Ensures(Contract.Result() != 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, cce.NonNull(types[i])),
true));
Formal result = new Formal(Token.NoToken,
new TypedIdent(Token.NoToken, "res",
cce.NonNull(types)[types.Length - 1]),
false);
return new Function(Token.NoToken, name, ToSeq(typeParams), args, result);
}
public static Function BoogieFunction(string name, params Type[] types) {
Contract.Requires(types != null);
Contract.Requires(name != null);
Contract.Ensures(Contract.Result() != null);
return BoogieFunction(name, new List(), types);
}
// boogie function where all arguments and the result have the same type U
public static Function UniformBoogieFunction(string name, int arity, Type U) {
Contract.Requires(U != null);
Contract.Requires(name != null);
Contract.Ensures(Contract.Result() != null);
Type[]/*!*/ types = new Type[arity + 1];
for (int i = 0; i < arity + 1; ++i)
types[i] = U;
return BoogieFunction(name, types);
}
public static List/*!*/ GenVarsForInParams(Function fun, VCExpressionGenerator gen) {
Contract.Requires(gen != null);
Contract.Requires(fun != null);
Contract.Ensures(cce.NonNullElements(Contract.Result>()));
List/*!*/ arguments = new List(fun.InParams.Length);
foreach (Formal/*!*/ f in fun.InParams) {
Contract.Assert(f != null);
VCExprVar/*!*/ var = gen.Variable(f.Name, f.TypedIdent.Type);
arguments.Add(var);
}
return arguments;
}
public static List/*!*/ ToList(params T[] args) where T : class{
Contract.Requires(cce.NonNullElements(args));
Contract.Ensures(cce.NonNullElements(Contract.Result>()));
return new List(args);
}
public static List/*!*/ ToList(TypeVariableSeq seq) {
Contract.Requires(seq != null);
Contract.Ensures(cce.NonNullElements(Contract.Result>()));
List/*!*/ res = new List(seq.Length);
foreach (TypeVariable/*!*/ var in seq) {
Contract.Assert(var != null);
res.Add(var);
}
return res;
}
public static TypeVariableSeq ToSeq(List/*!*/ list) {
Contract.Requires(cce.NonNullElements(list));
Contract.Ensures(Contract.Result() != null);
TypeVariableSeq/*!*/ res = new TypeVariableSeq();
foreach (TypeVariable/*!*/ var in list) {
Contract.Assert(var != null);
res.Add(var);
}
return res;
}
public static List/*!*/ Intersect(List a, List b) {
Contract.Requires(b != null);
Contract.Requires(a != null);
Contract.Ensures(Contract.Result>() != null);
List/*!*/ res = new List(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>/*!*/ ToPairList(IDictionary dict) {
Contract.Requires((dict != null));
Contract.Ensures(Contract.Result>>() != null);
List>/*!*/ res = new List>(dict);
return res;
}
public static void AddRangeWithoutDups(IEnumerable fromList, List toList) {
Contract.Requires(toList != null);
Contract.Requires(fromList != null);
foreach (T t in fromList)
if (!toList.Contains(t))
toList.Add(t);
}
public static void AddFreeVariablesWithoutDups(Type type, List/*!*/ toList) {
Contract.Requires(type != null);
Contract.Requires(cce.NonNullElements(toList));
foreach (TypeVariable var in type.FreeVariables) {
Contract.Assert(var != null);
if (!toList.Contains(var))
toList.Add(var);
}
}
public static List/*!*/ ToVCExprList(List/*!*/ list) {
Contract.Requires(cce.NonNullElements(list));
Contract.Ensures(cce.NonNullElements(Contract.Result>()));
List/*!*/ res = new List(list.Count);
foreach (VCExprVar/*!*/ var in list) {
Contract.Assert(var != null);
res.Add(var);
}
return res;
}
public static List/*!*/ VarVector(string baseName, int num, Type type, VCExpressionGenerator gen) {
Contract.Requires(gen != null);
Contract.Requires(type != null);
Contract.Requires(baseName != null);
Contract.Ensures(cce.NonNullElements(Contract.Result>()));
List/*!*/ res = new List(num);
for (int i = 0; i < num; ++i)
res.Add(gen.Variable(baseName + i, type));
return res;
}
public static List/*!*/ VarVector(string baseName, List/*!*/ types, VCExpressionGenerator gen) {
Contract.Requires(gen != null);
Contract.Requires(baseName != null);
Contract.Requires(cce.NonNullElements(types));
Contract.Ensures(cce.NonNullElements(Contract.Result>()));
List/*!*/ res = new List(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/*!*/ Dtors;
[ContractInvariantMethod]
void ObjectInvariant() {
Contract.Invariant(Ctor != null);
Contract.Invariant(cce.NonNullElements(Dtors));
}
public TypeCtorRepr(Function ctor, List/*!*/ dtors) {
Contract.Requires(ctor != null);
Contract.Requires(cce.NonNullElements(dtors));
Contract.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).
[ContractClass(typeof(TypeAxiomBuilderContracts))]
public abstract class TypeAxiomBuilder : ICloneable {
protected readonly VCExpressionGenerator/*!*/ Gen;
[ContractInvariantMethod]
void ObjectInvariant() {
Contract.Invariant(Gen != null);
Contract.Invariant(Ctor != null);
}
internal abstract MapTypeAbstractionBuilder/*!*/ MapTypeAbstracter {
get;
}
///////////////////////////////////////////////////////////////////////////
// Type Axioms
// list in which all typed axioms are collected
private readonly List/*!*/ AllTypeAxioms;
[ContractInvariantMethod]
void AllTypeAxiomsInvariantMethod() {
Contract.Invariant(cce.NonNullElements(AllTypeAxioms));
}
// list in which type axioms are incrementally collected
private readonly List/*!*/ IncTypeAxioms;
[ContractInvariantMethod]
void IncTypeAxiomsInvariantMethod() {
Contract.Invariant(cce.NonNullElements(IncTypeAxioms));
}
internal void AddTypeAxiom(VCExpr axiom) {
Contract.Requires(axiom != null);
AllTypeAxioms.Add(axiom);
IncTypeAxioms.Add(axiom);
}
// Return all axioms that were added since the last time NewAxioms
// was called
public VCExpr GetNewAxioms() {
Contract.Ensures(Contract.Result() != null);
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) {
Contract.Requires(typeRepr != null);
Contract.Ensures(Contract.Result() != null);
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) {
Contract.Requires(typeRepr != null);
Contract.Ensures(Contract.Result() != null);
if (CommandLineOptions.Clo.TypeEncodingMethod
== CommandLineOptions.TypeEncoding.None)
return VCExpressionGenerator.True;
List/*!*/ 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(),
"ctor:" + typeRepr.Name, -1, eq);
}
// generate an axiom (forall x0, x1, ... :: invFun(fun(x0, x1, ...) == xi)
protected VCExpr GenLeftInverseAxiom(Function fun, Function invFun, int dtorNum) {
Contract.Requires(invFun != null);
Contract.Requires(fun != null);
Contract.Ensures(Contract.Result() != null);
List/*!*/ quantifiedVars = HelperFuns.GenVarsForInParams(fun, Gen);
Contract.Assert(cce.NonNullElements(quantifiedVars));
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/*!*/ triggers = HelperFuns.ToList(Gen.Trigger(true, HelperFuns.ToList(funApp)));
Contract.Assert(cce.NonNullElements(triggers));
return Gen.Forall(quantifiedVars, triggers, "typeInv:" + invFun.Name, -1, eq);
}
///////////////////////////////////////////////////////////////////////////
// the type of everything that is not int, bool, or a type
[ContractInvariantMethod]
void ObjectInvariant2() {
Contract.Invariant(UDecl != null);
Contract.Invariant(TDecl != null);
Contract.Invariant(U != null);
Contract.Invariant(T != null);
}
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);
[Pure]
public abstract bool UnchangedType(Type/*!*/ type);
///////////////////////////////////////////////////////////////////////////
// Symbols for representing types
private readonly IDictionary/*!*/ BasicTypeReprs;
[ContractInvariantMethod]
void BasicTypeReprsInvariantMethod() {
Contract.Invariant(cce.NonNullDictionaryAndValues(BasicTypeReprs));
}
private VCExpr GetBasicTypeRepr(Type type) {
Contract.Requires(type != null);
Contract.Requires(type.IsBasic || type.IsBv);
Contract.Ensures(Contract.Result() != null);
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 cce.NonNull(res);
}
private readonly IDictionary/*!*/ TypeCtorReprs;
[ContractInvariantMethod]
void TypeCtorReprsInvariantMethod() {
Contract.Invariant(TypeCtorReprs != null);
}
internal TypeCtorRepr GetTypeCtorReprStruct(TypeCtorDecl decl) {
Contract.Requires(decl != null);
TypeCtorRepr reprSet;
if (!TypeCtorReprs.TryGetValue(decl, out reprSet)) {
Function/*!*/ ctor = HelperFuns.UniformBoogieFunction(decl.Name + "Type", decl.Arity, T);
Contract.Assert(ctor != null);
AddTypeAxiom(GenCtorAssignment(ctor));
List/*!*/ dtors = new List(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) {
Contract.Requires(decl != null);
Contract.Ensures(Contract.Result() != null);
return GetTypeCtorReprStruct(decl).Ctor;
}
public Function GetTypeDtor(TypeCtorDecl decl, int num) {
Contract.Requires(decl != null);
Contract.Ensures(Contract.Result() != null);
return GetTypeCtorReprStruct(decl).Dtors[num];
}
// mapping from free type variables to VCExpr variables
private readonly IDictionary/*!*/ TypeVariableMapping;
[ContractInvariantMethod]
void TypeVariableMappingInvariantMethod() {
Contract.Invariant(cce.NonNullDictionaryAndValues(TypeVariableMapping));
}
public VCExprVar Typed2Untyped(TypeVariable var) {
Contract.Requires(var != null);
Contract.Ensures(Contract.Result() != null);
VCExprVar res;
if (!TypeVariableMapping.TryGetValue(var, out res)) {
res = new VCExprVar(var.Name, T);
TypeVariableMapping.Add(var, res);
}
return cce.NonNull(res);
}
////////////////////////////////////////////////////////////////////////////
// Symbols for representing variables and constants
// Globally defined variables
private readonly IDictionary/*!*/ Typed2UntypedVariables;
[ContractInvariantMethod]
void Typed2UntypedVariablesInvariantMethod() {
Contract.Invariant(cce.NonNullDictionaryAndValues(Typed2UntypedVariables));
}
// This method must only be used for free (unbound) variables
public VCExprVar Typed2Untyped(VCExprVar var) {
Contract.Requires(var != null);
Contract.Ensures(Contract.Result() != null);
VCExprVar res = TryTyped2Untyped(var);
if (res == null) {
res = Gen.Variable(var.Name, TypeAfterErasure(var.Type));
Typed2UntypedVariables.Add(var, res);
AddVarTypeAxiom(res, var.Type);
}
return cce.NonNull(res);
}
///
/// This method is like Typed2Untyped, except in the case where the given variables
/// doesn't exist in the mapping. For that case, this method returns null whereas
/// Typed2Untyped creates a new variable that it adds to the mapping.
///
///
///
public VCExprVar TryTyped2Untyped(VCExprVar var) {
Contract.Requires(var != null);
VCExprVar res;
if (Typed2UntypedVariables.TryGetValue(var, out res)) {
return res;
} else {
return null;
}
}
protected abstract void AddVarTypeAxiom(VCExprVar/*!*/ var, Type/*!*/ originalType);
///////////////////////////////////////////////////////////////////////////
// Translation function from types to their term representation
public VCExpr Type2Term(Type type, IDictionary/*!*/ varMapping) {
Contract.Requires(type != null);
Contract.Requires(cce.NonNullDictionaryAndValues(varMapping));
Contract.Ensures(Contract.Result() != null);
//
if (type.IsBasic || type.IsBv) {
//
return GetBasicTypeRepr(type);
//
} else if (type.IsCtor) {
//
CtorType ctype = type.AsCtor;
Function/*!*/ repr = GetTypeCtorRepr(ctype.Decl);
List/*!*/ args = new List(ctype.Arguments.Length);
foreach (Type/*!*/ t in ctype.Arguments) {
Contract.Assert(t != null);
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 cce.NonNull(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);
Contract.Assert(false);
throw new cce.UnreachableException(); // please the compiler
}
}
////////////////////////////////////////////////////////////////////////////
public TypeAxiomBuilder(VCExpressionGenerator gen) {
Contract.Requires(gen != null);
this.Gen = gen;
AllTypeAxioms = new List();
IncTypeAxioms = new List();
BasicTypeReprs = new Dictionary();
CurrentCtorNum = BigNum.ZERO;
TypeCtorReprs = new Dictionary();
TypeVariableMapping = new Dictionary();
Typed2UntypedVariables = new Dictionary();
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) {
Contract.Requires(builder != null);
Gen = builder.Gen;
AllTypeAxioms = new List(builder.AllTypeAxioms);
IncTypeAxioms = new List(builder.IncTypeAxioms);
UDecl = builder.UDecl;
U = builder.U;
TDecl = builder.TDecl;
T = builder.T;
Ctor = builder.Ctor;
CurrentCtorNum = builder.CurrentCtorNum;
BasicTypeReprs = new Dictionary(builder.BasicTypeReprs);
TypeCtorReprs = new Dictionary(builder.TypeCtorReprs);
TypeVariableMapping =
new Dictionary(builder.TypeVariableMapping);
Typed2UntypedVariables =
new Dictionary(builder.Typed2UntypedVariables);
}
public abstract Object/*!*/ Clone();
}
[ContractClassFor(typeof(TypeAxiomBuilder))]
public abstract class TypeAxiomBuilderContracts : TypeAxiomBuilder {
public TypeAxiomBuilderContracts()
: base((VCExpressionGenerator)null) {
}
internal override MapTypeAbstractionBuilder MapTypeAbstracter {
get {
Contract.Ensures(Contract.Result() != null);
throw new NotImplementedException();
}
}
public override Type TypeAfterErasure(Type type) {
Contract.Requires(type != null);
Contract.Ensures(Contract.Result() != null);
throw new NotImplementedException();
}
public override bool UnchangedType(Type type) {
Contract.Requires(type != null);
throw new NotImplementedException();
}
protected override void AddVarTypeAxiom(VCExprVar var, Type originalType) {
Contract.Requires(var != null);
Contract.Requires(originalType != null);
throw new NotImplementedException();
}
public override object Clone() {
Contract.Ensures(Contract.Result