From df9664f5c4855e1f5b38e4a8a848303ef0a629ef Mon Sep 17 00:00:00 2001 From: imxyy_soope_ Date: Sat, 18 Apr 2026 16:44:06 +0800 Subject: [PATCH] LookupWith: retry --- fix-codegen/src/disassembler.rs | 5 +- fix-codegen/src/lib.rs | 6 +- fix-vm/src/lib.rs | 228 ++++++++++++++++---------------- 3 files changed, 119 insertions(+), 120 deletions(-) diff --git a/fix-codegen/src/disassembler.rs b/fix-codegen/src/disassembler.rs index 57aa4c4..cb239f7 100644 --- a/fix-codegen/src/disassembler.rs +++ b/fix-codegen/src/disassembler.rs @@ -336,10 +336,11 @@ impl<'a, Ctx: DisassemblerContext> Disassembler<'a, Ctx> { } Op::PushWith => ("PushWith", String::new()), Op::PopWith => ("PopWith", String::new()), - Op::WithLookup => { + Op::PrepareWith => ("PrepareWith", String::new()), + Op::LookupWith => { let idx = self.read_u32(); let name = self.ctx.resolve_string(idx); - ("WithLookup", format!("{:?}", name)) + ("LookupWith", format!("{:?}", name)) } Op::LoadBuiltins => ("LoadBuiltins", String::new()), diff --git a/fix-codegen/src/lib.rs b/fix-codegen/src/lib.rs index 5192487..80ac49f 100644 --- a/fix-codegen/src/lib.rs +++ b/fix-codegen/src/lib.rs @@ -79,7 +79,8 @@ pub enum Op { PushWith, PopWith, - WithLookup, + LookupWith, + PrepareWith, LoadBuiltins, LoadBuiltin, @@ -625,7 +626,8 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> { self.emit_with(namespace, body, thunks); } &Ir::WithLookup(name) => { - self.emit_op(Op::WithLookup); + self.emit_op(Op::PrepareWith); + self.emit_op(Op::LookupWith); self.emit_str_id(name); } } diff --git a/fix-vm/src/lib.rs b/fix-vm/src/lib.rs index 061229b..36ebe40 100644 --- a/fix-vm/src/lib.rs +++ b/fix-vm/src/lib.rs @@ -1,8 +1,10 @@ +#![warn(clippy::unwrap_used)] + use std::path::PathBuf; use fix_builtins::{BUILTINS, BuiltinId}; use fix_codegen::{AttrKeyType, InstructionPtr, OperandType}; -use fix_common::StringId; +use fix_common::{StringId, Symbol}; use fix_error::{Error, Result, Source}; use gc_arena::arena::CollectionPhase; use gc_arena::{Arena, Collect, Gc, Mutation, RefLock, Rootable}; @@ -19,6 +21,7 @@ pub use value::StaticValue; type VmResult = std::result::Result; +#[allow(dead_code)] enum VmError { Catchable(String), Uncatchable(Box), @@ -109,6 +112,7 @@ impl VmContextExt for T { } } +#[allow(dead_code)] pub struct Vm { arena: Arena]>, error_context: Vec, @@ -128,7 +132,7 @@ struct GcRoot<'gc> { empty_list: Value<'gc>, empty_attrs: Value<'gc>, import_cache: HashMap>, - current_env: Option>, + env: GcEnv<'gc>, } fn init_builtins<'gc>(mc: &Mutation<'gc>, ctx: &mut impl VmContext) -> Value<'gc> { @@ -197,15 +201,10 @@ impl<'gc> GcRoot<'gc> { empty_list: Value::new_gc(Gc::new(mc, List::default())), empty_attrs: Value::new_gc(Gc::new(mc, AttrSet::default())), import_cache: HashMap::new(), - current_env: None, + env: Gc::new(mc, RefLock::new(Env::empty())), } } - #[inline(always)] - fn env(&self) -> Gc<'gc, RefLock>> { - self.current_env.expect("no current env") - } - #[inline(always)] fn push_stack(&mut self, val: Value<'gc>) { self.stack.push(val); @@ -244,6 +243,7 @@ impl<'gc> GcRoot<'gc> { } } +#[allow(dead_code)] struct ErrorFrame { span_id: u32, message: Option, @@ -281,7 +281,7 @@ impl OperandData { match *self { OperandData::Const(sv) => sv.into(), OperandData::Local { layer, idx } => { - let mut cur = root.env(); + let mut cur = root.env; for _ in 0..layer { let prev = cur.borrow().prev.expect("env chain too short"); cur = prev; @@ -332,12 +332,6 @@ impl Vm { pub fn run(mut self) -> Result { const COLLECTOR_GRANULARITY: f64 = 1024.0; - self.arena.mutate_root(|mc, root| { - if root.current_env.is_none() { - root.current_env = Some(Gc::new(mc, RefLock::new(Env::empty()))); - } - }); - let mut pc = self.pc; loop { match self @@ -391,7 +385,6 @@ impl<'gc> GcRoot<'gc> { match ty { OperandType::Const => { let id = read!(u32); - #[allow(clippy::unwrap_used)] OperandData::Const(ctx.get_const(id)) } OperandType::Local => { @@ -438,12 +431,12 @@ impl<'gc> GcRoot<'gc> { thunk: Some(thunk), stack_depth: $depth, pc: $inst_start, - env: self.env(), + env: self.env, with_env: self.with_env, }); *pc = ip; - self.current_env = Some(env); + self.env = env; self.with_env = with_env; *state = ThunkState::Blackhole; continue 'dispatch; @@ -499,12 +492,12 @@ impl<'gc> GcRoot<'gc> { LoadLocal => { let idx = read!(u32) as usize; - self.push_stack(self.env().borrow().locals[idx]); + self.push_stack(self.env.borrow().locals[idx]); } LoadOuter => { let layer = read!(u8); let idx = read!(u32) as usize; - let mut cur = self.env(); + let mut cur = self.env; for _ in 0..layer { let prev = cur.borrow().prev.expect("LoadOuter: env chain too short"); cur = prev; @@ -515,11 +508,11 @@ impl<'gc> GcRoot<'gc> { StoreLocal => { let idx = read!(u32) as usize; let val = self.pop_stack(); - self.env().borrow_mut(mc).locals[idx] = val; + self.env.borrow_mut(mc).locals[idx] = val; } AllocLocals => { let count = read!(u32) as usize; - self.env() + self.env .borrow_mut(mc) .locals .extend(std::iter::repeat_n(Value::default(), count)); @@ -531,7 +524,7 @@ impl<'gc> GcRoot<'gc> { mc, RefLock::new(ThunkState::Pending { ip: entry_point as usize, - env: self.env(), + env: self.env, with_env: self.with_env, }), ); @@ -545,7 +538,7 @@ impl<'gc> GcRoot<'gc> { Closure { ip: entry_point, n_locals, - env: self.env(), + env: self.env, pattern: None, }, ); @@ -588,7 +581,7 @@ impl<'gc> GcRoot<'gc> { Closure { ip: entry_point, n_locals, - env: self.env(), + env: self.env, pattern: Some(pattern), }, ); @@ -618,11 +611,11 @@ impl<'gc> GcRoot<'gc> { pc: *pc, stack_depth: 0, thunk: None, - env: self.env(), + env: self.env, with_env: self.with_env, }); *pc = ip as usize; - self.current_env = Some(new_env); + self.env = new_env; } } else { todo!("call other types: {func:?}") @@ -1008,37 +1001,44 @@ impl<'gc> GcRoot<'gc> { }; self.with_env = scope.prev; } - WithLookup => { + PrepareWith => { + self.call_stack.push(CallFrame { + // sentinel value + pc: usize::MAX, + stack_depth: 0, + thunk: None, + env: self.env, + with_env: self.with_env, + }) + } + LookupWith => { let name = read!(StringId); - let mut cur = self.with_env; - let mut found_val = None; - - while let Some(scope) = cur { - // Using restrict() to force extraction sync due to CPS structure. - let env_val = scope - .env - .restrict() - .unwrap_or_else(|_| panic!("with scope env must be forced")); - let Some(attrs) = env_val.as_gc::() else { - return self - .handle_vm_error(vm_err("value in 'with' scope must be a set")); + let Some(&WithEnv { env, prev }) = self.with_env.as_deref() else { + let Some(CallFrame { with_env, .. }) = self.call_stack.pop() else { + unreachable!() }; + self.with_env = with_env; + return Action::Done(Err(Error::eval_error(format!( + "undefined variable '{}'", + Symbol::from(ctx.resolve_string(name)) + )))); + }; + self.push_stack(env); + try_force!(0, inst_start_pc); - if let Some(v) = attrs.lookup(name) { - found_val = Some(v); - break; - } - cur = scope.prev; - } + let env = self.pop_stack().as_gc::().unwrap(); + let Some(val) = env.lookup(name) else { + *pc = inst_start_pc; + self.with_env = prev; + continue 'dispatch; + }; - if let Some(v) = found_val { - self.push_stack(v); - } else { - let name_str = ctx.resolve_string(name); - return self - .handle_vm_error(vm_err(format!("undefined variable '{name_str}'"))); - } + self.push_stack(val); + let Some(CallFrame { with_env, .. }) = self.call_stack.pop() else { + unreachable!() + }; + self.with_env = with_env; } LoadBuiltins => { @@ -1184,84 +1184,80 @@ impl<'gc> GcRoot<'gc> { mc: &Mutation<'gc>, ) -> Option> { let ret_inst_pc = *pc - 1; - #[deny(unused_variables)] - if let Some(CallFrame { + let Some(CallFrame { pc: ret_pc, stack_depth, thunk, env, with_env, }) = self.call_stack.pop() - { - *pc = ret_pc; - if let Some(outer_thunk) = thunk { - let val = self.pop_stack(); - match val.restrict() { - Ok(val) => { - *outer_thunk.borrow_mut(mc) = ThunkState::Evaluated(val); - if ctx.bytecode().get(ret_pc).copied() - == Some(fix_codegen::Op::Return as u8) - { - self.push_stack(val.relax()); - } + else { + // Evaluation complete, return value + // FIXME: ForceMode + let val = self.pop_stack(); + return Some(Ok(ctx.convert_value(val))); + }; + *pc = ret_pc; + if let Some(outer_thunk) = thunk { + let val = self.pop_stack(); + match val.restrict() { + Ok(val) => { + *outer_thunk.borrow_mut(mc) = ThunkState::Evaluated(val); + if ctx.bytecode().get(ret_pc).copied() == Some(fix_codegen::Op::Return as u8) { + self.push_stack(val.relax()); } - Err(inner_thunk) => { - let mut state = inner_thunk.borrow_mut(mc); - match *state { - ThunkState::Pending { - ip: inner_ip, + } + // Recursively force the thunk + // TODO: extract forcing logic + Err(inner_thunk) => { + let mut state = inner_thunk.borrow_mut(mc); + match *state { + ThunkState::Pending { + ip: inner_ip, + env: inner_env, + with_env: inner_with_env, + } => { + self.call_stack.push(CallFrame { + pc: ret_pc, + stack_depth, + thunk: Some(outer_thunk), + env, + with_env, + }); + self.call_stack.push(CallFrame { + pc: ret_inst_pc, + stack_depth: 0, + thunk: Some(inner_thunk), env: inner_env, with_env: inner_with_env, - } => { - self.call_stack.push(CallFrame { - pc: ret_pc, - stack_depth, - thunk: Some(outer_thunk), - env, - with_env, - }); - self.call_stack.push(CallFrame { - pc: ret_inst_pc, - stack_depth: 0, - thunk: Some(inner_thunk), - env: inner_env, - with_env: inner_with_env, - }); - *state = ThunkState::Blackhole; - *pc = inner_ip; - self.current_env = Some(inner_env); - self.with_env = inner_with_env; - return None; - } - ThunkState::Evaluated(val) => { - *outer_thunk.borrow_mut(mc) = ThunkState::Evaluated(val); - if ctx.bytecode().get(ret_pc).copied() - == Some(fix_codegen::Op::Return as u8) - { - self.push_stack(val.relax()); - } - } - ThunkState::Apply { func: _, arg: _ } => todo!("force Apply thunk"), - ThunkState::Blackhole => { - return Some(Err(Error::eval_error( - "infinite recursion encountered", - ))); + }); + *state = ThunkState::Blackhole; + *pc = inner_ip; + self.env = inner_env; + self.with_env = inner_with_env; + return None; + } + ThunkState::Evaluated(val) => { + *outer_thunk.borrow_mut(mc) = ThunkState::Evaluated(val); + if ctx.bytecode().get(ret_pc).copied() + == Some(fix_codegen::Op::Return as u8) + { + self.push_stack(val.relax()); } } + ThunkState::Apply { func: _, arg: _ } => todo!("force Apply thunk"), + ThunkState::Blackhole => { + return Some(Err(Error::eval_error("infinite recursion encountered"))); + } } } - } else { - self.call_depth -= 1; } - self.current_env = Some(env); - self.with_env = with_env; - return None; + } else { + self.call_depth -= 1; } - // FIXME: ForceMode - self.current_env = None; - self.with_env = None; - let val = self.pop_stack(); - Some(Ok(ctx.convert_value(val))) + self.env = env; + self.with_env = with_env; + None } fn handle_vm_error(&mut self, e: VmError) -> Action {