Recalling a Witness

type array (a:Type) (n:N) = mref (repr a n) remains_init where repr a n = s:seq (option a){len s == n} and remains_init #a #n (s0:repr a n) (s1:repr a n) = ∀(i:N{i < n}). Some? s0. (i) =⇒ Some? s1. (i) Notice the interplay between refinements types and monotonic references. The refinement type s:seq (option a){len s == n} passed to mref constrains the stored sequence to be of the appropriate length in every state. Though concise and powerful, refinements of this form can only enforce invariants of each reachable program state taken in isolation. In order to constrain how the array contents can evolve, the preorder remains_init states that the sequence underlying an array can evolve from s0 to s1 only if every initialized index in s0 remains initialized in s1. Given this representation of array, the rest of the code is mostly determined. For instance, its create function takes a length n but no initial value for the array contents. abstract let create (a:Type) (n:N) : ST (array a n) (ensures (λ h0 x h1 → fresh x h0 h1 ∧ modifies { } h0 h1)) = alloc (Seq.create n None) remains_initlet create (a:Type) (n:N) : ST (array a n) (ensures (λ h0 x h1 → fresh x h0 h1 ∧ modifies { } h0 h1)) = alloc (Seq.create n None) remains_init The pure function as_seq h x enables reasoning about an array x in state h as a sequence of optional values. Below we use it to define which parts of an array are initialized, i.e., those indices at which the array contains Some value. let index #a #n (x:array a n) = i:N{i < n} let initialized #a #n (x:array a n) (i:index x) (h:heap) = Some? (as_seq h x). (i) The main use of monotonicity in our library is to observe that initialized is stable with respect to remains_init, the preorder associated with an array. As such, we can define a state-independent proposition (x `init_at` i) using the logical witnessed capabilities. Proceedings of the ACM on Programming Languages, Vol. 2, No. POPL, Article 65. Publication date: January 2018. 65:12 D. Ahman, C. Fournet, C. Hriţcu, K. Maillard, A. Rastogi, and N. Swamy let init_at #a #n (x:array a n) (i:index x) = witnessed (initialized x i) We can now prove that writing to an array at index i ensures that it becomes initialized at i, which is a necessary precondition to read from i. Notice the use of witness when writing, to record the fact that the index is initialized and will remain so; and the use of recall when reading, to recover that the array is initialized at i and so the underlying sequence contains Some v at i. abstract let write (#a:Type) (#n:N) (x:array a n) (i:index x) (v:a) : ST unit (ensures (λ h0 _ h1 →modifies {x} h0 h1 ∧ (as_seq h1 x). (i) == Some v ∧ x `init_at` i)) = x := Seq.upd ! x i (Some v); witness (initialized x i)let write (#a:Type) (#n:N) (x:array a n) (i:index x) (v:a) : ST unit (ensures (λ h0 _ h1 →modifies {x} h0 h1 ∧ (as_seq h1 x). (i) == Some v ∧ x `init_at` i)) = x := Seq.upd ! x i (Some v); witness (initialized x i) abstract let read (#a:Type) (#n:N) (x:array a n) (i:index x{x `init_at` i}) : ST a (ensures (λ h0 r h1 →modifies { } h0 h1 ∧ Some r == (as_seq h0 x). (i))) = recall (initialized x i); match ! r. (i) with Some v→ vlet read (#a:Type) (#n:N) (x:array a n) (i:index x{x `init_at` i}) : ST a (ensures (λ h0 r h1 →modifies { } h0 h1 ∧ Some r == (as_seq h0 x). (i))) = recall (initialized x i); match ! r. (i) with Some v→ v It is worth noting that in the absence of monotonicity, the init_at predicate defined above would need to be state-dependent, and thus carried through the subsequent stateful functions as a stateful invariant, causing unnecessary additional proof obligations and bloated specifications.