pub enum JournalEntry {
Show 19 variants
RegOverwrite {
slot: usize,
old: Value,
},
GlobalOverwrite {
idx: usize,
old: Option<Value>,
},
MultiRegOverwrite {
slots: Vec<(usize, Value)>,
},
ShowLine,
NondetRead {
dst_slot: usize,
dst_old: Value,
drawn: Value,
},
Branch {
taken: bool,
},
CollectionMutate {
slot: usize,
old: Value,
},
ListPop {
list_slot: usize,
list_old: Value,
dst_slot: usize,
dst_old: Value,
},
IterNextUndo {
advanced: bool,
dst_slot: usize,
dst_old: Value,
},
IterPopUndo {
items: Vec<Value>,
idx: usize,
},
CrdtInterior {
slot: usize,
snapshot: Value,
},
CrdtFieldInterior {
obj_slot: usize,
field: String,
snapshot: Value,
},
DivReconstruct {
dst_slot: usize,
divisor: Value,
remainder: Value,
},
ShiftReconstruct {
dst_slot: usize,
dropped: i64,
k: u32,
left: bool,
},
MaskReconstruct {
dst_slot: usize,
saved: i64,
mask: i64,
and: bool,
},
ModReconstruct {
dst_slot: usize,
quotient: i64,
divisor: Value,
},
CallUndo {
restore_len: usize,
},
CallValueUndo {
restore_len: usize,
captures: Vec<(usize, Value)>,
},
ReturnUndo {
frame_from: usize,
frame_vals: Vec<Value>,
iter_from: usize,
iter_vals: Vec<(Vec<Value>, usize)>,
rv_slot: usize,
rv_old: Value,
arg_lo: usize,
arg_old: Vec<Value>,
},
}Expand description
One recorded delta on the HistoryTape. Records only what a forward op DESTROYED, so reverse
execution can restore it. Zero-cost/control entries (a shown line, a branch direction) carry no
erased data — they are re-derivable — and so contribute nothing to the erased-bit measure.
Variants§
RegOverwrite
A register slot (base + r) was overwritten; restore old on reverse.
GlobalOverwrite
A promoted global was overwritten (or first-defined, old == None); restore it on reverse.
MultiRegOverwrite
Several register slots were overwritten at once (a tuple destructure). Reverse restores each.
ShowLine
A Show appended one output line; reverse truncates the output buffer. The content is
re-derivable on a re-forward, so no data is erased.
NondetRead
A NONDETERMINISTIC read (clock / random) drew drawn into a register. Reading does not mutate
the world, so reverse simply restores the register’s prior value (dst_old). Critically,
drawn is RECORDED — the entropy is captured on the tape (a deterministic replay could feed it
back), rather than silently discarded, which is what makes reversing a nondeterministic program
honest rather than a lie.
Branch
A conditional branch was taken (true) or fell through (false); reverse follows it back.
One bit of control flow, not erased data.
CollectionMutate
An in-place collection mutation (push / index-set / add / remove). old is the register’s
pre-mutation value — an Rc bump for a shared List/Map/Set, or a deep copy for a by-value
container. Because that bump forces the op’s copy-on-write, the original stays pristine, so
reverse restores old exactly (bit-identical, including aliasing). If instead the register
still holds the SAME Rc (an in-place mutation — only when value-semantics is off), reverse
declines rather than mis-restore.
ListPop
A ListPop: it both shrinks a list AND overwrites a destination register. Reverse restores
the pristine pre-pop list (list_old, which — COW-forced — still holds the popped element)
and the destination’s prior value (dst_old), so no push-back or empty-flag is needed.
IterNextUndo
An IterNext. advanced = it yielded an element (idx < len): reverse restores the
destination’s prior value and rewinds the top iterator’s index by one. !advanced = it was
exhausted (a pure jump to the loop exit): reverse does nothing.
IterPopUndo
An IterPop dropped the top iterator (a snapshot + cursor). Reverse re-pushes it so the loop
body’s IterNexts can be rewound.
CrdtInterior
A CRDT interior mutation (CrdtAppend/CrdtResolve on an Rc<RefCell<CrdtValue>>). Unlike a
by-value container, the mutation happens IN PLACE through the shared handle, so an Rc-bump
snapshot would share the mutated interior, not preserve it. Instead snapshot is a DEEP clone
of the pre-mutation CrdtValue (wrapped in a fresh handle); reverse copies it back THROUGH the
register’s live handle (*rc.borrow_mut() = …), so every alias observes the restore.
CrdtFieldInterior
A CrdtResolve onto a nested CRDT-register FIELD (registers[obj_slot]’s field) — the
field’s Rc<RefCell<CrdtValue>> interior is resolved in place. Like
JournalEntry::CrdtInterior but the handle lives inside a struct field, so snapshot (a
deep pre-resolve clone) is copied back through that field’s live handle. (A resolve onto a
plain field is a CollectionMutate instead.)
DivReconstruct
An IN-PLACE integer division a = a / b — the quotient is kept in dst_slot, the REMAINDER is
the only bit truly discarded. Reverse reconstructs a_old = q·b + r from the kept quotient and
the divisor, so the Landauer cost is the remainder’s few bits (< log₂ b), not the whole
dividend word. The divisor is a recomputable input (stored here for a self-contained reverse,
but it is not erased information, so it costs nothing).
ShiftReconstruct
An IN-PLACE bit shift (a = a << k / a = a >> k; Shl/Shr/DivPow2 with dst == lhs).
Only the shifted-OUT bits are destroyed — the rest of a_old survives in the kept result — so
the tape records just those k bits (dropped: the low k bits for a right shift, the top k
bits right-aligned for a left shift). Reverse recombines them with the kept result, so the
Landauer cost is k bits, not the whole 64-bit word.
MaskReconstruct
An IN-PLACE bitwise mask (a = a & b / a = a | b; BitAnd/BitOr with dst == lhs). The
bits the live mask b cannot recover are saved: for AND the bits where b = 0, for OR the
bits where b = 1. Reverse re-merges them under the complementary mask. The Landauer cost is
exactly the count of unrecoverable positions (64 − popcount b for AND, popcount b for OR).
ModReconstruct
An IN-PLACE integer modulo (a = a % b; Mod with dst == lhs). The remainder is kept in the
destination; the QUOTIENT is the discarded information. Reverse rebuilds a_old = q·b + r from
the saved quotient and the live divisor. The Landauer cost is the quotient’s magnitude bits.
CallUndo
A Call grew the register file (a callee window). Reverse truncates it back to restore_len
after the callee’s body has been reversed.
CallValueUndo
A CallValue (closure invocation): like Call it grew the register file, and it also bound
captures into the callee frame. captures are the pre-binding (slot, old_value) pairs (an
overlap capture slot survives the truncate, so its prior caller value must be restored).
Reverse restores the capture slots, then truncates back to restore_len.
ReturnUndo
A Return/ReturnNothing tore down the callee frame. Reverse rebuilds it: regrow the
registers to frame_from + frame_vals.len() restoring the callee’s saved window, restore the
iterator stack, and undo the return-value write (rv_slot←rv_old) and the argument-window
clear (arg_lo..←arg_old) — leaving the machine exactly in the callee’s pre-return state so
the callee body can be walked backward.
Trait Implementations§
Auto Trait Implementations§
impl Freeze for JournalEntry
impl !RefUnwindSafe for JournalEntry
impl !Send for JournalEntry
impl !Sync for JournalEntry
impl Unpin for JournalEntry
impl UnsafeUnpin for JournalEntry
impl !UnwindSafe for JournalEntry
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.