//-----------------------------------------------------------------------------
//
// 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;
///
/// Specifies the operations (e.g., join) on a mathematical lattice that depend
/// only on the elements of the lattice.
///
public abstract class MathematicalLattice
{
///
/// An element of the lattice. This class should be derived from in any
/// implementation of MathematicalLattice.
///
public abstract class Element : System.ICloneable {
///
/// Print out a debug-useful representation of the internal data structure of the lattice element.
///
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! 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);
///
/// Returns true if a <= this.
///
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)));
}
}
///
/// Provides an abstract interface for the operations of a lattice specific
/// to abstract interpretation (i.e., that deals with the expression language).
///
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));
}
///
/// Returns the predicate that corresponds to the given lattice element.
///
public abstract IExpr! ToPredicate(Element! e);
///
/// 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.
///
/// The function symbol.
/// The argument context.
/// True if it may understand f, false if it does not understand f.
public abstract bool Understands(IFunctionSymbol! f, IList/**/! args);
///
/// Return an expression that is equivalent to the given expression that does not
/// contain the given variable according to the lattice element and queryable.
///
/// The lattice element.
/// A queryable for asking addtional information.
/// The expression to find an equivalent expression.
/// The variable to eliminate.
/// The set of variables that can't be used in the resulting expression.
///
/// An equivalent expression to without
/// or null if not possible.
///
public abstract IExpr/*?*/ EquivalentExpr(Element! e, IQueryable! q, IExpr! expr, IVariable! var, Set/**/! prohibitedVars);
///
/// Answers a query about whether the given predicate holds given the lattice element.
///
/// The lattice element.
/// The predicate.
/// Yes, No, or Maybe.
public abstract Answer CheckPredicate(Element! e, IExpr! pred);
///
/// 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.
///
/// The lattice element.
/// The first variable.
/// The second variable.
/// Yes, No, or Maybe.
public abstract Answer CheckVariableDisequality(Element! e, IVariable! var1, IVariable! var2);
///
/// A default implementation of the given
/// the appropriate expression factories by calling CheckPredicate.
///
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*/!> entry in eToResult.GetSourceToResult())
{
IVariable! sourceName = entry.Key;
ISet/**/!>! emptyDictionary1 = new G.Dictionary*/!>();
private static readonly G.Dictionary! emptyDictionary2 = new G.Dictionary();
public ISet/**//*?*/ 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*/!>> GetSourceToResult()
public IEnumerable! GetSourceToResult()
{
return emptyDictionary1;
}
//public G.IEnumerable> 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/**/! indexMap = new Hashtable();
private static Hashtable/**/! 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;
}
///
/// 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.
///
public interface ICombineNameMap
{
ISet/**//*?*/ GetResultNames(IVariable! srcname);
IVariable/*?*/ GetSourceName(IVariable! resname);
//TODO: uncommet when works in compiler
//G.IEnumerable*/!>> GetSourceToResult();
IEnumerable! GetSourceToResult();
//G.IEnumerable> GetResultToSource();
IEnumerable! GetResultToSource();
}
///
/// Provides statistics on the number of times an operation is performed
/// and forwards the real operations to the given lattice in the constructor.
///
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/**/! 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);
}
}
}