singrdk/base/build/boogie/PRELUDE.bpl

651 lines
33 KiB
Plaintext
Raw Normal View History

2008-11-17 18:29:00 -05:00
// *******************************************
// * *
// * Boogie Procedure Language Prelude *
// * *
// *******************************************
//------------ New types
type real;
type elements;
type struct;
type exposeVersionType;
//------------ Encode the heap
var $Heap : [ref, <x>name]x where IsHeap($Heap);
// IsHeap(h) holds if h is a properly formed heap
function IsHeap(h: [ref, <x>name]x) returns (bool);
// records whether a pointer refers to allocated data
const unique $allocated : <bool>name;
// the elements from dereferencing an array pointer
const unique $elements : <elements>name;
// the name of the most derived class for which an object invariant holds
const unique $inv : <name>name;
// the name of (the supertype of) the class for which the object invariant is allowed to be broken
const unique $localinv: <name>name;
// dummy field that is havoced at unpacks so that it can be used to deduce state changes
const unique $exposeVersion : <exposeVersionType>name;
// declaration type of exposeVersion is System.Object
axiom DeclType($exposeVersion) == System.Object;
// the $sharingMode field indicates the object's sharing mode, which is either $SharingMode_Unshared or $SharingMode_LockProtected
const unique $sharingMode : name;
const unique $SharingMode_Unshared : name;
const unique $SharingMode_LockProtected : name;
// a reference to the object ($ownerRef, $ownerFrame)
const unique $ownerRef : <ref>name;
const unique $ownerFrame : <name>name;
const unique $PeerGroupPlaceholder : name; // used as a type name
// a map from class names to their representative "references" used to obtain values of static fields
function ClassRepr(class: name) returns (ref);
// this map is injective
function ClassReprInv(ref) returns (name);
axiom (∀ c: name • {ClassRepr(c)} ClassReprInv(ClassRepr(c)) == c);
axiom (∀ T:name • ¬($typeof(ClassRepr(T)) <: System.Object));
axiom (∀ T:name • ClassRepr(T) ≠ null);
axiom (∀ T:name, h:[ref,<x>name]x • {h[ClassRepr(T),$ownerFrame]} IsHeap(h) ⇒ h[ClassRepr(T),$ownerFrame] == $PeerGroupPlaceholder);
//------------ Fields
// fields are classified into whether or not the field is static (i.e., it is a field of a ClassRepr "ref")
// and whether or not it is directly modifiable by the user
// indicates a field is directly modifiable by the user
function IsDirectlyModifiableField(f: name) returns (bool);
axiom ¬(IsDirectlyModifiableField($allocated));
axiom IsDirectlyModifiableField($elements); // note: $elements is directly modifiable by the user
axiom ¬(IsDirectlyModifiableField($inv));
axiom ¬(IsDirectlyModifiableField($localinv));
axiom ¬(IsDirectlyModifiableField($ownerRef));
axiom ¬(IsDirectlyModifiableField($ownerFrame));
axiom ¬(IsDirectlyModifiableField($exposeVersion));
// indicates a field is static
function IsStaticField(f: name) returns (bool);
axiom ¬IsStaticField($allocated);
axiom ¬IsStaticField($elements);
axiom ¬IsStaticField($inv);
axiom ¬IsStaticField($localinv);
axiom ¬IsStaticField($exposeVersion);
// indicates if a is included in modifies o.* and o.**
function $IncludedInModifiesStar(f: name) returns (bool);
axiom ¬$IncludedInModifiesStar($ownerRef);
axiom ¬$IncludedInModifiesStar($ownerFrame);
// $inv and $localinv are not included either, but we don't need to say that in an axiom
axiom $IncludedInModifiesStar($exposeVersion);
axiom $IncludedInModifiesStar($elements);
//------------ Array elements
function ValueArrayGet (elements, int) returns (any);
function ValueArraySet (elements, int, any) returns (elements);
function IntArrayGet (elements, int) returns (int);
function IntArraySet (elements, int, int) returns (elements);
function RefArrayGet (elements, int) returns (ref);
function RefArraySet (elements, int, ref) returns (elements);
axiom (∀ A:elements, i:int, x:any • ValueArrayGet(ValueArraySet(A, i, x), i) == x);
axiom (∀ A:elements, i:int, j:int, x:any • i ≠ j ⇒ ValueArrayGet(ValueArraySet(A, i, x), j) == ValueArrayGet(A, j));
axiom (∀ A:elements, i:int, x:int • IntArrayGet(IntArraySet(A, i, x), i) == x);
axiom (∀ A:elements, i:int, j:int, x:int • i ≠ j ⇒ IntArrayGet(IntArraySet(A, i, x), j) == IntArrayGet(A, j));
axiom (∀ A:elements, i:int, x:ref • RefArrayGet(RefArraySet(A, i, x), i) == x);
axiom (∀ A:elements, i:int, j:int, x:ref • i ≠ j ⇒ RefArrayGet(RefArraySet(A, i, x), j) == RefArrayGet(A, j));
// the indices of multi-dimensional arrays are built up one dimension at a time
function ArrayIndex(arr: ref, dim: int, indexAtDim: int, remainingIndexContribution: int) returns (int);
// the expressions built up are injective in the indices
function ArrayIndexInvX(arrayIndex: int) returns (indexAtDim: int);
function ArrayIndexInvY(arrayIndex: int) returns (remainingIndexContribution: int);
axiom (∀ a:ref, d:int, x: int, y: int • {ArrayIndex(a,d,x,y)} ArrayIndexInvX(ArrayIndex(a,d,x,y)) == x);
axiom (∀ a:ref, d:int, x: int, y: int • {ArrayIndex(a,d,x,y)} ArrayIndexInvY(ArrayIndex(a,d,x,y)) == y);
axiom (∀ a:ref, i:int, heap:[ref,<x>name]x •
{ IntArrayGet(heap[a, $elements], i) }
IsHeap(heap) ⇒ InRange(IntArrayGet(heap[a, $elements], i), $ElementType($typeof(a))));
axiom (∀ a:ref, i:int, heap:[ref,<x>name]x •
{ $typeof(RefArrayGet(heap[a, $elements], i)) }
IsHeap(heap) ∧ RefArrayGet(heap[a, $elements], i) ≠ null ⇒
$typeof(RefArrayGet(heap[a, $elements], i)) <: $ElementType($typeof(a)));
axiom (∀ a:ref, T:name, i:int, r:int, heap:[ref,<x>name]x •
{ $typeof(a) <: NonNullRefArray(T, r), RefArrayGet(heap[a, $elements], i) }
IsHeap(heap) ∧ $typeof(a) <: NonNullRefArray(T,r) ⇒ RefArrayGet(heap[a, $elements], i) ≠ null);
//------------ Array properties: rank, length, dimensions, upper and lower bounds
function $Rank (ref) returns (int);
axiom (∀ a:ref • 1 ≤ $Rank(a));
axiom (∀ a:ref, T:name, r:int • {$typeof(a) <: RefArray(T,r)} a ≠ null ∧ $typeof(a) <: RefArray(T,r) ⇒ $Rank(a) == r);
axiom (∀ a:ref, T:name, r:int • {$typeof(a) <: NonNullRefArray(T,r)} a ≠ null ∧ $typeof(a) <: NonNullRefArray(T,r) ⇒ $Rank(a) == r);
axiom (∀ a:ref, T:name, r:int • {$typeof(a) <: ValueArray(T,r)} a ≠ null ∧ $typeof(a) <: ValueArray(T,r) ⇒ $Rank(a) == r);
axiom (∀ a:ref, T:name, r:int • {$typeof(a) <: IntArray(T,r)} a ≠ null ∧ $typeof(a) <: IntArray(T,r) ⇒ $Rank(a) == r);
function $Length (ref) returns (int);
axiom (∀ a:ref • {$Length(a)} 0 ≤ $Length(a) ∧ $Length(a) ≤ int#2147483647);
function $DimLength (ref, int) returns (int); // length per dimension up to rank
axiom (∀ a:ref, i:int • 0 ≤ $DimLength(a,i));
// The trigger used in the following axiom is restrictive, so that this disjunction is not
// produced too easily. Is the trigger perhaps sometimes too restrictive?
axiom (∀ a:ref • { $DimLength(a,0) } $Rank(a) == 1 ⇒ $DimLength(a,0) == $Length(a));
function $LBound (ref, int) returns (int);
function $UBound (ref, int) returns (int);
// Right now we only model C# arrays:
axiom (∀ a:ref, i:int • {$LBound(a,i)} $LBound(a,i) == 0);
axiom (∀ a:ref, i:int • {$UBound(a,i)} $UBound(a,i) == $DimLength(a,i)-1);
// Different categories of arrays are different types
const unique $ArrayCategoryValue: name;
const unique $ArrayCategoryInt: name;
const unique $ArrayCategoryRef: name;
const unique $ArrayCategoryNonNullRef: name;
function $ArrayCategory(arrayType: name) returns (arrayCategory: name);
axiom (∀ T: name, ET: name, r: int • { T <: ValueArray(ET, r) } T <: ValueArray(ET, r) ⇒ $ArrayCategory(T) == $ArrayCategoryValue);
axiom (∀ T: name, ET: name, r: int • { T <: IntArray(ET, r) } T <: IntArray(ET, r) ⇒ $ArrayCategory(T) == $ArrayCategoryInt);
axiom (∀ T: name, ET: name, r: int • { T <: RefArray(ET, r) } T <: RefArray(ET, r) ⇒ $ArrayCategory(T) == $ArrayCategoryRef);
axiom (∀ T: name, ET: name, r: int • { T <: NonNullRefArray(ET, r) } T <: NonNullRefArray(ET, r) ⇒ $ArrayCategory(T) == $ArrayCategoryNonNullRef);
//------------ Array types
const unique System.Array : name;
axiom System.Array <: System.Object;
function $ElementType(name) returns (name);
function ValueArray (elementType:name, rank:int) returns (name);
axiom (∀ T:name, r:int • {ValueArray(T,r)} ValueArray(T,r) <: ValueArray(T,r) ∧ ValueArray(T,r) <: System.Array);
function IntArray (elementType:name, rank:int) returns (name);
axiom (∀ T:name, r:int • {IntArray(T,r)} IntArray(T,r) <: IntArray(T,r) ∧ IntArray(T,r) <: System.Array);
function RefArray (elementType:name, rank:int) returns (name);
axiom (∀ T:name, r:int • {RefArray(T,r)} RefArray(T,r) <: RefArray(T,r) ∧ RefArray(T,r) <: System.Array);
function NonNullRefArray (elementType:name, rank:int) returns (name);
axiom (∀ T:name, r:int • {NonNullRefArray(T,r)} NonNullRefArray(T,r) <: NonNullRefArray(T,r) ∧ NonNullRefArray(T,r) <: System.Array);
// arrays of references are co-variant
axiom (∀ T:name, U:name, r:int • U <: T ⇒ RefArray(U,r) <: RefArray(T,r));
axiom (∀ T:name, U:name, r:int • U <: T ⇒ NonNullRefArray(U,r) <: NonNullRefArray(T,r));
axiom (∀ A: name, r: int • $ElementType(ValueArray(A,r)) == A);
axiom (∀ A: name, r: int • $ElementType(IntArray(A,r)) == A);
axiom (∀ A: name, r: int • $ElementType(RefArray(A,r)) == A);
axiom (∀ A: name, r: int • $ElementType(NonNullRefArray(A,r)) == A);
// subtypes of array types
axiom (∀ A: name, r: int, T: name • {T <: RefArray(A,r)} T <: RefArray(A,r) ⇒ T ≠ A ∧ T == RefArray($ElementType(T),r) ∧ $ElementType(T) <: A);
axiom (∀ A: name, r: int, T: name • {T <: NonNullRefArray(A,r)} T <: NonNullRefArray(A,r) ⇒ T ≠ A ∧ T == NonNullRefArray($ElementType(T),r) ∧ $ElementType(T) <: A);
axiom (∀ A: name, r: int, T: name • {T <: ValueArray(A, r)} T <: ValueArray(A, r) ⇒ T == ValueArray(A, r));
axiom (∀ A: name, r: int, T: name • {T <: IntArray(A, r)} T <: IntArray(A, r) ⇒ T == IntArray(A, r));
// supertypes of array types
axiom (∀ A: name, r: int, T: name • {RefArray(A,r) <: T} RefArray(A,r) <: T ⇒ System.Array <: T (T == RefArray($ElementType(T),r) ∧ A <: $ElementType(T)));
axiom (∀ A: name, r: int, T: name • {NonNullRefArray(A,r) <: T} NonNullRefArray(A,r) <: T ⇒ System.Array <: T (T == NonNullRefArray($ElementType(T),r) ∧ A <: $ElementType(T)));
axiom (∀ A: name, r: int, T: name • {ValueArray(A, r) <: T} ValueArray(A, r) <: T ⇒ System.Array <: T T == ValueArray(A, r));
axiom (∀ A: name, r: int, T: name • {IntArray(A, r) <: T} IntArray(A, r) <: T ⇒ System.Array <: T T == IntArray(A, r));
function $ArrayPtr (elementType:name) returns (name);
//------------ Encode structs
function $StructGet (struct, name) returns (any);
function $StructSet (struct, name, any) returns (struct);
axiom (∀ s:struct, f:name, x:any • $StructGet($StructSet(s, f, x), f) == x);
axiom (∀ s:struct, f:name, f':name, x:any • f ≠ f' ⇒ $StructGet($StructSet(s, f, x), f') == $StructGet(s, f'));
function ZeroInit(s:struct, typ:name) returns (bool);
// TODO: ZeroInit needs axiomatization that says the fields of s are 0 or null or ZeroInit, depending on their types
//------------ Encode type information
function $typeof (ref) returns (name);
function $BaseClass(sub: name) returns (base: name);
axiom (∀ T: name • { $BaseClass(T) } T <: $BaseClass(T) ∧ (T != System.Object ==> T != $BaseClass(T)));
// Incomparable subtype axiom:
function AsDirectSubClass(sub: name, base: name) returns (sub': name);
function OneClassDown(sub: name, base: name) returns (directSub: name);
axiom (∀ A: name, B: name, C: name • { C <: AsDirectSubClass(B,A) } C <: AsDirectSubClass(B,A) ⇒ OneClassDown(C,A) == B);
// primitive types are unordered in the type ordering
function $IsValueType(name) returns (bool);
axiom (∀ T: name • $IsValueType(T) ⇒ (∀ U: name • T <: U ⇒ T == U) ∧ (∀ U: name • U <: T ⇒ T == U));
// type constructor T[]
//
const unique System.Object : name;
// reflection
//
function $IsTokenForType (struct, name) returns (bool);
function TypeObject (name) returns (ref); // Corresponds with C# typeof(T)
const unique System.Type : name;
axiom System.Type <: System.Object;
axiom (∀ T:name • {TypeObject(T)} $IsNotNull(TypeObject(T), System.Type));
function TypeName(ref) returns (name); // the inverse of TypeObject, which is injective
axiom (∀ T:name • {TypeObject(T)} TypeName(TypeObject(T)) == T);
function $Is (ref, name) returns (bool);
axiom (∀ o:ref, T:name • {$Is(o, T)} $Is(o, T) ⇔ o == null $typeof(o) <: T);
function $IsNotNull(ref, name) returns (bool);
axiom (∀ o:ref, T:name • {$IsNotNull(o, T)} $IsNotNull(o, T) ⇔ o ≠ null ∧ $Is(o,T));
// $As(o,T) is to be used only when T denotes a reference type (see also BoxTester). It returns either o or null.
function $As (ref, name) returns (ref);
axiom (∀ o:ref, T:name • $Is(o, T) ⇒ $As(o, T) == o);
axiom (∀ o:ref, T:name • ¬ $Is(o, T) ⇒ $As(o, T) == null);
// Arrays are always valid (but may be committed)
axiom (∀ h: [ref,<x>name]x, o: ref • {$typeof(o) <: System.Array, h[o,$inv]} IsHeap(h) ∧ o ≠ null ∧ $typeof(o) <: System.Array ⇒ h[o,$inv] == $typeof(o) ∧ h[o,$localinv] == $typeof(o));
//---------- Types and allocation of reachable things
function IsAllocated(h: [ref,<x>name]x, o: any) returns (bool);
// everything in the range of a proper heap is allocated whenever the domain is
axiom (∀ h: [ref,<x>name]x, o: ref, f: name • {IsAllocated(h, h[o,f])} IsHeap(h) ∧ h[o, $allocated] ⇒ IsAllocated(h, h[o,f]));
axiom (∀ h: [ref,<x>name]x, o: ref, f: <ref>name • {h[h[o,f], $allocated]} IsHeap(h) ∧ h[o, $allocated] ⇒ h[h[o,f], $allocated]);
axiom (∀ h: [ref,<x>name]x, s: struct, f: name • {IsAllocated(h, $StructGet(s,f))} IsAllocated(h,s) ⇒ IsAllocated(h, $StructGet(s,f)));
axiom (∀ h: [ref,<x>name]x, e: elements, i: int• {IsAllocated(h, RefArrayGet(e,i))} IsAllocated(h,e) ⇒ IsAllocated(h, RefArrayGet(e,i)));
axiom (∀ h: [ref,<x>name]x, e: elements, i: int• {IsAllocated(h, ValueArrayGet(e,i))} IsAllocated(h,e) ⇒ IsAllocated(h, ValueArrayGet(e,i)));
// no need to have IsAllocated for IntArray's
axiom (∀ h: [ref,<x>name]x, o: ref • {h[o, $allocated]} IsAllocated(h,o) ⇒ h[o, $allocated]);
axiom (∀ h: [ref,<x>name]x, c:name • {h[ClassRepr(c), $allocated]} IsHeap(h) ⇒ h[ClassRepr(c), $allocated]);
const unique $BeingConstructed: ref;
const unique $NonNullFieldsAreInitialized: <bool>name;
axiom DeclType($NonNullFieldsAreInitialized) == System.Object;
// types of fields
function DeclType(field: name) returns (class: name); // for "class C { T f; ...", DeclType(f) == C
function AsNonNullRefField(field: <ref>name, T: name) returns (f: <ref>name); // for "class C { T! f; ...", AsNonNullRefField(f,T) == f
function AsRefField(field: <ref>name, T: name) returns (f: <ref>name); // for "class C { T f; ...", AsRefField(f,T) == f
// for integral types T
function AsRangeField(field: <int>name, T: name) returns (f: <int>name); // for "class C { T f; ...", AsRangeField(f,T) == f
axiom (∀ f: <ref>name, T: name • {AsNonNullRefField(f,T)} AsNonNullRefField(f,T)==f ⇒ AsRefField(f,T)==f);
// fields in the heap are well typed
axiom (∀ h: [ref,<x>name]x, o: ref, f: <ref>name, T: name • {h[o,AsRefField(f,T)]} IsHeap(h) ⇒ $Is(h[o,AsRefField(f,T)], T));
axiom (∀ h: [ref,<x>name]x, o: ref, f: <ref>name, T: name • {h[o,AsNonNullRefField(f,T)]} IsHeap(h) ∧ o ≠ null ∧ (o ≠ $BeingConstructed h[$BeingConstructed, $NonNullFieldsAreInitialized] == true) ⇒ h[o,AsNonNullRefField(f,T)] ≠ null);
axiom (∀ h: [ref,<x>name]x, o: ref, f: <int>name, T: name • {h[o,AsRangeField(f,T)]} IsHeap(h) ⇒ InRange(h[o,AsRangeField(f,T)], T));
// abstract classes, interfaces, ...
function $IsMemberlessType(name) returns (bool);
axiom (∀ o: ref • { $IsMemberlessType($typeof(o)) } ¬$IsMemberlessType($typeof(o)));
function $AsInterface(name) returns (name);
// this axiom relates a boxed struct to any interfaces that the struct implements
// otherwise, all that is known is that a boxed struct is of type System.Object which isn't strong enough
axiom (∀ $J: name, s: any, b: ref • { UnboxedType(Box(s,b)) <: $AsInterface($J) } $AsInterface($J) == $J && Box(s,b)==b && UnboxedType(Box(s,b)) <: $AsInterface($J) ==> $typeof(b) <: $J);
//------------ Immutable types
function $IsImmutable(T:name) returns (bool);
// We say here that System.Object is mutable, but only using the $IsImmutable predicate. The functions
// $AsImmutable and $AsMutable below are used to say that all subtypes below fixpoints of these functions
// are also fixpoints.
axiom !$IsImmutable(System.Object);
function $AsImmutable(T:name) returns (theType: name);
function $AsMutable(T:name) returns (theType: name);
axiom (∀ T: name, U:name • {U <: $AsImmutable(T)} U <: $AsImmutable(T) ⇒ $IsImmutable(U) ∧ $AsImmutable(U) == U);
axiom (∀ T: name, U:name • {U <: $AsMutable(T)} U <: $AsMutable(T) ⇒ !$IsImmutable(U) ∧ $AsMutable(U) == U);
function AsOwner(string: ref, owner: ref) returns (theString: ref);
axiom (∀ o: ref , T:name • {$typeof(o) <: $AsImmutable(T)}
o ≠ null ∧ o ≠ $BeingConstructed ∧ $typeof(o) <: $AsImmutable(T)
(∀ h: [ref,<x>name]x • {IsHeap(h)}
IsHeap(h)
h[o, $inv] == $typeof(o) ∧ h[o, $localinv] == $typeof(o) ∧
h[o, $ownerFrame] == $PeerGroupPlaceholder ∧
AsOwner(o, h[o, $ownerRef]) == o ∧
(∀ t: ref • {AsOwner(o, h[t, $ownerRef])}
AsOwner(o, h[t, $ownerRef]) == o
t == o h[t, $ownerFrame] ≠ $PeerGroupPlaceholder)));
//------------ Encode methodology
const unique System.String: name;
function $StringLength (ref) returns (int);
axiom (∀ s:ref • {$StringLength(s)} 0 ≤ $StringLength(s));
// for rep fields
function AsRepField(f: <ref>name, declaringType: name) returns (theField: <ref>name);
axiom (∀ h: [ref,<x>name]x, o: ref, f: <ref>name, T: name • {h[o,AsRepField(f,T)]} IsHeap(h) ∧ h[o,AsRepField(f,T)] ≠ null ⇒ h[h[o,AsRepField(f,T)], $ownerRef] == o ∧ h[h[o,AsRepField(f,T)], $ownerFrame] == T);
// for peer fields
function AsPeerField(f: <ref>name) returns (theField: <ref>name);
axiom (∀ h: [ref,<x>name]x, o: ref, f: <ref>name • {h[o,AsPeerField(f)]} IsHeap(h) ∧ h[o,AsPeerField(f)] ≠ null ⇒ h[h[o,AsPeerField(f)], $ownerRef] == h[o, $ownerRef] ∧ h[h[o,AsPeerField(f)], $ownerFrame] == h[o, $ownerFrame]);
// committed fields are fully valid
axiom (∀ h:[ref,<x>name]x, o:ref • {h[h[o,$ownerRef], $inv] <: h[o, $ownerFrame] }
IsHeap(h) ∧ h[o,$ownerFrame] ≠ $PeerGroupPlaceholder ∧ h[h[o,$ownerRef], $inv] <: h[o, $ownerFrame] ∧ h[h[o,$ownerRef], $localinv] ≠ $BaseClass(h[o, $ownerFrame]) ⇒
h[o,$inv] == $typeof(o) ∧ h[o,$localinv] == $typeof(o));
// The following procedure sets the owner of o and all its peers to (ow,fr).
// It expects o != null && o.$ownerFrame==$PeerGroupPlaceholder, but this condition is checked at the call site.
procedure $SetOwner(o: ref, ow: ref, fr: name);
modifies $Heap;
ensures (∀ p: ref, F: name •
{ $Heap[p, F] }
(F ≠ $ownerRef ∧ F ≠ $ownerFrame)
old($Heap[p, $ownerRef] ≠ $Heap[o, $ownerRef])
old($Heap[p, $ownerFrame] ≠ $Heap[o, $ownerFrame])
⇒ old($Heap[p, F]) == $Heap[p, F]);
ensures (∀ p: ref •
{ $Heap[p, $ownerRef] }
{ $Heap[p, $ownerFrame] }
old($Heap[p, $ownerRef] == $Heap[o, $ownerRef]) ∧
old($Heap[p, $ownerFrame] == $Heap[o, $ownerFrame])
⇒ $Heap[p, $ownerRef] == ow ∧ $Heap[p, $ownerFrame] == fr);
// The following procedure is called for "o.f = e;" where f is a rep field declared in a class T:
procedure $UpdateOwnersForRep(o: ref, T: name, e: ref);
modifies $Heap;
ensures (∀ p: ref, F: name •
{ $Heap[p, F] }
(F ≠ $ownerRef ∧ F ≠ $ownerFrame)
old($Heap[p, $ownerRef] ≠ $Heap[e, $ownerRef])
old($Heap[p, $ownerFrame] ≠ $Heap[e, $ownerFrame])
⇒ old($Heap[p, F]) == $Heap[p, F]);
ensures e == null ⇒ $Heap == old($Heap);
ensures e ≠ null ⇒ (∀ p: ref •
{ $Heap[p, $ownerRef] }
{ $Heap[p, $ownerFrame] }
old($Heap[p, $ownerRef] == $Heap[e, $ownerRef]) ∧
old($Heap[p, $ownerFrame] == $Heap[e, $ownerFrame])
⇒ $Heap[p, $ownerRef] == o ∧ $Heap[p, $ownerFrame] == T);
// The following procedure is called for "c.f = d;" where f is a peer field:
procedure $UpdateOwnersForPeer(c: ref, d: ref);
modifies $Heap;
ensures (∀ p: ref, F: name •
{ $Heap[p, F] }
(F ≠ $ownerRef ∧ F ≠ $ownerFrame)
old($Heap[p, $ownerRef] ≠ $Heap[d, $ownerRef] $Heap[p, $ownerFrame] ≠ $Heap[d, $ownerFrame])
⇒ old($Heap[p, F]) == $Heap[p, F]);
ensures d == null ⇒ $Heap == old($Heap);
ensures d ≠ null ⇒ (∀ p: ref •
{ $Heap[p, $ownerRef] }
{ $Heap[p, $ownerFrame] }
old($Heap[p, $ownerRef] == $Heap[d, $ownerRef] ∧ $Heap[p, $ownerFrame] == $Heap[d, $ownerFrame])
$Heap[p, $ownerRef] == old($Heap)[c, $ownerRef] ∧
$Heap[p, $ownerFrame] == old($Heap)[c, $ownerFrame]);
// Intuitively, the $FirstConsistentOwner field of an object is defined as the closest
// transitive owner that is consistent. The field is defined if the object is committed.
const unique $FirstConsistentOwner: <ref>name;
function $AsPureObject(ref) returns (ref); // used only for triggering
function ##FieldDependsOnFCO(o: ref, f: name, ev: exposeVersionType) returns (value: any);
// The following axiom say that for any committed object o, each field of o is determined
// not by the exposeVersion of o's first consistent owner.
axiom (∀ o: ref, f: name, h: [ref,<x>name]x •
{ h[$AsPureObject(o), f] }
IsHeap(h) ∧
o ≠ null ∧ h[o, $allocated] == true ∧
h[o, $ownerFrame] ≠ $PeerGroupPlaceholder ∧
h[h[o, $ownerRef], $inv] <: h[o, $ownerFrame] ∧
h[h[o, $ownerRef], $localinv] ≠ $BaseClass(h[o, $ownerFrame])
h[o, f] == ##FieldDependsOnFCO(o, f, h[h[o, $FirstConsistentOwner], $exposeVersion]));
axiom (∀ o: ref, h: [ref,<x>name]x •
{ h[o, $FirstConsistentOwner] }
IsHeap(h) ∧
o ≠ null ∧ h[o, $allocated] == true ∧
h[o, $ownerFrame] ≠ $PeerGroupPlaceholder ∧
h[h[o, $ownerRef], $inv] <: h[o, $ownerFrame] ∧
h[h[o, $ownerRef], $localinv] ≠ $BaseClass(h[o, $ownerFrame])
h[o, $FirstConsistentOwner] != null ∧
h[h[o, $FirstConsistentOwner], $allocated] == true ∧
// ¬ h[h[o, $FirstConsistentOwner], Committed]
(h[h[o, $FirstConsistentOwner], $ownerFrame] == $PeerGroupPlaceholder
¬(h[h[h[o, $FirstConsistentOwner], $ownerRef], $inv] <: h[h[o, $FirstConsistentOwner], $ownerFrame])
h[h[h[o, $FirstConsistentOwner], $ownerRef], $localinv] == $BaseClass(h[h[o, $FirstConsistentOwner], $ownerFrame])));
//---------- Boxed and unboxed values
// Unboxing is functional, but boxing is not
function Box (any, ref) returns (ref); // first arg is any because of reals and other non-int values
function Unbox (ref) returns (any);
// For simplicity, we track boxed values stored to locals, not those stored into the heap.
axiom (∀ x:any, p:ref • {Unbox(Box(x,p))} Unbox(Box(x,p)) == x);
function UnboxedType(ref) returns (name);
// Boxes are always consistent
axiom (∀ p: ref • {$IsValueType(UnboxedType(p))} $IsValueType(UnboxedType(p)) ⇒ (∀ heap: [ref,<x>name]x, x: any • {heap[Box(x,p),$inv]} IsHeap(heap) ⇒ heap[Box(x,p),$inv] == $typeof(Box(x,p)) ∧ heap[Box(x,p),$localinv] == $typeof(Box(x,p))));
// For reference types, boxing returns the reference
axiom (∀ x:any, p:ref • {UnboxedType(Box(x,p)) <: System.Object} UnboxedType(Box(x,p)) <: System.Object ∧ Box(x,p) == p ⇒ x == p);
// BoxTester is the value type equivalent of $As
function BoxTester(p:ref, typ: name) returns (ref);
axiom (∀ p:ref, typ: name • {BoxTester(p,typ)} UnboxedType(p) == typ ⇔ BoxTester(p,typ) ≠ null);
//---------- Various sized integers
const unique System.SByte : name; // sbyte
axiom $IsValueType(System.SByte);
const unique System.Byte : name; // byte
axiom $IsValueType(System.Byte);
const unique System.Int16 : name; //short
axiom $IsValueType(System.Int16);
const unique System.UInt16 : name; // ushort
axiom $IsValueType(System.UInt16);
const unique System.Int32 : name; // int
axiom $IsValueType(System.Int32);
const unique System.UInt32 : name; // uint
axiom $IsValueType(System.UInt32);
const unique System.Int64 : name; // long
axiom $IsValueType(System.Int64);
const unique System.UInt64 : name; // ulong
axiom $IsValueType(System.UInt64);
const unique System.Char : name; // char
axiom $IsValueType(System.Char);
const unique System.UIntPtr : name;
axiom $IsValueType(System.UIntPtr);
const unique System.IntPtr : name;
axiom $IsValueType(System.IntPtr);
const unique int#m2147483648: int; // System.Int32.MinValue
const unique int#2147483647: int; // System.Int32.MaxValue
const unique int#4294967295: int; // System.UInt32.MaxValue
const unique int#m9223372036854775808: int; // System.Int64.MinValue
const unique int#9223372036854775807: int; // System.Int64.MaxValue
const unique int#18446744073709551615: int; // System.UInt64.MaxValue
axiom int#m9223372036854775808 < int#m2147483648;
axiom int#m2147483648 < -100000;
axiom 100000 < int#2147483647;
axiom int#2147483647 < int#4294967295;
axiom int#4294967295 < int#9223372036854775807;
axiom int#9223372036854775807 < int#18446744073709551615;
axiom int#m9223372036854775808 + 1 == - int#9223372036854775807;
axiom int#m2147483648 + 1 == - int#2147483647;
function InRange(i: int, T: name) returns (bool);
axiom (∀ i:int • InRange(i, System.SByte) ⇔ -128 ≤ i ∧ i < 128);
axiom (∀ i:int • InRange(i, System.Byte) ⇔ 0 ≤ i ∧ i < 256);
axiom (∀ i:int • InRange(i, System.Int16) ⇔ -32768 ≤ i ∧ i < 32768);
axiom (∀ i:int • InRange(i, System.UInt16) ⇔ 0 ≤ i ∧ i < 65536);
axiom (∀ i:int • InRange(i, System.Int32) ⇔ int#m2147483648 ≤ i ∧ i ≤ int#2147483647);
axiom (∀ i:int • InRange(i, System.UInt32) ⇔ 0 ≤ i ∧ i ≤ int#4294967295);
axiom (∀ i:int • InRange(i, System.Int64) ⇔ int#m9223372036854775808 ≤ i ∧ i ≤ int#9223372036854775807);
axiom (∀ i:int • InRange(i, System.UInt64) ⇔ 0 ≤ i ∧ i ≤ int#18446744073709551615);
axiom (∀ i:int • InRange(i, System.Char) ⇔ 0 ≤ i ∧ i < 65536);
//---------- Type conversions and sizes
function $IntToInt(val: int, fromType: name, toType: name) returns (int);
function $IntToReal(int, fromType: name, toType: name) returns (real);
function $RealToInt(real, fromType: name, toType: name) returns (int);
function $RealToReal(val: real, fromType: name, toType: name) returns (real);
axiom (∀ z: int, B: name, C: name • InRange(z, C) ⇒ $IntToInt(z, B, C) == z);
function $SizeIs (name, int) returns (bool); // SizeIs(T,n) means that n = sizeof(T)
//------------ Formula/term operators
function $IfThenElse (bool, any, any) returns (any);
axiom (∀ b:bool, x:any, y:any • {$IfThenElse(b,x,y)} b ⇒ $IfThenElse(b,x,y) == x);
axiom (∀ b:bool, x:any, y:any • {$IfThenElse(b,x,y)} ¬b ⇒ $IfThenElse(b,x,y) == y);
//------------ Bit-level operators
function #neg (int) returns (int);
function #and (int, int) returns (int);
function #or (int, int) returns (int);
function #xor (int, int) returns (int);
function #shl (int, int) returns (int);
function #shr (int, int) returns (int);
function #rneg(real) returns (real);
function #radd(real, real) returns (real);
function #rsub(real, real) returns (real);
function #rmul(real, real) returns (real);
function #rdiv(real, real) returns (real);
function #rmod(real, real) returns (real);
function #rLess(real, real) returns (bool);
function #rAtmost(real, real) returns (bool);
function #rEq(real, real) returns (bool);
function #rNeq(real, real) returns (bool);
function #rAtleast(real, real) returns (bool);
function #rGreater(real, real) returns (bool);
//----------- Properties of operators
// the connection between % and /
axiom (∀ x:int, y:int • {x % y} {x / y} x % y == x - x / y * y);
// remainder is C# is complicated, because division rounds toward 0
axiom (∀ x:int, y:int • {x % y} 0 ≤ x ∧ 0 < y ⇒ 0 ≤ x % y ∧ x % y < y);
axiom (∀ x:int, y:int • {x % y} 0 ≤ x ∧ y < 0 ⇒ 0 ≤ x % y ∧ x % y < -y);
axiom (∀ x:int, y:int • {x % y} x ≤ 0 ∧ 0 < y ⇒ -y < x % y ∧ x % y ≤ 0);
axiom (∀ x:int, y:int • {x % y} x ≤ 0 ∧ y < 0 ⇒ y < x % y ∧ x % y ≤ 0);
axiom (∀ x:int, y:int • {(x + y) % y} 0 ≤ x ∧ 0 ≤ y ⇒ (x + y) % y == x % y);
// do we need this symmetric one, too?
axiom (∀ x:int, y:int • {(y + x) % y} 0 ≤ x ∧ 0 ≤ y ⇒ (y + x) % y == x % y);
axiom (∀ x:int, y:int • {(x - y) % y} 0 ≤ x-y ∧ 0 ≤ y ⇒ (x - y) % y == x % y);
// the following axiom prevents a matching loop in Simplify
// axiom (∀ x:int, y:int • {x * y / y * y} x * y / y * y == x * y);
// the following axiom has some unfortunate matching, but it does state a property about % that
// is sometime useful
axiom (∀ a: int, b: int, d: int • { a % d, b % d } 2 ≤ d ∧ a % d == b % d ∧ a < b ⇒ a + d ≤ b);
/***** These axioms provide good functionality, but in some cases they can be very expensive
// distributions of * and +/-
axiom (∀ x: int, y: int, z: int • { (x+y)*z } (x+y)*z == x*z + y*z);
axiom (∀ x: int, y: int, z: int • { (x-y)*z } (x-y)*z == x*z - y*z);
axiom (∀ x: int, y: int, z: int • { z*(x+y) } z*(x+y) == z*x + z*y);
axiom (∀ x: int, y: int, z: int • { z*(x-y) } z*(x-y) == z*x - z*y);
*****/
axiom (∀ x: int, y: int • { #and(x,y) } 0 ≤ x 0 ≤ y ⇒ 0 ≤ #and(x,y));
axiom (∀ x: int, y: int • { #or(x,y) } 0 ≤ x ∧ 0 ≤ y ⇒ 0 ≤ #or(x,y) ∧ #or(x,y) ≤ x + y);
axiom (∀ i:int • {#shl(i,0)} #shl(i,0) == i);
axiom (∀ i:int, j:int • {#shl(i,j)} 1 ≤ j ⇒ #shl(i,j) == #shl(i,j-1) * 2);
axiom (∀ i:int, j:int • {#shl(i,j)} 0 ≤ i ∧ i < 32768 ∧ 0 ≤ j ∧ j ≤ 16 ⇒ 0 ≤ #shl(i, j) ∧ #shl(i, j) ≤ int#2147483647);
axiom (∀ i:int • {#shr(i,0)} #shr(i,0) == i);
axiom (∀ i:int, j:int • {#shr(i,j)} 1 ≤ j ⇒ #shr(i,j) == #shr(i,j-1) / 2);
function #min(int, int) returns (int);
function #max(int, int) returns (int);
axiom (∀ x: int, y: int • { #min(x,y) } (#min(x,y) == x #min(x,y) == y) ∧ #min(x,y) ≤ x ∧ #min(x,y) ≤ y);
axiom (∀ x: int, y: int • { #max(x,y) } (#max(x,y) == x #max(x,y) == y) ∧ x ≤ #max(x,y) ∧ y ≤ #max(x,y));
//---------- Properties of String (Literals)
function #System.String.IsInterned$System.String$notnull([ref,<x>name]x, ref) returns (ref);
function #System.String.Equals$System.String([ref,<x>name]x, ref, ref) returns (bool);
function #System.String.Equals$System.String$System.String([ref,<x>name]x, ref, ref) returns (bool);
function ##StringEquals(ref, ref) returns (bool);
// two names for String.Equals
axiom (∀ h: [ref,<x>name]x, a: ref, b: ref •
{ #System.String.Equals$System.String(h, a, b) }
#System.String.Equals$System.String(h, a, b) == #System.String.Equals$System.String$System.String(h, a, b));
// String.Equals is independent of the heap, and it is reflexive and commutative
axiom (∀ h: [ref,<x>name]x, a: ref, b: ref •
{ #System.String.Equals$System.String$System.String(h, a, b) }
#System.String.Equals$System.String$System.String(h, a, b) == ##StringEquals(a, b) ∧
#System.String.Equals$System.String$System.String(h, a, b) == ##StringEquals(b, a) ∧
(a == b ⇒ ##StringEquals(a, b)));
// String.Equals is also transitive
axiom (∀ a: ref, b: ref, c: ref • ##StringEquals(a, b) ∧ ##StringEquals(b, c) ⇒ ##StringEquals(a, c));
// equal strings have the same interned ref
axiom (∀ h: [ref,<x>name]x, a: ref, b: ref •
{ #System.String.Equals$System.String$System.String(h, a, b) }
a ≠ null ∧ b ≠ null ∧ #System.String.Equals$System.String$System.String(h, a, b)
#System.String.IsInterned$System.String$notnull(h, a) ==
#System.String.IsInterned$System.String$notnull(h, b));
//-----------HACK for unknown value
const unique $UnknownRef : ref;
// ************** END PRELUDE **************