singrdk/base/build/boogie/PRELUDE.bpl

651 lines
33 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// *******************************************
// * *
// * 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 **************