Skip to main content

sema_core/
mutable_ops.rs

1//! Shared implementations of the `mutable-array` accessors.
2//!
3//! Both the stdlib natives (`sema-stdlib/src/mutable.rs`) and the VM's
4//! `MutArrGet` / `MutArrSet` intrinsic opcodes (`sema-vm/src/vm.rs`) dispatch
5//! into these, so the two paths raise byte-identical errors and can never
6//! drift apart semantically.
7
8use crate::{SemaError, Value};
9
10fn not_an_array(v: &Value, name: &str) -> SemaError {
11    SemaError::type_error("mutable-array", v.type_name())
12        .with_hint(format!("{name}: create one with (mutable-array/new)"))
13}
14
15/// `mutable-array/get`: indexed read. `default` is the optional third
16/// argument — returned on an out-of-bounds index instead of erroring.
17/// Type and index errors are raised regardless of the default.
18pub fn mutable_array_get(
19    arr: &Value,
20    idx: &Value,
21    default: Option<&Value>,
22) -> Result<Value, SemaError> {
23    let a = arr
24        .as_mutable_array()
25        .ok_or_else(|| not_an_array(arr, "mutable-array/get"))?;
26    let idx = idx.as_index("mutable-array/get")?;
27    let items = a.items.borrow();
28    match items.get(idx) {
29        Some(v) => Ok(v.clone()),
30        None => match default {
31            Some(d) => Ok(d.clone()),
32            None => Err(SemaError::eval(format!(
33                "mutable-array/get: index {idx} out of bounds (length {})",
34                items.len()
35            ))),
36        },
37    }
38}
39
40/// `mutable-array/set!`: indexed write into an existing slot. Takes `val` by
41/// move so a caller that owns the value (the VM's `MutArrSet` arm) pays no
42/// clone. The Sema-level contract returns the array itself — the caller hands
43/// back its own array handle after `Ok(())`.
44pub fn mutable_array_set(arr: &Value, idx: &Value, val: Value) -> Result<(), SemaError> {
45    let a = arr
46        .as_mutable_array()
47        .ok_or_else(|| not_an_array(arr, "mutable-array/set!"))?;
48    let idx = idx.as_index("mutable-array/set!")?;
49    let mut items = a.items.borrow_mut();
50    let len = items.len();
51    match items.get_mut(idx) {
52        Some(slot) => {
53            *slot = val;
54            Ok(())
55        }
56        None => Err(SemaError::eval(format!(
57            "mutable-array/set!: index {idx} out of bounds (length {len})"
58        ))
59        .with_hint("set! writes to an existing slot; use mutable-array/push! to grow")),
60    }
61}