// ---------------------------------------------------------------------- // Reference types type ref; // Type definition for reference types const Void: ref; // Constant for Void references // Is r1 <= r2 in the well-founded order? function ref_rank_leq(r1, r2: ref): bool { r2 == Void ==> r1 == Void } // ---------------------------------------------------------------------- // Heap and allocation type Field _; // Type of a field (with open subtype) // function IsGhostField(field: Field alpha): bool; // Is this field a ghost field? function FieldId(field: Field alpha, t: Type): int; // ID of field within t; used to express that all fields of the same class are distinct. // Default field value function Default(Field alpha): alpha; axiom (forall f: Field bool :: !Default(f)); axiom (forall f: Field int :: Default(f) == 0); axiom (forall f: Field ref :: Default(f) == Void); axiom (forall f: Field real :: Default(f) == 0.0); type HeapType = [ref, Field alpha]alpha; // Type of a heap (with generic field subtype and generic content type) const allocated: Field bool; // Ghost field for allocation status of objects // Function which defines basic properties of a heap function IsHeap(heap: HeapType): bool; // The global heap (which is always a heap) var Heap: HeapType where IsHeap(Heap); // Function that defines properties of two (transitively) successive heaps function HeapSucc(HeapType, HeapType): bool; axiom (forall h: HeapType, r: ref, f: Field alpha, x: alpha :: { h[r, f := x] } IsHeap(h[r, f := x]) ==> HeapSucc(h, h[r, f := x])); axiom (forall a,b,c: HeapType :: { HeapSucc(a,b), HeapSucc(b,c) } HeapSucc(a,b) && HeapSucc(b,c) ==> HeapSucc(a,c)); axiom (forall h: HeapType, k: HeapType :: { HeapSucc(h,k) } HeapSucc(h,k) ==> (forall o: ref :: { k[o, allocated] } h[o, allocated] ==> k[o, allocated])); // ---------------------------------------------------------------------- // Typing type Type; // Type definition for Eiffel types const unique ANY: Type; // Type for ANY const unique NONE: Type; // Type for NONE // Type function for objects. function type_of(o: ref): Type; function is_frozen(t: Type): bool; // Typing axioms axiom (forall t: Type :: { ANY <: t } ANY <: t <==> t == ANY); axiom (forall t: Type :: t <: ANY); // All types inherit from ANY. // axiom (forall t: Type :: NONE <: t); // NONE inherits from all types. // axiom (forall h: HeapType :: IsHeap(h) ==> h[Void, allocated]); // Void is always allocated. axiom (forall h: HeapType, f: Field ref, o: ref :: IsHeap(h) && h[o, allocated] ==> h[h[o, f], allocated]); // All reference fields are allocated. axiom (forall r: ref :: (r == Void) <==> (type_of(r) == NONE)); // Void is only reference of type NONE. // axiom (forall a, b: ref :: (type_of(a) != type_of(b)) ==> (a != b)); // Objects that have different dynamic type cannot be aliased. // axiom (forall t: Type :: is_frozen(t) ==> (forall t2: Type :: t2 <: t ==> t2 == t || t2 == NONE)); // Only NONE inherits from frozen types. axiom (forall t: Type, r: ref :: { is_frozen(t), type_of(r) } (r != Void && type_of(r) <: t && is_frozen(t)) ==> (type_of(r) == t)); // Non-void references of a frozen type are exact. // axiom (forall h: HeapType, t: Type, o: ref :: { is_frozen(t), attached_exact(h, o, t) } IsHeap(h) && is_frozen(t) && attached(h, o, t) ==> attached_exact(h, o, t)); // ---------------------------------------------------------------------- // Built-in attributes const closed: Field bool; // Ghost field for open/closed status of an object. const owner: Field ref; // Ghost field for owner of an object. const owns: Field (Set ref); // Ghost field for owns set of an object. const observers: Field (Set ref); // Ghost field for observers set of an object. const subjects: Field (Set ref); // Ghost field for subjects set of an object. // Analogue of `detachable_attribute' and `set_detachable_attribute' for built-in attributes: axiom (forall heap: HeapType, o: ref :: { heap[o, owner] } IsHeap(heap) && o != Void && heap[o, allocated] ==> heap[heap[o, owner], allocated]); axiom (forall heap: HeapType, o, o': ref :: { heap[o, owns][o'] } IsHeap(heap) && o != Void && heap[o, allocated] && heap[o, owns][o'] ==> heap[o', allocated]); axiom (forall heap: HeapType, o, o': ref :: { heap[o, subjects][o'] } IsHeap(heap) && o != Void && heap[o, allocated] && heap[o, subjects][o'] ==> heap[o', allocated]); axiom (forall heap: HeapType, o, o': ref :: { heap[o, observers][o'] } IsHeap(heap) && o != Void && heap[o, allocated] && heap[o, observers][o'] ==> heap[o', allocated]); // Is o open in h? (not closed and free) function is_open(h: HeapType, o: ref): bool { !h[o, closed] } // Is o closed in h? function is_closed(h: HeapType, o: ref): bool { h[o, closed] } // Is o free? (owner is open) function {:inline} is_free(h: HeapType, o: ref): bool { h[o, owner] == Void } // Is o wrapped in h? (closed and free) function is_wrapped(h: HeapType, o: ref): bool { h[o, closed] && is_free(h, o) } // Is o' in the ownership domain of o? Yes if they are equal, or both closed and o' is transitively owned by o function in_domain(h: HeapType, o: ref, o': ref): bool { o == o' || ( h[o, closed] && h[o', closed] && in_domain(h, o, h[o', owner]) ) } axiom (forall h: HeapType, o, o': ref :: { in_domain(h, o, o') } IsHeap(h) && h[o, closed] && h[o, owns][o'] ==> in_domain(h, o, o')); axiom (forall h: HeapType, o, o': ref :: { in_domain(h, o, o'), trans_owns(h, o) } IsHeap(h) && h[o, closed] ==> (in_trans_owns(h, o, o') <==> in_domain(h, o, o'))); axiom (forall h: HeapType, o, o': ref :: { in_domain(h, o, o') } IsHeap(h) && o != o' && Set#Equal(Set#Empty(), h[o, owns]) ==> !in_domain(h, o, o')); axiom (forall h: HeapType, o, o', o'': ref :: { in_domain(h, o, o'), Set#Equal(Set#Singleton(o''), h[o, owns]) } IsHeap(h) && h[o, closed] && o != o' && Set#Equal(Set#Singleton(o''), h[o, owns]) ==> (in_domain(h, o, o') <==> in_domain(h, o'', o'))); // Frame axiom: domain frames itself axiom (forall h, h': HeapType, x, x': ref :: { in_domain(h, x, x'), in_domain(h', x, x'), HeapSucc(h, h') } IsHeap(h) && IsHeap(h') && HeapSucc(h, h') && h[x, allocated] && h'[x, allocated] && (forall o: ref, f: Field T :: h[o, allocated] ==> h'[o, f] == h[o, f] || !in_domain(h, x, o)) ==> in_domain(h', x, x') == in_domain(h, x, x')); function domain(h: HeapType, o: ref): Set ref { (lambda o': ref :: in_domain(h, o, o')) } // Is o' in the transitive owns of o? // (the same as in_domain if o is closed) function in_trans_owns(h: HeapType, o: ref, o': ref): bool { o == o' || h[o, owns][o'] || (exists x: ref :: h[o, owns][x] && in_trans_owns(h, x, o')) } function trans_owns(h: HeapType, o: ref): Set ref { (lambda o': ref :: in_trans_owns(h, o, o')) } const universe: Set ref; axiom (forall o: ref :: { universe[o] } universe[o]); // ---------------------------------------------------------------------- // Models function IsModelField(field: Field alpha, t: Type): bool; // Is this field a model field in class t? axiom (forall f: Field alpha :: { IsModelField(f, ANY) } IsModelField(f, ANY) <==> f == closed || f == owner || f == owns || f == observers || f == subjects ); // ---------------------------------------------------------------------- // Frames // Set of object-field pairs type Frame = [ref, Field alpha]bool; function Frame#Subset(Frame, Frame): bool; axiom(forall a: Frame, b: Frame :: { Frame#Subset(a,b) } Frame#Subset(a,b) <==> (forall o: ref, f: Field alpha :: {a[o, f]} {b[o, f]} a[o, f] ==> b[o, f])); function Frame#Singleton(ref): Frame; axiom(forall o, o': ref, f: Field alpha :: { Frame#Singleton(o)[o', f] } Frame#Singleton(o)[o', f] <==> o == o'); function has_whole_object(frame: Frame, o: ref): bool { (forall f: Field alpha :: frame[o, f]) } // Frame is closed under ownership domains and includes all unallocated objects function { :inline } closed_under_domains(frame: Frame, h: HeapType): bool { (forall o': ref, f': Field U :: {frame[o', f']} !h[o', allocated] || (exists o: ref, f: Field V :: frame[o, f] && in_domain(h, o, o') && o != o') ==> frame[o', f']) } // Objects outside of ownership domains of frame did not change, unless they were newly allocated function {: inline } same_outside(h: HeapType, h': HeapType, frame: Frame): bool { (forall o: ref, f: Field T :: { h'[o, f] } o != Void && h[o, allocated] ==> h'[o, f] == h[o, f] || frame[o, f] || (exists o': ref :: {frame[o', closed]} o' != o && frame[o', closed] && in_domain(h, o', o)) // Using extra knowledge here to remove an existential: modifying object's domain requires its closed to be in the frame ) } // Objects outside of frame did not change, unless they were newly allocated function {: inline } flat_same_outside(h: HeapType, h': HeapType, frame: Frame): bool { (forall o: ref, f: Field T :: { h'[o, f] } o != Void && h[o, allocated] ==> h'[o, f] == h[o, f] || frame[o, f]) } // Objects inside the frame did not change function same_inside(h: HeapType, h': HeapType, frame: Frame): bool { (forall o: ref, f: Field T :: o != Void && h[o, allocated] && h'[o, allocated] && frame [o, f] ==> h'[o, f] == h[o, f]) } // This version corresponds to the old semantics of read clauses: // // Objects inside the old ownership domains of frame did not change // // function same_inside(h: HeapType, h': HeapType, frame: Frame): bool { // // (forall o: ref, f: Field T :: { h[o, f] } { h'[o, f] } // // h[o, allocated] && h'[o, f] != h[o, f] ==> // // !frame [o, f] && (forall o': ref, g: Field U :: frame[o', g] && o != o' ==> !in_domain(h, o', o))) // // } // Set of all writable objects const writable: Frame; // Set of all readable objects const readable: Frame; // ---------------------------------------------------------------------- // Invariants // Is invariant of object o satisifed? function user_inv(h: HeapType, o: ref): bool; // Read frame of user_inv function user_inv_readable(HeapType, ref): Frame; axiom (forall h: HeapType, x: ref, o: ref, f: Field T :: { user_inv_readable(h, x)[o, f] } IsHeap(h) ==> user_inv_readable(h, x)[o, f] == (in_trans_owns(h, x, o) || h[x, subjects][o])); // Uninterpreted function to trigger the invariant frame axiom function inv_frame_trigger(x: ref): bool; // Frame axiom axiom (forall h, h': HeapType, x: ref :: { user_inv(h, x), user_inv(h', x), HeapSucc(h, h'), inv_frame_trigger(x) } IsHeap(h) && IsHeap(h') && HeapSucc(h, h') && inv_frame_trigger(x) && x != Void && h[x, allocated] && h'[x, allocated] && is_open(h, x) && is_open(h', x) && user_inv(h, x) && (forall o: ref, f: Field T :: h[o, allocated] ==> // every object's field h'[o, f] == h[o, f] || // is unchanged f == closed || f == owner || // or is outside of the read set of the invariant !user_inv_readable(h, x)[o, f] // (!in_trans_owns(h, x, o) && guard(h, o, f, h'[o, f], x)) // or changed in a way that conforms to its guard ) ==> user_inv(h', x)); // Is object o closed or the invariant satisfied? function {:inline true} inv(h: HeapType, o: ref): bool { h[o, closed] ==> user_inv(h, o) } // Global heap invariants function {:inline true} global(h: HeapType): bool { h[Void, allocated] && is_open(h, Void) && // (forall o: ref :: h[o, allocated] && is_open(h, o) ==> is_free(h, o)) && // (forall o: ref :: { h[o, owner], is_open(h, o) } h[o, allocated] && is_open(h, o) ==> is_free(h, o)) && (forall o: ref, o': ref :: { h[o, owns][o'] } h[o, allocated] && h[o', allocated] && h[o, closed] && h[o, owns][o'] ==> (h[o', closed] && h[o', owner] == o)) && // G2 (forall o: ref :: { user_inv(h, o) } h[o, allocated] ==> inv(h, o)) // G1 } // All objects in valid heaps are valid. // This function introduces invariants automatically, so should be used with care. function {: inline } global_permissive(): bool { (forall h: HeapType, o: ref :: {is_wrapped (h, o)}{is_closed (h, o)} IsHeap(h) && h[o, allocated] ==> inv(h, o)) } // Condition under which an update heap[current, f] := v is guaranteed to preserve the invariant of o. function guard(heap: HeapType, current: ref, f: Field T, v: T, o: ref): bool; // All subjects know current for an observer function {: inline } admissibility2 (heap: HeapType, current: ref): bool { (forall s: ref :: heap[current, subjects][s] && s != Void ==> heap[s, observers][current]) } // Invariant cannot be invalidated by an update that conform to its guard function {: inline } admissibility3 (heap: HeapType, current: ref): bool { (forall s: ref, f: Field T, v: T :: heap[current, subjects][s] && s != current && s != Void && IsHeap(heap[s, f := v]) && guard(heap, s, f, v, current) ==> user_inv(heap[s, f := v], current)) } // ---------------------------------------------------------------------- // Built-in operations // Allocate fresh object procedure allocate(t: Type) returns (result: ref); modifies Heap; ensures global(Heap); ensures !old(Heap[result, allocated]); // AL1 ensures Heap[result, allocated]; // AL2 ensures result != Void; ensures type_of(result) == t; ensures (forall f: Field T :: f != allocated ==> Heap[result, f] == Default(f)); ensures has_whole_object(writable, result); ensures (forall o: ref, f: Field T :: o != result ==> Heap[o, f] == old(Heap[o, f])); free ensures HeapSucc(old(Heap), Heap); // Update Heap position Current.field with value. procedure update_heap(Current: ref, field: Field T, value: T); requires (Current != Void) && (Heap[Current, allocated]); // type:assign tag:attached_and_allocated requires field != closed && field != owner; // type:assign tag:closed_or_owner_not_allowed UP4 requires is_open(Heap, Current); // type:assign tag:target_open UP1 requires (forall o: ref :: Heap[Current, observers][o] ==> (is_open(Heap, o) || (IsHeap(Heap[Current, field := value]) ==> guard(Heap, Current, field, value, o)))); // type:assign tag:observers_open_or_guard_holds UP2 requires writable[Current, field]; // type:assign tag:attribute_writable UP3 modifies Heap; ensures global(Heap); ensures Heap == old(Heap[Current, field := value]); free ensures HeapSucc(old(Heap), Heap); // Unwrap o procedure unwrap(o: ref); requires (o != Void) && (Heap[o, allocated]); // type:pre tag:attached requires is_wrapped(Heap, o); // type:pre tag:wrapped UW1 requires writable[o, closed]; // type:pre tag:writable UW2 modifies Heap; ensures global(Heap); ensures is_open(Heap, o); // UWE1 ensures (forall o': ref :: old(Heap[o, owns][o']) ==> is_wrapped(Heap, o')); // UWE2 ensures (forall o': ref, f: Field T :: !(o' == o && f == closed) && !(old(Heap[o, owns][o']) && f == owner) ==> Heap[o', f] == old(Heap[o', f])); free ensures HeapSucc(old(Heap), Heap); procedure unwrap_all (Current: ref, s: Set ref); requires (forall o: ref :: s[o] ==> (o != Void) && (Heap[o, allocated])); // type:pre tag:attached requires (forall o: ref :: s[o] ==> is_wrapped(Heap, o)); // type:pre tag:wrapped UW1 requires (forall o: ref :: s[o] ==> writable[o, closed]); // type:pre tag:writable UW2 modifies Heap; ensures global(Heap); ensures (forall o: ref :: s[o] ==> is_open(Heap, o)); // UWE1 ensures (forall o: ref :: s[o] ==> (forall o': ref :: old(Heap[o, owns][o']) ==> is_wrapped(Heap, o'))); // UWE2 ensures (forall o: ref, f: Field T :: !(f == closed && s[o]) && !(f == owner && (exists o': ref :: s[o'] && old(Heap[o', owns][o]))) ==> Heap[o, f] == old(Heap[o, f])); ensures (forall o: ref :: s[o] ==> user_inv(Heap, o) && inv_frame_trigger(o)); free ensures HeapSucc(old(Heap), Heap); // Wrap o procedure wrap(o: ref); requires (o != Void) && (Heap[o, allocated]); // type:pre tag:attached requires is_open(Heap, o); // type:pre tag:open W1 requires writable[o, closed]; // type:pre tag:writable W4 // requires user_inv(Heap, o); // type:pre tag:invariant_holds W2 // Custom check now requires (forall o': ref :: { Heap[o, owns][o'] } Heap[o, owns][o'] ==> is_wrapped(Heap, o') && writable[o', owner]); // type:pre tag:owned_objects_wrapped_and_writable W3 modifies Heap; ensures global(Heap); ensures (forall o': ref :: old(Heap[o, owns][o']) ==> Heap[o', owner] == o); // WE2 ensures is_wrapped(Heap, o); // WE3 ensures (forall o': ref, f: Field T :: !(o' == o && f == closed) && !(old(Heap[o, owns][o']) && f == owner) ==> Heap[o', f] == old(Heap[o', f])); free ensures HeapSucc(old(Heap), Heap); procedure wrap_all(Current: ref, s: Set ref); requires (forall o: ref :: s[o] ==> (o != Void) && (Heap[o, allocated])); // type:pre tag:attached requires (forall o: ref :: s[o] ==> is_open(Heap, o)); // type:pre tag:open W1 requires (forall o: ref :: s[o] ==> writable[o, closed]); // type:pre tag:writable W4 requires (forall o: ref :: s[o] ==> user_inv(Heap, o)); // type:pre tag:invariant_holds W2 requires (forall o: ref :: s[o] ==> (forall o': ref :: Heap[o, owns][o'] ==> is_wrapped(Heap, o') && writable[o', owner])); // type:pre tag:owned_objects_wrapped_and_writable W3 modifies Heap; ensures global(Heap); ensures (forall o: ref :: s[o] ==> (forall o': ref :: old(Heap[o, owns][o']) ==> Heap[o', owner] == o)); // WE2 ensures (forall o: ref :: s[o] ==> is_wrapped(Heap, o)); // WE3 ensures (forall o: ref, f: Field T :: !(f == closed && s[o]) && !(f == owner && (exists o': ref :: s[o'] && old(Heap[o', owns][o]))) ==> Heap[o, f] == old(Heap[o, f])); free ensures HeapSucc(old(Heap), Heap); // ---------------------------------------------------------------------- // Attached/Detachable functions // Reference `o' is attached to an object of type `t' on heap `heap'. function attached_exact(heap: HeapType, o: ref, t: Type) returns (bool) { (o != Void) && (heap[o, allocated]) && (type_of(o) == t) } // Reference `o' is either Void or attached to an object of type `t' on heap `heap'. function detachable_exact(heap: HeapType, o: ref, t: Type) returns (bool) { (o == Void) || attached_exact(heap, o, t) } // Reference `o' is attached and conforms to type `t' on heap `heap'. function attached(heap: HeapType, o: ref, t: Type) returns (bool) { (o != Void) && (heap[o, allocated]) && (type_of(o) <: t) } // Reference `o' is either Void or attached and conforms to `t' on heap `heap'. function detachable(heap: HeapType, o: ref, t: Type) returns (bool) { (o == Void) || attached(heap, o, t) } // ---------------------------------------------------------------------- // Basic types // Integer boxing const unique INTEGER: Type; function boxed_int(i: int) returns (ref); function unboxed_int(r: ref) returns (int); axiom (forall i: int :: unboxed_int(boxed_int(i)) == i); axiom (forall i1, i2: int :: (i1 == i2) ==> (boxed_int(i1) == boxed_int(i2))); axiom (forall i: int :: boxed_int(i) != Void && type_of(boxed_int(i)) == INTEGER); axiom (forall heap: HeapType, i: int :: IsHeap(heap) ==> heap[boxed_int(i), allocated]); // Boolean boxing const unique BOOLEAN: Type; const unique boxed_true: ref; const unique boxed_false: ref; function boxed_bool(b: bool) returns (ref); function unboxed_bool(r: ref) returns (bool); axiom (boxed_bool(true) == boxed_true); axiom (boxed_bool(false) == boxed_false); axiom (unboxed_bool(boxed_true) == true); axiom (unboxed_bool(boxed_false) == false); axiom (boxed_true != boxed_false); axiom (boxed_true != Void && type_of(boxed_true) == BOOLEAN); axiom (boxed_false != Void && type_of(boxed_false) == BOOLEAN); axiom (forall heap: HeapType :: IsHeap(heap) ==> heap[boxed_true, allocated]); axiom (forall heap: HeapType :: IsHeap(heap) ==> heap[boxed_false, allocated]); // Bounded integers function is_integer_8(i: int) returns (bool) { (-128 <= i) && (i <= 127) } function is_integer_16(i: int) returns (bool) { (-32768 <= i) && (i <= 32767) } function is_integer_32(i: int) returns (bool) { (-2147483648 <= i) && (i <= 2147483647) } function is_integer_64(i: int) returns (bool) { (-9223372036854775808 <= i) && (i <= 9223372036854775807) } function is_natural(i: int) returns (bool) { (0 <= i) } function is_natural_8(i: int) returns (bool) { (0 <= i) && (i <= 255) } function is_natural_16(i: int) returns (bool) { (0 <= i) && (i <= 65535) } function is_natural_32(i: int) returns (bool) { (0 <= i) && (i <= 4294967295) } function is_natural_64(i: int) returns (bool) { (0 <= i) && (i <= 18446744073709551615) } // Numeric conversions function int_to_integer_8(i: int) returns (int); axiom (forall i: int :: { int_to_integer_8(i) } is_integer_8(i) ==> int_to_integer_8(i) == i); axiom (forall i: int :: { int_to_integer_8(i) } is_integer_8(int_to_integer_8(i))); function int_to_integer_16(i: int) returns (int); axiom (forall i: int :: { int_to_integer_16(i) } is_integer_16(i) ==> int_to_integer_16(i) == i); axiom (forall i: int :: { int_to_integer_16(i) } is_integer_16(int_to_integer_16(i))); function int_to_integer_32(i: int) returns (int); axiom (forall i: int :: { int_to_integer_32(i) } is_integer_32(i) ==> int_to_integer_32(i) == i); axiom (forall i: int :: { int_to_integer_32(i) } is_integer_32(int_to_integer_32(i))); function int_to_integer_64(i: int) returns (int); axiom (forall i: int :: { int_to_integer_64(i) } is_integer_64(i) ==> int_to_integer_64(i) == i); axiom (forall i: int :: { int_to_integer_64(i) } is_integer_64(int_to_integer_64(i))); function int_to_natural_8(i: int) returns (int); axiom (forall i: int :: { int_to_natural_8(i) } is_natural_8(i) ==> int_to_natural_8(i) == i); axiom (forall i: int :: { int_to_natural_8(i) } is_natural_8(int_to_natural_8(i))); function int_to_natural_16(i: int) returns (int); axiom (forall i: int :: { int_to_natural_16(i) } is_natural_16(i) ==> int_to_natural_16(i) == i); axiom (forall i: int :: { int_to_natural_16(i) } is_natural_16(int_to_natural_16(i))); function int_to_natural_32(i: int) returns (int); axiom (forall i: int :: { int_to_natural_32(i) } is_natural_32(i) ==> int_to_natural_32(i) == i); axiom (forall i: int :: { int_to_natural_32(i) } is_natural_32(int_to_natural_32(i))); function int_to_natural_64(i: int) returns (int); axiom (forall i: int :: { int_to_natural_64(i) } is_natural_64(i) ==> int_to_natural_64(i) == i); axiom (forall i: int :: { int_to_natural_64(i) } is_natural_64(int_to_natural_64(i))); function real_to_integer_32(r: real) returns (int); axiom (forall r: real :: { real_to_integer_32(r) } is_integer_32(int(r)) ==> real_to_integer_32(r) == int(r)); axiom (forall r: real :: { real_to_integer_32(r) } (!is_integer_32(int(r)) && r < 0.0) ==> real_to_integer_32(r) == -2147483648); axiom (forall r: real :: { real_to_integer_32(r) } (!is_integer_32(int(r)) && r > 0.0) ==> real_to_integer_32(r) == 2147483647); function real_to_integer_64(r: real) returns (int); axiom (forall r: real :: { real_to_integer_64(r) } is_integer_64(int(r)) ==> real_to_integer_64(r) == int(r)); axiom (forall r: real :: { real_to_integer_64(r) } (!is_integer_64(int(r)) && r < 0.0) ==> real_to_integer_64(r) == -9223372036854775808); axiom (forall r: real :: { real_to_integer_64(r) } (!is_integer_64(int(r)) && r > 0.0) ==> real_to_integer_64(r) == 9223372036854775807); // Arithmetic functions function add(a, b: int): int { a + b } function subtract(a, b: int): int { a - b } function multiply(a, b: int): int { a * b } function modulo(a, b: int): int { a mod b } function divide(a, b: int): int { a div b } function min(a, b: int): int { if a <= b then a else b } function max(a, b: int): int { if a >= b then a else b } function abs(a: int): int { if a >= 0 then a else -a } function sign(a: int): int { if a == 0 then 0 else if a > 0 then 1 else -1 } function min_real(a, b: real): real { if a <= b then a else b } function max_real(a, b: real): real { if a >= b then a else b } function abs_real(a: real): real { if a >= 0.0 then a else -a } function sign_real(a: real): int { if a == 0.0 then 0 else if a > 0.0 then 1 else -1 } function bit_and(a, b: int): int; function bit_or(a, b: int): int; function bit_xor(a, b: int): int; function bit_not(a: int): int; function bit_shift_left(a, n: int): int; function bit_shift_right(a, n: int): int; // ---------------------------------------------------------------------- // once procedures function global_once_value(rid: int): T; function object_once_value(o: ref, rid: int): T; // Expanded types // type unknown; // Address operator // function address(a: ref) returns (int); // axiom (forall a, b: ref :: (a != b) <==> (address(a) != address(b))); // Different objects have different heap addresses. // axiom (forall a: ref :: is_integer_64(address(a))); // Addresses are 64 bit integers. // Unsupported function unsupported() returns (T);