fix force

This commit is contained in:
2026-04-04 00:24:05 +08:00
parent e78e62795b
commit 95eea517e4
2 changed files with 142 additions and 160 deletions
+6
View File
@@ -478,6 +478,12 @@ impl<'gc> AttrSet<'gc> {
pub(crate) struct List<'gc> {
pub(crate) inner: SmallVec<[Value<'gc>; 4]>,
}
impl<'gc> Deref for List<'gc> {
type Target = SmallVec<[Value<'gc>; 4]>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
pub(crate) type Thunk<'gc> = RefLock<ThunkState<'gc>>;
+136 -160
View File
@@ -95,7 +95,11 @@ impl<'gc> GcRoot<'gc> {
#[inline(always)]
pub(super) fn pop_stack_forced(&mut self) -> StrictValue<'gc> {
self.stack.pop().expect("stack underflow").restrict().expect("forced")
self.stack
.pop()
.expect("stack underflow")
.restrict()
.expect("forced")
}
}
@@ -196,7 +200,7 @@ enum SelectKeyData {
}
macro_rules! try_vm {
($self:ident, $expr:expr) => {
($self:ident; $expr:expr) => {
match $expr {
Ok(v) => v,
Err(e) => return Runtime::handle_vm_error($self, e),
@@ -529,8 +533,7 @@ impl Runtime {
&SelectKeyData::Static(sid) => sid,
SelectKeyData::Dynamic => {
self.arena.mutate_root(|_, root| {
let v =
root.temp_stack.pop().expect("missing dynamic key");
let v = root.temp_stack.pop().expect("missing dynamic key");
root.push_stack(v);
});
self.force_tos();
@@ -555,9 +558,7 @@ impl Runtime {
let result = self.arena.mutate_root(|_, root| {
let val = root.pop_stack();
let Some(attrset) = val.as_gc::<AttrSet<'_>>() else {
return Err(vm_err(
"value is not a set while a set was expected",
));
return Err(vm_err("value is not a set while a set was expected"));
};
match attrset.lookup(key_sid) {
Some(v) => {
@@ -577,8 +578,7 @@ impl Runtime {
Ok(false) => {
self.arena
.mutate_root(|_, root| root.temp_stack.truncate(temp_base));
let name =
self.strings.resolve(key_sid.0).unwrap_or("«unknown»");
let name = self.strings.resolve(key_sid.0).unwrap_or("«unknown»");
return Runtime::handle_vm_error(
self,
vm_err(format!("attribute '{name}' missing")),
@@ -626,8 +626,7 @@ impl Runtime {
&SelectKeyData::Static(sid) => sid,
SelectKeyData::Dynamic => {
self.arena.mutate_root(|_, root| {
let v =
root.temp_stack.pop().expect("missing dynamic key");
let v = root.temp_stack.pop().expect("missing dynamic key");
root.push_stack(v);
});
self.force_tos();
@@ -652,11 +651,12 @@ impl Runtime {
let found = self.arena.mutate_root(|_, root| {
let val = root.pop_stack();
if let Some(attrset) = val.as_gc::<AttrSet<'_>>()
&& let Some(v) = attrset.lookup(key_sid) {
root.push_stack(v);
return true;
}
// Not a set or key missing → use default
&& let Some(v) = attrset.lookup(key_sid)
{
root.push_stack(v);
return true;
}
// Not a set or key missing: use default
false
});
@@ -706,24 +706,118 @@ impl Runtime {
self.push_empty_list();
}
OpAdd | OpSub | OpMul | OpDiv | OpEq | OpNeq | OpLt | OpGt | OpLeq | OpGeq
| OpConcat | OpUpdate => {
let tag = match op {
OpAdd => BinOpTag::Add,
OpSub => BinOpTag::Sub,
OpMul => BinOpTag::Mul,
OpDiv => BinOpTag::Div,
OpEq => BinOpTag::Eq,
OpNeq => BinOpTag::Neq,
OpLt => BinOpTag::Lt,
OpGt => BinOpTag::Gt,
OpLeq => BinOpTag::Leq,
OpGeq => BinOpTag::Geq,
OpConcat => BinOpTag::Concat,
OpUpdate => BinOpTag::Update,
OpAdd => {
self.force_n(2);
let strings = &self.strings;
let res = self.arena.mutate_root(|mc, root| {
let rhs = root.pop_stack_forced();
let lhs = root.pop_stack_forced();
// FIXME: path & string context
if let (Some(ls), Some(rs)) = (
Self::get_string(strings, lhs),
Self::get_string(strings, rhs),
) {
let ns = Gc::new(mc, NixString::new(format!("{ls}{rs}")));
root.push_stack(Value::new_gc(ns));
return Ok(());
}
let res = Self::numeric_binop(lhs, rhs, mc, i64::wrapping_add, |a, b| a + b)?;
root.push_stack(res);
VmResult::Ok(())
});
try_vm!(self; res);
}
OpSub | OpMul => {
self.force_n(2);
#[allow(clippy::type_complexity)]
let func: (fn(i64, i64) -> i64, fn(f64, f64) -> f64) = match op {
OpSub => (i64::wrapping_sub, |a, b| a - b),
OpMul => (i64::wrapping_mul, |a, b| a * b),
_ => unreachable!(),
};
try_vm!(self, self.compute_binop(tag))
let res = self.arena.mutate_root(|mc, root| {
let rhs = root.pop_stack_forced();
let lhs = root.pop_stack_forced();
let res = Self::numeric_binop(lhs, rhs, mc, func.0, func.1)?;
root.push_stack(res);
VmResult::Ok(())
});
try_vm!(self; res);
}
OpDiv => {
self.force_n(2);
let res = self.arena.mutate_root(|mc, root| {
let rhs = root.pop_stack_forced();
let lhs = root.pop_stack_forced();
match (Self::get_num(lhs), Self::get_num(rhs)) {
(_, Some(NixNum::Int(0))) => Err(vm_err("division by zero")),
(_, Some(NixNum::Float(0.))) => Err(vm_err("division by zero")),
_ => Ok(()),
}?;
let res = Self::numeric_binop(lhs, rhs, mc, |a, b| a / b, |a, b| a / b)?;
root.push_stack(res);
VmResult::Ok(())
});
try_vm!(self; res);
}
OpEq | OpNeq => {
self.force_n(2);
let map: fn(bool) -> bool = match op {
OpEq => |a| a,
OpNeq => |a| !a,
_ => unreachable!(),
};
let eq = try_vm!(self; self.values_equal());
self.push_stack(|_| Value::new_inline(map(eq)));
}
OpLt | OpGt | OpLeq | OpGeq => {
use std::cmp::Ordering;
self.force_n(2);
let pred: fn(Ordering) -> bool = match op {
OpLt => Ordering::is_lt,
OpGt => Ordering::is_gt,
OpLeq => Ordering::is_le,
OpGeq => Ordering::is_ge,
_ => unreachable!(),
};
try_vm!(self; self.compare_values(pred));
}
OpConcat => {
self.force_n(2);
let res = self.arena.mutate_root(|mc, root| {
let rhs = root.pop_stack_forced();
let lhs = root.pop_stack_forced();
// TODO: better type-assert ergonomic
let Some(l) = lhs.as_gc::<List<'_>>() else {
return Err(vm_err("cannot concatenate: left operand is not a list"));
};
let Some(r) = rhs.as_gc::<List<'_>>() else {
return Err(vm_err("cannot concatenate: right operand is not a list"));
};
let mut items = SmallVec::new();
items.extend_from_slice(&l);
items.extend_from_slice(&r);
root.push_stack(Value::new_gc(Gc::new(mc, List { inner: items })));
VmResult::Ok(())
});
try_vm!(self; res);
}
OpUpdate => {
self.force_n(2);
let res = self.arena.mutate_root(|mc, root| {
let rhs = root.pop_stack_forced();
let lhs = root.pop_stack_forced();
// TODO: better type-assert ergonomic
let Some(l) = lhs.as_gc::<AttrSet<'_>>() else {
return Err(vm_err("cannot update: left operand is not a set"));
};
let Some(r) = rhs.as_gc::<AttrSet<'_>>() else {
return Err(vm_err("cannot update: right operand is not a set"));
};
root.push_stack(Value::new_gc(l.merge(&r, mc)));
VmResult::Ok(())
});
try_vm!(self; res);
}
OpNeg => {
@@ -858,119 +952,7 @@ impl Runtime {
}
}
fn compute_binop(&mut self, op: BinOpTag) -> VmResult<()> {
self.force_n(2);
match op {
BinOpTag::Add => {
let strings = &self.strings;
self.arena.mutate_root(|mc, root| {
let rhs = root.pop_stack_forced();
let lhs = root.pop_stack_forced();
// FIXME: path & string context
if let (Some(ls), Some(rs)) = (
Self::get_string(strings, lhs),
Self::get_string(strings, rhs),
) {
let ns = Gc::new(mc, NixString::new(format!("{ls}{rs}")));
root.push_stack(Value::new_gc(ns));
return Ok(());
}
let res = Self::numeric_binop(lhs, rhs, mc, i64::wrapping_add, |a, b| a + b)?;
root.push_stack(res);
VmResult::Ok(())
})?;
}
BinOpTag::Sub => {
self.arena.mutate_root(|mc, root| {
let rhs = root.pop_stack_forced();
let lhs = root.pop_stack_forced();
let res = Self::numeric_binop(lhs, rhs, mc, i64::wrapping_sub, |a, b| a - b)?;
root.push_stack(res);
VmResult::Ok(())
})?;
}
BinOpTag::Mul => {
self.arena.mutate_root(|mc, root| {
let rhs = root.pop_stack_forced();
let lhs = root.pop_stack_forced();
let res = Self::numeric_binop(lhs, rhs, mc, i64::wrapping_mul, |a, b| a * b)?;
root.push_stack(res);
VmResult::Ok(())
})?;
}
BinOpTag::Div => {
self.arena.mutate_root(|mc, root| {
let rhs = root.pop_stack_forced();
let lhs = root.pop_stack_forced();
let res = match (Self::get_num(lhs), Self::get_num(rhs)) {
(_, Some(NixNum::Int(0))) => Err(vm_err("division by zero")),
(_, Some(NixNum::Float(0.))) => Err(vm_err("division by zero")),
(Some(NixNum::Int(a)), Some(NixNum::Int(b))) => {
Ok(Value::make_int(a.wrapping_div(b), mc))
}
(Some(NixNum::Float(a)), Some(NixNum::Float(b))) => {
Ok(Value::new_float(a / b))
}
(Some(NixNum::Int(a)), Some(NixNum::Float(b))) => {
Ok(Value::new_float(a as f64 / b))
}
(Some(NixNum::Float(a)), Some(NixNum::Int(b))) => {
Ok(Value::new_float(a / b as f64))
}
_ => Err(vm_err("cannot divide non-numbers")),
}?;
root.push_stack(res);
VmResult::Ok(())
})?;
}
BinOpTag::Eq => {
let eq = self.values_equal()?;
self.push_stack(|_| Value::new_inline(eq));
}
BinOpTag::Neq => {
let eq = self.values_equal()?;
self.push_stack(|_| Value::new_inline(!eq));
}
BinOpTag::Lt => self.compare_values(|o| o.is_lt())?,
BinOpTag::Gt => self.compare_values(|o| o.is_gt())?,
BinOpTag::Leq => self.compare_values(|o| o.is_le())?,
BinOpTag::Geq => self.compare_values(|o| o.is_ge())?,
BinOpTag::Concat => {
self.arena.mutate_root(|mc, root| {
let rhs = root.pop_stack_forced();
let lhs = root.pop_stack_forced();
let Some(l) = lhs.as_gc::<List<'_>>() else {
return Err(vm_err("cannot concatenate: left operand is not a list"));
};
let Some(r) = rhs.as_gc::<List<'_>>() else {
return Err(vm_err("cannot concatenate: right operand is not a list"));
};
let mut items = SmallVec::new();
items.extend(l.inner.iter().cloned());
items.extend(r.inner.iter().cloned());
root.push_stack(Value::new_gc(Gc::new(mc, List { inner: items })));
VmResult::Ok(())
})?;
}
BinOpTag::Update => {
self.arena.mutate_root(|mc, root| {
let rhs = root.pop_stack_forced();
let lhs = root.pop_stack_forced();
let Some(l) = lhs.as_gc::<AttrSet<'_>>() else {
return Err(vm_err("cannot update: left operand is not a set"));
};
let Some(r) = rhs.as_gc::<AttrSet<'_>>() else {
return Err(vm_err("cannot update: right operand is not a set"));
};
root.push_stack(Value::new_gc(l.merge(&r, mc)));
VmResult::Ok(())
})?;
}
}
Ok(())
}
#[inline]
fn numeric_binop<'gc>(
lhs: StrictValue<'gc>,
rhs: StrictValue<'gc>,
@@ -1056,8 +1038,8 @@ impl Runtime {
State::List(len) | State::AttrSet(len) => {
for i in 0..len {
self.arena.mutate_root(|_, root| {
let y = root.temp_stack.pop().unwrap();
let x = root.temp_stack.pop().unwrap();
let y = root.temp_stack.pop().expect("stack underflow");
let x = root.temp_stack.pop().expect("stack underflow");
root.push_stack(x);
root.push_stack(y);
});
@@ -1186,13 +1168,10 @@ impl Runtime {
pub(super) fn force_tos(&mut self) -> Action {
loop {
let run = self.arena.mutate_root(|_mc, root| {
let thunk = root
.stack
.tos_mut()
.expect("stack underflow");
let thunk = root.stack.tos_mut().expect("stack underflow");
let Some(thunk_state) = thunk.as_gc::<Thunk>() else {
return false
return false;
};
match *thunk_state.borrow() {
ThunkState::Pending { ip, env } => {
@@ -1210,7 +1189,7 @@ impl Runtime {
ThunkState::Evaluated(val) => {
*thunk = val;
false
},
}
ThunkState::Blackhole => todo!("force_tos"),
}
});
@@ -1261,7 +1240,7 @@ impl Runtime {
pub(super) fn handle_return(&mut self) -> Action {
self.force_tos();
let done= self.arena.mutate_root(|_, root| {
let done = self.arena.mutate_root(|_, root| {
let Some(frame) = root.frames.pop() else {
return true;
};
@@ -1276,21 +1255,18 @@ impl Runtime {
ForceMode::AsIs => (),
ForceMode::Shallow => {
if let done @ Action::Done(_) = self.force_tos_shallow() {
return done
return done;
}
}
ForceMode::Deep => {
if let done @ Action::Done(_) = self.force_tos_shallow() {
return done
return done;
}
}
}
let val = self.arena.mutate_root(|_, root| {
root.current_env = None;
convert_value(
root.stack.pop().expect("stack underflow"),
&self.strings,
)
convert_value(root.stack.pop().expect("stack underflow"), &self.strings)
});
Action::Done(Ok(val))
}