From 11b0b8a78e7d8d16f8d0249ee5086e662a15e11e Mon Sep 17 00:00:00 2001 From: imxyy_soope_ Date: Mon, 20 Apr 2026 17:57:54 +0800 Subject: [PATCH] better type assertion ergonomic --- fix-vm/src/boxing.rs | 32 +++- fix-vm/src/dispatch_tailcall.rs | 6 +- fix-vm/src/instructions/arithmetic.rs | 134 ++++++++--------- fix-vm/src/instructions/builtins_misc.rs | 24 +-- fix-vm/src/instructions/calls.rs | 24 +-- fix-vm/src/instructions/closures.rs | 20 +-- fix-vm/src/instructions/collections.rs | 48 +++--- fix-vm/src/instructions/control.rs | 20 +-- fix-vm/src/instructions/literals.rs | 44 +++--- fix-vm/src/instructions/variables.rs | 24 +-- fix-vm/src/instructions/with_scope.rs | 30 ++-- fix-vm/src/lib.rs | 71 ++++++--- fix-vm/src/value.rs | 180 +++++++++++++++++------ 13 files changed, 399 insertions(+), 258 deletions(-) diff --git a/fix-vm/src/boxing.rs b/fix-vm/src/boxing.rs index 4b8f4ce..7319152 100644 --- a/fix-vm/src/boxing.rs +++ b/fix-vm/src/boxing.rs @@ -160,6 +160,22 @@ enum TagVal { pub(crate) struct RawTag(TagVal); impl RawTag { + pub(crate) const P1: RawTag = RawTag(TagVal::_P1); + pub(crate) const P2: RawTag = RawTag(TagVal::_P2); + pub(crate) const P3: RawTag = RawTag(TagVal::_P3); + pub(crate) const P4: RawTag = RawTag(TagVal::_P4); + pub(crate) const P5: RawTag = RawTag(TagVal::_P5); + pub(crate) const P6: RawTag = RawTag(TagVal::_P6); + pub(crate) const P7: RawTag = RawTag(TagVal::_P7); + + pub(crate) const N1: RawTag = RawTag(TagVal::_N1); + pub(crate) const N2: RawTag = RawTag(TagVal::_N2); + pub(crate) const N3: RawTag = RawTag(TagVal::_N3); + pub(crate) const N4: RawTag = RawTag(TagVal::_N4); + pub(crate) const N5: RawTag = RawTag(TagVal::_N5); + pub(crate) const N6: RawTag = RawTag(TagVal::_N6); + pub(crate) const N7: RawTag = RawTag(TagVal::_N7); + #[inline] #[must_use] pub(crate) fn new(neg: bool, val: NonZeroU8) -> RawTag { @@ -195,7 +211,7 @@ impl RawTag { /// `val` must be in the range `1..8` #[inline] #[must_use] - pub(crate) unsafe fn new_unchecked(neg: bool, val: u8) -> RawTag { + pub(crate) const unsafe fn new_unchecked(neg: bool, val: u8) -> RawTag { RawTag(match (neg, val) { (false, 1) => TagVal::_P1, (false, 2) => TagVal::_P2, @@ -244,7 +260,7 @@ impl RawTag { #[inline] #[must_use] - pub(crate) fn neg_val(self) -> (bool, u8) { + pub(crate) const fn neg_val(self) -> (bool, u8) { match self.0 { TagVal::_P1 => (false, 1), TagVal::_P2 => (false, 2), @@ -285,17 +301,17 @@ impl Header { } #[inline] - fn tag(self) -> RawTag { + const fn tag(self) -> RawTag { unsafe { RawTag::new_unchecked(self.get_sign(), self.get_tag()) } } #[inline] - fn get_sign(self) -> bool { + const fn get_sign(self) -> bool { self.0 & 0x8000 != 0 } #[inline] - fn get_tag(self) -> u8 { + const fn get_tag(self) -> u8 { (self.0 & 0x0007) as u8 } @@ -341,7 +357,7 @@ impl Value { #[inline] #[must_use] - pub(crate) fn tag(&self) -> RawTag { + pub(crate) const fn tag(&self) -> RawTag { self.header.tag() } @@ -417,7 +433,7 @@ impl RawBox { #[inline] #[must_use] - pub(crate) fn tag(&self) -> Option { + pub(crate) const fn tag(&self) -> Option { if self.is_value() { Some(unsafe { self.value.tag() }) } else { @@ -433,7 +449,7 @@ impl RawBox { #[inline] #[must_use] - pub(crate) fn is_value(&self) -> bool { + pub(crate) const fn is_value(&self) -> bool { (unsafe { self.float.is_nan() } && unsafe { self.bits & SIGN_MASK != QUIET_NAN }) } diff --git a/fix-vm/src/dispatch_tailcall.rs b/fix-vm/src/dispatch_tailcall.rs index 1e0c817..94ada3a 100644 --- a/fix-vm/src/dispatch_tailcall.rs +++ b/fix-vm/src/dispatch_tailcall.rs @@ -2,7 +2,7 @@ use gc_arena::Mutation; -use crate::{BytecodeReader, StepResult, Vm, VmContext}; +use crate::{BytecodeReader, Step, Vm, VmContext}; pub(crate) enum TailResult { YieldFuel(u32), @@ -36,8 +36,8 @@ extern "rust-preserve-none" fn op_illegal<'gc, C: VmContext>( macro_rules! tail_dispatch_after { ($result:expr, $new_pc:expr, $vm:ident, $mc:ident, $ctx:ident, $bc:ident, $table:ident, $fuel:ident) => {{ match $result { - StepResult::Continue => {} - StepResult::Done => return TailResult::Done, + Step::Continue => {} + Step::Done => return TailResult::Done, } let new_pc: u32 = $new_pc; if $fuel == 0 { diff --git a/fix-vm/src/instructions/arithmetic.rs b/fix-vm/src/instructions/arithmetic.rs index cb46b16..6b4afee 100644 --- a/fix-vm/src/instructions/arithmetic.rs +++ b/fix-vm/src/instructions/arithmetic.rs @@ -2,7 +2,8 @@ use std::cmp::Ordering; use gc_arena::{Gc, Mutation}; -use crate::{BytecodeReader, NixNum, StepResult, StrictValue, Value, VmContextExt, VmError}; +use crate::{BytecodeReader, NixNum, Step, VmContextExt, VmError}; +use crate::value::*; impl<'gc> crate::Vm<'gc> { #[inline(always)] @@ -11,28 +12,28 @@ impl<'gc> crate::Vm<'gc> { ctx: &mut impl crate::VmContext, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>, - ) -> StepResult { + ) -> Step { if let Some(step) = self.try_force(1, reader, mc) { return step; } if let Some(step) = self.try_force(0, reader, mc) { return step; } - let rhs = self.pop_stack_forced(); - let lhs = self.pop_stack_forced(); + let rhs = self.pop_forced(); + let lhs = self.pop_forced(); if let (Some(ls), Some(rs)) = ( VmContextExt::get_string(ctx, lhs), VmContextExt::get_string(ctx, rhs), ) { let ns = Gc::new(mc, crate::NixString::new(format!("{ls}{rs}"))); - self.push_stack(Value::new_gc(ns)); - return StepResult::Continue; + self.push(Value::new_gc(ns)); + return Step::Continue; } let res = numeric_binop(lhs, rhs, mc, i64::wrapping_add, |a, b| a + b); match res { Ok(val) => { - self.push_stack(val); - StepResult::Continue + self.push(val); + Step::Continue } Err(e) => self.finish_vm_err(e), } @@ -43,7 +44,7 @@ impl<'gc> crate::Vm<'gc> { &mut self, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>, - ) -> StepResult { + ) -> Step { self.op_arith(reader, mc, i64::wrapping_sub, |a, b| a - b) } @@ -52,7 +53,7 @@ impl<'gc> crate::Vm<'gc> { &mut self, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>, - ) -> StepResult { + ) -> Step { self.op_arith(reader, mc, i64::wrapping_mul, |a, b| a * b) } @@ -63,20 +64,20 @@ impl<'gc> crate::Vm<'gc> { mc: &Mutation<'gc>, int_op: fn(i64, i64) -> i64, float_op: fn(f64, f64) -> f64, - ) -> StepResult { + ) -> Step { if let Some(step) = self.try_force(1, reader, mc) { return step; } if let Some(step) = self.try_force(0, reader, mc) { return step; } - let rhs = self.pop_stack_forced(); - let lhs = self.pop_stack_forced(); + let rhs = self.pop_forced(); + let lhs = self.pop_forced(); let res = numeric_binop(lhs, rhs, mc, int_op, float_op); match res { Ok(val) => { - self.push_stack(val); - StepResult::Continue + self.push(val); + Step::Continue } Err(e) => self.finish_vm_err(e), } @@ -87,15 +88,15 @@ impl<'gc> crate::Vm<'gc> { &mut self, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>, - ) -> StepResult { + ) -> Step { if let Some(step) = self.try_force(1, reader, mc) { return step; } if let Some(step) = self.try_force(0, reader, mc) { return step; } - let rhs = self.pop_stack_forced(); - let lhs = self.pop_stack_forced(); + let rhs = self.pop_forced(); + let lhs = self.pop_forced(); match (get_num(rhs), get_num(lhs)) { (_, Some(NixNum::Int(0))) | (_, Some(NixNum::Float(0.))) => { return self.finish_vm_err(VmError::Uncatchable(fix_error::Error::eval_error( @@ -107,8 +108,8 @@ impl<'gc> crate::Vm<'gc> { let res = numeric_binop(lhs, rhs, mc, |a, b| a / b, |a, b| a / b); match res { Ok(val) => { - self.push_stack(val); - StepResult::Continue + self.push(val); + Step::Continue } Err(e) => self.finish_vm_err(e), } @@ -120,7 +121,7 @@ impl<'gc> crate::Vm<'gc> { ctx: &mut impl crate::VmContext, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>, - ) -> StepResult { + ) -> Step { if let Some(step) = self.try_force(1, reader, mc) { return step; } @@ -131,8 +132,8 @@ impl<'gc> crate::Vm<'gc> { Ok(eq) => eq, Err(e) => return self.finish_vm_err(e), }; - self.push_stack(Value::new_inline(eq)); - StepResult::Continue + self.push(Value::new_inline(eq)); + Step::Continue } #[inline(always)] @@ -141,7 +142,7 @@ impl<'gc> crate::Vm<'gc> { ctx: &mut impl crate::VmContext, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>, - ) -> StepResult { + ) -> Step { if let Some(step) = self.try_force(1, reader, mc) { return step; } @@ -152,8 +153,8 @@ impl<'gc> crate::Vm<'gc> { Ok(eq) => eq, Err(e) => return self.finish_vm_err(e), }; - self.push_stack(Value::new_inline(!eq)); - StepResult::Continue + self.push(Value::new_inline(!eq)); + Step::Continue } #[inline(always)] @@ -162,7 +163,7 @@ impl<'gc> crate::Vm<'gc> { ctx: &mut impl crate::VmContext, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>, - ) -> StepResult { + ) -> Step { self.compare_values(ctx, reader, mc, Ordering::is_lt) } @@ -172,7 +173,7 @@ impl<'gc> crate::Vm<'gc> { ctx: &mut impl crate::VmContext, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>, - ) -> StepResult { + ) -> Step { self.compare_values(ctx, reader, mc, Ordering::is_gt) } @@ -182,7 +183,7 @@ impl<'gc> crate::Vm<'gc> { ctx: &mut impl crate::VmContext, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>, - ) -> StepResult { + ) -> Step { self.compare_values(ctx, reader, mc, Ordering::is_le) } @@ -192,7 +193,7 @@ impl<'gc> crate::Vm<'gc> { ctx: &mut impl crate::VmContext, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>, - ) -> StepResult { + ) -> Step { self.compare_values(ctx, reader, mc, Ordering::is_ge) } @@ -202,7 +203,7 @@ impl<'gc> crate::Vm<'gc> { reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>, pred: fn(Ordering) -> bool, - ) -> StepResult { + ) -> Step { if let Some(step) = self.try_force(1, reader, mc) { return step; } @@ -210,7 +211,7 @@ impl<'gc> crate::Vm<'gc> { return step; } match self.compare_values_inner(ctx, pred) { - Ok(()) => StepResult::Continue, + Ok(()) => Step::Continue, Err(e) => self.finish_vm_err(e), } } @@ -220,30 +221,26 @@ impl<'gc> crate::Vm<'gc> { &mut self, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>, - ) -> StepResult { + ) -> Step { if let Some(step) = self.try_force(1, reader, mc) { return step; } if let Some(step) = self.try_force(0, reader, mc) { return step; } - let rhs = self.pop_stack_forced(); - let lhs = self.pop_stack_forced(); - let Some(l) = lhs.as_gc::() else { - return self.finish_err(fix_error::Error::eval_error( - "cannot concatenate: left operand is not a list", - )); + let r = match self.pop_forced_expect_gc::() { + Ok(val) => val, + Err(got) => return self.finish_type_err(NixType::List, got) }; - let Some(r) = rhs.as_gc::() else { - return self.finish_err(fix_error::Error::eval_error( - "cannot concatenate: right operand is not a list", - )); + let l = match self.pop_forced_expect_gc::() { + Ok(val) => val, + Err(got) => return self.finish_type_err(NixType::List, got) }; let mut items = smallvec::SmallVec::new(); items.extend_from_slice(&l); items.extend_from_slice(&r); - self.push_stack(Value::new_gc(Gc::new(mc, crate::List { inner: items }))); - StepResult::Continue + self.push(Value::new_gc(Gc::new(mc, crate::List { inner: items }))); + Step::Continue } #[inline(always)] @@ -251,42 +248,38 @@ impl<'gc> crate::Vm<'gc> { &mut self, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>, - ) -> StepResult { + ) -> Step { if let Some(step) = self.try_force(1, reader, mc) { return step; } if let Some(step) = self.try_force(0, reader, mc) { return step; } - let rhs = self.pop_stack_forced(); - let lhs = self.pop_stack_forced(); - let Some(l) = lhs.as_gc::() else { - return self.finish_err(fix_error::Error::eval_error( - "cannot update: left operand is not a set", - )); + let r = match self.pop_forced_expect_gc::() { + Ok(val) => val, + Err(got) => return self.finish_type_err(NixType::AttrSet, got) }; - let Some(r) = rhs.as_gc::() else { - return self.finish_err(fix_error::Error::eval_error( - "cannot update: right operand is not a set", - )); + let l = match self.pop_forced_expect_gc::() { + Ok(val) => val, + Err(got) => return self.finish_type_err(NixType::AttrSet, got) }; - self.push_stack(Value::new_gc(l.merge(&r, mc))); - StepResult::Continue + self.push(Value::new_gc(l.merge(&r, mc))); + Step::Continue } #[inline(always)] - pub(crate) fn op_neg(&mut self) -> StepResult { + pub(crate) fn op_neg(&mut self) -> Step { todo!("implement unary operation"); } #[inline(always)] - pub(crate) fn op_not(&mut self) -> StepResult { + pub(crate) fn op_not(&mut self) -> Step { todo!("implement unary operation"); } pub(crate) fn values_equal(&mut self, ctx: &impl crate::VmContext) -> crate::VmResult { - let rhs = self.pop_stack_forced(); - let lhs = self.pop_stack_forced(); + let rhs = self.pop_forced(); + let lhs = self.pop_forced(); if let (Some(a), Some(b)) = (get_num(lhs), get_num(rhs)) { return Ok(match (a, b) { @@ -314,8 +307,8 @@ impl<'gc> crate::Vm<'gc> { } let len = a.inner.len(); for (x, y) in a.inner.iter().zip(b.inner.iter()).rev() { - self.push_stack(*x); - self.push_stack(*y); + self.push(*x); + self.push(*y); } for i in 0..len { let eq = self.values_equal(ctx)?; @@ -336,8 +329,8 @@ impl<'gc> crate::Vm<'gc> { if k1 != k2 { return Ok(false); } - self.push_stack(*v1); - self.push_stack(*v2); + self.push(*v1); + self.push(*v2); } for i in 0..len { let eq = self.values_equal(ctx)?; @@ -357,8 +350,8 @@ impl<'gc> crate::Vm<'gc> { ctx: &impl crate::VmContext, pred: fn(Ordering) -> bool, ) -> crate::VmResult<()> { - let rhs = self.pop_stack_forced(); - let lhs = self.pop_stack_forced(); + let rhs = self.pop_forced(); + let lhs = self.pop_forced(); if let (Some(a), Some(b)) = (get_num(lhs), get_num(rhs)) { let ord = match (a, b) { @@ -371,16 +364,17 @@ impl<'gc> crate::Vm<'gc> { a.partial_cmp(&(b as f64)).unwrap_or(Ordering::Less) } }; - self.push_stack(Value::new_inline(pred(ord))); + self.push(Value::new_inline(pred(ord))); return Ok(()); } if let (Some(a), Some(b)) = ( VmContextExt::get_string(ctx, lhs), VmContextExt::get_string(ctx, rhs), ) { - self.push_stack(Value::new_inline(pred(a.cmp(b)))); + self.push(Value::new_inline(pred(a.cmp(b)))); return Ok(()); } + // TODO: compare other types Err(crate::vm_err("cannot compare these types")) } } diff --git a/fix-vm/src/instructions/builtins_misc.rs b/fix-vm/src/instructions/builtins_misc.rs index 2ee8275..4295212 100644 --- a/fix-vm/src/instructions/builtins_misc.rs +++ b/fix-vm/src/instructions/builtins_misc.rs @@ -1,28 +1,28 @@ use fix_builtins::BuiltinId; use num_enum::TryFromPrimitive; -use crate::{BytecodeReader, PrimOp, StepResult, Value}; +use crate::{BytecodeReader, PrimOp, Step, Value}; impl<'gc> crate::Vm<'gc> { #[inline(always)] - pub(crate) fn op_load_builtins(&mut self) -> StepResult { - self.push_stack(self.builtins); - StepResult::Continue + pub(crate) fn op_load_builtins(&mut self) -> Step { + self.push(self.builtins); + Step::Continue } #[inline(always)] - pub(crate) fn op_load_builtin(&mut self, reader: &mut BytecodeReader<'_>) -> StepResult { + pub(crate) fn op_load_builtin(&mut self, reader: &mut BytecodeReader<'_>) -> Step { let Ok(id) = BuiltinId::try_from_primitive(reader.read_u8()) .map_err(|err| panic!("unknown builtin id: {}", err.number)); - self.push_stack(Value::new_inline(PrimOp { + self.push(Value::new_inline(PrimOp { id, arity: fix_builtins::BUILTINS[id as usize].1, })); - StepResult::Continue + Step::Continue } #[inline(always)] - pub(crate) fn op_mk_pos(&mut self, reader: &mut BytecodeReader<'_>) -> StepResult { + pub(crate) fn op_mk_pos(&mut self, reader: &mut BytecodeReader<'_>) -> Step { let _span_id = reader.read_u32(); todo!("MkPos"); } @@ -31,7 +31,7 @@ impl<'gc> crate::Vm<'gc> { pub(crate) fn op_load_repl_binding( &mut self, reader: &mut BytecodeReader<'_>, - ) -> StepResult { + ) -> Step { let _name = reader.read_string_id(); todo!("LoadReplBinding"); } @@ -40,7 +40,7 @@ impl<'gc> crate::Vm<'gc> { pub(crate) fn op_load_scoped_binding( &mut self, reader: &mut BytecodeReader<'_>, - ) -> StepResult { + ) -> Step { let _name = reader.read_string_id(); todo!("LoadScopedBinding"); } @@ -51,7 +51,7 @@ impl<'gc> crate::Vm<'gc> { ctx: &mut impl crate::VmContext, reader: &mut BytecodeReader<'_>, _mc: &gc_arena::Mutation<'gc>, - ) -> StepResult { + ) -> Step { let _parts_count = reader.read_u16() as usize; let _force_string = reader.read_u8() != 0; let mut _operands: smallvec::SmallVec<[crate::OperandData; 4]> = @@ -63,7 +63,7 @@ impl<'gc> crate::Vm<'gc> { } #[inline(always)] - pub(crate) fn op_resolve_path(&mut self, _ctx: &mut impl crate::VmContext) -> StepResult { + pub(crate) fn op_resolve_path(&mut self, _ctx: &mut impl crate::VmContext) -> Step { todo!("implement ResolvePath"); } } diff --git a/fix-vm/src/instructions/calls.rs b/fix-vm/src/instructions/calls.rs index c13c448..0611320 100644 --- a/fix-vm/src/instructions/calls.rs +++ b/fix-vm/src/instructions/calls.rs @@ -1,7 +1,7 @@ use fix_error::Error; use gc_arena::{Gc, Mutation, RefLock}; -use crate::{BytecodeReader, CallFrame, Closure, Env, StepResult, ThunkState, VmContextExt}; +use crate::{BytecodeReader, CallFrame, Closure, Env, Step, ThunkState, VmContextExt}; impl<'gc> crate::Vm<'gc> { #[inline(always)] @@ -10,7 +10,7 @@ impl<'gc> crate::Vm<'gc> { ctx: &mut impl crate::VmContext, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>, - ) -> StepResult { + ) -> Step { if let Some(step) = self.try_force(0, reader, mc) { return step; } @@ -18,7 +18,7 @@ impl<'gc> crate::Vm<'gc> { return self.finish_err(Error::eval_error("stack overflow; max-call-depth exceeded")); } self.call_depth += 1; - let func = self.pop_stack(); + let func = self.pop(); let arg = reader.read_operand_data(ctx).resolve(mc, self); if let Some(closure) = func.as_gc::() { let ip = closure.ip; @@ -41,7 +41,7 @@ impl<'gc> crate::Vm<'gc> { } else { todo!("call other types: {func:?}") } - StepResult::Continue + Step::Continue } #[inline(always)] @@ -50,7 +50,7 @@ impl<'gc> crate::Vm<'gc> { ctx: &mut impl crate::VmContext, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>, - ) -> StepResult { + ) -> Step { self.handle_return(reader, ctx, mc) } @@ -59,7 +59,7 @@ impl<'gc> crate::Vm<'gc> { reader: &mut BytecodeReader<'_>, ctx: &C, mc: &Mutation<'gc>, - ) -> StepResult { + ) -> Step { let ret_inst_pc = reader.pc() - 1; let Some(CallFrame { pc: ret_pc, @@ -69,18 +69,18 @@ impl<'gc> crate::Vm<'gc> { with_env, }) = self.call_stack.pop() else { - let val = self.pop_stack(); + let val = self.pop(); return self.finish_ok(ctx.convert_value(val)); }; reader.set_pc(ret_pc); if let Some(outer_thunk) = thunk { - let val = self.pop_stack(); + let val = self.pop(); match val.restrict() { Ok(val) => { *outer_thunk.borrow_mut(mc) = ThunkState::Evaluated(val); if reader.bytecode().get(ret_pc).copied() == Some(fix_codegen::Op::Return as u8) { - self.push_stack(val.relax()); + self.push(val.relax()); } } Err(inner_thunk) => { @@ -109,14 +109,14 @@ impl<'gc> crate::Vm<'gc> { reader.set_pc(inner_ip); self.env = inner_env; self.with_env = inner_with_env; - return StepResult::Continue; + return Step::Continue; } ThunkState::Evaluated(val) => { *outer_thunk.borrow_mut(mc) = ThunkState::Evaluated(val); if reader.bytecode().get(ret_pc).copied() == Some(fix_codegen::Op::Return as u8) { - self.push_stack(val.relax()); + self.push(val.relax()); } } ThunkState::Apply { func: _, arg: _ } => todo!("force Apply thunk"), @@ -132,6 +132,6 @@ impl<'gc> crate::Vm<'gc> { } self.env = env; self.with_env = with_env; - StepResult::Continue + Step::Continue } } diff --git a/fix-vm/src/instructions/closures.rs b/fix-vm/src/instructions/closures.rs index 78e7412..2befe3d 100644 --- a/fix-vm/src/instructions/closures.rs +++ b/fix-vm/src/instructions/closures.rs @@ -1,6 +1,6 @@ use gc_arena::{Gc, Mutation, RefLock}; -use crate::{BytecodeReader, StepResult, ThunkState, Value}; +use crate::{BytecodeReader, Step, ThunkState, Value}; impl<'gc> crate::Vm<'gc> { #[inline(always)] @@ -8,7 +8,7 @@ impl<'gc> crate::Vm<'gc> { &mut self, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>, - ) -> StepResult { + ) -> Step { let entry_point = reader.read_u32(); let thunk = Gc::new( mc, @@ -18,8 +18,8 @@ impl<'gc> crate::Vm<'gc> { with_env: self.with_env, }), ); - self.push_stack(Value::new_gc(thunk)); - StepResult::Continue + self.push(Value::new_gc(thunk)); + Step::Continue } #[inline(always)] @@ -27,7 +27,7 @@ impl<'gc> crate::Vm<'gc> { &mut self, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>, - ) -> StepResult { + ) -> Step { let entry_point = reader.read_u32(); let n_locals = reader.read_u32(); let closure = Gc::new( @@ -39,8 +39,8 @@ impl<'gc> crate::Vm<'gc> { pattern: None, }, ); - self.push_stack(Value::new_gc(closure)); - StepResult::Continue + self.push(Value::new_gc(closure)); + Step::Continue } #[inline(always)] @@ -48,7 +48,7 @@ impl<'gc> crate::Vm<'gc> { &mut self, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>, - ) -> StepResult { + ) -> Step { let entry_point = reader.read_u32(); let n_locals = reader.read_u32(); let req_count = reader.read_u16() as usize; @@ -89,7 +89,7 @@ impl<'gc> crate::Vm<'gc> { pattern: Some(pattern), }, ); - self.push_stack(Value::new_gc(closure)); - StepResult::Continue + self.push(Value::new_gc(closure)); + Step::Continue } } diff --git a/fix-vm/src/instructions/collections.rs b/fix-vm/src/instructions/collections.rs index 3402c33..07f94a8 100644 --- a/fix-vm/src/instructions/collections.rs +++ b/fix-vm/src/instructions/collections.rs @@ -3,7 +3,7 @@ use gc_arena::Gc; use smallvec::SmallVec; use crate::{ - AttrKeyData, AttrSet, BytecodeReader, List, NixString, OperandData, StepResult, Value, + AttrKeyData, AttrSet, BytecodeReader, List, NixString, OperandData, Step, Value, }; impl<'gc> crate::Vm<'gc> { @@ -13,7 +13,7 @@ impl<'gc> crate::Vm<'gc> { ctx: &mut impl crate::VmContext, reader: &mut BytecodeReader<'_>, mc: &gc_arena::Mutation<'gc>, - ) -> StepResult { + ) -> Step { let count = reader.read_u32() as usize; let mut entries: SmallVec<[AttrEntry; 4]> = SmallVec::with_capacity(count); for _ in 0..count { @@ -37,14 +37,14 @@ impl<'gc> crate::Vm<'gc> { } kv.sort_by_key(|(k, _)| *k); let attrs = Gc::new(mc, AttrSet::from_sorted_unchecked(kv)); - self.push_stack(Value::new_gc(attrs)); - StepResult::Continue + self.push(Value::new_gc(attrs)); + Step::Continue } #[inline(always)] - pub(crate) fn op_make_empty_attrs(&mut self) -> StepResult { - self.push_stack(self.empty_attrs); - StepResult::Continue + pub(crate) fn op_make_empty_attrs(&mut self) -> Step { + self.push(self.empty_attrs); + Step::Continue } #[inline(always)] @@ -53,7 +53,7 @@ impl<'gc> crate::Vm<'gc> { ctx: &mut impl crate::VmContext, reader: &mut BytecodeReader<'_>, mc: &gc_arena::Mutation<'gc>, - ) -> StepResult { + ) -> Step { let _span_id = reader.read_u32(); let key = reader.read_string_id(); @@ -61,7 +61,7 @@ impl<'gc> crate::Vm<'gc> { return step; } - let attrs = self.peek_stack(0).restrict().expect("forced"); + let attrs = self.peek(0).restrict().expect("forced"); let Some(attrset) = attrs.as_gc::() else { return self.finish_err(Error::eval_error( "value is not a set while a set was expected", @@ -70,7 +70,7 @@ impl<'gc> crate::Vm<'gc> { match attrset.lookup(key) { Some(v) => { - self.replace_stack(0, v); + self.replace(0, v); } None => loop { let byte = reader.bytecode()[reader.pc()]; @@ -80,7 +80,7 @@ impl<'gc> crate::Vm<'gc> { reader.set_pc(reader.pc() + 1 + 4); } else if byte == fix_codegen::Op::JumpIfSelectSucceeded as u8 { reader.set_pc(reader.pc() + 1 + 4); - let _ = self.pop_stack(); + let _ = self.pop(); break; } else { let name = ctx.resolve_string(key); @@ -89,7 +89,7 @@ impl<'gc> crate::Vm<'gc> { } }, } - StepResult::Continue + Step::Continue } #[inline(always)] @@ -98,7 +98,7 @@ impl<'gc> crate::Vm<'gc> { ctx: &mut impl crate::VmContext, reader: &mut BytecodeReader<'_>, mc: &gc_arena::Mutation<'gc>, - ) -> StepResult { + ) -> Step { let _span_id = reader.read_u32(); if let Some(step) = self.try_force(0, reader, mc) { @@ -129,28 +129,28 @@ impl<'gc> crate::Vm<'gc> { match attrset.lookup(key_sid) { Some(v) => { self.stack.truncate(self.stack.len() - 2); - self.push_stack(v); + self.push(v); } None => { let name = ctx.resolve_string(key_sid); return self.finish_err(Error::eval_error(format!("attribute '{name}' missing"))); } } - StepResult::Continue + Step::Continue } #[inline(always)] pub(crate) fn op_jump_if_select_succeeded( &mut self, reader: &mut BytecodeReader<'_>, - ) -> StepResult { + ) -> Step { let offset = reader.read_i32(); reader.set_pc(((reader.pc() as isize) + (offset as isize)) as usize); - StepResult::Continue + Step::Continue } #[inline(always)] - pub(crate) fn op_has_attr(&mut self, reader: &mut BytecodeReader<'_>) -> StepResult { + pub(crate) fn op_has_attr(&mut self, reader: &mut BytecodeReader<'_>) -> Step { let _n = reader.read_u16() as usize; todo!("HasAttr"); } @@ -161,21 +161,21 @@ impl<'gc> crate::Vm<'gc> { ctx: &mut impl crate::VmContext, reader: &mut BytecodeReader<'_>, mc: &gc_arena::Mutation<'gc>, - ) -> StepResult { + ) -> Step { let count = reader.read_u32() as usize; let mut items: SmallVec<[Value; 4]> = SmallVec::with_capacity(count); for _ in 0..count { items.push(reader.read_operand_data(ctx).resolve(mc, self)); } let list = Gc::new(mc, List { inner: items }); - self.push_stack(Value::new_gc(list)); - StepResult::Continue + self.push(Value::new_gc(list)); + Step::Continue } #[inline(always)] - pub(crate) fn op_make_empty_list(&mut self) -> StepResult { - self.push_stack(self.empty_list); - StepResult::Continue + pub(crate) fn op_make_empty_list(&mut self) -> Step { + self.push(self.empty_list); + Step::Continue } } diff --git a/fix-vm/src/instructions/control.rs b/fix-vm/src/instructions/control.rs index 82600c3..ba6b2ae 100644 --- a/fix-vm/src/instructions/control.rs +++ b/fix-vm/src/instructions/control.rs @@ -1,4 +1,4 @@ -use crate::{BytecodeReader, StepResult}; +use crate::{BytecodeReader, Step}; impl<'gc> crate::Vm<'gc> { #[inline(always)] @@ -6,16 +6,16 @@ impl<'gc> crate::Vm<'gc> { &mut self, reader: &mut BytecodeReader<'_>, mc: &gc_arena::Mutation<'gc>, - ) -> StepResult { + ) -> Step { let offset = reader.read_i32(); if let Some(step) = self.try_force(0, reader, mc) { return step; } - let cond = self.pop_stack(); + let cond = self.pop(); if cond.as_inline::() == Some(false) { reader.set_pc(((reader.pc() as isize) + (offset as isize)) as usize); } - StepResult::Continue + Step::Continue } #[inline(always)] @@ -23,27 +23,27 @@ impl<'gc> crate::Vm<'gc> { &mut self, reader: &mut BytecodeReader<'_>, mc: &gc_arena::Mutation<'gc>, - ) -> StepResult { + ) -> Step { let offset = reader.read_i32(); if let Some(step) = self.try_force(0, reader, mc) { return step; } - let cond = self.pop_stack(); + let cond = self.pop(); if cond.as_inline::() == Some(true) { reader.set_pc(((reader.pc() as isize) + (offset as isize)) as usize); } - StepResult::Continue + Step::Continue } #[inline(always)] - pub(crate) fn op_jump(&mut self, reader: &mut BytecodeReader<'_>) -> StepResult { + pub(crate) fn op_jump(&mut self, reader: &mut BytecodeReader<'_>) -> Step { let offset = reader.read_i32(); reader.set_pc(((reader.pc() as isize) + (offset as isize)) as usize); - StepResult::Continue + Step::Continue } #[inline(always)] - pub(crate) fn op_assert(&mut self, reader: &mut BytecodeReader<'_>) -> StepResult { + pub(crate) fn op_assert(&mut self, reader: &mut BytecodeReader<'_>) -> Step { let _raw_idx = reader.read_u32(); let _span_id = reader.read_u32(); todo!("implement Assert (force TOS)"); diff --git a/fix-vm/src/instructions/literals.rs b/fix-vm/src/instructions/literals.rs index 1d6f23f..9f95c41 100644 --- a/fix-vm/src/instructions/literals.rs +++ b/fix-vm/src/instructions/literals.rs @@ -1,13 +1,13 @@ use gc_arena::{Gc, Mutation}; -use crate::{BytecodeReader, StepResult, Value}; +use crate::{BytecodeReader, Step, Value}; impl<'gc> crate::Vm<'gc> { #[inline(always)] - pub(crate) fn op_push_smi(&mut self, reader: &mut BytecodeReader<'_>) -> StepResult { + pub(crate) fn op_push_smi(&mut self, reader: &mut BytecodeReader<'_>) -> Step { let val = reader.read_i32(); - self.push_stack(Value::new_inline(val)); - StepResult::Continue + self.push(Value::new_inline(val)); + Step::Continue } #[inline(always)] @@ -15,41 +15,41 @@ impl<'gc> crate::Vm<'gc> { &mut self, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>, - ) -> StepResult { + ) -> Step { let val = reader.read_i64(); - self.push_stack(Value::new_gc(Gc::new(mc, val))); - StepResult::Continue + self.push(Value::new_gc(Gc::new(mc, val))); + Step::Continue } #[inline(always)] - pub(crate) fn op_push_float(&mut self, reader: &mut BytecodeReader<'_>) -> StepResult { + pub(crate) fn op_push_float(&mut self, reader: &mut BytecodeReader<'_>) -> Step { let val = reader.read_f64(); - self.push_stack(Value::new_float(val)); - StepResult::Continue + self.push(Value::new_float(val)); + Step::Continue } #[inline(always)] - pub(crate) fn op_push_string(&mut self, reader: &mut BytecodeReader<'_>) -> StepResult { + pub(crate) fn op_push_string(&mut self, reader: &mut BytecodeReader<'_>) -> Step { let sid = reader.read_string_id(); - self.push_stack(Value::new_inline(sid)); - StepResult::Continue + self.push(Value::new_inline(sid)); + Step::Continue } #[inline(always)] - pub(crate) fn op_push_null(&mut self) -> StepResult { - self.push_stack(Value::new_inline(crate::Null)); - StepResult::Continue + pub(crate) fn op_push_null(&mut self) -> Step { + self.push(Value::new_inline(crate::Null)); + Step::Continue } #[inline(always)] - pub(crate) fn op_push_true(&mut self) -> StepResult { - self.push_stack(Value::new_inline(true)); - StepResult::Continue + pub(crate) fn op_push_true(&mut self) -> Step { + self.push(Value::new_inline(true)); + Step::Continue } #[inline(always)] - pub(crate) fn op_push_false(&mut self) -> StepResult { - self.push_stack(Value::new_inline(false)); - StepResult::Continue + pub(crate) fn op_push_false(&mut self) -> Step { + self.push(Value::new_inline(false)); + Step::Continue } } diff --git a/fix-vm/src/instructions/variables.rs b/fix-vm/src/instructions/variables.rs index 20ed49d..241023d 100644 --- a/fix-vm/src/instructions/variables.rs +++ b/fix-vm/src/instructions/variables.rs @@ -1,15 +1,15 @@ -use crate::{BytecodeReader, Mutation, StepResult, Value}; +use crate::{BytecodeReader, Mutation, Step, Value}; impl<'gc> crate::Vm<'gc> { #[inline(always)] - pub(crate) fn op_load_local(&mut self, reader: &mut BytecodeReader<'_>) -> StepResult { + pub(crate) fn op_load_local(&mut self, reader: &mut BytecodeReader<'_>) -> Step { let idx = reader.read_u32() as usize; - self.push_stack(self.env.borrow().locals[idx]); - StepResult::Continue + self.push(self.env.borrow().locals[idx]); + Step::Continue } #[inline(always)] - pub(crate) fn op_load_outer(&mut self, reader: &mut BytecodeReader<'_>) -> StepResult { + pub(crate) fn op_load_outer(&mut self, reader: &mut BytecodeReader<'_>) -> Step { let layer = reader.read_u8(); let idx = reader.read_u32() as usize; let mut cur = self.env; @@ -18,8 +18,8 @@ impl<'gc> crate::Vm<'gc> { cur = prev; } let val = cur.borrow().locals[idx]; - self.push_stack(val); - StepResult::Continue + self.push(val); + Step::Continue } #[inline(always)] @@ -27,11 +27,11 @@ impl<'gc> crate::Vm<'gc> { &mut self, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>, - ) -> StepResult { + ) -> Step { let idx = reader.read_u32() as usize; - let val = self.pop_stack(); + let val = self.pop(); self.env.borrow_mut(mc).locals[idx] = val; - StepResult::Continue + Step::Continue } #[inline(always)] @@ -39,12 +39,12 @@ impl<'gc> crate::Vm<'gc> { &mut self, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>, - ) -> StepResult { + ) -> Step { let count = reader.read_u32() as usize; self.env .borrow_mut(mc) .locals .extend(std::iter::repeat_n(Value::default(), count)); - StepResult::Continue + Step::Continue } } diff --git a/fix-vm/src/instructions/with_scope.rs b/fix-vm/src/instructions/with_scope.rs index 05a5032..4a10fde 100644 --- a/fix-vm/src/instructions/with_scope.rs +++ b/fix-vm/src/instructions/with_scope.rs @@ -2,7 +2,8 @@ use fix_common::Symbol; use fix_error::Error; use gc_arena::Gc; -use crate::{BytecodeReader, CallFrame, StepResult, WithEnv}; +use crate::{BytecodeReader, CallFrame, Step, WithEnv}; +use crate::value::*; impl<'gc> crate::Vm<'gc> { #[inline(always)] @@ -11,7 +12,7 @@ impl<'gc> crate::Vm<'gc> { ctx: &mut impl crate::VmContext, reader: &mut BytecodeReader<'_>, mc: &gc_arena::Mutation<'gc>, - ) -> StepResult { + ) -> Step { let env = reader.read_operand_data(ctx).resolve(mc, self); let scope = Gc::new( mc, @@ -21,20 +22,20 @@ impl<'gc> crate::Vm<'gc> { }, ); self.with_env = Some(scope); - StepResult::Continue + Step::Continue } #[inline(always)] - pub(crate) fn op_pop_with(&mut self) -> StepResult { + pub(crate) fn op_pop_with(&mut self) -> Step { let Some(scope) = self.with_env else { unreachable!("no with_scope to pop"); }; self.with_env = scope.prev; - StepResult::Continue + Step::Continue } #[inline(always)] - pub(crate) fn op_prepare_with(&mut self) -> StepResult { + pub(crate) fn op_prepare_with(&mut self) -> Step { self.call_stack.push(CallFrame { pc: usize::MAX, stack_depth: 0, @@ -42,7 +43,7 @@ impl<'gc> crate::Vm<'gc> { env: self.env, with_env: self.with_env, }); - StepResult::Continue + Step::Continue } #[inline(always)] @@ -51,7 +52,7 @@ impl<'gc> crate::Vm<'gc> { ctx: &mut impl crate::VmContext, reader: &mut BytecodeReader<'_>, mc: &gc_arena::Mutation<'gc>, - ) -> StepResult { + ) -> Step { let name = reader.read_string_id(); let Some(&WithEnv { env, prev }) = self.with_env.as_deref() else { @@ -64,23 +65,26 @@ impl<'gc> crate::Vm<'gc> { Symbol::from(ctx.resolve_string(name)) ))); }; - self.push_stack(env); + self.push(env); if let Some(step) = self.try_force(0, reader, mc) { return step; } - let env = self.pop_stack().as_gc::().unwrap(); + let env = match self.pop_forced_expect_gc::() { + Ok(val) => val, + Err(got) => return self.finish_type_err(NixType::List, got) + }; let Some(val) = env.lookup(name) else { reader.set_pc(reader.inst_start_pc()); self.with_env = prev; - return StepResult::Continue; + return Step::Continue; }; - self.push_stack(val); + self.push(val); let Some(CallFrame { with_env, .. }) = self.call_stack.pop() else { unreachable!() }; self.with_env = with_env; - StepResult::Continue + Step::Continue } } diff --git a/fix-vm/src/lib.rs b/fix-vm/src/lib.rs index ea62019..e461b1e 100644 --- a/fix-vm/src/lib.rs +++ b/fix-vm/src/lib.rs @@ -132,7 +132,7 @@ impl VmContextExt for T { } #[repr(u8)] -pub(crate) enum StepResult { +pub(crate) enum Step { Continue, Done, } @@ -271,36 +271,42 @@ impl<'gc> Vm<'gc> { } #[inline(always)] - pub(crate) fn finish_ok(&mut self, val: fix_common::Value) -> StepResult { + pub(crate) fn finish_ok(&mut self, val: fix_common::Value) -> Step { self.result = Some(Ok(val)); - StepResult::Done + Step::Done } #[inline(always)] - pub(crate) fn finish_err(&mut self, err: Box) -> StepResult { + pub(crate) fn finish_err(&mut self, err: Box) -> Step { self.result = Some(Err(err)); - StepResult::Done + Step::Done } #[inline(always)] - pub(crate) fn finish_vm_err(&mut self, err: VmError) -> StepResult { + pub(crate) fn finish_type_err(&mut self, expected: NixType, got: NixType) -> Step { + self.result = Some(Err(Error::eval_error(format!("expected {expected}, got {got}")))); + Step::Done + } + + #[inline(always)] + pub(crate) fn finish_vm_err(&mut self, err: VmError) -> Step { self.finish_err(err.into_error()) } #[inline(always)] - pub(crate) fn push_stack(&mut self, val: Value<'gc>) { + pub(crate) fn push(&mut self, val: Value<'gc>) { self.stack.push(val); } #[inline(always)] #[must_use] - pub(crate) fn pop_stack(&mut self) -> Value<'gc> { + pub(crate) fn pop(&mut self) -> Value<'gc> { self.stack.pop().expect("stack underflow") } #[inline(always)] #[must_use] - pub(crate) fn peek_stack(&mut self, depth: usize) -> Value<'gc> { + pub(crate) fn peek(&mut self, depth: usize) -> Value<'gc> { *self .stack .get(self.stack.len() - depth - 1) @@ -308,7 +314,18 @@ impl<'gc> Vm<'gc> { } #[inline(always)] - pub(crate) fn replace_stack(&mut self, depth: usize, val: Value<'gc>) { + #[must_use] + pub(crate) fn peek_forced(&mut self, depth: usize) -> StrictValue<'gc> { + self + .stack + .get(self.stack.len() - depth - 1) + .expect("stack underflow") + .restrict() + .expect("forced") + } + + #[inline(always)] + pub(crate) fn replace(&mut self, depth: usize, val: Value<'gc>) { let len = self.stack.len(); *self .stack @@ -318,7 +335,7 @@ impl<'gc> Vm<'gc> { #[inline(always)] #[cfg_attr(debug_assertions, track_caller)] - pub(crate) fn pop_stack_forced(&mut self) -> StrictValue<'gc> { + pub(crate) fn pop_forced(&mut self) -> StrictValue<'gc> { self.stack .pop() .expect("stack underflow") @@ -326,14 +343,34 @@ impl<'gc> Vm<'gc> { .expect("forced") } + #[inline(always)] + pub(crate) fn pop_forced_expect_inline(&mut self) -> std::result::Result { + self.pop_forced().expect_inline::() + } + + #[inline(always)] + pub(crate) fn pop_forced_expect_gc(&mut self) -> std::result::Result, NixType> { + self.pop_forced().expect_gc::() + } + + #[inline(always)] + pub(crate) fn pop_forced_expect_num(&mut self) -> std::result::Result { + self.pop_forced().expect_num() + } + + #[inline(always)] + pub(crate) fn pop_forced_expect_bool(&mut self) -> std::result::Result { + self.pop_forced().expect_bool() + } + #[inline(always)] pub(crate) fn try_force( &mut self, depth: usize, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>, - ) -> Option { - let thunk = self.peek_stack(depth).as_gc::()?; + ) -> Option { + let thunk = self.peek(depth).as_gc::()?; let mut state = thunk.borrow_mut(mc); match *state { ThunkState::Pending { ip, env, with_env } => { @@ -349,11 +386,11 @@ impl<'gc> Vm<'gc> { self.env = env; self.with_env = with_env; reader.set_pc(ip); - Some(StepResult::Continue) + Some(Step::Continue) } ThunkState::Evaluated(v) => { drop(state); - self.replace_stack(depth, v.relax()); + self.replace(depth, v.relax()); None } ThunkState::Apply { .. } => todo!("force apply"), @@ -545,8 +582,8 @@ impl<'gc> Vm<'gc> { }; match result { - StepResult::Continue => {} - StepResult::Done => { + Step::Continue => {} + Step::Done => { return Action::Done( self.result.take().expect("StepResult::Done without result"), ); diff --git a/fix-vm/src/value.rs b/fix-vm/src/value.rs index 141be58..c1b284f 100644 --- a/fix-vm/src/value.rs +++ b/fix-vm/src/value.rs @@ -23,10 +23,10 @@ mod private { /// # Safety /// -/// 1. TAG must be unique among all implementors. -/// 2. TAG must be within 1..=7 +/// TAG must be unique among all implementors. +#[allow(private_interfaces)] pub unsafe trait Storable: private::Cealed { - const TAG: (bool, u8); + const TAG: RawTag; } #[allow(private_bounds)] pub trait InlineStorable: Storable + RawStore {} @@ -38,15 +38,17 @@ macro_rules! define_value_types { gc { $($gtype:ty => $gtag:expr, $gname:literal;)* } ) => { $( + #[allow(private_interfaces)] unsafe impl Storable for $itype { - const TAG: (bool, u8) = $itag; + const TAG: RawTag = $itag; } impl InlineStorable for $itype {} impl private::Cealed for $itype {} )* $( + #[allow(private_interfaces)] unsafe impl Storable for $gtype { - const TAG: (bool, u8) = $gtag; + const TAG: RawTag = $gtag; } impl GcStorable for $gtype {} impl private::Cealed for $gtype {} @@ -54,11 +56,9 @@ macro_rules! define_value_types { const _: () = assert!(size_of::>() == 8); $(const _: () = assert!(size_of::<$itype>() <= 6);)* - $(const _: () = { let (_, val) = $itag; assert!(val >= 1 && val <= 7); };)* - $(const _: () = { let (_, val) = $gtag; assert!(val >= 1 && val <= 7); };)* const _: () = { - let tags: &[(bool, u8)] = &[$($itag),*, $($gtag),*]; + let tags: &[(bool, u8)] = &[$(RawTag::neg_val($itag)),*, $(RawTag::neg_val($gtag)),*]; let mut mask_false: u8 = 0; let mut mask_true: u8 = 0; let mut i = 0; @@ -80,12 +80,12 @@ macro_rules! define_value_types { const NEEDS_TRACE: bool = true; fn trace>(&self, cc: &mut T) { let Some(tag) = self.raw.tag() else { return }; - match tag.neg_val() { + match tag { $(<$gtype as Storable>::TAG => unsafe { self.load_gc::<$gtype>().trace(cc) },)* $(<$itype as Storable>::TAG => (),)* - (neg, val) => unreachable!("invalid tag: neg={neg}, val={val}"), + _ => unreachable!("invalid value tag"), } } } @@ -101,7 +101,7 @@ macro_rules! define_value_types { }),)* $(Some(<$gtype as Storable>::TAG) => write!(f, "{}(..)", $gname),)* - Some((neg, val)) => write!(f, "Unknown(neg={neg}, val={val})"), + _ => unreachable!("invalid value tag"), } } } @@ -110,20 +110,20 @@ macro_rules! define_value_types { define_value_types! { inline { - i32 => (false, 1), "SmallInt"; - bool => (false, 2), "Bool"; - Null => (false, 3), "Null"; - StringId => (false, 4), "SmallString"; - PrimOp => (false, 5), "PrimOp"; + i32 => RawTag::P1, "SmallInt"; + bool => RawTag::P2, "Bool"; + Null => RawTag::P3, "Null"; + StringId => RawTag::P4, "SmallString"; + PrimOp => RawTag::P5, "PrimOp"; } gc { - i64 => (false, 6), "BigInt"; - NixString => (false, 7), "String"; - AttrSet<'_> => (true, 1), "AttrSet"; - List<'_> => (true, 2), "List"; - Thunk<'_> => (true, 3), "Thunk"; - Closure<'_> => (true, 4), "Closure"; - PrimOpApp<'_> => (true, 5), "PrimOpApp"; + i64 => RawTag::P6, "BigInt"; + NixString => RawTag::P7, "String"; + AttrSet<'_> => RawTag::N1, "AttrSet"; + List<'_> => RawTag::N2, "List"; + Thunk<'_> => RawTag::N3, "Thunk"; + Closure<'_> => RawTag::N4, "Closure"; + PrimOpApp<'_> => RawTag::N5, "PrimOpApp"; } } @@ -132,7 +132,7 @@ define_value_types! { /// NaN-boxed value fitting in 8 bytes. #[derive(Copy, Clone)] #[repr(transparent)] -pub(crate) struct Value<'gc> { +pub struct Value<'gc> { raw: RawBox, _marker: PhantomData>, } @@ -167,16 +167,15 @@ impl<'gc> Value<'gc> { } } - /// Returns the `(negative, val)` tag, or `None` for a float. #[inline(always)] - fn tag(self) -> Option<(bool, u8)> { - self.raw.tag().map(|t| t.neg_val()) + const fn tag(self) -> Option { + self.raw.tag() } } impl<'gc> Value<'gc> { #[inline] - pub(crate) fn new_float(val: f64) -> Self { + pub fn new_float(val: f64) -> Self { Self { raw: RawBox::from_float(val), _marker: PhantomData, @@ -184,24 +183,24 @@ impl<'gc> Value<'gc> { } #[inline] - pub(crate) fn new_inline(val: T) -> Self { + pub fn new_inline(val: T) -> Self { Self::from_raw_value(RawValue::store( - unsafe { RawTag::new_unchecked(T::TAG.0, T::TAG.1) }, + T::TAG, val, )) } #[inline] - pub(crate) fn new_gc(gc: Gc<'gc, T>) -> Self { + pub fn new_gc(gc: Gc<'gc, T>) -> Self { let ptr = Gc::as_ptr(gc); Self::from_raw_value(RawValue::store( - unsafe { RawTag::new_unchecked(T::TAG.0, T::TAG.1) }, + T::TAG, ptr, )) } #[inline] - pub(crate) fn make_int(val: i64, mc: &Mutation<'gc>) -> Self { + pub fn make_int(val: i64, mc: &Mutation<'gc>) -> Self { if val >= i32::MIN as i64 && val <= i32::MAX as i64 { Value::new_inline(val as i32) } else { @@ -212,24 +211,24 @@ impl<'gc> Value<'gc> { impl<'gc> Value<'gc> { #[inline] - pub(crate) fn is_float(self) -> bool { + pub fn is_float(self) -> bool { self.raw.is_float() } #[inline] - pub(crate) fn is(self) -> bool { + pub fn is(self) -> bool { self.tag() == Some(T::TAG) } } impl<'gc> Value<'gc> { #[inline] - pub(crate) fn as_float(self) -> Option { + pub fn as_float(self) -> Option { self.raw.float().copied() } #[inline] - pub(crate) fn as_inline(self) -> Option { + pub fn as_inline(self) -> Option { if self.is::() { Some(unsafe { let rv = self.raw.value().unwrap_unchecked(); @@ -241,7 +240,7 @@ impl<'gc> Value<'gc> { } #[inline] - pub(crate) fn as_gc(self) -> Option> { + pub fn as_gc(self) -> Option> { if self.is::() { Some(unsafe { let rv = self.raw.value().unwrap_unchecked(); @@ -254,7 +253,7 @@ impl<'gc> Value<'gc> { } #[inline] - pub(crate) fn as_num(self) -> Option { + pub fn as_num(self) -> Option { if let Some(i) = self.as_inline::() { Some(NixNum::Int(i as i64)) } else if let Some(gc_i) = self.as_gc::() { @@ -265,13 +264,69 @@ impl<'gc> Value<'gc> { } #[inline] - pub(crate) fn restrict(self) -> Result, Gc<'gc, Thunk<'gc>>> { + pub fn restrict(self) -> Result, Gc<'gc, Thunk<'gc>>> { if let Some(thunk) = self.as_gc::>() { Err(thunk) } else { Ok(StrictValue(self)) } } + + #[inline] + pub fn ty(self) -> NixType { + if self.is_float() { + NixType::Float + } else if self.is::() || self.is::() { + NixType::Int + } else if self.is::() { + NixType::Bool + } else if self.is::() { + NixType::Null + } else if self.is::() { + NixType::String + } else if self.is::() { + NixType::PrimOp + } else if self.is::() { + NixType::String + } else if self.is::() { + NixType::AttrSet + } else if self.is::() { + NixType::List + } else if self.is::() { + NixType::Thunk + } else if self.is::() { + NixType::Closure + } else if self.is::() { + NixType::PrimOpApp + } else { + unreachable!("value has no recognized type tag") + } + } + + #[inline] + pub(crate) fn expect_inline(self) -> Result { + self.as_inline::().ok_or_else(|| self.ty()) + } + + #[inline] + pub(crate) fn expect_gc(self) -> Result, NixType> { + self.as_gc::().ok_or_else(|| self.ty()) + } + + #[inline] + pub(crate) fn expect_num(self) -> Result { + self.as_num().ok_or_else(|| self.ty()) + } + + #[inline] + pub(crate) fn expect_bool(self) -> Result { + self.as_inline::().ok_or_else(|| self.ty()) + } + + #[inline] + pub(crate) fn expect_float(self) -> Result { + self.as_float().ok_or_else(|| self.ty()) + } } #[derive(Copy, Clone, Default)] @@ -550,8 +605,9 @@ pub(crate) struct PrimOpApp<'gc> { pub(crate) args: SmallVec<[Value<'gc>; 2]>, } -#[derive(Copy, Clone, Default)] +#[derive(Copy, Clone, Default, Collect)] #[repr(transparent)] +#[collect(no_drop)] pub(crate) struct StrictValue<'gc>(Value<'gc>); impl<'gc> StrictValue<'gc> { @@ -575,9 +631,43 @@ impl fmt::Debug for StrictValue<'_> { } } -unsafe impl<'gc> Collect<'gc> for StrictValue<'gc> { - const NEEDS_TRACE: bool = true; - fn trace>(&self, cc: &mut T) { - self.0.trace(cc); +#[derive(Clone, Copy, Debug, PartialEq, Eq, Collect)] +#[collect(require_static)] +pub(crate) enum NixType { + Int, + Float, + Bool, + Null, + String, + AttrSet, + List, + Thunk, + Closure, + PrimOp, + PrimOpApp, +} + +impl NixType { + pub fn display(self) -> &'static str { + use NixType::*; + match self { + Int => "an integer", + Float => "a float", + Bool => "a boolean", + Null => "null", + String => "a string", + AttrSet => "a set", + List => "a list", + Thunk => "a thunk", + Closure => "a function", + PrimOp => "a built-in function", + PrimOpApp => "a partially applied built-in function", + } + } +} + +impl std::fmt::Display for NixType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.display()) } }