Skip to main content

ostd/specs/mm/embedding/
trace.rs

1//! Lazy trace generation: [`run`] generates a trace on the fly by
2//! repeatedly stepping with arbitrary-but-valid Ops.
3//!
4//! At each iteration `run` uses `choose` to pick *some* `Op` that
5//! satisfies [`super::op_pre`] against the current store. Because
6//! [`super::Op::NewVmSpace`] has no precondition, such an op always
7//! exists, so `choose` is well-defined. The picked op is then
8//! discharged into [`super::step`], and the recursion continues with
9//! the post-state.
10//!
11//! Compared to the previous `run_trace(trace: Seq<Op>)` design, this
12//! avoids needing a `valid_trace` predicate / `step_post` spec
13//! function: validity is established at the moment of choice rather
14//! than as a precondition over a pre-built trace.
15use vstd::prelude::*;
16use vstd_extra::ownership::*;
17
18use super::{Op, VmStore, op_pre, step};
19
20verus! {
21
22/// `op_pre` is always satisfiable: [`Op::NewVmSpace`] has no
23/// preconditions, so it's a witness for any state.
24pub proof fn op_pre_satisfiable<'rcu>(s: VmStore<'rcu>)
25    ensures
26        exists|op: Op| op_pre(s, op),
27{
28    assert(op_pre(s, Op::NewVmSpace));
29}
30
31/// Generates a trace of `n` steps by repeatedly choosing some op that
32/// satisfies [`super::op_pre`] against the current store and applying
33/// it via [`super::step`]. Preserves [`VmStore::inv`] throughout.
34///
35/// This is the soundness story over arbitrary call sequences: no
36/// matter which `n` operations the system picks (subject only to
37/// op-pre validity at each step), invariants hold.
38pub proof fn run<'rcu>(tracked s: &mut VmStore<'rcu>, n: nat)
39    requires
40        old(s).inv(),
41    ensures
42        final(s).inv(),
43    decreases n,
44{
45    if n > 0 {
46        op_pre_satisfiable(*s);
47        let op: Op = choose|op: Op| op_pre(*s, op);
48        step(s, op);
49        run(s, (n - 1) as nat);
50    }
51}
52
53} // verus!