Refactor GlobalStore to use separate RTL and user value stores
The `GlobalStore` was refactored to clearly distinguish between immutable RTL values and mutable user-defined global slots. The `Environment` struct now holds: - `rtl_values`: An immutable `Rc<[Value]>` for pre-defined RTL values. - `user_values`: An `Rc<RefCell<Vec<Value>>>` for user-defined mutable globals. The `GlobalStore` struct now takes both `rtl` and `user` as parameters and uses `rtl_len` to determine which store to access. This change separates concerns and better reflects the immutability of RTL values after the bootstrap phase, aligning with the project's concurrency rules. Documentation in `docs/Analysis_Environment.md` was updated to reflect these structural changes.
This commit is contained in:
+26
-14
@@ -84,35 +84,47 @@ impl VMObserver for TracingObserver {
|
||||
|
||||
/// Unified view of the global value space for VM and optimizer access.
|
||||
///
|
||||
/// Wraps a flat `Vec<Value>` containing both RTL slots `[0..rtl_len)` and user
|
||||
/// slots `[rtl_len..)`. The `rtl_len` boundary is informational — it enables
|
||||
/// a debug-mode write guard and prepares for future RTL sharing.
|
||||
/// Splits the global address space into two regions:
|
||||
/// - RTL slots `[0..rtl_len)`: immutable `Rc<[Value]>`, shared across all environments
|
||||
/// created from the same [`Rtl`](crate::ast::environment::Rtl) snapshot. No `RefCell` needed.
|
||||
/// - User slots `[rtl_len..)`: mutable `Rc<RefCell<Vec<Value>>>`, per-environment.
|
||||
///
|
||||
/// True RTL/user splitting (sharing the immutable RTL portion across environments)
|
||||
/// is a planned future optimization once the stream-bootstrap timing is resolved.
|
||||
/// During the RTL bootstrap phase `rtl_len` is 0 and both RTL and user values live in the
|
||||
/// `user` vec. After the freeze, `rtl` holds the immutable snapshot and `user` starts
|
||||
/// empty and grows as the script defines new globals.
|
||||
///
|
||||
/// Stream closures capture a `GlobalStore` during bootstrap (before the freeze), so their
|
||||
/// `rtl_len` remains 0 and they read all values from `user`. This enforces the invariant
|
||||
/// that stream lambdas run in an RTL-only execution context.
|
||||
#[derive(Clone)]
|
||||
pub struct GlobalStore {
|
||||
values: Rc<RefCell<Vec<Value>>>,
|
||||
/// Index boundary between frozen RTL slots and mutable user slots.
|
||||
rtl: Rc<[Value]>,
|
||||
user: Rc<RefCell<Vec<Value>>>,
|
||||
/// Index boundary: slots `[0..rtl_len)` are served from `rtl`, the rest from `user`.
|
||||
pub rtl_len: usize,
|
||||
}
|
||||
|
||||
impl GlobalStore {
|
||||
pub fn new(values: Rc<RefCell<Vec<Value>>>, rtl_len: usize) -> Self {
|
||||
Self { values, rtl_len }
|
||||
pub fn new(rtl: Rc<[Value]>, user: Rc<RefCell<Vec<Value>>>, rtl_len: usize) -> Self {
|
||||
Self { rtl, user, rtl_len }
|
||||
}
|
||||
|
||||
pub fn get(&self, idx: usize) -> Option<Value> {
|
||||
self.values.borrow().get(idx).cloned()
|
||||
if idx < self.rtl_len {
|
||||
self.rtl.get(idx).cloned()
|
||||
} else {
|
||||
self.user.borrow().get(idx - self.rtl_len).cloned()
|
||||
}
|
||||
}
|
||||
|
||||
fn set(&self, idx: usize, value: Value) {
|
||||
debug_assert!(idx >= self.rtl_len, "Cannot write to immutable RTL slot {}", idx);
|
||||
let mut v = self.values.borrow_mut();
|
||||
if idx >= v.len() {
|
||||
v.resize(idx + 1, Value::Void);
|
||||
let u = idx - self.rtl_len;
|
||||
let mut v = self.user.borrow_mut();
|
||||
if u >= v.len() {
|
||||
v.resize(u + 1, Value::Void);
|
||||
}
|
||||
v[idx] = value;
|
||||
v[u] = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user